@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.
Files changed (111) hide show
  1. package/package.json +8 -8
  2. package/src/adapter/cloudflare/agent.ts +160 -14
  3. package/src/adapter/cloudflare/define-agent.ts +12 -7
  4. package/src/adapter/cloudflare/index.ts +18 -0
  5. package/src/adapter/cloudflare/scheduling/schedule-rule.ts +322 -0
  6. package/src/adapter/cloudflare/scheduling/zoned-cron.ts +11 -6
  7. package/src/adapter/cloudflare/schema.ts +15 -0
  8. package/src/adapter/cloudflare/subagent/definition.ts +4 -4
  9. package/src/adapter/cloudflare/subagent/runner.ts +1 -1
  10. package/src/adapter/cloudflare/subagent/tools.ts +2 -2
  11. package/src/adapter/cloudflare/universal-agent/preparation.ts +3 -3
  12. package/src/adapter/cloudflare/universal-agent/tools.ts +4 -6
  13. package/src/adapter/cloudflare/workspace/version-control.ts +1 -1
  14. package/src/config/agent-context.ts +1 -1
  15. package/src/config/assembler.ts +29 -31
  16. package/src/config/assembly-snapshot.ts +1 -1
  17. package/src/config/definition.ts +1 -1
  18. package/src/config/harness-runtime.ts +9 -1
  19. package/src/config/model-guidance.ts +21 -0
  20. package/src/config/pinning.ts +22 -3
  21. package/src/config/ports.ts +27 -7
  22. package/src/config/public-contracts.ts +2 -2
  23. package/src/context/assembly.ts +1 -1
  24. package/src/context/extension/assembly.ts +2 -2
  25. package/src/context/memory/budget/gate.ts +19 -3
  26. package/src/conversation/harness-stream.ts +2 -1
  27. package/src/conversation/history.ts +2 -0
  28. package/src/conversation/message/projection.ts +38 -21
  29. package/src/conversation/state.ts +23 -2
  30. package/src/harness/session-harness.ts +160 -6
  31. package/src/index.ts +8 -7
  32. package/src/lib/artifacts.ts +18 -0
  33. package/src/lib/execution-level.ts +13 -0
  34. package/src/lib/prompt.ts +9 -10
  35. package/src/lib/token-estimate.ts +8 -0
  36. package/src/models/index.ts +32 -10
  37. package/src/models/openrouter/messages.ts +13 -8
  38. package/src/models/provider-streams.ts +45 -0
  39. package/src/session/cloudflare/migration.ts +71 -11
  40. package/src/skills/springbrand-worker-website/index.ts +1 -1
  41. package/src/submission/admission.ts +12 -1
  42. package/src/submission/contracts.ts +42 -2
  43. package/src/submission/delivery.ts +1 -1
  44. package/src/submission/finalization.ts +1 -1
  45. package/src/submission/repository.ts +64 -4
  46. package/src/submission/unattended.ts +40 -0
  47. package/src/telemetry/coordinator.ts +1 -1
  48. package/src/telemetry/subagent-lifecycle.ts +1 -1
  49. package/src/telemetry/turn-events.ts +1 -1
  50. package/src/tools/README.md +44 -0
  51. package/src/tools/{ai-adapter.ts → adapters/ai-to-pi.ts} +6 -9
  52. package/src/tools/{declared.ts → adapters/declared.ts} +6 -2
  53. package/src/tools/{nested-tools.ts → adapters/pi-to-ai.ts} +4 -4
  54. package/src/tools/assistant/ask-user.ts +317 -0
  55. package/src/tools/assistant/candidate.ts +15 -0
  56. package/src/tools/assistant/followups.ts +34 -0
  57. package/src/tools/assistant/index.ts +19 -0
  58. package/src/tools/assistant/memory.ts +73 -0
  59. package/src/tools/assistant/plan.ts +98 -0
  60. package/src/tools/{time.ts → assistant/time.ts} +1 -1
  61. package/src/tools/{core-host.ts → codemode/adapter.ts} +50 -53
  62. package/src/tools/{core.ts → codemode/index.ts} +36 -92
  63. package/src/tools/codemode/workspace-connector.ts +198 -0
  64. package/src/tools/extension/index.ts +42 -0
  65. package/src/tools/index.ts +29 -26
  66. package/src/tools/{gateway.ts → mcp/gateway.ts} +19 -19
  67. package/src/tools/{mcp.ts → mcp/index.ts} +5 -5
  68. package/src/tools/runtime/activation.ts +40 -0
  69. package/src/tools/{approval → runtime/approval}/repository.ts +13 -12
  70. package/src/tools/{compiler.ts → runtime/compiler.ts} +84 -147
  71. package/src/tools/runtime/contracts.ts +140 -0
  72. package/src/tools/{decision-gate.ts → runtime/decision-gate.ts} +147 -37
  73. package/src/tools/{human-decisions.ts → runtime/human-decisions.ts} +4 -4
  74. package/src/tools/{interaction → runtime/interaction}/repository.ts +1 -7
  75. package/src/tools/runtime/result.ts +22 -0
  76. package/src/tools/runtime/unattended-error.ts +4 -0
  77. package/src/tools/sandbox/index.ts +228 -0
  78. package/src/tools/schedule/index.ts +301 -0
  79. package/src/tools/skill/contracts.ts +20 -0
  80. package/src/tools/skill/index.ts +99 -0
  81. package/src/tools/skill/resources.ts +322 -0
  82. package/src/tools/skill/scripts.ts +168 -0
  83. package/src/tools/{subagent-authority.ts → subagent/authority.ts} +31 -31
  84. package/src/tools/{subagent.ts → subagent/index.ts} +34 -37
  85. package/src/tools/{subagent-run-repository.ts → subagent/repository.ts} +1 -1
  86. package/src/tools/{subagent-runtime.ts → subagent/runtime.ts} +1 -1
  87. package/src/tools/{temporary-agent → subagent/temporary}/approvals.ts +4 -7
  88. package/src/tools/{temporary-agent → subagent/temporary}/core.ts +1 -1
  89. package/src/tools/{temporary-agent → subagent/temporary}/runner.ts +1 -1
  90. package/src/tools/{temporary-agent → subagent/temporary}/workspace.ts +1 -4
  91. package/src/tools/{web-fetch.ts → web/fetch.ts} +4 -13
  92. package/src/tools/web/index.ts +2 -0
  93. package/src/tools/web/search/api.ts +1320 -0
  94. package/src/tools/web/search/index.ts +2 -0
  95. package/src/tools/{web-search/web-search.ts → web/search/search.ts} +5 -7
  96. package/src/tools/{workspace-sandbox.ts → workspace/index.ts} +12 -272
  97. package/src/tools/{workspace-revision.ts → workspace/revision.ts} +3 -3
  98. package/src/tools/base.ts +0 -534
  99. package/src/tools/schedule.ts +0 -261
  100. package/src/tools/skill.ts +0 -565
  101. package/src/tools/web-search/api.ts +0 -1318
  102. package/src/tools/web-search/index.ts +0 -2
  103. package/src/tools/workspace-state-connector.ts +0 -146
  104. package/src/tools/{approval → runtime/approval}/index.ts +0 -0
  105. package/src/tools/{interaction → runtime/interaction}/index.ts +0 -0
  106. package/src/tools/{temporary-agent → subagent/temporary}/coordinator.ts +0 -0
  107. package/src/tools/{subagent-types → subagent/types}/contract.ts +0 -0
  108. package/src/tools/{subagent-types → subagent/types}/extract/index.ts +0 -0
  109. package/src/tools/{subagent-types → subagent/types}/fanout/index.ts +0 -0
  110. package/src/tools/{subagent-types → subagent/types}/registry.ts +1 -1
  111. /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.1",
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.1",
20
+ "@cloudflare/codemode": "0.5.2",
21
21
  "@cloudflare/sandbox": "0.12.4",
22
22
  "@cloudflare/shell": "0.4.3",
23
- "@cloudflare/think": "0.17.0",
24
- "@earendil-works/pi-agent-core": "0.85.1",
25
- "@earendil-works/pi-ai": "0.85.1",
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.22.0",
33
+ "agents": "^0.23.0",
34
34
  "ai": "^7.0.0"
35
35
  },
36
36
  "devDependencies": {
37
- "agents": "0.22.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
- return this.currentAssemblyPin();
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
- return this.submissions.finish(
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
- return await this.executeHarnessSubmission(submission, mode);
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
- if (this.db.submissions.find(submissionId)?.abortReason) {
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
- record.status === "completed" || record.status === "declined"
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 status = record.status === "completed" || record.status === "declined"
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?: { idempotencyKey?: string },
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?: { idempotencyKey?: string },
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,