@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ToolRegistration } from "@earendil-works/pi-durable";
|
|
1
2
|
import {
|
|
2
3
|
DynamicWorkerExecutor,
|
|
3
4
|
resolveProvider,
|
|
@@ -9,19 +10,18 @@ import {
|
|
|
9
10
|
} from "@cloudflare/shell";
|
|
10
11
|
import { stateToolsFromBackend } from "@cloudflare/shell/workers";
|
|
11
12
|
import type {
|
|
12
|
-
AgentHarnessTool,
|
|
13
13
|
AgentTool,
|
|
14
14
|
AgentToolResult,
|
|
15
15
|
} from "@earendil-works/pi-agent-core";
|
|
16
16
|
import { Type } from "@earendil-works/pi-ai";
|
|
17
17
|
import type { WorkspacePort } from "../../../config/ports";
|
|
18
|
-
import type { AgentType } from "../../../tools/subagent
|
|
18
|
+
import type { AgentType } from "../../../tools/subagent/types/contract";
|
|
19
19
|
import {
|
|
20
20
|
compileHarnessTools,
|
|
21
21
|
type CompileHarnessToolsOptions,
|
|
22
22
|
type PiToolCandidate,
|
|
23
23
|
} from "../../../tools";
|
|
24
|
-
import { workspacePiToolCandidates } from "../../../tools/workspace
|
|
24
|
+
import { workspacePiToolCandidates } from "../../../tools/workspace";
|
|
25
25
|
|
|
26
26
|
const READONLY_WORKSPACE_TOOLS = new Set([
|
|
27
27
|
"read",
|
|
@@ -221,7 +221,7 @@ export function createCloudflareSubAgentTools(options: {
|
|
|
221
221
|
loader: WorkerLoader;
|
|
222
222
|
outbound: Fetcher;
|
|
223
223
|
settle: NonNullable<CompileHarnessToolsOptions["onSettlement"]>;
|
|
224
|
-
}):
|
|
224
|
+
}): ToolRegistration<any, any>[] {
|
|
225
225
|
const workspace = workspacePiToolCandidates(options.workspace).filter(
|
|
226
226
|
(candidate) =>
|
|
227
227
|
options.profile === "capable" ||
|
|
@@ -9,16 +9,16 @@ import type {
|
|
|
9
9
|
RuntimeSubagentPort,
|
|
10
10
|
WorkspacePort,
|
|
11
11
|
} from "../../../config/ports";
|
|
12
|
-
import { decodeSubagentCompletion } from "../../../tools/subagent
|
|
12
|
+
import { decodeSubagentCompletion } from "../../../tools/subagent/runtime";
|
|
13
13
|
import type { RuntimeDegradation } from "../../../config/degradation";
|
|
14
14
|
import type { RuntimeMemoryProfile } from "../../../config/profile";
|
|
15
|
-
import { AGENT_TYPES } from "../../../tools/subagent
|
|
15
|
+
import { AGENT_TYPES } from "../../../tools/subagent/types/registry";
|
|
16
16
|
import type { RuntimeAgentConfigContext } from "../../../config/agent-context";
|
|
17
17
|
import {
|
|
18
18
|
createBrowserExecutionFactory,
|
|
19
19
|
createWorkspaceCodeExecutionFactory,
|
|
20
20
|
type RuntimeBrowserBinding,
|
|
21
|
-
} from "../../../tools/
|
|
21
|
+
} from "../../../tools/codemode/adapter";
|
|
22
22
|
import {
|
|
23
23
|
createCloudflareSandboxAdapter,
|
|
24
24
|
type SandboxAdmission,
|
|
@@ -7,15 +7,13 @@ import type {
|
|
|
7
7
|
} from "../../../config/ports";
|
|
8
8
|
import type { RuntimeDegradation } from "../../../config/degradation";
|
|
9
9
|
import type { RuntimeMemoryProfile } from "../../../config/profile";
|
|
10
|
-
import type { WorkspaceRevisionRestorePort } from "../../../tools/workspace
|
|
10
|
+
import type { WorkspaceRevisionRestorePort } from "../../../tools/workspace/versioning";
|
|
11
11
|
import { schedulePiToolCandidates } from "../../../tools/schedule";
|
|
12
|
-
import { workspaceRevisionPiToolCandidate } from "../../../tools/workspace
|
|
13
|
-
import {
|
|
14
|
-
sandboxPiToolCandidates,
|
|
15
|
-
} from "../../../tools/workspace-sandbox";
|
|
12
|
+
import { workspaceRevisionPiToolCandidate } from "../../../tools/workspace/revision";
|
|
13
|
+
import { sandboxPiToolCandidates } from "../../../tools/sandbox";
|
|
16
14
|
import type { RuntimeCodeExecutionFactory } from "../../../config/ports";
|
|
17
15
|
import type { ToolAssemblyResult } from "../../../config/definition";
|
|
18
|
-
import type { PiToolCandidate } from "../../../tools/
|
|
16
|
+
import type { PiToolCandidate } from "../../../tools/runtime/contracts";
|
|
19
17
|
|
|
20
18
|
export function assembleUniversalAgentTools(options: {
|
|
21
19
|
hostTools?: readonly PiToolCandidate[];
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
type WorkspaceRevision,
|
|
6
6
|
type WorkspaceRevisionManifest,
|
|
7
7
|
type WorkspaceVersionPort,
|
|
8
|
-
} from "../../../tools/workspace
|
|
8
|
+
} from "../../../tools/workspace/versioning";
|
|
9
9
|
import { createWorkspaceGitFs } from "./git-fs";
|
|
10
10
|
|
|
11
11
|
const PAGE_SIZE = 256;
|
package/src/config/activation.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { connectConfiguredMcpServers } from "../lib/mcp";
|
|
|
2
2
|
import { sha256Hex } from "../lib/artifacts";
|
|
3
3
|
import type { RuntimeCandidate, RuntimeSnapshot } from "./assembler";
|
|
4
4
|
import type { HarnessRuntimeConfig } from "./harness-runtime";
|
|
5
|
-
import type { PreparedPiRuntime, PreparePiRuntimeOptions } from "./
|
|
5
|
+
import type { PreparedPiRuntime, PreparePiRuntimeOptions } from "./prepared-runtime";
|
|
6
6
|
import type { RuntimeGatewaySession, RuntimeSubmissionAdmissionHook } from "./ports";
|
|
7
7
|
import { withRuntimeLoadTimeout } from "./runtime-load";
|
|
8
8
|
import type { RuntimeLoadPhase } from "../conversation/state";
|
|
@@ -16,6 +16,16 @@ export interface ActiveRuntime {
|
|
|
16
16
|
readonly admission?: RuntimeSubmissionAdmissionHook;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/** Check the same candidate guards during admission and final activation. */
|
|
20
|
+
export async function verifyRuntimeCandidateGuards(
|
|
21
|
+
candidate: RuntimeCandidate,
|
|
22
|
+
phase: "admission" | "commit",
|
|
23
|
+
): Promise<void> {
|
|
24
|
+
for (const [index, guard] of candidate.commitGuards.entries()) {
|
|
25
|
+
await withRuntimeLoadTimeout(`${phase}-guard:${index}`, guard);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
async function closeRuntimeGatewaySession(
|
|
20
30
|
session: RuntimeGatewaySession | undefined,
|
|
21
31
|
step: string,
|
|
@@ -33,7 +43,6 @@ interface RuntimeActivationOptions {
|
|
|
33
43
|
readonly sessionId: string;
|
|
34
44
|
readonly createExtensionHostBinding: PreparePiRuntimeOptions["createExtensionHostBinding"];
|
|
35
45
|
advance(phase: RuntimeLoadPhase): void;
|
|
36
|
-
isExecuting(): boolean;
|
|
37
46
|
commit(runtime: ActiveRuntime): void;
|
|
38
47
|
}
|
|
39
48
|
|
|
@@ -92,19 +101,10 @@ export async function activateRuntimeCandidate(
|
|
|
92
101
|
throw error;
|
|
93
102
|
}
|
|
94
103
|
const revision = await sha256Hex(prepared.revisionDescriptor);
|
|
95
|
-
const contested = Boolean(previous) &&
|
|
96
|
-
revision !== previous?.revision &&
|
|
97
|
-
options.isExecuting();
|
|
98
|
-
if (contested) {
|
|
99
|
-
await closeRuntimeGatewaySession(gatewaySession, "gateway.close.contested");
|
|
100
|
-
throw new Error(
|
|
101
|
-
"Cannot reload Runtime while a revision-pinned SpringBrand Turn is active",
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
104
|
const next = Object.freeze({
|
|
105
105
|
snapshot: candidateSnapshot,
|
|
106
106
|
revision,
|
|
107
|
-
runtimeKey: candidate.runtimeKey ??
|
|
107
|
+
runtimeKey: candidate.runtimeKey ?? "default",
|
|
108
108
|
pi: prepared,
|
|
109
109
|
...(gatewaySession ? { gatewaySession } : {}),
|
|
110
110
|
...(candidate.admission
|
|
@@ -114,9 +114,7 @@ export async function activateRuntimeCandidate(
|
|
|
114
114
|
let activated = false;
|
|
115
115
|
const previousGatewaySession = previous?.gatewaySession;
|
|
116
116
|
try {
|
|
117
|
-
|
|
118
|
-
await withRuntimeLoadTimeout(`commit-guard:${index}`, guard);
|
|
119
|
-
}
|
|
117
|
+
await verifyRuntimeCandidateGuards(candidate, "commit");
|
|
120
118
|
options.pi.activate(candidateSnapshot);
|
|
121
119
|
activated = true;
|
|
122
120
|
options.commit(next);
|
package/src/config/assembler.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { schedulePiToolCandidates } from "../tools/schedule";
|
|
1
2
|
import type { SkillSource } from "agents/skills";
|
|
2
3
|
import type {
|
|
3
4
|
RuntimeGatewayPort,
|
|
@@ -36,10 +37,7 @@ import {
|
|
|
36
37
|
type PiRuntimeAssembly,
|
|
37
38
|
} from "./assembly-snapshot";
|
|
38
39
|
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
39
|
-
import type {
|
|
40
|
-
PiToolCandidate,
|
|
41
|
-
ToolExposureMode,
|
|
42
|
-
} from "../tools/compiler";
|
|
40
|
+
import type { PiToolCandidate, ToolExposureMode } from "../tools/runtime/contracts";
|
|
43
41
|
import type {
|
|
44
42
|
AssembleRuntimeSnapshotInput,
|
|
45
43
|
RuntimeSettings,
|
|
@@ -50,17 +48,11 @@ import type {
|
|
|
50
48
|
RuntimeToolSurfacePolicy,
|
|
51
49
|
} from "./definition";
|
|
52
50
|
import type { RuntimeCodeExecutionFactory } from "./ports";
|
|
53
|
-
import {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} from "../tools/base";
|
|
57
|
-
import {
|
|
58
|
-
BROWSER_EXECUTE_TOOL_NAME,
|
|
59
|
-
browserExecutionPiToolCandidate,
|
|
60
|
-
codeExecutionPiToolCandidate,
|
|
61
|
-
} from "../tools/core";
|
|
51
|
+
import { basePiToolCandidates } from "../tools/assistant";
|
|
52
|
+
import { memoryPiToolCandidate } from "../tools/assistant/memory";
|
|
53
|
+
import { BROWSER_EXECUTE_TOOL_NAME, browserExecutionPiToolCandidate, codeExecutionPiToolCandidate } from "../tools/codemode";
|
|
62
54
|
import { skillPiToolCandidates } from "../tools/skill";
|
|
63
|
-
import { createWebSearch, WEB_SEARCH_MODEL } from "../tools/web
|
|
55
|
+
import { createWebSearch, WEB_SEARCH_MODEL } from "../tools/web/search";
|
|
64
56
|
import { subagentPiToolCandidates } from "../tools/subagent";
|
|
65
57
|
import { resolvePiModel } from "../models";
|
|
66
58
|
import {
|
|
@@ -264,6 +256,16 @@ async function createToolSurface(
|
|
|
264
256
|
if (!EXECUTION_LEVELS.includes(candidate.requiredExecutionLevel)) {
|
|
265
257
|
throw new Error(`SpringBrand Tool ${name} execution level is invalid`);
|
|
266
258
|
}
|
|
259
|
+
if (candidate.unattendedExecution) {
|
|
260
|
+
requiredName(
|
|
261
|
+
candidate.unattendedExecution.capability,
|
|
262
|
+
`SpringBrand Tool ${name} unattended capability`,
|
|
263
|
+
);
|
|
264
|
+
requiredName(
|
|
265
|
+
candidate.unattendedExecution.operation,
|
|
266
|
+
`SpringBrand Tool ${name} unattended operation`,
|
|
267
|
+
);
|
|
268
|
+
}
|
|
267
269
|
if (tools.has(name)) {
|
|
268
270
|
throw new Error(`Duplicate Runtime SpringBrand Tool: ${name}`);
|
|
269
271
|
}
|
|
@@ -294,17 +296,12 @@ async function createToolSurface(
|
|
|
294
296
|
typeof candidate.tool.execute === "function",
|
|
295
297
|
);
|
|
296
298
|
const codeExecutionCandidates = Object.freeze([...mergeable]);
|
|
297
|
-
const codeExecutionToolNames = new Set(
|
|
298
|
-
codeExecutionCandidates.map(({ tool }) => tool.name),
|
|
299
|
-
);
|
|
300
299
|
const codeExecution = input.codeExecution.create(
|
|
301
300
|
codeExecutionCandidates,
|
|
302
301
|
);
|
|
303
|
-
const
|
|
304
|
-
({ tool }) => !codeExecutionToolNames.has(tool.name),
|
|
305
|
-
);
|
|
302
|
+
const directToolNames = new Set(directVisible.map(({ tool }) => tool.name));
|
|
306
303
|
const routeList = (deferred: boolean) => {
|
|
307
|
-
const matches =
|
|
304
|
+
const matches = directVisible.filter((candidate) =>
|
|
308
305
|
(defersTool?.(candidate.tool.name) === true) === deferred
|
|
309
306
|
);
|
|
310
307
|
return matches.length > 0
|
|
@@ -316,8 +313,9 @@ async function createToolSurface(
|
|
|
316
313
|
};
|
|
317
314
|
const routeGuidance = [
|
|
318
315
|
"## Tool routing",
|
|
319
|
-
"
|
|
316
|
+
"For top-level deferred Tools, use Provider Tool Search with their exact name, then call them directly.",
|
|
320
317
|
"Top-level immediate Tools are already available outside execute; call them directly.",
|
|
318
|
+
"Only Code Mode Tools listed below are callable inside execute through tools.<name>. Tools marked execute-only are not registered as top-level Tools.",
|
|
321
319
|
"",
|
|
322
320
|
"## Top-level deferred Tools",
|
|
323
321
|
routeList(true),
|
|
@@ -325,21 +323,20 @@ async function createToolSurface(
|
|
|
325
323
|
"## Top-level immediate Tools",
|
|
326
324
|
routeList(false),
|
|
327
325
|
"",
|
|
326
|
+
"## Code Mode Tools",
|
|
327
|
+
codeExecutionCandidates.length > 0
|
|
328
|
+
? codeExecutionCandidates.map(({ tool }) =>
|
|
329
|
+
`- \`tools.${tool.name}\` — ${directToolNames.has(tool.name) ? "also available as a top-level Tool" : "execute-only"}`
|
|
330
|
+
).join("\n")
|
|
331
|
+
: "- None.",
|
|
332
|
+
"",
|
|
328
333
|
].join("\n");
|
|
329
|
-
const materializeGuidance = codeExecutionCandidates.some(
|
|
330
|
-
({ tool }) => tool.name === "materialize_skill_resource",
|
|
331
|
-
)
|
|
332
|
-
? "Skill resource copying is available only inside execute through " +
|
|
333
|
-
"`tools.materialize_skill_resource({ name, path, destination })`; " +
|
|
334
|
-
"no Direct Tool is registered for it. " +
|
|
335
|
-
"Copy multiple resources sequentially in one execute so durable replay preserves call order.\n\n"
|
|
336
|
-
: "";
|
|
337
334
|
return Object.freeze({
|
|
338
335
|
candidates: Object.freeze([
|
|
339
336
|
classify(codeExecutionPiToolCandidate({
|
|
340
337
|
...codeExecution,
|
|
341
338
|
description:
|
|
342
|
-
routeGuidance + "\n" +
|
|
339
|
+
routeGuidance + "\n" + codeExecution.description,
|
|
343
340
|
})),
|
|
344
341
|
...directVisible.map(classify),
|
|
345
342
|
]),
|
|
@@ -586,6 +583,9 @@ class RuntimeBuilder {
|
|
|
586
583
|
? { headers: Object.freeze({ ...endpoint.headers }) }
|
|
587
584
|
: {}),
|
|
588
585
|
models: Object.freeze([...endpoint.models]),
|
|
586
|
+
...(endpoint.openRouterProviderPins
|
|
587
|
+
? { openRouterProviderPins: Object.freeze({ ...endpoint.openRouterProviderPins }) }
|
|
588
|
+
: {}),
|
|
589
589
|
...(endpoint.modelInputModalities
|
|
590
590
|
? {
|
|
591
591
|
modelInputModalities: Object.freeze(Object.fromEntries(
|
|
@@ -595,6 +595,22 @@ class RuntimeBuilder {
|
|
|
595
595
|
)),
|
|
596
596
|
}
|
|
597
597
|
: {}),
|
|
598
|
+
...(endpoint.modelMetadata
|
|
599
|
+
? {
|
|
600
|
+
modelMetadata: Object.freeze(Object.fromEntries(
|
|
601
|
+
Object.entries(endpoint.modelMetadata).map(([modelId, metadata]) => [
|
|
602
|
+
modelId,
|
|
603
|
+
Object.freeze({
|
|
604
|
+
...metadata,
|
|
605
|
+
cost: Object.freeze({ ...metadata.cost }),
|
|
606
|
+
...(metadata.thinkingLevelMap
|
|
607
|
+
? { thinkingLevelMap: Object.freeze({ ...metadata.thinkingLevelMap }) }
|
|
608
|
+
: {}),
|
|
609
|
+
}),
|
|
610
|
+
]),
|
|
611
|
+
)),
|
|
612
|
+
}
|
|
613
|
+
: {}),
|
|
598
614
|
}),
|
|
599
615
|
)),
|
|
600
616
|
});
|
|
@@ -632,6 +648,7 @@ class RuntimeBuilder {
|
|
|
632
648
|
: {}),
|
|
633
649
|
hostTools: [
|
|
634
650
|
...this.hostTools,
|
|
651
|
+
...(this.schedule ? schedulePiToolCandidates(this.schedule) : []),
|
|
635
652
|
...subagentPiToolCandidates(
|
|
636
653
|
this.subagents,
|
|
637
654
|
[...this.enabledSubagents],
|
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
import type { RuntimeProviderPort } from "./ports";
|
|
19
19
|
import { assembleSystemPrompt } from "../lib/prompt";
|
|
20
20
|
import { resolvePiModel } from "../models";
|
|
21
|
-
import type { PiToolCandidate } from "../tools/
|
|
21
|
+
import type { PiToolCandidate } from "../tools/runtime/contracts";
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* 收集完动态 Extension/MCP 候选后,一次性生成最终 Tool Surface。
|
package/src/config/definition.ts
CHANGED
|
@@ -17,7 +17,7 @@ import type {
|
|
|
17
17
|
} from "./profile";
|
|
18
18
|
import type { RuntimeExtensionConfig } from "../context/extension/contracts";
|
|
19
19
|
import type { RuntimeDegradation } from "./degradation";
|
|
20
|
-
import type { PiToolCandidate } from "../tools/
|
|
20
|
+
import type { PiToolCandidate } from "../tools/runtime/contracts";
|
|
21
21
|
import type {
|
|
22
22
|
RuntimeAgentContext,
|
|
23
23
|
RuntimeAgentPlanningContext,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createModels,
|
|
3
|
+
lazyStream,
|
|
3
4
|
type MutableModels,
|
|
4
5
|
} from "@earendil-works/pi-ai";
|
|
5
6
|
import {
|
|
@@ -14,18 +15,17 @@ export {
|
|
|
14
15
|
readModelStreamStallDetails,
|
|
15
16
|
} from "../models";
|
|
16
17
|
import {
|
|
17
|
-
|
|
18
|
+
assembleCurrentPiContext,
|
|
18
19
|
preparePiRuntime,
|
|
19
|
-
readPinnedPiRuntime,
|
|
20
20
|
readPreparedPiRuntime,
|
|
21
|
-
type
|
|
22
|
-
type PinnedPiRuntime,
|
|
21
|
+
type CurrentPiContextOptions,
|
|
23
22
|
type PreparePiRuntimeOptions,
|
|
24
23
|
type PreparedPiRuntime,
|
|
25
|
-
} from "./
|
|
24
|
+
} from "./prepared-runtime";
|
|
25
|
+
import { refreshModelGuidance } from "./model-guidance";
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
* Runtime
|
|
28
|
+
* Runtime 通过这个配置入口准备并读取当前 Harness 的模型、提示和工具输入。
|
|
29
29
|
*
|
|
30
30
|
* @remarks
|
|
31
31
|
* Runtime、Pi、Turn、Tool Candidate 和 canonical transcript 等核心术语见包入口 `../../index.ts`。
|
|
@@ -35,9 +35,7 @@ import {
|
|
|
35
35
|
* 它包含已加载 Extension、Tool candidate 和快照,但 Runtime 只能读取 revision descriptor 与 degradation。
|
|
36
36
|
* - revision descriptor 是影响执行行为的已排序 JSON 描述。
|
|
37
37
|
* Runtime 会对它取哈希,用来识别当前 Prepared Runtime 的基础版本。
|
|
38
|
-
*
|
|
39
|
-
* 它在 Prepared Runtime 基础上加入动态 system context、approval mode 和 base revision。
|
|
40
|
-
* 调用方:`AgentRuntimeKernel` 在配置加载、请求准入与打开 SessionHarness 前调用这些粗粒度能力。
|
|
38
|
+
* 调用方:`AgentRuntimeKernel` 在配置加载与打开 SessionHarness 前调用这些粗粒度能力。
|
|
41
39
|
*
|
|
42
40
|
* 执行位置仍由 `SessionHarness` 拥有,消息读写和投影由 `ConversationHistory` 拥有。
|
|
43
41
|
*/
|
|
@@ -84,39 +82,66 @@ export class HarnessRuntimeConfig {
|
|
|
84
82
|
);
|
|
85
83
|
}
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
interaction(prepared: PreparedPiRuntime, name: string) {
|
|
86
|
+
return readPreparedPiRuntime(prepared, this.owner).candidates.find(
|
|
87
|
+
(candidate) => candidate.tool.name === name,
|
|
88
|
+
)?.interaction;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Read all executable inputs from the current Prepared Runtime. */
|
|
92
|
+
async harnessInputs(
|
|
89
93
|
prepared: PreparedPiRuntime,
|
|
90
|
-
pinnedDescriptor: string,
|
|
91
94
|
request: {
|
|
92
|
-
readonly modelSessionId?: string;
|
|
95
|
+
readonly modelSessionId?: string | (() => string);
|
|
93
96
|
readonly onGeneration?: PiGenerationLifecycleObserver;
|
|
97
|
+
readonly includeMemory?: boolean;
|
|
98
|
+
readonly readExtensionContext?: CurrentPiContextOptions["readExtensionContext"];
|
|
99
|
+
readonly assertRequestAllowed?: () => void;
|
|
100
|
+
readonly onCompactionGeneration?: PiGenerationLifecycleObserver;
|
|
94
101
|
} = {},
|
|
95
102
|
) {
|
|
96
|
-
const
|
|
103
|
+
const state = readPreparedPiRuntime(prepared, this.owner);
|
|
104
|
+
const context = await assembleCurrentPiContext({
|
|
97
105
|
prepared,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
);
|
|
101
|
-
const
|
|
106
|
+
includeMemory: request.includeMemory,
|
|
107
|
+
readExtensionContext: request.readExtensionContext,
|
|
108
|
+
}, this.owner);
|
|
109
|
+
const currentModel = state.snapshot.pi.model;
|
|
102
110
|
let models!: MutableModels;
|
|
111
|
+
const requestStream = (observer = request.onGeneration) => {
|
|
112
|
+
const stream = withProviderBoundary(
|
|
113
|
+
this.models.streamSimple.bind(this.models), request.modelSessionId, observer,
|
|
114
|
+
);
|
|
115
|
+
return ((model, context, options) => lazyStream(model, async () => {
|
|
116
|
+
options?.signal?.throwIfAborted();
|
|
117
|
+
request.assertRequestAllowed?.();
|
|
118
|
+
const refreshed = await refreshModelGuidance(state.candidates, context);
|
|
119
|
+
options?.signal?.throwIfAborted();
|
|
120
|
+
request.assertRequestAllowed?.();
|
|
121
|
+
return stream(model, refreshed, options);
|
|
122
|
+
})) satisfies typeof stream;
|
|
123
|
+
};
|
|
103
124
|
models = new Proxy(this.models, {
|
|
104
125
|
get: (target, property) => {
|
|
105
126
|
if (property === "getModel") {
|
|
106
127
|
return (provider: string, modelId: string) =>
|
|
107
|
-
provider ===
|
|
108
|
-
?
|
|
128
|
+
provider === currentModel.provider && modelId === currentModel.id
|
|
129
|
+
? currentModel
|
|
109
130
|
: target.getModel(provider, modelId);
|
|
110
131
|
}
|
|
111
|
-
if (property === "streamSimple")
|
|
112
|
-
return withProviderBoundary(
|
|
113
|
-
target.streamSimple.bind(target),
|
|
114
|
-
request.modelSessionId,
|
|
115
|
-
request.onGeneration,
|
|
116
|
-
);
|
|
117
|
-
}
|
|
132
|
+
if (property === "streamSimple") return requestStream();
|
|
118
133
|
if (property === "completeSimple") {
|
|
119
|
-
|
|
134
|
+
const compactionModels = new Proxy(models, {
|
|
135
|
+
get: (base, key) => {
|
|
136
|
+
if (key === "streamSimple") return requestStream((event) => {
|
|
137
|
+
request.onGeneration?.(event);
|
|
138
|
+
request.onCompactionGeneration?.(event);
|
|
139
|
+
});
|
|
140
|
+
const value = Reflect.get(base, key, base);
|
|
141
|
+
return typeof value === "function" ? value.bind(base) : value;
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
return target.completeSimple.bind(compactionModels);
|
|
120
145
|
}
|
|
121
146
|
const value = Reflect.get(target, property, target);
|
|
122
147
|
return typeof value === "function" ? value.bind(target) : value;
|
|
@@ -126,16 +151,16 @@ export class HarnessRuntimeConfig {
|
|
|
126
151
|
models,
|
|
127
152
|
model: state.snapshot.pi.model,
|
|
128
153
|
thinkingLevel: state.snapshot.pi.thinkingLevel,
|
|
129
|
-
systemPrompt:
|
|
154
|
+
systemPrompt: context.systemPrompt,
|
|
155
|
+
degradations: context.degradations,
|
|
130
156
|
candidates: state.candidates,
|
|
131
157
|
workspace: state.snapshot.bindings.workspace,
|
|
132
158
|
platform: state.snapshot.bindings.platform,
|
|
133
159
|
compaction: {
|
|
134
160
|
enabled: true,
|
|
135
|
-
// Pi compacts above contextWindow - reserveTokens.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
(state.snapshot.pi.model.contextWindow - state.snapshot.pi.model.maxTokens) * 0.85),
|
|
161
|
+
// Pi compacts above contextWindow - reserveTokens.
|
|
162
|
+
reserveTokens: state.snapshot.pi.model.contextWindow -
|
|
163
|
+
compactionThreshold(state.snapshot.pi.model),
|
|
139
164
|
keepRecentTokens: Math.min(
|
|
140
165
|
20_000,
|
|
141
166
|
Math.floor(state.snapshot.pi.model.contextWindow * 0.15),
|
|
@@ -159,24 +184,27 @@ export class HarnessRuntimeConfig {
|
|
|
159
184
|
}
|
|
160
185
|
}
|
|
161
186
|
|
|
162
|
-
/**
|
|
163
|
-
* 在接受 Submission 前固定本次执行使用的动态上下文和审批模式。
|
|
164
|
-
*
|
|
165
|
-
* @remarks
|
|
166
|
-
* 调用方:Runtime 的准入流程为每个新 Submission 调用,并把 descriptor 与其哈希一起持久化。
|
|
167
|
-
*
|
|
168
|
-
* 实现理由:memory、workspace 和 Extension context 可能随时间变化,所以把准入时的 Turn 字段保存在 descriptor 中。
|
|
169
|
-
* 不要把 pin 推迟到 Turn 启动后,否则已持久化的 Submission 将失去可验证的执行基线。
|
|
170
|
-
*/
|
|
171
|
-
pin(options: PinPiRuntimeOptions): Promise<PinnedPiRuntime> {
|
|
172
|
-
return pinPiRuntime(options, this.owner);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
187
|
}
|
|
176
188
|
|
|
177
189
|
export type {
|
|
178
|
-
PinPiRuntimeOptions,
|
|
179
|
-
PinnedPiRuntime,
|
|
180
190
|
PreparePiRuntimeOptions,
|
|
181
191
|
PreparedPiRuntime,
|
|
182
|
-
} from "./
|
|
192
|
+
} from "./prepared-runtime";
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Input tokens above which a Turn compacts its history.
|
|
196
|
+
*
|
|
197
|
+
* OpenRouter rejects some requests well below a model's advertised window
|
|
198
|
+
* (seen from about 393k tokens on DeepSeek V4.1 Flash and 520k-630k on GPT-6
|
|
199
|
+
* Luna), before any provider is chosen. No model waits past this cap, so long
|
|
200
|
+
* Chats compact instead of failing.
|
|
201
|
+
*/
|
|
202
|
+
export const MAX_COMPACTION_THRESHOLD_TOKENS = 300_000;
|
|
203
|
+
|
|
204
|
+
/** 15% headroom within the input budget, capped at {@link MAX_COMPACTION_THRESHOLD_TOKENS}. */
|
|
205
|
+
export function compactionThreshold(model: { contextWindow: number; maxTokens: number }): number {
|
|
206
|
+
return Math.min(
|
|
207
|
+
Math.floor((model.contextWindow - model.maxTokens) * 0.85),
|
|
208
|
+
MAX_COMPACTION_THRESHOLD_TOKENS,
|
|
209
|
+
);
|
|
210
|
+
}
|
package/src/config/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ export * from "./assembly-view";
|
|
|
7
7
|
export * from "./definition";
|
|
8
8
|
export * from "./degradation";
|
|
9
9
|
export * from "./harness-runtime";
|
|
10
|
-
export * from "./
|
|
10
|
+
export * from "./prepared-runtime";
|
|
11
11
|
export * from "./ports";
|
|
12
12
|
export * from "./profile";
|
|
13
13
|
export * from "./runtime-config";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getCurrentTools, normalizeContext, type Context, type Tool, type TranscriptContext } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { PiToolCandidate } from "../tools/runtime/contracts";
|
|
3
|
+
|
|
4
|
+
/** Request-only presentation; Pi remains the source of transcript and active Tool selection. */
|
|
5
|
+
export async function refreshModelGuidance(candidates: readonly PiToolCandidate[], context: Context): Promise<TranscriptContext> {
|
|
6
|
+
const transcript = normalizeContext(context);
|
|
7
|
+
const active = new Set(getCurrentTools(transcript.messages).map((tool) => tool.name));
|
|
8
|
+
const byName = new Map(candidates.map((candidate) => [candidate.tool.name, candidate]));
|
|
9
|
+
const guidance = new Map(await Promise.all(candidates.filter((candidate) => active.has(candidate.tool.name) && candidate.modelGuidance)
|
|
10
|
+
.map(async (candidate) => [candidate.tool.name, await candidate.modelGuidance!()] as const)));
|
|
11
|
+
const messages = transcript.messages.map((message) => message.role !== "system" || !message.toolsAdded ? message : {
|
|
12
|
+
...message,
|
|
13
|
+
toolsAdded: message.toolsAdded.map((tool): Tool => {
|
|
14
|
+
const candidate = byName.get(tool.name), prompt = guidance.get(tool.name);
|
|
15
|
+
return { ...tool, ...(candidate?.deferLoading ? { deferLoading: true as const } : {}), ...(prompt ? { description: prompt.description } : {}) };
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
const sections = [...new Set([...guidance.values()].flatMap((value) => value.systemPrompt ? [value.systemPrompt] : []))];
|
|
19
|
+
return normalizeContext({ messages: sections.length ? [...messages, { role: "system", content: "", sections: { "springbrand.live-services": sections.join("\n\n") }, timestamp: 1 }] : messages });
|
|
20
|
+
}
|