@springbrand/agent-runtime 0.3.0-alpha.1 → 0.3.0-alpha.11
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 +8 -8
- package/src/adapter/cloudflare/agent.ts +160 -14
- package/src/adapter/cloudflare/define-agent.ts +12 -7
- package/src/adapter/cloudflare/index.ts +18 -0
- package/src/adapter/cloudflare/scheduling/schedule-rule.ts +322 -0
- package/src/adapter/cloudflare/scheduling/zoned-cron.ts +11 -6
- package/src/adapter/cloudflare/schema.ts +15 -0
- package/src/adapter/cloudflare/subagent/definition.ts +4 -4
- package/src/adapter/cloudflare/subagent/runner.ts +1 -1
- package/src/adapter/cloudflare/subagent/tools.ts +2 -2
- package/src/adapter/cloudflare/universal-agent/preparation.ts +3 -3
- package/src/adapter/cloudflare/universal-agent/tools.ts +4 -6
- package/src/adapter/cloudflare/workspace/version-control.ts +1 -1
- package/src/config/agent-context.ts +1 -1
- package/src/config/assembler.ts +29 -31
- package/src/config/assembly-snapshot.ts +1 -1
- package/src/config/definition.ts +1 -1
- package/src/config/harness-runtime.ts +9 -1
- package/src/config/model-guidance.ts +21 -0
- package/src/config/pinning.ts +22 -3
- package/src/config/ports.ts +27 -7
- package/src/config/public-contracts.ts +2 -2
- package/src/context/assembly.ts +1 -1
- package/src/context/extension/assembly.ts +2 -2
- package/src/context/memory/budget/gate.ts +19 -3
- package/src/conversation/harness-stream.ts +2 -1
- package/src/conversation/history.ts +2 -0
- package/src/conversation/message/projection.ts +38 -21
- package/src/conversation/state.ts +23 -2
- package/src/harness/session-harness.ts +160 -6
- package/src/index.ts +8 -7
- package/src/lib/artifacts.ts +18 -0
- package/src/lib/execution-level.ts +13 -0
- package/src/lib/prompt.ts +9 -10
- package/src/lib/token-estimate.ts +8 -0
- package/src/models/index.ts +32 -10
- package/src/models/openrouter/messages.ts +13 -8
- package/src/models/provider-streams.ts +45 -0
- package/src/session/cloudflare/migration.ts +71 -11
- package/src/skills/springbrand-worker-website/index.ts +1 -1
- package/src/submission/admission.ts +12 -1
- package/src/submission/contracts.ts +42 -2
- package/src/submission/delivery.ts +1 -1
- package/src/submission/finalization.ts +1 -1
- package/src/submission/repository.ts +64 -4
- package/src/submission/unattended.ts +40 -0
- package/src/telemetry/coordinator.ts +1 -1
- package/src/telemetry/subagent-lifecycle.ts +1 -1
- package/src/telemetry/turn-events.ts +1 -1
- package/src/tools/README.md +44 -0
- package/src/tools/{ai-adapter.ts → adapters/ai-to-pi.ts} +6 -9
- package/src/tools/{declared.ts → adapters/declared.ts} +6 -2
- package/src/tools/{nested-tools.ts → adapters/pi-to-ai.ts} +4 -4
- package/src/tools/assistant/ask-user.ts +317 -0
- package/src/tools/assistant/candidate.ts +15 -0
- package/src/tools/assistant/followups.ts +34 -0
- package/src/tools/assistant/index.ts +19 -0
- package/src/tools/assistant/memory.ts +73 -0
- package/src/tools/assistant/plan.ts +98 -0
- package/src/tools/{time.ts → assistant/time.ts} +1 -1
- package/src/tools/{core-host.ts → codemode/adapter.ts} +50 -53
- package/src/tools/{core.ts → codemode/index.ts} +36 -92
- package/src/tools/codemode/workspace-connector.ts +198 -0
- package/src/tools/extension/index.ts +42 -0
- package/src/tools/index.ts +29 -26
- package/src/tools/{gateway.ts → mcp/gateway.ts} +19 -19
- package/src/tools/{mcp.ts → mcp/index.ts} +5 -5
- package/src/tools/runtime/activation.ts +40 -0
- package/src/tools/{approval → runtime/approval}/repository.ts +13 -12
- package/src/tools/{compiler.ts → runtime/compiler.ts} +84 -147
- package/src/tools/runtime/contracts.ts +140 -0
- package/src/tools/{decision-gate.ts → runtime/decision-gate.ts} +147 -37
- package/src/tools/{human-decisions.ts → runtime/human-decisions.ts} +4 -4
- package/src/tools/{interaction → runtime/interaction}/repository.ts +1 -7
- package/src/tools/runtime/result.ts +22 -0
- package/src/tools/runtime/unattended-error.ts +4 -0
- package/src/tools/sandbox/index.ts +228 -0
- package/src/tools/schedule/index.ts +301 -0
- package/src/tools/skill/contracts.ts +20 -0
- package/src/tools/skill/index.ts +99 -0
- package/src/tools/skill/resources.ts +322 -0
- package/src/tools/skill/scripts.ts +168 -0
- package/src/tools/{subagent-authority.ts → subagent/authority.ts} +31 -31
- package/src/tools/{subagent.ts → subagent/index.ts} +34 -37
- package/src/tools/{subagent-run-repository.ts → subagent/repository.ts} +1 -1
- package/src/tools/{subagent-runtime.ts → subagent/runtime.ts} +1 -1
- package/src/tools/{temporary-agent → subagent/temporary}/approvals.ts +4 -7
- package/src/tools/{temporary-agent → subagent/temporary}/core.ts +1 -1
- package/src/tools/{temporary-agent → subagent/temporary}/runner.ts +1 -1
- package/src/tools/{temporary-agent → subagent/temporary}/workspace.ts +1 -4
- package/src/tools/{web-fetch.ts → web/fetch.ts} +4 -13
- package/src/tools/web/index.ts +2 -0
- package/src/tools/web/search/api.ts +1320 -0
- package/src/tools/web/search/index.ts +2 -0
- package/src/tools/{web-search/web-search.ts → web/search/search.ts} +5 -7
- package/src/tools/{workspace-sandbox.ts → workspace/index.ts} +12 -272
- package/src/tools/{workspace-revision.ts → workspace/revision.ts} +3 -3
- package/src/tools/base.ts +0 -534
- package/src/tools/schedule.ts +0 -261
- package/src/tools/skill.ts +0 -565
- package/src/tools/web-search/api.ts +0 -1318
- package/src/tools/web-search/index.ts +0 -2
- package/src/tools/workspace-state-connector.ts +0 -146
- package/src/tools/{approval → runtime/approval}/index.ts +0 -0
- package/src/tools/{interaction → runtime/interaction}/index.ts +0 -0
- package/src/tools/{temporary-agent → subagent/temporary}/coordinator.ts +0 -0
- package/src/tools/{subagent-types → subagent/types}/contract.ts +0 -0
- package/src/tools/{subagent-types → subagent/types}/extract/index.ts +0 -0
- package/src/tools/{subagent-types → subagent/types}/fanout/index.ts +0 -0
- package/src/tools/{subagent-types → subagent/types}/registry.ts +1 -1
- /package/src/tools/{workspace-versioning.ts → workspace/versioning.ts} +0 -0
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.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
".": "./src/index.ts",
|
|
14
14
|
"./adapter/cloudflare": "./src/adapter/cloudflare/index.ts",
|
|
15
15
|
"./contracts": "./src/config/public-contracts.ts",
|
|
16
|
-
"./declared-tool": "./src/tools/declared.ts",
|
|
16
|
+
"./declared-tool": "./src/tools/adapters/declared.ts",
|
|
17
17
|
"./models": "./src/lib/model-catalog.ts"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@cloudflare/codemode": "0.5.
|
|
20
|
+
"@cloudflare/codemode": "0.5.2",
|
|
21
21
|
"@cloudflare/sandbox": "0.12.4",
|
|
22
22
|
"@cloudflare/shell": "0.4.3",
|
|
23
|
-
"@cloudflare/think": "0.
|
|
24
|
-
"@earendil-works/pi-agent-core": "0.
|
|
25
|
-
"@earendil-works/pi-ai": "0.
|
|
23
|
+
"@cloudflare/think": "0.18.0",
|
|
24
|
+
"@earendil-works/pi-agent-core": "0.86.1",
|
|
25
|
+
"@earendil-works/pi-ai": "0.86.1",
|
|
26
26
|
"@types/lodash-es": "^4.17.12",
|
|
27
27
|
"cron-schedule": "6.0.0",
|
|
28
28
|
"isomorphic-git": "1.38.6",
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"zod": "^4.4.3"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"agents": "^0.
|
|
33
|
+
"agents": "^0.23.0",
|
|
34
34
|
"ai": "^7.0.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"agents": "0.
|
|
37
|
+
"agents": "0.23.0",
|
|
38
38
|
"ai": "7.0.56"
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
type SubmissionReceipt,
|
|
34
34
|
type SubmissionExecutionMode,
|
|
35
35
|
type SubmissionHandle,
|
|
36
|
+
type RuntimeUnattendedExecutionGrant,
|
|
36
37
|
} from "../../submission";
|
|
37
38
|
import type { ExecutionLevel } from "../../lib/execution-level";
|
|
38
39
|
import {
|
|
@@ -66,6 +67,7 @@ import {
|
|
|
66
67
|
import type { UIMessage, UIMessageChunk } from "ai";
|
|
67
68
|
import type {
|
|
68
69
|
AssistantMessage,
|
|
70
|
+
JsonObject,
|
|
69
71
|
} from "@earendil-works/pi-ai";
|
|
70
72
|
import { BACKGROUND_CONTEXT } from "@earendil-works/pi-agent-core/harness/context";
|
|
71
73
|
import { openCloudflareSession } from "../../session";
|
|
@@ -73,6 +75,7 @@ import { openSessionHarness, prepareSubmissionOperation } from "../../harness";
|
|
|
73
75
|
import type { SessionHarness } from "../../harness";
|
|
74
76
|
import {
|
|
75
77
|
compileHarnessTools,
|
|
78
|
+
harnessToolActivation,
|
|
76
79
|
decodeSubagentCompletion,
|
|
77
80
|
HarnessDecisionGate,
|
|
78
81
|
HarnessHumanDecisions,
|
|
@@ -98,6 +101,10 @@ import { RuntimeDatabase } from "./database";
|
|
|
98
101
|
// #region Runtime 内部数据与基础工具
|
|
99
102
|
|
|
100
103
|
const SCHEDULED_STABLE_TIMEOUT_MS = 30_000;
|
|
104
|
+
/** 定时 Run 未结束时拒绝普通输入的统一原因,服务端与客户端状态共用同一事实。 */
|
|
105
|
+
const SCHEDULED_INPUT_LOCKED_MESSAGE =
|
|
106
|
+
"This scheduled run is still in progress. Wait for it to finish before sending a message.";
|
|
107
|
+
|
|
101
108
|
interface ActiveTurn {
|
|
102
109
|
submissionId: string;
|
|
103
110
|
requestId: string;
|
|
@@ -390,7 +397,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
390
397
|
}
|
|
391
398
|
await this.broadcastApprovals();
|
|
392
399
|
const running = this.db.submissions.findRunning() as StoredSubmission | null;
|
|
393
|
-
if (running) {
|
|
400
|
+
if (running && !running.unattendedFailure) {
|
|
394
401
|
if (
|
|
395
402
|
this.db.approvals.listPendingForSubmission(running.submissionId).length > 0 ||
|
|
396
403
|
this.db.interactions.listPendingForSubmission(running.submissionId).length > 0
|
|
@@ -513,11 +520,15 @@ export abstract class AgentRuntimeKernel<
|
|
|
513
520
|
}> {
|
|
514
521
|
if (fresh) await this.prepareRuntimeForSubmission(submissionId);
|
|
515
522
|
else await this.ensureRuntimeReady();
|
|
516
|
-
|
|
523
|
+
const submission = this.db.submissions.find(submissionId);
|
|
524
|
+
if (!submission) {
|
|
525
|
+
throw new Error(`Unknown SpringBrand submission: ${submissionId}`);
|
|
526
|
+
}
|
|
527
|
+
return this.currentAssemblyPin(submission.origin !== "scheduled");
|
|
517
528
|
}
|
|
518
529
|
|
|
519
530
|
// initConfig can call this inside its serialized load; re-entering ensureRuntimeReady would deadlock.
|
|
520
|
-
private async currentAssemblyPin(): Promise<{
|
|
531
|
+
private async currentAssemblyPin(includeMemory = true): Promise<{
|
|
521
532
|
revision: string;
|
|
522
533
|
descriptor: string;
|
|
523
534
|
}> {
|
|
@@ -526,16 +537,18 @@ export abstract class AgentRuntimeKernel<
|
|
|
526
537
|
if (!runtime) {
|
|
527
538
|
throw new Error("Runtime revision is not initialized");
|
|
528
539
|
}
|
|
529
|
-
return this.pinAssembly(runtime.pi, runtime.revision);
|
|
540
|
+
return this.pinAssembly(runtime.pi, runtime.revision, includeMemory);
|
|
530
541
|
}
|
|
531
542
|
|
|
532
543
|
private async pinAssembly(
|
|
533
544
|
prepared: PreparedPiRuntime,
|
|
534
545
|
baseRevision: string,
|
|
546
|
+
includeMemory: boolean,
|
|
535
547
|
): Promise<{ revision: string; descriptor: string }> {
|
|
536
548
|
const pinned = await this.pi.pin({
|
|
537
549
|
prepared,
|
|
538
550
|
baseRevision,
|
|
551
|
+
includeMemory,
|
|
539
552
|
readExtensionContext: ({ label }) =>
|
|
540
553
|
this._hostGetContext(label),
|
|
541
554
|
});
|
|
@@ -668,11 +681,13 @@ export abstract class AgentRuntimeKernel<
|
|
|
668
681
|
return submission;
|
|
669
682
|
}
|
|
670
683
|
if (submission.abortReason && submission.status === "pending") {
|
|
671
|
-
|
|
684
|
+
const terminal = await this.submissions.finish(
|
|
672
685
|
submission,
|
|
673
686
|
"aborted",
|
|
674
687
|
submission.abortReason,
|
|
675
688
|
);
|
|
689
|
+
await this.projectTerminal(terminal);
|
|
690
|
+
return terminal;
|
|
676
691
|
}
|
|
677
692
|
if (mode === "recovery") {
|
|
678
693
|
this.telemetry.capture("turnRecovering", {
|
|
@@ -683,7 +698,11 @@ export abstract class AgentRuntimeKernel<
|
|
|
683
698
|
await this.broadcastApprovals();
|
|
684
699
|
}
|
|
685
700
|
try {
|
|
686
|
-
|
|
701
|
+
const outcome = await this.executeHarnessSubmission(submission, mode);
|
|
702
|
+
if (isTerminalSubmissionStatus(outcome.status)) {
|
|
703
|
+
await this.projectTerminal(outcome);
|
|
704
|
+
}
|
|
705
|
+
return outcome;
|
|
687
706
|
} catch (error) {
|
|
688
707
|
const failed = await this.submissions.finish(
|
|
689
708
|
submission,
|
|
@@ -697,10 +716,57 @@ export abstract class AgentRuntimeKernel<
|
|
|
697
716
|
: undefined,
|
|
698
717
|
mode !== "fresh",
|
|
699
718
|
);
|
|
719
|
+
await this.projectTerminal(failed);
|
|
700
720
|
return failed;
|
|
701
721
|
}
|
|
702
722
|
}
|
|
703
723
|
|
|
724
|
+
/** Project one authoritative Submission terminal after Harness and client settlement. */
|
|
725
|
+
private async projectTerminal(submission: StoredSubmission): Promise<void> {
|
|
726
|
+
if (!isTerminalSubmissionStatus(submission.status)) {
|
|
727
|
+
throw new Error(`Cannot project unfinished Submission: ${submission.submissionId}`);
|
|
728
|
+
}
|
|
729
|
+
const events = this.activeRuntime?.snapshot.bindings.turnEvents;
|
|
730
|
+
if (!events) return;
|
|
731
|
+
const messages = await this.getMessages();
|
|
732
|
+
try {
|
|
733
|
+
await events.onResponse(
|
|
734
|
+
messages.flatMap((message) =>
|
|
735
|
+
message.role === "user" || message.role === "assistant"
|
|
736
|
+
? [{
|
|
737
|
+
role: message.role,
|
|
738
|
+
text: message.parts
|
|
739
|
+
.filter(
|
|
740
|
+
(
|
|
741
|
+
part,
|
|
742
|
+
): part is Extract<UIMessage["parts"][number], { type: "text" }> =>
|
|
743
|
+
part.type === "text",
|
|
744
|
+
)
|
|
745
|
+
.map((part) => part.text)
|
|
746
|
+
.join(""),
|
|
747
|
+
}]
|
|
748
|
+
: []
|
|
749
|
+
),
|
|
750
|
+
);
|
|
751
|
+
} catch (error) {
|
|
752
|
+
console.warn("[runtime-turn-projection:degraded]", json({
|
|
753
|
+
error: errorText(error),
|
|
754
|
+
}));
|
|
755
|
+
}
|
|
756
|
+
try {
|
|
757
|
+
await events.onSubmissionTerminal?.({
|
|
758
|
+
submissionId: submission.submissionId,
|
|
759
|
+
status: submission.status,
|
|
760
|
+
...(submission.error ? { error: submission.error } : {}),
|
|
761
|
+
resultMessageId: submission.assistantMessageId,
|
|
762
|
+
});
|
|
763
|
+
} catch (error) {
|
|
764
|
+
console.warn("[runtime-terminal-projection:degraded]", json({
|
|
765
|
+
error: errorText(error),
|
|
766
|
+
}));
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
704
770
|
/** Run or recover the Submission through Pi's single durable AgentHarness. */
|
|
705
771
|
private async executeHarnessSubmission(
|
|
706
772
|
submission: StoredSubmission,
|
|
@@ -722,6 +788,17 @@ export abstract class AgentRuntimeKernel<
|
|
|
722
788
|
onGeneration: (event) => this.telemetry.generation(submissionId, event),
|
|
723
789
|
},
|
|
724
790
|
);
|
|
791
|
+
const prepareToolInput = (toolName: string, input: unknown): unknown => {
|
|
792
|
+
const prepare = harnessInput.candidates.find(({ tool }) =>
|
|
793
|
+
tool.name === toolName
|
|
794
|
+
)?.tool.prepareArguments;
|
|
795
|
+
if (!prepare) return input;
|
|
796
|
+
try {
|
|
797
|
+
return prepare(input);
|
|
798
|
+
} catch {
|
|
799
|
+
return input;
|
|
800
|
+
}
|
|
801
|
+
};
|
|
725
802
|
let runtime!: Awaited<ReturnType<typeof openSessionHarness>>;
|
|
726
803
|
let publishApprovalRequest = async (
|
|
727
804
|
_toolCallId: string,
|
|
@@ -732,6 +809,8 @@ export abstract class AgentRuntimeKernel<
|
|
|
732
809
|
submissionId,
|
|
733
810
|
requestId: submission.requestId,
|
|
734
811
|
executionLevel: activeRuntime.snapshot.profile.executionLevel,
|
|
812
|
+
origin: submission.origin,
|
|
813
|
+
scheduledMaxExecutionLevel: submission.scheduledMaxExecutionLevel ?? undefined,
|
|
735
814
|
},
|
|
736
815
|
candidates: harnessInput.candidates,
|
|
737
816
|
telemetry: this.telemetry,
|
|
@@ -759,6 +838,10 @@ export abstract class AgentRuntimeKernel<
|
|
|
759
838
|
}
|
|
760
839
|
},
|
|
761
840
|
gateTool: harnessInput.platform.gateTool,
|
|
841
|
+
onUnattendedFailure: (reason) => {
|
|
842
|
+
this.db.submissions.recordUnattendedFailure(submissionId, reason);
|
|
843
|
+
},
|
|
844
|
+
unattendedFailure: () => this.db.submissions.find(submissionId)?.unattendedFailure,
|
|
762
845
|
});
|
|
763
846
|
runtime = await openSessionHarness({
|
|
764
847
|
session: openCloudflareSession(this.ctx.storage),
|
|
@@ -771,7 +854,9 @@ export abstract class AgentRuntimeKernel<
|
|
|
771
854
|
workspace: harnessInput.workspace,
|
|
772
855
|
authorize: gate.authorize.bind(gate),
|
|
773
856
|
resolveReplay: gate.resolveReplay.bind(gate),
|
|
857
|
+
resolveConfirmation: gate.resolveConfirmation.bind(gate),
|
|
774
858
|
}),
|
|
859
|
+
...harnessToolActivation(harnessInput.candidates),
|
|
775
860
|
modelToolResultExemptions: harnessInput.candidates
|
|
776
861
|
.filter(({ outputBudget }) => outputBudget?.kind === "structure")
|
|
777
862
|
.map(({ tool }) => tool.name),
|
|
@@ -783,14 +868,26 @@ export abstract class AgentRuntimeKernel<
|
|
|
783
868
|
let stream: ReturnType<typeof bindHarnessStream> | undefined;
|
|
784
869
|
let deactivate = () => {};
|
|
785
870
|
try {
|
|
871
|
+
runtime.harness.hooks.on("after_response", ({ message }) => {
|
|
872
|
+
for (const part of message.content) {
|
|
873
|
+
if (part.type === "toolCall") {
|
|
874
|
+
part.arguments = prepareToolInput(
|
|
875
|
+
part.name,
|
|
876
|
+
part.arguments,
|
|
877
|
+
) as JsonObject;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return { message };
|
|
881
|
+
});
|
|
786
882
|
runtime.harness.hooks.on("before_tool", gate.beforeTool);
|
|
787
883
|
runtime.harness.hooks.on("before_drive", async (event, context) => {
|
|
788
|
-
|
|
884
|
+
const current = this.db.submissions.find(submissionId);
|
|
885
|
+
if (current?.abortReason || current?.unattendedFailure) {
|
|
789
886
|
await runtime.requestAbort(event.runId, context);
|
|
790
887
|
}
|
|
791
888
|
});
|
|
792
889
|
|
|
793
|
-
if (submission.abortReason && runtime.openOperation) {
|
|
890
|
+
if ((submission.abortReason || submission.unattendedFailure) && runtime.openOperation) {
|
|
794
891
|
await runtime.requestAbort(
|
|
795
892
|
runtime.openOperation.operationId,
|
|
796
893
|
BACKGROUND_CONTEXT,
|
|
@@ -807,11 +904,15 @@ export abstract class AgentRuntimeKernel<
|
|
|
807
904
|
);
|
|
808
905
|
if (preparation.kind === "settled") {
|
|
809
906
|
const record = preparation.outcome;
|
|
907
|
+
const unattendedFailure = this.db.submissions.find(submissionId)
|
|
908
|
+
?.unattendedFailure;
|
|
810
909
|
const terminal = await this.submissions.finish(
|
|
811
910
|
submission,
|
|
812
|
-
|
|
911
|
+
unattendedFailure
|
|
912
|
+
? "failed"
|
|
913
|
+
: record.status === "completed" || record.status === "declined"
|
|
813
914
|
? "succeeded" : record.status === "aborted" ? "aborted" : "failed",
|
|
814
|
-
record.error?.message,
|
|
915
|
+
unattendedFailure ?? record.error?.message,
|
|
815
916
|
);
|
|
816
917
|
this.broadcast(json({
|
|
817
918
|
type: MessageType.CF_AGENT_CHAT_MESSAGES,
|
|
@@ -870,6 +971,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
870
971
|
userMessageId: submission.userMessageId ?? undefined,
|
|
871
972
|
startedAt: submission.createdAt,
|
|
872
973
|
turnId: submissionId,
|
|
974
|
+
prepareToolInput,
|
|
873
975
|
publish: async (chunks) => {
|
|
874
976
|
for (const chunk of chunks) {
|
|
875
977
|
this.conversation.broadcastResponse({
|
|
@@ -936,7 +1038,11 @@ export abstract class AgentRuntimeKernel<
|
|
|
936
1038
|
return this.db.submissions.find(submissionId)!;
|
|
937
1039
|
}
|
|
938
1040
|
const record = outcome.outcome;
|
|
939
|
-
const
|
|
1041
|
+
const unattendedFailure = this.db.submissions.find(submissionId)
|
|
1042
|
+
?.unattendedFailure;
|
|
1043
|
+
const status = unattendedFailure
|
|
1044
|
+
? "failed"
|
|
1045
|
+
: record.status === "completed" || record.status === "declined"
|
|
940
1046
|
? "succeeded"
|
|
941
1047
|
: record.status === "aborted"
|
|
942
1048
|
? "aborted"
|
|
@@ -945,7 +1051,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
945
1051
|
const terminal = await this.submissions.finish(
|
|
946
1052
|
submission,
|
|
947
1053
|
status,
|
|
948
|
-
record.error?.message,
|
|
1054
|
+
unattendedFailure ?? record.error?.message,
|
|
949
1055
|
);
|
|
950
1056
|
const projectedOutcome = projectSubmissionOutcome(record, terminal);
|
|
951
1057
|
await stream.flushTerminal(projectedOutcome);
|
|
@@ -1270,11 +1376,41 @@ export abstract class AgentRuntimeKernel<
|
|
|
1270
1376
|
this.transcript.importSnapshot(snapshot);
|
|
1271
1377
|
}
|
|
1272
1378
|
|
|
1379
|
+
/**
|
|
1380
|
+
* 本 Session 是否有一条未结束的定时 Submission。
|
|
1381
|
+
*
|
|
1382
|
+
* 定时 Run 未结束时独占输入:普通消息在准入前被拒绝,不排队等它结束。
|
|
1383
|
+
* 定时注入本身走 `submitScheduledPrompt`,不经过这道闸。
|
|
1384
|
+
*/
|
|
1385
|
+
private get scheduledInputLocked(): boolean {
|
|
1386
|
+
return this.db.submissions.hasUnfinishedScheduled();
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
private rejectedOrdinaryInputReceipt(): SubmissionReceipt {
|
|
1390
|
+
return {
|
|
1391
|
+
submissionId: crypto.randomUUID(),
|
|
1392
|
+
status: "skipped",
|
|
1393
|
+
accepted: false,
|
|
1394
|
+
error: SCHEDULED_INPUT_LOCKED_MESSAGE,
|
|
1395
|
+
retryable: false,
|
|
1396
|
+
createdAt: Date.now(),
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
private rejectInputLocked(): MessageDispatchReceipt {
|
|
1401
|
+
return {
|
|
1402
|
+
kind: "rejected",
|
|
1403
|
+
code: "input_locked",
|
|
1404
|
+
message: SCHEDULED_INPUT_LOCKED_MESSAGE,
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1273
1408
|
/** Admit text and return immediately; completion continues in the Durable Object. */
|
|
1274
1409
|
async submitPrompt(
|
|
1275
1410
|
text: string,
|
|
1276
1411
|
options?: { idempotencyKey?: string },
|
|
1277
1412
|
): Promise<SubmissionReceipt> {
|
|
1413
|
+
if (this.scheduledInputLocked) return this.rejectedOrdinaryInputReceipt();
|
|
1278
1414
|
const submitted = await this.submitMessage({
|
|
1279
1415
|
role: "user",
|
|
1280
1416
|
content: [{ type: "text", text }],
|
|
@@ -1304,17 +1440,24 @@ export abstract class AgentRuntimeKernel<
|
|
|
1304
1440
|
}
|
|
1305
1441
|
|
|
1306
1442
|
async dispatchMessage(message: UIMessage, delivery: MessageDelivery): Promise<MessageDispatchReceipt> {
|
|
1443
|
+
if (this.scheduledInputLocked) return this.rejectInputLocked();
|
|
1307
1444
|
return this.delivery.dispatch(message, delivery);
|
|
1308
1445
|
}
|
|
1309
1446
|
|
|
1310
1447
|
async steerQueuedSubmission(submissionId: string): Promise<MessageDispatchReceipt> {
|
|
1448
|
+
if (this.scheduledInputLocked) return this.rejectInputLocked();
|
|
1311
1449
|
return this.delivery.steerQueued(submissionId);
|
|
1312
1450
|
}
|
|
1313
1451
|
|
|
1314
1452
|
/** Admit scheduled work after a bounded stable wait, otherwise return skipped. */
|
|
1315
1453
|
async submitScheduledPrompt(
|
|
1316
1454
|
prompt: string,
|
|
1317
|
-
options?: {
|
|
1455
|
+
options?: {
|
|
1456
|
+
idempotencyKey?: string;
|
|
1457
|
+
scheduledMaxExecutionLevel?: ExecutionLevel;
|
|
1458
|
+
/** @deprecated Retained for rolling upgrades; ignored by Runtime. */
|
|
1459
|
+
unattendedExecutionGrant?: RuntimeUnattendedExecutionGrant;
|
|
1460
|
+
},
|
|
1318
1461
|
): Promise<SubmissionReceipt> {
|
|
1319
1462
|
const requestId = crypto.randomUUID();
|
|
1320
1463
|
const submitted = await this.submitMessageWhenStable(
|
|
@@ -1326,6 +1469,8 @@ export abstract class AgentRuntimeKernel<
|
|
|
1326
1469
|
{
|
|
1327
1470
|
requestId,
|
|
1328
1471
|
idempotencyKey: options?.idempotencyKey,
|
|
1472
|
+
origin: "scheduled",
|
|
1473
|
+
scheduledMaxExecutionLevel: options?.scheduledMaxExecutionLevel,
|
|
1329
1474
|
},
|
|
1330
1475
|
SCHEDULED_STABLE_TIMEOUT_MS,
|
|
1331
1476
|
);
|
|
@@ -1410,7 +1555,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
1410
1555
|
summary: approval.summary,
|
|
1411
1556
|
executionLevel: approval.executionLevel,
|
|
1412
1557
|
requiredExecutionLevel: approval.requiredExecutionLevel,
|
|
1413
|
-
inputJson: approval.inputJson,
|
|
1558
|
+
inputJson: approval.confirmationJson ?? approval.inputJson,
|
|
1414
1559
|
requestId: approval.requestId,
|
|
1415
1560
|
};
|
|
1416
1561
|
}
|
|
@@ -1520,6 +1665,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
1520
1665
|
hasPendingInteraction: (id) => this.humanDecisions.isWaitingForInteraction(id),
|
|
1521
1666
|
hasPendingSteer: (id) => this.db.steers.listForSubmission(id).length > 0,
|
|
1522
1667
|
hasRunningAgentTools: this.db.agentTools.hasRunning(),
|
|
1668
|
+
hasUnfinishedScheduled: this.db.submissions.hasUnfinishedScheduled(),
|
|
1523
1669
|
previousState: this.state,
|
|
1524
1670
|
});
|
|
1525
1671
|
if (changed) this.setState(state);
|
|
@@ -114,7 +114,7 @@ interface RuntimeAgentDefinitionBase<
|
|
|
114
114
|
/** Prepare the optional telemetry consumer for this concrete Agent facet. */
|
|
115
115
|
readonly telemetry?: (
|
|
116
116
|
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
117
|
-
) => AgentTelemetryBinding | undefined
|
|
117
|
+
) => AgentTelemetryBinding | undefined | Promise<AgentTelemetryBinding | undefined>;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
export interface ReadonlyRuntimeAgentDefinition<
|
|
@@ -262,7 +262,12 @@ export interface RuntimeAgentControls {
|
|
|
262
262
|
*/
|
|
263
263
|
submitScheduledPrompt(
|
|
264
264
|
prompt: string,
|
|
265
|
-
opts?: {
|
|
265
|
+
opts?: {
|
|
266
|
+
idempotencyKey?: string;
|
|
267
|
+
scheduledMaxExecutionLevel?: import("../../lib/execution-level").ExecutionLevel;
|
|
268
|
+
/** @deprecated Retained for rolling upgrades; ignored by Runtime. */
|
|
269
|
+
unattendedExecutionGrant?: import("../../submission").RuntimeUnattendedExecutionGrant;
|
|
270
|
+
},
|
|
266
271
|
): Promise<SubmissionReceipt>;
|
|
267
272
|
/**
|
|
268
273
|
* 按提交编号查询当前处理状态。
|
|
@@ -406,14 +411,14 @@ export function defineRuntimeAgent<
|
|
|
406
411
|
| MutableRuntimeAgentDefinition<Env, Config, Command, Change>
|
|
407
412
|
| ReadonlyRuntimeAgentDefinition<Env, Config>,
|
|
408
413
|
): RuntimeAgentClass<Env, Command, Change> {
|
|
409
|
-
const resolveDefinitionTelemetry = (
|
|
414
|
+
const resolveDefinitionTelemetry = async (
|
|
410
415
|
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
411
|
-
): AgentTelemetryBinding | undefined => {
|
|
416
|
+
): Promise<AgentTelemetryBinding | undefined> => {
|
|
412
417
|
const telemetry = definition.telemetry;
|
|
413
418
|
if (!telemetry) return undefined;
|
|
414
|
-
return (telemetry as (
|
|
419
|
+
return await (telemetry as (
|
|
415
420
|
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
416
|
-
) => AgentTelemetryBinding | undefined)(context);
|
|
421
|
+
) => AgentTelemetryBinding | undefined | Promise<AgentTelemetryBinding | undefined>)(context);
|
|
417
422
|
};
|
|
418
423
|
|
|
419
424
|
const GeneratedRuntimeAgent = {
|
|
@@ -635,7 +640,7 @@ export function defineRuntimeAgent<
|
|
|
635
640
|
let resources = loaded.resources;
|
|
636
641
|
let commitGuard = loaded.commitGuard;
|
|
637
642
|
if (!this.telemetryResolved) {
|
|
638
|
-
this.resolvedTelemetry = resolveDefinitionTelemetry(context);
|
|
643
|
+
this.resolvedTelemetry = await resolveDefinitionTelemetry(context);
|
|
639
644
|
this.telemetryResolved = true;
|
|
640
645
|
}
|
|
641
646
|
const telemetry = this.resolvedTelemetry;
|
|
@@ -23,8 +23,26 @@ export {
|
|
|
23
23
|
export {
|
|
24
24
|
isValidTimeZone,
|
|
25
25
|
nextCronOccurrence,
|
|
26
|
+
wallTimeInstants,
|
|
26
27
|
zoneOffsetMs,
|
|
27
28
|
} from "./scheduling/zoned-cron";
|
|
29
|
+
export {
|
|
30
|
+
DEFAULT_LATENESS_WINDOW_MS,
|
|
31
|
+
isWithinLatenessWindow,
|
|
32
|
+
nextScheduledOccurrence,
|
|
33
|
+
resolveScheduleOccurrences,
|
|
34
|
+
toScheduleRuleSpec,
|
|
35
|
+
validateScheduleRuleSpec,
|
|
36
|
+
type LocalScheduleTrigger,
|
|
37
|
+
type ScheduledOccurrence,
|
|
38
|
+
type ScheduleOccurrence,
|
|
39
|
+
type ScheduleOccurrenceResolution,
|
|
40
|
+
type ScheduleRuleError,
|
|
41
|
+
type ScheduleRuleErrorCode,
|
|
42
|
+
type ScheduleRuleSpec,
|
|
43
|
+
type ScheduleSkipReason,
|
|
44
|
+
type SkippedOccurrence,
|
|
45
|
+
} from "./scheduling/schedule-rule";
|
|
28
46
|
|
|
29
47
|
export {
|
|
30
48
|
CloudflareSandboxAdapter,
|