svamp-cli 0.2.204 → 0.2.206

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.
Files changed (26) hide show
  1. package/bin/skills/loop/bin/stop-gate.mjs +84 -3
  2. package/bin/skills/loop/test/test-loop-gate.mjs +65 -12
  3. package/dist/{agentCommands-D92DfTGP.mjs → agentCommands-akIoZemu.mjs} +5 -5
  4. package/dist/{auth-BvO7QtaP.mjs → auth-Dzf0cndq.mjs} +1 -1
  5. package/dist/cli.mjs +63 -61
  6. package/dist/{commands-CHqMRENk.mjs → commands-8Rd6p4B4.mjs} +1 -1
  7. package/dist/{commands-wtMi-xJY.mjs → commands-BPhgtKi2.mjs} +2 -2
  8. package/dist/{commands-u68ODpBt.mjs → commands-BRmWVCNf.mjs} +34 -11
  9. package/dist/{commands-BduSUevP.mjs → commands-BfakuMvA.mjs} +2 -2
  10. package/dist/{commands-5AerKSMm.mjs → commands-Dp7BFqYl.mjs} +1 -1
  11. package/dist/{commands-DwWuWW99.mjs → commands-OMKB5-oS.mjs} +29 -11
  12. package/dist/{commands-Dyy_1c0I.mjs → commands-V6wL2BtU.mjs} +5 -5
  13. package/dist/{fleet-CFe5Kt6k.mjs → fleet-Ci190s0w.mjs} +1 -1
  14. package/dist/{frpc-DMV0fZBM.mjs → frpc-H52IJYcb.mjs} +1 -1
  15. package/dist/{headlessCli-COLHIWk8.mjs → headlessCli-tLrOER4U.mjs} +2 -2
  16. package/dist/index.mjs +1 -1
  17. package/dist/{package-CqGr9_jQ.mjs → package-MNPXOWOP.mjs} +1 -1
  18. package/dist/{rpc-CoNLjY5x.mjs → rpc-BHUHF_Mv.mjs} +10 -3
  19. package/dist/{rpc-DBpyHJUs.mjs → rpc-CFrVMETs.mjs} +1 -1
  20. package/dist/{run-CR8y6yQ7.mjs → run-C4OsABqC.mjs} +1 -1
  21. package/dist/{run-BfgkuBEg.mjs → run-C9ZeF2iG.mjs} +101 -18
  22. package/dist/{scheduler-C1_HeBdn.mjs → scheduler-ErjEISCv.mjs} +1 -1
  23. package/dist/{serveCommands-d3t4RkrF.mjs → serveCommands-feGogIWC.mjs} +5 -5
  24. package/dist/{serveManager-DA9elK4m.mjs → serveManager-Be-R7R8_.mjs} +2 -2
  25. package/dist/{sideband-DNLuglX3.mjs → sideband-cU4KDceu.mjs} +1 -1
  26. package/package.json +1 -1
@@ -155,17 +155,70 @@ if (done) {
155
155
  const inboxBlocks = Number(state.inbox_blocks) || 0;
156
156
  if (cfg.inbox_guard !== false && inboxBlocks < INBOX_BLOCK_CAP) {
157
157
  let pending = 0;
158
+ let pendingIds = [];
158
159
  try {
159
160
  const arr = readJSON(join(LOOP_DIR, '..', 'inbox.json'), []);
160
161
  const msgs = Array.isArray(arr) ? arr : (Array.isArray(arr?.messages) ? arr.messages : []);
161
- pending = msgs.filter((m) => m && !m.handled && !m.read).length;
162
+ const pendingMsgs = msgs.filter((m) => m && !m.handled && !m.read);
163
+ pending = pendingMsgs.length;
164
+ pendingIds = pendingMsgs.map((m) => m.messageId).filter(Boolean);
162
165
  } catch { pending = 0; } // fail-open: no/corrupt inbox file → never block
163
166
  if (pending > 0) {
167
+ // #0146: this guard already surfaced these messages — record them in the shared hint ledger so
168
+ // the non-urgent settle hint below won't re-surface the same ids on the next iteration.
169
+ try {
170
+ const HINTED = join(LOOP_DIR, 'hinted-inbox.json');
171
+ const hintedRaw = readJSON(HINTED, []);
172
+ const merged = new Set([...(Array.isArray(hintedRaw) ? hintedRaw : []), ...pendingIds]);
173
+ writeJSONAtomic(HINTED, [...merged]);
174
+ } catch {}
164
175
  writeJSONAtomic(STATE, { ...state, inbox_blocks: inboxBlocks + 1, last_oracle: oracleDetail });
165
176
  appendHistory({ ts: now, iteration: iterNum, decision: 'inbox-block', pending, detail: oracleDetail });
166
177
  block(`The loop's exit conditions are met (oracle empty + evaluator done), but you have ${pending} UNHANDLED inbox message(s). Handle them first — read/reply/triage/merge each (\`svamp session inbox list\`), then finish your turn. (This guard fires at most once per loop, then allows stop, so a flood can't trap the loop.)`);
167
178
  }
168
179
  }
180
+ // #0146: NON-URGENT inbox hint at the settle point. We only reach here when the gate would
181
+ // OTHERWISE ALLOW the end (oracle pass + evaluator done) AND the urgent/unhandled guard above did
182
+ // not block. Surface UNREAD, NON-URGENT, NOT-awaiting-reply inbox messages the agent hasn't been
183
+ // hinted about yet so they don't pile up unseen while the loop grinds. Reads the same durable
184
+ // inbox.json (sibling of the loop dir) — no subprocess/daemon round-trip. Hint-once-per-message:
185
+ // we record the hinted ids in <LOOP_DIR>/hinted-inbox.json and BLOCK ONCE so the agent sees the
186
+ // hint this turn; the next settle won't re-hint the same ids → it allows the end (non-blocking
187
+ // thereafter). Fully defensive: any error → fall through to the normal allow (never trap the loop).
188
+ // Disable with `inbox_hint: false` in loop.config.json.
189
+ if (cfg.inbox_hint !== false) {
190
+ try {
191
+ const HINTED = join(LOOP_DIR, 'hinted-inbox.json');
192
+ const arr = readJSON(join(LOOP_DIR, '..', 'inbox.json'), []);
193
+ const msgs = Array.isArray(arr) ? arr : (Array.isArray(arr?.messages) ? arr.messages : []);
194
+ const hintedRaw = readJSON(HINTED, []);
195
+ const hintedIds = new Set(Array.isArray(hintedRaw) ? hintedRaw : []);
196
+ // unread + not-handled-by-agent + NOT urgent + NOT awaiting-reply (those already interrupt the
197
+ // loop) + not yet hinted. `handled` means the agent already consumed the message into a turn, so
198
+ // it's not "unseen" — only genuinely-unseen non-urgent mail deserves a settle-point nudge.
199
+ const fresh = msgs.filter((m) => m
200
+ && m.read !== true
201
+ && m.handled !== true
202
+ && m.urgency !== 'urgent'
203
+ && !(m.channelId && m.correlationId) // awaiting-reply markers
204
+ && m.messageId && !hintedIds.has(m.messageId));
205
+ if (fresh.length > 0) {
206
+ const preview = fresh.slice(0, 3).map((m) => {
207
+ const who = m.from || m.fromSession || 'unknown';
208
+ const subj = m.subject ? ` re "${m.subject}"` : '';
209
+ return `from ${who}${subj}`;
210
+ }).join('; ');
211
+ const more = fresh.length > 3 ? ` (+${fresh.length - 3} more)` : '';
212
+ // Record BEFORE blocking so the next settle won't re-hint these ids (hint-once guarantee).
213
+ for (const m of fresh) hintedIds.add(m.messageId);
214
+ try { writeJSONAtomic(HINTED, [...hintedIds]); } catch {}
215
+ process.stderr.write(`[loop] 📥 ${fresh.length} unread inbox message(s) while you worked: ${preview}${more}\n`);
216
+ writeJSONAtomic(STATE, { ...state, last_oracle: oracleDetail });
217
+ appendHistory({ ts: now, iteration: iterNum, decision: 'inbox-hint', count: fresh.length, detail: preview });
218
+ block(`📥 ${fresh.length} unread inbox message(s) arrived while you worked: ${preview}${more}. Consider reading them (\`svamp session inbox list\`) before settling. (This hint fires once per message — finish your turn again to be re-checked; it won't block on these again.)`);
219
+ }
220
+ } catch { /* fail-open: any inbox-hint error → never block, fall through to allow */ }
221
+ }
169
222
  // #0128: NON-BLOCKING open-crew hint. The loop is about to STOP — surface any active crew children
170
223
  // the lead hasn't merged/closed so an orphaned/forgotten crew is visible at the natural checkpoint.
171
224
  // Purely informational: it NEVER blocks or affects the decision (the loop still stops). Computed
@@ -203,14 +256,42 @@ const giveUp = overIters || overTime || overTokens;
203
256
  const tokenField = maxTokens ? { tokens_used: tokensUsed } : {};
204
257
 
205
258
  if (giveUp) {
206
- // Bounded out: let it stop so we never block forever. (Single state write.)
207
259
  const why = overTokens ? `token budget (${tokensUsed}/${maxTokens} tokens)`
208
260
  : overTime ? `runtime budget (${cfg.budget.max_runtime_sec}s)`
209
261
  : `max_iterations (${max})`;
262
+ const limitName = overTokens ? 'token budget' : overTime ? 'runtime budget' : 'iteration limit';
263
+ // #0156: reaching the cap here means the exit conditions are NOT met (the `done` path above
264
+ // already handled the met case) — i.e. work is STILL REMAINING. Going silently dormant
265
+ // (active:false) makes a STALL indistinguishable from 'completed' (idle-pike's 11h-dormant bug).
266
+ // So block ONCE to make the agent surface the stall LOUDLY + offer extend/resume <options>;
267
+ // then on the next stop allow it (a stall must never trap the loop forever).
268
+ if (!state.stall_hinted) {
269
+ writeJSONAtomic(STATE, { ...state, iteration: nextIter, stall_hinted: true,
270
+ last_iteration_at: now, last_oracle: oracleDetail, ...tokenField });
271
+ appendHistory({ ts: now, iteration: nextIter, decision: 'stall_hint', reason: why, oracle: oraclePass, detail: oracleDetail });
272
+ const suggested = (overIters && max != null) ? Math.max(max * 2, nextIter + 10) : null;
273
+ const sess = SID || '<session>';
274
+ const resumeCmd = suggested != null
275
+ ? `svamp session loop ${sess} --max ${suggested}`
276
+ : `svamp session loop ${sess} --budget <bigger>`;
277
+ const firstLine = String(oracleDetail || 'oracle still failing').split('\n')[0];
278
+ block(
279
+ `⚠️ LOOP STALLED — hit the ${limitName} (${why}) but the exit conditions are NOT met: ${firstLine}. `
280
+ + `Work is still remaining; do NOT keep grinding, and do NOT just stop silently. In ONE final message:\n`
281
+ + `1) Tell the user the loop hit its ${limitName} with work still pending, and briefly why it didn't finish.\n`
282
+ + `2) Present clickable options so they can extend+resume or stop — exactly like:\n`
283
+ + `<options>\n`
284
+ + ` <option>Extend the limit${suggested != null ? ` to ${suggested}` : ''} and resume the loop</option>\n`
285
+ + ` <option>Stop the loop here</option>\n`
286
+ + `</options>\n`
287
+ + `If the user picks an extend option, run: ${resumeCmd} (this reactivates + extends the loop). Then end your turn.`,
288
+ );
289
+ }
290
+ // Already surfaced the stall once → allow stop so it can never trap the loop.
210
291
  writeJSONAtomic(STATE, { ...state, active: false, phase: 'gave_up', iteration: nextIter,
211
292
  completed_at: now, last_oracle: oracleDetail, gave_up_reason: why, ...tokenField });
212
293
  appendHistory({ ts: now, iteration: nextIter, decision: 'gave_up', reason: why, oracle: oraclePass, detail: oracleDetail });
213
- process.stderr.write(`[loop] reached ${why}; allowing stop.\n`);
294
+ process.stderr.write(`[loop] reached ${why}; allowing stop after stall hint.\n`);
214
295
  allow();
215
296
  }
216
297
 
@@ -113,13 +113,21 @@ try {
113
113
  ok(!r.blocked, 'gate allows when evaluator off and oracle passes');
114
114
  }
115
115
 
116
- // ---- Test 7: max_iterations bound -> eventually allows (never infinite) ----
117
- console.log('Test 7: max_iterations prevents infinite blocking');
116
+ // ---- Test 7: max_iterations bound -> STALL hint once, then allows (never infinite) ----
117
+ console.log('Test 7: max_iterations stall hint once, then allows');
118
118
  { const d = newProject({ evaluator: 'off', max: 1 }); dirs.push(d); // oracle keeps failing (answer.txt=TODO)
119
- const r1 = runGate(d); // iteration -> 1, block
120
- const r2 = runGate(d, true); // iteration -> 2 > max -> give up, allow
119
+ const r1 = runGate(d); // iteration -> 1, block (oracle fails)
120
+ const r2 = runGate(d, true); // iteration -> 2 > max -> #0156 STALL: block ONCE with options
121
+ const r3 = runGate(d, true); // already hinted -> give up, allow
121
122
  ok(r1.blocked, 'first failing attempt blocks');
122
- ok(!r2.blocked, 'after exceeding max_iterations the gate allows stop (no infinite loop)');
123
+ // #0156: exceeding the cap with the oracle still failing is a STALL — surface it loudly ONCE
124
+ // (don't go silently dormant), presenting extend/resume <options>.
125
+ ok(r2.blocked, '#0156: exceeding max_iterations blocks ONCE with a stall hint (not silent)');
126
+ ok(/STALL/i.test(r2.reason) && /<options>/.test(r2.reason), '#0156: stall hint explains + presents <options>');
127
+ ok(/loop .* --max/.test(r2.reason), '#0156: stall hint gives the resume+extend command');
128
+ ok(readState(d).stall_hinted === true, '#0156: state records stall_hinted after the hint');
129
+ ok(!r3.blocked, 'after the stall hint the gate allows stop (no infinite loop)');
130
+ ok(readState(d).phase === 'gave_up' && readState(d).active === false, 'finally marked gave_up/inactive');
123
131
  }
124
132
 
125
133
  // ---- Test 8: inactive loop -> no-op allow ----
@@ -152,8 +160,10 @@ try {
152
160
  const cfg = JSON.parse(readFileSync(cfgP, 'utf8')); cfg.budget = { max_runtime_sec: 1 }; writeFileSync(cfgP, JSON.stringify(cfg));
153
161
  const spP = join(d, '.svamp', SID, 'loop', 'loop-state.json');
154
162
  const sp = JSON.parse(readFileSync(spP, 'utf8')); sp.started_at = new Date(Date.now() - 5000).toISOString(); writeFileSync(spP, JSON.stringify(sp));
155
- const r = runGate(d);
156
- ok(!r.blocked, 'gate allows stop once runtime budget is exceeded');
163
+ const r = runGate(d); // #0156: first over-budget hit → stall hint (block once)
164
+ const r2 = runGate(d); // already hinted allow
165
+ ok(r.blocked && /STALL/i.test(r.reason), 'runtime over-budget surfaces a stall hint first (not silent)');
166
+ ok(!r2.blocked, 'gate allows stop once runtime budget is exceeded (after the stall hint)');
157
167
  ok(readState(d).gave_up_reason?.includes('runtime'), 'state records runtime give-up reason');
158
168
  }
159
169
 
@@ -180,8 +190,10 @@ try {
180
190
  JSON.stringify({ message: { usage: { input_tokens: 400, output_tokens: 300 } } }),
181
191
  JSON.stringify({ message: { usage: { input_tokens: 500, output_tokens: 200, cache_read_input_tokens: 50 } } }),
182
192
  ].join('\n')); // total = 1450 > 1000
183
- const r = runGate(d, false, tp);
184
- ok(!r.blocked, 'gate allows stop once token budget is exceeded');
193
+ const r = runGate(d, false, tp); // #0156: first over-budget hit → stall hint
194
+ const r2 = runGate(d, false, tp); // already hinted allow
195
+ ok(r.blocked && /STALL/i.test(r.reason), 'token over-budget surfaces a stall hint first (not silent)');
196
+ ok(!r2.blocked, 'gate allows stop once token budget is exceeded (after the stall hint)');
185
197
  ok(readState(d).gave_up_reason?.includes('token'), 'state records token give-up reason');
186
198
  ok(readState(d).tokens_used === 1450, 'tokens summed from transcript');
187
199
  }
@@ -193,8 +205,10 @@ try {
193
205
  const cfg = JSON.parse(readFileSync(cfgP, 'utf8')); cfg.max_iterations = null; writeFileSync(cfgP, JSON.stringify(cfg));
194
206
  const spP = join(d, '.svamp', SID, 'loop', 'loop-state.json');
195
207
  const sp = JSON.parse(readFileSync(spP, 'utf8')); sp.iteration = 200; writeFileSync(spP, JSON.stringify(sp)); // at the hard ceiling
196
- const r = runGate(d);
197
- ok(!r.blocked, 'gate allows stop at the hard fallback ceiling even with null max_iterations');
208
+ const r = runGate(d); // #0156: stall hint first
209
+ const r2 = runGate(d); // then allow
210
+ ok(r.blocked && /STALL/i.test(r.reason), 'hard-ceiling hit surfaces a stall hint first');
211
+ ok(!r2.blocked, 'gate allows stop at the hard fallback ceiling even with null max_iterations');
198
212
  }
199
213
 
200
214
  // ---- Test 14: non-git project fingerprint is content-sensitive (CRITICAL #1) ----
@@ -225,7 +239,8 @@ try {
225
239
  const cfg = JSON.parse(readFileSync(cfgP, 'utf8')); cfg.max_iterations = 1e9; writeFileSync(cfgP, JSON.stringify(cfg));
226
240
  const spP = join(d, '.svamp', SID, 'loop', 'loop-state.json');
227
241
  const sp = JSON.parse(readFileSync(spP, 'utf8')); sp.iteration = 201; writeFileSync(spP, JSON.stringify(sp));
228
- const r = runGate(d);
242
+ runGate(d); // #0156: stall hint first
243
+ const r = runGate(d); // then allow
229
244
  ok(!r.blocked && /max_iterations \(200\)/.test(readState(d).gave_up_reason || ''), 'max_iterations 1e9 clamped to 200 → gives up');
230
245
  }
231
246
 
@@ -374,6 +389,44 @@ try {
374
389
  ok(!r.blocked && readState(d).phase === 'done', 'all-handled inbox allows the loop to complete');
375
390
  }
376
391
 
392
+ // ---- Test 25: NON-URGENT inbox hint at settle — blocks ONCE, then allows (de-duped) (#0146) ----
393
+ console.log('Test 25: non-urgent inbox hint blocks once at settle, then allows (de-duped)');
394
+ { const d = newProject({ evaluator: 'on' }); dirs.push(d);
395
+ writeFileSync(join(d, 'answer.txt'), 'DONE\n'); // oracle passes
396
+ writeVerdict(d, { verdict: 'done', reason: 'ok', state_fp: fp(d) });
397
+ // Disable the #0103 urgent/unhandled guard so we exercise the non-urgent hint in isolation
398
+ // (otherwise the #0103 guard would block first on the same genuinely-pending message).
399
+ const cfgP = join(d, '.svamp', SID, 'loop', 'loop.config.json');
400
+ writeFileSync(cfgP, JSON.stringify({ ...JSON.parse(readFileSync(cfgP, 'utf8')), inbox_guard: false }));
401
+ // A genuinely-unseen (read=false, handled=false), non-urgent, non-awaiting-reply message.
402
+ writeFileSync(join(d, '.svamp', SID, 'inbox.json'), JSON.stringify([
403
+ { messageId: 'n1', from: 'agent:peer', subject: 'fyi', read: false, handled: false, urgency: 'normal' },
404
+ ]));
405
+ const r1 = runGate(d);
406
+ ok(r1.blocked && /unread inbox message/i.test(r1.reason), 'non-urgent unread message hints (blocks once) at the settle point');
407
+ ok(/fyi/.test(r1.reason), 'hint names the subject');
408
+ const hinted = JSON.parse(readFileSync(join(d, '.svamp', SID, 'loop', 'hinted-inbox.json'), 'utf8'));
409
+ ok(hinted.includes('n1'), 'hinted id recorded for de-dupe');
410
+ // Second settle: same message id already hinted → allows the loop to end (non-blocking thereafter).
411
+ const r2 = runGate(d);
412
+ ok(!r2.blocked && readState(d).phase === 'done', 'already-hinted message does not re-block — loop completes');
413
+ }
414
+
415
+ // ---- Test 26: urgent + awaiting-reply messages are NOT hinted by the non-urgent path (#0146) ----
416
+ console.log('Test 26: urgent / awaiting-reply messages skip the non-urgent hint');
417
+ { const d = newProject({ evaluator: 'on' }); dirs.push(d);
418
+ writeFileSync(join(d, 'answer.txt'), 'DONE\n');
419
+ writeVerdict(d, { verdict: 'done', reason: 'ok', state_fp: fp(d) });
420
+ // All handled (no #0103 block); one urgent, one awaiting-reply, one already-read → none qualify.
421
+ writeFileSync(join(d, '.svamp', SID, 'inbox.json'), JSON.stringify([
422
+ { messageId: 'u1', read: false, handled: true, urgency: 'urgent' },
423
+ { messageId: 'a1', read: false, handled: true, channelId: 'c', correlationId: 'x' },
424
+ { messageId: 'r1', read: true, handled: true, urgency: 'normal' },
425
+ ]));
426
+ const r = runGate(d);
427
+ ok(!r.blocked && readState(d).phase === 'done', 'no qualifying non-urgent unread message → loop completes (no noise)');
428
+ }
429
+
377
430
  console.log(`\n${fail === 0 ? '✅' : '❌'} ${pass} passed, ${fail} failed`);
378
431
  process.exit(fail === 0 ? 0 : 1);
379
432
  } finally {
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync } from '
2
2
  import { join, dirname } from 'node:path';
3
3
  import os from 'node:os';
4
4
  import { requireNotSandboxed } from './sandboxDetect-DNTcbgWD.mjs';
5
- import { A as shortId } from './run-BfgkuBEg.mjs';
5
+ import { A as shortId } from './run-C9ZeF2iG.mjs';
6
6
  import 'os';
7
7
  import 'fs/promises';
8
8
  import 'fs';
@@ -96,7 +96,7 @@ async function sessionSetTitle(title) {
96
96
  }
97
97
  async function sessionSetProjectDescription(description) {
98
98
  const dir = process.cwd();
99
- const { projectName, writeProjectInfo, sanitizeDescription, projectInfoPath } = await import('./run-BfgkuBEg.mjs').then(function (n) { return n.a8; });
99
+ const { projectName, writeProjectInfo, sanitizeDescription, projectInfoPath } = await import('./run-C9ZeF2iG.mjs').then(function (n) { return n.a8; });
100
100
  const desc = sanitizeDescription(description, 240);
101
101
  if (!desc) {
102
102
  console.error("Project description is empty.");
@@ -180,7 +180,7 @@ async function sessionBroadcast(action, args) {
180
180
  console.log(`Broadcast sent: ${action}`);
181
181
  }
182
182
  async function connectToMachineService() {
183
- const { connectAndGetMachine } = await import('./commands-u68ODpBt.mjs');
183
+ const { connectAndGetMachine } = await import('./commands-BRmWVCNf.mjs');
184
184
  return connectAndGetMachine();
185
185
  }
186
186
  function buildInboxMessage(args) {
@@ -258,7 +258,7 @@ async function inboxSend(targetSessionId, opts) {
258
258
  console.error("Message body is required.");
259
259
  process.exit(1);
260
260
  }
261
- const { connectAndResolveSession } = await import('./commands-u68ODpBt.mjs');
261
+ const { connectAndResolveSession } = await import('./commands-BRmWVCNf.mjs');
262
262
  let server;
263
263
  try {
264
264
  const { targetId, messageId } = await inboxSendCore(
@@ -312,7 +312,7 @@ async function inboxReply(messageId, body) {
312
312
  console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
313
313
  process.exit(1);
314
314
  }
315
- const { connectAndResolveSession } = await import('./commands-u68ODpBt.mjs');
315
+ const { connectAndResolveSession } = await import('./commands-BRmWVCNf.mjs');
316
316
  const { server: localServer, machine: localMachine } = await connectToMachineService();
317
317
  let localDisconnected = false;
318
318
  const disconnectLocal = async () => {
@@ -1,4 +1,4 @@
1
- import { P as resolveModel } from './run-BfgkuBEg.mjs';
1
+ import { P as resolveModel } from './run-C9ZeF2iG.mjs';
2
2
  import 'os';
3
3
  import 'fs/promises';
4
4
  import 'fs';