@springbrand/agent-runtime 0.2.0-alpha.15 → 0.2.0-alpha.16

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@springbrand/agent-runtime",
3
- "version": "0.2.0-alpha.15",
3
+ "version": "0.2.0-alpha.16",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src",
@@ -2,6 +2,7 @@ import type { SkillSource } from "agents/skills";
2
2
  import type { ScheduleSpec } from "./receipts";
3
3
  import type { RuntimeActivityProjection } from "./state";
4
4
  import type { ExecutionLevel } from "../lib/execution-level";
5
+ import type { RuntimeCodeExecutionFactory } from "../tool-registry";
5
6
  import type {
6
7
  RuntimeEventConfirmation,
7
8
  RuntimeLifecycleFact,
@@ -1024,6 +1025,7 @@ export interface RuntimeSkillSourceBinding {
1024
1025
  export interface RuntimeBindings {
1025
1026
  provider: RuntimeProviderPort;
1026
1027
  platform: RuntimePlatformPort;
1028
+ codeExecution?: RuntimeCodeExecutionFactory;
1027
1029
  /** Secret-capability binding; implementations must keep Runtime Grant in closure state. */
1028
1030
  gateway?: RuntimeGatewayPort;
1029
1031
  workspace?: WorkspacePort;
@@ -208,6 +208,7 @@ const IDEMPOTENT_TOOL_NAMES = new Set([
208
208
  "bind_resource",
209
209
  "delete",
210
210
  "edit",
211
+ "execute",
211
212
  "find",
212
213
  "get_time",
213
214
  "grep",
@@ -25,6 +25,7 @@ import { PiChunkEncoder } from "../message";
25
25
  import type { UIMessageChunk } from "ai";
26
26
  import { ChatStreamStalledError } from "agents/chat";
27
27
  import {
28
+ codeExecutionPiToolCandidate,
28
29
  compilePiTools,
29
30
  createPiToolGovernance,
30
31
  normalizeUpdatePlanArguments,
@@ -48,6 +49,7 @@ import {
48
49
  import { EXECUTION_LEVELS } from "../../lib/execution-level";
49
50
  import { projectToolOutputForModel } from "../../layers/context/budget/gate";
50
51
  import type { SpillWorkspace } from "../../lib/artifacts";
52
+ import { toolRegistryFromPiCandidates } from "../../tool-registry";
51
53
 
52
54
  // #region Single-run Pi bridge
53
55
 
@@ -527,7 +529,10 @@ export class PreparedPiTurnAdapter {
527
529
  messageId: options.submission.messageId,
528
530
  startedAt: options.submission.startedAt,
529
531
  });
530
- this.candidates = state.candidates.map((candidate) => ({
532
+ const bindCandidate = (
533
+ candidate: PiToolCandidate,
534
+ toolCallIdPrefix?: string,
535
+ ): PiToolCandidate => ({
531
536
  ...candidate,
532
537
  tool: {
533
538
  ...candidate.tool,
@@ -535,6 +540,9 @@ export class PreparedPiTurnAdapter {
535
540
  // 实时运行由 PiCore 调用它,恢复或审批续跑则由 retryTool 进入同一路径。
536
541
  // 顺序不能随便调整:门禁先于结果复用,审批可能生成结果,不确定的非幂等工作不能重放。
537
542
  execute: async (toolCallId, input, signal, onUpdate) => {
543
+ if (toolCallIdPrefix) {
544
+ toolCallId = `${toolCallIdPrefix}:${toolCallId}`;
545
+ }
538
546
  const requiredExecutionLevel = candidate.requiredExecutionLevelForInput
539
547
  ? await candidate.requiredExecutionLevelForInput(input)
540
548
  : candidate.requiredExecutionLevel;
@@ -667,7 +675,35 @@ export class PreparedPiTurnAdapter {
667
675
  );
668
676
  },
669
677
  },
670
- }));
678
+ });
679
+ this.candidates = state.candidates.map((candidate) => {
680
+ if (!candidate.codeExecutionTools) return bindCandidate(candidate);
681
+ const factory = state.snapshot.bindings.codeExecution;
682
+ if (!factory) {
683
+ throw new Error("Prepared Code Mode Tool requires a Runtime factory");
684
+ }
685
+ return bindCandidate({
686
+ ...candidate,
687
+ tool: {
688
+ ...candidate.tool,
689
+ execute: (toolCallId, input, signal, onUpdate) => {
690
+ const runtimeCandidate = codeExecutionPiToolCandidate(
691
+ factory.create(toolRegistryFromPiCandidates(
692
+ candidate.codeExecutionTools!.map((inner) =>
693
+ bindCandidate(inner, toolCallId)
694
+ ),
695
+ )),
696
+ );
697
+ return runtimeCandidate.tool.execute(
698
+ toolCallId,
699
+ input,
700
+ signal,
701
+ onUpdate,
702
+ );
703
+ },
704
+ },
705
+ });
706
+ });
671
707
  this.turn = new PiTurnAdapter({
672
708
  pi: {
673
709
  model: state.snapshot.pi.model,
@@ -46,6 +46,8 @@ export interface PiToolInteractionSpec {
46
46
  export interface PiToolCandidate {
47
47
  readonly owner: string;
48
48
  readonly tool: AgentTool<any, any>;
49
+ /** @internal Tools hidden behind this Code Mode candidate. */
50
+ readonly codeExecutionTools?: readonly PiToolCandidate[];
49
51
  /** Conservative maximum used in the stable Runtime descriptor. */
50
52
  readonly requiredExecutionLevel: ExecutionLevel;
51
53
  /** Trusted parameter-level policy, evaluated before approval or dispatch. */
@@ -252,12 +252,16 @@ async function createToolSurface(
252
252
  candidate.interaction ||
253
253
  typeof candidate.tool.execute !== "function",
254
254
  );
255
+ const codeExecutionTools = Object.freeze([...mergeable]);
255
256
  return Object.freeze([
256
- Object.freeze(codeExecutionPiToolCandidate(
257
- input.codeExecution.create(
258
- toolRegistryFromPiCandidates(mergeable),
257
+ Object.freeze({
258
+ ...codeExecutionPiToolCandidate(
259
+ input.codeExecution.create(
260
+ toolRegistryFromPiCandidates(codeExecutionTools),
261
+ ),
259
262
  ),
260
- )),
263
+ codeExecutionTools,
264
+ }),
261
265
  ...direct,
262
266
  ]);
263
267
  },
@@ -528,6 +532,7 @@ class RuntimeBuilder {
528
532
  const bindings: RuntimeBindings = Object.freeze({
529
533
  provider,
530
534
  platform: Object.freeze({ ...this.platform }),
535
+ ...(this.codeExecution ? { codeExecution: this.codeExecution } : {}),
531
536
  ...(this.gateway ? { gateway: this.gateway } : {}),
532
537
  ...(this.workspace ? { workspace: this.workspace } : {}),
533
538
  ...(this.memoryProfile.enabled && this.memory
@@ -8,6 +8,7 @@ import type {
8
8
  import type { RuntimeExtensionConfig } from "./kernel/extensions";
9
9
  import type { RuntimeDegradation } from "./kernel/degradation";
10
10
  import type { PiToolCandidate } from "./pi/tool/compiler";
11
+ import { validateToolArguments } from "@earendil-works/pi-ai";
11
12
 
12
13
  /** Tool Surface 筛选策略(不含 Manifest deny 列表)。 */
13
14
  export interface ToolSurfaceSelectionPolicy {
@@ -94,9 +95,15 @@ export function piCandidateToToolSpec(candidate: PiToolCandidate): ToolSpec {
94
95
  if (typeof execute !== "function") {
95
96
  throw new Error(`Tool "${name}" is not executable`);
96
97
  }
98
+ const validated = validateToolArguments(candidate.tool, {
99
+ type: "toolCall",
100
+ id: ctx.toolCallId,
101
+ name,
102
+ arguments: input as Record<string, unknown>,
103
+ });
97
104
  const result = await execute(
98
105
  ctx.toolCallId,
99
- input,
106
+ validated,
100
107
  ctx.signal,
101
108
  undefined,
102
109
  );