open-agents-ai 0.187.448 → 0.187.449
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 +934 -15
- 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,11 +505785,14 @@ 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) {
|
|
505731
505798
|
return {
|
|
@@ -516249,6 +516316,420 @@ var init_messageLog = __esm({
|
|
|
516249
516316
|
}
|
|
516250
516317
|
});
|
|
516251
516318
|
|
|
516319
|
+
// packages/orchestrator/dist/contextTree.js
|
|
516320
|
+
function classifyToolCall(name10, argsJson) {
|
|
516321
|
+
const direct = TOOL_PHASE_MAP[name10];
|
|
516322
|
+
if (direct)
|
|
516323
|
+
return direct;
|
|
516324
|
+
if (name10 === "shell" || name10 === "shell_async") {
|
|
516325
|
+
if (!argsJson)
|
|
516326
|
+
return "implement";
|
|
516327
|
+
const lower = argsJson.toLowerCase();
|
|
516328
|
+
if (/(?:^|"command":")[^"]*(?:test|pytest|jest|mocha|vitest|cargo test|go test|npm test|pnpm test|tsc|lint|check|verify)\b/i.test(lower)) {
|
|
516329
|
+
return "verify";
|
|
516330
|
+
}
|
|
516331
|
+
return "implement";
|
|
516332
|
+
}
|
|
516333
|
+
return null;
|
|
516334
|
+
}
|
|
516335
|
+
function detectPhase(toolCallLog, window2 = 10) {
|
|
516336
|
+
if (toolCallLog.length === 0)
|
|
516337
|
+
return "explore";
|
|
516338
|
+
const recent = toolCallLog.slice(-window2);
|
|
516339
|
+
const counts = {};
|
|
516340
|
+
for (const tc of recent) {
|
|
516341
|
+
const p2 = classifyToolCall(tc.name, tc.argsKey);
|
|
516342
|
+
if (p2)
|
|
516343
|
+
counts[p2] = (counts[p2] ?? 0) + 1;
|
|
516344
|
+
}
|
|
516345
|
+
const total = Object.values(counts).reduce((a2, b) => a2 + b, 0);
|
|
516346
|
+
if (total === 0)
|
|
516347
|
+
return "mixed";
|
|
516348
|
+
let best = null;
|
|
516349
|
+
for (const [phase, n2] of Object.entries(counts)) {
|
|
516350
|
+
if (!best || n2 > best.n)
|
|
516351
|
+
best = { phase, n: n2 };
|
|
516352
|
+
}
|
|
516353
|
+
if (!best || best.n / total < 0.4)
|
|
516354
|
+
return "mixed";
|
|
516355
|
+
return best.phase;
|
|
516356
|
+
}
|
|
516357
|
+
function extractAnchorsFromMessages(messages2, turn) {
|
|
516358
|
+
const anchors = [];
|
|
516359
|
+
const seenSummaries = /* @__PURE__ */ new Set();
|
|
516360
|
+
let counter = 0;
|
|
516361
|
+
const nextId2 = (kind) => `anc-${turn}-${kind}-${counter++}`;
|
|
516362
|
+
for (const msg of messages2) {
|
|
516363
|
+
const content = typeof msg.content === "string" ? msg.content : "";
|
|
516364
|
+
if (!content)
|
|
516365
|
+
continue;
|
|
516366
|
+
if (msg.role === "tool" || msg.role === "assistant") {
|
|
516367
|
+
const matches = content.match(FILE_PATH_RE) ?? [];
|
|
516368
|
+
const distinctPaths = Array.from(new Set(matches.filter((p2) => p2.length > 2 && p2.length < 120))).slice(0, 5);
|
|
516369
|
+
for (const p2 of distinctPaths) {
|
|
516370
|
+
const summary = `file: ${p2}`;
|
|
516371
|
+
if (seenSummaries.has(summary))
|
|
516372
|
+
continue;
|
|
516373
|
+
seenSummaries.add(summary);
|
|
516374
|
+
anchors.push({
|
|
516375
|
+
id: nextId2("file"),
|
|
516376
|
+
type: "file",
|
|
516377
|
+
summary,
|
|
516378
|
+
keywords: [p2.toLowerCase(), ...p2.toLowerCase().split(/[\/\.]/).filter((s2) => s2.length > 2)],
|
|
516379
|
+
turn
|
|
516380
|
+
});
|
|
516381
|
+
}
|
|
516382
|
+
}
|
|
516383
|
+
if (msg.role === "assistant" && DECISION_HINTS.test(content)) {
|
|
516384
|
+
const sentence = content.split(/(?<=[.!?])\s+/).find((s2) => DECISION_HINTS.test(s2)) ?? content.slice(0, 200);
|
|
516385
|
+
const summary = `decision: ${sentence.slice(0, 140).replace(/\s+/g, " ").trim()}`;
|
|
516386
|
+
if (!seenSummaries.has(summary)) {
|
|
516387
|
+
seenSummaries.add(summary);
|
|
516388
|
+
anchors.push({
|
|
516389
|
+
id: nextId2("dec"),
|
|
516390
|
+
type: "decision",
|
|
516391
|
+
summary,
|
|
516392
|
+
keywords: extractKeywords(sentence),
|
|
516393
|
+
turn
|
|
516394
|
+
});
|
|
516395
|
+
}
|
|
516396
|
+
}
|
|
516397
|
+
if (msg.role === "tool" && ERROR_HINTS.test(content)) {
|
|
516398
|
+
const firstErrLine = content.split("\n").find((l2) => ERROR_HINTS.test(l2)) ?? content.slice(0, 200);
|
|
516399
|
+
const summary = `error: ${firstErrLine.slice(0, 140).replace(/\s+/g, " ").trim()}`;
|
|
516400
|
+
if (!seenSummaries.has(summary)) {
|
|
516401
|
+
seenSummaries.add(summary);
|
|
516402
|
+
anchors.push({
|
|
516403
|
+
id: nextId2("err"),
|
|
516404
|
+
type: "error",
|
|
516405
|
+
summary,
|
|
516406
|
+
keywords: extractKeywords(firstErrLine),
|
|
516407
|
+
turn
|
|
516408
|
+
});
|
|
516409
|
+
}
|
|
516410
|
+
}
|
|
516411
|
+
}
|
|
516412
|
+
return anchors;
|
|
516413
|
+
}
|
|
516414
|
+
function extractKeywords(text) {
|
|
516415
|
+
const tokens = text.toLowerCase().replace(/[^\w./-]+/g, " ").split(/\s+/);
|
|
516416
|
+
const out = [];
|
|
516417
|
+
const seen = /* @__PURE__ */ new Set();
|
|
516418
|
+
for (const tk of tokens) {
|
|
516419
|
+
if (tk.length < 3)
|
|
516420
|
+
continue;
|
|
516421
|
+
if (STOPWORDS.has(tk))
|
|
516422
|
+
continue;
|
|
516423
|
+
if (seen.has(tk))
|
|
516424
|
+
continue;
|
|
516425
|
+
seen.add(tk);
|
|
516426
|
+
out.push(tk);
|
|
516427
|
+
if (out.length >= 8)
|
|
516428
|
+
break;
|
|
516429
|
+
}
|
|
516430
|
+
return out;
|
|
516431
|
+
}
|
|
516432
|
+
var TOOL_PHASE_MAP, FILE_PATH_RE, DECISION_HINTS, ERROR_HINTS, STOPWORDS, DEFAULT_OPTS, ContextTree;
|
|
516433
|
+
var init_contextTree = __esm({
|
|
516434
|
+
"packages/orchestrator/dist/contextTree.js"() {
|
|
516435
|
+
"use strict";
|
|
516436
|
+
TOOL_PHASE_MAP = {
|
|
516437
|
+
// explore
|
|
516438
|
+
file_read: "explore",
|
|
516439
|
+
list_directory: "explore",
|
|
516440
|
+
find_files: "explore",
|
|
516441
|
+
grep_search: "explore",
|
|
516442
|
+
file_explore: "explore",
|
|
516443
|
+
glob_find: "explore",
|
|
516444
|
+
// plan
|
|
516445
|
+
todo_write: "plan",
|
|
516446
|
+
todo_read: "plan",
|
|
516447
|
+
working_notes: "plan",
|
|
516448
|
+
memory_write: "plan",
|
|
516449
|
+
memory_read: "plan",
|
|
516450
|
+
// implement
|
|
516451
|
+
file_write: "implement",
|
|
516452
|
+
file_edit: "implement",
|
|
516453
|
+
file_patch: "implement",
|
|
516454
|
+
batch_edit: "implement",
|
|
516455
|
+
// verify (heuristic — shell with test-like commands also contributes;
|
|
516456
|
+
// unknown shell defaults to implement)
|
|
516457
|
+
run_tests: "verify"
|
|
516458
|
+
};
|
|
516459
|
+
FILE_PATH_RE = /[\w./-]*\/[\w./-]+(?:\.[a-z0-9]{1,6})?/gi;
|
|
516460
|
+
DECISION_HINTS = /(?:I'll |Let me |I will |I'm going to |Plan: |Decision: |Approach: )/i;
|
|
516461
|
+
ERROR_HINTS = /(?:error|fail|exception|traceback|enoent|enotfound|fatal)/i;
|
|
516462
|
+
STOPWORDS = /* @__PURE__ */ new Set([
|
|
516463
|
+
"the",
|
|
516464
|
+
"and",
|
|
516465
|
+
"or",
|
|
516466
|
+
"of",
|
|
516467
|
+
"to",
|
|
516468
|
+
"in",
|
|
516469
|
+
"is",
|
|
516470
|
+
"it",
|
|
516471
|
+
"this",
|
|
516472
|
+
"that",
|
|
516473
|
+
"for",
|
|
516474
|
+
"on",
|
|
516475
|
+
"with",
|
|
516476
|
+
"as",
|
|
516477
|
+
"by",
|
|
516478
|
+
"from",
|
|
516479
|
+
"at",
|
|
516480
|
+
"an",
|
|
516481
|
+
"be",
|
|
516482
|
+
"are",
|
|
516483
|
+
"was",
|
|
516484
|
+
"were",
|
|
516485
|
+
"if",
|
|
516486
|
+
"then",
|
|
516487
|
+
"you",
|
|
516488
|
+
"we",
|
|
516489
|
+
"i",
|
|
516490
|
+
"but",
|
|
516491
|
+
"not",
|
|
516492
|
+
"no",
|
|
516493
|
+
"do",
|
|
516494
|
+
"does",
|
|
516495
|
+
"did",
|
|
516496
|
+
"have",
|
|
516497
|
+
"has",
|
|
516498
|
+
"had",
|
|
516499
|
+
"will",
|
|
516500
|
+
"would",
|
|
516501
|
+
"should",
|
|
516502
|
+
"can",
|
|
516503
|
+
"could"
|
|
516504
|
+
]);
|
|
516505
|
+
DEFAULT_OPTS = {
|
|
516506
|
+
maxActiveMessages: 60,
|
|
516507
|
+
maxAnchorsPerNode: 12,
|
|
516508
|
+
phaseWindow: 10
|
|
516509
|
+
};
|
|
516510
|
+
ContextTree = class {
|
|
516511
|
+
snapshot;
|
|
516512
|
+
opts;
|
|
516513
|
+
currentPhase = "mixed";
|
|
516514
|
+
currentTurn = 0;
|
|
516515
|
+
toolCallLog = [];
|
|
516516
|
+
constructor(systemPromptHash, activeGoal, opts) {
|
|
516517
|
+
this.opts = { ...DEFAULT_OPTS, ...opts };
|
|
516518
|
+
this.snapshot = {
|
|
516519
|
+
rootSystemPromptHash: systemPromptHash,
|
|
516520
|
+
activeGoal,
|
|
516521
|
+
phases: {},
|
|
516522
|
+
history: [],
|
|
516523
|
+
archive: []
|
|
516524
|
+
};
|
|
516525
|
+
}
|
|
516526
|
+
/** Returns the current detected phase. */
|
|
516527
|
+
getCurrentPhase() {
|
|
516528
|
+
return this.currentPhase;
|
|
516529
|
+
}
|
|
516530
|
+
/** Returns a read-only snapshot for inspection / serialization. */
|
|
516531
|
+
getSnapshot() {
|
|
516532
|
+
return this.snapshot;
|
|
516533
|
+
}
|
|
516534
|
+
/** Update the active goal (e.g. after compaction rewrites the goal). */
|
|
516535
|
+
setActiveGoal(goal) {
|
|
516536
|
+
this.snapshot.activeGoal = goal;
|
|
516537
|
+
}
|
|
516538
|
+
/**
|
|
516539
|
+
* Record a tool call. Updates phase classification but does not move
|
|
516540
|
+
* messages between nodes (caller must invoke maybeTransition).
|
|
516541
|
+
*/
|
|
516542
|
+
observeToolCall(name10, argsKey, turn) {
|
|
516543
|
+
this.toolCallLog.push({ name: name10, argsKey, turn });
|
|
516544
|
+
if (this.toolCallLog.length > 200) {
|
|
516545
|
+
this.toolCallLog = this.toolCallLog.slice(-100);
|
|
516546
|
+
}
|
|
516547
|
+
this.currentTurn = turn;
|
|
516548
|
+
}
|
|
516549
|
+
/**
|
|
516550
|
+
* Detect a phase transition. Returns the previous phase if a transition
|
|
516551
|
+
* happened, or null otherwise. Callers should follow up with
|
|
516552
|
+
* contractInactive to summarize the just-exited phase.
|
|
516553
|
+
*/
|
|
516554
|
+
maybeTransition(turn) {
|
|
516555
|
+
const detected = detectPhase(this.toolCallLog, this.opts.phaseWindow);
|
|
516556
|
+
if (detected === this.currentPhase)
|
|
516557
|
+
return null;
|
|
516558
|
+
const from3 = this.currentPhase;
|
|
516559
|
+
this.currentPhase = detected;
|
|
516560
|
+
if (!this.snapshot.phases[detected]) {
|
|
516561
|
+
this.snapshot.phases[detected] = {
|
|
516562
|
+
status: "active",
|
|
516563
|
+
messages: [],
|
|
516564
|
+
anchors: [],
|
|
516565
|
+
startedAtTurn: turn
|
|
516566
|
+
};
|
|
516567
|
+
} else {
|
|
516568
|
+
this.snapshot.phases[detected].status = "active";
|
|
516569
|
+
}
|
|
516570
|
+
return { from: from3, to: detected };
|
|
516571
|
+
}
|
|
516572
|
+
/**
|
|
516573
|
+
* Sync a slice of messages into the current active phase node.
|
|
516574
|
+
* Caller is expected to provide ALL messages owned by the current
|
|
516575
|
+
* phase since the last sync (typically the suffix beyond a known
|
|
516576
|
+
* boundary index).
|
|
516577
|
+
*/
|
|
516578
|
+
observePhaseMessages(messages2) {
|
|
516579
|
+
const node = this.snapshot.phases[this.currentPhase];
|
|
516580
|
+
if (!node)
|
|
516581
|
+
return;
|
|
516582
|
+
node.messages = messages2;
|
|
516583
|
+
if (messages2.length > this.opts.maxActiveMessages) {
|
|
516584
|
+
this.contract(this.currentPhase, this.currentTurn);
|
|
516585
|
+
}
|
|
516586
|
+
}
|
|
516587
|
+
/**
|
|
516588
|
+
* Contract the named phase: extract anchors, generate a stub summary,
|
|
516589
|
+
* mark the node `contracted`. The caller may then archive via the
|
|
516590
|
+
* archive callback (Phase 1 hands off to memex).
|
|
516591
|
+
*/
|
|
516592
|
+
contract(phase, turn, summarizer) {
|
|
516593
|
+
const node = this.snapshot.phases[phase];
|
|
516594
|
+
if (!node || node.status !== "active")
|
|
516595
|
+
return null;
|
|
516596
|
+
const anchors = extractAnchorsFromMessages(node.messages, turn).slice(0, this.opts.maxAnchorsPerNode);
|
|
516597
|
+
node.anchors = [...node.anchors, ...anchors].slice(-this.opts.maxAnchorsPerNode);
|
|
516598
|
+
node.summary = summarizer ? summarizer(node.messages) : this.defaultSummary(phase, node.messages);
|
|
516599
|
+
node.status = "contracted";
|
|
516600
|
+
node.contractedAtTurn = turn;
|
|
516601
|
+
this.snapshot.history.push({ ...node, phase });
|
|
516602
|
+
if (this.snapshot.history.length > 32) {
|
|
516603
|
+
this.snapshot.history = this.snapshot.history.slice(-32);
|
|
516604
|
+
}
|
|
516605
|
+
return node;
|
|
516606
|
+
}
|
|
516607
|
+
/** Contract every active phase except the current one. */
|
|
516608
|
+
contractInactive(turn, summarizer) {
|
|
516609
|
+
const contracted = [];
|
|
516610
|
+
for (const [phase, node] of Object.entries(this.snapshot.phases)) {
|
|
516611
|
+
if (phase === this.currentPhase)
|
|
516612
|
+
continue;
|
|
516613
|
+
if (node.status !== "active")
|
|
516614
|
+
continue;
|
|
516615
|
+
this.contract(phase, turn, summarizer);
|
|
516616
|
+
contracted.push(phase);
|
|
516617
|
+
}
|
|
516618
|
+
return contracted;
|
|
516619
|
+
}
|
|
516620
|
+
/**
|
|
516621
|
+
* Mark a contracted node as archived after its summary has been moved
|
|
516622
|
+
* to memex. Caller passes the memex id.
|
|
516623
|
+
*/
|
|
516624
|
+
archive(phase, memexId) {
|
|
516625
|
+
const node = this.snapshot.phases[phase];
|
|
516626
|
+
if (!node || node.status !== "contracted")
|
|
516627
|
+
return;
|
|
516628
|
+
node.status = "archived";
|
|
516629
|
+
node.summary = void 0;
|
|
516630
|
+
if (!this.snapshot.archive.includes(memexId)) {
|
|
516631
|
+
this.snapshot.archive.push(memexId);
|
|
516632
|
+
}
|
|
516633
|
+
}
|
|
516634
|
+
/**
|
|
516635
|
+
* Restore a contracted phase: pulls the last contracted node for the phase
|
|
516636
|
+
* back into active state. Returns the restored node or null if none found.
|
|
516637
|
+
*/
|
|
516638
|
+
expand(phase) {
|
|
516639
|
+
const node = this.snapshot.phases[phase];
|
|
516640
|
+
if (node && node.status === "contracted") {
|
|
516641
|
+
node.status = "active";
|
|
516642
|
+
return node;
|
|
516643
|
+
}
|
|
516644
|
+
const histNode = [...this.snapshot.history].reverse().find((h) => h.phase === phase);
|
|
516645
|
+
if (histNode) {
|
|
516646
|
+
const restored = {
|
|
516647
|
+
status: "active",
|
|
516648
|
+
messages: [],
|
|
516649
|
+
anchors: histNode.anchors,
|
|
516650
|
+
startedAtTurn: this.currentTurn
|
|
516651
|
+
};
|
|
516652
|
+
this.snapshot.phases[phase] = restored;
|
|
516653
|
+
return restored;
|
|
516654
|
+
}
|
|
516655
|
+
return null;
|
|
516656
|
+
}
|
|
516657
|
+
/** Render a compact phase-status block for inclusion in prompts. */
|
|
516658
|
+
renderStatusBlock() {
|
|
516659
|
+
const lines = [`### Phase Status`, `- current: **${this.currentPhase}**`];
|
|
516660
|
+
const phaseSummaries = [];
|
|
516661
|
+
for (const [phase, node] of Object.entries(this.snapshot.phases)) {
|
|
516662
|
+
if (phase === this.currentPhase)
|
|
516663
|
+
continue;
|
|
516664
|
+
const status = node.status;
|
|
516665
|
+
const anchorCount = node.anchors.length;
|
|
516666
|
+
phaseSummaries.push(`- ${phase}: ${status}${anchorCount > 0 ? ` (${anchorCount} anchors)` : ""}`);
|
|
516667
|
+
}
|
|
516668
|
+
if (phaseSummaries.length > 0) {
|
|
516669
|
+
lines.push(...phaseSummaries);
|
|
516670
|
+
}
|
|
516671
|
+
if (this.snapshot.archive.length > 0) {
|
|
516672
|
+
lines.push(`- archived: ${this.snapshot.archive.length} (use memex_retrieve)`);
|
|
516673
|
+
}
|
|
516674
|
+
return lines.join("\n");
|
|
516675
|
+
}
|
|
516676
|
+
/** Render all retained anchors (across phases) for compaction summary. */
|
|
516677
|
+
renderAnchorBlock(maxPerPhase = 5) {
|
|
516678
|
+
const lines = [];
|
|
516679
|
+
for (const [phase, node] of Object.entries(this.snapshot.phases)) {
|
|
516680
|
+
if (node.anchors.length === 0)
|
|
516681
|
+
continue;
|
|
516682
|
+
lines.push(`**${phase} anchors:**`);
|
|
516683
|
+
for (const a2 of node.anchors.slice(-maxPerPhase)) {
|
|
516684
|
+
lines.push(`- [${a2.type}] ${a2.summary}`);
|
|
516685
|
+
}
|
|
516686
|
+
}
|
|
516687
|
+
return lines.length > 0 ? `### Phase Anchors
|
|
516688
|
+
${lines.join("\n")}` : "";
|
|
516689
|
+
}
|
|
516690
|
+
/** Look up anchors whose keywords overlap the given query (Phase 6 hook). */
|
|
516691
|
+
findAnchorsByKeywords(query, max = 5) {
|
|
516692
|
+
const tokens = extractKeywords(query.toLowerCase());
|
|
516693
|
+
if (tokens.length === 0)
|
|
516694
|
+
return [];
|
|
516695
|
+
const tokenSet = new Set(tokens);
|
|
516696
|
+
const scored = [];
|
|
516697
|
+
for (const node of Object.values(this.snapshot.phases)) {
|
|
516698
|
+
for (const a2 of node.anchors) {
|
|
516699
|
+
let score = 0;
|
|
516700
|
+
for (const k of a2.keywords) {
|
|
516701
|
+
if (tokenSet.has(k))
|
|
516702
|
+
score += 2;
|
|
516703
|
+
for (const t2 of tokenSet) {
|
|
516704
|
+
if (k.includes(t2) || t2.includes(k))
|
|
516705
|
+
score += 1;
|
|
516706
|
+
}
|
|
516707
|
+
}
|
|
516708
|
+
if (score > 0)
|
|
516709
|
+
scored.push({ anchor: a2, score });
|
|
516710
|
+
}
|
|
516711
|
+
}
|
|
516712
|
+
scored.sort((a2, b) => b.score - a2.score);
|
|
516713
|
+
return scored.slice(0, max).map((s2) => s2.anchor);
|
|
516714
|
+
}
|
|
516715
|
+
defaultSummary(phase, messages2) {
|
|
516716
|
+
const toolCallNames = /* @__PURE__ */ new Set();
|
|
516717
|
+
let assistantWords = 0;
|
|
516718
|
+
for (const m2 of messages2) {
|
|
516719
|
+
if (m2.role === "assistant" && m2.tool_calls) {
|
|
516720
|
+
for (const tc of m2.tool_calls)
|
|
516721
|
+
toolCallNames.add(tc.function.name);
|
|
516722
|
+
}
|
|
516723
|
+
if (m2.role === "assistant" && typeof m2.content === "string") {
|
|
516724
|
+
assistantWords += m2.content.split(/\s+/).length;
|
|
516725
|
+
}
|
|
516726
|
+
}
|
|
516727
|
+
return `${phase} phase: ${messages2.length} messages, ${toolCallNames.size} distinct tools used, ${assistantWords} words of assistant output.`;
|
|
516728
|
+
}
|
|
516729
|
+
};
|
|
516730
|
+
}
|
|
516731
|
+
});
|
|
516732
|
+
|
|
516252
516733
|
// packages/orchestrator/dist/codeGraphLink.js
|
|
516253
516734
|
function isCodeGraphLinkEnabled() {
|
|
516254
516735
|
return process.env["OA_CODEGRAPH_LINK"] !== "0";
|
|
@@ -517095,7 +517576,7 @@ function classifyThinkOutcome(raw) {
|
|
|
517095
517576
|
}
|
|
517096
517577
|
return null;
|
|
517097
517578
|
}
|
|
517098
|
-
var SYSTEM_PROMPT, SYSTEM_PROMPT_MEDIUM, SYSTEM_PROMPT_SMALL, VISUAL_TOOLS, AUDIO_TOOLS, SOCIAL_TOOLS, SPATIAL_TOOLS, CODE_TOOLS, AgenticRunner, OllamaAgenticBackend;
|
|
517579
|
+
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
517580
|
var init_agenticRunner = __esm({
|
|
517100
517581
|
"packages/orchestrator/dist/agenticRunner.js"() {
|
|
517101
517582
|
"use strict";
|
|
@@ -517109,12 +517590,23 @@ var init_agenticRunner = __esm({
|
|
|
517109
517590
|
init_reflectionBuffer();
|
|
517110
517591
|
init_taskHandoff();
|
|
517111
517592
|
init_messageLog();
|
|
517593
|
+
init_contextTree();
|
|
517112
517594
|
init_codeGraphLink();
|
|
517113
517595
|
init_dist5();
|
|
517114
517596
|
init_tool_batching();
|
|
517115
517597
|
init_hooks();
|
|
517116
517598
|
init_app_state();
|
|
517117
517599
|
init_streaming_executor();
|
|
517600
|
+
TOOL_SUBSETS = {
|
|
517601
|
+
web: ["web_search", "web_fetch", "web_crawl"],
|
|
517602
|
+
code: ["file_patch", "file_explore", "batch_edit", "file_read", "file_write", "file_edit"],
|
|
517603
|
+
agent: ["agent", "sub_agent", "background_run"],
|
|
517604
|
+
memory: ["memory_search", "memory_read", "memory_write", "memex_retrieve", "working_notes"],
|
|
517605
|
+
shell: ["shell", "shell_async", "kill_proc", "run_tests"],
|
|
517606
|
+
graph: ["graph_query", "graph_traverse", "code_graph"],
|
|
517607
|
+
skill: ["skill_list", "skill_execute", "skill_search"]
|
|
517608
|
+
};
|
|
517609
|
+
TOOL_AUTO_DEMOTE_TURNS = 10;
|
|
517118
517610
|
SYSTEM_PROMPT = loadPrompt("agentic/system-large.md");
|
|
517119
517611
|
SYSTEM_PROMPT_MEDIUM = loadPrompt("agentic/system-medium.md");
|
|
517120
517612
|
SYSTEM_PROMPT_SMALL = loadPrompt("agentic/system-small.md");
|
|
@@ -517222,6 +517714,21 @@ var init_agenticRunner = __esm({
|
|
|
517222
517714
|
_patchHistoryStore = null;
|
|
517223
517715
|
_toolSequence = [];
|
|
517224
517716
|
// Track tool calls for pattern detection
|
|
517717
|
+
// Phase 2 — tool subset expansion and usage tracking.
|
|
517718
|
+
// Tools promoted from deferred → inline by tool_search (or by subset
|
|
517719
|
+
// expansion). They stay inline for the rest of the run unless idle past
|
|
517720
|
+
// TOOL_AUTO_DEMOTE_TURNS, at which point buildToolDefinitions drops them
|
|
517721
|
+
// back to deferred to reclaim token budget.
|
|
517722
|
+
_activatedTools = /* @__PURE__ */ new Set();
|
|
517723
|
+
_toolLastUsedTurn = /* @__PURE__ */ new Map();
|
|
517724
|
+
// Phase 1 — Context Tree. Tracks current phase + per-phase anchors so
|
|
517725
|
+
// compactMessages can summarize-by-phase and Phase 6 can surface anchors
|
|
517726
|
+
// by keyword. Initialized lazily in run() because the system-prompt hash
|
|
517727
|
+
// depends on the actual prompt resolved at run time.
|
|
517728
|
+
_contextTree = null;
|
|
517729
|
+
// Phase 6 — last-surface guard so we don't re-inject the same anchor on
|
|
517730
|
+
// consecutive turns when its keywords still match.
|
|
517731
|
+
_lastSurfacedAnchorIds = /* @__PURE__ */ new Set();
|
|
517225
517732
|
/** WO-AM-10: Process pending episode embeddings in background batches */
|
|
517226
517733
|
async processPendingEmbeddings() {
|
|
517227
517734
|
if (this._pendingEmbeddings.length === 0 || !this._episodeStore)
|
|
@@ -517280,7 +517787,11 @@ var init_agenticRunner = __esm({
|
|
|
517280
517787
|
personality: options2?.personality ?? PERSONALITY_PRESETS.balanced,
|
|
517281
517788
|
personalityName: options2?.personalityName ?? "",
|
|
517282
517789
|
finalVarResolver: options2?.finalVarResolver ?? void 0,
|
|
517283
|
-
observerMode: options2?.observerMode ?? "both"
|
|
517790
|
+
observerMode: options2?.observerMode ?? "both",
|
|
517791
|
+
// Phase 4 — sub-agent isolation flag (defaults false). When true, this
|
|
517792
|
+
// runner skips cross-task handoff inheritance from the parent's
|
|
517793
|
+
// session.
|
|
517794
|
+
subAgent: options2?.subAgent ?? false
|
|
517284
517795
|
};
|
|
517285
517796
|
this._observerMode = this.options.observerMode;
|
|
517286
517797
|
}
|
|
@@ -517685,6 +518196,212 @@ ${body}`;
|
|
|
517685
518196
|
* Hannover reference: services/compact/apiMicrocompact.ts
|
|
517686
518197
|
* Research: arXiv:2307.03172 (Lost in the Middle — recent context matters most)
|
|
517687
518198
|
*/
|
|
518199
|
+
/**
|
|
518200
|
+
* Phase 5 — Proactive context pruning (precedes microcompact). Targets
|
|
518201
|
+
* specific waste patterns rather than blanket-clearing old tool results:
|
|
518202
|
+
*
|
|
518203
|
+
* 1. Duplicate tool calls — same (name, args) called multiple times.
|
|
518204
|
+
* Older instances replaced with "[deduped — see turn N]".
|
|
518205
|
+
* 2. Aged file_read results — file_read older than `agedFileReadTurns`
|
|
518206
|
+
* replaced with a summary stub (path + size + first-line preview).
|
|
518207
|
+
* 3. Successful shell/test runs — shell without error markers older
|
|
518208
|
+
* than `agedShellTurns` replaced with a "succeeded" stub.
|
|
518209
|
+
*
|
|
518210
|
+
* Mutates `messages` in place. Cheap O(n) walk; safe to run every turn.
|
|
518211
|
+
* Refs: proposal §3 Phase 5, AgentFold (arXiv:2510.24699) progressive
|
|
518212
|
+
* summarization, RECOMP (ICLR 2024) observation masking.
|
|
518213
|
+
*/
|
|
518214
|
+
proactivePrune(messages2, currentTurn) {
|
|
518215
|
+
if (process.env["OA_DISABLE_PROACTIVE_PRUNE"] === "1")
|
|
518216
|
+
return;
|
|
518217
|
+
const AGED_FILE_READ_TURNS = 10;
|
|
518218
|
+
const AGED_SHELL_TURNS = 5;
|
|
518219
|
+
const ERROR_MARKERS = /(?:error|fail|exception|traceback|enoent|enotfound|exit code [^0]|status[: ]+1\d?\d?)/i;
|
|
518220
|
+
const PRUNE_PREFIX = "[Tool result cleared";
|
|
518221
|
+
const DEDUPE_PREFIX = "[deduped — same call as turn";
|
|
518222
|
+
const FILE_AGED_PREFIX = "[file_read aged out, summary:";
|
|
518223
|
+
const SHELL_AGED_PREFIX = "[shell succeeded, output pruned —";
|
|
518224
|
+
const seen = /* @__PURE__ */ new Map();
|
|
518225
|
+
const pending = [];
|
|
518226
|
+
let scanTurn = 0;
|
|
518227
|
+
for (let i2 = 0; i2 < messages2.length; i2++) {
|
|
518228
|
+
const msg = messages2[i2];
|
|
518229
|
+
if (msg.role === "assistant" && msg.tool_calls && msg.tool_calls.length > 0) {
|
|
518230
|
+
scanTurn++;
|
|
518231
|
+
for (const tc of msg.tool_calls) {
|
|
518232
|
+
const name10 = tc.function.name;
|
|
518233
|
+
const argsRaw = (tc.function.arguments ?? "").slice(0, 200);
|
|
518234
|
+
const fp = `${name10}|${argsRaw}`;
|
|
518235
|
+
let resultIdx = -1;
|
|
518236
|
+
for (let j = i2 + 1; j < Math.min(messages2.length, i2 + 8); j++) {
|
|
518237
|
+
const r2 = messages2[j];
|
|
518238
|
+
if (r2.role === "tool" && r2.tool_call_id === tc.id) {
|
|
518239
|
+
resultIdx = j;
|
|
518240
|
+
break;
|
|
518241
|
+
}
|
|
518242
|
+
}
|
|
518243
|
+
if (resultIdx === -1)
|
|
518244
|
+
continue;
|
|
518245
|
+
const resultMsg = messages2[resultIdx];
|
|
518246
|
+
const content = typeof resultMsg.content === "string" ? resultMsg.content : "";
|
|
518247
|
+
if (content.startsWith(PRUNE_PREFIX) || content.startsWith(DEDUPE_PREFIX) || content.startsWith(FILE_AGED_PREFIX) || content.startsWith(SHELL_AGED_PREFIX)) {
|
|
518248
|
+
if (!seen.has(fp))
|
|
518249
|
+
seen.set(fp, { turn: scanTurn, idx: resultIdx });
|
|
518250
|
+
continue;
|
|
518251
|
+
}
|
|
518252
|
+
const prior = seen.get(fp);
|
|
518253
|
+
if (prior) {
|
|
518254
|
+
pending.push({
|
|
518255
|
+
idx: prior.idx,
|
|
518256
|
+
reason: "dedupe",
|
|
518257
|
+
replacement: `${DEDUPE_PREFIX} ${scanTurn} — duplicate ${name10}() call]`
|
|
518258
|
+
});
|
|
518259
|
+
seen.set(fp, { turn: scanTurn, idx: resultIdx });
|
|
518260
|
+
continue;
|
|
518261
|
+
}
|
|
518262
|
+
seen.set(fp, { turn: scanTurn, idx: resultIdx });
|
|
518263
|
+
const ageTurns = currentTurn - scanTurn;
|
|
518264
|
+
if (name10 === "file_read" && ageTurns > AGED_FILE_READ_TURNS && content.length > 200) {
|
|
518265
|
+
const pathArg = (() => {
|
|
518266
|
+
try {
|
|
518267
|
+
const o2 = JSON.parse(tc.function.arguments || "{}");
|
|
518268
|
+
return String(o2.path ?? o2.file ?? "?");
|
|
518269
|
+
} catch {
|
|
518270
|
+
return "?";
|
|
518271
|
+
}
|
|
518272
|
+
})();
|
|
518273
|
+
const firstLine = content.split("\n")[0]?.slice(0, 80) ?? "";
|
|
518274
|
+
pending.push({
|
|
518275
|
+
idx: resultIdx,
|
|
518276
|
+
reason: "aged_file",
|
|
518277
|
+
replacement: `${FILE_AGED_PREFIX} path=${pathArg}, size=${content.length} chars, first-line="${firstLine}"]`
|
|
518278
|
+
});
|
|
518279
|
+
continue;
|
|
518280
|
+
}
|
|
518281
|
+
if ((name10 === "shell" || name10 === "shell_async" || name10 === "run_tests") && ageTurns > AGED_SHELL_TURNS && content.length > 200 && !ERROR_MARKERS.test(content)) {
|
|
518282
|
+
const cmdArg = (() => {
|
|
518283
|
+
try {
|
|
518284
|
+
const o2 = JSON.parse(tc.function.arguments || "{}");
|
|
518285
|
+
return String(o2.command ?? o2.cmd ?? "?").slice(0, 80);
|
|
518286
|
+
} catch {
|
|
518287
|
+
return "?";
|
|
518288
|
+
}
|
|
518289
|
+
})();
|
|
518290
|
+
pending.push({
|
|
518291
|
+
idx: resultIdx,
|
|
518292
|
+
reason: "aged_shell",
|
|
518293
|
+
replacement: `${SHELL_AGED_PREFIX} command="${cmdArg}", output ${content.length} chars, no error markers detected]`
|
|
518294
|
+
});
|
|
518295
|
+
}
|
|
518296
|
+
}
|
|
518297
|
+
}
|
|
518298
|
+
}
|
|
518299
|
+
if (pending.length === 0)
|
|
518300
|
+
return;
|
|
518301
|
+
let dedupes = 0, agedFiles = 0, agedShells = 0;
|
|
518302
|
+
for (const p2 of pending) {
|
|
518303
|
+
const m2 = messages2[p2.idx];
|
|
518304
|
+
if (!m2)
|
|
518305
|
+
continue;
|
|
518306
|
+
messages2[p2.idx] = { ...m2, content: p2.replacement };
|
|
518307
|
+
if (p2.reason === "dedupe")
|
|
518308
|
+
dedupes++;
|
|
518309
|
+
else if (p2.reason === "aged_file")
|
|
518310
|
+
agedFiles++;
|
|
518311
|
+
else if (p2.reason === "aged_shell")
|
|
518312
|
+
agedShells++;
|
|
518313
|
+
}
|
|
518314
|
+
const parts = [];
|
|
518315
|
+
if (dedupes > 0)
|
|
518316
|
+
parts.push(`${dedupes} duplicate call(s)`);
|
|
518317
|
+
if (agedFiles > 0)
|
|
518318
|
+
parts.push(`${agedFiles} aged file_read(s)`);
|
|
518319
|
+
if (agedShells > 0)
|
|
518320
|
+
parts.push(`${agedShells} aged shell run(s)`);
|
|
518321
|
+
if (parts.length > 0) {
|
|
518322
|
+
this.emit({
|
|
518323
|
+
type: "status",
|
|
518324
|
+
content: `Proactive prune: replaced ${parts.join(", ")} with summary stubs`,
|
|
518325
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
518326
|
+
});
|
|
518327
|
+
}
|
|
518328
|
+
}
|
|
518329
|
+
/**
|
|
518330
|
+
* Phase 6 — Anchor Surfacing.
|
|
518331
|
+
*
|
|
518332
|
+
* Scans the most recent user / assistant text for keyword overlap with
|
|
518333
|
+
* the ContextTree's anchor index plus the memex archive index. When
|
|
518334
|
+
* matches are found, injects a single-line system hint listing the
|
|
518335
|
+
* available anchors so the model knows what archived context exists
|
|
518336
|
+
* (and may call memex_retrieve to expand it).
|
|
518337
|
+
*
|
|
518338
|
+
* Refs: proposal §3 Phase 6, Lost-in-the-Middle (arXiv:2307.03172) —
|
|
518339
|
+
* surfacing relevant context near the tail combats positional decay.
|
|
518340
|
+
*/
|
|
518341
|
+
surfaceAnchors(messages2) {
|
|
518342
|
+
if (process.env["OA_DISABLE_ANCHOR_SURFACING"] === "1")
|
|
518343
|
+
return;
|
|
518344
|
+
if (!this._contextTree)
|
|
518345
|
+
return;
|
|
518346
|
+
if (messages2.length === 0)
|
|
518347
|
+
return;
|
|
518348
|
+
const tail = messages2.slice(-6);
|
|
518349
|
+
const queryParts = [];
|
|
518350
|
+
for (const m2 of tail) {
|
|
518351
|
+
if (typeof m2.content === "string" && m2.content.length > 0) {
|
|
518352
|
+
if (m2.role === "user" || m2.role === "assistant") {
|
|
518353
|
+
queryParts.push(m2.content);
|
|
518354
|
+
}
|
|
518355
|
+
}
|
|
518356
|
+
}
|
|
518357
|
+
const query = queryParts.join(" ").slice(0, 800);
|
|
518358
|
+
if (!query)
|
|
518359
|
+
return;
|
|
518360
|
+
const anchors = this._contextTree.findAnchorsByKeywords(query, 5);
|
|
518361
|
+
const memexMatches = [];
|
|
518362
|
+
if (this._memexArchive.size > 0) {
|
|
518363
|
+
const tokens = query.toLowerCase().replace(/[^\w./-]+/g, " ").split(/\s+/).filter((t2) => t2.length > 3);
|
|
518364
|
+
const tokenSet = new Set(tokens);
|
|
518365
|
+
for (const e2 of this._memexArchive.values()) {
|
|
518366
|
+
const summaryLower = e2.summary.toLowerCase();
|
|
518367
|
+
let score = 0;
|
|
518368
|
+
for (const t2 of tokenSet) {
|
|
518369
|
+
if (summaryLower.includes(t2))
|
|
518370
|
+
score += 2;
|
|
518371
|
+
}
|
|
518372
|
+
if (e2.toolName === "todo_complete" && tokenSet.has("todo"))
|
|
518373
|
+
score += 1;
|
|
518374
|
+
if (score > 0)
|
|
518375
|
+
memexMatches.push({ id: e2.id, summary: e2.summary, score });
|
|
518376
|
+
}
|
|
518377
|
+
memexMatches.sort((a2, b) => b.score - a2.score);
|
|
518378
|
+
}
|
|
518379
|
+
const newAnchors = anchors.filter((a2) => !this._lastSurfacedAnchorIds.has(a2.id)).slice(0, 3);
|
|
518380
|
+
const newMemex = memexMatches.filter((m2) => !this._lastSurfacedAnchorIds.has(m2.id)).slice(0, 2);
|
|
518381
|
+
if (newAnchors.length === 0 && newMemex.length === 0)
|
|
518382
|
+
return;
|
|
518383
|
+
const lines = [`[Anchor surface] Relevant archived context for the current activity:`];
|
|
518384
|
+
for (const a2 of newAnchors) {
|
|
518385
|
+
lines.push(` - [${a2.type}] ${a2.summary}`);
|
|
518386
|
+
}
|
|
518387
|
+
for (const m2 of newMemex) {
|
|
518388
|
+
lines.push(` - [memex:${m2.id}] ${m2.summary} — call memex_retrieve("${m2.id}") for full body`);
|
|
518389
|
+
}
|
|
518390
|
+
lines.push(`(Anchors are reminders. Pull only what you actually need; ignore otherwise.)`);
|
|
518391
|
+
messages2.push({
|
|
518392
|
+
role: "system",
|
|
518393
|
+
content: lines.join("\n")
|
|
518394
|
+
});
|
|
518395
|
+
for (const a2 of newAnchors)
|
|
518396
|
+
this._lastSurfacedAnchorIds.add(a2.id);
|
|
518397
|
+
for (const m2 of newMemex)
|
|
518398
|
+
this._lastSurfacedAnchorIds.add(m2.id);
|
|
518399
|
+
this.emit({
|
|
518400
|
+
type: "status",
|
|
518401
|
+
content: `Anchor surface: surfaced ${newAnchors.length} anchor(s) + ${newMemex.length} memex entry(ies)`,
|
|
518402
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
518403
|
+
});
|
|
518404
|
+
}
|
|
517688
518405
|
microcompact(messages2, recentToolResults) {
|
|
517689
518406
|
const tier = this.options.modelTier ?? "large";
|
|
517690
518407
|
let keepResults = tier === "small" ? 6 : tier === "medium" ? 10 : 20;
|
|
@@ -518056,6 +518773,10 @@ Respond with your assessment, then take action.`;
|
|
|
518056
518773
|
this.aborted = false;
|
|
518057
518774
|
this._paused = false;
|
|
518058
518775
|
this._toolSequence = [];
|
|
518776
|
+
this._activatedTools.clear();
|
|
518777
|
+
this._toolLastUsedTurn.clear();
|
|
518778
|
+
this._contextTree = null;
|
|
518779
|
+
this._lastSurfacedAnchorIds.clear();
|
|
518059
518780
|
if (!this._memoryInitialized) {
|
|
518060
518781
|
try {
|
|
518061
518782
|
const path8 = await import("node:path");
|
|
@@ -518135,6 +518856,7 @@ Respond with your assessment, then take action.`;
|
|
|
518135
518856
|
this._hookManager.runSessionHook("session_start", this._sessionId);
|
|
518136
518857
|
const contextComposition = await this.assembleContext(task, context2);
|
|
518137
518858
|
const systemPrompt = contextComposition.assembled;
|
|
518859
|
+
this._contextTree = new ContextTree(`sys-${systemPrompt.length}`, cleanedTask.slice(0, 200));
|
|
518138
518860
|
this.emit({
|
|
518139
518861
|
type: "status",
|
|
518140
518862
|
content: `Context assembled: ${contextComposition.sections.map((s2) => `${s2.label}(${s2.tokenEstimate}t)`).join(" + ")} = ~${contextComposition.totalTokenEstimate}t`,
|
|
@@ -518152,6 +518874,8 @@ TASK: ${task}` : task;
|
|
|
518152
518874
|
{ role: "user", content: userContent }
|
|
518153
518875
|
];
|
|
518154
518876
|
try {
|
|
518877
|
+
if (this.options.subAgent)
|
|
518878
|
+
throw "skip-handoff-subagent";
|
|
518155
518879
|
const oaDir = this._workingDirectory ? _pathJoin(this._workingDirectory, ".oa") : _pathJoin(process.cwd(), ".oa");
|
|
518156
518880
|
const chainPairs = loadMessagePairsFromLog(oaDir, { currentTask: cleanedTask });
|
|
518157
518881
|
if (chainPairs.length > 0) {
|
|
@@ -518590,7 +519314,9 @@ ${memoryLines.join("\n")}`
|
|
|
518590
519314
|
}
|
|
518591
519315
|
}
|
|
518592
519316
|
this._lastAssistantTimestamp = Date.now();
|
|
519317
|
+
this.proactivePrune(compacted, turn);
|
|
518593
519318
|
this.microcompact(compacted, recentToolResults);
|
|
519319
|
+
this.surfaceAnchors(compacted);
|
|
518594
519320
|
const { maxOutputTokens: effectiveMaxTokens } = this.contextLimits();
|
|
518595
519321
|
const chatRequest = {
|
|
518596
519322
|
messages: compacted,
|
|
@@ -518879,6 +519605,28 @@ ${memoryLines.join("\n")}`
|
|
|
518879
519605
|
toolCallCount++;
|
|
518880
519606
|
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
519607
|
toolCallLog.push({ name: tc.name, argsKey, turn, timestampMs: Date.now() });
|
|
519608
|
+
this._toolLastUsedTurn.set(tc.name, turn);
|
|
519609
|
+
if (this._contextTree) {
|
|
519610
|
+
this._contextTree.observeToolCall(tc.name, argsKey, turn);
|
|
519611
|
+
const transition = this._contextTree.maybeTransition(turn);
|
|
519612
|
+
if (transition) {
|
|
519613
|
+
this.emit({
|
|
519614
|
+
type: "status",
|
|
519615
|
+
content: `Phase: ${transition.from} → ${transition.to}`,
|
|
519616
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
519617
|
+
});
|
|
519618
|
+
const contracted = this._contextTree.contractInactive(turn);
|
|
519619
|
+
if (contracted.length > 0) {
|
|
519620
|
+
this.emit({
|
|
519621
|
+
type: "status",
|
|
519622
|
+
content: `Phase contraction: ${contracted.join(", ")} → contracted (anchors retained)`,
|
|
519623
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
519624
|
+
});
|
|
519625
|
+
}
|
|
519626
|
+
this._taskState.phase = transition.to;
|
|
519627
|
+
this._taskState.phaseSince = turn;
|
|
519628
|
+
}
|
|
519629
|
+
}
|
|
518882
519630
|
const budgetRemaining = toolCallBudget.get(tc.name);
|
|
518883
519631
|
if (budgetRemaining !== void 0) {
|
|
518884
519632
|
if (budgetRemaining <= 0) {
|
|
@@ -519891,7 +520639,9 @@ Integrate this guidance into your current approach. Continue working on the task
|
|
|
519891
520639
|
} else {
|
|
519892
520640
|
compactedMsgs = await this.compactMessages(messages2, this._skillCompactionStrategy ?? "default");
|
|
519893
520641
|
}
|
|
520642
|
+
this.proactivePrune(compactedMsgs, this._taskState.toolCallCount);
|
|
519894
520643
|
this.microcompact(compactedMsgs);
|
|
520644
|
+
this.surfaceAnchors(compactedMsgs);
|
|
519895
520645
|
const chatRequest = { messages: compactedMsgs, tools: toolDefs, temperature: this.options.temperature, maxTokens: this.options.maxTokens, timeoutMs: this.options.requestTimeoutMs };
|
|
519896
520646
|
let response;
|
|
519897
520647
|
try {
|
|
@@ -520238,6 +520988,8 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
|
|
|
520238
520988
|
}
|
|
520239
520989
|
}
|
|
520240
520990
|
try {
|
|
520991
|
+
if (this.options.subAgent)
|
|
520992
|
+
throw "skip-handoff-write-subagent";
|
|
520241
520993
|
const outcome = consolidation.outcome === "success" ? "success" : consolidation.outcome === "aborted" ? "aborted" : consolidation.outcome === "timeout" ? "timeout" : "failed";
|
|
520242
520994
|
const oaDir = this._workingDirectory ? _pathJoin(this._workingDirectory, ".oa") : _pathJoin(process.cwd(), ".oa");
|
|
520243
520995
|
const transcriptPath = _pathJoin(oaDir, "consolidations", `${this._sessionId}.json`);
|
|
@@ -520678,6 +521430,9 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
|
|
|
520678
521430
|
if (ts.currentStep) {
|
|
520679
521431
|
parts.push(`**Currently doing:** ${ts.currentStep}`);
|
|
520680
521432
|
}
|
|
521433
|
+
if (ts.phase) {
|
|
521434
|
+
parts.push(`**Phase:** ${ts.phase}`);
|
|
521435
|
+
}
|
|
520681
521436
|
if (ts.completedSteps.length > 0) {
|
|
520682
521437
|
parts.push(`**Completed:**`);
|
|
520683
521438
|
for (const s2 of ts.completedSteps.slice(-10))
|
|
@@ -520702,9 +521457,75 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
|
|
|
520702
521457
|
parts.push(`- \`${path8}\` (${action})`);
|
|
520703
521458
|
}
|
|
520704
521459
|
}
|
|
521460
|
+
const todoBlock = this.formatCompletedTodoAnchors();
|
|
521461
|
+
if (todoBlock)
|
|
521462
|
+
parts.push(todoBlock);
|
|
520705
521463
|
parts.push(`**Tool calls:** ${ts.toolCallCount}`);
|
|
520706
521464
|
return parts.join("\n");
|
|
520707
521465
|
}
|
|
521466
|
+
/**
|
|
521467
|
+
* Phase 3 — render completed todos as compact anchor cards. Older
|
|
521468
|
+
* completed todos beyond the top-3 are archived to memex on first sight
|
|
521469
|
+
* so the model can still reach them via memex_retrieve. Returns the empty
|
|
521470
|
+
* string when the session has no todos.
|
|
521471
|
+
*
|
|
521472
|
+
* Anchor card shape: `[done] {content slice} (files: a.ts, b.ts) → outcome`
|
|
521473
|
+
*/
|
|
521474
|
+
formatCompletedTodoAnchors() {
|
|
521475
|
+
try {
|
|
521476
|
+
const sessionId = process.env["OA_SESSION_ID"] || this._sessionId;
|
|
521477
|
+
const todos = readTodos(sessionId);
|
|
521478
|
+
if (todos.length === 0)
|
|
521479
|
+
return "";
|
|
521480
|
+
const completed = todos.filter((t2) => t2.status === "completed");
|
|
521481
|
+
const blocked = todos.filter((t2) => t2.status === "blocked");
|
|
521482
|
+
if (completed.length === 0 && blocked.length === 0)
|
|
521483
|
+
return "";
|
|
521484
|
+
const parts = [];
|
|
521485
|
+
const topRecent = completed.sort((a2, b) => (b.completedAt || b.updatedAt) - (a2.completedAt || a2.updatedAt)).slice(0, 3);
|
|
521486
|
+
if (topRecent.length > 0) {
|
|
521487
|
+
parts.push(`**Recently completed (anchors):**`);
|
|
521488
|
+
for (const t2 of topRecent) {
|
|
521489
|
+
parts.push(`- [done] ${this.todoAnchorLine(t2)}`);
|
|
521490
|
+
}
|
|
521491
|
+
}
|
|
521492
|
+
const olderCompleted = completed.slice(3);
|
|
521493
|
+
let archivedCount = 0;
|
|
521494
|
+
for (const t2 of olderCompleted) {
|
|
521495
|
+
const memexId = `todo-${t2.id.slice(0, 8)}`;
|
|
521496
|
+
if (!this._memexArchive.has(memexId)) {
|
|
521497
|
+
this._memexArchive.set(memexId, {
|
|
521498
|
+
id: memexId,
|
|
521499
|
+
toolName: "todo_complete",
|
|
521500
|
+
summary: this.todoAnchorLine(t2),
|
|
521501
|
+
fullContent: JSON.stringify(t2, null, 2),
|
|
521502
|
+
turn: this._taskState.toolCallCount,
|
|
521503
|
+
timestamp: new Date(t2.completedAt || t2.updatedAt).toISOString()
|
|
521504
|
+
});
|
|
521505
|
+
archivedCount++;
|
|
521506
|
+
}
|
|
521507
|
+
}
|
|
521508
|
+
if (archivedCount > 0) {
|
|
521509
|
+
parts.push(`*(${archivedCount} older completed todo(s) archived → memex_retrieve("todo-{id}") for details, ${olderCompleted.length} total older)*`);
|
|
521510
|
+
} else if (olderCompleted.length > 0) {
|
|
521511
|
+
parts.push(`*(${olderCompleted.length} older completed todo(s) in memex archive)*`);
|
|
521512
|
+
}
|
|
521513
|
+
if (blocked.length > 0) {
|
|
521514
|
+
parts.push(`**Blocked:**`);
|
|
521515
|
+
for (const t2 of blocked.slice(0, 5)) {
|
|
521516
|
+
parts.push(`- [blocked] ${t2.content}${t2.blocker ? ` — ${t2.blocker}` : ""}`);
|
|
521517
|
+
}
|
|
521518
|
+
}
|
|
521519
|
+
return parts.length > 0 ? parts.join("\n") : "";
|
|
521520
|
+
} catch {
|
|
521521
|
+
return "";
|
|
521522
|
+
}
|
|
521523
|
+
}
|
|
521524
|
+
/** Build a single-line anchor for a completed todo (Phase 3 helper). */
|
|
521525
|
+
todoAnchorLine(t2) {
|
|
521526
|
+
const content = t2.content.length > 80 ? t2.content.slice(0, 77) + "..." : t2.content;
|
|
521527
|
+
return content;
|
|
521528
|
+
}
|
|
520708
521529
|
/**
|
|
520709
521530
|
* Format file state registry as compact markdown for domain-aware compaction.
|
|
520710
521531
|
* Only includes files with meaningful state (modified or recently accessed).
|
|
@@ -520954,6 +521775,33 @@ ${taskStateStr}
|
|
|
520954
521775
|
if (memexIndexStr)
|
|
520955
521776
|
enrichments.push(memexIndexStr);
|
|
520956
521777
|
}
|
|
521778
|
+
if (this._contextTree) {
|
|
521779
|
+
const droppedSlice = messages2.slice(headEndIdx, recentStart);
|
|
521780
|
+
const freshAnchors = extractAnchorsFromMessages(droppedSlice, this._taskState.toolCallCount);
|
|
521781
|
+
if (freshAnchors.length > 0) {
|
|
521782
|
+
const tree2 = this._contextTree;
|
|
521783
|
+
const phase = tree2.getCurrentPhase();
|
|
521784
|
+
const snap = tree2.getSnapshot();
|
|
521785
|
+
if (!snap.phases[phase]) {
|
|
521786
|
+
snap.phases[phase] = {
|
|
521787
|
+
status: "active",
|
|
521788
|
+
messages: [],
|
|
521789
|
+
anchors: [],
|
|
521790
|
+
startedAtTurn: this._taskState.toolCallCount
|
|
521791
|
+
};
|
|
521792
|
+
}
|
|
521793
|
+
snap.phases[phase].anchors = [
|
|
521794
|
+
...snap.phases[phase].anchors,
|
|
521795
|
+
...freshAnchors
|
|
521796
|
+
].slice(-12);
|
|
521797
|
+
}
|
|
521798
|
+
const phaseStatus = this._contextTree.renderStatusBlock();
|
|
521799
|
+
if (phaseStatus)
|
|
521800
|
+
enrichments.push(phaseStatus);
|
|
521801
|
+
const anchorBlock = this._contextTree.renderAnchorBlock();
|
|
521802
|
+
if (anchorBlock)
|
|
521803
|
+
enrichments.push(anchorBlock);
|
|
521804
|
+
}
|
|
520957
521805
|
const postCompactRestore = [];
|
|
520958
521806
|
const planSkel = this.buildPlanSkeleton();
|
|
520959
521807
|
if (planSkel)
|
|
@@ -522185,7 +523033,7 @@ ${transcript}`
|
|
|
522185
523033
|
const allTools = Array.from(this.tools.values()).filter((tool) => tool.name !== "tool_search");
|
|
522186
523034
|
const tier = this.options.modelTier ?? "large";
|
|
522187
523035
|
const taskGoal = this._taskState.goal || "";
|
|
522188
|
-
const
|
|
523036
|
+
const STOPWORDS2 = /* @__PURE__ */ new Set([
|
|
522189
523037
|
"with",
|
|
522190
523038
|
"that",
|
|
522191
523039
|
"this",
|
|
@@ -522235,7 +523083,7 @@ ${transcript}`
|
|
|
522235
523083
|
const getDesc = (tool) => dynamicDescs.get(tool.name) ?? tool.description;
|
|
522236
523084
|
const getIndexLabel = (tool) => {
|
|
522237
523085
|
const desc = getDesc(tool).toLowerCase().replace(/[`"'()[\]{}:;,.!?/\\|-]+/g, " ");
|
|
522238
|
-
const keywords = Array.from(new Set(desc.split(/\s+/).filter((word2) => word2.length > 2 && !
|
|
523086
|
+
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
523087
|
return keywords.length > 0 ? `${tool.name}(${keywords.join(",")})` : tool.name;
|
|
522240
523088
|
};
|
|
522241
523089
|
const CORE_TOOLS2 = /* @__PURE__ */ new Set([
|
|
@@ -522283,9 +523131,26 @@ ${transcript}`
|
|
|
522283
523131
|
scored.sort((a2, b) => b.score - a2.score);
|
|
522284
523132
|
const maxInlineExtra = tier === "small" ? 4 : 8;
|
|
522285
523133
|
const inlineExtras = scored.slice(0, maxInlineExtra).filter((s2) => s2.score > 0);
|
|
523134
|
+
const currentTurn = this._taskState.toolCallCount;
|
|
523135
|
+
let demoted = 0;
|
|
523136
|
+
for (const promoted of this._activatedTools) {
|
|
523137
|
+
const lastUsed = this._toolLastUsedTurn.get(promoted) ?? -1;
|
|
523138
|
+
if (lastUsed >= 0 && currentTurn - lastUsed > TOOL_AUTO_DEMOTE_TURNS) {
|
|
523139
|
+
this._activatedTools.delete(promoted);
|
|
523140
|
+
demoted++;
|
|
523141
|
+
}
|
|
523142
|
+
}
|
|
523143
|
+
if (demoted > 0) {
|
|
523144
|
+
this.emit({
|
|
523145
|
+
type: "status",
|
|
523146
|
+
content: `Tool auto-demote: ${demoted} idle promoted tool(s) dropped back to deferred`,
|
|
523147
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
523148
|
+
});
|
|
523149
|
+
}
|
|
522286
523150
|
const inlineNames = /* @__PURE__ */ new Set([
|
|
522287
523151
|
...allTools.filter((t2) => CORE_TOOLS2.has(t2.name)).map((t2) => t2.name),
|
|
522288
|
-
...inlineExtras.map((s2) => s2.tool.name)
|
|
523152
|
+
...inlineExtras.map((s2) => s2.tool.name),
|
|
523153
|
+
...Array.from(this._activatedTools).filter((name10) => allTools.some((t2) => t2.name === name10))
|
|
522289
523154
|
]);
|
|
522290
523155
|
const deferred = allTools.filter((t2) => !inlineNames.has(t2.name));
|
|
522291
523156
|
const inlineTools = allTools.filter((t2) => inlineNames.has(t2.name));
|
|
@@ -522317,11 +523182,12 @@ ${transcript}`
|
|
|
522317
523182
|
lines[lineIdx].push(entry);
|
|
522318
523183
|
return lines;
|
|
522319
523184
|
}, []).map((line) => `- ${line.join(", ")}`).join("\n");
|
|
523185
|
+
const subsetNames = Object.keys(TOOL_SUBSETS).join(", ");
|
|
522320
523186
|
defs.push({
|
|
522321
523187
|
type: "function",
|
|
522322
523188
|
function: {
|
|
522323
523189
|
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.
|
|
523190
|
+
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
523191
|
|
|
522326
523192
|
Available tools (${deferred.length}):
|
|
522327
523193
|
${catalog}`,
|
|
@@ -522334,16 +523200,64 @@ ${catalog}`,
|
|
|
522334
523200
|
}
|
|
522335
523201
|
}
|
|
522336
523202
|
});
|
|
523203
|
+
const activatedToolsRef = this._activatedTools;
|
|
523204
|
+
const subsetCatalog = TOOL_SUBSETS;
|
|
522337
523205
|
this.tools.set("tool_search", {
|
|
522338
523206
|
name: "tool_search",
|
|
522339
523207
|
description: "Search for deferred tools",
|
|
522340
523208
|
parameters: {},
|
|
522341
523209
|
execute: async (args) => {
|
|
522342
|
-
const query = String(args["query"] ?? "").toLowerCase();
|
|
523210
|
+
const query = String(args["query"] ?? "").toLowerCase().trim();
|
|
523211
|
+
const subsetMatch = subsetCatalog[query];
|
|
523212
|
+
if (subsetMatch && subsetMatch.length > 0) {
|
|
523213
|
+
const newlyPromoted = [];
|
|
523214
|
+
const alreadyAvailable = [];
|
|
523215
|
+
const unknown = [];
|
|
523216
|
+
for (const name10 of subsetMatch) {
|
|
523217
|
+
const tool = deferred.find((t2) => t2.name === name10) ?? allTools.find((t2) => t2.name === name10);
|
|
523218
|
+
if (!tool) {
|
|
523219
|
+
unknown.push(name10);
|
|
523220
|
+
continue;
|
|
523221
|
+
}
|
|
523222
|
+
if (inlineNames.has(name10)) {
|
|
523223
|
+
alreadyAvailable.push(name10);
|
|
523224
|
+
continue;
|
|
523225
|
+
}
|
|
523226
|
+
activatedToolsRef.add(name10);
|
|
523227
|
+
newlyPromoted.push(name10);
|
|
523228
|
+
}
|
|
523229
|
+
const lines = [`Subset "${query}" expanded:`];
|
|
523230
|
+
if (newlyPromoted.length > 0) {
|
|
523231
|
+
lines.push(` Promoted to inline (${newlyPromoted.length}): ${newlyPromoted.join(", ")}`);
|
|
523232
|
+
for (const name10 of newlyPromoted) {
|
|
523233
|
+
const tool = allTools.find((t2) => t2.name === name10);
|
|
523234
|
+
if (!tool)
|
|
523235
|
+
continue;
|
|
523236
|
+
lines.push("");
|
|
523237
|
+
lines.push(`## ${tool.name}`);
|
|
523238
|
+
lines.push(getDesc(tool));
|
|
523239
|
+
lines.push(`Parameters: ${JSON.stringify(tool.parameters)}`);
|
|
523240
|
+
}
|
|
523241
|
+
}
|
|
523242
|
+
if (alreadyAvailable.length > 0) {
|
|
523243
|
+
lines.push(` Already inline (${alreadyAvailable.length}): ${alreadyAvailable.join(", ")}`);
|
|
523244
|
+
}
|
|
523245
|
+
if (unknown.length > 0) {
|
|
523246
|
+
lines.push(` Not registered (${unknown.length}): ${unknown.join(", ")}`);
|
|
523247
|
+
}
|
|
523248
|
+
return { success: true, output: lines.join("\n") };
|
|
523249
|
+
}
|
|
522343
523250
|
const matches = deferred.filter((t2) => t2.name.toLowerCase().includes(query) || getDesc(t2).toLowerCase().includes(query)).slice(0, 5);
|
|
522344
523251
|
if (matches.length === 0) {
|
|
522345
|
-
|
|
523252
|
+
const subsetHint = Object.keys(subsetCatalog).join(", ");
|
|
523253
|
+
return {
|
|
523254
|
+
success: false,
|
|
523255
|
+
output: "",
|
|
523256
|
+
error: `No tools matching "${query}". Try a broader search, or call a subset name directly: ${subsetHint}.`
|
|
523257
|
+
};
|
|
522346
523258
|
}
|
|
523259
|
+
for (const t2 of matches)
|
|
523260
|
+
activatedToolsRef.add(t2.name);
|
|
522347
523261
|
const result = matches.map((t2) => {
|
|
522348
523262
|
const paramsStr = JSON.stringify(t2.parameters, null, 2);
|
|
522349
523263
|
return `## ${t2.name}
|
|
@@ -522354,7 +523268,7 @@ ${paramsStr}`;
|
|
|
522354
523268
|
}).join("\n\n---\n\n");
|
|
522355
523269
|
return {
|
|
522356
523270
|
success: true,
|
|
522357
|
-
output: `Found ${matches.length} tool(s)
|
|
523271
|
+
output: `Found ${matches.length} tool(s) — promoted to inline for the rest of this run:
|
|
522358
523272
|
|
|
522359
523273
|
${result}`
|
|
522360
523274
|
};
|
|
@@ -586285,7 +587199,12 @@ Review its full output in the [${id}] tab or via sub_agent(action='output', id='
|
|
|
586285
587199
|
compactionThreshold: subCompaction,
|
|
586286
587200
|
contextWindowSize: 0,
|
|
586287
587201
|
// sub-agents discover their own context window
|
|
586288
|
-
modelTier: subTier
|
|
587202
|
+
modelTier: subTier,
|
|
587203
|
+
// Phase 4 — sub-agent isolation: skip parent's cross-task handoff
|
|
587204
|
+
// read AND write. The sub-agent gets only what AgentTool composed
|
|
587205
|
+
// into the prompt (relevantFiles + constraints + task), nothing
|
|
587206
|
+
// leaked from the parent's session log or .oa/handoffs/latest.json.
|
|
587207
|
+
subAgent: opts.subAgentMode === true
|
|
586289
587208
|
});
|
|
586290
587209
|
const allSafe = buildSubAgentTools(repoRoot, config);
|
|
586291
587210
|
const nameSet = new Set(opts.toolNames || []);
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-agents-ai",
|
|
3
|
-
"version": "0.187.
|
|
3
|
+
"version": "0.187.449",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "open-agents-ai",
|
|
9
|
-
"version": "0.187.
|
|
9
|
+
"version": "0.187.449",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "CC-BY-NC-4.0",
|
|
12
12
|
"dependencies": {
|
package/package.json
CHANGED