@wrongstack/core 0.313.0 → 0.313.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/chronicle/index.js +84 -6391
- package/dist/chronicle/project-server.js +84 -4868
- package/dist/chunk-2BUUIMQB.js +225 -0
- package/dist/chunk-2FK4WTFF.js +207 -0
- package/dist/chunk-2FQL4PDC.js +292 -0
- package/dist/chunk-2KKODIQU.js +2256 -0
- package/dist/chunk-2YU4WVBW.js +134 -0
- package/dist/chunk-3E244UDA.js +356 -0
- package/dist/chunk-3XPVB7BA.js +135 -0
- package/dist/chunk-4LJJYBAU.js +388 -0
- package/dist/chunk-4VWO3ZXL.js +342 -0
- package/dist/chunk-4XV6IR37.js +187 -0
- package/dist/chunk-4ZHTKJHM.js +183 -0
- package/dist/chunk-52RTVH4M.js +361 -0
- package/dist/chunk-57GEWK5X.js +26 -0
- package/dist/chunk-5DSMEVA4.js +931 -0
- package/dist/chunk-5RP3QTRL.js +1223 -0
- package/dist/chunk-5S437PRC.js +50 -0
- package/dist/chunk-5XDUGTKN.js +511 -0
- package/dist/chunk-62V2AFVG.js +422 -0
- package/dist/chunk-65RDIOR2.js +2470 -0
- package/dist/chunk-6AKOTGLM.js +597 -0
- package/dist/chunk-7ZHZE5EO.js +28 -0
- package/dist/chunk-A455ATZB.js +140 -0
- package/dist/chunk-AD7ZFZCS.js +46 -0
- package/dist/chunk-AI4LLQ2U.js +856 -0
- package/dist/chunk-ASX6AW36.js +789 -0
- package/dist/chunk-AT4V4XGI.js +3522 -0
- package/dist/chunk-AWNQ6ED6.js +311 -0
- package/dist/chunk-B4AMLZPB.js +9 -0
- package/dist/chunk-BAKVJLEV.js +417 -0
- package/dist/chunk-BJHVULHO.js +55 -0
- package/dist/chunk-CB2HFKTP.js +9 -0
- package/dist/chunk-CE4ZOCOI.js +1640 -0
- package/dist/chunk-CQQL4ZOQ.js +151 -0
- package/dist/chunk-DEIQMBD2.js +65 -0
- package/dist/chunk-E6I5RXVK.js +58 -0
- package/dist/chunk-EFEHDQGY.js +1589 -0
- package/dist/chunk-EODDVJBM.js +82 -0
- package/dist/chunk-EPC3GZD4.js +308 -0
- package/dist/chunk-FAYRX2AD.js +13 -0
- package/dist/chunk-FFSX2QDQ.js +362 -0
- package/dist/chunk-FHAMQS7Q.js +5856 -0
- package/dist/chunk-FIV4IJL7.js +2263 -0
- package/dist/chunk-FNXVT7HF.js +52 -0
- package/dist/chunk-FSWCWSTS.js +395 -0
- package/dist/chunk-FW5LDDMJ.js +16464 -0
- package/dist/chunk-FXQLIX3K.js +211 -0
- package/dist/chunk-G5AAKEEZ.js +228 -0
- package/dist/chunk-G5S3S5ZN.js +38 -0
- package/dist/chunk-GAZEWQEZ.js +231 -0
- package/dist/chunk-GFJOMSHG.js +125 -0
- package/dist/chunk-GFXZTHNI.js +158 -0
- package/dist/chunk-GM54GJBU.js +960 -0
- package/dist/chunk-GPQ3NJPR.js +900 -0
- package/dist/chunk-GTCU6DZ3.js +1 -0
- package/dist/chunk-H5E77KUJ.js +15 -0
- package/dist/chunk-H72H4XDU.js +1023 -0
- package/dist/chunk-HA4ZYA6T.js +178 -0
- package/dist/chunk-I45KAW2N.js +5020 -0
- package/dist/chunk-IQ35HMQL.js +1396 -0
- package/dist/chunk-IYFBFX34.js +86 -0
- package/dist/chunk-JJPMWPB6.js +109 -0
- package/dist/chunk-JTHKOYWU.js +663 -0
- package/dist/chunk-JVGPR3TS.js +365 -0
- package/dist/chunk-JVKB2TBL.js +48 -0
- package/dist/chunk-JX7VBZDK.js +31 -0
- package/dist/chunk-K3G6JBUC.js +5232 -0
- package/dist/chunk-K4YUNKU7.js +589 -0
- package/dist/chunk-KBZGE6LN.js +710 -0
- package/dist/chunk-KDQJCT2J.js +45 -0
- package/dist/chunk-KI4L2DUL.js +560 -0
- package/dist/chunk-KJ2ZTQSL.js +47 -0
- package/dist/chunk-KSJ5UTP7.js +154 -0
- package/dist/chunk-LHOA5DWA.js +28 -0
- package/dist/chunk-LX5QWLBL.js +27 -0
- package/dist/chunk-LZDDLNCZ.js +279 -0
- package/dist/chunk-M2WF6AXG.js +115 -0
- package/dist/chunk-M7BS2K6U.js +1 -0
- package/dist/chunk-NDBIPFNY.js +102 -0
- package/dist/chunk-NEMIWEXC.js +1199 -0
- package/dist/chunk-NFAYHPRH.js +323 -0
- package/dist/chunk-O37BQ65S.js +21 -0
- package/dist/chunk-O74MJHXU.js +187 -0
- package/dist/chunk-OJZEX73F.js +112 -0
- package/dist/chunk-PAAWLLZX.js +74 -0
- package/dist/chunk-PFXFQQRT.js +339 -0
- package/dist/chunk-PFZO3C6H.js +14 -0
- package/dist/chunk-PHH7LD45.js +113 -0
- package/dist/chunk-QRTG4XTI.js +188 -0
- package/dist/chunk-QVOVMXJR.js +1351 -0
- package/dist/chunk-RCQYT2I4.js +757 -0
- package/dist/chunk-RD6DEE36.js +1178 -0
- package/dist/chunk-RJIZEJ42.js +30 -0
- package/dist/chunk-RPOA7IAE.js +150 -0
- package/dist/chunk-SN2DQRL6.js +1172 -0
- package/dist/chunk-SQJVFEBV.js +41 -0
- package/dist/chunk-SQP64RCS.js +5391 -0
- package/dist/chunk-SVT4RNTR.js +49 -0
- package/dist/chunk-SWUFN7XM.js +43 -0
- package/dist/chunk-SXMVYC5H.js +174 -0
- package/dist/chunk-SZ6AYUIZ.js +23 -0
- package/dist/chunk-T3KGZBU5.js +3171 -0
- package/dist/chunk-TBRL2RCC.js +9 -0
- package/dist/chunk-TRT2HRWZ.js +18 -0
- package/dist/chunk-TYW2GI5I.js +1077 -0
- package/dist/chunk-UIWONWVG.js +4959 -0
- package/dist/chunk-VB3H5WWB.js +151 -0
- package/dist/chunk-W4A6QA3D.js +258 -0
- package/dist/chunk-WBPVKEEE.js +227 -0
- package/dist/chunk-WEBMSQP2.js +123 -0
- package/dist/chunk-WOEZV3Q4.js +333 -0
- package/dist/chunk-WQQON3SV.js +473 -0
- package/dist/chunk-WQTOVMGV.js +119 -0
- package/dist/chunk-WZO23AWO.js +2779 -0
- package/dist/chunk-XBDP4QPW.js +7 -0
- package/dist/chunk-XEBHSJLT.js +103 -0
- package/dist/chunk-XQ2ECNK3.js +1 -0
- package/dist/chunk-XQ2XGIBF.js +2086 -0
- package/dist/chunk-Y2GE5TSI.js +355 -0
- package/dist/chunk-YBUJCZ57.js +351 -0
- package/dist/chunk-YNCVSFGF.js +149 -0
- package/dist/chunk-YUPMXOGP.js +282 -0
- package/dist/chunk-YY6APYUM.js +49 -0
- package/dist/chunk-Z2IJUO45.js +691 -0
- package/dist/chunk-ZEHUCJHY.js +129 -0
- package/dist/chunk-ZYU2YOJ6.js +1496 -0
- package/dist/chunk-ZZ4V3WIZ.js +163 -0
- package/dist/coordination/agents/index.js +145 -6260
- package/dist/coordination/index.js +365 -34324
- package/dist/coordination/mailbox-project-server.js +48 -3276
- package/dist/coordination/provider-status-tracker.d.ts +4 -0
- package/dist/core/agent.d.ts +14 -1
- package/dist/core/index.js +267 -12480
- package/dist/core/model-ref.js +5 -23
- package/dist/core/provider-runner.d.ts +36 -1
- package/dist/design/index.js +50 -1087
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +1599 -21888
- package/dist/execution/prompt-enhancer.js +16 -535
- package/dist/execution/retry-policy.d.ts +31 -1
- package/dist/extension/index.js +7 -311
- package/dist/goal/index.js +33 -3435
- package/dist/hooks/index.js +14 -1138
- package/dist/hq/index.js +112 -5139
- package/dist/hq/protocol.js +21 -668
- package/dist/index.js +1410 -92656
- package/dist/infrastructure/index.js +64 -4811
- package/dist/kernel/events/provider-events.d.ts +15 -0
- package/dist/kernel/index.js +14 -992
- package/dist/models/index.js +40 -1117
- package/dist/notifications/index.js +3 -108
- package/dist/observability/index.js +17 -279
- package/dist/plugin/index.js +584 -14835
- package/dist/prompts/index.js +14 -948
- package/dist/registry/index.js +9 -822
- package/dist/registry-R23UBE7H.js +14 -0
- package/dist/replay/index.js +6 -84
- package/dist/review-finding-store-DKAV3AS6.js +21 -0
- package/dist/review-report-store-OWY3EC3N.js +19 -0
- package/dist/security/index.js +114 -2202
- package/dist/session-catalog/index.js +36 -2103
- package/dist/session-catalog/project-server.js +42 -1435
- package/dist/skills/index.js +35 -1578
- package/dist/storage/index.js +182 -15600
- package/dist/tasking/index.js +10 -676
- package/dist/tools/index.js +52 -9590
- package/dist/types/config/autonomy.d.ts +5 -0
- package/dist/types/config/ui.d.ts +1 -1
- package/dist/types/errors.d.ts +18 -0
- package/dist/types/index.js +121 -1342
- package/dist/types/provider-runner.d.ts +8 -0
- package/dist/types/session-markers.js +8 -144
- package/dist/utils/error.js +3 -4
- package/dist/utils/expect-defined.js +3 -9
- package/dist/utils/heap-watchdog.js +6 -706
- package/dist/utils/index.js +269 -5685
- package/dist/utils/sage-output-block.js +5 -42
- package/dist/wiring/proxy-rewrite.d.ts +18 -0
- package/dist/wiring/proxy-rewrite.js +18 -121
- package/dist/worktree/index.js +6 -887
- package/package.json +4 -4
package/dist/models/index.js
CHANGED
|
@@ -1,507 +1,36 @@
|
|
|
1
|
-
// src/models/models-registry.ts
|
|
2
|
-
import * as fs from "node:fs/promises";
|
|
3
|
-
import * as path2 from "node:path";
|
|
4
|
-
|
|
5
|
-
// src/infrastructure/logger.ts
|
|
6
|
-
import * as fsp from "node:fs/promises";
|
|
7
|
-
import * as path from "node:path";
|
|
8
|
-
|
|
9
|
-
// src/utils/term.ts
|
|
10
|
-
var hasStdout = () => typeof process !== "undefined" && !!process.stdout;
|
|
11
|
-
function isStdoutTTY() {
|
|
12
|
-
return hasStdout() && Boolean(process.stdout.isTTY);
|
|
13
|
-
}
|
|
14
|
-
var activeOutputGuard = null;
|
|
15
|
-
function writeTo(s, stream) {
|
|
16
|
-
if (!stream || typeof stream.write !== "function") return false;
|
|
17
|
-
const guard = activeOutputGuard;
|
|
18
|
-
if (!guard) {
|
|
19
|
-
stream.write(s);
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
guard.suspend();
|
|
23
|
-
stream.write(s);
|
|
24
|
-
guard.resume();
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
function writeErr(s, stream = process.stderr) {
|
|
28
|
-
return writeTo(s, stream);
|
|
29
|
-
}
|
|
30
|
-
var ESCAPE_TERMINATOR = Object.freeze({
|
|
31
|
-
BEL: "\x07",
|
|
32
|
-
// OSC 0 / OSC 2 (title)
|
|
33
|
-
ST: "\x1B\\"
|
|
34
|
-
// DCS / strict OSC
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// src/utils/color.ts
|
|
38
|
-
var isColorTty = () => {
|
|
39
|
-
if (envFlag(process.env.NO_COLOR)) return false;
|
|
40
|
-
if (envFlag(process.env.FORCE_COLOR)) return true;
|
|
41
|
-
return isStdoutTTY();
|
|
42
|
-
};
|
|
43
|
-
function envFlag(value) {
|
|
44
|
-
if (value === void 0) return false;
|
|
45
|
-
if (value.trim() === "") return false;
|
|
46
|
-
return !/^(0|false|no|off)$/i.test(value.trim());
|
|
47
|
-
}
|
|
48
|
-
var COLOR = isColorTty();
|
|
49
|
-
var wrap = (open, close) => (s) => COLOR ? `\x1B[${open}m${s}\x1B[${close}m` : s;
|
|
50
|
-
var color = {
|
|
51
|
-
reset: wrap("0", "0"),
|
|
52
|
-
bold: wrap("1", "22"),
|
|
53
|
-
dim: wrap("2", "22"),
|
|
54
|
-
italic: wrap("3", "23"),
|
|
55
|
-
underline: wrap("4", "24"),
|
|
56
|
-
red: wrap("31", "39"),
|
|
57
|
-
green: wrap("32", "39"),
|
|
58
|
-
yellow: wrap("33", "39"),
|
|
59
|
-
blue: wrap("34", "39"),
|
|
60
|
-
magenta: wrap("35", "39"),
|
|
61
|
-
cyan: wrap("36", "39"),
|
|
62
|
-
gray: wrap("90", "39"),
|
|
63
|
-
amber: wrap("38;5;214", "39"),
|
|
64
|
-
pink: wrap("38;5;205", "39"),
|
|
65
|
-
bgRed: wrap("41", "49"),
|
|
66
|
-
bgGreen: wrap("42", "49")
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// src/infrastructure/logger.ts
|
|
70
|
-
var LEVEL_RANK = {
|
|
71
|
-
error: 0,
|
|
72
|
-
warn: 1,
|
|
73
|
-
info: 2,
|
|
74
|
-
debug: 3,
|
|
75
|
-
trace: 4
|
|
76
|
-
};
|
|
77
|
-
var COLORS = {
|
|
78
|
-
error: color.red,
|
|
79
|
-
warn: color.yellow,
|
|
80
|
-
info: color.cyan,
|
|
81
|
-
debug: color.gray,
|
|
82
|
-
trace: color.dim
|
|
83
|
-
};
|
|
84
|
-
var LOG_LEVELS = /* @__PURE__ */ new Set(["error", "warn", "info", "debug", "trace"]);
|
|
85
|
-
var LOG_FORMATS = /* @__PURE__ */ new Set(["pretty", "json"]);
|
|
86
|
-
var DefaultLogger = class _DefaultLogger {
|
|
87
|
-
/** How many file writes between rotation size checks (statSync is not free). */
|
|
88
|
-
static ROTATE_CHECK_EVERY = 100;
|
|
89
|
-
static MAX_PENDING_FILE_WRITES = 2e3;
|
|
90
|
-
static MAX_PENDING_FILE_BYTES = 8 * 1024 * 1024;
|
|
91
|
-
level;
|
|
92
|
-
file;
|
|
93
|
-
bindings;
|
|
94
|
-
format;
|
|
95
|
-
stderr;
|
|
96
|
-
maxFileBytes;
|
|
97
|
-
writesSinceRotateCheck = 0;
|
|
98
|
-
/**
|
|
99
|
-
* Serialized async tail for file writes. Every appendFile (and any
|
|
100
|
-
* chained rotation) is awaited through this promise so file I/O
|
|
101
|
-
* never overlaps itself — preserving the per-line ordering the
|
|
102
|
-
* sync version had, but without blocking the caller thread. Any
|
|
103
|
-
* rejection is swallowed (`catch(() => {})`) because logging must
|
|
104
|
-
* never crash the host.
|
|
105
|
-
*
|
|
106
|
-
* Children share the parent's tail: `child.tail === parent.tail`
|
|
107
|
-
* for the lifetime of the chain. Read/write access goes through
|
|
108
|
-
* `_tail` so that, when a child has been wired to a parent, both
|
|
109
|
-
* `enqueueRotate` and `log` always observe the parent's current tail
|
|
110
|
-
* rather than a stale snapshot taken at `child()` time.
|
|
111
|
-
*/
|
|
112
|
-
tail = Promise.resolve();
|
|
113
|
-
parent = null;
|
|
114
|
-
pendingFileWrites = 0;
|
|
115
|
-
pendingFileBytes = 0;
|
|
116
|
-
get root() {
|
|
117
|
-
return this.parent ? this.parent.root : this;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Resolve the current tail. For the root logger this is the field;
|
|
121
|
-
* for a child logger we always read through the parent so that a
|
|
122
|
-
* child's appends land on the parent's most recent tail, and a
|
|
123
|
-
* parent's `flush()` waits for everything the child chained.
|
|
124
|
-
*/
|
|
125
|
-
get _tail() {
|
|
126
|
-
return this.parent ? this.parent._tail : this.tail;
|
|
127
|
-
}
|
|
128
|
-
set _tail(next) {
|
|
129
|
-
if (this.parent) this.parent.tail = next;
|
|
130
|
-
else this.tail = next;
|
|
131
|
-
}
|
|
132
|
-
constructor(opts = {}) {
|
|
133
|
-
this.level = opts.level ?? parseLogLevel(process.env.WRONGSTACK_LOG_LEVEL);
|
|
134
|
-
this.file = opts.file;
|
|
135
|
-
this.bindings = opts.bindings ?? {};
|
|
136
|
-
this.format = opts.format ?? parseLogFormat(process.env.WRONGSTACK_LOG_FORMAT);
|
|
137
|
-
this.stderr = opts.stderr !== false;
|
|
138
|
-
this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
|
|
139
|
-
if (this.file) {
|
|
140
|
-
const dir = path.dirname(this.file);
|
|
141
|
-
this._tail = this._tail.then(async () => {
|
|
142
|
-
await fsp.mkdir(dir, { recursive: true });
|
|
143
|
-
}).catch(() => void 0);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
error(msg, ctx) {
|
|
147
|
-
this.log("error", msg, ctx);
|
|
148
|
-
}
|
|
149
|
-
warn(msg, ctx) {
|
|
150
|
-
this.log("warn", msg, ctx);
|
|
151
|
-
}
|
|
152
|
-
info(msg, ctx) {
|
|
153
|
-
this.log("info", msg, ctx);
|
|
154
|
-
}
|
|
155
|
-
debug(msg, ctx) {
|
|
156
|
-
this.log("debug", msg, ctx);
|
|
157
|
-
}
|
|
158
|
-
trace(msg, ctx) {
|
|
159
|
-
this.log("trace", msg, ctx);
|
|
160
|
-
}
|
|
161
|
-
child(bindings) {
|
|
162
|
-
const child = Object.create(_DefaultLogger.prototype);
|
|
163
|
-
child.level = this.level;
|
|
164
|
-
child.file = this.file;
|
|
165
|
-
child.bindings = { ...this.bindings, ...bindings };
|
|
166
|
-
child.format = this.format;
|
|
167
|
-
child.stderr = this.stderr;
|
|
168
|
-
child.maxFileBytes = this.maxFileBytes;
|
|
169
|
-
child.parent = this;
|
|
170
|
-
child.writesSinceRotateCheck = this.writesSinceRotateCheck;
|
|
171
|
-
return child;
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Wait until all queued file writes (and any pending rotation) have
|
|
175
|
-
* completed. `log()` is fire-and-forget by design — the caller never
|
|
176
|
-
* blocks on disk — so tests, shutdown handlers, and processes that
|
|
177
|
-
* need a deterministic "everything is on disk now" guarantee should
|
|
178
|
-
* `await logger.flush()` before reading the file or exiting.
|
|
179
|
-
*/
|
|
180
|
-
flush() {
|
|
181
|
-
return this._tail;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Size-based rotation: when the file outgrows `maxFileBytes`, rename it to
|
|
185
|
-
* `<file>.1` (dropping the previous `.1`) so the live file restarts empty.
|
|
186
|
-
* Checked on the first write and every ROTATE_CHECK_EVERY writes after.
|
|
187
|
-
* Best-effort: a rename can fail on Windows while another process holds
|
|
188
|
-
* the file — the next check retries. Multiple processes appending to the
|
|
189
|
-
* same log all run this check; whoever crosses the threshold first wins.
|
|
190
|
-
*
|
|
191
|
-
* Async: the rotation runs on the file-write tail (so its writes don't
|
|
192
|
-
* interleave with the next append), and the caller never blocks on a
|
|
193
|
-
* statSync / renameSync syscall on the hot log path.
|
|
194
|
-
*/
|
|
195
|
-
enqueueRotate(file) {
|
|
196
|
-
if (this.writesSinceRotateCheck++ % _DefaultLogger.ROTATE_CHECK_EVERY !== 0) return;
|
|
197
|
-
this._tail = this._tail.then(async () => {
|
|
198
|
-
let st;
|
|
199
|
-
try {
|
|
200
|
-
st = await fsp.stat(file);
|
|
201
|
-
} catch {
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
if (st.size < this.maxFileBytes) return;
|
|
205
|
-
try {
|
|
206
|
-
await fsp.rm(`${file}.1`, { force: true });
|
|
207
|
-
await fsp.rename(file, `${file}.1`);
|
|
208
|
-
} catch {
|
|
209
|
-
}
|
|
210
|
-
}).catch(() => void 0);
|
|
211
|
-
}
|
|
212
|
-
log(level, msg, ctx) {
|
|
213
|
-
const r = LEVEL_RANK[level];
|
|
214
|
-
const allowed = LEVEL_RANK[this.level];
|
|
215
|
-
if (r > allowed) return;
|
|
216
|
-
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
217
|
-
const entry = { ts, level, msg, ...this.bindings };
|
|
218
|
-
if (ctx !== void 0) {
|
|
219
|
-
entry.ctx = ctx instanceof Error ? { message: ctx.message, stack: ctx.stack } : ctx;
|
|
220
|
-
}
|
|
221
|
-
if (this.file) {
|
|
222
|
-
const line = `${JSON.stringify(entry)}
|
|
223
|
-
`;
|
|
224
|
-
const bytes = Buffer.byteLength(line, "utf8");
|
|
225
|
-
const root = this.root;
|
|
226
|
-
if (root.pendingFileWrites < _DefaultLogger.MAX_PENDING_FILE_WRITES && bytes <= _DefaultLogger.MAX_PENDING_FILE_BYTES && root.pendingFileBytes + bytes <= _DefaultLogger.MAX_PENDING_FILE_BYTES) {
|
|
227
|
-
root.pendingFileWrites += 1;
|
|
228
|
-
root.pendingFileBytes += bytes;
|
|
229
|
-
this.enqueueRotate(this.file);
|
|
230
|
-
this._tail = this._tail.then(() => fsp.appendFile(this.file, line, { mode: 384 })).catch(() => void 0).finally(() => {
|
|
231
|
-
root.pendingFileWrites = Math.max(0, root.pendingFileWrites - 1);
|
|
232
|
-
root.pendingFileBytes = Math.max(0, root.pendingFileBytes - bytes);
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
if (!this.stderr) return;
|
|
237
|
-
if (this.format === "json") {
|
|
238
|
-
writeErr(`${JSON.stringify(entry)}
|
|
239
|
-
`);
|
|
240
|
-
} else {
|
|
241
|
-
const head = `${color.dim(ts)} ${COLORS[level](level.toUpperCase().padEnd(5))} ${msg}`;
|
|
242
|
-
if (ctx !== void 0) {
|
|
243
|
-
writeErr(`${head} ${formatCtx(ctx)}
|
|
244
|
-
`);
|
|
245
|
-
} else {
|
|
246
|
-
writeErr(`${head}
|
|
247
|
-
`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
};
|
|
252
|
-
function parseLogLevel(raw) {
|
|
253
|
-
return raw && LOG_LEVELS.has(raw) ? raw : "info";
|
|
254
|
-
}
|
|
255
|
-
function parseLogFormat(raw) {
|
|
256
|
-
return raw && LOG_FORMATS.has(raw) ? raw : "pretty";
|
|
257
|
-
}
|
|
258
|
-
function formatCtx(ctx) {
|
|
259
|
-
if (ctx instanceof Error) return color.dim(ctx.message);
|
|
260
|
-
if (typeof ctx === "string") return color.dim(ctx);
|
|
261
|
-
try {
|
|
262
|
-
return color.dim(JSON.stringify(ctx));
|
|
263
|
-
} catch {
|
|
264
|
-
return color.dim(String(ctx));
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
var noOpLogger = {
|
|
268
|
-
// 'error' is the quietest level the Logger contract offers; the methods
|
|
269
|
-
// discard everything regardless, this only matters to level checks.
|
|
270
|
-
level: "error",
|
|
271
|
-
error: () => {
|
|
272
|
-
},
|
|
273
|
-
warn: () => {
|
|
274
|
-
},
|
|
275
|
-
info: () => {
|
|
276
|
-
},
|
|
277
|
-
debug: () => {
|
|
278
|
-
},
|
|
279
|
-
trace: () => {
|
|
280
|
-
},
|
|
281
|
-
child: () => noOpLogger
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
// src/utils/atomic-write.ts
|
|
285
1
|
import {
|
|
286
|
-
|
|
287
|
-
} from "
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
CONFIG_MIGRATION_NEEDED: "CONFIG_MIGRATION_NEEDED",
|
|
315
|
-
// Plugin
|
|
316
|
-
PLUGIN_LOAD_FAILED: "PLUGIN_LOAD_FAILED",
|
|
317
|
-
PLUGIN_API_MISMATCH: "PLUGIN_API_MISMATCH",
|
|
318
|
-
PLUGIN_MISSING_DEPENDENCY: "PLUGIN_MISSING_DEPENDENCY",
|
|
319
|
-
// Agent
|
|
320
|
-
AGENT_ITERATION_LIMIT: "AGENT_ITERATION_LIMIT",
|
|
321
|
-
AGENT_CONTEXT_OVERFLOW: "AGENT_CONTEXT_OVERFLOW",
|
|
322
|
-
AGENT_ABORTED: "AGENT_ABORTED",
|
|
323
|
-
AGENT_RUN_FAILED: "AGENT_RUN_FAILED",
|
|
324
|
-
// Session
|
|
325
|
-
SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
|
|
326
|
-
SESSION_CORRUPTED: "SESSION_CORRUPTED",
|
|
327
|
-
SESSION_WRITE_FAILED: "SESSION_WRITE_FAILED",
|
|
328
|
-
// Container / Registry
|
|
329
|
-
CONTAINER_TOKEN_ALREADY_BOUND: "CONTAINER_TOKEN_ALREADY_BOUND",
|
|
330
|
-
CONTAINER_TOKEN_NOT_BOUND: "CONTAINER_TOKEN_NOT_BOUND",
|
|
331
|
-
CONTAINER_CIRCULAR_DEPENDENCY: "CONTAINER_CIRCULAR_DEPENDENCY",
|
|
332
|
-
REGISTRY_DUPLICATE: "REGISTRY_DUPLICATE",
|
|
333
|
-
REGISTRY_NOT_FOUND: "REGISTRY_NOT_FOUND",
|
|
334
|
-
REGISTRY_INVALID: "REGISTRY_INVALID",
|
|
335
|
-
// File system
|
|
336
|
-
FS_READ_FAILED: "FS_READ_FAILED",
|
|
337
|
-
FS_WRITE_FAILED: "FS_WRITE_FAILED",
|
|
338
|
-
FS_MKDIR_FAILED: "FS_MKDIR_FAILED",
|
|
339
|
-
FS_DELETE_FAILED: "FS_DELETE_FAILED",
|
|
340
|
-
FS_ATOMIC_WRITE_FAILED: "FS_ATOMIC_WRITE_FAILED",
|
|
341
|
-
// SDD (Spec-Driven Development)
|
|
342
|
-
SDD_VALIDATION_FAILED: "SDD_VALIDATION_FAILED",
|
|
343
|
-
SDD_PARSE_FAILED: "SDD_PARSE_FAILED",
|
|
344
|
-
SDD_INVALID_STATE: "SDD_INVALID_STATE",
|
|
345
|
-
SDD_NOT_READY: "SDD_NOT_READY",
|
|
346
|
-
// General
|
|
347
|
-
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
348
|
-
PARSE_FAILED: "PARSE_FAILED",
|
|
349
|
-
UNKNOWN: "UNKNOWN"
|
|
350
|
-
};
|
|
351
|
-
var WrongStackError = class extends Error {
|
|
352
|
-
code;
|
|
353
|
-
subsystem;
|
|
354
|
-
severity;
|
|
355
|
-
recoverable;
|
|
356
|
-
context;
|
|
357
|
-
constructor(opts) {
|
|
358
|
-
super(opts.message, { cause: opts.cause });
|
|
359
|
-
this.name = "WrongStackError";
|
|
360
|
-
this.code = opts.code;
|
|
361
|
-
this.subsystem = opts.subsystem;
|
|
362
|
-
this.severity = opts.severity ?? "error";
|
|
363
|
-
this.recoverable = opts.recoverable ?? false;
|
|
364
|
-
this.context = opts.context;
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Render a one-line user-facing description.
|
|
368
|
-
* Subclasses should override for domain-specific formatting.
|
|
369
|
-
*/
|
|
370
|
-
describe() {
|
|
371
|
-
const ctx = this.context ? ` ${formatContext(this.context)}` : "";
|
|
372
|
-
return `${this.code}: ${this.message}${ctx}`;
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
|
-
function formatContext(ctx) {
|
|
376
|
-
const parts = Object.entries(ctx).filter(([, v]) => v !== void 0).slice(0, 3).map(([k, v]) => `${k}=${String(v)}`);
|
|
377
|
-
return parts.length > 0 ? `[${parts.join(" ")}]` : "";
|
|
378
|
-
}
|
|
379
|
-
var FsError = class extends WrongStackError {
|
|
380
|
-
path;
|
|
381
|
-
constructor(opts) {
|
|
382
|
-
super({
|
|
383
|
-
message: opts.message,
|
|
384
|
-
code: opts.code,
|
|
385
|
-
subsystem: "fs",
|
|
386
|
-
severity: "error",
|
|
387
|
-
recoverable: opts.code !== ERROR_CODES.FS_READ_FAILED,
|
|
388
|
-
context: { path: opts.path, ...opts.context },
|
|
389
|
-
cause: opts.cause
|
|
390
|
-
});
|
|
391
|
-
this.name = "FsError";
|
|
392
|
-
this.path = opts.path;
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
var FetchError = class extends WrongStackError {
|
|
396
|
-
status;
|
|
397
|
-
constructor(opts) {
|
|
398
|
-
super({
|
|
399
|
-
message: opts.message,
|
|
400
|
-
code: ERROR_CODES.VALIDATION_ERROR,
|
|
401
|
-
subsystem: "general",
|
|
402
|
-
severity: "error",
|
|
403
|
-
recoverable: opts.status === 429 || opts.status >= 500,
|
|
404
|
-
context: { status: opts.status, ...opts.context },
|
|
405
|
-
cause: opts.cause
|
|
406
|
-
});
|
|
407
|
-
this.name = "FetchError";
|
|
408
|
-
this.status = opts.status;
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
// src/utils/atomic-write.ts
|
|
413
|
-
var primitives = createPersistencePrimitives({
|
|
414
|
-
createLockTimeoutError: ({ targetPath, timeoutMs }) => new FsError({
|
|
415
|
-
message: `Timed out waiting for file lock: ${targetPath}`,
|
|
416
|
-
code: "FS_ATOMIC_WRITE_FAILED",
|
|
417
|
-
path: targetPath,
|
|
418
|
-
context: { timeoutMs }
|
|
419
|
-
})
|
|
420
|
-
});
|
|
421
|
-
var atomicWrite = primitives.atomicWrite;
|
|
422
|
-
var atomicReplaceWithWriter = primitives.atomicReplaceWithWriter;
|
|
423
|
-
var ensureDir = primitives.ensureDir;
|
|
424
|
-
var withFileLock = primitives.withFileLock;
|
|
425
|
-
|
|
426
|
-
// src/utils/merge-models-payload.ts
|
|
427
|
-
var REMOVE_PROVIDERS_KEY = "_removeProviders";
|
|
428
|
-
var REMOVE_MODELS_KEY = "_removeModels";
|
|
429
|
-
function mergeModelsPayload(base, overlay) {
|
|
430
|
-
const removeProviders = Array.isArray(overlay[REMOVE_PROVIDERS_KEY]) ? overlay[REMOVE_PROVIDERS_KEY] : [];
|
|
431
|
-
const removeModels = overlay[REMOVE_MODELS_KEY] && typeof overlay[REMOVE_MODELS_KEY] === "object" ? overlay[REMOVE_MODELS_KEY] : {};
|
|
432
|
-
const out = {};
|
|
433
|
-
for (const [id, provider] of Object.entries(base)) {
|
|
434
|
-
out[id] = cloneProvider(provider);
|
|
435
|
-
}
|
|
436
|
-
for (const [id, ovProvider] of Object.entries(overlay)) {
|
|
437
|
-
if (id === REMOVE_PROVIDERS_KEY || id === REMOVE_MODELS_KEY) continue;
|
|
438
|
-
const existing = out[id];
|
|
439
|
-
out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
|
|
440
|
-
}
|
|
441
|
-
for (const providerId of removeProviders) {
|
|
442
|
-
delete out[providerId];
|
|
443
|
-
}
|
|
444
|
-
for (const [providerId, modelIds] of Object.entries(removeModels)) {
|
|
445
|
-
const provider = out[providerId];
|
|
446
|
-
if (!provider?.models) continue;
|
|
447
|
-
for (const modelId of modelIds) {
|
|
448
|
-
delete provider.models[modelId];
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
return out;
|
|
452
|
-
}
|
|
453
|
-
function mergeProvider(base, overlay) {
|
|
454
|
-
const models = {};
|
|
455
|
-
for (const [mid, m] of Object.entries(base.models ?? {})) {
|
|
456
|
-
models[mid] = { ...m };
|
|
457
|
-
}
|
|
458
|
-
for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {
|
|
459
|
-
const existing = models[mid];
|
|
460
|
-
models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };
|
|
461
|
-
}
|
|
462
|
-
return {
|
|
463
|
-
...base,
|
|
464
|
-
// Overlay scalar fields win when explicitly provided; otherwise keep base.
|
|
465
|
-
...stripUndefined({
|
|
466
|
-
id: overlay.id,
|
|
467
|
-
name: overlay.name,
|
|
468
|
-
npm: overlay.npm,
|
|
469
|
-
api: overlay.api,
|
|
470
|
-
env: overlay.env,
|
|
471
|
-
doc: overlay.doc
|
|
472
|
-
}),
|
|
473
|
-
models
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
function mergeModel(base, overlay) {
|
|
477
|
-
const merged = { ...base, ...overlay };
|
|
478
|
-
if (base.limit || overlay.limit) {
|
|
479
|
-
merged.limit = { ...base.limit, ...overlay.limit };
|
|
480
|
-
}
|
|
481
|
-
if (base.cost || overlay.cost) {
|
|
482
|
-
merged.cost = { ...base.cost, ...overlay.cost };
|
|
483
|
-
}
|
|
484
|
-
if (base.modalities || overlay.modalities) {
|
|
485
|
-
merged.modalities = { ...base.modalities, ...overlay.modalities };
|
|
486
|
-
}
|
|
487
|
-
return merged;
|
|
488
|
-
}
|
|
489
|
-
function cloneProvider(p) {
|
|
490
|
-
const models = {};
|
|
491
|
-
for (const [mid, m] of Object.entries(p.models ?? {})) {
|
|
492
|
-
models[mid] = { ...m };
|
|
493
|
-
}
|
|
494
|
-
return { ...p, models };
|
|
495
|
-
}
|
|
496
|
-
function stripUndefined(obj) {
|
|
497
|
-
const out = {};
|
|
498
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
499
|
-
if (v !== void 0) out[k] = v;
|
|
500
|
-
}
|
|
501
|
-
return out;
|
|
502
|
-
}
|
|
2
|
+
LLMSelector
|
|
3
|
+
} from "../chunk-2FQL4PDC.js";
|
|
4
|
+
import {
|
|
5
|
+
noOpLogger
|
|
6
|
+
} from "../chunk-WBPVKEEE.js";
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_MODES
|
|
9
|
+
} from "../chunk-2FK4WTFF.js";
|
|
10
|
+
import {
|
|
11
|
+
mergeModelsPayload
|
|
12
|
+
} from "../chunk-EODDVJBM.js";
|
|
13
|
+
import {
|
|
14
|
+
FORBIDDEN_PROTO_KEYS
|
|
15
|
+
} from "../chunk-DEIQMBD2.js";
|
|
16
|
+
import "../chunk-5S437PRC.js";
|
|
17
|
+
import "../chunk-4VWO3ZXL.js";
|
|
18
|
+
import "../chunk-FSWCWSTS.js";
|
|
19
|
+
import {
|
|
20
|
+
atomicWrite
|
|
21
|
+
} from "../chunk-7ZHZE5EO.js";
|
|
22
|
+
import "../chunk-O37BQ65S.js";
|
|
23
|
+
import {
|
|
24
|
+
FetchError
|
|
25
|
+
} from "../chunk-WOEZV3Q4.js";
|
|
26
|
+
import {
|
|
27
|
+
toErrorMessage
|
|
28
|
+
} from "../chunk-TBRL2RCC.js";
|
|
29
|
+
import "../chunk-PFZO3C6H.js";
|
|
503
30
|
|
|
504
31
|
// src/models/models-registry.ts
|
|
32
|
+
import * as fs from "node:fs/promises";
|
|
33
|
+
import * as path from "node:path";
|
|
505
34
|
var DEFAULT_URL = "https://models.dev/api.json";
|
|
506
35
|
var ENV_URL_KEY = "WRONGSTACK_MODELS_DEV_URL";
|
|
507
36
|
var DEFAULT_TTL_SECONDS = 3 * 3600;
|
|
@@ -578,7 +107,7 @@ var DefaultModelsRegistry = class {
|
|
|
578
107
|
this.overlay = opts.overlay;
|
|
579
108
|
this.overlayUrl = opts.overlayUrl;
|
|
580
109
|
this.overlayFile = opts.overlayFile;
|
|
581
|
-
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ?
|
|
110
|
+
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path.join(path.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
|
|
582
111
|
this.logger = opts.logger ?? noOpLogger;
|
|
583
112
|
}
|
|
584
113
|
async load(opts = {}) {
|
|
@@ -853,7 +382,7 @@ var DefaultModelsRegistry = class {
|
|
|
853
382
|
}
|
|
854
383
|
/** Used by `wstack models refresh` to expose where the cache lives. */
|
|
855
384
|
cacheLocation() {
|
|
856
|
-
return
|
|
385
|
+
return path.resolve(this.cacheFile);
|
|
857
386
|
}
|
|
858
387
|
};
|
|
859
388
|
var REASONING_EFFORTS = /* @__PURE__ */ new Set([
|
|
@@ -923,212 +452,7 @@ function looksLikeModelsPayload(value, requireModels) {
|
|
|
923
452
|
|
|
924
453
|
// src/models/mode-store.ts
|
|
925
454
|
import * as fs2 from "node:fs/promises";
|
|
926
|
-
import * as
|
|
927
|
-
|
|
928
|
-
// src/types/mode-prompts.ts
|
|
929
|
-
import { readFileSync, statSync } from "node:fs";
|
|
930
|
-
import * as path3 from "node:path";
|
|
931
|
-
import { fileURLToPath } from "node:url";
|
|
932
|
-
function modePrompt(id) {
|
|
933
|
-
for (const dir of modePromptDirCandidates()) {
|
|
934
|
-
try {
|
|
935
|
-
return readFileSync(path3.join(dir, `${id}.md`), "utf8").trimEnd();
|
|
936
|
-
} catch {
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
return "";
|
|
940
|
-
}
|
|
941
|
-
function modePromptDirCandidates() {
|
|
942
|
-
const here = path3.dirname(fileURLToPath(import.meta.url));
|
|
943
|
-
const candidates = [
|
|
944
|
-
path3.resolve(here, "../../instructions/modes"),
|
|
945
|
-
path3.resolve(here, "../instructions/modes"),
|
|
946
|
-
path3.resolve(here, "instructions/modes")
|
|
947
|
-
];
|
|
948
|
-
return candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));
|
|
949
|
-
}
|
|
950
|
-
function isDirectory(candidate) {
|
|
951
|
-
try {
|
|
952
|
-
return statSync(candidate).isDirectory();
|
|
953
|
-
} catch {
|
|
954
|
-
return false;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
// src/types/mode.ts
|
|
959
|
-
var DEFAULT_MODES = [
|
|
960
|
-
{
|
|
961
|
-
id: "default",
|
|
962
|
-
name: "Default",
|
|
963
|
-
description: "Balanced general-purpose mode; use when no special token/coverage trade-off is needed",
|
|
964
|
-
prompt: "",
|
|
965
|
-
tags: ["general", "balanced"]
|
|
966
|
-
},
|
|
967
|
-
{
|
|
968
|
-
id: "brief",
|
|
969
|
-
name: "Brief",
|
|
970
|
-
description: "Ultra-compact responses for low-context, high-speed work",
|
|
971
|
-
prompt: modePrompt("brief"),
|
|
972
|
-
tags: ["lite", "fast", "concise", "token-saving"],
|
|
973
|
-
toolPreferences: ["read", "edit", "bash"],
|
|
974
|
-
suggestedSkills: []
|
|
975
|
-
},
|
|
976
|
-
{
|
|
977
|
-
id: "review-lite",
|
|
978
|
-
name: "Review Lite",
|
|
979
|
-
description: "Token-saving code review: changed files only, top correctness/security risks",
|
|
980
|
-
prompt: modePrompt("review-lite"),
|
|
981
|
-
tags: ["lite", "review", "quality", "token-saving"],
|
|
982
|
-
toolPreferences: ["git", "diff", "read", "grep"],
|
|
983
|
-
suggestedSkills: ["bug-hunter", "typescript-strict"]
|
|
984
|
-
},
|
|
985
|
-
{
|
|
986
|
-
id: "audit-lite",
|
|
987
|
-
name: "Audit Lite",
|
|
988
|
-
description: "Token-saving security triage for a small diff or named file",
|
|
989
|
-
prompt: modePrompt("audit-lite"),
|
|
990
|
-
tags: ["lite", "security", "audit", "token-saving"],
|
|
991
|
-
toolPreferences: ["grep", "read", "git"],
|
|
992
|
-
suggestedSkills: ["security-scanner"]
|
|
993
|
-
},
|
|
994
|
-
{
|
|
995
|
-
id: "plan-lite",
|
|
996
|
-
name: "Plan Lite",
|
|
997
|
-
description: "Token-saving planning: 3-6 actionable steps, minimal design debate",
|
|
998
|
-
prompt: modePrompt("plan-lite"),
|
|
999
|
-
tags: ["lite", "planning", "architecture", "token-saving"],
|
|
1000
|
-
toolPreferences: ["tree", "glob", "read", "grep"],
|
|
1001
|
-
suggestedSkills: ["refactor-planner"]
|
|
1002
|
-
},
|
|
1003
|
-
{
|
|
1004
|
-
id: "debug-lite",
|
|
1005
|
-
name: "Debug Lite",
|
|
1006
|
-
description: "Token-saving bug triage: one hypothesis, nearest evidence, narrow check",
|
|
1007
|
-
prompt: modePrompt("debug-lite"),
|
|
1008
|
-
tags: ["lite", "debug", "triage", "token-saving"],
|
|
1009
|
-
toolPreferences: ["read", "grep", "test", "logs"],
|
|
1010
|
-
suggestedSkills: ["bug-hunter"]
|
|
1011
|
-
},
|
|
1012
|
-
{
|
|
1013
|
-
id: "test-lite",
|
|
1014
|
-
name: "Test Lite",
|
|
1015
|
-
description: "Token-saving tests: one focused regression or narrow verification target",
|
|
1016
|
-
prompt: modePrompt("test-lite"),
|
|
1017
|
-
tags: ["lite", "testing", "qa", "token-saving"],
|
|
1018
|
-
toolPreferences: ["test", "read", "grep"],
|
|
1019
|
-
suggestedSkills: ["testing"]
|
|
1020
|
-
},
|
|
1021
|
-
{
|
|
1022
|
-
id: "refactor-lite",
|
|
1023
|
-
name: "Refactor Lite",
|
|
1024
|
-
description: "Token-saving cleanup: small scoped behavior-preserving changes",
|
|
1025
|
-
prompt: modePrompt("refactor-lite"),
|
|
1026
|
-
tags: ["lite", "refactor", "token-saving"],
|
|
1027
|
-
toolPreferences: ["read", "edit", "test"],
|
|
1028
|
-
suggestedSkills: ["typescript-strict"]
|
|
1029
|
-
},
|
|
1030
|
-
{
|
|
1031
|
-
id: "research-lite",
|
|
1032
|
-
name: "Research Lite",
|
|
1033
|
-
description: "Token-saving web research: one search, one authoritative fetch, short answer",
|
|
1034
|
-
prompt: modePrompt("research-lite"),
|
|
1035
|
-
tags: ["lite", "research", "web", "token-saving"],
|
|
1036
|
-
toolPreferences: ["search", "fetch"],
|
|
1037
|
-
suggestedSkills: ["research-web"]
|
|
1038
|
-
},
|
|
1039
|
-
{
|
|
1040
|
-
id: "code-reviewer",
|
|
1041
|
-
name: "Review Deep",
|
|
1042
|
-
description: "Comprehensive code review across contracts, edge cases, lifecycle, errors, concurrency",
|
|
1043
|
-
prompt: modePrompt("code-reviewer"),
|
|
1044
|
-
tags: ["deep", "review", "quality", "security"],
|
|
1045
|
-
toolPreferences: ["read", "grep", "git", "diff", "test"],
|
|
1046
|
-
suggestedSkills: ["bug-hunter", "security-scanner", "typescript-strict", "testing"]
|
|
1047
|
-
},
|
|
1048
|
-
{
|
|
1049
|
-
id: "code-auditor",
|
|
1050
|
-
name: "Audit Deep",
|
|
1051
|
-
description: "Comprehensive security audit with category coverage and exploitability notes",
|
|
1052
|
-
prompt: modePrompt("code-auditor"),
|
|
1053
|
-
tags: ["deep", "security", "audit", "compliance"],
|
|
1054
|
-
toolPreferences: ["grep", "read", "audit", "bash"],
|
|
1055
|
-
suggestedSkills: ["security-scanner", "bug-hunter", "audit-log"]
|
|
1056
|
-
},
|
|
1057
|
-
{
|
|
1058
|
-
id: "architect",
|
|
1059
|
-
name: "Architecture Deep",
|
|
1060
|
-
description: "Comprehensive architecture and cross-module contract analysis",
|
|
1061
|
-
prompt: modePrompt("architect"),
|
|
1062
|
-
tags: ["deep", "architecture", "design", "scalability"],
|
|
1063
|
-
toolPreferences: ["read", "glob", "tree", "diff"],
|
|
1064
|
-
suggestedSkills: ["api-design", "refactor-planner", "node-modern", "docker-deploy"]
|
|
1065
|
-
},
|
|
1066
|
-
{
|
|
1067
|
-
id: "debugger",
|
|
1068
|
-
name: "Debug Deep",
|
|
1069
|
-
description: "Comprehensive root-cause analysis with traces, logs, assumptions, and verification",
|
|
1070
|
-
prompt: modePrompt("debugger"),
|
|
1071
|
-
tags: ["deep", "debug", "investigation", "error-resolution"],
|
|
1072
|
-
toolPreferences: ["read", "grep", "bash", "logs", "test"],
|
|
1073
|
-
suggestedSkills: ["bug-hunter", "audit-log", "observability"]
|
|
1074
|
-
},
|
|
1075
|
-
{
|
|
1076
|
-
id: "tester",
|
|
1077
|
-
name: "Test Deep",
|
|
1078
|
-
description: "Comprehensive QA mode for coverage, boundaries, isolation, and integration gaps",
|
|
1079
|
-
prompt: modePrompt("tester"),
|
|
1080
|
-
tags: ["deep", "testing", "qa", "quality"],
|
|
1081
|
-
toolPreferences: ["read", "grep", "test", "bash"],
|
|
1082
|
-
suggestedSkills: ["testing", "bug-hunter", "typescript-strict"]
|
|
1083
|
-
},
|
|
1084
|
-
{
|
|
1085
|
-
id: "devops",
|
|
1086
|
-
name: "DevOps Deep",
|
|
1087
|
-
description: "Comprehensive infrastructure, deployment, observability, and operations review",
|
|
1088
|
-
prompt: modePrompt("devops"),
|
|
1089
|
-
tags: ["deep", "devops", "infrastructure", "operations"],
|
|
1090
|
-
toolPreferences: ["read", "bash", "grep", "logs", "git"],
|
|
1091
|
-
suggestedSkills: ["docker-deploy", "observability", "security-scanner"]
|
|
1092
|
-
},
|
|
1093
|
-
{
|
|
1094
|
-
id: "refactorer",
|
|
1095
|
-
name: "Refactor Deep",
|
|
1096
|
-
description: "Comprehensive modernization/refactor mode with contracts and verification discipline",
|
|
1097
|
-
prompt: modePrompt("refactorer"),
|
|
1098
|
-
tags: ["deep", "refactor", "modernization", "improvement"],
|
|
1099
|
-
toolPreferences: ["read", "edit", "test", "git", "grep"],
|
|
1100
|
-
suggestedSkills: ["refactor-planner", "typescript-strict", "node-modern", "testing"]
|
|
1101
|
-
},
|
|
1102
|
-
{
|
|
1103
|
-
id: "ui-design",
|
|
1104
|
-
name: "UI Design Deep",
|
|
1105
|
-
description: "Comprehensive design-first frontend/mobile UI work with kit, tokens, and accessibility",
|
|
1106
|
-
prompt: modePrompt("ui-design"),
|
|
1107
|
-
tags: ["deep", "ui", "frontend", "mobile", "design"],
|
|
1108
|
-
toolPreferences: ["design", "write", "edit", "read", "scaffold"],
|
|
1109
|
-
suggestedSkills: ["react-modern"]
|
|
1110
|
-
},
|
|
1111
|
-
{
|
|
1112
|
-
id: "teach",
|
|
1113
|
-
name: "Teach Deep",
|
|
1114
|
-
description: "Mentor mode with explanations, mental models, trade-offs, and takeaways",
|
|
1115
|
-
prompt: modePrompt("teach"),
|
|
1116
|
-
tags: ["deep", "teaching", "mentor", "learning"],
|
|
1117
|
-
toolPreferences: ["read", "edit", "explain"],
|
|
1118
|
-
suggestedSkills: ["prompt-engineering", "skill-creator", "node-modern", "typescript-strict"]
|
|
1119
|
-
},
|
|
1120
|
-
{
|
|
1121
|
-
id: "research-web",
|
|
1122
|
-
name: "Research Deep",
|
|
1123
|
-
description: "Comprehensive current-data research with cross-checking and reusable findings",
|
|
1124
|
-
prompt: modePrompt("research-web"),
|
|
1125
|
-
tags: ["deep", "research", "web", "current-data", "up-to-date"],
|
|
1126
|
-
toolPreferences: ["search", "fetch", "context_manager"],
|
|
1127
|
-
suggestedSkills: ["research-web", "tech-stack", "node-modern", "security-scanner", "react-modern"]
|
|
1128
|
-
}
|
|
1129
|
-
];
|
|
1130
|
-
|
|
1131
|
-
// src/models/mode-store.ts
|
|
455
|
+
import * as path2 from "node:path";
|
|
1132
456
|
var DefaultModeStore = class {
|
|
1133
457
|
activeModeId = null;
|
|
1134
458
|
modes;
|
|
@@ -1175,7 +499,7 @@ var DefaultModeStore = class {
|
|
|
1175
499
|
}
|
|
1176
500
|
async loadActiveMode() {
|
|
1177
501
|
try {
|
|
1178
|
-
const configPath =
|
|
502
|
+
const configPath = path2.join(this.configDir, "mode.json");
|
|
1179
503
|
const content = await fs2.readFile(configPath, "utf8");
|
|
1180
504
|
const data = JSON.parse(content);
|
|
1181
505
|
this.activeModeId = data.activeMode ?? null;
|
|
@@ -1186,7 +510,7 @@ var DefaultModeStore = class {
|
|
|
1186
510
|
async saveActiveMode() {
|
|
1187
511
|
try {
|
|
1188
512
|
await fs2.mkdir(this.configDir, { recursive: true });
|
|
1189
|
-
const configPath =
|
|
513
|
+
const configPath = path2.join(this.configDir, "mode.json");
|
|
1190
514
|
await atomicWrite(
|
|
1191
515
|
configPath,
|
|
1192
516
|
JSON.stringify({ activeMode: this.activeModeId }, null, 2)
|
|
@@ -1201,11 +525,11 @@ async function loadProjectModes(modesDir) {
|
|
|
1201
525
|
const entries = await fs2.readdir(modesDir);
|
|
1202
526
|
for (const entry of entries) {
|
|
1203
527
|
if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
|
|
1204
|
-
const filePath =
|
|
1205
|
-
const
|
|
1206
|
-
if (!
|
|
528
|
+
const filePath = path2.join(modesDir, entry);
|
|
529
|
+
const stat2 = await fs2.stat(filePath);
|
|
530
|
+
if (!stat2.isFile()) continue;
|
|
1207
531
|
const content = await fs2.readFile(filePath, "utf8");
|
|
1208
|
-
const id =
|
|
532
|
+
const id = path2.basename(entry, path2.extname(entry));
|
|
1209
533
|
modes.push({
|
|
1210
534
|
id,
|
|
1211
535
|
name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
@@ -1221,7 +545,7 @@ async function loadProjectModes(modesDir) {
|
|
|
1221
545
|
async function loadUserModes(modesDir) {
|
|
1222
546
|
const modes = [];
|
|
1223
547
|
try {
|
|
1224
|
-
const manifestPath =
|
|
548
|
+
const manifestPath = path2.join(modesDir, "modes.json");
|
|
1225
549
|
const content = await fs2.readFile(manifestPath, "utf8");
|
|
1226
550
|
const manifest = JSON.parse(content);
|
|
1227
551
|
for (const mode of manifest.modes) {
|
|
@@ -1232,394 +556,6 @@ async function loadUserModes(modesDir) {
|
|
|
1232
556
|
return modes;
|
|
1233
557
|
}
|
|
1234
558
|
|
|
1235
|
-
// src/utils/expect-defined.ts
|
|
1236
|
-
function expectDefined(value, label) {
|
|
1237
|
-
if (value === null || value === void 0) {
|
|
1238
|
-
const err = new Error(label ? `Expected ${label} to be defined` : "Expected value to be defined");
|
|
1239
|
-
err.name = "ExpectDefinedError";
|
|
1240
|
-
throw err;
|
|
1241
|
-
}
|
|
1242
|
-
return value;
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
// src/utils/token-estimate.ts
|
|
1246
|
-
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
1247
|
-
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
1248
|
-
var _estimateCacheOrder = [];
|
|
1249
|
-
var ESTIMATE_CACHE_MAX_SIZE = 5e4;
|
|
1250
|
-
function estimateCacheKey(source) {
|
|
1251
|
-
let h = 5381;
|
|
1252
|
-
for (let i = 0; i < source.length; i++) {
|
|
1253
|
-
h = (h << 5) + h + source.charCodeAt(i) | 0;
|
|
1254
|
-
}
|
|
1255
|
-
return `${source.length}:${h >>> 0}`;
|
|
1256
|
-
}
|
|
1257
|
-
function getCachedEstimate(source, compute) {
|
|
1258
|
-
const key = estimateCacheKey(source);
|
|
1259
|
-
const existing = ESTIMATE_CACHE.get(key);
|
|
1260
|
-
if (existing !== void 0) return existing;
|
|
1261
|
-
if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
|
|
1262
|
-
while (ESTIMATE_CACHE.size > Math.floor(ESTIMATE_CACHE_MAX_SIZE / 2)) {
|
|
1263
|
-
const oldest = _estimateCacheOrder.shift();
|
|
1264
|
-
if (oldest !== void 0) ESTIMATE_CACHE.delete(oldest);
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
const estimate = compute(source);
|
|
1268
|
-
ESTIMATE_CACHE.set(key, estimate);
|
|
1269
|
-
_estimateCacheOrder.push(key);
|
|
1270
|
-
return estimate;
|
|
1271
|
-
}
|
|
1272
|
-
function estimateToolInputTokens(input) {
|
|
1273
|
-
if (typeof input === "string") return RoughTokenEstimate(input);
|
|
1274
|
-
if (input === null || typeof input !== "object") {
|
|
1275
|
-
return RoughTokenEstimate(String(input));
|
|
1276
|
-
}
|
|
1277
|
-
return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));
|
|
1278
|
-
}
|
|
1279
|
-
function estimateToolResultTokens(content) {
|
|
1280
|
-
if (typeof content === "string") return RoughTokenEstimate(content);
|
|
1281
|
-
return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));
|
|
1282
|
-
}
|
|
1283
|
-
function estimateTextTokens(text) {
|
|
1284
|
-
return RoughTokenEstimate(text);
|
|
1285
|
-
}
|
|
1286
|
-
function computeMessageTokens(msg) {
|
|
1287
|
-
if (typeof msg.content === "string") return estimateTextTokens(msg.content);
|
|
1288
|
-
let total = 0;
|
|
1289
|
-
for (const b of msg.content) {
|
|
1290
|
-
if (b.type === "text") total += estimateTextTokens(b.text);
|
|
1291
|
-
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
1292
|
-
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
1293
|
-
else total += RoughTokenEstimate(JSON.stringify(b));
|
|
1294
|
-
}
|
|
1295
|
-
return total;
|
|
1296
|
-
}
|
|
1297
|
-
function estimateMessageTokens(messages) {
|
|
1298
|
-
let total = 0;
|
|
1299
|
-
for (const m of messages) {
|
|
1300
|
-
if (typeof m._estTokens === "number" && m._estTokens > 0) {
|
|
1301
|
-
total += m._estTokens;
|
|
1302
|
-
continue;
|
|
1303
|
-
}
|
|
1304
|
-
total += computeMessageTokens(m);
|
|
1305
|
-
}
|
|
1306
|
-
return total;
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
// src/types/blocks.ts
|
|
1310
|
-
function isTextBlock(b) {
|
|
1311
|
-
return b.type === "text";
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
// src/utils/compaction-preview.ts
|
|
1315
|
-
var PATH_HINT_PATTERN = /(?:(?:[A-Za-z]:)?[./\\]?[\w@.-]+(?:[\\/][\w@(). -]+)+\.[A-Za-z0-9]{1,12})/g;
|
|
1316
|
-
var PATH_BACKSLASH_PATTERN = /\\/g;
|
|
1317
|
-
var PATH_TRIM_PATTERN = /^["'`]+|["'`),;:]+$/g;
|
|
1318
|
-
var ERROR_LINE_PATTERN = /\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm)\b/i;
|
|
1319
|
-
var NEWLINE_SPLIT_PATTERN = /\r?\n/;
|
|
1320
|
-
var WHITESPACE_COLLAPSE_PATTERN = /\s+/g;
|
|
1321
|
-
function buildCompactionPreview(message, maxChars = 600) {
|
|
1322
|
-
if (maxChars <= 0) return "";
|
|
1323
|
-
if (typeof message.content === "string") {
|
|
1324
|
-
return truncatePreview(message.content, maxChars);
|
|
1325
|
-
}
|
|
1326
|
-
const parts = [];
|
|
1327
|
-
for (const block of message.content) {
|
|
1328
|
-
if (isTextBlock(block)) {
|
|
1329
|
-
const text = truncatePreview(block.text, maxChars);
|
|
1330
|
-
if (text) parts.push(text);
|
|
1331
|
-
continue;
|
|
1332
|
-
}
|
|
1333
|
-
if (block.type === "tool_use") {
|
|
1334
|
-
const input = truncatePreview(safePreviewString(block.input), 220);
|
|
1335
|
-
parts.push(`[tool_use: ${block.name}${input ? `; input=${input}` : ""}]`);
|
|
1336
|
-
continue;
|
|
1337
|
-
}
|
|
1338
|
-
if (block.type === "tool_result") {
|
|
1339
|
-
const raw = safePreviewString(block.content);
|
|
1340
|
-
const details = [block.is_error ? "error" : "ok"];
|
|
1341
|
-
if (block.name) details.push(`tool=${block.name}`);
|
|
1342
|
-
const files = extractPathHints(raw).slice(0, 5);
|
|
1343
|
-
if (files.length > 0) details.push(`files=${files.join(", ")}`);
|
|
1344
|
-
const error = firstErrorLine(raw);
|
|
1345
|
-
if (error) details.push(`error=${error}`);
|
|
1346
|
-
const sample = truncatePreview(raw, 260);
|
|
1347
|
-
if (sample) details.push(`sample=${sample}`);
|
|
1348
|
-
parts.push(`[tool_result: ${details.join("; ")}]`);
|
|
1349
|
-
continue;
|
|
1350
|
-
}
|
|
1351
|
-
parts.push(`[${block.type}]`);
|
|
1352
|
-
}
|
|
1353
|
-
return truncatePreview(parts.join(" "), maxChars);
|
|
1354
|
-
}
|
|
1355
|
-
function safePreviewString(value) {
|
|
1356
|
-
if (typeof value === "string") return value;
|
|
1357
|
-
try {
|
|
1358
|
-
return JSON.stringify(value);
|
|
1359
|
-
} catch {
|
|
1360
|
-
return String(value);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
function truncatePreview(value, maxChars) {
|
|
1364
|
-
const compact = value.replace(WHITESPACE_COLLAPSE_PATTERN, " ").trim();
|
|
1365
|
-
if (compact.length <= maxChars) return compact;
|
|
1366
|
-
return `${compact.slice(0, Math.max(0, maxChars - 1))}\u2026`;
|
|
1367
|
-
}
|
|
1368
|
-
function extractPathHints(content) {
|
|
1369
|
-
const text = safePreviewString(content);
|
|
1370
|
-
const out = /* @__PURE__ */ new Set();
|
|
1371
|
-
for (const match of text.matchAll(PATH_HINT_PATTERN)) {
|
|
1372
|
-
const clean = match[0]?.replace(PATH_BACKSLASH_PATTERN, "/").replace(PATH_TRIM_PATTERN, "");
|
|
1373
|
-
if (clean) out.add(clean);
|
|
1374
|
-
}
|
|
1375
|
-
return [...out];
|
|
1376
|
-
}
|
|
1377
|
-
function firstErrorLine(content) {
|
|
1378
|
-
const text = safePreviewString(content);
|
|
1379
|
-
for (const line of text.split(NEWLINE_SPLIT_PATTERN)) {
|
|
1380
|
-
if (!ERROR_LINE_PATTERN.test(line)) continue;
|
|
1381
|
-
const trimmed = line.replace(WHITESPACE_COLLAPSE_PATTERN, " ").trim();
|
|
1382
|
-
if (trimmed) return truncatePreview(trimmed, 220);
|
|
1383
|
-
}
|
|
1384
|
-
return void 0;
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
// src/utils/instruction-file.ts
|
|
1388
|
-
import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
1389
|
-
import * as path5 from "node:path";
|
|
1390
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1391
|
-
var textCache = /* @__PURE__ */ new Map();
|
|
1392
|
-
var rootCandidates;
|
|
1393
|
-
function readBundledInstructionText(relativePath) {
|
|
1394
|
-
const cached = textCache.get(relativePath);
|
|
1395
|
-
if (cached !== void 0) return cached;
|
|
1396
|
-
let resolved = "";
|
|
1397
|
-
for (const root of instructionRootCandidates()) {
|
|
1398
|
-
try {
|
|
1399
|
-
resolved = readFileSync2(path5.join(root, relativePath), "utf8").trimEnd();
|
|
1400
|
-
break;
|
|
1401
|
-
} catch {
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
textCache.set(relativePath, resolved);
|
|
1405
|
-
return resolved;
|
|
1406
|
-
}
|
|
1407
|
-
function instructionRootCandidates() {
|
|
1408
|
-
if (rootCandidates !== void 0) return rootCandidates;
|
|
1409
|
-
const here = path5.dirname(fileURLToPath2(import.meta.url));
|
|
1410
|
-
const candidates = [
|
|
1411
|
-
path5.resolve(here, "../../instructions"),
|
|
1412
|
-
path5.resolve(here, "../instructions"),
|
|
1413
|
-
path5.resolve(here, "instructions")
|
|
1414
|
-
];
|
|
1415
|
-
rootCandidates = candidates.sort((a, b) => Number(!isDirectory2(a)) - Number(!isDirectory2(b)));
|
|
1416
|
-
return rootCandidates;
|
|
1417
|
-
}
|
|
1418
|
-
function isDirectory2(candidate) {
|
|
1419
|
-
try {
|
|
1420
|
-
return statSync2(candidate).isDirectory();
|
|
1421
|
-
} catch {
|
|
1422
|
-
return false;
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
// src/models/llm-selector.ts
|
|
1427
|
-
var DEFAULT_SYSTEM_PROMPT = readBundledInstructionText("llm/llm-selector.md");
|
|
1428
|
-
function formatMessages(messages, maxTokens = 2048) {
|
|
1429
|
-
const previewChars = Math.max(
|
|
1430
|
-
120,
|
|
1431
|
-
Math.min(600, Math.floor(maxTokens * 3.2 / Math.max(1, messages.length)))
|
|
1432
|
-
);
|
|
1433
|
-
const lines = [];
|
|
1434
|
-
for (let i = 0; i < messages.length; i++) {
|
|
1435
|
-
const m = expectDefined(messages[i]);
|
|
1436
|
-
const role = m.role.padEnd(10, " ");
|
|
1437
|
-
const line = `[${i}][${role}]: ${buildCompactionPreview(m, previewChars)}`;
|
|
1438
|
-
lines.push({ index: i, text: line, tokens: estimateTextTokens(line) });
|
|
1439
|
-
}
|
|
1440
|
-
const total = lines.reduce((sum, line) => sum + line.tokens, 0);
|
|
1441
|
-
if (total <= maxTokens) return lines.map((line) => line.text).join("\n");
|
|
1442
|
-
const recentCount = Math.min(8, lines.length);
|
|
1443
|
-
const recentStart = lines.length - recentCount;
|
|
1444
|
-
const tail = [];
|
|
1445
|
-
let usedTokens = 0;
|
|
1446
|
-
for (let i = lines.length - 1; i >= recentStart; i--) {
|
|
1447
|
-
const line = expectDefined(lines[i]);
|
|
1448
|
-
if (tail.length >= 2 && usedTokens + line.tokens > Math.floor(maxTokens * 0.45)) continue;
|
|
1449
|
-
tail.unshift(line);
|
|
1450
|
-
usedTokens += line.tokens;
|
|
1451
|
-
}
|
|
1452
|
-
const head = [];
|
|
1453
|
-
for (let i = 0; i < recentStart; i++) {
|
|
1454
|
-
const line = expectDefined(lines[i]);
|
|
1455
|
-
if (usedTokens + line.tokens > maxTokens) break;
|
|
1456
|
-
head.push(line);
|
|
1457
|
-
usedTokens += line.tokens;
|
|
1458
|
-
}
|
|
1459
|
-
const omitted = Math.max(0, recentStart - head.length);
|
|
1460
|
-
const marker = omitted > 0 ? [`[\u2026 ${omitted} middle message(s) omitted from selector preview \u2026]`] : [];
|
|
1461
|
-
return [...head.map((line) => line.text), ...marker, ...tail.map((line) => line.text)].join("\n");
|
|
1462
|
-
}
|
|
1463
|
-
var LLMSelector = class {
|
|
1464
|
-
provider;
|
|
1465
|
-
model;
|
|
1466
|
-
maxContextTokens;
|
|
1467
|
-
systemPrompt;
|
|
1468
|
-
maxOutputTokens;
|
|
1469
|
-
oneShotOrchestrator;
|
|
1470
|
-
logger;
|
|
1471
|
-
constructor(opts) {
|
|
1472
|
-
this.provider = opts.provider;
|
|
1473
|
-
this.model = opts.model ?? "unknown";
|
|
1474
|
-
this.logger = opts.logger ?? noOpLogger;
|
|
1475
|
-
if (this.model === "unknown" && (process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1")) {
|
|
1476
|
-
this.logger.warn(
|
|
1477
|
-
"[LLMSelector] model not set \u2014 selector will use the provider default. Set `model` explicitly in LLMSelectorOptions to silence this warning."
|
|
1478
|
-
);
|
|
1479
|
-
}
|
|
1480
|
-
this.maxContextTokens = opts.maxContextTokens ?? 4e4;
|
|
1481
|
-
this.systemPrompt = opts.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
1482
|
-
this.maxOutputTokens = opts.maxOutputTokens ?? 1024;
|
|
1483
|
-
this.oneShotOrchestrator = opts.oneShotOrchestrator;
|
|
1484
|
-
}
|
|
1485
|
-
async select(messages, maxToKeep) {
|
|
1486
|
-
const effectiveBudget = Math.min(maxToKeep, this.maxContextTokens);
|
|
1487
|
-
const totalTokens = estimateMessageTokens(messages);
|
|
1488
|
-
const systemText = `${this.systemPrompt}
|
|
1489
|
-
|
|
1490
|
-
Conversation (${messages.length} messages, ~${totalTokens} tokens, budget: ${effectiveBudget}):
|
|
1491
|
-
`;
|
|
1492
|
-
const systemTokens = estimateTextTokens(systemText);
|
|
1493
|
-
const historyBudget = Math.max(512, effectiveBudget - systemTokens - this.maxOutputTokens);
|
|
1494
|
-
const historyText = formatMessages(messages, historyBudget);
|
|
1495
|
-
const budgetInstruction = totalTokens > effectiveBudget ? `
|
|
1496
|
-
|
|
1497
|
-
IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiveBudget}). You MUST collapse enough to fit. Prefer collapsing older/lower-importance ranges.` : "";
|
|
1498
|
-
const req = {
|
|
1499
|
-
model: this.model,
|
|
1500
|
-
system: [{ type: "text", text: systemText + budgetInstruction }],
|
|
1501
|
-
messages: [{ role: "user", content: historyText }],
|
|
1502
|
-
maxTokens: this.maxOutputTokens
|
|
1503
|
-
};
|
|
1504
|
-
let raw;
|
|
1505
|
-
const ac = new AbortController();
|
|
1506
|
-
try {
|
|
1507
|
-
if (this.oneShotOrchestrator) {
|
|
1508
|
-
const result = await this.oneShotOrchestrator.call({
|
|
1509
|
-
system: systemText + budgetInstruction,
|
|
1510
|
-
userPrompt: historyText,
|
|
1511
|
-
maxTokens: this.maxOutputTokens,
|
|
1512
|
-
timeoutMs: 3e4
|
|
1513
|
-
});
|
|
1514
|
-
if (result.error) throw new Error(result.error);
|
|
1515
|
-
raw = result.text;
|
|
1516
|
-
} else {
|
|
1517
|
-
const timeoutSignal = AbortSignal.timeout(3e4);
|
|
1518
|
-
const res = await this.provider.complete(req, {
|
|
1519
|
-
signal: AbortSignal.any([ac.signal, timeoutSignal])
|
|
1520
|
-
});
|
|
1521
|
-
const textBlocks = res.content.filter(isTextBlock);
|
|
1522
|
-
raw = textBlocks.map((b) => b.text).join("\n").trim();
|
|
1523
|
-
}
|
|
1524
|
-
} catch (err) {
|
|
1525
|
-
if (err instanceof Error) {
|
|
1526
|
-
this.logger.warn(`selector call failed, using recency fallback: ${err.message}`, {
|
|
1527
|
-
event: "llm_selector.call_failed"
|
|
1528
|
-
});
|
|
1529
|
-
}
|
|
1530
|
-
return this.fallbackSelect(messages, effectiveBudget);
|
|
1531
|
-
} finally {
|
|
1532
|
-
ac.abort();
|
|
1533
|
-
}
|
|
1534
|
-
return this.parseSelectorOutput(raw, messages);
|
|
1535
|
-
}
|
|
1536
|
-
fallbackSelect(messages, budget) {
|
|
1537
|
-
const toKeep = [];
|
|
1538
|
-
const toCollapse = [];
|
|
1539
|
-
let tokenCount = 0;
|
|
1540
|
-
let startIdx = 0;
|
|
1541
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1542
|
-
const m = expectDefined(messages[i]);
|
|
1543
|
-
const cost = estimateMessageTokens([m]);
|
|
1544
|
-
if (tokenCount + cost <= budget) {
|
|
1545
|
-
tokenCount += cost;
|
|
1546
|
-
} else {
|
|
1547
|
-
startIdx = i + 1;
|
|
1548
|
-
break;
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
if (startIdx > 0) {
|
|
1552
|
-
toCollapse.push({ from: 0, to: startIdx - 1 });
|
|
1553
|
-
}
|
|
1554
|
-
toKeep.push({ from: startIdx, to: messages.length - 1, importance: "high" });
|
|
1555
|
-
return {
|
|
1556
|
-
kept: toKeep,
|
|
1557
|
-
collapsed: toCollapse,
|
|
1558
|
-
reasoning: `Fallback: kept last ${messages.length - startIdx} messages within ${budget} token budget`
|
|
1559
|
-
};
|
|
1560
|
-
}
|
|
1561
|
-
/**
|
|
1562
|
-
* Parse and validate the raw LLM output into a SelectorResult.
|
|
1563
|
-
* Falls back to recency-based selection if the LLM output is malformed,
|
|
1564
|
-
* out-of-bounds, or internally inconsistent.
|
|
1565
|
-
*/
|
|
1566
|
-
parseSelectorOutput(raw, messages) {
|
|
1567
|
-
const messageCount = messages.length;
|
|
1568
|
-
if (messageCount === 0) {
|
|
1569
|
-
return { kept: [], collapsed: [], reasoning: "empty session" };
|
|
1570
|
-
}
|
|
1571
|
-
const jsonStart = raw.indexOf("{");
|
|
1572
|
-
const jsonEnd = raw.lastIndexOf("}");
|
|
1573
|
-
if (jsonStart === -1 || jsonEnd === -1) {
|
|
1574
|
-
return this.fallbackSelect(messages, this.maxContextTokens);
|
|
1575
|
-
}
|
|
1576
|
-
let parsed;
|
|
1577
|
-
try {
|
|
1578
|
-
parsed = JSON.parse(raw.slice(jsonStart, jsonEnd + 1));
|
|
1579
|
-
} catch {
|
|
1580
|
-
return this.fallbackSelect(messages, this.maxContextTokens);
|
|
1581
|
-
}
|
|
1582
|
-
const obj = parsed;
|
|
1583
|
-
const keptRaw = obj.kept ?? [];
|
|
1584
|
-
const collapsedRaw = obj.collapsed ?? [];
|
|
1585
|
-
const kept = [];
|
|
1586
|
-
for (const k of keptRaw) {
|
|
1587
|
-
if (typeof k.from !== "number" || typeof k.to !== "number" || k.from < 0 || k.to >= messageCount || k.from > k.to) {
|
|
1588
|
-
return this.fallbackSelect(messages, this.maxContextTokens);
|
|
1589
|
-
}
|
|
1590
|
-
kept.push({
|
|
1591
|
-
from: k.from,
|
|
1592
|
-
to: k.to,
|
|
1593
|
-
importance: k.importance ?? "medium"
|
|
1594
|
-
});
|
|
1595
|
-
}
|
|
1596
|
-
const collapsed = [];
|
|
1597
|
-
for (const c of collapsedRaw) {
|
|
1598
|
-
if (typeof c.from !== "number" || typeof c.to !== "number" || c.from < 0 || c.to >= messageCount || c.from > c.to) {
|
|
1599
|
-
return this.fallbackSelect(messages, this.maxContextTokens);
|
|
1600
|
-
}
|
|
1601
|
-
collapsed.push({ from: c.from, to: c.to, summary: c.summary });
|
|
1602
|
-
}
|
|
1603
|
-
const allRanges = [...kept, ...collapsed];
|
|
1604
|
-
for (let i = 0; i < allRanges.length; i++) {
|
|
1605
|
-
const a = allRanges[i];
|
|
1606
|
-
if (!a) continue;
|
|
1607
|
-
for (let j = i + 1; j < allRanges.length; j++) {
|
|
1608
|
-
const b = allRanges[j];
|
|
1609
|
-
if (!b) continue;
|
|
1610
|
-
if (a.from <= b.to && a.to >= b.from) {
|
|
1611
|
-
return this.fallbackSelect(messages, this.maxContextTokens);
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
return {
|
|
1616
|
-
kept,
|
|
1617
|
-
collapsed,
|
|
1618
|
-
reasoning: typeof obj.reasoning === "string" ? obj.reasoning : ""
|
|
1619
|
-
};
|
|
1620
|
-
}
|
|
1621
|
-
};
|
|
1622
|
-
|
|
1623
559
|
// src/models/codex-catalog.ts
|
|
1624
560
|
var CODEX_MODELS = [
|
|
1625
561
|
{
|
|
@@ -1813,19 +749,6 @@ function alibabaTokenPlanModelMeta(id) {
|
|
|
1813
749
|
|
|
1814
750
|
// src/models/models-dev-schema.ts
|
|
1815
751
|
import { z } from "zod";
|
|
1816
|
-
|
|
1817
|
-
// src/utils/deep-merge.ts
|
|
1818
|
-
var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
1819
|
-
"__proto__",
|
|
1820
|
-
"constructor",
|
|
1821
|
-
"prototype",
|
|
1822
|
-
"__defineGetter__",
|
|
1823
|
-
"__defineSetter__",
|
|
1824
|
-
"__lookupGetter__",
|
|
1825
|
-
"__lookupSetter__"
|
|
1826
|
-
]);
|
|
1827
|
-
|
|
1828
|
-
// src/models/models-dev-schema.ts
|
|
1829
752
|
var MODELS_DEV_MODALITY_VALUES = ["text", "image", "audio", "video", "pdf"];
|
|
1830
753
|
var REASONING_EFFORT_LITERALS = [
|
|
1831
754
|
"none",
|