@inerrata-corporation/errata 2.0.17-dev.3407 → 2.0.17-dev.3421

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 (2) hide show
  1. package/errata.mjs +495 -37
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -21903,6 +21903,24 @@ function parseIntentFences(text) {
21903
21903
  }
21904
21904
  return out2;
21905
21905
  }
21906
+ function parseShareFence(text) {
21907
+ const m = /```errata-share[^\n]*\n([\s\S]*?)```/.exec(text);
21908
+ if (!m) return null;
21909
+ const parts2 = [];
21910
+ for (const line of m[1].split(/\r?\n/)) {
21911
+ const kv = /^\s*priors\s*:\s*(.*?)\s*$/i.exec(line);
21912
+ if (kv) parts2.push(kv[1]);
21913
+ else if (parts2.length > 0 && line.trim()) parts2.push(line.trim());
21914
+ }
21915
+ const value = parts2.join(" ").trim();
21916
+ if (/^all$/i.test(value)) return { all: true };
21917
+ const priors = [
21918
+ ...new Set(
21919
+ value.split(/[\s,]+/).map((t) => t.replace(/^[`([]+|[`)\]]+$/g, "")).filter((t) => /^[\w.:-]+$/.test(t))
21920
+ )
21921
+ ];
21922
+ return priors.length > 0 ? { all: false, priors } : null;
21923
+ }
21906
21924
  function searchByIntent(store, intent, opts) {
21907
21925
  const limit = opts?.limit ?? 5;
21908
21926
  const minCoverage = opts?.minCoverage ?? DEFAULT_MIN_COVERAGE;
@@ -25010,6 +25028,7 @@ __export(src_exports2, {
25010
25028
  parseAbstractionFences: () => parseAbstractionFences,
25011
25029
  parseIntentFences: () => parseIntentFences,
25012
25030
  parseSemver: () => parseSemver,
25031
+ parseShareFence: () => parseShareFence,
25013
25032
  parseTriageFences: () => parseTriageFences,
25014
25033
  pendingAbstractions: () => pendingAbstractions,
25015
25034
  pendingDiscriminators: () => pendingDiscriminators,
@@ -25292,6 +25311,8 @@ function renderSnapshot(s) {
25292
25311
  lines.push(SEARCH_IMPERATIVE_HEADER);
25293
25312
  lines.push(SEARCH_IMPERATIVE_BODY);
25294
25313
  lines.push("");
25314
+ lines.push(s.isolated ? SUBAGENT_OWN_BLOCK_NOTE : SPAWN_SHARE_HINT);
25315
+ lines.push("");
25295
25316
  lines.push(`### Active workspace profile`);
25296
25317
  lines.push(`- **Name:** ${s.profile.name}`);
25297
25318
  const ctxName = (name2) => name2.startsWith("@") ? `\`${name2}\`` : name2;
@@ -25469,6 +25490,12 @@ function renderSnapshot(s) {
25469
25490
  }
25470
25491
  lines.push("");
25471
25492
  }
25493
+ if (s.sharedPriors?.length) {
25494
+ lines.push(`### \u{1F91D} Handed to you by the agent that spawned you`);
25495
+ lines.push("_It chose these for your task \u2014 cite one with its `[handle]` if you lean on it:_");
25496
+ for (const n of s.sharedPriors) lines.push(`- **${n.label}:** ${n.description}${tagOf(n)}`);
25497
+ lines.push("");
25498
+ }
25472
25499
  if (s.statedIntent) {
25473
25500
  const si = s.statedIntent;
25474
25501
  lines.push(`### \u{1F3AF} You said you're working on this`);
@@ -25650,6 +25677,34 @@ function budgetSnapshot(s, maxChars) {
25650
25677
  while (renderSnapshot(s).length > maxChars && dropLowestUnit(s)) dropped++;
25651
25678
  return dropped;
25652
25679
  }
25680
+ function isolateSnapshot(s) {
25681
+ const {
25682
+ workingFile: _workingFile,
25683
+ statedIntent: _statedIntent,
25684
+ selfCiteSkips: _selfCiteSkips,
25685
+ pendingUpdate: _pendingUpdate,
25686
+ daemonDistress: _daemonDistress,
25687
+ reviewItems: _reviewItems,
25688
+ causalNudge: _causalNudge,
25689
+ domainNudge: _domainNudge,
25690
+ sharedPriors: _sharedPriors,
25691
+ ...graphWide
25692
+ } = s;
25693
+ return {
25694
+ ...graphWide,
25695
+ isolated: true,
25696
+ recentProblems: [],
25697
+ recentResolved: [],
25698
+ regressions: [],
25699
+ recentConstraints: [],
25700
+ pendingEnrichment: [],
25701
+ needsRevisit: [],
25702
+ reviewCount: 0,
25703
+ motifs: [...s.motifs],
25704
+ skills: [...s.skills],
25705
+ ...s.remote ? { remote: [...s.remote] } : {}
25706
+ };
25707
+ }
25653
25708
  function assembleAgentContext(opts) {
25654
25709
  const snapshot = opts.snapshot ?? buildSnapshot({
25655
25710
  store: opts.store,
@@ -25666,6 +25721,7 @@ function assembleAgentContext(opts) {
25666
25721
  if (opts.daemonDistress) snapshot.daemonDistress = opts.daemonDistress;
25667
25722
  if (opts.reviewItems?.length) snapshot.reviewItems = [...opts.reviewItems];
25668
25723
  if (opts.statedIntent) snapshot.statedIntent = opts.statedIntent;
25724
+ if (opts.sharedPriors?.length) snapshot.sharedPriors = [...opts.sharedPriors];
25669
25725
  if (opts.remote && opts.remote.length > 0) {
25670
25726
  const seen = /* @__PURE__ */ new Set();
25671
25727
  const remote = opts.remote.filter((n) => {
@@ -25717,7 +25773,7 @@ function diffRenderLedger(before, after) {
25717
25773
  for (const i2 of before) (survived.has(key(i2)) ? shown : evicted).push(i2);
25718
25774
  return { shown, evicted };
25719
25775
  }
25720
- var RECALL_FIRST_HEADER, RECALL_FIRST_BODY, RECALL_FIRST_BLOCK, SEARCH_IMPERATIVE_HEADER, SEARCH_IMPERATIVE_BODY, EVICTION_ORDER, MOTIF_FLOOR, REMOTE_FLOOR, PROBLEM_FLOOR, REVIEW_ITEM_FLOOR, DEFAULT_AGENT_CONTEXT_BUDGET;
25776
+ var RECALL_FIRST_HEADER, RECALL_FIRST_BODY, RECALL_FIRST_BLOCK, SEARCH_IMPERATIVE_HEADER, SEARCH_IMPERATIVE_BODY, SPAWN_SHARE_HINT, SUBAGENT_OWN_BLOCK_NOTE, EVICTION_ORDER, MOTIF_FLOOR, REMOTE_FLOOR, PROBLEM_FLOOR, REVIEW_ITEM_FLOOR, DEFAULT_AGENT_CONTEXT_BUDGET;
25721
25777
  var init_render = __esm({
25722
25778
  "../../packages/context-writer/src/render.ts"() {
25723
25779
  "use strict";
@@ -25731,6 +25787,8 @@ var init_render = __esm({
25731
25787
  ${RECALL_FIRST_BODY}`;
25732
25788
  SEARCH_IMPERATIVE_HEADER = "### \u{1F50E} PRIORS ARE SEEDS \u2014 a sample, not the whole graph";
25733
25789
  SEARCH_IMPERATIVE_BODY = "Everything below is a budgeted slice of a much larger graph, not all errata knows. Starting a piece of work? Say what it is \u2014 we search on it between your tool calls and put what we find here:\n```errata-intent\nintent: <the work in hand, in your words>\n```\nNarration, not a detour: no tool call, no plan change. It is the one signal we cannot derive from your edits \u2014 everything else here is guessed from the files you touched.";
25790
+ SPAWN_SHARE_HINT = "_Spawning a subagent? It gets its own errata block, seeded from its task \u2014 none of the priors here. To hand it some, put this in its prompt (or `priors: all` for this whole block):_\n```errata-share\npriors: [handle], [handle]\n```";
25791
+ SUBAGENT_OWN_BLOCK_NOTE = "_This block is yours: seeded from the task you were given, then from what you state and touch. The agent that spawned you sees a different one._";
25734
25792
  EVICTION_ORDER = [
25735
25793
  "skills",
25736
25794
  // Review items beyond the floor drop immediately after skills — one naming
@@ -32004,6 +32062,9 @@ function errorCardHeadline(items) {
32004
32062
  const fixes = items.filter((it) => it.label === "Solution").length;
32005
32063
  return fixes > 0 ? `${plural(fixes, "known fix", "known fixes")} for this error` : `${plural(items.length, "prior")} for this error`;
32006
32064
  }
32065
+ function forSubagent(card, agentType) {
32066
+ return { ...card, headline: `\u21B3 ${agentType || "subagent"} \xB7 ${card.headline}` };
32067
+ }
32007
32068
  function leadItem(card) {
32008
32069
  return card.chain?.fix ?? card.items.find((it) => it.label === "Solution") ?? card.single ?? card.items[0];
32009
32070
  }
@@ -32083,6 +32144,29 @@ function rememberCard(sessionId, card, now = Date.now(), since = now) {
32083
32144
  if (current && current.at > since) return;
32084
32145
  remember(latestBySession, sessionId, { card, at: now });
32085
32146
  }
32147
+ function rememberAgentCard(sessionId, agentId, card, now = Date.now()) {
32148
+ if (!sessionId || !agentId) return;
32149
+ remember(latestByAgent, agentKey(sessionId, agentId), { card, at: now });
32150
+ }
32151
+ function subagentRows(body2, now = Date.now()) {
32152
+ const sessionId = typeof body2["session_id"] === "string" ? body2["session_id"] : "";
32153
+ const tasks = Array.isArray(body2["tasks"]) ? body2["tasks"] : [];
32154
+ const columns = typeof body2["columns"] === "number" && body2["columns"] > 20 ? body2["columns"] : 100;
32155
+ const lines = [];
32156
+ for (const task of tasks) {
32157
+ const id = typeof task["id"] === "string" ? task["id"] : "";
32158
+ const hit = id ? latestByAgent.get(agentKey(sessionId, id)) : void 0;
32159
+ if (!hit || now - hit.at > SUBAGENT_ROW_TTL_MS) continue;
32160
+ const lead2 = leadItem(hit.card);
32161
+ const title = String(task["description"] ?? task["label"] ?? "").trim();
32162
+ const plain = clip(
32163
+ `${title ? `${title} \xB7 ` : ""}\u26A1 errata \xB7 ${hit.card.headline}${lead2 ? ` \u2014 ${labelName(lead2.label)}: ${lead2.text}` : ""}`,
32164
+ columns - 2
32165
+ );
32166
+ lines.push(JSON.stringify({ id, content: plain.replace("\u26A1", `${AMBER}\u26A1${RESET}`) }));
32167
+ }
32168
+ return lines.join("\n");
32169
+ }
32086
32170
  function rememberOpenCount(sessionId, open2) {
32087
32171
  if (sessionId) remember(openBySession, sessionId, open2);
32088
32172
  }
@@ -32105,7 +32189,7 @@ function recallCardEnabled(env2 = process.env) {
32105
32189
  const v = (env2["ERRATA_RECALL_CARD"] ?? "").trim().toLowerCase();
32106
32190
  return !(v === "0" || v === "off" || v === "false" || v === "no");
32107
32191
  }
32108
- var LABEL_NAMES, INLINE_TEXT_WIDTH, ESC, RESET, DIM, BOLD, AMBER, MADDER, GREEN, INK, STATUS_ROWS, STATUS_TEXT_WIDTH, LABEL_WIDTH, STATUS_CARD_TTL_MS, IDLE_STATUS, latestBySession, MAX_SESSIONS, openBySession;
32192
+ var LABEL_NAMES, INLINE_TEXT_WIDTH, ESC, RESET, DIM, BOLD, AMBER, MADDER, GREEN, INK, STATUS_ROWS, STATUS_TEXT_WIDTH, LABEL_WIDTH, STATUS_CARD_TTL_MS, IDLE_STATUS, latestBySession, MAX_SESSIONS, latestByAgent, agentKey, SUBAGENT_ROW_TTL_MS, openBySession;
32109
32193
  var init_recall_card = __esm({
32110
32194
  "src/recall-card.ts"() {
32111
32195
  "use strict";
@@ -32129,6 +32213,9 @@ var init_recall_card = __esm({
32129
32213
  IDLE_STATUS = `${AMBER}\u26A1${RESET} ${DIM}errata \xB7 watching${RESET}`;
32130
32214
  latestBySession = /* @__PURE__ */ new Map();
32131
32215
  MAX_SESSIONS = 256;
32216
+ latestByAgent = /* @__PURE__ */ new Map();
32217
+ agentKey = (sessionId, agentId) => `${sessionId}#${agentId}`;
32218
+ SUBAGENT_ROW_TTL_MS = 30 * 6e4;
32132
32219
  openBySession = /* @__PURE__ */ new Map();
32133
32220
  }
32134
32221
  });
@@ -52721,6 +52808,7 @@ var init_message_accumulator = __esm({
52721
52808
  // src/webui.ts
52722
52809
  var webui_exports = {};
52723
52810
  __export(webui_exports, {
52811
+ SUBAGENT_REFRESH_MIN_MS: () => SUBAGENT_REFRESH_MIN_MS,
52724
52812
  asFailedPostToolUse: () => asFailedPostToolUse,
52725
52813
  buildWebUi: () => buildWebUi,
52726
52814
  decideContextInject: () => decideContextInject,
@@ -52728,6 +52816,7 @@ __export(webui_exports, {
52728
52816
  fileRecall: () => fileRecall,
52729
52817
  fileUriToFsPath: () => fileUriToFsPath,
52730
52818
  findFileByPath: () => findFileByPath,
52819
+ hookAudience: () => hookAudience,
52731
52820
  recallForFile: () => recallForFile,
52732
52821
  recallForTool: () => recallForTool
52733
52822
  });
@@ -52874,10 +52963,21 @@ ${lines.join("\n")}`,
52874
52963
  card: { headline: `${plural(items.length, "prior")} before this command runs`, items }
52875
52964
  };
52876
52965
  }
52966
+ function hookAudience(body2) {
52967
+ const sessionId = String(body2["session_id"] ?? "");
52968
+ const agentId = typeof body2["agent_id"] === "string" ? body2["agent_id"] : "";
52969
+ if (!agentId) return { sessionId, contextKey: sessionId, subagent: null, agentId: "" };
52970
+ return {
52971
+ sessionId,
52972
+ contextKey: sessionId ? `${sessionId}#${agentId}` : "",
52973
+ subagent: typeof body2["agent_type"] === "string" ? body2["agent_type"] : "",
52974
+ agentId
52975
+ };
52976
+ }
52877
52977
  function decideContextInject(args2) {
52878
52978
  const { event, sessionId, block, lastInjectedHash } = args2;
52879
52979
  if (!block) return null;
52880
- if (event === "SessionStart") {
52980
+ if (event === "SessionStart" || event === "SubagentStart") {
52881
52981
  if (sessionId) lastInjectedHash.set(sessionId, block.hash);
52882
52982
  } else if (sessionId && lastInjectedHash.get(sessionId) === block.hash) {
52883
52983
  return null;
@@ -52892,6 +52992,7 @@ function buildWebUi(deps) {
52892
52992
  const B = deps.basePath ?? "";
52893
52993
  const lastInjectedHash = /* @__PURE__ */ new Map();
52894
52994
  const fileRecallSeen = /* @__PURE__ */ new Map();
52995
+ const lastSubagentInjectAt = /* @__PURE__ */ new Map();
52895
52996
  app.get(
52896
52997
  "/",
52897
52998
  (c) => c.json({
@@ -53034,6 +53135,14 @@ function buildWebUi(deps) {
53034
53135
  if (hookEvent === "PostToolUseFailure") body2 = asFailedPostToolUse(body2);
53035
53136
  const event = hookEvent === "PostToolUseFailure" ? "PostToolUse" : hookEvent;
53036
53137
  let recall = null;
53138
+ const audience = hookAudience(body2);
53139
+ if (event === "PreToolUse" && (tool === "Agent" || tool === "Task") && audience.subagent === null) {
53140
+ try {
53141
+ deps.onSubagentSpawn?.(audience.sessionId, input);
53142
+ } catch {
53143
+ }
53144
+ return c.json({ ok: true, event, tool, spawn: true });
53145
+ }
53037
53146
  if (event === "PreToolUse") {
53038
53147
  const virtual = extractVirtualReads(input);
53039
53148
  if (virtual.length > 0) {
@@ -53062,7 +53171,10 @@ function buildWebUi(deps) {
53062
53171
  const workspaceRoot = (deps.paths.root ?? "").replace(/\\/g, "/");
53063
53172
  const fp = filePath.replace(/\\/g, "/");
53064
53173
  rel = fp.startsWith(workspaceRoot + "/") ? fp.slice(workspaceRoot.length + 1) : fp;
53065
- if (deps.onWorkingFileChanged) deps.onWorkingFileChanged(rel, String(body2["session_id"] ?? ""));
53174
+ if (deps.onWorkingFileChanged) {
53175
+ const agentId = typeof body2["agent_id"] === "string" && body2["agent_id"] ? body2["agent_id"] : void 0;
53176
+ deps.onWorkingFileChanged(rel, audience.sessionId, agentId);
53177
+ }
53066
53178
  }
53067
53179
  if (rel && event === "PostToolUse" && deps.onHookEvent) {
53068
53180
  deps.onHookEvent({
@@ -53130,7 +53242,7 @@ function buildWebUi(deps) {
53130
53242
  }
53131
53243
  }
53132
53244
  if (event === "PostToolUse" && !recall && rel && (tool === "Read" || tool === "Edit" || tool === "Write" || tool === "NotebookEdit")) {
53133
- const sid = String(body2["session_id"] ?? "");
53245
+ const sid = hookAudience(body2).contextKey;
53134
53246
  let seen = fileRecallSeen.get(sid);
53135
53247
  if (!seen) {
53136
53248
  seen = /* @__PURE__ */ new Set();
@@ -53144,13 +53256,46 @@ function buildWebUi(deps) {
53144
53256
  }
53145
53257
  }
53146
53258
  }
53147
- if ((event === "PostToolUse" || event === "PreToolUse") && recall) {
53259
+ let refreshed = null;
53260
+ if (event === "PostToolUse" && audience.subagent !== null && audience.contextKey && deps.subagentContext) {
53261
+ const last = lastSubagentInjectAt.get(audience.contextKey) ?? 0;
53262
+ if (Date.now() - last >= SUBAGENT_REFRESH_MIN_MS) {
53263
+ try {
53264
+ const own = deps.subagentContext({
53265
+ sessionId: audience.sessionId,
53266
+ agentId: audience.agentId,
53267
+ agentType: audience.subagent,
53268
+ start: false
53269
+ });
53270
+ const decision = decideContextInject({
53271
+ event,
53272
+ source: "",
53273
+ sessionId: audience.contextKey,
53274
+ block: own,
53275
+ lastInjectedHash
53276
+ });
53277
+ if (decision) {
53278
+ lastSubagentInjectAt.set(audience.contextKey, Date.now());
53279
+ refreshed = { additionalContext: decision.additionalContext, card: own?.card ?? null };
53280
+ }
53281
+ } catch {
53282
+ }
53283
+ }
53284
+ }
53285
+ if ((event === "PostToolUse" || event === "PreToolUse") && (recall || refreshed)) {
53148
53286
  const showCard = recallCardEnabled();
53149
- if (showCard) rememberCard(String(body2["session_id"] ?? ""), recall.card);
53150
- const line = showCard ? renderRecallLine(recall.card) : null;
53287
+ const { sessionId, subagent } = audience;
53288
+ const raw2 = recall?.card ?? refreshed?.card ?? null;
53289
+ const card = raw2 && subagent !== null ? forSubagent(raw2, subagent) : raw2;
53290
+ if (showCard && raw2 && card) {
53291
+ if (subagent !== null) rememberAgentCard(sessionId, audience.agentId, raw2);
53292
+ else rememberCard(sessionId, card);
53293
+ }
53294
+ const line = showCard && recall && card ? renderRecallLine(card) : null;
53295
+ const additionalContext = [recall?.context, refreshed?.additionalContext].filter(Boolean).join("\n\n");
53151
53296
  return c.json({
53152
53297
  ...line ? { systemMessage: line } : {},
53153
- hookSpecificOutput: { hookEventName: hookEvent, additionalContext: recall.context }
53298
+ hookSpecificOutput: { hookEventName: hookEvent, additionalContext }
53154
53299
  });
53155
53300
  }
53156
53301
  if (event === "PostToolUse") return c.body(null, 204);
@@ -53160,6 +53305,10 @@ function buildWebUi(deps) {
53160
53305
  const body2 = await c.req.json().catch(() => ({}));
53161
53306
  return c.text(statuslineText(String(body2["session_id"] ?? "")));
53162
53307
  });
53308
+ app.post("/api/statusline/subagents", async (c) => {
53309
+ const body2 = await c.req.json().catch(() => ({}));
53310
+ return c.text(subagentRows(body2));
53311
+ });
53163
53312
  app.post("/api/turn", async (c) => {
53164
53313
  let body2;
53165
53314
  try {
@@ -53172,6 +53321,9 @@ function buildWebUi(deps) {
53172
53321
  if (transcriptPath && deps.onTurn) {
53173
53322
  deps.onTurn({ sessionId, transcriptPath });
53174
53323
  }
53324
+ if (body2["hook_event_name"] === "SubagentStop" && typeof body2["agent_id"] === "string" && body2["agent_id"]) {
53325
+ deps.onSubagentStop?.(body2["agent_id"]);
53326
+ }
53175
53327
  return c.json({ ok: true });
53176
53328
  });
53177
53329
  app.post("/api/message", async (c) => {
@@ -53188,7 +53340,13 @@ function buildWebUi(deps) {
53188
53340
  const isFinal = body2["final"] !== false;
53189
53341
  const whole = accumulateMessageDelta(messageId, index, delta, isFinal, Date.now());
53190
53342
  if (whole !== null && whole.text && sessionId && deps.onMessage) {
53191
- deps.onMessage({ sessionId, text: whole.text, ...messageId ? { messageId } : {} });
53343
+ const agentId = typeof body2["agent_id"] === "string" && body2["agent_id"] ? body2["agent_id"] : void 0;
53344
+ deps.onMessage({
53345
+ sessionId,
53346
+ text: whole.text,
53347
+ ...messageId ? { messageId } : {},
53348
+ ...agentId ? { agentId } : {}
53349
+ });
53192
53350
  }
53193
53351
  return c.json({ ok: true, ...whole === null ? { buffered: true } : {} });
53194
53352
  });
@@ -53213,9 +53371,9 @@ function buildWebUi(deps) {
53213
53371
  } catch {
53214
53372
  return c.json({});
53215
53373
  }
53216
- const sid = String(body2["session_id"] ?? "");
53374
+ const { sessionId: sid, contextKey: contextKey2, subagent, agentId } = hookAudience(body2);
53217
53375
  const hookEvent = String(body2["hook_event_name"] ?? "UserPromptSubmit");
53218
- const atPrompt = hookEvent === "UserPromptSubmit" || hookEvent === "SessionStart";
53376
+ const atPrompt = hookEvent === "UserPromptSubmit" || hookEvent === "SessionStart" || hookEvent === "SubagentStart";
53219
53377
  if (sid && recallCardEnabled() && atPrompt) {
53220
53378
  setImmediate(() => {
53221
53379
  try {
@@ -53224,20 +53382,38 @@ function buildWebUi(deps) {
53224
53382
  }
53225
53383
  });
53226
53384
  }
53227
- const block = readContextFileWithHash(deps.paths.root);
53385
+ let block;
53386
+ let storedCard = null;
53387
+ if (subagent !== null) {
53388
+ const own = deps.subagentContext?.({
53389
+ sessionId: sid,
53390
+ agentId,
53391
+ agentType: subagent,
53392
+ ...typeof body2["task_description"] === "string" ? { taskDescription: body2["task_description"] } : {},
53393
+ start: hookEvent === "SubagentStart"
53394
+ }) ?? null;
53395
+ block = own;
53396
+ storedCard = own?.card ?? null;
53397
+ } else {
53398
+ block = readContextFileWithHash(deps.paths.root);
53399
+ storedCard = block ? readContextCard(deps.paths.root, block.hash) : null;
53400
+ }
53228
53401
  const decision = decideContextInject({
53229
53402
  event: hookEvent,
53230
53403
  source: String(body2["source"] ?? ""),
53231
- sessionId: sid,
53404
+ sessionId: contextKey2,
53232
53405
  block,
53233
53406
  lastInjectedHash
53234
53407
  });
53408
+ if (decision && subagent !== null) lastSubagentInjectAt.set(contextKey2, Date.now());
53235
53409
  let systemMessage = null;
53236
53410
  if (sid && block && recallCardEnabled() && (decision || atPrompt)) {
53237
- const card = readContextCard(deps.paths.root, block.hash);
53411
+ const card = storedCard && subagent !== null ? forSubagent(storedCard, subagent) : storedCard;
53238
53412
  if (card) {
53239
- rememberCard(sid, card);
53240
- if (decision && sid.startsWith("codex:")) systemMessage = renderRecallLine(card);
53413
+ if (subagent !== null && storedCard) rememberAgentCard(sid, agentId, storedCard);
53414
+ else rememberCard(sid, card);
53415
+ if (decision && hookEvent === "SubagentStart") systemMessage = renderRecallLine(card);
53416
+ else if (decision && sid.startsWith("codex:")) systemMessage = renderRecallLine(card);
53241
53417
  }
53242
53418
  }
53243
53419
  if (!decision) return c.json({});
@@ -53423,7 +53599,7 @@ function proposalToIngest(p, profile, daemonVersion) {
53423
53599
  payloadDigest: p.id
53424
53600
  };
53425
53601
  }
53426
- var messageAccumulator, GENERIC_ERROR_WORDS, wsPackagesCache, WS_PACKAGES_TTL_MS, loggedToolSessions, META_LABELS;
53602
+ var messageAccumulator, GENERIC_ERROR_WORDS, wsPackagesCache, WS_PACKAGES_TTL_MS, loggedToolSessions, SUBAGENT_REFRESH_MIN_MS, META_LABELS;
53427
53603
  var init_webui = __esm({
53428
53604
  "src/webui.ts"() {
53429
53605
  "use strict";
@@ -53484,6 +53660,7 @@ var init_webui = __esm({
53484
53660
  wsPackagesCache = /* @__PURE__ */ new WeakMap();
53485
53661
  WS_PACKAGES_TTL_MS = 6e4;
53486
53662
  loggedToolSessions = /* @__PURE__ */ new Set();
53663
+ SUBAGENT_REFRESH_MIN_MS = 6e4;
53487
53664
  META_LABELS = /* @__PURE__ */ new Set([
53488
53665
  "Pattern",
53489
53666
  "Technique",
@@ -75365,6 +75542,154 @@ async function chat(req) {
75365
75542
  init_src11();
75366
75543
  init_src6();
75367
75544
  init_recall_signal();
75545
+
75546
+ // src/subagent-state.ts
75547
+ init_src5();
75548
+ var SPAWN_CLAIM_TTL_MS = 2 * 6e4;
75549
+ var SUBAGENT_IDLE_TTL_MS = 60 * 6e4;
75550
+ var MAX_AGENTS = 200;
75551
+ var SEED_INTENT_MAX_CHARS = 400;
75552
+ function seedIntentOf(toolInput) {
75553
+ const prompt = typeof toolInput["prompt"] === "string" ? toolInput["prompt"] : "";
75554
+ const stated = parseIntentFences(prompt)[0]?.intent;
75555
+ if (stated) return stated;
75556
+ const description = typeof toolInput["description"] === "string" ? toolInput["description"].trim() : "";
75557
+ const body2 = prompt.replace(/```errata-[\s\S]*?```/g, " ").replace(/\s+/g, " ").trim();
75558
+ const seed = [description, body2].filter(Boolean).join(" \u2014 ");
75559
+ return seed.length > SEED_INTENT_MAX_CHARS ? `${seed.slice(0, SEED_INTENT_MAX_CHARS - 1)}\u2026` : seed;
75560
+ }
75561
+ function createSubagentState() {
75562
+ const pending = /* @__PURE__ */ new Map();
75563
+ const agents = /* @__PURE__ */ new Map();
75564
+ const sweep = (nowMs) => {
75565
+ for (const [id, a] of agents) if (nowMs - a.lastSeenMs > SUBAGENT_IDLE_TTL_MS) agents.delete(id);
75566
+ while (agents.size > MAX_AGENTS) agents.delete(agents.keys().next().value);
75567
+ for (const [sid, list] of pending) {
75568
+ const live3 = list.filter((s) => nowMs - s.atMs <= SPAWN_CLAIM_TTL_MS);
75569
+ if (live3.length) pending.set(sid, live3);
75570
+ else pending.delete(sid);
75571
+ }
75572
+ };
75573
+ const touch = (agentId, nowMs) => {
75574
+ const a = agents.get(agentId);
75575
+ if (!a) return void 0;
75576
+ if (nowMs - a.lastSeenMs > SUBAGENT_IDLE_TTL_MS) {
75577
+ agents.delete(agentId);
75578
+ return void 0;
75579
+ }
75580
+ a.lastSeenMs = nowMs;
75581
+ return a;
75582
+ };
75583
+ return {
75584
+ noteSpawn(sessionId, toolInput, nowMs) {
75585
+ sweep(nowMs);
75586
+ const prompt = typeof toolInput["prompt"] === "string" ? toolInput["prompt"] : "";
75587
+ const agentType = typeof toolInput["subagent_type"] === "string" ? toolInput["subagent_type"] : "";
75588
+ const list = pending.get(sessionId) ?? [];
75589
+ list.push({ agentType, seedIntent: seedIntentOf(toolInput), share: parseShareFence(prompt), atMs: nowMs });
75590
+ pending.set(sessionId, list);
75591
+ },
75592
+ start({ sessionId, agentId, agentType, taskDescription }, nowMs) {
75593
+ const existing = touch(agentId, nowMs);
75594
+ if (existing) return existing;
75595
+ sweep(nowMs);
75596
+ const list = pending.get(sessionId) ?? [];
75597
+ const i2 = list.findIndex((s) => s.agentType === agentType || s.agentType === "");
75598
+ const spawn6 = i2 >= 0 ? list.splice(i2, 1)[0] : void 0;
75599
+ const seed = spawn6?.seedIntent || (taskDescription ?? "").trim();
75600
+ const entry = {
75601
+ sessionId,
75602
+ agentType,
75603
+ ...seed ? { intent: seed } : {},
75604
+ ownIntent: false,
75605
+ share: spawn6?.share ?? null,
75606
+ lastSeenMs: nowMs
75607
+ };
75608
+ agents.set(agentId, entry);
75609
+ return entry;
75610
+ },
75611
+ get: (agentId, nowMs) => touch(agentId, nowMs),
75612
+ setIntent(agentId, text, nowMs) {
75613
+ const a = touch(agentId, nowMs);
75614
+ const trimmed = text.trim();
75615
+ if (!a || !trimmed) return false;
75616
+ const changed = a.intent !== trimmed;
75617
+ a.intent = trimmed;
75618
+ a.ownIntent = true;
75619
+ return changed;
75620
+ },
75621
+ setWorkingFile(agentId, rel, nowMs) {
75622
+ const a = touch(agentId, nowMs);
75623
+ if (!a || a.workingFile === rel) return false;
75624
+ a.workingFile = rel;
75625
+ return true;
75626
+ },
75627
+ stop(agentId) {
75628
+ agents.delete(agentId);
75629
+ }
75630
+ };
75631
+ }
75632
+
75633
+ // src/subagent-context.ts
75634
+ init_src6();
75635
+ init_src5();
75636
+ init_src2();
75637
+ init_recall_signal();
75638
+ var MAX_SHARED_PRIORS = 8;
75639
+ var INTENT_HITS = 4;
75640
+ function resolveSharedPriors(store, wanted, handleMap, remote) {
75641
+ const out2 = [];
75642
+ const seen = /* @__PURE__ */ new Set();
75643
+ for (const ref of wanted) {
75644
+ const id = handleMap[ref]?.id ?? ref;
75645
+ const node2 = store.getNode(id) ?? remote.find((n) => n.id === id || priorHandle(n) === ref) ?? void 0;
75646
+ if (!node2 || seen.has(node2.id)) continue;
75647
+ seen.add(node2.id);
75648
+ out2.push(node2);
75649
+ if (out2.length >= MAX_SHARED_PRIORS) break;
75650
+ }
75651
+ return out2;
75652
+ }
75653
+ function renderSubagentBlock(args2) {
75654
+ const { store, entry } = args2;
75655
+ const snapshot = isolateSnapshot(structuredClone(args2.base));
75656
+ if (entry.workingFile) {
75657
+ const slice = sliceForFile(store, entry.workingFile, args2.atCommit);
75658
+ if (slice) snapshot.workingFile = slice;
75659
+ }
75660
+ const hits = entry.intent ? searchByIntent(store, entry.intent, { limit: INTENT_HITS }).map((h) => ({
75661
+ id: h.node.id,
75662
+ label: h.node.label,
75663
+ description: h.node.description,
75664
+ coverage: h.coverage
75665
+ })) : [];
75666
+ const shared = entry.share && !entry.share.all ? resolveSharedPriors(store, entry.share.priors, args2.handleMap, args2.remote) : [];
75667
+ const { body: body2, snapshot: shown } = assembleAgentContext({
75668
+ store,
75669
+ profile: args2.profile,
75670
+ ...args2.atCommit ? { atCommit: args2.atCommit } : {},
75671
+ reviewCount: 0,
75672
+ reviewUiUrl: args2.reviewUiUrl,
75673
+ remote: args2.remote,
75674
+ snapshot,
75675
+ ...args2.edgeElicitation ? { edgeElicitation: args2.edgeElicitation } : {},
75676
+ ...entry.intent ? { statedIntent: { text: entry.intent, hits } } : {},
75677
+ ...shared.length ? { sharedPriors: shared } : {}
75678
+ });
75679
+ const profileNodes = [
75680
+ ...shown.profileContext.languages,
75681
+ ...shown.profileContext.packages,
75682
+ ...shown.profileContext.tools
75683
+ ].flatMap((e) => e.node ? [e.node] : []);
75684
+ return {
75685
+ body: body2,
75686
+ hash: contextHash(body2),
75687
+ card: contextCard(store, shown),
75688
+ handleNodes: [...shown.sharedPriors ?? [], ...shown.motifs, ...shown.remote ?? [], ...profileNodes]
75689
+ };
75690
+ }
75691
+
75692
+ // src/engine.ts
75368
75693
  init_src8();
75369
75694
  init_review2();
75370
75695
 
@@ -81593,7 +81918,7 @@ function createWatchBreaker(deps) {
81593
81918
  }
81594
81919
 
81595
81920
  // src/engine.ts
81596
- var DAEMON_VERSION = true ? "2.0.17-dev.3407" : "2.0.0-alpha.0";
81921
+ var DAEMON_VERSION = true ? "2.0.17-dev.3421" : "2.0.0-alpha.0";
81597
81922
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
81598
81923
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
81599
81924
  var GIT_OP_MUTE_MS = 4e3;
@@ -81991,6 +82316,10 @@ function createWorkspaceEngine(opts) {
81991
82316
  witnessParked: () => witnessQueue.length
81992
82317
  });
81993
82318
  const intents = createIntentState();
82319
+ const subagents = createSubagentState();
82320
+ let subagentBase = null;
82321
+ let subagentBaseVersion = 0;
82322
+ const subagentRenders = /* @__PURE__ */ new Map();
81994
82323
  const toolRuns = createToolRunState();
81995
82324
  let ctxRefreshTimer = null;
81996
82325
  let contextDirty = false;
@@ -82068,6 +82397,7 @@ function createWorkspaceEngine(opts) {
82068
82397
  contextDirty = true;
82069
82398
  return;
82070
82399
  }
82400
+ const baseForSubagents = prebuilt ? structuredClone(prebuilt) : null;
82071
82401
  const doneRender = markPass(`${prebuilt ? "context-render" : "context-render-inline"}:${profile.name}`);
82072
82402
  const pendingUpdate = opts.pendingUpdate?.() ?? null;
82073
82403
  const daemonDistress = opts.daemonDistress?.() ?? null;
@@ -82106,6 +82436,8 @@ function createWorkspaceEngine(opts) {
82106
82436
  } : {}
82107
82437
  });
82108
82438
  doneRender();
82439
+ subagentBase = baseForSubagents ?? structuredClone(snapshot);
82440
+ subagentBaseVersion++;
82109
82441
  try {
82110
82442
  const seq = store.currentIngestSeq();
82111
82443
  const stamped = recordRenderLedger(store, ledger, seq, Date.now());
@@ -82408,7 +82740,11 @@ function createWorkspaceEngine(opts) {
82408
82740
  }
82409
82741
  return { report, localSkills: 0 };
82410
82742
  };
82411
- const onWorkingFileChanged = (rel, sessionId) => {
82743
+ const onWorkingFileChanged = (rel, sessionId, agentId) => {
82744
+ if (agentId) {
82745
+ subagents.setWorkingFile(agentId, rel, Date.now());
82746
+ return;
82747
+ }
82412
82748
  if (!workingFiles.set(sessionId, rel)) return;
82413
82749
  contextDirty = true;
82414
82750
  if (ctxRefreshTimer) clearTimeout(ctxRefreshTimer);
@@ -82419,6 +82755,48 @@ function createWorkspaceEngine(opts) {
82419
82755
  }
82420
82756
  }, 500);
82421
82757
  };
82758
+ const onSubagentSpawn = (sessionId, toolInput) => {
82759
+ subagents.noteSpawn(sessionId, toolInput, Date.now());
82760
+ };
82761
+ const subagentContext = (a) => {
82762
+ const now = Date.now();
82763
+ const entry = a.start ? subagents.start(a, now) : subagents.get(a.agentId, now);
82764
+ if (!entry) return null;
82765
+ if (entry.share?.all) {
82766
+ const file2 = readContextFileWithHash(opts.workspaceRoot);
82767
+ return file2 ? { body: file2.body, hash: file2.hash, card: readContextCard(opts.workspaceRoot, file2.hash), handleNodes: [] } : null;
82768
+ }
82769
+ if (!subagentBase) {
82770
+ contextDirty = true;
82771
+ return null;
82772
+ }
82773
+ const key = JSON.stringify([subagentBaseVersion, lastRemoteAt, entry.intent, entry.workingFile, entry.share]);
82774
+ const cached2 = subagentRenders.get(a.agentId);
82775
+ if (cached2?.key === key) return cached2.block;
82776
+ const elicit = isEdgeElicitationEnabled();
82777
+ const handlesPath = join35(paths.configDir, "priming-handles.json");
82778
+ const atCommit = readGitRefState(join35(opts.workspaceRoot, ".git")).sha ?? void 0;
82779
+ const block = renderSubagentBlock({
82780
+ store,
82781
+ entry,
82782
+ base: subagentBase,
82783
+ profile,
82784
+ ...atCommit ? { atCommit } : {},
82785
+ reviewUiUrl: opts.reviewUrl(),
82786
+ remote: remotePriors,
82787
+ handleMap: readPrimingHandles(handlesPath),
82788
+ ...elicit ? { edgeElicitation: { instruction: PRIOR_TAG_INSTRUCTION } } : {}
82789
+ });
82790
+ if (elicit && block.handleNodes.length) writePrimingHandles(handlesPath, block.handleNodes);
82791
+ subagentRenders.delete(a.agentId);
82792
+ subagentRenders.set(a.agentId, { key, block });
82793
+ if (subagentRenders.size > 200) subagentRenders.delete(subagentRenders.keys().next().value);
82794
+ return block;
82795
+ };
82796
+ const onSubagentStop = (agentId) => {
82797
+ subagents.stop(agentId);
82798
+ subagentRenders.delete(agentId);
82799
+ };
82422
82800
  const onHookEvent = (e) => {
82423
82801
  const intentHints = deriveIntentHints(e.tool, e.oldString, e.newString);
82424
82802
  const operation = /(?:edit|write|patch)/i.test(e.tool) ? "write" : /read/i.test(e.tool) ? "read" : null;
@@ -82668,8 +83046,10 @@ function createWorkspaceEngine(opts) {
82668
83046
  }
82669
83047
  const touched = new Set(toolRuns.get(sessionId));
82670
83048
  if (touchedFileId) touched.add(touchedFileId);
83049
+ const fenceAgent = turn.agentId ?? (sessionId.startsWith("agent-") ? sessionId.slice(6) : void 0);
82671
83050
  for (const fence of parseIntentFences(turn.text)) {
82672
- if (intents.set(sessionId, fence.intent, t)) refreshContextNow();
83051
+ if (fenceAgent) subagents.setIntent(fenceAgent, fence.intent, t);
83052
+ else if (intents.set(sessionId, fence.intent, t)) refreshContextNow();
82673
83053
  }
82674
83054
  const plan = harvestInlineTags(store, turn.text, {
82675
83055
  sourceId: sessionLastProblem.get(sessionId),
@@ -83493,7 +83873,7 @@ function createWorkspaceEngine(opts) {
83493
83873
  }
83494
83874
  }
83495
83875
  }
83496
- setImmediate(() => void harvestTexts(e.sessionId, [{ text: e.text }]));
83876
+ setImmediate(() => void harvestTexts(e.sessionId, [{ text: e.text, ...e.agentId ? { agentId: e.agentId } : {} }]));
83497
83877
  };
83498
83878
  const harvestInFlight = /* @__PURE__ */ new Map();
83499
83879
  const repairedSessions = /* @__PURE__ */ new Set();
@@ -83732,6 +84112,9 @@ B: ${b}`,
83732
84112
  telemetry,
83733
84113
  webHooks: {
83734
84114
  onWorkingFileChanged,
84115
+ onSubagentSpawn,
84116
+ subagentContext,
84117
+ onSubagentStop,
83735
84118
  onHook: () => telemetry.count("hooks_fired"),
83736
84119
  onToolsObserved: (sessionId, toolNodeIds) => toolRuns.record(sessionId, toolNodeIds),
83737
84120
  onHookEvent,
@@ -84129,6 +84512,9 @@ async function startDaemon(opts) {
84129
84512
  profile: engine.profile,
84130
84513
  daemonVersion: DAEMON_VERSION,
84131
84514
  onWorkingFileChanged: engine.webHooks.onWorkingFileChanged,
84515
+ onSubagentSpawn: engine.webHooks.onSubagentSpawn,
84516
+ subagentContext: engine.webHooks.subagentContext,
84517
+ onSubagentStop: engine.webHooks.onSubagentStop,
84132
84518
  onHook: engine.webHooks.onHook,
84133
84519
  onHookEvent: engine.webHooks.onHookEvent,
84134
84520
  onShellEvent: engine.webHooks.onShellEvent,
@@ -86842,7 +87228,14 @@ function stage(profile, patch, now) {
86842
87228
  }
86843
87229
  };
86844
87230
  }
86845
- function unitTiming(postStarted, prepMs, receipt) {
87231
+ function isEdgeContentBlock(err2) {
87232
+ return isIntermediaryRejection(err2) && err2 instanceof CloudError && err2.status !== 408 && err2.status !== 429;
87233
+ }
87234
+ function stageMs(value) {
87235
+ const ms = Number.parseInt(value, 10);
87236
+ return Number.isFinite(ms) ? ms : -1;
87237
+ }
87238
+ function unitTiming(postStarted, prepMs, receipt, nowMs = Date.now()) {
86846
87239
  const s = (ms) => `${(ms / 1e3).toFixed(1)}s`;
86847
87240
  const parts2 = [`local ${s(prepMs)}`];
86848
87241
  const g = receipt.gate;
@@ -86850,10 +87243,13 @@ function unitTiming(postStarted, prepMs, receipt) {
86850
87243
  if (receipt.timing?.modelMs !== void 0) parts2.push(`model ${s(receipt.timing.modelMs)}`);
86851
87244
  if (receipt.timing?.orderWaitMs) parts2.push(`waited ${s(receipt.timing.orderWaitMs)} for the previous write`);
86852
87245
  if (receipt.timing?.ingestMs !== void 0) {
86853
- const top = Object.entries(receipt.timing.ingestStages ?? {}).slice(0, 4).map(([k, v]) => `${k} ${v}`);
87246
+ const top = Object.entries(receipt.timing.ingestStages ?? {}).sort((a, b) => stageMs(b[1]) - stageMs(a[1])).slice(0, 4).map(([k, v]) => `${k} ${v}`);
86854
87247
  parts2.push(`ingest ${s(receipt.timing.ingestMs)}${top.length ? ` [${top.join(", ")}]` : ""}`);
86855
87248
  }
86856
- return ` in ${s(Date.now() - postStarted + prepMs)} (${parts2.join(", ")})`;
87249
+ if (receipt.usage && (receipt.usage.input_tokens || receipt.usage.output_tokens)) {
87250
+ parts2.push(`tokens ${receipt.usage.input_tokens}/${receipt.usage.output_tokens}`);
87251
+ }
87252
+ return ` in ${s(nowMs - postStarted + prepMs)} (${parts2.join(", ")})`;
86857
87253
  }
86858
87254
  function addCounts(a, b) {
86859
87255
  const sum = (k) => (a[k] ?? 0) + (b[k] ?? 0);
@@ -86920,8 +87316,9 @@ async function walk10(root, input, client, opts, readBlob) {
86920
87316
  const prepared = /* @__PURE__ */ new Map();
86921
87317
  const launched = /* @__PURE__ */ new Map();
86922
87318
  const cap = opts.maxUnits;
87319
+ const edgeParked = /* @__PURE__ */ new Set();
86923
87320
  const predecessor = (k) => {
86924
- for (let j = k - 1; j >= 0; j--) if (!units[j].skip) return units[j];
87321
+ for (let j = k - 1; j >= 0; j--) if (!units[j].skip && !edgeParked.has(j)) return units[j];
86925
87322
  return void 0;
86926
87323
  };
86927
87324
  const prepare = (k) => {
@@ -87025,17 +87422,28 @@ async function walk10(root, input, client, opts, readBlob) {
87025
87422
  let first = head2.settled;
87026
87423
  let outcome = null;
87027
87424
  let attempt = 0;
87425
+ let edgeBlocks = 0;
87426
+ let parkedAtEdge = false;
87028
87427
  while (outcome === null) {
87029
87428
  attempt += 1;
87030
87429
  if (!first && body2.backfill && body2.backfill.anchorSeq === void 0 && anchor.seq !== void 0) {
87031
87430
  body2.backfill = { ...body2.backfill, anchorSeq: anchor.seq };
87032
87431
  }
87432
+ if (!first && body2.after) {
87433
+ const prev = predecessor(k);
87434
+ if (!prev) delete body2.after;
87435
+ else if (prev.sha !== body2.after.sha || prev.subIndex !== body2.after.subIndex) body2.after = { sha: prev.sha, subIndex: prev.subIndex };
87436
+ }
87033
87437
  const settled = await (first ?? settle(Promise.resolve().then(() => post(projectId, body2))));
87034
87438
  first = null;
87035
87439
  if ("err" in settled) {
87036
87440
  const err2 = settled.err;
87037
87441
  const status = err2 instanceof CloudError ? err2.status : 0;
87038
87442
  const elapsed = Math.round((Date.now() - started2) / 1e3);
87443
+ if (isEdgeContentBlock(err2) && ++edgeBlocks >= 2) {
87444
+ parkedAtEdge = true;
87445
+ break;
87446
+ }
87039
87447
  if (Date.now() - started2 > RECOVERY_DEADLINE_MS) {
87040
87448
  profile = stage(profile, { state: "paused", lastError: `unit ${label}: ${status || "connection"} after ${elapsed}s, giving up until the next run` }, now);
87041
87449
  persist(profile);
@@ -87069,6 +87477,19 @@ async function walk10(root, input, client, opts, readBlob) {
87069
87477
  }
87070
87478
  outcome = res;
87071
87479
  }
87480
+ if (parkedAtEdge || outcome === null) {
87481
+ edgeParked.add(k);
87482
+ const runId = `edge-blocked:${u.sha}/${u.subIndex}`;
87483
+ const gaps = [...profile.historyStage?.coverageGaps ?? []];
87484
+ const known = gaps.some((g) => g.runId === runId);
87485
+ counts = addCounts(counts, { commitsWalked: 1, deferredUnits: known ? 0 : 1 });
87486
+ if (!known) gaps.push({ sha: u.sha, subIndex: u.subIndex, runId, reason: "edge-blocked" });
87487
+ profile = stage(profile, { coverageGaps: gaps.slice(-1e3), unitsDone: doneSoFar, counts, ...lastInPosition ? { watermarkSha: positionSha } : {} }, now);
87488
+ persist(profile);
87489
+ processed += 1;
87490
+ log(`[errata] history ${label}: refused at the edge (HTML ${edgeBlocks}\xD7 on its text); coverage gap retained as ${runId}, walking on`);
87491
+ continue;
87492
+ }
87072
87493
  await launchAhead(k);
87073
87494
  switch (outcome.status) {
87074
87495
  case "completed": {
@@ -88313,6 +88734,10 @@ async function startMultiDaemon(opts = {}) {
88313
88734
  daemonVersion: DAEMON_VERSION,
88314
88735
  basePath: `/ws/${id}`,
88315
88736
  onWorkingFileChanged: engine.webHooks.onWorkingFileChanged,
88737
+ // CAP-subagent-prime: each Task-tool subagent's own block.
88738
+ onSubagentSpawn: engine.webHooks.onSubagentSpawn,
88739
+ subagentContext: engine.webHooks.subagentContext,
88740
+ onSubagentStop: engine.webHooks.onSubagentStop,
88316
88741
  onHook: engine.webHooks.onHook,
88317
88742
  onHookEvent: engine.webHooks.onHookEvent,
88318
88743
  onShellEvent: engine.webHooks.onShellEvent,
@@ -88629,6 +89054,19 @@ async function startMultiDaemon(opts = {}) {
88629
89054
  }
88630
89055
  return c.text(statuslineText(String(body2["session_id"] ?? "")));
88631
89056
  });
89057
+ app.post("/api/statusline/subagents", async (c) => {
89058
+ const body2 = await c.req.json().catch(() => ({}));
89059
+ const dir = statuslineDir(body2);
89060
+ if (dir !== null) {
89061
+ const d = normPath(dir);
89062
+ const known = listWorkspaceEntries().some((e) => {
89063
+ const root = normPath(e.path);
89064
+ return d === root || d.startsWith(`${root}/`);
89065
+ });
89066
+ if (!known) return c.text("");
89067
+ }
89068
+ return c.text(subagentRows(body2));
89069
+ });
88632
89070
  for (const r of records) {
88633
89071
  mountedIds.add(r.id);
88634
89072
  app.route(`/ws/${r.id}`, r.webApp);
@@ -90727,8 +91165,9 @@ import { readFileSync } from 'node:fs';
90727
91165
 
90728
91166
  try {
90729
91167
  const port = Number(process.argv[2]);
91168
+ const route = process.argv[3] === 'subagents' ? '/api/statusline/subagents' : '/api/statusline';
90730
91169
  if (Number.isInteger(port) && port > 0 && port < 65536) {
90731
- const response = await fetch('http://127.0.0.1:' + port + '/api/statusline', {
91170
+ const response = await fetch('http://127.0.0.1:' + port + route, {
90732
91171
  method: 'POST', headers: { 'Content-Type': 'application/json' },
90733
91172
  body: readFileSync(0, 'utf8') || '{}', signal: AbortSignal.timeout(800),
90734
91173
  });
@@ -90738,20 +91177,30 @@ try {
90738
91177
  `;
90739
91178
  var STATUSLINE_RELAY_FILE = "errata-statusline.mjs";
90740
91179
  function buildStatusLine(port, nodePath, relayPath) {
91180
+ return { type: "command", command: relayCommand(port, nodePath, relayPath), padding: 0 };
91181
+ }
91182
+ function buildSubagentStatusLine(port, nodePath, relayPath) {
91183
+ return { type: "command", command: `${relayCommand(port, nodePath, relayPath)} subagents` };
91184
+ }
91185
+ function relayCommand(port, nodePath, relayPath) {
90741
91186
  if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("Invalid status line port");
90742
91187
  const q = (p) => {
90743
91188
  if (/["$`\r\n]/.test(p)) throw new Error("Unsupported character in status line path");
90744
91189
  return `"${p.replace(/\\/g, "/")}"`;
90745
91190
  };
90746
- return { type: "command", command: `${q(nodePath)} ${q(relayPath)} ${port}`, padding: 0 };
91191
+ return `${q(nodePath)} ${q(relayPath)} ${port}`;
90747
91192
  }
90748
91193
  function isErrataStatusLine(value) {
90749
91194
  return !!value && typeof value === "object" && typeof value["command"] === "string" && value["command"].includes(STATUSLINE_RELAY_FILE);
90750
91195
  }
90751
91196
  function removeProjectStatusLine(settings) {
90752
- if (!isErrataStatusLine(settings["statusLine"])) return false;
90753
- delete settings["statusLine"];
90754
- return true;
91197
+ let removed = false;
91198
+ for (const key of ["statusLine", "subagentStatusLine"]) {
91199
+ if (!isErrataStatusLine(settings[key])) continue;
91200
+ delete settings[key];
91201
+ removed = true;
91202
+ }
91203
+ return removed;
90755
91204
  }
90756
91205
  function userClaudeSettingsPath() {
90757
91206
  const base = process.env["CLAUDE_CONFIG_DIR"] ?? join46(homedir8(), ".claude");
@@ -90772,15 +91221,23 @@ function ensureUserStatusLine(opts) {
90772
91221
  return "unreadable";
90773
91222
  }
90774
91223
  }
90775
- if (settings["statusLine"] !== void 0 && !isErrataStatusLine(settings["statusLine"])) return "kept-own";
91224
+ const ownMain = settings["statusLine"] !== void 0 && !isErrataStatusLine(settings["statusLine"]);
91225
+ const ownRows = settings["subagentStatusLine"] !== void 0 && !isErrataStatusLine(settings["subagentStatusLine"]);
91226
+ if (ownMain && ownRows) return "kept-own";
90776
91227
  const relay = join46(opts.relayDir, STATUSLINE_RELAY_FILE);
90777
91228
  mkdirSync10(opts.relayDir, { recursive: true });
90778
91229
  if (!existsSync36(relay) || readFileSync39(relay, "utf8") !== STATUSLINE_RELAY_SOURCE) {
90779
91230
  writeFileSync27(relay, STATUSLINE_RELAY_SOURCE, "utf8");
90780
91231
  }
90781
- const next = buildStatusLine(opts.port, opts.nodePath ?? process.execPath, relay);
90782
- if (JSON.stringify(settings["statusLine"]) === JSON.stringify(next)) return "unchanged";
90783
- settings["statusLine"] = next;
91232
+ const nodePath = opts.nodePath ?? process.execPath;
91233
+ const next = {
91234
+ ...ownMain ? {} : { statusLine: buildStatusLine(opts.port, nodePath, relay) },
91235
+ ...ownRows ? {} : { subagentStatusLine: buildSubagentStatusLine(opts.port, nodePath, relay) }
91236
+ };
91237
+ if (Object.entries(next).every(([k, v]) => JSON.stringify(settings[k]) === JSON.stringify(v))) {
91238
+ return ownMain ? "kept-own" : "unchanged";
91239
+ }
91240
+ Object.assign(settings, next);
90784
91241
  mkdirSync10(dirname14(settingsPath), { recursive: true });
90785
91242
  writeFileSync27(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
90786
91243
  return "installed";
@@ -93291,6 +93748,7 @@ async function installClaudeHooks(port) {
93291
93748
  }
93292
93749
  const matcher = "Read|Edit|Write|Grep|Glob|NotebookEdit|Bash";
93293
93750
  const postMatcher = `${matcher}|ToolSearch|mcp__errata__.*`;
93751
+ const preMatcher = `${matcher}|Agent|Task`;
93294
93752
  const preCmd = `${hookRelayCommand(port, "/api/hook")} ${ERRATA_TAG}`;
93295
93753
  const postCmd = `${hookRelayCommand(port, "/api/hook")} ${ERRATA_TAG}`;
93296
93754
  settings.hooks ??= {};
@@ -93300,7 +93758,7 @@ async function installClaudeHooks(port) {
93300
93758
  }
93301
93759
  };
93302
93760
  for (const [event, command, eventMatcher] of [
93303
- ["PreToolUse", preCmd, matcher],
93761
+ ["PreToolUse", preCmd, preMatcher],
93304
93762
  ["PostToolUse", postCmd, postMatcher],
93305
93763
  ["PostToolUseFailure", postCmd, postMatcher]
93306
93764
  ]) {
@@ -93325,7 +93783,7 @@ async function installClaudeHooks(port) {
93325
93783
  dropErrata(endList);
93326
93784
  endList.push({ hooks: [{ type: "command", command: endCmd }] });
93327
93785
  const injectCmd = `${hookRelayCommand(port, "/api/context-inject")} ${ERRATA_TAG}`;
93328
- for (const event of ["SessionStart", "UserPromptSubmit"]) {
93786
+ for (const event of ["SessionStart", "UserPromptSubmit", "SubagentStart"]) {
93329
93787
  const list = settings.hooks[event] ??= [];
93330
93788
  dropErrata(list);
93331
93789
  list.push({ hooks: [{ type: "command", command: injectCmd }] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.17-dev.3407",
3
+ "version": "2.0.17-dev.3421",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {