opencode-matrixx 2.6.4 → 2.6.6
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/oracle/behavioral-summary.d.ts +1 -1
- package/dist/agents/oracle/high-accuracy-mode.d.ts +1 -1
- package/dist/agents/oracle/interview-mode.d.ts +1 -1
- package/dist/agents/oracle/plan-generation.d.ts +1 -1
- package/dist/agents/oracle/plan-template.d.ts +1 -1
- package/dist/agents/oracle/system-prompt.d.ts +1 -1
- package/dist/cli/setup/index.d.ts +1 -0
- package/dist/cli/setup/preset-wizard.d.ts +36 -0
- package/dist/cli/setup/types.d.ts +5 -0
- package/dist/cli.js +499 -64
- package/dist/config/index.d.ts +0 -1
- package/dist/config/migrations/model-migration.d.ts +1 -2
- package/dist/config/preset-applier.d.ts +17 -0
- package/dist/config/schema/agent-overrides.d.ts +0 -133
- package/dist/config/schema/categories.d.ts +0 -14
- package/dist/config/schema/commands.d.ts +1 -1
- package/dist/config/schema/hooks.d.ts +1 -0
- package/dist/config/schema/matrixx-config.d.ts +29 -144
- package/dist/config/schema/model-config.d.ts +0 -22
- package/dist/config/schema/model-presets.d.ts +40 -0
- package/dist/config/schema/task.d.ts +6 -0
- package/dist/config/schema/tasks.d.ts +25 -0
- package/dist/config/schema.d.ts +3 -0
- package/dist/create-hooks.d.ts +1 -0
- package/dist/features/background-agent/handle-index.d.ts +63 -0
- package/dist/features/background-agent/handle-index.test.d.ts +1 -0
- package/dist/features/background-agent/index.d.ts +1 -0
- package/dist/features/background-agent/manager-handles.test.d.ts +1 -0
- package/dist/features/background-agent/manager.d.ts +12 -0
- package/dist/features/builtin-commands/templates/preset.d.ts +1 -0
- package/dist/features/builtin-commands/types.d.ts +1 -1
- package/dist/features/preset-state/index.d.ts +1 -0
- package/dist/features/preset-state/manager.d.ts +5 -0
- package/dist/features/session-state/state.d.ts +6 -0
- package/dist/features/task-storage/types.d.ts +2 -0
- package/dist/hooks/background-task-blocker/hook.d.ts +10 -0
- package/dist/hooks/background-task-blocker/index.d.ts +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/json-error-recovery/hook.d.ts +1 -1
- package/dist/hooks/task-continuation-enforcer/continuation-injection.test.d.ts +1 -0
- package/dist/hooks/task-continuation-enforcer/staleness.d.ts +7 -1
- package/dist/hooks/task-continuation-enforcer/todo.d.ts +8 -0
- package/dist/hooks/task-continuation-enforcer/todo.test.d.ts +1 -0
- package/dist/hooks/tasks-todowrite-disabler/constants.d.ts +1 -1
- package/dist/index.js +923 -624
- package/dist/matrixx.schema.json +114 -244
- package/dist/mcp/types.d.ts +1 -1
- package/dist/mcp/{grep-app.d.ts → white-rabbit.d.ts} +1 -1
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
- package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
- package/dist/shared/config-errors.d.ts +3 -0
- package/dist/shared/context-mode-prompt.d.ts +12 -0
- package/dist/shared/context-mode-prompt.test.d.ts +1 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/merge-categories.d.ts +1 -16
- package/dist/shared/task-system-gating.d.ts +10 -0
- package/dist/tools/background-task/constants.d.ts +0 -1
- package/dist/tools/background-task/index.d.ts +1 -1
- package/dist/tools/background-task/tools.d.ts +0 -1
- package/dist/tools/background-task/types.d.ts +0 -5
- package/dist/tools/delegate-task/categories.d.ts +3 -18
- package/dist/tools/delegate-task/complexity-constants.d.ts +2 -7
- package/dist/tools/delegate-task/executor-types.d.ts +7 -2
- package/dist/tools/delegate-task/sync-task.test.d.ts +1 -0
- package/dist/tools/delegate-task/timing.d.ts +5 -0
- package/dist/tools/delegate-task/timing.test.d.ts +1 -0
- package/dist/tools/delegate-task/types.d.ts +5 -2
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/preset/index.d.ts +1 -0
- package/dist/tools/preset/tools.d.ts +7 -0
- package/dist/tools/task/constants.d.ts +1 -0
- package/package.json +1 -1
- package/dist/config/presets.d.ts +0 -4
- package/dist/config/resolve-tiers.d.ts +0 -32
- package/dist/features/builtin-commands/templates/profile.d.ts +0 -1
- package/dist/shared/model-tiers.d.ts +0 -44
- package/dist/shared/tier-resolver.d.ts +0 -26
- package/dist/tools/background-task/create-background-task.d.ts +0 -3
package/dist/index.js
CHANGED
|
@@ -29560,7 +29560,7 @@ var init_ulw_research = __esm(() => {
|
|
|
29560
29560
|
"look_at",
|
|
29561
29561
|
"delegate_agent",
|
|
29562
29562
|
"websearch_web_search_exa",
|
|
29563
|
-
"
|
|
29563
|
+
"white_rabbit_searchGitHub",
|
|
29564
29564
|
"context7_resolve-library-id",
|
|
29565
29565
|
"context7_query-docs"
|
|
29566
29566
|
],
|
|
@@ -29671,7 +29671,7 @@ task(
|
|
|
29671
29671
|
prompt="""
|
|
29672
29672
|
Search open source repositories for implementations related to: <CURRENT_LEAD>
|
|
29673
29673
|
|
|
29674
|
-
Use
|
|
29674
|
+
Use white_rabbit_searchGitHub to find real-world code patterns.
|
|
29675
29675
|
Focus on:
|
|
29676
29676
|
- Production implementations in similar projects
|
|
29677
29677
|
- Reference implementations or canonical examples
|
|
@@ -44410,31 +44410,16 @@ var ModelFallbackEntrySchema = exports_external.object({
|
|
|
44410
44410
|
model: exports_external.string().min(1),
|
|
44411
44411
|
variant: exports_external.string().optional()
|
|
44412
44412
|
});
|
|
44413
|
-
var TierSpecSchema = exports_external.object({
|
|
44414
|
-
providerPriority: exports_external.array(exports_external.string()),
|
|
44415
|
-
modelPattern: exports_external.string().refine((s) => {
|
|
44416
|
-
try {
|
|
44417
|
-
new RegExp(s);
|
|
44418
|
-
return true;
|
|
44419
|
-
} catch {
|
|
44420
|
-
return false;
|
|
44421
|
-
}
|
|
44422
|
-
}, { message: "Invalid regex pattern" }),
|
|
44423
|
-
fallbackTier: exports_external.string().optional(),
|
|
44424
|
-
fallback: exports_external.array(ModelFallbackEntrySchema).optional()
|
|
44425
|
-
});
|
|
44426
|
-
var TiersSchema = exports_external.record(exports_external.string(), TierSpecSchema);
|
|
44427
44413
|
var complexityDowngradeValueSchema = exports_external.string().refine((s) => {
|
|
44428
|
-
if (s.
|
|
44429
|
-
|
|
44430
|
-
|
|
44431
|
-
|
|
44432
|
-
if (s.includes("
|
|
44433
|
-
|
|
44434
|
-
|
|
44435
|
-
|
|
44436
|
-
|
|
44437
|
-
}, { message: "Must be tier:<name> or <provider>/<model>" });
|
|
44414
|
+
if (!s.includes("/"))
|
|
44415
|
+
return false;
|
|
44416
|
+
if (s.startsWith("/") || s.endsWith("/"))
|
|
44417
|
+
return false;
|
|
44418
|
+
if (s.includes("//"))
|
|
44419
|
+
return false;
|
|
44420
|
+
const parts = s.split("/");
|
|
44421
|
+
return parts.length >= 2 && parts.every((p) => p.length > 0);
|
|
44422
|
+
}, { message: "Must be <provider>/<model>" });
|
|
44438
44423
|
var ComplexityDowngradesSchema = exports_external.record(exports_external.string(), exports_external.record(exports_external.string(), complexityDowngradeValueSchema));
|
|
44439
44424
|
var AgentModelRequirementsSchema = exports_external.object({
|
|
44440
44425
|
fallbackChain: exports_external.array(ModelFallbackEntrySchema),
|
|
@@ -44861,6 +44846,12 @@ async function resolveCommandsInText(text, depth = 0, maxDepth = 3) {
|
|
|
44861
44846
|
return resolved;
|
|
44862
44847
|
}
|
|
44863
44848
|
// src/shared/config-errors.ts
|
|
44849
|
+
class ConfigError extends Error {
|
|
44850
|
+
constructor(message) {
|
|
44851
|
+
super(message);
|
|
44852
|
+
this.name = "ConfigError";
|
|
44853
|
+
}
|
|
44854
|
+
}
|
|
44864
44855
|
var configLoadErrors = [];
|
|
44865
44856
|
function getConfigLoadErrors() {
|
|
44866
44857
|
return configLoadErrors;
|
|
@@ -48112,7 +48103,7 @@ var TARGET_TOOLS = new Set([
|
|
|
48112
48103
|
"context7_query-docs",
|
|
48113
48104
|
"websearch_web_search_exa",
|
|
48114
48105
|
"context7_get-library-docs",
|
|
48115
|
-
"
|
|
48106
|
+
"white_rabbit_searchgithub"
|
|
48116
48107
|
]);
|
|
48117
48108
|
var AGENT_TOOLS = new Set([
|
|
48118
48109
|
"task",
|
|
@@ -48425,8 +48416,11 @@ function getPlanProgress(planPath) {
|
|
|
48425
48416
|
const content = readFileSync10(planPath, "utf-8");
|
|
48426
48417
|
const uncheckedMatches = content.match(/^[-*]\s*\[\s*\]/gm) || [];
|
|
48427
48418
|
const checkedMatches = content.match(/^[-*]\s*\[[xX]\]/gm) || [];
|
|
48428
|
-
const
|
|
48429
|
-
const
|
|
48419
|
+
const numberedUnchecked = content.match(/^[-*]\s*\[\s*\]\s*\d+\./gm) || [];
|
|
48420
|
+
const numberedChecked = content.match(/^[-*]\s*\[[xX]\]\s*\d+\./gm) || [];
|
|
48421
|
+
const useNumbered = numberedUnchecked.length + numberedChecked.length > 0;
|
|
48422
|
+
const total = useNumbered ? numberedUnchecked.length + numberedChecked.length : uncheckedMatches.length + checkedMatches.length;
|
|
48423
|
+
const completed = useNumbered ? numberedChecked.length : checkedMatches.length;
|
|
48430
48424
|
return {
|
|
48431
48425
|
total,
|
|
48432
48426
|
completed,
|
|
@@ -48498,10 +48492,14 @@ function registerSubagentSession(sessionID, parentSessionID) {
|
|
|
48498
48492
|
subagentSessions.add(sessionID);
|
|
48499
48493
|
subagentParentMap.set(sessionID, parentSessionID);
|
|
48500
48494
|
}
|
|
48495
|
+
function unregisterSubagentSession(sessionID) {
|
|
48496
|
+
subagentSessions.delete(sessionID);
|
|
48497
|
+
subagentParentMap.delete(sessionID);
|
|
48498
|
+
}
|
|
48501
48499
|
function getSubagentSessionIDs(parentSessionID) {
|
|
48502
48500
|
const ids = [];
|
|
48503
|
-
for (const
|
|
48504
|
-
if (
|
|
48501
|
+
for (const subID of subagentSessions) {
|
|
48502
|
+
if (subagentParentMap.get(subID) === parentSessionID)
|
|
48505
48503
|
ids.push(subID);
|
|
48506
48504
|
}
|
|
48507
48505
|
return ids;
|
|
@@ -50255,79 +50253,49 @@ This prevents it from being picked up again.
|
|
|
50255
50253
|
- DO begin working immediately if the user included a task with /pickup
|
|
50256
50254
|
`;
|
|
50257
50255
|
|
|
50258
|
-
// src/features/builtin-commands/templates/
|
|
50259
|
-
var
|
|
50256
|
+
// src/features/builtin-commands/templates/preset.ts
|
|
50257
|
+
var PRESET_TEMPLATE = `You are managing the active model preset.
|
|
50258
|
+
|
|
50259
|
+
This command is provided by the Matrixx plugin. It uses the built-in \`preset\` tool to list, show, or switch model presets \u2014 no external scripts or direct config edits needed.
|
|
50260
50260
|
|
|
50261
50261
|
## Usage
|
|
50262
50262
|
|
|
50263
50263
|
\`\`\`
|
|
50264
|
-
/
|
|
50265
|
-
/
|
|
50266
|
-
/
|
|
50267
|
-
/
|
|
50268
|
-
/
|
|
50269
|
-
/profile set <name> --project \u2192 Change profile project-scoped (.opencode/matrixx.jsonc)
|
|
50264
|
+
/preset \u2192 List available presets and mark the active one
|
|
50265
|
+
/preset list \u2192 List available presets
|
|
50266
|
+
/preset show [<name>] \u2192 Show a preset's agent/category model assignments (defaults to active)
|
|
50267
|
+
/preset set <name> \u2192 Switch the active preset for the current session (live)
|
|
50268
|
+
/preset set <name> --save [--global|--project] \u2192 Also persist active_preset to config
|
|
50270
50269
|
\`\`\`
|
|
50271
50270
|
|
|
50272
|
-
##
|
|
50273
|
-
|
|
50274
|
-
| Profile | Description | Key Characteristic |
|
|
50275
|
-
|---------|-------------|-------------------|
|
|
50276
|
-
| free | Zero-cost, uses only free/open models | Kimi K2.5 Free, Grok (free), GLM |
|
|
50277
|
-
| budget | Cost-optimized for tight budgets | Sonnet/Haiku mix, no Opus |
|
|
50278
|
-
| economy | Balanced cost and capability | Primarily Sonnet/Haiku |
|
|
50279
|
-
| balanced | Default \u2014 best overall value | Opus/Sonnet/Haiku tiered |
|
|
50280
|
-
| performance | Maximum quality, no compromises | Opus for all heavy agents |
|
|
50281
|
-
| go | Premium OpenCode Go subscription | Kimi K2.6, DeepSeek V4 Pro, GLM 5.1 |
|
|
50282
|
-
|
|
50283
|
-
## How It Works
|
|
50284
|
-
|
|
50285
|
-
Each profile sets the \`model\` field on every agent and category in Matrixx's configuration. When you switch profiles, the agent models are swapped accordingly.
|
|
50286
|
-
|
|
50287
|
-
The profile name is stored in your \`matrixx.json\` or \`matrixx.jsonc\` config file. On the next session, Matrixx reads it and applies the profile's model mapping, merging it with any explicit per-agent overrides you may have set.
|
|
50288
|
-
|
|
50289
|
-
## Task: Change the Profile
|
|
50290
|
-
|
|
50291
|
-
Follow these steps:
|
|
50292
|
-
|
|
50293
|
-
### Step 1: Know the Current Config Location
|
|
50294
|
-
|
|
50295
|
-
Matrixx reads config from two locations (user config wins over project config):
|
|
50296
|
-
- **Project**: \`.opencode/matrixx.json\` or \`.opencode/matrixx.jsonc\`
|
|
50297
|
-
- **Global**: \`~/.config/opencode/matrixx.json\` or \`~/.config/opencode/matrixx.jsonc\`
|
|
50298
|
-
|
|
50299
|
-
### Step 2: Determine Which Config File to Edit
|
|
50271
|
+
## Step 1: Determine the action
|
|
50300
50272
|
|
|
50301
|
-
|
|
50302
|
-
- \`--project\` (default) \u2192 Edit \`.opencode/matrixx.jsonc\`
|
|
50303
|
-
- If no flag and neither file exists, use \`--project\`
|
|
50273
|
+
Parse the arguments passed to this command:
|
|
50304
50274
|
|
|
50305
|
-
|
|
50275
|
+
- No arguments or \`list\` \u2192 call \`preset\` with \`action: "list"\`.
|
|
50276
|
+
- \`show\` \u2192 call \`preset\` with \`action: "show"\` (optionally \`name\`).
|
|
50277
|
+
- \`set <name>\` \u2192 call \`preset\` with \`action: "set"\`, \`name: "<name>"\`.
|
|
50278
|
+
- \`set <name> --save\` \u2192 also pass \`save: true\`; \`--global\` \u2192 \`scope: "global"\`, \`--project\` (default) \u2192 \`scope: "project"\`.
|
|
50306
50279
|
|
|
50307
|
-
|
|
50280
|
+
## Step 2: Call the built-in \`preset\` tool
|
|
50308
50281
|
|
|
50309
|
-
|
|
50282
|
+
Use the \`preset\` tool with the resolved arguments. The tool validates the preset name, applies the session overlay (delegate-task categories switch immediately), and \u2014 with \`--save\` \u2014 persists \`active_preset\` to the project (\`.opencode/matrixx.jsonc\`) or global (\`~/.config/opencode/matrixx.jsonc\`) config.
|
|
50310
50283
|
|
|
50311
|
-
|
|
50284
|
+
The tool handles all validation and file operations \u2014 do NOT edit config files directly.
|
|
50312
50285
|
|
|
50313
|
-
|
|
50286
|
+
## Step 3: Confirm and instruct
|
|
50314
50287
|
|
|
50315
|
-
|
|
50316
|
-
|
|
50317
|
-
### Step 6: Write the Config File Back
|
|
50318
|
-
|
|
50319
|
-
Use the \`write\` tool (or \`edit\` tool for minimal changes) to save the updated config.
|
|
50320
|
-
|
|
50321
|
-
### Step 7: Confirm and Advise
|
|
50288
|
+
After a successful switch:
|
|
50322
50289
|
|
|
50323
|
-
|
|
50324
|
-
|
|
50325
|
-
|
|
50326
|
-
3. If they want session-level switching without restarting, suggest using the profile with \`--global\` or \`--project\` as appropriate
|
|
50290
|
+
1. Report the tool's output to the user.
|
|
50291
|
+
2. Note that delegate-task categories use the new preset immediately (session-scoped live switch).
|
|
50292
|
+
3. If \`--save\` was used, tell the user the persisted \`active_preset\` takes effect for builtin agents on the next session.
|
|
50327
50293
|
|
|
50328
|
-
|
|
50294
|
+
## Important constraints
|
|
50329
50295
|
|
|
50330
|
-
|
|
50296
|
+
- Use the built-in \`preset\` tool. Do NOT bypass it by editing config files directly.
|
|
50297
|
+
- Do not invent preset names \u2014 if the requested name is unknown, report the available presets and stop.
|
|
50298
|
+
- \`--save\` without a scope defaults to project scope.`;
|
|
50331
50299
|
|
|
50332
50300
|
// src/features/builtin-commands/templates/refactor.ts
|
|
50333
50301
|
var REFACTOR_TEMPLATE = `# Intelligent Refactor Command
|
|
@@ -51596,16 +51564,16 @@ ${REMOVE_DEADCODE_TEMPLATE}
|
|
|
51596
51564
|
</command-instruction>`,
|
|
51597
51565
|
argumentHint: "[target-path] [--scope=<file|module|project>] [--dry-run]"
|
|
51598
51566
|
},
|
|
51599
|
-
|
|
51600
|
-
description:
|
|
51567
|
+
preset: {
|
|
51568
|
+
description: "(builtin) Switch the active model preset (session-scoped live switch; --save persists to config)",
|
|
51601
51569
|
template: `<command-instruction>
|
|
51602
|
-
${
|
|
51570
|
+
${PRESET_TEMPLATE}
|
|
51603
51571
|
</command-instruction>
|
|
51604
51572
|
|
|
51605
51573
|
<user-request>
|
|
51606
51574
|
$ARGUMENTS
|
|
51607
51575
|
</user-request>`,
|
|
51608
|
-
argumentHint: "[list|show|set <name> [--global|--project]]"
|
|
51576
|
+
argumentHint: "[list|show|set <name> [--save [--global|--project]]]"
|
|
51609
51577
|
},
|
|
51610
51578
|
"end-ultrawork": {
|
|
51611
51579
|
description: "(builtin) Deactivate ultrawork mode and return to default Matrixx behavior for the current session",
|
|
@@ -52670,6 +52638,16 @@ function createBackgroundNotificationHook(manager) {
|
|
|
52670
52638
|
event: eventHandler
|
|
52671
52639
|
};
|
|
52672
52640
|
}
|
|
52641
|
+
// src/hooks/background-task-blocker/hook.ts
|
|
52642
|
+
function createBackgroundTaskBlockerHook() {
|
|
52643
|
+
return {
|
|
52644
|
+
"tool.execute.before": async (input, _output) => {
|
|
52645
|
+
if (input.tool === "background_task") {
|
|
52646
|
+
throw new Error('Tool "background_task" is disabled. Use the "task" tool with category/subagent_type and run_in_background=true for agent delegation.');
|
|
52647
|
+
}
|
|
52648
|
+
}
|
|
52649
|
+
};
|
|
52650
|
+
}
|
|
52673
52651
|
// src/hooks/bash-file-read-guard.ts
|
|
52674
52652
|
var WARNING_MESSAGE = "Prefer the Read tool over `cat`/`head`/`tail` for reading file contents. " + "The Read tool provides line numbers and hash anchors for precise editing.";
|
|
52675
52653
|
var FILE_READ_PATTERNS = [
|
|
@@ -59099,7 +59077,7 @@ var JSON_ERROR_TOOL_EXCLUDE_LIST = [
|
|
|
59099
59077
|
"grep",
|
|
59100
59078
|
"webfetch",
|
|
59101
59079
|
"look_at",
|
|
59102
|
-
"
|
|
59080
|
+
"white_rabbit_searchgithub",
|
|
59103
59081
|
"websearch_web_search_exa"
|
|
59104
59082
|
];
|
|
59105
59083
|
var JSON_ERROR_PATTERNS = [
|
|
@@ -60125,16 +60103,38 @@ init_logger();
|
|
|
60125
60103
|
import { randomUUID } from "crypto";
|
|
60126
60104
|
import { existsSync as existsSync36, mkdirSync as mkdirSync14, readdirSync as readdirSync11, readFileSync as readFileSync30, renameSync as renameSync3, unlinkSync as unlinkSync8, writeFileSync as writeFileSync16 } from "fs";
|
|
60127
60105
|
import { basename as basename5, dirname as dirname10, isAbsolute as isAbsolute3, join as join44 } from "path";
|
|
60106
|
+
|
|
60107
|
+
// src/shared/task-system-gating.ts
|
|
60108
|
+
var TASK_SYSTEM_DEFAULT = true;
|
|
60109
|
+
function resolveTasksConfig(config2) {
|
|
60110
|
+
const canonical = config2?.tasks;
|
|
60111
|
+
const legacyTasks = config2?.morpheus?.tasks;
|
|
60112
|
+
const legacyPoll = config2?.task?.pollTimeoutMs;
|
|
60113
|
+
return {
|
|
60114
|
+
enabled: canonical?.enabled ?? config2?.experimental?.task_system ?? config2?.new_task_system_enabled ?? TASK_SYSTEM_DEFAULT,
|
|
60115
|
+
scope: canonical?.scope ?? legacyTasks?.scope ?? "project",
|
|
60116
|
+
storage_path: canonical?.storage_path ?? legacyTasks?.storage_path,
|
|
60117
|
+
task_list_id: canonical?.task_list_id ?? legacyTasks?.task_list_id,
|
|
60118
|
+
stale_after_hours: canonical?.stale_after_hours ?? legacyTasks?.stale_after_hours,
|
|
60119
|
+
session_scoped: canonical?.session_scoped ?? legacyTasks?.session_scoped ?? true,
|
|
60120
|
+
pollTimeoutMs: canonical?.pollTimeoutMs ?? legacyPoll
|
|
60121
|
+
};
|
|
60122
|
+
}
|
|
60123
|
+
function isTaskSystemEnabled(config2) {
|
|
60124
|
+
return resolveTasksConfig(config2).enabled;
|
|
60125
|
+
}
|
|
60126
|
+
|
|
60127
|
+
// src/features/task-storage/storage.ts
|
|
60128
60128
|
function getProjectTaskDir(directory) {
|
|
60129
60129
|
return join44(directory, ".matrixx", "tasks");
|
|
60130
60130
|
}
|
|
60131
60131
|
function getTaskDir(config2 = {}, directory) {
|
|
60132
|
-
const tasksConfig = config2
|
|
60133
|
-
const storagePath = tasksConfig
|
|
60132
|
+
const tasksConfig = resolveTasksConfig(config2);
|
|
60133
|
+
const storagePath = tasksConfig.storage_path;
|
|
60134
60134
|
if (storagePath) {
|
|
60135
60135
|
return isAbsolute3(storagePath) ? storagePath : join44(directory ?? process.cwd(), storagePath);
|
|
60136
60136
|
}
|
|
60137
|
-
if (tasksConfig
|
|
60137
|
+
if (tasksConfig.scope === "global" || !directory) {
|
|
60138
60138
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
60139
60139
|
const listId = resolveTaskListId(config2);
|
|
60140
60140
|
return join44(configDir, "tasks", listId);
|
|
@@ -60148,7 +60148,7 @@ function resolveTaskListId(config2 = {}) {
|
|
|
60148
60148
|
const envId = process.env.ULTRAWORK_TASK_LIST_ID?.trim();
|
|
60149
60149
|
if (envId)
|
|
60150
60150
|
return sanitizePathSegment(envId);
|
|
60151
|
-
const configId = config2.
|
|
60151
|
+
const configId = resolveTasksConfig(config2).task_list_id?.trim();
|
|
60152
60152
|
if (configId)
|
|
60153
60153
|
return sanitizePathSegment(configId);
|
|
60154
60154
|
return sanitizePathSegment(basename5(process.cwd()));
|
|
@@ -60369,6 +60369,7 @@ async function getGitHead(directory) {
|
|
|
60369
60369
|
|
|
60370
60370
|
// src/tools/task/constants.ts
|
|
60371
60371
|
var TASK_ID_PATTERN = /^T-[A-Za-z0-9]+(-[A-Za-z0-9]+)*$/;
|
|
60372
|
+
var DEDUP_WINDOW_MS = 10 * 60 * 1000;
|
|
60372
60373
|
|
|
60373
60374
|
// src/tools/task/types.ts
|
|
60374
60375
|
var TaskStatusSchema = exports_external.enum(["pending", "in_progress", "completed", "deleted"]);
|
|
@@ -64921,7 +64922,7 @@ init_logger();
|
|
|
64921
64922
|
|
|
64922
64923
|
// src/hooks/task-continuation-enforcer/idle-event.ts
|
|
64923
64924
|
import { existsSync as existsSync43, readdirSync as readdirSync15 } from "fs";
|
|
64924
|
-
import { join as
|
|
64925
|
+
import { join as join52 } from "path";
|
|
64925
64926
|
init_logger();
|
|
64926
64927
|
|
|
64927
64928
|
// src/hooks/task-continuation-enforcer/abort-detection.ts
|
|
@@ -64945,14 +64946,15 @@ init_logger();
|
|
|
64945
64946
|
|
|
64946
64947
|
// src/hooks/task-continuation-enforcer/continuation-injection.ts
|
|
64947
64948
|
import { existsSync as existsSync42, readdirSync as readdirSync14 } from "fs";
|
|
64948
|
-
import { join as
|
|
64949
|
+
import { join as join51 } from "path";
|
|
64949
64950
|
init_logger();
|
|
64950
64951
|
// src/hooks/task-continuation-enforcer/staleness.ts
|
|
64951
64952
|
import { statSync as statSync8 } from "fs";
|
|
64953
|
+
import { join as join50 } from "path";
|
|
64952
64954
|
var DEFAULT_STALE_AFTER_HOURS = 24;
|
|
64953
64955
|
var HOUR_MS = 60 * 60 * 1000;
|
|
64954
64956
|
function getStaleAfterMs(config2) {
|
|
64955
|
-
const hours = config2
|
|
64957
|
+
const hours = resolveTasksConfig(config2).stale_after_hours ?? DEFAULT_STALE_AFTER_HOURS;
|
|
64956
64958
|
return hours * HOUR_MS;
|
|
64957
64959
|
}
|
|
64958
64960
|
function getTaskAgeMs(taskPath) {
|
|
@@ -64967,6 +64969,9 @@ function isTaskStale(taskPath, staleAfterMs) {
|
|
|
64967
64969
|
const ageMs = getTaskAgeMs(taskPath);
|
|
64968
64970
|
return ageMs !== null && ageMs > staleAfterMs;
|
|
64969
64971
|
}
|
|
64972
|
+
function filterFreshIncompleteTasks(tasks, taskDir, staleAfterMs) {
|
|
64973
|
+
return tasks.filter((t) => !isTaskStale(join50(taskDir, `${t.id}.json`), staleAfterMs));
|
|
64974
|
+
}
|
|
64970
64975
|
function formatTaskAge(ageMs) {
|
|
64971
64976
|
const hours = Math.floor(ageMs / HOUR_MS);
|
|
64972
64977
|
if (hours >= 24) {
|
|
@@ -64987,6 +64992,17 @@ function getIncompleteTasks(tasks) {
|
|
|
64987
64992
|
return task.blockedBy.every((bid) => byId.get(bid)?.status === "completed");
|
|
64988
64993
|
});
|
|
64989
64994
|
}
|
|
64995
|
+
function dropSubtasksWithResolvedParent(tasks) {
|
|
64996
|
+
const byId = new Map(tasks.map((t) => [t.id, t]));
|
|
64997
|
+
return tasks.filter((task) => {
|
|
64998
|
+
if (!task.parentID)
|
|
64999
|
+
return true;
|
|
65000
|
+
const parent = byId.get(task.parentID);
|
|
65001
|
+
if (!parent)
|
|
65002
|
+
return true;
|
|
65003
|
+
return parent.status !== "completed" && parent.status !== "deleted";
|
|
65004
|
+
});
|
|
65005
|
+
}
|
|
64990
65006
|
function filterTasksBySession(tasks, options) {
|
|
64991
65007
|
if (options.sessionScoped === false)
|
|
64992
65008
|
return tasks;
|
|
@@ -65059,10 +65075,10 @@ async function injectContinuation(args) {
|
|
|
65059
65075
|
if (parsed)
|
|
65060
65076
|
tasks.push(parsed);
|
|
65061
65077
|
}
|
|
65062
|
-
filteredTasks = filterTasksBySession(tasks, {
|
|
65078
|
+
filteredTasks = filterTasksBySession(dropSubtasksWithResolvedParent(tasks), {
|
|
65063
65079
|
sessionID,
|
|
65064
65080
|
subagentIDs: getSubagentSessionIDs(sessionID),
|
|
65065
|
-
sessionScoped: config2
|
|
65081
|
+
sessionScoped: resolveTasksConfig(config2).session_scoped
|
|
65066
65082
|
});
|
|
65067
65083
|
total = filteredTasks.length;
|
|
65068
65084
|
if (total === 0) {
|
|
@@ -65085,9 +65101,10 @@ async function injectContinuation(args) {
|
|
|
65085
65101
|
isBootstrap = true;
|
|
65086
65102
|
stateForBootstrap._bootstrap = undefined;
|
|
65087
65103
|
}
|
|
65088
|
-
const
|
|
65104
|
+
const staleAfterMs = getStaleAfterMs(config2);
|
|
65105
|
+
const freshIncompleteCount = isBootstrap ? 1 : filterFreshIncompleteTasks(getIncompleteTasks(filteredTasks), taskDir, staleAfterMs).length;
|
|
65089
65106
|
if (!isBootstrap && freshIncompleteCount === 0) {
|
|
65090
|
-
log(`[${HOOK_NAME14}] Skipped injection:
|
|
65107
|
+
log(`[${HOOK_NAME14}] Skipped injection: only stale tasks remain`, { sessionID, total });
|
|
65091
65108
|
return;
|
|
65092
65109
|
}
|
|
65093
65110
|
let agentName = resolvedInfo?.agent;
|
|
@@ -65122,14 +65139,14 @@ async function injectContinuation(args) {
|
|
|
65122
65139
|
prompt = BOOTSTRAP_PROMPT;
|
|
65123
65140
|
} else {
|
|
65124
65141
|
const incompleteTasks = getIncompleteTasks(filteredTasks);
|
|
65125
|
-
const
|
|
65142
|
+
const freshIncompleteTasks = filterFreshIncompleteTasks(incompleteTasks, taskDir, staleAfterMs);
|
|
65126
65143
|
const taskList = incompleteTasks.map((task) => {
|
|
65127
|
-
const ageMs = getTaskAgeMs(
|
|
65144
|
+
const ageMs = getTaskAgeMs(join51(taskDir, `${task.id}.json`));
|
|
65128
65145
|
const staleSuffix = ageMs !== null && ageMs > staleAfterMs ? ` (stale: ${formatTaskAge(ageMs)})` : "";
|
|
65129
65146
|
return `- [${task.status}] ${task.subject} (${task.id})${staleSuffix}`;
|
|
65130
65147
|
}).join(`
|
|
65131
65148
|
`);
|
|
65132
|
-
const staleCount = incompleteTasks.
|
|
65149
|
+
const staleCount = incompleteTasks.length - freshIncompleteTasks.length;
|
|
65133
65150
|
const staleNote = staleCount > 0 ? `
|
|
65134
65151
|
|
|
65135
65152
|
Note: ${staleCount} remaining task(s) have had no activity for >${Math.round(staleAfterMs / 3600000)}h and may be orphaned. Mark them completed/deleted if superseded.` : "";
|
|
@@ -65322,10 +65339,10 @@ async function handleSessionIdle(args) {
|
|
|
65322
65339
|
if (parsed)
|
|
65323
65340
|
tasks.push(parsed);
|
|
65324
65341
|
}
|
|
65325
|
-
const filteredTasks = filterTasksBySession(tasks, {
|
|
65342
|
+
const filteredTasks = filterTasksBySession(dropSubtasksWithResolvedParent(tasks), {
|
|
65326
65343
|
sessionID,
|
|
65327
65344
|
subagentIDs: getSubagentSessionIDs(sessionID),
|
|
65328
|
-
sessionScoped: config2
|
|
65345
|
+
sessionScoped: resolveTasksConfig(config2).session_scoped
|
|
65329
65346
|
});
|
|
65330
65347
|
total = filteredTasks.length;
|
|
65331
65348
|
if (total === 0) {
|
|
@@ -65342,7 +65359,7 @@ async function handleSessionIdle(args) {
|
|
|
65342
65359
|
const incompleteTasks = getIncompleteTasks(filteredTasks);
|
|
65343
65360
|
incompleteCount = incompleteTasks.length;
|
|
65344
65361
|
const staleAfterMs = getStaleAfterMs(config2);
|
|
65345
|
-
const staleIncompleteCount = incompleteTasks.filter((t) => isTaskStale(
|
|
65362
|
+
const staleIncompleteCount = incompleteTasks.filter((t) => isTaskStale(join52(taskDir, `${t.id}.json`), staleAfterMs)).length;
|
|
65346
65363
|
const activeIncompleteCount = incompleteCount - staleIncompleteCount;
|
|
65347
65364
|
if (activeIncompleteCount === 0 && staleIncompleteCount > 0) {
|
|
65348
65365
|
log(`[${HOOK_NAME14}] Skipped: only stale tasks remain`, {
|
|
@@ -65790,7 +65807,7 @@ var TODO_FETCH_ERROR_FRAGMENT = "Failed to fetch todos for task-notepad";
|
|
|
65790
65807
|
// src/hooks/task-notepad/hook.ts
|
|
65791
65808
|
init_logger();
|
|
65792
65809
|
import { existsSync as existsSync44, mkdirSync as mkdirSync16, readdirSync as readdirSync16, readFileSync as readFileSync33, statSync as statSync9, writeFileSync as writeFileSync18 } from "fs";
|
|
65793
|
-
import { dirname as dirname13, join as
|
|
65810
|
+
import { dirname as dirname13, join as join53 } from "path";
|
|
65794
65811
|
function createTaskNotepadHook(ctx) {
|
|
65795
65812
|
return {
|
|
65796
65813
|
"tool.execute.after": async (input, output) => {
|
|
@@ -65841,7 +65858,7 @@ async function fetchTodos(ctx, sessionID) {
|
|
|
65841
65858
|
}
|
|
65842
65859
|
}
|
|
65843
65860
|
function resolveActivePlan(directory) {
|
|
65844
|
-
const plansDir =
|
|
65861
|
+
const plansDir = join53(directory, PLAN_DIR_NAME, PLANS_SUBDIR);
|
|
65845
65862
|
if (!existsSync44(plansDir))
|
|
65846
65863
|
return null;
|
|
65847
65864
|
let entries;
|
|
@@ -65854,7 +65871,7 @@ function resolveActivePlan(directory) {
|
|
|
65854
65871
|
for (const entry of entries) {
|
|
65855
65872
|
if (!entry.endsWith(PLAN_EXTENSION))
|
|
65856
65873
|
continue;
|
|
65857
|
-
const fullPath =
|
|
65874
|
+
const fullPath = join53(plansDir, entry);
|
|
65858
65875
|
try {
|
|
65859
65876
|
const stats = statSync9(fullPath);
|
|
65860
65877
|
if (!stats.isFile())
|
|
@@ -65897,7 +65914,7 @@ function appendCompletion(directory, planName, todo) {
|
|
|
65897
65914
|
log(`[${HOOK_NAME16}] Appended completion to notepad`, { plan: planName, taskID: todo.id });
|
|
65898
65915
|
}
|
|
65899
65916
|
function notepadPathFor(directory, planName, taskID) {
|
|
65900
|
-
return
|
|
65917
|
+
return join53(directory, PLAN_DIR_NAME, NOTEPAD_DIR_NAME, planName, TASKS_SUBDIR, `${taskID}${NOTEPAD_EXTENSION}`);
|
|
65901
65918
|
}
|
|
65902
65919
|
function renderNotepad(todo) {
|
|
65903
65920
|
const priority = todo.priority ?? "medium";
|
|
@@ -65963,15 +65980,9 @@ to continue: task(session_id="${sessionId}", prompt="...")`;
|
|
|
65963
65980
|
"tool.execute.after": toolExecuteAfter
|
|
65964
65981
|
};
|
|
65965
65982
|
}
|
|
65966
|
-
// src/shared/task-system-gating.ts
|
|
65967
|
-
var TASK_SYSTEM_DEFAULT = true;
|
|
65968
|
-
function isTaskSystemEnabled(config2) {
|
|
65969
|
-
return config2?.experimental?.task_system ?? TASK_SYSTEM_DEFAULT;
|
|
65970
|
-
}
|
|
65971
|
-
|
|
65972
65983
|
// src/hooks/tasks-todowrite-disabler/constants.ts
|
|
65973
65984
|
var BLOCKED_TOOLS2 = ["TodoWrite", "TodoRead"];
|
|
65974
|
-
var REPLACEMENT_MESSAGE = `TodoRead/TodoWrite are DISABLED because
|
|
65985
|
+
var REPLACEMENT_MESSAGE = `TodoRead/TodoWrite are DISABLED because tasks.enabled is true.
|
|
65975
65986
|
|
|
65976
65987
|
**ACTION REQUIRED**: RE-REGISTER what you were about to write as Todo using Task tools NOW. Then ASSIGN yourself and START WORKING immediately.
|
|
65977
65988
|
|
|
@@ -67579,7 +67590,7 @@ function createWebFetchRedirectGuardHook(_ctx) {
|
|
|
67579
67590
|
}
|
|
67580
67591
|
// src/hooks/write-existing-file-guard/hook.ts
|
|
67581
67592
|
import { existsSync as existsSync45 } from "fs";
|
|
67582
|
-
import { isAbsolute as isAbsolute4, join as
|
|
67593
|
+
import { isAbsolute as isAbsolute4, join as join54, normalize, resolve as resolve6, sep } from "path";
|
|
67583
67594
|
function createWriteExistingFileGuardHook(ctx) {
|
|
67584
67595
|
return {
|
|
67585
67596
|
"tool.execute.before": async (input, output) => {
|
|
@@ -67594,7 +67605,7 @@ function createWriteExistingFileGuardHook(ctx) {
|
|
|
67594
67605
|
}
|
|
67595
67606
|
const resolvedPath = normalize(isAbsolute4(filePath) ? filePath : resolve6(ctx.directory, filePath));
|
|
67596
67607
|
if (existsSync45(resolvedPath)) {
|
|
67597
|
-
const matrixxRoot =
|
|
67608
|
+
const matrixxRoot = join54(ctx.directory, ".matrixx") + sep;
|
|
67598
67609
|
const isMatrixMarkdown = resolvedPath.startsWith(matrixxRoot) && resolvedPath.endsWith(".md");
|
|
67599
67610
|
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
67600
67611
|
const isPlansPath = normalizedPath.includes(".matrixx/plans");
|
|
@@ -68040,7 +68051,7 @@ function getLanguageId(ext) {
|
|
|
68040
68051
|
}
|
|
68041
68052
|
// src/tools/lsp/server-installation.ts
|
|
68042
68053
|
import { existsSync as existsSync46 } from "fs";
|
|
68043
|
-
import { join as
|
|
68054
|
+
import { join as join55 } from "path";
|
|
68044
68055
|
function isServerInstalled(command) {
|
|
68045
68056
|
if (command.length === 0)
|
|
68046
68057
|
return false;
|
|
@@ -68068,23 +68079,23 @@ function isServerInstalled(command) {
|
|
|
68068
68079
|
const paths = pathEnv.split(pathSeparator);
|
|
68069
68080
|
for (const p of paths) {
|
|
68070
68081
|
for (const suffix of exts) {
|
|
68071
|
-
if (existsSync46(
|
|
68082
|
+
if (existsSync46(join55(p, cmd + suffix))) {
|
|
68072
68083
|
return true;
|
|
68073
68084
|
}
|
|
68074
68085
|
}
|
|
68075
68086
|
}
|
|
68076
68087
|
const cwd = process.cwd();
|
|
68077
68088
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
68078
|
-
const dataDir =
|
|
68089
|
+
const dataDir = join55(getDataDir(), "opencode");
|
|
68079
68090
|
const additionalBases = [
|
|
68080
|
-
|
|
68081
|
-
|
|
68082
|
-
|
|
68083
|
-
|
|
68091
|
+
join55(cwd, "node_modules", ".bin"),
|
|
68092
|
+
join55(configDir, "bin"),
|
|
68093
|
+
join55(configDir, "node_modules", ".bin"),
|
|
68094
|
+
join55(dataDir, "bin")
|
|
68084
68095
|
];
|
|
68085
68096
|
for (const base of additionalBases) {
|
|
68086
68097
|
for (const suffix of exts) {
|
|
68087
|
-
if (existsSync46(
|
|
68098
|
+
if (existsSync46(join55(base, cmd + suffix))) {
|
|
68088
68099
|
return true;
|
|
68089
68100
|
}
|
|
68090
68101
|
}
|
|
@@ -68096,7 +68107,7 @@ function isServerInstalled(command) {
|
|
|
68096
68107
|
}
|
|
68097
68108
|
// src/tools/lsp/server-config-loader.ts
|
|
68098
68109
|
import { existsSync as existsSync47, readFileSync as readFileSync34 } from "fs";
|
|
68099
|
-
import { join as
|
|
68110
|
+
import { join as join56 } from "path";
|
|
68100
68111
|
function loadJsonFile(path12) {
|
|
68101
68112
|
if (!existsSync47(path12))
|
|
68102
68113
|
return null;
|
|
@@ -68110,9 +68121,9 @@ function getConfigPaths3() {
|
|
|
68110
68121
|
const cwd = process.cwd();
|
|
68111
68122
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
68112
68123
|
return {
|
|
68113
|
-
project: detectConfigFile(
|
|
68114
|
-
user: detectConfigFile(
|
|
68115
|
-
opencode: detectConfigFile(
|
|
68124
|
+
project: detectConfigFile(join56(cwd, ".opencode", "matrixx")).path,
|
|
68125
|
+
user: detectConfigFile(join56(configDir, "matrixx")).path,
|
|
68126
|
+
opencode: detectConfigFile(join56(configDir, "opencode")).path
|
|
68116
68127
|
};
|
|
68117
68128
|
}
|
|
68118
68129
|
function loadAllConfigs() {
|
|
@@ -68961,7 +68972,7 @@ class LSPServerManager {
|
|
|
68961
68972
|
var lspManager = LSPServerManager.getInstance();
|
|
68962
68973
|
// src/tools/lsp/lsp-client-wrapper.ts
|
|
68963
68974
|
import { existsSync as existsSync49, statSync as statSync11 } from "fs";
|
|
68964
|
-
import { dirname as dirname14, extname as extname3, join as
|
|
68975
|
+
import { dirname as dirname14, extname as extname3, join as join57, resolve as resolve8 } from "path";
|
|
68965
68976
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
68966
68977
|
function findWorkspaceRoot(filePath) {
|
|
68967
68978
|
let dir = resolve8(filePath);
|
|
@@ -68977,7 +68988,7 @@ function findWorkspaceRoot(filePath) {
|
|
|
68977
68988
|
let prevDir = "";
|
|
68978
68989
|
while (dir !== prevDir) {
|
|
68979
68990
|
for (const marker of markers) {
|
|
68980
|
-
if (existsSync49(
|
|
68991
|
+
if (existsSync49(join57(dir, marker))) {
|
|
68981
68992
|
return dir;
|
|
68982
68993
|
}
|
|
68983
68994
|
}
|
|
@@ -81829,13 +81840,13 @@ var DEFAULT_MAX_MATCHES = 500;
|
|
|
81829
81840
|
// src/tools/ast-grep/sg-cli-path.ts
|
|
81830
81841
|
import { existsSync as existsSync51, statSync as statSync12 } from "fs";
|
|
81831
81842
|
import { createRequire as createRequire4 } from "module";
|
|
81832
|
-
import { dirname as dirname15, join as
|
|
81843
|
+
import { dirname as dirname15, join as join59 } from "path";
|
|
81833
81844
|
|
|
81834
81845
|
// src/tools/ast-grep/downloader.ts
|
|
81835
81846
|
import { existsSync as existsSync50 } from "fs";
|
|
81836
81847
|
import { createRequire as createRequire3 } from "module";
|
|
81837
81848
|
import { homedir as homedir8 } from "os";
|
|
81838
|
-
import { join as
|
|
81849
|
+
import { join as join58 } from "path";
|
|
81839
81850
|
init_logger();
|
|
81840
81851
|
var REPO2 = "ast-grep/ast-grep";
|
|
81841
81852
|
var DEFAULT_VERSION = "0.40.0";
|
|
@@ -81860,12 +81871,12 @@ var PLATFORM_MAP2 = {
|
|
|
81860
81871
|
function getCacheDir3() {
|
|
81861
81872
|
if (process.platform === "win32") {
|
|
81862
81873
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
81863
|
-
const base2 = localAppData ||
|
|
81864
|
-
return
|
|
81874
|
+
const base2 = localAppData || join58(homedir8(), "AppData", "Local");
|
|
81875
|
+
return join58(base2, "matrixx", "bin");
|
|
81865
81876
|
}
|
|
81866
81877
|
const xdgCache = process.env.XDG_CACHE_HOME;
|
|
81867
|
-
const base = xdgCache ||
|
|
81868
|
-
return
|
|
81878
|
+
const base = xdgCache || join58(homedir8(), ".cache");
|
|
81879
|
+
return join58(base, "matrixx", "bin");
|
|
81869
81880
|
}
|
|
81870
81881
|
function getBinaryName3() {
|
|
81871
81882
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
@@ -81882,7 +81893,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
81882
81893
|
}
|
|
81883
81894
|
const cacheDir = getCacheDir3();
|
|
81884
81895
|
const binaryName = getBinaryName3();
|
|
81885
|
-
const binaryPath =
|
|
81896
|
+
const binaryPath = join58(cacheDir, binaryName);
|
|
81886
81897
|
if (existsSync50(binaryPath)) {
|
|
81887
81898
|
return binaryPath;
|
|
81888
81899
|
}
|
|
@@ -81891,7 +81902,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
81891
81902
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version3}/${assetName}`;
|
|
81892
81903
|
log(`[matrixx] Downloading ast-grep binary...`);
|
|
81893
81904
|
try {
|
|
81894
|
-
const archivePath =
|
|
81905
|
+
const archivePath = join58(cacheDir, assetName);
|
|
81895
81906
|
ensureCacheDir(cacheDir);
|
|
81896
81907
|
await downloadArchive(downloadUrl, archivePath);
|
|
81897
81908
|
await extractZipArchive(archivePath, cacheDir);
|
|
@@ -81945,7 +81956,7 @@ function findSgCliPathSync() {
|
|
|
81945
81956
|
const require2 = createRequire4(import.meta.url);
|
|
81946
81957
|
const cliPackageJsonPath = require2.resolve("@ast-grep/cli/package.json");
|
|
81947
81958
|
const cliDirectory = dirname15(cliPackageJsonPath);
|
|
81948
|
-
const sgPath =
|
|
81959
|
+
const sgPath = join59(cliDirectory, binaryName);
|
|
81949
81960
|
if (existsSync51(sgPath) && isValidBinary(sgPath)) {
|
|
81950
81961
|
return sgPath;
|
|
81951
81962
|
}
|
|
@@ -81957,7 +81968,7 @@ function findSgCliPathSync() {
|
|
|
81957
81968
|
const packageJsonPath = require2.resolve(`${platformPackage}/package.json`);
|
|
81958
81969
|
const packageDirectory = dirname15(packageJsonPath);
|
|
81959
81970
|
const astGrepBinaryName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
81960
|
-
const binaryPath =
|
|
81971
|
+
const binaryPath = join59(packageDirectory, astGrepBinaryName);
|
|
81961
81972
|
if (existsSync51(binaryPath) && isValidBinary(binaryPath)) {
|
|
81962
81973
|
return binaryPath;
|
|
81963
81974
|
}
|
|
@@ -84388,17 +84399,17 @@ var {spawn: spawn10 } = globalThis.Bun;
|
|
|
84388
84399
|
// src/tools/grep/constants.ts
|
|
84389
84400
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
84390
84401
|
import { existsSync as existsSync58 } from "fs";
|
|
84391
|
-
import { dirname as dirname16, join as
|
|
84402
|
+
import { dirname as dirname16, join as join61 } from "path";
|
|
84392
84403
|
|
|
84393
84404
|
// src/tools/grep/downloader.ts
|
|
84394
84405
|
import { existsSync as existsSync57, readdirSync as readdirSync18 } from "fs";
|
|
84395
|
-
import { join as
|
|
84406
|
+
import { join as join60 } from "path";
|
|
84396
84407
|
function findFileRecursive(dir, filename) {
|
|
84397
84408
|
try {
|
|
84398
84409
|
const entries = readdirSync18(dir, { withFileTypes: true, recursive: true });
|
|
84399
84410
|
for (const entry of entries) {
|
|
84400
84411
|
if (entry.isFile() && entry.name === filename) {
|
|
84401
|
-
return
|
|
84412
|
+
return join60(entry.parentPath ?? dir, entry.name);
|
|
84402
84413
|
}
|
|
84403
84414
|
}
|
|
84404
84415
|
} catch {
|
|
@@ -84419,11 +84430,11 @@ function getPlatformKey() {
|
|
|
84419
84430
|
}
|
|
84420
84431
|
function getInstallDir() {
|
|
84421
84432
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
84422
|
-
return
|
|
84433
|
+
return join60(homeDir, ".cache", "matrixx", "bin");
|
|
84423
84434
|
}
|
|
84424
84435
|
function getRgPath() {
|
|
84425
84436
|
const isWindows = process.platform === "win32";
|
|
84426
|
-
return
|
|
84437
|
+
return join60(getInstallDir(), isWindows ? "rg.exe" : "rg");
|
|
84427
84438
|
}
|
|
84428
84439
|
async function extractTarGz2(archivePath, destDir) {
|
|
84429
84440
|
const platformKey = getPlatformKey();
|
|
@@ -84440,7 +84451,7 @@ async function extractZip2(archivePath, destDir) {
|
|
|
84440
84451
|
const binaryName = process.platform === "win32" ? "rg.exe" : "rg";
|
|
84441
84452
|
const foundPath = findFileRecursive(destDir, binaryName);
|
|
84442
84453
|
if (foundPath) {
|
|
84443
|
-
const destPath =
|
|
84454
|
+
const destPath = join60(destDir, binaryName);
|
|
84444
84455
|
if (foundPath !== destPath) {
|
|
84445
84456
|
const { renameSync: renameSync4 } = await import("fs");
|
|
84446
84457
|
renameSync4(foundPath, destPath);
|
|
@@ -84461,7 +84472,7 @@ async function downloadAndInstallRipgrep() {
|
|
|
84461
84472
|
ensureCacheDir(installDir);
|
|
84462
84473
|
const filename = `ripgrep-${RG_VERSION}-${config4.platform}.${config4.extension}`;
|
|
84463
84474
|
const url3 = `https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/${filename}`;
|
|
84464
|
-
const archivePath =
|
|
84475
|
+
const archivePath = join60(installDir, filename);
|
|
84465
84476
|
try {
|
|
84466
84477
|
await downloadArchive(url3, archivePath);
|
|
84467
84478
|
if (config4.extension === "tar.gz") {
|
|
@@ -84506,11 +84517,11 @@ function getOpenCodeBundledRg() {
|
|
|
84506
84517
|
const isWindows = process.platform === "win32";
|
|
84507
84518
|
const rgName = isWindows ? "rg.exe" : "rg";
|
|
84508
84519
|
const candidates = [
|
|
84509
|
-
|
|
84510
|
-
|
|
84511
|
-
|
|
84512
|
-
|
|
84513
|
-
|
|
84520
|
+
join61(getDataDir(), "opencode", "bin", rgName),
|
|
84521
|
+
join61(execDir, rgName),
|
|
84522
|
+
join61(execDir, "bin", rgName),
|
|
84523
|
+
join61(execDir, "..", "bin", rgName),
|
|
84524
|
+
join61(execDir, "..", "libexec", rgName)
|
|
84514
84525
|
];
|
|
84515
84526
|
for (const candidate of candidates) {
|
|
84516
84527
|
if (existsSync58(candidate)) {
|
|
@@ -85018,12 +85029,12 @@ var HandoffSchema = exports_external.object({
|
|
|
85018
85029
|
});
|
|
85019
85030
|
// src/features/handoff/storage.ts
|
|
85020
85031
|
import { existsSync as existsSync59, mkdirSync as mkdirSync17, readFileSync as readFileSync39, renameSync as renameSync4, writeFileSync as writeFileSync22 } from "fs";
|
|
85021
|
-
import { join as
|
|
85032
|
+
import { join as join62 } from "path";
|
|
85022
85033
|
function getHandoffFilePath(directory) {
|
|
85023
|
-
return
|
|
85034
|
+
return join62(directory, HANDOFF_FILE_PATH);
|
|
85024
85035
|
}
|
|
85025
85036
|
function getHandoffConsumedFilePath(directory) {
|
|
85026
|
-
return
|
|
85037
|
+
return join62(directory, HANDOFF_CONSUMED_FILE_PATH);
|
|
85027
85038
|
}
|
|
85028
85039
|
function handoffFileExists(directory) {
|
|
85029
85040
|
try {
|
|
@@ -85061,7 +85072,7 @@ function writeHandoffFile(directory, content) {
|
|
|
85061
85072
|
}
|
|
85062
85073
|
function ensureHandoffDir(directory) {
|
|
85063
85074
|
try {
|
|
85064
|
-
mkdirSync17(
|
|
85075
|
+
mkdirSync17(join62(directory, ".matrixx"), { recursive: true });
|
|
85065
85076
|
return true;
|
|
85066
85077
|
} catch {
|
|
85067
85078
|
return false;
|
|
@@ -85217,13 +85228,13 @@ ${body}
|
|
|
85217
85228
|
|
|
85218
85229
|
// src/tools/handoff/list-handler.ts
|
|
85219
85230
|
import { existsSync as existsSync60, readdirSync as readdirSync19, statSync as statSync14 } from "fs";
|
|
85220
|
-
import { join as
|
|
85231
|
+
import { join as join63 } from "path";
|
|
85221
85232
|
var MATRIX_DIR = ".matrixx";
|
|
85222
85233
|
function isHandoffFile(name) {
|
|
85223
85234
|
return name === "handoff.md" || name.startsWith("handoff.consumed");
|
|
85224
85235
|
}
|
|
85225
85236
|
function handleList(directory) {
|
|
85226
|
-
const matrixxDir =
|
|
85237
|
+
const matrixxDir = join63(directory, MATRIX_DIR);
|
|
85227
85238
|
if (!existsSync60(matrixxDir)) {
|
|
85228
85239
|
return "No handoffs found in .matrixx/";
|
|
85229
85240
|
}
|
|
@@ -85241,7 +85252,7 @@ function handleList(directory) {
|
|
|
85241
85252
|
lines.push(`Handoff files in ${matrixxDir}:`);
|
|
85242
85253
|
lines.push("");
|
|
85243
85254
|
for (const name of handoffs) {
|
|
85244
|
-
const fullPath =
|
|
85255
|
+
const fullPath = join63(matrixxDir, name);
|
|
85245
85256
|
let size = "?";
|
|
85246
85257
|
let mtime = "?";
|
|
85247
85258
|
try {
|
|
@@ -85467,7 +85478,7 @@ The Bash tool can execute these commands directly. Do NOT retry with interactive
|
|
|
85467
85478
|
// src/tools/pdf-extract-figures/tools.ts
|
|
85468
85479
|
import { existsSync as existsSync61, mkdtempSync, writeFileSync as writeFileSync23 } from "fs";
|
|
85469
85480
|
import { tmpdir as tmpdir5 } from "os";
|
|
85470
|
-
import { join as
|
|
85481
|
+
import { join as join64 } from "path";
|
|
85471
85482
|
var PYTHON_SCRIPT = `#!/usr/bin/env python3
|
|
85472
85483
|
import sys
|
|
85473
85484
|
import json
|
|
@@ -85618,11 +85629,11 @@ async function runExtraction(args) {
|
|
|
85618
85629
|
}
|
|
85619
85630
|
let tmpDir;
|
|
85620
85631
|
try {
|
|
85621
|
-
tmpDir = mkdtempSync(
|
|
85632
|
+
tmpDir = mkdtempSync(join64(tmpdir5(), "matrixx-pdf-extract-"));
|
|
85622
85633
|
} catch {
|
|
85623
85634
|
return "Error: Could not create temporary directory.";
|
|
85624
85635
|
}
|
|
85625
|
-
const scriptPath =
|
|
85636
|
+
const scriptPath = join64(tmpDir, "extract_pdf_figures.py");
|
|
85626
85637
|
writeFileSync23(scriptPath, PYTHON_SCRIPT);
|
|
85627
85638
|
const payload = buildArgsPayload(args);
|
|
85628
85639
|
log("[pdf_extract_figures] Running extraction", { file_path: args.file_path, payload });
|
|
@@ -85674,7 +85685,7 @@ import { existsSync as existsSync62 } from "fs";
|
|
|
85674
85685
|
import { basename as basename7 } from "path";
|
|
85675
85686
|
|
|
85676
85687
|
// src/tools/plan/types.ts
|
|
85677
|
-
import { basename as basename6, isAbsolute as isAbsolute5, join as
|
|
85688
|
+
import { basename as basename6, isAbsolute as isAbsolute5, join as join65, relative as relative4, resolve as resolve9 } from "path";
|
|
85678
85689
|
|
|
85679
85690
|
// src/tools/plan/constants.ts
|
|
85680
85691
|
var PLANS_DIR3 = ".matrixx/plans";
|
|
@@ -85700,8 +85711,8 @@ function validatePlanFilePath(filePath, directory) {
|
|
|
85700
85711
|
if (!PLAN_FILENAME_KEBAB_REGEX.test(base)) {
|
|
85701
85712
|
return { error: `fileName must match kebab-case ^[a-z0-9-]+\\.md$: ${base}` };
|
|
85702
85713
|
}
|
|
85703
|
-
const resolved = isAbsolute5(filePath) ? resolve9(filePath) : resolve9(
|
|
85704
|
-
const plansDir = resolve9(
|
|
85714
|
+
const resolved = isAbsolute5(filePath) ? resolve9(filePath) : resolve9(join65(directory, filePath));
|
|
85715
|
+
const plansDir = resolve9(join65(directory, PLANS_DIR3));
|
|
85705
85716
|
const relToPlans = relative4(plansDir, resolved);
|
|
85706
85717
|
if (relToPlans.startsWith("..") || isAbsolute5(relToPlans)) {
|
|
85707
85718
|
return { error: `filePath must be inside ${PLANS_DIR3}: ${filePath}` };
|
|
@@ -85805,7 +85816,7 @@ function createPlanDeleteTool(ctx) {
|
|
|
85805
85816
|
}
|
|
85806
85817
|
// src/tools/plan/plan-list.ts
|
|
85807
85818
|
import { existsSync as existsSync64, readdirSync as readdirSync20, statSync as statSync15 } from "fs";
|
|
85808
|
-
import { join as
|
|
85819
|
+
import { join as join66 } from "path";
|
|
85809
85820
|
function createPlanListTool(ctx) {
|
|
85810
85821
|
return tool({
|
|
85811
85822
|
description: `List plan files under .matrixx/plans/*.md. Filters *.md with kebab-case, sorts by mtime.`,
|
|
@@ -85813,13 +85824,13 @@ function createPlanListTool(ctx) {
|
|
|
85813
85824
|
execute: async (_args, context) => {
|
|
85814
85825
|
try {
|
|
85815
85826
|
const directory = resolveDirectory(context?.directory, ctx?.directory);
|
|
85816
|
-
const plansDir =
|
|
85827
|
+
const plansDir = join66(directory, PLANS_DIR3);
|
|
85817
85828
|
if (!existsSync64(plansDir)) {
|
|
85818
85829
|
return JSON.stringify({ plans: [] });
|
|
85819
85830
|
}
|
|
85820
85831
|
const entries = readdirSync20(plansDir).filter((f) => f.endsWith(".md") && PLAN_FILENAME_KEBAB_REGEX.test(f));
|
|
85821
85832
|
const plans = entries.map((fileName) => {
|
|
85822
|
-
const filePath =
|
|
85833
|
+
const filePath = join66(plansDir, fileName);
|
|
85823
85834
|
try {
|
|
85824
85835
|
const stat2 = statSync15(filePath);
|
|
85825
85836
|
return {
|
|
@@ -86965,10 +86976,152 @@ function createPlanUpdateTool(ctx) {
|
|
|
86965
86976
|
}
|
|
86966
86977
|
});
|
|
86967
86978
|
}
|
|
86979
|
+
// src/tools/preset/tools.ts
|
|
86980
|
+
import { existsSync as existsSync67, mkdirSync as mkdirSync18, readFileSync as readFileSync40, writeFileSync as writeFileSync24 } from "fs";
|
|
86981
|
+
import { homedir as homedir9 } from "os";
|
|
86982
|
+
import { dirname as dirname17, join as join67 } from "path";
|
|
86983
|
+
|
|
86984
|
+
// src/features/preset-state/manager.ts
|
|
86985
|
+
init_logger();
|
|
86986
|
+
var HOOK_NAME19 = "preset-state";
|
|
86987
|
+
var presetBySession = new Map;
|
|
86988
|
+
function setSessionPreset(sessionID, presetName) {
|
|
86989
|
+
presetBySession.set(sessionID, presetName);
|
|
86990
|
+
log(`[${HOOK_NAME19}] Preset set for session`, { sessionID, presetName });
|
|
86991
|
+
}
|
|
86992
|
+
function getSessionPreset(sessionID) {
|
|
86993
|
+
return presetBySession.get(sessionID);
|
|
86994
|
+
}
|
|
86995
|
+
// src/tools/preset/tools.ts
|
|
86996
|
+
init_logger();
|
|
86997
|
+
var ACTIONS = ["list", "show", "set"];
|
|
86998
|
+
var SCOPES = ["global", "project"];
|
|
86999
|
+
function resolveConfigPath(scope, directory) {
|
|
87000
|
+
if (scope === "global") {
|
|
87001
|
+
return join67(homedir9(), ".config", "opencode", "matrixx.jsonc");
|
|
87002
|
+
}
|
|
87003
|
+
return join67(directory ?? process.cwd(), ".opencode", "matrixx.jsonc");
|
|
87004
|
+
}
|
|
87005
|
+
function persistActivePreset(presetName, scope, directory) {
|
|
87006
|
+
const configPath = resolveConfigPath(scope, directory);
|
|
87007
|
+
let raw = {};
|
|
87008
|
+
if (existsSync67(configPath)) {
|
|
87009
|
+
const content = readFileSync40(configPath, "utf-8");
|
|
87010
|
+
const parsed = parseJsoncSafe(content);
|
|
87011
|
+
if (parsed.errors.length > 0) {
|
|
87012
|
+
return {
|
|
87013
|
+
ok: false,
|
|
87014
|
+
error: `Cannot parse existing config at ${configPath}: ${parsed.errors[0]?.message}`
|
|
87015
|
+
};
|
|
87016
|
+
}
|
|
87017
|
+
raw = parsed.data ?? {};
|
|
87018
|
+
} else {
|
|
87019
|
+
mkdirSync18(dirname17(configPath), { recursive: true });
|
|
87020
|
+
}
|
|
87021
|
+
raw.active_preset = presetName;
|
|
87022
|
+
writeFileSync24(configPath, `${JSON.stringify(raw, null, 2)}
|
|
87023
|
+
`);
|
|
87024
|
+
log("[preset] persisted active_preset", { presetName, scope, configPath });
|
|
87025
|
+
return { ok: true, path: configPath };
|
|
87026
|
+
}
|
|
87027
|
+
function unknownPresetError(name, presetNames) {
|
|
87028
|
+
const available = presetNames.length > 0 ? presetNames.join(", ") : "(none defined)";
|
|
87029
|
+
return `Error: Unknown preset "${name}". Available presets: ${available}`;
|
|
87030
|
+
}
|
|
87031
|
+
function formatPreset(name, preset) {
|
|
87032
|
+
const lines = [`Preset "${name}":`];
|
|
87033
|
+
if (preset.default_model) {
|
|
87034
|
+
lines.push(` default_model: ${preset.default_model}`);
|
|
87035
|
+
}
|
|
87036
|
+
const agentEntries = Object.entries(preset.agents ?? {});
|
|
87037
|
+
if (agentEntries.length > 0) {
|
|
87038
|
+
lines.push(" agents:");
|
|
87039
|
+
for (const [agent, entry] of agentEntries) {
|
|
87040
|
+
lines.push(` - ${agent}: ${entry.model}${entry.variant ? ` (variant: ${entry.variant})` : ""}`);
|
|
87041
|
+
}
|
|
87042
|
+
}
|
|
87043
|
+
const categoryEntries = Object.entries(preset.categories ?? {});
|
|
87044
|
+
if (categoryEntries.length > 0) {
|
|
87045
|
+
lines.push(" categories:");
|
|
87046
|
+
for (const [category, entry] of categoryEntries) {
|
|
87047
|
+
lines.push(` - ${category}: ${entry.model}${entry.variant ? ` (variant: ${entry.variant})` : ""}`);
|
|
87048
|
+
}
|
|
87049
|
+
}
|
|
87050
|
+
return lines.join(`
|
|
87051
|
+
`);
|
|
87052
|
+
}
|
|
87053
|
+
function runPresetCommand(input) {
|
|
87054
|
+
const { action, name, save, scope, sessionID, options } = input;
|
|
87055
|
+
const presets = options?.pluginConfig?.model_presets ?? {};
|
|
87056
|
+
const presetNames = Object.keys(presets);
|
|
87057
|
+
const activePreset = options?.pluginConfig?.active_preset;
|
|
87058
|
+
const sessionPreset = getSessionPreset(sessionID);
|
|
87059
|
+
const effective = sessionPreset ?? activePreset;
|
|
87060
|
+
if (action === "list") {
|
|
87061
|
+
if (presetNames.length === 0) {
|
|
87062
|
+
return "No model presets defined. Add a `model_presets` section to your matrixx config.";
|
|
87063
|
+
}
|
|
87064
|
+
const lines = presetNames.map((p) => `- ${p}${p === effective ? " (active)" : ""}`);
|
|
87065
|
+
const activeLine = effective ? `Active: ${effective}${sessionPreset ? ` (session overlay for ${sessionID})` : ""}` : "Active: (none)";
|
|
87066
|
+
return `Available presets:
|
|
87067
|
+
${lines.join(`
|
|
87068
|
+
`)}
|
|
87069
|
+
|
|
87070
|
+
${activeLine}`;
|
|
87071
|
+
}
|
|
87072
|
+
if (action === "show") {
|
|
87073
|
+
const target = name ?? effective;
|
|
87074
|
+
if (!target) {
|
|
87075
|
+
return "No preset specified and no active preset set. Usage: preset show <name>";
|
|
87076
|
+
}
|
|
87077
|
+
const preset = presets[target];
|
|
87078
|
+
if (!preset)
|
|
87079
|
+
return unknownPresetError(target, presetNames);
|
|
87080
|
+
return formatPreset(target, preset);
|
|
87081
|
+
}
|
|
87082
|
+
if (!name) {
|
|
87083
|
+
return "Error: `preset set` requires a preset name. Usage: preset set <name> [--save [--global|--project]]";
|
|
87084
|
+
}
|
|
87085
|
+
if (!presets[name])
|
|
87086
|
+
return unknownPresetError(name, presetNames);
|
|
87087
|
+
setSessionPreset(sessionID, name);
|
|
87088
|
+
let message = `\u2713 Active preset set to "${name}" for this session.
|
|
87089
|
+
|
|
87090
|
+
` + `- delegate-task categories switch immediately
|
|
87091
|
+
` + `- builtin agents apply on next session`;
|
|
87092
|
+
if (save) {
|
|
87093
|
+
const result = persistActivePreset(name, scope, options?.directory);
|
|
87094
|
+
if (!result.ok)
|
|
87095
|
+
return `Error: ${result.error}`;
|
|
87096
|
+
message += `
|
|
87097
|
+
|
|
87098
|
+
Persisted active_preset to ${result.path} (scope: ${scope}). Next session starts on "${name}".`;
|
|
87099
|
+
}
|
|
87100
|
+
return message;
|
|
87101
|
+
}
|
|
87102
|
+
function createPresetTool(options) {
|
|
87103
|
+
const preset = tool({
|
|
87104
|
+
description: "Manage model presets: list available presets, show a preset's model assignments, " + "or set the active preset for the current session. `set <name>` switches delegate-task " + "categories immediately and applies to builtin agents on the next session; " + "`set <name> --save [--global|--project]` additionally persists `active_preset` to config.",
|
|
87105
|
+
args: {
|
|
87106
|
+
action: tool.schema.enum(ACTIONS).describe("Operation: list available presets, show a preset's details, or set the active preset"),
|
|
87107
|
+
name: tool.schema.string().optional().describe("Preset name (required for set; optional for show \u2014 defaults to the active preset)"),
|
|
87108
|
+
save: tool.schema.boolean().optional().describe("Persist the active preset to config (default: false \u2014 session-only overlay)"),
|
|
87109
|
+
scope: tool.schema.enum(SCOPES).optional().describe("Where to persist when --save: project (.opencode/matrixx.jsonc, default) or global (~/.config/opencode/matrixx.jsonc)")
|
|
87110
|
+
},
|
|
87111
|
+
async execute(args, context) {
|
|
87112
|
+
const action = args.action;
|
|
87113
|
+
const name = args.name;
|
|
87114
|
+
const save = args.save;
|
|
87115
|
+
const scope = args.scope ?? "project";
|
|
87116
|
+
return runPresetCommand({ action, name, save, scope, sessionID: context.sessionID, options });
|
|
87117
|
+
}
|
|
87118
|
+
});
|
|
87119
|
+
return { preset };
|
|
87120
|
+
}
|
|
86968
87121
|
// src/tools/session-manager/constants.ts
|
|
86969
|
-
import { join as
|
|
86970
|
-
var TODO_DIR =
|
|
86971
|
-
var TRANSCRIPT_DIR2 =
|
|
87122
|
+
import { join as join68 } from "path";
|
|
87123
|
+
var TODO_DIR = join68(getOpenCodeConfigDir({ binary: "opencode" }), "todos");
|
|
87124
|
+
var TRANSCRIPT_DIR2 = join68(getOpenCodeConfigDir({ binary: "opencode" }), "transcripts");
|
|
86972
87125
|
var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
|
|
86973
87126
|
|
|
86974
87127
|
Returns a list of available session IDs with metadata including message count, date range, and agents used.
|
|
@@ -87040,9 +87193,9 @@ Agents Used: build, oracle, librarian
|
|
|
87040
87193
|
Has Todos: Yes (12 items, 8 completed)
|
|
87041
87194
|
Has Transcript: Yes (234 entries)`;
|
|
87042
87195
|
// src/tools/session-manager/storage.ts
|
|
87043
|
-
import { existsSync as
|
|
87196
|
+
import { existsSync as existsSync68 } from "fs";
|
|
87044
87197
|
import { readdir, readFile as readFile3 } from "fs/promises";
|
|
87045
|
-
import { join as
|
|
87198
|
+
import { join as join69 } from "path";
|
|
87046
87199
|
var sdkClient = null;
|
|
87047
87200
|
function setStorageClient(client2) {
|
|
87048
87201
|
sdkClient = client2;
|
|
@@ -87061,7 +87214,7 @@ async function getMainSessions(options) {
|
|
|
87061
87214
|
return [];
|
|
87062
87215
|
}
|
|
87063
87216
|
}
|
|
87064
|
-
if (!
|
|
87217
|
+
if (!existsSync68(SESSION_STORAGE))
|
|
87065
87218
|
return [];
|
|
87066
87219
|
const sessions = [];
|
|
87067
87220
|
try {
|
|
@@ -87069,13 +87222,13 @@ async function getMainSessions(options) {
|
|
|
87069
87222
|
for (const projectDir of projectDirs) {
|
|
87070
87223
|
if (!projectDir.isDirectory())
|
|
87071
87224
|
continue;
|
|
87072
|
-
const projectPath =
|
|
87225
|
+
const projectPath = join69(SESSION_STORAGE, projectDir.name);
|
|
87073
87226
|
const sessionFiles = await readdir(projectPath);
|
|
87074
87227
|
for (const file3 of sessionFiles) {
|
|
87075
87228
|
if (!file3.endsWith(".json"))
|
|
87076
87229
|
continue;
|
|
87077
87230
|
try {
|
|
87078
|
-
const content = await readFile3(
|
|
87231
|
+
const content = await readFile3(join69(projectPath, file3), "utf-8");
|
|
87079
87232
|
const meta3 = JSON.parse(content);
|
|
87080
87233
|
if (meta3.parentID)
|
|
87081
87234
|
continue;
|
|
@@ -87100,7 +87253,7 @@ async function getAllSessions() {
|
|
|
87100
87253
|
return [];
|
|
87101
87254
|
}
|
|
87102
87255
|
}
|
|
87103
|
-
if (!
|
|
87256
|
+
if (!existsSync68(MESSAGE_STORAGE))
|
|
87104
87257
|
return [];
|
|
87105
87258
|
const sessions = [];
|
|
87106
87259
|
async function scanDirectory(dir) {
|
|
@@ -87108,7 +87261,7 @@ async function getAllSessions() {
|
|
|
87108
87261
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
87109
87262
|
for (const entry of entries) {
|
|
87110
87263
|
if (entry.isDirectory()) {
|
|
87111
|
-
const sessionPath =
|
|
87264
|
+
const sessionPath = join69(dir, entry.name);
|
|
87112
87265
|
const files = await readdir(sessionPath);
|
|
87113
87266
|
if (files.some((f) => f.endsWith(".json"))) {
|
|
87114
87267
|
sessions.push(entry.name);
|
|
@@ -87169,7 +87322,7 @@ async function readSessionMessages(sessionID) {
|
|
|
87169
87322
|
}
|
|
87170
87323
|
}
|
|
87171
87324
|
const messageDir = getMessageDir(sessionID);
|
|
87172
|
-
if (!messageDir || !
|
|
87325
|
+
if (!messageDir || !existsSync68(messageDir))
|
|
87173
87326
|
return [];
|
|
87174
87327
|
const messages2 = [];
|
|
87175
87328
|
try {
|
|
@@ -87178,7 +87331,7 @@ async function readSessionMessages(sessionID) {
|
|
|
87178
87331
|
if (!file3.endsWith(".json"))
|
|
87179
87332
|
continue;
|
|
87180
87333
|
try {
|
|
87181
|
-
const content = await readFile3(
|
|
87334
|
+
const content = await readFile3(join69(messageDir, file3), "utf-8");
|
|
87182
87335
|
const meta3 = JSON.parse(content);
|
|
87183
87336
|
const parts = await readParts2(meta3.id);
|
|
87184
87337
|
messages2.push({
|
|
@@ -87202,8 +87355,8 @@ async function readSessionMessages(sessionID) {
|
|
|
87202
87355
|
});
|
|
87203
87356
|
}
|
|
87204
87357
|
async function readParts2(messageID) {
|
|
87205
|
-
const partDir =
|
|
87206
|
-
if (!
|
|
87358
|
+
const partDir = join69(PART_STORAGE, messageID);
|
|
87359
|
+
if (!existsSync68(partDir))
|
|
87207
87360
|
return [];
|
|
87208
87361
|
const parts = [];
|
|
87209
87362
|
try {
|
|
@@ -87212,7 +87365,7 @@ async function readParts2(messageID) {
|
|
|
87212
87365
|
if (!file3.endsWith(".json"))
|
|
87213
87366
|
continue;
|
|
87214
87367
|
try {
|
|
87215
|
-
const content = await readFile3(
|
|
87368
|
+
const content = await readFile3(join69(partDir, file3), "utf-8");
|
|
87216
87369
|
parts.push(JSON.parse(content));
|
|
87217
87370
|
} catch {}
|
|
87218
87371
|
}
|
|
@@ -87236,14 +87389,14 @@ async function readSessionTodos(sessionID) {
|
|
|
87236
87389
|
return [];
|
|
87237
87390
|
}
|
|
87238
87391
|
}
|
|
87239
|
-
if (!
|
|
87392
|
+
if (!existsSync68(TODO_DIR))
|
|
87240
87393
|
return [];
|
|
87241
87394
|
try {
|
|
87242
87395
|
const allFiles = await readdir(TODO_DIR);
|
|
87243
87396
|
const todoFiles = allFiles.filter((f) => f.includes(sessionID) && f.endsWith(".json"));
|
|
87244
87397
|
for (const file3 of todoFiles) {
|
|
87245
87398
|
try {
|
|
87246
|
-
const content = await readFile3(
|
|
87399
|
+
const content = await readFile3(join69(TODO_DIR, file3), "utf-8");
|
|
87247
87400
|
const data = JSON.parse(content);
|
|
87248
87401
|
if (Array.isArray(data)) {
|
|
87249
87402
|
return data.map((item) => ({
|
|
@@ -87261,10 +87414,10 @@ async function readSessionTodos(sessionID) {
|
|
|
87261
87414
|
return [];
|
|
87262
87415
|
}
|
|
87263
87416
|
async function readSessionTranscript(sessionID) {
|
|
87264
|
-
if (!
|
|
87417
|
+
if (!existsSync68(TRANSCRIPT_DIR2))
|
|
87265
87418
|
return 0;
|
|
87266
|
-
const transcriptFile =
|
|
87267
|
-
if (!
|
|
87419
|
+
const transcriptFile = join69(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
|
|
87420
|
+
if (!existsSync68(transcriptFile))
|
|
87268
87421
|
return 0;
|
|
87269
87422
|
try {
|
|
87270
87423
|
const content = await readFile3(transcriptFile, "utf-8");
|
|
@@ -87774,20 +87927,20 @@ function createSkillTool(options = {}) {
|
|
|
87774
87927
|
}
|
|
87775
87928
|
var skill = createSkillTool();
|
|
87776
87929
|
// src/tools/slashcommand/command-discovery.ts
|
|
87777
|
-
import { existsSync as
|
|
87778
|
-
import { basename as basename8, join as
|
|
87930
|
+
import { existsSync as existsSync69, readdirSync as readdirSync21, readFileSync as readFileSync41 } from "fs";
|
|
87931
|
+
import { basename as basename8, join as join70 } from "path";
|
|
87779
87932
|
function discoverCommandsFromDir2(commandsDir, scope) {
|
|
87780
|
-
if (!
|
|
87933
|
+
if (!existsSync69(commandsDir))
|
|
87781
87934
|
return [];
|
|
87782
87935
|
const entries = readdirSync21(commandsDir, { withFileTypes: true });
|
|
87783
87936
|
const commands2 = [];
|
|
87784
87937
|
for (const entry of entries) {
|
|
87785
87938
|
if (!isMarkdownFile(entry))
|
|
87786
87939
|
continue;
|
|
87787
|
-
const commandPath =
|
|
87940
|
+
const commandPath = join70(commandsDir, entry.name);
|
|
87788
87941
|
const commandName = basename8(entry.name, ".md");
|
|
87789
87942
|
try {
|
|
87790
|
-
const content =
|
|
87943
|
+
const content = readFileSync41(commandPath, "utf-8");
|
|
87791
87944
|
const { data, body } = parseFrontmatter(content);
|
|
87792
87945
|
const isOpencodeSource = scope === "opencode" || scope === "opencode-project";
|
|
87793
87946
|
const metadata = {
|
|
@@ -87811,8 +87964,8 @@ function discoverCommandsFromDir2(commandsDir, scope) {
|
|
|
87811
87964
|
}
|
|
87812
87965
|
function discoverCommandsSync(directory) {
|
|
87813
87966
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
87814
|
-
const opencodeGlobalDir =
|
|
87815
|
-
const opencodeProjectDir =
|
|
87967
|
+
const opencodeGlobalDir = join70(configDir, "command");
|
|
87968
|
+
const opencodeProjectDir = join70(directory ?? process.cwd(), ".opencode", "command");
|
|
87816
87969
|
const opencodeGlobalCommands = discoverCommandsFromDir2(opencodeGlobalDir, "opencode");
|
|
87817
87970
|
const opencodeProjectCommands = discoverCommandsFromDir2(opencodeProjectDir, "opencode-project");
|
|
87818
87971
|
const builtinCommandsMap = loadBuiltinCommands();
|
|
@@ -87836,7 +87989,7 @@ function discoverCommandsSync(directory) {
|
|
|
87836
87989
|
];
|
|
87837
87990
|
}
|
|
87838
87991
|
// src/tools/slashcommand/command-output-formatter.ts
|
|
87839
|
-
import { dirname as
|
|
87992
|
+
import { dirname as dirname18 } from "path";
|
|
87840
87993
|
async function formatLoadedCommand(command, userMessage) {
|
|
87841
87994
|
const sections = [];
|
|
87842
87995
|
sections.push(`# /${command.name} Command
|
|
@@ -87875,7 +88028,7 @@ async function formatLoadedCommand(command, userMessage) {
|
|
|
87875
88028
|
if (!content && command.lazyContentLoader) {
|
|
87876
88029
|
content = await command.lazyContentLoader.load();
|
|
87877
88030
|
}
|
|
87878
|
-
const commandDir = command.path ?
|
|
88031
|
+
const commandDir = command.path ? dirname18(command.path) : process.cwd();
|
|
87879
88032
|
const withFileReferences = await resolveFileReferencesInText(content, commandDir);
|
|
87880
88033
|
const resolvedContent = await resolveCommandsInText(withFileReferences);
|
|
87881
88034
|
let finalContent = resolvedContent.trim();
|
|
@@ -88587,8 +88740,6 @@ function createBackgroundOutput(manager, client2) {
|
|
|
88587
88740
|
}
|
|
88588
88741
|
});
|
|
88589
88742
|
}
|
|
88590
|
-
// src/tools/background-task/create-background-task.ts
|
|
88591
|
-
init_logger();
|
|
88592
88743
|
// src/tools/assembly/constants.ts
|
|
88593
88744
|
var DEFAULT_VOTERS = 3;
|
|
88594
88745
|
var DEFAULT_ROUNDS = 2;
|
|
@@ -88954,9 +89105,9 @@ function createAssemblyTool(options) {
|
|
|
88954
89105
|
});
|
|
88955
89106
|
}
|
|
88956
89107
|
// src/tools/dcp-switch-profile/tools.ts
|
|
88957
|
-
import { existsSync as
|
|
88958
|
-
import { homedir as
|
|
88959
|
-
import { join as
|
|
89108
|
+
import { existsSync as existsSync70, writeFileSync as writeFileSync25 } from "fs";
|
|
89109
|
+
import { homedir as homedir10 } from "os";
|
|
89110
|
+
import { join as join71 } from "path";
|
|
88960
89111
|
|
|
88961
89112
|
// src/config/schema/dcp.ts
|
|
88962
89113
|
var DcpCompressOverrideSchema = exports_external.object({
|
|
@@ -89129,11 +89280,11 @@ var DcpConfigSchema = exports_external.object({
|
|
|
89129
89280
|
});
|
|
89130
89281
|
|
|
89131
89282
|
// src/tools/dcp-switch-profile/tools.ts
|
|
89132
|
-
var DCP_PLUGIN_DIR =
|
|
89133
|
-
var DCP_SYMLINK =
|
|
89283
|
+
var DCP_PLUGIN_DIR = join71(homedir10(), ".config", "opencode", "node_modules", "@tarquinen", "opencode-dcp");
|
|
89284
|
+
var DCP_SYMLINK = join71(homedir10(), ".config", "opencode", "dcp.jsonc");
|
|
89134
89285
|
var VALID_PROFILES = ["economy", "balanced", "performance", "ultimate"];
|
|
89135
89286
|
function checkDcpInstalled() {
|
|
89136
|
-
if (!
|
|
89287
|
+
if (!existsSync70(DCP_PLUGIN_DIR)) {
|
|
89137
89288
|
return `DCP is not installed at ${DCP_PLUGIN_DIR}. Install it with: npm install --prefix ~/.config/opencode @tarquinen/opencode-dcp`;
|
|
89138
89289
|
}
|
|
89139
89290
|
return null;
|
|
@@ -89211,7 +89362,7 @@ function switchProfile(profile, options) {
|
|
|
89211
89362
|
if (dcpError)
|
|
89212
89363
|
return dcpError;
|
|
89213
89364
|
const inlineConfig = buildInlineConfig(profile, options);
|
|
89214
|
-
|
|
89365
|
+
writeFileSync25(DCP_SYMLINK, `${JSON.stringify(inlineConfig, null, 2)}
|
|
89215
89366
|
`);
|
|
89216
89367
|
return `\u2713 Switched to DCP profile: ${profile}
|
|
89217
89368
|
|
|
@@ -89557,149 +89708,23 @@ function createDelegateAgent(ctx, backgroundManager, disabledAgents = []) {
|
|
|
89557
89708
|
}
|
|
89558
89709
|
});
|
|
89559
89710
|
}
|
|
89560
|
-
// src/shared/tier-resolver.ts
|
|
89561
|
-
init_logger();
|
|
89562
|
-
|
|
89563
|
-
// src/shared/model-tiers.ts
|
|
89564
|
-
function compilePattern(pattern) {
|
|
89565
|
-
try {
|
|
89566
|
-
return new RegExp(pattern);
|
|
89567
|
-
} catch {
|
|
89568
|
-
return /(?!)/;
|
|
89569
|
-
}
|
|
89570
|
-
}
|
|
89571
|
-
var DEFAULT_TIER_SPECS = {
|
|
89572
|
-
free: {
|
|
89573
|
-
name: "free",
|
|
89574
|
-
providerPriority: ["opencode", "xai", "opencode-go", "zai-coding-plan"],
|
|
89575
|
-
modelPattern: /-free$|kimi-k2\.5-free|minimax-m2\.5-free|grok-code-fast/,
|
|
89576
|
-
fallback: [
|
|
89577
|
-
{ providers: ["opencode"], model: "kimi-k2.5-free" },
|
|
89578
|
-
{ providers: ["xai"], model: "grok-code-fast-1" }
|
|
89579
|
-
]
|
|
89580
|
-
},
|
|
89581
|
-
fast: {
|
|
89582
|
-
name: "fast",
|
|
89583
|
-
providerPriority: ["anthropic", "openai", "google", "opencode-go"],
|
|
89584
|
-
modelPattern: /claude-haiku|gpt-5-nano|gemini-2\.5-flash|deepseek-v4-flash/,
|
|
89585
|
-
fallback: [{ providers: ["anthropic"], model: "claude-haiku-4-5" }],
|
|
89586
|
-
fallbackTier: "free"
|
|
89587
|
-
},
|
|
89588
|
-
standard: {
|
|
89589
|
-
name: "standard",
|
|
89590
|
-
providerPriority: ["anthropic", "openai", "google", "opencode-go"],
|
|
89591
|
-
modelPattern: /claude-sonnet|gpt-5\.2|gemini-2\.5-pro/,
|
|
89592
|
-
fallback: [{ providers: ["anthropic"], model: "claude-sonnet-4-6" }],
|
|
89593
|
-
fallbackTier: "fast"
|
|
89594
|
-
},
|
|
89595
|
-
premium: {
|
|
89596
|
-
name: "premium",
|
|
89597
|
-
providerPriority: ["anthropic", "openai", "google", "opencode-go"],
|
|
89598
|
-
modelPattern: /claude-opus|gpt-5\.3-codex|gemini-3-pro/,
|
|
89599
|
-
fallback: [{ providers: ["anthropic"], model: "claude-opus-4-6" }],
|
|
89600
|
-
fallbackTier: "standard"
|
|
89601
|
-
},
|
|
89602
|
-
frontier: {
|
|
89603
|
-
name: "frontier",
|
|
89604
|
-
providerPriority: ["anthropic", "openai", "google", "opencode-go"],
|
|
89605
|
-
modelPattern: /claude-opus|gpt-5\.3-codex|gemini-3\.1-pro/,
|
|
89606
|
-
fallback: [{ providers: ["anthropic"], model: "claude-opus-4-6" }],
|
|
89607
|
-
fallbackTier: "premium"
|
|
89608
|
-
}
|
|
89609
|
-
};
|
|
89610
|
-
function buildTierSpecs(config4) {
|
|
89611
|
-
if (!config4?.tiers)
|
|
89612
|
-
return DEFAULT_TIER_SPECS;
|
|
89613
|
-
const result = {};
|
|
89614
|
-
for (const [name, raw] of Object.entries(config4.tiers)) {
|
|
89615
|
-
if (!raw || typeof raw !== "object")
|
|
89616
|
-
continue;
|
|
89617
|
-
const providerPriority = Array.isArray(raw.providerPriority) ? raw.providerPriority : [];
|
|
89618
|
-
const rawPattern = raw.modelPattern;
|
|
89619
|
-
const patternString = typeof rawPattern === "string" ? rawPattern : "";
|
|
89620
|
-
result[name] = {
|
|
89621
|
-
name,
|
|
89622
|
-
providerPriority,
|
|
89623
|
-
modelPattern: compilePattern(patternString),
|
|
89624
|
-
fallbackTier: raw.fallbackTier,
|
|
89625
|
-
fallback: raw.fallback
|
|
89626
|
-
};
|
|
89627
|
-
}
|
|
89628
|
-
return result;
|
|
89629
|
-
}
|
|
89630
|
-
function getTierSpec(tierName, config4) {
|
|
89631
|
-
if (!tierName)
|
|
89632
|
-
return;
|
|
89633
|
-
const specs = buildTierSpecs(config4);
|
|
89634
|
-
return specs[tierName];
|
|
89635
|
-
}
|
|
89636
|
-
|
|
89637
|
-
// src/shared/tier-resolver.ts
|
|
89638
|
-
function resolveTier(tier, ctx, config4) {
|
|
89639
|
-
const spec = getTierSpec(tier, config4);
|
|
89640
|
-
if (!spec)
|
|
89641
|
-
return null;
|
|
89642
|
-
if (ctx.availableModels.size > 0) {
|
|
89643
|
-
for (const provider of spec.providerPriority) {
|
|
89644
|
-
const match = findRegexMatch(spec.modelPattern, provider, ctx.availableModels);
|
|
89645
|
-
if (match) {
|
|
89646
|
-
log("[tier-resolver] resolved via regex", { tier, provider, match });
|
|
89647
|
-
return { model: match, provenance: "tier-resolved", tier };
|
|
89648
|
-
}
|
|
89649
|
-
}
|
|
89650
|
-
log("[tier-resolver] no live match found, recursing to fallback tier", { tier });
|
|
89651
|
-
} else if (ctx.connectedProviders) {
|
|
89652
|
-
const connectedSet = new Set(ctx.connectedProviders);
|
|
89653
|
-
const fallback = spec.fallback ?? [];
|
|
89654
|
-
for (const entry of fallback) {
|
|
89655
|
-
for (const provider of entry.providers) {
|
|
89656
|
-
if (connectedSet.has(provider)) {
|
|
89657
|
-
const model = `${provider}/${entry.model}`;
|
|
89658
|
-
log("[tier-resolver] using config fallback (cold cache)", { tier, provider, model });
|
|
89659
|
-
return { model, provenance: "tier-static-fallback", tier };
|
|
89660
|
-
}
|
|
89661
|
-
}
|
|
89662
|
-
}
|
|
89663
|
-
log("[tier-resolver] no config fallback provider is connected", { tier });
|
|
89664
|
-
} else {
|
|
89665
|
-
log("[tier-resolver] no live models and no connected providers cache", { tier });
|
|
89666
|
-
}
|
|
89667
|
-
if (spec.fallbackTier) {
|
|
89668
|
-
return resolveTier(spec.fallbackTier, ctx, config4);
|
|
89669
|
-
}
|
|
89670
|
-
return null;
|
|
89671
|
-
}
|
|
89672
|
-
function findRegexMatch(pattern, provider, available) {
|
|
89673
|
-
for (const candidate of available) {
|
|
89674
|
-
if (!candidate.startsWith(`${provider}/`))
|
|
89675
|
-
continue;
|
|
89676
|
-
if (pattern.test(candidate))
|
|
89677
|
-
return candidate;
|
|
89678
|
-
}
|
|
89679
|
-
return null;
|
|
89680
|
-
}
|
|
89681
|
-
|
|
89682
89711
|
// src/tools/delegate-task/complexity-types.ts
|
|
89683
89712
|
function isDowngradable(level) {
|
|
89684
89713
|
return level === 1 || level === 2;
|
|
89685
89714
|
}
|
|
89686
89715
|
|
|
89687
89716
|
// src/tools/delegate-task/complexity-constants.ts
|
|
89688
|
-
function resolveComplexityModel(category, complexity, originalModel, userDowngrades, config4
|
|
89717
|
+
function resolveComplexityModel(category, complexity, originalModel, userDowngrades, config4) {
|
|
89689
89718
|
if (!isDowngradable(complexity)) {
|
|
89690
89719
|
return { model: originalModel, downgraded: false };
|
|
89691
89720
|
}
|
|
89692
89721
|
let complexityDowngrades;
|
|
89693
|
-
let tiersConfig;
|
|
89694
89722
|
if (config4) {
|
|
89695
89723
|
const maybe = config4;
|
|
89696
|
-
const hasHolderKeys = "complexityDowngrades" in maybe
|
|
89724
|
+
const hasHolderKeys = "complexityDowngrades" in maybe;
|
|
89697
89725
|
if (hasHolderKeys) {
|
|
89698
89726
|
const holder = config4;
|
|
89699
89727
|
complexityDowngrades = holder.complexityDowngrades;
|
|
89700
|
-
if (holder.tiers) {
|
|
89701
|
-
tiersConfig = holder;
|
|
89702
|
-
}
|
|
89703
89728
|
} else {
|
|
89704
89729
|
complexityDowngrades = config4;
|
|
89705
89730
|
}
|
|
@@ -89709,20 +89734,6 @@ function resolveComplexityModel(category, complexity, originalModel, userDowngra
|
|
|
89709
89734
|
if (!rawDowngrade) {
|
|
89710
89735
|
return { model: originalModel, downgraded: false };
|
|
89711
89736
|
}
|
|
89712
|
-
if (rawDowngrade.startsWith("tier:")) {
|
|
89713
|
-
const tierName = rawDowngrade.slice(5).trim();
|
|
89714
|
-
if (!tierName) {
|
|
89715
|
-
return { model: originalModel, downgraded: false };
|
|
89716
|
-
}
|
|
89717
|
-
if (!tierContext || !tiersConfig) {
|
|
89718
|
-
return { model: originalModel, downgraded: false };
|
|
89719
|
-
}
|
|
89720
|
-
const resolved = resolveTier(tierName, tierContext, tiersConfig);
|
|
89721
|
-
if (!resolved) {
|
|
89722
|
-
return { model: originalModel, downgraded: false };
|
|
89723
|
-
}
|
|
89724
|
-
return { model: resolved.model, downgraded: true };
|
|
89725
|
-
}
|
|
89726
89737
|
return { model: rawDowngrade, downgraded: true };
|
|
89727
89738
|
}
|
|
89728
89739
|
// src/tools/delegate-task/complexity-scorer.ts
|
|
@@ -89999,14 +90010,14 @@ You are NOT an interactive assistant. You are an autonomous problem-solver.
|
|
|
89999
90010
|
- Report completion with summary of changes made
|
|
90000
90011
|
</Category_Context>`;
|
|
90001
90012
|
var DEFAULT_CATEGORIES = {
|
|
90002
|
-
construct: {
|
|
90003
|
-
source: {
|
|
90004
|
-
"deep-jack": {
|
|
90005
|
-
"matrix-bend": {
|
|
90006
|
-
"bullet-time": {
|
|
90007
|
-
"blue-pill": {
|
|
90008
|
-
"red-pill": {
|
|
90009
|
-
broadcast: {
|
|
90013
|
+
construct: {},
|
|
90014
|
+
source: { variant: "max" },
|
|
90015
|
+
"deep-jack": { variant: "max" },
|
|
90016
|
+
"matrix-bend": {},
|
|
90017
|
+
"bullet-time": {},
|
|
90018
|
+
"blue-pill": {},
|
|
90019
|
+
"red-pill": { variant: "max" },
|
|
90020
|
+
broadcast: {}
|
|
90010
90021
|
};
|
|
90011
90022
|
var CATEGORY_PROMPT_APPENDS = {
|
|
90012
90023
|
construct: VISUAL_CATEGORY_PROMPT_APPEND,
|
|
@@ -90339,93 +90350,10 @@ function isPlanFamily(category) {
|
|
|
90339
90350
|
// src/tools/delegate-task/tools.ts
|
|
90340
90351
|
init_logger();
|
|
90341
90352
|
|
|
90342
|
-
// src/config/resolve-tiers.ts
|
|
90343
|
-
init_logger();
|
|
90344
|
-
function resolveTiersInConfig(config4, ctx) {
|
|
90345
|
-
const defaultTier = config4.default_tier;
|
|
90346
|
-
const agents = walkAgentOverrides(config4.agents, defaultTier, ctx, config4);
|
|
90347
|
-
const categories = walkCategoryOverrides(config4.categories, defaultTier, ctx, config4);
|
|
90348
|
-
const { default_tier: _drop, ...rest } = config4;
|
|
90349
|
-
return { ...rest, agents, categories };
|
|
90350
|
-
}
|
|
90351
|
-
function resolveTiersInCategoryRegistry(registry4, ctx, config4) {
|
|
90352
|
-
const result = {};
|
|
90353
|
-
for (const [name, entry] of Object.entries(registry4)) {
|
|
90354
|
-
if (!entry)
|
|
90355
|
-
continue;
|
|
90356
|
-
result[name] = resolveEntry(entry, undefined, ctx, config4);
|
|
90357
|
-
}
|
|
90358
|
-
return result;
|
|
90359
|
-
}
|
|
90360
|
-
function walkAgentOverrides(agents, defaultTier, ctx, config4) {
|
|
90361
|
-
if (!agents)
|
|
90362
|
-
return agents;
|
|
90363
|
-
const result = {};
|
|
90364
|
-
for (const [name, entry] of Object.entries(agents)) {
|
|
90365
|
-
if (!entry) {
|
|
90366
|
-
result[name] = entry;
|
|
90367
|
-
continue;
|
|
90368
|
-
}
|
|
90369
|
-
result[name] = resolveEntry(entry, defaultTier, ctx, config4);
|
|
90370
|
-
}
|
|
90371
|
-
return result;
|
|
90372
|
-
}
|
|
90373
|
-
function walkCategoryOverrides(categories, defaultTier, ctx, config4) {
|
|
90374
|
-
if (!categories)
|
|
90375
|
-
return categories;
|
|
90376
|
-
const result = {};
|
|
90377
|
-
for (const [name, entry] of Object.entries(categories)) {
|
|
90378
|
-
if (!entry) {
|
|
90379
|
-
result[name] = entry;
|
|
90380
|
-
continue;
|
|
90381
|
-
}
|
|
90382
|
-
result[name] = resolveEntry(entry, defaultTier, ctx, config4);
|
|
90383
|
-
}
|
|
90384
|
-
return result;
|
|
90385
|
-
}
|
|
90386
|
-
function resolveEntry(entry, defaultTier, ctx, config4) {
|
|
90387
|
-
if (entry.model) {
|
|
90388
|
-
const { tier: _t2, ...rest2 } = entry;
|
|
90389
|
-
return rest2;
|
|
90390
|
-
}
|
|
90391
|
-
const tier = entry.tier ?? defaultTier;
|
|
90392
|
-
if (!tier)
|
|
90393
|
-
return entry;
|
|
90394
|
-
const resolved = resolveTier(tier, ctx, config4);
|
|
90395
|
-
if (!resolved) {
|
|
90396
|
-
log("[resolveTiersInConfig] tier could not be resolved", { tier });
|
|
90397
|
-
return entry;
|
|
90398
|
-
}
|
|
90399
|
-
const { tier: _t, ...rest } = entry;
|
|
90400
|
-
return { ...rest, model: resolved.model };
|
|
90401
|
-
}
|
|
90402
|
-
|
|
90403
90353
|
// src/shared/merge-categories.ts
|
|
90404
|
-
function mergeCategories(userCategories
|
|
90405
|
-
const
|
|
90406
|
-
|
|
90407
|
-
return Object.fromEntries(Object.entries(merged).filter(([, config5]) => !config5.disable));
|
|
90408
|
-
}
|
|
90409
|
-
function resolveDefaultCategories(config4) {
|
|
90410
|
-
const providerCache = readProviderModelsCache();
|
|
90411
|
-
const availableModels = new Set;
|
|
90412
|
-
if (providerCache) {
|
|
90413
|
-
const connected = new Set(providerCache.connected);
|
|
90414
|
-
for (const [providerId, models] of Object.entries(providerCache.models)) {
|
|
90415
|
-
if (!connected.has(providerId))
|
|
90416
|
-
continue;
|
|
90417
|
-
for (const item of models) {
|
|
90418
|
-
const modelID = typeof item === "string" ? item : item?.id;
|
|
90419
|
-
if (modelID)
|
|
90420
|
-
availableModels.add(`${providerId}/${modelID}`);
|
|
90421
|
-
}
|
|
90422
|
-
}
|
|
90423
|
-
}
|
|
90424
|
-
const ctx = {
|
|
90425
|
-
availableModels,
|
|
90426
|
-
connectedProviders: readConnectedProvidersCache()
|
|
90427
|
-
};
|
|
90428
|
-
return resolveTiersInCategoryRegistry(DEFAULT_CATEGORIES, ctx, config4);
|
|
90354
|
+
function mergeCategories(userCategories) {
|
|
90355
|
+
const merged = userCategories ? { ...DEFAULT_CATEGORIES, ...userCategories } : { ...DEFAULT_CATEGORIES };
|
|
90356
|
+
return Object.fromEntries(Object.entries(merged).filter(([, config4]) => !config4.disable));
|
|
90429
90357
|
}
|
|
90430
90358
|
// src/tools/delegate-task/background-continuation.ts
|
|
90431
90359
|
async function executeBackgroundContinuation(args, ctx, executorCtx, parentContext) {
|
|
@@ -90500,6 +90428,9 @@ function getTimingConfig() {
|
|
|
90500
90428
|
SESSION_CONTINUATION_STABILITY_MS
|
|
90501
90429
|
};
|
|
90502
90430
|
}
|
|
90431
|
+
function setPollTimeoutMs(ms) {
|
|
90432
|
+
MAX_POLL_TIME_MS = ms;
|
|
90433
|
+
}
|
|
90503
90434
|
|
|
90504
90435
|
// src/tools/delegate-task/background-task.ts
|
|
90505
90436
|
async function executeBackgroundTask(args, ctx, executorCtx, parentContext, agentToUse, categoryModel, systemContent) {
|
|
@@ -90578,6 +90509,67 @@ session_id: ${sessionId}
|
|
|
90578
90509
|
});
|
|
90579
90510
|
}
|
|
90580
90511
|
}
|
|
90512
|
+
// src/config/preset-applier.ts
|
|
90513
|
+
init_logger();
|
|
90514
|
+
function resolvePresetModel(preset, key) {
|
|
90515
|
+
const entry = preset.agents?.[key] ?? preset.categories?.[key];
|
|
90516
|
+
if (entry)
|
|
90517
|
+
return entry;
|
|
90518
|
+
if (preset.default_model)
|
|
90519
|
+
return { model: preset.default_model };
|
|
90520
|
+
return;
|
|
90521
|
+
}
|
|
90522
|
+
function applyActivePreset(config4, presetName) {
|
|
90523
|
+
const name = presetName ?? config4.active_preset;
|
|
90524
|
+
if (!name)
|
|
90525
|
+
return config4;
|
|
90526
|
+
const preset = config4.model_presets?.[name];
|
|
90527
|
+
if (!preset) {
|
|
90528
|
+
log("[applyActivePreset] unknown active_preset", { active_preset: name });
|
|
90529
|
+
return config4;
|
|
90530
|
+
}
|
|
90531
|
+
const agents = fillRegistry(config4.agents, preset.agents, preset.default_model);
|
|
90532
|
+
const categories = fillRegistry(config4.categories, preset.categories, preset.default_model);
|
|
90533
|
+
const result = { ...config4 };
|
|
90534
|
+
if (agents !== config4.agents)
|
|
90535
|
+
result.agents = agents;
|
|
90536
|
+
if (categories !== config4.categories) {
|
|
90537
|
+
result.categories = categories;
|
|
90538
|
+
}
|
|
90539
|
+
return result;
|
|
90540
|
+
}
|
|
90541
|
+
function fillRegistry(registry4, presetEntries, defaultModel) {
|
|
90542
|
+
if (!registry4)
|
|
90543
|
+
return registry4;
|
|
90544
|
+
let changed = false;
|
|
90545
|
+
const result = {};
|
|
90546
|
+
for (const [name, entry] of Object.entries(registry4)) {
|
|
90547
|
+
if (!entry) {
|
|
90548
|
+
result[name] = entry;
|
|
90549
|
+
continue;
|
|
90550
|
+
}
|
|
90551
|
+
const filled = applyEntry(entry, presetEntries?.[name], defaultModel);
|
|
90552
|
+
if (filled) {
|
|
90553
|
+
changed = true;
|
|
90554
|
+
result[name] = filled;
|
|
90555
|
+
} else {
|
|
90556
|
+
result[name] = entry;
|
|
90557
|
+
}
|
|
90558
|
+
}
|
|
90559
|
+
return changed ? result : registry4;
|
|
90560
|
+
}
|
|
90561
|
+
function applyEntry(entry, presetEntry, defaultModel) {
|
|
90562
|
+
if (entry.model)
|
|
90563
|
+
return null;
|
|
90564
|
+
const model = presetEntry?.model ?? defaultModel;
|
|
90565
|
+
if (!model)
|
|
90566
|
+
return null;
|
|
90567
|
+
const variant = entry.variant ?? presetEntry?.variant;
|
|
90568
|
+
if (variant === undefined)
|
|
90569
|
+
return { ...entry, model };
|
|
90570
|
+
return { ...entry, model, variant };
|
|
90571
|
+
}
|
|
90572
|
+
|
|
90581
90573
|
// src/tools/delegate-task/category-resolver.ts
|
|
90582
90574
|
init_logger();
|
|
90583
90575
|
|
|
@@ -90635,7 +90627,7 @@ async function getAvailableModelsForDelegateTask(client2) {
|
|
|
90635
90627
|
// src/tools/delegate-task/categories.ts
|
|
90636
90628
|
init_logger();
|
|
90637
90629
|
function resolveCategoryConfig(categoryName, options) {
|
|
90638
|
-
const { userCategories, inheritedModel: _inheritedModel, systemDefaultModel, availableModels,
|
|
90630
|
+
const { userCategories, inheritedModel: _inheritedModel, systemDefaultModel, availableModels, modelRequirements, presetEntry } = options;
|
|
90639
90631
|
const defaultConfig = DEFAULT_CATEGORIES[categoryName];
|
|
90640
90632
|
const userConfig = userCategories?.[categoryName];
|
|
90641
90633
|
const hasExplicitUserConfig = userConfig !== undefined;
|
|
@@ -90654,16 +90646,13 @@ function resolveCategoryConfig(categoryName, options) {
|
|
|
90654
90646
|
if (!defaultConfig && !userConfig) {
|
|
90655
90647
|
return null;
|
|
90656
90648
|
}
|
|
90657
|
-
const
|
|
90658
|
-
const
|
|
90659
|
-
const effectiveDefaultModel = resolveTierOnEntry(defaultConfig, tierContext, tierConfig);
|
|
90660
|
-
const model = normalizeModel(effectiveUserModel ?? userConfig?.model) ?? normalizeModel(effectiveDefaultModel ?? defaultConfig?.model) ?? systemDefaultModel;
|
|
90661
|
-
const hasUserModelOverride = (effectiveUserModel ?? userConfig?.model) !== undefined;
|
|
90649
|
+
const model = normalizeModel(userConfig?.model) ?? normalizeModel(presetEntry?.model) ?? normalizeModel(defaultConfig?.model) ?? systemDefaultModel;
|
|
90650
|
+
const hasUserModelOverride = (userConfig?.model ?? presetEntry?.model) !== undefined;
|
|
90662
90651
|
const config4 = {
|
|
90663
90652
|
...defaultConfig,
|
|
90664
90653
|
...userConfig,
|
|
90665
90654
|
model,
|
|
90666
|
-
variant: userConfig?.variant ?? (hasUserModelOverride ? undefined : defaultConfig?.variant)
|
|
90655
|
+
variant: userConfig?.variant ?? presetEntry?.variant ?? (hasUserModelOverride ? undefined : defaultConfig?.variant)
|
|
90667
90656
|
};
|
|
90668
90657
|
let promptAppend = defaultPromptAppend;
|
|
90669
90658
|
if (userConfig?.prompt_append) {
|
|
@@ -90673,12 +90662,6 @@ ${userConfig.prompt_append}` : userConfig.prompt_append;
|
|
|
90673
90662
|
}
|
|
90674
90663
|
return { config: config4, promptAppend, model, temperature: config4.temperature };
|
|
90675
90664
|
}
|
|
90676
|
-
function resolveTierOnEntry(entry, ctx, tiersConfig) {
|
|
90677
|
-
if (!entry || entry.model || !entry.tier || !ctx)
|
|
90678
|
-
return;
|
|
90679
|
-
const resolved = resolveTier(entry.tier, ctx, tiersConfig);
|
|
90680
|
-
return resolved?.model;
|
|
90681
|
-
}
|
|
90682
90665
|
|
|
90683
90666
|
// src/tools/delegate-task/model-selection.ts
|
|
90684
90667
|
function normalizeModel2(model) {
|
|
@@ -90737,11 +90720,33 @@ function resolveModelForDelegateTask(input) {
|
|
|
90737
90720
|
var MOUSE_AGENT2 = "mouse";
|
|
90738
90721
|
|
|
90739
90722
|
// src/tools/delegate-task/category-resolver.ts
|
|
90723
|
+
function resolveCategoryPresetEntry(categoryName, ctx) {
|
|
90724
|
+
const { sessionID, modelPresets, activePreset } = ctx;
|
|
90725
|
+
if (!modelPresets)
|
|
90726
|
+
return;
|
|
90727
|
+
const overlayName = sessionID ? getSessionPreset(sessionID) : undefined;
|
|
90728
|
+
let result;
|
|
90729
|
+
if (overlayName) {
|
|
90730
|
+
const overlayPreset = modelPresets[overlayName];
|
|
90731
|
+
if (overlayPreset) {
|
|
90732
|
+
const entry = resolvePresetModel(overlayPreset, categoryName);
|
|
90733
|
+
if (entry)
|
|
90734
|
+
result = entry;
|
|
90735
|
+
} else {
|
|
90736
|
+
log("[preset] unknown session overlay preset, ignoring", { sessionID, presetName: overlayName });
|
|
90737
|
+
}
|
|
90738
|
+
}
|
|
90739
|
+
if (!result && activePreset) {
|
|
90740
|
+
const preset = modelPresets[activePreset];
|
|
90741
|
+
if (preset)
|
|
90742
|
+
result = resolvePresetModel(preset, categoryName);
|
|
90743
|
+
}
|
|
90744
|
+
return result;
|
|
90745
|
+
}
|
|
90740
90746
|
async function resolveCategoryExecution(args, executorCtx, inheritedModel, systemDefaultModel) {
|
|
90741
|
-
const { client: client2, userCategories, mouseModel, globalModel, modelRequirements, complexityDowngrades
|
|
90747
|
+
const { client: client2, userCategories, mouseModel, globalModel, modelRequirements, complexityDowngrades } = executorCtx;
|
|
90742
90748
|
const categoryRequirements = getCategoryModelRequirements(modelRequirements ? { modelRequirements } : undefined);
|
|
90743
90749
|
const availableModels = await getAvailableModelsForDelegateTask(client2);
|
|
90744
|
-
const connectedProviders = readConnectedProvidersCache();
|
|
90745
90750
|
const categoryName = args.category;
|
|
90746
90751
|
const enabledCategories = mergeCategories(userCategories);
|
|
90747
90752
|
const categoryExists = enabledCategories[categoryName] !== undefined;
|
|
@@ -90750,8 +90755,8 @@ async function resolveCategoryExecution(args, executorCtx, inheritedModel, syste
|
|
|
90750
90755
|
inheritedModel,
|
|
90751
90756
|
systemDefaultModel,
|
|
90752
90757
|
availableModels,
|
|
90753
|
-
|
|
90754
|
-
|
|
90758
|
+
modelRequirements,
|
|
90759
|
+
presetEntry: resolveCategoryPresetEntry(categoryName, executorCtx)
|
|
90755
90760
|
});
|
|
90756
90761
|
if (!resolved) {
|
|
90757
90762
|
const requirement2 = categoryRequirements[categoryName];
|
|
@@ -90847,9 +90852,7 @@ Available categories: ${allCategoryNames}`
|
|
|
90847
90852
|
}
|
|
90848
90853
|
if (actualModel && (complexityLevel === 1 || complexityLevel === 2)) {
|
|
90849
90854
|
const userDowngrades = userCategories?.[args.category]?.complexity_downgrades;
|
|
90850
|
-
const
|
|
90851
|
-
const tierCtx = { availableModels, connectedProviders };
|
|
90852
|
-
const resolvedDowngrade = resolveComplexityModel(args.category, complexityLevel, actualModel, userDowngrades, holder, tierCtx);
|
|
90855
|
+
const resolvedDowngrade = resolveComplexityModel(args.category, complexityLevel, actualModel, userDowngrades, complexityDowngrades);
|
|
90853
90856
|
if (resolvedDowngrade.downgraded) {
|
|
90854
90857
|
complexityDowngraded = true;
|
|
90855
90858
|
log("[complexity] Auto-downgraded model", {
|
|
@@ -91365,7 +91368,7 @@ async function pollSyncSession(ctx, client2, input) {
|
|
|
91365
91368
|
let idleStallCount = 0;
|
|
91366
91369
|
let lastMsgCount = 0;
|
|
91367
91370
|
let stableIdlePolls = 0;
|
|
91368
|
-
log("[task] Starting poll loop", { sessionID: input.sessionID, agentToUse: input.agentToUse });
|
|
91371
|
+
log("[task] Starting poll loop", { sessionID: input.sessionID, agentToUse: input.agentToUse, maxPollTimeMs });
|
|
91369
91372
|
while (Date.now() - pollStart < maxPollTimeMs) {
|
|
91370
91373
|
if (ctx.abort?.aborted) {
|
|
91371
91374
|
log("[task] Aborted by user", { sessionID: input.sessionID });
|
|
@@ -91743,6 +91746,7 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
|
|
|
91743
91746
|
const toastManager = getTaskToastManager();
|
|
91744
91747
|
let taskId;
|
|
91745
91748
|
let syncSessionID;
|
|
91749
|
+
let pollTimedOut = false;
|
|
91746
91750
|
try {
|
|
91747
91751
|
const createSessionResult = await deps.createSyncSession(client2, {
|
|
91748
91752
|
parentSessionID: parentContext.sessionID,
|
|
@@ -91825,6 +91829,7 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
|
|
|
91825
91829
|
}
|
|
91826
91830
|
});
|
|
91827
91831
|
if (pollError) {
|
|
91832
|
+
pollTimedOut = pollError.startsWith("Poll timeout reached");
|
|
91828
91833
|
return pollError;
|
|
91829
91834
|
}
|
|
91830
91835
|
const result = await deps.fetchSyncResult(client2, sessionID, undefined, {
|
|
@@ -91861,10 +91866,12 @@ session_id: ${sessionID}
|
|
|
91861
91866
|
});
|
|
91862
91867
|
} finally {
|
|
91863
91868
|
if (syncSessionID) {
|
|
91864
|
-
|
|
91865
|
-
|
|
91866
|
-
|
|
91867
|
-
|
|
91869
|
+
unregisterSubagentSession(syncSessionID);
|
|
91870
|
+
if (!pollTimedOut) {
|
|
91871
|
+
client2.session.abort({ path: { id: syncSessionID } }).catch((err) => {
|
|
91872
|
+
log("[delegate-task] Sync session abort failed:", err);
|
|
91873
|
+
});
|
|
91874
|
+
}
|
|
91868
91875
|
}
|
|
91869
91876
|
}
|
|
91870
91877
|
}
|
|
@@ -92190,7 +92197,7 @@ Prompts MUST be in English.`;
|
|
|
92190
92197
|
let actualModel;
|
|
92191
92198
|
let isUnstableAgent = false;
|
|
92192
92199
|
if (args.category) {
|
|
92193
|
-
const resolution = await resolveCategoryExecution(args, options, inheritedModel, systemDefaultModel);
|
|
92200
|
+
const resolution = await resolveCategoryExecution(args, { ...options, sessionID: ctx.sessionID }, inheritedModel, systemDefaultModel);
|
|
92194
92201
|
if (resolution.error) {
|
|
92195
92202
|
return resolution.error;
|
|
92196
92203
|
}
|
|
@@ -92591,8 +92598,8 @@ Original error: ${createResult.error}`;
|
|
|
92591
92598
|
});
|
|
92592
92599
|
}
|
|
92593
92600
|
// src/tools/task/task-cleanup.ts
|
|
92594
|
-
import { existsSync as
|
|
92595
|
-
import { join as
|
|
92601
|
+
import { existsSync as existsSync71, readdirSync as readdirSync22, readFileSync as readFileSync42, unlinkSync as unlinkSync11 } from "fs";
|
|
92602
|
+
import { join as join72 } from "path";
|
|
92596
92603
|
init_logger();
|
|
92597
92604
|
function parseOlderThan(value) {
|
|
92598
92605
|
const match = value.trim().match(/^(\d+)(d|h|m)$/);
|
|
@@ -92651,17 +92658,17 @@ Returns counts: {deleted, remaining, deletedIds}`,
|
|
|
92651
92658
|
}
|
|
92652
92659
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
92653
92660
|
const taskDir = getTaskDir(config4, directory);
|
|
92654
|
-
if (!
|
|
92661
|
+
if (!existsSync71(taskDir)) {
|
|
92655
92662
|
return JSON.stringify({ deleted: 0, remaining: 0, deletedIds: [] });
|
|
92656
92663
|
}
|
|
92657
92664
|
const files = readdirSync22(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-"));
|
|
92658
92665
|
const total = files.length;
|
|
92659
92666
|
const deletedIds = [];
|
|
92660
92667
|
for (const file3 of files) {
|
|
92661
|
-
const filePath =
|
|
92668
|
+
const filePath = join72(taskDir, file3);
|
|
92662
92669
|
let raw;
|
|
92663
92670
|
try {
|
|
92664
|
-
const content =
|
|
92671
|
+
const content = readFileSync42(filePath, "utf-8");
|
|
92665
92672
|
raw = JSON.parse(content);
|
|
92666
92673
|
} catch {
|
|
92667
92674
|
continue;
|
|
@@ -92694,7 +92701,9 @@ Returns counts: {deleted, remaining, deletedIds}`,
|
|
|
92694
92701
|
});
|
|
92695
92702
|
}
|
|
92696
92703
|
// src/tools/task/task-create.ts
|
|
92697
|
-
import {
|
|
92704
|
+
import { readdirSync as readdirSync23, statSync as statSync17 } from "fs";
|
|
92705
|
+
import { join as join73 } from "path";
|
|
92706
|
+
init_logger();
|
|
92698
92707
|
function createTaskCreateTool(config4, ctx) {
|
|
92699
92708
|
return tool({
|
|
92700
92709
|
description: `Create a new task with auto-generated ID and threadID recording.
|
|
@@ -92736,6 +92745,13 @@ async function handleCreate2(args, config4, ctx, context) {
|
|
|
92736
92745
|
return JSON.stringify({ error: "task_lock_unavailable" });
|
|
92737
92746
|
}
|
|
92738
92747
|
try {
|
|
92748
|
+
const existingTask = findDuplicateTask(taskDir, validatedArgs.subject, directory);
|
|
92749
|
+
if (existingTask) {
|
|
92750
|
+
return JSON.stringify({
|
|
92751
|
+
task: { id: existingTask.id, subject: existingTask.subject },
|
|
92752
|
+
deduplicated: true
|
|
92753
|
+
});
|
|
92754
|
+
}
|
|
92739
92755
|
const taskId = generateTaskId();
|
|
92740
92756
|
const task = {
|
|
92741
92757
|
id: taskId,
|
|
@@ -92752,7 +92768,7 @@ async function handleCreate2(args, config4, ctx, context) {
|
|
|
92752
92768
|
projectRoot: directory
|
|
92753
92769
|
};
|
|
92754
92770
|
const validatedTask = TaskObjectSchema.parse(task);
|
|
92755
|
-
writeJsonAtomic(
|
|
92771
|
+
writeJsonAtomic(join73(taskDir, `${taskId}.json`), validatedTask);
|
|
92756
92772
|
return JSON.stringify({
|
|
92757
92773
|
task: {
|
|
92758
92774
|
id: validatedTask.id,
|
|
@@ -92772,8 +92788,47 @@ async function handleCreate2(args, config4, ctx, context) {
|
|
|
92772
92788
|
return JSON.stringify({ error: "internal_error" });
|
|
92773
92789
|
}
|
|
92774
92790
|
}
|
|
92791
|
+
function findDuplicateTask(taskDir, subject, directory) {
|
|
92792
|
+
let files;
|
|
92793
|
+
try {
|
|
92794
|
+
files = readdirSync23(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-"));
|
|
92795
|
+
} catch (error95) {
|
|
92796
|
+
log(`[task-create] Failed to list task dir ${taskDir} during dedup scan`, error95);
|
|
92797
|
+
return null;
|
|
92798
|
+
}
|
|
92799
|
+
for (const file3 of files) {
|
|
92800
|
+
const filePath = join73(taskDir, file3);
|
|
92801
|
+
let existing;
|
|
92802
|
+
try {
|
|
92803
|
+
existing = readJsonSafe(filePath, TaskObjectSchema);
|
|
92804
|
+
} catch (error95) {
|
|
92805
|
+
log(`[task-create] Failed to read task file ${filePath} during dedup scan`, error95);
|
|
92806
|
+
continue;
|
|
92807
|
+
}
|
|
92808
|
+
if (!existing)
|
|
92809
|
+
continue;
|
|
92810
|
+
const isActive = existing.status === "pending" || existing.status === "in_progress";
|
|
92811
|
+
if (!isActive)
|
|
92812
|
+
continue;
|
|
92813
|
+
if (existing.subject.trim() !== subject.trim())
|
|
92814
|
+
continue;
|
|
92815
|
+
if (existing.projectRoot !== directory)
|
|
92816
|
+
continue;
|
|
92817
|
+
let mtimeMs;
|
|
92818
|
+
try {
|
|
92819
|
+
mtimeMs = statSync17(filePath).mtimeMs;
|
|
92820
|
+
} catch (error95) {
|
|
92821
|
+
log(`[task-create] Failed to stat task file ${filePath} during dedup scan`, error95);
|
|
92822
|
+
continue;
|
|
92823
|
+
}
|
|
92824
|
+
if (Date.now() - mtimeMs > DEDUP_WINDOW_MS)
|
|
92825
|
+
continue;
|
|
92826
|
+
return existing;
|
|
92827
|
+
}
|
|
92828
|
+
return null;
|
|
92829
|
+
}
|
|
92775
92830
|
// src/tools/task/task-get.ts
|
|
92776
|
-
import { join as
|
|
92831
|
+
import { join as join74 } from "path";
|
|
92777
92832
|
function parseTaskId(id) {
|
|
92778
92833
|
if (!TASK_ID_PATTERN.test(id))
|
|
92779
92834
|
return null;
|
|
@@ -92798,7 +92853,7 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
92798
92853
|
}
|
|
92799
92854
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
92800
92855
|
const taskDir = getTaskDir(config4, directory);
|
|
92801
|
-
const taskPath =
|
|
92856
|
+
const taskPath = join74(taskDir, `${taskId}.json`);
|
|
92802
92857
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
92803
92858
|
return JSON.stringify({ task: task ?? null });
|
|
92804
92859
|
} catch (error95) {
|
|
@@ -92811,35 +92866,42 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
92811
92866
|
});
|
|
92812
92867
|
}
|
|
92813
92868
|
// src/tools/task/task-list.ts
|
|
92814
|
-
import { existsSync as
|
|
92815
|
-
import { join as
|
|
92869
|
+
import { existsSync as existsSync72, readdirSync as readdirSync24 } from "fs";
|
|
92870
|
+
import { join as join75 } from "path";
|
|
92816
92871
|
function createTaskList(config4, ctx) {
|
|
92817
92872
|
return tool({
|
|
92818
92873
|
description: `List all active tasks with summary information.
|
|
92819
92874
|
|
|
92820
92875
|
Returns tasks excluding completed and deleted statuses by default.
|
|
92821
92876
|
For each task's blockedBy field, filters to only include unresolved (non-completed) blockers.
|
|
92822
|
-
Returns summary format: id, subject, status, owner, blockedBy (not full description)
|
|
92823
|
-
|
|
92824
|
-
|
|
92877
|
+
Returns summary format: id, subject, status, owner, blockedBy (not full description).
|
|
92878
|
+
|
|
92879
|
+
Optional \`parentID\` filter: when provided, returns only tasks whose parentID matches
|
|
92880
|
+
(i.e. the subtasks of that parent task).`,
|
|
92881
|
+
args: {
|
|
92882
|
+
parentID: tool.schema.string().optional().describe("Filter to subtasks of this parent task ID")
|
|
92883
|
+
},
|
|
92884
|
+
execute: async (args, context) => {
|
|
92885
|
+
const parentID = args.parentID;
|
|
92825
92886
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
92826
92887
|
const taskDir = getTaskDir(config4, directory);
|
|
92827
|
-
if (!
|
|
92888
|
+
if (!existsSync72(taskDir)) {
|
|
92828
92889
|
return JSON.stringify({ tasks: [] });
|
|
92829
92890
|
}
|
|
92830
|
-
const files =
|
|
92891
|
+
const files = readdirSync24(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-")).map((f) => f.replace(".json", ""));
|
|
92831
92892
|
if (files.length === 0) {
|
|
92832
92893
|
return JSON.stringify({ tasks: [] });
|
|
92833
92894
|
}
|
|
92834
92895
|
const allTasks = [];
|
|
92835
92896
|
for (const fileId of files) {
|
|
92836
|
-
const task = readJsonSafe(
|
|
92897
|
+
const task = readJsonSafe(join75(taskDir, `${fileId}.json`), TaskObjectSchema);
|
|
92837
92898
|
if (task) {
|
|
92838
92899
|
allTasks.push(task);
|
|
92839
92900
|
}
|
|
92840
92901
|
}
|
|
92841
92902
|
const activeTasks = allTasks.filter((task) => task.status !== "completed" && task.status !== "deleted");
|
|
92842
|
-
const
|
|
92903
|
+
const filteredTasks = parentID ? activeTasks.filter((task) => task.parentID === parentID) : activeTasks;
|
|
92904
|
+
const summaries = filteredTasks.map((task) => {
|
|
92843
92905
|
const unresolvedBlockers = task.blockedBy.filter((blockerId) => {
|
|
92844
92906
|
const blockerTask = allTasks.find((t) => t.id === blockerId);
|
|
92845
92907
|
return blockerTask?.status !== "completed";
|
|
@@ -92849,7 +92911,8 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
|
|
|
92849
92911
|
subject: task.subject,
|
|
92850
92912
|
status: task.status,
|
|
92851
92913
|
owner: task.owner,
|
|
92852
|
-
blockedBy: unresolvedBlockers
|
|
92914
|
+
blockedBy: unresolvedBlockers,
|
|
92915
|
+
parentID: task.parentID
|
|
92853
92916
|
};
|
|
92854
92917
|
});
|
|
92855
92918
|
return JSON.stringify({
|
|
@@ -92860,7 +92923,7 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
|
|
|
92860
92923
|
});
|
|
92861
92924
|
}
|
|
92862
92925
|
// src/tools/task/task-update.ts
|
|
92863
|
-
import { join as
|
|
92926
|
+
import { join as join76 } from "path";
|
|
92864
92927
|
function parseTaskId2(id) {
|
|
92865
92928
|
if (!TASK_ID_PATTERN.test(id))
|
|
92866
92929
|
return null;
|
|
@@ -92906,7 +92969,7 @@ async function handleUpdate(args, config4, ctx, context) {
|
|
|
92906
92969
|
return JSON.stringify({ error: "task_lock_unavailable" });
|
|
92907
92970
|
}
|
|
92908
92971
|
try {
|
|
92909
|
-
const taskPath =
|
|
92972
|
+
const taskPath = join76(taskDir, `${taskId}.json`);
|
|
92910
92973
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
92911
92974
|
if (!task) {
|
|
92912
92975
|
return JSON.stringify({ error: "task_not_found" });
|
|
@@ -93051,6 +93114,7 @@ function createToolGuardHooks(args) {
|
|
|
93051
93114
|
const { ctx, pluginConfig, isHookEnabled, safeHookEnabled } = args;
|
|
93052
93115
|
const evolutionEnabled = pluginConfig.evolution?.enabled === true;
|
|
93053
93116
|
const safeHook = (hookName, factory) => safeCreateHook(hookName, factory, { enabled: safeHookEnabled });
|
|
93117
|
+
const backgroundTaskBlocker = isHookEnabled("background-task-blocker") ? safeHook("background-task-blocker", () => createBackgroundTaskBlockerHook()) : null;
|
|
93054
93118
|
const commentChecker = isHookEnabled("comment-checker") ? safeHook("comment-checker", () => createCommentCheckerHooks(pluginConfig.comment_checker)) : null;
|
|
93055
93119
|
const toolOutputTruncator = isHookEnabled("tool-output-truncator") ? safeHook("tool-output-truncator", () => createToolOutputTruncatorHook(ctx, { experimental: pluginConfig.experimental })) : null;
|
|
93056
93120
|
let directoryAgentsInjector = null;
|
|
@@ -93084,6 +93148,7 @@ function createToolGuardHooks(args) {
|
|
|
93084
93148
|
const taskEditGuard = isHookEnabled("task-edit-guard") ? safeHook("task-edit-guard", () => createTaskEditGuardHook(ctx)) : null;
|
|
93085
93149
|
const evolutionWatcher = evolutionEnabled && isHookEnabled("evolution-watcher") ? safeHook("evolution-watcher", () => createEvolutionWatcherHook(ctx, pluginConfig.evolution)) : null;
|
|
93086
93150
|
return {
|
|
93151
|
+
backgroundTaskBlocker,
|
|
93087
93152
|
commentChecker,
|
|
93088
93153
|
toolOutputTruncator,
|
|
93089
93154
|
directoryAgentsInjector,
|
|
@@ -93443,6 +93508,10 @@ class ConcurrencyManager {
|
|
|
93443
93508
|
return this.queues.get(model)?.length ?? 0;
|
|
93444
93509
|
}
|
|
93445
93510
|
}
|
|
93511
|
+
// src/features/background-agent/handle-index.ts
|
|
93512
|
+
import { existsSync as existsSync73, mkdirSync as mkdirSync19, readdirSync as readdirSync25, readFileSync as readFileSync43, renameSync as renameSync5, unlinkSync as unlinkSync12, writeFileSync as writeFileSync26 } from "fs";
|
|
93513
|
+
import { dirname as dirname19, join as join77 } from "path";
|
|
93514
|
+
|
|
93446
93515
|
// src/features/background-agent/constants.ts
|
|
93447
93516
|
var TASK_TTL_MS = 30 * 60 * 1000;
|
|
93448
93517
|
var MIN_STABILITY_TIME_MS2 = 10 * 1000;
|
|
@@ -93457,6 +93526,126 @@ var POLLING_INTERVAL_MS = 3000;
|
|
|
93457
93526
|
var PRUNE_THROTTLE_MS = 30000;
|
|
93458
93527
|
var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
|
|
93459
93528
|
|
|
93529
|
+
// src/features/background-agent/handle-index.ts
|
|
93530
|
+
var BG_HANDLE_DIR_NAME = "bg-handles";
|
|
93531
|
+
var BG_HANDLE_FILE_PREFIX = "bg_";
|
|
93532
|
+
var BgHandleSchema = exports_external.object({
|
|
93533
|
+
taskId: exports_external.string(),
|
|
93534
|
+
parentSessionID: exports_external.string(),
|
|
93535
|
+
parentMessageID: exports_external.string(),
|
|
93536
|
+
description: exports_external.string(),
|
|
93537
|
+
agent: exports_external.string(),
|
|
93538
|
+
status: exports_external.enum(["pending", "running", "completed", "error", "cancelled", "interrupt"]),
|
|
93539
|
+
model: exports_external.object({
|
|
93540
|
+
providerID: exports_external.string(),
|
|
93541
|
+
modelID: exports_external.string(),
|
|
93542
|
+
variant: exports_external.string().optional(),
|
|
93543
|
+
temperature: exports_external.number().optional()
|
|
93544
|
+
}).optional(),
|
|
93545
|
+
category: exports_external.string().optional(),
|
|
93546
|
+
concurrencyGroup: exports_external.string().optional(),
|
|
93547
|
+
queuedAt: exports_external.number().optional(),
|
|
93548
|
+
startedAt: exports_external.number().optional(),
|
|
93549
|
+
completedAt: exports_external.number().optional()
|
|
93550
|
+
}).strict();
|
|
93551
|
+
function getBgHandleDir(directory) {
|
|
93552
|
+
return join77(directory, ".matrixx", BG_HANDLE_DIR_NAME);
|
|
93553
|
+
}
|
|
93554
|
+
function getHandlePath(directory, taskId) {
|
|
93555
|
+
return join77(getBgHandleDir(directory), `${taskId}.json`);
|
|
93556
|
+
}
|
|
93557
|
+
function ensureDir3(dirPath) {
|
|
93558
|
+
if (!existsSync73(dirPath)) {
|
|
93559
|
+
mkdirSync19(dirPath, { recursive: true });
|
|
93560
|
+
}
|
|
93561
|
+
}
|
|
93562
|
+
function toHandle(task) {
|
|
93563
|
+
return {
|
|
93564
|
+
taskId: task.id,
|
|
93565
|
+
parentSessionID: task.parentSessionID,
|
|
93566
|
+
parentMessageID: task.parentMessageID,
|
|
93567
|
+
description: task.description,
|
|
93568
|
+
agent: task.agent,
|
|
93569
|
+
status: task.status,
|
|
93570
|
+
model: task.model,
|
|
93571
|
+
category: task.category,
|
|
93572
|
+
concurrencyGroup: task.concurrencyGroup,
|
|
93573
|
+
queuedAt: task.queuedAt?.getTime(),
|
|
93574
|
+
startedAt: task.startedAt?.getTime(),
|
|
93575
|
+
completedAt: task.completedAt?.getTime()
|
|
93576
|
+
};
|
|
93577
|
+
}
|
|
93578
|
+
function writeHandle(directory, task) {
|
|
93579
|
+
const filePath = getHandlePath(directory, task.id);
|
|
93580
|
+
ensureDir3(dirname19(filePath));
|
|
93581
|
+
const tempPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
93582
|
+
try {
|
|
93583
|
+
writeFileSync26(tempPath, JSON.stringify(toHandle(task), null, 2), "utf-8");
|
|
93584
|
+
renameSync5(tempPath, filePath);
|
|
93585
|
+
} catch (error95) {
|
|
93586
|
+
try {
|
|
93587
|
+
if (existsSync73(tempPath)) {
|
|
93588
|
+
unlinkSync12(tempPath);
|
|
93589
|
+
}
|
|
93590
|
+
} catch {}
|
|
93591
|
+
throw error95;
|
|
93592
|
+
}
|
|
93593
|
+
}
|
|
93594
|
+
function readHandleFile(filePath) {
|
|
93595
|
+
try {
|
|
93596
|
+
if (!existsSync73(filePath)) {
|
|
93597
|
+
return null;
|
|
93598
|
+
}
|
|
93599
|
+
const parsed = JSON.parse(readFileSync43(filePath, "utf-8"));
|
|
93600
|
+
const result = BgHandleSchema.safeParse(parsed);
|
|
93601
|
+
return result.success ? result.data : null;
|
|
93602
|
+
} catch {
|
|
93603
|
+
return null;
|
|
93604
|
+
}
|
|
93605
|
+
}
|
|
93606
|
+
function readHandles(directory) {
|
|
93607
|
+
const dir = getBgHandleDir(directory);
|
|
93608
|
+
if (!existsSync73(dir)) {
|
|
93609
|
+
return [];
|
|
93610
|
+
}
|
|
93611
|
+
const handles = [];
|
|
93612
|
+
for (const name of readdirSync25(dir)) {
|
|
93613
|
+
if (!name.startsWith(BG_HANDLE_FILE_PREFIX) || !name.endsWith(".json")) {
|
|
93614
|
+
continue;
|
|
93615
|
+
}
|
|
93616
|
+
const handle = readHandleFile(join77(dir, name));
|
|
93617
|
+
if (handle) {
|
|
93618
|
+
handles.push(handle);
|
|
93619
|
+
}
|
|
93620
|
+
}
|
|
93621
|
+
return handles;
|
|
93622
|
+
}
|
|
93623
|
+
function sweepStaleHandles(directory, ttlMs = TASK_TTL_MS, now = Date.now()) {
|
|
93624
|
+
const dir = getBgHandleDir(directory);
|
|
93625
|
+
if (!existsSync73(dir)) {
|
|
93626
|
+
return 0;
|
|
93627
|
+
}
|
|
93628
|
+
let removed = 0;
|
|
93629
|
+
for (const name of readdirSync25(dir)) {
|
|
93630
|
+
if (!name.startsWith(BG_HANDLE_FILE_PREFIX) || !name.endsWith(".json")) {
|
|
93631
|
+
continue;
|
|
93632
|
+
}
|
|
93633
|
+
const filePath = join77(dir, name);
|
|
93634
|
+
const handle = readHandleFile(filePath);
|
|
93635
|
+
const timestamp = handle?.completedAt ?? handle?.startedAt ?? handle?.queuedAt;
|
|
93636
|
+
if (handle && timestamp !== undefined && now - timestamp <= ttlMs) {
|
|
93637
|
+
continue;
|
|
93638
|
+
}
|
|
93639
|
+
try {
|
|
93640
|
+
unlinkSync12(filePath);
|
|
93641
|
+
removed++;
|
|
93642
|
+
} catch {}
|
|
93643
|
+
}
|
|
93644
|
+
if (removed > 0) {
|
|
93645
|
+
log(`[bg-handles] Swept ${removed} stale handle file(s) from ${dir}`);
|
|
93646
|
+
}
|
|
93647
|
+
return removed;
|
|
93648
|
+
}
|
|
93460
93649
|
// src/features/background-agent/error-helpers.ts
|
|
93461
93650
|
function getErrorText(error95) {
|
|
93462
93651
|
if (!error95)
|
|
@@ -93559,19 +93748,19 @@ function detectRepetitiveToolUse(window2) {
|
|
|
93559
93748
|
}
|
|
93560
93749
|
|
|
93561
93750
|
// src/features/background-agent/message-dir.ts
|
|
93562
|
-
import { existsSync as
|
|
93563
|
-
import { join as
|
|
93751
|
+
import { existsSync as existsSync74, readdirSync as readdirSync26, readFileSync as readFileSync44 } from "fs";
|
|
93752
|
+
import { join as join78 } from "path";
|
|
93564
93753
|
var MESSAGE_DIR_CACHE_CAP = 1000;
|
|
93565
93754
|
var messageDirCache = new Map;
|
|
93566
93755
|
function computeMessageDir(sessionID) {
|
|
93567
|
-
if (!
|
|
93756
|
+
if (!existsSync74(MESSAGE_STORAGE))
|
|
93568
93757
|
return null;
|
|
93569
|
-
const directPath =
|
|
93570
|
-
if (
|
|
93758
|
+
const directPath = join78(MESSAGE_STORAGE, sessionID);
|
|
93759
|
+
if (existsSync74(directPath))
|
|
93571
93760
|
return directPath;
|
|
93572
|
-
for (const dir of
|
|
93573
|
-
const sessionPath =
|
|
93574
|
-
if (
|
|
93761
|
+
for (const dir of readdirSync26(MESSAGE_STORAGE)) {
|
|
93762
|
+
const sessionPath = join78(MESSAGE_STORAGE, dir, sessionID);
|
|
93763
|
+
if (existsSync74(sessionPath))
|
|
93575
93764
|
return sessionPath;
|
|
93576
93765
|
}
|
|
93577
93766
|
return null;
|
|
@@ -93605,11 +93794,11 @@ function hasPartialAgentOrModel(message) {
|
|
|
93605
93794
|
}
|
|
93606
93795
|
function findNearestMessageExcludingCompaction(messageDir) {
|
|
93607
93796
|
try {
|
|
93608
|
-
const files =
|
|
93797
|
+
const files = readdirSync26(messageDir).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
93609
93798
|
const parsedByFile = new Map;
|
|
93610
93799
|
for (const file3 of files) {
|
|
93611
93800
|
try {
|
|
93612
|
-
const content =
|
|
93801
|
+
const content = readFileSync44(join78(messageDir, file3), "utf-8");
|
|
93613
93802
|
const parsed = JSON.parse(content);
|
|
93614
93803
|
parsedByFile.set(file3, parsed);
|
|
93615
93804
|
if (hasFullAgentAndModel(parsed)) {
|
|
@@ -93757,6 +93946,25 @@ class TaskHistory {
|
|
|
93757
93946
|
}
|
|
93758
93947
|
|
|
93759
93948
|
// src/features/background-agent/manager.ts
|
|
93949
|
+
function restoreTaskFromHandle(handle) {
|
|
93950
|
+
const interrupted = handle.status === "running" || handle.status === "pending";
|
|
93951
|
+
return {
|
|
93952
|
+
id: handle.taskId,
|
|
93953
|
+
parentSessionID: handle.parentSessionID,
|
|
93954
|
+
parentMessageID: handle.parentMessageID,
|
|
93955
|
+
description: handle.description,
|
|
93956
|
+
prompt: "",
|
|
93957
|
+
agent: handle.agent,
|
|
93958
|
+
status: interrupted ? "interrupt" : handle.status,
|
|
93959
|
+
queuedAt: handle.queuedAt !== undefined ? new Date(handle.queuedAt) : undefined,
|
|
93960
|
+
startedAt: handle.startedAt !== undefined ? new Date(handle.startedAt) : undefined,
|
|
93961
|
+
completedAt: handle.completedAt !== undefined ? new Date(handle.completedAt) : undefined,
|
|
93962
|
+
model: handle.model,
|
|
93963
|
+
category: handle.category,
|
|
93964
|
+
concurrencyGroup: handle.concurrencyGroup
|
|
93965
|
+
};
|
|
93966
|
+
}
|
|
93967
|
+
|
|
93760
93968
|
class BackgroundManager {
|
|
93761
93969
|
static cleanupManagers = new Set;
|
|
93762
93970
|
static cleanupRegistered = false;
|
|
@@ -93797,6 +94005,28 @@ class BackgroundManager {
|
|
|
93797
94005
|
this.enableParentSessionNotifications = options?.enableParentSessionNotifications ?? true;
|
|
93798
94006
|
this.registerProcessCleanup();
|
|
93799
94007
|
}
|
|
94008
|
+
persistHandle(task) {
|
|
94009
|
+
try {
|
|
94010
|
+
writeHandle(this.directory, task);
|
|
94011
|
+
} catch (error95) {
|
|
94012
|
+
log("[background-agent] Failed to persist background handle:", { taskId: task.id, error: error95 });
|
|
94013
|
+
}
|
|
94014
|
+
}
|
|
94015
|
+
async restoreHandles() {
|
|
94016
|
+
const swept = sweepStaleHandles(this.directory);
|
|
94017
|
+
const handles = readHandles(this.directory);
|
|
94018
|
+
let restored = 0;
|
|
94019
|
+
for (const handle of handles) {
|
|
94020
|
+
if (this.tasks.has(handle.taskId)) {
|
|
94021
|
+
continue;
|
|
94022
|
+
}
|
|
94023
|
+
this.tasks.set(handle.taskId, restoreTaskFromHandle(handle));
|
|
94024
|
+
restored++;
|
|
94025
|
+
}
|
|
94026
|
+
if (restored > 0 || swept > 0) {
|
|
94027
|
+
log("[background-agent] Restored background handles:", { restored, swept });
|
|
94028
|
+
}
|
|
94029
|
+
}
|
|
93800
94030
|
async launch(input) {
|
|
93801
94031
|
log("[background-agent] launch() called with:", {
|
|
93802
94032
|
agent: input.agent,
|
|
@@ -93948,6 +94178,7 @@ class BackgroundManager {
|
|
|
93948
94178
|
};
|
|
93949
94179
|
task.concurrencyKey = concurrencyKey;
|
|
93950
94180
|
task.concurrencyGroup = concurrencyKey;
|
|
94181
|
+
this.persistHandle(task);
|
|
93951
94182
|
this.taskHistory.record(input.parentSessionID, { id: task.id, sessionID, agent: input.agent, description: input.description, status: "running", category: input.category, startedAt: task.startedAt });
|
|
93952
94183
|
this.startPolling();
|
|
93953
94184
|
log("[background-agent] Launching task:", { taskId: task.id, sessionID, agent: input.agent });
|
|
@@ -93999,6 +94230,7 @@ class BackgroundManager {
|
|
|
93999
94230
|
existingTask.error = errorMessage;
|
|
94000
94231
|
}
|
|
94001
94232
|
existingTask.completedAt = new Date;
|
|
94233
|
+
this.persistHandle(existingTask);
|
|
94002
94234
|
if (existingTask.concurrencyKey) {
|
|
94003
94235
|
this.concurrencyManager.release(existingTask.concurrencyKey);
|
|
94004
94236
|
existingTask.concurrencyKey = undefined;
|
|
@@ -94101,6 +94333,7 @@ class BackgroundManager {
|
|
|
94101
94333
|
concurrencyGroup
|
|
94102
94334
|
};
|
|
94103
94335
|
this.tasks.set(task.id, task);
|
|
94336
|
+
this.persistHandle(task);
|
|
94104
94337
|
registerSubagentSession(input.sessionID, input.parentSessionID);
|
|
94105
94338
|
this.startPolling();
|
|
94106
94339
|
this.taskHistory.record(input.parentSessionID, { id: task.id, sessionID: input.sessionID, agent: input.agent || "task", description: input.description, status: "running", startedAt: task.startedAt });
|
|
@@ -94147,6 +94380,7 @@ class BackgroundManager {
|
|
|
94147
94380
|
existingTask.parentTools = input.parentTools;
|
|
94148
94381
|
}
|
|
94149
94382
|
existingTask.startedAt = new Date;
|
|
94383
|
+
this.persistHandle(existingTask);
|
|
94150
94384
|
existingTask.progress = {
|
|
94151
94385
|
toolCalls: existingTask.progress?.toolCalls ?? 0,
|
|
94152
94386
|
lastUpdate: new Date
|
|
@@ -94202,6 +94436,7 @@ class BackgroundManager {
|
|
|
94202
94436
|
const errorMessage = error95 instanceof Error ? error95.message : String(error95);
|
|
94203
94437
|
existingTask.error = errorMessage;
|
|
94204
94438
|
existingTask.completedAt = new Date;
|
|
94439
|
+
this.persistHandle(existingTask);
|
|
94205
94440
|
if (existingTask.concurrencyKey) {
|
|
94206
94441
|
this.concurrencyManager.release(existingTask.concurrencyKey);
|
|
94207
94442
|
existingTask.concurrencyKey = undefined;
|
|
@@ -94366,6 +94601,7 @@ class BackgroundManager {
|
|
|
94366
94601
|
task.status = "error";
|
|
94367
94602
|
task.error = errorMessage ?? "Session error";
|
|
94368
94603
|
task.completedAt = new Date;
|
|
94604
|
+
this.persistHandle(task);
|
|
94369
94605
|
this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "error", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt });
|
|
94370
94606
|
if (task.concurrencyKey) {
|
|
94371
94607
|
this.concurrencyManager.release(task.concurrencyKey);
|
|
@@ -94534,6 +94770,7 @@ class BackgroundManager {
|
|
|
94534
94770
|
}
|
|
94535
94771
|
task.status = "cancelled";
|
|
94536
94772
|
task.completedAt = new Date;
|
|
94773
|
+
this.persistHandle(task);
|
|
94537
94774
|
if (reason) {
|
|
94538
94775
|
task.error = reason;
|
|
94539
94776
|
}
|
|
@@ -94656,6 +94893,7 @@ class BackgroundManager {
|
|
|
94656
94893
|
}
|
|
94657
94894
|
task.status = "completed";
|
|
94658
94895
|
task.completedAt = new Date;
|
|
94896
|
+
this.persistHandle(task);
|
|
94659
94897
|
this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "completed", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt });
|
|
94660
94898
|
if (task.concurrencyKey) {
|
|
94661
94899
|
this.concurrencyManager.release(task.concurrencyKey);
|
|
@@ -94788,6 +95026,7 @@ class BackgroundManager {
|
|
|
94788
95026
|
task.status = "error";
|
|
94789
95027
|
task.error = errorMessage;
|
|
94790
95028
|
task.completedAt = new Date;
|
|
95029
|
+
this.persistHandle(task);
|
|
94791
95030
|
if (task.concurrencyKey) {
|
|
94792
95031
|
this.concurrencyManager.release(task.concurrencyKey);
|
|
94793
95032
|
task.concurrencyKey = undefined;
|
|
@@ -94813,7 +95052,7 @@ class BackgroundManager {
|
|
|
94813
95052
|
}
|
|
94814
95053
|
this.tasks.delete(taskId);
|
|
94815
95054
|
if (task.sessionID) {
|
|
94816
|
-
|
|
95055
|
+
unregisterSubagentSession(task.sessionID);
|
|
94817
95056
|
}
|
|
94818
95057
|
}
|
|
94819
95058
|
}
|
|
@@ -94872,6 +95111,7 @@ class BackgroundManager {
|
|
|
94872
95111
|
task.status = "cancelled";
|
|
94873
95112
|
task.error = `Stale timeout (no activity for ${staleMinutes2}min since start)`;
|
|
94874
95113
|
task.completedAt = new Date;
|
|
95114
|
+
this.persistHandle(task);
|
|
94875
95115
|
if (task.concurrencyKey) {
|
|
94876
95116
|
this.concurrencyManager.release(task.concurrencyKey);
|
|
94877
95117
|
task.concurrencyKey = undefined;
|
|
@@ -94900,6 +95140,7 @@ class BackgroundManager {
|
|
|
94900
95140
|
task.status = "cancelled";
|
|
94901
95141
|
task.error = `Stale timeout (no activity for ${staleMinutes}min)`;
|
|
94902
95142
|
task.completedAt = new Date;
|
|
95143
|
+
this.persistHandle(task);
|
|
94903
95144
|
if (task.concurrencyKey) {
|
|
94904
95145
|
this.concurrencyManager.release(task.concurrencyKey);
|
|
94905
95146
|
task.concurrencyKey = undefined;
|
|
@@ -95791,9 +96032,9 @@ class TmuxSessionManager {
|
|
|
95791
96032
|
var SESSION_TIMEOUT_MS3 = 10 * 60 * 1000;
|
|
95792
96033
|
var MIN_STABILITY_TIME_MS4 = 10 * 1000;
|
|
95793
96034
|
// src/agents/dynamic-agent-prompt-builder.ts
|
|
95794
|
-
import { existsSync as
|
|
95795
|
-
import { homedir as
|
|
95796
|
-
import { join as
|
|
96035
|
+
import { existsSync as existsSync75, readFileSync as readFileSync45 } from "fs";
|
|
96036
|
+
import { homedir as homedir11 } from "os";
|
|
96037
|
+
import { join as join79 } from "path";
|
|
95797
96038
|
init_logger();
|
|
95798
96039
|
function categorizeTools(toolNames) {
|
|
95799
96040
|
return toolNames.map((name) => {
|
|
@@ -96155,24 +96396,24 @@ function exploreCtxPart(hasGrepGlob) {
|
|
|
96155
96396
|
function resolveContextModeDisciplinePath() {
|
|
96156
96397
|
try {
|
|
96157
96398
|
const resolved = __require.resolve("context-mode/configs/opencode/AGENTS.md");
|
|
96158
|
-
if (
|
|
96399
|
+
if (existsSync75(resolved))
|
|
96159
96400
|
return resolved;
|
|
96160
96401
|
} catch {}
|
|
96161
96402
|
try {
|
|
96162
96403
|
const cacheDir = getOpenCodeCacheDir();
|
|
96163
|
-
const p =
|
|
96164
|
-
if (
|
|
96404
|
+
const p = join79(cacheDir, "packages/context-mode@latest/node_modules/context-mode/configs/opencode/AGENTS.md");
|
|
96405
|
+
if (existsSync75(p))
|
|
96165
96406
|
return p;
|
|
96166
96407
|
} catch {}
|
|
96167
96408
|
try {
|
|
96168
|
-
const base = process.env.XDG_CACHE_HOME ??
|
|
96169
|
-
const p =
|
|
96170
|
-
if (
|
|
96409
|
+
const base = process.env.XDG_CACHE_HOME ?? join79(homedir11(), ".cache");
|
|
96410
|
+
const p = join79(base, "opencode/packages/context-mode@latest/node_modules/context-mode/configs/opencode/AGENTS.md");
|
|
96411
|
+
if (existsSync75(p))
|
|
96171
96412
|
return p;
|
|
96172
96413
|
} catch {}
|
|
96173
96414
|
try {
|
|
96174
|
-
const p2 =
|
|
96175
|
-
if (
|
|
96415
|
+
const p2 = join79(homedir11(), ".cache/opencode/packages/context-mode@latest/node_modules/context-mode/configs/opencode/AGENTS.md");
|
|
96416
|
+
if (existsSync75(p2))
|
|
96176
96417
|
return p2;
|
|
96177
96418
|
} catch {}
|
|
96178
96419
|
return null;
|
|
@@ -96190,11 +96431,11 @@ function loadContextModeDiscipline(hasGrepGlob = true) {
|
|
|
96190
96431
|
const p = resolveContextModeDisciplinePath();
|
|
96191
96432
|
if (!p)
|
|
96192
96433
|
throw new Error("discipline file not found");
|
|
96193
|
-
const raw =
|
|
96434
|
+
const raw = readFileSync45(p, "utf8");
|
|
96194
96435
|
let version4 = "1.0.169";
|
|
96195
96436
|
try {
|
|
96196
96437
|
const pkgResolved = __require.resolve("context-mode/package.json");
|
|
96197
|
-
const pkgRaw =
|
|
96438
|
+
const pkgRaw = readFileSync45(pkgResolved, "utf8");
|
|
96198
96439
|
const pkg = JSON.parse(pkgRaw);
|
|
96199
96440
|
if (pkg.version)
|
|
96200
96441
|
version4 = pkg.version;
|
|
@@ -96241,11 +96482,11 @@ function loadCompactContextDiscipline(hasGrepGlob = true) {
|
|
|
96241
96482
|
const p = resolveContextModeDisciplinePath();
|
|
96242
96483
|
if (!p)
|
|
96243
96484
|
throw new Error("discipline file not found");
|
|
96244
|
-
const raw =
|
|
96485
|
+
const raw = readFileSync45(p, "utf8");
|
|
96245
96486
|
let version4 = "1.0.169";
|
|
96246
96487
|
try {
|
|
96247
96488
|
const pkgResolved = __require.resolve("context-mode/package.json");
|
|
96248
|
-
const pkgRaw =
|
|
96489
|
+
const pkgRaw = readFileSync45(pkgResolved, "utf8");
|
|
96249
96490
|
const pkg = JSON.parse(pkgRaw);
|
|
96250
96491
|
if (pkg.version)
|
|
96251
96492
|
version4 = pkg.version;
|
|
@@ -97314,8 +97555,8 @@ function createBddContractAgent(model) {
|
|
|
97314
97555
|
createBddContractAgent.mode = MODE2;
|
|
97315
97556
|
|
|
97316
97557
|
// src/agents/builtin-agents/resolve-file-uri.ts
|
|
97317
|
-
import { existsSync as
|
|
97318
|
-
import { homedir as
|
|
97558
|
+
import { existsSync as existsSync76, readFileSync as readFileSync46 } from "fs";
|
|
97559
|
+
import { homedir as homedir12 } from "os";
|
|
97319
97560
|
import { isAbsolute as isAbsolute6, resolve as resolve10 } from "path";
|
|
97320
97561
|
function resolvePromptAppend(promptAppend, configDir) {
|
|
97321
97562
|
if (!promptAppend.startsWith("file://"))
|
|
@@ -97324,16 +97565,16 @@ function resolvePromptAppend(promptAppend, configDir) {
|
|
|
97324
97565
|
let filePath;
|
|
97325
97566
|
try {
|
|
97326
97567
|
const decoded = decodeURIComponent(encoded);
|
|
97327
|
-
const expanded = decoded.startsWith("~/") ? decoded.replace(/^~\//, `${
|
|
97568
|
+
const expanded = decoded.startsWith("~/") ? decoded.replace(/^~\//, `${homedir12()}/`) : decoded;
|
|
97328
97569
|
filePath = isAbsolute6(expanded) ? expanded : resolve10(configDir ?? process.cwd(), expanded);
|
|
97329
97570
|
} catch {
|
|
97330
97571
|
return `[WARNING: Malformed file URI (invalid percent-encoding): ${promptAppend}]`;
|
|
97331
97572
|
}
|
|
97332
|
-
if (!
|
|
97573
|
+
if (!existsSync76(filePath)) {
|
|
97333
97574
|
return `[WARNING: Could not resolve file URI: ${promptAppend}]`;
|
|
97334
97575
|
}
|
|
97335
97576
|
try {
|
|
97336
|
-
return
|
|
97577
|
+
return readFileSync46(filePath, "utf8");
|
|
97337
97578
|
} catch {
|
|
97338
97579
|
return `[WARNING: Could not read file: ${promptAppend}]`;
|
|
97339
97580
|
}
|
|
@@ -99217,16 +99458,19 @@ var OPERATOR_PROMPT_METADATA = {
|
|
|
99217
99458
|
category: "exploration",
|
|
99218
99459
|
cost: "CHEAP",
|
|
99219
99460
|
promptAlias: "Librarian",
|
|
99220
|
-
keyTrigger: "External library/source mentioned \u2192 fire `librarian` background",
|
|
99461
|
+
keyTrigger: "External library/source mentioned or code search needed \u2192 fire `librarian` background",
|
|
99221
99462
|
triggers: [
|
|
99222
|
-
{ domain: "Librarian", trigger: "Unfamiliar packages / libraries, struggles at weird behaviour (to find existing implementation of opensource)" }
|
|
99463
|
+
{ domain: "Librarian", trigger: "Unfamiliar packages / libraries, struggles at weird behaviour (to find existing implementation of opensource)" },
|
|
99464
|
+
{ domain: "Code Search", trigger: "Find real-world code examples, search GitHub repos for patterns, or need OSS implementation references" }
|
|
99223
99465
|
],
|
|
99224
99466
|
useWhen: [
|
|
99225
99467
|
"How do I use [library]?",
|
|
99226
99468
|
"What's the best practice for [framework feature]?",
|
|
99227
99469
|
"Why does [external dependency] behave this way?",
|
|
99228
99470
|
"Find examples of [library] usage",
|
|
99229
|
-
"Working with unfamiliar npm/pip/cargo packages"
|
|
99471
|
+
"Working with unfamiliar npm/pip/cargo packages",
|
|
99472
|
+
"Show me real-world code examples of [pattern]",
|
|
99473
|
+
"Search GitHub for implementations of [feature]"
|
|
99230
99474
|
]
|
|
99231
99475
|
};
|
|
99232
99476
|
function createOperatorAgent(model) {
|
|
@@ -99327,7 +99571,7 @@ context7_query-docs(libraryId: id, query: "specific topic")
|
|
|
99327
99571
|
Tool 1: context7_resolve-library-id("library-name")
|
|
99328
99572
|
\u2192 then context7_query-docs(libraryId: id, query: "specific-topic")
|
|
99329
99573
|
Tool 2: webfetch(relevant_pages_from_sitemap) // Targeted, not random
|
|
99330
|
-
Tool 3:
|
|
99574
|
+
Tool 3: white_rabbit_searchGitHub(query: "usage pattern", language: ["TypeScript"])
|
|
99331
99575
|
\`\`\`
|
|
99332
99576
|
|
|
99333
99577
|
**Output**: Summarize findings with links to official docs (versioned if applicable) and real-world examples.
|
|
@@ -99346,8 +99590,8 @@ Step 2: Get commit SHA for permalinks
|
|
|
99346
99590
|
cd \${TMPDIR:-/tmp}/repo-name && git rev-parse HEAD
|
|
99347
99591
|
|
|
99348
99592
|
Step 3: Find the implementation
|
|
99349
|
-
-
|
|
99350
|
-
- read
|
|
99593
|
+
- ctx_search / ast_grep_search for function/class \u2014 NOT grep (blocked under enforce:true)
|
|
99594
|
+
- ctx_execute_file for analysis (or read for targeted sections)
|
|
99351
99595
|
- git blame for context if needed
|
|
99352
99596
|
|
|
99353
99597
|
Step 4: Construct permalink
|
|
@@ -99357,7 +99601,7 @@ Step 4: Construct permalink
|
|
|
99357
99601
|
**Parallel acceleration (4+ calls)**:
|
|
99358
99602
|
\`\`\`
|
|
99359
99603
|
Tool 1: gh repo clone owner/repo \${TMPDIR:-/tmp}/repo -- --depth 1
|
|
99360
|
-
Tool 2:
|
|
99604
|
+
Tool 2: white_rabbit_searchGitHub(query: "function_name", repo: "owner/repo")
|
|
99361
99605
|
Tool 3: gh api repos/owner/repo/commits/HEAD --jq '.sha'
|
|
99362
99606
|
Tool 4: context7_get-library-docs(id, topic: "relevant-api")
|
|
99363
99607
|
\`\`\`
|
|
@@ -99396,8 +99640,8 @@ Tool 1: context7_resolve-library-id \u2192 context7_query-docs
|
|
|
99396
99640
|
Tool 2: webfetch(targeted_doc_pages_from_sitemap)
|
|
99397
99641
|
|
|
99398
99642
|
// Code Search
|
|
99399
|
-
Tool 3:
|
|
99400
|
-
Tool 4:
|
|
99643
|
+
Tool 3: white_rabbit_searchGitHub(query: "pattern1", language: [...])
|
|
99644
|
+
Tool 4: white_rabbit_searchGitHub(query: "pattern2", useRegexp: true)
|
|
99401
99645
|
|
|
99402
99646
|
// Source Analysis
|
|
99403
99647
|
Tool 5: gh repo clone owner/repo \${TMPDIR:-/tmp}/repo -- --depth 1
|
|
@@ -99453,7 +99697,7 @@ https://github.com/tanstack/query/blob/abc123def/packages/react-query/src/useQue
|
|
|
99453
99697
|
| **Sitemap Discovery** | webfetch | \`webfetch(docs_url + "/sitemap.xml")\` to understand doc structure |
|
|
99454
99698
|
| **Read Doc Page** | webfetch | \`webfetch(specific_doc_page)\` for targeted documentation |
|
|
99455
99699
|
| **Latest Info** | websearch_exa | \`websearch_exa_web_search_exa("query ${new Date().getFullYear()}")\` |
|
|
99456
|
-
| **Fast Code Search** |
|
|
99700
|
+
| **Fast Code Search** | white_rabbit | \`white_rabbit_searchGitHub(query, language, useRegexp)\` |
|
|
99457
99701
|
| **Deep Code Search** | gh CLI | \`gh search code "query" --repo owner/repo\` |
|
|
99458
99702
|
| **Clone Repo** | gh CLI | \`gh repo clone owner/repo \${TMPDIR:-/tmp}/name -- --depth 1\` |
|
|
99459
99703
|
| **Issues/PRs** | gh CLI | \`gh search issues/prs "query" --repo owner/repo\` |
|
|
@@ -99489,16 +99733,16 @@ Use OS-appropriate temp directory:
|
|
|
99489
99733
|
**Doc Discovery is SEQUENTIAL** (websearch \u2192 version check \u2192 sitemap \u2192 investigate).
|
|
99490
99734
|
**Main phase is PARALLEL** once you know where to look.
|
|
99491
99735
|
|
|
99492
|
-
**Always vary queries** when using
|
|
99736
|
+
**Always vary queries** when using white_rabbit:
|
|
99493
99737
|
\`\`\`
|
|
99494
99738
|
// GOOD: Different angles
|
|
99495
|
-
|
|
99496
|
-
|
|
99497
|
-
|
|
99739
|
+
white_rabbit_searchGitHub(query: "useQuery(", language: ["TypeScript"])
|
|
99740
|
+
white_rabbit_searchGitHub(query: "queryOptions", language: ["TypeScript"])
|
|
99741
|
+
white_rabbit_searchGitHub(query: "staleTime:", language: ["TypeScript"])
|
|
99498
99742
|
|
|
99499
99743
|
// BAD: Same pattern
|
|
99500
|
-
|
|
99501
|
-
|
|
99744
|
+
white_rabbit_searchGitHub(query: "useQuery")
|
|
99745
|
+
white_rabbit_searchGitHub(query: "useQuery")
|
|
99502
99746
|
\`\`\`
|
|
99503
99747
|
|
|
99504
99748
|
---
|
|
@@ -99508,7 +99752,7 @@ grep_app_searchGitHub(query: "useQuery")
|
|
|
99508
99752
|
| Failure | Recovery Action |
|
|
99509
99753
|
|---------|-----------------|
|
|
99510
99754
|
| context7 not found | Clone repo, read source + README directly |
|
|
99511
|
-
|
|
|
99755
|
+
| white_rabbit no results | Broaden query, try concept instead of exact name |
|
|
99512
99756
|
| gh API rate limit | Use cloned repo in temp directory |
|
|
99513
99757
|
| Repo not found | Search for forks or mirrors |
|
|
99514
99758
|
| Sitemap not found | Try \`/sitemap-0.xml\`, \`/sitemap_index.xml\`, or fetch docs index page and parse navigation |
|
|
@@ -99519,7 +99763,7 @@ grep_app_searchGitHub(query: "useQuery")
|
|
|
99519
99763
|
|
|
99520
99764
|
## COMMUNICATION RULES
|
|
99521
99765
|
|
|
99522
|
-
1. **NO TOOL NAMES**: Say "I'll search the codebase" not "I'll use
|
|
99766
|
+
1. **NO TOOL NAMES**: Say "I'll search the codebase" not "I'll use white_rabbit"
|
|
99523
99767
|
2. **NO PREAMBLE**: Answer directly, skip "I'll help you with..."
|
|
99524
99768
|
3. **ALWAYS CITE**: Every code claim needs a permalink
|
|
99525
99769
|
4. **USE MARKDOWN**: Code blocks with language identifiers
|
|
@@ -100496,8 +100740,8 @@ Your response has **FAILED** if:
|
|
|
100496
100740
|
Use the right tool for the job:
|
|
100497
100741
|
- **Semantic search** (definitions, references): LSP tools
|
|
100498
100742
|
- **Structural patterns** (function shapes, class structures): ast_grep_search
|
|
100499
|
-
- **Text patterns** (strings, comments, logs):
|
|
100500
|
-
- **File patterns** (find by name/extension):
|
|
100743
|
+
- **Text patterns** (strings, comments, logs): ctx_search (indexed KB) -> ctx_batch_execute / ctx_execute for multi-file analysis. ast_grep_search for structural patterns. grep/glob only as fallback when ctx_* unavailable.
|
|
100744
|
+
- **File patterns** (find by name/extension): ctx_search (indexed KB) -> LSP / ast_grep_search. glob only as fallback when ctx_* unavailable.
|
|
100501
100745
|
- **History/evolution** (when added, who changed): git commands
|
|
100502
100746
|
|
|
100503
100747
|
Flood with parallel calls. Cross-validate findings across multiple tools.`
|
|
@@ -100695,6 +100939,13 @@ This will:
|
|
|
100695
100939
|
5. **Seraph Before Plan** - Always catch gaps before committing to plan
|
|
100696
100940
|
6. **Choice-Based Handoff** - Present "Start Work" vs "High Accuracy Review" choice after plan
|
|
100697
100941
|
7. **Draft as External Memory** - Continuously record to draft; delete after plan complete
|
|
100942
|
+
8. **Background-First Delegation** - Default to \`run_in_background=true\` for every delegation; never nest a blocking subagent call inside an already-blocking session
|
|
100943
|
+
|
|
100944
|
+
### Delegation Policy (MANDATORY)
|
|
100945
|
+
|
|
100946
|
+
- **Default to background mode**: every Oracle delegation passes \`run_in_background=true\`.
|
|
100947
|
+
- **Never nest a blocking subagent call** (\`run_in_background=false\`) inside an already-blocking session. Oracle may itself run under a fixed poll budget (default 600s); a nested blocking call consumes that budget and stalls at the timeout.
|
|
100948
|
+
- **Use sequential top-level calls instead**: fire the delegation in background, return to your work, then collect the result with \`background_output(task_id=...)\`.
|
|
100698
100949
|
|
|
100699
100950
|
---
|
|
100700
100951
|
|
|
@@ -100728,12 +100979,14 @@ var ORACLE_HIGH_ACCURACY_MODE = `# PHASE 3: PLAN GENERATION
|
|
|
100728
100979
|
\`\`\`typescript
|
|
100729
100980
|
// After generating initial plan
|
|
100730
100981
|
while (true) {
|
|
100731
|
-
|
|
100982
|
+
// Fire Smith in background (never a blocking nested call), then collect its verdict.
|
|
100983
|
+
const smithTask = task(
|
|
100732
100984
|
subagent_type="smith",
|
|
100733
100985
|
load_skills=[],
|
|
100734
100986
|
prompt=".matrixx/plans/{name}.md",
|
|
100735
|
-
run_in_background=
|
|
100987
|
+
run_in_background=true
|
|
100736
100988
|
)
|
|
100989
|
+
const result = background_output(task_id=smithTask.task_id)
|
|
100737
100990
|
|
|
100738
100991
|
if (result.verdict === "OKAY") {
|
|
100739
100992
|
break // Plan approved - exit loop
|
|
@@ -101338,7 +101591,7 @@ task(subagent_type="operator", load_skills=[], prompt="I'm designing architectur
|
|
|
101338
101591
|
|
|
101339
101592
|
**Oracle Consultation** (recommend when stakes are high):
|
|
101340
101593
|
\`\`\`typescript
|
|
101341
|
-
task(subagent_type="oracle", load_skills=[], prompt="Architecture consultation needed: [context]...", run_in_background=
|
|
101594
|
+
task(subagent_type="oracle", load_skills=[], prompt="Architecture consultation needed: [context]...", run_in_background=true)
|
|
101342
101595
|
\`\`\`
|
|
101343
101596
|
|
|
101344
101597
|
**Interview Focus:**
|
|
@@ -101500,12 +101753,14 @@ todoWrite([
|
|
|
101500
101753
|
|
|
101501
101754
|
> **Edge \u2014 Trivial/Simple bypass:** If complexity is 1-2 (Trivial/Simple) and the request is ambiguous but single-scope (one domain, no architectural keywords), **bypass Seraph** and ask directly per Morpheus Phase 0 (ask ONE clarifying question).
|
|
101502
101755
|
|
|
101503
|
-
If gate **passes**, summon Seraph:
|
|
101756
|
+
If gate **passes**, summon Seraph in **background mode** (never blocking \u2014 see policy below):
|
|
101504
101757
|
|
|
101505
101758
|
\`\`\`typescript
|
|
101506
|
-
|
|
101759
|
+
// Fire Seraph in background \u2014 NEVER run_in_background=false from this session.
|
|
101760
|
+
const seraphTask = task(
|
|
101507
101761
|
subagent_type="seraph",
|
|
101508
101762
|
load_skills=[],
|
|
101763
|
+
run_in_background=true,
|
|
101509
101764
|
prompt=\`Review this planning session before I generate the work plan:
|
|
101510
101765
|
|
|
101511
101766
|
**User's Goal**: {summarize what user wants}
|
|
@@ -101525,11 +101780,18 @@ task(
|
|
|
101525
101780
|
3. Potential scope creep areas to lock down
|
|
101526
101781
|
4. Assumptions I'm making that need validation
|
|
101527
101782
|
5. Missing acceptance criteria
|
|
101528
|
-
6. Edge cases not addressed
|
|
101529
|
-
run_in_background=false
|
|
101783
|
+
6. Edge cases not addressed\`
|
|
101530
101784
|
)
|
|
101785
|
+
|
|
101786
|
+
// Continue drafting/other work, then collect once when needed:
|
|
101787
|
+
const seraphReview = background_output(task_id=seraphTask.task_id)
|
|
101531
101788
|
\`\`\`
|
|
101532
101789
|
|
|
101790
|
+
> **NO-BLOCKING-NESTING POLICY (MANDATORY \u2014 applies to EVERY Oracle delegation):**
|
|
101791
|
+
> - **Default to background mode**: always pass \`run_in_background=true\`. Oracle's own invocations default to background; blocking is never the default.
|
|
101792
|
+
> - **Never nest a blocking subagent call** (\`run_in_background=false\`) inside your own session. Oracle may itself be running inside a fixed poll budget (default 600s); a blocking nested call consumes that same budget and stalls at the timeout.
|
|
101793
|
+
> - **Use sequential top-level calls instead**: fire the delegation in background, return to your work, then collect the result with \`background_output(task_id=...)\`. If a result is required before proceeding, collect it once and continue \u2014 never block on a nested subagent.
|
|
101794
|
+
|
|
101533
101795
|
If gate **does NOT pass**, skip Seraph and proceed directly to plan generation (note \`Seraph bypassed: complexity=X / no multi-component signal\` in summary).
|
|
101534
101796
|
|
|
101535
101797
|
## Post-Seraph: Auto-Generate Plan and Summarize
|
|
@@ -101875,7 +102137,7 @@ Parallel Speedup: ~40% faster than sequential
|
|
|
101875
102137
|
|
|
101876
102138
|
| Wave | Tasks | Recommended Agents |
|
|
101877
102139
|
|------|-------|-------------------|
|
|
101878
|
-
| 1 | 1, 5 | task(category="...", load_skills=[...], run_in_background=
|
|
102140
|
+
| 1 | 1, 5 | task(category="...", load_skills=[...], run_in_background=true) |
|
|
101879
102141
|
| 2 | 2, 3, 6 | dispatch parallel after Wave 1 completes |
|
|
101880
102142
|
| 3 | 4 | final integration task |
|
|
101881
102143
|
|
|
@@ -102891,14 +103153,6 @@ var document_reader = {
|
|
|
102891
103153
|
enabled: true
|
|
102892
103154
|
};
|
|
102893
103155
|
|
|
102894
|
-
// src/mcp/grep-app.ts
|
|
102895
|
-
var grep_app = {
|
|
102896
|
-
type: "remote",
|
|
102897
|
-
url: "https://mcp.grep.app",
|
|
102898
|
-
enabled: true,
|
|
102899
|
-
oauth: false
|
|
102900
|
-
};
|
|
102901
|
-
|
|
102902
103156
|
// src/mcp/mcp-validator.ts
|
|
102903
103157
|
function validateWebsearchConfig(config4) {
|
|
102904
103158
|
const provider = config4?.provider || "exa";
|
|
@@ -102947,8 +103201,16 @@ function createWebsearchConfig(config4) {
|
|
|
102947
103201
|
}
|
|
102948
103202
|
var websearch = createWebsearchConfig();
|
|
102949
103203
|
|
|
103204
|
+
// src/mcp/white-rabbit.ts
|
|
103205
|
+
var white_rabbit = {
|
|
103206
|
+
type: "remote",
|
|
103207
|
+
url: "https://mcp.grep.app",
|
|
103208
|
+
enabled: true,
|
|
103209
|
+
oauth: false
|
|
103210
|
+
};
|
|
103211
|
+
|
|
102950
103212
|
// src/mcp/types.ts
|
|
102951
|
-
var McpNameSchema = exports_external.enum(["websearch", "context7", "
|
|
103213
|
+
var McpNameSchema = exports_external.enum(["websearch", "context7", "white_rabbit", "document_reader"]);
|
|
102952
103214
|
var AnyMcpNameSchema = exports_external.string().min(1);
|
|
102953
103215
|
|
|
102954
103216
|
// src/mcp/index.ts
|
|
@@ -102997,8 +103259,8 @@ function createBuiltinMcps(disabledMcps = [], config4, options = {}) {
|
|
|
102997
103259
|
if (!disabledMcps.includes("context7")) {
|
|
102998
103260
|
mcps.context7 = context7;
|
|
102999
103261
|
}
|
|
103000
|
-
if (!disabledMcps.includes("
|
|
103001
|
-
mcps.
|
|
103262
|
+
if (!disabledMcps.includes("white_rabbit")) {
|
|
103263
|
+
mcps.white_rabbit = white_rabbit;
|
|
103002
103264
|
}
|
|
103003
103265
|
if (!disabledMcps.includes("document_reader")) {
|
|
103004
103266
|
if (commandAvailable("uvx")) {
|
|
@@ -103100,7 +103362,7 @@ function applyToolConfig(params) {
|
|
|
103100
103362
|
const denyTaskTools = !isTaskSystem ? { "task_*": "deny", task: "deny" } : {};
|
|
103101
103363
|
params.config.tools = {
|
|
103102
103364
|
...params.config.tools,
|
|
103103
|
-
"
|
|
103365
|
+
"white_rabbit_*": false,
|
|
103104
103366
|
LspHover: false,
|
|
103105
103367
|
LspCodeActions: false,
|
|
103106
103368
|
LspCodeActionResolve: false,
|
|
@@ -103112,7 +103374,7 @@ function applyToolConfig(params) {
|
|
|
103112
103374
|
const questionPermission = isCliRunMode ? "deny" : "allow";
|
|
103113
103375
|
const operator = agentByKey(params.agentResult, "operator");
|
|
103114
103376
|
if (operator) {
|
|
103115
|
-
operator.permission = { ...operator.permission, "
|
|
103377
|
+
operator.permission = { ...operator.permission, "white_rabbit_*": "allow" };
|
|
103116
103378
|
}
|
|
103117
103379
|
const construct = agentByKey(params.agentResult, "construct");
|
|
103118
103380
|
if (construct) {
|
|
@@ -103220,6 +103482,9 @@ function createManagers(args) {
|
|
|
103220
103482
|
},
|
|
103221
103483
|
enableParentSessionNotifications: backgroundNotificationHookEnabled
|
|
103222
103484
|
});
|
|
103485
|
+
backgroundManager.restoreHandles().catch((error95) => {
|
|
103486
|
+
log("[index] Failed to restore background handles:", error95);
|
|
103487
|
+
});
|
|
103223
103488
|
initTaskToastManager(ctx.client);
|
|
103224
103489
|
const configHandler = createConfigHandler({
|
|
103225
103490
|
ctx: { directory: ctx.directory, client: ctx.client },
|
|
@@ -103306,7 +103571,8 @@ function createToolRegistry(args) {
|
|
|
103306
103571
|
availableSkills: skillContext.availableSkills,
|
|
103307
103572
|
modelRequirements: pluginConfig.modelRequirements,
|
|
103308
103573
|
complexityDowngrades: pluginConfig.complexityDowngrades,
|
|
103309
|
-
|
|
103574
|
+
modelPresets: pluginConfig.model_presets,
|
|
103575
|
+
activePreset: pluginConfig.active_preset,
|
|
103310
103576
|
agentOverrides: pluginConfig.agents,
|
|
103311
103577
|
onSyncSessionCreated: async (event) => {
|
|
103312
103578
|
log("[index] onSyncSessionCreated callback", {
|
|
@@ -103367,6 +103633,7 @@ function createToolRegistry(args) {
|
|
|
103367
103633
|
...createHandoffTools(ctx),
|
|
103368
103634
|
...createPdfExtractFiguresTool(),
|
|
103369
103635
|
...createDcpSwitchProfileTool({ pluginConfig }),
|
|
103636
|
+
...createPresetTool({ pluginConfig, directory: ctx.directory }),
|
|
103370
103637
|
...backgroundTools,
|
|
103371
103638
|
delegate_agent: delegateAgent,
|
|
103372
103639
|
...lookAt ? { look_at: lookAt } : {},
|
|
@@ -103393,6 +103660,10 @@ function createToolRegistry(args) {
|
|
|
103393
103660
|
// src/create-tools.ts
|
|
103394
103661
|
async function createTools(args) {
|
|
103395
103662
|
const { ctx, pluginConfig, managers } = args;
|
|
103663
|
+
const pollTimeout = resolveTasksConfig(pluginConfig).pollTimeoutMs;
|
|
103664
|
+
if (pollTimeout !== undefined) {
|
|
103665
|
+
setPollTimeoutMs(pollTimeout);
|
|
103666
|
+
}
|
|
103396
103667
|
const skillContext = await createSkillContext({
|
|
103397
103668
|
directory: ctx.directory,
|
|
103398
103669
|
pluginConfig
|
|
@@ -103494,10 +103765,8 @@ var AgentPermissionSchema = exports_external.object({
|
|
|
103494
103765
|
});
|
|
103495
103766
|
|
|
103496
103767
|
// src/config/schema/agent-overrides.ts
|
|
103497
|
-
var TierNameSchema = exports_external.enum(["free", "fast", "standard", "premium", "frontier"]);
|
|
103498
103768
|
var AgentOverrideConfigSchema = exports_external.object({
|
|
103499
103769
|
model: exports_external.string().optional(),
|
|
103500
|
-
tier: TierNameSchema.optional(),
|
|
103501
103770
|
variant: exports_external.string().optional(),
|
|
103502
103771
|
category: exports_external.string().optional(),
|
|
103503
103772
|
skills: exports_external.array(exports_external.string()).optional(),
|
|
@@ -103588,7 +103857,6 @@ var BrowserAutomationConfigSchema = exports_external.object({
|
|
|
103588
103857
|
var CategoryConfigSchema = exports_external.object({
|
|
103589
103858
|
description: exports_external.string().optional(),
|
|
103590
103859
|
model: exports_external.string().optional(),
|
|
103591
|
-
tier: TierNameSchema.optional(),
|
|
103592
103860
|
variant: exports_external.string().optional(),
|
|
103593
103861
|
temperature: exports_external.number().min(0).max(2).optional(),
|
|
103594
103862
|
top_p: exports_external.number().min(0).max(1).optional(),
|
|
@@ -103626,7 +103894,7 @@ var BuiltinCommandNameSchema = exports_external.enum([
|
|
|
103626
103894
|
"refactor",
|
|
103627
103895
|
"start-work",
|
|
103628
103896
|
"stop-continuation",
|
|
103629
|
-
"
|
|
103897
|
+
"preset",
|
|
103630
103898
|
"end-ultrawork",
|
|
103631
103899
|
"handoff",
|
|
103632
103900
|
"research",
|
|
@@ -103758,6 +104026,7 @@ var HookNameEnum = exports_external.enum([
|
|
|
103758
104026
|
"preemptive-compaction",
|
|
103759
104027
|
"rules-injector",
|
|
103760
104028
|
"background-notification",
|
|
104029
|
+
"background-task-blocker",
|
|
103761
104030
|
"auto-update-checker",
|
|
103762
104031
|
"startup-toast",
|
|
103763
104032
|
"keyword-detector",
|
|
@@ -103823,6 +104092,29 @@ var MatrixxSelfConfigSkillConfigSchema = exports_external.object({
|
|
|
103823
104092
|
proactive: exports_external.boolean().default(false)
|
|
103824
104093
|
});
|
|
103825
104094
|
|
|
104095
|
+
// src/config/schema/model-presets.ts
|
|
104096
|
+
var providerModelSchema = exports_external.string().refine((s) => {
|
|
104097
|
+
if (!s.includes("/"))
|
|
104098
|
+
return false;
|
|
104099
|
+
if (s.startsWith("/") || s.endsWith("/"))
|
|
104100
|
+
return false;
|
|
104101
|
+
if (s.includes("//"))
|
|
104102
|
+
return false;
|
|
104103
|
+
const parts = s.split("/");
|
|
104104
|
+
return parts.length >= 2 && parts.every((p) => p.length > 0);
|
|
104105
|
+
}, { message: "Must be <provider>/<model>" });
|
|
104106
|
+
var ModelPresetEntrySchema = exports_external.object({
|
|
104107
|
+
model: providerModelSchema,
|
|
104108
|
+
variant: exports_external.string().optional()
|
|
104109
|
+
});
|
|
104110
|
+
var ModelPresetSchema = exports_external.object({
|
|
104111
|
+
default_model: providerModelSchema.optional(),
|
|
104112
|
+
agents: exports_external.record(exports_external.string(), ModelPresetEntrySchema).optional(),
|
|
104113
|
+
categories: exports_external.record(exports_external.string(), ModelPresetEntrySchema).optional()
|
|
104114
|
+
});
|
|
104115
|
+
var ModelPresetsSchema = exports_external.record(exports_external.string(), ModelPresetSchema);
|
|
104116
|
+
var ActivePresetSchema = exports_external.string().min(1);
|
|
104117
|
+
|
|
103826
104118
|
// src/config/schema/morpheus.ts
|
|
103827
104119
|
var MorpheusTasksConfigSchema = exports_external.object({
|
|
103828
104120
|
storage_path: exports_external.string().optional(),
|
|
@@ -103949,6 +104241,22 @@ var SkillsConfigSchema = exports_external.union([
|
|
|
103949
104241
|
}).catchall(SkillEntrySchema)
|
|
103950
104242
|
]);
|
|
103951
104243
|
|
|
104244
|
+
// src/config/schema/task.ts
|
|
104245
|
+
var TaskConfigSchema = exports_external.object({
|
|
104246
|
+
pollTimeoutMs: exports_external.number().min(60000).optional()
|
|
104247
|
+
});
|
|
104248
|
+
|
|
104249
|
+
// src/config/schema/tasks.ts
|
|
104250
|
+
var TasksConfigSchema = exports_external.object({
|
|
104251
|
+
enabled: exports_external.boolean().optional().default(true),
|
|
104252
|
+
scope: exports_external.enum(["global", "project"]).default("project").optional().describe("Task storage scope: project \u2192 .matrixx/tasks per project (default), global \u2192 <opencode-config>/tasks/{listId}"),
|
|
104253
|
+
storage_path: exports_external.string().optional(),
|
|
104254
|
+
task_list_id: exports_external.string().optional(),
|
|
104255
|
+
stale_after_hours: exports_external.number().int().min(1).optional().describe("Pending tasks with no file activity for this many hours are considered stale (default: 24). Stale tasks are excluded from task-continuation directives."),
|
|
104256
|
+
session_scoped: exports_external.boolean().default(true).optional().describe("When true, the task-continuation-enforcer only considers tasks created by the current session (or its subagent sessions). When false, all project tasks are considered regardless of session origin."),
|
|
104257
|
+
pollTimeoutMs: exports_external.number().min(60000).optional()
|
|
104258
|
+
});
|
|
104259
|
+
|
|
103952
104260
|
// src/config/schema/tdd-enforcer.ts
|
|
103953
104261
|
var TddEnforcerConfigSchema = exports_external.object({
|
|
103954
104262
|
enabled: exports_external.boolean().default(false)
|
|
@@ -103980,7 +104288,8 @@ var WebsearchConfigSchema = exports_external.object({
|
|
|
103980
104288
|
var MatrixxConfigSchema = exports_external.object({
|
|
103981
104289
|
$schema: exports_external.string().optional(),
|
|
103982
104290
|
global_model: exports_external.string().optional(),
|
|
103983
|
-
|
|
104291
|
+
model_presets: ModelPresetsSchema.optional(),
|
|
104292
|
+
active_preset: ActivePresetSchema.optional(),
|
|
103984
104293
|
new_task_system_enabled: exports_external.boolean().optional(),
|
|
103985
104294
|
default_run_agent: exports_external.string().optional(),
|
|
103986
104295
|
disabled_mcps: exports_external.array(AnyMcpNameSchema).optional(),
|
|
@@ -104002,6 +104311,8 @@ var MatrixxConfigSchema = exports_external.object({
|
|
|
104002
104311
|
background_task: BackgroundTaskConfigSchema.optional(),
|
|
104003
104312
|
notification: NotificationConfigSchema.optional(),
|
|
104004
104313
|
babysitting: BabysittingConfigSchema.optional(),
|
|
104314
|
+
tasks: TasksConfigSchema.optional(),
|
|
104315
|
+
task: TaskConfigSchema.optional(),
|
|
104005
104316
|
tdd_enforcer: TddEnforcerConfigSchema.optional(),
|
|
104006
104317
|
browser_automation_engine: BrowserAutomationConfigSchema.optional(),
|
|
104007
104318
|
websearch: WebsearchConfigSchema.optional(),
|
|
@@ -104017,7 +104328,6 @@ var MatrixxConfigSchema = exports_external.object({
|
|
|
104017
104328
|
rtk: RtkConfigSchema.optional(),
|
|
104018
104329
|
evolution: EvolutionConfigSchema.optional(),
|
|
104019
104330
|
_migrations: exports_external.array(exports_external.string()).optional(),
|
|
104020
|
-
tiers: TiersSchema.optional(),
|
|
104021
104331
|
modelRequirements: ModelRequirementsSchema.optional(),
|
|
104022
104332
|
complexityDowngrades: ComplexityDowngradesSchema.optional()
|
|
104023
104333
|
});
|
|
@@ -104025,11 +104335,8 @@ var MatrixxConfigSchema = exports_external.object({
|
|
|
104025
104335
|
init_logger();
|
|
104026
104336
|
var MIGRATION_KEY = "model-migration";
|
|
104027
104337
|
function normalizeModelInput(value) {
|
|
104028
|
-
if (value.startsWith("tier:")) {
|
|
104029
|
-
return value;
|
|
104030
|
-
}
|
|
104031
104338
|
if (value.includes("/")) {
|
|
104032
|
-
log(`[migration] prefixed model '${value}' is deprecated, use
|
|
104339
|
+
log(`[migration] prefixed model '${value}' is deprecated, use bare '<model>'`);
|
|
104033
104340
|
}
|
|
104034
104341
|
return value;
|
|
104035
104342
|
}
|
|
@@ -104041,26 +104348,6 @@ function migrateMatrixxConfig(raw) {
|
|
|
104041
104348
|
if (typeof migrated.global_model === "string") {
|
|
104042
104349
|
migrated = { ...migrated, global_model: normalizeModelInput(migrated.global_model) };
|
|
104043
104350
|
}
|
|
104044
|
-
if (migrated.tiers) {
|
|
104045
|
-
const nextTiers = {};
|
|
104046
|
-
for (const [tierName, spec] of Object.entries(migrated.tiers)) {
|
|
104047
|
-
if (!spec || typeof spec !== "object") {
|
|
104048
|
-
nextTiers[tierName] = spec;
|
|
104049
|
-
continue;
|
|
104050
|
-
}
|
|
104051
|
-
const typed = spec;
|
|
104052
|
-
if (!typed.fallback) {
|
|
104053
|
-
nextTiers[tierName] = spec;
|
|
104054
|
-
continue;
|
|
104055
|
-
}
|
|
104056
|
-
const nextFallback = typed.fallback.map((entry) => ({
|
|
104057
|
-
...entry,
|
|
104058
|
-
model: normalizeModelInput(entry.model)
|
|
104059
|
-
}));
|
|
104060
|
-
nextTiers[tierName] = { ...spec, fallback: nextFallback };
|
|
104061
|
-
}
|
|
104062
|
-
migrated = { ...migrated, tiers: nextTiers };
|
|
104063
|
-
}
|
|
104064
104351
|
if (migrated.modelRequirements) {
|
|
104065
104352
|
const nextMR = { ...migrated.modelRequirements };
|
|
104066
104353
|
for (const scope of ["agents", "categories"]) {
|
|
@@ -104205,7 +104492,7 @@ function mergeConfigs(base, override) {
|
|
|
104205
104492
|
});
|
|
104206
104493
|
return merged;
|
|
104207
104494
|
}
|
|
104208
|
-
function
|
|
104495
|
+
function resolveConfigPath2(baseDir, configName) {
|
|
104209
104496
|
const newBase = path13.join(baseDir, "matrixx");
|
|
104210
104497
|
const newDetected = detectConfigFile(newBase);
|
|
104211
104498
|
if (newDetected.format !== "none") {
|
|
@@ -104219,29 +104506,39 @@ function resolveConfigPath(baseDir, configName) {
|
|
|
104219
104506
|
}
|
|
104220
104507
|
return `${newBase}.json`;
|
|
104221
104508
|
}
|
|
104509
|
+
function assertModelsResolvable(config4) {
|
|
104510
|
+
const globalModel = config4.global_model;
|
|
104511
|
+
const unresolved = [];
|
|
104512
|
+
for (const [name, entry] of Object.entries(config4.agents ?? {})) {
|
|
104513
|
+
if (entry && !entry.disable && !entry.model && !globalModel) {
|
|
104514
|
+
unresolved.push(`agent "${name}"`);
|
|
104515
|
+
}
|
|
104516
|
+
}
|
|
104517
|
+
for (const [name, entry] of Object.entries(config4.categories ?? {})) {
|
|
104518
|
+
if (entry && !entry.disable && !entry.model && !globalModel) {
|
|
104519
|
+
unresolved.push(`category "${name}"`);
|
|
104520
|
+
}
|
|
104521
|
+
}
|
|
104522
|
+
if (unresolved.length > 0) {
|
|
104523
|
+
throw new ConfigError(`Matrixx config error: no model resolved for ${unresolved.join(", ")}. ` + `Set an explicit "model" on the entry, define model_presets with an active_preset, ` + `or run "bunx opencode-matrixx setup" to generate a preset from your connected providers.`);
|
|
104524
|
+
}
|
|
104525
|
+
}
|
|
104222
104526
|
async function loadPluginConfig(directory, ctx) {
|
|
104223
104527
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
104224
|
-
const userConfigPath =
|
|
104528
|
+
const userConfigPath = resolveConfigPath2(configDir, "matrixx");
|
|
104225
104529
|
const projectDir = path13.join(directory, ".opencode");
|
|
104226
|
-
const projectConfigPath =
|
|
104530
|
+
const projectConfigPath = resolveConfigPath2(projectDir, "matrixx");
|
|
104227
104531
|
let config4 = loadConfigFromPath(userConfigPath, ctx) ?? {};
|
|
104228
104532
|
const projectConfig = loadConfigFromPath(projectConfigPath, ctx);
|
|
104229
104533
|
if (projectConfig) {
|
|
104230
104534
|
config4 = mergeConfigs(config4, projectConfig);
|
|
104231
104535
|
}
|
|
104232
104536
|
config4 = migrateMatrixxConfig(config4);
|
|
104233
|
-
|
|
104234
|
-
|
|
104235
|
-
|
|
104236
|
-
|
|
104237
|
-
|
|
104238
|
-
};
|
|
104239
|
-
config4 = resolveTiersInConfig(config4, tierCtx);
|
|
104240
|
-
const resolvedDefaults = resolveTiersInCategoryRegistry(DEFAULT_CATEGORIES, tierCtx, config4);
|
|
104241
|
-
config4 = {
|
|
104242
|
-
...config4,
|
|
104243
|
-
categories: { ...resolvedDefaults, ...config4.categories ?? {} }
|
|
104244
|
-
};
|
|
104537
|
+
config4 = applyActivePreset(config4, config4.active_preset);
|
|
104538
|
+
if (config4.active_preset && config4.model_presets?.[config4.active_preset]) {
|
|
104539
|
+
log("Active model preset applied", { active_preset: config4.active_preset });
|
|
104540
|
+
}
|
|
104541
|
+
assertModelsResolvable(config4);
|
|
104245
104542
|
log("Final merged config", {
|
|
104246
104543
|
agents: config4.agents,
|
|
104247
104544
|
disabled_agents: config4.disabled_agents,
|
|
@@ -104461,19 +104758,19 @@ function createEventHandler2(args) {
|
|
|
104461
104758
|
};
|
|
104462
104759
|
const recentSyntheticIdles = new Map;
|
|
104463
104760
|
const recentRealIdles = new Map;
|
|
104464
|
-
const
|
|
104761
|
+
const DEDUP_WINDOW_MS2 = 500;
|
|
104465
104762
|
return async (input) => {
|
|
104466
104763
|
pruneRecentSyntheticIdles({
|
|
104467
104764
|
recentSyntheticIdles,
|
|
104468
104765
|
recentRealIdles,
|
|
104469
104766
|
now: Date.now(),
|
|
104470
|
-
dedupWindowMs:
|
|
104767
|
+
dedupWindowMs: DEDUP_WINDOW_MS2
|
|
104471
104768
|
});
|
|
104472
104769
|
if (input.event.type === "session.idle") {
|
|
104473
104770
|
const sessionID = input.event.properties?.sessionID;
|
|
104474
104771
|
if (sessionID) {
|
|
104475
104772
|
const emittedAt = recentSyntheticIdles.get(sessionID);
|
|
104476
|
-
if (emittedAt && Date.now() - emittedAt <
|
|
104773
|
+
if (emittedAt && Date.now() - emittedAt < DEDUP_WINDOW_MS2) {
|
|
104477
104774
|
recentSyntheticIdles.delete(sessionID);
|
|
104478
104775
|
return;
|
|
104479
104776
|
}
|
|
@@ -104485,7 +104782,7 @@ function createEventHandler2(args) {
|
|
|
104485
104782
|
if (syntheticIdle) {
|
|
104486
104783
|
const sessionID = syntheticIdle.event.properties?.sessionID;
|
|
104487
104784
|
const emittedAt = recentRealIdles.get(sessionID);
|
|
104488
|
-
if (emittedAt && Date.now() - emittedAt <
|
|
104785
|
+
if (emittedAt && Date.now() - emittedAt < DEDUP_WINDOW_MS2) {
|
|
104489
104786
|
recentRealIdles.delete(sessionID);
|
|
104490
104787
|
return;
|
|
104491
104788
|
}
|
|
@@ -104661,22 +104958,22 @@ function createToolExecuteAfterHandler2(args) {
|
|
|
104661
104958
|
|
|
104662
104959
|
// src/features/assembly-state/manager.ts
|
|
104663
104960
|
init_logger();
|
|
104664
|
-
var
|
|
104961
|
+
var HOOK_NAME20 = "assembly-state";
|
|
104665
104962
|
var disabledSessions = new Set;
|
|
104666
104963
|
function isAssemblyDisabled(sessionID) {
|
|
104667
104964
|
return disabledSessions.has(sessionID);
|
|
104668
104965
|
}
|
|
104669
104966
|
function disableAssembly(sessionID) {
|
|
104670
104967
|
disabledSessions.add(sessionID);
|
|
104671
|
-
log(`[${
|
|
104968
|
+
log(`[${HOOK_NAME20}] Assembly disabled for session`, { sessionID });
|
|
104672
104969
|
disabledSessions.add(sessionID);
|
|
104673
|
-
log(`[${
|
|
104970
|
+
log(`[${HOOK_NAME20}] Consensus disabled for session`, { sessionID });
|
|
104674
104971
|
}
|
|
104675
104972
|
function enableAssembly(sessionID) {
|
|
104676
104973
|
disabledSessions.delete(sessionID);
|
|
104677
|
-
log(`[${
|
|
104974
|
+
log(`[${HOOK_NAME20}] Assembly enabled for session`, { sessionID });
|
|
104678
104975
|
disabledSessions.delete(sessionID);
|
|
104679
|
-
log(`[${
|
|
104976
|
+
log(`[${HOOK_NAME20}] Consensus enabled for session`, { sessionID });
|
|
104680
104977
|
}
|
|
104681
104978
|
// src/plugin/session-agent-resolver.ts
|
|
104682
104979
|
async function resolveSessionAgent(client2, sessionId, timeoutMs = 5000) {
|
|
@@ -104719,6 +105016,7 @@ function createToolExecuteBeforeHandler2(args) {
|
|
|
104719
105016
|
const taskEditGuardHook = hooks2.taskEditGuard?.["tool.execute.before"];
|
|
104720
105017
|
const tasksTodowriteDisablerHook = hooks2.tasksTodowriteDisabler?.["tool.execute.before"];
|
|
104721
105018
|
const oracleMdOnlyHook = hooks2.oracleMdOnly?.["tool.execute.before"];
|
|
105019
|
+
const backgroundTaskBlockerHook = hooks2.backgroundTaskBlocker?.["tool.execute.before"];
|
|
104722
105020
|
const contextModeEnforcerHook = hooks2.contextModeEnforcer?.["tool.execute.before"];
|
|
104723
105021
|
const nonInteractiveEnvHook = hooks2.nonInteractiveEnv?.["tool.execute.before"];
|
|
104724
105022
|
const bashFileReadGuardHook = hooks2.bashFileReadGuard?.["tool.execute.before"];
|
|
@@ -104739,7 +105037,8 @@ function createToolExecuteBeforeHandler2(args) {
|
|
|
104739
105037
|
taskEditGuardHook?.(input, output),
|
|
104740
105038
|
tasksTodowriteDisablerHook?.(input, output),
|
|
104741
105039
|
oracleMdOnlyHook?.(input, output),
|
|
104742
|
-
contextModeEnforcerHook?.(input, output)
|
|
105040
|
+
contextModeEnforcerHook?.(input, output),
|
|
105041
|
+
backgroundTaskBlockerHook?.(input, output)
|
|
104743
105042
|
]);
|
|
104744
105043
|
let resolvePromise;
|
|
104745
105044
|
if (input.tool === "task") {
|