opencode-matrixx 2.6.4 → 2.6.5
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/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 +486 -59
- 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/matrixx-config.d.ts +28 -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/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/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 +673 -598
- package/dist/matrixx.schema.json +113 -244
- package/dist/mcp/types.d.ts +1 -1
- package/dist/mcp/{grep-app.d.ts → white-rabbit.d.ts} +1 -1
- package/dist/shared/config-errors.d.ts +3 -0
- package/dist/shared/merge-categories.d.ts +1 -16
- package/dist/shared/task-system-gating.d.ts +10 -0
- 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/timing.d.ts +5 -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/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,13 @@ 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.startsWith("tier:")) {
|
|
44429
|
-
const name = s.slice(5);
|
|
44430
|
-
return name.length > 0;
|
|
44431
|
-
}
|
|
44432
44414
|
if (s.includes("/")) {
|
|
44433
44415
|
const parts = s.split("/");
|
|
44434
44416
|
return parts.length === 2 && parts[0].length > 0 && parts[1].length > 0;
|
|
44435
44417
|
}
|
|
44436
44418
|
return false;
|
|
44437
|
-
}, { message: "Must be
|
|
44419
|
+
}, { message: "Must be <provider>/<model>" });
|
|
44438
44420
|
var ComplexityDowngradesSchema = exports_external.record(exports_external.string(), exports_external.record(exports_external.string(), complexityDowngradeValueSchema));
|
|
44439
44421
|
var AgentModelRequirementsSchema = exports_external.object({
|
|
44440
44422
|
fallbackChain: exports_external.array(ModelFallbackEntrySchema),
|
|
@@ -44861,6 +44843,12 @@ async function resolveCommandsInText(text, depth = 0, maxDepth = 3) {
|
|
|
44861
44843
|
return resolved;
|
|
44862
44844
|
}
|
|
44863
44845
|
// src/shared/config-errors.ts
|
|
44846
|
+
class ConfigError extends Error {
|
|
44847
|
+
constructor(message) {
|
|
44848
|
+
super(message);
|
|
44849
|
+
this.name = "ConfigError";
|
|
44850
|
+
}
|
|
44851
|
+
}
|
|
44864
44852
|
var configLoadErrors = [];
|
|
44865
44853
|
function getConfigLoadErrors() {
|
|
44866
44854
|
return configLoadErrors;
|
|
@@ -48112,7 +48100,7 @@ var TARGET_TOOLS = new Set([
|
|
|
48112
48100
|
"context7_query-docs",
|
|
48113
48101
|
"websearch_web_search_exa",
|
|
48114
48102
|
"context7_get-library-docs",
|
|
48115
|
-
"
|
|
48103
|
+
"white_rabbit_searchgithub"
|
|
48116
48104
|
]);
|
|
48117
48105
|
var AGENT_TOOLS = new Set([
|
|
48118
48106
|
"task",
|
|
@@ -48498,10 +48486,14 @@ function registerSubagentSession(sessionID, parentSessionID) {
|
|
|
48498
48486
|
subagentSessions.add(sessionID);
|
|
48499
48487
|
subagentParentMap.set(sessionID, parentSessionID);
|
|
48500
48488
|
}
|
|
48489
|
+
function unregisterSubagentSession(sessionID) {
|
|
48490
|
+
subagentSessions.delete(sessionID);
|
|
48491
|
+
subagentParentMap.delete(sessionID);
|
|
48492
|
+
}
|
|
48501
48493
|
function getSubagentSessionIDs(parentSessionID) {
|
|
48502
48494
|
const ids = [];
|
|
48503
|
-
for (const
|
|
48504
|
-
if (
|
|
48495
|
+
for (const subID of subagentSessions) {
|
|
48496
|
+
if (subagentParentMap.get(subID) === parentSessionID)
|
|
48505
48497
|
ids.push(subID);
|
|
48506
48498
|
}
|
|
48507
48499
|
return ids;
|
|
@@ -50255,79 +50247,49 @@ This prevents it from being picked up again.
|
|
|
50255
50247
|
- DO begin working immediately if the user included a task with /pickup
|
|
50256
50248
|
`;
|
|
50257
50249
|
|
|
50258
|
-
// src/features/builtin-commands/templates/
|
|
50259
|
-
var
|
|
50250
|
+
// src/features/builtin-commands/templates/preset.ts
|
|
50251
|
+
var PRESET_TEMPLATE = `You are managing the active model preset.
|
|
50252
|
+
|
|
50253
|
+
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
50254
|
|
|
50261
50255
|
## Usage
|
|
50262
50256
|
|
|
50263
50257
|
\`\`\`
|
|
50264
|
-
/
|
|
50265
|
-
/
|
|
50266
|
-
/
|
|
50267
|
-
/
|
|
50268
|
-
/
|
|
50269
|
-
/profile set <name> --project \u2192 Change profile project-scoped (.opencode/matrixx.jsonc)
|
|
50258
|
+
/preset \u2192 List available presets and mark the active one
|
|
50259
|
+
/preset list \u2192 List available presets
|
|
50260
|
+
/preset show [<name>] \u2192 Show a preset's agent/category model assignments (defaults to active)
|
|
50261
|
+
/preset set <name> \u2192 Switch the active preset for the current session (live)
|
|
50262
|
+
/preset set <name> --save [--global|--project] \u2192 Also persist active_preset to config
|
|
50270
50263
|
\`\`\`
|
|
50271
50264
|
|
|
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
|
|
50265
|
+
## Step 1: Determine the action
|
|
50300
50266
|
|
|
50301
|
-
|
|
50302
|
-
- \`--project\` (default) \u2192 Edit \`.opencode/matrixx.jsonc\`
|
|
50303
|
-
- If no flag and neither file exists, use \`--project\`
|
|
50267
|
+
Parse the arguments passed to this command:
|
|
50304
50268
|
|
|
50305
|
-
|
|
50269
|
+
- No arguments or \`list\` \u2192 call \`preset\` with \`action: "list"\`.
|
|
50270
|
+
- \`show\` \u2192 call \`preset\` with \`action: "show"\` (optionally \`name\`).
|
|
50271
|
+
- \`set <name>\` \u2192 call \`preset\` with \`action: "set"\`, \`name: "<name>"\`.
|
|
50272
|
+
- \`set <name> --save\` \u2192 also pass \`save: true\`; \`--global\` \u2192 \`scope: "global"\`, \`--project\` (default) \u2192 \`scope: "project"\`.
|
|
50306
50273
|
|
|
50307
|
-
|
|
50274
|
+
## Step 2: Call the built-in \`preset\` tool
|
|
50308
50275
|
|
|
50309
|
-
|
|
50276
|
+
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
50277
|
|
|
50311
|
-
|
|
50278
|
+
The tool handles all validation and file operations \u2014 do NOT edit config files directly.
|
|
50312
50279
|
|
|
50313
|
-
|
|
50280
|
+
## Step 3: Confirm and instruct
|
|
50314
50281
|
|
|
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
|
|
50282
|
+
After a successful switch:
|
|
50322
50283
|
|
|
50323
|
-
|
|
50324
|
-
|
|
50325
|
-
|
|
50326
|
-
3. If they want session-level switching without restarting, suggest using the profile with \`--global\` or \`--project\` as appropriate
|
|
50284
|
+
1. Report the tool's output to the user.
|
|
50285
|
+
2. Note that delegate-task categories use the new preset immediately (session-scoped live switch).
|
|
50286
|
+
3. If \`--save\` was used, tell the user the persisted \`active_preset\` takes effect for builtin agents on the next session.
|
|
50327
50287
|
|
|
50328
|
-
|
|
50288
|
+
## Important constraints
|
|
50329
50289
|
|
|
50330
|
-
|
|
50290
|
+
- Use the built-in \`preset\` tool. Do NOT bypass it by editing config files directly.
|
|
50291
|
+
- Do not invent preset names \u2014 if the requested name is unknown, report the available presets and stop.
|
|
50292
|
+
- \`--save\` without a scope defaults to project scope.`;
|
|
50331
50293
|
|
|
50332
50294
|
// src/features/builtin-commands/templates/refactor.ts
|
|
50333
50295
|
var REFACTOR_TEMPLATE = `# Intelligent Refactor Command
|
|
@@ -51596,16 +51558,16 @@ ${REMOVE_DEADCODE_TEMPLATE}
|
|
|
51596
51558
|
</command-instruction>`,
|
|
51597
51559
|
argumentHint: "[target-path] [--scope=<file|module|project>] [--dry-run]"
|
|
51598
51560
|
},
|
|
51599
|
-
|
|
51600
|
-
description:
|
|
51561
|
+
preset: {
|
|
51562
|
+
description: "(builtin) Switch the active model preset (session-scoped live switch; --save persists to config)",
|
|
51601
51563
|
template: `<command-instruction>
|
|
51602
|
-
${
|
|
51564
|
+
${PRESET_TEMPLATE}
|
|
51603
51565
|
</command-instruction>
|
|
51604
51566
|
|
|
51605
51567
|
<user-request>
|
|
51606
51568
|
$ARGUMENTS
|
|
51607
51569
|
</user-request>`,
|
|
51608
|
-
argumentHint: "[list|show|set <name> [--global|--project]]"
|
|
51570
|
+
argumentHint: "[list|show|set <name> [--save [--global|--project]]]"
|
|
51609
51571
|
},
|
|
51610
51572
|
"end-ultrawork": {
|
|
51611
51573
|
description: "(builtin) Deactivate ultrawork mode and return to default Matrixx behavior for the current session",
|
|
@@ -59099,7 +59061,7 @@ var JSON_ERROR_TOOL_EXCLUDE_LIST = [
|
|
|
59099
59061
|
"grep",
|
|
59100
59062
|
"webfetch",
|
|
59101
59063
|
"look_at",
|
|
59102
|
-
"
|
|
59064
|
+
"white_rabbit_searchgithub",
|
|
59103
59065
|
"websearch_web_search_exa"
|
|
59104
59066
|
];
|
|
59105
59067
|
var JSON_ERROR_PATTERNS = [
|
|
@@ -60125,16 +60087,38 @@ init_logger();
|
|
|
60125
60087
|
import { randomUUID } from "crypto";
|
|
60126
60088
|
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
60089
|
import { basename as basename5, dirname as dirname10, isAbsolute as isAbsolute3, join as join44 } from "path";
|
|
60090
|
+
|
|
60091
|
+
// src/shared/task-system-gating.ts
|
|
60092
|
+
var TASK_SYSTEM_DEFAULT = true;
|
|
60093
|
+
function resolveTasksConfig(config2) {
|
|
60094
|
+
const canonical = config2?.tasks;
|
|
60095
|
+
const legacyTasks = config2?.morpheus?.tasks;
|
|
60096
|
+
const legacyPoll = config2?.task?.pollTimeoutMs;
|
|
60097
|
+
return {
|
|
60098
|
+
enabled: canonical?.enabled ?? config2?.experimental?.task_system ?? config2?.new_task_system_enabled ?? TASK_SYSTEM_DEFAULT,
|
|
60099
|
+
scope: canonical?.scope ?? legacyTasks?.scope ?? "project",
|
|
60100
|
+
storage_path: canonical?.storage_path ?? legacyTasks?.storage_path,
|
|
60101
|
+
task_list_id: canonical?.task_list_id ?? legacyTasks?.task_list_id,
|
|
60102
|
+
stale_after_hours: canonical?.stale_after_hours ?? legacyTasks?.stale_after_hours,
|
|
60103
|
+
session_scoped: canonical?.session_scoped ?? legacyTasks?.session_scoped ?? true,
|
|
60104
|
+
pollTimeoutMs: canonical?.pollTimeoutMs ?? legacyPoll
|
|
60105
|
+
};
|
|
60106
|
+
}
|
|
60107
|
+
function isTaskSystemEnabled(config2) {
|
|
60108
|
+
return resolveTasksConfig(config2).enabled;
|
|
60109
|
+
}
|
|
60110
|
+
|
|
60111
|
+
// src/features/task-storage/storage.ts
|
|
60128
60112
|
function getProjectTaskDir(directory) {
|
|
60129
60113
|
return join44(directory, ".matrixx", "tasks");
|
|
60130
60114
|
}
|
|
60131
60115
|
function getTaskDir(config2 = {}, directory) {
|
|
60132
|
-
const tasksConfig = config2
|
|
60133
|
-
const storagePath = tasksConfig
|
|
60116
|
+
const tasksConfig = resolveTasksConfig(config2);
|
|
60117
|
+
const storagePath = tasksConfig.storage_path;
|
|
60134
60118
|
if (storagePath) {
|
|
60135
60119
|
return isAbsolute3(storagePath) ? storagePath : join44(directory ?? process.cwd(), storagePath);
|
|
60136
60120
|
}
|
|
60137
|
-
if (tasksConfig
|
|
60121
|
+
if (tasksConfig.scope === "global" || !directory) {
|
|
60138
60122
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
60139
60123
|
const listId = resolveTaskListId(config2);
|
|
60140
60124
|
return join44(configDir, "tasks", listId);
|
|
@@ -60148,7 +60132,7 @@ function resolveTaskListId(config2 = {}) {
|
|
|
60148
60132
|
const envId = process.env.ULTRAWORK_TASK_LIST_ID?.trim();
|
|
60149
60133
|
if (envId)
|
|
60150
60134
|
return sanitizePathSegment(envId);
|
|
60151
|
-
const configId = config2.
|
|
60135
|
+
const configId = resolveTasksConfig(config2).task_list_id?.trim();
|
|
60152
60136
|
if (configId)
|
|
60153
60137
|
return sanitizePathSegment(configId);
|
|
60154
60138
|
return sanitizePathSegment(basename5(process.cwd()));
|
|
@@ -60369,6 +60353,7 @@ async function getGitHead(directory) {
|
|
|
60369
60353
|
|
|
60370
60354
|
// src/tools/task/constants.ts
|
|
60371
60355
|
var TASK_ID_PATTERN = /^T-[A-Za-z0-9]+(-[A-Za-z0-9]+)*$/;
|
|
60356
|
+
var DEDUP_WINDOW_MS = 10 * 60 * 1000;
|
|
60372
60357
|
|
|
60373
60358
|
// src/tools/task/types.ts
|
|
60374
60359
|
var TaskStatusSchema = exports_external.enum(["pending", "in_progress", "completed", "deleted"]);
|
|
@@ -64921,7 +64906,7 @@ init_logger();
|
|
|
64921
64906
|
|
|
64922
64907
|
// src/hooks/task-continuation-enforcer/idle-event.ts
|
|
64923
64908
|
import { existsSync as existsSync43, readdirSync as readdirSync15 } from "fs";
|
|
64924
|
-
import { join as
|
|
64909
|
+
import { join as join52 } from "path";
|
|
64925
64910
|
init_logger();
|
|
64926
64911
|
|
|
64927
64912
|
// src/hooks/task-continuation-enforcer/abort-detection.ts
|
|
@@ -64945,14 +64930,15 @@ init_logger();
|
|
|
64945
64930
|
|
|
64946
64931
|
// src/hooks/task-continuation-enforcer/continuation-injection.ts
|
|
64947
64932
|
import { existsSync as existsSync42, readdirSync as readdirSync14 } from "fs";
|
|
64948
|
-
import { join as
|
|
64933
|
+
import { join as join51 } from "path";
|
|
64949
64934
|
init_logger();
|
|
64950
64935
|
// src/hooks/task-continuation-enforcer/staleness.ts
|
|
64951
64936
|
import { statSync as statSync8 } from "fs";
|
|
64937
|
+
import { join as join50 } from "path";
|
|
64952
64938
|
var DEFAULT_STALE_AFTER_HOURS = 24;
|
|
64953
64939
|
var HOUR_MS = 60 * 60 * 1000;
|
|
64954
64940
|
function getStaleAfterMs(config2) {
|
|
64955
|
-
const hours = config2
|
|
64941
|
+
const hours = resolveTasksConfig(config2).stale_after_hours ?? DEFAULT_STALE_AFTER_HOURS;
|
|
64956
64942
|
return hours * HOUR_MS;
|
|
64957
64943
|
}
|
|
64958
64944
|
function getTaskAgeMs(taskPath) {
|
|
@@ -64967,6 +64953,9 @@ function isTaskStale(taskPath, staleAfterMs) {
|
|
|
64967
64953
|
const ageMs = getTaskAgeMs(taskPath);
|
|
64968
64954
|
return ageMs !== null && ageMs > staleAfterMs;
|
|
64969
64955
|
}
|
|
64956
|
+
function filterFreshIncompleteTasks(tasks, taskDir, staleAfterMs) {
|
|
64957
|
+
return tasks.filter((t) => !isTaskStale(join50(taskDir, `${t.id}.json`), staleAfterMs));
|
|
64958
|
+
}
|
|
64970
64959
|
function formatTaskAge(ageMs) {
|
|
64971
64960
|
const hours = Math.floor(ageMs / HOUR_MS);
|
|
64972
64961
|
if (hours >= 24) {
|
|
@@ -64987,6 +64976,17 @@ function getIncompleteTasks(tasks) {
|
|
|
64987
64976
|
return task.blockedBy.every((bid) => byId.get(bid)?.status === "completed");
|
|
64988
64977
|
});
|
|
64989
64978
|
}
|
|
64979
|
+
function dropSubtasksWithResolvedParent(tasks) {
|
|
64980
|
+
const byId = new Map(tasks.map((t) => [t.id, t]));
|
|
64981
|
+
return tasks.filter((task) => {
|
|
64982
|
+
if (!task.parentID)
|
|
64983
|
+
return true;
|
|
64984
|
+
const parent = byId.get(task.parentID);
|
|
64985
|
+
if (!parent)
|
|
64986
|
+
return true;
|
|
64987
|
+
return parent.status !== "completed" && parent.status !== "deleted";
|
|
64988
|
+
});
|
|
64989
|
+
}
|
|
64990
64990
|
function filterTasksBySession(tasks, options) {
|
|
64991
64991
|
if (options.sessionScoped === false)
|
|
64992
64992
|
return tasks;
|
|
@@ -65059,10 +65059,10 @@ async function injectContinuation(args) {
|
|
|
65059
65059
|
if (parsed)
|
|
65060
65060
|
tasks.push(parsed);
|
|
65061
65061
|
}
|
|
65062
|
-
filteredTasks = filterTasksBySession(tasks, {
|
|
65062
|
+
filteredTasks = filterTasksBySession(dropSubtasksWithResolvedParent(tasks), {
|
|
65063
65063
|
sessionID,
|
|
65064
65064
|
subagentIDs: getSubagentSessionIDs(sessionID),
|
|
65065
|
-
sessionScoped: config2
|
|
65065
|
+
sessionScoped: resolveTasksConfig(config2).session_scoped
|
|
65066
65066
|
});
|
|
65067
65067
|
total = filteredTasks.length;
|
|
65068
65068
|
if (total === 0) {
|
|
@@ -65085,9 +65085,10 @@ async function injectContinuation(args) {
|
|
|
65085
65085
|
isBootstrap = true;
|
|
65086
65086
|
stateForBootstrap._bootstrap = undefined;
|
|
65087
65087
|
}
|
|
65088
|
-
const
|
|
65088
|
+
const staleAfterMs = getStaleAfterMs(config2);
|
|
65089
|
+
const freshIncompleteCount = isBootstrap ? 1 : filterFreshIncompleteTasks(getIncompleteTasks(filteredTasks), taskDir, staleAfterMs).length;
|
|
65089
65090
|
if (!isBootstrap && freshIncompleteCount === 0) {
|
|
65090
|
-
log(`[${HOOK_NAME14}] Skipped injection:
|
|
65091
|
+
log(`[${HOOK_NAME14}] Skipped injection: only stale tasks remain`, { sessionID, total });
|
|
65091
65092
|
return;
|
|
65092
65093
|
}
|
|
65093
65094
|
let agentName = resolvedInfo?.agent;
|
|
@@ -65122,14 +65123,14 @@ async function injectContinuation(args) {
|
|
|
65122
65123
|
prompt = BOOTSTRAP_PROMPT;
|
|
65123
65124
|
} else {
|
|
65124
65125
|
const incompleteTasks = getIncompleteTasks(filteredTasks);
|
|
65125
|
-
const
|
|
65126
|
+
const freshIncompleteTasks = filterFreshIncompleteTasks(incompleteTasks, taskDir, staleAfterMs);
|
|
65126
65127
|
const taskList = incompleteTasks.map((task) => {
|
|
65127
|
-
const ageMs = getTaskAgeMs(
|
|
65128
|
+
const ageMs = getTaskAgeMs(join51(taskDir, `${task.id}.json`));
|
|
65128
65129
|
const staleSuffix = ageMs !== null && ageMs > staleAfterMs ? ` (stale: ${formatTaskAge(ageMs)})` : "";
|
|
65129
65130
|
return `- [${task.status}] ${task.subject} (${task.id})${staleSuffix}`;
|
|
65130
65131
|
}).join(`
|
|
65131
65132
|
`);
|
|
65132
|
-
const staleCount = incompleteTasks.
|
|
65133
|
+
const staleCount = incompleteTasks.length - freshIncompleteTasks.length;
|
|
65133
65134
|
const staleNote = staleCount > 0 ? `
|
|
65134
65135
|
|
|
65135
65136
|
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 +65323,10 @@ async function handleSessionIdle(args) {
|
|
|
65322
65323
|
if (parsed)
|
|
65323
65324
|
tasks.push(parsed);
|
|
65324
65325
|
}
|
|
65325
|
-
const filteredTasks = filterTasksBySession(tasks, {
|
|
65326
|
+
const filteredTasks = filterTasksBySession(dropSubtasksWithResolvedParent(tasks), {
|
|
65326
65327
|
sessionID,
|
|
65327
65328
|
subagentIDs: getSubagentSessionIDs(sessionID),
|
|
65328
|
-
sessionScoped: config2
|
|
65329
|
+
sessionScoped: resolveTasksConfig(config2).session_scoped
|
|
65329
65330
|
});
|
|
65330
65331
|
total = filteredTasks.length;
|
|
65331
65332
|
if (total === 0) {
|
|
@@ -65342,7 +65343,7 @@ async function handleSessionIdle(args) {
|
|
|
65342
65343
|
const incompleteTasks = getIncompleteTasks(filteredTasks);
|
|
65343
65344
|
incompleteCount = incompleteTasks.length;
|
|
65344
65345
|
const staleAfterMs = getStaleAfterMs(config2);
|
|
65345
|
-
const staleIncompleteCount = incompleteTasks.filter((t) => isTaskStale(
|
|
65346
|
+
const staleIncompleteCount = incompleteTasks.filter((t) => isTaskStale(join52(taskDir, `${t.id}.json`), staleAfterMs)).length;
|
|
65346
65347
|
const activeIncompleteCount = incompleteCount - staleIncompleteCount;
|
|
65347
65348
|
if (activeIncompleteCount === 0 && staleIncompleteCount > 0) {
|
|
65348
65349
|
log(`[${HOOK_NAME14}] Skipped: only stale tasks remain`, {
|
|
@@ -65790,7 +65791,7 @@ var TODO_FETCH_ERROR_FRAGMENT = "Failed to fetch todos for task-notepad";
|
|
|
65790
65791
|
// src/hooks/task-notepad/hook.ts
|
|
65791
65792
|
init_logger();
|
|
65792
65793
|
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
|
|
65794
|
+
import { dirname as dirname13, join as join53 } from "path";
|
|
65794
65795
|
function createTaskNotepadHook(ctx) {
|
|
65795
65796
|
return {
|
|
65796
65797
|
"tool.execute.after": async (input, output) => {
|
|
@@ -65841,7 +65842,7 @@ async function fetchTodos(ctx, sessionID) {
|
|
|
65841
65842
|
}
|
|
65842
65843
|
}
|
|
65843
65844
|
function resolveActivePlan(directory) {
|
|
65844
|
-
const plansDir =
|
|
65845
|
+
const plansDir = join53(directory, PLAN_DIR_NAME, PLANS_SUBDIR);
|
|
65845
65846
|
if (!existsSync44(plansDir))
|
|
65846
65847
|
return null;
|
|
65847
65848
|
let entries;
|
|
@@ -65854,7 +65855,7 @@ function resolveActivePlan(directory) {
|
|
|
65854
65855
|
for (const entry of entries) {
|
|
65855
65856
|
if (!entry.endsWith(PLAN_EXTENSION))
|
|
65856
65857
|
continue;
|
|
65857
|
-
const fullPath =
|
|
65858
|
+
const fullPath = join53(plansDir, entry);
|
|
65858
65859
|
try {
|
|
65859
65860
|
const stats = statSync9(fullPath);
|
|
65860
65861
|
if (!stats.isFile())
|
|
@@ -65897,7 +65898,7 @@ function appendCompletion(directory, planName, todo) {
|
|
|
65897
65898
|
log(`[${HOOK_NAME16}] Appended completion to notepad`, { plan: planName, taskID: todo.id });
|
|
65898
65899
|
}
|
|
65899
65900
|
function notepadPathFor(directory, planName, taskID) {
|
|
65900
|
-
return
|
|
65901
|
+
return join53(directory, PLAN_DIR_NAME, NOTEPAD_DIR_NAME, planName, TASKS_SUBDIR, `${taskID}${NOTEPAD_EXTENSION}`);
|
|
65901
65902
|
}
|
|
65902
65903
|
function renderNotepad(todo) {
|
|
65903
65904
|
const priority = todo.priority ?? "medium";
|
|
@@ -65963,15 +65964,9 @@ to continue: task(session_id="${sessionId}", prompt="...")`;
|
|
|
65963
65964
|
"tool.execute.after": toolExecuteAfter
|
|
65964
65965
|
};
|
|
65965
65966
|
}
|
|
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
65967
|
// src/hooks/tasks-todowrite-disabler/constants.ts
|
|
65973
65968
|
var BLOCKED_TOOLS2 = ["TodoWrite", "TodoRead"];
|
|
65974
|
-
var REPLACEMENT_MESSAGE = `TodoRead/TodoWrite are DISABLED because
|
|
65969
|
+
var REPLACEMENT_MESSAGE = `TodoRead/TodoWrite are DISABLED because tasks.enabled is true.
|
|
65975
65970
|
|
|
65976
65971
|
**ACTION REQUIRED**: RE-REGISTER what you were about to write as Todo using Task tools NOW. Then ASSIGN yourself and START WORKING immediately.
|
|
65977
65972
|
|
|
@@ -67579,7 +67574,7 @@ function createWebFetchRedirectGuardHook(_ctx) {
|
|
|
67579
67574
|
}
|
|
67580
67575
|
// src/hooks/write-existing-file-guard/hook.ts
|
|
67581
67576
|
import { existsSync as existsSync45 } from "fs";
|
|
67582
|
-
import { isAbsolute as isAbsolute4, join as
|
|
67577
|
+
import { isAbsolute as isAbsolute4, join as join54, normalize, resolve as resolve6, sep } from "path";
|
|
67583
67578
|
function createWriteExistingFileGuardHook(ctx) {
|
|
67584
67579
|
return {
|
|
67585
67580
|
"tool.execute.before": async (input, output) => {
|
|
@@ -67594,7 +67589,7 @@ function createWriteExistingFileGuardHook(ctx) {
|
|
|
67594
67589
|
}
|
|
67595
67590
|
const resolvedPath = normalize(isAbsolute4(filePath) ? filePath : resolve6(ctx.directory, filePath));
|
|
67596
67591
|
if (existsSync45(resolvedPath)) {
|
|
67597
|
-
const matrixxRoot =
|
|
67592
|
+
const matrixxRoot = join54(ctx.directory, ".matrixx") + sep;
|
|
67598
67593
|
const isMatrixMarkdown = resolvedPath.startsWith(matrixxRoot) && resolvedPath.endsWith(".md");
|
|
67599
67594
|
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
67600
67595
|
const isPlansPath = normalizedPath.includes(".matrixx/plans");
|
|
@@ -68040,7 +68035,7 @@ function getLanguageId(ext) {
|
|
|
68040
68035
|
}
|
|
68041
68036
|
// src/tools/lsp/server-installation.ts
|
|
68042
68037
|
import { existsSync as existsSync46 } from "fs";
|
|
68043
|
-
import { join as
|
|
68038
|
+
import { join as join55 } from "path";
|
|
68044
68039
|
function isServerInstalled(command) {
|
|
68045
68040
|
if (command.length === 0)
|
|
68046
68041
|
return false;
|
|
@@ -68068,23 +68063,23 @@ function isServerInstalled(command) {
|
|
|
68068
68063
|
const paths = pathEnv.split(pathSeparator);
|
|
68069
68064
|
for (const p of paths) {
|
|
68070
68065
|
for (const suffix of exts) {
|
|
68071
|
-
if (existsSync46(
|
|
68066
|
+
if (existsSync46(join55(p, cmd + suffix))) {
|
|
68072
68067
|
return true;
|
|
68073
68068
|
}
|
|
68074
68069
|
}
|
|
68075
68070
|
}
|
|
68076
68071
|
const cwd = process.cwd();
|
|
68077
68072
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
68078
|
-
const dataDir =
|
|
68073
|
+
const dataDir = join55(getDataDir(), "opencode");
|
|
68079
68074
|
const additionalBases = [
|
|
68080
|
-
|
|
68081
|
-
|
|
68082
|
-
|
|
68083
|
-
|
|
68075
|
+
join55(cwd, "node_modules", ".bin"),
|
|
68076
|
+
join55(configDir, "bin"),
|
|
68077
|
+
join55(configDir, "node_modules", ".bin"),
|
|
68078
|
+
join55(dataDir, "bin")
|
|
68084
68079
|
];
|
|
68085
68080
|
for (const base of additionalBases) {
|
|
68086
68081
|
for (const suffix of exts) {
|
|
68087
|
-
if (existsSync46(
|
|
68082
|
+
if (existsSync46(join55(base, cmd + suffix))) {
|
|
68088
68083
|
return true;
|
|
68089
68084
|
}
|
|
68090
68085
|
}
|
|
@@ -68096,7 +68091,7 @@ function isServerInstalled(command) {
|
|
|
68096
68091
|
}
|
|
68097
68092
|
// src/tools/lsp/server-config-loader.ts
|
|
68098
68093
|
import { existsSync as existsSync47, readFileSync as readFileSync34 } from "fs";
|
|
68099
|
-
import { join as
|
|
68094
|
+
import { join as join56 } from "path";
|
|
68100
68095
|
function loadJsonFile(path12) {
|
|
68101
68096
|
if (!existsSync47(path12))
|
|
68102
68097
|
return null;
|
|
@@ -68110,9 +68105,9 @@ function getConfigPaths3() {
|
|
|
68110
68105
|
const cwd = process.cwd();
|
|
68111
68106
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
68112
68107
|
return {
|
|
68113
|
-
project: detectConfigFile(
|
|
68114
|
-
user: detectConfigFile(
|
|
68115
|
-
opencode: detectConfigFile(
|
|
68108
|
+
project: detectConfigFile(join56(cwd, ".opencode", "matrixx")).path,
|
|
68109
|
+
user: detectConfigFile(join56(configDir, "matrixx")).path,
|
|
68110
|
+
opencode: detectConfigFile(join56(configDir, "opencode")).path
|
|
68116
68111
|
};
|
|
68117
68112
|
}
|
|
68118
68113
|
function loadAllConfigs() {
|
|
@@ -68961,7 +68956,7 @@ class LSPServerManager {
|
|
|
68961
68956
|
var lspManager = LSPServerManager.getInstance();
|
|
68962
68957
|
// src/tools/lsp/lsp-client-wrapper.ts
|
|
68963
68958
|
import { existsSync as existsSync49, statSync as statSync11 } from "fs";
|
|
68964
|
-
import { dirname as dirname14, extname as extname3, join as
|
|
68959
|
+
import { dirname as dirname14, extname as extname3, join as join57, resolve as resolve8 } from "path";
|
|
68965
68960
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
68966
68961
|
function findWorkspaceRoot(filePath) {
|
|
68967
68962
|
let dir = resolve8(filePath);
|
|
@@ -68977,7 +68972,7 @@ function findWorkspaceRoot(filePath) {
|
|
|
68977
68972
|
let prevDir = "";
|
|
68978
68973
|
while (dir !== prevDir) {
|
|
68979
68974
|
for (const marker of markers) {
|
|
68980
|
-
if (existsSync49(
|
|
68975
|
+
if (existsSync49(join57(dir, marker))) {
|
|
68981
68976
|
return dir;
|
|
68982
68977
|
}
|
|
68983
68978
|
}
|
|
@@ -81829,13 +81824,13 @@ var DEFAULT_MAX_MATCHES = 500;
|
|
|
81829
81824
|
// src/tools/ast-grep/sg-cli-path.ts
|
|
81830
81825
|
import { existsSync as existsSync51, statSync as statSync12 } from "fs";
|
|
81831
81826
|
import { createRequire as createRequire4 } from "module";
|
|
81832
|
-
import { dirname as dirname15, join as
|
|
81827
|
+
import { dirname as dirname15, join as join59 } from "path";
|
|
81833
81828
|
|
|
81834
81829
|
// src/tools/ast-grep/downloader.ts
|
|
81835
81830
|
import { existsSync as existsSync50 } from "fs";
|
|
81836
81831
|
import { createRequire as createRequire3 } from "module";
|
|
81837
81832
|
import { homedir as homedir8 } from "os";
|
|
81838
|
-
import { join as
|
|
81833
|
+
import { join as join58 } from "path";
|
|
81839
81834
|
init_logger();
|
|
81840
81835
|
var REPO2 = "ast-grep/ast-grep";
|
|
81841
81836
|
var DEFAULT_VERSION = "0.40.0";
|
|
@@ -81860,12 +81855,12 @@ var PLATFORM_MAP2 = {
|
|
|
81860
81855
|
function getCacheDir3() {
|
|
81861
81856
|
if (process.platform === "win32") {
|
|
81862
81857
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
81863
|
-
const base2 = localAppData ||
|
|
81864
|
-
return
|
|
81858
|
+
const base2 = localAppData || join58(homedir8(), "AppData", "Local");
|
|
81859
|
+
return join58(base2, "matrixx", "bin");
|
|
81865
81860
|
}
|
|
81866
81861
|
const xdgCache = process.env.XDG_CACHE_HOME;
|
|
81867
|
-
const base = xdgCache ||
|
|
81868
|
-
return
|
|
81862
|
+
const base = xdgCache || join58(homedir8(), ".cache");
|
|
81863
|
+
return join58(base, "matrixx", "bin");
|
|
81869
81864
|
}
|
|
81870
81865
|
function getBinaryName3() {
|
|
81871
81866
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
@@ -81882,7 +81877,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
81882
81877
|
}
|
|
81883
81878
|
const cacheDir = getCacheDir3();
|
|
81884
81879
|
const binaryName = getBinaryName3();
|
|
81885
|
-
const binaryPath =
|
|
81880
|
+
const binaryPath = join58(cacheDir, binaryName);
|
|
81886
81881
|
if (existsSync50(binaryPath)) {
|
|
81887
81882
|
return binaryPath;
|
|
81888
81883
|
}
|
|
@@ -81891,7 +81886,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
81891
81886
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version3}/${assetName}`;
|
|
81892
81887
|
log(`[matrixx] Downloading ast-grep binary...`);
|
|
81893
81888
|
try {
|
|
81894
|
-
const archivePath =
|
|
81889
|
+
const archivePath = join58(cacheDir, assetName);
|
|
81895
81890
|
ensureCacheDir(cacheDir);
|
|
81896
81891
|
await downloadArchive(downloadUrl, archivePath);
|
|
81897
81892
|
await extractZipArchive(archivePath, cacheDir);
|
|
@@ -81945,7 +81940,7 @@ function findSgCliPathSync() {
|
|
|
81945
81940
|
const require2 = createRequire4(import.meta.url);
|
|
81946
81941
|
const cliPackageJsonPath = require2.resolve("@ast-grep/cli/package.json");
|
|
81947
81942
|
const cliDirectory = dirname15(cliPackageJsonPath);
|
|
81948
|
-
const sgPath =
|
|
81943
|
+
const sgPath = join59(cliDirectory, binaryName);
|
|
81949
81944
|
if (existsSync51(sgPath) && isValidBinary(sgPath)) {
|
|
81950
81945
|
return sgPath;
|
|
81951
81946
|
}
|
|
@@ -81957,7 +81952,7 @@ function findSgCliPathSync() {
|
|
|
81957
81952
|
const packageJsonPath = require2.resolve(`${platformPackage}/package.json`);
|
|
81958
81953
|
const packageDirectory = dirname15(packageJsonPath);
|
|
81959
81954
|
const astGrepBinaryName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
81960
|
-
const binaryPath =
|
|
81955
|
+
const binaryPath = join59(packageDirectory, astGrepBinaryName);
|
|
81961
81956
|
if (existsSync51(binaryPath) && isValidBinary(binaryPath)) {
|
|
81962
81957
|
return binaryPath;
|
|
81963
81958
|
}
|
|
@@ -84388,17 +84383,17 @@ var {spawn: spawn10 } = globalThis.Bun;
|
|
|
84388
84383
|
// src/tools/grep/constants.ts
|
|
84389
84384
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
84390
84385
|
import { existsSync as existsSync58 } from "fs";
|
|
84391
|
-
import { dirname as dirname16, join as
|
|
84386
|
+
import { dirname as dirname16, join as join61 } from "path";
|
|
84392
84387
|
|
|
84393
84388
|
// src/tools/grep/downloader.ts
|
|
84394
84389
|
import { existsSync as existsSync57, readdirSync as readdirSync18 } from "fs";
|
|
84395
|
-
import { join as
|
|
84390
|
+
import { join as join60 } from "path";
|
|
84396
84391
|
function findFileRecursive(dir, filename) {
|
|
84397
84392
|
try {
|
|
84398
84393
|
const entries = readdirSync18(dir, { withFileTypes: true, recursive: true });
|
|
84399
84394
|
for (const entry of entries) {
|
|
84400
84395
|
if (entry.isFile() && entry.name === filename) {
|
|
84401
|
-
return
|
|
84396
|
+
return join60(entry.parentPath ?? dir, entry.name);
|
|
84402
84397
|
}
|
|
84403
84398
|
}
|
|
84404
84399
|
} catch {
|
|
@@ -84419,11 +84414,11 @@ function getPlatformKey() {
|
|
|
84419
84414
|
}
|
|
84420
84415
|
function getInstallDir() {
|
|
84421
84416
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
84422
|
-
return
|
|
84417
|
+
return join60(homeDir, ".cache", "matrixx", "bin");
|
|
84423
84418
|
}
|
|
84424
84419
|
function getRgPath() {
|
|
84425
84420
|
const isWindows = process.platform === "win32";
|
|
84426
|
-
return
|
|
84421
|
+
return join60(getInstallDir(), isWindows ? "rg.exe" : "rg");
|
|
84427
84422
|
}
|
|
84428
84423
|
async function extractTarGz2(archivePath, destDir) {
|
|
84429
84424
|
const platformKey = getPlatformKey();
|
|
@@ -84440,7 +84435,7 @@ async function extractZip2(archivePath, destDir) {
|
|
|
84440
84435
|
const binaryName = process.platform === "win32" ? "rg.exe" : "rg";
|
|
84441
84436
|
const foundPath = findFileRecursive(destDir, binaryName);
|
|
84442
84437
|
if (foundPath) {
|
|
84443
|
-
const destPath =
|
|
84438
|
+
const destPath = join60(destDir, binaryName);
|
|
84444
84439
|
if (foundPath !== destPath) {
|
|
84445
84440
|
const { renameSync: renameSync4 } = await import("fs");
|
|
84446
84441
|
renameSync4(foundPath, destPath);
|
|
@@ -84461,7 +84456,7 @@ async function downloadAndInstallRipgrep() {
|
|
|
84461
84456
|
ensureCacheDir(installDir);
|
|
84462
84457
|
const filename = `ripgrep-${RG_VERSION}-${config4.platform}.${config4.extension}`;
|
|
84463
84458
|
const url3 = `https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/${filename}`;
|
|
84464
|
-
const archivePath =
|
|
84459
|
+
const archivePath = join60(installDir, filename);
|
|
84465
84460
|
try {
|
|
84466
84461
|
await downloadArchive(url3, archivePath);
|
|
84467
84462
|
if (config4.extension === "tar.gz") {
|
|
@@ -84506,11 +84501,11 @@ function getOpenCodeBundledRg() {
|
|
|
84506
84501
|
const isWindows = process.platform === "win32";
|
|
84507
84502
|
const rgName = isWindows ? "rg.exe" : "rg";
|
|
84508
84503
|
const candidates = [
|
|
84509
|
-
|
|
84510
|
-
|
|
84511
|
-
|
|
84512
|
-
|
|
84513
|
-
|
|
84504
|
+
join61(getDataDir(), "opencode", "bin", rgName),
|
|
84505
|
+
join61(execDir, rgName),
|
|
84506
|
+
join61(execDir, "bin", rgName),
|
|
84507
|
+
join61(execDir, "..", "bin", rgName),
|
|
84508
|
+
join61(execDir, "..", "libexec", rgName)
|
|
84514
84509
|
];
|
|
84515
84510
|
for (const candidate of candidates) {
|
|
84516
84511
|
if (existsSync58(candidate)) {
|
|
@@ -85018,12 +85013,12 @@ var HandoffSchema = exports_external.object({
|
|
|
85018
85013
|
});
|
|
85019
85014
|
// src/features/handoff/storage.ts
|
|
85020
85015
|
import { existsSync as existsSync59, mkdirSync as mkdirSync17, readFileSync as readFileSync39, renameSync as renameSync4, writeFileSync as writeFileSync22 } from "fs";
|
|
85021
|
-
import { join as
|
|
85016
|
+
import { join as join62 } from "path";
|
|
85022
85017
|
function getHandoffFilePath(directory) {
|
|
85023
|
-
return
|
|
85018
|
+
return join62(directory, HANDOFF_FILE_PATH);
|
|
85024
85019
|
}
|
|
85025
85020
|
function getHandoffConsumedFilePath(directory) {
|
|
85026
|
-
return
|
|
85021
|
+
return join62(directory, HANDOFF_CONSUMED_FILE_PATH);
|
|
85027
85022
|
}
|
|
85028
85023
|
function handoffFileExists(directory) {
|
|
85029
85024
|
try {
|
|
@@ -85061,7 +85056,7 @@ function writeHandoffFile(directory, content) {
|
|
|
85061
85056
|
}
|
|
85062
85057
|
function ensureHandoffDir(directory) {
|
|
85063
85058
|
try {
|
|
85064
|
-
mkdirSync17(
|
|
85059
|
+
mkdirSync17(join62(directory, ".matrixx"), { recursive: true });
|
|
85065
85060
|
return true;
|
|
85066
85061
|
} catch {
|
|
85067
85062
|
return false;
|
|
@@ -85217,13 +85212,13 @@ ${body}
|
|
|
85217
85212
|
|
|
85218
85213
|
// src/tools/handoff/list-handler.ts
|
|
85219
85214
|
import { existsSync as existsSync60, readdirSync as readdirSync19, statSync as statSync14 } from "fs";
|
|
85220
|
-
import { join as
|
|
85215
|
+
import { join as join63 } from "path";
|
|
85221
85216
|
var MATRIX_DIR = ".matrixx";
|
|
85222
85217
|
function isHandoffFile(name) {
|
|
85223
85218
|
return name === "handoff.md" || name.startsWith("handoff.consumed");
|
|
85224
85219
|
}
|
|
85225
85220
|
function handleList(directory) {
|
|
85226
|
-
const matrixxDir =
|
|
85221
|
+
const matrixxDir = join63(directory, MATRIX_DIR);
|
|
85227
85222
|
if (!existsSync60(matrixxDir)) {
|
|
85228
85223
|
return "No handoffs found in .matrixx/";
|
|
85229
85224
|
}
|
|
@@ -85241,7 +85236,7 @@ function handleList(directory) {
|
|
|
85241
85236
|
lines.push(`Handoff files in ${matrixxDir}:`);
|
|
85242
85237
|
lines.push("");
|
|
85243
85238
|
for (const name of handoffs) {
|
|
85244
|
-
const fullPath =
|
|
85239
|
+
const fullPath = join63(matrixxDir, name);
|
|
85245
85240
|
let size = "?";
|
|
85246
85241
|
let mtime = "?";
|
|
85247
85242
|
try {
|
|
@@ -85467,7 +85462,7 @@ The Bash tool can execute these commands directly. Do NOT retry with interactive
|
|
|
85467
85462
|
// src/tools/pdf-extract-figures/tools.ts
|
|
85468
85463
|
import { existsSync as existsSync61, mkdtempSync, writeFileSync as writeFileSync23 } from "fs";
|
|
85469
85464
|
import { tmpdir as tmpdir5 } from "os";
|
|
85470
|
-
import { join as
|
|
85465
|
+
import { join as join64 } from "path";
|
|
85471
85466
|
var PYTHON_SCRIPT = `#!/usr/bin/env python3
|
|
85472
85467
|
import sys
|
|
85473
85468
|
import json
|
|
@@ -85618,11 +85613,11 @@ async function runExtraction(args) {
|
|
|
85618
85613
|
}
|
|
85619
85614
|
let tmpDir;
|
|
85620
85615
|
try {
|
|
85621
|
-
tmpDir = mkdtempSync(
|
|
85616
|
+
tmpDir = mkdtempSync(join64(tmpdir5(), "matrixx-pdf-extract-"));
|
|
85622
85617
|
} catch {
|
|
85623
85618
|
return "Error: Could not create temporary directory.";
|
|
85624
85619
|
}
|
|
85625
|
-
const scriptPath =
|
|
85620
|
+
const scriptPath = join64(tmpDir, "extract_pdf_figures.py");
|
|
85626
85621
|
writeFileSync23(scriptPath, PYTHON_SCRIPT);
|
|
85627
85622
|
const payload = buildArgsPayload(args);
|
|
85628
85623
|
log("[pdf_extract_figures] Running extraction", { file_path: args.file_path, payload });
|
|
@@ -85674,7 +85669,7 @@ import { existsSync as existsSync62 } from "fs";
|
|
|
85674
85669
|
import { basename as basename7 } from "path";
|
|
85675
85670
|
|
|
85676
85671
|
// src/tools/plan/types.ts
|
|
85677
|
-
import { basename as basename6, isAbsolute as isAbsolute5, join as
|
|
85672
|
+
import { basename as basename6, isAbsolute as isAbsolute5, join as join65, relative as relative4, resolve as resolve9 } from "path";
|
|
85678
85673
|
|
|
85679
85674
|
// src/tools/plan/constants.ts
|
|
85680
85675
|
var PLANS_DIR3 = ".matrixx/plans";
|
|
@@ -85700,8 +85695,8 @@ function validatePlanFilePath(filePath, directory) {
|
|
|
85700
85695
|
if (!PLAN_FILENAME_KEBAB_REGEX.test(base)) {
|
|
85701
85696
|
return { error: `fileName must match kebab-case ^[a-z0-9-]+\\.md$: ${base}` };
|
|
85702
85697
|
}
|
|
85703
|
-
const resolved = isAbsolute5(filePath) ? resolve9(filePath) : resolve9(
|
|
85704
|
-
const plansDir = resolve9(
|
|
85698
|
+
const resolved = isAbsolute5(filePath) ? resolve9(filePath) : resolve9(join65(directory, filePath));
|
|
85699
|
+
const plansDir = resolve9(join65(directory, PLANS_DIR3));
|
|
85705
85700
|
const relToPlans = relative4(plansDir, resolved);
|
|
85706
85701
|
if (relToPlans.startsWith("..") || isAbsolute5(relToPlans)) {
|
|
85707
85702
|
return { error: `filePath must be inside ${PLANS_DIR3}: ${filePath}` };
|
|
@@ -85805,7 +85800,7 @@ function createPlanDeleteTool(ctx) {
|
|
|
85805
85800
|
}
|
|
85806
85801
|
// src/tools/plan/plan-list.ts
|
|
85807
85802
|
import { existsSync as existsSync64, readdirSync as readdirSync20, statSync as statSync15 } from "fs";
|
|
85808
|
-
import { join as
|
|
85803
|
+
import { join as join66 } from "path";
|
|
85809
85804
|
function createPlanListTool(ctx) {
|
|
85810
85805
|
return tool({
|
|
85811
85806
|
description: `List plan files under .matrixx/plans/*.md. Filters *.md with kebab-case, sorts by mtime.`,
|
|
@@ -85813,13 +85808,13 @@ function createPlanListTool(ctx) {
|
|
|
85813
85808
|
execute: async (_args, context) => {
|
|
85814
85809
|
try {
|
|
85815
85810
|
const directory = resolveDirectory(context?.directory, ctx?.directory);
|
|
85816
|
-
const plansDir =
|
|
85811
|
+
const plansDir = join66(directory, PLANS_DIR3);
|
|
85817
85812
|
if (!existsSync64(plansDir)) {
|
|
85818
85813
|
return JSON.stringify({ plans: [] });
|
|
85819
85814
|
}
|
|
85820
85815
|
const entries = readdirSync20(plansDir).filter((f) => f.endsWith(".md") && PLAN_FILENAME_KEBAB_REGEX.test(f));
|
|
85821
85816
|
const plans = entries.map((fileName) => {
|
|
85822
|
-
const filePath =
|
|
85817
|
+
const filePath = join66(plansDir, fileName);
|
|
85823
85818
|
try {
|
|
85824
85819
|
const stat2 = statSync15(filePath);
|
|
85825
85820
|
return {
|
|
@@ -86965,10 +86960,152 @@ function createPlanUpdateTool(ctx) {
|
|
|
86965
86960
|
}
|
|
86966
86961
|
});
|
|
86967
86962
|
}
|
|
86963
|
+
// src/tools/preset/tools.ts
|
|
86964
|
+
import { existsSync as existsSync67, mkdirSync as mkdirSync18, readFileSync as readFileSync40, writeFileSync as writeFileSync24 } from "fs";
|
|
86965
|
+
import { homedir as homedir9 } from "os";
|
|
86966
|
+
import { dirname as dirname17, join as join67 } from "path";
|
|
86967
|
+
|
|
86968
|
+
// src/features/preset-state/manager.ts
|
|
86969
|
+
init_logger();
|
|
86970
|
+
var HOOK_NAME19 = "preset-state";
|
|
86971
|
+
var presetBySession = new Map;
|
|
86972
|
+
function setSessionPreset(sessionID, presetName) {
|
|
86973
|
+
presetBySession.set(sessionID, presetName);
|
|
86974
|
+
log(`[${HOOK_NAME19}] Preset set for session`, { sessionID, presetName });
|
|
86975
|
+
}
|
|
86976
|
+
function getSessionPreset(sessionID) {
|
|
86977
|
+
return presetBySession.get(sessionID);
|
|
86978
|
+
}
|
|
86979
|
+
// src/tools/preset/tools.ts
|
|
86980
|
+
init_logger();
|
|
86981
|
+
var ACTIONS = ["list", "show", "set"];
|
|
86982
|
+
var SCOPES = ["global", "project"];
|
|
86983
|
+
function resolveConfigPath(scope, directory) {
|
|
86984
|
+
if (scope === "global") {
|
|
86985
|
+
return join67(homedir9(), ".config", "opencode", "matrixx.jsonc");
|
|
86986
|
+
}
|
|
86987
|
+
return join67(directory ?? process.cwd(), ".opencode", "matrixx.jsonc");
|
|
86988
|
+
}
|
|
86989
|
+
function persistActivePreset(presetName, scope, directory) {
|
|
86990
|
+
const configPath = resolveConfigPath(scope, directory);
|
|
86991
|
+
let raw = {};
|
|
86992
|
+
if (existsSync67(configPath)) {
|
|
86993
|
+
const content = readFileSync40(configPath, "utf-8");
|
|
86994
|
+
const parsed = parseJsoncSafe(content);
|
|
86995
|
+
if (parsed.errors.length > 0) {
|
|
86996
|
+
return {
|
|
86997
|
+
ok: false,
|
|
86998
|
+
error: `Cannot parse existing config at ${configPath}: ${parsed.errors[0]?.message}`
|
|
86999
|
+
};
|
|
87000
|
+
}
|
|
87001
|
+
raw = parsed.data ?? {};
|
|
87002
|
+
} else {
|
|
87003
|
+
mkdirSync18(dirname17(configPath), { recursive: true });
|
|
87004
|
+
}
|
|
87005
|
+
raw.active_preset = presetName;
|
|
87006
|
+
writeFileSync24(configPath, `${JSON.stringify(raw, null, 2)}
|
|
87007
|
+
`);
|
|
87008
|
+
log("[preset] persisted active_preset", { presetName, scope, configPath });
|
|
87009
|
+
return { ok: true, path: configPath };
|
|
87010
|
+
}
|
|
87011
|
+
function unknownPresetError(name, presetNames) {
|
|
87012
|
+
const available = presetNames.length > 0 ? presetNames.join(", ") : "(none defined)";
|
|
87013
|
+
return `Error: Unknown preset "${name}". Available presets: ${available}`;
|
|
87014
|
+
}
|
|
87015
|
+
function formatPreset(name, preset) {
|
|
87016
|
+
const lines = [`Preset "${name}":`];
|
|
87017
|
+
if (preset.default_model) {
|
|
87018
|
+
lines.push(` default_model: ${preset.default_model}`);
|
|
87019
|
+
}
|
|
87020
|
+
const agentEntries = Object.entries(preset.agents ?? {});
|
|
87021
|
+
if (agentEntries.length > 0) {
|
|
87022
|
+
lines.push(" agents:");
|
|
87023
|
+
for (const [agent, entry] of agentEntries) {
|
|
87024
|
+
lines.push(` - ${agent}: ${entry.model}${entry.variant ? ` (variant: ${entry.variant})` : ""}`);
|
|
87025
|
+
}
|
|
87026
|
+
}
|
|
87027
|
+
const categoryEntries = Object.entries(preset.categories ?? {});
|
|
87028
|
+
if (categoryEntries.length > 0) {
|
|
87029
|
+
lines.push(" categories:");
|
|
87030
|
+
for (const [category, entry] of categoryEntries) {
|
|
87031
|
+
lines.push(` - ${category}: ${entry.model}${entry.variant ? ` (variant: ${entry.variant})` : ""}`);
|
|
87032
|
+
}
|
|
87033
|
+
}
|
|
87034
|
+
return lines.join(`
|
|
87035
|
+
`);
|
|
87036
|
+
}
|
|
87037
|
+
function runPresetCommand(input) {
|
|
87038
|
+
const { action, name, save, scope, sessionID, options } = input;
|
|
87039
|
+
const presets = options?.pluginConfig?.model_presets ?? {};
|
|
87040
|
+
const presetNames = Object.keys(presets);
|
|
87041
|
+
const activePreset = options?.pluginConfig?.active_preset;
|
|
87042
|
+
const sessionPreset = getSessionPreset(sessionID);
|
|
87043
|
+
const effective = sessionPreset ?? activePreset;
|
|
87044
|
+
if (action === "list") {
|
|
87045
|
+
if (presetNames.length === 0) {
|
|
87046
|
+
return "No model presets defined. Add a `model_presets` section to your matrixx config.";
|
|
87047
|
+
}
|
|
87048
|
+
const lines = presetNames.map((p) => `- ${p}${p === effective ? " (active)" : ""}`);
|
|
87049
|
+
const activeLine = effective ? `Active: ${effective}${sessionPreset ? ` (session overlay for ${sessionID})` : ""}` : "Active: (none)";
|
|
87050
|
+
return `Available presets:
|
|
87051
|
+
${lines.join(`
|
|
87052
|
+
`)}
|
|
87053
|
+
|
|
87054
|
+
${activeLine}`;
|
|
87055
|
+
}
|
|
87056
|
+
if (action === "show") {
|
|
87057
|
+
const target = name ?? effective;
|
|
87058
|
+
if (!target) {
|
|
87059
|
+
return "No preset specified and no active preset set. Usage: preset show <name>";
|
|
87060
|
+
}
|
|
87061
|
+
const preset = presets[target];
|
|
87062
|
+
if (!preset)
|
|
87063
|
+
return unknownPresetError(target, presetNames);
|
|
87064
|
+
return formatPreset(target, preset);
|
|
87065
|
+
}
|
|
87066
|
+
if (!name) {
|
|
87067
|
+
return "Error: `preset set` requires a preset name. Usage: preset set <name> [--save [--global|--project]]";
|
|
87068
|
+
}
|
|
87069
|
+
if (!presets[name])
|
|
87070
|
+
return unknownPresetError(name, presetNames);
|
|
87071
|
+
setSessionPreset(sessionID, name);
|
|
87072
|
+
let message = `\u2713 Active preset set to "${name}" for this session.
|
|
87073
|
+
|
|
87074
|
+
` + `- delegate-task categories switch immediately
|
|
87075
|
+
` + `- builtin agents apply on next session`;
|
|
87076
|
+
if (save) {
|
|
87077
|
+
const result = persistActivePreset(name, scope, options?.directory);
|
|
87078
|
+
if (!result.ok)
|
|
87079
|
+
return `Error: ${result.error}`;
|
|
87080
|
+
message += `
|
|
87081
|
+
|
|
87082
|
+
Persisted active_preset to ${result.path} (scope: ${scope}). Next session starts on "${name}".`;
|
|
87083
|
+
}
|
|
87084
|
+
return message;
|
|
87085
|
+
}
|
|
87086
|
+
function createPresetTool(options) {
|
|
87087
|
+
const preset = tool({
|
|
87088
|
+
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.",
|
|
87089
|
+
args: {
|
|
87090
|
+
action: tool.schema.enum(ACTIONS).describe("Operation: list available presets, show a preset's details, or set the active preset"),
|
|
87091
|
+
name: tool.schema.string().optional().describe("Preset name (required for set; optional for show \u2014 defaults to the active preset)"),
|
|
87092
|
+
save: tool.schema.boolean().optional().describe("Persist the active preset to config (default: false \u2014 session-only overlay)"),
|
|
87093
|
+
scope: tool.schema.enum(SCOPES).optional().describe("Where to persist when --save: project (.opencode/matrixx.jsonc, default) or global (~/.config/opencode/matrixx.jsonc)")
|
|
87094
|
+
},
|
|
87095
|
+
async execute(args, context) {
|
|
87096
|
+
const action = args.action;
|
|
87097
|
+
const name = args.name;
|
|
87098
|
+
const save = args.save;
|
|
87099
|
+
const scope = args.scope ?? "project";
|
|
87100
|
+
return runPresetCommand({ action, name, save, scope, sessionID: context.sessionID, options });
|
|
87101
|
+
}
|
|
87102
|
+
});
|
|
87103
|
+
return { preset };
|
|
87104
|
+
}
|
|
86968
87105
|
// src/tools/session-manager/constants.ts
|
|
86969
|
-
import { join as
|
|
86970
|
-
var TODO_DIR =
|
|
86971
|
-
var TRANSCRIPT_DIR2 =
|
|
87106
|
+
import { join as join68 } from "path";
|
|
87107
|
+
var TODO_DIR = join68(getOpenCodeConfigDir({ binary: "opencode" }), "todos");
|
|
87108
|
+
var TRANSCRIPT_DIR2 = join68(getOpenCodeConfigDir({ binary: "opencode" }), "transcripts");
|
|
86972
87109
|
var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
|
|
86973
87110
|
|
|
86974
87111
|
Returns a list of available session IDs with metadata including message count, date range, and agents used.
|
|
@@ -87040,9 +87177,9 @@ Agents Used: build, oracle, librarian
|
|
|
87040
87177
|
Has Todos: Yes (12 items, 8 completed)
|
|
87041
87178
|
Has Transcript: Yes (234 entries)`;
|
|
87042
87179
|
// src/tools/session-manager/storage.ts
|
|
87043
|
-
import { existsSync as
|
|
87180
|
+
import { existsSync as existsSync68 } from "fs";
|
|
87044
87181
|
import { readdir, readFile as readFile3 } from "fs/promises";
|
|
87045
|
-
import { join as
|
|
87182
|
+
import { join as join69 } from "path";
|
|
87046
87183
|
var sdkClient = null;
|
|
87047
87184
|
function setStorageClient(client2) {
|
|
87048
87185
|
sdkClient = client2;
|
|
@@ -87061,7 +87198,7 @@ async function getMainSessions(options) {
|
|
|
87061
87198
|
return [];
|
|
87062
87199
|
}
|
|
87063
87200
|
}
|
|
87064
|
-
if (!
|
|
87201
|
+
if (!existsSync68(SESSION_STORAGE))
|
|
87065
87202
|
return [];
|
|
87066
87203
|
const sessions = [];
|
|
87067
87204
|
try {
|
|
@@ -87069,13 +87206,13 @@ async function getMainSessions(options) {
|
|
|
87069
87206
|
for (const projectDir of projectDirs) {
|
|
87070
87207
|
if (!projectDir.isDirectory())
|
|
87071
87208
|
continue;
|
|
87072
|
-
const projectPath =
|
|
87209
|
+
const projectPath = join69(SESSION_STORAGE, projectDir.name);
|
|
87073
87210
|
const sessionFiles = await readdir(projectPath);
|
|
87074
87211
|
for (const file3 of sessionFiles) {
|
|
87075
87212
|
if (!file3.endsWith(".json"))
|
|
87076
87213
|
continue;
|
|
87077
87214
|
try {
|
|
87078
|
-
const content = await readFile3(
|
|
87215
|
+
const content = await readFile3(join69(projectPath, file3), "utf-8");
|
|
87079
87216
|
const meta3 = JSON.parse(content);
|
|
87080
87217
|
if (meta3.parentID)
|
|
87081
87218
|
continue;
|
|
@@ -87100,7 +87237,7 @@ async function getAllSessions() {
|
|
|
87100
87237
|
return [];
|
|
87101
87238
|
}
|
|
87102
87239
|
}
|
|
87103
|
-
if (!
|
|
87240
|
+
if (!existsSync68(MESSAGE_STORAGE))
|
|
87104
87241
|
return [];
|
|
87105
87242
|
const sessions = [];
|
|
87106
87243
|
async function scanDirectory(dir) {
|
|
@@ -87108,7 +87245,7 @@ async function getAllSessions() {
|
|
|
87108
87245
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
87109
87246
|
for (const entry of entries) {
|
|
87110
87247
|
if (entry.isDirectory()) {
|
|
87111
|
-
const sessionPath =
|
|
87248
|
+
const sessionPath = join69(dir, entry.name);
|
|
87112
87249
|
const files = await readdir(sessionPath);
|
|
87113
87250
|
if (files.some((f) => f.endsWith(".json"))) {
|
|
87114
87251
|
sessions.push(entry.name);
|
|
@@ -87169,7 +87306,7 @@ async function readSessionMessages(sessionID) {
|
|
|
87169
87306
|
}
|
|
87170
87307
|
}
|
|
87171
87308
|
const messageDir = getMessageDir(sessionID);
|
|
87172
|
-
if (!messageDir || !
|
|
87309
|
+
if (!messageDir || !existsSync68(messageDir))
|
|
87173
87310
|
return [];
|
|
87174
87311
|
const messages2 = [];
|
|
87175
87312
|
try {
|
|
@@ -87178,7 +87315,7 @@ async function readSessionMessages(sessionID) {
|
|
|
87178
87315
|
if (!file3.endsWith(".json"))
|
|
87179
87316
|
continue;
|
|
87180
87317
|
try {
|
|
87181
|
-
const content = await readFile3(
|
|
87318
|
+
const content = await readFile3(join69(messageDir, file3), "utf-8");
|
|
87182
87319
|
const meta3 = JSON.parse(content);
|
|
87183
87320
|
const parts = await readParts2(meta3.id);
|
|
87184
87321
|
messages2.push({
|
|
@@ -87202,8 +87339,8 @@ async function readSessionMessages(sessionID) {
|
|
|
87202
87339
|
});
|
|
87203
87340
|
}
|
|
87204
87341
|
async function readParts2(messageID) {
|
|
87205
|
-
const partDir =
|
|
87206
|
-
if (!
|
|
87342
|
+
const partDir = join69(PART_STORAGE, messageID);
|
|
87343
|
+
if (!existsSync68(partDir))
|
|
87207
87344
|
return [];
|
|
87208
87345
|
const parts = [];
|
|
87209
87346
|
try {
|
|
@@ -87212,7 +87349,7 @@ async function readParts2(messageID) {
|
|
|
87212
87349
|
if (!file3.endsWith(".json"))
|
|
87213
87350
|
continue;
|
|
87214
87351
|
try {
|
|
87215
|
-
const content = await readFile3(
|
|
87352
|
+
const content = await readFile3(join69(partDir, file3), "utf-8");
|
|
87216
87353
|
parts.push(JSON.parse(content));
|
|
87217
87354
|
} catch {}
|
|
87218
87355
|
}
|
|
@@ -87236,14 +87373,14 @@ async function readSessionTodos(sessionID) {
|
|
|
87236
87373
|
return [];
|
|
87237
87374
|
}
|
|
87238
87375
|
}
|
|
87239
|
-
if (!
|
|
87376
|
+
if (!existsSync68(TODO_DIR))
|
|
87240
87377
|
return [];
|
|
87241
87378
|
try {
|
|
87242
87379
|
const allFiles = await readdir(TODO_DIR);
|
|
87243
87380
|
const todoFiles = allFiles.filter((f) => f.includes(sessionID) && f.endsWith(".json"));
|
|
87244
87381
|
for (const file3 of todoFiles) {
|
|
87245
87382
|
try {
|
|
87246
|
-
const content = await readFile3(
|
|
87383
|
+
const content = await readFile3(join69(TODO_DIR, file3), "utf-8");
|
|
87247
87384
|
const data = JSON.parse(content);
|
|
87248
87385
|
if (Array.isArray(data)) {
|
|
87249
87386
|
return data.map((item) => ({
|
|
@@ -87261,10 +87398,10 @@ async function readSessionTodos(sessionID) {
|
|
|
87261
87398
|
return [];
|
|
87262
87399
|
}
|
|
87263
87400
|
async function readSessionTranscript(sessionID) {
|
|
87264
|
-
if (!
|
|
87401
|
+
if (!existsSync68(TRANSCRIPT_DIR2))
|
|
87265
87402
|
return 0;
|
|
87266
|
-
const transcriptFile =
|
|
87267
|
-
if (!
|
|
87403
|
+
const transcriptFile = join69(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
|
|
87404
|
+
if (!existsSync68(transcriptFile))
|
|
87268
87405
|
return 0;
|
|
87269
87406
|
try {
|
|
87270
87407
|
const content = await readFile3(transcriptFile, "utf-8");
|
|
@@ -87774,20 +87911,20 @@ function createSkillTool(options = {}) {
|
|
|
87774
87911
|
}
|
|
87775
87912
|
var skill = createSkillTool();
|
|
87776
87913
|
// src/tools/slashcommand/command-discovery.ts
|
|
87777
|
-
import { existsSync as
|
|
87778
|
-
import { basename as basename8, join as
|
|
87914
|
+
import { existsSync as existsSync69, readdirSync as readdirSync21, readFileSync as readFileSync41 } from "fs";
|
|
87915
|
+
import { basename as basename8, join as join70 } from "path";
|
|
87779
87916
|
function discoverCommandsFromDir2(commandsDir, scope) {
|
|
87780
|
-
if (!
|
|
87917
|
+
if (!existsSync69(commandsDir))
|
|
87781
87918
|
return [];
|
|
87782
87919
|
const entries = readdirSync21(commandsDir, { withFileTypes: true });
|
|
87783
87920
|
const commands2 = [];
|
|
87784
87921
|
for (const entry of entries) {
|
|
87785
87922
|
if (!isMarkdownFile(entry))
|
|
87786
87923
|
continue;
|
|
87787
|
-
const commandPath =
|
|
87924
|
+
const commandPath = join70(commandsDir, entry.name);
|
|
87788
87925
|
const commandName = basename8(entry.name, ".md");
|
|
87789
87926
|
try {
|
|
87790
|
-
const content =
|
|
87927
|
+
const content = readFileSync41(commandPath, "utf-8");
|
|
87791
87928
|
const { data, body } = parseFrontmatter(content);
|
|
87792
87929
|
const isOpencodeSource = scope === "opencode" || scope === "opencode-project";
|
|
87793
87930
|
const metadata = {
|
|
@@ -87811,8 +87948,8 @@ function discoverCommandsFromDir2(commandsDir, scope) {
|
|
|
87811
87948
|
}
|
|
87812
87949
|
function discoverCommandsSync(directory) {
|
|
87813
87950
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
87814
|
-
const opencodeGlobalDir =
|
|
87815
|
-
const opencodeProjectDir =
|
|
87951
|
+
const opencodeGlobalDir = join70(configDir, "command");
|
|
87952
|
+
const opencodeProjectDir = join70(directory ?? process.cwd(), ".opencode", "command");
|
|
87816
87953
|
const opencodeGlobalCommands = discoverCommandsFromDir2(opencodeGlobalDir, "opencode");
|
|
87817
87954
|
const opencodeProjectCommands = discoverCommandsFromDir2(opencodeProjectDir, "opencode-project");
|
|
87818
87955
|
const builtinCommandsMap = loadBuiltinCommands();
|
|
@@ -87836,7 +87973,7 @@ function discoverCommandsSync(directory) {
|
|
|
87836
87973
|
];
|
|
87837
87974
|
}
|
|
87838
87975
|
// src/tools/slashcommand/command-output-formatter.ts
|
|
87839
|
-
import { dirname as
|
|
87976
|
+
import { dirname as dirname18 } from "path";
|
|
87840
87977
|
async function formatLoadedCommand(command, userMessage) {
|
|
87841
87978
|
const sections = [];
|
|
87842
87979
|
sections.push(`# /${command.name} Command
|
|
@@ -87875,7 +88012,7 @@ async function formatLoadedCommand(command, userMessage) {
|
|
|
87875
88012
|
if (!content && command.lazyContentLoader) {
|
|
87876
88013
|
content = await command.lazyContentLoader.load();
|
|
87877
88014
|
}
|
|
87878
|
-
const commandDir = command.path ?
|
|
88015
|
+
const commandDir = command.path ? dirname18(command.path) : process.cwd();
|
|
87879
88016
|
const withFileReferences = await resolveFileReferencesInText(content, commandDir);
|
|
87880
88017
|
const resolvedContent = await resolveCommandsInText(withFileReferences);
|
|
87881
88018
|
let finalContent = resolvedContent.trim();
|
|
@@ -88954,9 +89091,9 @@ function createAssemblyTool(options) {
|
|
|
88954
89091
|
});
|
|
88955
89092
|
}
|
|
88956
89093
|
// src/tools/dcp-switch-profile/tools.ts
|
|
88957
|
-
import { existsSync as
|
|
88958
|
-
import { homedir as
|
|
88959
|
-
import { join as
|
|
89094
|
+
import { existsSync as existsSync70, writeFileSync as writeFileSync25 } from "fs";
|
|
89095
|
+
import { homedir as homedir10 } from "os";
|
|
89096
|
+
import { join as join71 } from "path";
|
|
88960
89097
|
|
|
88961
89098
|
// src/config/schema/dcp.ts
|
|
88962
89099
|
var DcpCompressOverrideSchema = exports_external.object({
|
|
@@ -89129,11 +89266,11 @@ var DcpConfigSchema = exports_external.object({
|
|
|
89129
89266
|
});
|
|
89130
89267
|
|
|
89131
89268
|
// src/tools/dcp-switch-profile/tools.ts
|
|
89132
|
-
var DCP_PLUGIN_DIR =
|
|
89133
|
-
var DCP_SYMLINK =
|
|
89269
|
+
var DCP_PLUGIN_DIR = join71(homedir10(), ".config", "opencode", "node_modules", "@tarquinen", "opencode-dcp");
|
|
89270
|
+
var DCP_SYMLINK = join71(homedir10(), ".config", "opencode", "dcp.jsonc");
|
|
89134
89271
|
var VALID_PROFILES = ["economy", "balanced", "performance", "ultimate"];
|
|
89135
89272
|
function checkDcpInstalled() {
|
|
89136
|
-
if (!
|
|
89273
|
+
if (!existsSync70(DCP_PLUGIN_DIR)) {
|
|
89137
89274
|
return `DCP is not installed at ${DCP_PLUGIN_DIR}. Install it with: npm install --prefix ~/.config/opencode @tarquinen/opencode-dcp`;
|
|
89138
89275
|
}
|
|
89139
89276
|
return null;
|
|
@@ -89211,7 +89348,7 @@ function switchProfile(profile, options) {
|
|
|
89211
89348
|
if (dcpError)
|
|
89212
89349
|
return dcpError;
|
|
89213
89350
|
const inlineConfig = buildInlineConfig(profile, options);
|
|
89214
|
-
|
|
89351
|
+
writeFileSync25(DCP_SYMLINK, `${JSON.stringify(inlineConfig, null, 2)}
|
|
89215
89352
|
`);
|
|
89216
89353
|
return `\u2713 Switched to DCP profile: ${profile}
|
|
89217
89354
|
|
|
@@ -89557,149 +89694,23 @@ function createDelegateAgent(ctx, backgroundManager, disabledAgents = []) {
|
|
|
89557
89694
|
}
|
|
89558
89695
|
});
|
|
89559
89696
|
}
|
|
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
89697
|
// src/tools/delegate-task/complexity-types.ts
|
|
89683
89698
|
function isDowngradable(level) {
|
|
89684
89699
|
return level === 1 || level === 2;
|
|
89685
89700
|
}
|
|
89686
89701
|
|
|
89687
89702
|
// src/tools/delegate-task/complexity-constants.ts
|
|
89688
|
-
function resolveComplexityModel(category, complexity, originalModel, userDowngrades, config4
|
|
89703
|
+
function resolveComplexityModel(category, complexity, originalModel, userDowngrades, config4) {
|
|
89689
89704
|
if (!isDowngradable(complexity)) {
|
|
89690
89705
|
return { model: originalModel, downgraded: false };
|
|
89691
89706
|
}
|
|
89692
89707
|
let complexityDowngrades;
|
|
89693
|
-
let tiersConfig;
|
|
89694
89708
|
if (config4) {
|
|
89695
89709
|
const maybe = config4;
|
|
89696
|
-
const hasHolderKeys = "complexityDowngrades" in maybe
|
|
89710
|
+
const hasHolderKeys = "complexityDowngrades" in maybe;
|
|
89697
89711
|
if (hasHolderKeys) {
|
|
89698
89712
|
const holder = config4;
|
|
89699
89713
|
complexityDowngrades = holder.complexityDowngrades;
|
|
89700
|
-
if (holder.tiers) {
|
|
89701
|
-
tiersConfig = holder;
|
|
89702
|
-
}
|
|
89703
89714
|
} else {
|
|
89704
89715
|
complexityDowngrades = config4;
|
|
89705
89716
|
}
|
|
@@ -89709,20 +89720,6 @@ function resolveComplexityModel(category, complexity, originalModel, userDowngra
|
|
|
89709
89720
|
if (!rawDowngrade) {
|
|
89710
89721
|
return { model: originalModel, downgraded: false };
|
|
89711
89722
|
}
|
|
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
89723
|
return { model: rawDowngrade, downgraded: true };
|
|
89727
89724
|
}
|
|
89728
89725
|
// src/tools/delegate-task/complexity-scorer.ts
|
|
@@ -89999,14 +89996,14 @@ You are NOT an interactive assistant. You are an autonomous problem-solver.
|
|
|
89999
89996
|
- Report completion with summary of changes made
|
|
90000
89997
|
</Category_Context>`;
|
|
90001
89998
|
var DEFAULT_CATEGORIES = {
|
|
90002
|
-
construct: {
|
|
90003
|
-
source: {
|
|
90004
|
-
"deep-jack": {
|
|
90005
|
-
"matrix-bend": {
|
|
90006
|
-
"bullet-time": {
|
|
90007
|
-
"blue-pill": {
|
|
90008
|
-
"red-pill": {
|
|
90009
|
-
broadcast: {
|
|
89999
|
+
construct: {},
|
|
90000
|
+
source: { variant: "max" },
|
|
90001
|
+
"deep-jack": { variant: "max" },
|
|
90002
|
+
"matrix-bend": {},
|
|
90003
|
+
"bullet-time": {},
|
|
90004
|
+
"blue-pill": {},
|
|
90005
|
+
"red-pill": { variant: "max" },
|
|
90006
|
+
broadcast: {}
|
|
90010
90007
|
};
|
|
90011
90008
|
var CATEGORY_PROMPT_APPENDS = {
|
|
90012
90009
|
construct: VISUAL_CATEGORY_PROMPT_APPEND,
|
|
@@ -90339,93 +90336,10 @@ function isPlanFamily(category) {
|
|
|
90339
90336
|
// src/tools/delegate-task/tools.ts
|
|
90340
90337
|
init_logger();
|
|
90341
90338
|
|
|
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
90339
|
// 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);
|
|
90340
|
+
function mergeCategories(userCategories) {
|
|
90341
|
+
const merged = userCategories ? { ...DEFAULT_CATEGORIES, ...userCategories } : { ...DEFAULT_CATEGORIES };
|
|
90342
|
+
return Object.fromEntries(Object.entries(merged).filter(([, config4]) => !config4.disable));
|
|
90429
90343
|
}
|
|
90430
90344
|
// src/tools/delegate-task/background-continuation.ts
|
|
90431
90345
|
async function executeBackgroundContinuation(args, ctx, executorCtx, parentContext) {
|
|
@@ -90500,6 +90414,9 @@ function getTimingConfig() {
|
|
|
90500
90414
|
SESSION_CONTINUATION_STABILITY_MS
|
|
90501
90415
|
};
|
|
90502
90416
|
}
|
|
90417
|
+
function setPollTimeoutMs(ms) {
|
|
90418
|
+
MAX_POLL_TIME_MS = ms;
|
|
90419
|
+
}
|
|
90503
90420
|
|
|
90504
90421
|
// src/tools/delegate-task/background-task.ts
|
|
90505
90422
|
async function executeBackgroundTask(args, ctx, executorCtx, parentContext, agentToUse, categoryModel, systemContent) {
|
|
@@ -90578,6 +90495,67 @@ session_id: ${sessionId}
|
|
|
90578
90495
|
});
|
|
90579
90496
|
}
|
|
90580
90497
|
}
|
|
90498
|
+
// src/config/preset-applier.ts
|
|
90499
|
+
init_logger();
|
|
90500
|
+
function resolvePresetModel(preset, key) {
|
|
90501
|
+
const entry = preset.agents?.[key] ?? preset.categories?.[key];
|
|
90502
|
+
if (entry)
|
|
90503
|
+
return entry;
|
|
90504
|
+
if (preset.default_model)
|
|
90505
|
+
return { model: preset.default_model };
|
|
90506
|
+
return;
|
|
90507
|
+
}
|
|
90508
|
+
function applyActivePreset(config4, presetName) {
|
|
90509
|
+
const name = presetName ?? config4.active_preset;
|
|
90510
|
+
if (!name)
|
|
90511
|
+
return config4;
|
|
90512
|
+
const preset = config4.model_presets?.[name];
|
|
90513
|
+
if (!preset) {
|
|
90514
|
+
log("[applyActivePreset] unknown active_preset", { active_preset: name });
|
|
90515
|
+
return config4;
|
|
90516
|
+
}
|
|
90517
|
+
const agents = fillRegistry(config4.agents, preset.agents, preset.default_model);
|
|
90518
|
+
const categories = fillRegistry(config4.categories, preset.categories, preset.default_model);
|
|
90519
|
+
const result = { ...config4 };
|
|
90520
|
+
if (agents !== config4.agents)
|
|
90521
|
+
result.agents = agents;
|
|
90522
|
+
if (categories !== config4.categories) {
|
|
90523
|
+
result.categories = categories;
|
|
90524
|
+
}
|
|
90525
|
+
return result;
|
|
90526
|
+
}
|
|
90527
|
+
function fillRegistry(registry4, presetEntries, defaultModel) {
|
|
90528
|
+
if (!registry4)
|
|
90529
|
+
return registry4;
|
|
90530
|
+
let changed = false;
|
|
90531
|
+
const result = {};
|
|
90532
|
+
for (const [name, entry] of Object.entries(registry4)) {
|
|
90533
|
+
if (!entry) {
|
|
90534
|
+
result[name] = entry;
|
|
90535
|
+
continue;
|
|
90536
|
+
}
|
|
90537
|
+
const filled = applyEntry(entry, presetEntries?.[name], defaultModel);
|
|
90538
|
+
if (filled) {
|
|
90539
|
+
changed = true;
|
|
90540
|
+
result[name] = filled;
|
|
90541
|
+
} else {
|
|
90542
|
+
result[name] = entry;
|
|
90543
|
+
}
|
|
90544
|
+
}
|
|
90545
|
+
return changed ? result : registry4;
|
|
90546
|
+
}
|
|
90547
|
+
function applyEntry(entry, presetEntry, defaultModel) {
|
|
90548
|
+
if (entry.model)
|
|
90549
|
+
return null;
|
|
90550
|
+
const model = presetEntry?.model ?? defaultModel;
|
|
90551
|
+
if (!model)
|
|
90552
|
+
return null;
|
|
90553
|
+
const variant = entry.variant ?? presetEntry?.variant;
|
|
90554
|
+
if (variant === undefined)
|
|
90555
|
+
return { ...entry, model };
|
|
90556
|
+
return { ...entry, model, variant };
|
|
90557
|
+
}
|
|
90558
|
+
|
|
90581
90559
|
// src/tools/delegate-task/category-resolver.ts
|
|
90582
90560
|
init_logger();
|
|
90583
90561
|
|
|
@@ -90635,7 +90613,7 @@ async function getAvailableModelsForDelegateTask(client2) {
|
|
|
90635
90613
|
// src/tools/delegate-task/categories.ts
|
|
90636
90614
|
init_logger();
|
|
90637
90615
|
function resolveCategoryConfig(categoryName, options) {
|
|
90638
|
-
const { userCategories, inheritedModel: _inheritedModel, systemDefaultModel, availableModels,
|
|
90616
|
+
const { userCategories, inheritedModel: _inheritedModel, systemDefaultModel, availableModels, modelRequirements, presetEntry } = options;
|
|
90639
90617
|
const defaultConfig = DEFAULT_CATEGORIES[categoryName];
|
|
90640
90618
|
const userConfig = userCategories?.[categoryName];
|
|
90641
90619
|
const hasExplicitUserConfig = userConfig !== undefined;
|
|
@@ -90654,16 +90632,13 @@ function resolveCategoryConfig(categoryName, options) {
|
|
|
90654
90632
|
if (!defaultConfig && !userConfig) {
|
|
90655
90633
|
return null;
|
|
90656
90634
|
}
|
|
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;
|
|
90635
|
+
const model = normalizeModel(userConfig?.model) ?? normalizeModel(presetEntry?.model) ?? normalizeModel(defaultConfig?.model) ?? systemDefaultModel;
|
|
90636
|
+
const hasUserModelOverride = (userConfig?.model ?? presetEntry?.model) !== undefined;
|
|
90662
90637
|
const config4 = {
|
|
90663
90638
|
...defaultConfig,
|
|
90664
90639
|
...userConfig,
|
|
90665
90640
|
model,
|
|
90666
|
-
variant: userConfig?.variant ?? (hasUserModelOverride ? undefined : defaultConfig?.variant)
|
|
90641
|
+
variant: userConfig?.variant ?? presetEntry?.variant ?? (hasUserModelOverride ? undefined : defaultConfig?.variant)
|
|
90667
90642
|
};
|
|
90668
90643
|
let promptAppend = defaultPromptAppend;
|
|
90669
90644
|
if (userConfig?.prompt_append) {
|
|
@@ -90673,12 +90648,6 @@ ${userConfig.prompt_append}` : userConfig.prompt_append;
|
|
|
90673
90648
|
}
|
|
90674
90649
|
return { config: config4, promptAppend, model, temperature: config4.temperature };
|
|
90675
90650
|
}
|
|
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
90651
|
|
|
90683
90652
|
// src/tools/delegate-task/model-selection.ts
|
|
90684
90653
|
function normalizeModel2(model) {
|
|
@@ -90737,11 +90706,33 @@ function resolveModelForDelegateTask(input) {
|
|
|
90737
90706
|
var MOUSE_AGENT2 = "mouse";
|
|
90738
90707
|
|
|
90739
90708
|
// src/tools/delegate-task/category-resolver.ts
|
|
90709
|
+
function resolveCategoryPresetEntry(categoryName, ctx) {
|
|
90710
|
+
const { sessionID, modelPresets, activePreset } = ctx;
|
|
90711
|
+
if (!modelPresets)
|
|
90712
|
+
return;
|
|
90713
|
+
const overlayName = sessionID ? getSessionPreset(sessionID) : undefined;
|
|
90714
|
+
let result;
|
|
90715
|
+
if (overlayName) {
|
|
90716
|
+
const overlayPreset = modelPresets[overlayName];
|
|
90717
|
+
if (overlayPreset) {
|
|
90718
|
+
const entry = resolvePresetModel(overlayPreset, categoryName);
|
|
90719
|
+
if (entry)
|
|
90720
|
+
result = entry;
|
|
90721
|
+
} else {
|
|
90722
|
+
log("[preset] unknown session overlay preset, ignoring", { sessionID, presetName: overlayName });
|
|
90723
|
+
}
|
|
90724
|
+
}
|
|
90725
|
+
if (!result && activePreset) {
|
|
90726
|
+
const preset = modelPresets[activePreset];
|
|
90727
|
+
if (preset)
|
|
90728
|
+
result = resolvePresetModel(preset, categoryName);
|
|
90729
|
+
}
|
|
90730
|
+
return result;
|
|
90731
|
+
}
|
|
90740
90732
|
async function resolveCategoryExecution(args, executorCtx, inheritedModel, systemDefaultModel) {
|
|
90741
|
-
const { client: client2, userCategories, mouseModel, globalModel, modelRequirements, complexityDowngrades
|
|
90733
|
+
const { client: client2, userCategories, mouseModel, globalModel, modelRequirements, complexityDowngrades } = executorCtx;
|
|
90742
90734
|
const categoryRequirements = getCategoryModelRequirements(modelRequirements ? { modelRequirements } : undefined);
|
|
90743
90735
|
const availableModels = await getAvailableModelsForDelegateTask(client2);
|
|
90744
|
-
const connectedProviders = readConnectedProvidersCache();
|
|
90745
90736
|
const categoryName = args.category;
|
|
90746
90737
|
const enabledCategories = mergeCategories(userCategories);
|
|
90747
90738
|
const categoryExists = enabledCategories[categoryName] !== undefined;
|
|
@@ -90750,8 +90741,8 @@ async function resolveCategoryExecution(args, executorCtx, inheritedModel, syste
|
|
|
90750
90741
|
inheritedModel,
|
|
90751
90742
|
systemDefaultModel,
|
|
90752
90743
|
availableModels,
|
|
90753
|
-
|
|
90754
|
-
|
|
90744
|
+
modelRequirements,
|
|
90745
|
+
presetEntry: resolveCategoryPresetEntry(categoryName, executorCtx)
|
|
90755
90746
|
});
|
|
90756
90747
|
if (!resolved) {
|
|
90757
90748
|
const requirement2 = categoryRequirements[categoryName];
|
|
@@ -90847,9 +90838,7 @@ Available categories: ${allCategoryNames}`
|
|
|
90847
90838
|
}
|
|
90848
90839
|
if (actualModel && (complexityLevel === 1 || complexityLevel === 2)) {
|
|
90849
90840
|
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);
|
|
90841
|
+
const resolvedDowngrade = resolveComplexityModel(args.category, complexityLevel, actualModel, userDowngrades, complexityDowngrades);
|
|
90853
90842
|
if (resolvedDowngrade.downgraded) {
|
|
90854
90843
|
complexityDowngraded = true;
|
|
90855
90844
|
log("[complexity] Auto-downgraded model", {
|
|
@@ -91861,7 +91850,7 @@ session_id: ${sessionID}
|
|
|
91861
91850
|
});
|
|
91862
91851
|
} finally {
|
|
91863
91852
|
if (syncSessionID) {
|
|
91864
|
-
|
|
91853
|
+
unregisterSubagentSession(syncSessionID);
|
|
91865
91854
|
client2.session.abort({ path: { id: syncSessionID } }).catch((err) => {
|
|
91866
91855
|
log("[delegate-task] Sync session abort failed:", err);
|
|
91867
91856
|
});
|
|
@@ -92190,7 +92179,7 @@ Prompts MUST be in English.`;
|
|
|
92190
92179
|
let actualModel;
|
|
92191
92180
|
let isUnstableAgent = false;
|
|
92192
92181
|
if (args.category) {
|
|
92193
|
-
const resolution = await resolveCategoryExecution(args, options, inheritedModel, systemDefaultModel);
|
|
92182
|
+
const resolution = await resolveCategoryExecution(args, { ...options, sessionID: ctx.sessionID }, inheritedModel, systemDefaultModel);
|
|
92194
92183
|
if (resolution.error) {
|
|
92195
92184
|
return resolution.error;
|
|
92196
92185
|
}
|
|
@@ -92591,8 +92580,8 @@ Original error: ${createResult.error}`;
|
|
|
92591
92580
|
});
|
|
92592
92581
|
}
|
|
92593
92582
|
// src/tools/task/task-cleanup.ts
|
|
92594
|
-
import { existsSync as
|
|
92595
|
-
import { join as
|
|
92583
|
+
import { existsSync as existsSync71, readdirSync as readdirSync22, readFileSync as readFileSync42, unlinkSync as unlinkSync11 } from "fs";
|
|
92584
|
+
import { join as join72 } from "path";
|
|
92596
92585
|
init_logger();
|
|
92597
92586
|
function parseOlderThan(value) {
|
|
92598
92587
|
const match = value.trim().match(/^(\d+)(d|h|m)$/);
|
|
@@ -92651,17 +92640,17 @@ Returns counts: {deleted, remaining, deletedIds}`,
|
|
|
92651
92640
|
}
|
|
92652
92641
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
92653
92642
|
const taskDir = getTaskDir(config4, directory);
|
|
92654
|
-
if (!
|
|
92643
|
+
if (!existsSync71(taskDir)) {
|
|
92655
92644
|
return JSON.stringify({ deleted: 0, remaining: 0, deletedIds: [] });
|
|
92656
92645
|
}
|
|
92657
92646
|
const files = readdirSync22(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-"));
|
|
92658
92647
|
const total = files.length;
|
|
92659
92648
|
const deletedIds = [];
|
|
92660
92649
|
for (const file3 of files) {
|
|
92661
|
-
const filePath =
|
|
92650
|
+
const filePath = join72(taskDir, file3);
|
|
92662
92651
|
let raw;
|
|
92663
92652
|
try {
|
|
92664
|
-
const content =
|
|
92653
|
+
const content = readFileSync42(filePath, "utf-8");
|
|
92665
92654
|
raw = JSON.parse(content);
|
|
92666
92655
|
} catch {
|
|
92667
92656
|
continue;
|
|
@@ -92694,7 +92683,9 @@ Returns counts: {deleted, remaining, deletedIds}`,
|
|
|
92694
92683
|
});
|
|
92695
92684
|
}
|
|
92696
92685
|
// src/tools/task/task-create.ts
|
|
92697
|
-
import {
|
|
92686
|
+
import { readdirSync as readdirSync23, statSync as statSync17 } from "fs";
|
|
92687
|
+
import { join as join73 } from "path";
|
|
92688
|
+
init_logger();
|
|
92698
92689
|
function createTaskCreateTool(config4, ctx) {
|
|
92699
92690
|
return tool({
|
|
92700
92691
|
description: `Create a new task with auto-generated ID and threadID recording.
|
|
@@ -92736,6 +92727,13 @@ async function handleCreate2(args, config4, ctx, context) {
|
|
|
92736
92727
|
return JSON.stringify({ error: "task_lock_unavailable" });
|
|
92737
92728
|
}
|
|
92738
92729
|
try {
|
|
92730
|
+
const existingTask = findDuplicateTask(taskDir, validatedArgs.subject, directory);
|
|
92731
|
+
if (existingTask) {
|
|
92732
|
+
return JSON.stringify({
|
|
92733
|
+
task: { id: existingTask.id, subject: existingTask.subject },
|
|
92734
|
+
deduplicated: true
|
|
92735
|
+
});
|
|
92736
|
+
}
|
|
92739
92737
|
const taskId = generateTaskId();
|
|
92740
92738
|
const task = {
|
|
92741
92739
|
id: taskId,
|
|
@@ -92752,7 +92750,7 @@ async function handleCreate2(args, config4, ctx, context) {
|
|
|
92752
92750
|
projectRoot: directory
|
|
92753
92751
|
};
|
|
92754
92752
|
const validatedTask = TaskObjectSchema.parse(task);
|
|
92755
|
-
writeJsonAtomic(
|
|
92753
|
+
writeJsonAtomic(join73(taskDir, `${taskId}.json`), validatedTask);
|
|
92756
92754
|
return JSON.stringify({
|
|
92757
92755
|
task: {
|
|
92758
92756
|
id: validatedTask.id,
|
|
@@ -92772,8 +92770,47 @@ async function handleCreate2(args, config4, ctx, context) {
|
|
|
92772
92770
|
return JSON.stringify({ error: "internal_error" });
|
|
92773
92771
|
}
|
|
92774
92772
|
}
|
|
92773
|
+
function findDuplicateTask(taskDir, subject, directory) {
|
|
92774
|
+
let files;
|
|
92775
|
+
try {
|
|
92776
|
+
files = readdirSync23(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-"));
|
|
92777
|
+
} catch (error95) {
|
|
92778
|
+
log(`[task-create] Failed to list task dir ${taskDir} during dedup scan`, error95);
|
|
92779
|
+
return null;
|
|
92780
|
+
}
|
|
92781
|
+
for (const file3 of files) {
|
|
92782
|
+
const filePath = join73(taskDir, file3);
|
|
92783
|
+
let existing;
|
|
92784
|
+
try {
|
|
92785
|
+
existing = readJsonSafe(filePath, TaskObjectSchema);
|
|
92786
|
+
} catch (error95) {
|
|
92787
|
+
log(`[task-create] Failed to read task file ${filePath} during dedup scan`, error95);
|
|
92788
|
+
continue;
|
|
92789
|
+
}
|
|
92790
|
+
if (!existing)
|
|
92791
|
+
continue;
|
|
92792
|
+
const isActive = existing.status === "pending" || existing.status === "in_progress";
|
|
92793
|
+
if (!isActive)
|
|
92794
|
+
continue;
|
|
92795
|
+
if (existing.subject.trim() !== subject.trim())
|
|
92796
|
+
continue;
|
|
92797
|
+
if (existing.projectRoot !== directory)
|
|
92798
|
+
continue;
|
|
92799
|
+
let mtimeMs;
|
|
92800
|
+
try {
|
|
92801
|
+
mtimeMs = statSync17(filePath).mtimeMs;
|
|
92802
|
+
} catch (error95) {
|
|
92803
|
+
log(`[task-create] Failed to stat task file ${filePath} during dedup scan`, error95);
|
|
92804
|
+
continue;
|
|
92805
|
+
}
|
|
92806
|
+
if (Date.now() - mtimeMs > DEDUP_WINDOW_MS)
|
|
92807
|
+
continue;
|
|
92808
|
+
return existing;
|
|
92809
|
+
}
|
|
92810
|
+
return null;
|
|
92811
|
+
}
|
|
92775
92812
|
// src/tools/task/task-get.ts
|
|
92776
|
-
import { join as
|
|
92813
|
+
import { join as join74 } from "path";
|
|
92777
92814
|
function parseTaskId(id) {
|
|
92778
92815
|
if (!TASK_ID_PATTERN.test(id))
|
|
92779
92816
|
return null;
|
|
@@ -92798,7 +92835,7 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
92798
92835
|
}
|
|
92799
92836
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
92800
92837
|
const taskDir = getTaskDir(config4, directory);
|
|
92801
|
-
const taskPath =
|
|
92838
|
+
const taskPath = join74(taskDir, `${taskId}.json`);
|
|
92802
92839
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
92803
92840
|
return JSON.stringify({ task: task ?? null });
|
|
92804
92841
|
} catch (error95) {
|
|
@@ -92811,35 +92848,42 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
92811
92848
|
});
|
|
92812
92849
|
}
|
|
92813
92850
|
// src/tools/task/task-list.ts
|
|
92814
|
-
import { existsSync as
|
|
92815
|
-
import { join as
|
|
92851
|
+
import { existsSync as existsSync72, readdirSync as readdirSync24 } from "fs";
|
|
92852
|
+
import { join as join75 } from "path";
|
|
92816
92853
|
function createTaskList(config4, ctx) {
|
|
92817
92854
|
return tool({
|
|
92818
92855
|
description: `List all active tasks with summary information.
|
|
92819
92856
|
|
|
92820
92857
|
Returns tasks excluding completed and deleted statuses by default.
|
|
92821
92858
|
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
|
-
|
|
92859
|
+
Returns summary format: id, subject, status, owner, blockedBy (not full description).
|
|
92860
|
+
|
|
92861
|
+
Optional \`parentID\` filter: when provided, returns only tasks whose parentID matches
|
|
92862
|
+
(i.e. the subtasks of that parent task).`,
|
|
92863
|
+
args: {
|
|
92864
|
+
parentID: tool.schema.string().optional().describe("Filter to subtasks of this parent task ID")
|
|
92865
|
+
},
|
|
92866
|
+
execute: async (args, context) => {
|
|
92867
|
+
const parentID = args.parentID;
|
|
92825
92868
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
92826
92869
|
const taskDir = getTaskDir(config4, directory);
|
|
92827
|
-
if (!
|
|
92870
|
+
if (!existsSync72(taskDir)) {
|
|
92828
92871
|
return JSON.stringify({ tasks: [] });
|
|
92829
92872
|
}
|
|
92830
|
-
const files =
|
|
92873
|
+
const files = readdirSync24(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-")).map((f) => f.replace(".json", ""));
|
|
92831
92874
|
if (files.length === 0) {
|
|
92832
92875
|
return JSON.stringify({ tasks: [] });
|
|
92833
92876
|
}
|
|
92834
92877
|
const allTasks = [];
|
|
92835
92878
|
for (const fileId of files) {
|
|
92836
|
-
const task = readJsonSafe(
|
|
92879
|
+
const task = readJsonSafe(join75(taskDir, `${fileId}.json`), TaskObjectSchema);
|
|
92837
92880
|
if (task) {
|
|
92838
92881
|
allTasks.push(task);
|
|
92839
92882
|
}
|
|
92840
92883
|
}
|
|
92841
92884
|
const activeTasks = allTasks.filter((task) => task.status !== "completed" && task.status !== "deleted");
|
|
92842
|
-
const
|
|
92885
|
+
const filteredTasks = parentID ? activeTasks.filter((task) => task.parentID === parentID) : activeTasks;
|
|
92886
|
+
const summaries = filteredTasks.map((task) => {
|
|
92843
92887
|
const unresolvedBlockers = task.blockedBy.filter((blockerId) => {
|
|
92844
92888
|
const blockerTask = allTasks.find((t) => t.id === blockerId);
|
|
92845
92889
|
return blockerTask?.status !== "completed";
|
|
@@ -92849,7 +92893,8 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
|
|
|
92849
92893
|
subject: task.subject,
|
|
92850
92894
|
status: task.status,
|
|
92851
92895
|
owner: task.owner,
|
|
92852
|
-
blockedBy: unresolvedBlockers
|
|
92896
|
+
blockedBy: unresolvedBlockers,
|
|
92897
|
+
parentID: task.parentID
|
|
92853
92898
|
};
|
|
92854
92899
|
});
|
|
92855
92900
|
return JSON.stringify({
|
|
@@ -92860,7 +92905,7 @@ Returns summary format: id, subject, status, owner, blockedBy (not full descript
|
|
|
92860
92905
|
});
|
|
92861
92906
|
}
|
|
92862
92907
|
// src/tools/task/task-update.ts
|
|
92863
|
-
import { join as
|
|
92908
|
+
import { join as join76 } from "path";
|
|
92864
92909
|
function parseTaskId2(id) {
|
|
92865
92910
|
if (!TASK_ID_PATTERN.test(id))
|
|
92866
92911
|
return null;
|
|
@@ -92906,7 +92951,7 @@ async function handleUpdate(args, config4, ctx, context) {
|
|
|
92906
92951
|
return JSON.stringify({ error: "task_lock_unavailable" });
|
|
92907
92952
|
}
|
|
92908
92953
|
try {
|
|
92909
|
-
const taskPath =
|
|
92954
|
+
const taskPath = join76(taskDir, `${taskId}.json`);
|
|
92910
92955
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
92911
92956
|
if (!task) {
|
|
92912
92957
|
return JSON.stringify({ error: "task_not_found" });
|
|
@@ -93559,19 +93604,19 @@ function detectRepetitiveToolUse(window2) {
|
|
|
93559
93604
|
}
|
|
93560
93605
|
|
|
93561
93606
|
// src/features/background-agent/message-dir.ts
|
|
93562
|
-
import { existsSync as
|
|
93563
|
-
import { join as
|
|
93607
|
+
import { existsSync as existsSync73, readdirSync as readdirSync25, readFileSync as readFileSync43 } from "fs";
|
|
93608
|
+
import { join as join77 } from "path";
|
|
93564
93609
|
var MESSAGE_DIR_CACHE_CAP = 1000;
|
|
93565
93610
|
var messageDirCache = new Map;
|
|
93566
93611
|
function computeMessageDir(sessionID) {
|
|
93567
|
-
if (!
|
|
93612
|
+
if (!existsSync73(MESSAGE_STORAGE))
|
|
93568
93613
|
return null;
|
|
93569
|
-
const directPath =
|
|
93570
|
-
if (
|
|
93614
|
+
const directPath = join77(MESSAGE_STORAGE, sessionID);
|
|
93615
|
+
if (existsSync73(directPath))
|
|
93571
93616
|
return directPath;
|
|
93572
|
-
for (const dir of
|
|
93573
|
-
const sessionPath =
|
|
93574
|
-
if (
|
|
93617
|
+
for (const dir of readdirSync25(MESSAGE_STORAGE)) {
|
|
93618
|
+
const sessionPath = join77(MESSAGE_STORAGE, dir, sessionID);
|
|
93619
|
+
if (existsSync73(sessionPath))
|
|
93575
93620
|
return sessionPath;
|
|
93576
93621
|
}
|
|
93577
93622
|
return null;
|
|
@@ -93605,11 +93650,11 @@ function hasPartialAgentOrModel(message) {
|
|
|
93605
93650
|
}
|
|
93606
93651
|
function findNearestMessageExcludingCompaction(messageDir) {
|
|
93607
93652
|
try {
|
|
93608
|
-
const files =
|
|
93653
|
+
const files = readdirSync25(messageDir).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
93609
93654
|
const parsedByFile = new Map;
|
|
93610
93655
|
for (const file3 of files) {
|
|
93611
93656
|
try {
|
|
93612
|
-
const content =
|
|
93657
|
+
const content = readFileSync43(join77(messageDir, file3), "utf-8");
|
|
93613
93658
|
const parsed = JSON.parse(content);
|
|
93614
93659
|
parsedByFile.set(file3, parsed);
|
|
93615
93660
|
if (hasFullAgentAndModel(parsed)) {
|
|
@@ -94813,7 +94858,7 @@ class BackgroundManager {
|
|
|
94813
94858
|
}
|
|
94814
94859
|
this.tasks.delete(taskId);
|
|
94815
94860
|
if (task.sessionID) {
|
|
94816
|
-
|
|
94861
|
+
unregisterSubagentSession(task.sessionID);
|
|
94817
94862
|
}
|
|
94818
94863
|
}
|
|
94819
94864
|
}
|
|
@@ -95791,9 +95836,9 @@ class TmuxSessionManager {
|
|
|
95791
95836
|
var SESSION_TIMEOUT_MS3 = 10 * 60 * 1000;
|
|
95792
95837
|
var MIN_STABILITY_TIME_MS4 = 10 * 1000;
|
|
95793
95838
|
// src/agents/dynamic-agent-prompt-builder.ts
|
|
95794
|
-
import { existsSync as
|
|
95795
|
-
import { homedir as
|
|
95796
|
-
import { join as
|
|
95839
|
+
import { existsSync as existsSync74, readFileSync as readFileSync44 } from "fs";
|
|
95840
|
+
import { homedir as homedir11 } from "os";
|
|
95841
|
+
import { join as join78 } from "path";
|
|
95797
95842
|
init_logger();
|
|
95798
95843
|
function categorizeTools(toolNames) {
|
|
95799
95844
|
return toolNames.map((name) => {
|
|
@@ -96155,24 +96200,24 @@ function exploreCtxPart(hasGrepGlob) {
|
|
|
96155
96200
|
function resolveContextModeDisciplinePath() {
|
|
96156
96201
|
try {
|
|
96157
96202
|
const resolved = __require.resolve("context-mode/configs/opencode/AGENTS.md");
|
|
96158
|
-
if (
|
|
96203
|
+
if (existsSync74(resolved))
|
|
96159
96204
|
return resolved;
|
|
96160
96205
|
} catch {}
|
|
96161
96206
|
try {
|
|
96162
96207
|
const cacheDir = getOpenCodeCacheDir();
|
|
96163
|
-
const p =
|
|
96164
|
-
if (
|
|
96208
|
+
const p = join78(cacheDir, "packages/context-mode@latest/node_modules/context-mode/configs/opencode/AGENTS.md");
|
|
96209
|
+
if (existsSync74(p))
|
|
96165
96210
|
return p;
|
|
96166
96211
|
} catch {}
|
|
96167
96212
|
try {
|
|
96168
|
-
const base = process.env.XDG_CACHE_HOME ??
|
|
96169
|
-
const p =
|
|
96170
|
-
if (
|
|
96213
|
+
const base = process.env.XDG_CACHE_HOME ?? join78(homedir11(), ".cache");
|
|
96214
|
+
const p = join78(base, "opencode/packages/context-mode@latest/node_modules/context-mode/configs/opencode/AGENTS.md");
|
|
96215
|
+
if (existsSync74(p))
|
|
96171
96216
|
return p;
|
|
96172
96217
|
} catch {}
|
|
96173
96218
|
try {
|
|
96174
|
-
const p2 =
|
|
96175
|
-
if (
|
|
96219
|
+
const p2 = join78(homedir11(), ".cache/opencode/packages/context-mode@latest/node_modules/context-mode/configs/opencode/AGENTS.md");
|
|
96220
|
+
if (existsSync74(p2))
|
|
96176
96221
|
return p2;
|
|
96177
96222
|
} catch {}
|
|
96178
96223
|
return null;
|
|
@@ -96190,11 +96235,11 @@ function loadContextModeDiscipline(hasGrepGlob = true) {
|
|
|
96190
96235
|
const p = resolveContextModeDisciplinePath();
|
|
96191
96236
|
if (!p)
|
|
96192
96237
|
throw new Error("discipline file not found");
|
|
96193
|
-
const raw =
|
|
96238
|
+
const raw = readFileSync44(p, "utf8");
|
|
96194
96239
|
let version4 = "1.0.169";
|
|
96195
96240
|
try {
|
|
96196
96241
|
const pkgResolved = __require.resolve("context-mode/package.json");
|
|
96197
|
-
const pkgRaw =
|
|
96242
|
+
const pkgRaw = readFileSync44(pkgResolved, "utf8");
|
|
96198
96243
|
const pkg = JSON.parse(pkgRaw);
|
|
96199
96244
|
if (pkg.version)
|
|
96200
96245
|
version4 = pkg.version;
|
|
@@ -96241,11 +96286,11 @@ function loadCompactContextDiscipline(hasGrepGlob = true) {
|
|
|
96241
96286
|
const p = resolveContextModeDisciplinePath();
|
|
96242
96287
|
if (!p)
|
|
96243
96288
|
throw new Error("discipline file not found");
|
|
96244
|
-
const raw =
|
|
96289
|
+
const raw = readFileSync44(p, "utf8");
|
|
96245
96290
|
let version4 = "1.0.169";
|
|
96246
96291
|
try {
|
|
96247
96292
|
const pkgResolved = __require.resolve("context-mode/package.json");
|
|
96248
|
-
const pkgRaw =
|
|
96293
|
+
const pkgRaw = readFileSync44(pkgResolved, "utf8");
|
|
96249
96294
|
const pkg = JSON.parse(pkgRaw);
|
|
96250
96295
|
if (pkg.version)
|
|
96251
96296
|
version4 = pkg.version;
|
|
@@ -97314,8 +97359,8 @@ function createBddContractAgent(model) {
|
|
|
97314
97359
|
createBddContractAgent.mode = MODE2;
|
|
97315
97360
|
|
|
97316
97361
|
// src/agents/builtin-agents/resolve-file-uri.ts
|
|
97317
|
-
import { existsSync as
|
|
97318
|
-
import { homedir as
|
|
97362
|
+
import { existsSync as existsSync75, readFileSync as readFileSync45 } from "fs";
|
|
97363
|
+
import { homedir as homedir12 } from "os";
|
|
97319
97364
|
import { isAbsolute as isAbsolute6, resolve as resolve10 } from "path";
|
|
97320
97365
|
function resolvePromptAppend(promptAppend, configDir) {
|
|
97321
97366
|
if (!promptAppend.startsWith("file://"))
|
|
@@ -97324,16 +97369,16 @@ function resolvePromptAppend(promptAppend, configDir) {
|
|
|
97324
97369
|
let filePath;
|
|
97325
97370
|
try {
|
|
97326
97371
|
const decoded = decodeURIComponent(encoded);
|
|
97327
|
-
const expanded = decoded.startsWith("~/") ? decoded.replace(/^~\//, `${
|
|
97372
|
+
const expanded = decoded.startsWith("~/") ? decoded.replace(/^~\//, `${homedir12()}/`) : decoded;
|
|
97328
97373
|
filePath = isAbsolute6(expanded) ? expanded : resolve10(configDir ?? process.cwd(), expanded);
|
|
97329
97374
|
} catch {
|
|
97330
97375
|
return `[WARNING: Malformed file URI (invalid percent-encoding): ${promptAppend}]`;
|
|
97331
97376
|
}
|
|
97332
|
-
if (!
|
|
97377
|
+
if (!existsSync75(filePath)) {
|
|
97333
97378
|
return `[WARNING: Could not resolve file URI: ${promptAppend}]`;
|
|
97334
97379
|
}
|
|
97335
97380
|
try {
|
|
97336
|
-
return
|
|
97381
|
+
return readFileSync45(filePath, "utf8");
|
|
97337
97382
|
} catch {
|
|
97338
97383
|
return `[WARNING: Could not read file: ${promptAppend}]`;
|
|
97339
97384
|
}
|
|
@@ -99217,16 +99262,19 @@ var OPERATOR_PROMPT_METADATA = {
|
|
|
99217
99262
|
category: "exploration",
|
|
99218
99263
|
cost: "CHEAP",
|
|
99219
99264
|
promptAlias: "Librarian",
|
|
99220
|
-
keyTrigger: "External library/source mentioned \u2192 fire `librarian` background",
|
|
99265
|
+
keyTrigger: "External library/source mentioned or code search needed \u2192 fire `librarian` background",
|
|
99221
99266
|
triggers: [
|
|
99222
|
-
{ domain: "Librarian", trigger: "Unfamiliar packages / libraries, struggles at weird behaviour (to find existing implementation of opensource)" }
|
|
99267
|
+
{ domain: "Librarian", trigger: "Unfamiliar packages / libraries, struggles at weird behaviour (to find existing implementation of opensource)" },
|
|
99268
|
+
{ domain: "Code Search", trigger: "Find real-world code examples, search GitHub repos for patterns, or need OSS implementation references" }
|
|
99223
99269
|
],
|
|
99224
99270
|
useWhen: [
|
|
99225
99271
|
"How do I use [library]?",
|
|
99226
99272
|
"What's the best practice for [framework feature]?",
|
|
99227
99273
|
"Why does [external dependency] behave this way?",
|
|
99228
99274
|
"Find examples of [library] usage",
|
|
99229
|
-
"Working with unfamiliar npm/pip/cargo packages"
|
|
99275
|
+
"Working with unfamiliar npm/pip/cargo packages",
|
|
99276
|
+
"Show me real-world code examples of [pattern]",
|
|
99277
|
+
"Search GitHub for implementations of [feature]"
|
|
99230
99278
|
]
|
|
99231
99279
|
};
|
|
99232
99280
|
function createOperatorAgent(model) {
|
|
@@ -99327,7 +99375,7 @@ context7_query-docs(libraryId: id, query: "specific topic")
|
|
|
99327
99375
|
Tool 1: context7_resolve-library-id("library-name")
|
|
99328
99376
|
\u2192 then context7_query-docs(libraryId: id, query: "specific-topic")
|
|
99329
99377
|
Tool 2: webfetch(relevant_pages_from_sitemap) // Targeted, not random
|
|
99330
|
-
Tool 3:
|
|
99378
|
+
Tool 3: white_rabbit_searchGitHub(query: "usage pattern", language: ["TypeScript"])
|
|
99331
99379
|
\`\`\`
|
|
99332
99380
|
|
|
99333
99381
|
**Output**: Summarize findings with links to official docs (versioned if applicable) and real-world examples.
|
|
@@ -99357,7 +99405,7 @@ Step 4: Construct permalink
|
|
|
99357
99405
|
**Parallel acceleration (4+ calls)**:
|
|
99358
99406
|
\`\`\`
|
|
99359
99407
|
Tool 1: gh repo clone owner/repo \${TMPDIR:-/tmp}/repo -- --depth 1
|
|
99360
|
-
Tool 2:
|
|
99408
|
+
Tool 2: white_rabbit_searchGitHub(query: "function_name", repo: "owner/repo")
|
|
99361
99409
|
Tool 3: gh api repos/owner/repo/commits/HEAD --jq '.sha'
|
|
99362
99410
|
Tool 4: context7_get-library-docs(id, topic: "relevant-api")
|
|
99363
99411
|
\`\`\`
|
|
@@ -99396,8 +99444,8 @@ Tool 1: context7_resolve-library-id \u2192 context7_query-docs
|
|
|
99396
99444
|
Tool 2: webfetch(targeted_doc_pages_from_sitemap)
|
|
99397
99445
|
|
|
99398
99446
|
// Code Search
|
|
99399
|
-
Tool 3:
|
|
99400
|
-
Tool 4:
|
|
99447
|
+
Tool 3: white_rabbit_searchGitHub(query: "pattern1", language: [...])
|
|
99448
|
+
Tool 4: white_rabbit_searchGitHub(query: "pattern2", useRegexp: true)
|
|
99401
99449
|
|
|
99402
99450
|
// Source Analysis
|
|
99403
99451
|
Tool 5: gh repo clone owner/repo \${TMPDIR:-/tmp}/repo -- --depth 1
|
|
@@ -99453,7 +99501,7 @@ https://github.com/tanstack/query/blob/abc123def/packages/react-query/src/useQue
|
|
|
99453
99501
|
| **Sitemap Discovery** | webfetch | \`webfetch(docs_url + "/sitemap.xml")\` to understand doc structure |
|
|
99454
99502
|
| **Read Doc Page** | webfetch | \`webfetch(specific_doc_page)\` for targeted documentation |
|
|
99455
99503
|
| **Latest Info** | websearch_exa | \`websearch_exa_web_search_exa("query ${new Date().getFullYear()}")\` |
|
|
99456
|
-
| **Fast Code Search** |
|
|
99504
|
+
| **Fast Code Search** | white_rabbit | \`white_rabbit_searchGitHub(query, language, useRegexp)\` |
|
|
99457
99505
|
| **Deep Code Search** | gh CLI | \`gh search code "query" --repo owner/repo\` |
|
|
99458
99506
|
| **Clone Repo** | gh CLI | \`gh repo clone owner/repo \${TMPDIR:-/tmp}/name -- --depth 1\` |
|
|
99459
99507
|
| **Issues/PRs** | gh CLI | \`gh search issues/prs "query" --repo owner/repo\` |
|
|
@@ -99489,16 +99537,16 @@ Use OS-appropriate temp directory:
|
|
|
99489
99537
|
**Doc Discovery is SEQUENTIAL** (websearch \u2192 version check \u2192 sitemap \u2192 investigate).
|
|
99490
99538
|
**Main phase is PARALLEL** once you know where to look.
|
|
99491
99539
|
|
|
99492
|
-
**Always vary queries** when using
|
|
99540
|
+
**Always vary queries** when using white_rabbit:
|
|
99493
99541
|
\`\`\`
|
|
99494
99542
|
// GOOD: Different angles
|
|
99495
|
-
|
|
99496
|
-
|
|
99497
|
-
|
|
99543
|
+
white_rabbit_searchGitHub(query: "useQuery(", language: ["TypeScript"])
|
|
99544
|
+
white_rabbit_searchGitHub(query: "queryOptions", language: ["TypeScript"])
|
|
99545
|
+
white_rabbit_searchGitHub(query: "staleTime:", language: ["TypeScript"])
|
|
99498
99546
|
|
|
99499
99547
|
// BAD: Same pattern
|
|
99500
|
-
|
|
99501
|
-
|
|
99548
|
+
white_rabbit_searchGitHub(query: "useQuery")
|
|
99549
|
+
white_rabbit_searchGitHub(query: "useQuery")
|
|
99502
99550
|
\`\`\`
|
|
99503
99551
|
|
|
99504
99552
|
---
|
|
@@ -99508,7 +99556,7 @@ grep_app_searchGitHub(query: "useQuery")
|
|
|
99508
99556
|
| Failure | Recovery Action |
|
|
99509
99557
|
|---------|-----------------|
|
|
99510
99558
|
| context7 not found | Clone repo, read source + README directly |
|
|
99511
|
-
|
|
|
99559
|
+
| white_rabbit no results | Broaden query, try concept instead of exact name |
|
|
99512
99560
|
| gh API rate limit | Use cloned repo in temp directory |
|
|
99513
99561
|
| Repo not found | Search for forks or mirrors |
|
|
99514
99562
|
| Sitemap not found | Try \`/sitemap-0.xml\`, \`/sitemap_index.xml\`, or fetch docs index page and parse navigation |
|
|
@@ -99519,7 +99567,7 @@ grep_app_searchGitHub(query: "useQuery")
|
|
|
99519
99567
|
|
|
99520
99568
|
## COMMUNICATION RULES
|
|
99521
99569
|
|
|
99522
|
-
1. **NO TOOL NAMES**: Say "I'll search the codebase" not "I'll use
|
|
99570
|
+
1. **NO TOOL NAMES**: Say "I'll search the codebase" not "I'll use white_rabbit"
|
|
99523
99571
|
2. **NO PREAMBLE**: Answer directly, skip "I'll help you with..."
|
|
99524
99572
|
3. **ALWAYS CITE**: Every code claim needs a permalink
|
|
99525
99573
|
4. **USE MARKDOWN**: Code blocks with language identifiers
|
|
@@ -102891,14 +102939,6 @@ var document_reader = {
|
|
|
102891
102939
|
enabled: true
|
|
102892
102940
|
};
|
|
102893
102941
|
|
|
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
102942
|
// src/mcp/mcp-validator.ts
|
|
102903
102943
|
function validateWebsearchConfig(config4) {
|
|
102904
102944
|
const provider = config4?.provider || "exa";
|
|
@@ -102947,8 +102987,16 @@ function createWebsearchConfig(config4) {
|
|
|
102947
102987
|
}
|
|
102948
102988
|
var websearch = createWebsearchConfig();
|
|
102949
102989
|
|
|
102990
|
+
// src/mcp/white-rabbit.ts
|
|
102991
|
+
var white_rabbit = {
|
|
102992
|
+
type: "remote",
|
|
102993
|
+
url: "https://mcp.grep.app",
|
|
102994
|
+
enabled: true,
|
|
102995
|
+
oauth: false
|
|
102996
|
+
};
|
|
102997
|
+
|
|
102950
102998
|
// src/mcp/types.ts
|
|
102951
|
-
var McpNameSchema = exports_external.enum(["websearch", "context7", "
|
|
102999
|
+
var McpNameSchema = exports_external.enum(["websearch", "context7", "white_rabbit", "document_reader"]);
|
|
102952
103000
|
var AnyMcpNameSchema = exports_external.string().min(1);
|
|
102953
103001
|
|
|
102954
103002
|
// src/mcp/index.ts
|
|
@@ -102997,8 +103045,8 @@ function createBuiltinMcps(disabledMcps = [], config4, options = {}) {
|
|
|
102997
103045
|
if (!disabledMcps.includes("context7")) {
|
|
102998
103046
|
mcps.context7 = context7;
|
|
102999
103047
|
}
|
|
103000
|
-
if (!disabledMcps.includes("
|
|
103001
|
-
mcps.
|
|
103048
|
+
if (!disabledMcps.includes("white_rabbit")) {
|
|
103049
|
+
mcps.white_rabbit = white_rabbit;
|
|
103002
103050
|
}
|
|
103003
103051
|
if (!disabledMcps.includes("document_reader")) {
|
|
103004
103052
|
if (commandAvailable("uvx")) {
|
|
@@ -103100,7 +103148,7 @@ function applyToolConfig(params) {
|
|
|
103100
103148
|
const denyTaskTools = !isTaskSystem ? { "task_*": "deny", task: "deny" } : {};
|
|
103101
103149
|
params.config.tools = {
|
|
103102
103150
|
...params.config.tools,
|
|
103103
|
-
"
|
|
103151
|
+
"white_rabbit_*": false,
|
|
103104
103152
|
LspHover: false,
|
|
103105
103153
|
LspCodeActions: false,
|
|
103106
103154
|
LspCodeActionResolve: false,
|
|
@@ -103112,7 +103160,7 @@ function applyToolConfig(params) {
|
|
|
103112
103160
|
const questionPermission = isCliRunMode ? "deny" : "allow";
|
|
103113
103161
|
const operator = agentByKey(params.agentResult, "operator");
|
|
103114
103162
|
if (operator) {
|
|
103115
|
-
operator.permission = { ...operator.permission, "
|
|
103163
|
+
operator.permission = { ...operator.permission, "white_rabbit_*": "allow" };
|
|
103116
103164
|
}
|
|
103117
103165
|
const construct = agentByKey(params.agentResult, "construct");
|
|
103118
103166
|
if (construct) {
|
|
@@ -103306,7 +103354,8 @@ function createToolRegistry(args) {
|
|
|
103306
103354
|
availableSkills: skillContext.availableSkills,
|
|
103307
103355
|
modelRequirements: pluginConfig.modelRequirements,
|
|
103308
103356
|
complexityDowngrades: pluginConfig.complexityDowngrades,
|
|
103309
|
-
|
|
103357
|
+
modelPresets: pluginConfig.model_presets,
|
|
103358
|
+
activePreset: pluginConfig.active_preset,
|
|
103310
103359
|
agentOverrides: pluginConfig.agents,
|
|
103311
103360
|
onSyncSessionCreated: async (event) => {
|
|
103312
103361
|
log("[index] onSyncSessionCreated callback", {
|
|
@@ -103367,6 +103416,7 @@ function createToolRegistry(args) {
|
|
|
103367
103416
|
...createHandoffTools(ctx),
|
|
103368
103417
|
...createPdfExtractFiguresTool(),
|
|
103369
103418
|
...createDcpSwitchProfileTool({ pluginConfig }),
|
|
103419
|
+
...createPresetTool({ pluginConfig, directory: ctx.directory }),
|
|
103370
103420
|
...backgroundTools,
|
|
103371
103421
|
delegate_agent: delegateAgent,
|
|
103372
103422
|
...lookAt ? { look_at: lookAt } : {},
|
|
@@ -103393,6 +103443,10 @@ function createToolRegistry(args) {
|
|
|
103393
103443
|
// src/create-tools.ts
|
|
103394
103444
|
async function createTools(args) {
|
|
103395
103445
|
const { ctx, pluginConfig, managers } = args;
|
|
103446
|
+
const pollTimeout = resolveTasksConfig(pluginConfig).pollTimeoutMs;
|
|
103447
|
+
if (pollTimeout !== undefined) {
|
|
103448
|
+
setPollTimeoutMs(pollTimeout);
|
|
103449
|
+
}
|
|
103396
103450
|
const skillContext = await createSkillContext({
|
|
103397
103451
|
directory: ctx.directory,
|
|
103398
103452
|
pluginConfig
|
|
@@ -103494,10 +103548,8 @@ var AgentPermissionSchema = exports_external.object({
|
|
|
103494
103548
|
});
|
|
103495
103549
|
|
|
103496
103550
|
// src/config/schema/agent-overrides.ts
|
|
103497
|
-
var TierNameSchema = exports_external.enum(["free", "fast", "standard", "premium", "frontier"]);
|
|
103498
103551
|
var AgentOverrideConfigSchema = exports_external.object({
|
|
103499
103552
|
model: exports_external.string().optional(),
|
|
103500
|
-
tier: TierNameSchema.optional(),
|
|
103501
103553
|
variant: exports_external.string().optional(),
|
|
103502
103554
|
category: exports_external.string().optional(),
|
|
103503
103555
|
skills: exports_external.array(exports_external.string()).optional(),
|
|
@@ -103588,7 +103640,6 @@ var BrowserAutomationConfigSchema = exports_external.object({
|
|
|
103588
103640
|
var CategoryConfigSchema = exports_external.object({
|
|
103589
103641
|
description: exports_external.string().optional(),
|
|
103590
103642
|
model: exports_external.string().optional(),
|
|
103591
|
-
tier: TierNameSchema.optional(),
|
|
103592
103643
|
variant: exports_external.string().optional(),
|
|
103593
103644
|
temperature: exports_external.number().min(0).max(2).optional(),
|
|
103594
103645
|
top_p: exports_external.number().min(0).max(1).optional(),
|
|
@@ -103626,7 +103677,7 @@ var BuiltinCommandNameSchema = exports_external.enum([
|
|
|
103626
103677
|
"refactor",
|
|
103627
103678
|
"start-work",
|
|
103628
103679
|
"stop-continuation",
|
|
103629
|
-
"
|
|
103680
|
+
"preset",
|
|
103630
103681
|
"end-ultrawork",
|
|
103631
103682
|
"handoff",
|
|
103632
103683
|
"research",
|
|
@@ -103823,6 +103874,25 @@ var MatrixxSelfConfigSkillConfigSchema = exports_external.object({
|
|
|
103823
103874
|
proactive: exports_external.boolean().default(false)
|
|
103824
103875
|
});
|
|
103825
103876
|
|
|
103877
|
+
// src/config/schema/model-presets.ts
|
|
103878
|
+
var providerModelSchema = exports_external.string().refine((s) => {
|
|
103879
|
+
if (!s.includes("/"))
|
|
103880
|
+
return false;
|
|
103881
|
+
const parts = s.split("/");
|
|
103882
|
+
return parts.length === 2 && parts[0].length > 0 && parts[1].length > 0;
|
|
103883
|
+
}, { message: "Must be <provider>/<model>" });
|
|
103884
|
+
var ModelPresetEntrySchema = exports_external.object({
|
|
103885
|
+
model: providerModelSchema,
|
|
103886
|
+
variant: exports_external.string().optional()
|
|
103887
|
+
});
|
|
103888
|
+
var ModelPresetSchema = exports_external.object({
|
|
103889
|
+
default_model: providerModelSchema.optional(),
|
|
103890
|
+
agents: exports_external.record(exports_external.string(), ModelPresetEntrySchema).optional(),
|
|
103891
|
+
categories: exports_external.record(exports_external.string(), ModelPresetEntrySchema).optional()
|
|
103892
|
+
});
|
|
103893
|
+
var ModelPresetsSchema = exports_external.record(exports_external.string(), ModelPresetSchema);
|
|
103894
|
+
var ActivePresetSchema = exports_external.string().min(1);
|
|
103895
|
+
|
|
103826
103896
|
// src/config/schema/morpheus.ts
|
|
103827
103897
|
var MorpheusTasksConfigSchema = exports_external.object({
|
|
103828
103898
|
storage_path: exports_external.string().optional(),
|
|
@@ -103949,6 +104019,22 @@ var SkillsConfigSchema = exports_external.union([
|
|
|
103949
104019
|
}).catchall(SkillEntrySchema)
|
|
103950
104020
|
]);
|
|
103951
104021
|
|
|
104022
|
+
// src/config/schema/task.ts
|
|
104023
|
+
var TaskConfigSchema = exports_external.object({
|
|
104024
|
+
pollTimeoutMs: exports_external.number().min(60000).optional()
|
|
104025
|
+
});
|
|
104026
|
+
|
|
104027
|
+
// src/config/schema/tasks.ts
|
|
104028
|
+
var TasksConfigSchema = exports_external.object({
|
|
104029
|
+
enabled: exports_external.boolean().optional().default(true),
|
|
104030
|
+
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}"),
|
|
104031
|
+
storage_path: exports_external.string().optional(),
|
|
104032
|
+
task_list_id: exports_external.string().optional(),
|
|
104033
|
+
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."),
|
|
104034
|
+
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."),
|
|
104035
|
+
pollTimeoutMs: exports_external.number().min(60000).optional()
|
|
104036
|
+
});
|
|
104037
|
+
|
|
103952
104038
|
// src/config/schema/tdd-enforcer.ts
|
|
103953
104039
|
var TddEnforcerConfigSchema = exports_external.object({
|
|
103954
104040
|
enabled: exports_external.boolean().default(false)
|
|
@@ -103980,7 +104066,8 @@ var WebsearchConfigSchema = exports_external.object({
|
|
|
103980
104066
|
var MatrixxConfigSchema = exports_external.object({
|
|
103981
104067
|
$schema: exports_external.string().optional(),
|
|
103982
104068
|
global_model: exports_external.string().optional(),
|
|
103983
|
-
|
|
104069
|
+
model_presets: ModelPresetsSchema.optional(),
|
|
104070
|
+
active_preset: ActivePresetSchema.optional(),
|
|
103984
104071
|
new_task_system_enabled: exports_external.boolean().optional(),
|
|
103985
104072
|
default_run_agent: exports_external.string().optional(),
|
|
103986
104073
|
disabled_mcps: exports_external.array(AnyMcpNameSchema).optional(),
|
|
@@ -104002,6 +104089,8 @@ var MatrixxConfigSchema = exports_external.object({
|
|
|
104002
104089
|
background_task: BackgroundTaskConfigSchema.optional(),
|
|
104003
104090
|
notification: NotificationConfigSchema.optional(),
|
|
104004
104091
|
babysitting: BabysittingConfigSchema.optional(),
|
|
104092
|
+
tasks: TasksConfigSchema.optional(),
|
|
104093
|
+
task: TaskConfigSchema.optional(),
|
|
104005
104094
|
tdd_enforcer: TddEnforcerConfigSchema.optional(),
|
|
104006
104095
|
browser_automation_engine: BrowserAutomationConfigSchema.optional(),
|
|
104007
104096
|
websearch: WebsearchConfigSchema.optional(),
|
|
@@ -104017,7 +104106,6 @@ var MatrixxConfigSchema = exports_external.object({
|
|
|
104017
104106
|
rtk: RtkConfigSchema.optional(),
|
|
104018
104107
|
evolution: EvolutionConfigSchema.optional(),
|
|
104019
104108
|
_migrations: exports_external.array(exports_external.string()).optional(),
|
|
104020
|
-
tiers: TiersSchema.optional(),
|
|
104021
104109
|
modelRequirements: ModelRequirementsSchema.optional(),
|
|
104022
104110
|
complexityDowngrades: ComplexityDowngradesSchema.optional()
|
|
104023
104111
|
});
|
|
@@ -104025,11 +104113,8 @@ var MatrixxConfigSchema = exports_external.object({
|
|
|
104025
104113
|
init_logger();
|
|
104026
104114
|
var MIGRATION_KEY = "model-migration";
|
|
104027
104115
|
function normalizeModelInput(value) {
|
|
104028
|
-
if (value.startsWith("tier:")) {
|
|
104029
|
-
return value;
|
|
104030
|
-
}
|
|
104031
104116
|
if (value.includes("/")) {
|
|
104032
|
-
log(`[migration] prefixed model '${value}' is deprecated, use
|
|
104117
|
+
log(`[migration] prefixed model '${value}' is deprecated, use bare '<model>'`);
|
|
104033
104118
|
}
|
|
104034
104119
|
return value;
|
|
104035
104120
|
}
|
|
@@ -104041,26 +104126,6 @@ function migrateMatrixxConfig(raw) {
|
|
|
104041
104126
|
if (typeof migrated.global_model === "string") {
|
|
104042
104127
|
migrated = { ...migrated, global_model: normalizeModelInput(migrated.global_model) };
|
|
104043
104128
|
}
|
|
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
104129
|
if (migrated.modelRequirements) {
|
|
104065
104130
|
const nextMR = { ...migrated.modelRequirements };
|
|
104066
104131
|
for (const scope of ["agents", "categories"]) {
|
|
@@ -104205,7 +104270,7 @@ function mergeConfigs(base, override) {
|
|
|
104205
104270
|
});
|
|
104206
104271
|
return merged;
|
|
104207
104272
|
}
|
|
104208
|
-
function
|
|
104273
|
+
function resolveConfigPath2(baseDir, configName) {
|
|
104209
104274
|
const newBase = path13.join(baseDir, "matrixx");
|
|
104210
104275
|
const newDetected = detectConfigFile(newBase);
|
|
104211
104276
|
if (newDetected.format !== "none") {
|
|
@@ -104219,29 +104284,39 @@ function resolveConfigPath(baseDir, configName) {
|
|
|
104219
104284
|
}
|
|
104220
104285
|
return `${newBase}.json`;
|
|
104221
104286
|
}
|
|
104287
|
+
function assertModelsResolvable(config4) {
|
|
104288
|
+
const globalModel = config4.global_model;
|
|
104289
|
+
const unresolved = [];
|
|
104290
|
+
for (const [name, entry] of Object.entries(config4.agents ?? {})) {
|
|
104291
|
+
if (entry && !entry.disable && !entry.model && !globalModel) {
|
|
104292
|
+
unresolved.push(`agent "${name}"`);
|
|
104293
|
+
}
|
|
104294
|
+
}
|
|
104295
|
+
for (const [name, entry] of Object.entries(config4.categories ?? {})) {
|
|
104296
|
+
if (entry && !entry.disable && !entry.model && !globalModel) {
|
|
104297
|
+
unresolved.push(`category "${name}"`);
|
|
104298
|
+
}
|
|
104299
|
+
}
|
|
104300
|
+
if (unresolved.length > 0) {
|
|
104301
|
+
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.`);
|
|
104302
|
+
}
|
|
104303
|
+
}
|
|
104222
104304
|
async function loadPluginConfig(directory, ctx) {
|
|
104223
104305
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
104224
|
-
const userConfigPath =
|
|
104306
|
+
const userConfigPath = resolveConfigPath2(configDir, "matrixx");
|
|
104225
104307
|
const projectDir = path13.join(directory, ".opencode");
|
|
104226
|
-
const projectConfigPath =
|
|
104308
|
+
const projectConfigPath = resolveConfigPath2(projectDir, "matrixx");
|
|
104227
104309
|
let config4 = loadConfigFromPath(userConfigPath, ctx) ?? {};
|
|
104228
104310
|
const projectConfig = loadConfigFromPath(projectConfigPath, ctx);
|
|
104229
104311
|
if (projectConfig) {
|
|
104230
104312
|
config4 = mergeConfigs(config4, projectConfig);
|
|
104231
104313
|
}
|
|
104232
104314
|
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
|
-
};
|
|
104315
|
+
config4 = applyActivePreset(config4, config4.active_preset);
|
|
104316
|
+
if (config4.active_preset && config4.model_presets?.[config4.active_preset]) {
|
|
104317
|
+
log("Active model preset applied", { active_preset: config4.active_preset });
|
|
104318
|
+
}
|
|
104319
|
+
assertModelsResolvable(config4);
|
|
104245
104320
|
log("Final merged config", {
|
|
104246
104321
|
agents: config4.agents,
|
|
104247
104322
|
disabled_agents: config4.disabled_agents,
|
|
@@ -104461,19 +104536,19 @@ function createEventHandler2(args) {
|
|
|
104461
104536
|
};
|
|
104462
104537
|
const recentSyntheticIdles = new Map;
|
|
104463
104538
|
const recentRealIdles = new Map;
|
|
104464
|
-
const
|
|
104539
|
+
const DEDUP_WINDOW_MS2 = 500;
|
|
104465
104540
|
return async (input) => {
|
|
104466
104541
|
pruneRecentSyntheticIdles({
|
|
104467
104542
|
recentSyntheticIdles,
|
|
104468
104543
|
recentRealIdles,
|
|
104469
104544
|
now: Date.now(),
|
|
104470
|
-
dedupWindowMs:
|
|
104545
|
+
dedupWindowMs: DEDUP_WINDOW_MS2
|
|
104471
104546
|
});
|
|
104472
104547
|
if (input.event.type === "session.idle") {
|
|
104473
104548
|
const sessionID = input.event.properties?.sessionID;
|
|
104474
104549
|
if (sessionID) {
|
|
104475
104550
|
const emittedAt = recentSyntheticIdles.get(sessionID);
|
|
104476
|
-
if (emittedAt && Date.now() - emittedAt <
|
|
104551
|
+
if (emittedAt && Date.now() - emittedAt < DEDUP_WINDOW_MS2) {
|
|
104477
104552
|
recentSyntheticIdles.delete(sessionID);
|
|
104478
104553
|
return;
|
|
104479
104554
|
}
|
|
@@ -104485,7 +104560,7 @@ function createEventHandler2(args) {
|
|
|
104485
104560
|
if (syntheticIdle) {
|
|
104486
104561
|
const sessionID = syntheticIdle.event.properties?.sessionID;
|
|
104487
104562
|
const emittedAt = recentRealIdles.get(sessionID);
|
|
104488
|
-
if (emittedAt && Date.now() - emittedAt <
|
|
104563
|
+
if (emittedAt && Date.now() - emittedAt < DEDUP_WINDOW_MS2) {
|
|
104489
104564
|
recentRealIdles.delete(sessionID);
|
|
104490
104565
|
return;
|
|
104491
104566
|
}
|
|
@@ -104661,22 +104736,22 @@ function createToolExecuteAfterHandler2(args) {
|
|
|
104661
104736
|
|
|
104662
104737
|
// src/features/assembly-state/manager.ts
|
|
104663
104738
|
init_logger();
|
|
104664
|
-
var
|
|
104739
|
+
var HOOK_NAME20 = "assembly-state";
|
|
104665
104740
|
var disabledSessions = new Set;
|
|
104666
104741
|
function isAssemblyDisabled(sessionID) {
|
|
104667
104742
|
return disabledSessions.has(sessionID);
|
|
104668
104743
|
}
|
|
104669
104744
|
function disableAssembly(sessionID) {
|
|
104670
104745
|
disabledSessions.add(sessionID);
|
|
104671
|
-
log(`[${
|
|
104746
|
+
log(`[${HOOK_NAME20}] Assembly disabled for session`, { sessionID });
|
|
104672
104747
|
disabledSessions.add(sessionID);
|
|
104673
|
-
log(`[${
|
|
104748
|
+
log(`[${HOOK_NAME20}] Consensus disabled for session`, { sessionID });
|
|
104674
104749
|
}
|
|
104675
104750
|
function enableAssembly(sessionID) {
|
|
104676
104751
|
disabledSessions.delete(sessionID);
|
|
104677
|
-
log(`[${
|
|
104752
|
+
log(`[${HOOK_NAME20}] Assembly enabled for session`, { sessionID });
|
|
104678
104753
|
disabledSessions.delete(sessionID);
|
|
104679
|
-
log(`[${
|
|
104754
|
+
log(`[${HOOK_NAME20}] Consensus enabled for session`, { sessionID });
|
|
104680
104755
|
}
|
|
104681
104756
|
// src/plugin/session-agent-resolver.ts
|
|
104682
104757
|
async function resolveSessionAgent(client2, sessionId, timeoutMs = 5000) {
|