open-agents-ai 0.39.1 → 0.41.0
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/README.md +51 -12
- package/dist/index.js +645 -76
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -109,7 +109,7 @@ Long conversations consume context window tokens. Open Agents uses progressive c
|
|
|
109
109
|
|
|
110
110
|
### How It Works
|
|
111
111
|
|
|
112
|
-
Compaction triggers automatically when estimated token usage reaches
|
|
112
|
+
Compaction triggers automatically when estimated token usage reaches a tier-proportional threshold of the model's context window. The system:
|
|
113
113
|
|
|
114
114
|
1. **Preserves** the system prompt and initial user task (head messages)
|
|
115
115
|
2. **Summarizes** middle messages (tool calls, results, exploration) into a structured digest
|
|
@@ -131,24 +131,63 @@ Six strategies are available via `/compact <strategy>`:
|
|
|
131
131
|
|
|
132
132
|
### Automatic Compaction
|
|
133
133
|
|
|
134
|
-
Compaction thresholds scale
|
|
134
|
+
Compaction thresholds scale **proportionally** with the model's actual context window size:
|
|
135
135
|
|
|
136
|
-
| Model Tier |
|
|
137
|
-
|
|
138
|
-
| Large (30B+) |
|
|
139
|
-
| Medium (8-29B) |
|
|
140
|
-
| Small (≤7B) |
|
|
136
|
+
| Model Tier | Normal Mode | Deep Context Mode | Recent Messages Kept |
|
|
137
|
+
|------------|-------------|-------------------|---------------------|
|
|
138
|
+
| Large (30B+) | 75% of context window | 85% of context window | 4-12 (normal) / 4-24 (deep) |
|
|
139
|
+
| Medium (8-29B) | 70% of context window | 85% of context window | 4-12 (normal) / 4-24 (deep) |
|
|
140
|
+
| Small (≤7B) | 65% of context window | 85% of context window | 4-12 (normal) / 4-24 (deep) |
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
For example, a 128K-context large model compacts at ~96K tokens in normal mode (75%) or ~109K tokens in deep mode (85%) — instead of the previous fixed 40K threshold that wasted 69% of available context.
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
### Deep Context Mode (`/deep`)
|
|
145
|
+
|
|
146
|
+
Toggle with `/deep` — relaxes compaction so large models leverage more of their context window for complex multi-step reasoning.
|
|
147
|
+
|
|
148
|
+
When deep context is active:
|
|
149
|
+
- **Compaction fires at 85%** of context instead of 65-75% — the model retains much more working memory
|
|
150
|
+
- **Double the recent messages** (up to 24 instead of 12) preserved after compaction
|
|
151
|
+
- **Richer summaries** — compression budget increased from 20% to 30% of context
|
|
152
|
+
- **Larger tool outputs** — cap raised from 8K to 16K chars per tool result
|
|
153
|
+
- **Relaxed output folding** — more head/tail lines preserved (50/25 instead of 20/10 for large models)
|
|
154
|
+
|
|
155
|
+
This mirrors how human cognition works during deep problem-solving: situationally-relevant memories are transiently activated to occupy a larger portion of working memory, with the most relevant details in high-attention positions while supporting context backs them up. LLM attention mechanisms work similarly — earlier relevant context still influences generation even at lower positional weight.
|
|
156
|
+
|
|
157
|
+
Use deep context for:
|
|
158
|
+
- Complex multi-file refactoring or debugging
|
|
159
|
+
- Architecture analysis across many files
|
|
160
|
+
- Long debugging sessions where error context from earlier is critical
|
|
161
|
+
- Tasks where the agent needs to reason about patterns across many files
|
|
162
|
+
|
|
163
|
+
The setting persists to `.oa/settings.json`. Deep context is particularly valuable for models with 64K+ context windows (Qwen3.5-122B, Llama 3.1 70B, etc.) where the default thresholds were leaving significant capacity unused.
|
|
164
|
+
|
|
165
|
+
### Status Bar Context Tracking (`Ctx:` + `SNR:`)
|
|
166
|
+
|
|
167
|
+
The status bar displays a live `Ctx:` gauge showing estimated context window usage, plus an `SNR:` gauge showing context quality:
|
|
145
168
|
|
|
146
169
|
```
|
|
147
|
-
In: 12,345 | Out: 4,567 | Ctx: 18,000/131,072 86% | Exp: 4.2x
|
|
148
|
-
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
149
|
-
|
|
170
|
+
In: 12,345 | Out: 4,567 | Ctx: 18,000/131,072 86% | SNR: 72% d'2.1 | Exp: 4.2x
|
|
171
|
+
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
|
|
172
|
+
Context window usage Signal-to-Noise Ratio
|
|
150
173
|
```
|
|
151
174
|
|
|
175
|
+
**SNR (Signal-to-Noise Ratio)** — measures how much of the agent's memory context is relevant to the current task vs noise. Inspired by neuroscience signal detection theory:
|
|
176
|
+
|
|
177
|
+
- **d-prime (d')**: psychophysics metric measuring separation between signal and noise distributions. d' >= 2.0 = excellent discrimination, d' ≈ 1.0 = moderate, d' <= 0.5 = noisy
|
|
178
|
+
- **Signal**: memory entries with high keyword overlap to the current task (PFC gating analogy)
|
|
179
|
+
- **Noise**: entries with low relevance or high redundancy (dentate gyrus pattern separation)
|
|
180
|
+
- **Sparsity**: how much of the context is unique vs redundant (sparse distributed memory)
|
|
181
|
+
|
|
182
|
+
The SNR formula combines three components:
|
|
183
|
+
- 50% **signal proportion** (relevant entries / total entries)
|
|
184
|
+
- 30% **d-prime quality** (normalized to 0-1 from the 0-3 d' range)
|
|
185
|
+
- 20% **sparsity** (1 - average pairwise n-gram overlap)
|
|
186
|
+
|
|
187
|
+
Color coding: green (>=70%), yellow (40-70%), red (<40%). SNR is evaluated at task start and task completion. In deep context mode with `/deep`, parallel evaluator agents (PFC Relevance Evaluator + Dentate Gyrus Noise Detector) can run a full consensus-based evaluation.
|
|
188
|
+
|
|
189
|
+
Research basis: d-prime from signal detection theory (Green & Swets 1966), hippocampal pattern separation (Yassa & Stark 2011), PFC gating (Miller & Cohen 2001), biased competition (Desimone & Duncan 1995), multi-agent debate (Du et al., arXiv:2305.14325).
|
|
190
|
+
|
|
152
191
|
This gauge reflects the **post-compaction** token count — when compaction fires, the `Ctx:` value drops to match the actual compressed message history. The compaction warning message shows the before/after:
|
|
153
192
|
|
|
154
193
|
```
|
package/dist/index.js
CHANGED
|
@@ -6133,8 +6133,8 @@ async function loadTranscribeCli() {
|
|
|
6133
6133
|
const nvmBase = join15(homedir6(), ".nvm", "versions", "node");
|
|
6134
6134
|
if (existsSync12(nvmBase)) {
|
|
6135
6135
|
try {
|
|
6136
|
-
const { readdirSync:
|
|
6137
|
-
for (const ver of
|
|
6136
|
+
const { readdirSync: readdirSync14 } = await import("node:fs");
|
|
6137
|
+
for (const ver of readdirSync14(nvmBase)) {
|
|
6138
6138
|
const tcPath = join15(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
|
|
6139
6139
|
if (existsSync12(join15(tcPath, "dist", "index.js"))) {
|
|
6140
6140
|
const { createRequire: createRequire4 } = await import("node:module");
|
|
@@ -13180,6 +13180,7 @@ Rules:
|
|
|
13180
13180
|
taskTimeoutMs: options?.taskTimeoutMs ?? 36e5,
|
|
13181
13181
|
// 60 min
|
|
13182
13182
|
compactionThreshold: options?.compactionThreshold ?? 4e4,
|
|
13183
|
+
deepContext: options?.deepContext ?? false,
|
|
13183
13184
|
dynamicContext: options?.dynamicContext ?? "",
|
|
13184
13185
|
streamEnabled: options?.streamEnabled ?? false,
|
|
13185
13186
|
bruteForce: options?.bruteForce ?? true,
|
|
@@ -13225,19 +13226,47 @@ Rules:
|
|
|
13225
13226
|
/**
|
|
13226
13227
|
* Compute all context-dependent limits from the current contextWindowSize
|
|
13227
13228
|
* and modelTier. Returns sensible defaults when contextWindowSize is 0.
|
|
13229
|
+
*
|
|
13230
|
+
* Key insight: LLM attention is most effective when the most relevant content
|
|
13231
|
+
* sits in high-attention positions (start and end of context). Rather than
|
|
13232
|
+
* compacting aggressively and losing detail, we scale thresholds proportionally
|
|
13233
|
+
* to the model's actual context window so larger models can leverage more of
|
|
13234
|
+
* their capacity. The deepContext flag further relaxes limits for complex
|
|
13235
|
+
* multi-step reasoning where earlier context carries critical signal.
|
|
13236
|
+
*
|
|
13237
|
+
* Context utilization targets:
|
|
13238
|
+
* - Small models: compact at ~65% (limited attention span, aggressive helps)
|
|
13239
|
+
* - Medium models: compact at ~70% (balanced)
|
|
13240
|
+
* - Large models: compact at ~75% (plenty of room, let attention work)
|
|
13241
|
+
* - Deep context: compact at ~85% (trust the model's full attention window)
|
|
13228
13242
|
*/
|
|
13229
13243
|
contextLimits() {
|
|
13230
13244
|
const ctx = this.options.contextWindowSize;
|
|
13231
13245
|
const tier = this.options.modelTier ?? "large";
|
|
13232
|
-
const
|
|
13233
|
-
|
|
13234
|
-
|
|
13246
|
+
const deep = this.options.deepContext ?? false;
|
|
13247
|
+
let compactionThreshold;
|
|
13248
|
+
if (ctx > 0) {
|
|
13249
|
+
const factor = deep ? 0.85 : tier === "small" ? 0.65 : tier === "medium" ? 0.7 : 0.75;
|
|
13250
|
+
compactionThreshold = Math.floor(ctx * factor);
|
|
13251
|
+
} else {
|
|
13252
|
+
compactionThreshold = deep ? 8e4 : this.options.compactionThreshold;
|
|
13253
|
+
}
|
|
13254
|
+
const keepRecentMax = deep ? 24 : 12;
|
|
13255
|
+
let keepRecent;
|
|
13256
|
+
if (ctx > 0) {
|
|
13257
|
+
const keepRecentDivisor = tier === "small" ? 2e3 : tier === "medium" ? 3e3 : 4e3;
|
|
13258
|
+
keepRecent = Math.max(4, Math.min(keepRecentMax, Math.floor(ctx / keepRecentDivisor)));
|
|
13259
|
+
} else {
|
|
13260
|
+
keepRecent = deep ? 20 : 12;
|
|
13261
|
+
}
|
|
13235
13262
|
const maxOutputTokens = ctx > 0 ? Math.min(this.options.maxTokens, Math.max(2048, Math.floor(ctx * 0.25))) : this.options.maxTokens;
|
|
13236
|
-
const
|
|
13237
|
-
const
|
|
13238
|
-
const
|
|
13239
|
-
const
|
|
13240
|
-
const
|
|
13263
|
+
const toolOutputCeiling = deep ? 16e3 : 8e3;
|
|
13264
|
+
const toolOutputMaxChars = ctx > 0 ? Math.max(2e3, Math.min(toolOutputCeiling, Math.floor(ctx * (deep ? 0.8 : 0.5)))) : toolOutputCeiling;
|
|
13265
|
+
const foldLineThreshold = deep ? tier === "small" ? 50 : tier === "medium" ? 80 : 120 : tier === "small" ? 30 : tier === "medium" ? 35 : 40;
|
|
13266
|
+
const foldHeadLines = deep ? tier === "small" ? 20 : tier === "medium" ? 30 : 50 : tier === "small" ? 12 : tier === "medium" ? 16 : 20;
|
|
13267
|
+
const foldTailLines = deep ? tier === "small" ? 10 : tier === "medium" ? 15 : 25 : tier === "small" ? 5 : tier === "medium" ? 8 : 10;
|
|
13268
|
+
const maxSummaryCeiling = deep ? 16e3 : 8e3;
|
|
13269
|
+
const maxSummaryChars = ctx > 0 ? Math.max(2e3, Math.min(maxSummaryCeiling, Math.floor(ctx * (deep ? 0.3 : 0.2)))) : deep ? 8e3 : 4e3;
|
|
13241
13270
|
const repetitionWindow = tier === "small" ? 6 : tier === "medium" ? 8 : 10;
|
|
13242
13271
|
return {
|
|
13243
13272
|
compactionThreshold,
|
|
@@ -14040,8 +14069,8 @@ ${marker}` : marker);
|
|
|
14040
14069
|
return;
|
|
14041
14070
|
try {
|
|
14042
14071
|
const { mkdirSync: mkdirSync14, writeFileSync: writeFileSync13 } = __require("node:fs");
|
|
14043
|
-
const { join:
|
|
14044
|
-
const sessionDir =
|
|
14072
|
+
const { join: join43 } = __require("node:path");
|
|
14073
|
+
const sessionDir = join43(this._workingDirectory, ".oa", "session", this._sessionId);
|
|
14045
14074
|
mkdirSync14(sessionDir, { recursive: true });
|
|
14046
14075
|
const checkpoint = {
|
|
14047
14076
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -14054,7 +14083,7 @@ ${marker}` : marker);
|
|
|
14054
14083
|
memexEntryCount: this._memexArchive.size,
|
|
14055
14084
|
fileRegistrySize: this._fileRegistry.size
|
|
14056
14085
|
};
|
|
14057
|
-
writeFileSync13(
|
|
14086
|
+
writeFileSync13(join43(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
|
|
14058
14087
|
} catch {
|
|
14059
14088
|
}
|
|
14060
14089
|
}
|
|
@@ -15958,8 +15987,8 @@ var init_listen = __esm({
|
|
|
15958
15987
|
const nvmBase = join27(homedir8(), ".nvm", "versions", "node");
|
|
15959
15988
|
if (existsSync19(nvmBase)) {
|
|
15960
15989
|
try {
|
|
15961
|
-
const { readdirSync:
|
|
15962
|
-
for (const ver of
|
|
15990
|
+
const { readdirSync: readdirSync14 } = await import("node:fs");
|
|
15991
|
+
for (const ver of readdirSync14(nvmBase)) {
|
|
15963
15992
|
const tcPath = join27(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
|
|
15964
15993
|
if (existsSync19(join27(tcPath, "dist", "index.js"))) {
|
|
15965
15994
|
const { createRequire: createRequire4 } = await import("node:module");
|
|
@@ -16189,10 +16218,10 @@ transcribe-cli error: ${transcribeCliError}` : "";
|
|
|
16189
16218
|
wordTimestamps: false
|
|
16190
16219
|
});
|
|
16191
16220
|
if (outputDir) {
|
|
16192
|
-
const { basename:
|
|
16221
|
+
const { basename: basename16 } = await import("node:path");
|
|
16193
16222
|
const transcriptDir = join27(outputDir, ".oa", "transcripts");
|
|
16194
16223
|
mkdirSync5(transcriptDir, { recursive: true });
|
|
16195
|
-
const outFile = join27(transcriptDir, `${
|
|
16224
|
+
const outFile = join27(transcriptDir, `${basename16(filePath)}.txt`);
|
|
16196
16225
|
writeFileSync5(outFile, result.text, "utf-8");
|
|
16197
16226
|
}
|
|
16198
16227
|
return {
|
|
@@ -16578,18 +16607,18 @@ function renderTaskAborted() {
|
|
|
16578
16607
|
${c2.yellow("\u26A0")} ${c2.bold("Task aborted by user")}
|
|
16579
16608
|
`);
|
|
16580
16609
|
}
|
|
16581
|
-
function renderToolCallStart(toolName, args) {
|
|
16610
|
+
function renderToolCallStart(toolName, args, verbose) {
|
|
16582
16611
|
const icon = TOOL_ICONS[toolName] ?? "\u{1F527}";
|
|
16583
16612
|
const label = TOOL_LABELS[toolName] ?? toolName;
|
|
16584
|
-
const argsSummary = formatToolArgs(toolName, args);
|
|
16613
|
+
const argsSummary = formatToolArgs(toolName, args, verbose);
|
|
16585
16614
|
const colorFn = _colorsEnabled ? TOOL_COLORS[toolName] ?? c2.dim : (t) => t;
|
|
16586
16615
|
const emojiPrefix = _emojisEnabled ? `${icon} ` : "";
|
|
16587
16616
|
process.stdout.write(`
|
|
16588
16617
|
${c2.dim("\u23BF")} ${emojiPrefix}${colorFn(c2.bold(label))}${argsSummary ? c2.dim(": ") + argsSummary : ""}
|
|
16589
16618
|
`);
|
|
16590
16619
|
}
|
|
16591
|
-
function renderToolResult(toolName, success, output) {
|
|
16592
|
-
const maxW = getTermWidth() - 10;
|
|
16620
|
+
function renderToolResult(toolName, success, output, verbose) {
|
|
16621
|
+
const maxW = verbose ? Math.max(getTermWidth() - 10, 200) : getTermWidth() - 10;
|
|
16593
16622
|
const prefix = ` ${c2.dim("\u23BF")} `;
|
|
16594
16623
|
switch (toolName) {
|
|
16595
16624
|
case "file_write": {
|
|
@@ -16647,18 +16676,47 @@ function renderToolResult(toolName, success, output) {
|
|
|
16647
16676
|
`);
|
|
16648
16677
|
return;
|
|
16649
16678
|
}
|
|
16650
|
-
const maxLines = 6;
|
|
16679
|
+
const maxLines = verbose ? 200 : 6;
|
|
16651
16680
|
const shown = lines.slice(0, maxLines);
|
|
16652
16681
|
for (const line of shown) {
|
|
16653
|
-
if (isRawJsonDump(line)) {
|
|
16682
|
+
if (isRawJsonDump(line) && !verbose) {
|
|
16654
16683
|
process.stdout.write(`${prefix}${c2.dim("(content omitted)")}
|
|
16655
16684
|
`);
|
|
16656
16685
|
return;
|
|
16657
16686
|
}
|
|
16658
|
-
|
|
16659
|
-
|
|
16660
|
-
|
|
16687
|
+
if (verbose) {
|
|
16688
|
+
const termW = getTermWidth() - 10;
|
|
16689
|
+
if (line.length > termW) {
|
|
16690
|
+
let remaining = line;
|
|
16691
|
+
let first = true;
|
|
16692
|
+
while (remaining.length > 0) {
|
|
16693
|
+
if (remaining.length <= termW) {
|
|
16694
|
+
const formatted2 = formatMarkdownLine(remaining);
|
|
16695
|
+
process.stdout.write(`${first ? prefix : prefix + " "}${formatted2 === remaining ? highlightToolOutput(remaining) : formatted2}
|
|
16696
|
+
`);
|
|
16697
|
+
break;
|
|
16698
|
+
}
|
|
16699
|
+
let breakAt = remaining.lastIndexOf(" ", termW);
|
|
16700
|
+
if (breakAt < termW * 0.3)
|
|
16701
|
+
breakAt = termW;
|
|
16702
|
+
const chunk = remaining.slice(0, breakAt);
|
|
16703
|
+
remaining = remaining.slice(breakAt).trimStart();
|
|
16704
|
+
const formatted = formatMarkdownLine(chunk);
|
|
16705
|
+
process.stdout.write(`${first ? prefix : prefix + " "}${formatted === chunk ? highlightToolOutput(chunk) : formatted}
|
|
16706
|
+
`);
|
|
16707
|
+
first = false;
|
|
16708
|
+
}
|
|
16709
|
+
} else {
|
|
16710
|
+
const formatted = formatMarkdownLine(line);
|
|
16711
|
+
process.stdout.write(`${prefix}${formatted === line ? highlightToolOutput(line) : formatted}
|
|
16712
|
+
`);
|
|
16713
|
+
}
|
|
16714
|
+
} else {
|
|
16715
|
+
const cropped = line.length > maxW ? line.slice(0, maxW - 3) + "..." : line;
|
|
16716
|
+
const formatted = formatMarkdownLine(cropped);
|
|
16717
|
+
process.stdout.write(`${prefix}${formatted === cropped ? highlightToolOutput(cropped) : formatted}
|
|
16661
16718
|
`);
|
|
16719
|
+
}
|
|
16662
16720
|
}
|
|
16663
16721
|
if (lines.length > maxLines) {
|
|
16664
16722
|
process.stdout.write(`${prefix}${c2.dim(`... ${lines.length - maxLines} more lines`)}
|
|
@@ -16914,6 +16972,7 @@ function renderSlashHelp() {
|
|
|
16914
16972
|
["/compact", "Force context compaction now (default strategy)"],
|
|
16915
16973
|
["/compact <strategy>", "Compact with strategy: aggressive, decisions, errors, summary, structured"],
|
|
16916
16974
|
["/bruteforce", "Toggle brute-force mode (auto re-engage on turn limit)"],
|
|
16975
|
+
["/deep", "Toggle deep context \u2014 relaxes compaction so large models use 85% of context"],
|
|
16917
16976
|
["/tools", "List agent-created custom tools"],
|
|
16918
16977
|
["/skills", "List available AIWG skills"],
|
|
16919
16978
|
["/skills <keyword>", "Filter skills by name or trigger"],
|
|
@@ -16996,8 +17055,8 @@ function renderConfig(config) {
|
|
|
16996
17055
|
}
|
|
16997
17056
|
process.stdout.write("\n");
|
|
16998
17057
|
}
|
|
16999
|
-
function formatToolArgs(toolName, args) {
|
|
17000
|
-
const maxArg = Math.max(40, getTermWidth() - 20);
|
|
17058
|
+
function formatToolArgs(toolName, args, verbose) {
|
|
17059
|
+
const maxArg = verbose ? 1e4 : Math.max(40, getTermWidth() - 20);
|
|
17001
17060
|
switch (toolName) {
|
|
17002
17061
|
case "file_read":
|
|
17003
17062
|
case "file_write":
|
|
@@ -19451,6 +19510,18 @@ async function handleSlashCommand(input, ctx) {
|
|
|
19451
19510
|
renderInfo(`Brute-force mode: ${isOn ? "on" : "off"}${hasLocal ? " (project-local)" : ""}` + (isOn ? " \u2014 agent will auto re-engage when turn limit is hit, reassess and try creative strategies" : ""));
|
|
19452
19511
|
return "handled";
|
|
19453
19512
|
}
|
|
19513
|
+
case "deep":
|
|
19514
|
+
case "deep-context": {
|
|
19515
|
+
if (!ctx.deepContextToggle) {
|
|
19516
|
+
renderWarning("Deep context not available");
|
|
19517
|
+
return "handled";
|
|
19518
|
+
}
|
|
19519
|
+
const isOn = ctx.deepContextToggle();
|
|
19520
|
+
const save = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
|
|
19521
|
+
save({ deepContext: isOn });
|
|
19522
|
+
renderInfo(`Deep context mode: ${isOn ? "ON" : "OFF"}${hasLocal ? " (project-local)" : ""}` + (isOn ? " \u2014 compaction relaxed to 85% of context window. Large models will retain more working memory for complex reasoning." : " \u2014 compaction restored to default thresholds."));
|
|
19523
|
+
return "handled";
|
|
19524
|
+
}
|
|
19454
19525
|
case "emojis":
|
|
19455
19526
|
case "emoji": {
|
|
19456
19527
|
const current = ctx.getEmojis?.() ?? true;
|
|
@@ -19871,17 +19942,17 @@ async function handleUpdate(subcommand, ctx) {
|
|
|
19871
19942
|
try {
|
|
19872
19943
|
const { createRequire: createRequire4 } = await import("node:module");
|
|
19873
19944
|
const { fileURLToPath: fileURLToPath10 } = await import("node:url");
|
|
19874
|
-
const { dirname: dirname13, join:
|
|
19875
|
-
const { existsSync:
|
|
19945
|
+
const { dirname: dirname13, join: join43 } = await import("node:path");
|
|
19946
|
+
const { existsSync: existsSync30 } = await import("node:fs");
|
|
19876
19947
|
const req = createRequire4(import.meta.url);
|
|
19877
19948
|
const thisDir = dirname13(fileURLToPath10(import.meta.url));
|
|
19878
19949
|
const candidates = [
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19950
|
+
join43(thisDir, "..", "package.json"),
|
|
19951
|
+
join43(thisDir, "..", "..", "package.json"),
|
|
19952
|
+
join43(thisDir, "..", "..", "..", "package.json")
|
|
19882
19953
|
];
|
|
19883
19954
|
for (const pkgPath of candidates) {
|
|
19884
|
-
if (
|
|
19955
|
+
if (existsSync30(pkgPath)) {
|
|
19885
19956
|
const pkg = req(pkgPath);
|
|
19886
19957
|
if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
|
|
19887
19958
|
currentVersion = pkg.version ?? "0.0.0";
|
|
@@ -24071,6 +24142,348 @@ DMN state directory: ${this.stateDir}`);
|
|
|
24071
24142
|
}
|
|
24072
24143
|
});
|
|
24073
24144
|
|
|
24145
|
+
// packages/cli/dist/tui/snr-engine.js
|
|
24146
|
+
import { existsSync as existsSync27, readdirSync as readdirSync12, readFileSync as readFileSync20 } from "node:fs";
|
|
24147
|
+
import { join as join37, basename as basename14 } from "node:path";
|
|
24148
|
+
function computeDPrime(signalScores, noiseScores) {
|
|
24149
|
+
if (signalScores.length === 0 || noiseScores.length === 0)
|
|
24150
|
+
return 0;
|
|
24151
|
+
const mean = (arr) => arr.reduce((s, v) => s + v, 0) / arr.length;
|
|
24152
|
+
const variance = (arr, mu) => arr.reduce((s, v) => s + (v - mu) ** 2, 0) / Math.max(1, arr.length - 1);
|
|
24153
|
+
const muSignal = mean(signalScores);
|
|
24154
|
+
const muNoise = mean(noiseScores);
|
|
24155
|
+
const varSignal = variance(signalScores, muSignal);
|
|
24156
|
+
const varNoise = variance(noiseScores, muNoise);
|
|
24157
|
+
const pooledStd = Math.sqrt((varSignal + varNoise) / 2);
|
|
24158
|
+
if (pooledStd === 0)
|
|
24159
|
+
return muSignal > muNoise ? 3 : 0;
|
|
24160
|
+
return (muSignal - muNoise) / pooledStd;
|
|
24161
|
+
}
|
|
24162
|
+
function computeSparsity(entries) {
|
|
24163
|
+
if (entries.length <= 1)
|
|
24164
|
+
return 1;
|
|
24165
|
+
const ngramSets = entries.map((e) => {
|
|
24166
|
+
const words = e.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
24167
|
+
const ngrams = /* @__PURE__ */ new Set();
|
|
24168
|
+
for (let i = 0; i < words.length - 1; i++) {
|
|
24169
|
+
ngrams.add(`${words[i]} ${words[i + 1]}`);
|
|
24170
|
+
}
|
|
24171
|
+
return ngrams;
|
|
24172
|
+
});
|
|
24173
|
+
let totalPairs = 0;
|
|
24174
|
+
let totalJaccard = 0;
|
|
24175
|
+
for (let i = 0; i < ngramSets.length; i++) {
|
|
24176
|
+
for (let j = i + 1; j < ngramSets.length; j++) {
|
|
24177
|
+
const a = ngramSets[i];
|
|
24178
|
+
const b = ngramSets[j];
|
|
24179
|
+
if (a.size === 0 && b.size === 0)
|
|
24180
|
+
continue;
|
|
24181
|
+
const intersection = new Set([...a].filter((x) => b.has(x)));
|
|
24182
|
+
const union = /* @__PURE__ */ new Set([...a, ...b]);
|
|
24183
|
+
totalJaccard += union.size > 0 ? intersection.size / union.size : 0;
|
|
24184
|
+
totalPairs++;
|
|
24185
|
+
}
|
|
24186
|
+
}
|
|
24187
|
+
if (totalPairs === 0)
|
|
24188
|
+
return 1;
|
|
24189
|
+
const avgOverlap = totalJaccard / totalPairs;
|
|
24190
|
+
return Math.max(0, Math.min(1, 1 - avgOverlap));
|
|
24191
|
+
}
|
|
24192
|
+
function adaptTool3(tool) {
|
|
24193
|
+
return {
|
|
24194
|
+
name: tool.name,
|
|
24195
|
+
description: tool.description,
|
|
24196
|
+
parameters: tool.parameters,
|
|
24197
|
+
async execute(args) {
|
|
24198
|
+
const result = await tool.execute(args);
|
|
24199
|
+
return { success: result.success, output: result.output, error: result.error };
|
|
24200
|
+
}
|
|
24201
|
+
};
|
|
24202
|
+
}
|
|
24203
|
+
function renderSNRUpdate(score) {
|
|
24204
|
+
const pct = Math.round(score.ratio * 100);
|
|
24205
|
+
const color = pct >= 70 ? c2.green : pct >= 40 ? c2.yellow : c2.red;
|
|
24206
|
+
const capWarn = score.capacityWarning ? c2.red(" [CAPACITY]") : "";
|
|
24207
|
+
process.stdout.write(` ${color("\u25C8")} SNR: ${color(`${pct}%`)} (d'=${score.dPrime.toFixed(1)}, signal=${score.signalCount}/${score.entriesEvaluated}, sparsity=${Math.round(score.sparsity * 100)}%) [${score.evaluators.join("+")}]${capWarn}
|
|
24208
|
+
`);
|
|
24209
|
+
}
|
|
24210
|
+
var SNREngine;
|
|
24211
|
+
var init_snr_engine = __esm({
|
|
24212
|
+
"packages/cli/dist/tui/snr-engine.js"() {
|
|
24213
|
+
"use strict";
|
|
24214
|
+
init_dist5();
|
|
24215
|
+
init_dist2();
|
|
24216
|
+
init_project_context();
|
|
24217
|
+
init_render();
|
|
24218
|
+
SNREngine = class {
|
|
24219
|
+
config;
|
|
24220
|
+
repoRoot;
|
|
24221
|
+
lastScore = null;
|
|
24222
|
+
evaluationCount = 0;
|
|
24223
|
+
constructor(config, repoRoot) {
|
|
24224
|
+
this.config = config;
|
|
24225
|
+
this.repoRoot = repoRoot;
|
|
24226
|
+
}
|
|
24227
|
+
get score() {
|
|
24228
|
+
return this.lastScore;
|
|
24229
|
+
}
|
|
24230
|
+
/**
|
|
24231
|
+
* Quick local-only SNR estimation (no LLM calls, fast).
|
|
24232
|
+
* @param contextSlots — total context window tokens (for Hopfield capacity warning)
|
|
24233
|
+
*/
|
|
24234
|
+
computeLocalSNR(currentTask, contextEntries, contextSlots) {
|
|
24235
|
+
if (contextEntries.length === 0) {
|
|
24236
|
+
return this.makeScore(1, 3, 0, 0, 0, 1, ["local-empty"]);
|
|
24237
|
+
}
|
|
24238
|
+
const taskTerms = new Set(currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 3).map((w) => w.replace(/[^a-z0-9]/g, "")));
|
|
24239
|
+
const scores = contextEntries.map((entry) => {
|
|
24240
|
+
const entryTerms = entry.toLowerCase().split(/\s+/).filter((w) => w.length > 3).map((w) => w.replace(/[^a-z0-9]/g, ""));
|
|
24241
|
+
const matchCount = entryTerms.filter((t) => taskTerms.has(t)).length;
|
|
24242
|
+
const score2 = entryTerms.length > 0 ? matchCount / Math.sqrt(entryTerms.length) : 0;
|
|
24243
|
+
return { entry, score: score2 };
|
|
24244
|
+
});
|
|
24245
|
+
const allScores = scores.map((s) => s.score);
|
|
24246
|
+
const mean = allScores.reduce((a, b) => a + b, 0) / allScores.length;
|
|
24247
|
+
const std = Math.sqrt(allScores.reduce((a, v) => a + (v - mean) ** 2, 0) / allScores.length);
|
|
24248
|
+
const threshold = mean + 0.5 * std;
|
|
24249
|
+
const signalScores = allScores.filter((s) => s >= threshold);
|
|
24250
|
+
const noiseScores = allScores.filter((s) => s < threshold);
|
|
24251
|
+
const dPrime = computeDPrime(signalScores.length > 0 ? signalScores : [mean], noiseScores.length > 0 ? noiseScores : [0]);
|
|
24252
|
+
const sparsity = computeSparsity(contextEntries);
|
|
24253
|
+
const signalProportion = signalScores.length / allScores.length;
|
|
24254
|
+
const dPrimeNorm = Math.min(1, Math.max(0, dPrime / 3));
|
|
24255
|
+
const ratio = 0.5 * signalProportion + 0.3 * dPrimeNorm + 0.2 * sparsity;
|
|
24256
|
+
const capacityWarning = contextSlots ? contextEntries.length > Math.floor(contextSlots * 0.138) : false;
|
|
24257
|
+
const score = this.makeScore(ratio, dPrime, contextEntries.length, signalScores.length, noiseScores.length, sparsity, ["local-keyword"], capacityWarning);
|
|
24258
|
+
this.lastScore = score;
|
|
24259
|
+
this.evaluationCount++;
|
|
24260
|
+
return score;
|
|
24261
|
+
}
|
|
24262
|
+
/**
|
|
24263
|
+
* Full SNR evaluation using parallel LLM evaluator agents.
|
|
24264
|
+
* Mirrors PFC gating + multi-agent debate (Du et al. 2023).
|
|
24265
|
+
*
|
|
24266
|
+
* Spawns 2 lightweight agents with different evaluation perspectives:
|
|
24267
|
+
* - "Relevance Evaluator" — scores entries by task relevance (PFC role)
|
|
24268
|
+
* - "Noise Detector" — identifies redundant/stale/irrelevant entries (DG role)
|
|
24269
|
+
*
|
|
24270
|
+
* Their consensus determines the final SNR.
|
|
24271
|
+
*/
|
|
24272
|
+
async evaluateWithAgents(currentTask, memoryTopics, onEvent) {
|
|
24273
|
+
const entries = this.loadMemoryEntries(memoryTopics);
|
|
24274
|
+
if (entries.length === 0) {
|
|
24275
|
+
const score2 = this.makeScore(1, 3, 0, 0, 0, 1, ["agents-empty"]);
|
|
24276
|
+
this.lastScore = score2;
|
|
24277
|
+
return score2;
|
|
24278
|
+
}
|
|
24279
|
+
const entrySummaries = entries.map((e, i) => `[${i}] topic=${e.topic} key=${e.key}: ${e.value.slice(0, 200)}${e.value.length > 200 ? "..." : ""}`);
|
|
24280
|
+
const entriesBlock = entrySummaries.join("\n");
|
|
24281
|
+
const [relevanceResult, noiseResult] = await Promise.allSettled([
|
|
24282
|
+
this.runEvaluatorAgent("relevance", `You are a Prefrontal Cortex Gating Evaluator. Your role is to assess which
|
|
24283
|
+
memory entries are RELEVANT to the current task.
|
|
24284
|
+
|
|
24285
|
+
CURRENT TASK: ${currentTask}
|
|
24286
|
+
|
|
24287
|
+
MEMORY ENTRIES TO EVALUATE:
|
|
24288
|
+
${entriesBlock}
|
|
24289
|
+
|
|
24290
|
+
For each entry, output a JSON array of objects:
|
|
24291
|
+
[{"index": 0, "score": 0.8, "reason": "directly related to task"}, ...]
|
|
24292
|
+
|
|
24293
|
+
Score 0-1 where:
|
|
24294
|
+
- 1.0 = directly relevant to the current task
|
|
24295
|
+
- 0.7 = supporting context (useful background)
|
|
24296
|
+
- 0.4 = marginally relevant
|
|
24297
|
+
- 0.1 = not relevant to current task
|
|
24298
|
+
|
|
24299
|
+
Call task_complete with the JSON array when done.`, onEvent),
|
|
24300
|
+
this.runEvaluatorAgent("noise", `You are a Dentate Gyrus Pattern Separation Evaluator. Your role is to identify
|
|
24301
|
+
NOISE in the memory context \u2014 entries that are redundant, stale, or interfering.
|
|
24302
|
+
|
|
24303
|
+
CURRENT TASK: ${currentTask}
|
|
24304
|
+
|
|
24305
|
+
MEMORY ENTRIES TO EVALUATE:
|
|
24306
|
+
${entriesBlock}
|
|
24307
|
+
|
|
24308
|
+
For each entry, output a JSON array of objects:
|
|
24309
|
+
[{"index": 0, "noise_score": 0.2, "reason": "unique, not redundant"}, ...]
|
|
24310
|
+
|
|
24311
|
+
Noise score 0-1 where:
|
|
24312
|
+
- 1.0 = pure noise (completely redundant, stale, or interfering with task)
|
|
24313
|
+
- 0.7 = mostly noise (outdated or largely redundant with another entry)
|
|
24314
|
+
- 0.4 = moderate noise (some overlap with other entries)
|
|
24315
|
+
- 0.1 = clean signal (unique, fresh, non-interfering)
|
|
24316
|
+
|
|
24317
|
+
Look for: duplicate information, outdated facts, entries that contradict
|
|
24318
|
+
newer entries, entries about completely unrelated topics.
|
|
24319
|
+
|
|
24320
|
+
Call task_complete with the JSON array when done.`, onEvent)
|
|
24321
|
+
]);
|
|
24322
|
+
const relevanceScores = this.parseEvaluatorResult(relevanceResult.status === "fulfilled" ? relevanceResult.value : "", entries.length, 0.5);
|
|
24323
|
+
const noiseScores = this.parseEvaluatorResult(noiseResult.status === "fulfilled" ? noiseResult.value : "", entries.length, 0.5);
|
|
24324
|
+
const combinedScores = entries.map((_, i) => {
|
|
24325
|
+
const rel = relevanceScores[i] ?? 0.5;
|
|
24326
|
+
const noise = noiseScores[i] ?? 0.5;
|
|
24327
|
+
const votes = [rel, 1 - noise].sort((a, b) => a - b);
|
|
24328
|
+
return (votes[0] + votes[1]) / 2;
|
|
24329
|
+
});
|
|
24330
|
+
const mean = combinedScores.reduce((a, b) => a + b, 0) / combinedScores.length;
|
|
24331
|
+
const std = Math.sqrt(combinedScores.reduce((a, v) => a + (v - mean) ** 2, 0) / combinedScores.length);
|
|
24332
|
+
const threshold = Math.max(0.3, mean);
|
|
24333
|
+
const signalEntries = combinedScores.filter((s) => s >= threshold);
|
|
24334
|
+
const noiseEntries = combinedScores.filter((s) => s < threshold);
|
|
24335
|
+
const dPrime = computeDPrime(signalEntries.length > 0 ? signalEntries : [mean], noiseEntries.length > 0 ? noiseEntries : [0.1]);
|
|
24336
|
+
const sparsity = computeSparsity(entries.map((e) => e.value));
|
|
24337
|
+
const signalProportion = signalEntries.length / combinedScores.length;
|
|
24338
|
+
const dPrimeNorm = Math.min(1, Math.max(0, dPrime / 3));
|
|
24339
|
+
const ratio = 0.5 * signalProportion + 0.3 * dPrimeNorm + 0.2 * sparsity;
|
|
24340
|
+
const evaluators = [
|
|
24341
|
+
relevanceResult.status === "fulfilled" ? "relevance-agent" : "relevance-failed",
|
|
24342
|
+
noiseResult.status === "fulfilled" ? "noise-agent" : "noise-failed"
|
|
24343
|
+
];
|
|
24344
|
+
const score = this.makeScore(ratio, dPrime, entries.length, signalEntries.length, noiseEntries.length, sparsity, evaluators);
|
|
24345
|
+
this.lastScore = score;
|
|
24346
|
+
this.evaluationCount++;
|
|
24347
|
+
return score;
|
|
24348
|
+
}
|
|
24349
|
+
// ── Evaluator agent ──────────────────────────────────────────────────
|
|
24350
|
+
async runEvaluatorAgent(name, prompt, onEvent) {
|
|
24351
|
+
const backend = new OllamaAgenticBackend(this.config.backendUrl, this.config.model, this.config.apiKey);
|
|
24352
|
+
const modelTier = getModelTier(this.config.model);
|
|
24353
|
+
const runner = new AgenticRunner(backend, {
|
|
24354
|
+
maxTurns: 5,
|
|
24355
|
+
// Evaluators are very focused — 5 turns max
|
|
24356
|
+
maxTokens: 4096,
|
|
24357
|
+
temperature: 0,
|
|
24358
|
+
// Deterministic scoring
|
|
24359
|
+
requestTimeoutMs: this.config.timeoutMs,
|
|
24360
|
+
taskTimeoutMs: this.config.timeoutMs,
|
|
24361
|
+
compactionThreshold: modelTier === "small" ? 8e3 : 16e3,
|
|
24362
|
+
modelTier
|
|
24363
|
+
});
|
|
24364
|
+
const tools = [
|
|
24365
|
+
new MemoryReadTool(this.repoRoot),
|
|
24366
|
+
new MemorySearchTool(this.repoRoot)
|
|
24367
|
+
];
|
|
24368
|
+
runner.registerTools([
|
|
24369
|
+
...tools.map(adaptTool3),
|
|
24370
|
+
{
|
|
24371
|
+
name: "task_complete",
|
|
24372
|
+
description: "Signal evaluation is complete with your scored results.",
|
|
24373
|
+
parameters: {
|
|
24374
|
+
type: "object",
|
|
24375
|
+
properties: {
|
|
24376
|
+
summary: { type: "string", description: "JSON array of scored entries" }
|
|
24377
|
+
},
|
|
24378
|
+
required: ["summary"]
|
|
24379
|
+
},
|
|
24380
|
+
async execute(args) {
|
|
24381
|
+
return { success: true, output: args["summary"] || "[]" };
|
|
24382
|
+
}
|
|
24383
|
+
}
|
|
24384
|
+
]);
|
|
24385
|
+
if (onEvent)
|
|
24386
|
+
runner.onEvent(onEvent);
|
|
24387
|
+
const result = await runner.run(prompt, `SNR Evaluator (${name}). Working directory: ${this.repoRoot}`);
|
|
24388
|
+
return result.summary || "[]";
|
|
24389
|
+
}
|
|
24390
|
+
// ── Memory loading ───────────────────────────────────────────────────
|
|
24391
|
+
loadMemoryEntries(topics) {
|
|
24392
|
+
const entries = [];
|
|
24393
|
+
const dirs = [
|
|
24394
|
+
join37(this.repoRoot, ".oa", "memory"),
|
|
24395
|
+
join37(this.repoRoot, ".open-agents", "memory")
|
|
24396
|
+
];
|
|
24397
|
+
for (const dir of dirs) {
|
|
24398
|
+
if (!existsSync27(dir))
|
|
24399
|
+
continue;
|
|
24400
|
+
try {
|
|
24401
|
+
const files = readdirSync12(dir).filter((f) => f.endsWith(".json"));
|
|
24402
|
+
for (const f of files) {
|
|
24403
|
+
const topic = basename14(f, ".json");
|
|
24404
|
+
if (topics.length > 0 && !topics.includes(topic))
|
|
24405
|
+
continue;
|
|
24406
|
+
try {
|
|
24407
|
+
const data = JSON.parse(readFileSync20(join37(dir, f), "utf-8"));
|
|
24408
|
+
for (const [key, val] of Object.entries(data)) {
|
|
24409
|
+
const value = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
|
|
24410
|
+
entries.push({ topic, key, value });
|
|
24411
|
+
}
|
|
24412
|
+
} catch {
|
|
24413
|
+
}
|
|
24414
|
+
}
|
|
24415
|
+
} catch {
|
|
24416
|
+
}
|
|
24417
|
+
}
|
|
24418
|
+
return entries;
|
|
24419
|
+
}
|
|
24420
|
+
// ── Result parsing ───────────────────────────────────────────────────
|
|
24421
|
+
parseEvaluatorResult(summary, entryCount, defaultScore) {
|
|
24422
|
+
const scores = new Array(entryCount).fill(defaultScore);
|
|
24423
|
+
try {
|
|
24424
|
+
const jsonMatch = summary.match(/\[[\s\S]*\]/);
|
|
24425
|
+
if (!jsonMatch)
|
|
24426
|
+
return scores;
|
|
24427
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
24428
|
+
for (const item of parsed) {
|
|
24429
|
+
const idx = typeof item.index === "number" ? item.index : -1;
|
|
24430
|
+
const score = typeof item.score === "number" ? item.score : typeof item.noise_score === "number" ? item.noise_score : defaultScore;
|
|
24431
|
+
if (idx >= 0 && idx < entryCount) {
|
|
24432
|
+
scores[idx] = Math.max(0, Math.min(1, score));
|
|
24433
|
+
}
|
|
24434
|
+
}
|
|
24435
|
+
} catch {
|
|
24436
|
+
}
|
|
24437
|
+
return scores;
|
|
24438
|
+
}
|
|
24439
|
+
// ── Memory pruning recommendations ──────────────────────────────────
|
|
24440
|
+
/**
|
|
24441
|
+
* Identify low-SNR memory entries that should be pruned.
|
|
24442
|
+
* Mirrors synaptic downscaling during slow-wave sleep — weak connections
|
|
24443
|
+
* (low-relevance, high-redundancy entries) are depotentiated.
|
|
24444
|
+
*
|
|
24445
|
+
* Returns topics+keys of entries scoring below the noise threshold.
|
|
24446
|
+
* Does NOT delete anything — the caller decides what to do with the list.
|
|
24447
|
+
*/
|
|
24448
|
+
getPruningCandidates(currentTask, noiseThreshold = 0.3) {
|
|
24449
|
+
const entries = this.loadMemoryEntries([]);
|
|
24450
|
+
if (entries.length === 0)
|
|
24451
|
+
return [];
|
|
24452
|
+
const taskTerms = new Set(currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 3).map((w) => w.replace(/[^a-z0-9]/g, "")));
|
|
24453
|
+
const candidates = [];
|
|
24454
|
+
for (const entry of entries) {
|
|
24455
|
+
const entryTerms = entry.value.toLowerCase().split(/\s+/).filter((w) => w.length > 3).map((w) => w.replace(/[^a-z0-9]/g, ""));
|
|
24456
|
+
const matchCount = entryTerms.filter((t) => taskTerms.has(t)).length;
|
|
24457
|
+
const relevance = entryTerms.length > 0 ? matchCount / Math.sqrt(entryTerms.length) : 0;
|
|
24458
|
+
if (relevance < noiseThreshold) {
|
|
24459
|
+
candidates.push({
|
|
24460
|
+
topic: entry.topic,
|
|
24461
|
+
key: entry.key,
|
|
24462
|
+
score: relevance,
|
|
24463
|
+
reason: relevance === 0 ? "no keyword overlap with current task" : `low relevance (${(relevance * 100).toFixed(0)}% < ${(noiseThreshold * 100).toFixed(0)}% threshold)`
|
|
24464
|
+
});
|
|
24465
|
+
}
|
|
24466
|
+
}
|
|
24467
|
+
return candidates.sort((a, b) => a.score - b.score);
|
|
24468
|
+
}
|
|
24469
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
24470
|
+
makeScore(ratio, dPrime, total, signal, noise, sparsity, evaluators, capacityWarning = false) {
|
|
24471
|
+
return {
|
|
24472
|
+
ratio: Math.max(0, Math.min(1, ratio)),
|
|
24473
|
+
dPrime: Math.max(0, dPrime),
|
|
24474
|
+
entriesEvaluated: total,
|
|
24475
|
+
signalCount: signal,
|
|
24476
|
+
noiseCount: noise,
|
|
24477
|
+
sparsity: Math.max(0, Math.min(1, sparsity)),
|
|
24478
|
+
evaluatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24479
|
+
evaluators,
|
|
24480
|
+
capacityWarning
|
|
24481
|
+
};
|
|
24482
|
+
}
|
|
24483
|
+
};
|
|
24484
|
+
}
|
|
24485
|
+
});
|
|
24486
|
+
|
|
24074
24487
|
// packages/cli/dist/tui/telegram-bridge.js
|
|
24075
24488
|
function renderTelegramStart(botUsername, adminId) {
|
|
24076
24489
|
process.stdout.write(`
|
|
@@ -24327,7 +24740,7 @@ function themeForTool(toolName) {
|
|
|
24327
24740
|
return THEME_DEFAULT;
|
|
24328
24741
|
}
|
|
24329
24742
|
}
|
|
24330
|
-
var DENSITY, WAVE, THEME_DEFAULT, THEME_FILE, THEME_SHELL, THEME_WEB, THEME_SEARCH, THEME_MEMORY, THEME_SKILL, THEME_TOOL_CREATE, DEFAULT_METRICS, BrailleSpinner;
|
|
24743
|
+
var DENSITY, WAVE, THEME_DEFAULT, THEME_FILE, THEME_SHELL, THEME_WEB, THEME_SEARCH, THEME_MEMORY, THEME_SKILL, THEME_TOOL_CREATE, THEME_DREAM, DEFAULT_METRICS, BrailleSpinner;
|
|
24331
24744
|
var init_braille_spinner = __esm({
|
|
24332
24745
|
"packages/cli/dist/tui/braille-spinner.js"() {
|
|
24333
24746
|
"use strict";
|
|
@@ -24384,10 +24797,16 @@ var init_braille_spinner = __esm({
|
|
|
24384
24797
|
ramp: [237, 173, 174, 179, 180, 215, 216, 222, 229],
|
|
24385
24798
|
speed: 2
|
|
24386
24799
|
};
|
|
24800
|
+
THEME_DREAM = {
|
|
24801
|
+
ramp: [237, 94, 130, 136, 172, 178, 179, 214, 220],
|
|
24802
|
+
speed: 1
|
|
24803
|
+
};
|
|
24387
24804
|
DEFAULT_METRICS = {
|
|
24388
24805
|
contextPct: 0,
|
|
24389
24806
|
tokenRate: 0,
|
|
24390
|
-
isStreaming: false
|
|
24807
|
+
isStreaming: false,
|
|
24808
|
+
snr: 0.5,
|
|
24809
|
+
isDreaming: false
|
|
24391
24810
|
};
|
|
24392
24811
|
BrailleSpinner = class {
|
|
24393
24812
|
frame = 0;
|
|
@@ -24439,6 +24858,10 @@ var init_braille_spinner = __esm({
|
|
|
24439
24858
|
this._metrics.tokenRate = metrics.tokenRate;
|
|
24440
24859
|
if (metrics.isStreaming !== void 0)
|
|
24441
24860
|
this._metrics.isStreaming = metrics.isStreaming;
|
|
24861
|
+
if (metrics.snr !== void 0)
|
|
24862
|
+
this._metrics.snr = Math.max(0, Math.min(1, metrics.snr));
|
|
24863
|
+
if (metrics.isDreaming !== void 0)
|
|
24864
|
+
this._metrics.isDreaming = metrics.isDreaming;
|
|
24442
24865
|
}
|
|
24443
24866
|
/**
|
|
24444
24867
|
* Render the current animation frame as an ANSI-colored string.
|
|
@@ -24450,28 +24873,41 @@ var init_braille_spinner = __esm({
|
|
|
24450
24873
|
* used — gentle ripples at low usage, full waves at high usage.
|
|
24451
24874
|
* - Slinky: a secondary slow sine wave creates organic compression and
|
|
24452
24875
|
* expansion across columns, making the wave feel elastic and alive.
|
|
24876
|
+
* - SNR entropy: low SNR injects phase jitter per-column (noisy neural signal),
|
|
24877
|
+
* high SNR produces smooth coherent waves (clean signal propagation).
|
|
24878
|
+
* - Dream mode: slower, deeper breathing with warm amber color override.
|
|
24453
24879
|
*/
|
|
24454
24880
|
render(width) {
|
|
24455
24881
|
const cycleLen = WAVE.length;
|
|
24456
|
-
const baseSpeed = this.theme.speed;
|
|
24457
24882
|
const m = this._metrics;
|
|
24458
|
-
const
|
|
24883
|
+
const activeTheme = m.isDreaming ? THEME_DREAM : this.theme;
|
|
24884
|
+
const activeColorRamp = m.isDreaming ? buildColorRamp(THEME_DREAM.ramp) : this.colorRamp;
|
|
24885
|
+
const baseSpeed = activeTheme.speed;
|
|
24886
|
+
const breathRate = m.isDreaming ? 0.04 : 0.08;
|
|
24887
|
+
const breathPhase = Math.sin(this.frame * breathRate);
|
|
24459
24888
|
let speed;
|
|
24460
24889
|
if (m.isStreaming && m.tokenRate > 0) {
|
|
24461
24890
|
const rateBoost = Math.min(4, m.tokenRate / 8);
|
|
24462
24891
|
speed = baseSpeed + rateBoost + breathPhase * 0.3;
|
|
24892
|
+
} else if (m.isDreaming) {
|
|
24893
|
+
speed = baseSpeed * 0.6 + breathPhase * 1.2;
|
|
24463
24894
|
} else {
|
|
24464
24895
|
speed = baseSpeed + breathPhase * 0.8;
|
|
24465
24896
|
}
|
|
24466
24897
|
const pressure = Math.max(0, Math.min(100, m.contextPct)) / 100;
|
|
24467
24898
|
const densityScale = 0.3 + pressure * 0.7;
|
|
24899
|
+
const snr = m.snr;
|
|
24900
|
+
const entropy = Math.max(0, 1 - snr);
|
|
24468
24901
|
const slinkyFreq = 0.02 + (m.isStreaming ? 0.01 : 0);
|
|
24469
24902
|
const slinkyAmp = 1.5 + pressure * 2;
|
|
24903
|
+
const jitterSeed = this.frame * 7919;
|
|
24470
24904
|
let buf = "";
|
|
24471
24905
|
let lastColor = -1;
|
|
24472
24906
|
for (let col = 0; col < width; col++) {
|
|
24473
24907
|
const slinkyOffset = Math.sin(col * 0.1 + this.frame * slinkyFreq) * slinkyAmp;
|
|
24474
|
-
const
|
|
24908
|
+
const jitterHash = Math.sin((col * 127 + jitterSeed) * 1e-3) * 2 + Math.cos((col * 311 + jitterSeed) * 7e-4) * 1.5;
|
|
24909
|
+
const snrJitter = jitterHash * entropy * 3;
|
|
24910
|
+
const rawPhase = col * speed + this.frame + slinkyOffset + snrJitter;
|
|
24475
24911
|
const normalizedPhase = (rawPhase % cycleLen + cycleLen) % cycleLen;
|
|
24476
24912
|
const waveIdx = Math.round(normalizedPhase) % cycleLen;
|
|
24477
24913
|
let amplitude;
|
|
@@ -24489,7 +24925,7 @@ var init_braille_spinner = __esm({
|
|
|
24489
24925
|
}
|
|
24490
24926
|
scaledIdx = Math.max(0, Math.min(cycleLen - 1, scaledIdx));
|
|
24491
24927
|
const ch = WAVE[scaledIdx];
|
|
24492
|
-
const color =
|
|
24928
|
+
const color = activeColorRamp[scaledIdx];
|
|
24493
24929
|
if (color !== lastColor) {
|
|
24494
24930
|
buf += `\x1B[38;5;${color}m`;
|
|
24495
24931
|
lastColor = color;
|
|
@@ -24801,6 +25237,13 @@ var init_status_bar = __esm({
|
|
|
24801
25237
|
setActiveTool(toolName) {
|
|
24802
25238
|
this._brailleSpinner.setTool(toolName);
|
|
24803
25239
|
}
|
|
25240
|
+
/**
|
|
25241
|
+
* Forward metrics directly to the braille spinner.
|
|
25242
|
+
* Used for SNR, dream mode, and other neural activity indicators.
|
|
25243
|
+
*/
|
|
25244
|
+
setBrailleMetrics(metrics) {
|
|
25245
|
+
this._brailleSpinner.setMetrics(metrics);
|
|
25246
|
+
}
|
|
24804
25247
|
/** Context window size to display. Can be updated if model changes. */
|
|
24805
25248
|
setContextWindowSize(size) {
|
|
24806
25249
|
this.metrics.contextWindowSize = size;
|
|
@@ -24827,6 +25270,21 @@ var init_status_bar = __esm({
|
|
|
24827
25270
|
recordSpeedTaskEnd() {
|
|
24828
25271
|
this._speedTracker.taskEnd();
|
|
24829
25272
|
}
|
|
25273
|
+
/** SNR (Signal-to-Noise Ratio) from context quality evaluation */
|
|
25274
|
+
_snr = null;
|
|
25275
|
+
/** Update the SNR gauge with a new evaluation result */
|
|
25276
|
+
setSNR(ratio, dPrime, capacityWarning) {
|
|
25277
|
+
this._snr = { ratio, dPrime, capacityWarning };
|
|
25278
|
+
this._brailleSpinner.setMetrics({ snr: ratio });
|
|
25279
|
+
if (this.active)
|
|
25280
|
+
this.renderFooterPreserveCursor();
|
|
25281
|
+
}
|
|
25282
|
+
/** Clear the SNR gauge (e.g., when starting a fresh task) */
|
|
25283
|
+
clearSNR() {
|
|
25284
|
+
this._snr = null;
|
|
25285
|
+
if (this.active)
|
|
25286
|
+
this.renderFooterPreserveCursor();
|
|
25287
|
+
}
|
|
24830
25288
|
/** Model capabilities — shown as emoji indicators on the status bar */
|
|
24831
25289
|
_caps = {
|
|
24832
25290
|
vision: false,
|
|
@@ -25033,6 +25491,14 @@ var init_status_bar = __esm({
|
|
|
25033
25491
|
const costStr = m.estimatedCost < 0.01 ? `$${m.estimatedCost.toFixed(4)}` : m.estimatedCost < 1 ? `$${m.estimatedCost.toFixed(3)}` : `$${m.estimatedCost.toFixed(2)}`;
|
|
25034
25492
|
costLabel = pipe + pastel2(222, "Cost: ") + c2.bold(costStr);
|
|
25035
25493
|
}
|
|
25494
|
+
let snrLabel = "";
|
|
25495
|
+
if (this._snr) {
|
|
25496
|
+
const snrPct = Math.round(this._snr.ratio * 100);
|
|
25497
|
+
const snrColor = snrPct >= 70 ? c2.green : snrPct >= 40 ? c2.yellow : c2.red;
|
|
25498
|
+
const dPrimeStr = this._snr.dPrime.toFixed(1);
|
|
25499
|
+
const capStr = this._snr.capacityWarning ? c2.red(" !CAP") : "";
|
|
25500
|
+
snrLabel = pipe + pastel2(183, "SNR: ") + snrColor(c2.bold(`${snrPct}%`)) + c2.dim(` d'${dPrimeStr}`) + capStr;
|
|
25501
|
+
}
|
|
25036
25502
|
let speedLabel = "";
|
|
25037
25503
|
if (this._speedTracker.hasData) {
|
|
25038
25504
|
const ratio = this._speedTracker.getSpeedRatio();
|
|
@@ -25056,7 +25522,7 @@ var init_status_bar = __esm({
|
|
|
25056
25522
|
if (this._caps.thinking)
|
|
25057
25523
|
capParts.push("\u{1F9E0}");
|
|
25058
25524
|
const capsLabel = capParts.length > 0 ? pipe + pastel2(183, capParts.join(" ")) : "";
|
|
25059
|
-
return ` ${tokInLabel}${pipe}${tokOutLabel}${pipe}${ctxLabel}${speedLabel}${costLabel}${capsLabel}${recordingLabel}`;
|
|
25525
|
+
return ` ${tokInLabel}${pipe}${tokOutLabel}${pipe}${ctxLabel}${snrLabel}${speedLabel}${costLabel}${capsLabel}${recordingLabel}`;
|
|
25060
25526
|
}
|
|
25061
25527
|
// -------------------------------------------------------------------------
|
|
25062
25528
|
// Private
|
|
@@ -25335,11 +25801,11 @@ var init_status_bar = __esm({
|
|
|
25335
25801
|
import * as readline2 from "node:readline";
|
|
25336
25802
|
import { Writable } from "node:stream";
|
|
25337
25803
|
import { cwd } from "node:process";
|
|
25338
|
-
import { resolve as resolve23, join as
|
|
25804
|
+
import { resolve as resolve23, join as join38, dirname as dirname11, extname as extname9 } from "node:path";
|
|
25339
25805
|
import { createRequire as createRequire2 } from "node:module";
|
|
25340
25806
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
25341
|
-
import { readFileSync as
|
|
25342
|
-
import { existsSync as
|
|
25807
|
+
import { readFileSync as readFileSync21, rmSync as rmSync2, readdirSync as readdirSync13 } from "node:fs";
|
|
25808
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
25343
25809
|
function formatTimeAgo(date) {
|
|
25344
25810
|
const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
|
|
25345
25811
|
if (seconds < 60)
|
|
@@ -25358,12 +25824,12 @@ function getVersion() {
|
|
|
25358
25824
|
const require2 = createRequire2(import.meta.url);
|
|
25359
25825
|
const thisDir = dirname11(fileURLToPath8(import.meta.url));
|
|
25360
25826
|
const candidates = [
|
|
25361
|
-
|
|
25362
|
-
|
|
25363
|
-
|
|
25827
|
+
join38(thisDir, "..", "package.json"),
|
|
25828
|
+
join38(thisDir, "..", "..", "package.json"),
|
|
25829
|
+
join38(thisDir, "..", "..", "..", "package.json")
|
|
25364
25830
|
];
|
|
25365
25831
|
for (const pkgPath of candidates) {
|
|
25366
|
-
if (
|
|
25832
|
+
if (existsSync28(pkgPath)) {
|
|
25367
25833
|
const pkg = require2(pkgPath);
|
|
25368
25834
|
if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
|
|
25369
25835
|
return pkg.version ?? "0.0.0";
|
|
@@ -25374,7 +25840,7 @@ function getVersion() {
|
|
|
25374
25840
|
}
|
|
25375
25841
|
return "0.0.0";
|
|
25376
25842
|
}
|
|
25377
|
-
function
|
|
25843
|
+
function adaptTool4(tool) {
|
|
25378
25844
|
return {
|
|
25379
25845
|
name: tool.name,
|
|
25380
25846
|
description: tool.description,
|
|
@@ -25470,7 +25936,7 @@ function buildTools(repoRoot, config, contextWindowSize) {
|
|
|
25470
25936
|
new AgendaTool(repoRoot)
|
|
25471
25937
|
];
|
|
25472
25938
|
return [
|
|
25473
|
-
...executionTools.map(
|
|
25939
|
+
...executionTools.map(adaptTool4),
|
|
25474
25940
|
createSubAgentTool(config, repoRoot, contextWindowSize),
|
|
25475
25941
|
createTaskCompleteTool()
|
|
25476
25942
|
];
|
|
@@ -25522,7 +25988,7 @@ function createSubAgentTool(config, repoRoot, ctxWindowSize) {
|
|
|
25522
25988
|
new MemoryReadTool(repoRoot),
|
|
25523
25989
|
new MemoryWriteTool(repoRoot)
|
|
25524
25990
|
];
|
|
25525
|
-
subRunner.registerTools(subTools.map(
|
|
25991
|
+
subRunner.registerTools(subTools.map(adaptTool4));
|
|
25526
25992
|
subRunner.registerTool(createTaskCompleteTool());
|
|
25527
25993
|
if (background) {
|
|
25528
25994
|
const promise = subRunner.run(task, `Working directory: ${repoRoot}`).then((result2) => {
|
|
@@ -25552,7 +26018,30 @@ Use task_status("${taskId}") or task_output("${taskId}") to check progress.`
|
|
|
25552
26018
|
}
|
|
25553
26019
|
};
|
|
25554
26020
|
}
|
|
25555
|
-
function
|
|
26021
|
+
function gatherMemorySnippets(root) {
|
|
26022
|
+
const snippets = [];
|
|
26023
|
+
const dirs = [
|
|
26024
|
+
join38(root, ".oa", "memory"),
|
|
26025
|
+
join38(root, ".open-agents", "memory")
|
|
26026
|
+
];
|
|
26027
|
+
for (const dir of dirs) {
|
|
26028
|
+
if (!existsSync28(dir))
|
|
26029
|
+
continue;
|
|
26030
|
+
try {
|
|
26031
|
+
for (const f of readdirSync13(dir).filter((f2) => f2.endsWith(".json"))) {
|
|
26032
|
+
const data = JSON.parse(readFileSync21(join38(dir, f), "utf-8"));
|
|
26033
|
+
for (const val of Object.values(data)) {
|
|
26034
|
+
const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
|
|
26035
|
+
if (v.length > 10)
|
|
26036
|
+
snippets.push(v);
|
|
26037
|
+
}
|
|
26038
|
+
}
|
|
26039
|
+
} catch {
|
|
26040
|
+
}
|
|
26041
|
+
}
|
|
26042
|
+
return snippets;
|
|
26043
|
+
}
|
|
26044
|
+
function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction) {
|
|
25556
26045
|
const voiceStyleMap = {
|
|
25557
26046
|
concise: 1,
|
|
25558
26047
|
balanced: 3,
|
|
@@ -25576,6 +26065,7 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
|
|
|
25576
26065
|
taskTimeoutMs: 36e5,
|
|
25577
26066
|
// 60 minutes — never give up prematurely
|
|
25578
26067
|
compactionThreshold,
|
|
26068
|
+
deepContext: deepContext ?? false,
|
|
25579
26069
|
dynamicContext,
|
|
25580
26070
|
modelTier,
|
|
25581
26071
|
streamEnabled: stream?.enabled ?? false,
|
|
@@ -25686,7 +26176,7 @@ ${entry.fullContent}`
|
|
|
25686
26176
|
renderVoiceText(desc);
|
|
25687
26177
|
voice.speak(desc);
|
|
25688
26178
|
}
|
|
25689
|
-
renderToolCallStart(event.toolName ?? "unknown", event.toolArgs ?? {});
|
|
26179
|
+
renderToolCallStart(event.toolName ?? "unknown", event.toolArgs ?? {}, config.verbose);
|
|
25690
26180
|
});
|
|
25691
26181
|
break;
|
|
25692
26182
|
case "tool_result": {
|
|
@@ -25702,7 +26192,7 @@ ${entry.fullContent}`
|
|
|
25702
26192
|
const toolDurationMs = toolCallStartMs > 0 ? Date.now() - toolCallStartMs : 0;
|
|
25703
26193
|
toolCallStartMs = 0;
|
|
25704
26194
|
contentWrite(() => {
|
|
25705
|
-
renderToolResult(event.toolName ?? "unknown", event.success ?? false, event.content ?? "");
|
|
26195
|
+
renderToolResult(event.toolName ?? "unknown", event.success ?? false, event.content ?? "", config.verbose);
|
|
25706
26196
|
if (config.verbose && toolDurationMs > 0) {
|
|
25707
26197
|
const durStr = toolDurationMs < 1e3 ? `${toolDurationMs}ms` : `${(toolDurationMs / 1e3).toFixed(1)}s`;
|
|
25708
26198
|
const sizeStr = resultLen > 0 ? ` | ${resultLen.toLocaleString()} chars (~${Math.ceil(resultLen / 4).toLocaleString()} tokens)` : "";
|
|
@@ -25764,6 +26254,8 @@ ${entry.fullContent}`
|
|
|
25764
26254
|
break;
|
|
25765
26255
|
case "compaction":
|
|
25766
26256
|
contentWrite(() => renderWarning(`Context compacted: ${event.content}`));
|
|
26257
|
+
if (onCompaction)
|
|
26258
|
+
onCompaction();
|
|
25767
26259
|
break;
|
|
25768
26260
|
case "status":
|
|
25769
26261
|
contentWrite(() => renderInfo(event.content ?? ""));
|
|
@@ -25924,6 +26416,7 @@ async function startInteractive(config, repoPath) {
|
|
|
25924
26416
|
let streamEnabled = savedSettings.stream ?? false;
|
|
25925
26417
|
let bruteForceEnabled = savedSettings.bruteforce ?? true;
|
|
25926
26418
|
let currentStyle = PRESET_NAMES.includes(savedSettings.style) ? savedSettings.style : "balanced";
|
|
26419
|
+
let deepContextEnabled = savedSettings.deepContext ?? false;
|
|
25927
26420
|
if (savedSettings.emojis !== void 0)
|
|
25928
26421
|
setEmojisEnabled(savedSettings.emojis);
|
|
25929
26422
|
if (savedSettings.colors !== void 0)
|
|
@@ -26067,6 +26560,37 @@ async function startInteractive(config, repoPath) {
|
|
|
26067
26560
|
let dreamEngine = null;
|
|
26068
26561
|
let blessEngine = null;
|
|
26069
26562
|
let dmnEngine = null;
|
|
26563
|
+
const snrEngine = new SNREngine(config, repoRoot);
|
|
26564
|
+
let pendingSNREval = null;
|
|
26565
|
+
function scheduleAgentSNREval(taskPrompt) {
|
|
26566
|
+
if (pendingSNREval)
|
|
26567
|
+
return;
|
|
26568
|
+
pendingSNREval = (async () => {
|
|
26569
|
+
try {
|
|
26570
|
+
const score = await snrEngine.evaluateWithAgents(taskPrompt, []);
|
|
26571
|
+
statusBar.setSNR(score.ratio, score.dPrime, score.capacityWarning);
|
|
26572
|
+
renderSNRUpdate(score);
|
|
26573
|
+
} catch {
|
|
26574
|
+
} finally {
|
|
26575
|
+
pendingSNREval = null;
|
|
26576
|
+
}
|
|
26577
|
+
})();
|
|
26578
|
+
}
|
|
26579
|
+
function compactionSNRCallback() {
|
|
26580
|
+
if (!deepContextEnabled)
|
|
26581
|
+
return void 0;
|
|
26582
|
+
return () => {
|
|
26583
|
+
try {
|
|
26584
|
+
const memSnippets = gatherMemorySnippets(repoRoot);
|
|
26585
|
+
if (memSnippets.length > 0 && lastSubmittedPrompt) {
|
|
26586
|
+
const snr = snrEngine.computeLocalSNR(lastSubmittedPrompt, memSnippets, resolvedContextWindowSize);
|
|
26587
|
+
statusBar.setSNR(snr.ratio, snr.dPrime, snr.capacityWarning);
|
|
26588
|
+
scheduleAgentSNREval(lastSubmittedPrompt);
|
|
26589
|
+
}
|
|
26590
|
+
} catch {
|
|
26591
|
+
}
|
|
26592
|
+
};
|
|
26593
|
+
}
|
|
26070
26594
|
let telegramBridge = null;
|
|
26071
26595
|
let activeTelegramChatId = null;
|
|
26072
26596
|
let dmnRetriggerTimer = null;
|
|
@@ -26096,6 +26620,8 @@ Respond concisely and safely.`;
|
|
|
26096
26620
|
return;
|
|
26097
26621
|
}
|
|
26098
26622
|
writeContent(() => renderInfo("DMN re-activating..."));
|
|
26623
|
+
statusBar.setProcessing(true);
|
|
26624
|
+
statusBar.setBrailleMetrics({ isDreaming: true });
|
|
26099
26625
|
try {
|
|
26100
26626
|
const proposal = await dmnEngine.runCycle((event) => {
|
|
26101
26627
|
if (event.type === "tool_call") {
|
|
@@ -26103,6 +26629,8 @@ Respond concisely and safely.`;
|
|
|
26103
26629
|
`));
|
|
26104
26630
|
}
|
|
26105
26631
|
});
|
|
26632
|
+
statusBar.setProcessing(false);
|
|
26633
|
+
statusBar.setBrailleMetrics({ isDreaming: false });
|
|
26106
26634
|
if (proposal) {
|
|
26107
26635
|
const provenanceNote = proposal.provenance.length > 0 ? `
|
|
26108
26636
|
|
|
@@ -26118,6 +26646,8 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
26118
26646
|
scheduleDMNRetrigger(6e4);
|
|
26119
26647
|
}
|
|
26120
26648
|
} catch (err) {
|
|
26649
|
+
statusBar.setProcessing(false);
|
|
26650
|
+
statusBar.setBrailleMetrics({ isDreaming: false });
|
|
26121
26651
|
writeContent(() => renderWarning(`DMN cycle error: ${err instanceof Error ? err.message : String(err)}`));
|
|
26122
26652
|
scheduleDMNRetrigger(6e4);
|
|
26123
26653
|
}
|
|
@@ -26175,6 +26705,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
26175
26705
|
"/telegram",
|
|
26176
26706
|
"/compact",
|
|
26177
26707
|
"/gc",
|
|
26708
|
+
"/deep",
|
|
26178
26709
|
"/style",
|
|
26179
26710
|
"/personality"
|
|
26180
26711
|
];
|
|
@@ -26336,6 +26867,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
26336
26867
|
bruteForceEnabled = !bruteForceEnabled;
|
|
26337
26868
|
return bruteForceEnabled;
|
|
26338
26869
|
},
|
|
26870
|
+
deepContextToggle() {
|
|
26871
|
+
deepContextEnabled = !deepContextEnabled;
|
|
26872
|
+
return deepContextEnabled;
|
|
26873
|
+
},
|
|
26339
26874
|
setStyle(preset) {
|
|
26340
26875
|
currentStyle = preset;
|
|
26341
26876
|
},
|
|
@@ -26376,6 +26911,8 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
26376
26911
|
}
|
|
26377
26912
|
dreamEngine = new DreamEngine(currentConfig, repoRoot);
|
|
26378
26913
|
writeContent(() => renderDreamStart(mode));
|
|
26914
|
+
statusBar.setProcessing(true);
|
|
26915
|
+
statusBar.setBrailleMetrics({ isDreaming: true });
|
|
26379
26916
|
dreamEngine.start(mode, (event) => {
|
|
26380
26917
|
if (event.type === "tool_call") {
|
|
26381
26918
|
writeContent(() => renderToolCallStart(event.toolName ?? "unknown", event.toolArgs ?? {}));
|
|
@@ -26389,10 +26926,14 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
26389
26926
|
});
|
|
26390
26927
|
}
|
|
26391
26928
|
}).then((state) => {
|
|
26929
|
+
statusBar.setProcessing(false);
|
|
26930
|
+
statusBar.setBrailleMetrics({ isDreaming: false });
|
|
26392
26931
|
writeContent(() => renderDreamEnd(state));
|
|
26393
26932
|
dreamEngine = null;
|
|
26394
26933
|
showPrompt();
|
|
26395
26934
|
}).catch((err) => {
|
|
26935
|
+
statusBar.setProcessing(false);
|
|
26936
|
+
statusBar.setBrailleMetrics({ isDreaming: false });
|
|
26396
26937
|
writeContent(() => renderError(`Dream error: ${err instanceof Error ? err.message : String(err)}`));
|
|
26397
26938
|
dreamEngine = null;
|
|
26398
26939
|
showPrompt();
|
|
@@ -26402,6 +26943,8 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
26402
26943
|
if (dreamEngine?.isActive) {
|
|
26403
26944
|
dreamEngine.stop();
|
|
26404
26945
|
dreamEngine = null;
|
|
26946
|
+
statusBar.setProcessing(false);
|
|
26947
|
+
statusBar.setBrailleMetrics({ isDreaming: false });
|
|
26405
26948
|
}
|
|
26406
26949
|
},
|
|
26407
26950
|
isDreaming() {
|
|
@@ -26615,8 +27158,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
26615
27158
|
return true;
|
|
26616
27159
|
},
|
|
26617
27160
|
destroyProject() {
|
|
26618
|
-
const oaPath =
|
|
26619
|
-
if (
|
|
27161
|
+
const oaPath = join38(repoRoot, OA_DIR);
|
|
27162
|
+
if (existsSync28(oaPath)) {
|
|
26620
27163
|
try {
|
|
26621
27164
|
rmSync2(oaPath, { recursive: true, force: true });
|
|
26622
27165
|
writeContent(() => renderInfo(`Removed ${OA_DIR}/ directory.`));
|
|
@@ -26875,7 +27418,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
|
|
|
26875
27418
|
toolPatternStore: toolPatternStore ?? void 0
|
|
26876
27419
|
}, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary) => {
|
|
26877
27420
|
lastCompletedSummary = summary;
|
|
26878
|
-
}, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle);
|
|
27421
|
+
}, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle, deepContextEnabled, compactionSNRCallback());
|
|
26879
27422
|
activeTask = task;
|
|
26880
27423
|
showPrompt();
|
|
26881
27424
|
await task.promise;
|
|
@@ -26890,13 +27433,13 @@ Execute this skill now. Follow the behavioral guidance above.`;
|
|
|
26890
27433
|
}
|
|
26891
27434
|
}
|
|
26892
27435
|
const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
|
|
26893
|
-
const isImage = isImagePath(cleanPath) &&
|
|
26894
|
-
const isMedia = !isImage && isTranscribablePath(cleanPath) &&
|
|
27436
|
+
const isImage = isImagePath(cleanPath) && existsSync28(resolve23(repoRoot, cleanPath));
|
|
27437
|
+
const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync28(resolve23(repoRoot, cleanPath));
|
|
26895
27438
|
if (activeTask) {
|
|
26896
27439
|
if (isImage) {
|
|
26897
27440
|
try {
|
|
26898
27441
|
const imgPath = resolve23(repoRoot, cleanPath);
|
|
26899
|
-
const imgBuffer =
|
|
27442
|
+
const imgBuffer = readFileSync21(imgPath);
|
|
26900
27443
|
const base64 = imgBuffer.toString("base64");
|
|
26901
27444
|
const ext = extname9(cleanPath).toLowerCase();
|
|
26902
27445
|
const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
|
|
@@ -26965,6 +27508,14 @@ Summarize or analyze this transcription as appropriate.`;
|
|
|
26965
27508
|
const displayText = isImage ? `[Image: ${cleanPath}]` : inputLineCount > 1 ? `[pasted ${inputLineCount} lines]` : fullInput;
|
|
26966
27509
|
writeContent(() => renderUserMessage(displayText));
|
|
26967
27510
|
lastSubmittedPrompt = fullInput;
|
|
27511
|
+
try {
|
|
27512
|
+
const memSnippets = gatherMemorySnippets(repoRoot);
|
|
27513
|
+
if (memSnippets.length > 0) {
|
|
27514
|
+
const snr = snrEngine.computeLocalSNR(fullInput, memSnippets, resolvedContextWindowSize);
|
|
27515
|
+
statusBar.setSNR(snr.ratio, snr.dPrime, snr.capacityWarning);
|
|
27516
|
+
}
|
|
27517
|
+
} catch {
|
|
27518
|
+
}
|
|
26968
27519
|
let taskInput = fullInput;
|
|
26969
27520
|
if (restoredSessionContext) {
|
|
26970
27521
|
taskInput = `${restoredSessionContext}
|
|
@@ -26987,7 +27538,7 @@ NEW TASK: ${fullInput}`;
|
|
|
26987
27538
|
toolPatternStore: toolPatternStore ?? void 0
|
|
26988
27539
|
}, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary) => {
|
|
26989
27540
|
lastCompletedSummary = summary;
|
|
26990
|
-
}, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle);
|
|
27541
|
+
}, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle, deepContextEnabled, compactionSNRCallback());
|
|
26991
27542
|
activeTask = task;
|
|
26992
27543
|
showPrompt();
|
|
26993
27544
|
await task.promise;
|
|
@@ -27016,6 +27567,17 @@ NEW TASK: ${fullInput}`;
|
|
|
27016
27567
|
} finally {
|
|
27017
27568
|
statusBar.setProcessing(false);
|
|
27018
27569
|
statusBar.recordSpeedTaskEnd();
|
|
27570
|
+
try {
|
|
27571
|
+
const memSnippets = gatherMemorySnippets(repoRoot);
|
|
27572
|
+
if (memSnippets.length > 0 && lastSubmittedPrompt) {
|
|
27573
|
+
const snr = snrEngine.computeLocalSNR(lastSubmittedPrompt, memSnippets, resolvedContextWindowSize);
|
|
27574
|
+
statusBar.setSNR(snr.ratio, snr.dPrime, snr.capacityWarning);
|
|
27575
|
+
}
|
|
27576
|
+
} catch {
|
|
27577
|
+
}
|
|
27578
|
+
if (deepContextEnabled && lastSubmittedPrompt) {
|
|
27579
|
+
scheduleAgentSNREval(lastSubmittedPrompt);
|
|
27580
|
+
}
|
|
27019
27581
|
if (activeTask) {
|
|
27020
27582
|
sessionFilesTouched = Array.from(activeTask.filesTouched);
|
|
27021
27583
|
sessionToolCallCount = activeTask.toolCallCount;
|
|
@@ -27070,6 +27632,8 @@ Respond concisely and safely.`;
|
|
|
27070
27632
|
}
|
|
27071
27633
|
if (dmnEngine) {
|
|
27072
27634
|
writeContent(() => renderInfo("No queued tasks \u2014 DMN self-reflection activating..."));
|
|
27635
|
+
statusBar.setProcessing(true);
|
|
27636
|
+
statusBar.setBrailleMetrics({ isDreaming: true });
|
|
27073
27637
|
try {
|
|
27074
27638
|
const proposal = await dmnEngine.runCycle((event) => {
|
|
27075
27639
|
if (event.type === "tool_call") {
|
|
@@ -27077,6 +27641,8 @@ Respond concisely and safely.`;
|
|
|
27077
27641
|
`));
|
|
27078
27642
|
}
|
|
27079
27643
|
});
|
|
27644
|
+
statusBar.setProcessing(false);
|
|
27645
|
+
statusBar.setBrailleMetrics({ isDreaming: false });
|
|
27080
27646
|
if (proposal) {
|
|
27081
27647
|
const provenanceNote = proposal.provenance.length > 0 ? `
|
|
27082
27648
|
|
|
@@ -27096,6 +27662,8 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
27096
27662
|
scheduleDMNRetrigger(cooldownSec * 1e3);
|
|
27097
27663
|
}
|
|
27098
27664
|
} catch (err) {
|
|
27665
|
+
statusBar.setProcessing(false);
|
|
27666
|
+
statusBar.setBrailleMetrics({ isDreaming: false });
|
|
27099
27667
|
writeContent(() => renderWarning(`DMN cycle error: ${err instanceof Error ? err.message : String(err)}`));
|
|
27100
27668
|
if (blessEngine?.isActive) {
|
|
27101
27669
|
scheduleDMNRetrigger(6e4);
|
|
@@ -27210,6 +27778,7 @@ var init_interactive = __esm({
|
|
|
27210
27778
|
init_dream_engine();
|
|
27211
27779
|
init_bless_engine();
|
|
27212
27780
|
init_dmn_engine();
|
|
27781
|
+
init_snr_engine();
|
|
27213
27782
|
init_telegram_bridge();
|
|
27214
27783
|
init_status_bar();
|
|
27215
27784
|
init_dist6();
|
|
@@ -27248,7 +27817,7 @@ import { glob } from "glob";
|
|
|
27248
27817
|
import ignore from "ignore";
|
|
27249
27818
|
import { readFile as readFile14, stat as stat4 } from "node:fs/promises";
|
|
27250
27819
|
import { createHash } from "node:crypto";
|
|
27251
|
-
import { join as
|
|
27820
|
+
import { join as join39, relative as relative3, extname as extname10, basename as basename15 } from "node:path";
|
|
27252
27821
|
var DEFAULT_EXCLUDE, LANGUAGE_MAP, CodebaseIndexer;
|
|
27253
27822
|
var init_codebase_indexer = __esm({
|
|
27254
27823
|
"packages/indexer/dist/codebase-indexer.js"() {
|
|
@@ -27292,7 +27861,7 @@ var init_codebase_indexer = __esm({
|
|
|
27292
27861
|
const ig = ignore.default();
|
|
27293
27862
|
if (this.config.respectGitignore) {
|
|
27294
27863
|
try {
|
|
27295
|
-
const gitignoreContent = await readFile14(
|
|
27864
|
+
const gitignoreContent = await readFile14(join39(this.config.rootDir, ".gitignore"), "utf-8");
|
|
27296
27865
|
ig.add(gitignoreContent);
|
|
27297
27866
|
} catch {
|
|
27298
27867
|
}
|
|
@@ -27307,7 +27876,7 @@ var init_codebase_indexer = __esm({
|
|
|
27307
27876
|
for (const relativePath of files) {
|
|
27308
27877
|
if (ig.ignores(relativePath))
|
|
27309
27878
|
continue;
|
|
27310
|
-
const fullPath =
|
|
27879
|
+
const fullPath = join39(this.config.rootDir, relativePath);
|
|
27311
27880
|
try {
|
|
27312
27881
|
const fileStat = await stat4(fullPath);
|
|
27313
27882
|
if (fileStat.size > this.config.maxFileSize)
|
|
@@ -27330,7 +27899,7 @@ var init_codebase_indexer = __esm({
|
|
|
27330
27899
|
}
|
|
27331
27900
|
buildTree(files) {
|
|
27332
27901
|
const root = {
|
|
27333
|
-
name:
|
|
27902
|
+
name: basename15(this.config.rootDir),
|
|
27334
27903
|
path: this.config.rootDir,
|
|
27335
27904
|
type: "directory",
|
|
27336
27905
|
children: []
|
|
@@ -27353,7 +27922,7 @@ var init_codebase_indexer = __esm({
|
|
|
27353
27922
|
if (!child) {
|
|
27354
27923
|
child = {
|
|
27355
27924
|
name: part,
|
|
27356
|
-
path:
|
|
27925
|
+
path: join39(current.path, part),
|
|
27357
27926
|
type: "directory",
|
|
27358
27927
|
children: []
|
|
27359
27928
|
};
|
|
@@ -27436,13 +28005,13 @@ __export(index_repo_exports, {
|
|
|
27436
28005
|
indexRepoCommand: () => indexRepoCommand
|
|
27437
28006
|
});
|
|
27438
28007
|
import { resolve as resolve24 } from "node:path";
|
|
27439
|
-
import { existsSync as
|
|
28008
|
+
import { existsSync as existsSync29, statSync as statSync10 } from "node:fs";
|
|
27440
28009
|
import { cwd as cwd2 } from "node:process";
|
|
27441
28010
|
async function indexRepoCommand(opts, _config) {
|
|
27442
28011
|
const repoRoot = resolve24(opts.repoPath ?? cwd2());
|
|
27443
28012
|
printHeader("Index Repository");
|
|
27444
28013
|
printInfo(`Indexing: ${repoRoot}`);
|
|
27445
|
-
if (!
|
|
28014
|
+
if (!existsSync29(repoRoot)) {
|
|
27446
28015
|
printError(`Path does not exist: ${repoRoot}`);
|
|
27447
28016
|
process.exit(1);
|
|
27448
28017
|
}
|
|
@@ -27694,7 +28263,7 @@ var config_exports = {};
|
|
|
27694
28263
|
__export(config_exports, {
|
|
27695
28264
|
configCommand: () => configCommand
|
|
27696
28265
|
});
|
|
27697
|
-
import { join as
|
|
28266
|
+
import { join as join40, resolve as resolve25 } from "node:path";
|
|
27698
28267
|
import { homedir as homedir13 } from "node:os";
|
|
27699
28268
|
import { cwd as cwd3 } from "node:process";
|
|
27700
28269
|
function redactIfSensitive(key, value) {
|
|
@@ -27753,7 +28322,7 @@ function handleShow(opts, config) {
|
|
|
27753
28322
|
}
|
|
27754
28323
|
}
|
|
27755
28324
|
printSection("Config File");
|
|
27756
|
-
printInfo(`~/.open-agents/config.json (${
|
|
28325
|
+
printInfo(`~/.open-agents/config.json (${join40(homedir13(), ".open-agents", "config.json")})`);
|
|
27757
28326
|
printSection("Priority Chain");
|
|
27758
28327
|
printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
|
|
27759
28328
|
printInfo(" 2. Project .oa/settings.json (--local)");
|
|
@@ -27792,7 +28361,7 @@ function handleSet(opts, _config) {
|
|
|
27792
28361
|
const coerced = coerceForSettings(key, value);
|
|
27793
28362
|
saveProjectSettings(repoRoot, { [key]: coerced });
|
|
27794
28363
|
printSuccess(`Project override set: ${key} = ${redactIfSensitive(key, value)}`);
|
|
27795
|
-
printInfo(`Saved to ${
|
|
28364
|
+
printInfo(`Saved to ${join40(repoRoot, ".oa", "settings.json")}`);
|
|
27796
28365
|
printInfo("This override applies only when running in this workspace.");
|
|
27797
28366
|
} catch (err) {
|
|
27798
28367
|
printError(`Failed to save: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -28012,7 +28581,7 @@ __export(eval_exports, {
|
|
|
28012
28581
|
});
|
|
28013
28582
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
28014
28583
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync12 } from "node:fs";
|
|
28015
|
-
import { join as
|
|
28584
|
+
import { join as join41 } from "node:path";
|
|
28016
28585
|
async function evalCommand(opts, config) {
|
|
28017
28586
|
const suiteName = opts.suite ?? "basic";
|
|
28018
28587
|
const suite = SUITES[suiteName];
|
|
@@ -28133,9 +28702,9 @@ async function evalCommand(opts, config) {
|
|
|
28133
28702
|
process.exit(failed > 0 ? 1 : 0);
|
|
28134
28703
|
}
|
|
28135
28704
|
function createTempEvalRepo() {
|
|
28136
|
-
const dir =
|
|
28705
|
+
const dir = join41(tmpdir7(), `open-agents-eval-${Date.now()}`);
|
|
28137
28706
|
mkdirSync13(dir, { recursive: true });
|
|
28138
|
-
writeFileSync12(
|
|
28707
|
+
writeFileSync12(join41(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
|
|
28139
28708
|
return dir;
|
|
28140
28709
|
}
|
|
28141
28710
|
var BASIC_SUITE, FULL_SUITE, SUITES;
|
|
@@ -28195,7 +28764,7 @@ init_updater();
|
|
|
28195
28764
|
import { parseArgs as nodeParseArgs2 } from "node:util";
|
|
28196
28765
|
import { createRequire as createRequire3 } from "node:module";
|
|
28197
28766
|
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
28198
|
-
import { dirname as dirname12, join as
|
|
28767
|
+
import { dirname as dirname12, join as join42 } from "node:path";
|
|
28199
28768
|
|
|
28200
28769
|
// packages/cli/dist/cli.js
|
|
28201
28770
|
import { createInterface } from "node:readline";
|
|
@@ -28302,7 +28871,7 @@ init_output();
|
|
|
28302
28871
|
function getVersion2() {
|
|
28303
28872
|
try {
|
|
28304
28873
|
const require2 = createRequire3(import.meta.url);
|
|
28305
|
-
const pkgPath =
|
|
28874
|
+
const pkgPath = join42(dirname12(fileURLToPath9(import.meta.url)), "..", "package.json");
|
|
28306
28875
|
const pkg = require2(pkgPath);
|
|
28307
28876
|
return pkg.version;
|
|
28308
28877
|
} catch {
|
package/package.json
CHANGED