@wrongstack/core 0.270.0 → 0.272.1
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/{agent-bridge-PcHQl_UQ.d.ts → agent-bridge-DFQYEeXf.d.ts} +1 -1
- package/dist/{agent-subagent-runner-SHJW7t8q.d.ts → agent-subagent-runner-BZa_IEcd.d.ts} +7 -7
- package/dist/{brain-BYcK__Ym.d.ts → brain-etbcbRwV.d.ts} +95 -2
- package/dist/{compactor-C2RKEBtC.d.ts → compactor-72ug-ZRB.d.ts} +1 -1
- package/dist/{config-C_ae2k86.d.ts → config-rRS8yorV.d.ts} +71 -2
- package/dist/{context-Dp87Bcaq.d.ts → context-Dw55zZ_Q.d.ts} +110 -1
- package/dist/coordination/index.d.ts +181 -17
- package/dist/coordination/index.js +1018 -166
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +25 -25
- package/dist/defaults/index.js +804 -222
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +23 -18
- package/dist/execution/index.js +136 -41
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +36 -6
- package/dist/execution/prompt-enhancer.js +35 -9
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{global-mailbox-Bvrz1P3f.d.ts → global-mailbox-DJ4EoRr0.d.ts} +145 -5
- package/dist/{goal-preamble-CA_4yiGQ.d.ts → goal-preamble-hM8BH7TK.d.ts} +9 -9
- package/dist/{goal-store-DhuJoUNG.d.ts → goal-store-CWlbT0TO.d.ts} +1 -1
- package/dist/hq/index.d.ts +95 -6
- package/dist/hq/index.js +628 -50
- package/dist/hq/index.js.map +1 -1
- package/dist/{index-whDfTANu.d.ts → index-2Lhk5v0o.d.ts} +2 -2
- package/dist/{index-W4VJCzHa.d.ts → index-DWm_PE9L.d.ts} +5 -5
- package/dist/{index-CZQ6Pwbs.d.ts → index-DqW4o62H.d.ts} +8 -8
- package/dist/index.d.ts +96 -56
- package/dist/index.js +2464 -519
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +5 -3
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-DJdZiRcv.d.ts → mcp-servers-BpWHTKlE.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +28 -5
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-C3a-2-Yd.d.ts → models-registry-CXQFUn5t.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CJSpTe5O.d.ts → multi-agent-coordinator-jyimfo7D.d.ts} +1 -1
- package/dist/{null-fleet-bus-QVshIsDx.d.ts → null-fleet-bus-DOGQcvrY.d.ts} +6 -6
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-D9y5Pkcc.d.ts → parallel-eternal-engine-rItJBYp9.d.ts} +9 -9
- package/dist/{path-resolver-CnQ8SIfh.d.ts → path-resolver-DrpF5MGK.d.ts} +3 -3
- package/dist/{permission-CvYQNUqZ.d.ts → permission-CC7XFYWG.d.ts} +1 -1
- package/dist/{permission-policy-D5Ss8j4B.d.ts → permission-policy-cYR4RJmw.d.ts} +2 -2
- package/dist/{pipeline-l_zzFRh3.d.ts → pipeline-Ckkn3AOA.d.ts} +2 -2
- package/dist/{plan-templates-NtPgyeJA.d.ts → plan-templates-BvHw5Znw.d.ts} +33 -9
- package/dist/{provider-model-resolve-d5poT5y0.d.ts → provider-model-resolve-nZqnCeaR.d.ts} +3 -3
- package/dist/{provider-runner-gkctlQV_.d.ts → provider-runner-zVOn1p67.d.ts} +3 -3
- package/dist/{retry-policy-CtFhfwa8.d.ts → retry-policy-BV7nzeAd.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +2 -0
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-BLsVmTIK.d.ts → secret-vault-eMBKfheR.d.ts} +9 -1
- package/dist/security/index.d.ts +5 -5
- package/dist/security/index.js +137 -10
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-CXl2_y9W.d.ts → selector-C4ORTOid.d.ts} +1 -1
- package/dist/{session-event-bridge-Ccud20CC.d.ts → session-event-bridge-CeNpUL9w.d.ts} +1 -1
- package/dist/{session-reader-ZeXQmsmE.d.ts → session-reader-BepLSnGL.d.ts} +1 -1
- package/dist/storage/index.d.ts +50 -13
- package/dist/storage/index.js +620 -220
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js +9 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.js +202 -41
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +17 -4
- package/dist/utils/index.js +48 -9
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as crypto2 from 'crypto';
|
|
2
|
-
import { randomBytes, randomUUID, createHash, createCipheriv, createDecipheriv } from 'crypto';
|
|
2
|
+
import { randomBytes, randomUUID, createHash, createCipheriv, createDecipheriv, scryptSync } from 'crypto';
|
|
3
3
|
import * as fsp3 from 'fs/promises';
|
|
4
4
|
import { readFile, readdir, stat, mkdir } from 'fs/promises';
|
|
5
5
|
import * as path3 from 'path';
|
|
@@ -9,6 +9,8 @@ import * as net from 'net';
|
|
|
9
9
|
import * as os6 from 'os';
|
|
10
10
|
import { hostname } from 'os';
|
|
11
11
|
import * as fs3 from 'fs';
|
|
12
|
+
import { createReadStream } from 'fs';
|
|
13
|
+
import { createInterface } from 'readline';
|
|
12
14
|
import { execFile, spawn } from 'child_process';
|
|
13
15
|
import { promisify } from 'util';
|
|
14
16
|
import { EventEmitter } from 'events';
|
|
@@ -59,8 +61,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
59
61
|
}
|
|
60
62
|
let mode;
|
|
61
63
|
try {
|
|
62
|
-
const
|
|
63
|
-
mode =
|
|
64
|
+
const stat16 = await fsp3.stat(targetPath);
|
|
65
|
+
mode = stat16.mode & 511;
|
|
64
66
|
} catch {
|
|
65
67
|
mode = opts.mode;
|
|
66
68
|
}
|
|
@@ -100,8 +102,8 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
100
102
|
}
|
|
101
103
|
if (code !== "EEXIST") throw err;
|
|
102
104
|
try {
|
|
103
|
-
const
|
|
104
|
-
if (Date.now() -
|
|
105
|
+
const stat16 = await fsp3.stat(lockPath);
|
|
106
|
+
if (Date.now() - stat16.mtimeMs > staleMs) {
|
|
105
107
|
await fsp3.unlink(lockPath);
|
|
106
108
|
continue;
|
|
107
109
|
}
|
|
@@ -111,7 +113,7 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
111
113
|
if (Date.now() - started >= timeoutMs) {
|
|
112
114
|
throw new Error(`Timed out waiting for file lock: ${targetPath}`);
|
|
113
115
|
}
|
|
114
|
-
await new Promise((
|
|
116
|
+
await new Promise((resolve19) => setTimeout(resolve19, 25));
|
|
115
117
|
}
|
|
116
118
|
}
|
|
117
119
|
try {
|
|
@@ -144,7 +146,7 @@ async function renameWithRetry(from, to) {
|
|
|
144
146
|
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
145
147
|
throw err;
|
|
146
148
|
}
|
|
147
|
-
await new Promise((
|
|
149
|
+
await new Promise((resolve19) => setTimeout(resolve19, delays[i]));
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
throw lastErr;
|
|
@@ -430,11 +432,11 @@ var init_session_registry = __esm({
|
|
|
430
432
|
*/
|
|
431
433
|
async breakStaleLock(lockPath) {
|
|
432
434
|
try {
|
|
433
|
-
const [
|
|
435
|
+
const [stat16, content] = await Promise.all([
|
|
434
436
|
fsp3.stat(lockPath),
|
|
435
437
|
fsp3.readFile(lockPath, "utf8").catch(() => "")
|
|
436
438
|
]);
|
|
437
|
-
const ageMs = Date.now() -
|
|
439
|
+
const ageMs = Date.now() - stat16.mtimeMs;
|
|
438
440
|
const ownerPid = Number.parseInt(content.trim(), 10);
|
|
439
441
|
const ownerDead = Number.isInteger(ownerPid) && ownerPid > 0 && ownerPid !== process.pid && !pidAlive(ownerPid);
|
|
440
442
|
if (ownerDead || ageMs > STALE_LOCK_MS) {
|
|
@@ -477,9 +479,9 @@ var init_session_registry = __esm({
|
|
|
477
479
|
for (const name of await fsp3.readdir(dir)) {
|
|
478
480
|
const isTemp = (name.startsWith(`${base}.`) || name.startsWith(`.${base}.`)) && name.endsWith(".tmp");
|
|
479
481
|
if (!isTemp) continue;
|
|
480
|
-
const
|
|
481
|
-
if (!
|
|
482
|
-
if (now -
|
|
482
|
+
const stat16 = await fsp3.stat(path3.join(dir, name)).catch(() => null);
|
|
483
|
+
if (!stat16) continue;
|
|
484
|
+
if (now - stat16.mtimeMs > STALE_TMP_MS) stale.push({ name, mtimeMs: stat16.mtimeMs });
|
|
483
485
|
}
|
|
484
486
|
stale.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
485
487
|
await Promise.all(
|
|
@@ -562,6 +564,25 @@ function looksSecret(name) {
|
|
|
562
564
|
}
|
|
563
565
|
return false;
|
|
564
566
|
}
|
|
567
|
+
function valueHasEmbeddedCredential(value) {
|
|
568
|
+
return /\b[a-z][a-z0-9+.-]*:\/\/[^/\s:@]*:[^/\s@]+@/i.test(value);
|
|
569
|
+
}
|
|
570
|
+
var NODE_OPTIONS_INJECTION_FLAG = /^(?:--require|-r|--import|--loader|--experimental-loader)$/;
|
|
571
|
+
var NODE_OPTIONS_INJECTION_FLAG_EQ = /^(?:--require|-r|--import|--loader|--experimental-loader)=/;
|
|
572
|
+
function sanitizeNodeOptions(value) {
|
|
573
|
+
const tokens = value.split(/\s+/).filter(Boolean);
|
|
574
|
+
const kept = [];
|
|
575
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
576
|
+
const tok = tokens[i];
|
|
577
|
+
if (NODE_OPTIONS_INJECTION_FLAG_EQ.test(tok)) continue;
|
|
578
|
+
if (NODE_OPTIONS_INJECTION_FLAG.test(tok)) {
|
|
579
|
+
i++;
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
kept.push(tok);
|
|
583
|
+
}
|
|
584
|
+
return kept.join(" ");
|
|
585
|
+
}
|
|
565
586
|
function buildChildEnv(optsOrSessionId) {
|
|
566
587
|
const opts = typeof optsOrSessionId === "string" ? { sessionId: optsOrSessionId } : optsOrSessionId ?? {};
|
|
567
588
|
const hasOwn = Object.hasOwn(process.env, "WRONGSTACK_CHILD_ENV_PASSTHROUGH");
|
|
@@ -582,11 +603,17 @@ function buildChildEnv(optsOrSessionId) {
|
|
|
582
603
|
continue;
|
|
583
604
|
}
|
|
584
605
|
const upper = k.toUpperCase();
|
|
606
|
+
if (valueHasEmbeddedCredential(v)) continue;
|
|
585
607
|
if (ALLOWED_KEYS.has(upper)) {
|
|
586
608
|
out[k] = v;
|
|
587
609
|
continue;
|
|
588
610
|
}
|
|
589
611
|
if (looksSecret(upper)) continue;
|
|
612
|
+
if (upper === "NODE_OPTIONS") {
|
|
613
|
+
const sanitized = sanitizeNodeOptions(v);
|
|
614
|
+
if (sanitized) out[k] = sanitized;
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
590
617
|
if (upper.startsWith("NODE_") || upper.startsWith("NPM_") || upper.startsWith("PNPM_") || upper.startsWith("YARN_") || upper.startsWith("GIT_") || upper.startsWith("CI") || upper.startsWith("XDG_") || // Our own non-secret knobs (WRONGSTACK_HOME, WRONGSTACK_SESSION_ID, …).
|
|
591
618
|
// Secrets never live in WRONGSTACK_* env vars (they're in the encrypted
|
|
592
619
|
// vault). Forwarding keeps child wstack processes — e.g. ones spawned
|
|
@@ -676,7 +703,7 @@ function envFlag(value) {
|
|
|
676
703
|
return !/^(0|false|no|off)$/i.test(value.trim());
|
|
677
704
|
}
|
|
678
705
|
var COLOR = isColorTty();
|
|
679
|
-
var wrap = (
|
|
706
|
+
var wrap = (open10, close) => (s) => COLOR ? `\x1B[${open10}m${s}\x1B[${close}m` : s;
|
|
680
707
|
var color = {
|
|
681
708
|
reset: wrap("0", "0"),
|
|
682
709
|
bold: wrap("1", "22"),
|
|
@@ -727,9 +754,9 @@ async function updateJsonObjectFile(filePath, mutator) {
|
|
|
727
754
|
await writeJsonObjectFile(filePath, next);
|
|
728
755
|
return next;
|
|
729
756
|
}
|
|
730
|
-
function getJsonPath(root,
|
|
757
|
+
function getJsonPath(root, path51) {
|
|
731
758
|
let current = root;
|
|
732
|
-
for (const segment of
|
|
759
|
+
for (const segment of path51) {
|
|
733
760
|
if (typeof segment === "number") {
|
|
734
761
|
if (!Array.isArray(current)) return void 0;
|
|
735
762
|
current = current[segment];
|
|
@@ -740,13 +767,13 @@ function getJsonPath(root, path48) {
|
|
|
740
767
|
}
|
|
741
768
|
return current;
|
|
742
769
|
}
|
|
743
|
-
function setJsonPath(root,
|
|
744
|
-
if (
|
|
770
|
+
function setJsonPath(root, path51, value) {
|
|
771
|
+
if (path51.length === 0) {
|
|
745
772
|
if (!isJsonObject(value)) throw new Error("Root config value must be an object");
|
|
746
773
|
return value;
|
|
747
774
|
}
|
|
748
|
-
const parent = ensureJsonParent(root,
|
|
749
|
-
const leaf = lastPathSegment(
|
|
775
|
+
const parent = ensureJsonParent(root, path51);
|
|
776
|
+
const leaf = lastPathSegment(path51);
|
|
750
777
|
if (typeof leaf === "number") {
|
|
751
778
|
if (!Array.isArray(parent)) throw new Error(`Cannot set numeric segment ${leaf} on non-array parent`);
|
|
752
779
|
parent[leaf] = value;
|
|
@@ -756,10 +783,10 @@ function setJsonPath(root, path48, value) {
|
|
|
756
783
|
}
|
|
757
784
|
return root;
|
|
758
785
|
}
|
|
759
|
-
function removeJsonPath(root,
|
|
760
|
-
if (
|
|
761
|
-
const parent = getJsonPath(root,
|
|
762
|
-
const leaf = lastPathSegment(
|
|
786
|
+
function removeJsonPath(root, path51) {
|
|
787
|
+
if (path51.length === 0) return false;
|
|
788
|
+
const parent = getJsonPath(root, path51.slice(0, -1));
|
|
789
|
+
const leaf = lastPathSegment(path51);
|
|
763
790
|
if (typeof leaf === "number") {
|
|
764
791
|
if (!Array.isArray(parent) || leaf < 0 || leaf >= parent.length) return false;
|
|
765
792
|
parent.splice(leaf, 1);
|
|
@@ -769,27 +796,27 @@ function removeJsonPath(root, path48) {
|
|
|
769
796
|
delete parent[leaf];
|
|
770
797
|
return true;
|
|
771
798
|
}
|
|
772
|
-
async function setJsonPathInFile(filePath,
|
|
773
|
-
return updateJsonObjectFile(filePath, (config) => setJsonPath(config,
|
|
799
|
+
async function setJsonPathInFile(filePath, path51, value) {
|
|
800
|
+
return updateJsonObjectFile(filePath, (config) => setJsonPath(config, path51, value));
|
|
774
801
|
}
|
|
775
|
-
async function removeJsonPathInFile(filePath,
|
|
802
|
+
async function removeJsonPathInFile(filePath, path51) {
|
|
776
803
|
return updateJsonObjectFile(filePath, (config) => {
|
|
777
|
-
removeJsonPath(config,
|
|
804
|
+
removeJsonPath(config, path51);
|
|
778
805
|
});
|
|
779
806
|
}
|
|
780
807
|
function isJsonObject(value) {
|
|
781
808
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
782
809
|
}
|
|
783
|
-
function lastPathSegment(
|
|
784
|
-
const segment =
|
|
810
|
+
function lastPathSegment(path51) {
|
|
811
|
+
const segment = path51[path51.length - 1];
|
|
785
812
|
if (segment === void 0) throw new Error("Invalid empty JSON path");
|
|
786
813
|
return segment;
|
|
787
814
|
}
|
|
788
|
-
function ensureJsonParent(root,
|
|
815
|
+
function ensureJsonParent(root, path51) {
|
|
789
816
|
let current = root;
|
|
790
|
-
for (let i = 0; i <
|
|
791
|
-
const segment =
|
|
792
|
-
const nextSegment =
|
|
817
|
+
for (let i = 0; i < path51.length - 1; i += 1) {
|
|
818
|
+
const segment = path51[i];
|
|
819
|
+
const nextSegment = path51[i + 1];
|
|
793
820
|
if (segment === void 0) throw new Error("Invalid empty JSON path segment");
|
|
794
821
|
const nextContainer = typeof nextSegment === "number" ? [] : {};
|
|
795
822
|
if (typeof segment === "number") {
|
|
@@ -808,6 +835,9 @@ var MAX_TOOL_CALLS = 80;
|
|
|
808
835
|
var MAX_FACTS = 40;
|
|
809
836
|
var MAX_ERRORS = 20;
|
|
810
837
|
var MAX_DIGEST_CHARS = 4e3;
|
|
838
|
+
var RECENT_TOOL_CALL_SCAN_LIMIT = 20;
|
|
839
|
+
var EXTRACT_CONTENT_CAP_CHARS = 1e4;
|
|
840
|
+
var EXTRACT_ERROR_TAIL_LINES = 200;
|
|
811
841
|
var WRITE_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "replace", "patch"]);
|
|
812
842
|
var READ_TOOLS = /* @__PURE__ */ new Set(["read", "grep", "glob", "ls", "tree"]);
|
|
813
843
|
function createContextEvidenceState() {
|
|
@@ -833,8 +863,9 @@ function recordUserIntentEvidence(ctx, text) {
|
|
|
833
863
|
}
|
|
834
864
|
function recordToolOutputEvidence(ctx, input) {
|
|
835
865
|
const state = ensureEvidence(ctx);
|
|
836
|
-
const
|
|
837
|
-
const
|
|
866
|
+
const scanContent = input.content.length > EXTRACT_CONTENT_CAP_CHARS ? input.content.slice(0, EXTRACT_CONTENT_CAP_CHARS) : input.content;
|
|
867
|
+
const files = extractFiles(ctx, input.toolName, input.input, scanContent);
|
|
868
|
+
const symbols = extractSymbols(scanContent, input.input);
|
|
838
869
|
const commands = extractCommands(input.toolName, input.input);
|
|
839
870
|
const errors = extractErrors(input.content);
|
|
840
871
|
const summary = summarizeToolOutput(input.toolName, input.input, input.content, {
|
|
@@ -877,7 +908,8 @@ function markAssistantReferencedEvidence(ctx, text) {
|
|
|
877
908
|
const state = ensureEvidence(ctx);
|
|
878
909
|
const haystack = text.toLowerCase();
|
|
879
910
|
if (!haystack.trim()) return;
|
|
880
|
-
|
|
911
|
+
const recent = state.toolCalls.length > RECENT_TOOL_CALL_SCAN_LIMIT ? state.toolCalls.slice(-RECENT_TOOL_CALL_SCAN_LIMIT) : state.toolCalls;
|
|
912
|
+
for (const tool of recent) {
|
|
881
913
|
if (!metadataReferencedByText(tool, haystack)) continue;
|
|
882
914
|
tool.status = "referenced";
|
|
883
915
|
tool.referenceCount++;
|
|
@@ -1032,7 +1064,8 @@ function extractCommands(toolName, input) {
|
|
|
1032
1064
|
return [command.slice(0, 220)];
|
|
1033
1065
|
}
|
|
1034
1066
|
function extractErrors(content) {
|
|
1035
|
-
const
|
|
1067
|
+
const allLines = content.split(/\r?\n/);
|
|
1068
|
+
const lines = allLines.length > EXTRACT_ERROR_TAIL_LINES ? allLines.slice(-EXTRACT_ERROR_TAIL_LINES) : allLines;
|
|
1036
1069
|
const errors = [];
|
|
1037
1070
|
for (const line of lines) {
|
|
1038
1071
|
if (!/\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm|typeerror|syntaxerror)\b/i.test(line)) continue;
|
|
@@ -1441,8 +1474,8 @@ async function expandGlob(pattern) {
|
|
|
1441
1474
|
for (const e of entries) {
|
|
1442
1475
|
const full = `${dir}${SEP}${e}`;
|
|
1443
1476
|
try {
|
|
1444
|
-
const
|
|
1445
|
-
if (
|
|
1477
|
+
const stat16 = await fsp3.stat(full);
|
|
1478
|
+
if (stat16.isDirectory()) await walk3(full, rest);
|
|
1446
1479
|
} catch {
|
|
1447
1480
|
}
|
|
1448
1481
|
}
|
|
@@ -1459,8 +1492,8 @@ async function expandGlob(pattern) {
|
|
|
1459
1492
|
if (entries.includes(seg)) {
|
|
1460
1493
|
const full = `${dir}${SEP}${seg}`;
|
|
1461
1494
|
try {
|
|
1462
|
-
const
|
|
1463
|
-
if (
|
|
1495
|
+
const stat16 = await fsp3.stat(full);
|
|
1496
|
+
if (stat16.isDirectory()) await walk3(full, rest);
|
|
1464
1497
|
} catch {
|
|
1465
1498
|
}
|
|
1466
1499
|
}
|
|
@@ -1476,6 +1509,7 @@ function escapeRegex(s) {
|
|
|
1476
1509
|
}
|
|
1477
1510
|
var COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
|
|
1478
1511
|
var CACHE_MAX_SIZE = 2e3;
|
|
1512
|
+
var NEVER_MATCH = /[^\s\S]/;
|
|
1479
1513
|
function getCachedGlob(pattern) {
|
|
1480
1514
|
const cached = COMPILED_GLOB_CACHE.get(pattern);
|
|
1481
1515
|
if (cached) return cached;
|
|
@@ -1485,7 +1519,12 @@ function getCachedGlob(pattern) {
|
|
|
1485
1519
|
COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
|
|
1486
1520
|
}
|
|
1487
1521
|
}
|
|
1488
|
-
|
|
1522
|
+
let re;
|
|
1523
|
+
try {
|
|
1524
|
+
re = compileGlob(pattern);
|
|
1525
|
+
} catch {
|
|
1526
|
+
re = NEVER_MATCH;
|
|
1527
|
+
}
|
|
1489
1528
|
COMPILED_GLOB_CACHE.set(pattern, re);
|
|
1490
1529
|
return re;
|
|
1491
1530
|
}
|
|
@@ -1733,11 +1772,11 @@ function validateAgainstSchema(value, schema) {
|
|
|
1733
1772
|
walk(value, schema, "", errors);
|
|
1734
1773
|
return { ok: errors.length === 0, errors };
|
|
1735
1774
|
}
|
|
1736
|
-
function walk(value, schema,
|
|
1775
|
+
function walk(value, schema, path51, errors) {
|
|
1737
1776
|
if (schema.enum !== void 0) {
|
|
1738
1777
|
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
1739
1778
|
errors.push({
|
|
1740
|
-
path:
|
|
1779
|
+
path: path51 || "<root>",
|
|
1741
1780
|
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
1742
1781
|
});
|
|
1743
1782
|
return;
|
|
@@ -1746,7 +1785,7 @@ function walk(value, schema, path48, errors) {
|
|
|
1746
1785
|
if (typeof schema.type === "string") {
|
|
1747
1786
|
if (!checkType(value, schema.type)) {
|
|
1748
1787
|
errors.push({
|
|
1749
|
-
path:
|
|
1788
|
+
path: path51 || "<root>",
|
|
1750
1789
|
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
1751
1790
|
});
|
|
1752
1791
|
return;
|
|
@@ -1756,20 +1795,20 @@ function walk(value, schema, path48, errors) {
|
|
|
1756
1795
|
const obj = value;
|
|
1757
1796
|
for (const req of schema.required ?? []) {
|
|
1758
1797
|
if (!(req in obj)) {
|
|
1759
|
-
errors.push({ path: joinPath(
|
|
1798
|
+
errors.push({ path: joinPath(path51, req), message: "required property missing" });
|
|
1760
1799
|
}
|
|
1761
1800
|
}
|
|
1762
1801
|
if (schema.properties) {
|
|
1763
1802
|
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
1764
1803
|
if (key in obj) {
|
|
1765
|
-
walk(obj[key], subSchema, joinPath(
|
|
1804
|
+
walk(obj[key], subSchema, joinPath(path51, key), errors);
|
|
1766
1805
|
}
|
|
1767
1806
|
}
|
|
1768
1807
|
}
|
|
1769
1808
|
}
|
|
1770
1809
|
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
1771
1810
|
for (let i = 0; i < value.length; i++) {
|
|
1772
|
-
walk(value[i], schema.items, `${
|
|
1811
|
+
walk(value[i], schema.items, `${path51}[${i}]`, errors);
|
|
1773
1812
|
}
|
|
1774
1813
|
}
|
|
1775
1814
|
}
|
|
@@ -2168,7 +2207,7 @@ function stripSingleLineComments(s) {
|
|
|
2168
2207
|
|
|
2169
2208
|
// src/utils/sleep.ts
|
|
2170
2209
|
function sleep(ms) {
|
|
2171
|
-
return new Promise((
|
|
2210
|
+
return new Promise((resolve19) => setTimeout(resolve19, ms));
|
|
2172
2211
|
}
|
|
2173
2212
|
|
|
2174
2213
|
// src/utils/string.ts
|
|
@@ -2431,18 +2470,20 @@ var MODEL_FAMILY_RATIO = {
|
|
|
2431
2470
|
deepseek: 3.5
|
|
2432
2471
|
};
|
|
2433
2472
|
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
2473
|
+
var _estimateCacheOrder = [];
|
|
2434
2474
|
var ESTIMATE_CACHE_MAX_SIZE = 5e4;
|
|
2435
2475
|
function getCachedEstimate(key, compute) {
|
|
2436
2476
|
const existing = ESTIMATE_CACHE.get(key);
|
|
2437
2477
|
if (existing !== void 0) return existing;
|
|
2438
2478
|
if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
ESTIMATE_CACHE.delete(
|
|
2479
|
+
while (ESTIMATE_CACHE.size > Math.floor(ESTIMATE_CACHE_MAX_SIZE / 2)) {
|
|
2480
|
+
const oldest = _estimateCacheOrder.shift();
|
|
2481
|
+
if (oldest !== void 0) ESTIMATE_CACHE.delete(oldest);
|
|
2442
2482
|
}
|
|
2443
2483
|
}
|
|
2444
2484
|
const estimate = compute(key);
|
|
2445
2485
|
ESTIMATE_CACHE.set(key, estimate);
|
|
2486
|
+
_estimateCacheOrder.push(key);
|
|
2446
2487
|
return estimate;
|
|
2447
2488
|
}
|
|
2448
2489
|
function estimateToolInputTokens(input) {
|
|
@@ -4463,11 +4504,88 @@ var ALGO = "aes-256-gcm";
|
|
|
4463
4504
|
var KEY_FILE_MODE = 384;
|
|
4464
4505
|
var KEY_FILE_MAGIC = Buffer.from("WSKV", "ascii");
|
|
4465
4506
|
var VERSIONED_KEY_FILE_SIZE = KEY_FILE_MAGIC.length + 1 + KEY_BYTES;
|
|
4507
|
+
var KEK_MAGIC = Buffer.from("WSKW", "ascii");
|
|
4508
|
+
var KEK_SALT_BYTES = 16;
|
|
4509
|
+
var WRAPPED_KEY_FILE_SIZE = KEK_MAGIC.length + 1 + KEK_SALT_BYTES + IV_BYTES + TAG_BYTES + KEY_BYTES;
|
|
4510
|
+
var SCRYPT_N = 1 << 15;
|
|
4511
|
+
var SCRYPT_R = 8;
|
|
4512
|
+
var SCRYPT_P = 1;
|
|
4513
|
+
var SCRYPT_MAXMEM = 64 * 1024 * 1024;
|
|
4514
|
+
function getVaultPassphrase() {
|
|
4515
|
+
const v = process.env["WRONGSTACK_VAULT_PASSPHRASE"];
|
|
4516
|
+
return v && v.length > 0 ? v : void 0;
|
|
4517
|
+
}
|
|
4518
|
+
function isWrappedKeyFile(buf) {
|
|
4519
|
+
return buf.length === WRAPPED_KEY_FILE_SIZE && buf.subarray(0, KEK_MAGIC.length).equals(KEK_MAGIC);
|
|
4520
|
+
}
|
|
4521
|
+
function deriveKEK(passphrase, salt) {
|
|
4522
|
+
return scryptSync(passphrase, salt, KEY_BYTES, {
|
|
4523
|
+
N: SCRYPT_N,
|
|
4524
|
+
r: SCRYPT_R,
|
|
4525
|
+
p: SCRYPT_P,
|
|
4526
|
+
maxmem: SCRYPT_MAXMEM
|
|
4527
|
+
});
|
|
4528
|
+
}
|
|
4529
|
+
function wrapDataKey(dataKey, keyVersion, passphrase) {
|
|
4530
|
+
const salt = randomBytes(KEK_SALT_BYTES);
|
|
4531
|
+
const iv = randomBytes(IV_BYTES);
|
|
4532
|
+
const kek = deriveKEK(passphrase, salt);
|
|
4533
|
+
const cipher = createCipheriv(ALGO, kek, iv);
|
|
4534
|
+
const ct = Buffer.concat([cipher.update(dataKey), cipher.final()]);
|
|
4535
|
+
const tag = cipher.getAuthTag();
|
|
4536
|
+
const out = Buffer.alloc(WRAPPED_KEY_FILE_SIZE);
|
|
4537
|
+
let off = 0;
|
|
4538
|
+
KEK_MAGIC.copy(out, off);
|
|
4539
|
+
off += KEK_MAGIC.length;
|
|
4540
|
+
out[off] = keyVersion & 255;
|
|
4541
|
+
off += 1;
|
|
4542
|
+
salt.copy(out, off);
|
|
4543
|
+
off += KEK_SALT_BYTES;
|
|
4544
|
+
iv.copy(out, off);
|
|
4545
|
+
off += IV_BYTES;
|
|
4546
|
+
tag.copy(out, off);
|
|
4547
|
+
off += TAG_BYTES;
|
|
4548
|
+
ct.copy(out, off);
|
|
4549
|
+
return out;
|
|
4550
|
+
}
|
|
4551
|
+
function unwrapDataKey(buf, keyFile) {
|
|
4552
|
+
const passphrase = getVaultPassphrase();
|
|
4553
|
+
if (!passphrase) {
|
|
4554
|
+
throw new ConfigError({
|
|
4555
|
+
message: `SecretVault: key file ${keyFile} is passphrase-protected \u2014 set the WRONGSTACK_VAULT_PASSPHRASE environment variable to unlock it.`,
|
|
4556
|
+
code: ERROR_CODES.CONFIG_INVALID,
|
|
4557
|
+
context: { keyFile }
|
|
4558
|
+
});
|
|
4559
|
+
}
|
|
4560
|
+
let off = KEK_MAGIC.length;
|
|
4561
|
+
const version = buf[off];
|
|
4562
|
+
off += 1;
|
|
4563
|
+
const salt = buf.subarray(off, off + KEK_SALT_BYTES);
|
|
4564
|
+
off += KEK_SALT_BYTES;
|
|
4565
|
+
const iv = buf.subarray(off, off + IV_BYTES);
|
|
4566
|
+
off += IV_BYTES;
|
|
4567
|
+
const tag = buf.subarray(off, off + TAG_BYTES);
|
|
4568
|
+
off += TAG_BYTES;
|
|
4569
|
+
const ct = buf.subarray(off, off + KEY_BYTES);
|
|
4570
|
+
const kek = deriveKEK(passphrase, salt);
|
|
4571
|
+
const decipher = createDecipheriv(ALGO, kek, iv);
|
|
4572
|
+
decipher.setAuthTag(tag);
|
|
4573
|
+
try {
|
|
4574
|
+
const key = Buffer.concat([decipher.update(ct), decipher.final()]);
|
|
4575
|
+
return { key: Buffer.from(key), version };
|
|
4576
|
+
} catch {
|
|
4577
|
+
throw new ConfigError({
|
|
4578
|
+
message: `SecretVault: failed to unlock key file ${keyFile} \u2014 wrong WRONGSTACK_VAULT_PASSPHRASE (key unwrap authentication failed).`,
|
|
4579
|
+
code: ERROR_CODES.CONFIG_INVALID,
|
|
4580
|
+
context: { keyFile }
|
|
4581
|
+
});
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4466
4584
|
function checkKeyFilePermissions(keyFile) {
|
|
4467
4585
|
if (process.platform === "win32") return;
|
|
4468
4586
|
try {
|
|
4469
|
-
const
|
|
4470
|
-
const actualMode =
|
|
4587
|
+
const stat16 = fs3.statSync(keyFile);
|
|
4588
|
+
const actualMode = stat16.mode & 511;
|
|
4471
4589
|
if (actualMode !== KEY_FILE_MODE) {
|
|
4472
4590
|
console.warn(JSON.stringify({
|
|
4473
4591
|
level: "warn",
|
|
@@ -4555,25 +4673,56 @@ var DefaultSecretVault = class {
|
|
|
4555
4673
|
const oldVersion = this._keyVersion;
|
|
4556
4674
|
const newKey = randomBytes(KEY_BYTES);
|
|
4557
4675
|
const newVersion = oldVersion + 1;
|
|
4558
|
-
const keyFileBuf = Buffer.alloc(VERSIONED_KEY_FILE_SIZE);
|
|
4559
|
-
KEY_FILE_MAGIC.copy(keyFileBuf, 0);
|
|
4560
|
-
keyFileBuf[KEY_FILE_MAGIC.length] = newVersion;
|
|
4561
|
-
newKey.copy(keyFileBuf, KEY_FILE_MAGIC.length + 1);
|
|
4562
4676
|
fs3.mkdirSync(path3.dirname(this.keyFile), { recursive: true });
|
|
4563
|
-
|
|
4677
|
+
const passphrase = getVaultPassphrase();
|
|
4678
|
+
if (passphrase) {
|
|
4679
|
+
fs3.writeFileSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase), { mode: 384 });
|
|
4680
|
+
} else {
|
|
4681
|
+
const keyFileBuf = Buffer.alloc(VERSIONED_KEY_FILE_SIZE);
|
|
4682
|
+
KEY_FILE_MAGIC.copy(keyFileBuf, 0);
|
|
4683
|
+
keyFileBuf[KEY_FILE_MAGIC.length] = newVersion;
|
|
4684
|
+
newKey.copy(keyFileBuf, KEY_FILE_MAGIC.length + 1);
|
|
4685
|
+
fs3.writeFileSync(this.keyFile, keyFileBuf, { mode: 384 });
|
|
4686
|
+
}
|
|
4564
4687
|
checkKeyFilePermissions(this.keyFile);
|
|
4565
4688
|
this.key = newKey;
|
|
4566
4689
|
this._keyVersion = newVersion;
|
|
4567
4690
|
return { oldVersion, newVersion };
|
|
4568
4691
|
}
|
|
4692
|
+
/**
|
|
4693
|
+
* If WRONGSTACK_VAULT_PASSPHRASE is set but the key on disk is still stored
|
|
4694
|
+
* unwrapped (legacy v1 / versioned v2), re-write it in passphrase-wrapped (v3)
|
|
4695
|
+
* form. The data key is preserved, so all existing ciphertext keeps
|
|
4696
|
+
* decrypting. Best-effort: a write failure leaves the working unwrapped file
|
|
4697
|
+
* in place and is not fatal to load.
|
|
4698
|
+
*/
|
|
4699
|
+
migrateToWrappedIfPassphrase() {
|
|
4700
|
+
const passphrase = getVaultPassphrase();
|
|
4701
|
+
if (!passphrase || !this.key) return;
|
|
4702
|
+
try {
|
|
4703
|
+
fs3.writeFileSync(this.keyFile, wrapDataKey(this.key, this._keyVersion, passphrase), {
|
|
4704
|
+
mode: 384
|
|
4705
|
+
});
|
|
4706
|
+
checkKeyFilePermissions(this.keyFile);
|
|
4707
|
+
} catch {
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4569
4710
|
loadOrCreateKey() {
|
|
4570
4711
|
if (this.key) return this.key;
|
|
4571
4712
|
try {
|
|
4572
4713
|
const buf = fs3.readFileSync(this.keyFile);
|
|
4714
|
+
if (isWrappedKeyFile(buf)) {
|
|
4715
|
+
const { key: key2, version } = unwrapDataKey(buf, this.keyFile);
|
|
4716
|
+
this.key = key2;
|
|
4717
|
+
this._keyVersion = version;
|
|
4718
|
+
checkKeyFilePermissions(this.keyFile);
|
|
4719
|
+
return this.key;
|
|
4720
|
+
}
|
|
4573
4721
|
if (buf.length === KEY_BYTES) {
|
|
4574
4722
|
this.key = buf;
|
|
4575
4723
|
this._keyVersion = 1;
|
|
4576
4724
|
checkKeyFilePermissions(this.keyFile);
|
|
4725
|
+
this.migrateToWrappedIfPassphrase();
|
|
4577
4726
|
return this.key;
|
|
4578
4727
|
}
|
|
4579
4728
|
if (buf.length === VERSIONED_KEY_FILE_SIZE) {
|
|
@@ -4597,6 +4746,7 @@ var DefaultSecretVault = class {
|
|
|
4597
4746
|
this.key = Buffer.from(key2);
|
|
4598
4747
|
this._keyVersion = version;
|
|
4599
4748
|
checkKeyFilePermissions(this.keyFile);
|
|
4749
|
+
this.migrateToWrappedIfPassphrase();
|
|
4600
4750
|
return this.key;
|
|
4601
4751
|
}
|
|
4602
4752
|
throw new ConfigError({
|
|
@@ -4609,11 +4759,20 @@ var DefaultSecretVault = class {
|
|
|
4609
4759
|
}
|
|
4610
4760
|
fs3.mkdirSync(path3.dirname(this.keyFile), { recursive: true });
|
|
4611
4761
|
const key = randomBytes(KEY_BYTES);
|
|
4762
|
+
const passphrase = getVaultPassphrase();
|
|
4763
|
+
const initialBytes = passphrase ? wrapDataKey(key, 1, passphrase) : key;
|
|
4612
4764
|
try {
|
|
4613
|
-
fs3.writeFileSync(this.keyFile,
|
|
4765
|
+
fs3.writeFileSync(this.keyFile, initialBytes, { mode: 384, flag: "wx" });
|
|
4614
4766
|
} catch (err) {
|
|
4615
4767
|
if (err.code !== "EEXIST") throw err;
|
|
4616
4768
|
const buf = fs3.readFileSync(this.keyFile);
|
|
4769
|
+
if (isWrappedKeyFile(buf)) {
|
|
4770
|
+
const { key: winnerKey, version } = unwrapDataKey(buf, this.keyFile);
|
|
4771
|
+
this.key = winnerKey;
|
|
4772
|
+
this._keyVersion = version;
|
|
4773
|
+
checkKeyFilePermissions(this.keyFile);
|
|
4774
|
+
return this.key;
|
|
4775
|
+
}
|
|
4617
4776
|
if (buf.length === KEY_BYTES) {
|
|
4618
4777
|
this.key = buf;
|
|
4619
4778
|
this._keyVersion = 1;
|
|
@@ -5217,17 +5376,9 @@ function findPreserveStart(messages, preserveK) {
|
|
|
5217
5376
|
const prev = messages[preserveStart - 1];
|
|
5218
5377
|
if (!first || !prev || first.role !== "user" || prev.role !== "assistant") break;
|
|
5219
5378
|
if (typeof first.content === "string" || typeof prev.content === "string") break;
|
|
5220
|
-
const
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
if (block.type === "tool_result") resultIds.add(block.tool_use_id);
|
|
5224
|
-
}
|
|
5225
|
-
if (resultIds.size === 0) break;
|
|
5226
|
-
const hasMatchingUse = prev.content.some((block) => {
|
|
5227
|
-
pairRepairInnerIterations++;
|
|
5228
|
-
return block.type === "tool_use" && resultIds.has(block.id);
|
|
5229
|
-
});
|
|
5230
|
-
if (!hasMatchingUse) break;
|
|
5379
|
+
const pairCheck = hasMatchingToolPair(first.content, prev.content);
|
|
5380
|
+
pairRepairInnerIterations += pairCheck.iterations;
|
|
5381
|
+
if (!pairCheck.matched) break;
|
|
5231
5382
|
preserveStart--;
|
|
5232
5383
|
}
|
|
5233
5384
|
if (compactionDebugEnabled()) {
|
|
@@ -5246,9 +5397,34 @@ function findPreserveStart(messages, preserveK) {
|
|
|
5246
5397
|
}
|
|
5247
5398
|
return preserveStart;
|
|
5248
5399
|
}
|
|
5400
|
+
function hasMatchingToolPair(resultContent, useContent) {
|
|
5401
|
+
let iterations = 0;
|
|
5402
|
+
let firstResultId;
|
|
5403
|
+
let resultIds;
|
|
5404
|
+
for (const block of resultContent) {
|
|
5405
|
+
iterations++;
|
|
5406
|
+
if (block.type !== "tool_result") continue;
|
|
5407
|
+
if (firstResultId === void 0) {
|
|
5408
|
+
firstResultId = block.tool_use_id;
|
|
5409
|
+
} else {
|
|
5410
|
+
resultIds ??= /* @__PURE__ */ new Set([firstResultId]);
|
|
5411
|
+
resultIds.add(block.tool_use_id);
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
if (firstResultId === void 0) return { matched: false, iterations };
|
|
5415
|
+
for (const block of useContent) {
|
|
5416
|
+
iterations++;
|
|
5417
|
+
if (block.type !== "tool_use") continue;
|
|
5418
|
+
if (resultIds ? resultIds.has(block.id) : block.id === firstResultId) {
|
|
5419
|
+
return { matched: true, iterations };
|
|
5420
|
+
}
|
|
5421
|
+
}
|
|
5422
|
+
return { matched: false, iterations };
|
|
5423
|
+
}
|
|
5249
5424
|
function eliseOldToolResults(messages, opts) {
|
|
5250
5425
|
const preserveStart = findPreserveStart(messages, opts.preserveK);
|
|
5251
5426
|
let hasOversized = false;
|
|
5427
|
+
let firstOversizedIndex = -1;
|
|
5252
5428
|
let fastPathIterations = 0;
|
|
5253
5429
|
let fastPathInnerIterations = 0;
|
|
5254
5430
|
for (let i = 0; i < preserveStart && !hasOversized; i++) {
|
|
@@ -5260,6 +5436,7 @@ function eliseOldToolResults(messages, opts) {
|
|
|
5260
5436
|
const oversized = b.type === "tool_result" && estimateToolResultTokens(b.content) >= opts.eliseThreshold || b.type === "tool_use" && estimateToolInputTokens(b.input) >= opts.eliseThreshold;
|
|
5261
5437
|
if (oversized) {
|
|
5262
5438
|
hasOversized = true;
|
|
5439
|
+
firstOversizedIndex = i;
|
|
5263
5440
|
break;
|
|
5264
5441
|
}
|
|
5265
5442
|
}
|
|
@@ -5282,26 +5459,29 @@ function eliseOldToolResults(messages, opts) {
|
|
|
5282
5459
|
let changed = false;
|
|
5283
5460
|
let fullPassIterations = 0;
|
|
5284
5461
|
let fullPassInnerIterations = 0;
|
|
5285
|
-
|
|
5286
|
-
for (let i =
|
|
5462
|
+
let next;
|
|
5463
|
+
for (let i = firstOversizedIndex; i < preserveStart; i++) {
|
|
5287
5464
|
fullPassIterations++;
|
|
5288
5465
|
const msg = messages[i];
|
|
5289
|
-
if (
|
|
5290
|
-
next[i] = msg;
|
|
5291
|
-
continue;
|
|
5292
|
-
}
|
|
5466
|
+
if (!msg || !Array.isArray(msg.content)) continue;
|
|
5293
5467
|
const original = msg.content;
|
|
5294
|
-
|
|
5468
|
+
let newContent;
|
|
5469
|
+
for (let idx = 0; idx < original.length; idx++) {
|
|
5470
|
+
fullPassInnerIterations++;
|
|
5471
|
+
const b = original[idx];
|
|
5472
|
+
if (!b) continue;
|
|
5295
5473
|
if (b.type === "tool_use") {
|
|
5296
5474
|
const tokens2 = estimateToolInputTokens(b.input);
|
|
5297
|
-
if (tokens2 < opts.eliseThreshold)
|
|
5475
|
+
if (tokens2 < opts.eliseThreshold) continue;
|
|
5298
5476
|
const elidedInput = summarizeToolUseInputElision(b, tokens2);
|
|
5299
5477
|
saved += Math.max(0, tokens2 - estimateToolInputTokens(elidedInput));
|
|
5300
|
-
|
|
5478
|
+
newContent ??= original.slice();
|
|
5479
|
+
newContent[idx] = { ...b, input: elidedInput };
|
|
5480
|
+
continue;
|
|
5301
5481
|
}
|
|
5302
|
-
if (b.type !== "tool_result")
|
|
5482
|
+
if (b.type !== "tool_result") continue;
|
|
5303
5483
|
const tokens = estimateToolResultTokens(b.content);
|
|
5304
|
-
if (tokens < opts.eliseThreshold)
|
|
5484
|
+
if (tokens < opts.eliseThreshold) continue;
|
|
5305
5485
|
saved += tokens;
|
|
5306
5486
|
const elided = {
|
|
5307
5487
|
type: "tool_result",
|
|
@@ -5309,15 +5489,14 @@ function eliseOldToolResults(messages, opts) {
|
|
|
5309
5489
|
content: summarizeToolResultElision(b, tokens),
|
|
5310
5490
|
is_error: b.is_error
|
|
5311
5491
|
};
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5492
|
+
newContent ??= original.slice();
|
|
5493
|
+
newContent[idx] = elided;
|
|
5494
|
+
}
|
|
5495
|
+
if (newContent) {
|
|
5496
|
+
next ??= messages.slice();
|
|
5317
5497
|
next[i] = { ...msg, content: newContent };
|
|
5318
5498
|
changed = true;
|
|
5319
5499
|
}
|
|
5320
|
-
fullPassInnerIterations += original.length;
|
|
5321
5500
|
if (compactionDebugEnabled()) {
|
|
5322
5501
|
const ratio = fullPassInnerIterations / fullPassIterations;
|
|
5323
5502
|
if (ratio > 10) {
|
|
@@ -5344,7 +5523,7 @@ function eliseOldToolResults(messages, opts) {
|
|
|
5344
5523
|
tokensSaved: saved,
|
|
5345
5524
|
changed
|
|
5346
5525
|
});
|
|
5347
|
-
return { messages: changed ? next : messages, saved, changed };
|
|
5526
|
+
return { messages: changed && next ? next : messages, saved, changed };
|
|
5348
5527
|
}
|
|
5349
5528
|
function summarizeToolUseInputElision(block, tokens) {
|
|
5350
5529
|
const fields = {};
|
|
@@ -6096,6 +6275,7 @@ var DefaultSecretScrubber = class {
|
|
|
6096
6275
|
// src/models/models-registry.ts
|
|
6097
6276
|
init_atomic_write();
|
|
6098
6277
|
var DEFAULT_URL = "https://models.dev/api.json";
|
|
6278
|
+
var ENV_URL_KEY = "WRONGSTACK_MODELS_DEV_URL";
|
|
6099
6279
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
6100
6280
|
var DEFAULT_REFRESH_TIMEOUT_MS = 15e3;
|
|
6101
6281
|
var FAMILY_BY_NPM = {
|
|
@@ -6141,7 +6321,7 @@ var DefaultModelsRegistry = class {
|
|
|
6141
6321
|
overlayCacheFile;
|
|
6142
6322
|
constructor(opts) {
|
|
6143
6323
|
this.cacheFile = opts.cacheFile;
|
|
6144
|
-
this.url = opts.url ?? DEFAULT_URL;
|
|
6324
|
+
this.url = opts.url ?? process.env[ENV_URL_KEY] ?? DEFAULT_URL;
|
|
6145
6325
|
this.ttlMs = (opts.ttlSeconds ?? DEFAULT_TTL_SECONDS) * 1e3;
|
|
6146
6326
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
6147
6327
|
this.seed = opts.seed;
|
|
@@ -6186,6 +6366,10 @@ var DefaultModelsRegistry = class {
|
|
|
6186
6366
|
const cached = await this.readCacheAt(this.cacheFile);
|
|
6187
6367
|
if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {
|
|
6188
6368
|
this.fetchedAt = new Date(cached.fetchedAt);
|
|
6369
|
+
const ageSeconds = Math.floor((Date.now() - this.fetchedAt.getTime()) / 1e3);
|
|
6370
|
+
console.warn(
|
|
6371
|
+
`ModelsRegistry: models.dev unavailable (${toErrorMessage(err)}); using stale cache from ${formatAge(ageSeconds)} ago. Run \`wstack models refresh\` to retry.`
|
|
6372
|
+
);
|
|
6189
6373
|
return cached.payload;
|
|
6190
6374
|
}
|
|
6191
6375
|
if (overlayAvailable) {
|
|
@@ -6271,7 +6455,13 @@ var DefaultModelsRegistry = class {
|
|
|
6271
6455
|
return json;
|
|
6272
6456
|
} catch {
|
|
6273
6457
|
const cached = await this.readCacheAt(this.overlayCacheFile);
|
|
6274
|
-
if (cached && this.isWithinMaxStaleAge(cached.fetchedAt))
|
|
6458
|
+
if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {
|
|
6459
|
+
const ageSeconds = Math.floor((Date.now() - new Date(cached.fetchedAt).getTime()) / 1e3);
|
|
6460
|
+
console.warn(
|
|
6461
|
+
`ModelsRegistry: overlay unavailable; using stale overlay from ${formatAge(ageSeconds)} ago.`
|
|
6462
|
+
);
|
|
6463
|
+
return cached.payload;
|
|
6464
|
+
}
|
|
6275
6465
|
return void 0;
|
|
6276
6466
|
}
|
|
6277
6467
|
}
|
|
@@ -6368,6 +6558,16 @@ var DefaultModelsRegistry = class {
|
|
|
6368
6558
|
return path3.resolve(this.cacheFile);
|
|
6369
6559
|
}
|
|
6370
6560
|
};
|
|
6561
|
+
function formatAge(seconds) {
|
|
6562
|
+
if (seconds < 60) return "<1m";
|
|
6563
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
|
|
6564
|
+
if (seconds < 86400) {
|
|
6565
|
+
const h = Math.floor(seconds / 3600);
|
|
6566
|
+
const m = Math.floor(seconds % 3600 / 60);
|
|
6567
|
+
return m > 0 ? `${h}h ${m}m` : `${h}h`;
|
|
6568
|
+
}
|
|
6569
|
+
return `${Math.floor(seconds / 86400)}d`;
|
|
6570
|
+
}
|
|
6371
6571
|
function hasEntries(payload) {
|
|
6372
6572
|
return payload !== void 0 && Object.keys(payload).length > 0;
|
|
6373
6573
|
}
|
|
@@ -6863,7 +7063,7 @@ var InMemoryAgentBridge = class {
|
|
|
6863
7063
|
});
|
|
6864
7064
|
}
|
|
6865
7065
|
this.inflightGuards.add(correlationId);
|
|
6866
|
-
return new Promise((
|
|
7066
|
+
return new Promise((resolve19, reject) => {
|
|
6867
7067
|
const timer = setTimeout(() => {
|
|
6868
7068
|
this.inflightGuards.delete(correlationId);
|
|
6869
7069
|
this.pendingRequests.delete(correlationId);
|
|
@@ -6882,7 +7082,7 @@ var InMemoryAgentBridge = class {
|
|
|
6882
7082
|
return;
|
|
6883
7083
|
}
|
|
6884
7084
|
this.pendingRequests.set(correlationId, {
|
|
6885
|
-
resolve:
|
|
7085
|
+
resolve: resolve19,
|
|
6886
7086
|
reject,
|
|
6887
7087
|
timer
|
|
6888
7088
|
});
|
|
@@ -8176,6 +8376,8 @@ function generateSessionId(startedAt, model) {
|
|
|
8176
8376
|
const modelPart = model ? `_${sanitizeModel(model)}` : "";
|
|
8177
8377
|
return `${date}/${time}Z${modelPart}_${suffix}`;
|
|
8178
8378
|
}
|
|
8379
|
+
|
|
8380
|
+
// src/storage/session-store.ts
|
|
8179
8381
|
var DefaultSessionStore = class _DefaultSessionStore {
|
|
8180
8382
|
dir;
|
|
8181
8383
|
events;
|
|
@@ -8193,6 +8395,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8193
8395
|
_loadCache = /* @__PURE__ */ new Map();
|
|
8194
8396
|
_indexCache = null;
|
|
8195
8397
|
static LOAD_CACHE_MAX_ENTRIES = 50;
|
|
8398
|
+
static LIST_SCAN_CONCURRENCY = 32;
|
|
8196
8399
|
constructor(opts) {
|
|
8197
8400
|
this.dir = opts.dir;
|
|
8198
8401
|
this.events = opts.events;
|
|
@@ -8209,7 +8412,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8209
8412
|
this._loadCache.clear();
|
|
8210
8413
|
}
|
|
8211
8414
|
}
|
|
8212
|
-
//
|
|
8415
|
+
// ── Storage event helpers ───────────────────────────────────────────────────
|
|
8213
8416
|
emitRead(sessionId, filePath, operation, outcome, durationMs, error) {
|
|
8214
8417
|
this.events?.emit("storage.read", {
|
|
8215
8418
|
sessionId,
|
|
@@ -8324,7 +8527,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8324
8527
|
this.events,
|
|
8325
8528
|
{
|
|
8326
8529
|
resumed: true,
|
|
8327
|
-
// Shard directory (sessions/<date>/)
|
|
8530
|
+
// Shard directory (sessions/<date>/) — must match create() so the
|
|
8328
8531
|
// .summary.json sidecar lands next to the JSONL instead of the
|
|
8329
8532
|
// sessions root (where summaryFor() would never find it).
|
|
8330
8533
|
dir: path3.dirname(file),
|
|
@@ -8354,9 +8557,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8354
8557
|
let cacheHit = false;
|
|
8355
8558
|
try {
|
|
8356
8559
|
const s = await fsp3.stat(file);
|
|
8357
|
-
const
|
|
8560
|
+
const stat16 = { mtimeMs: s.mtimeMs, size: s.size };
|
|
8358
8561
|
const cached = this._loadCache.get(id);
|
|
8359
|
-
if (cached && cached.mtimeMs ===
|
|
8562
|
+
if (cached && cached.mtimeMs === stat16.mtimeMs && cached.size === stat16.size) {
|
|
8360
8563
|
cacheHit = true;
|
|
8361
8564
|
this._loadCache.delete(id);
|
|
8362
8565
|
this._loadCache.set(id, cached);
|
|
@@ -8365,26 +8568,100 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8365
8568
|
const raw = await fsp3.readFile(file, "utf8");
|
|
8366
8569
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
8367
8570
|
const events = [];
|
|
8571
|
+
let sessionStartEvent;
|
|
8572
|
+
let sessionEndEvent;
|
|
8573
|
+
let sessionModel;
|
|
8574
|
+
let sessionProvider;
|
|
8575
|
+
let sessionPendingToolUses;
|
|
8576
|
+
const messages = [];
|
|
8577
|
+
const openToolUses = /* @__PURE__ */ new Set();
|
|
8578
|
+
let usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
8368
8579
|
for (const line of lines) {
|
|
8369
8580
|
try {
|
|
8370
8581
|
const parsed = JSON.parse(line);
|
|
8371
8582
|
if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
|
|
8372
|
-
|
|
8583
|
+
const ev = parsed;
|
|
8584
|
+
events.push(ev);
|
|
8585
|
+
if (ev.type === "session_start" && !sessionStartEvent) {
|
|
8586
|
+
sessionStartEvent = ev;
|
|
8587
|
+
sessionModel = ev.model;
|
|
8588
|
+
sessionProvider = ev.provider;
|
|
8589
|
+
}
|
|
8590
|
+
if (ev.type === "session_end") {
|
|
8591
|
+
sessionEndEvent = ev;
|
|
8592
|
+
sessionPendingToolUses = ev.pendingToolUses;
|
|
8593
|
+
}
|
|
8594
|
+
if (ev.type === "user_input") {
|
|
8595
|
+
openToolUses.clear();
|
|
8596
|
+
messages.push({ role: "user", content: ev.content, ts: ev.ts });
|
|
8597
|
+
} else if (ev.type === "llm_response") {
|
|
8598
|
+
messages.push({ role: "assistant", content: ev.content, ts: ev.ts });
|
|
8599
|
+
for (const b of ev.content) {
|
|
8600
|
+
if (b.type === "tool_use") openToolUses.add(b.id);
|
|
8601
|
+
}
|
|
8602
|
+
usage = {
|
|
8603
|
+
input: usage.input + (ev.usage.input ?? 0),
|
|
8604
|
+
output: usage.output + (ev.usage.output ?? 0),
|
|
8605
|
+
cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
|
|
8606
|
+
cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
|
|
8607
|
+
};
|
|
8608
|
+
} else if (ev.type === "tool_result") {
|
|
8609
|
+
if (!openToolUses.has(ev.id)) {
|
|
8610
|
+
this.events?.emit("session.damaged", {
|
|
8611
|
+
sessionId: id,
|
|
8612
|
+
detail: `Orphan tool_result "${ev.id}" has no matching tool_use`
|
|
8613
|
+
});
|
|
8614
|
+
continue;
|
|
8615
|
+
}
|
|
8616
|
+
openToolUses.delete(ev.id);
|
|
8617
|
+
const resultBlock = {
|
|
8618
|
+
type: "tool_result",
|
|
8619
|
+
tool_use_id: ev.id,
|
|
8620
|
+
content: typeof ev.content === "string" ? ev.content : JSON.stringify(ev.content),
|
|
8621
|
+
is_error: ev.isError
|
|
8622
|
+
};
|
|
8623
|
+
const last = messages[messages.length - 1];
|
|
8624
|
+
const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
|
|
8625
|
+
if (lastIsToolResultUser && Array.isArray(last.content)) {
|
|
8626
|
+
last.content.push(resultBlock);
|
|
8627
|
+
} else {
|
|
8628
|
+
messages.push({ role: "user", content: [resultBlock], ts: ev.ts });
|
|
8629
|
+
}
|
|
8630
|
+
}
|
|
8373
8631
|
}
|
|
8374
8632
|
} catch {
|
|
8375
8633
|
}
|
|
8376
8634
|
}
|
|
8377
|
-
|
|
8378
|
-
|
|
8635
|
+
if (openToolUses.size > 0) {
|
|
8636
|
+
this.events?.emit("session.damaged", {
|
|
8637
|
+
sessionId: id,
|
|
8638
|
+
detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
|
|
8639
|
+
});
|
|
8640
|
+
}
|
|
8641
|
+
const repaired = repairToolUseAdjacency(messages);
|
|
8642
|
+
if (repaired.report.changed) {
|
|
8643
|
+
this.events?.emit("session.damaged", {
|
|
8644
|
+
sessionId: id,
|
|
8645
|
+
detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
|
|
8646
|
+
});
|
|
8647
|
+
}
|
|
8648
|
+
const meta = {
|
|
8649
|
+
id,
|
|
8650
|
+
startedAt: sessionStartEvent?.ts ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
8651
|
+
endedAt: sessionEndEvent?.ts,
|
|
8652
|
+
model: sessionModel,
|
|
8653
|
+
provider: sessionProvider,
|
|
8654
|
+
pendingToolUses: sessionPendingToolUses
|
|
8655
|
+
};
|
|
8379
8656
|
const toolCallEnds = extractToolCallEnds(events);
|
|
8380
|
-
const data = { metadata: meta, events, messages, usage, toolCallEnds };
|
|
8657
|
+
const data = { metadata: meta, events, messages: repaired.messages, usage, toolCallEnds };
|
|
8381
8658
|
if (this._loadCache.size >= _DefaultSessionStore.LOAD_CACHE_MAX_ENTRIES) {
|
|
8382
8659
|
const oldest = this._loadCache.keys().next().value;
|
|
8383
8660
|
if (oldest !== void 0) {
|
|
8384
8661
|
this._loadCache.delete(oldest);
|
|
8385
8662
|
}
|
|
8386
8663
|
}
|
|
8387
|
-
this._loadCache.set(id, { mtimeMs:
|
|
8664
|
+
this._loadCache.set(id, { mtimeMs: stat16.mtimeMs, size: stat16.size, data });
|
|
8388
8665
|
return data;
|
|
8389
8666
|
} catch (err) {
|
|
8390
8667
|
outcome = "failure";
|
|
@@ -8415,20 +8692,12 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8415
8692
|
});
|
|
8416
8693
|
return indexed.slice(0, limit);
|
|
8417
8694
|
}
|
|
8418
|
-
|
|
8419
|
-
const sessions = await Promise.all(ids.map((id) => this.summaryFor(id).catch(() => null)));
|
|
8420
|
-
const out = sessions.filter((s) => s !== null);
|
|
8421
|
-
out.sort((a, b) => {
|
|
8422
|
-
if (a.startedAt < b.startedAt) return 1;
|
|
8423
|
-
if (a.startedAt > b.startedAt) return -1;
|
|
8424
|
-
return a.id.localeCompare(b.id);
|
|
8425
|
-
});
|
|
8426
|
-
return out.slice(0, limit);
|
|
8695
|
+
return await this.listFromDirectoryScan(limit);
|
|
8427
8696
|
} catch {
|
|
8428
8697
|
return [];
|
|
8429
8698
|
}
|
|
8430
8699
|
}
|
|
8431
|
-
//
|
|
8700
|
+
// ── Session index (_index.jsonl) ─────────────────────────────────────────
|
|
8432
8701
|
//
|
|
8433
8702
|
// One JSON line per closed session, appended atomically on close().
|
|
8434
8703
|
// When a session is deleted, a tombstone {action:"delete",id:"..."} is
|
|
@@ -8494,15 +8763,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8494
8763
|
* Returns empty array when the index doesn't exist or is corrupt.
|
|
8495
8764
|
*/
|
|
8496
8765
|
async readIndex() {
|
|
8497
|
-
let
|
|
8766
|
+
let stat16;
|
|
8498
8767
|
try {
|
|
8499
8768
|
const s = await fsp3.stat(this.indexFile);
|
|
8500
|
-
|
|
8769
|
+
stat16 = { mtimeMs: s.mtimeMs, size: s.size };
|
|
8501
8770
|
} catch {
|
|
8502
8771
|
this._indexCache = null;
|
|
8503
8772
|
return [];
|
|
8504
8773
|
}
|
|
8505
|
-
if (this._indexCache !== null && this._indexCache.mtimeMs ===
|
|
8774
|
+
if (this._indexCache !== null && this._indexCache.mtimeMs === stat16.mtimeMs && this._indexCache.size === stat16.size) {
|
|
8506
8775
|
return [...this._indexCache.summaries];
|
|
8507
8776
|
}
|
|
8508
8777
|
let raw;
|
|
@@ -8530,7 +8799,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8530
8799
|
}
|
|
8531
8800
|
}
|
|
8532
8801
|
const summaries = Array.from(seen.values());
|
|
8533
|
-
this._indexCache = { ...
|
|
8802
|
+
this._indexCache = { ...stat16, summaries };
|
|
8534
8803
|
return [...summaries];
|
|
8535
8804
|
}
|
|
8536
8805
|
/**
|
|
@@ -8548,46 +8817,105 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8548
8817
|
this._indexCache = null;
|
|
8549
8818
|
return valid.length;
|
|
8550
8819
|
}
|
|
8820
|
+
async listFromDirectoryScan(limit) {
|
|
8821
|
+
const refs = await this.collectSessionFiles(this.dir);
|
|
8822
|
+
const candidates = await mapWithConcurrency(
|
|
8823
|
+
refs,
|
|
8824
|
+
_DefaultSessionStore.LIST_SCAN_CONCURRENCY,
|
|
8825
|
+
async (ref) => {
|
|
8826
|
+
const manifest = await this.readSummaryManifest(ref.id);
|
|
8827
|
+
if (manifest) return { summary: manifest, needsBackfill: false };
|
|
8828
|
+
const summary = await this.summaryHeaderFor(ref);
|
|
8829
|
+
return summary ? { summary, needsBackfill: true } : null;
|
|
8830
|
+
}
|
|
8831
|
+
);
|
|
8832
|
+
const out = candidates.filter((s) => s !== null);
|
|
8833
|
+
out.sort((a, b) => compareSessionSummaries(a.summary, b.summary));
|
|
8834
|
+
const selected = out.slice(0, limit);
|
|
8835
|
+
const summaries = await mapWithConcurrency(
|
|
8836
|
+
selected,
|
|
8837
|
+
Math.min(_DefaultSessionStore.LIST_SCAN_CONCURRENCY, Math.max(1, limit)),
|
|
8838
|
+
async (candidate) => {
|
|
8839
|
+
if (!candidate.needsBackfill) return candidate.summary;
|
|
8840
|
+
return await this.summaryFor(candidate.summary.id).catch(() => candidate.summary);
|
|
8841
|
+
}
|
|
8842
|
+
);
|
|
8843
|
+
return summaries.filter((s) => s !== null);
|
|
8844
|
+
}
|
|
8845
|
+
async collectSessionFiles(dir, prefix = "", depth = 0) {
|
|
8846
|
+
let entries;
|
|
8847
|
+
try {
|
|
8848
|
+
entries = await fsp3.readdir(dir, { withFileTypes: true });
|
|
8849
|
+
} catch {
|
|
8850
|
+
return [];
|
|
8851
|
+
}
|
|
8852
|
+
const dirEntries = [];
|
|
8853
|
+
const files = [];
|
|
8854
|
+
for (const entry of entries) {
|
|
8855
|
+
if (entry.name.startsWith(".") && entry.name !== ".wrongstack") continue;
|
|
8856
|
+
if (entry.name === "shared" || entry.name === "subagents" || entry.name === "attachments")
|
|
8857
|
+
continue;
|
|
8858
|
+
if (entry.isDirectory()) {
|
|
8859
|
+
dirEntries.push(entry);
|
|
8860
|
+
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
8861
|
+
if (entry.name === "_index.jsonl") continue;
|
|
8862
|
+
const base = entry.name.replace(/\.jsonl$/, "");
|
|
8863
|
+
const id = prefix ? `${prefix}/${base}` : base;
|
|
8864
|
+
files.push({ id, filePath: path3.join(dir, entry.name) });
|
|
8865
|
+
}
|
|
8866
|
+
}
|
|
8867
|
+
const childFileArrays = await Promise.all(
|
|
8868
|
+
dirEntries.map((entry) => {
|
|
8869
|
+
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
8870
|
+
return this.collectSessionFiles(path3.join(dir, entry.name), childPrefix, depth + 1);
|
|
8871
|
+
})
|
|
8872
|
+
);
|
|
8873
|
+
return [...childFileArrays.flat(), ...files];
|
|
8874
|
+
}
|
|
8551
8875
|
/** Recursively collect session IDs from date-shard subdirectories.
|
|
8552
|
-
* IDs include the date-prefix path (e.g. "2026-06-06/17-46-57Z_
|
|
8876
|
+
* IDs include the date-prefix path (e.g. "2026-06-06/17-46-57Z_…").
|
|
8553
8877
|
* Skips `.jsonl`/`.summary.json` root files, dot-files, and
|
|
8554
8878
|
* sub-directories that belong to fleet/subagent sessions. */
|
|
8555
8879
|
async collectSessionIds(dir, prefix = "", depth = 0) {
|
|
8556
|
-
const ids = [];
|
|
8557
8880
|
let entries;
|
|
8558
8881
|
try {
|
|
8559
8882
|
entries = await fsp3.readdir(dir, { withFileTypes: true });
|
|
8560
8883
|
} catch {
|
|
8561
|
-
return
|
|
8884
|
+
return [];
|
|
8562
8885
|
}
|
|
8886
|
+
const dirEntries = [];
|
|
8887
|
+
const fileIds = [];
|
|
8563
8888
|
for (const entry of entries) {
|
|
8564
8889
|
if (entry.name.startsWith(".") && entry.name !== ".wrongstack") continue;
|
|
8565
8890
|
if (entry.name === "shared" || entry.name === "subagents" || entry.name === "attachments")
|
|
8566
8891
|
continue;
|
|
8567
8892
|
if (entry.isDirectory()) {
|
|
8568
|
-
|
|
8569
|
-
ids.push(...await this.collectSessionIds(path3.join(dir, entry.name), childPrefix, depth + 1));
|
|
8893
|
+
dirEntries.push(entry);
|
|
8570
8894
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
8571
8895
|
if (entry.name === "_index.jsonl") continue;
|
|
8572
8896
|
const base = entry.name.replace(/\.jsonl$/, "");
|
|
8573
|
-
|
|
8897
|
+
fileIds.push(prefix ? `${prefix}/${base}` : base);
|
|
8574
8898
|
}
|
|
8575
8899
|
}
|
|
8576
|
-
|
|
8900
|
+
const childIdArrays = await Promise.all(
|
|
8901
|
+
dirEntries.map((entry) => {
|
|
8902
|
+
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
8903
|
+
return this.collectSessionIds(path3.join(dir, entry.name), childPrefix, depth + 1);
|
|
8904
|
+
})
|
|
8905
|
+
);
|
|
8906
|
+
return [...childIdArrays.flat(), ...fileIds];
|
|
8577
8907
|
}
|
|
8578
8908
|
async summaryFor(id) {
|
|
8579
8909
|
const manifest = this.sessionPath(id, ".summary.json");
|
|
8580
8910
|
const t0 = Date.now();
|
|
8581
8911
|
let outcome = "success";
|
|
8582
8912
|
let errorMsg;
|
|
8913
|
+
const fromManifest = await this.readSummaryManifest(id, t0);
|
|
8914
|
+
if (fromManifest) return fromManifest;
|
|
8583
8915
|
try {
|
|
8584
|
-
const raw = await fsp3.readFile(manifest, "utf8");
|
|
8585
|
-
this.emitRead(id, manifest, "summary", "success", Date.now() - t0);
|
|
8586
|
-
return JSON.parse(raw);
|
|
8587
|
-
} catch {
|
|
8588
8916
|
const full = this.sessionPath(id, ".jsonl");
|
|
8589
|
-
const
|
|
8590
|
-
const summary = await this.summarize(id,
|
|
8917
|
+
const stat16 = await fsp3.stat(full);
|
|
8918
|
+
const summary = await this.summarize(id, stat16.mtime.toISOString());
|
|
8591
8919
|
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
8592
8920
|
const msg = toErrorMessage(err);
|
|
8593
8921
|
this.emitError(id, manifest, "summary_fallback", msg, true);
|
|
@@ -8600,9 +8928,81 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8600
8928
|
}));
|
|
8601
8929
|
});
|
|
8602
8930
|
outcome = "failure";
|
|
8603
|
-
errorMsg = "summary fallback \
|
|
8931
|
+
errorMsg = "summary fallback \xE2\u20AC\u201D manifest rebuilt";
|
|
8604
8932
|
this.emitRead(id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
|
|
8605
8933
|
return summary;
|
|
8934
|
+
} catch (err) {
|
|
8935
|
+
outcome = "failure";
|
|
8936
|
+
errorMsg = toErrorMessage(err);
|
|
8937
|
+
this.emitRead(id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
|
|
8938
|
+
return {
|
|
8939
|
+
id,
|
|
8940
|
+
title: "(damaged)",
|
|
8941
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8942
|
+
model: "unknown",
|
|
8943
|
+
provider: "unknown",
|
|
8944
|
+
tokenTotal: 0
|
|
8945
|
+
};
|
|
8946
|
+
}
|
|
8947
|
+
}
|
|
8948
|
+
async readSummaryManifest(id, startTime = Date.now()) {
|
|
8949
|
+
const manifest = this.sessionPath(id, ".summary.json");
|
|
8950
|
+
try {
|
|
8951
|
+
const raw = await fsp3.readFile(manifest, "utf8");
|
|
8952
|
+
this.emitRead(id, manifest, "summary", "success", Date.now() - startTime);
|
|
8953
|
+
return JSON.parse(raw);
|
|
8954
|
+
} catch {
|
|
8955
|
+
return null;
|
|
8956
|
+
}
|
|
8957
|
+
}
|
|
8958
|
+
async summaryHeaderFor(ref) {
|
|
8959
|
+
let mtime = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
8960
|
+
try {
|
|
8961
|
+
const stat16 = await fsp3.stat(ref.filePath);
|
|
8962
|
+
if (!stat16.isFile()) {
|
|
8963
|
+
return {
|
|
8964
|
+
id: ref.id,
|
|
8965
|
+
title: "(damaged)",
|
|
8966
|
+
startedAt: stat16.mtime.toISOString(),
|
|
8967
|
+
model: "unknown",
|
|
8968
|
+
provider: "unknown",
|
|
8969
|
+
tokenTotal: 0
|
|
8970
|
+
};
|
|
8971
|
+
}
|
|
8972
|
+
mtime = stat16.mtime.toISOString();
|
|
8973
|
+
} catch {
|
|
8974
|
+
return null;
|
|
8975
|
+
}
|
|
8976
|
+
try {
|
|
8977
|
+
for await (const event of this.iterSessionEvents(ref.filePath)) {
|
|
8978
|
+
if (event.type === "session_start") {
|
|
8979
|
+
return {
|
|
8980
|
+
id: ref.id,
|
|
8981
|
+
title: "(empty session)",
|
|
8982
|
+
startedAt: event.ts,
|
|
8983
|
+
model: event.model ?? "unknown",
|
|
8984
|
+
provider: event.provider ?? "unknown",
|
|
8985
|
+
tokenTotal: 0
|
|
8986
|
+
};
|
|
8987
|
+
}
|
|
8988
|
+
}
|
|
8989
|
+
return {
|
|
8990
|
+
id: ref.id,
|
|
8991
|
+
title: "(empty session)",
|
|
8992
|
+
startedAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
8993
|
+
model: "unknown",
|
|
8994
|
+
provider: "unknown",
|
|
8995
|
+
tokenTotal: 0
|
|
8996
|
+
};
|
|
8997
|
+
} catch {
|
|
8998
|
+
return {
|
|
8999
|
+
id: ref.id,
|
|
9000
|
+
title: "(damaged)",
|
|
9001
|
+
startedAt: mtime,
|
|
9002
|
+
model: "unknown",
|
|
9003
|
+
provider: "unknown",
|
|
9004
|
+
tokenTotal: 0
|
|
9005
|
+
};
|
|
8606
9006
|
}
|
|
8607
9007
|
}
|
|
8608
9008
|
/**
|
|
@@ -8669,8 +9069,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8669
9069
|
const pruneFile = async (dir, name, prefix) => {
|
|
8670
9070
|
const jsonlPath = path3.join(dir, name);
|
|
8671
9071
|
try {
|
|
8672
|
-
const
|
|
8673
|
-
if (
|
|
9072
|
+
const stat16 = await fsp3.stat(jsonlPath);
|
|
9073
|
+
if (stat16.mtimeMs >= cutoff) return;
|
|
8674
9074
|
} catch {
|
|
8675
9075
|
return;
|
|
8676
9076
|
}
|
|
@@ -8727,39 +9127,62 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8727
9127
|
}
|
|
8728
9128
|
async summarize(id, mtime) {
|
|
8729
9129
|
try {
|
|
8730
|
-
const
|
|
8731
|
-
|
|
8732
|
-
|
|
9130
|
+
const file = this.sessionPath(id, ".jsonl");
|
|
9131
|
+
let title = "(empty session)";
|
|
9132
|
+
let startedAt = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
9133
|
+
let endedAt;
|
|
9134
|
+
let model = "unknown";
|
|
9135
|
+
let provider = "unknown";
|
|
9136
|
+
let tokenIn = 0;
|
|
9137
|
+
let tokenOut = 0;
|
|
8733
9138
|
let iterationCount = 0;
|
|
8734
9139
|
let toolCallCount = 0;
|
|
8735
9140
|
let toolErrorCount = 0;
|
|
8736
9141
|
let fileChangeCount = 0;
|
|
8737
9142
|
const toolBreakdown = {};
|
|
8738
9143
|
let outcome;
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
9144
|
+
let lastEventType;
|
|
9145
|
+
let hasError = false;
|
|
9146
|
+
let sawStart = false;
|
|
9147
|
+
for await (const e of this.iterSessionEvents(file)) {
|
|
9148
|
+
lastEventType = e.type;
|
|
9149
|
+
if (e.type === "session_start") {
|
|
9150
|
+
if (!sawStart) {
|
|
9151
|
+
sawStart = true;
|
|
9152
|
+
startedAt = e.ts;
|
|
9153
|
+
model = e.model ?? "unknown";
|
|
9154
|
+
provider = e.provider ?? "unknown";
|
|
9155
|
+
}
|
|
9156
|
+
} else if (e.type === "session_end") {
|
|
9157
|
+
endedAt = e.ts;
|
|
9158
|
+
} else if (e.type === "user_input") {
|
|
9159
|
+
if (title === "(empty session)") title = userInputTitle(e.content);
|
|
9160
|
+
} else if (e.type === "llm_response") {
|
|
9161
|
+
tokenIn += e.usage.input ?? 0;
|
|
9162
|
+
tokenOut += e.usage.output ?? 0;
|
|
9163
|
+
} else if (e.type === "in_flight_start") iterationCount++;
|
|
8742
9164
|
else if (e.type === "tool_call_start") {
|
|
8743
9165
|
toolCallCount++;
|
|
8744
9166
|
toolBreakdown[e.name] = (toolBreakdown[e.name] ?? 0) + 1;
|
|
8745
9167
|
} else if (e.type === "tool_result" && e.isError) toolErrorCount++;
|
|
8746
9168
|
else if (e.type === "file_snapshot") fileChangeCount += e.files.length;
|
|
9169
|
+
else if (e.type === "error" || e.type === "provider_error") hasError = true;
|
|
8747
9170
|
}
|
|
8748
|
-
if (
|
|
9171
|
+
if (lastEventType === "session_end") {
|
|
8749
9172
|
outcome = "completed";
|
|
8750
|
-
} else if (
|
|
9173
|
+
} else if (lastEventType === "in_flight_start") {
|
|
8751
9174
|
outcome = "aborted";
|
|
8752
|
-
} else if (
|
|
9175
|
+
} else if (hasError) {
|
|
8753
9176
|
outcome = "error";
|
|
8754
9177
|
}
|
|
8755
9178
|
return {
|
|
8756
9179
|
id,
|
|
8757
9180
|
title,
|
|
8758
|
-
startedAt
|
|
8759
|
-
endedAt
|
|
8760
|
-
model
|
|
8761
|
-
provider
|
|
8762
|
-
tokenTotal:
|
|
9181
|
+
startedAt,
|
|
9182
|
+
endedAt,
|
|
9183
|
+
model,
|
|
9184
|
+
provider,
|
|
9185
|
+
tokenTotal: tokenIn + tokenOut,
|
|
8763
9186
|
iterationCount: iterationCount > 0 ? iterationCount : void 0,
|
|
8764
9187
|
toolCallCount: toolCallCount > 0 ? toolCallCount : void 0,
|
|
8765
9188
|
toolErrorCount: toolErrorCount > 0 ? toolErrorCount : void 0,
|
|
@@ -8778,75 +9201,24 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8778
9201
|
};
|
|
8779
9202
|
}
|
|
8780
9203
|
}
|
|
8781
|
-
|
|
8782
|
-
const
|
|
8783
|
-
const
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
replay(events, sessionId = "unknown") {
|
|
8794
|
-
const messages = [];
|
|
8795
|
-
let usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
8796
|
-
const openToolUses = /* @__PURE__ */ new Set();
|
|
8797
|
-
for (const e of events) {
|
|
8798
|
-
if (e.type === "user_input") {
|
|
8799
|
-
openToolUses.clear();
|
|
8800
|
-
messages.push({ role: "user", content: e.content, ts: e.ts });
|
|
8801
|
-
} else if (e.type === "llm_response") {
|
|
8802
|
-
messages.push({ role: "assistant", content: e.content, ts: e.ts });
|
|
8803
|
-
for (const b of e.content) {
|
|
8804
|
-
if (b.type === "tool_use") openToolUses.add(b.id);
|
|
8805
|
-
}
|
|
8806
|
-
usage = {
|
|
8807
|
-
input: usage.input + (e.usage.input ?? 0),
|
|
8808
|
-
output: usage.output + (e.usage.output ?? 0),
|
|
8809
|
-
cacheRead: (usage.cacheRead ?? 0) + (e.usage.cacheRead ?? 0),
|
|
8810
|
-
cacheWrite: (usage.cacheWrite ?? 0) + (e.usage.cacheWrite ?? 0)
|
|
8811
|
-
};
|
|
8812
|
-
} else if (e.type === "tool_result") {
|
|
8813
|
-
if (!openToolUses.has(e.id)) {
|
|
8814
|
-
this.events?.emit("session.damaged", {
|
|
8815
|
-
sessionId,
|
|
8816
|
-
detail: `Orphan tool_result "${e.id}" has no matching tool_use`
|
|
8817
|
-
});
|
|
8818
|
-
continue;
|
|
8819
|
-
}
|
|
8820
|
-
openToolUses.delete(e.id);
|
|
8821
|
-
const resultBlock = {
|
|
8822
|
-
type: "tool_result",
|
|
8823
|
-
tool_use_id: e.id,
|
|
8824
|
-
content: typeof e.content === "string" ? e.content : JSON.stringify(e.content),
|
|
8825
|
-
is_error: e.isError
|
|
8826
|
-
};
|
|
8827
|
-
const last = messages[messages.length - 1];
|
|
8828
|
-
const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
|
|
8829
|
-
if (lastIsToolResultUser && Array.isArray(last.content)) {
|
|
8830
|
-
last.content.push(resultBlock);
|
|
8831
|
-
} else {
|
|
8832
|
-
messages.push({ role: "user", content: [resultBlock], ts: e.ts });
|
|
9204
|
+
async *iterSessionEvents(file) {
|
|
9205
|
+
const stream = createReadStream(file, { encoding: "utf8" });
|
|
9206
|
+
const lines = createInterface({ input: stream, crlfDelay: Infinity });
|
|
9207
|
+
try {
|
|
9208
|
+
for await (const line of lines) {
|
|
9209
|
+
if (!line.trim()) continue;
|
|
9210
|
+
try {
|
|
9211
|
+
const parsed = JSON.parse(line);
|
|
9212
|
+
if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
|
|
9213
|
+
yield parsed;
|
|
9214
|
+
}
|
|
9215
|
+
} catch {
|
|
8833
9216
|
}
|
|
8834
9217
|
}
|
|
9218
|
+
} finally {
|
|
9219
|
+
lines.close();
|
|
9220
|
+
stream.destroy();
|
|
8835
9221
|
}
|
|
8836
|
-
if (openToolUses.size > 0) {
|
|
8837
|
-
this.events?.emit("session.damaged", {
|
|
8838
|
-
sessionId,
|
|
8839
|
-
detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
|
|
8840
|
-
});
|
|
8841
|
-
}
|
|
8842
|
-
const repaired = repairToolUseAdjacency(messages);
|
|
8843
|
-
if (repaired.report.changed) {
|
|
8844
|
-
this.events?.emit("session.damaged", {
|
|
8845
|
-
sessionId,
|
|
8846
|
-
detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
|
|
8847
|
-
});
|
|
8848
|
-
}
|
|
8849
|
-
return { messages: repaired.messages, usage };
|
|
8850
9222
|
}
|
|
8851
9223
|
};
|
|
8852
9224
|
function extractToolCallEnds(events) {
|
|
@@ -8906,7 +9278,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
8906
9278
|
/**
|
|
8907
9279
|
* Lazy session_start/session_resumed init, shared by all appenders.
|
|
8908
9280
|
* A single promise (not a boolean) so a second append racing the first
|
|
8909
|
-
* can't push its event into the buffer BEFORE the first append's event
|
|
9281
|
+
* can't push its event into the buffer BEFORE the first append's event —
|
|
8910
9282
|
* every appender awaits the same init and resumes in FIFO call order.
|
|
8911
9283
|
*/
|
|
8912
9284
|
initPromise = null;
|
|
@@ -8919,24 +9291,24 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
8919
9291
|
lastAppendWarnAt = 0;
|
|
8920
9292
|
secretScrubber;
|
|
8921
9293
|
onCloseCb;
|
|
8922
|
-
/** Implements SessionWriter.traceId
|
|
9294
|
+
/** Implements SessionWriter.traceId — propagated from ContextInit.traceId. */
|
|
8923
9295
|
traceId;
|
|
8924
|
-
//
|
|
9296
|
+
// ── Write buffer — batches events to reduce per-event disk I/O ─────────
|
|
8925
9297
|
//
|
|
8926
9298
|
// Every append() pushes the scrubbed event into an in-memory buffer instead
|
|
8927
9299
|
// of calling handle.appendFile() synchronously. The buffer flushes to disk
|
|
8928
9300
|
// when it reaches FLUSH_SIZE events OR after FLUSH_INTERVAL_MS of inactivity.
|
|
8929
9301
|
// This cuts the number of disk writes by ~95% without changing the on-disk
|
|
8930
|
-
// format
|
|
9302
|
+
// format — the JSONL is still one JSON object per line.
|
|
8931
9303
|
writeBuffer = [];
|
|
8932
9304
|
flushTimer = null;
|
|
8933
9305
|
static FLUSH_INTERVAL_MS = 500;
|
|
8934
9306
|
static FLUSH_SIZE = 50;
|
|
8935
|
-
//
|
|
9307
|
+
// ── Write serialization ─────────────────────────────────────────────────
|
|
8936
9308
|
//
|
|
8937
9309
|
// All disk writes are funneled through a FIFO promise chain. Without it,
|
|
8938
9310
|
// a timer-driven flush racing an explicit flush()/close() issues two
|
|
8939
|
-
// concurrent appendFile() calls on the shared O_APPEND handle
|
|
9311
|
+
// concurrent appendFile() calls on the shared O_APPEND handle — the kernel
|
|
8940
9312
|
// may complete them out of order (chronology breaks) or, for large
|
|
8941
9313
|
// batches, interleave partial writes (torn JSONL lines). The chain keeps
|
|
8942
9314
|
// exactly one write in flight; failures don't break the chain.
|
|
@@ -8950,7 +9322,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
8950
9322
|
);
|
|
8951
9323
|
return write;
|
|
8952
9324
|
}
|
|
8953
|
-
//
|
|
9325
|
+
// ── Enriched summary tracking ──────────────────────────────────────────
|
|
8954
9326
|
iterationCount = 0;
|
|
8955
9327
|
toolCallCount = 0;
|
|
8956
9328
|
toolErrorCount = 0;
|
|
@@ -9042,7 +9414,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
9042
9414
|
* (user_input, llm_response) call this so they survive SIGKILL/crash
|
|
9043
9415
|
* instead of sitting in the in-memory buffer for up to 500ms.
|
|
9044
9416
|
*
|
|
9045
|
-
* Idempotent
|
|
9417
|
+
* Idempotent — cancels any pending timer and writes whatever has
|
|
9046
9418
|
* accumulated in the buffer. Safe to call even when the buffer
|
|
9047
9419
|
* is empty (no-op).
|
|
9048
9420
|
*/
|
|
@@ -9065,7 +9437,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
9065
9437
|
/**
|
|
9066
9438
|
* Flush all buffered events to disk as a single appendFile call.
|
|
9067
9439
|
* Errors use the same throttled-warning pattern the old per-event
|
|
9068
|
-
* append path used
|
|
9440
|
+
* append path used — one warning every 5s with a suppressed count.
|
|
9069
9441
|
* On failure the buffer is cleared (events are best-effort, same as
|
|
9070
9442
|
* the old per-event path where a failed write was silently dropped).
|
|
9071
9443
|
*/
|
|
@@ -9248,7 +9620,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
9248
9620
|
/**
|
|
9249
9621
|
* Truncate the session file to the checkpoint with the given promptIndex,
|
|
9250
9622
|
* removing all events that follow it. Uses a single-pass byte-offset scan
|
|
9251
|
-
* so post-checkpoint content is never read or parsed
|
|
9623
|
+
* so post-checkpoint content is never read or parsed — O(1) memory instead
|
|
9252
9624
|
* of O(N) JSON.parse calls over the full file.
|
|
9253
9625
|
*/
|
|
9254
9626
|
async truncateToCheckpoint(targetPromptIndex) {
|
|
@@ -9405,7 +9777,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
9405
9777
|
await fsp3.writeFile(this.filePath, record, "utf8");
|
|
9406
9778
|
}
|
|
9407
9779
|
/**
|
|
9408
|
-
* Idea #1
|
|
9780
|
+
* Idea #1 — write an in-flight marker. The agent loop should call
|
|
9409
9781
|
* this at the start of each long-running operation; a matching
|
|
9410
9782
|
* `clearInFlightMarker` follows on clean exit. A stale marker
|
|
9411
9783
|
* (no end) is what `SessionRecovery.detectStale` looks for.
|
|
@@ -9422,9 +9794,9 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
9422
9794
|
this.events?.emit("in_flight.started", { context, ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
9423
9795
|
}
|
|
9424
9796
|
/**
|
|
9425
|
-
* Idea #1
|
|
9797
|
+
* Idea #1 — close the in-flight marker. Idempotent in spirit
|
|
9426
9798
|
* (you can call it after a successful iteration even if you
|
|
9427
|
-
* didn't open one this round)
|
|
9799
|
+
* didn't open one this round) — but the session log records
|
|
9428
9800
|
* every call so postmortem tooling can see "the agent finished
|
|
9429
9801
|
* cleanly X times, then died without finishing Y".
|
|
9430
9802
|
*/
|
|
@@ -9441,6 +9813,27 @@ function userInputTitle(content) {
|
|
|
9441
9813
|
const text = typeof content === "string" ? content : content.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
9442
9814
|
return (text || "(non-text input)").slice(0, 60);
|
|
9443
9815
|
}
|
|
9816
|
+
function compareSessionSummaries(a, b) {
|
|
9817
|
+
if (a.startedAt < b.startedAt) return 1;
|
|
9818
|
+
if (a.startedAt > b.startedAt) return -1;
|
|
9819
|
+
return a.id.localeCompare(b.id);
|
|
9820
|
+
}
|
|
9821
|
+
async function mapWithConcurrency(items, concurrency, fn) {
|
|
9822
|
+
if (items.length === 0) return [];
|
|
9823
|
+
const out = new Array(items.length);
|
|
9824
|
+
let next = 0;
|
|
9825
|
+
const workerCount = Math.min(Math.max(1, concurrency), items.length);
|
|
9826
|
+
const workers = Array.from({ length: workerCount }, async () => {
|
|
9827
|
+
for (; ; ) {
|
|
9828
|
+
const idx = next++;
|
|
9829
|
+
if (idx >= items.length) return;
|
|
9830
|
+
const item = items[idx];
|
|
9831
|
+
if (item !== void 0) out[idx] = await fn(item);
|
|
9832
|
+
}
|
|
9833
|
+
});
|
|
9834
|
+
await Promise.all(workers);
|
|
9835
|
+
return out;
|
|
9836
|
+
}
|
|
9444
9837
|
|
|
9445
9838
|
// src/storage/queue-store.ts
|
|
9446
9839
|
init_atomic_write();
|
|
@@ -10016,6 +10409,20 @@ var DefaultMemoryStore = class {
|
|
|
10016
10409
|
*/
|
|
10017
10410
|
persistBackup;
|
|
10018
10411
|
backupDir;
|
|
10412
|
+
/**
|
|
10413
|
+
* Per-scope tracked byte sizes — incremented on `remember()`, decremented on
|
|
10414
|
+
* `forget()`, recalculated after `consolidate()`. Eliminates the redundant
|
|
10415
|
+
* readAll() call that previously checked the file size after every write.
|
|
10416
|
+
*/
|
|
10417
|
+
_trackedByteSizes = {};
|
|
10418
|
+
/** Result cache for scoreRelevant() — keyed by scope + context hash, TTL 30s. */
|
|
10419
|
+
_scoreCache = /* @__PURE__ */ new Map();
|
|
10420
|
+
/**
|
|
10421
|
+
* Per-entry cached lowercase strings — computed once per scoreRelevant() call,
|
|
10422
|
+
* stored here so repeated scoring of the same entries avoids re-computation.
|
|
10423
|
+
* Cleared on every mutation (remember/forget/consolidate/clear).
|
|
10424
|
+
*/
|
|
10425
|
+
_cachedLower = null;
|
|
10019
10426
|
constructor(opts) {
|
|
10020
10427
|
this.files = {
|
|
10021
10428
|
"project-agents": opts.paths.inProjectAgentsFile,
|
|
@@ -10049,6 +10456,20 @@ var DefaultMemoryStore = class {
|
|
|
10049
10456
|
}
|
|
10050
10457
|
}
|
|
10051
10458
|
}
|
|
10459
|
+
/**
|
|
10460
|
+
* Recalculate the tracked byte size for a scope by re-reading the file and
|
|
10461
|
+
* summing the serialized byte length of each line. Called after consolidate()
|
|
10462
|
+
* (which modifies the file) to keep the tracker accurate.
|
|
10463
|
+
*/
|
|
10464
|
+
async _recalcTrackedByteSize(scope) {
|
|
10465
|
+
const raw = await this.backend.readAll(scope, this.files[scope]);
|
|
10466
|
+
let total = 0;
|
|
10467
|
+
for (const line of raw.split("\n")) {
|
|
10468
|
+
if (line.trim()) total += Buffer.byteLength(line, "utf8");
|
|
10469
|
+
}
|
|
10470
|
+
this._trackedByteSizes[scope] = total;
|
|
10471
|
+
return total;
|
|
10472
|
+
}
|
|
10052
10473
|
async readAll() {
|
|
10053
10474
|
const parts = [];
|
|
10054
10475
|
for (const scope of ["project-agents", "project-memory", "user-memory"]) {
|
|
@@ -10149,6 +10570,7 @@ ${body.trim()}`);
|
|
|
10149
10570
|
const t0 = Date.now();
|
|
10150
10571
|
try {
|
|
10151
10572
|
await this.backend.remember(scope, entry, filePath);
|
|
10573
|
+
this._scoreCache.clear();
|
|
10152
10574
|
const dur = Date.now() - t0;
|
|
10153
10575
|
this.events?.emit("storage.write", {
|
|
10154
10576
|
sessionId: "~memory~",
|
|
@@ -10173,16 +10595,21 @@ ${body.trim()}`);
|
|
|
10173
10595
|
});
|
|
10174
10596
|
throw err;
|
|
10175
10597
|
}
|
|
10176
|
-
|
|
10177
|
-
if (
|
|
10598
|
+
let trackedSize = this._trackedByteSizes[scope];
|
|
10599
|
+
if (trackedSize === void 0) {
|
|
10600
|
+
trackedSize = await this._recalcTrackedByteSize(scope);
|
|
10601
|
+
}
|
|
10602
|
+
if (trackedSize > MAX_BYTES_TOTAL) {
|
|
10178
10603
|
const removed = await this.backend.consolidate(scope, this.files[scope]);
|
|
10179
10604
|
if (removed > 0) {
|
|
10180
10605
|
this.events?.emit("memory.consolidated", {
|
|
10181
10606
|
scope,
|
|
10182
10607
|
removed
|
|
10183
10608
|
});
|
|
10609
|
+
await this._recalcTrackedByteSize(scope);
|
|
10184
10610
|
}
|
|
10185
10611
|
}
|
|
10612
|
+
this._trackedByteSizes[scope] = (this._trackedByteSizes[scope] ?? 0) + Buffer.byteLength(JSON.stringify(entry), "utf8");
|
|
10186
10613
|
await this.mirrorBackup(scope);
|
|
10187
10614
|
this.events?.emit("memory.remembered", {
|
|
10188
10615
|
scope,
|
|
@@ -10201,16 +10628,30 @@ ${body.trim()}`);
|
|
|
10201
10628
|
async scoreRelevant(ctx, scope = "project-memory", limit = 8) {
|
|
10202
10629
|
const all = await this.list(scope);
|
|
10203
10630
|
if (all.length === 0) return [];
|
|
10631
|
+
const ctxHash = `${scope}|${ctx.currentTask}|${(ctx.activeSkills ?? []).join(",")}|${(ctx.toolNames ?? []).join(",")}`;
|
|
10632
|
+
const now = Date.now();
|
|
10633
|
+
const TTL_MS = 3e4;
|
|
10634
|
+
const cached = this._scoreCache.get(ctxHash);
|
|
10635
|
+
if (cached && cached.expiresAt > now && cached.entries === all) {
|
|
10636
|
+
return cached.scored.slice(0, Math.min(limit, 15));
|
|
10637
|
+
}
|
|
10204
10638
|
const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
10205
10639
|
const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
|
|
10206
10640
|
const toolWords = (ctx.toolNames ?? []).flatMap((t2) => t2.toLowerCase().split("_"));
|
|
10207
|
-
|
|
10641
|
+
this._cachedLower = /* @__PURE__ */ new WeakMap();
|
|
10208
10642
|
const scored = [];
|
|
10209
10643
|
for (const entry of all) {
|
|
10210
10644
|
let score = 0;
|
|
10211
10645
|
const reasons = [];
|
|
10212
|
-
|
|
10213
|
-
|
|
10646
|
+
let cachedLower = this._cachedLower.get(entry);
|
|
10647
|
+
if (!cachedLower) {
|
|
10648
|
+
cachedLower = {
|
|
10649
|
+
textLower: entry.text.toLowerCase(),
|
|
10650
|
+
tagsLower: (entry.tags ?? []).map((t2) => t2.toLowerCase())
|
|
10651
|
+
};
|
|
10652
|
+
this._cachedLower.set(entry, cachedLower);
|
|
10653
|
+
}
|
|
10654
|
+
const { textLower, tagsLower } = cachedLower;
|
|
10214
10655
|
let taskHits = 0;
|
|
10215
10656
|
for (const w of taskWords) {
|
|
10216
10657
|
if (textLower.includes(w)) {
|
|
@@ -10296,6 +10737,7 @@ ${body.trim()}`);
|
|
|
10296
10737
|
const relevant = scored.filter(
|
|
10297
10738
|
(s) => s.score >= threshold || s.priority === "critical" || s.priority === "high"
|
|
10298
10739
|
);
|
|
10740
|
+
this._scoreCache.set(ctxHash, { entries: all, scored: relevant, expiresAt: now + TTL_MS });
|
|
10299
10741
|
return relevant.slice(0, Math.min(limit, 15));
|
|
10300
10742
|
}
|
|
10301
10743
|
async forget(query, scope = "project-memory") {
|
|
@@ -10305,6 +10747,7 @@ ${body.trim()}`);
|
|
|
10305
10747
|
let removed = 0;
|
|
10306
10748
|
try {
|
|
10307
10749
|
removed = await this.backend.forget(scope, query, filePath);
|
|
10750
|
+
this._scoreCache.clear();
|
|
10308
10751
|
const dur = Date.now() - t0;
|
|
10309
10752
|
this.events?.emit("storage.write", {
|
|
10310
10753
|
sessionId: "~memory~",
|
|
@@ -10336,6 +10779,7 @@ ${body.trim()}`);
|
|
|
10336
10779
|
removed
|
|
10337
10780
|
});
|
|
10338
10781
|
await this.mirrorBackup(scope);
|
|
10782
|
+
await this._recalcTrackedByteSize(scope);
|
|
10339
10783
|
}
|
|
10340
10784
|
return removed;
|
|
10341
10785
|
});
|
|
@@ -10347,6 +10791,7 @@ ${body.trim()}`);
|
|
|
10347
10791
|
let removed = 0;
|
|
10348
10792
|
try {
|
|
10349
10793
|
removed = await this.backend.consolidate(scope, filePath);
|
|
10794
|
+
this._scoreCache.clear();
|
|
10350
10795
|
const dur = Date.now() - t0;
|
|
10351
10796
|
this.events?.emit("storage.write", {
|
|
10352
10797
|
sessionId: "~memory~",
|
|
@@ -10377,6 +10822,7 @@ ${body.trim()}`);
|
|
|
10377
10822
|
removed
|
|
10378
10823
|
});
|
|
10379
10824
|
await this.mirrorBackup(scope);
|
|
10825
|
+
await this._recalcTrackedByteSize(scope);
|
|
10380
10826
|
}
|
|
10381
10827
|
});
|
|
10382
10828
|
}
|
|
@@ -10387,6 +10833,7 @@ ${body.trim()}`);
|
|
|
10387
10833
|
const t0 = Date.now();
|
|
10388
10834
|
try {
|
|
10389
10835
|
await this.backend.clear(scope, filePath);
|
|
10836
|
+
this._scoreCache.clear();
|
|
10390
10837
|
const dur = Date.now() - t0;
|
|
10391
10838
|
this.events?.emit("storage.write", {
|
|
10392
10839
|
sessionId: "~memory~",
|
|
@@ -10413,6 +10860,7 @@ ${body.trim()}`);
|
|
|
10413
10860
|
}
|
|
10414
10861
|
this.events?.emit("memory.cleared", { scope });
|
|
10415
10862
|
await this.mirrorBackup(scope);
|
|
10863
|
+
this._trackedByteSizes[scope] = 0;
|
|
10416
10864
|
});
|
|
10417
10865
|
return;
|
|
10418
10866
|
}
|
|
@@ -10470,8 +10918,8 @@ ${body.trim()}`);
|
|
|
10470
10918
|
if (!this.persistBackup || scope === "project-agents") return;
|
|
10471
10919
|
try {
|
|
10472
10920
|
const content = await this.backend.readAll(scope, this.files[scope]);
|
|
10473
|
-
const { writeFile: writeFile14, mkdir:
|
|
10474
|
-
await
|
|
10921
|
+
const { writeFile: writeFile14, mkdir: mkdir24 } = await import('fs/promises');
|
|
10922
|
+
await mkdir24(this.backupDir, { recursive: true });
|
|
10475
10923
|
await writeFile14(`${this.backupDir}/${scope}.md`, content, "utf8");
|
|
10476
10924
|
} catch {
|
|
10477
10925
|
}
|
|
@@ -10657,6 +11105,51 @@ var defaultIndexing = {
|
|
|
10657
11105
|
watchExternal: true,
|
|
10658
11106
|
debounceMs: 400
|
|
10659
11107
|
};
|
|
11108
|
+
var IN_PROJECT_FORBIDDEN_KEYS = /* @__PURE__ */ new Set([
|
|
11109
|
+
"provider",
|
|
11110
|
+
"apiKey",
|
|
11111
|
+
"baseUrl",
|
|
11112
|
+
"providers",
|
|
11113
|
+
"mcpServers",
|
|
11114
|
+
"hooks",
|
|
11115
|
+
"plugins",
|
|
11116
|
+
"sync",
|
|
11117
|
+
"yolo",
|
|
11118
|
+
"extensions"
|
|
11119
|
+
]);
|
|
11120
|
+
function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => console.warn(msg)) {
|
|
11121
|
+
const stripped = [];
|
|
11122
|
+
const out = {};
|
|
11123
|
+
for (const [k, v] of Object.entries(inProject)) {
|
|
11124
|
+
if (IN_PROJECT_FORBIDDEN_KEYS.has(k)) {
|
|
11125
|
+
stripped.push(k);
|
|
11126
|
+
continue;
|
|
11127
|
+
}
|
|
11128
|
+
out[k] = v;
|
|
11129
|
+
}
|
|
11130
|
+
if (stripped.length > 0) {
|
|
11131
|
+
warn(
|
|
11132
|
+
JSON.stringify({
|
|
11133
|
+
level: "warn",
|
|
11134
|
+
event: "config.in_project_unsafe_fields_ignored",
|
|
11135
|
+
path: sourcePath,
|
|
11136
|
+
ignoredKeys: stripped,
|
|
11137
|
+
message: `Ignored ${stripped.length} unsafe field(s) from the repo-committed config "${sourcePath}": ${stripped.join(", ")}. These can only be set in your personal ~/.wrongstack/config.json, not in a project-committed file.`,
|
|
11138
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11139
|
+
})
|
|
11140
|
+
);
|
|
11141
|
+
}
|
|
11142
|
+
return out;
|
|
11143
|
+
}
|
|
11144
|
+
function samePath(a, b) {
|
|
11145
|
+
let ra = path3.resolve(a);
|
|
11146
|
+
let rb = path3.resolve(b);
|
|
11147
|
+
if (process.platform === "win32" || process.platform === "darwin") {
|
|
11148
|
+
ra = ra.toLowerCase();
|
|
11149
|
+
rb = rb.toLowerCase();
|
|
11150
|
+
}
|
|
11151
|
+
return ra === rb;
|
|
11152
|
+
}
|
|
10660
11153
|
function deepMerge2(base, patch) {
|
|
10661
11154
|
const opts = { arrayMode: "concat-primitives" };
|
|
10662
11155
|
if (envBoolOptional(process.env.WRONGSTACK_DEBUG_CONFIG)) {
|
|
@@ -10685,14 +11178,15 @@ var DefaultConfigLoader = class {
|
|
|
10685
11178
|
}
|
|
10686
11179
|
async load(opts = {}) {
|
|
10687
11180
|
let cfg = { ...BEHAVIOR_DEFAULTS };
|
|
11181
|
+
const inProjectCollides = samePath(this.paths.inProjectConfig, this.paths.globalConfig) || samePath(this.paths.inProjectConfig, this.paths.projectLocalConfig);
|
|
10688
11182
|
const [global, local, inProject] = await Promise.all([
|
|
10689
11183
|
this.readJson(this.paths.globalConfig),
|
|
10690
11184
|
this.readJson(this.paths.projectLocalConfig),
|
|
10691
|
-
this.readJson(this.paths.inProjectConfig)
|
|
11185
|
+
inProjectCollides ? Promise.resolve({}) : this.readJson(this.paths.inProjectConfig)
|
|
10692
11186
|
]);
|
|
10693
11187
|
cfg = deepMerge2(cfg, global);
|
|
10694
11188
|
cfg = deepMerge2(cfg, local);
|
|
10695
|
-
cfg = deepMerge2(cfg, inProject);
|
|
11189
|
+
cfg = deepMerge2(cfg, stripUnsafeInProjectFields(inProject, this.paths.inProjectConfig));
|
|
10696
11190
|
for (const [key, fn] of Object.entries(ENV_MAP)) {
|
|
10697
11191
|
const v = process.env[key];
|
|
10698
11192
|
if (v) fn(cfg, v);
|
|
@@ -12085,6 +12579,9 @@ function isClearlyDestructiveBashCommand(command, projectRoot) {
|
|
|
12085
12579
|
}
|
|
12086
12580
|
|
|
12087
12581
|
// src/security/permission-policy.ts
|
|
12582
|
+
function matchesTrust(patterns, subject) {
|
|
12583
|
+
return patterns.includes(subject) || matchAny(patterns, subject);
|
|
12584
|
+
}
|
|
12088
12585
|
var DefaultPermissionPolicy = class {
|
|
12089
12586
|
policy = {};
|
|
12090
12587
|
loaded = false;
|
|
@@ -12221,7 +12718,7 @@ var DefaultPermissionPolicy = class {
|
|
|
12221
12718
|
this._evalCache.set(cacheKey, decision);
|
|
12222
12719
|
return decision;
|
|
12223
12720
|
}
|
|
12224
|
-
if (entry?.deny && subject &&
|
|
12721
|
+
if (entry?.deny && subject && matchesTrust(entry.deny, subject)) {
|
|
12225
12722
|
const decision = { permission: "deny", source: "deny", reason: "matched deny pattern" };
|
|
12226
12723
|
this._evalCache.set(cacheKey, decision);
|
|
12227
12724
|
return decision;
|
|
@@ -12231,7 +12728,7 @@ var DefaultPermissionPolicy = class {
|
|
|
12231
12728
|
this._evalCache.set(cacheKey, decision);
|
|
12232
12729
|
return decision;
|
|
12233
12730
|
}
|
|
12234
|
-
if (entry?.allow && subject &&
|
|
12731
|
+
if (entry?.allow && subject && matchesTrust(entry.allow, subject)) {
|
|
12235
12732
|
const decision = { permission: "auto", source: "trust", reason: "matched allow pattern" };
|
|
12236
12733
|
this._evalCache.set(cacheKey, decision);
|
|
12237
12734
|
return decision;
|
|
@@ -12767,6 +13264,16 @@ function handleMessageStop(state, ev) {
|
|
|
12767
13264
|
async function streamProviderToResponse(provider, req, signal, ctx, events, logger) {
|
|
12768
13265
|
const state = createStreamingState(req.model);
|
|
12769
13266
|
logger.debug("Stream started", { providerId: provider.id, model: req.model });
|
|
13267
|
+
const TEXT_BATCH_SIZE = 4;
|
|
13268
|
+
let pendingText = "";
|
|
13269
|
+
let pendingCount = 0;
|
|
13270
|
+
const flushText = () => {
|
|
13271
|
+
if (pendingCount > 0) {
|
|
13272
|
+
events.emit("provider.text_delta", { ctx, text: pendingText });
|
|
13273
|
+
pendingText = "";
|
|
13274
|
+
pendingCount = 0;
|
|
13275
|
+
}
|
|
13276
|
+
};
|
|
12770
13277
|
const iter = provider.stream(req, { signal })[Symbol.asyncIterator]();
|
|
12771
13278
|
try {
|
|
12772
13279
|
for (; ; ) {
|
|
@@ -12786,9 +13293,12 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
|
|
|
12786
13293
|
break;
|
|
12787
13294
|
case "text_delta":
|
|
12788
13295
|
handleTextDelta(state, ev.text);
|
|
12789
|
-
|
|
13296
|
+
pendingText += ev.text;
|
|
13297
|
+
pendingCount++;
|
|
13298
|
+
if (pendingCount >= TEXT_BATCH_SIZE) flushText();
|
|
12790
13299
|
break;
|
|
12791
13300
|
case "tool_use_start": {
|
|
13301
|
+
flushText();
|
|
12792
13302
|
const idVal = ev.id;
|
|
12793
13303
|
const nameVal = ev.name;
|
|
12794
13304
|
handleToolUseStart(state, { id: idVal, name: nameVal });
|
|
@@ -12800,6 +13310,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
|
|
|
12800
13310
|
handleToolUseInputDelta(state, ev);
|
|
12801
13311
|
break;
|
|
12802
13312
|
case "tool_use_stop": {
|
|
13313
|
+
flushText();
|
|
12803
13314
|
const stoppedName = state.tools.get(ev.id)?.name ?? "unknown";
|
|
12804
13315
|
handleToolUseStop(state, ev);
|
|
12805
13316
|
events.emit("provider.tool_use_stop", { ctx, id: ev.id, name: stoppedName });
|
|
@@ -12809,6 +13320,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
|
|
|
12809
13320
|
handleThinkingStart(state, ev);
|
|
12810
13321
|
break;
|
|
12811
13322
|
case "thinking_delta":
|
|
13323
|
+
flushText();
|
|
12812
13324
|
handleThinkingDelta(state, ev.text);
|
|
12813
13325
|
events.emit("provider.thinking_delta", { ctx, text: ev.text });
|
|
12814
13326
|
break;
|
|
@@ -12840,6 +13352,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
|
|
|
12840
13352
|
eventType: String(evAny.type),
|
|
12841
13353
|
errorMessage: errMsg
|
|
12842
13354
|
});
|
|
13355
|
+
flushText();
|
|
12843
13356
|
events.emit("provider.stream_error", {
|
|
12844
13357
|
ctx,
|
|
12845
13358
|
eventType: String(evAny.type),
|
|
@@ -12850,6 +13363,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
|
|
|
12850
13363
|
} catch (err) {
|
|
12851
13364
|
if (signal.aborted) {
|
|
12852
13365
|
state.stopReason = "end_turn";
|
|
13366
|
+
flushText();
|
|
12853
13367
|
logger.debug("Stream aborted \u2014 returning partial state", {
|
|
12854
13368
|
providerId: provider.id,
|
|
12855
13369
|
model: req.model,
|
|
@@ -12868,8 +13382,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
|
|
|
12868
13382
|
});
|
|
12869
13383
|
await Promise.race([
|
|
12870
13384
|
drainPromise,
|
|
12871
|
-
new Promise((
|
|
12872
|
-
drainTimer = setTimeout(
|
|
13385
|
+
new Promise((resolve19) => {
|
|
13386
|
+
drainTimer = setTimeout(resolve19, STREAM_DRAIN_TIMEOUT_MS);
|
|
12873
13387
|
})
|
|
12874
13388
|
]);
|
|
12875
13389
|
} finally {
|
|
@@ -12878,6 +13392,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
|
|
|
12878
13392
|
} catch {
|
|
12879
13393
|
}
|
|
12880
13394
|
}
|
|
13395
|
+
flushText();
|
|
12881
13396
|
logger.debug("Stream completed", {
|
|
12882
13397
|
providerId: provider.id,
|
|
12883
13398
|
model: req.model,
|
|
@@ -12975,7 +13490,7 @@ async function runProviderWithRetry(opts) {
|
|
|
12975
13490
|
description
|
|
12976
13491
|
});
|
|
12977
13492
|
}
|
|
12978
|
-
await new Promise((
|
|
13493
|
+
await new Promise((resolve19, reject) => {
|
|
12979
13494
|
let settled = false;
|
|
12980
13495
|
const cleanup = () => {
|
|
12981
13496
|
clearTimeout(t2);
|
|
@@ -12991,7 +13506,7 @@ async function runProviderWithRetry(opts) {
|
|
|
12991
13506
|
if (settled) return;
|
|
12992
13507
|
settled = true;
|
|
12993
13508
|
cleanup();
|
|
12994
|
-
|
|
13509
|
+
resolve19();
|
|
12995
13510
|
}, delay);
|
|
12996
13511
|
if (signal.aborted) {
|
|
12997
13512
|
onAbort();
|
|
@@ -14734,7 +15249,14 @@ var EternalAutonomyEngine = class {
|
|
|
14734
15249
|
try {
|
|
14735
15250
|
const reloaded = await loadGoal(this.goalPath, this.opts.events);
|
|
14736
15251
|
iterationIndex = reloaded?.iterations ?? 0;
|
|
14737
|
-
} catch {
|
|
15252
|
+
} catch (err) {
|
|
15253
|
+
console.error(JSON.stringify({
|
|
15254
|
+
level: "warn",
|
|
15255
|
+
event: "autonomy.goal_reload_failed",
|
|
15256
|
+
message: toErrorMessage(err),
|
|
15257
|
+
context: { goalPath: this.goalPath },
|
|
15258
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
15259
|
+
}));
|
|
14738
15260
|
}
|
|
14739
15261
|
this.opts.onIteration?.({
|
|
14740
15262
|
at: (this.opts.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -14969,7 +15491,14 @@ ${lastFew}` : "No prior iterations yet.",
|
|
|
14969
15491
|
} finally {
|
|
14970
15492
|
clearTimeout(timer);
|
|
14971
15493
|
}
|
|
14972
|
-
} catch {
|
|
15494
|
+
} catch (err) {
|
|
15495
|
+
console.error(JSON.stringify({
|
|
15496
|
+
level: "warn",
|
|
15497
|
+
event: "autonomy.brainstorm_failed",
|
|
15498
|
+
message: toErrorMessage(err),
|
|
15499
|
+
context: { goal: goal.goal.slice(0, 100) },
|
|
15500
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
15501
|
+
}));
|
|
14973
15502
|
return null;
|
|
14974
15503
|
}
|
|
14975
15504
|
}
|
|
@@ -15492,13 +16021,13 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
15492
16021
|
if (!bus?.hasListenerFor("budget.threshold_reached")) {
|
|
15493
16022
|
return Promise.resolve("stop");
|
|
15494
16023
|
}
|
|
15495
|
-
return new Promise((
|
|
16024
|
+
return new Promise((resolve19) => {
|
|
15496
16025
|
let resolved = false;
|
|
15497
16026
|
const respond = (d) => {
|
|
15498
16027
|
if (resolved) return;
|
|
15499
16028
|
resolved = true;
|
|
15500
16029
|
clearTimeout(fallback);
|
|
15501
|
-
|
|
16030
|
+
resolve19(d);
|
|
15502
16031
|
};
|
|
15503
16032
|
const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
|
|
15504
16033
|
bus.emit("budget.threshold_reached", {
|
|
@@ -18849,6 +19378,68 @@ Working rules:
|
|
|
18849
19378
|
- When in doubt, flag as medium rather than ignoring potential issues`
|
|
18850
19379
|
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
18851
19380
|
};
|
|
19381
|
+
var SHADOW_AGENT = {
|
|
19382
|
+
id: "shadow-agent",
|
|
19383
|
+
name: "Shadow",
|
|
19384
|
+
role: "shadow-agent",
|
|
19385
|
+
prompt: `You are the Shadow Agent \u2014 a silent background monitor for the WrongStack fleet.
|
|
19386
|
+
|
|
19387
|
+
Your job is to observe, detect anomalies, and be ready to intervene \u2014 but only when commanded.
|
|
19388
|
+
|
|
19389
|
+
## Core Responsibilities
|
|
19390
|
+
|
|
19391
|
+
1. **Fleet Monitoring** (every 30s)
|
|
19392
|
+
- Call \`fleet_status\` + \`fleet_health\` on each heartbeat
|
|
19393
|
+
- Track what each agent is doing (task descriptions)
|
|
19394
|
+
- Detect stuck agents (>5min no events), idle agents, crashed agents
|
|
19395
|
+
|
|
19396
|
+
2. **FleetBus Subscription**
|
|
19397
|
+
- Subscribe to \`subagent.*\` events to track lifecycle
|
|
19398
|
+
- Subscribe to \`tool.executed\` to monitor activity
|
|
19399
|
+
- Track agent joins (subagent.started) and leaves (subagent.stopped)
|
|
19400
|
+
|
|
19401
|
+
3. **Mailbox Surveillance**
|
|
19402
|
+
- Monitor for \`control\` type messages starting with "hoop"
|
|
19403
|
+
- Detect orphan assigns (assign without result within 5min)
|
|
19404
|
+
- Cross-session awareness via shared project mailbox
|
|
19405
|
+
|
|
19406
|
+
4. **Spike Detection**
|
|
19407
|
+
- Track task duration per agent
|
|
19408
|
+
- Flag agents that spawn and die within <5 seconds
|
|
19409
|
+
- Log spike events with reason (completed/error/killed/timeout)
|
|
19410
|
+
|
|
19411
|
+
5. **Intervention Commands**
|
|
19412
|
+
Parse these from mailbox control messages:
|
|
19413
|
+
- \`hoop <agentId>\` \u2014 terminate specific agent
|
|
19414
|
+
- \`hoop all\` \u2014 terminate all running agents
|
|
19415
|
+
- \`shadow status\` \u2014 report current fleet snapshot
|
|
19416
|
+
- \`shadow mute\` \u2014 pause heartbeat monitoring
|
|
19417
|
+
- \`shadow resume\` \u2014 resume heartbeat monitoring
|
|
19418
|
+
- \`shadow interval <ms>\` \u2014 change heartbeat interval
|
|
19419
|
+
- \`shadow model <model-id>\` \u2014 change analysis model
|
|
19420
|
+
|
|
19421
|
+
## Operating Rules
|
|
19422
|
+
|
|
19423
|
+
- **Silent by default**: Use DEBUG level logging unless anomaly detected
|
|
19424
|
+
- **Deterministic**: Same state always produces same actions \u2014 no randomness
|
|
19425
|
+
- **Report on anomaly**: When anomaly detected, use \`mail_send\` to broadcast warning
|
|
19426
|
+
- **Never auto-intervene**: Always report unless explicitly commanded
|
|
19427
|
+
- **Minimal footprint**: Small state, efficient snapshots
|
|
19428
|
+
|
|
19429
|
+
## Startup Sequence
|
|
19430
|
+
|
|
19431
|
+
1. Send broadcast: \`shadow:started { intervalMs, model, startTime }\`
|
|
19432
|
+
2. Subscribe to FleetBus for all relevant events
|
|
19433
|
+
3. Schedule heartbeat cron job at configured interval
|
|
19434
|
+
4. Wait for commands or anomalies
|
|
19435
|
+
|
|
19436
|
+
## Shutdown Sequence
|
|
19437
|
+
|
|
19438
|
+
1. Cancel all cron jobs (\`cron_cancel\`)
|
|
19439
|
+
2. Send broadcast: \`shadow:stopped { reason, finalState }\`
|
|
19440
|
+
3. Clean up FleetBus subscriptions`
|
|
19441
|
+
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
19442
|
+
};
|
|
18852
19443
|
var CRITIC_AGENT = {
|
|
18853
19444
|
id: "critic",
|
|
18854
19445
|
name: "Critic",
|
|
@@ -18889,6 +19480,7 @@ var FLEET_ROSTER = {
|
|
|
18889
19480
|
"refactor-planner": REFACTOR_PLANNER_AGENT,
|
|
18890
19481
|
"security-scanner": SECURITY_SCANNER_AGENT,
|
|
18891
19482
|
"critic": CRITIC_AGENT,
|
|
19483
|
+
"shadow-agent": SHADOW_AGENT,
|
|
18892
19484
|
...Object.fromEntries(
|
|
18893
19485
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.config])
|
|
18894
19486
|
)
|
|
@@ -18900,6 +19492,8 @@ var FLEET_ROSTER_BUDGETS = {
|
|
|
18900
19492
|
"refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
|
|
18901
19493
|
"security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
|
|
18902
19494
|
"critic": { timeoutMs: 5 * 60 * 60 * 1e3, maxIterations: 4e3, maxToolCalls: 12e3 },
|
|
19495
|
+
"shadow-agent": { timeoutMs: 24 * 60 * 60 * 1e3, maxIterations: 1e4, maxToolCalls: 5e3 },
|
|
19496
|
+
// Long-running background monitor
|
|
18903
19497
|
...Object.fromEntries(
|
|
18904
19498
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
|
|
18905
19499
|
)
|
|
@@ -19338,7 +19932,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19338
19932
|
taskIds.map((id) => {
|
|
19339
19933
|
const cached = this.completedResults.find((r) => r.taskId === id);
|
|
19340
19934
|
if (cached) return cached;
|
|
19341
|
-
return new Promise((
|
|
19935
|
+
return new Promise((resolve19, reject) => {
|
|
19342
19936
|
const timeout = setTimeout(() => {
|
|
19343
19937
|
this.off("task.completed", handler);
|
|
19344
19938
|
reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
|
|
@@ -19347,7 +19941,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19347
19941
|
if (result.taskId === id) {
|
|
19348
19942
|
clearTimeout(timeout);
|
|
19349
19943
|
this.off("task.completed", handler);
|
|
19350
|
-
|
|
19944
|
+
resolve19(result);
|
|
19351
19945
|
}
|
|
19352
19946
|
};
|
|
19353
19947
|
this.on("task.completed", handler);
|
|
@@ -19615,12 +20209,12 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19615
20209
|
}
|
|
19616
20210
|
return new Promise((resolveDecision) => {
|
|
19617
20211
|
let settled = false;
|
|
19618
|
-
const
|
|
20212
|
+
const resolve19 = (d) => {
|
|
19619
20213
|
if (settled) return;
|
|
19620
20214
|
settled = true;
|
|
19621
20215
|
resolveDecision(d);
|
|
19622
20216
|
};
|
|
19623
|
-
const fallback = setTimeout(() =>
|
|
20217
|
+
const fallback = setTimeout(() => resolve19("stop"), DECISION_TIMEOUT_MS);
|
|
19624
20218
|
budget._events?.emit("budget.threshold_reached", {
|
|
19625
20219
|
kind: "timeout",
|
|
19626
20220
|
used,
|
|
@@ -19636,11 +20230,11 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19636
20230
|
// disagreeing, resolves as a stop). Async grants still resolve.
|
|
19637
20231
|
extend: (extra) => {
|
|
19638
20232
|
clearTimeout(fallback);
|
|
19639
|
-
queueMicrotask(() =>
|
|
20233
|
+
queueMicrotask(() => resolve19({ extend: extra }));
|
|
19640
20234
|
},
|
|
19641
20235
|
deny: () => {
|
|
19642
20236
|
clearTimeout(fallback);
|
|
19643
|
-
|
|
20237
|
+
resolve19("stop");
|
|
19644
20238
|
}
|
|
19645
20239
|
});
|
|
19646
20240
|
});
|
|
@@ -20107,7 +20701,14 @@ ${personaLine}Task: ${task}
|
|
|
20107
20701
|
subagentIds.push(subagentId);
|
|
20108
20702
|
taskIds.push(taskId);
|
|
20109
20703
|
await coordinator.assign(spec);
|
|
20110
|
-
} catch {
|
|
20704
|
+
} catch (err) {
|
|
20705
|
+
console.error(JSON.stringify({
|
|
20706
|
+
level: "warn",
|
|
20707
|
+
event: "parallel_engine.spawn_failed",
|
|
20708
|
+
message: toErrorMessage(err),
|
|
20709
|
+
context: { slot: i, task, subagentId },
|
|
20710
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
20711
|
+
}));
|
|
20111
20712
|
}
|
|
20112
20713
|
})()
|
|
20113
20714
|
);
|
|
@@ -20132,7 +20733,14 @@ ${personaLine}Task: ${task}
|
|
|
20132
20733
|
} finally {
|
|
20133
20734
|
clearTimeout(timer);
|
|
20134
20735
|
}
|
|
20135
|
-
} catch {
|
|
20736
|
+
} catch (err) {
|
|
20737
|
+
console.error(JSON.stringify({
|
|
20738
|
+
level: "warn",
|
|
20739
|
+
event: "parallel_engine.brainstorm_results_failed",
|
|
20740
|
+
message: toErrorMessage(err),
|
|
20741
|
+
context: { slotCount, taskIds },
|
|
20742
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
20743
|
+
}));
|
|
20136
20744
|
results = coordinator.results().slice(-taskIds.length);
|
|
20137
20745
|
}
|
|
20138
20746
|
await Promise.allSettled(subagentIds.map((id) => coordinator.remove(id)));
|
|
@@ -20166,7 +20774,14 @@ ${personaLine}Task: ${task}
|
|
|
20166
20774
|
if (file) tasks.push(`[git] inspect and fix: ${file}`);
|
|
20167
20775
|
}
|
|
20168
20776
|
}
|
|
20169
|
-
} catch {
|
|
20777
|
+
} catch (err) {
|
|
20778
|
+
console.error(JSON.stringify({
|
|
20779
|
+
level: "warn",
|
|
20780
|
+
event: "parallel_engine.git_status_failed",
|
|
20781
|
+
message: toErrorMessage(err),
|
|
20782
|
+
context: { projectRoot: this.opts.projectRoot },
|
|
20783
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
20784
|
+
}));
|
|
20170
20785
|
}
|
|
20171
20786
|
}
|
|
20172
20787
|
if (tasks.length < this.slots) {
|
|
@@ -20520,7 +21135,7 @@ var CollabSession = class extends EventEmitter {
|
|
|
20520
21135
|
}
|
|
20521
21136
|
for (const filePath of allFiles) {
|
|
20522
21137
|
try {
|
|
20523
|
-
const [content,
|
|
21138
|
+
const [content, stat16] = await Promise.all([
|
|
20524
21139
|
fsp3.readFile(filePath, "utf8"),
|
|
20525
21140
|
fsp3.stat(filePath)
|
|
20526
21141
|
]);
|
|
@@ -20530,8 +21145,8 @@ var CollabSession = class extends EventEmitter {
|
|
|
20530
21145
|
path: filePath,
|
|
20531
21146
|
content,
|
|
20532
21147
|
language,
|
|
20533
|
-
snapshotMtimeMs:
|
|
20534
|
-
snapshotSizeBytes:
|
|
21148
|
+
snapshotMtimeMs: stat16.mtimeMs,
|
|
21149
|
+
snapshotSizeBytes: stat16.size
|
|
20535
21150
|
});
|
|
20536
21151
|
} catch {
|
|
20537
21152
|
this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
|
|
@@ -20944,9 +21559,9 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
20944
21559
|
for (const file of this.snapshot.files) {
|
|
20945
21560
|
if (file.snapshotMtimeMs === void 0 && file.snapshotSizeBytes === void 0) continue;
|
|
20946
21561
|
try {
|
|
20947
|
-
const
|
|
20948
|
-
const mtimeChanged = file.snapshotMtimeMs !== void 0 &&
|
|
20949
|
-
const sizeChanged = file.snapshotSizeBytes !== void 0 &&
|
|
21562
|
+
const stat16 = await fsp3.stat(file.path);
|
|
21563
|
+
const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat16.mtimeMs > file.snapshotMtimeMs + 1;
|
|
21564
|
+
const sizeChanged = file.snapshotSizeBytes !== void 0 && stat16.size !== file.snapshotSizeBytes;
|
|
20950
21565
|
if (mtimeChanged || sizeChanged) {
|
|
20951
21566
|
warnings.push(`${file.path} changed after the collab snapshot was captured.`);
|
|
20952
21567
|
}
|
|
@@ -22865,11 +23480,11 @@ var Director = class _Director {
|
|
|
22865
23480
|
if (cached) return cached;
|
|
22866
23481
|
const existing = this.taskWaiters.get(id);
|
|
22867
23482
|
if (existing) return existing.promise;
|
|
22868
|
-
let
|
|
23483
|
+
let resolve19;
|
|
22869
23484
|
const promise = new Promise((res) => {
|
|
22870
|
-
|
|
23485
|
+
resolve19 = res;
|
|
22871
23486
|
});
|
|
22872
|
-
this.taskWaiters.set(id, { promise, resolve:
|
|
23487
|
+
this.taskWaiters.set(id, { promise, resolve: resolve19 });
|
|
22873
23488
|
return promise;
|
|
22874
23489
|
})
|
|
22875
23490
|
);
|
|
@@ -23265,7 +23880,7 @@ function createDelegateTool(opts) {
|
|
|
23265
23880
|
subagentId
|
|
23266
23881
|
});
|
|
23267
23882
|
const dir = director;
|
|
23268
|
-
const result = await new Promise((
|
|
23883
|
+
const result = await new Promise((resolve19) => {
|
|
23269
23884
|
let settled = false;
|
|
23270
23885
|
let timer;
|
|
23271
23886
|
const finish = (value) => {
|
|
@@ -23275,7 +23890,7 @@ function createDelegateTool(opts) {
|
|
|
23275
23890
|
offTool();
|
|
23276
23891
|
offIter();
|
|
23277
23892
|
offProgress();
|
|
23278
|
-
|
|
23893
|
+
resolve19(value);
|
|
23279
23894
|
};
|
|
23280
23895
|
const arm = () => {
|
|
23281
23896
|
if (timer) clearTimeout(timer);
|
|
@@ -23707,8 +24322,8 @@ async function loadProjectModes(modesDir) {
|
|
|
23707
24322
|
for (const entry of entries) {
|
|
23708
24323
|
if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
|
|
23709
24324
|
const filePath = path3.join(modesDir, entry);
|
|
23710
|
-
const
|
|
23711
|
-
if (!
|
|
24325
|
+
const stat16 = await fsp3.stat(filePath);
|
|
24326
|
+
if (!stat16.isFile()) continue;
|
|
23712
24327
|
const content = await fsp3.readFile(filePath, "utf8");
|
|
23713
24328
|
const id = path3.basename(entry, path3.extname(entry));
|
|
23714
24329
|
modes.push({
|
|
@@ -25045,10 +25660,10 @@ var AISpecBuilder = class {
|
|
|
25045
25660
|
async saveSession() {
|
|
25046
25661
|
if (!this.sessionPath) return;
|
|
25047
25662
|
try {
|
|
25048
|
-
const
|
|
25049
|
-
const
|
|
25663
|
+
const fsp26 = await import('fs/promises');
|
|
25664
|
+
const path51 = await import('path');
|
|
25050
25665
|
const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
|
|
25051
|
-
await
|
|
25666
|
+
await fsp26.mkdir(path51.dirname(this.sessionPath), { recursive: true });
|
|
25052
25667
|
await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
|
|
25053
25668
|
} catch {
|
|
25054
25669
|
}
|
|
@@ -25057,8 +25672,8 @@ var AISpecBuilder = class {
|
|
|
25057
25672
|
async loadSession() {
|
|
25058
25673
|
if (!this.sessionPath) return false;
|
|
25059
25674
|
try {
|
|
25060
|
-
const
|
|
25061
|
-
const raw = await
|
|
25675
|
+
const fsp26 = await import('fs/promises');
|
|
25676
|
+
const raw = await fsp26.readFile(this.sessionPath, "utf8");
|
|
25062
25677
|
const loaded = JSON.parse(raw);
|
|
25063
25678
|
if (loaded?.id && loaded?.phase && loaded?.title) {
|
|
25064
25679
|
this.session = loaded;
|
|
@@ -25072,8 +25687,8 @@ var AISpecBuilder = class {
|
|
|
25072
25687
|
async deleteSession() {
|
|
25073
25688
|
if (!this.sessionPath) return;
|
|
25074
25689
|
try {
|
|
25075
|
-
const
|
|
25076
|
-
await
|
|
25690
|
+
const fsp26 = await import('fs/promises');
|
|
25691
|
+
await fsp26.unlink(this.sessionPath);
|
|
25077
25692
|
} catch {
|
|
25078
25693
|
}
|
|
25079
25694
|
}
|
|
@@ -25775,15 +26390,15 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
25775
26390
|
maxId = id;
|
|
25776
26391
|
}
|
|
25777
26392
|
}
|
|
25778
|
-
const
|
|
26393
|
+
const path51 = [];
|
|
25779
26394
|
let current = maxId;
|
|
25780
26395
|
const visited = /* @__PURE__ */ new Set();
|
|
25781
26396
|
while (current && !visited.has(current)) {
|
|
25782
26397
|
visited.add(current);
|
|
25783
|
-
|
|
26398
|
+
path51.unshift(current);
|
|
25784
26399
|
current = prev.get(current) ?? null;
|
|
25785
26400
|
}
|
|
25786
|
-
return
|
|
26401
|
+
return path51;
|
|
25787
26402
|
}
|
|
25788
26403
|
function computeParallelGroups(graph, blockedByMap) {
|
|
25789
26404
|
const groups = [];
|
|
@@ -26606,9 +27221,9 @@ var DefaultHealthRegistry = class {
|
|
|
26606
27221
|
}
|
|
26607
27222
|
async runOne(check) {
|
|
26608
27223
|
let timer = null;
|
|
26609
|
-
const timeout = new Promise((
|
|
27224
|
+
const timeout = new Promise((resolve19) => {
|
|
26610
27225
|
timer = setTimeout(
|
|
26611
|
-
() =>
|
|
27226
|
+
() => resolve19({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
|
|
26612
27227
|
this.timeoutMs
|
|
26613
27228
|
);
|
|
26614
27229
|
});
|
|
@@ -26791,7 +27406,7 @@ async function startMetricsServer(opts) {
|
|
|
26791
27406
|
const tls = opts.tls;
|
|
26792
27407
|
const useHttps = !!(tls?.cert && tls?.key);
|
|
26793
27408
|
const host = opts.host ?? "127.0.0.1";
|
|
26794
|
-
const
|
|
27409
|
+
const path51 = opts.path ?? "/metrics";
|
|
26795
27410
|
const healthPath = opts.healthPath ?? "/healthz";
|
|
26796
27411
|
const healthRegistry = opts.healthRegistry;
|
|
26797
27412
|
const listener = (req, res) => {
|
|
@@ -26801,7 +27416,7 @@ async function startMetricsServer(opts) {
|
|
|
26801
27416
|
return;
|
|
26802
27417
|
}
|
|
26803
27418
|
const url = req.url.split("?")[0];
|
|
26804
|
-
if (url ===
|
|
27419
|
+
if (url === path51) {
|
|
26805
27420
|
let body;
|
|
26806
27421
|
try {
|
|
26807
27422
|
body = renderPrometheus(opts.sink.snapshot());
|
|
@@ -26847,14 +27462,14 @@ async function startMetricsServer(opts) {
|
|
|
26847
27462
|
const { createServer } = await import('http');
|
|
26848
27463
|
server = createServer(listener);
|
|
26849
27464
|
}
|
|
26850
|
-
await new Promise((
|
|
27465
|
+
await new Promise((resolve19, reject) => {
|
|
26851
27466
|
const onError = (err) => {
|
|
26852
27467
|
server.off("listening", onListening);
|
|
26853
27468
|
reject(err);
|
|
26854
27469
|
};
|
|
26855
27470
|
const onListening = () => {
|
|
26856
27471
|
server.off("error", onError);
|
|
26857
|
-
|
|
27472
|
+
resolve19();
|
|
26858
27473
|
};
|
|
26859
27474
|
server.once("error", onError);
|
|
26860
27475
|
server.once("listening", onListening);
|
|
@@ -26865,9 +27480,9 @@ async function startMetricsServer(opts) {
|
|
|
26865
27480
|
const protocol = useHttps ? "https" : "http";
|
|
26866
27481
|
return {
|
|
26867
27482
|
port: boundPort,
|
|
26868
|
-
url: `${protocol}://${host}:${boundPort}${
|
|
26869
|
-
close: () => new Promise((
|
|
26870
|
-
server.close((err) => err ? reject(err) :
|
|
27483
|
+
url: `${protocol}://${host}:${boundPort}${path51}`,
|
|
27484
|
+
close: () => new Promise((resolve19, reject) => {
|
|
27485
|
+
server.close((err) => err ? reject(err) : resolve19());
|
|
26871
27486
|
})
|
|
26872
27487
|
};
|
|
26873
27488
|
}
|
|
@@ -27804,13 +28419,13 @@ var SkillInstaller = class {
|
|
|
27804
28419
|
context: { reason: "path_traversal", skillName: skill.name }
|
|
27805
28420
|
});
|
|
27806
28421
|
}
|
|
27807
|
-
const
|
|
27808
|
-
if (
|
|
28422
|
+
const stat16 = await fsp3.stat(srcPath);
|
|
28423
|
+
if (stat16.size > MAX_SKILL_FILE_SIZE) {
|
|
27809
28424
|
throw new FsError({
|
|
27810
|
-
message: `Skill file "${file}" is too large (${(
|
|
28425
|
+
message: `Skill file "${file}" is too large (${(stat16.size / 1024).toFixed(1)}KB). Max: ${MAX_SKILL_FILE_SIZE / 1024}KB`,
|
|
27811
28426
|
code: ERROR_CODES.FS_WRITE_FAILED,
|
|
27812
28427
|
path: srcPath,
|
|
27813
|
-
context: { skillName: skill.name, fileSize:
|
|
28428
|
+
context: { skillName: skill.name, fileSize: stat16.size, maxSize: MAX_SKILL_FILE_SIZE }
|
|
27814
28429
|
});
|
|
27815
28430
|
}
|
|
27816
28431
|
await fsp3.mkdir(path3.dirname(destPath), { recursive: true });
|
|
@@ -28068,6 +28683,12 @@ var GraphMemoryBackend = class {
|
|
|
28068
28683
|
edges = [];
|
|
28069
28684
|
loadedScope = null;
|
|
28070
28685
|
loaded = false;
|
|
28686
|
+
/**
|
|
28687
|
+
* Promise that resolves when the current in-flight _saveGraph completes.
|
|
28688
|
+
* Tests call flush() to await this before deleting the backend or its temp dir.
|
|
28689
|
+
* Each save operation chains onto the previous one so concurrent saves are serialised.
|
|
28690
|
+
*/
|
|
28691
|
+
_saveDone = Promise.resolve();
|
|
28071
28692
|
constructor(opts) {
|
|
28072
28693
|
this.file = new FileMemoryBackend({ paths: opts.paths });
|
|
28073
28694
|
this.graphFile = opts.graphPath ?? `${opts.paths.projectDir}/memory-graph.json`;
|
|
@@ -28094,7 +28715,8 @@ var GraphMemoryBackend = class {
|
|
|
28094
28715
|
tags: entry.tags,
|
|
28095
28716
|
priority: entry.priority
|
|
28096
28717
|
});
|
|
28097
|
-
|
|
28718
|
+
const recentNodes = [...this.nodes.values()].slice(-100);
|
|
28719
|
+
for (const other of recentNodes) {
|
|
28098
28720
|
if (other.id === nodeId) continue;
|
|
28099
28721
|
const sim = wordOverlap(entry.text, other.entry.text);
|
|
28100
28722
|
const tagSim = sharedTags(entry.tags ?? [], other.tags ?? []);
|
|
@@ -28110,7 +28732,8 @@ var GraphMemoryBackend = class {
|
|
|
28110
28732
|
}
|
|
28111
28733
|
}
|
|
28112
28734
|
}
|
|
28113
|
-
|
|
28735
|
+
this._saveDone = this._saveGraph(scope);
|
|
28736
|
+
await this._saveDone;
|
|
28114
28737
|
}
|
|
28115
28738
|
async forget(scope, query, filePath) {
|
|
28116
28739
|
const removed = await this.file.forget(scope, query, filePath);
|
|
@@ -28125,7 +28748,8 @@ var GraphMemoryBackend = class {
|
|
|
28125
28748
|
}
|
|
28126
28749
|
for (const id of toRemove) this.nodes.delete(id);
|
|
28127
28750
|
this.edges = this.edges.filter((e) => !toRemove.includes(e.from) && !toRemove.includes(e.to));
|
|
28128
|
-
|
|
28751
|
+
this._saveDone = this._saveGraph(scope);
|
|
28752
|
+
await this._saveDone;
|
|
28129
28753
|
}
|
|
28130
28754
|
return removed;
|
|
28131
28755
|
}
|
|
@@ -28237,7 +28861,8 @@ var GraphMemoryBackend = class {
|
|
|
28237
28861
|
this.loadedScope = scope;
|
|
28238
28862
|
this.loaded = true;
|
|
28239
28863
|
}
|
|
28240
|
-
|
|
28864
|
+
/** Fire-and-forget graph persistence. Named _saveGraph to signal it must not be awaited. */
|
|
28865
|
+
async _saveGraph(scope) {
|
|
28241
28866
|
this.loadedScope = scope;
|
|
28242
28867
|
this.loaded = true;
|
|
28243
28868
|
try {
|
|
@@ -28245,16 +28870,21 @@ var GraphMemoryBackend = class {
|
|
|
28245
28870
|
nodes: [...this.nodes.entries()],
|
|
28246
28871
|
edges: this.edges
|
|
28247
28872
|
};
|
|
28248
|
-
|
|
28249
|
-
|
|
28250
|
-
{ recursive: true }
|
|
28251
|
-
);
|
|
28873
|
+
const dir = this.graphFile.substring(0, this.graphFile.lastIndexOf("/"));
|
|
28874
|
+
await fsp3.mkdir(dir, { recursive: true });
|
|
28252
28875
|
const tmp = `${this.graphFile}.tmp`;
|
|
28253
28876
|
await fsp3.writeFile(tmp, JSON.stringify(data));
|
|
28254
28877
|
await fsp3.rename(tmp, this.graphFile);
|
|
28255
28878
|
} catch {
|
|
28256
28879
|
}
|
|
28257
28880
|
}
|
|
28881
|
+
/**
|
|
28882
|
+
* Wait for all in-flight _saveGraph operations to complete.
|
|
28883
|
+
* Call this before deleting the backend or its temp directory.
|
|
28884
|
+
*/
|
|
28885
|
+
async flush() {
|
|
28886
|
+
await this._saveDone;
|
|
28887
|
+
}
|
|
28258
28888
|
};
|
|
28259
28889
|
function wordOverlap(a, b) {
|
|
28260
28890
|
const wordsA = new Set(a.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
|
|
@@ -28355,84 +28985,89 @@ var SessionMemoryConsolidator = class {
|
|
|
28355
28985
|
this.minIterations = opts.minIterations ?? 2;
|
|
28356
28986
|
this.maxExistingEntries = opts.maxExistingEntries ?? 15;
|
|
28357
28987
|
}
|
|
28358
|
-
afterRun =
|
|
28988
|
+
afterRun = (ctx, result) => {
|
|
28359
28989
|
if (result.status !== "done") return;
|
|
28360
28990
|
if (!result.finalText || result.finalText.trim().length < 20) return;
|
|
28361
28991
|
if (result.iterations < this.minIterations) return;
|
|
28362
28992
|
const provider = this.provider ?? ctx.provider;
|
|
28363
28993
|
if (!provider?.complete) return;
|
|
28364
|
-
|
|
28365
|
-
|
|
28366
|
-
|
|
28367
|
-
|
|
28368
|
-
|
|
28369
|
-
existingEntries
|
|
28370
|
-
|
|
28371
|
-
|
|
28372
|
-
|
|
28373
|
-
|
|
28374
|
-
|
|
28375
|
-
|
|
28376
|
-
|
|
28377
|
-
|
|
28378
|
-
|
|
28379
|
-
|
|
28380
|
-
|
|
28381
|
-
|
|
28382
|
-
|
|
28383
|
-
|
|
28384
|
-
|
|
28385
|
-
|
|
28386
|
-
|
|
28387
|
-
|
|
28388
|
-
|
|
28389
|
-
|
|
28390
|
-
|
|
28391
|
-
|
|
28392
|
-
|
|
28393
|
-
|
|
28394
|
-
|
|
28395
|
-
|
|
28396
|
-
|
|
28397
|
-
|
|
28398
|
-
|
|
28399
|
-
|
|
28400
|
-
|
|
28401
|
-
|
|
28994
|
+
const _finalText = result.finalText;
|
|
28995
|
+
const _iterations = result.iterations;
|
|
28996
|
+
const _model = this.model ?? ctx.model;
|
|
28997
|
+
void (async () => {
|
|
28998
|
+
try {
|
|
28999
|
+
const existingEntries = await this.memoryStore.list("project-memory", this.maxExistingEntries);
|
|
29000
|
+
const prompt = buildConsolidationPrompt(
|
|
29001
|
+
_finalText,
|
|
29002
|
+
_iterations,
|
|
29003
|
+
existingEntries
|
|
29004
|
+
);
|
|
29005
|
+
const signal = AbortSignal.timeout(15e3);
|
|
29006
|
+
const response = await provider.complete(
|
|
29007
|
+
{
|
|
29008
|
+
model: _model,
|
|
29009
|
+
system: [{ type: "text", text: prompt }],
|
|
29010
|
+
messages: [
|
|
29011
|
+
{ role: "user", content: "Review the session and return memory operations as JSON." }
|
|
29012
|
+
],
|
|
29013
|
+
maxTokens: 500
|
|
29014
|
+
},
|
|
29015
|
+
{ signal }
|
|
29016
|
+
);
|
|
29017
|
+
const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
|
|
29018
|
+
if (!text) return;
|
|
29019
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
29020
|
+
if (!jsonMatch) return;
|
|
29021
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
29022
|
+
if (!Array.isArray(parsed.operations) || parsed.operations.length === 0) return;
|
|
29023
|
+
let added = 0;
|
|
29024
|
+
let edited = 0;
|
|
29025
|
+
let deleted = 0;
|
|
29026
|
+
for (const op of parsed.operations) {
|
|
29027
|
+
switch (op.action) {
|
|
29028
|
+
case "add": {
|
|
29029
|
+
if (op.text?.trim()) {
|
|
29030
|
+
await this.memoryStore.remember(op.text.trim(), void 0, {
|
|
29031
|
+
type: op.type,
|
|
29032
|
+
tags: op.tags,
|
|
29033
|
+
priority: op.priority
|
|
29034
|
+
});
|
|
29035
|
+
added++;
|
|
29036
|
+
}
|
|
29037
|
+
break;
|
|
28402
29038
|
}
|
|
28403
|
-
|
|
28404
|
-
|
|
28405
|
-
|
|
28406
|
-
|
|
28407
|
-
|
|
28408
|
-
|
|
28409
|
-
|
|
28410
|
-
|
|
28411
|
-
|
|
28412
|
-
}
|
|
28413
|
-
|
|
29039
|
+
case "edit": {
|
|
29040
|
+
if (op.query && op.text?.trim()) {
|
|
29041
|
+
await this.memoryStore.forget(op.query);
|
|
29042
|
+
await this.memoryStore.remember(op.text.trim(), void 0, {
|
|
29043
|
+
type: op.type,
|
|
29044
|
+
tags: op.tags,
|
|
29045
|
+
priority: op.priority
|
|
29046
|
+
});
|
|
29047
|
+
edited++;
|
|
29048
|
+
}
|
|
29049
|
+
break;
|
|
28414
29050
|
}
|
|
28415
|
-
|
|
28416
|
-
|
|
28417
|
-
|
|
28418
|
-
|
|
28419
|
-
|
|
28420
|
-
|
|
29051
|
+
case "delete": {
|
|
29052
|
+
if (op.query) {
|
|
29053
|
+
const n = await this.memoryStore.forget(op.query);
|
|
29054
|
+
deleted += n;
|
|
29055
|
+
}
|
|
29056
|
+
break;
|
|
28421
29057
|
}
|
|
28422
|
-
break;
|
|
28423
29058
|
}
|
|
28424
29059
|
}
|
|
28425
|
-
|
|
28426
|
-
|
|
28427
|
-
|
|
28428
|
-
|
|
28429
|
-
|
|
28430
|
-
|
|
28431
|
-
process.stderr.write(`[memory] Session consolidation: ${parts.join(", ")}
|
|
29060
|
+
if (added > 0 || edited > 0 || deleted > 0) {
|
|
29061
|
+
const parts = [];
|
|
29062
|
+
if (added) parts.push(`${added} added`);
|
|
29063
|
+
if (edited) parts.push(`${edited} edited`);
|
|
29064
|
+
if (deleted) parts.push(`${deleted} deleted`);
|
|
29065
|
+
process.stderr.write(`[memory] Session consolidation: ${parts.join(", ")}
|
|
28432
29066
|
`);
|
|
29067
|
+
}
|
|
29068
|
+
} catch {
|
|
28433
29069
|
}
|
|
28434
|
-
}
|
|
28435
|
-
}
|
|
29070
|
+
})();
|
|
28436
29071
|
};
|
|
28437
29072
|
};
|
|
28438
29073
|
function sessionScopedPath(dir, sessionId, suffix) {
|
|
@@ -29059,15 +29694,15 @@ var SessionRecovery = class {
|
|
|
29059
29694
|
async detectStale(sessionId) {
|
|
29060
29695
|
const fp = this.filePath(sessionId);
|
|
29061
29696
|
const TAIL_SIZE = 8192;
|
|
29062
|
-
let
|
|
29697
|
+
let stat16;
|
|
29063
29698
|
try {
|
|
29064
|
-
|
|
29699
|
+
stat16 = await fsp3.stat(fp);
|
|
29065
29700
|
} catch (err) {
|
|
29066
29701
|
if (err.code === "ENOENT") return null;
|
|
29067
29702
|
return null;
|
|
29068
29703
|
}
|
|
29069
|
-
if (
|
|
29070
|
-
const position = Math.max(0,
|
|
29704
|
+
if (stat16.size === 0) return null;
|
|
29705
|
+
const position = Math.max(0, stat16.size - TAIL_SIZE);
|
|
29071
29706
|
const buf = Buffer.alloc(TAIL_SIZE);
|
|
29072
29707
|
let fh;
|
|
29073
29708
|
try {
|
|
@@ -29545,6 +30180,9 @@ var AgentStatusTracker = class {
|
|
|
29545
30180
|
leaderName;
|
|
29546
30181
|
// Live agent map: agentId → AgentEntry
|
|
29547
30182
|
agents = /* @__PURE__ */ new Map();
|
|
30183
|
+
// Last full agent list flushed (leader + subagents). Lets external consumers
|
|
30184
|
+
// read the current state synchronously without re-deriving it.
|
|
30185
|
+
lastAgents = [];
|
|
29548
30186
|
// Leader tracking
|
|
29549
30187
|
leaderStatus = "idle";
|
|
29550
30188
|
leaderCurrentTool;
|
|
@@ -29556,6 +30194,7 @@ var AgentStatusTracker = class {
|
|
|
29556
30194
|
leaderCtxPct;
|
|
29557
30195
|
leaderModel;
|
|
29558
30196
|
leaderPartialText = "";
|
|
30197
|
+
leaderStartedAt;
|
|
29559
30198
|
unsubscribers = [];
|
|
29560
30199
|
onUpdate;
|
|
29561
30200
|
sweepTimer = null;
|
|
@@ -29566,9 +30205,17 @@ var AgentStatusTracker = class {
|
|
|
29566
30205
|
this.leaderName = opts.leaderName ?? "leader";
|
|
29567
30206
|
this.onUpdate = opts.onUpdate;
|
|
29568
30207
|
}
|
|
30208
|
+
/** Current full agent list (leader + subagents) as of the last flush. */
|
|
30209
|
+
getAgents() {
|
|
30210
|
+
return this.lastAgents.length > 0 ? [...this.lastAgents] : [];
|
|
30211
|
+
}
|
|
29569
30212
|
start() {
|
|
29570
30213
|
this.unsubscribers.push(
|
|
29571
|
-
this.events.onPattern("agent.run.started", () => {
|
|
30214
|
+
this.events.onPattern("agent.run.started", (_event, payload) => {
|
|
30215
|
+
const p = payload;
|
|
30216
|
+
this.markLeaderStarted(p?.at);
|
|
30217
|
+
this.captureLeaderContext(p?.ctx);
|
|
30218
|
+
if (p?.model) this.leaderModel = p.model;
|
|
29572
30219
|
this.leaderStatus = "running";
|
|
29573
30220
|
this.leaderIterations++;
|
|
29574
30221
|
this.flush();
|
|
@@ -29576,25 +30223,36 @@ var AgentStatusTracker = class {
|
|
|
29576
30223
|
);
|
|
29577
30224
|
this.unsubscribers.push(
|
|
29578
30225
|
this.events.onPattern("iteration.started", (_e, payload) => {
|
|
29579
|
-
const
|
|
29580
|
-
|
|
29581
|
-
|
|
29582
|
-
|
|
29583
|
-
|
|
30226
|
+
const p = payload;
|
|
30227
|
+
const ctx = p?.ctx;
|
|
30228
|
+
this.markLeaderStarted();
|
|
30229
|
+
this.leaderStatus = "running";
|
|
30230
|
+
if (typeof p?.index === "number") {
|
|
30231
|
+
this.leaderIterations = Math.max(this.leaderIterations, p.index + 1);
|
|
29584
30232
|
}
|
|
30233
|
+
if (!ctx) {
|
|
30234
|
+
this.flush();
|
|
30235
|
+
return;
|
|
30236
|
+
}
|
|
30237
|
+
this.captureLeaderContext(ctx);
|
|
29585
30238
|
this.flush();
|
|
29586
30239
|
})
|
|
29587
30240
|
);
|
|
29588
30241
|
this.unsubscribers.push(
|
|
29589
|
-
this.events.onPattern("agent.run.completed", () => {
|
|
29590
|
-
|
|
30242
|
+
this.events.onPattern("agent.run.completed", (_event, payload) => {
|
|
30243
|
+
const p = payload;
|
|
30244
|
+
this.captureLeaderContext(p?.ctx);
|
|
30245
|
+
this.leaderStatus = p?.status === "failed" ? "error" : "idle";
|
|
29591
30246
|
this.leaderCurrentTool = void 0;
|
|
29592
30247
|
this.leaderPartialText = "";
|
|
30248
|
+
if (this.leaderStatus === "idle") this.leaderStartedAt = void 0;
|
|
29593
30249
|
this.flush();
|
|
29594
30250
|
})
|
|
29595
30251
|
);
|
|
29596
30252
|
this.unsubscribers.push(
|
|
29597
|
-
this.events.onPattern("agent.run.error", () => {
|
|
30253
|
+
this.events.onPattern("agent.run.error", (_event, payload) => {
|
|
30254
|
+
const p = payload;
|
|
30255
|
+
this.captureLeaderContext(p?.ctx);
|
|
29598
30256
|
this.leaderStatus = "error";
|
|
29599
30257
|
this.leaderCurrentTool = void 0;
|
|
29600
30258
|
this.leaderPartialText = "";
|
|
@@ -29605,6 +30263,7 @@ var AgentStatusTracker = class {
|
|
|
29605
30263
|
this.events.onPattern("tool.started", (_event, payload) => {
|
|
29606
30264
|
const p = payload;
|
|
29607
30265
|
if (p?.name) {
|
|
30266
|
+
this.markLeaderStarted();
|
|
29608
30267
|
this.leaderCurrentTool = p.name;
|
|
29609
30268
|
this.leaderToolCalls++;
|
|
29610
30269
|
}
|
|
@@ -29620,12 +30279,14 @@ var AgentStatusTracker = class {
|
|
|
29620
30279
|
);
|
|
29621
30280
|
this.unsubscribers.push(
|
|
29622
30281
|
this.events.onPattern("brain.ask_human", () => {
|
|
30282
|
+
this.markLeaderStarted();
|
|
29623
30283
|
this.leaderStatus = "waiting_user";
|
|
29624
30284
|
this.flush();
|
|
29625
30285
|
})
|
|
29626
30286
|
);
|
|
29627
30287
|
this.unsubscribers.push(
|
|
29628
30288
|
this.events.onPattern("llm.stream_started", () => {
|
|
30289
|
+
this.markLeaderStarted();
|
|
29629
30290
|
this.leaderStatus = "streaming";
|
|
29630
30291
|
this.leaderPartialText = "";
|
|
29631
30292
|
this.flush();
|
|
@@ -29633,14 +30294,42 @@ var AgentStatusTracker = class {
|
|
|
29633
30294
|
);
|
|
29634
30295
|
this.unsubscribers.push(
|
|
29635
30296
|
this.events.onPattern("provider.text_delta", (_e, payload) => {
|
|
29636
|
-
const
|
|
30297
|
+
const p = payload;
|
|
30298
|
+
const text = p?.text;
|
|
29637
30299
|
if (!text) return;
|
|
30300
|
+
this.markLeaderStarted();
|
|
30301
|
+
this.captureLeaderContext(p?.ctx);
|
|
29638
30302
|
this.leaderStatus = "streaming";
|
|
29639
30303
|
const next = this.leaderPartialText + text;
|
|
29640
30304
|
this.leaderPartialText = next.length > PARTIAL_TEXT_CAP ? next.slice(next.length - PARTIAL_TEXT_CAP) : next;
|
|
29641
30305
|
this.schedulePartialFlush();
|
|
29642
30306
|
})
|
|
29643
30307
|
);
|
|
30308
|
+
this.unsubscribers.push(
|
|
30309
|
+
this.events.onPattern("provider.response", (_e, payload) => {
|
|
30310
|
+
const p = payload;
|
|
30311
|
+
this.captureLeaderContext(p?.ctx);
|
|
30312
|
+
this.flush();
|
|
30313
|
+
})
|
|
30314
|
+
);
|
|
30315
|
+
this.unsubscribers.push(
|
|
30316
|
+
this.events.onPattern("provider.fallback", (_e, payload) => {
|
|
30317
|
+
const p = payload;
|
|
30318
|
+
if (p?.to?.model) {
|
|
30319
|
+
this.leaderModel = p.to.providerId ? `${p.to.providerId}/${p.to.model}` : p.to.model;
|
|
30320
|
+
this.flush();
|
|
30321
|
+
}
|
|
30322
|
+
})
|
|
30323
|
+
);
|
|
30324
|
+
this.unsubscribers.push(
|
|
30325
|
+
this.events.onPattern("ctx.pct", (_e, payload) => {
|
|
30326
|
+
const p = payload;
|
|
30327
|
+
if (typeof p?.load === "number" && Number.isFinite(p.load)) {
|
|
30328
|
+
this.leaderCtxPct = Math.round(p.load * 100);
|
|
30329
|
+
this.flush();
|
|
30330
|
+
}
|
|
30331
|
+
})
|
|
30332
|
+
);
|
|
29644
30333
|
this.unsubscribers.push(
|
|
29645
30334
|
this.events.onPattern("token.accounted", (_e, payload) => {
|
|
29646
30335
|
const p = payload;
|
|
@@ -29654,7 +30343,8 @@ var AgentStatusTracker = class {
|
|
|
29654
30343
|
const touch = (id) => {
|
|
29655
30344
|
let entry = this.agents.get(id);
|
|
29656
30345
|
if (!entry) {
|
|
29657
|
-
|
|
30346
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
30347
|
+
entry = { id, name: id, status: "idle", iterations: 0, toolCalls: 0, startedAt: now, lastActivityAt: now };
|
|
29658
30348
|
this.agents.set(id, entry);
|
|
29659
30349
|
}
|
|
29660
30350
|
entry.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -29667,6 +30357,7 @@ var AgentStatusTracker = class {
|
|
|
29667
30357
|
const entry = touch(p.subagentId);
|
|
29668
30358
|
entry.name = p.name?.trim() || entry.name;
|
|
29669
30359
|
if (p.model) entry.model = p.model;
|
|
30360
|
+
if (!entry.startedAt) entry.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29670
30361
|
entry.status = "running";
|
|
29671
30362
|
this.flush();
|
|
29672
30363
|
})
|
|
@@ -29686,6 +30377,7 @@ var AgentStatusTracker = class {
|
|
|
29686
30377
|
if (!p?.subagentId) return;
|
|
29687
30378
|
const entry = touch(p.subagentId);
|
|
29688
30379
|
entry.status = "running";
|
|
30380
|
+
if (!entry.startedAt) entry.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29689
30381
|
entry.iterations++;
|
|
29690
30382
|
this.flush();
|
|
29691
30383
|
})
|
|
@@ -29696,6 +30388,7 @@ var AgentStatusTracker = class {
|
|
|
29696
30388
|
if (!p?.subagentId) return;
|
|
29697
30389
|
const entry = touch(p.subagentId);
|
|
29698
30390
|
entry.status = "running";
|
|
30391
|
+
if (!entry.startedAt) entry.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29699
30392
|
entry.currentTool = p.name;
|
|
29700
30393
|
entry.toolCalls++;
|
|
29701
30394
|
this.flush();
|
|
@@ -29707,6 +30400,7 @@ var AgentStatusTracker = class {
|
|
|
29707
30400
|
if (!p?.subagentId) return;
|
|
29708
30401
|
const entry = touch(p.subagentId);
|
|
29709
30402
|
entry.status = "running";
|
|
30403
|
+
if (!entry.startedAt) entry.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29710
30404
|
if (typeof p.iteration === "number") entry.iterations = p.iteration;
|
|
29711
30405
|
if (typeof p.toolCalls === "number") entry.toolCalls = p.toolCalls;
|
|
29712
30406
|
if (typeof p.costUsd === "number") entry.costUsd = p.costUsd;
|
|
@@ -29794,6 +30488,7 @@ var AgentStatusTracker = class {
|
|
|
29794
30488
|
const leaderEntry = {
|
|
29795
30489
|
id: "leader",
|
|
29796
30490
|
name: this.leaderName,
|
|
30491
|
+
startedAt: this.leaderStartedAt,
|
|
29797
30492
|
status: this.leaderStatus,
|
|
29798
30493
|
currentTool: this.leaderCurrentTool,
|
|
29799
30494
|
iterations: this.leaderIterations,
|
|
@@ -29807,6 +30502,11 @@ var AgentStatusTracker = class {
|
|
|
29807
30502
|
lastActivityAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
29808
30503
|
};
|
|
29809
30504
|
const allAgents = [leaderEntry, ...this.agents.values()];
|
|
30505
|
+
this.lastAgents = allAgents;
|
|
30506
|
+
try {
|
|
30507
|
+
this.events.emit("session.agents_updated", { agents: allAgents });
|
|
30508
|
+
} catch {
|
|
30509
|
+
}
|
|
29810
30510
|
this.registry.updateAgents(allAgents).then(() => {
|
|
29811
30511
|
try {
|
|
29812
30512
|
this.onUpdate?.();
|
|
@@ -29814,6 +30514,23 @@ var AgentStatusTracker = class {
|
|
|
29814
30514
|
}
|
|
29815
30515
|
}).catch(() => void 0);
|
|
29816
30516
|
}
|
|
30517
|
+
markLeaderStarted(startedAt) {
|
|
30518
|
+
if (this.leaderStartedAt && (this.leaderStatus === "running" || this.leaderStatus === "streaming" || this.leaderStatus === "waiting_user")) {
|
|
30519
|
+
return;
|
|
30520
|
+
}
|
|
30521
|
+
this.leaderStartedAt = startedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
30522
|
+
}
|
|
30523
|
+
captureLeaderContext(ctx) {
|
|
30524
|
+
if (typeof ctx !== "object" || ctx === null) return;
|
|
30525
|
+
const c = ctx;
|
|
30526
|
+
if (typeof c.model === "string" && c.model.length > 0) this.leaderModel = c.model;
|
|
30527
|
+
const metaLimit = c.meta?.["effectiveMaxContext"];
|
|
30528
|
+
const providerMax = c.provider?.capabilities?.maxContext;
|
|
30529
|
+
const maxContext = typeof metaLimit === "number" && metaLimit > 0 ? metaLimit : typeof providerMax === "number" && providerMax > 0 ? providerMax : void 0;
|
|
30530
|
+
if (typeof c.lastRequestTokens === "number" && c.lastRequestTokens > 0 && maxContext !== void 0) {
|
|
30531
|
+
this.leaderCtxPct = Math.round(c.lastRequestTokens / maxContext * 100);
|
|
30532
|
+
}
|
|
30533
|
+
}
|
|
29817
30534
|
};
|
|
29818
30535
|
var INSTANCES_FILE = "webui-instances.json";
|
|
29819
30536
|
var DISCOVERY_TTL_MS = 2500;
|
|
@@ -30466,8 +31183,8 @@ var CloudSync = class {
|
|
|
30466
31183
|
const localPath = this.categoryToPath(cat);
|
|
30467
31184
|
if (!localPath) continue;
|
|
30468
31185
|
try {
|
|
30469
|
-
const
|
|
30470
|
-
if (
|
|
31186
|
+
const stat16 = await fsp3.stat(localPath);
|
|
31187
|
+
if (stat16.isDirectory()) {
|
|
30471
31188
|
const files = await this.walkDir(localPath, localPath);
|
|
30472
31189
|
for (const file of files) {
|
|
30473
31190
|
const content = await fsp3.readFile(file, "utf8");
|
|
@@ -30492,8 +31209,8 @@ var CloudSync = class {
|
|
|
30492
31209
|
const localPath = this.categoryToPath(cat);
|
|
30493
31210
|
if (!localPath) continue;
|
|
30494
31211
|
try {
|
|
30495
|
-
const
|
|
30496
|
-
if (
|
|
31212
|
+
const stat16 = await fsp3.stat(localPath);
|
|
31213
|
+
if (stat16.isDirectory()) {
|
|
30497
31214
|
const files = await this.walkDir(localPath, localPath);
|
|
30498
31215
|
for (const file of files) {
|
|
30499
31216
|
const content = await fsp3.readFile(file);
|
|
@@ -30684,7 +31401,13 @@ function parseHqFrame(raw) {
|
|
|
30684
31401
|
}
|
|
30685
31402
|
}
|
|
30686
31403
|
}
|
|
30687
|
-
var KNOWN_HQ_EVENT_PAYLOAD_TYPES = /* @__PURE__ */ new Set([
|
|
31404
|
+
var KNOWN_HQ_EVENT_PAYLOAD_TYPES = /* @__PURE__ */ new Set([
|
|
31405
|
+
"mailbox.snapshot",
|
|
31406
|
+
"mailbox.event",
|
|
31407
|
+
"session.snapshot",
|
|
31408
|
+
"session.transcript",
|
|
31409
|
+
"session.ended"
|
|
31410
|
+
]);
|
|
30688
31411
|
function isHqMailboxMessageSummary(x) {
|
|
30689
31412
|
if (typeof x !== "object" || x === null) return false;
|
|
30690
31413
|
const v = x;
|
|
@@ -30731,6 +31454,52 @@ function isHqMailboxEventPayload(x) {
|
|
|
30731
31454
|
if (v.agent !== void 0 && !isHqMailboxAgentSummary(v.agent)) return false;
|
|
30732
31455
|
return true;
|
|
30733
31456
|
}
|
|
31457
|
+
var HQ_SESSION_AGENT_STATUSES = /* @__PURE__ */ new Set([
|
|
31458
|
+
"idle",
|
|
31459
|
+
"running",
|
|
31460
|
+
"streaming",
|
|
31461
|
+
"waiting_user",
|
|
31462
|
+
"error"
|
|
31463
|
+
]);
|
|
31464
|
+
function isHqSessionAgentSummary(x) {
|
|
31465
|
+
if (typeof x !== "object" || x === null) return false;
|
|
31466
|
+
const v = x;
|
|
31467
|
+
return typeof v.id === "string" && typeof v.name === "string" && typeof v.status === "string" && HQ_SESSION_AGENT_STATUSES.has(v.status) && typeof v.iterations === "number" && typeof v.toolCalls === "number" && typeof v.lastActivityAt === "string";
|
|
31468
|
+
}
|
|
31469
|
+
var HQ_SESSION_STATUSES = /* @__PURE__ */ new Set(["active", "idle", "closing", "stale"]);
|
|
31470
|
+
function isHqSessionSnapshotPayload(x) {
|
|
31471
|
+
if (typeof x !== "object" || x === null) return false;
|
|
31472
|
+
const v = x;
|
|
31473
|
+
if (typeof v.sessionId !== "string" || typeof v.clientKind !== "string" || typeof v.machineId !== "string" || typeof v.projectId !== "string" || typeof v.projectName !== "string" || typeof v.projectRoot !== "string" || typeof v.status !== "string" || !HQ_SESSION_STATUSES.has(v.status) || typeof v.startedAt !== "string" || typeof v.lastActivityAt !== "string" || typeof v.agentCount !== "number" || !Array.isArray(v.agents)) {
|
|
31474
|
+
return false;
|
|
31475
|
+
}
|
|
31476
|
+
for (const agent of v.agents) {
|
|
31477
|
+
if (!isHqSessionAgentSummary(agent)) return false;
|
|
31478
|
+
}
|
|
31479
|
+
return true;
|
|
31480
|
+
}
|
|
31481
|
+
var HQ_TRANSCRIPT_ROLES = /* @__PURE__ */ new Set(["user", "assistant", "tool", "system", "error"]);
|
|
31482
|
+
function isHqTranscriptEntry(x) {
|
|
31483
|
+
if (typeof x !== "object" || x === null) return false;
|
|
31484
|
+
const v = x;
|
|
31485
|
+
return typeof v.ts === "string" && typeof v.role === "string" && HQ_TRANSCRIPT_ROLES.has(v.role) && typeof v.text === "string";
|
|
31486
|
+
}
|
|
31487
|
+
function isHqTranscriptAppendPayload(x) {
|
|
31488
|
+
if (typeof x !== "object" || x === null) return false;
|
|
31489
|
+
const v = x;
|
|
31490
|
+
if (typeof v.sessionId !== "string" || typeof v.fromSeq !== "number" || !Array.isArray(v.entries)) {
|
|
31491
|
+
return false;
|
|
31492
|
+
}
|
|
31493
|
+
for (const entry of v.entries) {
|
|
31494
|
+
if (!isHqTranscriptEntry(entry)) return false;
|
|
31495
|
+
}
|
|
31496
|
+
return true;
|
|
31497
|
+
}
|
|
31498
|
+
function isHqSessionEndedPayload(x) {
|
|
31499
|
+
if (typeof x !== "object" || x === null) return false;
|
|
31500
|
+
const v = x;
|
|
31501
|
+
return typeof v.sessionId === "string" && typeof v.endedAt === "string";
|
|
31502
|
+
}
|
|
30734
31503
|
function parseHqEventPayload(eventType, payload) {
|
|
30735
31504
|
if (!KNOWN_HQ_EVENT_PAYLOAD_TYPES.has(eventType)) {
|
|
30736
31505
|
return { ok: true, payload };
|
|
@@ -30740,6 +31509,12 @@ function parseHqEventPayload(eventType, payload) {
|
|
|
30740
31509
|
return isHqMailboxSnapshotPayload(payload) ? { ok: true, payload } : { ok: false, reason: "malformed-payload" };
|
|
30741
31510
|
case "mailbox.event":
|
|
30742
31511
|
return isHqMailboxEventPayload(payload) ? { ok: true, payload } : { ok: false, reason: "malformed-payload" };
|
|
31512
|
+
case "session.snapshot":
|
|
31513
|
+
return isHqSessionSnapshotPayload(payload) ? { ok: true, payload } : { ok: false, reason: "malformed-payload" };
|
|
31514
|
+
case "session.transcript":
|
|
31515
|
+
return isHqTranscriptAppendPayload(payload) ? { ok: true, payload } : { ok: false, reason: "malformed-payload" };
|
|
31516
|
+
case "session.ended":
|
|
31517
|
+
return isHqSessionEndedPayload(payload) ? { ok: true, payload } : { ok: false, reason: "malformed-payload" };
|
|
30743
31518
|
default: {
|
|
30744
31519
|
const _exhaustive = eventType;
|
|
30745
31520
|
return _exhaustive;
|
|
@@ -31210,6 +31985,41 @@ var HqPublisher = class {
|
|
|
31210
31985
|
...input.timestamp !== void 0 ? { timestamp: input.timestamp } : {}
|
|
31211
31986
|
});
|
|
31212
31987
|
}
|
|
31988
|
+
/** The client identity this publisher announced (clientId, kind, machineId, …). */
|
|
31989
|
+
get identity() {
|
|
31990
|
+
return this.options.client;
|
|
31991
|
+
}
|
|
31992
|
+
/** The project identity this publisher is bound to. */
|
|
31993
|
+
get project() {
|
|
31994
|
+
return this.options.project;
|
|
31995
|
+
}
|
|
31996
|
+
/** Publish a live session/terminal snapshot (state + agents). */
|
|
31997
|
+
publishSessionSnapshot(payload, opts) {
|
|
31998
|
+
return this.publishEvent({
|
|
31999
|
+
type: "session.snapshot",
|
|
32000
|
+
payload,
|
|
32001
|
+
sessionId: payload.sessionId,
|
|
32002
|
+
...opts?.timestamp !== void 0 ? { timestamp: opts.timestamp } : {}
|
|
32003
|
+
});
|
|
32004
|
+
}
|
|
32005
|
+
/** Publish an incremental batch of transcript turns for a session. */
|
|
32006
|
+
publishTranscriptAppend(payload, opts) {
|
|
32007
|
+
return this.publishEvent({
|
|
32008
|
+
type: "session.transcript",
|
|
32009
|
+
payload,
|
|
32010
|
+
sessionId: payload.sessionId,
|
|
32011
|
+
...opts?.timestamp !== void 0 ? { timestamp: opts.timestamp } : {}
|
|
32012
|
+
});
|
|
32013
|
+
}
|
|
32014
|
+
/** Mark a session/terminal as ended. */
|
|
32015
|
+
publishSessionEnded(payload, opts) {
|
|
32016
|
+
return this.publishEvent({
|
|
32017
|
+
type: "session.ended",
|
|
32018
|
+
payload,
|
|
32019
|
+
sessionId: payload.sessionId,
|
|
32020
|
+
...opts?.timestamp !== void 0 ? { timestamp: opts.timestamp } : {}
|
|
32021
|
+
});
|
|
32022
|
+
}
|
|
31213
32023
|
pollCommands() {
|
|
31214
32024
|
this.sendFrame({
|
|
31215
32025
|
type: "client.command_poll",
|
|
@@ -31395,7 +32205,7 @@ var GlobalMailbox = class {
|
|
|
31395
32205
|
/**
|
|
31396
32206
|
* @param projectDir — `~/.wrongstack/projects/<slug>/`
|
|
31397
32207
|
* @param events — optional EventBus for real-time TUI/WebUI notifications
|
|
31398
|
-
* @param hqPublisher — optional HQ publisher for cross-project telemetry
|
|
32208
|
+
* @param hqPublisher — optional HQ publisher, or getter, for cross-project telemetry
|
|
31399
32209
|
*/
|
|
31400
32210
|
constructor(projectDir, events, hqPublisher) {
|
|
31401
32211
|
this.messagePath = path3.join(projectDir, MAILBOX_FILE);
|
|
@@ -31407,15 +32217,19 @@ var GlobalMailbox = class {
|
|
|
31407
32217
|
get hqMailboxId() {
|
|
31408
32218
|
return `${path3.basename(path3.dirname(this.messagePath))}:mailbox`;
|
|
31409
32219
|
}
|
|
32220
|
+
get hqPublisher() {
|
|
32221
|
+
return typeof this._hqPublisher === "function" ? this._hqPublisher() : this._hqPublisher;
|
|
32222
|
+
}
|
|
31410
32223
|
publishHqMailboxEvent(input) {
|
|
31411
32224
|
try {
|
|
31412
|
-
this.
|
|
32225
|
+
this.hqPublisher?.publishMailboxEvent(input);
|
|
31413
32226
|
} catch {
|
|
31414
32227
|
}
|
|
31415
32228
|
}
|
|
31416
32229
|
publishHqMailboxSnapshot() {
|
|
31417
|
-
|
|
31418
|
-
|
|
32230
|
+
const publisher = this.hqPublisher;
|
|
32231
|
+
if (publisher === void 0) return;
|
|
32232
|
+
void publisher.publishMailboxSnapshot(this, { mailboxId: this.hqMailboxId }).catch(() => {
|
|
31419
32233
|
});
|
|
31420
32234
|
}
|
|
31421
32235
|
// ── Messages ────────────────────────────────────────────────────────────
|
|
@@ -31777,30 +32591,69 @@ var GlobalMailbox = class {
|
|
|
31777
32591
|
async _readMessages() {
|
|
31778
32592
|
try {
|
|
31779
32593
|
const raw = await fsp3.readFile(this.messagePath, "utf8");
|
|
31780
|
-
|
|
31781
|
-
const messages = [];
|
|
31782
|
-
for (const line of lines) {
|
|
31783
|
-
try {
|
|
31784
|
-
const parsed = JSON.parse(line);
|
|
31785
|
-
if (!parsed["readBy"]) {
|
|
31786
|
-
const readBy = {};
|
|
31787
|
-
if (parsed["read"] && parsed["readAt"]) {
|
|
31788
|
-
readBy[parsed["to"]] = parsed["readAt"];
|
|
31789
|
-
}
|
|
31790
|
-
parsed["readBy"] = readBy;
|
|
31791
|
-
delete parsed["read"];
|
|
31792
|
-
delete parsed["readAt"];
|
|
31793
|
-
}
|
|
31794
|
-
messages.push(parsed);
|
|
31795
|
-
} catch {
|
|
31796
|
-
}
|
|
31797
|
-
}
|
|
31798
|
-
return messages;
|
|
32594
|
+
return this._parseLines(raw);
|
|
31799
32595
|
} catch (err) {
|
|
31800
32596
|
if (err.code === "ENOENT") return [];
|
|
31801
32597
|
throw err;
|
|
31802
32598
|
}
|
|
31803
32599
|
}
|
|
32600
|
+
/**
|
|
32601
|
+
* Read only newly-appended bytes from the file and append them to the
|
|
32602
|
+
* in-memory cache. This avoids re-reading and re-parsing the entire file
|
|
32603
|
+
* when another process appended messages since our last read.
|
|
32604
|
+
*
|
|
32605
|
+
* Only safe when the file grew in size (messages are append-only). When
|
|
32606
|
+
* the file was rewritten (ack/purge changed existing content), callers
|
|
32607
|
+
* must fall back to {@link _readMessages}.
|
|
32608
|
+
*
|
|
32609
|
+
* @returns The (now up-to-date) message cache.
|
|
32610
|
+
*/
|
|
32611
|
+
async _readNewMessagesOnly(fd, oldSize, newSize) {
|
|
32612
|
+
const tailLen = newSize - oldSize;
|
|
32613
|
+
const buf = Buffer.alloc(tailLen);
|
|
32614
|
+
await fd.read(buf, 0, tailLen, oldSize);
|
|
32615
|
+
const tail = buf.toString("utf8");
|
|
32616
|
+
for (const line of tail.split(LINE_SEPARATOR)) {
|
|
32617
|
+
if (!line.trim()) continue;
|
|
32618
|
+
try {
|
|
32619
|
+
const parsed = JSON.parse(line);
|
|
32620
|
+
if (!parsed["readBy"]) {
|
|
32621
|
+
const readBy = {};
|
|
32622
|
+
if (parsed["read"] && parsed["readAt"]) {
|
|
32623
|
+
readBy[parsed["to"]] = parsed["readAt"];
|
|
32624
|
+
}
|
|
32625
|
+
parsed["readBy"] = readBy;
|
|
32626
|
+
delete parsed["read"];
|
|
32627
|
+
delete parsed["readAt"];
|
|
32628
|
+
}
|
|
32629
|
+
this._messageCache.push(parsed);
|
|
32630
|
+
} catch {
|
|
32631
|
+
}
|
|
32632
|
+
}
|
|
32633
|
+
return this._messageCache;
|
|
32634
|
+
}
|
|
32635
|
+
/** Parse a JSONL string into MailboxMessage[], including migration. */
|
|
32636
|
+
_parseLines(raw) {
|
|
32637
|
+
const lines = raw.split(LINE_SEPARATOR).filter((l) => l.trim().length > 0);
|
|
32638
|
+
const messages = [];
|
|
32639
|
+
for (const line of lines) {
|
|
32640
|
+
try {
|
|
32641
|
+
const parsed = JSON.parse(line);
|
|
32642
|
+
if (!parsed["readBy"]) {
|
|
32643
|
+
const readBy = {};
|
|
32644
|
+
if (parsed["read"] && parsed["readAt"]) {
|
|
32645
|
+
readBy[parsed["to"]] = parsed["readAt"];
|
|
32646
|
+
}
|
|
32647
|
+
parsed["readBy"] = readBy;
|
|
32648
|
+
delete parsed["read"];
|
|
32649
|
+
delete parsed["readAt"];
|
|
32650
|
+
}
|
|
32651
|
+
messages.push(parsed);
|
|
32652
|
+
} catch {
|
|
32653
|
+
}
|
|
32654
|
+
}
|
|
32655
|
+
return messages;
|
|
32656
|
+
}
|
|
31804
32657
|
/**
|
|
31805
32658
|
* Read messages, then adopt the result as the in-memory cache. Use this
|
|
31806
32659
|
* from writers that just took the file lock — the read reflects the
|
|
@@ -31820,6 +32673,10 @@ var GlobalMailbox = class {
|
|
|
31820
32673
|
* stat matches the cached mtime+size we return the cached array — no
|
|
31821
32674
|
* file read and no JSON.parse — collapsing the per-iteration query
|
|
31822
32675
|
* cost on the mailbox-loop hot path.
|
|
32676
|
+
*
|
|
32677
|
+
* When the file only grew (new messages appended by another process),
|
|
32678
|
+
* we read and parse just the tail bytes instead of the entire file.
|
|
32679
|
+
* This avoids re-parsing the full 10K-message history on every check.
|
|
31823
32680
|
*/
|
|
31824
32681
|
async _readMessagesCached() {
|
|
31825
32682
|
try {
|
|
@@ -31827,6 +32684,17 @@ var GlobalMailbox = class {
|
|
|
31827
32684
|
if (this._messageCache !== null && this._messageCacheMtime === st.mtimeMs && this._messageCacheSize === st.size) {
|
|
31828
32685
|
return this._messageCache;
|
|
31829
32686
|
}
|
|
32687
|
+
if (this._messageCache !== null && this._messageCacheSize >= 0 && st.size > this._messageCacheSize) {
|
|
32688
|
+
const fd = await fsp3.open(this.messagePath, "r");
|
|
32689
|
+
try {
|
|
32690
|
+
const updated = await this._readNewMessagesOnly(fd, this._messageCacheSize, st.size);
|
|
32691
|
+
this._messageCacheMtime = st.mtimeMs;
|
|
32692
|
+
this._messageCacheSize = st.size;
|
|
32693
|
+
return updated;
|
|
32694
|
+
} finally {
|
|
32695
|
+
await fd.close();
|
|
32696
|
+
}
|
|
32697
|
+
}
|
|
31830
32698
|
const all = await this._readMessages();
|
|
31831
32699
|
this._setMessageCache(all, st.mtimeMs, st.size);
|
|
31832
32700
|
return all;
|
|
@@ -32191,7 +33059,7 @@ function resolveHqConfig(options = {}) {
|
|
|
32191
33059
|
};
|
|
32192
33060
|
}
|
|
32193
33061
|
function stableMachineId() {
|
|
32194
|
-
return createHash("sha256").update(
|
|
33062
|
+
return createHash("sha256").update(hostname()).digest("hex").slice(0, 12);
|
|
32195
33063
|
}
|
|
32196
33064
|
function deriveProjectId(projectRoot) {
|
|
32197
33065
|
return createHash("sha256").update(projectRoot).digest("hex").slice(0, 12);
|
|
@@ -32233,6 +33101,381 @@ function createHqPublisherFromEnv(options) {
|
|
|
32233
33101
|
function createGlobalMailbox(options) {
|
|
32234
33102
|
return new GlobalMailbox(options.projectDir, options.events, options.hqPublisher);
|
|
32235
33103
|
}
|
|
33104
|
+
|
|
33105
|
+
// src/hq/agent-bridge.ts
|
|
33106
|
+
function startAgentMonitorEventBridge(opts) {
|
|
33107
|
+
const { events, clientId, projectId, publish } = opts;
|
|
33108
|
+
const seq = { current: 0 };
|
|
33109
|
+
function nextSeq() {
|
|
33110
|
+
seq.current += 1;
|
|
33111
|
+
return seq.current;
|
|
33112
|
+
}
|
|
33113
|
+
function buildEnvelope(type, payload) {
|
|
33114
|
+
return createHqEventEnvelope({
|
|
33115
|
+
id: `${Date.now().toString(36)}-${nextSeq()}`,
|
|
33116
|
+
type,
|
|
33117
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33118
|
+
clientId,
|
|
33119
|
+
projectId,
|
|
33120
|
+
seq: nextSeq(),
|
|
33121
|
+
payload
|
|
33122
|
+
});
|
|
33123
|
+
}
|
|
33124
|
+
const offMessage = events.on("agent.timeline.message", (payload) => {
|
|
33125
|
+
if (!publish) return;
|
|
33126
|
+
const msgPayload = {
|
|
33127
|
+
subagentId: payload.subagentId,
|
|
33128
|
+
agentName: payload.agentName,
|
|
33129
|
+
content: payload.content,
|
|
33130
|
+
kind: payload.kind,
|
|
33131
|
+
iteration: payload.iteration,
|
|
33132
|
+
ts: payload.ts
|
|
33133
|
+
};
|
|
33134
|
+
if (payload.toolName !== void 0) msgPayload.toolName = payload.toolName;
|
|
33135
|
+
if (payload.costUsd !== void 0) msgPayload.costUsd = payload.costUsd;
|
|
33136
|
+
publish(buildEnvelope("agent.message", msgPayload));
|
|
33137
|
+
});
|
|
33138
|
+
const offStatus = events.on("agent.status_changed", (payload) => {
|
|
33139
|
+
if (!publish) return;
|
|
33140
|
+
const statusPayload = {
|
|
33141
|
+
subagentId: payload.subagentId,
|
|
33142
|
+
agentName: payload.agentName,
|
|
33143
|
+
status: payload.status,
|
|
33144
|
+
ts: payload.ts
|
|
33145
|
+
};
|
|
33146
|
+
if (payload.summary !== void 0) statusPayload.summary = payload.summary;
|
|
33147
|
+
if (payload.task !== void 0) statusPayload.task = payload.task;
|
|
33148
|
+
publish(buildEnvelope("agent.status", statusPayload));
|
|
33149
|
+
});
|
|
33150
|
+
return () => {
|
|
33151
|
+
offMessage();
|
|
33152
|
+
offStatus();
|
|
33153
|
+
};
|
|
33154
|
+
}
|
|
33155
|
+
|
|
33156
|
+
// src/hq/transcript-mapper.ts
|
|
33157
|
+
function blocksToText(content) {
|
|
33158
|
+
if (typeof content === "string") return content;
|
|
33159
|
+
if (Array.isArray(content)) {
|
|
33160
|
+
return content.filter(
|
|
33161
|
+
(b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string"
|
|
33162
|
+
).map((b) => b.text).join("\n");
|
|
33163
|
+
}
|
|
33164
|
+
return "";
|
|
33165
|
+
}
|
|
33166
|
+
function asString(v) {
|
|
33167
|
+
if (typeof v === "string") return v;
|
|
33168
|
+
try {
|
|
33169
|
+
return JSON.stringify(v, null, 2);
|
|
33170
|
+
} catch {
|
|
33171
|
+
return String(v);
|
|
33172
|
+
}
|
|
33173
|
+
}
|
|
33174
|
+
function argsEntry(ts, name, input, id) {
|
|
33175
|
+
return {
|
|
33176
|
+
ts,
|
|
33177
|
+
role: "tool",
|
|
33178
|
+
tool: String(name ?? "tool"),
|
|
33179
|
+
toolInput: input !== void 0 && input !== null ? asString(input) : "{}",
|
|
33180
|
+
text: "",
|
|
33181
|
+
...typeof id === "string" ? { toolUseId: id } : {}
|
|
33182
|
+
};
|
|
33183
|
+
}
|
|
33184
|
+
function mapSessionEventToEntries(ev) {
|
|
33185
|
+
const ts = typeof ev["ts"] === "string" ? ev["ts"] : "";
|
|
33186
|
+
const make = (role, text, extra) => ({
|
|
33187
|
+
ts,
|
|
33188
|
+
role,
|
|
33189
|
+
text,
|
|
33190
|
+
...extra
|
|
33191
|
+
});
|
|
33192
|
+
switch (ev["type"]) {
|
|
33193
|
+
case "user_input": {
|
|
33194
|
+
const t2 = blocksToText(ev["content"]);
|
|
33195
|
+
return t2.trim() ? [make("user", t2)] : [];
|
|
33196
|
+
}
|
|
33197
|
+
case "llm_response": {
|
|
33198
|
+
const out = [];
|
|
33199
|
+
const t2 = blocksToText(ev["content"]);
|
|
33200
|
+
if (t2.trim()) out.push(make("assistant", t2));
|
|
33201
|
+
const content = ev["content"];
|
|
33202
|
+
if (Array.isArray(content)) {
|
|
33203
|
+
for (const b of content) {
|
|
33204
|
+
if (b && typeof b === "object" && b.type === "tool_use") {
|
|
33205
|
+
const block = b;
|
|
33206
|
+
out.push(argsEntry(ts, block.name, block.input, block.id));
|
|
33207
|
+
}
|
|
33208
|
+
}
|
|
33209
|
+
}
|
|
33210
|
+
return out;
|
|
33211
|
+
}
|
|
33212
|
+
case "tool_use":
|
|
33213
|
+
return [argsEntry(ts, ev["name"], ev["input"], ev["id"])];
|
|
33214
|
+
case "tool_call_start":
|
|
33215
|
+
return [argsEntry(ts, ev["name"], ev["input"] ?? ev["args"], ev["id"])];
|
|
33216
|
+
case "tool_result": {
|
|
33217
|
+
const isError = ev["isError"] === true;
|
|
33218
|
+
const content = ev["content"] ?? ev["output"];
|
|
33219
|
+
const outStr = content !== void 0 && content !== null ? asString(content) : "";
|
|
33220
|
+
return [
|
|
33221
|
+
make(isError ? "error" : "tool", outStr, {
|
|
33222
|
+
tool: "\u21B3 result",
|
|
33223
|
+
...isError ? { isError: true } : {},
|
|
33224
|
+
...typeof ev["id"] === "string" ? { toolUseId: ev["id"] } : {}
|
|
33225
|
+
})
|
|
33226
|
+
];
|
|
33227
|
+
}
|
|
33228
|
+
case "tool_call_end": {
|
|
33229
|
+
const isError = ev["isError"] === true;
|
|
33230
|
+
const content = ev["output"] ?? ev["content"];
|
|
33231
|
+
const outStr = content !== void 0 && content !== null ? asString(content) : "";
|
|
33232
|
+
if (!outStr.trim() && !isError && typeof ev["durationMs"] !== "number") return [];
|
|
33233
|
+
return [
|
|
33234
|
+
make(isError ? "error" : "tool", outStr, {
|
|
33235
|
+
tool: typeof ev["name"] === "string" ? String(ev["name"]) : "\u21B3 result",
|
|
33236
|
+
...typeof ev["durationMs"] === "number" ? { durationMs: ev["durationMs"] } : {},
|
|
33237
|
+
...isError ? { isError: true } : {},
|
|
33238
|
+
...typeof ev["id"] === "string" ? { toolUseId: ev["id"] } : {}
|
|
33239
|
+
})
|
|
33240
|
+
];
|
|
33241
|
+
}
|
|
33242
|
+
case "error":
|
|
33243
|
+
case "provider_error":
|
|
33244
|
+
return [make("error", String(ev["message"] ?? "error"))];
|
|
33245
|
+
case "agent_spawned":
|
|
33246
|
+
return [make("system", `spawned ${String(ev["role"] ?? "agent")}`)];
|
|
33247
|
+
case "task_completed":
|
|
33248
|
+
return [make("system", `task done: ${String(ev["title"] ?? "")}`)];
|
|
33249
|
+
case "task_failed":
|
|
33250
|
+
return [make("system", `task failed: ${String(ev["title"] ?? "")}`)];
|
|
33251
|
+
default:
|
|
33252
|
+
return [];
|
|
33253
|
+
}
|
|
33254
|
+
}
|
|
33255
|
+
function isResultEntry(e) {
|
|
33256
|
+
return (e.role === "tool" || e.role === "error") && e.toolUseId !== void 0 && e.toolInput === void 0;
|
|
33257
|
+
}
|
|
33258
|
+
function mergeToolResults(flat) {
|
|
33259
|
+
const out = [];
|
|
33260
|
+
const argsById = /* @__PURE__ */ new Map();
|
|
33261
|
+
for (const src of flat) {
|
|
33262
|
+
const e = { ...src };
|
|
33263
|
+
if (isResultEntry(e) && e.toolUseId !== void 0) {
|
|
33264
|
+
const tgt = argsById.get(e.toolUseId);
|
|
33265
|
+
if (tgt) {
|
|
33266
|
+
tgt.text = e.text || "";
|
|
33267
|
+
if (e.durationMs !== void 0) tgt.durationMs = e.durationMs;
|
|
33268
|
+
if (e.isError) {
|
|
33269
|
+
tgt.role = "error";
|
|
33270
|
+
tgt.isError = true;
|
|
33271
|
+
}
|
|
33272
|
+
continue;
|
|
33273
|
+
}
|
|
33274
|
+
}
|
|
33275
|
+
out.push(e);
|
|
33276
|
+
if (e.role === "tool" && e.toolUseId !== void 0 && e.toolInput !== void 0) {
|
|
33277
|
+
argsById.set(e.toolUseId, e);
|
|
33278
|
+
}
|
|
33279
|
+
}
|
|
33280
|
+
return out;
|
|
33281
|
+
}
|
|
33282
|
+
function buildTranscriptFromEvents(events) {
|
|
33283
|
+
const flat = [];
|
|
33284
|
+
for (const ev of events) {
|
|
33285
|
+
for (const e of mapSessionEventToEntries(ev)) flat.push(e);
|
|
33286
|
+
}
|
|
33287
|
+
return mergeToolResults(flat);
|
|
33288
|
+
}
|
|
33289
|
+
|
|
33290
|
+
// src/hq/session-bridge.ts
|
|
33291
|
+
var VALID_AGENT_STATUS = /* @__PURE__ */ new Set([
|
|
33292
|
+
"idle",
|
|
33293
|
+
"running",
|
|
33294
|
+
"streaming",
|
|
33295
|
+
"waiting_user",
|
|
33296
|
+
"error"
|
|
33297
|
+
]);
|
|
33298
|
+
function toAgentSummary(a) {
|
|
33299
|
+
const status = VALID_AGENT_STATUS.has(a.status) ? a.status : "idle";
|
|
33300
|
+
return {
|
|
33301
|
+
id: a.id,
|
|
33302
|
+
name: a.name,
|
|
33303
|
+
status,
|
|
33304
|
+
iterations: a.iterations,
|
|
33305
|
+
toolCalls: a.toolCalls,
|
|
33306
|
+
lastActivityAt: a.lastActivityAt,
|
|
33307
|
+
...a.startedAt !== void 0 ? { startedAt: a.startedAt } : {},
|
|
33308
|
+
...a.currentTool !== void 0 ? { currentTool: a.currentTool } : {},
|
|
33309
|
+
...a.costUsd !== void 0 ? { costUsd: a.costUsd } : {},
|
|
33310
|
+
...a.tokensIn !== void 0 ? { tokensIn: a.tokensIn } : {},
|
|
33311
|
+
...a.tokensOut !== void 0 ? { tokensOut: a.tokensOut } : {},
|
|
33312
|
+
...a.ctxPct !== void 0 ? { ctxPct: a.ctxPct } : {},
|
|
33313
|
+
...a.model !== void 0 ? { model: a.model } : {},
|
|
33314
|
+
...a.partialText !== void 0 ? { partialText: a.partialText } : {}
|
|
33315
|
+
};
|
|
33316
|
+
}
|
|
33317
|
+
function deriveSessionStatus(agents) {
|
|
33318
|
+
return agents.some(
|
|
33319
|
+
(a) => a.status === "running" || a.status === "streaming" || a.status === "waiting_user"
|
|
33320
|
+
) ? "active" : "idle";
|
|
33321
|
+
}
|
|
33322
|
+
function startSessionTelemetryBridge(opts) {
|
|
33323
|
+
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
33324
|
+
const publisher = opts.publisher;
|
|
33325
|
+
const identity = publisher.identity;
|
|
33326
|
+
const project = publisher.project;
|
|
33327
|
+
const startedAt = opts.startedAt ?? now();
|
|
33328
|
+
const wpaths = resolveWstackPaths({
|
|
33329
|
+
projectRoot: opts.projectRoot,
|
|
33330
|
+
...opts.globalRoot !== void 0 ? { globalRoot: opts.globalRoot } : {}
|
|
33331
|
+
});
|
|
33332
|
+
const sessionFile = path3.join(wpaths.projectSessions, `${opts.sessionId}.jsonl`);
|
|
33333
|
+
let agents = (opts.initialAgents ?? []).map(toAgentSummary);
|
|
33334
|
+
let lastActivityAt = agents.reduce(
|
|
33335
|
+
(latest, agent) => agent.lastActivityAt > latest ? agent.lastActivityAt : latest,
|
|
33336
|
+
startedAt
|
|
33337
|
+
);
|
|
33338
|
+
let lastSnapshotHash = "";
|
|
33339
|
+
let disposed = false;
|
|
33340
|
+
function buildSnapshot() {
|
|
33341
|
+
return {
|
|
33342
|
+
sessionId: opts.sessionId,
|
|
33343
|
+
clientKind: identity.kind,
|
|
33344
|
+
machineId: identity.machineId,
|
|
33345
|
+
projectId: project.projectId,
|
|
33346
|
+
projectName: opts.projectName ?? project.projectName,
|
|
33347
|
+
projectRoot: opts.projectRoot,
|
|
33348
|
+
status: deriveSessionStatus(agents),
|
|
33349
|
+
startedAt,
|
|
33350
|
+
lastActivityAt,
|
|
33351
|
+
agentCount: agents.length,
|
|
33352
|
+
agents,
|
|
33353
|
+
...identity.hostname !== void 0 ? { hostname: identity.hostname } : {},
|
|
33354
|
+
...identity.pid !== void 0 ? { pid: identity.pid } : {},
|
|
33355
|
+
...opts.gitBranch !== void 0 ? { gitBranch: opts.gitBranch } : {}
|
|
33356
|
+
};
|
|
33357
|
+
}
|
|
33358
|
+
function publishSnapshot(force = false) {
|
|
33359
|
+
if (disposed) return;
|
|
33360
|
+
const snap = buildSnapshot();
|
|
33361
|
+
const hash = JSON.stringify({ ...snap, lastActivityAt: "" });
|
|
33362
|
+
if (!force && hash === lastSnapshotHash) return;
|
|
33363
|
+
lastSnapshotHash = hash;
|
|
33364
|
+
try {
|
|
33365
|
+
publisher.publishSessionSnapshot(snap);
|
|
33366
|
+
} catch {
|
|
33367
|
+
}
|
|
33368
|
+
}
|
|
33369
|
+
const offAgents = opts.events?.on("session.agents_updated", (payload) => {
|
|
33370
|
+
agents = payload.agents.map(toAgentSummary);
|
|
33371
|
+
lastActivityAt = now();
|
|
33372
|
+
publishSnapshot();
|
|
33373
|
+
});
|
|
33374
|
+
publishSnapshot(true);
|
|
33375
|
+
let offset = 0;
|
|
33376
|
+
let partial = "";
|
|
33377
|
+
let seqEmitted = 0;
|
|
33378
|
+
let tailing = false;
|
|
33379
|
+
let watcher = null;
|
|
33380
|
+
let watchPending = false;
|
|
33381
|
+
function setupWatcher() {
|
|
33382
|
+
if (disposed || watcher) return;
|
|
33383
|
+
try {
|
|
33384
|
+
const nextWatcher = fs3.watch(sessionFile, () => {
|
|
33385
|
+
if (watchPending || disposed) return;
|
|
33386
|
+
watchPending = true;
|
|
33387
|
+
setTimeout(() => {
|
|
33388
|
+
watchPending = false;
|
|
33389
|
+
void tail();
|
|
33390
|
+
}, 25);
|
|
33391
|
+
});
|
|
33392
|
+
nextWatcher.on("error", () => {
|
|
33393
|
+
try {
|
|
33394
|
+
nextWatcher.close();
|
|
33395
|
+
} catch {
|
|
33396
|
+
}
|
|
33397
|
+
if (watcher === nextWatcher) watcher = null;
|
|
33398
|
+
});
|
|
33399
|
+
watcher = nextWatcher;
|
|
33400
|
+
} catch {
|
|
33401
|
+
watcher = null;
|
|
33402
|
+
}
|
|
33403
|
+
}
|
|
33404
|
+
async function tail() {
|
|
33405
|
+
if (disposed || tailing) return;
|
|
33406
|
+
tailing = true;
|
|
33407
|
+
try {
|
|
33408
|
+
const stat16 = await fsp3.stat(sessionFile).catch(() => null);
|
|
33409
|
+
if (disposed) return;
|
|
33410
|
+
if (!stat16) return;
|
|
33411
|
+
setupWatcher();
|
|
33412
|
+
if (stat16.size <= offset) return;
|
|
33413
|
+
const fd = await fsp3.open(sessionFile, "r");
|
|
33414
|
+
try {
|
|
33415
|
+
if (disposed) return;
|
|
33416
|
+
const len = stat16.size - offset;
|
|
33417
|
+
const buf = Buffer.allocUnsafe(len);
|
|
33418
|
+
await fd.read(buf, 0, len, offset);
|
|
33419
|
+
offset = stat16.size;
|
|
33420
|
+
partial += buf.toString("utf8");
|
|
33421
|
+
const lines = partial.split("\n");
|
|
33422
|
+
partial = lines.pop() ?? "";
|
|
33423
|
+
const entries = [];
|
|
33424
|
+
for (const line of lines) {
|
|
33425
|
+
const trimmed = line.trim();
|
|
33426
|
+
if (!trimmed) continue;
|
|
33427
|
+
let obj;
|
|
33428
|
+
try {
|
|
33429
|
+
obj = JSON.parse(trimmed);
|
|
33430
|
+
} catch {
|
|
33431
|
+
continue;
|
|
33432
|
+
}
|
|
33433
|
+
for (const entry of mapSessionEventToEntries(obj)) entries.push(entry);
|
|
33434
|
+
}
|
|
33435
|
+
if (entries.length > 0) {
|
|
33436
|
+
try {
|
|
33437
|
+
publisher.publishTranscriptAppend({
|
|
33438
|
+
sessionId: opts.sessionId,
|
|
33439
|
+
fromSeq: seqEmitted,
|
|
33440
|
+
entries
|
|
33441
|
+
});
|
|
33442
|
+
} catch {
|
|
33443
|
+
}
|
|
33444
|
+
seqEmitted += entries.length;
|
|
33445
|
+
lastActivityAt = now();
|
|
33446
|
+
}
|
|
33447
|
+
} finally {
|
|
33448
|
+
await fd.close();
|
|
33449
|
+
}
|
|
33450
|
+
} catch {
|
|
33451
|
+
} finally {
|
|
33452
|
+
tailing = false;
|
|
33453
|
+
}
|
|
33454
|
+
}
|
|
33455
|
+
const snapshotTimer = setInterval(() => publishSnapshot(true), opts.snapshotIntervalMs ?? 2500);
|
|
33456
|
+
const tailTimer = setInterval(() => void tail(), opts.transcriptIntervalMs ?? 500);
|
|
33457
|
+
snapshotTimer.unref?.();
|
|
33458
|
+
tailTimer.unref?.();
|
|
33459
|
+
void tail();
|
|
33460
|
+
return () => {
|
|
33461
|
+
if (disposed) return;
|
|
33462
|
+
disposed = true;
|
|
33463
|
+
offAgents?.();
|
|
33464
|
+
if (watcher) {
|
|
33465
|
+
try {
|
|
33466
|
+
watcher.close();
|
|
33467
|
+
} catch {
|
|
33468
|
+
}
|
|
33469
|
+
watcher = null;
|
|
33470
|
+
}
|
|
33471
|
+
clearInterval(snapshotTimer);
|
|
33472
|
+
clearInterval(tailTimer);
|
|
33473
|
+
try {
|
|
33474
|
+
publisher.publishSessionEnded({ sessionId: opts.sessionId, endedAt: now() });
|
|
33475
|
+
} catch {
|
|
33476
|
+
}
|
|
33477
|
+
};
|
|
33478
|
+
}
|
|
32236
33479
|
var MATCHERS = {
|
|
32237
33480
|
pnpmWorkspace: (files) => files.includes("pnpm-workspace.yaml"),
|
|
32238
33481
|
gradlew: (_files, dirs) => dirs.includes("gradlew"),
|
|
@@ -33110,8 +34353,8 @@ var ReportGenerator = class {
|
|
|
33110
34353
|
try {
|
|
33111
34354
|
await stat(this.options.outputDir);
|
|
33112
34355
|
} catch {
|
|
33113
|
-
const { mkdir:
|
|
33114
|
-
await
|
|
34356
|
+
const { mkdir: mkdir24 } = await import('fs/promises');
|
|
34357
|
+
await mkdir24(this.options.outputDir, { recursive: true });
|
|
33115
34358
|
}
|
|
33116
34359
|
}
|
|
33117
34360
|
generateMarkdown(result) {
|
|
@@ -33399,7 +34642,7 @@ var SecurityScannerOrchestrator = class {
|
|
|
33399
34642
|
message: errAsErr.message,
|
|
33400
34643
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
33401
34644
|
}));
|
|
33402
|
-
await new Promise((
|
|
34645
|
+
await new Promise((resolve19) => setTimeout(resolve19, delay));
|
|
33403
34646
|
return this.completeWithRetry(provider, request, abortController, attempt + 1);
|
|
33404
34647
|
}
|
|
33405
34648
|
}
|
|
@@ -34532,12 +35775,12 @@ var BrainDecisionQueue = class {
|
|
|
34532
35775
|
options: request.options,
|
|
34533
35776
|
rationale: "Decision escalated to human authority."
|
|
34534
35777
|
};
|
|
34535
|
-
const pending = new Promise((
|
|
34536
|
-
const entry = { request, resolve:
|
|
35778
|
+
const pending = new Promise((resolve19) => {
|
|
35779
|
+
const entry = { request, resolve: resolve19 };
|
|
34537
35780
|
if (this.opts.timeoutMs && this.opts.timeoutMs > 0) {
|
|
34538
35781
|
entry.timer = setTimeout(() => {
|
|
34539
35782
|
this.pending.delete(request.id);
|
|
34540
|
-
|
|
35783
|
+
resolve19({ type: "deny", reason: "Brain human decision timed out." });
|
|
34541
35784
|
}, this.opts.timeoutMs);
|
|
34542
35785
|
}
|
|
34543
35786
|
this.pending.set(request.id, entry);
|
|
@@ -34639,6 +35882,10 @@ var DefaultMailbox = class {
|
|
|
34639
35882
|
_messageCache = null;
|
|
34640
35883
|
_messageCacheMtime = -1;
|
|
34641
35884
|
_messageCacheSize = -1;
|
|
35885
|
+
/** Primary index: recipient → Set of messages (points into _messageCache). */
|
|
35886
|
+
_byTo = /* @__PURE__ */ new Map();
|
|
35887
|
+
/** Secondary index: sender → Set of messages (points into _messageCache). */
|
|
35888
|
+
_byFrom = /* @__PURE__ */ new Map();
|
|
34642
35889
|
constructor(sessionDir) {
|
|
34643
35890
|
this.filePath = path3.join(sessionDir, MAILBOX_FILE2);
|
|
34644
35891
|
}
|
|
@@ -34674,12 +35921,30 @@ var DefaultMailbox = class {
|
|
|
34674
35921
|
}
|
|
34675
35922
|
// ── Query ─────────────────────────────────────────────────────────────
|
|
34676
35923
|
async query(q) {
|
|
34677
|
-
const
|
|
35924
|
+
const needFullScan = q.unreadBy !== void 0 || q.since !== void 0;
|
|
35925
|
+
let candidates;
|
|
35926
|
+
if (needFullScan) {
|
|
35927
|
+
candidates = await this._readAllCached();
|
|
35928
|
+
} else {
|
|
35929
|
+
await this._readAllCached();
|
|
35930
|
+
if (q.to !== void 0) {
|
|
35931
|
+
const direct = this._byTo.get(q.to);
|
|
35932
|
+
const broadcast = this._byTo.get("*");
|
|
35933
|
+
const combined = /* @__PURE__ */ new Map();
|
|
35934
|
+
if (direct) for (const m of direct) combined.set(m.id, m);
|
|
35935
|
+
if (broadcast) for (const m of broadcast) combined.set(m.id, m);
|
|
35936
|
+
candidates = Array.from(combined.values());
|
|
35937
|
+
} else if (q.from !== void 0) {
|
|
35938
|
+
candidates = Array.from(this._byFrom.get(q.from) ?? []);
|
|
35939
|
+
} else {
|
|
35940
|
+
candidates = await this._readAllCached();
|
|
35941
|
+
}
|
|
35942
|
+
}
|
|
34678
35943
|
const limit = q.limit ?? 50;
|
|
34679
35944
|
const order = q.minPriority !== void 0 ? { low: 0, normal: 1, high: 2 } : null;
|
|
34680
35945
|
const minPriorityRank = order && q.minPriority !== void 0 ? order[q.minPriority] : 0;
|
|
34681
35946
|
const filtered = [];
|
|
34682
|
-
for (const msg of
|
|
35947
|
+
for (const msg of candidates) {
|
|
34683
35948
|
if (q.to !== void 0 && msg.to !== q.to && msg.to !== "*") continue;
|
|
34684
35949
|
if (q.from !== void 0 && msg.from !== q.from) continue;
|
|
34685
35950
|
if (q.unreadBy !== void 0 && q.unreadBy in msg.readBy) continue;
|
|
@@ -34780,6 +36045,8 @@ var DefaultMailbox = class {
|
|
|
34780
36045
|
this._messageCache = null;
|
|
34781
36046
|
this._messageCacheMtime = -1;
|
|
34782
36047
|
this._messageCacheSize = -1;
|
|
36048
|
+
this._byTo.clear();
|
|
36049
|
+
this._byFrom.clear();
|
|
34783
36050
|
}
|
|
34784
36051
|
async clearAll() {
|
|
34785
36052
|
await withFileLock(this.filePath, async () => {
|
|
@@ -34838,36 +36105,81 @@ var DefaultMailbox = class {
|
|
|
34838
36105
|
async _readAll() {
|
|
34839
36106
|
try {
|
|
34840
36107
|
const raw = await fsp3.readFile(this.filePath, "utf8");
|
|
34841
|
-
|
|
34842
|
-
const messages = [];
|
|
34843
|
-
for (const line of lines) {
|
|
34844
|
-
try {
|
|
34845
|
-
const parsed = JSON.parse(line);
|
|
34846
|
-
if (!parsed["readBy"]) {
|
|
34847
|
-
const readBy = {};
|
|
34848
|
-
if (parsed["read"] && parsed["readAt"]) {
|
|
34849
|
-
readBy[parsed["to"] ?? "unknown"] = parsed["readAt"];
|
|
34850
|
-
}
|
|
34851
|
-
parsed["readBy"] = readBy;
|
|
34852
|
-
delete parsed["read"];
|
|
34853
|
-
delete parsed["readAt"];
|
|
34854
|
-
}
|
|
34855
|
-
messages.push(parsed);
|
|
34856
|
-
} catch {
|
|
34857
|
-
}
|
|
34858
|
-
}
|
|
34859
|
-
return messages;
|
|
36108
|
+
return this._parseLines(raw);
|
|
34860
36109
|
} catch (err) {
|
|
34861
36110
|
if (err.code === "ENOENT") return [];
|
|
34862
36111
|
throw err;
|
|
34863
36112
|
}
|
|
34864
36113
|
}
|
|
36114
|
+
/**
|
|
36115
|
+
* Read only newly-appended bytes from the file and append them to the
|
|
36116
|
+
* in-memory cache, avoiding a full re-read when the file only grew.
|
|
36117
|
+
*/
|
|
36118
|
+
async _readNewMessagesOnly(fd, oldSize, newSize) {
|
|
36119
|
+
const tailLen = newSize - oldSize;
|
|
36120
|
+
const buf = Buffer.alloc(tailLen);
|
|
36121
|
+
await fd.read(buf, 0, tailLen, oldSize);
|
|
36122
|
+
const tail = buf.toString("utf8");
|
|
36123
|
+
for (const line of tail.split(LINE_SEPARATOR2)) {
|
|
36124
|
+
if (!line.trim()) continue;
|
|
36125
|
+
try {
|
|
36126
|
+
const parsed = JSON.parse(line);
|
|
36127
|
+
if (!parsed["readBy"]) {
|
|
36128
|
+
const readBy = {};
|
|
36129
|
+
if (parsed["read"] && parsed["readAt"]) {
|
|
36130
|
+
readBy[parsed["to"] ?? "unknown"] = parsed["readAt"];
|
|
36131
|
+
}
|
|
36132
|
+
parsed["readBy"] = readBy;
|
|
36133
|
+
delete parsed["read"];
|
|
36134
|
+
delete parsed["readAt"];
|
|
36135
|
+
}
|
|
36136
|
+
const msg = parsed;
|
|
36137
|
+
this._messageCache.push(msg);
|
|
36138
|
+
this._indexMsg(msg);
|
|
36139
|
+
} catch {
|
|
36140
|
+
}
|
|
36141
|
+
}
|
|
36142
|
+
return this._messageCache;
|
|
36143
|
+
}
|
|
36144
|
+
/** Parse a JSONL string into MailboxMessage[], including migration. */
|
|
36145
|
+
_parseLines(raw) {
|
|
36146
|
+
const lines = raw.split(LINE_SEPARATOR2).filter((l) => l.trim().length > 0);
|
|
36147
|
+
const messages = [];
|
|
36148
|
+
for (const line of lines) {
|
|
36149
|
+
try {
|
|
36150
|
+
const parsed = JSON.parse(line);
|
|
36151
|
+
if (!parsed["readBy"]) {
|
|
36152
|
+
const readBy = {};
|
|
36153
|
+
if (parsed["read"] && parsed["readAt"]) {
|
|
36154
|
+
readBy[parsed["to"] ?? "unknown"] = parsed["readAt"];
|
|
36155
|
+
}
|
|
36156
|
+
parsed["readBy"] = readBy;
|
|
36157
|
+
delete parsed["read"];
|
|
36158
|
+
delete parsed["readAt"];
|
|
36159
|
+
}
|
|
36160
|
+
messages.push(parsed);
|
|
36161
|
+
} catch {
|
|
36162
|
+
}
|
|
36163
|
+
}
|
|
36164
|
+
return messages;
|
|
36165
|
+
}
|
|
34865
36166
|
async _readAllCached() {
|
|
34866
36167
|
try {
|
|
34867
36168
|
const st = await fsp3.stat(this.filePath);
|
|
34868
36169
|
if (this._messageCache !== null && this._messageCacheMtime === st.mtimeMs && this._messageCacheSize === st.size) {
|
|
34869
36170
|
return this._messageCache;
|
|
34870
36171
|
}
|
|
36172
|
+
if (this._messageCache !== null && this._messageCacheSize >= 0 && st.size > this._messageCacheSize) {
|
|
36173
|
+
const fd = await fsp3.open(this.filePath, "r");
|
|
36174
|
+
try {
|
|
36175
|
+
const updated = await this._readNewMessagesOnly(fd, this._messageCacheSize, st.size);
|
|
36176
|
+
this._messageCacheMtime = st.mtimeMs;
|
|
36177
|
+
this._messageCacheSize = st.size;
|
|
36178
|
+
return updated;
|
|
36179
|
+
} finally {
|
|
36180
|
+
await fd.close();
|
|
36181
|
+
}
|
|
36182
|
+
}
|
|
34871
36183
|
const all = await this._readAll();
|
|
34872
36184
|
this._setMessageCache(all, st.mtimeMs, st.size);
|
|
34873
36185
|
return all;
|
|
@@ -34884,9 +36196,12 @@ var DefaultMailbox = class {
|
|
|
34884
36196
|
this._messageCache = null;
|
|
34885
36197
|
this._messageCacheMtime = -1;
|
|
34886
36198
|
this._messageCacheSize = -1;
|
|
36199
|
+
this._byTo.clear();
|
|
36200
|
+
this._byFrom.clear();
|
|
34887
36201
|
return;
|
|
34888
36202
|
}
|
|
34889
36203
|
this._messageCache = messages;
|
|
36204
|
+
this._buildIndexes(messages);
|
|
34890
36205
|
if (mtime !== void 0 && size !== void 0) {
|
|
34891
36206
|
this._messageCacheMtime = mtime;
|
|
34892
36207
|
this._messageCacheSize = size;
|
|
@@ -34904,9 +36219,35 @@ var DefaultMailbox = class {
|
|
|
34904
36219
|
this._messageCache = null;
|
|
34905
36220
|
this._messageCacheMtime = -1;
|
|
34906
36221
|
this._messageCacheSize = -1;
|
|
36222
|
+
this._byTo.clear();
|
|
36223
|
+
this._byFrom.clear();
|
|
34907
36224
|
return;
|
|
34908
36225
|
}
|
|
34909
36226
|
this._messageCache.push(msg);
|
|
36227
|
+
this._indexMsg(msg);
|
|
36228
|
+
}
|
|
36229
|
+
/** Rebuild both indexes from a full message list. */
|
|
36230
|
+
_buildIndexes(messages) {
|
|
36231
|
+
this._byTo.clear();
|
|
36232
|
+
this._byFrom.clear();
|
|
36233
|
+
for (const msg of messages) {
|
|
36234
|
+
this._indexMsg(msg);
|
|
36235
|
+
}
|
|
36236
|
+
}
|
|
36237
|
+
/** Add a single message to both indexes. */
|
|
36238
|
+
_indexMsg(msg) {
|
|
36239
|
+
const toSet = this._byTo.get(msg.to);
|
|
36240
|
+
if (toSet) {
|
|
36241
|
+
toSet.add(msg);
|
|
36242
|
+
} else {
|
|
36243
|
+
this._byTo.set(msg.to, /* @__PURE__ */ new Set([msg]));
|
|
36244
|
+
}
|
|
36245
|
+
const fromSet = this._byFrom.get(msg.from);
|
|
36246
|
+
if (fromSet) {
|
|
36247
|
+
fromSet.add(msg);
|
|
36248
|
+
} else {
|
|
36249
|
+
this._byFrom.set(msg.from, /* @__PURE__ */ new Set([msg]));
|
|
36250
|
+
}
|
|
34910
36251
|
}
|
|
34911
36252
|
};
|
|
34912
36253
|
var BrainMonitor = class {
|
|
@@ -38053,17 +39394,17 @@ ${input.detail}`
|
|
|
38053
39394
|
_waitForDagProgress(timeoutMs) {
|
|
38054
39395
|
const before = this._dagProgressKey();
|
|
38055
39396
|
if (this.dag.isDone()) return Promise.resolve();
|
|
38056
|
-
return new Promise((
|
|
39397
|
+
return new Promise((resolve19) => {
|
|
38057
39398
|
let off;
|
|
38058
39399
|
const timer = setTimeout(() => {
|
|
38059
39400
|
off?.();
|
|
38060
|
-
|
|
39401
|
+
resolve19();
|
|
38061
39402
|
}, timeoutMs);
|
|
38062
39403
|
off = this.dag.onEvent(() => {
|
|
38063
39404
|
if (this._dagProgressKey() === before) return;
|
|
38064
39405
|
clearTimeout(timer);
|
|
38065
39406
|
off?.();
|
|
38066
|
-
|
|
39407
|
+
resolve19();
|
|
38067
39408
|
});
|
|
38068
39409
|
});
|
|
38069
39410
|
}
|
|
@@ -38337,6 +39678,442 @@ ${input.detail}`
|
|
|
38337
39678
|
this.onCoordinatorEvent?.(event);
|
|
38338
39679
|
}
|
|
38339
39680
|
};
|
|
39681
|
+
var AgentMonitorService = class {
|
|
39682
|
+
_fleetBus;
|
|
39683
|
+
_events;
|
|
39684
|
+
_transcriptsDir;
|
|
39685
|
+
_maxEntries;
|
|
39686
|
+
_streamEnabled;
|
|
39687
|
+
_onEntry;
|
|
39688
|
+
/** Per-subagent virtual sessions. */
|
|
39689
|
+
_sessions = /* @__PURE__ */ new Map();
|
|
39690
|
+
/** Disposers for FleetBus subscriptions, keyed by subagentId. */
|
|
39691
|
+
_subscriptions = /* @__PURE__ */ new Map();
|
|
39692
|
+
/** Generic fleet-wide subscription disposer. */
|
|
39693
|
+
_fleetDisposer;
|
|
39694
|
+
/** Track whether service is running. */
|
|
39695
|
+
_started = false;
|
|
39696
|
+
constructor(opts) {
|
|
39697
|
+
this._fleetBus = opts.fleetBus;
|
|
39698
|
+
this._events = opts.events;
|
|
39699
|
+
this._transcriptsDir = opts.transcriptsDir;
|
|
39700
|
+
this._maxEntries = opts.maxEntriesPerAgent ?? 500;
|
|
39701
|
+
this._streamEnabled = opts.streamEnabled ?? false;
|
|
39702
|
+
this._onEntry = opts.onEntry;
|
|
39703
|
+
}
|
|
39704
|
+
// ── Public API ────────────────────────────────────────────────────
|
|
39705
|
+
/** Set the FleetBus to listen on. Must be called before `start()`. */
|
|
39706
|
+
setFleetBus(bus) {
|
|
39707
|
+
this._fleetBus = bus;
|
|
39708
|
+
}
|
|
39709
|
+
get streamEnabled() {
|
|
39710
|
+
return this._streamEnabled;
|
|
39711
|
+
}
|
|
39712
|
+
/** Enable/disable streaming agent conversations to the main chat timeline. */
|
|
39713
|
+
setStreamEnabled(enabled) {
|
|
39714
|
+
this._streamEnabled = enabled;
|
|
39715
|
+
}
|
|
39716
|
+
/** Get a snapshot of all known agent sessions. */
|
|
39717
|
+
getAllSessions() {
|
|
39718
|
+
return Array.from(this._sessions.values());
|
|
39719
|
+
}
|
|
39720
|
+
/** Get a specific agent's virtual session, or undefined. */
|
|
39721
|
+
getSession(subagentId) {
|
|
39722
|
+
return this._sessions.get(subagentId);
|
|
39723
|
+
}
|
|
39724
|
+
/** Get transcript entries for a specific agent, newest first. */
|
|
39725
|
+
getTranscript(subagentId, limit = 50) {
|
|
39726
|
+
const session = this._sessions.get(subagentId);
|
|
39727
|
+
if (!session) return [];
|
|
39728
|
+
return session.transcript.slice(-limit).reverse();
|
|
39729
|
+
}
|
|
39730
|
+
/** Set a callback for each new timeline entry (HQ bridge). */
|
|
39731
|
+
setOnEntry(handler) {
|
|
39732
|
+
this._onEntry = handler;
|
|
39733
|
+
}
|
|
39734
|
+
// ── Lifecycle ──────────────────────────────────────────────────────
|
|
39735
|
+
/** Start listening to FleetBus events. */
|
|
39736
|
+
start() {
|
|
39737
|
+
if (this._started) return;
|
|
39738
|
+
if (!this._fleetBus) {
|
|
39739
|
+
this._started = true;
|
|
39740
|
+
return;
|
|
39741
|
+
}
|
|
39742
|
+
this._started = true;
|
|
39743
|
+
this._fleetDisposer = this._fleetBus.onAny((event) => {
|
|
39744
|
+
this._routeEvent(event.subagentId, event.type, event.payload);
|
|
39745
|
+
});
|
|
39746
|
+
}
|
|
39747
|
+
/** Stop listening and clean up all subscriptions. */
|
|
39748
|
+
stop() {
|
|
39749
|
+
if (!this._started) return;
|
|
39750
|
+
this._started = false;
|
|
39751
|
+
if (this._fleetDisposer) {
|
|
39752
|
+
this._fleetDisposer();
|
|
39753
|
+
this._fleetDisposer = void 0;
|
|
39754
|
+
}
|
|
39755
|
+
for (const disposer of this._subscriptions.values()) {
|
|
39756
|
+
disposer();
|
|
39757
|
+
}
|
|
39758
|
+
this._subscriptions.clear();
|
|
39759
|
+
}
|
|
39760
|
+
/** Ensure a subagent is being tracked. Called when a subagent spawns. */
|
|
39761
|
+
trackSubagent(subagentId, agentName, task) {
|
|
39762
|
+
if (this._sessions.has(subagentId)) return;
|
|
39763
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
39764
|
+
const session = {
|
|
39765
|
+
subagentId,
|
|
39766
|
+
agentName,
|
|
39767
|
+
createdAt: now,
|
|
39768
|
+
status: "spawned",
|
|
39769
|
+
task,
|
|
39770
|
+
transcript: []
|
|
39771
|
+
};
|
|
39772
|
+
this._sessions.set(subagentId, session);
|
|
39773
|
+
this._addEntry(subagentId, {
|
|
39774
|
+
id: this._uid(),
|
|
39775
|
+
subagentId,
|
|
39776
|
+
agentName,
|
|
39777
|
+
ts: now,
|
|
39778
|
+
kind: "system",
|
|
39779
|
+
content: task ? `\u{1F3AF} Spawned: ${task}` : "\u{1F916} Agent spawned",
|
|
39780
|
+
iteration: 0
|
|
39781
|
+
});
|
|
39782
|
+
this._events.emit("agent.status_changed", {
|
|
39783
|
+
subagentId,
|
|
39784
|
+
agentName,
|
|
39785
|
+
status: "spawned",
|
|
39786
|
+
ts: now,
|
|
39787
|
+
summary: task,
|
|
39788
|
+
task
|
|
39789
|
+
});
|
|
39790
|
+
}
|
|
39791
|
+
/** Mark a subagent as completed/failed/etc. Called on subagent finish. */
|
|
39792
|
+
completeSubagent(subagentId, status, summary) {
|
|
39793
|
+
const session = this._sessions.get(subagentId);
|
|
39794
|
+
if (!session) return;
|
|
39795
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
39796
|
+
session.status = status;
|
|
39797
|
+
this._addEntry(subagentId, {
|
|
39798
|
+
id: this._uid(),
|
|
39799
|
+
subagentId,
|
|
39800
|
+
agentName: session.agentName,
|
|
39801
|
+
ts: now,
|
|
39802
|
+
kind: "status",
|
|
39803
|
+
content: summary ?? `Agent ${status}`,
|
|
39804
|
+
iteration: 999
|
|
39805
|
+
});
|
|
39806
|
+
this._events.emit("agent.status_changed", {
|
|
39807
|
+
subagentId,
|
|
39808
|
+
agentName: session.agentName,
|
|
39809
|
+
status,
|
|
39810
|
+
ts: now,
|
|
39811
|
+
summary,
|
|
39812
|
+
task: session.task
|
|
39813
|
+
});
|
|
39814
|
+
}
|
|
39815
|
+
// ── Internal ───────────────────────────────────────────────────────
|
|
39816
|
+
_routeEvent(subagentId, type, payload) {
|
|
39817
|
+
const session = this._sessions.get(subagentId);
|
|
39818
|
+
if (!session) return;
|
|
39819
|
+
switch (type) {
|
|
39820
|
+
case "provider.text_delta": {
|
|
39821
|
+
const text = payload.text;
|
|
39822
|
+
if (!text || text.length === 0) return;
|
|
39823
|
+
const iteration = payload.iteration ?? 0;
|
|
39824
|
+
this._addEntry(subagentId, {
|
|
39825
|
+
id: this._uid(),
|
|
39826
|
+
subagentId,
|
|
39827
|
+
agentName: session.agentName,
|
|
39828
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
39829
|
+
kind: "text",
|
|
39830
|
+
content: text,
|
|
39831
|
+
iteration
|
|
39832
|
+
});
|
|
39833
|
+
break;
|
|
39834
|
+
}
|
|
39835
|
+
case "provider.thinking_delta": {
|
|
39836
|
+
const text = payload.text;
|
|
39837
|
+
if (!text || text.length === 0) return;
|
|
39838
|
+
const iteration = payload.iteration ?? 0;
|
|
39839
|
+
this._addEntry(subagentId, {
|
|
39840
|
+
id: this._uid(),
|
|
39841
|
+
subagentId,
|
|
39842
|
+
agentName: session.agentName,
|
|
39843
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
39844
|
+
kind: "text",
|
|
39845
|
+
content: `\u{1F9E0} ${text}`,
|
|
39846
|
+
iteration
|
|
39847
|
+
});
|
|
39848
|
+
break;
|
|
39849
|
+
}
|
|
39850
|
+
case "tool.started": {
|
|
39851
|
+
const name = payload.name;
|
|
39852
|
+
if (!name) return;
|
|
39853
|
+
this._addEntry(subagentId, {
|
|
39854
|
+
id: this._uid(),
|
|
39855
|
+
subagentId,
|
|
39856
|
+
agentName: session.agentName,
|
|
39857
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
39858
|
+
kind: "tool_use",
|
|
39859
|
+
content: `\u{1F527} ${name}()`,
|
|
39860
|
+
iteration: payload.iteration ?? 0,
|
|
39861
|
+
toolName: name
|
|
39862
|
+
});
|
|
39863
|
+
break;
|
|
39864
|
+
}
|
|
39865
|
+
case "tool.executed": {
|
|
39866
|
+
const name = payload.name;
|
|
39867
|
+
const ok = payload.ok;
|
|
39868
|
+
const durationMs = payload.durationMs;
|
|
39869
|
+
if (!name) return;
|
|
39870
|
+
const statusIcon2 = ok ? "\u2705" : "\u274C";
|
|
39871
|
+
const duration = durationMs !== void 0 ? ` (${durationMs}ms)` : "";
|
|
39872
|
+
this._addEntry(subagentId, {
|
|
39873
|
+
id: this._uid(),
|
|
39874
|
+
subagentId,
|
|
39875
|
+
agentName: session.agentName,
|
|
39876
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
39877
|
+
kind: "tool_result",
|
|
39878
|
+
content: `${statusIcon2} ${name}${duration}`,
|
|
39879
|
+
iteration: payload.iteration ?? 0,
|
|
39880
|
+
toolName: name,
|
|
39881
|
+
toolOk: ok
|
|
39882
|
+
});
|
|
39883
|
+
break;
|
|
39884
|
+
}
|
|
39885
|
+
case "iteration.completed": {
|
|
39886
|
+
const index = payload.index ?? 0;
|
|
39887
|
+
if (index > 0 && index % 5 === 0) {
|
|
39888
|
+
this._addEntry(subagentId, {
|
|
39889
|
+
id: this._uid(),
|
|
39890
|
+
subagentId,
|
|
39891
|
+
agentName: session.agentName,
|
|
39892
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
39893
|
+
kind: "status",
|
|
39894
|
+
content: `\u{1F504} Iteration ${index}`,
|
|
39895
|
+
iteration: index
|
|
39896
|
+
});
|
|
39897
|
+
}
|
|
39898
|
+
break;
|
|
39899
|
+
}
|
|
39900
|
+
}
|
|
39901
|
+
}
|
|
39902
|
+
_addEntry(subagentId, entry) {
|
|
39903
|
+
const session = this._sessions.get(subagentId);
|
|
39904
|
+
if (!session) return;
|
|
39905
|
+
session.transcript.push(entry);
|
|
39906
|
+
if (session.transcript.length > this._maxEntries) {
|
|
39907
|
+
session.transcript.splice(0, session.transcript.length - this._maxEntries);
|
|
39908
|
+
}
|
|
39909
|
+
this._appendToFile(subagentId, entry).catch(() => {
|
|
39910
|
+
});
|
|
39911
|
+
this._events.emit("agent.timeline.message", {
|
|
39912
|
+
subagentId: entry.subagentId,
|
|
39913
|
+
agentName: entry.agentName,
|
|
39914
|
+
content: entry.content,
|
|
39915
|
+
kind: entry.kind === "tool_result" ? "tool_use" : entry.kind === "system" ? "status" : entry.kind,
|
|
39916
|
+
iteration: entry.iteration,
|
|
39917
|
+
ts: entry.ts,
|
|
39918
|
+
toolName: entry.toolName,
|
|
39919
|
+
costUsd: entry.costUsd
|
|
39920
|
+
});
|
|
39921
|
+
this._onEntry?.(entry);
|
|
39922
|
+
}
|
|
39923
|
+
async _appendToFile(subagentId, entry) {
|
|
39924
|
+
const dir = path3.join(this._transcriptsDir, subagentId);
|
|
39925
|
+
await fsp3.mkdir(dir, { recursive: true });
|
|
39926
|
+
const filePath = path3.join(dir, "transcript.jsonl");
|
|
39927
|
+
const line = JSON.stringify(entry) + "\n";
|
|
39928
|
+
await fsp3.appendFile(filePath, line, { encoding: "utf8" });
|
|
39929
|
+
}
|
|
39930
|
+
_uid() {
|
|
39931
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
|
39932
|
+
}
|
|
39933
|
+
};
|
|
39934
|
+
function createAgentMonitorService(opts) {
|
|
39935
|
+
return new AgentMonitorService(opts);
|
|
39936
|
+
}
|
|
39937
|
+
|
|
39938
|
+
// src/coordination/adaptive-concurrency.ts
|
|
39939
|
+
var DEFAULT_CONFIG = Object.freeze({
|
|
39940
|
+
enabled: false,
|
|
39941
|
+
minConcurrent: 1,
|
|
39942
|
+
maxConcurrent: 16,
|
|
39943
|
+
decreaseFactor: 0.5,
|
|
39944
|
+
successThreshold: 10,
|
|
39945
|
+
recoveryIntervalMs: 3e4
|
|
39946
|
+
});
|
|
39947
|
+
var AdaptiveConcurrencyController = class {
|
|
39948
|
+
config;
|
|
39949
|
+
state;
|
|
39950
|
+
disposers = [];
|
|
39951
|
+
stateChangeHandlers = [];
|
|
39952
|
+
constructor(fleetBus, setMaxConcurrent, config = {}, onStateChange) {
|
|
39953
|
+
this.config = {
|
|
39954
|
+
enabled: config.enabled ?? DEFAULT_CONFIG.enabled,
|
|
39955
|
+
minConcurrent: config.minConcurrent ?? DEFAULT_CONFIG.minConcurrent,
|
|
39956
|
+
maxConcurrent: config.maxConcurrent ?? DEFAULT_CONFIG.maxConcurrent,
|
|
39957
|
+
decreaseFactor: config.decreaseFactor ?? DEFAULT_CONFIG.decreaseFactor,
|
|
39958
|
+
successThreshold: config.successThreshold ?? DEFAULT_CONFIG.successThreshold,
|
|
39959
|
+
recoveryIntervalMs: config.recoveryIntervalMs ?? DEFAULT_CONFIG.recoveryIntervalMs
|
|
39960
|
+
};
|
|
39961
|
+
this.state = {
|
|
39962
|
+
current: this.config.maxConcurrent,
|
|
39963
|
+
min: this.config.minConcurrent,
|
|
39964
|
+
max: this.config.maxConcurrent,
|
|
39965
|
+
consecutiveSuccesses: 0,
|
|
39966
|
+
consecutiveFailures: 0,
|
|
39967
|
+
totalDecreases: 0,
|
|
39968
|
+
totalIncreases: 0,
|
|
39969
|
+
enabled: this.config.enabled
|
|
39970
|
+
};
|
|
39971
|
+
if (onStateChange) {
|
|
39972
|
+
this.stateChangeHandlers.push(onStateChange);
|
|
39973
|
+
}
|
|
39974
|
+
if (this.config.enabled) {
|
|
39975
|
+
setMaxConcurrent(this.state.current);
|
|
39976
|
+
}
|
|
39977
|
+
this.setupEventHandlers(fleetBus, setMaxConcurrent);
|
|
39978
|
+
}
|
|
39979
|
+
setupEventHandlers(fleetBus, setMaxConcurrent) {
|
|
39980
|
+
if (!this.config.enabled) return;
|
|
39981
|
+
const off = fleetBus.onAny((event) => {
|
|
39982
|
+
if (!this.config.enabled) return;
|
|
39983
|
+
if (event.type === "error" || event.type === "provider_error") {
|
|
39984
|
+
const payload = event.payload;
|
|
39985
|
+
if (payload?.status === 429 || payload?.code === "rate_limit_error" || payload?.kind === "rate_limit") {
|
|
39986
|
+
this.handleRateLimit(setMaxConcurrent);
|
|
39987
|
+
}
|
|
39988
|
+
}
|
|
39989
|
+
});
|
|
39990
|
+
this.disposers.push(off);
|
|
39991
|
+
}
|
|
39992
|
+
/**
|
|
39993
|
+
* Handle a rate limit (429) error - decrease concurrency
|
|
39994
|
+
*/
|
|
39995
|
+
handleRateLimit(setMaxConcurrent) {
|
|
39996
|
+
if (this.state.current <= this.config.minConcurrent) {
|
|
39997
|
+
this.state.consecutiveFailures++;
|
|
39998
|
+
this.state.consecutiveSuccesses = 0;
|
|
39999
|
+
this.notifyStateChange();
|
|
40000
|
+
return;
|
|
40001
|
+
}
|
|
40002
|
+
const newConcurrent = Math.max(
|
|
40003
|
+
this.config.minConcurrent,
|
|
40004
|
+
Math.floor(this.state.current * this.config.decreaseFactor)
|
|
40005
|
+
);
|
|
40006
|
+
if (newConcurrent < this.state.current) {
|
|
40007
|
+
const previousConcurrent = this.state.current;
|
|
40008
|
+
this.state.current = newConcurrent;
|
|
40009
|
+
this.state.consecutiveFailures++;
|
|
40010
|
+
this.state.consecutiveSuccesses = 0;
|
|
40011
|
+
this.state.totalDecreases++;
|
|
40012
|
+
setMaxConcurrent(this.state.current);
|
|
40013
|
+
this.notifyStateChange();
|
|
40014
|
+
console.log(
|
|
40015
|
+
JSON.stringify({
|
|
40016
|
+
level: "warn",
|
|
40017
|
+
event: "adaptive_concurrency.decreased",
|
|
40018
|
+
reason: "rate_limit",
|
|
40019
|
+
previousConcurrent,
|
|
40020
|
+
newConcurrent: this.state.current,
|
|
40021
|
+
decreaseFactor: this.config.decreaseFactor,
|
|
40022
|
+
totalDecreases: this.state.totalDecreases
|
|
40023
|
+
})
|
|
40024
|
+
);
|
|
40025
|
+
}
|
|
40026
|
+
}
|
|
40027
|
+
/**
|
|
40028
|
+
* Force a decrease (e.g., manual trigger or other error types)
|
|
40029
|
+
*/
|
|
40030
|
+
decrease(target) {
|
|
40031
|
+
if (!this.config.enabled) return;
|
|
40032
|
+
const newConcurrent = target ?? Math.max(
|
|
40033
|
+
this.config.minConcurrent,
|
|
40034
|
+
Math.floor(this.state.current * this.config.decreaseFactor)
|
|
40035
|
+
);
|
|
40036
|
+
if (newConcurrent < this.state.current) {
|
|
40037
|
+
const previousConcurrent = this.state.current;
|
|
40038
|
+
this.state.current = newConcurrent;
|
|
40039
|
+
this.state.consecutiveSuccesses = 0;
|
|
40040
|
+
this.state.totalDecreases++;
|
|
40041
|
+
this.notifyStateChange();
|
|
40042
|
+
console.log(
|
|
40043
|
+
JSON.stringify({
|
|
40044
|
+
level: "warn",
|
|
40045
|
+
event: "adaptive_concurrency.decreased",
|
|
40046
|
+
reason: "manual",
|
|
40047
|
+
previousConcurrent,
|
|
40048
|
+
newConcurrent: this.state.current,
|
|
40049
|
+
totalDecreases: this.state.totalDecreases
|
|
40050
|
+
})
|
|
40051
|
+
);
|
|
40052
|
+
}
|
|
40053
|
+
}
|
|
40054
|
+
/**
|
|
40055
|
+
* Get the current state
|
|
40056
|
+
*/
|
|
40057
|
+
getState() {
|
|
40058
|
+
return { ...this.state };
|
|
40059
|
+
}
|
|
40060
|
+
/**
|
|
40061
|
+
* Update configuration at runtime
|
|
40062
|
+
*/
|
|
40063
|
+
updateConfig(config) {
|
|
40064
|
+
if (config.enabled !== void 0) {
|
|
40065
|
+
this.config.enabled = config.enabled;
|
|
40066
|
+
}
|
|
40067
|
+
if (config.minConcurrent !== void 0) {
|
|
40068
|
+
this.config.minConcurrent = config.minConcurrent;
|
|
40069
|
+
}
|
|
40070
|
+
if (config.maxConcurrent !== void 0) {
|
|
40071
|
+
this.config.maxConcurrent = config.maxConcurrent;
|
|
40072
|
+
}
|
|
40073
|
+
if (config.decreaseFactor !== void 0) {
|
|
40074
|
+
this.config.decreaseFactor = config.decreaseFactor;
|
|
40075
|
+
}
|
|
40076
|
+
if (config.successThreshold !== void 0) {
|
|
40077
|
+
this.config.successThreshold = config.successThreshold;
|
|
40078
|
+
}
|
|
40079
|
+
if (config.recoveryIntervalMs !== void 0) {
|
|
40080
|
+
this.config.recoveryIntervalMs = config.recoveryIntervalMs;
|
|
40081
|
+
}
|
|
40082
|
+
this.state.current = Math.max(this.config.minConcurrent, Math.min(this.state.current, this.config.maxConcurrent));
|
|
40083
|
+
this.state.enabled = this.config.enabled;
|
|
40084
|
+
this.state.min = this.config.minConcurrent;
|
|
40085
|
+
this.state.max = this.config.maxConcurrent;
|
|
40086
|
+
this.notifyStateChange();
|
|
40087
|
+
}
|
|
40088
|
+
/**
|
|
40089
|
+
* Dispose of the controller and clean up event listeners
|
|
40090
|
+
*/
|
|
40091
|
+
dispose() {
|
|
40092
|
+
for (const dispose of this.disposers) {
|
|
40093
|
+
dispose();
|
|
40094
|
+
}
|
|
40095
|
+
this.disposers.length = 0;
|
|
40096
|
+
this.stateChangeHandlers = [];
|
|
40097
|
+
}
|
|
40098
|
+
notifyStateChange() {
|
|
40099
|
+
const state = this.getState();
|
|
40100
|
+
for (const handler of this.stateChangeHandlers) {
|
|
40101
|
+
handler(state);
|
|
40102
|
+
}
|
|
40103
|
+
}
|
|
40104
|
+
/**
|
|
40105
|
+
* Register a state change handler
|
|
40106
|
+
*/
|
|
40107
|
+
onStateChange(handler) {
|
|
40108
|
+
this.stateChangeHandlers.push(handler);
|
|
40109
|
+
return () => {
|
|
40110
|
+
const index = this.stateChangeHandlers.indexOf(handler);
|
|
40111
|
+
if (index !== -1) {
|
|
40112
|
+
this.stateChangeHandlers.splice(index, 1);
|
|
40113
|
+
}
|
|
40114
|
+
};
|
|
40115
|
+
}
|
|
40116
|
+
};
|
|
38340
40117
|
|
|
38341
40118
|
// src/tools/mcp-control.ts
|
|
38342
40119
|
function createMcpControlTool(opts) {
|
|
@@ -38364,11 +40141,18 @@ function createMcpControlTool(opts) {
|
|
|
38364
40141
|
};
|
|
38365
40142
|
return {
|
|
38366
40143
|
name: "mcp_control",
|
|
38367
|
-
description: "Manage MCP server lifecycle: list available servers, search by name or capability, enable or disable servers at runtime, restart running servers. Use activate/deactivate to ephemerally toggle tool registration without disconnecting \u2014 ideal for token-saving mode where MCP tools are lazy-loaded on demand.",
|
|
40144
|
+
description: "Manage MCP server lifecycle: list available servers, search by name or capability, enable or disable servers at runtime, restart running servers. Use activate/deactivate to ephemerally toggle tool registration without disconnecting \u2014 ideal for token-saving mode where MCP tools are lazy-loaded on demand. NOTE: `enable`/`restart` start a server process, which for the built-in stdio presets runs `npx -y <package>` \u2014 i.e. it fetches and executes an npm package from the network. Treat it as code execution.",
|
|
38368
40145
|
category: "mcp",
|
|
38369
40146
|
permission: "auto",
|
|
38370
40147
|
mutating: true,
|
|
38371
|
-
|
|
40148
|
+
// `enable`/`restart` spawn a server process that, for the stdio presets,
|
|
40149
|
+
// fetches and runs an npm package (`npx -y <pkg>`) — effectively remote
|
|
40150
|
+
// code execution. Marking the tool destructive means the YOLO
|
|
40151
|
+
// `confirmDestructive` safety net still prompts before it runs (plain
|
|
40152
|
+
// non-YOLO already confirms via the CONFIG_MUTATE dangerous-capability
|
|
40153
|
+
// net in the executor). Read-only actions (list/search) ride the same
|
|
40154
|
+
// tool but are cheap to confirm/trust once.
|
|
40155
|
+
riskTier: "destructive",
|
|
38372
40156
|
capabilities: [ToolCapabilities.CONFIG_MUTATE],
|
|
38373
40157
|
inputSchema,
|
|
38374
40158
|
async execute(raw) {
|
|
@@ -38890,13 +40674,13 @@ function createAgentToolHandler(a) {
|
|
|
38890
40674
|
}
|
|
38891
40675
|
}
|
|
38892
40676
|
function waitForConfirm(info) {
|
|
38893
|
-
return new Promise((
|
|
40677
|
+
return new Promise((resolve19) => {
|
|
38894
40678
|
a.events.emit("tool.confirm_needed", {
|
|
38895
40679
|
tool: info.tool,
|
|
38896
40680
|
input: info.input,
|
|
38897
40681
|
toolUseId: info.toolUseId,
|
|
38898
40682
|
suggestedPattern: info.suggestedPattern,
|
|
38899
|
-
resolve:
|
|
40683
|
+
resolve: resolve19
|
|
38900
40684
|
});
|
|
38901
40685
|
});
|
|
38902
40686
|
}
|
|
@@ -39433,12 +41217,12 @@ function attachMailboxCheckerInner(a, source) {
|
|
|
39433
41217
|
// src/core/iteration-limit.ts
|
|
39434
41218
|
function requestLimitExtension(opts) {
|
|
39435
41219
|
const { events, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
|
|
39436
|
-
return new Promise((
|
|
41220
|
+
return new Promise((resolve19) => {
|
|
39437
41221
|
let resolved = false;
|
|
39438
41222
|
const timerFired = () => {
|
|
39439
41223
|
if (!resolved) {
|
|
39440
41224
|
resolved = true;
|
|
39441
|
-
|
|
41225
|
+
resolve19(0);
|
|
39442
41226
|
}
|
|
39443
41227
|
};
|
|
39444
41228
|
const timer = setTimeout(timerFired, timeoutMs);
|
|
@@ -39447,14 +41231,14 @@ function requestLimitExtension(opts) {
|
|
|
39447
41231
|
if (!resolved) {
|
|
39448
41232
|
resolved = true;
|
|
39449
41233
|
clearTimeout(timer);
|
|
39450
|
-
|
|
41234
|
+
resolve19(0);
|
|
39451
41235
|
}
|
|
39452
41236
|
};
|
|
39453
41237
|
const grant = (extra) => {
|
|
39454
41238
|
if (!resolved) {
|
|
39455
41239
|
resolved = true;
|
|
39456
41240
|
clearTimeout(timer);
|
|
39457
|
-
|
|
41241
|
+
resolve19(Math.max(0, extra));
|
|
39458
41242
|
}
|
|
39459
41243
|
};
|
|
39460
41244
|
events.emit("iteration.limit_reached", {
|
|
@@ -39468,7 +41252,7 @@ function requestLimitExtension(opts) {
|
|
|
39468
41252
|
if (!resolved) {
|
|
39469
41253
|
resolved = true;
|
|
39470
41254
|
clearTimeout(timer);
|
|
39471
|
-
|
|
41255
|
+
resolve19(100);
|
|
39472
41256
|
}
|
|
39473
41257
|
});
|
|
39474
41258
|
}
|
|
@@ -39489,25 +41273,29 @@ function createAgentLoopHandler(a, handlers) {
|
|
|
39489
41273
|
const checkMailbox = attachMailboxChecker(a);
|
|
39490
41274
|
async function compactContextIfNeeded() {
|
|
39491
41275
|
const msgCount = a.ctx.messages.length;
|
|
39492
|
-
|
|
41276
|
+
const maxContext = currentMaxContext();
|
|
41277
|
+
if (_lastCompactionMsgCount === msgCount && _lastCompactionWasNoop && _lastCompactionMaxContext === maxContext && maxContext > 0) {
|
|
39493
41278
|
return;
|
|
39494
41279
|
}
|
|
39495
41280
|
await a.pipelines.contextWindow.run(a.ctx);
|
|
39496
41281
|
_lastCompactionMsgCount = msgCount;
|
|
41282
|
+
_lastCompactionMaxContext = maxContext;
|
|
39497
41283
|
const stashed = a.ctx.lastRequestTokens;
|
|
39498
41284
|
const tokens = typeof stashed === "number" && stashed > 0 ? stashed : 0;
|
|
39499
|
-
const load =
|
|
41285
|
+
const load = maxContext > 0 ? tokens / maxContext : 0;
|
|
39500
41286
|
_lastCompactionWasNoop = tokens > 0 && load < 0.5;
|
|
39501
41287
|
}
|
|
39502
41288
|
const calibrationKey = (model = a.ctx.model) => `${a.ctx.provider?.id ?? "unknown"}/${model}`;
|
|
39503
41289
|
function emitContextPct() {
|
|
39504
41290
|
const msgCount = a.ctx.messages.length;
|
|
39505
41291
|
const toolCount = (a.ctx.tools ?? []).length;
|
|
39506
|
-
|
|
41292
|
+
const maxContext = currentMaxContext();
|
|
41293
|
+
if (msgCount === _lastEmittedMsgCount && toolCount === _lastEmittedToolCount && maxContext === _lastEmittedMaxContext && maxContext > 0) {
|
|
39507
41294
|
return;
|
|
39508
41295
|
}
|
|
39509
41296
|
_lastEmittedMsgCount = msgCount;
|
|
39510
41297
|
_lastEmittedToolCount = toolCount;
|
|
41298
|
+
_lastEmittedMaxContext = maxContext;
|
|
39511
41299
|
if (msgCount !== _lastPreFlightMsgCount) {
|
|
39512
41300
|
a.ctx.lastRequestTokens = estimateRequestTokens(
|
|
39513
41301
|
a.ctx.messages,
|
|
@@ -39517,11 +41305,6 @@ function createAgentLoopHandler(a, handlers) {
|
|
|
39517
41305
|
_lastPreFlightMsgCount = msgCount;
|
|
39518
41306
|
a.ctx.meta["lastRequestTokensAt"] = { msgCount, toolCount };
|
|
39519
41307
|
}
|
|
39520
|
-
if (!_maxContext) {
|
|
39521
|
-
const metaLimit = a.ctx.meta?.["effectiveMaxContext"];
|
|
39522
|
-
const providerMax = a.ctx.provider.capabilities.maxContext;
|
|
39523
|
-
_maxContext = typeof metaLimit === "number" && metaLimit > 0 ? metaLimit : typeof providerMax === "number" && providerMax > 0 ? providerMax : 2e5;
|
|
39524
|
-
}
|
|
39525
41308
|
let total;
|
|
39526
41309
|
const stashed = a.ctx.lastRequestTokens;
|
|
39527
41310
|
if (typeof stashed === "number" && stashed > 0) {
|
|
@@ -39536,13 +41319,19 @@ function createAgentLoopHandler(a, handlers) {
|
|
|
39536
41319
|
);
|
|
39537
41320
|
total = est.total;
|
|
39538
41321
|
}
|
|
39539
|
-
a.events.emit("ctx.pct", { load: total /
|
|
41322
|
+
a.events.emit("ctx.pct", { load: total / maxContext, tokens: total, maxContext });
|
|
41323
|
+
}
|
|
41324
|
+
function currentMaxContext() {
|
|
41325
|
+
const metaLimit = a.ctx.meta?.["effectiveMaxContext"];
|
|
41326
|
+
const providerMax = a.ctx.provider.capabilities.maxContext;
|
|
41327
|
+
return typeof metaLimit === "number" && metaLimit > 0 ? metaLimit : typeof providerMax === "number" && providerMax > 0 ? providerMax : 2e5;
|
|
39540
41328
|
}
|
|
39541
|
-
let _maxContext = 0;
|
|
39542
41329
|
let _lastEmittedMsgCount = -1;
|
|
39543
41330
|
let _lastEmittedToolCount = -1;
|
|
41331
|
+
let _lastEmittedMaxContext = -1;
|
|
39544
41332
|
let _lastPreFlightMsgCount = -1;
|
|
39545
41333
|
let _lastCompactionMsgCount = -1;
|
|
41334
|
+
let _lastCompactionMaxContext = -1;
|
|
39546
41335
|
let _lastCompactionWasNoop = false;
|
|
39547
41336
|
function foldBlockIntoConversation(block) {
|
|
39548
41337
|
const messages = a.ctx.messages;
|
|
@@ -39612,7 +41401,8 @@ function createAgentLoopHandler(a, handlers) {
|
|
|
39612
41401
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
39613
41402
|
content: inputPayload.content
|
|
39614
41403
|
});
|
|
39615
|
-
|
|
41404
|
+
void a.ctx.session.flush().catch(() => {
|
|
41405
|
+
});
|
|
39616
41406
|
const promptIndex = a.ctx.messages.filter((m) => m.role === "user").length - 1;
|
|
39617
41407
|
const preview = inputPayload.text.slice(0, 80) + (inputPayload.text.length > 80 ? "\u2026" : "");
|
|
39618
41408
|
await a.ctx.session.writeCheckpoint(promptIndex, preview);
|
|
@@ -39993,12 +41783,26 @@ var Agent = class {
|
|
|
39993
41783
|
const { blocks, text } = normalizeInput(userInput);
|
|
39994
41784
|
const inputPayload = { content: blocks, text, ctx: this.ctx };
|
|
39995
41785
|
await this.extensions.runBeforeRun(this.ctx, inputPayload);
|
|
41786
|
+
const runStartedAt = Date.now();
|
|
41787
|
+
const runStartedIso = new Date(runStartedAt).toISOString();
|
|
39996
41788
|
try {
|
|
41789
|
+
this.events.emit("agent.run.started", {
|
|
41790
|
+
ctx: this.ctx,
|
|
41791
|
+
model: opts.model ?? this.ctx.model,
|
|
41792
|
+
at: runStartedIso
|
|
41793
|
+
});
|
|
39997
41794
|
const autonomousContinue = opts.autonomousContinue ?? this.autonomousContinue;
|
|
39998
41795
|
const result = await this._loopHandler.runInner(inputPayload, opts, controller, autonomousContinue);
|
|
39999
41796
|
span?.setAttribute("agent.status", result.status);
|
|
40000
41797
|
span?.setAttribute("agent.iterations", result.iterations);
|
|
40001
41798
|
await this.extensions.runAfterRun(this.ctx, result);
|
|
41799
|
+
this.events.emit("agent.run.completed", {
|
|
41800
|
+
ctx: this.ctx,
|
|
41801
|
+
status: result.status,
|
|
41802
|
+
iterations: result.iterations,
|
|
41803
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41804
|
+
durationMs: Date.now() - runStartedAt
|
|
41805
|
+
});
|
|
40002
41806
|
return result;
|
|
40003
41807
|
} catch (err) {
|
|
40004
41808
|
const wse = err instanceof AgentError ? err : toWrongStackError(err);
|
|
@@ -40013,6 +41817,21 @@ var Agent = class {
|
|
|
40013
41817
|
abortReason: signal.aborted ? signalAbortReason(signal) : void 0
|
|
40014
41818
|
};
|
|
40015
41819
|
await this.extensions.runAfterRun(this.ctx, result);
|
|
41820
|
+
if (result.status === "failed") {
|
|
41821
|
+
this.events.emit("agent.run.error", {
|
|
41822
|
+
ctx: this.ctx,
|
|
41823
|
+
err: safeError,
|
|
41824
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41825
|
+
durationMs: Date.now() - runStartedAt
|
|
41826
|
+
});
|
|
41827
|
+
}
|
|
41828
|
+
this.events.emit("agent.run.completed", {
|
|
41829
|
+
ctx: this.ctx,
|
|
41830
|
+
status: result.status,
|
|
41831
|
+
iterations: result.iterations,
|
|
41832
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41833
|
+
durationMs: Date.now() - runStartedAt
|
|
41834
|
+
});
|
|
40016
41835
|
return result;
|
|
40017
41836
|
} finally {
|
|
40018
41837
|
span?.end();
|
|
@@ -40170,13 +41989,13 @@ async function runShellHook(spec, input, logger) {
|
|
|
40170
41989
|
logger?.warn?.(`hook rejected: command not in allowlist: ${spec.command}`);
|
|
40171
41990
|
return null;
|
|
40172
41991
|
}
|
|
40173
|
-
return await new Promise((
|
|
41992
|
+
return await new Promise((resolve19) => {
|
|
40174
41993
|
let settled = false;
|
|
40175
41994
|
const done = (v) => {
|
|
40176
41995
|
if (settled) return;
|
|
40177
41996
|
settled = true;
|
|
40178
41997
|
clearTimeout(timer);
|
|
40179
|
-
|
|
41998
|
+
resolve19(v);
|
|
40180
41999
|
};
|
|
40181
42000
|
let child;
|
|
40182
42001
|
try {
|
|
@@ -40189,7 +42008,7 @@ async function runShellHook(spec, input, logger) {
|
|
|
40189
42008
|
});
|
|
40190
42009
|
} catch (err2) {
|
|
40191
42010
|
logger?.warn?.(`hook spawn failed: ${toErrorMessage(err2)}`);
|
|
40192
|
-
return
|
|
42011
|
+
return resolve19(null);
|
|
40193
42012
|
}
|
|
40194
42013
|
const timer = setTimeout(() => {
|
|
40195
42014
|
logger?.warn?.(`hook command timed out after ${timeoutMs}ms: ${spec.command}`);
|
|
@@ -40365,14 +42184,20 @@ var HookRunner = class {
|
|
|
40365
42184
|
};
|
|
40366
42185
|
|
|
40367
42186
|
// src/execution/model-runtime.ts
|
|
40368
|
-
function resolveModelRuntime(settings, reasoning) {
|
|
42187
|
+
function resolveModelRuntime(settings, reasoning, capabilities) {
|
|
40369
42188
|
const warnings = [];
|
|
40370
42189
|
if (!settings) {
|
|
40371
|
-
return { reasoning: void 0, cache: void 0, warnings };
|
|
42190
|
+
return { reasoning: void 0, cache: void 0, parameters: void 0, warnings };
|
|
40372
42191
|
}
|
|
40373
42192
|
const reasoningField = resolveReasoningForRequest(settings, reasoning, warnings);
|
|
40374
42193
|
const cacheField = resolveCacheForRequest(settings);
|
|
40375
|
-
|
|
42194
|
+
const paramsField = resolveParametersForRequest(settings.parameters, capabilities);
|
|
42195
|
+
return {
|
|
42196
|
+
reasoning: reasoningField,
|
|
42197
|
+
cache: cacheField,
|
|
42198
|
+
parameters: paramsField,
|
|
42199
|
+
warnings
|
|
42200
|
+
};
|
|
40376
42201
|
}
|
|
40377
42202
|
function resolveReasoningForRequest(settings, rc, warnings) {
|
|
40378
42203
|
const cfg = settings.reasoning;
|
|
@@ -40430,11 +42255,38 @@ function resolveCacheForRequest(settings, _warnings) {
|
|
|
40430
42255
|
const out = { ttl };
|
|
40431
42256
|
return out;
|
|
40432
42257
|
}
|
|
42258
|
+
function resolveParametersForRequest(params, caps, _warnings) {
|
|
42259
|
+
if (!params) return void 0;
|
|
42260
|
+
const out = {};
|
|
42261
|
+
if (params.topK !== void 0 && caps?.topK !== false) {
|
|
42262
|
+
out.topK = params.topK;
|
|
42263
|
+
}
|
|
42264
|
+
if (params.frequencyPenalty !== void 0 && caps?.frequencyPenalty !== false) {
|
|
42265
|
+
out.frequencyPenalty = params.frequencyPenalty;
|
|
42266
|
+
}
|
|
42267
|
+
if (params.presencePenalty !== void 0 && caps?.presencePenalty !== false) {
|
|
42268
|
+
out.presencePenalty = params.presencePenalty;
|
|
42269
|
+
}
|
|
42270
|
+
if (params.seed !== void 0 && caps?.seed !== false) {
|
|
42271
|
+
out.seed = params.seed;
|
|
42272
|
+
}
|
|
42273
|
+
if (params.user !== void 0) {
|
|
42274
|
+
out.user = params.user;
|
|
42275
|
+
}
|
|
42276
|
+
if (params.logprobs !== void 0 && caps?.logprobs !== false) {
|
|
42277
|
+
out.logprobs = params.logprobs;
|
|
42278
|
+
if (params.topLogprobs !== void 0) {
|
|
42279
|
+
out.topLogprobs = params.topLogprobs;
|
|
42280
|
+
}
|
|
42281
|
+
}
|
|
42282
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
42283
|
+
}
|
|
40433
42284
|
function applyModelRuntime(req, opts) {
|
|
40434
42285
|
const settings = opts.getSettings();
|
|
40435
42286
|
if (!settings) return req;
|
|
40436
42287
|
const rc = opts.getReasoningConfig();
|
|
40437
|
-
const
|
|
42288
|
+
const caps = opts.getCapabilities?.();
|
|
42289
|
+
const resolved = resolveModelRuntime(settings, rc, caps);
|
|
40438
42290
|
for (const w of resolved.warnings) opts.onWarning?.(w);
|
|
40439
42291
|
const next = { ...req };
|
|
40440
42292
|
if (resolved.reasoning !== void 0) {
|
|
@@ -40443,6 +42295,9 @@ function applyModelRuntime(req, opts) {
|
|
|
40443
42295
|
if (resolved.cache !== void 0) {
|
|
40444
42296
|
next.cache = resolved.cache;
|
|
40445
42297
|
}
|
|
42298
|
+
if (resolved.parameters !== void 0) {
|
|
42299
|
+
Object.assign(next, resolved.parameters);
|
|
42300
|
+
}
|
|
40446
42301
|
return next;
|
|
40447
42302
|
}
|
|
40448
42303
|
async function bootConfig(options = {}) {
|
|
@@ -40765,8 +42620,8 @@ var InputBuilder = class {
|
|
|
40765
42620
|
async registerFile(input) {
|
|
40766
42621
|
const ref = await this.store.add({ ...input, kind: "file" });
|
|
40767
42622
|
this.refs.push(ref);
|
|
40768
|
-
const
|
|
40769
|
-
return `[file:${
|
|
42623
|
+
const path51 = ref.meta.filename ?? ref.meta.label ?? String(ref.seq);
|
|
42624
|
+
return `[file:${path51}]`;
|
|
40770
42625
|
}
|
|
40771
42626
|
/**
|
|
40772
42627
|
* Whether `appendPaste(text)` would collapse the text to a placeholder
|
|
@@ -40915,9 +42770,9 @@ var DefaultSystemPromptBuilder = class {
|
|
|
40915
42770
|
skillBodyCache;
|
|
40916
42771
|
/** Tools from last build — used for memory relevance scoring. */
|
|
40917
42772
|
_lastBuildTools;
|
|
40918
|
-
/** Cached rendered online agents string, keyed by
|
|
42773
|
+
/** Cached rendered online agents string, keyed by content fingerprint. */
|
|
40919
42774
|
_lastOnlineAgents;
|
|
40920
|
-
/** Cached full buildToolUsage output — keyed by tools array +
|
|
42775
|
+
/** Cached full buildToolUsage output — keyed by tools array ref + agents fingerprint. */
|
|
40921
42776
|
_toolsUsageCache;
|
|
40922
42777
|
/**
|
|
40923
42778
|
* Normalizes `tokenSavingMode` to a boolean for backward-compatible boolean checks.
|
|
@@ -41053,13 +42908,13 @@ var DefaultSystemPromptBuilder = class {
|
|
|
41053
42908
|
if (!planPath) return "";
|
|
41054
42909
|
let raw;
|
|
41055
42910
|
try {
|
|
41056
|
-
const
|
|
41057
|
-
if (this._planCache && this._planCache.path === planPath && this._planCache.mtimeMs ===
|
|
42911
|
+
const stat16 = await fsp3.stat(planPath);
|
|
42912
|
+
if (this._planCache && this._planCache.path === planPath && this._planCache.mtimeMs === stat16.mtimeMs) {
|
|
41058
42913
|
return this._planCache.text;
|
|
41059
42914
|
}
|
|
41060
42915
|
raw = await fsp3.readFile(planPath, "utf8");
|
|
41061
42916
|
const text = this._formatPlan(raw);
|
|
41062
|
-
this._planCache = { path: planPath, mtimeMs:
|
|
42917
|
+
this._planCache = { path: planPath, mtimeMs: stat16.mtimeMs, text };
|
|
41063
42918
|
return text;
|
|
41064
42919
|
} catch {
|
|
41065
42920
|
this._planCache = void 0;
|
|
@@ -41074,8 +42929,8 @@ var DefaultSystemPromptBuilder = class {
|
|
|
41074
42929
|
return "";
|
|
41075
42930
|
}
|
|
41076
42931
|
if (!Array.isArray(parsed.items) || parsed.items.length === 0) return "";
|
|
41077
|
-
const
|
|
41078
|
-
if (
|
|
42932
|
+
const open10 = parsed.items.filter((i) => i?.status !== "done");
|
|
42933
|
+
if (open10.length === 0) return "";
|
|
41079
42934
|
const lines = ["## Active plan"];
|
|
41080
42935
|
if (parsed.title) lines.push(`*${parsed.title}*`, "");
|
|
41081
42936
|
parsed.items.forEach((it, idx) => {
|
|
@@ -41090,7 +42945,8 @@ var DefaultSystemPromptBuilder = class {
|
|
|
41090
42945
|
}
|
|
41091
42946
|
buildToolUsage(tools, ctx) {
|
|
41092
42947
|
if (tools.length === 0) return "## Tool usage\n\nNo tools registered.";
|
|
41093
|
-
|
|
42948
|
+
const agentsHash = this.agentsFingerprint(ctx.onlineAgents);
|
|
42949
|
+
if (this._toolsUsageCache?.toolsRef === tools && this._toolsUsageCache?.agentsHash === agentsHash) {
|
|
41094
42950
|
return this._toolsUsageCache.text;
|
|
41095
42951
|
}
|
|
41096
42952
|
const byCat = /* @__PURE__ */ new Map();
|
|
@@ -41377,7 +43233,7 @@ the server connection \u2014 only tool visibility changes.`);
|
|
|
41377
43233
|
|
|
41378
43234
|
Use \`context_manager\` to manage context. Call \`{"action":"check"}\` to see token budget.`);
|
|
41379
43235
|
} else {
|
|
41380
|
-
const maxCtx = this.
|
|
43236
|
+
const maxCtx = this.modelCapabilities()?.maxContextTokens ?? 128e3;
|
|
41381
43237
|
const threshold = maxCtx <= 32e3 ? "50" : "70";
|
|
41382
43238
|
lines.push(`
|
|
41383
43239
|
## Context management
|
|
@@ -41395,15 +43251,38 @@ summarize it, and let the tool result hold only the summary.`);
|
|
|
41395
43251
|
}
|
|
41396
43252
|
}
|
|
41397
43253
|
const text = lines.join("\n");
|
|
41398
|
-
this._toolsUsageCache = { toolsRef: tools,
|
|
43254
|
+
this._toolsUsageCache = { toolsRef: tools, agentsHash, text };
|
|
41399
43255
|
return text;
|
|
41400
43256
|
}
|
|
41401
43257
|
/**
|
|
41402
|
-
*
|
|
43258
|
+
* Cheap content fingerprint of the online agents array. The mailbox
|
|
43259
|
+
* rebuilds the array as a fresh object on every status check, so caching
|
|
43260
|
+
* by reference always misses — this lets the renderOnlineAgents and
|
|
43261
|
+
* buildToolUsage caches detect membership changes instead.
|
|
43262
|
+
*
|
|
43263
|
+
* O(n) over agent names with no per-element string concatenation. Uses
|
|
43264
|
+
* FNV-1a over character codes so two different agent sets collide only
|
|
43265
|
+
* if they produce the identical sequence of name characters — astronomically
|
|
43266
|
+
* unlikely. A collision would produce a stale agent list in the prompt,
|
|
43267
|
+
* a cosmetic issue, not a correctness bug.
|
|
43268
|
+
*/
|
|
43269
|
+
agentsFingerprint(agents) {
|
|
43270
|
+
if (!agents || agents.length === 0) return "0";
|
|
43271
|
+
let h = 2166136261;
|
|
43272
|
+
for (const a of agents) {
|
|
43273
|
+
for (let i = 0; i < a.name.length; i++) {
|
|
43274
|
+
h ^= a.name.charCodeAt(i);
|
|
43275
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
43276
|
+
}
|
|
43277
|
+
}
|
|
43278
|
+
return `${agents.length}:${h.toString(36)}`;
|
|
43279
|
+
}
|
|
43280
|
+
/**
|
|
43281
|
+
* Render the online agents list, cached by content fingerprint. The agents
|
|
41403
43282
|
* list changes at join/leave pace (seconds to minutes), not every prompt
|
|
41404
|
-
* build turn (hundreds of ms).
|
|
41405
|
-
* the
|
|
41406
|
-
*
|
|
43283
|
+
* build turn (hundreds of ms). The fingerprint detects membership changes
|
|
43284
|
+
* without holding the array reference — the mailbox rebuilds the array as
|
|
43285
|
+
* a fresh object on every status check, so reference equality always misses.
|
|
41407
43286
|
*
|
|
41408
43287
|
* Tier behaviour:
|
|
41409
43288
|
* - 'off' / 'medium' / 'aggressive' → full list with names, sessions, sources
|
|
@@ -41411,13 +43290,14 @@ summarize it, and let the tool result hold only the summary.`);
|
|
|
41411
43290
|
*/
|
|
41412
43291
|
renderOnlineAgents(agents) {
|
|
41413
43292
|
if (!agents || agents.length === 0) return "";
|
|
41414
|
-
|
|
43293
|
+
const hash = this.agentsFingerprint(agents);
|
|
43294
|
+
if (this._lastOnlineAgents?.hash === hash) {
|
|
41415
43295
|
return this._lastOnlineAgents.text;
|
|
41416
43296
|
}
|
|
41417
43297
|
const totalCount = agents.length;
|
|
41418
43298
|
if (this.tier === "minimal" || this.tier === "light") {
|
|
41419
43299
|
const text2 = ` (${totalCount} agent${totalCount !== 1 ? "s" : ""} online)`;
|
|
41420
|
-
this._lastOnlineAgents = {
|
|
43300
|
+
this._lastOnlineAgents = { hash, text: text2 };
|
|
41421
43301
|
return text2;
|
|
41422
43302
|
}
|
|
41423
43303
|
const agentList = agents.map(
|
|
@@ -41427,11 +43307,21 @@ summarize it, and let the tool result hold only the summary.`);
|
|
|
41427
43307
|
|
|
41428
43308
|
**Currently online (${totalCount} agent${totalCount !== 1 ? "s" : ""}):**
|
|
41429
43309
|
${agentList}`;
|
|
41430
|
-
this._lastOnlineAgents = {
|
|
43310
|
+
this._lastOnlineAgents = { hash, text };
|
|
41431
43311
|
return text;
|
|
41432
43312
|
}
|
|
41433
43313
|
async buildEnvironment(ctx) {
|
|
41434
|
-
const
|
|
43314
|
+
const modelCapabilities = this.modelCapabilities();
|
|
43315
|
+
const cacheKey = [
|
|
43316
|
+
ctx.projectRoot,
|
|
43317
|
+
ctx.provider ?? "",
|
|
43318
|
+
ctx.model ?? "",
|
|
43319
|
+
modelCapabilities?.maxContextTokens ?? 0,
|
|
43320
|
+
modelCapabilities?.supportsTools ? 1 : 0,
|
|
43321
|
+
modelCapabilities?.supportsVision ? 1 : 0,
|
|
43322
|
+
modelCapabilities?.supportsReasoning ? 1 : 0
|
|
43323
|
+
].join("\0");
|
|
43324
|
+
const cached = this.envCacheByRoot.get(cacheKey);
|
|
41435
43325
|
if (cached) return cached;
|
|
41436
43326
|
const today = this.opts.todayIso ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
41437
43327
|
const platform2 = `${os6.platform()} ${os6.release()}`;
|
|
@@ -41463,15 +43353,15 @@ ${agentList}`;
|
|
|
41463
43353
|
`- Running on: ${ctx.provider ?? "<unknown provider>"}/${ctx.model ?? "<unknown model>"}`
|
|
41464
43354
|
);
|
|
41465
43355
|
}
|
|
41466
|
-
if (
|
|
43356
|
+
if (modelCapabilities) {
|
|
41467
43357
|
lines.push(
|
|
41468
|
-
`- Context window: ${
|
|
43358
|
+
`- Context window: ${modelCapabilities.maxContextTokens.toLocaleString()} tokens max`
|
|
41469
43359
|
);
|
|
41470
43360
|
}
|
|
41471
43361
|
}
|
|
41472
|
-
if (tier !== "aggressive" &&
|
|
43362
|
+
if (tier !== "aggressive" && modelCapabilities) {
|
|
41473
43363
|
lines.push(
|
|
41474
|
-
`- Context window: ${
|
|
43364
|
+
`- Context window: ${modelCapabilities.maxContextTokens.toLocaleString()} tokens max`
|
|
41475
43365
|
);
|
|
41476
43366
|
}
|
|
41477
43367
|
if (tier !== "aggressive" && (ctx.provider || ctx.model)) {
|
|
@@ -41493,9 +43383,13 @@ ${agentList}`;
|
|
|
41493
43383
|
);
|
|
41494
43384
|
}
|
|
41495
43385
|
const text = lines.join("\n");
|
|
41496
|
-
this.envCacheByRoot.set(
|
|
43386
|
+
this.envCacheByRoot.set(cacheKey, text);
|
|
41497
43387
|
return text;
|
|
41498
43388
|
}
|
|
43389
|
+
modelCapabilities() {
|
|
43390
|
+
const caps = this.opts.modelCapabilities;
|
|
43391
|
+
return typeof caps === "function" ? caps() : caps;
|
|
43392
|
+
}
|
|
41499
43393
|
async buildMemoryAndSkills() {
|
|
41500
43394
|
const parts = [];
|
|
41501
43395
|
const memoryCount = this.tier === "minimal" ? 3 : this.tier === "light" ? 5 : 8;
|
|
@@ -41621,19 +43515,19 @@ ${clean.trim()}`);
|
|
|
41621
43515
|
}
|
|
41622
43516
|
async dirExists(p) {
|
|
41623
43517
|
try {
|
|
41624
|
-
const
|
|
41625
|
-
return
|
|
43518
|
+
const stat16 = await fsp3.stat(p);
|
|
43519
|
+
return stat16.isDirectory();
|
|
41626
43520
|
} catch {
|
|
41627
43521
|
return false;
|
|
41628
43522
|
}
|
|
41629
43523
|
}
|
|
41630
43524
|
async gitStatus(root) {
|
|
41631
|
-
return new Promise((
|
|
43525
|
+
return new Promise((resolve19) => {
|
|
41632
43526
|
let settled = false;
|
|
41633
43527
|
const finish = (s) => {
|
|
41634
43528
|
if (settled) return;
|
|
41635
43529
|
settled = true;
|
|
41636
|
-
|
|
43530
|
+
resolve19(s);
|
|
41637
43531
|
};
|
|
41638
43532
|
let proc;
|
|
41639
43533
|
const timer = setTimeout(() => {
|
|
@@ -42554,18 +44448,21 @@ Rules:
|
|
|
42554
44448
|
- Be concise: one tight instruction per version (a few sentences at most). No preamble, no explanation, no quotes, no markdown headers.
|
|
42555
44449
|
- If the message is already clear and complete, return it essentially unchanged.
|
|
42556
44450
|
|
|
42557
|
-
|
|
42558
|
-
|
|
42559
|
-
-
|
|
44451
|
+
Detect the language of the user's LATEST message and output accordingly:
|
|
44452
|
+
|
|
44453
|
+
- If that message is ALREADY in English: output exactly ONE refined version, in English. Nothing else \u2014 no "---" line, no second copy.
|
|
44454
|
+
- If that message is in ANY OTHER language (Turkish, Spanish, \u2026): output TWO versions separated by a line containing only "---":
|
|
44455
|
+
- First version: refined in the SAME LANGUAGE the user wrote in.
|
|
44456
|
+
- Second version: refined in ENGLISH (translate the intent into clear English while preserving all concrete details).
|
|
42560
44457
|
|
|
42561
|
-
Output format:
|
|
44458
|
+
Output format for non-English input:
|
|
42562
44459
|
<refined in user's language>
|
|
42563
44460
|
---
|
|
42564
44461
|
<refined in English>
|
|
42565
44462
|
|
|
42566
|
-
When earlier conversation turns are provided, they are CONTEXT ONLY. Use them to resolve references in the user's latest message \u2014 "it", "that", "the same", "the other one", "this file", "again" \u2014 so the refined instruction is self-contained. Refine ONLY the user's latest message; do not answer it, do not act on or restate earlier turns, and do not summarize the conversation.
|
|
44463
|
+
When earlier conversation turns are provided, they are CONTEXT ONLY. Use them to resolve references in the user's latest message \u2014 "it", "that", "the same", "the other one", "this file", "again" \u2014 so the refined instruction is self-contained. Refine ONLY the user's latest message; do not answer it, do not act on or restate earlier turns, and do not summarize the conversation. The conversation language does NOT decide the output language \u2014 only the language of the latest message does.
|
|
42567
44464
|
|
|
42568
|
-
Output ONLY the
|
|
44465
|
+
Output ONLY the refined request(s) in the format above \u2014 nothing else.`;
|
|
42569
44466
|
var AFFIRMATION_RE = /^(y|n|yes|no|yep|nope|ok|okay|sure|go|go ahead|continue|proceed|stop|cancel|done|next|skip|retry|again|please do|do it)\b[.! ]*$/i;
|
|
42570
44467
|
function shouldEnhance(text) {
|
|
42571
44468
|
const t2 = text.trim();
|
|
@@ -42578,6 +44475,24 @@ function shouldEnhance(text) {
|
|
|
42578
44475
|
if (words.length < 3) return false;
|
|
42579
44476
|
return true;
|
|
42580
44477
|
}
|
|
44478
|
+
var EFFORT_PREFERENCE = [
|
|
44479
|
+
"low",
|
|
44480
|
+
"minimal",
|
|
44481
|
+
"medium",
|
|
44482
|
+
"high",
|
|
44483
|
+
"xhigh",
|
|
44484
|
+
"max",
|
|
44485
|
+
"none"
|
|
44486
|
+
];
|
|
44487
|
+
function gatedEnhancerReasoning(rc) {
|
|
44488
|
+
if (!rc) return void 0;
|
|
44489
|
+
if (rc.effortSupported && rc.effortLevels.length > 0) {
|
|
44490
|
+
const lowest = EFFORT_PREFERENCE.find((e) => rc.effortLevels.includes(e)) ?? rc.effortLevels[0];
|
|
44491
|
+
if (lowest) return { effort: lowest };
|
|
44492
|
+
}
|
|
44493
|
+
if (rc.disableSupported) return { enabled: false };
|
|
44494
|
+
return void 0;
|
|
44495
|
+
}
|
|
42581
44496
|
function normalizedEqual(a, b) {
|
|
42582
44497
|
const norm = (s) => s.trim().replace(/\s+/g, " ").toLowerCase();
|
|
42583
44498
|
return norm(a) === norm(b);
|
|
@@ -42601,11 +44516,17 @@ async function enhanceUserPrompt(opts) {
|
|
|
42601
44516
|
model,
|
|
42602
44517
|
system: [{ type: "text", text: ENHANCER_SYSTEM_PROMPT }],
|
|
42603
44518
|
messages: [{ role: "user", content: buildRefinerInput(text, opts.history) }],
|
|
42604
|
-
maxTokens
|
|
44519
|
+
maxTokens,
|
|
42605
44520
|
// NOTE: deliberately NO `temperature`. The main agent loop never sets it,
|
|
42606
44521
|
// and reasoning models (DeepSeek reasoner, o1/o3, …) return HTTP 400 when
|
|
42607
44522
|
// `temperature` is present — which would make every refine call fail and
|
|
42608
44523
|
// silently fall back to the original (no panel shown).
|
|
44524
|
+
//
|
|
44525
|
+
// A reasoning hint is forwarded ONLY when the caller supplies one (it must
|
|
44526
|
+
// already be gated to the model's advertised support — see
|
|
44527
|
+
// `gatedEnhancerReasoning`). Absent it, no reasoning field is sent, which
|
|
44528
|
+
// is identical to the original behavior.
|
|
44529
|
+
...opts.reasoning ? { reasoning: opts.reasoning } : {}
|
|
42609
44530
|
};
|
|
42610
44531
|
const timer = new AbortController();
|
|
42611
44532
|
const to = setTimeout(() => timer.abort(new Error("enhancer timeout")), timeoutMs);
|
|
@@ -42619,7 +44540,6 @@ async function enhanceUserPrompt(opts) {
|
|
|
42619
44540
|
}
|
|
42620
44541
|
const sepIdx = raw.indexOf("\n---\n");
|
|
42621
44542
|
if (sepIdx === -1) {
|
|
42622
|
-
opts.onError?.("model did not produce two versions");
|
|
42623
44543
|
return { refined: raw, english: raw };
|
|
42624
44544
|
}
|
|
42625
44545
|
const refined = raw.slice(0, sepIdx).trim();
|
|
@@ -43090,7 +45010,7 @@ var PhaseOrchestrator = class {
|
|
|
43090
45010
|
async mergeOne(phase, handle) {
|
|
43091
45011
|
if (!this.worktrees) return;
|
|
43092
45012
|
try {
|
|
43093
|
-
const
|
|
45013
|
+
const resolve19 = this.ctx.resolveConflict ? async (info) => {
|
|
43094
45014
|
const shouldResolve = await this.shouldAttemptConflictResolution(phase, info);
|
|
43095
45015
|
if (!shouldResolve) return false;
|
|
43096
45016
|
this.emit("phase.conflictResolving", {
|
|
@@ -43104,7 +45024,7 @@ var PhaseOrchestrator = class {
|
|
|
43104
45024
|
const mergeOpts = {
|
|
43105
45025
|
squash: true
|
|
43106
45026
|
};
|
|
43107
|
-
if (
|
|
45027
|
+
if (resolve19 !== void 0) mergeOpts.resolve = resolve19;
|
|
43108
45028
|
const result = await this.worktrees.merge(handle, mergeOpts);
|
|
43109
45029
|
if (result.resolved) {
|
|
43110
45030
|
this.emit("phase.conflictResolved", { phaseId: phase.id, name: phase.name });
|
|
@@ -43455,7 +45375,7 @@ var PhaseOrchestrator = class {
|
|
|
43455
45375
|
}
|
|
43456
45376
|
}
|
|
43457
45377
|
delay(ms) {
|
|
43458
|
-
return new Promise((
|
|
45378
|
+
return new Promise((resolve19) => setTimeout(resolve19, ms));
|
|
43459
45379
|
}
|
|
43460
45380
|
};
|
|
43461
45381
|
|
|
@@ -44516,8 +46436,8 @@ var CollaborationBus = class {
|
|
|
44516
46436
|
if (this.isPaused()) return false;
|
|
44517
46437
|
this.pausedAtMs = Date.now();
|
|
44518
46438
|
this.pausedBy = byParticipant;
|
|
44519
|
-
this.pausePromise = new Promise((
|
|
44520
|
-
this.pauseResolve =
|
|
46439
|
+
this.pausePromise = new Promise((resolve19) => {
|
|
46440
|
+
this.pauseResolve = resolve19;
|
|
44521
46441
|
});
|
|
44522
46442
|
return true;
|
|
44523
46443
|
}
|
|
@@ -44553,8 +46473,8 @@ var CollaborationBus = class {
|
|
|
44553
46473
|
return true;
|
|
44554
46474
|
}
|
|
44555
46475
|
let timer;
|
|
44556
|
-
const timeoutPromise = new Promise((
|
|
44557
|
-
timer = setTimeout(() =>
|
|
46476
|
+
const timeoutPromise = new Promise((resolve19) => {
|
|
46477
|
+
timer = setTimeout(() => resolve19("timeout"), timeoutMs);
|
|
44558
46478
|
});
|
|
44559
46479
|
const resumedPromise = this.pausePromise.then(() => "resumed").catch(() => "resumed");
|
|
44560
46480
|
const winner = await Promise.race([resumedPromise, timeoutPromise]);
|
|
@@ -44573,6 +46493,24 @@ var CollaborationBus = class {
|
|
|
44573
46493
|
// "skip the bash call, just give it the answer I typed". The
|
|
44574
46494
|
// injection is matched by tool_use_id, consumed once, and discarded.
|
|
44575
46495
|
injectionQueue = /* @__PURE__ */ new Map();
|
|
46496
|
+
onConsumed;
|
|
46497
|
+
/**
|
|
46498
|
+
* Register a listener fired when `collabInjectMiddleware` actually splices a
|
|
46499
|
+
* queued injection into a tool call (Phase 4 feedback loop). The webui's
|
|
46500
|
+
* CollaborationWebSocketHandler uses it to broadcast a
|
|
46501
|
+
* `collab.injection.granted` with phase `'consumed'` so observers learn the
|
|
46502
|
+
* injection was applied (and to which real tool). Last registration wins.
|
|
46503
|
+
*/
|
|
46504
|
+
onInjectionConsumed(fn) {
|
|
46505
|
+
this.onConsumed = fn;
|
|
46506
|
+
}
|
|
46507
|
+
/**
|
|
46508
|
+
* Invoked by `collabInjectMiddleware` immediately after it replaces a tool
|
|
46509
|
+
* call's result with a queued injection. No-op when no listener is set.
|
|
46510
|
+
*/
|
|
46511
|
+
notifyInjectionConsumed(info) {
|
|
46512
|
+
this.onConsumed?.(info);
|
|
46513
|
+
}
|
|
44576
46514
|
/**
|
|
44577
46515
|
* Queue a manual tool result. The next time the agent's toolCall
|
|
44578
46516
|
* pipeline sees a matching `toolUse.id`, the
|
|
@@ -44835,7 +46773,7 @@ async function gitOtherWorktrees(cwd, signal) {
|
|
|
44835
46773
|
return branches.slice(1);
|
|
44836
46774
|
}
|
|
44837
46775
|
function runGit(args, cwd, signal) {
|
|
44838
|
-
return new Promise((
|
|
46776
|
+
return new Promise((resolve19, reject) => {
|
|
44839
46777
|
let stdout = "";
|
|
44840
46778
|
let stderr = "";
|
|
44841
46779
|
const child = spawn("git", args, {
|
|
@@ -44854,7 +46792,7 @@ function runGit(args, cwd, signal) {
|
|
|
44854
46792
|
});
|
|
44855
46793
|
child.on("error", (err) => reject(err));
|
|
44856
46794
|
child.on("close", (code) => {
|
|
44857
|
-
if (code === 0)
|
|
46795
|
+
if (code === 0) resolve19(stdout);
|
|
44858
46796
|
else reject(new Error(stderr || `git ${args[0]} exited ${code}`));
|
|
44859
46797
|
});
|
|
44860
46798
|
});
|
|
@@ -44967,8 +46905,8 @@ function extractManifestPath(msg) {
|
|
|
44967
46905
|
}
|
|
44968
46906
|
return void 0;
|
|
44969
46907
|
}
|
|
44970
|
-
function isManifestFile(
|
|
44971
|
-
const name = pathBasename(
|
|
46908
|
+
function isManifestFile(path51) {
|
|
46909
|
+
const name = pathBasename(path51).toLowerCase();
|
|
44972
46910
|
const manifests = [
|
|
44973
46911
|
"package.json",
|
|
44974
46912
|
"package-lock.json",
|
|
@@ -45068,6 +47006,13 @@ function collabInjectMiddleware(bus, opts = {}) {
|
|
|
45068
47006
|
content: typeof injected.content === "string" ? injected.content : JSON.stringify(injected.content),
|
|
45069
47007
|
is_error: injected.isError
|
|
45070
47008
|
};
|
|
47009
|
+
bus.notifyInjectionConsumed({
|
|
47010
|
+
toolUseId: payload.toolUse.id,
|
|
47011
|
+
toolName: payload.toolUse.name,
|
|
47012
|
+
authorId: injected.authorId,
|
|
47013
|
+
reason: injected.reason,
|
|
47014
|
+
isError: injected.isError
|
|
47015
|
+
});
|
|
45071
47016
|
};
|
|
45072
47017
|
}
|
|
45073
47018
|
|
|
@@ -45472,7 +47417,7 @@ function createGitPlugin() {
|
|
|
45472
47417
|
}
|
|
45473
47418
|
async function runGit2(args, cwd) {
|
|
45474
47419
|
try {
|
|
45475
|
-
return await new Promise((
|
|
47420
|
+
return await new Promise((resolve19, reject) => {
|
|
45476
47421
|
const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"], signal: AbortSignal.timeout(3e4), windowsHide: true });
|
|
45477
47422
|
let stdout = "";
|
|
45478
47423
|
let stderr = "";
|
|
@@ -45493,7 +47438,7 @@ async function runGit2(args, cwd) {
|
|
|
45493
47438
|
})
|
|
45494
47439
|
);
|
|
45495
47440
|
});
|
|
45496
|
-
child.on("close", (code) =>
|
|
47441
|
+
child.on("close", (code) => resolve19({ stdout, stderr, code: code ?? 0 }));
|
|
45497
47442
|
});
|
|
45498
47443
|
} catch (err) {
|
|
45499
47444
|
if (err instanceof WrongStackError) throw err;
|
|
@@ -46295,7 +48240,7 @@ If NOTHING worth flagging:
|
|
|
46295
48240
|
## \u{1F982} Chimera Review \u2014 all clear \u2705
|
|
46296
48241
|
No issues found in N changed files.`;
|
|
46297
48242
|
async function runGit3(args, cwd) {
|
|
46298
|
-
return new Promise((
|
|
48243
|
+
return new Promise((resolve19, reject) => {
|
|
46299
48244
|
let child;
|
|
46300
48245
|
try {
|
|
46301
48246
|
child = spawn("git", args, {
|
|
@@ -46316,8 +48261,8 @@ async function runGit3(args, cwd) {
|
|
|
46316
48261
|
child.stderr?.on("data", (d) => {
|
|
46317
48262
|
stderr += d;
|
|
46318
48263
|
});
|
|
46319
|
-
child.on("error", () =>
|
|
46320
|
-
child.on("close", (code) =>
|
|
48264
|
+
child.on("error", () => resolve19({ stdout, stderr, code: 1 }));
|
|
48265
|
+
child.on("close", (code) => resolve19({ stdout, stderr, code: code ?? 0 }));
|
|
46321
48266
|
});
|
|
46322
48267
|
}
|
|
46323
48268
|
async function isGitRepo2(cwd) {
|
|
@@ -46471,6 +48416,6 @@ function createChimeraPlugin() {
|
|
|
46471
48416
|
};
|
|
46472
48417
|
}
|
|
46473
48418
|
|
|
46474
|
-
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AgentStatusTracker, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousBrain, AutonomousCoordinator, AutonomousRunner, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, CHIMERA_REVIEW_PROMPT, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, ChangeManager, CheckpointManager, CloudSync, CollabSession, CollaborationBus, ConfigError, ConfigMigrationError, ConsensusProtocol, Container, Context, ConversationState, DANGEROUS_FOR_SUBAGENTS, DECISION_TIMEOUT_MS, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CIRCUIT_BREAKER_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_HQ_REDACTION_POLICY, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_QUALITY_CHECKS, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DEFAULT_TUI_THINKING_WORD, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMailbox, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorAlertLevel, DirectorStateCheckpoint, DoneConditionChecker, ENHANCER_SYSTEM_PROMPT, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FORBIDDEN_PROTO_KEYS, FileMemoryBackend, FleetBus, FleetCostCapError, FleetManager, FleetNotifier, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, GlobalMailbox, GraphMemoryBackend, HEAVY_BUDGET, HQ_AUTH_FILE_VERSION, HQ_PROTOCOL_VERSION, HookRegistry, HookRunner, HqPublisher, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, KNOWLEDGE_AGENTS, KnowledgeGraph, LAYER_1_IDENTITY, LIGHT_BUDGET, LLMSelector, LargeAnswerStore, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, MAX_TUI_THINKING_WORD_LENGTH, MEDIUM_BUDGET, MEMORY_TYPE_LABELS, META_AGENTS, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, ObservableBrainArbiter, PLANNING_AGENTS, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddError, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionMemoryConsolidator, SessionRecovery, SessionRegistry, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, StreamHangError, SubagentBudget, TIMEOUT_PREEMPT_FRACTION, TOKENS, TaskAuctioneer, TaskDAG, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolCapabilities, ToolError, ToolErrorCategory, ToolExecutor, ToolRegistry, VERIFY_AGENTS, WIDE_SUBAGENT_CAPABILITIES, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyModelRuntime, applyRosterBudget, asBlocks, asText, assertNever, assertNotPrivateHost, assertSafePath, assessCommitSafety, atomicWrite, attachAutoExtend, attachDepWatcherBridge, attachMailboxChecker, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildContextEvidenceDigest, buildGoalPreamble, buildLosslessDigest, buildMailboxBlock, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildQueuedMessagesBlock, buildRecoveryStrategies, buildSmartDigest, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compactLog, compactSchemaDescriptions, compactToolDefinitionForWire, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeMessageTokens, computeTaskItemProgress, computeTaskProgress, consumeBtwNotes, consumeQueuedMessagesUpdate, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createAutonomyBrain, createChimeraPlugin, createContextEvidenceState, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createGlobalMailbox, createHqEventEnvelope, createHqPublisherFromEnv, createMailboxChecker, createMailboxEventPayload, createMailboxHooks, createMailboxSnapshotPayload, createMailboxSnapshotPayloadFromMailbox, createMcpControlTool, createMcpUseTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createStrategyCompactor, createSyncPlugin, createTieredBrainArbiter, createToolOutputSerializer, decryptConfigSecrets, deepMerge, defaultGitignoreUpdater, defaultHqDataDir, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, definePlugin, deriveTodosFromPlanItem, describeCatalogModel, detectEcosystem, detectNewlineStyle, detectEcosystem as detectPackageEcosystem, dispatchAgent, downloadGitHubTarball, eliseOldToolResults, emptyGoal, emptyHqAuthFile, emptyPlan, emptyTaskFile, encryptConfigSecrets, encryptedPrefixForVersion, enhanceUserPrompt, ensureDir, ensureHqFirstRunAuthFile, escapeGlobSubject, estimateMessageTokens, estimateMessages, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expandIPv6, expectDefined, extractRunEnv, extractText, filesystemServer, findCriticalPath, findPreserveStart, flagsToConfigPatch, formatContextWindowModeList, formatDecisionSummary, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTaskList, formatTaskProgress, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getDangerousCapabilities, getFileHistory, getFilesByAgent, getFullLog, getFullPackageLog, getJsonPath, getLastAuthor, getManifestPackages, getPackageAuthor, getPackagesByAgent, getPlanTemplate, getSessionRegistry, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hasCapability, hasDangerousCapabilityForSubagents, hasSessionRegistry, hasTextContent, hashRequest, hookMatcherMatches, hqAuthFilePath, hqRuntimeFilePath, injectPendingMailboxMessages, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isJsonObject, isPathSubjectKey, isPluginError, isPrimitiveArray, isPrivateIPv4, isPrivateIPv6, isSddError, isSecretField, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, jsonObjectFileExists, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTasks, loadTodosCheckpoint, loadUserModes, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, mapMailboxAgentToHqSummary, mapMailboxMessageToHqSummary, markAssistantReferencedEvidence, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, mintHqBrowserToken, mintHqToken, mutateHqAuthFile, mutatePlan, mutateTasks, noOpLogger, noOpVault, normalizePathSubject, normalizeRecipient, normalizeToLf, normalizeTokenSavingTier, normalizeTuiThinkingWord, normalizedEqual, onResize, parseContinueDirective, parseEncryptedVersion, parseEntries, parseHqEventPayload, parseHqFrame, parseProgressFromText, parseSkillRef, peekQueuedMessages, pendingBtwCount, phaseForRole, playwrightServer, projectHash, projectSlug, readHqAuthFile, readHqRuntimeFileSync, readJsonObjectFile, recentTextTurns, recordActualUsage, recordFileAction, recordPackageAction, recordProgress, recordToolOutputEvidence, recordUserIntentEvidence, redactHqEvent, redactHqValue, removeJsonPath, removeJsonPathInFile, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, repeatedReadPressure, resetCalibration, resolveAuditLevel, resolveCacheForRequest, resolveChimeraConfig, resolveContextWindowPolicy, resolveHqConfig, resolveHqConfigFromEnv, resolveHqDataDir, resolveMailboxIdentity, resolveModelMatrix, resolveModelRuntime, resolveProjectDir, resolveProviderModelList, resolveReasoningForRequest, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, rotateConfigKeys, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, scoreAgents, scoreMessage, scrubAndTruncateHqPreview, securitySlashCommand, sentinelServer, setBtwNote, setJsonPath, setJsonPathInFile, setOutputLineGuard, setPlanItemStatus, setProgress, setQueuedMessagesSnapshot, setRawMode, shouldEnhance, slackServer, sleep, sshManagerServer, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, startPackageOutdatedWatcher, startTechStackConsumer, stripAnsi, subjectForToolInput, summarizeHqToolArgs, summarizeUsage, templateToMarkdown, toErrorMessage, toStyle, toWrongStackError, topologicalSort, truncate, unifiedDiff, unloadPlugins, updateJsonObjectFile, updatePackageOutdatedStatus, validateAgainstSchema, watchHqAuthFile, wireMetricsToEvents, withDisabledToolFiltering, withFileLock, wrapAsState, writeErr, writeHqAuthFile, writeHqRuntimeFile, writeJsonObjectFile, writeOut, wstackGlobalRoot, zaiVisionServer };
|
|
48419
|
+
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, AdaptiveConcurrencyController, Agent, AgentError, AgentMonitorService, AgentStatusTracker, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousBrain, AutonomousCoordinator, AutonomousRunner, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, CHIMERA_REVIEW_PROMPT, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, ChangeManager, CheckpointManager, CloudSync, CollabSession, CollaborationBus, ConfigError, ConfigMigrationError, ConsensusProtocol, Container, Context, ConversationState, DANGEROUS_FOR_SUBAGENTS, DECISION_TIMEOUT_MS, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CIRCUIT_BREAKER_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_HQ_REDACTION_POLICY, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_QUALITY_CHECKS, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DEFAULT_TUI_THINKING_WORD, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMailbox, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorAlertLevel, DirectorStateCheckpoint, DoneConditionChecker, ENHANCER_SYSTEM_PROMPT, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FORBIDDEN_PROTO_KEYS, FileMemoryBackend, FleetBus, FleetCostCapError, FleetManager, FleetNotifier, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, GlobalMailbox, GraphMemoryBackend, HEAVY_BUDGET, HQ_AUTH_FILE_VERSION, HQ_PROTOCOL_VERSION, HookRegistry, HookRunner, HqPublisher, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, KNOWLEDGE_AGENTS, KnowledgeGraph, LAYER_1_IDENTITY, LIGHT_BUDGET, LLMSelector, LargeAnswerStore, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, MAX_TUI_THINKING_WORD_LENGTH, MEDIUM_BUDGET, MEMORY_TYPE_LABELS, META_AGENTS, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, ObservableBrainArbiter, PLANNING_AGENTS, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddError, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionMemoryConsolidator, SessionRecovery, SessionRegistry, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, StreamHangError, SubagentBudget, TIMEOUT_PREEMPT_FRACTION, TOKENS, TaskAuctioneer, TaskDAG, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolCapabilities, ToolError, ToolErrorCategory, ToolExecutor, ToolRegistry, VERIFY_AGENTS, WIDE_SUBAGENT_CAPABILITIES, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyModelRuntime, applyRosterBudget, asBlocks, asText, assertNever, assertNotPrivateHost, assertSafePath, assessCommitSafety, atomicWrite, attachAutoExtend, attachDepWatcherBridge, attachMailboxChecker, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildContextEvidenceDigest, buildGoalPreamble, buildLosslessDigest, buildMailboxBlock, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildQueuedMessagesBlock, buildRecoveryStrategies, buildSmartDigest, buildTranscriptFromEvents, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compactLog, compactSchemaDescriptions, compactToolDefinitionForWire, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeMessageTokens, computeTaskItemProgress, computeTaskProgress, consumeBtwNotes, consumeQueuedMessagesUpdate, context7Server, contextManagerTool, createAgentMonitorService, createAutoExecutor, createAutoPhaseFromTaskGraph, createAutonomyBrain, createChimeraPlugin, createContextEvidenceState, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createGlobalMailbox, createHqEventEnvelope, createHqPublisherFromEnv, createMailboxChecker, createMailboxEventPayload, createMailboxHooks, createMailboxSnapshotPayload, createMailboxSnapshotPayloadFromMailbox, createMcpControlTool, createMcpUseTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createStrategyCompactor, createSyncPlugin, createTieredBrainArbiter, createToolOutputSerializer, decryptConfigSecrets, deepMerge, defaultGitignoreUpdater, defaultHqDataDir, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, definePlugin, deriveTodosFromPlanItem, describeCatalogModel, detectEcosystem, detectNewlineStyle, detectEcosystem as detectPackageEcosystem, dispatchAgent, downloadGitHubTarball, eliseOldToolResults, emptyGoal, emptyHqAuthFile, emptyPlan, emptyTaskFile, encryptConfigSecrets, encryptedPrefixForVersion, enhanceUserPrompt, ensureDir, ensureHqFirstRunAuthFile, escapeGlobSubject, estimateMessageTokens, estimateMessages, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expandIPv6, expectDefined, extractRunEnv, extractText, filesystemServer, findCriticalPath, findPreserveStart, flagsToConfigPatch, formatContextWindowModeList, formatDecisionSummary, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTaskList, formatTaskProgress, formatTodosList, gatedEnhancerReasoning, generateSessionId, getAgentDefinition, getCalibrationState, getContextWindowMode, getDangerousCapabilities, getFileHistory, getFilesByAgent, getFullLog, getFullPackageLog, getJsonPath, getLastAuthor, getManifestPackages, getPackageAuthor, getPackagesByAgent, getPlanTemplate, getSessionRegistry, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hasCapability, hasDangerousCapabilityForSubagents, hasSessionRegistry, hasTextContent, hashRequest, hookMatcherMatches, hqAuthFilePath, hqRuntimeFilePath, injectPendingMailboxMessages, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isJsonObject, isPathSubjectKey, isPluginError, isPrimitiveArray, isPrivateIPv4, isPrivateIPv6, isSddError, isSecretField, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, jsonObjectFileExists, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTasks, loadTodosCheckpoint, loadUserModes, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, mapMailboxAgentToHqSummary, mapMailboxMessageToHqSummary, mapSessionEventToEntries, markAssistantReferencedEvidence, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, mergeToolResults, migratePlaintextSecrets, miniMaxVisionServer, mintHqBrowserToken, mintHqToken, mutateHqAuthFile, mutatePlan, mutateTasks, noOpLogger, noOpVault, normalizePathSubject, normalizeRecipient, normalizeToLf, normalizeTokenSavingTier, normalizeTuiThinkingWord, normalizedEqual, onResize, parseContinueDirective, parseEncryptedVersion, parseEntries, parseHqEventPayload, parseHqFrame, parseProgressFromText, parseSkillRef, peekQueuedMessages, pendingBtwCount, phaseForRole, playwrightServer, projectHash, projectSlug, readHqAuthFile, readHqRuntimeFileSync, readJsonObjectFile, recentTextTurns, recordActualUsage, recordFileAction, recordPackageAction, recordProgress, recordToolOutputEvidence, recordUserIntentEvidence, redactHqEvent, redactHqValue, removeJsonPath, removeJsonPathInFile, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, repeatedReadPressure, resetCalibration, resolveAuditLevel, resolveCacheForRequest, resolveChimeraConfig, resolveContextWindowPolicy, resolveHqConfig, resolveHqConfigFromEnv, resolveHqDataDir, resolveMailboxIdentity, resolveModelMatrix, resolveModelRuntime, resolveProjectDir, resolveProviderModelList, resolveReasoningForRequest, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, rotateConfigKeys, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, sanitizeModel, sanitizeNodeOptions, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, scoreAgents, scoreMessage, scrubAndTruncateHqPreview, securitySlashCommand, sentinelServer, setBtwNote, setJsonPath, setJsonPathInFile, setOutputLineGuard, setPlanItemStatus, setProgress, setQueuedMessagesSnapshot, setRawMode, shouldEnhance, slackServer, sleep, sshManagerServer, stableStringify, startAgentMonitorEventBridge, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, startPackageOutdatedWatcher, startSessionTelemetryBridge, startTechStackConsumer, stripAnsi, subjectForToolInput, summarizeHqToolArgs, summarizeUsage, templateToMarkdown, toErrorMessage, toStyle, toWrongStackError, topologicalSort, truncate, unifiedDiff, unloadPlugins, updateJsonObjectFile, updatePackageOutdatedStatus, validateAgainstSchema, watchHqAuthFile, wireMetricsToEvents, withDisabledToolFiltering, withFileLock, wrapAsState, writeErr, writeHqAuthFile, writeHqRuntimeFile, writeJsonObjectFile, writeOut, wstackGlobalRoot, zaiVisionServer };
|
|
46475
48420
|
//# sourceMappingURL=index.js.map
|
|
46476
48421
|
//# sourceMappingURL=index.js.map
|