oh-my-opencode 3.0.0-beta.7 → 3.0.0-beta.8

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 (36) hide show
  1. package/README.md +7 -5
  2. package/README.zh-cn.md +5 -0
  3. package/bin/oh-my-opencode.js +80 -0
  4. package/bin/platform.js +38 -0
  5. package/bin/platform.test.ts +148 -0
  6. package/dist/agents/sisyphus-junior.d.ts +1 -1
  7. package/dist/cli/config-manager.d.ts +9 -1
  8. package/dist/cli/index.js +172 -119
  9. package/dist/config/schema.d.ts +2 -0
  10. package/dist/features/background-agent/manager.d.ts +5 -0
  11. package/dist/features/hook-message-injector/index.d.ts +1 -1
  12. package/dist/features/opencode-skill-loader/skill-content.d.ts +10 -0
  13. package/dist/features/skill-mcp-manager/manager.d.ts +10 -0
  14. package/dist/features/task-toast-manager/index.d.ts +1 -1
  15. package/dist/features/task-toast-manager/manager.d.ts +2 -1
  16. package/dist/features/task-toast-manager/types.d.ts +5 -0
  17. package/dist/hooks/comment-checker/cli.d.ts +0 -1
  18. package/dist/hooks/comment-checker/cli.test.d.ts +1 -0
  19. package/dist/hooks/index.d.ts +1 -0
  20. package/dist/hooks/sisyphus-task-retry/index.d.ts +24 -0
  21. package/dist/hooks/sisyphus-task-retry/index.test.d.ts +1 -0
  22. package/dist/index.js +2300 -413
  23. package/dist/shared/index.d.ts +2 -0
  24. package/dist/shared/session-cursor.d.ts +13 -0
  25. package/dist/shared/session-cursor.test.d.ts +1 -0
  26. package/dist/shared/shell-env.d.ts +41 -0
  27. package/dist/shared/shell-env.test.d.ts +1 -0
  28. package/dist/tools/look-at/tools.d.ts +7 -0
  29. package/dist/tools/look-at/tools.test.d.ts +1 -0
  30. package/dist/tools/lsp/client.d.ts +0 -7
  31. package/dist/tools/lsp/constants.d.ts +0 -3
  32. package/dist/tools/lsp/tools.d.ts +0 -3
  33. package/dist/tools/lsp/types.d.ts +0 -56
  34. package/dist/tools/lsp/utils.d.ts +1 -8
  35. package/package.json +18 -3
  36. package/postinstall.mjs +43 -0
package/dist/index.js CHANGED
@@ -4245,7 +4245,8 @@ var init_migration = __esm(() => {
4245
4245
  explore: "explore",
4246
4246
  "frontend-ui-ux-engineer": "frontend-ui-ux-engineer",
4247
4247
  "document-writer": "document-writer",
4248
- "multimodal-looker": "multimodal-looker"
4248
+ "multimodal-looker": "multimodal-looker",
4249
+ "orchestrator-sisyphus": "orchestrator-sisyphus"
4249
4250
  };
4250
4251
  BUILTIN_AGENT_NAMES = new Set([
4251
4252
  "Sisyphus",
@@ -4629,6 +4630,115 @@ function applyAgentVariant(config, agentName, message) {
4629
4630
  }
4630
4631
  }
4631
4632
 
4633
+ // src/shared/session-cursor.ts
4634
+ function buildMessageKey(message, index) {
4635
+ const id = message.info?.id;
4636
+ if (id)
4637
+ return `id:${id}`;
4638
+ const time = message.info?.time;
4639
+ if (typeof time === "number" || typeof time === "string") {
4640
+ return `t:${time}:${index}`;
4641
+ }
4642
+ const created = time?.created;
4643
+ if (typeof created === "number") {
4644
+ return `t:${created}:${index}`;
4645
+ }
4646
+ if (typeof created === "string") {
4647
+ return `t:${created}:${index}`;
4648
+ }
4649
+ return `i:${index}`;
4650
+ }
4651
+ function consumeNewMessages(sessionID, messages) {
4652
+ if (!sessionID)
4653
+ return messages;
4654
+ const keys = messages.map((message, index) => buildMessageKey(message, index));
4655
+ const cursor = sessionCursors.get(sessionID);
4656
+ let startIndex = 0;
4657
+ if (cursor) {
4658
+ if (cursor.lastCount > messages.length) {
4659
+ startIndex = 0;
4660
+ } else if (cursor.lastKey) {
4661
+ const lastIndex = keys.lastIndexOf(cursor.lastKey);
4662
+ if (lastIndex >= 0) {
4663
+ startIndex = lastIndex + 1;
4664
+ } else {
4665
+ startIndex = 0;
4666
+ }
4667
+ }
4668
+ }
4669
+ if (messages.length === 0) {
4670
+ sessionCursors.delete(sessionID);
4671
+ } else {
4672
+ sessionCursors.set(sessionID, {
4673
+ lastKey: keys[keys.length - 1],
4674
+ lastCount: messages.length
4675
+ });
4676
+ }
4677
+ return messages.slice(startIndex);
4678
+ }
4679
+ function resetMessageCursor(sessionID) {
4680
+ if (sessionID) {
4681
+ sessionCursors.delete(sessionID);
4682
+ return;
4683
+ }
4684
+ sessionCursors.clear();
4685
+ }
4686
+ var sessionCursors;
4687
+ var init_session_cursor = __esm(() => {
4688
+ sessionCursors = new Map;
4689
+ });
4690
+
4691
+ // src/shared/shell-env.ts
4692
+ function detectShellType() {
4693
+ if (process.env.PSModulePath) {
4694
+ return "powershell";
4695
+ }
4696
+ if (process.env.SHELL) {
4697
+ return "unix";
4698
+ }
4699
+ return process.platform === "win32" ? "cmd" : "unix";
4700
+ }
4701
+ function shellEscape(value, shellType) {
4702
+ if (value === "") {
4703
+ return shellType === "cmd" ? '""' : "''";
4704
+ }
4705
+ switch (shellType) {
4706
+ case "unix":
4707
+ if (/[^a-zA-Z0-9_\-.:\/]/.test(value)) {
4708
+ return `'${value.replace(/'/g, "'\\''")}'`;
4709
+ }
4710
+ return value;
4711
+ case "powershell":
4712
+ return `'${value.replace(/'/g, "''")}'`;
4713
+ case "cmd":
4714
+ return `"${value.replace(/%/g, "%%").replace(/"/g, '""')}"`;
4715
+ default:
4716
+ return value;
4717
+ }
4718
+ }
4719
+ function buildEnvPrefix(env, shellType) {
4720
+ const entries = Object.entries(env);
4721
+ if (entries.length === 0) {
4722
+ return "";
4723
+ }
4724
+ switch (shellType) {
4725
+ case "unix": {
4726
+ const assignments = entries.map(([key, value]) => `${key}=${shellEscape(value, shellType)}`).join(" ");
4727
+ return `export ${assignments};`;
4728
+ }
4729
+ case "powershell": {
4730
+ const assignments = entries.map(([key, value]) => `$env:${key}=${shellEscape(value, shellType)}`).join("; ");
4731
+ return `${assignments};`;
4732
+ }
4733
+ case "cmd": {
4734
+ const assignments = entries.map(([key, value]) => `set ${key}=${shellEscape(value, shellType)}`).join(" && ");
4735
+ return `${assignments} &&`;
4736
+ }
4737
+ default:
4738
+ return "";
4739
+ }
4740
+ }
4741
+
4632
4742
  // src/shared/index.ts
4633
4743
  var init_shared = __esm(() => {
4634
4744
  init_frontmatter();
@@ -4651,6 +4761,7 @@ var init_shared = __esm(() => {
4651
4761
  init_permission_compat();
4652
4762
  init_external_plugin_detector();
4653
4763
  init_zip_extractor();
4764
+ init_session_cursor();
4654
4765
  });
4655
4766
 
4656
4767
  // node_modules/picomatch/lib/constants.js
@@ -14097,7 +14208,7 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
14097
14208
  }
14098
14209
  }).catch(() => {});
14099
14210
  }
14100
- async function injectContinuation(sessionID, incompleteCount, total) {
14211
+ async function injectContinuation(sessionID, incompleteCount, total, resolvedInfo) {
14101
14212
  const state2 = sessions.get(sessionID);
14102
14213
  if (state2?.isRecovering) {
14103
14214
  log(`[${HOOK_NAME}] Skipped injection: in recovery`, { sessionID });
@@ -14121,29 +14232,37 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
14121
14232
  log(`[${HOOK_NAME}] Skipped injection: no incomplete todos`, { sessionID });
14122
14233
  return;
14123
14234
  }
14124
- const messageDir = getMessageDir(sessionID);
14125
- const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
14126
- const agentName = prevMessage?.agent;
14235
+ let agentName = resolvedInfo?.agent;
14236
+ let model = resolvedInfo?.model;
14237
+ let tools = resolvedInfo?.tools;
14238
+ if (!agentName || !model) {
14239
+ const messageDir = getMessageDir(sessionID);
14240
+ const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
14241
+ agentName = agentName ?? prevMessage?.agent;
14242
+ model = model ?? (prevMessage?.model?.providerID && prevMessage?.model?.modelID ? { providerID: prevMessage.model.providerID, modelID: prevMessage.model.modelID } : undefined);
14243
+ tools = tools ?? prevMessage?.tools;
14244
+ }
14127
14245
  if (agentName && skipAgents.includes(agentName)) {
14128
14246
  log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
14129
14247
  return;
14130
14248
  }
14131
- const editPermission = prevMessage?.tools?.edit;
14132
- const writePermission = prevMessage?.tools?.write;
14133
- const hasWritePermission = !prevMessage?.tools || editPermission !== false && editPermission !== "deny" && (writePermission !== false && writePermission !== "deny");
14249
+ const editPermission = tools?.edit;
14250
+ const writePermission = tools?.write;
14251
+ const hasWritePermission = !tools || editPermission !== false && editPermission !== "deny" && (writePermission !== false && writePermission !== "deny");
14134
14252
  if (!hasWritePermission) {
14135
- log(`[${HOOK_NAME}] Skipped: agent lacks write permission`, { sessionID, agent: prevMessage?.agent });
14253
+ log(`[${HOOK_NAME}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
14136
14254
  return;
14137
14255
  }
14138
14256
  const prompt = `${CONTINUATION_PROMPT}
14139
14257
 
14140
14258
  [Status: ${todos.length - freshIncompleteCount}/${todos.length} completed, ${freshIncompleteCount} remaining]`;
14141
14259
  try {
14142
- log(`[${HOOK_NAME}] Injecting continuation`, { sessionID, agent: prevMessage?.agent, incompleteCount: freshIncompleteCount });
14260
+ log(`[${HOOK_NAME}] Injecting continuation`, { sessionID, agent: agentName, model, incompleteCount: freshIncompleteCount });
14143
14261
  await ctx.client.session.prompt({
14144
14262
  path: { id: sessionID },
14145
14263
  body: {
14146
- agent: prevMessage?.agent,
14264
+ agent: agentName,
14265
+ ...model !== undefined ? { model } : {},
14147
14266
  parts: [{ type: "text", text: prompt }]
14148
14267
  },
14149
14268
  query: { directory: ctx.directory }
@@ -14153,7 +14272,7 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
14153
14272
  log(`[${HOOK_NAME}] Injection failed`, { sessionID, error: String(err) });
14154
14273
  }
14155
14274
  }
14156
- function startCountdown(sessionID, incompleteCount, total) {
14275
+ function startCountdown(sessionID, incompleteCount, total, resolvedInfo) {
14157
14276
  const state2 = getState(sessionID);
14158
14277
  cancelCountdown(sessionID);
14159
14278
  let secondsRemaining = COUNTDOWN_SECONDS;
@@ -14167,7 +14286,7 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
14167
14286
  }, 1000);
14168
14287
  state2.countdownTimer = setTimeout(() => {
14169
14288
  cancelCountdown(sessionID);
14170
- injectContinuation(sessionID, incompleteCount, total);
14289
+ injectContinuation(sessionID, incompleteCount, total, resolvedInfo);
14171
14290
  }, COUNTDOWN_SECONDS * 1000);
14172
14291
  log(`[${HOOK_NAME}] Countdown started`, { sessionID, seconds: COUNTDOWN_SECONDS, incompleteCount });
14173
14292
  }
@@ -14249,27 +14368,32 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
14249
14368
  log(`[${HOOK_NAME}] All todos complete`, { sessionID, total: todos.length });
14250
14369
  return;
14251
14370
  }
14252
- let agentName;
14371
+ let resolvedInfo;
14253
14372
  try {
14254
14373
  const messagesResp = await ctx.client.session.messages({
14255
14374
  path: { id: sessionID }
14256
14375
  });
14257
14376
  const messages = messagesResp.data ?? [];
14258
14377
  for (let i = messages.length - 1;i >= 0; i--) {
14259
- if (messages[i].info?.agent) {
14260
- agentName = messages[i].info?.agent;
14378
+ const info = messages[i].info;
14379
+ if (info?.agent || info?.model) {
14380
+ resolvedInfo = {
14381
+ agent: info.agent,
14382
+ model: info.model,
14383
+ tools: info.tools
14384
+ };
14261
14385
  break;
14262
14386
  }
14263
14387
  }
14264
14388
  } catch (err) {
14265
14389
  log(`[${HOOK_NAME}] Failed to fetch messages for agent check`, { sessionID, error: String(err) });
14266
14390
  }
14267
- log(`[${HOOK_NAME}] Agent check`, { sessionID, agentName, skipAgents });
14268
- if (agentName && skipAgents.includes(agentName)) {
14269
- log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
14391
+ log(`[${HOOK_NAME}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents });
14392
+ if (resolvedInfo?.agent && skipAgents.includes(resolvedInfo.agent)) {
14393
+ log(`[${HOOK_NAME}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedInfo.agent });
14270
14394
  return;
14271
14395
  }
14272
- startCountdown(sessionID, incompleteCount, todos.length);
14396
+ startCountdown(sessionID, incompleteCount, todos.length, resolvedInfo);
14273
14397
  return;
14274
14398
  }
14275
14399
  if (event.type === "message.updated") {
@@ -15510,6 +15634,9 @@ async function getCommentCheckerPath() {
15510
15634
  })();
15511
15635
  return initPromise;
15512
15636
  }
15637
+ function getCommentCheckerPathSync() {
15638
+ return resolvedCliPath ?? findCommentCheckerPathSync();
15639
+ }
15513
15640
  function startBackgroundInit() {
15514
15641
  if (!initPromise) {
15515
15642
  initPromise = getCommentCheckerPath();
@@ -15520,9 +15647,8 @@ function startBackgroundInit() {
15520
15647
  });
15521
15648
  }
15522
15649
  }
15523
- var COMMENT_CHECKER_CLI_PATH = findCommentCheckerPathSync();
15524
15650
  async function runCommentChecker(input, cliPath, customPrompt) {
15525
- const binaryPath = cliPath ?? resolvedCliPath ?? COMMENT_CHECKER_CLI_PATH;
15651
+ const binaryPath = cliPath ?? resolvedCliPath ?? getCommentCheckerPathSync();
15526
15652
  if (!binaryPath) {
15527
15653
  debugLog2("comment-checker binary not found");
15528
15654
  return { hasComments: false, message: "" };
@@ -15698,8 +15824,6 @@ var TRUNCATABLE_TOOLS = [
15698
15824
  "glob",
15699
15825
  "Glob",
15700
15826
  "safe_glob",
15701
- "lsp_find_references",
15702
- "lsp_symbols",
15703
15827
  "lsp_diagnostics",
15704
15828
  "ast_grep_search",
15705
15829
  "interactive_bash",
@@ -19901,7 +20025,7 @@ THE USER ASKED FOR X. DELIVER EXACTLY X. NOT A SUBSET. NOT A DEMO. NOT A STARTIN
19901
20025
  }
19902
20026
  var KEYWORD_DETECTORS = [
19903
20027
  {
19904
- pattern: /(ultrawork|ulw)/i,
20028
+ pattern: /\b(ultrawork|ulw)\b/i,
19905
20029
  message: getUltraworkMessage
19906
20030
  },
19907
20031
  {
@@ -20067,6 +20191,23 @@ EOF`,
20067
20191
  }
20068
20192
  };
20069
20193
 
20194
+ // src/hooks/non-interactive-env/detector.ts
20195
+ function isNonInteractive() {
20196
+ if (process.env.CI === "true" || process.env.CI === "1") {
20197
+ return true;
20198
+ }
20199
+ if (process.env.OPENCODE_RUN === "true" || process.env.OPENCODE_NON_INTERACTIVE === "true") {
20200
+ return true;
20201
+ }
20202
+ if (process.env.GITHUB_ACTIONS === "true") {
20203
+ return true;
20204
+ }
20205
+ if (process.stdout.isTTY !== true) {
20206
+ return true;
20207
+ }
20208
+ return false;
20209
+ }
20210
+
20070
20211
  // src/hooks/non-interactive-env/index.ts
20071
20212
  init_shared();
20072
20213
  var BANNED_COMMAND_PATTERNS = SHELL_COMMAND_PATTERNS.banned.filter((cmd) => !cmd.includes("(")).map((cmd) => new RegExp(`\\b${cmd}\\b`));
@@ -20078,18 +20219,6 @@ function detectBannedCommand(command) {
20078
20219
  }
20079
20220
  return;
20080
20221
  }
20081
- function shellEscape(value) {
20082
- if (value === "")
20083
- return "''";
20084
- if (/[^a-zA-Z0-9_\-.:\/]/.test(value)) {
20085
- return `'${value.replace(/'/g, "'\\''")}'`;
20086
- }
20087
- return value;
20088
- }
20089
- function buildEnvPrefix(env) {
20090
- const exports = Object.entries(env).map(([key, value]) => `${key}=${shellEscape(value)}`).join(" ");
20091
- return `export ${exports};`;
20092
- }
20093
20222
  function createNonInteractiveEnvHook(_ctx) {
20094
20223
  return {
20095
20224
  "tool.execute.before": async (input, output) => {
@@ -20108,7 +20237,11 @@ function createNonInteractiveEnvHook(_ctx) {
20108
20237
  if (!isGitCommand) {
20109
20238
  return;
20110
20239
  }
20111
- const envPrefix = buildEnvPrefix(NON_INTERACTIVE_ENV);
20240
+ if (!isNonInteractive()) {
20241
+ return;
20242
+ }
20243
+ const shellType = detectShellType();
20244
+ const envPrefix = buildEnvPrefix(NON_INTERACTIVE_ENV, shellType);
20112
20245
  output.args.command = `${envPrefix} ${command}`;
20113
20246
  log(`[${HOOK_NAME2}] Prepended non-interactive env vars to git command`, {
20114
20247
  sessionID: input.sessionID,
@@ -20819,10 +20952,25 @@ function createRalphLoopHook(ctx, options) {
20819
20952
  }
20820
20953
  }).catch(() => {});
20821
20954
  try {
20822
- const messageDir = getMessageDir9(sessionID);
20823
- const currentMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
20824
- const agent = currentMessage?.agent;
20825
- const model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
20955
+ let agent;
20956
+ let model;
20957
+ try {
20958
+ const messagesResp = await ctx.client.session.messages({ path: { id: sessionID } });
20959
+ const messages = messagesResp.data ?? [];
20960
+ for (let i2 = messages.length - 1;i2 >= 0; i2--) {
20961
+ const info = messages[i2].info;
20962
+ if (info?.agent || info?.model) {
20963
+ agent = info.agent;
20964
+ model = info.model;
20965
+ break;
20966
+ }
20967
+ }
20968
+ } catch {
20969
+ const messageDir = getMessageDir9(sessionID);
20970
+ const currentMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
20971
+ agent = currentMessage?.agent;
20972
+ model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
20973
+ }
20826
20974
  await ctx.client.session.prompt({
20827
20975
  path: { id: sessionID },
20828
20976
  body: {
@@ -20935,7 +21083,7 @@ function extractPromptText3(parts) {
20935
21083
  // src/hooks/auto-slash-command/executor.ts
20936
21084
  init_shared();
20937
21085
  init_file_utils();
20938
- import { existsSync as existsSync36, readdirSync as readdirSync12, readFileSync as readFileSync24 } from "fs";
21086
+ import { existsSync as existsSync36, readdirSync as readdirSync12, readFileSync as readFileSync25 } from "fs";
20939
21087
  import { join as join45, basename as basename2, dirname as dirname8 } from "path";
20940
21088
  import { homedir as homedir14 } from "os";
20941
21089
  // src/features/opencode-skill-loader/loader.ts
@@ -22573,6 +22721,46 @@ function createBuiltinSkills() {
22573
22721
  }
22574
22722
 
22575
22723
  // src/features/opencode-skill-loader/skill-content.ts
22724
+ init_frontmatter();
22725
+ import { readFileSync as readFileSync24 } from "fs";
22726
+ var cachedSkills = null;
22727
+ async function getAllSkills() {
22728
+ if (cachedSkills)
22729
+ return cachedSkills;
22730
+ const [discoveredSkills, builtinSkillDefs] = await Promise.all([
22731
+ discoverSkills({ includeClaudeCodePaths: true }),
22732
+ Promise.resolve(createBuiltinSkills())
22733
+ ]);
22734
+ const builtinSkillsAsLoaded = builtinSkillDefs.map((skill) => ({
22735
+ name: skill.name,
22736
+ definition: {
22737
+ name: skill.name,
22738
+ description: skill.description,
22739
+ template: skill.template,
22740
+ model: skill.model,
22741
+ agent: skill.agent,
22742
+ subtask: skill.subtask
22743
+ },
22744
+ scope: "builtin",
22745
+ license: skill.license,
22746
+ compatibility: skill.compatibility,
22747
+ metadata: skill.metadata,
22748
+ allowedTools: skill.allowedTools,
22749
+ mcpConfig: skill.mcpConfig
22750
+ }));
22751
+ const discoveredNames = new Set(discoveredSkills.map((s) => s.name));
22752
+ const uniqueBuiltins = builtinSkillsAsLoaded.filter((s) => !discoveredNames.has(s.name));
22753
+ cachedSkills = [...discoveredSkills, ...uniqueBuiltins];
22754
+ return cachedSkills;
22755
+ }
22756
+ async function extractSkillTemplate(skill) {
22757
+ if (skill.path) {
22758
+ const content = readFileSync24(skill.path, "utf-8");
22759
+ const { body } = parseFrontmatter(content);
22760
+ return body.trim();
22761
+ }
22762
+ return skill.definition.template || "";
22763
+ }
22576
22764
  function injectGitMasterConfig(template, config) {
22577
22765
  if (!config)
22578
22766
  return template;
@@ -22608,6 +22796,29 @@ function resolveMultipleSkills(skillNames, options) {
22608
22796
  }
22609
22797
  return { resolved, notFound };
22610
22798
  }
22799
+ async function resolveMultipleSkillsAsync(skillNames, options) {
22800
+ const allSkills = await getAllSkills();
22801
+ const skillMap = new Map;
22802
+ for (const skill of allSkills) {
22803
+ skillMap.set(skill.name, skill);
22804
+ }
22805
+ const resolved = new Map;
22806
+ const notFound = [];
22807
+ for (const name of skillNames) {
22808
+ const skill = skillMap.get(name);
22809
+ if (skill) {
22810
+ const template = await extractSkillTemplate(skill);
22811
+ if (name === "git-master" && options?.gitMasterConfig) {
22812
+ resolved.set(name, injectGitMasterConfig(template, options.gitMasterConfig));
22813
+ } else {
22814
+ resolved.set(name, template);
22815
+ }
22816
+ } else {
22817
+ notFound.push(name);
22818
+ }
22819
+ }
22820
+ return { resolved, notFound };
22821
+ }
22611
22822
  // src/hooks/auto-slash-command/executor.ts
22612
22823
  function discoverCommandsFromDir(commandsDir, scope) {
22613
22824
  if (!existsSync36(commandsDir)) {
@@ -22621,7 +22832,7 @@ function discoverCommandsFromDir(commandsDir, scope) {
22621
22832
  const commandPath = join45(commandsDir, entry.name);
22622
22833
  const commandName = basename2(entry.name, ".md");
22623
22834
  try {
22624
- const content = readFileSync24(commandPath, "utf-8");
22835
+ const content = readFileSync25(commandPath, "utf-8");
22625
22836
  const { data, body } = parseFrontmatter(content);
22626
22837
  const isOpencodeSource = scope === "opencode" || scope === "opencode-project";
22627
22838
  const metadata = {
@@ -23001,7 +23212,7 @@ var NOTEPAD_DIR = "notepads";
23001
23212
  var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
23002
23213
  var PROMETHEUS_PLANS_DIR = ".sisyphus/plans";
23003
23214
  // src/features/boulder-state/storage.ts
23004
- import { existsSync as existsSync38, readFileSync as readFileSync25, writeFileSync as writeFileSync15, mkdirSync as mkdirSync11, readdirSync as readdirSync14 } from "fs";
23215
+ import { existsSync as existsSync38, readFileSync as readFileSync26, writeFileSync as writeFileSync15, mkdirSync as mkdirSync11, readdirSync as readdirSync14 } from "fs";
23005
23216
  import { dirname as dirname9, join as join47, basename as basename3 } from "path";
23006
23217
  function getBoulderFilePath(directory) {
23007
23218
  return join47(directory, BOULDER_DIR, BOULDER_FILE);
@@ -23012,7 +23223,7 @@ function readBoulderState(directory) {
23012
23223
  return null;
23013
23224
  }
23014
23225
  try {
23015
- const content = readFileSync25(filePath, "utf-8");
23226
+ const content = readFileSync26(filePath, "utf-8");
23016
23227
  return JSON.parse(content);
23017
23228
  } catch {
23018
23229
  return null;
@@ -23043,6 +23254,18 @@ function appendSessionId(directory, sessionId) {
23043
23254
  }
23044
23255
  return state2;
23045
23256
  }
23257
+ function clearBoulderState(directory) {
23258
+ const filePath = getBoulderFilePath(directory);
23259
+ try {
23260
+ if (existsSync38(filePath)) {
23261
+ const { unlinkSync: unlinkSync10 } = __require("fs");
23262
+ unlinkSync10(filePath);
23263
+ }
23264
+ return true;
23265
+ } catch {
23266
+ return false;
23267
+ }
23268
+ }
23046
23269
  function findPrometheusPlans(directory) {
23047
23270
  const plansDir = join47(directory, PROMETHEUS_PLANS_DIR);
23048
23271
  if (!existsSync38(plansDir)) {
@@ -23064,7 +23287,7 @@ function getPlanProgress(planPath) {
23064
23287
  return { total: 0, completed: 0, isComplete: true };
23065
23288
  }
23066
23289
  try {
23067
- const content = readFileSync25(planPath, "utf-8");
23290
+ const content = readFileSync26(planPath, "utf-8");
23068
23291
  const uncheckedMatches = content.match(/^[-*]\s*\[\s*\]/gm) || [];
23069
23292
  const checkedMatches = content.match(/^[-*]\s*\[[xX]\]/gm) || [];
23070
23293
  const total = uncheckedMatches.length + checkedMatches.length;
@@ -23092,6 +23315,25 @@ function createBoulderState(planPath, sessionId) {
23092
23315
  // src/hooks/start-work/index.ts
23093
23316
  init_logger();
23094
23317
  var HOOK_NAME5 = "start-work";
23318
+ var KEYWORD_PATTERN = /\b(ultrawork|ulw)\b/gi;
23319
+ function extractUserRequestPlanName(promptText) {
23320
+ const userRequestMatch = promptText.match(/<user-request>\s*([\s\S]*?)\s*<\/user-request>/i);
23321
+ if (!userRequestMatch)
23322
+ return null;
23323
+ const rawArg = userRequestMatch[1].trim();
23324
+ if (!rawArg)
23325
+ return null;
23326
+ const cleanedArg = rawArg.replace(KEYWORD_PATTERN, "").trim();
23327
+ return cleanedArg || null;
23328
+ }
23329
+ function findPlanByName(plans, requestedName) {
23330
+ const lowerName = requestedName.toLowerCase();
23331
+ const exactMatch = plans.find((p) => getPlanName(p).toLowerCase() === lowerName);
23332
+ if (exactMatch)
23333
+ return exactMatch;
23334
+ const partialMatch = plans.find((p) => getPlanName(p).toLowerCase().includes(lowerName));
23335
+ return partialMatch || null;
23336
+ }
23095
23337
  function createStartWorkHook(ctx) {
23096
23338
  return {
23097
23339
  "chat.message": async (input, output) => {
@@ -23109,7 +23351,64 @@ function createStartWorkHook(ctx) {
23109
23351
  const sessionId = input.sessionID;
23110
23352
  const timestamp2 = new Date().toISOString();
23111
23353
  let contextInfo = "";
23112
- if (existingState) {
23354
+ const explicitPlanName = extractUserRequestPlanName(promptText);
23355
+ if (explicitPlanName) {
23356
+ log(`[${HOOK_NAME5}] Explicit plan name requested: ${explicitPlanName}`, {
23357
+ sessionID: input.sessionID
23358
+ });
23359
+ const allPlans = findPrometheusPlans(ctx.directory);
23360
+ const matchedPlan = findPlanByName(allPlans, explicitPlanName);
23361
+ if (matchedPlan) {
23362
+ const progress = getPlanProgress(matchedPlan);
23363
+ if (progress.isComplete) {
23364
+ contextInfo = `
23365
+ ## Plan Already Complete
23366
+
23367
+ The requested plan "${getPlanName(matchedPlan)}" has been completed.
23368
+ All ${progress.total} tasks are done. Create a new plan with: /plan "your task"`;
23369
+ } else {
23370
+ if (existingState) {
23371
+ clearBoulderState(ctx.directory);
23372
+ }
23373
+ const newState = createBoulderState(matchedPlan, sessionId);
23374
+ writeBoulderState(ctx.directory, newState);
23375
+ contextInfo = `
23376
+ ## Auto-Selected Plan
23377
+
23378
+ **Plan**: ${getPlanName(matchedPlan)}
23379
+ **Path**: ${matchedPlan}
23380
+ **Progress**: ${progress.completed}/${progress.total} tasks
23381
+ **Session ID**: ${sessionId}
23382
+ **Started**: ${timestamp2}
23383
+
23384
+ boulder.json has been created. Read the plan and begin execution.`;
23385
+ }
23386
+ } else {
23387
+ const incompletePlans = allPlans.filter((p) => !getPlanProgress(p).isComplete);
23388
+ if (incompletePlans.length > 0) {
23389
+ const planList = incompletePlans.map((p, i2) => {
23390
+ const prog = getPlanProgress(p);
23391
+ return `${i2 + 1}. [${getPlanName(p)}] - Progress: ${prog.completed}/${prog.total}`;
23392
+ }).join(`
23393
+ `);
23394
+ contextInfo = `
23395
+ ## Plan Not Found
23396
+
23397
+ Could not find a plan matching "${explicitPlanName}".
23398
+
23399
+ Available incomplete plans:
23400
+ ${planList}
23401
+
23402
+ Ask the user which plan to work on.`;
23403
+ } else {
23404
+ contextInfo = `
23405
+ ## Plan Not Found
23406
+
23407
+ Could not find a plan matching "${explicitPlanName}".
23408
+ No incomplete plans available. Create a new plan with: /plan "your task"`;
23409
+ }
23410
+ }
23411
+ } else if (existingState) {
23113
23412
  const progress = getPlanProgress(existingState.active_plan);
23114
23413
  if (!progress.isComplete) {
23115
23414
  appendSessionId(ctx.directory, sessionId);
@@ -23133,7 +23432,7 @@ The previous plan (${existingState.plan_name}) has been completed.
23133
23432
  Looking for new plans...`;
23134
23433
  }
23135
23434
  }
23136
- if (!existingState || getPlanProgress(existingState.active_plan).isComplete) {
23435
+ if (!existingState && !explicitPlanName || existingState && !explicitPlanName && getPlanProgress(existingState.active_plan).isComplete) {
23137
23436
  const plans = findPrometheusPlans(ctx.directory);
23138
23437
  const incompletePlans = plans.filter((p) => !getPlanProgress(p).isComplete);
23139
23438
  if (plans.length === 0) {
@@ -23249,34 +23548,45 @@ RULES:
23249
23548
  - Use the notepad at .sisyphus/notepads/{PLAN_NAME}/ to record learnings
23250
23549
  - Do not stop until all tasks are complete
23251
23550
  - If blocked, document the blocker and move to the next task`;
23252
- var VERIFICATION_REMINDER = `**MANDATORY VERIFICATION - SUBAGENTS LIE**
23551
+ var VERIFICATION_REMINDER = `**MANDATORY: WHAT YOU MUST DO RIGHT NOW**
23552
+
23553
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
23253
23554
 
23254
- Subagents FREQUENTLY claim completion when:
23255
- - Tests are actually FAILING
23256
- - Code has type/lint ERRORS
23257
- - Implementation is INCOMPLETE
23258
- - Patterns were NOT followed
23555
+ \u26A0\uFE0F CRITICAL: Subagents FREQUENTLY LIE about completion.
23556
+ Tests FAILING, code has ERRORS, implementation INCOMPLETE - but they say "done".
23259
23557
 
23260
- **YOU MUST VERIFY EVERYTHING YOURSELF:**
23558
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
23261
23559
 
23262
- 1. Run \`lsp_diagnostics\` on changed files - Must be CLEAN
23263
- 2. Run tests yourself - Must PASS (not "agent said it passed")
23264
- 3. Read the actual code - Must match requirements
23265
- 4. Check build/typecheck - Must succeed
23560
+ **STEP 1: VERIFY WITH YOUR OWN TOOL CALLS (DO THIS NOW)**
23266
23561
 
23267
- DO NOT TRUST THE AGENT'S SELF-REPORT.
23268
- VERIFY EACH CLAIM WITH YOUR OWN TOOL CALLS.
23562
+ Run these commands YOURSELF - do NOT trust agent's claims:
23563
+ 1. \`lsp_diagnostics\` on changed files \u2192 Must be CLEAN
23564
+ 2. \`bash\` to run tests \u2192 Must PASS
23565
+ 3. \`bash\` to run build/typecheck \u2192 Must succeed
23566
+ 4. \`Read\` the actual code \u2192 Must match requirements
23269
23567
 
23270
- **HANDS-ON QA REQUIRED (after ALL tasks complete):**
23568
+ **STEP 2: DETERMINE IF HANDS-ON QA IS NEEDED**
23271
23569
 
23272
- | Deliverable Type | Verification Tool | Action |
23273
- |------------------|-------------------|--------|
23274
- | **Frontend/UI** | \`/playwright\` skill | Navigate, interact, screenshot evidence |
23275
- | **TUI/CLI** | \`interactive_bash\` (tmux) | Run interactively, verify output |
23276
- | **API/Backend** | \`bash\` with curl | Send requests, verify responses |
23570
+ | Deliverable Type | QA Method | Tool |
23571
+ |------------------|-----------|------|
23572
+ | **Frontend/UI** | Browser interaction | \`/playwright\` skill |
23573
+ | **TUI/CLI** | Run interactively | \`interactive_bash\` (tmux) |
23574
+ | **API/Backend** | Send real requests | \`bash\` with curl |
23277
23575
 
23278
- Static analysis CANNOT catch: visual bugs, animation issues, user flow breakages, integration problems.
23279
- **FAILURE TO DO HANDS-ON QA = INCOMPLETE WORK.**`;
23576
+ Static analysis CANNOT catch: visual bugs, animation issues, user flow breakages.
23577
+
23578
+ **STEP 3: IF QA IS NEEDED - ADD TO TODO IMMEDIATELY**
23579
+
23580
+ \`\`\`
23581
+ todowrite([
23582
+ { id: "qa-X", content: "HANDS-ON QA: [specific verification action]", status: "pending", priority: "high" }
23583
+ ])
23584
+ \`\`\`
23585
+
23586
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
23587
+
23588
+ **BLOCKING: DO NOT proceed to next task until Steps 1-3 are complete.**
23589
+ **FAILURE TO DO QA = INCOMPLETE WORK = USER WILL REJECT.**`;
23280
23590
  var ORCHESTRATOR_DELEGATION_REQUIRED = `
23281
23591
 
23282
23592
  ---
@@ -23365,19 +23675,37 @@ function buildOrchestratorReminder(planName, progress, sessionId) {
23365
23675
  return `
23366
23676
  ---
23367
23677
 
23368
- **State:** Plan: ${planName} | ${progress.completed}/${progress.total} done, ${remaining} left
23678
+ **BOULDER STATE:** Plan: \`${planName}\` | \u2705 ${progress.completed}/${progress.total} done | \u23F3 ${remaining} remaining
23369
23679
 
23370
23680
  ---
23371
23681
 
23372
23682
  ${buildVerificationReminder(sessionId)}
23373
23683
 
23374
- ALL pass? \u2192 commit atomic unit, mark \`[x]\`, next task.`;
23684
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
23685
+
23686
+ **AFTER VERIFICATION PASSES - YOUR NEXT ACTIONS (IN ORDER):**
23687
+
23688
+ 1. **COMMIT** atomic unit (only verified changes)
23689
+ 2. **MARK** \`[x]\` in plan file for completed task
23690
+ 3. **PROCEED** to next task immediately
23691
+
23692
+ **DO NOT STOP. ${remaining} tasks remain. Keep bouldering.**`;
23375
23693
  }
23376
23694
  function buildStandaloneVerificationReminder(sessionId) {
23377
23695
  return `
23378
23696
  ---
23379
23697
 
23380
- ${buildVerificationReminder(sessionId)}`;
23698
+ ${buildVerificationReminder(sessionId)}
23699
+
23700
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
23701
+
23702
+ **AFTER VERIFICATION - CHECK YOUR TODO LIST:**
23703
+
23704
+ 1. Run \`todoread\` to see remaining tasks
23705
+ 2. If QA tasks exist \u2192 execute them BEFORE marking complete
23706
+ 3. Mark completed tasks \u2192 proceed to next pending task
23707
+
23708
+ **NO TODO = NO TRACKING = INCOMPLETE WORK. Use todowrite aggressively.**`;
23381
23709
  }
23382
23710
  function extractSessionIdFromOutput(output) {
23383
23711
  const match = output.match(/Session ID:\s*(ses_[a-zA-Z0-9]+)/);
@@ -23539,9 +23867,22 @@ function createSisyphusOrchestratorHook(ctx, options) {
23539
23867
  [Status: ${total - remaining}/${total} completed, ${remaining} remaining]`;
23540
23868
  try {
23541
23869
  log(`[${HOOK_NAME6}] Injecting boulder continuation`, { sessionID, planName, remaining });
23542
- const messageDir = getMessageDir11(sessionID);
23543
- const currentMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
23544
- const model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
23870
+ let model;
23871
+ try {
23872
+ const messagesResp = await ctx.client.session.messages({ path: { id: sessionID } });
23873
+ const messages = messagesResp.data ?? [];
23874
+ for (let i2 = messages.length - 1;i2 >= 0; i2--) {
23875
+ const msgModel = messages[i2].info?.model;
23876
+ if (msgModel?.providerID && msgModel?.modelID) {
23877
+ model = { providerID: msgModel.providerID, modelID: msgModel.modelID };
23878
+ break;
23879
+ }
23880
+ }
23881
+ } catch {
23882
+ const messageDir = getMessageDir11(sessionID);
23883
+ const currentMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
23884
+ model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
23885
+ }
23545
23886
  await ctx.client.session.prompt({
23546
23887
  path: { id: sessionID },
23547
23888
  body: {
@@ -23762,6 +24103,118 @@ ${buildStandaloneVerificationReminder(subagentSessionId)}
23762
24103
  }
23763
24104
  };
23764
24105
  }
24106
+ // src/hooks/sisyphus-task-retry/index.ts
24107
+ var SISYPHUS_TASK_ERROR_PATTERNS = [
24108
+ {
24109
+ pattern: "run_in_background",
24110
+ errorType: "missing_run_in_background",
24111
+ fixHint: "Add run_in_background=false (for delegation) or run_in_background=true (for parallel exploration)"
24112
+ },
24113
+ {
24114
+ pattern: "skills",
24115
+ errorType: "missing_skills",
24116
+ fixHint: "Add skills=[] parameter (empty array if no skills needed)"
24117
+ },
24118
+ {
24119
+ pattern: "category OR subagent_type",
24120
+ errorType: "mutual_exclusion",
24121
+ fixHint: "Provide ONLY one of: category (e.g., 'general', 'quick') OR subagent_type (e.g., 'oracle', 'explore')"
24122
+ },
24123
+ {
24124
+ pattern: "Must provide either category or subagent_type",
24125
+ errorType: "missing_category_or_agent",
24126
+ fixHint: "Add either category='general' OR subagent_type='explore'"
24127
+ },
24128
+ {
24129
+ pattern: "Unknown category",
24130
+ errorType: "unknown_category",
24131
+ fixHint: "Use a valid category from the Available list in the error message"
24132
+ },
24133
+ {
24134
+ pattern: "Agent name cannot be empty",
24135
+ errorType: "empty_agent",
24136
+ fixHint: "Provide a non-empty subagent_type value"
24137
+ },
24138
+ {
24139
+ pattern: "Unknown agent",
24140
+ errorType: "unknown_agent",
24141
+ fixHint: "Use a valid agent from the Available agents list in the error message"
24142
+ },
24143
+ {
24144
+ pattern: "Cannot call primary agent",
24145
+ errorType: "primary_agent",
24146
+ fixHint: "Primary agents cannot be called via sisyphus_task. Use a subagent like 'explore', 'oracle', or 'librarian'"
24147
+ },
24148
+ {
24149
+ pattern: "Skills not found",
24150
+ errorType: "unknown_skills",
24151
+ fixHint: "Use valid skill names from the Available list in the error message"
24152
+ }
24153
+ ];
24154
+ function detectSisyphusTaskError(output) {
24155
+ if (!output.includes("\u274C"))
24156
+ return null;
24157
+ for (const errorPattern of SISYPHUS_TASK_ERROR_PATTERNS) {
24158
+ if (output.includes(errorPattern.pattern)) {
24159
+ return {
24160
+ errorType: errorPattern.errorType,
24161
+ originalOutput: output
24162
+ };
24163
+ }
24164
+ }
24165
+ return null;
24166
+ }
24167
+ function extractAvailableList(output) {
24168
+ const availableMatch = output.match(/Available[^:]*:\s*(.+)$/m);
24169
+ return availableMatch ? availableMatch[1].trim() : null;
24170
+ }
24171
+ function buildRetryGuidance(errorInfo) {
24172
+ const pattern = SISYPHUS_TASK_ERROR_PATTERNS.find((p) => p.errorType === errorInfo.errorType);
24173
+ if (!pattern) {
24174
+ return `[sisyphus_task ERROR] Fix the error and retry with correct parameters.`;
24175
+ }
24176
+ let guidance = `
24177
+ [sisyphus_task CALL FAILED - IMMEDIATE RETRY REQUIRED]
24178
+
24179
+ **Error Type**: ${errorInfo.errorType}
24180
+ **Fix**: ${pattern.fixHint}
24181
+ `;
24182
+ const availableList = extractAvailableList(errorInfo.originalOutput);
24183
+ if (availableList) {
24184
+ guidance += `
24185
+ **Available Options**: ${availableList}
24186
+ `;
24187
+ }
24188
+ guidance += `
24189
+ **Action**: Retry sisyphus_task NOW with corrected parameters.
24190
+
24191
+ Example of CORRECT call:
24192
+ \`\`\`
24193
+ sisyphus_task(
24194
+ description="Task description",
24195
+ prompt="Detailed prompt...",
24196
+ category="general", // OR subagent_type="explore"
24197
+ run_in_background=false,
24198
+ skills=[]
24199
+ )
24200
+ \`\`\`
24201
+ `;
24202
+ return guidance;
24203
+ }
24204
+ function createSisyphusTaskRetryHook(_ctx) {
24205
+ return {
24206
+ "tool.execute.after": async (input, output) => {
24207
+ if (input.tool.toLowerCase() !== "sisyphus_task")
24208
+ return;
24209
+ const errorInfo = detectSisyphusTaskError(output.output);
24210
+ if (errorInfo) {
24211
+ const guidance = buildRetryGuidance(errorInfo);
24212
+ output.output += `
24213
+ ${guidance}`;
24214
+ }
24215
+ }
24216
+ };
24217
+ }
23765
24218
  // src/features/context-injector/collector.ts
23766
24219
  var PRIORITY_ORDER = {
23767
24220
  critical: 0,
@@ -23962,7 +24415,7 @@ function createFirstMessageVariantGate() {
23962
24415
  }
23963
24416
  // src/features/claude-code-mcp-loader/loader.ts
23964
24417
  init_shared();
23965
- import { existsSync as existsSync40, readFileSync as readFileSync26 } from "fs";
24418
+ import { existsSync as existsSync40, readFileSync as readFileSync27 } from "fs";
23966
24419
  import { join as join49 } from "path";
23967
24420
 
23968
24421
  // src/features/claude-code-mcp-loader/env-expander.ts
@@ -24057,7 +24510,7 @@ function getSystemMcpServerNames() {
24057
24510
  if (!existsSync40(path8))
24058
24511
  continue;
24059
24512
  try {
24060
- const content = readFileSync26(path8, "utf-8");
24513
+ const content = readFileSync27(path8, "utf-8");
24061
24514
  const config = JSON.parse(content);
24062
24515
  if (!config?.mcpServers)
24063
24516
  continue;
@@ -24102,42 +24555,12 @@ async function loadMcpConfigs() {
24102
24555
  return { servers, loadedServers };
24103
24556
  }
24104
24557
  // src/tools/lsp/constants.ts
24105
- var SYMBOL_KIND_MAP = {
24106
- 1: "File",
24107
- 2: "Module",
24108
- 3: "Namespace",
24109
- 4: "Package",
24110
- 5: "Class",
24111
- 6: "Method",
24112
- 7: "Property",
24113
- 8: "Field",
24114
- 9: "Constructor",
24115
- 10: "Enum",
24116
- 11: "Interface",
24117
- 12: "Function",
24118
- 13: "Variable",
24119
- 14: "Constant",
24120
- 15: "String",
24121
- 16: "Number",
24122
- 17: "Boolean",
24123
- 18: "Array",
24124
- 19: "Object",
24125
- 20: "Key",
24126
- 21: "Null",
24127
- 22: "EnumMember",
24128
- 23: "Struct",
24129
- 24: "Event",
24130
- 25: "Operator",
24131
- 26: "TypeParameter"
24132
- };
24133
24558
  var SEVERITY_MAP = {
24134
24559
  1: "error",
24135
24560
  2: "warning",
24136
24561
  3: "information",
24137
24562
  4: "hint"
24138
24563
  };
24139
- var DEFAULT_MAX_REFERENCES = 200;
24140
- var DEFAULT_MAX_SYMBOLS = 200;
24141
24564
  var DEFAULT_MAX_DIAGNOSTICS = 200;
24142
24565
  var LSP_INSTALL_HINTS = {
24143
24566
  typescript: "npm install -g typescript-language-server typescript",
@@ -24178,7 +24601,8 @@ var LSP_INSTALL_HINTS = {
24178
24601
  nixd: "nix profile install nixpkgs#nixd",
24179
24602
  tinymist: "See https://github.com/Myriad-Dreamin/tinymist",
24180
24603
  "haskell-language-server": "ghcup install hls",
24181
- bash: "npm install -g bash-language-server"
24604
+ bash: "npm install -g bash-language-server",
24605
+ "kotlin-ls": "See https://github.com/Kotlin/kotlin-lsp"
24182
24606
  };
24183
24607
  var BUILTIN_SERVERS = {
24184
24608
  typescript: {
@@ -24354,6 +24778,10 @@ var BUILTIN_SERVERS = {
24354
24778
  "haskell-language-server": {
24355
24779
  command: ["haskell-language-server-wrapper", "--lsp"],
24356
24780
  extensions: [".hs", ".lhs"]
24781
+ },
24782
+ "kotlin-ls": {
24783
+ command: ["kotlin-lsp"],
24784
+ extensions: [".kt", ".kts"]
24357
24785
  }
24358
24786
  };
24359
24787
  var EXT_TO_LANG = {
@@ -24489,14 +24917,14 @@ var EXT_TO_LANG = {
24489
24917
  ".gql": "graphql"
24490
24918
  };
24491
24919
  // src/tools/lsp/config.ts
24492
- import { existsSync as existsSync41, readFileSync as readFileSync27 } from "fs";
24920
+ import { existsSync as existsSync41, readFileSync as readFileSync28 } from "fs";
24493
24921
  import { join as join50 } from "path";
24494
24922
  import { homedir as homedir15 } from "os";
24495
24923
  function loadJsonFile(path8) {
24496
24924
  if (!existsSync41(path8))
24497
24925
  return null;
24498
24926
  try {
24499
- return JSON.parse(readFileSync27(path8, "utf-8"));
24927
+ return JSON.parse(readFileSync28(path8, "utf-8"));
24500
24928
  } catch {
24501
24929
  return null;
24502
24930
  }
@@ -24695,7 +25123,7 @@ function getAllServers() {
24695
25123
  }
24696
25124
  // src/tools/lsp/client.ts
24697
25125
  var {spawn: spawn6 } = globalThis.Bun;
24698
- import { readFileSync as readFileSync28 } from "fs";
25126
+ import { readFileSync as readFileSync29 } from "fs";
24699
25127
  import { extname, resolve as resolve8 } from "path";
24700
25128
  import { pathToFileURL } from "url";
24701
25129
  class LSPServerManager {
@@ -25145,7 +25573,7 @@ ${msg}`);
25145
25573
  const absPath = resolve8(filePath);
25146
25574
  if (this.openedFiles.has(absPath))
25147
25575
  return;
25148
- const text = readFileSync28(absPath, "utf-8");
25576
+ const text = readFileSync29(absPath, "utf-8");
25149
25577
  const ext = extname(absPath);
25150
25578
  const languageId = getLanguageId(ext);
25151
25579
  this.notify("textDocument/didOpen", {
@@ -25159,41 +25587,6 @@ ${msg}`);
25159
25587
  this.openedFiles.add(absPath);
25160
25588
  await new Promise((r) => setTimeout(r, 1000));
25161
25589
  }
25162
- async hover(filePath, line, character) {
25163
- const absPath = resolve8(filePath);
25164
- await this.openFile(absPath);
25165
- return this.send("textDocument/hover", {
25166
- textDocument: { uri: pathToFileURL(absPath).href },
25167
- position: { line: line - 1, character }
25168
- });
25169
- }
25170
- async definition(filePath, line, character) {
25171
- const absPath = resolve8(filePath);
25172
- await this.openFile(absPath);
25173
- return this.send("textDocument/definition", {
25174
- textDocument: { uri: pathToFileURL(absPath).href },
25175
- position: { line: line - 1, character }
25176
- });
25177
- }
25178
- async references(filePath, line, character, includeDeclaration = true) {
25179
- const absPath = resolve8(filePath);
25180
- await this.openFile(absPath);
25181
- return this.send("textDocument/references", {
25182
- textDocument: { uri: pathToFileURL(absPath).href },
25183
- position: { line: line - 1, character },
25184
- context: { includeDeclaration }
25185
- });
25186
- }
25187
- async documentSymbols(filePath) {
25188
- const absPath = resolve8(filePath);
25189
- await this.openFile(absPath);
25190
- return this.send("textDocument/documentSymbol", {
25191
- textDocument: { uri: pathToFileURL(absPath).href }
25192
- });
25193
- }
25194
- async workspaceSymbols(query) {
25195
- return this.send("workspace/symbol", { query });
25196
- }
25197
25590
  async diagnostics(filePath) {
25198
25591
  const absPath = resolve8(filePath);
25199
25592
  const uri = pathToFileURL(absPath).href;
@@ -25226,24 +25619,6 @@ ${msg}`);
25226
25619
  newName
25227
25620
  });
25228
25621
  }
25229
- async codeAction(filePath, startLine, startChar, endLine, endChar, only) {
25230
- const absPath = resolve8(filePath);
25231
- await this.openFile(absPath);
25232
- return this.send("textDocument/codeAction", {
25233
- textDocument: { uri: pathToFileURL(absPath).href },
25234
- range: {
25235
- start: { line: startLine - 1, character: startChar },
25236
- end: { line: endLine - 1, character: endChar }
25237
- },
25238
- context: {
25239
- diagnostics: [],
25240
- only
25241
- }
25242
- });
25243
- }
25244
- async codeActionResolve(codeAction) {
25245
- return this.send("codeAction/resolve", codeAction);
25246
- }
25247
25622
  isAlive() {
25248
25623
  return this.proc !== null && !this.processExited && this.proc.exitCode === null;
25249
25624
  }
@@ -25261,7 +25636,7 @@ ${msg}`);
25261
25636
  // src/tools/lsp/utils.ts
25262
25637
  import { extname as extname2, resolve as resolve9 } from "path";
25263
25638
  import { fileURLToPath as fileURLToPath2 } from "url";
25264
- import { existsSync as existsSync42, readFileSync as readFileSync29, writeFileSync as writeFileSync16 } from "fs";
25639
+ import { existsSync as existsSync42, readFileSync as readFileSync30, writeFileSync as writeFileSync16 } from "fs";
25265
25640
  function findWorkspaceRoot(filePath) {
25266
25641
  let dir = resolve9(filePath);
25267
25642
  if (!existsSync42(dir) || !__require("fs").statSync(dir).isDirectory()) {
@@ -25341,45 +25716,11 @@ async function withLspClient(filePath, fn) {
25341
25716
  lspManager.releaseClient(root, server.id);
25342
25717
  }
25343
25718
  }
25344
- function formatLocation(loc) {
25345
- if ("targetUri" in loc) {
25346
- const uri2 = uriToPath(loc.targetUri);
25347
- const line2 = loc.targetRange.start.line + 1;
25348
- const char2 = loc.targetRange.start.character;
25349
- return `${uri2}:${line2}:${char2}`;
25350
- }
25351
- const uri = uriToPath(loc.uri);
25352
- const line = loc.range.start.line + 1;
25353
- const char = loc.range.start.character;
25354
- return `${uri}:${line}:${char}`;
25355
- }
25356
- function formatSymbolKind(kind) {
25357
- return SYMBOL_KIND_MAP[kind] || `Unknown(${kind})`;
25358
- }
25359
25719
  function formatSeverity(severity) {
25360
25720
  if (!severity)
25361
25721
  return "unknown";
25362
25722
  return SEVERITY_MAP[severity] || `unknown(${severity})`;
25363
25723
  }
25364
- function formatDocumentSymbol(symbol, indent = 0) {
25365
- const prefix = " ".repeat(indent);
25366
- const kind = formatSymbolKind(symbol.kind);
25367
- const line = symbol.range.start.line + 1;
25368
- let result = `${prefix}${symbol.name} (${kind}) - line ${line}`;
25369
- if (symbol.children && symbol.children.length > 0) {
25370
- for (const child of symbol.children) {
25371
- result += `
25372
- ` + formatDocumentSymbol(child, indent + 1);
25373
- }
25374
- }
25375
- return result;
25376
- }
25377
- function formatSymbolInfo(symbol) {
25378
- const kind = formatSymbolKind(symbol.kind);
25379
- const loc = formatLocation(symbol.location);
25380
- const container = symbol.containerName ? ` (in ${symbol.containerName})` : "";
25381
- return `${symbol.name} (${kind})${container} - ${loc}`;
25382
- }
25383
25724
  function formatDiagnostic(diag) {
25384
25725
  const severity = formatSeverity(diag.severity);
25385
25726
  const line = diag.range.start.line + 1;
@@ -25426,7 +25767,7 @@ function formatPrepareRenameResult(result) {
25426
25767
  }
25427
25768
  function applyTextEditsToFile(filePath, edits) {
25428
25769
  try {
25429
- let content = readFileSync29(filePath, "utf-8");
25770
+ let content = readFileSync30(filePath, "utf-8");
25430
25771
  const lines = content.split(`
25431
25772
  `);
25432
25773
  const sortedEdits = [...edits].sort((a, b) => {
@@ -25492,7 +25833,7 @@ function applyWorkspaceEdit(edit) {
25492
25833
  try {
25493
25834
  const oldPath = uriToPath(change.oldUri);
25494
25835
  const newPath = uriToPath(change.newUri);
25495
- const content = readFileSync29(oldPath, "utf-8");
25836
+ const content = readFileSync30(oldPath, "utf-8");
25496
25837
  writeFileSync16(newPath, content, "utf-8");
25497
25838
  __require("fs").unlinkSync(oldPath);
25498
25839
  result.filesModified.push(newPath);
@@ -37866,128 +38207,6 @@ function tool(input) {
37866
38207
  tool.schema = exports_external;
37867
38208
 
37868
38209
  // src/tools/lsp/tools.ts
37869
- var lsp_goto_definition = tool({
37870
- description: "Jump to symbol definition. Find WHERE something is defined.",
37871
- args: {
37872
- filePath: tool.schema.string(),
37873
- line: tool.schema.number().min(1).describe("1-based"),
37874
- character: tool.schema.number().min(0).describe("0-based")
37875
- },
37876
- execute: async (args, context) => {
37877
- try {
37878
- const result = await withLspClient(args.filePath, async (client) => {
37879
- return await client.definition(args.filePath, args.line, args.character);
37880
- });
37881
- if (!result) {
37882
- const output2 = "No definition found";
37883
- return output2;
37884
- }
37885
- const locations = Array.isArray(result) ? result : [result];
37886
- if (locations.length === 0) {
37887
- const output2 = "No definition found";
37888
- return output2;
37889
- }
37890
- const output = locations.map(formatLocation).join(`
37891
- `);
37892
- return output;
37893
- } catch (e) {
37894
- const output = `Error: ${e instanceof Error ? e.message : String(e)}`;
37895
- return output;
37896
- }
37897
- }
37898
- });
37899
- var lsp_find_references = tool({
37900
- description: "Find ALL usages/references of a symbol across the entire workspace.",
37901
- args: {
37902
- filePath: tool.schema.string(),
37903
- line: tool.schema.number().min(1).describe("1-based"),
37904
- character: tool.schema.number().min(0).describe("0-based"),
37905
- includeDeclaration: tool.schema.boolean().optional().describe("Include the declaration itself")
37906
- },
37907
- execute: async (args, context) => {
37908
- try {
37909
- const result = await withLspClient(args.filePath, async (client) => {
37910
- return await client.references(args.filePath, args.line, args.character, args.includeDeclaration ?? true);
37911
- });
37912
- if (!result || result.length === 0) {
37913
- const output2 = "No references found";
37914
- return output2;
37915
- }
37916
- const total = result.length;
37917
- const truncated = total > DEFAULT_MAX_REFERENCES;
37918
- const limited = truncated ? result.slice(0, DEFAULT_MAX_REFERENCES) : result;
37919
- const lines = limited.map(formatLocation);
37920
- if (truncated) {
37921
- lines.unshift(`Found ${total} references (showing first ${DEFAULT_MAX_REFERENCES}):`);
37922
- }
37923
- const output = lines.join(`
37924
- `);
37925
- return output;
37926
- } catch (e) {
37927
- const output = `Error: ${e instanceof Error ? e.message : String(e)}`;
37928
- return output;
37929
- }
37930
- }
37931
- });
37932
- var lsp_symbols = tool({
37933
- description: "Get symbols from file (document) or search across workspace. Use scope='document' for file outline, scope='workspace' for project-wide symbol search.",
37934
- args: {
37935
- filePath: tool.schema.string().describe("File path for LSP context"),
37936
- scope: tool.schema.enum(["document", "workspace"]).default("document").describe("'document' for file symbols, 'workspace' for project-wide search"),
37937
- query: tool.schema.string().optional().describe("Symbol name to search (required for workspace scope)"),
37938
- limit: tool.schema.number().optional().describe("Max results (default 50)")
37939
- },
37940
- execute: async (args, context) => {
37941
- try {
37942
- const scope = args.scope ?? "document";
37943
- if (scope === "workspace") {
37944
- if (!args.query) {
37945
- return "Error: 'query' is required for workspace scope";
37946
- }
37947
- const result = await withLspClient(args.filePath, async (client) => {
37948
- return await client.workspaceSymbols(args.query);
37949
- });
37950
- if (!result || result.length === 0) {
37951
- return "No symbols found";
37952
- }
37953
- const total = result.length;
37954
- const limit = Math.min(args.limit ?? DEFAULT_MAX_SYMBOLS, DEFAULT_MAX_SYMBOLS);
37955
- const truncated = total > limit;
37956
- const limited = result.slice(0, limit);
37957
- const lines = limited.map(formatSymbolInfo);
37958
- if (truncated) {
37959
- lines.unshift(`Found ${total} symbols (showing first ${limit}):`);
37960
- }
37961
- return lines.join(`
37962
- `);
37963
- } else {
37964
- const result = await withLspClient(args.filePath, async (client) => {
37965
- return await client.documentSymbols(args.filePath);
37966
- });
37967
- if (!result || result.length === 0) {
37968
- return "No symbols found";
37969
- }
37970
- const total = result.length;
37971
- const limit = Math.min(args.limit ?? DEFAULT_MAX_SYMBOLS, DEFAULT_MAX_SYMBOLS);
37972
- const truncated = total > limit;
37973
- const limited = truncated ? result.slice(0, limit) : result;
37974
- const lines = [];
37975
- if (truncated) {
37976
- lines.push(`Found ${total} symbols (showing first ${limit}):`);
37977
- }
37978
- if ("range" in limited[0]) {
37979
- lines.push(...limited.map((s) => formatDocumentSymbol(s)));
37980
- } else {
37981
- lines.push(...limited.map(formatSymbolInfo));
37982
- }
37983
- return lines.join(`
37984
- `);
37985
- }
37986
- } catch (e) {
37987
- return `Error: ${e instanceof Error ? e.message : String(e)}`;
37988
- }
37989
- }
37990
- });
37991
38210
  var lsp_diagnostics = tool({
37992
38211
  description: "Get errors, warnings, hints from language server BEFORE running build.",
37993
38212
  args: {
@@ -39222,7 +39441,7 @@ var glob = tool({
39222
39441
  init_shared();
39223
39442
  init_file_utils();
39224
39443
  init_shared();
39225
- import { existsSync as existsSync48, readdirSync as readdirSync17, readFileSync as readFileSync30 } from "fs";
39444
+ import { existsSync as existsSync48, readdirSync as readdirSync17, readFileSync as readFileSync31 } from "fs";
39226
39445
  import { join as join55, basename as basename4, dirname as dirname12 } from "path";
39227
39446
  function discoverCommandsFromDir2(commandsDir, scope) {
39228
39447
  if (!existsSync48(commandsDir)) {
@@ -39236,7 +39455,7 @@ function discoverCommandsFromDir2(commandsDir, scope) {
39236
39455
  const commandPath = join55(commandsDir, entry.name);
39237
39456
  const commandName = basename4(entry.name, ".md");
39238
39457
  try {
39239
- const content = readFileSync30(commandPath, "utf-8");
39458
+ const content = readFileSync31(commandPath, "utf-8");
39240
39459
  const { data, body } = parseFrontmatter(content);
39241
39460
  const isOpencodeSource = scope === "opencode" || scope === "opencode-project";
39242
39461
  const metadata = {
@@ -39363,7 +39582,7 @@ ${commandListForDescription}
39363
39582
  }
39364
39583
  function createSlashcommandTool(options = {}) {
39365
39584
  let cachedCommands = options.commands ?? null;
39366
- let cachedSkills = options.skills ?? null;
39585
+ let cachedSkills2 = options.skills ?? null;
39367
39586
  let cachedDescription = null;
39368
39587
  const getCommands = () => {
39369
39588
  if (cachedCommands)
@@ -39372,10 +39591,10 @@ function createSlashcommandTool(options = {}) {
39372
39591
  return cachedCommands;
39373
39592
  };
39374
39593
  const getSkills = async () => {
39375
- if (cachedSkills)
39376
- return cachedSkills;
39377
- cachedSkills = await discoverAllSkills();
39378
- return cachedSkills;
39594
+ if (cachedSkills2)
39595
+ return cachedSkills2;
39596
+ cachedSkills2 = await discoverAllSkills();
39597
+ return cachedSkills2;
39379
39598
  };
39380
39599
  const getAllItems = async () => {
39381
39600
  const commands = getCommands();
@@ -40116,7 +40335,28 @@ var interactive_bash = tool({
40116
40335
  }
40117
40336
  const subcommand = parts[0].toLowerCase();
40118
40337
  if (BLOCKED_TMUX_SUBCOMMANDS.includes(subcommand)) {
40119
- return `Error: '${parts[0]}' is blocked. Use bash tool instead for capturing/printing terminal output.`;
40338
+ const sessionIdx = parts.findIndex((p) => p === "-t" || p.startsWith("-t"));
40339
+ let sessionName = "omo-session";
40340
+ if (sessionIdx !== -1) {
40341
+ if (parts[sessionIdx] === "-t" && parts[sessionIdx + 1]) {
40342
+ sessionName = parts[sessionIdx + 1];
40343
+ } else if (parts[sessionIdx].startsWith("-t")) {
40344
+ sessionName = parts[sessionIdx].slice(2);
40345
+ }
40346
+ }
40347
+ return `Error: '${parts[0]}' is blocked in interactive_bash.
40348
+
40349
+ **USE BASH TOOL INSTEAD:**
40350
+
40351
+ \`\`\`bash
40352
+ # Capture terminal output
40353
+ tmux capture-pane -p -t ${sessionName}
40354
+
40355
+ # Or capture with history (last 1000 lines)
40356
+ tmux capture-pane -p -t ${sessionName} -S -1000
40357
+ \`\`\`
40358
+
40359
+ The Bash tool can execute these commands directly. Do NOT retry with interactive_bash.`;
40120
40360
  }
40121
40361
  const proc = Bun.spawn([tmuxPath2, ...parts], {
40122
40362
  stdout: "pipe",
@@ -40155,8 +40395,6 @@ Skills provide specialized knowledge and step-by-step guidance.
40155
40395
  Use this when a task matches an available skill's description.`;
40156
40396
  // src/tools/skill/tools.ts
40157
40397
  import { dirname as dirname13 } from "path";
40158
- import { readFileSync as readFileSync31 } from "fs";
40159
- init_frontmatter();
40160
40398
  function loadedSkillToInfo(skill) {
40161
40399
  return {
40162
40400
  name: skill.name,
@@ -40199,9 +40437,7 @@ async function extractSkillBody(skill) {
40199
40437
  return templateMatch2 ? templateMatch2[1].trim() : fullTemplate;
40200
40438
  }
40201
40439
  if (skill.path) {
40202
- const content = readFileSync31(skill.path, "utf-8");
40203
- const { body } = parseFrontmatter(content);
40204
- return body.trim();
40440
+ return extractSkillTemplate(skill);
40205
40441
  }
40206
40442
  const templateMatch = skill.definition.template?.match(/<skill-instruction>([\s\S]*?)<\/skill-instruction>/);
40207
40443
  return templateMatch ? templateMatch[1].trim() : skill.definition.template || "";
@@ -40267,15 +40503,15 @@ async function formatMcpCapabilities(skill, manager, sessionID) {
40267
40503
  `);
40268
40504
  }
40269
40505
  function createSkillTool(options = {}) {
40270
- let cachedSkills = null;
40506
+ let cachedSkills2 = null;
40271
40507
  let cachedDescription = null;
40272
40508
  const getSkills = async () => {
40273
40509
  if (options.skills)
40274
40510
  return options.skills;
40275
- if (cachedSkills)
40276
- return cachedSkills;
40277
- cachedSkills = await discoverSkills({ includeClaudeCodePaths: !options.opencodeOnly });
40278
- return cachedSkills;
40511
+ if (cachedSkills2)
40512
+ return cachedSkills2;
40513
+ cachedSkills2 = await getAllSkills();
40514
+ return cachedSkills2;
40279
40515
  };
40280
40516
  const getDescription = async () => {
40281
40517
  if (cachedDescription)
@@ -40474,6 +40710,7 @@ var BACKGROUND_CANCEL_DESCRIPTION = `Cancel running background task(s). Use all=
40474
40710
 
40475
40711
  // src/tools/background-task/tools.ts
40476
40712
  init_logger();
40713
+ init_session_cursor();
40477
40714
  function formatDuration(start, end) {
40478
40715
  const duration3 = (end ?? new Date).getTime() - start.getTime();
40479
40716
  const seconds = Math.floor(duration3 / 1000);
@@ -40580,8 +40817,22 @@ Session ID: ${task.sessionID}
40580
40817
  const timeB = String(b.info?.time ?? "");
40581
40818
  return timeA.localeCompare(timeB);
40582
40819
  });
40820
+ const newMessages = consumeNewMessages(task.sessionID, sortedMessages);
40821
+ if (newMessages.length === 0) {
40822
+ const duration4 = formatDuration(task.startedAt, task.completedAt);
40823
+ return `Task Result
40824
+
40825
+ Task ID: ${task.id}
40826
+ Description: ${task.description}
40827
+ Duration: ${duration4}
40828
+ Session ID: ${task.sessionID}
40829
+
40830
+ ---
40831
+
40832
+ (No new output since last check)`;
40833
+ }
40583
40834
  const extractedContent = [];
40584
- for (const message of sortedMessages) {
40835
+ for (const message of newMessages) {
40585
40836
  for (const part of message.parts ?? []) {
40586
40837
  if ((part.type === "text" || part.type === "reasoning") && part.text) {
40587
40838
  extractedContent.push(part.text);
@@ -40735,6 +40986,7 @@ Pass \`resume=session_id\` to continue previous agent with full context. Prompts
40735
40986
  import { existsSync as existsSync50, readdirSync as readdirSync19 } from "fs";
40736
40987
  import { join as join58 } from "path";
40737
40988
  init_logger();
40989
+ init_session_cursor();
40738
40990
  function getMessageDir13(sessionID) {
40739
40991
  if (!existsSync50(MESSAGE_STORAGE))
40740
40992
  return null;
@@ -40966,8 +41218,16 @@ session_id: ${sessionID}
40966
41218
  const timeB = b.info?.time?.created ?? 0;
40967
41219
  return timeA - timeB;
40968
41220
  });
41221
+ const newMessages = consumeNewMessages(sessionID, sortedMessages);
41222
+ if (newMessages.length === 0) {
41223
+ return `No new output since last check.
41224
+
41225
+ <task_metadata>
41226
+ session_id: ${sessionID}
41227
+ </task_metadata>`;
41228
+ }
40969
41229
  const extractedContent = [];
40970
- for (const message of sortedMessages) {
41230
+ for (const message of newMessages) {
40971
41231
  for (const part of message.parts ?? []) {
40972
41232
  if ((part.type === "text" || part.type === "reasoning") && part.text) {
40973
41233
  extractedContent.push(part.text);
@@ -41002,6 +41262,21 @@ var LOOK_AT_DESCRIPTION = `Analyze media files (PDFs, images, diagrams) that req
41002
41262
  import { extname as extname3, basename as basename5 } from "path";
41003
41263
  import { pathToFileURL as pathToFileURL2 } from "url";
41004
41264
  init_logger();
41265
+ function normalizeArgs(args) {
41266
+ return {
41267
+ file_path: args.file_path ?? args.path ?? "",
41268
+ goal: args.goal ?? ""
41269
+ };
41270
+ }
41271
+ function validateArgs(args) {
41272
+ if (!args.file_path) {
41273
+ return `Error: Missing required parameter 'file_path'. Usage: look_at(file_path="/path/to/file", goal="what to extract")`;
41274
+ }
41275
+ if (!args.goal) {
41276
+ return `Error: Missing required parameter 'goal'. Usage: look_at(file_path="/path/to/file", goal="what to extract")`;
41277
+ }
41278
+ return null;
41279
+ }
41005
41280
  function inferMimeType(filePath) {
41006
41281
  const ext = extname3(filePath).toLowerCase();
41007
41282
  const mimeTypes = {
@@ -41046,7 +41321,13 @@ function createLookAt(ctx) {
41046
41321
  file_path: tool.schema.string().describe("Absolute path to the file to analyze"),
41047
41322
  goal: tool.schema.string().describe("What specific information to extract from the file")
41048
41323
  },
41049
- async execute(args, toolContext) {
41324
+ async execute(rawArgs, toolContext) {
41325
+ const args = normalizeArgs(rawArgs);
41326
+ const validationError = validateArgs(args);
41327
+ if (validationError) {
41328
+ log(`[look_at] Validation failed: ${validationError}`);
41329
+ return validationError;
41330
+ }
41050
41331
  log(`[look_at] Analyzing file: ${args.file_path}, goal: ${args.goal}`);
41051
41332
  const mimeType = inferMimeType(args.file_path);
41052
41333
  const filename = basename5(args.file_path);
@@ -41143,7 +41424,8 @@ class TaskToastManager {
41143
41424
  status: task.status ?? "running",
41144
41425
  startedAt: new Date,
41145
41426
  isBackground: task.isBackground,
41146
- skills: task.skills
41427
+ skills: task.skills,
41428
+ modelInfo: task.modelInfo
41147
41429
  };
41148
41430
  this.tasks.set(task.id, trackedTask);
41149
41431
  this.showTaskListToast(trackedTask);
@@ -41190,6 +41472,17 @@ class TaskToastManager {
41190
41472
  const queued = this.getQueuedTasks();
41191
41473
  const concurrencyInfo = this.getConcurrencyInfo();
41192
41474
  const lines = [];
41475
+ if (newTask.modelInfo && newTask.modelInfo.type !== "user-defined") {
41476
+ const icon = "\u26A0\uFE0F";
41477
+ const suffixMap = {
41478
+ inherited: " (inherited)",
41479
+ "category-default": " (category default)",
41480
+ "system-default": " (system default)"
41481
+ };
41482
+ const suffix = suffixMap[newTask.modelInfo.type] ?? "";
41483
+ lines.push(`${icon} Model: ${newTask.modelInfo.model}${suffix}`);
41484
+ lines.push("");
41485
+ }
41193
41486
  if (running.length > 0) {
41194
41487
  lines.push(`Running (${running.length}):${concurrencyInfo}`);
41195
41488
  for (const task of running) {
@@ -41296,17 +41589,55 @@ function formatDuration2(start, end) {
41296
41589
  return `${minutes}m ${seconds % 60}s`;
41297
41590
  return `${seconds}s`;
41298
41591
  }
41299
- function resolveCategoryConfig(categoryName, userCategories) {
41592
+ function formatDetailedError(error45, ctx) {
41593
+ const message = error45 instanceof Error ? error45.message : String(error45);
41594
+ const stack = error45 instanceof Error ? error45.stack : undefined;
41595
+ const lines = [
41596
+ `\u274C ${ctx.operation} failed`,
41597
+ "",
41598
+ `**Error**: ${message}`
41599
+ ];
41600
+ if (ctx.sessionID) {
41601
+ lines.push(`**Session ID**: ${ctx.sessionID}`);
41602
+ }
41603
+ if (ctx.agent) {
41604
+ lines.push(`**Agent**: ${ctx.agent}${ctx.category ? ` (category: ${ctx.category})` : ""}`);
41605
+ }
41606
+ if (ctx.args) {
41607
+ lines.push("", "**Arguments**:");
41608
+ lines.push(`- description: "${ctx.args.description}"`);
41609
+ lines.push(`- category: ${ctx.args.category ?? "(none)"}`);
41610
+ lines.push(`- subagent_type: ${ctx.args.subagent_type ?? "(none)"}`);
41611
+ lines.push(`- run_in_background: ${ctx.args.run_in_background}`);
41612
+ lines.push(`- skills: [${ctx.args.skills?.join(", ") ?? ""}]`);
41613
+ if (ctx.args.resume) {
41614
+ lines.push(`- resume: ${ctx.args.resume}`);
41615
+ }
41616
+ }
41617
+ if (stack) {
41618
+ lines.push("", "**Stack Trace**:");
41619
+ lines.push("```");
41620
+ lines.push(stack.split(`
41621
+ `).slice(0, 10).join(`
41622
+ `));
41623
+ lines.push("```");
41624
+ }
41625
+ return lines.join(`
41626
+ `);
41627
+ }
41628
+ function resolveCategoryConfig(categoryName, options) {
41629
+ const { userCategories, parentModelString, systemDefaultModel } = options;
41300
41630
  const defaultConfig = DEFAULT_CATEGORIES[categoryName];
41301
41631
  const userConfig = userCategories?.[categoryName];
41302
41632
  const defaultPromptAppend = CATEGORY_PROMPT_APPENDS[categoryName] ?? "";
41303
41633
  if (!defaultConfig && !userConfig) {
41304
41634
  return null;
41305
41635
  }
41636
+ const model = userConfig?.model ?? parentModelString ?? defaultConfig?.model ?? systemDefaultModel;
41306
41637
  const config3 = {
41307
41638
  ...defaultConfig,
41308
41639
  ...userConfig,
41309
- model: userConfig?.model ?? defaultConfig?.model ?? "anthropic/claude-sonnet-4-5"
41640
+ model
41310
41641
  };
41311
41642
  let promptAppend = defaultPromptAppend;
41312
41643
  if (userConfig?.prompt_append) {
@@ -41314,7 +41645,7 @@ function resolveCategoryConfig(categoryName, userCategories) {
41314
41645
 
41315
41646
  ` + userConfig.prompt_append : userConfig.prompt_append;
41316
41647
  }
41317
- return { config: config3, promptAppend };
41648
+ return { config: config3, promptAppend, model };
41318
41649
  }
41319
41650
  function buildSystemContent(input) {
41320
41651
  const { skillContent, categoryPromptAppend } = input;
@@ -41352,9 +41683,10 @@ function createSisyphusTask(options) {
41352
41683
  const runInBackground = args.run_in_background === true;
41353
41684
  let skillContent;
41354
41685
  if (args.skills.length > 0) {
41355
- const { resolved, notFound } = resolveMultipleSkills(args.skills, { gitMasterConfig });
41686
+ const { resolved, notFound } = await resolveMultipleSkillsAsync(args.skills, { gitMasterConfig });
41356
41687
  if (notFound.length > 0) {
41357
- const available = createBuiltinSkills().map((s) => s.name).join(", ");
41688
+ const allSkills = await discoverSkills({ includeClaudeCodePaths: true });
41689
+ const available = allSkills.map((s) => s.name).join(", ");
41358
41690
  return `\u274C Skills not found: ${notFound.join(", ")}. Available: ${available}`;
41359
41691
  }
41360
41692
  skillContent = Array.from(resolved.values()).join(`
@@ -41402,8 +41734,11 @@ Status: ${task.status}
41402
41734
  Agent continues with full previous context preserved.
41403
41735
  Use \`background_output\` with task_id="${task.id}" to check progress.`;
41404
41736
  } catch (error45) {
41405
- const message = error45 instanceof Error ? error45.message : String(error45);
41406
- return `\u274C Failed to resume task: ${message}`;
41737
+ return formatDetailedError(error45, {
41738
+ operation: "Resume background task",
41739
+ args,
41740
+ sessionID: args.resume
41741
+ });
41407
41742
  }
41408
41743
  }
41409
41744
  const toastManager2 = getTaskToastManager();
@@ -41422,10 +41757,25 @@ Use \`background_output\` with task_id="${task.id}" to check progress.`;
41422
41757
  metadata: { sessionId: args.resume, sync: true }
41423
41758
  });
41424
41759
  try {
41425
- const resumeMessageDir = getMessageDir14(args.resume);
41426
- const resumeMessage = resumeMessageDir ? findNearestMessageWithFields(resumeMessageDir) : null;
41427
- const resumeAgent = resumeMessage?.agent;
41428
- const resumeModel = resumeMessage?.model?.providerID && resumeMessage?.model?.modelID ? { providerID: resumeMessage.model.providerID, modelID: resumeMessage.model.modelID } : undefined;
41760
+ let resumeAgent;
41761
+ let resumeModel;
41762
+ try {
41763
+ const messagesResp = await client2.session.messages({ path: { id: args.resume } });
41764
+ const messages2 = messagesResp.data ?? [];
41765
+ for (let i2 = messages2.length - 1;i2 >= 0; i2--) {
41766
+ const info = messages2[i2].info;
41767
+ if (info?.agent || info?.model) {
41768
+ resumeAgent = info.agent;
41769
+ resumeModel = info.model;
41770
+ break;
41771
+ }
41772
+ }
41773
+ } catch {
41774
+ const resumeMessageDir = getMessageDir14(args.resume);
41775
+ const resumeMessage = resumeMessageDir ? findNearestMessageWithFields(resumeMessageDir) : null;
41776
+ resumeAgent = resumeMessage?.agent;
41777
+ resumeModel = resumeMessage?.model?.providerID && resumeMessage?.model?.modelID ? { providerID: resumeMessage.model.providerID, modelID: resumeMessage.model.modelID } : undefined;
41778
+ }
41429
41779
  await client2.session.prompt({
41430
41780
  path: { id: args.resume },
41431
41781
  body: {
@@ -41511,23 +41861,60 @@ ${textContent || "(No text output)"}`;
41511
41861
  if (!args.category && !args.subagent_type) {
41512
41862
  return `\u274C Invalid arguments: Must provide either category or subagent_type.`;
41513
41863
  }
41864
+ let systemDefaultModel;
41865
+ try {
41866
+ const openCodeConfig = await client2.config.get();
41867
+ systemDefaultModel = openCodeConfig?.model;
41868
+ } catch {
41869
+ systemDefaultModel = undefined;
41870
+ }
41514
41871
  let agentToUse;
41515
41872
  let categoryModel;
41516
41873
  let categoryPromptAppend;
41874
+ const parentModelString = parentModel ? `${parentModel.providerID}/${parentModel.modelID}` : undefined;
41875
+ let modelInfo;
41517
41876
  if (args.category) {
41518
- const resolved = resolveCategoryConfig(args.category, userCategories);
41877
+ const resolved = resolveCategoryConfig(args.category, {
41878
+ userCategories,
41879
+ parentModelString,
41880
+ systemDefaultModel
41881
+ });
41519
41882
  if (!resolved) {
41520
41883
  return `\u274C Unknown category: "${args.category}". Available: ${Object.keys({ ...DEFAULT_CATEGORIES, ...userCategories }).join(", ")}`;
41521
41884
  }
41885
+ const actualModel = resolved.model;
41886
+ const userDefinedModel = userCategories?.[args.category]?.model;
41887
+ const categoryDefaultModel = DEFAULT_CATEGORIES[args.category]?.model;
41888
+ if (!actualModel) {
41889
+ return `\u274C No model configured. Set a model in your OpenCode config, plugin config, or use a category with a default model.`;
41890
+ }
41891
+ if (!parseModelString(actualModel)) {
41892
+ return `\u274C Invalid model format "${actualModel}". Expected "provider/model" format (e.g., "anthropic/claude-sonnet-4-5").`;
41893
+ }
41894
+ switch (actualModel) {
41895
+ case userDefinedModel:
41896
+ modelInfo = { model: actualModel, type: "user-defined" };
41897
+ break;
41898
+ case parentModelString:
41899
+ modelInfo = { model: actualModel, type: "inherited" };
41900
+ break;
41901
+ case categoryDefaultModel:
41902
+ modelInfo = { model: actualModel, type: "category-default" };
41903
+ break;
41904
+ case systemDefaultModel:
41905
+ modelInfo = { model: actualModel, type: "system-default" };
41906
+ break;
41907
+ }
41522
41908
  agentToUse = SISYPHUS_JUNIOR_AGENT;
41523
- const parsedModel = parseModelString(resolved.config.model);
41909
+ const parsedModel = parseModelString(actualModel);
41524
41910
  categoryModel = parsedModel ? resolved.config.variant ? { ...parsedModel, variant: resolved.config.variant } : parsedModel : undefined;
41525
41911
  categoryPromptAppend = resolved.promptAppend || undefined;
41526
41912
  } else {
41527
- agentToUse = args.subagent_type.trim();
41528
- if (!agentToUse) {
41913
+ if (!args.subagent_type?.trim()) {
41529
41914
  return `\u274C Agent name cannot be empty.`;
41530
41915
  }
41916
+ const agentName = args.subagent_type.trim();
41917
+ agentToUse = agentName;
41531
41918
  try {
41532
41919
  const agentsResult = await client2.app.agents();
41533
41920
  const agents = agentsResult.data ?? agentsResult;
@@ -41572,8 +41959,12 @@ Status: ${task.status}
41572
41959
 
41573
41960
  System notifies on completion. Use \`background_output\` with task_id="${task.id}" to check.`;
41574
41961
  } catch (error45) {
41575
- const message = error45 instanceof Error ? error45.message : String(error45);
41576
- return `\u274C Failed to launch task: ${message}`;
41962
+ return formatDetailedError(error45, {
41963
+ operation: "Launch background task",
41964
+ args,
41965
+ agent: agentToUse,
41966
+ category: args.category
41967
+ });
41577
41968
  }
41578
41969
  }
41579
41970
  const toastManager = getTaskToastManager();
@@ -41605,7 +41996,8 @@ System notifies on completion. Use \`background_output\` with task_id="${task.id
41605
41996
  description: args.description,
41606
41997
  agent: agentToUse,
41607
41998
  isBackground: false,
41608
- skills: args.skills
41999
+ skills: args.skills,
42000
+ modelInfo
41609
42001
  });
41610
42002
  }
41611
42003
  ctx.metadata?.({
@@ -41633,13 +42025,21 @@ System notifies on completion. Use \`background_output\` with task_id="${task.id
41633
42025
  }
41634
42026
  const errorMessage = promptError instanceof Error ? promptError.message : String(promptError);
41635
42027
  if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
41636
- return `\u274C Agent "${agentToUse}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.
41637
-
41638
- Session ID: ${sessionID}`;
42028
+ return formatDetailedError(new Error(`Agent "${agentToUse}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`), {
42029
+ operation: "Send prompt to agent",
42030
+ args,
42031
+ sessionID,
42032
+ agent: agentToUse,
42033
+ category: args.category
42034
+ });
41639
42035
  }
41640
- return `\u274C Failed to send prompt: ${errorMessage}
41641
-
41642
- Session ID: ${sessionID}`;
42036
+ return formatDetailedError(promptError, {
42037
+ operation: "Send prompt",
42038
+ args,
42039
+ sessionID,
42040
+ agent: agentToUse,
42041
+ category: args.category
42042
+ });
41643
42043
  }
41644
42044
  const POLL_INTERVAL_MS = 500;
41645
42045
  const MAX_POLL_TIME_MS = 10 * 60 * 1000;
@@ -41739,8 +42139,13 @@ ${textContent || "(No text output)"}`;
41739
42139
  if (syncSessionID) {
41740
42140
  subagentSessions.delete(syncSessionID);
41741
42141
  }
41742
- const message = error45 instanceof Error ? error45.message : String(error45);
41743
- return `\u274C Task failed: ${message}`;
42142
+ return formatDetailedError(error45, {
42143
+ operation: "Execute task",
42144
+ args,
42145
+ sessionID: syncSessionID,
42146
+ agent: agentToUse,
42147
+ category: args.category
42148
+ });
41744
42149
  }
41745
42150
  }
41746
42151
  });
@@ -41756,9 +42161,6 @@ function createBackgroundTools(manager, client2) {
41756
42161
  };
41757
42162
  }
41758
42163
  var builtinTools = {
41759
- lsp_goto_definition,
41760
- lsp_find_references,
41761
- lsp_symbols,
41762
42164
  lsp_diagnostics,
41763
42165
  lsp_servers,
41764
42166
  lsp_prepare_rename,
@@ -41963,6 +42365,7 @@ class BackgroundManager {
41963
42365
  existingTask.completedAt = new Date;
41964
42366
  if (existingTask.concurrencyKey) {
41965
42367
  this.concurrencyManager.release(existingTask.concurrencyKey);
42368
+ existingTask.concurrencyKey = undefined;
41966
42369
  }
41967
42370
  this.markForNotification(existingTask);
41968
42371
  this.notifyParentSession(existingTask).catch((err) => {
@@ -42040,6 +42443,7 @@ class BackgroundManager {
42040
42443
  existingTask.parentMessageID = input.parentMessageID;
42041
42444
  existingTask.parentModel = input.parentModel;
42042
42445
  existingTask.parentAgent = input.parentAgent;
42446
+ existingTask.startedAt = new Date;
42043
42447
  existingTask.progress = {
42044
42448
  toolCalls: existingTask.progress?.toolCalls ?? 0,
42045
42449
  lastUpdate: new Date
@@ -42081,6 +42485,10 @@ class BackgroundManager {
42081
42485
  const errorMessage = error45 instanceof Error ? error45.message : String(error45);
42082
42486
  existingTask.error = errorMessage;
42083
42487
  existingTask.completedAt = new Date;
42488
+ if (existingTask.concurrencyKey) {
42489
+ this.concurrencyManager.release(existingTask.concurrencyKey);
42490
+ existingTask.concurrencyKey = undefined;
42491
+ }
42084
42492
  this.markForNotification(existingTask);
42085
42493
  this.notifyParentSession(existingTask).catch((err) => {
42086
42494
  log("[background-agent] Failed to notify on resume error:", err);
@@ -42151,6 +42559,11 @@ class BackgroundManager {
42151
42559
  }
42152
42560
  task.status = "completed";
42153
42561
  task.completedAt = new Date;
42562
+ if (task.concurrencyKey) {
42563
+ this.concurrencyManager.release(task.concurrencyKey);
42564
+ task.concurrencyKey = undefined;
42565
+ }
42566
+ this.cleanupPendingByParent(task);
42154
42567
  this.markForNotification(task);
42155
42568
  await this.notifyParentSession(task);
42156
42569
  log("[background-agent] Task completed via session.idle event:", task.id);
@@ -42173,7 +42586,9 @@ class BackgroundManager {
42173
42586
  }
42174
42587
  if (task.concurrencyKey) {
42175
42588
  this.concurrencyManager.release(task.concurrencyKey);
42589
+ task.concurrencyKey = undefined;
42176
42590
  }
42591
+ this.cleanupPendingByParent(task);
42177
42592
  this.tasks.delete(task.id);
42178
42593
  this.clearNotificationsForTask(task.id);
42179
42594
  subagentSessions.delete(sessionID);
@@ -42227,6 +42642,17 @@ class BackgroundManager {
42227
42642
  }
42228
42643
  }
42229
42644
  }
42645
+ cleanupPendingByParent(task) {
42646
+ if (!task.parentSessionID)
42647
+ return;
42648
+ const pending = this.pendingByParent.get(task.parentSessionID);
42649
+ if (pending) {
42650
+ pending.delete(task.id);
42651
+ if (pending.size === 0) {
42652
+ this.pendingByParent.delete(task.parentSessionID);
42653
+ }
42654
+ }
42655
+ }
42230
42656
  startPolling() {
42231
42657
  if (this.pollingInterval)
42232
42658
  return;
@@ -42305,15 +42731,27 @@ Do NOT poll - continue productive work.
42305
42731
  Use \`background_output(task_id="${task.id}")\` to retrieve this result when ready.
42306
42732
  </system-reminder>`;
42307
42733
  }
42308
- const messageDir = getMessageDir15(task.parentSessionID);
42309
- const currentMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
42310
- const agent = currentMessage?.agent ?? task.parentAgent;
42311
- const model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
42734
+ let agent = task.parentAgent;
42735
+ let model;
42736
+ try {
42737
+ const messagesResp = await this.client.session.messages({ path: { id: task.parentSessionID } });
42738
+ const messages = messagesResp.data ?? [];
42739
+ for (let i2 = messages.length - 1;i2 >= 0; i2--) {
42740
+ const info = messages[i2].info;
42741
+ if (info?.agent || info?.model) {
42742
+ agent = info.agent ?? task.parentAgent;
42743
+ model = info.model;
42744
+ break;
42745
+ }
42746
+ }
42747
+ } catch {
42748
+ const messageDir = getMessageDir15(task.parentSessionID);
42749
+ const currentMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
42750
+ agent = currentMessage?.agent ?? task.parentAgent;
42751
+ model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
42752
+ }
42312
42753
  log("[background-agent] notifyParentSession context:", {
42313
42754
  taskId: task.id,
42314
- messageDir: !!messageDir,
42315
- currentAgent: currentMessage?.agent,
42316
- currentModel: currentMessage?.model,
42317
42755
  resolvedAgent: agent,
42318
42756
  resolvedModel: model
42319
42757
  });
@@ -42372,7 +42810,9 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
42372
42810
  task.completedAt = new Date;
42373
42811
  if (task.concurrencyKey) {
42374
42812
  this.concurrencyManager.release(task.concurrencyKey);
42813
+ task.concurrencyKey = undefined;
42375
42814
  }
42815
+ this.cleanupPendingByParent(task);
42376
42816
  this.clearNotificationsForTask(taskId);
42377
42817
  this.tasks.delete(taskId);
42378
42818
  subagentSessions.delete(task.sessionID);
@@ -42416,6 +42856,11 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
42416
42856
  }
42417
42857
  task.status = "completed";
42418
42858
  task.completedAt = new Date;
42859
+ if (task.concurrencyKey) {
42860
+ this.concurrencyManager.release(task.concurrencyKey);
42861
+ task.concurrencyKey = undefined;
42862
+ }
42863
+ this.cleanupPendingByParent(task);
42419
42864
  this.markForNotification(task);
42420
42865
  await this.notifyParentSession(task);
42421
42866
  log("[background-agent] Task completed via polling:", task.id);
@@ -42467,6 +42912,11 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
42467
42912
  if (!hasIncompleteTodos2) {
42468
42913
  task.status = "completed";
42469
42914
  task.completedAt = new Date;
42915
+ if (task.concurrencyKey) {
42916
+ this.concurrencyManager.release(task.concurrencyKey);
42917
+ task.concurrencyKey = undefined;
42918
+ }
42919
+ this.cleanupPendingByParent(task);
42470
42920
  this.markForNotification(task);
42471
42921
  await this.notifyParentSession(task);
42472
42922
  log("[background-agent] Task completed via stability detection:", task.id);
@@ -42764,6 +43214,7 @@ var InitializedNotificationSchema = NotificationSchema.extend({
42764
43214
  method: literal("notifications/initialized"),
42765
43215
  params: NotificationsParamsSchema.optional()
42766
43216
  });
43217
+ var isInitializedNotification = (value) => InitializedNotificationSchema.safeParse(value).success;
42767
43218
  var PingRequestSchema = RequestSchema.extend({
42768
43219
  method: literal("ping"),
42769
43220
  params: BaseRequestParamsSchema.optional()
@@ -45037,6 +45488,1317 @@ function isElectron() {
45037
45488
  return "type" in process2;
45038
45489
  }
45039
45490
 
45491
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/transport.js
45492
+ function normalizeHeaders(headers) {
45493
+ if (!headers)
45494
+ return {};
45495
+ if (headers instanceof Headers) {
45496
+ return Object.fromEntries(headers.entries());
45497
+ }
45498
+ if (Array.isArray(headers)) {
45499
+ return Object.fromEntries(headers);
45500
+ }
45501
+ return { ...headers };
45502
+ }
45503
+ function createFetchWithInit(baseFetch = fetch, baseInit) {
45504
+ if (!baseInit) {
45505
+ return baseFetch;
45506
+ }
45507
+ return async (url2, init) => {
45508
+ const mergedInit = {
45509
+ ...baseInit,
45510
+ ...init,
45511
+ headers: init?.headers ? { ...normalizeHeaders(baseInit.headers), ...normalizeHeaders(init.headers) } : baseInit.headers
45512
+ };
45513
+ return baseFetch(url2, mergedInit);
45514
+ };
45515
+ }
45516
+
45517
+ // node_modules/pkce-challenge/dist/index.node.js
45518
+ var crypto2;
45519
+ crypto2 = globalThis.crypto?.webcrypto ?? globalThis.crypto ?? import("crypto").then((m) => m.webcrypto);
45520
+ async function getRandomValues(size) {
45521
+ return (await crypto2).getRandomValues(new Uint8Array(size));
45522
+ }
45523
+ async function random(size) {
45524
+ const mask = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
45525
+ const evenDistCutoff = Math.pow(2, 8) - Math.pow(2, 8) % mask.length;
45526
+ let result = "";
45527
+ while (result.length < size) {
45528
+ const randomBytes = await getRandomValues(size - result.length);
45529
+ for (const randomByte of randomBytes) {
45530
+ if (randomByte < evenDistCutoff) {
45531
+ result += mask[randomByte % mask.length];
45532
+ }
45533
+ }
45534
+ }
45535
+ return result;
45536
+ }
45537
+ async function generateVerifier(length) {
45538
+ return await random(length);
45539
+ }
45540
+ async function generateChallenge(code_verifier) {
45541
+ const buffer = await (await crypto2).subtle.digest("SHA-256", new TextEncoder().encode(code_verifier));
45542
+ return btoa(String.fromCharCode(...new Uint8Array(buffer))).replace(/\//g, "_").replace(/\+/g, "-").replace(/=/g, "");
45543
+ }
45544
+ async function pkceChallenge(length) {
45545
+ if (!length)
45546
+ length = 43;
45547
+ if (length < 43 || length > 128) {
45548
+ throw `Expected a length between 43 and 128. Received ${length}.`;
45549
+ }
45550
+ const verifier = await generateVerifier(length);
45551
+ const challenge = await generateChallenge(verifier);
45552
+ return {
45553
+ code_verifier: verifier,
45554
+ code_challenge: challenge
45555
+ };
45556
+ }
45557
+
45558
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/auth.js
45559
+ var SafeUrlSchema = url().superRefine((val, ctx) => {
45560
+ if (!URL.canParse(val)) {
45561
+ ctx.addIssue({
45562
+ code: ZodIssueCode.custom,
45563
+ message: "URL must be parseable",
45564
+ fatal: true
45565
+ });
45566
+ return NEVER;
45567
+ }
45568
+ }).refine((url2) => {
45569
+ const u = new URL(url2);
45570
+ return u.protocol !== "javascript:" && u.protocol !== "data:" && u.protocol !== "vbscript:";
45571
+ }, { message: "URL cannot use javascript:, data:, or vbscript: scheme" });
45572
+ var OAuthProtectedResourceMetadataSchema = looseObject({
45573
+ resource: string2().url(),
45574
+ authorization_servers: array(SafeUrlSchema).optional(),
45575
+ jwks_uri: string2().url().optional(),
45576
+ scopes_supported: array(string2()).optional(),
45577
+ bearer_methods_supported: array(string2()).optional(),
45578
+ resource_signing_alg_values_supported: array(string2()).optional(),
45579
+ resource_name: string2().optional(),
45580
+ resource_documentation: string2().optional(),
45581
+ resource_policy_uri: string2().url().optional(),
45582
+ resource_tos_uri: string2().url().optional(),
45583
+ tls_client_certificate_bound_access_tokens: boolean2().optional(),
45584
+ authorization_details_types_supported: array(string2()).optional(),
45585
+ dpop_signing_alg_values_supported: array(string2()).optional(),
45586
+ dpop_bound_access_tokens_required: boolean2().optional()
45587
+ });
45588
+ var OAuthMetadataSchema = looseObject({
45589
+ issuer: string2(),
45590
+ authorization_endpoint: SafeUrlSchema,
45591
+ token_endpoint: SafeUrlSchema,
45592
+ registration_endpoint: SafeUrlSchema.optional(),
45593
+ scopes_supported: array(string2()).optional(),
45594
+ response_types_supported: array(string2()),
45595
+ response_modes_supported: array(string2()).optional(),
45596
+ grant_types_supported: array(string2()).optional(),
45597
+ token_endpoint_auth_methods_supported: array(string2()).optional(),
45598
+ token_endpoint_auth_signing_alg_values_supported: array(string2()).optional(),
45599
+ service_documentation: SafeUrlSchema.optional(),
45600
+ revocation_endpoint: SafeUrlSchema.optional(),
45601
+ revocation_endpoint_auth_methods_supported: array(string2()).optional(),
45602
+ revocation_endpoint_auth_signing_alg_values_supported: array(string2()).optional(),
45603
+ introspection_endpoint: string2().optional(),
45604
+ introspection_endpoint_auth_methods_supported: array(string2()).optional(),
45605
+ introspection_endpoint_auth_signing_alg_values_supported: array(string2()).optional(),
45606
+ code_challenge_methods_supported: array(string2()).optional(),
45607
+ client_id_metadata_document_supported: boolean2().optional()
45608
+ });
45609
+ var OpenIdProviderMetadataSchema = looseObject({
45610
+ issuer: string2(),
45611
+ authorization_endpoint: SafeUrlSchema,
45612
+ token_endpoint: SafeUrlSchema,
45613
+ userinfo_endpoint: SafeUrlSchema.optional(),
45614
+ jwks_uri: SafeUrlSchema,
45615
+ registration_endpoint: SafeUrlSchema.optional(),
45616
+ scopes_supported: array(string2()).optional(),
45617
+ response_types_supported: array(string2()),
45618
+ response_modes_supported: array(string2()).optional(),
45619
+ grant_types_supported: array(string2()).optional(),
45620
+ acr_values_supported: array(string2()).optional(),
45621
+ subject_types_supported: array(string2()),
45622
+ id_token_signing_alg_values_supported: array(string2()),
45623
+ id_token_encryption_alg_values_supported: array(string2()).optional(),
45624
+ id_token_encryption_enc_values_supported: array(string2()).optional(),
45625
+ userinfo_signing_alg_values_supported: array(string2()).optional(),
45626
+ userinfo_encryption_alg_values_supported: array(string2()).optional(),
45627
+ userinfo_encryption_enc_values_supported: array(string2()).optional(),
45628
+ request_object_signing_alg_values_supported: array(string2()).optional(),
45629
+ request_object_encryption_alg_values_supported: array(string2()).optional(),
45630
+ request_object_encryption_enc_values_supported: array(string2()).optional(),
45631
+ token_endpoint_auth_methods_supported: array(string2()).optional(),
45632
+ token_endpoint_auth_signing_alg_values_supported: array(string2()).optional(),
45633
+ display_values_supported: array(string2()).optional(),
45634
+ claim_types_supported: array(string2()).optional(),
45635
+ claims_supported: array(string2()).optional(),
45636
+ service_documentation: string2().optional(),
45637
+ claims_locales_supported: array(string2()).optional(),
45638
+ ui_locales_supported: array(string2()).optional(),
45639
+ claims_parameter_supported: boolean2().optional(),
45640
+ request_parameter_supported: boolean2().optional(),
45641
+ request_uri_parameter_supported: boolean2().optional(),
45642
+ require_request_uri_registration: boolean2().optional(),
45643
+ op_policy_uri: SafeUrlSchema.optional(),
45644
+ op_tos_uri: SafeUrlSchema.optional(),
45645
+ client_id_metadata_document_supported: boolean2().optional()
45646
+ });
45647
+ var OpenIdProviderDiscoveryMetadataSchema = object({
45648
+ ...OpenIdProviderMetadataSchema.shape,
45649
+ ...OAuthMetadataSchema.pick({
45650
+ code_challenge_methods_supported: true
45651
+ }).shape
45652
+ });
45653
+ var OAuthTokensSchema = object({
45654
+ access_token: string2(),
45655
+ id_token: string2().optional(),
45656
+ token_type: string2(),
45657
+ expires_in: exports_coerce.number().optional(),
45658
+ scope: string2().optional(),
45659
+ refresh_token: string2().optional()
45660
+ }).strip();
45661
+ var OAuthErrorResponseSchema = object({
45662
+ error: string2(),
45663
+ error_description: string2().optional(),
45664
+ error_uri: string2().optional()
45665
+ });
45666
+ var OptionalSafeUrlSchema = SafeUrlSchema.optional().or(literal("").transform(() => {
45667
+ return;
45668
+ }));
45669
+ var OAuthClientMetadataSchema = object({
45670
+ redirect_uris: array(SafeUrlSchema),
45671
+ token_endpoint_auth_method: string2().optional(),
45672
+ grant_types: array(string2()).optional(),
45673
+ response_types: array(string2()).optional(),
45674
+ client_name: string2().optional(),
45675
+ client_uri: SafeUrlSchema.optional(),
45676
+ logo_uri: OptionalSafeUrlSchema,
45677
+ scope: string2().optional(),
45678
+ contacts: array(string2()).optional(),
45679
+ tos_uri: OptionalSafeUrlSchema,
45680
+ policy_uri: string2().optional(),
45681
+ jwks_uri: SafeUrlSchema.optional(),
45682
+ jwks: any().optional(),
45683
+ software_id: string2().optional(),
45684
+ software_version: string2().optional(),
45685
+ software_statement: string2().optional()
45686
+ }).strip();
45687
+ var OAuthClientInformationSchema = object({
45688
+ client_id: string2(),
45689
+ client_secret: string2().optional(),
45690
+ client_id_issued_at: number2().optional(),
45691
+ client_secret_expires_at: number2().optional()
45692
+ }).strip();
45693
+ var OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge(OAuthClientInformationSchema);
45694
+ var OAuthClientRegistrationErrorSchema = object({
45695
+ error: string2(),
45696
+ error_description: string2().optional()
45697
+ }).strip();
45698
+ var OAuthTokenRevocationRequestSchema = object({
45699
+ token: string2(),
45700
+ token_type_hint: string2().optional()
45701
+ }).strip();
45702
+
45703
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/auth-utils.js
45704
+ function resourceUrlFromServerUrl(url2) {
45705
+ const resourceURL = typeof url2 === "string" ? new URL(url2) : new URL(url2.href);
45706
+ resourceURL.hash = "";
45707
+ return resourceURL;
45708
+ }
45709
+ function checkResourceAllowed({ requestedResource, configuredResource }) {
45710
+ const requested = typeof requestedResource === "string" ? new URL(requestedResource) : new URL(requestedResource.href);
45711
+ const configured = typeof configuredResource === "string" ? new URL(configuredResource) : new URL(configuredResource.href);
45712
+ if (requested.origin !== configured.origin) {
45713
+ return false;
45714
+ }
45715
+ if (requested.pathname.length < configured.pathname.length) {
45716
+ return false;
45717
+ }
45718
+ const requestedPath = requested.pathname.endsWith("/") ? requested.pathname : requested.pathname + "/";
45719
+ const configuredPath = configured.pathname.endsWith("/") ? configured.pathname : configured.pathname + "/";
45720
+ return requestedPath.startsWith(configuredPath);
45721
+ }
45722
+
45723
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/server/auth/errors.js
45724
+ class OAuthError extends Error {
45725
+ constructor(message, errorUri) {
45726
+ super(message);
45727
+ this.errorUri = errorUri;
45728
+ this.name = this.constructor.name;
45729
+ }
45730
+ toResponseObject() {
45731
+ const response = {
45732
+ error: this.errorCode,
45733
+ error_description: this.message
45734
+ };
45735
+ if (this.errorUri) {
45736
+ response.error_uri = this.errorUri;
45737
+ }
45738
+ return response;
45739
+ }
45740
+ get errorCode() {
45741
+ return this.constructor.errorCode;
45742
+ }
45743
+ }
45744
+
45745
+ class InvalidRequestError extends OAuthError {
45746
+ }
45747
+ InvalidRequestError.errorCode = "invalid_request";
45748
+
45749
+ class InvalidClientError extends OAuthError {
45750
+ }
45751
+ InvalidClientError.errorCode = "invalid_client";
45752
+
45753
+ class InvalidGrantError extends OAuthError {
45754
+ }
45755
+ InvalidGrantError.errorCode = "invalid_grant";
45756
+
45757
+ class UnauthorizedClientError extends OAuthError {
45758
+ }
45759
+ UnauthorizedClientError.errorCode = "unauthorized_client";
45760
+
45761
+ class UnsupportedGrantTypeError extends OAuthError {
45762
+ }
45763
+ UnsupportedGrantTypeError.errorCode = "unsupported_grant_type";
45764
+
45765
+ class InvalidScopeError extends OAuthError {
45766
+ }
45767
+ InvalidScopeError.errorCode = "invalid_scope";
45768
+
45769
+ class AccessDeniedError extends OAuthError {
45770
+ }
45771
+ AccessDeniedError.errorCode = "access_denied";
45772
+
45773
+ class ServerError extends OAuthError {
45774
+ }
45775
+ ServerError.errorCode = "server_error";
45776
+
45777
+ class TemporarilyUnavailableError extends OAuthError {
45778
+ }
45779
+ TemporarilyUnavailableError.errorCode = "temporarily_unavailable";
45780
+
45781
+ class UnsupportedResponseTypeError extends OAuthError {
45782
+ }
45783
+ UnsupportedResponseTypeError.errorCode = "unsupported_response_type";
45784
+
45785
+ class UnsupportedTokenTypeError extends OAuthError {
45786
+ }
45787
+ UnsupportedTokenTypeError.errorCode = "unsupported_token_type";
45788
+
45789
+ class InvalidTokenError extends OAuthError {
45790
+ }
45791
+ InvalidTokenError.errorCode = "invalid_token";
45792
+
45793
+ class MethodNotAllowedError extends OAuthError {
45794
+ }
45795
+ MethodNotAllowedError.errorCode = "method_not_allowed";
45796
+
45797
+ class TooManyRequestsError extends OAuthError {
45798
+ }
45799
+ TooManyRequestsError.errorCode = "too_many_requests";
45800
+
45801
+ class InvalidClientMetadataError extends OAuthError {
45802
+ }
45803
+ InvalidClientMetadataError.errorCode = "invalid_client_metadata";
45804
+
45805
+ class InsufficientScopeError extends OAuthError {
45806
+ }
45807
+ InsufficientScopeError.errorCode = "insufficient_scope";
45808
+
45809
+ class InvalidTargetError extends OAuthError {
45810
+ }
45811
+ InvalidTargetError.errorCode = "invalid_target";
45812
+ var OAUTH_ERRORS = {
45813
+ [InvalidRequestError.errorCode]: InvalidRequestError,
45814
+ [InvalidClientError.errorCode]: InvalidClientError,
45815
+ [InvalidGrantError.errorCode]: InvalidGrantError,
45816
+ [UnauthorizedClientError.errorCode]: UnauthorizedClientError,
45817
+ [UnsupportedGrantTypeError.errorCode]: UnsupportedGrantTypeError,
45818
+ [InvalidScopeError.errorCode]: InvalidScopeError,
45819
+ [AccessDeniedError.errorCode]: AccessDeniedError,
45820
+ [ServerError.errorCode]: ServerError,
45821
+ [TemporarilyUnavailableError.errorCode]: TemporarilyUnavailableError,
45822
+ [UnsupportedResponseTypeError.errorCode]: UnsupportedResponseTypeError,
45823
+ [UnsupportedTokenTypeError.errorCode]: UnsupportedTokenTypeError,
45824
+ [InvalidTokenError.errorCode]: InvalidTokenError,
45825
+ [MethodNotAllowedError.errorCode]: MethodNotAllowedError,
45826
+ [TooManyRequestsError.errorCode]: TooManyRequestsError,
45827
+ [InvalidClientMetadataError.errorCode]: InvalidClientMetadataError,
45828
+ [InsufficientScopeError.errorCode]: InsufficientScopeError,
45829
+ [InvalidTargetError.errorCode]: InvalidTargetError
45830
+ };
45831
+
45832
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/client/auth.js
45833
+ class UnauthorizedError extends Error {
45834
+ constructor(message) {
45835
+ super(message ?? "Unauthorized");
45836
+ }
45837
+ }
45838
+ function isClientAuthMethod(method) {
45839
+ return ["client_secret_basic", "client_secret_post", "none"].includes(method);
45840
+ }
45841
+ var AUTHORIZATION_CODE_RESPONSE_TYPE = "code";
45842
+ var AUTHORIZATION_CODE_CHALLENGE_METHOD = "S256";
45843
+ function selectClientAuthMethod(clientInformation, supportedMethods) {
45844
+ const hasClientSecret = clientInformation.client_secret !== undefined;
45845
+ if (supportedMethods.length === 0) {
45846
+ return hasClientSecret ? "client_secret_post" : "none";
45847
+ }
45848
+ if ("token_endpoint_auth_method" in clientInformation && clientInformation.token_endpoint_auth_method && isClientAuthMethod(clientInformation.token_endpoint_auth_method) && supportedMethods.includes(clientInformation.token_endpoint_auth_method)) {
45849
+ return clientInformation.token_endpoint_auth_method;
45850
+ }
45851
+ if (hasClientSecret && supportedMethods.includes("client_secret_basic")) {
45852
+ return "client_secret_basic";
45853
+ }
45854
+ if (hasClientSecret && supportedMethods.includes("client_secret_post")) {
45855
+ return "client_secret_post";
45856
+ }
45857
+ if (supportedMethods.includes("none")) {
45858
+ return "none";
45859
+ }
45860
+ return hasClientSecret ? "client_secret_post" : "none";
45861
+ }
45862
+ function applyClientAuthentication(method, clientInformation, headers, params) {
45863
+ const { client_id, client_secret } = clientInformation;
45864
+ switch (method) {
45865
+ case "client_secret_basic":
45866
+ applyBasicAuth(client_id, client_secret, headers);
45867
+ return;
45868
+ case "client_secret_post":
45869
+ applyPostAuth(client_id, client_secret, params);
45870
+ return;
45871
+ case "none":
45872
+ applyPublicAuth(client_id, params);
45873
+ return;
45874
+ default:
45875
+ throw new Error(`Unsupported client authentication method: ${method}`);
45876
+ }
45877
+ }
45878
+ function applyBasicAuth(clientId, clientSecret, headers) {
45879
+ if (!clientSecret) {
45880
+ throw new Error("client_secret_basic authentication requires a client_secret");
45881
+ }
45882
+ const credentials = btoa(`${clientId}:${clientSecret}`);
45883
+ headers.set("Authorization", `Basic ${credentials}`);
45884
+ }
45885
+ function applyPostAuth(clientId, clientSecret, params) {
45886
+ params.set("client_id", clientId);
45887
+ if (clientSecret) {
45888
+ params.set("client_secret", clientSecret);
45889
+ }
45890
+ }
45891
+ function applyPublicAuth(clientId, params) {
45892
+ params.set("client_id", clientId);
45893
+ }
45894
+ async function parseErrorResponse(input) {
45895
+ const statusCode = input instanceof Response ? input.status : undefined;
45896
+ const body = input instanceof Response ? await input.text() : input;
45897
+ try {
45898
+ const result = OAuthErrorResponseSchema.parse(JSON.parse(body));
45899
+ const { error: error45, error_description, error_uri } = result;
45900
+ const errorClass = OAUTH_ERRORS[error45] || ServerError;
45901
+ return new errorClass(error_description || "", error_uri);
45902
+ } catch (error45) {
45903
+ const errorMessage = `${statusCode ? `HTTP ${statusCode}: ` : ""}Invalid OAuth error response: ${error45}. Raw body: ${body}`;
45904
+ return new ServerError(errorMessage);
45905
+ }
45906
+ }
45907
+ async function auth(provider, options) {
45908
+ try {
45909
+ return await authInternal(provider, options);
45910
+ } catch (error45) {
45911
+ if (error45 instanceof InvalidClientError || error45 instanceof UnauthorizedClientError) {
45912
+ await provider.invalidateCredentials?.("all");
45913
+ return await authInternal(provider, options);
45914
+ } else if (error45 instanceof InvalidGrantError) {
45915
+ await provider.invalidateCredentials?.("tokens");
45916
+ return await authInternal(provider, options);
45917
+ }
45918
+ throw error45;
45919
+ }
45920
+ }
45921
+ async function authInternal(provider, { serverUrl, authorizationCode, scope, resourceMetadataUrl, fetchFn }) {
45922
+ let resourceMetadata;
45923
+ let authorizationServerUrl;
45924
+ try {
45925
+ resourceMetadata = await discoverOAuthProtectedResourceMetadata(serverUrl, { resourceMetadataUrl }, fetchFn);
45926
+ if (resourceMetadata.authorization_servers && resourceMetadata.authorization_servers.length > 0) {
45927
+ authorizationServerUrl = resourceMetadata.authorization_servers[0];
45928
+ }
45929
+ } catch {}
45930
+ if (!authorizationServerUrl) {
45931
+ authorizationServerUrl = new URL("/", serverUrl);
45932
+ }
45933
+ const resource = await selectResourceURL(serverUrl, provider, resourceMetadata);
45934
+ const metadata = await discoverAuthorizationServerMetadata(authorizationServerUrl, {
45935
+ fetchFn
45936
+ });
45937
+ let clientInformation = await Promise.resolve(provider.clientInformation());
45938
+ if (!clientInformation) {
45939
+ if (authorizationCode !== undefined) {
45940
+ throw new Error("Existing OAuth client information is required when exchanging an authorization code");
45941
+ }
45942
+ const supportsUrlBasedClientId = metadata?.client_id_metadata_document_supported === true;
45943
+ const clientMetadataUrl = provider.clientMetadataUrl;
45944
+ if (clientMetadataUrl && !isHttpsUrl(clientMetadataUrl)) {
45945
+ throw new InvalidClientMetadataError(`clientMetadataUrl must be a valid HTTPS URL with a non-root pathname, got: ${clientMetadataUrl}`);
45946
+ }
45947
+ const shouldUseUrlBasedClientId = supportsUrlBasedClientId && clientMetadataUrl;
45948
+ if (shouldUseUrlBasedClientId) {
45949
+ clientInformation = {
45950
+ client_id: clientMetadataUrl
45951
+ };
45952
+ await provider.saveClientInformation?.(clientInformation);
45953
+ } else {
45954
+ if (!provider.saveClientInformation) {
45955
+ throw new Error("OAuth client information must be saveable for dynamic registration");
45956
+ }
45957
+ const fullInformation = await registerClient(authorizationServerUrl, {
45958
+ metadata,
45959
+ clientMetadata: provider.clientMetadata,
45960
+ fetchFn
45961
+ });
45962
+ await provider.saveClientInformation(fullInformation);
45963
+ clientInformation = fullInformation;
45964
+ }
45965
+ }
45966
+ const nonInteractiveFlow = !provider.redirectUrl;
45967
+ if (authorizationCode !== undefined || nonInteractiveFlow) {
45968
+ const tokens2 = await fetchToken(provider, authorizationServerUrl, {
45969
+ metadata,
45970
+ resource,
45971
+ authorizationCode,
45972
+ fetchFn
45973
+ });
45974
+ await provider.saveTokens(tokens2);
45975
+ return "AUTHORIZED";
45976
+ }
45977
+ const tokens = await provider.tokens();
45978
+ if (tokens?.refresh_token) {
45979
+ try {
45980
+ const newTokens = await refreshAuthorization(authorizationServerUrl, {
45981
+ metadata,
45982
+ clientInformation,
45983
+ refreshToken: tokens.refresh_token,
45984
+ resource,
45985
+ addClientAuthentication: provider.addClientAuthentication,
45986
+ fetchFn
45987
+ });
45988
+ await provider.saveTokens(newTokens);
45989
+ return "AUTHORIZED";
45990
+ } catch (error45) {
45991
+ if (!(error45 instanceof OAuthError) || error45 instanceof ServerError) {} else {
45992
+ throw error45;
45993
+ }
45994
+ }
45995
+ }
45996
+ const state2 = provider.state ? await provider.state() : undefined;
45997
+ const { authorizationUrl, codeVerifier } = await startAuthorization(authorizationServerUrl, {
45998
+ metadata,
45999
+ clientInformation,
46000
+ state: state2,
46001
+ redirectUrl: provider.redirectUrl,
46002
+ scope: scope || resourceMetadata?.scopes_supported?.join(" ") || provider.clientMetadata.scope,
46003
+ resource
46004
+ });
46005
+ await provider.saveCodeVerifier(codeVerifier);
46006
+ await provider.redirectToAuthorization(authorizationUrl);
46007
+ return "REDIRECT";
46008
+ }
46009
+ function isHttpsUrl(value) {
46010
+ if (!value)
46011
+ return false;
46012
+ try {
46013
+ const url2 = new URL(value);
46014
+ return url2.protocol === "https:" && url2.pathname !== "/";
46015
+ } catch {
46016
+ return false;
46017
+ }
46018
+ }
46019
+ async function selectResourceURL(serverUrl, provider, resourceMetadata) {
46020
+ const defaultResource = resourceUrlFromServerUrl(serverUrl);
46021
+ if (provider.validateResourceURL) {
46022
+ return await provider.validateResourceURL(defaultResource, resourceMetadata?.resource);
46023
+ }
46024
+ if (!resourceMetadata) {
46025
+ return;
46026
+ }
46027
+ if (!checkResourceAllowed({ requestedResource: defaultResource, configuredResource: resourceMetadata.resource })) {
46028
+ throw new Error(`Protected resource ${resourceMetadata.resource} does not match expected ${defaultResource} (or origin)`);
46029
+ }
46030
+ return new URL(resourceMetadata.resource);
46031
+ }
46032
+ function extractWWWAuthenticateParams(res) {
46033
+ const authenticateHeader = res.headers.get("WWW-Authenticate");
46034
+ if (!authenticateHeader) {
46035
+ return {};
46036
+ }
46037
+ const [type2, scheme] = authenticateHeader.split(" ");
46038
+ if (type2.toLowerCase() !== "bearer" || !scheme) {
46039
+ return {};
46040
+ }
46041
+ const resourceMetadataMatch = extractFieldFromWwwAuth(res, "resource_metadata") || undefined;
46042
+ let resourceMetadataUrl;
46043
+ if (resourceMetadataMatch) {
46044
+ try {
46045
+ resourceMetadataUrl = new URL(resourceMetadataMatch);
46046
+ } catch {}
46047
+ }
46048
+ const scope = extractFieldFromWwwAuth(res, "scope") || undefined;
46049
+ const error45 = extractFieldFromWwwAuth(res, "error") || undefined;
46050
+ return {
46051
+ resourceMetadataUrl,
46052
+ scope,
46053
+ error: error45
46054
+ };
46055
+ }
46056
+ function extractFieldFromWwwAuth(response, fieldName) {
46057
+ const wwwAuthHeader = response.headers.get("WWW-Authenticate");
46058
+ if (!wwwAuthHeader) {
46059
+ return null;
46060
+ }
46061
+ const pattern = new RegExp(`${fieldName}=(?:"([^"]+)"|([^\\s,]+))`);
46062
+ const match = wwwAuthHeader.match(pattern);
46063
+ if (match) {
46064
+ return match[1] || match[2];
46065
+ }
46066
+ return null;
46067
+ }
46068
+ async function discoverOAuthProtectedResourceMetadata(serverUrl, opts, fetchFn = fetch) {
46069
+ const response = await discoverMetadataWithFallback(serverUrl, "oauth-protected-resource", fetchFn, {
46070
+ protocolVersion: opts?.protocolVersion,
46071
+ metadataUrl: opts?.resourceMetadataUrl
46072
+ });
46073
+ if (!response || response.status === 404) {
46074
+ await response?.body?.cancel();
46075
+ throw new Error(`Resource server does not implement OAuth 2.0 Protected Resource Metadata.`);
46076
+ }
46077
+ if (!response.ok) {
46078
+ await response.body?.cancel();
46079
+ throw new Error(`HTTP ${response.status} trying to load well-known OAuth protected resource metadata.`);
46080
+ }
46081
+ return OAuthProtectedResourceMetadataSchema.parse(await response.json());
46082
+ }
46083
+ async function fetchWithCorsRetry(url2, headers, fetchFn = fetch) {
46084
+ try {
46085
+ return await fetchFn(url2, { headers });
46086
+ } catch (error45) {
46087
+ if (error45 instanceof TypeError) {
46088
+ if (headers) {
46089
+ return fetchWithCorsRetry(url2, undefined, fetchFn);
46090
+ } else {
46091
+ return;
46092
+ }
46093
+ }
46094
+ throw error45;
46095
+ }
46096
+ }
46097
+ function buildWellKnownPath(wellKnownPrefix, pathname = "", options = {}) {
46098
+ if (pathname.endsWith("/")) {
46099
+ pathname = pathname.slice(0, -1);
46100
+ }
46101
+ return options.prependPathname ? `${pathname}/.well-known/${wellKnownPrefix}` : `/.well-known/${wellKnownPrefix}${pathname}`;
46102
+ }
46103
+ async function tryMetadataDiscovery(url2, protocolVersion, fetchFn = fetch) {
46104
+ const headers = {
46105
+ "MCP-Protocol-Version": protocolVersion
46106
+ };
46107
+ return await fetchWithCorsRetry(url2, headers, fetchFn);
46108
+ }
46109
+ function shouldAttemptFallback(response, pathname) {
46110
+ return !response || response.status >= 400 && response.status < 500 && pathname !== "/";
46111
+ }
46112
+ async function discoverMetadataWithFallback(serverUrl, wellKnownType, fetchFn, opts) {
46113
+ const issuer = new URL(serverUrl);
46114
+ const protocolVersion = opts?.protocolVersion ?? LATEST_PROTOCOL_VERSION;
46115
+ let url2;
46116
+ if (opts?.metadataUrl) {
46117
+ url2 = new URL(opts.metadataUrl);
46118
+ } else {
46119
+ const wellKnownPath = buildWellKnownPath(wellKnownType, issuer.pathname);
46120
+ url2 = new URL(wellKnownPath, opts?.metadataServerUrl ?? issuer);
46121
+ url2.search = issuer.search;
46122
+ }
46123
+ let response = await tryMetadataDiscovery(url2, protocolVersion, fetchFn);
46124
+ if (!opts?.metadataUrl && shouldAttemptFallback(response, issuer.pathname)) {
46125
+ const rootUrl = new URL(`/.well-known/${wellKnownType}`, issuer);
46126
+ response = await tryMetadataDiscovery(rootUrl, protocolVersion, fetchFn);
46127
+ }
46128
+ return response;
46129
+ }
46130
+ function buildDiscoveryUrls(authorizationServerUrl) {
46131
+ const url2 = typeof authorizationServerUrl === "string" ? new URL(authorizationServerUrl) : authorizationServerUrl;
46132
+ const hasPath = url2.pathname !== "/";
46133
+ const urlsToTry = [];
46134
+ if (!hasPath) {
46135
+ urlsToTry.push({
46136
+ url: new URL("/.well-known/oauth-authorization-server", url2.origin),
46137
+ type: "oauth"
46138
+ });
46139
+ urlsToTry.push({
46140
+ url: new URL(`/.well-known/openid-configuration`, url2.origin),
46141
+ type: "oidc"
46142
+ });
46143
+ return urlsToTry;
46144
+ }
46145
+ let pathname = url2.pathname;
46146
+ if (pathname.endsWith("/")) {
46147
+ pathname = pathname.slice(0, -1);
46148
+ }
46149
+ urlsToTry.push({
46150
+ url: new URL(`/.well-known/oauth-authorization-server${pathname}`, url2.origin),
46151
+ type: "oauth"
46152
+ });
46153
+ urlsToTry.push({
46154
+ url: new URL(`/.well-known/openid-configuration${pathname}`, url2.origin),
46155
+ type: "oidc"
46156
+ });
46157
+ urlsToTry.push({
46158
+ url: new URL(`${pathname}/.well-known/openid-configuration`, url2.origin),
46159
+ type: "oidc"
46160
+ });
46161
+ return urlsToTry;
46162
+ }
46163
+ async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn = fetch, protocolVersion = LATEST_PROTOCOL_VERSION } = {}) {
46164
+ const headers = {
46165
+ "MCP-Protocol-Version": protocolVersion,
46166
+ Accept: "application/json"
46167
+ };
46168
+ const urlsToTry = buildDiscoveryUrls(authorizationServerUrl);
46169
+ for (const { url: endpointUrl, type: type2 } of urlsToTry) {
46170
+ const response = await fetchWithCorsRetry(endpointUrl, headers, fetchFn);
46171
+ if (!response) {
46172
+ continue;
46173
+ }
46174
+ if (!response.ok) {
46175
+ await response.body?.cancel();
46176
+ if (response.status >= 400 && response.status < 500) {
46177
+ continue;
46178
+ }
46179
+ throw new Error(`HTTP ${response.status} trying to load ${type2 === "oauth" ? "OAuth" : "OpenID provider"} metadata from ${endpointUrl}`);
46180
+ }
46181
+ if (type2 === "oauth") {
46182
+ return OAuthMetadataSchema.parse(await response.json());
46183
+ } else {
46184
+ return OpenIdProviderDiscoveryMetadataSchema.parse(await response.json());
46185
+ }
46186
+ }
46187
+ return;
46188
+ }
46189
+ async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state: state2, resource }) {
46190
+ let authorizationUrl;
46191
+ if (metadata) {
46192
+ authorizationUrl = new URL(metadata.authorization_endpoint);
46193
+ if (!metadata.response_types_supported.includes(AUTHORIZATION_CODE_RESPONSE_TYPE)) {
46194
+ throw new Error(`Incompatible auth server: does not support response type ${AUTHORIZATION_CODE_RESPONSE_TYPE}`);
46195
+ }
46196
+ if (metadata.code_challenge_methods_supported && !metadata.code_challenge_methods_supported.includes(AUTHORIZATION_CODE_CHALLENGE_METHOD)) {
46197
+ throw new Error(`Incompatible auth server: does not support code challenge method ${AUTHORIZATION_CODE_CHALLENGE_METHOD}`);
46198
+ }
46199
+ } else {
46200
+ authorizationUrl = new URL("/authorize", authorizationServerUrl);
46201
+ }
46202
+ const challenge = await pkceChallenge();
46203
+ const codeVerifier = challenge.code_verifier;
46204
+ const codeChallenge = challenge.code_challenge;
46205
+ authorizationUrl.searchParams.set("response_type", AUTHORIZATION_CODE_RESPONSE_TYPE);
46206
+ authorizationUrl.searchParams.set("client_id", clientInformation.client_id);
46207
+ authorizationUrl.searchParams.set("code_challenge", codeChallenge);
46208
+ authorizationUrl.searchParams.set("code_challenge_method", AUTHORIZATION_CODE_CHALLENGE_METHOD);
46209
+ authorizationUrl.searchParams.set("redirect_uri", String(redirectUrl));
46210
+ if (state2) {
46211
+ authorizationUrl.searchParams.set("state", state2);
46212
+ }
46213
+ if (scope) {
46214
+ authorizationUrl.searchParams.set("scope", scope);
46215
+ }
46216
+ if (scope?.includes("offline_access")) {
46217
+ authorizationUrl.searchParams.append("prompt", "consent");
46218
+ }
46219
+ if (resource) {
46220
+ authorizationUrl.searchParams.set("resource", resource.href);
46221
+ }
46222
+ return { authorizationUrl, codeVerifier };
46223
+ }
46224
+ function prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, redirectUri) {
46225
+ return new URLSearchParams({
46226
+ grant_type: "authorization_code",
46227
+ code: authorizationCode,
46228
+ code_verifier: codeVerifier,
46229
+ redirect_uri: String(redirectUri)
46230
+ });
46231
+ }
46232
+ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequestParams, clientInformation, addClientAuthentication, resource, fetchFn }) {
46233
+ const tokenUrl = metadata?.token_endpoint ? new URL(metadata.token_endpoint) : new URL("/token", authorizationServerUrl);
46234
+ const headers = new Headers({
46235
+ "Content-Type": "application/x-www-form-urlencoded",
46236
+ Accept: "application/json"
46237
+ });
46238
+ if (resource) {
46239
+ tokenRequestParams.set("resource", resource.href);
46240
+ }
46241
+ if (addClientAuthentication) {
46242
+ await addClientAuthentication(headers, tokenRequestParams, tokenUrl, metadata);
46243
+ } else if (clientInformation) {
46244
+ const supportedMethods = metadata?.token_endpoint_auth_methods_supported ?? [];
46245
+ const authMethod = selectClientAuthMethod(clientInformation, supportedMethods);
46246
+ applyClientAuthentication(authMethod, clientInformation, headers, tokenRequestParams);
46247
+ }
46248
+ const response = await (fetchFn ?? fetch)(tokenUrl, {
46249
+ method: "POST",
46250
+ headers,
46251
+ body: tokenRequestParams
46252
+ });
46253
+ if (!response.ok) {
46254
+ throw await parseErrorResponse(response);
46255
+ }
46256
+ return OAuthTokensSchema.parse(await response.json());
46257
+ }
46258
+ async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }) {
46259
+ const tokenRequestParams = new URLSearchParams({
46260
+ grant_type: "refresh_token",
46261
+ refresh_token: refreshToken
46262
+ });
46263
+ const tokens = await executeTokenRequest(authorizationServerUrl, {
46264
+ metadata,
46265
+ tokenRequestParams,
46266
+ clientInformation,
46267
+ addClientAuthentication,
46268
+ resource,
46269
+ fetchFn
46270
+ });
46271
+ return { refresh_token: refreshToken, ...tokens };
46272
+ }
46273
+ async function fetchToken(provider, authorizationServerUrl, { metadata, resource, authorizationCode, fetchFn } = {}) {
46274
+ const scope = provider.clientMetadata.scope;
46275
+ let tokenRequestParams;
46276
+ if (provider.prepareTokenRequest) {
46277
+ tokenRequestParams = await provider.prepareTokenRequest(scope);
46278
+ }
46279
+ if (!tokenRequestParams) {
46280
+ if (!authorizationCode) {
46281
+ throw new Error("Either provider.prepareTokenRequest() or authorizationCode is required");
46282
+ }
46283
+ if (!provider.redirectUrl) {
46284
+ throw new Error("redirectUrl is required for authorization_code flow");
46285
+ }
46286
+ const codeVerifier = await provider.codeVerifier();
46287
+ tokenRequestParams = prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, provider.redirectUrl);
46288
+ }
46289
+ const clientInformation = await provider.clientInformation();
46290
+ return executeTokenRequest(authorizationServerUrl, {
46291
+ metadata,
46292
+ tokenRequestParams,
46293
+ clientInformation: clientInformation ?? undefined,
46294
+ addClientAuthentication: provider.addClientAuthentication,
46295
+ resource,
46296
+ fetchFn
46297
+ });
46298
+ }
46299
+ async function registerClient(authorizationServerUrl, { metadata, clientMetadata, fetchFn }) {
46300
+ let registrationUrl;
46301
+ if (metadata) {
46302
+ if (!metadata.registration_endpoint) {
46303
+ throw new Error("Incompatible auth server: does not support dynamic client registration");
46304
+ }
46305
+ registrationUrl = new URL(metadata.registration_endpoint);
46306
+ } else {
46307
+ registrationUrl = new URL("/register", authorizationServerUrl);
46308
+ }
46309
+ const response = await (fetchFn ?? fetch)(registrationUrl, {
46310
+ method: "POST",
46311
+ headers: {
46312
+ "Content-Type": "application/json"
46313
+ },
46314
+ body: JSON.stringify(clientMetadata)
46315
+ });
46316
+ if (!response.ok) {
46317
+ throw await parseErrorResponse(response);
46318
+ }
46319
+ return OAuthClientInformationFullSchema.parse(await response.json());
46320
+ }
46321
+
46322
+ // node_modules/eventsource-parser/dist/index.js
46323
+ class ParseError2 extends Error {
46324
+ constructor(message, options) {
46325
+ super(message), this.name = "ParseError", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;
46326
+ }
46327
+ }
46328
+ function noop(_arg) {}
46329
+ function createParser(callbacks) {
46330
+ if (typeof callbacks == "function")
46331
+ throw new TypeError("`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?");
46332
+ const { onEvent = noop, onError = noop, onRetry = noop, onComment } = callbacks;
46333
+ let incompleteLine = "", isFirstChunk = true, id, data = "", eventType = "";
46334
+ function feed(newChunk) {
46335
+ const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] = splitLines(`${incompleteLine}${chunk}`);
46336
+ for (const line of complete)
46337
+ parseLine(line);
46338
+ incompleteLine = incomplete, isFirstChunk = false;
46339
+ }
46340
+ function parseLine(line) {
46341
+ if (line === "") {
46342
+ dispatchEvent();
46343
+ return;
46344
+ }
46345
+ if (line.startsWith(":")) {
46346
+ onComment && onComment(line.slice(line.startsWith(": ") ? 2 : 1));
46347
+ return;
46348
+ }
46349
+ const fieldSeparatorIndex = line.indexOf(":");
46350
+ if (fieldSeparatorIndex !== -1) {
46351
+ const field = line.slice(0, fieldSeparatorIndex), offset = line[fieldSeparatorIndex + 1] === " " ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
46352
+ processField(field, value, line);
46353
+ return;
46354
+ }
46355
+ processField(line, "", line);
46356
+ }
46357
+ function processField(field, value, line) {
46358
+ switch (field) {
46359
+ case "event":
46360
+ eventType = value;
46361
+ break;
46362
+ case "data":
46363
+ data = `${data}${value}
46364
+ `;
46365
+ break;
46366
+ case "id":
46367
+ id = value.includes("\x00") ? undefined : value;
46368
+ break;
46369
+ case "retry":
46370
+ /^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(new ParseError2(`Invalid \`retry\` value: "${value}"`, {
46371
+ type: "invalid-retry",
46372
+ value,
46373
+ line
46374
+ }));
46375
+ break;
46376
+ default:
46377
+ onError(new ParseError2(`Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`, { type: "unknown-field", field, value, line }));
46378
+ break;
46379
+ }
46380
+ }
46381
+ function dispatchEvent() {
46382
+ data.length > 0 && onEvent({
46383
+ id,
46384
+ event: eventType || undefined,
46385
+ data: data.endsWith(`
46386
+ `) ? data.slice(0, -1) : data
46387
+ }), id = undefined, data = "", eventType = "";
46388
+ }
46389
+ function reset(options = {}) {
46390
+ incompleteLine && options.consume && parseLine(incompleteLine), isFirstChunk = true, id = undefined, data = "", eventType = "", incompleteLine = "";
46391
+ }
46392
+ return { feed, reset };
46393
+ }
46394
+ function splitLines(chunk) {
46395
+ const lines = [];
46396
+ let incompleteLine = "", searchIndex = 0;
46397
+ for (;searchIndex < chunk.length; ) {
46398
+ const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
46399
+ `, searchIndex);
46400
+ let lineEnd = -1;
46401
+ if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = Math.min(crIndex, lfIndex) : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1) {
46402
+ incompleteLine = chunk.slice(searchIndex);
46403
+ break;
46404
+ } else {
46405
+ const line = chunk.slice(searchIndex, lineEnd);
46406
+ lines.push(line), searchIndex = lineEnd + 1, chunk[searchIndex - 1] === "\r" && chunk[searchIndex] === `
46407
+ ` && searchIndex++;
46408
+ }
46409
+ }
46410
+ return [lines, incompleteLine];
46411
+ }
46412
+
46413
+ // node_modules/eventsource-parser/dist/stream.js
46414
+ class EventSourceParserStream extends TransformStream {
46415
+ constructor({ onError, onRetry, onComment } = {}) {
46416
+ let parser;
46417
+ super({
46418
+ start(controller) {
46419
+ parser = createParser({
46420
+ onEvent: (event) => {
46421
+ controller.enqueue(event);
46422
+ },
46423
+ onError(error45) {
46424
+ onError === "terminate" ? controller.error(error45) : typeof onError == "function" && onError(error45);
46425
+ },
46426
+ onRetry,
46427
+ onComment
46428
+ });
46429
+ },
46430
+ transform(chunk) {
46431
+ parser.feed(chunk);
46432
+ }
46433
+ });
46434
+ }
46435
+ }
46436
+
46437
+ // node_modules/@modelcontextprotocol/sdk/dist/esm/client/streamableHttp.js
46438
+ var DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS = {
46439
+ initialReconnectionDelay: 1000,
46440
+ maxReconnectionDelay: 30000,
46441
+ reconnectionDelayGrowFactor: 1.5,
46442
+ maxRetries: 2
46443
+ };
46444
+
46445
+ class StreamableHTTPError extends Error {
46446
+ constructor(code, message) {
46447
+ super(`Streamable HTTP error: ${message}`);
46448
+ this.code = code;
46449
+ }
46450
+ }
46451
+
46452
+ class StreamableHTTPClientTransport {
46453
+ constructor(url2, opts) {
46454
+ this._hasCompletedAuthFlow = false;
46455
+ this._url = url2;
46456
+ this._resourceMetadataUrl = undefined;
46457
+ this._scope = undefined;
46458
+ this._requestInit = opts?.requestInit;
46459
+ this._authProvider = opts?.authProvider;
46460
+ this._fetch = opts?.fetch;
46461
+ this._fetchWithInit = createFetchWithInit(opts?.fetch, opts?.requestInit);
46462
+ this._sessionId = opts?.sessionId;
46463
+ this._reconnectionOptions = opts?.reconnectionOptions ?? DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS;
46464
+ }
46465
+ async _authThenStart() {
46466
+ if (!this._authProvider) {
46467
+ throw new UnauthorizedError("No auth provider");
46468
+ }
46469
+ let result;
46470
+ try {
46471
+ result = await auth(this._authProvider, {
46472
+ serverUrl: this._url,
46473
+ resourceMetadataUrl: this._resourceMetadataUrl,
46474
+ scope: this._scope,
46475
+ fetchFn: this._fetchWithInit
46476
+ });
46477
+ } catch (error45) {
46478
+ this.onerror?.(error45);
46479
+ throw error45;
46480
+ }
46481
+ if (result !== "AUTHORIZED") {
46482
+ throw new UnauthorizedError;
46483
+ }
46484
+ return await this._startOrAuthSse({ resumptionToken: undefined });
46485
+ }
46486
+ async _commonHeaders() {
46487
+ const headers = {};
46488
+ if (this._authProvider) {
46489
+ const tokens = await this._authProvider.tokens();
46490
+ if (tokens) {
46491
+ headers["Authorization"] = `Bearer ${tokens.access_token}`;
46492
+ }
46493
+ }
46494
+ if (this._sessionId) {
46495
+ headers["mcp-session-id"] = this._sessionId;
46496
+ }
46497
+ if (this._protocolVersion) {
46498
+ headers["mcp-protocol-version"] = this._protocolVersion;
46499
+ }
46500
+ const extraHeaders = normalizeHeaders(this._requestInit?.headers);
46501
+ return new Headers({
46502
+ ...headers,
46503
+ ...extraHeaders
46504
+ });
46505
+ }
46506
+ async _startOrAuthSse(options) {
46507
+ const { resumptionToken } = options;
46508
+ try {
46509
+ const headers = await this._commonHeaders();
46510
+ headers.set("Accept", "text/event-stream");
46511
+ if (resumptionToken) {
46512
+ headers.set("last-event-id", resumptionToken);
46513
+ }
46514
+ const response = await (this._fetch ?? fetch)(this._url, {
46515
+ method: "GET",
46516
+ headers,
46517
+ signal: this._abortController?.signal
46518
+ });
46519
+ if (!response.ok) {
46520
+ await response.body?.cancel();
46521
+ if (response.status === 401 && this._authProvider) {
46522
+ return await this._authThenStart();
46523
+ }
46524
+ if (response.status === 405) {
46525
+ return;
46526
+ }
46527
+ throw new StreamableHTTPError(response.status, `Failed to open SSE stream: ${response.statusText}`);
46528
+ }
46529
+ this._handleSseStream(response.body, options, true);
46530
+ } catch (error45) {
46531
+ this.onerror?.(error45);
46532
+ throw error45;
46533
+ }
46534
+ }
46535
+ _getNextReconnectionDelay(attempt) {
46536
+ if (this._serverRetryMs !== undefined) {
46537
+ return this._serverRetryMs;
46538
+ }
46539
+ const initialDelay = this._reconnectionOptions.initialReconnectionDelay;
46540
+ const growFactor = this._reconnectionOptions.reconnectionDelayGrowFactor;
46541
+ const maxDelay = this._reconnectionOptions.maxReconnectionDelay;
46542
+ return Math.min(initialDelay * Math.pow(growFactor, attempt), maxDelay);
46543
+ }
46544
+ _scheduleReconnection(options, attemptCount = 0) {
46545
+ const maxRetries = this._reconnectionOptions.maxRetries;
46546
+ if (attemptCount >= maxRetries) {
46547
+ this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));
46548
+ return;
46549
+ }
46550
+ const delay2 = this._getNextReconnectionDelay(attemptCount);
46551
+ this._reconnectionTimeout = setTimeout(() => {
46552
+ this._startOrAuthSse(options).catch((error45) => {
46553
+ this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error45 instanceof Error ? error45.message : String(error45)}`));
46554
+ this._scheduleReconnection(options, attemptCount + 1);
46555
+ });
46556
+ }, delay2);
46557
+ }
46558
+ _handleSseStream(stream, options, isReconnectable) {
46559
+ if (!stream) {
46560
+ return;
46561
+ }
46562
+ const { onresumptiontoken, replayMessageId } = options;
46563
+ let lastEventId;
46564
+ let hasPrimingEvent = false;
46565
+ let receivedResponse = false;
46566
+ const processStream = async () => {
46567
+ try {
46568
+ const reader = stream.pipeThrough(new TextDecoderStream).pipeThrough(new EventSourceParserStream({
46569
+ onRetry: (retryMs) => {
46570
+ this._serverRetryMs = retryMs;
46571
+ }
46572
+ })).getReader();
46573
+ while (true) {
46574
+ const { value: event, done } = await reader.read();
46575
+ if (done) {
46576
+ break;
46577
+ }
46578
+ if (event.id) {
46579
+ lastEventId = event.id;
46580
+ hasPrimingEvent = true;
46581
+ onresumptiontoken?.(event.id);
46582
+ }
46583
+ if (!event.data) {
46584
+ continue;
46585
+ }
46586
+ if (!event.event || event.event === "message") {
46587
+ try {
46588
+ const message = JSONRPCMessageSchema.parse(JSON.parse(event.data));
46589
+ if (isJSONRPCResultResponse(message)) {
46590
+ receivedResponse = true;
46591
+ if (replayMessageId !== undefined) {
46592
+ message.id = replayMessageId;
46593
+ }
46594
+ }
46595
+ this.onmessage?.(message);
46596
+ } catch (error45) {
46597
+ this.onerror?.(error45);
46598
+ }
46599
+ }
46600
+ }
46601
+ const canResume = isReconnectable || hasPrimingEvent;
46602
+ const needsReconnect = canResume && !receivedResponse;
46603
+ if (needsReconnect && this._abortController && !this._abortController.signal.aborted) {
46604
+ this._scheduleReconnection({
46605
+ resumptionToken: lastEventId,
46606
+ onresumptiontoken,
46607
+ replayMessageId
46608
+ }, 0);
46609
+ }
46610
+ } catch (error45) {
46611
+ this.onerror?.(new Error(`SSE stream disconnected: ${error45}`));
46612
+ const canResume = isReconnectable || hasPrimingEvent;
46613
+ const needsReconnect = canResume && !receivedResponse;
46614
+ if (needsReconnect && this._abortController && !this._abortController.signal.aborted) {
46615
+ try {
46616
+ this._scheduleReconnection({
46617
+ resumptionToken: lastEventId,
46618
+ onresumptiontoken,
46619
+ replayMessageId
46620
+ }, 0);
46621
+ } catch (error46) {
46622
+ this.onerror?.(new Error(`Failed to reconnect: ${error46 instanceof Error ? error46.message : String(error46)}`));
46623
+ }
46624
+ }
46625
+ }
46626
+ };
46627
+ processStream();
46628
+ }
46629
+ async start() {
46630
+ if (this._abortController) {
46631
+ throw new Error("StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.");
46632
+ }
46633
+ this._abortController = new AbortController;
46634
+ }
46635
+ async finishAuth(authorizationCode) {
46636
+ if (!this._authProvider) {
46637
+ throw new UnauthorizedError("No auth provider");
46638
+ }
46639
+ const result = await auth(this._authProvider, {
46640
+ serverUrl: this._url,
46641
+ authorizationCode,
46642
+ resourceMetadataUrl: this._resourceMetadataUrl,
46643
+ scope: this._scope,
46644
+ fetchFn: this._fetchWithInit
46645
+ });
46646
+ if (result !== "AUTHORIZED") {
46647
+ throw new UnauthorizedError("Failed to authorize");
46648
+ }
46649
+ }
46650
+ async close() {
46651
+ if (this._reconnectionTimeout) {
46652
+ clearTimeout(this._reconnectionTimeout);
46653
+ this._reconnectionTimeout = undefined;
46654
+ }
46655
+ this._abortController?.abort();
46656
+ this.onclose?.();
46657
+ }
46658
+ async send(message, options) {
46659
+ try {
46660
+ const { resumptionToken, onresumptiontoken } = options || {};
46661
+ if (resumptionToken) {
46662
+ this._startOrAuthSse({ resumptionToken, replayMessageId: isJSONRPCRequest(message) ? message.id : undefined }).catch((err) => this.onerror?.(err));
46663
+ return;
46664
+ }
46665
+ const headers = await this._commonHeaders();
46666
+ headers.set("content-type", "application/json");
46667
+ headers.set("accept", "application/json, text/event-stream");
46668
+ const init = {
46669
+ ...this._requestInit,
46670
+ method: "POST",
46671
+ headers,
46672
+ body: JSON.stringify(message),
46673
+ signal: this._abortController?.signal
46674
+ };
46675
+ const response = await (this._fetch ?? fetch)(this._url, init);
46676
+ const sessionId = response.headers.get("mcp-session-id");
46677
+ if (sessionId) {
46678
+ this._sessionId = sessionId;
46679
+ }
46680
+ if (!response.ok) {
46681
+ const text = await response.text().catch(() => null);
46682
+ if (response.status === 401 && this._authProvider) {
46683
+ if (this._hasCompletedAuthFlow) {
46684
+ throw new StreamableHTTPError(401, "Server returned 401 after successful authentication");
46685
+ }
46686
+ const { resourceMetadataUrl, scope } = extractWWWAuthenticateParams(response);
46687
+ this._resourceMetadataUrl = resourceMetadataUrl;
46688
+ this._scope = scope;
46689
+ const result = await auth(this._authProvider, {
46690
+ serverUrl: this._url,
46691
+ resourceMetadataUrl: this._resourceMetadataUrl,
46692
+ scope: this._scope,
46693
+ fetchFn: this._fetchWithInit
46694
+ });
46695
+ if (result !== "AUTHORIZED") {
46696
+ throw new UnauthorizedError;
46697
+ }
46698
+ this._hasCompletedAuthFlow = true;
46699
+ return this.send(message);
46700
+ }
46701
+ if (response.status === 403 && this._authProvider) {
46702
+ const { resourceMetadataUrl, scope, error: error45 } = extractWWWAuthenticateParams(response);
46703
+ if (error45 === "insufficient_scope") {
46704
+ const wwwAuthHeader = response.headers.get("WWW-Authenticate");
46705
+ if (this._lastUpscopingHeader === wwwAuthHeader) {
46706
+ throw new StreamableHTTPError(403, "Server returned 403 after trying upscoping");
46707
+ }
46708
+ if (scope) {
46709
+ this._scope = scope;
46710
+ }
46711
+ if (resourceMetadataUrl) {
46712
+ this._resourceMetadataUrl = resourceMetadataUrl;
46713
+ }
46714
+ this._lastUpscopingHeader = wwwAuthHeader ?? undefined;
46715
+ const result = await auth(this._authProvider, {
46716
+ serverUrl: this._url,
46717
+ resourceMetadataUrl: this._resourceMetadataUrl,
46718
+ scope: this._scope,
46719
+ fetchFn: this._fetch
46720
+ });
46721
+ if (result !== "AUTHORIZED") {
46722
+ throw new UnauthorizedError;
46723
+ }
46724
+ return this.send(message);
46725
+ }
46726
+ }
46727
+ throw new StreamableHTTPError(response.status, `Error POSTing to endpoint: ${text}`);
46728
+ }
46729
+ this._hasCompletedAuthFlow = false;
46730
+ this._lastUpscopingHeader = undefined;
46731
+ if (response.status === 202) {
46732
+ await response.body?.cancel();
46733
+ if (isInitializedNotification(message)) {
46734
+ this._startOrAuthSse({ resumptionToken: undefined }).catch((err) => this.onerror?.(err));
46735
+ }
46736
+ return;
46737
+ }
46738
+ const messages = Array.isArray(message) ? message : [message];
46739
+ const hasRequests = messages.filter((msg) => ("method" in msg) && ("id" in msg) && msg.id !== undefined).length > 0;
46740
+ const contentType = response.headers.get("content-type");
46741
+ if (hasRequests) {
46742
+ if (contentType?.includes("text/event-stream")) {
46743
+ this._handleSseStream(response.body, { onresumptiontoken }, false);
46744
+ } else if (contentType?.includes("application/json")) {
46745
+ const data = await response.json();
46746
+ const responseMessages = Array.isArray(data) ? data.map((msg) => JSONRPCMessageSchema.parse(msg)) : [JSONRPCMessageSchema.parse(data)];
46747
+ for (const msg of responseMessages) {
46748
+ this.onmessage?.(msg);
46749
+ }
46750
+ } else {
46751
+ await response.body?.cancel();
46752
+ throw new StreamableHTTPError(-1, `Unexpected content type: ${contentType}`);
46753
+ }
46754
+ } else {
46755
+ await response.body?.cancel();
46756
+ }
46757
+ } catch (error45) {
46758
+ this.onerror?.(error45);
46759
+ throw error45;
46760
+ }
46761
+ }
46762
+ get sessionId() {
46763
+ return this._sessionId;
46764
+ }
46765
+ async terminateSession() {
46766
+ if (!this._sessionId) {
46767
+ return;
46768
+ }
46769
+ try {
46770
+ const headers = await this._commonHeaders();
46771
+ const init = {
46772
+ ...this._requestInit,
46773
+ method: "DELETE",
46774
+ headers,
46775
+ signal: this._abortController?.signal
46776
+ };
46777
+ const response = await (this._fetch ?? fetch)(this._url, init);
46778
+ await response.body?.cancel();
46779
+ if (!response.ok && response.status !== 405) {
46780
+ throw new StreamableHTTPError(response.status, `Failed to terminate session: ${response.statusText}`);
46781
+ }
46782
+ this._sessionId = undefined;
46783
+ } catch (error45) {
46784
+ this.onerror?.(error45);
46785
+ throw error45;
46786
+ }
46787
+ }
46788
+ setProtocolVersion(version2) {
46789
+ this._protocolVersion = version2;
46790
+ }
46791
+ get protocolVersion() {
46792
+ return this._protocolVersion;
46793
+ }
46794
+ async resumeStream(lastEventId, options) {
46795
+ await this._startOrAuthSse({
46796
+ resumptionToken: lastEventId,
46797
+ onresumptiontoken: options?.onresumptiontoken
46798
+ });
46799
+ }
46800
+ }
46801
+
45040
46802
  // src/features/skill-mcp-manager/env-cleaner.ts
45041
46803
  var EXCLUDED_ENV_PATTERNS = [
45042
46804
  /^NPM_CONFIG_/i,
@@ -45060,6 +46822,22 @@ function createCleanMcpEnvironment(customEnv = {}) {
45060
46822
  }
45061
46823
 
45062
46824
  // src/features/skill-mcp-manager/manager.ts
46825
+ function getConnectionType(config3) {
46826
+ if (config3.type === "http" || config3.type === "sse") {
46827
+ return "http";
46828
+ }
46829
+ if (config3.type === "stdio") {
46830
+ return "stdio";
46831
+ }
46832
+ if (config3.url) {
46833
+ return "http";
46834
+ }
46835
+ if (config3.command) {
46836
+ return "stdio";
46837
+ }
46838
+ return null;
46839
+ }
46840
+
45063
46841
  class SkillMcpManager {
45064
46842
  clients = new Map;
45065
46843
  pendingConnections = new Map;
@@ -45122,17 +46900,88 @@ class SkillMcpManager {
45122
46900
  }
45123
46901
  }
45124
46902
  async createClient(info, config3) {
46903
+ const connectionType = getConnectionType(config3);
46904
+ if (!connectionType) {
46905
+ throw new Error(`MCP server "${info.serverName}" has no valid connection configuration.
46906
+
46907
+ ` + `The MCP configuration in skill "${info.skillName}" must specify either:
46908
+ ` + ` - A URL for HTTP connection (remote MCP server)
46909
+ ` + ` - A command for stdio connection (local MCP process)
46910
+
46911
+ ` + `Examples:
46912
+ ` + ` HTTP:
46913
+ ` + ` mcp:
46914
+ ` + ` ${info.serverName}:
46915
+ ` + ` url: https://mcp.example.com/mcp
46916
+ ` + ` headers:
46917
+ ` + ` Authorization: Bearer \${API_KEY}
46918
+
46919
+ ` + ` Stdio:
46920
+ ` + ` mcp:
46921
+ ` + ` ${info.serverName}:
46922
+ ` + ` command: npx
46923
+ ` + ` args: [-y, @some/mcp-server]`);
46924
+ }
46925
+ if (connectionType === "http") {
46926
+ return this.createHttpClient(info, config3);
46927
+ } else {
46928
+ return this.createStdioClient(info, config3);
46929
+ }
46930
+ }
46931
+ async createHttpClient(info, config3) {
45125
46932
  const key = this.getClientKey(info);
45126
- if (!config3.command) {
45127
- throw new Error(`MCP server "${info.serverName}" is missing required 'command' field.
46933
+ if (!config3.url) {
46934
+ throw new Error(`MCP server "${info.serverName}" is configured for HTTP but missing 'url' field.`);
46935
+ }
46936
+ let url2;
46937
+ try {
46938
+ url2 = new URL(config3.url);
46939
+ } catch {
46940
+ throw new Error(`MCP server "${info.serverName}" has invalid URL: ${config3.url}
45128
46941
 
45129
- ` + `The MCP configuration in skill "${info.skillName}" must specify a command to execute.
46942
+ ` + `Expected a valid URL like: https://mcp.example.com/mcp`);
46943
+ }
46944
+ this.registerProcessCleanup();
46945
+ const requestInit = {};
46946
+ if (config3.headers && Object.keys(config3.headers).length > 0) {
46947
+ requestInit.headers = config3.headers;
46948
+ }
46949
+ const transport = new StreamableHTTPClientTransport(url2, {
46950
+ requestInit: Object.keys(requestInit).length > 0 ? requestInit : undefined
46951
+ });
46952
+ const client2 = new Client({ name: `skill-mcp-${info.skillName}-${info.serverName}`, version: "1.0.0" }, { capabilities: {} });
46953
+ try {
46954
+ await client2.connect(transport);
46955
+ } catch (error45) {
46956
+ try {
46957
+ await transport.close();
46958
+ } catch {}
46959
+ const errorMessage = error45 instanceof Error ? error45.message : String(error45);
46960
+ throw new Error(`Failed to connect to MCP server "${info.serverName}".
45130
46961
 
45131
- ` + `Example:
45132
- ` + ` mcp:
45133
- ` + ` ${info.serverName}:
45134
- ` + ` command: npx
45135
- ` + ` args: [-y, @some/mcp-server]`);
46962
+ ` + `URL: ${config3.url}
46963
+ ` + `Reason: ${errorMessage}
46964
+
46965
+ ` + `Hints:
46966
+ ` + ` - Verify the URL is correct and the server is running
46967
+ ` + ` - Check if authentication headers are required
46968
+ ` + ` - Ensure the server supports MCP over HTTP`);
46969
+ }
46970
+ const managedClient = {
46971
+ client: client2,
46972
+ transport,
46973
+ skillName: info.skillName,
46974
+ lastUsedAt: Date.now(),
46975
+ connectionType: "http"
46976
+ };
46977
+ this.clients.set(key, managedClient);
46978
+ this.startCleanupTimer();
46979
+ return client2;
46980
+ }
46981
+ async createStdioClient(info, config3) {
46982
+ const key = this.getClientKey(info);
46983
+ if (!config3.command) {
46984
+ throw new Error(`MCP server "${info.serverName}" is configured for stdio but missing 'command' field.`);
45136
46985
  }
45137
46986
  const command = config3.command;
45138
46987
  const args = config3.args || [];
@@ -45162,7 +47011,14 @@ class SkillMcpManager {
45162
47011
  ` + ` - Check if the MCP server package exists
45163
47012
  ` + ` - Verify the args are correct for this server`);
45164
47013
  }
45165
- this.clients.set(key, { client: client2, transport, skillName: info.skillName, lastUsedAt: Date.now() });
47014
+ const managedClient = {
47015
+ client: client2,
47016
+ transport,
47017
+ skillName: info.skillName,
47018
+ lastUsedAt: Date.now(),
47019
+ connectionType: "stdio"
47020
+ };
47021
+ this.clients.set(key, managedClient);
45166
47022
  this.startCleanupTimer();
45167
47023
  return client2;
45168
47024
  }
@@ -45365,6 +47221,7 @@ var HookNameSchema = exports_external.enum([
45365
47221
  "claude-code-hooks",
45366
47222
  "auto-slash-command",
45367
47223
  "edit-error-recovery",
47224
+ "sisyphus-task-retry",
45368
47225
  "prometheus-md-only",
45369
47226
  "start-work",
45370
47227
  "sisyphus-orchestrator"
@@ -45828,28 +47685,55 @@ function buildFrontendSection(agents) {
45828
47685
  const frontendAgent = agents.find((a) => a.name === "frontend-ui-ux-engineer");
45829
47686
  if (!frontendAgent)
45830
47687
  return "";
45831
- return `### Frontend Files: Decision Gate (NOT a blind block)
47688
+ return `### Frontend Files: VISUAL = HARD BLOCK (zero tolerance)
45832
47689
 
45833
- Frontend files (.tsx, .jsx, .vue, .svelte, .css, etc.) require **classification before action**.
47690
+ **DEFAULT ASSUMPTION**: Any frontend file change is VISUAL until proven otherwise.
45834
47691
 
45835
- #### Step 1: Classify the Change Type
47692
+ #### HARD BLOCK: Visual Changes (NEVER touch directly)
45836
47693
 
45837
- | Change Type | Examples | Action |
45838
- |-------------|----------|--------|
45839
- | **Visual/UI/UX** | Color, spacing, layout, typography, animation, responsive breakpoints, hover states, shadows, borders, icons, images | **DELEGATE** to \`frontend-ui-ux-engineer\` |
45840
- | **Pure Logic** | API calls, data fetching, state management, event handlers (non-visual), type definitions, utility functions, business logic | **CAN handle directly** |
45841
- | **Mixed** | Component changes both visual AND logic | **Split**: handle logic yourself, delegate visual to \`frontend-ui-ux-engineer\` |
47694
+ | Pattern | Action | No Exceptions |
47695
+ |---------|--------|---------------|
47696
+ | \`.tsx\`, \`.jsx\` with styling | DELEGATE | Even "just add className" |
47697
+ | \`.vue\`, \`.svelte\` | DELEGATE | Even single prop change |
47698
+ | \`.css\`, \`.scss\`, \`.sass\`, \`.less\` | DELEGATE | Even color/margin tweak |
47699
+ | Any file with visual keywords | DELEGATE | See keyword list below |
45842
47700
 
45843
- #### Step 2: Ask Yourself
47701
+ #### Keyword Detection (INSTANT DELEGATE)
45844
47702
 
45845
- Before touching any frontend file, think:
45846
- > "Is this change about **how it LOOKS** or **how it WORKS**?"
47703
+ If your change involves **ANY** of these keywords \u2192 **STOP. DELEGATE.**
45847
47704
 
45848
- - **LOOKS** (colors, sizes, positions, animations) \u2192 DELEGATE
45849
- - **WORKS** (data flow, API integration, state) \u2192 Handle directly
47705
+ \`\`\`
47706
+ style, className, tailwind, css, color, background, border, shadow,
47707
+ margin, padding, width, height, flex, grid, animation, transition,
47708
+ hover, responsive, font-size, font-weight, icon, svg, image, layout,
47709
+ position, display, opacity, z-index, transform, gradient, theme
47710
+ \`\`\`
45850
47711
 
45851
- #### When in Doubt \u2192 DELEGATE if ANY of these keywords involved:
45852
- style, className, tailwind, color, background, border, shadow, margin, padding, width, height, flex, grid, animation, transition, hover, responsive, font-size, icon, svg`;
47712
+ **YOU CANNOT**:
47713
+ - "Just quickly fix this style"
47714
+ - "It's only one className"
47715
+ - "Too simple to delegate"
47716
+
47717
+ #### EXCEPTION: Pure Logic Only
47718
+
47719
+ You MAY handle directly **ONLY IF ALL** conditions are met:
47720
+ 1. Change is **100% logic** (API, state, event handlers, types, utils)
47721
+ 2. **Zero** visual keywords in your diff
47722
+ 3. No styling, layout, or appearance changes whatsoever
47723
+
47724
+ | Pure Logic Examples | Visual Examples (DELEGATE) |
47725
+ |---------------------|---------------------------|
47726
+ | Add onClick API call | Change button color |
47727
+ | Fix pagination logic | Add loading spinner animation |
47728
+ | Add form validation | Make modal responsive |
47729
+ | Update state management | Adjust spacing/margins |
47730
+
47731
+ #### Mixed Changes \u2192 SPLIT
47732
+
47733
+ If change has BOTH logic AND visual:
47734
+ 1. Handle logic yourself
47735
+ 2. DELEGATE visual part to \`frontend-ui-ux-engineer\`
47736
+ 3. **Never** combine them into one edit`;
45853
47737
  }
45854
47738
  function buildOracleSection(agents) {
45855
47739
  const oracleAgent = agents.find((a) => a.name === "oracle");
@@ -45889,7 +47773,7 @@ function buildHardBlocksSection(agents) {
45889
47773
  "| Leave code in broken state after failures | Never |"
45890
47774
  ];
45891
47775
  if (frontendAgent) {
45892
- blocks.unshift("| Frontend VISUAL changes (styling, layout, animation) | Always delegate to `frontend-ui-ux-engineer` |");
47776
+ blocks.unshift("| Frontend VISUAL changes (styling, className, layout, animation, any visual keyword) | **HARD BLOCK** - Always delegate to `frontend-ui-ux-engineer`. Zero tolerance. |");
45893
47777
  }
45894
47778
  return `## Hard Blocks (NEVER violate)
45895
47779
 
@@ -45908,7 +47792,7 @@ function buildAntiPatternsSection(agents) {
45908
47792
  "| **Debugging** | Shotgun debugging, random changes |"
45909
47793
  ];
45910
47794
  if (frontendAgent) {
45911
- patterns.splice(4, 0, "| **Frontend** | Direct edit to visual/styling code (logic changes OK) |");
47795
+ patterns.splice(4, 0, "| **Frontend** | ANY direct edit to visual/styling code. Keyword detected = DELEGATE. Pure logic only = OK |");
45912
47796
  }
45913
47797
  return `## Anti-Patterns (BLOCKING violations)
45914
47798
 
@@ -49618,7 +51502,7 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory
49618
51502
  }
49619
51503
  if (!disabledAgents.includes("orchestrator-sisyphus")) {
49620
51504
  const orchestratorOverride = agentOverrides["orchestrator-sisyphus"];
49621
- const orchestratorModel = orchestratorOverride?.model;
51505
+ const orchestratorModel = orchestratorOverride?.model ?? systemDefaultModel;
49622
51506
  let orchestratorConfig = createOrchestratorSisyphusAgent({
49623
51507
  model: orchestratorModel,
49624
51508
  availableAgents
@@ -49705,11 +51589,11 @@ var SISYPHUS_JUNIOR_DEFAULTS = {
49705
51589
  model: "anthropic/claude-sonnet-4-5",
49706
51590
  temperature: 0.1
49707
51591
  };
49708
- function createSisyphusJuniorAgentWithOverrides(override) {
51592
+ function createSisyphusJuniorAgentWithOverrides(override, systemDefaultModel) {
49709
51593
  if (override?.disable) {
49710
51594
  override = undefined;
49711
51595
  }
49712
- const model = override?.model ?? SISYPHUS_JUNIOR_DEFAULTS.model;
51596
+ const model = override?.model ?? systemDefaultModel ?? SISYPHUS_JUNIOR_DEFAULTS.model;
49713
51597
  const temperature = override?.temperature ?? SISYPHUS_JUNIOR_DEFAULTS.temperature;
49714
51598
  const promptAppend = override?.prompt_append;
49715
51599
  const prompt = buildSisyphusJuniorPrompt(promptAppend);
@@ -52485,7 +54369,7 @@ function createConfigHandler(deps) {
52485
54369
  const agentConfig = {
52486
54370
  Sisyphus: builtinAgents.Sisyphus
52487
54371
  };
52488
- agentConfig["Sisyphus-Junior"] = createSisyphusJuniorAgentWithOverrides(pluginConfig.agents?.["Sisyphus-Junior"]);
54372
+ agentConfig["Sisyphus-Junior"] = createSisyphusJuniorAgentWithOverrides(pluginConfig.agents?.["Sisyphus-Junior"], config3.model);
52489
54373
  if (builderEnabled) {
52490
54374
  const { name: _buildName, ...buildConfigWithoutName } = configAgent?.build ?? {};
52491
54375
  const migratedBuildConfig = migrateAgentConfig(buildConfigWithoutName);
@@ -52697,6 +54581,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
52697
54581
  checkSessionExists: async (sessionId) => sessionExists(sessionId)
52698
54582
  }) : null;
52699
54583
  const editErrorRecovery = isHookEnabled("edit-error-recovery") ? createEditErrorRecoveryHook(ctx) : null;
54584
+ const sisyphusTaskRetry = isHookEnabled("sisyphus-task-retry") ? createSisyphusTaskRetryHook(ctx) : null;
52700
54585
  const startWork = isHookEnabled("start-work") ? createStartWorkHook(ctx) : null;
52701
54586
  const sisyphusOrchestrator = isHookEnabled("sisyphus-orchestrator") ? createSisyphusOrchestratorHook(ctx) : null;
52702
54587
  const prometheusMdOnly = isHookEnabled("prometheus-md-only") ? createPrometheusMdOnlyHook(ctx) : null;
@@ -52859,6 +54744,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
52859
54744
  }
52860
54745
  if (sessionInfo?.id) {
52861
54746
  clearSessionAgent(sessionInfo.id);
54747
+ resetMessageCursor(sessionInfo.id);
52862
54748
  firstMessageVariantGate.clear(sessionInfo.id);
52863
54749
  await skillMcpManager.disconnectSession(sessionInfo.id);
52864
54750
  await lspManager.cleanupTempDirectoryClients();
@@ -52943,6 +54829,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
52943
54829
  await agentUsageReminder?.["tool.execute.after"](input, output);
52944
54830
  await interactiveBashSession?.["tool.execute.after"](input, output);
52945
54831
  await editErrorRecovery?.["tool.execute.after"](input, output);
54832
+ await sisyphusTaskRetry?.["tool.execute.after"](input, output);
52946
54833
  await sisyphusOrchestrator?.["tool.execute.after"]?.(input, output);
52947
54834
  await taskResumeInfo["tool.execute.after"](input, output);
52948
54835
  }