@bingjiang0611/rotom 0.1.25 → 0.1.26

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.
Files changed (107) hide show
  1. package/THIRD_PARTY_NOTICES.md +1 -1
  2. package/extensions/observability/index.ts +20 -2
  3. package/extensions/qoder/messages.mjs +1 -1
  4. package/extensions/qoder/provider.mjs +14 -6
  5. package/extensions/qoder/transport.mjs +53 -11
  6. package/extensions/third-party/node_modules/.package-lock.json +6 -6
  7. package/extensions/third-party/node_modules/pi-subagents/README.md +17 -0
  8. package/extensions/third-party/node_modules/pi-subagents/agents/delegate.md +2 -0
  9. package/extensions/third-party/node_modules/pi-subagents/agents/oracle.md +18 -14
  10. package/extensions/third-party/node_modules/pi-subagents/agents/reviewer.md +5 -3
  11. package/extensions/third-party/node_modules/pi-subagents/docs/models.md +1 -1
  12. package/extensions/third-party/node_modules/pi-subagents/docs/tool-reference.md +2 -2
  13. package/extensions/third-party/node_modules/pi-subagents/docs/workflows.md +2 -2
  14. package/extensions/third-party/node_modules/pi-subagents/package.json +1 -1
  15. package/extensions/third-party/node_modules/pi-subagents/prompts/gather-context-and-clarify.md +1 -1
  16. package/extensions/third-party/node_modules/pi-subagents/prompts/parallel-research.md +4 -6
  17. package/extensions/third-party/node_modules/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +8 -8
  18. package/extensions/third-party/node_modules/pi-subagents/skills/pi-subagents/references/execution-controls.md +5 -5
  19. package/extensions/third-party/node_modules/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +7 -8
  20. package/extensions/third-party/node_modules/pi-subagents/src/agents/agents.ts +0 -1
  21. package/extensions/third-party/node_modules/pi-subagents/src/extension/index.ts +9 -2
  22. package/extensions/third-party/node_modules/pi-subagents/src/profiles/profiles.ts +1 -2
  23. package/extensions/third-party/node_modules/pi-subagents/src/runs/background/async-execution.ts +81 -52
  24. package/extensions/third-party/node_modules/pi-subagents/src/runs/background/process-terminal.ts +13 -0
  25. package/extensions/third-party/node_modules/pi-subagents/src/runs/background/stale-run-reconciler.ts +7 -3
  26. package/extensions/third-party/node_modules/sol-pi/UPSTREAM.md +1 -0
  27. package/extensions/third-party/node_modules/sol-pi/package.json +1 -1
  28. package/extensions/third-party/node_modules/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/index.ts +1 -0
  29. package/extensions/third-party/node_modules/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/receipt.ts +79 -16
  30. package/extensions/third-party/package-lock.json +8 -8
  31. package/extensions/third-party/package.json +2 -2
  32. package/extensions/third-party/vendor/README.md +12 -12
  33. package/extensions/third-party/vendor/pi-subagents-0.52.1-rotom.10.tgz +0 -0
  34. package/extensions/third-party/vendor/sol-pi-0.1.0-rotom.2.tgz +0 -0
  35. package/package.json +3 -3
  36. package/runtime/pi/fork-build.json +9 -9
  37. package/runtime/pi/node_modules/.package-lock.json +7 -7
  38. package/runtime/pi/node_modules/@earendil-works/pi-agent-core/package.json +1 -1
  39. package/runtime/pi/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  40. package/runtime/pi/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +2 -1
  41. package/runtime/pi/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  42. package/runtime/pi/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts +8 -0
  43. package/runtime/pi/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts.map +1 -1
  44. package/runtime/pi/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js +37 -1
  45. package/runtime/pi/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js.map +1 -1
  46. package/runtime/pi/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts +6 -0
  47. package/runtime/pi/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
  48. package/runtime/pi/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +34 -0
  49. package/runtime/pi/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  50. package/runtime/pi/node_modules/@earendil-works/pi-ai/package.json +1 -1
  51. package/runtime/pi/node_modules/@earendil-works/pi-codemode/package.json +1 -1
  52. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/anthropic-messages-H6BRNJYC.js +6 -0
  53. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{azure-openai-responses-KZZFKCXW.js → azure-openai-responses-45AJGMB6.js} +1 -1
  54. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
  55. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{chunk-2CKNKV2I.js → chunk-5MTGRVNE.js} +2 -2
  56. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{chunk-ITZNWD4K.js → chunk-7OCND3IM.js} +1 -1
  57. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/chunk-CTEAMRDY.js +2 -0
  58. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{chunk-6NUCYK64.js → chunk-ESL4WJJF.js} +9 -9
  59. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{chunk-DSV77X2M.js → chunk-MXCOAUPD.js} +1 -1
  60. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{cloudflare-workers-ai-system-one-UTBL2KOF.js → cloudflare-workers-ai-system-one-IEPXKTM7.js} +1 -1
  61. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{execute-4WILIDZF.js → execute-ULZNGRYF.js} +1 -1
  62. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{google-generative-ai-KJOKBJM3.js → google-generative-ai-KXRSXWRQ.js} +1 -1
  63. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{google-vertex-VCLZ36OB.js → google-vertex-CVLJXJIO.js} +1 -1
  64. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{llama-cpp-classify-FYN623RH.js → llama-cpp-classify-42ZH3VVO.js} +1 -1
  65. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{openai-codex-responses-ZYGHVE5B.js → openai-codex-responses-L4P7LV62.js} +1 -1
  66. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{openai-completions-BGCEXFAB.js → openai-completions-ZIOWDPDJ.js} +1 -1
  67. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{openai-decisions-WVPMQ4JD.js → openai-decisions-L7N23NC7.js} +1 -1
  68. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{openai-responses-KCCCXI3B.js → openai-responses-XT6CQ4R7.js} +1 -1
  69. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{openrouter-images-DUPG5DZO.js → openrouter-images-FQ7HEE4T.js} +1 -1
  70. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{typesafe-system-one-ZSK3RN4B.js → typesafe-system-one-IH2QHGKL.js} +1 -1
  71. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/{virtual-modules-INPREDLP.js → virtual-modules-YSF6BJRF.js} +1 -1
  72. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/cli-runtime.js +1 -1
  73. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/index.js +1 -1
  74. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/rpc-entry.js +1 -1
  75. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts +5 -0
  76. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  77. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +56 -27
  78. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  79. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.d.ts +1 -0
  80. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  81. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.js +4 -1
  82. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.js.map +1 -1
  83. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  84. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +37 -4
  85. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  86. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/docs/json.md +6 -0
  87. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/docs/rpc-extension-ui.md +2 -0
  88. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/docs/settings.md +3 -1
  89. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/package.json +1 -1
  90. package/runtime/pi/node_modules/@earendil-works/pi-mcp/package.json +1 -1
  91. package/runtime/pi/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  92. package/runtime/pi/node_modules/@earendil-works/pi-tui/package.json +1 -1
  93. package/runtime/pi/package-lock.json +7 -7
  94. package/runtime/pi/vendor/earendil-works-pi-agent-core-1.3.0-rotom.2.tgz +0 -0
  95. package/runtime/pi/vendor/earendil-works-pi-ai-1.3.0-rotom.2.tgz +0 -0
  96. package/runtime/pi/vendor/earendil-works-pi-codemode-1.3.0-rotom.2.tgz +0 -0
  97. package/runtime/pi/vendor/earendil-works-pi-coding-agent-1.3.0-rotom.2.tgz +0 -0
  98. package/runtime/pi/vendor/earendil-works-pi-mcp-1.3.0-rotom.2.tgz +0 -0
  99. package/runtime/pi/vendor/earendil-works-pi-telemetry-1.3.0-rotom.2.tgz +0 -0
  100. package/runtime/pi/vendor/earendil-works-pi-tui-1.3.0-rotom.2.tgz +0 -0
  101. package/runtime/product-config.mjs +4 -4
  102. package/extensions/third-party/node_modules/pi-subagents/agents/researcher.md +0 -52
  103. package/extensions/third-party/node_modules/pi-subagents/docs/agents.md +0 -383
  104. package/extensions/third-party/vendor/pi-subagents-0.52.1-rotom.7.tgz +0 -0
  105. package/extensions/third-party/vendor/sol-pi-0.1.0-rotom.1.tgz +0 -0
  106. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/anthropic-messages-UIH6DSZT.js +0 -6
  107. package/runtime/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/chunks/chunk-TEPMHNKQ.js +0 -2
@@ -12,7 +12,7 @@ rotom 的根产品许可证尚未确定(当前 `UNLICENSED`);此状态不
12
12
  | `@narumitw/pi-goal` | `0.54.4-rotom.8` | MIT | Independently maintained fork (`packages/rotom-goal/`), included from its integrity-pinned vendor archive |
13
13
  | `pi-context-view` | `0.7.1` | MIT | Included unmodified under `extensions/third-party/node_modules/` |
14
14
  | `pi-subagents` | `0.52.1-rotom.7` | MIT | Independently maintained Subagent source, included from its integrity-pinned vendor archive |
15
- | `sol-pi` (SoL-Pi, Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES) | `0.1.0-rotom.1` | MIT | Maintained import (`packages/rotom-sol-pi/`), included from its integrity-pinned vendor archive |
15
+ | `sol-pi` (SoL-Pi, Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES) | `0.1.0-rotom.2` | MIT | Maintained import (`packages/rotom-sol-pi/`), included from its integrity-pinned vendor archive |
16
16
 
17
17
  Each included extension retains its original `LICENSE` and package metadata; transitive dependency notices remain in their installed package directories. The modified Subagent, Computer Use, Goal and SoL-Pi archives' origin and scope are documented in `extensions/third-party/vendor/README.md` and each fork's `package/UPSTREAM.md`. Shipping the complete dependency does not enable its unselected tools, templates, or skills.
18
18
 
@@ -20,7 +20,10 @@ const QODER_TRACE_ERROR_MESSAGES = new Map([
20
20
  ["Qoder: upstream_error_event", "upstream_error_event"],
21
21
  ["Qoder: upstream_error_frame", "upstream_error_frame"],
22
22
  ["Qoder: request_failed", "transport_failure"],
23
+ ["Qoder: connection_failed", "transport_failure"],
23
24
  ["Qoder: stream_failed", "transport_failure"],
25
+ ["Qoder: invalid_tool_id", "invalid_tool_id"],
26
+ ["Qoder: model_refusal", "model_refusal"],
24
27
  ]);
25
28
 
26
29
  const EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
@@ -34,7 +37,7 @@ const PROVIDER_ERROR_CATEGORIES: ReadonlyArray<readonly [category: string, patte
34
37
  ["websocket", /^WebSocket (?:error|closed)\b/u],
35
38
  ["aborted", /^This operation was aborted$/u],
36
39
  ["overloaded", /overloaded/iu],
37
- ["network", /^(?:fetch failed|terminated|Connection error\.)$|^Qoder: (?:request|stream)_failed$|\b(?:ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b|socket hang up|^upstream connect error/u],
40
+ ["network", /^(?:fetch failed|terminated|Connection error\.)(?: \(caused by: .*\))?$|^Qoder: (?:request|stream|connection)_failed$|\b(?:ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b|socket hang up|^upstream connect error/u],
38
41
  ["auth", /^API key auth failed\b/u],
39
42
  ["provider_error", /^Codex error:|^\{"type":"error"|^Qoder: /u],
40
43
  ];
@@ -376,8 +379,23 @@ function assistantAttributes(message: any): TraceAttributes {
376
379
 
377
380
  function qoderDiagnosticAttributes(value: unknown): TraceAttributes {
378
381
  const diagnostic = record(value);
379
- const allowedCodes = ["upstream_error_frame", "legacy_error", "transport_failure"];
382
+ const allowedCodes = ["upstream_error_frame", "legacy_error", "transport_failure", "invalid_tool_id"];
380
383
  if (!diagnostic || !allowedCodes.includes(String(diagnostic.code))) return {};
384
+ if (diagnostic.code === "invalid_tool_id") {
385
+ // Event payloads are untrusted: persist only fixed categories and the
386
+ // parser's bounded wire index, never identities, arguments or prose.
387
+ return {
388
+ "pi.ai.qoder.diagnostic_code": "invalid_tool_id",
389
+ ...(["non_string", "oversized", "changed"].includes(diagnostic.reason)
390
+ ? { "pi.ai.qoder.tool_id_reason": diagnostic.reason } : {}),
391
+ ...(diagnostic.reason === "changed" && ["extension", "fragment", "distinct"].includes(diagnostic.relation)
392
+ ? { "pi.ai.qoder.tool_id_relation": diagnostic.relation } : {}),
393
+ ...(diagnostic.reason === "changed" && typeof diagnostic.previousArgumentsComplete === "boolean"
394
+ ? { "pi.ai.qoder.previous_arguments_complete": diagnostic.previousArgumentsComplete } : {}),
395
+ ...(Number.isSafeInteger(diagnostic.wireIndex) && diagnostic.wireIndex >= 0 && diagnostic.wireIndex < 64
396
+ ? { "pi.ai.qoder.tool_wire_index": diagnostic.wireIndex } : {}),
397
+ };
398
+ }
381
399
  const status = Number.isSafeInteger(diagnostic.status) && (diagnostic.status as number) >= 0
382
400
  ? diagnostic.status as number
383
401
  : undefined;
@@ -257,7 +257,7 @@ export function buildQoderPayload(model, context, { maxTokens, reasoningMode } =
257
257
  messages: convertMessages(model, context),
258
258
  stream: true,
259
259
  stream_options: { include_usage: true },
260
- max_tokens: maxTokens,
260
+ ...(maxTokens !== undefined ? { max_tokens: maxTokens } : {}),
261
261
  };
262
262
  if (reasoningMode) payload.reasoning_effort = reasoningMode.effort;
263
263
  const tools = context.tools ?? [];
@@ -19,8 +19,9 @@ export const MODEL = {
19
19
  // "openai-completions" identity. Older sessions fail model_scope by design.
20
20
  api: 'qoder', provider: PROVIDER_ID, baseUrl: BASE_URL,
21
21
  reasoning: false, input: ['text'],
22
- // Conservative adapter limits, not a claim about the service's capacity.
23
- contextWindow: 32000, maxTokens: 4096,
22
+ // Managed input window; zero means no declared output maximum in Pi.
23
+ // The service owns the default output budget, not a guessed adapter cap.
24
+ contextWindow: 32000, maxTokens: 0,
24
25
  // Pi requires numeric prices. These placeholders are NOT zero-cost evidence.
25
26
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
26
27
  compat: { supportsStore: false, supportsDeveloperRole: false, supportsReasoningEffort: false, supportsStrictMode: false, maxTokensField: 'max_tokens' },
@@ -109,7 +110,7 @@ export async function createQoderProvider({ piAI, clampMaxTokens, getToken, getC
109
110
  messages: transcript.messages.filter(message => message.role !== 'system'),
110
111
  };
111
112
  const reasoning = REASONING_MODEL_IDS.includes(model.id);
112
- if (!SUPPORTED_MODEL_IDS.includes(model.id) || model.provider !== PROVIDER_ID || model.baseUrl !== BASE_URL || model.api !== MODEL.api || model.reasoning !== reasoning || !Number.isSafeInteger(model.contextWindow) || model.contextWindow < 4096 || model.contextWindow > (EXPANDED_INPUT_MODEL_IDS.includes(model.id) ? 272000 : 32000) || model.maxTokens > 4096) throw new QoderError('model_scope_rejected');
113
+ if (!SUPPORTED_MODEL_IDS.includes(model.id) || model.provider !== PROVIDER_ID || model.baseUrl !== BASE_URL || model.api !== MODEL.api || model.reasoning !== reasoning || !Number.isSafeInteger(model.contextWindow) || model.contextWindow < 4096 || model.contextWindow > (EXPANDED_INPUT_MODEL_IDS.includes(model.id) ? 272000 : 32000) || !Number.isSafeInteger(model.maxTokens) || model.maxTokens < 0) throw new QoderError('model_scope_rejected');
113
114
  if (options.signal?.aborted) throw new QoderError('aborted');
114
115
  // Refresh through the session's native registry before building any payload,
115
116
  // including tool continuations and compaction. Never retry an inference.
@@ -173,8 +174,15 @@ export async function createQoderProvider({ piAI, clampMaxTokens, getToken, getC
173
174
  // the agent lifecycle; only request encoding and response translation move
174
175
  // in-house here.
175
176
  const nativeModel = { ...model, contextWindow: input.contextWindow, input: input.images ? ['text', 'image'] : ['text'], ...(controls ? { thinkingLevelMap: thinkingMap(controls.levels), compat: { ...MODEL.compat, supportsReasoningEffort: true } } : {}) };
176
- clampMaxTokens ??= await nativeOutputBudget();
177
- const maxTokens = clampMaxTokens(nativeModel, context, Math.min(options.maxTokens ?? MODEL.maxTokens, MODEL.maxTokens));
177
+ // Omit the default output limit, including for resumed models that still
178
+ // carry the former 4096 metadata. Explicit caller budgets (e.g. compaction)
179
+ // still use Pi's context headroom rather than an independent fixed ceiling.
180
+ let maxTokens;
181
+ if (options.maxTokens !== undefined) {
182
+ if (!Number.isSafeInteger(options.maxTokens) || options.maxTokens < 1) throw new QoderError('request_limits_rejected');
183
+ clampMaxTokens ??= await nativeOutputBudget();
184
+ maxTokens = clampMaxTokens(nativeModel, context, options.maxTokens);
185
+ }
178
186
  let payload = buildQoderPayload(nativeModel, context, { maxTokens, reasoningMode });
179
187
  // Preserve Pi's BeforeProviderRequest hook: an extension may rewrite the
180
188
  // payload, but openQoderStream re-validates the result against the bound
@@ -186,7 +194,7 @@ export async function createQoderProvider({ piAI, clampMaxTokens, getToken, getC
186
194
  // encoding and HTTP dispatch, then yields validated chunk records; a
187
195
  // pre-stream failure rejects here and lazyStream turns it into an error
188
196
  // event carrying the stable "Qoder: <code>" message the session-policy reads.
189
- const { chunks } = await openQoderStream({ payload, getToken: requestToken, getLegacyCredential: () => requestCredential, legacyModel: entry, reasoningMode, fetchImpl, signal: options.signal, modelId: model.id, timeoutMs: reasoning ? 180000 : 60000, onDiagnostic, sessionId: options.sessionId, onMetering(data) {
197
+ const { chunks } = await openQoderStream({ payload, getToken: requestToken, getLegacyCredential: () => requestCredential, legacyModel: entry, reasoningMode, fetchImpl, signal: options.signal, modelId: model.id, timeoutMs: reasoning ? 180000 : 60000, onDiagnostic, sessionId: options.sessionId, onResponse: options.onResponse && (response => options.onResponse(response, model)), onMetering(data) {
190
198
  for (const notify of [() => recordMetering?.({ ...data, fingerprint: requestCredential?.fingerprint }), () => onMetering?.(data)]) {
191
199
  try { void Promise.resolve(notify()).catch(() => {}); } catch { /* Scoped metadata only; never retry a model request. */ }
192
200
  }
@@ -35,9 +35,9 @@ export function inputCapabilities(entry) {
35
35
  // so it is a managed budget, not the upstream limit. The maintainer aligned it
36
36
  // with the Codex-declared 272000 to keep one compaction percentage across
37
37
  // providers; the fixed 400K wire selector still is not an advertised capacity.
38
- // Consequence, measured: full 4096 output needs input <= window - 8192, so the
39
- // researched >=272K single-request input is upstream capability, not product
40
- // capacity, and oversized single turns lose answer budget instead of failing.
38
+ // Explicit output budgets retain Pi's native context headroom clamp. With
39
+ // no requested budget, the service decides output length; the managed input
40
+ // window and native compaction reserve remain unchanged.
41
41
  return Object.freeze({ images: reviewed && entry.reportedImages === true, contextWindow: expanded ? 272000 : Math.min(32000, entry?.reportedContextWindow ?? 32000), contextLength: expanded ? 400000 : Math.min(32000, entry?.reportedContextWindow ?? 32000) });
42
42
  }
43
43
  // Only inline, canonical base64 in the three exercised codecs.
@@ -261,6 +261,9 @@ export async function* qoderChunks(body, { signal, toolNames = [], onDiagnostic,
261
261
  if (chunk.choices.length > 1) fail('multiple_choices_unsupported');
262
262
  const choices = chunk.choices.map(choice => {
263
263
  if (choice.index !== undefined && choice.index !== 0) fail('invalid_choice_index');
264
+ // A refusal may omit delta and even precede later tool frames. Never
265
+ // reinterpret that explicit refusal as a malformed delta or execute tools.
266
+ if (choice.finish_reason === 'refusal') fail('model_refusal');
264
267
  // Observed Sonus/Ultimate COSY frames use function_call with modern
265
268
  // tool_calls deltas. Still require complete tools and validated EOF;
266
269
  // never reinterpret the direct HTTP route or legacy function_call deltas.
@@ -297,11 +300,12 @@ export async function* qoderChunks(body, { signal, toolNames = [], onDiagnostic,
297
300
  let index = wireIndexes.get(wireIndex);
298
301
  if (index === undefined) { index = calls.has(wireIndex) ? freeIndex() : wireIndex; wireIndexes.set(wireIndex, index); }
299
302
  let call = calls.get(index) ?? { id: '', name: '', arguments: '' };
300
- // Observed Ultimate batches restart index 0 for each serial tool.
303
+ // Observed Ultimate batches restart index 0 for each serial tool,
304
+ // including on the fixed COSY route after unwrapping its envelope.
301
305
  // Rebase only a new full, unique identity after a complete prior call
302
306
  // and with an explicit declared function name. Never concatenate IDs,
303
307
  // alias a correction, or guess an interleaved/unfinished call's owner.
304
- if (allowOpaqueReasoning && opaqueModelId === 'ultimate' && !allowLegacyEnvelope && wireIndex === 0 && t.index === 0 && call.id && typeof t.id === 'string' && t.id && call.id !== t.id) {
308
+ if (allowOpaqueReasoning && opaqueModelId === 'ultimate' && wireIndex === 0 && t.index === 0 && call.id && typeof t.id === 'string' && t.id && call.id !== t.id) {
305
309
  let args; try { args = JSON.parse(call.arguments); } catch { /* Incomplete predecessor: reject the identity change below. */ }
306
310
  if (record(args) && toolNames.includes(call.name) && toolNames.includes(t.function.name) && t.id.length <= 512 && !t.id.startsWith(call.id) && !call.id.startsWith(t.id) && ![...calls.values()].some(c => c.id === t.id)) {
307
311
  index = freeIndex(); wireIndexes.set(wireIndex, index); call = { id: '', name: '', arguments: '' };
@@ -309,7 +313,23 @@ export async function* qoderChunks(body, { signal, toolNames = [], onDiagnostic,
309
313
  }
310
314
  const out = { index, type: 'function', function: {} };
311
315
  if (t.id !== undefined && t.id !== '') {
312
- if (typeof t.id !== 'string' || t.id.length > 512 || (call.id && call.id !== t.id)) fail('invalid_tool_id');
316
+ if (typeof t.id !== 'string' || t.id.length > 512 || (call.id && call.id !== t.id)) {
317
+ let reason = 'changed';
318
+ if (typeof t.id !== 'string') reason = 'non_string';
319
+ else if (t.id.length > 512) reason = 'oversized';
320
+ const diagnostic = { code: 'invalid_tool_id', reason, wireIndex };
321
+ if (reason === 'changed') {
322
+ // Describe identity/argument structure only; never emit IDs or arguments.
323
+ diagnostic.relation = 'distinct';
324
+ if (t.id.startsWith(call.id)) diagnostic.relation = 'extension';
325
+ else if (call.id.startsWith(t.id) || call.id.endsWith(t.id)) diagnostic.relation = 'fragment';
326
+ let previousArguments;
327
+ try { previousArguments = JSON.parse(call.arguments); } catch { /* Partial JSON is not a complete tool argument object. */ }
328
+ diagnostic.previousArgumentsComplete = record(previousArguments);
329
+ }
330
+ try { void Promise.resolve(onDiagnostic?.(diagnostic)).catch(() => {}); } catch { /* Optional metadata must not replace the validation error. */ }
331
+ fail('invalid_tool_id');
332
+ }
313
333
  call.id = out.id = t.id;
314
334
  }
315
335
  for (const field of ['name', 'arguments']) {
@@ -399,7 +419,22 @@ export async function* normalizeSSE(body, options) {
399
419
  // stream-time failures. It never yields STREAM_DONE, so normal completion of
400
420
  // `chunks` is itself proof of clean termination because qoderChunks fails
401
421
  // closed on any incomplete or malformed stream ending.
402
- export async function openQoderStream({ payload, getToken, getLegacyCredential, legacyModel, fetchImpl = globalThis.fetch, modelId = 'lite', sessionId = randomUUID(), timeoutMs = 60000, onDiagnostic, onMetering, reasoningMode, signal } = {}) {
422
+ // Same connect-phase rule as Pi's `connectFailureCause` (this module stays
423
+ // dependency-free): socket codes that only occur while connecting, or a proxy
424
+ // tunnel / TLS setup that failed before any request bytes were sent. undici
425
+ // nests these in `cause`, as an AggregateError for multi-address connects.
426
+ const UNSENT_CONNECT_ERROR_CODES = new Set(['ENOTFOUND', 'EAI_AGAIN', 'ECONNREFUSED', 'ENETUNREACH', 'ENETDOWN', 'EHOSTUNREACH', 'EADDRNOTAVAIL', 'UND_ERR_CONNECT_TIMEOUT']);
427
+ const UNSENT_CONNECT_ERROR_MESSAGE = /before secure TLS connection was established|when HTTP Tunneling/;
428
+ function neverSent(error) {
429
+ let current = error;
430
+ for (let depth = 0; depth < 5 && current instanceof Error; depth++) {
431
+ if (UNSENT_CONNECT_ERROR_CODES.has(current.code) || UNSENT_CONNECT_ERROR_MESSAGE.test(current.message)) return true;
432
+ current = current.cause ?? (current instanceof AggregateError ? current.errors[0] : undefined);
433
+ }
434
+ return false;
435
+ }
436
+
437
+ export async function openQoderStream({ payload, getToken, getLegacyCredential, legacyModel, fetchImpl = globalThis.fetch, modelId = 'lite', sessionId = randomUUID(), timeoutMs = 60000, onDiagnostic, onMetering, onResponse, reasoningMode, signal } = {}) {
403
438
  // Models.json, onPayload and SDK overrides must never redirect credentials.
404
439
  const input = inputCapabilities(legacyModel?.id === modelId ? legacyModel : undefined);
405
440
  if (signal?.aborted) fail('aborted');
@@ -450,9 +485,8 @@ export async function openQoderStream({ payload, getToken, getLegacyCredential,
450
485
  payload.reasoning_effort = 'none';
451
486
  }
452
487
  if (payload.tools !== undefined && (!Array.isArray(payload.tools) || payload.tools.some(t => !record(t) || t.type !== 'function' || !record(t.function) || typeof t.function.name !== 'string'))) fail('request_scope_rejected');
453
- const maxTokens = payload.max_tokens ?? 4096;
454
- if (!Number.isSafeInteger(maxTokens) || maxTokens < 1 || maxTokens > 4096) fail('request_limits_rejected');
455
- payload.max_tokens = maxTokens;
488
+ const maxTokens = payload.max_tokens;
489
+ if (maxTokens !== undefined && (!Number.isSafeInteger(maxTokens) || maxTokens < 1)) fail('request_limits_rejected');
456
490
  const requestId = randomUUID();
457
491
  payload.metadata = { context: { request_id: requestId, request_set_id: requestId, session_id: sessionId, task_id: 'common', client_type: 'rotom' } };
458
492
  const legacy = LEGACY_MODEL_IDS.includes(modelId);
@@ -491,6 +525,9 @@ export async function openQoderStream({ payload, getToken, getLegacyCredential,
491
525
  if (controller.signal.aborted) { void result.body?.cancel().catch(() => {}); fail('aborted'); }
492
526
  return result;
493
527
  }, controller.signal);
528
+ // Response-header timing for observability (status only matters there); the
529
+ // body is untouched, so this cannot alter the stream.
530
+ await onResponse?.({ status: response.status, headers: Object.fromEntries(response.headers) });
494
531
  if (!response.ok) {
495
532
  void response.body?.cancel().catch(() => {});
496
533
  fail(`http_${response.status}`);
@@ -502,7 +539,12 @@ export async function openQoderStream({ payload, getToken, getLegacyCredential,
502
539
  if (!controller.signal.aborted) {
503
540
  try { void Promise.resolve(onDiagnostic?.({ code: 'transport_failure', phase: 'request' })).catch(() => {}); } catch { /* Allowlisted metadata only. */ }
504
541
  }
505
- throw new QoderError(controller.signal.aborted ? 'aborted' : 'request_failed');
542
+ // Only failures that prove the request never left this machine (offline,
543
+ // DNS, refused/unroutable connect) become connection_failed, which Pi waits
544
+ // out without a retry limit. A reset after the body was sent may already be
545
+ // billed upstream, so it stays the generic, non-retried request_failed.
546
+ const unreachable = dispatchStarted && neverSent(error);
547
+ throw new QoderError(controller.signal.aborted ? 'aborted' : unreachable ? 'connection_failed' : 'request_failed');
506
548
  }
507
549
  const frames = qoderChunks(response.body, { signal: controller.signal, onDiagnostic, onCreditUsage: usage => meter.observe(usage), allowReasoning: allowReasoning && reasoningMode?.enabled !== false, allowOpaqueReasoning: OPAQUE_MODEL_IDS.includes(modelId), opaqueModelId: modelId, allowLegacyEnvelope: legacy, allowLegacyMetricsDone: ['mmodel', 'smodel', 'ultimate'].includes(modelId), toolNames: (payload.tools ?? []).map(t => t.function?.name) });
508
550
  async function* chunks() {
@@ -131,9 +131,9 @@
131
131
  }
132
132
  },
133
133
  "node_modules/pi-subagents": {
134
- "version": "0.52.1-rotom.7",
135
- "resolved": "file:vendor/pi-subagents-0.52.1-rotom.7.tgz",
136
- "integrity": "sha512-6hbdcuDAqFSsYxlGzVptP1vbQc+x6Zxu4VAtSQVke6ZZWn+T+MWGvaLV06qATmCWo2bVfj0CNjJb8egQj96FPg==",
134
+ "version": "0.52.1-rotom.10",
135
+ "resolved": "file:vendor/pi-subagents-0.52.1-rotom.10.tgz",
136
+ "integrity": "sha512-5mXSC3zXponiWE+NP16rJ4zo1j1mHS04bbFO+fOc31RVVHA8rKvZ3nvemDOHT9qCBqINAiRcXJuhpZCNNNeoHg==",
137
137
  "license": "MIT",
138
138
  "dependencies": {
139
139
  "acorn": "8.18.0",
@@ -166,9 +166,9 @@
166
166
  }
167
167
  },
168
168
  "node_modules/sol-pi": {
169
- "version": "0.1.0-rotom.1",
170
- "resolved": "file:vendor/sol-pi-0.1.0-rotom.1.tgz",
171
- "integrity": "sha512-t4h2lPg0ktcA4oyiBwKJur13Noc5pfYUhV5lXkRuoPV3CQ/Bf1oYil8zIgz8rBB5vMrYXUn6zRhDQg+S/VljAw==",
169
+ "version": "0.1.0-rotom.2",
170
+ "resolved": "file:vendor/sol-pi-0.1.0-rotom.2.tgz",
171
+ "integrity": "sha512-9SOaxYtxnfbnU+IByPpz8dmk95eDsGs4dvi1sq9i2g3VrGxIok0sGatlhPLhiOUgETWZyRBtRHULCm+yZiOu2g==",
172
172
  "license": "MIT",
173
173
  "engines": {
174
174
  "node": ">=22.19.0"
@@ -38,6 +38,23 @@ macOS 的 mount device ID 可能在重启后重新编号。store 现在只在 Da
38
38
 
39
39
  在 `.4` 的精简基础上,终态投影出错时保留已观察到的受控进程关闭证据与错误说明,将结果明确标记为 unknown,而不是推断任务成功或授权重放。本版重新归档当前源码,使后续终态修复实际进入产品;不覆盖旧 `.4` 归档,不迁移存活安装。它属于后续开发,不包含在已冻结的 rotom `0.1.17` 安装包中。
40
40
 
41
+ ## `0.52.1-rotom.9`:父运行时退出前等待关闭证明
42
+
43
+ 正常退出、reload、new/resume 或 SDK dispose 时,先暂停当前扩展运行时实际启动的异步 runner,等待真实 `close` 回调完成终态证据处理,再清理运行时。只保留当前进程中的 ChildProcess 对象,不按历史 PID 补发信号;其他运行时的任务不受影响。回收开始后拒绝新 writer,正常关闭后可由新运行时恢复原 canonical session。后台任务不再跨越其所属运行时的销毁继续执行。任务结果先于 close 回调到达时,仅在本进程仍持有该 run/runner 实例的真实观察者期间保留 pending;不会提前将其改成 observer-unavailable,也不会把观察者登记当成关闭证明。
44
+
45
+ 强杀、异常 owner loss、缺失/写入失败的证据仍是 unknown,不从候选记录或 PID 消失推断历史退出;不补写旧任务的成功证明、不自动恢复旧任务。源码版本不会热更新当前安装。验证、首次失败与剩余限制见[父运行时关闭修复](../../docs/agent/subagent-parent-shutdown.md)。
46
+
47
+ ## `0.52.1-rotom.10`:内置角色可在 owned scope 启动
48
+
49
+ 会话复盘发现 `delegate` 在 owned scope 下 6/6 启动失败、`researcher` 1/1 失败,原因都是缺少显式 `extensions`。本版调整内置角色:
50
+
51
+ - `delegate` 声明空 `extensions` 与 `defaultContext: fresh`,工具白名单不变。
52
+ - `oracle`(别名 `advisor`)改为 `defaultContext: fresh`、空 `extensions`,任务由父会话写成决策简报(目标/验收、已定决策、约束、方案、相关文件、具体问题);仍只读,不再依赖 fork。
53
+ - `reviewer` 增加 `bash`,提示词限定只读检查(diff/log/rg、运行现有测试与类型检查),禁止写文件、Git 写操作、安装和网络;这是提示约束,不是沙箱。
54
+ - 删除 `researcher`:它依赖的 `web_search`/`fetch_content` 在 rotom 中不存在。需要网页检索时由父会话自行完成,或配置显式声明 web 工具与 `extensions` 的自定义 agent。
55
+
56
+ 自定义 agent、显式 fork 请求与 admission 拒绝规则不变。
57
+
41
58
  ## 开发
42
59
 
43
60
  在本目录运行;依赖是固定版本及本目录 lockfile,勿复制维护者 node_modules:
@@ -4,6 +4,8 @@ description: Lightweight subagent that inherits the parent model with no default
4
4
  systemPromptMode: append
5
5
  inheritProjectContext: true
6
6
  tools: read, grep, find, ls, bash, edit, write, contact_supervisor
7
+ extensions:
8
+ defaultContext: fresh
7
9
  inheritSkills: false
8
10
  ---
9
11
 
@@ -1,41 +1,44 @@
1
1
  ---
2
2
  name: oracle
3
3
  aliases: advisor
4
- description: High-context decision-consistency oracle that protects inherited state and prevents drift
4
+ description: Read-only decision-consistency oracle; checks a parent-written decision brief against the code and prevents drift
5
5
  tools: read, grep, find, ls, bash
6
+ extensions:
6
7
  thinking: high
7
8
  systemPromptMode: replace
8
9
  inheritProjectContext: true
9
10
  inheritSkills: false
10
- defaultContext: fork
11
+ defaultContext: fresh
11
12
  ---
12
13
 
13
- You are the oracle: a high-context decision-consistency subagent.
14
+ You are the oracle: a read-only decision-consistency subagent.
14
15
 
15
- Your primary job is to prevent the main agent from making hidden, conflicting, or inconsistent decisions by treating the inherited forked context as the authoritative contract. You are not the primary executor. You do not silently become a second decision-maker.
16
+ Your primary job is to prevent the main agent from making hidden, conflicting, or inconsistent decisions. You start with a fresh context: you do not see the parent conversation. The task is a decision brief written by the main agent, and it is your authoritative contract together with the code. You are not the primary executor. You do not silently become a second decision-maker.
16
17
 
17
- Before you do anything else, reconstruct the key inherited decisions, constraints, and open questions from the forked conversation, codebase state, and task. Those decisions form your baseline contract. Preserve them unless there is strong evidence they should be overturned.
18
+ A useful brief contains: the goal and acceptance criteria, decisions already made and why, hard constraints, the current plan or proposed move, the files or documents that matter, and the specific question. Before you do anything else, restate the decisions, constraints, and open questions from the brief and verify the claims that matter against the named files and the codebase. Those decisions form your baseline contract. Preserve them unless there is strong evidence they should be overturned.
18
19
 
19
- Match search scope to the question. For runtime behavior, begin with specific source symbols, types, methods, and paths. For product, plan, policy, or decision drift, treat supplied documents and inherited context as first-class evidence. If source conflicts with docs about runtime behavior, trust source and report the conflict.
20
+ If the brief omits something that changes the answer (a missing constraint, an unstated decision, no acceptance criteria), do not invent it. Ask the main agent through `contact_supervisor` when available; otherwise name the gap in "Need from main agent" and state which assumption you used.
20
21
 
21
- If the task asks about asking or consulting the oracle, or asks to ask, consult, discuss with, or come to agreement with the oracle about a plan, design, or architecture decision, treat it as a short live consultation unless the parent explicitly requests a one-shot report. In a first response, return the strongest challenge point or focused follow-up question when a material tradeoff remains, so the parent can resume this same session for one targeted round. A one-shot response remains suitable for an explicit one-shot request, a trivial question, or a fully settled first answer. When runtime bridge instructions provide `contact_supervisor`, ask one focused question or challenge if a material unknown, contradiction, or unapproved decision would make a final recommendation guessy. If no supervisor channel is available, return the best recommendation and name the decision that still needs the main agent.
22
+ Match search scope to the question. For runtime behavior, begin with specific source symbols, types, methods, and paths. For product, plan, policy, or decision drift, treat the brief and the documents it names as first-class evidence. If source conflicts with docs about runtime behavior, trust source and report the conflict.
23
+
24
+ If the task asks about asking or consulting the oracle, or asks to ask, consult, discuss with, or come to agreement with the oracle about a plan, design, or architecture decision, treat it as a short live consultation unless the parent explicitly requests a one-shot report. In a first response, return the strongest challenge point or focused follow-up question when a material tradeoff remains, so the parent can send one targeted follow-up round. A one-shot response remains suitable for an explicit one-shot request, a trivial question, or a fully settled first answer. When runtime bridge instructions provide `contact_supervisor`, ask one focused question or challenge if a material unknown, contradiction, or unapproved decision would make a final recommendation guessy. If no supervisor channel is available, return the best recommendation and name the decision that still needs the main agent.
22
25
 
23
26
  If you need clarification from the main agent and bridge instructions provide `contact_supervisor`, use it with `reason: "need_decision"` and wait for the reply. Use `reason: "progress_update"` only for concise updates when blocked, explicitly asked for progress, or when a recommendation or concern would benefit from immediate discussion. Keep coordination traffic tight and purposeful. Do not narrate your whole review through `contact_supervisor`.
24
27
 
25
28
  Do not send routine completion handoffs. If no coordination is needed, or after needed coordination is answered, return the final oracle recommendation normally. If `contact_supervisor` is unavailable, return the best recommendation and name the decision that still needs the main agent. Use generic `intercom` only when an external intercom provider explicitly supplies that tool and the task identifies a safe target.
26
29
 
27
30
  Core responsibilities:
28
- - reconstruct inherited decisions, constraints, and open questions from the context
29
- - identify drift between the current trajectory and those inherited decisions
31
+ - restate the decisions, constraints, and open questions from the brief and check them against the code
32
+ - identify drift between the current trajectory and those decisions
30
33
  - surface contradictions and hidden assumptions the main agent may be missing
31
34
  - call out when a proposed move conflicts with an earlier decision or constraint
32
35
  - protect consistency over novelty; prefer the path that honors existing decisions unless the context clearly supports a pivot
33
36
  - when you do recommend a pivot, explain exactly which prior assumption or decision should be revised and why
34
- - exploit your clean forked context to spot things the main agent may have missed due to context rot, accumulated reasoning, or errors in the original instruction
37
+ - use your clean context to spot things the main agent may have missed due to context rot, accumulated reasoning, or errors in the original instruction
35
38
  - look beyond the explicit question and suggest guidance based on the overall agent trajectory, even when not directly asked
36
39
 
37
40
  What you do not do by default:
38
- - do not edit files or write code
41
+ - do not edit files, write code, or change any state (no writes, installs, commits, or network calls)
39
42
  - do not propose additional parallel decision-makers or new subagent trees unless explicitly asked
40
43
  - do not assume a `worker` implementation handoff is the default outcome
41
44
  - do not propose broad pivots unless the context clearly supports them
@@ -50,21 +53,22 @@ Working rules:
50
53
 
51
54
  Your output should follow this shape. If no executor handoff is warranted, say so plainly.
52
55
 
53
- Inherited decisions:
56
+ Decisions in the brief:
54
57
  - the key decisions, constraints, and assumptions already in play
58
+ - any brief claim the code contradicts
55
59
 
56
60
  Diagnosis:
57
61
  - what is actually going on
58
62
  - what the main agent may be missing
59
63
 
60
64
  Drift / contradiction check:
61
- - where the current trajectory conflicts with inherited decisions or constraints
65
+ - where the current trajectory conflicts with the brief's decisions or constraints
62
66
  - what assumptions have quietly changed
63
67
 
64
68
  Recommendation:
65
69
  - the best next move
66
70
  - why it is the best move
67
- - if recommending a pivot, which inherited decision is being revised and why
71
+ - if recommending a pivot, which decision from the brief is being revised and why
68
72
 
69
73
  Risks:
70
74
  - what could still go wrong
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: reviewer
3
3
  description: Versatile review specialist for code diffs, plans, proposed solutions, codebase health, and PR/issue validation
4
- tools: read, grep, find, ls
4
+ tools: read, grep, find, ls, bash
5
5
  extensions:
6
6
  defaultContext: fresh
7
7
  thinking: high
@@ -55,7 +55,9 @@ Review a PR or issue by understanding the context, then verifying:
55
55
  - Start from the exact diff and named source seam for code-behavior review. Use specific source, symbol, type, method, and path searches for discovery. Use broad or unscoped `grep` only when exhaustive verification is required, such as checking call sites, imports, removed names, or absence of a pattern.
56
56
  - Read the relevant files first. Read plan and progress when the task supplies them.
57
57
  - Repo-local `progress.md` files are allowed scratch/memory files. Do not flag them as repo noise, delete them, or ask to remove them just because they are untracked. If they appear in a coding repo, they should remain untracked and be covered by `.gitignore`.
58
- - Do not use shell commands or write files. Report any test or Git command that a supervisor must run.
58
+ - Use `bash` only for read-only inspection: `git diff`/`git log`/`git show`/`git status`, `rg`, listing files, and running existing tests, type checks or linters that do not modify tracked files.
59
+ - Never change the workspace or anything outside it: no file writes or redirects, no formatters with write mode, no `git add`/`commit`/`checkout`/`reset`/`stash`/`push`, no package installs, no network calls or deploys. If a check would need any of these, report the exact command for the supervisor instead of running it.
60
+ - Report the commands you ran and their results; a check you did not run stays unverified.
59
61
  - Do not invent issues. Only report problems you can justify from evidence.
60
62
  - Prefer small corrective edits over broad rewrites.
61
63
  - If everything looks good, say so plainly.
@@ -73,7 +75,7 @@ Structure your findings clearly:
73
75
  ```
74
76
  ## Review
75
77
  - Correct: what is already good (with evidence)
76
- - Fixed: issue, location, and resolution (if you applied a fix)
78
+ - Fix: issue, location, and suggested resolution
77
79
  - Blocker: critical issue that must be resolved before proceeding
78
80
  - Note: observation, risk, or follow-up item
79
81
  ```
@@ -109,7 +109,7 @@ Project settings win over user settings. Use `agentOverrides.<name>.extensions`
109
109
  "subagents": {
110
110
  "defaultExtensions": [],
111
111
  "agentOverrides": {
112
- "researcher": {
112
+ "scout": {
113
113
  "extensions": ["./tools/research.ts"]
114
114
  }
115
115
  }
@@ -36,7 +36,7 @@ Chaining is code-driven through `workflowScript`. Use `await runs.run(...)` for
36
36
  | `action` | string | - | Agent management (including `guide`, `children.list`, and `refine`/`refine.show`/`refine.rollback`), mission (`mission.create/list/show/update/resolve-decision/attach-run/close`), Herdr inspector (`inspector.open/status/close`), status/control, watchdog, or doctor action. Scheduling is retired in the maintained component. |
37
37
  | `topic` | `overview \| workflows \| agents \| missions \| observability \| tool-reference \| configuration \| models \| watchdog \| extension-api` | `overview` | Packaged guide topic for `action: "guide"`. |
38
38
  | `config` | object/string | - | Agent config for management create/update. |
39
- | `context` | `fresh \| fork` | global or per-agent default, else `fresh` | Explicit `fresh` or `fork` overrides every workflow child. When omitted, [`defaultSubagentContext`](configuration.md#defaultsubagentcontext) wins over each agent's `defaultContext`; explicit `"fork"` requires a persisted parent session and current leaf or fails; only an implicit fork default can fall back to `fresh` when those prerequisites are absent. Packaged `worker`, `scout`, and `reviewer` default to `fresh`; `oracle` and its `advisor` alias default to `fork` (unsupported in owned scope). |
39
+ | `context` | `fresh \| fork` | global or per-agent default, else `fresh` | Explicit `fresh` or `fork` overrides every workflow child. When omitted, [`defaultSubagentContext`](configuration.md#defaultsubagentcontext) wins over each agent's `defaultContext`; explicit `"fork"` requires a persisted parent session and current leaf or fails; only an implicit fork default can fall back to `fresh` when those prerequisites are absent. All packaged agents, including `oracle` and its `advisor` alias, default to `fresh`. |
40
40
  | `missionId` | string | - | Attach a workflow to an existing project mission instead of creating its default enclosing mission. |
41
41
  | `mission` | object/false | auto-create | Override the default enclosing mission with `{ title \| summary, objective?, goal?, budget?, labels? }`. Set exactly one non-empty `title` or `summary`; `objective` and `labels` are optional. `goal` may only be `true`, requires `budget.tokens`, and enables continuation notices. Pass `false` for an intentionally ephemeral workflow with no mission for it or its children and no `state` global. Explicit mission persistence failures are strict. |
42
42
  | `handoffPath` | string | - | Aggregate handoff manifest required by `action: "worktree.discard"`. |
@@ -73,7 +73,7 @@ Explicit `context: "fork"` fails fast when the parent session is not persisted,
73
73
 
74
74
  When the inherited transcript contains signed Anthropic `thinking` / `redacted_thinking` blocks, `pi-subagents` strips those provider-private blocks from the forked child session. It forces thinking `off` only when the child's effective primary or fallback model resolves through the model registry to the Anthropic provider or `anthropic-messages` API; unresolved models are treated conservatively. The result reports every affected child, including on failed runs. Use `context: "fresh"` when an Anthropic child needs thinking. Explicit `context: "fork"` never silently downgrades to `fresh`.
75
75
 
76
- In workflow runs that omit `context`, each `runs.run` child follows the global `defaultSubagentContext` when set, then its own `defaultContext`. Without the global setting, a fresh-default scout can run fresh beside a fork-default oracle in legacy scope. If the parent session file or current leaf is not available yet, implicit fork-default children run fresh. Pass explicit `context: "fork"` or `context: "fresh"` when you intentionally want one context for every child.
76
+ In workflow runs that omit `context`, each `runs.run` child follows the global `defaultSubagentContext` when set, then its own `defaultContext`. Without the global setting, a fresh-default scout can run fresh beside a fork-default custom agent in legacy scope. If the parent session file or current leaf is not available yet, implicit fork-default children run fresh. Pass explicit `context: "fork"` or `context: "fresh"` when you intentionally want one context for every child.
77
77
 
78
78
  ### Workflow steering
79
79
 
@@ -12,7 +12,7 @@ Use orchestration as parent-agent guidance, not as a runtime workflow mode. For
12
12
  clarify → scout → worker → fresh reviewers → worker
13
13
  ```
14
14
 
15
- Packaged `worker`, `scout`, and `reviewer` default to fresh context. `oracle` and its `advisor` alias default to forked context when a launch omits `context`. If the parent has no persisted session file or current leaf yet, that implicit default falls back to `fresh`. Pass `context: "fresh"` when you intentionally want a fresh child run, or `context: "fork"` when fork must remain strict.
15
+ All packaged agents (`worker`, `scout`, `reviewer`, `delegate`, `oracle` and its `advisor` alias) default to fresh context; `oracle` works from a decision brief the parent writes into the task. A custom agent's implicit `defaultContext: fork` falls back to `fresh` when the parent has no persisted session file or current leaf yet. Pass `context: "fresh"` when you intentionally want a fresh child run, or `context: "fork"` when fork must remain strict.
16
16
 
17
17
  Child-safety boundaries are enforced at runtime:
18
18
 
@@ -29,7 +29,7 @@ The package includes reusable prompt templates for common workflows. You do not
29
29
  |--------|------------|
30
30
  | `/parallel-review` | Launch fresh-context reviewers with distinct angles, then synthesize what to fix. |
31
31
  | `/review-loop` | Run parent-controlled worker, reviewer, and fix-worker cycles until clean or capped. |
32
- | `/parallel-research` | Combine `researcher` and `scout` for external evidence, local code context, and practical tradeoffs. |
32
+ | `/parallel-research` | Combine `scout` lanes for local code context and practical tradeoffs with external evidence the parent gathers. |
33
33
  | `/gather-context-and-clarify` | Scout/research first, then ask the user the clarification questions that matter. |
34
34
  | `/parallel-cleanup` | Run review-only cleanup passes after implementation. |
35
35
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-subagents",
3
- "version": "0.52.1-rotom.7",
3
+ "version": "0.52.1-rotom.10",
4
4
  "description": "Rotom-maintained private Subagent component; derived from pi-subagents",
5
5
  "author": "Nico Bailon",
6
6
  "license": "MIT",
@@ -4,7 +4,7 @@ description: Use subagents to gather context, then ask clarifying questions
4
4
 
5
5
  Based on our discussion and my intent, launch focused context-gathering subagents before planning or implementing.
6
6
 
7
- Use `scout` to inspect the relevant local files, existing patterns, constraints, tests, and likely integration points. Use `researcher` when external docs, recent sources, ecosystem context, or primary evidence would improve the answer.
7
+ Use `scout` to inspect the relevant local files, existing patterns, constraints, tests, and likely integration points. When external docs, recent sources, or primary evidence would improve the answer, gather them in the parent; there is no builtin web-research agent.
8
8
 
9
9
  Give each subagent a specific meta prompt. Ask them to return concise findings plus the remaining clarification questions that matter for implementation confidence.
10
10
 
@@ -4,22 +4,20 @@ description: Parallel subagents research
4
4
 
5
5
  Launch parallel research subagents to build a grounded answer to the current question or decision.
6
6
 
7
- Use fresh context, not forked context, unless I explicitly ask for forked context. Researchers and scouts should inspect sources directly instead of relying on the main conversation history.
7
+ Use fresh context, not forked context, unless I explicitly ask for forked context. Scouts should inspect sources directly instead of relying on the main conversation history.
8
8
 
9
- Use a combination of `researcher` and `scout` subagents:
10
- - Use `researcher` for web, docs, standards, ecosystem, recent changes, benchmarks, and primary-source evidence.
11
- - Use `scout` for local codebase context, existing implementation patterns, repo constraints, and files that would be affected.
9
+ Use `scout` subagents for local codebase context, existing implementation patterns, repo constraints, and files that would be affected. There is no builtin web-research agent: the parent gathers external evidence itself with its own tools, or uses a custom agent that explicitly declares web tools and their `extensions`.
12
10
 
13
11
  Give each subagent a distinct angle. Unless I specify angles, use these three:
14
12
 
15
13
  1. External evidence
16
- Use `researcher` to find current, authoritative sources: official docs, specs, release notes, benchmarks, issue threads, or primary explanations.
14
+ Gathered by the parent (or a custom web-research agent): current, authoritative sources such as official docs, specs, release notes, benchmarks, issue threads, or primary explanations.
17
15
 
18
16
  2. Local code context
19
17
  Use `scout` to inspect the repository for relevant files, existing patterns, constraints, tests, and likely integration points.
20
18
 
21
19
  3. Practical tradeoffs
22
- Use `researcher` or `scout`, whichever fits the question, to compare options, risks, edge cases, maintenance cost, and what would be easiest to validate.
20
+ Use `scout` to compare options, risks, edge cases, maintenance cost, and what would be easiest to validate.
23
21
 
24
22
  Adapt the angles when the question calls for it:
25
23
  - Library/API questions: include official docs and recent examples.
@@ -8,9 +8,9 @@ This file is a detailed reference loaded from `skills/pi-subagents/SKILL.md`.
8
8
 
9
9
  - **Explicit forking requires a persisted parent session.** If the current session
10
10
  does not have a persisted session file or current leaf, explicit `context: "fork"`
11
- fails. An agent-level `defaultContext: fork` is a preference: packaged `oracle`
12
- and its `advisor` alias fall back to `fresh` when those fork preconditions are not
13
- met yet. Use `context: "fresh"` when you do not want a fork even after the parent
11
+ fails. An agent-level `defaultContext: fork` is a preference that falls back to
12
+ `fresh` when those fork preconditions are not met yet; all packaged agents
13
+ default to `fresh`. Use `context: "fresh"` when you do not want a fork even after the parent
14
14
  session exists.
15
15
  - **Forked runs inherit parent history.** They are branched threads, not fresh
16
16
  filtered contexts. Use fresh context for adversarial reviewers unless the user explicitly asks for forked context.
@@ -29,7 +29,7 @@ Runtime config can change orchestration behavior. `intercomBridge.resultDelivery
29
29
 
30
30
  ### Prefer async orchestration
31
31
 
32
- Launch every subagent asynchronously by default. Use `async: true` for scouts, researchers, workers, reviewers, validators, oracle checks, one-off delegates, and scripted workflows unless you intentionally need a foreground/blocking run. Launch all execution through `workflowScript`; use `return runs.run("main", { agent, task })` for one isolated child and `runs.all([...])` when two or more child lanes, monitors, or dependent steps should move together. The parent should keep moving: inspect code while scouts run, prepare validation while a worker implements, do a local diff pass while reviewers review, and synthesize or verify while a fix worker applies accepted feedback. Async is the default orchestration posture; foreground runs are the explicit opt-out.
32
+ Launch every subagent asynchronously by default. Use `async: true` for scouts, workers, reviewers, validators, oracle checks, one-off delegates, and scripted workflows unless you intentionally need a foreground/blocking run. Launch all execution through `workflowScript`; use `return runs.run("main", { agent, task })` for one isolated child and `runs.all([...])` when two or more child lanes, monitors, or dependent steps should move together. The parent should keep moving: inspect code while scouts run, prepare validation while a worker implements, do a local diff pass while reviewers review, and synthesize or verify while a fix worker applies accepted feedback. Async is the default orchestration posture; foreground runs are the explicit opt-out.
33
33
 
34
34
  ### Use subagent_wait() to block until async runs finish
35
35
 
@@ -108,14 +108,14 @@ Run the work through seven gated phases:
108
108
 
109
109
  For straightforward non-trivial work, this sequence is the lightweight version of the parent-owned loop. When the task is complex, use Fable mode above. In either case, factor in the packaged prompt workflows without literally invoking slash commands. Use the same patterns through tools and subagents.
110
110
 
111
- Keep builtin agent defaults unless the user explicitly asks for a different model, thinking level, skills, output behavior, context mode, or other override. Do not add overrides just because you are orchestrating; the defaults encode the intended role behavior. In particular, packaged `worker`, `scout`, and `reviewer` default to fresh context; `oracle` and its `advisor` alias default to fork in legacy scope.
111
+ Keep builtin agent defaults unless the user explicitly asks for a different model, thinking level, skills, output behavior, context mode, or other override. Do not add overrides just because you are orchestrating; the defaults encode the intended role behavior. In particular, every packaged agent (`worker`, `scout`, `reviewer`, `delegate`, `oracle` and its `advisor` alias) defaults to fresh context; give `oracle` a written decision brief instead of a fork.
112
112
 
113
113
  When the user approves launching a subagent to carry out a plan or workflow, treat that as approval to generate a proper role-specific meta prompt for that subagent. Include the approved plan path or summary, clarified requirements, non-goals, relevant context, role boundaries, files or areas to inspect, acceptance criteria, expected output, and validation expectations. Do not pass vague instructions like “implement the plan fully” or “review this” by themselves.
114
114
 
115
- - `/gather-context-and-clarify` maps to: launch `scout` and, when needed, `researcher`; synthesize findings; then use `interview` to ask every clarification question needed for shared understanding.
115
+ - `/gather-context-and-clarify` maps to: launch `scout`, gather any external evidence in the parent; synthesize findings; then use `interview` to ask every clarification question needed for shared understanding.
116
116
  - `/parallel-review` maps to: launch fresh-context `reviewer` agents with distinct review angles; synthesize the feedback before applying anything.
117
117
  - `/review-loop` maps to: keep the parent in charge of worker → fresh reviewers → synthesized fix worker cycles until no fixes worth doing now remain, an unapproved decision appears, or the review-round cap is reached.
118
- - `/parallel-research` maps to: combine local `scout` context with external `researcher` evidence when current docs, ecosystem behavior, or API details matter.
118
+ - `/parallel-research` maps to: combine local `scout` context with external evidence the parent gathers when current docs, ecosystem behavior, or API details matter.
119
119
  - `/parallel-cleanup` maps to: use review-only cleanup passes after implementation, especially for simplicity, verbosity, and redundant tests.
120
120
 
121
121
  For feature work, use this sequence as scaffolding for parent-agent behavior:
@@ -138,7 +138,7 @@ For very large work, split into serial milestones instead of launching a swarm o
138
138
 
139
139
  Keep orchestration authority in the parent session. Child subagents should not launch more subagents, read this skill, or run their own orchestration loops unless the parent intentionally selected a fanout agent whose builtin `tools` includes `subagent`. Spawned subagents do not receive the `pi-subagents` skill, parent-only status/control/slash messages, or prior parent `subagent` tool-call/tool-result artifacts. Ordinary children also do not receive the `subagent` extension tool. Child context filtering strips old hidden orchestration-instruction messages when they appear in inherited history. Every child receives a boundary instruction: ordinary children are told the parent owns orchestration and they must not propose or run subagents; explicit fanout children are told to use `subagent` only for the assigned fanout work, with `maxSubagentDepth` still enforced. Implementation children must call real edit/write tools instead of printing pseudo tool calls. Pass children concrete role-specific work instead.
140
140
 
141
- 1. Clarify first. This is mandatory. Gather code context with `scout`, add `researcher` only when external evidence matters, then ask the user clarifying questions with `interview` until scope, acceptance criteria, constraints, and non-goals are clear.
141
+ 1. Clarify first. This is mandatory. Gather code context with `scout`, add parent-gathered external evidence only when it matters, then ask the user clarifying questions with `interview` until scope, acceptance criteria, constraints, and non-goals are clear.
142
142
  2. Define the validation contract. State acceptance before implementation: expected behavior, checks to run, user flows to exercise, and evidence required in the worker handoff. For UI, CLI, integration, or workflow changes, include at least one validator angle that uses the product the way a user would rather than only reading code.
143
143
  3. Plan when useful. For complex work, write a plan doc yourself and get approval before implementation. For simple work, confirm shared understanding and explicitly note why planning is skipped.
144
144
  4. Implement with one writer. After approval, launch `worker` asynchronously with a proper meta prompt that includes clarified requirements, relevant context, plan path or summary, the validation contract, and output expectations. Packaged `worker` defaults to fresh context; provide the necessary task context explicitly. Fork is only available in an explicitly selected legacy scope. While it runs, prepare validation or inspect adjacent code instead of editing the same worktree.