@cydm/pie 2.7.7 → 2.7.9

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-W5B3OXHQ.js";
5
+ } from "./chunk-5SP2H5QK.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-2FEDB5GR.js";
15
+ } from "./chunk-SMGBZQI5.js";
16
16
  import {
17
17
  Type
18
18
  } from "./chunk-XXGYUYXI.js";
@@ -899,6 +899,11 @@ 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
+
902
907
  // ../../packages/shared-headless-capabilities/src/tool-policy.ts
903
908
  var BUILTIN_TOOL_CAPABILITY_METADATA = {
904
909
  search_context: { riskClass: "read_only", concurrencySafe: true, permissionScope: "session", availableInPlanMode: true, allowedForSubagentByDefault: false, maxOutputChars: 2e4 },
@@ -971,13 +976,51 @@ function getSubagentToolPolicyDecision(tool) {
971
976
  }
972
977
  var DEFAULT_PLAN_MODE_TOOL_NAMES = Object.entries(BUILTIN_TOOL_CAPABILITY_METADATA).filter(([, metadata]) => metadata.availableInPlanMode).map(([name]) => name);
973
978
 
974
- // ../../packages/shared-headless-capabilities/src/capability.ts
975
- function defineSharedCapability(definition) {
976
- return definition;
979
+ // ../../packages/shared-headless-capabilities/src/plan.ts
980
+ var import_shell_quote = __toESM(require_shell_quote(), 1);
981
+
982
+ // ../../packages/shared-headless-capabilities/src/shell-resolution.ts
983
+ function defaultShellKindForPlatform(platform = process.platform) {
984
+ return platform === "win32" ? "powershell" : "bash";
985
+ }
986
+ function resolveRequestShellKind(shell, platform = process.platform) {
987
+ if (shell === "bash" || shell === "powershell" || shell === "cmd") return shell;
988
+ return defaultShellKindForPlatform(platform);
977
989
  }
978
990
 
979
991
  // ../../packages/shared-headless-capabilities/src/plan.ts
980
- var import_shell_quote = __toESM(require_shell_quote(), 1);
992
+ var PLAN_POWERSHELL_READ_COMMANDS = new Set([
993
+ "Get-Content",
994
+ "Get-ChildItem",
995
+ "Get-Item",
996
+ "Get-Location",
997
+ "Get-Date",
998
+ "Get-Command",
999
+ "Select-String",
1000
+ "Measure-Object",
1001
+ "Compare-Object",
1002
+ "Test-Path"
1003
+ ].map((name) => name.toLowerCase()));
1004
+ var PLAN_POWERSHELL_READ_ALIASES = /* @__PURE__ */ new Set(["cat", "ls", "pwd", "echo", "sort"]);
1005
+ var PLAN_POWERSHELL_SAFE_PARAMETERS = /^(?:-(?:Raw|TotalCount|Head|Tail|First|Last|Wait|LiteralPath|Path|Name|Pattern|Recurse|Filter|Include|Exclude|SimpleMatch|CaseSensitive|Quiet|Word|Line|Character|Encoding|ErrorAction|WarningAction))$/i;
1006
+ var PLAN_POWERSHELL_SEPARATORS = /[;\n\r\t]|[<>]{1,2}|\||&&|\|\|/;
1007
+ var PLAN_POWERSHELL_NON_ASCII = /[^\x20-\x7E]/;
1008
+ var PLAN_POWERSHELL_UNSAFE_TOKENS = /\$\(|`|%\{|[(){}\[\]]|\bsource\b|\.\s+[/~]|\b(bash|sh|zsh|pwsh|powershell|cmd|npm|npx|node|python3?|git)\b/i;
1009
+ function isPlanModeSafePowerShellCommand(command) {
1010
+ const trimmed = command.trim();
1011
+ if (!trimmed) return false;
1012
+ if (PLAN_POWERSHELL_NON_ASCII.test(command)) return false;
1013
+ if (PLAN_POWERSHELL_SEPARATORS.test(trimmed)) return false;
1014
+ if (PLAN_POWERSHELL_UNSAFE_TOKENS.test(trimmed)) return false;
1015
+ const tokens = trimmed.split(/\s+/).filter(Boolean);
1016
+ const head = tokens[0].toLowerCase();
1017
+ if (!head) return false;
1018
+ if (!PLAN_POWERSHELL_READ_COMMANDS.has(head) && !PLAN_POWERSHELL_READ_ALIASES.has(head)) return false;
1019
+ for (const token of tokens.slice(1)) {
1020
+ if (token.startsWith("-") && !PLAN_POWERSHELL_SAFE_PARAMETERS.test(token)) return false;
1021
+ }
1022
+ return true;
1023
+ }
981
1024
  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"]);
982
1025
  var GIT_READ_SUBCOMMANDS = /* @__PURE__ */ new Set(["status", "log", "diff", "show", "rev-parse", "ls-files", "ls-tree", "blame", "describe", "shortlog", "merge-base"]);
983
1026
  var UNSAFE_GIT_FLAGS = /^(?:-c|--config-env|--exec|--output|--ext-diff|--textconv)(?:=|$)/;
@@ -1095,7 +1138,21 @@ function isReadOnlySegment(words) {
1095
1138
  }
1096
1139
  if (program === "uniq") return isReadOnlyUniq(args);
1097
1140
  if (program === "date") {
1098
- return args.every((arg) => arg.startsWith("-") || arg.startsWith("+"));
1141
+ const safeFlags = /* @__PURE__ */ new Set(["-u", "--utc", "--universal", "--help", "--version", "--debug"]);
1142
+ const valueFlags = /* @__PURE__ */ new Set(["-d", "--date", "-f", "--file", "-r", "--reference"]);
1143
+ const valueLongFlags = /^(?:--date|--file|--reference|--iso-8601|--rfc-email|--rfc-2822|--rfc-3339)=/;
1144
+ for (let index = 0; index < args.length; index += 1) {
1145
+ const arg = args[index];
1146
+ if (arg.startsWith("+")) continue;
1147
+ if (safeFlags.has(arg) || valueLongFlags.test(arg)) continue;
1148
+ if (valueFlags.has(arg)) {
1149
+ if (index + 1 >= args.length) return false;
1150
+ index += 1;
1151
+ continue;
1152
+ }
1153
+ return false;
1154
+ }
1155
+ return true;
1099
1156
  }
1100
1157
  if (program === "jq") {
1101
1158
  return true;
@@ -1154,7 +1211,9 @@ ${inlineMatch[1]}`;
1154
1211
  }
1155
1212
  return items;
1156
1213
  }
1157
- function isPlanModeSafeCommand(command) {
1214
+ function isPlanModeSafeCommand(command, shell = "bash") {
1215
+ if (shell === "powershell") return isPlanModeSafePowerShellCommand(command);
1216
+ if (shell === "cmd") return false;
1158
1217
  const trimmed = command.trim();
1159
1218
  if (!trimmed || /[$`\n\r\0]/.test(trimmed)) return false;
1160
1219
  if (/\{[^{}]*\}/.test(trimmed) || /\{\s*,/.test(trimmed)) return false;
@@ -1994,36 +2053,47 @@ function projectHostContextBlock(block, registry) {
1994
2053
  };
1995
2054
  }
1996
2055
  }
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;
2056
+ function projectionSeparator(projectionText) {
2057
+ return projectionText.endsWith("\n") && !projectionText.endsWith("\n\n") ? "" : "\n\n";
2004
2058
  }
2005
2059
  function projectHostEnvelopes(messages, registry, onProjection) {
2006
2060
  let changed = false;
2007
2061
  const next = [];
2008
2062
  for (const message of messages) {
2009
- const block = leadingHostContextBlock(message);
2010
- if (!block) {
2063
+ if (message.role !== "user") {
2011
2064
  next.push(message);
2012
2065
  continue;
2013
2066
  }
2014
- const outcome = projectHostContextBlock(block, registry);
2015
- onProjection?.(outcome);
2016
2067
  const content = message.content;
2017
- const rest = content.slice(1);
2068
+ if (!Array.isArray(content)) {
2069
+ next.push(message);
2070
+ continue;
2071
+ }
2072
+ let blockCount = 0;
2073
+ while (blockCount < content.length && isHostContextBlock(content[blockCount])) blockCount += 1;
2074
+ if (blockCount === 0) {
2075
+ next.push(message);
2076
+ continue;
2077
+ }
2078
+ const outcomes = [];
2079
+ for (let i = 0; i < blockCount; i++) {
2080
+ const outcome = projectHostContextBlock(content[i], registry);
2081
+ outcomes.push(outcome);
2082
+ onProjection?.(outcome);
2083
+ }
2084
+ const rest = content.slice(blockCount);
2085
+ let text = outcomes[outcomes.length - 1].text;
2086
+ for (let i = outcomes.length - 2; i >= 0; i--) {
2087
+ text = `${outcomes[i].text}${projectionSeparator(outcomes[i].text)}${text}`;
2088
+ }
2089
+ const separator = projectionSeparator(outcomes[outcomes.length - 1].text);
2018
2090
  const first = rest[0];
2019
2091
  let projectedContent;
2020
2092
  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;
2093
+ const merged = first.text.trim().length > 0 ? `${text}${separator}${first.text}` : text;
2024
2094
  projectedContent = [{ ...first, text: merged }, ...rest.slice(1)];
2025
2095
  } else {
2026
- projectedContent = [{ type: "text", text: outcome.text }, ...rest];
2096
+ projectedContent = [{ type: "text", text }, ...rest];
2027
2097
  }
2028
2098
  next.push({ ...message, content: projectedContent });
2029
2099
  changed = true;
@@ -8158,7 +8228,10 @@ function withSubagentRunContext(stream, hooks) {
8158
8228
  ${SUBAGENT_FINALIZATION_HINT}` } : context, options);
8159
8229
  };
8160
8230
  }
8161
- function isRetryableSubagentFailure(message) {
8231
+ function isRetryableSubagentFailureFrom(facts) {
8232
+ if (facts.retryable === false) return false;
8233
+ if (facts.replayClass === "stream_lost" || facts.replayClass === "stream_lost_partial") return true;
8234
+ const message = facts.errorMessage ?? "";
8162
8235
  const remoteClosure = /SSE stream (?:aborted|closed before end|ended before response completed)/i.test(message);
8163
8236
  const classificationText = message.replace(/econnaborted/ig, "").replace(/SSE stream aborted/ig, "remote stream closed");
8164
8237
  if (/\b(?:400|401|403|404|422|429)\b|quota|weekly|monthly|limit reached|exhausted|unauthorized|authentication|invalid|protocol|stopReason=length|token limit|abort/i.test(classificationText)) return false;
@@ -8176,6 +8249,13 @@ function cancellableBackoff(ms, signal) {
8176
8249
  if (signal?.aborted) finish();
8177
8250
  });
8178
8251
  }
8252
+ function factsOf(error) {
8253
+ if (error instanceof Error) {
8254
+ const carrier = error;
8255
+ return { errorMessage: error.message, retryable: carrier.retryable, replayClass: carrier.replayClass };
8256
+ }
8257
+ return { errorMessage: String(error) };
8258
+ }
8179
8259
  function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivity, hooks) {
8180
8260
  return async (model, context, options) => {
8181
8261
  for (let attempt = 0; ; attempt++) {
@@ -8223,7 +8303,7 @@ function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivi
8223
8303
  message = terminal;
8224
8304
  } catch (error) {
8225
8305
  finishUsage?.();
8226
- if (!options?.signal?.aborted && attempt < 2 && isRetryableSubagentFailure(error instanceof Error ? error.message : String(error))) {
8306
+ if (!options?.signal?.aborted && attempt < 2 && isRetryableSubagentFailureFrom(factsOf(error))) {
8227
8307
  try {
8228
8308
  onRetry?.();
8229
8309
  } catch {
@@ -8234,7 +8314,7 @@ function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivi
8234
8314
  throw error;
8235
8315
  }
8236
8316
  if (options?.signal?.aborted) throw new Error("Subagent aborted by parent");
8237
- if (message.stopReason === "error" && attempt < 2 && isRetryableSubagentFailure(message.errorMessage ?? "")) {
8317
+ if (message.stopReason === "error" && attempt < 2 && isRetryableSubagentFailureFrom({ errorMessage: message.errorMessage, retryable: message.retryable, replayClass: message.replayClass })) {
8238
8318
  try {
8239
8319
  onRetry?.();
8240
8320
  } catch {
@@ -8383,8 +8463,16 @@ var PROTECTED_POSIX_PATHS = /* @__PURE__ */ new Set([
8383
8463
  "/Volumes"
8384
8464
  ]);
8385
8465
  var POWERSHELL_DESTRUCTIVE_PATTERNS = [
8386
- { pattern: /\bRemove-Item\b[^;\n|]*(?:-Recurse|-Force)/i, warning: "may recursively or forcefully remove files", commandClass: "filesystem_write" },
8387
- { pattern: /\bRemove-Item\b[^;\n|]*(?:HKLM:|HKCU:|Registry::)/i, warning: "may remove registry entries", commandClass: "infra_destructive" },
8466
+ // PowerShell resolves these aliases before execution. The classifier must
8467
+ // treat the aliases exactly like their canonical cmdlets; otherwise a
8468
+ // guarded shell can run `ri -Recurse -Force` without the confirmation that
8469
+ // `Remove-Item -Recurse -Force` receives.
8470
+ { pattern: /\b(?:Remove-Item|ri|rm|rmdir|del|erase|rd)\b[^;\n|]*(?:-Recurse|-Force)\b/i, warning: "may recursively or forcefully remove files", commandClass: "filesystem_write" },
8471
+ { pattern: /\b(?:Remove-Item|ri|rm|rmdir|del|erase|rd)\b[^;\n|]*(?:HKLM:|HKCU:|Registry::)/i, warning: "may remove registry entries", commandClass: "infra_destructive" },
8472
+ // These write aliases are not all destructive in the recursive-delete sense,
8473
+ // but they can overwrite or create arbitrary files. Keep them behind the
8474
+ // same explicit shell confirmation boundary as other PowerShell writes.
8475
+ { pattern: /\b(?:Set-Content|sc|set|Add-Content|ac|add|New-Item|ni|md|mkdir|Copy-Item|cpi|cp|copy|Move-Item|mi|mv|move|Rename-Item|rni|ren)\b/i, warning: "may write or overwrite files", commandClass: "filesystem_write" },
8388
8476
  { pattern: /\b(Set-ExecutionPolicy|Start-Process\b[^;\n|]*-Verb\s+RunAs)\b/i, warning: "may change execution policy or request elevation", commandClass: "privilege_escalation" },
8389
8477
  { pattern: /\b(Stop-Computer|Restart-Computer)\b/i, warning: "may stop or restart the machine", commandClass: "infra_destructive" },
8390
8478
  { pattern: /(^|[;|&\n])\s*(?:Invoke-Expression|iex)\b/i, warning: "evaluates a dynamically constructed string as code", commandClass: "privilege_escalation" },
@@ -8409,9 +8497,6 @@ function lastPipelineCommand(command) {
8409
8497
  const segments = command.split("|").map((part) => part.trim()).filter(Boolean);
8410
8498
  return segments[segments.length - 1] || command;
8411
8499
  }
8412
- function classifyBashCommand(command, options) {
8413
- return classifyShellCommand(command, "bash", options);
8414
- }
8415
8500
  function splitShellSegments(command) {
8416
8501
  const segments = [];
8417
8502
  let current = "";
@@ -8593,6 +8678,66 @@ function resolveStaticCwdTarget(target, cwd, root) {
8593
8678
  if (resolved !== normalizedRoot && !isStrictDescendant(resolved, normalizedRoot)) return void 0;
8594
8679
  return resolved;
8595
8680
  }
8681
+ var DYNAMIC_EXECUTION_WARNING = "may execute nested shell or interpreter code";
8682
+ function executableName(word) {
8683
+ const normalized = word.replace(/\\/g, "/").toLowerCase();
8684
+ const base = normalized.slice(normalized.lastIndexOf("/") + 1);
8685
+ return base.endsWith(".exe") ? base.slice(0, -4) : base;
8686
+ }
8687
+ function hasDynamicExecutionInvocation(words) {
8688
+ if (words.length === 0) return false;
8689
+ const program = executableName(words[0]);
8690
+ if (program === "env" || program === "command" || program === "exec") {
8691
+ let index = 1;
8692
+ while (index < words.length && (words[index].startsWith("-") || /^[A-Za-z_][A-Za-z0-9_]*=/.test(words[index]))) index += 1;
8693
+ return index < words.length && hasDynamicExecutionInvocation(words.slice(index));
8694
+ }
8695
+ if (["bash", "sh", "zsh", "dash", "ksh", "fish"].includes(program)) {
8696
+ return words.slice(1).some((word) => /^-[^-]*c$/i.test(word) || /^--command(?:=|$)/i.test(word));
8697
+ }
8698
+ if (["pwsh", "powershell"].includes(program)) {
8699
+ return words.slice(1).some((word) => /^-(?:c|command|encodedcommand|e|file)(?:=|$)/i.test(word));
8700
+ }
8701
+ if (program === "cmd") {
8702
+ return words.slice(1).some((word) => /^\/(?:c|k)$/i.test(word));
8703
+ }
8704
+ if (["python", "python3", "python2", "perl", "ruby"].includes(program)) {
8705
+ return words.slice(1).some((word) => /^(?:-c|-e|-m)$/i.test(word));
8706
+ }
8707
+ if (program === "node") {
8708
+ return words.slice(1).some((word) => /^(?:-e|--eval|-p|--print|-r|--require)$/i.test(word));
8709
+ }
8710
+ if (program === "find") {
8711
+ return words.slice(1).some((word) => /^-(?:exec|execdir|ok|okdir)$/.test(word));
8712
+ }
8713
+ if (program === "rg") {
8714
+ return words.slice(1).some((word) => /^--pre(?:=|$)|^--pre-glob(?:=|$)/.test(word));
8715
+ }
8716
+ if (program === "xargs") {
8717
+ const optionsWithValues = /* @__PURE__ */ new Set(["-a", "-d", "-E", "-I", "-L", "-n", "-P", "-s"]);
8718
+ let index = 1;
8719
+ while (index < words.length && words[index].startsWith("-")) {
8720
+ if (optionsWithValues.has(words[index])) index += 1;
8721
+ index += 1;
8722
+ }
8723
+ return index < words.length && hasDynamicExecutionInvocation(words.slice(index));
8724
+ }
8725
+ return false;
8726
+ }
8727
+ function dynamicExecutionWarnings(command) {
8728
+ const warnings = [];
8729
+ for (const segment of splitShellSegments(command)) {
8730
+ const words = splitShellWords(segment);
8731
+ if (words) {
8732
+ if (hasDynamicExecutionInvocation(words)) warnings.push(DYNAMIC_EXECUTION_WARNING);
8733
+ continue;
8734
+ }
8735
+ if (/^(?:[A-Za-z]:[\\/]|[./])?(?:bash|sh|zsh|dash|ksh|fish|pwsh|powershell|cmd|python\d*|node|perl|ruby)\b[^|;&\n]*(?:\s-c(?:\s|$)|\s--?(?:command|encodedcommand|eval|execute|file)(?:[=\s]|$)|\s\/(?:c|k)(?:\s|$))/i.test(segment.trim())) {
8736
+ warnings.push(DYNAMIC_EXECUTION_WARNING);
8737
+ }
8738
+ }
8739
+ return [...new Set(warnings)];
8740
+ }
8596
8741
  function isStaticallyScopedRecursiveForceRemove(command, options) {
8597
8742
  if (!options?.cwd) return false;
8598
8743
  const root = options.sandboxRoot || options.cwd;
@@ -8634,6 +8779,12 @@ function classifyShellCommand(command, shell = "bash", options) {
8634
8779
  destructive = true;
8635
8780
  }
8636
8781
  }
8782
+ const dynamicWarnings = dynamicExecutionWarnings(command);
8783
+ if (dynamicWarnings.length > 0) {
8784
+ warnings.push(...dynamicWarnings);
8785
+ commandClass = "unknown_shell";
8786
+ destructive = true;
8787
+ }
8637
8788
  for (const entry of destructivePatterns) {
8638
8789
  if (entry.pattern.test(command)) {
8639
8790
  warnings.push(entry.warning);
@@ -8809,7 +8960,8 @@ function wrapBashGuard(tool, plan) {
8809
8960
  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.`,
8810
8961
  execute: async (toolCallId, params, signal, onUpdate) => {
8811
8962
  const command = typeof params?.command === "string" ? params.command : "";
8812
- const classification = classifyBashCommand(command);
8963
+ const shell = resolveRequestShellKind(params?.shell);
8964
+ const classification = classifyShellCommand(command, shell);
8813
8965
  if (classification.destructive) {
8814
8966
  const reason = `destructive shell command blocked for subagent: ${classification.riskWarnings.join("; ")}`;
8815
8967
  plan.toolsExcluded.push({ name: tool.name, reason });
@@ -9425,7 +9577,11 @@ ${DELEGATION_REPORT_INSTRUCTION}`;
9425
9577
  throw new Error(`Subagent output token limit reached (stopReason=length): the provider cut the final response before the report was produced${messages.some(messageHasOwnText) ? " (earlier-turn narration discarded)" : ", no text response"}. The output/thinking budget was too small for this task at its reasoning level. Rerun with a narrower task or a lower thinkingLevel.`);
9426
9578
  }
9427
9579
  const detail = terminalError ? `: ${terminalError.slice(0, 200)}` : "";
9428
- throw new Error(`Subagent response failed (provider/transport failure before the report was produced, stopReason=${terminalStopReason ?? "none"}${collectedResponse ? ", partial transcript discarded" : ", no text response"})${detail}`);
9580
+ const failure = new Error(`Subagent response failed (provider/transport failure before the report was produced, stopReason=${terminalStopReason ?? "none"}${collectedResponse ? ", partial transcript discarded" : ", no text response"})${detail}`);
9581
+ const carrier = failure;
9582
+ carrier.retryable = lastAssistant?.retryable;
9583
+ carrier.replayClass = lastAssistant?.replayClass;
9584
+ throw failure;
9429
9585
  }
9430
9586
  let response;
9431
9587
  if (!collectedResponse) {
@@ -9443,6 +9599,7 @@ ${DELEGATION_REPORT_INSTRUCTION}`;
9443
9599
  }
9444
9600
  const durationMs = Date.now() - startTime;
9445
9601
  task.status = "completed";
9602
+ task.failure = void 0;
9446
9603
  observeDelegationPhase(task.observation, "completed");
9447
9604
  task.result = response;
9448
9605
  task.details = {
@@ -9469,6 +9626,12 @@ ${DELEGATION_REPORT_INSTRUCTION}`;
9469
9626
  observeDelegationPhase(task.observation, timedOut ? "deadline" : signal?.aborted ? "cancelled" : "failed");
9470
9627
  phaseProgress(task.observation.phase);
9471
9628
  task.error = errorMessage.slice(0, 2e3);
9629
+ const failureCarrier = error;
9630
+ if (failureCarrier && (failureCarrier.retryable !== void 0 || failureCarrier.replayClass !== void 0)) {
9631
+ task.failure = { ...failureCarrier.retryable !== void 0 ? { retryable: failureCarrier.retryable } : {}, ...failureCarrier.replayClass !== void 0 ? { replayClass: failureCarrier.replayClass } : {} };
9632
+ } else {
9633
+ task.failure = void 0;
9634
+ }
9472
9635
  task.report ??= parseDelegationReport(void 0, task.id);
9473
9636
  task.parentHandoffRequired = isParentHandoffRequired(task);
9474
9637
  task.completedAt = Date.now();
@@ -9805,7 +9968,7 @@ ${SPAWN_USAGE_HINT}` }],
9805
9968
  await run(task, toolContext.reportProgress, signal);
9806
9969
  if (task.wasRecall || task.mode !== "read" || task.status !== "failed" || !task.error) return;
9807
9970
  if (signal?.aborted) return;
9808
- if (!isRetryableSubagentFailure(task.error)) return;
9971
+ if (!isRetryableSubagentFailureFrom({ errorMessage: task.error, retryable: task.failure?.retryable, replayClass: task.failure?.replayClass })) return;
9809
9972
  observeDelegationPhase(task.observation, "task_replay");
9810
9973
  const settleMs = Math.max(0, (deps.transientRetryBackoffMs ?? 2e3) * 10);
9811
9974
  toolContext.reportProgress?.({ message: `[${task.name}] Provider/transport failure \u2014 replaying the read task once after ${Math.round(settleMs / 1e3)}s`, increment: 0 });
@@ -9813,6 +9976,7 @@ ${SPAWN_USAGE_HINT}` }],
9813
9976
  if (signal.aborted) {
9814
9977
  task.status = "cancelled";
9815
9978
  task.error = "Subagent cancelled during replay backoff";
9979
+ task.failure = void 0;
9816
9980
  observeDelegationPhase(task.observation, "cancelled");
9817
9981
  task.completedAt = Date.now();
9818
9982
  return;
@@ -9822,6 +9986,7 @@ ${SPAWN_USAGE_HINT}` }],
9822
9986
  task.runtimeEvidence = [];
9823
9987
  task.reportedEvidence = [];
9824
9988
  task.error = void 0;
9989
+ task.failure = void 0;
9825
9990
  task.report = void 0;
9826
9991
  task.result = void 0;
9827
9992
  task.details = void 0;
@@ -9875,6 +10040,10 @@ ${SPAWN_USAGE_HINT}` }],
9875
10040
  observation: task.observation,
9876
10041
  parentHandoffRequired: task.parentHandoffRequired,
9877
10042
  failureReason: task.error,
10043
+ // Structured provider verdict so the parent model can distinguish
10044
+ // stream loss (safe to re-dispatch; no tool calls executed) from hard
10045
+ // provider vetoes (auth/quota/config — not recoverable here).
10046
+ failure: task.failure,
9878
10047
  details: task.details
9879
10048
  })),
9880
10049
  totalDuration,
@@ -10532,6 +10701,164 @@ function createAskUserCapability() {
10532
10701
  };
10533
10702
  }
10534
10703
 
10704
+ // ../../packages/shared-headless-capabilities/src/policy-enforcer.ts
10705
+ var POLICY_ENFORCED = /* @__PURE__ */ Symbol.for("pie.capabilityPolicy.enforced");
10706
+ function evaluateToolPermission(tool, context = {}) {
10707
+ const mode = context.mode ?? "normal";
10708
+ const metadata = getToolCapabilityMetadata(tool);
10709
+ const base = describeToolPolicyDecision(tool, mode);
10710
+ if (!metadata && mode === "normal") {
10711
+ return {
10712
+ disposition: "allow",
10713
+ allowed: true,
10714
+ reason: "unclassified_tool",
10715
+ policyReason: "unclassified_tool",
10716
+ mode,
10717
+ yoloMode: context.yoloMode
10718
+ };
10719
+ }
10720
+ if (!metadata || !base.allowed) {
10721
+ return {
10722
+ disposition: "deny",
10723
+ allowed: false,
10724
+ reason: base.reason ?? "tool not allowed by runtime policy",
10725
+ policyReason: base.reason ?? "tool not allowed by runtime policy",
10726
+ mode,
10727
+ yoloMode: context.yoloMode,
10728
+ riskClass: metadata?.riskClass,
10729
+ permissionScope: metadata?.permissionScope,
10730
+ requiresPermission: metadata?.requiresPermission
10731
+ };
10732
+ }
10733
+ const requiresConfirmation = Boolean(metadata.highRisk || metadata.requiresPermission);
10734
+ if (!requiresConfirmation || context.yoloMode || context.confirmationPolicy === "allow") {
10735
+ return {
10736
+ disposition: "allow",
10737
+ allowed: true,
10738
+ mode,
10739
+ yoloMode: context.yoloMode,
10740
+ riskClass: metadata.riskClass,
10741
+ permissionScope: metadata.permissionScope,
10742
+ requiresPermission: requiresConfirmation
10743
+ };
10744
+ }
10745
+ if (context.confirmationPolicy === "deny" || mode === "subagent") {
10746
+ const reason = mode === "subagent" ? "subagent cannot request interactive permission" : "permission confirmation is unavailable";
10747
+ return {
10748
+ disposition: "deny",
10749
+ allowed: false,
10750
+ reason,
10751
+ policyReason: reason,
10752
+ mode,
10753
+ yoloMode: context.yoloMode,
10754
+ riskClass: metadata.riskClass,
10755
+ permissionScope: metadata.permissionScope,
10756
+ requiresPermission: true
10757
+ };
10758
+ }
10759
+ return {
10760
+ disposition: "confirm",
10761
+ allowed: false,
10762
+ reason: "permission confirmation required",
10763
+ policyReason: "permission confirmation required",
10764
+ mode,
10765
+ yoloMode: context.yoloMode,
10766
+ riskClass: metadata.riskClass,
10767
+ permissionScope: metadata.permissionScope,
10768
+ requiresPermission: true
10769
+ };
10770
+ }
10771
+ function blockedResult(toolName, decision) {
10772
+ return {
10773
+ content: [
10774
+ {
10775
+ type: "text",
10776
+ text: `Blocked ${toolName}: ${decision.reason ?? decision.policyReason ?? "permission denied"}`
10777
+ }
10778
+ ],
10779
+ details: { permissionDecision: decision },
10780
+ isError: true
10781
+ };
10782
+ }
10783
+ function planModeShellCommandBlocked(target, args) {
10784
+ const metadata = getToolCapabilityMetadata(target);
10785
+ if (!metadata?.executesShell) return void 0;
10786
+ const command = args?.command;
10787
+ if (typeof command !== "string") {
10788
+ return `Plan mode: shell tool received no command to validate. Use plan_scratch for temporary drafts, submit the finished plan with plan_submit, or toggle Plan mode off (/tools/plan) to execute.`;
10789
+ }
10790
+ const shell = resolveRequestShellKind(args?.shell);
10791
+ if (isPlanModeSafeCommand(command, shell)) return void 0;
10792
+ return `Plan mode: command "${command}" is not a read-only ${shell} command. Use plan_scratch for temporary drafts, submit the finished plan with plan_submit, or toggle Plan mode off (/tools/plan) to execute.`;
10793
+ }
10794
+ function mergePermissionDetails(details, decision) {
10795
+ if (details && typeof details === "object" && !Array.isArray(details)) {
10796
+ const record = details;
10797
+ if (record.permissionDecision) {
10798
+ return { ...record, capabilityPolicyDecision: decision };
10799
+ }
10800
+ return { ...record, permissionDecision: decision };
10801
+ }
10802
+ return { permissionDecision: decision };
10803
+ }
10804
+ function createPolicyEnforcedTool(tool, options = {}) {
10805
+ const marker = tool[POLICY_ENFORCED];
10806
+ const intactWrapper = !!marker && marker.wrapperExecute === tool.execute;
10807
+ if (intactWrapper && marker.getContext === options.getContext && marker.permissionProvider === options.permissionProvider) {
10808
+ return tool;
10809
+ }
10810
+ const target = intactWrapper ? marker.base : tool;
10811
+ const wrapped = {
10812
+ ...target,
10813
+ execute: async (toolCallId, args, signal, onUpdate) => {
10814
+ let decision = evaluateToolPermission(target, options.getContext?.());
10815
+ if (decision.disposition === "confirm") {
10816
+ const approved = await options.permissionProvider?.confirm({ toolName: target.name, args, decision }).catch(() => false);
10817
+ if (!approved) {
10818
+ return blockedResult(target.name, {
10819
+ ...decision,
10820
+ disposition: "deny",
10821
+ reason: "permission denied by user",
10822
+ policyReason: "permission denied by user"
10823
+ });
10824
+ }
10825
+ decision = {
10826
+ ...decision,
10827
+ disposition: "allow",
10828
+ allowed: true,
10829
+ reason: void 0,
10830
+ policyReason: void 0
10831
+ };
10832
+ }
10833
+ if (!decision.allowed) return blockedResult(target.name, decision);
10834
+ const planShellReason = (options.getContext?.().mode ?? "normal") === "plan" ? planModeShellCommandBlocked(target, args) : void 0;
10835
+ if (planShellReason) {
10836
+ return blockedResult(target.name, {
10837
+ ...decision,
10838
+ allowed: false,
10839
+ reason: planShellReason,
10840
+ policyReason: "plan_mode_non_read_only_shell_command"
10841
+ });
10842
+ }
10843
+ const result = await target.execute(toolCallId, args, signal, onUpdate);
10844
+ return {
10845
+ ...result,
10846
+ details: mergePermissionDetails(result.details, decision)
10847
+ };
10848
+ }
10849
+ };
10850
+ wrapped[POLICY_ENFORCED] = {
10851
+ getContext: options.getContext,
10852
+ permissionProvider: options.permissionProvider,
10853
+ wrapperExecute: wrapped.execute,
10854
+ base: target
10855
+ };
10856
+ return wrapped;
10857
+ }
10858
+ function createPolicyEnforcedTools(tools, options = {}) {
10859
+ return tools.map((tool) => createPolicyEnforcedTool(tool, options));
10860
+ }
10861
+
10535
10862
  // ../../packages/shared-headless-capabilities/src/builtin/fs/read.ts
10536
10863
  import * as nodePath from "node:path";
10537
10864
 
@@ -16935,144 +17262,6 @@ function maybeAdvanceTodoExecutionState(state, signal) {
16935
17262
  };
16936
17263
  }
16937
17264
 
16938
- // ../../packages/shared-headless-capabilities/src/policy-enforcer.ts
16939
- var POLICY_ENFORCED = /* @__PURE__ */ Symbol.for("pie.capabilityPolicy.enforced");
16940
- function evaluateToolPermission(tool, context = {}) {
16941
- const mode = context.mode ?? "normal";
16942
- const metadata = getToolCapabilityMetadata(tool);
16943
- const base = describeToolPolicyDecision(tool, mode);
16944
- if (!metadata && mode === "normal") {
16945
- return {
16946
- disposition: "allow",
16947
- allowed: true,
16948
- reason: "unclassified_tool",
16949
- policyReason: "unclassified_tool",
16950
- mode,
16951
- yoloMode: context.yoloMode
16952
- };
16953
- }
16954
- if (!metadata || !base.allowed) {
16955
- return {
16956
- disposition: "deny",
16957
- allowed: false,
16958
- reason: base.reason ?? "tool not allowed by runtime policy",
16959
- policyReason: base.reason ?? "tool not allowed by runtime policy",
16960
- mode,
16961
- yoloMode: context.yoloMode,
16962
- riskClass: metadata?.riskClass,
16963
- permissionScope: metadata?.permissionScope,
16964
- requiresPermission: metadata?.requiresPermission
16965
- };
16966
- }
16967
- const requiresConfirmation = Boolean(metadata.highRisk || metadata.requiresPermission);
16968
- if (!requiresConfirmation || context.yoloMode || context.confirmationPolicy === "allow") {
16969
- return {
16970
- disposition: "allow",
16971
- allowed: true,
16972
- mode,
16973
- yoloMode: context.yoloMode,
16974
- riskClass: metadata.riskClass,
16975
- permissionScope: metadata.permissionScope,
16976
- requiresPermission: requiresConfirmation
16977
- };
16978
- }
16979
- if (context.confirmationPolicy === "deny" || mode === "subagent") {
16980
- const reason = mode === "subagent" ? "subagent cannot request interactive permission" : "permission confirmation is unavailable";
16981
- return {
16982
- disposition: "deny",
16983
- allowed: false,
16984
- reason,
16985
- policyReason: reason,
16986
- mode,
16987
- yoloMode: context.yoloMode,
16988
- riskClass: metadata.riskClass,
16989
- permissionScope: metadata.permissionScope,
16990
- requiresPermission: true
16991
- };
16992
- }
16993
- return {
16994
- disposition: "confirm",
16995
- allowed: false,
16996
- reason: "permission confirmation required",
16997
- policyReason: "permission confirmation required",
16998
- mode,
16999
- yoloMode: context.yoloMode,
17000
- riskClass: metadata.riskClass,
17001
- permissionScope: metadata.permissionScope,
17002
- requiresPermission: true
17003
- };
17004
- }
17005
- function blockedResult(toolName, decision) {
17006
- return {
17007
- content: [
17008
- {
17009
- type: "text",
17010
- text: `Blocked ${toolName}: ${decision.reason ?? decision.policyReason ?? "permission denied"}`
17011
- }
17012
- ],
17013
- details: { permissionDecision: decision },
17014
- isError: true
17015
- };
17016
- }
17017
- function mergePermissionDetails(details, decision) {
17018
- if (details && typeof details === "object" && !Array.isArray(details)) {
17019
- const record = details;
17020
- if (record.permissionDecision) {
17021
- return { ...record, capabilityPolicyDecision: decision };
17022
- }
17023
- return { ...record, permissionDecision: decision };
17024
- }
17025
- return { permissionDecision: decision };
17026
- }
17027
- function createPolicyEnforcedTool(tool, options = {}) {
17028
- const marker = tool[POLICY_ENFORCED];
17029
- const intactWrapper = !!marker && marker.wrapperExecute === tool.execute;
17030
- if (intactWrapper && marker.getContext === options.getContext && marker.permissionProvider === options.permissionProvider) {
17031
- return tool;
17032
- }
17033
- const target = intactWrapper ? marker.base : tool;
17034
- const wrapped = {
17035
- ...target,
17036
- execute: async (toolCallId, args, signal, onUpdate) => {
17037
- let decision = evaluateToolPermission(target, options.getContext?.());
17038
- if (decision.disposition === "confirm") {
17039
- const approved = await options.permissionProvider?.confirm({ toolName: target.name, args, decision }).catch(() => false);
17040
- if (!approved) {
17041
- return blockedResult(target.name, {
17042
- ...decision,
17043
- disposition: "deny",
17044
- reason: "permission denied by user",
17045
- policyReason: "permission denied by user"
17046
- });
17047
- }
17048
- decision = {
17049
- ...decision,
17050
- disposition: "allow",
17051
- allowed: true,
17052
- reason: void 0,
17053
- policyReason: void 0
17054
- };
17055
- }
17056
- if (!decision.allowed) return blockedResult(target.name, decision);
17057
- const result = await target.execute(toolCallId, args, signal, onUpdate);
17058
- return {
17059
- ...result,
17060
- details: mergePermissionDetails(result.details, decision)
17061
- };
17062
- }
17063
- };
17064
- wrapped[POLICY_ENFORCED] = {
17065
- getContext: options.getContext,
17066
- permissionProvider: options.permissionProvider,
17067
- wrapperExecute: wrapped.execute,
17068
- base: target
17069
- };
17070
- return wrapped;
17071
- }
17072
- function createPolicyEnforcedTools(tools, options = {}) {
17073
- return tools.map((tool) => createPolicyEnforcedTool(tool, options));
17074
- }
17075
-
17076
17265
  // ../../packages/shared-headless-capabilities/src/system-prompt-tools.ts
17077
17266
  function normalizeDescription(description) {
17078
17267
  const text = String(description || "").replace(/\s+/g, " ").trim();
@@ -19451,6 +19640,7 @@ export {
19451
19640
  generateEntryId,
19452
19641
  createHostEnvelopeProjectorRegistry,
19453
19642
  projectHostEnvelopes,
19643
+ isHostContextBlock,
19454
19644
  FileSessionStore,
19455
19645
  createSessionManager,
19456
19646
  prepareSessionCompaction,
@@ -19491,10 +19681,9 @@ export {
19491
19681
  supersedeExecutionState,
19492
19682
  shouldPreserveExecutionStateForUserText,
19493
19683
  BUILTIN_TOOL_CAPABILITY_METADATA,
19494
- getToolCapabilityMetadata,
19495
19684
  isToolAllowedInPlanMode,
19685
+ defaultShellKindForPlatform,
19496
19686
  extractPlanTodoItems,
19497
- isPlanModeSafeCommand,
19498
19687
  markCompletedPlanSteps,
19499
19688
  createPlanCapability,
19500
19689
  modalityForReadFileUnderstandingKind,