@springbrand/agent-runtime 0.2.0-alpha.67 → 0.2.0-alpha.69
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 +3 -3
- package/src/adapter/cloudflare/subagent/runner.ts +23 -22
- package/src/adapter/cloudflare/subagent/tools.ts +1 -1
- package/src/adapter/cloudflare/universal-agent/preparation.ts +0 -7
- package/src/db/schema.ts +0 -12
- package/src/db/submission-admission.repo.ts +5 -20
- package/src/db/submission.repo.ts +5 -21
- package/src/kernel/bindings.ts +8 -7
- package/src/kernel/durable-lifecycle.ts +0 -9
- package/src/kernel/profile.ts +3 -0
- package/src/kernel/public-contracts.ts +0 -1
- package/src/kernel/subagent-runtime.ts +2 -12
- package/src/kernel/submission-authority.ts +4 -24
- package/src/model-usage.ts +52 -0
- package/src/pi/assembly/context.ts +26 -60
- package/src/pi/runtime-adapter/assembly.ts +6 -2
- package/src/pi/runtime-adapter/execution.ts +78 -114
- package/src/pi/runtime-adapter/index.ts +3 -4
- package/src/pi/runtime-adapter/transcript.ts +31 -46
- package/src/pi/session/index.ts +22 -22
- package/src/pi/session/migration.ts +646 -0
- package/src/pi/session/storage.ts +523 -311
- package/src/pi/tool/compiler.ts +2 -2
- package/src/pi/tool/core-host.ts +29 -7
- package/src/pi/tool/mcp.ts +23 -15
- package/src/pi/tool/subagent.ts +0 -9
- package/src/pi/tool/workspace-state-connector.ts +23 -20
- package/src/runtime-agent.ts +11 -0
- package/src/runtime-assembler.ts +13 -2
- package/src/runtime-definition.ts +2 -0
- package/src/runtime.ts +24 -83
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SnapshotMessage
|
|
1
|
+
import type { SnapshotMessage } from "agents/chat";
|
|
2
2
|
import type {
|
|
3
3
|
AgentMessage,
|
|
4
4
|
CustomMessage,
|
|
@@ -21,12 +21,7 @@ import {
|
|
|
21
21
|
type PiToolApprovalView,
|
|
22
22
|
type UIUserSidecar,
|
|
23
23
|
} from "../message";
|
|
24
|
-
import {
|
|
25
|
-
appendSessionMessageSync,
|
|
26
|
-
clearSessionSync,
|
|
27
|
-
PiTranscriptStore,
|
|
28
|
-
moveSessionLeafSync,
|
|
29
|
-
} from "../session";
|
|
24
|
+
import { openPiSessionStorage, type CloudflareSessionStorage } from "../session";
|
|
30
25
|
import type { PiCanonicalMessageCommit } from "./execution";
|
|
31
26
|
import type { PiRecoveredToolSettlement } from "./recovery";
|
|
32
27
|
import type { RuntimeModelUsageEvent } from "../../kernel/bindings";
|
|
@@ -199,7 +194,7 @@ function hostText(message: UserMessage | AssistantMessage): string {
|
|
|
199
194
|
* Pi Session 保存规范分支,Runtime 数据库保存 Submission、审批和用户 sidecar;分开保存可避免把 UI 字段写进模型上下文。
|
|
200
195
|
*/
|
|
201
196
|
export class PiRuntimeTranscript {
|
|
202
|
-
private readonly store:
|
|
197
|
+
private readonly store: CloudflareSessionStorage;
|
|
203
198
|
|
|
204
199
|
private modelBranch() {
|
|
205
200
|
return this.store.branch().filter((entry) =>
|
|
@@ -215,15 +210,15 @@ export class PiRuntimeTranscript {
|
|
|
215
210
|
*
|
|
216
211
|
* `PiRuntimeAdapter.createTranscript` 每个 Runtime Kernel 构造时调用一次;`hasActiveTurn` 必须反映当前 Submission 生命周期。
|
|
217
212
|
*
|
|
218
|
-
*
|
|
213
|
+
* 官方 Storage 契约由同一 DO SQLite 实现;模型目录用于压缩,活动 Turn 检查阻止并发导入。
|
|
219
214
|
*/
|
|
220
215
|
constructor(
|
|
221
|
-
|
|
216
|
+
storage: DurableObjectStorage,
|
|
222
217
|
private readonly durability: PiTranscriptDurability,
|
|
223
218
|
private readonly models: Models,
|
|
224
219
|
private readonly hasActiveTurn: () => boolean,
|
|
225
220
|
) {
|
|
226
|
-
this.store =
|
|
221
|
+
this.store = openPiSessionStorage(storage);
|
|
227
222
|
}
|
|
228
223
|
|
|
229
224
|
// #region 规范消息写入
|
|
@@ -240,19 +235,15 @@ export class PiRuntimeTranscript {
|
|
|
240
235
|
message: AgentMessage,
|
|
241
236
|
options: {
|
|
242
237
|
submissionId?: string;
|
|
243
|
-
createdAt?: number;
|
|
244
238
|
userMessage?: UIMessage & { role: "user" };
|
|
245
239
|
} = {},
|
|
246
240
|
): boolean {
|
|
247
|
-
const inserted =
|
|
241
|
+
const inserted = this.store.appendMessage({
|
|
248
242
|
id,
|
|
249
243
|
message,
|
|
250
244
|
...(options.submissionId
|
|
251
245
|
? { submissionId: options.submissionId }
|
|
252
246
|
: {}),
|
|
253
|
-
...(options.createdAt === undefined
|
|
254
|
-
? {}
|
|
255
|
-
: { createdAt: options.createdAt }),
|
|
256
247
|
});
|
|
257
248
|
if (inserted && options.userMessage) {
|
|
258
249
|
const sidecar = captureUIUserSidecar(options.userMessage);
|
|
@@ -280,7 +271,6 @@ export class PiRuntimeTranscript {
|
|
|
280
271
|
id,
|
|
281
272
|
inserted: this.append(id, message, {
|
|
282
273
|
submissionId,
|
|
283
|
-
createdAt: message.timestamp,
|
|
284
274
|
}),
|
|
285
275
|
};
|
|
286
276
|
}
|
|
@@ -301,7 +291,6 @@ export class PiRuntimeTranscript {
|
|
|
301
291
|
id: commit.id,
|
|
302
292
|
inserted: this.append(commit.id, commit.message, {
|
|
303
293
|
submissionId,
|
|
304
|
-
createdAt: commit.message.timestamp,
|
|
305
294
|
}),
|
|
306
295
|
}
|
|
307
296
|
: this.commitTurnMessage(
|
|
@@ -329,7 +318,7 @@ export class PiRuntimeTranscript {
|
|
|
329
318
|
}],
|
|
330
319
|
display: true,
|
|
331
320
|
timestamp,
|
|
332
|
-
} satisfies CustomMessage, { submissionId
|
|
321
|
+
} satisfies CustomMessage, { submissionId });
|
|
333
322
|
}
|
|
334
323
|
|
|
335
324
|
/**
|
|
@@ -348,7 +337,6 @@ export class PiRuntimeTranscript {
|
|
|
348
337
|
settlement.message,
|
|
349
338
|
{
|
|
350
339
|
submissionId,
|
|
351
|
-
createdAt: settlement.message.timestamp,
|
|
352
340
|
},
|
|
353
341
|
);
|
|
354
342
|
}
|
|
@@ -434,7 +422,7 @@ export class PiRuntimeTranscript {
|
|
|
434
422
|
* 这里委托同步 Session 存储辅助函数写入叶子标记,不删除原分支,保留 Pi 的分支结构。
|
|
435
423
|
*/
|
|
436
424
|
moveLeaf(id: string): void {
|
|
437
|
-
|
|
425
|
+
this.store.moveBranchTip(id);
|
|
438
426
|
}
|
|
439
427
|
|
|
440
428
|
/**
|
|
@@ -452,7 +440,7 @@ export class PiRuntimeTranscript {
|
|
|
452
440
|
model: Model<Api>;
|
|
453
441
|
signal?: AbortSignal;
|
|
454
442
|
submissionId?: string;
|
|
455
|
-
|
|
443
|
+
onCompactionUsage?: (event: RuntimeModelUsageEvent, didCompact: boolean) => void;
|
|
456
444
|
},
|
|
457
445
|
): Promise<AgentMessage[]> {
|
|
458
446
|
const branch = this.modelBranch();
|
|
@@ -465,6 +453,9 @@ export class PiRuntimeTranscript {
|
|
|
465
453
|
JSON.stringify(message) !== JSON.stringify(messages[index])
|
|
466
454
|
)
|
|
467
455
|
) {
|
|
456
|
+
if (options.force) {
|
|
457
|
+
throw new Error("Forced context compaction requires canonical messages");
|
|
458
|
+
}
|
|
468
459
|
console.warn(
|
|
469
460
|
"[runtime-compaction:degraded]",
|
|
470
461
|
JSON.stringify({ reason: "canonical-message-mismatch" }),
|
|
@@ -479,29 +470,22 @@ export class PiRuntimeTranscript {
|
|
|
479
470
|
model: options.model,
|
|
480
471
|
signal: options.signal,
|
|
481
472
|
});
|
|
482
|
-
if (
|
|
483
|
-
throw new Error("Forced context compaction did not reduce the model input");
|
|
484
|
-
}
|
|
485
|
-
if (result.compactionEntry) {
|
|
486
|
-
// 整对象写入:assembly 侧已经造好了完整的 compaction 条目,这里不再拆成位置
|
|
487
|
-
// 参数。旧写法拆 5 个参数喂 pi 的 Session.appendCompaction,pi 0.81 把它加到
|
|
488
|
-
// 7 个参数(usage / retainedTail)之后,多出来的会被静默丢掉且编译器不报。
|
|
473
|
+
if (result.compactionEntry || result.modelUsage) {
|
|
489
474
|
this.durability.transaction(() => {
|
|
490
|
-
const eventId = this.store.
|
|
491
|
-
if (
|
|
492
|
-
options.
|
|
493
|
-
options.onCompactionPersisted &&
|
|
494
|
-
result.modelUsage
|
|
495
|
-
) {
|
|
496
|
-
options.onCompactionPersisted({
|
|
475
|
+
const eventId = this.store.recordCompaction(result.compactionEntry, result.modelUsage?.usage);
|
|
476
|
+
if (options.submissionId && options.onCompactionUsage && result.modelUsage) {
|
|
477
|
+
options.onCompactionUsage({
|
|
497
478
|
eventId,
|
|
498
479
|
submissionId: options.submissionId,
|
|
499
480
|
kind: "compaction",
|
|
500
481
|
...result.modelUsage,
|
|
501
|
-
});
|
|
482
|
+
}, result.didCompact);
|
|
502
483
|
}
|
|
503
484
|
});
|
|
504
485
|
}
|
|
486
|
+
if (options.force && !result.didCompact) {
|
|
487
|
+
throw new Error("Forced context compaction did not reduce the model input");
|
|
488
|
+
}
|
|
505
489
|
if (result.degraded) {
|
|
506
490
|
console.warn(
|
|
507
491
|
"[runtime-compaction:degraded]",
|
|
@@ -532,7 +516,6 @@ export class PiRuntimeTranscript {
|
|
|
532
516
|
}
|
|
533
517
|
const appended = this.append(entry.id, entry.message, {
|
|
534
518
|
submissionId: currentSubmissionId,
|
|
535
|
-
createdAt: entry.createdAt,
|
|
536
519
|
...(entry.message.role === "user" && entry.userSidecar
|
|
537
520
|
? {
|
|
538
521
|
userMessage: {
|
|
@@ -607,7 +590,7 @@ export class PiRuntimeTranscript {
|
|
|
607
590
|
* 两类 Transcript 数据必须一起删除,否则复用消息 ID 时可能重新读到旧附件或 UI 元数据。
|
|
608
591
|
*/
|
|
609
592
|
clear(): void {
|
|
610
|
-
|
|
593
|
+
this.store.clear();
|
|
611
594
|
this.durability.clearUserSidecars();
|
|
612
595
|
}
|
|
613
596
|
|
|
@@ -720,6 +703,7 @@ export class PiRuntimeTranscript {
|
|
|
720
703
|
parts: [],
|
|
721
704
|
metadata,
|
|
722
705
|
});
|
|
706
|
+
activeAssistant = { submissionId: entry.submissionId, index: result.length - 1 };
|
|
723
707
|
} else {
|
|
724
708
|
const previous = result[index]!;
|
|
725
709
|
result[index] = {
|
|
@@ -730,7 +714,8 @@ export class PiRuntimeTranscript {
|
|
|
730
714
|
},
|
|
731
715
|
};
|
|
732
716
|
}
|
|
733
|
-
|
|
717
|
+
// A stream may settle after the stop marker. Keep its existing UI identity
|
|
718
|
+
// until the next user message rather than creating a second assistant.
|
|
734
719
|
continue;
|
|
735
720
|
}
|
|
736
721
|
if (entry.message.role === "assistant") {
|
|
@@ -787,7 +772,10 @@ export class PiRuntimeTranscript {
|
|
|
787
772
|
{ type: "step-start" },
|
|
788
773
|
...projected.parts,
|
|
789
774
|
],
|
|
790
|
-
metadata:
|
|
775
|
+
metadata: {
|
|
776
|
+
...(previous.metadata as Record<string, unknown> | undefined),
|
|
777
|
+
...(projected.metadata as Record<string, unknown> | undefined),
|
|
778
|
+
},
|
|
791
779
|
};
|
|
792
780
|
}
|
|
793
781
|
continue;
|
|
@@ -861,21 +849,18 @@ export class PiRuntimeTranscript {
|
|
|
861
849
|
*
|
|
862
850
|
* 浏览器投影、Host 历史、Snapshot 以及 Turn 启动时的助手序号计算都从这里读取,调用方无需再查关联表。
|
|
863
851
|
*
|
|
864
|
-
*
|
|
852
|
+
* UI uses the message event time; Pi entry timestamps describe storage commits.
|
|
865
853
|
*/
|
|
866
854
|
async storedMessages(): Promise<StoredTranscriptMessage[]> {
|
|
867
855
|
const submissionByMessage =
|
|
868
856
|
this.durability.listMessageSubmissionLinks();
|
|
869
857
|
return this.store.branch().flatMap((entry) => {
|
|
870
858
|
if (entry.type !== "message") return [];
|
|
871
|
-
const parsedTimestamp = Date.parse(entry.timestamp);
|
|
872
859
|
return [{
|
|
873
860
|
id: entry.id,
|
|
874
861
|
submissionId: submissionByMessage.get(entry.id) ?? null,
|
|
875
862
|
message: entry.message,
|
|
876
|
-
createdAt:
|
|
877
|
-
? parsedTimestamp
|
|
878
|
-
: entry.message.timestamp,
|
|
863
|
+
createdAt: entry.message.timestamp,
|
|
879
864
|
}];
|
|
880
865
|
});
|
|
881
866
|
}
|
package/src/pi/session/index.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
PiTranscriptStore,
|
|
4
|
-
appendSessionMessageSync,
|
|
5
|
-
clearSessionSync,
|
|
6
|
-
moveSessionLeafSync,
|
|
7
|
-
} from "./storage";
|
|
1
|
+
import { migrateLegacyPiSession } from "./migration";
|
|
2
|
+
import { CloudflareSessionStorage } from "./storage";
|
|
8
3
|
|
|
9
|
-
export
|
|
10
|
-
appendMessage: appendSessionMessageSync,
|
|
11
|
-
clear: clearSessionSync,
|
|
12
|
-
/**
|
|
13
|
-
* 建库并返回回合事实源。
|
|
14
|
-
*
|
|
15
|
-
* 这里不再构造 pi 的 `Session` —— 会话所有权归这个 Durable Object,
|
|
16
|
-
* pi 只在需要压缩时被当作纯函数调用。理由见 `PiTranscriptStore` 的注释。
|
|
17
|
-
*/
|
|
18
|
-
create(sql: SqlTaggedTemplate) {
|
|
19
|
-
return { store: new PiTranscriptStore(sql) };
|
|
20
|
-
},
|
|
21
|
-
moveLeaf: moveSessionLeafSync,
|
|
22
|
-
});
|
|
4
|
+
export { CloudflareSessionStorage } from "./storage";
|
|
23
5
|
|
|
24
|
-
|
|
6
|
+
/** Open only after the startup transaction has installed the current database format. */
|
|
7
|
+
export function openPiSessionStorage(
|
|
8
|
+
storage: DurableObjectStorage,
|
|
9
|
+
): CloudflareSessionStorage {
|
|
10
|
+
return storage.transactionSync(() => {
|
|
11
|
+
const tables = new Set(
|
|
12
|
+
storage.sql
|
|
13
|
+
.exec<{ name: string }>(
|
|
14
|
+
"SELECT name FROM sqlite_master WHERE type = 'table' AND name IN ('pi_storage_state', 'pi_session_entries')",
|
|
15
|
+
)
|
|
16
|
+
.toArray()
|
|
17
|
+
.map((row) => row.name),
|
|
18
|
+
);
|
|
19
|
+
if (!tables.has("pi_storage_state") && tables.has("pi_session_entries")) {
|
|
20
|
+
migrateLegacyPiSession(storage, { dryRun: false });
|
|
21
|
+
}
|
|
22
|
+
return new CloudflareSessionStorage(storage);
|
|
23
|
+
});
|
|
24
|
+
}
|