oh-my-opencode 3.8.5 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.ja.md +3 -3
  2. package/README.ko.md +3 -3
  3. package/README.md +3 -3
  4. package/README.ru.md +367 -0
  5. package/README.zh-cn.md +3 -3
  6. package/bin/oh-my-opencode.js +96 -34
  7. package/bin/platform.d.ts +14 -0
  8. package/bin/platform.js +44 -0
  9. package/bin/platform.test.ts +56 -1
  10. package/dist/agents/atlas/agent.d.ts +1 -1
  11. package/dist/agents/dynamic-agent-prompt-builder.d.ts +1 -0
  12. package/dist/agents/env-context.d.ts +1 -1
  13. package/dist/agents/hephaestus.d.ts +1 -1
  14. package/dist/agents/sisyphus-gemini-overlays.d.ts +2 -0
  15. package/dist/agents/sisyphus.d.ts +1 -1
  16. package/dist/cli/config-manager/antigravity-provider-configuration.d.ts +3 -3
  17. package/dist/cli/index.js +208 -64
  18. package/dist/cli/run/event-state.d.ts +2 -0
  19. package/dist/cli/run/poll-for-completion.d.ts +2 -0
  20. package/dist/config/schema/agent-overrides.d.ts +1 -0
  21. package/dist/config/schema/categories.d.ts +2 -0
  22. package/dist/config/schema/hooks.d.ts +1 -0
  23. package/dist/config/schema/oh-my-opencode-config.d.ts +3 -12
  24. package/dist/create-hooks.d.ts +1 -0
  25. package/dist/features/background-agent/manager.d.ts +9 -0
  26. package/dist/features/boulder-state/storage.d.ts +1 -1
  27. package/dist/features/boulder-state/types.d.ts +2 -0
  28. package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
  29. package/dist/features/claude-code-plugin-loader/types.d.ts +13 -3
  30. package/dist/features/context-injector/types.d.ts +2 -2
  31. package/dist/features/hook-message-injector/injector.d.ts +2 -0
  32. package/dist/hooks/anthropic-context-window-limit-recovery/types.d.ts +1 -0
  33. package/dist/hooks/atlas/boulder-continuation-injector.d.ts +1 -0
  34. package/dist/hooks/atlas/types.d.ts +1 -0
  35. package/dist/hooks/background-notification/hook.d.ts +11 -0
  36. package/dist/hooks/claude-code-hooks/dispatch-hook.d.ts +4 -0
  37. package/dist/hooks/claude-code-hooks/execute-http-hook.d.ts +4 -0
  38. package/dist/hooks/claude-code-hooks/types.d.ts +9 -1
  39. package/dist/hooks/index.d.ts +1 -0
  40. package/dist/hooks/no-hephaestus-non-gpt/hook.d.ts +5 -1
  41. package/dist/hooks/ralph-loop/completion-promise-detector.d.ts +1 -0
  42. package/dist/hooks/ralph-loop/loop-state-controller.d.ts +2 -0
  43. package/dist/hooks/ralph-loop/ralph-loop-hook.d.ts +1 -0
  44. package/dist/hooks/ralph-loop/types.d.ts +1 -0
  45. package/dist/hooks/read-image-resizer/hook.d.ts +12 -0
  46. package/dist/hooks/read-image-resizer/image-dimensions.d.ts +2 -0
  47. package/dist/hooks/read-image-resizer/image-resizer.d.ts +3 -0
  48. package/dist/hooks/read-image-resizer/index.d.ts +1 -0
  49. package/dist/hooks/read-image-resizer/types.d.ts +14 -0
  50. package/dist/hooks/session-notification.d.ts +1 -0
  51. package/dist/hooks/start-work/index.d.ts +3 -0
  52. package/dist/hooks/start-work/parse-user-request.d.ts +5 -0
  53. package/dist/hooks/start-work/worktree-detector.d.ts +1 -0
  54. package/dist/hooks/stop-continuation-guard/hook.d.ts +6 -1
  55. package/dist/hooks/think-mode/hook.d.ts +14 -2
  56. package/dist/hooks/think-mode/switcher.d.ts +0 -56
  57. package/dist/hooks/think-mode/types.d.ts +1 -15
  58. package/dist/hooks/todo-continuation-enforcer/constants.d.ts +1 -1
  59. package/dist/hooks/todo-continuation-enforcer/pending-question-detection.d.ts +14 -0
  60. package/dist/index.js +2203 -704
  61. package/dist/oh-my-opencode.schema.json +10 -14
  62. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
  63. package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
  64. package/dist/shared/model-suggestion-retry.d.ts +4 -2
  65. package/dist/shared/prompt-timeout-context.d.ts +12 -0
  66. package/dist/shared/spawn-with-windows-hide.d.ts +15 -0
  67. package/dist/tools/delegate-task/category-resolver.d.ts +1 -0
  68. package/dist/tools/delegate-task/skill-resolver.d.ts +1 -0
  69. package/dist/tools/delegate-task/token-limiter.d.ts +4 -0
  70. package/dist/tools/delegate-task/types.d.ts +9 -0
  71. package/dist/tools/hashline-edit/tool-description.d.ts +1 -1
  72. package/dist/tools/hashline-edit/validation.d.ts +1 -0
  73. package/package.json +13 -8
  74. package/postinstall.mjs +23 -7
package/dist/index.js CHANGED
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
7
12
  var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
8
20
  target = mod != null ? __create(__getProtoOf(mod)) : {};
9
21
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
22
  for (let key of __getOwnPropNames(mod))
11
23
  if (!__hasOwnProp.call(to, key))
12
24
  __defProp(to, key, {
13
- get: () => mod[key],
25
+ get: __accessProp.bind(mod, key),
14
26
  enumerable: true
15
27
  });
28
+ if (canCache)
29
+ cache.set(mod, to);
16
30
  return to;
17
31
  };
18
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
19
37
  var __export = (target, all) => {
20
38
  for (var name in all)
21
39
  __defProp(target, name, {
22
40
  get: all[name],
23
41
  enumerable: true,
24
42
  configurable: true,
25
- set: (newValue) => all[name] = () => newValue
43
+ set: __exportSetter.bind(all, name)
26
44
  });
27
45
  };
28
46
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -5248,10 +5266,10 @@ WHY THIS FORMAT IS MANDATORY:
5248
5266
  `, PLAN_AGENT_NAMES, PLAN_FAMILY_NAMES;
5249
5267
  var init_constants = __esm(() => {
5250
5268
  DEFAULT_CATEGORIES = {
5251
- "visual-engineering": { model: "google/gemini-3-pro", variant: "high" },
5269
+ "visual-engineering": { model: "google/gemini-3.1-pro", variant: "high" },
5252
5270
  ultrabrain: { model: "openai/gpt-5.3-codex", variant: "xhigh" },
5253
5271
  deep: { model: "openai/gpt-5.3-codex", variant: "medium" },
5254
- artistry: { model: "google/gemini-3-pro", variant: "high" },
5272
+ artistry: { model: "google/gemini-3.1-pro", variant: "high" },
5255
5273
  quick: { model: "anthropic/claude-haiku-4-5" },
5256
5274
  "unspecified-low": { model: "anthropic/claude-sonnet-4-6" },
5257
5275
  "unspecified-high": { model: "anthropic/claude-opus-4-6", variant: "max" },
@@ -8167,7 +8185,7 @@ var require_compile = __commonJS((exports) => {
8167
8185
  const schOrFunc = root.refs[ref];
8168
8186
  if (schOrFunc)
8169
8187
  return schOrFunc;
8170
- let _sch = resolve10.call(this, root, ref);
8188
+ let _sch = resolve13.call(this, root, ref);
8171
8189
  if (_sch === undefined) {
8172
8190
  const schema2 = (_a = root.localRefs) === null || _a === undefined ? undefined : _a[ref];
8173
8191
  const { schemaId } = this.opts;
@@ -8194,7 +8212,7 @@ var require_compile = __commonJS((exports) => {
8194
8212
  function sameSchemaEnv(s1, s2) {
8195
8213
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
8196
8214
  }
8197
- function resolve10(root, ref) {
8215
+ function resolve13(root, ref) {
8198
8216
  let sch;
8199
8217
  while (typeof (sch = this.refs[ref]) == "string")
8200
8218
  ref = sch;
@@ -8724,7 +8742,7 @@ var require_fast_uri = __commonJS((exports, module) => {
8724
8742
  }
8725
8743
  return uri;
8726
8744
  }
8727
- function resolve10(baseURI, relativeURI, options) {
8745
+ function resolve13(baseURI, relativeURI, options) {
8728
8746
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
8729
8747
  const resolved = resolveComponent(parse7(baseURI, schemelessOptions), parse7(relativeURI, schemelessOptions), schemelessOptions, true);
8730
8748
  schemelessOptions.skipEscape = true;
@@ -8952,7 +8970,7 @@ var require_fast_uri = __commonJS((exports, module) => {
8952
8970
  var fastUri = {
8953
8971
  SCHEMES,
8954
8972
  normalize: normalize2,
8955
- resolve: resolve10,
8973
+ resolve: resolve13,
8956
8974
  resolveComponent,
8957
8975
  equal,
8958
8976
  serialize,
@@ -11833,12 +11851,12 @@ var require_isexe = __commonJS((exports, module) => {
11833
11851
  if (typeof Promise !== "function") {
11834
11852
  throw new TypeError("callback not provided");
11835
11853
  }
11836
- return new Promise(function(resolve10, reject) {
11854
+ return new Promise(function(resolve13, reject) {
11837
11855
  isexe(path11, options || {}, function(er, is) {
11838
11856
  if (er) {
11839
11857
  reject(er);
11840
11858
  } else {
11841
- resolve10(is);
11859
+ resolve13(is);
11842
11860
  }
11843
11861
  });
11844
11862
  });
@@ -11900,27 +11918,27 @@ var require_which = __commonJS((exports, module) => {
11900
11918
  opt = {};
11901
11919
  const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
11902
11920
  const found = [];
11903
- const step = (i2) => new Promise((resolve10, reject) => {
11921
+ const step = (i2) => new Promise((resolve13, reject) => {
11904
11922
  if (i2 === pathEnv.length)
11905
- return opt.all && found.length ? resolve10(found) : reject(getNotFoundError(cmd));
11923
+ return opt.all && found.length ? resolve13(found) : reject(getNotFoundError(cmd));
11906
11924
  const ppRaw = pathEnv[i2];
11907
11925
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
11908
11926
  const pCmd = path11.join(pathPart, cmd);
11909
11927
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
11910
- resolve10(subStep(p, i2, 0));
11928
+ resolve13(subStep(p, i2, 0));
11911
11929
  });
11912
- const subStep = (p, i2, ii) => new Promise((resolve10, reject) => {
11930
+ const subStep = (p, i2, ii) => new Promise((resolve13, reject) => {
11913
11931
  if (ii === pathExt.length)
11914
- return resolve10(step(i2 + 1));
11932
+ return resolve13(step(i2 + 1));
11915
11933
  const ext = pathExt[ii];
11916
11934
  isexe(p + ext, { pathExt: pathExtExe }, (er, is) => {
11917
11935
  if (!er && is) {
11918
11936
  if (opt.all)
11919
11937
  found.push(p + ext);
11920
11938
  else
11921
- return resolve10(p + ext);
11939
+ return resolve13(p + ext);
11922
11940
  }
11923
- return resolve10(subStep(p, i2, ii + 1));
11941
+ return resolve13(subStep(p, i2, ii + 1));
11924
11942
  });
11925
11943
  });
11926
11944
  return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
@@ -12239,7 +12257,7 @@ var COUNTDOWN_SECONDS = 2;
12239
12257
  var TOAST_DURATION_MS = 900;
12240
12258
  var COUNTDOWN_GRACE_PERIOD_MS = 500;
12241
12259
  var ABORT_WINDOW_MS = 3000;
12242
- var CONTINUATION_COOLDOWN_MS = 30000;
12260
+ var CONTINUATION_COOLDOWN_MS = 5000;
12243
12261
  var MAX_CONSECUTIVE_FAILURES = 5;
12244
12262
  var FAILURE_RESET_WINDOW_MS = 5 * 60 * 1000;
12245
12263
  // src/features/run-continuation-state/constants.ts
@@ -17101,14 +17119,15 @@ var AGENT_MODEL_REQUIREMENTS = {
17101
17119
  },
17102
17120
  hephaestus: {
17103
17121
  fallbackChain: [
17104
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "medium" }
17122
+ { providers: ["openai", "venice", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
17123
+ { providers: ["github-copilot"], model: "gpt-5.2", variant: "medium" }
17105
17124
  ],
17106
- requiresProvider: ["openai", "github-copilot", "opencode"]
17125
+ requiresProvider: ["openai", "github-copilot", "venice", "opencode"]
17107
17126
  },
17108
17127
  oracle: {
17109
17128
  fallbackChain: [
17110
17129
  { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
17111
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
17130
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
17112
17131
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" }
17113
17132
  ]
17114
17133
  },
@@ -17141,7 +17160,7 @@ var AGENT_MODEL_REQUIREMENTS = {
17141
17160
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
17142
17161
  { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
17143
17162
  { providers: ["opencode"], model: "kimi-k2.5-free" },
17144
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
17163
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" }
17145
17164
  ]
17146
17165
  },
17147
17166
  metis: {
@@ -17149,14 +17168,14 @@ var AGENT_MODEL_REQUIREMENTS = {
17149
17168
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
17150
17169
  { providers: ["opencode"], model: "kimi-k2.5-free" },
17151
17170
  { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
17152
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" }
17171
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" }
17153
17172
  ]
17154
17173
  },
17155
17174
  momus: {
17156
17175
  fallbackChain: [
17157
17176
  { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "medium" },
17158
17177
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
17159
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" }
17178
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" }
17160
17179
  ]
17161
17180
  },
17162
17181
  atlas: {
@@ -17170,33 +17189,33 @@ var AGENT_MODEL_REQUIREMENTS = {
17170
17189
  var CATEGORY_MODEL_REQUIREMENTS = {
17171
17190
  "visual-engineering": {
17172
17191
  fallbackChain: [
17173
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
17192
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
17174
17193
  { providers: ["zai-coding-plan", "opencode"], model: "glm-5" },
17175
17194
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" }
17176
17195
  ]
17177
17196
  },
17178
17197
  ultrabrain: {
17179
17198
  fallbackChain: [
17180
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "xhigh" },
17181
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
17199
+ { providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "xhigh" },
17200
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
17182
17201
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" }
17183
17202
  ]
17184
17203
  },
17185
17204
  deep: {
17186
17205
  fallbackChain: [
17187
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
17206
+ { providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
17188
17207
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
17189
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" }
17208
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" }
17190
17209
  ],
17191
17210
  requiresModel: "gpt-5.3-codex"
17192
17211
  },
17193
17212
  artistry: {
17194
17213
  fallbackChain: [
17195
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro", variant: "high" },
17214
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro", variant: "high" },
17196
17215
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
17197
17216
  { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2" }
17198
17217
  ],
17199
- requiresModel: "gemini-3-pro"
17218
+ requiresModel: "gemini-3.1-pro"
17200
17219
  },
17201
17220
  quick: {
17202
17221
  fallbackChain: [
@@ -17208,7 +17227,7 @@ var CATEGORY_MODEL_REQUIREMENTS = {
17208
17227
  "unspecified-low": {
17209
17228
  fallbackChain: [
17210
17229
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" },
17211
- { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
17230
+ { providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "medium" },
17212
17231
  { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" }
17213
17232
  ]
17214
17233
  },
@@ -17216,11 +17235,12 @@ var CATEGORY_MODEL_REQUIREMENTS = {
17216
17235
  fallbackChain: [
17217
17236
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-opus-4-6", variant: "max" },
17218
17237
  { providers: ["openai", "github-copilot", "opencode"], model: "gpt-5.2", variant: "high" },
17219
- { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-pro" }
17238
+ { providers: ["google", "github-copilot", "opencode"], model: "gemini-3.1-pro" }
17220
17239
  ]
17221
17240
  },
17222
17241
  writing: {
17223
17242
  fallbackChain: [
17243
+ { providers: ["opencode"], model: "kimi-k2.5-free" },
17224
17244
  { providers: ["google", "github-copilot", "opencode"], model: "gemini-3-flash" },
17225
17245
  { providers: ["anthropic", "github-copilot", "opencode"], model: "claude-sonnet-4-6" }
17226
17246
  ]
@@ -17684,10 +17704,10 @@ function isModelCacheAvailable() {
17684
17704
  // src/shared/provider-model-id-transform.ts
17685
17705
  function transformModelForProvider(provider, model) {
17686
17706
  if (provider === "github-copilot") {
17687
- return model.replace("claude-opus-4-6", "claude-opus-4.6").replace("claude-sonnet-4-6", "claude-sonnet-4.6").replace("claude-sonnet-4-5", "claude-sonnet-4.5").replace("claude-haiku-4-5", "claude-haiku-4.5").replace("claude-sonnet-4", "claude-sonnet-4").replace(/gemini-3-pro(?!-)/g, "gemini-3-pro-preview").replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview");
17707
+ return model.replace("claude-opus-4-6", "claude-opus-4.6").replace("claude-sonnet-4-6", "claude-sonnet-4.6").replace("claude-sonnet-4-5", "claude-sonnet-4.5").replace("claude-haiku-4-5", "claude-haiku-4.5").replace("claude-sonnet-4", "claude-sonnet-4").replace(/gemini-3\.1-pro(?!-)/g, "gemini-3.1-pro-preview").replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview");
17688
17708
  }
17689
17709
  if (provider === "google") {
17690
- return model.replace(/gemini-3-pro(?!-)/g, "gemini-3-pro-preview").replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview");
17710
+ return model.replace(/gemini-3\.1-pro(?!-)/g, "gemini-3.1-pro-preview").replace(/gemini-3-flash(?!-)/g, "gemini-3-flash-preview");
17691
17711
  }
17692
17712
  return model;
17693
17713
  }
@@ -18012,8 +18032,10 @@ function isAnyProviderConnected(providers, availableModels) {
18012
18032
  }
18013
18033
  // src/features/hook-message-injector/injector.ts
18014
18034
  import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync7, readdirSync, writeFileSync as writeFileSync4 } from "fs";
18035
+ import { randomBytes } from "crypto";
18015
18036
  import { join as join12 } from "path";
18016
18037
  init_logger();
18038
+ var processPrefix = randomBytes(4).toString("hex");
18017
18039
  function convertSDKMessageToStoredMessage(msg) {
18018
18040
  const info = msg.info;
18019
18041
  if (!info)
@@ -18545,6 +18567,42 @@ async function enforceMainPaneWidth(mainPaneId, windowWidth, mainPaneSizeOrOptio
18545
18567
  }
18546
18568
  // src/shared/model-suggestion-retry.ts
18547
18569
  init_logger();
18570
+
18571
+ // src/shared/prompt-timeout-context.ts
18572
+ var PROMPT_TIMEOUT_MS = 120000;
18573
+ function createPromptTimeoutContext(args, timeoutMs) {
18574
+ const timeoutController = new AbortController;
18575
+ let timeoutID = null;
18576
+ let timedOut = false;
18577
+ const abortOnUpstreamSignal = () => {
18578
+ timeoutController.abort(args.signal?.reason);
18579
+ };
18580
+ if (args.signal) {
18581
+ if (args.signal.aborted) {
18582
+ timeoutController.abort(args.signal.reason);
18583
+ } else {
18584
+ args.signal.addEventListener("abort", abortOnUpstreamSignal, { once: true });
18585
+ }
18586
+ }
18587
+ timeoutID = setTimeout(() => {
18588
+ timedOut = true;
18589
+ timeoutController.abort(new Error(`prompt timed out after ${timeoutMs}ms`));
18590
+ }, timeoutMs);
18591
+ return {
18592
+ signal: timeoutController.signal,
18593
+ wasTimedOut: () => timedOut,
18594
+ cleanup: () => {
18595
+ if (timeoutID !== null) {
18596
+ clearTimeout(timeoutID);
18597
+ }
18598
+ if (args.signal) {
18599
+ args.signal.removeEventListener("abort", abortOnUpstreamSignal);
18600
+ }
18601
+ }
18602
+ };
18603
+ }
18604
+
18605
+ // src/shared/model-suggestion-retry.ts
18548
18606
  function extractMessage(error) {
18549
18607
  if (typeof error === "string")
18550
18608
  return error;
@@ -18602,24 +18660,47 @@ function parseModelSuggestion(error) {
18602
18660
  }
18603
18661
  return null;
18604
18662
  }
18605
- async function promptWithModelSuggestionRetry(client, args) {
18606
- const promptPromise = client.session.promptAsync(args);
18607
- let timeoutID = null;
18608
- const timeoutPromise = new Promise((_, reject) => {
18609
- timeoutID = setTimeout(() => {
18610
- reject(new Error("promptAsync timed out after 120000ms"));
18611
- }, 120000);
18663
+ async function promptWithModelSuggestionRetry(client, args, options = {}) {
18664
+ const timeoutMs = options.timeoutMs ?? PROMPT_TIMEOUT_MS;
18665
+ const timeoutContext = createPromptTimeoutContext(args, timeoutMs);
18666
+ const promptPromise = client.session.promptAsync({
18667
+ ...args,
18668
+ signal: timeoutContext.signal
18612
18669
  });
18613
18670
  try {
18614
- await Promise.race([promptPromise, timeoutPromise]);
18671
+ await promptPromise;
18672
+ if (timeoutContext.wasTimedOut()) {
18673
+ throw new Error(`promptAsync timed out after ${timeoutMs}ms`);
18674
+ }
18675
+ } catch (error) {
18676
+ if (timeoutContext.wasTimedOut()) {
18677
+ throw new Error(`promptAsync timed out after ${timeoutMs}ms`);
18678
+ }
18679
+ throw error;
18615
18680
  } finally {
18616
- if (timeoutID !== null)
18617
- clearTimeout(timeoutID);
18681
+ timeoutContext.cleanup();
18618
18682
  }
18619
18683
  }
18620
- async function promptSyncWithModelSuggestionRetry(client, args) {
18684
+ async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
18685
+ const timeoutMs = options.timeoutMs ?? PROMPT_TIMEOUT_MS;
18621
18686
  try {
18622
- await client.session.prompt(args);
18687
+ const timeoutContext = createPromptTimeoutContext(args, timeoutMs);
18688
+ try {
18689
+ await client.session.prompt({
18690
+ ...args,
18691
+ signal: timeoutContext.signal
18692
+ });
18693
+ if (timeoutContext.wasTimedOut()) {
18694
+ throw new Error(`prompt timed out after ${timeoutMs}ms`);
18695
+ }
18696
+ } catch (error) {
18697
+ if (timeoutContext.wasTimedOut()) {
18698
+ throw new Error(`prompt timed out after ${timeoutMs}ms`);
18699
+ }
18700
+ throw error;
18701
+ } finally {
18702
+ timeoutContext.cleanup();
18703
+ }
18623
18704
  } catch (error) {
18624
18705
  const suggestion = parseModelSuggestion(error);
18625
18706
  if (!suggestion || !args.body.model) {
@@ -18629,7 +18710,7 @@ async function promptSyncWithModelSuggestionRetry(client, args) {
18629
18710
  original: `${suggestion.providerID}/${suggestion.modelID}`,
18630
18711
  suggested: suggestion.suggestion
18631
18712
  });
18632
- await client.session.prompt({
18713
+ const retryArgs = {
18633
18714
  ...args,
18634
18715
  body: {
18635
18716
  ...args.body,
@@ -18638,7 +18719,24 @@ async function promptSyncWithModelSuggestionRetry(client, args) {
18638
18719
  modelID: suggestion.suggestion
18639
18720
  }
18640
18721
  }
18641
- });
18722
+ };
18723
+ const timeoutContext = createPromptTimeoutContext(retryArgs, timeoutMs);
18724
+ try {
18725
+ await client.session.prompt({
18726
+ ...retryArgs,
18727
+ signal: timeoutContext.signal
18728
+ });
18729
+ if (timeoutContext.wasTimedOut()) {
18730
+ throw new Error(`prompt timed out after ${timeoutMs}ms`);
18731
+ }
18732
+ } catch (retryError) {
18733
+ if (timeoutContext.wasTimedOut()) {
18734
+ throw new Error(`prompt timed out after ${timeoutMs}ms`);
18735
+ }
18736
+ throw retryError;
18737
+ } finally {
18738
+ timeoutContext.cleanup();
18739
+ }
18642
18740
  }
18643
18741
  }
18644
18742
  // src/shared/opencode-server-auth.ts
@@ -19159,9 +19257,31 @@ function isLastAssistantMessageAborted(messages) {
19159
19257
  return errorName === "MessageAbortedError" || errorName === "AbortError";
19160
19258
  }
19161
19259
 
19260
+ // src/hooks/todo-continuation-enforcer/pending-question-detection.ts
19261
+ init_logger();
19262
+ function hasUnansweredQuestion(messages) {
19263
+ if (!messages || messages.length === 0)
19264
+ return false;
19265
+ for (let i2 = messages.length - 1;i2 >= 0; i2--) {
19266
+ const msg = messages[i2];
19267
+ const role = msg.info?.role ?? msg.role;
19268
+ if (role === "user")
19269
+ return false;
19270
+ if (role === "assistant" && msg.parts) {
19271
+ const hasQuestion = msg.parts.some((part) => (part.type === "tool_use" || part.type === "tool-invocation") && (part.name === "question" || part.toolName === "question"));
19272
+ if (hasQuestion) {
19273
+ log(`[${HOOK_NAME}] Detected pending question tool in last assistant message`);
19274
+ return true;
19275
+ }
19276
+ return false;
19277
+ }
19278
+ }
19279
+ return false;
19280
+ }
19281
+
19162
19282
  // src/hooks/todo-continuation-enforcer/todo.ts
19163
19283
  function getIncompleteCount(todos) {
19164
- return todos.filter((todo) => todo.status !== "completed" && todo.status !== "cancelled").length;
19284
+ return todos.filter((todo) => todo.status !== "completed" && todo.status !== "cancelled" && todo.status !== "blocked" && todo.status !== "deleted").length;
19165
19285
  }
19166
19286
 
19167
19287
  // src/hooks/todo-continuation-enforcer/countdown.ts
@@ -19377,6 +19497,10 @@ async function handleSessionIdle(args) {
19377
19497
  log(`[${HOOK_NAME}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
19378
19498
  return;
19379
19499
  }
19500
+ if (hasUnansweredQuestion(messages)) {
19501
+ log(`[${HOOK_NAME}] Skipped: pending question awaiting user response`, { sessionID });
19502
+ return;
19503
+ }
19380
19504
  } catch (error) {
19381
19505
  log(`[${HOOK_NAME}] Messages fetch failed, continuing`, { sessionID, error: String(error) });
19382
19506
  }
@@ -20103,6 +20227,7 @@ function createSessionNotification(ctx, config = {}) {
20103
20227
  idleConfirmationDelay: 1500,
20104
20228
  skipIfIncompleteTodos: true,
20105
20229
  maxTrackedSessions: 100,
20230
+ enforceMainSessionFilter: true,
20106
20231
  ...config
20107
20232
  };
20108
20233
  const scheduler = createIdleNotificationScheduler({
@@ -20135,9 +20260,11 @@ function createSessionNotification(ctx, config = {}) {
20135
20260
  const shouldNotifyForSession = (sessionID) => {
20136
20261
  if (subagentSessions.has(sessionID))
20137
20262
  return false;
20138
- const mainSessionID = getMainSessionID();
20139
- if (mainSessionID && sessionID !== mainSessionID)
20140
- return false;
20263
+ if (mergedConfig.enforceMainSessionFilter) {
20264
+ const mainSessionID = getMainSessionID();
20265
+ if (mainSessionID && sessionID !== mainSessionID)
20266
+ return false;
20267
+ }
20141
20268
  return true;
20142
20269
  };
20143
20270
  const getEventToolName = (properties) => {
@@ -34526,7 +34653,7 @@ var TRUNCATE_CONFIG = {
34526
34653
  function getOrCreateRetryState(autoCompactState, sessionID) {
34527
34654
  let state2 = autoCompactState.retryStateBySession.get(sessionID);
34528
34655
  if (!state2) {
34529
- state2 = { attempt: 0, lastAttemptTime: 0 };
34656
+ state2 = { attempt: 0, lastAttemptTime: 0, firstAttemptTime: 0 };
34530
34657
  autoCompactState.retryStateBySession.set(sessionID, state2);
34531
34658
  }
34532
34659
  return state2;
@@ -35222,8 +35349,26 @@ function resolveCompactionModel(pluginConfig, sessionID, originalProviderID, ori
35222
35349
  }
35223
35350
 
35224
35351
  // src/hooks/anthropic-context-window-limit-recovery/summarize-retry-strategy.ts
35352
+ var SUMMARIZE_RETRY_TOTAL_TIMEOUT_MS = 120000;
35225
35353
  async function runSummarizeRetryStrategy(params) {
35226
35354
  const retryState = getOrCreateRetryState(params.autoCompactState, params.sessionID);
35355
+ const now = Date.now();
35356
+ if (retryState.firstAttemptTime === 0) {
35357
+ retryState.firstAttemptTime = now;
35358
+ }
35359
+ const elapsedTimeMs = now - retryState.firstAttemptTime;
35360
+ if (elapsedTimeMs >= SUMMARIZE_RETRY_TOTAL_TIMEOUT_MS) {
35361
+ clearSessionState(params.autoCompactState, params.sessionID);
35362
+ await params.client.tui.showToast({
35363
+ body: {
35364
+ title: "Auto Compact Timed Out",
35365
+ message: "Compaction retries exceeded the timeout window. Please start a new session.",
35366
+ variant: "error",
35367
+ duration: 5000
35368
+ }
35369
+ }).catch(() => {});
35370
+ return;
35371
+ }
35227
35372
  if (params.errorType?.includes("non-empty content")) {
35228
35373
  const attempt = getEmptyContentAttempt(params.autoCompactState, params.sessionID);
35229
35374
  if (attempt < 3) {
@@ -35253,6 +35398,7 @@ async function runSummarizeRetryStrategy(params) {
35253
35398
  }
35254
35399
  if (Date.now() - retryState.lastAttemptTime > 300000) {
35255
35400
  retryState.attempt = 0;
35401
+ retryState.firstAttemptTime = Date.now();
35256
35402
  params.autoCompactState.truncateStateBySession.delete(params.sessionID);
35257
35403
  }
35258
35404
  if (retryState.attempt < RETRY_CONFIG.maxAttempts) {
@@ -35280,8 +35426,21 @@ async function runSummarizeRetryStrategy(params) {
35280
35426
  });
35281
35427
  return;
35282
35428
  } catch {
35429
+ const remainingTimeMs = SUMMARIZE_RETRY_TOTAL_TIMEOUT_MS - (Date.now() - retryState.firstAttemptTime);
35430
+ if (remainingTimeMs <= 0) {
35431
+ clearSessionState(params.autoCompactState, params.sessionID);
35432
+ await params.client.tui.showToast({
35433
+ body: {
35434
+ title: "Auto Compact Timed Out",
35435
+ message: "Compaction retries exceeded the timeout window. Please start a new session.",
35436
+ variant: "error",
35437
+ duration: 5000
35438
+ }
35439
+ }).catch(() => {});
35440
+ return;
35441
+ }
35283
35442
  const delay3 = RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffFactor, retryState.attempt - 1);
35284
- const cappedDelay = Math.min(delay3, RETRY_CONFIG.maxDelayMs);
35443
+ const cappedDelay = Math.min(delay3, RETRY_CONFIG.maxDelayMs, remainingTimeMs);
35285
35444
  setTimeout(() => {
35286
35445
  runSummarizeRetryStrategy(params);
35287
35446
  }, cappedDelay);
@@ -35877,24 +36036,11 @@ function extractModelPrefix(modelID) {
35877
36036
  function normalizeModelID(modelID) {
35878
36037
  return modelID.replace(/\.(\d+)/g, "-$1");
35879
36038
  }
35880
- function resolveProvider(providerID, modelID) {
35881
- if (providerID === "github-copilot") {
35882
- const modelLower = modelID.toLowerCase();
35883
- if (modelLower.includes("claude"))
35884
- return "anthropic";
35885
- if (modelLower.includes("gemini"))
35886
- return "google";
35887
- if (modelLower.includes("gpt") || modelLower.includes("o1") || modelLower.includes("o3")) {
35888
- return "openai";
35889
- }
35890
- }
35891
- return providerID;
35892
- }
35893
36039
  var HIGH_VARIANT_MAP = {
35894
36040
  "claude-sonnet-4-6": "claude-sonnet-4-6-high",
35895
36041
  "claude-opus-4-6": "claude-opus-4-6-high",
35896
- "gemini-3-pro": "gemini-3-pro-high",
35897
- "gemini-3-pro-low": "gemini-3-pro-high",
36042
+ "gemini-3-1-pro": "gemini-3-1-pro-high",
36043
+ "gemini-3-1-pro-low": "gemini-3-1-pro-high",
35898
36044
  "gemini-3-flash": "gemini-3-flash-high",
35899
36045
  "gpt-5": "gpt-5-high",
35900
36046
  "gpt-5-mini": "gpt-5-mini-high",
@@ -35909,74 +36055,10 @@ var HIGH_VARIANT_MAP = {
35909
36055
  "gpt-5-2": "gpt-5-2-high",
35910
36056
  "gpt-5-2-chat-latest": "gpt-5-2-chat-latest-high",
35911
36057
  "gpt-5-2-pro": "gpt-5-2-pro-high",
35912
- "antigravity-gemini-3-pro": "antigravity-gemini-3-pro-high",
36058
+ "antigravity-gemini-3-1-pro": "antigravity-gemini-3-1-pro-high",
35913
36059
  "antigravity-gemini-3-flash": "antigravity-gemini-3-flash-high"
35914
36060
  };
35915
36061
  var ALREADY_HIGH = new Set(Object.values(HIGH_VARIANT_MAP));
35916
- var THINKING_CONFIGS = {
35917
- anthropic: {
35918
- thinking: {
35919
- type: "enabled",
35920
- budgetTokens: 64000
35921
- },
35922
- maxTokens: 128000
35923
- },
35924
- "google-vertex-anthropic": {
35925
- thinking: {
35926
- type: "enabled",
35927
- budgetTokens: 64000
35928
- },
35929
- maxTokens: 128000
35930
- },
35931
- "amazon-bedrock": {
35932
- reasoningConfig: {
35933
- type: "enabled",
35934
- budgetTokens: 32000
35935
- },
35936
- maxTokens: 64000
35937
- },
35938
- google: {
35939
- providerOptions: {
35940
- google: {
35941
- thinkingConfig: {
35942
- thinkingLevel: "HIGH"
35943
- }
35944
- }
35945
- }
35946
- },
35947
- "google-vertex": {
35948
- providerOptions: {
35949
- "google-vertex": {
35950
- thinkingConfig: {
35951
- thinkingLevel: "HIGH"
35952
- }
35953
- }
35954
- }
35955
- },
35956
- openai: {
35957
- reasoning_effort: "high"
35958
- },
35959
- "zai-coding-plan": {
35960
- providerOptions: {
35961
- "zai-coding-plan": {
35962
- extra_body: {
35963
- thinking: {
35964
- type: "disabled"
35965
- }
35966
- }
35967
- }
35968
- }
35969
- }
35970
- };
35971
- var THINKING_CAPABLE_MODELS = {
35972
- anthropic: ["claude-sonnet-4", "claude-opus-4", "claude-3"],
35973
- "google-vertex-anthropic": ["claude-sonnet-4", "claude-opus-4", "claude-3"],
35974
- "amazon-bedrock": ["claude", "anthropic"],
35975
- google: ["gemini-2", "gemini-3"],
35976
- "google-vertex": ["gemini-2", "gemini-3"],
35977
- openai: ["gpt-5", "o1", "o3"],
35978
- "zai-coding-plan": ["glm"]
35979
- };
35980
36062
  function getHighVariant(modelID) {
35981
36063
  const normalized = normalizeModelID(modelID);
35982
36064
  const { prefix, base } = extractModelPrefix(normalized);
@@ -35994,65 +36076,28 @@ function isAlreadyHighVariant(modelID) {
35994
36076
  const { base } = extractModelPrefix(normalized);
35995
36077
  return ALREADY_HIGH.has(base) || base.endsWith("-high");
35996
36078
  }
35997
- function isThinkingProvider(provider) {
35998
- return provider in THINKING_CONFIGS;
35999
- }
36000
- function getThinkingConfig(providerID, modelID) {
36001
- const normalized = normalizeModelID(modelID);
36002
- const { base } = extractModelPrefix(normalized);
36003
- if (isAlreadyHighVariant(normalized)) {
36004
- return null;
36005
- }
36006
- const resolvedProvider = resolveProvider(providerID, modelID);
36007
- if (!isThinkingProvider(resolvedProvider)) {
36008
- return null;
36009
- }
36010
- const config2 = THINKING_CONFIGS[resolvedProvider];
36011
- const capablePatterns = THINKING_CAPABLE_MODELS[resolvedProvider];
36012
- const baseLower = base.toLowerCase();
36013
- const isCapable = capablePatterns.some((pattern) => baseLower.includes(pattern.toLowerCase()));
36014
- return isCapable ? config2 : null;
36015
- }
36016
36079
  // src/hooks/think-mode/hook.ts
36017
36080
  var thinkModeState = new Map;
36018
36081
  function createThinkModeHook() {
36019
- function isDisabledThinkingConfig(config2) {
36020
- const thinkingConfig = config2.thinking;
36021
- if (typeof thinkingConfig === "object" && thinkingConfig !== null && "type" in thinkingConfig && thinkingConfig.type === "disabled") {
36022
- return true;
36023
- }
36024
- const providerOptions = config2.providerOptions;
36025
- if (typeof providerOptions !== "object" || providerOptions === null) {
36026
- return false;
36027
- }
36028
- return Object.values(providerOptions).some((providerConfig) => {
36029
- if (typeof providerConfig !== "object" || providerConfig === null) {
36030
- return false;
36031
- }
36032
- const providerConfigMap = providerConfig;
36033
- const extraBody = providerConfigMap.extra_body;
36034
- if (typeof extraBody !== "object" || extraBody === null) {
36035
- return false;
36036
- }
36037
- const extraBodyMap = extraBody;
36038
- const extraThinking = extraBodyMap.thinking;
36039
- return typeof extraThinking === "object" && extraThinking !== null && extraThinking.type === "disabled";
36040
- });
36041
- }
36042
36082
  return {
36043
- "chat.params": async (output, sessionID) => {
36083
+ "chat.message": async (input, output) => {
36044
36084
  const promptText = extractPromptText(output.parts);
36085
+ const sessionID = input.sessionID;
36045
36086
  const state3 = {
36046
36087
  requested: false,
36047
36088
  modelSwitched: false,
36048
- thinkingConfigInjected: false
36089
+ variantSet: false
36049
36090
  };
36050
36091
  if (!detectThinkKeyword(promptText)) {
36051
36092
  thinkModeState.set(sessionID, state3);
36052
36093
  return;
36053
36094
  }
36054
36095
  state3.requested = true;
36055
- const currentModel = output.message.model;
36096
+ if (typeof output.message.variant === "string") {
36097
+ thinkModeState.set(sessionID, state3);
36098
+ return;
36099
+ }
36100
+ const currentModel = input.model;
36056
36101
  if (!currentModel) {
36057
36102
  thinkModeState.set(sessionID, state3);
36058
36103
  return;
@@ -36064,50 +36109,20 @@ function createThinkModeHook() {
36064
36109
  return;
36065
36110
  }
36066
36111
  const highVariant = getHighVariant(currentModel.modelID);
36067
- const thinkingConfig = getThinkingConfig(currentModel.providerID, currentModel.modelID);
36068
36112
  if (highVariant) {
36069
36113
  output.message.model = {
36070
36114
  providerID: currentModel.providerID,
36071
36115
  modelID: highVariant
36072
36116
  };
36117
+ output.message.variant = "high";
36073
36118
  state3.modelSwitched = true;
36119
+ state3.variantSet = true;
36074
36120
  log("Think mode: model switched to high variant", {
36075
36121
  sessionID,
36076
36122
  from: currentModel.modelID,
36077
36123
  to: highVariant
36078
36124
  });
36079
36125
  }
36080
- if (thinkingConfig) {
36081
- const messageData = output.message;
36082
- const agentThinking = messageData.thinking;
36083
- const agentProviderOptions = messageData.providerOptions;
36084
- const agentDisabledThinking = agentThinking?.type === "disabled";
36085
- const agentHasCustomProviderOptions = Boolean(agentProviderOptions);
36086
- if (agentDisabledThinking) {
36087
- log("Think mode: skipping - agent has thinking disabled", {
36088
- sessionID,
36089
- provider: currentModel.providerID
36090
- });
36091
- } else if (agentHasCustomProviderOptions) {
36092
- log("Think mode: skipping - agent has custom providerOptions", {
36093
- sessionID,
36094
- provider: currentModel.providerID
36095
- });
36096
- } else if (!isDisabledThinkingConfig(thinkingConfig)) {
36097
- Object.assign(output.message, thinkingConfig);
36098
- state3.thinkingConfigInjected = true;
36099
- log("Think mode: thinking config injected", {
36100
- sessionID,
36101
- provider: currentModel.providerID,
36102
- config: thinkingConfig
36103
- });
36104
- } else {
36105
- log("Think mode: skipping disabled thinking config", {
36106
- sessionID,
36107
- provider: currentModel.providerID
36108
- });
36109
- }
36110
- }
36111
36126
  thinkModeState.set(sessionID, state3);
36112
36127
  },
36113
36128
  event: async ({ event }) => {
@@ -36652,6 +36667,76 @@ function isHookCommandDisabled(eventType, command, config2) {
36652
36667
  });
36653
36668
  }
36654
36669
 
36670
+ // src/hooks/claude-code-hooks/execute-http-hook.ts
36671
+ var DEFAULT_HTTP_HOOK_TIMEOUT_S = 30;
36672
+ var ALLOWED_SCHEMES = new Set(["http:", "https:"]);
36673
+ function interpolateEnvVars(value, allowedEnvVars) {
36674
+ const allowedSet = new Set(allowedEnvVars);
36675
+ return value.replace(/\$\{(\w+)\}|\$(\w+)/g, (_match, bracedVar, bareVar) => {
36676
+ const varName = bracedVar ?? bareVar;
36677
+ if (allowedSet.has(varName)) {
36678
+ return process.env[varName] ?? "";
36679
+ }
36680
+ return "";
36681
+ });
36682
+ }
36683
+ function resolveHeaders(hook) {
36684
+ const headers = {
36685
+ "Content-Type": "application/json"
36686
+ };
36687
+ if (!hook.headers)
36688
+ return headers;
36689
+ const allowedEnvVars = hook.allowedEnvVars ?? [];
36690
+ for (const [key, value] of Object.entries(hook.headers)) {
36691
+ headers[key] = interpolateEnvVars(value, allowedEnvVars);
36692
+ }
36693
+ return headers;
36694
+ }
36695
+ async function executeHttpHook(hook, stdin) {
36696
+ try {
36697
+ const parsed = new URL(hook.url);
36698
+ if (!ALLOWED_SCHEMES.has(parsed.protocol)) {
36699
+ return {
36700
+ exitCode: 1,
36701
+ stderr: `HTTP hook URL scheme "${parsed.protocol}" is not allowed. Only http: and https: are permitted.`
36702
+ };
36703
+ }
36704
+ } catch {
36705
+ return { exitCode: 1, stderr: `HTTP hook URL is invalid: ${hook.url}` };
36706
+ }
36707
+ const timeoutS = hook.timeout ?? DEFAULT_HTTP_HOOK_TIMEOUT_S;
36708
+ const headers = resolveHeaders(hook);
36709
+ try {
36710
+ const response = await fetch(hook.url, {
36711
+ method: "POST",
36712
+ headers,
36713
+ body: stdin,
36714
+ signal: AbortSignal.timeout(timeoutS * 1000)
36715
+ });
36716
+ if (!response.ok) {
36717
+ return {
36718
+ exitCode: 1,
36719
+ stderr: `HTTP hook returned status ${response.status}: ${response.statusText}`,
36720
+ stdout: await response.text().catch(() => "")
36721
+ };
36722
+ }
36723
+ const body = await response.text();
36724
+ if (!body) {
36725
+ return { exitCode: 0, stdout: "", stderr: "" };
36726
+ }
36727
+ try {
36728
+ const parsed = JSON.parse(body);
36729
+ if (typeof parsed.exitCode === "number") {
36730
+ return { exitCode: parsed.exitCode, stdout: body, stderr: "" };
36731
+ }
36732
+ } catch {}
36733
+ return { exitCode: 0, stdout: body, stderr: "" };
36734
+ } catch (error45) {
36735
+ const message = error45 instanceof Error ? error45.message : String(error45);
36736
+ return { exitCode: 1, stderr: `HTTP hook error: ${message}` };
36737
+ }
36738
+ }
36739
+
36655
36740
  // src/hooks/claude-code-hooks/plugin-config.ts
36656
36741
  var isWindows = process.platform === "win32";
36657
36742
  var DEFAULT_CONFIG = {
@@ -36659,6 +36744,19 @@ var DEFAULT_CONFIG = {
36659
36744
  zshPath: "/bin/zsh"
36660
36745
  };
36661
36746
 
36747
+ // src/hooks/claude-code-hooks/dispatch-hook.ts
36748
+ function getHookIdentifier(hook) {
36749
+ if (hook.type === "http")
36750
+ return hook.url;
36751
+ return hook.command.split("/").pop() || hook.command;
36752
+ }
36753
+ async function dispatchHook(hook, stdinJson, cwd) {
36754
+ if (hook.type === "http") {
36755
+ return executeHttpHook(hook, stdinJson);
36756
+ }
36757
+ return executeHookCommand(hook.command, stdinJson, cwd, { forceZsh: DEFAULT_CONFIG.forceZsh, zshPath: DEFAULT_CONFIG.zshPath });
36758
+ }
36759
+
36662
36760
  // src/hooks/claude-code-hooks/user-prompt-submit.ts
36663
36761
  var USER_PROMPT_SUBMIT_TAG_OPEN = "<user-prompt-submit-hook>";
36664
36762
  var USER_PROMPT_SUBMIT_TAG_CLOSE = "</user-prompt-submit-hook>";
@@ -36693,13 +36791,14 @@ async function executeUserPromptSubmitHooks(ctx, config2, extendedConfig) {
36693
36791
  if (!matcher.hooks || matcher.hooks.length === 0)
36694
36792
  continue;
36695
36793
  for (const hook of matcher.hooks) {
36696
- if (hook.type !== "command")
36794
+ if (hook.type !== "command" && hook.type !== "http")
36697
36795
  continue;
36698
- if (isHookCommandDisabled("UserPromptSubmit", hook.command, extendedConfig ?? null)) {
36699
- log("UserPromptSubmit hook command skipped (disabled by config)", { command: hook.command });
36796
+ const hookName = getHookIdentifier(hook);
36797
+ if (isHookCommandDisabled("UserPromptSubmit", hookName, extendedConfig ?? null)) {
36798
+ log("UserPromptSubmit hook command skipped (disabled by config)", { command: hookName });
36700
36799
  continue;
36701
36800
  }
36702
- const result = await executeHookCommand(hook.command, JSON.stringify(stdinData), ctx.cwd, { forceZsh: DEFAULT_CONFIG.forceZsh, zshPath: DEFAULT_CONFIG.zshPath });
36801
+ const result = await dispatchHook(hook, JSON.stringify(stdinData), ctx.cwd);
36703
36802
  if (result.stdout) {
36704
36803
  const output = result.stdout.trim();
36705
36804
  if (output.startsWith(USER_PROMPT_SUBMIT_TAG_OPEN)) {
@@ -36976,16 +37075,16 @@ async function executePreCompactHooks(ctx, config2, extendedConfig) {
36976
37075
  if (!matcher.hooks || matcher.hooks.length === 0)
36977
37076
  continue;
36978
37077
  for (const hook of matcher.hooks) {
36979
- if (hook.type !== "command")
37078
+ if (hook.type !== "command" && hook.type !== "http")
36980
37079
  continue;
36981
- if (isHookCommandDisabled("PreCompact", hook.command, extendedConfig ?? null)) {
36982
- log("PreCompact hook command skipped (disabled by config)", { command: hook.command });
37080
+ const hookName = getHookIdentifier(hook);
37081
+ if (isHookCommandDisabled("PreCompact", hookName, extendedConfig ?? null)) {
37082
+ log("PreCompact hook command skipped (disabled by config)", { command: hookName });
36983
37083
  continue;
36984
37084
  }
36985
- const hookName = hook.command.split("/").pop() || hook.command;
36986
37085
  if (!firstHookName)
36987
37086
  firstHookName = hookName;
36988
- const result = await executeHookCommand(hook.command, JSON.stringify(stdinData), ctx.cwd, { forceZsh: DEFAULT_CONFIG.forceZsh, zshPath: DEFAULT_CONFIG.zshPath });
37087
+ const result = await dispatchHook(hook, JSON.stringify(stdinData), ctx.cwd);
36989
37088
  if (result.exitCode === 2) {
36990
37089
  log("PreCompact hook blocked", { hookName, stderr: result.stderr });
36991
37090
  continue;
@@ -37083,13 +37182,14 @@ async function executeStopHooks(ctx, config2, extendedConfig) {
37083
37182
  if (!matcher.hooks || matcher.hooks.length === 0)
37084
37183
  continue;
37085
37184
  for (const hook of matcher.hooks) {
37086
- if (hook.type !== "command")
37185
+ if (hook.type !== "command" && hook.type !== "http")
37087
37186
  continue;
37088
- if (isHookCommandDisabled("Stop", hook.command, extendedConfig ?? null)) {
37089
- log("Stop hook command skipped (disabled by config)", { command: hook.command });
37187
+ const hookName = getHookIdentifier(hook);
37188
+ if (isHookCommandDisabled("Stop", hookName, extendedConfig ?? null)) {
37189
+ log("Stop hook command skipped (disabled by config)", { command: hookName });
37090
37190
  continue;
37091
37191
  }
37092
- const result = await executeHookCommand(hook.command, JSON.stringify(stdinData), ctx.cwd, { forceZsh: DEFAULT_CONFIG.forceZsh, zshPath: DEFAULT_CONFIG.zshPath });
37192
+ const result = await dispatchHook(hook, JSON.stringify(stdinData), ctx.cwd);
37093
37193
  if (result.exitCode === 2) {
37094
37194
  const reason = result.stderr || result.stdout || "Blocked by stop hook";
37095
37195
  return {
@@ -37238,16 +37338,16 @@ async function executePostToolUseHooks(ctx, config2, extendedConfig) {
37238
37338
  if (!matcher.hooks || matcher.hooks.length === 0)
37239
37339
  continue;
37240
37340
  for (const hook of matcher.hooks) {
37241
- if (hook.type !== "command")
37341
+ if (hook.type !== "command" && hook.type !== "http")
37242
37342
  continue;
37243
- if (isHookCommandDisabled("PostToolUse", hook.command, extendedConfig ?? null)) {
37244
- log("PostToolUse hook command skipped (disabled by config)", { command: hook.command, toolName: ctx.toolName });
37343
+ const hookName = getHookIdentifier(hook);
37344
+ if (isHookCommandDisabled("PostToolUse", hookName, extendedConfig ?? null)) {
37345
+ log("PostToolUse hook command skipped (disabled by config)", { command: hookName, toolName: ctx.toolName });
37245
37346
  continue;
37246
37347
  }
37247
- const hookName = hook.command.split("/").pop() || hook.command;
37248
37348
  if (!firstHookName)
37249
37349
  firstHookName = hookName;
37250
- const result = await executeHookCommand(hook.command, JSON.stringify(stdinData), ctx.cwd, { forceZsh: DEFAULT_CONFIG.forceZsh, zshPath: DEFAULT_CONFIG.zshPath });
37350
+ const result = await dispatchHook(hook, JSON.stringify(stdinData), ctx.cwd);
37251
37351
  if (result.stdout) {
37252
37352
  messages.push(result.stdout);
37253
37353
  }
@@ -37475,16 +37575,16 @@ async function executePreToolUseHooks(ctx, config2, extendedConfig) {
37475
37575
  if (!matcher.hooks || matcher.hooks.length === 0)
37476
37576
  continue;
37477
37577
  for (const hook of matcher.hooks) {
37478
- if (hook.type !== "command")
37578
+ if (hook.type !== "command" && hook.type !== "http")
37479
37579
  continue;
37480
- if (isHookCommandDisabled("PreToolUse", hook.command, extendedConfig ?? null)) {
37481
- log("PreToolUse hook command skipped (disabled by config)", { command: hook.command, toolName: ctx.toolName });
37580
+ const hookName = getHookIdentifier(hook);
37581
+ if (isHookCommandDisabled("PreToolUse", hookName, extendedConfig ?? null)) {
37582
+ log("PreToolUse hook command skipped (disabled by config)", { command: hookName, toolName: ctx.toolName });
37482
37583
  continue;
37483
37584
  }
37484
- const hookName = hook.command.split("/").pop() || hook.command;
37485
37585
  if (!firstHookName)
37486
37586
  firstHookName = hookName;
37487
- const result = await executeHookCommand(hook.command, JSON.stringify(stdinData), ctx.cwd, { forceZsh: DEFAULT_CONFIG.forceZsh, zshPath: DEFAULT_CONFIG.zshPath });
37587
+ const result = await dispatchHook(hook, JSON.stringify(stdinData), ctx.cwd);
37488
37588
  if (result.exitCode === 2) {
37489
37589
  return {
37490
37590
  decision: "deny",
@@ -38165,7 +38265,11 @@ function createBackgroundNotificationHook(manager) {
38165
38265
  const eventHandler = async ({ event }) => {
38166
38266
  manager.handleEvent(event);
38167
38267
  };
38268
+ const chatMessageHandler = async (input, output) => {
38269
+ manager.injectPendingNotificationsIntoChatMessage(output, input.sessionID);
38270
+ };
38168
38271
  return {
38272
+ "chat.message": chatMessageHandler,
38169
38273
  event: eventHandler
38170
38274
  };
38171
38275
  }
@@ -38481,6 +38585,64 @@ function getConfigContext() {
38481
38585
  function getConfigDir() {
38482
38586
  return getConfigContext().paths.configDir;
38483
38587
  }
38588
+ // src/shared/spawn-with-windows-hide.ts
38589
+ var {spawn: bunSpawn } = globalThis.Bun;
38590
+ import { spawn as nodeSpawn } from "child_process";
38591
+ import { Readable } from "stream";
38592
+ function toReadableStream(stream) {
38593
+ if (!stream) {
38594
+ return;
38595
+ }
38596
+ return Readable.toWeb(stream);
38597
+ }
38598
+ function wrapNodeProcess(proc) {
38599
+ let resolveExited;
38600
+ let exitCode = null;
38601
+ const exited = new Promise((resolve5) => {
38602
+ resolveExited = resolve5;
38603
+ });
38604
+ proc.on("exit", (code) => {
38605
+ exitCode = code ?? 1;
38606
+ resolveExited(exitCode);
38607
+ });
38608
+ proc.on("error", () => {
38609
+ if (exitCode === null) {
38610
+ exitCode = 1;
38611
+ resolveExited(1);
38612
+ }
38613
+ });
38614
+ return {
38615
+ get exitCode() {
38616
+ return exitCode;
38617
+ },
38618
+ exited,
38619
+ stdout: toReadableStream(proc.stdout),
38620
+ stderr: toReadableStream(proc.stderr),
38621
+ kill(signal) {
38622
+ try {
38623
+ if (!signal) {
38624
+ proc.kill();
38625
+ return;
38626
+ }
38627
+ proc.kill(signal);
38628
+ } catch {}
38629
+ }
38630
+ };
38631
+ }
38632
+ function spawnWithWindowsHide(command, options) {
38633
+ if (process.platform !== "win32") {
38634
+ return bunSpawn(command, options);
38635
+ }
38636
+ const [cmd, ...args] = command;
38637
+ const proc = nodeSpawn(cmd, args, {
38638
+ cwd: options.cwd,
38639
+ env: options.env,
38640
+ stdio: [options.stdin ?? "pipe", options.stdout ?? "pipe", options.stderr ?? "pipe"],
38641
+ windowsHide: true,
38642
+ shell: true
38643
+ });
38644
+ return wrapNodeProcess(proc);
38645
+ }
38484
38646
  // src/cli/config-manager/bun-install.ts
38485
38647
  var BUN_INSTALL_TIMEOUT_SECONDS = 60;
38486
38648
  var BUN_INSTALL_TIMEOUT_MS = BUN_INSTALL_TIMEOUT_SECONDS * 1000;
@@ -38490,7 +38652,7 @@ async function runBunInstall() {
38490
38652
  }
38491
38653
  async function runBunInstallWithDetails() {
38492
38654
  try {
38493
- const proc = Bun.spawn(["bun", "install"], {
38655
+ const proc = spawnWithWindowsHide(["bun", "install"], {
38494
38656
  cwd: getConfigDir(),
38495
38657
  stdout: "inherit",
38496
38658
  stderr: "inherit"
@@ -38932,6 +39094,16 @@ function clearAgentUsageState(sessionID) {
38932
39094
  }
38933
39095
 
38934
39096
  // src/hooks/agent-usage-reminder/hook.ts
39097
+ var ORCHESTRATOR_AGENTS = new Set([
39098
+ "sisyphus",
39099
+ "sisyphus-junior",
39100
+ "atlas",
39101
+ "hephaestus",
39102
+ "prometheus"
39103
+ ]);
39104
+ function isOrchestratorAgent(agentName) {
39105
+ return ORCHESTRATOR_AGENTS.has(getAgentConfigKey(agentName));
39106
+ }
38935
39107
  function createAgentUsageReminderHook(_ctx) {
38936
39108
  const sessionStates = new Map;
38937
39109
  function getOrCreateState(sessionID) {
@@ -38959,6 +39131,10 @@ function createAgentUsageReminderHook(_ctx) {
38959
39131
  }
38960
39132
  const toolExecuteAfter = async (input, output) => {
38961
39133
  const { tool, sessionID } = input;
39134
+ const agent = getSessionAgent(sessionID);
39135
+ if (agent && !isOrchestratorAgent(agent)) {
39136
+ return;
39137
+ }
38962
39138
  const toolLower = tool.toLowerCase();
38963
39139
  if (AGENT_TOOLS.has(toolLower)) {
38964
39140
  markAgentUsed(sessionID);
@@ -39000,12 +39176,9 @@ function createAgentUsageReminderHook(_ctx) {
39000
39176
  function extractModelName(model) {
39001
39177
  return model.includes("/") ? model.split("/").pop() ?? model : model;
39002
39178
  }
39003
- var GPT_MODEL_PREFIXES = ["gpt-", "gpt4", "o1", "o3", "o4"];
39004
39179
  function isGptModel(model) {
39005
- if (model.startsWith("openai/") || model.startsWith("github-copilot/gpt-"))
39006
- return true;
39007
39180
  const modelName = extractModelName(model).toLowerCase();
39008
- return GPT_MODEL_PREFIXES.some((prefix) => modelName.startsWith(prefix));
39181
+ return modelName.includes("gpt");
39009
39182
  }
39010
39183
  var GEMINI_PROVIDERS = ["google/", "google-vertex/"];
39011
39184
  function isGeminiModel(model) {
@@ -40241,7 +40414,7 @@ function isOmoSession(sessionName) {
40241
40414
  async function killAllTrackedSessions(state3) {
40242
40415
  for (const sessionName of state3.tmuxSessions) {
40243
40416
  try {
40244
- const proc = Bun.spawn(["tmux", "kill-session", "-t", sessionName], {
40417
+ const proc = spawnWithWindowsHide(["tmux", "kill-session", "-t", sessionName], {
40245
40418
  stdout: "ignore",
40246
40419
  stderr: "ignore"
40247
40420
  });
@@ -40561,6 +40734,7 @@ function readState(directory, customPath) {
40561
40734
  active: isActive,
40562
40735
  iteration: iterationNum,
40563
40736
  max_iterations: Number(data.max_iterations) || DEFAULT_MAX_ITERATIONS,
40737
+ message_count_at_start: typeof data.message_count_at_start === "number" ? data.message_count_at_start : typeof data.message_count_at_start === "string" && data.message_count_at_start.trim() !== "" ? Number(data.message_count_at_start) : undefined,
40564
40738
  completion_promise: stripQuotes(data.completion_promise) || DEFAULT_COMPLETION_PROMISE,
40565
40739
  started_at: stripQuotes(data.started_at) || new Date().toISOString(),
40566
40740
  prompt: body.trim(),
@@ -40584,6 +40758,8 @@ function writeState(directory, state3, customPath) {
40584
40758
  const ultraworkLine = state3.ultrawork !== undefined ? `ultrawork: ${state3.ultrawork}
40585
40759
  ` : "";
40586
40760
  const strategyLine = state3.strategy ? `strategy: "${state3.strategy}"
40761
+ ` : "";
40762
+ const messageCountAtStartLine = typeof state3.message_count_at_start === "number" ? `message_count_at_start: ${state3.message_count_at_start}
40587
40763
  ` : "";
40588
40764
  const content = `---
40589
40765
  active: ${state3.active}
@@ -40591,7 +40767,7 @@ iteration: ${state3.iteration}
40591
40767
  max_iterations: ${state3.max_iterations}
40592
40768
  completion_promise: "${state3.completion_promise}"
40593
40769
  started_at: "${state3.started_at}"
40594
- ${sessionIdLine}${ultraworkLine}${strategyLine}---
40770
+ ${sessionIdLine}${ultraworkLine}${strategyLine}${messageCountAtStartLine}---
40595
40771
  ${state3.prompt}
40596
40772
  `;
40597
40773
  writeFileSync16(filePath, content, "utf-8");
@@ -40662,6 +40838,7 @@ function createLoopStateController(options) {
40662
40838
  active: true,
40663
40839
  iteration: 1,
40664
40840
  max_iterations: loopOptions?.maxIterations ?? config2?.default_max_iterations ?? DEFAULT_MAX_ITERATIONS,
40841
+ message_count_at_start: loopOptions?.messageCountAtStart,
40665
40842
  completion_promise: loopOptions?.completionPromise ?? DEFAULT_COMPLETION_PROMISE,
40666
40843
  ultrawork: loopOptions?.ultrawork,
40667
40844
  strategy: loopOptions?.strategy ?? config2?.default_strategy ?? "continue",
@@ -40709,6 +40886,17 @@ function createLoopStateController(options) {
40709
40886
  return null;
40710
40887
  }
40711
40888
  return state3;
40889
+ },
40890
+ setMessageCountAtStart(sessionID, messageCountAtStart) {
40891
+ const state3 = readState(directory, stateDir);
40892
+ if (!state3 || state3.session_id !== sessionID) {
40893
+ return null;
40894
+ }
40895
+ state3.message_count_at_start = messageCountAtStart;
40896
+ if (!writeState(directory, state3, stateDir)) {
40897
+ return null;
40898
+ }
40899
+ return state3;
40712
40900
  }
40713
40901
  };
40714
40902
  }
@@ -40779,12 +40967,13 @@ async function detectCompletionInSessionMessages(ctx, options) {
40779
40967
  const messagesResponse = response;
40780
40968
  const responseData = typeof messagesResponse === "object" && messagesResponse !== null && "data" in messagesResponse ? messagesResponse.data : undefined;
40781
40969
  const messageArray = Array.isArray(messagesResponse) ? messagesResponse : Array.isArray(responseData) ? responseData : [];
40782
- const assistantMessages = messageArray.filter((msg) => msg.info?.role === "assistant");
40970
+ const scopedMessages = typeof options.sinceMessageIndex === "number" && options.sinceMessageIndex >= 0 && options.sinceMessageIndex < messageArray.length ? messageArray.slice(options.sinceMessageIndex) : messageArray;
40971
+ const assistantMessages = scopedMessages.filter((msg) => msg.info?.role === "assistant");
40783
40972
  if (assistantMessages.length === 0)
40784
40973
  return false;
40785
40974
  const pattern = buildPromisePattern(options.promise);
40786
- const recentAssistants = assistantMessages.slice(-3);
40787
- for (const assistant of recentAssistants) {
40975
+ for (let index = assistantMessages.length - 1;index >= 0; index -= 1) {
40976
+ const assistant = assistantMessages[index];
40788
40977
  if (!assistant.parts)
40789
40978
  continue;
40790
40979
  let responseText = "";
@@ -40936,14 +41125,6 @@ async function continueIteration(ctx, state3, options) {
40936
41125
  if (!newSessionID) {
40937
41126
  return;
40938
41127
  }
40939
- const boundState = options.loopState.setSessionID(newSessionID);
40940
- if (!boundState) {
40941
- log(`[${HOOK_NAME3}] Failed to bind loop state to new session`, {
40942
- previousSessionID: options.previousSessionID,
40943
- newSessionID
40944
- });
40945
- return;
40946
- }
40947
41128
  await injectContinuationPrompt(ctx, {
40948
41129
  sessionID: newSessionID,
40949
41130
  inheritFromSessionID: options.previousSessionID,
@@ -40952,6 +41133,14 @@ async function continueIteration(ctx, state3, options) {
40952
41133
  apiTimeoutMs: options.apiTimeoutMs
40953
41134
  });
40954
41135
  await selectSessionInTui(ctx.client, newSessionID);
41136
+ const boundState = options.loopState.setSessionID(newSessionID);
41137
+ if (!boundState) {
41138
+ log(`[${HOOK_NAME3}] Failed to bind loop state to new session`, {
41139
+ previousSessionID: options.previousSessionID,
41140
+ newSessionID
41141
+ });
41142
+ return;
41143
+ }
40955
41144
  return;
40956
41145
  }
40957
41146
  await injectContinuationPrompt(ctx, {
@@ -40964,106 +41153,117 @@ async function continueIteration(ctx, state3, options) {
40964
41153
 
40965
41154
  // src/hooks/ralph-loop/ralph-loop-event-handler.ts
40966
41155
  function createRalphLoopEventHandler(ctx, options) {
41156
+ const inFlightSessions = new Set;
40967
41157
  return async ({ event }) => {
40968
41158
  const props = event.properties;
40969
41159
  if (event.type === "session.idle") {
40970
41160
  const sessionID = props?.sessionID;
40971
41161
  if (!sessionID)
40972
41162
  return;
40973
- if (options.sessionRecovery.isRecovering(sessionID)) {
40974
- log(`[${HOOK_NAME3}] Skipped: in recovery`, { sessionID });
40975
- return;
40976
- }
40977
- const state3 = options.loopState.getState();
40978
- if (!state3 || !state3.active) {
41163
+ if (inFlightSessions.has(sessionID)) {
41164
+ log(`[${HOOK_NAME3}] Skipped: handler in flight`, { sessionID });
40979
41165
  return;
40980
41166
  }
40981
- if (state3.session_id && state3.session_id !== sessionID) {
40982
- if (options.checkSessionExists) {
40983
- try {
40984
- const exists = await options.checkSessionExists(state3.session_id);
40985
- if (!exists) {
40986
- options.loopState.clear();
40987
- log(`[${HOOK_NAME3}] Cleared orphaned state from deleted session`, {
40988
- orphanedSessionId: state3.session_id,
40989
- currentSessionId: sessionID
41167
+ inFlightSessions.add(sessionID);
41168
+ try {
41169
+ if (options.sessionRecovery.isRecovering(sessionID)) {
41170
+ log(`[${HOOK_NAME3}] Skipped: in recovery`, { sessionID });
41171
+ return;
41172
+ }
41173
+ const state3 = options.loopState.getState();
41174
+ if (!state3 || !state3.active) {
41175
+ return;
41176
+ }
41177
+ if (state3.session_id && state3.session_id !== sessionID) {
41178
+ if (options.checkSessionExists) {
41179
+ try {
41180
+ const exists = await options.checkSessionExists(state3.session_id);
41181
+ if (!exists) {
41182
+ options.loopState.clear();
41183
+ log(`[${HOOK_NAME3}] Cleared orphaned state from deleted session`, {
41184
+ orphanedSessionId: state3.session_id,
41185
+ currentSessionId: sessionID
41186
+ });
41187
+ return;
41188
+ }
41189
+ } catch (err) {
41190
+ log(`[${HOOK_NAME3}] Failed to check session existence`, {
41191
+ sessionId: state3.session_id,
41192
+ error: String(err)
40990
41193
  });
40991
- return;
40992
41194
  }
40993
- } catch (err) {
40994
- log(`[${HOOK_NAME3}] Failed to check session existence`, {
40995
- sessionId: state3.session_id,
40996
- error: String(err)
40997
- });
40998
41195
  }
41196
+ return;
40999
41197
  }
41000
- return;
41001
- }
41002
- const transcriptPath = options.getTranscriptPath(sessionID);
41003
- const completionViaTranscript = detectCompletionInTranscript(transcriptPath, state3.completion_promise);
41004
- const completionViaApi = completionViaTranscript ? false : await detectCompletionInSessionMessages(ctx, {
41005
- sessionID,
41006
- promise: state3.completion_promise,
41007
- apiTimeoutMs: options.apiTimeoutMs,
41008
- directory: options.directory
41009
- });
41010
- if (completionViaTranscript || completionViaApi) {
41011
- log(`[${HOOK_NAME3}] Completion detected!`, {
41198
+ const transcriptPath = options.getTranscriptPath(sessionID);
41199
+ const completionViaTranscript = detectCompletionInTranscript(transcriptPath, state3.completion_promise);
41200
+ const completionViaApi = completionViaTranscript ? false : await detectCompletionInSessionMessages(ctx, {
41012
41201
  sessionID,
41013
- iteration: state3.iteration,
41014
41202
  promise: state3.completion_promise,
41015
- detectedVia: completionViaTranscript ? "transcript_file" : "session_messages_api"
41203
+ apiTimeoutMs: options.apiTimeoutMs,
41204
+ directory: options.directory,
41205
+ sinceMessageIndex: state3.message_count_at_start
41016
41206
  });
41017
- options.loopState.clear();
41018
- const title = state3.ultrawork ? "ULTRAWORK LOOP COMPLETE!" : "Ralph Loop Complete!";
41019
- const message = state3.ultrawork ? `JUST ULW ULW! Task completed after ${state3.iteration} iteration(s)` : `Task completed after ${state3.iteration} iteration(s)`;
41020
- await ctx.client.tui?.showToast?.({ body: { title, message, variant: "success", duration: 5000 } }).catch(() => {});
41021
- return;
41022
- }
41023
- if (state3.iteration >= state3.max_iterations) {
41024
- log(`[${HOOK_NAME3}] Max iterations reached`, {
41207
+ if (completionViaTranscript || completionViaApi) {
41208
+ log(`[${HOOK_NAME3}] Completion detected!`, {
41209
+ sessionID,
41210
+ iteration: state3.iteration,
41211
+ promise: state3.completion_promise,
41212
+ detectedVia: completionViaTranscript ? "transcript_file" : "session_messages_api"
41213
+ });
41214
+ options.loopState.clear();
41215
+ const title = state3.ultrawork ? "ULTRAWORK LOOP COMPLETE!" : "Ralph Loop Complete!";
41216
+ const message = state3.ultrawork ? `JUST ULW ULW! Task completed after ${state3.iteration} iteration(s)` : `Task completed after ${state3.iteration} iteration(s)`;
41217
+ await ctx.client.tui?.showToast?.({ body: { title, message, variant: "success", duration: 5000 } }).catch(() => {});
41218
+ return;
41219
+ }
41220
+ if (state3.iteration >= state3.max_iterations) {
41221
+ log(`[${HOOK_NAME3}] Max iterations reached`, {
41222
+ sessionID,
41223
+ iteration: state3.iteration,
41224
+ max: state3.max_iterations
41225
+ });
41226
+ options.loopState.clear();
41227
+ await ctx.client.tui?.showToast?.({
41228
+ body: { title: "Ralph Loop Stopped", message: `Max iterations (${state3.max_iterations}) reached without completion`, variant: "warning", duration: 5000 }
41229
+ }).catch(() => {});
41230
+ return;
41231
+ }
41232
+ const newState = options.loopState.incrementIteration();
41233
+ if (!newState) {
41234
+ log(`[${HOOK_NAME3}] Failed to increment iteration`, { sessionID });
41235
+ return;
41236
+ }
41237
+ log(`[${HOOK_NAME3}] Continuing loop`, {
41025
41238
  sessionID,
41026
- iteration: state3.iteration,
41027
- max: state3.max_iterations
41239
+ iteration: newState.iteration,
41240
+ max: newState.max_iterations
41028
41241
  });
41029
- options.loopState.clear();
41030
41242
  await ctx.client.tui?.showToast?.({
41031
- body: { title: "Ralph Loop Stopped", message: `Max iterations (${state3.max_iterations}) reached without completion`, variant: "warning", duration: 5000 }
41243
+ body: {
41244
+ title: "Ralph Loop",
41245
+ message: `Iteration ${newState.iteration}/${newState.max_iterations}`,
41246
+ variant: "info",
41247
+ duration: 2000
41248
+ }
41032
41249
  }).catch(() => {});
41033
- return;
41034
- }
41035
- const newState = options.loopState.incrementIteration();
41036
- if (!newState) {
41037
- log(`[${HOOK_NAME3}] Failed to increment iteration`, { sessionID });
41038
- return;
41039
- }
41040
- log(`[${HOOK_NAME3}] Continuing loop`, {
41041
- sessionID,
41042
- iteration: newState.iteration,
41043
- max: newState.max_iterations
41044
- });
41045
- await ctx.client.tui?.showToast?.({
41046
- body: {
41047
- title: "Ralph Loop",
41048
- message: `Iteration ${newState.iteration}/${newState.max_iterations}`,
41049
- variant: "info",
41050
- duration: 2000
41250
+ try {
41251
+ await continueIteration(ctx, newState, {
41252
+ previousSessionID: sessionID,
41253
+ directory: options.directory,
41254
+ apiTimeoutMs: options.apiTimeoutMs,
41255
+ loopState: options.loopState
41256
+ });
41257
+ } catch (err) {
41258
+ log(`[${HOOK_NAME3}] Failed to inject continuation`, {
41259
+ sessionID,
41260
+ error: String(err)
41261
+ });
41051
41262
  }
41052
- }).catch(() => {});
41053
- try {
41054
- await continueIteration(ctx, newState, {
41055
- previousSessionID: sessionID,
41056
- directory: options.directory,
41057
- apiTimeoutMs: options.apiTimeoutMs,
41058
- loopState: options.loopState
41059
- });
41060
- } catch (err) {
41061
- log(`[${HOOK_NAME3}] Failed to inject continuation`, {
41062
- sessionID,
41063
- error: String(err)
41064
- });
41263
+ return;
41264
+ } finally {
41265
+ inFlightSessions.delete(sessionID);
41065
41266
  }
41066
- return;
41067
41267
  }
41068
41268
  if (event.type === "session.deleted") {
41069
41269
  const sessionInfo = props?.info;
@@ -41100,6 +41300,16 @@ function createRalphLoopEventHandler(ctx, options) {
41100
41300
 
41101
41301
  // src/hooks/ralph-loop/ralph-loop-hook.ts
41102
41302
  var DEFAULT_API_TIMEOUT = 5000;
41303
+ function getMessageCountFromResponse(messagesResponse) {
41304
+ if (Array.isArray(messagesResponse)) {
41305
+ return messagesResponse.length;
41306
+ }
41307
+ if (typeof messagesResponse === "object" && messagesResponse !== null && "data" in messagesResponse) {
41308
+ const data = messagesResponse.data;
41309
+ return Array.isArray(data) ? data.length : 0;
41310
+ }
41311
+ return 0;
41312
+ }
41103
41313
  function createRalphLoopHook(ctx, options) {
41104
41314
  const config2 = options?.config;
41105
41315
  const stateDir = config2?.state_dir;
@@ -41122,7 +41332,20 @@ function createRalphLoopHook(ctx, options) {
41122
41332
  });
41123
41333
  return {
41124
41334
  event,
41125
- startLoop: loopState.startLoop,
41335
+ startLoop: (sessionID, prompt, loopOptions) => {
41336
+ const startSuccess = loopState.startLoop(sessionID, prompt, loopOptions);
41337
+ if (!startSuccess || typeof loopOptions?.messageCountAtStart === "number") {
41338
+ return startSuccess;
41339
+ }
41340
+ ctx.client.session.messages({
41341
+ path: { id: sessionID },
41342
+ query: { directory: ctx.directory }
41343
+ }).then((messagesResponse) => {
41344
+ const messageCountAtStart = getMessageCountFromResponse(messagesResponse);
41345
+ loopState.setMessageCountAtStart(sessionID, messageCountAtStart);
41346
+ }).catch(() => {});
41347
+ return startSuccess;
41348
+ },
41126
41349
  cancelLoop: loopState.cancelLoop,
41127
41350
  getState: loopState.getState
41128
41351
  };
@@ -41177,12 +41400,12 @@ var TOAST_MESSAGE2 = [
41177
41400
  ].join(`
41178
41401
  `);
41179
41402
  var SISYPHUS_DISPLAY = getAgentDisplayName("sisyphus");
41180
- function showToast2(ctx, sessionID) {
41403
+ function showToast2(ctx, sessionID, variant) {
41181
41404
  ctx.client.tui.showToast({
41182
41405
  body: {
41183
41406
  title: TOAST_TITLE2,
41184
41407
  message: TOAST_MESSAGE2,
41185
- variant: "error",
41408
+ variant,
41186
41409
  duration: 1e4
41187
41410
  }
41188
41411
  }).catch((error45) => {
@@ -41192,14 +41415,18 @@ function showToast2(ctx, sessionID) {
41192
41415
  });
41193
41416
  });
41194
41417
  }
41195
- function createNoHephaestusNonGptHook(ctx) {
41418
+ function createNoHephaestusNonGptHook(ctx, options) {
41196
41419
  return {
41197
41420
  "chat.message": async (input, output) => {
41198
41421
  const rawAgent = input.agent ?? getSessionAgent(input.sessionID) ?? "";
41199
41422
  const agentKey = getAgentConfigKey(rawAgent);
41200
41423
  const modelID = input.model?.modelID;
41424
+ const allowNonGptModel = options?.allowNonGptModel === true;
41201
41425
  if (agentKey === "hephaestus" && modelID && !isGptModel(modelID)) {
41202
- showToast2(ctx, input.sessionID);
41426
+ showToast2(ctx, input.sessionID, allowNonGptModel ? "warning" : "error");
41427
+ if (allowNonGptModel) {
41428
+ return;
41429
+ }
41203
41430
  input.agent = SISYPHUS_DISPLAY;
41204
41431
  if (output?.message) {
41205
41432
  output.message.agent = SISYPHUS_DISPLAY;
@@ -42269,6 +42496,15 @@ $ARGUMENTS
42269
42496
  // src/features/builtin-commands/templates/start-work.ts
42270
42497
  var START_WORK_TEMPLATE = `You are starting a Sisyphus work session.
42271
42498
 
42499
+ ## ARGUMENTS
42500
+
42501
+ - \`/start-work [plan-name] [--worktree <path>]\`
42502
+ - \`plan-name\` (optional): name or partial match of the plan to start
42503
+ - \`--worktree <path>\` (optional): absolute path to an existing git worktree to work in
42504
+ - If specified and valid: hook pre-sets worktree_path in boulder.json
42505
+ - If specified but invalid: you must run \`git worktree add <path> <branch>\` first
42506
+ - If omitted: you MUST choose or create a worktree (see Worktree Setup below)
42507
+
42272
42508
  ## WHAT TO DO
42273
42509
 
42274
42510
  1. **Find available plans**: Search for Prometheus-generated plan files at \`.sisyphus/plans/\`
@@ -42284,17 +42520,24 @@ var START_WORK_TEMPLATE = `You are starting a Sisyphus work session.
42284
42520
  - If ONE plan: auto-select it
42285
42521
  - If MULTIPLE plans: show list with timestamps, ask user to select
42286
42522
 
42287
- 4. **Create/Update boulder.json**:
42523
+ 4. **Worktree Setup** (when \`worktree_path\` not already set in boulder.json):
42524
+ 1. \`git worktree list --porcelain\` \u2014 see available worktrees
42525
+ 2. Create: \`git worktree add <absolute-path> <branch-or-HEAD>\`
42526
+ 3. Update boulder.json to add \`"worktree_path": "<absolute-path>"\`
42527
+ 4. All work happens inside that worktree directory
42528
+
42529
+ 5. **Create/Update boulder.json**:
42288
42530
  \`\`\`json
42289
42531
  {
42290
42532
  "active_plan": "/absolute/path/to/plan.md",
42291
42533
  "started_at": "ISO_TIMESTAMP",
42292
42534
  "session_ids": ["session_id_1", "session_id_2"],
42293
- "plan_name": "plan-name"
42535
+ "plan_name": "plan-name",
42536
+ "worktree_path": "/absolute/path/to/git/worktree"
42294
42537
  }
42295
42538
  \`\`\`
42296
42539
 
42297
- 5. **Read the plan file** and start executing tasks according to atlas workflow
42540
+ 6. **Read the plan file** and start executing tasks according to atlas workflow
42298
42541
 
42299
42542
  ## OUTPUT FORMAT
42300
42543
 
@@ -42318,6 +42561,7 @@ Resuming Work Session
42318
42561
  Active Plan: {plan-name}
42319
42562
  Progress: {completed}/{total} tasks
42320
42563
  Sessions: {count} (appending current session)
42564
+ Worktree: {worktree_path}
42321
42565
 
42322
42566
  Reading plan and continuing from last incomplete task...
42323
42567
  \`\`\`
@@ -42329,6 +42573,7 @@ Starting Work Session
42329
42573
  Plan: {plan-name}
42330
42574
  Session ID: {session_id}
42331
42575
  Started: {timestamp}
42576
+ Worktree: {worktree_path}
42332
42577
 
42333
42578
  Reading plan and beginning execution...
42334
42579
  \`\`\`
@@ -42337,6 +42582,7 @@ Reading plan and beginning execution...
42337
42582
 
42338
42583
  - The session_id is injected by the hook - use it directly
42339
42584
  - Always update boulder.json BEFORE starting work
42585
+ - Always set worktree_path in boulder.json before executing any tasks
42340
42586
  - Read the FULL plan file before delegating any tasks
42341
42587
  - Follow atlas delegation protocols (7-section format)`;
42342
42588
 
@@ -45838,8 +46084,8 @@ function getPlanProgress(planPath) {
45838
46084
  }
45839
46085
  try {
45840
46086
  const content = readFileSync35(planPath, "utf-8");
45841
- const uncheckedMatches = content.match(/^[-*]\s*\[\s*\]/gm) || [];
45842
- const checkedMatches = content.match(/^[-*]\s*\[[xX]\]/gm) || [];
46087
+ const uncheckedMatches = content.match(/^\s*[-*]\s*\[\s*\]/gm) || [];
46088
+ const checkedMatches = content.match(/^\s*[-*]\s*\[[xX]\]/gm) || [];
45843
46089
  const total = uncheckedMatches.length + checkedMatches.length;
45844
46090
  const completed = checkedMatches.length;
45845
46091
  return {
@@ -45854,13 +46100,14 @@ function getPlanProgress(planPath) {
45854
46100
  function getPlanName(planPath) {
45855
46101
  return basename3(planPath, ".md");
45856
46102
  }
45857
- function createBoulderState(planPath, sessionId, agent) {
46103
+ function createBoulderState(planPath, sessionId, agent, worktreePath) {
45858
46104
  return {
45859
46105
  active_plan: planPath,
45860
46106
  started_at: new Date().toISOString(),
45861
46107
  session_ids: [sessionId],
45862
46108
  plan_name: getPlanName(planPath),
45863
- ...agent !== undefined ? { agent } : {}
46109
+ ...agent !== undefined ? { agent } : {},
46110
+ ...worktreePath !== undefined ? { worktree_path: worktreePath } : {}
45864
46111
  };
45865
46112
  }
45866
46113
  // src/hooks/prometheus-md-only/agent-resolution.ts
@@ -46060,19 +46307,48 @@ to continue: task(session_id="${sessionId}", prompt="...")`;
46060
46307
  };
46061
46308
  }
46062
46309
  // src/hooks/start-work/start-work-hook.ts
46310
+ import { statSync as statSync6 } from "fs";
46063
46311
  init_logger();
46064
- var HOOK_NAME6 = "start-work";
46065
- var KEYWORD_PATTERN = /\b(ultrawork|ulw)\b/gi;
46066
- function extractUserRequestPlanName(promptText) {
46067
- const userRequestMatch = promptText.match(/<user-request>\s*([\s\S]*?)\s*<\/user-request>/i);
46068
- if (!userRequestMatch)
46312
+
46313
+ // src/hooks/start-work/worktree-detector.ts
46314
+ import { execFileSync as execFileSync2 } from "child_process";
46315
+ function detectWorktreePath(directory) {
46316
+ try {
46317
+ return execFileSync2("git", ["rev-parse", "--show-toplevel"], {
46318
+ cwd: directory,
46319
+ encoding: "utf-8",
46320
+ timeout: 5000,
46321
+ stdio: ["pipe", "pipe", "pipe"]
46322
+ }).trim();
46323
+ } catch {
46069
46324
  return null;
46070
- const rawArg = userRequestMatch[1].trim();
46325
+ }
46326
+ }
46327
+
46328
+ // src/hooks/start-work/parse-user-request.ts
46329
+ var KEYWORD_PATTERN = /\b(ultrawork|ulw)\b/gi;
46330
+ var WORKTREE_FLAG_PATTERN = /--worktree(?:\s+(\S+))?/;
46331
+ function parseUserRequest(promptText) {
46332
+ const match = promptText.match(/<user-request>\s*([\s\S]*?)\s*<\/user-request>/i);
46333
+ if (!match)
46334
+ return { planName: null, explicitWorktreePath: null };
46335
+ let rawArg = match[1].trim();
46071
46336
  if (!rawArg)
46072
- return null;
46337
+ return { planName: null, explicitWorktreePath: null };
46338
+ const worktreeMatch = rawArg.match(WORKTREE_FLAG_PATTERN);
46339
+ const explicitWorktreePath = worktreeMatch ? worktreeMatch[1] ?? null : null;
46340
+ if (worktreeMatch) {
46341
+ rawArg = rawArg.replace(worktreeMatch[0], "").trim();
46342
+ }
46073
46343
  const cleanedArg = rawArg.replace(KEYWORD_PATTERN, "").trim();
46074
- return cleanedArg || null;
46344
+ return {
46345
+ planName: cleanedArg || null,
46346
+ explicitWorktreePath
46347
+ };
46075
46348
  }
46349
+
46350
+ // src/hooks/start-work/start-work-hook.ts
46351
+ var HOOK_NAME6 = "start-work";
46076
46352
  function findPlanByName(plans, requestedName) {
46077
46353
  const lowerName = requestedName.toLowerCase();
46078
46354
  const exactMatch = plans.find((p) => getPlanName(p).toLowerCase() === lowerName);
@@ -46081,29 +46357,48 @@ function findPlanByName(plans, requestedName) {
46081
46357
  const partialMatch = plans.find((p) => getPlanName(p).toLowerCase().includes(lowerName));
46082
46358
  return partialMatch || null;
46083
46359
  }
46360
+ var MODEL_DECIDES_WORKTREE_BLOCK = `
46361
+ ## Worktree Setup Required
46362
+
46363
+ No worktree specified. Before starting work, you MUST choose or create one:
46364
+
46365
+ 1. \`git worktree list --porcelain\` \u2014 list existing worktrees
46366
+ 2. Create if needed: \`git worktree add <absolute-path> <branch-or-HEAD>\`
46367
+ 3. Update \`.sisyphus/boulder.json\` \u2014 add \`"worktree_path": "<absolute-path>"\`
46368
+ 4. Work exclusively inside that worktree directory`;
46369
+ function resolveWorktreeContext(explicitWorktreePath) {
46370
+ if (explicitWorktreePath === null) {
46371
+ return { worktreePath: undefined, block: MODEL_DECIDES_WORKTREE_BLOCK };
46372
+ }
46373
+ const validatedPath = detectWorktreePath(explicitWorktreePath);
46374
+ if (validatedPath) {
46375
+ return { worktreePath: validatedPath, block: `
46376
+ **Worktree**: ${validatedPath}` };
46377
+ }
46378
+ return {
46379
+ worktreePath: undefined,
46380
+ block: `
46381
+ **Worktree** (needs setup): \`git worktree add ${explicitWorktreePath} <branch>\`, then add \`"worktree_path"\` to boulder.json`
46382
+ };
46383
+ }
46084
46384
  function createStartWorkHook(ctx) {
46085
46385
  return {
46086
46386
  "chat.message": async (input, output) => {
46087
46387
  const parts = output.parts;
46088
46388
  const promptText = parts?.filter((p) => p.type === "text" && p.text).map((p) => p.text).join(`
46089
46389
  `).trim() || "";
46090
- const isStartWorkCommand = promptText.includes("<session-context>");
46091
- if (!isStartWorkCommand) {
46390
+ if (!promptText.includes("<session-context>"))
46092
46391
  return;
46093
- }
46094
- log(`[${HOOK_NAME6}] Processing start-work command`, {
46095
- sessionID: input.sessionID
46096
- });
46392
+ log(`[${HOOK_NAME6}] Processing start-work command`, { sessionID: input.sessionID });
46097
46393
  updateSessionAgent(input.sessionID, "atlas");
46098
46394
  const existingState = readBoulderState(ctx.directory);
46099
46395
  const sessionId = input.sessionID;
46100
46396
  const timestamp2 = new Date().toISOString();
46397
+ const { planName: explicitPlanName, explicitWorktreePath } = parseUserRequest(promptText);
46398
+ const { worktreePath, block: worktreeBlock } = resolveWorktreeContext(explicitWorktreePath);
46101
46399
  let contextInfo = "";
46102
- const explicitPlanName = extractUserRequestPlanName(promptText);
46103
46400
  if (explicitPlanName) {
46104
- log(`[${HOOK_NAME6}] Explicit plan name requested: ${explicitPlanName}`, {
46105
- sessionID: input.sessionID
46106
- });
46401
+ log(`[${HOOK_NAME6}] Explicit plan name requested: ${explicitPlanName}`, { sessionID: input.sessionID });
46107
46402
  const allPlans = findPrometheusPlans(ctx.directory);
46108
46403
  const matchedPlan = findPlanByName(allPlans, explicitPlanName);
46109
46404
  if (matchedPlan) {
@@ -46115,10 +46410,9 @@ function createStartWorkHook(ctx) {
46115
46410
  The requested plan "${getPlanName(matchedPlan)}" has been completed.
46116
46411
  All ${progress.total} tasks are done. Create a new plan with: /plan "your task"`;
46117
46412
  } else {
46118
- if (existingState) {
46413
+ if (existingState)
46119
46414
  clearBoulderState(ctx.directory);
46120
- }
46121
- const newState = createBoulderState(matchedPlan, sessionId, "atlas");
46415
+ const newState = createBoulderState(matchedPlan, sessionId, "atlas", worktreePath);
46122
46416
  writeBoulderState(ctx.directory, newState);
46123
46417
  contextInfo = `
46124
46418
  ## Auto-Selected Plan
@@ -46128,6 +46422,7 @@ All ${progress.total} tasks are done. Create a new plan with: /plan "your task"`
46128
46422
  **Progress**: ${progress.completed}/${progress.total} tasks
46129
46423
  **Session ID**: ${sessionId}
46130
46424
  **Started**: ${timestamp2}
46425
+ ${worktreeBlock}
46131
46426
 
46132
46427
  boulder.json has been created. Read the plan and begin execution.`;
46133
46428
  }
@@ -46159,7 +46454,19 @@ No incomplete plans available. Create a new plan with: /plan "your task"`;
46159
46454
  } else if (existingState) {
46160
46455
  const progress = getPlanProgress(existingState.active_plan);
46161
46456
  if (!progress.isComplete) {
46162
- appendSessionId(ctx.directory, sessionId);
46457
+ const effectiveWorktree = worktreePath ?? existingState.worktree_path;
46458
+ if (worktreePath !== undefined) {
46459
+ const updatedSessions = existingState.session_ids.includes(sessionId) ? existingState.session_ids : [...existingState.session_ids, sessionId];
46460
+ writeBoulderState(ctx.directory, {
46461
+ ...existingState,
46462
+ worktree_path: worktreePath,
46463
+ session_ids: updatedSessions
46464
+ });
46465
+ } else {
46466
+ appendSessionId(ctx.directory, sessionId);
46467
+ }
46468
+ const worktreeDisplay = effectiveWorktree ? `
46469
+ **Worktree**: ${effectiveWorktree}` : worktreeBlock;
46163
46470
  contextInfo = `
46164
46471
  ## Active Work Session Found
46165
46472
 
@@ -46169,6 +46476,7 @@ No incomplete plans available. Create a new plan with: /plan "your task"`;
46169
46476
  **Progress**: ${progress.completed}/${progress.total} tasks completed
46170
46477
  **Sessions**: ${existingState.session_ids.length + 1} (current session appended)
46171
46478
  **Started**: ${existingState.started_at}
46479
+ ${worktreeDisplay}
46172
46480
 
46173
46481
  The current session (${sessionId}) has been added to session_ids.
46174
46482
  Read the plan file and continue from the first unchecked task.`;
@@ -46185,7 +46493,6 @@ Looking for new plans...`;
46185
46493
  const incompletePlans = plans.filter((p) => !getPlanProgress(p).isComplete);
46186
46494
  if (plans.length === 0) {
46187
46495
  contextInfo += `
46188
-
46189
46496
  ## No Plans Found
46190
46497
 
46191
46498
  No Prometheus plan files found at .sisyphus/plans/
@@ -46199,7 +46506,7 @@ All ${plans.length} plan(s) are complete. Create a new plan with: /plan "your ta
46199
46506
  } else if (incompletePlans.length === 1) {
46200
46507
  const planPath = incompletePlans[0];
46201
46508
  const progress = getPlanProgress(planPath);
46202
- const newState = createBoulderState(planPath, sessionId, "atlas");
46509
+ const newState = createBoulderState(planPath, sessionId, "atlas", worktreePath);
46203
46510
  writeBoulderState(ctx.directory, newState);
46204
46511
  contextInfo += `
46205
46512
 
@@ -46210,13 +46517,13 @@ All ${plans.length} plan(s) are complete. Create a new plan with: /plan "your ta
46210
46517
  **Progress**: ${progress.completed}/${progress.total} tasks
46211
46518
  **Session ID**: ${sessionId}
46212
46519
  **Started**: ${timestamp2}
46520
+ ${worktreeBlock}
46213
46521
 
46214
46522
  boulder.json has been created. Read the plan and begin execution.`;
46215
46523
  } else {
46216
46524
  const planList = incompletePlans.map((p, i2) => {
46217
46525
  const progress = getPlanProgress(p);
46218
- const stat = __require("fs").statSync(p);
46219
- const modified = new Date(stat.mtimeMs).toISOString();
46526
+ const modified = new Date(statSync6(p).mtimeMs).toISOString();
46220
46527
  return `${i2 + 1}. [${getPlanName(p)}] - Modified: ${modified} - Progress: ${progress.completed}/${progress.total}`;
46221
46528
  }).join(`
46222
46529
  `);
@@ -46231,6 +46538,7 @@ Session ID: ${sessionId}
46231
46538
  ${planList}
46232
46539
 
46233
46540
  Ask the user which plan to work on. Present the options above and wait for their response.
46541
+ ${worktreeBlock}
46234
46542
  </system-reminder>`;
46235
46543
  }
46236
46544
  }
@@ -46244,7 +46552,8 @@ ${contextInfo}`;
46244
46552
  }
46245
46553
  log(`[${HOOK_NAME6}] Context injected`, {
46246
46554
  sessionID: input.sessionID,
46247
- hasExistingState: !!existingState
46555
+ hasExistingState: !!existingState,
46556
+ worktreePath
46248
46557
  });
46249
46558
  }
46250
46559
  };
@@ -46496,6 +46805,7 @@ async function injectBoulderContinuation(input) {
46496
46805
  remaining,
46497
46806
  total,
46498
46807
  agent,
46808
+ worktreePath,
46499
46809
  backgroundManager,
46500
46810
  sessionState
46501
46811
  } = input;
@@ -46504,9 +46814,12 @@ async function injectBoulderContinuation(input) {
46504
46814
  log(`[${HOOK_NAME7}] Skipped injection: background tasks running`, { sessionID });
46505
46815
  return;
46506
46816
  }
46817
+ const worktreeContext = worktreePath ? `
46818
+
46819
+ [Worktree: ${worktreePath}]` : "";
46507
46820
  const prompt = BOULDER_CONTINUATION_PROMPT.replace(/{PLAN_NAME}/g, planName) + `
46508
46821
 
46509
- [Status: ${total - remaining}/${total} completed, ${remaining} remaining]`;
46822
+ [Status: ${total - remaining}/${total} completed, ${remaining} remaining]` + worktreeContext;
46510
46823
  try {
46511
46824
  log(`[${HOOK_NAME7}] Injecting boulder continuation`, { sessionID, planName, remaining });
46512
46825
  const promptContext = await resolveRecentPromptContextForSession(ctx, sessionID);
@@ -46525,6 +46838,7 @@ async function injectBoulderContinuation(input) {
46525
46838
  log(`[${HOOK_NAME7}] Boulder continuation injected`, { sessionID });
46526
46839
  } catch (err) {
46527
46840
  sessionState.promptFailureCount += 1;
46841
+ sessionState.lastFailureAt = Date.now();
46528
46842
  log(`[${HOOK_NAME7}] Boulder continuation failed`, {
46529
46843
  sessionID,
46530
46844
  error: String(err),
@@ -46549,6 +46863,7 @@ async function getLastAgentFromSession(sessionID, client) {
46549
46863
 
46550
46864
  // src/hooks/atlas/event-handler.ts
46551
46865
  var CONTINUATION_COOLDOWN_MS2 = 5000;
46866
+ var FAILURE_BACKOFF_MS = 5 * 60 * 1000;
46552
46867
  function createAtlasEventHandler(input) {
46553
46868
  const { ctx, options, sessions, getState } = input;
46554
46869
  return async ({ event }) => {
@@ -46576,17 +46891,24 @@ function createAtlasEventHandler(input) {
46576
46891
  return;
46577
46892
  }
46578
46893
  const state3 = getState(sessionID);
46894
+ const now = Date.now();
46579
46895
  if (state3.lastEventWasAbortError) {
46580
46896
  state3.lastEventWasAbortError = false;
46581
46897
  log(`[${HOOK_NAME7}] Skipped: abort error immediately before idle`, { sessionID });
46582
46898
  return;
46583
46899
  }
46584
46900
  if (state3.promptFailureCount >= 2) {
46585
- log(`[${HOOK_NAME7}] Skipped: continuation disabled after repeated prompt failures`, {
46586
- sessionID,
46587
- promptFailureCount: state3.promptFailureCount
46588
- });
46589
- return;
46901
+ const timeSinceLastFailure = state3.lastFailureAt !== undefined ? now - state3.lastFailureAt : Number.POSITIVE_INFINITY;
46902
+ if (timeSinceLastFailure < FAILURE_BACKOFF_MS) {
46903
+ log(`[${HOOK_NAME7}] Skipped: continuation in backoff after repeated failures`, {
46904
+ sessionID,
46905
+ promptFailureCount: state3.promptFailureCount,
46906
+ backoffRemaining: FAILURE_BACKOFF_MS - timeSinceLastFailure
46907
+ });
46908
+ return;
46909
+ }
46910
+ state3.promptFailureCount = 0;
46911
+ state3.lastFailureAt = undefined;
46590
46912
  }
46591
46913
  const backgroundManager = options?.backgroundManager;
46592
46914
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((t) => t.status === "running") : false;
@@ -46602,21 +46924,21 @@ function createAtlasEventHandler(input) {
46602
46924
  log(`[${HOOK_NAME7}] Skipped: continuation stopped for session`, { sessionID });
46603
46925
  return;
46604
46926
  }
46927
+ const sessionAgent = getSessionAgent(sessionID);
46605
46928
  const lastAgent = await getLastAgentFromSession(sessionID, ctx.client);
46606
- const lastAgentKey = getAgentConfigKey(lastAgent ?? "");
46929
+ const effectiveAgent = sessionAgent ?? lastAgent;
46930
+ const lastAgentKey = getAgentConfigKey(effectiveAgent ?? "");
46607
46931
  const requiredAgent = getAgentConfigKey(boulderState.agent ?? "atlas");
46608
46932
  const lastAgentMatchesRequired = lastAgentKey === requiredAgent;
46609
- const boulderAgentWasNotExplicitlySet = boulderState.agent === undefined;
46610
46933
  const boulderAgentDefaultsToAtlas = requiredAgent === "atlas";
46611
46934
  const lastAgentIsSisyphus = lastAgentKey === "sisyphus";
46612
- const allowSisyphusWhenDefaultAtlas = boulderAgentWasNotExplicitlySet && boulderAgentDefaultsToAtlas && lastAgentIsSisyphus;
46613
- const agentMatches = lastAgentMatchesRequired || allowSisyphusWhenDefaultAtlas;
46935
+ const allowSisyphusForAtlasBoulder = boulderAgentDefaultsToAtlas && lastAgentIsSisyphus;
46936
+ const agentMatches = lastAgentMatchesRequired || allowSisyphusForAtlasBoulder;
46614
46937
  if (!agentMatches) {
46615
46938
  log(`[${HOOK_NAME7}] Skipped: last agent does not match boulder agent`, {
46616
46939
  sessionID,
46617
- lastAgent: lastAgent ?? "unknown",
46618
- requiredAgent,
46619
- boulderAgentExplicitlySet: boulderState.agent !== undefined
46940
+ lastAgent: effectiveAgent ?? "unknown",
46941
+ requiredAgent
46620
46942
  });
46621
46943
  return;
46622
46944
  }
@@ -46625,7 +46947,6 @@ function createAtlasEventHandler(input) {
46625
46947
  log(`[${HOOK_NAME7}] Boulder complete`, { sessionID, plan: boulderState.plan_name });
46626
46948
  return;
46627
46949
  }
46628
- const now = Date.now();
46629
46950
  if (state3.lastContinuationInjectedAt && now - state3.lastContinuationInjectedAt < CONTINUATION_COOLDOWN_MS2) {
46630
46951
  log(`[${HOOK_NAME7}] Skipped: continuation cooldown active`, {
46631
46952
  sessionID,
@@ -46643,6 +46964,7 @@ function createAtlasEventHandler(input) {
46643
46964
  remaining,
46644
46965
  total: progress.total,
46645
46966
  agent: boulderState.agent,
46967
+ worktreePath: boulderState.worktree_path,
46646
46968
  backgroundManager,
46647
46969
  sessionState: state3
46648
46970
  });
@@ -47125,12 +47447,36 @@ function createQuestionLabelTruncatorHook() {
47125
47447
  // src/hooks/stop-continuation-guard/hook.ts
47126
47448
  init_logger();
47127
47449
  var HOOK_NAME8 = "stop-continuation-guard";
47128
- function createStopContinuationGuardHook(ctx) {
47450
+ function createStopContinuationGuardHook(ctx, options) {
47129
47451
  const stoppedSessions = new Set;
47130
47452
  const stop = (sessionID) => {
47131
47453
  stoppedSessions.add(sessionID);
47132
47454
  setContinuationMarkerSource(ctx.directory, sessionID, "stop", "stopped", "continuation stopped");
47133
47455
  log(`[${HOOK_NAME8}] Continuation stopped for session`, { sessionID });
47456
+ const backgroundManager = options?.backgroundManager;
47457
+ if (!backgroundManager) {
47458
+ return;
47459
+ }
47460
+ const cancellableTasks = backgroundManager.getAllDescendantTasks(sessionID).filter((task) => task.status === "running" || task.status === "pending");
47461
+ if (cancellableTasks.length === 0) {
47462
+ return;
47463
+ }
47464
+ Promise.allSettled(cancellableTasks.map(async (task) => {
47465
+ await backgroundManager.cancelTask(task.id, {
47466
+ source: "stop-continuation",
47467
+ reason: "Continuation stopped via /stop-continuation",
47468
+ abortSession: task.status === "running",
47469
+ skipNotification: true
47470
+ });
47471
+ })).then((results) => {
47472
+ const cancelledCount = results.filter((result) => result.status === "fulfilled").length;
47473
+ const failedCount = results.length - cancelledCount;
47474
+ log(`[${HOOK_NAME8}] Cancelled background tasks for stopped session`, {
47475
+ sessionID,
47476
+ cancelledCount,
47477
+ failedCount
47478
+ });
47479
+ });
47134
47480
  };
47135
47481
  const isStopped = (sessionID) => {
47136
47482
  return stoppedSessions.has(sessionID);
@@ -47531,10 +47877,24 @@ function createUnstableAgentBabysitterHook(ctx, options) {
47531
47877
  // src/hooks/preemptive-compaction.ts
47532
47878
  init_logger();
47533
47879
  var DEFAULT_ACTUAL_LIMIT = 200000;
47880
+ var PREEMPTIVE_COMPACTION_TIMEOUT_MS = 120000;
47534
47881
  function getAnthropicActualLimit3(modelCacheState) {
47535
47882
  return (modelCacheState?.anthropicContext1MEnabled ?? false) || process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : DEFAULT_ACTUAL_LIMIT;
47536
47883
  }
47537
47884
  var PREEMPTIVE_COMPACTION_THRESHOLD = 0.78;
47885
+ function withTimeout2(promise2, timeoutMs, errorMessage) {
47886
+ let timeoutID;
47887
+ const timeoutPromise = new Promise((_, reject) => {
47888
+ timeoutID = setTimeout(() => {
47889
+ reject(new Error(errorMessage));
47890
+ }, timeoutMs);
47891
+ });
47892
+ return Promise.race([promise2, timeoutPromise]).finally(() => {
47893
+ if (timeoutID !== undefined) {
47894
+ clearTimeout(timeoutID);
47895
+ }
47896
+ });
47897
+ }
47538
47898
  function isAnthropicProvider2(providerID) {
47539
47899
  return providerID === "anthropic" || providerID === "google-vertex-anthropic";
47540
47900
  }
@@ -47561,11 +47921,11 @@ function createPreemptiveCompactionHook(ctx, pluginConfig, modelCacheState) {
47561
47921
  compactionInProgress.add(sessionID);
47562
47922
  try {
47563
47923
  const { providerID: targetProviderID, modelID: targetModelID } = resolveCompactionModel(pluginConfig, sessionID, cached2.providerID, modelID);
47564
- await ctx.client.session.summarize({
47924
+ await withTimeout2(ctx.client.session.summarize({
47565
47925
  path: { id: sessionID },
47566
47926
  body: { providerID: targetProviderID, modelID: targetModelID, auto: true },
47567
47927
  query: { directory: ctx.directory }
47568
- });
47928
+ }), PREEMPTIVE_COMPACTION_TIMEOUT_MS, `Compaction summarize timed out after ${PREEMPTIVE_COMPACTION_TIMEOUT_MS}ms`);
47569
47929
  compactedSessions.add(sessionID);
47570
47930
  } catch (error45) {
47571
47931
  log("[preemptive-compaction] Compaction failed", { sessionID, error: String(error45) });
@@ -47773,7 +48133,9 @@ var AgentOverridesSchema = exports_external.object({
47773
48133
  build: AgentOverrideConfigSchema.optional(),
47774
48134
  plan: AgentOverrideConfigSchema.optional(),
47775
48135
  sisyphus: AgentOverrideConfigSchema.optional(),
47776
- hephaestus: AgentOverrideConfigSchema.optional(),
48136
+ hephaestus: AgentOverrideConfigSchema.extend({
48137
+ allow_non_gpt_model: exports_external.boolean().optional()
48138
+ }).optional(),
47777
48139
  "sisyphus-junior": AgentOverrideConfigSchema.optional(),
47778
48140
  "OpenCode-Builder": AgentOverrideConfigSchema.optional(),
47779
48141
  prometheus: AgentOverrideConfigSchema.optional(),
@@ -47824,6 +48186,7 @@ var CategoryConfigSchema = exports_external.object({
47824
48186
  textVerbosity: exports_external.enum(["low", "medium", "high"]).optional(),
47825
48187
  tools: exports_external.record(exports_external.string(), exports_external.boolean()).optional(),
47826
48188
  prompt_append: exports_external.string().optional(),
48189
+ max_prompt_tokens: exports_external.number().int().positive().optional(),
47827
48190
  is_unstable_agent: exports_external.boolean().optional(),
47828
48191
  disable: exports_external.boolean().optional()
47829
48192
  });
@@ -47959,7 +48322,8 @@ var HookNameSchema = exports_external.enum([
47959
48322
  "runtime-fallback",
47960
48323
  "write-existing-file-guard",
47961
48324
  "anthropic-effort",
47962
- "hashline-read-enhancer"
48325
+ "hashline-read-enhancer",
48326
+ "read-image-resizer"
47963
48327
  ]);
47964
48328
  // src/config/schema/notification.ts
47965
48329
  var NotificationConfigSchema = exports_external.object({
@@ -48066,7 +48430,7 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
48066
48430
  new_task_system_enabled: exports_external.boolean().optional(),
48067
48431
  default_run_agent: exports_external.string().optional(),
48068
48432
  disabled_mcps: exports_external.array(AnyMcpNameSchema).optional(),
48069
- disabled_agents: exports_external.array(BuiltinAgentNameSchema).optional(),
48433
+ disabled_agents: exports_external.array(exports_external.string()).optional(),
48070
48434
  disabled_skills: exports_external.array(BuiltinSkillNameSchema).optional(),
48071
48435
  disabled_hooks: exports_external.array(exports_external.string()).optional(),
48072
48436
  disabled_commands: exports_external.array(BuiltinCommandNameSchema).optional(),
@@ -49535,6 +49899,511 @@ ${JSON_ERROR_REMINDER}`;
49535
49899
  }
49536
49900
  };
49537
49901
  }
49902
+ // src/tools/look-at/mime-type-inference.ts
49903
+ import { extname as extname2 } from "path";
49904
+ function inferMimeTypeFromBase64(base64Data) {
49905
+ if (base64Data.startsWith("data:")) {
49906
+ const match = base64Data.match(/^data:([^;]+);/);
49907
+ if (match)
49908
+ return match[1];
49909
+ }
49910
+ try {
49911
+ const cleanData = base64Data.replace(/^data:[^;]+;base64,/, "");
49912
+ const header = atob(cleanData.slice(0, 16));
49913
+ if (header.startsWith("\x89PNG"))
49914
+ return "image/png";
49915
+ if (header.startsWith("\xFF\xD8\xFF"))
49916
+ return "image/jpeg";
49917
+ if (header.startsWith("GIF8"))
49918
+ return "image/gif";
49919
+ if (header.startsWith("RIFF") && header.includes("WEBP"))
49920
+ return "image/webp";
49921
+ if (header.startsWith("%PDF"))
49922
+ return "application/pdf";
49923
+ } catch {}
49924
+ return "image/png";
49925
+ }
49926
+ function inferMimeTypeFromFilePath(filePath) {
49927
+ const ext = extname2(filePath).toLowerCase();
49928
+ const mimeTypes = {
49929
+ ".jpg": "image/jpeg",
49930
+ ".jpeg": "image/jpeg",
49931
+ ".png": "image/png",
49932
+ ".webp": "image/webp",
49933
+ ".heic": "image/heic",
49934
+ ".heif": "image/heif",
49935
+ ".mp4": "video/mp4",
49936
+ ".mpeg": "video/mpeg",
49937
+ ".mpg": "video/mpeg",
49938
+ ".mov": "video/mov",
49939
+ ".avi": "video/avi",
49940
+ ".flv": "video/x-flv",
49941
+ ".webm": "video/webm",
49942
+ ".wmv": "video/wmv",
49943
+ ".3gpp": "video/3gpp",
49944
+ ".3gp": "video/3gpp",
49945
+ ".wav": "audio/wav",
49946
+ ".mp3": "audio/mp3",
49947
+ ".aiff": "audio/aiff",
49948
+ ".aac": "audio/aac",
49949
+ ".ogg": "audio/ogg",
49950
+ ".flac": "audio/flac",
49951
+ ".pdf": "application/pdf",
49952
+ ".txt": "text/plain",
49953
+ ".csv": "text/csv",
49954
+ ".md": "text/md",
49955
+ ".html": "text/html",
49956
+ ".json": "application/json",
49957
+ ".xml": "application/xml",
49958
+ ".js": "text/javascript",
49959
+ ".py": "text/x-python"
49960
+ };
49961
+ return mimeTypes[ext] || "application/octet-stream";
49962
+ }
49963
+ function extractBase64Data(imageData) {
49964
+ if (imageData.startsWith("data:")) {
49965
+ const commaIndex = imageData.indexOf(",");
49966
+ if (commaIndex !== -1) {
49967
+ return imageData.slice(commaIndex + 1);
49968
+ }
49969
+ }
49970
+ return imageData;
49971
+ }
49972
+
49973
+ // src/hooks/read-image-resizer/image-dimensions.ts
49974
+ var HEADER_BYTES = 32768;
49975
+ var HEADER_BASE64_CHARS = Math.ceil(HEADER_BYTES / 3) * 4;
49976
+ function toImageDimensions(width, height) {
49977
+ if (!Number.isFinite(width) || !Number.isFinite(height)) {
49978
+ return null;
49979
+ }
49980
+ if (width <= 0 || height <= 0) {
49981
+ return null;
49982
+ }
49983
+ return { width, height };
49984
+ }
49985
+ function parsePngDimensions(buffer) {
49986
+ if (buffer.length < 24) {
49987
+ return null;
49988
+ }
49989
+ const isPngSignature = buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71 && buffer[4] === 13 && buffer[5] === 10 && buffer[6] === 26 && buffer[7] === 10;
49990
+ if (!isPngSignature || buffer.toString("ascii", 12, 16) !== "IHDR") {
49991
+ return null;
49992
+ }
49993
+ const width = buffer.readUInt32BE(16);
49994
+ const height = buffer.readUInt32BE(20);
49995
+ return toImageDimensions(width, height);
49996
+ }
49997
+ function parseGifDimensions(buffer) {
49998
+ if (buffer.length < 10) {
49999
+ return null;
50000
+ }
50001
+ if (buffer.toString("ascii", 0, 4) !== "GIF8") {
50002
+ return null;
50003
+ }
50004
+ const width = buffer.readUInt16LE(6);
50005
+ const height = buffer.readUInt16LE(8);
50006
+ return toImageDimensions(width, height);
50007
+ }
50008
+ function parseJpegDimensions(buffer) {
50009
+ if (buffer.length < 4 || buffer[0] !== 255 || buffer[1] !== 216) {
50010
+ return null;
50011
+ }
50012
+ let offset = 2;
50013
+ while (offset < buffer.length) {
50014
+ if (buffer[offset] !== 255) {
50015
+ offset += 1;
50016
+ continue;
50017
+ }
50018
+ while (offset < buffer.length && buffer[offset] === 255) {
50019
+ offset += 1;
50020
+ }
50021
+ if (offset >= buffer.length) {
50022
+ return null;
50023
+ }
50024
+ const marker = buffer[offset];
50025
+ offset += 1;
50026
+ if (marker === 217 || marker === 218) {
50027
+ break;
50028
+ }
50029
+ if (offset + 1 >= buffer.length) {
50030
+ return null;
50031
+ }
50032
+ const segmentLength = buffer.readUInt16BE(offset);
50033
+ if (segmentLength < 2) {
50034
+ return null;
50035
+ }
50036
+ if ((marker === 192 || marker === 194) && offset + 7 < buffer.length) {
50037
+ const height = buffer.readUInt16BE(offset + 3);
50038
+ const width = buffer.readUInt16BE(offset + 5);
50039
+ return toImageDimensions(width, height);
50040
+ }
50041
+ offset += segmentLength;
50042
+ }
50043
+ return null;
50044
+ }
50045
+ function readUInt24LE(buffer, offset) {
50046
+ return buffer[offset] | buffer[offset + 1] << 8 | buffer[offset + 2] << 16;
50047
+ }
50048
+ function parseWebpDimensions(buffer) {
50049
+ if (buffer.length < 16) {
50050
+ return null;
50051
+ }
50052
+ if (buffer.toString("ascii", 0, 4) !== "RIFF" || buffer.toString("ascii", 8, 12) !== "WEBP") {
50053
+ return null;
50054
+ }
50055
+ const chunkType = buffer.toString("ascii", 12, 16);
50056
+ if (chunkType === "VP8 ") {
50057
+ if (buffer[23] !== 157 || buffer[24] !== 1 || buffer[25] !== 42) {
50058
+ return null;
50059
+ }
50060
+ const width = buffer.readUInt16LE(26) & 16383;
50061
+ const height = buffer.readUInt16LE(28) & 16383;
50062
+ return toImageDimensions(width, height);
50063
+ }
50064
+ if (chunkType === "VP8L") {
50065
+ if (buffer.length < 25 || buffer[20] !== 47) {
50066
+ return null;
50067
+ }
50068
+ const bits = buffer.readUInt32LE(21);
50069
+ const width = (bits & 16383) + 1;
50070
+ const height = (bits >>> 14 & 16383) + 1;
50071
+ return toImageDimensions(width, height);
50072
+ }
50073
+ if (chunkType === "VP8X") {
50074
+ const width = readUInt24LE(buffer, 24) + 1;
50075
+ const height = readUInt24LE(buffer, 27) + 1;
50076
+ return toImageDimensions(width, height);
50077
+ }
50078
+ return null;
50079
+ }
50080
+ function parseImageDimensions(base64DataUrl, mimeType) {
50081
+ try {
50082
+ if (!base64DataUrl || !mimeType) {
50083
+ return null;
50084
+ }
50085
+ const rawBase64 = extractBase64Data(base64DataUrl);
50086
+ if (!rawBase64) {
50087
+ return null;
50088
+ }
50089
+ const headerBase64 = rawBase64.length > HEADER_BASE64_CHARS ? rawBase64.slice(0, HEADER_BASE64_CHARS) : rawBase64;
50090
+ const buffer = Buffer.from(headerBase64, "base64");
50091
+ if (buffer.length === 0) {
50092
+ return null;
50093
+ }
50094
+ const normalizedMime = mimeType.toLowerCase();
50095
+ if (normalizedMime === "image/png") {
50096
+ return parsePngDimensions(buffer);
50097
+ }
50098
+ if (normalizedMime === "image/gif") {
50099
+ return parseGifDimensions(buffer);
50100
+ }
50101
+ if (normalizedMime === "image/jpeg" || normalizedMime === "image/jpg") {
50102
+ return parseJpegDimensions(buffer);
50103
+ }
50104
+ if (normalizedMime === "image/webp") {
50105
+ return parseWebpDimensions(buffer);
50106
+ }
50107
+ return null;
50108
+ } catch {
50109
+ return null;
50110
+ }
50111
+ }
50112
+
50113
+ // src/hooks/read-image-resizer/image-resizer.ts
50114
+ var ANTHROPIC_MAX_LONG_EDGE = 1568;
50115
+ var ANTHROPIC_MAX_FILE_SIZE = 5 * 1024 * 1024;
50116
+ function resolveSharpFactory(sharpModule) {
50117
+ if (typeof sharpModule === "function") {
50118
+ return sharpModule;
50119
+ }
50120
+ if (!sharpModule || typeof sharpModule !== "object") {
50121
+ return null;
50122
+ }
50123
+ const defaultExport = Reflect.get(sharpModule, "default");
50124
+ return typeof defaultExport === "function" ? defaultExport : null;
50125
+ }
50126
+ function resolveSharpFormat(mimeType) {
50127
+ const normalizedMime = mimeType.toLowerCase();
50128
+ if (normalizedMime === "image/png") {
50129
+ return "png";
50130
+ }
50131
+ if (normalizedMime === "image/gif") {
50132
+ return "gif";
50133
+ }
50134
+ if (normalizedMime === "image/webp") {
50135
+ return "webp";
50136
+ }
50137
+ return "jpeg";
50138
+ }
50139
+ function canAdjustQuality(format2) {
50140
+ return format2 === "jpeg" || format2 === "webp";
50141
+ }
50142
+ function toDimensions(metadata) {
50143
+ const { width, height } = metadata;
50144
+ if (!width || !height) {
50145
+ return null;
50146
+ }
50147
+ return { width, height };
50148
+ }
50149
+ async function renderResizedBuffer(args) {
50150
+ const { sharpFactory, inputBuffer, target, format: format2, quality } = args;
50151
+ return sharpFactory(inputBuffer).resize(target.width, target.height, { fit: "inside" }).toFormat(format2, quality ? { quality } : undefined).toBuffer();
50152
+ }
50153
+ function getErrorMessage3(error45) {
50154
+ return error45 instanceof Error ? error45.message : String(error45);
50155
+ }
50156
+ function calculateTargetDimensions(width, height, maxLongEdge = ANTHROPIC_MAX_LONG_EDGE) {
50157
+ if (width <= 0 || height <= 0 || maxLongEdge <= 0) {
50158
+ return null;
50159
+ }
50160
+ const longEdge = Math.max(width, height);
50161
+ if (longEdge <= maxLongEdge) {
50162
+ return null;
50163
+ }
50164
+ if (width >= height) {
50165
+ return {
50166
+ width: maxLongEdge,
50167
+ height: Math.max(1, Math.floor(height * maxLongEdge / width))
50168
+ };
50169
+ }
50170
+ return {
50171
+ width: Math.max(1, Math.floor(width * maxLongEdge / height)),
50172
+ height: maxLongEdge
50173
+ };
50174
+ }
50175
+ async function resizeImage(base64DataUrl, mimeType, target) {
50176
+ try {
50177
+ const sharpModuleName = "sharp";
50178
+ const sharpModule = await import(sharpModuleName).catch(() => null);
50179
+ if (!sharpModule) {
50180
+ log("[read-image-resizer] sharp unavailable, skipping resize");
50181
+ return null;
50182
+ }
50183
+ const sharpFactory = resolveSharpFactory(sharpModule);
50184
+ if (!sharpFactory) {
50185
+ log("[read-image-resizer] sharp import has unexpected shape");
50186
+ return null;
50187
+ }
50188
+ const rawBase64 = extractBase64Data(base64DataUrl);
50189
+ if (!rawBase64) {
50190
+ return null;
50191
+ }
50192
+ const inputBuffer = Buffer.from(rawBase64, "base64");
50193
+ if (inputBuffer.length === 0) {
50194
+ return null;
50195
+ }
50196
+ const original = toDimensions(await sharpFactory(inputBuffer).metadata());
50197
+ if (!original) {
50198
+ return null;
50199
+ }
50200
+ const format2 = resolveSharpFormat(mimeType);
50201
+ let resizedBuffer = await renderResizedBuffer({
50202
+ sharpFactory,
50203
+ inputBuffer,
50204
+ target,
50205
+ format: format2
50206
+ });
50207
+ if (resizedBuffer.length > ANTHROPIC_MAX_FILE_SIZE && canAdjustQuality(format2)) {
50208
+ for (const quality of [80, 60, 40]) {
50209
+ resizedBuffer = await renderResizedBuffer({
50210
+ sharpFactory,
50211
+ inputBuffer,
50212
+ target,
50213
+ format: format2,
50214
+ quality
50215
+ });
50216
+ if (resizedBuffer.length <= ANTHROPIC_MAX_FILE_SIZE) {
50217
+ break;
50218
+ }
50219
+ }
50220
+ }
50221
+ const resized = toDimensions(await sharpFactory(resizedBuffer).metadata());
50222
+ if (!resized) {
50223
+ return null;
50224
+ }
50225
+ return {
50226
+ resizedDataUrl: `data:${mimeType};base64,${resizedBuffer.toString("base64")}`,
50227
+ original,
50228
+ resized
50229
+ };
50230
+ } catch (error45) {
50231
+ log("[read-image-resizer] resize failed", {
50232
+ error: getErrorMessage3(error45),
50233
+ mimeType,
50234
+ target
50235
+ });
50236
+ return null;
50237
+ }
50238
+ }
50239
+
50240
+ // src/shared/session-model-state.ts
50241
+ var sessionModels = new Map;
50242
+ function setSessionModel(sessionID, model) {
50243
+ sessionModels.set(sessionID, model);
50244
+ }
50245
+ function getSessionModel(sessionID) {
50246
+ return sessionModels.get(sessionID);
50247
+ }
50248
+ function clearSessionModel(sessionID) {
50249
+ sessionModels.delete(sessionID);
50250
+ }
50251
+
50252
+ // src/hooks/read-image-resizer/hook.ts
50253
+ var SUPPORTED_IMAGE_MIMES = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
50254
+ var TOKEN_DIVISOR = 750;
50255
+ function isReadTool2(toolName) {
50256
+ return toolName.toLowerCase() === "read";
50257
+ }
50258
+ function asRecord2(value) {
50259
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
50260
+ return null;
50261
+ }
50262
+ return value;
50263
+ }
50264
+ function isImageAttachmentRecord(value) {
50265
+ const filename = value.filename;
50266
+ return typeof value.mime === "string" && typeof value.url === "string" && (typeof filename === "undefined" || typeof filename === "string");
50267
+ }
50268
+ function extractImageAttachments(output) {
50269
+ const attachmentsValue = output.attachments;
50270
+ if (!Array.isArray(attachmentsValue)) {
50271
+ return [];
50272
+ }
50273
+ const attachments = [];
50274
+ for (const attachmentValue of attachmentsValue) {
50275
+ const attachmentRecord = asRecord2(attachmentValue);
50276
+ if (!attachmentRecord) {
50277
+ continue;
50278
+ }
50279
+ const mime = attachmentRecord.mime;
50280
+ const url2 = attachmentRecord.url;
50281
+ if (typeof mime !== "string" || typeof url2 !== "string") {
50282
+ continue;
50283
+ }
50284
+ const normalizedMime = mime.toLowerCase();
50285
+ if (!SUPPORTED_IMAGE_MIMES.has(normalizedMime)) {
50286
+ continue;
50287
+ }
50288
+ attachmentRecord.mime = normalizedMime;
50289
+ attachmentRecord.url = url2;
50290
+ if (isImageAttachmentRecord(attachmentRecord)) {
50291
+ attachments.push(attachmentRecord);
50292
+ }
50293
+ }
50294
+ return attachments;
50295
+ }
50296
+ function calculateTokens(width, height) {
50297
+ return Math.ceil(width * height / TOKEN_DIVISOR);
50298
+ }
50299
+ function formatResizeAppendix(entries) {
50300
+ const header = entries.some((entry) => entry.status === "resized") ? "[Image Resize Info]" : "[Image Info]";
50301
+ const lines = [`
50302
+
50303
+ ${header}`];
50304
+ for (const entry of entries) {
50305
+ if (entry.status === "unknown-dims" || !entry.originalDims) {
50306
+ lines.push(`- ${entry.filename}: dimensions could not be parsed`);
50307
+ continue;
50308
+ }
50309
+ const original = entry.originalDims;
50310
+ const originalText = `${original.width}x${original.height}`;
50311
+ const originalTokens = calculateTokens(original.width, original.height);
50312
+ if (entry.status === "within-limits") {
50313
+ lines.push(`- ${entry.filename}: ${originalText} (within limits, tokens: ${originalTokens})`);
50314
+ continue;
50315
+ }
50316
+ if (entry.status === "resize-skipped") {
50317
+ lines.push(`- ${entry.filename}: ${originalText} (resize skipped, tokens: ${originalTokens})`);
50318
+ continue;
50319
+ }
50320
+ if (!entry.resizedDims) {
50321
+ lines.push(`- ${entry.filename}: ${originalText} (resize skipped, tokens: ${originalTokens})`);
50322
+ continue;
50323
+ }
50324
+ const resized = entry.resizedDims;
50325
+ const resizedText = `${resized.width}x${resized.height}`;
50326
+ const resizedTokens = calculateTokens(resized.width, resized.height);
50327
+ lines.push(`- ${entry.filename}: ${originalText} -> ${resizedText} (resized, tokens: ${originalTokens} -> ${resizedTokens})`);
50328
+ }
50329
+ return lines.join(`
50330
+ `);
50331
+ }
50332
+ function resolveFilename(attachment, index) {
50333
+ if (attachment.filename && attachment.filename.trim().length > 0) {
50334
+ return attachment.filename;
50335
+ }
50336
+ return `image-${index + 1}`;
50337
+ }
50338
+ function createReadImageResizerHook(_ctx) {
50339
+ return {
50340
+ "tool.execute.after": async (input, output) => {
50341
+ if (!isReadTool2(input.tool)) {
50342
+ return;
50343
+ }
50344
+ const sessionModel = getSessionModel(input.sessionID);
50345
+ if (sessionModel?.providerID !== "anthropic") {
50346
+ return;
50347
+ }
50348
+ if (typeof output.output !== "string") {
50349
+ return;
50350
+ }
50351
+ const outputRecord = output;
50352
+ const attachments = extractImageAttachments(outputRecord);
50353
+ if (attachments.length === 0) {
50354
+ return;
50355
+ }
50356
+ const entries = [];
50357
+ for (const [index, attachment] of attachments.entries()) {
50358
+ const filename = resolveFilename(attachment, index);
50359
+ try {
50360
+ const originalDims = parseImageDimensions(attachment.url, attachment.mime);
50361
+ if (!originalDims) {
50362
+ entries.push({ filename, originalDims: null, resizedDims: null, status: "unknown-dims" });
50363
+ continue;
50364
+ }
50365
+ const targetDims = calculateTargetDimensions(originalDims.width, originalDims.height);
50366
+ if (!targetDims) {
50367
+ entries.push({
50368
+ filename,
50369
+ originalDims,
50370
+ resizedDims: null,
50371
+ status: "within-limits"
50372
+ });
50373
+ continue;
50374
+ }
50375
+ const resizedResult = await resizeImage(attachment.url, attachment.mime, targetDims);
50376
+ if (!resizedResult) {
50377
+ entries.push({
50378
+ filename,
50379
+ originalDims,
50380
+ resizedDims: null,
50381
+ status: "resize-skipped"
50382
+ });
50383
+ continue;
50384
+ }
50385
+ attachment.url = resizedResult.resizedDataUrl;
50386
+ entries.push({
50387
+ filename,
50388
+ originalDims: resizedResult.original,
50389
+ resizedDims: resizedResult.resized,
50390
+ status: "resized"
50391
+ });
50392
+ } catch (error45) {
50393
+ log("[read-image-resizer] attachment processing failed", {
50394
+ error: error45 instanceof Error ? error45.message : String(error45),
50395
+ filename
50396
+ });
50397
+ entries.push({ filename, originalDims: null, resizedDims: null, status: "unknown-dims" });
50398
+ }
50399
+ }
50400
+ if (entries.length === 0) {
50401
+ return;
50402
+ }
50403
+ output.output += formatResizeAppendix(entries);
50404
+ }
50405
+ };
50406
+ }
49538
50407
  // src/hooks/anthropic-effort/hook.ts
49539
50408
  var OPUS_4_6_PATTERN = /claude-opus-4[-.]6/i;
49540
50409
  function normalizeModelID2(modelID) {
@@ -50019,9 +50888,9 @@ function getLanguageId(ext) {
50019
50888
  }
50020
50889
  // src/tools/lsp/lsp-process.ts
50021
50890
  init_logger();
50022
- var {spawn: bunSpawn } = globalThis.Bun;
50023
- import { spawn as nodeSpawn } from "child_process";
50024
- import { existsSync as existsSync51, statSync as statSync6 } from "fs";
50891
+ var {spawn: bunSpawn2 } = globalThis.Bun;
50892
+ import { spawn as nodeSpawn2 } from "child_process";
50893
+ import { existsSync as existsSync51, statSync as statSync7 } from "fs";
50025
50894
  function shouldUseNodeSpawn() {
50026
50895
  return process.platform === "win32";
50027
50896
  }
@@ -50030,7 +50899,7 @@ function validateCwd(cwd) {
50030
50899
  if (!existsSync51(cwd)) {
50031
50900
  return { valid: false, error: `Working directory does not exist: ${cwd}` };
50032
50901
  }
50033
- const stats = statSync6(cwd);
50902
+ const stats = statSync7(cwd);
50034
50903
  if (!stats.isDirectory()) {
50035
50904
  return { valid: false, error: `Path is not a directory: ${cwd}` };
50036
50905
  }
@@ -50039,7 +50908,7 @@ function validateCwd(cwd) {
50039
50908
  return { valid: false, error: `Cannot access working directory: ${cwd} (${err instanceof Error ? err.message : String(err)})` };
50040
50909
  }
50041
50910
  }
50042
- function wrapNodeProcess(proc) {
50911
+ function wrapNodeProcess2(proc) {
50043
50912
  let resolveExited;
50044
50913
  let exitCode = null;
50045
50914
  const exitedPromise = new Promise((resolve8) => {
@@ -50140,16 +51009,16 @@ function spawnProcess(command, options) {
50140
51009
  if (shouldUseNodeSpawn()) {
50141
51010
  const [cmd, ...args] = command;
50142
51011
  log("[LSP] Using Node.js child_process on Windows to avoid Bun spawn segfault");
50143
- const proc2 = nodeSpawn(cmd, args, {
51012
+ const proc2 = nodeSpawn2(cmd, args, {
50144
51013
  cwd: options.cwd,
50145
51014
  env: options.env,
50146
51015
  stdio: ["pipe", "pipe", "pipe"],
50147
51016
  windowsHide: true,
50148
51017
  shell: true
50149
51018
  });
50150
- return wrapNodeProcess(proc2);
51019
+ return wrapNodeProcess2(proc2);
50151
51020
  }
50152
- const proc = bunSpawn(command, {
51021
+ const proc = bunSpawn2(command, {
50153
51022
  stdin: "pipe",
50154
51023
  stdout: "pipe",
50155
51024
  stderr: "pipe",
@@ -50160,7 +51029,7 @@ function spawnProcess(command, options) {
50160
51029
  }
50161
51030
  // src/tools/lsp/lsp-client.ts
50162
51031
  import { readFileSync as readFileSync38 } from "fs";
50163
- import { extname as extname2, resolve as resolve8 } from "path";
51032
+ import { extname as extname3, resolve as resolve8 } from "path";
50164
51033
  import { pathToFileURL as pathToFileURL2 } from "url";
50165
51034
 
50166
51035
  // src/tools/lsp/lsp-client-connection.ts
@@ -50168,7 +51037,7 @@ import { pathToFileURL } from "url";
50168
51037
 
50169
51038
  // src/tools/lsp/lsp-client-transport.ts
50170
51039
  var import_node = __toESM(require_main(), 1);
50171
- import { Readable, Writable } from "stream";
51040
+ import { Readable as Readable2, Writable } from "stream";
50172
51041
  init_logger();
50173
51042
 
50174
51043
  class LSPClientTransport {
@@ -50204,7 +51073,7 @@ class LSPClientTransport {
50204
51073
  stderr: ${stderr}` : ""));
50205
51074
  }
50206
51075
  const stdoutReader = this.proc.stdout.getReader();
50207
- const nodeReadable = new Readable({
51076
+ const nodeReadable = new Readable2({
50208
51077
  async read() {
50209
51078
  try {
50210
51079
  const { done, value } = await stdoutReader.read();
@@ -50425,7 +51294,7 @@ class LSPClient extends LSPClientConnection {
50425
51294
  const uri = pathToFileURL2(absPath).href;
50426
51295
  const text = readFileSync38(absPath, "utf-8");
50427
51296
  if (!this.openedFiles.has(absPath)) {
50428
- const ext = extname2(absPath);
51297
+ const ext = extname3(absPath);
50429
51298
  const languageId = getLanguageId(ext);
50430
51299
  const version2 = 1;
50431
51300
  this.sendNotification("textDocument/didOpen", {
@@ -50762,7 +51631,7 @@ class LSPServerManager {
50762
51631
  }
50763
51632
  var lspManager = LSPServerManager.getInstance();
50764
51633
  // src/tools/lsp/lsp-client-wrapper.ts
50765
- import { extname as extname3, resolve as resolve9 } from "path";
51634
+ import { extname as extname4, resolve as resolve9 } from "path";
50766
51635
  import { fileURLToPath as fileURLToPath3 } from "url";
50767
51636
  import { existsSync as existsSync52 } from "fs";
50768
51637
  function findWorkspaceRoot(filePath) {
@@ -50823,7 +51692,7 @@ function formatServerLookupError(result) {
50823
51692
  }
50824
51693
  async function withLspClient(filePath, fn) {
50825
51694
  const absPath = resolve9(filePath);
50826
- const ext = extname3(absPath);
51695
+ const ext = extname4(absPath);
50827
51696
  const result = findServerForExtension(ext);
50828
51697
  if (result.status !== "found") {
50829
51698
  throw new Error(formatServerLookupError(result));
@@ -51301,7 +52170,7 @@ var DEFAULT_MAX_MATCHES = 500;
51301
52170
  // src/tools/ast-grep/sg-cli-path.ts
51302
52171
  import { createRequire as createRequire4 } from "module";
51303
52172
  import { dirname as dirname16, join as join63 } from "path";
51304
- import { existsSync as existsSync54, statSync as statSync7 } from "fs";
52173
+ import { existsSync as existsSync54, statSync as statSync8 } from "fs";
51305
52174
 
51306
52175
  // src/tools/ast-grep/downloader.ts
51307
52176
  import { existsSync as existsSync53 } from "fs";
@@ -51388,7 +52257,7 @@ async function ensureAstGrepBinary() {
51388
52257
  // src/tools/ast-grep/sg-cli-path.ts
51389
52258
  function isValidBinary(filePath) {
51390
52259
  try {
51391
- return statSync7(filePath).size > 1e4;
52260
+ return statSync8(filePath).size > 1e4;
51392
52261
  } catch {
51393
52262
  return false;
51394
52263
  }
@@ -51823,6 +52692,9 @@ ${hint}`;
51823
52692
  });
51824
52693
  return { ast_grep_search, ast_grep_replace };
51825
52694
  }
52695
+ // src/tools/grep/tools.ts
52696
+ import { resolve as resolve10 } from "path";
52697
+
51826
52698
  // src/tools/grep/cli.ts
51827
52699
  var {spawn: spawn11 } = globalThis.Bun;
51828
52700
 
@@ -52333,10 +53205,12 @@ function createGrepTools(ctx) {
52333
53205
  output_mode: tool.schema.enum(["content", "files_with_matches", "count"]).optional().describe('Output mode: "content" shows matching lines, "files_with_matches" shows only file paths (default), "count" shows match counts per file.'),
52334
53206
  head_limit: tool.schema.number().optional().describe("Limit output to first N entries. 0 or omitted means no limit.")
52335
53207
  },
52336
- execute: async (args) => {
53208
+ execute: async (args, context) => {
52337
53209
  try {
52338
53210
  const globs = args.include ? [args.include] : undefined;
52339
- const searchPath = args.path ?? ctx.directory;
53211
+ const runtimeCtx = context;
53212
+ const dir = typeof runtimeCtx.directory === "string" ? runtimeCtx.directory : ctx.directory;
53213
+ const searchPath = args.path ? resolve10(dir, args.path) : dir;
52340
53214
  const paths = [searchPath];
52341
53215
  const outputMode = args.output_mode ?? "files_with_matches";
52342
53216
  const headLimit = args.head_limit ?? 0;
@@ -52365,7 +53239,11 @@ function createGrepTools(ctx) {
52365
53239
  });
52366
53240
  return { grep };
52367
53241
  }
53242
+ // src/tools/glob/tools.ts
53243
+ import { resolve as resolve12 } from "path";
53244
+
52368
53245
  // src/tools/glob/cli.ts
53246
+ import { resolve as resolve11 } from "path";
52369
53247
  var {spawn: spawn12 } = globalThis.Bun;
52370
53248
 
52371
53249
  // src/tools/glob/constants.ts
@@ -52450,10 +53328,9 @@ async function runRgFilesInternal(options, resolvedCli) {
52450
53328
  let cwd;
52451
53329
  if (isRg) {
52452
53330
  const args = buildRgArgs2(options);
52453
- const paths = options.paths?.length ? options.paths : ["."];
52454
- args.push(...paths);
53331
+ cwd = options.paths?.[0] || ".";
53332
+ args.push(".");
52455
53333
  command = [cli.path, ...args];
52456
- cwd = undefined;
52457
53334
  } else if (isWindows2) {
52458
53335
  command = buildPowerShellCommand(options);
52459
53336
  cwd = undefined;
@@ -52500,7 +53377,7 @@ async function runRgFilesInternal(options, resolvedCli) {
52500
53377
  }
52501
53378
  let filePath;
52502
53379
  if (isRg) {
52503
- filePath = line;
53380
+ filePath = cwd ? resolve11(cwd, line) : line;
52504
53381
  } else if (isWindows2) {
52505
53382
  filePath = line.trim();
52506
53383
  } else {
@@ -52555,14 +53432,15 @@ function createGlobTools(ctx) {
52555
53432
  pattern: tool.schema.string().describe("The glob pattern to match files against"),
52556
53433
  path: tool.schema.string().optional().describe("The directory to search in. If not specified, the current working directory will be used. " + 'IMPORTANT: Omit this field to use the default directory. DO NOT enter "undefined" or "null" - ' + "simply omit it for the default behavior. Must be a valid directory path if provided.")
52557
53434
  },
52558
- execute: async (args) => {
53435
+ execute: async (args, context) => {
52559
53436
  try {
52560
53437
  const cli = await resolveGrepCliWithAutoInstall();
52561
- const searchPath = args.path ?? ctx.directory;
52562
- const paths = [searchPath];
53438
+ const runtimeCtx = context;
53439
+ const dir = typeof runtimeCtx.directory === "string" ? runtimeCtx.directory : ctx.directory;
53440
+ const searchPath = args.path ? resolve12(dir, args.path) : dir;
52563
53441
  const result = await runRgFiles({
52564
53442
  pattern: args.pattern,
52565
- paths
53443
+ paths: [searchPath]
52566
53444
  }, cli);
52567
53445
  return formatGlobResult(result);
52568
53446
  } catch (e) {
@@ -53470,7 +54348,7 @@ async function searchInSession(sessionID, query, caseSensitive = false, maxResul
53470
54348
  // src/tools/session-manager/tools.ts
53471
54349
  var SEARCH_TIMEOUT_MS = 60000;
53472
54350
  var MAX_SESSIONS_TO_SCAN = 50;
53473
- function withTimeout2(promise2, ms, operation) {
54351
+ function withTimeout3(promise2, ms, operation) {
53474
54352
  return Promise.race([
53475
54353
  promise2,
53476
54354
  new Promise((_, reject) => setTimeout(() => reject(new Error(`${operation} timed out after ${ms}ms`)), ms))
@@ -53557,7 +54435,7 @@ function createSessionManagerTools(ctx) {
53557
54435
  }
53558
54436
  return allResults.slice(0, resultLimit);
53559
54437
  };
53560
- const results = await withTimeout2(searchOperation(), SEARCH_TIMEOUT_MS, "Search");
54438
+ const results = await withTimeout3(searchOperation(), SEARCH_TIMEOUT_MS, "Search");
53561
54439
  return formatSearchResults(results);
53562
54440
  } catch (e) {
53563
54441
  return `Error: ${e instanceof Error ? e.message : String(e)}`;
@@ -53675,7 +54553,7 @@ tmux capture-pane -p -t ${sessionName} -S -1000
53675
54553
 
53676
54554
  The Bash tool can execute these commands directly. Do NOT retry with interactive_bash.`;
53677
54555
  }
53678
- const proc = Bun.spawn([tmuxPath2, ...parts], {
54556
+ const proc = spawnWithWindowsHide([tmuxPath2, ...parts], {
53679
54557
  stdout: "pipe",
53680
54558
  stderr: "pipe"
53681
54559
  });
@@ -53893,10 +54771,10 @@ init_logger();
53893
54771
 
53894
54772
  // src/tools/background-task/delay.ts
53895
54773
  function delay3(ms) {
53896
- return new Promise((resolve10) => setTimeout(resolve10, ms));
54774
+ return new Promise((resolve13) => setTimeout(resolve13, ms));
53897
54775
  }
53898
54776
  // src/tools/background-task/session-messages.ts
53899
- function getErrorMessage3(value) {
54777
+ function getErrorMessage4(value) {
53900
54778
  if (Array.isArray(value))
53901
54779
  return null;
53902
54780
  if (value.error === undefined || value.error === null)
@@ -54049,7 +54927,7 @@ async function formatFullSession(task, client2, options) {
54049
54927
  const messagesResult = await client2.session.messages({
54050
54928
  path: { id: task.sessionID }
54051
54929
  });
54052
- const errorMessage = getErrorMessage3(messagesResult);
54930
+ const errorMessage = getErrorMessage4(messagesResult);
54053
54931
  if (errorMessage) {
54054
54932
  return `Error fetching messages: ${errorMessage}`;
54055
54933
  }
@@ -54147,7 +55025,7 @@ async function formatTaskResult(task, client2) {
54147
55025
  const messagesResult = await client2.session.messages({
54148
55026
  path: { id: task.sessionID }
54149
55027
  });
54150
- const errorMessage = getErrorMessage3(messagesResult);
55028
+ const errorMessage = getErrorMessage4(messagesResult);
54151
55029
  if (errorMessage) {
54152
55030
  return `Error fetching messages: ${errorMessage}`;
54153
55031
  }
@@ -54250,6 +55128,14 @@ function formatResolvedTitle(task) {
54250
55128
  const label = task.agent === SISYPHUS_JUNIOR_AGENT && task.category ? task.category : task.agent;
54251
55129
  return `${label} - ${task.description}`;
54252
55130
  }
55131
+ function isTaskActiveStatus(status) {
55132
+ return status === "pending" || status === "running";
55133
+ }
55134
+ function appendTimeoutNote(output, timeoutMs) {
55135
+ return `${output}
55136
+
55137
+ > **Timed out waiting** after ${timeoutMs}ms. Task is still running; showing latest available output.`;
55138
+ }
54253
55139
  function createBackgroundOutput(manager, client2) {
54254
55140
  return tool({
54255
55141
  description: BACKGROUND_OUTPUT_DESCRIPTION,
@@ -54290,7 +55176,8 @@ function createBackgroundOutput(manager, client2) {
54290
55176
  const timeoutMs = Math.min(args.timeout ?? 60000, 600000);
54291
55177
  const fullSession = args.full_session ?? true;
54292
55178
  let resolvedTask = task;
54293
- if (shouldBlock && (task.status === "pending" || task.status === "running")) {
55179
+ let didTimeoutWhileActive = false;
55180
+ if (shouldBlock && isTaskActiveStatus(task.status)) {
54294
55181
  const startTime = Date.now();
54295
55182
  while (Date.now() - startTime < timeoutMs) {
54296
55183
  await delay3(1000);
@@ -54298,27 +55185,33 @@ function createBackgroundOutput(manager, client2) {
54298
55185
  if (!currentTask) {
54299
55186
  return `Task was deleted: ${args.task_id}`;
54300
55187
  }
54301
- if (currentTask.status !== "pending" && currentTask.status !== "running") {
54302
- resolvedTask = currentTask;
55188
+ resolvedTask = currentTask;
55189
+ if (!isTaskActiveStatus(currentTask.status)) {
54303
55190
  break;
54304
55191
  }
54305
55192
  }
54306
- const finalCheck = manager.getTask(args.task_id);
54307
- if (finalCheck) {
54308
- resolvedTask = finalCheck;
55193
+ if (isTaskActiveStatus(resolvedTask.status)) {
55194
+ const finalCheck = manager.getTask(args.task_id);
55195
+ if (finalCheck) {
55196
+ resolvedTask = finalCheck;
55197
+ }
55198
+ }
55199
+ if (isTaskActiveStatus(resolvedTask.status)) {
55200
+ didTimeoutWhileActive = true;
54309
55201
  }
54310
55202
  }
54311
- const isActive = resolvedTask.status === "pending" || resolvedTask.status === "running";
55203
+ const isActive = isTaskActiveStatus(resolvedTask.status);
54312
55204
  const includeThinking = isActive || (args.include_thinking ?? false);
54313
55205
  const includeToolResults = isActive || (args.include_tool_results ?? false);
54314
55206
  if (fullSession) {
54315
- return await formatFullSession(resolvedTask, client2, {
55207
+ const output = await formatFullSession(resolvedTask, client2, {
54316
55208
  includeThinking,
54317
55209
  messageLimit: args.message_limit,
54318
55210
  sinceMessageId: args.since_message_id,
54319
55211
  includeToolResults,
54320
55212
  thinkingMaxChars: args.thinking_max_chars
54321
55213
  });
55214
+ return didTimeoutWhileActive ? appendTimeoutNote(output, timeoutMs) : output;
54322
55215
  }
54323
55216
  if (resolvedTask.status === "completed") {
54324
55217
  return await formatTaskResult(resolvedTask, client2);
@@ -54326,7 +55219,8 @@ function createBackgroundOutput(manager, client2) {
54326
55219
  if (resolvedTask.status === "error" || resolvedTask.status === "cancelled" || resolvedTask.status === "interrupt") {
54327
55220
  return formatTaskStatus(resolvedTask);
54328
55221
  }
54329
- return formatTaskStatus(resolvedTask);
55222
+ const statusOutput = formatTaskStatus(resolvedTask);
55223
+ return didTimeoutWhileActive ? appendTimeoutNote(statusOutput, timeoutMs) : statusOutput;
54330
55224
  } catch (error45) {
54331
55225
  return `Error getting output: ${error45 instanceof Error ? error45.message : String(error45)}`;
54332
55226
  }
@@ -54482,7 +55376,7 @@ Task ID: ${task.id}`;
54482
55376
 
54483
55377
  Task ID: ${task.id}`;
54484
55378
  }
54485
- await new Promise((resolve10) => setTimeout(resolve10, WAIT_FOR_SESSION_INTERVAL_MS));
55379
+ await new Promise((resolve13) => setTimeout(resolve13, WAIT_FOR_SESSION_INTERVAL_MS));
54486
55380
  sessionId = manager.getTask(task.id)?.sessionID;
54487
55381
  }
54488
55382
  await toolContext.metadata?.({
@@ -54575,7 +55469,7 @@ async function waitForCompletion(sessionID, toolContext, ctx) {
54575
55469
  log(`[call_omo_agent] Aborted by user`);
54576
55470
  throw new Error("Task aborted.");
54577
55471
  }
54578
- await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS));
55472
+ await new Promise((resolve13) => setTimeout(resolve13, POLL_INTERVAL_MS));
54579
55473
  const statusResult = await ctx.client.session.status();
54580
55474
  const allStatuses = normalizeSDKResponse(statusResult, {});
54581
55475
  const sessionStatus = allStatuses[sessionID];
@@ -54822,77 +55716,6 @@ function validateArgs(args) {
54822
55716
  return null;
54823
55717
  }
54824
55718
 
54825
- // src/tools/look-at/mime-type-inference.ts
54826
- import { extname as extname4 } from "path";
54827
- function inferMimeTypeFromBase64(base64Data) {
54828
- if (base64Data.startsWith("data:")) {
54829
- const match = base64Data.match(/^data:([^;]+);/);
54830
- if (match)
54831
- return match[1];
54832
- }
54833
- try {
54834
- const cleanData = base64Data.replace(/^data:[^;]+;base64,/, "");
54835
- const header = atob(cleanData.slice(0, 16));
54836
- if (header.startsWith("\x89PNG"))
54837
- return "image/png";
54838
- if (header.startsWith("\xFF\xD8\xFF"))
54839
- return "image/jpeg";
54840
- if (header.startsWith("GIF8"))
54841
- return "image/gif";
54842
- if (header.startsWith("RIFF") && header.includes("WEBP"))
54843
- return "image/webp";
54844
- if (header.startsWith("%PDF"))
54845
- return "application/pdf";
54846
- } catch {}
54847
- return "image/png";
54848
- }
54849
- function inferMimeTypeFromFilePath(filePath) {
54850
- const ext = extname4(filePath).toLowerCase();
54851
- const mimeTypes = {
54852
- ".jpg": "image/jpeg",
54853
- ".jpeg": "image/jpeg",
54854
- ".png": "image/png",
54855
- ".webp": "image/webp",
54856
- ".heic": "image/heic",
54857
- ".heif": "image/heif",
54858
- ".mp4": "video/mp4",
54859
- ".mpeg": "video/mpeg",
54860
- ".mpg": "video/mpeg",
54861
- ".mov": "video/mov",
54862
- ".avi": "video/avi",
54863
- ".flv": "video/x-flv",
54864
- ".webm": "video/webm",
54865
- ".wmv": "video/wmv",
54866
- ".3gpp": "video/3gpp",
54867
- ".3gp": "video/3gpp",
54868
- ".wav": "audio/wav",
54869
- ".mp3": "audio/mp3",
54870
- ".aiff": "audio/aiff",
54871
- ".aac": "audio/aac",
54872
- ".ogg": "audio/ogg",
54873
- ".flac": "audio/flac",
54874
- ".pdf": "application/pdf",
54875
- ".txt": "text/plain",
54876
- ".csv": "text/csv",
54877
- ".md": "text/md",
54878
- ".html": "text/html",
54879
- ".json": "application/json",
54880
- ".xml": "application/xml",
54881
- ".js": "text/javascript",
54882
- ".py": "text/x-python"
54883
- };
54884
- return mimeTypes[ext] || "application/octet-stream";
54885
- }
54886
- function extractBase64Data(imageData) {
54887
- if (imageData.startsWith("data:")) {
54888
- const commaIndex = imageData.indexOf(",");
54889
- if (commaIndex !== -1) {
54890
- return imageData.slice(commaIndex + 1);
54891
- }
54892
- }
54893
- return imageData;
54894
- }
54895
-
54896
55719
  // src/tools/look-at/multimodal-agent-metadata.ts
54897
55720
  function isObject4(value) {
54898
55721
  return typeof value === "object" && value !== null;
@@ -55062,47 +55885,153 @@ init_logger();
55062
55885
 
55063
55886
  // src/tools/delegate-task/prompt-builder.ts
55064
55887
  init_constants();
55888
+
55889
+ // src/tools/delegate-task/token-limiter.ts
55890
+ var CHARACTERS_PER_TOKEN = 4;
55891
+ function estimateTokenCount(text) {
55892
+ if (!text) {
55893
+ return 0;
55894
+ }
55895
+ return Math.ceil(text.length / CHARACTERS_PER_TOKEN);
55896
+ }
55897
+ function truncateToTokenBudget(content, maxTokens) {
55898
+ if (!content || maxTokens <= 0) {
55899
+ return "";
55900
+ }
55901
+ const maxCharacters = maxTokens * CHARACTERS_PER_TOKEN;
55902
+ if (content.length <= maxCharacters) {
55903
+ return content;
55904
+ }
55905
+ const sliced = content.slice(0, maxCharacters);
55906
+ const lastNewline = sliced.lastIndexOf(`
55907
+ `);
55908
+ if (lastNewline > 0) {
55909
+ return `${sliced.slice(0, lastNewline)}
55910
+ [TRUNCATED]`;
55911
+ }
55912
+ return `${sliced}
55913
+ [TRUNCATED]`;
55914
+ }
55915
+ function joinSystemParts(parts) {
55916
+ const filtered = parts.filter((part) => part.trim().length > 0);
55917
+ if (filtered.length === 0) {
55918
+ return;
55919
+ }
55920
+ return filtered.join(`
55921
+
55922
+ `);
55923
+ }
55924
+ function reduceSegmentToFitBudget(content, overflowTokens) {
55925
+ if (overflowTokens <= 0 || !content) {
55926
+ return content;
55927
+ }
55928
+ const currentTokens = estimateTokenCount(content);
55929
+ const nextBudget = Math.max(0, currentTokens - overflowTokens);
55930
+ return truncateToTokenBudget(content, nextBudget);
55931
+ }
55932
+ function buildSystemContentWithTokenLimit(input, maxTokens) {
55933
+ const skillParts = input.skillContents?.length ? [...input.skillContents] : input.skillContent ? [input.skillContent] : [];
55934
+ const categoryPromptAppend = input.categoryPromptAppend ?? "";
55935
+ const agentsContext = input.agentsContext ?? input.planAgentPrepend ?? "";
55936
+ if (maxTokens === undefined) {
55937
+ return joinSystemParts([agentsContext, ...skillParts, categoryPromptAppend]);
55938
+ }
55939
+ let nextSkills = [...skillParts];
55940
+ let nextCategoryPromptAppend = categoryPromptAppend;
55941
+ let nextAgentsContext = agentsContext;
55942
+ const buildCurrentContent = () => joinSystemParts([nextAgentsContext, ...nextSkills, nextCategoryPromptAppend]);
55943
+ let systemContent = buildCurrentContent();
55944
+ if (!systemContent) {
55945
+ return;
55946
+ }
55947
+ let overflowTokens = estimateTokenCount(systemContent) - maxTokens;
55948
+ if (overflowTokens > 0) {
55949
+ for (let index = 0;index < nextSkills.length && overflowTokens > 0; index += 1) {
55950
+ const skill2 = nextSkills[index];
55951
+ const reducedSkill = reduceSegmentToFitBudget(skill2, overflowTokens);
55952
+ nextSkills[index] = reducedSkill;
55953
+ systemContent = buildCurrentContent();
55954
+ if (!systemContent) {
55955
+ return;
55956
+ }
55957
+ overflowTokens = estimateTokenCount(systemContent) - maxTokens;
55958
+ }
55959
+ nextSkills = nextSkills.filter((skill2) => skill2.trim().length > 0);
55960
+ systemContent = buildCurrentContent();
55961
+ if (!systemContent) {
55962
+ return;
55963
+ }
55964
+ overflowTokens = estimateTokenCount(systemContent) - maxTokens;
55965
+ }
55966
+ if (overflowTokens > 0 && nextCategoryPromptAppend) {
55967
+ nextCategoryPromptAppend = reduceSegmentToFitBudget(nextCategoryPromptAppend, overflowTokens);
55968
+ systemContent = buildCurrentContent();
55969
+ if (!systemContent) {
55970
+ return;
55971
+ }
55972
+ overflowTokens = estimateTokenCount(systemContent) - maxTokens;
55973
+ }
55974
+ if (overflowTokens > 0 && nextAgentsContext) {
55975
+ nextAgentsContext = reduceSegmentToFitBudget(nextAgentsContext, overflowTokens);
55976
+ systemContent = buildCurrentContent();
55977
+ if (!systemContent) {
55978
+ return;
55979
+ }
55980
+ }
55981
+ if (!systemContent) {
55982
+ return;
55983
+ }
55984
+ return truncateToTokenBudget(systemContent, maxTokens);
55985
+ }
55986
+
55987
+ // src/tools/delegate-task/prompt-builder.ts
55988
+ var FREE_OR_LOCAL_PROMPT_TOKEN_LIMIT = 24000;
55989
+ function usesFreeOrLocalModel(model) {
55990
+ if (!model) {
55991
+ return false;
55992
+ }
55993
+ const provider = model.providerID.toLowerCase();
55994
+ const modelId = model.modelID.toLowerCase();
55995
+ return provider.includes("local") || provider === "ollama" || provider === "lmstudio" || modelId.includes("free");
55996
+ }
55065
55997
  function buildSystemContent(input) {
55066
55998
  const {
55067
55999
  skillContent,
56000
+ skillContents,
55068
56001
  categoryPromptAppend,
56002
+ agentsContext,
56003
+ maxPromptTokens,
56004
+ model,
55069
56005
  agentName,
55070
56006
  availableCategories,
55071
56007
  availableSkills
55072
56008
  } = input;
55073
56009
  const planAgentPrepend = isPlanAgent(agentName) ? buildPlanAgentSystemPrepend(availableCategories, availableSkills) : "";
55074
- if (!skillContent && !categoryPromptAppend && !planAgentPrepend) {
55075
- return;
55076
- }
55077
- const parts = [];
55078
- if (planAgentPrepend) {
55079
- parts.push(planAgentPrepend);
55080
- }
55081
- if (skillContent) {
55082
- parts.push(skillContent);
55083
- }
55084
- if (categoryPromptAppend) {
55085
- parts.push(categoryPromptAppend);
55086
- }
55087
- return parts.join(`
55088
-
55089
- `) || undefined;
56010
+ const effectiveMaxPromptTokens = maxPromptTokens ?? (usesFreeOrLocalModel(model) ? FREE_OR_LOCAL_PROMPT_TOKEN_LIMIT : undefined);
56011
+ return buildSystemContentWithTokenLimit({
56012
+ skillContent,
56013
+ skillContents,
56014
+ categoryPromptAppend,
56015
+ agentsContext: agentsContext ?? planAgentPrepend,
56016
+ planAgentPrepend
56017
+ }, effectiveMaxPromptTokens);
55090
56018
  }
55091
56019
 
55092
56020
  // src/tools/delegate-task/skill-resolver.ts
55093
56021
  async function resolveSkillContent2(skills2, options) {
55094
56022
  if (skills2.length === 0) {
55095
- return { content: undefined, error: null };
56023
+ return { content: undefined, contents: [], error: null };
55096
56024
  }
55097
56025
  const { resolved, notFound } = await resolveMultipleSkillsAsync(skills2, options);
55098
56026
  if (notFound.length > 0) {
55099
56027
  const allSkills = await discoverSkills({ includeClaudeCodePaths: true, directory: options?.directory });
55100
56028
  const available = allSkills.map((s) => s.name).join(", ");
55101
- return { content: undefined, error: `Skills not found: ${notFound.join(", ")}. Available: ${available}` };
56029
+ return { content: undefined, contents: [], error: `Skills not found: ${notFound.join(", ")}. Available: ${available}` };
55102
56030
  }
55103
- return { content: Array.from(resolved.values()).join(`
56031
+ const contents = Array.from(resolved.values());
56032
+ return { content: contents.join(`
55104
56033
 
55105
- `), error: null };
56034
+ `), contents, error: null };
55106
56035
  }
55107
56036
  // src/tools/delegate-task/parent-context-resolver.ts
55108
56037
  init_logger();
@@ -55292,7 +56221,7 @@ async function pollSyncSession(ctx, client2, input) {
55292
56221
 
55293
56222
  Session ID: ${input.sessionID}`;
55294
56223
  }
55295
- await new Promise((resolve10) => setTimeout(resolve10, syncTiming.POLL_INTERVAL_MS));
56224
+ await new Promise((resolve13) => setTimeout(resolve13, syncTiming.POLL_INTERVAL_MS));
55296
56225
  pollCount++;
55297
56226
  let statusResult;
55298
56227
  try {
@@ -55554,7 +56483,7 @@ async function executeUnstableAgentTask(args, ctx, executorCtx, parentContext, a
55554
56483
 
55555
56484
  Task ID: ${task.id}`;
55556
56485
  }
55557
- await new Promise((resolve10) => setTimeout(resolve10, timing.WAIT_FOR_SESSION_INTERVAL_MS));
56486
+ await new Promise((resolve13) => setTimeout(resolve13, timing.WAIT_FOR_SESSION_INTERVAL_MS));
55558
56487
  const updated = manager.getTask(task.id);
55559
56488
  sessionID = updated?.sessionID;
55560
56489
  }
@@ -55595,7 +56524,7 @@ Task ID: ${task.id}`;
55595
56524
 
55596
56525
  Session ID: ${sessionID}`;
55597
56526
  }
55598
- await new Promise((resolve10) => setTimeout(resolve10, timingCfg.POLL_INTERVAL_MS));
56527
+ await new Promise((resolve13) => setTimeout(resolve13, timingCfg.POLL_INTERVAL_MS));
55599
56528
  const currentTask = manager.getTask(task.id);
55600
56529
  if (currentTask && (currentTask.status === "interrupt" || currentTask.status === "error" || currentTask.status === "cancelled")) {
55601
56530
  terminalStatus = { status: currentTask.status, error: currentTask.error };
@@ -55724,7 +56653,7 @@ async function executeBackgroundTask(args, ctx, executorCtx, parentContext, agen
55724
56653
 
55725
56654
  Task ID: ${task.id}`;
55726
56655
  }
55727
- await new Promise((resolve10) => setTimeout(resolve10, timing.WAIT_FOR_SESSION_INTERVAL_MS));
56656
+ await new Promise((resolve13) => setTimeout(resolve13, timing.WAIT_FOR_SESSION_INTERVAL_MS));
55728
56657
  const updated = manager.getTask(task.id);
55729
56658
  sessionId = updated?.sessionID;
55730
56659
  }
@@ -56173,6 +57102,7 @@ async function resolveCategoryExecution(args, executorCtx, inheritedModel, syste
56173
57102
  agentToUse: "",
56174
57103
  categoryModel: undefined,
56175
57104
  categoryPromptAppend: undefined,
57105
+ maxPromptTokens: undefined,
56176
57106
  modelInfo: undefined,
56177
57107
  actualModel: undefined,
56178
57108
  isUnstableAgent: false,
@@ -56189,6 +57119,7 @@ Available categories: ${allCategoryNames}`
56189
57119
  agentToUse: "",
56190
57120
  categoryModel: undefined,
56191
57121
  categoryPromptAppend: undefined,
57122
+ maxPromptTokens: undefined,
56192
57123
  modelInfo: undefined,
56193
57124
  actualModel: undefined,
56194
57125
  isUnstableAgent: false,
@@ -56222,6 +57153,7 @@ Available categories: ${allCategoryNames}`
56222
57153
  agentToUse: "",
56223
57154
  categoryModel: undefined,
56224
57155
  categoryPromptAppend: undefined,
57156
+ maxPromptTokens: undefined,
56225
57157
  modelInfo: undefined,
56226
57158
  actualModel: undefined,
56227
57159
  isUnstableAgent: false,
@@ -56247,6 +57179,7 @@ Available categories: ${allCategoryNames}`
56247
57179
  agentToUse: "",
56248
57180
  categoryModel: undefined,
56249
57181
  categoryPromptAppend: undefined,
57182
+ maxPromptTokens: undefined,
56250
57183
  modelInfo: undefined,
56251
57184
  actualModel: undefined,
56252
57185
  isUnstableAgent: false,
@@ -56268,6 +57201,7 @@ Available categories: ${categoryNames.join(", ")}`
56268
57201
  agentToUse: SISYPHUS_JUNIOR_AGENT2,
56269
57202
  categoryModel,
56270
57203
  categoryPromptAppend,
57204
+ maxPromptTokens: resolved.config.max_prompt_tokens,
56271
57205
  modelInfo,
56272
57206
  actualModel,
56273
57207
  isUnstableAgent,
@@ -56477,7 +57411,7 @@ function createDelegateTask(options) {
56477
57411
  throw new Error(`Invalid arguments: load_skills=null is not allowed. Pass [] if no skills needed.`);
56478
57412
  }
56479
57413
  const runInBackground = args.run_in_background === true;
56480
- const { content: skillContent, error: skillError } = await resolveSkillContent2(args.load_skills, {
57414
+ const { content: skillContent, contents: skillContents, error: skillError } = await resolveSkillContent2(args.load_skills, {
56481
57415
  gitMasterConfig: options.gitMasterConfig,
56482
57416
  browserProvider: options.browserProvider,
56483
57417
  disabledSkills: options.disabledSkills,
@@ -56511,6 +57445,7 @@ function createDelegateTask(options) {
56511
57445
  let actualModel;
56512
57446
  let isUnstableAgent = false;
56513
57447
  let fallbackChain;
57448
+ let maxPromptTokens;
56514
57449
  if (args.category) {
56515
57450
  const resolution = await resolveCategoryExecution(args, options, inheritedModel, systemDefaultModel);
56516
57451
  if (resolution.error) {
@@ -56523,6 +57458,7 @@ function createDelegateTask(options) {
56523
57458
  actualModel = resolution.actualModel;
56524
57459
  isUnstableAgent = resolution.isUnstableAgent;
56525
57460
  fallbackChain = resolution.fallbackChain;
57461
+ maxPromptTokens = resolution.maxPromptTokens;
56526
57462
  const isRunInBackgroundExplicitlyFalse = args.run_in_background === false || args.run_in_background === "false";
56527
57463
  log("[task] unstable agent detection", {
56528
57464
  category: args.category,
@@ -56536,8 +57472,11 @@ function createDelegateTask(options) {
56536
57472
  if (isUnstableAgent && isRunInBackgroundExplicitlyFalse) {
56537
57473
  const systemContent2 = buildSystemContent({
56538
57474
  skillContent,
57475
+ skillContents,
56539
57476
  categoryPromptAppend,
56540
57477
  agentName: agentToUse,
57478
+ maxPromptTokens,
57479
+ model: categoryModel,
56541
57480
  availableCategories,
56542
57481
  availableSkills
56543
57482
  });
@@ -56554,8 +57493,11 @@ function createDelegateTask(options) {
56554
57493
  }
56555
57494
  const systemContent = buildSystemContent({
56556
57495
  skillContent,
57496
+ skillContents,
56557
57497
  categoryPromptAppend,
56558
57498
  agentName: agentToUse,
57499
+ maxPromptTokens,
57500
+ model: categoryModel,
56559
57501
  availableCategories,
56560
57502
  availableSkills
56561
57503
  });
@@ -57356,14 +58298,19 @@ function normalizeEditPayload(payload) {
57356
58298
  return toNewLines(payload).join(`
57357
58299
  `);
57358
58300
  }
58301
+ function canonicalAnchor(anchor) {
58302
+ if (!anchor)
58303
+ return "";
58304
+ return normalizeLineRef(anchor);
58305
+ }
57359
58306
  function buildDedupeKey(edit) {
57360
58307
  switch (edit.op) {
57361
58308
  case "replace":
57362
- return `replace|${edit.pos}|${edit.end ?? ""}|${normalizeEditPayload(edit.lines)}`;
58309
+ return `replace|${canonicalAnchor(edit.pos)}|${edit.end ? canonicalAnchor(edit.end) : ""}|${normalizeEditPayload(edit.lines)}`;
57363
58310
  case "append":
57364
- return `append|${edit.pos ?? ""}|${normalizeEditPayload(edit.lines)}`;
58311
+ return `append|${canonicalAnchor(edit.pos)}|${normalizeEditPayload(edit.lines)}`;
57365
58312
  case "prepend":
57366
- return `prepend|${edit.pos ?? ""}|${normalizeEditPayload(edit.lines)}`;
58313
+ return `prepend|${canonicalAnchor(edit.pos)}|${normalizeEditPayload(edit.lines)}`;
57367
58314
  default:
57368
58315
  return JSON.stringify(edit);
57369
58316
  }
@@ -57748,66 +58695,448 @@ function applyHashlineEditsWithReport(content, edits) {
57748
58695
  deduplicatedEdits: dedupeResult.deduplicatedEdits
57749
58696
  };
57750
58697
  }
57751
- // src/tools/hashline-edit/diff-utils.ts
57752
- function generateUnifiedDiff(oldContent, newContent, filePath) {
57753
- const oldLines = oldContent.split(`
57754
- `);
57755
- const newLines = newContent.split(`
58698
+ // node_modules/diff/libesm/diff/base.js
58699
+ class Diff {
58700
+ diff(oldStr, newStr, options = {}) {
58701
+ let callback;
58702
+ if (typeof options === "function") {
58703
+ callback = options;
58704
+ options = {};
58705
+ } else if ("callback" in options) {
58706
+ callback = options.callback;
58707
+ }
58708
+ const oldString = this.castInput(oldStr, options);
58709
+ const newString = this.castInput(newStr, options);
58710
+ const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
58711
+ const newTokens = this.removeEmpty(this.tokenize(newString, options));
58712
+ return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
58713
+ }
58714
+ diffWithOptionsObj(oldTokens, newTokens, options, callback) {
58715
+ var _a;
58716
+ const done = (value) => {
58717
+ value = this.postProcess(value, options);
58718
+ if (callback) {
58719
+ setTimeout(function() {
58720
+ callback(value);
58721
+ }, 0);
58722
+ return;
58723
+ } else {
58724
+ return value;
58725
+ }
58726
+ };
58727
+ const newLen = newTokens.length, oldLen = oldTokens.length;
58728
+ let editLength = 1;
58729
+ let maxEditLength = newLen + oldLen;
58730
+ if (options.maxEditLength != null) {
58731
+ maxEditLength = Math.min(maxEditLength, options.maxEditLength);
58732
+ }
58733
+ const maxExecutionTime = (_a = options.timeout) !== null && _a !== undefined ? _a : Infinity;
58734
+ const abortAfterTimestamp = Date.now() + maxExecutionTime;
58735
+ const bestPath = [{ oldPos: -1, lastComponent: undefined }];
58736
+ let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
58737
+ if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
58738
+ return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
58739
+ }
58740
+ let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
58741
+ const execEditLength = () => {
58742
+ for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength);diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
58743
+ let basePath;
58744
+ const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
58745
+ if (removePath) {
58746
+ bestPath[diagonalPath - 1] = undefined;
58747
+ }
58748
+ let canAdd = false;
58749
+ if (addPath) {
58750
+ const addPathNewPos = addPath.oldPos - diagonalPath;
58751
+ canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
58752
+ }
58753
+ const canRemove = removePath && removePath.oldPos + 1 < oldLen;
58754
+ if (!canAdd && !canRemove) {
58755
+ bestPath[diagonalPath] = undefined;
58756
+ continue;
58757
+ }
58758
+ if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
58759
+ basePath = this.addToPath(addPath, true, false, 0, options);
58760
+ } else {
58761
+ basePath = this.addToPath(removePath, false, true, 1, options);
58762
+ }
58763
+ newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
58764
+ if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
58765
+ return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
58766
+ } else {
58767
+ bestPath[diagonalPath] = basePath;
58768
+ if (basePath.oldPos + 1 >= oldLen) {
58769
+ maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
58770
+ }
58771
+ if (newPos + 1 >= newLen) {
58772
+ minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
58773
+ }
58774
+ }
58775
+ }
58776
+ editLength++;
58777
+ };
58778
+ if (callback) {
58779
+ (function exec2() {
58780
+ setTimeout(function() {
58781
+ if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
58782
+ return callback(undefined);
58783
+ }
58784
+ if (!execEditLength()) {
58785
+ exec2();
58786
+ }
58787
+ }, 0);
58788
+ })();
58789
+ } else {
58790
+ while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
58791
+ const ret = execEditLength();
58792
+ if (ret) {
58793
+ return ret;
58794
+ }
58795
+ }
58796
+ }
58797
+ }
58798
+ addToPath(path11, added, removed, oldPosInc, options) {
58799
+ const last = path11.lastComponent;
58800
+ if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
58801
+ return {
58802
+ oldPos: path11.oldPos + oldPosInc,
58803
+ lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
58804
+ };
58805
+ } else {
58806
+ return {
58807
+ oldPos: path11.oldPos + oldPosInc,
58808
+ lastComponent: { count: 1, added, removed, previousComponent: last }
58809
+ };
58810
+ }
58811
+ }
58812
+ extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
58813
+ const newLen = newTokens.length, oldLen = oldTokens.length;
58814
+ let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
58815
+ while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
58816
+ newPos++;
58817
+ oldPos++;
58818
+ commonCount++;
58819
+ if (options.oneChangePerToken) {
58820
+ basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
58821
+ }
58822
+ }
58823
+ if (commonCount && !options.oneChangePerToken) {
58824
+ basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
58825
+ }
58826
+ basePath.oldPos = oldPos;
58827
+ return newPos;
58828
+ }
58829
+ equals(left, right, options) {
58830
+ if (options.comparator) {
58831
+ return options.comparator(left, right);
58832
+ } else {
58833
+ return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
58834
+ }
58835
+ }
58836
+ removeEmpty(array2) {
58837
+ const ret = [];
58838
+ for (let i2 = 0;i2 < array2.length; i2++) {
58839
+ if (array2[i2]) {
58840
+ ret.push(array2[i2]);
58841
+ }
58842
+ }
58843
+ return ret;
58844
+ }
58845
+ castInput(value, options) {
58846
+ return value;
58847
+ }
58848
+ tokenize(value, options) {
58849
+ return Array.from(value);
58850
+ }
58851
+ join(chars) {
58852
+ return chars.join("");
58853
+ }
58854
+ postProcess(changeObjects, options) {
58855
+ return changeObjects;
58856
+ }
58857
+ get useLongestToken() {
58858
+ return false;
58859
+ }
58860
+ buildValues(lastComponent, newTokens, oldTokens) {
58861
+ const components = [];
58862
+ let nextComponent;
58863
+ while (lastComponent) {
58864
+ components.push(lastComponent);
58865
+ nextComponent = lastComponent.previousComponent;
58866
+ delete lastComponent.previousComponent;
58867
+ lastComponent = nextComponent;
58868
+ }
58869
+ components.reverse();
58870
+ const componentLen = components.length;
58871
+ let componentPos = 0, newPos = 0, oldPos = 0;
58872
+ for (;componentPos < componentLen; componentPos++) {
58873
+ const component = components[componentPos];
58874
+ if (!component.removed) {
58875
+ if (!component.added && this.useLongestToken) {
58876
+ let value = newTokens.slice(newPos, newPos + component.count);
58877
+ value = value.map(function(value2, i2) {
58878
+ const oldValue = oldTokens[oldPos + i2];
58879
+ return oldValue.length > value2.length ? oldValue : value2;
58880
+ });
58881
+ component.value = this.join(value);
58882
+ } else {
58883
+ component.value = this.join(newTokens.slice(newPos, newPos + component.count));
58884
+ }
58885
+ newPos += component.count;
58886
+ if (!component.added) {
58887
+ oldPos += component.count;
58888
+ }
58889
+ } else {
58890
+ component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
58891
+ oldPos += component.count;
58892
+ }
58893
+ }
58894
+ return components;
58895
+ }
58896
+ }
58897
+
58898
+ // node_modules/diff/libesm/diff/line.js
58899
+ class LineDiff extends Diff {
58900
+ constructor() {
58901
+ super(...arguments);
58902
+ this.tokenize = tokenize;
58903
+ }
58904
+ equals(left, right, options) {
58905
+ if (options.ignoreWhitespace) {
58906
+ if (!options.newlineIsToken || !left.includes(`
58907
+ `)) {
58908
+ left = left.trim();
58909
+ }
58910
+ if (!options.newlineIsToken || !right.includes(`
58911
+ `)) {
58912
+ right = right.trim();
58913
+ }
58914
+ } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
58915
+ if (left.endsWith(`
58916
+ `)) {
58917
+ left = left.slice(0, -1);
58918
+ }
58919
+ if (right.endsWith(`
58920
+ `)) {
58921
+ right = right.slice(0, -1);
58922
+ }
58923
+ }
58924
+ return super.equals(left, right, options);
58925
+ }
58926
+ }
58927
+ var lineDiff = new LineDiff;
58928
+ function diffLines(oldStr, newStr, options) {
58929
+ return lineDiff.diff(oldStr, newStr, options);
58930
+ }
58931
+ function tokenize(value, options) {
58932
+ if (options.stripTrailingCr) {
58933
+ value = value.replace(/\r\n/g, `
57756
58934
  `);
57757
- const maxLines = Math.max(oldLines.length, newLines.length);
57758
- let diff = `--- ${filePath}
57759
- +++ ${filePath}
57760
- `;
57761
- let inHunk = false;
57762
- let oldStart = 1;
57763
- let newStart = 1;
57764
- let oldCount = 0;
57765
- let newCount = 0;
57766
- let hunkLines = [];
57767
- for (let i2 = 0;i2 < maxLines; i2++) {
57768
- const oldLine = oldLines[i2] ?? "";
57769
- const newLine = newLines[i2] ?? "";
57770
- if (oldLine !== newLine) {
57771
- if (!inHunk) {
57772
- oldStart = i2 + 1;
57773
- newStart = i2 + 1;
57774
- oldCount = 0;
57775
- newCount = 0;
57776
- hunkLines = [];
57777
- inHunk = true;
57778
- }
57779
- if (oldLines[i2] !== undefined) {
57780
- hunkLines.push(`-${oldLine}`);
57781
- oldCount++;
57782
- }
57783
- if (newLines[i2] !== undefined) {
57784
- hunkLines.push(`+${newLine}`);
57785
- newCount++;
57786
- }
57787
- } else if (inHunk) {
57788
- hunkLines.push(` ${oldLine}`);
57789
- oldCount++;
57790
- newCount++;
57791
- if (hunkLines.length > 6) {
57792
- diff += `@@ -${oldStart},${oldCount} +${newStart},${newCount} @@
57793
- `;
57794
- diff += hunkLines.join(`
57795
- `) + `
57796
- `;
57797
- inHunk = false;
58935
+ }
58936
+ const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
58937
+ if (!linesAndNewlines[linesAndNewlines.length - 1]) {
58938
+ linesAndNewlines.pop();
58939
+ }
58940
+ for (let i2 = 0;i2 < linesAndNewlines.length; i2++) {
58941
+ const line = linesAndNewlines[i2];
58942
+ if (i2 % 2 && !options.newlineIsToken) {
58943
+ retLines[retLines.length - 1] += line;
58944
+ } else {
58945
+ retLines.push(line);
58946
+ }
58947
+ }
58948
+ return retLines;
58949
+ }
58950
+
58951
+ // node_modules/diff/libesm/patch/create.js
58952
+ var INCLUDE_HEADERS = {
58953
+ includeIndex: true,
58954
+ includeUnderline: true,
58955
+ includeFileHeaders: true
58956
+ };
58957
+ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
58958
+ let optionsObj;
58959
+ if (!options) {
58960
+ optionsObj = {};
58961
+ } else if (typeof options === "function") {
58962
+ optionsObj = { callback: options };
58963
+ } else {
58964
+ optionsObj = options;
58965
+ }
58966
+ if (typeof optionsObj.context === "undefined") {
58967
+ optionsObj.context = 4;
58968
+ }
58969
+ const context = optionsObj.context;
58970
+ if (optionsObj.newlineIsToken) {
58971
+ throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
58972
+ }
58973
+ if (!optionsObj.callback) {
58974
+ return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
58975
+ } else {
58976
+ const { callback } = optionsObj;
58977
+ diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
58978
+ const patch = diffLinesResultToPatch(diff);
58979
+ callback(patch);
58980
+ } }));
58981
+ }
58982
+ function diffLinesResultToPatch(diff) {
58983
+ if (!diff) {
58984
+ return;
58985
+ }
58986
+ diff.push({ value: "", lines: [] });
58987
+ function contextLines(lines) {
58988
+ return lines.map(function(entry) {
58989
+ return " " + entry;
58990
+ });
58991
+ }
58992
+ const hunks = [];
58993
+ let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
58994
+ for (let i2 = 0;i2 < diff.length; i2++) {
58995
+ const current = diff[i2], lines = current.lines || splitLines(current.value);
58996
+ current.lines = lines;
58997
+ if (current.added || current.removed) {
58998
+ if (!oldRangeStart) {
58999
+ const prev = diff[i2 - 1];
59000
+ oldRangeStart = oldLine;
59001
+ newRangeStart = newLine;
59002
+ if (prev) {
59003
+ curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
59004
+ oldRangeStart -= curRange.length;
59005
+ newRangeStart -= curRange.length;
59006
+ }
59007
+ }
59008
+ for (const line of lines) {
59009
+ curRange.push((current.added ? "+" : "-") + line);
59010
+ }
59011
+ if (current.added) {
59012
+ newLine += lines.length;
59013
+ } else {
59014
+ oldLine += lines.length;
59015
+ }
59016
+ } else {
59017
+ if (oldRangeStart) {
59018
+ if (lines.length <= context * 2 && i2 < diff.length - 2) {
59019
+ for (const line of contextLines(lines)) {
59020
+ curRange.push(line);
59021
+ }
59022
+ } else {
59023
+ const contextSize = Math.min(lines.length, context);
59024
+ for (const line of contextLines(lines.slice(0, contextSize))) {
59025
+ curRange.push(line);
59026
+ }
59027
+ const hunk = {
59028
+ oldStart: oldRangeStart,
59029
+ oldLines: oldLine - oldRangeStart + contextSize,
59030
+ newStart: newRangeStart,
59031
+ newLines: newLine - newRangeStart + contextSize,
59032
+ lines: curRange
59033
+ };
59034
+ hunks.push(hunk);
59035
+ oldRangeStart = 0;
59036
+ newRangeStart = 0;
59037
+ curRange = [];
59038
+ }
59039
+ }
59040
+ oldLine += lines.length;
59041
+ newLine += lines.length;
59042
+ }
59043
+ }
59044
+ for (const hunk of hunks) {
59045
+ for (let i2 = 0;i2 < hunk.lines.length; i2++) {
59046
+ if (hunk.lines[i2].endsWith(`
59047
+ `)) {
59048
+ hunk.lines[i2] = hunk.lines[i2].slice(0, -1);
59049
+ } else {
59050
+ hunk.lines.splice(i2 + 1, 0, "\");
59051
+ i2++;
59052
+ }
57798
59053
  }
57799
59054
  }
59055
+ return {
59056
+ oldFileName,
59057
+ newFileName,
59058
+ oldHeader,
59059
+ newHeader,
59060
+ hunks
59061
+ };
57800
59062
  }
57801
- if (inHunk && hunkLines.length > 0) {
57802
- diff += `@@ -${oldStart},${oldCount} +${newStart},${newCount} @@
57803
- `;
57804
- diff += hunkLines.join(`
59063
+ }
59064
+ function formatPatch(patch, headerOptions) {
59065
+ if (!headerOptions) {
59066
+ headerOptions = INCLUDE_HEADERS;
59067
+ }
59068
+ if (Array.isArray(patch)) {
59069
+ if (patch.length > 1 && !headerOptions.includeFileHeaders) {
59070
+ throw new Error("Cannot omit file headers on a multi-file patch. " + "(The result would be unparseable; how would a tool trying to apply " + "the patch know which changes are to which file?)");
59071
+ }
59072
+ return patch.map((p) => formatPatch(p, headerOptions)).join(`
59073
+ `);
59074
+ }
59075
+ const ret = [];
59076
+ if (headerOptions.includeIndex && patch.oldFileName == patch.newFileName) {
59077
+ ret.push("Index: " + patch.oldFileName);
59078
+ }
59079
+ if (headerOptions.includeUnderline) {
59080
+ ret.push("===================================================================");
59081
+ }
59082
+ if (headerOptions.includeFileHeaders) {
59083
+ ret.push("--- " + patch.oldFileName + (typeof patch.oldHeader === "undefined" ? "" : "\t" + patch.oldHeader));
59084
+ ret.push("+++ " + patch.newFileName + (typeof patch.newHeader === "undefined" ? "" : "\t" + patch.newHeader));
59085
+ }
59086
+ for (let i2 = 0;i2 < patch.hunks.length; i2++) {
59087
+ const hunk = patch.hunks[i2];
59088
+ if (hunk.oldLines === 0) {
59089
+ hunk.oldStart -= 1;
59090
+ }
59091
+ if (hunk.newLines === 0) {
59092
+ hunk.newStart -= 1;
59093
+ }
59094
+ ret.push("@@ -" + hunk.oldStart + "," + hunk.oldLines + " +" + hunk.newStart + "," + hunk.newLines + " @@");
59095
+ for (const line of hunk.lines) {
59096
+ ret.push(line);
59097
+ }
59098
+ }
59099
+ return ret.join(`
57805
59100
  `) + `
57806
59101
  `;
59102
+ }
59103
+ function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
59104
+ if (typeof options === "function") {
59105
+ options = { callback: options };
57807
59106
  }
57808
- return diff || `--- ${filePath}
57809
- +++ ${filePath}
57810
- `;
59107
+ if (!(options === null || options === undefined ? undefined : options.callback)) {
59108
+ const patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
59109
+ if (!patchObj) {
59110
+ return;
59111
+ }
59112
+ return formatPatch(patchObj, options === null || options === undefined ? undefined : options.headerOptions);
59113
+ } else {
59114
+ const { callback } = options;
59115
+ structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: (patchObj) => {
59116
+ if (!patchObj) {
59117
+ callback(undefined);
59118
+ } else {
59119
+ callback(formatPatch(patchObj, options.headerOptions));
59120
+ }
59121
+ } }));
59122
+ }
59123
+ }
59124
+ function splitLines(text) {
59125
+ const hasTrailingNl = text.endsWith(`
59126
+ `);
59127
+ const result = text.split(`
59128
+ `).map((line) => line + `
59129
+ `);
59130
+ if (hasTrailingNl) {
59131
+ result.pop();
59132
+ } else {
59133
+ result.push(result.pop().slice(0, -1));
59134
+ }
59135
+ return result;
59136
+ }
59137
+ // src/tools/hashline-edit/diff-utils.ts
59138
+ function generateUnifiedDiff(oldContent, newContent, filePath) {
59139
+ return createTwoFilesPatch(filePath, filePath, oldContent, newContent, undefined, undefined, { context: 3 });
57811
59140
  }
57812
59141
  function countLineDiffs(oldContent, newContent) {
57813
59142
  const oldLines = oldContent.split(`
@@ -57979,7 +59308,7 @@ function resolveToolCallID2(ctx) {
57979
59308
  function canCreateFromMissingFile(edits) {
57980
59309
  if (edits.length === 0)
57981
59310
  return false;
57982
- return edits.every((edit) => edit.op === "append" || edit.op === "prepend");
59311
+ return edits.every((edit) => (edit.op === "append" || edit.op === "prepend") && !edit.pos);
57983
59312
  }
57984
59313
  function buildSuccessMeta(effectivePath, beforeContent, afterContent, noopEdits, deduplicatedEdits) {
57985
59314
  const unifiedDiff = generateUnifiedDiff(beforeContent, afterContent, effectivePath);
@@ -58023,16 +59352,16 @@ async function executeHashlineEditTool(args, context) {
58023
59352
  const metadataContext = context;
58024
59353
  const filePath = args.filePath;
58025
59354
  const { delete: deleteMode, rename } = args;
58026
- if (!deleteMode && (!args.edits || !Array.isArray(args.edits) || args.edits.length === 0)) {
58027
- return "Error: edits parameter must be a non-empty array";
58028
- }
58029
- const edits = deleteMode ? [] : normalizeHashlineEdits(args.edits);
58030
59355
  if (deleteMode && rename) {
58031
59356
  return "Error: delete and rename cannot be used together";
58032
59357
  }
58033
- if (deleteMode && edits.length > 0) {
59358
+ if (deleteMode && args.edits.length > 0) {
58034
59359
  return "Error: delete mode requires edits to be an empty array";
58035
59360
  }
59361
+ if (!deleteMode && (!args.edits || !Array.isArray(args.edits) || args.edits.length === 0)) {
59362
+ return "Error: edits parameter must be a non-empty array";
59363
+ }
59364
+ const edits = deleteMode ? [] : normalizeHashlineEdits(args.edits);
58036
59365
  const file2 = Bun.file(filePath);
58037
59366
  const exists = await file2.exists();
58038
59367
  if (!exists && !deleteMode && !canCreateFromMissingFile(edits)) {
@@ -58097,7 +59426,7 @@ WORKFLOW:
58097
59426
  VALIDATION:
58098
59427
  Payload shape: { "filePath": string, "edits": [...], "delete"?: boolean, "rename"?: string }
58099
59428
  Each edit must be one of: replace, append, prepend
58100
- Edit shape: { "op": "replace"|"append"|"prepend", "pos"?: "LINE#ID", "end"?: "LINE#ID", "lines"?: string|string[]|null }
59429
+ Edit shape: { "op": "replace"|"append"|"prepend", "pos"?: "LINE#ID", "end"?: "LINE#ID", "lines": string|string[]|null }
58101
59430
  lines must contain plain replacement text only (no LINE#ID prefixes, no diff + markers)
58102
59431
  CRITICAL: all operations validate against the same pre-edit file snapshot and apply bottom-up. Refs/tags are interpreted against the last-read version of the file.
58103
59432
 
@@ -58171,7 +59500,7 @@ function createHashlineEditTool() {
58171
59500
  ]).describe("Hashline edit operation mode"),
58172
59501
  pos: tool.schema.string().optional().describe("Primary anchor in LINE#ID format"),
58173
59502
  end: tool.schema.string().optional().describe("Range end anchor in LINE#ID format"),
58174
- lines: tool.schema.union([tool.schema.string(), tool.schema.array(tool.schema.string()), tool.schema.null()]).optional().describe("Replacement or inserted lines. null/[] deletes with replace")
59503
+ lines: tool.schema.union([tool.schema.string(), tool.schema.array(tool.schema.string()), tool.schema.null()]).describe("Replacement or inserted lines. null/[] deletes with replace")
58175
59504
  })).describe("Array of edit operations to apply (empty when delete=true)")
58176
59505
  },
58177
59506
  execute: async (args, context) => executeHashlineEditTool(args, context)
@@ -58281,7 +59610,9 @@ function createSessionHooks(args) {
58281
59610
  const prometheusMdOnly = isHookEnabled("prometheus-md-only") ? safeHook("prometheus-md-only", () => createPrometheusMdOnlyHook(ctx)) : null;
58282
59611
  const sisyphusJuniorNotepad = isHookEnabled("sisyphus-junior-notepad") ? safeHook("sisyphus-junior-notepad", () => createSisyphusJuniorNotepadHook(ctx)) : null;
58283
59612
  const noSisyphusGpt = isHookEnabled("no-sisyphus-gpt") ? safeHook("no-sisyphus-gpt", () => createNoSisyphusGptHook(ctx)) : null;
58284
- const noHephaestusNonGpt = isHookEnabled("no-hephaestus-non-gpt") ? safeHook("no-hephaestus-non-gpt", () => createNoHephaestusNonGptHook(ctx)) : null;
59613
+ const noHephaestusNonGpt = isHookEnabled("no-hephaestus-non-gpt") ? safeHook("no-hephaestus-non-gpt", () => createNoHephaestusNonGptHook(ctx, {
59614
+ allowNonGptModel: pluginConfig.agents?.hephaestus?.allow_non_gpt_model
59615
+ })) : null;
58285
59616
  const questionLabelTruncator = isHookEnabled("question-label-truncator") ? safeHook("question-label-truncator", () => createQuestionLabelTruncatorHook()) : null;
58286
59617
  const taskResumeInfo = isHookEnabled("task-resume-info") ? safeHook("task-resume-info", () => createTaskResumeInfoHook()) : null;
58287
59618
  const anthropicEffort = isHookEnabled("anthropic-effort") ? safeHook("anthropic-effort", () => createAnthropicEffortHook()) : null;
@@ -58344,8 +59675,9 @@ function createToolGuardHooks(args) {
58344
59675
  const rulesInjector = isHookEnabled("rules-injector") ? safeHook("rules-injector", () => createRulesInjectorHook(ctx, modelCacheState)) : null;
58345
59676
  const tasksTodowriteDisabler = isHookEnabled("tasks-todowrite-disabler") ? safeHook("tasks-todowrite-disabler", () => createTasksTodowriteDisablerHook({ experimental: pluginConfig.experimental })) : null;
58346
59677
  const writeExistingFileGuard = isHookEnabled("write-existing-file-guard") ? safeHook("write-existing-file-guard", () => createWriteExistingFileGuardHook(ctx)) : null;
58347
- const hashlineReadEnhancer = isHookEnabled("hashline-read-enhancer") ? safeHook("hashline-read-enhancer", () => createHashlineReadEnhancerHook(ctx, { hashline_edit: { enabled: pluginConfig.hashline_edit ?? true } })) : null;
59678
+ const hashlineReadEnhancer = isHookEnabled("hashline-read-enhancer") ? safeHook("hashline-read-enhancer", () => createHashlineReadEnhancerHook(ctx, { hashline_edit: { enabled: pluginConfig.hashline_edit ?? false } })) : null;
58348
59679
  const jsonErrorRecovery = isHookEnabled("json-error-recovery") ? safeHook("json-error-recovery", () => createJsonErrorRecoveryHook(ctx)) : null;
59680
+ const readImageResizer = isHookEnabled("read-image-resizer") ? safeHook("read-image-resizer", () => createReadImageResizerHook(ctx)) : null;
58349
59681
  return {
58350
59682
  commentChecker,
58351
59683
  toolOutputTruncator,
@@ -58356,7 +59688,8 @@ function createToolGuardHooks(args) {
58356
59688
  tasksTodowriteDisabler,
58357
59689
  writeExistingFileGuard,
58358
59690
  hashlineReadEnhancer,
58359
- jsonErrorRecovery
59691
+ jsonErrorRecovery,
59692
+ readImageResizer
58360
59693
  };
58361
59694
  }
58362
59695
 
@@ -58372,6 +59705,7 @@ var CONTEXT_SEPARATOR = `
58372
59705
  ---
58373
59706
 
58374
59707
  `;
59708
+ var registrationCounter = 0;
58375
59709
 
58376
59710
  class ContextCollector {
58377
59711
  sessions = new Map;
@@ -58386,7 +59720,7 @@ class ContextCollector {
58386
59720
  source: options.source,
58387
59721
  content: options.content,
58388
59722
  priority: options.priority ?? "normal",
58389
- timestamp: Date.now(),
59723
+ registrationOrder: ++registrationCounter,
58390
59724
  metadata: options.metadata
58391
59725
  };
58392
59726
  sessionMap.set(key, entry);
@@ -58425,7 +59759,7 @@ class ContextCollector {
58425
59759
  const priorityDiff = PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority];
58426
59760
  if (priorityDiff !== 0)
58427
59761
  return priorityDiff;
58428
- return a.timestamp - b.timestamp;
59762
+ return a.registrationOrder - b.registrationOrder;
58429
59763
  });
58430
59764
  }
58431
59765
  }
@@ -58486,7 +59820,7 @@ function createContextInjectorMessagesTransformHook(collector) {
58486
59820
  return;
58487
59821
  }
58488
59822
  const syntheticPart = {
58489
- id: `synthetic_hook_${Date.now()}`,
59823
+ id: `synthetic_hook_${sessionID}`,
58490
59824
  messageID: lastUserMessage.info.id,
58491
59825
  sessionID: lastUserMessage.info.sessionID ?? "",
58492
59826
  type: "text",
@@ -58591,7 +59925,9 @@ function createContinuationHooks(args) {
58591
59925
  sessionRecovery
58592
59926
  } = args;
58593
59927
  const safeHook = (hookName, factory) => safeCreateHook(hookName, factory, { enabled: safeHookEnabled });
58594
- const stopContinuationGuard = isHookEnabled("stop-continuation-guard") ? safeHook("stop-continuation-guard", () => createStopContinuationGuardHook(ctx)) : null;
59928
+ const stopContinuationGuard = isHookEnabled("stop-continuation-guard") ? safeHook("stop-continuation-guard", () => createStopContinuationGuardHook(ctx, {
59929
+ backgroundManager
59930
+ })) : null;
58595
59931
  const compactionContextInjector = isHookEnabled("compaction-context-injector") ? safeHook("compaction-context-injector", () => createCompactionContextInjector(backgroundManager)) : null;
58596
59932
  const compactionTodoPreserver = isHookEnabled("compaction-todo-preserver") ? safeHook("compaction-todo-preserver", () => createCompactionTodoPreserverHook(ctx)) : null;
58597
59933
  const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer") ? safeHook("todo-continuation-enforcer", () => createTodoContinuationEnforcer(ctx, {
@@ -58779,14 +60115,14 @@ class ConcurrencyManager {
58779
60115
  this.counts.set(model, current + 1);
58780
60116
  return;
58781
60117
  }
58782
- return new Promise((resolve10, reject) => {
60118
+ return new Promise((resolve13, reject) => {
58783
60119
  const queue = this.queues.get(model) ?? [];
58784
60120
  const entry = {
58785
60121
  resolve: () => {
58786
60122
  if (entry.settled)
58787
60123
  return;
58788
60124
  entry.settled = true;
58789
- resolve10();
60125
+ resolve13();
58790
60126
  },
58791
60127
  rawReject: reject,
58792
60128
  settled: false
@@ -59307,6 +60643,7 @@ async function checkAndInterruptStaleTasks(args) {
59307
60643
  class BackgroundManager {
59308
60644
  tasks;
59309
60645
  notifications;
60646
+ pendingNotifications;
59310
60647
  pendingByParent;
59311
60648
  client;
59312
60649
  directory;
@@ -59328,6 +60665,7 @@ class BackgroundManager {
59328
60665
  constructor(ctx, config3, options) {
59329
60666
  this.tasks = new Map;
59330
60667
  this.notifications = new Map;
60668
+ this.pendingNotifications = new Map;
59331
60669
  this.pendingByParent = new Map;
59332
60670
  this.client = ctx.client;
59333
60671
  this.directory = ctx.directory;
@@ -59893,6 +61231,7 @@ class BackgroundManager {
59893
61231
  for (const descendant of this.getAllDescendantTasks(sessionID)) {
59894
61232
  tasksToCancel.set(descendant.id, descendant);
59895
61233
  }
61234
+ this.pendingNotifications.delete(sessionID);
59896
61235
  if (tasksToCancel.size === 0)
59897
61236
  return;
59898
61237
  for (const task of tasksToCancel.values()) {
@@ -59926,6 +61265,11 @@ class BackgroundManager {
59926
61265
  subagentSessions.delete(task.sessionID);
59927
61266
  }
59928
61267
  }
61268
+ for (const task of tasksToCancel.values()) {
61269
+ if (task.parentSessionID) {
61270
+ this.pendingNotifications.delete(task.parentSessionID);
61271
+ }
61272
+ }
59929
61273
  SessionCategoryRegistry.remove(sessionID);
59930
61274
  }
59931
61275
  if (event.type === "session.status") {
@@ -59969,6 +61313,34 @@ class BackgroundManager {
59969
61313
  clearNotifications(sessionID) {
59970
61314
  this.notifications.delete(sessionID);
59971
61315
  }
61316
+ queuePendingNotification(sessionID, notification2) {
61317
+ if (!sessionID)
61318
+ return;
61319
+ const existingNotifications = this.pendingNotifications.get(sessionID) ?? [];
61320
+ existingNotifications.push(notification2);
61321
+ this.pendingNotifications.set(sessionID, existingNotifications);
61322
+ }
61323
+ injectPendingNotificationsIntoChatMessage(output, sessionID) {
61324
+ const pendingNotifications = this.pendingNotifications.get(sessionID);
61325
+ if (!pendingNotifications || pendingNotifications.length === 0) {
61326
+ return;
61327
+ }
61328
+ this.pendingNotifications.delete(sessionID);
61329
+ const notificationContent = pendingNotifications.join(`
61330
+
61331
+ `);
61332
+ const firstTextPartIndex = output.parts.findIndex((part) => part.type === "text");
61333
+ if (firstTextPartIndex === -1) {
61334
+ output.parts.unshift(createInternalAgentTextPart(notificationContent));
61335
+ return;
61336
+ }
61337
+ const originalText = output.parts[firstTextPartIndex].text ?? "";
61338
+ output.parts[firstTextPartIndex].text = `${notificationContent}
61339
+
61340
+ ---
61341
+
61342
+ ${originalText}`;
61343
+ }
59972
61344
  async validateSessionHasOutput(sessionID) {
59973
61345
  try {
59974
61346
  const response = await this.client.session.messages({
@@ -60264,6 +61636,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
60264
61636
  taskId: task.id,
60265
61637
  parentSessionID: task.parentSessionID
60266
61638
  });
61639
+ this.queuePendingNotification(task.parentSessionID, notification2);
60267
61640
  } else {
60268
61641
  log("[background-agent] Failed to send notification:", error45);
60269
61642
  }
@@ -60452,6 +61825,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
60452
61825
  this.concurrencyManager.clear();
60453
61826
  this.tasks.clear();
60454
61827
  this.notifications.clear();
61828
+ this.pendingNotifications.clear();
60455
61829
  this.pendingByParent.clear();
60456
61830
  this.notificationQueueByParent.clear();
60457
61831
  this.queuesByKey.clear();
@@ -62003,7 +63377,7 @@ class Protocol {
62003
63377
  return;
62004
63378
  }
62005
63379
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
62006
- await new Promise((resolve10) => setTimeout(resolve10, pollInterval));
63380
+ await new Promise((resolve13) => setTimeout(resolve13, pollInterval));
62007
63381
  options?.signal?.throwIfAborted();
62008
63382
  }
62009
63383
  } catch (error45) {
@@ -62015,7 +63389,7 @@ class Protocol {
62015
63389
  }
62016
63390
  request(request, resultSchema, options) {
62017
63391
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
62018
- return new Promise((resolve10, reject) => {
63392
+ return new Promise((resolve13, reject) => {
62019
63393
  const earlyReject = (error45) => {
62020
63394
  reject(error45);
62021
63395
  };
@@ -62093,7 +63467,7 @@ class Protocol {
62093
63467
  if (!parseResult.success) {
62094
63468
  reject(parseResult.error);
62095
63469
  } else {
62096
- resolve10(parseResult.data);
63470
+ resolve13(parseResult.data);
62097
63471
  }
62098
63472
  } catch (error45) {
62099
63473
  reject(error45);
@@ -62284,12 +63658,12 @@ class Protocol {
62284
63658
  interval = task.pollInterval;
62285
63659
  }
62286
63660
  } catch {}
62287
- return new Promise((resolve10, reject) => {
63661
+ return new Promise((resolve13, reject) => {
62288
63662
  if (signal.aborted) {
62289
63663
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
62290
63664
  return;
62291
63665
  }
62292
- const timeoutId = setTimeout(resolve10, interval);
63666
+ const timeoutId = setTimeout(resolve13, interval);
62293
63667
  signal.addEventListener("abort", () => {
62294
63668
  clearTimeout(timeoutId);
62295
63669
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -63027,8 +64401,8 @@ async function random(size) {
63027
64401
  const evenDistCutoff = Math.pow(2, 8) - Math.pow(2, 8) % mask.length;
63028
64402
  let result = "";
63029
64403
  while (result.length < size) {
63030
- const randomBytes = await getRandomValues(size - result.length);
63031
- for (const randomByte of randomBytes) {
64404
+ const randomBytes2 = await getRandomValues(size - result.length);
64405
+ for (const randomByte of randomBytes2) {
63032
64406
  if (randomByte < evenDistCutoff) {
63033
64407
  result += mask[randomByte % mask.length];
63034
64408
  }
@@ -63834,7 +65208,7 @@ function createParser(callbacks) {
63834
65208
  const { onEvent = noop, onError = noop, onRetry = noop, onComment } = callbacks;
63835
65209
  let incompleteLine = "", isFirstChunk = true, id, data = "", eventType = "";
63836
65210
  function feed(newChunk) {
63837
- const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] = splitLines(`${incompleteLine}${chunk}`);
65211
+ const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] = splitLines2(`${incompleteLine}${chunk}`);
63838
65212
  for (const line of complete)
63839
65213
  parseLine(line);
63840
65214
  incompleteLine = incomplete, isFirstChunk = false;
@@ -63893,7 +65267,7 @@ function createParser(callbacks) {
63893
65267
  }
63894
65268
  return { feed, reset };
63895
65269
  }
63896
- function splitLines(chunk) {
65270
+ function splitLines2(chunk) {
63897
65271
  const lines = [];
63898
65272
  let incompleteLine = "", searchIndex = 0;
63899
65273
  for (;searchIndex < chunk.length; ) {
@@ -64533,10 +65907,10 @@ async function findAvailablePort2(startPort = DEFAULT_PORT) {
64533
65907
 
64534
65908
  // src/features/mcp-oauth/oauth-authorization-flow.ts
64535
65909
  import { spawn as spawn13 } from "child_process";
64536
- import { createHash as createHash2, randomBytes } from "crypto";
65910
+ import { createHash as createHash2, randomBytes as randomBytes2 } from "crypto";
64537
65911
  import { createServer } from "http";
64538
65912
  function generateCodeVerifier() {
64539
- return randomBytes(32).toString("base64url");
65913
+ return randomBytes2(32).toString("base64url");
64540
65914
  }
64541
65915
  function generateCodeChallenge(verifier) {
64542
65916
  return createHash2("sha256").update(verifier).digest("base64url");
@@ -64559,7 +65933,7 @@ function buildAuthorizationUrl(authorizationEndpoint, options) {
64559
65933
  }
64560
65934
  var CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
64561
65935
  function startCallbackServer(port) {
64562
- return new Promise((resolve10, reject) => {
65936
+ return new Promise((resolve13, reject) => {
64563
65937
  let timeoutId;
64564
65938
  const server = createServer((request, response) => {
64565
65939
  clearTimeout(timeoutId);
@@ -64585,7 +65959,7 @@ function startCallbackServer(port) {
64585
65959
  response.writeHead(200, { "content-type": "text/html" });
64586
65960
  response.end("<html><body><h1>Authorization successful. You can close this tab.</h1></body></html>");
64587
65961
  server.close();
64588
- resolve10({ code, state: state3 });
65962
+ resolve13({ code, state: state3 });
64589
65963
  });
64590
65964
  timeoutId = setTimeout(() => {
64591
65965
  server.close();
@@ -64621,7 +65995,7 @@ function openBrowser(url2) {
64621
65995
  async function runAuthorizationCodeRedirect(options) {
64622
65996
  const verifier = generateCodeVerifier();
64623
65997
  const challenge = generateCodeChallenge(verifier);
64624
- const state3 = randomBytes(16).toString("hex");
65998
+ const state3 = randomBytes2(16).toString("hex");
64625
65999
  const authorizationUrl = buildAuthorizationUrl(options.authorizationEndpoint, {
64626
66000
  clientId: options.clientId,
64627
66001
  redirectUri: options.redirectUri,
@@ -65037,7 +66411,7 @@ class StdioClientTransport {
65037
66411
  if (this._process) {
65038
66412
  throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
65039
66413
  }
65040
- return new Promise((resolve10, reject) => {
66414
+ return new Promise((resolve13, reject) => {
65041
66415
  this._process = import_cross_spawn.default(this._serverParams.command, this._serverParams.args ?? [], {
65042
66416
  env: {
65043
66417
  ...getDefaultEnvironment(),
@@ -65053,7 +66427,7 @@ class StdioClientTransport {
65053
66427
  this.onerror?.(error45);
65054
66428
  });
65055
66429
  this._process.on("spawn", () => {
65056
- resolve10();
66430
+ resolve13();
65057
66431
  });
65058
66432
  this._process.on("close", (_code) => {
65059
66433
  this._process = undefined;
@@ -65100,20 +66474,20 @@ class StdioClientTransport {
65100
66474
  if (this._process) {
65101
66475
  const processToClose = this._process;
65102
66476
  this._process = undefined;
65103
- const closePromise = new Promise((resolve10) => {
66477
+ const closePromise = new Promise((resolve13) => {
65104
66478
  processToClose.once("close", () => {
65105
- resolve10();
66479
+ resolve13();
65106
66480
  });
65107
66481
  });
65108
66482
  try {
65109
66483
  processToClose.stdin?.end();
65110
66484
  } catch {}
65111
- await Promise.race([closePromise, new Promise((resolve10) => setTimeout(resolve10, 2000).unref())]);
66485
+ await Promise.race([closePromise, new Promise((resolve13) => setTimeout(resolve13, 2000).unref())]);
65112
66486
  if (processToClose.exitCode === null) {
65113
66487
  try {
65114
66488
  processToClose.kill("SIGTERM");
65115
66489
  } catch {}
65116
- await Promise.race([closePromise, new Promise((resolve10) => setTimeout(resolve10, 2000).unref())]);
66490
+ await Promise.race([closePromise, new Promise((resolve13) => setTimeout(resolve13, 2000).unref())]);
65117
66491
  }
65118
66492
  if (processToClose.exitCode === null) {
65119
66493
  try {
@@ -65124,15 +66498,15 @@ class StdioClientTransport {
65124
66498
  this._readBuffer.clear();
65125
66499
  }
65126
66500
  send(message) {
65127
- return new Promise((resolve10) => {
66501
+ return new Promise((resolve13) => {
65128
66502
  if (!this._process?.stdin) {
65129
66503
  throw new Error("Not connected");
65130
66504
  }
65131
66505
  const json3 = serializeMessage(message);
65132
66506
  if (this._process.stdin.write(json3)) {
65133
- resolve10();
66507
+ resolve13();
65134
66508
  } else {
65135
- this._process.stdin.once("drain", resolve10);
66509
+ this._process.stdin.once("drain", resolve13);
65136
66510
  }
65137
66511
  });
65138
66512
  }
@@ -66225,7 +67599,7 @@ class TmuxSessionManager {
66225
67599
  } catch (err) {
66226
67600
  log("[tmux-session-manager] session status check error", { error: String(err) });
66227
67601
  }
66228
- await new Promise((resolve10) => setTimeout(resolve10, SESSION_READY_POLL_INTERVAL_MS));
67602
+ await new Promise((resolve13) => setTimeout(resolve13, SESSION_READY_POLL_INTERVAL_MS));
66229
67603
  }
66230
67604
  log("[tmux-session-manager] session ready timeout", {
66231
67605
  sessionId,
@@ -66480,6 +67854,134 @@ Before responding, enumerate in your head:
66480
67854
  Then ACTUALLY CALL those tools using the JSON tool schema. Produce the tool_use blocks. Execute.
66481
67855
  </TOOL_CALL_MANDATE>`;
66482
67856
  }
67857
+ function buildGeminiToolGuide() {
67858
+ return `<GEMINI_TOOL_GUIDE>
67859
+ ## Tool Usage Guide \u2014 WHEN and HOW to Call Each Tool
67860
+
67861
+ You have access to tools via function calling. This guide defines WHEN to call each one.
67862
+ **Violating these patterns = failed response.**
67863
+
67864
+ ### Reading & Search (ALWAYS parallelizable \u2014 call multiple simultaneously)
67865
+
67866
+ | Tool | When to Call | Parallel? |
67867
+ |---|---|---|
67868
+ | \`Read\` | Before making ANY claim about file contents. Before editing any file. | \uFFFD Yes \u2014 read multiple files at once |
67869
+ | \`Grep\` | Finding patterns, imports, usages across codebase. BEFORE claiming "X is used in Y". | \u2705 Yes \u2014 run multiple greps at once |
67870
+ | \`Glob\` | Finding files by name/extension pattern. BEFORE claiming "file X exists". | \u2705 Yes \u2014 run multiple globs at once |
67871
+ | \`AstGrepSearch\` | Finding code patterns with AST awareness (structural matches). | \u2705 Yes |
67872
+
67873
+ ### Code Intelligence (parallelizable on different files)
67874
+
67875
+ | Tool | When to Call | Parallel? |
67876
+ |---|---|---|
67877
+ | \`LspDiagnostics\` | **AFTER EVERY edit.** BEFORE claiming task is done. MANDATORY. | \u2705 Yes \u2014 different files |
67878
+ | \`LspGotoDefinition\` | Finding where a symbol is defined. | \u2705 Yes |
67879
+ | \`LspFindReferences\` | Finding all usages of a symbol across workspace. | \u2705 Yes |
67880
+ | \`LspSymbols\` | Getting file outline or searching workspace symbols. | \u2705 Yes |
67881
+
67882
+ ### Editing (SEQUENTIAL \u2014 must Read first)
67883
+
67884
+ | Tool | When to Call | Parallel? |
67885
+ |---|---|---|
67886
+ | \`Edit\` | Modifying existing files. MUST Read file first to get LINE#ID anchors. | \u274C After Read |
67887
+ | \`Write\` | Creating NEW files only. Or full file overwrite. | \u274C Sequential |
67888
+
67889
+ ### Execution & Delegation
67890
+
67891
+ | Tool | When to Call | Parallel? |
67892
+ |---|---|---|
67893
+ | \`Bash\` | Running tests, builds, git commands. | \u274C Usually sequential |
67894
+ | \`Task\` | ANY non-trivial implementation. Research via explore/librarian. | \u2705 Fire multiple in background |
67895
+
67896
+ ### Correct Sequences (MANDATORY \u2014 follow these exactly):
67897
+
67898
+ 1. **Answer about code**: Read \u2192 (analyze) \u2192 Answer
67899
+ 2. **Edit code**: Read \u2192 Edit \u2192 LspDiagnostics \u2192 Report
67900
+ 3. **Find something**: Grep/Glob (parallel) \u2192 Read results \u2192 Report
67901
+ 4. **Implement feature**: Task(delegate) \u2192 Verify results \u2192 Report
67902
+ 5. **Debug**: Read error \u2192 Read file \u2192 Grep related \u2192 Fix \u2192 LspDiagnostics
67903
+
67904
+ ### PARALLEL RULES:
67905
+
67906
+ - **Independent reads/searches**: ALWAYS call simultaneously in ONE response
67907
+ - **Dependent operations**: Call sequentially (Edit AFTER Read, LspDiagnostics AFTER Edit)
67908
+ - **Background agents**: ALWAYS \`run_in_background=true\`, continue working
67909
+ </GEMINI_TOOL_GUIDE>`;
67910
+ }
67911
+ function buildGeminiToolCallExamples() {
67912
+ return `<GEMINI_TOOL_CALL_EXAMPLES>
67913
+ ## Correct Tool Calling Patterns \u2014 Follow These Examples
67914
+
67915
+ ### Example 1: User asks about code \u2192 Read FIRST, then answer
67916
+ **User**: "How does the auth middleware work?"
67917
+ **CORRECT**:
67918
+ \`\`\`
67919
+ \u2192 Call Read(filePath="/src/middleware/auth.ts")
67920
+ \u2192 Call Read(filePath="/src/config/auth.ts") // parallel with above
67921
+ \u2192 (After reading) Answer based on ACTUAL file contents
67922
+ \`\`\`
67923
+ **WRONG**:
67924
+ \`\`\`
67925
+ \u2192 "The auth middleware likely validates JWT tokens by..." \u2190 HALLUCINATION. You didn't read the file.
67926
+ \`\`\`
67927
+
67928
+ ### Example 2: User asks to edit code \u2192 Read, Edit, Verify
67929
+ **User**: "Fix the type error in user.ts"
67930
+ **CORRECT**:
67931
+ \`\`\`
67932
+ \u2192 Call Read(filePath="/src/models/user.ts")
67933
+ \u2192 Call LspDiagnostics(filePath="/src/models/user.ts") // parallel with Read
67934
+ \u2192 (After reading) Call Edit with LINE#ID anchors
67935
+ \u2192 Call LspDiagnostics(filePath="/src/models/user.ts") // verify fix
67936
+ \u2192 Report: "Fixed. Diagnostics clean."
67937
+ \`\`\`
67938
+ **WRONG**:
67939
+ \`\`\`
67940
+ \u2192 Call Edit without reading first \u2190 No LINE#ID anchors = WILL FAIL
67941
+ \u2192 Skip LspDiagnostics after edit \u2190 UNVERIFIED
67942
+ \`\`\`
67943
+
67944
+ ### Example 3: User asks to find something \u2192 Search in parallel
67945
+ **User**: "Where is the database connection configured?"
67946
+ **CORRECT**:
67947
+ \`\`\`
67948
+ \u2192 Call Grep(pattern="database|connection|pool", path="/src") // fires simultaneously
67949
+ \u2192 Call Glob(pattern="**/*database*") // fires simultaneously
67950
+ \u2192 Call Glob(pattern="**/*db*") // fires simultaneously
67951
+ \u2192 (After results) Read the most relevant files
67952
+ \u2192 Report findings with file paths
67953
+ \`\`\`
67954
+
67955
+ ### Example 4: User asks to implement a feature \u2192 DELEGATE
67956
+ **User**: "Add a new /health endpoint to the API"
67957
+ **CORRECT**:
67958
+ \`\`\`
67959
+ \u2192 Call Task(category="quick", load_skills=["typescript-programmer"], prompt="...")
67960
+ \u2192 (After agent completes) Read changed files to verify
67961
+ \u2192 Call LspDiagnostics on changed files
67962
+ \u2192 Report
67963
+ \`\`\`
67964
+ **WRONG**:
67965
+ \`\`\`
67966
+ \u2192 Write the code yourself \u2190 YOU ARE AN ORCHESTRATOR, NOT AN IMPLEMENTER
67967
+ \`\`\`
67968
+
67969
+ ### Example 5: Investigation \u2260 Implementation
67970
+ **User**: "Look into why the tests are failing"
67971
+ **CORRECT**:
67972
+ \`\`\`
67973
+ \u2192 Call Bash(command="npm test") // see actual failures
67974
+ \u2192 Call Read on failing test files
67975
+ \u2192 Call Read on source files under test
67976
+ \u2192 Report: "Tests fail because X. Root cause: Y. Proposed fix: Z."
67977
+ \u2192 STOP \u2014 wait for user to say "fix it"
67978
+ \`\`\`
67979
+ **WRONG**:
67980
+ \`\`\`
67981
+ \u2192 Start editing source files immediately \u2190 "look into" \u2260 "fix"
67982
+ \`\`\`
67983
+ </GEMINI_TOOL_CALL_EXAMPLES>`;
67984
+ }
66483
67985
  function buildGeminiDelegationOverride() {
66484
67986
  return `<GEMINI_DELEGATION_OVERRIDE>
66485
67987
  ## DELEGATION IS MANDATORY \u2014 YOU ARE NOT AN IMPLEMENTER
@@ -66781,12 +68283,11 @@ Briefly announce "Consulting Oracle for [reason]" before invocation.
66781
68283
 
66782
68284
  ### Oracle Background Task Policy:
66783
68285
 
66784
- **You MUST collect Oracle results before your final answer. No exceptions.**
68286
+ **Collect Oracle results before your final answer. No exceptions.**
66785
68287
 
66786
- - Oracle may take several minutes. This is normal and expected.
66787
- - When Oracle is running and you finish your own exploration/analysis, your next action is \`background_output(task_id="...")\` on Oracle \u2014 NOT delivering a final answer.
66788
- - Oracle catches blind spots you cannot see \u2014 its value is HIGHEST when you think you don't need it.
66789
- - **NEVER** cancel Oracle. **NEVER** use \`background_cancel(all=true)\` when Oracle is running. Cancel disposable tasks (explore, librarian) individually by taskId instead.
68288
+ - Oracle takes minutes. When done with your own work: **end your response** \u2014 wait for the \`<system-reminder>\`.
68289
+ - Do NOT poll \`background_output\` on a running Oracle. The notification will come.
68290
+ - Never cancel Oracle.
66790
68291
  </Oracle_Usage>`;
66791
68292
  }
66792
68293
  function buildHardBlocksSection() {
@@ -66795,8 +68296,8 @@ function buildHardBlocksSection() {
66795
68296
  "- Commit without explicit request \u2014 **Never**",
66796
68297
  "- Speculate about unread code \u2014 **Never**",
66797
68298
  "- Leave code in broken state after failures \u2014 **Never**",
66798
- "- `background_cancel(all=true)` when Oracle is running \u2014 **Never.** Cancel tasks individually by taskId.",
66799
- "- Delivering final answer before collecting Oracle result \u2014 **Never.** Always `background_output` Oracle first."
68299
+ "- `background_cancel(all=true)` \u2014 **Never.** Always cancel individually by taskId.",
68300
+ "- Delivering final answer before collecting Oracle result \u2014 **Never.**"
66800
68301
  ];
66801
68302
  return `## Hard Blocks (NEVER violate)
66802
68303
 
@@ -66810,14 +68311,29 @@ function buildAntiPatternsSection() {
66810
68311
  '- **Testing**: Deleting failing tests to "pass"',
66811
68312
  "- **Search**: Firing agents for single-line typos or obvious syntax errors",
66812
68313
  "- **Debugging**: Shotgun debugging, random changes",
66813
- "- **Background Tasks**: `background_cancel(all=true)` \u2014 always cancel individually by taskId",
66814
- "- **Oracle**: Skipping Oracle results when Oracle was launched \u2014 ALWAYS collect via `background_output`"
68314
+ "- **Background Tasks**: Polling `background_output` on running tasks \u2014 end response and wait for notification",
68315
+ "- **Oracle**: Delivering answer without collecting Oracle results"
66815
68316
  ];
66816
68317
  return `## Anti-Patterns (BLOCKING violations)
66817
68318
 
66818
68319
  ${patterns.join(`
66819
68320
  `)}`;
66820
68321
  }
68322
+ function buildNonClaudePlannerSection(model) {
68323
+ const isNonClaude = !model.toLowerCase().includes("claude");
68324
+ if (!isNonClaude)
68325
+ return "";
68326
+ return `### Plan Agent Dependency (Non-Claude)
68327
+
68328
+ Multi-step task? **ALWAYS consult Plan Agent first.** Do NOT start implementation without a plan.
68329
+
68330
+ - Single-file fix or trivial change \u2192 proceed directly
68331
+ - Anything else (2+ steps, unclear scope, architecture) \u2192 \`task(subagent_type="plan", ...)\` FIRST
68332
+ - Use \`session_id\` to resume the same Plan Agent \u2014 ask follow-up questions aggressively
68333
+ - If ANY part of the task is ambiguous, ask Plan Agent before guessing
68334
+
68335
+ Plan Agent returns a structured work breakdown with parallel execution opportunities. Follow it.`;
68336
+ }
66821
68337
  function buildDeepParallelSection(model, categories2) {
66822
68338
  const isNonClaude = !model.toLowerCase().includes("claude");
66823
68339
  const hasDeepCategory = categories2.some((c) => c.name === "deep");
@@ -66825,16 +68341,17 @@ function buildDeepParallelSection(model, categories2) {
66825
68341
  return "";
66826
68342
  return `### Deep Parallel Delegation
66827
68343
 
66828
- For implementation tasks, actively decompose and delegate to \`deep\` category agents in parallel.
68344
+ Delegate EVERY independent unit to a \`deep\` agent in parallel (\`run_in_background=true\`).
68345
+ If a task decomposes into 4 independent units, spawn 4 agents simultaneously \u2014 not 1 at a time.
66829
68346
 
66830
- 1. Break the implementation into independent work units
66831
- 2. Maximize parallel deep agents \u2014 spawn one per independent unit (\`run_in_background=true\`)
66832
- 3. Give each agent a GOAL, not step-by-step instructions \u2014 deep agents explore and solve autonomously
66833
- 4. Collect results, integrate, verify coherence`;
68347
+ 1. Decompose the implementation into independent work units
68348
+ 2. Assign one \`deep\` agent per unit \u2014 all via \`run_in_background=true\`
68349
+ 3. Give each agent a clear GOAL with success criteria, not step-by-step instructions
68350
+ 4. Collect all results, integrate, verify coherence across units`;
66834
68351
  }
66835
68352
 
66836
68353
  // src/agents/sisyphus.ts
66837
- var MODE = "primary";
68354
+ var MODE = "all";
66838
68355
  function buildTaskManagementSection(useTaskSystem) {
66839
68356
  if (useTaskSystem) {
66840
68357
  return `<Task_Management>
@@ -66954,6 +68471,7 @@ function buildDynamicSisyphusPrompt(model, availableAgents, availableTools = [],
66954
68471
  const hardBlocks = buildHardBlocksSection();
66955
68472
  const antiPatterns = buildAntiPatternsSection();
66956
68473
  const deepParallelSection = buildDeepParallelSection(model, availableCategories);
68474
+ const nonClaudePlannerSection = buildNonClaudePlannerSection(model);
66957
68475
  const taskManagementSection = buildTaskManagementSection(useTaskSystem);
66958
68476
  const todoHookNote = useTaskSystem ? "YOUR TASK CREATION WOULD BE TRACKED BY HOOK([SYSTEM REMINDER - TASK CONTINUATION])" : "YOUR TODO CREATION WOULD BE TRACKED BY HOOK([SYSTEM REMINDER - TODO CONTINUATION])";
66959
68477
  return `<Role>
@@ -67110,7 +68628,7 @@ task(subagent_type="explore", run_in_background=true, load_skills=[], descriptio
67110
68628
  // Reference Grep (external)
67111
68629
  task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find JWT security docs", prompt="I'm implementing JWT auth and need current security best practices to choose token storage (httpOnly cookies vs localStorage) and set expiration policy. Find: OWASP auth guidelines, recommended token lifetimes, refresh token rotation strategies, common JWT vulnerabilities. Skip 'what is JWT' tutorials \u2014 production security guidance only.")
67112
68630
  task(subagent_type="librarian", run_in_background=true, load_skills=[], description="Find Express auth patterns", prompt="I'm building Express auth middleware and need production-quality patterns to structure my middleware chain. Find how established Express apps (1000+ stars) handle: middleware ordering, token refresh, role-based access control, auth error propagation. Skip basic tutorials \u2014 I need battle-tested patterns with proper error handling.")
67113
- // Continue working immediately. Collect with background_output when needed.
68631
+ // Continue working immediately. System notifies on completion \u2014 collect with background_output then.
67114
68632
 
67115
68633
  // WRONG: Sequential or blocking
67116
68634
  result = task(..., run_in_background=false) // Never wait synchronously for explore/librarian
@@ -67118,10 +68636,10 @@ result = task(..., run_in_background=false) // Never wait synchronously for exp
67118
68636
 
67119
68637
  ### Background Result Collection:
67120
68638
  1. Launch parallel agents \u2192 receive task_ids
67121
- 2. Continue immediate work (explore, librarian results)
67122
- 3. When results needed: \`background_output(task_id="...")\`
67123
- 4. **If Oracle is running**: STOP all other output. Follow Oracle Completion Protocol in <Oracle_Usage>.
67124
- 5. Cleanup: Cancel disposable tasks (explore, librarian) individually via \`background_cancel(taskId="...")\`. Never use \`background_cancel(all=true)\`.
68639
+ 2. Continue immediate work
68640
+ 3. System sends \`<system-reminder>\` on each task completion \u2014 then call \`background_output(task_id="...")\`
68641
+ 4. Need results not yet ready? **End your response.** The notification will trigger your next turn.
68642
+ 5. Cleanup: Cancel disposable tasks individually via \`background_cancel(taskId="...")\`
67125
68643
 
67126
68644
  ### Search Stop Conditions
67127
68645
 
@@ -67145,6 +68663,8 @@ STOP searching when:
67145
68663
 
67146
68664
  ${categorySkillsGuide}
67147
68665
 
68666
+ ${nonClaudePlannerSection}
68667
+
67148
68668
  ${deepParallelSection}
67149
68669
 
67150
68670
  ${delegationTable}
@@ -67258,9 +68778,8 @@ If verification fails:
67258
68778
  3. Report: "Done. Note: found N pre-existing lint errors unrelated to my changes."
67259
68779
 
67260
68780
  ### Before Delivering Final Answer:
67261
- - **If Oracle is running**: STOP. Follow Oracle Completion Protocol in <Oracle_Usage>. Do NOT deliver any answer.
67262
- - Cancel disposable background tasks (explore, librarian) individually via \`background_cancel(taskId="...")\`.
67263
- - **Never use \`background_cancel(all=true)\`.**
68781
+ - If Oracle is running: **end your response** and wait for the completion notification first.
68782
+ - Cancel disposable background tasks individually via \`background_cancel(taskId="...")\`.
67264
68783
  </Behavior_Instructions>
67265
68784
 
67266
68785
  ${oracleSection}
@@ -67333,10 +68852,16 @@ function createSisyphusAgent(model, availableAgents, availableToolNames, availab
67333
68852
  ${buildGeminiIntentGateEnforcement()}
67334
68853
 
67335
68854
  ${buildGeminiToolMandate()}`);
67336
- prompt += `
67337
- ` + buildGeminiDelegationOverride();
67338
- prompt += `
67339
- ` + buildGeminiVerificationOverride();
68855
+ prompt = prompt.replace("</tool_usage_rules>", `</tool_usage_rules>
68856
+
68857
+ ${buildGeminiToolGuide()}
68858
+
68859
+ ${buildGeminiToolCallExamples()}`);
68860
+ prompt = prompt.replace("<Constraints>", `${buildGeminiDelegationOverride()}
68861
+
68862
+ ${buildGeminiVerificationOverride()}
68863
+
68864
+ <Constraints>`);
67340
68865
  }
67341
68866
  const permission = {
67342
68867
  question: "allow",
@@ -69544,7 +71069,7 @@ ${agentRows.join(`
69544
71069
  }
69545
71070
 
69546
71071
  // src/agents/atlas/agent.ts
69547
- var MODE7 = "primary";
71072
+ var MODE7 = "all";
69548
71073
  function getAtlasPromptSource(model) {
69549
71074
  if (model && isGptModel(model)) {
69550
71075
  return "gpt";
@@ -69585,18 +71110,13 @@ function buildDynamicOrchestratorPrompt(ctx) {
69585
71110
  return basePrompt.replace("{CATEGORY_SECTION}", categorySection).replace("{AGENT_SECTION}", agentSection).replace("{DECISION_MATRIX}", decisionMatrix).replace("{SKILLS_SECTION}", skillsSection).replace("{{CATEGORY_SKILLS_DELEGATION_GUIDE}}", categorySkillsGuide);
69586
71111
  }
69587
71112
  function createAtlasAgent(ctx) {
69588
- const restrictions = createAgentToolRestrictions([
69589
- "task",
69590
- "call_omo_agent"
69591
- ]);
69592
71113
  const baseConfig = {
69593
71114
  description: "Orchestrates work via task() to complete ALL tasks in a todo list until fully done. (Atlas - OhMyOpenCode)",
69594
71115
  mode: MODE7,
69595
71116
  ...ctx.model ? { model: ctx.model } : {},
69596
71117
  temperature: 0.1,
69597
71118
  prompt: buildDynamicOrchestratorPrompt(ctx),
69598
- color: "#10B981",
69599
- ...restrictions
71119
+ color: "#10B981"
69600
71120
  };
69601
71121
  return baseConfig;
69602
71122
  }
@@ -69847,7 +71367,7 @@ var momusPromptMetadata = {
69847
71367
  };
69848
71368
 
69849
71369
  // src/agents/hephaestus.ts
69850
- var MODE9 = "primary";
71370
+ var MODE9 = "all";
69851
71371
  function buildTodoDisciplineSection(useTaskSystem) {
69852
71372
  if (useTaskSystem) {
69853
71373
  return `## Task Discipline (NON-NEGOTIABLE)
@@ -70377,7 +71897,7 @@ function buildAgent(source, model, categories2, gitMasterConfig, browserProvider
70377
71897
  // src/agents/builtin-agents/resolve-file-uri.ts
70378
71898
  import { existsSync as existsSync64, readFileSync as readFileSync44 } from "fs";
70379
71899
  import { homedir as homedir13 } from "os";
70380
- import { isAbsolute as isAbsolute9, resolve as resolve10 } from "path";
71900
+ import { isAbsolute as isAbsolute9, resolve as resolve13 } from "path";
70381
71901
  function resolvePromptAppend(promptAppend, configDir) {
70382
71902
  if (!promptAppend.startsWith("file://"))
70383
71903
  return promptAppend;
@@ -70386,7 +71906,7 @@ function resolvePromptAppend(promptAppend, configDir) {
70386
71906
  try {
70387
71907
  const decoded = decodeURIComponent(encoded);
70388
71908
  const expanded = decoded.startsWith("~/") ? decoded.replace(/^~\//, `${homedir13()}/`) : decoded;
70389
- filePath = isAbsolute9(expanded) ? expanded : resolve10(configDir ?? process.cwd(), expanded);
71909
+ filePath = isAbsolute9(expanded) ? expanded : resolve13(configDir ?? process.cwd(), expanded);
70390
71910
  } catch {
70391
71911
  return `[WARNING: Malformed file URI (invalid percent-encoding): ${promptAppend}]`;
70392
71912
  }
@@ -70452,25 +71972,10 @@ function applyOverrides(config3, override, mergedCategories, directory) {
70452
71972
 
70453
71973
  // src/agents/env-context.ts
70454
71974
  function createEnvContext() {
70455
- const now = new Date;
70456
71975
  const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
70457
71976
  const locale = Intl.DateTimeFormat().resolvedOptions().locale;
70458
- const dateStr = now.toLocaleDateString(locale, {
70459
- weekday: "short",
70460
- year: "numeric",
70461
- month: "short",
70462
- day: "numeric"
70463
- });
70464
- const timeStr = now.toLocaleTimeString(locale, {
70465
- hour: "2-digit",
70466
- minute: "2-digit",
70467
- second: "2-digit",
70468
- hour12: true
70469
- });
70470
71977
  return `
70471
71978
  <omo-env>
70472
- Current date: ${dateStr}
70473
- Current time: ${timeStr}
70474
71979
  Timezone: ${timezone}
70475
71980
  Locale: ${locale}
70476
71981
  </omo-env>`;
@@ -73701,6 +75206,8 @@ async function applyAgentConfig(params) {
73701
75206
  key,
73702
75207
  value ? migrateAgentConfig(value) : value
73703
75208
  ]));
75209
+ const disabledAgentNames = new Set((migratedDisabledAgents ?? []).map((a) => a.toLowerCase()));
75210
+ const filterDisabledAgents = (agents) => Object.fromEntries(Object.entries(agents).filter(([name]) => !disabledAgentNames.has(name.toLowerCase())));
73704
75211
  const isSisyphusEnabled = params.pluginConfig.sisyphus_agent?.disabled !== true;
73705
75212
  const builderEnabled = params.pluginConfig.sisyphus_agent?.default_builder_enabled ?? false;
73706
75213
  const plannerEnabled = params.pluginConfig.sisyphus_agent?.planner_enabled ?? true;
@@ -73754,9 +75261,9 @@ async function applyAgentConfig(params) {
73754
75261
  params.config.agent = {
73755
75262
  ...agentConfig,
73756
75263
  ...Object.fromEntries(Object.entries(builtinAgents).filter(([key]) => key !== "sisyphus")),
73757
- ...userAgents,
73758
- ...projectAgents,
73759
- ...pluginAgents,
75264
+ ...filterDisabledAgents(userAgents),
75265
+ ...filterDisabledAgents(projectAgents),
75266
+ ...filterDisabledAgents(pluginAgents),
73760
75267
  ...filteredConfigAgents,
73761
75268
  build: { ...migratedBuild, mode: "subagent", hidden: true },
73762
75269
  ...planDemoteConfig ? { plan: planDemoteConfig } : {}
@@ -73764,9 +75271,9 @@ async function applyAgentConfig(params) {
73764
75271
  } else {
73765
75272
  params.config.agent = {
73766
75273
  ...builtinAgents,
73767
- ...userAgents,
73768
- ...projectAgents,
73769
- ...pluginAgents,
75274
+ ...filterDisabledAgents(userAgents),
75275
+ ...filterDisabledAgents(projectAgents),
75276
+ ...filterDisabledAgents(pluginAgents),
73770
75277
  ...configAgent
73771
75278
  };
73772
75279
  }
@@ -74719,6 +76226,7 @@ function applyToolConfig(params) {
74719
76226
  function createConfigHandler(deps) {
74720
76227
  const { ctx, pluginConfig, modelCacheState } = deps;
74721
76228
  return async (config3) => {
76229
+ const formatterConfig = config3.formatter;
74722
76230
  applyProviderConfig({ config: config3, modelCacheState });
74723
76231
  const pluginComponents = await loadPluginComponents({ pluginConfig });
74724
76232
  const agentResult = await applyAgentConfig({
@@ -74730,6 +76238,7 @@ function createConfigHandler(deps) {
74730
76238
  applyToolConfig({ config: config3, pluginConfig, agentResult });
74731
76239
  await applyMcpConfig({ config: config3, pluginConfig, pluginComponents });
74732
76240
  await applyCommandConfig({ config: config3, pluginConfig, ctx, pluginComponents });
76241
+ config3.formatter = formatterConfig;
74733
76242
  log("[config-handler] config handler applied", {
74734
76243
  agentCount: Object.keys(agentResult).length,
74735
76244
  commandCount: Object.keys(config3.command ?? {}).length
@@ -74921,7 +76430,7 @@ function createToolRegistry(args) {
74921
76430
  task_list: createTaskList(pluginConfig),
74922
76431
  task_update: createTaskUpdateTool(pluginConfig, ctx)
74923
76432
  } : {};
74924
- const hashlineEnabled = pluginConfig.hashline_edit ?? true;
76433
+ const hashlineEnabled = pluginConfig.hashline_edit ?? false;
74925
76434
  const hashlineToolsRecord = hashlineEnabled ? { edit: createHashlineEditTool() } : {};
74926
76435
  const allTools = {
74927
76436
  ...builtinTools,
@@ -75140,15 +76649,6 @@ function createChatHeadersHandler(args) {
75140
76649
  };
75141
76650
  }
75142
76651
 
75143
- // src/shared/session-model-state.ts
75144
- var sessionModels = new Map;
75145
- function setSessionModel(sessionID, model) {
75146
- sessionModels.set(sessionID, model);
75147
- }
75148
- function clearSessionModel(sessionID) {
75149
- sessionModels.delete(sessionID);
75150
- }
75151
-
75152
76652
  // src/plugin/ultrawork-db-model-override.ts
75153
76653
  import { Database } from "bun:sqlite";
75154
76654
  import { join as join84 } from "path";
@@ -75313,11 +76813,11 @@ function applyUltraworkModelOverrideOnMessage(pluginConfig, inputAgentName, outp
75313
76813
  const override = resolveUltraworkOverride(pluginConfig, inputAgentName, output, sessionID);
75314
76814
  if (!override)
75315
76815
  return;
76816
+ if (override.variant) {
76817
+ output.message["variant"] = override.variant;
76818
+ output.message["thinking"] = override.variant;
76819
+ }
75316
76820
  if (!override.providerID || !override.modelID) {
75317
- if (override.variant) {
75318
- output.message["variant"] = override.variant;
75319
- output.message["thinking"] = override.variant;
75320
- }
75321
76821
  return;
75322
76822
  }
75323
76823
  const targetModel = { providerID: override.providerID, modelID: override.modelID };
@@ -75329,10 +76829,6 @@ function applyUltraworkModelOverrideOnMessage(pluginConfig, inputAgentName, outp
75329
76829
  if (!messageId) {
75330
76830
  log("[ultrawork-model-override] No message ID found, falling back to direct mutation");
75331
76831
  output.message.model = targetModel;
75332
- if (override.variant) {
75333
- output.message["variant"] = override.variant;
75334
- output.message["thinking"] = override.variant;
75335
- }
75336
76832
  return;
75337
76833
  }
75338
76834
  const fromModel = output.message.model?.modelID ?? "unknown";
@@ -75404,8 +76900,10 @@ function createChatMessageHandler3(args) {
75404
76900
  setSessionModel(input.sessionID, input.model);
75405
76901
  }
75406
76902
  await hooks2.stopContinuationGuard?.["chat.message"]?.(input);
76903
+ await hooks2.backgroundNotificationHook?.["chat.message"]?.(input, output);
75407
76904
  await hooks2.runtimeFallback?.["chat.message"]?.(input, output);
75408
76905
  await hooks2.keywordDetector?.["chat.message"]?.(input, output);
76906
+ await hooks2.thinkMode?.["chat.message"]?.(input, output);
75409
76907
  await hooks2.claudeCodeHooks?.["chat.message"]?.(input, output);
75410
76908
  await hooks2.autoSlashCommand?.["chat.message"]?.(input, output);
75411
76909
  await hooks2.noSisyphusGpt?.["chat.message"]?.(input, output);
@@ -75856,6 +77354,7 @@ function createToolExecuteAfterHandler3(args) {
75856
77354
  await hooks2.delegateTaskRetry?.["tool.execute.after"]?.(input, output);
75857
77355
  await hooks2.atlasHook?.["tool.execute.after"]?.(input, output);
75858
77356
  await hooks2.taskResumeInfo?.["tool.execute.after"]?.(input, output);
77357
+ await hooks2.readImageResizer?.["tool.execute.after"]?.(input, output);
75859
77358
  await hooks2.hashlineReadEnhancer?.["tool.execute.after"]?.(input, output);
75860
77359
  await hooks2.jsonErrorRecovery?.["tool.execute.after"]?.(input, output);
75861
77360
  };