@springbrand/agent-runtime 0.3.0-alpha.2 → 0.3.0-alpha.21
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 +11 -9
- package/src/adapter/cloudflare/agent.ts +233 -179
- package/src/adapter/cloudflare/define-agent.ts +252 -104
- 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 +21 -2
- package/src/adapter/cloudflare/subagent/definition.ts +25 -13
- package/src/adapter/cloudflare/subagent/runner.ts +43 -50
- package/src/adapter/cloudflare/subagent/tools.ts +4 -4
- 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/activation.ts +13 -15
- package/src/config/agent-context.ts +1 -1
- package/src/config/assembler.ts +48 -31
- package/src/config/assembly-snapshot.ts +1 -1
- package/src/config/definition.ts +1 -1
- package/src/config/harness-runtime.ts +77 -49
- package/src/config/index.ts +1 -1
- package/src/config/model-guidance.ts +20 -0
- package/src/config/ports.ts +57 -7
- package/src/config/{pinning.ts → prepared-runtime.ts} +59 -123
- package/src/config/public-contracts.ts +2 -2
- package/src/config/runtime-config.ts +2 -1
- 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-resume.ts +89 -238
- package/src/conversation/harness-stream.ts +87 -71
- package/src/conversation/history.ts +154 -152
- package/src/conversation/message/contract.ts +28 -4
- package/src/conversation/message/conversion.ts +46 -5
- package/src/conversation/message/projection.ts +61 -21
- package/src/conversation/session-entry.ts +29 -0
- package/src/conversation/state.ts +23 -2
- package/src/harness/governance.ts +206 -0
- package/src/harness/index.ts +3 -0
- package/src/harness/session-harness.ts +269 -445
- package/src/harness/submission-operation.ts +15 -40
- package/src/index.ts +18 -8
- 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/model-protocol/openrouter.ts +41 -0
- package/src/models/index.ts +200 -35
- package/src/models/openrouter/messages.ts +13 -8
- package/src/models/openrouter/openai.ts +200 -0
- package/src/models/openrouter/protocol-response.ts +85 -0
- package/src/models/provider-streams.ts +45 -0
- package/src/session/cloudflare/migration.ts +382 -864
- package/src/session/cloudflare/storage.ts +13 -536
- package/src/session/index.ts +22 -35
- package/src/skills/springbrand-worker-website/index.ts +1 -1
- package/src/submission/admission.ts +20 -30
- package/src/submission/contracts.ts +42 -2
- package/src/submission/delivery.ts +1 -1
- package/src/submission/finalization.ts +36 -15
- package/src/submission/lifecycle.ts +28 -15
- package/src/submission/repository.ts +97 -49
- package/src/submission/steer-delivery.ts +32 -76
- package/src/submission/unattended.ts +40 -0
- package/src/telemetry/contract.ts +0 -1
- package/src/telemetry/coordinator.ts +1 -1
- package/src/telemetry/runtime-telemetry.ts +0 -4
- package/src/telemetry/subagent-lifecycle.ts +1 -1
- package/src/telemetry/turn-events.ts +35 -25
- 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 +319 -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/runtime/compiler.ts +246 -0
- package/src/tools/runtime/contracts.ts +140 -0
- package/src/tools/{decision-gate.ts → runtime/decision-gate.ts} +149 -39
- 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 +2 -3
- 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 +1322 -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/compiler.ts +0 -327
- 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
|
@@ -12,12 +12,14 @@ import type {
|
|
|
12
12
|
MessageDelivery,
|
|
13
13
|
MessageDispatchReceipt,
|
|
14
14
|
SubmissionReceipt,
|
|
15
|
+
StoredRuntimeKey,
|
|
16
|
+
RuntimeKeyRewrite,
|
|
15
17
|
} from "../../submission";
|
|
16
18
|
import type { RuntimeState } from "../../conversation";
|
|
17
19
|
import type {
|
|
18
20
|
TemporaryAgentApprovalDecision,
|
|
19
21
|
TemporaryAgentApprovalRequest,
|
|
20
|
-
|
|
22
|
+
TemporaryAgentLaunchSelection,
|
|
21
23
|
} from "../../tools";
|
|
22
24
|
import { TEMPORARY_AGENT_LAUNCH_KEY } from "../../tools";
|
|
23
25
|
import {
|
|
@@ -31,7 +33,9 @@ import {
|
|
|
31
33
|
} from "../../lib/execution-level";
|
|
32
34
|
import {
|
|
33
35
|
assembleRuntimeSnapshot,
|
|
36
|
+
verifyRuntimeCandidateGuards,
|
|
34
37
|
type RuntimeCandidate,
|
|
38
|
+
type RuntimeSubmissionAdmissionHook,
|
|
35
39
|
} from "../../config";
|
|
36
40
|
import type {
|
|
37
41
|
ResolvedResources,
|
|
@@ -71,6 +75,7 @@ export type {
|
|
|
71
75
|
RuntimeAgentToolResult,
|
|
72
76
|
RuntimeAssemblyContext,
|
|
73
77
|
} from "../../config";
|
|
78
|
+
export type { RuntimeKeyRewrite, StoredRuntimeKey } from "../../submission";
|
|
74
79
|
|
|
75
80
|
/** Host 一次性准备的完整 Runtime 加载结果。 */
|
|
76
81
|
export interface LoadedRuntime<Config> {
|
|
@@ -83,7 +88,7 @@ export interface LoadedRuntime<Config> {
|
|
|
83
88
|
export interface RuntimeConfigUpdate<Change> {
|
|
84
89
|
readonly changed: boolean;
|
|
85
90
|
readonly change: Change;
|
|
86
|
-
/** Restore
|
|
91
|
+
/** Restore committed state if an immediate replacement fails. */
|
|
87
92
|
readonly rollback?: () => Promise<void>;
|
|
88
93
|
}
|
|
89
94
|
|
|
@@ -96,16 +101,27 @@ interface RuntimeAgentDefinitionBase<
|
|
|
96
101
|
/** 必须等于 Worker 导出名(Wrangler `class_name`)。 */
|
|
97
102
|
readonly name: string;
|
|
98
103
|
|
|
99
|
-
/** Refresh Host-owned inputs before a new Submission
|
|
104
|
+
/** Refresh Host-owned inputs before a new Submission records its Runtime key. */
|
|
100
105
|
readonly beforeSubmission?: (
|
|
101
106
|
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
102
|
-
) => Promise<
|
|
107
|
+
) => Promise<void>;
|
|
108
|
+
|
|
109
|
+
/** Prepare per-Submission Host state after admission and before first execution. */
|
|
110
|
+
readonly afterSubmission?: (
|
|
111
|
+
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
112
|
+
) => Promise<void>;
|
|
103
113
|
|
|
104
114
|
readonly load: (
|
|
105
115
|
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
106
116
|
requestedRuntimeKey?: string,
|
|
107
117
|
) => Promise<LoadedRuntime<Config>>;
|
|
108
118
|
|
|
119
|
+
/** Resolve saved identities; unfinished work must still belong to the selected Agent. */
|
|
120
|
+
readonly migrateStoredRuntimeKeys?: (
|
|
121
|
+
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
122
|
+
keys: readonly StoredRuntimeKey[],
|
|
123
|
+
) => Promise<readonly RuntimeKeyRewrite[]>;
|
|
124
|
+
|
|
109
125
|
readonly tools: (
|
|
110
126
|
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
111
127
|
config: Config,
|
|
@@ -114,7 +130,7 @@ interface RuntimeAgentDefinitionBase<
|
|
|
114
130
|
/** Prepare the optional telemetry consumer for this concrete Agent facet. */
|
|
115
131
|
readonly telemetry?: (
|
|
116
132
|
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
117
|
-
) => AgentTelemetryBinding | undefined
|
|
133
|
+
) => AgentTelemetryBinding | undefined | Promise<AgentTelemetryBinding | undefined>;
|
|
118
134
|
}
|
|
119
135
|
|
|
120
136
|
export interface ReadonlyRuntimeAgentDefinition<
|
|
@@ -262,7 +278,12 @@ export interface RuntimeAgentControls {
|
|
|
262
278
|
*/
|
|
263
279
|
submitScheduledPrompt(
|
|
264
280
|
prompt: string,
|
|
265
|
-
opts?: {
|
|
281
|
+
opts?: {
|
|
282
|
+
idempotencyKey?: string;
|
|
283
|
+
scheduledMaxExecutionLevel?: import("../../lib/execution-level").ExecutionLevel;
|
|
284
|
+
/** @deprecated Retained for rolling upgrades; ignored by Runtime. */
|
|
285
|
+
unattendedExecutionGrant?: import("../../submission").RuntimeUnattendedExecutionGrant;
|
|
286
|
+
},
|
|
266
287
|
): Promise<SubmissionReceipt>;
|
|
267
288
|
/**
|
|
268
289
|
* 按提交编号查询当前处理状态。
|
|
@@ -311,6 +332,7 @@ declare class RuntimeAgentHostSeam {
|
|
|
311
332
|
protected prepareToolInteractionResponse(
|
|
312
333
|
input: import("./agent").ToolInteractionResponsePreparation,
|
|
313
334
|
): Promise<unknown | null>;
|
|
335
|
+
protected hasUnfinishedRuntimeWork(): boolean;
|
|
314
336
|
}
|
|
315
337
|
|
|
316
338
|
/**
|
|
@@ -354,10 +376,9 @@ const TERMINAL_AGENT_TOOL_STATUSES = [
|
|
|
354
376
|
"interrupted",
|
|
355
377
|
] as const;
|
|
356
378
|
const AGENT_TOOL_RETENTION_MS = 24 * 60 * 60 * 1_000;
|
|
379
|
+
const STABLE_RUNTIME_KEY_MIGRATION = "agent-runtime:stable-runtime-key-migration-v1";
|
|
357
380
|
|
|
358
|
-
function parseTemporaryAgentLaunch
|
|
359
|
-
input: unknown,
|
|
360
|
-
): TemporaryAgentLaunch<Config> {
|
|
381
|
+
function parseTemporaryAgentLaunch(input: unknown): TemporaryAgentLaunchSelection {
|
|
361
382
|
const record = input != null && typeof input === "object"
|
|
362
383
|
? input as Record<string, unknown>
|
|
363
384
|
: {};
|
|
@@ -375,17 +396,15 @@ function parseTemporaryAgentLaunch<Config>(
|
|
|
375
396
|
) {
|
|
376
397
|
throw new Error("invalid temporary agent executionLevel");
|
|
377
398
|
}
|
|
378
|
-
|
|
379
|
-
throw new Error("invalid temporary agent config");
|
|
380
|
-
}
|
|
381
|
-
return {
|
|
399
|
+
const selection: TemporaryAgentLaunchSelection = {
|
|
382
400
|
subagentName: read("subagentName", 128),
|
|
383
401
|
instructions: read("instructions", 65_536),
|
|
384
402
|
task: read("task", 65_536),
|
|
385
403
|
runtimeKey: read("runtimeKey", 512),
|
|
386
|
-
config: record.config as Config,
|
|
387
404
|
executionLevel: executionLevel as ExecutionLevel,
|
|
388
405
|
};
|
|
406
|
+
if ("config" in record) throw new Error("unexpected temporary agent config");
|
|
407
|
+
return selection;
|
|
389
408
|
}
|
|
390
409
|
|
|
391
410
|
/**
|
|
@@ -406,15 +425,17 @@ export function defineRuntimeAgent<
|
|
|
406
425
|
| MutableRuntimeAgentDefinition<Env, Config, Command, Change>
|
|
407
426
|
| ReadonlyRuntimeAgentDefinition<Env, Config>,
|
|
408
427
|
): RuntimeAgentClass<Env, Command, Change> {
|
|
409
|
-
const resolveDefinitionTelemetry = (
|
|
428
|
+
const resolveDefinitionTelemetry = async (
|
|
410
429
|
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
411
|
-
): AgentTelemetryBinding | undefined => {
|
|
430
|
+
): Promise<AgentTelemetryBinding | undefined> => {
|
|
412
431
|
const telemetry = definition.telemetry;
|
|
413
432
|
if (!telemetry) return undefined;
|
|
414
|
-
return (telemetry as (
|
|
433
|
+
return await (telemetry as (
|
|
415
434
|
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
416
|
-
) => AgentTelemetryBinding | undefined)(context);
|
|
435
|
+
) => AgentTelemetryBinding | undefined | Promise<AgentTelemetryBinding | undefined>)(context);
|
|
417
436
|
};
|
|
437
|
+
const migrateSavedKeys = definition.migrateStoredRuntimeKeys as
|
|
438
|
+
RuntimeAgentDefinitionBase<Env, Config, Command, Change>["migrateStoredRuntimeKeys"];
|
|
418
439
|
|
|
419
440
|
const GeneratedRuntimeAgent = {
|
|
420
441
|
[definition.name]: class extends AgentToolRuntimeKernel<Env> {
|
|
@@ -423,9 +444,71 @@ export function defineRuntimeAgent<
|
|
|
423
444
|
private loading?: Promise<void>;
|
|
424
445
|
private telemetryResolved = false;
|
|
425
446
|
private resolvedTelemetry?: AgentTelemetryBinding;
|
|
426
|
-
private temporaryLaunch?:
|
|
447
|
+
private temporaryLaunch?: TemporaryAgentLaunchSelection;
|
|
427
448
|
private temporaryDispose?: () => Promise<void>;
|
|
428
449
|
private temporaryCleanup?: Promise<void>;
|
|
450
|
+
private storedKeyMigration?: Promise<void>;
|
|
451
|
+
|
|
452
|
+
async onStart(): Promise<void> {
|
|
453
|
+
await this.migrateStoredRuntimeKeys();
|
|
454
|
+
await super.onStart();
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
private async resolveSavedKeyRewrites(
|
|
458
|
+
keys: readonly StoredRuntimeKey[],
|
|
459
|
+
): Promise<readonly RuntimeKeyRewrite[]> {
|
|
460
|
+
if (!migrateSavedKeys) {
|
|
461
|
+
return keys.map(({ runtimeKey }) => ({ from: runtimeKey, to: runtimeKey }));
|
|
462
|
+
}
|
|
463
|
+
const rewrites = await migrateSavedKeys(this.createDefinitionContext(), keys);
|
|
464
|
+
if (
|
|
465
|
+
rewrites.length !== keys.length ||
|
|
466
|
+
rewrites.some(({ from, to }, index) =>
|
|
467
|
+
from !== keys[index]?.runtimeKey || !to.trim())
|
|
468
|
+
) {
|
|
469
|
+
throw new Error("Saved Runtime key migration returned an invalid rewrite plan");
|
|
470
|
+
}
|
|
471
|
+
return rewrites;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
private migrateStoredRuntimeKeys(): Promise<void> {
|
|
475
|
+
if (!migrateSavedKeys) return Promise.resolve();
|
|
476
|
+
if (this.storedKeyMigration) return this.storedKeyMigration;
|
|
477
|
+
const migration = (async () => {
|
|
478
|
+
if (await this.ctx.storage.get<boolean>(STABLE_RUNTIME_KEY_MIGRATION)) return;
|
|
479
|
+
const savedLaunch = await this.ctx.storage.get<unknown>(TEMPORARY_AGENT_LAUNCH_KEY);
|
|
480
|
+
const launch = savedLaunch === undefined
|
|
481
|
+
? undefined
|
|
482
|
+
: parseTemporaryAgentLaunch(savedLaunch);
|
|
483
|
+
const keysByValue = new Map(
|
|
484
|
+
this.savedRuntimeKeys().map((key) => [key.runtimeKey, key]),
|
|
485
|
+
);
|
|
486
|
+
if (launch) {
|
|
487
|
+
keysByValue.set(launch.runtimeKey, {
|
|
488
|
+
runtimeKey: launch.runtimeKey,
|
|
489
|
+
unfinished: keysByValue.get(launch.runtimeKey)?.unfinished ?? true,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
const keys = [...keysByValue.values()];
|
|
493
|
+
if (keys.length > 0) {
|
|
494
|
+
const rewrites = await this.resolveSavedKeyRewrites(keys);
|
|
495
|
+
this.rewriteSavedRuntimeKeys(rewrites);
|
|
496
|
+
if (launch) {
|
|
497
|
+
const runtimeKey = rewrites.find(({ from }) => from === launch.runtimeKey)?.to;
|
|
498
|
+
if (!runtimeKey) throw new Error("Saved temporary Agent key was not migrated");
|
|
499
|
+
const currentLaunch = { ...launch, runtimeKey };
|
|
500
|
+
await this.ctx.storage.put(TEMPORARY_AGENT_LAUNCH_KEY, currentLaunch);
|
|
501
|
+
this.temporaryLaunch = currentLaunch;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
await this.ctx.storage.put(STABLE_RUNTIME_KEY_MIGRATION, true);
|
|
505
|
+
})().catch((error) => {
|
|
506
|
+
this.storedKeyMigration = undefined;
|
|
507
|
+
throw error;
|
|
508
|
+
});
|
|
509
|
+
this.storedKeyMigration = migration;
|
|
510
|
+
return migration;
|
|
511
|
+
}
|
|
429
512
|
|
|
430
513
|
protected get role(): RuntimeAgentRole {
|
|
431
514
|
return this.parentPath.at(-1)?.className === definition.name
|
|
@@ -440,15 +523,27 @@ export function defineRuntimeAgent<
|
|
|
440
523
|
return this.ensureConfig();
|
|
441
524
|
}
|
|
442
525
|
|
|
443
|
-
protected override ensureRuntimeVersion(runtimeKey: string): Promise<void> {
|
|
444
|
-
return this.ensureConfig(runtimeKey);
|
|
526
|
+
protected override ensureRuntimeVersion(runtimeKey: string, force = false): Promise<void> {
|
|
527
|
+
return this.ensureConfig(runtimeKey, force);
|
|
445
528
|
}
|
|
446
529
|
|
|
447
|
-
protected async
|
|
530
|
+
protected async prepareRuntimeForAdmission(): Promise<{
|
|
531
|
+
runtimeKey: string;
|
|
532
|
+
admission?: RuntimeSubmissionAdmissionHook;
|
|
533
|
+
}> {
|
|
448
534
|
const beforeSubmission = definition.beforeSubmission as RuntimeAgentDefinitionBase<Env, Config, Command, Change>["beforeSubmission"];
|
|
535
|
+
await beforeSubmission?.(this.createDefinitionContext());
|
|
536
|
+
const requestedKey = this.role === "temporary"
|
|
537
|
+
? this.loadedRuntimeKey ?? this.temporaryLaunch?.runtimeKey
|
|
538
|
+
: undefined;
|
|
539
|
+
return this.inspectCandidate(requestedKey);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
protected async prepareRuntimeForSubmission(submissionId: string): Promise<void> {
|
|
543
|
+
const afterSubmission = definition.afterSubmission as RuntimeAgentDefinitionBase<Env, Config, Command, Change>["afterSubmission"];
|
|
449
544
|
try {
|
|
450
|
-
|
|
451
|
-
await this.
|
|
545
|
+
await this.ensureRuntimeReady();
|
|
546
|
+
await afterSubmission?.(this.createDefinitionContext(undefined, submissionId));
|
|
452
547
|
} catch (error) {
|
|
453
548
|
this.hasLoadedRuntime = false;
|
|
454
549
|
throw error;
|
|
@@ -462,7 +557,7 @@ export function defineRuntimeAgent<
|
|
|
462
557
|
runtimeKey?: string,
|
|
463
558
|
options?: { force?: boolean },
|
|
464
559
|
): Promise<void> {
|
|
465
|
-
await this.ensureConfig(runtimeKey, options?.force === true);
|
|
560
|
+
await this.runConfigControl(() => this.ensureConfig(runtimeKey, options?.force === true));
|
|
466
561
|
}
|
|
467
562
|
|
|
468
563
|
async updateConfig(
|
|
@@ -475,26 +570,36 @@ export function defineRuntimeAgent<
|
|
|
475
570
|
if (!update) {
|
|
476
571
|
return { change: undefined as Change, runtime: "unchanged" };
|
|
477
572
|
}
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
await result.rollback();
|
|
489
|
-
} catch (rollbackError) {
|
|
490
|
-
throw new AggregateError(
|
|
491
|
-
[reloadError, rollbackError],
|
|
492
|
-
"Runtime Config rollback failed",
|
|
493
|
-
);
|
|
573
|
+
const deferActivation = this.isSubmissionExecuting();
|
|
574
|
+
const apply = async (): Promise<RuntimeConfigUpdateResult<Change>> => {
|
|
575
|
+
const result = await update(this.createDefinitionContext(), command);
|
|
576
|
+
if (!result.changed) {
|
|
577
|
+
return { change: result.change, runtime: "unchanged" };
|
|
578
|
+
}
|
|
579
|
+
try {
|
|
580
|
+
if (deferActivation) {
|
|
581
|
+
await this.inspectCandidate();
|
|
582
|
+
return { change: result.change, runtime: "deferred" };
|
|
494
583
|
}
|
|
584
|
+
await this.ensureConfig(undefined, true);
|
|
585
|
+
return { change: result.change, runtime: "reloaded" };
|
|
586
|
+
} catch (reloadError) {
|
|
587
|
+
if (result.rollback) {
|
|
588
|
+
try {
|
|
589
|
+
await result.rollback();
|
|
590
|
+
} catch (rollbackError) {
|
|
591
|
+
throw new AggregateError(
|
|
592
|
+
[reloadError, rollbackError],
|
|
593
|
+
"Runtime Config rollback failed",
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return { change: result.change, runtime: "reload-failed" };
|
|
495
598
|
}
|
|
496
|
-
|
|
497
|
-
|
|
599
|
+
};
|
|
600
|
+
return deferActivation
|
|
601
|
+
? apply()
|
|
602
|
+
: this.runConfigControl(apply);
|
|
498
603
|
}
|
|
499
604
|
|
|
500
605
|
async getRuntimeAssembly(): Promise<RuntimeAssemblyView> {
|
|
@@ -546,13 +651,105 @@ export function defineRuntimeAgent<
|
|
|
546
651
|
if (this.role !== "temporary") {
|
|
547
652
|
throw new Error("Agent Tool runs require a temporary child facet");
|
|
548
653
|
}
|
|
549
|
-
const
|
|
654
|
+
const parsed = parseTemporaryAgentLaunch(input);
|
|
655
|
+
const [rewrite] = await this.resolveSavedKeyRewrites([
|
|
656
|
+
{ runtimeKey: parsed.runtimeKey, unfinished: true },
|
|
657
|
+
]);
|
|
658
|
+
const runtimeKey = rewrite?.to;
|
|
659
|
+
if (!runtimeKey) throw new Error("Temporary Agent runtime key must not be empty");
|
|
660
|
+
const launch = { ...parsed, runtimeKey };
|
|
550
661
|
this.temporaryLaunch = launch;
|
|
551
662
|
await this.ctx.storage.put(TEMPORARY_AGENT_LAUNCH_KEY, launch);
|
|
552
663
|
await this.reloadRuntime(launch.runtimeKey, { force: true });
|
|
553
664
|
return launch.task;
|
|
554
665
|
}
|
|
555
666
|
|
|
667
|
+
/** One definition load and assembly path for admission and execution. */
|
|
668
|
+
private async prepareCandidate(requestedRuntimeKey?: string): Promise<{
|
|
669
|
+
candidate: RuntimeCandidate;
|
|
670
|
+
runtimeKey: string;
|
|
671
|
+
dispose?: () => Promise<void>;
|
|
672
|
+
}> {
|
|
673
|
+
const loadContext = this.createDefinitionContext();
|
|
674
|
+
const loaded = await withRuntimeLoadTimeout(
|
|
675
|
+
"definition.load",
|
|
676
|
+
() => (
|
|
677
|
+
definition.load as (
|
|
678
|
+
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
679
|
+
requestedRuntimeKey?: string,
|
|
680
|
+
) => Promise<LoadedRuntime<Config>>
|
|
681
|
+
)(loadContext, requestedRuntimeKey),
|
|
682
|
+
{ timeoutMs: RUNTIME_LOAD_TIMEOUT_MS },
|
|
683
|
+
);
|
|
684
|
+
const runtimeKey = loaded.runtimeKey.trim();
|
|
685
|
+
if (!runtimeKey) {
|
|
686
|
+
throw new Error("Resolved Runtime key must not be empty");
|
|
687
|
+
}
|
|
688
|
+
if (requestedRuntimeKey && runtimeKey !== requestedRuntimeKey) {
|
|
689
|
+
throw new Error(
|
|
690
|
+
`Requested Runtime key ${requestedRuntimeKey} resolved to ${runtimeKey}`,
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
const context = this.createDefinitionContext({ value: loaded.config });
|
|
694
|
+
let tools = await withRuntimeLoadTimeout(
|
|
695
|
+
"definition.tools",
|
|
696
|
+
() => (
|
|
697
|
+
definition.tools as (
|
|
698
|
+
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
699
|
+
config: Config,
|
|
700
|
+
) => ToolAssemblyResult | Promise<ToolAssemblyResult>
|
|
701
|
+
)(context, loaded.config),
|
|
702
|
+
{ timeoutMs: RUNTIME_LOAD_TIMEOUT_MS },
|
|
703
|
+
);
|
|
704
|
+
const dispose = tools.dispose;
|
|
705
|
+
try {
|
|
706
|
+
let resources = loaded.resources;
|
|
707
|
+
let commitGuard = loaded.commitGuard;
|
|
708
|
+
if (!this.telemetryResolved) {
|
|
709
|
+
this.resolvedTelemetry = await resolveDefinitionTelemetry(context);
|
|
710
|
+
this.telemetryResolved = true;
|
|
711
|
+
}
|
|
712
|
+
const telemetry = this.resolvedTelemetry;
|
|
713
|
+
if (this.role === "temporary") {
|
|
714
|
+
tools = this.applyTemporaryToolPolicy(tools);
|
|
715
|
+
resources = await this.applyTemporaryResources(resources);
|
|
716
|
+
commitGuard = undefined;
|
|
717
|
+
}
|
|
718
|
+
const candidate = await assembleRuntimeSnapshot({
|
|
719
|
+
ctx: context,
|
|
720
|
+
tools,
|
|
721
|
+
resources,
|
|
722
|
+
...(commitGuard ? { commitGuard } : {}),
|
|
723
|
+
...(telemetry ? { telemetry } : {}),
|
|
724
|
+
});
|
|
725
|
+
return {
|
|
726
|
+
candidate: { ...candidate, runtimeKey },
|
|
727
|
+
runtimeKey,
|
|
728
|
+
...(dispose ? { dispose } : {}),
|
|
729
|
+
};
|
|
730
|
+
} catch (error) {
|
|
731
|
+
await dispose?.();
|
|
732
|
+
throw error;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/** Validate a candidate without changing the active Runtime or its Gateway. */
|
|
737
|
+
private async inspectCandidate(requestedRuntimeKey?: string): Promise<{
|
|
738
|
+
runtimeKey: string;
|
|
739
|
+
admission?: RuntimeSubmissionAdmissionHook;
|
|
740
|
+
}> {
|
|
741
|
+
const prepared = await this.prepareCandidate(requestedRuntimeKey);
|
|
742
|
+
try {
|
|
743
|
+
await verifyRuntimeCandidateGuards(prepared.candidate, "admission");
|
|
744
|
+
return {
|
|
745
|
+
runtimeKey: prepared.runtimeKey,
|
|
746
|
+
admission: prepared.candidate.admission,
|
|
747
|
+
};
|
|
748
|
+
} finally {
|
|
749
|
+
await prepared.dispose?.();
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
556
753
|
// 作用:把首次加载和按 key 重载合并成一个串行入口。
|
|
557
754
|
// 调用:Runtime readiness、Session 配置和重载都会调用。
|
|
558
755
|
// 原因:共享同一个 Promise 可合并并发首加载,显式切 key 则等待前序完成后再判断。
|
|
@@ -601,64 +798,16 @@ export function defineRuntimeAgent<
|
|
|
601
798
|
// 先创建局部 Promise,再赋给 `loading`,失败时也能在 finally 中放开重试。
|
|
602
799
|
const pending = (async () => {
|
|
603
800
|
this.runtimeLoad.begin();
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
requestedRuntimeKey?: string,
|
|
611
|
-
) => Promise<LoadedRuntime<Config>>
|
|
612
|
-
)(
|
|
613
|
-
loadContext,
|
|
614
|
-
requestedRuntimeKey,
|
|
615
|
-
),
|
|
616
|
-
{ timeoutMs: RUNTIME_LOAD_TIMEOUT_MS },
|
|
617
|
-
);
|
|
618
|
-
const resolvedRuntimeKey = loaded.runtimeKey.trim();
|
|
619
|
-
if (!resolvedRuntimeKey) {
|
|
620
|
-
throw new Error("Resolved Runtime key must not be empty");
|
|
621
|
-
}
|
|
622
|
-
const context = this.createDefinitionContext({
|
|
623
|
-
value: loaded.config,
|
|
624
|
-
});
|
|
625
|
-
let tools = await withRuntimeLoadTimeout(
|
|
626
|
-
"definition.tools",
|
|
627
|
-
() => (
|
|
628
|
-
definition.tools as (
|
|
629
|
-
context: RuntimeAgentPlanningContext<Env, Command, Change>,
|
|
630
|
-
config: Config,
|
|
631
|
-
) => ToolAssemblyResult | Promise<ToolAssemblyResult>
|
|
632
|
-
)(context, loaded.config),
|
|
633
|
-
{ timeoutMs: RUNTIME_LOAD_TIMEOUT_MS },
|
|
634
|
-
);
|
|
635
|
-
let resources = loaded.resources;
|
|
636
|
-
let commitGuard = loaded.commitGuard;
|
|
637
|
-
if (!this.telemetryResolved) {
|
|
638
|
-
this.resolvedTelemetry = resolveDefinitionTelemetry(context);
|
|
639
|
-
this.telemetryResolved = true;
|
|
640
|
-
}
|
|
641
|
-
const telemetry = this.resolvedTelemetry;
|
|
642
|
-
if (this.role === "temporary") {
|
|
643
|
-
this.temporaryDispose = tools.dispose;
|
|
644
|
-
tools = this.applyTemporaryToolPolicy(tools);
|
|
645
|
-
resources = await this.applyTemporaryResources(resources);
|
|
646
|
-
commitGuard = undefined;
|
|
801
|
+
const prepared = await this.prepareCandidate(requestedRuntimeKey);
|
|
802
|
+
try {
|
|
803
|
+
await this.initCandidate(prepared.candidate);
|
|
804
|
+
} catch (error) {
|
|
805
|
+
await prepared.dispose?.();
|
|
806
|
+
throw error;
|
|
647
807
|
}
|
|
648
|
-
|
|
649
|
-
ctx: context,
|
|
650
|
-
tools,
|
|
651
|
-
resources,
|
|
652
|
-
...(commitGuard ? { commitGuard } : {}),
|
|
653
|
-
...(telemetry ? { telemetry } : {}),
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
await this.initCandidate({
|
|
657
|
-
...candidate,
|
|
658
|
-
runtimeKey: resolvedRuntimeKey,
|
|
659
|
-
});
|
|
808
|
+
if (this.role === "temporary") this.temporaryDispose = prepared.dispose;
|
|
660
809
|
this.hasLoadedRuntime = true;
|
|
661
|
-
this.loadedRuntimeKey =
|
|
810
|
+
this.loadedRuntimeKey = prepared.runtimeKey;
|
|
662
811
|
this.runtimeLoad.complete();
|
|
663
812
|
// 提交之前 turnEvents 绑定还不存在,此前每一次广播都到不了 Host。
|
|
664
813
|
// Host 的会话列表把装配后的第一次投影当作「facet 已就绪」的信号。
|
|
@@ -718,7 +867,7 @@ export function defineRuntimeAgent<
|
|
|
718
867
|
resources: ResolvedResources,
|
|
719
868
|
): Promise<ResolvedResources> {
|
|
720
869
|
const launch = this.temporaryLaunch ??
|
|
721
|
-
await this.ctx.storage.get<
|
|
870
|
+
await this.ctx.storage.get<TemporaryAgentLaunchSelection>(
|
|
722
871
|
TEMPORARY_AGENT_LAUNCH_KEY,
|
|
723
872
|
);
|
|
724
873
|
if (!launch) throw new Error("Temporary Agent is not initialized");
|
|
@@ -749,7 +898,7 @@ export function defineRuntimeAgent<
|
|
|
749
898
|
}
|
|
750
899
|
|
|
751
900
|
private async requestParentTemporaryApproval(
|
|
752
|
-
launch:
|
|
901
|
+
launch: TemporaryAgentLaunchSelection,
|
|
753
902
|
request: {
|
|
754
903
|
toolCallId: string;
|
|
755
904
|
toolName: string;
|
|
@@ -815,7 +964,7 @@ export function defineRuntimeAgent<
|
|
|
815
964
|
this.temporaryCleanup = (async () => {
|
|
816
965
|
if (!this.temporaryDispose) {
|
|
817
966
|
const launch = this.temporaryLaunch ??
|
|
818
|
-
await this.ctx.storage.get<
|
|
967
|
+
await this.ctx.storage.get<TemporaryAgentLaunchSelection>(
|
|
819
968
|
TEMPORARY_AGENT_LAUNCH_KEY,
|
|
820
969
|
);
|
|
821
970
|
if (launch) await this.ensureConfig(launch.runtimeKey);
|
|
@@ -885,7 +1034,6 @@ export function defineRuntimeAgent<
|
|
|
885
1034
|
input: {
|
|
886
1035
|
...normalized,
|
|
887
1036
|
runtimeKey: agent.loadedRuntimeKey,
|
|
888
|
-
config: runtimeConfig.value,
|
|
889
1037
|
executionLevel: agent.executionLevel(),
|
|
890
1038
|
},
|
|
891
1039
|
runId: `temporary-agent:${runContext.toolCallId}`,
|
|
@@ -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,
|