@springbrand/agent-runtime 0.1.3-alpha.1 → 0.1.3-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -3
- package/src/adapter/cloudflare/index.ts +56 -0
- package/src/adapter/cloudflare/resources/runtime-resources.ts +89 -0
- package/src/adapter/cloudflare/sandbox/adapter.ts +1513 -0
- package/src/adapter/cloudflare/sandbox/id.ts +23 -0
- package/src/adapter/cloudflare/sandbox/policy.ts +15 -0
- package/src/adapter/cloudflare/subagent/definition.ts +574 -0
- package/src/adapter/cloudflare/subagent/runner.ts +175 -0
- package/src/adapter/cloudflare/subagent/tools.ts +254 -0
- package/src/adapter/cloudflare/universal-agent/hooks.ts +35 -0
- package/src/adapter/cloudflare/universal-agent/preparation.ts +277 -0
- package/src/adapter/cloudflare/universal-agent/tools.ts +80 -0
- package/src/adapter/cloudflare/workspace/git-fs.ts +178 -0
- package/src/adapter/cloudflare/workspace/publisher.ts +31 -0
- package/src/adapter/cloudflare/workspace/scoped-workspace.ts +376 -0
- package/src/adapter/cloudflare/workspace/version-control.ts +374 -0
- package/src/agent-tool-runtime.ts +152 -0
- package/src/db/agent-tool.repo.ts +27 -0
- package/src/db/index.ts +33 -0
- package/src/db/interaction.repo.ts +185 -0
- package/src/db/schema.ts +25 -1
- package/src/db/submission.repo.ts +63 -1
- package/src/index.ts +57 -21
- package/src/kernel/approval-lifecycle.ts +41 -6
- package/src/kernel/bindings.ts +73 -9
- package/src/kernel/interaction-lifecycle.ts +395 -0
- package/src/kernel/public-contracts.ts +2 -0
- package/src/kernel/recoverable-chat-agent.ts +104 -6
- package/src/kernel/runtime-assembly-view.ts +37 -0
- package/src/kernel/runtime-assembly.ts +41 -0
- package/src/kernel/runtime-config.ts +4 -0
- package/src/kernel/runtime-load.ts +191 -0
- package/src/kernel/state.ts +12 -1
- package/src/kernel/submission-lifecycle.ts +33 -2
- package/src/layers/orchestration/temporary-agent/core.ts +12 -1
- package/src/layers/orchestration/temporary-agent/runner.ts +1 -2
- package/src/lib/mcp.ts +7 -3
- package/src/lib/prompt.ts +1 -1
- package/src/lib/telemetry-dev.ts +7 -4
- package/src/pi/assembly/context.ts +3 -3
- package/src/pi/assembly/extensions.ts +11 -22
- package/src/pi/assembly/snapshot.ts +6 -3
- package/src/pi/message/contract.ts +7 -0
- package/src/pi/message/conversion.ts +9 -1
- package/src/pi/runtime-adapter/assembly.ts +26 -31
- package/src/pi/runtime-adapter/execution.ts +198 -15
- package/src/pi/runtime-adapter/index.ts +24 -8
- package/src/pi/runtime-adapter/models.ts +382 -35
- package/src/pi/runtime-adapter/recovery.ts +188 -1
- package/src/pi/runtime-adapter/transcript.ts +61 -3
- package/src/pi/tool/ai-adapter.ts +58 -1
- package/src/pi/tool/base.ts +190 -12
- package/src/pi/tool/compiler.ts +34 -1
- package/src/pi/tool/core-host.ts +19 -24
- package/src/pi/tool/core.ts +30 -120
- package/src/pi/tool/gateway.ts +54 -0
- package/src/pi/tool/index.ts +2 -0
- package/src/pi/tool/mcp.ts +96 -68
- package/src/pi/tool/schedule.ts +41 -19
- package/src/pi/tool/skill.ts +126 -420
- package/src/pi/tool/subagent.ts +14 -2
- package/src/pi/tool/web-fetch.ts +281 -0
- package/src/pi/tool/web-search/api.ts +34 -18
- package/src/pi/tool/web-search/web-search.ts +0 -1
- package/src/pi/tool/workspace-revision.ts +64 -0
- package/src/pi/tool/workspace-sandbox.ts +105 -263
- package/src/pi/turn/index.ts +20 -0
- package/src/pi/turn/interaction.ts +181 -0
- package/src/pi/turn/tool-recovery.ts +244 -1
- package/src/runtime-agent-context.ts +112 -0
- package/src/runtime-agent.ts +568 -321
- package/src/{plugins.ts → runtime-assembler.ts} +372 -398
- package/src/runtime-definition.ts +175 -0
- package/src/runtime.ts +835 -204
- package/src/tool-registry.ts +143 -0
- package/src/workspace-versioning.ts +46 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import type { SqlTaggedTemplate } from "agents/chat";
|
|
2
|
+
|
|
3
|
+
// Tool Interaction 的统一定义见 ./index.ts。
|
|
4
|
+
// #region 状态与行类型
|
|
5
|
+
export type ToolInteractionStatus = "pending" | "responded" | "cancelled";
|
|
6
|
+
|
|
7
|
+
export interface StoredToolInteraction {
|
|
8
|
+
interactionId: string;
|
|
9
|
+
submissionId: string;
|
|
10
|
+
requestId: string;
|
|
11
|
+
toolCallId: string;
|
|
12
|
+
toolName: string;
|
|
13
|
+
inputJson: string;
|
|
14
|
+
status: ToolInteractionStatus;
|
|
15
|
+
responseJson: string | null;
|
|
16
|
+
createdAt: number;
|
|
17
|
+
respondedAt: number | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface NewToolInteraction {
|
|
21
|
+
interactionId: string;
|
|
22
|
+
submissionId: string;
|
|
23
|
+
requestId: string;
|
|
24
|
+
toolCallId: string;
|
|
25
|
+
toolName: string;
|
|
26
|
+
inputJson: string;
|
|
27
|
+
status: ToolInteractionStatus;
|
|
28
|
+
createdAt: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type ToolInteractionRow = {
|
|
32
|
+
interaction_id: string;
|
|
33
|
+
submission_id: string;
|
|
34
|
+
request_id: string;
|
|
35
|
+
tool_call_id: string;
|
|
36
|
+
tool_name: string;
|
|
37
|
+
input_json: string;
|
|
38
|
+
status: string;
|
|
39
|
+
response_json: string | null;
|
|
40
|
+
created_at: number;
|
|
41
|
+
responded_at: number | null;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// #endregion
|
|
45
|
+
// #region 行映射
|
|
46
|
+
// 把 SQLite 的 Interaction 行转成 Runtime 对象。
|
|
47
|
+
// find 与 findPending* 查到记录后调用,上层因而不需要理解 snake_case 列名。
|
|
48
|
+
// 状态由表内字符串转成联合类型;新增列或状态时必须同步核对类型、SELECT 和映射。
|
|
49
|
+
function mapRow(row: ToolInteractionRow): StoredToolInteraction {
|
|
50
|
+
return {
|
|
51
|
+
interactionId: row.interaction_id,
|
|
52
|
+
submissionId: row.submission_id,
|
|
53
|
+
requestId: row.request_id,
|
|
54
|
+
toolCallId: row.tool_call_id,
|
|
55
|
+
toolName: row.tool_name,
|
|
56
|
+
inputJson: row.input_json,
|
|
57
|
+
status: row.status as ToolInteractionStatus,
|
|
58
|
+
responseJson: row.response_json,
|
|
59
|
+
createdAt: row.created_at,
|
|
60
|
+
respondedAt: row.responded_at,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// #endregion
|
|
65
|
+
// #region Repository
|
|
66
|
+
export class ToolInteractionRepository {
|
|
67
|
+
// 保存当前 Agent SQLite 的查询入口。
|
|
68
|
+
// RuntimeDatabase 构造时调用,InteractionLifecycle 随后共享这个 Repository。
|
|
69
|
+
// Interaction 必须与所属 Submission 在同一 Agent 存储中查询,不能跨 Durable Object 拼接状态。
|
|
70
|
+
constructor(private readonly sql: SqlTaggedTemplate) {}
|
|
71
|
+
|
|
72
|
+
// 按 interactionId 读取一条完整 Interaction,不存在时返回 null。
|
|
73
|
+
// InteractionLifecycle 在请求去重、响应、取消和收尾时调用,调用方必须处理 null。
|
|
74
|
+
// interaction_id 是主键,LIMIT 1 明确只消费一行。
|
|
75
|
+
find(interactionId: string): StoredToolInteraction | null {
|
|
76
|
+
const row = this.sql<ToolInteractionRow>`
|
|
77
|
+
SELECT interaction_id, submission_id, request_id,
|
|
78
|
+
tool_call_id, tool_name, input_json,
|
|
79
|
+
status, response_json, created_at, responded_at
|
|
80
|
+
FROM pi_tool_interactions
|
|
81
|
+
WHERE interaction_id = ${interactionId}
|
|
82
|
+
LIMIT 1
|
|
83
|
+
`[0];
|
|
84
|
+
return row ? mapRow(row) : null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 持久化一条新的 pending Interaction。
|
|
88
|
+
// InteractionLifecycle.ensurePending 在准入事务内确认没有同一记录后调用。
|
|
89
|
+
// 主键和 (submission_id, tool_call_id) 唯一约束是持久化去重防线,不能用覆盖式 INSERT 掩盖冲突。
|
|
90
|
+
insert(interaction: NewToolInteraction): void {
|
|
91
|
+
this.sql`
|
|
92
|
+
INSERT INTO pi_tool_interactions (
|
|
93
|
+
interaction_id, submission_id, request_id,
|
|
94
|
+
tool_call_id, tool_name, input_json,
|
|
95
|
+
status, created_at
|
|
96
|
+
) VALUES (
|
|
97
|
+
${interaction.interactionId},
|
|
98
|
+
${interaction.submissionId},
|
|
99
|
+
${interaction.requestId},
|
|
100
|
+
${interaction.toolCallId},
|
|
101
|
+
${interaction.toolName},
|
|
102
|
+
${interaction.inputJson},
|
|
103
|
+
${interaction.status},
|
|
104
|
+
${interaction.createdAt}
|
|
105
|
+
)
|
|
106
|
+
`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 只把 pending Interaction 落成 responded 或 cancelled,并记录响应体与时间。
|
|
110
|
+
// InteractionLifecycle 在收到客户端响应或判定取消时调用,迟到的重复投递不应覆盖首次结果。
|
|
111
|
+
// WHERE status='pending' 实现首次响应胜出,不能去掉这个条件;调用方在事务后重读来确认是否真的生效。
|
|
112
|
+
settle(
|
|
113
|
+
interactionId: string,
|
|
114
|
+
status: Exclude<ToolInteractionStatus, "pending">,
|
|
115
|
+
respondedAt: number,
|
|
116
|
+
responseJson?: string,
|
|
117
|
+
): void {
|
|
118
|
+
this.sql`
|
|
119
|
+
UPDATE pi_tool_interactions
|
|
120
|
+
SET status = ${status},
|
|
121
|
+
response_json = ${responseJson ?? null},
|
|
122
|
+
responded_at = ${respondedAt}
|
|
123
|
+
WHERE interaction_id = ${interactionId}
|
|
124
|
+
AND status = 'pending'
|
|
125
|
+
`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 按创建顺序列出某次 Submission 仍等待客户端响应的 interactionId。
|
|
129
|
+
// InteractionLifecycle.cancelPending 在取消或终止 Turn 时调用,随后逐条追加取消恢复事实。
|
|
130
|
+
// ORDER BY created_at 保留原始顺序,不能依赖 SQLite 未声明的默认行顺序。
|
|
131
|
+
listPendingForSubmission(submissionId: string): string[] {
|
|
132
|
+
return this.sql<{ interaction_id: string }>`
|
|
133
|
+
SELECT interaction_id
|
|
134
|
+
FROM pi_tool_interactions
|
|
135
|
+
WHERE submission_id = ${submissionId}
|
|
136
|
+
AND status = 'pending'
|
|
137
|
+
ORDER BY created_at ASC
|
|
138
|
+
`.map((row) => row.interaction_id);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 只在某个 toolCallId 唯一匹配一条活跃 pending Interaction 时返回该记录。
|
|
142
|
+
// Runtime.respondToolInteraction 的入口调用它 —— 前端只有 toolCallId,不知道 submissionId。
|
|
143
|
+
// toolCallId 只在单个 Submission 内唯一,LIMIT 2 用最小查询识别跨 Submission 重名;
|
|
144
|
+
// 不能用 LIMIT 1 静默选中任意一条,那会把答案投给错的那次调用。口径与 ApprovalRepository 同名方法一致。
|
|
145
|
+
findPendingByToolCallId(toolCallId: string): StoredToolInteraction | null {
|
|
146
|
+
const matches = this.sql<ToolInteractionRow>`
|
|
147
|
+
SELECT interaction.interaction_id, interaction.submission_id,
|
|
148
|
+
interaction.request_id, interaction.tool_call_id,
|
|
149
|
+
interaction.tool_name, interaction.input_json,
|
|
150
|
+
interaction.status, interaction.response_json,
|
|
151
|
+
interaction.created_at, interaction.responded_at
|
|
152
|
+
FROM pi_tool_interactions interaction
|
|
153
|
+
JOIN pi_submissions submission
|
|
154
|
+
ON submission.submission_id = interaction.submission_id
|
|
155
|
+
WHERE interaction.tool_call_id = ${toolCallId}
|
|
156
|
+
AND interaction.status = 'pending'
|
|
157
|
+
AND submission.status NOT IN (
|
|
158
|
+
'completed', 'aborted', 'skipped', 'error'
|
|
159
|
+
)
|
|
160
|
+
LIMIT 2
|
|
161
|
+
`;
|
|
162
|
+
const only = matches.length === 1 ? matches[0] : undefined;
|
|
163
|
+
return only ? mapRow(only) : null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 列出所属 Submission 仍活跃的全部 pending Interaction。
|
|
167
|
+
// Runtime 启动扫描调用它,用来判断哪些 park 还需要续跑依据。
|
|
168
|
+
// 与 findPendingByToolCallId 使用同样的 JOIN 和状态条件,修改其一时必须核对另一个。
|
|
169
|
+
listPendingActive(): StoredToolInteraction[] {
|
|
170
|
+
return this.sql<ToolInteractionRow>`
|
|
171
|
+
SELECT interaction.interaction_id, interaction.submission_id,
|
|
172
|
+
interaction.request_id, interaction.tool_call_id,
|
|
173
|
+
interaction.tool_name, interaction.input_json,
|
|
174
|
+
interaction.status, interaction.response_json,
|
|
175
|
+
interaction.created_at, interaction.responded_at
|
|
176
|
+
FROM pi_tool_interactions interaction
|
|
177
|
+
JOIN pi_submissions submission
|
|
178
|
+
ON submission.submission_id = interaction.submission_id
|
|
179
|
+
WHERE interaction.status = 'pending'
|
|
180
|
+
AND submission.status IN ('pending', 'running')
|
|
181
|
+
ORDER BY interaction.created_at ASC
|
|
182
|
+
`.map(mapRow);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// #endregion
|
package/src/db/schema.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { SqlTaggedTemplate } from "agents/chat";
|
|
|
4
4
|
// 创建当前 Runtime 需要但尚不存在的 SQLite 表。
|
|
5
5
|
// AgentRuntimeKernel 构造时通过 RuntimeDatabase.initializeSchema 调用,重复启动依赖 IF NOT EXISTS 保留现有表。
|
|
6
6
|
// pi_approvals 有一次显式的 risk → 四档升级;步骤均可重入,中断后下次初始化会继续。
|
|
7
|
+
// pi_tool_interactions 是 client-settled tool 的 pending 索引:结果的权威副本仍在 pi_tool_settlements,
|
|
8
|
+
// 这张表只回答「这次工具调用还等不等得到客户端响应」。
|
|
7
9
|
export function initializeSchema(sql: SqlTaggedTemplate): void {
|
|
8
10
|
sql`CREATE TABLE IF NOT EXISTS pi_message_ui (
|
|
9
11
|
message_id TEXT PRIMARY KEY,
|
|
@@ -21,7 +23,9 @@ export function initializeSchema(sql: SqlTaggedTemplate): void {
|
|
|
21
23
|
assembly_revision TEXT NOT NULL,
|
|
22
24
|
assembly_descriptor TEXT NOT NULL,
|
|
23
25
|
assistant_message_id TEXT NOT NULL,
|
|
24
|
-
abort_reason TEXT
|
|
26
|
+
abort_reason TEXT,
|
|
27
|
+
recovery_error_count INTEGER NOT NULL DEFAULT 0,
|
|
28
|
+
recovery_reason TEXT
|
|
25
29
|
)`;
|
|
26
30
|
const submissionColumns = new Set(
|
|
27
31
|
sql<{ name: string }>`PRAGMA table_info(pi_submissions)`
|
|
@@ -39,6 +43,13 @@ export function initializeSchema(sql: SqlTaggedTemplate): void {
|
|
|
39
43
|
if (!submissionColumns.has("regenerate_message_id")) {
|
|
40
44
|
sql`ALTER TABLE pi_submissions ADD COLUMN regenerate_message_id TEXT`;
|
|
41
45
|
}
|
|
46
|
+
if (!submissionColumns.has("recovery_error_count")) {
|
|
47
|
+
sql`ALTER TABLE pi_submissions
|
|
48
|
+
ADD COLUMN recovery_error_count INTEGER NOT NULL DEFAULT 0`;
|
|
49
|
+
}
|
|
50
|
+
if (!submissionColumns.has("recovery_reason")) {
|
|
51
|
+
sql`ALTER TABLE pi_submissions ADD COLUMN recovery_reason TEXT`;
|
|
52
|
+
}
|
|
42
53
|
sql`CREATE UNIQUE INDEX IF NOT EXISTS pi_submissions_one_running
|
|
43
54
|
ON pi_submissions(status)
|
|
44
55
|
WHERE status = 'running'`;
|
|
@@ -110,6 +121,19 @@ export function initializeSchema(sql: SqlTaggedTemplate): void {
|
|
|
110
121
|
END`;
|
|
111
122
|
sql`ALTER TABLE pi_approvals DROP COLUMN risk`;
|
|
112
123
|
}
|
|
124
|
+
sql`CREATE TABLE IF NOT EXISTS pi_tool_interactions (
|
|
125
|
+
interaction_id TEXT PRIMARY KEY,
|
|
126
|
+
submission_id TEXT NOT NULL,
|
|
127
|
+
request_id TEXT NOT NULL,
|
|
128
|
+
tool_call_id TEXT NOT NULL,
|
|
129
|
+
tool_name TEXT NOT NULL,
|
|
130
|
+
input_json TEXT NOT NULL,
|
|
131
|
+
status TEXT NOT NULL,
|
|
132
|
+
response_json TEXT,
|
|
133
|
+
created_at INTEGER NOT NULL,
|
|
134
|
+
responded_at INTEGER,
|
|
135
|
+
UNIQUE (submission_id, tool_call_id)
|
|
136
|
+
)`;
|
|
113
137
|
sql`CREATE TABLE IF NOT EXISTS pi_recovery_milestones (
|
|
114
138
|
submission_id TEXT NOT NULL,
|
|
115
139
|
seq INTEGER NOT NULL,
|
|
@@ -9,6 +9,9 @@ export type SubmissionStatus =
|
|
|
9
9
|
| "aborted"
|
|
10
10
|
| "skipped"
|
|
11
11
|
| "error";
|
|
12
|
+
export type SubmissionRecoveryReason =
|
|
13
|
+
| "no_meaningful_model_progress"
|
|
14
|
+
| "transient_model_error";
|
|
12
15
|
|
|
13
16
|
const TERMINAL_SUBMISSION_STATUSES: ReadonlySet<SubmissionStatus> = new Set([
|
|
14
17
|
"completed",
|
|
@@ -43,6 +46,8 @@ export interface StoredSubmission {
|
|
|
43
46
|
queuedUiMessageJson: string | null;
|
|
44
47
|
userMessageId: string | null;
|
|
45
48
|
regenerateMessageId: string | null;
|
|
49
|
+
recoveryErrorCount: number;
|
|
50
|
+
recoveryReason: SubmissionRecoveryReason | null;
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
export interface NewSubmission {
|
|
@@ -76,6 +81,8 @@ type SubmissionRow = {
|
|
|
76
81
|
queued_ui_message_json: string | null;
|
|
77
82
|
user_message_id: string | null;
|
|
78
83
|
regenerate_message_id: string | null;
|
|
84
|
+
recovery_error_count: number;
|
|
85
|
+
recovery_reason: string | null;
|
|
79
86
|
};
|
|
80
87
|
|
|
81
88
|
// #endregion
|
|
@@ -101,6 +108,8 @@ function mapRow(row: SubmissionRow): StoredSubmission {
|
|
|
101
108
|
queuedUiMessageJson: row.queued_ui_message_json,
|
|
102
109
|
userMessageId: row.user_message_id,
|
|
103
110
|
regenerateMessageId: row.regenerate_message_id,
|
|
111
|
+
recoveryErrorCount: row.recovery_error_count,
|
|
112
|
+
recoveryReason: row.recovery_reason as SubmissionRecoveryReason | null,
|
|
104
113
|
};
|
|
105
114
|
}
|
|
106
115
|
|
|
@@ -121,7 +130,7 @@ export class SubmissionRepository {
|
|
|
121
130
|
error, created_at, completed_at, assembly_revision,
|
|
122
131
|
assembly_descriptor, assistant_message_id, abort_reason,
|
|
123
132
|
queued_input_json, queued_ui_message_json, user_message_id,
|
|
124
|
-
regenerate_message_id
|
|
133
|
+
regenerate_message_id, recovery_error_count, recovery_reason
|
|
125
134
|
FROM pi_submissions
|
|
126
135
|
WHERE submission_id = ${id}
|
|
127
136
|
`[0];
|
|
@@ -171,6 +180,17 @@ export class SubmissionRepository {
|
|
|
171
180
|
);
|
|
172
181
|
}
|
|
173
182
|
|
|
183
|
+
// 按创建顺序列出还在排队或运行的 Submission id。
|
|
184
|
+
// 换装配前的「是不是全都停在等人」判断调用它,逐条核对每一条的人机等待。
|
|
185
|
+
// 状态集合与 countUnfinished 必须一致,修改其一时另一个也要跟着改。
|
|
186
|
+
listUnfinishedIds(): string[] {
|
|
187
|
+
return this.sql<{ submission_id: string }>`
|
|
188
|
+
SELECT submission_id FROM pi_submissions
|
|
189
|
+
WHERE status IN ('pending', 'running')
|
|
190
|
+
ORDER BY created_at ASC, rowid ASC
|
|
191
|
+
`.map((row) => row.submission_id);
|
|
192
|
+
}
|
|
193
|
+
|
|
174
194
|
findRunning(): StoredSubmission | null {
|
|
175
195
|
const id = this.sql<{ submission_id: string }>`
|
|
176
196
|
SELECT submission_id FROM pi_submissions
|
|
@@ -243,6 +263,24 @@ export class SubmissionRepository {
|
|
|
243
263
|
return updated.length === 1;
|
|
244
264
|
}
|
|
245
265
|
|
|
266
|
+
// 把一条已 pin 的 Submission 换到另一对 revision / descriptor 上。
|
|
267
|
+
// Runtime 在「停在人机等待期间换了装配」之后调用,必须与那次装配迁移里程碑同事务。
|
|
268
|
+
// 与 pinAssembly 的区别就是它要求已经 pin 过:首次 pin 走 pinAssembly,
|
|
269
|
+
// 这里不接受空值,避免把一次漏写的准入 pin 伪装成一次迁移。
|
|
270
|
+
repinAssembly(id: string, revision: string, descriptor: string): boolean {
|
|
271
|
+
const updated = this.sql<{ submission_id: string }>`
|
|
272
|
+
UPDATE pi_submissions
|
|
273
|
+
SET assembly_revision = ${revision},
|
|
274
|
+
assembly_descriptor = ${descriptor}
|
|
275
|
+
WHERE submission_id = ${id}
|
|
276
|
+
AND assembly_revision <> ''
|
|
277
|
+
AND assembly_descriptor <> ''
|
|
278
|
+
AND status IN ('pending', 'running')
|
|
279
|
+
RETURNING submission_id
|
|
280
|
+
`;
|
|
281
|
+
return updated.length === 1;
|
|
282
|
+
}
|
|
283
|
+
|
|
246
284
|
// 只在当前状态属于 from 时把 Submission 切换到 to,否则返回 false。
|
|
247
285
|
// SubmissionLifecycle 开始 Turn 时调用,调用方用返回值识别过期或重复的状态切换。
|
|
248
286
|
// Cloudflare SQLite 查询是同步的,两条语句之间不会让其他事件交错;UPDATE 再次带旧状态条件,不能去掉这层幂等保护。
|
|
@@ -309,6 +347,30 @@ export class SubmissionRepository {
|
|
|
309
347
|
`;
|
|
310
348
|
}
|
|
311
349
|
|
|
350
|
+
// chatRecovery 遇到 partial 会重置 attempt;Submission 计数保证同一 Turn 的可恢复模型错误仍绝对封顶。
|
|
351
|
+
incrementRecoveryErrorCount(
|
|
352
|
+
id: string,
|
|
353
|
+
reason: SubmissionRecoveryReason,
|
|
354
|
+
): number {
|
|
355
|
+
return this.sql<{ recovery_error_count: number }>`
|
|
356
|
+
UPDATE pi_submissions
|
|
357
|
+
SET recovery_error_count = recovery_error_count + 1,
|
|
358
|
+
recovery_reason = ${reason}
|
|
359
|
+
WHERE submission_id = ${id}
|
|
360
|
+
AND status IN ('pending', 'running')
|
|
361
|
+
RETURNING recovery_error_count
|
|
362
|
+
`[0]?.recovery_error_count ?? 0;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
clearRecoveryReason(id: string): void {
|
|
366
|
+
this.sql`
|
|
367
|
+
UPDATE pi_submissions
|
|
368
|
+
SET recovery_reason = NULL
|
|
369
|
+
WHERE submission_id = ${id}
|
|
370
|
+
AND status IN ('pending', 'running')
|
|
371
|
+
`;
|
|
372
|
+
}
|
|
373
|
+
|
|
312
374
|
// 返回所有标准会话消息到 Submission 的关联映射。
|
|
313
375
|
// Transcript.storedMessages 读取 Pi Session 分支时调用,它用 Map 为每条消息补回 submissionId。
|
|
314
376
|
// 关联表由 Pi Session 存储拥有,本方法只读不写;不能在 RuntimeDatabase.clearAll 中单独删它而破坏 Transcript 的所有权。
|
package/src/index.ts
CHANGED
|
@@ -22,11 +22,10 @@
|
|
|
22
22
|
* - `SubAgent` 是由父 Agent 发起、但拥有独立执行上下文的子任务执行者。
|
|
23
23
|
* - `canonical transcript` 是 Pi 持久化和恢复 Turn 时使用的权威消息历史。
|
|
24
24
|
* - `admission` 是提示进入 Turn 前的接纳、去重和状态登记流程。
|
|
25
|
-
* - `
|
|
26
|
-
* - `
|
|
27
|
-
* - `
|
|
28
|
-
* - `
|
|
29
|
-
* - `Port` 是 Runtime 调用外部能力时依赖的最小接口。
|
|
25
|
+
* - `defineRuntimeAgent.load` 返回 config 和已加载 Resource。
|
|
26
|
+
* - `defineRuntimeAgent.tools` 是 Host 第一层 Tool 注册表声明。
|
|
27
|
+
* - `assembleRuntimeSnapshot` 消费该输入与 hooks,产出候选 Snapshot。
|
|
28
|
+
* - `Port` 是 Runtime 调用外部能力时依赖的最小接口(Definition 作者不直接装配)。
|
|
30
29
|
* - `RuntimeBindings` 是本次装配选中的 Port 和可执行对象集合。
|
|
31
30
|
* - `RuntimeBuilder` 是包内实现,用来校验贡献并生成候选结果。
|
|
32
31
|
* - `RuntimeCandidate` 是尚未生效的候选结果和提交前检查。
|
|
@@ -38,11 +37,8 @@
|
|
|
38
37
|
*
|
|
39
38
|
* Snapshot 不允许替换已选集合,但不承诺把每个 Port 的内部对象深冻结。
|
|
40
39
|
*
|
|
41
|
-
* 正常调用顺序是 `
|
|
42
|
-
*
|
|
43
|
-
* 应用只创建 `AgentConfig` 和 Plugin。
|
|
44
|
-
*
|
|
45
|
-
* Runtime 独占校验、排序和原子提交。
|
|
40
|
+
* 正常调用顺序是 `assemble → validate → freeze → guard → commit`。
|
|
41
|
+
* Runtime 独占跨能力校验和原子提交。
|
|
46
42
|
*
|
|
47
43
|
* @packageDocumentation
|
|
48
44
|
*/
|
|
@@ -50,23 +46,45 @@ export * from "./kernel/bindings";
|
|
|
50
46
|
export * from "./kernel/extensions";
|
|
51
47
|
export * from "./kernel/profile";
|
|
52
48
|
export * from "./kernel/receipts";
|
|
49
|
+
export * from "./kernel/runtime-config";
|
|
50
|
+
export type { RuntimeAssemblyView } from "./kernel/runtime-assembly-view";
|
|
53
51
|
export * from "./runtime-agent";
|
|
54
52
|
export * from "./kernel/state";
|
|
53
|
+
export * from "./workspace-versioning";
|
|
55
54
|
export * from "./lib/execution-level";
|
|
56
|
-
export { definePlugin } from "./plugins";
|
|
57
55
|
export type {
|
|
58
|
-
|
|
59
|
-
AgentPlugin,
|
|
60
|
-
AgentPluginSpec,
|
|
61
|
-
PluginKind,
|
|
62
|
-
PluginPreparation,
|
|
63
|
-
PreparedPlugin,
|
|
56
|
+
RuntimeAssemblyInput,
|
|
64
57
|
RuntimeDegradation,
|
|
65
58
|
RuntimeExtensionContribution,
|
|
66
59
|
RuntimeProfileContribution,
|
|
67
60
|
RuntimeSkillContribution,
|
|
68
61
|
RuntimeToolSurfacePolicy,
|
|
69
|
-
} from "./
|
|
62
|
+
} from "./runtime-assembler";
|
|
63
|
+
export { assembleRuntimeSnapshot, toolRegistryPiToolCandidates } from "./runtime-assembler";
|
|
64
|
+
export type {
|
|
65
|
+
PlatformToolContext,
|
|
66
|
+
PlatformToolRegistry,
|
|
67
|
+
PlatformToolSpec,
|
|
68
|
+
ProfileOverrides,
|
|
69
|
+
ResolvedConnectors,
|
|
70
|
+
ResolvedResources,
|
|
71
|
+
RuntimeAgentContext,
|
|
72
|
+
RuntimeAgentHooks,
|
|
73
|
+
RuntimeSettings,
|
|
74
|
+
RuntimeToolBindings,
|
|
75
|
+
ToolAssemblyResult,
|
|
76
|
+
ToolContext,
|
|
77
|
+
ToolRegistry,
|
|
78
|
+
ToolSpec,
|
|
79
|
+
} from "./runtime-definition";
|
|
80
|
+
export {
|
|
81
|
+
emptyPlatformToolRegistry,
|
|
82
|
+
emptyToolRegistry,
|
|
83
|
+
mergeToolRegistries,
|
|
84
|
+
normalizeToolAssembly,
|
|
85
|
+
piCandidateToToolSpec,
|
|
86
|
+
toolRegistryFromPiCandidates,
|
|
87
|
+
} from "./tool-registry";
|
|
70
88
|
export type { ModelOption } from "./lib/model-catalog";
|
|
71
89
|
export {
|
|
72
90
|
assembleSubagentPrompt,
|
|
@@ -92,16 +110,32 @@ export type {
|
|
|
92
110
|
SettledPiToolCall,
|
|
93
111
|
} from "./pi/tool";
|
|
94
112
|
export { basePiToolCandidates } from "./pi/tool";
|
|
95
|
-
export { schedulePiToolCandidates } from "./pi/tool";
|
|
96
113
|
export {
|
|
114
|
+
createMemoryTools,
|
|
115
|
+
memoryPiToolCandidate,
|
|
116
|
+
} from "./pi/tool";
|
|
117
|
+
export {
|
|
118
|
+
createScheduleTools,
|
|
119
|
+
schedulePiToolCandidates,
|
|
120
|
+
} from "./pi/tool";
|
|
121
|
+
export {
|
|
122
|
+
createSandboxTools,
|
|
123
|
+
createWorkspaceRevisionTools,
|
|
124
|
+
createWorkspaceTools,
|
|
97
125
|
sandboxPiToolCandidates,
|
|
126
|
+
workspaceRevisionPiToolCandidate,
|
|
98
127
|
workspacePiToolCandidates,
|
|
99
128
|
} from "./pi/tool";
|
|
100
|
-
export {
|
|
129
|
+
export {
|
|
130
|
+
createSubagentTools,
|
|
131
|
+
subagentPiToolCandidates,
|
|
132
|
+
} from "./pi/tool";
|
|
101
133
|
export { skillPiToolCandidates } from "./pi/tool";
|
|
102
134
|
export type { PiSkillBinding } from "./pi/tool";
|
|
103
135
|
export {
|
|
104
136
|
browserQuickActionPiToolCandidates,
|
|
137
|
+
createCodeExecutionTool,
|
|
138
|
+
codeExecutionPiToolCandidate,
|
|
105
139
|
} from "./pi/tool";
|
|
106
140
|
export {
|
|
107
141
|
createWorkspaceCodeExecutionPort,
|
|
@@ -113,10 +147,11 @@ export {
|
|
|
113
147
|
export type {
|
|
114
148
|
TemporaryAgentApprovalDecision,
|
|
115
149
|
TemporaryAgentApprovalRequest,
|
|
116
|
-
|
|
150
|
+
TemporaryAgentLaunch,
|
|
117
151
|
TemporaryAgentRequest,
|
|
118
152
|
TemporaryAgentRunContext,
|
|
119
153
|
} from "./layers/orchestration/temporary-agent/core";
|
|
154
|
+
export { TEMPORARY_AGENT_LAUNCH_KEY } from "./layers/orchestration/temporary-agent/core";
|
|
120
155
|
export {
|
|
121
156
|
temporaryAgentExtensionIsSafe,
|
|
122
157
|
temporaryAgentToolAllowed,
|
|
@@ -126,6 +161,7 @@ export {
|
|
|
126
161
|
} from "./layers/orchestration/temporary-agent/workspace";
|
|
127
162
|
export {
|
|
128
163
|
createPiModels,
|
|
164
|
+
modelRequestUrl,
|
|
129
165
|
resolvePiApiKey,
|
|
130
166
|
resolvePiModel,
|
|
131
167
|
} from "./pi/runtime-adapter/models";
|
|
@@ -101,7 +101,10 @@ interface ApprovalLifecycleOptions<
|
|
|
101
101
|
export class ApprovalLifecycle<
|
|
102
102
|
TSubmission extends ApprovalSubmission,
|
|
103
103
|
> {
|
|
104
|
-
private readonly waiters = new Map<
|
|
104
|
+
private readonly waiters = new Map<
|
|
105
|
+
string,
|
|
106
|
+
{ readonly release: () => void; readonly discard: () => void }
|
|
107
|
+
>();
|
|
105
108
|
|
|
106
109
|
/**
|
|
107
110
|
* 接好审批流程需要的宿主依赖。
|
|
@@ -217,6 +220,27 @@ export class ApprovalLifecycle<
|
|
|
217
220
|
return this.wait(pending.approval, signal);
|
|
218
221
|
}
|
|
219
222
|
|
|
223
|
+
/**
|
|
224
|
+
* 丢弃某条提交在内存里的全部审批等待器。
|
|
225
|
+
*
|
|
226
|
+
* @remarks
|
|
227
|
+
* 宿主在 park 期间换掉装配、中断内存执行器之后调用。持久审批一个字都不动:
|
|
228
|
+
* 被丢弃的只是「唤醒这一条执行器」的能力。
|
|
229
|
+
*
|
|
230
|
+
* 不丢弃的后果很具体:决定到来时 `decide` 会看见等待器,把 Turn 唤回那条已经
|
|
231
|
+
* 退场的旧执行器上,于是既拿不到新能力,也永远不会走持久续跑 —— Turn 就停在
|
|
232
|
+
* running 上不动了。等待器以 `PiTurnInterruptedError` 拒绝,工具编译器认得它,
|
|
233
|
+
* 不会为此写一条权威的错误结算。
|
|
234
|
+
*/
|
|
235
|
+
discardWaiters(submissionId: string): void {
|
|
236
|
+
for (
|
|
237
|
+
const executionId of this.options.db.approvals
|
|
238
|
+
.listPendingForSubmission(submissionId)
|
|
239
|
+
) {
|
|
240
|
+
this.waiters.get(executionId)?.discard();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
220
244
|
/**
|
|
221
245
|
* 在已批准工具结果落盘后,确认对应恢复调度已经完成。
|
|
222
246
|
*
|
|
@@ -364,7 +388,7 @@ export class ApprovalLifecycle<
|
|
|
364
388
|
await this.options.onApprovalsChanged();
|
|
365
389
|
|
|
366
390
|
if (waiter) {
|
|
367
|
-
waiter();
|
|
391
|
+
waiter.release();
|
|
368
392
|
return { ok: true };
|
|
369
393
|
}
|
|
370
394
|
|
|
@@ -540,10 +564,21 @@ export class ApprovalLifecycle<
|
|
|
540
564
|
return;
|
|
541
565
|
}
|
|
542
566
|
signal?.addEventListener("abort", abort, { once: true });
|
|
543
|
-
this.waiters.set(approval.executionId,
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
567
|
+
this.waiters.set(approval.executionId, {
|
|
568
|
+
release: () => {
|
|
569
|
+
signal?.removeEventListener("abort", abort);
|
|
570
|
+
this.waiters.delete(approval.executionId);
|
|
571
|
+
resolve();
|
|
572
|
+
},
|
|
573
|
+
// 只把等待器摘掉,不去 settle 这个 Promise。让它继续挂着,park 住的
|
|
574
|
+
// 那次 execute 就永远不会往下走 —— 无论是拒绝(Pi 会把它当成工具失败,
|
|
575
|
+
// 接着再要一次模型输出)还是放行(工具会真的跑一遍),都会让一条本该
|
|
576
|
+
// 退场的执行器继续做事。它所属的 adapter 已经被中止并丢弃,这个悬着的
|
|
577
|
+
// Promise 随它一起变成垃圾。
|
|
578
|
+
discard: () => {
|
|
579
|
+
signal?.removeEventListener("abort", abort);
|
|
580
|
+
this.waiters.delete(approval.executionId);
|
|
581
|
+
},
|
|
547
582
|
});
|
|
548
583
|
});
|
|
549
584
|
}
|