open-agents-ai 0.187.448 → 0.187.450
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/dist/index.js +1203 -19
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -505627,6 +505627,18 @@ var init_agent_tool = __esm({
|
|
|
505627
505627
|
description: {
|
|
505628
505628
|
type: "string",
|
|
505629
505629
|
description: "Short label for this agent (3-5 words, shown in status)"
|
|
505630
|
+
},
|
|
505631
|
+
// Phase 4 — explicit handoff inputs (clean isolation: parent decides
|
|
505632
|
+
// exactly what the sub-agent sees, instead of inheriting raw history).
|
|
505633
|
+
relevant_files: {
|
|
505634
|
+
type: "array",
|
|
505635
|
+
items: { type: "string" },
|
|
505636
|
+
description: "List of file paths to pre-load into the sub-agent's context. When set, the sub-agent sees these files in its initial system message and does not need to re-discover them. Saves tokens and prevents duplicate file_read calls in the parent's transcript."
|
|
505637
|
+
},
|
|
505638
|
+
constraints: {
|
|
505639
|
+
type: "array",
|
|
505640
|
+
items: { type: "string" },
|
|
505641
|
+
description: "Explicit rules the sub-agent must follow (e.g. 'do not modify foo.ts', 'use only TypeScript', 'return JSON only'). Rendered as a [Constraints] section in the sub-agent's initial system message."
|
|
505630
505642
|
}
|
|
505631
505643
|
},
|
|
505632
505644
|
required: ["prompt"]
|
|
@@ -505648,6 +505660,8 @@ var init_agent_tool = __esm({
|
|
|
505648
505660
|
const modelOverride = args["model"] ? String(args["model"]) : void 0;
|
|
505649
505661
|
const isolation = args["isolation"] ? String(args["isolation"]) : void 0;
|
|
505650
505662
|
const description = args["description"] ? String(args["description"]) : void 0;
|
|
505663
|
+
const relevantFilePaths = Array.isArray(args["relevant_files"]) ? args["relevant_files"].map((s2) => String(s2)) : [];
|
|
505664
|
+
const constraints = Array.isArray(args["constraints"]) ? args["constraints"].map((s2) => String(s2)) : [];
|
|
505651
505665
|
if (!prompt) {
|
|
505652
505666
|
return { success: false, output: "", error: "prompt is required", durationMs: performance.now() - start2 };
|
|
505653
505667
|
}
|
|
@@ -505672,11 +505686,58 @@ var init_agent_tool = __esm({
|
|
|
505672
505686
|
const model = modelOverride ?? this.config.model;
|
|
505673
505687
|
const agentId = generateAgentId(subagentType);
|
|
505674
505688
|
const label = description ?? `${subagentType}: ${prompt.slice(0, 40)}`;
|
|
505689
|
+
const preloadedFiles = [];
|
|
505690
|
+
if (relevantFilePaths.length > 0) {
|
|
505691
|
+
const fs7 = await import("node:fs");
|
|
505692
|
+
const path8 = await import("node:path");
|
|
505693
|
+
const FILE_CAP = 8 * 1024;
|
|
505694
|
+
const TOTAL_FILE_CAP = 5;
|
|
505695
|
+
for (const p2 of relevantFilePaths.slice(0, TOTAL_FILE_CAP)) {
|
|
505696
|
+
try {
|
|
505697
|
+
const abs = path8.isAbsolute(p2) ? p2 : path8.join(this.config.workingDir, p2);
|
|
505698
|
+
const content = fs7.readFileSync(abs, "utf-8");
|
|
505699
|
+
preloadedFiles.push({
|
|
505700
|
+
path: p2,
|
|
505701
|
+
content: content.length > FILE_CAP ? content.slice(0, FILE_CAP) + `
|
|
505702
|
+
[... truncated, original ${content.length} bytes]` : content
|
|
505703
|
+
});
|
|
505704
|
+
} catch {
|
|
505705
|
+
}
|
|
505706
|
+
}
|
|
505707
|
+
}
|
|
505708
|
+
const composedPrompt = (() => {
|
|
505709
|
+
if (preloadedFiles.length === 0 && constraints.length === 0)
|
|
505710
|
+
return prompt;
|
|
505711
|
+
const lines = [];
|
|
505712
|
+
lines.push("[Sub-Agent Handoff — parent has provided this scoped context]");
|
|
505713
|
+
lines.push("");
|
|
505714
|
+
if (preloadedFiles.length > 0) {
|
|
505715
|
+
lines.push("## Pre-loaded files");
|
|
505716
|
+
lines.push("These files have already been read for you. Reference them directly without calling file_read.");
|
|
505717
|
+
lines.push("");
|
|
505718
|
+
for (const f2 of preloadedFiles) {
|
|
505719
|
+
lines.push(`### ${f2.path}`);
|
|
505720
|
+
lines.push("```");
|
|
505721
|
+
lines.push(f2.content);
|
|
505722
|
+
lines.push("```");
|
|
505723
|
+
lines.push("");
|
|
505724
|
+
}
|
|
505725
|
+
}
|
|
505726
|
+
if (constraints.length > 0) {
|
|
505727
|
+
lines.push("## Constraints (must follow)");
|
|
505728
|
+
for (const c9 of constraints)
|
|
505729
|
+
lines.push(`- ${c9}`);
|
|
505730
|
+
lines.push("");
|
|
505731
|
+
}
|
|
505732
|
+
lines.push("## Task");
|
|
505733
|
+
lines.push(prompt);
|
|
505734
|
+
return lines.join("\n");
|
|
505735
|
+
})();
|
|
505675
505736
|
if (isolation === "worktree") {
|
|
505676
505737
|
this.callbacks.onViewRegister?.(agentId, label);
|
|
505677
505738
|
const spawn27 = this.callbacks.spawnSubprocess({
|
|
505678
505739
|
id: agentId,
|
|
505679
|
-
task:
|
|
505740
|
+
task: composedPrompt,
|
|
505680
505741
|
model,
|
|
505681
505742
|
workingDir: this.config.workingDir
|
|
505682
505743
|
});
|
|
@@ -505694,11 +505755,14 @@ var init_agent_tool = __esm({
|
|
|
505694
505755
|
this.callbacks.onViewRegister?.(agentId, label);
|
|
505695
505756
|
const promise = this.callbacks.spawnInProcess({
|
|
505696
505757
|
id: agentId,
|
|
505697
|
-
task:
|
|
505758
|
+
task: composedPrompt,
|
|
505698
505759
|
toolNames: resolved.toolNames,
|
|
505699
505760
|
maxTurns: resolved.maxTurns,
|
|
505700
505761
|
model,
|
|
505701
|
-
systemPromptAddition: resolved.systemPromptAddition
|
|
505762
|
+
systemPromptAddition: resolved.systemPromptAddition,
|
|
505763
|
+
relevantFiles: preloadedFiles,
|
|
505764
|
+
constraints,
|
|
505765
|
+
subAgentMode: true
|
|
505702
505766
|
}).then((result) => {
|
|
505703
505767
|
this.callbacks?.onViewStatus?.(agentId, result.completed ? "completed" : "failed");
|
|
505704
505768
|
return result.completed ? `Agent completed: ${result.summary} (${result.turns} turns, ${result.toolCalls} tool calls)` : `Agent incomplete after ${result.turns} turns`;
|
|
@@ -505721,19 +505785,26 @@ var init_agent_tool = __esm({
|
|
|
505721
505785
|
try {
|
|
505722
505786
|
const result = await this.callbacks.spawnInProcess({
|
|
505723
505787
|
id: agentId,
|
|
505724
|
-
task:
|
|
505788
|
+
task: composedPrompt,
|
|
505725
505789
|
toolNames: resolved.toolNames,
|
|
505726
505790
|
maxTurns: resolved.maxTurns,
|
|
505727
505791
|
model,
|
|
505728
|
-
systemPromptAddition: resolved.systemPromptAddition
|
|
505792
|
+
systemPromptAddition: resolved.systemPromptAddition,
|
|
505793
|
+
relevantFiles: preloadedFiles,
|
|
505794
|
+
constraints,
|
|
505795
|
+
subAgentMode: true
|
|
505729
505796
|
});
|
|
505730
505797
|
if (result.completed) {
|
|
505798
|
+
const filesLine = result.filesModified && result.filesModified.length > 0 ? `
|
|
505799
|
+
Files modified: ${result.filesModified.slice(0, 8).join(", ")}${result.filesModified.length > 8 ? ` (+${result.filesModified.length - 8} more)` : ""}` : "";
|
|
505800
|
+
const artifactLine = result.artifactIds && result.artifactIds.length > 0 ? `
|
|
505801
|
+
Artifacts: ${result.artifactIds.join(", ")} (call memex_retrieve to view full sub-agent transcript)` : "";
|
|
505731
505802
|
return {
|
|
505732
505803
|
success: true,
|
|
505733
505804
|
output: `Agent completed: ${result.summary}
|
|
505734
505805
|
Type: ${subagentType}
|
|
505735
505806
|
Turns: ${result.turns}, Tool calls: ${result.toolCalls}
|
|
505736
|
-
Duration: ${(result.durationMs / 1e3).toFixed(1)}s
|
|
505807
|
+
Duration: ${(result.durationMs / 1e3).toFixed(1)}s` + filesLine + artifactLine,
|
|
505737
505808
|
durationMs: performance.now() - start2
|
|
505738
505809
|
};
|
|
505739
505810
|
}
|
|
@@ -516249,6 +516320,442 @@ var init_messageLog = __esm({
|
|
|
516249
516320
|
}
|
|
516250
516321
|
});
|
|
516251
516322
|
|
|
516323
|
+
// packages/orchestrator/dist/contextTree.js
|
|
516324
|
+
function classifyToolCall(name10, argsJson) {
|
|
516325
|
+
const direct = TOOL_PHASE_MAP[name10];
|
|
516326
|
+
if (direct)
|
|
516327
|
+
return direct;
|
|
516328
|
+
if (name10 === "shell" || name10 === "shell_async") {
|
|
516329
|
+
if (!argsJson)
|
|
516330
|
+
return "implement";
|
|
516331
|
+
const lower = argsJson.toLowerCase();
|
|
516332
|
+
if (/(?:^|"command":")[^"]*(?:test|pytest|jest|mocha|vitest|cargo test|go test|npm test|pnpm test|tsc|lint|check|verify)\b/i.test(lower)) {
|
|
516333
|
+
return "verify";
|
|
516334
|
+
}
|
|
516335
|
+
return "implement";
|
|
516336
|
+
}
|
|
516337
|
+
return null;
|
|
516338
|
+
}
|
|
516339
|
+
function detectPhase(toolCallLog, window2 = 10) {
|
|
516340
|
+
if (toolCallLog.length === 0)
|
|
516341
|
+
return "explore";
|
|
516342
|
+
const recent = toolCallLog.slice(-window2);
|
|
516343
|
+
const counts = {};
|
|
516344
|
+
for (const tc of recent) {
|
|
516345
|
+
const p2 = classifyToolCall(tc.name, tc.argsKey);
|
|
516346
|
+
if (p2)
|
|
516347
|
+
counts[p2] = (counts[p2] ?? 0) + 1;
|
|
516348
|
+
}
|
|
516349
|
+
const total = Object.values(counts).reduce((a2, b) => a2 + b, 0);
|
|
516350
|
+
if (total === 0)
|
|
516351
|
+
return "mixed";
|
|
516352
|
+
let best = null;
|
|
516353
|
+
for (const [phase, n2] of Object.entries(counts)) {
|
|
516354
|
+
if (!best || n2 > best.n)
|
|
516355
|
+
best = { phase, n: n2 };
|
|
516356
|
+
}
|
|
516357
|
+
if (!best || best.n / total < 0.4)
|
|
516358
|
+
return "mixed";
|
|
516359
|
+
return best.phase;
|
|
516360
|
+
}
|
|
516361
|
+
function extractAnchorsFromMessages(messages2, turn) {
|
|
516362
|
+
const anchors = [];
|
|
516363
|
+
const seenSummaries = /* @__PURE__ */ new Set();
|
|
516364
|
+
let counter = 0;
|
|
516365
|
+
const nextId2 = (kind) => `anc-${turn}-${kind}-${counter++}`;
|
|
516366
|
+
for (const msg of messages2) {
|
|
516367
|
+
const content = typeof msg.content === "string" ? msg.content : "";
|
|
516368
|
+
if (!content)
|
|
516369
|
+
continue;
|
|
516370
|
+
if (msg.role === "tool" || msg.role === "assistant") {
|
|
516371
|
+
const matches = content.match(FILE_PATH_RE) ?? [];
|
|
516372
|
+
const distinctPaths = Array.from(new Set(matches.filter((p2) => p2.length > 2 && p2.length < 120))).slice(0, 5);
|
|
516373
|
+
for (const p2 of distinctPaths) {
|
|
516374
|
+
const summary = `file: ${p2}`;
|
|
516375
|
+
if (seenSummaries.has(summary))
|
|
516376
|
+
continue;
|
|
516377
|
+
seenSummaries.add(summary);
|
|
516378
|
+
anchors.push({
|
|
516379
|
+
id: nextId2("file"),
|
|
516380
|
+
type: "file",
|
|
516381
|
+
summary,
|
|
516382
|
+
keywords: [p2.toLowerCase(), ...p2.toLowerCase().split(/[\/\.]/).filter((s2) => s2.length > 2)],
|
|
516383
|
+
turn
|
|
516384
|
+
});
|
|
516385
|
+
}
|
|
516386
|
+
}
|
|
516387
|
+
if (msg.role === "assistant" && DECISION_HINTS.test(content)) {
|
|
516388
|
+
const sentence = content.split(/(?<=[.!?])\s+/).find((s2) => DECISION_HINTS.test(s2)) ?? content.slice(0, 200);
|
|
516389
|
+
const summary = `decision: ${sentence.slice(0, 140).replace(/\s+/g, " ").trim()}`;
|
|
516390
|
+
if (!seenSummaries.has(summary)) {
|
|
516391
|
+
seenSummaries.add(summary);
|
|
516392
|
+
anchors.push({
|
|
516393
|
+
id: nextId2("dec"),
|
|
516394
|
+
type: "decision",
|
|
516395
|
+
summary,
|
|
516396
|
+
keywords: extractKeywords(sentence),
|
|
516397
|
+
turn
|
|
516398
|
+
});
|
|
516399
|
+
}
|
|
516400
|
+
}
|
|
516401
|
+
if (msg.role === "tool" && ERROR_HINTS.test(content)) {
|
|
516402
|
+
const firstErrLine = content.split("\n").find((l2) => ERROR_HINTS.test(l2)) ?? content.slice(0, 200);
|
|
516403
|
+
const summary = `error: ${firstErrLine.slice(0, 140).replace(/\s+/g, " ").trim()}`;
|
|
516404
|
+
if (!seenSummaries.has(summary)) {
|
|
516405
|
+
seenSummaries.add(summary);
|
|
516406
|
+
anchors.push({
|
|
516407
|
+
id: nextId2("err"),
|
|
516408
|
+
type: "error",
|
|
516409
|
+
summary,
|
|
516410
|
+
keywords: extractKeywords(firstErrLine),
|
|
516411
|
+
turn
|
|
516412
|
+
});
|
|
516413
|
+
}
|
|
516414
|
+
}
|
|
516415
|
+
}
|
|
516416
|
+
return anchors;
|
|
516417
|
+
}
|
|
516418
|
+
function extractKeywords(text) {
|
|
516419
|
+
const tokens = text.toLowerCase().replace(/[^\w./-]+/g, " ").split(/\s+/);
|
|
516420
|
+
const out = [];
|
|
516421
|
+
const seen = /* @__PURE__ */ new Set();
|
|
516422
|
+
for (const tk of tokens) {
|
|
516423
|
+
if (tk.length < 3)
|
|
516424
|
+
continue;
|
|
516425
|
+
if (STOPWORDS.has(tk))
|
|
516426
|
+
continue;
|
|
516427
|
+
if (seen.has(tk))
|
|
516428
|
+
continue;
|
|
516429
|
+
seen.add(tk);
|
|
516430
|
+
out.push(tk);
|
|
516431
|
+
if (out.length >= 8)
|
|
516432
|
+
break;
|
|
516433
|
+
}
|
|
516434
|
+
return out;
|
|
516435
|
+
}
|
|
516436
|
+
var TOOL_PHASE_MAP, FILE_PATH_RE, DECISION_HINTS, ERROR_HINTS, STOPWORDS, DEFAULT_OPTS, ContextTree;
|
|
516437
|
+
var init_contextTree = __esm({
|
|
516438
|
+
"packages/orchestrator/dist/contextTree.js"() {
|
|
516439
|
+
"use strict";
|
|
516440
|
+
TOOL_PHASE_MAP = {
|
|
516441
|
+
// explore
|
|
516442
|
+
file_read: "explore",
|
|
516443
|
+
list_directory: "explore",
|
|
516444
|
+
find_files: "explore",
|
|
516445
|
+
grep_search: "explore",
|
|
516446
|
+
file_explore: "explore",
|
|
516447
|
+
glob_find: "explore",
|
|
516448
|
+
// plan
|
|
516449
|
+
todo_write: "plan",
|
|
516450
|
+
todo_read: "plan",
|
|
516451
|
+
working_notes: "plan",
|
|
516452
|
+
memory_write: "plan",
|
|
516453
|
+
memory_read: "plan",
|
|
516454
|
+
// implement
|
|
516455
|
+
file_write: "implement",
|
|
516456
|
+
file_edit: "implement",
|
|
516457
|
+
file_patch: "implement",
|
|
516458
|
+
batch_edit: "implement",
|
|
516459
|
+
// verify (heuristic — shell with test-like commands also contributes;
|
|
516460
|
+
// unknown shell defaults to implement)
|
|
516461
|
+
run_tests: "verify"
|
|
516462
|
+
};
|
|
516463
|
+
FILE_PATH_RE = /[\w./-]*\/[\w./-]+(?:\.[a-z0-9]{1,6})?/gi;
|
|
516464
|
+
DECISION_HINTS = /(?:I'll |Let me |I will |I'm going to |Plan: |Decision: |Approach: )/i;
|
|
516465
|
+
ERROR_HINTS = /(?:error|fail|exception|traceback|enoent|enotfound|fatal)/i;
|
|
516466
|
+
STOPWORDS = /* @__PURE__ */ new Set([
|
|
516467
|
+
"the",
|
|
516468
|
+
"and",
|
|
516469
|
+
"or",
|
|
516470
|
+
"of",
|
|
516471
|
+
"to",
|
|
516472
|
+
"in",
|
|
516473
|
+
"is",
|
|
516474
|
+
"it",
|
|
516475
|
+
"this",
|
|
516476
|
+
"that",
|
|
516477
|
+
"for",
|
|
516478
|
+
"on",
|
|
516479
|
+
"with",
|
|
516480
|
+
"as",
|
|
516481
|
+
"by",
|
|
516482
|
+
"from",
|
|
516483
|
+
"at",
|
|
516484
|
+
"an",
|
|
516485
|
+
"be",
|
|
516486
|
+
"are",
|
|
516487
|
+
"was",
|
|
516488
|
+
"were",
|
|
516489
|
+
"if",
|
|
516490
|
+
"then",
|
|
516491
|
+
"you",
|
|
516492
|
+
"we",
|
|
516493
|
+
"i",
|
|
516494
|
+
"but",
|
|
516495
|
+
"not",
|
|
516496
|
+
"no",
|
|
516497
|
+
"do",
|
|
516498
|
+
"does",
|
|
516499
|
+
"did",
|
|
516500
|
+
"have",
|
|
516501
|
+
"has",
|
|
516502
|
+
"had",
|
|
516503
|
+
"will",
|
|
516504
|
+
"would",
|
|
516505
|
+
"should",
|
|
516506
|
+
"can",
|
|
516507
|
+
"could"
|
|
516508
|
+
]);
|
|
516509
|
+
DEFAULT_OPTS = {
|
|
516510
|
+
maxActiveMessages: 60,
|
|
516511
|
+
maxAnchorsPerNode: 12,
|
|
516512
|
+
phaseWindow: 10
|
|
516513
|
+
};
|
|
516514
|
+
ContextTree = class {
|
|
516515
|
+
snapshot;
|
|
516516
|
+
opts;
|
|
516517
|
+
currentPhase = "mixed";
|
|
516518
|
+
currentTurn = 0;
|
|
516519
|
+
toolCallLog = [];
|
|
516520
|
+
constructor(systemPromptHash, activeGoal, opts) {
|
|
516521
|
+
this.opts = { ...DEFAULT_OPTS, ...opts };
|
|
516522
|
+
this.snapshot = {
|
|
516523
|
+
rootSystemPromptHash: systemPromptHash,
|
|
516524
|
+
activeGoal,
|
|
516525
|
+
phases: {},
|
|
516526
|
+
history: [],
|
|
516527
|
+
archive: []
|
|
516528
|
+
};
|
|
516529
|
+
}
|
|
516530
|
+
/** Returns the current detected phase. */
|
|
516531
|
+
getCurrentPhase() {
|
|
516532
|
+
return this.currentPhase;
|
|
516533
|
+
}
|
|
516534
|
+
/** Returns a read-only snapshot for inspection / serialization. */
|
|
516535
|
+
getSnapshot() {
|
|
516536
|
+
return this.snapshot;
|
|
516537
|
+
}
|
|
516538
|
+
/** Update the active goal (e.g. after compaction rewrites the goal). */
|
|
516539
|
+
setActiveGoal(goal) {
|
|
516540
|
+
this.snapshot.activeGoal = goal;
|
|
516541
|
+
}
|
|
516542
|
+
/**
|
|
516543
|
+
* Record a tool call. Updates phase classification but does not move
|
|
516544
|
+
* messages between nodes (caller must invoke maybeTransition).
|
|
516545
|
+
*/
|
|
516546
|
+
observeToolCall(name10, argsKey, turn) {
|
|
516547
|
+
this.toolCallLog.push({ name: name10, argsKey, turn });
|
|
516548
|
+
if (this.toolCallLog.length > 200) {
|
|
516549
|
+
this.toolCallLog = this.toolCallLog.slice(-100);
|
|
516550
|
+
}
|
|
516551
|
+
this.currentTurn = turn;
|
|
516552
|
+
}
|
|
516553
|
+
/**
|
|
516554
|
+
* Detect a phase transition. Returns the previous phase if a transition
|
|
516555
|
+
* happened, or null otherwise. Callers should follow up with
|
|
516556
|
+
* contractInactive to summarize the just-exited phase.
|
|
516557
|
+
*/
|
|
516558
|
+
maybeTransition(turn) {
|
|
516559
|
+
const detected = detectPhase(this.toolCallLog, this.opts.phaseWindow);
|
|
516560
|
+
if (detected === this.currentPhase)
|
|
516561
|
+
return null;
|
|
516562
|
+
const from3 = this.currentPhase;
|
|
516563
|
+
this.currentPhase = detected;
|
|
516564
|
+
if (!this.snapshot.phases[detected]) {
|
|
516565
|
+
this.snapshot.phases[detected] = {
|
|
516566
|
+
status: "active",
|
|
516567
|
+
messages: [],
|
|
516568
|
+
anchors: [],
|
|
516569
|
+
startedAtTurn: turn
|
|
516570
|
+
};
|
|
516571
|
+
} else {
|
|
516572
|
+
this.snapshot.phases[detected].status = "active";
|
|
516573
|
+
}
|
|
516574
|
+
return { from: from3, to: detected };
|
|
516575
|
+
}
|
|
516576
|
+
/**
|
|
516577
|
+
* Sync a slice of messages into the current active phase node.
|
|
516578
|
+
* Caller is expected to provide ALL messages owned by the current
|
|
516579
|
+
* phase since the last sync (typically the suffix beyond a known
|
|
516580
|
+
* boundary index).
|
|
516581
|
+
*/
|
|
516582
|
+
observePhaseMessages(messages2) {
|
|
516583
|
+
const node = this.snapshot.phases[this.currentPhase];
|
|
516584
|
+
if (!node)
|
|
516585
|
+
return;
|
|
516586
|
+
node.messages = messages2;
|
|
516587
|
+
if (messages2.length > this.opts.maxActiveMessages) {
|
|
516588
|
+
this.contract(this.currentPhase, this.currentTurn);
|
|
516589
|
+
}
|
|
516590
|
+
}
|
|
516591
|
+
/**
|
|
516592
|
+
* Contract the named phase: extract anchors, generate a stub summary,
|
|
516593
|
+
* mark the node `contracted`. The caller may then archive via the
|
|
516594
|
+
* archive callback (Phase 1 hands off to memex).
|
|
516595
|
+
*
|
|
516596
|
+
* `summarizer` may be sync (returns string) or async (returns Promise<string>).
|
|
516597
|
+
* When async, the node is contracted IMMEDIATELY with the default stub
|
|
516598
|
+
* summary; the real summary lands later via the returned promise's
|
|
516599
|
+
* resolution updating `node.summary` in place. The next compactMessages
|
|
516600
|
+
* render therefore picks up the LLM summary without blocking the turn
|
|
516601
|
+
* loop on it.
|
|
516602
|
+
*/
|
|
516603
|
+
contract(phase, turn, summarizer) {
|
|
516604
|
+
const node = this.snapshot.phases[phase];
|
|
516605
|
+
if (!node || node.status !== "active")
|
|
516606
|
+
return null;
|
|
516607
|
+
const anchors = extractAnchorsFromMessages(node.messages, turn).slice(0, this.opts.maxAnchorsPerNode);
|
|
516608
|
+
node.anchors = [...node.anchors, ...anchors].slice(-this.opts.maxAnchorsPerNode);
|
|
516609
|
+
node.summary = this.defaultSummary(phase, node.messages);
|
|
516610
|
+
node.status = "contracted";
|
|
516611
|
+
node.contractedAtTurn = turn;
|
|
516612
|
+
if (summarizer) {
|
|
516613
|
+
try {
|
|
516614
|
+
const result = summarizer(node.messages);
|
|
516615
|
+
if (result && typeof result.then === "function") {
|
|
516616
|
+
result.then((s2) => {
|
|
516617
|
+
if (typeof s2 === "string" && s2.length > 0)
|
|
516618
|
+
node.summary = s2;
|
|
516619
|
+
}).catch(() => {
|
|
516620
|
+
});
|
|
516621
|
+
} else if (typeof result === "string") {
|
|
516622
|
+
node.summary = result;
|
|
516623
|
+
}
|
|
516624
|
+
} catch {
|
|
516625
|
+
}
|
|
516626
|
+
}
|
|
516627
|
+
this.snapshot.history.push({ ...node, phase });
|
|
516628
|
+
if (this.snapshot.history.length > 32) {
|
|
516629
|
+
this.snapshot.history = this.snapshot.history.slice(-32);
|
|
516630
|
+
}
|
|
516631
|
+
return node;
|
|
516632
|
+
}
|
|
516633
|
+
/** Contract every active phase except the current one. */
|
|
516634
|
+
contractInactive(turn, summarizer) {
|
|
516635
|
+
const contracted = [];
|
|
516636
|
+
for (const [phase, node] of Object.entries(this.snapshot.phases)) {
|
|
516637
|
+
if (phase === this.currentPhase)
|
|
516638
|
+
continue;
|
|
516639
|
+
if (node.status !== "active")
|
|
516640
|
+
continue;
|
|
516641
|
+
this.contract(phase, turn, summarizer);
|
|
516642
|
+
contracted.push(phase);
|
|
516643
|
+
}
|
|
516644
|
+
return contracted;
|
|
516645
|
+
}
|
|
516646
|
+
/**
|
|
516647
|
+
* Mark a contracted node as archived after its summary has been moved
|
|
516648
|
+
* to memex. Caller passes the memex id.
|
|
516649
|
+
*/
|
|
516650
|
+
archive(phase, memexId) {
|
|
516651
|
+
const node = this.snapshot.phases[phase];
|
|
516652
|
+
if (!node || node.status !== "contracted")
|
|
516653
|
+
return;
|
|
516654
|
+
node.status = "archived";
|
|
516655
|
+
node.summary = void 0;
|
|
516656
|
+
if (!this.snapshot.archive.includes(memexId)) {
|
|
516657
|
+
this.snapshot.archive.push(memexId);
|
|
516658
|
+
}
|
|
516659
|
+
}
|
|
516660
|
+
/**
|
|
516661
|
+
* Restore a contracted phase: pulls the last contracted node for the phase
|
|
516662
|
+
* back into active state. Returns the restored node or null if none found.
|
|
516663
|
+
*/
|
|
516664
|
+
expand(phase) {
|
|
516665
|
+
const node = this.snapshot.phases[phase];
|
|
516666
|
+
if (node && node.status === "contracted") {
|
|
516667
|
+
node.status = "active";
|
|
516668
|
+
return node;
|
|
516669
|
+
}
|
|
516670
|
+
const histNode = [...this.snapshot.history].reverse().find((h) => h.phase === phase);
|
|
516671
|
+
if (histNode) {
|
|
516672
|
+
const restored = {
|
|
516673
|
+
status: "active",
|
|
516674
|
+
messages: [],
|
|
516675
|
+
anchors: histNode.anchors,
|
|
516676
|
+
startedAtTurn: this.currentTurn
|
|
516677
|
+
};
|
|
516678
|
+
this.snapshot.phases[phase] = restored;
|
|
516679
|
+
return restored;
|
|
516680
|
+
}
|
|
516681
|
+
return null;
|
|
516682
|
+
}
|
|
516683
|
+
/** Render a compact phase-status block for inclusion in prompts. */
|
|
516684
|
+
renderStatusBlock() {
|
|
516685
|
+
const lines = [`### Phase Status`, `- current: **${this.currentPhase}**`];
|
|
516686
|
+
const phaseSummaries = [];
|
|
516687
|
+
for (const [phase, node] of Object.entries(this.snapshot.phases)) {
|
|
516688
|
+
if (phase === this.currentPhase)
|
|
516689
|
+
continue;
|
|
516690
|
+
const status = node.status;
|
|
516691
|
+
const anchorCount = node.anchors.length;
|
|
516692
|
+
phaseSummaries.push(`- ${phase}: ${status}${anchorCount > 0 ? ` (${anchorCount} anchors)` : ""}`);
|
|
516693
|
+
}
|
|
516694
|
+
if (phaseSummaries.length > 0) {
|
|
516695
|
+
lines.push(...phaseSummaries);
|
|
516696
|
+
}
|
|
516697
|
+
if (this.snapshot.archive.length > 0) {
|
|
516698
|
+
lines.push(`- archived: ${this.snapshot.archive.length} (use memex_retrieve)`);
|
|
516699
|
+
}
|
|
516700
|
+
return lines.join("\n");
|
|
516701
|
+
}
|
|
516702
|
+
/** Render all retained anchors (across phases) for compaction summary. */
|
|
516703
|
+
renderAnchorBlock(maxPerPhase = 5) {
|
|
516704
|
+
const lines = [];
|
|
516705
|
+
for (const [phase, node] of Object.entries(this.snapshot.phases)) {
|
|
516706
|
+
if (node.anchors.length === 0)
|
|
516707
|
+
continue;
|
|
516708
|
+
lines.push(`**${phase} anchors:**`);
|
|
516709
|
+
for (const a2 of node.anchors.slice(-maxPerPhase)) {
|
|
516710
|
+
lines.push(`- [${a2.type}] ${a2.summary}`);
|
|
516711
|
+
}
|
|
516712
|
+
}
|
|
516713
|
+
return lines.length > 0 ? `### Phase Anchors
|
|
516714
|
+
${lines.join("\n")}` : "";
|
|
516715
|
+
}
|
|
516716
|
+
/** Look up anchors whose keywords overlap the given query (Phase 6 hook). */
|
|
516717
|
+
findAnchorsByKeywords(query, max = 5) {
|
|
516718
|
+
const tokens = extractKeywords(query.toLowerCase());
|
|
516719
|
+
if (tokens.length === 0)
|
|
516720
|
+
return [];
|
|
516721
|
+
const tokenSet = new Set(tokens);
|
|
516722
|
+
const scored = [];
|
|
516723
|
+
for (const node of Object.values(this.snapshot.phases)) {
|
|
516724
|
+
for (const a2 of node.anchors) {
|
|
516725
|
+
let score = 0;
|
|
516726
|
+
for (const k of a2.keywords) {
|
|
516727
|
+
if (tokenSet.has(k))
|
|
516728
|
+
score += 2;
|
|
516729
|
+
for (const t2 of tokenSet) {
|
|
516730
|
+
if (k.includes(t2) || t2.includes(k))
|
|
516731
|
+
score += 1;
|
|
516732
|
+
}
|
|
516733
|
+
}
|
|
516734
|
+
if (score > 0)
|
|
516735
|
+
scored.push({ anchor: a2, score });
|
|
516736
|
+
}
|
|
516737
|
+
}
|
|
516738
|
+
scored.sort((a2, b) => b.score - a2.score);
|
|
516739
|
+
return scored.slice(0, max).map((s2) => s2.anchor);
|
|
516740
|
+
}
|
|
516741
|
+
defaultSummary(phase, messages2) {
|
|
516742
|
+
const toolCallNames = /* @__PURE__ */ new Set();
|
|
516743
|
+
let assistantWords = 0;
|
|
516744
|
+
for (const m2 of messages2) {
|
|
516745
|
+
if (m2.role === "assistant" && m2.tool_calls) {
|
|
516746
|
+
for (const tc of m2.tool_calls)
|
|
516747
|
+
toolCallNames.add(tc.function.name);
|
|
516748
|
+
}
|
|
516749
|
+
if (m2.role === "assistant" && typeof m2.content === "string") {
|
|
516750
|
+
assistantWords += m2.content.split(/\s+/).length;
|
|
516751
|
+
}
|
|
516752
|
+
}
|
|
516753
|
+
return `${phase} phase: ${messages2.length} messages, ${toolCallNames.size} distinct tools used, ${assistantWords} words of assistant output.`;
|
|
516754
|
+
}
|
|
516755
|
+
};
|
|
516756
|
+
}
|
|
516757
|
+
});
|
|
516758
|
+
|
|
516252
516759
|
// packages/orchestrator/dist/codeGraphLink.js
|
|
516253
516760
|
function isCodeGraphLinkEnabled() {
|
|
516254
516761
|
return process.env["OA_CODEGRAPH_LINK"] !== "0";
|
|
@@ -517095,7 +517602,7 @@ function classifyThinkOutcome(raw) {
|
|
|
517095
517602
|
}
|
|
517096
517603
|
return null;
|
|
517097
517604
|
}
|
|
517098
|
-
var SYSTEM_PROMPT, SYSTEM_PROMPT_MEDIUM, SYSTEM_PROMPT_SMALL, VISUAL_TOOLS, AUDIO_TOOLS, SOCIAL_TOOLS, SPATIAL_TOOLS, CODE_TOOLS, AgenticRunner, OllamaAgenticBackend;
|
|
517605
|
+
var TOOL_SUBSETS, TOOL_AUTO_DEMOTE_TURNS, SYSTEM_PROMPT, SYSTEM_PROMPT_MEDIUM, SYSTEM_PROMPT_SMALL, VISUAL_TOOLS, AUDIO_TOOLS, SOCIAL_TOOLS, SPATIAL_TOOLS, CODE_TOOLS, AgenticRunner, OllamaAgenticBackend;
|
|
517099
517606
|
var init_agenticRunner = __esm({
|
|
517100
517607
|
"packages/orchestrator/dist/agenticRunner.js"() {
|
|
517101
517608
|
"use strict";
|
|
@@ -517109,12 +517616,23 @@ var init_agenticRunner = __esm({
|
|
|
517109
517616
|
init_reflectionBuffer();
|
|
517110
517617
|
init_taskHandoff();
|
|
517111
517618
|
init_messageLog();
|
|
517619
|
+
init_contextTree();
|
|
517112
517620
|
init_codeGraphLink();
|
|
517113
517621
|
init_dist5();
|
|
517114
517622
|
init_tool_batching();
|
|
517115
517623
|
init_hooks();
|
|
517116
517624
|
init_app_state();
|
|
517117
517625
|
init_streaming_executor();
|
|
517626
|
+
TOOL_SUBSETS = {
|
|
517627
|
+
web: ["web_search", "web_fetch", "web_crawl"],
|
|
517628
|
+
code: ["file_patch", "file_explore", "batch_edit", "file_read", "file_write", "file_edit"],
|
|
517629
|
+
agent: ["agent", "sub_agent", "background_run"],
|
|
517630
|
+
memory: ["memory_search", "memory_read", "memory_write", "memex_retrieve", "working_notes"],
|
|
517631
|
+
shell: ["shell", "shell_async", "kill_proc", "run_tests"],
|
|
517632
|
+
graph: ["graph_query", "graph_traverse", "code_graph"],
|
|
517633
|
+
skill: ["skill_list", "skill_execute", "skill_search"]
|
|
517634
|
+
};
|
|
517635
|
+
TOOL_AUTO_DEMOTE_TURNS = 10;
|
|
517118
517636
|
SYSTEM_PROMPT = loadPrompt("agentic/system-large.md");
|
|
517119
517637
|
SYSTEM_PROMPT_MEDIUM = loadPrompt("agentic/system-medium.md");
|
|
517120
517638
|
SYSTEM_PROMPT_SMALL = loadPrompt("agentic/system-small.md");
|
|
@@ -517222,6 +517740,26 @@ var init_agenticRunner = __esm({
|
|
|
517222
517740
|
_patchHistoryStore = null;
|
|
517223
517741
|
_toolSequence = [];
|
|
517224
517742
|
// Track tool calls for pattern detection
|
|
517743
|
+
// Phase 2 — tool subset expansion and usage tracking.
|
|
517744
|
+
// Tools promoted from deferred → inline by tool_search (or by subset
|
|
517745
|
+
// expansion). They stay inline for the rest of the run unless idle past
|
|
517746
|
+
// TOOL_AUTO_DEMOTE_TURNS, at which point buildToolDefinitions drops them
|
|
517747
|
+
// back to deferred to reclaim token budget.
|
|
517748
|
+
_activatedTools = /* @__PURE__ */ new Set();
|
|
517749
|
+
_toolLastUsedTurn = /* @__PURE__ */ new Map();
|
|
517750
|
+
// Phase 1 — Context Tree. Tracks current phase + per-phase anchors so
|
|
517751
|
+
// compactMessages can summarize-by-phase and Phase 6 can surface anchors
|
|
517752
|
+
// by keyword. Initialized lazily in run() because the system-prompt hash
|
|
517753
|
+
// depends on the actual prompt resolved at run time.
|
|
517754
|
+
_contextTree = null;
|
|
517755
|
+
// Phase 1 — index in `messages` where the current phase's slice begins.
|
|
517756
|
+
// On phase transition we capture messages[_phaseMessageStartIdx..now]
|
|
517757
|
+
// as the OUTGOING phase's owned slice (via tree.observePhaseMessages),
|
|
517758
|
+
// then advance the cursor so the new phase starts fresh.
|
|
517759
|
+
_phaseMessageStartIdx = 0;
|
|
517760
|
+
// Phase 6 — last-surface guard so we don't re-inject the same anchor on
|
|
517761
|
+
// consecutive turns when its keywords still match.
|
|
517762
|
+
_lastSurfacedAnchorIds = /* @__PURE__ */ new Set();
|
|
517225
517763
|
/** WO-AM-10: Process pending episode embeddings in background batches */
|
|
517226
517764
|
async processPendingEmbeddings() {
|
|
517227
517765
|
if (this._pendingEmbeddings.length === 0 || !this._episodeStore)
|
|
@@ -517280,7 +517818,11 @@ var init_agenticRunner = __esm({
|
|
|
517280
517818
|
personality: options2?.personality ?? PERSONALITY_PRESETS.balanced,
|
|
517281
517819
|
personalityName: options2?.personalityName ?? "",
|
|
517282
517820
|
finalVarResolver: options2?.finalVarResolver ?? void 0,
|
|
517283
|
-
observerMode: options2?.observerMode ?? "both"
|
|
517821
|
+
observerMode: options2?.observerMode ?? "both",
|
|
517822
|
+
// Phase 4 — sub-agent isolation flag (defaults false). When true, this
|
|
517823
|
+
// runner skips cross-task handoff inheritance from the parent's
|
|
517824
|
+
// session.
|
|
517825
|
+
subAgent: options2?.subAgent ?? false
|
|
517284
517826
|
};
|
|
517285
517827
|
this._observerMode = this.options.observerMode;
|
|
517286
517828
|
}
|
|
@@ -517305,6 +517847,32 @@ var init_agenticRunner = __esm({
|
|
|
517305
517847
|
return this._fileRegistry;
|
|
517306
517848
|
}
|
|
517307
517849
|
/** Get a Memex archive entry by ID (for the dereference tool) */
|
|
517850
|
+
/**
|
|
517851
|
+
* Phase 4 (Task C) — public read access to the runner's TaskState. Used
|
|
517852
|
+
* by the CLI's spawnInProcess wiring to extract `modifiedFiles` from a
|
|
517853
|
+
* sub-agent run and surface them on the parent's `InProcessResult`. The
|
|
517854
|
+
* caller treats the returned object as read-only.
|
|
517855
|
+
*/
|
|
517856
|
+
getTaskState() {
|
|
517857
|
+
return this._taskState;
|
|
517858
|
+
}
|
|
517859
|
+
/**
|
|
517860
|
+
* Phase 4 (Task C) — direct memex archive helper for parent runners
|
|
517861
|
+
* that want to file a sub-agent's verbose result under a known id.
|
|
517862
|
+
* Returns the assigned id so the caller can surface it on the result.
|
|
517863
|
+
*/
|
|
517864
|
+
archiveToMemex(opts) {
|
|
517865
|
+
const id = opts.id ?? this.quickHash(`${opts.toolName}|${opts.summary}|${Date.now()}`);
|
|
517866
|
+
this._memexArchive.set(id, {
|
|
517867
|
+
id,
|
|
517868
|
+
toolName: opts.toolName,
|
|
517869
|
+
summary: opts.summary,
|
|
517870
|
+
fullContent: opts.fullContent,
|
|
517871
|
+
turn: opts.turn ?? this._taskState.toolCallCount,
|
|
517872
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
517873
|
+
});
|
|
517874
|
+
return id;
|
|
517875
|
+
}
|
|
517308
517876
|
getMemexEntry(id) {
|
|
517309
517877
|
return this._memexArchive.get(id);
|
|
517310
517878
|
}
|
|
@@ -517685,6 +518253,265 @@ ${body}`;
|
|
|
517685
518253
|
* Hannover reference: services/compact/apiMicrocompact.ts
|
|
517686
518254
|
* Research: arXiv:2307.03172 (Lost in the Middle — recent context matters most)
|
|
517687
518255
|
*/
|
|
518256
|
+
/**
|
|
518257
|
+
* Phase 5 — Proactive context pruning (precedes microcompact). Targets
|
|
518258
|
+
* specific waste patterns rather than blanket-clearing old tool results:
|
|
518259
|
+
*
|
|
518260
|
+
* 1. Duplicate tool calls — same (name, args) called multiple times.
|
|
518261
|
+
* Older instances replaced with "[deduped — see turn N]".
|
|
518262
|
+
* 2. Aged file_read results — file_read older than `agedFileReadTurns`
|
|
518263
|
+
* replaced with a summary stub (path + size + first-line preview).
|
|
518264
|
+
* 3. Successful shell/test runs — shell without error markers older
|
|
518265
|
+
* than `agedShellTurns` replaced with a "succeeded" stub.
|
|
518266
|
+
*
|
|
518267
|
+
* Mutates `messages` in place. Cheap O(n) walk; safe to run every turn.
|
|
518268
|
+
* Refs: proposal §3 Phase 5, AgentFold (arXiv:2510.24699) progressive
|
|
518269
|
+
* summarization, RECOMP (ICLR 2024) observation masking.
|
|
518270
|
+
*/
|
|
518271
|
+
proactivePrune(messages2, currentTurn) {
|
|
518272
|
+
if (process.env["OA_DISABLE_PROACTIVE_PRUNE"] === "1")
|
|
518273
|
+
return;
|
|
518274
|
+
const AGED_FILE_READ_TURNS = 10;
|
|
518275
|
+
const AGED_SHELL_TURNS = 5;
|
|
518276
|
+
const ERROR_MARKERS = /(?:error|fail|exception|traceback|enoent|enotfound|exit code [^0]|status[: ]+1\d?\d?)/i;
|
|
518277
|
+
const PRUNE_PREFIX = "[Tool result cleared";
|
|
518278
|
+
const DEDUPE_PREFIX = "[deduped — same call as turn";
|
|
518279
|
+
const FILE_AGED_PREFIX = "[file_read aged out, summary:";
|
|
518280
|
+
const SHELL_AGED_PREFIX = "[shell succeeded, output pruned —";
|
|
518281
|
+
const seen = /* @__PURE__ */ new Map();
|
|
518282
|
+
const pending = [];
|
|
518283
|
+
let scanTurn = 0;
|
|
518284
|
+
for (let i2 = 0; i2 < messages2.length; i2++) {
|
|
518285
|
+
const msg = messages2[i2];
|
|
518286
|
+
if (msg.role === "assistant" && msg.tool_calls && msg.tool_calls.length > 0) {
|
|
518287
|
+
scanTurn++;
|
|
518288
|
+
for (const tc of msg.tool_calls) {
|
|
518289
|
+
const name10 = tc.function.name;
|
|
518290
|
+
const argsRaw = (tc.function.arguments ?? "").slice(0, 200);
|
|
518291
|
+
const fp = `${name10}|${argsRaw}`;
|
|
518292
|
+
let resultIdx = -1;
|
|
518293
|
+
for (let j = i2 + 1; j < Math.min(messages2.length, i2 + 8); j++) {
|
|
518294
|
+
const r2 = messages2[j];
|
|
518295
|
+
if (r2.role === "tool" && r2.tool_call_id === tc.id) {
|
|
518296
|
+
resultIdx = j;
|
|
518297
|
+
break;
|
|
518298
|
+
}
|
|
518299
|
+
}
|
|
518300
|
+
if (resultIdx === -1)
|
|
518301
|
+
continue;
|
|
518302
|
+
const resultMsg = messages2[resultIdx];
|
|
518303
|
+
const content = typeof resultMsg.content === "string" ? resultMsg.content : "";
|
|
518304
|
+
if (content.startsWith(PRUNE_PREFIX) || content.startsWith(DEDUPE_PREFIX) || content.startsWith(FILE_AGED_PREFIX) || content.startsWith(SHELL_AGED_PREFIX)) {
|
|
518305
|
+
if (!seen.has(fp))
|
|
518306
|
+
seen.set(fp, { turn: scanTurn, idx: resultIdx });
|
|
518307
|
+
continue;
|
|
518308
|
+
}
|
|
518309
|
+
const prior = seen.get(fp);
|
|
518310
|
+
if (prior) {
|
|
518311
|
+
pending.push({
|
|
518312
|
+
idx: prior.idx,
|
|
518313
|
+
reason: "dedupe",
|
|
518314
|
+
replacement: `${DEDUPE_PREFIX} ${scanTurn} — duplicate ${name10}() call]`
|
|
518315
|
+
});
|
|
518316
|
+
seen.set(fp, { turn: scanTurn, idx: resultIdx });
|
|
518317
|
+
continue;
|
|
518318
|
+
}
|
|
518319
|
+
seen.set(fp, { turn: scanTurn, idx: resultIdx });
|
|
518320
|
+
const ageTurns = currentTurn - scanTurn;
|
|
518321
|
+
if (name10 === "file_read" && ageTurns > AGED_FILE_READ_TURNS && content.length > 200) {
|
|
518322
|
+
const pathArg = (() => {
|
|
518323
|
+
try {
|
|
518324
|
+
const o2 = JSON.parse(tc.function.arguments || "{}");
|
|
518325
|
+
return String(o2.path ?? o2.file ?? "?");
|
|
518326
|
+
} catch {
|
|
518327
|
+
return "?";
|
|
518328
|
+
}
|
|
518329
|
+
})();
|
|
518330
|
+
const firstLine = content.split("\n")[0]?.slice(0, 80) ?? "";
|
|
518331
|
+
pending.push({
|
|
518332
|
+
idx: resultIdx,
|
|
518333
|
+
reason: "aged_file",
|
|
518334
|
+
replacement: `${FILE_AGED_PREFIX} path=${pathArg}, size=${content.length} chars, first-line="${firstLine}"]`
|
|
518335
|
+
});
|
|
518336
|
+
continue;
|
|
518337
|
+
}
|
|
518338
|
+
if ((name10 === "shell" || name10 === "shell_async" || name10 === "run_tests") && ageTurns > AGED_SHELL_TURNS && content.length > 200 && !ERROR_MARKERS.test(content)) {
|
|
518339
|
+
const cmdArg = (() => {
|
|
518340
|
+
try {
|
|
518341
|
+
const o2 = JSON.parse(tc.function.arguments || "{}");
|
|
518342
|
+
return String(o2.command ?? o2.cmd ?? "?").slice(0, 80);
|
|
518343
|
+
} catch {
|
|
518344
|
+
return "?";
|
|
518345
|
+
}
|
|
518346
|
+
})();
|
|
518347
|
+
pending.push({
|
|
518348
|
+
idx: resultIdx,
|
|
518349
|
+
reason: "aged_shell",
|
|
518350
|
+
replacement: `${SHELL_AGED_PREFIX} command="${cmdArg}", output ${content.length} chars, no error markers detected]`
|
|
518351
|
+
});
|
|
518352
|
+
}
|
|
518353
|
+
}
|
|
518354
|
+
}
|
|
518355
|
+
}
|
|
518356
|
+
if (pending.length === 0)
|
|
518357
|
+
return;
|
|
518358
|
+
let dedupes = 0, agedFiles = 0, agedShells = 0;
|
|
518359
|
+
for (const p2 of pending) {
|
|
518360
|
+
const m2 = messages2[p2.idx];
|
|
518361
|
+
if (!m2)
|
|
518362
|
+
continue;
|
|
518363
|
+
messages2[p2.idx] = { ...m2, content: p2.replacement };
|
|
518364
|
+
if (p2.reason === "dedupe")
|
|
518365
|
+
dedupes++;
|
|
518366
|
+
else if (p2.reason === "aged_file")
|
|
518367
|
+
agedFiles++;
|
|
518368
|
+
else if (p2.reason === "aged_shell")
|
|
518369
|
+
agedShells++;
|
|
518370
|
+
}
|
|
518371
|
+
const parts = [];
|
|
518372
|
+
if (dedupes > 0)
|
|
518373
|
+
parts.push(`${dedupes} duplicate call(s)`);
|
|
518374
|
+
if (agedFiles > 0)
|
|
518375
|
+
parts.push(`${agedFiles} aged file_read(s)`);
|
|
518376
|
+
if (agedShells > 0)
|
|
518377
|
+
parts.push(`${agedShells} aged shell run(s)`);
|
|
518378
|
+
if (parts.length > 0) {
|
|
518379
|
+
this.emit({
|
|
518380
|
+
type: "status",
|
|
518381
|
+
content: `Proactive prune: replaced ${parts.join(", ")} with summary stubs`,
|
|
518382
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
518383
|
+
});
|
|
518384
|
+
}
|
|
518385
|
+
}
|
|
518386
|
+
/**
|
|
518387
|
+
* Phase 1 (Task B) — build an async LLM summarizer for ContextTree.contract().
|
|
518388
|
+
*
|
|
518389
|
+
* Returns a function that takes a phase's owned message slice and asks the
|
|
518390
|
+
* backend for a 2-3 sentence summary of what the phase accomplished. Hard
|
|
518391
|
+
* caps the messages slice at ~3000 chars (≈750 tokens) of representative
|
|
518392
|
+
* content to keep the cost bounded. Never blocks the turn loop — the
|
|
518393
|
+
* returned promise updates ContextNode.summary in place when it resolves.
|
|
518394
|
+
*
|
|
518395
|
+
* Returns null when the disable knob is set or the backend is missing the
|
|
518396
|
+
* chatCompletion method.
|
|
518397
|
+
*/
|
|
518398
|
+
makePhaseSummarizer() {
|
|
518399
|
+
if (process.env["OA_DISABLE_PHASE_SUMMARIZER"] === "1")
|
|
518400
|
+
return null;
|
|
518401
|
+
if (!this.backend || typeof this.backend.chatCompletion !== "function")
|
|
518402
|
+
return null;
|
|
518403
|
+
return async (phase, msgs) => {
|
|
518404
|
+
const tail = msgs.slice(-8);
|
|
518405
|
+
const blob = tail.map((m2) => {
|
|
518406
|
+
const role = m2.role;
|
|
518407
|
+
const content = typeof m2.content === "string" ? m2.content.slice(0, 400) : "";
|
|
518408
|
+
if (m2.role === "assistant" && m2.tool_calls && m2.tool_calls.length > 0) {
|
|
518409
|
+
const calls = m2.tool_calls.map((tc) => `${tc.function.name}(${(tc.function.arguments || "").slice(0, 80)})`).join(", ");
|
|
518410
|
+
return `[${role}] ${content || ""} | tool_calls: ${calls}`.slice(0, 500);
|
|
518411
|
+
}
|
|
518412
|
+
return `[${role}] ${content}`.slice(0, 500);
|
|
518413
|
+
}).join("\n");
|
|
518414
|
+
const prompt = `You are summarizing the "${phase}" phase of an agent's task. Below is the message slice owned by this phase. Write a 2-3 sentence summary focused on WHAT was done and any KEY FINDINGS. No fluff. No "the agent did X" framing — just the facts.
|
|
518415
|
+
|
|
518416
|
+
=== ${phase} phase messages ===
|
|
518417
|
+
${blob}
|
|
518418
|
+
=== end ===`;
|
|
518419
|
+
try {
|
|
518420
|
+
const response = await this.backend.chatCompletion({
|
|
518421
|
+
messages: [
|
|
518422
|
+
{ role: "system", content: "You write extremely concise phase summaries for agent context management." },
|
|
518423
|
+
{ role: "user", content: prompt }
|
|
518424
|
+
],
|
|
518425
|
+
tools: [],
|
|
518426
|
+
temperature: 0,
|
|
518427
|
+
maxTokens: 256,
|
|
518428
|
+
timeoutMs: 3e4
|
|
518429
|
+
});
|
|
518430
|
+
const text = response.choices?.[0]?.message?.content;
|
|
518431
|
+
if (typeof text === "string" && text.length > 0) {
|
|
518432
|
+
return `${phase}: ${text.replace(/\s+/g, " ").trim().slice(0, 600)}`;
|
|
518433
|
+
}
|
|
518434
|
+
} catch {
|
|
518435
|
+
}
|
|
518436
|
+
return "";
|
|
518437
|
+
};
|
|
518438
|
+
}
|
|
518439
|
+
/**
|
|
518440
|
+
* Phase 6 — Anchor Surfacing.
|
|
518441
|
+
*
|
|
518442
|
+
* Scans the most recent user / assistant text for keyword overlap with
|
|
518443
|
+
* the ContextTree's anchor index plus the memex archive index. When
|
|
518444
|
+
* matches are found, injects a single-line system hint listing the
|
|
518445
|
+
* available anchors so the model knows what archived context exists
|
|
518446
|
+
* (and may call memex_retrieve to expand it).
|
|
518447
|
+
*
|
|
518448
|
+
* Refs: proposal §3 Phase 6, Lost-in-the-Middle (arXiv:2307.03172) —
|
|
518449
|
+
* surfacing relevant context near the tail combats positional decay.
|
|
518450
|
+
*/
|
|
518451
|
+
surfaceAnchors(messages2) {
|
|
518452
|
+
if (process.env["OA_DISABLE_ANCHOR_SURFACING"] === "1")
|
|
518453
|
+
return;
|
|
518454
|
+
if (!this._contextTree)
|
|
518455
|
+
return;
|
|
518456
|
+
if (messages2.length === 0)
|
|
518457
|
+
return;
|
|
518458
|
+
const tail = messages2.slice(-6);
|
|
518459
|
+
const queryParts = [];
|
|
518460
|
+
for (const m2 of tail) {
|
|
518461
|
+
if (typeof m2.content === "string" && m2.content.length > 0) {
|
|
518462
|
+
if (m2.role === "user" || m2.role === "assistant") {
|
|
518463
|
+
queryParts.push(m2.content);
|
|
518464
|
+
}
|
|
518465
|
+
}
|
|
518466
|
+
}
|
|
518467
|
+
const query = queryParts.join(" ").slice(0, 800);
|
|
518468
|
+
if (!query)
|
|
518469
|
+
return;
|
|
518470
|
+
const anchors = this._contextTree.findAnchorsByKeywords(query, 5);
|
|
518471
|
+
const memexMatches = [];
|
|
518472
|
+
if (this._memexArchive.size > 0) {
|
|
518473
|
+
const tokens = query.toLowerCase().replace(/[^\w./-]+/g, " ").split(/\s+/).filter((t2) => t2.length > 3);
|
|
518474
|
+
const tokenSet = new Set(tokens);
|
|
518475
|
+
for (const e2 of this._memexArchive.values()) {
|
|
518476
|
+
const summaryLower = e2.summary.toLowerCase();
|
|
518477
|
+
let score = 0;
|
|
518478
|
+
for (const t2 of tokenSet) {
|
|
518479
|
+
if (summaryLower.includes(t2))
|
|
518480
|
+
score += 2;
|
|
518481
|
+
}
|
|
518482
|
+
if (e2.toolName === "todo_complete" && tokenSet.has("todo"))
|
|
518483
|
+
score += 1;
|
|
518484
|
+
if (score > 0)
|
|
518485
|
+
memexMatches.push({ id: e2.id, summary: e2.summary, score });
|
|
518486
|
+
}
|
|
518487
|
+
memexMatches.sort((a2, b) => b.score - a2.score);
|
|
518488
|
+
}
|
|
518489
|
+
const newAnchors = anchors.filter((a2) => !this._lastSurfacedAnchorIds.has(a2.id)).slice(0, 3);
|
|
518490
|
+
const newMemex = memexMatches.filter((m2) => !this._lastSurfacedAnchorIds.has(m2.id)).slice(0, 2);
|
|
518491
|
+
if (newAnchors.length === 0 && newMemex.length === 0)
|
|
518492
|
+
return;
|
|
518493
|
+
const lines = [`[Anchor surface] Relevant archived context for the current activity:`];
|
|
518494
|
+
for (const a2 of newAnchors) {
|
|
518495
|
+
lines.push(` - [${a2.type}] ${a2.summary}`);
|
|
518496
|
+
}
|
|
518497
|
+
for (const m2 of newMemex) {
|
|
518498
|
+
lines.push(` - [memex:${m2.id}] ${m2.summary} — call memex_retrieve("${m2.id}") for full body`);
|
|
518499
|
+
}
|
|
518500
|
+
lines.push(`(Anchors are reminders. Pull only what you actually need; ignore otherwise.)`);
|
|
518501
|
+
messages2.push({
|
|
518502
|
+
role: "system",
|
|
518503
|
+
content: lines.join("\n")
|
|
518504
|
+
});
|
|
518505
|
+
for (const a2 of newAnchors)
|
|
518506
|
+
this._lastSurfacedAnchorIds.add(a2.id);
|
|
518507
|
+
for (const m2 of newMemex)
|
|
518508
|
+
this._lastSurfacedAnchorIds.add(m2.id);
|
|
518509
|
+
this.emit({
|
|
518510
|
+
type: "status",
|
|
518511
|
+
content: `Anchor surface: surfaced ${newAnchors.length} anchor(s) + ${newMemex.length} memex entry(ies)`,
|
|
518512
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
518513
|
+
});
|
|
518514
|
+
}
|
|
517688
518515
|
microcompact(messages2, recentToolResults) {
|
|
517689
518516
|
const tier = this.options.modelTier ?? "large";
|
|
517690
518517
|
let keepResults = tier === "small" ? 6 : tier === "medium" ? 10 : 20;
|
|
@@ -518056,6 +518883,10 @@ Respond with your assessment, then take action.`;
|
|
|
518056
518883
|
this.aborted = false;
|
|
518057
518884
|
this._paused = false;
|
|
518058
518885
|
this._toolSequence = [];
|
|
518886
|
+
this._activatedTools.clear();
|
|
518887
|
+
this._toolLastUsedTurn.clear();
|
|
518888
|
+
this._contextTree = null;
|
|
518889
|
+
this._lastSurfacedAnchorIds.clear();
|
|
518059
518890
|
if (!this._memoryInitialized) {
|
|
518060
518891
|
try {
|
|
518061
518892
|
const path8 = await import("node:path");
|
|
@@ -518135,6 +518966,8 @@ Respond with your assessment, then take action.`;
|
|
|
518135
518966
|
this._hookManager.runSessionHook("session_start", this._sessionId);
|
|
518136
518967
|
const contextComposition = await this.assembleContext(task, context2);
|
|
518137
518968
|
const systemPrompt = contextComposition.assembled;
|
|
518969
|
+
this._contextTree = new ContextTree(`sys-${systemPrompt.length}`, cleanedTask.slice(0, 200));
|
|
518970
|
+
this._phaseMessageStartIdx = 2;
|
|
518138
518971
|
this.emit({
|
|
518139
518972
|
type: "status",
|
|
518140
518973
|
content: `Context assembled: ${contextComposition.sections.map((s2) => `${s2.label}(${s2.tokenEstimate}t)`).join(" + ")} = ~${contextComposition.totalTokenEstimate}t`,
|
|
@@ -518152,6 +518985,8 @@ TASK: ${task}` : task;
|
|
|
518152
518985
|
{ role: "user", content: userContent }
|
|
518153
518986
|
];
|
|
518154
518987
|
try {
|
|
518988
|
+
if (this.options.subAgent)
|
|
518989
|
+
throw "skip-handoff-subagent";
|
|
518155
518990
|
const oaDir = this._workingDirectory ? _pathJoin(this._workingDirectory, ".oa") : _pathJoin(process.cwd(), ".oa");
|
|
518156
518991
|
const chainPairs = loadMessagePairsFromLog(oaDir, { currentTask: cleanedTask });
|
|
518157
518992
|
if (chainPairs.length > 0) {
|
|
@@ -518590,7 +519425,9 @@ ${memoryLines.join("\n")}`
|
|
|
518590
519425
|
}
|
|
518591
519426
|
}
|
|
518592
519427
|
this._lastAssistantTimestamp = Date.now();
|
|
519428
|
+
this.proactivePrune(compacted, turn);
|
|
518593
519429
|
this.microcompact(compacted, recentToolResults);
|
|
519430
|
+
this.surfaceAnchors(compacted);
|
|
518594
519431
|
const { maxOutputTokens: effectiveMaxTokens } = this.contextLimits();
|
|
518595
519432
|
const chatRequest = {
|
|
518596
519433
|
messages: compacted,
|
|
@@ -518879,6 +519716,37 @@ ${memoryLines.join("\n")}`
|
|
|
518879
519716
|
toolCallCount++;
|
|
518880
519717
|
const argsKey = Object.entries(tc.arguments ?? {}).sort(([a2], [b]) => a2.localeCompare(b)).map(([k, v]) => `${k}=${typeof v === "string" ? v.slice(0, 160) : JSON.stringify(v).slice(0, 160)}`).join(",");
|
|
518881
519718
|
toolCallLog.push({ name: tc.name, argsKey, turn, timestampMs: Date.now() });
|
|
519719
|
+
this._toolLastUsedTurn.set(tc.name, turn);
|
|
519720
|
+
if (this._contextTree) {
|
|
519721
|
+
this._contextTree.observeToolCall(tc.name, argsKey, turn);
|
|
519722
|
+
const transition = this._contextTree.maybeTransition(turn);
|
|
519723
|
+
if (transition) {
|
|
519724
|
+
const phaseSlice = messages2.slice(this._phaseMessageStartIdx);
|
|
519725
|
+
if (phaseSlice.length > 0) {
|
|
519726
|
+
const fromNode = this._contextTree.getSnapshot().phases[transition.from];
|
|
519727
|
+
if (fromNode) {
|
|
519728
|
+
fromNode.messages = phaseSlice;
|
|
519729
|
+
}
|
|
519730
|
+
}
|
|
519731
|
+
this.emit({
|
|
519732
|
+
type: "status",
|
|
519733
|
+
content: `Phase: ${transition.from} → ${transition.to}`,
|
|
519734
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
519735
|
+
});
|
|
519736
|
+
const summarizer = this.makePhaseSummarizer();
|
|
519737
|
+
const contracted = this._contextTree.contractInactive(turn, summarizer ? ((msgs) => summarizer(transition.from, msgs)) : void 0);
|
|
519738
|
+
if (contracted.length > 0) {
|
|
519739
|
+
this.emit({
|
|
519740
|
+
type: "status",
|
|
519741
|
+
content: `Phase contraction: ${contracted.join(", ")} → contracted (${summarizer ? "LLM-summarized" : "stub-summarized"}, anchors retained)`,
|
|
519742
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
519743
|
+
});
|
|
519744
|
+
}
|
|
519745
|
+
this._phaseMessageStartIdx = messages2.length;
|
|
519746
|
+
this._taskState.phase = transition.to;
|
|
519747
|
+
this._taskState.phaseSince = turn;
|
|
519748
|
+
}
|
|
519749
|
+
}
|
|
518882
519750
|
const budgetRemaining = toolCallBudget.get(tc.name);
|
|
518883
519751
|
if (budgetRemaining !== void 0) {
|
|
518884
519752
|
if (budgetRemaining <= 0) {
|
|
@@ -519891,7 +520759,9 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
519891
520759
|
} else {
|
|
519892
520760
|
compactedMsgs = await this.compactMessages(messages2, this._skillCompactionStrategy ?? "default");
|
|
519893
520761
|
}
|
|
520762
|
+
this.proactivePrune(compactedMsgs, this._taskState.toolCallCount);
|
|
519894
520763
|
this.microcompact(compactedMsgs);
|
|
520764
|
+
this.surfaceAnchors(compactedMsgs);
|
|
519895
520765
|
const chatRequest = { messages: compactedMsgs, tools: toolDefs, temperature: this.options.temperature, maxTokens: this.options.maxTokens, timeoutMs: this.options.requestTimeoutMs };
|
|
519896
520766
|
let response;
|
|
519897
520767
|
try {
|
|
@@ -520238,6 +521108,8 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
|
|
|
520238
521108
|
}
|
|
520239
521109
|
}
|
|
520240
521110
|
try {
|
|
521111
|
+
if (this.options.subAgent)
|
|
521112
|
+
throw "skip-handoff-write-subagent";
|
|
520241
521113
|
const outcome = consolidation.outcome === "success" ? "success" : consolidation.outcome === "aborted" ? "aborted" : consolidation.outcome === "timeout" ? "timeout" : "failed";
|
|
520242
521114
|
const oaDir = this._workingDirectory ? _pathJoin(this._workingDirectory, ".oa") : _pathJoin(process.cwd(), ".oa");
|
|
520243
521115
|
const transcriptPath = _pathJoin(oaDir, "consolidations", `${this._sessionId}.json`);
|
|
@@ -520678,6 +521550,9 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
|
|
|
520678
521550
|
if (ts.currentStep) {
|
|
520679
521551
|
parts.push(`**Currently doing:** ${ts.currentStep}`);
|
|
520680
521552
|
}
|
|
521553
|
+
if (ts.phase) {
|
|
521554
|
+
parts.push(`**Phase:** ${ts.phase}`);
|
|
521555
|
+
}
|
|
520681
521556
|
if (ts.completedSteps.length > 0) {
|
|
520682
521557
|
parts.push(`**Completed:**`);
|
|
520683
521558
|
for (const s2 of ts.completedSteps.slice(-10))
|
|
@@ -520702,9 +521577,75 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
|
|
|
520702
521577
|
parts.push(`- \`${path8}\` (${action})`);
|
|
520703
521578
|
}
|
|
520704
521579
|
}
|
|
521580
|
+
const todoBlock = this.formatCompletedTodoAnchors();
|
|
521581
|
+
if (todoBlock)
|
|
521582
|
+
parts.push(todoBlock);
|
|
520705
521583
|
parts.push(`**Tool calls:** ${ts.toolCallCount}`);
|
|
520706
521584
|
return parts.join("\n");
|
|
520707
521585
|
}
|
|
521586
|
+
/**
|
|
521587
|
+
* Phase 3 — render completed todos as compact anchor cards. Older
|
|
521588
|
+
* completed todos beyond the top-3 are archived to memex on first sight
|
|
521589
|
+
* so the model can still reach them via memex_retrieve. Returns the empty
|
|
521590
|
+
* string when the session has no todos.
|
|
521591
|
+
*
|
|
521592
|
+
* Anchor card shape: `[done] {content slice} (files: a.ts, b.ts) → outcome`
|
|
521593
|
+
*/
|
|
521594
|
+
formatCompletedTodoAnchors() {
|
|
521595
|
+
try {
|
|
521596
|
+
const sessionId = process.env["OA_SESSION_ID"] || this._sessionId;
|
|
521597
|
+
const todos = readTodos(sessionId);
|
|
521598
|
+
if (todos.length === 0)
|
|
521599
|
+
return "";
|
|
521600
|
+
const completed = todos.filter((t2) => t2.status === "completed");
|
|
521601
|
+
const blocked = todos.filter((t2) => t2.status === "blocked");
|
|
521602
|
+
if (completed.length === 0 && blocked.length === 0)
|
|
521603
|
+
return "";
|
|
521604
|
+
const parts = [];
|
|
521605
|
+
const topRecent = completed.sort((a2, b) => (b.completedAt || b.updatedAt) - (a2.completedAt || a2.updatedAt)).slice(0, 3);
|
|
521606
|
+
if (topRecent.length > 0) {
|
|
521607
|
+
parts.push(`**Recently completed (anchors):**`);
|
|
521608
|
+
for (const t2 of topRecent) {
|
|
521609
|
+
parts.push(`- [done] ${this.todoAnchorLine(t2)}`);
|
|
521610
|
+
}
|
|
521611
|
+
}
|
|
521612
|
+
const olderCompleted = completed.slice(3);
|
|
521613
|
+
let archivedCount = 0;
|
|
521614
|
+
for (const t2 of olderCompleted) {
|
|
521615
|
+
const memexId = `todo-${t2.id.slice(0, 8)}`;
|
|
521616
|
+
if (!this._memexArchive.has(memexId)) {
|
|
521617
|
+
this._memexArchive.set(memexId, {
|
|
521618
|
+
id: memexId,
|
|
521619
|
+
toolName: "todo_complete",
|
|
521620
|
+
summary: this.todoAnchorLine(t2),
|
|
521621
|
+
fullContent: JSON.stringify(t2, null, 2),
|
|
521622
|
+
turn: this._taskState.toolCallCount,
|
|
521623
|
+
timestamp: new Date(t2.completedAt || t2.updatedAt).toISOString()
|
|
521624
|
+
});
|
|
521625
|
+
archivedCount++;
|
|
521626
|
+
}
|
|
521627
|
+
}
|
|
521628
|
+
if (archivedCount > 0) {
|
|
521629
|
+
parts.push(`*(${archivedCount} older completed todo(s) archived → memex_retrieve("todo-{id}") for details, ${olderCompleted.length} total older)*`);
|
|
521630
|
+
} else if (olderCompleted.length > 0) {
|
|
521631
|
+
parts.push(`*(${olderCompleted.length} older completed todo(s) in memex archive)*`);
|
|
521632
|
+
}
|
|
521633
|
+
if (blocked.length > 0) {
|
|
521634
|
+
parts.push(`**Blocked:**`);
|
|
521635
|
+
for (const t2 of blocked.slice(0, 5)) {
|
|
521636
|
+
parts.push(`- [blocked] ${t2.content}${t2.blocker ? ` — ${t2.blocker}` : ""}`);
|
|
521637
|
+
}
|
|
521638
|
+
}
|
|
521639
|
+
return parts.length > 0 ? parts.join("\n") : "";
|
|
521640
|
+
} catch {
|
|
521641
|
+
return "";
|
|
521642
|
+
}
|
|
521643
|
+
}
|
|
521644
|
+
/** Build a single-line anchor for a completed todo (Phase 3 helper). */
|
|
521645
|
+
todoAnchorLine(t2) {
|
|
521646
|
+
const content = t2.content.length > 80 ? t2.content.slice(0, 77) + "..." : t2.content;
|
|
521647
|
+
return content;
|
|
521648
|
+
}
|
|
520708
521649
|
/**
|
|
520709
521650
|
* Format file state registry as compact markdown for domain-aware compaction.
|
|
520710
521651
|
* Only includes files with meaningful state (modified or recently accessed).
|
|
@@ -520954,6 +521895,33 @@ ${taskStateStr}
|
|
|
520954
521895
|
if (memexIndexStr)
|
|
520955
521896
|
enrichments.push(memexIndexStr);
|
|
520956
521897
|
}
|
|
521898
|
+
if (this._contextTree) {
|
|
521899
|
+
const droppedSlice = messages2.slice(headEndIdx, recentStart);
|
|
521900
|
+
const freshAnchors = extractAnchorsFromMessages(droppedSlice, this._taskState.toolCallCount);
|
|
521901
|
+
if (freshAnchors.length > 0) {
|
|
521902
|
+
const tree2 = this._contextTree;
|
|
521903
|
+
const phase = tree2.getCurrentPhase();
|
|
521904
|
+
const snap = tree2.getSnapshot();
|
|
521905
|
+
if (!snap.phases[phase]) {
|
|
521906
|
+
snap.phases[phase] = {
|
|
521907
|
+
status: "active",
|
|
521908
|
+
messages: [],
|
|
521909
|
+
anchors: [],
|
|
521910
|
+
startedAtTurn: this._taskState.toolCallCount
|
|
521911
|
+
};
|
|
521912
|
+
}
|
|
521913
|
+
snap.phases[phase].anchors = [
|
|
521914
|
+
...snap.phases[phase].anchors,
|
|
521915
|
+
...freshAnchors
|
|
521916
|
+
].slice(-12);
|
|
521917
|
+
}
|
|
521918
|
+
const phaseStatus = this._contextTree.renderStatusBlock();
|
|
521919
|
+
if (phaseStatus)
|
|
521920
|
+
enrichments.push(phaseStatus);
|
|
521921
|
+
const anchorBlock = this._contextTree.renderAnchorBlock();
|
|
521922
|
+
if (anchorBlock)
|
|
521923
|
+
enrichments.push(anchorBlock);
|
|
521924
|
+
}
|
|
520957
521925
|
const postCompactRestore = [];
|
|
520958
521926
|
const planSkel = this.buildPlanSkeleton();
|
|
520959
521927
|
if (planSkel)
|
|
@@ -522185,7 +523153,7 @@ ${transcript}`
|
|
|
522185
523153
|
const allTools = Array.from(this.tools.values()).filter((tool) => tool.name !== "tool_search");
|
|
522186
523154
|
const tier = this.options.modelTier ?? "large";
|
|
522187
523155
|
const taskGoal = this._taskState.goal || "";
|
|
522188
|
-
const
|
|
523156
|
+
const STOPWORDS2 = /* @__PURE__ */ new Set([
|
|
522189
523157
|
"with",
|
|
522190
523158
|
"that",
|
|
522191
523159
|
"this",
|
|
@@ -522235,7 +523203,7 @@ ${transcript}`
|
|
|
522235
523203
|
const getDesc = (tool) => dynamicDescs.get(tool.name) ?? tool.description;
|
|
522236
523204
|
const getIndexLabel = (tool) => {
|
|
522237
523205
|
const desc = getDesc(tool).toLowerCase().replace(/[`"'()[\]{}:;,.!?/\\|-]+/g, " ");
|
|
522238
|
-
const keywords = Array.from(new Set(desc.split(/\s+/).filter((word2) => word2.length > 2 && !
|
|
523206
|
+
const keywords = Array.from(new Set(desc.split(/\s+/).filter((word2) => word2.length > 2 && !STOPWORDS2.has(word2) && !tool.name.toLowerCase().includes(word2)))).slice(0, 4);
|
|
522239
523207
|
return keywords.length > 0 ? `${tool.name}(${keywords.join(",")})` : tool.name;
|
|
522240
523208
|
};
|
|
522241
523209
|
const CORE_TOOLS2 = /* @__PURE__ */ new Set([
|
|
@@ -522283,9 +523251,26 @@ ${transcript}`
|
|
|
522283
523251
|
scored.sort((a2, b) => b.score - a2.score);
|
|
522284
523252
|
const maxInlineExtra = tier === "small" ? 4 : 8;
|
|
522285
523253
|
const inlineExtras = scored.slice(0, maxInlineExtra).filter((s2) => s2.score > 0);
|
|
523254
|
+
const currentTurn = this._taskState.toolCallCount;
|
|
523255
|
+
let demoted = 0;
|
|
523256
|
+
for (const promoted of this._activatedTools) {
|
|
523257
|
+
const lastUsed = this._toolLastUsedTurn.get(promoted) ?? -1;
|
|
523258
|
+
if (lastUsed >= 0 && currentTurn - lastUsed > TOOL_AUTO_DEMOTE_TURNS) {
|
|
523259
|
+
this._activatedTools.delete(promoted);
|
|
523260
|
+
demoted++;
|
|
523261
|
+
}
|
|
523262
|
+
}
|
|
523263
|
+
if (demoted > 0) {
|
|
523264
|
+
this.emit({
|
|
523265
|
+
type: "status",
|
|
523266
|
+
content: `Tool auto-demote: ${demoted} idle promoted tool(s) dropped back to deferred`,
|
|
523267
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
523268
|
+
});
|
|
523269
|
+
}
|
|
522286
523270
|
const inlineNames = /* @__PURE__ */ new Set([
|
|
522287
523271
|
...allTools.filter((t2) => CORE_TOOLS2.has(t2.name)).map((t2) => t2.name),
|
|
522288
|
-
...inlineExtras.map((s2) => s2.tool.name)
|
|
523272
|
+
...inlineExtras.map((s2) => s2.tool.name),
|
|
523273
|
+
...Array.from(this._activatedTools).filter((name10) => allTools.some((t2) => t2.name === name10))
|
|
522289
523274
|
]);
|
|
522290
523275
|
const deferred = allTools.filter((t2) => !inlineNames.has(t2.name));
|
|
522291
523276
|
const inlineTools = allTools.filter((t2) => inlineNames.has(t2.name));
|
|
@@ -522317,11 +523302,12 @@ ${transcript}`
|
|
|
522317
523302
|
lines[lineIdx].push(entry);
|
|
522318
523303
|
return lines;
|
|
522319
523304
|
}, []).map((line) => `- ${line.join(", ")}`).join("\n");
|
|
523305
|
+
const subsetNames = Object.keys(TOOL_SUBSETS).join(", ");
|
|
522320
523306
|
defs.push({
|
|
522321
523307
|
type: "function",
|
|
522322
523308
|
function: {
|
|
522323
523309
|
name: "tool_search",
|
|
522324
|
-
description: `Search for and activate additional tools not in your current tool list. Call this when your task needs a tool you don't have. Pass a search query describing what you need. The catalog below is a compact index; full schemas are returned only after search.
|
|
523310
|
+
description: `Search for and activate additional tools not in your current tool list. Call this when your task needs a tool you don't have. Pass a search query describing what you need. The catalog below is a compact index; full schemas are returned only after search. Subset shortcuts (call tool_search with one of these as the query to promote the whole group at once): ${subsetNames}.
|
|
522325
523311
|
|
|
522326
523312
|
Available tools (${deferred.length}):
|
|
522327
523313
|
${catalog}`,
|
|
@@ -522334,16 +523320,64 @@ ${catalog}`,
|
|
|
522334
523320
|
}
|
|
522335
523321
|
}
|
|
522336
523322
|
});
|
|
523323
|
+
const activatedToolsRef = this._activatedTools;
|
|
523324
|
+
const subsetCatalog = TOOL_SUBSETS;
|
|
522337
523325
|
this.tools.set("tool_search", {
|
|
522338
523326
|
name: "tool_search",
|
|
522339
523327
|
description: "Search for deferred tools",
|
|
522340
523328
|
parameters: {},
|
|
522341
523329
|
execute: async (args) => {
|
|
522342
|
-
const query = String(args["query"] ?? "").toLowerCase();
|
|
523330
|
+
const query = String(args["query"] ?? "").toLowerCase().trim();
|
|
523331
|
+
const subsetMatch = subsetCatalog[query];
|
|
523332
|
+
if (subsetMatch && subsetMatch.length > 0) {
|
|
523333
|
+
const newlyPromoted = [];
|
|
523334
|
+
const alreadyAvailable = [];
|
|
523335
|
+
const unknown = [];
|
|
523336
|
+
for (const name10 of subsetMatch) {
|
|
523337
|
+
const tool = deferred.find((t2) => t2.name === name10) ?? allTools.find((t2) => t2.name === name10);
|
|
523338
|
+
if (!tool) {
|
|
523339
|
+
unknown.push(name10);
|
|
523340
|
+
continue;
|
|
523341
|
+
}
|
|
523342
|
+
if (inlineNames.has(name10)) {
|
|
523343
|
+
alreadyAvailable.push(name10);
|
|
523344
|
+
continue;
|
|
523345
|
+
}
|
|
523346
|
+
activatedToolsRef.add(name10);
|
|
523347
|
+
newlyPromoted.push(name10);
|
|
523348
|
+
}
|
|
523349
|
+
const lines = [`Subset "${query}" expanded:`];
|
|
523350
|
+
if (newlyPromoted.length > 0) {
|
|
523351
|
+
lines.push(` Promoted to inline (${newlyPromoted.length}): ${newlyPromoted.join(", ")}`);
|
|
523352
|
+
for (const name10 of newlyPromoted) {
|
|
523353
|
+
const tool = allTools.find((t2) => t2.name === name10);
|
|
523354
|
+
if (!tool)
|
|
523355
|
+
continue;
|
|
523356
|
+
lines.push("");
|
|
523357
|
+
lines.push(`## ${tool.name}`);
|
|
523358
|
+
lines.push(getDesc(tool));
|
|
523359
|
+
lines.push(`Parameters: ${JSON.stringify(tool.parameters)}`);
|
|
523360
|
+
}
|
|
523361
|
+
}
|
|
523362
|
+
if (alreadyAvailable.length > 0) {
|
|
523363
|
+
lines.push(` Already inline (${alreadyAvailable.length}): ${alreadyAvailable.join(", ")}`);
|
|
523364
|
+
}
|
|
523365
|
+
if (unknown.length > 0) {
|
|
523366
|
+
lines.push(` Not registered (${unknown.length}): ${unknown.join(", ")}`);
|
|
523367
|
+
}
|
|
523368
|
+
return { success: true, output: lines.join("\n") };
|
|
523369
|
+
}
|
|
522343
523370
|
const matches = deferred.filter((t2) => t2.name.toLowerCase().includes(query) || getDesc(t2).toLowerCase().includes(query)).slice(0, 5);
|
|
522344
523371
|
if (matches.length === 0) {
|
|
522345
|
-
|
|
523372
|
+
const subsetHint = Object.keys(subsetCatalog).join(", ");
|
|
523373
|
+
return {
|
|
523374
|
+
success: false,
|
|
523375
|
+
output: "",
|
|
523376
|
+
error: `No tools matching "${query}". Try a broader search, or call a subset name directly: ${subsetHint}.`
|
|
523377
|
+
};
|
|
522346
523378
|
}
|
|
523379
|
+
for (const t2 of matches)
|
|
523380
|
+
activatedToolsRef.add(t2.name);
|
|
522347
523381
|
const result = matches.map((t2) => {
|
|
522348
523382
|
const paramsStr = JSON.stringify(t2.parameters, null, 2);
|
|
522349
523383
|
return `## ${t2.name}
|
|
@@ -522354,7 +523388,7 @@ ${paramsStr}`;
|
|
|
522354
523388
|
}).join("\n\n---\n\n");
|
|
522355
523389
|
return {
|
|
522356
523390
|
success: true,
|
|
522357
|
-
output: `Found ${matches.length} tool(s)
|
|
523391
|
+
output: `Found ${matches.length} tool(s) — promoted to inline for the rest of this run:
|
|
522358
523392
|
|
|
522359
523393
|
${result}`
|
|
522360
523394
|
};
|
|
@@ -583889,6 +584923,119 @@ ${incompleteList}${more}
|
|
|
583889
584923
|
}
|
|
583890
584924
|
};
|
|
583891
584925
|
}
|
|
584926
|
+
function wireAgentToolMinimal(tool, config, repoRoot) {
|
|
584927
|
+
const typeRegistry = getAgentTypeRegistry();
|
|
584928
|
+
const allToolNames = [
|
|
584929
|
+
"file_read",
|
|
584930
|
+
"file_write",
|
|
584931
|
+
"file_edit",
|
|
584932
|
+
"shell",
|
|
584933
|
+
"grep",
|
|
584934
|
+
"glob",
|
|
584935
|
+
"list_directory",
|
|
584936
|
+
"web_fetch",
|
|
584937
|
+
"web_search",
|
|
584938
|
+
"memory_read",
|
|
584939
|
+
"memory_write",
|
|
584940
|
+
"task_complete",
|
|
584941
|
+
"batch_edit",
|
|
584942
|
+
"file_patch",
|
|
584943
|
+
"git_info",
|
|
584944
|
+
"agent",
|
|
584945
|
+
"send_message"
|
|
584946
|
+
];
|
|
584947
|
+
tool.setCallbacks({
|
|
584948
|
+
resolveType: (typeName) => {
|
|
584949
|
+
const def = typeRegistry.get(typeName);
|
|
584950
|
+
if (!def) return void 0;
|
|
584951
|
+
const resolvedNames = typeRegistry.resolveTools(typeName, allToolNames);
|
|
584952
|
+
return {
|
|
584953
|
+
type: def.type,
|
|
584954
|
+
maxTurns: def.maxTurns,
|
|
584955
|
+
toolNames: resolvedNames,
|
|
584956
|
+
systemPromptAddition: def.systemPromptAddition,
|
|
584957
|
+
canSpawnAgents: def.canSpawnAgents
|
|
584958
|
+
};
|
|
584959
|
+
},
|
|
584960
|
+
listTypes: () => typeRegistry.listTypes(),
|
|
584961
|
+
spawnInProcess: async (opts) => {
|
|
584962
|
+
let backend;
|
|
584963
|
+
if (config.backendType === "nexus") {
|
|
584964
|
+
const nexusTool = new NexusTool(repoRoot);
|
|
584965
|
+
backend = new NexusAgenticBackend(nexusTool.sendCommand.bind(nexusTool), opts.model, void 0, config.apiKey);
|
|
584966
|
+
} else {
|
|
584967
|
+
backend = new OllamaAgenticBackend(config.backendUrl, opts.model, config.apiKey);
|
|
584968
|
+
}
|
|
584969
|
+
const subTier = getModelTier(opts.model);
|
|
584970
|
+
const subCompaction = subTier === "small" ? 12e3 : subTier === "medium" ? 24e3 : 4e4;
|
|
584971
|
+
const subRunner = new AgenticRunner(backend, {
|
|
584972
|
+
maxTurns: opts.maxTurns,
|
|
584973
|
+
maxTokens: 16384,
|
|
584974
|
+
temperature: 0,
|
|
584975
|
+
requestTimeoutMs: config.timeoutMs,
|
|
584976
|
+
taskTimeoutMs: config.timeoutMs * 2,
|
|
584977
|
+
compactionThreshold: subCompaction,
|
|
584978
|
+
contextWindowSize: 0,
|
|
584979
|
+
modelTier: subTier,
|
|
584980
|
+
subAgent: opts.subAgentMode === true
|
|
584981
|
+
});
|
|
584982
|
+
subRunner.setWorkingDirectory(repoRoot);
|
|
584983
|
+
const allSafe = buildSubAgentTools(repoRoot, config);
|
|
584984
|
+
const nameSet = new Set(opts.toolNames || []);
|
|
584985
|
+
const subToolInstances = nameSet.size > 0 ? allSafe.filter((t2) => nameSet.has(t2.name)) : allSafe.slice();
|
|
584986
|
+
const nameOf = (t2) => t2.name;
|
|
584987
|
+
if (!subToolInstances.some((t2) => nameOf(t2) === "todo_write")) subToolInstances.push(new TodoWriteTool());
|
|
584988
|
+
if (!subToolInstances.some((t2) => nameOf(t2) === "todo_read")) subToolInstances.push(new TodoReadTool());
|
|
584989
|
+
subRunner.registerTools(subToolInstances.map(adaptTool6));
|
|
584990
|
+
subRunner.registerTool(createTaskCompleteTool(subTier));
|
|
584991
|
+
const systemCtx = opts.systemPromptAddition ? `Working directory: ${repoRoot}
|
|
584992
|
+
|
|
584993
|
+
${opts.systemPromptAddition}` : `Working directory: ${repoRoot}`;
|
|
584994
|
+
const result = await subRunner.run(opts.task, systemCtx);
|
|
584995
|
+
const subState = subRunner.getTaskState();
|
|
584996
|
+
const filesModified = Array.from(subState.modifiedFiles.keys());
|
|
584997
|
+
const artifactIds = [];
|
|
584998
|
+
if (_parentRunnerForArchive && result.summary && result.summary.length > 0) {
|
|
584999
|
+
const artifactId = `subagent-${opts.id}-result`;
|
|
585000
|
+
try {
|
|
585001
|
+
_parentRunnerForArchive.archiveToMemex({
|
|
585002
|
+
id: artifactId,
|
|
585003
|
+
toolName: "agent",
|
|
585004
|
+
summary: `Sub-agent ${opts.id}: ${result.summary.slice(0, 120)}`,
|
|
585005
|
+
fullContent: `Sub-agent task: ${opts.task.slice(0, 1e3)}
|
|
585006
|
+
|
|
585007
|
+
Completed: ${result.completed}
|
|
585008
|
+
Turns: ${result.turns}, Tool calls: ${result.toolCalls}
|
|
585009
|
+
Files modified: ${filesModified.join(", ") || "(none)"}
|
|
585010
|
+
|
|
585011
|
+
Summary:
|
|
585012
|
+
${result.summary}`
|
|
585013
|
+
});
|
|
585014
|
+
artifactIds.push(artifactId);
|
|
585015
|
+
} catch {
|
|
585016
|
+
}
|
|
585017
|
+
}
|
|
585018
|
+
return {
|
|
585019
|
+
completed: result.completed,
|
|
585020
|
+
summary: result.summary,
|
|
585021
|
+
turns: result.turns,
|
|
585022
|
+
toolCalls: result.toolCalls,
|
|
585023
|
+
durationMs: result.durationMs,
|
|
585024
|
+
filesModified,
|
|
585025
|
+
artifactIds
|
|
585026
|
+
};
|
|
585027
|
+
},
|
|
585028
|
+
spawnSubprocess: (opts) => {
|
|
585029
|
+
const entry = spawnFullSubAgent(
|
|
585030
|
+
opts.task,
|
|
585031
|
+
{ model: opts.model, backendUrl: config.backendUrl, workingDir: opts.workingDir }
|
|
585032
|
+
);
|
|
585033
|
+
return { id: entry.id, pid: entry.pid ?? 0 };
|
|
585034
|
+
},
|
|
585035
|
+
trackTask: () => {
|
|
585036
|
+
}
|
|
585037
|
+
});
|
|
585038
|
+
}
|
|
583892
585039
|
function buildSubAgentTools(repoRoot, config) {
|
|
583893
585040
|
return [
|
|
583894
585041
|
// File + search
|
|
@@ -584770,7 +585917,8 @@ RULES:
|
|
|
584770
585917
|
}
|
|
584771
585918
|
} catch {
|
|
584772
585919
|
}
|
|
584773
|
-
const
|
|
585920
|
+
const envOverride = Number.parseInt(process.env["OA_COMPACTION_THRESHOLD"] ?? "", 10);
|
|
585921
|
+
const compactionThreshold = Number.isFinite(envOverride) && envOverride > 0 ? envOverride : modelTier === "small" ? 12e3 : modelTier === "medium" ? 24e3 : 4e4;
|
|
584774
585922
|
let identityInjection = "";
|
|
584775
585923
|
try {
|
|
584776
585924
|
const ikStateFile = join109(repoRoot, ".oa", "identity", "self-state.json");
|
|
@@ -584818,6 +585966,7 @@ RULES:
|
|
|
584818
585966
|
});
|
|
584819
585967
|
runner.setWorkingDirectory(repoRoot);
|
|
584820
585968
|
_activeRunnerRef = runner;
|
|
585969
|
+
_parentRunnerForArchive = runner;
|
|
584821
585970
|
let _checkinPoller = null;
|
|
584822
585971
|
try {
|
|
584823
585972
|
const oaSessionId = process.env["OA_SESSION_ID"];
|
|
@@ -586285,7 +587434,12 @@ Review its full output in the [${id}] tab or via sub_agent(action='output', id='
|
|
|
586285
587434
|
compactionThreshold: subCompaction,
|
|
586286
587435
|
contextWindowSize: 0,
|
|
586287
587436
|
// sub-agents discover their own context window
|
|
586288
|
-
modelTier: subTier
|
|
587437
|
+
modelTier: subTier,
|
|
587438
|
+
// Phase 4 — sub-agent isolation: skip parent's cross-task handoff
|
|
587439
|
+
// read AND write. The sub-agent gets only what AgentTool composed
|
|
587440
|
+
// into the prompt (relevantFiles + constraints + task), nothing
|
|
587441
|
+
// leaked from the parent's session log or .oa/handoffs/latest.json.
|
|
587442
|
+
subAgent: opts.subAgentMode === true
|
|
586289
587443
|
});
|
|
586290
587444
|
const allSafe = buildSubAgentTools(repoRoot, config);
|
|
586291
587445
|
const nameSet = new Set(opts.toolNames || []);
|
|
@@ -586299,12 +587453,38 @@ Review its full output in the [${id}] tab or via sub_agent(action='output', id='
|
|
|
586299
587453
|
|
|
586300
587454
|
${opts.systemPromptAddition}` : `Working directory: ${repoRoot}`;
|
|
586301
587455
|
const result = await subRunner.run(opts.task, systemCtx);
|
|
587456
|
+
const subState = subRunner.getTaskState();
|
|
587457
|
+
const filesModified = Array.from(subState.modifiedFiles.keys());
|
|
587458
|
+
const artifactIds = [];
|
|
587459
|
+
if (_parentRunnerForArchive && result.summary && result.summary.length > 0) {
|
|
587460
|
+
const artifactId = `subagent-${opts.id}-result`;
|
|
587461
|
+
try {
|
|
587462
|
+
_parentRunnerForArchive.archiveToMemex({
|
|
587463
|
+
id: artifactId,
|
|
587464
|
+
toolName: "agent",
|
|
587465
|
+
summary: `Sub-agent ${opts.id}: ${result.summary.slice(0, 120)}`,
|
|
587466
|
+
fullContent: `Sub-agent task: ${opts.task.slice(0, 1e3)}
|
|
587467
|
+
|
|
587468
|
+
Completed: ${result.completed}
|
|
587469
|
+
Turns: ${result.turns}, Tool calls: ${result.toolCalls}
|
|
587470
|
+
Duration: ${result.durationMs}ms
|
|
587471
|
+
Files modified: ${filesModified.join(", ") || "(none)"}
|
|
587472
|
+
|
|
587473
|
+
Summary:
|
|
587474
|
+
${result.summary}`
|
|
587475
|
+
});
|
|
587476
|
+
artifactIds.push(artifactId);
|
|
587477
|
+
} catch {
|
|
587478
|
+
}
|
|
587479
|
+
}
|
|
586302
587480
|
return {
|
|
586303
587481
|
completed: result.completed,
|
|
586304
587482
|
summary: result.summary,
|
|
586305
587483
|
turns: result.turns,
|
|
586306
587484
|
toolCalls: result.toolCalls,
|
|
586307
|
-
durationMs: result.durationMs
|
|
587485
|
+
durationMs: result.durationMs,
|
|
587486
|
+
filesModified,
|
|
587487
|
+
artifactIds
|
|
586308
587488
|
};
|
|
586309
587489
|
},
|
|
586310
587490
|
spawnSubprocess: (opts) => {
|
|
@@ -589922,6 +591102,9 @@ async function runWithTUI(task, config, repoPath, callbacks) {
|
|
|
589922
591102
|
renderCompactHeader(config.model);
|
|
589923
591103
|
renderUserMessage(task);
|
|
589924
591104
|
setTerminalTitle(task.slice(0, 60), getVersion4());
|
|
591105
|
+
if (!_wireAgentToolCallbacks) {
|
|
591106
|
+
_wireAgentToolCallbacks = (tool) => wireAgentToolMinimal(tool, config, repoRoot);
|
|
591107
|
+
}
|
|
589925
591108
|
try {
|
|
589926
591109
|
const handle2 = startTask(task, config, repoRoot);
|
|
589927
591110
|
await handle2.promise;
|
|
@@ -590187,7 +591370,7 @@ Rules:
|
|
|
590187
591370
|
process.exit(1);
|
|
590188
591371
|
}
|
|
590189
591372
|
}
|
|
590190
|
-
var _interactiveSessionActive, _interactiveSessionReason, _voiceChatSession, taskManager, _apiCallbacks, _shellToolRef, _replToolRef, _fullSubAgentToolRef, _agentToolRef, _sendMessageToolRef, _agentLifecycleMgr, _activeRunnerRef, _wireSubAgentCallbacks, _wireAgentToolCallbacks, _wireSubAgentToolCallbacks, _autoUpdatedThisSession, _mcpManager, _pluginManager, _mcpTools, SELF_IMPROVE_INTERVAL, _tasksSinceImprove;
|
|
591373
|
+
var _interactiveSessionActive, _interactiveSessionReason, _voiceChatSession, taskManager, _apiCallbacks, _shellToolRef, _replToolRef, _fullSubAgentToolRef, _agentToolRef, _sendMessageToolRef, _agentLifecycleMgr, _activeRunnerRef, _parentRunnerForArchive, _wireSubAgentCallbacks, _wireAgentToolCallbacks, _wireSubAgentToolCallbacks, _autoUpdatedThisSession, _mcpManager, _pluginManager, _mcpTools, SELF_IMPROVE_INTERVAL, _tasksSinceImprove;
|
|
590191
591374
|
var init_interactive = __esm({
|
|
590192
591375
|
"packages/cli/src/tui/interactive.ts"() {
|
|
590193
591376
|
"use strict";
|
|
@@ -590245,6 +591428,7 @@ var init_interactive = __esm({
|
|
|
590245
591428
|
_sendMessageToolRef = null;
|
|
590246
591429
|
_agentLifecycleMgr = getSharedTaskManager();
|
|
590247
591430
|
_activeRunnerRef = null;
|
|
591431
|
+
_parentRunnerForArchive = null;
|
|
590248
591432
|
_wireSubAgentCallbacks = null;
|
|
590249
591433
|
_wireAgentToolCallbacks = null;
|
|
590250
591434
|
_wireSubAgentToolCallbacks = null;
|