oh-my-opencode 3.8.5 → 3.9.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.
- package/bin/oh-my-opencode.js +96 -34
- package/bin/platform.d.ts +14 -0
- package/bin/platform.js +44 -0
- package/bin/platform.test.ts +56 -1
- package/dist/agents/atlas/agent.d.ts +1 -1
- package/dist/agents/env-context.d.ts +1 -1
- package/dist/agents/hephaestus.d.ts +1 -1
- package/dist/agents/sisyphus.d.ts +1 -1
- package/dist/cli/config-manager/antigravity-provider-configuration.d.ts +3 -3
- package/dist/cli/index.js +199 -59
- package/dist/cli/run/event-state.d.ts +2 -0
- package/dist/cli/run/poll-for-completion.d.ts +2 -0
- package/dist/config/schema/agent-overrides.d.ts +1 -0
- package/dist/config/schema/categories.d.ts +2 -0
- package/dist/config/schema/oh-my-opencode-config.d.ts +3 -12
- package/dist/features/background-agent/manager.d.ts +9 -0
- package/dist/features/boulder-state/storage.d.ts +1 -1
- package/dist/features/boulder-state/types.d.ts +2 -0
- package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
- package/dist/hooks/anthropic-context-window-limit-recovery/types.d.ts +1 -0
- package/dist/hooks/atlas/boulder-continuation-injector.d.ts +1 -0
- package/dist/hooks/atlas/types.d.ts +1 -0
- package/dist/hooks/background-notification/hook.d.ts +11 -0
- package/dist/hooks/no-hephaestus-non-gpt/hook.d.ts +5 -1
- package/dist/hooks/ralph-loop/completion-promise-detector.d.ts +1 -0
- package/dist/hooks/ralph-loop/loop-state-controller.d.ts +2 -0
- package/dist/hooks/ralph-loop/ralph-loop-hook.d.ts +1 -0
- package/dist/hooks/ralph-loop/types.d.ts +1 -0
- package/dist/hooks/session-notification.d.ts +1 -0
- package/dist/hooks/start-work/index.d.ts +3 -0
- package/dist/hooks/start-work/parse-user-request.d.ts +5 -0
- package/dist/hooks/start-work/worktree-detector.d.ts +1 -0
- package/dist/hooks/stop-continuation-guard/hook.d.ts +6 -1
- package/dist/hooks/think-mode/hook.d.ts +14 -2
- package/dist/hooks/think-mode/switcher.d.ts +0 -56
- package/dist/hooks/think-mode/types.d.ts +1 -15
- package/dist/hooks/todo-continuation-enforcer/constants.d.ts +1 -1
- package/dist/hooks/todo-continuation-enforcer/pending-question-detection.d.ts +14 -0
- package/dist/index.js +1310 -503
- package/dist/oh-my-opencode.schema.json +9 -13
- package/dist/shared/model-suggestion-retry.d.ts +4 -2
- package/dist/shared/prompt-timeout-context.d.ts +12 -0
- package/dist/shared/spawn-with-windows-hide.d.ts +15 -0
- package/dist/tools/delegate-task/category-resolver.d.ts +1 -0
- package/dist/tools/delegate-task/skill-resolver.d.ts +1 -0
- package/dist/tools/delegate-task/token-limiter.d.ts +4 -0
- package/dist/tools/delegate-task/types.d.ts +9 -0
- package/dist/tools/hashline-edit/tool-description.d.ts +1 -1
- package/dist/tools/hashline-edit/validation.d.ts +1 -0
- package/package.json +13 -8
- 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: (
|
|
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: (
|
|
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" },
|
|
@@ -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 =
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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", "
|
|
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
|
}
|
|
@@ -18545,6 +18565,42 @@ async function enforceMainPaneWidth(mainPaneId, windowWidth, mainPaneSizeOrOptio
|
|
|
18545
18565
|
}
|
|
18546
18566
|
// src/shared/model-suggestion-retry.ts
|
|
18547
18567
|
init_logger();
|
|
18568
|
+
|
|
18569
|
+
// src/shared/prompt-timeout-context.ts
|
|
18570
|
+
var PROMPT_TIMEOUT_MS = 120000;
|
|
18571
|
+
function createPromptTimeoutContext(args, timeoutMs) {
|
|
18572
|
+
const timeoutController = new AbortController;
|
|
18573
|
+
let timeoutID = null;
|
|
18574
|
+
let timedOut = false;
|
|
18575
|
+
const abortOnUpstreamSignal = () => {
|
|
18576
|
+
timeoutController.abort(args.signal?.reason);
|
|
18577
|
+
};
|
|
18578
|
+
if (args.signal) {
|
|
18579
|
+
if (args.signal.aborted) {
|
|
18580
|
+
timeoutController.abort(args.signal.reason);
|
|
18581
|
+
} else {
|
|
18582
|
+
args.signal.addEventListener("abort", abortOnUpstreamSignal, { once: true });
|
|
18583
|
+
}
|
|
18584
|
+
}
|
|
18585
|
+
timeoutID = setTimeout(() => {
|
|
18586
|
+
timedOut = true;
|
|
18587
|
+
timeoutController.abort(new Error(`prompt timed out after ${timeoutMs}ms`));
|
|
18588
|
+
}, timeoutMs);
|
|
18589
|
+
return {
|
|
18590
|
+
signal: timeoutController.signal,
|
|
18591
|
+
wasTimedOut: () => timedOut,
|
|
18592
|
+
cleanup: () => {
|
|
18593
|
+
if (timeoutID !== null) {
|
|
18594
|
+
clearTimeout(timeoutID);
|
|
18595
|
+
}
|
|
18596
|
+
if (args.signal) {
|
|
18597
|
+
args.signal.removeEventListener("abort", abortOnUpstreamSignal);
|
|
18598
|
+
}
|
|
18599
|
+
}
|
|
18600
|
+
};
|
|
18601
|
+
}
|
|
18602
|
+
|
|
18603
|
+
// src/shared/model-suggestion-retry.ts
|
|
18548
18604
|
function extractMessage(error) {
|
|
18549
18605
|
if (typeof error === "string")
|
|
18550
18606
|
return error;
|
|
@@ -18602,24 +18658,47 @@ function parseModelSuggestion(error) {
|
|
|
18602
18658
|
}
|
|
18603
18659
|
return null;
|
|
18604
18660
|
}
|
|
18605
|
-
async function promptWithModelSuggestionRetry(client, args) {
|
|
18606
|
-
const
|
|
18607
|
-
|
|
18608
|
-
const
|
|
18609
|
-
|
|
18610
|
-
|
|
18611
|
-
}, 120000);
|
|
18661
|
+
async function promptWithModelSuggestionRetry(client, args, options = {}) {
|
|
18662
|
+
const timeoutMs = options.timeoutMs ?? PROMPT_TIMEOUT_MS;
|
|
18663
|
+
const timeoutContext = createPromptTimeoutContext(args, timeoutMs);
|
|
18664
|
+
const promptPromise = client.session.promptAsync({
|
|
18665
|
+
...args,
|
|
18666
|
+
signal: timeoutContext.signal
|
|
18612
18667
|
});
|
|
18613
18668
|
try {
|
|
18614
|
-
await
|
|
18669
|
+
await promptPromise;
|
|
18670
|
+
if (timeoutContext.wasTimedOut()) {
|
|
18671
|
+
throw new Error(`promptAsync timed out after ${timeoutMs}ms`);
|
|
18672
|
+
}
|
|
18673
|
+
} catch (error) {
|
|
18674
|
+
if (timeoutContext.wasTimedOut()) {
|
|
18675
|
+
throw new Error(`promptAsync timed out after ${timeoutMs}ms`);
|
|
18676
|
+
}
|
|
18677
|
+
throw error;
|
|
18615
18678
|
} finally {
|
|
18616
|
-
|
|
18617
|
-
clearTimeout(timeoutID);
|
|
18679
|
+
timeoutContext.cleanup();
|
|
18618
18680
|
}
|
|
18619
18681
|
}
|
|
18620
|
-
async function promptSyncWithModelSuggestionRetry(client, args) {
|
|
18682
|
+
async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
|
|
18683
|
+
const timeoutMs = options.timeoutMs ?? PROMPT_TIMEOUT_MS;
|
|
18621
18684
|
try {
|
|
18622
|
-
|
|
18685
|
+
const timeoutContext = createPromptTimeoutContext(args, timeoutMs);
|
|
18686
|
+
try {
|
|
18687
|
+
await client.session.prompt({
|
|
18688
|
+
...args,
|
|
18689
|
+
signal: timeoutContext.signal
|
|
18690
|
+
});
|
|
18691
|
+
if (timeoutContext.wasTimedOut()) {
|
|
18692
|
+
throw new Error(`prompt timed out after ${timeoutMs}ms`);
|
|
18693
|
+
}
|
|
18694
|
+
} catch (error) {
|
|
18695
|
+
if (timeoutContext.wasTimedOut()) {
|
|
18696
|
+
throw new Error(`prompt timed out after ${timeoutMs}ms`);
|
|
18697
|
+
}
|
|
18698
|
+
throw error;
|
|
18699
|
+
} finally {
|
|
18700
|
+
timeoutContext.cleanup();
|
|
18701
|
+
}
|
|
18623
18702
|
} catch (error) {
|
|
18624
18703
|
const suggestion = parseModelSuggestion(error);
|
|
18625
18704
|
if (!suggestion || !args.body.model) {
|
|
@@ -18629,7 +18708,7 @@ async function promptSyncWithModelSuggestionRetry(client, args) {
|
|
|
18629
18708
|
original: `${suggestion.providerID}/${suggestion.modelID}`,
|
|
18630
18709
|
suggested: suggestion.suggestion
|
|
18631
18710
|
});
|
|
18632
|
-
|
|
18711
|
+
const retryArgs = {
|
|
18633
18712
|
...args,
|
|
18634
18713
|
body: {
|
|
18635
18714
|
...args.body,
|
|
@@ -18638,7 +18717,24 @@ async function promptSyncWithModelSuggestionRetry(client, args) {
|
|
|
18638
18717
|
modelID: suggestion.suggestion
|
|
18639
18718
|
}
|
|
18640
18719
|
}
|
|
18641
|
-
}
|
|
18720
|
+
};
|
|
18721
|
+
const timeoutContext = createPromptTimeoutContext(retryArgs, timeoutMs);
|
|
18722
|
+
try {
|
|
18723
|
+
await client.session.prompt({
|
|
18724
|
+
...retryArgs,
|
|
18725
|
+
signal: timeoutContext.signal
|
|
18726
|
+
});
|
|
18727
|
+
if (timeoutContext.wasTimedOut()) {
|
|
18728
|
+
throw new Error(`prompt timed out after ${timeoutMs}ms`);
|
|
18729
|
+
}
|
|
18730
|
+
} catch (retryError) {
|
|
18731
|
+
if (timeoutContext.wasTimedOut()) {
|
|
18732
|
+
throw new Error(`prompt timed out after ${timeoutMs}ms`);
|
|
18733
|
+
}
|
|
18734
|
+
throw retryError;
|
|
18735
|
+
} finally {
|
|
18736
|
+
timeoutContext.cleanup();
|
|
18737
|
+
}
|
|
18642
18738
|
}
|
|
18643
18739
|
}
|
|
18644
18740
|
// src/shared/opencode-server-auth.ts
|
|
@@ -19159,9 +19255,31 @@ function isLastAssistantMessageAborted(messages) {
|
|
|
19159
19255
|
return errorName === "MessageAbortedError" || errorName === "AbortError";
|
|
19160
19256
|
}
|
|
19161
19257
|
|
|
19258
|
+
// src/hooks/todo-continuation-enforcer/pending-question-detection.ts
|
|
19259
|
+
init_logger();
|
|
19260
|
+
function hasUnansweredQuestion(messages) {
|
|
19261
|
+
if (!messages || messages.length === 0)
|
|
19262
|
+
return false;
|
|
19263
|
+
for (let i2 = messages.length - 1;i2 >= 0; i2--) {
|
|
19264
|
+
const msg = messages[i2];
|
|
19265
|
+
const role = msg.info?.role ?? msg.role;
|
|
19266
|
+
if (role === "user")
|
|
19267
|
+
return false;
|
|
19268
|
+
if (role === "assistant" && msg.parts) {
|
|
19269
|
+
const hasQuestion = msg.parts.some((part) => (part.type === "tool_use" || part.type === "tool-invocation") && (part.name === "question" || part.toolName === "question"));
|
|
19270
|
+
if (hasQuestion) {
|
|
19271
|
+
log(`[${HOOK_NAME}] Detected pending question tool in last assistant message`);
|
|
19272
|
+
return true;
|
|
19273
|
+
}
|
|
19274
|
+
return false;
|
|
19275
|
+
}
|
|
19276
|
+
}
|
|
19277
|
+
return false;
|
|
19278
|
+
}
|
|
19279
|
+
|
|
19162
19280
|
// src/hooks/todo-continuation-enforcer/todo.ts
|
|
19163
19281
|
function getIncompleteCount(todos) {
|
|
19164
|
-
return todos.filter((todo) => todo.status !== "completed" && todo.status !== "cancelled").length;
|
|
19282
|
+
return todos.filter((todo) => todo.status !== "completed" && todo.status !== "cancelled" && todo.status !== "blocked" && todo.status !== "deleted").length;
|
|
19165
19283
|
}
|
|
19166
19284
|
|
|
19167
19285
|
// src/hooks/todo-continuation-enforcer/countdown.ts
|
|
@@ -19377,6 +19495,10 @@ async function handleSessionIdle(args) {
|
|
|
19377
19495
|
log(`[${HOOK_NAME}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
|
|
19378
19496
|
return;
|
|
19379
19497
|
}
|
|
19498
|
+
if (hasUnansweredQuestion(messages)) {
|
|
19499
|
+
log(`[${HOOK_NAME}] Skipped: pending question awaiting user response`, { sessionID });
|
|
19500
|
+
return;
|
|
19501
|
+
}
|
|
19380
19502
|
} catch (error) {
|
|
19381
19503
|
log(`[${HOOK_NAME}] Messages fetch failed, continuing`, { sessionID, error: String(error) });
|
|
19382
19504
|
}
|
|
@@ -20103,6 +20225,7 @@ function createSessionNotification(ctx, config = {}) {
|
|
|
20103
20225
|
idleConfirmationDelay: 1500,
|
|
20104
20226
|
skipIfIncompleteTodos: true,
|
|
20105
20227
|
maxTrackedSessions: 100,
|
|
20228
|
+
enforceMainSessionFilter: true,
|
|
20106
20229
|
...config
|
|
20107
20230
|
};
|
|
20108
20231
|
const scheduler = createIdleNotificationScheduler({
|
|
@@ -20135,9 +20258,11 @@ function createSessionNotification(ctx, config = {}) {
|
|
|
20135
20258
|
const shouldNotifyForSession = (sessionID) => {
|
|
20136
20259
|
if (subagentSessions.has(sessionID))
|
|
20137
20260
|
return false;
|
|
20138
|
-
|
|
20139
|
-
|
|
20140
|
-
|
|
20261
|
+
if (mergedConfig.enforceMainSessionFilter) {
|
|
20262
|
+
const mainSessionID = getMainSessionID();
|
|
20263
|
+
if (mainSessionID && sessionID !== mainSessionID)
|
|
20264
|
+
return false;
|
|
20265
|
+
}
|
|
20141
20266
|
return true;
|
|
20142
20267
|
};
|
|
20143
20268
|
const getEventToolName = (properties) => {
|
|
@@ -34526,7 +34651,7 @@ var TRUNCATE_CONFIG = {
|
|
|
34526
34651
|
function getOrCreateRetryState(autoCompactState, sessionID) {
|
|
34527
34652
|
let state2 = autoCompactState.retryStateBySession.get(sessionID);
|
|
34528
34653
|
if (!state2) {
|
|
34529
|
-
state2 = { attempt: 0, lastAttemptTime: 0 };
|
|
34654
|
+
state2 = { attempt: 0, lastAttemptTime: 0, firstAttemptTime: 0 };
|
|
34530
34655
|
autoCompactState.retryStateBySession.set(sessionID, state2);
|
|
34531
34656
|
}
|
|
34532
34657
|
return state2;
|
|
@@ -35222,8 +35347,26 @@ function resolveCompactionModel(pluginConfig, sessionID, originalProviderID, ori
|
|
|
35222
35347
|
}
|
|
35223
35348
|
|
|
35224
35349
|
// src/hooks/anthropic-context-window-limit-recovery/summarize-retry-strategy.ts
|
|
35350
|
+
var SUMMARIZE_RETRY_TOTAL_TIMEOUT_MS = 120000;
|
|
35225
35351
|
async function runSummarizeRetryStrategy(params) {
|
|
35226
35352
|
const retryState = getOrCreateRetryState(params.autoCompactState, params.sessionID);
|
|
35353
|
+
const now = Date.now();
|
|
35354
|
+
if (retryState.firstAttemptTime === 0) {
|
|
35355
|
+
retryState.firstAttemptTime = now;
|
|
35356
|
+
}
|
|
35357
|
+
const elapsedTimeMs = now - retryState.firstAttemptTime;
|
|
35358
|
+
if (elapsedTimeMs >= SUMMARIZE_RETRY_TOTAL_TIMEOUT_MS) {
|
|
35359
|
+
clearSessionState(params.autoCompactState, params.sessionID);
|
|
35360
|
+
await params.client.tui.showToast({
|
|
35361
|
+
body: {
|
|
35362
|
+
title: "Auto Compact Timed Out",
|
|
35363
|
+
message: "Compaction retries exceeded the timeout window. Please start a new session.",
|
|
35364
|
+
variant: "error",
|
|
35365
|
+
duration: 5000
|
|
35366
|
+
}
|
|
35367
|
+
}).catch(() => {});
|
|
35368
|
+
return;
|
|
35369
|
+
}
|
|
35227
35370
|
if (params.errorType?.includes("non-empty content")) {
|
|
35228
35371
|
const attempt = getEmptyContentAttempt(params.autoCompactState, params.sessionID);
|
|
35229
35372
|
if (attempt < 3) {
|
|
@@ -35253,6 +35396,7 @@ async function runSummarizeRetryStrategy(params) {
|
|
|
35253
35396
|
}
|
|
35254
35397
|
if (Date.now() - retryState.lastAttemptTime > 300000) {
|
|
35255
35398
|
retryState.attempt = 0;
|
|
35399
|
+
retryState.firstAttemptTime = Date.now();
|
|
35256
35400
|
params.autoCompactState.truncateStateBySession.delete(params.sessionID);
|
|
35257
35401
|
}
|
|
35258
35402
|
if (retryState.attempt < RETRY_CONFIG.maxAttempts) {
|
|
@@ -35280,8 +35424,21 @@ async function runSummarizeRetryStrategy(params) {
|
|
|
35280
35424
|
});
|
|
35281
35425
|
return;
|
|
35282
35426
|
} catch {
|
|
35427
|
+
const remainingTimeMs = SUMMARIZE_RETRY_TOTAL_TIMEOUT_MS - (Date.now() - retryState.firstAttemptTime);
|
|
35428
|
+
if (remainingTimeMs <= 0) {
|
|
35429
|
+
clearSessionState(params.autoCompactState, params.sessionID);
|
|
35430
|
+
await params.client.tui.showToast({
|
|
35431
|
+
body: {
|
|
35432
|
+
title: "Auto Compact Timed Out",
|
|
35433
|
+
message: "Compaction retries exceeded the timeout window. Please start a new session.",
|
|
35434
|
+
variant: "error",
|
|
35435
|
+
duration: 5000
|
|
35436
|
+
}
|
|
35437
|
+
}).catch(() => {});
|
|
35438
|
+
return;
|
|
35439
|
+
}
|
|
35283
35440
|
const delay3 = RETRY_CONFIG.initialDelayMs * Math.pow(RETRY_CONFIG.backoffFactor, retryState.attempt - 1);
|
|
35284
|
-
const cappedDelay = Math.min(delay3, RETRY_CONFIG.maxDelayMs);
|
|
35441
|
+
const cappedDelay = Math.min(delay3, RETRY_CONFIG.maxDelayMs, remainingTimeMs);
|
|
35285
35442
|
setTimeout(() => {
|
|
35286
35443
|
runSummarizeRetryStrategy(params);
|
|
35287
35444
|
}, cappedDelay);
|
|
@@ -35877,24 +36034,11 @@ function extractModelPrefix(modelID) {
|
|
|
35877
36034
|
function normalizeModelID(modelID) {
|
|
35878
36035
|
return modelID.replace(/\.(\d+)/g, "-$1");
|
|
35879
36036
|
}
|
|
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
36037
|
var HIGH_VARIANT_MAP = {
|
|
35894
36038
|
"claude-sonnet-4-6": "claude-sonnet-4-6-high",
|
|
35895
36039
|
"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",
|
|
36040
|
+
"gemini-3-1-pro": "gemini-3-1-pro-high",
|
|
36041
|
+
"gemini-3-1-pro-low": "gemini-3-1-pro-high",
|
|
35898
36042
|
"gemini-3-flash": "gemini-3-flash-high",
|
|
35899
36043
|
"gpt-5": "gpt-5-high",
|
|
35900
36044
|
"gpt-5-mini": "gpt-5-mini-high",
|
|
@@ -35909,74 +36053,10 @@ var HIGH_VARIANT_MAP = {
|
|
|
35909
36053
|
"gpt-5-2": "gpt-5-2-high",
|
|
35910
36054
|
"gpt-5-2-chat-latest": "gpt-5-2-chat-latest-high",
|
|
35911
36055
|
"gpt-5-2-pro": "gpt-5-2-pro-high",
|
|
35912
|
-
"antigravity-gemini-3-pro": "antigravity-gemini-3-pro-high",
|
|
36056
|
+
"antigravity-gemini-3-1-pro": "antigravity-gemini-3-1-pro-high",
|
|
35913
36057
|
"antigravity-gemini-3-flash": "antigravity-gemini-3-flash-high"
|
|
35914
36058
|
};
|
|
35915
36059
|
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
36060
|
function getHighVariant(modelID) {
|
|
35981
36061
|
const normalized = normalizeModelID(modelID);
|
|
35982
36062
|
const { prefix, base } = extractModelPrefix(normalized);
|
|
@@ -35994,65 +36074,28 @@ function isAlreadyHighVariant(modelID) {
|
|
|
35994
36074
|
const { base } = extractModelPrefix(normalized);
|
|
35995
36075
|
return ALREADY_HIGH.has(base) || base.endsWith("-high");
|
|
35996
36076
|
}
|
|
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
36077
|
// src/hooks/think-mode/hook.ts
|
|
36017
36078
|
var thinkModeState = new Map;
|
|
36018
36079
|
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
36080
|
return {
|
|
36043
|
-
"chat.
|
|
36081
|
+
"chat.message": async (input, output) => {
|
|
36044
36082
|
const promptText = extractPromptText(output.parts);
|
|
36083
|
+
const sessionID = input.sessionID;
|
|
36045
36084
|
const state3 = {
|
|
36046
36085
|
requested: false,
|
|
36047
36086
|
modelSwitched: false,
|
|
36048
|
-
|
|
36087
|
+
variantSet: false
|
|
36049
36088
|
};
|
|
36050
36089
|
if (!detectThinkKeyword(promptText)) {
|
|
36051
36090
|
thinkModeState.set(sessionID, state3);
|
|
36052
36091
|
return;
|
|
36053
36092
|
}
|
|
36054
36093
|
state3.requested = true;
|
|
36055
|
-
|
|
36094
|
+
if (typeof output.message.variant === "string") {
|
|
36095
|
+
thinkModeState.set(sessionID, state3);
|
|
36096
|
+
return;
|
|
36097
|
+
}
|
|
36098
|
+
const currentModel = input.model;
|
|
36056
36099
|
if (!currentModel) {
|
|
36057
36100
|
thinkModeState.set(sessionID, state3);
|
|
36058
36101
|
return;
|
|
@@ -36064,50 +36107,20 @@ function createThinkModeHook() {
|
|
|
36064
36107
|
return;
|
|
36065
36108
|
}
|
|
36066
36109
|
const highVariant = getHighVariant(currentModel.modelID);
|
|
36067
|
-
const thinkingConfig = getThinkingConfig(currentModel.providerID, currentModel.modelID);
|
|
36068
36110
|
if (highVariant) {
|
|
36069
36111
|
output.message.model = {
|
|
36070
36112
|
providerID: currentModel.providerID,
|
|
36071
36113
|
modelID: highVariant
|
|
36072
36114
|
};
|
|
36115
|
+
output.message.variant = "high";
|
|
36073
36116
|
state3.modelSwitched = true;
|
|
36117
|
+
state3.variantSet = true;
|
|
36074
36118
|
log("Think mode: model switched to high variant", {
|
|
36075
36119
|
sessionID,
|
|
36076
36120
|
from: currentModel.modelID,
|
|
36077
36121
|
to: highVariant
|
|
36078
36122
|
});
|
|
36079
36123
|
}
|
|
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
36124
|
thinkModeState.set(sessionID, state3);
|
|
36112
36125
|
},
|
|
36113
36126
|
event: async ({ event }) => {
|
|
@@ -38165,7 +38178,11 @@ function createBackgroundNotificationHook(manager) {
|
|
|
38165
38178
|
const eventHandler = async ({ event }) => {
|
|
38166
38179
|
manager.handleEvent(event);
|
|
38167
38180
|
};
|
|
38181
|
+
const chatMessageHandler = async (input, output) => {
|
|
38182
|
+
manager.injectPendingNotificationsIntoChatMessage(output, input.sessionID);
|
|
38183
|
+
};
|
|
38168
38184
|
return {
|
|
38185
|
+
"chat.message": chatMessageHandler,
|
|
38169
38186
|
event: eventHandler
|
|
38170
38187
|
};
|
|
38171
38188
|
}
|
|
@@ -38481,6 +38498,64 @@ function getConfigContext() {
|
|
|
38481
38498
|
function getConfigDir() {
|
|
38482
38499
|
return getConfigContext().paths.configDir;
|
|
38483
38500
|
}
|
|
38501
|
+
// src/shared/spawn-with-windows-hide.ts
|
|
38502
|
+
var {spawn: bunSpawn } = globalThis.Bun;
|
|
38503
|
+
import { spawn as nodeSpawn } from "child_process";
|
|
38504
|
+
import { Readable } from "stream";
|
|
38505
|
+
function toReadableStream(stream) {
|
|
38506
|
+
if (!stream) {
|
|
38507
|
+
return;
|
|
38508
|
+
}
|
|
38509
|
+
return Readable.toWeb(stream);
|
|
38510
|
+
}
|
|
38511
|
+
function wrapNodeProcess(proc) {
|
|
38512
|
+
let resolveExited;
|
|
38513
|
+
let exitCode = null;
|
|
38514
|
+
const exited = new Promise((resolve5) => {
|
|
38515
|
+
resolveExited = resolve5;
|
|
38516
|
+
});
|
|
38517
|
+
proc.on("exit", (code) => {
|
|
38518
|
+
exitCode = code ?? 1;
|
|
38519
|
+
resolveExited(exitCode);
|
|
38520
|
+
});
|
|
38521
|
+
proc.on("error", () => {
|
|
38522
|
+
if (exitCode === null) {
|
|
38523
|
+
exitCode = 1;
|
|
38524
|
+
resolveExited(1);
|
|
38525
|
+
}
|
|
38526
|
+
});
|
|
38527
|
+
return {
|
|
38528
|
+
get exitCode() {
|
|
38529
|
+
return exitCode;
|
|
38530
|
+
},
|
|
38531
|
+
exited,
|
|
38532
|
+
stdout: toReadableStream(proc.stdout),
|
|
38533
|
+
stderr: toReadableStream(proc.stderr),
|
|
38534
|
+
kill(signal) {
|
|
38535
|
+
try {
|
|
38536
|
+
if (!signal) {
|
|
38537
|
+
proc.kill();
|
|
38538
|
+
return;
|
|
38539
|
+
}
|
|
38540
|
+
proc.kill(signal);
|
|
38541
|
+
} catch {}
|
|
38542
|
+
}
|
|
38543
|
+
};
|
|
38544
|
+
}
|
|
38545
|
+
function spawnWithWindowsHide(command, options) {
|
|
38546
|
+
if (process.platform !== "win32") {
|
|
38547
|
+
return bunSpawn(command, options);
|
|
38548
|
+
}
|
|
38549
|
+
const [cmd, ...args] = command;
|
|
38550
|
+
const proc = nodeSpawn(cmd, args, {
|
|
38551
|
+
cwd: options.cwd,
|
|
38552
|
+
env: options.env,
|
|
38553
|
+
stdio: [options.stdin ?? "pipe", options.stdout ?? "pipe", options.stderr ?? "pipe"],
|
|
38554
|
+
windowsHide: true,
|
|
38555
|
+
shell: true
|
|
38556
|
+
});
|
|
38557
|
+
return wrapNodeProcess(proc);
|
|
38558
|
+
}
|
|
38484
38559
|
// src/cli/config-manager/bun-install.ts
|
|
38485
38560
|
var BUN_INSTALL_TIMEOUT_SECONDS = 60;
|
|
38486
38561
|
var BUN_INSTALL_TIMEOUT_MS = BUN_INSTALL_TIMEOUT_SECONDS * 1000;
|
|
@@ -38490,7 +38565,7 @@ async function runBunInstall() {
|
|
|
38490
38565
|
}
|
|
38491
38566
|
async function runBunInstallWithDetails() {
|
|
38492
38567
|
try {
|
|
38493
|
-
const proc =
|
|
38568
|
+
const proc = spawnWithWindowsHide(["bun", "install"], {
|
|
38494
38569
|
cwd: getConfigDir(),
|
|
38495
38570
|
stdout: "inherit",
|
|
38496
38571
|
stderr: "inherit"
|
|
@@ -39000,12 +39075,9 @@ function createAgentUsageReminderHook(_ctx) {
|
|
|
39000
39075
|
function extractModelName(model) {
|
|
39001
39076
|
return model.includes("/") ? model.split("/").pop() ?? model : model;
|
|
39002
39077
|
}
|
|
39003
|
-
var GPT_MODEL_PREFIXES = ["gpt-", "gpt4", "o1", "o3", "o4"];
|
|
39004
39078
|
function isGptModel(model) {
|
|
39005
|
-
if (model.startsWith("openai/") || model.startsWith("github-copilot/gpt-"))
|
|
39006
|
-
return true;
|
|
39007
39079
|
const modelName = extractModelName(model).toLowerCase();
|
|
39008
|
-
return
|
|
39080
|
+
return modelName.includes("gpt");
|
|
39009
39081
|
}
|
|
39010
39082
|
var GEMINI_PROVIDERS = ["google/", "google-vertex/"];
|
|
39011
39083
|
function isGeminiModel(model) {
|
|
@@ -40241,7 +40313,7 @@ function isOmoSession(sessionName) {
|
|
|
40241
40313
|
async function killAllTrackedSessions(state3) {
|
|
40242
40314
|
for (const sessionName of state3.tmuxSessions) {
|
|
40243
40315
|
try {
|
|
40244
|
-
const proc =
|
|
40316
|
+
const proc = spawnWithWindowsHide(["tmux", "kill-session", "-t", sessionName], {
|
|
40245
40317
|
stdout: "ignore",
|
|
40246
40318
|
stderr: "ignore"
|
|
40247
40319
|
});
|
|
@@ -40561,6 +40633,7 @@ function readState(directory, customPath) {
|
|
|
40561
40633
|
active: isActive,
|
|
40562
40634
|
iteration: iterationNum,
|
|
40563
40635
|
max_iterations: Number(data.max_iterations) || DEFAULT_MAX_ITERATIONS,
|
|
40636
|
+
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
40637
|
completion_promise: stripQuotes(data.completion_promise) || DEFAULT_COMPLETION_PROMISE,
|
|
40565
40638
|
started_at: stripQuotes(data.started_at) || new Date().toISOString(),
|
|
40566
40639
|
prompt: body.trim(),
|
|
@@ -40584,6 +40657,8 @@ function writeState(directory, state3, customPath) {
|
|
|
40584
40657
|
const ultraworkLine = state3.ultrawork !== undefined ? `ultrawork: ${state3.ultrawork}
|
|
40585
40658
|
` : "";
|
|
40586
40659
|
const strategyLine = state3.strategy ? `strategy: "${state3.strategy}"
|
|
40660
|
+
` : "";
|
|
40661
|
+
const messageCountAtStartLine = typeof state3.message_count_at_start === "number" ? `message_count_at_start: ${state3.message_count_at_start}
|
|
40587
40662
|
` : "";
|
|
40588
40663
|
const content = `---
|
|
40589
40664
|
active: ${state3.active}
|
|
@@ -40591,7 +40666,7 @@ iteration: ${state3.iteration}
|
|
|
40591
40666
|
max_iterations: ${state3.max_iterations}
|
|
40592
40667
|
completion_promise: "${state3.completion_promise}"
|
|
40593
40668
|
started_at: "${state3.started_at}"
|
|
40594
|
-
${sessionIdLine}${ultraworkLine}${strategyLine}---
|
|
40669
|
+
${sessionIdLine}${ultraworkLine}${strategyLine}${messageCountAtStartLine}---
|
|
40595
40670
|
${state3.prompt}
|
|
40596
40671
|
`;
|
|
40597
40672
|
writeFileSync16(filePath, content, "utf-8");
|
|
@@ -40662,6 +40737,7 @@ function createLoopStateController(options) {
|
|
|
40662
40737
|
active: true,
|
|
40663
40738
|
iteration: 1,
|
|
40664
40739
|
max_iterations: loopOptions?.maxIterations ?? config2?.default_max_iterations ?? DEFAULT_MAX_ITERATIONS,
|
|
40740
|
+
message_count_at_start: loopOptions?.messageCountAtStart,
|
|
40665
40741
|
completion_promise: loopOptions?.completionPromise ?? DEFAULT_COMPLETION_PROMISE,
|
|
40666
40742
|
ultrawork: loopOptions?.ultrawork,
|
|
40667
40743
|
strategy: loopOptions?.strategy ?? config2?.default_strategy ?? "continue",
|
|
@@ -40709,6 +40785,17 @@ function createLoopStateController(options) {
|
|
|
40709
40785
|
return null;
|
|
40710
40786
|
}
|
|
40711
40787
|
return state3;
|
|
40788
|
+
},
|
|
40789
|
+
setMessageCountAtStart(sessionID, messageCountAtStart) {
|
|
40790
|
+
const state3 = readState(directory, stateDir);
|
|
40791
|
+
if (!state3 || state3.session_id !== sessionID) {
|
|
40792
|
+
return null;
|
|
40793
|
+
}
|
|
40794
|
+
state3.message_count_at_start = messageCountAtStart;
|
|
40795
|
+
if (!writeState(directory, state3, stateDir)) {
|
|
40796
|
+
return null;
|
|
40797
|
+
}
|
|
40798
|
+
return state3;
|
|
40712
40799
|
}
|
|
40713
40800
|
};
|
|
40714
40801
|
}
|
|
@@ -40779,12 +40866,13 @@ async function detectCompletionInSessionMessages(ctx, options) {
|
|
|
40779
40866
|
const messagesResponse = response;
|
|
40780
40867
|
const responseData = typeof messagesResponse === "object" && messagesResponse !== null && "data" in messagesResponse ? messagesResponse.data : undefined;
|
|
40781
40868
|
const messageArray = Array.isArray(messagesResponse) ? messagesResponse : Array.isArray(responseData) ? responseData : [];
|
|
40782
|
-
const
|
|
40869
|
+
const scopedMessages = typeof options.sinceMessageIndex === "number" && options.sinceMessageIndex >= 0 && options.sinceMessageIndex < messageArray.length ? messageArray.slice(options.sinceMessageIndex) : messageArray;
|
|
40870
|
+
const assistantMessages = scopedMessages.filter((msg) => msg.info?.role === "assistant");
|
|
40783
40871
|
if (assistantMessages.length === 0)
|
|
40784
40872
|
return false;
|
|
40785
40873
|
const pattern = buildPromisePattern(options.promise);
|
|
40786
|
-
|
|
40787
|
-
|
|
40874
|
+
for (let index = assistantMessages.length - 1;index >= 0; index -= 1) {
|
|
40875
|
+
const assistant = assistantMessages[index];
|
|
40788
40876
|
if (!assistant.parts)
|
|
40789
40877
|
continue;
|
|
40790
40878
|
let responseText = "";
|
|
@@ -40936,14 +41024,6 @@ async function continueIteration(ctx, state3, options) {
|
|
|
40936
41024
|
if (!newSessionID) {
|
|
40937
41025
|
return;
|
|
40938
41026
|
}
|
|
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
41027
|
await injectContinuationPrompt(ctx, {
|
|
40948
41028
|
sessionID: newSessionID,
|
|
40949
41029
|
inheritFromSessionID: options.previousSessionID,
|
|
@@ -40952,6 +41032,14 @@ async function continueIteration(ctx, state3, options) {
|
|
|
40952
41032
|
apiTimeoutMs: options.apiTimeoutMs
|
|
40953
41033
|
});
|
|
40954
41034
|
await selectSessionInTui(ctx.client, newSessionID);
|
|
41035
|
+
const boundState = options.loopState.setSessionID(newSessionID);
|
|
41036
|
+
if (!boundState) {
|
|
41037
|
+
log(`[${HOOK_NAME3}] Failed to bind loop state to new session`, {
|
|
41038
|
+
previousSessionID: options.previousSessionID,
|
|
41039
|
+
newSessionID
|
|
41040
|
+
});
|
|
41041
|
+
return;
|
|
41042
|
+
}
|
|
40955
41043
|
return;
|
|
40956
41044
|
}
|
|
40957
41045
|
await injectContinuationPrompt(ctx, {
|
|
@@ -40964,106 +41052,117 @@ async function continueIteration(ctx, state3, options) {
|
|
|
40964
41052
|
|
|
40965
41053
|
// src/hooks/ralph-loop/ralph-loop-event-handler.ts
|
|
40966
41054
|
function createRalphLoopEventHandler(ctx, options) {
|
|
41055
|
+
const inFlightSessions = new Set;
|
|
40967
41056
|
return async ({ event }) => {
|
|
40968
41057
|
const props = event.properties;
|
|
40969
41058
|
if (event.type === "session.idle") {
|
|
40970
41059
|
const sessionID = props?.sessionID;
|
|
40971
41060
|
if (!sessionID)
|
|
40972
41061
|
return;
|
|
40973
|
-
if (
|
|
40974
|
-
log(`[${HOOK_NAME3}] Skipped: in
|
|
41062
|
+
if (inFlightSessions.has(sessionID)) {
|
|
41063
|
+
log(`[${HOOK_NAME3}] Skipped: handler in flight`, { sessionID });
|
|
40975
41064
|
return;
|
|
40976
41065
|
}
|
|
40977
|
-
|
|
40978
|
-
|
|
40979
|
-
|
|
40980
|
-
|
|
40981
|
-
|
|
40982
|
-
|
|
40983
|
-
|
|
40984
|
-
|
|
40985
|
-
|
|
40986
|
-
|
|
40987
|
-
|
|
40988
|
-
|
|
40989
|
-
|
|
41066
|
+
inFlightSessions.add(sessionID);
|
|
41067
|
+
try {
|
|
41068
|
+
if (options.sessionRecovery.isRecovering(sessionID)) {
|
|
41069
|
+
log(`[${HOOK_NAME3}] Skipped: in recovery`, { sessionID });
|
|
41070
|
+
return;
|
|
41071
|
+
}
|
|
41072
|
+
const state3 = options.loopState.getState();
|
|
41073
|
+
if (!state3 || !state3.active) {
|
|
41074
|
+
return;
|
|
41075
|
+
}
|
|
41076
|
+
if (state3.session_id && state3.session_id !== sessionID) {
|
|
41077
|
+
if (options.checkSessionExists) {
|
|
41078
|
+
try {
|
|
41079
|
+
const exists = await options.checkSessionExists(state3.session_id);
|
|
41080
|
+
if (!exists) {
|
|
41081
|
+
options.loopState.clear();
|
|
41082
|
+
log(`[${HOOK_NAME3}] Cleared orphaned state from deleted session`, {
|
|
41083
|
+
orphanedSessionId: state3.session_id,
|
|
41084
|
+
currentSessionId: sessionID
|
|
41085
|
+
});
|
|
41086
|
+
return;
|
|
41087
|
+
}
|
|
41088
|
+
} catch (err) {
|
|
41089
|
+
log(`[${HOOK_NAME3}] Failed to check session existence`, {
|
|
41090
|
+
sessionId: state3.session_id,
|
|
41091
|
+
error: String(err)
|
|
40990
41092
|
});
|
|
40991
|
-
return;
|
|
40992
41093
|
}
|
|
40993
|
-
} catch (err) {
|
|
40994
|
-
log(`[${HOOK_NAME3}] Failed to check session existence`, {
|
|
40995
|
-
sessionId: state3.session_id,
|
|
40996
|
-
error: String(err)
|
|
40997
|
-
});
|
|
40998
41094
|
}
|
|
41095
|
+
return;
|
|
40999
41096
|
}
|
|
41000
|
-
|
|
41001
|
-
|
|
41002
|
-
|
|
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!`, {
|
|
41097
|
+
const transcriptPath = options.getTranscriptPath(sessionID);
|
|
41098
|
+
const completionViaTranscript = detectCompletionInTranscript(transcriptPath, state3.completion_promise);
|
|
41099
|
+
const completionViaApi = completionViaTranscript ? false : await detectCompletionInSessionMessages(ctx, {
|
|
41012
41100
|
sessionID,
|
|
41013
|
-
iteration: state3.iteration,
|
|
41014
41101
|
promise: state3.completion_promise,
|
|
41015
|
-
|
|
41102
|
+
apiTimeoutMs: options.apiTimeoutMs,
|
|
41103
|
+
directory: options.directory,
|
|
41104
|
+
sinceMessageIndex: state3.message_count_at_start
|
|
41016
41105
|
});
|
|
41017
|
-
|
|
41018
|
-
|
|
41019
|
-
|
|
41020
|
-
|
|
41021
|
-
|
|
41022
|
-
|
|
41023
|
-
|
|
41024
|
-
|
|
41106
|
+
if (completionViaTranscript || completionViaApi) {
|
|
41107
|
+
log(`[${HOOK_NAME3}] Completion detected!`, {
|
|
41108
|
+
sessionID,
|
|
41109
|
+
iteration: state3.iteration,
|
|
41110
|
+
promise: state3.completion_promise,
|
|
41111
|
+
detectedVia: completionViaTranscript ? "transcript_file" : "session_messages_api"
|
|
41112
|
+
});
|
|
41113
|
+
options.loopState.clear();
|
|
41114
|
+
const title = state3.ultrawork ? "ULTRAWORK LOOP COMPLETE!" : "Ralph Loop Complete!";
|
|
41115
|
+
const message = state3.ultrawork ? `JUST ULW ULW! Task completed after ${state3.iteration} iteration(s)` : `Task completed after ${state3.iteration} iteration(s)`;
|
|
41116
|
+
await ctx.client.tui?.showToast?.({ body: { title, message, variant: "success", duration: 5000 } }).catch(() => {});
|
|
41117
|
+
return;
|
|
41118
|
+
}
|
|
41119
|
+
if (state3.iteration >= state3.max_iterations) {
|
|
41120
|
+
log(`[${HOOK_NAME3}] Max iterations reached`, {
|
|
41121
|
+
sessionID,
|
|
41122
|
+
iteration: state3.iteration,
|
|
41123
|
+
max: state3.max_iterations
|
|
41124
|
+
});
|
|
41125
|
+
options.loopState.clear();
|
|
41126
|
+
await ctx.client.tui?.showToast?.({
|
|
41127
|
+
body: { title: "Ralph Loop Stopped", message: `Max iterations (${state3.max_iterations}) reached without completion`, variant: "warning", duration: 5000 }
|
|
41128
|
+
}).catch(() => {});
|
|
41129
|
+
return;
|
|
41130
|
+
}
|
|
41131
|
+
const newState = options.loopState.incrementIteration();
|
|
41132
|
+
if (!newState) {
|
|
41133
|
+
log(`[${HOOK_NAME3}] Failed to increment iteration`, { sessionID });
|
|
41134
|
+
return;
|
|
41135
|
+
}
|
|
41136
|
+
log(`[${HOOK_NAME3}] Continuing loop`, {
|
|
41025
41137
|
sessionID,
|
|
41026
|
-
iteration:
|
|
41027
|
-
max:
|
|
41138
|
+
iteration: newState.iteration,
|
|
41139
|
+
max: newState.max_iterations
|
|
41028
41140
|
});
|
|
41029
|
-
options.loopState.clear();
|
|
41030
41141
|
await ctx.client.tui?.showToast?.({
|
|
41031
|
-
body: {
|
|
41142
|
+
body: {
|
|
41143
|
+
title: "Ralph Loop",
|
|
41144
|
+
message: `Iteration ${newState.iteration}/${newState.max_iterations}`,
|
|
41145
|
+
variant: "info",
|
|
41146
|
+
duration: 2000
|
|
41147
|
+
}
|
|
41032
41148
|
}).catch(() => {});
|
|
41033
|
-
|
|
41034
|
-
|
|
41035
|
-
|
|
41036
|
-
|
|
41037
|
-
|
|
41038
|
-
|
|
41039
|
-
|
|
41040
|
-
|
|
41041
|
-
|
|
41042
|
-
|
|
41043
|
-
|
|
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
|
|
41149
|
+
try {
|
|
41150
|
+
await continueIteration(ctx, newState, {
|
|
41151
|
+
previousSessionID: sessionID,
|
|
41152
|
+
directory: options.directory,
|
|
41153
|
+
apiTimeoutMs: options.apiTimeoutMs,
|
|
41154
|
+
loopState: options.loopState
|
|
41155
|
+
});
|
|
41156
|
+
} catch (err) {
|
|
41157
|
+
log(`[${HOOK_NAME3}] Failed to inject continuation`, {
|
|
41158
|
+
sessionID,
|
|
41159
|
+
error: String(err)
|
|
41160
|
+
});
|
|
41051
41161
|
}
|
|
41052
|
-
|
|
41053
|
-
|
|
41054
|
-
|
|
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
|
-
});
|
|
41162
|
+
return;
|
|
41163
|
+
} finally {
|
|
41164
|
+
inFlightSessions.delete(sessionID);
|
|
41065
41165
|
}
|
|
41066
|
-
return;
|
|
41067
41166
|
}
|
|
41068
41167
|
if (event.type === "session.deleted") {
|
|
41069
41168
|
const sessionInfo = props?.info;
|
|
@@ -41100,6 +41199,16 @@ function createRalphLoopEventHandler(ctx, options) {
|
|
|
41100
41199
|
|
|
41101
41200
|
// src/hooks/ralph-loop/ralph-loop-hook.ts
|
|
41102
41201
|
var DEFAULT_API_TIMEOUT = 5000;
|
|
41202
|
+
function getMessageCountFromResponse(messagesResponse) {
|
|
41203
|
+
if (Array.isArray(messagesResponse)) {
|
|
41204
|
+
return messagesResponse.length;
|
|
41205
|
+
}
|
|
41206
|
+
if (typeof messagesResponse === "object" && messagesResponse !== null && "data" in messagesResponse) {
|
|
41207
|
+
const data = messagesResponse.data;
|
|
41208
|
+
return Array.isArray(data) ? data.length : 0;
|
|
41209
|
+
}
|
|
41210
|
+
return 0;
|
|
41211
|
+
}
|
|
41103
41212
|
function createRalphLoopHook(ctx, options) {
|
|
41104
41213
|
const config2 = options?.config;
|
|
41105
41214
|
const stateDir = config2?.state_dir;
|
|
@@ -41122,7 +41231,20 @@ function createRalphLoopHook(ctx, options) {
|
|
|
41122
41231
|
});
|
|
41123
41232
|
return {
|
|
41124
41233
|
event,
|
|
41125
|
-
startLoop:
|
|
41234
|
+
startLoop: (sessionID, prompt, loopOptions) => {
|
|
41235
|
+
const startSuccess = loopState.startLoop(sessionID, prompt, loopOptions);
|
|
41236
|
+
if (!startSuccess || typeof loopOptions?.messageCountAtStart === "number") {
|
|
41237
|
+
return startSuccess;
|
|
41238
|
+
}
|
|
41239
|
+
ctx.client.session.messages({
|
|
41240
|
+
path: { id: sessionID },
|
|
41241
|
+
query: { directory: ctx.directory }
|
|
41242
|
+
}).then((messagesResponse) => {
|
|
41243
|
+
const messageCountAtStart = getMessageCountFromResponse(messagesResponse);
|
|
41244
|
+
loopState.setMessageCountAtStart(sessionID, messageCountAtStart);
|
|
41245
|
+
}).catch(() => {});
|
|
41246
|
+
return startSuccess;
|
|
41247
|
+
},
|
|
41126
41248
|
cancelLoop: loopState.cancelLoop,
|
|
41127
41249
|
getState: loopState.getState
|
|
41128
41250
|
};
|
|
@@ -41177,12 +41299,12 @@ var TOAST_MESSAGE2 = [
|
|
|
41177
41299
|
].join(`
|
|
41178
41300
|
`);
|
|
41179
41301
|
var SISYPHUS_DISPLAY = getAgentDisplayName("sisyphus");
|
|
41180
|
-
function showToast2(ctx, sessionID) {
|
|
41302
|
+
function showToast2(ctx, sessionID, variant) {
|
|
41181
41303
|
ctx.client.tui.showToast({
|
|
41182
41304
|
body: {
|
|
41183
41305
|
title: TOAST_TITLE2,
|
|
41184
41306
|
message: TOAST_MESSAGE2,
|
|
41185
|
-
variant
|
|
41307
|
+
variant,
|
|
41186
41308
|
duration: 1e4
|
|
41187
41309
|
}
|
|
41188
41310
|
}).catch((error45) => {
|
|
@@ -41192,14 +41314,18 @@ function showToast2(ctx, sessionID) {
|
|
|
41192
41314
|
});
|
|
41193
41315
|
});
|
|
41194
41316
|
}
|
|
41195
|
-
function createNoHephaestusNonGptHook(ctx) {
|
|
41317
|
+
function createNoHephaestusNonGptHook(ctx, options) {
|
|
41196
41318
|
return {
|
|
41197
41319
|
"chat.message": async (input, output) => {
|
|
41198
41320
|
const rawAgent = input.agent ?? getSessionAgent(input.sessionID) ?? "";
|
|
41199
41321
|
const agentKey = getAgentConfigKey(rawAgent);
|
|
41200
41322
|
const modelID = input.model?.modelID;
|
|
41323
|
+
const allowNonGptModel = options?.allowNonGptModel === true;
|
|
41201
41324
|
if (agentKey === "hephaestus" && modelID && !isGptModel(modelID)) {
|
|
41202
|
-
showToast2(ctx, input.sessionID);
|
|
41325
|
+
showToast2(ctx, input.sessionID, allowNonGptModel ? "warning" : "error");
|
|
41326
|
+
if (allowNonGptModel) {
|
|
41327
|
+
return;
|
|
41328
|
+
}
|
|
41203
41329
|
input.agent = SISYPHUS_DISPLAY;
|
|
41204
41330
|
if (output?.message) {
|
|
41205
41331
|
output.message.agent = SISYPHUS_DISPLAY;
|
|
@@ -42269,6 +42395,15 @@ $ARGUMENTS
|
|
|
42269
42395
|
// src/features/builtin-commands/templates/start-work.ts
|
|
42270
42396
|
var START_WORK_TEMPLATE = `You are starting a Sisyphus work session.
|
|
42271
42397
|
|
|
42398
|
+
## ARGUMENTS
|
|
42399
|
+
|
|
42400
|
+
- \`/start-work [plan-name] [--worktree <path>]\`
|
|
42401
|
+
- \`plan-name\` (optional): name or partial match of the plan to start
|
|
42402
|
+
- \`--worktree <path>\` (optional): absolute path to an existing git worktree to work in
|
|
42403
|
+
- If specified and valid: hook pre-sets worktree_path in boulder.json
|
|
42404
|
+
- If specified but invalid: you must run \`git worktree add <path> <branch>\` first
|
|
42405
|
+
- If omitted: you MUST choose or create a worktree (see Worktree Setup below)
|
|
42406
|
+
|
|
42272
42407
|
## WHAT TO DO
|
|
42273
42408
|
|
|
42274
42409
|
1. **Find available plans**: Search for Prometheus-generated plan files at \`.sisyphus/plans/\`
|
|
@@ -42284,17 +42419,24 @@ var START_WORK_TEMPLATE = `You are starting a Sisyphus work session.
|
|
|
42284
42419
|
- If ONE plan: auto-select it
|
|
42285
42420
|
- If MULTIPLE plans: show list with timestamps, ask user to select
|
|
42286
42421
|
|
|
42287
|
-
4. **
|
|
42422
|
+
4. **Worktree Setup** (when \`worktree_path\` not already set in boulder.json):
|
|
42423
|
+
1. \`git worktree list --porcelain\` \u2014 see available worktrees
|
|
42424
|
+
2. Create: \`git worktree add <absolute-path> <branch-or-HEAD>\`
|
|
42425
|
+
3. Update boulder.json to add \`"worktree_path": "<absolute-path>"\`
|
|
42426
|
+
4. All work happens inside that worktree directory
|
|
42427
|
+
|
|
42428
|
+
5. **Create/Update boulder.json**:
|
|
42288
42429
|
\`\`\`json
|
|
42289
42430
|
{
|
|
42290
42431
|
"active_plan": "/absolute/path/to/plan.md",
|
|
42291
42432
|
"started_at": "ISO_TIMESTAMP",
|
|
42292
42433
|
"session_ids": ["session_id_1", "session_id_2"],
|
|
42293
|
-
"plan_name": "plan-name"
|
|
42434
|
+
"plan_name": "plan-name",
|
|
42435
|
+
"worktree_path": "/absolute/path/to/git/worktree"
|
|
42294
42436
|
}
|
|
42295
42437
|
\`\`\`
|
|
42296
42438
|
|
|
42297
|
-
|
|
42439
|
+
6. **Read the plan file** and start executing tasks according to atlas workflow
|
|
42298
42440
|
|
|
42299
42441
|
## OUTPUT FORMAT
|
|
42300
42442
|
|
|
@@ -42318,6 +42460,7 @@ Resuming Work Session
|
|
|
42318
42460
|
Active Plan: {plan-name}
|
|
42319
42461
|
Progress: {completed}/{total} tasks
|
|
42320
42462
|
Sessions: {count} (appending current session)
|
|
42463
|
+
Worktree: {worktree_path}
|
|
42321
42464
|
|
|
42322
42465
|
Reading plan and continuing from last incomplete task...
|
|
42323
42466
|
\`\`\`
|
|
@@ -42329,6 +42472,7 @@ Starting Work Session
|
|
|
42329
42472
|
Plan: {plan-name}
|
|
42330
42473
|
Session ID: {session_id}
|
|
42331
42474
|
Started: {timestamp}
|
|
42475
|
+
Worktree: {worktree_path}
|
|
42332
42476
|
|
|
42333
42477
|
Reading plan and beginning execution...
|
|
42334
42478
|
\`\`\`
|
|
@@ -42337,6 +42481,7 @@ Reading plan and beginning execution...
|
|
|
42337
42481
|
|
|
42338
42482
|
- The session_id is injected by the hook - use it directly
|
|
42339
42483
|
- Always update boulder.json BEFORE starting work
|
|
42484
|
+
- Always set worktree_path in boulder.json before executing any tasks
|
|
42340
42485
|
- Read the FULL plan file before delegating any tasks
|
|
42341
42486
|
- Follow atlas delegation protocols (7-section format)`;
|
|
42342
42487
|
|
|
@@ -45838,8 +45983,8 @@ function getPlanProgress(planPath) {
|
|
|
45838
45983
|
}
|
|
45839
45984
|
try {
|
|
45840
45985
|
const content = readFileSync35(planPath, "utf-8");
|
|
45841
|
-
const uncheckedMatches = content.match(
|
|
45842
|
-
const checkedMatches = content.match(
|
|
45986
|
+
const uncheckedMatches = content.match(/^\s*[-*]\s*\[\s*\]/gm) || [];
|
|
45987
|
+
const checkedMatches = content.match(/^\s*[-*]\s*\[[xX]\]/gm) || [];
|
|
45843
45988
|
const total = uncheckedMatches.length + checkedMatches.length;
|
|
45844
45989
|
const completed = checkedMatches.length;
|
|
45845
45990
|
return {
|
|
@@ -45854,13 +45999,14 @@ function getPlanProgress(planPath) {
|
|
|
45854
45999
|
function getPlanName(planPath) {
|
|
45855
46000
|
return basename3(planPath, ".md");
|
|
45856
46001
|
}
|
|
45857
|
-
function createBoulderState(planPath, sessionId, agent) {
|
|
46002
|
+
function createBoulderState(planPath, sessionId, agent, worktreePath) {
|
|
45858
46003
|
return {
|
|
45859
46004
|
active_plan: planPath,
|
|
45860
46005
|
started_at: new Date().toISOString(),
|
|
45861
46006
|
session_ids: [sessionId],
|
|
45862
46007
|
plan_name: getPlanName(planPath),
|
|
45863
|
-
...agent !== undefined ? { agent } : {}
|
|
46008
|
+
...agent !== undefined ? { agent } : {},
|
|
46009
|
+
...worktreePath !== undefined ? { worktree_path: worktreePath } : {}
|
|
45864
46010
|
};
|
|
45865
46011
|
}
|
|
45866
46012
|
// src/hooks/prometheus-md-only/agent-resolution.ts
|
|
@@ -46060,19 +46206,48 @@ to continue: task(session_id="${sessionId}", prompt="...")`;
|
|
|
46060
46206
|
};
|
|
46061
46207
|
}
|
|
46062
46208
|
// src/hooks/start-work/start-work-hook.ts
|
|
46209
|
+
import { statSync as statSync6 } from "fs";
|
|
46063
46210
|
init_logger();
|
|
46064
|
-
|
|
46065
|
-
|
|
46066
|
-
|
|
46067
|
-
|
|
46068
|
-
|
|
46211
|
+
|
|
46212
|
+
// src/hooks/start-work/worktree-detector.ts
|
|
46213
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
46214
|
+
function detectWorktreePath(directory) {
|
|
46215
|
+
try {
|
|
46216
|
+
return execFileSync2("git", ["rev-parse", "--show-toplevel"], {
|
|
46217
|
+
cwd: directory,
|
|
46218
|
+
encoding: "utf-8",
|
|
46219
|
+
timeout: 5000,
|
|
46220
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
46221
|
+
}).trim();
|
|
46222
|
+
} catch {
|
|
46069
46223
|
return null;
|
|
46070
|
-
|
|
46224
|
+
}
|
|
46225
|
+
}
|
|
46226
|
+
|
|
46227
|
+
// src/hooks/start-work/parse-user-request.ts
|
|
46228
|
+
var KEYWORD_PATTERN = /\b(ultrawork|ulw)\b/gi;
|
|
46229
|
+
var WORKTREE_FLAG_PATTERN = /--worktree(?:\s+(\S+))?/;
|
|
46230
|
+
function parseUserRequest(promptText) {
|
|
46231
|
+
const match = promptText.match(/<user-request>\s*([\s\S]*?)\s*<\/user-request>/i);
|
|
46232
|
+
if (!match)
|
|
46233
|
+
return { planName: null, explicitWorktreePath: null };
|
|
46234
|
+
let rawArg = match[1].trim();
|
|
46071
46235
|
if (!rawArg)
|
|
46072
|
-
return null;
|
|
46236
|
+
return { planName: null, explicitWorktreePath: null };
|
|
46237
|
+
const worktreeMatch = rawArg.match(WORKTREE_FLAG_PATTERN);
|
|
46238
|
+
const explicitWorktreePath = worktreeMatch ? worktreeMatch[1] ?? null : null;
|
|
46239
|
+
if (worktreeMatch) {
|
|
46240
|
+
rawArg = rawArg.replace(worktreeMatch[0], "").trim();
|
|
46241
|
+
}
|
|
46073
46242
|
const cleanedArg = rawArg.replace(KEYWORD_PATTERN, "").trim();
|
|
46074
|
-
return
|
|
46243
|
+
return {
|
|
46244
|
+
planName: cleanedArg || null,
|
|
46245
|
+
explicitWorktreePath
|
|
46246
|
+
};
|
|
46075
46247
|
}
|
|
46248
|
+
|
|
46249
|
+
// src/hooks/start-work/start-work-hook.ts
|
|
46250
|
+
var HOOK_NAME6 = "start-work";
|
|
46076
46251
|
function findPlanByName(plans, requestedName) {
|
|
46077
46252
|
const lowerName = requestedName.toLowerCase();
|
|
46078
46253
|
const exactMatch = plans.find((p) => getPlanName(p).toLowerCase() === lowerName);
|
|
@@ -46081,29 +46256,48 @@ function findPlanByName(plans, requestedName) {
|
|
|
46081
46256
|
const partialMatch = plans.find((p) => getPlanName(p).toLowerCase().includes(lowerName));
|
|
46082
46257
|
return partialMatch || null;
|
|
46083
46258
|
}
|
|
46259
|
+
var MODEL_DECIDES_WORKTREE_BLOCK = `
|
|
46260
|
+
## Worktree Setup Required
|
|
46261
|
+
|
|
46262
|
+
No worktree specified. Before starting work, you MUST choose or create one:
|
|
46263
|
+
|
|
46264
|
+
1. \`git worktree list --porcelain\` \u2014 list existing worktrees
|
|
46265
|
+
2. Create if needed: \`git worktree add <absolute-path> <branch-or-HEAD>\`
|
|
46266
|
+
3. Update \`.sisyphus/boulder.json\` \u2014 add \`"worktree_path": "<absolute-path>"\`
|
|
46267
|
+
4. Work exclusively inside that worktree directory`;
|
|
46268
|
+
function resolveWorktreeContext(explicitWorktreePath) {
|
|
46269
|
+
if (explicitWorktreePath === null) {
|
|
46270
|
+
return { worktreePath: undefined, block: MODEL_DECIDES_WORKTREE_BLOCK };
|
|
46271
|
+
}
|
|
46272
|
+
const validatedPath = detectWorktreePath(explicitWorktreePath);
|
|
46273
|
+
if (validatedPath) {
|
|
46274
|
+
return { worktreePath: validatedPath, block: `
|
|
46275
|
+
**Worktree**: ${validatedPath}` };
|
|
46276
|
+
}
|
|
46277
|
+
return {
|
|
46278
|
+
worktreePath: undefined,
|
|
46279
|
+
block: `
|
|
46280
|
+
**Worktree** (needs setup): \`git worktree add ${explicitWorktreePath} <branch>\`, then add \`"worktree_path"\` to boulder.json`
|
|
46281
|
+
};
|
|
46282
|
+
}
|
|
46084
46283
|
function createStartWorkHook(ctx) {
|
|
46085
46284
|
return {
|
|
46086
46285
|
"chat.message": async (input, output) => {
|
|
46087
46286
|
const parts = output.parts;
|
|
46088
46287
|
const promptText = parts?.filter((p) => p.type === "text" && p.text).map((p) => p.text).join(`
|
|
46089
46288
|
`).trim() || "";
|
|
46090
|
-
|
|
46091
|
-
if (!isStartWorkCommand) {
|
|
46289
|
+
if (!promptText.includes("<session-context>"))
|
|
46092
46290
|
return;
|
|
46093
|
-
}
|
|
46094
|
-
log(`[${HOOK_NAME6}] Processing start-work command`, {
|
|
46095
|
-
sessionID: input.sessionID
|
|
46096
|
-
});
|
|
46291
|
+
log(`[${HOOK_NAME6}] Processing start-work command`, { sessionID: input.sessionID });
|
|
46097
46292
|
updateSessionAgent(input.sessionID, "atlas");
|
|
46098
46293
|
const existingState = readBoulderState(ctx.directory);
|
|
46099
46294
|
const sessionId = input.sessionID;
|
|
46100
46295
|
const timestamp2 = new Date().toISOString();
|
|
46296
|
+
const { planName: explicitPlanName, explicitWorktreePath } = parseUserRequest(promptText);
|
|
46297
|
+
const { worktreePath, block: worktreeBlock } = resolveWorktreeContext(explicitWorktreePath);
|
|
46101
46298
|
let contextInfo = "";
|
|
46102
|
-
const explicitPlanName = extractUserRequestPlanName(promptText);
|
|
46103
46299
|
if (explicitPlanName) {
|
|
46104
|
-
log(`[${HOOK_NAME6}] Explicit plan name requested: ${explicitPlanName}`, {
|
|
46105
|
-
sessionID: input.sessionID
|
|
46106
|
-
});
|
|
46300
|
+
log(`[${HOOK_NAME6}] Explicit plan name requested: ${explicitPlanName}`, { sessionID: input.sessionID });
|
|
46107
46301
|
const allPlans = findPrometheusPlans(ctx.directory);
|
|
46108
46302
|
const matchedPlan = findPlanByName(allPlans, explicitPlanName);
|
|
46109
46303
|
if (matchedPlan) {
|
|
@@ -46115,10 +46309,9 @@ function createStartWorkHook(ctx) {
|
|
|
46115
46309
|
The requested plan "${getPlanName(matchedPlan)}" has been completed.
|
|
46116
46310
|
All ${progress.total} tasks are done. Create a new plan with: /plan "your task"`;
|
|
46117
46311
|
} else {
|
|
46118
|
-
if (existingState)
|
|
46312
|
+
if (existingState)
|
|
46119
46313
|
clearBoulderState(ctx.directory);
|
|
46120
|
-
|
|
46121
|
-
const newState = createBoulderState(matchedPlan, sessionId, "atlas");
|
|
46314
|
+
const newState = createBoulderState(matchedPlan, sessionId, "atlas", worktreePath);
|
|
46122
46315
|
writeBoulderState(ctx.directory, newState);
|
|
46123
46316
|
contextInfo = `
|
|
46124
46317
|
## Auto-Selected Plan
|
|
@@ -46128,6 +46321,7 @@ All ${progress.total} tasks are done. Create a new plan with: /plan "your task"`
|
|
|
46128
46321
|
**Progress**: ${progress.completed}/${progress.total} tasks
|
|
46129
46322
|
**Session ID**: ${sessionId}
|
|
46130
46323
|
**Started**: ${timestamp2}
|
|
46324
|
+
${worktreeBlock}
|
|
46131
46325
|
|
|
46132
46326
|
boulder.json has been created. Read the plan and begin execution.`;
|
|
46133
46327
|
}
|
|
@@ -46159,7 +46353,19 @@ No incomplete plans available. Create a new plan with: /plan "your task"`;
|
|
|
46159
46353
|
} else if (existingState) {
|
|
46160
46354
|
const progress = getPlanProgress(existingState.active_plan);
|
|
46161
46355
|
if (!progress.isComplete) {
|
|
46162
|
-
|
|
46356
|
+
const effectiveWorktree = worktreePath ?? existingState.worktree_path;
|
|
46357
|
+
if (worktreePath !== undefined) {
|
|
46358
|
+
const updatedSessions = existingState.session_ids.includes(sessionId) ? existingState.session_ids : [...existingState.session_ids, sessionId];
|
|
46359
|
+
writeBoulderState(ctx.directory, {
|
|
46360
|
+
...existingState,
|
|
46361
|
+
worktree_path: worktreePath,
|
|
46362
|
+
session_ids: updatedSessions
|
|
46363
|
+
});
|
|
46364
|
+
} else {
|
|
46365
|
+
appendSessionId(ctx.directory, sessionId);
|
|
46366
|
+
}
|
|
46367
|
+
const worktreeDisplay = effectiveWorktree ? `
|
|
46368
|
+
**Worktree**: ${effectiveWorktree}` : worktreeBlock;
|
|
46163
46369
|
contextInfo = `
|
|
46164
46370
|
## Active Work Session Found
|
|
46165
46371
|
|
|
@@ -46169,6 +46375,7 @@ No incomplete plans available. Create a new plan with: /plan "your task"`;
|
|
|
46169
46375
|
**Progress**: ${progress.completed}/${progress.total} tasks completed
|
|
46170
46376
|
**Sessions**: ${existingState.session_ids.length + 1} (current session appended)
|
|
46171
46377
|
**Started**: ${existingState.started_at}
|
|
46378
|
+
${worktreeDisplay}
|
|
46172
46379
|
|
|
46173
46380
|
The current session (${sessionId}) has been added to session_ids.
|
|
46174
46381
|
Read the plan file and continue from the first unchecked task.`;
|
|
@@ -46185,7 +46392,6 @@ Looking for new plans...`;
|
|
|
46185
46392
|
const incompletePlans = plans.filter((p) => !getPlanProgress(p).isComplete);
|
|
46186
46393
|
if (plans.length === 0) {
|
|
46187
46394
|
contextInfo += `
|
|
46188
|
-
|
|
46189
46395
|
## No Plans Found
|
|
46190
46396
|
|
|
46191
46397
|
No Prometheus plan files found at .sisyphus/plans/
|
|
@@ -46199,7 +46405,7 @@ All ${plans.length} plan(s) are complete. Create a new plan with: /plan "your ta
|
|
|
46199
46405
|
} else if (incompletePlans.length === 1) {
|
|
46200
46406
|
const planPath = incompletePlans[0];
|
|
46201
46407
|
const progress = getPlanProgress(planPath);
|
|
46202
|
-
const newState = createBoulderState(planPath, sessionId, "atlas");
|
|
46408
|
+
const newState = createBoulderState(planPath, sessionId, "atlas", worktreePath);
|
|
46203
46409
|
writeBoulderState(ctx.directory, newState);
|
|
46204
46410
|
contextInfo += `
|
|
46205
46411
|
|
|
@@ -46210,13 +46416,13 @@ All ${plans.length} plan(s) are complete. Create a new plan with: /plan "your ta
|
|
|
46210
46416
|
**Progress**: ${progress.completed}/${progress.total} tasks
|
|
46211
46417
|
**Session ID**: ${sessionId}
|
|
46212
46418
|
**Started**: ${timestamp2}
|
|
46419
|
+
${worktreeBlock}
|
|
46213
46420
|
|
|
46214
46421
|
boulder.json has been created. Read the plan and begin execution.`;
|
|
46215
46422
|
} else {
|
|
46216
46423
|
const planList = incompletePlans.map((p, i2) => {
|
|
46217
46424
|
const progress = getPlanProgress(p);
|
|
46218
|
-
const
|
|
46219
|
-
const modified = new Date(stat.mtimeMs).toISOString();
|
|
46425
|
+
const modified = new Date(statSync6(p).mtimeMs).toISOString();
|
|
46220
46426
|
return `${i2 + 1}. [${getPlanName(p)}] - Modified: ${modified} - Progress: ${progress.completed}/${progress.total}`;
|
|
46221
46427
|
}).join(`
|
|
46222
46428
|
`);
|
|
@@ -46231,6 +46437,7 @@ Session ID: ${sessionId}
|
|
|
46231
46437
|
${planList}
|
|
46232
46438
|
|
|
46233
46439
|
Ask the user which plan to work on. Present the options above and wait for their response.
|
|
46440
|
+
${worktreeBlock}
|
|
46234
46441
|
</system-reminder>`;
|
|
46235
46442
|
}
|
|
46236
46443
|
}
|
|
@@ -46244,7 +46451,8 @@ ${contextInfo}`;
|
|
|
46244
46451
|
}
|
|
46245
46452
|
log(`[${HOOK_NAME6}] Context injected`, {
|
|
46246
46453
|
sessionID: input.sessionID,
|
|
46247
|
-
hasExistingState: !!existingState
|
|
46454
|
+
hasExistingState: !!existingState,
|
|
46455
|
+
worktreePath
|
|
46248
46456
|
});
|
|
46249
46457
|
}
|
|
46250
46458
|
};
|
|
@@ -46496,6 +46704,7 @@ async function injectBoulderContinuation(input) {
|
|
|
46496
46704
|
remaining,
|
|
46497
46705
|
total,
|
|
46498
46706
|
agent,
|
|
46707
|
+
worktreePath,
|
|
46499
46708
|
backgroundManager,
|
|
46500
46709
|
sessionState
|
|
46501
46710
|
} = input;
|
|
@@ -46504,9 +46713,12 @@ async function injectBoulderContinuation(input) {
|
|
|
46504
46713
|
log(`[${HOOK_NAME7}] Skipped injection: background tasks running`, { sessionID });
|
|
46505
46714
|
return;
|
|
46506
46715
|
}
|
|
46716
|
+
const worktreeContext = worktreePath ? `
|
|
46717
|
+
|
|
46718
|
+
[Worktree: ${worktreePath}]` : "";
|
|
46507
46719
|
const prompt = BOULDER_CONTINUATION_PROMPT.replace(/{PLAN_NAME}/g, planName) + `
|
|
46508
46720
|
|
|
46509
|
-
[Status: ${total - remaining}/${total} completed, ${remaining} remaining]
|
|
46721
|
+
[Status: ${total - remaining}/${total} completed, ${remaining} remaining]` + worktreeContext;
|
|
46510
46722
|
try {
|
|
46511
46723
|
log(`[${HOOK_NAME7}] Injecting boulder continuation`, { sessionID, planName, remaining });
|
|
46512
46724
|
const promptContext = await resolveRecentPromptContextForSession(ctx, sessionID);
|
|
@@ -46525,6 +46737,7 @@ async function injectBoulderContinuation(input) {
|
|
|
46525
46737
|
log(`[${HOOK_NAME7}] Boulder continuation injected`, { sessionID });
|
|
46526
46738
|
} catch (err) {
|
|
46527
46739
|
sessionState.promptFailureCount += 1;
|
|
46740
|
+
sessionState.lastFailureAt = Date.now();
|
|
46528
46741
|
log(`[${HOOK_NAME7}] Boulder continuation failed`, {
|
|
46529
46742
|
sessionID,
|
|
46530
46743
|
error: String(err),
|
|
@@ -46549,6 +46762,7 @@ async function getLastAgentFromSession(sessionID, client) {
|
|
|
46549
46762
|
|
|
46550
46763
|
// src/hooks/atlas/event-handler.ts
|
|
46551
46764
|
var CONTINUATION_COOLDOWN_MS2 = 5000;
|
|
46765
|
+
var FAILURE_BACKOFF_MS = 5 * 60 * 1000;
|
|
46552
46766
|
function createAtlasEventHandler(input) {
|
|
46553
46767
|
const { ctx, options, sessions, getState } = input;
|
|
46554
46768
|
return async ({ event }) => {
|
|
@@ -46576,17 +46790,24 @@ function createAtlasEventHandler(input) {
|
|
|
46576
46790
|
return;
|
|
46577
46791
|
}
|
|
46578
46792
|
const state3 = getState(sessionID);
|
|
46793
|
+
const now = Date.now();
|
|
46579
46794
|
if (state3.lastEventWasAbortError) {
|
|
46580
46795
|
state3.lastEventWasAbortError = false;
|
|
46581
46796
|
log(`[${HOOK_NAME7}] Skipped: abort error immediately before idle`, { sessionID });
|
|
46582
46797
|
return;
|
|
46583
46798
|
}
|
|
46584
46799
|
if (state3.promptFailureCount >= 2) {
|
|
46585
|
-
|
|
46586
|
-
|
|
46587
|
-
|
|
46588
|
-
|
|
46589
|
-
|
|
46800
|
+
const timeSinceLastFailure = state3.lastFailureAt !== undefined ? now - state3.lastFailureAt : Number.POSITIVE_INFINITY;
|
|
46801
|
+
if (timeSinceLastFailure < FAILURE_BACKOFF_MS) {
|
|
46802
|
+
log(`[${HOOK_NAME7}] Skipped: continuation in backoff after repeated failures`, {
|
|
46803
|
+
sessionID,
|
|
46804
|
+
promptFailureCount: state3.promptFailureCount,
|
|
46805
|
+
backoffRemaining: FAILURE_BACKOFF_MS - timeSinceLastFailure
|
|
46806
|
+
});
|
|
46807
|
+
return;
|
|
46808
|
+
}
|
|
46809
|
+
state3.promptFailureCount = 0;
|
|
46810
|
+
state3.lastFailureAt = undefined;
|
|
46590
46811
|
}
|
|
46591
46812
|
const backgroundManager = options?.backgroundManager;
|
|
46592
46813
|
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((t) => t.status === "running") : false;
|
|
@@ -46606,17 +46827,15 @@ function createAtlasEventHandler(input) {
|
|
|
46606
46827
|
const lastAgentKey = getAgentConfigKey(lastAgent ?? "");
|
|
46607
46828
|
const requiredAgent = getAgentConfigKey(boulderState.agent ?? "atlas");
|
|
46608
46829
|
const lastAgentMatchesRequired = lastAgentKey === requiredAgent;
|
|
46609
|
-
const boulderAgentWasNotExplicitlySet = boulderState.agent === undefined;
|
|
46610
46830
|
const boulderAgentDefaultsToAtlas = requiredAgent === "atlas";
|
|
46611
46831
|
const lastAgentIsSisyphus = lastAgentKey === "sisyphus";
|
|
46612
|
-
const
|
|
46613
|
-
const agentMatches = lastAgentMatchesRequired ||
|
|
46832
|
+
const allowSisyphusForAtlasBoulder = boulderAgentDefaultsToAtlas && lastAgentIsSisyphus;
|
|
46833
|
+
const agentMatches = lastAgentMatchesRequired || allowSisyphusForAtlasBoulder;
|
|
46614
46834
|
if (!agentMatches) {
|
|
46615
46835
|
log(`[${HOOK_NAME7}] Skipped: last agent does not match boulder agent`, {
|
|
46616
46836
|
sessionID,
|
|
46617
46837
|
lastAgent: lastAgent ?? "unknown",
|
|
46618
|
-
requiredAgent
|
|
46619
|
-
boulderAgentExplicitlySet: boulderState.agent !== undefined
|
|
46838
|
+
requiredAgent
|
|
46620
46839
|
});
|
|
46621
46840
|
return;
|
|
46622
46841
|
}
|
|
@@ -46625,7 +46844,6 @@ function createAtlasEventHandler(input) {
|
|
|
46625
46844
|
log(`[${HOOK_NAME7}] Boulder complete`, { sessionID, plan: boulderState.plan_name });
|
|
46626
46845
|
return;
|
|
46627
46846
|
}
|
|
46628
|
-
const now = Date.now();
|
|
46629
46847
|
if (state3.lastContinuationInjectedAt && now - state3.lastContinuationInjectedAt < CONTINUATION_COOLDOWN_MS2) {
|
|
46630
46848
|
log(`[${HOOK_NAME7}] Skipped: continuation cooldown active`, {
|
|
46631
46849
|
sessionID,
|
|
@@ -46643,6 +46861,7 @@ function createAtlasEventHandler(input) {
|
|
|
46643
46861
|
remaining,
|
|
46644
46862
|
total: progress.total,
|
|
46645
46863
|
agent: boulderState.agent,
|
|
46864
|
+
worktreePath: boulderState.worktree_path,
|
|
46646
46865
|
backgroundManager,
|
|
46647
46866
|
sessionState: state3
|
|
46648
46867
|
});
|
|
@@ -47125,12 +47344,36 @@ function createQuestionLabelTruncatorHook() {
|
|
|
47125
47344
|
// src/hooks/stop-continuation-guard/hook.ts
|
|
47126
47345
|
init_logger();
|
|
47127
47346
|
var HOOK_NAME8 = "stop-continuation-guard";
|
|
47128
|
-
function createStopContinuationGuardHook(ctx) {
|
|
47347
|
+
function createStopContinuationGuardHook(ctx, options) {
|
|
47129
47348
|
const stoppedSessions = new Set;
|
|
47130
47349
|
const stop = (sessionID) => {
|
|
47131
47350
|
stoppedSessions.add(sessionID);
|
|
47132
47351
|
setContinuationMarkerSource(ctx.directory, sessionID, "stop", "stopped", "continuation stopped");
|
|
47133
47352
|
log(`[${HOOK_NAME8}] Continuation stopped for session`, { sessionID });
|
|
47353
|
+
const backgroundManager = options?.backgroundManager;
|
|
47354
|
+
if (!backgroundManager) {
|
|
47355
|
+
return;
|
|
47356
|
+
}
|
|
47357
|
+
const cancellableTasks = backgroundManager.getAllDescendantTasks(sessionID).filter((task) => task.status === "running" || task.status === "pending");
|
|
47358
|
+
if (cancellableTasks.length === 0) {
|
|
47359
|
+
return;
|
|
47360
|
+
}
|
|
47361
|
+
Promise.allSettled(cancellableTasks.map(async (task) => {
|
|
47362
|
+
await backgroundManager.cancelTask(task.id, {
|
|
47363
|
+
source: "stop-continuation",
|
|
47364
|
+
reason: "Continuation stopped via /stop-continuation",
|
|
47365
|
+
abortSession: task.status === "running",
|
|
47366
|
+
skipNotification: true
|
|
47367
|
+
});
|
|
47368
|
+
})).then((results) => {
|
|
47369
|
+
const cancelledCount = results.filter((result) => result.status === "fulfilled").length;
|
|
47370
|
+
const failedCount = results.length - cancelledCount;
|
|
47371
|
+
log(`[${HOOK_NAME8}] Cancelled background tasks for stopped session`, {
|
|
47372
|
+
sessionID,
|
|
47373
|
+
cancelledCount,
|
|
47374
|
+
failedCount
|
|
47375
|
+
});
|
|
47376
|
+
});
|
|
47134
47377
|
};
|
|
47135
47378
|
const isStopped = (sessionID) => {
|
|
47136
47379
|
return stoppedSessions.has(sessionID);
|
|
@@ -47531,10 +47774,24 @@ function createUnstableAgentBabysitterHook(ctx, options) {
|
|
|
47531
47774
|
// src/hooks/preemptive-compaction.ts
|
|
47532
47775
|
init_logger();
|
|
47533
47776
|
var DEFAULT_ACTUAL_LIMIT = 200000;
|
|
47777
|
+
var PREEMPTIVE_COMPACTION_TIMEOUT_MS = 120000;
|
|
47534
47778
|
function getAnthropicActualLimit3(modelCacheState) {
|
|
47535
47779
|
return (modelCacheState?.anthropicContext1MEnabled ?? false) || process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : DEFAULT_ACTUAL_LIMIT;
|
|
47536
47780
|
}
|
|
47537
47781
|
var PREEMPTIVE_COMPACTION_THRESHOLD = 0.78;
|
|
47782
|
+
function withTimeout2(promise2, timeoutMs, errorMessage) {
|
|
47783
|
+
let timeoutID;
|
|
47784
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
47785
|
+
timeoutID = setTimeout(() => {
|
|
47786
|
+
reject(new Error(errorMessage));
|
|
47787
|
+
}, timeoutMs);
|
|
47788
|
+
});
|
|
47789
|
+
return Promise.race([promise2, timeoutPromise]).finally(() => {
|
|
47790
|
+
if (timeoutID !== undefined) {
|
|
47791
|
+
clearTimeout(timeoutID);
|
|
47792
|
+
}
|
|
47793
|
+
});
|
|
47794
|
+
}
|
|
47538
47795
|
function isAnthropicProvider2(providerID) {
|
|
47539
47796
|
return providerID === "anthropic" || providerID === "google-vertex-anthropic";
|
|
47540
47797
|
}
|
|
@@ -47561,11 +47818,11 @@ function createPreemptiveCompactionHook(ctx, pluginConfig, modelCacheState) {
|
|
|
47561
47818
|
compactionInProgress.add(sessionID);
|
|
47562
47819
|
try {
|
|
47563
47820
|
const { providerID: targetProviderID, modelID: targetModelID } = resolveCompactionModel(pluginConfig, sessionID, cached2.providerID, modelID);
|
|
47564
|
-
await ctx.client.session.summarize({
|
|
47821
|
+
await withTimeout2(ctx.client.session.summarize({
|
|
47565
47822
|
path: { id: sessionID },
|
|
47566
47823
|
body: { providerID: targetProviderID, modelID: targetModelID, auto: true },
|
|
47567
47824
|
query: { directory: ctx.directory }
|
|
47568
|
-
});
|
|
47825
|
+
}), PREEMPTIVE_COMPACTION_TIMEOUT_MS, `Compaction summarize timed out after ${PREEMPTIVE_COMPACTION_TIMEOUT_MS}ms`);
|
|
47569
47826
|
compactedSessions.add(sessionID);
|
|
47570
47827
|
} catch (error45) {
|
|
47571
47828
|
log("[preemptive-compaction] Compaction failed", { sessionID, error: String(error45) });
|
|
@@ -47773,7 +48030,9 @@ var AgentOverridesSchema = exports_external.object({
|
|
|
47773
48030
|
build: AgentOverrideConfigSchema.optional(),
|
|
47774
48031
|
plan: AgentOverrideConfigSchema.optional(),
|
|
47775
48032
|
sisyphus: AgentOverrideConfigSchema.optional(),
|
|
47776
|
-
hephaestus: AgentOverrideConfigSchema.
|
|
48033
|
+
hephaestus: AgentOverrideConfigSchema.extend({
|
|
48034
|
+
allow_non_gpt_model: exports_external.boolean().optional()
|
|
48035
|
+
}).optional(),
|
|
47777
48036
|
"sisyphus-junior": AgentOverrideConfigSchema.optional(),
|
|
47778
48037
|
"OpenCode-Builder": AgentOverrideConfigSchema.optional(),
|
|
47779
48038
|
prometheus: AgentOverrideConfigSchema.optional(),
|
|
@@ -47824,6 +48083,7 @@ var CategoryConfigSchema = exports_external.object({
|
|
|
47824
48083
|
textVerbosity: exports_external.enum(["low", "medium", "high"]).optional(),
|
|
47825
48084
|
tools: exports_external.record(exports_external.string(), exports_external.boolean()).optional(),
|
|
47826
48085
|
prompt_append: exports_external.string().optional(),
|
|
48086
|
+
max_prompt_tokens: exports_external.number().int().positive().optional(),
|
|
47827
48087
|
is_unstable_agent: exports_external.boolean().optional(),
|
|
47828
48088
|
disable: exports_external.boolean().optional()
|
|
47829
48089
|
});
|
|
@@ -48066,7 +48326,7 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
48066
48326
|
new_task_system_enabled: exports_external.boolean().optional(),
|
|
48067
48327
|
default_run_agent: exports_external.string().optional(),
|
|
48068
48328
|
disabled_mcps: exports_external.array(AnyMcpNameSchema).optional(),
|
|
48069
|
-
disabled_agents: exports_external.array(
|
|
48329
|
+
disabled_agents: exports_external.array(exports_external.string()).optional(),
|
|
48070
48330
|
disabled_skills: exports_external.array(BuiltinSkillNameSchema).optional(),
|
|
48071
48331
|
disabled_hooks: exports_external.array(exports_external.string()).optional(),
|
|
48072
48332
|
disabled_commands: exports_external.array(BuiltinCommandNameSchema).optional(),
|
|
@@ -50019,9 +50279,9 @@ function getLanguageId(ext) {
|
|
|
50019
50279
|
}
|
|
50020
50280
|
// src/tools/lsp/lsp-process.ts
|
|
50021
50281
|
init_logger();
|
|
50022
|
-
var {spawn:
|
|
50023
|
-
import { spawn as
|
|
50024
|
-
import { existsSync as existsSync51, statSync as
|
|
50282
|
+
var {spawn: bunSpawn2 } = globalThis.Bun;
|
|
50283
|
+
import { spawn as nodeSpawn2 } from "child_process";
|
|
50284
|
+
import { existsSync as existsSync51, statSync as statSync7 } from "fs";
|
|
50025
50285
|
function shouldUseNodeSpawn() {
|
|
50026
50286
|
return process.platform === "win32";
|
|
50027
50287
|
}
|
|
@@ -50030,7 +50290,7 @@ function validateCwd(cwd) {
|
|
|
50030
50290
|
if (!existsSync51(cwd)) {
|
|
50031
50291
|
return { valid: false, error: `Working directory does not exist: ${cwd}` };
|
|
50032
50292
|
}
|
|
50033
|
-
const stats =
|
|
50293
|
+
const stats = statSync7(cwd);
|
|
50034
50294
|
if (!stats.isDirectory()) {
|
|
50035
50295
|
return { valid: false, error: `Path is not a directory: ${cwd}` };
|
|
50036
50296
|
}
|
|
@@ -50039,7 +50299,7 @@ function validateCwd(cwd) {
|
|
|
50039
50299
|
return { valid: false, error: `Cannot access working directory: ${cwd} (${err instanceof Error ? err.message : String(err)})` };
|
|
50040
50300
|
}
|
|
50041
50301
|
}
|
|
50042
|
-
function
|
|
50302
|
+
function wrapNodeProcess2(proc) {
|
|
50043
50303
|
let resolveExited;
|
|
50044
50304
|
let exitCode = null;
|
|
50045
50305
|
const exitedPromise = new Promise((resolve8) => {
|
|
@@ -50140,16 +50400,16 @@ function spawnProcess(command, options) {
|
|
|
50140
50400
|
if (shouldUseNodeSpawn()) {
|
|
50141
50401
|
const [cmd, ...args] = command;
|
|
50142
50402
|
log("[LSP] Using Node.js child_process on Windows to avoid Bun spawn segfault");
|
|
50143
|
-
const proc2 =
|
|
50403
|
+
const proc2 = nodeSpawn2(cmd, args, {
|
|
50144
50404
|
cwd: options.cwd,
|
|
50145
50405
|
env: options.env,
|
|
50146
50406
|
stdio: ["pipe", "pipe", "pipe"],
|
|
50147
50407
|
windowsHide: true,
|
|
50148
50408
|
shell: true
|
|
50149
50409
|
});
|
|
50150
|
-
return
|
|
50410
|
+
return wrapNodeProcess2(proc2);
|
|
50151
50411
|
}
|
|
50152
|
-
const proc =
|
|
50412
|
+
const proc = bunSpawn2(command, {
|
|
50153
50413
|
stdin: "pipe",
|
|
50154
50414
|
stdout: "pipe",
|
|
50155
50415
|
stderr: "pipe",
|
|
@@ -50168,7 +50428,7 @@ import { pathToFileURL } from "url";
|
|
|
50168
50428
|
|
|
50169
50429
|
// src/tools/lsp/lsp-client-transport.ts
|
|
50170
50430
|
var import_node = __toESM(require_main(), 1);
|
|
50171
|
-
import { Readable, Writable } from "stream";
|
|
50431
|
+
import { Readable as Readable2, Writable } from "stream";
|
|
50172
50432
|
init_logger();
|
|
50173
50433
|
|
|
50174
50434
|
class LSPClientTransport {
|
|
@@ -50204,7 +50464,7 @@ class LSPClientTransport {
|
|
|
50204
50464
|
stderr: ${stderr}` : ""));
|
|
50205
50465
|
}
|
|
50206
50466
|
const stdoutReader = this.proc.stdout.getReader();
|
|
50207
|
-
const nodeReadable = new
|
|
50467
|
+
const nodeReadable = new Readable2({
|
|
50208
50468
|
async read() {
|
|
50209
50469
|
try {
|
|
50210
50470
|
const { done, value } = await stdoutReader.read();
|
|
@@ -51301,7 +51561,7 @@ var DEFAULT_MAX_MATCHES = 500;
|
|
|
51301
51561
|
// src/tools/ast-grep/sg-cli-path.ts
|
|
51302
51562
|
import { createRequire as createRequire4 } from "module";
|
|
51303
51563
|
import { dirname as dirname16, join as join63 } from "path";
|
|
51304
|
-
import { existsSync as existsSync54, statSync as
|
|
51564
|
+
import { existsSync as existsSync54, statSync as statSync8 } from "fs";
|
|
51305
51565
|
|
|
51306
51566
|
// src/tools/ast-grep/downloader.ts
|
|
51307
51567
|
import { existsSync as existsSync53 } from "fs";
|
|
@@ -51388,7 +51648,7 @@ async function ensureAstGrepBinary() {
|
|
|
51388
51648
|
// src/tools/ast-grep/sg-cli-path.ts
|
|
51389
51649
|
function isValidBinary(filePath) {
|
|
51390
51650
|
try {
|
|
51391
|
-
return
|
|
51651
|
+
return statSync8(filePath).size > 1e4;
|
|
51392
51652
|
} catch {
|
|
51393
51653
|
return false;
|
|
51394
51654
|
}
|
|
@@ -53470,7 +53730,7 @@ async function searchInSession(sessionID, query, caseSensitive = false, maxResul
|
|
|
53470
53730
|
// src/tools/session-manager/tools.ts
|
|
53471
53731
|
var SEARCH_TIMEOUT_MS = 60000;
|
|
53472
53732
|
var MAX_SESSIONS_TO_SCAN = 50;
|
|
53473
|
-
function
|
|
53733
|
+
function withTimeout3(promise2, ms, operation) {
|
|
53474
53734
|
return Promise.race([
|
|
53475
53735
|
promise2,
|
|
53476
53736
|
new Promise((_, reject) => setTimeout(() => reject(new Error(`${operation} timed out after ${ms}ms`)), ms))
|
|
@@ -53557,7 +53817,7 @@ function createSessionManagerTools(ctx) {
|
|
|
53557
53817
|
}
|
|
53558
53818
|
return allResults.slice(0, resultLimit);
|
|
53559
53819
|
};
|
|
53560
|
-
const results = await
|
|
53820
|
+
const results = await withTimeout3(searchOperation(), SEARCH_TIMEOUT_MS, "Search");
|
|
53561
53821
|
return formatSearchResults(results);
|
|
53562
53822
|
} catch (e) {
|
|
53563
53823
|
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
@@ -53675,7 +53935,7 @@ tmux capture-pane -p -t ${sessionName} -S -1000
|
|
|
53675
53935
|
|
|
53676
53936
|
The Bash tool can execute these commands directly. Do NOT retry with interactive_bash.`;
|
|
53677
53937
|
}
|
|
53678
|
-
const proc =
|
|
53938
|
+
const proc = spawnWithWindowsHide([tmuxPath2, ...parts], {
|
|
53679
53939
|
stdout: "pipe",
|
|
53680
53940
|
stderr: "pipe"
|
|
53681
53941
|
});
|
|
@@ -54250,6 +54510,14 @@ function formatResolvedTitle(task) {
|
|
|
54250
54510
|
const label = task.agent === SISYPHUS_JUNIOR_AGENT && task.category ? task.category : task.agent;
|
|
54251
54511
|
return `${label} - ${task.description}`;
|
|
54252
54512
|
}
|
|
54513
|
+
function isTaskActiveStatus(status) {
|
|
54514
|
+
return status === "pending" || status === "running";
|
|
54515
|
+
}
|
|
54516
|
+
function appendTimeoutNote(output, timeoutMs) {
|
|
54517
|
+
return `${output}
|
|
54518
|
+
|
|
54519
|
+
> **Timed out waiting** after ${timeoutMs}ms. Task is still running; showing latest available output.`;
|
|
54520
|
+
}
|
|
54253
54521
|
function createBackgroundOutput(manager, client2) {
|
|
54254
54522
|
return tool({
|
|
54255
54523
|
description: BACKGROUND_OUTPUT_DESCRIPTION,
|
|
@@ -54290,7 +54558,8 @@ function createBackgroundOutput(manager, client2) {
|
|
|
54290
54558
|
const timeoutMs = Math.min(args.timeout ?? 60000, 600000);
|
|
54291
54559
|
const fullSession = args.full_session ?? true;
|
|
54292
54560
|
let resolvedTask = task;
|
|
54293
|
-
|
|
54561
|
+
let didTimeoutWhileActive = false;
|
|
54562
|
+
if (shouldBlock && isTaskActiveStatus(task.status)) {
|
|
54294
54563
|
const startTime = Date.now();
|
|
54295
54564
|
while (Date.now() - startTime < timeoutMs) {
|
|
54296
54565
|
await delay3(1000);
|
|
@@ -54298,27 +54567,33 @@ function createBackgroundOutput(manager, client2) {
|
|
|
54298
54567
|
if (!currentTask) {
|
|
54299
54568
|
return `Task was deleted: ${args.task_id}`;
|
|
54300
54569
|
}
|
|
54301
|
-
|
|
54302
|
-
|
|
54570
|
+
resolvedTask = currentTask;
|
|
54571
|
+
if (!isTaskActiveStatus(currentTask.status)) {
|
|
54303
54572
|
break;
|
|
54304
54573
|
}
|
|
54305
54574
|
}
|
|
54306
|
-
|
|
54307
|
-
|
|
54308
|
-
|
|
54575
|
+
if (isTaskActiveStatus(resolvedTask.status)) {
|
|
54576
|
+
const finalCheck = manager.getTask(args.task_id);
|
|
54577
|
+
if (finalCheck) {
|
|
54578
|
+
resolvedTask = finalCheck;
|
|
54579
|
+
}
|
|
54580
|
+
}
|
|
54581
|
+
if (isTaskActiveStatus(resolvedTask.status)) {
|
|
54582
|
+
didTimeoutWhileActive = true;
|
|
54309
54583
|
}
|
|
54310
54584
|
}
|
|
54311
|
-
const isActive = resolvedTask.status
|
|
54585
|
+
const isActive = isTaskActiveStatus(resolvedTask.status);
|
|
54312
54586
|
const includeThinking = isActive || (args.include_thinking ?? false);
|
|
54313
54587
|
const includeToolResults = isActive || (args.include_tool_results ?? false);
|
|
54314
54588
|
if (fullSession) {
|
|
54315
|
-
|
|
54589
|
+
const output = await formatFullSession(resolvedTask, client2, {
|
|
54316
54590
|
includeThinking,
|
|
54317
54591
|
messageLimit: args.message_limit,
|
|
54318
54592
|
sinceMessageId: args.since_message_id,
|
|
54319
54593
|
includeToolResults,
|
|
54320
54594
|
thinkingMaxChars: args.thinking_max_chars
|
|
54321
54595
|
});
|
|
54596
|
+
return didTimeoutWhileActive ? appendTimeoutNote(output, timeoutMs) : output;
|
|
54322
54597
|
}
|
|
54323
54598
|
if (resolvedTask.status === "completed") {
|
|
54324
54599
|
return await formatTaskResult(resolvedTask, client2);
|
|
@@ -54326,7 +54601,8 @@ function createBackgroundOutput(manager, client2) {
|
|
|
54326
54601
|
if (resolvedTask.status === "error" || resolvedTask.status === "cancelled" || resolvedTask.status === "interrupt") {
|
|
54327
54602
|
return formatTaskStatus(resolvedTask);
|
|
54328
54603
|
}
|
|
54329
|
-
|
|
54604
|
+
const statusOutput = formatTaskStatus(resolvedTask);
|
|
54605
|
+
return didTimeoutWhileActive ? appendTimeoutNote(statusOutput, timeoutMs) : statusOutput;
|
|
54330
54606
|
} catch (error45) {
|
|
54331
54607
|
return `Error getting output: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
54332
54608
|
}
|
|
@@ -55062,47 +55338,153 @@ init_logger();
|
|
|
55062
55338
|
|
|
55063
55339
|
// src/tools/delegate-task/prompt-builder.ts
|
|
55064
55340
|
init_constants();
|
|
55341
|
+
|
|
55342
|
+
// src/tools/delegate-task/token-limiter.ts
|
|
55343
|
+
var CHARACTERS_PER_TOKEN = 4;
|
|
55344
|
+
function estimateTokenCount(text) {
|
|
55345
|
+
if (!text) {
|
|
55346
|
+
return 0;
|
|
55347
|
+
}
|
|
55348
|
+
return Math.ceil(text.length / CHARACTERS_PER_TOKEN);
|
|
55349
|
+
}
|
|
55350
|
+
function truncateToTokenBudget(content, maxTokens) {
|
|
55351
|
+
if (!content || maxTokens <= 0) {
|
|
55352
|
+
return "";
|
|
55353
|
+
}
|
|
55354
|
+
const maxCharacters = maxTokens * CHARACTERS_PER_TOKEN;
|
|
55355
|
+
if (content.length <= maxCharacters) {
|
|
55356
|
+
return content;
|
|
55357
|
+
}
|
|
55358
|
+
const sliced = content.slice(0, maxCharacters);
|
|
55359
|
+
const lastNewline = sliced.lastIndexOf(`
|
|
55360
|
+
`);
|
|
55361
|
+
if (lastNewline > 0) {
|
|
55362
|
+
return `${sliced.slice(0, lastNewline)}
|
|
55363
|
+
[TRUNCATED]`;
|
|
55364
|
+
}
|
|
55365
|
+
return `${sliced}
|
|
55366
|
+
[TRUNCATED]`;
|
|
55367
|
+
}
|
|
55368
|
+
function joinSystemParts(parts) {
|
|
55369
|
+
const filtered = parts.filter((part) => part.trim().length > 0);
|
|
55370
|
+
if (filtered.length === 0) {
|
|
55371
|
+
return;
|
|
55372
|
+
}
|
|
55373
|
+
return filtered.join(`
|
|
55374
|
+
|
|
55375
|
+
`);
|
|
55376
|
+
}
|
|
55377
|
+
function reduceSegmentToFitBudget(content, overflowTokens) {
|
|
55378
|
+
if (overflowTokens <= 0 || !content) {
|
|
55379
|
+
return content;
|
|
55380
|
+
}
|
|
55381
|
+
const currentTokens = estimateTokenCount(content);
|
|
55382
|
+
const nextBudget = Math.max(0, currentTokens - overflowTokens);
|
|
55383
|
+
return truncateToTokenBudget(content, nextBudget);
|
|
55384
|
+
}
|
|
55385
|
+
function buildSystemContentWithTokenLimit(input, maxTokens) {
|
|
55386
|
+
const skillParts = input.skillContents?.length ? [...input.skillContents] : input.skillContent ? [input.skillContent] : [];
|
|
55387
|
+
const categoryPromptAppend = input.categoryPromptAppend ?? "";
|
|
55388
|
+
const agentsContext = input.agentsContext ?? input.planAgentPrepend ?? "";
|
|
55389
|
+
if (maxTokens === undefined) {
|
|
55390
|
+
return joinSystemParts([agentsContext, ...skillParts, categoryPromptAppend]);
|
|
55391
|
+
}
|
|
55392
|
+
let nextSkills = [...skillParts];
|
|
55393
|
+
let nextCategoryPromptAppend = categoryPromptAppend;
|
|
55394
|
+
let nextAgentsContext = agentsContext;
|
|
55395
|
+
const buildCurrentContent = () => joinSystemParts([nextAgentsContext, ...nextSkills, nextCategoryPromptAppend]);
|
|
55396
|
+
let systemContent = buildCurrentContent();
|
|
55397
|
+
if (!systemContent) {
|
|
55398
|
+
return;
|
|
55399
|
+
}
|
|
55400
|
+
let overflowTokens = estimateTokenCount(systemContent) - maxTokens;
|
|
55401
|
+
if (overflowTokens > 0) {
|
|
55402
|
+
for (let index = 0;index < nextSkills.length && overflowTokens > 0; index += 1) {
|
|
55403
|
+
const skill2 = nextSkills[index];
|
|
55404
|
+
const reducedSkill = reduceSegmentToFitBudget(skill2, overflowTokens);
|
|
55405
|
+
nextSkills[index] = reducedSkill;
|
|
55406
|
+
systemContent = buildCurrentContent();
|
|
55407
|
+
if (!systemContent) {
|
|
55408
|
+
return;
|
|
55409
|
+
}
|
|
55410
|
+
overflowTokens = estimateTokenCount(systemContent) - maxTokens;
|
|
55411
|
+
}
|
|
55412
|
+
nextSkills = nextSkills.filter((skill2) => skill2.trim().length > 0);
|
|
55413
|
+
systemContent = buildCurrentContent();
|
|
55414
|
+
if (!systemContent) {
|
|
55415
|
+
return;
|
|
55416
|
+
}
|
|
55417
|
+
overflowTokens = estimateTokenCount(systemContent) - maxTokens;
|
|
55418
|
+
}
|
|
55419
|
+
if (overflowTokens > 0 && nextCategoryPromptAppend) {
|
|
55420
|
+
nextCategoryPromptAppend = reduceSegmentToFitBudget(nextCategoryPromptAppend, overflowTokens);
|
|
55421
|
+
systemContent = buildCurrentContent();
|
|
55422
|
+
if (!systemContent) {
|
|
55423
|
+
return;
|
|
55424
|
+
}
|
|
55425
|
+
overflowTokens = estimateTokenCount(systemContent) - maxTokens;
|
|
55426
|
+
}
|
|
55427
|
+
if (overflowTokens > 0 && nextAgentsContext) {
|
|
55428
|
+
nextAgentsContext = reduceSegmentToFitBudget(nextAgentsContext, overflowTokens);
|
|
55429
|
+
systemContent = buildCurrentContent();
|
|
55430
|
+
if (!systemContent) {
|
|
55431
|
+
return;
|
|
55432
|
+
}
|
|
55433
|
+
}
|
|
55434
|
+
if (!systemContent) {
|
|
55435
|
+
return;
|
|
55436
|
+
}
|
|
55437
|
+
return truncateToTokenBudget(systemContent, maxTokens);
|
|
55438
|
+
}
|
|
55439
|
+
|
|
55440
|
+
// src/tools/delegate-task/prompt-builder.ts
|
|
55441
|
+
var FREE_OR_LOCAL_PROMPT_TOKEN_LIMIT = 24000;
|
|
55442
|
+
function usesFreeOrLocalModel(model) {
|
|
55443
|
+
if (!model) {
|
|
55444
|
+
return false;
|
|
55445
|
+
}
|
|
55446
|
+
const provider = model.providerID.toLowerCase();
|
|
55447
|
+
const modelId = model.modelID.toLowerCase();
|
|
55448
|
+
return provider.includes("local") || provider === "ollama" || provider === "lmstudio" || modelId.includes("free");
|
|
55449
|
+
}
|
|
55065
55450
|
function buildSystemContent(input) {
|
|
55066
55451
|
const {
|
|
55067
55452
|
skillContent,
|
|
55453
|
+
skillContents,
|
|
55068
55454
|
categoryPromptAppend,
|
|
55455
|
+
agentsContext,
|
|
55456
|
+
maxPromptTokens,
|
|
55457
|
+
model,
|
|
55069
55458
|
agentName,
|
|
55070
55459
|
availableCategories,
|
|
55071
55460
|
availableSkills
|
|
55072
55461
|
} = input;
|
|
55073
55462
|
const planAgentPrepend = isPlanAgent(agentName) ? buildPlanAgentSystemPrepend(availableCategories, availableSkills) : "";
|
|
55074
|
-
|
|
55075
|
-
|
|
55076
|
-
|
|
55077
|
-
|
|
55078
|
-
|
|
55079
|
-
|
|
55080
|
-
|
|
55081
|
-
|
|
55082
|
-
parts.push(skillContent);
|
|
55083
|
-
}
|
|
55084
|
-
if (categoryPromptAppend) {
|
|
55085
|
-
parts.push(categoryPromptAppend);
|
|
55086
|
-
}
|
|
55087
|
-
return parts.join(`
|
|
55088
|
-
|
|
55089
|
-
`) || undefined;
|
|
55463
|
+
const effectiveMaxPromptTokens = maxPromptTokens ?? (usesFreeOrLocalModel(model) ? FREE_OR_LOCAL_PROMPT_TOKEN_LIMIT : undefined);
|
|
55464
|
+
return buildSystemContentWithTokenLimit({
|
|
55465
|
+
skillContent,
|
|
55466
|
+
skillContents,
|
|
55467
|
+
categoryPromptAppend,
|
|
55468
|
+
agentsContext: agentsContext ?? planAgentPrepend,
|
|
55469
|
+
planAgentPrepend
|
|
55470
|
+
}, effectiveMaxPromptTokens);
|
|
55090
55471
|
}
|
|
55091
55472
|
|
|
55092
55473
|
// src/tools/delegate-task/skill-resolver.ts
|
|
55093
55474
|
async function resolveSkillContent2(skills2, options) {
|
|
55094
55475
|
if (skills2.length === 0) {
|
|
55095
|
-
return { content: undefined, error: null };
|
|
55476
|
+
return { content: undefined, contents: [], error: null };
|
|
55096
55477
|
}
|
|
55097
55478
|
const { resolved, notFound } = await resolveMultipleSkillsAsync(skills2, options);
|
|
55098
55479
|
if (notFound.length > 0) {
|
|
55099
55480
|
const allSkills = await discoverSkills({ includeClaudeCodePaths: true, directory: options?.directory });
|
|
55100
55481
|
const available = allSkills.map((s) => s.name).join(", ");
|
|
55101
|
-
return { content: undefined, error: `Skills not found: ${notFound.join(", ")}. Available: ${available}` };
|
|
55482
|
+
return { content: undefined, contents: [], error: `Skills not found: ${notFound.join(", ")}. Available: ${available}` };
|
|
55102
55483
|
}
|
|
55103
|
-
|
|
55484
|
+
const contents = Array.from(resolved.values());
|
|
55485
|
+
return { content: contents.join(`
|
|
55104
55486
|
|
|
55105
|
-
`), error: null };
|
|
55487
|
+
`), contents, error: null };
|
|
55106
55488
|
}
|
|
55107
55489
|
// src/tools/delegate-task/parent-context-resolver.ts
|
|
55108
55490
|
init_logger();
|
|
@@ -56173,6 +56555,7 @@ async function resolveCategoryExecution(args, executorCtx, inheritedModel, syste
|
|
|
56173
56555
|
agentToUse: "",
|
|
56174
56556
|
categoryModel: undefined,
|
|
56175
56557
|
categoryPromptAppend: undefined,
|
|
56558
|
+
maxPromptTokens: undefined,
|
|
56176
56559
|
modelInfo: undefined,
|
|
56177
56560
|
actualModel: undefined,
|
|
56178
56561
|
isUnstableAgent: false,
|
|
@@ -56189,6 +56572,7 @@ Available categories: ${allCategoryNames}`
|
|
|
56189
56572
|
agentToUse: "",
|
|
56190
56573
|
categoryModel: undefined,
|
|
56191
56574
|
categoryPromptAppend: undefined,
|
|
56575
|
+
maxPromptTokens: undefined,
|
|
56192
56576
|
modelInfo: undefined,
|
|
56193
56577
|
actualModel: undefined,
|
|
56194
56578
|
isUnstableAgent: false,
|
|
@@ -56222,6 +56606,7 @@ Available categories: ${allCategoryNames}`
|
|
|
56222
56606
|
agentToUse: "",
|
|
56223
56607
|
categoryModel: undefined,
|
|
56224
56608
|
categoryPromptAppend: undefined,
|
|
56609
|
+
maxPromptTokens: undefined,
|
|
56225
56610
|
modelInfo: undefined,
|
|
56226
56611
|
actualModel: undefined,
|
|
56227
56612
|
isUnstableAgent: false,
|
|
@@ -56247,6 +56632,7 @@ Available categories: ${allCategoryNames}`
|
|
|
56247
56632
|
agentToUse: "",
|
|
56248
56633
|
categoryModel: undefined,
|
|
56249
56634
|
categoryPromptAppend: undefined,
|
|
56635
|
+
maxPromptTokens: undefined,
|
|
56250
56636
|
modelInfo: undefined,
|
|
56251
56637
|
actualModel: undefined,
|
|
56252
56638
|
isUnstableAgent: false,
|
|
@@ -56268,6 +56654,7 @@ Available categories: ${categoryNames.join(", ")}`
|
|
|
56268
56654
|
agentToUse: SISYPHUS_JUNIOR_AGENT2,
|
|
56269
56655
|
categoryModel,
|
|
56270
56656
|
categoryPromptAppend,
|
|
56657
|
+
maxPromptTokens: resolved.config.max_prompt_tokens,
|
|
56271
56658
|
modelInfo,
|
|
56272
56659
|
actualModel,
|
|
56273
56660
|
isUnstableAgent,
|
|
@@ -56477,7 +56864,7 @@ function createDelegateTask(options) {
|
|
|
56477
56864
|
throw new Error(`Invalid arguments: load_skills=null is not allowed. Pass [] if no skills needed.`);
|
|
56478
56865
|
}
|
|
56479
56866
|
const runInBackground = args.run_in_background === true;
|
|
56480
|
-
const { content: skillContent, error: skillError } = await resolveSkillContent2(args.load_skills, {
|
|
56867
|
+
const { content: skillContent, contents: skillContents, error: skillError } = await resolveSkillContent2(args.load_skills, {
|
|
56481
56868
|
gitMasterConfig: options.gitMasterConfig,
|
|
56482
56869
|
browserProvider: options.browserProvider,
|
|
56483
56870
|
disabledSkills: options.disabledSkills,
|
|
@@ -56511,6 +56898,7 @@ function createDelegateTask(options) {
|
|
|
56511
56898
|
let actualModel;
|
|
56512
56899
|
let isUnstableAgent = false;
|
|
56513
56900
|
let fallbackChain;
|
|
56901
|
+
let maxPromptTokens;
|
|
56514
56902
|
if (args.category) {
|
|
56515
56903
|
const resolution = await resolveCategoryExecution(args, options, inheritedModel, systemDefaultModel);
|
|
56516
56904
|
if (resolution.error) {
|
|
@@ -56523,6 +56911,7 @@ function createDelegateTask(options) {
|
|
|
56523
56911
|
actualModel = resolution.actualModel;
|
|
56524
56912
|
isUnstableAgent = resolution.isUnstableAgent;
|
|
56525
56913
|
fallbackChain = resolution.fallbackChain;
|
|
56914
|
+
maxPromptTokens = resolution.maxPromptTokens;
|
|
56526
56915
|
const isRunInBackgroundExplicitlyFalse = args.run_in_background === false || args.run_in_background === "false";
|
|
56527
56916
|
log("[task] unstable agent detection", {
|
|
56528
56917
|
category: args.category,
|
|
@@ -56536,8 +56925,11 @@ function createDelegateTask(options) {
|
|
|
56536
56925
|
if (isUnstableAgent && isRunInBackgroundExplicitlyFalse) {
|
|
56537
56926
|
const systemContent2 = buildSystemContent({
|
|
56538
56927
|
skillContent,
|
|
56928
|
+
skillContents,
|
|
56539
56929
|
categoryPromptAppend,
|
|
56540
56930
|
agentName: agentToUse,
|
|
56931
|
+
maxPromptTokens,
|
|
56932
|
+
model: categoryModel,
|
|
56541
56933
|
availableCategories,
|
|
56542
56934
|
availableSkills
|
|
56543
56935
|
});
|
|
@@ -56554,8 +56946,11 @@ function createDelegateTask(options) {
|
|
|
56554
56946
|
}
|
|
56555
56947
|
const systemContent = buildSystemContent({
|
|
56556
56948
|
skillContent,
|
|
56949
|
+
skillContents,
|
|
56557
56950
|
categoryPromptAppend,
|
|
56558
56951
|
agentName: agentToUse,
|
|
56952
|
+
maxPromptTokens,
|
|
56953
|
+
model: categoryModel,
|
|
56559
56954
|
availableCategories,
|
|
56560
56955
|
availableSkills
|
|
56561
56956
|
});
|
|
@@ -57356,14 +57751,19 @@ function normalizeEditPayload(payload) {
|
|
|
57356
57751
|
return toNewLines(payload).join(`
|
|
57357
57752
|
`);
|
|
57358
57753
|
}
|
|
57754
|
+
function canonicalAnchor(anchor) {
|
|
57755
|
+
if (!anchor)
|
|
57756
|
+
return "";
|
|
57757
|
+
return normalizeLineRef(anchor);
|
|
57758
|
+
}
|
|
57359
57759
|
function buildDedupeKey(edit) {
|
|
57360
57760
|
switch (edit.op) {
|
|
57361
57761
|
case "replace":
|
|
57362
|
-
return `replace|${edit.pos}|${edit.end
|
|
57762
|
+
return `replace|${canonicalAnchor(edit.pos)}|${edit.end ? canonicalAnchor(edit.end) : ""}|${normalizeEditPayload(edit.lines)}`;
|
|
57363
57763
|
case "append":
|
|
57364
|
-
return `append|${edit.pos
|
|
57764
|
+
return `append|${canonicalAnchor(edit.pos)}|${normalizeEditPayload(edit.lines)}`;
|
|
57365
57765
|
case "prepend":
|
|
57366
|
-
return `prepend|${edit.pos
|
|
57766
|
+
return `prepend|${canonicalAnchor(edit.pos)}|${normalizeEditPayload(edit.lines)}`;
|
|
57367
57767
|
default:
|
|
57368
57768
|
return JSON.stringify(edit);
|
|
57369
57769
|
}
|
|
@@ -57748,66 +58148,448 @@ function applyHashlineEditsWithReport(content, edits) {
|
|
|
57748
58148
|
deduplicatedEdits: dedupeResult.deduplicatedEdits
|
|
57749
58149
|
};
|
|
57750
58150
|
}
|
|
57751
|
-
//
|
|
57752
|
-
|
|
57753
|
-
|
|
57754
|
-
|
|
57755
|
-
|
|
58151
|
+
// node_modules/diff/libesm/diff/base.js
|
|
58152
|
+
class Diff {
|
|
58153
|
+
diff(oldStr, newStr, options = {}) {
|
|
58154
|
+
let callback;
|
|
58155
|
+
if (typeof options === "function") {
|
|
58156
|
+
callback = options;
|
|
58157
|
+
options = {};
|
|
58158
|
+
} else if ("callback" in options) {
|
|
58159
|
+
callback = options.callback;
|
|
58160
|
+
}
|
|
58161
|
+
const oldString = this.castInput(oldStr, options);
|
|
58162
|
+
const newString = this.castInput(newStr, options);
|
|
58163
|
+
const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
|
|
58164
|
+
const newTokens = this.removeEmpty(this.tokenize(newString, options));
|
|
58165
|
+
return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
|
|
58166
|
+
}
|
|
58167
|
+
diffWithOptionsObj(oldTokens, newTokens, options, callback) {
|
|
58168
|
+
var _a;
|
|
58169
|
+
const done = (value) => {
|
|
58170
|
+
value = this.postProcess(value, options);
|
|
58171
|
+
if (callback) {
|
|
58172
|
+
setTimeout(function() {
|
|
58173
|
+
callback(value);
|
|
58174
|
+
}, 0);
|
|
58175
|
+
return;
|
|
58176
|
+
} else {
|
|
58177
|
+
return value;
|
|
58178
|
+
}
|
|
58179
|
+
};
|
|
58180
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
58181
|
+
let editLength = 1;
|
|
58182
|
+
let maxEditLength = newLen + oldLen;
|
|
58183
|
+
if (options.maxEditLength != null) {
|
|
58184
|
+
maxEditLength = Math.min(maxEditLength, options.maxEditLength);
|
|
58185
|
+
}
|
|
58186
|
+
const maxExecutionTime = (_a = options.timeout) !== null && _a !== undefined ? _a : Infinity;
|
|
58187
|
+
const abortAfterTimestamp = Date.now() + maxExecutionTime;
|
|
58188
|
+
const bestPath = [{ oldPos: -1, lastComponent: undefined }];
|
|
58189
|
+
let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
|
|
58190
|
+
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
58191
|
+
return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
|
|
58192
|
+
}
|
|
58193
|
+
let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
|
|
58194
|
+
const execEditLength = () => {
|
|
58195
|
+
for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength);diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
58196
|
+
let basePath;
|
|
58197
|
+
const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
|
|
58198
|
+
if (removePath) {
|
|
58199
|
+
bestPath[diagonalPath - 1] = undefined;
|
|
58200
|
+
}
|
|
58201
|
+
let canAdd = false;
|
|
58202
|
+
if (addPath) {
|
|
58203
|
+
const addPathNewPos = addPath.oldPos - diagonalPath;
|
|
58204
|
+
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
58205
|
+
}
|
|
58206
|
+
const canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
58207
|
+
if (!canAdd && !canRemove) {
|
|
58208
|
+
bestPath[diagonalPath] = undefined;
|
|
58209
|
+
continue;
|
|
58210
|
+
}
|
|
58211
|
+
if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
|
|
58212
|
+
basePath = this.addToPath(addPath, true, false, 0, options);
|
|
58213
|
+
} else {
|
|
58214
|
+
basePath = this.addToPath(removePath, false, true, 1, options);
|
|
58215
|
+
}
|
|
58216
|
+
newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
|
|
58217
|
+
if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
58218
|
+
return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
|
|
58219
|
+
} else {
|
|
58220
|
+
bestPath[diagonalPath] = basePath;
|
|
58221
|
+
if (basePath.oldPos + 1 >= oldLen) {
|
|
58222
|
+
maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
|
|
58223
|
+
}
|
|
58224
|
+
if (newPos + 1 >= newLen) {
|
|
58225
|
+
minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
|
|
58226
|
+
}
|
|
58227
|
+
}
|
|
58228
|
+
}
|
|
58229
|
+
editLength++;
|
|
58230
|
+
};
|
|
58231
|
+
if (callback) {
|
|
58232
|
+
(function exec2() {
|
|
58233
|
+
setTimeout(function() {
|
|
58234
|
+
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
|
|
58235
|
+
return callback(undefined);
|
|
58236
|
+
}
|
|
58237
|
+
if (!execEditLength()) {
|
|
58238
|
+
exec2();
|
|
58239
|
+
}
|
|
58240
|
+
}, 0);
|
|
58241
|
+
})();
|
|
58242
|
+
} else {
|
|
58243
|
+
while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
58244
|
+
const ret = execEditLength();
|
|
58245
|
+
if (ret) {
|
|
58246
|
+
return ret;
|
|
58247
|
+
}
|
|
58248
|
+
}
|
|
58249
|
+
}
|
|
58250
|
+
}
|
|
58251
|
+
addToPath(path11, added, removed, oldPosInc, options) {
|
|
58252
|
+
const last = path11.lastComponent;
|
|
58253
|
+
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
58254
|
+
return {
|
|
58255
|
+
oldPos: path11.oldPos + oldPosInc,
|
|
58256
|
+
lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
|
|
58257
|
+
};
|
|
58258
|
+
} else {
|
|
58259
|
+
return {
|
|
58260
|
+
oldPos: path11.oldPos + oldPosInc,
|
|
58261
|
+
lastComponent: { count: 1, added, removed, previousComponent: last }
|
|
58262
|
+
};
|
|
58263
|
+
}
|
|
58264
|
+
}
|
|
58265
|
+
extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
|
|
58266
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
58267
|
+
let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
58268
|
+
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
|
|
58269
|
+
newPos++;
|
|
58270
|
+
oldPos++;
|
|
58271
|
+
commonCount++;
|
|
58272
|
+
if (options.oneChangePerToken) {
|
|
58273
|
+
basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
58274
|
+
}
|
|
58275
|
+
}
|
|
58276
|
+
if (commonCount && !options.oneChangePerToken) {
|
|
58277
|
+
basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
58278
|
+
}
|
|
58279
|
+
basePath.oldPos = oldPos;
|
|
58280
|
+
return newPos;
|
|
58281
|
+
}
|
|
58282
|
+
equals(left, right, options) {
|
|
58283
|
+
if (options.comparator) {
|
|
58284
|
+
return options.comparator(left, right);
|
|
58285
|
+
} else {
|
|
58286
|
+
return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
58287
|
+
}
|
|
58288
|
+
}
|
|
58289
|
+
removeEmpty(array2) {
|
|
58290
|
+
const ret = [];
|
|
58291
|
+
for (let i2 = 0;i2 < array2.length; i2++) {
|
|
58292
|
+
if (array2[i2]) {
|
|
58293
|
+
ret.push(array2[i2]);
|
|
58294
|
+
}
|
|
58295
|
+
}
|
|
58296
|
+
return ret;
|
|
58297
|
+
}
|
|
58298
|
+
castInput(value, options) {
|
|
58299
|
+
return value;
|
|
58300
|
+
}
|
|
58301
|
+
tokenize(value, options) {
|
|
58302
|
+
return Array.from(value);
|
|
58303
|
+
}
|
|
58304
|
+
join(chars) {
|
|
58305
|
+
return chars.join("");
|
|
58306
|
+
}
|
|
58307
|
+
postProcess(changeObjects, options) {
|
|
58308
|
+
return changeObjects;
|
|
58309
|
+
}
|
|
58310
|
+
get useLongestToken() {
|
|
58311
|
+
return false;
|
|
58312
|
+
}
|
|
58313
|
+
buildValues(lastComponent, newTokens, oldTokens) {
|
|
58314
|
+
const components = [];
|
|
58315
|
+
let nextComponent;
|
|
58316
|
+
while (lastComponent) {
|
|
58317
|
+
components.push(lastComponent);
|
|
58318
|
+
nextComponent = lastComponent.previousComponent;
|
|
58319
|
+
delete lastComponent.previousComponent;
|
|
58320
|
+
lastComponent = nextComponent;
|
|
58321
|
+
}
|
|
58322
|
+
components.reverse();
|
|
58323
|
+
const componentLen = components.length;
|
|
58324
|
+
let componentPos = 0, newPos = 0, oldPos = 0;
|
|
58325
|
+
for (;componentPos < componentLen; componentPos++) {
|
|
58326
|
+
const component = components[componentPos];
|
|
58327
|
+
if (!component.removed) {
|
|
58328
|
+
if (!component.added && this.useLongestToken) {
|
|
58329
|
+
let value = newTokens.slice(newPos, newPos + component.count);
|
|
58330
|
+
value = value.map(function(value2, i2) {
|
|
58331
|
+
const oldValue = oldTokens[oldPos + i2];
|
|
58332
|
+
return oldValue.length > value2.length ? oldValue : value2;
|
|
58333
|
+
});
|
|
58334
|
+
component.value = this.join(value);
|
|
58335
|
+
} else {
|
|
58336
|
+
component.value = this.join(newTokens.slice(newPos, newPos + component.count));
|
|
58337
|
+
}
|
|
58338
|
+
newPos += component.count;
|
|
58339
|
+
if (!component.added) {
|
|
58340
|
+
oldPos += component.count;
|
|
58341
|
+
}
|
|
58342
|
+
} else {
|
|
58343
|
+
component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
|
|
58344
|
+
oldPos += component.count;
|
|
58345
|
+
}
|
|
58346
|
+
}
|
|
58347
|
+
return components;
|
|
58348
|
+
}
|
|
58349
|
+
}
|
|
58350
|
+
|
|
58351
|
+
// node_modules/diff/libesm/diff/line.js
|
|
58352
|
+
class LineDiff extends Diff {
|
|
58353
|
+
constructor() {
|
|
58354
|
+
super(...arguments);
|
|
58355
|
+
this.tokenize = tokenize;
|
|
58356
|
+
}
|
|
58357
|
+
equals(left, right, options) {
|
|
58358
|
+
if (options.ignoreWhitespace) {
|
|
58359
|
+
if (!options.newlineIsToken || !left.includes(`
|
|
58360
|
+
`)) {
|
|
58361
|
+
left = left.trim();
|
|
58362
|
+
}
|
|
58363
|
+
if (!options.newlineIsToken || !right.includes(`
|
|
58364
|
+
`)) {
|
|
58365
|
+
right = right.trim();
|
|
58366
|
+
}
|
|
58367
|
+
} else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
|
|
58368
|
+
if (left.endsWith(`
|
|
58369
|
+
`)) {
|
|
58370
|
+
left = left.slice(0, -1);
|
|
58371
|
+
}
|
|
58372
|
+
if (right.endsWith(`
|
|
58373
|
+
`)) {
|
|
58374
|
+
right = right.slice(0, -1);
|
|
58375
|
+
}
|
|
58376
|
+
}
|
|
58377
|
+
return super.equals(left, right, options);
|
|
58378
|
+
}
|
|
58379
|
+
}
|
|
58380
|
+
var lineDiff = new LineDiff;
|
|
58381
|
+
function diffLines(oldStr, newStr, options) {
|
|
58382
|
+
return lineDiff.diff(oldStr, newStr, options);
|
|
58383
|
+
}
|
|
58384
|
+
function tokenize(value, options) {
|
|
58385
|
+
if (options.stripTrailingCr) {
|
|
58386
|
+
value = value.replace(/\r\n/g, `
|
|
57756
58387
|
`);
|
|
57757
|
-
|
|
57758
|
-
|
|
57759
|
-
|
|
57760
|
-
|
|
57761
|
-
|
|
57762
|
-
let
|
|
57763
|
-
|
|
57764
|
-
|
|
57765
|
-
|
|
57766
|
-
|
|
57767
|
-
|
|
57768
|
-
|
|
57769
|
-
|
|
57770
|
-
|
|
57771
|
-
|
|
57772
|
-
|
|
57773
|
-
|
|
57774
|
-
|
|
57775
|
-
|
|
57776
|
-
|
|
57777
|
-
|
|
57778
|
-
|
|
57779
|
-
|
|
57780
|
-
|
|
57781
|
-
|
|
57782
|
-
|
|
57783
|
-
|
|
57784
|
-
|
|
57785
|
-
|
|
57786
|
-
|
|
57787
|
-
|
|
57788
|
-
|
|
57789
|
-
|
|
57790
|
-
|
|
57791
|
-
|
|
57792
|
-
|
|
57793
|
-
|
|
57794
|
-
|
|
57795
|
-
|
|
57796
|
-
|
|
57797
|
-
|
|
58388
|
+
}
|
|
58389
|
+
const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
|
|
58390
|
+
if (!linesAndNewlines[linesAndNewlines.length - 1]) {
|
|
58391
|
+
linesAndNewlines.pop();
|
|
58392
|
+
}
|
|
58393
|
+
for (let i2 = 0;i2 < linesAndNewlines.length; i2++) {
|
|
58394
|
+
const line = linesAndNewlines[i2];
|
|
58395
|
+
if (i2 % 2 && !options.newlineIsToken) {
|
|
58396
|
+
retLines[retLines.length - 1] += line;
|
|
58397
|
+
} else {
|
|
58398
|
+
retLines.push(line);
|
|
58399
|
+
}
|
|
58400
|
+
}
|
|
58401
|
+
return retLines;
|
|
58402
|
+
}
|
|
58403
|
+
|
|
58404
|
+
// node_modules/diff/libesm/patch/create.js
|
|
58405
|
+
var INCLUDE_HEADERS = {
|
|
58406
|
+
includeIndex: true,
|
|
58407
|
+
includeUnderline: true,
|
|
58408
|
+
includeFileHeaders: true
|
|
58409
|
+
};
|
|
58410
|
+
function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
58411
|
+
let optionsObj;
|
|
58412
|
+
if (!options) {
|
|
58413
|
+
optionsObj = {};
|
|
58414
|
+
} else if (typeof options === "function") {
|
|
58415
|
+
optionsObj = { callback: options };
|
|
58416
|
+
} else {
|
|
58417
|
+
optionsObj = options;
|
|
58418
|
+
}
|
|
58419
|
+
if (typeof optionsObj.context === "undefined") {
|
|
58420
|
+
optionsObj.context = 4;
|
|
58421
|
+
}
|
|
58422
|
+
const context = optionsObj.context;
|
|
58423
|
+
if (optionsObj.newlineIsToken) {
|
|
58424
|
+
throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
|
|
58425
|
+
}
|
|
58426
|
+
if (!optionsObj.callback) {
|
|
58427
|
+
return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
|
|
58428
|
+
} else {
|
|
58429
|
+
const { callback } = optionsObj;
|
|
58430
|
+
diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
|
|
58431
|
+
const patch = diffLinesResultToPatch(diff);
|
|
58432
|
+
callback(patch);
|
|
58433
|
+
} }));
|
|
58434
|
+
}
|
|
58435
|
+
function diffLinesResultToPatch(diff) {
|
|
58436
|
+
if (!diff) {
|
|
58437
|
+
return;
|
|
58438
|
+
}
|
|
58439
|
+
diff.push({ value: "", lines: [] });
|
|
58440
|
+
function contextLines(lines) {
|
|
58441
|
+
return lines.map(function(entry) {
|
|
58442
|
+
return " " + entry;
|
|
58443
|
+
});
|
|
58444
|
+
}
|
|
58445
|
+
const hunks = [];
|
|
58446
|
+
let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
|
|
58447
|
+
for (let i2 = 0;i2 < diff.length; i2++) {
|
|
58448
|
+
const current = diff[i2], lines = current.lines || splitLines(current.value);
|
|
58449
|
+
current.lines = lines;
|
|
58450
|
+
if (current.added || current.removed) {
|
|
58451
|
+
if (!oldRangeStart) {
|
|
58452
|
+
const prev = diff[i2 - 1];
|
|
58453
|
+
oldRangeStart = oldLine;
|
|
58454
|
+
newRangeStart = newLine;
|
|
58455
|
+
if (prev) {
|
|
58456
|
+
curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
|
|
58457
|
+
oldRangeStart -= curRange.length;
|
|
58458
|
+
newRangeStart -= curRange.length;
|
|
58459
|
+
}
|
|
58460
|
+
}
|
|
58461
|
+
for (const line of lines) {
|
|
58462
|
+
curRange.push((current.added ? "+" : "-") + line);
|
|
58463
|
+
}
|
|
58464
|
+
if (current.added) {
|
|
58465
|
+
newLine += lines.length;
|
|
58466
|
+
} else {
|
|
58467
|
+
oldLine += lines.length;
|
|
58468
|
+
}
|
|
58469
|
+
} else {
|
|
58470
|
+
if (oldRangeStart) {
|
|
58471
|
+
if (lines.length <= context * 2 && i2 < diff.length - 2) {
|
|
58472
|
+
for (const line of contextLines(lines)) {
|
|
58473
|
+
curRange.push(line);
|
|
58474
|
+
}
|
|
58475
|
+
} else {
|
|
58476
|
+
const contextSize = Math.min(lines.length, context);
|
|
58477
|
+
for (const line of contextLines(lines.slice(0, contextSize))) {
|
|
58478
|
+
curRange.push(line);
|
|
58479
|
+
}
|
|
58480
|
+
const hunk = {
|
|
58481
|
+
oldStart: oldRangeStart,
|
|
58482
|
+
oldLines: oldLine - oldRangeStart + contextSize,
|
|
58483
|
+
newStart: newRangeStart,
|
|
58484
|
+
newLines: newLine - newRangeStart + contextSize,
|
|
58485
|
+
lines: curRange
|
|
58486
|
+
};
|
|
58487
|
+
hunks.push(hunk);
|
|
58488
|
+
oldRangeStart = 0;
|
|
58489
|
+
newRangeStart = 0;
|
|
58490
|
+
curRange = [];
|
|
58491
|
+
}
|
|
58492
|
+
}
|
|
58493
|
+
oldLine += lines.length;
|
|
58494
|
+
newLine += lines.length;
|
|
58495
|
+
}
|
|
58496
|
+
}
|
|
58497
|
+
for (const hunk of hunks) {
|
|
58498
|
+
for (let i2 = 0;i2 < hunk.lines.length; i2++) {
|
|
58499
|
+
if (hunk.lines[i2].endsWith(`
|
|
58500
|
+
`)) {
|
|
58501
|
+
hunk.lines[i2] = hunk.lines[i2].slice(0, -1);
|
|
58502
|
+
} else {
|
|
58503
|
+
hunk.lines.splice(i2 + 1, 0, "\");
|
|
58504
|
+
i2++;
|
|
58505
|
+
}
|
|
57798
58506
|
}
|
|
57799
58507
|
}
|
|
58508
|
+
return {
|
|
58509
|
+
oldFileName,
|
|
58510
|
+
newFileName,
|
|
58511
|
+
oldHeader,
|
|
58512
|
+
newHeader,
|
|
58513
|
+
hunks
|
|
58514
|
+
};
|
|
57800
58515
|
}
|
|
57801
|
-
|
|
57802
|
-
|
|
57803
|
-
|
|
57804
|
-
|
|
58516
|
+
}
|
|
58517
|
+
function formatPatch(patch, headerOptions) {
|
|
58518
|
+
if (!headerOptions) {
|
|
58519
|
+
headerOptions = INCLUDE_HEADERS;
|
|
58520
|
+
}
|
|
58521
|
+
if (Array.isArray(patch)) {
|
|
58522
|
+
if (patch.length > 1 && !headerOptions.includeFileHeaders) {
|
|
58523
|
+
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?)");
|
|
58524
|
+
}
|
|
58525
|
+
return patch.map((p) => formatPatch(p, headerOptions)).join(`
|
|
58526
|
+
`);
|
|
58527
|
+
}
|
|
58528
|
+
const ret = [];
|
|
58529
|
+
if (headerOptions.includeIndex && patch.oldFileName == patch.newFileName) {
|
|
58530
|
+
ret.push("Index: " + patch.oldFileName);
|
|
58531
|
+
}
|
|
58532
|
+
if (headerOptions.includeUnderline) {
|
|
58533
|
+
ret.push("===================================================================");
|
|
58534
|
+
}
|
|
58535
|
+
if (headerOptions.includeFileHeaders) {
|
|
58536
|
+
ret.push("--- " + patch.oldFileName + (typeof patch.oldHeader === "undefined" ? "" : "\t" + patch.oldHeader));
|
|
58537
|
+
ret.push("+++ " + patch.newFileName + (typeof patch.newHeader === "undefined" ? "" : "\t" + patch.newHeader));
|
|
58538
|
+
}
|
|
58539
|
+
for (let i2 = 0;i2 < patch.hunks.length; i2++) {
|
|
58540
|
+
const hunk = patch.hunks[i2];
|
|
58541
|
+
if (hunk.oldLines === 0) {
|
|
58542
|
+
hunk.oldStart -= 1;
|
|
58543
|
+
}
|
|
58544
|
+
if (hunk.newLines === 0) {
|
|
58545
|
+
hunk.newStart -= 1;
|
|
58546
|
+
}
|
|
58547
|
+
ret.push("@@ -" + hunk.oldStart + "," + hunk.oldLines + " +" + hunk.newStart + "," + hunk.newLines + " @@");
|
|
58548
|
+
for (const line of hunk.lines) {
|
|
58549
|
+
ret.push(line);
|
|
58550
|
+
}
|
|
58551
|
+
}
|
|
58552
|
+
return ret.join(`
|
|
57805
58553
|
`) + `
|
|
57806
58554
|
`;
|
|
58555
|
+
}
|
|
58556
|
+
function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
|
|
58557
|
+
if (typeof options === "function") {
|
|
58558
|
+
options = { callback: options };
|
|
57807
58559
|
}
|
|
57808
|
-
|
|
57809
|
-
|
|
57810
|
-
|
|
58560
|
+
if (!(options === null || options === undefined ? undefined : options.callback)) {
|
|
58561
|
+
const patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
|
|
58562
|
+
if (!patchObj) {
|
|
58563
|
+
return;
|
|
58564
|
+
}
|
|
58565
|
+
return formatPatch(patchObj, options === null || options === undefined ? undefined : options.headerOptions);
|
|
58566
|
+
} else {
|
|
58567
|
+
const { callback } = options;
|
|
58568
|
+
structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: (patchObj) => {
|
|
58569
|
+
if (!patchObj) {
|
|
58570
|
+
callback(undefined);
|
|
58571
|
+
} else {
|
|
58572
|
+
callback(formatPatch(patchObj, options.headerOptions));
|
|
58573
|
+
}
|
|
58574
|
+
} }));
|
|
58575
|
+
}
|
|
58576
|
+
}
|
|
58577
|
+
function splitLines(text) {
|
|
58578
|
+
const hasTrailingNl = text.endsWith(`
|
|
58579
|
+
`);
|
|
58580
|
+
const result = text.split(`
|
|
58581
|
+
`).map((line) => line + `
|
|
58582
|
+
`);
|
|
58583
|
+
if (hasTrailingNl) {
|
|
58584
|
+
result.pop();
|
|
58585
|
+
} else {
|
|
58586
|
+
result.push(result.pop().slice(0, -1));
|
|
58587
|
+
}
|
|
58588
|
+
return result;
|
|
58589
|
+
}
|
|
58590
|
+
// src/tools/hashline-edit/diff-utils.ts
|
|
58591
|
+
function generateUnifiedDiff(oldContent, newContent, filePath) {
|
|
58592
|
+
return createTwoFilesPatch(filePath, filePath, oldContent, newContent, undefined, undefined, { context: 3 });
|
|
57811
58593
|
}
|
|
57812
58594
|
function countLineDiffs(oldContent, newContent) {
|
|
57813
58595
|
const oldLines = oldContent.split(`
|
|
@@ -57979,7 +58761,7 @@ function resolveToolCallID2(ctx) {
|
|
|
57979
58761
|
function canCreateFromMissingFile(edits) {
|
|
57980
58762
|
if (edits.length === 0)
|
|
57981
58763
|
return false;
|
|
57982
|
-
return edits.every((edit) => edit.op === "append" || edit.op === "prepend");
|
|
58764
|
+
return edits.every((edit) => (edit.op === "append" || edit.op === "prepend") && !edit.pos);
|
|
57983
58765
|
}
|
|
57984
58766
|
function buildSuccessMeta(effectivePath, beforeContent, afterContent, noopEdits, deduplicatedEdits) {
|
|
57985
58767
|
const unifiedDiff = generateUnifiedDiff(beforeContent, afterContent, effectivePath);
|
|
@@ -58023,16 +58805,16 @@ async function executeHashlineEditTool(args, context) {
|
|
|
58023
58805
|
const metadataContext = context;
|
|
58024
58806
|
const filePath = args.filePath;
|
|
58025
58807
|
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
58808
|
if (deleteMode && rename) {
|
|
58031
58809
|
return "Error: delete and rename cannot be used together";
|
|
58032
58810
|
}
|
|
58033
|
-
if (deleteMode && edits.length > 0) {
|
|
58811
|
+
if (deleteMode && args.edits.length > 0) {
|
|
58034
58812
|
return "Error: delete mode requires edits to be an empty array";
|
|
58035
58813
|
}
|
|
58814
|
+
if (!deleteMode && (!args.edits || !Array.isArray(args.edits) || args.edits.length === 0)) {
|
|
58815
|
+
return "Error: edits parameter must be a non-empty array";
|
|
58816
|
+
}
|
|
58817
|
+
const edits = deleteMode ? [] : normalizeHashlineEdits(args.edits);
|
|
58036
58818
|
const file2 = Bun.file(filePath);
|
|
58037
58819
|
const exists = await file2.exists();
|
|
58038
58820
|
if (!exists && !deleteMode && !canCreateFromMissingFile(edits)) {
|
|
@@ -58097,7 +58879,7 @@ WORKFLOW:
|
|
|
58097
58879
|
VALIDATION:
|
|
58098
58880
|
Payload shape: { "filePath": string, "edits": [...], "delete"?: boolean, "rename"?: string }
|
|
58099
58881
|
Each edit must be one of: replace, append, prepend
|
|
58100
|
-
Edit shape: { "op": "replace"|"append"|"prepend", "pos"?: "LINE#ID", "end"?: "LINE#ID", "lines"
|
|
58882
|
+
Edit shape: { "op": "replace"|"append"|"prepend", "pos"?: "LINE#ID", "end"?: "LINE#ID", "lines": string|string[]|null }
|
|
58101
58883
|
lines must contain plain replacement text only (no LINE#ID prefixes, no diff + markers)
|
|
58102
58884
|
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
58885
|
|
|
@@ -58171,7 +58953,7 @@ function createHashlineEditTool() {
|
|
|
58171
58953
|
]).describe("Hashline edit operation mode"),
|
|
58172
58954
|
pos: tool.schema.string().optional().describe("Primary anchor in LINE#ID format"),
|
|
58173
58955
|
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()]).
|
|
58956
|
+
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
58957
|
})).describe("Array of edit operations to apply (empty when delete=true)")
|
|
58176
58958
|
},
|
|
58177
58959
|
execute: async (args, context) => executeHashlineEditTool(args, context)
|
|
@@ -58281,7 +59063,9 @@ function createSessionHooks(args) {
|
|
|
58281
59063
|
const prometheusMdOnly = isHookEnabled("prometheus-md-only") ? safeHook("prometheus-md-only", () => createPrometheusMdOnlyHook(ctx)) : null;
|
|
58282
59064
|
const sisyphusJuniorNotepad = isHookEnabled("sisyphus-junior-notepad") ? safeHook("sisyphus-junior-notepad", () => createSisyphusJuniorNotepadHook(ctx)) : null;
|
|
58283
59065
|
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
|
|
59066
|
+
const noHephaestusNonGpt = isHookEnabled("no-hephaestus-non-gpt") ? safeHook("no-hephaestus-non-gpt", () => createNoHephaestusNonGptHook(ctx, {
|
|
59067
|
+
allowNonGptModel: pluginConfig.agents?.hephaestus?.allow_non_gpt_model
|
|
59068
|
+
})) : null;
|
|
58285
59069
|
const questionLabelTruncator = isHookEnabled("question-label-truncator") ? safeHook("question-label-truncator", () => createQuestionLabelTruncatorHook()) : null;
|
|
58286
59070
|
const taskResumeInfo = isHookEnabled("task-resume-info") ? safeHook("task-resume-info", () => createTaskResumeInfoHook()) : null;
|
|
58287
59071
|
const anthropicEffort = isHookEnabled("anthropic-effort") ? safeHook("anthropic-effort", () => createAnthropicEffortHook()) : null;
|
|
@@ -58591,7 +59375,9 @@ function createContinuationHooks(args) {
|
|
|
58591
59375
|
sessionRecovery
|
|
58592
59376
|
} = args;
|
|
58593
59377
|
const safeHook = (hookName, factory) => safeCreateHook(hookName, factory, { enabled: safeHookEnabled });
|
|
58594
|
-
const stopContinuationGuard = isHookEnabled("stop-continuation-guard") ? safeHook("stop-continuation-guard", () => createStopContinuationGuardHook(ctx
|
|
59378
|
+
const stopContinuationGuard = isHookEnabled("stop-continuation-guard") ? safeHook("stop-continuation-guard", () => createStopContinuationGuardHook(ctx, {
|
|
59379
|
+
backgroundManager
|
|
59380
|
+
})) : null;
|
|
58595
59381
|
const compactionContextInjector = isHookEnabled("compaction-context-injector") ? safeHook("compaction-context-injector", () => createCompactionContextInjector(backgroundManager)) : null;
|
|
58596
59382
|
const compactionTodoPreserver = isHookEnabled("compaction-todo-preserver") ? safeHook("compaction-todo-preserver", () => createCompactionTodoPreserverHook(ctx)) : null;
|
|
58597
59383
|
const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer") ? safeHook("todo-continuation-enforcer", () => createTodoContinuationEnforcer(ctx, {
|
|
@@ -59307,6 +60093,7 @@ async function checkAndInterruptStaleTasks(args) {
|
|
|
59307
60093
|
class BackgroundManager {
|
|
59308
60094
|
tasks;
|
|
59309
60095
|
notifications;
|
|
60096
|
+
pendingNotifications;
|
|
59310
60097
|
pendingByParent;
|
|
59311
60098
|
client;
|
|
59312
60099
|
directory;
|
|
@@ -59328,6 +60115,7 @@ class BackgroundManager {
|
|
|
59328
60115
|
constructor(ctx, config3, options) {
|
|
59329
60116
|
this.tasks = new Map;
|
|
59330
60117
|
this.notifications = new Map;
|
|
60118
|
+
this.pendingNotifications = new Map;
|
|
59331
60119
|
this.pendingByParent = new Map;
|
|
59332
60120
|
this.client = ctx.client;
|
|
59333
60121
|
this.directory = ctx.directory;
|
|
@@ -59893,6 +60681,7 @@ class BackgroundManager {
|
|
|
59893
60681
|
for (const descendant of this.getAllDescendantTasks(sessionID)) {
|
|
59894
60682
|
tasksToCancel.set(descendant.id, descendant);
|
|
59895
60683
|
}
|
|
60684
|
+
this.pendingNotifications.delete(sessionID);
|
|
59896
60685
|
if (tasksToCancel.size === 0)
|
|
59897
60686
|
return;
|
|
59898
60687
|
for (const task of tasksToCancel.values()) {
|
|
@@ -59926,6 +60715,11 @@ class BackgroundManager {
|
|
|
59926
60715
|
subagentSessions.delete(task.sessionID);
|
|
59927
60716
|
}
|
|
59928
60717
|
}
|
|
60718
|
+
for (const task of tasksToCancel.values()) {
|
|
60719
|
+
if (task.parentSessionID) {
|
|
60720
|
+
this.pendingNotifications.delete(task.parentSessionID);
|
|
60721
|
+
}
|
|
60722
|
+
}
|
|
59929
60723
|
SessionCategoryRegistry.remove(sessionID);
|
|
59930
60724
|
}
|
|
59931
60725
|
if (event.type === "session.status") {
|
|
@@ -59969,6 +60763,34 @@ class BackgroundManager {
|
|
|
59969
60763
|
clearNotifications(sessionID) {
|
|
59970
60764
|
this.notifications.delete(sessionID);
|
|
59971
60765
|
}
|
|
60766
|
+
queuePendingNotification(sessionID, notification2) {
|
|
60767
|
+
if (!sessionID)
|
|
60768
|
+
return;
|
|
60769
|
+
const existingNotifications = this.pendingNotifications.get(sessionID) ?? [];
|
|
60770
|
+
existingNotifications.push(notification2);
|
|
60771
|
+
this.pendingNotifications.set(sessionID, existingNotifications);
|
|
60772
|
+
}
|
|
60773
|
+
injectPendingNotificationsIntoChatMessage(output, sessionID) {
|
|
60774
|
+
const pendingNotifications = this.pendingNotifications.get(sessionID);
|
|
60775
|
+
if (!pendingNotifications || pendingNotifications.length === 0) {
|
|
60776
|
+
return;
|
|
60777
|
+
}
|
|
60778
|
+
this.pendingNotifications.delete(sessionID);
|
|
60779
|
+
const notificationContent = pendingNotifications.join(`
|
|
60780
|
+
|
|
60781
|
+
`);
|
|
60782
|
+
const firstTextPartIndex = output.parts.findIndex((part) => part.type === "text");
|
|
60783
|
+
if (firstTextPartIndex === -1) {
|
|
60784
|
+
output.parts.unshift(createInternalAgentTextPart(notificationContent));
|
|
60785
|
+
return;
|
|
60786
|
+
}
|
|
60787
|
+
const originalText = output.parts[firstTextPartIndex].text ?? "";
|
|
60788
|
+
output.parts[firstTextPartIndex].text = `${notificationContent}
|
|
60789
|
+
|
|
60790
|
+
---
|
|
60791
|
+
|
|
60792
|
+
${originalText}`;
|
|
60793
|
+
}
|
|
59972
60794
|
async validateSessionHasOutput(sessionID) {
|
|
59973
60795
|
try {
|
|
59974
60796
|
const response = await this.client.session.messages({
|
|
@@ -60264,6 +61086,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
60264
61086
|
taskId: task.id,
|
|
60265
61087
|
parentSessionID: task.parentSessionID
|
|
60266
61088
|
});
|
|
61089
|
+
this.queuePendingNotification(task.parentSessionID, notification2);
|
|
60267
61090
|
} else {
|
|
60268
61091
|
log("[background-agent] Failed to send notification:", error45);
|
|
60269
61092
|
}
|
|
@@ -60452,6 +61275,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
60452
61275
|
this.concurrencyManager.clear();
|
|
60453
61276
|
this.tasks.clear();
|
|
60454
61277
|
this.notifications.clear();
|
|
61278
|
+
this.pendingNotifications.clear();
|
|
60455
61279
|
this.pendingByParent.clear();
|
|
60456
61280
|
this.notificationQueueByParent.clear();
|
|
60457
61281
|
this.queuesByKey.clear();
|
|
@@ -63834,7 +64658,7 @@ function createParser(callbacks) {
|
|
|
63834
64658
|
const { onEvent = noop, onError = noop, onRetry = noop, onComment } = callbacks;
|
|
63835
64659
|
let incompleteLine = "", isFirstChunk = true, id, data = "", eventType = "";
|
|
63836
64660
|
function feed(newChunk) {
|
|
63837
|
-
const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] =
|
|
64661
|
+
const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] = splitLines2(`${incompleteLine}${chunk}`);
|
|
63838
64662
|
for (const line of complete)
|
|
63839
64663
|
parseLine(line);
|
|
63840
64664
|
incompleteLine = incomplete, isFirstChunk = false;
|
|
@@ -63893,7 +64717,7 @@ function createParser(callbacks) {
|
|
|
63893
64717
|
}
|
|
63894
64718
|
return { feed, reset };
|
|
63895
64719
|
}
|
|
63896
|
-
function
|
|
64720
|
+
function splitLines2(chunk) {
|
|
63897
64721
|
const lines = [];
|
|
63898
64722
|
let incompleteLine = "", searchIndex = 0;
|
|
63899
64723
|
for (;searchIndex < chunk.length; ) {
|
|
@@ -66834,7 +67658,7 @@ For implementation tasks, actively decompose and delegate to \`deep\` category a
|
|
|
66834
67658
|
}
|
|
66835
67659
|
|
|
66836
67660
|
// src/agents/sisyphus.ts
|
|
66837
|
-
var MODE = "
|
|
67661
|
+
var MODE = "all";
|
|
66838
67662
|
function buildTaskManagementSection(useTaskSystem) {
|
|
66839
67663
|
if (useTaskSystem) {
|
|
66840
67664
|
return `<Task_Management>
|
|
@@ -69544,7 +70368,7 @@ ${agentRows.join(`
|
|
|
69544
70368
|
}
|
|
69545
70369
|
|
|
69546
70370
|
// src/agents/atlas/agent.ts
|
|
69547
|
-
var MODE7 = "
|
|
70371
|
+
var MODE7 = "all";
|
|
69548
70372
|
function getAtlasPromptSource(model) {
|
|
69549
70373
|
if (model && isGptModel(model)) {
|
|
69550
70374
|
return "gpt";
|
|
@@ -69585,18 +70409,13 @@ function buildDynamicOrchestratorPrompt(ctx) {
|
|
|
69585
70409
|
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
70410
|
}
|
|
69587
70411
|
function createAtlasAgent(ctx) {
|
|
69588
|
-
const restrictions = createAgentToolRestrictions([
|
|
69589
|
-
"task",
|
|
69590
|
-
"call_omo_agent"
|
|
69591
|
-
]);
|
|
69592
70412
|
const baseConfig = {
|
|
69593
70413
|
description: "Orchestrates work via task() to complete ALL tasks in a todo list until fully done. (Atlas - OhMyOpenCode)",
|
|
69594
70414
|
mode: MODE7,
|
|
69595
70415
|
...ctx.model ? { model: ctx.model } : {},
|
|
69596
70416
|
temperature: 0.1,
|
|
69597
70417
|
prompt: buildDynamicOrchestratorPrompt(ctx),
|
|
69598
|
-
color: "#10B981"
|
|
69599
|
-
...restrictions
|
|
70418
|
+
color: "#10B981"
|
|
69600
70419
|
};
|
|
69601
70420
|
return baseConfig;
|
|
69602
70421
|
}
|
|
@@ -69847,7 +70666,7 @@ var momusPromptMetadata = {
|
|
|
69847
70666
|
};
|
|
69848
70667
|
|
|
69849
70668
|
// src/agents/hephaestus.ts
|
|
69850
|
-
var MODE9 = "
|
|
70669
|
+
var MODE9 = "all";
|
|
69851
70670
|
function buildTodoDisciplineSection(useTaskSystem) {
|
|
69852
70671
|
if (useTaskSystem) {
|
|
69853
70672
|
return `## Task Discipline (NON-NEGOTIABLE)
|
|
@@ -70452,25 +71271,10 @@ function applyOverrides(config3, override, mergedCategories, directory) {
|
|
|
70452
71271
|
|
|
70453
71272
|
// src/agents/env-context.ts
|
|
70454
71273
|
function createEnvContext() {
|
|
70455
|
-
const now = new Date;
|
|
70456
71274
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
70457
71275
|
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
71276
|
return `
|
|
70471
71277
|
<omo-env>
|
|
70472
|
-
Current date: ${dateStr}
|
|
70473
|
-
Current time: ${timeStr}
|
|
70474
71278
|
Timezone: ${timezone}
|
|
70475
71279
|
Locale: ${locale}
|
|
70476
71280
|
</omo-env>`;
|
|
@@ -71412,6 +72216,7 @@ function remapAgentKeysToDisplayNames(agents) {
|
|
|
71412
72216
|
const displayName = AGENT_DISPLAY_NAMES[key];
|
|
71413
72217
|
if (displayName && displayName !== key) {
|
|
71414
72218
|
result[displayName] = value;
|
|
72219
|
+
result[key] = value;
|
|
71415
72220
|
} else {
|
|
71416
72221
|
result[key] = value;
|
|
71417
72222
|
}
|
|
@@ -73701,6 +74506,8 @@ async function applyAgentConfig(params) {
|
|
|
73701
74506
|
key,
|
|
73702
74507
|
value ? migrateAgentConfig(value) : value
|
|
73703
74508
|
]));
|
|
74509
|
+
const disabledAgentNames = new Set((migratedDisabledAgents ?? []).map((a) => a.toLowerCase()));
|
|
74510
|
+
const filterDisabledAgents = (agents) => Object.fromEntries(Object.entries(agents).filter(([name]) => !disabledAgentNames.has(name.toLowerCase())));
|
|
73704
74511
|
const isSisyphusEnabled = params.pluginConfig.sisyphus_agent?.disabled !== true;
|
|
73705
74512
|
const builderEnabled = params.pluginConfig.sisyphus_agent?.default_builder_enabled ?? false;
|
|
73706
74513
|
const plannerEnabled = params.pluginConfig.sisyphus_agent?.planner_enabled ?? true;
|
|
@@ -73754,9 +74561,9 @@ async function applyAgentConfig(params) {
|
|
|
73754
74561
|
params.config.agent = {
|
|
73755
74562
|
...agentConfig,
|
|
73756
74563
|
...Object.fromEntries(Object.entries(builtinAgents).filter(([key]) => key !== "sisyphus")),
|
|
73757
|
-
...userAgents,
|
|
73758
|
-
...projectAgents,
|
|
73759
|
-
...pluginAgents,
|
|
74564
|
+
...filterDisabledAgents(userAgents),
|
|
74565
|
+
...filterDisabledAgents(projectAgents),
|
|
74566
|
+
...filterDisabledAgents(pluginAgents),
|
|
73760
74567
|
...filteredConfigAgents,
|
|
73761
74568
|
build: { ...migratedBuild, mode: "subagent", hidden: true },
|
|
73762
74569
|
...planDemoteConfig ? { plan: planDemoteConfig } : {}
|
|
@@ -73764,9 +74571,9 @@ async function applyAgentConfig(params) {
|
|
|
73764
74571
|
} else {
|
|
73765
74572
|
params.config.agent = {
|
|
73766
74573
|
...builtinAgents,
|
|
73767
|
-
...userAgents,
|
|
73768
|
-
...projectAgents,
|
|
73769
|
-
...pluginAgents,
|
|
74574
|
+
...filterDisabledAgents(userAgents),
|
|
74575
|
+
...filterDisabledAgents(projectAgents),
|
|
74576
|
+
...filterDisabledAgents(pluginAgents),
|
|
73770
74577
|
...configAgent
|
|
73771
74578
|
};
|
|
73772
74579
|
}
|
|
@@ -74719,6 +75526,7 @@ function applyToolConfig(params) {
|
|
|
74719
75526
|
function createConfigHandler(deps) {
|
|
74720
75527
|
const { ctx, pluginConfig, modelCacheState } = deps;
|
|
74721
75528
|
return async (config3) => {
|
|
75529
|
+
const formatterConfig = config3.formatter;
|
|
74722
75530
|
applyProviderConfig({ config: config3, modelCacheState });
|
|
74723
75531
|
const pluginComponents = await loadPluginComponents({ pluginConfig });
|
|
74724
75532
|
const agentResult = await applyAgentConfig({
|
|
@@ -74730,6 +75538,7 @@ function createConfigHandler(deps) {
|
|
|
74730
75538
|
applyToolConfig({ config: config3, pluginConfig, agentResult });
|
|
74731
75539
|
await applyMcpConfig({ config: config3, pluginConfig, pluginComponents });
|
|
74732
75540
|
await applyCommandConfig({ config: config3, pluginConfig, ctx, pluginComponents });
|
|
75541
|
+
config3.formatter = formatterConfig;
|
|
74733
75542
|
log("[config-handler] config handler applied", {
|
|
74734
75543
|
agentCount: Object.keys(agentResult).length,
|
|
74735
75544
|
commandCount: Object.keys(config3.command ?? {}).length
|
|
@@ -75313,11 +76122,11 @@ function applyUltraworkModelOverrideOnMessage(pluginConfig, inputAgentName, outp
|
|
|
75313
76122
|
const override = resolveUltraworkOverride(pluginConfig, inputAgentName, output, sessionID);
|
|
75314
76123
|
if (!override)
|
|
75315
76124
|
return;
|
|
76125
|
+
if (override.variant) {
|
|
76126
|
+
output.message["variant"] = override.variant;
|
|
76127
|
+
output.message["thinking"] = override.variant;
|
|
76128
|
+
}
|
|
75316
76129
|
if (!override.providerID || !override.modelID) {
|
|
75317
|
-
if (override.variant) {
|
|
75318
|
-
output.message["variant"] = override.variant;
|
|
75319
|
-
output.message["thinking"] = override.variant;
|
|
75320
|
-
}
|
|
75321
76130
|
return;
|
|
75322
76131
|
}
|
|
75323
76132
|
const targetModel = { providerID: override.providerID, modelID: override.modelID };
|
|
@@ -75329,10 +76138,6 @@ function applyUltraworkModelOverrideOnMessage(pluginConfig, inputAgentName, outp
|
|
|
75329
76138
|
if (!messageId) {
|
|
75330
76139
|
log("[ultrawork-model-override] No message ID found, falling back to direct mutation");
|
|
75331
76140
|
output.message.model = targetModel;
|
|
75332
|
-
if (override.variant) {
|
|
75333
|
-
output.message["variant"] = override.variant;
|
|
75334
|
-
output.message["thinking"] = override.variant;
|
|
75335
|
-
}
|
|
75336
76141
|
return;
|
|
75337
76142
|
}
|
|
75338
76143
|
const fromModel = output.message.model?.modelID ?? "unknown";
|
|
@@ -75404,8 +76209,10 @@ function createChatMessageHandler3(args) {
|
|
|
75404
76209
|
setSessionModel(input.sessionID, input.model);
|
|
75405
76210
|
}
|
|
75406
76211
|
await hooks2.stopContinuationGuard?.["chat.message"]?.(input);
|
|
76212
|
+
await hooks2.backgroundNotificationHook?.["chat.message"]?.(input, output);
|
|
75407
76213
|
await hooks2.runtimeFallback?.["chat.message"]?.(input, output);
|
|
75408
76214
|
await hooks2.keywordDetector?.["chat.message"]?.(input, output);
|
|
76215
|
+
await hooks2.thinkMode?.["chat.message"]?.(input, output);
|
|
75409
76216
|
await hooks2.claudeCodeHooks?.["chat.message"]?.(input, output);
|
|
75410
76217
|
await hooks2.autoSlashCommand?.["chat.message"]?.(input, output);
|
|
75411
76218
|
await hooks2.noSisyphusGpt?.["chat.message"]?.(input, output);
|