oh-my-opencode 3.3.2 → 3.4.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/dist/agents/atlas/default.d.ts +1 -1
- package/dist/agents/atlas/gpt.d.ts +1 -1
- package/dist/agents/dynamic-agent-prompt-builder.d.ts +2 -2
- package/dist/agents/prometheus/high-accuracy-mode.d.ts +1 -1
- package/dist/agents/prometheus/index.d.ts +1 -1
- package/dist/agents/prometheus/interview-mode.d.ts +1 -1
- package/dist/agents/prometheus/plan-generation.d.ts +1 -1
- package/dist/agents/utils.d.ts +1 -1
- package/dist/cli/index.js +51 -34
- package/dist/features/background-agent/manager.d.ts +2 -0
- package/dist/features/background-agent/spawner/background-session-creator.d.ts +10 -0
- package/dist/features/background-agent/spawner/concurrency-key-from-launch-input.d.ts +2 -0
- package/dist/features/background-agent/spawner/parent-directory-resolver.d.ts +6 -0
- package/dist/features/background-agent/spawner/tmux-callback-invoker.d.ts +8 -0
- package/dist/features/builtin-commands/templates/handoff.d.ts +1 -0
- package/dist/features/builtin-commands/types.d.ts +1 -1
- package/dist/features/claude-tasks/index.d.ts +1 -0
- package/dist/features/claude-tasks/session-storage.d.ts +9 -0
- package/dist/features/tmux-subagent/manager-cleanup.d.ts +12 -0
- package/dist/features/tmux-subagent/manager.d.ts +2 -4
- package/dist/features/tmux-subagent/polling-manager.d.ts +12 -0
- package/dist/features/tmux-subagent/session-cleaner.d.ts +23 -0
- package/dist/features/tmux-subagent/session-spawner.d.ts +34 -0
- package/dist/hooks/interactive-bash-session/hook.d.ts +23 -0
- package/dist/hooks/interactive-bash-session/index.d.ts +4 -23
- package/dist/hooks/interactive-bash-session/parser.d.ts +26 -0
- package/dist/hooks/interactive-bash-session/state-manager.d.ts +4 -0
- package/dist/hooks/keyword-detector/ultrawork/default.d.ts +1 -1
- package/dist/hooks/keyword-detector/ultrawork/gpt5.2.d.ts +1 -1
- package/dist/hooks/keyword-detector/ultrawork/planner.d.ts +1 -1
- package/dist/hooks/session-recovery/index.d.ts +1 -1
- package/dist/index.js +1200 -690
- package/dist/plugin-handlers/plan-model-inheritance.d.ts +1 -0
- package/dist/shared/git-worktree/collect-git-diff-stats.d.ts +2 -0
- package/dist/shared/git-worktree/format-file-changes.d.ts +2 -0
- package/dist/shared/git-worktree/index.d.ts +5 -0
- package/dist/shared/git-worktree/parse-diff-numstat.d.ts +2 -0
- package/dist/shared/git-worktree/parse-status-porcelain.d.ts +2 -0
- package/dist/shared/git-worktree/types.d.ts +7 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/model-resolution-pipeline.d.ts +1 -0
- package/dist/tools/background-task/index.d.ts +1 -2
- package/dist/tools/background-task/modules/background-cancel.d.ts +4 -0
- package/dist/tools/background-task/modules/background-output.d.ts +3 -0
- package/dist/tools/background-task/modules/background-task.d.ts +3 -0
- package/dist/tools/background-task/modules/formatters.d.ts +11 -0
- package/dist/tools/background-task/modules/message-processing.d.ts +59 -0
- package/dist/tools/background-task/modules/utils.d.ts +15 -0
- package/dist/tools/background-task/tools.d.ts +7 -47
- package/dist/tools/background-task/types.d.ts +70 -0
- package/dist/tools/call-omo-agent/background-executor.d.ts +12 -0
- package/dist/tools/call-omo-agent/completion-poller.d.ts +11 -0
- package/dist/tools/call-omo-agent/message-dir.d.ts +1 -0
- package/dist/tools/call-omo-agent/message-processor.d.ts +2 -0
- package/dist/tools/call-omo-agent/session-creator.d.ts +15 -0
- package/dist/tools/call-omo-agent/sync-executor.d.ts +12 -0
- package/dist/tools/call-omo-agent/types.d.ts +10 -0
- package/dist/tools/delegate-task/constants.d.ts +11 -4
- package/dist/tools/delegate-task/executor.d.ts +5 -1
- package/dist/tools/delegate-task/skill-resolver.d.ts +9 -0
- package/dist/tools/delegate-task/types.d.ts +15 -1
- package/dist/tools/lsp/client.d.ts +1 -0
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -4522,29 +4522,30 @@ var init_agent_category = () => {};
|
|
|
4522
4522
|
// src/shared/migration/config-migration.ts
|
|
4523
4523
|
import * as fs5 from "fs";
|
|
4524
4524
|
function migrateConfigFile(configPath, rawConfig) {
|
|
4525
|
+
const copy = structuredClone(rawConfig);
|
|
4525
4526
|
let needsWrite = false;
|
|
4526
|
-
const existingMigrations = Array.isArray(
|
|
4527
|
+
const existingMigrations = Array.isArray(copy._migrations) ? new Set(copy._migrations) : new Set;
|
|
4527
4528
|
const allNewMigrations = [];
|
|
4528
|
-
if (
|
|
4529
|
-
const { migrated, changed } = migrateAgentNames(
|
|
4529
|
+
if (copy.agents && typeof copy.agents === "object") {
|
|
4530
|
+
const { migrated, changed } = migrateAgentNames(copy.agents);
|
|
4530
4531
|
if (changed) {
|
|
4531
|
-
|
|
4532
|
+
copy.agents = migrated;
|
|
4532
4533
|
needsWrite = true;
|
|
4533
4534
|
}
|
|
4534
4535
|
}
|
|
4535
|
-
if (
|
|
4536
|
-
const { migrated, changed, newMigrations } = migrateModelVersions(
|
|
4536
|
+
if (copy.agents && typeof copy.agents === "object") {
|
|
4537
|
+
const { migrated, changed, newMigrations } = migrateModelVersions(copy.agents, existingMigrations);
|
|
4537
4538
|
if (changed) {
|
|
4538
|
-
|
|
4539
|
+
copy.agents = migrated;
|
|
4539
4540
|
needsWrite = true;
|
|
4540
4541
|
log("Migrated model versions in agents config");
|
|
4541
4542
|
}
|
|
4542
4543
|
allNewMigrations.push(...newMigrations);
|
|
4543
4544
|
}
|
|
4544
|
-
if (
|
|
4545
|
-
const { migrated, changed, newMigrations } = migrateModelVersions(
|
|
4545
|
+
if (copy.categories && typeof copy.categories === "object") {
|
|
4546
|
+
const { migrated, changed, newMigrations } = migrateModelVersions(copy.categories, existingMigrations);
|
|
4546
4547
|
if (changed) {
|
|
4547
|
-
|
|
4548
|
+
copy.categories = migrated;
|
|
4548
4549
|
needsWrite = true;
|
|
4549
4550
|
log("Migrated model versions in categories config");
|
|
4550
4551
|
}
|
|
@@ -4553,18 +4554,18 @@ function migrateConfigFile(configPath, rawConfig) {
|
|
|
4553
4554
|
if (allNewMigrations.length > 0) {
|
|
4554
4555
|
const updatedMigrations = Array.from(existingMigrations);
|
|
4555
4556
|
updatedMigrations.push(...allNewMigrations);
|
|
4556
|
-
|
|
4557
|
+
copy._migrations = updatedMigrations;
|
|
4557
4558
|
needsWrite = true;
|
|
4558
4559
|
}
|
|
4559
|
-
if (
|
|
4560
|
-
|
|
4561
|
-
delete
|
|
4560
|
+
if (copy.omo_agent) {
|
|
4561
|
+
copy.sisyphus_agent = copy.omo_agent;
|
|
4562
|
+
delete copy.omo_agent;
|
|
4562
4563
|
needsWrite = true;
|
|
4563
4564
|
}
|
|
4564
|
-
if (
|
|
4565
|
+
if (copy.disabled_agents && Array.isArray(copy.disabled_agents)) {
|
|
4565
4566
|
const migrated = [];
|
|
4566
4567
|
let changed = false;
|
|
4567
|
-
for (const agent of
|
|
4568
|
+
for (const agent of copy.disabled_agents) {
|
|
4568
4569
|
const newAgent = AGENT_NAME_MAP[agent.toLowerCase()] ?? AGENT_NAME_MAP[agent] ?? agent;
|
|
4569
4570
|
if (newAgent !== agent) {
|
|
4570
4571
|
changed = true;
|
|
@@ -4572,14 +4573,14 @@ function migrateConfigFile(configPath, rawConfig) {
|
|
|
4572
4573
|
migrated.push(newAgent);
|
|
4573
4574
|
}
|
|
4574
4575
|
if (changed) {
|
|
4575
|
-
|
|
4576
|
+
copy.disabled_agents = migrated;
|
|
4576
4577
|
needsWrite = true;
|
|
4577
4578
|
}
|
|
4578
4579
|
}
|
|
4579
|
-
if (
|
|
4580
|
-
const { migrated, changed, removed } = migrateHookNames(
|
|
4580
|
+
if (copy.disabled_hooks && Array.isArray(copy.disabled_hooks)) {
|
|
4581
|
+
const { migrated, changed, removed } = migrateHookNames(copy.disabled_hooks);
|
|
4581
4582
|
if (changed) {
|
|
4582
|
-
|
|
4583
|
+
copy.disabled_hooks = migrated;
|
|
4583
4584
|
needsWrite = true;
|
|
4584
4585
|
}
|
|
4585
4586
|
if (removed.length > 0) {
|
|
@@ -4590,13 +4591,20 @@ function migrateConfigFile(configPath, rawConfig) {
|
|
|
4590
4591
|
try {
|
|
4591
4592
|
const timestamp2 = new Date().toISOString().replace(/[:.]/g, "-");
|
|
4592
4593
|
const backupPath = `${configPath}.bak.${timestamp2}`;
|
|
4593
|
-
|
|
4594
|
-
|
|
4594
|
+
try {
|
|
4595
|
+
fs5.copyFileSync(configPath, backupPath);
|
|
4596
|
+
} catch {}
|
|
4597
|
+
fs5.writeFileSync(configPath, JSON.stringify(copy, null, 2) + `
|
|
4595
4598
|
`, "utf-8");
|
|
4596
4599
|
log(`Migrated config file: ${configPath} (backup: ${backupPath})`);
|
|
4597
4600
|
} catch (err) {
|
|
4598
4601
|
log(`Failed to write migrated config to ${configPath}:`, err);
|
|
4602
|
+
return false;
|
|
4603
|
+
}
|
|
4604
|
+
for (const key of Object.keys(rawConfig)) {
|
|
4605
|
+
delete rawConfig[key];
|
|
4599
4606
|
}
|
|
4607
|
+
Object.assign(rawConfig, copy);
|
|
4600
4608
|
}
|
|
4601
4609
|
return needsWrite;
|
|
4602
4610
|
}
|
|
@@ -5470,7 +5478,8 @@ async function fetchAvailableModels(client, options) {
|
|
|
5470
5478
|
log("[fetchAvailableModels] provider-models cache empty, falling back to models.json");
|
|
5471
5479
|
} else {
|
|
5472
5480
|
log("[fetchAvailableModels] using provider-models cache (whitelist-filtered)");
|
|
5473
|
-
|
|
5481
|
+
const modelsByProvider = providerModelsCache.models;
|
|
5482
|
+
for (const [providerId, modelIds] of Object.entries(modelsByProvider)) {
|
|
5474
5483
|
if (!connectedSet.has(providerId)) {
|
|
5475
5484
|
continue;
|
|
5476
5485
|
}
|
|
@@ -5643,7 +5652,7 @@ function resolveModelPipeline(request) {
|
|
|
5643
5652
|
return { model: match, provenance: "category-default", attempted };
|
|
5644
5653
|
}
|
|
5645
5654
|
} else {
|
|
5646
|
-
const connectedProviders = readConnectedProvidersCache();
|
|
5655
|
+
const connectedProviders = constraints.connectedProviders ?? readConnectedProvidersCache();
|
|
5647
5656
|
if (connectedProviders === null) {
|
|
5648
5657
|
log("Model resolved via category default (no cache, first run)", {
|
|
5649
5658
|
model: normalizedCategoryDefault
|
|
@@ -5667,7 +5676,7 @@ function resolveModelPipeline(request) {
|
|
|
5667
5676
|
}
|
|
5668
5677
|
if (fallbackChain && fallbackChain.length > 0) {
|
|
5669
5678
|
if (availableModels.size === 0) {
|
|
5670
|
-
const connectedProviders = readConnectedProvidersCache();
|
|
5679
|
+
const connectedProviders = constraints.connectedProviders ?? readConnectedProvidersCache();
|
|
5671
5680
|
const connectedSet = connectedProviders ? new Set(connectedProviders) : null;
|
|
5672
5681
|
if (connectedSet === null) {
|
|
5673
5682
|
log("Model fallback chain skipped (no connected providers cache) - falling through to system default");
|
|
@@ -6172,6 +6181,127 @@ function injectServerAuthIntoClient(client) {
|
|
|
6172
6181
|
// src/shared/port-utils.ts
|
|
6173
6182
|
var init_port_utils = () => {};
|
|
6174
6183
|
|
|
6184
|
+
// src/shared/git-worktree/parse-status-porcelain.ts
|
|
6185
|
+
function parseGitStatusPorcelain(output) {
|
|
6186
|
+
const map2 = new Map;
|
|
6187
|
+
if (!output)
|
|
6188
|
+
return map2;
|
|
6189
|
+
for (const line of output.split(`
|
|
6190
|
+
`)) {
|
|
6191
|
+
if (!line)
|
|
6192
|
+
continue;
|
|
6193
|
+
const status = line.substring(0, 2).trim();
|
|
6194
|
+
const filePath = line.substring(3);
|
|
6195
|
+
if (!filePath)
|
|
6196
|
+
continue;
|
|
6197
|
+
if (status === "A" || status === "??") {
|
|
6198
|
+
map2.set(filePath, "added");
|
|
6199
|
+
} else if (status === "D") {
|
|
6200
|
+
map2.set(filePath, "deleted");
|
|
6201
|
+
} else {
|
|
6202
|
+
map2.set(filePath, "modified");
|
|
6203
|
+
}
|
|
6204
|
+
}
|
|
6205
|
+
return map2;
|
|
6206
|
+
}
|
|
6207
|
+
|
|
6208
|
+
// src/shared/git-worktree/parse-diff-numstat.ts
|
|
6209
|
+
function parseGitDiffNumstat(output, statusMap) {
|
|
6210
|
+
if (!output)
|
|
6211
|
+
return [];
|
|
6212
|
+
const stats = [];
|
|
6213
|
+
for (const line of output.split(`
|
|
6214
|
+
`)) {
|
|
6215
|
+
const parts = line.split("\t");
|
|
6216
|
+
if (parts.length < 3)
|
|
6217
|
+
continue;
|
|
6218
|
+
const [addedStr, removedStr, path5] = parts;
|
|
6219
|
+
const added = addedStr === "-" ? 0 : parseInt(addedStr, 10);
|
|
6220
|
+
const removed = removedStr === "-" ? 0 : parseInt(removedStr, 10);
|
|
6221
|
+
stats.push({
|
|
6222
|
+
path: path5,
|
|
6223
|
+
added,
|
|
6224
|
+
removed,
|
|
6225
|
+
status: statusMap.get(path5) ?? "modified"
|
|
6226
|
+
});
|
|
6227
|
+
}
|
|
6228
|
+
return stats;
|
|
6229
|
+
}
|
|
6230
|
+
|
|
6231
|
+
// src/shared/git-worktree/collect-git-diff-stats.ts
|
|
6232
|
+
import { execFileSync } from "child_process";
|
|
6233
|
+
function collectGitDiffStats(directory) {
|
|
6234
|
+
try {
|
|
6235
|
+
const diffOutput = execFileSync("git", ["diff", "--numstat", "HEAD"], {
|
|
6236
|
+
cwd: directory,
|
|
6237
|
+
encoding: "utf-8",
|
|
6238
|
+
timeout: 5000,
|
|
6239
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
6240
|
+
}).trim();
|
|
6241
|
+
if (!diffOutput)
|
|
6242
|
+
return [];
|
|
6243
|
+
const statusOutput = execFileSync("git", ["status", "--porcelain"], {
|
|
6244
|
+
cwd: directory,
|
|
6245
|
+
encoding: "utf-8",
|
|
6246
|
+
timeout: 5000,
|
|
6247
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
6248
|
+
}).trim();
|
|
6249
|
+
const statusMap = parseGitStatusPorcelain(statusOutput);
|
|
6250
|
+
return parseGitDiffNumstat(diffOutput, statusMap);
|
|
6251
|
+
} catch {
|
|
6252
|
+
return [];
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6255
|
+
var init_collect_git_diff_stats = () => {};
|
|
6256
|
+
|
|
6257
|
+
// src/shared/git-worktree/format-file-changes.ts
|
|
6258
|
+
function formatFileChanges(stats, notepadPath) {
|
|
6259
|
+
if (stats.length === 0)
|
|
6260
|
+
return `[FILE CHANGES SUMMARY]
|
|
6261
|
+
No file changes detected.
|
|
6262
|
+
`;
|
|
6263
|
+
const modified = stats.filter((s) => s.status === "modified");
|
|
6264
|
+
const added = stats.filter((s) => s.status === "added");
|
|
6265
|
+
const deleted = stats.filter((s) => s.status === "deleted");
|
|
6266
|
+
const lines = ["[FILE CHANGES SUMMARY]"];
|
|
6267
|
+
if (modified.length > 0) {
|
|
6268
|
+
lines.push("Modified files:");
|
|
6269
|
+
for (const f of modified) {
|
|
6270
|
+
lines.push(` ${f.path} (+${f.added}, -${f.removed})`);
|
|
6271
|
+
}
|
|
6272
|
+
lines.push("");
|
|
6273
|
+
}
|
|
6274
|
+
if (added.length > 0) {
|
|
6275
|
+
lines.push("Created files:");
|
|
6276
|
+
for (const f of added) {
|
|
6277
|
+
lines.push(` ${f.path} (+${f.added})`);
|
|
6278
|
+
}
|
|
6279
|
+
lines.push("");
|
|
6280
|
+
}
|
|
6281
|
+
if (deleted.length > 0) {
|
|
6282
|
+
lines.push("Deleted files:");
|
|
6283
|
+
for (const f of deleted) {
|
|
6284
|
+
lines.push(` ${f.path} (-${f.removed})`);
|
|
6285
|
+
}
|
|
6286
|
+
lines.push("");
|
|
6287
|
+
}
|
|
6288
|
+
if (notepadPath) {
|
|
6289
|
+
const notepadStat = stats.find((s) => s.path.includes("notepad") || s.path.includes(".sisyphus"));
|
|
6290
|
+
if (notepadStat) {
|
|
6291
|
+
lines.push("[NOTEPAD UPDATED]");
|
|
6292
|
+
lines.push(` ${notepadStat.path} (+${notepadStat.added})`);
|
|
6293
|
+
lines.push("");
|
|
6294
|
+
}
|
|
6295
|
+
}
|
|
6296
|
+
return lines.join(`
|
|
6297
|
+
`);
|
|
6298
|
+
}
|
|
6299
|
+
|
|
6300
|
+
// src/shared/git-worktree/index.ts
|
|
6301
|
+
var init_git_worktree = __esm(() => {
|
|
6302
|
+
init_collect_git_diff_stats();
|
|
6303
|
+
});
|
|
6304
|
+
|
|
6175
6305
|
// src/shared/safe-create-hook.ts
|
|
6176
6306
|
function safeCreateHook(name, factory, options) {
|
|
6177
6307
|
const enabled = options?.enabled ?? true;
|
|
@@ -6234,6 +6364,7 @@ var init_shared = __esm(() => {
|
|
|
6234
6364
|
init_tmux();
|
|
6235
6365
|
init_model_suggestion_retry();
|
|
6236
6366
|
init_port_utils();
|
|
6367
|
+
init_git_worktree();
|
|
6237
6368
|
init_safe_create_hook();
|
|
6238
6369
|
});
|
|
6239
6370
|
|
|
@@ -11599,6 +11730,12 @@ function isPlanAgent(agentName) {
|
|
|
11599
11730
|
const lowerName = agentName.toLowerCase().trim();
|
|
11600
11731
|
return PLAN_AGENT_NAMES.some((name) => lowerName === name || lowerName.includes(name));
|
|
11601
11732
|
}
|
|
11733
|
+
function isPlanFamily(agentName) {
|
|
11734
|
+
if (!agentName)
|
|
11735
|
+
return false;
|
|
11736
|
+
const lowerName = agentName.toLowerCase().trim();
|
|
11737
|
+
return PLAN_FAMILY_NAMES.some((name) => lowerName === name || lowerName.includes(name));
|
|
11738
|
+
}
|
|
11602
11739
|
var VISUAL_CATEGORY_PROMPT_APPEND = `<Category_Context>
|
|
11603
11740
|
You are working on VISUAL/UI tasks.
|
|
11604
11741
|
|
|
@@ -12016,7 +12153,7 @@ WHY THIS FORMAT IS MANDATORY:
|
|
|
12016
12153
|
- QA criteria ensure verifiable completion
|
|
12017
12154
|
</FINAL_OUTPUT_FOR_CALLER>
|
|
12018
12155
|
|
|
12019
|
-
`, PLAN_AGENT_NAMES;
|
|
12156
|
+
`, PLAN_AGENT_NAMES, PLAN_FAMILY_NAMES;
|
|
12020
12157
|
var init_constants4 = __esm(() => {
|
|
12021
12158
|
DEFAULT_CATEGORIES = {
|
|
12022
12159
|
"visual-engineering": { model: "google/gemini-3-pro" },
|
|
@@ -12048,7 +12185,8 @@ var init_constants4 = __esm(() => {
|
|
|
12048
12185
|
"unspecified-high": "Tasks that don't fit other categories, high effort required",
|
|
12049
12186
|
writing: "Documentation, prose, technical writing"
|
|
12050
12187
|
};
|
|
12051
|
-
PLAN_AGENT_NAMES = ["plan"
|
|
12188
|
+
PLAN_AGENT_NAMES = ["plan"];
|
|
12189
|
+
PLAN_FAMILY_NAMES = ["plan", "prometheus"];
|
|
12052
12190
|
});
|
|
12053
12191
|
|
|
12054
12192
|
// node_modules/ajv/dist/compile/codegen/code.js
|
|
@@ -12429,11 +12567,11 @@ var require_codegen = __commonJS((exports) => {
|
|
|
12429
12567
|
const rhs = this.rhs === undefined ? "" : ` = ${this.rhs}`;
|
|
12430
12568
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
12431
12569
|
}
|
|
12432
|
-
optimizeNames(names,
|
|
12570
|
+
optimizeNames(names, constants18) {
|
|
12433
12571
|
if (!names[this.name.str])
|
|
12434
12572
|
return;
|
|
12435
12573
|
if (this.rhs)
|
|
12436
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
12574
|
+
this.rhs = optimizeExpr(this.rhs, names, constants18);
|
|
12437
12575
|
return this;
|
|
12438
12576
|
}
|
|
12439
12577
|
get names() {
|
|
@@ -12451,10 +12589,10 @@ var require_codegen = __commonJS((exports) => {
|
|
|
12451
12589
|
render({ _n }) {
|
|
12452
12590
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
12453
12591
|
}
|
|
12454
|
-
optimizeNames(names,
|
|
12592
|
+
optimizeNames(names, constants18) {
|
|
12455
12593
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
|
|
12456
12594
|
return;
|
|
12457
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
12595
|
+
this.rhs = optimizeExpr(this.rhs, names, constants18);
|
|
12458
12596
|
return this;
|
|
12459
12597
|
}
|
|
12460
12598
|
get names() {
|
|
@@ -12520,8 +12658,8 @@ var require_codegen = __commonJS((exports) => {
|
|
|
12520
12658
|
optimizeNodes() {
|
|
12521
12659
|
return `${this.code}` ? this : undefined;
|
|
12522
12660
|
}
|
|
12523
|
-
optimizeNames(names,
|
|
12524
|
-
this.code = optimizeExpr(this.code, names,
|
|
12661
|
+
optimizeNames(names, constants18) {
|
|
12662
|
+
this.code = optimizeExpr(this.code, names, constants18);
|
|
12525
12663
|
return this;
|
|
12526
12664
|
}
|
|
12527
12665
|
get names() {
|
|
@@ -12551,12 +12689,12 @@ var require_codegen = __commonJS((exports) => {
|
|
|
12551
12689
|
}
|
|
12552
12690
|
return nodes.length > 0 ? this : undefined;
|
|
12553
12691
|
}
|
|
12554
|
-
optimizeNames(names,
|
|
12692
|
+
optimizeNames(names, constants18) {
|
|
12555
12693
|
const { nodes } = this;
|
|
12556
12694
|
let i2 = nodes.length;
|
|
12557
12695
|
while (i2--) {
|
|
12558
12696
|
const n = nodes[i2];
|
|
12559
|
-
if (n.optimizeNames(names,
|
|
12697
|
+
if (n.optimizeNames(names, constants18))
|
|
12560
12698
|
continue;
|
|
12561
12699
|
subtractNames(names, n.names);
|
|
12562
12700
|
nodes.splice(i2, 1);
|
|
@@ -12613,12 +12751,12 @@ var require_codegen = __commonJS((exports) => {
|
|
|
12613
12751
|
return;
|
|
12614
12752
|
return this;
|
|
12615
12753
|
}
|
|
12616
|
-
optimizeNames(names,
|
|
12754
|
+
optimizeNames(names, constants18) {
|
|
12617
12755
|
var _a;
|
|
12618
|
-
this.else = (_a = this.else) === null || _a === undefined ? undefined : _a.optimizeNames(names,
|
|
12619
|
-
if (!(super.optimizeNames(names,
|
|
12756
|
+
this.else = (_a = this.else) === null || _a === undefined ? undefined : _a.optimizeNames(names, constants18);
|
|
12757
|
+
if (!(super.optimizeNames(names, constants18) || this.else))
|
|
12620
12758
|
return;
|
|
12621
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
12759
|
+
this.condition = optimizeExpr(this.condition, names, constants18);
|
|
12622
12760
|
return this;
|
|
12623
12761
|
}
|
|
12624
12762
|
get names() {
|
|
@@ -12643,10 +12781,10 @@ var require_codegen = __commonJS((exports) => {
|
|
|
12643
12781
|
render(opts) {
|
|
12644
12782
|
return `for(${this.iteration})` + super.render(opts);
|
|
12645
12783
|
}
|
|
12646
|
-
optimizeNames(names,
|
|
12647
|
-
if (!super.optimizeNames(names,
|
|
12784
|
+
optimizeNames(names, constants18) {
|
|
12785
|
+
if (!super.optimizeNames(names, constants18))
|
|
12648
12786
|
return;
|
|
12649
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
12787
|
+
this.iteration = optimizeExpr(this.iteration, names, constants18);
|
|
12650
12788
|
return this;
|
|
12651
12789
|
}
|
|
12652
12790
|
get names() {
|
|
@@ -12684,10 +12822,10 @@ var require_codegen = __commonJS((exports) => {
|
|
|
12684
12822
|
render(opts) {
|
|
12685
12823
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
12686
12824
|
}
|
|
12687
|
-
optimizeNames(names,
|
|
12688
|
-
if (!super.optimizeNames(names,
|
|
12825
|
+
optimizeNames(names, constants18) {
|
|
12826
|
+
if (!super.optimizeNames(names, constants18))
|
|
12689
12827
|
return;
|
|
12690
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
12828
|
+
this.iterable = optimizeExpr(this.iterable, names, constants18);
|
|
12691
12829
|
return this;
|
|
12692
12830
|
}
|
|
12693
12831
|
get names() {
|
|
@@ -12732,11 +12870,11 @@ var require_codegen = __commonJS((exports) => {
|
|
|
12732
12870
|
(_b = this.finally) === null || _b === undefined || _b.optimizeNodes();
|
|
12733
12871
|
return this;
|
|
12734
12872
|
}
|
|
12735
|
-
optimizeNames(names,
|
|
12873
|
+
optimizeNames(names, constants18) {
|
|
12736
12874
|
var _a, _b;
|
|
12737
|
-
super.optimizeNames(names,
|
|
12738
|
-
(_a = this.catch) === null || _a === undefined || _a.optimizeNames(names,
|
|
12739
|
-
(_b = this.finally) === null || _b === undefined || _b.optimizeNames(names,
|
|
12875
|
+
super.optimizeNames(names, constants18);
|
|
12876
|
+
(_a = this.catch) === null || _a === undefined || _a.optimizeNames(names, constants18);
|
|
12877
|
+
(_b = this.finally) === null || _b === undefined || _b.optimizeNames(names, constants18);
|
|
12740
12878
|
return this;
|
|
12741
12879
|
}
|
|
12742
12880
|
get names() {
|
|
@@ -13010,7 +13148,7 @@ var require_codegen = __commonJS((exports) => {
|
|
|
13010
13148
|
function addExprNames(names, from) {
|
|
13011
13149
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
13012
13150
|
}
|
|
13013
|
-
function optimizeExpr(expr, names,
|
|
13151
|
+
function optimizeExpr(expr, names, constants18) {
|
|
13014
13152
|
if (expr instanceof code_1.Name)
|
|
13015
13153
|
return replaceName(expr);
|
|
13016
13154
|
if (!canOptimize(expr))
|
|
@@ -13025,14 +13163,14 @@ var require_codegen = __commonJS((exports) => {
|
|
|
13025
13163
|
return items;
|
|
13026
13164
|
}, []));
|
|
13027
13165
|
function replaceName(n) {
|
|
13028
|
-
const c =
|
|
13166
|
+
const c = constants18[n.str];
|
|
13029
13167
|
if (c === undefined || names[n.str] !== 1)
|
|
13030
13168
|
return n;
|
|
13031
13169
|
delete names[n.str];
|
|
13032
13170
|
return c;
|
|
13033
13171
|
}
|
|
13034
13172
|
function canOptimize(e) {
|
|
13035
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
13173
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants18[c.str] !== undefined);
|
|
13036
13174
|
}
|
|
13037
13175
|
}
|
|
13038
13176
|
function subtractNames(names, from) {
|
|
@@ -13479,37 +13617,37 @@ var require_dataType = __commonJS((exports) => {
|
|
|
13479
13617
|
DataType2[DataType2["Wrong"] = 1] = "Wrong";
|
|
13480
13618
|
})(DataType || (exports.DataType = DataType = {}));
|
|
13481
13619
|
function getSchemaTypes(schema2) {
|
|
13482
|
-
const
|
|
13483
|
-
const hasNull =
|
|
13620
|
+
const types22 = getJSONTypes(schema2.type);
|
|
13621
|
+
const hasNull = types22.includes("null");
|
|
13484
13622
|
if (hasNull) {
|
|
13485
13623
|
if (schema2.nullable === false)
|
|
13486
13624
|
throw new Error("type: null contradicts nullable: false");
|
|
13487
13625
|
} else {
|
|
13488
|
-
if (!
|
|
13626
|
+
if (!types22.length && schema2.nullable !== undefined) {
|
|
13489
13627
|
throw new Error('"nullable" cannot be used without "type"');
|
|
13490
13628
|
}
|
|
13491
13629
|
if (schema2.nullable === true)
|
|
13492
|
-
|
|
13630
|
+
types22.push("null");
|
|
13493
13631
|
}
|
|
13494
|
-
return
|
|
13632
|
+
return types22;
|
|
13495
13633
|
}
|
|
13496
13634
|
exports.getSchemaTypes = getSchemaTypes;
|
|
13497
13635
|
function getJSONTypes(ts) {
|
|
13498
|
-
const
|
|
13499
|
-
if (
|
|
13500
|
-
return
|
|
13501
|
-
throw new Error("type must be JSONType or JSONType[]: " +
|
|
13636
|
+
const types22 = Array.isArray(ts) ? ts : ts ? [ts] : [];
|
|
13637
|
+
if (types22.every(rules_1.isJSONType))
|
|
13638
|
+
return types22;
|
|
13639
|
+
throw new Error("type must be JSONType or JSONType[]: " + types22.join(","));
|
|
13502
13640
|
}
|
|
13503
13641
|
exports.getJSONTypes = getJSONTypes;
|
|
13504
|
-
function coerceAndCheckDataType(it,
|
|
13642
|
+
function coerceAndCheckDataType(it, types22) {
|
|
13505
13643
|
const { gen, data, opts } = it;
|
|
13506
|
-
const coerceTo = coerceToTypes(
|
|
13507
|
-
const checkTypes =
|
|
13644
|
+
const coerceTo = coerceToTypes(types22, opts.coerceTypes);
|
|
13645
|
+
const checkTypes = types22.length > 0 && !(coerceTo.length === 0 && types22.length === 1 && (0, applicability_1.schemaHasRulesForType)(it, types22[0]));
|
|
13508
13646
|
if (checkTypes) {
|
|
13509
|
-
const wrongType = checkDataTypes(
|
|
13647
|
+
const wrongType = checkDataTypes(types22, data, opts.strictNumbers, DataType.Wrong);
|
|
13510
13648
|
gen.if(wrongType, () => {
|
|
13511
13649
|
if (coerceTo.length)
|
|
13512
|
-
coerceData(it,
|
|
13650
|
+
coerceData(it, types22, coerceTo);
|
|
13513
13651
|
else
|
|
13514
13652
|
reportTypeError(it);
|
|
13515
13653
|
});
|
|
@@ -13518,15 +13656,15 @@ var require_dataType = __commonJS((exports) => {
|
|
|
13518
13656
|
}
|
|
13519
13657
|
exports.coerceAndCheckDataType = coerceAndCheckDataType;
|
|
13520
13658
|
var COERCIBLE = new Set(["string", "number", "integer", "boolean", "null"]);
|
|
13521
|
-
function coerceToTypes(
|
|
13522
|
-
return coerceTypes ?
|
|
13659
|
+
function coerceToTypes(types22, coerceTypes) {
|
|
13660
|
+
return coerceTypes ? types22.filter((t) => COERCIBLE.has(t) || coerceTypes === "array" && t === "array") : [];
|
|
13523
13661
|
}
|
|
13524
|
-
function coerceData(it,
|
|
13662
|
+
function coerceData(it, types22, coerceTo) {
|
|
13525
13663
|
const { gen, data, opts } = it;
|
|
13526
13664
|
const dataType = gen.let("dataType", (0, codegen_1._)`typeof ${data}`);
|
|
13527
13665
|
const coerced = gen.let("coerced", (0, codegen_1._)`undefined`);
|
|
13528
13666
|
if (opts.coerceTypes === "array") {
|
|
13529
|
-
gen.if((0, codegen_1._)`${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen.assign(data, (0, codegen_1._)`${data}[0]`).assign(dataType, (0, codegen_1._)`typeof ${data}`).if(checkDataTypes(
|
|
13667
|
+
gen.if((0, codegen_1._)`${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen.assign(data, (0, codegen_1._)`${data}[0]`).assign(dataType, (0, codegen_1._)`typeof ${data}`).if(checkDataTypes(types22, data, opts.strictNumbers), () => gen.assign(coerced, data)));
|
|
13530
13668
|
}
|
|
13531
13669
|
gen.if((0, codegen_1._)`${coerced} !== undefined`);
|
|
13532
13670
|
for (const t of coerceTo) {
|
|
@@ -13602,19 +13740,19 @@ var require_dataType = __commonJS((exports) => {
|
|
|
13602
13740
|
return checkDataType(dataTypes[0], data, strictNums, correct);
|
|
13603
13741
|
}
|
|
13604
13742
|
let cond;
|
|
13605
|
-
const
|
|
13606
|
-
if (
|
|
13743
|
+
const types22 = (0, util_1.toHash)(dataTypes);
|
|
13744
|
+
if (types22.array && types22.object) {
|
|
13607
13745
|
const notObj = (0, codegen_1._)`typeof ${data} != "object"`;
|
|
13608
|
-
cond =
|
|
13609
|
-
delete
|
|
13610
|
-
delete
|
|
13611
|
-
delete
|
|
13746
|
+
cond = types22.null ? notObj : (0, codegen_1._)`!${data} || ${notObj}`;
|
|
13747
|
+
delete types22.null;
|
|
13748
|
+
delete types22.array;
|
|
13749
|
+
delete types22.object;
|
|
13612
13750
|
} else {
|
|
13613
13751
|
cond = codegen_1.nil;
|
|
13614
13752
|
}
|
|
13615
|
-
if (
|
|
13616
|
-
delete
|
|
13617
|
-
for (const t in
|
|
13753
|
+
if (types22.number)
|
|
13754
|
+
delete types22.integer;
|
|
13755
|
+
for (const t in types22)
|
|
13618
13756
|
cond = (0, codegen_1.and)(cond, checkDataType(t, data, strictNums, correct));
|
|
13619
13757
|
return cond;
|
|
13620
13758
|
}
|
|
@@ -14402,9 +14540,9 @@ var require_validate = __commonJS((exports) => {
|
|
|
14402
14540
|
function typeAndKeywords(it, errsCount) {
|
|
14403
14541
|
if (it.opts.jtd)
|
|
14404
14542
|
return schemaKeywords(it, [], false, errsCount);
|
|
14405
|
-
const
|
|
14406
|
-
const checkedTypes = (0, dataType_1.coerceAndCheckDataType)(it,
|
|
14407
|
-
schemaKeywords(it,
|
|
14543
|
+
const types22 = (0, dataType_1.getSchemaTypes)(it.schema);
|
|
14544
|
+
const checkedTypes = (0, dataType_1.coerceAndCheckDataType)(it, types22);
|
|
14545
|
+
schemaKeywords(it, types22, !checkedTypes, errsCount);
|
|
14408
14546
|
}
|
|
14409
14547
|
function checkRefsAndKeywords(it) {
|
|
14410
14548
|
const { schema: schema2, errSchemaPath, opts, self } = it;
|
|
@@ -14454,7 +14592,7 @@ var require_validate = __commonJS((exports) => {
|
|
|
14454
14592
|
if (items instanceof codegen_1.Name)
|
|
14455
14593
|
gen.assign((0, codegen_1._)`${evaluated}.items`, items);
|
|
14456
14594
|
}
|
|
14457
|
-
function schemaKeywords(it,
|
|
14595
|
+
function schemaKeywords(it, types22, typeErrors, errsCount) {
|
|
14458
14596
|
const { gen, schema: schema2, data, allErrors, opts, self } = it;
|
|
14459
14597
|
const { RULES } = self;
|
|
14460
14598
|
if (schema2.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema2, RULES))) {
|
|
@@ -14462,7 +14600,7 @@ var require_validate = __commonJS((exports) => {
|
|
|
14462
14600
|
return;
|
|
14463
14601
|
}
|
|
14464
14602
|
if (!opts.jtd)
|
|
14465
|
-
checkStrictTypes(it,
|
|
14603
|
+
checkStrictTypes(it, types22);
|
|
14466
14604
|
gen.block(() => {
|
|
14467
14605
|
for (const group of RULES.rules)
|
|
14468
14606
|
groupKeywords(group);
|
|
@@ -14474,7 +14612,7 @@ var require_validate = __commonJS((exports) => {
|
|
|
14474
14612
|
if (group.type) {
|
|
14475
14613
|
gen.if((0, dataType_2.checkDataType)(group.type, data, opts.strictNumbers));
|
|
14476
14614
|
iterateKeywords(it, group);
|
|
14477
|
-
if (
|
|
14615
|
+
if (types22.length === 1 && types22[0] === group.type && typeErrors) {
|
|
14478
14616
|
gen.else();
|
|
14479
14617
|
(0, dataType_2.reportTypeError)(it);
|
|
14480
14618
|
}
|
|
@@ -14498,27 +14636,27 @@ var require_validate = __commonJS((exports) => {
|
|
|
14498
14636
|
}
|
|
14499
14637
|
});
|
|
14500
14638
|
}
|
|
14501
|
-
function checkStrictTypes(it,
|
|
14639
|
+
function checkStrictTypes(it, types22) {
|
|
14502
14640
|
if (it.schemaEnv.meta || !it.opts.strictTypes)
|
|
14503
14641
|
return;
|
|
14504
|
-
checkContextTypes(it,
|
|
14642
|
+
checkContextTypes(it, types22);
|
|
14505
14643
|
if (!it.opts.allowUnionTypes)
|
|
14506
|
-
checkMultipleTypes(it,
|
|
14644
|
+
checkMultipleTypes(it, types22);
|
|
14507
14645
|
checkKeywordTypes(it, it.dataTypes);
|
|
14508
14646
|
}
|
|
14509
|
-
function checkContextTypes(it,
|
|
14510
|
-
if (!
|
|
14647
|
+
function checkContextTypes(it, types22) {
|
|
14648
|
+
if (!types22.length)
|
|
14511
14649
|
return;
|
|
14512
14650
|
if (!it.dataTypes.length) {
|
|
14513
|
-
it.dataTypes =
|
|
14651
|
+
it.dataTypes = types22;
|
|
14514
14652
|
return;
|
|
14515
14653
|
}
|
|
14516
|
-
|
|
14654
|
+
types22.forEach((t) => {
|
|
14517
14655
|
if (!includesType(it.dataTypes, t)) {
|
|
14518
14656
|
strictTypesError(it, `type "${t}" not allowed by context "${it.dataTypes.join(",")}"`);
|
|
14519
14657
|
}
|
|
14520
14658
|
});
|
|
14521
|
-
narrowSchemaTypes(it,
|
|
14659
|
+
narrowSchemaTypes(it, types22);
|
|
14522
14660
|
}
|
|
14523
14661
|
function checkMultipleTypes(it, ts) {
|
|
14524
14662
|
if (ts.length > 1 && !(ts.length === 2 && ts.includes("null"))) {
|
|
@@ -20108,6 +20246,9 @@ function extractMessageIndex(error) {
|
|
|
20108
20246
|
}
|
|
20109
20247
|
function detectErrorType(error) {
|
|
20110
20248
|
const message = getErrorMessage(error);
|
|
20249
|
+
if (message.includes("assistant message prefill") || message.includes("conversation must end with a user message")) {
|
|
20250
|
+
return "assistant_prefill_unsupported";
|
|
20251
|
+
}
|
|
20111
20252
|
if (message.includes("thinking") && (message.includes("first block") || message.includes("must start with") || message.includes("preceeding") || message.includes("final block") || message.includes("cannot be thinking") || message.includes("expected") && message.includes("found"))) {
|
|
20112
20253
|
return "thinking_block_order";
|
|
20113
20254
|
}
|
|
@@ -20229,12 +20370,14 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
20229
20370
|
const toastTitles = {
|
|
20230
20371
|
tool_result_missing: "Tool Crash Recovery",
|
|
20231
20372
|
thinking_block_order: "Thinking Block Recovery",
|
|
20232
|
-
thinking_disabled_violation: "Thinking Strip Recovery"
|
|
20373
|
+
thinking_disabled_violation: "Thinking Strip Recovery",
|
|
20374
|
+
assistant_prefill_unsupported: "Prefill Error Recovery"
|
|
20233
20375
|
};
|
|
20234
20376
|
const toastMessages = {
|
|
20235
20377
|
tool_result_missing: "Injecting cancelled tool results...",
|
|
20236
20378
|
thinking_block_order: "Fixing message structure...",
|
|
20237
|
-
thinking_disabled_violation: "Stripping thinking blocks..."
|
|
20379
|
+
thinking_disabled_violation: "Stripping thinking blocks...",
|
|
20380
|
+
assistant_prefill_unsupported: "Sending 'Continue' to recover..."
|
|
20238
20381
|
};
|
|
20239
20382
|
await ctx.client.tui.showToast({
|
|
20240
20383
|
body: {
|
|
@@ -20261,6 +20404,8 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
20261
20404
|
const resumeConfig = extractResumeConfig(lastUser, sessionID);
|
|
20262
20405
|
await resumeSession(ctx.client, resumeConfig);
|
|
20263
20406
|
}
|
|
20407
|
+
} else if (errorType === "assistant_prefill_unsupported") {
|
|
20408
|
+
success = true;
|
|
20264
20409
|
}
|
|
20265
20410
|
return success;
|
|
20266
20411
|
} catch (err) {
|
|
@@ -24082,9 +24227,9 @@ You ARE the planner. Your job: create bulletproof work plans.
|
|
|
24082
24227
|
### Research Protocol
|
|
24083
24228
|
1. **Fire parallel background agents** for comprehensive context:
|
|
24084
24229
|
\`\`\`
|
|
24085
|
-
task(
|
|
24086
|
-
task(
|
|
24087
|
-
task(
|
|
24230
|
+
task(subagent_type="explore", load_skills=[], prompt="Find existing patterns for [topic] in codebase", run_in_background=true)
|
|
24231
|
+
task(subagent_type="explore", load_skills=[], prompt="Find test infrastructure and conventions", run_in_background=true)
|
|
24232
|
+
task(subagent_type="librarian", load_skills=[], prompt="Find official docs and best practices for [technology]", run_in_background=true)
|
|
24088
24233
|
\`\`\`
|
|
24089
24234
|
2. **Wait for results** before planning - rushed plans fail
|
|
24090
24235
|
3. **Synthesize findings** into informed requirements
|
|
@@ -24245,10 +24390,10 @@ Use these when they provide clear value based on the decision framework above:
|
|
|
24245
24390
|
|
|
24246
24391
|
| Resource | When to Use | How to Use |
|
|
24247
24392
|
|----------|-------------|------------|
|
|
24248
|
-
| explore agent | Need codebase patterns you don't have | \`task(subagent_type="explore", run_in_background=true, ...)\` |
|
|
24249
|
-
| librarian agent | External library docs, OSS examples | \`task(subagent_type="librarian", run_in_background=true, ...)\` |
|
|
24250
|
-
| oracle agent | Stuck on architecture/debugging after 2+ attempts | \`task(subagent_type="oracle", ...)\` |
|
|
24251
|
-
| plan agent | Complex multi-step with dependencies (5+ steps) | \`task(subagent_type="plan", ...)\` |
|
|
24393
|
+
| explore agent | Need codebase patterns you don't have | \`task(subagent_type="explore", load_skills=[], run_in_background=true, ...)\` |
|
|
24394
|
+
| librarian agent | External library docs, OSS examples | \`task(subagent_type="librarian", load_skills=[], run_in_background=true, ...)\` |
|
|
24395
|
+
| oracle agent | Stuck on architecture/debugging after 2+ attempts | \`task(subagent_type="oracle", load_skills=[], ...)\` |
|
|
24396
|
+
| plan agent | Complex multi-step with dependencies (5+ steps) | \`task(subagent_type="plan", load_skills=[], ...)\` |
|
|
24252
24397
|
| task category | Specialized work matching a category | \`task(category="...", load_skills=[...])\` |
|
|
24253
24398
|
|
|
24254
24399
|
<tool_usage_rules>
|
|
@@ -24418,7 +24563,7 @@ TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
|
|
|
24418
24563
|
| Architecture decision needed | MUST call plan agent |
|
|
24419
24564
|
|
|
24420
24565
|
\`\`\`
|
|
24421
|
-
task(subagent_type="plan", prompt="<gathered context + user request>")
|
|
24566
|
+
task(subagent_type="plan", load_skills=[], prompt="<gathered context + user request>")
|
|
24422
24567
|
\`\`\`
|
|
24423
24568
|
|
|
24424
24569
|
**WHY PLAN AGENT IS MANDATORY:**
|
|
@@ -24433,9 +24578,9 @@ task(subagent_type="plan", prompt="<gathered context + user request>")
|
|
|
24433
24578
|
|
|
24434
24579
|
| Scenario | Action |
|
|
24435
24580
|
|----------|--------|
|
|
24436
|
-
| Plan agent asks clarifying questions | \`task(session_id="{returned_session_id}", prompt="<your answer>")\` |
|
|
24437
|
-
| Need to refine the plan | \`task(session_id="{returned_session_id}", prompt="Please adjust: <feedback>")\` |
|
|
24438
|
-
| Plan needs more detail | \`task(session_id="{returned_session_id}", prompt="Add more detail to Task N")\` |
|
|
24581
|
+
| Plan agent asks clarifying questions | \`task(session_id="{returned_session_id}", load_skills=[], prompt="<your answer>")\` |
|
|
24582
|
+
| Need to refine the plan | \`task(session_id="{returned_session_id}", load_skills=[], prompt="Please adjust: <feedback>")\` |
|
|
24583
|
+
| Plan needs more detail | \`task(session_id="{returned_session_id}", load_skills=[], prompt="Add more detail to Task N")\` |
|
|
24439
24584
|
|
|
24440
24585
|
**WHY SESSION_ID IS CRITICAL:**
|
|
24441
24586
|
- Plan agent retains FULL conversation context
|
|
@@ -24445,10 +24590,10 @@ task(subagent_type="plan", prompt="<gathered context + user request>")
|
|
|
24445
24590
|
|
|
24446
24591
|
\`\`\`
|
|
24447
24592
|
// WRONG: Starting fresh loses all context
|
|
24448
|
-
task(subagent_type="plan", prompt="Here's more info...")
|
|
24593
|
+
task(subagent_type="plan", load_skills=[], prompt="Here's more info...")
|
|
24449
24594
|
|
|
24450
24595
|
// CORRECT: Resume preserves everything
|
|
24451
|
-
task(session_id="ses_abc123", prompt="Here's my answer to your question: ...")
|
|
24596
|
+
task(session_id="ses_abc123", load_skills=[], prompt="Here's my answer to your question: ...")
|
|
24452
24597
|
\`\`\`
|
|
24453
24598
|
|
|
24454
24599
|
**FAILURE TO CALL PLAN AGENT = INCOMPLETE WORK.**
|
|
@@ -24461,10 +24606,10 @@ task(session_id="ses_abc123", prompt="Here's my answer to your question: ...")
|
|
|
24461
24606
|
|
|
24462
24607
|
| Task Type | Action | Why |
|
|
24463
24608
|
|-----------|--------|-----|
|
|
24464
|
-
| Codebase exploration | task(subagent_type="explore", run_in_background=true) | Parallel, context-efficient |
|
|
24465
|
-
| Documentation lookup | task(subagent_type="librarian", run_in_background=true) | Specialized knowledge |
|
|
24466
|
-
| Planning | task(subagent_type="plan") | Parallel task graph + structured TODO list |
|
|
24467
|
-
| Hard problem (conventional) | task(subagent_type="oracle") | Architecture, debugging, complex logic |
|
|
24609
|
+
| Codebase exploration | task(subagent_type="explore", load_skills=[], run_in_background=true) | Parallel, context-efficient |
|
|
24610
|
+
| Documentation lookup | task(subagent_type="librarian", load_skills=[], run_in_background=true) | Specialized knowledge |
|
|
24611
|
+
| Planning | task(subagent_type="plan", load_skills=[]) | Parallel task graph + structured TODO list |
|
|
24612
|
+
| Hard problem (conventional) | task(subagent_type="oracle", load_skills=[]) | Architecture, debugging, complex logic |
|
|
24468
24613
|
| Hard problem (non-conventional) | task(category="artistry", load_skills=[...]) | Different approach needed |
|
|
24469
24614
|
| Implementation | task(category="...", load_skills=[...]) | Domain-optimized models |
|
|
24470
24615
|
|
|
@@ -24898,7 +25043,7 @@ function clearInteractiveBashSessionState(sessionID) {
|
|
|
24898
25043
|
}
|
|
24899
25044
|
}
|
|
24900
25045
|
|
|
24901
|
-
// src/hooks/interactive-bash-session/
|
|
25046
|
+
// src/hooks/interactive-bash-session/parser.ts
|
|
24902
25047
|
function tokenizeCommand(cmd) {
|
|
24903
25048
|
const tokens = [];
|
|
24904
25049
|
let current = "";
|
|
@@ -24980,33 +25125,44 @@ function findSubcommand(tokens) {
|
|
|
24980
25125
|
}
|
|
24981
25126
|
return "";
|
|
24982
25127
|
}
|
|
25128
|
+
|
|
25129
|
+
// src/hooks/interactive-bash-session/state-manager.ts
|
|
25130
|
+
function getOrCreateState(sessionID, sessionStates) {
|
|
25131
|
+
if (!sessionStates.has(sessionID)) {
|
|
25132
|
+
const persisted = loadInteractiveBashSessionState(sessionID);
|
|
25133
|
+
const state2 = persisted ?? {
|
|
25134
|
+
sessionID,
|
|
25135
|
+
tmuxSessions: new Set,
|
|
25136
|
+
updatedAt: Date.now()
|
|
25137
|
+
};
|
|
25138
|
+
sessionStates.set(sessionID, state2);
|
|
25139
|
+
}
|
|
25140
|
+
return sessionStates.get(sessionID);
|
|
25141
|
+
}
|
|
25142
|
+
function isOmoSession(sessionName) {
|
|
25143
|
+
return sessionName !== null && sessionName.startsWith(OMO_SESSION_PREFIX);
|
|
25144
|
+
}
|
|
25145
|
+
async function killAllTrackedSessions(state2) {
|
|
25146
|
+
for (const sessionName of state2.tmuxSessions) {
|
|
25147
|
+
try {
|
|
25148
|
+
const proc = Bun.spawn(["tmux", "kill-session", "-t", sessionName], {
|
|
25149
|
+
stdout: "ignore",
|
|
25150
|
+
stderr: "ignore"
|
|
25151
|
+
});
|
|
25152
|
+
await proc.exited;
|
|
25153
|
+
} catch {}
|
|
25154
|
+
}
|
|
25155
|
+
for (const sessionId of subagentSessions) {}
|
|
25156
|
+
}
|
|
25157
|
+
|
|
25158
|
+
// src/hooks/interactive-bash-session/hook.ts
|
|
24983
25159
|
function createInteractiveBashSessionHook(ctx) {
|
|
24984
25160
|
const sessionStates = new Map;
|
|
24985
|
-
function
|
|
24986
|
-
|
|
24987
|
-
const persisted = loadInteractiveBashSessionState(sessionID);
|
|
24988
|
-
const state2 = persisted ?? {
|
|
24989
|
-
sessionID,
|
|
24990
|
-
tmuxSessions: new Set,
|
|
24991
|
-
updatedAt: Date.now()
|
|
24992
|
-
};
|
|
24993
|
-
sessionStates.set(sessionID, state2);
|
|
24994
|
-
}
|
|
24995
|
-
return sessionStates.get(sessionID);
|
|
24996
|
-
}
|
|
24997
|
-
function isOmoSession(sessionName) {
|
|
24998
|
-
return sessionName !== null && sessionName.startsWith(OMO_SESSION_PREFIX);
|
|
25161
|
+
function getOrCreateStateLocal(sessionID) {
|
|
25162
|
+
return getOrCreateState(sessionID, sessionStates);
|
|
24999
25163
|
}
|
|
25000
|
-
async function
|
|
25001
|
-
|
|
25002
|
-
try {
|
|
25003
|
-
const proc = Bun.spawn(["tmux", "kill-session", "-t", sessionName], {
|
|
25004
|
-
stdout: "ignore",
|
|
25005
|
-
stderr: "ignore"
|
|
25006
|
-
});
|
|
25007
|
-
await proc.exited;
|
|
25008
|
-
} catch {}
|
|
25009
|
-
}
|
|
25164
|
+
async function killAllTrackedSessionsLocal(state2) {
|
|
25165
|
+
await killAllTrackedSessions(state2);
|
|
25010
25166
|
for (const sessionId of subagentSessions) {
|
|
25011
25167
|
ctx.client.session.abort({ path: { id: sessionId } }).catch(() => {});
|
|
25012
25168
|
}
|
|
@@ -25023,7 +25179,7 @@ function createInteractiveBashSessionHook(ctx) {
|
|
|
25023
25179
|
const tmuxCommand = args.tmux_command;
|
|
25024
25180
|
const tokens = tokenizeCommand(tmuxCommand);
|
|
25025
25181
|
const subCommand = findSubcommand(tokens);
|
|
25026
|
-
const state2 =
|
|
25182
|
+
const state2 = getOrCreateStateLocal(sessionID);
|
|
25027
25183
|
let stateChanged = false;
|
|
25028
25184
|
const toolOutput = output?.output ?? "";
|
|
25029
25185
|
if (toolOutput.startsWith("Error:")) {
|
|
@@ -25061,8 +25217,8 @@ function createInteractiveBashSessionHook(ctx) {
|
|
|
25061
25217
|
const sessionInfo = props?.info;
|
|
25062
25218
|
const sessionID = sessionInfo?.id;
|
|
25063
25219
|
if (sessionID) {
|
|
25064
|
-
const state2 =
|
|
25065
|
-
await
|
|
25220
|
+
const state2 = getOrCreateStateLocal(sessionID);
|
|
25221
|
+
await killAllTrackedSessionsLocal(state2);
|
|
25066
25222
|
sessionStates.delete(sessionID);
|
|
25067
25223
|
clearInteractiveBashSessionState(sessionID);
|
|
25068
25224
|
}
|
|
@@ -25210,7 +25366,7 @@ function buildReminderMessage(availableSkills) {
|
|
|
25210
25366
|
function createCategorySkillReminderHook(_ctx, availableSkills = []) {
|
|
25211
25367
|
const sessionStates = new Map;
|
|
25212
25368
|
const reminderMessage = buildReminderMessage(availableSkills);
|
|
25213
|
-
function
|
|
25369
|
+
function getOrCreateState2(sessionID) {
|
|
25214
25370
|
if (!sessionStates.has(sessionID)) {
|
|
25215
25371
|
sessionStates.set(sessionID, {
|
|
25216
25372
|
delegationUsed: false,
|
|
@@ -25233,7 +25389,7 @@ function createCategorySkillReminderHook(_ctx, availableSkills = []) {
|
|
|
25233
25389
|
if (!isTargetAgent(sessionID, input.agent)) {
|
|
25234
25390
|
return;
|
|
25235
25391
|
}
|
|
25236
|
-
const state2 =
|
|
25392
|
+
const state2 = getOrCreateState2(sessionID);
|
|
25237
25393
|
if (DELEGATION_TOOLS.has(toolLower)) {
|
|
25238
25394
|
state2.delegationUsed = true;
|
|
25239
25395
|
log("[category-skill-reminder] Delegation tool used", { sessionID, tool });
|
|
@@ -25404,7 +25560,7 @@ IMPORTANT:
|
|
|
25404
25560
|
|
|
25405
25561
|
Original task:
|
|
25406
25562
|
{{PROMPT}}`;
|
|
25407
|
-
var DEFAULT_API_TIMEOUT =
|
|
25563
|
+
var DEFAULT_API_TIMEOUT = 5000;
|
|
25408
25564
|
function createRalphLoopHook(ctx, options) {
|
|
25409
25565
|
const sessions = new Map;
|
|
25410
25566
|
const config = options?.config;
|
|
@@ -25412,6 +25568,21 @@ function createRalphLoopHook(ctx, options) {
|
|
|
25412
25568
|
const getTranscriptPath2 = options?.getTranscriptPath ?? getTranscriptPath;
|
|
25413
25569
|
const apiTimeout = options?.apiTimeout ?? DEFAULT_API_TIMEOUT;
|
|
25414
25570
|
const checkSessionExists = options?.checkSessionExists;
|
|
25571
|
+
async function withTimeout(promise, timeoutMs) {
|
|
25572
|
+
let timeoutId;
|
|
25573
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
25574
|
+
timeoutId = setTimeout(() => {
|
|
25575
|
+
reject(new Error("API timeout"));
|
|
25576
|
+
}, timeoutMs);
|
|
25577
|
+
});
|
|
25578
|
+
try {
|
|
25579
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
25580
|
+
} finally {
|
|
25581
|
+
if (timeoutId !== undefined) {
|
|
25582
|
+
clearTimeout(timeoutId);
|
|
25583
|
+
}
|
|
25584
|
+
}
|
|
25585
|
+
}
|
|
25415
25586
|
function getSessionState(sessionID) {
|
|
25416
25587
|
let state2 = sessions.get(sessionID);
|
|
25417
25588
|
if (!state2) {
|
|
@@ -25451,26 +25622,35 @@ function createRalphLoopHook(ctx, options) {
|
|
|
25451
25622
|
}
|
|
25452
25623
|
async function detectCompletionInSessionMessages(sessionID, promise) {
|
|
25453
25624
|
try {
|
|
25454
|
-
const response = await
|
|
25455
|
-
|
|
25456
|
-
|
|
25457
|
-
|
|
25458
|
-
}),
|
|
25459
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("API timeout")), apiTimeout))
|
|
25460
|
-
]);
|
|
25625
|
+
const response = await withTimeout(ctx.client.session.messages({
|
|
25626
|
+
path: { id: sessionID },
|
|
25627
|
+
query: { directory: ctx.directory }
|
|
25628
|
+
}), apiTimeout);
|
|
25461
25629
|
const messages = response.data ?? [];
|
|
25462
25630
|
if (!Array.isArray(messages))
|
|
25463
25631
|
return false;
|
|
25464
25632
|
const assistantMessages = messages.filter((msg) => msg.info?.role === "assistant");
|
|
25465
|
-
|
|
25466
|
-
if (!lastAssistant?.parts)
|
|
25633
|
+
if (assistantMessages.length === 0)
|
|
25467
25634
|
return false;
|
|
25468
25635
|
const pattern = new RegExp(`<promise>\\s*${escapeRegex(promise)}\\s*</promise>`, "is");
|
|
25469
|
-
const
|
|
25636
|
+
const recentAssistants = assistantMessages.slice(-3);
|
|
25637
|
+
for (const assistant of recentAssistants) {
|
|
25638
|
+
if (!assistant.parts)
|
|
25639
|
+
continue;
|
|
25640
|
+
const responseText = assistant.parts.filter((p) => p.type === "text" || p.type === "reasoning").map((p) => p.text ?? "").join(`
|
|
25470
25641
|
`);
|
|
25471
|
-
|
|
25642
|
+
if (pattern.test(responseText)) {
|
|
25643
|
+
return true;
|
|
25644
|
+
}
|
|
25645
|
+
}
|
|
25646
|
+
return false;
|
|
25472
25647
|
} catch (err) {
|
|
25473
|
-
|
|
25648
|
+
setTimeout(() => {
|
|
25649
|
+
log(`[${HOOK_NAME3}] Session messages check failed`, {
|
|
25650
|
+
sessionID,
|
|
25651
|
+
error: String(err)
|
|
25652
|
+
});
|
|
25653
|
+
}, 0);
|
|
25474
25654
|
return false;
|
|
25475
25655
|
}
|
|
25476
25656
|
}
|
|
@@ -25611,7 +25791,7 @@ function createRalphLoopHook(ctx, options) {
|
|
|
25611
25791
|
let agent;
|
|
25612
25792
|
let model;
|
|
25613
25793
|
try {
|
|
25614
|
-
const messagesResp = await ctx.client.session.messages({ path: { id: sessionID } });
|
|
25794
|
+
const messagesResp = await withTimeout(ctx.client.session.messages({ path: { id: sessionID } }), apiTimeout);
|
|
25615
25795
|
const messages = messagesResp.data ?? [];
|
|
25616
25796
|
for (let i2 = messages.length - 1;i2 >= 0; i2--) {
|
|
25617
25797
|
const info = messages[i2].info;
|
|
@@ -26817,6 +26997,185 @@ Reading plan and beginning execution...
|
|
|
26817
26997
|
- Read the FULL plan file before delegating any tasks
|
|
26818
26998
|
- Follow atlas delegation protocols (7-section format)`;
|
|
26819
26999
|
|
|
27000
|
+
// src/features/builtin-commands/templates/handoff.ts
|
|
27001
|
+
var HANDOFF_TEMPLATE = `# Handoff Command
|
|
27002
|
+
|
|
27003
|
+
## Purpose
|
|
27004
|
+
|
|
27005
|
+
Use /handoff when:
|
|
27006
|
+
- The current session context is getting too long and quality is degrading
|
|
27007
|
+
- You want to start fresh while preserving essential context from this session
|
|
27008
|
+
- The context window is approaching capacity
|
|
27009
|
+
|
|
27010
|
+
This creates a detailed context summary that can be used to continue work in a new session.
|
|
27011
|
+
|
|
27012
|
+
---
|
|
27013
|
+
|
|
27014
|
+
# PHASE 0: VALIDATE REQUEST
|
|
27015
|
+
|
|
27016
|
+
Before proceeding, confirm:
|
|
27017
|
+
- [ ] There is meaningful work or context in this session to preserve
|
|
27018
|
+
- [ ] The user wants to create a handoff summary (not just asking about it)
|
|
27019
|
+
|
|
27020
|
+
If the session is nearly empty or has no meaningful context, inform the user there is nothing substantial to hand off.
|
|
27021
|
+
|
|
27022
|
+
---
|
|
27023
|
+
|
|
27024
|
+
# PHASE 1: GATHER PROGRAMMATIC CONTEXT
|
|
27025
|
+
|
|
27026
|
+
Execute these tools to gather concrete data:
|
|
27027
|
+
|
|
27028
|
+
1. session_read({ session_id: "$SESSION_ID" }) \u2014 full session history
|
|
27029
|
+
2. todoread() \u2014 current task progress
|
|
27030
|
+
3. Bash({ command: "git diff --stat HEAD~10..HEAD" }) \u2014 recent file changes
|
|
27031
|
+
4. Bash({ command: "git status --porcelain" }) \u2014 uncommitted changes
|
|
27032
|
+
|
|
27033
|
+
Suggested execution order:
|
|
27034
|
+
|
|
27035
|
+
\`\`\`
|
|
27036
|
+
session_read({ session_id: "$SESSION_ID" })
|
|
27037
|
+
todoread()
|
|
27038
|
+
Bash({ command: "git diff --stat HEAD~10..HEAD" })
|
|
27039
|
+
Bash({ command: "git status --porcelain" })
|
|
27040
|
+
\`\`\`
|
|
27041
|
+
|
|
27042
|
+
Analyze the gathered outputs to understand:
|
|
27043
|
+
- What the user asked for (exact wording)
|
|
27044
|
+
- What work was completed
|
|
27045
|
+
- What tasks remain incomplete (include todo state)
|
|
27046
|
+
- What decisions were made
|
|
27047
|
+
- What files were modified or discussed (include git diff/stat + status)
|
|
27048
|
+
- What patterns, constraints, or preferences were established
|
|
27049
|
+
|
|
27050
|
+
---
|
|
27051
|
+
|
|
27052
|
+
# PHASE 2: EXTRACT CONTEXT
|
|
27053
|
+
|
|
27054
|
+
Write the context summary from first person perspective ("I did...", "I told you...").
|
|
27055
|
+
|
|
27056
|
+
Focus on:
|
|
27057
|
+
- Capabilities and behavior, not file-by-file implementation details
|
|
27058
|
+
- What matters for continuing the work
|
|
27059
|
+
- Avoiding excessive implementation details (variable names, storage keys, constants) unless critical
|
|
27060
|
+
- USER REQUESTS (AS-IS) must be verbatim (do not paraphrase)
|
|
27061
|
+
- EXPLICIT CONSTRAINTS must be verbatim only (do not invent)
|
|
27062
|
+
|
|
27063
|
+
Questions to consider when extracting:
|
|
27064
|
+
- What did I just do or implement?
|
|
27065
|
+
- What instructions did I already give which are still relevant (e.g. follow patterns in the codebase)?
|
|
27066
|
+
- What files did I tell you are important or that I am working on?
|
|
27067
|
+
- Did I provide a plan or spec that should be included?
|
|
27068
|
+
- What did I already tell you that is important (libraries, patterns, constraints, preferences)?
|
|
27069
|
+
- What important technical details did I discover (APIs, methods, patterns)?
|
|
27070
|
+
- What caveats, limitations, or open questions did I find?
|
|
27071
|
+
|
|
27072
|
+
---
|
|
27073
|
+
|
|
27074
|
+
# PHASE 3: FORMAT OUTPUT
|
|
27075
|
+
|
|
27076
|
+
Generate a handoff summary using this exact format:
|
|
27077
|
+
|
|
27078
|
+
\`\`\`
|
|
27079
|
+
HANDOFF CONTEXT
|
|
27080
|
+
===============
|
|
27081
|
+
|
|
27082
|
+
USER REQUESTS (AS-IS)
|
|
27083
|
+
---------------------
|
|
27084
|
+
- [Exact verbatim user requests - NOT paraphrased]
|
|
27085
|
+
|
|
27086
|
+
GOAL
|
|
27087
|
+
----
|
|
27088
|
+
[One sentence describing what should be done next]
|
|
27089
|
+
|
|
27090
|
+
WORK COMPLETED
|
|
27091
|
+
--------------
|
|
27092
|
+
- [First person bullet points of what was done]
|
|
27093
|
+
- [Include specific file paths when relevant]
|
|
27094
|
+
- [Note key implementation decisions]
|
|
27095
|
+
|
|
27096
|
+
CURRENT STATE
|
|
27097
|
+
-------------
|
|
27098
|
+
- [Current state of the codebase or task]
|
|
27099
|
+
- [Build/test status if applicable]
|
|
27100
|
+
- [Any environment or configuration state]
|
|
27101
|
+
|
|
27102
|
+
PENDING TASKS
|
|
27103
|
+
-------------
|
|
27104
|
+
- [Tasks that were planned but not completed]
|
|
27105
|
+
- [Next logical steps to take]
|
|
27106
|
+
- [Any blockers or issues encountered]
|
|
27107
|
+
- [Include current todo state from todoread()]
|
|
27108
|
+
|
|
27109
|
+
KEY FILES
|
|
27110
|
+
---------
|
|
27111
|
+
- [path/to/file1] - [brief role description]
|
|
27112
|
+
- [path/to/file2] - [brief role description]
|
|
27113
|
+
(Maximum 10 files, prioritized by importance)
|
|
27114
|
+
- (Include files from git diff/stat and git status)
|
|
27115
|
+
|
|
27116
|
+
IMPORTANT DECISIONS
|
|
27117
|
+
-------------------
|
|
27118
|
+
- [Technical decisions that were made and why]
|
|
27119
|
+
- [Trade-offs that were considered]
|
|
27120
|
+
- [Patterns or conventions established]
|
|
27121
|
+
|
|
27122
|
+
EXPLICIT CONSTRAINTS
|
|
27123
|
+
--------------------
|
|
27124
|
+
- [Verbatim constraints only - from user or existing AGENTS.md]
|
|
27125
|
+
- If none, write: None
|
|
27126
|
+
|
|
27127
|
+
CONTEXT FOR CONTINUATION
|
|
27128
|
+
------------------------
|
|
27129
|
+
- [What the next session needs to know to continue]
|
|
27130
|
+
- [Warnings or gotchas to be aware of]
|
|
27131
|
+
- [References to documentation if relevant]
|
|
27132
|
+
\`\`\`
|
|
27133
|
+
|
|
27134
|
+
Rules for the summary:
|
|
27135
|
+
- Plain text with bullets
|
|
27136
|
+
- No markdown headers with # (use the format above with dashes)
|
|
27137
|
+
- No bold, italic, or code fences within content
|
|
27138
|
+
- Use workspace-relative paths for files
|
|
27139
|
+
- Keep it focused - only include what matters for continuation
|
|
27140
|
+
- Pick an appropriate length based on complexity
|
|
27141
|
+
- USER REQUESTS (AS-IS) and EXPLICIT CONSTRAINTS must be verbatim only
|
|
27142
|
+
|
|
27143
|
+
---
|
|
27144
|
+
|
|
27145
|
+
# PHASE 4: PROVIDE INSTRUCTIONS
|
|
27146
|
+
|
|
27147
|
+
After generating the summary, instruct the user:
|
|
27148
|
+
|
|
27149
|
+
\`\`\`
|
|
27150
|
+
---
|
|
27151
|
+
|
|
27152
|
+
TO CONTINUE IN A NEW SESSION:
|
|
27153
|
+
|
|
27154
|
+
1. Press 'n' in OpenCode TUI to open a new session, or run 'opencode' in a new terminal
|
|
27155
|
+
2. Paste the HANDOFF CONTEXT above as your first message
|
|
27156
|
+
3. Add your request: "Continue from the handoff context above. [Your next task]"
|
|
27157
|
+
|
|
27158
|
+
The new session will have all context needed to continue seamlessly.
|
|
27159
|
+
\`\`\`
|
|
27160
|
+
|
|
27161
|
+
---
|
|
27162
|
+
|
|
27163
|
+
# IMPORTANT CONSTRAINTS
|
|
27164
|
+
|
|
27165
|
+
- DO NOT attempt to programmatically create new sessions (no API available to agents)
|
|
27166
|
+
- DO provide a self-contained summary that works without access to this session
|
|
27167
|
+
- DO include workspace-relative file paths
|
|
27168
|
+
- DO NOT include sensitive information (API keys, credentials, secrets)
|
|
27169
|
+
- DO NOT exceed 10 files in the KEY FILES section
|
|
27170
|
+
- DO keep the GOAL section to a single sentence or short paragraph
|
|
27171
|
+
|
|
27172
|
+
---
|
|
27173
|
+
|
|
27174
|
+
# EXECUTE NOW
|
|
27175
|
+
|
|
27176
|
+
Begin by gathering programmatic context, then synthesize the handoff summary.
|
|
27177
|
+
`;
|
|
27178
|
+
|
|
26820
27179
|
// src/features/builtin-commands/commands.ts
|
|
26821
27180
|
var BUILTIN_COMMAND_DEFINITIONS = {
|
|
26822
27181
|
"init-deep": {
|
|
@@ -26887,6 +27246,22 @@ $ARGUMENTS
|
|
|
26887
27246
|
template: `<command-instruction>
|
|
26888
27247
|
${STOP_CONTINUATION_TEMPLATE}
|
|
26889
27248
|
</command-instruction>`
|
|
27249
|
+
},
|
|
27250
|
+
handoff: {
|
|
27251
|
+
description: "(builtin) Create a detailed context summary for continuing work in a new session",
|
|
27252
|
+
template: `<command-instruction>
|
|
27253
|
+
${HANDOFF_TEMPLATE}
|
|
27254
|
+
</command-instruction>
|
|
27255
|
+
|
|
27256
|
+
<session-context>
|
|
27257
|
+
Session ID: $SESSION_ID
|
|
27258
|
+
Timestamp: $TIMESTAMP
|
|
27259
|
+
</session-context>
|
|
27260
|
+
|
|
27261
|
+
<user-request>
|
|
27262
|
+
$ARGUMENTS
|
|
27263
|
+
</user-request>`,
|
|
27264
|
+
argumentHint: "[goal]"
|
|
26890
27265
|
}
|
|
26891
27266
|
};
|
|
26892
27267
|
function loadBuiltinCommands(disabledCommands) {
|
|
@@ -30128,11 +30503,11 @@ ${contextInfo}`;
|
|
|
30128
30503
|
};
|
|
30129
30504
|
}
|
|
30130
30505
|
// src/hooks/atlas/index.ts
|
|
30131
|
-
import { execSync as execSync2 } from "child_process";
|
|
30132
30506
|
init_hook_message_injector();
|
|
30133
30507
|
init_logger();
|
|
30134
30508
|
init_system_directive();
|
|
30135
30509
|
init_session_utils();
|
|
30510
|
+
init_git_worktree();
|
|
30136
30511
|
var HOOK_NAME7 = "atlas";
|
|
30137
30512
|
function isSisyphusPath(filePath) {
|
|
30138
30513
|
return /\.sisyphus[/\\]/.test(filePath);
|
|
@@ -30372,99 +30747,6 @@ function extractSessionIdFromOutput(output) {
|
|
|
30372
30747
|
const match = output.match(/Session ID:\s*(ses_[a-zA-Z0-9]+)/);
|
|
30373
30748
|
return match?.[1] ?? "<session_id>";
|
|
30374
30749
|
}
|
|
30375
|
-
function getGitDiffStats(directory) {
|
|
30376
|
-
try {
|
|
30377
|
-
const output = execSync2("git diff --numstat HEAD", {
|
|
30378
|
-
cwd: directory,
|
|
30379
|
-
encoding: "utf-8",
|
|
30380
|
-
timeout: 5000,
|
|
30381
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
30382
|
-
}).trim();
|
|
30383
|
-
if (!output)
|
|
30384
|
-
return [];
|
|
30385
|
-
const statusOutput = execSync2("git status --porcelain", {
|
|
30386
|
-
cwd: directory,
|
|
30387
|
-
encoding: "utf-8",
|
|
30388
|
-
timeout: 5000,
|
|
30389
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
30390
|
-
}).trim();
|
|
30391
|
-
const statusMap = new Map;
|
|
30392
|
-
for (const line of statusOutput.split(`
|
|
30393
|
-
`)) {
|
|
30394
|
-
if (!line)
|
|
30395
|
-
continue;
|
|
30396
|
-
const status = line.substring(0, 2).trim();
|
|
30397
|
-
const filePath = line.substring(3);
|
|
30398
|
-
if (status === "A" || status === "??") {
|
|
30399
|
-
statusMap.set(filePath, "added");
|
|
30400
|
-
} else if (status === "D") {
|
|
30401
|
-
statusMap.set(filePath, "deleted");
|
|
30402
|
-
} else {
|
|
30403
|
-
statusMap.set(filePath, "modified");
|
|
30404
|
-
}
|
|
30405
|
-
}
|
|
30406
|
-
const stats = [];
|
|
30407
|
-
for (const line of output.split(`
|
|
30408
|
-
`)) {
|
|
30409
|
-
const parts = line.split("\t");
|
|
30410
|
-
if (parts.length < 3)
|
|
30411
|
-
continue;
|
|
30412
|
-
const [addedStr, removedStr, path8] = parts;
|
|
30413
|
-
const added = addedStr === "-" ? 0 : parseInt(addedStr, 10);
|
|
30414
|
-
const removed = removedStr === "-" ? 0 : parseInt(removedStr, 10);
|
|
30415
|
-
stats.push({
|
|
30416
|
-
path: path8,
|
|
30417
|
-
added,
|
|
30418
|
-
removed,
|
|
30419
|
-
status: statusMap.get(path8) ?? "modified"
|
|
30420
|
-
});
|
|
30421
|
-
}
|
|
30422
|
-
return stats;
|
|
30423
|
-
} catch {
|
|
30424
|
-
return [];
|
|
30425
|
-
}
|
|
30426
|
-
}
|
|
30427
|
-
function formatFileChanges(stats, notepadPath) {
|
|
30428
|
-
if (stats.length === 0)
|
|
30429
|
-
return `[FILE CHANGES SUMMARY]
|
|
30430
|
-
No file changes detected.
|
|
30431
|
-
`;
|
|
30432
|
-
const modified = stats.filter((s) => s.status === "modified");
|
|
30433
|
-
const added = stats.filter((s) => s.status === "added");
|
|
30434
|
-
const deleted = stats.filter((s) => s.status === "deleted");
|
|
30435
|
-
const lines = ["[FILE CHANGES SUMMARY]"];
|
|
30436
|
-
if (modified.length > 0) {
|
|
30437
|
-
lines.push("Modified files:");
|
|
30438
|
-
for (const f of modified) {
|
|
30439
|
-
lines.push(` ${f.path} (+${f.added}, -${f.removed})`);
|
|
30440
|
-
}
|
|
30441
|
-
lines.push("");
|
|
30442
|
-
}
|
|
30443
|
-
if (added.length > 0) {
|
|
30444
|
-
lines.push("Created files:");
|
|
30445
|
-
for (const f of added) {
|
|
30446
|
-
lines.push(` ${f.path} (+${f.added})`);
|
|
30447
|
-
}
|
|
30448
|
-
lines.push("");
|
|
30449
|
-
}
|
|
30450
|
-
if (deleted.length > 0) {
|
|
30451
|
-
lines.push("Deleted files:");
|
|
30452
|
-
for (const f of deleted) {
|
|
30453
|
-
lines.push(` ${f.path} (-${f.removed})`);
|
|
30454
|
-
}
|
|
30455
|
-
lines.push("");
|
|
30456
|
-
}
|
|
30457
|
-
if (notepadPath) {
|
|
30458
|
-
const notepadStat = stats.find((s) => s.path.includes("notepad") || s.path.includes(".sisyphus"));
|
|
30459
|
-
if (notepadStat) {
|
|
30460
|
-
lines.push("[NOTEPAD UPDATED]");
|
|
30461
|
-
lines.push(` ${notepadStat.path} (+${notepadStat.added})`);
|
|
30462
|
-
lines.push("");
|
|
30463
|
-
}
|
|
30464
|
-
}
|
|
30465
|
-
return lines.join(`
|
|
30466
|
-
`);
|
|
30467
|
-
}
|
|
30468
30750
|
var CONTINUATION_COOLDOWN_MS = 5000;
|
|
30469
30751
|
function isAbortError(error) {
|
|
30470
30752
|
if (!error)
|
|
@@ -30744,7 +31026,7 @@ function createAtlasHook(ctx, options) {
|
|
|
30744
31026
|
return;
|
|
30745
31027
|
}
|
|
30746
31028
|
if (output.output && typeof output.output === "string") {
|
|
30747
|
-
const gitStats =
|
|
31029
|
+
const gitStats = collectGitDiffStats(ctx.directory);
|
|
30748
31030
|
const fileChanges = formatFileChanges(gitStats);
|
|
30749
31031
|
const subagentSessionId = extractSessionIdFromOutput(output.output);
|
|
30750
31032
|
const boulderState = readBoulderState(ctx.directory);
|
|
@@ -32577,6 +32859,7 @@ class LSPServerManager {
|
|
|
32577
32859
|
clients = new Map;
|
|
32578
32860
|
cleanupInterval = null;
|
|
32579
32861
|
IDLE_TIMEOUT = 5 * 60 * 1000;
|
|
32862
|
+
INIT_TIMEOUT = 60 * 1000;
|
|
32580
32863
|
constructor() {
|
|
32581
32864
|
this.startCleanupTimer();
|
|
32582
32865
|
this.registerProcessCleanup();
|
|
@@ -32641,35 +32924,68 @@ class LSPServerManager {
|
|
|
32641
32924
|
async getClient(root, server) {
|
|
32642
32925
|
const key = this.getKey(root, server.id);
|
|
32643
32926
|
let managed = this.clients.get(key);
|
|
32927
|
+
if (managed) {
|
|
32928
|
+
const now = Date.now();
|
|
32929
|
+
if (managed.isInitializing && managed.initializingSince !== undefined && now - managed.initializingSince >= this.INIT_TIMEOUT) {
|
|
32930
|
+
try {
|
|
32931
|
+
await managed.client.stop();
|
|
32932
|
+
} catch {}
|
|
32933
|
+
this.clients.delete(key);
|
|
32934
|
+
managed = undefined;
|
|
32935
|
+
}
|
|
32936
|
+
}
|
|
32644
32937
|
if (managed) {
|
|
32645
32938
|
if (managed.initPromise) {
|
|
32646
|
-
|
|
32939
|
+
try {
|
|
32940
|
+
await managed.initPromise;
|
|
32941
|
+
} catch {
|
|
32942
|
+
try {
|
|
32943
|
+
await managed.client.stop();
|
|
32944
|
+
} catch {}
|
|
32945
|
+
this.clients.delete(key);
|
|
32946
|
+
managed = undefined;
|
|
32947
|
+
}
|
|
32647
32948
|
}
|
|
32648
|
-
if (managed
|
|
32649
|
-
managed.
|
|
32650
|
-
|
|
32651
|
-
|
|
32949
|
+
if (managed) {
|
|
32950
|
+
if (managed.client.isAlive()) {
|
|
32951
|
+
managed.refCount++;
|
|
32952
|
+
managed.lastUsedAt = Date.now();
|
|
32953
|
+
return managed.client;
|
|
32954
|
+
}
|
|
32955
|
+
try {
|
|
32956
|
+
await managed.client.stop();
|
|
32957
|
+
} catch {}
|
|
32958
|
+
this.clients.delete(key);
|
|
32652
32959
|
}
|
|
32653
|
-
await managed.client.stop();
|
|
32654
|
-
this.clients.delete(key);
|
|
32655
32960
|
}
|
|
32656
32961
|
const client = new LSPClient(root, server);
|
|
32657
32962
|
const initPromise3 = (async () => {
|
|
32658
32963
|
await client.start();
|
|
32659
32964
|
await client.initialize();
|
|
32660
32965
|
})();
|
|
32966
|
+
const initStartedAt = Date.now();
|
|
32661
32967
|
this.clients.set(key, {
|
|
32662
32968
|
client,
|
|
32663
|
-
lastUsedAt:
|
|
32969
|
+
lastUsedAt: initStartedAt,
|
|
32664
32970
|
refCount: 1,
|
|
32665
32971
|
initPromise: initPromise3,
|
|
32666
|
-
isInitializing: true
|
|
32972
|
+
isInitializing: true,
|
|
32973
|
+
initializingSince: initStartedAt
|
|
32667
32974
|
});
|
|
32668
|
-
|
|
32975
|
+
try {
|
|
32976
|
+
await initPromise3;
|
|
32977
|
+
} catch (error) {
|
|
32978
|
+
this.clients.delete(key);
|
|
32979
|
+
try {
|
|
32980
|
+
await client.stop();
|
|
32981
|
+
} catch {}
|
|
32982
|
+
throw error;
|
|
32983
|
+
}
|
|
32669
32984
|
const m = this.clients.get(key);
|
|
32670
32985
|
if (m) {
|
|
32671
32986
|
m.initPromise = undefined;
|
|
32672
32987
|
m.isInitializing = false;
|
|
32988
|
+
m.initializingSince = undefined;
|
|
32673
32989
|
}
|
|
32674
32990
|
return client;
|
|
32675
32991
|
}
|
|
@@ -32682,19 +32998,25 @@ class LSPServerManager {
|
|
|
32682
32998
|
await client.start();
|
|
32683
32999
|
await client.initialize();
|
|
32684
33000
|
})();
|
|
33001
|
+
const initStartedAt = Date.now();
|
|
32685
33002
|
this.clients.set(key, {
|
|
32686
33003
|
client,
|
|
32687
|
-
lastUsedAt:
|
|
33004
|
+
lastUsedAt: initStartedAt,
|
|
32688
33005
|
refCount: 0,
|
|
32689
33006
|
initPromise: initPromise3,
|
|
32690
|
-
isInitializing: true
|
|
33007
|
+
isInitializing: true,
|
|
33008
|
+
initializingSince: initStartedAt
|
|
32691
33009
|
});
|
|
32692
33010
|
initPromise3.then(() => {
|
|
32693
33011
|
const m = this.clients.get(key);
|
|
32694
33012
|
if (m) {
|
|
32695
33013
|
m.initPromise = undefined;
|
|
32696
33014
|
m.isInitializing = false;
|
|
33015
|
+
m.initializingSince = undefined;
|
|
32697
33016
|
}
|
|
33017
|
+
}).catch(() => {
|
|
33018
|
+
this.clients.delete(key);
|
|
33019
|
+
client.stop().catch(() => {});
|
|
32698
33020
|
});
|
|
32699
33021
|
}
|
|
32700
33022
|
releaseClient(root, serverId) {
|
|
@@ -43454,10 +43776,10 @@ function _property(property, schema2, params) {
|
|
|
43454
43776
|
...normalizeParams(params)
|
|
43455
43777
|
});
|
|
43456
43778
|
}
|
|
43457
|
-
function _mime(
|
|
43779
|
+
function _mime(types14, params) {
|
|
43458
43780
|
return new $ZodCheckMimeType({
|
|
43459
43781
|
check: "mime_type",
|
|
43460
|
-
mime:
|
|
43782
|
+
mime: types14,
|
|
43461
43783
|
...normalizeParams(params)
|
|
43462
43784
|
});
|
|
43463
43785
|
}
|
|
@@ -45367,7 +45689,7 @@ var ZodFile = /* @__PURE__ */ $constructor("ZodFile", (inst, def) => {
|
|
|
45367
45689
|
ZodType.init(inst, def);
|
|
45368
45690
|
inst.min = (size, params) => inst.check(_minSize(size, params));
|
|
45369
45691
|
inst.max = (size, params) => inst.check(_maxSize(size, params));
|
|
45370
|
-
inst.mime = (
|
|
45692
|
+
inst.mime = (types14, params) => inst.check(_mime(Array.isArray(types14) ? types14 : [types14], params));
|
|
45371
45693
|
});
|
|
45372
45694
|
function file(params) {
|
|
45373
45695
|
return _file(ZodFile, params);
|
|
@@ -48280,10 +48602,9 @@ function createSkillMcpTool(options) {
|
|
|
48280
48602
|
var BACKGROUND_OUTPUT_DESCRIPTION = `Get output from background task. Use full_session=true to fetch session messages with filters. System notifies on completion, so block=true rarely needed.`;
|
|
48281
48603
|
var BACKGROUND_CANCEL_DESCRIPTION = `Cancel running background task(s). Use all=true to cancel ALL before final answer.`;
|
|
48282
48604
|
|
|
48283
|
-
// src/tools/background-task/
|
|
48605
|
+
// src/tools/background-task/modules/background-task.ts
|
|
48284
48606
|
init_hook_message_injector();
|
|
48285
48607
|
init_logger();
|
|
48286
|
-
init_session_cursor();
|
|
48287
48608
|
|
|
48288
48609
|
// src/features/tool-metadata-store/index.ts
|
|
48289
48610
|
var pendingStore = new Map;
|
|
@@ -48314,9 +48635,8 @@ function consumeToolMetadata(sessionID, callID) {
|
|
|
48314
48635
|
return;
|
|
48315
48636
|
}
|
|
48316
48637
|
|
|
48317
|
-
// src/tools/background-task/
|
|
48318
|
-
|
|
48319
|
-
var THINKING_MAX_CHARS = 2000;
|
|
48638
|
+
// src/tools/background-task/modules/utils.ts
|
|
48639
|
+
init_hook_message_injector();
|
|
48320
48640
|
function formatDuration(start, end) {
|
|
48321
48641
|
const duration3 = (end ?? new Date).getTime() - start.getTime();
|
|
48322
48642
|
const seconds = Math.floor(duration3 / 1000);
|
|
@@ -48330,14 +48650,70 @@ function formatDuration(start, end) {
|
|
|
48330
48650
|
return `${seconds}s`;
|
|
48331
48651
|
}
|
|
48332
48652
|
}
|
|
48333
|
-
function delay(ms) {
|
|
48334
|
-
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
48335
|
-
}
|
|
48336
48653
|
function truncateText(text, maxLength) {
|
|
48337
48654
|
if (text.length <= maxLength)
|
|
48338
48655
|
return text;
|
|
48339
48656
|
return text.slice(0, maxLength) + "...";
|
|
48340
48657
|
}
|
|
48658
|
+
function delay(ms) {
|
|
48659
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
48660
|
+
}
|
|
48661
|
+
function formatMessageTime(value) {
|
|
48662
|
+
if (typeof value === "string") {
|
|
48663
|
+
const date5 = new Date(value);
|
|
48664
|
+
return Number.isNaN(date5.getTime()) ? value : date5.toISOString();
|
|
48665
|
+
}
|
|
48666
|
+
if (typeof value === "object" && value !== null) {
|
|
48667
|
+
if ("created" in value) {
|
|
48668
|
+
const created = value.created;
|
|
48669
|
+
if (typeof created === "number") {
|
|
48670
|
+
return new Date(created).toISOString();
|
|
48671
|
+
}
|
|
48672
|
+
}
|
|
48673
|
+
}
|
|
48674
|
+
return "Unknown time";
|
|
48675
|
+
}
|
|
48676
|
+
// src/tools/background-task/modules/message-processing.ts
|
|
48677
|
+
function getErrorMessage2(value) {
|
|
48678
|
+
if (Array.isArray(value))
|
|
48679
|
+
return null;
|
|
48680
|
+
if (value.error === undefined || value.error === null)
|
|
48681
|
+
return null;
|
|
48682
|
+
if (typeof value.error === "string" && value.error.length > 0)
|
|
48683
|
+
return value.error;
|
|
48684
|
+
return String(value.error);
|
|
48685
|
+
}
|
|
48686
|
+
function isSessionMessage(value) {
|
|
48687
|
+
return typeof value === "object" && value !== null;
|
|
48688
|
+
}
|
|
48689
|
+
function extractMessages2(value) {
|
|
48690
|
+
if (Array.isArray(value)) {
|
|
48691
|
+
return value.filter(isSessionMessage);
|
|
48692
|
+
}
|
|
48693
|
+
if (Array.isArray(value.data)) {
|
|
48694
|
+
return value.data.filter(isSessionMessage);
|
|
48695
|
+
}
|
|
48696
|
+
return [];
|
|
48697
|
+
}
|
|
48698
|
+
function extractToolResultText(part) {
|
|
48699
|
+
if (typeof part.content === "string" && part.content.length > 0) {
|
|
48700
|
+
return [part.content];
|
|
48701
|
+
}
|
|
48702
|
+
if (Array.isArray(part.content)) {
|
|
48703
|
+
const blocks = part.content.filter((block) => (block.type === "text" || block.type === "reasoning") && block.text).map((block) => block.text);
|
|
48704
|
+
if (blocks.length > 0)
|
|
48705
|
+
return blocks;
|
|
48706
|
+
}
|
|
48707
|
+
if (part.output && part.output.length > 0) {
|
|
48708
|
+
return [part.output];
|
|
48709
|
+
}
|
|
48710
|
+
return [];
|
|
48711
|
+
}
|
|
48712
|
+
|
|
48713
|
+
// src/tools/background-task/modules/formatters.ts
|
|
48714
|
+
init_session_cursor();
|
|
48715
|
+
var MAX_MESSAGE_LIMIT = 100;
|
|
48716
|
+
var THINKING_MAX_CHARS = 2000;
|
|
48341
48717
|
function formatTaskStatus(task) {
|
|
48342
48718
|
let duration3;
|
|
48343
48719
|
if (task.status === "pending" && task.queuedAt) {
|
|
@@ -48397,27 +48773,6 @@ ${statusNote}
|
|
|
48397
48773
|
${promptPreview}
|
|
48398
48774
|
\`\`\`${lastMessageSection}`;
|
|
48399
48775
|
}
|
|
48400
|
-
function getErrorMessage2(value) {
|
|
48401
|
-
if (Array.isArray(value))
|
|
48402
|
-
return null;
|
|
48403
|
-
if (value.error === undefined || value.error === null)
|
|
48404
|
-
return null;
|
|
48405
|
-
if (typeof value.error === "string" && value.error.length > 0)
|
|
48406
|
-
return value.error;
|
|
48407
|
-
return String(value.error);
|
|
48408
|
-
}
|
|
48409
|
-
function isSessionMessage(value) {
|
|
48410
|
-
return typeof value === "object" && value !== null;
|
|
48411
|
-
}
|
|
48412
|
-
function extractMessages2(value) {
|
|
48413
|
-
if (Array.isArray(value)) {
|
|
48414
|
-
return value.filter(isSessionMessage);
|
|
48415
|
-
}
|
|
48416
|
-
if (Array.isArray(value.data)) {
|
|
48417
|
-
return value.data.filter(isSessionMessage);
|
|
48418
|
-
}
|
|
48419
|
-
return [];
|
|
48420
|
-
}
|
|
48421
48776
|
async function formatTaskResult(task, client2) {
|
|
48422
48777
|
if (!task.sessionID) {
|
|
48423
48778
|
return `Error: Task has no sessionID`;
|
|
@@ -48508,20 +48863,6 @@ Session ID: ${task.sessionID}
|
|
|
48508
48863
|
|
|
48509
48864
|
${textContent || "(No text output)"}`;
|
|
48510
48865
|
}
|
|
48511
|
-
function extractToolResultText(part) {
|
|
48512
|
-
if (typeof part.content === "string" && part.content.length > 0) {
|
|
48513
|
-
return [part.content];
|
|
48514
|
-
}
|
|
48515
|
-
if (Array.isArray(part.content)) {
|
|
48516
|
-
const blocks = part.content.filter((block) => (block.type === "text" || block.type === "reasoning") && block.text).map((block) => block.text);
|
|
48517
|
-
if (blocks.length > 0)
|
|
48518
|
-
return blocks;
|
|
48519
|
-
}
|
|
48520
|
-
if (part.output && part.output.length > 0) {
|
|
48521
|
-
return [part.output];
|
|
48522
|
-
}
|
|
48523
|
-
return [];
|
|
48524
|
-
}
|
|
48525
48866
|
async function formatFullSession(task, client2, options) {
|
|
48526
48867
|
if (!task.sessionID) {
|
|
48527
48868
|
return formatTaskStatus(task);
|
|
@@ -48615,6 +48956,25 @@ async function formatFullSession(task, client2, options) {
|
|
|
48615
48956
|
return lines.join(`
|
|
48616
48957
|
`);
|
|
48617
48958
|
}
|
|
48959
|
+
|
|
48960
|
+
// src/tools/background-task/modules/background-output.ts
|
|
48961
|
+
var SISYPHUS_JUNIOR_AGENT = "sisyphus-junior";
|
|
48962
|
+
function resolveToolCallID(ctx) {
|
|
48963
|
+
if (typeof ctx.callID === "string" && ctx.callID.trim() !== "") {
|
|
48964
|
+
return ctx.callID;
|
|
48965
|
+
}
|
|
48966
|
+
if (typeof ctx.callId === "string" && ctx.callId.trim() !== "") {
|
|
48967
|
+
return ctx.callId;
|
|
48968
|
+
}
|
|
48969
|
+
if (typeof ctx.call_id === "string" && ctx.call_id.trim() !== "") {
|
|
48970
|
+
return ctx.call_id;
|
|
48971
|
+
}
|
|
48972
|
+
return;
|
|
48973
|
+
}
|
|
48974
|
+
function formatResolvedTitle(task) {
|
|
48975
|
+
const label = task.agent === SISYPHUS_JUNIOR_AGENT && task.category ? task.category : task.agent;
|
|
48976
|
+
return `${label} - ${task.description}`;
|
|
48977
|
+
}
|
|
48618
48978
|
function createBackgroundOutput(manager, client2) {
|
|
48619
48979
|
return tool({
|
|
48620
48980
|
description: BACKGROUND_OUTPUT_DESCRIPTION,
|
|
@@ -48629,12 +48989,29 @@ function createBackgroundOutput(manager, client2) {
|
|
|
48629
48989
|
include_tool_results: tool.schema.boolean().optional().describe("Include tool results in full_session output (default: false)"),
|
|
48630
48990
|
thinking_max_chars: tool.schema.number().optional().describe("Max characters for thinking content (default: 2000)")
|
|
48631
48991
|
},
|
|
48632
|
-
async execute(args) {
|
|
48992
|
+
async execute(args, toolContext) {
|
|
48633
48993
|
try {
|
|
48994
|
+
const ctx = toolContext;
|
|
48634
48995
|
const task = manager.getTask(args.task_id);
|
|
48635
48996
|
if (!task) {
|
|
48636
48997
|
return `Task not found: ${args.task_id}`;
|
|
48637
48998
|
}
|
|
48999
|
+
const resolvedTitle = formatResolvedTitle(task);
|
|
49000
|
+
const meta = {
|
|
49001
|
+
title: resolvedTitle,
|
|
49002
|
+
metadata: {
|
|
49003
|
+
task_id: task.id,
|
|
49004
|
+
agent: task.agent,
|
|
49005
|
+
category: task.category,
|
|
49006
|
+
description: task.description,
|
|
49007
|
+
sessionId: task.sessionID ?? "pending"
|
|
49008
|
+
}
|
|
49009
|
+
};
|
|
49010
|
+
await ctx.metadata?.(meta);
|
|
49011
|
+
const callID = resolveToolCallID(ctx);
|
|
49012
|
+
if (callID) {
|
|
49013
|
+
storeToolMetadata(ctx.sessionID, callID, meta);
|
|
49014
|
+
}
|
|
48638
49015
|
if (args.full_session === true) {
|
|
48639
49016
|
return await formatFullSession(task, client2, {
|
|
48640
49017
|
includeThinking: args.include_thinking === true,
|
|
@@ -48682,6 +49059,7 @@ ${formatTaskStatus(finalTask)}`;
|
|
|
48682
49059
|
}
|
|
48683
49060
|
});
|
|
48684
49061
|
}
|
|
49062
|
+
// src/tools/background-task/modules/background-cancel.ts
|
|
48685
49063
|
function createBackgroundCancel(manager, client2) {
|
|
48686
49064
|
return tool({
|
|
48687
49065
|
description: BACKGROUND_CANCEL_DESCRIPTION,
|
|
@@ -48774,21 +49152,6 @@ Status: ${task.status}`;
|
|
|
48774
49152
|
}
|
|
48775
49153
|
});
|
|
48776
49154
|
}
|
|
48777
|
-
function formatMessageTime(value) {
|
|
48778
|
-
if (typeof value === "string") {
|
|
48779
|
-
const date5 = new Date(value);
|
|
48780
|
-
return Number.isNaN(date5.getTime()) ? value : date5.toISOString();
|
|
48781
|
-
}
|
|
48782
|
-
if (typeof value === "object" && value !== null) {
|
|
48783
|
-
if ("created" in value) {
|
|
48784
|
-
const created = value.created;
|
|
48785
|
-
if (typeof created === "number") {
|
|
48786
|
-
return new Date(created).toISOString();
|
|
48787
|
-
}
|
|
48788
|
-
}
|
|
48789
|
-
}
|
|
48790
|
-
return "Unknown time";
|
|
48791
|
-
}
|
|
48792
49155
|
// src/tools/call-omo-agent/constants.ts
|
|
48793
49156
|
var ALLOWED_AGENTS = [
|
|
48794
49157
|
"explore",
|
|
@@ -48805,12 +49168,17 @@ Available: {agents}
|
|
|
48805
49168
|
|
|
48806
49169
|
Pass \`session_id=<id>\` to continue previous agent with full context. Prompts MUST be in English. Use \`background_output\` for async results.`;
|
|
48807
49170
|
// src/tools/call-omo-agent/tools.ts
|
|
48808
|
-
import { existsSync as existsSync49, readdirSync as readdirSync16 } from "fs";
|
|
48809
|
-
import { join as join57 } from "path";
|
|
48810
49171
|
init_shared();
|
|
48811
|
-
|
|
49172
|
+
|
|
49173
|
+
// src/tools/call-omo-agent/background-executor.ts
|
|
49174
|
+
init_shared();
|
|
49175
|
+
init_hook_message_injector();
|
|
49176
|
+
|
|
49177
|
+
// src/tools/call-omo-agent/message-dir.ts
|
|
48812
49178
|
init_hook_message_injector();
|
|
48813
|
-
|
|
49179
|
+
import { existsSync as existsSync49, readdirSync as readdirSync16 } from "fs";
|
|
49180
|
+
import { join as join57 } from "path";
|
|
49181
|
+
function getMessageDir11(sessionID) {
|
|
48814
49182
|
if (!sessionID.startsWith("ses_"))
|
|
48815
49183
|
return null;
|
|
48816
49184
|
if (!existsSync49(MESSAGE_STORAGE))
|
|
@@ -48825,40 +49193,11 @@ function getMessageDir10(sessionID) {
|
|
|
48825
49193
|
}
|
|
48826
49194
|
return null;
|
|
48827
49195
|
}
|
|
48828
|
-
|
|
48829
|
-
|
|
48830
|
-
`);
|
|
48831
|
-
const description = CALL_OMO_AGENT_DESCRIPTION.replace("{agents}", agentDescriptions);
|
|
48832
|
-
return tool({
|
|
48833
|
-
description,
|
|
48834
|
-
args: {
|
|
48835
|
-
description: tool.schema.string().describe("A short (3-5 words) description of the task"),
|
|
48836
|
-
prompt: tool.schema.string().describe("The task for the agent to perform"),
|
|
48837
|
-
subagent_type: tool.schema.string().describe("The type of specialized agent to use for this task (explore or librarian only)"),
|
|
48838
|
-
run_in_background: tool.schema.boolean().describe("REQUIRED. true: run asynchronously (use background_output to get results), false: run synchronously and wait for completion"),
|
|
48839
|
-
session_id: tool.schema.string().describe("Existing Task session to continue").optional()
|
|
48840
|
-
},
|
|
48841
|
-
async execute(args, toolContext) {
|
|
48842
|
-
const toolCtx = toolContext;
|
|
48843
|
-
log(`[call_omo_agent] Starting with agent: ${args.subagent_type}, background: ${args.run_in_background}`);
|
|
48844
|
-
if (![...ALLOWED_AGENTS].some((name) => name.toLowerCase() === args.subagent_type.toLowerCase())) {
|
|
48845
|
-
return `Error: Invalid agent type "${args.subagent_type}". Only ${ALLOWED_AGENTS.join(", ")} are allowed.`;
|
|
48846
|
-
}
|
|
48847
|
-
const normalizedAgent = args.subagent_type.toLowerCase();
|
|
48848
|
-
args = { ...args, subagent_type: normalizedAgent };
|
|
48849
|
-
if (args.run_in_background) {
|
|
48850
|
-
if (args.session_id) {
|
|
48851
|
-
return `Error: session_id is not supported in background mode. Use run_in_background=false to continue an existing session.`;
|
|
48852
|
-
}
|
|
48853
|
-
return await executeBackground(args, toolCtx, backgroundManager);
|
|
48854
|
-
}
|
|
48855
|
-
return await executeSync(args, toolCtx, ctx);
|
|
48856
|
-
}
|
|
48857
|
-
});
|
|
48858
|
-
}
|
|
49196
|
+
|
|
49197
|
+
// src/tools/call-omo-agent/background-executor.ts
|
|
48859
49198
|
async function executeBackground(args, toolContext, manager) {
|
|
48860
49199
|
try {
|
|
48861
|
-
const messageDir =
|
|
49200
|
+
const messageDir = getMessageDir11(toolContext.sessionID);
|
|
48862
49201
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
48863
49202
|
const firstMessageAgent = messageDir ? findFirstMessageWithAgent(messageDir) : null;
|
|
48864
49203
|
const sessionAgent = getSessionAgent(toolContext.sessionID);
|
|
@@ -48920,8 +49259,14 @@ Use \`background_output\` tool with task_id="${task.id}" to check progress:
|
|
|
48920
49259
|
return `Failed to launch background agent task: ${message}`;
|
|
48921
49260
|
}
|
|
48922
49261
|
}
|
|
48923
|
-
|
|
48924
|
-
|
|
49262
|
+
|
|
49263
|
+
// src/tools/call-omo-agent/sync-executor.ts
|
|
49264
|
+
init_shared();
|
|
49265
|
+
init_shared();
|
|
49266
|
+
|
|
49267
|
+
// src/tools/call-omo-agent/session-creator.ts
|
|
49268
|
+
init_shared();
|
|
49269
|
+
async function createOrGetSession(args, toolContext, ctx) {
|
|
48925
49270
|
if (args.session_id) {
|
|
48926
49271
|
log(`[call_omo_agent] Using existing session: ${args.session_id}`);
|
|
48927
49272
|
const sessionResult = await ctx.client.session.get({
|
|
@@ -48929,9 +49274,9 @@ async function executeSync(args, toolContext, ctx) {
|
|
|
48929
49274
|
});
|
|
48930
49275
|
if (sessionResult.error) {
|
|
48931
49276
|
log(`[call_omo_agent] Session get error:`, sessionResult.error);
|
|
48932
|
-
|
|
49277
|
+
throw new Error(`Failed to get existing session: ${sessionResult.error}`);
|
|
48933
49278
|
}
|
|
48934
|
-
sessionID
|
|
49279
|
+
return { sessionID: args.session_id, isNew: false };
|
|
48935
49280
|
} else {
|
|
48936
49281
|
log(`[call_omo_agent] Creating new session with parent: ${toolContext.sessionID}`);
|
|
48937
49282
|
const parentSession = await ctx.client.session.get({
|
|
@@ -48958,55 +49303,27 @@ async function executeSync(args, toolContext, ctx) {
|
|
|
48958
49303
|
log(`[call_omo_agent] Session create error:`, createResult.error);
|
|
48959
49304
|
const errorStr = String(createResult.error);
|
|
48960
49305
|
if (errorStr.toLowerCase().includes("unauthorized")) {
|
|
48961
|
-
|
|
49306
|
+
throw new Error(`Failed to create session (Unauthorized). This may be due to:
|
|
48962
49307
|
1. OAuth token restrictions (e.g., Claude Code credentials are restricted to Claude Code only)
|
|
48963
49308
|
2. Provider authentication issues
|
|
48964
49309
|
3. Session permission inheritance problems
|
|
48965
49310
|
|
|
48966
49311
|
Try using a different provider or API key authentication.
|
|
48967
49312
|
|
|
48968
|
-
Original error: ${createResult.error}
|
|
49313
|
+
Original error: ${createResult.error}`);
|
|
48969
49314
|
}
|
|
48970
|
-
|
|
49315
|
+
throw new Error(`Failed to create session: ${createResult.error}`);
|
|
48971
49316
|
}
|
|
48972
|
-
sessionID = createResult.data.id;
|
|
49317
|
+
const sessionID = createResult.data.id;
|
|
48973
49318
|
log(`[call_omo_agent] Created session: ${sessionID}`);
|
|
49319
|
+
return { sessionID, isNew: true };
|
|
48974
49320
|
}
|
|
48975
|
-
|
|
48976
|
-
title: args.description,
|
|
48977
|
-
metadata: { sessionId: sessionID }
|
|
48978
|
-
});
|
|
48979
|
-
log(`[call_omo_agent] Sending prompt to session ${sessionID}`);
|
|
48980
|
-
log(`[call_omo_agent] Prompt text:`, args.prompt.substring(0, 100));
|
|
48981
|
-
try {
|
|
48982
|
-
await ctx.client.session.promptAsync({
|
|
48983
|
-
path: { id: sessionID },
|
|
48984
|
-
body: {
|
|
48985
|
-
agent: args.subagent_type,
|
|
48986
|
-
tools: {
|
|
48987
|
-
...getAgentToolRestrictions(args.subagent_type),
|
|
48988
|
-
task: false
|
|
48989
|
-
},
|
|
48990
|
-
parts: [{ type: "text", text: args.prompt }]
|
|
48991
|
-
}
|
|
48992
|
-
});
|
|
48993
|
-
} catch (error45) {
|
|
48994
|
-
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
48995
|
-
log(`[call_omo_agent] Prompt error:`, errorMessage);
|
|
48996
|
-
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
|
|
48997
|
-
return `Error: Agent "${args.subagent_type}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.
|
|
48998
|
-
|
|
48999
|
-
<task_metadata>
|
|
49000
|
-
session_id: ${sessionID}
|
|
49001
|
-
</task_metadata>`;
|
|
49002
|
-
}
|
|
49003
|
-
return `Error: Failed to send prompt: ${errorMessage}
|
|
49321
|
+
}
|
|
49004
49322
|
|
|
49005
|
-
|
|
49006
|
-
|
|
49007
|
-
|
|
49008
|
-
|
|
49009
|
-
log(`[call_omo_agent] Prompt sent, polling for completion...`);
|
|
49323
|
+
// src/tools/call-omo-agent/completion-poller.ts
|
|
49324
|
+
init_shared();
|
|
49325
|
+
async function waitForCompletion(sessionID, toolContext, ctx) {
|
|
49326
|
+
log(`[call_omo_agent] Polling for completion...`);
|
|
49010
49327
|
const POLL_INTERVAL_MS = 500;
|
|
49011
49328
|
const MAX_POLL_TIME_MS = 5 * 60 * 1000;
|
|
49012
49329
|
const pollStart = Date.now();
|
|
@@ -49016,11 +49333,7 @@ session_id: ${sessionID}
|
|
|
49016
49333
|
while (Date.now() - pollStart < MAX_POLL_TIME_MS) {
|
|
49017
49334
|
if (toolContext.abort?.aborted) {
|
|
49018
49335
|
log(`[call_omo_agent] Aborted by user`);
|
|
49019
|
-
|
|
49020
|
-
|
|
49021
|
-
<task_metadata>
|
|
49022
|
-
session_id: ${sessionID}
|
|
49023
|
-
</task_metadata>`;
|
|
49336
|
+
throw new Error("Task aborted.");
|
|
49024
49337
|
}
|
|
49025
49338
|
await new Promise((resolve11) => setTimeout(resolve11, POLL_INTERVAL_MS));
|
|
49026
49339
|
const statusResult = await ctx.client.session.status();
|
|
@@ -49047,18 +49360,20 @@ session_id: ${sessionID}
|
|
|
49047
49360
|
}
|
|
49048
49361
|
if (Date.now() - pollStart >= MAX_POLL_TIME_MS) {
|
|
49049
49362
|
log(`[call_omo_agent] Timeout reached`);
|
|
49050
|
-
|
|
49051
|
-
|
|
49052
|
-
<task_metadata>
|
|
49053
|
-
session_id: ${sessionID}
|
|
49054
|
-
</task_metadata>`;
|
|
49363
|
+
throw new Error("Agent task timed out after 5 minutes.");
|
|
49055
49364
|
}
|
|
49365
|
+
}
|
|
49366
|
+
|
|
49367
|
+
// src/tools/call-omo-agent/message-processor.ts
|
|
49368
|
+
init_shared();
|
|
49369
|
+
init_session_cursor();
|
|
49370
|
+
async function processMessages(sessionID, ctx) {
|
|
49056
49371
|
const messagesResult = await ctx.client.session.messages({
|
|
49057
49372
|
path: { id: sessionID }
|
|
49058
49373
|
});
|
|
49059
49374
|
if (messagesResult.error) {
|
|
49060
49375
|
log(`[call_omo_agent] Messages error:`, messagesResult.error);
|
|
49061
|
-
|
|
49376
|
+
throw new Error(`Failed to get messages: ${messagesResult.error}`);
|
|
49062
49377
|
}
|
|
49063
49378
|
const messages = messagesResult.data;
|
|
49064
49379
|
log(`[call_omo_agent] Got ${messages.length} messages`);
|
|
@@ -49066,11 +49381,7 @@ session_id: ${sessionID}
|
|
|
49066
49381
|
if (relevantMessages.length === 0) {
|
|
49067
49382
|
log(`[call_omo_agent] No assistant or tool messages found`);
|
|
49068
49383
|
log(`[call_omo_agent] All messages:`, JSON.stringify(messages, null, 2));
|
|
49069
|
-
|
|
49070
|
-
|
|
49071
|
-
<task_metadata>
|
|
49072
|
-
session_id: ${sessionID}
|
|
49073
|
-
</task_metadata>`;
|
|
49384
|
+
throw new Error("No assistant or tool response found");
|
|
49074
49385
|
}
|
|
49075
49386
|
log(`[call_omo_agent] Found ${relevantMessages.length} relevant messages`);
|
|
49076
49387
|
const sortedMessages = [...relevantMessages].sort((a, b) => {
|
|
@@ -49080,11 +49391,7 @@ session_id: ${sessionID}
|
|
|
49080
49391
|
});
|
|
49081
49392
|
const newMessages = consumeNewMessages(sessionID, sortedMessages);
|
|
49082
49393
|
if (newMessages.length === 0) {
|
|
49083
|
-
return
|
|
49084
|
-
|
|
49085
|
-
<task_metadata>
|
|
49086
|
-
session_id: ${sessionID}
|
|
49087
|
-
</task_metadata>`;
|
|
49394
|
+
return "No new output since last check.";
|
|
49088
49395
|
}
|
|
49089
49396
|
const extractedContent = [];
|
|
49090
49397
|
for (const message of newMessages) {
|
|
@@ -49109,12 +49416,87 @@ session_id: ${sessionID}
|
|
|
49109
49416
|
|
|
49110
49417
|
`);
|
|
49111
49418
|
log(`[call_omo_agent] Got response, length: ${responseText.length}`);
|
|
49419
|
+
return responseText;
|
|
49420
|
+
}
|
|
49421
|
+
|
|
49422
|
+
// src/tools/call-omo-agent/sync-executor.ts
|
|
49423
|
+
async function executeSync(args, toolContext, ctx) {
|
|
49424
|
+
const { sessionID } = await createOrGetSession(args, toolContext, ctx);
|
|
49425
|
+
await toolContext.metadata?.({
|
|
49426
|
+
title: args.description,
|
|
49427
|
+
metadata: { sessionId: sessionID }
|
|
49428
|
+
});
|
|
49429
|
+
log(`[call_omo_agent] Sending prompt to session ${sessionID}`);
|
|
49430
|
+
log(`[call_omo_agent] Prompt text:`, args.prompt.substring(0, 100));
|
|
49431
|
+
try {
|
|
49432
|
+
await ctx.client.session.promptAsync({
|
|
49433
|
+
path: { id: sessionID },
|
|
49434
|
+
body: {
|
|
49435
|
+
agent: args.subagent_type,
|
|
49436
|
+
tools: {
|
|
49437
|
+
...getAgentToolRestrictions(args.subagent_type),
|
|
49438
|
+
task: false
|
|
49439
|
+
},
|
|
49440
|
+
parts: [{ type: "text", text: args.prompt }]
|
|
49441
|
+
}
|
|
49442
|
+
});
|
|
49443
|
+
} catch (error45) {
|
|
49444
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
49445
|
+
log(`[call_omo_agent] Prompt error:`, errorMessage);
|
|
49446
|
+
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
|
|
49447
|
+
return `Error: Agent "${args.subagent_type}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.
|
|
49448
|
+
|
|
49449
|
+
<task_metadata>
|
|
49450
|
+
session_id: ${sessionID}
|
|
49451
|
+
</task_metadata>`;
|
|
49452
|
+
}
|
|
49453
|
+
return `Error: Failed to send prompt: ${errorMessage}
|
|
49454
|
+
|
|
49455
|
+
<task_metadata>
|
|
49456
|
+
session_id: ${sessionID}
|
|
49457
|
+
</task_metadata>`;
|
|
49458
|
+
}
|
|
49459
|
+
await waitForCompletion(sessionID, toolContext, ctx);
|
|
49460
|
+
const responseText = await processMessages(sessionID, ctx);
|
|
49112
49461
|
const output = responseText + `
|
|
49113
49462
|
|
|
49114
49463
|
` + ["<task_metadata>", `session_id: ${sessionID}`, "</task_metadata>"].join(`
|
|
49115
49464
|
`);
|
|
49116
49465
|
return output;
|
|
49117
49466
|
}
|
|
49467
|
+
|
|
49468
|
+
// src/tools/call-omo-agent/tools.ts
|
|
49469
|
+
function createCallOmoAgent(ctx, backgroundManager) {
|
|
49470
|
+
const agentDescriptions = ALLOWED_AGENTS.map((name) => `- ${name}: Specialized agent for ${name} tasks`).join(`
|
|
49471
|
+
`);
|
|
49472
|
+
const description = CALL_OMO_AGENT_DESCRIPTION.replace("{agents}", agentDescriptions);
|
|
49473
|
+
return tool({
|
|
49474
|
+
description,
|
|
49475
|
+
args: {
|
|
49476
|
+
description: tool.schema.string().describe("A short (3-5 words) description of the task"),
|
|
49477
|
+
prompt: tool.schema.string().describe("The task for the agent to perform"),
|
|
49478
|
+
subagent_type: tool.schema.string().describe("The type of specialized agent to use for this task (explore or librarian only)"),
|
|
49479
|
+
run_in_background: tool.schema.boolean().describe("REQUIRED. true: run asynchronously (use background_output to get results), false: run synchronously and wait for completion"),
|
|
49480
|
+
session_id: tool.schema.string().describe("Existing Task session to continue").optional()
|
|
49481
|
+
},
|
|
49482
|
+
async execute(args, toolContext) {
|
|
49483
|
+
const toolCtx = toolContext;
|
|
49484
|
+
log(`[call_omo_agent] Starting with agent: ${args.subagent_type}, background: ${args.run_in_background}`);
|
|
49485
|
+
if (![...ALLOWED_AGENTS].some((name) => name.toLowerCase() === args.subagent_type.toLowerCase())) {
|
|
49486
|
+
return `Error: Invalid agent type "${args.subagent_type}". Only ${ALLOWED_AGENTS.join(", ")} are allowed.`;
|
|
49487
|
+
}
|
|
49488
|
+
const normalizedAgent = args.subagent_type.toLowerCase();
|
|
49489
|
+
args = { ...args, subagent_type: normalizedAgent };
|
|
49490
|
+
if (args.run_in_background) {
|
|
49491
|
+
if (args.session_id) {
|
|
49492
|
+
return `Error: session_id is not supported in background mode. Use run_in_background=false to continue an existing session.`;
|
|
49493
|
+
}
|
|
49494
|
+
return await executeBackground(args, toolCtx, backgroundManager);
|
|
49495
|
+
}
|
|
49496
|
+
return await executeSync(args, toolCtx, ctx);
|
|
49497
|
+
}
|
|
49498
|
+
});
|
|
49499
|
+
}
|
|
49118
49500
|
// src/tools/look-at/constants.ts
|
|
49119
49501
|
var MULTIMODAL_LOOKER_AGENT = "multimodal-looker";
|
|
49120
49502
|
var LOOK_AT_DESCRIPTION = `Analyze media files (PDFs, images, diagrams) that require interpretation beyond raw text. Extracts specific information or summaries from documents, describes visual content. Use when you need analyzed/extracted data rather than literal file contents.`;
|
|
@@ -49426,7 +49808,7 @@ function parseModelString(model) {
|
|
|
49426
49808
|
}
|
|
49427
49809
|
return;
|
|
49428
49810
|
}
|
|
49429
|
-
function
|
|
49811
|
+
function getMessageDir12(sessionID) {
|
|
49430
49812
|
if (!sessionID.startsWith("ses_"))
|
|
49431
49813
|
return null;
|
|
49432
49814
|
if (!existsSync50(MESSAGE_STORAGE))
|
|
@@ -49692,7 +50074,19 @@ init_shared();
|
|
|
49692
50074
|
init_model_availability();
|
|
49693
50075
|
init_connected_providers_cache();
|
|
49694
50076
|
init_model_requirements();
|
|
49695
|
-
var
|
|
50077
|
+
var SISYPHUS_JUNIOR_AGENT2 = "sisyphus-junior";
|
|
50078
|
+
function resolveToolCallID2(ctx) {
|
|
50079
|
+
if (typeof ctx.callID === "string" && ctx.callID.trim() !== "") {
|
|
50080
|
+
return ctx.callID;
|
|
50081
|
+
}
|
|
50082
|
+
if (typeof ctx.callId === "string" && ctx.callId.trim() !== "") {
|
|
50083
|
+
return ctx.callId;
|
|
50084
|
+
}
|
|
50085
|
+
if (typeof ctx.call_id === "string" && ctx.call_id.trim() !== "") {
|
|
50086
|
+
return ctx.call_id;
|
|
50087
|
+
}
|
|
50088
|
+
return;
|
|
50089
|
+
}
|
|
49696
50090
|
async function resolveSkillContent(skills, options) {
|
|
49697
50091
|
if (skills.length === 0) {
|
|
49698
50092
|
return { content: undefined, error: null };
|
|
@@ -49708,7 +50102,7 @@ async function resolveSkillContent(skills, options) {
|
|
|
49708
50102
|
`), error: null };
|
|
49709
50103
|
}
|
|
49710
50104
|
function resolveParentContext(ctx) {
|
|
49711
|
-
const messageDir =
|
|
50105
|
+
const messageDir = getMessageDir12(ctx.sessionID);
|
|
49712
50106
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
49713
50107
|
const firstMessageAgent = messageDir ? findFirstMessageWithAgent(messageDir) : null;
|
|
49714
50108
|
const sessionAgent = getSessionAgent(ctx.sessionID);
|
|
@@ -49758,9 +50152,9 @@ async function executeBackgroundContinuation(args, ctx, executorCtx, parentConte
|
|
|
49758
50152
|
}
|
|
49759
50153
|
};
|
|
49760
50154
|
await ctx.metadata?.(bgContMeta);
|
|
49761
|
-
|
|
49762
|
-
|
|
49763
|
-
|
|
50155
|
+
const bgContCallID = resolveToolCallID2(ctx);
|
|
50156
|
+
if (bgContCallID)
|
|
50157
|
+
storeToolMetadata(ctx.sessionID, bgContCallID, bgContMeta);
|
|
49764
50158
|
return `Background task continued.
|
|
49765
50159
|
|
|
49766
50160
|
Task ID: ${task.id}
|
|
@@ -49808,12 +50202,13 @@ async function executeSyncContinuation(args, ctx, executorCtx) {
|
|
|
49808
50202
|
}
|
|
49809
50203
|
};
|
|
49810
50204
|
await ctx.metadata?.(syncContMeta);
|
|
49811
|
-
|
|
49812
|
-
|
|
49813
|
-
|
|
50205
|
+
const syncContCallID = resolveToolCallID2(ctx);
|
|
50206
|
+
if (syncContCallID)
|
|
50207
|
+
storeToolMetadata(ctx.sessionID, syncContCallID, syncContMeta);
|
|
49814
50208
|
try {
|
|
49815
50209
|
let resumeAgent;
|
|
49816
50210
|
let resumeModel;
|
|
50211
|
+
let resumeVariant;
|
|
49817
50212
|
try {
|
|
49818
50213
|
const messagesResp = await client2.session.messages({ path: { id: args.session_id } });
|
|
49819
50214
|
const messages2 = messagesResp.data ?? [];
|
|
@@ -49822,20 +50217,23 @@ async function executeSyncContinuation(args, ctx, executorCtx) {
|
|
|
49822
50217
|
if (info?.agent || info?.model || info?.modelID && info?.providerID) {
|
|
49823
50218
|
resumeAgent = info.agent;
|
|
49824
50219
|
resumeModel = info.model ?? (info.providerID && info.modelID ? { providerID: info.providerID, modelID: info.modelID } : undefined);
|
|
50220
|
+
resumeVariant = info.variant;
|
|
49825
50221
|
break;
|
|
49826
50222
|
}
|
|
49827
50223
|
}
|
|
49828
50224
|
} catch {
|
|
49829
|
-
const resumeMessageDir =
|
|
50225
|
+
const resumeMessageDir = getMessageDir12(args.session_id);
|
|
49830
50226
|
const resumeMessage = resumeMessageDir ? findNearestMessageWithFields(resumeMessageDir) : null;
|
|
49831
50227
|
resumeAgent = resumeMessage?.agent;
|
|
49832
50228
|
resumeModel = resumeMessage?.model?.providerID && resumeMessage?.model?.modelID ? { providerID: resumeMessage.model.providerID, modelID: resumeMessage.model.modelID } : undefined;
|
|
50229
|
+
resumeVariant = resumeMessage?.model?.variant;
|
|
49833
50230
|
}
|
|
49834
|
-
await client2
|
|
50231
|
+
await promptSyncWithModelSuggestionRetry(client2, {
|
|
49835
50232
|
path: { id: args.session_id },
|
|
49836
50233
|
body: {
|
|
49837
50234
|
...resumeAgent !== undefined ? { agent: resumeAgent } : {},
|
|
49838
50235
|
...resumeModel !== undefined ? { model: resumeModel } : {},
|
|
50236
|
+
...resumeVariant !== undefined ? { variant: resumeVariant } : {},
|
|
49839
50237
|
tools: {
|
|
49840
50238
|
...resumeAgent ? getAgentToolRestrictions(resumeAgent) : {},
|
|
49841
50239
|
task: false,
|
|
@@ -49854,27 +50252,6 @@ async function executeSyncContinuation(args, ctx, executorCtx) {
|
|
|
49854
50252
|
|
|
49855
50253
|
Session ID: ${args.session_id}`;
|
|
49856
50254
|
}
|
|
49857
|
-
const timing = getTimingConfig();
|
|
49858
|
-
const pollStart = Date.now();
|
|
49859
|
-
let lastMsgCount = 0;
|
|
49860
|
-
let stablePolls = 0;
|
|
49861
|
-
while (Date.now() - pollStart < 60000) {
|
|
49862
|
-
await new Promise((resolve11) => setTimeout(resolve11, timing.POLL_INTERVAL_MS));
|
|
49863
|
-
const elapsed = Date.now() - pollStart;
|
|
49864
|
-
if (elapsed < timing.SESSION_CONTINUATION_STABILITY_MS)
|
|
49865
|
-
continue;
|
|
49866
|
-
const messagesCheck = await client2.session.messages({ path: { id: args.session_id } });
|
|
49867
|
-
const msgs = messagesCheck.data ?? messagesCheck;
|
|
49868
|
-
const currentMsgCount = msgs.length;
|
|
49869
|
-
if (currentMsgCount > 0 && currentMsgCount === lastMsgCount) {
|
|
49870
|
-
stablePolls++;
|
|
49871
|
-
if (stablePolls >= timing.STABILITY_POLLS_REQUIRED)
|
|
49872
|
-
break;
|
|
49873
|
-
} else {
|
|
49874
|
-
stablePolls = 0;
|
|
49875
|
-
lastMsgCount = currentMsgCount;
|
|
49876
|
-
}
|
|
49877
|
-
}
|
|
49878
50255
|
const messagesResult = await client2.session.messages({
|
|
49879
50256
|
path: { id: args.session_id }
|
|
49880
50257
|
});
|
|
@@ -49962,9 +50339,9 @@ Task ID: ${task.id}`;
|
|
|
49962
50339
|
}
|
|
49963
50340
|
};
|
|
49964
50341
|
await ctx.metadata?.(bgTaskMeta);
|
|
49965
|
-
|
|
49966
|
-
|
|
49967
|
-
|
|
50342
|
+
const bgTaskCallID = resolveToolCallID2(ctx);
|
|
50343
|
+
if (bgTaskCallID)
|
|
50344
|
+
storeToolMetadata(ctx.sessionID, bgTaskCallID, bgTaskMeta);
|
|
49968
50345
|
const startTime = new Date;
|
|
49969
50346
|
const timingCfg = getTimingConfig();
|
|
49970
50347
|
const pollStart = Date.now();
|
|
@@ -50087,9 +50464,9 @@ Task ID: ${task.id}`;
|
|
|
50087
50464
|
}
|
|
50088
50465
|
};
|
|
50089
50466
|
await ctx.metadata?.(unstableMeta);
|
|
50090
|
-
|
|
50091
|
-
|
|
50092
|
-
|
|
50467
|
+
const unstableCallID = resolveToolCallID2(ctx);
|
|
50468
|
+
if (unstableCallID)
|
|
50469
|
+
storeToolMetadata(ctx.sessionID, unstableCallID, unstableMeta);
|
|
50093
50470
|
return `Background task launched.
|
|
50094
50471
|
|
|
50095
50472
|
Task ID: ${task.id}
|
|
@@ -50176,12 +50553,12 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
|
|
|
50176
50553
|
}
|
|
50177
50554
|
};
|
|
50178
50555
|
await ctx.metadata?.(syncTaskMeta);
|
|
50179
|
-
|
|
50180
|
-
|
|
50181
|
-
|
|
50556
|
+
const syncTaskCallID = resolveToolCallID2(ctx);
|
|
50557
|
+
if (syncTaskCallID)
|
|
50558
|
+
storeToolMetadata(ctx.sessionID, syncTaskCallID, syncTaskMeta);
|
|
50182
50559
|
try {
|
|
50183
|
-
const allowTask =
|
|
50184
|
-
await
|
|
50560
|
+
const allowTask = isPlanFamily(agentToUse);
|
|
50561
|
+
await promptSyncWithModelSuggestionRetry(client2, {
|
|
50185
50562
|
path: { id: sessionID },
|
|
50186
50563
|
body: {
|
|
50187
50564
|
agent: agentToUse,
|
|
@@ -50218,62 +50595,6 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
|
|
|
50218
50595
|
category: args.category
|
|
50219
50596
|
});
|
|
50220
50597
|
}
|
|
50221
|
-
const syncTiming = getTimingConfig();
|
|
50222
|
-
const pollStart = Date.now();
|
|
50223
|
-
let lastMsgCount = 0;
|
|
50224
|
-
let stablePolls = 0;
|
|
50225
|
-
let pollCount = 0;
|
|
50226
|
-
log("[task] Starting poll loop", { sessionID, agentToUse });
|
|
50227
|
-
while (Date.now() - pollStart < syncTiming.MAX_POLL_TIME_MS) {
|
|
50228
|
-
if (ctx.abort?.aborted) {
|
|
50229
|
-
log("[task] Aborted by user", { sessionID });
|
|
50230
|
-
if (toastManager && taskId)
|
|
50231
|
-
toastManager.removeTask(taskId);
|
|
50232
|
-
return `Task aborted.
|
|
50233
|
-
|
|
50234
|
-
Session ID: ${sessionID}`;
|
|
50235
|
-
}
|
|
50236
|
-
await new Promise((resolve11) => setTimeout(resolve11, syncTiming.POLL_INTERVAL_MS));
|
|
50237
|
-
pollCount++;
|
|
50238
|
-
const statusResult = await client2.session.status();
|
|
50239
|
-
const allStatuses = statusResult.data ?? {};
|
|
50240
|
-
const sessionStatus = allStatuses[sessionID];
|
|
50241
|
-
if (pollCount % 10 === 0) {
|
|
50242
|
-
log("[task] Poll status", {
|
|
50243
|
-
sessionID,
|
|
50244
|
-
pollCount,
|
|
50245
|
-
elapsed: Math.floor((Date.now() - pollStart) / 1000) + "s",
|
|
50246
|
-
sessionStatus: sessionStatus?.type ?? "not_in_status",
|
|
50247
|
-
stablePolls,
|
|
50248
|
-
lastMsgCount
|
|
50249
|
-
});
|
|
50250
|
-
}
|
|
50251
|
-
if (sessionStatus && sessionStatus.type !== "idle") {
|
|
50252
|
-
stablePolls = 0;
|
|
50253
|
-
lastMsgCount = 0;
|
|
50254
|
-
continue;
|
|
50255
|
-
}
|
|
50256
|
-
const elapsed = Date.now() - pollStart;
|
|
50257
|
-
if (elapsed < syncTiming.MIN_STABILITY_TIME_MS) {
|
|
50258
|
-
continue;
|
|
50259
|
-
}
|
|
50260
|
-
const messagesCheck = await client2.session.messages({ path: { id: sessionID } });
|
|
50261
|
-
const msgs = messagesCheck.data ?? messagesCheck;
|
|
50262
|
-
const currentMsgCount = msgs.length;
|
|
50263
|
-
if (currentMsgCount === lastMsgCount) {
|
|
50264
|
-
stablePolls++;
|
|
50265
|
-
if (stablePolls >= syncTiming.STABILITY_POLLS_REQUIRED) {
|
|
50266
|
-
log("[task] Poll complete - messages stable", { sessionID, pollCount, currentMsgCount });
|
|
50267
|
-
break;
|
|
50268
|
-
}
|
|
50269
|
-
} else {
|
|
50270
|
-
stablePolls = 0;
|
|
50271
|
-
lastMsgCount = currentMsgCount;
|
|
50272
|
-
}
|
|
50273
|
-
}
|
|
50274
|
-
if (Date.now() - pollStart >= syncTiming.MAX_POLL_TIME_MS) {
|
|
50275
|
-
log("[task] Poll timeout reached", { sessionID, pollCount, lastMsgCount, stablePolls });
|
|
50276
|
-
}
|
|
50277
50598
|
const messagesResult = await client2.session.messages({
|
|
50278
50599
|
path: { id: sessionID }
|
|
50279
50600
|
});
|
|
@@ -50327,8 +50648,8 @@ session_id: ${sessionID}
|
|
|
50327
50648
|
}
|
|
50328
50649
|
async function resolveCategoryExecution(args, executorCtx, inheritedModel, systemDefaultModel) {
|
|
50329
50650
|
const { client: client2, userCategories, sisyphusJuniorModel } = executorCtx;
|
|
50330
|
-
const connectedProviders = readConnectedProvidersCache();
|
|
50331
|
-
const availableModels = await fetchAvailableModels(client2, {
|
|
50651
|
+
const connectedProviders = executorCtx.connectedProvidersOverride !== undefined ? executorCtx.connectedProvidersOverride : readConnectedProvidersCache();
|
|
50652
|
+
const availableModels = executorCtx.availableModelsOverride !== undefined ? executorCtx.availableModelsOverride : await fetchAvailableModels(client2, {
|
|
50332
50653
|
connectedProviders: connectedProviders ?? undefined
|
|
50333
50654
|
});
|
|
50334
50655
|
const resolved = resolveCategoryConfig(args.category, {
|
|
@@ -50365,7 +50686,7 @@ async function resolveCategoryExecution(args, executorCtx, inheritedModel, syste
|
|
|
50365
50686
|
userModel: explicitCategoryModel ?? overrideModel,
|
|
50366
50687
|
categoryDefaultModel: resolved.model
|
|
50367
50688
|
},
|
|
50368
|
-
constraints: { availableModels },
|
|
50689
|
+
constraints: { availableModels, connectedProviders },
|
|
50369
50690
|
policy: {
|
|
50370
50691
|
fallbackChain: requirement.fallbackChain,
|
|
50371
50692
|
systemDefaultModel
|
|
@@ -50433,7 +50754,7 @@ Available categories: ${categoryNames.join(", ")}`
|
|
|
50433
50754
|
const unstableModel = actualModel?.toLowerCase();
|
|
50434
50755
|
const isUnstableAgent = resolved.config.is_unstable_agent === true || (unstableModel ? unstableModel.includes("gemini") || unstableModel.includes("minimax") : false);
|
|
50435
50756
|
return {
|
|
50436
|
-
agentToUse:
|
|
50757
|
+
agentToUse: SISYPHUS_JUNIOR_AGENT2,
|
|
50437
50758
|
categoryModel,
|
|
50438
50759
|
categoryPromptAppend,
|
|
50439
50760
|
modelInfo,
|
|
@@ -50442,25 +50763,25 @@ Available categories: ${categoryNames.join(", ")}`
|
|
|
50442
50763
|
};
|
|
50443
50764
|
}
|
|
50444
50765
|
async function resolveSubagentExecution(args, executorCtx, parentAgent, categoryExamples) {
|
|
50445
|
-
const { client: client2 } = executorCtx;
|
|
50766
|
+
const { client: client2, agentOverrides } = executorCtx;
|
|
50446
50767
|
if (!args.subagent_type?.trim()) {
|
|
50447
50768
|
return { agentToUse: "", categoryModel: undefined, error: `Agent name cannot be empty.` };
|
|
50448
50769
|
}
|
|
50449
50770
|
const agentName = args.subagent_type.trim();
|
|
50450
|
-
if (agentName.toLowerCase() ===
|
|
50771
|
+
if (agentName.toLowerCase() === SISYPHUS_JUNIOR_AGENT2.toLowerCase()) {
|
|
50451
50772
|
return {
|
|
50452
50773
|
agentToUse: "",
|
|
50453
50774
|
categoryModel: undefined,
|
|
50454
|
-
error: `Cannot use subagent_type="${
|
|
50775
|
+
error: `Cannot use subagent_type="${SISYPHUS_JUNIOR_AGENT2}" directly. Use category parameter instead (e.g., ${categoryExamples}).
|
|
50455
50776
|
|
|
50456
50777
|
Sisyphus-Junior is spawned automatically when you specify a category. Pick the appropriate category for your task domain.`
|
|
50457
50778
|
};
|
|
50458
50779
|
}
|
|
50459
|
-
if (
|
|
50780
|
+
if (isPlanFamily(agentName) && isPlanFamily(parentAgent)) {
|
|
50460
50781
|
return {
|
|
50461
50782
|
agentToUse: "",
|
|
50462
50783
|
categoryModel: undefined,
|
|
50463
|
-
error: `You are prometheus. You cannot delegate to
|
|
50784
|
+
error: `You are a plan-family agent (plan/prometheus). You cannot delegate to other plan-family agents via task.
|
|
50464
50785
|
|
|
50465
50786
|
Create the work plan directly - that's your job as the planning agent.`
|
|
50466
50787
|
};
|
|
@@ -50489,7 +50810,34 @@ Create the work plan directly - that's your job as the planning agent.`
|
|
|
50489
50810
|
};
|
|
50490
50811
|
}
|
|
50491
50812
|
agentToUse = matchedAgent.name;
|
|
50492
|
-
|
|
50813
|
+
const agentNameLower = agentToUse.toLowerCase();
|
|
50814
|
+
const agentOverride = agentOverrides?.[agentNameLower] ?? (agentOverrides ? Object.entries(agentOverrides).find(([key]) => key.toLowerCase() === agentNameLower)?.[1] : undefined);
|
|
50815
|
+
const agentRequirement = AGENT_MODEL_REQUIREMENTS[agentNameLower];
|
|
50816
|
+
if (agentOverride?.model || agentRequirement) {
|
|
50817
|
+
const connectedProviders = executorCtx.connectedProvidersOverride !== undefined ? executorCtx.connectedProvidersOverride : readConnectedProvidersCache();
|
|
50818
|
+
const availableModels = executorCtx.availableModelsOverride !== undefined ? executorCtx.availableModelsOverride : await fetchAvailableModels(client2, {
|
|
50819
|
+
connectedProviders: connectedProviders ?? undefined
|
|
50820
|
+
});
|
|
50821
|
+
const matchedAgentModelStr = matchedAgent.model ? `${matchedAgent.model.providerID}/${matchedAgent.model.modelID}` : undefined;
|
|
50822
|
+
const resolution = resolveModelPipeline({
|
|
50823
|
+
intent: {
|
|
50824
|
+
userModel: agentOverride?.model,
|
|
50825
|
+
categoryDefaultModel: matchedAgentModelStr
|
|
50826
|
+
},
|
|
50827
|
+
constraints: { availableModels, connectedProviders },
|
|
50828
|
+
policy: {
|
|
50829
|
+
fallbackChain: agentRequirement?.fallbackChain,
|
|
50830
|
+
systemDefaultModel: undefined
|
|
50831
|
+
}
|
|
50832
|
+
});
|
|
50833
|
+
if (resolution) {
|
|
50834
|
+
const parsed = parseModelString(resolution.model);
|
|
50835
|
+
if (parsed) {
|
|
50836
|
+
const variantToUse = agentOverride?.variant ?? resolution.variant;
|
|
50837
|
+
categoryModel = variantToUse ? { ...parsed, variant: variantToUse } : parsed;
|
|
50838
|
+
}
|
|
50839
|
+
}
|
|
50840
|
+
} else if (matchedAgent.model) {
|
|
50493
50841
|
categoryModel = matchedAgent.model;
|
|
50494
50842
|
}
|
|
50495
50843
|
} catch {}
|
|
@@ -50736,6 +51084,9 @@ function resolveTaskListId(config3 = {}) {
|
|
|
50736
51084
|
const envId = process.env.ULTRAWORK_TASK_LIST_ID?.trim();
|
|
50737
51085
|
if (envId)
|
|
50738
51086
|
return sanitizePathSegment(envId);
|
|
51087
|
+
const claudeEnvId = process.env.CLAUDE_CODE_TASK_LIST_ID?.trim();
|
|
51088
|
+
if (claudeEnvId)
|
|
51089
|
+
return sanitizePathSegment(claudeEnvId);
|
|
50739
51090
|
const configId = config3.sisyphus?.tasks?.task_list_id?.trim();
|
|
50740
51091
|
if (configId)
|
|
50741
51092
|
return sanitizePathSegment(configId);
|
|
@@ -51345,6 +51696,7 @@ class BackgroundManager {
|
|
|
51345
51696
|
processingKeys = new Set;
|
|
51346
51697
|
completionTimers = new Map;
|
|
51347
51698
|
idleDeferralTimers = new Map;
|
|
51699
|
+
notificationQueueByParent = new Map;
|
|
51348
51700
|
constructor(ctx, config3, options) {
|
|
51349
51701
|
this.tasks = new Map;
|
|
51350
51702
|
this.notifications = new Map;
|
|
@@ -51452,13 +51804,14 @@ class BackgroundManager {
|
|
|
51452
51804
|
});
|
|
51453
51805
|
const parentDirectory = parentSession?.data?.directory ?? this.directory;
|
|
51454
51806
|
log(`[background-agent] Parent dir: ${parentSession?.data?.directory}, using: ${parentDirectory}`);
|
|
51807
|
+
const inheritedPermission = parentSession?.data?.permission;
|
|
51808
|
+
const permissionRules = Array.isArray(inheritedPermission) ? inheritedPermission.filter((r) => r?.permission !== "question") : [];
|
|
51809
|
+
permissionRules.push({ permission: "question", action: "deny", pattern: "*" });
|
|
51455
51810
|
const createResult = await this.client.session.create({
|
|
51456
51811
|
body: {
|
|
51457
51812
|
parentID: input.parentSessionID,
|
|
51458
51813
|
title: `${input.description} (@${input.agent} subagent)`,
|
|
51459
|
-
permission:
|
|
51460
|
-
{ permission: "question", action: "deny", pattern: "*" }
|
|
51461
|
-
]
|
|
51814
|
+
permission: permissionRules
|
|
51462
51815
|
},
|
|
51463
51816
|
query: {
|
|
51464
51817
|
directory: parentDirectory
|
|
@@ -51553,7 +51906,7 @@ class BackgroundManager {
|
|
|
51553
51906
|
}).catch(() => {});
|
|
51554
51907
|
this.markForNotification(existingTask);
|
|
51555
51908
|
this.cleanupPendingByParent(existingTask);
|
|
51556
|
-
this.notifyParentSession(existingTask).catch((err) => {
|
|
51909
|
+
this.enqueueNotificationForParent(existingTask.parentSessionID, () => this.notifyParentSession(existingTask)).catch((err) => {
|
|
51557
51910
|
log("[background-agent] Failed to notify on error:", err);
|
|
51558
51911
|
});
|
|
51559
51912
|
}
|
|
@@ -51747,7 +52100,7 @@ class BackgroundManager {
|
|
|
51747
52100
|
}
|
|
51748
52101
|
this.markForNotification(existingTask);
|
|
51749
52102
|
this.cleanupPendingByParent(existingTask);
|
|
51750
|
-
this.notifyParentSession(existingTask).catch((err) => {
|
|
52103
|
+
this.enqueueNotificationForParent(existingTask.parentSessionID, () => this.notifyParentSession(existingTask)).catch((err) => {
|
|
51751
52104
|
log("[background-agent] Failed to notify on resume error:", err);
|
|
51752
52105
|
});
|
|
51753
52106
|
});
|
|
@@ -52000,7 +52353,7 @@ class BackgroundManager {
|
|
|
52000
52353
|
}
|
|
52001
52354
|
this.markForNotification(task);
|
|
52002
52355
|
try {
|
|
52003
|
-
await this.notifyParentSession(task);
|
|
52356
|
+
await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task));
|
|
52004
52357
|
log(`[background-agent] Task cancelled via ${source}:`, task.id);
|
|
52005
52358
|
} catch (err) {
|
|
52006
52359
|
log("[background-agent] Error in notifyParentSession for cancelled task:", { taskId: task.id, error: err });
|
|
@@ -52095,7 +52448,7 @@ class BackgroundManager {
|
|
|
52095
52448
|
}).catch(() => {});
|
|
52096
52449
|
}
|
|
52097
52450
|
try {
|
|
52098
|
-
await this.notifyParentSession(task);
|
|
52451
|
+
await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task));
|
|
52099
52452
|
log(`[background-agent] Task completed via ${source}:`, task.id);
|
|
52100
52453
|
} catch (err) {
|
|
52101
52454
|
log("[background-agent] Error in notifyParentSession:", { taskId: task.id, error: err });
|
|
@@ -52114,14 +52467,18 @@ class BackgroundManager {
|
|
|
52114
52467
|
});
|
|
52115
52468
|
}
|
|
52116
52469
|
const pendingSet = this.pendingByParent.get(task.parentSessionID);
|
|
52470
|
+
let allComplete = false;
|
|
52471
|
+
let remainingCount = 0;
|
|
52117
52472
|
if (pendingSet) {
|
|
52118
52473
|
pendingSet.delete(task.id);
|
|
52119
|
-
|
|
52474
|
+
remainingCount = pendingSet.size;
|
|
52475
|
+
allComplete = remainingCount === 0;
|
|
52476
|
+
if (allComplete) {
|
|
52120
52477
|
this.pendingByParent.delete(task.parentSessionID);
|
|
52121
52478
|
}
|
|
52479
|
+
} else {
|
|
52480
|
+
allComplete = true;
|
|
52122
52481
|
}
|
|
52123
|
-
const allComplete = !pendingSet || pendingSet.size === 0;
|
|
52124
|
-
const remainingCount = pendingSet?.size ?? 0;
|
|
52125
52482
|
const statusText = task.status === "completed" ? "COMPLETED" : "CANCELLED";
|
|
52126
52483
|
const errorInfo = task.error ? `
|
|
52127
52484
|
**Error:** ${task.error}` : "";
|
|
@@ -52173,7 +52530,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
52173
52530
|
});
|
|
52174
52531
|
return;
|
|
52175
52532
|
}
|
|
52176
|
-
const messageDir =
|
|
52533
|
+
const messageDir = getMessageDir13(task.parentSessionID);
|
|
52177
52534
|
const currentMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
52178
52535
|
agent = currentMessage?.agent ?? task.parentAgent;
|
|
52179
52536
|
model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
|
|
@@ -52346,7 +52703,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
52346
52703
|
}).catch(() => {});
|
|
52347
52704
|
log(`[background-agent] Task ${task.id} interrupted: stale timeout`);
|
|
52348
52705
|
try {
|
|
52349
|
-
await this.notifyParentSession(task);
|
|
52706
|
+
await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task));
|
|
52350
52707
|
} catch (err) {
|
|
52351
52708
|
log("[background-agent] Error in notifyParentSession for stale task:", { taskId: task.id, error: err });
|
|
52352
52709
|
}
|
|
@@ -52497,11 +52854,26 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
52497
52854
|
this.tasks.clear();
|
|
52498
52855
|
this.notifications.clear();
|
|
52499
52856
|
this.pendingByParent.clear();
|
|
52857
|
+
this.notificationQueueByParent.clear();
|
|
52500
52858
|
this.queuesByKey.clear();
|
|
52501
52859
|
this.processingKeys.clear();
|
|
52502
52860
|
this.unregisterProcessCleanup();
|
|
52503
52861
|
log("[background-agent] Shutdown complete");
|
|
52504
52862
|
}
|
|
52863
|
+
enqueueNotificationForParent(parentSessionID, operation) {
|
|
52864
|
+
if (!parentSessionID) {
|
|
52865
|
+
return operation();
|
|
52866
|
+
}
|
|
52867
|
+
const previous = this.notificationQueueByParent.get(parentSessionID) ?? Promise.resolve();
|
|
52868
|
+
const current = previous.catch(() => {}).then(operation);
|
|
52869
|
+
this.notificationQueueByParent.set(parentSessionID, current);
|
|
52870
|
+
current.finally(() => {
|
|
52871
|
+
if (this.notificationQueueByParent.get(parentSessionID) === current) {
|
|
52872
|
+
this.notificationQueueByParent.delete(parentSessionID);
|
|
52873
|
+
}
|
|
52874
|
+
}).catch(() => {});
|
|
52875
|
+
return current;
|
|
52876
|
+
}
|
|
52505
52877
|
}
|
|
52506
52878
|
function registerProcessSignal(signal, handler, exitAfter) {
|
|
52507
52879
|
const listener = () => {
|
|
@@ -52514,7 +52886,7 @@ function registerProcessSignal(signal, handler, exitAfter) {
|
|
|
52514
52886
|
process.on(signal, listener);
|
|
52515
52887
|
return listener;
|
|
52516
52888
|
}
|
|
52517
|
-
function
|
|
52889
|
+
function getMessageDir13(sessionID) {
|
|
52518
52890
|
if (!existsSync53(MESSAGE_STORAGE))
|
|
52519
52891
|
return null;
|
|
52520
52892
|
const directPath = join64(MESSAGE_STORAGE, sessionID);
|
|
@@ -57914,15 +58286,130 @@ async function executeActions(actions, ctx) {
|
|
|
57914
58286
|
return { success: true, spawnedPaneId, results };
|
|
57915
58287
|
}
|
|
57916
58288
|
|
|
58289
|
+
// src/features/tmux-subagent/polling-manager.ts
|
|
58290
|
+
init_tmux();
|
|
58291
|
+
init_tmux();
|
|
58292
|
+
init_shared();
|
|
58293
|
+
var SESSION_TIMEOUT_MS2 = 10 * 60 * 1000;
|
|
58294
|
+
var MIN_STABILITY_TIME_MS3 = 10 * 1000;
|
|
58295
|
+
var STABLE_POLLS_REQUIRED = 3;
|
|
58296
|
+
|
|
58297
|
+
class TmuxPollingManager {
|
|
58298
|
+
client;
|
|
58299
|
+
sessions;
|
|
58300
|
+
closeSessionById;
|
|
58301
|
+
pollInterval;
|
|
58302
|
+
constructor(client2, sessions, closeSessionById) {
|
|
58303
|
+
this.client = client2;
|
|
58304
|
+
this.sessions = sessions;
|
|
58305
|
+
this.closeSessionById = closeSessionById;
|
|
58306
|
+
}
|
|
58307
|
+
startPolling() {
|
|
58308
|
+
if (this.pollInterval)
|
|
58309
|
+
return;
|
|
58310
|
+
this.pollInterval = setInterval(() => this.pollSessions(), POLL_INTERVAL_BACKGROUND_MS);
|
|
58311
|
+
log("[tmux-session-manager] polling started");
|
|
58312
|
+
}
|
|
58313
|
+
stopPolling() {
|
|
58314
|
+
if (this.pollInterval) {
|
|
58315
|
+
clearInterval(this.pollInterval);
|
|
58316
|
+
this.pollInterval = undefined;
|
|
58317
|
+
log("[tmux-session-manager] polling stopped");
|
|
58318
|
+
}
|
|
58319
|
+
}
|
|
58320
|
+
async pollSessions() {
|
|
58321
|
+
if (this.sessions.size === 0) {
|
|
58322
|
+
this.stopPolling();
|
|
58323
|
+
return;
|
|
58324
|
+
}
|
|
58325
|
+
try {
|
|
58326
|
+
const statusResult = await this.client.session.status({ path: undefined });
|
|
58327
|
+
const allStatuses = statusResult.data ?? {};
|
|
58328
|
+
log("[tmux-session-manager] pollSessions", {
|
|
58329
|
+
trackedSessions: Array.from(this.sessions.keys()),
|
|
58330
|
+
allStatusKeys: Object.keys(allStatuses)
|
|
58331
|
+
});
|
|
58332
|
+
const now = Date.now();
|
|
58333
|
+
const sessionsToClose = [];
|
|
58334
|
+
for (const [sessionId, tracked] of this.sessions.entries()) {
|
|
58335
|
+
const status = allStatuses[sessionId];
|
|
58336
|
+
const isIdle = status?.type === "idle";
|
|
58337
|
+
if (status) {
|
|
58338
|
+
tracked.lastSeenAt = new Date(now);
|
|
58339
|
+
}
|
|
58340
|
+
const missingSince = !status ? now - tracked.lastSeenAt.getTime() : 0;
|
|
58341
|
+
const missingTooLong = missingSince >= SESSION_MISSING_GRACE_MS;
|
|
58342
|
+
const isTimedOut = now - tracked.createdAt.getTime() > SESSION_TIMEOUT_MS2;
|
|
58343
|
+
const elapsedMs = now - tracked.createdAt.getTime();
|
|
58344
|
+
let shouldCloseViaStability = false;
|
|
58345
|
+
if (isIdle && elapsedMs >= MIN_STABILITY_TIME_MS3) {
|
|
58346
|
+
try {
|
|
58347
|
+
const messagesResult = await this.client.session.messages({
|
|
58348
|
+
path: { id: sessionId }
|
|
58349
|
+
});
|
|
58350
|
+
const currentMsgCount = Array.isArray(messagesResult.data) ? messagesResult.data.length : 0;
|
|
58351
|
+
if (tracked.lastMessageCount === currentMsgCount) {
|
|
58352
|
+
tracked.stableIdlePolls = (tracked.stableIdlePolls ?? 0) + 1;
|
|
58353
|
+
if (tracked.stableIdlePolls >= STABLE_POLLS_REQUIRED) {
|
|
58354
|
+
const recheckResult = await this.client.session.status({ path: undefined });
|
|
58355
|
+
const recheckStatuses = recheckResult.data ?? {};
|
|
58356
|
+
const recheckStatus = recheckStatuses[sessionId];
|
|
58357
|
+
if (recheckStatus?.type === "idle") {
|
|
58358
|
+
shouldCloseViaStability = true;
|
|
58359
|
+
} else {
|
|
58360
|
+
tracked.stableIdlePolls = 0;
|
|
58361
|
+
log("[tmux-session-manager] stability reached but session not idle on recheck, resetting", {
|
|
58362
|
+
sessionId,
|
|
58363
|
+
recheckStatus: recheckStatus?.type
|
|
58364
|
+
});
|
|
58365
|
+
}
|
|
58366
|
+
}
|
|
58367
|
+
} else {
|
|
58368
|
+
tracked.stableIdlePolls = 0;
|
|
58369
|
+
}
|
|
58370
|
+
tracked.lastMessageCount = currentMsgCount;
|
|
58371
|
+
} catch (msgErr) {
|
|
58372
|
+
log("[tmux-session-manager] failed to fetch messages for stability check", {
|
|
58373
|
+
sessionId,
|
|
58374
|
+
error: String(msgErr)
|
|
58375
|
+
});
|
|
58376
|
+
}
|
|
58377
|
+
} else if (!isIdle) {
|
|
58378
|
+
tracked.stableIdlePolls = 0;
|
|
58379
|
+
}
|
|
58380
|
+
log("[tmux-session-manager] session check", {
|
|
58381
|
+
sessionId,
|
|
58382
|
+
statusType: status?.type,
|
|
58383
|
+
isIdle,
|
|
58384
|
+
elapsedMs,
|
|
58385
|
+
stableIdlePolls: tracked.stableIdlePolls,
|
|
58386
|
+
lastMessageCount: tracked.lastMessageCount,
|
|
58387
|
+
missingSince,
|
|
58388
|
+
missingTooLong,
|
|
58389
|
+
isTimedOut,
|
|
58390
|
+
shouldCloseViaStability
|
|
58391
|
+
});
|
|
58392
|
+
if (shouldCloseViaStability || missingTooLong || isTimedOut) {
|
|
58393
|
+
sessionsToClose.push(sessionId);
|
|
58394
|
+
}
|
|
58395
|
+
}
|
|
58396
|
+
for (const sessionId of sessionsToClose) {
|
|
58397
|
+
log("[tmux-session-manager] closing session due to poll", { sessionId });
|
|
58398
|
+
await this.closeSessionById(sessionId);
|
|
58399
|
+
}
|
|
58400
|
+
} catch (err) {
|
|
58401
|
+
log("[tmux-session-manager] poll error", { error: String(err) });
|
|
58402
|
+
}
|
|
58403
|
+
}
|
|
58404
|
+
}
|
|
58405
|
+
|
|
57917
58406
|
// src/features/tmux-subagent/manager.ts
|
|
57918
58407
|
var defaultTmuxDeps = {
|
|
57919
58408
|
isInsideTmux,
|
|
57920
58409
|
getCurrentPaneId
|
|
57921
58410
|
};
|
|
57922
|
-
var
|
|
57923
|
-
var
|
|
57924
|
-
var STABLE_POLLS_REQUIRED = 3;
|
|
57925
|
-
|
|
58411
|
+
var SESSION_TIMEOUT_MS3 = 10 * 60 * 1000;
|
|
58412
|
+
var MIN_STABILITY_TIME_MS4 = 10 * 1000;
|
|
57926
58413
|
class TmuxSessionManager {
|
|
57927
58414
|
client;
|
|
57928
58415
|
tmuxConfig;
|
|
@@ -57930,8 +58417,8 @@ class TmuxSessionManager {
|
|
|
57930
58417
|
sourcePaneId;
|
|
57931
58418
|
sessions = new Map;
|
|
57932
58419
|
pendingSessions = new Set;
|
|
57933
|
-
pollInterval;
|
|
57934
58420
|
deps;
|
|
58421
|
+
pollingManager;
|
|
57935
58422
|
constructor(ctx, tmuxConfig, deps = defaultTmuxDeps) {
|
|
57936
58423
|
this.client = ctx.client;
|
|
57937
58424
|
this.tmuxConfig = tmuxConfig;
|
|
@@ -57939,6 +58426,7 @@ class TmuxSessionManager {
|
|
|
57939
58426
|
const defaultPort = process.env.OPENCODE_PORT ?? "4096";
|
|
57940
58427
|
this.serverUrl = ctx.serverUrl?.toString() ?? `http://localhost:${defaultPort}`;
|
|
57941
58428
|
this.sourcePaneId = deps.getCurrentPaneId();
|
|
58429
|
+
this.pollingManager = new TmuxPollingManager(this.client, this.sessions, this.closeSessionById.bind(this));
|
|
57942
58430
|
log("[tmux-session-manager] initialized", {
|
|
57943
58431
|
configEnabled: this.tmuxConfig.enabled,
|
|
57944
58432
|
tmuxConfig: this.tmuxConfig,
|
|
@@ -57987,6 +58475,9 @@ class TmuxSessionManager {
|
|
|
57987
58475
|
});
|
|
57988
58476
|
return false;
|
|
57989
58477
|
}
|
|
58478
|
+
async pollSessions() {
|
|
58479
|
+
await this.pollingManager.pollSessions();
|
|
58480
|
+
}
|
|
57990
58481
|
async onSessionCreated(event) {
|
|
57991
58482
|
const enabled = this.isEnabled();
|
|
57992
58483
|
log("[tmux-session-manager] onSessionCreated called", {
|
|
@@ -58081,7 +58572,7 @@ class TmuxSessionManager {
|
|
|
58081
58572
|
paneId: result.spawnedPaneId,
|
|
58082
58573
|
sessionReady
|
|
58083
58574
|
});
|
|
58084
|
-
this.startPolling();
|
|
58575
|
+
this.pollingManager.startPolling();
|
|
58085
58576
|
} else {
|
|
58086
58577
|
log("[tmux-session-manager] spawn failed", {
|
|
58087
58578
|
success: result.success,
|
|
@@ -58116,104 +58607,7 @@ class TmuxSessionManager {
|
|
|
58116
58607
|
}
|
|
58117
58608
|
this.sessions.delete(event.sessionID);
|
|
58118
58609
|
if (this.sessions.size === 0) {
|
|
58119
|
-
this.stopPolling();
|
|
58120
|
-
}
|
|
58121
|
-
}
|
|
58122
|
-
startPolling() {
|
|
58123
|
-
if (this.pollInterval)
|
|
58124
|
-
return;
|
|
58125
|
-
this.pollInterval = setInterval(() => this.pollSessions(), POLL_INTERVAL_BACKGROUND_MS);
|
|
58126
|
-
log("[tmux-session-manager] polling started");
|
|
58127
|
-
}
|
|
58128
|
-
stopPolling() {
|
|
58129
|
-
if (this.pollInterval) {
|
|
58130
|
-
clearInterval(this.pollInterval);
|
|
58131
|
-
this.pollInterval = undefined;
|
|
58132
|
-
log("[tmux-session-manager] polling stopped");
|
|
58133
|
-
}
|
|
58134
|
-
}
|
|
58135
|
-
async pollSessions() {
|
|
58136
|
-
if (this.sessions.size === 0) {
|
|
58137
|
-
this.stopPolling();
|
|
58138
|
-
return;
|
|
58139
|
-
}
|
|
58140
|
-
try {
|
|
58141
|
-
const statusResult = await this.client.session.status({ path: undefined });
|
|
58142
|
-
const allStatuses = statusResult.data ?? {};
|
|
58143
|
-
log("[tmux-session-manager] pollSessions", {
|
|
58144
|
-
trackedSessions: Array.from(this.sessions.keys()),
|
|
58145
|
-
allStatusKeys: Object.keys(allStatuses)
|
|
58146
|
-
});
|
|
58147
|
-
const now = Date.now();
|
|
58148
|
-
const sessionsToClose = [];
|
|
58149
|
-
for (const [sessionId, tracked] of this.sessions.entries()) {
|
|
58150
|
-
const status = allStatuses[sessionId];
|
|
58151
|
-
const isIdle = status?.type === "idle";
|
|
58152
|
-
if (status) {
|
|
58153
|
-
tracked.lastSeenAt = new Date(now);
|
|
58154
|
-
}
|
|
58155
|
-
const missingSince = !status ? now - tracked.lastSeenAt.getTime() : 0;
|
|
58156
|
-
const missingTooLong = missingSince >= SESSION_MISSING_GRACE_MS;
|
|
58157
|
-
const isTimedOut = now - tracked.createdAt.getTime() > SESSION_TIMEOUT_MS2;
|
|
58158
|
-
const elapsedMs = now - tracked.createdAt.getTime();
|
|
58159
|
-
let shouldCloseViaStability = false;
|
|
58160
|
-
if (isIdle && elapsedMs >= MIN_STABILITY_TIME_MS3) {
|
|
58161
|
-
try {
|
|
58162
|
-
const messagesResult = await this.client.session.messages({
|
|
58163
|
-
path: { id: sessionId }
|
|
58164
|
-
});
|
|
58165
|
-
const currentMsgCount = Array.isArray(messagesResult.data) ? messagesResult.data.length : 0;
|
|
58166
|
-
if (tracked.lastMessageCount === currentMsgCount) {
|
|
58167
|
-
tracked.stableIdlePolls = (tracked.stableIdlePolls ?? 0) + 1;
|
|
58168
|
-
if (tracked.stableIdlePolls >= STABLE_POLLS_REQUIRED) {
|
|
58169
|
-
const recheckResult = await this.client.session.status({ path: undefined });
|
|
58170
|
-
const recheckStatuses = recheckResult.data ?? {};
|
|
58171
|
-
const recheckStatus = recheckStatuses[sessionId];
|
|
58172
|
-
if (recheckStatus?.type === "idle") {
|
|
58173
|
-
shouldCloseViaStability = true;
|
|
58174
|
-
} else {
|
|
58175
|
-
tracked.stableIdlePolls = 0;
|
|
58176
|
-
log("[tmux-session-manager] stability reached but session not idle on recheck, resetting", {
|
|
58177
|
-
sessionId,
|
|
58178
|
-
recheckStatus: recheckStatus?.type
|
|
58179
|
-
});
|
|
58180
|
-
}
|
|
58181
|
-
}
|
|
58182
|
-
} else {
|
|
58183
|
-
tracked.stableIdlePolls = 0;
|
|
58184
|
-
}
|
|
58185
|
-
tracked.lastMessageCount = currentMsgCount;
|
|
58186
|
-
} catch (msgErr) {
|
|
58187
|
-
log("[tmux-session-manager] failed to fetch messages for stability check", {
|
|
58188
|
-
sessionId,
|
|
58189
|
-
error: String(msgErr)
|
|
58190
|
-
});
|
|
58191
|
-
}
|
|
58192
|
-
} else if (!isIdle) {
|
|
58193
|
-
tracked.stableIdlePolls = 0;
|
|
58194
|
-
}
|
|
58195
|
-
log("[tmux-session-manager] session check", {
|
|
58196
|
-
sessionId,
|
|
58197
|
-
statusType: status?.type,
|
|
58198
|
-
isIdle,
|
|
58199
|
-
elapsedMs,
|
|
58200
|
-
stableIdlePolls: tracked.stableIdlePolls,
|
|
58201
|
-
lastMessageCount: tracked.lastMessageCount,
|
|
58202
|
-
missingSince,
|
|
58203
|
-
missingTooLong,
|
|
58204
|
-
isTimedOut,
|
|
58205
|
-
shouldCloseViaStability
|
|
58206
|
-
});
|
|
58207
|
-
if (shouldCloseViaStability || missingTooLong || isTimedOut) {
|
|
58208
|
-
sessionsToClose.push(sessionId);
|
|
58209
|
-
}
|
|
58210
|
-
}
|
|
58211
|
-
for (const sessionId of sessionsToClose) {
|
|
58212
|
-
log("[tmux-session-manager] closing session due to poll", { sessionId });
|
|
58213
|
-
await this.closeSessionById(sessionId);
|
|
58214
|
-
}
|
|
58215
|
-
} catch (err) {
|
|
58216
|
-
log("[tmux-session-manager] poll error", { error: String(err) });
|
|
58610
|
+
this.pollingManager.stopPolling();
|
|
58217
58611
|
}
|
|
58218
58612
|
}
|
|
58219
58613
|
async closeSessionById(sessionId) {
|
|
@@ -58230,7 +58624,7 @@ class TmuxSessionManager {
|
|
|
58230
58624
|
}
|
|
58231
58625
|
this.sessions.delete(sessionId);
|
|
58232
58626
|
if (this.sessions.size === 0) {
|
|
58233
|
-
this.stopPolling();
|
|
58627
|
+
this.pollingManager.stopPolling();
|
|
58234
58628
|
}
|
|
58235
58629
|
}
|
|
58236
58630
|
createEventHandler() {
|
|
@@ -58239,7 +58633,7 @@ class TmuxSessionManager {
|
|
|
58239
58633
|
};
|
|
58240
58634
|
}
|
|
58241
58635
|
async cleanup() {
|
|
58242
|
-
this.stopPolling();
|
|
58636
|
+
this.pollingManager.stopPolling();
|
|
58243
58637
|
if (this.sessions.size > 0) {
|
|
58244
58638
|
log("[tmux-session-manager] closing all panes", { count: this.sessions.size });
|
|
58245
58639
|
const state2 = this.sourcePaneId ? await queryWindowState(this.sourcePaneId) : null;
|
|
@@ -60766,13 +61160,13 @@ ACCUMULATED WISDOM:
|
|
|
60766
61160
|
|
|
60767
61161
|
**For exploration (explore/librarian)**: ALWAYS background
|
|
60768
61162
|
\`\`\`typescript
|
|
60769
|
-
task(subagent_type="explore", run_in_background=true, ...)
|
|
60770
|
-
task(subagent_type="librarian", run_in_background=true, ...)
|
|
61163
|
+
task(subagent_type="explore", load_skills=[], run_in_background=true, ...)
|
|
61164
|
+
task(subagent_type="librarian", load_skills=[], run_in_background=true, ...)
|
|
60771
61165
|
\`\`\`
|
|
60772
61166
|
|
|
60773
61167
|
**For task execution**: NEVER background
|
|
60774
61168
|
\`\`\`typescript
|
|
60775
|
-
task(category="...", run_in_background=false, ...)
|
|
61169
|
+
task(category="...", load_skills=[...], run_in_background=false, ...)
|
|
60776
61170
|
\`\`\`
|
|
60777
61171
|
|
|
60778
61172
|
**Parallel task groups**: Invoke multiple in ONE message
|
|
@@ -61096,12 +61490,12 @@ ACCUMULATED WISDOM: [from notepad]
|
|
|
61096
61490
|
<parallel_execution>
|
|
61097
61491
|
**Exploration (explore/librarian)**: ALWAYS background
|
|
61098
61492
|
\`\`\`typescript
|
|
61099
|
-
task(subagent_type="explore", run_in_background=true, ...)
|
|
61493
|
+
task(subagent_type="explore", load_skills=[], run_in_background=true, ...)
|
|
61100
61494
|
\`\`\`
|
|
61101
61495
|
|
|
61102
61496
|
**Task execution**: NEVER background
|
|
61103
61497
|
\`\`\`typescript
|
|
61104
|
-
task(category="...", run_in_background=false, ...)
|
|
61498
|
+
task(category="...", load_skills=[...], run_in_background=false, ...)
|
|
61105
61499
|
\`\`\`
|
|
61106
61500
|
|
|
61107
61501
|
**Parallel task groups**: Invoke multiple in ONE message
|
|
@@ -62200,6 +62594,50 @@ var agentMetadata = {
|
|
|
62200
62594
|
function isFactory(source) {
|
|
62201
62595
|
return typeof source === "function";
|
|
62202
62596
|
}
|
|
62597
|
+
function sanitizeMarkdownTableCell(value) {
|
|
62598
|
+
return value.replace(/\r?\n/g, " ").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
62599
|
+
}
|
|
62600
|
+
function isRecord3(value) {
|
|
62601
|
+
return typeof value === "object" && value !== null;
|
|
62602
|
+
}
|
|
62603
|
+
function parseRegisteredAgentSummaries(input) {
|
|
62604
|
+
if (!Array.isArray(input))
|
|
62605
|
+
return [];
|
|
62606
|
+
const result = [];
|
|
62607
|
+
for (const item of input) {
|
|
62608
|
+
if (!isRecord3(item))
|
|
62609
|
+
continue;
|
|
62610
|
+
const name = typeof item.name === "string" ? item.name : undefined;
|
|
62611
|
+
if (!name)
|
|
62612
|
+
continue;
|
|
62613
|
+
const hidden = item.hidden;
|
|
62614
|
+
if (hidden === true)
|
|
62615
|
+
continue;
|
|
62616
|
+
const disabled = item.disabled;
|
|
62617
|
+
if (disabled === true)
|
|
62618
|
+
continue;
|
|
62619
|
+
const enabled = item.enabled;
|
|
62620
|
+
if (enabled === false)
|
|
62621
|
+
continue;
|
|
62622
|
+
const description = typeof item.description === "string" ? item.description : "";
|
|
62623
|
+
result.push({ name, description: sanitizeMarkdownTableCell(description) });
|
|
62624
|
+
}
|
|
62625
|
+
return result;
|
|
62626
|
+
}
|
|
62627
|
+
function buildCustomAgentMetadata(agentName, description) {
|
|
62628
|
+
const shortDescription = sanitizeMarkdownTableCell(truncateDescription(description));
|
|
62629
|
+
const safeAgentName = sanitizeMarkdownTableCell(agentName);
|
|
62630
|
+
return {
|
|
62631
|
+
category: "specialist",
|
|
62632
|
+
cost: "CHEAP",
|
|
62633
|
+
triggers: [
|
|
62634
|
+
{
|
|
62635
|
+
domain: `Custom agent: ${safeAgentName}`,
|
|
62636
|
+
trigger: shortDescription || "Use when this agent's description matches the task"
|
|
62637
|
+
}
|
|
62638
|
+
]
|
|
62639
|
+
};
|
|
62640
|
+
}
|
|
62203
62641
|
function buildAgent(source, model, categories, gitMasterConfig, browserProvider, disabledSkills) {
|
|
62204
62642
|
const base = isFactory(source) ? source(model) : source;
|
|
62205
62643
|
const categoryConfigs = categories ? { ...DEFAULT_CATEGORIES, ...categories } : DEFAULT_CATEGORIES;
|
|
@@ -62330,7 +62768,7 @@ function mapScopeToLocation(scope) {
|
|
|
62330
62768
|
return "project";
|
|
62331
62769
|
return "plugin";
|
|
62332
62770
|
}
|
|
62333
|
-
async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory, systemDefaultModel, categories, gitMasterConfig, discoveredSkills = [],
|
|
62771
|
+
async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory, systemDefaultModel, categories, gitMasterConfig, discoveredSkills = [], customAgentSummaries, browserProvider, uiSelectedModel, disabledSkills) {
|
|
62334
62772
|
const connectedProviders = readConnectedProvidersCache();
|
|
62335
62773
|
const availableModels = await fetchAvailableModels(undefined, {
|
|
62336
62774
|
connectedProviders: connectedProviders ?? undefined
|
|
@@ -62356,6 +62794,9 @@ async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, dir
|
|
|
62356
62794
|
location: mapScopeToLocation(skill2.scope)
|
|
62357
62795
|
}));
|
|
62358
62796
|
const availableSkills = [...builtinAvailable, ...discoveredAvailable];
|
|
62797
|
+
const registeredAgents = parseRegisteredAgentSummaries(customAgentSummaries);
|
|
62798
|
+
const builtinAgentNames = new Set(Object.keys(agentSources).map((n) => n.toLowerCase()));
|
|
62799
|
+
const disabledAgentNames = new Set(disabledAgents.map((n) => n.toLowerCase()));
|
|
62359
62800
|
const pendingAgentConfigs = new Map;
|
|
62360
62801
|
for (const [name, source] of Object.entries(agentSources)) {
|
|
62361
62802
|
const agentName = name;
|
|
@@ -62407,6 +62848,20 @@ async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, dir
|
|
|
62407
62848
|
});
|
|
62408
62849
|
}
|
|
62409
62850
|
}
|
|
62851
|
+
for (const agent of registeredAgents) {
|
|
62852
|
+
const lowerName = agent.name.toLowerCase();
|
|
62853
|
+
if (builtinAgentNames.has(lowerName))
|
|
62854
|
+
continue;
|
|
62855
|
+
if (disabledAgentNames.has(lowerName))
|
|
62856
|
+
continue;
|
|
62857
|
+
if (availableAgents.some((a) => a.name.toLowerCase() === lowerName))
|
|
62858
|
+
continue;
|
|
62859
|
+
availableAgents.push({
|
|
62860
|
+
name: agent.name,
|
|
62861
|
+
description: agent.description,
|
|
62862
|
+
metadata: buildCustomAgentMetadata(agent.name, agent.description)
|
|
62863
|
+
});
|
|
62864
|
+
}
|
|
62410
62865
|
const sisyphusOverride = agentOverrides["sisyphus"];
|
|
62411
62866
|
const sisyphusRequirement = AGENT_MODEL_REQUIREMENTS["sisyphus"];
|
|
62412
62867
|
const hasSisyphusExplicitConfig = sisyphusOverride !== undefined;
|
|
@@ -62451,7 +62906,9 @@ async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, dir
|
|
|
62451
62906
|
if (hephaestusResolution) {
|
|
62452
62907
|
const { model: hephaestusModel, variant: hephaestusResolvedVariant } = hephaestusResolution;
|
|
62453
62908
|
let hephaestusConfig = createHephaestusAgent(hephaestusModel, availableAgents, undefined, availableSkills, availableCategories);
|
|
62454
|
-
|
|
62909
|
+
if (!hephaestusOverride?.variant) {
|
|
62910
|
+
hephaestusConfig = { ...hephaestusConfig, variant: hephaestusResolvedVariant ?? "medium" };
|
|
62911
|
+
}
|
|
62455
62912
|
const hepOverrideCategory = hephaestusOverride?.category;
|
|
62456
62913
|
if (hepOverrideCategory) {
|
|
62457
62914
|
hephaestusConfig = applyCategoryOverride(hephaestusConfig, hepOverrideCategory, mergedCategories);
|
|
@@ -62862,8 +63319,8 @@ Or should I just note down this single fix?"
|
|
|
62862
63319
|
**Research First:**
|
|
62863
63320
|
\`\`\`typescript
|
|
62864
63321
|
// Prompt structure: CONTEXT (what I'm doing) + GOAL (what I'm trying to achieve) + QUESTION (what I need to know) + REQUEST (what to find)
|
|
62865
|
-
task(subagent_type="explore", prompt="I'm refactoring [target] and need to understand its impact scope before making changes. Find all usages via lsp_find_references - show calling code, patterns of use, and potential breaking points.", run_in_background=true)
|
|
62866
|
-
task(subagent_type="explore", prompt="I'm about to modify [affected code] and need to ensure behavior preservation. Find existing test coverage - which tests exercise this code, what assertions exist, and any gaps in coverage.", run_in_background=true)
|
|
63322
|
+
task(subagent_type="explore", load_skills=[], prompt="I'm refactoring [target] and need to understand its impact scope before making changes. Find all usages via lsp_find_references - show calling code, patterns of use, and potential breaking points.", run_in_background=true)
|
|
63323
|
+
task(subagent_type="explore", load_skills=[], prompt="I'm about to modify [affected code] and need to ensure behavior preservation. Find existing test coverage - which tests exercise this code, what assertions exist, and any gaps in coverage.", run_in_background=true)
|
|
62867
63324
|
\`\`\`
|
|
62868
63325
|
|
|
62869
63326
|
**Interview Focus:**
|
|
@@ -62887,9 +63344,9 @@ task(subagent_type="explore", prompt="I'm about to modify [affected code] and ne
|
|
|
62887
63344
|
\`\`\`typescript
|
|
62888
63345
|
// Launch BEFORE asking user questions
|
|
62889
63346
|
// Prompt structure: CONTEXT + GOAL + QUESTION + REQUEST
|
|
62890
|
-
task(subagent_type="explore", prompt="I'm building a new [feature] and want to maintain codebase consistency. Find similar implementations in this project - their structure, patterns used, and conventions to follow.", run_in_background=true)
|
|
62891
|
-
task(subagent_type="explore", prompt="I'm adding [feature type] to the project and need to understand existing conventions. Find how similar features are organized - file structure, naming patterns, and architectural approach.", run_in_background=true)
|
|
62892
|
-
task(subagent_type="librarian", prompt="I'm implementing [technology] and want to follow established best practices. Find official documentation and community recommendations - setup patterns, common pitfalls, and production-ready examples.", run_in_background=true)
|
|
63347
|
+
task(subagent_type="explore", load_skills=[], prompt="I'm building a new [feature] and want to maintain codebase consistency. Find similar implementations in this project - their structure, patterns used, and conventions to follow.", run_in_background=true)
|
|
63348
|
+
task(subagent_type="explore", load_skills=[], prompt="I'm adding [feature type] to the project and need to understand existing conventions. Find how similar features are organized - file structure, naming patterns, and architectural approach.", run_in_background=true)
|
|
63349
|
+
task(subagent_type="librarian", load_skills=[], prompt="I'm implementing [technology] and want to follow established best practices. Find official documentation and community recommendations - setup patterns, common pitfalls, and production-ready examples.", run_in_background=true)
|
|
62893
63350
|
\`\`\`
|
|
62894
63351
|
|
|
62895
63352
|
**Interview Focus** (AFTER research):
|
|
@@ -62928,7 +63385,7 @@ Based on your stack, I'd recommend NextAuth.js - it integrates well with Next.js
|
|
|
62928
63385
|
|
|
62929
63386
|
Run this check:
|
|
62930
63387
|
\`\`\`typescript
|
|
62931
|
-
task(subagent_type="explore", prompt="I'm assessing this project's test setup before planning work that may require TDD. I need to understand what testing capabilities exist. Find test infrastructure: package.json test scripts, config files (jest.config, vitest.config, pytest.ini), and existing test files. Report: 1) Does test infra exist? 2) What framework? 3) Example test patterns.", run_in_background=true)
|
|
63388
|
+
task(subagent_type="explore", load_skills=[], prompt="I'm assessing this project's test setup before planning work that may require TDD. I need to understand what testing capabilities exist. Find test infrastructure: package.json test scripts, config files (jest.config, vitest.config, pytest.ini), and existing test files. Report: 1) Does test infra exist? 2) What framework? 3) Example test patterns.", run_in_background=true)
|
|
62932
63389
|
\`\`\`
|
|
62933
63390
|
|
|
62934
63391
|
#### Step 2: Ask the Test Question (MANDATORY)
|
|
@@ -63026,13 +63483,13 @@ Add to draft immediately:
|
|
|
63026
63483
|
|
|
63027
63484
|
**Research First:**
|
|
63028
63485
|
\`\`\`typescript
|
|
63029
|
-
task(subagent_type="explore", prompt="I'm planning architectural changes and need to understand the current system design. Find existing architecture: module boundaries, dependency patterns, data flow, and key abstractions used.", run_in_background=true)
|
|
63030
|
-
task(subagent_type="librarian", prompt="I'm designing architecture for [domain] and want to make informed decisions. Find architectural best practices - proven patterns, trade-offs, and lessons learned from similar systems.", run_in_background=true)
|
|
63486
|
+
task(subagent_type="explore", load_skills=[], prompt="I'm planning architectural changes and need to understand the current system design. Find existing architecture: module boundaries, dependency patterns, data flow, and key abstractions used.", run_in_background=true)
|
|
63487
|
+
task(subagent_type="librarian", load_skills=[], prompt="I'm designing architecture for [domain] and want to make informed decisions. Find architectural best practices - proven patterns, trade-offs, and lessons learned from similar systems.", run_in_background=true)
|
|
63031
63488
|
\`\`\`
|
|
63032
63489
|
|
|
63033
63490
|
**Oracle Consultation** (recommend when stakes are high):
|
|
63034
63491
|
\`\`\`typescript
|
|
63035
|
-
task(subagent_type="oracle", prompt="Architecture consultation needed: [context]...", run_in_background=false)
|
|
63492
|
+
task(subagent_type="oracle", load_skills=[], prompt="Architecture consultation needed: [context]...", run_in_background=false)
|
|
63036
63493
|
\`\`\`
|
|
63037
63494
|
|
|
63038
63495
|
**Interview Focus:**
|
|
@@ -63049,9 +63506,9 @@ task(subagent_type="oracle", prompt="Architecture consultation needed: [context]
|
|
|
63049
63506
|
|
|
63050
63507
|
**Parallel Investigation:**
|
|
63051
63508
|
\`\`\`typescript
|
|
63052
|
-
task(subagent_type="explore", prompt="I'm researching how to implement [feature] and need to understand current approach. Find how X is currently handled in this codebase - implementation details, edge cases covered, and any known limitations.", run_in_background=true)
|
|
63053
|
-
task(subagent_type="librarian", prompt="I'm implementing Y and need authoritative guidance. Find official documentation - API reference, configuration options, and recommended usage patterns.", run_in_background=true)
|
|
63054
|
-
task(subagent_type="librarian", prompt="I'm looking for battle-tested implementations of Z. Find open source projects that solve this - focus on production-quality code, how they handle edge cases, and any gotchas documented.", run_in_background=true)
|
|
63509
|
+
task(subagent_type="explore", load_skills=[], prompt="I'm researching how to implement [feature] and need to understand current approach. Find how X is currently handled in this codebase - implementation details, edge cases covered, and any known limitations.", run_in_background=true)
|
|
63510
|
+
task(subagent_type="librarian", load_skills=[], prompt="I'm implementing Y and need authoritative guidance. Find official documentation - API reference, configuration options, and recommended usage patterns.", run_in_background=true)
|
|
63511
|
+
task(subagent_type="librarian", load_skills=[], prompt="I'm looking for battle-tested implementations of Z. Find open source projects that solve this - focus on production-quality code, how they handle edge cases, and any gotchas documented.", run_in_background=true)
|
|
63055
63512
|
\`\`\`
|
|
63056
63513
|
|
|
63057
63514
|
**Interview Focus:**
|
|
@@ -63077,17 +63534,17 @@ task(subagent_type="librarian", prompt="I'm looking for battle-tested implementa
|
|
|
63077
63534
|
|
|
63078
63535
|
**For Understanding Codebase:**
|
|
63079
63536
|
\`\`\`typescript
|
|
63080
|
-
task(subagent_type="explore", prompt="I'm working on [topic] and need to understand how it's organized in this project. Find all related files - show the structure, patterns used, and conventions I should follow.", run_in_background=true)
|
|
63537
|
+
task(subagent_type="explore", load_skills=[], prompt="I'm working on [topic] and need to understand how it's organized in this project. Find all related files - show the structure, patterns used, and conventions I should follow.", run_in_background=true)
|
|
63081
63538
|
\`\`\`
|
|
63082
63539
|
|
|
63083
63540
|
**For External Knowledge:**
|
|
63084
63541
|
\`\`\`typescript
|
|
63085
|
-
task(subagent_type="librarian", prompt="I'm integrating [library] and need to understand [specific feature]. Find official documentation - API details, configuration options, and recommended best practices.", run_in_background=true)
|
|
63542
|
+
task(subagent_type="librarian", load_skills=[], prompt="I'm integrating [library] and need to understand [specific feature]. Find official documentation - API details, configuration options, and recommended best practices.", run_in_background=true)
|
|
63086
63543
|
\`\`\`
|
|
63087
63544
|
|
|
63088
63545
|
**For Implementation Examples:**
|
|
63089
63546
|
\`\`\`typescript
|
|
63090
|
-
task(subagent_type="librarian", prompt="I'm implementing [feature] and want to learn from existing solutions. Find open source implementations - focus on production-quality code, architecture decisions, and common patterns.", run_in_background=true)
|
|
63547
|
+
task(subagent_type="librarian", load_skills=[], prompt="I'm implementing [feature] and want to learn from existing solutions. Find open source implementations - focus on production-quality code, architecture decisions, and common patterns.", run_in_background=true)
|
|
63091
63548
|
\`\`\`
|
|
63092
63549
|
|
|
63093
63550
|
## Interview Mode Anti-Patterns
|
|
@@ -63192,6 +63649,7 @@ todoWrite([
|
|
|
63192
63649
|
\`\`\`typescript
|
|
63193
63650
|
task(
|
|
63194
63651
|
subagent_type="metis",
|
|
63652
|
+
load_skills=[],
|
|
63195
63653
|
prompt=\`Review this planning session before I generate the work plan:
|
|
63196
63654
|
|
|
63197
63655
|
**User's Goal**: {summarize what user wants}
|
|
@@ -63364,6 +63822,7 @@ var PROMETHEUS_HIGH_ACCURACY_MODE = `# PHASE 3: PLAN GENERATION
|
|
|
63364
63822
|
while (true) {
|
|
63365
63823
|
const result = task(
|
|
63366
63824
|
subagent_type="momus",
|
|
63825
|
+
load_skills=[],
|
|
63367
63826
|
prompt=".sisyphus/plans/{name}.md",
|
|
63368
63827
|
run_in_background=false
|
|
63369
63828
|
)
|
|
@@ -64735,9 +65194,8 @@ function createWebsearchConfig(config3) {
|
|
|
64735
65194
|
}
|
|
64736
65195
|
return {
|
|
64737
65196
|
type: "remote",
|
|
64738
|
-
url: "https://mcp.exa.ai/mcp?tools=web_search_exa",
|
|
65197
|
+
url: process.env.EXA_API_KEY ? `https://mcp.exa.ai/mcp?tools=web_search_exa&exaApiKey=${encodeURIComponent(process.env.EXA_API_KEY)}` : "https://mcp.exa.ai/mcp?tools=web_search_exa",
|
|
64739
65198
|
enabled: true,
|
|
64740
|
-
headers: process.env.EXA_API_KEY ? { "x-api-key": process.env.EXA_API_KEY } : undefined,
|
|
64741
65199
|
oauth: false
|
|
64742
65200
|
};
|
|
64743
65201
|
}
|
|
@@ -64780,6 +65238,31 @@ init_shared();
|
|
|
64780
65238
|
init_migration();
|
|
64781
65239
|
init_model_requirements();
|
|
64782
65240
|
init_constants4();
|
|
65241
|
+
|
|
65242
|
+
// src/plugin-handlers/plan-model-inheritance.ts
|
|
65243
|
+
var MODEL_SETTINGS_KEYS = [
|
|
65244
|
+
"model",
|
|
65245
|
+
"variant",
|
|
65246
|
+
"temperature",
|
|
65247
|
+
"top_p",
|
|
65248
|
+
"maxTokens",
|
|
65249
|
+
"thinking",
|
|
65250
|
+
"reasoningEffort",
|
|
65251
|
+
"textVerbosity",
|
|
65252
|
+
"providerOptions"
|
|
65253
|
+
];
|
|
65254
|
+
function buildPlanDemoteConfig(prometheusConfig, planOverride) {
|
|
65255
|
+
const modelSettings = {};
|
|
65256
|
+
for (const key of MODEL_SETTINGS_KEYS) {
|
|
65257
|
+
const value = planOverride?.[key] ?? prometheusConfig?.[key];
|
|
65258
|
+
if (value !== undefined) {
|
|
65259
|
+
modelSettings[key] = value;
|
|
65260
|
+
}
|
|
65261
|
+
}
|
|
65262
|
+
return { mode: "subagent", ...modelSettings };
|
|
65263
|
+
}
|
|
65264
|
+
|
|
65265
|
+
// src/plugin-handlers/config-handler.ts
|
|
64783
65266
|
function resolveCategoryConfig2(categoryName, userCategories) {
|
|
64784
65267
|
return userCategories?.[categoryName] ?? DEFAULT_CATEGORIES[categoryName];
|
|
64785
65268
|
}
|
|
@@ -64884,7 +65367,24 @@ function createConfigHandler(deps) {
|
|
|
64884
65367
|
const browserProvider = pluginConfig.browser_automation_engine?.provider ?? "playwright";
|
|
64885
65368
|
const currentModel = config3.model;
|
|
64886
65369
|
const disabledSkills = new Set(pluginConfig.disabled_skills ?? []);
|
|
64887
|
-
const
|
|
65370
|
+
const configAgent = config3.agent;
|
|
65371
|
+
function isRecord4(value) {
|
|
65372
|
+
return typeof value === "object" && value !== null;
|
|
65373
|
+
}
|
|
65374
|
+
function buildCustomAgentSummaryInput(agents) {
|
|
65375
|
+
if (!agents)
|
|
65376
|
+
return [];
|
|
65377
|
+
const result = [];
|
|
65378
|
+
for (const [name, value] of Object.entries(agents)) {
|
|
65379
|
+
if (!isRecord4(value))
|
|
65380
|
+
continue;
|
|
65381
|
+
const description = typeof value.description === "string" ? value.description : "";
|
|
65382
|
+
const hidden = value.hidden === true;
|
|
65383
|
+
const disabled = value.disabled === true || value.enabled === false;
|
|
65384
|
+
result.push({ name, description, hidden, disabled });
|
|
65385
|
+
}
|
|
65386
|
+
return result;
|
|
65387
|
+
}
|
|
64888
65388
|
const userAgents = pluginConfig.claude_code?.agents ?? true ? loadUserAgents() : {};
|
|
64889
65389
|
const projectAgents = pluginConfig.claude_code?.agents ?? true ? loadProjectAgents() : {};
|
|
64890
65390
|
const rawPluginAgents = pluginComponents.agents;
|
|
@@ -64892,12 +65392,18 @@ function createConfigHandler(deps) {
|
|
|
64892
65392
|
k,
|
|
64893
65393
|
v ? migrateAgentConfig(v) : v
|
|
64894
65394
|
]));
|
|
65395
|
+
const customAgentSummaries = [
|
|
65396
|
+
...buildCustomAgentSummaryInput(configAgent),
|
|
65397
|
+
...buildCustomAgentSummaryInput(userAgents),
|
|
65398
|
+
...buildCustomAgentSummaryInput(projectAgents),
|
|
65399
|
+
...buildCustomAgentSummaryInput(pluginAgents)
|
|
65400
|
+
];
|
|
65401
|
+
const builtinAgents = await createBuiltinAgents(migratedDisabledAgents, pluginConfig.agents, ctx.directory, undefined, pluginConfig.categories, pluginConfig.git_master, allDiscoveredSkills, customAgentSummaries, browserProvider, currentModel, disabledSkills);
|
|
64895
65402
|
const isSisyphusEnabled = pluginConfig.sisyphus_agent?.disabled !== true;
|
|
64896
65403
|
const builderEnabled = pluginConfig.sisyphus_agent?.default_builder_enabled ?? false;
|
|
64897
65404
|
const plannerEnabled = pluginConfig.sisyphus_agent?.planner_enabled ?? true;
|
|
64898
65405
|
const replacePlan = pluginConfig.sisyphus_agent?.replace_plan ?? true;
|
|
64899
65406
|
const shouldDemotePlan = plannerEnabled && replacePlan;
|
|
64900
|
-
const configAgent = config3.agent;
|
|
64901
65407
|
if (isSisyphusEnabled && builtinAgents.sisyphus) {
|
|
64902
65408
|
config3.default_agent = "sisyphus";
|
|
64903
65409
|
const agentConfig = {
|
|
@@ -64984,9 +65490,7 @@ function createConfigHandler(deps) {
|
|
|
64984
65490
|
value ? migrateAgentConfig(value) : value
|
|
64985
65491
|
])) : {};
|
|
64986
65492
|
const migratedBuild = configAgent?.build ? migrateAgentConfig(configAgent.build) : {};
|
|
64987
|
-
const planDemoteConfig = shouldDemotePlan ?
|
|
64988
|
-
mode: "subagent"
|
|
64989
|
-
} : undefined;
|
|
65493
|
+
const planDemoteConfig = shouldDemotePlan ? buildPlanDemoteConfig(agentConfig["prometheus"], pluginConfig.agents?.plan) : undefined;
|
|
64990
65494
|
config3.agent = {
|
|
64991
65495
|
...agentConfig,
|
|
64992
65496
|
...Object.fromEntries(Object.entries(builtinAgents).filter(([k]) => k !== "sisyphus")),
|
|
@@ -65022,6 +65526,7 @@ function createConfigHandler(deps) {
|
|
|
65022
65526
|
};
|
|
65023
65527
|
const isCliRunMode = process.env.OPENCODE_CLI_RUN_MODE === "true";
|
|
65024
65528
|
const questionPermission = isCliRunMode ? "deny" : "allow";
|
|
65529
|
+
const todoPermission = pluginConfig.experimental?.task_system ? { todowrite: "deny", todoread: "deny" } : {};
|
|
65025
65530
|
if (agentResult.librarian) {
|
|
65026
65531
|
const agent = agentResult.librarian;
|
|
65027
65532
|
agent.permission = { ...agent.permission, "grep_app_*": "allow" };
|
|
@@ -65032,23 +65537,23 @@ function createConfigHandler(deps) {
|
|
|
65032
65537
|
}
|
|
65033
65538
|
if (agentResult["atlas"]) {
|
|
65034
65539
|
const agent = agentResult["atlas"];
|
|
65035
|
-
agent.permission = { ...agent.permission, task: "allow", call_omo_agent: "deny", "task_*": "allow", teammate: "allow" };
|
|
65540
|
+
agent.permission = { ...agent.permission, ...todoPermission, task: "allow", call_omo_agent: "deny", "task_*": "allow", teammate: "allow" };
|
|
65036
65541
|
}
|
|
65037
65542
|
if (agentResult.sisyphus) {
|
|
65038
65543
|
const agent = agentResult.sisyphus;
|
|
65039
|
-
agent.permission = { ...agent.permission, call_omo_agent: "deny", task: "allow", question: questionPermission, "task_*": "allow", teammate: "allow" };
|
|
65544
|
+
agent.permission = { ...agent.permission, ...todoPermission, call_omo_agent: "deny", task: "allow", question: questionPermission, "task_*": "allow", teammate: "allow" };
|
|
65040
65545
|
}
|
|
65041
65546
|
if (agentResult.hephaestus) {
|
|
65042
65547
|
const agent = agentResult.hephaestus;
|
|
65043
|
-
agent.permission = { ...agent.permission, call_omo_agent: "deny", task: "allow", question: questionPermission };
|
|
65548
|
+
agent.permission = { ...agent.permission, ...todoPermission, call_omo_agent: "deny", task: "allow", question: questionPermission };
|
|
65044
65549
|
}
|
|
65045
65550
|
if (agentResult["prometheus"]) {
|
|
65046
65551
|
const agent = agentResult["prometheus"];
|
|
65047
|
-
agent.permission = { ...agent.permission, call_omo_agent: "deny", task: "allow", question: questionPermission, "task_*": "allow", teammate: "allow" };
|
|
65552
|
+
agent.permission = { ...agent.permission, ...todoPermission, call_omo_agent: "deny", task: "allow", question: questionPermission, "task_*": "allow", teammate: "allow" };
|
|
65048
65553
|
}
|
|
65049
65554
|
if (agentResult["sisyphus-junior"]) {
|
|
65050
65555
|
const agent = agentResult["sisyphus-junior"];
|
|
65051
|
-
agent.permission = { ...agent.permission, task: "allow", "task_*": "allow", teammate: "allow" };
|
|
65556
|
+
agent.permission = { ...agent.permission, ...todoPermission, task: "allow", "task_*": "allow", teammate: "allow" };
|
|
65052
65557
|
}
|
|
65053
65558
|
config3.permission = {
|
|
65054
65559
|
...config3.permission,
|
|
@@ -65264,10 +65769,6 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
65264
65769
|
backgroundManager,
|
|
65265
65770
|
config: pluginConfig.babysitting
|
|
65266
65771
|
}), { enabled: safeHookEnabled }) : null;
|
|
65267
|
-
if (sessionRecovery && todoContinuationEnforcer) {
|
|
65268
|
-
sessionRecovery.setOnAbortCallback(todoContinuationEnforcer.markRecovering);
|
|
65269
|
-
sessionRecovery.setOnRecoveryCompleteCallback(todoContinuationEnforcer.markRecoveryComplete);
|
|
65270
|
-
}
|
|
65271
65772
|
const backgroundNotificationHook = isHookEnabled("background-notification") ? safeCreateHook("background-notification", () => createBackgroundNotificationHook(backgroundManager), { enabled: safeHookEnabled }) : null;
|
|
65272
65773
|
const backgroundTools = createBackgroundTools(backgroundManager, ctx.client);
|
|
65273
65774
|
const callOmoAgent = createCallOmoAgent(ctx, backgroundManager);
|
|
@@ -65322,6 +65823,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
65322
65823
|
disabledSkills,
|
|
65323
65824
|
availableCategories,
|
|
65324
65825
|
availableSkills,
|
|
65826
|
+
agentOverrides: pluginConfig.agents,
|
|
65325
65827
|
onSyncSessionCreated: async (event) => {
|
|
65326
65828
|
log("[index] onSyncSessionCreated callback", {
|
|
65327
65829
|
sessionID: event.sessionID,
|
|
@@ -65367,6 +65869,14 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
65367
65869
|
modelCacheState
|
|
65368
65870
|
});
|
|
65369
65871
|
const taskSystemEnabled = pluginConfig.experimental?.task_system ?? false;
|
|
65872
|
+
if (sessionRecovery && todoContinuationEnforcer) {
|
|
65873
|
+
sessionRecovery.setOnAbortCallback((sessionID) => {
|
|
65874
|
+
todoContinuationEnforcer?.markRecovering(sessionID);
|
|
65875
|
+
});
|
|
65876
|
+
sessionRecovery.setOnRecoveryCompleteCallback((sessionID) => {
|
|
65877
|
+
todoContinuationEnforcer?.markRecoveryComplete(sessionID);
|
|
65878
|
+
});
|
|
65879
|
+
}
|
|
65370
65880
|
const taskToolsRecord = taskSystemEnabled ? {
|
|
65371
65881
|
task_create: createTaskCreateTool(pluginConfig, ctx),
|
|
65372
65882
|
task_get: createTaskGetTool(pluginConfig),
|