@wrongstack/core 0.273.0 → 0.273.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-BZ2enORi.d.ts → agent-bridge-DpKIxHhE.d.ts} +1 -1
- package/dist/{agent-subagent-runner-ehb4xGvd.d.ts → agent-subagent-runner-Dx7fZ1bE.d.ts} +7 -7
- package/dist/{brain-BxN2k2HP.d.ts → brain-BDcQaku-.d.ts} +11 -5
- package/dist/{compactor-72ug-ZRB.d.ts → compactor-BuSdj3fq.d.ts} +1 -1
- package/dist/{config-C8IYxlO8.d.ts → config-CR2yoG8c.d.ts} +54 -4
- package/dist/{context-Dw55zZ_Q.d.ts → context-DulAr8Zo.d.ts} +24 -0
- package/dist/coordination/index.d.ts +23 -16
- package/dist/coordination/index.js +113 -13
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +26 -26
- package/dist/defaults/index.js +405 -8
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +15 -15
- package/dist/execution/index.js +75 -1
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{global-mailbox-C9dsc9Y_.d.ts → global-mailbox-CwcubDkA.d.ts} +1 -1
- package/dist/{goal-preamble-NhflDjYb.d.ts → goal-preamble-Bu0a2uCG.d.ts} +9 -9
- package/dist/{goal-store-Cx363x7Z.d.ts → goal-store-CTmFuZ8J.d.ts} +1 -1
- package/dist/hq/index.d.ts +5 -5
- package/dist/{index-B7fHDt0B.d.ts → index-CTq5wU3m.d.ts} +5 -5
- package/dist/{index-BbVprU-9.d.ts → index-CxP-HBhX.d.ts} +2 -2
- package/dist/index.d.ts +63 -42
- package/dist/index.js +598 -56
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/kernel/index.d.ts +11 -11
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-B6fSRNC1.d.ts → mcp-servers-BQaOE71z.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/{models-registry-4C6Wr91w.d.ts → models-registry-BEcny4kP.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-q1skFeNP.d.ts → multi-agent-coordinator-Bx8EFkv2.d.ts} +1 -1
- package/dist/{null-fleet-bus-C9rrgQwc.d.ts → null-fleet-bus-BC5ZXCQw.d.ts} +6 -6
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-CtXly2Sf.d.ts → parallel-eternal-engine-C345TI3n.d.ts} +9 -9
- package/dist/{path-resolver-Bim6G5Jz.d.ts → path-resolver-C-W_wzkF.d.ts} +3 -3
- package/dist/{permission-CC7XFYWG.d.ts → permission-CsBGZkxp.d.ts} +1 -1
- package/dist/{permission-policy-cYR4RJmw.d.ts → permission-policy-g3Sg0GdZ.d.ts} +2 -2
- package/dist/{pipeline-CNVKuQDQ.d.ts → pipeline-xnw_24Z8.d.ts} +2 -2
- package/dist/{plan-templates-C4wXMmiM.d.ts → plan-templates-DGaiYEcS.d.ts} +31 -5
- package/dist/{provider-model-resolve-DFd3IPpw.d.ts → provider-model-resolve-Cz6OlIOp.d.ts} +3 -3
- package/dist/{provider-runner-BpM0mdBE.d.ts → provider-runner-7J0HqF6B.d.ts} +3 -3
- package/dist/{retry-policy-BV7nzeAd.d.ts → retry-policy-kqXJOVkX.d.ts} +1 -1
- package/dist/sdd/index.d.ts +9 -9
- package/dist/{secret-vault-eMBKfheR.d.ts → secret-vault-CMQUr-eB.d.ts} +1 -1
- package/dist/security/index.d.ts +5 -5
- package/dist/{selector-C4ORTOid.d.ts → selector-B4r34PWR.d.ts} +1 -1
- package/dist/{session-event-bridge-CeNpUL9w.d.ts → session-event-bridge-BD3LoyLC.d.ts} +1 -1
- package/dist/{session-reader-BepLSnGL.d.ts → session-reader-DjrKGD9c.d.ts} +1 -1
- package/dist/storage/index.d.ts +11 -11
- package/dist/storage/index.js +377 -11
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.js +28 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +2 -2
- package/dist/{worktree-manager-BDuXTaWL.d.ts → worktree-manager-DHdrWQ_7.d.ts} +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8260,6 +8260,34 @@ var DefaultSessionReader = class {
|
|
|
8260
8260
|
ids = filtered.map((s) => s.id);
|
|
8261
8261
|
}
|
|
8262
8262
|
const hits = [];
|
|
8263
|
+
const streaming = this.store.searchEvents?.bind(this.store);
|
|
8264
|
+
if (streaming) {
|
|
8265
|
+
for (const id of ids) {
|
|
8266
|
+
const matched = await streaming(
|
|
8267
|
+
id,
|
|
8268
|
+
(ev) => {
|
|
8269
|
+
if (allowedTypes && !allowedTypes.has(ev.type)) return false;
|
|
8270
|
+
const text = eventText(ev);
|
|
8271
|
+
if (text === null) return false;
|
|
8272
|
+
return matcher(text) !== null;
|
|
8273
|
+
},
|
|
8274
|
+
{ limit: limit - hits.length }
|
|
8275
|
+
);
|
|
8276
|
+
for (const m of matched) {
|
|
8277
|
+
const text = expectDefined(eventText(m.event));
|
|
8278
|
+
const hit = expectDefined(matcher(text));
|
|
8279
|
+
hits.push({
|
|
8280
|
+
sessionId: id,
|
|
8281
|
+
eventIndex: m.eventIndex,
|
|
8282
|
+
ts: m.ts,
|
|
8283
|
+
type: m.event.type,
|
|
8284
|
+
snippet: snippetOf(text, hit.start, hit.end)
|
|
8285
|
+
});
|
|
8286
|
+
if (hits.length >= limit) return hits;
|
|
8287
|
+
}
|
|
8288
|
+
}
|
|
8289
|
+
return hits;
|
|
8290
|
+
}
|
|
8263
8291
|
for (const id of ids) {
|
|
8264
8292
|
let data;
|
|
8265
8293
|
try {
|
|
@@ -8798,6 +8826,91 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8798
8826
|
}
|
|
8799
8827
|
}
|
|
8800
8828
|
}
|
|
8829
|
+
/**
|
|
8830
|
+
* Streaming search over a session's JSONL. Walks the file once, parses
|
|
8831
|
+
* each event lazily, and yields only the events that match `predicate`.
|
|
8832
|
+
* Stops as soon as `opts.limit` matches are collected.
|
|
8833
|
+
*
|
|
8834
|
+
* Why this exists: `load()` parses the entire file into memory and
|
|
8835
|
+
* rebuilds `messages`/`toolCallEnds` for every caller. `search()` only
|
|
8836
|
+
* needs to know which events contain matching text — a per-line
|
|
8837
|
+
* predicate is enough. The full parse work (and the `_loadCache` poll)
|
|
8838
|
+
* is wasted in that case.
|
|
8839
|
+
*
|
|
8840
|
+
* Memory: O(hits) regardless of file size. Disk: one linear scan,
|
|
8841
|
+
* terminated at `limit` if the caller asked for one.
|
|
8842
|
+
*
|
|
8843
|
+
* Errors: missing file yields []. Corrupt lines are skipped (same
|
|
8844
|
+
* policy as `load()`). Aborting via `signal` rejects with `AbortError`.
|
|
8845
|
+
*/
|
|
8846
|
+
async searchEvents(id, predicate, opts) {
|
|
8847
|
+
const file = this.sessionPath(id, ".jsonl");
|
|
8848
|
+
const limit = opts?.limit;
|
|
8849
|
+
const signal = opts?.signal;
|
|
8850
|
+
const out = [];
|
|
8851
|
+
let stat16;
|
|
8852
|
+
try {
|
|
8853
|
+
stat16 = await fsp3.stat(file);
|
|
8854
|
+
} catch (err) {
|
|
8855
|
+
if (err.code === "ENOENT") return [];
|
|
8856
|
+
throw err;
|
|
8857
|
+
}
|
|
8858
|
+
if (stat16.size === 0) return [];
|
|
8859
|
+
let fh;
|
|
8860
|
+
try {
|
|
8861
|
+
fh = await fsp3.open(file, "r");
|
|
8862
|
+
const CHUNK = 64 * 1024;
|
|
8863
|
+
const buf = Buffer.alloc(CHUNK);
|
|
8864
|
+
let leftover = "";
|
|
8865
|
+
let eventIndex = 0;
|
|
8866
|
+
for (let position = 0; ; position += buf.byteLength) {
|
|
8867
|
+
if (signal?.aborted) {
|
|
8868
|
+
const reason = signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
8869
|
+
throw reason;
|
|
8870
|
+
}
|
|
8871
|
+
const { bytesRead } = await fh.read(buf, 0, CHUNK, position);
|
|
8872
|
+
if (bytesRead === 0) break;
|
|
8873
|
+
const text = leftover + buf.subarray(0, bytesRead).toString("utf8");
|
|
8874
|
+
const parts = text.split("\n");
|
|
8875
|
+
leftover = parts.pop() ?? "";
|
|
8876
|
+
for (const line of parts) {
|
|
8877
|
+
if (!line) continue;
|
|
8878
|
+
let ev;
|
|
8879
|
+
try {
|
|
8880
|
+
const parsed = JSON.parse(line);
|
|
8881
|
+
if (parsed === null || typeof parsed !== "object" || typeof parsed.type !== "string" || typeof parsed.ts !== "string") {
|
|
8882
|
+
continue;
|
|
8883
|
+
}
|
|
8884
|
+
ev = parsed;
|
|
8885
|
+
} catch {
|
|
8886
|
+
continue;
|
|
8887
|
+
}
|
|
8888
|
+
if (predicate(ev, eventIndex, ev.ts)) {
|
|
8889
|
+
out.push({ event: ev, eventIndex, ts: ev.ts });
|
|
8890
|
+
if (limit !== void 0 && out.length >= limit) {
|
|
8891
|
+
return out;
|
|
8892
|
+
}
|
|
8893
|
+
}
|
|
8894
|
+
eventIndex++;
|
|
8895
|
+
}
|
|
8896
|
+
}
|
|
8897
|
+
if (leftover.trim()) {
|
|
8898
|
+
try {
|
|
8899
|
+
const parsed = JSON.parse(leftover);
|
|
8900
|
+
if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
|
|
8901
|
+
const ev = parsed;
|
|
8902
|
+
if (predicate(ev, eventIndex, ev.ts)) {
|
|
8903
|
+
out.push({ event: ev, eventIndex, ts: ev.ts });
|
|
8904
|
+
}
|
|
8905
|
+
}
|
|
8906
|
+
} catch {
|
|
8907
|
+
}
|
|
8908
|
+
}
|
|
8909
|
+
return out;
|
|
8910
|
+
} finally {
|
|
8911
|
+
if (fh) await fh.close().catch(() => void 0);
|
|
8912
|
+
}
|
|
8913
|
+
}
|
|
8801
8914
|
async list(limit = 20) {
|
|
8802
8915
|
try {
|
|
8803
8916
|
await ensureDir(this.dir);
|
|
@@ -11139,7 +11252,8 @@ var BEHAVIOR_DEFAULTS = {
|
|
|
11139
11252
|
hardThreshold: 0.9,
|
|
11140
11253
|
autoCompact: true,
|
|
11141
11254
|
preserveK: DEFAULT_CONTEXT_CONFIG.preserveK,
|
|
11142
|
-
eliseThreshold: DEFAULT_CONTEXT_CONFIG.eliseThreshold
|
|
11255
|
+
eliseThreshold: DEFAULT_CONTEXT_CONFIG.eliseThreshold,
|
|
11256
|
+
strategy: "hybrid"
|
|
11143
11257
|
},
|
|
11144
11258
|
tools: {
|
|
11145
11259
|
defaultExecutionStrategy: DEFAULT_TOOLS_CONFIG.defaultExecutionStrategy,
|
|
@@ -11162,6 +11276,13 @@ var BEHAVIOR_DEFAULTS = {
|
|
|
11162
11276
|
allowOutsideProjectRoot: true
|
|
11163
11277
|
},
|
|
11164
11278
|
mcpServers: {},
|
|
11279
|
+
fallbackAuto: true,
|
|
11280
|
+
maxConcurrent: 4,
|
|
11281
|
+
yolo: false,
|
|
11282
|
+
nextPrediction: false,
|
|
11283
|
+
hints: true,
|
|
11284
|
+
debugStream: false,
|
|
11285
|
+
configScope: "global",
|
|
11165
11286
|
indexing: {
|
|
11166
11287
|
onSessionStart: true,
|
|
11167
11288
|
onEdit: true,
|
|
@@ -11169,8 +11290,55 @@ var BEHAVIOR_DEFAULTS = {
|
|
|
11169
11290
|
debounceMs: 400
|
|
11170
11291
|
},
|
|
11171
11292
|
session: { ...DEFAULT_SESSION_LOGGING_CONFIG },
|
|
11172
|
-
autonomy: {
|
|
11293
|
+
autonomy: {
|
|
11294
|
+
defaultMode: "off",
|
|
11295
|
+
autoProceedDelayMs: DEFAULT_AUTONOMY_CONFIG.autoProceedDelayMs,
|
|
11296
|
+
autoProceedMaxIterations: 50,
|
|
11297
|
+
autonomyNextPrompt: "auto {{suggestion}}",
|
|
11298
|
+
terminalTitleAnimation: true,
|
|
11299
|
+
yolo: false,
|
|
11300
|
+
streamFleet: true,
|
|
11301
|
+
chime: false,
|
|
11302
|
+
confirmExit: true,
|
|
11303
|
+
mouseMode: false,
|
|
11304
|
+
enhance: true,
|
|
11305
|
+
enhanceDelayMs: 6e4,
|
|
11306
|
+
enhanceLanguage: "original",
|
|
11307
|
+
statuslineMode: "detailed",
|
|
11308
|
+
thinkingWord: DEFAULT_TUI_THINKING_WORD
|
|
11309
|
+
},
|
|
11310
|
+
circuitBreaker: { ...DEFAULT_CIRCUIT_BREAKER_CONFIG },
|
|
11311
|
+
modelRuntime: {
|
|
11312
|
+
reasoning: { mode: "auto", effort: "high", preserve: false },
|
|
11313
|
+
cache: {}
|
|
11314
|
+
}
|
|
11173
11315
|
};
|
|
11316
|
+
function isPlainRecord(value) {
|
|
11317
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11318
|
+
}
|
|
11319
|
+
function cloneJsonValue(value) {
|
|
11320
|
+
return structuredClone(value);
|
|
11321
|
+
}
|
|
11322
|
+
function fillMissingDefaults(target, defaults) {
|
|
11323
|
+
const value = cloneJsonValue(target);
|
|
11324
|
+
const changed = fillMissingDefaultsInPlace(value, defaults);
|
|
11325
|
+
return { value, changed };
|
|
11326
|
+
}
|
|
11327
|
+
function fillMissingDefaultsInPlace(target, defaults) {
|
|
11328
|
+
let changed = false;
|
|
11329
|
+
for (const [key, defaultValue] of Object.entries(defaults)) {
|
|
11330
|
+
if (!Object.prototype.hasOwnProperty.call(target, key)) {
|
|
11331
|
+
target[key] = cloneJsonValue(defaultValue);
|
|
11332
|
+
changed = true;
|
|
11333
|
+
continue;
|
|
11334
|
+
}
|
|
11335
|
+
const current = target[key];
|
|
11336
|
+
if (isPlainRecord(current) && isPlainRecord(defaultValue)) {
|
|
11337
|
+
changed = fillMissingDefaultsInPlace(current, defaultValue) || changed;
|
|
11338
|
+
}
|
|
11339
|
+
}
|
|
11340
|
+
return changed;
|
|
11341
|
+
}
|
|
11174
11342
|
function envBool(v) {
|
|
11175
11343
|
return !/^(0|false|no|off)$/i.test(v.trim());
|
|
11176
11344
|
}
|
|
@@ -11222,27 +11390,139 @@ var defaultIndexing = {
|
|
|
11222
11390
|
watchExternal: true,
|
|
11223
11391
|
debounceMs: 400
|
|
11224
11392
|
};
|
|
11225
|
-
var
|
|
11393
|
+
var IN_PROJECT_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
11394
|
+
"version",
|
|
11395
|
+
"model",
|
|
11396
|
+
"cwd",
|
|
11397
|
+
"context",
|
|
11398
|
+
"tools",
|
|
11399
|
+
"features",
|
|
11400
|
+
"autonomy",
|
|
11401
|
+
"indexing",
|
|
11402
|
+
"session",
|
|
11403
|
+
"log",
|
|
11404
|
+
"launch",
|
|
11405
|
+
"nextPrediction",
|
|
11406
|
+
"hints",
|
|
11407
|
+
"debugStream",
|
|
11408
|
+
"configScope",
|
|
11409
|
+
"maxConcurrent",
|
|
11410
|
+
"fallbackModels",
|
|
11411
|
+
"fallbackAuto",
|
|
11412
|
+
"models",
|
|
11413
|
+
"modelMatrix",
|
|
11414
|
+
"circuitBreaker",
|
|
11415
|
+
"adaptiveConcurrency",
|
|
11416
|
+
"modelRuntime"
|
|
11417
|
+
]);
|
|
11418
|
+
var KNOWN_DENIED_IN_PROJECT = [
|
|
11419
|
+
{ key: "provider", reason: "Provider id override; can intercept prompts/responses." },
|
|
11420
|
+
{ key: "apiKey", reason: "Overrides user API key; exfiltrates prompts." },
|
|
11421
|
+
{ key: "baseUrl", reason: "Redirects provider endpoint; leaks real API key." },
|
|
11422
|
+
{ key: "providers", reason: "Per-provider apiKey/baseUrl/oauthConfig; same redirect/exfil." },
|
|
11423
|
+
{ key: "mcpServers", reason: "Arbitrary command/args/env spawned at boot (RCE)." },
|
|
11424
|
+
{ key: "hooks", reason: "Shell command arrays on lifecycle events (RCE)." },
|
|
11425
|
+
{ key: "plugins", reason: "Dynamic npm package load at boot (RCE)." },
|
|
11426
|
+
{ key: "sync", reason: "Carries githubToken credential and target repo." },
|
|
11427
|
+
{ key: "yolo", reason: "Disables all permission confirmation prompts." },
|
|
11428
|
+
{ key: "extensions", reason: "Per-plugin config can carry command/credential fields." },
|
|
11429
|
+
{ key: "hq", reason: "Carries HQ client token credential and endpoint URL." }
|
|
11430
|
+
];
|
|
11431
|
+
var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
11432
|
+
"version",
|
|
11226
11433
|
"provider",
|
|
11434
|
+
"model",
|
|
11227
11435
|
"apiKey",
|
|
11228
11436
|
"baseUrl",
|
|
11437
|
+
"maxConcurrent",
|
|
11229
11438
|
"providers",
|
|
11439
|
+
"models",
|
|
11440
|
+
"modelMatrix",
|
|
11441
|
+
"context",
|
|
11442
|
+
"tools",
|
|
11230
11443
|
"mcpServers",
|
|
11444
|
+
"fallbackModels",
|
|
11445
|
+
"fallbackAuto",
|
|
11231
11446
|
"hooks",
|
|
11232
11447
|
"plugins",
|
|
11233
|
-
"
|
|
11448
|
+
"log",
|
|
11449
|
+
"features",
|
|
11234
11450
|
"yolo",
|
|
11451
|
+
"nextPrediction",
|
|
11452
|
+
"cwd",
|
|
11453
|
+
"autonomy",
|
|
11454
|
+
"hints",
|
|
11455
|
+
"debugStream",
|
|
11456
|
+
"configScope",
|
|
11457
|
+
"indexing",
|
|
11458
|
+
"circuitBreaker",
|
|
11459
|
+
"adaptiveConcurrency",
|
|
11460
|
+
"launch",
|
|
11461
|
+
"session",
|
|
11462
|
+
"modelRuntime",
|
|
11463
|
+
"hq",
|
|
11464
|
+
"sync",
|
|
11235
11465
|
"extensions"
|
|
11236
11466
|
]);
|
|
11467
|
+
function assertInProjectAllowListComplete() {
|
|
11468
|
+
const missingFromBoth = [];
|
|
11469
|
+
for (const key of KNOWN_CONFIG_TOP_LEVEL_KEYS) {
|
|
11470
|
+
if (IN_PROJECT_ALLOWED_KEYS.has(key)) continue;
|
|
11471
|
+
const denied = KNOWN_DENIED_IN_PROJECT.find((d) => d.key === key);
|
|
11472
|
+
if (!denied) missingFromBoth.push(key);
|
|
11473
|
+
}
|
|
11474
|
+
const staleDenials = KNOWN_DENIED_IN_PROJECT.filter((d) => !KNOWN_CONFIG_TOP_LEVEL_KEYS.has(d.key)).map((d) => d.key);
|
|
11475
|
+
const duplicate = KNOWN_DENIED_IN_PROJECT.filter((d) => IN_PROJECT_ALLOWED_KEYS.has(d.key)).map((d) => d.key);
|
|
11476
|
+
const problems = [];
|
|
11477
|
+
if (missingFromBoth.length > 0) {
|
|
11478
|
+
problems.push(
|
|
11479
|
+
`new Config field(s) not classified as allowed or denied for in-project config: ` + missingFromBoth.join(", ") + ". Add each to IN_PROJECT_ALLOWED_KEYS (if safe) or KNOWN_DENIED_IN_PROJECT (with a reason)."
|
|
11480
|
+
);
|
|
11481
|
+
}
|
|
11482
|
+
if (staleDenials.length > 0) {
|
|
11483
|
+
problems.push(
|
|
11484
|
+
`KNOWN_DENIED_IN_PROJECT references keys that no longer exist on Config: ` + staleDenials.join(", ") + ". Remove them or restore the field on Config."
|
|
11485
|
+
);
|
|
11486
|
+
}
|
|
11487
|
+
if (duplicate.length > 0) {
|
|
11488
|
+
problems.push(
|
|
11489
|
+
`field(s) appear in BOTH IN_PROJECT_ALLOWED_KEYS and KNOWN_DENIED_IN_PROJECT: ` + duplicate.join(", ") + ". The allow-list wins at runtime; remove from one of the two."
|
|
11490
|
+
);
|
|
11491
|
+
}
|
|
11492
|
+
if (problems.length > 0) {
|
|
11493
|
+
throw new Error(
|
|
11494
|
+
`stripUnsafeInProjectFields drift check failed:
|
|
11495
|
+
- ${problems.join("\n - ")}`
|
|
11496
|
+
);
|
|
11497
|
+
}
|
|
11498
|
+
}
|
|
11499
|
+
var driftChecked = false;
|
|
11237
11500
|
function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => console.warn(msg)) {
|
|
11501
|
+
if (!driftChecked) {
|
|
11502
|
+
assertInProjectAllowListComplete();
|
|
11503
|
+
driftChecked = true;
|
|
11504
|
+
}
|
|
11238
11505
|
const stripped = [];
|
|
11239
11506
|
const out = {};
|
|
11240
11507
|
for (const [k, v] of Object.entries(inProject)) {
|
|
11241
|
-
if (
|
|
11242
|
-
|
|
11508
|
+
if (IN_PROJECT_ALLOWED_KEYS.has(k)) {
|
|
11509
|
+
out[k] = v;
|
|
11243
11510
|
continue;
|
|
11244
11511
|
}
|
|
11245
|
-
|
|
11512
|
+
stripped.push(k);
|
|
11513
|
+
}
|
|
11514
|
+
const outTools = out["tools"];
|
|
11515
|
+
if (outTools && typeof outTools === "object") {
|
|
11516
|
+
const execCfg = outTools["exec"];
|
|
11517
|
+
if (execCfg && typeof execCfg === "object" && "allow" in execCfg) {
|
|
11518
|
+
const clonedExec = { ...execCfg };
|
|
11519
|
+
delete clonedExec["allow"];
|
|
11520
|
+
out["tools"] = {
|
|
11521
|
+
...outTools,
|
|
11522
|
+
exec: clonedExec
|
|
11523
|
+
};
|
|
11524
|
+
stripped.push("tools.exec.allow");
|
|
11525
|
+
}
|
|
11246
11526
|
}
|
|
11247
11527
|
if (stripped.length > 0) {
|
|
11248
11528
|
warn(
|
|
@@ -11251,7 +11531,7 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
|
|
|
11251
11531
|
event: "config.in_project_unsafe_fields_ignored",
|
|
11252
11532
|
path: sourcePath,
|
|
11253
11533
|
ignoredKeys: stripped,
|
|
11254
|
-
message: `Ignored ${stripped.length}
|
|
11534
|
+
message: `Ignored ${stripped.length} field(s) from the repo-committed config "${sourcePath}": ${stripped.join(", ")}. Only a small allow-list of benign preferences (model, context, tools limits, features, \u2026) may be set by <project>/.wrongstack/config.json. Everything else must live in your personal ~/.wrongstack/config.json.`,
|
|
11255
11535
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11256
11536
|
})
|
|
11257
11537
|
);
|
|
@@ -11295,6 +11575,7 @@ var DefaultConfigLoader = class {
|
|
|
11295
11575
|
}
|
|
11296
11576
|
async load(opts = {}) {
|
|
11297
11577
|
let cfg = { ...BEHAVIOR_DEFAULTS };
|
|
11578
|
+
await this.ensureGlobalDefaults();
|
|
11298
11579
|
const inProjectCollides = samePath(this.paths.inProjectConfig, this.paths.globalConfig) || samePath(this.paths.inProjectConfig, this.paths.projectLocalConfig);
|
|
11299
11580
|
const [global, local, inProject] = await Promise.all([
|
|
11300
11581
|
this.readJson(this.paths.globalConfig),
|
|
@@ -11359,6 +11640,80 @@ var DefaultConfigLoader = class {
|
|
|
11359
11640
|
}
|
|
11360
11641
|
return Object.freeze(cfg);
|
|
11361
11642
|
}
|
|
11643
|
+
async ensureGlobalDefaults() {
|
|
11644
|
+
const fp = this.paths.globalConfig;
|
|
11645
|
+
const t0 = Date.now();
|
|
11646
|
+
try {
|
|
11647
|
+
await withFileLock(fp, async () => {
|
|
11648
|
+
let parsed;
|
|
11649
|
+
try {
|
|
11650
|
+
const raw = await fsp3.readFile(fp, "utf8");
|
|
11651
|
+
const result = safeParse(raw);
|
|
11652
|
+
if (!result.ok || !isPlainRecord(result.value)) {
|
|
11653
|
+
return;
|
|
11654
|
+
}
|
|
11655
|
+
parsed = result.value;
|
|
11656
|
+
} catch (err) {
|
|
11657
|
+
if (err.code !== "ENOENT") {
|
|
11658
|
+
this.events?.emit("storage.error", {
|
|
11659
|
+
sessionId: "~config~",
|
|
11660
|
+
store: "config",
|
|
11661
|
+
filePath: fp,
|
|
11662
|
+
operation: "ensure_defaults",
|
|
11663
|
+
outcome: "failure",
|
|
11664
|
+
error: storageErrorString(err),
|
|
11665
|
+
recoverable: false,
|
|
11666
|
+
durationMs: Date.now() - t0,
|
|
11667
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
11668
|
+
});
|
|
11669
|
+
console.warn(JSON.stringify({
|
|
11670
|
+
level: "warn",
|
|
11671
|
+
event: "config.defaults_read_failed",
|
|
11672
|
+
path: fp,
|
|
11673
|
+
message: toErrorMessage(err),
|
|
11674
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11675
|
+
}));
|
|
11676
|
+
return;
|
|
11677
|
+
}
|
|
11678
|
+
parsed = {};
|
|
11679
|
+
}
|
|
11680
|
+
const { value, changed } = fillMissingDefaults(
|
|
11681
|
+
parsed,
|
|
11682
|
+
BEHAVIOR_DEFAULTS
|
|
11683
|
+
);
|
|
11684
|
+
if (!changed) return;
|
|
11685
|
+
await atomicWrite(fp, JSON.stringify(value, null, 2), { mode: 384 });
|
|
11686
|
+
this.events?.emit("storage.write", {
|
|
11687
|
+
sessionId: "~config~",
|
|
11688
|
+
store: "config",
|
|
11689
|
+
filePath: fp,
|
|
11690
|
+
operation: "ensure_defaults",
|
|
11691
|
+
outcome: "success",
|
|
11692
|
+
durationMs: Date.now() - t0,
|
|
11693
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
11694
|
+
});
|
|
11695
|
+
});
|
|
11696
|
+
} catch (err) {
|
|
11697
|
+
this.events?.emit("storage.error", {
|
|
11698
|
+
sessionId: "~config~",
|
|
11699
|
+
store: "config",
|
|
11700
|
+
filePath: fp,
|
|
11701
|
+
operation: "ensure_defaults",
|
|
11702
|
+
outcome: "failure",
|
|
11703
|
+
error: storageErrorString(err),
|
|
11704
|
+
recoverable: false,
|
|
11705
|
+
durationMs: Date.now() - t0,
|
|
11706
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
11707
|
+
});
|
|
11708
|
+
console.warn(JSON.stringify({
|
|
11709
|
+
level: "warn",
|
|
11710
|
+
event: "config.defaults_write_failed",
|
|
11711
|
+
path: fp,
|
|
11712
|
+
message: toErrorMessage(err),
|
|
11713
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11714
|
+
}));
|
|
11715
|
+
}
|
|
11716
|
+
}
|
|
11362
11717
|
/**
|
|
11363
11718
|
* Persist a sync config to ~/.wrongstack/sync.json, with the token encrypted
|
|
11364
11719
|
* by the vault (if provided). The file is isolated from the main config
|
|
@@ -14395,6 +14750,7 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
14395
14750
|
level,
|
|
14396
14751
|
tokens,
|
|
14397
14752
|
load,
|
|
14753
|
+
hardThreshold: adaptiveThresholds.hard,
|
|
14398
14754
|
budget,
|
|
14399
14755
|
signals: { repeatedReadCount: repetition }
|
|
14400
14756
|
});
|
|
@@ -14442,6 +14798,7 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
14442
14798
|
}
|
|
14443
14799
|
}
|
|
14444
14800
|
async compact(ctx, aggressive, pressure) {
|
|
14801
|
+
let postCompactionOverflow = null;
|
|
14445
14802
|
try {
|
|
14446
14803
|
const report = await this.compactor.compact(ctx, { aggressive });
|
|
14447
14804
|
this.recordAttempt(pressure.level, pressure.tokens, report);
|
|
@@ -14471,6 +14828,38 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
14471
14828
|
...report.collapsedDigest ? { digest: truncateDigest(report.collapsedDigest) } : {}
|
|
14472
14829
|
});
|
|
14473
14830
|
ctx.clearFileTracking();
|
|
14831
|
+
const afterTokens = report.fullRequestTokensAfter ?? report.after;
|
|
14832
|
+
const afterLoad = this._maxContext > 0 ? afterTokens / this._maxContext : 0;
|
|
14833
|
+
const stillHard = afterLoad >= pressure.hardThreshold;
|
|
14834
|
+
const fatal = stillHard && (this.failureMode === "throw" || this.failureMode === "throw_on_hard" && pressure.level === "hard");
|
|
14835
|
+
if (stillHard) {
|
|
14836
|
+
const error = new Error(
|
|
14837
|
+
`Auto-compaction left context above the hard threshold after ${pressure.level} compaction`
|
|
14838
|
+
);
|
|
14839
|
+
this.events?.emit("compaction.failed", {
|
|
14840
|
+
err: error,
|
|
14841
|
+
aggressive,
|
|
14842
|
+
level: pressure.level,
|
|
14843
|
+
tokens: afterTokens,
|
|
14844
|
+
maxContext: this._maxContext,
|
|
14845
|
+
budget: computeContextWindowBudget(ctx, afterTokens, this._maxContext),
|
|
14846
|
+
signals: pressure.signals,
|
|
14847
|
+
load: afterLoad,
|
|
14848
|
+
fatal
|
|
14849
|
+
});
|
|
14850
|
+
if (fatal) {
|
|
14851
|
+
postCompactionOverflow = new AgentError({
|
|
14852
|
+
message: `Auto-compaction did not reduce context below hard threshold`,
|
|
14853
|
+
code: ERROR_CODES.AGENT_CONTEXT_OVERFLOW,
|
|
14854
|
+
recoverable: true,
|
|
14855
|
+
context: {
|
|
14856
|
+
level: pressure.level,
|
|
14857
|
+
tokens: afterTokens,
|
|
14858
|
+
maxContext: this._maxContext
|
|
14859
|
+
}
|
|
14860
|
+
});
|
|
14861
|
+
}
|
|
14862
|
+
}
|
|
14474
14863
|
} catch (err) {
|
|
14475
14864
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
14476
14865
|
const fatal = this.failureMode === "throw" || this.failureMode === "throw_on_hard" && pressure.level === "hard";
|
|
@@ -14499,6 +14888,7 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
14499
14888
|
});
|
|
14500
14889
|
}
|
|
14501
14890
|
}
|
|
14891
|
+
if (postCompactionOverflow) throw postCompactionOverflow;
|
|
14502
14892
|
}
|
|
14503
14893
|
};
|
|
14504
14894
|
function computeContextWindowBudget(ctx, inputTokens, maxContext) {
|
|
@@ -32905,6 +33295,10 @@ var AGENT_REAP_MS = 3e4;
|
|
|
32905
33295
|
var AGENT_SWEEP_INTERVAL_MS = 1e4;
|
|
32906
33296
|
var PARTIAL_TEXT_CAP = 1200;
|
|
32907
33297
|
var PARTIAL_FLUSH_THROTTLE_MS = 300;
|
|
33298
|
+
function clampPct(pct) {
|
|
33299
|
+
if (!Number.isFinite(pct)) return 0;
|
|
33300
|
+
return Math.max(0, Math.min(100, pct));
|
|
33301
|
+
}
|
|
32908
33302
|
var AgentStatusTracker = class {
|
|
32909
33303
|
events;
|
|
32910
33304
|
registry;
|
|
@@ -33056,7 +33450,7 @@ var AgentStatusTracker = class {
|
|
|
33056
33450
|
this.events.onPattern("ctx.pct", (_e, payload) => {
|
|
33057
33451
|
const p = payload;
|
|
33058
33452
|
if (typeof p?.load === "number" && Number.isFinite(p.load)) {
|
|
33059
|
-
this.leaderCtxPct = Math.round(p.load * 100);
|
|
33453
|
+
this.leaderCtxPct = clampPct(Math.round(p.load * 100));
|
|
33060
33454
|
this.flush();
|
|
33061
33455
|
}
|
|
33062
33456
|
})
|
|
@@ -33098,7 +33492,7 @@ var AgentStatusTracker = class {
|
|
|
33098
33492
|
const p = payload;
|
|
33099
33493
|
if (!p?.subagentId) return;
|
|
33100
33494
|
const entry = touch(p.subagentId);
|
|
33101
|
-
if (typeof p.load === "number") entry.ctxPct = Math.round(p.load * 100);
|
|
33495
|
+
if (typeof p.load === "number") entry.ctxPct = clampPct(Math.round(p.load * 100));
|
|
33102
33496
|
this.flush();
|
|
33103
33497
|
})
|
|
33104
33498
|
);
|
|
@@ -33259,7 +33653,7 @@ var AgentStatusTracker = class {
|
|
|
33259
33653
|
const providerMax = c.provider?.capabilities?.maxContext;
|
|
33260
33654
|
const maxContext = typeof metaLimit === "number" && metaLimit > 0 ? metaLimit : typeof providerMax === "number" && providerMax > 0 ? providerMax : void 0;
|
|
33261
33655
|
if (typeof c.lastRequestTokens === "number" && c.lastRequestTokens > 0 && maxContext !== void 0) {
|
|
33262
|
-
this.leaderCtxPct = Math.round(c.lastRequestTokens / maxContext * 100);
|
|
33656
|
+
this.leaderCtxPct = clampPct(Math.round(c.lastRequestTokens / maxContext * 100));
|
|
33263
33657
|
}
|
|
33264
33658
|
}
|
|
33265
33659
|
};
|
|
@@ -38647,6 +39041,9 @@ var DefaultMailbox = class {
|
|
|
38647
39041
|
await withFileLock(this.filePath, async () => {
|
|
38648
39042
|
await fsp3.appendFile(this.filePath, line, "utf8");
|
|
38649
39043
|
this._pushToCache(msg);
|
|
39044
|
+
const { mtime, size } = await this._statUnderLockOrAbsent();
|
|
39045
|
+
this._messageCacheMtime = mtime;
|
|
39046
|
+
this._messageCacheSize = size;
|
|
38650
39047
|
});
|
|
38651
39048
|
return msg;
|
|
38652
39049
|
}
|
|
@@ -38725,7 +39122,8 @@ var DefaultMailbox = class {
|
|
|
38725
39122
|
const serialized = all.map((m) => JSON.stringify(m)).join(LINE_SEPARATOR2) + LINE_SEPARATOR2;
|
|
38726
39123
|
await fsp3.writeFile(this.filePath, serialized, "utf8");
|
|
38727
39124
|
}
|
|
38728
|
-
this.
|
|
39125
|
+
const { mtime, size } = await this._statUnderLockOrAbsent();
|
|
39126
|
+
this._setMessageCache(all, mtime, size);
|
|
38729
39127
|
});
|
|
38730
39128
|
return updated;
|
|
38731
39129
|
}
|
|
@@ -38782,7 +39180,8 @@ var DefaultMailbox = class {
|
|
|
38782
39180
|
async clearAll() {
|
|
38783
39181
|
await withFileLock(this.filePath, async () => {
|
|
38784
39182
|
await fsp3.writeFile(this.filePath, "", "utf8");
|
|
38785
|
-
this.
|
|
39183
|
+
const { mtime, size } = await this._statUnderLockOrAbsent();
|
|
39184
|
+
this._setMessageCache([], mtime, size);
|
|
38786
39185
|
});
|
|
38787
39186
|
}
|
|
38788
39187
|
async purgeStale(opts) {
|
|
@@ -38815,7 +39214,8 @@ var DefaultMailbox = class {
|
|
|
38815
39214
|
const content = kept.map((m) => JSON.stringify(m)).join(LINE_SEPARATOR2) + LINE_SEPARATOR2;
|
|
38816
39215
|
await fsp3.writeFile(this.filePath, content, "utf8");
|
|
38817
39216
|
}
|
|
38818
|
-
this.
|
|
39217
|
+
const { mtime, size } = await this._statUnderLockOrAbsent();
|
|
39218
|
+
this._setMessageCache(kept, mtime, size);
|
|
38819
39219
|
});
|
|
38820
39220
|
return {
|
|
38821
39221
|
completedPurged,
|
|
@@ -38894,6 +39294,23 @@ var DefaultMailbox = class {
|
|
|
38894
39294
|
}
|
|
38895
39295
|
return messages;
|
|
38896
39296
|
}
|
|
39297
|
+
/**
|
|
39298
|
+
* Stat the mailbox file under the assumption that we are holding the
|
|
39299
|
+
* file lock, and that a write to the file has just completed. Returns
|
|
39300
|
+
* the (mtimeMs, size) pair, or (-1, -1) if the file does not exist
|
|
39301
|
+
* (e.g. ackMany/purgeStale on a session that has never sent a message).
|
|
39302
|
+
*/
|
|
39303
|
+
async _statUnderLockOrAbsent() {
|
|
39304
|
+
try {
|
|
39305
|
+
const st = await fsp3.stat(this.filePath);
|
|
39306
|
+
return { mtime: st.mtimeMs, size: st.size };
|
|
39307
|
+
} catch (err) {
|
|
39308
|
+
if (err.code === "ENOENT") {
|
|
39309
|
+
return { mtime: -1, size: -1 };
|
|
39310
|
+
}
|
|
39311
|
+
throw err;
|
|
39312
|
+
}
|
|
39313
|
+
}
|
|
38897
39314
|
async _readAllCached() {
|
|
38898
39315
|
try {
|
|
38899
39316
|
const st = await fsp3.stat(this.filePath);
|
|
@@ -38933,16 +39350,8 @@ var DefaultMailbox = class {
|
|
|
38933
39350
|
}
|
|
38934
39351
|
this._messageCache = messages;
|
|
38935
39352
|
this._buildIndexes(messages);
|
|
38936
|
-
|
|
38937
|
-
|
|
38938
|
-
this._messageCacheSize = size;
|
|
38939
|
-
return;
|
|
38940
|
-
}
|
|
38941
|
-
void fsp3.stat(this.filePath).then((st) => {
|
|
38942
|
-
this._messageCacheMtime = st.mtimeMs;
|
|
38943
|
-
this._messageCacheSize = st.size;
|
|
38944
|
-
}).catch(() => {
|
|
38945
|
-
});
|
|
39353
|
+
this._messageCacheMtime = mtime;
|
|
39354
|
+
this._messageCacheSize = size;
|
|
38946
39355
|
}
|
|
38947
39356
|
_pushToCache(msg) {
|
|
38948
39357
|
if (this._messageCache === null) return;
|
|
@@ -44009,17 +44418,71 @@ function createAgentLoopHandler(a, handlers) {
|
|
|
44009
44418
|
const msgCount = a.ctx.messages.length;
|
|
44010
44419
|
const maxContext = currentMaxContext();
|
|
44011
44420
|
if (_lastCompactionMsgCount === msgCount && _lastCompactionWasNoop && _lastCompactionMaxContext === maxContext && maxContext > 0) {
|
|
44012
|
-
return;
|
|
44421
|
+
return false;
|
|
44013
44422
|
}
|
|
44423
|
+
const beforeMessages = a.ctx.messages;
|
|
44424
|
+
const beforeMsgCount = a.ctx.messages.length;
|
|
44014
44425
|
await a.pipelines.contextWindow.run(a.ctx);
|
|
44015
|
-
_lastCompactionMsgCount =
|
|
44426
|
+
_lastCompactionMsgCount = a.ctx.messages.length;
|
|
44016
44427
|
_lastCompactionMaxContext = maxContext;
|
|
44017
|
-
const
|
|
44018
|
-
const tokens =
|
|
44428
|
+
const changed = a.ctx.messages !== beforeMessages || a.ctx.messages.length !== beforeMsgCount;
|
|
44429
|
+
const tokens = refreshContextRequestTokenStash({ force: changed });
|
|
44019
44430
|
const load = maxContext > 0 ? tokens / maxContext : 0;
|
|
44020
44431
|
_lastCompactionWasNoop = tokens > 0 && load < 0.5;
|
|
44432
|
+
if (changed) {
|
|
44433
|
+
_lastEmittedMsgCount = -1;
|
|
44434
|
+
_lastEmittedToolCount = -1;
|
|
44435
|
+
_lastEmittedMaxContext = -1;
|
|
44436
|
+
}
|
|
44437
|
+
return changed;
|
|
44021
44438
|
}
|
|
44022
44439
|
const calibrationKey = (model = a.ctx.model) => `${a.ctx.provider?.id ?? "unknown"}/${model}`;
|
|
44440
|
+
function stashRequestTokens(req) {
|
|
44441
|
+
const preFlight = estimateRequestTokens(
|
|
44442
|
+
req.messages,
|
|
44443
|
+
req.system,
|
|
44444
|
+
req.tools ?? [],
|
|
44445
|
+
calibrationKey(req.model)
|
|
44446
|
+
);
|
|
44447
|
+
a.ctx.lastRequestTokens = preFlight.total;
|
|
44448
|
+
_lastPreFlightMsgCount = req.messages.length;
|
|
44449
|
+
a.ctx.meta["lastRequestTokensAt"] = {
|
|
44450
|
+
msgCount: req.messages.length,
|
|
44451
|
+
toolCount: (req.tools ?? []).length
|
|
44452
|
+
};
|
|
44453
|
+
return preFlight;
|
|
44454
|
+
}
|
|
44455
|
+
function refreshContextRequestTokenStash(opts = {}) {
|
|
44456
|
+
const msgCount = a.ctx.messages.length;
|
|
44457
|
+
const toolCount = (a.ctx.tools ?? []).length;
|
|
44458
|
+
const stashed = a.ctx.lastRequestTokens;
|
|
44459
|
+
const stashedAt = a.ctx.meta?.["lastRequestTokensAt"];
|
|
44460
|
+
if (!opts.force && typeof stashed === "number" && stashed > 0 && typeof stashedAt === "object" && stashedAt !== null) {
|
|
44461
|
+
const meta = stashedAt;
|
|
44462
|
+
if (meta.msgCount === msgCount && (typeof meta.toolCount !== "number" || meta.toolCount === toolCount)) {
|
|
44463
|
+
return stashed;
|
|
44464
|
+
}
|
|
44465
|
+
}
|
|
44466
|
+
const refreshed = estimateRequestTokens(
|
|
44467
|
+
a.ctx.messages,
|
|
44468
|
+
a.ctx.systemPrompt,
|
|
44469
|
+
a.ctx.tools ?? [],
|
|
44470
|
+
calibrationKey()
|
|
44471
|
+
).total;
|
|
44472
|
+
a.ctx.lastRequestTokens = refreshed;
|
|
44473
|
+
_lastPreFlightMsgCount = msgCount;
|
|
44474
|
+
a.ctx.meta["lastRequestTokensAt"] = { msgCount, toolCount };
|
|
44475
|
+
return refreshed;
|
|
44476
|
+
}
|
|
44477
|
+
async function buildRequestWithPreflightCompaction(opts) {
|
|
44478
|
+
let req = await handlers.response.buildAndRunRequestPipeline(opts);
|
|
44479
|
+
let preFlight = stashRequestTokens(req);
|
|
44480
|
+
if (await compactContextIfNeeded()) {
|
|
44481
|
+
req = await handlers.response.buildAndRunRequestPipeline(opts);
|
|
44482
|
+
preFlight = stashRequestTokens(req);
|
|
44483
|
+
}
|
|
44484
|
+
return { req, preFlight };
|
|
44485
|
+
}
|
|
44023
44486
|
function emitContextPct() {
|
|
44024
44487
|
const msgCount = a.ctx.messages.length;
|
|
44025
44488
|
const toolCount = (a.ctx.tools ?? []).length;
|
|
@@ -44053,7 +44516,9 @@ function createAgentLoopHandler(a, handlers) {
|
|
|
44053
44516
|
);
|
|
44054
44517
|
total = est.total;
|
|
44055
44518
|
}
|
|
44056
|
-
|
|
44519
|
+
const rawLoad = maxContext > 0 ? total / maxContext : 0;
|
|
44520
|
+
const load = Math.max(0, Math.min(1, rawLoad));
|
|
44521
|
+
a.events.emit("ctx.pct", { load, rawLoad, tokens: total, maxContext });
|
|
44057
44522
|
}
|
|
44058
44523
|
function currentMaxContext() {
|
|
44059
44524
|
const metaLimit = a.ctx.meta?.["effectiveMaxContext"];
|
|
@@ -44219,19 +44684,7 @@ function createAgentLoopHandler(a, handlers) {
|
|
|
44219
44684
|
const reason = `interrupted: ${mailboxResult.interruptReason ?? "operator request"}`;
|
|
44220
44685
|
return { status: "aborted", iterations, abortReason: reason, finalText };
|
|
44221
44686
|
}
|
|
44222
|
-
const req = await
|
|
44223
|
-
const preFlight = estimateRequestTokens(
|
|
44224
|
-
req.messages,
|
|
44225
|
-
req.system,
|
|
44226
|
-
req.tools ?? [],
|
|
44227
|
-
calibrationKey(req.model)
|
|
44228
|
-
);
|
|
44229
|
-
a.ctx.lastRequestTokens = preFlight.total;
|
|
44230
|
-
_lastPreFlightMsgCount = req.messages.length;
|
|
44231
|
-
a.ctx.meta["lastRequestTokensAt"] = {
|
|
44232
|
-
msgCount: req.messages.length,
|
|
44233
|
-
toolCount: (req.tools ?? []).length
|
|
44234
|
-
};
|
|
44687
|
+
const { req, preFlight } = await buildRequestWithPreflightCompaction(opts);
|
|
44235
44688
|
await a.ctx.session.append({
|
|
44236
44689
|
type: "llm_request",
|
|
44237
44690
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -44343,10 +44796,10 @@ function createAgentLoopHandler(a, handlers) {
|
|
|
44343
44796
|
toolLoopCount = 0;
|
|
44344
44797
|
}
|
|
44345
44798
|
if (toolUses.length === 0) {
|
|
44799
|
+
await compactContextIfNeeded();
|
|
44346
44800
|
emitContextPct();
|
|
44347
44801
|
a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
|
|
44348
44802
|
if (autonomousContinue && responseResult.directive === "continue") {
|
|
44349
|
-
await compactContextIfNeeded();
|
|
44350
44803
|
await a.extensions.runAfterIteration(a.ctx, i);
|
|
44351
44804
|
continue;
|
|
44352
44805
|
}
|
|
@@ -44364,15 +44817,15 @@ function createAgentLoopHandler(a, handlers) {
|
|
|
44364
44817
|
throw toolErr;
|
|
44365
44818
|
}
|
|
44366
44819
|
if (autonomousContinue && consumeAutonomousContinue(a.ctx)) {
|
|
44820
|
+
await compactContextIfNeeded();
|
|
44367
44821
|
emitContextPct();
|
|
44368
44822
|
a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
|
|
44369
|
-
await compactContextIfNeeded();
|
|
44370
44823
|
await a.extensions.runAfterIteration(a.ctx, i);
|
|
44371
44824
|
continue;
|
|
44372
44825
|
}
|
|
44826
|
+
await compactContextIfNeeded();
|
|
44373
44827
|
emitContextPct();
|
|
44374
44828
|
a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
|
|
44375
|
-
await compactContextIfNeeded();
|
|
44376
44829
|
await a.extensions.runAfterIteration(a.ctx, i);
|
|
44377
44830
|
if (autonomousContinue && responseResult.directive === "continue") {
|
|
44378
44831
|
continue;
|
|
@@ -45486,6 +45939,52 @@ fresh suggestions via \`/suggest\`.`;
|
|
|
45486
45939
|
|
|
45487
45940
|
// src/core/system-prompt-builder.ts
|
|
45488
45941
|
var LAYER_1_IDENTITY = PROMPT;
|
|
45942
|
+
function effectiveShell(platform2, wrongstackShell) {
|
|
45943
|
+
if (platform2 !== "win32") return "posix";
|
|
45944
|
+
const v = wrongstackShell?.trim().toLowerCase();
|
|
45945
|
+
if (v === "powershell" || v === "powershell.exe") return "powershell";
|
|
45946
|
+
if (v === "pwsh" || v === "pwsh.exe") return "pwsh";
|
|
45947
|
+
if (v === "cmd" || v === "cmd.exe") return "cmd";
|
|
45948
|
+
return "cmd";
|
|
45949
|
+
}
|
|
45950
|
+
var SHELL_DISPLAY = {
|
|
45951
|
+
pwsh: "pwsh (PowerShell 7+) \u2014 write PowerShell syntax, not bash",
|
|
45952
|
+
powershell: "powershell (Windows PowerShell 5.1) \u2014 write PowerShell syntax, not bash",
|
|
45953
|
+
cmd: "cmd.exe (Command Prompt) \u2014 write cmd syntax, not bash"
|
|
45954
|
+
};
|
|
45955
|
+
function shellGuidanceBlock(shell, detail) {
|
|
45956
|
+
if (shell === "posix") return "";
|
|
45957
|
+
if (shell === "cmd") {
|
|
45958
|
+
if (detail === "short") {
|
|
45959
|
+
return "- Shell syntax: cmd.exe \u2014 use `%VAR%`, `2>nul`, `dir`/`type`/`del`/`where` (NOT bash `$VAR`, `/dev/null`, `ls`/`cat`/`rm`).";
|
|
45960
|
+
}
|
|
45961
|
+
return [
|
|
45962
|
+
"## Shell \u2014 cmd.exe",
|
|
45963
|
+
"The `bash` tool runs **cmd.exe** on this machine. Write cmd syntax, not bash/POSIX:",
|
|
45964
|
+
"- Env vars: `%NAME%` (NOT `$NAME`); set with `set NAME=value`.",
|
|
45965
|
+
"- Discard output: `2>nul` / `>nul` (NOT `2>/dev/null`).",
|
|
45966
|
+
"- No `ls`/`cat`/`rm`/`which`/`head` \u2014 use `dir`/`type`/`del`/`where` and `more`.",
|
|
45967
|
+
"- Chain with `&&` / `||` / `&`. Prefer the dedicated read/grep/glob tools over shell file ops."
|
|
45968
|
+
].join("\n");
|
|
45969
|
+
}
|
|
45970
|
+
if (detail === "short") {
|
|
45971
|
+
return "- Shell syntax: PowerShell \u2014 use `$env:VAR`, `2>$null`, `Get-Content`/`Select-Object` (NOT bash `$VAR`, `/dev/null`, `cat`/`head`).";
|
|
45972
|
+
}
|
|
45973
|
+
const chain = shell === "pwsh" ? "- Chain with `&&` / `||` (supported in PowerShell 7)." : "- `&&` / `||` are NOT available in Windows PowerShell 5.1 \u2014 separate commands with `;` (and check `$LASTEXITCODE`).";
|
|
45974
|
+
return [
|
|
45975
|
+
`## Shell \u2014 PowerShell${shell === "pwsh" ? " 7+ (pwsh)" : " 5.1 (powershell)"}`,
|
|
45976
|
+
"The `bash` tool runs **PowerShell** on this machine. Write PowerShell syntax, not bash/POSIX:",
|
|
45977
|
+
"- Env vars: read `$env:NAME`, set `$env:NAME = 'value'` (NOT `$NAME`, `%NAME%`, or `export`).",
|
|
45978
|
+
"- Discard output: `... 2>$null` or `$null = ...` (NOT `2>/dev/null`).",
|
|
45979
|
+
"- No bash builtins \u2014 use cmdlets: `head -n N`\u2192`Select-Object -First N`, `tail`\u2192`-Last N`, `cat`\u2192`Get-Content`, `which x`\u2192`Get-Command x`, `rm -rf p`\u2192`Remove-Item -Recurse -Force p`, `touch f`\u2192`New-Item -ItemType File f`. Prefer the grep/glob tools over `Select-String`.",
|
|
45980
|
+
"- Read a line window of a file: `Get-Content path | Select-Object -Skip N -First M` (the `sed -n` / `head|tail` equivalent).",
|
|
45981
|
+
"- Pipes work normally; `rg`/`git`/`node` and other native exes run as-is \u2014 only the *shell builtins* differ. (`rg --files src | rg pattern` is fine.)",
|
|
45982
|
+
'- Call exes whose path has spaces via the call operator: `& "C:\\Program Files\\app.exe" args`.',
|
|
45983
|
+
"- Multi-line literals: single-quoted here-string `@'\u2026'@` with the closing `'@` at column 0.",
|
|
45984
|
+
"- Non-interactive only: no `Read-Host`/`Get-Credential`/`pause`; add `-Confirm:$false` to destructive cmdlets.",
|
|
45985
|
+
chain
|
|
45986
|
+
].join("\n");
|
|
45987
|
+
}
|
|
45489
45988
|
var DefaultSystemPromptBuilder = class {
|
|
45490
45989
|
constructor(opts = {}) {
|
|
45491
45990
|
this.opts = opts;
|
|
@@ -46059,7 +46558,8 @@ ${agentList}`;
|
|
|
46059
46558
|
if (cached) return cached;
|
|
46060
46559
|
const today = this.opts.todayIso ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
46061
46560
|
const platform2 = `${os6.platform()} ${os6.release()}`;
|
|
46062
|
-
const
|
|
46561
|
+
const effShell = effectiveShell(os6.platform(), process.env["WRONGSTACK_SHELL"]);
|
|
46562
|
+
const shell = effShell === "posix" ? process.env.SHELL ?? process.env.ComSpec ?? "unknown" : SHELL_DISPLAY[effShell];
|
|
46063
46563
|
const node = process.version;
|
|
46064
46564
|
const isGit = await this.dirExists(path3.join(ctx.projectRoot, ".git"));
|
|
46065
46565
|
const [git, langs] = await Promise.all([
|
|
@@ -46107,6 +46607,10 @@ ${agentList}`;
|
|
|
46107
46607
|
lines.push(`- Mode: ${this.opts.modeId}`);
|
|
46108
46608
|
}
|
|
46109
46609
|
}
|
|
46610
|
+
if (effShell !== "posix" && tier !== "minimal") {
|
|
46611
|
+
const guide = shellGuidanceBlock(effShell, tier === "light" ? "short" : "full");
|
|
46612
|
+
if (guide) lines.push("", guide);
|
|
46613
|
+
}
|
|
46110
46614
|
if (this.skillCache) {
|
|
46111
46615
|
lines.push(
|
|
46112
46616
|
"",
|
|
@@ -47422,7 +47926,6 @@ var PhaseGraphBuilder = class _PhaseGraphBuilder {
|
|
|
47422
47926
|
const phase = phaseArray[i];
|
|
47423
47927
|
if (!phase) continue;
|
|
47424
47928
|
phase.nextPhases = i < phaseArray.length - 1 ? [phaseArray[i + 1]?.id ?? ""] : [];
|
|
47425
|
-
phase.dependsOn = i > 0 ? [phaseArray[i - 1]?.id ?? ""] : [];
|
|
47426
47929
|
}
|
|
47427
47930
|
const graph = {
|
|
47428
47931
|
id: graphId,
|
|
@@ -47526,6 +48029,7 @@ var PhaseOrchestrator = class {
|
|
|
47526
48029
|
async start() {
|
|
47527
48030
|
this.stopped = false;
|
|
47528
48031
|
this.paused = false;
|
|
48032
|
+
this.normalizeForResume();
|
|
47529
48033
|
this.graph.startedAt = Date.now();
|
|
47530
48034
|
this.graph.updatedAt = Date.now();
|
|
47531
48035
|
let readyPhases = this.getReadyPhases();
|
|
@@ -47548,6 +48052,30 @@ var PhaseOrchestrator = class {
|
|
|
47548
48052
|
this.tickInterval = setInterval(() => this.tick(), 1e3);
|
|
47549
48053
|
}
|
|
47550
48054
|
}
|
|
48055
|
+
/**
|
|
48056
|
+
* Make a (possibly resumed) graph runnable. A graph loaded from disk after an
|
|
48057
|
+
* interrupted run can carry transient state from the dead process: phases left
|
|
48058
|
+
* `running` and tasks left `in_progress`. The scheduler only starts `pending`
|
|
48059
|
+
* phases (getReadyPhases) and only runs `pending` tasks (getExecutableTasks),
|
|
48060
|
+
* so without this a resumed phase/task would stall forever. Reset that
|
|
48061
|
+
* transient state to `pending`; terminal phases (completed/failed/skipped) and
|
|
48062
|
+
* already-completed tasks are untouched, so completed work is never re-run.
|
|
48063
|
+
* For a freshly built graph this is a no-op.
|
|
48064
|
+
*/
|
|
48065
|
+
normalizeForResume() {
|
|
48066
|
+
this.graph.activePhaseIds = [];
|
|
48067
|
+
for (const phase of this.graph.phases.values()) {
|
|
48068
|
+
if (phase.status === "running") {
|
|
48069
|
+
phase.status = "pending";
|
|
48070
|
+
phase.updatedAt = Date.now();
|
|
48071
|
+
}
|
|
48072
|
+
if (phase.status === "pending") {
|
|
48073
|
+
for (const task of phase.taskGraph.nodes.values()) {
|
|
48074
|
+
if (task.status === "in_progress") task.status = "pending";
|
|
48075
|
+
}
|
|
48076
|
+
}
|
|
48077
|
+
}
|
|
48078
|
+
}
|
|
47551
48079
|
/** Wait for all pending phase merges, dependency-ordered and globally serialized. */
|
|
47552
48080
|
async drainMerges() {
|
|
47553
48081
|
await Promise.allSettled([...this.phaseMergePromise.values()]);
|
|
@@ -47754,6 +48282,24 @@ var PhaseOrchestrator = class {
|
|
|
47754
48282
|
this.ctx.onPhaseFail?.(phase, new Error(error));
|
|
47755
48283
|
await this.keepWorktreeForReview(phase);
|
|
47756
48284
|
}
|
|
48285
|
+
/**
|
|
48286
|
+
* A phase whose tasks all succeeded was marked `completed` and queued for
|
|
48287
|
+
* merge, but the merge back into the base branch failed. Its work is NOT
|
|
48288
|
+
* integrated, so correct the graph: move the phase out of `completedPhaseIds`
|
|
48289
|
+
* into `failedPhaseIds` and flip its status to `failed`. Without this the
|
|
48290
|
+
* persisted graph (and the board) would claim the phase succeeded while a
|
|
48291
|
+
* `phase.failed` event fired — an inconsistency that hides un-merged work.
|
|
48292
|
+
* Idempotent: safe to call more than once for the same phase.
|
|
48293
|
+
*/
|
|
48294
|
+
markPhaseMergeFailed(phase, error) {
|
|
48295
|
+
if (phase.status !== "failed") {
|
|
48296
|
+
this.graph.completedPhaseIds = this.graph.completedPhaseIds.filter((id) => id !== phase.id);
|
|
48297
|
+
this.updatePhaseStatus(phase, "failed");
|
|
48298
|
+
}
|
|
48299
|
+
if (!this.graph.failedPhaseIds.includes(phase.id)) this.graph.failedPhaseIds.push(phase.id);
|
|
48300
|
+
this.emit("phase.failed", { phaseId: phase.id, name: phase.name, error });
|
|
48301
|
+
this.ctx.onPhaseFail?.(phase, new Error(error));
|
|
48302
|
+
}
|
|
47757
48303
|
/** Trim long verifier output so it fits cleanly in an event/error message. */
|
|
47758
48304
|
truncate(text, max = 500) {
|
|
47759
48305
|
const t2 = text.trim();
|
|
@@ -47785,7 +48331,7 @@ var PhaseOrchestrator = class {
|
|
|
47785
48331
|
message: msg,
|
|
47786
48332
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
47787
48333
|
}));
|
|
47788
|
-
this.
|
|
48334
|
+
this.markPhaseMergeFailed(phase, msg);
|
|
47789
48335
|
});
|
|
47790
48336
|
await this.mergeQueue;
|
|
47791
48337
|
})();
|
|
@@ -47830,11 +48376,7 @@ var PhaseOrchestrator = class {
|
|
|
47830
48376
|
worktreeDir: handle.dir,
|
|
47831
48377
|
error: toErrorMessage(err)
|
|
47832
48378
|
});
|
|
47833
|
-
this.
|
|
47834
|
-
phaseId: phase.id,
|
|
47835
|
-
name: phase.name,
|
|
47836
|
-
error: `worktree merge failed: ${toErrorMessage(err)}`
|
|
47837
|
-
});
|
|
48379
|
+
this.markPhaseMergeFailed(phase, `worktree merge failed: ${toErrorMessage(err)}`);
|
|
47838
48380
|
}
|
|
47839
48381
|
}
|
|
47840
48382
|
async shouldAttemptConflictResolution(phase, info) {
|
|
@@ -47986,7 +48528,7 @@ var PhaseOrchestrator = class {
|
|
|
47986
48528
|
const dep = this.graph.phases.get(depId);
|
|
47987
48529
|
return dep?.status === "completed" || dep?.status === "skipped" || dep?.status === "failed";
|
|
47988
48530
|
});
|
|
47989
|
-
if (depsDone
|
|
48531
|
+
if (depsDone) {
|
|
47990
48532
|
ready.push(phase);
|
|
47991
48533
|
}
|
|
47992
48534
|
}
|
|
@@ -48364,8 +48906,8 @@ var AutoPhaseRunner = class {
|
|
|
48364
48906
|
}
|
|
48365
48907
|
this.maxRunTimer?.unref?.();
|
|
48366
48908
|
if (this.opts.events) {
|
|
48367
|
-
const
|
|
48368
|
-
const onUntyped =
|
|
48909
|
+
const bus = this.opts.events;
|
|
48910
|
+
const onUntyped = (event, handler) => bus.on(event, handler);
|
|
48369
48911
|
this.unsubscribeCompleted = onUntyped("graph.completed", this.graphCompletedHandler);
|
|
48370
48912
|
this.unsubscribeFailed = onUntyped("graph.failed", this.graphFailedHandler);
|
|
48371
48913
|
}
|