@springbrand/agent-runtime 0.3.0-alpha.20 → 0.3.0-alpha.22
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 +5 -5
- package/src/adapter/cloudflare/agent.ts +79 -80
- package/src/adapter/cloudflare/define-agent.ts +220 -93
- package/src/adapter/cloudflare/schema.ts +6 -2
- package/src/config/activation.ts +13 -15
- package/src/config/harness-runtime.ts +26 -36
- package/src/config/index.ts +1 -1
- package/src/config/{pinning.ts → prepared-runtime.ts} +20 -118
- package/src/config/runtime-config.ts +2 -1
- package/src/conversation/message/contract.ts +16 -4
- package/src/conversation/message/conversion.ts +43 -2
- package/src/harness/session-harness.ts +21 -6
- package/src/submission/admission.ts +8 -29
- package/src/submission/lifecycle.ts +38 -16
- package/src/submission/repository.ts +39 -30
- package/src/telemetry/contract.ts +0 -1
- package/src/telemetry/coordinator.ts +13 -3
- package/src/telemetry/runtime-telemetry.ts +0 -4
- package/src/tools/assistant/ask-user.ts +3 -1
- package/src/tools/extension/index.ts +2 -2
- package/src/tools/web/search/api.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@springbrand/agent-runtime",
|
|
3
|
-
"version": "0.3.0-alpha.
|
|
3
|
+
"version": "0.3.0-alpha.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"./models": "./src/lib/model-catalog.ts"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@cloudflare/codemode": "0.5.
|
|
20
|
+
"@cloudflare/codemode": "0.5.4",
|
|
21
21
|
"@cloudflare/sandbox": "0.12.4",
|
|
22
22
|
"@cloudflare/shell": "0.4.3",
|
|
23
|
-
"@cloudflare/think": "0.
|
|
23
|
+
"@cloudflare/think": "0.20.2",
|
|
24
24
|
"@earendil-works/pi-agent-core": "1.1.0",
|
|
25
25
|
"@earendil-works/pi-ai": "1.1.0",
|
|
26
26
|
"@types/lodash-es": "^4.17.12",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"@earendil-works/chord": "1.1.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"agents": "
|
|
35
|
+
"agents": ">=0.28.1 <0.29.0",
|
|
36
36
|
"ai": "^7.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"agents": "0.
|
|
39
|
+
"agents": "0.28.1",
|
|
40
40
|
"ai": "7.0.56"
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
isTerminalSubmissionStatus,
|
|
20
20
|
SubmissionLifecycle,
|
|
21
21
|
SubmissionAdmission,
|
|
22
|
+
ADMISSION_ATTEMPT_WINDOW_MS,
|
|
22
23
|
SubmissionDelivery,
|
|
23
24
|
SubmissionFinalizer,
|
|
24
25
|
projectSubmissionReceipt,
|
|
@@ -33,6 +34,8 @@ import {
|
|
|
33
34
|
type SubmissionReceipt,
|
|
34
35
|
type SubmissionExecutionMode,
|
|
35
36
|
type SubmissionHandle,
|
|
37
|
+
type StoredRuntimeKey,
|
|
38
|
+
type RuntimeKeyRewrite,
|
|
36
39
|
type RuntimeUnattendedExecutionGrant,
|
|
37
40
|
} from "../../submission";
|
|
38
41
|
import type { ExecutionLevel } from "../../lib/execution-level";
|
|
@@ -43,7 +46,6 @@ import {
|
|
|
43
46
|
RuntimeLoadTracker,
|
|
44
47
|
prepareRuntimeCandidate,
|
|
45
48
|
projectRuntimeAssembly,
|
|
46
|
-
type PreparedPiRuntime,
|
|
47
49
|
type RuntimeAssemblyInput,
|
|
48
50
|
type RuntimeAssemblyView,
|
|
49
51
|
type RuntimeCandidate,
|
|
@@ -88,7 +90,10 @@ import {
|
|
|
88
90
|
type TemporaryAgentRequest,
|
|
89
91
|
type TemporaryAgentRunContext,
|
|
90
92
|
} from "../../tools";
|
|
91
|
-
import {
|
|
93
|
+
import {
|
|
94
|
+
ASK_USER_TOOL_NAME,
|
|
95
|
+
askUserPiToolCandidate,
|
|
96
|
+
} from "../../tools/assistant/ask-user";
|
|
92
97
|
import { RuntimeDatabase } from "./database";
|
|
93
98
|
|
|
94
99
|
/**
|
|
@@ -156,17 +161,22 @@ export abstract class AgentRuntimeKernel<
|
|
|
156
161
|
};
|
|
157
162
|
|
|
158
163
|
protected abstract ensureRuntimeReady(): Promise<void>;
|
|
159
|
-
/** Prepare per-Submission Host state before
|
|
164
|
+
/** Prepare per-Submission Host state before first execution. */
|
|
160
165
|
protected prepareRuntimeForSubmission(_submissionId: string): Promise<void> {
|
|
161
166
|
return this.ensureRuntimeReady();
|
|
162
167
|
}
|
|
163
168
|
|
|
164
169
|
/** Runs before a new Submission records its Runtime key. */
|
|
165
|
-
protected prepareRuntimeForAdmission(): Promise<
|
|
166
|
-
|
|
170
|
+
protected async prepareRuntimeForAdmission(): Promise<{
|
|
171
|
+
runtimeKey: string;
|
|
172
|
+
admission?: ActiveRuntime["admission"];
|
|
173
|
+
}> {
|
|
174
|
+
await this.ensureRuntimeReady();
|
|
175
|
+
const runtime = this.requireRuntime();
|
|
176
|
+
return { runtimeKey: runtime.runtimeKey, admission: runtime.admission };
|
|
167
177
|
}
|
|
168
178
|
|
|
169
|
-
protected ensureRuntimeVersion(_runtimeKey: string): Promise<void> {
|
|
179
|
+
protected ensureRuntimeVersion(_runtimeKey: string, _force = false): Promise<void> {
|
|
170
180
|
return this.ensureRuntimeReady();
|
|
171
181
|
}
|
|
172
182
|
protected get submissionContext() {
|
|
@@ -176,6 +186,37 @@ export abstract class AgentRuntimeKernel<
|
|
|
176
186
|
};
|
|
177
187
|
}
|
|
178
188
|
|
|
189
|
+
/** Narrow storage seam for Host-validated, one-time Agent identity upgrades. */
|
|
190
|
+
protected savedRuntimeKeys(): readonly StoredRuntimeKey[] {
|
|
191
|
+
return this.db.submissions.listRuntimeKeys();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
protected rewriteSavedRuntimeKeys(rewrites: readonly RuntimeKeyRewrite[]): void {
|
|
195
|
+
this.db.submissions.rewriteRuntimeKeys(rewrites);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** One Runtime-owned fact for Host lifecycle guards; includes async preparation. */
|
|
199
|
+
protected hasUnfinishedRuntimeWork(): boolean {
|
|
200
|
+
return this.submissions.isBusy() ||
|
|
201
|
+
this.db.submissionAdmissions.countPending(Date.now() - ADMISSION_ATTEMPT_WINDOW_MS) > 0 ||
|
|
202
|
+
this.db.agentTools.hasRunning() ||
|
|
203
|
+
Boolean(
|
|
204
|
+
this.state.turn?.activeSubmissionId ||
|
|
205
|
+
this.state.approvals?.length ||
|
|
206
|
+
(this.state.activity?.activity && this.state.activity.activity !== "idle"),
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** True while a Submission owns this Chat's active execution slice. */
|
|
211
|
+
protected isSubmissionExecuting(): boolean {
|
|
212
|
+
return this.submissions.currentActive() !== undefined;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** External Runtime changes share the same serial queue as Submission execution. */
|
|
216
|
+
protected runConfigControl<T>(run: () => Promise<T>): Promise<T> {
|
|
217
|
+
return this.submissions.runConfigControl(run);
|
|
218
|
+
}
|
|
219
|
+
|
|
179
220
|
/** Let the Host prepare a validated client response before Tool settlement. */
|
|
180
221
|
protected prepareToolInteractionResponse(
|
|
181
222
|
input: ToolInteractionResponsePreparation,
|
|
@@ -318,7 +359,6 @@ export abstract class AgentRuntimeKernel<
|
|
|
318
359
|
createExtensionHostBinding: (permissions, labels) =>
|
|
319
360
|
this.createExtensionHostBinding(permissions, labels),
|
|
320
361
|
advance: (phase) => this.runtimeLoad.advance(phase),
|
|
321
|
-
isExecuting: () => Boolean(this.submissions.currentActive()),
|
|
322
362
|
commit: (runtime) => {
|
|
323
363
|
this.telemetry.configure(candidate.telemetry);
|
|
324
364
|
this.activeRuntime = runtime;
|
|
@@ -516,57 +556,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
516
556
|
|
|
517
557
|
// #endregion
|
|
518
558
|
|
|
519
|
-
// #region Submission
|
|
520
|
-
|
|
521
|
-
/** Pin dynamic Context and execution policy before the first assembly is used. */
|
|
522
|
-
private async admissionPin(fresh: boolean, submissionId: string): Promise<{
|
|
523
|
-
revision: string;
|
|
524
|
-
descriptor: string;
|
|
525
|
-
}> {
|
|
526
|
-
if (fresh) await this.prepareRuntimeForSubmission(submissionId);
|
|
527
|
-
const submission = this.db.submissions.find(submissionId);
|
|
528
|
-
if (!submission) {
|
|
529
|
-
throw new Error(`Unknown SpringBrand submission: ${submissionId}`);
|
|
530
|
-
}
|
|
531
|
-
return this.currentAssemblyPin(submission.origin !== "scheduled");
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// initConfig can call this inside its serialized load; re-entering ensureRuntimeReady would deadlock.
|
|
535
|
-
private async currentAssemblyPin(includeMemory = true): Promise<{
|
|
536
|
-
revision: string;
|
|
537
|
-
descriptor: string;
|
|
538
|
-
}> {
|
|
539
|
-
await this.runtimeTurnEvents.drain();
|
|
540
|
-
const runtime = this.activeRuntime;
|
|
541
|
-
if (!runtime) {
|
|
542
|
-
throw new Error("Runtime revision is not initialized");
|
|
543
|
-
}
|
|
544
|
-
return this.pinAssembly(runtime.pi, runtime.revision, includeMemory);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
private async pinAssembly(
|
|
548
|
-
prepared: PreparedPiRuntime,
|
|
549
|
-
baseRevision: string,
|
|
550
|
-
includeMemory: boolean,
|
|
551
|
-
): Promise<{ revision: string; descriptor: string }> {
|
|
552
|
-
const pinned = await this.pi.pin({
|
|
553
|
-
prepared,
|
|
554
|
-
baseRevision,
|
|
555
|
-
includeMemory,
|
|
556
|
-
readExtensionContext: ({ label }) =>
|
|
557
|
-
this._hostGetContext(label),
|
|
558
|
-
});
|
|
559
|
-
if (pinned.degradations.length > 0) {
|
|
560
|
-
console.warn(
|
|
561
|
-
"[runtime-context:degraded]",
|
|
562
|
-
json({ degradations: pinned.degradations }),
|
|
563
|
-
);
|
|
564
|
-
}
|
|
565
|
-
return {
|
|
566
|
-
revision: await sha256Hex(pinned.descriptor),
|
|
567
|
-
descriptor: pinned.descriptor,
|
|
568
|
-
};
|
|
569
|
-
}
|
|
559
|
+
// #region Submission 准入与当前配置
|
|
570
560
|
|
|
571
561
|
private get admission(): SubmissionAdmission {
|
|
572
562
|
return this.submissionAdmission ??= new SubmissionAdmission({
|
|
@@ -574,10 +564,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
574
564
|
steers: this.db.steers,
|
|
575
565
|
submissions: this.db.submissions,
|
|
576
566
|
transaction: (run) => this.db.transaction(run),
|
|
577
|
-
prepareRuntime:
|
|
578
|
-
await this.prepareRuntimeForAdmission();
|
|
579
|
-
return { runtimeKey: this.activeRuntime!.runtimeKey, admission: this.activeRuntime?.admission };
|
|
580
|
-
},
|
|
567
|
+
prepareRuntime: () => this.prepareRuntimeForAdmission(),
|
|
581
568
|
findUserMessage: (id) => this.transcript.findUserMessage(id),
|
|
582
569
|
onAdmitted: (submission, hostAccepted) =>
|
|
583
570
|
this.runtimeTurnEvents.submissionAdmitted(submission, hostAccepted),
|
|
@@ -783,27 +770,32 @@ export abstract class AgentRuntimeKernel<
|
|
|
783
770
|
): Promise<StoredSubmission> {
|
|
784
771
|
const continuation = mode !== "fresh";
|
|
785
772
|
const submissionId = submission.submissionId;
|
|
786
|
-
submission = await this.admission.
|
|
773
|
+
submission = await this.admission.prepareCurrentRuntime(
|
|
787
774
|
submission,
|
|
788
|
-
() => this.
|
|
789
|
-
(key) => this.ensureRuntimeVersion(key),
|
|
775
|
+
(key) => this.ensureRuntimeVersion(key, true),
|
|
790
776
|
);
|
|
777
|
+
if (!continuation) await this.prepareRuntimeForSubmission(submissionId);
|
|
778
|
+
await this.runtimeTurnEvents.drain();
|
|
791
779
|
const activeRuntime = this.requireRuntime();
|
|
792
780
|
// A fresh provider Session per Submission keeps one failing upstream route from pinning later turns.
|
|
793
781
|
const recovery = new ModelRecovery(submissionId);
|
|
794
|
-
const harnessInput = this.pi.harnessInputs(
|
|
782
|
+
const harnessInput = await this.pi.harnessInputs(
|
|
795
783
|
activeRuntime.pi,
|
|
796
|
-
submission.assemblyDescriptor,
|
|
797
784
|
{
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
785
|
+
modelSessionId: () => recovery.sessionId(),
|
|
786
|
+
assertRequestAllowed: () => {
|
|
787
|
+
const current = this.db.submissions.find(submissionId);
|
|
788
|
+
if (current?.abortReason || current?.unattendedFailure) throw new Error(current.abortReason ?? current.unattendedFailure!);
|
|
789
|
+
},
|
|
790
|
+
onGeneration: (event) => this.telemetry.generation(submissionId, event),
|
|
791
|
+
onCompactionGeneration: (event) => this.runtimeTurnEvents.compactionGeneration(submissionId, event),
|
|
792
|
+
includeMemory: submission.origin !== "scheduled",
|
|
793
|
+
readExtensionContext: ({ label }) => this._hostGetContext(label),
|
|
805
794
|
},
|
|
806
795
|
);
|
|
796
|
+
if (harnessInput.degradations.length > 0) {
|
|
797
|
+
console.warn("[runtime-context:degraded]", json({ degradations: harnessInput.degradations }));
|
|
798
|
+
}
|
|
807
799
|
const prepareToolInput = (toolName: string, input: unknown): unknown => {
|
|
808
800
|
const prepare = harnessInput.candidates.find(({ tool }) =>
|
|
809
801
|
tool.name === toolName
|
|
@@ -906,7 +898,6 @@ export abstract class AgentRuntimeKernel<
|
|
|
906
898
|
this.db.submissions.transition(submissionId, "running", ["pending"]);
|
|
907
899
|
this.telemetry.capture("turnStarted", {
|
|
908
900
|
submissionId,
|
|
909
|
-
assemblyRevision: submission.assemblyRevision,
|
|
910
901
|
});
|
|
911
902
|
submission = this.db.submissions.find(submissionId)!;
|
|
912
903
|
}
|
|
@@ -1533,11 +1524,19 @@ export abstract class AgentRuntimeKernel<
|
|
|
1533
1524
|
if (!submission || isTerminalSubmissionStatus(submission.status)) {
|
|
1534
1525
|
return { ok: false };
|
|
1535
1526
|
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1527
|
+
const active = this.submissions.currentActive();
|
|
1528
|
+
if (active && active.submissionId !== submission.submissionId) {
|
|
1529
|
+
return { ok: false };
|
|
1530
|
+
}
|
|
1531
|
+
// ask_user has a Config-independent validator. A parked answer must not
|
|
1532
|
+
// replace the Gateway; the continuation loads current Config instead.
|
|
1533
|
+
const askUser = pending.toolName === ASK_USER_TOOL_NAME;
|
|
1534
|
+
if (!askUser && !active) {
|
|
1535
|
+
await this.ensureRuntimeVersion(submission.runtimeKey, true);
|
|
1536
|
+
}
|
|
1537
|
+
const spec = askUser
|
|
1538
|
+
? askUserPiToolCandidate().interaction
|
|
1539
|
+
: this.pi.interaction(this.requireRuntime().pi, pending.toolName);
|
|
1541
1540
|
if (!spec) return { ok: false };
|
|
1542
1541
|
|
|
1543
1542
|
const accepted = await this.humanDecisions.respond(
|