@wrongstack/core 0.6.0 → 0.6.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.
Files changed (35) hide show
  1. package/dist/{agent-bridge-BKNiE1VH.d.ts → agent-bridge-DaCvA_uK.d.ts} +1 -1
  2. package/dist/coordination/index.d.ts +5 -5
  3. package/dist/coordination/index.js +178 -54
  4. package/dist/coordination/index.js.map +1 -1
  5. package/dist/defaults/index.d.ts +10 -10
  6. package/dist/defaults/index.js +534 -75
  7. package/dist/defaults/index.js.map +1 -1
  8. package/dist/{events-DPQKFX7W.d.ts → events-CzkeaVVl.d.ts} +8 -2
  9. package/dist/execution/index.d.ts +149 -6
  10. package/dist/execution/index.js +345 -16
  11. package/dist/execution/index.js.map +1 -1
  12. package/dist/extension/index.d.ts +2 -2
  13. package/dist/{goal-store-BQ3YX1h1.d.ts → goal-store-DVCfj7Ff.d.ts} +20 -0
  14. package/dist/{index-B0qTujQW.d.ts → index-BkKLQjea.d.ts} +1 -1
  15. package/dist/{index-DkdRz6yK.d.ts → index-i9rPR53g.d.ts} +11 -4
  16. package/dist/index.d.ts +50 -16
  17. package/dist/index.js +769 -86
  18. package/dist/index.js.map +1 -1
  19. package/dist/infrastructure/index.d.ts +2 -2
  20. package/dist/kernel/index.d.ts +2 -2
  21. package/dist/kernel/index.js.map +1 -1
  22. package/dist/{multi-agent-Cpp7FXUl.d.ts → multi-agent-MfI6dmv-.d.ts} +30 -5
  23. package/dist/observability/index.d.ts +1 -1
  24. package/dist/{path-resolver--g_hKJ7V.d.ts → path-resolver-CWINz5XR.d.ts} +1 -1
  25. package/dist/{plan-templates-CKJs_sYh.d.ts → plan-templates-Bne1pB4d.d.ts} +1 -1
  26. package/dist/{provider-runner-hl4Il3xS.d.ts → provider-runner-CZYIzeBp.d.ts} +1 -1
  27. package/dist/sdd/index.d.ts +2 -2
  28. package/dist/storage/index.d.ts +3 -3
  29. package/dist/storage/index.js +3 -0
  30. package/dist/storage/index.js.map +1 -1
  31. package/dist/{tool-executor-B03CRwu-.d.ts → tool-executor-40Q6shR-.d.ts} +1 -1
  32. package/dist/types/index.d.ts +7 -7
  33. package/dist/types/index.js +16 -1
  34. package/dist/types/index.js.map +1 -1
  35. package/package.json +5 -1
package/dist/index.js CHANGED
@@ -2934,13 +2934,17 @@ var ToolExecutor = class {
2934
2934
  const ctrl = new AbortController();
2935
2935
  const timer = setTimeout(() => ctrl.abort(new Error("tool timeout")), timeoutMs);
2936
2936
  const combined = AbortSignal.any([parentSignal, ctrl.signal]);
2937
+ let cleanupCalled = false;
2938
+ let caught = false;
2937
2939
  try {
2938
2940
  if (typeof tool.executeStream === "function") {
2939
2941
  return await this.runStreamedTool(tool, input, ctx, combined, toolUseId);
2940
2942
  }
2941
2943
  return await tool.execute(input, ctx, { signal: combined });
2942
2944
  } catch (err) {
2945
+ caught = true;
2943
2946
  if (combined.aborted && typeof tool.cleanup === "function") {
2947
+ cleanupCalled = true;
2944
2948
  try {
2945
2949
  await tool.cleanup(input, ctx);
2946
2950
  } catch {
@@ -2949,6 +2953,16 @@ var ToolExecutor = class {
2949
2953
  throw err;
2950
2954
  } finally {
2951
2955
  clearTimeout(timer);
2956
+ if (combined.aborted && !caught) {
2957
+ if (!cleanupCalled && typeof tool.cleanup === "function") {
2958
+ try {
2959
+ await tool.cleanup(input, ctx);
2960
+ } catch {
2961
+ }
2962
+ }
2963
+ const reason = combined.reason instanceof Error ? combined.reason : new Error(typeof combined.reason === "string" ? combined.reason : "aborted");
2964
+ throw reason;
2965
+ }
2952
2966
  }
2953
2967
  }
2954
2968
  async runStreamedTool(tool, input, ctx, signal, toolUseId) {
@@ -3006,7 +3020,8 @@ var ToolExecutor = class {
3006
3020
  if (subjectKey) {
3007
3021
  const v = obj[subjectKey];
3008
3022
  if (typeof v === "string") {
3009
- return subjectKey === "path" || subjectKey === "file" || subjectKey === "files" ? normalizePath(v) : escapeGlob(v);
3023
+ const isPathKey = subjectKey === "path" || subjectKey === "file" || subjectKey === "files";
3024
+ return isPathKey ? normalizePath(v) : escapeGlob(v);
3010
3025
  }
3011
3026
  }
3012
3027
  if (toolName === "bash" && typeof obj.command === "string") {
@@ -6277,17 +6292,23 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
6277
6292
  handleTextDelta(state, ev.text);
6278
6293
  events.emit("provider.text_delta", { ctx, text: ev.text });
6279
6294
  break;
6280
- case "tool_use_start":
6281
- handleToolUseStart(state, ev);
6282
- events.emit("provider.tool_use_start", { ctx, id: ev.id, name: ev.name });
6295
+ case "tool_use_start": {
6296
+ const idVal = ev.id;
6297
+ const nameVal = ev.name;
6298
+ handleToolUseStart(state, { id: idVal, name: nameVal });
6299
+ const emittedPayload = { ctx, id: idVal ?? "unknown", name: nameVal ?? "unknown" };
6300
+ events.emit("provider.tool_use_start", emittedPayload);
6283
6301
  break;
6302
+ }
6284
6303
  case "tool_use_input_delta":
6285
6304
  handleToolUseInputDelta(state, ev);
6286
6305
  break;
6287
- case "tool_use_stop":
6306
+ case "tool_use_stop": {
6307
+ const stoppedName = state.tools.get(ev.id)?.name ?? "unknown";
6288
6308
  handleToolUseStop(state, ev);
6289
- events.emit("provider.tool_use_stop", { ctx, id: ev.id });
6309
+ events.emit("provider.tool_use_stop", { ctx, id: ev.id, name: stoppedName });
6290
6310
  break;
6311
+ }
6291
6312
  case "thinking_start":
6292
6313
  handleThinkingStart(state, ev);
6293
6314
  break;
@@ -7303,6 +7324,8 @@ function emptyGoal(goal) {
7303
7324
  lastActivityAt: now,
7304
7325
  iterations: 0,
7305
7326
  engineState: "idle",
7327
+ goalState: "active",
7328
+ todoAttempts: {},
7306
7329
  journal: []
7307
7330
  };
7308
7331
  }
@@ -7340,6 +7363,7 @@ function formatGoal(goal, journalLimit = 10) {
7340
7363
  lines.push(`Set: ${goal.setAt}`);
7341
7364
  lines.push(`Last activity: ${goal.lastActivityAt}`);
7342
7365
  lines.push(`Iterations: ${goal.iterations}`);
7366
+ lines.push(`Mission: ${goal.goalState ?? "active"}`);
7343
7367
  lines.push(`Engine: ${goal.engineState}`);
7344
7368
  const usage = summarizeUsage(goal);
7345
7369
  if (usage.iterationsWithUsage > 0) {
@@ -7363,6 +7387,8 @@ function formatGoal(goal, journalLimit = 10) {
7363
7387
 
7364
7388
  // src/execution/eternal-autonomy.ts
7365
7389
  var execFileP = promisify(execFile);
7390
+ var BRAINSTORM_DONE = /* @__PURE__ */ Symbol("brainstorm-done");
7391
+ var GOAL_COMPLETE_MARKER = /^\s*\[goal[_\s-]?complete\]\s*$/im;
7366
7392
  var EternalAutonomyEngine = class {
7367
7393
  constructor(opts) {
7368
7394
  this.opts = opts;
@@ -7372,6 +7398,14 @@ var EternalAutonomyEngine = class {
7372
7398
  state = "idle";
7373
7399
  stopRequested = false;
7374
7400
  consecutiveFailures = 0;
7401
+ consecutiveBrainstormDone = 0;
7402
+ /**
7403
+ * Count of consecutive transient (recoverable) provider failures. Drives
7404
+ * the exponential backoff between iterations. Reset on the first
7405
+ * successful iteration so a single bad afternoon doesn't permanently
7406
+ * slow the loop down.
7407
+ */
7408
+ consecutiveTransientRetries = 0;
7375
7409
  currentCtrl = null;
7376
7410
  iterationsSinceCompact = 0;
7377
7411
  goalPath;
@@ -7441,9 +7475,16 @@ var EternalAutonomyEngine = class {
7441
7475
  this.stopRequested = true;
7442
7476
  return false;
7443
7477
  }
7478
+ const missionState = goal.goalState ?? "active";
7479
+ if (missionState !== "active") {
7480
+ this.stopRequested = true;
7481
+ return false;
7482
+ }
7444
7483
  const action = await this.decide(goal);
7445
7484
  if (!action) {
7446
- await sleep(5e3);
7485
+ if (!this.stopRequested) {
7486
+ await sleep(5e3);
7487
+ }
7447
7488
  return false;
7448
7489
  }
7449
7490
  const ctrl = new AbortController();
@@ -7454,13 +7495,29 @@ var EternalAutonomyEngine = class {
7454
7495
  );
7455
7496
  let status = "success";
7456
7497
  let note;
7498
+ let finalText = "";
7499
+ let isTransientFailure = false;
7457
7500
  const tc = this.opts.agent.ctx?.tokenCounter;
7458
7501
  const beforeUsage = tc?.total?.();
7459
7502
  const beforeCost = tc?.estimateCost?.().total;
7460
7503
  try {
7461
7504
  const result = await this.opts.agent.run(
7462
7505
  [{ type: "text", text: action.directive }],
7463
- { signal: ctrl.signal }
7506
+ {
7507
+ signal: ctrl.signal,
7508
+ // Enable per-call autonomous continuation so the agent can chain
7509
+ // multiple internal tool/response cycles end-to-end on one
7510
+ // directive instead of returning to the engine after a single
7511
+ // round-trip. The model uses `[continue]` / `[done]` markers
7512
+ // (or the `continue_to_next_iteration` tool) to control the
7513
+ // inner loop. Without this flag the engine produced shallow
7514
+ // iterations and almost never let a real task finish.
7515
+ autonomousContinue: true,
7516
+ // Cap the inner loop so a runaway agent.run can't burn through
7517
+ // the iteration timeout — the engine's own outer loop is the
7518
+ // long-running thing, each tick should be bounded.
7519
+ maxIterations: this.opts.iterationMaxAgentSteps ?? 50
7520
+ }
7464
7521
  );
7465
7522
  if (result.status === "aborted") {
7466
7523
  status = "aborted";
@@ -7468,22 +7525,30 @@ var EternalAutonomyEngine = class {
7468
7525
  } else if (result.status === "failed") {
7469
7526
  status = "failure";
7470
7527
  note = result.error?.describe?.() ?? "agent run failed";
7528
+ isTransientFailure = result.error?.recoverable === true;
7471
7529
  } else if (result.status === "max_iterations") {
7472
7530
  status = "failure";
7473
7531
  note = `max iterations (${result.iterations})`;
7474
7532
  } else {
7475
7533
  status = "success";
7476
- const tail = (result.finalText ?? "").slice(0, 240).replace(/\s+/g, " ").trim();
7534
+ finalText = result.finalText ?? "";
7535
+ const tail = finalText.slice(0, 240).replace(/\s+/g, " ").trim();
7477
7536
  if (tail) note = tail;
7478
7537
  }
7479
7538
  } catch (err) {
7480
7539
  const isAbort = err instanceof Error && (err.name === "AbortError" || err.message.includes("abort"));
7481
7540
  status = isAbort ? "aborted" : "failure";
7482
7541
  note = err instanceof Error ? err.message : String(err);
7542
+ if (!isAbort && typeof err?.recoverable === "boolean") {
7543
+ isTransientFailure = err.recoverable;
7544
+ }
7483
7545
  } finally {
7484
7546
  clearTimeout(timer);
7485
7547
  this.currentCtrl = null;
7486
7548
  }
7549
+ if (action.source === "todo" && action.todoId && status !== "success") {
7550
+ await this.bumpTodoAttempt(action.todoId);
7551
+ }
7487
7552
  const afterUsage = tc?.total?.();
7488
7553
  const afterCost = tc?.estimateCost?.().total;
7489
7554
  const tokens = beforeUsage && afterUsage ? {
@@ -7516,6 +7581,14 @@ var EternalAutonomyEngine = class {
7516
7581
  costUsd
7517
7582
  });
7518
7583
  if (status === "failure") {
7584
+ if (isTransientFailure) {
7585
+ this.consecutiveTransientRetries++;
7586
+ const delay = this.computeTransientBackoffMs();
7587
+ if (delay > 0) {
7588
+ await this.sleepInterruptible(delay);
7589
+ }
7590
+ return false;
7591
+ }
7519
7592
  this.consecutiveFailures++;
7520
7593
  return false;
7521
7594
  }
@@ -7524,6 +7597,12 @@ var EternalAutonomyEngine = class {
7524
7597
  this.consecutiveFailures++;
7525
7598
  return false;
7526
7599
  }
7600
+ this.consecutiveTransientRetries = 0;
7601
+ if (GOAL_COMPLETE_MARKER.test(finalText)) {
7602
+ await this.markGoalCompleted(action, finalText);
7603
+ this.stopRequested = true;
7604
+ return true;
7605
+ }
7527
7606
  this.iterationsSinceCompact++;
7528
7607
  await this.maybeCompact().catch((err) => {
7529
7608
  this.opts.onError?.(
@@ -7587,11 +7666,12 @@ var EternalAutonomyEngine = class {
7587
7666
  async decide(goal) {
7588
7667
  const forceBrainstorm = this.consecutiveFailures >= (this.opts.failureBudget ?? 3);
7589
7668
  if (!forceBrainstorm) {
7590
- const todo = this.pickPendingTodo();
7669
+ const todo = this.pickPendingTodo(goal);
7591
7670
  if (todo) {
7592
7671
  return {
7593
7672
  source: "todo",
7594
7673
  task: todo.content,
7674
+ todoId: todo.id,
7595
7675
  directive: this.buildDirective(goal, "todo", todo.content)
7596
7676
  };
7597
7677
  }
@@ -7605,17 +7685,38 @@ var EternalAutonomyEngine = class {
7605
7685
  }
7606
7686
  }
7607
7687
  const brainstormed = await this.brainstormTask(goal);
7688
+ if (brainstormed === BRAINSTORM_DONE) {
7689
+ this.consecutiveBrainstormDone++;
7690
+ const threshold = this.opts.brainstormDoneStopThreshold ?? 3;
7691
+ if (this.consecutiveBrainstormDone >= threshold) {
7692
+ await this.markGoalCompleted(
7693
+ { source: "brainstorm", task: "no further work", directive: "" },
7694
+ `brainstorm returned DONE ${this.consecutiveBrainstormDone}x in a row`
7695
+ );
7696
+ this.stopRequested = true;
7697
+ }
7698
+ return null;
7699
+ }
7608
7700
  if (!brainstormed) return null;
7701
+ this.consecutiveBrainstormDone = 0;
7609
7702
  return {
7610
7703
  source: "brainstorm",
7611
7704
  task: brainstormed,
7612
7705
  directive: this.buildDirective(goal, "brainstorm", brainstormed)
7613
7706
  };
7614
7707
  }
7615
- pickPendingTodo() {
7708
+ pickPendingTodo(goal) {
7616
7709
  const todos = this.opts.agent.ctx.todos;
7617
7710
  if (!Array.isArray(todos)) return null;
7618
- return todos.find((t2) => t2.status === "pending") ?? null;
7711
+ const attempts = goal.todoAttempts ?? {};
7712
+ const ceiling = this.opts.todoMaxAttempts ?? 3;
7713
+ for (const t2 of todos) {
7714
+ if (t2.status !== "pending") continue;
7715
+ const used = attempts[t2.id] ?? 0;
7716
+ if (used >= ceiling) continue;
7717
+ return t2;
7718
+ }
7719
+ return null;
7619
7720
  }
7620
7721
  async pickGitTask() {
7621
7722
  let out;
@@ -7652,7 +7753,10 @@ ${lastFew}` : "No prior iterations yet.",
7652
7753
  "- One sentence, imperative form, under 200 chars.",
7653
7754
  "- No preamble, no explanation, no markdown \u2014 just the task line.",
7654
7755
  "- If recent iterations show repeated failures on the same target, pivot.",
7655
- "- If the goal appears fully accomplished, output exactly: DONE"
7756
+ "- If the goal appears fully accomplished AND you can name a concrete",
7757
+ " artifact / test / output that proves it, output exactly: DONE",
7758
+ "- Be conservative with DONE: if the recent journal contains failures",
7759
+ " or aborted entries, the goal is almost certainly NOT done."
7656
7760
  ].join("\n");
7657
7761
  try {
7658
7762
  const ctrl = new AbortController();
@@ -7664,9 +7768,11 @@ ${lastFew}` : "No prior iterations yet.",
7664
7768
  );
7665
7769
  if (result.status !== "done") return null;
7666
7770
  const text = (result.finalText ?? "").trim();
7667
- if (!text || text === "DONE") return null;
7771
+ if (!text) return null;
7772
+ if (/^DONE\.?$/i.test(text)) return BRAINSTORM_DONE;
7668
7773
  const firstLine = text.split("\n").find((l) => l.trim().length > 0)?.trim();
7669
7774
  if (!firstLine) return null;
7775
+ if (/^DONE\.?$/i.test(firstLine)) return BRAINSTORM_DONE;
7670
7776
  return firstLine.slice(0, 240);
7671
7777
  } finally {
7672
7778
  clearTimeout(timer);
@@ -7676,19 +7782,82 @@ ${lastFew}` : "No prior iterations yet.",
7676
7782
  }
7677
7783
  }
7678
7784
  buildDirective(goal, source, task) {
7785
+ const recentJournal = goal.journal.slice(-5).map((e) => ` #${e.iteration} [${e.status}] ${e.task}${e.note ? ` \u2014 ${e.note.slice(0, 80)}` : ""}`).join("\n");
7679
7786
  return [
7680
- "[ETERNAL AUTONOMY \u2014 iteration directive]",
7787
+ "\u2550\u2550\u2550 ETERNAL AUTONOMY \u2014 iteration directive \u2550\u2550\u2550",
7681
7788
  "",
7682
- `Goal: ${goal.goal}`,
7789
+ `Mission: ${goal.goal}`,
7790
+ `Iteration: #${goal.iterations + 1}`,
7683
7791
  `Source: ${source}`,
7684
7792
  `Task: ${task}`,
7685
7793
  "",
7686
- "Execute this task end-to-end using the tools available to you. Make the",
7687
- "changes, run tests if relevant, and commit / push as appropriate. Do not",
7688
- "ask for confirmation \u2014 YOLO mode is active. When the task is done, stop;",
7689
- "the loop will pick the next action."
7794
+ recentJournal ? `Recent journal (last 5):
7795
+ ${recentJournal}` : "No prior iterations.",
7796
+ "",
7797
+ "\u2500\u2500 EXECUTION PROTOCOL \u2500\u2500",
7798
+ "You are inside a long-running autonomous loop. Each iteration you",
7799
+ "execute ONE concrete task that advances the Mission. No user is",
7800
+ "available to clarify \u2014 make defensible decisions and move forward.",
7801
+ "",
7802
+ "1. EXECUTE END-TO-END",
7803
+ " \u2022 Use multiple tool calls freely. Emit `[continue]` on its own line",
7804
+ " to chain to the next internal step without returning.",
7805
+ " \u2022 When this iteration's Task is finished (real artifact / passing",
7806
+ " test / applied diff / clean output), emit `[done]` on its own line.",
7807
+ " \u2022 Do not stop on the first obstacle \u2014 try at least 3 distinct",
7808
+ " approaches before giving up. YOLO is active; no confirmations.",
7809
+ "",
7810
+ "2. UPDATE TODO STATE (when Source is `todo`)",
7811
+ " \u2022 Mark this todo `in_progress` via the todos tool before tool work.",
7812
+ " \u2022 Mark it `completed` on success, with a one-line outcome note.",
7813
+ " \u2022 If you cannot make progress after 2 distinct attempts, mark it",
7814
+ " `cancelled` with the obstacle. The loop will skip it next time.",
7815
+ "",
7816
+ "3. MISSION-COMPLETE PROTOCOL",
7817
+ " \u2022 If \u2014 and ONLY if \u2014 the OVERALL Mission (not just this Task) is",
7818
+ " verifiably accomplished, emit on its own line:",
7819
+ " [GOAL_COMPLETE]",
7820
+ " followed by a one-paragraph verification recipe (artifact path,",
7821
+ " test command, or 10-second reproduction). This halts the loop.",
7822
+ " \u2022 NEVER emit [GOAL_COMPLETE] on optimism, partial progress, or",
7823
+ ' "looks fine". Required: a concrete artifact that proves it AND',
7824
+ " no recent journal failures contradicting completion.",
7825
+ " \u2022 If unsure, emit `[done]` instead and let the next iteration",
7826
+ " decide. The loop is patient; false completion is not.",
7827
+ "",
7828
+ "4. NO INTERACTIVITY",
7829
+ " \u2022 Do not ask questions, do not request confirmation, do not propose",
7830
+ " options. Pick the best path and execute. The user is asleep."
7690
7831
  ].join("\n");
7691
7832
  }
7833
+ /**
7834
+ * Exponential backoff for transient provider errors. `2^N * base`
7835
+ * capped at `transientBackoffMaxMs`. Zero base disables backoff.
7836
+ * Public-private to keep `runOneIteration` readable; the value is
7837
+ * recomputed each call from the current retry count, so callers
7838
+ * don't have to track state.
7839
+ */
7840
+ computeTransientBackoffMs() {
7841
+ const base = this.opts.transientBackoffBaseMs ?? 2e3;
7842
+ const cap = this.opts.transientBackoffMaxMs ?? 6e4;
7843
+ if (base <= 0) return 0;
7844
+ const exponent = Math.max(0, this.consecutiveTransientRetries - 1);
7845
+ return Math.min(cap, base * Math.pow(2, exponent));
7846
+ }
7847
+ /**
7848
+ * Sleep that wakes early if `stopRequested` flips. Polls every 250 ms
7849
+ * so SIGINT / `/autonomy stop` can land in the middle of a long
7850
+ * backoff instead of waiting up to a minute for the timer.
7851
+ */
7852
+ async sleepInterruptible(totalMs) {
7853
+ const step = 250;
7854
+ let remaining = totalMs;
7855
+ while (remaining > 0 && !this.stopRequested) {
7856
+ const chunk = Math.min(step, remaining);
7857
+ await sleep(chunk);
7858
+ remaining -= chunk;
7859
+ }
7860
+ }
7692
7861
  async appendIterationEntry(entry) {
7693
7862
  const current = await loadGoal(this.goalPath);
7694
7863
  if (!current) {
@@ -7697,6 +7866,39 @@ ${lastFew}` : "No prior iterations yet.",
7697
7866
  const updated = appendJournal(current, entry);
7698
7867
  await saveGoal(this.goalPath, updated);
7699
7868
  }
7869
+ /**
7870
+ * Persistent per-todo failure counter. Skipped silently when the goal
7871
+ * file has been removed (graceful clear). Each non-success iteration
7872
+ * against a todo source bumps the counter by 1; `pickPendingTodo` reads
7873
+ * the counter to rotate past stuck todos once they cross `todoMaxAttempts`.
7874
+ */
7875
+ async bumpTodoAttempt(todoId) {
7876
+ const current = await loadGoal(this.goalPath);
7877
+ if (!current) return;
7878
+ const attempts = { ...current.todoAttempts ?? {} };
7879
+ attempts[todoId] = (attempts[todoId] ?? 0) + 1;
7880
+ await saveGoal(this.goalPath, { ...current, todoAttempts: attempts });
7881
+ }
7882
+ /**
7883
+ * Flip the mission to `completed` and journal it. Called from two
7884
+ * paths: (a) `[GOAL_COMPLETE]` marker in a successful iteration's
7885
+ * finalText, (b) `brainstorm` returning DONE consecutively past the
7886
+ * configured threshold. Idempotent — re-entry is a no-op once the
7887
+ * goal is already `completed`.
7888
+ */
7889
+ async markGoalCompleted(action, note) {
7890
+ const current = await loadGoal(this.goalPath);
7891
+ if (!current) return;
7892
+ if (current.goalState === "completed") return;
7893
+ const withFlag = { ...current, goalState: "completed" };
7894
+ const withEntry = appendJournal(withFlag, {
7895
+ source: action.source,
7896
+ task: `MISSION COMPLETE \u2014 ${action.task}`.slice(0, 240),
7897
+ status: "success",
7898
+ note: note.slice(0, 240)
7899
+ });
7900
+ await saveGoal(this.goalPath, withEntry);
7901
+ }
7700
7902
  async appendFailure(task, note) {
7701
7903
  await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
7702
7904
  }
@@ -7711,6 +7913,142 @@ function sleep(ms) {
7711
7913
  return new Promise((resolve5) => setTimeout(resolve5, ms));
7712
7914
  }
7713
7915
 
7916
+ // src/execution/autonomy-prompt-contributor.ts
7917
+ function makeAutonomyPromptContributor(opts) {
7918
+ return async (ctx) => {
7919
+ if (ctx.subagent) return [];
7920
+ if (!opts.enabled()) return [];
7921
+ let goal;
7922
+ try {
7923
+ goal = await loadGoal(opts.goalPath);
7924
+ } catch {
7925
+ return [];
7926
+ }
7927
+ if (!goal) return [];
7928
+ const missionState = goal.goalState ?? "active";
7929
+ if (missionState !== "active") return [];
7930
+ const tailSize = opts.journalTailSize ?? 5;
7931
+ const journalTail = goal.journal.slice(-tailSize).map((e) => {
7932
+ const note = e.note ? ` \u2014 ${e.note.slice(0, 80)}` : "";
7933
+ return ` #${e.iteration} [${e.status}] ${e.task}${note}`;
7934
+ });
7935
+ const text = [
7936
+ "## ETERNAL AUTONOMY \u2014 active mission",
7937
+ "",
7938
+ "You are inside a long-running autonomous loop. The user is asleep",
7939
+ "and is not available to confirm decisions. Each turn you receive a",
7940
+ "directive describing one concrete sub-task that advances the mission.",
7941
+ "",
7942
+ `Mission: ${goal.goal}`,
7943
+ `Iteration: #${goal.iterations}`,
7944
+ journalTail.length > 0 ? `Recent journal (last ${journalTail.length}):
7945
+ ${journalTail.join("\n")}` : "Recent journal: (none \u2014 this is the first iteration)",
7946
+ "",
7947
+ "### Loop control markers",
7948
+ "Emit these on their own line in your final text \u2014 case-insensitive,",
7949
+ "whitespace-tolerant, but they must occupy the entire line:",
7950
+ "- `[continue]` \u2014 chain to the next internal step without returning.",
7951
+ "- `[done]` \u2014 the current sub-task is finished; return to the engine.",
7952
+ "- `[GOAL_COMPLETE]` \u2014 emit ONLY when the OVERALL mission is",
7953
+ " verifiably done. Must be followed by a one-paragraph verification",
7954
+ " recipe (artifact path, test command, or 10-second reproduction).",
7955
+ " The engine halts on this marker \u2014 false positives waste real",
7956
+ " human time. If unsure, emit `[done]` and let the next iteration",
7957
+ " decide.",
7958
+ "",
7959
+ "### Operating principles",
7960
+ "- YOLO is active. Do NOT ask for confirmation, do NOT propose",
7961
+ " options. Pick the best path and execute it.",
7962
+ "- Use tools freely; multiple calls per turn are normal and expected.",
7963
+ "- When working on a todo, mark it `in_progress` via the todos tool",
7964
+ " before tool work and `completed` (or `cancelled` with a reason)",
7965
+ " when done. The loop reads todo state between iterations.",
7966
+ "- If an approach fails twice in a row, pivot. Don't grind on the",
7967
+ " same wall \u2014 try a different angle, file a cancel on the todo, or",
7968
+ " surface the obstacle via `[done]` and let the next iteration",
7969
+ " re-plan."
7970
+ ].join("\n");
7971
+ return [
7972
+ {
7973
+ type: "text",
7974
+ text,
7975
+ cache_control: { type: "ephemeral" }
7976
+ }
7977
+ ];
7978
+ };
7979
+ }
7980
+
7981
+ // src/execution/goal-preamble.ts
7982
+ function buildGoalPreamble(goal) {
7983
+ return [
7984
+ "[GOAL \u2014 LOCKED IN. You will work on this until it is verifiably done.",
7985
+ "The user granted you full autonomy. Read these constraints once, then act.",
7986
+ "",
7987
+ "YOUR GOAL:",
7988
+ "---",
7989
+ goal,
7990
+ "---",
7991
+ "",
7992
+ "AUTHORITY YOU HAVE:",
7993
+ "- Spawn as many subagents as the work needs (delegate / spawn_subagent).",
7994
+ " Parallel + recursive fan-out are both fine. There is no spawn budget.",
7995
+ "- Use any provider/model per subagent \u2014 pick the right tool for each",
7996
+ " piece of work. Heavy reasoning model for planning, fast model for",
7997
+ " batch work, specialist model for domain code.",
7998
+ "- Run unlimited tool calls and iterations. There is NO hidden budget.",
7999
+ " The Agent loop auto-extends every 100 iterations forever.",
8000
+ "- Retry failed tools with different inputs, alternative paths, fresh",
8001
+ " subagents. Switch providers mid-run if one is rate-limited.",
8002
+ "- Re-plan freely when an approach hits a dead end. You are not obliged",
8003
+ " to stick with the first plan you proposed.",
8004
+ "",
8005
+ 'WHAT "DONE" MEANS \u2014 non-negotiable:',
8006
+ "- You can name a concrete artifact (a passing test, a written file at",
8007
+ " a specific path, a fixed bug verified by re-running the failing case,",
8008
+ " a clean grep that previously had matches).",
8009
+ "- You can tell the user HOW to verify it themselves in 10 seconds.",
8010
+ '- You have NOT hedged. None of: "looks like it should work", "I',
8011
+ ' believe this fixes it", "the changes appear correct".',
8012
+ "",
8013
+ "WHAT IS NOT DONE \u2014 never report any of these as completion:",
8014
+ "- An error message you didn't recover from.",
8015
+ '- An empty result, a 0-line file, a "no matches found" you accepted',
8016
+ " without questioning the search.",
8017
+ '- "Should I continue?" / "Want me to also...?" / "Let me know if you',
8018
+ ' want X." Those are hedges. The user already told you to finish the',
8019
+ " goal \u2014 just do it.",
8020
+ "- Partial progress dressed up as success. Fixed 3 of 5 bugs = 60%",
8021
+ " done, not done.",
8022
+ "- A subagent's failed/timeout/stopped TaskResult that you didn't",
8023
+ " respond to with a fresh attempt (different role, different model,",
8024
+ " tighter prompt).",
8025
+ "",
8026
+ "PERSISTENCE PROTOCOL:",
8027
+ "- If blocked, try at least 3 different angles before reporting the",
8028
+ " problem to the user. Different tool inputs, different subagent",
8029
+ " roles, different providers, different decomposition of the task.",
8030
+ "- If a tool fails, read its error, alter the input, try again. Do",
8031
+ " not just report the failure back.",
8032
+ "- If a subagent returns useless output, respawn with a tighter prompt",
8033
+ ' or a different role. Do not accept "I could not determine\u2026" as the',
8034
+ " final answer.",
8035
+ "- Use `ask_subagent` for one-shot questions when you don't need a",
8036
+ " full delegated task.",
8037
+ "",
8038
+ "REPORTING:",
8039
+ "- Stream short progress notes between major actions so the user can",
8040
+ " monitor. Do not go silent for 50 tool calls then dump a wall of",
8041
+ " text \u2014 but also do not narrate every tool call.",
8042
+ "- Use the shared scratchpad (if available) to leave breadcrumbs",
8043
+ " subagents can read.",
8044
+ "- Final response must include: (a) what was accomplished, (b) how",
8045
+ " to verify, (c) any caveats (residual TODOs, things the user",
8046
+ " should know about).",
8047
+ "",
8048
+ "BEGIN.]"
8049
+ ].join("\n");
8050
+ }
8051
+
7714
8052
  // src/coordination/director.ts
7715
8053
  init_atomic_write();
7716
8054
 
@@ -8021,7 +8359,7 @@ var BudgetThresholdSignal = class extends Error {
8021
8359
  this.decision = decision;
8022
8360
  }
8023
8361
  };
8024
- var SubagentBudget = class {
8362
+ var SubagentBudget = class _SubagentBudget {
8025
8363
  limits;
8026
8364
  iterations = 0;
8027
8365
  toolCalls = 0;
@@ -8030,6 +8368,26 @@ var SubagentBudget = class {
8030
8368
  costUsd = 0;
8031
8369
  startTime = null;
8032
8370
  _onThreshold;
8371
+ /**
8372
+ * Tracks which budget kinds currently have an extension request
8373
+ * in flight. While a kind is here, further `checkLimit` calls for the
8374
+ * same kind are no-ops — without this dedup, every `recordIteration`
8375
+ * after the limit is reached spawns a fresh decision Promise (until
8376
+ * the first one lands and patches limits), flooding the FleetBus
8377
+ * with redundant threshold events. Cleared in `checkLimitAsync`'s
8378
+ * `finally`.
8379
+ */
8380
+ pendingExtensions = /* @__PURE__ */ new Set();
8381
+ /**
8382
+ * Hard cap on how long `checkLimitAsync` waits for the coordinator to
8383
+ * respond before defaulting to 'stop'. Without this fallback an absent
8384
+ * or hung listener (Director not built / event filter detached mid-run)
8385
+ * leaves the budget over-limit and never enforces anything, since
8386
+ * `checkLimit` returns synchronously via `void this.checkLimitAsync`.
8387
+ * Hardcoded for now — most fleets set their own per-task timeout that
8388
+ * dwarfs this anyway. 30 s matches the existing event-emit timeoutMs.
8389
+ */
8390
+ static DECISION_TIMEOUT_MS = 3e4;
8033
8391
  /**
8034
8392
  * Injected by the runner when wiring the budget to its EventBus.
8035
8393
  * Used by `checkLimitAsync` to emit `budget.threshold_reached` events.
@@ -8072,56 +8430,92 @@ var SubagentBudget = class {
8072
8430
  if (kind === "timeout" || !this._onThreshold) {
8073
8431
  throw new BudgetExceededError(kind, limit, used);
8074
8432
  }
8075
- void this.checkLimitAsync(kind, used, limit);
8433
+ const bus = this._events;
8434
+ if (!bus || bus.listenerCount("budget.threshold_reached") === 0) {
8435
+ throw new BudgetExceededError(kind, limit, used);
8436
+ }
8437
+ if (this.pendingExtensions.has(kind)) return;
8438
+ this.pendingExtensions.add(kind);
8439
+ const decision = this.negotiateExtension(kind, used, limit);
8440
+ throw new BudgetThresholdSignal(kind, limit, used, decision);
8076
8441
  }
8077
8442
  /**
8078
- * Async threshold negotiation with the coordinator. Fire-and-forget
8079
- * any error thrown here becomes an unhandled rejection in the test environment
8080
- * because the runner's catch only handles the synchronous throw from `checkLimit`.
8443
+ * Drive the threshold handler to a decision. Resolves with `'stop'`
8444
+ * (signal the runner to abort) or `{ extend: ... }` (limits already
8445
+ * patched in-place; the runner should not abort). Always releases the
8446
+ * `pendingExtensions` slot in `finally`.
8447
+ *
8448
+ * The 'continue' return from a sync handler is treated as
8449
+ * `{ extend: {} }` — keep going without patching, next overrun will
8450
+ * fire a fresh signal.
8081
8451
  */
8082
- async checkLimitAsync(kind, used, limit) {
8083
- const result = this._onThreshold({
8084
- kind,
8085
- used,
8086
- limit,
8087
- // Inject a requestDecision helper the handler can call to emit the
8088
- // budget.threshold_reached event and wait for the coordinator's verdict.
8089
- // The runner wires this by injecting its EventBus into ctx.budget._events.
8090
- requestDecision: () => {
8091
- return new Promise((resolve5) => {
8092
- this._events?.emit("budget.threshold_reached", {
8093
- kind,
8094
- used,
8095
- limit,
8096
- timeoutMs: 3e4,
8097
- extend: (extra) => resolve5({ extend: extra }),
8098
- deny: () => resolve5("stop")
8452
+ async negotiateExtension(kind, used, limit) {
8453
+ try {
8454
+ const result = this._onThreshold({
8455
+ kind,
8456
+ used,
8457
+ limit,
8458
+ // Inject a requestDecision helper the handler can call to emit the
8459
+ // budget.threshold_reached event and wait for the coordinator's verdict.
8460
+ // A hard fallback timer guarantees the promise eventually resolves
8461
+ // even if no listener responds — without it, an absent/detached
8462
+ // Director would leave the budget permanently in "asking" state.
8463
+ requestDecision: () => {
8464
+ const bus = this._events;
8465
+ if (!bus || bus.listenerCount("budget.threshold_reached") === 0) {
8466
+ return Promise.resolve("stop");
8467
+ }
8468
+ return new Promise((resolve5) => {
8469
+ let resolved = false;
8470
+ const respond = (d) => {
8471
+ if (resolved) return;
8472
+ resolved = true;
8473
+ resolve5(d);
8474
+ };
8475
+ const fallback = setTimeout(
8476
+ () => respond("stop"),
8477
+ _SubagentBudget.DECISION_TIMEOUT_MS
8478
+ );
8479
+ bus.emit("budget.threshold_reached", {
8480
+ kind,
8481
+ used,
8482
+ limit,
8483
+ timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
8484
+ extend: (extra) => {
8485
+ clearTimeout(fallback);
8486
+ respond({ extend: extra });
8487
+ },
8488
+ deny: () => {
8489
+ clearTimeout(fallback);
8490
+ respond("stop");
8491
+ }
8492
+ });
8099
8493
  });
8100
- });
8494
+ }
8495
+ });
8496
+ if (result === "throw") return "stop";
8497
+ if (result === "continue") return { extend: {} };
8498
+ const decision = await result;
8499
+ if (decision === "stop") return "stop";
8500
+ const ext = decision.extend;
8501
+ if (ext.maxIterations !== void 0) {
8502
+ this.limits.maxIterations = ext.maxIterations;
8101
8503
  }
8102
- });
8103
- if (result === "throw") {
8104
- throw new BudgetExceededError(kind, limit, used);
8105
- }
8106
- if (result === "continue") {
8107
- return;
8108
- }
8109
- const decision = await result;
8110
- if (decision === "stop") {
8111
- throw new BudgetExceededError(kind, limit, used);
8112
- }
8113
- const ext = decision.extend;
8114
- if (ext.maxIterations !== void 0) {
8115
- this.limits.maxIterations = ext.maxIterations;
8116
- }
8117
- if (ext.maxToolCalls !== void 0) {
8118
- this.limits.maxToolCalls = ext.maxToolCalls;
8119
- }
8120
- if (ext.maxTokens !== void 0) {
8121
- this.limits.maxTokens = ext.maxTokens;
8122
- }
8123
- if (ext.maxCostUsd !== void 0) {
8124
- this.limits.maxCostUsd = ext.maxCostUsd;
8504
+ if (ext.maxToolCalls !== void 0) {
8505
+ this.limits.maxToolCalls = ext.maxToolCalls;
8506
+ }
8507
+ if (ext.maxTokens !== void 0) {
8508
+ this.limits.maxTokens = ext.maxTokens;
8509
+ }
8510
+ if (ext.maxCostUsd !== void 0) {
8511
+ this.limits.maxCostUsd = ext.maxCostUsd;
8512
+ }
8513
+ if (ext.timeoutMs !== void 0) {
8514
+ this.limits.timeoutMs = ext.timeoutMs;
8515
+ }
8516
+ return decision;
8517
+ } finally {
8518
+ this.pendingExtensions.delete(kind);
8125
8519
  }
8126
8520
  }
8127
8521
  recordIteration() {
@@ -8404,6 +8798,19 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
8404
8798
  setRunner(runner) {
8405
8799
  this.runner = runner;
8406
8800
  }
8801
+ /**
8802
+ * Change the in-flight dispatch ceiling at runtime. Lowering does NOT
8803
+ * preempt running tasks — already-dispatched subagents finish their
8804
+ * current task; only future dispatches respect the new cap. Raising
8805
+ * immediately tries to fill the freed slots from the pending queue.
8806
+ */
8807
+ setMaxConcurrent(n) {
8808
+ if (!Number.isFinite(n) || n < 1) {
8809
+ throw new Error(`maxConcurrent must be a finite integer >= 1, got ${n}`);
8810
+ }
8811
+ this.config.maxConcurrent = Math.floor(n);
8812
+ this.tryDispatchNext();
8813
+ }
8407
8814
  async spawn(subagent) {
8408
8815
  const id = subagent.id || randomUUID();
8409
8816
  if (this.subagents.has(id)) {
@@ -8663,14 +9070,65 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
8663
9070
  this.recordCompletion(result);
8664
9071
  }
8665
9072
  async executeWithTimeout(runner, task, ctx, budget) {
8666
- const timeoutMs = budget.limits.timeoutMs;
8667
- if (timeoutMs === void 0) return runner(task, ctx);
9073
+ const initialTimeoutMs = budget.limits.timeoutMs;
9074
+ if (initialTimeoutMs === void 0) return runner(task, ctx);
9075
+ const start = Date.now();
8668
9076
  let timer = null;
8669
9077
  const timeoutPromise = new Promise((_, reject) => {
8670
- timer = setTimeout(() => {
8671
- this.subagents.get(ctx.subagentId)?.abortController.abort();
8672
- reject(new BudgetExceededError("timeout", timeoutMs, Date.now()));
8673
- }, timeoutMs);
9078
+ const armFor = (ms) => {
9079
+ if (timer) clearTimeout(timer);
9080
+ timer = setTimeout(async () => {
9081
+ const elapsed = Date.now() - start;
9082
+ const limit = budget.limits.timeoutMs ?? initialTimeoutMs;
9083
+ if (!budget.onThreshold) {
9084
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
9085
+ reject(new BudgetExceededError("timeout", limit, elapsed));
9086
+ return;
9087
+ }
9088
+ try {
9089
+ const result = budget.onThreshold({
9090
+ kind: "timeout",
9091
+ used: elapsed,
9092
+ limit,
9093
+ requestDecision: () => new Promise((resolveDecision) => {
9094
+ budget._events?.emit("budget.threshold_reached", {
9095
+ kind: "timeout",
9096
+ used: elapsed,
9097
+ limit,
9098
+ timeoutMs: 3e4,
9099
+ extend: (extra) => resolveDecision({ extend: extra }),
9100
+ deny: () => resolveDecision("stop")
9101
+ });
9102
+ })
9103
+ });
9104
+ const decision = typeof result === "string" ? result : await result;
9105
+ if (decision === "continue") {
9106
+ armFor(Math.max(1e3, limit));
9107
+ return;
9108
+ }
9109
+ if (decision === "throw" || decision === "stop") {
9110
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
9111
+ reject(new BudgetExceededError("timeout", limit, elapsed));
9112
+ return;
9113
+ }
9114
+ if (decision.extend.timeoutMs !== void 0) {
9115
+ budget.limits.timeoutMs = decision.extend.timeoutMs;
9116
+ const newLimit = decision.extend.timeoutMs;
9117
+ const remaining = Math.max(1e3, newLimit - elapsed);
9118
+ armFor(remaining);
9119
+ return;
9120
+ }
9121
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
9122
+ reject(new BudgetExceededError("timeout", limit, elapsed));
9123
+ } catch (err) {
9124
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
9125
+ reject(
9126
+ err instanceof BudgetExceededError ? err : new BudgetExceededError("timeout", limit, elapsed)
9127
+ );
9128
+ }
9129
+ }, ms);
9130
+ };
9131
+ armFor(initialTimeoutMs);
8674
9132
  });
8675
9133
  try {
8676
9134
  return await Promise.race([runner(task, ctx), timeoutPromise]);
@@ -9281,7 +9739,7 @@ var Director = class {
9281
9739
  return;
9282
9740
  }
9283
9741
  extendCounts.set(guardKey, prior + 1);
9284
- setTimeout(() => {
9742
+ setImmediate(() => {
9285
9743
  const extra = {};
9286
9744
  switch (payload.kind) {
9287
9745
  case "iterations":
@@ -9296,9 +9754,12 @@ var Director = class {
9296
9754
  case "cost":
9297
9755
  extra.maxCostUsd = Math.min(payload.limit * 1.5, 10);
9298
9756
  break;
9757
+ case "timeout":
9758
+ extra.timeoutMs = Math.min(Math.ceil(payload.limit * 1.5), 60 * 6e4);
9759
+ break;
9299
9760
  }
9300
9761
  payload.extend(extra);
9301
- }, Math.min(payload.timeoutMs, 3e4));
9762
+ });
9302
9763
  });
9303
9764
  }
9304
9765
  /** Best-effort session-writer append. Swallows failures — the director
@@ -10093,6 +10554,7 @@ function makeAgentSubagentRunner(opts) {
10093
10554
  const detachFleet = opts.fleetBus?.attach(ctx.subagentId, events, task.id);
10094
10555
  const aborter = new AbortController();
10095
10556
  ctx.budget._events = events;
10557
+ ctx.budget.onThreshold = ({ requestDecision }) => requestDecision();
10096
10558
  let budgetError = null;
10097
10559
  const onBudgetError = (err) => {
10098
10560
  if (err instanceof BudgetThresholdSignal) {
@@ -16118,16 +16580,16 @@ Use \`/security report <number>\` to view a specific report.` };
16118
16580
  }
16119
16581
  const index = parseInt(reportId, 10) - 1;
16120
16582
  if (!isNaN(index) && reports[index]) {
16121
- const { readFile: readFile29 } = await import('fs/promises');
16122
- const content = await readFile29(join(reportsDir, reports[index]), "utf-8");
16583
+ const { readFile: readFile30 } = await import('fs/promises');
16584
+ const content = await readFile30(join(reportsDir, reports[index]), "utf-8");
16123
16585
  return { message: `# Security Report
16124
16586
 
16125
16587
  ${content}` };
16126
16588
  }
16127
16589
  const match = reports.find((r) => r.includes(reportId));
16128
16590
  if (match) {
16129
- const { readFile: readFile29 } = await import('fs/promises');
16130
- const content = await readFile29(join(reportsDir, match), "utf-8");
16591
+ const { readFile: readFile30 } = await import('fs/promises');
16592
+ const content = await readFile30(join(reportsDir, match), "utf-8");
16131
16593
  return { message: `# Security Report
16132
16594
 
16133
16595
  ${content}` };
@@ -16382,6 +16844,226 @@ var FleetManager = class {
16382
16844
  return { pending, live: [] };
16383
16845
  }
16384
16846
  };
16847
+ function createMcpControlTool(opts) {
16848
+ const { getConfig, configPath, registry } = opts;
16849
+ const inputSchema = {
16850
+ type: "object",
16851
+ properties: {
16852
+ action: {
16853
+ type: "string",
16854
+ enum: ["list", "search", "enable", "disable", "restart"],
16855
+ description: "The management action to perform."
16856
+ },
16857
+ /** Filter for `search`. Matches server name or description case-insensitively. */
16858
+ query: {
16859
+ type: "string",
16860
+ description: "Search term for `search` action. Matches server name or description."
16861
+ },
16862
+ /** Target server name for `enable`, `disable`, `restart`. */
16863
+ server: {
16864
+ type: "string",
16865
+ description: 'Server name (e.g. "github", "filesystem", "brave-search").'
16866
+ }
16867
+ },
16868
+ required: ["action"]
16869
+ };
16870
+ return {
16871
+ name: "mcp_control",
16872
+ description: "Manage MCP server lifecycle: list available servers, search by name or capability, enable or disable servers at runtime, restart running servers.",
16873
+ category: "mcp",
16874
+ permission: "auto",
16875
+ mutating: false,
16876
+ riskTier: "standard",
16877
+ inputSchema,
16878
+ async execute(raw, ctx) {
16879
+ const input = raw;
16880
+ return mcpControlDispatch(input, { getConfig, configPath, registry });
16881
+ }
16882
+ };
16883
+ }
16884
+ async function mcpControlDispatch(input, deps) {
16885
+ const { action, query, server } = input;
16886
+ switch (action) {
16887
+ case "list":
16888
+ return renderList(deps);
16889
+ case "search":
16890
+ return renderSearch(query ?? "", deps);
16891
+ case "enable":
16892
+ return runEnable(server, deps);
16893
+ case "disable":
16894
+ return runDisable(server, deps);
16895
+ case "restart":
16896
+ return runRestart(server, deps);
16897
+ default:
16898
+ return `Unknown action "${action}". Use one of: list, search, enable, disable, restart.`;
16899
+ }
16900
+ }
16901
+ function renderList(deps) {
16902
+ const configured = deps.getConfig().mcpServers ?? {};
16903
+ const live = deps.registry.describe();
16904
+ if (Object.keys(configured).length === 0) {
16905
+ return [
16906
+ "No MCP servers configured.",
16907
+ ' Use `mcp_control({ action: "search" })` to see available presets,',
16908
+ ' then `mcp_control({ action: "enable", server: "<name>" })` to add one.'
16909
+ ].join("\n");
16910
+ }
16911
+ const lines = [];
16912
+ const liveMap = new Map(live.map((s) => [s.name, s]));
16913
+ for (const [name, cfg] of Object.entries(configured)) {
16914
+ const liveInfo = liveMap.get(name);
16915
+ const toolCount = liveInfo ? ` (${liveInfo.toolCount} tools)` : "";
16916
+ const stateStr = liveInfo ? badge(liveInfo.state) : dim("\u25CB not loaded");
16917
+ const enabled = cfg.enabled === false ? `${dim("disabled")} ` : `${green("\u25CF enabled")} `;
16918
+ lines.push(` ${bold(name)} ${enabled}${stateStr}${toolCount}`);
16919
+ if (cfg.description) lines.push(` ${dim(cfg.description)}`);
16920
+ }
16921
+ lines.push("");
16922
+ lines.push(dim(' Use `mcp_control({ action: "search", query: "<keyword>" })` to find servers.'));
16923
+ lines.push(dim(' Use `mcp_control({ action: "enable", server: "<name>" })` to start a server.'));
16924
+ return lines.join("\n");
16925
+ }
16926
+ function renderSearch(query, deps) {
16927
+ const configured = deps.getConfig().mcpServers ?? {};
16928
+ const all = allServers();
16929
+ const q = query.toLowerCase();
16930
+ const configuredNames = new Set(Object.keys(configured));
16931
+ const configuredEntries = Object.entries(configured).filter(
16932
+ ([name, cfg]) => name.toLowerCase().includes(q) || (cfg.description ?? "").toLowerCase().includes(q)
16933
+ );
16934
+ const unconfiguredEntries = Object.entries(all).filter(([name]) => !configuredNames.has(name)).filter(
16935
+ ([name, cfg]) => name.toLowerCase().includes(q) || (cfg.description ?? "").toLowerCase().includes(q)
16936
+ );
16937
+ const lines = [];
16938
+ if (configuredEntries.length > 0) {
16939
+ lines.push(bold('Configured servers matching "') + query + '":');
16940
+ for (const [name, cfg] of configuredEntries) {
16941
+ lines.push(` ${bold(name)} ${cfg.description ?? cfg.transport}`);
16942
+ }
16943
+ lines.push("");
16944
+ }
16945
+ if (unconfiguredEntries.length > 0) {
16946
+ lines.push(bold('Available presets matching "') + query + '":');
16947
+ for (const [name, cfg] of unconfiguredEntries) {
16948
+ const warn = cfg.permission === "deny" ? red(" \u26A0 confirm required") : "";
16949
+ lines.push(` ${bold(name)} ${cfg.description ?? cfg.transport}${warn}`);
16950
+ }
16951
+ lines.push("");
16952
+ }
16953
+ if (configuredEntries.length === 0 && unconfiguredEntries.length === 0) {
16954
+ return `No servers match "${query}". Try a shorter keyword or \`mcp_control({ action: "list" })\`.`;
16955
+ }
16956
+ const total = configuredEntries.length + unconfiguredEntries.length;
16957
+ lines.push(dim(` ${total} server${total !== 1 ? "s" : ""} shown. Run \`enable\` on one to activate it.`));
16958
+ return lines.join("\n");
16959
+ }
16960
+ async function runEnable(name, deps) {
16961
+ if (!name) return '`server` is required for enable. Example: { action: "enable", server: "github" }';
16962
+ const all = allServers();
16963
+ const configured = deps.getConfig().mcpServers ?? {};
16964
+ const cfg = configured[name] ?? all[name];
16965
+ if (!cfg) {
16966
+ const known = Object.keys(all).join(", ");
16967
+ return `Unknown server "${name}". Available presets: ${known}`;
16968
+ }
16969
+ const full = await readConfig(deps.configPath);
16970
+ const mcpServers = {
16971
+ ...full.mcpServers ?? {}
16972
+ };
16973
+ mcpServers[name] = { ...cfg, enabled: true };
16974
+ full.mcpServers = mcpServers;
16975
+ await writeConfig(deps.configPath, full);
16976
+ try {
16977
+ const live = deps.registry.describe().find((s) => s.name === name);
16978
+ if (live && live.state === "connected") {
16979
+ return `${green("\u25CF")} Server "${name}" is already running (${live.toolCount} tools registered).`;
16980
+ }
16981
+ await deps.registry.start({ ...cfg, enabled: true });
16982
+ const updated = deps.registry.describe().find((s) => s.name === name);
16983
+ return `${green("\u2713 Enabled and started")} "${name}"${updated ? ` (${updated.toolCount} tools registered).` : "."}`;
16984
+ } catch (err) {
16985
+ return `${red("\u2717 Failed to start")} "${name}": ${err instanceof Error ? err.message : String(err)}`;
16986
+ }
16987
+ }
16988
+ async function runDisable(name, deps) {
16989
+ if (!name) return '`server` is required for disable. Example: { action: "disable", server: "github" }';
16990
+ const configured = deps.getConfig().mcpServers ?? {};
16991
+ if (!configured[name]) {
16992
+ return `Server "${name}" is not in config. Add it with \`mcp_control({ action: "enable", server: "${name}" })\`.`;
16993
+ }
16994
+ const full = await readConfig(deps.configPath);
16995
+ const mcpServers = {
16996
+ ...full.mcpServers ?? {}
16997
+ };
16998
+ const existing = mcpServers[name];
16999
+ mcpServers[name] = { ...existing, enabled: false };
17000
+ full.mcpServers = mcpServers;
17001
+ await writeConfig(deps.configPath, full);
17002
+ try {
17003
+ await deps.registry.stop(name);
17004
+ return `${yellow("\u25CB Disabled")} "${name}". It will not be started on next boot.`;
17005
+ } catch {
17006
+ return `${yellow("\u25CB Disabled")} "${name}" (it was not running). Config updated.`;
17007
+ }
17008
+ }
17009
+ async function runRestart(name, deps) {
17010
+ if (!name) return '`server` is required for restart. Example: { action: "restart", server: "github" }';
17011
+ const configured = deps.getConfig().mcpServers ?? {};
17012
+ if (!configured[name]) {
17013
+ return `Server "${name}" is not configured. Use \`mcp_control({ action: "enable", server: "${name}" })\` first.`;
17014
+ }
17015
+ try {
17016
+ await deps.registry.restart(name);
17017
+ const updated = deps.registry.describe().find((s) => s.name === name);
17018
+ return `${green("\u2713 Restarted")} "${name}"${updated ? ` (${updated.toolCount} tools registered).` : "."}`;
17019
+ } catch (err) {
17020
+ return `${red("\u2717 Restart failed")} for "${name}": ${err instanceof Error ? err.message : String(err)}`;
17021
+ }
17022
+ }
17023
+ async function readConfig(p) {
17024
+ try {
17025
+ return JSON.parse(await fsp2.readFile(p, "utf8"));
17026
+ } catch {
17027
+ return {};
17028
+ }
17029
+ }
17030
+ async function writeConfig(p, cfg) {
17031
+ const raw = JSON.stringify(cfg, null, 2);
17032
+ const tmp = p + ".tmp";
17033
+ await fsp2.writeFile(tmp, raw, "utf8");
17034
+ await fsp2.rename(tmp, p);
17035
+ }
17036
+ function bold(s) {
17037
+ return `\x1B[1m${s}\x1B[0m`;
17038
+ }
17039
+ function dim(s) {
17040
+ return `\x1B[2m${s}\x1B[0m`;
17041
+ }
17042
+ function green(s) {
17043
+ return `\x1B[32m${s}\x1B[0m`;
17044
+ }
17045
+ function yellow(s) {
17046
+ return `\x1B[33m${s}\x1B[0m`;
17047
+ }
17048
+ function red(s) {
17049
+ return `\x1B[31m${s}\x1B[0m`;
17050
+ }
17051
+ function badge(state) {
17052
+ switch (state) {
17053
+ case "connected":
17054
+ return green("\u25CF connected");
17055
+ case "connecting":
17056
+ return `\x1B[36m\u25D0 connecting\x1B[0m`;
17057
+ case "reconnecting":
17058
+ return `\x1B[36m\u25D1 reconnecting\x1B[0m`;
17059
+ case "disconnected":
17060
+ return dim("\u25CB disconnected");
17061
+ case "failed":
17062
+ return red("\u2717 failed");
17063
+ default:
17064
+ return dim(state);
17065
+ }
17066
+ }
16385
17067
 
16386
17068
  // src/extension/registry.ts
16387
17069
  var ExtensionRegistry = class {
@@ -16844,6 +17526,7 @@ var Agent = class {
16844
17526
  let effectiveLimit = opts.maxIterations ?? this.maxIterations;
16845
17527
  const hasHardLimit = effectiveLimit > 0 && Number.isFinite(effectiveLimit);
16846
17528
  let recoveryRetries = 0;
17529
+ const autonomousContinue = opts.autonomousContinue ?? this.autonomousContinue;
16847
17530
  const diRunner = this.container.has(TOKENS.ProviderRunner) ? this.container.resolve(TOKENS.ProviderRunner) : null;
16848
17531
  const baseRunner = diRunner ? (ctx, req) => diRunner.run({
16849
17532
  provider: ctx.provider,
@@ -16870,7 +17553,7 @@ var Agent = class {
16870
17553
  if (controller.signal.aborted) {
16871
17554
  return { status: "aborted", iterations };
16872
17555
  }
16873
- if (this.autonomousContinue) {
17556
+ if (autonomousContinue) {
16874
17557
  consumeAutonomousContinue(this.ctx);
16875
17558
  }
16876
17559
  const limitCheck = await this.checkIterationLimit(
@@ -16949,18 +17632,18 @@ var Agent = class {
16949
17632
  const toolUses = res.content.filter(isToolUseBlock);
16950
17633
  if (toolUses.length === 0) {
16951
17634
  this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
16952
- if (this.autonomousContinue && responseResult.directive === "continue") {
17635
+ if (autonomousContinue && responseResult.directive === "continue") {
16953
17636
  await this.compactContextIfNeeded();
16954
17637
  await this.extensions.runAfterIteration(this.ctx, i);
16955
17638
  continue;
16956
17639
  }
16957
- if (this.autonomousContinue && responseResult.directive === "stop") {
17640
+ if (autonomousContinue && responseResult.directive === "stop") {
16958
17641
  return { status: "done", iterations, finalText };
16959
17642
  }
16960
17643
  return { status: "done", iterations, finalText };
16961
17644
  }
16962
17645
  await this.executeTools(toolUses);
16963
- if (this.autonomousContinue && consumeAutonomousContinue(this.ctx)) {
17646
+ if (autonomousContinue && consumeAutonomousContinue(this.ctx)) {
16964
17647
  this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
16965
17648
  await this.compactContextIfNeeded();
16966
17649
  await this.extensions.runAfterIteration(this.ctx, i);
@@ -16969,10 +17652,10 @@ var Agent = class {
16969
17652
  this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
16970
17653
  await this.compactContextIfNeeded();
16971
17654
  await this.extensions.runAfterIteration(this.ctx, i);
16972
- if (this.autonomousContinue && responseResult.directive === "continue") {
17655
+ if (autonomousContinue && responseResult.directive === "continue") {
16973
17656
  continue;
16974
17657
  }
16975
- if (this.autonomousContinue && responseResult.directive === "stop") {
17658
+ if (autonomousContinue && responseResult.directive === "stop") {
16976
17659
  return { status: "done", iterations, finalText };
16977
17660
  }
16978
17661
  }
@@ -17062,7 +17745,7 @@ var Agent = class {
17062
17745
  }
17063
17746
  }
17064
17747
  let directive = "none";
17065
- if (this.autonomousContinue && finalText) {
17748
+ if (finalText) {
17066
17749
  directive = parseContinueDirective(finalText);
17067
17750
  }
17068
17751
  return { finalText, aborted: false, done: false, directive };
@@ -18658,6 +19341,6 @@ function wrapApiForCapabilityCheck(plugin, api, log, enforce = false) {
18658
19341
  });
18659
19342
  }
18660
19343
 
18661
- export { AISpecBuilder, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, ScopedEventBus, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolError, ToolExecutor, ToolRegistry, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, atomicWrite, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildChildEnv, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, compileUserRegex, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDefaultPipelines, createDelegateTool, createMessage, createSecuritySlashCommand, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, isAgentError, isConfigError, isContextWindowModeId, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeContinueToNextIterationTool, makeDirectorSessionFactory, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, projectHash, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, securitySlashCommand, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
19344
+ export { AISpecBuilder, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, ScopedEventBus, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolError, ToolExecutor, ToolRegistry, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, atomicWrite, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, compileUserRegex, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDefaultPipelines, createDelegateTool, createMcpControlTool, createMessage, createSecuritySlashCommand, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, isAgentError, isConfigError, isContextWindowModeId, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAutonomyPromptContributor, makeContinueToNextIterationTool, makeDirectorSessionFactory, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, projectHash, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, securitySlashCommand, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
18662
19345
  //# sourceMappingURL=index.js.map
18663
19346
  //# sourceMappingURL=index.js.map