@springbrand/agent-runtime 0.2.0-alpha.42 → 0.2.0-alpha.43

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.
@@ -51,7 +51,7 @@ export const webFetchParameters = Type.Object({
51
51
  "默认 false(保留位,当前实现总是返回原始内容)。",
52
52
  }),
53
53
  ),
54
- });
54
+ }, { additionalProperties: false });
55
55
 
56
56
  /** webFetch 工具调用的结构化 details,随 AgentToolResult 一起持久化。 */
57
57
  export interface WebFetchDetails {
@@ -18,7 +18,7 @@ export const webSearchParameters = Type.Object({
18
18
  description: "Additional URLs to analyze along with search (up to 20)",
19
19
  maxItems: 20,
20
20
  })),
21
- });
21
+ }, { additionalProperties: false });
22
22
 
23
23
  export interface WebSearchDetails extends Omit<WebSearchResult, "text"> {
24
24
  resultCount: number;
@@ -119,6 +119,7 @@ export function webSearchPiToolCandidate(
119
119
  };
120
120
  return {
121
121
  owner: "runtime-base",
122
+ exposureMode: "direct",
122
123
  requiredExecutionLevel: "safe",
123
124
  source: "action",
124
125
  tool,
@@ -14,7 +14,7 @@ const parameters = Type.Object({
14
14
  ], {
15
15
  description: "Use previous for the revision immediately before the current one.",
16
16
  }),
17
- });
17
+ }, { additionalProperties: false });
18
18
 
19
19
  function result<T>(details: T): AgentToolResult<T> {
20
20
  return {
@@ -43,6 +43,7 @@ export function workspaceRevisionPiToolCandidate(
43
43
  };
44
44
  return {
45
45
  owner: "workspace",
46
+ exposureMode: "direct",
46
47
  tool,
47
48
  requiredExecutionLevel: "safe",
48
49
  alwaysRequiresApproval: true,
@@ -1,5 +1,4 @@
1
1
  import {
2
- createBashTool,
3
2
  createDeleteTool,
4
3
  createEditTool,
5
4
  createFindTool,
@@ -68,7 +67,7 @@ const workspaceReadParameters = Type.Object({
68
67
  minimum: 1,
69
68
  description: "Maximum lines to return; a page is capped by size regardless",
70
69
  })),
71
- });
70
+ }, { additionalProperties: false });
72
71
  const workspaceEditParameters = Type.Object({
73
72
  path: Type.String({
74
73
  minLength: 1,
@@ -80,7 +79,7 @@ const workspaceEditParameters = Type.Object({
80
79
  "Exact existing text to replace. Include enough surrounding context to match one location.",
81
80
  }),
82
81
  new_string: Type.String({ description: "Replacement text." }),
83
- });
82
+ }, { additionalProperties: false });
84
83
 
85
84
  /**
86
85
  * 单页 read 返回的字符上限。
@@ -176,7 +175,7 @@ function pageReadResult(
176
175
  const span = `lines ${fromLine}-${toLine} of ${totalLines ?? toLine}`;
177
176
  const lossyNote = lossy
178
177
  ? " Some lines were longer than 2000 chars and are cut short; " +
179
- "paging cannot recover them — use grep or bash on this path instead."
178
+ "paging cannot recover them — use grep on this path instead."
180
179
  : "";
181
180
  const marker = fromLine === 1 && eof && !lossy
182
181
  ? ""
@@ -399,12 +398,6 @@ export function workspacePiToolCandidates(
399
398
  },
400
399
  };
401
400
 
402
- const bash = aiToolToPi(
403
- "bash",
404
- createBashTool({ ops: workspace }),
405
- { label: "Workspace Bash" },
406
- );
407
-
408
401
  return [
409
402
  {
410
403
  owner: "workspace",
@@ -420,13 +413,6 @@ export function workspacePiToolCandidates(
420
413
  requiredExecutionLevel: "safe" as const,
421
414
  tool,
422
415
  })),
423
- {
424
- owner: "workspace",
425
- requiredExecutionLevel: "high" as const,
426
- summary: "Run a Bash script over Workspace files",
427
- source: "action" as const,
428
- tool: bash,
429
- },
430
416
  ];
431
417
  }
432
418
 
@@ -460,15 +446,18 @@ const sandboxExecParameters = Type.Object({
460
446
  description: "Maximum command runtime in milliseconds, from 1 to 60000.",
461
447
  }),
462
448
  ),
463
- });
464
- const sandboxStartParameters = Type.Object(sandboxCommandParameters);
449
+ }, { additionalProperties: false });
450
+ const sandboxStartParameters = Type.Object(
451
+ sandboxCommandParameters,
452
+ { additionalProperties: false },
453
+ );
465
454
  const sandboxProcessParameters = Type.Object({
466
455
  id: Type.String({
467
456
  minLength: 1,
468
457
  maxLength: 128,
469
458
  description: "Process ID returned by sandbox_start_process.",
470
459
  }),
471
- });
460
+ }, { additionalProperties: false });
472
461
  const sandboxPublishParameters = Type.Object({
473
462
  paths: Type.Array(Type.String({
474
463
  minLength: 1,
@@ -479,7 +468,7 @@ const sandboxPublishParameters = Type.Object({
479
468
  maxItems: 100,
480
469
  description: "Files to copy from the temporary Sandbox into the persistent Workspace.",
481
470
  }),
482
- });
471
+ }, { additionalProperties: false });
483
472
 
484
473
  /**
485
474
  * 为现有 RuntimeSandboxPort 创建 Pi 命令、进程和文件发布工具。
@@ -35,7 +35,10 @@ import {
35
35
  type PiRuntimeAssembly,
36
36
  } from "./pi/assembly/snapshot";
37
37
  import type { AgentTool } from "@earendil-works/pi-agent-core";
38
- import type { PiToolCandidate } from "./pi/tool/compiler";
38
+ import type {
39
+ PiToolCandidate,
40
+ ToolExposureMode,
41
+ } from "./pi/tool/compiler";
39
42
  import type {
40
43
  AssembleRuntimeSnapshotInput,
41
44
  RuntimeSettings,
@@ -178,6 +181,10 @@ interface ToolSurface {
178
181
  readonly degradations: readonly RuntimeDegradation[];
179
182
  }
180
183
 
184
+ function exposureOf(candidate: PiToolCandidate): ToolExposureMode {
185
+ return candidate.exposureMode ?? "both";
186
+ }
187
+
181
188
  // 唯一 Tool Surface seam:只读取已配置 Port、Resource 和 Agent policy,
182
189
  // 统一生成可见 Tool,并在一处做名称、执行档位与冲突校验。
183
190
  async function createToolSurface(
@@ -203,14 +210,14 @@ async function createToolSurface(
203
210
  });
204
211
  // 平台没有浏览器能力时注册空集:宁可没有这个 Tool,也不注册一个必然失败的 Tool 误导模型。
205
212
  // `create()` 推迟到确认这个名字真的可见之后,被 deny 的装配不白建一个浏览器连接器。
206
- // `direct` 使它只走 Direct 调用,不再被并进 `execute` 的工具集——两个 Code Mode 类工具互相嵌套
213
+ // `direct` exposure 使它只走 Direct 调用,不再被并进 `execute` 的工具集——两个 Code Mode 类工具互相嵌套
207
214
  // 会让「哪次执行被记录、被重放」变得无法解释,而它们的分工本就由 System Prompt 划清。
208
215
  const browserVisible = !deny.has(BROWSER_EXECUTE_TOOL_NAME) &&
209
216
  allowsTool?.(BROWSER_EXECUTE_TOOL_NAME) !== false;
210
217
  const browserCandidates = input.platform.browser && browserVisible
211
218
  ? [{
212
219
  ...browserExecutionPiToolCandidate(input.platform.browser.create()),
213
- direct: true as const,
220
+ exposureMode: "direct" as const,
214
221
  }]
215
222
  : [];
216
223
  const baseCandidates = basePiToolCandidates(input.webSearch);
@@ -256,7 +263,7 @@ async function createToolSurface(
256
263
 
257
264
  const finalized = [...tools.values()];
258
265
  const directVisible = finalized.filter(
259
- (candidate) => !candidate.codeExecutionOnly,
266
+ (candidate) => exposureOf(candidate) !== "codemode",
260
267
  );
261
268
  if (tools.has("execute")) {
262
269
  throw new Error("SpringBrand reserved Runtime Tool name: execute");
@@ -273,7 +280,7 @@ async function createToolSurface(
273
280
  }
274
281
  const mergeable = finalized.filter(
275
282
  (candidate) =>
276
- !candidate.direct &&
283
+ exposureOf(candidate) !== "direct" &&
277
284
  !candidate.interaction &&
278
285
  typeof candidate.tool.execute === "function",
279
286
  );
@@ -316,7 +323,7 @@ async function createToolSurface(
316
323
  ? "Skill resource copying is available only inside execute through " +
317
324
  "`tools.materialize_skill_resource({ name, path, destination })`; " +
318
325
  "no Direct Tool is registered for it. " +
319
- "Use a loop or Promise.all when copying multiple resources.\n\n"
326
+ "Copy multiple resources sequentially in one execute so durable replay preserves call order.\n\n"
320
327
  : "";
321
328
  return Object.freeze({
322
329
  candidates: Object.freeze([
package/src/runtime.ts CHANGED
@@ -899,6 +899,7 @@ export abstract class AgentRuntimeKernel<
899
899
  return this.pi.createTurn({
900
900
  prepared: this.preparedPi(),
901
901
  pinnedDescriptor: submission.assemblyDescriptor,
902
+ modelSessionId: this.name,
902
903
  submission: {
903
904
  id: submission.submissionId,
904
905
  requestId: submission.requestId,
@@ -996,6 +997,7 @@ export abstract class AgentRuntimeKernel<
996
997
  ...(event.error ? { error: errorText(event.error) } : {}),
997
998
  occurredAt: event.occurredAt,
998
999
  });
1000
+ this.projectNestedToolSettlement(submission.submissionId, event);
999
1001
  },
1000
1002
  ...(toolExecutors && Object.keys(toolExecutors).length > 0
1001
1003
  ? { toolExecutors }
@@ -2200,6 +2202,32 @@ export abstract class AgentRuntimeKernel<
2200
2202
  return this.drainRuntimeEvents();
2201
2203
  }
2202
2204
 
2205
+ private projectNestedToolSettlement(
2206
+ submissionId: string,
2207
+ event: Readonly<{
2208
+ toolCallId: string;
2209
+ toolName: string;
2210
+ outcome: "completed" | "failed" | "cancelled";
2211
+ occurredAt: number;
2212
+ }>,
2213
+ ): void {
2214
+ const settlement: RuntimeToolSettlementEvent = {
2215
+ eventId: `${submissionId}:tool:${event.toolCallId}`,
2216
+ submissionId,
2217
+ toolCallId: event.toolCallId,
2218
+ toolName: event.toolName,
2219
+ status: event.outcome === "completed" ? "success" : "error",
2220
+ };
2221
+ this.db.transaction(() => {
2222
+ this.db.runtimeEvents.insert({
2223
+ eventId: settlement.eventId,
2224
+ body: json({ type: "tool-settlement", event: settlement }),
2225
+ createdAt: event.occurredAt,
2226
+ });
2227
+ });
2228
+ this.ctx.waitUntil(this.drainRuntimeEvents());
2229
+ }
2230
+
2203
2231
  // 终态事务用这个同步内核先补齐 ToolResult,再在同一事务末尾写 marker。
2204
2232
  private settleToolSync(
2205
2233
  submissionId: string,