fix(runtime): freeze miniapp kind vocabulary

This commit is contained in:
2026-08-06 01:31:38 +08:00
parent b81a0edcca
commit 44b6a83a30
4 changed files with 10 additions and 7 deletions
@@ -52,8 +52,12 @@ export type CoreAppRecord = {
manifest: AppManifest; manifest: AppManifest;
}; };
/** Transitional input accepted while older host adapters are upgraded. */ /**
export type CoreAppRecordInput = Omit<CoreAppRecord, "kind"> & { kind: MiniAppKind | "core" | "extension" }; * The Runtime installation boundary uses the frozen SDK v1 vocabulary.
* Legacy host labels (`core` / `extension`) are intentionally not accepted
* here; callers must migrate before they can register an App.
*/
export type CoreAppRecordInput = Omit<CoreAppRecord, "kind"> & { kind: MiniAppKind };
const INITIAL_CORE_APPS: readonly CoreAppRecord[] = [ const INITIAL_CORE_APPS: readonly CoreAppRecord[] = [
{ {
@@ -132,8 +136,7 @@ export class CoreAppRegistry {
if (!isAppScope(record.app_scope) || record.manifest.app_scope !== record.app_scope) throw new Error("Core App Registry received an invalid app_scope."); if (!isAppScope(record.app_scope) || record.manifest.app_scope !== record.app_scope) throw new Error("Core App Registry received an invalid app_scope.");
if (this.apps.has(record.app_scope)) throw new Error(`Core App Registry has duplicate app_scope: ${record.app_scope}`); if (this.apps.has(record.app_scope)) throw new Error(`Core App Registry has duplicate app_scope: ${record.app_scope}`);
if (!isManifest(record.manifest)) throw new Error(`Core App Registry has an invalid manifest: ${record.app_scope}`); if (!isManifest(record.manifest)) throw new Error(`Core App Registry has an invalid manifest: ${record.app_scope}`);
const kind: MiniAppKind = record.kind === "core" ? "system" : record.kind === "extension" ? "bundled" : record.kind; this.apps.set(record.app_scope, copyRecord(record));
this.apps.set(record.app_scope, copyRecord({ ...record, kind }));
} }
} }
@@ -69,7 +69,7 @@ describe("LineUpRuntime", () => {
const transport = new FakeTransport([[{ message_seq: 30, from_uid: "agent_1", payload: launch }], []]); const transport = new FakeTransport([[{ message_seq: 30, from_uid: "agent_1", payload: launch }], []]);
const runtime = new LineUpRuntime({ storage, createTransport: () => transport, now: () => "2026-08-04T00:00:00.000Z" }); const runtime = new LineUpRuntime({ storage, createTransport: () => transport, now: () => "2026-08-04T00:00:00.000Z" });
runtime.apps.install({ runtime.apps.install({
app_scope: "draw-and-guess", kind: "extension", enabled: true, default_eligible: false, recovery: false, app_scope: "draw-and-guess", kind: "bundled", enabled: true, default_eligible: false, recovery: false,
manifest: { app_scope: "draw-and-guess", version: "1.0.0", permissions: [], tools: [{ name: "app.launch", handling: "launch", parameters: ["app_scope", "instance_id"] }] }, manifest: { app_scope: "draw-and-guess", version: "1.0.0", permissions: [], tools: [{ name: "app.launch", handling: "launch", parameters: ["app_scope", "instance_id"] }] },
}); });
await runtime.login(login); await runtime.login(login);
@@ -4,7 +4,7 @@ import { ToolRouter } from "@/runtime/coordination/tool-router";
function extension(enabled = true) { function extension(enabled = true) {
return { return {
app_scope: "draw-and-guess", kind: "extension" as const, enabled, default_eligible: false, recovery: false, app_scope: "draw-and-guess", kind: "bundled" as const, enabled, default_eligible: false, recovery: false,
manifest: { app_scope: "draw-and-guess", version: "1.0.0", permissions: [], tools: [{ name: "app.launch", handling: "launch" as const, parameters: ["app_scope"] }] }, manifest: { app_scope: "draw-and-guess", version: "1.0.0", permissions: [], tools: [{ name: "app.launch", handling: "launch" as const, parameters: ["app_scope"] }] },
}; };
} }
@@ -36,7 +36,7 @@ describe("ClientInventoryPublisher", () => {
it("advertises only enabled Runtime manifests and their declarative tools", () => { it("advertises only enabled Runtime manifests and their declarative tools", () => {
const inventory = new ClientInventoryPublisher().sync(new SurfaceRegistry().snapshot(), new CapabilityRegistry().inventory(), [{ const inventory = new ClientInventoryPublisher().sync(new SurfaceRegistry().snapshot(), new CapabilityRegistry().inventory(), [{
app_scope: "draw-and-guess", kind: "extension", enabled: true, default_eligible: false, recovery: false, app_scope: "draw-and-guess", kind: "bundled", enabled: true, default_eligible: false, recovery: false,
manifest: { app_scope: "draw-and-guess", version: "1.0.0", permissions: [], tools: [{ name: "app.launch", handling: "launch", parameters: ["app_scope"] }] }, manifest: { app_scope: "draw-and-guess", version: "1.0.0", permissions: [], tools: [{ name: "app.launch", handling: "launch", parameters: ["app_scope"] }] },
}]).inventory; }]).inventory;
expect(inventory.applications).toContainEqual(expect.objectContaining({ id: "draw-and-guess", tools: [{ name: "app.launch", handling: "launch" }] })); expect(inventory.applications).toContainEqual(expect.objectContaining({ id: "draw-and-guess", tools: [{ name: "app.launch", handling: "launch" }] }));