@cydm/pie 2.7.6 → 2.7.7

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.
@@ -2,7 +2,7 @@ import { createRequire as __createRequire } from "node:module"; const require =
2
2
  import {
3
3
  Agent,
4
4
  getToolCallerIdentity
5
- } from "./chunk-5YWEUFLM.js";
5
+ } from "./chunk-W5B3OXHQ.js";
6
6
  import {
7
7
  AssistantMessageEventStream,
8
8
  FileSystemGateway,
@@ -12,7 +12,7 @@ import {
12
12
  getPlatformConfig,
13
13
  hashText,
14
14
  streamSimple
15
- } from "./chunk-ZF5FHTES.js";
15
+ } from "./chunk-2FEDB5GR.js";
16
16
  import {
17
17
  Type
18
18
  } from "./chunk-XXGYUYXI.js";
@@ -899,11 +899,6 @@ function shouldPreserveExecutionStateForUserText(text) {
899
899
  return CONTINUE_PATTERNS.some((pattern) => pattern.test(trimmed));
900
900
  }
901
901
 
902
- // ../../packages/shared-headless-capabilities/src/capability.ts
903
- function defineSharedCapability(definition) {
904
- return definition;
905
- }
906
-
907
902
  // ../../packages/shared-headless-capabilities/src/tool-policy.ts
908
903
  var BUILTIN_TOOL_CAPABILITY_METADATA = {
909
904
  search_context: { riskClass: "read_only", concurrencySafe: true, permissionScope: "session", availableInPlanMode: true, allowedForSubagentByDefault: false, maxOutputChars: 2e4 },
@@ -914,6 +909,7 @@ var BUILTIN_TOOL_CAPABILITY_METADATA = {
914
909
  write_file: { riskClass: "write", concurrencySafe: false, requiresPermission: true, permissionScope: "filesystem", writesFile: true, highRisk: true },
915
910
  edit_file: { riskClass: "write", concurrencySafe: false, requiresPermission: true, permissionScope: "filesystem", writesFile: true, highRisk: true },
916
911
  bash: { riskClass: "shell", concurrencySafe: false, requiresPermission: true, permissionScope: "shell", executesShell: true, availableInPlanMode: true, allowedForSubagentByDefault: true, highRisk: true, maxOutputChars: 1e5 },
912
+ shell: { riskClass: "shell", concurrencySafe: false, requiresPermission: true, permissionScope: "shell", executesShell: true, availableInPlanMode: true, allowedForSubagentByDefault: true, highRisk: true, maxOutputChars: 1e5 },
917
913
  web_research: { riskClass: "network", concurrencySafe: true, permissionScope: "network", readsNetwork: true, availableInPlanMode: true, allowedForSubagentByDefault: true, maxOutputChars: 1e5 },
918
914
  web_search: { riskClass: "network", concurrencySafe: true, permissionScope: "network", readsNetwork: true, availableInPlanMode: true, allowedForSubagentByDefault: true, maxOutputChars: 1e5 },
919
915
  web_fetch: { riskClass: "network", concurrencySafe: true, permissionScope: "network", readsNetwork: true, availableInPlanMode: true, allowedForSubagentByDefault: true, maxOutputChars: 1e5 },
@@ -975,6 +971,11 @@ function getSubagentToolPolicyDecision(tool) {
975
971
  }
976
972
  var DEFAULT_PLAN_MODE_TOOL_NAMES = Object.entries(BUILTIN_TOOL_CAPABILITY_METADATA).filter(([, metadata]) => metadata.availableInPlanMode).map(([name]) => name);
977
973
 
974
+ // ../../packages/shared-headless-capabilities/src/capability.ts
975
+ function defineSharedCapability(definition) {
976
+ return definition;
977
+ }
978
+
978
979
  // ../../packages/shared-headless-capabilities/src/plan.ts
979
980
  var import_shell_quote = __toESM(require_shell_quote(), 1);
980
981
  var SIMPLE_READ_COMMANDS = /* @__PURE__ */ new Set(["pwd", "ls", "cat", "head", "tail", "wc", "grep", "rg", "which", "type", "echo", "printf", "cd", "true", "false", "tree", "jq", "stat", "file", "du", "df", "basename", "dirname", "date", "uname", "sort", "uniq", "cut", "column", "shasum", "printenv"]);
@@ -1752,6 +1753,9 @@ function normalizeUserContentBlock(block) {
1752
1753
  if (block.type === "text") {
1753
1754
  return { type: "text", text: block.text };
1754
1755
  }
1756
+ if (block.type === "hostContext") {
1757
+ return { type: "text", text: "x".repeat(512) };
1758
+ }
1755
1759
  if (block.type === "image") {
1756
1760
  return { type: "image", mimeType: block.mimeType };
1757
1761
  }
@@ -1932,6 +1936,107 @@ function getSessionEntryText(entry) {
1932
1936
  }).filter(Boolean).join("\n");
1933
1937
  }
1934
1938
 
1939
+ // ../../packages/agent-framework/src/session/host-envelope.ts
1940
+ import { createHash } from "node:crypto";
1941
+ function createHostEnvelopeProjectorRegistry(projectors = []) {
1942
+ const byKey = /* @__PURE__ */ new Map();
1943
+ const latestByKind = /* @__PURE__ */ new Map();
1944
+ const key = (kind, version) => `${kind}@${version}`;
1945
+ for (const projector of projectors) validateAndIndex(projector);
1946
+ function validateAndIndex(input) {
1947
+ if (!input.kind || typeof input.kind !== "string") throw new Error("Host envelope projector requires a kind.");
1948
+ if (!Number.isInteger(input.version) || input.version < 1) throw new Error(`Host envelope projector ${input.kind} requires an integer version >= 1.`);
1949
+ if (typeof input.project !== "function") throw new Error(`Host envelope projector ${input.kind} requires a project function.`);
1950
+ const projector = Object.freeze({
1951
+ kind: input.kind,
1952
+ version: input.version,
1953
+ project: input.project
1954
+ });
1955
+ const existing = byKey.get(key(projector.kind, projector.version));
1956
+ if (existing && existing.project !== projector.project) {
1957
+ throw new Error(`Duplicate host envelope projector registration for ${projector.kind}@${projector.version}: a version's text must never change after registration.`);
1958
+ }
1959
+ byKey.set(key(projector.kind, projector.version), projector);
1960
+ const currentLatest = latestByKind.get(projector.kind);
1961
+ if (!currentLatest || projector.version > currentLatest.version) latestByKind.set(projector.kind, projector);
1962
+ }
1963
+ return {
1964
+ register: validateAndIndex,
1965
+ resolve: (kind, version) => byKey.get(key(kind, version)),
1966
+ latest: (kind) => latestByKind.get(kind),
1967
+ listPins: () => [...byKey.values()].map(({ kind, version }) => ({ kind, version }))
1968
+ };
1969
+ }
1970
+ function projectHostContextBlock(block, registry) {
1971
+ let hash;
1972
+ try {
1973
+ hash = createHash("sha256").update(JSON.stringify({ kind: block.kind, version: block.version, data: block.data })).digest("hex").slice(0, 16);
1974
+ } catch {
1975
+ hash = "unhashable";
1976
+ }
1977
+ const projector = registry.resolve(block.kind, block.version);
1978
+ if (!projector) {
1979
+ return {
1980
+ text: `runtime state: unknown (projector missing: ${block.kind}@${block.version})`,
1981
+ degraded: true,
1982
+ audit: { kind: block.kind, version: block.version, hash }
1983
+ };
1984
+ }
1985
+ try {
1986
+ const text = projector.project(block.data);
1987
+ if (typeof text !== "string" || text.trim().length === 0) throw new Error("projector returned empty text");
1988
+ return { text, degraded: false, audit: { kind: block.kind, version: block.version, hash } };
1989
+ } catch (error) {
1990
+ return {
1991
+ text: `runtime state: unknown (projector failed: ${block.kind}@${block.version}: ${error instanceof Error ? error.message.slice(0, 120) : String(error).slice(0, 120)})`,
1992
+ degraded: true,
1993
+ audit: { kind: block.kind, version: block.version, hash }
1994
+ };
1995
+ }
1996
+ }
1997
+ function leadingHostContextBlock(message) {
1998
+ if (message.role !== "user") return void 0;
1999
+ const content = message.content;
2000
+ if (!Array.isArray(content) || content.length === 0) return void 0;
2001
+ const first = content[0];
2002
+ if (!isHostContextBlock(first)) return void 0;
2003
+ return first;
2004
+ }
2005
+ function projectHostEnvelopes(messages, registry, onProjection) {
2006
+ let changed = false;
2007
+ const next = [];
2008
+ for (const message of messages) {
2009
+ const block = leadingHostContextBlock(message);
2010
+ if (!block) {
2011
+ next.push(message);
2012
+ continue;
2013
+ }
2014
+ const outcome = projectHostContextBlock(block, registry);
2015
+ onProjection?.(outcome);
2016
+ const content = message.content;
2017
+ const rest = content.slice(1);
2018
+ const first = rest[0];
2019
+ let projectedContent;
2020
+ if (first && first.type === "text" && typeof first.text === "string") {
2021
+ const merged = first.text.trim().length > 0 ? `${outcome.text}
2022
+
2023
+ ${first.text}` : outcome.text;
2024
+ projectedContent = [{ ...first, text: merged }, ...rest.slice(1)];
2025
+ } else {
2026
+ projectedContent = [{ type: "text", text: outcome.text }, ...rest];
2027
+ }
2028
+ next.push({ ...message, content: projectedContent });
2029
+ changed = true;
2030
+ }
2031
+ return changed ? next : messages;
2032
+ }
2033
+ function isHostContextBlock(value) {
2034
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
2035
+ const block = value;
2036
+ const version = block.version;
2037
+ return block.type === "hostContext" && typeof block.kind === "string" && block.kind.length > 0 && block.kind.length <= 128 && typeof version === "number" && Number.isInteger(version) && version >= 1 && !!block.data && typeof block.data === "object" && !Array.isArray(block.data);
2038
+ }
2039
+
1935
2040
  // ../../packages/agent-framework/src/session/store.ts
1936
2041
  import { createRequire } from "node:module";
1937
2042
  import { fileURLToPath } from "node:url";
@@ -2972,7 +3077,19 @@ function normalizeSessionDisplayName(raw) {
2972
3077
  }
2973
3078
  function isSyntheticEnvelopePreview(preview) {
2974
3079
  const trimmed = preview.trimStart();
2975
- return trimmed.startsWith("<environment_context>") || trimmed.startsWith("<turn_aborted>") || trimmed.startsWith("# AGENTS.md instructions") || trimmed.startsWith("## Project Context\nProject-specific instructions from AGENTS.md");
3080
+ if (trimmed.startsWith("<environment_context>") || trimmed.startsWith("<turn_aborted>") || trimmed.startsWith("# AGENTS.md instructions") || trimmed.startsWith("## Project Context\nProject-specific instructions from AGENTS.md")) {
3081
+ return true;
3082
+ }
3083
+ const runtimeMatch = trimmed.match(/^<runtime-mode-context>[\s\S]*?<\/runtime-mode-context>/);
3084
+ if (runtimeMatch) {
3085
+ const rest = trimmed.slice(runtimeMatch[0].length).trim();
3086
+ if (rest.length === 0) return true;
3087
+ return false;
3088
+ }
3089
+ if (trimmed.startsWith("<runtime-mode-context>")) {
3090
+ return true;
3091
+ }
3092
+ return /^HE_INPUT kind=(?:USER_REQUEST|QUEUE_WAKE) source=/.test(trimmed);
2976
3093
  }
2977
3094
  function isAutoGeneratedSessionName(name) {
2978
3095
  return /^Session (\d|[a-z0-9]{6,8}$)/i.test(name.trim());
@@ -2986,7 +3103,10 @@ function userEntryPreview(entry) {
2986
3103
  if (!message) return "";
2987
3104
  if (typeof message.content === "string") return message.content;
2988
3105
  if (!Array.isArray(message.content)) return "";
2989
- return message.content.map((block) => isRecord2(block) && block.type === "text" && typeof block.text === "string" ? block.text : "").join(" ").trim();
3106
+ return message.content.map((block) => {
3107
+ if (isRecord2(block) && block.type === "hostContext") return "";
3108
+ return isRecord2(block) && block.type === "text" && typeof block.text === "string" ? block.text : "";
3109
+ }).join(" ").trim();
2990
3110
  }
2991
3111
  function entryJsonBytes(entry) {
2992
3112
  try {
@@ -4917,7 +5037,7 @@ function isUserContentBlock(value) {
4917
5037
  return false;
4918
5038
  }
4919
5039
  const type = value.type;
4920
- return type === "text" || type === "image" || type === "audio" || type === "video" || type === "fileRef";
5040
+ return type === "text" || type === "image" || type === "audio" || type === "video" || type === "fileRef" || type === "hostContext";
4921
5041
  }
4922
5042
  function normalizePromptInput(input) {
4923
5043
  if (typeof input === "string") {
@@ -5629,7 +5749,7 @@ function toolOutcomeKind(event) {
5629
5749
  return typeof kind === "string" ? kind : void 0;
5630
5750
  }
5631
5751
  function isCommandNonzero(event) {
5632
- return event.toolName === "bash" && toolOutcomeKind(event) === "command_nonzero";
5752
+ return toolOutcomeKind(event) === "command_nonzero";
5633
5753
  }
5634
5754
  function isStateChangingSuccess(event) {
5635
5755
  if (event.isError) return false;
@@ -5637,7 +5757,7 @@ function isStateChangingSuccess(event) {
5637
5757
  if (outcomeKind === "command_nonzero" || outcomeKind === "timeout" || outcomeKind === "signal") return false;
5638
5758
  if (event.toolName === "edit_file" || event.toolName === "write_file") return true;
5639
5759
  const details = event.result && typeof event.result === "object" ? event.result.details : void 0;
5640
- return event.toolName === "bash" && details?.commandClass === "filesystem_write";
5760
+ return details?.commandClass === "filesystem_write";
5641
5761
  }
5642
5762
  var AgentSessionRuntimeGuard = class {
5643
5763
  constructor(trigger, advise) {
@@ -7915,14 +8035,22 @@ var SessionTraceSnapshotWriter = class {
7915
8035
 
7916
8036
  // ../../packages/agent-framework/src/delegation.ts
7917
8037
  var DELEGATION_REPORT_MAX_CHARS = 24e3;
7918
- var DELEGATION_REPORT_INSTRUCTION = 'Return your final response once as a JSON object: {"summary":string,"result":string,"remainingWork"?:string[],"evidenceRefs"?:string[]}. Do not make an extra tool call to report. Evidence references are your claims, not runtime verification.';
8038
+ var DELEGATION_REPORT_INSTRUCTION = 'Return your final response once as a JSON object: {"summary":string,"result":string,"remainingWork"?:string[],"evidenceRefs"?:string[]}. Keep the entire JSON response within 24,000 characters: give concise conclusions and bounded evidence references, not repeated drafting notes. Do not make an extra tool call to report. Evidence references are your claims, not runtime verification.';
7919
8039
  function parseDelegationReport(text, runId, truncated = false) {
7920
8040
  const raw = (text ?? "").slice(0, DELEGATION_REPORT_MAX_CHARS);
7921
8041
  const base = { version: 1, runId: runId.slice(0, 160), raw };
7922
- if (truncated || (text?.length ?? 0) > DELEGATION_REPORT_MAX_CHARS) return { ...base, availability: "truncated" };
7923
- if (!raw.trim()) return { ...base, availability: "missing" };
8042
+ const stopReason = typeof truncated === "string" ? truncated.slice(0, 40) : void 0;
8043
+ const reason = stopReason === "length" ? "provider_length" : ["error", "aborted", "deadline", "cancelled", "no_terminal"].includes(stopReason ?? "") ? stopReason : stopReason && stopReason !== "stop" ? "no_terminal" : truncated === true ? "provider_length" : void 0;
8044
+ if (reason) return { ...base, stopReason, reason, availability: raw ? "truncated" : "missing" };
8045
+ if ((text?.length ?? 0) > DELEGATION_REPORT_MAX_CHARS) return { ...base, availability: "truncated", reason: "char_cap" };
8046
+ if (!raw.trim()) return { ...base, availability: "missing", reason: "empty" };
8047
+ let value;
8048
+ try {
8049
+ value = JSON.parse(raw);
8050
+ } catch {
8051
+ return { ...base, availability: "malformed", reason: "invalid_json" };
8052
+ }
7924
8053
  try {
7925
- const value = JSON.parse(raw);
7926
8054
  if (!value || typeof value !== "object" || Array.isArray(value) || typeof value.summary !== "string" || typeof value.result !== "string") throw new Error();
7927
8055
  if (Object.keys(value).some((key) => !["summary", "result", "remainingWork", "evidenceRefs"].includes(key))) throw new Error();
7928
8056
  for (const key of ["remainingWork", "evidenceRefs"]) {
@@ -7930,7 +8058,7 @@ function parseDelegationReport(text, runId, truncated = false) {
7930
8058
  }
7931
8059
  return { ...base, availability: "available", report: value };
7932
8060
  } catch {
7933
- return { ...base, availability: "malformed" };
8061
+ return { ...base, availability: "malformed", reason: "invalid_shape" };
7934
8062
  }
7935
8063
  }
7936
8064
  function observeDelegationPhase(observation, phase, workActivity = true) {
@@ -7959,6 +8087,58 @@ function observeDelegationTool(observation, event) {
7959
8087
  if (observation.receipts.length > 32) observation.receipts.shift();
7960
8088
  }
7961
8089
 
8090
+ // ../../packages/shared-headless-capabilities/src/subagent-usage.ts
8091
+ function createSubagentUsage(sessionId, runId) {
8092
+ return { sessionId, runId, attempts: 0, reportedAttempts: 0, unknownAttempts: 0, pendingAttempts: 0, status: "pending", reportedTokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cacheWriteLong: 0, totalTokens: 0 }, estimatedCost: null };
8093
+ }
8094
+ function settleSubagentUsage(usage, settled) {
8095
+ usage.status = !settled || usage.pendingAttempts > 0 ? "pending" : usage.attempts === 0 || usage.reportedAttempts === 0 ? "unknown" : usage.unknownAttempts > 0 ? "partial" : "complete";
8096
+ }
8097
+ function beginSubagentUsage(usage, model) {
8098
+ usage.attempts++;
8099
+ usage.pendingAttempts++;
8100
+ let ended = false;
8101
+ return (message) => {
8102
+ if (ended) return;
8103
+ ended = true;
8104
+ usage.pendingAttempts--;
8105
+ const values = message?.usage;
8106
+ const known = message?.usageReported === true && values && [values.input, values.output, values.cacheRead, values.cacheWrite, values.totalTokens].every((n) => Number.isFinite(n) && n >= 0);
8107
+ if (known) {
8108
+ usage.reportedAttempts++;
8109
+ for (const key of Object.keys(usage.reportedTokens)) usage.reportedTokens[key] += values[key] ?? 0;
8110
+ } else usage.unknownAttempts++;
8111
+ const priced = known && ["input", "output", "cacheRead", "cacheWrite"].every((key) => typeof model?.cost?.[key] === "number" && Number.isFinite(model.cost[key]) && model.cost[key] >= 0) && Object.values(model.cost).some((value) => typeof value === "number" && value > 0) && Number.isFinite(values.cost?.total) && values.cost.total >= 0;
8112
+ if (usage.attempts === 1 && priced) usage.estimatedCost = values.cost.total;
8113
+ else if (priced && usage.estimatedCost !== null) usage.estimatedCost += values.cost.total;
8114
+ else usage.estimatedCost = null;
8115
+ };
8116
+ }
8117
+ function restoreSubagentUsageRuns(value) {
8118
+ if (!Array.isArray(value)) return void 0;
8119
+ return value.slice(-4).flatMap((item) => {
8120
+ if (!item || typeof item !== "object" || typeof item.sessionId !== "string" || typeof item.runId !== "string" || item.sessionId.length > 160 || item.runId.length > 160 || !["pending", "partial", "complete", "unknown"].includes(item.status)) return [];
8121
+ if (![item.attempts, item.reportedAttempts, item.unknownAttempts, item.pendingAttempts].every((n) => Number.isSafeInteger(n) && n >= 0)) return [];
8122
+ if (item.reportedAttempts + item.unknownAttempts + item.pendingAttempts !== item.attempts) return [];
8123
+ const tokens = item.reportedTokens;
8124
+ const keys = ["input", "output", "cacheRead", "cacheWrite", "cacheWriteLong", "totalTokens"];
8125
+ if (!tokens || !keys.every((key) => Number.isFinite(tokens[key]) && tokens[key] >= 0)) return [];
8126
+ const expectedStatus = item.pendingAttempts > 0 ? "pending" : item.reportedAttempts === 0 ? "unknown" : item.unknownAttempts > 0 ? "partial" : "complete";
8127
+ if (item.status !== expectedStatus && !(item.status === "pending" && item.attempts === 0)) return [];
8128
+ return [{
8129
+ sessionId: item.sessionId,
8130
+ runId: item.runId,
8131
+ attempts: item.attempts,
8132
+ reportedAttempts: item.reportedAttempts,
8133
+ unknownAttempts: item.unknownAttempts,
8134
+ pendingAttempts: item.pendingAttempts,
8135
+ status: item.status,
8136
+ reportedTokens: Object.fromEntries(keys.map((key) => [key, tokens[key]])),
8137
+ estimatedCost: item.reportedAttempts > 0 && item.unknownAttempts === 0 && item.pendingAttempts === 0 && typeof item.estimatedCost === "number" && Number.isFinite(item.estimatedCost) && item.estimatedCost >= 0 ? item.estimatedCost : null
8138
+ }];
8139
+ });
8140
+ }
8141
+
7962
8142
  // ../../packages/shared-headless-capabilities/src/subagent-stream.ts
7963
8143
  var SUBAGENT_FINALIZATION_HINT = "The run's soft deadline has arrived. Do not start new tools. Return the requested final report now, including remaining work; already-running tools may finish.";
7964
8144
  function subagentSoftDeadlineMs(budgetMs) {
@@ -7969,6 +8149,10 @@ function withSubagentRunContext(stream, hooks) {
7969
8149
  if (hooks.hardDeadlineAt !== void 0 && Date.now() >= hooks.hardDeadlineAt) throw new Error("Subagent hard deadline reached");
7970
8150
  if (hooks.softDeadlineAt !== void 0 && Date.now() >= hooks.softDeadlineAt) hooks.finalizing = true;
7971
8151
  if (hooks.observation) observeDelegationPhase(hooks.observation, hooks.finalizing ? "finalizing" : "request");
8152
+ try {
8153
+ hooks.onPhase?.(hooks.finalizing ? "finalizing" : "request");
8154
+ } catch {
8155
+ }
7972
8156
  return stream(model, hooks.finalizing ? { ...context, systemPrompt: `${context.systemPrompt ?? ""}
7973
8157
 
7974
8158
  ${SUBAGENT_FINALIZATION_HINT}` } : context, options);
@@ -7992,11 +8176,17 @@ function cancellableBackoff(ms, signal) {
7992
8176
  if (signal?.aborted) finish();
7993
8177
  });
7994
8178
  }
7995
- function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivity) {
8179
+ function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivity, hooks) {
7996
8180
  return async (model, context, options) => {
7997
8181
  for (let attempt = 0; ; attempt++) {
8182
+ if (hooks) {
8183
+ hooks.terminalText = void 0;
8184
+ hooks.terminalStopReason = void 0;
8185
+ }
7998
8186
  if (options?.signal?.aborted) throw new Error("Subagent aborted by parent");
8187
+ if (hooks?.hardDeadlineAt !== void 0 && Date.now() >= hooks.hardDeadlineAt) throw new Error("Subagent hard deadline reached");
7999
8188
  let message;
8189
+ const finishUsage = hooks?.usage ? beginSubagentUsage(hooks.usage, model) : void 0;
8000
8190
  try {
8001
8191
  const response = await stream(model, context, options);
8002
8192
  let terminal;
@@ -8009,8 +8199,19 @@ function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivi
8009
8199
  onActivity?.(next.value.type);
8010
8200
  } catch {
8011
8201
  }
8012
- if (next.value.type === "done") terminal = next.value.message;
8013
- if (next.value.type === "error") terminal = next.value.error;
8202
+ if (next.value.type === "done") {
8203
+ terminal = next.value.message;
8204
+ finishUsage?.(terminal);
8205
+ }
8206
+ if (next.value.type === "error") {
8207
+ terminal = next.value.error;
8208
+ finishUsage?.(terminal);
8209
+ }
8210
+ if (hooks && (next.value.type === "done" || next.value.type === "error")) {
8211
+ const reportTerminal = terminal && ["stop", "length", "error", "aborted"].includes(terminal.stopReason) && !terminal.content.some((block) => block.type === "toolCall");
8212
+ hooks.terminalStopReason = reportTerminal && terminal ? terminal.stopReason : void 0;
8213
+ hooks.terminalText = reportTerminal && terminal ? terminal.content.filter((block) => block.type === "text").map((block) => block.text).join("\n").slice(0, 24001) : void 0;
8214
+ }
8014
8215
  }
8015
8216
  } finally {
8016
8217
  try {
@@ -8021,6 +8222,7 @@ function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivi
8021
8222
  if (!terminal) throw new Error("Provider protocol error: response ended without a terminal event");
8022
8223
  message = terminal;
8023
8224
  } catch (error) {
8225
+ finishUsage?.();
8024
8226
  if (!options?.signal?.aborted && attempt < 2 && isRetryableSubagentFailure(error instanceof Error ? error.message : String(error))) {
8025
8227
  try {
8026
8228
  onRetry?.();
@@ -8110,6 +8312,7 @@ function toSessionRecord(session, messages) {
8110
8312
  runCount: session.runCount,
8111
8313
  revision: session.revision,
8112
8314
  reports: session.reports?.slice(-4),
8315
+ usageRuns: session.usageRuns?.slice(-4).map((usage) => ({ ...usage, reportedTokens: { ...usage.reportedTokens } })),
8113
8316
  messages
8114
8317
  };
8115
8318
  }
@@ -8184,6 +8387,7 @@ var POWERSHELL_DESTRUCTIVE_PATTERNS = [
8184
8387
  { pattern: /\bRemove-Item\b[^;\n|]*(?:HKLM:|HKCU:|Registry::)/i, warning: "may remove registry entries", commandClass: "infra_destructive" },
8185
8388
  { pattern: /\b(Set-ExecutionPolicy|Start-Process\b[^;\n|]*-Verb\s+RunAs)\b/i, warning: "may change execution policy or request elevation", commandClass: "privilege_escalation" },
8186
8389
  { pattern: /\b(Stop-Computer|Restart-Computer)\b/i, warning: "may stop or restart the machine", commandClass: "infra_destructive" },
8390
+ { pattern: /(^|[;|&\n])\s*(?:Invoke-Expression|iex)\b/i, warning: "evaluates a dynamically constructed string as code", commandClass: "privilege_escalation" },
8187
8391
  { pattern: /\bgit\s+reset\s+--hard\b/i, warning: "may discard uncommitted changes", commandClass: "git_destructive" },
8188
8392
  { pattern: /\bgit\s+push\b[^;&|\n]*[ \t](--force|--force-with-lease|-f)\b/i, warning: "may overwrite remote history", commandClass: "git_destructive" }
8189
8393
  ];
@@ -8602,13 +8806,13 @@ function wrapBashGuard(tool, plan) {
8602
8806
  return {
8603
8807
  ...tool,
8604
8808
  description: `${tool.description}
8605
- Subagent guarded mode uses the same bash risk policy as the parent agent. Destructive commands are blocked; normal diagnostic, search, test, and build commands are allowed.`,
8809
+ Subagent guarded mode blocks destructive shell commands while preserving the parent's execution checks. Destructive commands are blocked; normal diagnostic, search, test, and build commands are allowed.`,
8606
8810
  execute: async (toolCallId, params, signal, onUpdate) => {
8607
8811
  const command = typeof params?.command === "string" ? params.command : "";
8608
8812
  const classification = classifyBashCommand(command);
8609
8813
  if (classification.destructive) {
8610
- const reason = `destructive bash command blocked for subagent: ${classification.riskWarnings.join("; ")}`;
8611
- plan.toolsExcluded.push({ name: "bash", reason });
8814
+ const reason = `destructive shell command blocked for subagent: ${classification.riskWarnings.join("; ")}`;
8815
+ plan.toolsExcluded.push({ name: tool.name, reason });
8612
8816
  return {
8613
8817
  content: [{ type: "text", text: reason }],
8614
8818
  details: {
@@ -8636,10 +8840,6 @@ function filterToolsForSubagent(runtimeTools, parentTools, plan, mode) {
8636
8840
  plan.toolsExcluded.push({ name: tool.name, reason: "not available in parent tool set" });
8637
8841
  continue;
8638
8842
  }
8639
- if (tool.name === "bash") {
8640
- filtered.push(wrapBashGuard(tool, plan));
8641
- continue;
8642
- }
8643
8843
  if (mode === "edit" && (tool.name === "write_file" || tool.name === "edit_file")) {
8644
8844
  filtered.push(tool);
8645
8845
  continue;
@@ -8649,7 +8849,7 @@ function filterToolsForSubagent(runtimeTools, parentTools, plan, mode) {
8649
8849
  plan.toolsExcluded.push({ name: tool.name, reason: policy.reason || "not allowed for subagent" });
8650
8850
  continue;
8651
8851
  }
8652
- filtered.push(tool);
8852
+ filtered.push(getToolCapabilityMetadata(tool)?.executesShell === true ? wrapBashGuard(tool, plan) : tool);
8653
8853
  }
8654
8854
  return filtered;
8655
8855
  }
@@ -8745,12 +8945,13 @@ function createAgentInstance(deps, systemPrompt, tools, model, apiKey, thinkingL
8745
8945
  }
8746
8946
  }), hooks), deps.transientRetryBackoffMs, () => {
8747
8947
  if (hooks.observation) observeDelegationPhase(hooks.observation, "response_retry");
8948
+ hooks.onPhase?.("response_retry");
8748
8949
  }, () => {
8749
8950
  if (hooks.observation) {
8750
8951
  hooks.observation.lastWorkAt = Date.now();
8751
8952
  hooks.observation.lastActivity = "response_event";
8752
8953
  }
8753
- });
8954
+ }, hooks);
8754
8955
  if (deps.createAgent) {
8755
8956
  const agent2 = deps.createAgent({
8756
8957
  streamFn,
@@ -8780,7 +8981,7 @@ function createAgentInstance(deps, systemPrompt, tools, model, apiKey, thinkingL
8780
8981
  return agent;
8781
8982
  }
8782
8983
  function isParentHandoffRequired(task) {
8783
- return task.mode === "edit" || task.status === "timeout" || task.status === "failed" || task.confidenceSignals.some((signal) => signal.type === "tool_error" || signal.type === "no_tool_use" || signal.type === "no_text_report");
8984
+ return task.report?.availability !== "available" || task.mode === "edit" || task.status === "timeout" || task.status === "failed" || task.confidenceSignals.some((signal) => signal.type === "tool_error" || signal.type === "no_tool_use" || signal.type === "no_text_report");
8784
8985
  }
8785
8986
  function shellFromRecord(record) {
8786
8987
  return {
@@ -8794,7 +8995,12 @@ function shellFromRecord(record) {
8794
8995
  timeoutSeconds: record.timeoutSeconds,
8795
8996
  agent: null,
8796
8997
  pendingMessages: record.messages,
8797
- reports: Array.isArray(record.reports) ? record.reports.slice(-4).map((report) => parseDelegationReport(typeof report?.raw === "string" ? report.raw : void 0, typeof report?.runId === "string" ? report.runId : "legacy", report?.availability === "truncated")) : void 0,
8998
+ reports: Array.isArray(record.reports) ? record.reports.slice(-4).map((report) => {
8999
+ const parsed = parseDelegationReport(typeof report?.raw === "string" ? report.raw : void 0, typeof report?.runId === "string" ? report.runId : "legacy", report?.stopReason ?? report?.availability === "truncated");
9000
+ if (["empty", "invalid_json", "invalid_shape", "char_cap", "provider_length", "error", "aborted", "deadline", "cancelled", "no_terminal"].includes(report?.reason ?? "")) parsed.reason = report.reason;
9001
+ return parsed;
9002
+ }) : void 0,
9003
+ usageRuns: restoreSubagentUsageRuns(record.usageRuns),
8798
9004
  createdAt: record.createdAt,
8799
9005
  lastUsedAt: record.lastUsedAt,
8800
9006
  runCount: record.runCount,
@@ -8876,7 +9082,7 @@ async function persistSubagentSession(deps, session, task) {
8876
9082
  if (task.status === "completed" && task.result && transcriptChars > SESSION_TRANSCRIPT_WARN_CHARS) {
8877
9083
  task.result += `
8878
9084
 
8879
- [Session growth warning: session ${session.id} now carries ~${Math.round(transcriptChars / 4)} tokens of transcript; every recall pays for the whole history. For the next round, consider a fresh subagent with a compact brief instead of recalling.]`;
9085
+ [Session growth warning: session ${session.id} now carries approximately ${transcriptChars} JSON characters of transcript (not billing tokens); full recall carries that history, report recall excludes it. Cost is unknown. For the next round, consider a fresh subagent with a compact brief instead of recalling.]`;
8880
9086
  }
8881
9087
  if (!deps.sessionStore) return;
8882
9088
  if (transcriptChars > SESSION_RECORD_MAX_CHARS) {
@@ -8908,6 +9114,13 @@ async function runSubagentWithProgress(deps, task, reportProgress, signal, sessi
8908
9114
  let heartbeat;
8909
9115
  let session;
8910
9116
  let runStarted = false;
9117
+ let runUsage;
9118
+ const shownPhases = /* @__PURE__ */ new Set();
9119
+ const phaseProgress = (phase) => {
9120
+ if (shownPhases.has(phase) || shownPhases.size >= 16) return;
9121
+ shownPhases.add(phase);
9122
+ report({ message: `[${task.name}] Phase: ${phase}` });
9123
+ };
8911
9124
  let baseline = 0;
8912
9125
  let hardDeadlineAt;
8913
9126
  let pendingRun;
@@ -8916,7 +9129,9 @@ async function runSubagentWithProgress(deps, task, reportProgress, signal, sessi
8916
9129
  if (!runStarted || !session?.agent) return;
8917
9130
  const last = [...session.agent.state.messages.slice(baseline)].reverse().find((m) => m?.role === "assistant");
8918
9131
  const terminal = isReportTerminal(last) ? last : void 0;
8919
- task.report = parseDelegationReport(terminal ? collectLastAssistantText([terminal]) || void 0 : void 0, `${task.id}:${session.runCount + 1}`, ["length", "error", "aborted"].includes(terminal?.stopReason));
9132
+ const hooks = agentRunHooks.get(session.agent);
9133
+ const capturedText = terminal && hooks?.terminalStopReason === terminal.stopReason ? hooks?.terminalText : void 0;
9134
+ task.report = parseDelegationReport(terminal ? collectLastAssistantText([terminal]) || capturedText || void 0 : void 0, `${task.id}:${session.runCount + 1}`, terminal?.stopReason ?? (timedOut ? "deadline" : signal?.aborted ? "cancelled" : "no_terminal"));
8920
9135
  };
8921
9136
  let agentSettled = true;
8922
9137
  let releaseLease;
@@ -8959,7 +9174,12 @@ async function runSubagentWithProgress(deps, task, reportProgress, signal, sessi
8959
9174
  }
8960
9175
  const nextModelOverride = task.modelOverride ?? session.modelOverride;
8961
9176
  const nextThinking = task.thinkingLevel ?? session.thinkingLevel;
8962
- if (task.resumeMode === "report" && session.reports?.at(-1)?.availability !== "available") throw new Error("Report recall unavailable: the latest run has no valid report.");
9177
+ const latestReport = session.reports?.at(-1);
9178
+ if (task.resumeMode === "report" && latestReport?.availability !== "available") throw new Error(`Report recall unavailable: session=${session.id}, run=${latestReport?.runId ?? session.runCount}, availability=${latestReport?.availability ?? "missing"}, reason=${latestReport?.reason ?? "legacy_or_missing"}. Explicitly choose resumeMode:"full" (full history; cost unknown), or spawn a new bounded task. No older report or transcript fallback was used.`);
9179
+ const history = session.agent?.state.messages ?? session.pendingMessages ?? [];
9180
+ const historyChars = task.resumeMode === "report" ? 0 : estimateTranscriptChars(history);
9181
+ task.recallInput = { mode: task.resumeMode ?? "full", messageCount: task.resumeMode === "report" ? 0 : history.length, jsonChars: historyChars, warning: historyChars > SESSION_TRANSCRIPT_WARN_CHARS, cost: "unknown" };
9182
+ report({ message: task.resumeMode === "report" ? `[${task.name}] Report recall: old transcript is not carried into the prompt; cost unknown.` : `[${task.name}] Full recall input: ${task.recallInput.messageCount} historical messages, approximately ${task.recallInput.jsonChars} JSON characters (not billing tokens); cost unknown.${task.recallInput.warning ? " Large history: context risk; consider report recall or a new bounded task." : ""}` });
8963
9183
  const mustRebuild = task.resumeMode === "report" || session.agent === null || task.requestedMode !== void 0 || task.modelOverride !== void 0 || task.thinkingLevel !== void 0;
8964
9184
  if (!mustRebuild) {
8965
9185
  subagent = session.agent;
@@ -9063,6 +9283,14 @@ async function runSubagentWithProgress(deps, task, reportProgress, signal, sessi
9063
9283
  if (sessions) retainLiveSession(sessions, session, Math.max(1, deps.maxLiveSessions ?? DEFAULT_MAX_LIVE_SESSIONS));
9064
9284
  if (signal?.aborted || sessions?.disposed) throw new Error("Subagent capability disposed or aborted by parent");
9065
9285
  const hooks = agentRunHooks.get(subagent);
9286
+ runUsage = createSubagentUsage(session.id, `${task.id}:${session.runCount + 1}`);
9287
+ (task.usageRuns ??= []).push(runUsage);
9288
+ if (hooks) {
9289
+ hooks.usage = runUsage;
9290
+ hooks.onPhase = phaseProgress;
9291
+ hooks.terminalText = void 0;
9292
+ hooks.terminalStopReason = void 0;
9293
+ }
9066
9294
  hardDeadlineAt = Date.now() + runTimeoutSeconds * 1e3;
9067
9295
  if (hooks) {
9068
9296
  hooks.observation = task.observation;
@@ -9086,6 +9314,7 @@ async function runSubagentWithProgress(deps, task, reportProgress, signal, sessi
9086
9314
  softTimeout = setTimeout(() => {
9087
9315
  if (hooks) hooks.finalizing = true;
9088
9316
  observeDelegationPhase(task.observation, "finalizing", false);
9317
+ phaseProgress("finalizing");
9089
9318
  }, subagentSoftDeadlineMs(runTimeoutSeconds * 1e3));
9090
9319
  timeout = setTimeout(() => {
9091
9320
  timedOut = true;
@@ -9189,7 +9418,7 @@ ${DELEGATION_REPORT_INSTRUCTION}`;
9189
9418
  const terminalStopReason = lastAssistant?.stopReason;
9190
9419
  const terminalError = typeof lastAssistant?.errorMessage === "string" ? lastAssistant.errorMessage : void 0;
9191
9420
  const collectedResponse = isReportTerminal(lastAssistant) ? collectLastAssistantText([lastAssistant]) || void 0 : void 0;
9192
- task.report = parseDelegationReport(collectedResponse, `${task.id}:${(session?.runCount ?? 0) + 1}`, terminalStopReason === "length" || terminalStopReason === "error" || terminalStopReason === "aborted");
9421
+ task.report = parseDelegationReport(collectedResponse, `${task.id}:${(session?.runCount ?? 0) + 1}`, terminalStopReason ?? "no_terminal");
9193
9422
  const streamInterrupted = terminalStopReason === "error" || terminalStopReason === "aborted" || terminalError || !collectedResponse && terminalStopReason !== "stop" || terminalStopReason === "length" && !messageHasOwnText(lastAssistant);
9194
9423
  if (streamInterrupted) {
9195
9424
  if (terminalStopReason === "length") {
@@ -9238,6 +9467,7 @@ ${DELEGATION_REPORT_INSTRUCTION}`;
9238
9467
  refreshReport();
9239
9468
  task.status = timedOut ? "timeout" : signal?.aborted ? "cancelled" : "failed";
9240
9469
  observeDelegationPhase(task.observation, timedOut ? "deadline" : signal?.aborted ? "cancelled" : "failed");
9470
+ phaseProgress(task.observation.phase);
9241
9471
  task.error = errorMessage.slice(0, 2e3);
9242
9472
  task.report ??= parseDelegationReport(void 0, task.id);
9243
9473
  task.parentHandoffRequired = isParentHandoffRequired(task);
@@ -9247,14 +9477,21 @@ ${DELEGATION_REPORT_INSTRUCTION}`;
9247
9477
  if (heartbeat) {
9248
9478
  clearInterval(heartbeat);
9249
9479
  }
9480
+ if (runUsage) settleSubagentUsage(runUsage, agentSettled);
9250
9481
  const finalize = async () => {
9482
+ if (runUsage) settleSubagentUsage(runUsage, true);
9251
9483
  try {
9252
9484
  if (session) {
9253
9485
  const hooks = session.agent ? agentRunHooks.get(session.agent) : void 0;
9254
- if (hooks && hooks.observation === task.observation) hooks.observation = void 0;
9486
+ if (hooks && hooks.observation === task.observation) {
9487
+ hooks.observation = void 0;
9488
+ hooks.onPhase = void 0;
9489
+ hooks.usage = void 0;
9490
+ }
9255
9491
  if (runStarted) {
9256
9492
  refreshReport();
9257
9493
  session.runCount++;
9494
+ if (runUsage) session.usageRuns = [...session.usageRuns ?? [], runUsage].slice(-4);
9258
9495
  session.reports = [...session.reports ?? [], task.report ?? parseDelegationReport(void 0, task.id)].slice(-4);
9259
9496
  }
9260
9497
  session.lastUsedAt = Date.now();
@@ -9305,7 +9542,9 @@ function buildParallelResult(tasks, totalDuration) {
9305
9542
  if (task.details) {
9306
9543
  resultText += `, ${task.details.durationMs}ms, ${task.details.toolCalls} tools`;
9307
9544
  }
9308
- resultText += `, report=${task.report?.availability ?? "missing"})
9545
+ resultText += `, report=${task.report?.availability ?? "missing"}${task.report?.reason ? ` (${task.report.reason})` : ""})
9546
+ `;
9547
+ for (const usage of task.usageRuns ?? []) resultText += `Child usage ${usage.sessionId}/${usage.runId}: ${usage.status}, ${usage.attempts} attempts (${usage.reportedAttempts} reported, ${usage.unknownAttempts} unknown, ${usage.pendingAttempts} pending); reported token subtotal=${usage.reportedTokens.totalTokens}; estimated cost=${usage.estimatedCost ?? "unknown"}. Not added to parent ledger.
9309
9548
  `;
9310
9549
  if (task.observation) resultText += `Phase: ${task.observation.phase}; last activity: ${task.observation.lastActivity} at ${task.observation.lastWorkAt}
9311
9550
  `;
@@ -9501,7 +9740,7 @@ Default mode="read" is for investigation, search, review, or triage and cannot e
9501
9740
  thinkingLevel: Type.Optional(Type.Union([Type.Literal("off"), Type.Literal("minimal"), Type.Literal("low"), Type.Literal("medium"), Type.Literal("high"), Type.Literal("xhigh")], { description: "off|minimal|low|medium|high|xhigh. Default medium; override only when the user asks or for cheap lookups (off)." })),
9502
9741
  timeout: Type.Optional(Type.Number({ description: "Seconds. Default 1200; set higher for long tasks." })),
9503
9742
  resumeMode: Type.Optional(Type.Union([Type.Literal("full"), Type.Literal("report")], { description: "Default full retains prior transcript. Explicit report rebuilds with current tools/model/credentials and only the latest valid historical report as data plus the new task; fails if unavailable." })),
9504
- sessionId: Type.Optional(Type.String({ description: "Continue a previous subagent session by id (from an earlier result's Session line); accumulated context makes iteration cheaper. Optional mode/model/thinkingLevel reconfigure it; one task at a time; recalls never replay on failure and must target an idle session; prefer a fresh task for independent reviews." }))
9743
+ sessionId: Type.Optional(Type.String({ description: "Continue a previous subagent session by id (from an earlier result's Session line); full mode retains historical input with unknown cost. Optional mode/model/thinkingLevel reconfigure it; one task at a time; recalls never replay on failure and must target an idle session; prefer a fresh task for independent reviews." }))
9505
9744
  }
9506
9745
  // No additionalProperties lock: legacy callers may send task aliases
9507
9746
  // (description/prompt/objective); normalizeTaskDefs resolves them at
@@ -9631,6 +9870,8 @@ ${SPAWN_USAGE_HINT}` }],
9631
9870
  reportedEvidenceCount: task.reportedEvidence.length,
9632
9871
  confidenceSignalCount: task.confidenceSignals.length,
9633
9872
  report: task.report,
9873
+ recallInput: task.recallInput,
9874
+ usageRuns: task.usageRuns?.map((usage) => ({ ...usage, reportedTokens: { ...usage.reportedTokens } })),
9634
9875
  observation: task.observation,
9635
9876
  parentHandoffRequired: task.parentHandoffRequired,
9636
9877
  failureReason: task.error,
@@ -10978,7 +11219,7 @@ function createReadTool(cwd, options) {
10978
11219
  label: "read",
10979
11220
  description: `Read a file's contents. Use this before editing, and use offset/limit for large files, or tail=N to read the last N lines when the line count is unknown. Images are attached for the current model when it supports vision; otherwise a configured file-understanding service analyzes them. Video, audio, and documents require a configured understanding service. Pass question to specify what to focus on; it does not force a separate model request. Example: read_file path='Scripts/PlayerController.cs' or read_file path='Data/config.json'. ${pathResolutionDescription} For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). When you need the full file, continue with a larger offset until complete.`,
10980
11221
  parameters: readSchema,
10981
- execute: async (_toolCallId, params, signal) => {
11222
+ execute: async (_toolCallId, params, signal, _onUpdate) => {
10982
11223
  const { path: path3, root, question } = params;
10983
11224
  const tail = params.tail;
10984
11225
  let offset = params.offset;
@@ -11005,7 +11246,7 @@ function createReadTool(cwd, options) {
11005
11246
  const message = error instanceof Error ? error.message : String(error);
11006
11247
  if (/outside sandbox|outside root|escapes sandbox/i.test(message)) {
11007
11248
  return createReadErrorResult(
11008
- `${message}. read_file is workspace-scoped; for intentional runtime logs or temporary files outside the workspace, use bash with a bounded command such as head, tail, sed -n, or cat on that path.`
11249
+ `${message}. read_file is workspace-scoped; for intentional runtime logs or temporary files outside the workspace, use the shell tool with a bounded command such as head, tail, sed -n, or cat on that path.`
11009
11250
  );
11010
11251
  }
11011
11252
  return createReadErrorResult(message);
@@ -16914,7 +17155,7 @@ function unavailableResult(query, requestedMode, reason) {
16914
17155
  text: [
16915
17156
  `Web search is unavailable for "${query}".`,
16916
17157
  reason,
16917
- "Do not use bash, curl, Python, or DuckDuckGo scraping as a fallback; report that web_search is unavailable or ask the user to configure a provider that supports native web search."
17158
+ "Do not use shell, curl, Python, or DuckDuckGo scraping as a fallback; report that web_search is unavailable or ask the user to configure a provider that supports native web search."
16918
17159
  ].join("\n")
16919
17160
  }],
16920
17161
  details: {
@@ -17422,7 +17663,7 @@ function createSharedWebSearchTool(deps) {
17422
17663
  return {
17423
17664
  name: "web_search",
17424
17665
  label: "web_search",
17425
- description: "Search the public web using a provider-native web search capability. Use for current information, online research, or documentation outside the local workspace. Results include sources. Do not use bash/curl/python as a web-search fallback. Only set domain filters when the user explicitly asks to limit sources.",
17666
+ description: "Search the public web using a provider-native web search capability. Use for current information, online research, or documentation outside the local workspace. Results include sources. Do not use shell/curl/python as a web-search fallback. Only set domain filters when the user explicitly asks to limit sources.",
17426
17667
  parameters: webSearchSchema,
17427
17668
  execute: async (_toolCallId, rawParams, signal) => {
17428
17669
  const input = rawParams;
@@ -18013,7 +18254,7 @@ function createSharedWebFetchTool(deps = {}) {
18013
18254
  return {
18014
18255
  name: "web_fetch",
18015
18256
  label: "web_fetch",
18016
- description: "Fetch and read a specific public web URL. Use web_search to discover sources, then web_fetch to inspect a chosen URL. Do not use bash/curl/Python as a fallback for web page fetching.",
18257
+ description: "Fetch and read a specific public web URL. Use web_search to discover sources, then web_fetch to inspect a chosen URL. Do not use shell/curl/Python as a fallback for web page fetching.",
18017
18258
  parameters: webFetchSchema,
18018
18259
  capabilityMetadata: {
18019
18260
  riskClass: "network",
@@ -18637,7 +18878,7 @@ function createSharedWebResearchTool(deps) {
18637
18878
  return {
18638
18879
  name: "web_research",
18639
18880
  label: "web_research",
18640
- description: "Canonical web tool. For normal research, pass query. To read a known page, pass url. For low-level search results only, pass query with mode='search'. Use for current information, official docs, news, releases, issues, and webpage summaries. Do not use bash/curl/Python as a web fallback.",
18881
+ description: "Canonical web tool. For normal research, pass query. To read a known page, pass url. For low-level search results only, pass query with mode='search'. Use for current information, official docs, news, releases, issues, and webpage summaries. Do not use shell/curl/Python as a web fallback.",
18641
18882
  parameters: webResearchSchema,
18642
18883
  capabilityMetadata: {
18643
18884
  riskClass: "network",
@@ -19208,6 +19449,8 @@ export {
19208
19449
  findFirstKeptEntrySummaryId,
19209
19450
  createCompactionSummary,
19210
19451
  generateEntryId,
19452
+ createHostEnvelopeProjectorRegistry,
19453
+ projectHostEnvelopes,
19211
19454
  FileSessionStore,
19212
19455
  createSessionManager,
19213
19456
  prepareSessionCompaction,
@@ -19248,6 +19491,7 @@ export {
19248
19491
  supersedeExecutionState,
19249
19492
  shouldPreserveExecutionStateForUserText,
19250
19493
  BUILTIN_TOOL_CAPABILITY_METADATA,
19494
+ getToolCapabilityMetadata,
19251
19495
  isToolAllowedInPlanMode,
19252
19496
  extractPlanTodoItems,
19253
19497
  isPlanModeSafeCommand,