@springbrand/agent-runtime 0.2.0-alpha.71 → 0.3.0-alpha.1
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 +3 -3
- package/src/{agent-tool-runtime.ts → adapter/cloudflare/agent-tool.ts} +1 -1
- package/src/adapter/cloudflare/agent.ts +1539 -0
- package/src/{db/index.ts → adapter/cloudflare/database.ts} +18 -55
- package/src/{runtime-agent.ts → adapter/cloudflare/define-agent.ts} +25 -35
- package/src/adapter/cloudflare/dynamic-worker-observability.ts +1 -1
- package/src/adapter/cloudflare/resources/runtime-resources.ts +4 -4
- package/src/adapter/cloudflare/sandbox/adapter.ts +1 -1
- package/src/{db → adapter/cloudflare}/schema.ts +16 -83
- package/src/adapter/cloudflare/subagent/definition.ts +63 -33
- package/src/adapter/cloudflare/subagent/runner.ts +174 -120
- package/src/adapter/cloudflare/subagent/tools.ts +13 -14
- package/src/adapter/cloudflare/universal-agent/preparation.ts +7 -7
- package/src/adapter/cloudflare/universal-agent/tools.ts +10 -10
- package/src/adapter/cloudflare/workspace/git-fs.ts +1 -1
- package/src/adapter/cloudflare/workspace/scoped-workspace.ts +1 -1
- package/src/adapter/cloudflare/workspace/version-control.ts +2 -2
- package/src/config/activation.ts +140 -0
- package/src/{runtime-agent-context.ts → config/agent-context.ts} +3 -3
- package/src/{runtime-assembler.ts → config/assembler.ts} +20 -19
- package/src/{kernel/runtime-assembly.ts → config/assembly-projection.ts} +4 -4
- package/src/{pi/assembly/snapshot.ts → config/assembly-snapshot.ts} +6 -6
- package/src/{runtime-definition.ts → config/definition.ts} +13 -13
- package/src/config/harness-runtime.ts +182 -0
- package/src/config/index.ts +14 -0
- package/src/{pi/runtime-adapter/assembly.ts → config/pinning.ts} +38 -16
- package/src/{kernel/bindings.ts → config/ports.ts} +9 -13
- package/src/{kernel → config}/profile.ts +7 -0
- package/src/{kernel → config}/public-contracts.ts +6 -6
- package/src/{kernel → config}/runtime-load.ts +1 -1
- package/src/{pi/assembly/context.ts → context/assembly.ts} +4 -206
- package/src/{pi/assembly/extensions.ts → context/extension/assembly.ts} +5 -5
- package/src/context/extension/index.ts +3 -0
- package/src/context/index.ts +6 -0
- package/src/{layers/context → context/memory}/budget/gate.ts +2 -2
- package/src/conversation/harness-resume.ts +288 -0
- package/src/conversation/harness-stream.ts +98 -0
- package/src/{pi/runtime-adapter/transcript.ts → conversation/history.ts} +206 -233
- package/src/conversation/index.ts +6 -0
- package/src/{pi → conversation}/message/contract.ts +3 -0
- package/src/{pi → conversation}/message/conversion.ts +75 -0
- package/src/{pi → conversation}/message/index.ts +1 -0
- package/src/{pi → conversation}/message/model-input.ts +1 -1
- package/src/{pi → conversation}/message/projection.ts +76 -10
- package/src/{db/message-ui.repo.ts → conversation/repository.ts} +4 -0
- package/src/conversation/state.ts +175 -0
- package/src/harness/index.ts +7 -0
- package/src/harness/session-harness.ts +481 -0
- package/src/harness/submission-operation.ts +49 -0
- package/src/index.ts +38 -43
- package/src/lib/mcp.ts +1 -1
- package/src/{pi/runtime-adapter/models.ts → models/index.ts} +65 -17
- package/src/{pi/turn/tool-recovery.ts → session/cloudflare/legacy-tool-recovery.ts} +33 -525
- package/src/{pi/session → session/cloudflare}/migration.ts +334 -8
- package/src/{pi/session → session/cloudflare}/storage.ts +8 -36
- package/src/session/index.ts +39 -0
- package/src/submission/admission.ts +275 -0
- package/src/{kernel/receipts.ts → submission/contracts.ts} +14 -0
- package/src/submission/delivery.ts +212 -0
- package/src/submission/finalization.ts +111 -0
- package/src/submission/index.ts +9 -0
- package/src/{kernel/submission-lifecycle.ts → submission/lifecycle.ts} +41 -28
- package/src/{db/submission.repo.ts → submission/repository.ts} +61 -118
- package/src/submission/steer-delivery.ts +167 -0
- package/src/{db/steer.repo.ts → submission/steer-repository.ts} +27 -5
- package/src/telemetry/contract.ts +12 -0
- package/src/telemetry/coordinator.ts +65 -0
- package/src/telemetry/delivery.ts +2 -2
- package/src/telemetry/index.ts +5 -0
- package/src/{model-usage.ts → telemetry/model-usage.ts} +1 -1
- package/src/telemetry/recorder.ts +1 -1
- package/src/telemetry/runtime-telemetry.ts +12 -0
- package/src/{kernel/durable-lifecycle.ts → telemetry/subagent-lifecycle.ts} +1 -1
- package/src/telemetry/turn-events.ts +279 -0
- package/src/{pi/tool → tools}/ai-adapter.ts +2 -2
- package/src/tools/approval/index.ts +1 -0
- package/src/{db/approval.repo.ts → tools/approval/repository.ts} +10 -30
- package/src/{pi/tool → tools}/base.ts +14 -30
- package/src/tools/compiler.ts +327 -0
- package/src/{pi/tool → tools}/core-host.ts +3 -3
- package/src/{pi/tool → tools}/core.ts +3 -3
- package/src/tools/decision-gate.ts +227 -0
- package/src/{pi/tool → tools}/gateway.ts +1 -1
- package/src/tools/human-decisions.ts +127 -0
- package/src/tools/index.ts +30 -0
- package/src/tools/interaction/index.ts +1 -0
- package/src/{pi/tool → tools}/mcp.ts +1 -1
- package/src/{pi/tool → tools}/schedule.ts +3 -3
- package/src/{pi/tool → tools}/skill.ts +4 -4
- package/src/{kernel → tools}/subagent-runtime.ts +1 -1
- package/src/{pi/tool → tools}/subagent.ts +4 -4
- package/src/tools/temporary-agent/approvals.ts +126 -0
- package/src/{layers/orchestration/temporary-agent/core.ts → tools/temporary-agent/coordinator.ts} +41 -49
- package/src/tools/temporary-agent/core.ts +53 -0
- package/src/{layers/orchestration → tools}/temporary-agent/runner.ts +1 -1
- package/src/{layers/orchestration → tools}/temporary-agent/workspace.ts +1 -1
- package/src/{pi/tool → tools}/web-search/api.ts +1 -1
- package/src/{pi/tool → tools}/workspace-revision.ts +2 -2
- package/src/{pi/tool → tools}/workspace-sandbox.ts +2 -2
- package/src/{pi/tool → tools}/workspace-state-connector.ts +1 -1
- package/src/db/milestone.repo.ts +0 -104
- package/src/db/settlement.repo.ts +0 -104
- package/src/kernel/approval-lifecycle.ts +0 -638
- package/src/kernel/interaction-lifecycle.ts +0 -433
- package/src/kernel/recoverable-chat-agent.ts +0 -1020
- package/src/kernel/state.ts +0 -91
- package/src/pi/assembly/index.ts +0 -19
- package/src/pi/runtime-adapter/execution.ts +0 -1163
- package/src/pi/runtime-adapter/index.ts +0 -293
- package/src/pi/runtime-adapter/recovery.ts +0 -982
- package/src/pi/session/index.ts +0 -24
- package/src/pi/tool/compiler.ts +0 -483
- package/src/pi/tool/index.ts +0 -38
- package/src/pi/turn/approval.ts +0 -181
- package/src/pi/turn/index.ts +0 -82
- package/src/pi/turn/interaction.ts +0 -181
- package/src/runtime.ts +0 -4760
- /package/src/{kernel/runtime-assembly-view.ts → config/assembly-view.ts} +0 -0
- /package/src/{kernel → config}/degradation.ts +0 -0
- /package/src/{kernel → config}/runtime-config.ts +0 -0
- /package/src/{kernel/extensions.ts → context/extension/contracts.ts} +0 -0
- /package/src/{db/ext-context.repo.ts → context/extension/repository.ts} +0 -0
- /package/src/{pi/runtime-adapter/openrouter-messages.ts → models/openrouter/messages.ts} +0 -0
- /package/src/{pi/runtime-adapter → models/openrouter}/test-fixtures/openrouter-tool-search.sse +0 -0
- /package/src/{db/submission-admission.repo.ts → submission/admission-repository.ts} +0 -0
- /package/src/{db/telemetry-outbox.repo.ts → telemetry/repository.ts} +0 -0
- /package/src/{db/runtime-event-outbox.repo.ts → telemetry/runtime-event-repository.ts} +0 -0
- /package/src/{pi/tool → tools}/declared.ts +0 -0
- /package/src/{db/interaction.repo.ts → tools/interaction/repository.ts} +0 -0
- /package/src/{pi/tool → tools}/nested-tools.ts +0 -0
- /package/src/{kernel/submission-authority.ts → tools/subagent-authority.ts} +0 -0
- /package/src/{db/agent-tool.repo.ts → tools/subagent-run-repository.ts} +0 -0
- /package/src/{layers/orchestration/subagents/agent-types → tools/subagent-types}/contract.ts +0 -0
- /package/src/{layers/orchestration/subagents/agent-types → tools/subagent-types}/extract/index.ts +0 -0
- /package/src/{layers/orchestration/subagents/agent-types → tools/subagent-types}/fanout/index.ts +0 -0
- /package/src/{layers/orchestration/subagents/agent-types → tools/subagent-types}/registry.ts +0 -0
- /package/src/{pi/tool → tools}/time.ts +0 -0
- /package/src/{pi/tool → tools}/web-fetch.ts +0 -0
- /package/src/{pi/tool → tools}/web-search/index.ts +0 -0
- /package/src/{pi/tool → tools}/web-search/web-search.ts +0 -0
- /package/src/{workspace-versioning.ts → tools/workspace-versioning.ts} +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { ApprovalDecision } from "../submission/contracts";
|
|
2
|
+
import type { RuntimeTelemetryCoordinator } from "../telemetry/coordinator";
|
|
3
|
+
import type { ApprovalRepository } from "./approval/repository";
|
|
4
|
+
import type { PiToolInteractionSpec } from "./compiler";
|
|
5
|
+
import type { StoredToolInteraction, ToolInteractionRepository } from "./interaction/repository";
|
|
6
|
+
|
|
7
|
+
/** Human decisions own validation and durable facts; the caller resumes Pi. */
|
|
8
|
+
export class HarnessHumanDecisions {
|
|
9
|
+
constructor(private readonly options: {
|
|
10
|
+
readonly approvals: ApprovalRepository;
|
|
11
|
+
readonly interactions: ToolInteractionRepository;
|
|
12
|
+
readonly transaction: <T>(run: () => T) => T;
|
|
13
|
+
readonly isSubmissionActive: (submissionId: string) => boolean;
|
|
14
|
+
readonly telemetry: Pick<RuntimeTelemetryCoordinator, "capture">;
|
|
15
|
+
}) {}
|
|
16
|
+
|
|
17
|
+
isWaitingForApproval(submissionId: string): boolean {
|
|
18
|
+
return this.options.approvals.listPendingForSubmission(submissionId).length > 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
isWaitingForInteraction(submissionId: string): boolean {
|
|
22
|
+
return this.options.interactions.listPendingForSubmission(submissionId).length > 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async respond(
|
|
26
|
+
pending: StoredToolInteraction,
|
|
27
|
+
spec: PiToolInteractionSpec,
|
|
28
|
+
response: unknown,
|
|
29
|
+
prepareResponse: (input: {
|
|
30
|
+
toolCallId: string;
|
|
31
|
+
toolName: string;
|
|
32
|
+
toolInput: unknown;
|
|
33
|
+
response: unknown;
|
|
34
|
+
}) => Promise<unknown | null>,
|
|
35
|
+
): Promise<boolean> {
|
|
36
|
+
if (!this.options.isSubmissionActive(pending.submissionId) ||
|
|
37
|
+
this.options.interactions.find(pending.interactionId)?.status !== "pending") return false;
|
|
38
|
+
let input: unknown;
|
|
39
|
+
try { input = JSON.parse(pending.inputJson); } catch { return false; }
|
|
40
|
+
if (!spec.validateResponse(input, response)) return false;
|
|
41
|
+
const prepared = await prepareResponse({
|
|
42
|
+
toolCallId: pending.toolCallId,
|
|
43
|
+
toolName: pending.toolName,
|
|
44
|
+
toolInput: input,
|
|
45
|
+
response,
|
|
46
|
+
});
|
|
47
|
+
if (prepared === null || !spec.validateResponse(input, prepared)) return false;
|
|
48
|
+
return this.options.transaction(() => {
|
|
49
|
+
// Preparation may await a Host operation while stop, steer, or another answer wins.
|
|
50
|
+
const current = this.options.interactions.find(pending.interactionId);
|
|
51
|
+
if (current?.status !== "pending" ||
|
|
52
|
+
!this.options.isSubmissionActive(current.submissionId)) return false;
|
|
53
|
+
const occurredAt = Date.now();
|
|
54
|
+
this.options.interactions.settle(pending.interactionId, "responded", occurredAt, JSON.stringify(prepared));
|
|
55
|
+
this.options.telemetry.capture("interactionResponded", {
|
|
56
|
+
submissionId: current.submissionId,
|
|
57
|
+
toolCallId: current.toolCallId,
|
|
58
|
+
interactionId: current.interactionId,
|
|
59
|
+
response: prepared,
|
|
60
|
+
occurredAt,
|
|
61
|
+
});
|
|
62
|
+
return true;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
approve(executionId: string, decision: ApprovalDecision): { ok: boolean; submissionId?: string } {
|
|
67
|
+
if (decision.decision === "allow_level") {
|
|
68
|
+
throw new Error("allow_level must be applied by the User Agent Host before resuming the Tool");
|
|
69
|
+
}
|
|
70
|
+
return this.options.transaction(() => {
|
|
71
|
+
const pending = this.options.approvals.find(executionId);
|
|
72
|
+
if (!pending) return { ok: true };
|
|
73
|
+
if (pending.status !== "pending" || !this.options.isSubmissionActive(pending.submissionId)) {
|
|
74
|
+
return { ok: false };
|
|
75
|
+
}
|
|
76
|
+
const status = decision.decision === "allow_once" ? "approved" : "rejected";
|
|
77
|
+
const reason = decision.decision === "deny" ? decision.reason : undefined;
|
|
78
|
+
const occurredAt = Date.now();
|
|
79
|
+
this.options.approvals.decide(executionId, status, occurredAt, reason);
|
|
80
|
+
this.options.telemetry.capture("approvalDecided", {
|
|
81
|
+
submissionId: pending.submissionId,
|
|
82
|
+
toolCallId: pending.toolCallId,
|
|
83
|
+
executionId,
|
|
84
|
+
decision: status,
|
|
85
|
+
...(reason ? { reason } : {}),
|
|
86
|
+
occurredAt,
|
|
87
|
+
});
|
|
88
|
+
return { ok: true, submissionId: pending.submissionId };
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
cancelInteractions(submissionId: string, reason: string): void {
|
|
93
|
+
this.options.transaction(() => {
|
|
94
|
+
for (const id of this.options.interactions.listPendingForSubmission(submissionId)) {
|
|
95
|
+
const pending = this.options.interactions.find(id)!;
|
|
96
|
+
const occurredAt = Date.now();
|
|
97
|
+
this.options.interactions.settle(id, "cancelled", occurredAt);
|
|
98
|
+
this.options.telemetry.capture("interactionCancelled", {
|
|
99
|
+
submissionId,
|
|
100
|
+
toolCallId: pending.toolCallId,
|
|
101
|
+
interactionId: id,
|
|
102
|
+
reason,
|
|
103
|
+
occurredAt,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
cancelForSubmission(submissionId: string, reason: string): void {
|
|
110
|
+
this.options.transaction(() => {
|
|
111
|
+
for (const id of this.options.approvals.listPendingForSubmission(submissionId)) {
|
|
112
|
+
const pending = this.options.approvals.find(id)!;
|
|
113
|
+
const occurredAt = Date.now();
|
|
114
|
+
this.options.approvals.decide(id, "rejected", occurredAt, reason);
|
|
115
|
+
this.options.telemetry.capture("approvalDecided", {
|
|
116
|
+
submissionId,
|
|
117
|
+
toolCallId: pending.toolCallId,
|
|
118
|
+
executionId: id,
|
|
119
|
+
decision: "cancelled",
|
|
120
|
+
reason,
|
|
121
|
+
occurredAt,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
this.cancelInteractions(submissionId, reason);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export * from "./ai-adapter";
|
|
2
|
+
export * from "./approval";
|
|
3
|
+
export * from "./base";
|
|
4
|
+
export * from "./compiler";
|
|
5
|
+
export * from "./core";
|
|
6
|
+
export * from "./core-host";
|
|
7
|
+
export * from "./declared";
|
|
8
|
+
export * from "./decision-gate";
|
|
9
|
+
export * from "./human-decisions";
|
|
10
|
+
export * from "./gateway";
|
|
11
|
+
export * from "./mcp";
|
|
12
|
+
export * from "./interaction";
|
|
13
|
+
export * from "./schedule";
|
|
14
|
+
export * from "./skill";
|
|
15
|
+
export * from "./subagent";
|
|
16
|
+
export * from "./subagent-authority";
|
|
17
|
+
export * from "./subagent-run-repository";
|
|
18
|
+
export * from "./subagent-runtime";
|
|
19
|
+
export * from "./subagent-types/contract";
|
|
20
|
+
export * from "./subagent-types/registry";
|
|
21
|
+
export * from "./temporary-agent/approvals";
|
|
22
|
+
export * from "./temporary-agent/core";
|
|
23
|
+
export * from "./temporary-agent/coordinator";
|
|
24
|
+
export * from "./temporary-agent/runner";
|
|
25
|
+
export * from "./temporary-agent/workspace";
|
|
26
|
+
export * from "./time";
|
|
27
|
+
export * from "./workspace-sandbox";
|
|
28
|
+
export * from "./workspace-revision";
|
|
29
|
+
export * from "./workspace-versioning";
|
|
30
|
+
export * from "./web-search";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./repository";
|
|
@@ -6,9 +6,9 @@ import { Type, type TSchema } from "@earendil-works/pi-ai";
|
|
|
6
6
|
import type {
|
|
7
7
|
RuntimeSchedulePort,
|
|
8
8
|
RuntimeScheduleUpdate,
|
|
9
|
-
} from "
|
|
10
|
-
import type { ScheduleSpec } from "
|
|
11
|
-
import { serializeOutput } from "
|
|
9
|
+
} from "../config/ports";
|
|
10
|
+
import type { ScheduleSpec } from "../submission/contracts";
|
|
11
|
+
import { serializeOutput } from "../lib/artifacts";
|
|
12
12
|
import type { PiToolCandidate } from "./compiler";
|
|
13
13
|
|
|
14
14
|
const scheduleTriggerParameters = Type.Union([
|
|
@@ -12,10 +12,10 @@ import { z } from "zod";
|
|
|
12
12
|
import type {
|
|
13
13
|
RuntimeSkillScriptPolicy,
|
|
14
14
|
WorkspacePort,
|
|
15
|
-
} from "
|
|
16
|
-
import type { RuntimeSkillBodyTransform } from "
|
|
17
|
-
import { STORAGE_LEAF_MAX_CHARS } from "
|
|
18
|
-
import { serializeOutput } from "
|
|
15
|
+
} from "../config/ports";
|
|
16
|
+
import type { RuntimeSkillBodyTransform } from "../config/definition";
|
|
17
|
+
import { STORAGE_LEAF_MAX_CHARS } from "../context/memory/budget/gate";
|
|
18
|
+
import { serializeOutput } from "../lib/artifacts";
|
|
19
19
|
import { aiToolToPi } from "./ai-adapter";
|
|
20
20
|
import type { PiToolCandidate } from "./compiler";
|
|
21
21
|
import { piCandidatesToAiTools } from "./nested-tools";
|
|
@@ -5,13 +5,13 @@ import type {
|
|
|
5
5
|
RuntimeSubagentPort,
|
|
6
6
|
RuntimeSubagentRunResult,
|
|
7
7
|
RuntimeSubagentUsageEvent,
|
|
8
|
-
} from "
|
|
9
|
-
import { AGENT_TYPES } from "
|
|
10
|
-
import { serializeOutput } from "
|
|
8
|
+
} from "../config/ports";
|
|
9
|
+
import { AGENT_TYPES } from "./subagent-types/registry";
|
|
10
|
+
import { serializeOutput } from "../lib/artifacts";
|
|
11
11
|
import {
|
|
12
12
|
isZeroSubagentUsage,
|
|
13
13
|
subagentTerminalStatus,
|
|
14
|
-
} from "
|
|
14
|
+
} from "./subagent-runtime";
|
|
15
15
|
import type { PiToolCandidate } from "./compiler";
|
|
16
16
|
|
|
17
17
|
const BACKGROUND_MAX_BUDGET_MS = 10 * 60 * 1_000;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { ApprovalDecision, ApprovalReceipt } from "../../submission/contracts";
|
|
2
|
+
import type { RuntimeTelemetryCoordinator } from "../../telemetry/coordinator";
|
|
3
|
+
import type {
|
|
4
|
+
TemporaryAgentApprovalDecision,
|
|
5
|
+
TemporaryAgentApprovalRequest,
|
|
6
|
+
} from "./core";
|
|
7
|
+
|
|
8
|
+
interface PendingApproval {
|
|
9
|
+
receipt: ApprovalReceipt;
|
|
10
|
+
submissionId?: string;
|
|
11
|
+
toolCallId: string;
|
|
12
|
+
resolve(decision: TemporaryAgentApprovalDecision): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Human decisions belong to the temporary execution and expire with it. */
|
|
16
|
+
export class TemporaryAgentApprovals {
|
|
17
|
+
private readonly pending = new Map<string, PendingApproval>();
|
|
18
|
+
|
|
19
|
+
constructor(private readonly options: {
|
|
20
|
+
capture: RuntimeTelemetryCoordinator["capture"];
|
|
21
|
+
onChanged(): Promise<void>;
|
|
22
|
+
}) {}
|
|
23
|
+
|
|
24
|
+
find(executionId: string): ApprovalReceipt | null {
|
|
25
|
+
return this.pending.get(executionId)?.receipt ?? null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
list(): ApprovalReceipt[] {
|
|
29
|
+
return [...this.pending.values()].map(({ receipt }) => receipt);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
request(
|
|
33
|
+
request: TemporaryAgentApprovalRequest,
|
|
34
|
+
submissionId?: string,
|
|
35
|
+
): Promise<TemporaryAgentApprovalDecision> {
|
|
36
|
+
if (this.pending.has(request.executionId)) {
|
|
37
|
+
return Promise.reject(new Error(
|
|
38
|
+
`temporary agent approval already pending: ${request.executionId}`,
|
|
39
|
+
));
|
|
40
|
+
}
|
|
41
|
+
const summary = `${request.subagentName} requests ${request.toolName}`;
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
const pending: PendingApproval = {
|
|
44
|
+
receipt: {
|
|
45
|
+
executionId: request.executionId,
|
|
46
|
+
source: "temporary-agent",
|
|
47
|
+
action: request.toolName,
|
|
48
|
+
summary,
|
|
49
|
+
executionLevel: request.executionLevel,
|
|
50
|
+
requiredExecutionLevel: request.requiredExecutionLevel,
|
|
51
|
+
inputJson: JSON.stringify(request.input ?? null),
|
|
52
|
+
requestId: request.requestId,
|
|
53
|
+
},
|
|
54
|
+
submissionId,
|
|
55
|
+
toolCallId: request.toolCallId,
|
|
56
|
+
resolve,
|
|
57
|
+
};
|
|
58
|
+
this.pending.set(request.executionId, pending);
|
|
59
|
+
if (submissionId) {
|
|
60
|
+
this.options.capture("approvalRequested", {
|
|
61
|
+
submissionId,
|
|
62
|
+
toolCallId: request.toolCallId,
|
|
63
|
+
executionId: request.executionId,
|
|
64
|
+
toolName: request.toolName,
|
|
65
|
+
summary,
|
|
66
|
+
input: request.input,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
void this.options.onChanged().catch((error) => {
|
|
70
|
+
if (this.pending.get(request.executionId) === pending) {
|
|
71
|
+
this.pending.delete(request.executionId);
|
|
72
|
+
}
|
|
73
|
+
reject(error);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async cancel(
|
|
79
|
+
executionId: string,
|
|
80
|
+
reason = "temporary agent was cancelled",
|
|
81
|
+
): Promise<{ ok: boolean }> {
|
|
82
|
+
return this.settle(executionId, { approved: false, reason }, "cancelled");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Undefined leaves ordinary durable Tool approvals to their fact owner. */
|
|
86
|
+
async decide(
|
|
87
|
+
executionId: string,
|
|
88
|
+
decision: ApprovalDecision,
|
|
89
|
+
): Promise<{ ok: boolean } | undefined> {
|
|
90
|
+
if (!this.pending.has(executionId)) return undefined;
|
|
91
|
+
if (decision.decision === "allow_level") {
|
|
92
|
+
throw new Error(
|
|
93
|
+
"allow_level must be applied by the User Agent Host before resuming the Tool",
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return this.settle(
|
|
97
|
+
executionId,
|
|
98
|
+
decision.decision === "allow_once"
|
|
99
|
+
? { approved: true }
|
|
100
|
+
: { approved: false, reason: decision.reason },
|
|
101
|
+
decision.decision === "allow_once" ? "approved" : "rejected",
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private async settle(
|
|
106
|
+
executionId: string,
|
|
107
|
+
result: TemporaryAgentApprovalDecision,
|
|
108
|
+
decision: "approved" | "rejected" | "cancelled",
|
|
109
|
+
): Promise<{ ok: boolean }> {
|
|
110
|
+
const pending = this.pending.get(executionId);
|
|
111
|
+
if (!pending) return { ok: false };
|
|
112
|
+
this.pending.delete(executionId);
|
|
113
|
+
pending.resolve(result);
|
|
114
|
+
if (pending.submissionId) {
|
|
115
|
+
this.options.capture("approvalDecided", {
|
|
116
|
+
submissionId: pending.submissionId,
|
|
117
|
+
toolCallId: pending.toolCallId,
|
|
118
|
+
executionId,
|
|
119
|
+
decision,
|
|
120
|
+
...(result.reason ? { reason: result.reason } : {}),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
await this.options.onChanged();
|
|
124
|
+
return { ok: true };
|
|
125
|
+
}
|
|
126
|
+
}
|
package/src/{layers/orchestration/temporary-agent/core.ts → tools/temporary-agent/coordinator.ts}
RENAMED
|
@@ -1,55 +1,26 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TemporaryAgentExecutor, TemporaryAgentRequest } from "./core";
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
config: Config;
|
|
16
|
-
executionLevel: ExecutionLevel;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface TemporaryAgentRunContext {
|
|
20
|
-
signal: AbortSignal;
|
|
21
|
-
requestId: string;
|
|
22
|
-
toolCallId: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* 启动一个临时 Agent,并在它完整收尾后返回最终文本。
|
|
27
|
-
*
|
|
28
|
-
* @remarks
|
|
29
|
-
* `TemporaryAgentCoordinator.run` 在获得并发槽位后调用;实现方应把子运行和外部资源清理收口到同一个 Promise。
|
|
30
|
-
*
|
|
31
|
-
* 协调器只依赖这个函数而不依赖 Durable Object、UI 或具体 Agent SDK,避免把基础设施生命周期写进并发规则。
|
|
32
|
-
*
|
|
33
|
-
* Agent 和 Runtime 的术语见 `src/index.ts`。
|
|
34
|
-
*/
|
|
35
|
-
export type TemporaryAgentExecutor = (
|
|
36
|
-
request: TemporaryAgentRequest,
|
|
37
|
-
) => Promise<string>;
|
|
3
|
+
export type TemporaryAgentLifecycleEvent = {
|
|
4
|
+
type: "started";
|
|
5
|
+
request: TemporaryAgentRequest;
|
|
6
|
+
occurredAt: number;
|
|
7
|
+
} | {
|
|
8
|
+
type: "finished";
|
|
9
|
+
outcome: "completed" | "failed" | "cancelled";
|
|
10
|
+
durationMs: number;
|
|
11
|
+
occurredAt: number;
|
|
12
|
+
output?: string;
|
|
13
|
+
error?: unknown;
|
|
14
|
+
};
|
|
38
15
|
|
|
39
|
-
|
|
40
|
-
executionId: string;
|
|
41
|
-
subagentName: string;
|
|
42
|
-
requestId: string;
|
|
43
|
-
toolCallId: string;
|
|
44
|
-
toolName: string;
|
|
45
|
-
executionLevel: ExecutionLevel;
|
|
46
|
-
requiredExecutionLevel: ExecutionLevel;
|
|
47
|
-
input: unknown;
|
|
48
|
-
}
|
|
16
|
+
type TemporaryAgentObserver = (event: TemporaryAgentLifecycleEvent) => void;
|
|
49
17
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
18
|
+
function observe(observer: TemporaryAgentObserver | undefined, event: TemporaryAgentLifecycleEvent): void {
|
|
19
|
+
try {
|
|
20
|
+
observer?.(event);
|
|
21
|
+
} catch {
|
|
22
|
+
// Observation must not fail a child or prevent releasing its execution slot.
|
|
23
|
+
}
|
|
53
24
|
}
|
|
54
25
|
|
|
55
26
|
// 作用:去掉临时 Agent 请求三个字段的首尾空白,并拒绝空值。
|
|
@@ -95,13 +66,34 @@ export class TemporaryAgentCoordinator {
|
|
|
95
66
|
request: TemporaryAgentRequest,
|
|
96
67
|
execute: TemporaryAgentExecutor,
|
|
97
68
|
signal?: AbortSignal,
|
|
69
|
+
observer?: TemporaryAgentObserver,
|
|
98
70
|
): Promise<string> {
|
|
99
71
|
const normalized = normalizeRequest(request);
|
|
100
72
|
await this.acquire(signal);
|
|
73
|
+
const startedAt = Date.now();
|
|
74
|
+
observe(observer, { type: "started", request: normalized, occurredAt: startedAt });
|
|
101
75
|
try {
|
|
102
76
|
const text = (await execute(normalized)).trim();
|
|
103
77
|
if (!text) throw new Error("temporary agent final text must not be blank");
|
|
78
|
+
const occurredAt = Date.now();
|
|
79
|
+
observe(observer, {
|
|
80
|
+
type: "finished",
|
|
81
|
+
outcome: "completed",
|
|
82
|
+
output: text,
|
|
83
|
+
durationMs: Math.max(0, occurredAt - startedAt),
|
|
84
|
+
occurredAt,
|
|
85
|
+
});
|
|
104
86
|
return text;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
const occurredAt = Date.now();
|
|
89
|
+
observe(observer, {
|
|
90
|
+
type: "finished",
|
|
91
|
+
outcome: signal?.aborted ? "cancelled" : "failed",
|
|
92
|
+
error,
|
|
93
|
+
durationMs: Math.max(0, occurredAt - startedAt),
|
|
94
|
+
occurredAt,
|
|
95
|
+
});
|
|
96
|
+
throw error;
|
|
105
97
|
} finally {
|
|
106
98
|
this.release();
|
|
107
99
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ExecutionLevel } from "../../lib/execution-level";
|
|
2
|
+
|
|
3
|
+
export const TEMPORARY_AGENT_LAUNCH_KEY =
|
|
4
|
+
"universal-agent:temporary-agent-launch";
|
|
5
|
+
|
|
6
|
+
export interface TemporaryAgentRequest {
|
|
7
|
+
subagentName: string;
|
|
8
|
+
instructions: string;
|
|
9
|
+
task: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface TemporaryAgentLaunch<Config = unknown>
|
|
13
|
+
extends TemporaryAgentRequest {
|
|
14
|
+
runtimeKey: string;
|
|
15
|
+
config: Config;
|
|
16
|
+
executionLevel: ExecutionLevel;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface TemporaryAgentRunContext {
|
|
20
|
+
signal: AbortSignal;
|
|
21
|
+
requestId: string;
|
|
22
|
+
toolCallId: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 启动一个临时 Agent,并在它完整收尾后返回最终文本。
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* `TemporaryAgentCoordinator.run` 在获得并发槽位后调用;实现方应把子运行和外部资源清理收口到同一个 Promise。
|
|
30
|
+
*
|
|
31
|
+
* 协调器只依赖这个函数而不依赖 Durable Object、UI 或具体 Agent SDK,避免把基础设施生命周期写进并发规则。
|
|
32
|
+
*
|
|
33
|
+
* Agent 和 Runtime 的术语见 `src/index.ts`。
|
|
34
|
+
*/
|
|
35
|
+
export type TemporaryAgentExecutor = (
|
|
36
|
+
request: TemporaryAgentRequest,
|
|
37
|
+
) => Promise<string>;
|
|
38
|
+
|
|
39
|
+
export interface TemporaryAgentApprovalRequest {
|
|
40
|
+
executionId: string;
|
|
41
|
+
subagentName: string;
|
|
42
|
+
requestId: string;
|
|
43
|
+
toolCallId: string;
|
|
44
|
+
toolName: string;
|
|
45
|
+
executionLevel: ExecutionLevel;
|
|
46
|
+
requiredExecutionLevel: ExecutionLevel;
|
|
47
|
+
input: unknown;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface TemporaryAgentApprovalDecision {
|
|
51
|
+
approved: boolean;
|
|
52
|
+
reason?: string;
|
|
53
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
2
2
|
import { Type } from "@earendil-works/pi-ai";
|
|
3
|
-
import type { WorkspaceRevisionRestorePort } from "
|
|
4
|
-
import { serializeOutput } from "
|
|
3
|
+
import type { WorkspaceRevisionRestorePort } from "./workspace-versioning";
|
|
4
|
+
import { serializeOutput } from "../lib/artifacts";
|
|
5
5
|
import type { PiToolCandidate } from "./compiler";
|
|
6
6
|
|
|
7
7
|
const parameters = Type.Object({
|
|
@@ -16,8 +16,8 @@ import { Type } from "@earendil-works/pi-ai";
|
|
|
16
16
|
import type {
|
|
17
17
|
RuntimeSandboxPort,
|
|
18
18
|
WorkspacePort,
|
|
19
|
-
} from "
|
|
20
|
-
import { serializeOutput } from "
|
|
19
|
+
} from "../config/ports";
|
|
20
|
+
import { serializeOutput } from "../lib/artifacts";
|
|
21
21
|
import { aiToolToPi } from "./ai-adapter";
|
|
22
22
|
import type { PiToolCandidate } from "./compiler";
|
|
23
23
|
|
|
@@ -2,7 +2,7 @@ import { STATE_TYPES } from "@cloudflare/shell";
|
|
|
2
2
|
import type { StateBackend } from "@cloudflare/shell/workers";
|
|
3
3
|
import { StateConnector } from "@cloudflare/shell/workers";
|
|
4
4
|
import type { ConnectorTools } from "@cloudflare/codemode";
|
|
5
|
-
import type { WorkspacePort } from "
|
|
5
|
+
import type { WorkspacePort } from "../config/ports";
|
|
6
6
|
|
|
7
7
|
export const WORKSPACE_DIRECT_TEXT_READ_MAX_BYTES = 1024 * 1024;
|
|
8
8
|
|