refactor(runtime): remove legacy extension sdk bypass
This commit is contained in:
@@ -115,21 +115,6 @@ export interface InteractRuntimeSDK {
|
|||||||
/** @deprecated Use InteractRuntimeSDK; kept for the compatibility app_scope=chat name. */
|
/** @deprecated Use InteractRuntimeSDK; kept for the compatibility app_scope=chat name. */
|
||||||
export type ChatRuntimeSDK = InteractRuntimeSDK;
|
export type ChatRuntimeSDK = InteractRuntimeSDK;
|
||||||
|
|
||||||
/**
|
|
||||||
* The intentionally smaller SDK granted to an installed Extension App. It
|
|
||||||
* does not reveal another App's inbox, the focus stack, Transport, DOM root,
|
|
||||||
* storage, credentials, or Host capabilities.
|
|
||||||
*/
|
|
||||||
export interface ExtensionRuntimeSDK {
|
|
||||||
readonly app_scope: AppScope;
|
|
||||||
readonly instance_id: string;
|
|
||||||
readonly conversation_id: string;
|
|
||||||
listMessages(afterMessageID?: string): readonly ScopedConversationItem[];
|
|
||||||
acknowledgeMessage(messageID: string): boolean;
|
|
||||||
reportResult(result: JsonObject): Promise<CommandReceipt>;
|
|
||||||
reportFailure(message: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RuntimeAppEvent =
|
export type RuntimeAppEvent =
|
||||||
| { type: "agent-message"; message: ScopedConversationItem }
|
| { type: "agent-message"; message: ScopedConversationItem }
|
||||||
| { type: "local-item"; item: ConversationItem; local_id: string }
|
| { type: "local-item"; item: ConversationItem; local_id: string }
|
||||||
|
|||||||
@@ -84,11 +84,6 @@ describe("LineUpRuntime", () => {
|
|||||||
const interaction = runtime.lifecycle.instances.activeFor("chat", conversationID);
|
const interaction = runtime.lifecycle.instances.activeFor("chat", conversationID);
|
||||||
expect(interaction).toMatchObject({ state: "background" });
|
expect(interaction).toMatchObject({ state: "background" });
|
||||||
expect(runtime.snapshot()?.app_inbox).toEqual([expect.objectContaining({ app_scope: "draw-and-guess", message_id: "launch-game" })]);
|
expect(runtime.snapshot()?.app_inbox).toEqual([expect.objectContaining({ app_scope: "draw-and-guess", message_id: "launch-game" })]);
|
||||||
const game = runtime.openExtensionApp("draw-and-guess", "draw-and-guess:game-001");
|
|
||||||
expect(game.listMessages()).toEqual([expect.objectContaining({ scope: { app_scope: "draw-and-guess", conversation_id: conversationID } })]);
|
|
||||||
expect(await game.reportResult({ winner: "user_1" })).toMatchObject({ disposition: "accepted" });
|
|
||||||
expect(transport.sent.at(-1)?.payload).toContain("lineup.v1.app.result");
|
|
||||||
|
|
||||||
runtime.closeAppInstance("draw-and-guess:game-001");
|
runtime.closeAppInstance("draw-and-guess:game-001");
|
||||||
expect(runtime.lifecycle.focus.foreground()).toBe(interaction?.instance_id);
|
expect(runtime.lifecycle.focus.foreground()).toBe(interaction?.instance_id);
|
||||||
expect(runtime.lifecycle.instances.get(interaction!.instance_id)).toMatchObject({ state: "foreground" });
|
expect(runtime.lifecycle.instances.get(interaction!.instance_id)).toMatchObject({ state: "foreground" });
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import {
|
|||||||
type ChatRuntimeSDK,
|
type ChatRuntimeSDK,
|
||||||
type ChatViewModel,
|
type ChatViewModel,
|
||||||
type CommandReceipt,
|
type CommandReceipt,
|
||||||
type ExtensionRuntimeSDK,
|
|
||||||
type RuntimeAppEvent,
|
type RuntimeAppEvent,
|
||||||
type Unsubscribe,
|
type Unsubscribe,
|
||||||
} from "@/runtime/app-management/app-sdk";
|
} from "@/runtime/app-management/app-sdk";
|
||||||
@@ -295,23 +294,6 @@ export class LineUpRuntime {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Opens a per-instance Extension projection after Runtime has started it. */
|
|
||||||
public openExtensionApp(appScope: AppScope, instanceID: string): ExtensionRuntimeSDK {
|
|
||||||
const app = this.apps.get(appScope);
|
|
||||||
const instance = this.lifecycle.instances.get(instanceID);
|
|
||||||
const conversationID = this.requireConversationID();
|
|
||||||
if (!app || app.kind !== "bundled" || !app.enabled || !instance || instance.app_scope !== appScope || instance.conversation_id !== conversationID) {
|
|
||||||
throw new Error("Bundled MiniApp SDK is unavailable for this instance.");
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
app_scope: appScope, instance_id: instanceID, conversation_id: conversationID,
|
|
||||||
listMessages: afterMessageID => this.listAppMessages(appScope, afterMessageID),
|
|
||||||
acknowledgeMessage: messageID => this.store.acknowledgeAppInbox(appScope, messageID),
|
|
||||||
reportResult: result => this.queueProtocolAction("app-result", "lineup.v1.app.result", { instance_id: instanceID, app_scope: appScope, result }),
|
|
||||||
reportFailure: message => this.closeAppInstance(instanceID, boundedFailure(message)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The restricted SDK injected into a bundled MiniApp instance. */
|
/** The restricted SDK injected into a bundled MiniApp instance. */
|
||||||
public openBundledMiniApp(appScope: AppScope, instanceID: string): LineUpMiniAppSDK {
|
public openBundledMiniApp(appScope: AppScope, instanceID: string): LineUpMiniAppSDK {
|
||||||
const app = this.apps.get(appScope);
|
const app = this.apps.get(appScope);
|
||||||
@@ -1295,11 +1277,6 @@ export class LineUpRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function boundedFailure(value: string): string {
|
|
||||||
const message = value.trim();
|
|
||||||
return message ? message.slice(0, 500) : "Extension App failed.";
|
|
||||||
}
|
|
||||||
|
|
||||||
function appSessionHistory(records: readonly StandardInteractionRecord[]): readonly { id: string; role: "agent" | "user"; text: string }[] {
|
function appSessionHistory(records: readonly StandardInteractionRecord[]): readonly { id: string; role: "agent" | "user"; text: string }[] {
|
||||||
const history: Array<{ id: string; role: "agent" | "user"; text: string }> = [];
|
const history: Array<{ id: string; role: "agent" | "user"; text: string }> = [];
|
||||||
for (const record of records) {
|
for (const record of records) {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
| 🔴 | P2 | A8 | 当前 Task Dashboard/Whiteboard Surface 主要是 Host 硬编码的静态页面,无法证明真实业务 UI 在各自受限 Surface 内运行 | `main.ts:180-198`、`isolated-surface-host.ts` | 至少提供能代表业务边界的受限 Surface fixture,并验证 MiniApp 业务逻辑不能取得 Host DOM 或 Runtime 内部。 |
|
| 🔴 | P2 | A8 | 当前 Task Dashboard/Whiteboard Surface 主要是 Host 硬编码的静态页面,无法证明真实业务 UI 在各自受限 Surface 内运行 | `main.ts:180-198`、`isolated-surface-host.ts` | 至少提供能代表业务边界的受限 Surface fixture,并验证 MiniApp 业务逻辑不能取得 Host DOM 或 Runtime 内部。 |
|
||||||
| ✅ | P2 | A9 | MiniApp progress 没有进入可恢复的 Tool 状态记录 | `miniapp-tool-state.ts`、`lineup-runtime.test.ts` | 已在 Tool 记录保存最后一次 `percent/status/detail`,ConversationStore 随记录持久化;Runtime 重启后可恢复,且补充状态机和重启回归测试。 |
|
| ✅ | P2 | A9 | MiniApp progress 没有进入可恢复的 Tool 状态记录 | `miniapp-tool-state.ts`、`lineup-runtime.test.ts` | 已在 Tool 记录保存最后一次 `percent/status/detail`,ConversationStore 随记录持久化;Runtime 重启后可恢复,且补充状态机和重启回归测试。 |
|
||||||
| ✅ | P3 | A10 | App session 记录和 opened/closed 事件没有显式保存 `agent_id` / `conversation_id` 字段 | `lineup-runtime.ts`、`lineup-runtime.test.ts` | opened/closed 事件现在都带 `agent_id`、`conversation_id`、`app_session_id`、`instance_id`、`app_scope`;并补充生命周期回归测试。 |
|
| ✅ | P3 | A10 | App session 记录和 opened/closed 事件没有显式保存 `agent_id` / `conversation_id` 字段 | `lineup-runtime.ts`、`lineup-runtime.test.ts` | opened/closed 事件现在都带 `agent_id`、`conversation_id`、`app_session_id`、`instance_id`、`app_scope`;并补充生命周期回归测试。 |
|
||||||
| 🔴 | P3 | A11 | 旧的 `openExtensionApp` 公开入口仍提供较宽的旁路能力 | `lineup-runtime.ts:286-300` | P3 也必须在本迭代处理:删除公开旁路,或让它只返回受统一 SDK v1 约束的适配器。 |
|
| ✅ | P3 | A11 | 旧的 `openExtensionApp` 公开入口仍提供较宽的旁路能力 | `lineup-runtime.ts`、`app-sdk.ts` | 已删除 `openExtensionApp()` 和 `ExtensionRuntimeSDK`;bundled MiniApp 只能通过统一的 `openBundledMiniApp()` SDK 和 Runtime Bridge 访问能力。旧测试已改为验证统一生命周期路径。 |
|
||||||
|
|
||||||
## 1. 已验证通过的部分
|
## 1. 已验证通过的部分
|
||||||
|
|
||||||
@@ -96,10 +96,10 @@ Interact 仍然可以使用可信 DOM,但它的 SDK 已明确拆成两层:`s
|
|||||||
基础构建与自动化测试:通过
|
基础构建与自动化测试:通过
|
||||||
真实登录和消息发送:通过
|
真实登录和消息发送:通过
|
||||||
App 架构边界:部分通过(A1/A2/A5/A6 已实现,A2 待浏览器复验)
|
App 架构边界:部分通过(A1/A2/A5/A6 已实现,A2 待浏览器复验)
|
||||||
MiniApp 隔离与 SDK 规范:A1~A10 已通过,仍需处理后续 P3
|
MiniApp 隔离与 SDK 规范:A1~A11 已通过。
|
||||||
标准交互终态契约:通过
|
标准交互终态契约:通过
|
||||||
迭代整体验收:不通过,继续处理 A8、A11;A2 需在最终浏览器复验中确认
|
迭代整体验收:不通过,剩余 A8 真实浏览器验收;A2 也需在最终浏览器复验中确认
|
||||||
```
|
```
|
||||||
|
|
||||||
本轮独立验收由子 agent 完成。A6 已在本轮修复并通过自动化验证,A7 已完成契约收敛并通过全量测试,A9 已完成进度持久化并通过重启回归,A10 已补齐生命周期事件上下文;下一轮继续处理 A8、A11,并重新执行
|
本轮独立验收由子 agent 完成。A6 已在本轮修复并通过自动化验证,A7 已完成契约收敛并通过全量测试,A9 已完成进度持久化并通过重启回归,A10 已补齐生命周期事件上下文,A11 已删除旧 SDK 旁路;下一轮只需完成 A8 的真实浏览器验收并重新执行独立验收。
|
||||||
独立验收。P2/P3 不能被静默删除,必须在本迭代结束前解决或保留明确的延期记录。
|
独立验收。P2/P3 不能被静默删除,必须在本迭代结束前解决或保留明确的延期记录。
|
||||||
|
|||||||
Reference in New Issue
Block a user