@wrongstack/core 0.292.1 → 0.293.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/coordination/index.js.map +2 -2
- package/dist/core/fallback-profile-manager.d.ts +0 -2
- package/dist/core/fallback-profile-manager.d.ts.map +1 -1
- package/dist/core/system-prompt-builder.d.ts.map +1 -1
- package/dist/defaults/index.js +346 -317
- package/dist/defaults/index.js.map +4 -4
- package/dist/execution/compaction-core.d.ts +3 -0
- package/dist/execution/compaction-core.d.ts.map +1 -1
- package/dist/execution/compactor.d.ts +4 -0
- package/dist/execution/compactor.d.ts.map +1 -1
- package/dist/execution/index.js +133 -49
- package/dist/execution/index.js.map +4 -4
- package/dist/execution/intelligent-compactor.d.ts +5 -1
- package/dist/execution/intelligent-compactor.d.ts.map +1 -1
- package/dist/execution/selective-compactor.d.ts +4 -0
- package/dist/execution/selective-compactor.d.ts.map +1 -1
- package/dist/goal/phase-orchestrator.d.ts +4 -0
- package/dist/goal/phase-orchestrator.d.ts.map +1 -1
- package/dist/hooks/runner.d.ts +0 -2
- package/dist/hooks/runner.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +116 -109
- package/dist/index.js.map +2 -2
- package/dist/models/alibaba-token-plan-catalog.d.ts +85 -0
- package/dist/models/alibaba-token-plan-catalog.d.ts.map +1 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +200 -15
- package/dist/models/index.js.map +4 -4
- package/dist/models/llm-selector.d.ts +4 -0
- package/dist/models/llm-selector.d.ts.map +1 -1
- package/dist/models/models-registry.d.ts +8 -0
- package/dist/models/models-registry.d.ts.map +1 -1
- package/dist/security/index.js +0 -23
- package/dist/security/index.js.map +2 -2
- package/dist/security/permission-policy.d.ts +0 -23
- package/dist/security/permission-policy.d.ts.map +1 -1
- package/dist/storage/config-loader.d.ts.map +1 -1
- package/dist/storage/index.js +6 -5
- package/dist/storage/index.js.map +2 -2
- package/dist/tools/index.js.map +2 -2
- package/dist/types/config.d.ts +9 -16
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.js +2 -3
- package/dist/types/index.js.map +2 -2
- package/dist/utils/index.js +15 -0
- package/dist/utils/index.js.map +2 -2
- package/dist/utils/merge-models-payload.d.ts +9 -0
- package/dist/utils/merge-models-payload.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/defaults/index.js
CHANGED
|
@@ -1625,15 +1625,30 @@ function deepEqual(a, b) {
|
|
|
1625
1625
|
}
|
|
1626
1626
|
|
|
1627
1627
|
// src/utils/merge-models-payload.ts
|
|
1628
|
+
var REMOVE_PROVIDERS_KEY = "_removeProviders";
|
|
1629
|
+
var REMOVE_MODELS_KEY = "_removeModels";
|
|
1628
1630
|
function mergeModelsPayload(base, overlay) {
|
|
1631
|
+
const removeProviders = Array.isArray(overlay[REMOVE_PROVIDERS_KEY]) ? overlay[REMOVE_PROVIDERS_KEY] : [];
|
|
1632
|
+
const removeModels = overlay[REMOVE_MODELS_KEY] && typeof overlay[REMOVE_MODELS_KEY] === "object" ? overlay[REMOVE_MODELS_KEY] : {};
|
|
1629
1633
|
const out = {};
|
|
1630
1634
|
for (const [id, provider] of Object.entries(base)) {
|
|
1631
1635
|
out[id] = cloneProvider(provider);
|
|
1632
1636
|
}
|
|
1633
1637
|
for (const [id, ovProvider] of Object.entries(overlay)) {
|
|
1638
|
+
if (id === REMOVE_PROVIDERS_KEY || id === REMOVE_MODELS_KEY) continue;
|
|
1634
1639
|
const existing = out[id];
|
|
1635
1640
|
out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
|
|
1636
1641
|
}
|
|
1642
|
+
for (const providerId of removeProviders) {
|
|
1643
|
+
delete out[providerId];
|
|
1644
|
+
}
|
|
1645
|
+
for (const [providerId, modelIds] of Object.entries(removeModels)) {
|
|
1646
|
+
const provider = out[providerId];
|
|
1647
|
+
if (!provider || !provider.models) continue;
|
|
1648
|
+
for (const modelId of modelIds) {
|
|
1649
|
+
delete provider.models[modelId];
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1637
1652
|
return out;
|
|
1638
1653
|
}
|
|
1639
1654
|
function mergeProvider(base, overlay) {
|
|
@@ -15077,25 +15092,30 @@ var WHITESPACE_COLLAPSE_PATTERN = /\s+/g;
|
|
|
15077
15092
|
function compactionDebugEnabled() {
|
|
15078
15093
|
return process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1";
|
|
15079
15094
|
}
|
|
15095
|
+
var _debugLogger;
|
|
15096
|
+
function setCompactionDebugLogger(logger) {
|
|
15097
|
+
_debugLogger = logger;
|
|
15098
|
+
}
|
|
15080
15099
|
function emitCompactionMetrics(event, metrics) {
|
|
15081
15100
|
if (!compactionDebugEnabled()) return;
|
|
15082
|
-
|
|
15083
|
-
|
|
15084
|
-
|
|
15085
|
-
|
|
15086
|
-
|
|
15087
|
-
|
|
15088
|
-
|
|
15089
|
-
|
|
15090
|
-
|
|
15091
|
-
|
|
15092
|
-
|
|
15093
|
-
|
|
15094
|
-
|
|
15095
|
-
|
|
15096
|
-
|
|
15097
|
-
|
|
15098
|
-
|
|
15101
|
+
const ctx = {
|
|
15102
|
+
event,
|
|
15103
|
+
messageCount: metrics.messageCount,
|
|
15104
|
+
preserveStart: metrics.preserveStart,
|
|
15105
|
+
fastPathIterations: metrics.fastPathIterations,
|
|
15106
|
+
fastPathInnerIterations: metrics.fastPathInnerIterations,
|
|
15107
|
+
fastPathInnerPerOuter: metrics.fastPathIterations > 0 ? metrics.fastPathInnerIterations / metrics.fastPathIterations : 0,
|
|
15108
|
+
fullPassIterations: metrics.fullPassIterations,
|
|
15109
|
+
fullPassInnerIterations: metrics.fullPassInnerIterations,
|
|
15110
|
+
fullPassInnerPerOuter: metrics.fullPassIterations > 0 ? metrics.fullPassInnerIterations / metrics.fullPassIterations : 0,
|
|
15111
|
+
tokensSaved: metrics.tokensSaved,
|
|
15112
|
+
changed: metrics.changed
|
|
15113
|
+
};
|
|
15114
|
+
if (_debugLogger) {
|
|
15115
|
+
_debugLogger.debug(`compaction: ${event}`, ctx);
|
|
15116
|
+
} else {
|
|
15117
|
+
console.log(JSON.stringify({ level: "debug", ...ctx }));
|
|
15118
|
+
}
|
|
15099
15119
|
}
|
|
15100
15120
|
var estimateMessages = estimateMessageTokens;
|
|
15101
15121
|
function hasTextContent(m) {
|
|
@@ -15127,18 +15147,20 @@ function findPreserveStart(messages, preserveK) {
|
|
|
15127
15147
|
preserveStart--;
|
|
15128
15148
|
}
|
|
15129
15149
|
if (compactionDebugEnabled()) {
|
|
15130
|
-
|
|
15131
|
-
|
|
15132
|
-
|
|
15133
|
-
|
|
15134
|
-
|
|
15135
|
-
|
|
15136
|
-
|
|
15137
|
-
|
|
15138
|
-
|
|
15139
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
15150
|
+
const ctx = {
|
|
15151
|
+
event: "compaction.find_preserve_start.ended",
|
|
15152
|
+
messageCount: messages.length,
|
|
15153
|
+
preserveK,
|
|
15154
|
+
preserveStart,
|
|
15155
|
+
pairRepairIterations,
|
|
15156
|
+
pairRepairInnerIterations,
|
|
15157
|
+
pairRepairInnerPerOuter: pairRepairIterations > 0 ? pairRepairInnerIterations / pairRepairIterations : 0
|
|
15158
|
+
};
|
|
15159
|
+
if (_debugLogger) {
|
|
15160
|
+
_debugLogger.debug("compaction: find_preserve_start.ended", ctx);
|
|
15161
|
+
} else {
|
|
15162
|
+
console.log(JSON.stringify({ level: "debug", ...ctx }));
|
|
15163
|
+
}
|
|
15142
15164
|
}
|
|
15143
15165
|
return preserveStart;
|
|
15144
15166
|
}
|
|
@@ -15253,16 +15275,18 @@ function eliseOldToolResults(messages, opts) {
|
|
|
15253
15275
|
if (compactionDebugEnabled()) {
|
|
15254
15276
|
const ratio = fullPassInnerIterations / fullPassIterations;
|
|
15255
15277
|
if (ratio > 10) {
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
|
|
15259
|
-
|
|
15260
|
-
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
})
|
|
15265
|
-
|
|
15278
|
+
const ctx = {
|
|
15279
|
+
event: "compaction.elision.regression",
|
|
15280
|
+
message: `fullPassInnerPerOuter=${ratio.toFixed(2)} exceeds threshold 10 \u2014 possible O(n\xB7m) regression`,
|
|
15281
|
+
messageCount: messages.length,
|
|
15282
|
+
fullPassIterations,
|
|
15283
|
+
fullPassInnerIterations
|
|
15284
|
+
};
|
|
15285
|
+
if (_debugLogger) {
|
|
15286
|
+
_debugLogger.error(`compaction: elision.regression \u2014 ratio ${ratio.toFixed(2)}`, ctx);
|
|
15287
|
+
} else {
|
|
15288
|
+
console.error(JSON.stringify({ level: "error", ...ctx }));
|
|
15289
|
+
}
|
|
15266
15290
|
}
|
|
15267
15291
|
}
|
|
15268
15292
|
}
|
|
@@ -16287,6 +16311,207 @@ function resolveContextWindowPolicy(config = {}, overrideMode) {
|
|
|
16287
16311
|
};
|
|
16288
16312
|
}
|
|
16289
16313
|
|
|
16314
|
+
// src/infrastructure/logger.ts
|
|
16315
|
+
import * as fsp10 from "node:fs/promises";
|
|
16316
|
+
import * as path14 from "node:path";
|
|
16317
|
+
var LEVEL_RANK2 = {
|
|
16318
|
+
error: 0,
|
|
16319
|
+
warn: 1,
|
|
16320
|
+
info: 2,
|
|
16321
|
+
debug: 3,
|
|
16322
|
+
trace: 4
|
|
16323
|
+
};
|
|
16324
|
+
var COLORS = {
|
|
16325
|
+
error: color.red,
|
|
16326
|
+
warn: color.yellow,
|
|
16327
|
+
info: color.cyan,
|
|
16328
|
+
debug: color.gray,
|
|
16329
|
+
trace: color.dim
|
|
16330
|
+
};
|
|
16331
|
+
var LOG_LEVELS = /* @__PURE__ */ new Set(["error", "warn", "info", "debug", "trace"]);
|
|
16332
|
+
var LOG_FORMATS = /* @__PURE__ */ new Set(["pretty", "json"]);
|
|
16333
|
+
var DefaultLogger = class _DefaultLogger {
|
|
16334
|
+
/** How many file writes between rotation size checks (statSync is not free). */
|
|
16335
|
+
static ROTATE_CHECK_EVERY = 100;
|
|
16336
|
+
level;
|
|
16337
|
+
file;
|
|
16338
|
+
bindings;
|
|
16339
|
+
format;
|
|
16340
|
+
stderr;
|
|
16341
|
+
maxFileBytes;
|
|
16342
|
+
writesSinceRotateCheck = 0;
|
|
16343
|
+
/**
|
|
16344
|
+
* Serialized async tail for file writes. Every appendFile (and any
|
|
16345
|
+
* chained rotation) is awaited through this promise so file I/O
|
|
16346
|
+
* never overlaps itself — preserving the per-line ordering the
|
|
16347
|
+
* sync version had, but without blocking the caller thread. Any
|
|
16348
|
+
* rejection is swallowed (`catch(() => {})`) because logging must
|
|
16349
|
+
* never crash the host.
|
|
16350
|
+
*
|
|
16351
|
+
* Children share the parent's tail: `child.tail === parent.tail`
|
|
16352
|
+
* for the lifetime of the chain. Read/write access goes through
|
|
16353
|
+
* `_tail` so that, when a child has been wired to a parent, both
|
|
16354
|
+
* `enqueueRotate` and `log` always observe the parent's current tail
|
|
16355
|
+
* rather than a stale snapshot taken at `child()` time.
|
|
16356
|
+
*/
|
|
16357
|
+
tail = Promise.resolve();
|
|
16358
|
+
parent = null;
|
|
16359
|
+
/**
|
|
16360
|
+
* Resolve the current tail. For the root logger this is the field;
|
|
16361
|
+
* for a child logger we always read through the parent so that a
|
|
16362
|
+
* child's appends land on the parent's most recent tail, and a
|
|
16363
|
+
* parent's `flush()` waits for everything the child chained.
|
|
16364
|
+
*/
|
|
16365
|
+
get _tail() {
|
|
16366
|
+
return this.parent ? this.parent._tail : this.tail;
|
|
16367
|
+
}
|
|
16368
|
+
set _tail(next) {
|
|
16369
|
+
if (this.parent) this.parent.tail = next;
|
|
16370
|
+
else this.tail = next;
|
|
16371
|
+
}
|
|
16372
|
+
constructor(opts = {}) {
|
|
16373
|
+
this.level = opts.level ?? parseLogLevel(process.env.WRONGSTACK_LOG_LEVEL);
|
|
16374
|
+
this.file = opts.file;
|
|
16375
|
+
this.bindings = opts.bindings ?? {};
|
|
16376
|
+
this.format = opts.format ?? parseLogFormat(process.env.WRONGSTACK_LOG_FORMAT);
|
|
16377
|
+
this.stderr = opts.stderr !== false;
|
|
16378
|
+
this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
|
|
16379
|
+
if (this.file) {
|
|
16380
|
+
const dir = path14.dirname(this.file);
|
|
16381
|
+
this._tail = this._tail.then(async () => {
|
|
16382
|
+
await fsp10.mkdir(dir, { recursive: true });
|
|
16383
|
+
}).catch(() => void 0);
|
|
16384
|
+
}
|
|
16385
|
+
}
|
|
16386
|
+
error(msg, ctx) {
|
|
16387
|
+
this.log("error", msg, ctx);
|
|
16388
|
+
}
|
|
16389
|
+
warn(msg, ctx) {
|
|
16390
|
+
this.log("warn", msg, ctx);
|
|
16391
|
+
}
|
|
16392
|
+
info(msg, ctx) {
|
|
16393
|
+
this.log("info", msg, ctx);
|
|
16394
|
+
}
|
|
16395
|
+
debug(msg, ctx) {
|
|
16396
|
+
this.log("debug", msg, ctx);
|
|
16397
|
+
}
|
|
16398
|
+
trace(msg, ctx) {
|
|
16399
|
+
this.log("trace", msg, ctx);
|
|
16400
|
+
}
|
|
16401
|
+
child(bindings) {
|
|
16402
|
+
const child = Object.create(_DefaultLogger.prototype);
|
|
16403
|
+
child.level = this.level;
|
|
16404
|
+
child.file = this.file;
|
|
16405
|
+
child.bindings = { ...this.bindings, ...bindings };
|
|
16406
|
+
child.format = this.format;
|
|
16407
|
+
child.stderr = this.stderr;
|
|
16408
|
+
child.maxFileBytes = this.maxFileBytes;
|
|
16409
|
+
child.parent = this;
|
|
16410
|
+
child.writesSinceRotateCheck = this.writesSinceRotateCheck;
|
|
16411
|
+
return child;
|
|
16412
|
+
}
|
|
16413
|
+
/**
|
|
16414
|
+
* Wait until all queued file writes (and any pending rotation) have
|
|
16415
|
+
* completed. `log()` is fire-and-forget by design — the caller never
|
|
16416
|
+
* blocks on disk — so tests, shutdown handlers, and processes that
|
|
16417
|
+
* need a deterministic "everything is on disk now" guarantee should
|
|
16418
|
+
* `await logger.flush()` before reading the file or exiting.
|
|
16419
|
+
*/
|
|
16420
|
+
flush() {
|
|
16421
|
+
return this._tail;
|
|
16422
|
+
}
|
|
16423
|
+
/**
|
|
16424
|
+
* Size-based rotation: when the file outgrows `maxFileBytes`, rename it to
|
|
16425
|
+
* `<file>.1` (dropping the previous `.1`) so the live file restarts empty.
|
|
16426
|
+
* Checked on the first write and every ROTATE_CHECK_EVERY writes after.
|
|
16427
|
+
* Best-effort: a rename can fail on Windows while another process holds
|
|
16428
|
+
* the file — the next check retries. Multiple processes appending to the
|
|
16429
|
+
* same log all run this check; whoever crosses the threshold first wins.
|
|
16430
|
+
*
|
|
16431
|
+
* Async: the rotation runs on the file-write tail (so its writes don't
|
|
16432
|
+
* interleave with the next append), and the caller never blocks on a
|
|
16433
|
+
* statSync / renameSync syscall on the hot log path.
|
|
16434
|
+
*/
|
|
16435
|
+
enqueueRotate(file) {
|
|
16436
|
+
if (this.writesSinceRotateCheck++ % _DefaultLogger.ROTATE_CHECK_EVERY !== 0) return;
|
|
16437
|
+
this._tail = this._tail.then(async () => {
|
|
16438
|
+
let st;
|
|
16439
|
+
try {
|
|
16440
|
+
st = await fsp10.stat(file);
|
|
16441
|
+
} catch {
|
|
16442
|
+
return;
|
|
16443
|
+
}
|
|
16444
|
+
if (st.size < this.maxFileBytes) return;
|
|
16445
|
+
try {
|
|
16446
|
+
await fsp10.rm(`${file}.1`, { force: true });
|
|
16447
|
+
await fsp10.rename(file, `${file}.1`);
|
|
16448
|
+
} catch {
|
|
16449
|
+
}
|
|
16450
|
+
}).catch(() => void 0);
|
|
16451
|
+
}
|
|
16452
|
+
log(level, msg, ctx) {
|
|
16453
|
+
const r = LEVEL_RANK2[level];
|
|
16454
|
+
const allowed = LEVEL_RANK2[this.level];
|
|
16455
|
+
if (r > allowed) return;
|
|
16456
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
16457
|
+
const entry = { ts, level, msg, ...this.bindings };
|
|
16458
|
+
if (ctx !== void 0) {
|
|
16459
|
+
entry.ctx = ctx instanceof Error ? { message: ctx.message, stack: ctx.stack } : ctx;
|
|
16460
|
+
}
|
|
16461
|
+
if (this.file) {
|
|
16462
|
+
this.enqueueRotate(this.file);
|
|
16463
|
+
const line = `${JSON.stringify(entry)}
|
|
16464
|
+
`;
|
|
16465
|
+
this._tail = this._tail.then(() => fsp10.appendFile(this.file, line)).catch(() => void 0);
|
|
16466
|
+
}
|
|
16467
|
+
if (!this.stderr) return;
|
|
16468
|
+
if (this.format === "json") {
|
|
16469
|
+
writeErr(`${JSON.stringify(entry)}
|
|
16470
|
+
`);
|
|
16471
|
+
} else {
|
|
16472
|
+
const head = `${color.dim(ts)} ${COLORS[level](level.toUpperCase().padEnd(5))} ${msg}`;
|
|
16473
|
+
if (ctx !== void 0) {
|
|
16474
|
+
writeErr(`${head} ${formatCtx(ctx)}
|
|
16475
|
+
`);
|
|
16476
|
+
} else {
|
|
16477
|
+
writeErr(`${head}
|
|
16478
|
+
`);
|
|
16479
|
+
}
|
|
16480
|
+
}
|
|
16481
|
+
}
|
|
16482
|
+
};
|
|
16483
|
+
function parseLogLevel(raw) {
|
|
16484
|
+
return raw && LOG_LEVELS.has(raw) ? raw : "info";
|
|
16485
|
+
}
|
|
16486
|
+
function parseLogFormat(raw) {
|
|
16487
|
+
return raw && LOG_FORMATS.has(raw) ? raw : "pretty";
|
|
16488
|
+
}
|
|
16489
|
+
function formatCtx(ctx) {
|
|
16490
|
+
if (ctx instanceof Error) return color.dim(ctx.message);
|
|
16491
|
+
if (typeof ctx === "string") return color.dim(ctx);
|
|
16492
|
+
try {
|
|
16493
|
+
return color.dim(JSON.stringify(ctx));
|
|
16494
|
+
} catch {
|
|
16495
|
+
return color.dim(String(ctx));
|
|
16496
|
+
}
|
|
16497
|
+
}
|
|
16498
|
+
var noOpLogger = {
|
|
16499
|
+
// 'error' is the quietest level the Logger contract offers; the methods
|
|
16500
|
+
// discard everything regardless, this only matters to level checks.
|
|
16501
|
+
level: "error",
|
|
16502
|
+
error: () => {
|
|
16503
|
+
},
|
|
16504
|
+
warn: () => {
|
|
16505
|
+
},
|
|
16506
|
+
info: () => {
|
|
16507
|
+
},
|
|
16508
|
+
debug: () => {
|
|
16509
|
+
},
|
|
16510
|
+
trace: () => {
|
|
16511
|
+
},
|
|
16512
|
+
child: () => noOpLogger
|
|
16513
|
+
};
|
|
16514
|
+
|
|
16290
16515
|
// src/types/default-config.ts
|
|
16291
16516
|
var DEFAULT_TOOLS_CONFIG = Object.freeze({
|
|
16292
16517
|
defaultExecutionStrategy: "smart",
|
|
@@ -16332,10 +16557,13 @@ var HybridCompactor = class {
|
|
|
16332
16557
|
preserveK;
|
|
16333
16558
|
eliseThreshold;
|
|
16334
16559
|
smart;
|
|
16560
|
+
logger;
|
|
16335
16561
|
constructor(opts = {}) {
|
|
16336
16562
|
this.preserveK = opts.preserveK ?? 5;
|
|
16337
16563
|
this.eliseThreshold = opts.eliseThreshold ?? 2e3;
|
|
16338
16564
|
this.smart = opts.smart ?? false;
|
|
16565
|
+
this.logger = opts.logger ?? noOpLogger;
|
|
16566
|
+
setCompactionDebugLogger(this.logger);
|
|
16339
16567
|
}
|
|
16340
16568
|
async compact(ctx, opts = {}) {
|
|
16341
16569
|
const beforeTokens = estimateMessages(ctx.messages);
|
|
@@ -16673,7 +16901,7 @@ var AutonomousRunner = class {
|
|
|
16673
16901
|
};
|
|
16674
16902
|
|
|
16675
16903
|
// src/storage/goal-store.ts
|
|
16676
|
-
import * as
|
|
16904
|
+
import * as fsp11 from "node:fs/promises";
|
|
16677
16905
|
var MAX_JOURNAL_ENTRIES = 500;
|
|
16678
16906
|
function goalFilePath(projectRoot) {
|
|
16679
16907
|
return resolveWstackPaths({ projectRoot }).projectGoal;
|
|
@@ -16682,7 +16910,7 @@ async function loadGoal(filePath, events, warn) {
|
|
|
16682
16910
|
const t0 = Date.now();
|
|
16683
16911
|
let raw;
|
|
16684
16912
|
try {
|
|
16685
|
-
raw = await
|
|
16913
|
+
raw = await fsp11.readFile(filePath, "utf8");
|
|
16686
16914
|
} catch (err) {
|
|
16687
16915
|
const code = err.code;
|
|
16688
16916
|
if (code === "ENOENT") {
|
|
@@ -16871,7 +17099,7 @@ function isDesignStack(v) {
|
|
|
16871
17099
|
// src/execution/design-kit-loader.ts
|
|
16872
17100
|
import { existsSync } from "node:fs";
|
|
16873
17101
|
import * as fs4 from "node:fs/promises";
|
|
16874
|
-
import * as
|
|
17102
|
+
import * as path15 from "node:path";
|
|
16875
17103
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
16876
17104
|
var KIT_FILE = "KIT.md";
|
|
16877
17105
|
var TOKENS_FILE = "tokens.json";
|
|
@@ -16984,7 +17212,7 @@ var DefaultDesignKitLoader = class {
|
|
|
16984
17212
|
}
|
|
16985
17213
|
for (const e of entries) {
|
|
16986
17214
|
if (!e.isDirectory()) continue;
|
|
16987
|
-
const kitFile =
|
|
17215
|
+
const kitFile = path15.join(dir, e.name, KIT_FILE);
|
|
16988
17216
|
try {
|
|
16989
17217
|
const raw = await fs4.readFile(kitFile, "utf8");
|
|
16990
17218
|
const fm = parseKitFrontmatter(raw);
|
|
@@ -17058,7 +17286,7 @@ var DefaultDesignKitLoader = class {
|
|
|
17058
17286
|
const m = await this.find(id);
|
|
17059
17287
|
let tokens;
|
|
17060
17288
|
if (m) {
|
|
17061
|
-
const tokensPath =
|
|
17289
|
+
const tokensPath = path15.join(path15.dirname(m.path), TOKENS_FILE);
|
|
17062
17290
|
try {
|
|
17063
17291
|
const raw = await fs4.readFile(tokensPath, "utf8");
|
|
17064
17292
|
const parsed = JSON.parse(raw);
|
|
@@ -17085,12 +17313,12 @@ var DefaultDesignKitLoader = class {
|
|
|
17085
17313
|
};
|
|
17086
17314
|
function resolveBundledDesignKitsDir() {
|
|
17087
17315
|
try {
|
|
17088
|
-
const here =
|
|
17316
|
+
const here = path15.dirname(fileURLToPath3(import.meta.url));
|
|
17089
17317
|
const candidates = [
|
|
17090
|
-
|
|
17091
|
-
|
|
17092
|
-
|
|
17093
|
-
|
|
17318
|
+
path15.join(here, "design-kits"),
|
|
17319
|
+
path15.join(here, "..", "design-kits"),
|
|
17320
|
+
path15.join(here, "..", "..", "design-kits"),
|
|
17321
|
+
path15.join(here, "..", "..", "..", "design-kits")
|
|
17094
17322
|
];
|
|
17095
17323
|
for (const c of candidates) {
|
|
17096
17324
|
if (existsSync(c)) return c;
|
|
@@ -17119,10 +17347,10 @@ function _resetDesignKitLoaderMemo() {
|
|
|
17119
17347
|
// src/execution/design-project-store.ts
|
|
17120
17348
|
import { existsSync as existsSync2 } from "node:fs";
|
|
17121
17349
|
import * as fs5 from "node:fs/promises";
|
|
17122
|
-
import * as
|
|
17350
|
+
import * as path16 from "node:path";
|
|
17123
17351
|
var DESIGN_DIR = ".design";
|
|
17124
17352
|
function designProjectDir(projectRoot) {
|
|
17125
|
-
return
|
|
17353
|
+
return path16.join(projectRoot, DESIGN_DIR);
|
|
17126
17354
|
}
|
|
17127
17355
|
var RULE_FILES = ["rules.md", "RULES.md", "design.md"];
|
|
17128
17356
|
var rulesCache = /* @__PURE__ */ new Map();
|
|
@@ -17131,7 +17359,7 @@ async function loadProjectDesignRules(projectRoot) {
|
|
|
17131
17359
|
let rules;
|
|
17132
17360
|
for (const name of RULE_FILES) {
|
|
17133
17361
|
try {
|
|
17134
|
-
const txt = await fs5.readFile(
|
|
17362
|
+
const txt = await fs5.readFile(path16.join(designProjectDir(projectRoot), name), "utf8");
|
|
17135
17363
|
if (txt.trim()) {
|
|
17136
17364
|
rules = txt.trim();
|
|
17137
17365
|
break;
|
|
@@ -17152,7 +17380,7 @@ function parseOverrides(value) {
|
|
|
17152
17380
|
}
|
|
17153
17381
|
async function loadActiveKit(projectRoot) {
|
|
17154
17382
|
try {
|
|
17155
|
-
const raw = await fs5.readFile(
|
|
17383
|
+
const raw = await fs5.readFile(path16.join(designProjectDir(projectRoot), "active.json"), "utf8");
|
|
17156
17384
|
const parsed = JSON.parse(raw);
|
|
17157
17385
|
if (parsed && typeof parsed.kit === "string") {
|
|
17158
17386
|
return {
|
|
@@ -17186,7 +17414,7 @@ function applyTokenOverrides(tokens, overrides) {
|
|
|
17186
17414
|
async function ensureDesignDir(projectRoot) {
|
|
17187
17415
|
const dir = designProjectDir(projectRoot);
|
|
17188
17416
|
await fs5.mkdir(dir, { recursive: true });
|
|
17189
|
-
const gi =
|
|
17417
|
+
const gi = path16.join(dir, ".gitignore");
|
|
17190
17418
|
if (!existsSync2(gi)) {
|
|
17191
17419
|
try {
|
|
17192
17420
|
await fs5.writeFile(gi, "*\n");
|
|
@@ -17200,11 +17428,11 @@ async function recordKitChoice(projectRoot, kit, stack, source, isoTime, overrid
|
|
|
17200
17428
|
const dir = await ensureDesignDir(projectRoot);
|
|
17201
17429
|
const record = { kit, stack: stack ?? null };
|
|
17202
17430
|
if (overrides && Object.keys(overrides).length > 0) record.overrides = overrides;
|
|
17203
|
-
await fs5.writeFile(
|
|
17431
|
+
await fs5.writeFile(path16.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
|
|
17204
17432
|
`);
|
|
17205
17433
|
const line = `- ${isoTime} \xB7 kit=${kit}${stack ? ` stack=${stack}` : ""} \xB7 via=${source}
|
|
17206
17434
|
`;
|
|
17207
|
-
await fs5.appendFile(
|
|
17435
|
+
await fs5.appendFile(path16.join(dir, "decisions.md"), line);
|
|
17208
17436
|
} catch {
|
|
17209
17437
|
}
|
|
17210
17438
|
}
|
|
@@ -17220,11 +17448,11 @@ async function recordOverrides(projectRoot, patch, isoTime) {
|
|
|
17220
17448
|
const dir = await ensureDesignDir(projectRoot);
|
|
17221
17449
|
const record = { kit: active.kit, stack: active.stack ?? null };
|
|
17222
17450
|
if (Object.keys(merged).length > 0) record.overrides = merged;
|
|
17223
|
-
await fs5.writeFile(
|
|
17451
|
+
await fs5.writeFile(path16.join(dir, "active.json"), `${JSON.stringify(record, null, 2)}
|
|
17224
17452
|
`);
|
|
17225
17453
|
const keys = Object.keys(patch).join(",");
|
|
17226
17454
|
await fs5.appendFile(
|
|
17227
|
-
|
|
17455
|
+
path16.join(dir, "decisions.md"),
|
|
17228
17456
|
`- ${isoTime} \xB7 kit=${active.kit} \xB7 override=${keys} \xB7 via=set
|
|
17229
17457
|
`
|
|
17230
17458
|
);
|
|
@@ -17234,7 +17462,7 @@ async function recordOverrides(projectRoot, patch, isoTime) {
|
|
|
17234
17462
|
}
|
|
17235
17463
|
async function clearPersistedActiveKit(projectRoot) {
|
|
17236
17464
|
try {
|
|
17237
|
-
await fs5.rm(
|
|
17465
|
+
await fs5.rm(path16.join(designProjectDir(projectRoot), "active.json"), { force: true });
|
|
17238
17466
|
} catch {
|
|
17239
17467
|
}
|
|
17240
17468
|
}
|
|
@@ -19018,6 +19246,7 @@ var IntelligentCompactor = class {
|
|
|
19018
19246
|
summarizerPrompt;
|
|
19019
19247
|
summarizerModel;
|
|
19020
19248
|
oneShotOrchestrator;
|
|
19249
|
+
logger;
|
|
19021
19250
|
constructor(opts) {
|
|
19022
19251
|
this.provider = opts.provider;
|
|
19023
19252
|
this.warnThreshold = opts.warnThreshold ?? 0.5;
|
|
@@ -19029,6 +19258,8 @@ var IntelligentCompactor = class {
|
|
|
19029
19258
|
this.summarizerPrompt = opts.summarizerPrompt ?? readBundledInstructionText("llm/intelligent-compactor-summarizer.md");
|
|
19030
19259
|
this.summarizerModel = opts.summarizerModel;
|
|
19031
19260
|
this.oneShotOrchestrator = opts.oneShotOrchestrator;
|
|
19261
|
+
this.logger = opts.logger ?? noOpLogger;
|
|
19262
|
+
setCompactionDebugLogger(this.logger);
|
|
19032
19263
|
}
|
|
19033
19264
|
async compact(ctx, opts = {}) {
|
|
19034
19265
|
const beforeTokens = estimateMessages(ctx.messages);
|
|
@@ -20422,11 +20653,13 @@ var LLMSelector = class {
|
|
|
20422
20653
|
systemPrompt;
|
|
20423
20654
|
maxOutputTokens;
|
|
20424
20655
|
oneShotOrchestrator;
|
|
20656
|
+
logger;
|
|
20425
20657
|
constructor(opts) {
|
|
20426
20658
|
this.provider = opts.provider;
|
|
20427
20659
|
this.model = opts.model ?? "unknown";
|
|
20660
|
+
this.logger = opts.logger ?? noOpLogger;
|
|
20428
20661
|
if (this.model === "unknown" && (process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1")) {
|
|
20429
|
-
|
|
20662
|
+
this.logger.warn(
|
|
20430
20663
|
"[LLMSelector] model not set \u2014 selector will use the provider default. Set `model` explicitly in LLMSelectorOptions to silence this warning."
|
|
20431
20664
|
);
|
|
20432
20665
|
}
|
|
@@ -20476,14 +20709,9 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
|
|
|
20476
20709
|
}
|
|
20477
20710
|
} catch (err) {
|
|
20478
20711
|
if (err instanceof Error) {
|
|
20479
|
-
|
|
20480
|
-
|
|
20481
|
-
|
|
20482
|
-
event: "llm_selector.call_failed",
|
|
20483
|
-
message: `selector call failed, using recency fallback: ${err.message}`,
|
|
20484
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
20485
|
-
})
|
|
20486
|
-
);
|
|
20712
|
+
this.logger.warn(`selector call failed, using recency fallback: ${err.message}`, {
|
|
20713
|
+
event: "llm_selector.call_failed"
|
|
20714
|
+
});
|
|
20487
20715
|
}
|
|
20488
20716
|
return this.fallbackSelect(messages, effectiveBudget);
|
|
20489
20717
|
} finally {
|
|
@@ -20590,6 +20818,7 @@ var SelectiveCompactor = class {
|
|
|
20590
20818
|
eliseThreshold;
|
|
20591
20819
|
summarizerModel;
|
|
20592
20820
|
summarizerPrompt;
|
|
20821
|
+
logger;
|
|
20593
20822
|
constructor(opts) {
|
|
20594
20823
|
this.provider = opts.provider;
|
|
20595
20824
|
this.selector = opts.selector ?? new LLMSelector({ provider: opts.provider, model: opts.selectorModel, maxOutputTokens: opts.selectorMaxOutputTokens });
|
|
@@ -20600,8 +20829,10 @@ var SelectiveCompactor = class {
|
|
|
20600
20829
|
this.preserveK = opts.preserveK ?? 4;
|
|
20601
20830
|
this.eliseThreshold = opts.eliseThreshold ?? 300;
|
|
20602
20831
|
this.summarizerModel = opts.summarizerModel ?? opts.selectorModel;
|
|
20832
|
+
this.logger = opts.logger ?? noOpLogger;
|
|
20833
|
+
setCompactionDebugLogger(this.logger);
|
|
20603
20834
|
if (this.summarizerModel === void 0 && (process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1")) {
|
|
20604
|
-
|
|
20835
|
+
this.logger.warn(
|
|
20605
20836
|
"[SelectiveCompactor] summarizerModel not set \u2014 will fall back to ctx.model at summarize time. Set `summarizerModel` explicitly to silence this warning."
|
|
20606
20837
|
);
|
|
20607
20838
|
}
|
|
@@ -20830,7 +21061,7 @@ Summarize the following message range:`;
|
|
|
20830
21061
|
|
|
20831
21062
|
// src/execution/skill-loader.ts
|
|
20832
21063
|
import * as fs6 from "node:fs/promises";
|
|
20833
|
-
import * as
|
|
21064
|
+
import * as path17 from "node:path";
|
|
20834
21065
|
|
|
20835
21066
|
// src/skills/foreign-sources.ts
|
|
20836
21067
|
var FOREIGN_SKILL_TOOLS = [
|
|
@@ -20980,7 +21211,7 @@ async function entryIsDirectory(dir, entry) {
|
|
|
20980
21211
|
if (entry.isDirectory()) return true;
|
|
20981
21212
|
if (entry.isSymbolicLink()) {
|
|
20982
21213
|
try {
|
|
20983
|
-
return (await fs6.stat(
|
|
21214
|
+
return (await fs6.stat(path17.join(dir, entry.name))).isDirectory();
|
|
20984
21215
|
} catch {
|
|
20985
21216
|
return false;
|
|
20986
21217
|
}
|
|
@@ -21001,7 +21232,7 @@ var DefaultSkillLoader = class {
|
|
|
21001
21232
|
for (const tool of FOREIGN_SKILL_TOOLS) {
|
|
21002
21233
|
if (!foreignIds.includes(tool.id)) continue;
|
|
21003
21234
|
dirs.push({
|
|
21004
|
-
dir:
|
|
21235
|
+
dir: path17.join(root, "." + tool.id, tool.subdir),
|
|
21005
21236
|
source: "foreign",
|
|
21006
21237
|
originTool: tool.id
|
|
21007
21238
|
});
|
|
@@ -21028,7 +21259,7 @@ var DefaultSkillLoader = class {
|
|
|
21028
21259
|
);
|
|
21029
21260
|
for (const e of entries) {
|
|
21030
21261
|
if (!await entryIsDirectory(dir, e)) continue;
|
|
21031
|
-
const skillFile =
|
|
21262
|
+
const skillFile = path17.join(dir, e.name, "SKILL.md");
|
|
21032
21263
|
try {
|
|
21033
21264
|
const raw = await fs6.readFile(skillFile, "utf8");
|
|
21034
21265
|
const fm = parseSkillFrontmatter(raw);
|
|
@@ -21112,7 +21343,7 @@ var DefaultSkillLoader = class {
|
|
|
21112
21343
|
if (cached !== void 0) return cached;
|
|
21113
21344
|
const m = await this.find(name);
|
|
21114
21345
|
if (!m) throw new Error(`Skill "${name}" not found`);
|
|
21115
|
-
const savePath =
|
|
21346
|
+
const savePath = path17.join(path17.dirname(m.path), "SKILL.save.md");
|
|
21116
21347
|
let result;
|
|
21117
21348
|
try {
|
|
21118
21349
|
result = await fs6.readFile(savePath, "utf8");
|
|
@@ -21146,11 +21377,11 @@ function parseDescriptionFromText(desc) {
|
|
|
21146
21377
|
|
|
21147
21378
|
// src/execution/prompt-loader.ts
|
|
21148
21379
|
import * as fs8 from "node:fs/promises";
|
|
21149
|
-
import * as
|
|
21380
|
+
import * as path19 from "node:path";
|
|
21150
21381
|
|
|
21151
21382
|
// src/storage/prompt-store.ts
|
|
21152
21383
|
import * as fs7 from "node:fs/promises";
|
|
21153
|
-
import * as
|
|
21384
|
+
import * as path18 from "node:path";
|
|
21154
21385
|
var SCHEMA_VERSION = 2;
|
|
21155
21386
|
function migratePromptEntry(raw) {
|
|
21156
21387
|
if (!raw || typeof raw !== "object") return null;
|
|
@@ -21216,7 +21447,7 @@ var DefaultPromptStore = class {
|
|
|
21216
21447
|
if (!file.endsWith(".json")) continue;
|
|
21217
21448
|
try {
|
|
21218
21449
|
const raw = JSON.parse(
|
|
21219
|
-
await fs7.readFile(
|
|
21450
|
+
await fs7.readFile(path18.join(this.dir, file), "utf8")
|
|
21220
21451
|
);
|
|
21221
21452
|
const migrated = migratePromptEntry(raw.entry);
|
|
21222
21453
|
if (migrated) entries.push(migrated);
|
|
@@ -21230,7 +21461,7 @@ var DefaultPromptStore = class {
|
|
|
21230
21461
|
);
|
|
21231
21462
|
}
|
|
21232
21463
|
async get(id) {
|
|
21233
|
-
const file =
|
|
21464
|
+
const file = path18.join(this.dir, `${id}.json`);
|
|
21234
21465
|
try {
|
|
21235
21466
|
const raw = JSON.parse(await fs7.readFile(file, "utf8"));
|
|
21236
21467
|
return migratePromptEntry(raw.entry);
|
|
@@ -21240,12 +21471,12 @@ var DefaultPromptStore = class {
|
|
|
21240
21471
|
}
|
|
21241
21472
|
async save(entry) {
|
|
21242
21473
|
await ensureDir(this.dir);
|
|
21243
|
-
const file =
|
|
21474
|
+
const file = path18.join(this.dir, `${entry.id}.json`);
|
|
21244
21475
|
const raw = { version: SCHEMA_VERSION, entry };
|
|
21245
21476
|
await atomicWrite(file, JSON.stringify(raw, null, 2));
|
|
21246
21477
|
}
|
|
21247
21478
|
async delete(id) {
|
|
21248
|
-
const file =
|
|
21479
|
+
const file = path18.join(this.dir, `${id}.json`);
|
|
21249
21480
|
try {
|
|
21250
21481
|
await fs7.unlink(file);
|
|
21251
21482
|
return true;
|
|
@@ -21334,7 +21565,7 @@ var DefaultPromptLoader = class {
|
|
|
21334
21565
|
constructor(opts) {
|
|
21335
21566
|
this.projectStore = typeof opts.paths.inProjectPrompts === "string" ? new DefaultPromptStore(opts.paths.inProjectPrompts) : void 0;
|
|
21336
21567
|
this.userStore = typeof opts.paths.globalPrompts === "string" ? new DefaultPromptStore(opts.paths.globalPrompts) : void 0;
|
|
21337
|
-
this.builtinDir = opts.bundledDir ?
|
|
21568
|
+
this.builtinDir = opts.bundledDir ? path19.join(opts.bundledDir, "prompts") : void 0;
|
|
21338
21569
|
}
|
|
21339
21570
|
async list() {
|
|
21340
21571
|
if (this.cache) return this.cache;
|
|
@@ -21466,7 +21697,7 @@ async function walkJson(dir) {
|
|
|
21466
21697
|
return out;
|
|
21467
21698
|
}
|
|
21468
21699
|
for (const e of entries) {
|
|
21469
|
-
const full =
|
|
21700
|
+
const full = path19.join(dir, e.name);
|
|
21470
21701
|
if (e.isDirectory()) {
|
|
21471
21702
|
out.push(...await walkJson(full));
|
|
21472
21703
|
} else if (e.name.endsWith(".json") && e.name !== "index.json" && e.name !== "schema.json") {
|
|
@@ -21665,11 +21896,11 @@ function runWithProcessTelemetry(context, run) {
|
|
|
21665
21896
|
// src/execution/tool-executor.ts
|
|
21666
21897
|
import { isDeepStrictEqual } from "node:util";
|
|
21667
21898
|
import * as fs9 from "node:fs/promises";
|
|
21668
|
-
import * as
|
|
21899
|
+
import * as path21 from "node:path";
|
|
21669
21900
|
|
|
21670
21901
|
// src/security/kanban-boundary.ts
|
|
21671
21902
|
import { realpath as realpath3 } from "node:fs/promises";
|
|
21672
|
-
import * as
|
|
21903
|
+
import * as path20 from "node:path";
|
|
21673
21904
|
import {
|
|
21674
21905
|
evaluateKanbanBoundaryOpaque,
|
|
21675
21906
|
evaluateKanbanBoundaryPath,
|
|
@@ -21744,10 +21975,10 @@ function resolveKanbanIdentity(ctx) {
|
|
|
21744
21975
|
async function extractCandidatePaths(toolName, input, ctx) {
|
|
21745
21976
|
if (toolName === "patch" && typeof input["patch"] === "string") {
|
|
21746
21977
|
const directoryInput = stringValue(input["directory"]) ?? ctx.workingDir;
|
|
21747
|
-
const directory =
|
|
21978
|
+
const directory = path20.isAbsolute(directoryInput) ? directoryInput : path20.resolve(ctx.workingDir, directoryInput);
|
|
21748
21979
|
const strip = Math.max(1, numericValue(input["strip"]) ?? 1);
|
|
21749
21980
|
const targets = extractPatchTargets(input["patch"], strip).map(
|
|
21750
|
-
(target) => relativeToProject(
|
|
21981
|
+
(target) => relativeToProject(path20.resolve(directory, target), ctx.projectRoot)
|
|
21751
21982
|
);
|
|
21752
21983
|
return Promise.all(targets.map((target) => canonicalizeCandidatePath(target, ctx)));
|
|
21753
21984
|
}
|
|
@@ -21757,7 +21988,7 @@ async function extractCandidatePaths(toolName, input, ctx) {
|
|
|
21757
21988
|
collectPathValues(input, values, pathKeys);
|
|
21758
21989
|
if (toolName === "scaffold" && typeof input["name"] === "string") {
|
|
21759
21990
|
const cwd = stringValue(input["cwd"]) ?? ctx.workingDir;
|
|
21760
|
-
values.push(
|
|
21991
|
+
values.push(path20.join(cwd, input["name"]));
|
|
21761
21992
|
}
|
|
21762
21993
|
const candidates = [
|
|
21763
21994
|
...new Set(values.flatMap(splitPathList).map((value) => resolveInputPath(value, ctx)))
|
|
@@ -21765,21 +21996,21 @@ async function extractCandidatePaths(toolName, input, ctx) {
|
|
|
21765
21996
|
return Promise.all(candidates.map((candidate) => canonicalizeCandidatePath(candidate, ctx)));
|
|
21766
21997
|
}
|
|
21767
21998
|
async function canonicalizeCandidatePath(candidate, ctx) {
|
|
21768
|
-
if (
|
|
21769
|
-
const absolute =
|
|
21999
|
+
if (path20.isAbsolute(candidate)) return candidate;
|
|
22000
|
+
const absolute = path20.resolve(ctx.projectRoot, candidate);
|
|
21770
22001
|
const canonicalRoot = await realpath3(ctx.projectRoot).catch(() => ctx.projectRoot);
|
|
21771
22002
|
let probe = absolute;
|
|
21772
22003
|
const missingSegments = [];
|
|
21773
22004
|
while (true) {
|
|
21774
22005
|
try {
|
|
21775
|
-
const canonical =
|
|
22006
|
+
const canonical = path20.join(await realpath3(probe), ...missingSegments);
|
|
21776
22007
|
return relativeToProject(canonical, canonicalRoot);
|
|
21777
22008
|
} catch (cause) {
|
|
21778
22009
|
const code = cause.code;
|
|
21779
22010
|
if (code !== "ENOENT" && code !== "ENOTDIR") return absolute;
|
|
21780
|
-
const parent =
|
|
22011
|
+
const parent = path20.dirname(probe);
|
|
21781
22012
|
if (parent === probe) return absolute;
|
|
21782
|
-
missingSegments.unshift(
|
|
22013
|
+
missingSegments.unshift(path20.basename(probe));
|
|
21783
22014
|
probe = parent;
|
|
21784
22015
|
}
|
|
21785
22016
|
}
|
|
@@ -21802,12 +22033,12 @@ function splitPathList(value) {
|
|
|
21802
22033
|
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
21803
22034
|
}
|
|
21804
22035
|
function resolveInputPath(value, ctx) {
|
|
21805
|
-
const absolute =
|
|
22036
|
+
const absolute = path20.isAbsolute(value) ? value : path20.resolve(ctx.workingDir, value);
|
|
21806
22037
|
return relativeToProject(absolute, ctx.projectRoot);
|
|
21807
22038
|
}
|
|
21808
22039
|
function relativeToProject(absolute, projectRoot) {
|
|
21809
|
-
const relative6 =
|
|
21810
|
-
return relative6.startsWith("../") ||
|
|
22040
|
+
const relative6 = path20.relative(projectRoot, absolute).replace(/\\/g, "/");
|
|
22041
|
+
return relative6.startsWith("../") || path20.isAbsolute(relative6) ? absolute : relative6 || ".";
|
|
21811
22042
|
}
|
|
21812
22043
|
function extractPatchTargets(patchText, strip) {
|
|
21813
22044
|
const targets = [];
|
|
@@ -22151,7 +22382,7 @@ ${errorDetails}`,
|
|
|
22151
22382
|
const inputPath = use.input && typeof use.input === "object" ? use.input.path : void 0;
|
|
22152
22383
|
const caps = tool.capabilities ?? [];
|
|
22153
22384
|
const hasFileCapability = caps.includes("fs.read") || caps.includes("fs.write");
|
|
22154
|
-
const absPath = hasFileCapability && typeof inputPath === "string" ?
|
|
22385
|
+
const absPath = hasFileCapability && typeof inputPath === "string" ? path21.isAbsolute(inputPath) ? inputPath : path21.resolve(ctx.projectRoot, inputPath) : void 0;
|
|
22155
22386
|
let writeTargetExisted;
|
|
22156
22387
|
if (tool.name === "write" && caps.includes("fs.write") && absPath) {
|
|
22157
22388
|
writeTargetExisted = await fs9.stat(absPath).then(
|
|
@@ -22743,11 +22974,11 @@ async function maybePersistLargeToolOutput(toolName, content, budget) {
|
|
|
22743
22974
|
return content;
|
|
22744
22975
|
}
|
|
22745
22976
|
try {
|
|
22746
|
-
const dir =
|
|
22977
|
+
const dir = path21.join(wstackGlobalRoot(), "tool-output");
|
|
22747
22978
|
await fs9.mkdir(dir, { recursive: true });
|
|
22748
22979
|
const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
|
|
22749
22980
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
22750
|
-
const filePath =
|
|
22981
|
+
const filePath = path21.join(dir, `${stamp}-${safeTool}-${randomUUID11()}.log`);
|
|
22751
22982
|
await fs9.writeFile(filePath, content, "utf8");
|
|
22752
22983
|
const marker = `[full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
|
|
22753
22984
|
const fixedBytes = Buffer.byteLength(marker + TOOL_OUTPUT_ARTIFACT_OMISSION, "utf8");
|
|
@@ -23066,191 +23297,6 @@ function createContextManagerTool(opts = {}) {
|
|
|
23066
23297
|
}
|
|
23067
23298
|
var contextManagerTool = createContextManagerTool();
|
|
23068
23299
|
|
|
23069
|
-
// src/infrastructure/logger.ts
|
|
23070
|
-
import * as fsp11 from "node:fs/promises";
|
|
23071
|
-
import * as path21 from "node:path";
|
|
23072
|
-
var LEVEL_RANK2 = {
|
|
23073
|
-
error: 0,
|
|
23074
|
-
warn: 1,
|
|
23075
|
-
info: 2,
|
|
23076
|
-
debug: 3,
|
|
23077
|
-
trace: 4
|
|
23078
|
-
};
|
|
23079
|
-
var COLORS = {
|
|
23080
|
-
error: color.red,
|
|
23081
|
-
warn: color.yellow,
|
|
23082
|
-
info: color.cyan,
|
|
23083
|
-
debug: color.gray,
|
|
23084
|
-
trace: color.dim
|
|
23085
|
-
};
|
|
23086
|
-
var LOG_LEVELS = /* @__PURE__ */ new Set(["error", "warn", "info", "debug", "trace"]);
|
|
23087
|
-
var LOG_FORMATS = /* @__PURE__ */ new Set(["pretty", "json"]);
|
|
23088
|
-
var DefaultLogger = class _DefaultLogger {
|
|
23089
|
-
/** How many file writes between rotation size checks (statSync is not free). */
|
|
23090
|
-
static ROTATE_CHECK_EVERY = 100;
|
|
23091
|
-
level;
|
|
23092
|
-
file;
|
|
23093
|
-
bindings;
|
|
23094
|
-
format;
|
|
23095
|
-
stderr;
|
|
23096
|
-
maxFileBytes;
|
|
23097
|
-
writesSinceRotateCheck = 0;
|
|
23098
|
-
/**
|
|
23099
|
-
* Serialized async tail for file writes. Every appendFile (and any
|
|
23100
|
-
* chained rotation) is awaited through this promise so file I/O
|
|
23101
|
-
* never overlaps itself — preserving the per-line ordering the
|
|
23102
|
-
* sync version had, but without blocking the caller thread. Any
|
|
23103
|
-
* rejection is swallowed (`catch(() => {})`) because logging must
|
|
23104
|
-
* never crash the host.
|
|
23105
|
-
*
|
|
23106
|
-
* Children share the parent's tail: `child.tail === parent.tail`
|
|
23107
|
-
* for the lifetime of the chain. Read/write access goes through
|
|
23108
|
-
* `_tail` so that, when a child has been wired to a parent, both
|
|
23109
|
-
* `enqueueRotate` and `log` always observe the parent's current tail
|
|
23110
|
-
* rather than a stale snapshot taken at `child()` time.
|
|
23111
|
-
*/
|
|
23112
|
-
tail = Promise.resolve();
|
|
23113
|
-
parent = null;
|
|
23114
|
-
/**
|
|
23115
|
-
* Resolve the current tail. For the root logger this is the field;
|
|
23116
|
-
* for a child logger we always read through the parent so that a
|
|
23117
|
-
* child's appends land on the parent's most recent tail, and a
|
|
23118
|
-
* parent's `flush()` waits for everything the child chained.
|
|
23119
|
-
*/
|
|
23120
|
-
get _tail() {
|
|
23121
|
-
return this.parent ? this.parent._tail : this.tail;
|
|
23122
|
-
}
|
|
23123
|
-
set _tail(next) {
|
|
23124
|
-
if (this.parent) this.parent.tail = next;
|
|
23125
|
-
else this.tail = next;
|
|
23126
|
-
}
|
|
23127
|
-
constructor(opts = {}) {
|
|
23128
|
-
this.level = opts.level ?? parseLogLevel(process.env.WRONGSTACK_LOG_LEVEL);
|
|
23129
|
-
this.file = opts.file;
|
|
23130
|
-
this.bindings = opts.bindings ?? {};
|
|
23131
|
-
this.format = opts.format ?? parseLogFormat(process.env.WRONGSTACK_LOG_FORMAT);
|
|
23132
|
-
this.stderr = opts.stderr !== false;
|
|
23133
|
-
this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
|
|
23134
|
-
if (this.file) {
|
|
23135
|
-
const dir = path21.dirname(this.file);
|
|
23136
|
-
this._tail = this._tail.then(async () => {
|
|
23137
|
-
await fsp11.mkdir(dir, { recursive: true });
|
|
23138
|
-
}).catch(() => void 0);
|
|
23139
|
-
}
|
|
23140
|
-
}
|
|
23141
|
-
error(msg, ctx) {
|
|
23142
|
-
this.log("error", msg, ctx);
|
|
23143
|
-
}
|
|
23144
|
-
warn(msg, ctx) {
|
|
23145
|
-
this.log("warn", msg, ctx);
|
|
23146
|
-
}
|
|
23147
|
-
info(msg, ctx) {
|
|
23148
|
-
this.log("info", msg, ctx);
|
|
23149
|
-
}
|
|
23150
|
-
debug(msg, ctx) {
|
|
23151
|
-
this.log("debug", msg, ctx);
|
|
23152
|
-
}
|
|
23153
|
-
trace(msg, ctx) {
|
|
23154
|
-
this.log("trace", msg, ctx);
|
|
23155
|
-
}
|
|
23156
|
-
child(bindings) {
|
|
23157
|
-
const child = Object.create(_DefaultLogger.prototype);
|
|
23158
|
-
child.level = this.level;
|
|
23159
|
-
child.file = this.file;
|
|
23160
|
-
child.bindings = { ...this.bindings, ...bindings };
|
|
23161
|
-
child.format = this.format;
|
|
23162
|
-
child.stderr = this.stderr;
|
|
23163
|
-
child.maxFileBytes = this.maxFileBytes;
|
|
23164
|
-
child.parent = this;
|
|
23165
|
-
child.writesSinceRotateCheck = this.writesSinceRotateCheck;
|
|
23166
|
-
return child;
|
|
23167
|
-
}
|
|
23168
|
-
/**
|
|
23169
|
-
* Wait until all queued file writes (and any pending rotation) have
|
|
23170
|
-
* completed. `log()` is fire-and-forget by design — the caller never
|
|
23171
|
-
* blocks on disk — so tests, shutdown handlers, and processes that
|
|
23172
|
-
* need a deterministic "everything is on disk now" guarantee should
|
|
23173
|
-
* `await logger.flush()` before reading the file or exiting.
|
|
23174
|
-
*/
|
|
23175
|
-
flush() {
|
|
23176
|
-
return this._tail;
|
|
23177
|
-
}
|
|
23178
|
-
/**
|
|
23179
|
-
* Size-based rotation: when the file outgrows `maxFileBytes`, rename it to
|
|
23180
|
-
* `<file>.1` (dropping the previous `.1`) so the live file restarts empty.
|
|
23181
|
-
* Checked on the first write and every ROTATE_CHECK_EVERY writes after.
|
|
23182
|
-
* Best-effort: a rename can fail on Windows while another process holds
|
|
23183
|
-
* the file — the next check retries. Multiple processes appending to the
|
|
23184
|
-
* same log all run this check; whoever crosses the threshold first wins.
|
|
23185
|
-
*
|
|
23186
|
-
* Async: the rotation runs on the file-write tail (so its writes don't
|
|
23187
|
-
* interleave with the next append), and the caller never blocks on a
|
|
23188
|
-
* statSync / renameSync syscall on the hot log path.
|
|
23189
|
-
*/
|
|
23190
|
-
enqueueRotate(file) {
|
|
23191
|
-
if (this.writesSinceRotateCheck++ % _DefaultLogger.ROTATE_CHECK_EVERY !== 0) return;
|
|
23192
|
-
this._tail = this._tail.then(async () => {
|
|
23193
|
-
let st;
|
|
23194
|
-
try {
|
|
23195
|
-
st = await fsp11.stat(file);
|
|
23196
|
-
} catch {
|
|
23197
|
-
return;
|
|
23198
|
-
}
|
|
23199
|
-
if (st.size < this.maxFileBytes) return;
|
|
23200
|
-
try {
|
|
23201
|
-
await fsp11.rm(`${file}.1`, { force: true });
|
|
23202
|
-
await fsp11.rename(file, `${file}.1`);
|
|
23203
|
-
} catch {
|
|
23204
|
-
}
|
|
23205
|
-
}).catch(() => void 0);
|
|
23206
|
-
}
|
|
23207
|
-
log(level, msg, ctx) {
|
|
23208
|
-
const r = LEVEL_RANK2[level];
|
|
23209
|
-
const allowed = LEVEL_RANK2[this.level];
|
|
23210
|
-
if (r > allowed) return;
|
|
23211
|
-
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
23212
|
-
const entry = { ts, level, msg, ...this.bindings };
|
|
23213
|
-
if (ctx !== void 0) {
|
|
23214
|
-
entry.ctx = ctx instanceof Error ? { message: ctx.message, stack: ctx.stack } : ctx;
|
|
23215
|
-
}
|
|
23216
|
-
if (this.file) {
|
|
23217
|
-
this.enqueueRotate(this.file);
|
|
23218
|
-
const line = `${JSON.stringify(entry)}
|
|
23219
|
-
`;
|
|
23220
|
-
this._tail = this._tail.then(() => fsp11.appendFile(this.file, line)).catch(() => void 0);
|
|
23221
|
-
}
|
|
23222
|
-
if (!this.stderr) return;
|
|
23223
|
-
if (this.format === "json") {
|
|
23224
|
-
writeErr(`${JSON.stringify(entry)}
|
|
23225
|
-
`);
|
|
23226
|
-
} else {
|
|
23227
|
-
const head = `${color.dim(ts)} ${COLORS[level](level.toUpperCase().padEnd(5))} ${msg}`;
|
|
23228
|
-
if (ctx !== void 0) {
|
|
23229
|
-
writeErr(`${head} ${formatCtx(ctx)}
|
|
23230
|
-
`);
|
|
23231
|
-
} else {
|
|
23232
|
-
writeErr(`${head}
|
|
23233
|
-
`);
|
|
23234
|
-
}
|
|
23235
|
-
}
|
|
23236
|
-
}
|
|
23237
|
-
};
|
|
23238
|
-
function parseLogLevel(raw) {
|
|
23239
|
-
return raw && LOG_LEVELS.has(raw) ? raw : "info";
|
|
23240
|
-
}
|
|
23241
|
-
function parseLogFormat(raw) {
|
|
23242
|
-
return raw && LOG_FORMATS.has(raw) ? raw : "pretty";
|
|
23243
|
-
}
|
|
23244
|
-
function formatCtx(ctx) {
|
|
23245
|
-
if (ctx instanceof Error) return color.dim(ctx.message);
|
|
23246
|
-
if (typeof ctx === "string") return color.dim(ctx);
|
|
23247
|
-
try {
|
|
23248
|
-
return color.dim(JSON.stringify(ctx));
|
|
23249
|
-
} catch {
|
|
23250
|
-
return color.dim(String(ctx));
|
|
23251
|
-
}
|
|
23252
|
-
}
|
|
23253
|
-
|
|
23254
23300
|
// src/infrastructure/mcp-servers.ts
|
|
23255
23301
|
var filesystemServer = () => ({
|
|
23256
23302
|
name: "filesystem",
|
|
@@ -23829,6 +23875,7 @@ var DefaultModelsRegistry = class {
|
|
|
23829
23875
|
overlayUrl;
|
|
23830
23876
|
overlayFile;
|
|
23831
23877
|
overlayCacheFile;
|
|
23878
|
+
logger;
|
|
23832
23879
|
constructor(opts) {
|
|
23833
23880
|
this.cacheFile = opts.cacheFile;
|
|
23834
23881
|
this.url = opts.url ?? process.env[ENV_URL_KEY] ?? DEFAULT_URL;
|
|
@@ -23842,6 +23889,7 @@ var DefaultModelsRegistry = class {
|
|
|
23842
23889
|
this.overlayUrl = opts.overlayUrl;
|
|
23843
23890
|
this.overlayFile = opts.overlayFile;
|
|
23844
23891
|
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path24.join(path24.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
|
|
23892
|
+
this.logger = opts.logger ?? noOpLogger;
|
|
23845
23893
|
}
|
|
23846
23894
|
async load(opts = {}) {
|
|
23847
23895
|
if (this.payload && !opts.force) return this.payload;
|
|
@@ -23890,16 +23938,18 @@ var DefaultModelsRegistry = class {
|
|
|
23890
23938
|
if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {
|
|
23891
23939
|
this.fetchedAt = new Date(cached.fetchedAt);
|
|
23892
23940
|
const ageSeconds = Math.floor((Date.now() - this.fetchedAt.getTime()) / 1e3);
|
|
23893
|
-
|
|
23894
|
-
`ModelsRegistry: models.dev unavailable (${toErrorMessage(err)}); using stale cache from ${formatAge(ageSeconds)} ago. Run \`wstack models refresh\` to retry
|
|
23941
|
+
this.logger.warn(
|
|
23942
|
+
`ModelsRegistry: models.dev unavailable (${toErrorMessage(err)}); using stale cache from ${formatAge(ageSeconds)} ago. Run \`wstack models refresh\` to retry.`,
|
|
23943
|
+
{ event: "models_registry.stale_cache_fallback" }
|
|
23895
23944
|
);
|
|
23896
23945
|
return cached.payload;
|
|
23897
23946
|
}
|
|
23898
23947
|
if (overlayAvailable) {
|
|
23899
|
-
|
|
23948
|
+
this.logger.warn(
|
|
23900
23949
|
`ModelsRegistry: models.dev unavailable (${toErrorMessage(
|
|
23901
23950
|
err
|
|
23902
|
-
)}); serving curated overlay only
|
|
23951
|
+
)}); serving curated overlay only.`,
|
|
23952
|
+
{ event: "models_registry.overlay_only_fallback" }
|
|
23903
23953
|
);
|
|
23904
23954
|
return {};
|
|
23905
23955
|
}
|
|
@@ -23995,8 +24045,9 @@ var DefaultModelsRegistry = class {
|
|
|
23995
24045
|
const cached = await this.readCacheAt(this.overlayCacheFile);
|
|
23996
24046
|
if (cached && this.isWithinMaxStaleAge(cached.fetchedAt)) {
|
|
23997
24047
|
const ageSeconds = Math.floor((Date.now() - new Date(cached.fetchedAt).getTime()) / 1e3);
|
|
23998
|
-
|
|
23999
|
-
`ModelsRegistry: overlay unavailable; using stale overlay from ${formatAge(ageSeconds)} ago
|
|
24048
|
+
this.logger.warn(
|
|
24049
|
+
`ModelsRegistry: overlay unavailable; using stale overlay from ${formatAge(ageSeconds)} ago.`,
|
|
24050
|
+
{ event: "models_registry.overlay_stale_fallback", ageSeconds }
|
|
24000
24051
|
);
|
|
24001
24052
|
return cached.payload;
|
|
24002
24053
|
}
|
|
@@ -25149,9 +25200,6 @@ var DefaultPermissionPolicy = class {
|
|
|
25149
25200
|
loaded = false;
|
|
25150
25201
|
trustFile;
|
|
25151
25202
|
yolo;
|
|
25152
|
-
yoloDestructive;
|
|
25153
|
-
/** Deprecated compatibility flag; no longer gates YOLO calls. */
|
|
25154
|
-
confirmDestructive;
|
|
25155
25203
|
/**
|
|
25156
25204
|
* Session-scoped "soft deny" map. When the user presses 'n' (block once),
|
|
25157
25205
|
* the tool+pattern is added here. If the LLM retries in the same session,
|
|
@@ -25204,8 +25252,6 @@ var DefaultPermissionPolicy = class {
|
|
|
25204
25252
|
constructor(opts) {
|
|
25205
25253
|
this.trustFile = opts.trustFile;
|
|
25206
25254
|
this.yolo = opts.yolo ?? false;
|
|
25207
|
-
this.yoloDestructive = opts.yoloDestructive ?? opts.forceAllYolo ?? false;
|
|
25208
|
-
this.confirmDestructive = opts.confirmDestructive ?? false;
|
|
25209
25255
|
this.promptDelegate = opts.promptDelegate;
|
|
25210
25256
|
}
|
|
25211
25257
|
/**
|
|
@@ -25226,24 +25272,6 @@ var DefaultPermissionPolicy = class {
|
|
|
25226
25272
|
getYolo() {
|
|
25227
25273
|
return this.yolo;
|
|
25228
25274
|
}
|
|
25229
|
-
/** Toggle the destructive YOLO override at runtime. */
|
|
25230
|
-
setYoloDestructive(enabled) {
|
|
25231
|
-
if (this.yoloDestructive !== enabled) this._evalCache.clear();
|
|
25232
|
-
this.yoloDestructive = enabled;
|
|
25233
|
-
}
|
|
25234
|
-
/** Check whether the destructive YOLO override is active. */
|
|
25235
|
-
getYoloDestructive() {
|
|
25236
|
-
return this.yoloDestructive;
|
|
25237
|
-
}
|
|
25238
|
-
/** Toggle deprecated destructive confirmation compatibility flag. */
|
|
25239
|
-
setConfirmDestructive(enabled) {
|
|
25240
|
-
if (this.confirmDestructive !== enabled) this._evalCache.clear();
|
|
25241
|
-
this.confirmDestructive = enabled;
|
|
25242
|
-
}
|
|
25243
|
-
/** Check deprecated destructive confirmation compatibility flag. */
|
|
25244
|
-
getConfirmDestructive() {
|
|
25245
|
-
return this.confirmDestructive;
|
|
25246
|
-
}
|
|
25247
25275
|
/** Read-only diagnostics for policy inspector/editor surfaces. */
|
|
25248
25276
|
getPolicyDiagnostics() {
|
|
25249
25277
|
return this.policyDiagnostics.map((diagnostic) => ({ ...diagnostic }));
|
|
@@ -26544,9 +26572,10 @@ var BEHAVIOR_DEFAULTS = {
|
|
|
26544
26572
|
prompts: true,
|
|
26545
26573
|
// 'auto' → resolveTokenSavingTier picks a concrete tier from the model's
|
|
26546
26574
|
// context window ONCE per session (cache-safe): lean prompt on small
|
|
26547
|
-
// windows (<32k medium, <
|
|
26548
|
-
// a big fraction;
|
|
26549
|
-
//
|
|
26575
|
+
// windows (<32k medium, <128k light) where the fixed identity+tool prose
|
|
26576
|
+
// is a big fraction; minimal trimming on >=128k so modern large-window
|
|
26577
|
+
// models still get cost savings without capability loss. Explicit tiers
|
|
26578
|
+
// are respected verbatim.
|
|
26550
26579
|
tokenSavingMode: "auto",
|
|
26551
26580
|
allowOutsideProjectRoot: true
|
|
26552
26581
|
},
|
|
@@ -26614,7 +26643,7 @@ var BEHAVIOR_DEFAULTS = {
|
|
|
26614
26643
|
// Mirrored from the top-level yolo default so the autonomy subsystem
|
|
26615
26644
|
// (which reads autonomy.yolo) stays consistent with config.yolo.
|
|
26616
26645
|
yolo: false,
|
|
26617
|
-
|
|
26646
|
+
fleetChatVerbosity: "off",
|
|
26618
26647
|
chime: false,
|
|
26619
26648
|
confirmExit: true,
|
|
26620
26649
|
mouseMode: false,
|
|
@@ -26632,7 +26661,7 @@ var BEHAVIOR_DEFAULTS = {
|
|
|
26632
26661
|
// silently omitted, and surfaced as a per-request warning. Users who
|
|
26633
26662
|
// want a specific effort can opt in via `/settings` or the WebUI panel.
|
|
26634
26663
|
reasoning: { mode: "auto" },
|
|
26635
|
-
cache: {}
|
|
26664
|
+
cache: { ttl: "1h" }
|
|
26636
26665
|
}
|
|
26637
26666
|
};
|
|
26638
26667
|
function isPlainRecord(value) {
|