@springbrand/agent-runtime 0.3.0-alpha.1 → 0.3.0-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 +6 -6
- package/src/adapter/cloudflare/agent.ts +157 -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/message/projection.ts +38 -21
- package/src/conversation/state.ts +23 -2
- package/src/harness/session-harness.ts +86 -5
- package/src/index.ts +7 -7
- package/src/lib/artifacts.ts +18 -0
- package/src/lib/execution-level.ts +13 -0
- package/src/lib/prompt.ts +7 -6
- package/src/lib/token-estimate.ts +8 -0
- package/src/models/index.ts +1 -0
- package/src/session/cloudflare/migration.ts +70 -6
- 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} +2 -2
- 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 -49
- 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 +28 -26
- package/src/tools/{gateway.ts → mcp/gateway.ts} +19 -19
- package/src/tools/{mcp.ts → mcp/index.ts} +5 -5
- 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 +117 -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.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -13,14 +13,14 @@
|
|
|
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.
|
|
23
|
+
"@cloudflare/think": "0.18.0",
|
|
24
24
|
"@earendil-works/pi-agent-core": "0.85.1",
|
|
25
25
|
"@earendil-works/pi-ai": "0.85.1",
|
|
26
26
|
"@types/lodash-es": "^4.17.12",
|
|
@@ -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 {
|
|
@@ -98,6 +99,10 @@ import { RuntimeDatabase } from "./database";
|
|
|
98
99
|
// #region Runtime 内部数据与基础工具
|
|
99
100
|
|
|
100
101
|
const SCHEDULED_STABLE_TIMEOUT_MS = 30_000;
|
|
102
|
+
/** 定时 Run 未结束时拒绝普通输入的统一原因,服务端与客户端状态共用同一事实。 */
|
|
103
|
+
const SCHEDULED_INPUT_LOCKED_MESSAGE =
|
|
104
|
+
"This scheduled run is still in progress. Wait for it to finish before sending a message.";
|
|
105
|
+
|
|
101
106
|
interface ActiveTurn {
|
|
102
107
|
submissionId: string;
|
|
103
108
|
requestId: string;
|
|
@@ -390,7 +395,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
390
395
|
}
|
|
391
396
|
await this.broadcastApprovals();
|
|
392
397
|
const running = this.db.submissions.findRunning() as StoredSubmission | null;
|
|
393
|
-
if (running) {
|
|
398
|
+
if (running && !running.unattendedFailure) {
|
|
394
399
|
if (
|
|
395
400
|
this.db.approvals.listPendingForSubmission(running.submissionId).length > 0 ||
|
|
396
401
|
this.db.interactions.listPendingForSubmission(running.submissionId).length > 0
|
|
@@ -513,11 +518,15 @@ export abstract class AgentRuntimeKernel<
|
|
|
513
518
|
}> {
|
|
514
519
|
if (fresh) await this.prepareRuntimeForSubmission(submissionId);
|
|
515
520
|
else await this.ensureRuntimeReady();
|
|
516
|
-
|
|
521
|
+
const submission = this.db.submissions.find(submissionId);
|
|
522
|
+
if (!submission) {
|
|
523
|
+
throw new Error(`Unknown SpringBrand submission: ${submissionId}`);
|
|
524
|
+
}
|
|
525
|
+
return this.currentAssemblyPin(submission.origin !== "scheduled");
|
|
517
526
|
}
|
|
518
527
|
|
|
519
528
|
// initConfig can call this inside its serialized load; re-entering ensureRuntimeReady would deadlock.
|
|
520
|
-
private async currentAssemblyPin(): Promise<{
|
|
529
|
+
private async currentAssemblyPin(includeMemory = true): Promise<{
|
|
521
530
|
revision: string;
|
|
522
531
|
descriptor: string;
|
|
523
532
|
}> {
|
|
@@ -526,16 +535,18 @@ export abstract class AgentRuntimeKernel<
|
|
|
526
535
|
if (!runtime) {
|
|
527
536
|
throw new Error("Runtime revision is not initialized");
|
|
528
537
|
}
|
|
529
|
-
return this.pinAssembly(runtime.pi, runtime.revision);
|
|
538
|
+
return this.pinAssembly(runtime.pi, runtime.revision, includeMemory);
|
|
530
539
|
}
|
|
531
540
|
|
|
532
541
|
private async pinAssembly(
|
|
533
542
|
prepared: PreparedPiRuntime,
|
|
534
543
|
baseRevision: string,
|
|
544
|
+
includeMemory: boolean,
|
|
535
545
|
): Promise<{ revision: string; descriptor: string }> {
|
|
536
546
|
const pinned = await this.pi.pin({
|
|
537
547
|
prepared,
|
|
538
548
|
baseRevision,
|
|
549
|
+
includeMemory,
|
|
539
550
|
readExtensionContext: ({ label }) =>
|
|
540
551
|
this._hostGetContext(label),
|
|
541
552
|
});
|
|
@@ -668,11 +679,13 @@ export abstract class AgentRuntimeKernel<
|
|
|
668
679
|
return submission;
|
|
669
680
|
}
|
|
670
681
|
if (submission.abortReason && submission.status === "pending") {
|
|
671
|
-
|
|
682
|
+
const terminal = await this.submissions.finish(
|
|
672
683
|
submission,
|
|
673
684
|
"aborted",
|
|
674
685
|
submission.abortReason,
|
|
675
686
|
);
|
|
687
|
+
await this.projectTerminal(terminal);
|
|
688
|
+
return terminal;
|
|
676
689
|
}
|
|
677
690
|
if (mode === "recovery") {
|
|
678
691
|
this.telemetry.capture("turnRecovering", {
|
|
@@ -683,7 +696,11 @@ export abstract class AgentRuntimeKernel<
|
|
|
683
696
|
await this.broadcastApprovals();
|
|
684
697
|
}
|
|
685
698
|
try {
|
|
686
|
-
|
|
699
|
+
const outcome = await this.executeHarnessSubmission(submission, mode);
|
|
700
|
+
if (isTerminalSubmissionStatus(outcome.status)) {
|
|
701
|
+
await this.projectTerminal(outcome);
|
|
702
|
+
}
|
|
703
|
+
return outcome;
|
|
687
704
|
} catch (error) {
|
|
688
705
|
const failed = await this.submissions.finish(
|
|
689
706
|
submission,
|
|
@@ -697,10 +714,57 @@ export abstract class AgentRuntimeKernel<
|
|
|
697
714
|
: undefined,
|
|
698
715
|
mode !== "fresh",
|
|
699
716
|
);
|
|
717
|
+
await this.projectTerminal(failed);
|
|
700
718
|
return failed;
|
|
701
719
|
}
|
|
702
720
|
}
|
|
703
721
|
|
|
722
|
+
/** Project one authoritative Submission terminal after Harness and client settlement. */
|
|
723
|
+
private async projectTerminal(submission: StoredSubmission): Promise<void> {
|
|
724
|
+
if (!isTerminalSubmissionStatus(submission.status)) {
|
|
725
|
+
throw new Error(`Cannot project unfinished Submission: ${submission.submissionId}`);
|
|
726
|
+
}
|
|
727
|
+
const events = this.activeRuntime?.snapshot.bindings.turnEvents;
|
|
728
|
+
if (!events) return;
|
|
729
|
+
const messages = await this.getMessages();
|
|
730
|
+
try {
|
|
731
|
+
await events.onResponse(
|
|
732
|
+
messages.flatMap((message) =>
|
|
733
|
+
message.role === "user" || message.role === "assistant"
|
|
734
|
+
? [{
|
|
735
|
+
role: message.role,
|
|
736
|
+
text: message.parts
|
|
737
|
+
.filter(
|
|
738
|
+
(
|
|
739
|
+
part,
|
|
740
|
+
): part is Extract<UIMessage["parts"][number], { type: "text" }> =>
|
|
741
|
+
part.type === "text",
|
|
742
|
+
)
|
|
743
|
+
.map((part) => part.text)
|
|
744
|
+
.join(""),
|
|
745
|
+
}]
|
|
746
|
+
: []
|
|
747
|
+
),
|
|
748
|
+
);
|
|
749
|
+
} catch (error) {
|
|
750
|
+
console.warn("[runtime-turn-projection:degraded]", json({
|
|
751
|
+
error: errorText(error),
|
|
752
|
+
}));
|
|
753
|
+
}
|
|
754
|
+
try {
|
|
755
|
+
await events.onSubmissionTerminal?.({
|
|
756
|
+
submissionId: submission.submissionId,
|
|
757
|
+
status: submission.status,
|
|
758
|
+
...(submission.error ? { error: submission.error } : {}),
|
|
759
|
+
resultMessageId: submission.assistantMessageId,
|
|
760
|
+
});
|
|
761
|
+
} catch (error) {
|
|
762
|
+
console.warn("[runtime-terminal-projection:degraded]", json({
|
|
763
|
+
error: errorText(error),
|
|
764
|
+
}));
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
704
768
|
/** Run or recover the Submission through Pi's single durable AgentHarness. */
|
|
705
769
|
private async executeHarnessSubmission(
|
|
706
770
|
submission: StoredSubmission,
|
|
@@ -722,6 +786,17 @@ export abstract class AgentRuntimeKernel<
|
|
|
722
786
|
onGeneration: (event) => this.telemetry.generation(submissionId, event),
|
|
723
787
|
},
|
|
724
788
|
);
|
|
789
|
+
const prepareToolInput = (toolName: string, input: unknown): unknown => {
|
|
790
|
+
const prepare = harnessInput.candidates.find(({ tool }) =>
|
|
791
|
+
tool.name === toolName
|
|
792
|
+
)?.tool.prepareArguments;
|
|
793
|
+
if (!prepare) return input;
|
|
794
|
+
try {
|
|
795
|
+
return prepare(input);
|
|
796
|
+
} catch {
|
|
797
|
+
return input;
|
|
798
|
+
}
|
|
799
|
+
};
|
|
725
800
|
let runtime!: Awaited<ReturnType<typeof openSessionHarness>>;
|
|
726
801
|
let publishApprovalRequest = async (
|
|
727
802
|
_toolCallId: string,
|
|
@@ -732,6 +807,8 @@ export abstract class AgentRuntimeKernel<
|
|
|
732
807
|
submissionId,
|
|
733
808
|
requestId: submission.requestId,
|
|
734
809
|
executionLevel: activeRuntime.snapshot.profile.executionLevel,
|
|
810
|
+
origin: submission.origin,
|
|
811
|
+
scheduledMaxExecutionLevel: submission.scheduledMaxExecutionLevel ?? undefined,
|
|
735
812
|
},
|
|
736
813
|
candidates: harnessInput.candidates,
|
|
737
814
|
telemetry: this.telemetry,
|
|
@@ -759,6 +836,10 @@ export abstract class AgentRuntimeKernel<
|
|
|
759
836
|
}
|
|
760
837
|
},
|
|
761
838
|
gateTool: harnessInput.platform.gateTool,
|
|
839
|
+
onUnattendedFailure: (reason) => {
|
|
840
|
+
this.db.submissions.recordUnattendedFailure(submissionId, reason);
|
|
841
|
+
},
|
|
842
|
+
unattendedFailure: () => this.db.submissions.find(submissionId)?.unattendedFailure,
|
|
762
843
|
});
|
|
763
844
|
runtime = await openSessionHarness({
|
|
764
845
|
session: openCloudflareSession(this.ctx.storage),
|
|
@@ -771,6 +852,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
771
852
|
workspace: harnessInput.workspace,
|
|
772
853
|
authorize: gate.authorize.bind(gate),
|
|
773
854
|
resolveReplay: gate.resolveReplay.bind(gate),
|
|
855
|
+
resolveConfirmation: gate.resolveConfirmation.bind(gate),
|
|
774
856
|
}),
|
|
775
857
|
modelToolResultExemptions: harnessInput.candidates
|
|
776
858
|
.filter(({ outputBudget }) => outputBudget?.kind === "structure")
|
|
@@ -783,14 +865,26 @@ export abstract class AgentRuntimeKernel<
|
|
|
783
865
|
let stream: ReturnType<typeof bindHarnessStream> | undefined;
|
|
784
866
|
let deactivate = () => {};
|
|
785
867
|
try {
|
|
868
|
+
runtime.harness.hooks.on("after_response", ({ message }) => {
|
|
869
|
+
for (const part of message.content) {
|
|
870
|
+
if (part.type === "toolCall") {
|
|
871
|
+
part.arguments = prepareToolInput(
|
|
872
|
+
part.name,
|
|
873
|
+
part.arguments,
|
|
874
|
+
) as Record<string, unknown>;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
return { message };
|
|
878
|
+
});
|
|
786
879
|
runtime.harness.hooks.on("before_tool", gate.beforeTool);
|
|
787
880
|
runtime.harness.hooks.on("before_drive", async (event, context) => {
|
|
788
|
-
|
|
881
|
+
const current = this.db.submissions.find(submissionId);
|
|
882
|
+
if (current?.abortReason || current?.unattendedFailure) {
|
|
789
883
|
await runtime.requestAbort(event.runId, context);
|
|
790
884
|
}
|
|
791
885
|
});
|
|
792
886
|
|
|
793
|
-
if (submission.abortReason && runtime.openOperation) {
|
|
887
|
+
if ((submission.abortReason || submission.unattendedFailure) && runtime.openOperation) {
|
|
794
888
|
await runtime.requestAbort(
|
|
795
889
|
runtime.openOperation.operationId,
|
|
796
890
|
BACKGROUND_CONTEXT,
|
|
@@ -807,11 +901,15 @@ export abstract class AgentRuntimeKernel<
|
|
|
807
901
|
);
|
|
808
902
|
if (preparation.kind === "settled") {
|
|
809
903
|
const record = preparation.outcome;
|
|
904
|
+
const unattendedFailure = this.db.submissions.find(submissionId)
|
|
905
|
+
?.unattendedFailure;
|
|
810
906
|
const terminal = await this.submissions.finish(
|
|
811
907
|
submission,
|
|
812
|
-
|
|
908
|
+
unattendedFailure
|
|
909
|
+
? "failed"
|
|
910
|
+
: record.status === "completed" || record.status === "declined"
|
|
813
911
|
? "succeeded" : record.status === "aborted" ? "aborted" : "failed",
|
|
814
|
-
record.error?.message,
|
|
912
|
+
unattendedFailure ?? record.error?.message,
|
|
815
913
|
);
|
|
816
914
|
this.broadcast(json({
|
|
817
915
|
type: MessageType.CF_AGENT_CHAT_MESSAGES,
|
|
@@ -870,6 +968,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
870
968
|
userMessageId: submission.userMessageId ?? undefined,
|
|
871
969
|
startedAt: submission.createdAt,
|
|
872
970
|
turnId: submissionId,
|
|
971
|
+
prepareToolInput,
|
|
873
972
|
publish: async (chunks) => {
|
|
874
973
|
for (const chunk of chunks) {
|
|
875
974
|
this.conversation.broadcastResponse({
|
|
@@ -936,7 +1035,11 @@ export abstract class AgentRuntimeKernel<
|
|
|
936
1035
|
return this.db.submissions.find(submissionId)!;
|
|
937
1036
|
}
|
|
938
1037
|
const record = outcome.outcome;
|
|
939
|
-
const
|
|
1038
|
+
const unattendedFailure = this.db.submissions.find(submissionId)
|
|
1039
|
+
?.unattendedFailure;
|
|
1040
|
+
const status = unattendedFailure
|
|
1041
|
+
? "failed"
|
|
1042
|
+
: record.status === "completed" || record.status === "declined"
|
|
940
1043
|
? "succeeded"
|
|
941
1044
|
: record.status === "aborted"
|
|
942
1045
|
? "aborted"
|
|
@@ -945,7 +1048,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
945
1048
|
const terminal = await this.submissions.finish(
|
|
946
1049
|
submission,
|
|
947
1050
|
status,
|
|
948
|
-
record.error?.message,
|
|
1051
|
+
unattendedFailure ?? record.error?.message,
|
|
949
1052
|
);
|
|
950
1053
|
const projectedOutcome = projectSubmissionOutcome(record, terminal);
|
|
951
1054
|
await stream.flushTerminal(projectedOutcome);
|
|
@@ -1270,11 +1373,41 @@ export abstract class AgentRuntimeKernel<
|
|
|
1270
1373
|
this.transcript.importSnapshot(snapshot);
|
|
1271
1374
|
}
|
|
1272
1375
|
|
|
1376
|
+
/**
|
|
1377
|
+
* 本 Session 是否有一条未结束的定时 Submission。
|
|
1378
|
+
*
|
|
1379
|
+
* 定时 Run 未结束时独占输入:普通消息在准入前被拒绝,不排队等它结束。
|
|
1380
|
+
* 定时注入本身走 `submitScheduledPrompt`,不经过这道闸。
|
|
1381
|
+
*/
|
|
1382
|
+
private get scheduledInputLocked(): boolean {
|
|
1383
|
+
return this.db.submissions.hasUnfinishedScheduled();
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
private rejectedOrdinaryInputReceipt(): SubmissionReceipt {
|
|
1387
|
+
return {
|
|
1388
|
+
submissionId: crypto.randomUUID(),
|
|
1389
|
+
status: "skipped",
|
|
1390
|
+
accepted: false,
|
|
1391
|
+
error: SCHEDULED_INPUT_LOCKED_MESSAGE,
|
|
1392
|
+
retryable: false,
|
|
1393
|
+
createdAt: Date.now(),
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
private rejectInputLocked(): MessageDispatchReceipt {
|
|
1398
|
+
return {
|
|
1399
|
+
kind: "rejected",
|
|
1400
|
+
code: "input_locked",
|
|
1401
|
+
message: SCHEDULED_INPUT_LOCKED_MESSAGE,
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1273
1405
|
/** Admit text and return immediately; completion continues in the Durable Object. */
|
|
1274
1406
|
async submitPrompt(
|
|
1275
1407
|
text: string,
|
|
1276
1408
|
options?: { idempotencyKey?: string },
|
|
1277
1409
|
): Promise<SubmissionReceipt> {
|
|
1410
|
+
if (this.scheduledInputLocked) return this.rejectedOrdinaryInputReceipt();
|
|
1278
1411
|
const submitted = await this.submitMessage({
|
|
1279
1412
|
role: "user",
|
|
1280
1413
|
content: [{ type: "text", text }],
|
|
@@ -1304,17 +1437,24 @@ export abstract class AgentRuntimeKernel<
|
|
|
1304
1437
|
}
|
|
1305
1438
|
|
|
1306
1439
|
async dispatchMessage(message: UIMessage, delivery: MessageDelivery): Promise<MessageDispatchReceipt> {
|
|
1440
|
+
if (this.scheduledInputLocked) return this.rejectInputLocked();
|
|
1307
1441
|
return this.delivery.dispatch(message, delivery);
|
|
1308
1442
|
}
|
|
1309
1443
|
|
|
1310
1444
|
async steerQueuedSubmission(submissionId: string): Promise<MessageDispatchReceipt> {
|
|
1445
|
+
if (this.scheduledInputLocked) return this.rejectInputLocked();
|
|
1311
1446
|
return this.delivery.steerQueued(submissionId);
|
|
1312
1447
|
}
|
|
1313
1448
|
|
|
1314
1449
|
/** Admit scheduled work after a bounded stable wait, otherwise return skipped. */
|
|
1315
1450
|
async submitScheduledPrompt(
|
|
1316
1451
|
prompt: string,
|
|
1317
|
-
options?: {
|
|
1452
|
+
options?: {
|
|
1453
|
+
idempotencyKey?: string;
|
|
1454
|
+
scheduledMaxExecutionLevel?: ExecutionLevel;
|
|
1455
|
+
/** @deprecated Retained for rolling upgrades; ignored by Runtime. */
|
|
1456
|
+
unattendedExecutionGrant?: RuntimeUnattendedExecutionGrant;
|
|
1457
|
+
},
|
|
1318
1458
|
): Promise<SubmissionReceipt> {
|
|
1319
1459
|
const requestId = crypto.randomUUID();
|
|
1320
1460
|
const submitted = await this.submitMessageWhenStable(
|
|
@@ -1326,6 +1466,8 @@ export abstract class AgentRuntimeKernel<
|
|
|
1326
1466
|
{
|
|
1327
1467
|
requestId,
|
|
1328
1468
|
idempotencyKey: options?.idempotencyKey,
|
|
1469
|
+
origin: "scheduled",
|
|
1470
|
+
scheduledMaxExecutionLevel: options?.scheduledMaxExecutionLevel,
|
|
1329
1471
|
},
|
|
1330
1472
|
SCHEDULED_STABLE_TIMEOUT_MS,
|
|
1331
1473
|
);
|
|
@@ -1410,7 +1552,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
1410
1552
|
summary: approval.summary,
|
|
1411
1553
|
executionLevel: approval.executionLevel,
|
|
1412
1554
|
requiredExecutionLevel: approval.requiredExecutionLevel,
|
|
1413
|
-
inputJson: approval.inputJson,
|
|
1555
|
+
inputJson: approval.confirmationJson ?? approval.inputJson,
|
|
1414
1556
|
requestId: approval.requestId,
|
|
1415
1557
|
};
|
|
1416
1558
|
}
|
|
@@ -1520,6 +1662,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
1520
1662
|
hasPendingInteraction: (id) => this.humanDecisions.isWaitingForInteraction(id),
|
|
1521
1663
|
hasPendingSteer: (id) => this.db.steers.listForSubmission(id).length > 0,
|
|
1522
1664
|
hasRunningAgentTools: this.db.agentTools.hasRunning(),
|
|
1665
|
+
hasUnfinishedScheduled: this.db.submissions.hasUnfinishedScheduled(),
|
|
1523
1666
|
previousState: this.state,
|
|
1524
1667
|
});
|
|
1525
1668
|
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,
|