@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.
- package/errata.mjs +495 -37
- 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)
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
53150
|
-
const
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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:
|
|
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 =
|
|
53411
|
+
const card = storedCard && subagent !== null ? forSubagent(storedCard, subagent) : storedCard;
|
|
53238
53412
|
if (card) {
|
|
53239
|
-
|
|
53240
|
-
|
|
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.
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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 +
|
|
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
|
|
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
|
-
|
|
90753
|
-
|
|
90754
|
-
|
|
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
|
-
|
|
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
|
|
90782
|
-
|
|
90783
|
-
|
|
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,
|
|
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 }] });
|