opencode-matrixx 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -23
- package/dist/agents/architect/agent.d.ts +9 -9
- package/dist/agents/architect/default.d.ts +3 -3
- package/dist/agents/architect/gpt.d.ts +3 -3
- package/dist/agents/architect/index.d.ts +4 -4
- package/dist/agents/architect/prompt-section-builder.d.ts +1 -1
- package/dist/agents/builtin-agents/{atlas-agent.d.ts → architect-agent.d.ts} +1 -1
- package/dist/agents/builtin-agents/general-agents.d.ts +1 -1
- package/dist/agents/builtin-agents/model-resolution.d.ts +1 -1
- package/dist/agents/index.d.ts +2 -2
- package/dist/agents/merovingian.d.ts +3 -2
- package/dist/agents/types.d.ts +2 -2
- package/dist/config/index.d.ts +2 -2
- package/dist/config/schema/agent-names.d.ts +4 -18
- package/dist/config/schema/assembly.d.ts +13 -0
- package/dist/config/schema/categories.d.ts +0 -1
- package/dist/config/schema/commands.d.ts +4 -0
- package/dist/config/schema/hooks.d.ts +1 -2
- package/dist/config/schema/matrixx-config.d.ts +18 -3
- package/dist/config/schema/security.d.ts +0 -5
- package/dist/config/schema.d.ts +1 -0
- package/dist/create-hooks.d.ts +2 -2
- package/dist/features/assembly-state/index.d.ts +1 -0
- package/dist/features/assembly-state/manager.d.ts +6 -0
- package/dist/features/background-agent/error-helpers.d.ts +6 -0
- package/dist/features/background-agent/manager.d.ts +6 -8
- package/dist/features/background-agent/message-dir.d.ts +5 -0
- package/dist/features/background-agent/notification-builder.d.ts +17 -0
- package/dist/features/builtin-commands/templates/assembly.d.ts +1 -0
- package/dist/features/builtin-commands/templates/research.d.ts +1 -0
- package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
- package/dist/features/builtin-commands/templates/ultrawork.d.ts +1 -0
- package/dist/features/builtin-commands/types.d.ts +1 -1
- package/dist/features/builtin-skills/skills/index.d.ts +2 -0
- package/dist/features/builtin-skills/skills/remove-ai-slops.d.ts +2 -0
- package/dist/features/builtin-skills/skills/ulw-research.d.ts +2 -0
- package/dist/features/claude-code-command-loader/loader.d.ts +0 -1
- package/dist/features/hook-message-injector/injector.d.ts +2 -3
- package/dist/features/mission-state/types.d.ts +1 -1
- package/dist/features/tmux-subagent/index.d.ts +0 -2
- package/dist/features/ultrawork-state/index.d.ts +1 -0
- package/dist/features/ultrawork-state/manager.d.ts +6 -0
- package/dist/hooks/architect/{atlas-hook.d.ts → architect-hook.d.ts} +2 -2
- package/dist/hooks/architect/event-handler.d.ts +3 -3
- package/dist/hooks/architect/index.d.ts +2 -2
- package/dist/hooks/architect/types.d.ts +1 -1
- package/dist/hooks/auto-update-checker/cache.d.ts +0 -2
- package/dist/hooks/auto-update-checker/index.d.ts +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/interactive-bash-session/parser.d.ts +0 -6
- package/dist/hooks/mouse-notepad/constants.d.ts +1 -1
- package/dist/index.js +1966 -631
- package/dist/plugin/hooks/create-continuation-hooks.d.ts +2 -2
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +1 -1
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/model-resolution-pipeline.d.ts +1 -24
- package/dist/shared/model-resolution-types.d.ts +1 -0
- package/dist/shared/model-resolver.d.ts +4 -2
- package/dist/shared/opencode-config-dir.d.ts +1 -2
- package/dist/shared/session-directory-resolver.d.ts +0 -6
- package/dist/shared/tool-guards.d.ts +16 -0
- package/dist/tools/assembly/assembly-executor.d.ts +17 -0
- package/dist/tools/assembly/constants.d.ts +6 -0
- package/dist/tools/assembly/index.d.ts +2 -0
- package/dist/tools/assembly/provider-selector.d.ts +3 -0
- package/dist/tools/assembly/synthesizer.d.ts +3 -0
- package/dist/tools/assembly/tool-description.d.ts +2 -0
- package/dist/tools/assembly/tools.d.ts +10 -0
- package/dist/tools/assembly/types.d.ts +25 -0
- package/dist/tools/assembly/voter-spawner.d.ts +3 -0
- package/dist/tools/delegate-task/executor-types.d.ts +1 -1
- package/dist/tools/delegate-task/mouse-agent.d.ts +1 -1
- package/dist/tools/delegate-task/types.d.ts +1 -1
- package/dist/tools/handoff/constants.d.ts +0 -3
- package/dist/tools/index.d.ts +1 -0
- package/package.json +4 -29
- package/bin/matrixx.js +0 -80
- package/bin/platform.js +0 -38
- package/bin/platform.test.ts +0 -148
- package/dist/cli/cli-installer.d.ts +0 -2
- package/dist/cli/cli-program.d.ts +0 -1
- package/dist/cli/config-manager/add-plugin-to-opencode-config.d.ts +0 -2
- package/dist/cli/config-manager/add-provider-config.d.ts +0 -2
- package/dist/cli/config-manager/antigravity-provider-configuration.d.ts +0 -122
- package/dist/cli/config-manager/auth-plugins.d.ts +0 -3
- package/dist/cli/config-manager/bun-install.d.ts +0 -7
- package/dist/cli/config-manager/config-context.d.ts +0 -13
- package/dist/cli/config-manager/deep-merge-record.d.ts +0 -1
- package/dist/cli/config-manager/detect-current-config.d.ts +0 -2
- package/dist/cli/config-manager/ensure-config-directory-exists.d.ts +0 -1
- package/dist/cli/config-manager/format-error-with-suggestion.d.ts +0 -1
- package/dist/cli/config-manager/generate-matrixx-config.d.ts +0 -2
- package/dist/cli/config-manager/jsonc-provider-editor.d.ts +0 -1
- package/dist/cli/config-manager/npm-dist-tags.d.ts +0 -7
- package/dist/cli/config-manager/opencode-binary.d.ts +0 -2
- package/dist/cli/config-manager/opencode-config-format.d.ts +0 -6
- package/dist/cli/config-manager/parse-opencode-config-file.d.ts +0 -10
- package/dist/cli/config-manager/plugin-name-with-version.d.ts +0 -1
- package/dist/cli/config-manager/write-matrixx-config.d.ts +0 -2
- package/dist/cli/config-manager.d.ts +0 -14
- package/dist/cli/doctor/checks/config.d.ts +0 -2
- package/dist/cli/doctor/checks/dependencies.d.ts +0 -4
- package/dist/cli/doctor/checks/index.d.ts +0 -7
- package/dist/cli/doctor/checks/model-resolution-cache.d.ts +0 -2
- package/dist/cli/doctor/checks/model-resolution-config.d.ts +0 -2
- package/dist/cli/doctor/checks/model-resolution-details.d.ts +0 -6
- package/dist/cli/doctor/checks/model-resolution-effective-model.d.ts +0 -3
- package/dist/cli/doctor/checks/model-resolution-types.d.ts +0 -37
- package/dist/cli/doctor/checks/model-resolution-variant.d.ts +0 -5
- package/dist/cli/doctor/checks/model-resolution.d.ts +0 -6
- package/dist/cli/doctor/checks/system-binary.d.ts +0 -8
- package/dist/cli/doctor/checks/system-loaded-version.d.ts +0 -10
- package/dist/cli/doctor/checks/system-plugin.d.ts +0 -15
- package/dist/cli/doctor/checks/system.d.ts +0 -3
- package/dist/cli/doctor/checks/tools-gh.d.ts +0 -11
- package/dist/cli/doctor/checks/tools-lsp.d.ts +0 -6
- package/dist/cli/doctor/checks/tools-mcp.d.ts +0 -3
- package/dist/cli/doctor/checks/tools.d.ts +0 -3
- package/dist/cli/doctor/constants.d.ts +0 -31
- package/dist/cli/doctor/format-default.d.ts +0 -2
- package/dist/cli/doctor/format-shared.d.ts +0 -6
- package/dist/cli/doctor/format-status.d.ts +0 -2
- package/dist/cli/doctor/format-verbose.d.ts +0 -2
- package/dist/cli/doctor/formatter.d.ts +0 -3
- package/dist/cli/doctor/index.d.ts +0 -5
- package/dist/cli/doctor/runner.d.ts +0 -5
- package/dist/cli/doctor/types.d.ts +0 -96
- package/dist/cli/fallback-chain-resolution.d.ts +0 -10
- package/dist/cli/get-local-version/formatter.d.ts +0 -3
- package/dist/cli/get-local-version/get-local-version.d.ts +0 -2
- package/dist/cli/get-local-version/index.d.ts +0 -2
- package/dist/cli/get-local-version/types.d.ts +0 -13
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -30690
- package/dist/cli/install-validators.d.ts +0 -31
- package/dist/cli/install.d.ts +0 -2
- package/dist/cli/mcp-oauth/index.d.ts +0 -6
- package/dist/cli/mcp-oauth/login.d.ts +0 -7
- package/dist/cli/mcp-oauth/logout.d.ts +0 -5
- package/dist/cli/mcp-oauth/status.d.ts +0 -1
- package/dist/cli/model-fallback-types.d.ts +0 -25
- package/dist/cli/model-fallback.d.ts +0 -4
- package/dist/cli/provider-availability.d.ts +0 -4
- package/dist/cli/provider-model-id-transform.d.ts +0 -1
- package/dist/cli/run/agent-resolver.d.ts +0 -5
- package/dist/cli/run/completion.d.ts +0 -2
- package/dist/cli/run/event-formatting.d.ts +0 -3
- package/dist/cli/run/event-handlers.d.ts +0 -10
- package/dist/cli/run/event-state.d.ts +0 -13
- package/dist/cli/run/event-stream-processor.d.ts +0 -3
- package/dist/cli/run/events.d.ts +0 -4
- package/dist/cli/run/index.d.ts +0 -9
- package/dist/cli/run/json-output.d.ts +0 -12
- package/dist/cli/run/on-complete-hook.d.ts +0 -7
- package/dist/cli/run/opencode-bin-path.d.ts +0 -3
- package/dist/cli/run/opencode-binary-resolver.d.ts +0 -4
- package/dist/cli/run/poll-for-completion.d.ts +0 -9
- package/dist/cli/run/runner.d.ts +0 -5
- package/dist/cli/run/server-connection.d.ts +0 -6
- package/dist/cli/run/session-resolver.d.ts +0 -6
- package/dist/cli/run/types.d.ts +0 -119
- package/dist/cli/tui-install-prompts.d.ts +0 -2
- package/dist/cli/tui-installer.d.ts +0 -2
- package/dist/cli/types.d.ts +0 -36
- package/dist/features/background-agent/background-task-notification-template.d.ts +0 -11
- package/dist/features/background-agent/opencode-client.d.ts +0 -2
- package/dist/features/background-agent/task-poller.d.ts +0 -21
- package/dist/features/claude-tasks/index.d.ts +0 -3
- package/dist/features/claude-tasks/session-storage.d.ts +0 -10
- package/dist/features/mcp-oauth/index.d.ts +0 -3
- package/dist/features/mcp-oauth/schema.d.ts +0 -5
- package/dist/features/run-continuation-state/constants.d.ts +0 -1
- package/dist/features/run-continuation-state/index.d.ts +0 -3
- package/dist/features/run-continuation-state/storage.d.ts +0 -6
- package/dist/features/run-continuation-state/types.d.ts +0 -13
- package/dist/features/tmux-subagent/cleanup.d.ts +0 -1
- package/dist/features/tmux-subagent/polling.d.ts +0 -1
- package/dist/hooks/session-recovery/recover-empty-content-message-sdk.d.ts +0 -13
- package/dist/hooks/task-reminder/hook.d.ts +0 -19
- package/dist/hooks/task-reminder/index.d.ts +0 -1
- package/dist/tools/delegate-agent/background-agent-executor.d.ts +0 -5
- package/dist/tools/delegate-agent/message-storage-directory.d.ts +0 -1
- package/dist/tools/delegate-agent/subagent-session-creator.d.ts +0 -10
- package/dist/tools/delegate-agent/tool-context-with-metadata.d.ts +0 -10
- package/postinstall.mjs +0 -43
- /package/dist/agents/builtin-agents/{sisyphus-agent.d.ts → morpheus-agent.d.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -4531,7 +4531,7 @@ var require_picomatch2 = __commonJS((exports, module) => {
|
|
|
4531
4531
|
});
|
|
4532
4532
|
|
|
4533
4533
|
// src/hooks/matrix-loop/constants.ts
|
|
4534
|
-
var
|
|
4534
|
+
var HOOK_NAME5 = "matrix-loop", DEFAULT_STATE_FILE = ".matrixx/matrix-loop.local.md", DEFAULT_MAX_ITERATIONS = 100, DEFAULT_COMPLETION_PROMISE = "DONE", DEFAULT_VERIFICATION_AGENT = "merovingian", DEFAULT_VERIFICATION_TIMEOUT_MS = 30000, DEFAULT_VERIFICATION_MAX_RETRIES = 2;
|
|
4535
4535
|
|
|
4536
4536
|
// src/hooks/matrix-loop/storage.ts
|
|
4537
4537
|
var exports_storage = {};
|
|
@@ -17496,7 +17496,8 @@ import * as fs4 from "fs";
|
|
|
17496
17496
|
// src/shared/migration/hook-names.ts
|
|
17497
17497
|
var HOOK_NAME_MAP = {
|
|
17498
17498
|
"anthropic-auto-compact": "anthropic-context-window-limit-recovery",
|
|
17499
|
-
"sisyphus-orchestrator": "
|
|
17499
|
+
"sisyphus-orchestrator": "architect",
|
|
17500
|
+
"sisyphus-junior-notepad": "mouse-notepad",
|
|
17500
17501
|
"empty-message-sanitizer": null
|
|
17501
17502
|
};
|
|
17502
17503
|
function migrateHookNames(hooks) {
|
|
@@ -18876,7 +18877,7 @@ async function isCallerOrchestrator(sessionID, client) {
|
|
|
18876
18877
|
if (isSqliteBackend() && client) {
|
|
18877
18878
|
try {
|
|
18878
18879
|
const nearest2 = await findNearestMessageWithFieldsFromSDK(client, sessionID);
|
|
18879
|
-
return getAgentConfigKey(nearest2?.agent ?? "") === "
|
|
18880
|
+
return getAgentConfigKey(nearest2?.agent ?? "") === "architect";
|
|
18880
18881
|
} catch (error) {
|
|
18881
18882
|
log("[session-utils] SDK orchestrator check failed", { sessionID, error: String(error) });
|
|
18882
18883
|
return false;
|
|
@@ -18967,6 +18968,7 @@ function getCurrentPaneId() {
|
|
|
18967
18968
|
var {spawn: spawn4 } = globalThis.Bun;
|
|
18968
18969
|
|
|
18969
18970
|
// src/tools/interactive-bash/tmux-path-resolver.ts
|
|
18971
|
+
init_logger();
|
|
18970
18972
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
18971
18973
|
var tmuxPath = null;
|
|
18972
18974
|
var initPromise = null;
|
|
@@ -19021,7 +19023,9 @@ function getCachedTmuxPath() {
|
|
|
19021
19023
|
function startBackgroundCheck() {
|
|
19022
19024
|
if (!initPromise) {
|
|
19023
19025
|
initPromise = getTmuxPath();
|
|
19024
|
-
initPromise.catch(() => {
|
|
19026
|
+
initPromise.catch((err) => {
|
|
19027
|
+
log("[interactive-bash] Background tmux path resolution failed:", err);
|
|
19028
|
+
});
|
|
19025
19029
|
}
|
|
19026
19030
|
}
|
|
19027
19031
|
|
|
@@ -19251,6 +19255,13 @@ async function spawnTmuxPane(sessionId, description, config, serverUrl, targetPa
|
|
|
19251
19255
|
}
|
|
19252
19256
|
return { success: true, paneId };
|
|
19253
19257
|
}
|
|
19258
|
+
// src/shared/tool-guards.ts
|
|
19259
|
+
function isReadTool(toolName) {
|
|
19260
|
+
return toolName.toLowerCase() === "read";
|
|
19261
|
+
}
|
|
19262
|
+
function isWriteTool(toolName) {
|
|
19263
|
+
return toolName.toLowerCase() === "write";
|
|
19264
|
+
}
|
|
19254
19265
|
// src/shared/transcript-path.ts
|
|
19255
19266
|
import { join as join15 } from "path";
|
|
19256
19267
|
var TRANSCRIPT_DIR = join15(getClaudeConfigDir(), "transcripts");
|
|
@@ -19405,6 +19416,9 @@ function createAgentUsageReminderHook(_ctx) {
|
|
|
19405
19416
|
event: eventHandler
|
|
19406
19417
|
};
|
|
19407
19418
|
}
|
|
19419
|
+
// src/hooks/anthropic-context-window-limit-recovery/executor.ts
|
|
19420
|
+
init_logger();
|
|
19421
|
+
|
|
19408
19422
|
// src/hooks/anthropic-context-window-limit-recovery/aggressive-truncation-strategy.ts
|
|
19409
19423
|
init_logger();
|
|
19410
19424
|
|
|
@@ -20269,7 +20283,9 @@ async function runAggressiveTruncationStrategy(params) {
|
|
|
20269
20283
|
variant: aggressiveResult.sufficient ? "success" : "warning",
|
|
20270
20284
|
duration: 4000
|
|
20271
20285
|
}
|
|
20272
|
-
}).catch(() => {
|
|
20286
|
+
}).catch((err) => {
|
|
20287
|
+
log("[auto-compact] Toast failed:", err);
|
|
20288
|
+
});
|
|
20273
20289
|
log("[auto-compact] aggressive truncation completed", aggressiveResult);
|
|
20274
20290
|
if (aggressiveResult.sufficient) {
|
|
20275
20291
|
clearSessionState(params.autoCompactState, params.sessionID);
|
|
@@ -20283,7 +20299,9 @@ async function runAggressiveTruncationStrategy(params) {
|
|
|
20283
20299
|
},
|
|
20284
20300
|
query: { directory: params.directory }
|
|
20285
20301
|
});
|
|
20286
|
-
} catch {
|
|
20302
|
+
} catch (err) {
|
|
20303
|
+
log("[auto-compact] Failed to send continue prompt:", err);
|
|
20304
|
+
}
|
|
20287
20305
|
}, 500);
|
|
20288
20306
|
return { handled: true, nextTruncateAttempt };
|
|
20289
20307
|
}
|
|
@@ -20294,6 +20312,12 @@ async function runAggressiveTruncationStrategy(params) {
|
|
|
20294
20312
|
});
|
|
20295
20313
|
return { handled: false, nextTruncateAttempt };
|
|
20296
20314
|
}
|
|
20315
|
+
// src/hooks/anthropic-context-window-limit-recovery/summarize-retry-strategy.ts
|
|
20316
|
+
init_logger();
|
|
20317
|
+
|
|
20318
|
+
// src/hooks/anthropic-context-window-limit-recovery/empty-content-recovery.ts
|
|
20319
|
+
init_logger();
|
|
20320
|
+
|
|
20297
20321
|
// src/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.ts
|
|
20298
20322
|
var IGNORE_TYPES2 = new Set(["thinking", "redacted_thinking", "meta"]);
|
|
20299
20323
|
var TOOL_TYPES2 = new Set(["tool", "tool_use", "tool_result"]);
|
|
@@ -20440,7 +20464,9 @@ async function fixEmptyMessages(params) {
|
|
|
20440
20464
|
variant: "error",
|
|
20441
20465
|
duration: 5000
|
|
20442
20466
|
}
|
|
20443
|
-
}).catch(() => {
|
|
20467
|
+
}).catch((err) => {
|
|
20468
|
+
log("[auto-compact] Toast failed:", err);
|
|
20469
|
+
});
|
|
20444
20470
|
return false;
|
|
20445
20471
|
}
|
|
20446
20472
|
if (result.fixed) {
|
|
@@ -20451,7 +20477,9 @@ async function fixEmptyMessages(params) {
|
|
|
20451
20477
|
variant: "warning",
|
|
20452
20478
|
duration: 3000
|
|
20453
20479
|
}
|
|
20454
|
-
}).catch(() => {
|
|
20480
|
+
}).catch((err) => {
|
|
20481
|
+
log("[auto-compact] Toast failed:", err);
|
|
20482
|
+
});
|
|
20455
20483
|
}
|
|
20456
20484
|
return result.fixed;
|
|
20457
20485
|
}
|
|
@@ -20481,7 +20509,9 @@ async function fixEmptyMessages(params) {
|
|
|
20481
20509
|
variant: "error",
|
|
20482
20510
|
duration: 5000
|
|
20483
20511
|
}
|
|
20484
|
-
}).catch(() => {
|
|
20512
|
+
}).catch((err) => {
|
|
20513
|
+
log("[auto-compact] Toast failed:", err);
|
|
20514
|
+
});
|
|
20485
20515
|
return false;
|
|
20486
20516
|
}
|
|
20487
20517
|
for (const messageID of emptyMessageIds) {
|
|
@@ -20506,7 +20536,9 @@ async function fixEmptyMessages(params) {
|
|
|
20506
20536
|
variant: "warning",
|
|
20507
20537
|
duration: 3000
|
|
20508
20538
|
}
|
|
20509
|
-
}).catch(() => {
|
|
20539
|
+
}).catch((err) => {
|
|
20540
|
+
log("[auto-compact] Toast failed:", err);
|
|
20541
|
+
});
|
|
20510
20542
|
}
|
|
20511
20543
|
return fixed;
|
|
20512
20544
|
}
|
|
@@ -20537,7 +20569,9 @@ async function runSummarizeRetryStrategy(params) {
|
|
|
20537
20569
|
variant: "error",
|
|
20538
20570
|
duration: 1e4
|
|
20539
20571
|
}
|
|
20540
|
-
}).catch(() => {
|
|
20572
|
+
}).catch((err) => {
|
|
20573
|
+
log("[auto-compact] Toast failed:", err);
|
|
20574
|
+
});
|
|
20541
20575
|
return;
|
|
20542
20576
|
}
|
|
20543
20577
|
}
|
|
@@ -20560,7 +20594,9 @@ async function runSummarizeRetryStrategy(params) {
|
|
|
20560
20594
|
variant: "warning",
|
|
20561
20595
|
duration: 3000
|
|
20562
20596
|
}
|
|
20563
|
-
}).catch(() => {
|
|
20597
|
+
}).catch((err) => {
|
|
20598
|
+
log("[auto-compact] Toast failed:", err);
|
|
20599
|
+
});
|
|
20564
20600
|
const summarizeBody = { providerID, modelID, auto: true };
|
|
20565
20601
|
await params.client.session.summarize({
|
|
20566
20602
|
path: { id: params.sessionID },
|
|
@@ -20584,7 +20620,9 @@ async function runSummarizeRetryStrategy(params) {
|
|
|
20584
20620
|
variant: "warning",
|
|
20585
20621
|
duration: 3000
|
|
20586
20622
|
}
|
|
20587
|
-
}).catch(() => {
|
|
20623
|
+
}).catch((err) => {
|
|
20624
|
+
log("[auto-compact] Toast failed:", err);
|
|
20625
|
+
});
|
|
20588
20626
|
}
|
|
20589
20627
|
}
|
|
20590
20628
|
clearSessionState(params.autoCompactState, params.sessionID);
|
|
@@ -20595,7 +20633,9 @@ async function runSummarizeRetryStrategy(params) {
|
|
|
20595
20633
|
variant: "error",
|
|
20596
20634
|
duration: 5000
|
|
20597
20635
|
}
|
|
20598
|
-
}).catch(() => {
|
|
20636
|
+
}).catch((err) => {
|
|
20637
|
+
log("[auto-compact] Toast failed:", err);
|
|
20638
|
+
});
|
|
20599
20639
|
}
|
|
20600
20640
|
// src/hooks/anthropic-context-window-limit-recovery/executor.ts
|
|
20601
20641
|
async function executeCompact(sessionID, msg, autoCompactState, client, directory, experimental) {
|
|
@@ -20607,7 +20647,9 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
|
|
|
20607
20647
|
variant: "warning",
|
|
20608
20648
|
duration: 5000
|
|
20609
20649
|
}
|
|
20610
|
-
}).catch(() => {
|
|
20650
|
+
}).catch((err) => {
|
|
20651
|
+
log("[auto-compact] Toast failed:", err);
|
|
20652
|
+
});
|
|
20611
20653
|
return;
|
|
20612
20654
|
}
|
|
20613
20655
|
autoCompactState.compactionInProgress.add(sessionID);
|
|
@@ -20883,7 +20925,9 @@ function createAnthropicContextWindowLimitRecoveryHook(ctx, options) {
|
|
|
20883
20925
|
variant: "warning",
|
|
20884
20926
|
duration: 3000
|
|
20885
20927
|
}
|
|
20886
|
-
}).catch(() => {
|
|
20928
|
+
}).catch((err) => {
|
|
20929
|
+
log("[auto-compact] Toast failed:", err);
|
|
20930
|
+
});
|
|
20887
20931
|
const timeoutID = setTimeout(() => {
|
|
20888
20932
|
pendingCompactionTimeoutBySession.delete(sessionID);
|
|
20889
20933
|
executeCompact(sessionID, { providerID, modelID }, autoCompactState, ctx.client, ctx.directory, experimental);
|
|
@@ -20934,7 +20978,9 @@ function createAnthropicContextWindowLimitRecoveryHook(ctx, options) {
|
|
|
20934
20978
|
variant: "warning",
|
|
20935
20979
|
duration: 3000
|
|
20936
20980
|
}
|
|
20937
|
-
}).catch(() => {
|
|
20981
|
+
}).catch((err) => {
|
|
20982
|
+
log("[auto-compact] Toast failed:", err);
|
|
20983
|
+
});
|
|
20938
20984
|
await executeCompact(sessionID, { providerID, modelID }, autoCompactState, ctx.client, ctx.directory, experimental);
|
|
20939
20985
|
}
|
|
20940
20986
|
};
|
|
@@ -21178,7 +21224,7 @@ You have an active work plan with incomplete tasks. Continue working.
|
|
|
21178
21224
|
RULES:
|
|
21179
21225
|
- **FIRST**: Read the plan file NOW to check exact current progress \u2014 count remaining \`- [ ]\` tasks
|
|
21180
21226
|
- Proceed without asking for permission
|
|
21181
|
-
-
|
|
21227
|
+
- **IMMEDIATELY AFTER COMPLETION**: Mark \`- [ ]\` \u2192 \`- [x]\` in the plan file. This is MANDATORY \u2014 unmarked checkboxes = lost progress.
|
|
21182
21228
|
- Use the notepad at .matrixx/notepads/{PLAN_NAME}/ to record learnings
|
|
21183
21229
|
- Do not stop until all tasks are complete
|
|
21184
21230
|
- If blocked, document the blocker and move to the next task`;
|
|
@@ -21250,7 +21296,7 @@ ${createSystemDirective(SystemDirectiveTypes.DELEGATION_REQUIRED)}
|
|
|
21250
21296
|
|
|
21251
21297
|
**STOP. YOU ARE VIOLATING ORCHESTRATOR PROTOCOL.**
|
|
21252
21298
|
|
|
21253
|
-
You (
|
|
21299
|
+
You (Architect) are attempting to directly modify a file outside \`.matrixx/\`.
|
|
21254
21300
|
|
|
21255
21301
|
**Path attempted:** $FILE_PATH
|
|
21256
21302
|
|
|
@@ -21376,7 +21422,7 @@ async function getLastAgentFromSession(sessionID, client) {
|
|
|
21376
21422
|
|
|
21377
21423
|
// src/hooks/architect/event-handler.ts
|
|
21378
21424
|
var CONTINUATION_COOLDOWN_MS = 5000;
|
|
21379
|
-
function
|
|
21425
|
+
function createArchitectEventHandler(input) {
|
|
21380
21426
|
const { ctx, options, sessions, getState } = input;
|
|
21381
21427
|
return async ({ event }) => {
|
|
21382
21428
|
const props = event.properties;
|
|
@@ -21450,6 +21496,7 @@ function createAtlasEventHandler(input) {
|
|
|
21450
21496
|
const progress = getPlanProgress(missionState.active_plan);
|
|
21451
21497
|
if (progress.isComplete) {
|
|
21452
21498
|
log(`[${HOOK_NAME}] Mission complete`, { sessionID, plan: missionState.plan_name });
|
|
21499
|
+
clearMissionState(ctx.directory);
|
|
21453
21500
|
return;
|
|
21454
21501
|
}
|
|
21455
21502
|
const now = Date.now();
|
|
@@ -21591,15 +21638,24 @@ Read(".matrixx/plans/${planName}.md")
|
|
|
21591
21638
|
Count exactly: how many \`- [ ]\` remain? How many \`- [x]\` completed?
|
|
21592
21639
|
This is YOUR ground truth. Use it to decide what comes next.
|
|
21593
21640
|
|
|
21594
|
-
**STEP 7: MARK COMPLETION IN PLAN FILE (IMMEDIATELY)**
|
|
21641
|
+
**STEP 7: MARK COMPLETION IN PLAN FILE (IMMEDIATELY \u2014 NON-NEGOTIABLE)**
|
|
21595
21642
|
|
|
21596
|
-
|
|
21643
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
21597
21644
|
|
|
21598
|
-
|
|
21599
|
-
- Change \`- [ ]\` to \`- [x]\` for the completed task
|
|
21600
|
-
- Use \`Edit\` tool to modify the checkbox
|
|
21645
|
+
**CRITICAL: THIS STEP IS MANDATORY. Skipping it = progress lost.**
|
|
21601
21646
|
|
|
21602
|
-
|
|
21647
|
+
RIGHT NOW \u2014 Do not delay. Verification passed \u2192 Mark IMMEDIATELY.
|
|
21648
|
+
|
|
21649
|
+
1. Open plan file: \`.matrixx/plans/${planName}.md\`
|
|
21650
|
+
2. Find the \`- [ ]\` checkbox for the completed task
|
|
21651
|
+
3. Change \`- [ ]\` \u2192 \`- [x]\` using the \`Edit\` tool
|
|
21652
|
+
4. Verify the edit was applied
|
|
21653
|
+
|
|
21654
|
+
**WARNING**: If you skip this step, the task appears UNDONE.
|
|
21655
|
+
The system counts \`- [x]\` checkboxes to track progress.
|
|
21656
|
+
No checkbox = No progress = Work wasted.
|
|
21657
|
+
|
|
21658
|
+
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
21603
21659
|
|
|
21604
21660
|
**STEP 8: COMMIT ATOMIC UNIT**
|
|
21605
21661
|
|
|
@@ -21700,6 +21756,9 @@ function createToolExecuteAfterHandler(input) {
|
|
|
21700
21756
|
const missionState = readMissionState(ctx.directory);
|
|
21701
21757
|
if (missionState) {
|
|
21702
21758
|
const progress = getPlanProgress(missionState.active_plan);
|
|
21759
|
+
if (progress.isComplete) {
|
|
21760
|
+
return;
|
|
21761
|
+
}
|
|
21703
21762
|
if (toolInput.sessionID && !missionState.session_ids?.includes(toolInput.sessionID)) {
|
|
21704
21763
|
appendSessionId(ctx.directory, toolInput.sessionID);
|
|
21705
21764
|
log(`[${HOOK_NAME}] Appended session to mission`, {
|
|
@@ -21778,8 +21837,8 @@ ${prompt}`;
|
|
|
21778
21837
|
};
|
|
21779
21838
|
}
|
|
21780
21839
|
|
|
21781
|
-
// src/hooks/architect/
|
|
21782
|
-
function
|
|
21840
|
+
// src/hooks/architect/architect-hook.ts
|
|
21841
|
+
function createArchitectHook(ctx, options) {
|
|
21783
21842
|
const sessions = new Map;
|
|
21784
21843
|
const pendingFilePaths = new Map;
|
|
21785
21844
|
function getState(sessionID) {
|
|
@@ -21791,7 +21850,7 @@ function createAtlasHook(ctx, options) {
|
|
|
21791
21850
|
return state3;
|
|
21792
21851
|
}
|
|
21793
21852
|
return {
|
|
21794
|
-
handler:
|
|
21853
|
+
handler: createArchitectEventHandler({ ctx, options, sessions, getState }),
|
|
21795
21854
|
"tool.execute.before": createToolExecuteBeforeHandler({ ctx, pendingFilePaths }),
|
|
21796
21855
|
"tool.execute.after": createToolExecuteAfterHandler({ ctx, pendingFilePaths })
|
|
21797
21856
|
};
|
|
@@ -21870,6 +21929,22 @@ function findSlashCommandPartIndex(parts) {
|
|
|
21870
21929
|
import { existsSync as existsSync22, readdirSync as readdirSync10, readFileSync as readFileSync17 } from "fs";
|
|
21871
21930
|
import { basename as basename3, dirname as dirname4, join as join31 } from "path";
|
|
21872
21931
|
|
|
21932
|
+
// src/features/builtin-commands/templates/assembly.ts
|
|
21933
|
+
var ASSEMBLY_TEMPLATE = `Toggle assembly tool availability for the current session.
|
|
21934
|
+
|
|
21935
|
+
This command will:
|
|
21936
|
+
1. Enable the assembly tool \u2014 makes multi-model voting available to the LLM
|
|
21937
|
+
2. Disable the assembly tool \u2014 hides it from the LLM for this session
|
|
21938
|
+
3. Show current assembly tool status
|
|
21939
|
+
|
|
21940
|
+
Usage:
|
|
21941
|
+
/assembly enable \u2014 Show assembly tool
|
|
21942
|
+
/assembly disable \u2014 Hide assembly tool
|
|
21943
|
+
/assembly status \u2014 Show current state
|
|
21944
|
+
|
|
21945
|
+
The change is per-session and clears when the session ends.
|
|
21946
|
+
By default, the assembly tool is enabled and available in every session.`;
|
|
21947
|
+
|
|
21873
21948
|
// src/features/builtin-commands/templates/end-ultrawork.ts
|
|
21874
21949
|
var END_ULTRAWORK_TEMPLATE = `Deactivate ultrawork mode for the current session.
|
|
21875
21950
|
|
|
@@ -23438,6 +23513,51 @@ $ARGUMENTS
|
|
|
23438
23513
|
</user-request>
|
|
23439
23514
|
`;
|
|
23440
23515
|
|
|
23516
|
+
// src/features/builtin-commands/templates/research.ts
|
|
23517
|
+
var RESEARCH_TEMPLATE = `# /research \u2014 Saturation Research Command
|
|
23518
|
+
|
|
23519
|
+
Execute a systematic multi-round research process on the given topic.
|
|
23520
|
+
|
|
23521
|
+
## Usage
|
|
23522
|
+
|
|
23523
|
+
\`\`\`
|
|
23524
|
+
/research <topic> \u2192 Full research (all directions)
|
|
23525
|
+
/research <topic> --scope=code \u2192 Local codebase only
|
|
23526
|
+
/research <topic> --scope=docs \u2192 Documentation only
|
|
23527
|
+
/research <topic> --scope=web \u2192 Web search only
|
|
23528
|
+
/research <topic> --scope=oss \u2192 OSS repos only
|
|
23529
|
+
/research <topic> --max-rounds=3 \u2192 Limit to 3 rounds
|
|
23530
|
+
\`\`\`
|
|
23531
|
+
|
|
23532
|
+
## What Happens
|
|
23533
|
+
|
|
23534
|
+
1. Creates artifact directory: \`.matrixx/research-<topic>-<timestamp>/\`
|
|
23535
|
+
2. Spawns 4 parallel research agents (explore + operator swarms)
|
|
23536
|
+
3. Collects findings, identifies new leads
|
|
23537
|
+
4. Recursively follows leads until convergence (novelty-based stop, max 5 rounds)
|
|
23538
|
+
5. Verifies contested claims by running code
|
|
23539
|
+
6. Produces cited synthesis in \`final-synthesis.md\`
|
|
23540
|
+
|
|
23541
|
+
## Output
|
|
23542
|
+
|
|
23543
|
+
All artifacts written to \`.matrixx/research-<topic>-<timestamp>/\`:
|
|
23544
|
+
- \`round-N-findings.md\` \u2014 per-round findings
|
|
23545
|
+
- \`leads.md\` \u2014 tracked leads
|
|
23546
|
+
- \`contested-claims.md\` \u2014 claims needing verification
|
|
23547
|
+
- \`final-synthesis.md\` \u2014 the final cited synthesis
|
|
23548
|
+
|
|
23549
|
+
## Examples
|
|
23550
|
+
|
|
23551
|
+
\`\`\`
|
|
23552
|
+
/research how does context window management work in this codebase
|
|
23553
|
+
/research React 19 server component patterns --scope=web --max-rounds=3
|
|
23554
|
+
/research authentication middleware best practices --scope=all
|
|
23555
|
+
\`\`\`
|
|
23556
|
+
|
|
23557
|
+
---
|
|
23558
|
+
|
|
23559
|
+
Execute the full research protocol using the ulw-research skill.`;
|
|
23560
|
+
|
|
23441
23561
|
// src/features/builtin-commands/templates/start-work.ts
|
|
23442
23562
|
var START_WORK_TEMPLATE = `You are starting a Morpheus work session.
|
|
23443
23563
|
|
|
@@ -23466,7 +23586,7 @@ var START_WORK_TEMPLATE = `You are starting a Morpheus work session.
|
|
|
23466
23586
|
}
|
|
23467
23587
|
\`\`\`
|
|
23468
23588
|
|
|
23469
|
-
5. **Read the plan file** and start executing tasks according to
|
|
23589
|
+
5. **Read the plan file** and start executing tasks according to architect workflow
|
|
23470
23590
|
|
|
23471
23591
|
## OUTPUT FORMAT
|
|
23472
23592
|
|
|
@@ -23510,7 +23630,7 @@ Reading plan and beginning execution...
|
|
|
23510
23630
|
- The session_id is injected by the hook - use it directly
|
|
23511
23631
|
- Always update mission.json BEFORE starting work
|
|
23512
23632
|
- Read the FULL plan file before delegating any tasks
|
|
23513
|
-
- Follow
|
|
23633
|
+
- Follow architect delegation protocols (7-section format)`;
|
|
23514
23634
|
|
|
23515
23635
|
// src/features/builtin-commands/templates/stop-continuation.ts
|
|
23516
23636
|
var STOP_CONTINUATION_TEMPLATE = `Stop all continuation mechanisms for the current session.
|
|
@@ -23527,6 +23647,22 @@ After running this command:
|
|
|
23527
23647
|
|
|
23528
23648
|
Use this when you need to pause automated continuation and take manual control.`;
|
|
23529
23649
|
|
|
23650
|
+
// src/features/builtin-commands/templates/ultrawork.ts
|
|
23651
|
+
var ULTRAWORK_TEMPLATE = `Toggle ultrawork mode for the current session.
|
|
23652
|
+
|
|
23653
|
+
This command will:
|
|
23654
|
+
1. Enable ultrawork mode \u2014 injects ultrawork mode instructions into every message
|
|
23655
|
+
2. Disable ultrawork mode \u2014 prevents ultrawork mode from activating (even on "ultrawork" keyword)
|
|
23656
|
+
3. Show current ultrawork mode status
|
|
23657
|
+
|
|
23658
|
+
Usage:
|
|
23659
|
+
/ultrawork enable \u2014 Force ultrawork mode on all messages
|
|
23660
|
+
/ultrawork disable \u2014 Block ultrawork mode (keyword ignored)
|
|
23661
|
+
/ultrawork status \u2014 Show current state
|
|
23662
|
+
|
|
23663
|
+
The change is per-session and clears when the session ends.
|
|
23664
|
+
By default, ultrawork mode activates when the LLM or user mentions the "ultrawork" keyword.`;
|
|
23665
|
+
|
|
23530
23666
|
// src/features/builtin-commands/commands.ts
|
|
23531
23667
|
var BUILTIN_COMMAND_DEFINITIONS = {
|
|
23532
23668
|
"init-deep": {
|
|
@@ -23653,6 +23789,39 @@ ${END_ULTRAWORK_TEMPLATE}
|
|
|
23653
23789
|
$ARGUMENTS
|
|
23654
23790
|
</user-request>`,
|
|
23655
23791
|
argumentHint: "[optional follow-up task]"
|
|
23792
|
+
},
|
|
23793
|
+
research: {
|
|
23794
|
+
description: "(builtin) Saturation research with parallel swarms and convergence \u2014 code, docs, web, and OSS repos",
|
|
23795
|
+
template: `<command-instruction>
|
|
23796
|
+
${RESEARCH_TEMPLATE}
|
|
23797
|
+
</command-instruction>
|
|
23798
|
+
|
|
23799
|
+
<user-request>
|
|
23800
|
+
$ARGUMENTS
|
|
23801
|
+
</user-request>`,
|
|
23802
|
+
argumentHint: "<topic> [--scope=code|docs|web|oss|all] [--max-rounds=N]"
|
|
23803
|
+
},
|
|
23804
|
+
assembly: {
|
|
23805
|
+
description: "(builtin) Toggle assembly tool enable/disable for the current session",
|
|
23806
|
+
template: `<command-instruction>
|
|
23807
|
+
${ASSEMBLY_TEMPLATE}
|
|
23808
|
+
</command-instruction>
|
|
23809
|
+
|
|
23810
|
+
<user-request>
|
|
23811
|
+
$ARGUMENTS
|
|
23812
|
+
</user-request>`,
|
|
23813
|
+
argumentHint: "[enable|disable|status]"
|
|
23814
|
+
},
|
|
23815
|
+
ultrawork: {
|
|
23816
|
+
description: "(builtin) Toggle ultrawork mode on/off for the current session",
|
|
23817
|
+
template: `<command-instruction>
|
|
23818
|
+
${ULTRAWORK_TEMPLATE}
|
|
23819
|
+
</command-instruction>
|
|
23820
|
+
|
|
23821
|
+
<user-request>
|
|
23822
|
+
$ARGUMENTS
|
|
23823
|
+
</user-request>`,
|
|
23824
|
+
argumentHint: "[enable|disable|status]"
|
|
23656
23825
|
}
|
|
23657
23826
|
};
|
|
23658
23827
|
function loadBuiltinCommands(disabledCommands) {
|
|
@@ -30252,17 +30421,306 @@ Fix these inline when possible. For bulk fixes, run \`bun run lint:fix\`.
|
|
|
30252
30421
|
| Report done without running verification | Never trust \u2014 always verify |
|
|
30253
30422
|
`
|
|
30254
30423
|
};
|
|
30424
|
+
// src/features/builtin-skills/skills/remove-ai-slops.ts
|
|
30425
|
+
var REMOVE_AI_SLOPS_SKILL_NAME = "remove-ai-slops";
|
|
30426
|
+
var REMOVE_AI_SLOPS_SKILL_DESCRIPTION = "Detects and removes AI-generated code smells across 7 categories: verbose comments, redundant error handling, over-engineered patterns, generic AI phrasing, cargo-cult boilerplate, padding/verbosity, weird codegen artifacts. Hybrid mode: scan first, then guide fixes. Triggers: 'cleanup code', 'remove ai slop', 'code cleanup', 'fix ai code', 'de-ai', 'remove ai comments', 'clean ai smells'.";
|
|
30427
|
+
var removeAiSlopsSkill = {
|
|
30428
|
+
name: REMOVE_AI_SLOPS_SKILL_NAME,
|
|
30429
|
+
description: REMOVE_AI_SLOPS_SKILL_DESCRIPTION,
|
|
30430
|
+
allowedTools: [
|
|
30431
|
+
"read",
|
|
30432
|
+
"write",
|
|
30433
|
+
"edit",
|
|
30434
|
+
"grep",
|
|
30435
|
+
"glob",
|
|
30436
|
+
"ast_grep_search",
|
|
30437
|
+
"ast_grep_replace",
|
|
30438
|
+
"lsp_diagnostics",
|
|
30439
|
+
"lsp_find_references"
|
|
30440
|
+
],
|
|
30441
|
+
template: `# Remove AI Slops \u2014 Code Smell Detector & Fixer
|
|
30442
|
+
|
|
30443
|
+
Detect and fix AI-generated code smells across 7 categories. Operates in **hybrid mode**: scan first to identify issues, then guide you through fixes one finding at a time.
|
|
30444
|
+
|
|
30445
|
+
## Phase 0: Scope & Setup
|
|
30446
|
+
|
|
30447
|
+
Determine scope:
|
|
30448
|
+
- If no files specified, scan recently changed files (\`git diff --name-only HEAD~1\`)
|
|
30449
|
+
- If a file/dir is specified, scan those
|
|
30450
|
+
- If \`--all\` flag, scan entire \`src/\` directory
|
|
30451
|
+
|
|
30452
|
+
Create \`.matrixx/slop-scan-<timestamp>.md\` for tracking findings.
|
|
30453
|
+
|
|
30454
|
+
## Phase 1: Scan for Slop
|
|
30455
|
+
|
|
30456
|
+
Run all 7 category detectors. For each category, output findings grouped by file with line ranges.
|
|
30457
|
+
|
|
30458
|
+
---
|
|
30459
|
+
|
|
30460
|
+
### Category 1: Verbose Comments
|
|
30461
|
+
|
|
30462
|
+
**Detect**: AST patterns and regex for comments that restate the obvious.
|
|
30463
|
+
|
|
30464
|
+
**Patterns to flag:**
|
|
30465
|
+
- \`// This function does X by doing Y\` \u2014 describes implementation, not intent
|
|
30466
|
+
- \`// Loop through each item\` \u2014 obvious from the \`for\` loop itself
|
|
30467
|
+
- \`// Initialize variable\` \u2014 obvious from \`const x =\`
|
|
30468
|
+
- \`// Return result\` \u2014 obvious from \`return\`
|
|
30469
|
+
- \`// Call the API\` \u2014 obvious from the function call on the next line
|
|
30470
|
+
- \`// Check if condition is true\` \u2014 obvious from \`if\`
|
|
30471
|
+
- Block comments that just repeat what the function signature says
|
|
30472
|
+
|
|
30473
|
+
**Detection:**
|
|
30474
|
+
- Use \`ast_grep_search\` with patterns like:
|
|
30475
|
+
- \`// $COMMENT\` where $COMMENT matches obvious patterns
|
|
30476
|
+
- Function-level JSDoc that adds no info beyond the signature
|
|
30477
|
+
- Use \`grep\` with regex for known AI comment phrases
|
|
30478
|
+
|
|
30479
|
+
**Severity**: LOW (cosmetic, but clutters code)
|
|
30480
|
+
|
|
30481
|
+
---
|
|
30482
|
+
|
|
30483
|
+
### Category 2: Redundant Error Handling
|
|
30484
|
+
|
|
30485
|
+
**Detect**: Error handling patterns that add no value.
|
|
30486
|
+
|
|
30487
|
+
**Patterns to flag:**
|
|
30488
|
+
- Empty catch blocks: \`catch (e) {}\`
|
|
30489
|
+
- Catch-and-rethrow: \`catch(e) { console.error(e); throw e; }\` (re-throw with no added value)
|
|
30490
|
+
- Redundant null checks on already-guarded paths
|
|
30491
|
+
- \`try/catch\` that wraps every single function call individually
|
|
30492
|
+
- \`catch (error: any)\` (TypeScript \u2014 \`unknown\` preferred)
|
|
30493
|
+
|
|
30494
|
+
**Detection:**
|
|
30495
|
+
- Use \`ast_grep_search\` for \`catch($$$) { $$$ }\` and analyze body
|
|
30496
|
+
- \`ast_grep_search\` for \`if ($VAR != null)\` patterns on already-checked variables
|
|
30497
|
+
- Use \`lsp_find_references\` to check if null checks are redundant
|
|
30498
|
+
|
|
30499
|
+
**Severity**: MEDIUM (adds noise, but doesn't break things)
|
|
30500
|
+
|
|
30501
|
+
---
|
|
30502
|
+
|
|
30503
|
+
### Category 3: Over-Engineered Patterns
|
|
30504
|
+
|
|
30505
|
+
**Detect**: Unnecessary abstraction layers and overly complex patterns.
|
|
30506
|
+
|
|
30507
|
+
**Patterns to flag:**
|
|
30508
|
+
- Abstract factory factory or other nested indirection
|
|
30509
|
+
- AbstractSingletonProxyBean-style pattern names
|
|
30510
|
+
- Single-implementation interfaces (unless part of a public API contract)
|
|
30511
|
+
- Builder pattern for objects with 1-3 simple properties
|
|
30512
|
+
- Strategy pattern with a single strategy
|
|
30513
|
+
- Decorator patterns around every function
|
|
30514
|
+
- Excessive generic constraints (\`<T extends X extends Y extends Z>\`)
|
|
30515
|
+
|
|
30516
|
+
**Detection:**
|
|
30517
|
+
- Use \`lsp_find_references\` to check if interfaces have exactly 1 implementation
|
|
30518
|
+
- \`ast_grep_search\` for class names containing "Abstract", "Factory", "Builder", "Manager"
|
|
30519
|
+
- \`grep\` for pattern names like \`*Abstract*\`, \`*Factory*\`, \`*Proxy*\`, \`*Singleton*\`
|
|
30520
|
+
- Check for builder functions/classes with very few properties
|
|
30521
|
+
|
|
30522
|
+
**Severity**: HIGH (indicates misunderstanding of the codebase)
|
|
30523
|
+
|
|
30524
|
+
---
|
|
30525
|
+
|
|
30526
|
+
### Category 4: Generic AI Phrasing
|
|
30527
|
+
|
|
30528
|
+
**Detect**: Code comments and documentation with AI-typical phrasing.
|
|
30529
|
+
|
|
30530
|
+
**Phrases to flag (regex case-insensitive):**
|
|
30531
|
+
- "It's worth noting that"
|
|
30532
|
+
- "It is important to note"
|
|
30533
|
+
- "It is important to mention"
|
|
30534
|
+
- "Let's step through this"
|
|
30535
|
+
- "In conclusion"
|
|
30536
|
+
- "As previously mentioned"
|
|
30537
|
+
- "It should be noted that"
|
|
30538
|
+
- "It's crucial to understand"
|
|
30539
|
+
- "Let's break this down"
|
|
30540
|
+
- "The beauty of this approach"
|
|
30541
|
+
- "This is a classic example of"
|
|
30542
|
+
- "Essentially, what this does is"
|
|
30543
|
+
- "In the world of"
|
|
30544
|
+
- "When it comes to"
|
|
30545
|
+
- "One might argue that"
|
|
30546
|
+
- "It goes without saying"
|
|
30547
|
+
|
|
30548
|
+
**Detection:**
|
|
30549
|
+
- Use \`grep -i\` with regex patterns for each phrase in \`*.ts\` and \`*.md\` files
|
|
30550
|
+
- Focus comments, docstrings, and inline documentation
|
|
30551
|
+
|
|
30552
|
+
**Severity**: LOW (cosmetic, but unprofessional)
|
|
30553
|
+
|
|
30554
|
+
---
|
|
30555
|
+
|
|
30556
|
+
### Category 5: Cargo-Cult Boilerplate
|
|
30557
|
+
|
|
30558
|
+
**Detect**: Patterns copied without understanding.
|
|
30559
|
+
|
|
30560
|
+
**Patterns to flag:**
|
|
30561
|
+
- Excessive defensive programming (checking the same condition at multiple levels)
|
|
30562
|
+
- \`try/catch\` with \`console.error\` and rethrow in every function
|
|
30563
|
+
- Identical error handling block copy-pasted across multiple functions
|
|
30564
|
+
- Redundant type guards on already-typed parameters
|
|
30565
|
+
- \`if (process.env.NODE_ENV !== 'production')\` debug logging patterns
|
|
30566
|
+
- \`// TODO: implement\` stubs in production code
|
|
30567
|
+
- Mock implementations that replicate the real implementation exactly
|
|
30568
|
+
|
|
30569
|
+
**Detection:**
|
|
30570
|
+
- Use \`grep\` to find repeated identical code blocks across files
|
|
30571
|
+
- \`ast_grep_search\` for try/catch patterns in every function of a file
|
|
30572
|
+
- \`grep\` for TODO/FIXME/HACK comments in non-test files
|
|
30573
|
+
- Check for \`console.log\` statements in production code
|
|
30574
|
+
|
|
30575
|
+
**Severity**: MEDIUM (code bloat, maintenance burden)
|
|
30576
|
+
|
|
30577
|
+
---
|
|
30578
|
+
|
|
30579
|
+
### Category 6: Padding/Verbosity
|
|
30580
|
+
|
|
30581
|
+
**Detect**: Unnecessary code that adds no information.
|
|
30582
|
+
|
|
30583
|
+
**Patterns to flag:**
|
|
30584
|
+
- Empty \`@returns\` tags in JSDoc
|
|
30585
|
+
- \`@param {string} name - The name\` \u2014 repeats the obvious
|
|
30586
|
+
- Redundant type annotations: \`const x: string = "hello"\` (type is obvious)
|
|
30587
|
+
- Unnecessary intermediate variables: \`const result = doSomething(); return result;\` when \`return doSomething()\` works
|
|
30588
|
+
- \`const x = y as SomeType\` when y is already SomeType
|
|
30589
|
+
- \`() => { return x }\` when \`() => x\` works
|
|
30590
|
+
- Destructuring with unused variables
|
|
30591
|
+
- \`export default\` for a single export (prefer named)
|
|
30592
|
+
- Variables assigned but never read
|
|
30593
|
+
|
|
30594
|
+
**Detection:**
|
|
30595
|
+
- Use \`ast_grep_search\` for \`const $VAR = $EXPR as $TYPE\` where $VAR is typed
|
|
30596
|
+
- \`ast_grep_search\` for \`() => { return { $$$ } }\`
|
|
30597
|
+
- \`grep\` for \`@returns\` without description in JSDoc
|
|
30598
|
+
- \`lsp_diagnostics\` will catch unused variables
|
|
30599
|
+
|
|
30600
|
+
**Severity**: LOW (cosmetic, minor bloat)
|
|
30601
|
+
|
|
30602
|
+
---
|
|
30603
|
+
|
|
30604
|
+
### Category 7: Weird Codegen Artifacts
|
|
30605
|
+
|
|
30606
|
+
**Detect**: Inconsistencies that suggest AI-generated code was inserted without review.
|
|
30607
|
+
|
|
30608
|
+
**Patterns to flag:**
|
|
30609
|
+
- Mix of import styles in the same file (CommonJS + ESM, default + named)
|
|
30610
|
+
- Inconsistent error handling within the same file (some async/await, some .then())
|
|
30611
|
+
- Mixed indentation or naming conventions in the same file
|
|
30612
|
+
- Unused imports (LSP will catch these)
|
|
30613
|
+
- \`import * as\` that imports everything but only uses one thing
|
|
30614
|
+
- Code that doesn't follow the file's established patterns
|
|
30615
|
+
- Types/interfaces defined inline instead of in a shared types file
|
|
30616
|
+
- Magic numbers and strings that should be constants
|
|
30617
|
+
- Language-mismatched conventions (e.g., Java-style getters/setters in TypeScript)
|
|
30618
|
+
|
|
30619
|
+
**Detection:**
|
|
30620
|
+
- Use \`grep\` for mixed module systems in the same file
|
|
30621
|
+
- \`lsp_diagnostics\` for unused imports
|
|
30622
|
+
- \`ast_grep_search\` for inline type definitions that should be shared
|
|
30623
|
+
- Manual review of file-level pattern consistency
|
|
30624
|
+
|
|
30625
|
+
**Severity**: MEDIUM-HIGH (indicates un-reviewed code)
|
|
30626
|
+
|
|
30627
|
+
---
|
|
30628
|
+
|
|
30629
|
+
## Phase 2: Report Findings
|
|
30630
|
+
|
|
30631
|
+
After scanning all categories, present a structured report:
|
|
30632
|
+
|
|
30633
|
+
\`\`\`markdown
|
|
30634
|
+
# AI Slop Scan Results
|
|
30635
|
+
|
|
30636
|
+
Scanned: <files/dirs>
|
|
30637
|
+
Total findings: <count>
|
|
30638
|
+
|
|
30639
|
+
| Category | Count | Critical | High | Medium | Low |
|
|
30640
|
+
|----------|-------|----------|------|--------|-----|
|
|
30641
|
+
| 1. Verbose Comments | N | - | - | - | N |
|
|
30642
|
+
| 2. Redundant Error Handling | N | - | N | N | - |
|
|
30643
|
+
| ... | ... | ... | ... | ... | ... |
|
|
30644
|
+
|
|
30645
|
+
## Findings by File
|
|
30646
|
+
|
|
30647
|
+
### <file-path.ts>
|
|
30648
|
+
|
|
30649
|
+
#### [HIGH] Category 3: Over-Engineered \u2014 AbstractUserFactoryManager
|
|
30650
|
+
- **Location**: line 42-89
|
|
30651
|
+
- **Current**: \`class AbstractUserFactoryManager<T extends ...>\`
|
|
30652
|
+
- **Suggestion**: Replace with simple function \`createUser()\`
|
|
30653
|
+
- **Severity**: HIGH
|
|
30654
|
+
- **Status**: PENDING
|
|
30655
|
+
|
|
30656
|
+
#### [MED] Category 2: Redundant Error Handling
|
|
30657
|
+
- **Location**: line 15-18
|
|
30658
|
+
- **Current**:
|
|
30659
|
+
\`\`\`typescript
|
|
30660
|
+
try {
|
|
30661
|
+
await doSomething()
|
|
30662
|
+
} catch (e) {
|
|
30663
|
+
console.error(e)
|
|
30664
|
+
throw e
|
|
30665
|
+
}
|
|
30666
|
+
\`\`\`
|
|
30667
|
+
- **Suggestion**: Remove try/catch \u2014 caller handles errors. Or add meaningful error context.
|
|
30668
|
+
- **Severity**: MEDIUM
|
|
30669
|
+
- **Status**: PENDING
|
|
30670
|
+
|
|
30671
|
+
... (repeat for all findings)
|
|
30672
|
+
\`\`\`
|
|
30673
|
+
|
|
30674
|
+
## Phase 3: Guided Fix
|
|
30675
|
+
|
|
30676
|
+
For each finding, present the user with options:
|
|
30677
|
+
|
|
30678
|
+
1. **Apply fix** \u2014 Show the proposed change as a diff, ask for confirmation
|
|
30679
|
+
2. **Skip** \u2014 Mark as reviewed and acceptable
|
|
30680
|
+
3. **Snooze** \u2014 Skip for now but flag for later review
|
|
30681
|
+
4. **Adjust** \u2014 User provides alternative fix
|
|
30682
|
+
|
|
30683
|
+
Apply fixes using the \`edit\` tool only after user confirmation.
|
|
30684
|
+
|
|
30685
|
+
**Fix application pattern:**
|
|
30686
|
+
1. Present the proposed replacement code
|
|
30687
|
+
2. Wait for user response (\`y\`/\`n\`/\`skip\`/\`adjust\`)
|
|
30688
|
+
3. On approval: \`edit\` the file
|
|
30689
|
+
4. Run \`lsp_diagnostics\` on the file to verify no new errors
|
|
30690
|
+
5. Mark finding as FIXED / SKIPPED
|
|
30691
|
+
|
|
30692
|
+
**Batch support**: User can say "apply all LOW" or "apply all for file X" to batch-approve.
|
|
30693
|
+
|
|
30694
|
+
## Phase 4: Verify
|
|
30695
|
+
|
|
30696
|
+
After all fixes applied:
|
|
30697
|
+
1. \`bun run typecheck\` \u2014 no new type errors
|
|
30698
|
+
2. \`bun run lint\` \u2014 no new lint errors
|
|
30699
|
+
3. Verify no changes broke tests for affected files
|
|
30700
|
+
|
|
30701
|
+
Write final summary to \`.matrixx/slop-scan-<timestamp>.md\`.
|
|
30702
|
+
|
|
30703
|
+
## Rules
|
|
30704
|
+
|
|
30705
|
+
- NEVER auto-apply fixes without user approval
|
|
30706
|
+
- ALWAYS show the proposed change before applying
|
|
30707
|
+
- ALWAYS run \`lsp_diagnostics\` after each edit
|
|
30708
|
+
- Scan ALL 7 categories every time \u2014 don't skip categories
|
|
30709
|
+
- Flag findings only once (deduplicate across rounds)
|
|
30710
|
+
- For \`--all\` mode, process files in batches of 5 to avoid overload
|
|
30711
|
+
- If a pattern is clearly intentional (pre-existing convention), skip it`
|
|
30712
|
+
};
|
|
30255
30713
|
// src/features/builtin-skills/skills/review-work.ts
|
|
30256
30714
|
var REVIEW_WORK_SKILL_NAME = "review-work";
|
|
30257
|
-
var REVIEW_WORK_SKILL_DESCRIPTION = "Post-implementation review orchestrator. Launches
|
|
30715
|
+
var REVIEW_WORK_SKILL_DESCRIPTION = "Post-implementation review orchestrator. Launches 6 parallel background sub-agents: Oracle (goal/constraint verification), Oracle (code quality), Oracle (security), unspecified-high (hands-on QA execution), unspecified-high (context mining from GitHub/git/Slack/Notion), unspecified-high (AI slop detection). All must pass for review to pass. MUST USE after completing any significant implementation work. Triggers: 'review work', 'review my work', 'review changes', 'QA my work', 'verify implementation', 'check my work', 'validate changes', 'post-implementation review'.";
|
|
30258
30716
|
var reviewWorkSkill = {
|
|
30259
30717
|
name: REVIEW_WORK_SKILL_NAME,
|
|
30260
30718
|
description: REVIEW_WORK_SKILL_DESCRIPTION,
|
|
30261
30719
|
template: `# Review Work - 5-Agent Parallel Review Orchestrator
|
|
30262
30720
|
|
|
30263
|
-
Launch
|
|
30721
|
+
Launch 6 specialized sub-agents in parallel to review completed implementation work from every angle. All 6 must pass for the review to pass. If even ONE fails, the review fails.
|
|
30264
30722
|
|
|
30265
|
-
The
|
|
30723
|
+
The 6 agents cover complementary concerns - together they form a comprehensive review that no single reviewer could match:
|
|
30266
30724
|
|
|
30267
30725
|
| # | Agent | Type | Role | Focus Level |
|
|
30268
30726
|
|---|-------|------|------|-------------|
|
|
@@ -30271,6 +30729,7 @@ The 5 agents cover complementary concerns - together they form a comprehensive r
|
|
|
30271
30729
|
| 3 | Code Reviewer | Oracle | Is the code well-written? | MAIN |
|
|
30272
30730
|
| 4 | Security Auditor | Oracle | Is it secure? | SUB |
|
|
30273
30731
|
| 5 | Context Miner | unspecified-high | Did we miss any context? | MAIN |
|
|
30732
|
+
| 6 | AI Slop Detector | unspecified-high | Remove AI-generated code smells | SUP |
|
|
30274
30733
|
|
|
30275
30734
|
---
|
|
30276
30735
|
|
|
@@ -30604,9 +31063,54 @@ OUTPUT FORMAT:
|
|
|
30604
31063
|
|
|
30605
31064
|
---
|
|
30606
31065
|
|
|
31066
|
+
### Agent 6: AI Slop Detection (unspecified-high) - SUP
|
|
31067
|
+
|
|
31068
|
+
\`\`\`
|
|
31069
|
+
task(
|
|
31070
|
+
category="unspecified-high",
|
|
31071
|
+
run_in_background=true,
|
|
31072
|
+
load_skills=["remove-ai-slops"],
|
|
31073
|
+
description="Detect AI-generated code smells in changes",
|
|
31074
|
+
prompt="""
|
|
31075
|
+
<review_type>AI SLOP DETECTION (supplementary)</review_type>
|
|
31076
|
+
|
|
31077
|
+
<original_goal>
|
|
31078
|
+
{GOAL}
|
|
31079
|
+
</original_goal>
|
|
31080
|
+
|
|
31081
|
+
<changed_files>
|
|
31082
|
+
{CHANGED_FILES}
|
|
31083
|
+
</changed_files>
|
|
31084
|
+
|
|
31085
|
+
<background>
|
|
31086
|
+
{BACKGROUND}
|
|
31087
|
+
</background>
|
|
31088
|
+
|
|
31089
|
+
Review the changed files for AI-generated code smells. Load and follow the \`remove-ai-slops\` skill.
|
|
31090
|
+
|
|
31091
|
+
Run the scan across all 7 categories:
|
|
31092
|
+
1. Verbose Comments \u2014 comments that restate the obvious
|
|
31093
|
+
2. Redundant Error Handling \u2014 catch-and-rethrow, empty catches, redundant null checks
|
|
31094
|
+
3. Over-Engineered Patterns \u2014 unnecessary abstractions, single-use interfaces
|
|
31095
|
+
4. Generic AI Phrasing \u2014 "It's worth noting...", "Let's step through..."
|
|
31096
|
+
5. Cargo-Cult Boilerplate \u2014 repeated identical blocks, TODO stubs
|
|
31097
|
+
6. Padding/Verbosity \u2014 empty JSDoc, redundant type annotations
|
|
31098
|
+
7. Weird Codegen Artifacts \u2014 mixed import styles, inconsistent patterns
|
|
31099
|
+
|
|
31100
|
+
OUTPUT FORMAT:
|
|
31101
|
+
<verdict>PASS or FAIL (FAIL only if HIGH severity issues found)</verdict>
|
|
31102
|
+
<severity>NONE / LOW / MEDIUM / HIGH</severity>
|
|
31103
|
+
<summary>1-3 sentence overall assessment</summary>
|
|
31104
|
+
<findings>- [CATEGORY_N] [HIGH/MEDIUM/LOW] Description - File:path:line - Current / Suggestion</findings>
|
|
31105
|
+
<recommendations>Suggested fixes for findings. Empty if PASS.</recommendations>
|
|
31106
|
+
""")
|
|
31107
|
+
\`\`\`
|
|
31108
|
+
|
|
31109
|
+
---
|
|
31110
|
+
|
|
30607
31111
|
## Phase 2: Wait & Collect
|
|
30608
31112
|
|
|
30609
|
-
After launching all
|
|
31113
|
+
After launching all 6 agents in one turn, **end your response**. Wait for system notifications as each agent completes.
|
|
30610
31114
|
|
|
30611
31115
|
Collect via \`background_output(task_id="...")\` as each completes. Track verdicts:
|
|
30612
31116
|
|
|
@@ -30617,14 +31121,15 @@ Collect via \`background_output(task_id="...")\` as each completes. Track verdic
|
|
|
30617
31121
|
| 3. Code Quality | pending | - |
|
|
30618
31122
|
| 4. Security | pending | - |
|
|
30619
31123
|
| 5. Context Mining | pending | - |
|
|
31124
|
+
| 6. AI Slop Detection | pending | - |
|
|
30620
31125
|
|
|
30621
|
-
Do NOT deliver the final report until ALL
|
|
31126
|
+
Do NOT deliver the final report until ALL 6 have completed.
|
|
30622
31127
|
|
|
30623
31128
|
---
|
|
30624
31129
|
|
|
30625
31130
|
## Phase 3: Deliver Verdict
|
|
30626
31131
|
|
|
30627
|
-
ALL
|
|
31132
|
+
ALL 6 returned PASS \u2192 **REVIEW PASSED**
|
|
30628
31133
|
ANY returned FAIL \u2192 **REVIEW FAILED**
|
|
30629
31134
|
|
|
30630
31135
|
Final report format:
|
|
@@ -30641,6 +31146,7 @@ Final report format:
|
|
|
30641
31146
|
| 3 | Code Quality | Oracle | PASS/FAIL | HIGH/MED/LOW |
|
|
30642
31147
|
| 4 | Security (supplementary) | Oracle | PASS/FAIL | Severity |
|
|
30643
31148
|
| 5 | Context Mining | unspecified-high | PASS/FAIL | HIGH/MED/LOW |
|
|
31149
|
+
| 6 | AI Slop Detection (supplementary) | unspecified-high | PASS/FAIL | NONE/LOW/MED/HIGH |
|
|
30644
31150
|
|
|
30645
31151
|
## Blocking Issues
|
|
30646
31152
|
[Aggregated from all agents - deduplicated, prioritized]
|
|
@@ -31828,6 +32334,252 @@ NEVER write tests AFTER implementation \u2014 write them BEFORE
|
|
|
31828
32334
|
\`\`\`
|
|
31829
32335
|
`
|
|
31830
32336
|
};
|
|
32337
|
+
// src/features/builtin-skills/skills/ulw-research.ts
|
|
32338
|
+
var ULW_RESEARCH_SKILL_NAME = "ulw-research";
|
|
32339
|
+
var ULW_RESEARCH_SKILL_DESCRIPTION = "Saturation research orchestrator. Spawns parallel explore/librarian swarms across code, docs, web, and OSS repos. Recursively follows EXPAND leads until convergence (novelty-based, max 5 rounds). Proves contested claims by running code. Returns cited synthesis. Writes artifacts to .matrixx/. Triggers: 'research', 'deep dive', 'investigate', 'explore codebase', 'find how', 'saturation research', 'ulw research'.";
|
|
32340
|
+
var ulwResearchSkill = {
|
|
32341
|
+
name: ULW_RESEARCH_SKILL_NAME,
|
|
32342
|
+
description: ULW_RESEARCH_SKILL_DESCRIPTION,
|
|
32343
|
+
agent: "oracle",
|
|
32344
|
+
allowedTools: [
|
|
32345
|
+
"read",
|
|
32346
|
+
"write",
|
|
32347
|
+
"edit",
|
|
32348
|
+
"bash",
|
|
32349
|
+
"grep",
|
|
32350
|
+
"glob",
|
|
32351
|
+
"look_at",
|
|
32352
|
+
"delegate_agent",
|
|
32353
|
+
"websearch_web_search_exa",
|
|
32354
|
+
"grep_app_searchGitHub",
|
|
32355
|
+
"context7_resolve-library-id",
|
|
32356
|
+
"context7_query-docs"
|
|
32357
|
+
],
|
|
32358
|
+
template: `# ULW-Research \u2014 Saturation Research Orchestrator
|
|
32359
|
+
|
|
32360
|
+
Run a systematic, multi-round research process that converges on comprehensive coverage.
|
|
32361
|
+
|
|
32362
|
+
## Inputs
|
|
32363
|
+
|
|
32364
|
+
- **TOPIC**: The research question or topic (from user message)
|
|
32365
|
+
- **SCOPE**: Optional scope narrowing (--scope=code|docs|web|oss|all, default: all)
|
|
32366
|
+
- **MAX_ROUNDS**: Optional (--max-rounds=N, default: 5, hard limit: 5)
|
|
32367
|
+
|
|
32368
|
+
## Artifact Directory
|
|
32369
|
+
|
|
32370
|
+
Create \`.matrixx/research-<slug>-<timestamp>/\` for all outputs:
|
|
32371
|
+
- \`meta.md\` \u2014 Research metadata (topic, scope, start time, max rounds)
|
|
32372
|
+
- \`round-<N>-findings.md\` \u2014 Per-round combined findings
|
|
32373
|
+
- \`leads.md\` \u2014 Tracked EXPAND leads across rounds (status: pending/explored/exhausted)
|
|
32374
|
+
- \`contested-claims.md\` \u2014 Claims that need code verification
|
|
32375
|
+
- \`convergence-log.md\` \u2014 Novelty scores and decisions per round
|
|
32376
|
+
- \`final-synthesis.md\` \u2014 Cited synthesis (final deliverable)
|
|
32377
|
+
|
|
32378
|
+
All artifacts are written via the \`write\` tool. The next session can resume from artifacts if interrupted.
|
|
32379
|
+
|
|
32380
|
+
## Phase 0: Parse and Seed
|
|
32381
|
+
|
|
32382
|
+
1. Create artifact directory: \`.matrixx/research-<topic-slug>-<YYYYMMDD-HHmmss>/\`
|
|
32383
|
+
2. Write \`meta.md\` with topic, timestamp, scope, max rounds
|
|
32384
|
+
3. Initialize \`leads.md\` with seed queries extracted from the topic:
|
|
32385
|
+
- 1-3 code-local search terms
|
|
32386
|
+
- 1-3 documentation/Context7 search terms
|
|
32387
|
+
- 1-3 web search terms
|
|
32388
|
+
- 1-3 OSS repository search terms
|
|
32389
|
+
4. Write round 0 to \`convergence-log.md\`
|
|
32390
|
+
|
|
32391
|
+
## Phase 1: Parallel Swarm (per round)
|
|
32392
|
+
|
|
32393
|
+
For each round, spawn 4 parallel background agents. Each agent gets ONLY its direction-specific query (not the full research state).
|
|
32394
|
+
|
|
32395
|
+
### Direction 1: Local Code (explore agent)
|
|
32396
|
+
|
|
32397
|
+
\`\`\`
|
|
32398
|
+
task(
|
|
32399
|
+
subagent_type="explore",
|
|
32400
|
+
run_in_background=true,
|
|
32401
|
+
description="Research code: <lead>",
|
|
32402
|
+
prompt="""
|
|
32403
|
+
Search the local codebase for implementations, patterns, and references related to: <CURRENT_LEAD>
|
|
32404
|
+
|
|
32405
|
+
Use grep, glob, and read tools. Find:
|
|
32406
|
+
- Relevant source files and their key functions
|
|
32407
|
+
- Architectural patterns used
|
|
32408
|
+
- Configuration and usage examples
|
|
32409
|
+
- Related tests that reveal intended behavior
|
|
32410
|
+
|
|
32411
|
+
OUTPUT: List of findings with file paths, line numbers, and brief descriptions.
|
|
32412
|
+
Flag any claims that seem contested or unverifiable by reading alone.
|
|
32413
|
+
""")
|
|
32414
|
+
\`\`\`
|
|
32415
|
+
|
|
32416
|
+
### Direction 2: Documentation (explore agent)
|
|
32417
|
+
|
|
32418
|
+
\`\`\`
|
|
32419
|
+
task(
|
|
32420
|
+
subagent_type="explore",
|
|
32421
|
+
run_in_background=true,
|
|
32422
|
+
description="Research docs: <lead>",
|
|
32423
|
+
prompt="""
|
|
32424
|
+
Research documentation and knowledge base files related to: <CURRENT_LEAD>
|
|
32425
|
+
|
|
32426
|
+
Read AGENTS.md files, README.md, doc/ directories, and inline documentation.
|
|
32427
|
+
Use context7_resolve-library-id and context7_query-docs for library/framework docs.
|
|
32428
|
+
|
|
32429
|
+
OUTPUT: Structured findings from docs with source attribution.
|
|
32430
|
+
Flag any claims that contradict other sources.
|
|
32431
|
+
""")
|
|
32432
|
+
\`\`\`
|
|
32433
|
+
|
|
32434
|
+
### Direction 3: Web (operator agent)
|
|
32435
|
+
|
|
32436
|
+
\`\`\`
|
|
32437
|
+
task(
|
|
32438
|
+
subagent_type="operator",
|
|
32439
|
+
run_in_background=true,
|
|
32440
|
+
description="Research web: <lead>",
|
|
32441
|
+
prompt="""
|
|
32442
|
+
Research the web for information about: <CURRENT_LEAD>
|
|
32443
|
+
|
|
32444
|
+
Use websearch_web_search_exa and context7 tools. Find:
|
|
32445
|
+
- Official documentation and blog posts
|
|
32446
|
+
- Technical articles and tutorials
|
|
32447
|
+
- Implementation patterns and best practices
|
|
32448
|
+
- Real-world usage examples
|
|
32449
|
+
|
|
32450
|
+
OUTPUT: Cited findings with URLs, publication dates, and reliability assessment.
|
|
32451
|
+
Flag any contested claims or conflicting information.
|
|
32452
|
+
""")
|
|
32453
|
+
\`\`\`
|
|
32454
|
+
|
|
32455
|
+
### Direction 4: OSS Repos (operator agent)
|
|
32456
|
+
|
|
32457
|
+
\`\`\`
|
|
32458
|
+
task(
|
|
32459
|
+
subagent_type="operator",
|
|
32460
|
+
run_in_background=true,
|
|
32461
|
+
description="Research OSS: <lead>",
|
|
32462
|
+
prompt="""
|
|
32463
|
+
Search open source repositories for implementations related to: <CURRENT_LEAD>
|
|
32464
|
+
|
|
32465
|
+
Use grep_app_searchGitHub to find real-world code patterns.
|
|
32466
|
+
Focus on:
|
|
32467
|
+
- Production implementations in similar projects
|
|
32468
|
+
- Reference implementations or canonical examples
|
|
32469
|
+
- Common patterns and anti-patterns
|
|
32470
|
+
- Version-specific changes or migrations
|
|
32471
|
+
|
|
32472
|
+
OUTPUT: Code patterns with repo URLs, file paths, and usage context.
|
|
32473
|
+
Flag any patterns that seem contested or deprecated.
|
|
32474
|
+
""")
|
|
32475
|
+
\`\`\`
|
|
32476
|
+
|
|
32477
|
+
### Collect Results
|
|
32478
|
+
|
|
32479
|
+
After all 4 agents complete, collect via \`background_output(task_id="...")\`:
|
|
32480
|
+
1. Merge findings into \`round-<N>-findings.md\`
|
|
32481
|
+
2. Extract new EXPAND leads \u2192 append to \`leads.md\`
|
|
32482
|
+
3. Extract contested claims \u2192 append to \`contested-claims.md\`
|
|
32483
|
+
|
|
32484
|
+
## Phase 2: Convergence Check (after each round)
|
|
32485
|
+
|
|
32486
|
+
Evaluate novelty by comparing this round's findings against ALL previous rounds:
|
|
32487
|
+
|
|
32488
|
+
1. **Total items this round**: Count of unique file paths, URLs, code patterns, concepts, library names
|
|
32489
|
+
2. **Novel items**: Items not present in ANY prior round
|
|
32490
|
+
3. **Novelty ratio**: novel_items / total_items_this_round
|
|
32491
|
+
|
|
32492
|
+
**Decision:**
|
|
32493
|
+
- \`novelty_ratio > 0.3\` AND \`round < MAX_ROUNDS\` \u2192 **EXPAND**: Pick top 3 new leads, go to Phase 1
|
|
32494
|
+
- \`novelty_ratio <= 0.3\` \u2192 **STOP**: Natural convergence, proceed to Phase 4
|
|
32495
|
+
- \`round == MAX_ROUNDS\` \u2192 **STOP**: Forced convergence
|
|
32496
|
+
- \`total_items_this_round == 0\` \u2192 **STOP**: No findings
|
|
32497
|
+
|
|
32498
|
+
Record convergence state in \`convergence-log.md\` with round number and novelty score.
|
|
32499
|
+
|
|
32500
|
+
**Lead selection for next round**: Pick top 3 leads prioritizing:
|
|
32501
|
+
- Specificity (concrete file paths or URLs > vague concepts)
|
|
32502
|
+
- Freshness (leads from current round > old leads)
|
|
32503
|
+
- Diversity (one per direction preferred)
|
|
32504
|
+
|
|
32505
|
+
## Phase 3: Contested Claim Verification (if needed)
|
|
32506
|
+
|
|
32507
|
+
If \`contested-claims.md\` is non-empty after convergence, verify each claim:
|
|
32508
|
+
|
|
32509
|
+
\`\`\`
|
|
32510
|
+
task(
|
|
32511
|
+
subagent_type="explore",
|
|
32512
|
+
run_in_background=true,
|
|
32513
|
+
description="Verify claim: <summary>",
|
|
32514
|
+
prompt="""
|
|
32515
|
+
Verify this contested claim by examining code:
|
|
32516
|
+
|
|
32517
|
+
CLAIM: <claim_text>
|
|
32518
|
+
EVIDENCE FOR: <supporting_evidence>
|
|
32519
|
+
EVIDENCE AGAINST: <opposing_evidence>
|
|
32520
|
+
|
|
32521
|
+
Verification steps:
|
|
32522
|
+
1. Find the relevant source files
|
|
32523
|
+
2. Read the implementation
|
|
32524
|
+
3. If runnable: write a minimal test script and execute it
|
|
32525
|
+
4. Determine verdict
|
|
32526
|
+
|
|
32527
|
+
OUTPUT:
|
|
32528
|
+
<verdict>CONFIRMED / REFUTED / INCONCLUSIVE</verdict>
|
|
32529
|
+
<evidence>Specific code or outputs supporting the verdict</evidence>
|
|
32530
|
+
""")
|
|
32531
|
+
\`\`\`
|
|
32532
|
+
|
|
32533
|
+
Max 10 contested claims verified. If none, skip Phase 3.
|
|
32534
|
+
|
|
32535
|
+
## Phase 4: Final Synthesis
|
|
32536
|
+
|
|
32537
|
+
Produce \`final-synthesis.md\`:
|
|
32538
|
+
|
|
32539
|
+
\`\`\`markdown
|
|
32540
|
+
# Research Synthesis: <TOPIC>
|
|
32541
|
+
|
|
32542
|
+
**Date**: <timestamp>
|
|
32543
|
+
**Rounds**: <N> (converged at round <M>)
|
|
32544
|
+
**Sources**: <count> unique sources
|
|
32545
|
+
|
|
32546
|
+
## Executive Summary
|
|
32547
|
+
2-3 paragraphs synthesizing the most important findings.
|
|
32548
|
+
|
|
32549
|
+
## Key Findings
|
|
32550
|
+
|
|
32551
|
+
### Finding 1: <title>
|
|
32552
|
+
- **Status**: CONFIRMED / CONTESTED / INCONCLUSIVE
|
|
32553
|
+
- **Confidence**: HIGH / MEDIUM / LOW
|
|
32554
|
+
- **Summary**: 2-3 sentence synthesis
|
|
32555
|
+
- **Evidence**: Details with citations
|
|
32556
|
+
- **Sources**: [1] path, [2] URL, [3] repo
|
|
32557
|
+
|
|
32558
|
+
(Repeat for all significant findings)
|
|
32559
|
+
|
|
32560
|
+
## Contested Claims Resolution
|
|
32561
|
+
| Claim | Verdict | Evidence |
|
|
32562
|
+
|-------|---------|----------|
|
|
32563
|
+
|
|
32564
|
+
## Research Gaps
|
|
32565
|
+
- Areas not fully covered
|
|
32566
|
+
- Questions remaining unanswered
|
|
32567
|
+
|
|
32568
|
+
## Source Index
|
|
32569
|
+
| # | Type | Source | Key Insight |
|
|
32570
|
+
|---|------|--------|-------------|
|
|
32571
|
+
\`\`\`
|
|
32572
|
+
|
|
32573
|
+
## Rules
|
|
32574
|
+
|
|
32575
|
+
- NEVER fabricate citations \u2014 every claim must reference a real source found during research
|
|
32576
|
+
- ALWAYS write intermediate artifacts \u2014 preserves partial results if interrupted
|
|
32577
|
+
- MAX 5 rounds regardless of novelty
|
|
32578
|
+
- Each background agent gets a FOCUSED prompt \u2014 never dump the entire research state
|
|
32579
|
+
- Contested claims MUST be verified by code execution when possible
|
|
32580
|
+
- The final synthesis must be self-contained without intermediate artifacts
|
|
32581
|
+
- If a round produces zero findings, STOP immediately`
|
|
32582
|
+
};
|
|
31831
32583
|
// src/features/builtin-skills/skills.ts
|
|
31832
32584
|
function createBuiltinSkills(options = {}) {
|
|
31833
32585
|
const { browserProvider = "playwright", disabledSkills: rawDisabledSkills } = options;
|
|
@@ -31840,7 +32592,7 @@ function createBuiltinSkills(options = {}) {
|
|
|
31840
32592
|
} else {
|
|
31841
32593
|
browserSkill = playwrightSkill;
|
|
31842
32594
|
}
|
|
31843
|
-
const skills = [browserSkill, frontendUiUxSkill, gitMasterSkill, devBrowserSkill, dslCoreSkill, dslGrammarSkill, dslCodegenSkill, dslMetamodelSkill, dslToolingSkill, dslTextxEcosystemSkill, dslPyecoreAdvancedSkill, dslModelTransformationSkill, dslTestingSkill, dslValidationSkill, dslCompositionSkill, frontendA11ySkill, frontendBuildToolingSkill, frontendPerfSkill, frontendStateDataSkill, frontendTestingSkill, reactNextjsPatternsSkill, svelteSveltekitPatternsSkill, documentReaderSkill, securityCoreSkill, securitySecretsSkill, securitySastSkill, securityDastSkill, securityDependenciesSkill, securityApiSkill, securityCryptoSkill, securityInfraSkill, securityReviewSkill, tddEnforcerSkill, reviewWorkSkill, qualityGateSkill, softwareDevSkill, matrixxSelfConfigSkill];
|
|
32595
|
+
const skills = [browserSkill, frontendUiUxSkill, gitMasterSkill, devBrowserSkill, dslCoreSkill, dslGrammarSkill, dslCodegenSkill, dslMetamodelSkill, dslToolingSkill, dslTextxEcosystemSkill, dslPyecoreAdvancedSkill, dslModelTransformationSkill, dslTestingSkill, dslValidationSkill, dslCompositionSkill, frontendA11ySkill, frontendBuildToolingSkill, frontendPerfSkill, frontendStateDataSkill, frontendTestingSkill, reactNextjsPatternsSkill, svelteSveltekitPatternsSkill, documentReaderSkill, securityCoreSkill, securitySecretsSkill, securitySastSkill, securityDastSkill, securityDependenciesSkill, securityApiSkill, securityCryptoSkill, securityInfraSkill, securityReviewSkill, tddEnforcerSkill, reviewWorkSkill, qualityGateSkill, softwareDevSkill, matrixxSelfConfigSkill, ulwResearchSkill, removeAiSlopsSkill];
|
|
31844
32596
|
if (!disabledSkills) {
|
|
31845
32597
|
return skills;
|
|
31846
32598
|
}
|
|
@@ -32541,67 +33293,6 @@ function revertPinnedVersion(configPath, failedVersion, originalEntry) {
|
|
|
32541
33293
|
// src/hooks/auto-update-checker/hook.ts
|
|
32542
33294
|
init_logger();
|
|
32543
33295
|
|
|
32544
|
-
// src/cli/config-manager/config-context.ts
|
|
32545
|
-
var configContext = null;
|
|
32546
|
-
function getConfigContext() {
|
|
32547
|
-
if (!configContext) {
|
|
32548
|
-
if (true) {
|
|
32549
|
-
console.warn("[config-context] getConfigContext() called before initConfigContext(); defaulting to CLI paths.");
|
|
32550
|
-
}
|
|
32551
|
-
const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null });
|
|
32552
|
-
configContext = { binary: "opencode", version: null, paths };
|
|
32553
|
-
}
|
|
32554
|
-
return configContext;
|
|
32555
|
-
}
|
|
32556
|
-
function getConfigDir() {
|
|
32557
|
-
return getConfigContext().paths.configDir;
|
|
32558
|
-
}
|
|
32559
|
-
// src/cli/config-manager/bun-install.ts
|
|
32560
|
-
var BUN_INSTALL_TIMEOUT_SECONDS = 60;
|
|
32561
|
-
var BUN_INSTALL_TIMEOUT_MS = BUN_INSTALL_TIMEOUT_SECONDS * 1000;
|
|
32562
|
-
async function runBunInstall() {
|
|
32563
|
-
const result = await runBunInstallWithDetails();
|
|
32564
|
-
return result.success;
|
|
32565
|
-
}
|
|
32566
|
-
async function runBunInstallWithDetails() {
|
|
32567
|
-
try {
|
|
32568
|
-
const proc = Bun.spawn(["bun", "install"], {
|
|
32569
|
-
cwd: getConfigDir(),
|
|
32570
|
-
stdout: "inherit",
|
|
32571
|
-
stderr: "inherit"
|
|
32572
|
-
});
|
|
32573
|
-
let timeoutId;
|
|
32574
|
-
const timeoutPromise = new Promise((resolve3) => {
|
|
32575
|
-
timeoutId = setTimeout(() => resolve3("timeout"), BUN_INSTALL_TIMEOUT_MS);
|
|
32576
|
-
});
|
|
32577
|
-
const exitPromise = proc.exited.then(() => "completed");
|
|
32578
|
-
const result = await Promise.race([exitPromise, timeoutPromise]);
|
|
32579
|
-
clearTimeout(timeoutId);
|
|
32580
|
-
if (result === "timeout") {
|
|
32581
|
-
try {
|
|
32582
|
-
proc.kill();
|
|
32583
|
-
} catch {}
|
|
32584
|
-
return {
|
|
32585
|
-
success: false,
|
|
32586
|
-
timedOut: true,
|
|
32587
|
-
error: `bun install timed out after ${BUN_INSTALL_TIMEOUT_SECONDS} seconds. Try running manually: cd ${getConfigDir()} && bun i`
|
|
32588
|
-
};
|
|
32589
|
-
}
|
|
32590
|
-
if (proc.exitCode !== 0) {
|
|
32591
|
-
return {
|
|
32592
|
-
success: false,
|
|
32593
|
-
error: `bun install failed with exit code ${proc.exitCode}`
|
|
32594
|
-
};
|
|
32595
|
-
}
|
|
32596
|
-
return { success: true };
|
|
32597
|
-
} catch (err) {
|
|
32598
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
32599
|
-
return {
|
|
32600
|
-
success: false,
|
|
32601
|
-
error: `bun install failed: ${message}. Is bun installed? Try: curl -fsSL https://bun.sh/install | bash`
|
|
32602
|
-
};
|
|
32603
|
-
}
|
|
32604
|
-
}
|
|
32605
33296
|
// src/hooks/auto-update-checker/hook/background-update-check.ts
|
|
32606
33297
|
init_logger();
|
|
32607
33298
|
|
|
@@ -32615,7 +33306,9 @@ async function showUpdateAvailableToast(ctx, latestVersion, getToastMessage) {
|
|
|
32615
33306
|
variant: "info",
|
|
32616
33307
|
duration: 8000
|
|
32617
33308
|
}
|
|
32618
|
-
}).catch(() => {
|
|
33309
|
+
}).catch((err) => {
|
|
33310
|
+
log("[auto-update] Update available toast failed:", err);
|
|
33311
|
+
});
|
|
32619
33312
|
log(`[auto-update-checker] Update available toast shown: v${latestVersion}`);
|
|
32620
33313
|
}
|
|
32621
33314
|
async function showAutoUpdatedToast(ctx, oldVersion, newVersion) {
|
|
@@ -32627,14 +33320,36 @@ Restart OpenCode to apply.`,
|
|
|
32627
33320
|
variant: "success",
|
|
32628
33321
|
duration: 8000
|
|
32629
33322
|
}
|
|
32630
|
-
}).catch(() => {
|
|
33323
|
+
}).catch((err) => {
|
|
33324
|
+
log("[auto-update] Auto-updated toast failed:", err);
|
|
33325
|
+
});
|
|
32631
33326
|
log(`[auto-update-checker] Auto-updated toast shown: v${oldVersion} \u2192 v${newVersion}`);
|
|
32632
33327
|
}
|
|
32633
33328
|
|
|
32634
33329
|
// src/hooks/auto-update-checker/hook/background-update-check.ts
|
|
33330
|
+
var BUN_INSTALL_TIMEOUT_SECONDS = 60;
|
|
33331
|
+
var BUN_INSTALL_TIMEOUT_MS = BUN_INSTALL_TIMEOUT_SECONDS * 1000;
|
|
32635
33332
|
async function runBunInstallSafe() {
|
|
32636
33333
|
try {
|
|
32637
|
-
|
|
33334
|
+
const configPaths = getOpenCodeConfigPaths({ binary: "opencode", version: null });
|
|
33335
|
+
const proc = Bun.spawn(["bun", "install"], {
|
|
33336
|
+
cwd: configPaths.configDir,
|
|
33337
|
+
stdout: "inherit",
|
|
33338
|
+
stderr: "inherit"
|
|
33339
|
+
});
|
|
33340
|
+
let timeoutId;
|
|
33341
|
+
const timeoutPromise = new Promise((resolve3) => {
|
|
33342
|
+
timeoutId = setTimeout(() => resolve3(false), BUN_INSTALL_TIMEOUT_MS);
|
|
33343
|
+
});
|
|
33344
|
+
const exitPromise = proc.exited.then(() => true);
|
|
33345
|
+
const result = await Promise.race([exitPromise, timeoutPromise]);
|
|
33346
|
+
clearTimeout(timeoutId);
|
|
33347
|
+
if (!result) {
|
|
33348
|
+
try {
|
|
33349
|
+
proc.kill();
|
|
33350
|
+
} catch {}
|
|
33351
|
+
}
|
|
33352
|
+
return result;
|
|
32638
33353
|
} catch (err) {
|
|
32639
33354
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
32640
33355
|
log("[auto-update-checker] bun install error:", errorMessage);
|
|
@@ -32705,7 +33420,9 @@ ${errorMessages}`,
|
|
|
32705
33420
|
variant: "error",
|
|
32706
33421
|
duration: 1e4
|
|
32707
33422
|
}
|
|
32708
|
-
}).catch(() => {
|
|
33423
|
+
}).catch((err) => {
|
|
33424
|
+
log("[auto-update] Config errors toast failed:", err);
|
|
33425
|
+
});
|
|
32709
33426
|
log(`[auto-update-checker] Config load errors shown: ${errors.length} error(s)`);
|
|
32710
33427
|
clearConfigLoadErrors();
|
|
32711
33428
|
}
|
|
@@ -32714,7 +33431,9 @@ ${errorMessages}`,
|
|
|
32714
33431
|
init_logger();
|
|
32715
33432
|
async function updateAndShowConnectedProvidersCacheStatus(ctx) {
|
|
32716
33433
|
const hadCache = hasConnectedProvidersCache();
|
|
32717
|
-
updateConnectedProvidersCache(ctx.client).catch(() => {
|
|
33434
|
+
updateConnectedProvidersCache(ctx.client).catch((err) => {
|
|
33435
|
+
log("[auto-update] Provider cache update failed:", err);
|
|
33436
|
+
});
|
|
32718
33437
|
if (!hadCache) {
|
|
32719
33438
|
await ctx.client.tui.showToast({
|
|
32720
33439
|
body: {
|
|
@@ -32723,7 +33442,9 @@ async function updateAndShowConnectedProvidersCacheStatus(ctx) {
|
|
|
32723
33442
|
variant: "info",
|
|
32724
33443
|
duration: 8000
|
|
32725
33444
|
}
|
|
32726
|
-
}).catch(() => {
|
|
33445
|
+
}).catch((err) => {
|
|
33446
|
+
log("[auto-update] Providers toast failed:", err);
|
|
33447
|
+
});
|
|
32727
33448
|
log("[auto-update-checker] Connected providers cache toast shown (first run)");
|
|
32728
33449
|
} else {
|
|
32729
33450
|
log("[auto-update-checker] Connected providers cache exists, updating in background");
|
|
@@ -32742,7 +33463,9 @@ async function showModelCacheWarningIfNeeded(ctx) {
|
|
|
32742
33463
|
variant: "warning",
|
|
32743
33464
|
duration: 1e4
|
|
32744
33465
|
}
|
|
32745
|
-
}).catch(() => {
|
|
33466
|
+
}).catch((err) => {
|
|
33467
|
+
log("[auto-update] Model cache warning toast failed:", err);
|
|
33468
|
+
});
|
|
32746
33469
|
log("[auto-update-checker] Model cache warning shown");
|
|
32747
33470
|
}
|
|
32748
33471
|
|
|
@@ -32750,6 +33473,7 @@ async function showModelCacheWarningIfNeeded(ctx) {
|
|
|
32750
33473
|
init_logger();
|
|
32751
33474
|
|
|
32752
33475
|
// src/hooks/auto-update-checker/hook/spinner-toast.ts
|
|
33476
|
+
init_logger();
|
|
32753
33477
|
var SISYPHUS_SPINNER = ["\xB7", "\u2022", "\u25CF", "\u25CB", "\u25CC", "\u25E6", " "];
|
|
32754
33478
|
async function showSpinnerToast(ctx, version, message) {
|
|
32755
33479
|
const totalDuration = 5000;
|
|
@@ -32764,7 +33488,9 @@ async function showSpinnerToast(ctx, version, message) {
|
|
|
32764
33488
|
variant: "info",
|
|
32765
33489
|
duration: frameInterval + 50
|
|
32766
33490
|
}
|
|
32767
|
-
}).catch(() => {
|
|
33491
|
+
}).catch((err) => {
|
|
33492
|
+
log("[auto-update] Spinner toast failed:", err);
|
|
33493
|
+
});
|
|
32768
33494
|
await new Promise((resolve3) => setTimeout(resolve3, frameInterval));
|
|
32769
33495
|
}
|
|
32770
33496
|
}
|
|
@@ -32816,13 +33542,17 @@ v${latestVersion} available. Restart OpenCode to apply.` : "OpenCode is now on S
|
|
|
32816
33542
|
await updateAndShowConnectedProvidersCacheStatus(ctx);
|
|
32817
33543
|
if (localDevVersion) {
|
|
32818
33544
|
if (showStartupToast) {
|
|
32819
|
-
showLocalDevToast(ctx, displayVersion, isMorpheusEnabled).catch(() => {
|
|
33545
|
+
showLocalDevToast(ctx, displayVersion, isMorpheusEnabled).catch((err) => {
|
|
33546
|
+
log("[auto-update] Startup toast failed:", err);
|
|
33547
|
+
});
|
|
32820
33548
|
}
|
|
32821
33549
|
log("[auto-update-checker] Local development mode");
|
|
32822
33550
|
return;
|
|
32823
33551
|
}
|
|
32824
33552
|
if (showStartupToast) {
|
|
32825
|
-
showVersionToast(ctx, displayVersion, getToastMessage(false)).catch(() => {
|
|
33553
|
+
showVersionToast(ctx, displayVersion, getToastMessage(false)).catch((err) => {
|
|
33554
|
+
log("[auto-update] Version toast failed:", err);
|
|
33555
|
+
});
|
|
32826
33556
|
}
|
|
32827
33557
|
runBackgroundUpdateCheck(ctx, autoUpdate, getToastMessage).catch((err) => {
|
|
32828
33558
|
log("[auto-update-checker] Background update check failed:", err);
|
|
@@ -48727,9 +49457,6 @@ function cleanupStaleEntries() {
|
|
|
48727
49457
|
}
|
|
48728
49458
|
}
|
|
48729
49459
|
}
|
|
48730
|
-
function isWriteTool(toolName) {
|
|
48731
|
-
return toolName.toLowerCase() === "write";
|
|
48732
|
-
}
|
|
48733
49460
|
function extractFilePath(args) {
|
|
48734
49461
|
const path10 = args.path ?? args.filePath ?? args.file_path;
|
|
48735
49462
|
return typeof path10 === "string" ? path10 : undefined;
|
|
@@ -48801,12 +49528,6 @@ var CONTENT_CLOSE_TAG = "</content>";
|
|
|
48801
49528
|
var FILE_OPEN_TAG = "<file>";
|
|
48802
49529
|
var FILE_CLOSE_TAG = "</file>";
|
|
48803
49530
|
var OPENCODE_LINE_TRUNCATION_SUFFIX = "... (line truncated to 2000 chars)";
|
|
48804
|
-
function isReadTool(toolName) {
|
|
48805
|
-
return toolName.toLowerCase() === "read";
|
|
48806
|
-
}
|
|
48807
|
-
function isWriteTool2(toolName) {
|
|
48808
|
-
return toolName.toLowerCase() === "write";
|
|
48809
|
-
}
|
|
48810
49531
|
function shouldProcess(config2) {
|
|
48811
49532
|
return config2.hashline_edit?.enabled ?? false;
|
|
48812
49533
|
}
|
|
@@ -48927,7 +49648,7 @@ function createHashlineReadEnhancerHook(_ctx, config2) {
|
|
|
48927
49648
|
return {
|
|
48928
49649
|
"tool.execute.after": async (input, output) => {
|
|
48929
49650
|
if (!isReadTool(input.tool)) {
|
|
48930
|
-
if (
|
|
49651
|
+
if (isWriteTool(input.tool) && typeof output.output === "string" && shouldProcess(config2)) {
|
|
48931
49652
|
await appendWriteHashlineOutput(output);
|
|
48932
49653
|
}
|
|
48933
49654
|
return;
|
|
@@ -48942,6 +49663,9 @@ function createHashlineReadEnhancerHook(_ctx, config2) {
|
|
|
48942
49663
|
}
|
|
48943
49664
|
};
|
|
48944
49665
|
}
|
|
49666
|
+
// src/hooks/interactive-bash-session/hook.ts
|
|
49667
|
+
init_logger();
|
|
49668
|
+
|
|
48945
49669
|
// src/hooks/interactive-bash-session/constants.ts
|
|
48946
49670
|
import { join as join46 } from "path";
|
|
48947
49671
|
var INTERACTIVE_BASH_SESSION_STORAGE = join46(OPENCODE_STORAGE, "interactive-bash-session");
|
|
@@ -49121,7 +49845,9 @@ function createInteractiveBashSessionHook(ctx) {
|
|
|
49121
49845
|
async function killAllTrackedSessionsLocal(state3) {
|
|
49122
49846
|
await killAllTrackedSessions(state3);
|
|
49123
49847
|
for (const sessionId of subagentSessions) {
|
|
49124
|
-
ctx.client.session.abort({ path: { id: sessionId } }).catch(() => {
|
|
49848
|
+
ctx.client.session.abort({ path: { id: sessionId } }).catch((err) => {
|
|
49849
|
+
log("[interactive-bash] Session abort failed:", err);
|
|
49850
|
+
});
|
|
49125
49851
|
}
|
|
49126
49852
|
}
|
|
49127
49853
|
const toolExecuteAfter = async (input, output) => {
|
|
@@ -49950,6 +50676,21 @@ function detectKeywordsWithType(text, agentName, modelID) {
|
|
|
49950
50676
|
function extractPromptText3(parts) {
|
|
49951
50677
|
return parts.filter((p) => p.type === "text").map((p) => p.text || "").join(" ");
|
|
49952
50678
|
}
|
|
50679
|
+
// src/features/ultrawork-state/manager.ts
|
|
50680
|
+
init_logger();
|
|
50681
|
+
var HOOK_NAME4 = "ultrawork-state";
|
|
50682
|
+
var sessionStates = new Map;
|
|
50683
|
+
function getUltraworkState(sessionID) {
|
|
50684
|
+
return sessionStates.get(sessionID);
|
|
50685
|
+
}
|
|
50686
|
+
function enableUltrawork(sessionID) {
|
|
50687
|
+
sessionStates.set(sessionID, "enabled");
|
|
50688
|
+
log(`[${HOOK_NAME4}] Ultrawork enabled for session`, { sessionID });
|
|
50689
|
+
}
|
|
50690
|
+
function disableUltrawork(sessionID) {
|
|
50691
|
+
sessionStates.set(sessionID, "disabled");
|
|
50692
|
+
log(`[${HOOK_NAME4}] Ultrawork disabled for session`, { sessionID });
|
|
50693
|
+
}
|
|
49953
50694
|
// src/hooks/keyword-detector/hook.ts
|
|
49954
50695
|
function createKeywordDetectorHook(ctx, _collector) {
|
|
49955
50696
|
return {
|
|
@@ -49985,8 +50726,15 @@ function createKeywordDetectorHook(ctx, _collector) {
|
|
|
49985
50726
|
return;
|
|
49986
50727
|
}
|
|
49987
50728
|
}
|
|
50729
|
+
const ultraworkState = getUltraworkState(input.sessionID);
|
|
49988
50730
|
const hasUltrawork = detectedKeywords.some((k) => k.type === "ultrawork");
|
|
49989
|
-
|
|
50731
|
+
const isUltraworkBlocked = ultraworkState === "disabled";
|
|
50732
|
+
const isUltraworkForced = ultraworkState === "enabled";
|
|
50733
|
+
if (isUltraworkBlocked) {
|
|
50734
|
+
log(`[keyword-detector] Ultrawork blocked by runtime state`, { sessionID: input.sessionID });
|
|
50735
|
+
return;
|
|
50736
|
+
}
|
|
50737
|
+
if (hasUltrawork || isUltraworkForced) {
|
|
49990
50738
|
log(`[keyword-detector] Ultrawork mode activated`, { sessionID: input.sessionID });
|
|
49991
50739
|
if (output.message.variant === undefined) {
|
|
49992
50740
|
output.message.variant = "max";
|
|
@@ -50074,7 +50822,7 @@ function createLoopStateController(options) {
|
|
|
50074
50822
|
};
|
|
50075
50823
|
const success2 = writeState(directory, state3, stateDir);
|
|
50076
50824
|
if (success2) {
|
|
50077
|
-
log(`[${
|
|
50825
|
+
log(`[${HOOK_NAME5}] Loop started`, {
|
|
50078
50826
|
sessionID,
|
|
50079
50827
|
maxIterations: state3.max_iterations,
|
|
50080
50828
|
completionPromise: state3.completion_promise
|
|
@@ -50089,7 +50837,7 @@ function createLoopStateController(options) {
|
|
|
50089
50837
|
}
|
|
50090
50838
|
const success2 = clearState(directory, stateDir);
|
|
50091
50839
|
if (success2) {
|
|
50092
|
-
log(`[${
|
|
50840
|
+
log(`[${HOOK_NAME5}] Loop cancelled`, { sessionID, iteration: state3.iteration });
|
|
50093
50841
|
}
|
|
50094
50842
|
return success2;
|
|
50095
50843
|
},
|
|
@@ -50185,7 +50933,7 @@ async function detectCompletionInSessionMessages(ctx, options) {
|
|
|
50185
50933
|
return false;
|
|
50186
50934
|
} catch (err) {
|
|
50187
50935
|
setTimeout(() => {
|
|
50188
|
-
log(`[${
|
|
50936
|
+
log(`[${HOOK_NAME5}] Session messages check failed`, {
|
|
50189
50937
|
sessionID: options.sessionID,
|
|
50190
50938
|
error: String(err)
|
|
50191
50939
|
});
|
|
@@ -50266,7 +51014,7 @@ async function verifyCompletion(ctx, options) {
|
|
|
50266
51014
|
query: { directory: options.directory }
|
|
50267
51015
|
});
|
|
50268
51016
|
if (createResult.error || !createResult.data?.id) {
|
|
50269
|
-
log(`[${
|
|
51017
|
+
log(`[${HOOK_NAME5}] Verification session creation failed`);
|
|
50270
51018
|
return { verified: true };
|
|
50271
51019
|
}
|
|
50272
51020
|
verifySessionID = createResult.data.id;
|
|
@@ -50296,20 +51044,22 @@ async function verifyCompletion(ctx, options) {
|
|
|
50296
51044
|
return parseVerificationResponse(assistantText);
|
|
50297
51045
|
}
|
|
50298
51046
|
} catch {
|
|
50299
|
-
log(`[${
|
|
51047
|
+
log(`[${HOOK_NAME5}] Verification poll error`);
|
|
50300
51048
|
}
|
|
50301
51049
|
await new Promise((resolve5) => {
|
|
50302
51050
|
setTimeout(resolve5, pollIntervalMs);
|
|
50303
51051
|
});
|
|
50304
51052
|
}
|
|
50305
|
-
log(`[${
|
|
51053
|
+
log(`[${HOOK_NAME5}] Verification timed out, assuming pass`);
|
|
50306
51054
|
return { verified: true };
|
|
50307
51055
|
} catch (err) {
|
|
50308
|
-
log(`[${
|
|
51056
|
+
log(`[${HOOK_NAME5}] Verification error: ${String(err)}`);
|
|
50309
51057
|
return { verified: true };
|
|
50310
51058
|
} finally {
|
|
50311
51059
|
if (verifySessionID) {
|
|
50312
|
-
ctx.client.session.delete({ path: { id: verifySessionID } }).catch(() => {
|
|
51060
|
+
ctx.client.session.delete({ path: { id: verifySessionID } }).catch((err) => {
|
|
51061
|
+
log("[matrix-loop] Verification session cleanup failed:", err);
|
|
51062
|
+
});
|
|
50313
51063
|
}
|
|
50314
51064
|
}
|
|
50315
51065
|
}
|
|
@@ -50379,7 +51129,7 @@ async function injectContinuationPrompt(ctx, options) {
|
|
|
50379
51129
|
|
|
50380
51130
|
// src/hooks/matrix-loop/matrix-loop-event-handler.ts
|
|
50381
51131
|
async function handleVerificationFailure(ctx, options, state3, sessionID, reason, failedCount) {
|
|
50382
|
-
log(`[${
|
|
51132
|
+
log(`[${HOOK_NAME5}] Verification failed`, { sessionID, reason });
|
|
50383
51133
|
const newState = options.loopState.incrementIteration();
|
|
50384
51134
|
if (newState) {
|
|
50385
51135
|
newState.verification_failed_count = failedCount + 1;
|
|
@@ -50397,7 +51147,7 @@ ${buildContinuationPrompt(newState ?? state3)}`,
|
|
|
50397
51147
|
apiTimeoutMs: options.apiTimeoutMs
|
|
50398
51148
|
});
|
|
50399
51149
|
} catch (err) {
|
|
50400
|
-
log(`[${
|
|
51150
|
+
log(`[${HOOK_NAME5}] Failed to inject verification feedback`, { sessionID, error: String(err) });
|
|
50401
51151
|
}
|
|
50402
51152
|
}
|
|
50403
51153
|
function createMatrixLoopEventHandler(ctx, options) {
|
|
@@ -50409,13 +51159,13 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50409
51159
|
if (!sessionID)
|
|
50410
51160
|
return;
|
|
50411
51161
|
if (inFlightSessions.has(sessionID)) {
|
|
50412
|
-
log(`[${
|
|
51162
|
+
log(`[${HOOK_NAME5}] Skipped: handler in flight`, { sessionID });
|
|
50413
51163
|
return;
|
|
50414
51164
|
}
|
|
50415
51165
|
inFlightSessions.add(sessionID);
|
|
50416
51166
|
try {
|
|
50417
51167
|
if (options.sessionRecovery.isRecovering(sessionID)) {
|
|
50418
|
-
log(`[${
|
|
51168
|
+
log(`[${HOOK_NAME5}] Skipped: in recovery`, { sessionID });
|
|
50419
51169
|
return;
|
|
50420
51170
|
}
|
|
50421
51171
|
const state3 = options.loopState.getState();
|
|
@@ -50427,11 +51177,11 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50427
51177
|
const exists = await options.checkSessionExists(state3.session_id);
|
|
50428
51178
|
if (!exists) {
|
|
50429
51179
|
options.loopState.clear();
|
|
50430
|
-
log(`[${
|
|
51180
|
+
log(`[${HOOK_NAME5}] Cleared orphaned state from deleted session`, { orphanedSessionId: state3.session_id, currentSessionId: sessionID });
|
|
50431
51181
|
return;
|
|
50432
51182
|
}
|
|
50433
51183
|
} catch (err) {
|
|
50434
|
-
log(`[${
|
|
51184
|
+
log(`[${HOOK_NAME5}] Failed to check session existence`, { sessionId: state3.session_id, error: String(err) });
|
|
50435
51185
|
}
|
|
50436
51186
|
}
|
|
50437
51187
|
return;
|
|
@@ -50450,7 +51200,7 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50450
51200
|
messages = Array.isArray(messagesResponse) ? messagesResponse : Array.isArray(responseData) ? responseData : [];
|
|
50451
51201
|
} catch (err) {
|
|
50452
51202
|
setTimeout(() => {
|
|
50453
|
-
log(`[${
|
|
51203
|
+
log(`[${HOOK_NAME5}] Session messages fetch failed`, { sessionID, error: String(err) });
|
|
50454
51204
|
}, 0);
|
|
50455
51205
|
messages = [];
|
|
50456
51206
|
}
|
|
@@ -50463,13 +51213,15 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50463
51213
|
preFetchedMessages: messages
|
|
50464
51214
|
});
|
|
50465
51215
|
if (completionViaTranscript || completionViaApi) {
|
|
50466
|
-
log(`[${
|
|
51216
|
+
log(`[${HOOK_NAME5}] Completion detected!`, { sessionID, iteration: state3.iteration, promise: state3.completion_promise, detectedVia: completionViaTranscript ? "transcript_file" : "session_messages_api" });
|
|
50467
51217
|
if (options.verification?.enabled === true) {
|
|
50468
51218
|
const maxRetries = options.verification?.maxRetries ?? DEFAULT_VERIFICATION_MAX_RETRIES;
|
|
50469
51219
|
const failedCount = state3.verification_failed_count ?? 0;
|
|
50470
51220
|
if (failedCount >= maxRetries) {
|
|
50471
51221
|
options.loopState.clear();
|
|
50472
|
-
await ctx.client.tui.showToast({ body: { title: "Matrix Loop Complete", message: `Force-completed after ${failedCount} failed verification(s)`, variant: "warning", duration: 5000 } }).catch(() => {
|
|
51222
|
+
await ctx.client.tui.showToast({ body: { title: "Matrix Loop Complete", message: `Force-completed after ${failedCount} failed verification(s)`, variant: "warning", duration: 5000 } }).catch((err) => {
|
|
51223
|
+
log("[matrix-loop] Force complete toast failed:", err);
|
|
51224
|
+
});
|
|
50473
51225
|
return;
|
|
50474
51226
|
}
|
|
50475
51227
|
const result = await verifyCompletion(ctx, {
|
|
@@ -50490,26 +51242,32 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50490
51242
|
options.loopState.clear();
|
|
50491
51243
|
const title = state3.ultrawork ? "ULTRAWORK LOOP COMPLETE!" : "Matrix Loop Complete!";
|
|
50492
51244
|
const message = state3.ultrawork ? `JUST ULW ULW! Task completed after ${state3.iteration} iteration(s)` : `Task completed after ${state3.iteration} iteration(s)`;
|
|
50493
|
-
await ctx.client.tui.showToast({ body: { title, message, variant: "success", duration: 5000 } }).catch(() => {
|
|
51245
|
+
await ctx.client.tui.showToast({ body: { title, message, variant: "success", duration: 5000 } }).catch((err) => {
|
|
51246
|
+
log("[matrix-loop] Loop complete toast failed:", err);
|
|
51247
|
+
});
|
|
50494
51248
|
return;
|
|
50495
51249
|
}
|
|
50496
51250
|
if (state3.iteration >= state3.max_iterations) {
|
|
50497
|
-
log(`[${
|
|
51251
|
+
log(`[${HOOK_NAME5}] Max iterations reached`, { sessionID, iteration: state3.iteration, max: state3.max_iterations });
|
|
50498
51252
|
options.loopState.clear();
|
|
50499
|
-
await ctx.client.tui.showToast({ body: { title: "Matrix Loop Stopped", message: `Max iterations (${state3.max_iterations}) reached without completion`, variant: "warning", duration: 5000 } }).catch(() => {
|
|
51253
|
+
await ctx.client.tui.showToast({ body: { title: "Matrix Loop Stopped", message: `Max iterations (${state3.max_iterations}) reached without completion`, variant: "warning", duration: 5000 } }).catch((err) => {
|
|
51254
|
+
log("[matrix-loop] Max iterations toast failed:", err);
|
|
51255
|
+
});
|
|
50500
51256
|
return;
|
|
50501
51257
|
}
|
|
50502
51258
|
const newState = options.loopState.incrementIteration();
|
|
50503
51259
|
if (!newState) {
|
|
50504
|
-
log(`[${
|
|
51260
|
+
log(`[${HOOK_NAME5}] Failed to increment iteration`, { sessionID });
|
|
50505
51261
|
return;
|
|
50506
51262
|
}
|
|
50507
|
-
log(`[${
|
|
50508
|
-
await ctx.client.tui.showToast({ body: { title: "Matrix Loop", message: `Iteration ${newState.iteration}/${newState.max_iterations}`, variant: "info", duration: 2000 } }).catch(() => {
|
|
51263
|
+
log(`[${HOOK_NAME5}] Continuing loop`, { sessionID, iteration: newState.iteration, max: newState.max_iterations });
|
|
51264
|
+
await ctx.client.tui.showToast({ body: { title: "Matrix Loop", message: `Iteration ${newState.iteration}/${newState.max_iterations}`, variant: "info", duration: 2000 } }).catch((err) => {
|
|
51265
|
+
log("[matrix-loop] Iteration toast failed:", err);
|
|
51266
|
+
});
|
|
50509
51267
|
try {
|
|
50510
51268
|
await injectContinuationPrompt(ctx, { sessionID, prompt: buildContinuationPrompt(newState), directory: options.directory, apiTimeoutMs: options.apiTimeoutMs });
|
|
50511
51269
|
} catch (err) {
|
|
50512
|
-
log(`[${
|
|
51270
|
+
log(`[${HOOK_NAME5}] Failed to inject continuation`, { sessionID, error: String(err) });
|
|
50513
51271
|
}
|
|
50514
51272
|
return;
|
|
50515
51273
|
} finally {
|
|
@@ -50523,7 +51281,7 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50523
51281
|
const state3 = options.loopState.getState();
|
|
50524
51282
|
if (state3?.session_id === sessionInfo.id) {
|
|
50525
51283
|
options.loopState.clear();
|
|
50526
|
-
log(`[${
|
|
51284
|
+
log(`[${HOOK_NAME5}] Session deleted, loop cleared`, { sessionID: sessionInfo.id });
|
|
50527
51285
|
}
|
|
50528
51286
|
options.sessionRecovery.clear(sessionInfo.id);
|
|
50529
51287
|
return;
|
|
@@ -50536,7 +51294,7 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50536
51294
|
const state3 = options.loopState.getState();
|
|
50537
51295
|
if (state3?.session_id === sessionID) {
|
|
50538
51296
|
options.loopState.clear();
|
|
50539
|
-
log(`[${
|
|
51297
|
+
log(`[${HOOK_NAME5}] User aborted, loop cleared`, { sessionID });
|
|
50540
51298
|
}
|
|
50541
51299
|
options.sessionRecovery.clear(sessionID);
|
|
50542
51300
|
}
|
|
@@ -50583,7 +51341,7 @@ function createMatrixLoopHook(ctx, options) {
|
|
|
50583
51341
|
// src/hooks/matrix-loop/index.ts
|
|
50584
51342
|
init_storage();
|
|
50585
51343
|
// src/hooks/mouse-notepad/constants.ts
|
|
50586
|
-
var
|
|
51344
|
+
var HOOK_NAME6 = "mouse-notepad";
|
|
50587
51345
|
var NOTEPAD_DIRECTIVE = `
|
|
50588
51346
|
<Work_Context>
|
|
50589
51347
|
## Notepad Location (for recording learnings)
|
|
@@ -50630,14 +51388,14 @@ function createMouseNotepadHook(ctx) {
|
|
|
50630
51388
|
return;
|
|
50631
51389
|
}
|
|
50632
51390
|
output.args.prompt = NOTEPAD_DIRECTIVE + prompt;
|
|
50633
|
-
log(`[${
|
|
51391
|
+
log(`[${HOOK_NAME6}] Injected notepad directive to task`, {
|
|
50634
51392
|
sessionID: input.sessionID
|
|
50635
51393
|
});
|
|
50636
51394
|
}
|
|
50637
51395
|
};
|
|
50638
51396
|
}
|
|
50639
51397
|
// src/hooks/non-interactive-env/constants.ts
|
|
50640
|
-
var
|
|
51398
|
+
var HOOK_NAME7 = "non-interactive-env";
|
|
50641
51399
|
var NON_INTERACTIVE_ENV = {
|
|
50642
51400
|
CI: "true",
|
|
50643
51401
|
DEBIAN_FRONTEND: "noninteractive",
|
|
@@ -50728,7 +51486,7 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
50728
51486
|
}
|
|
50729
51487
|
const envPrefix = buildEnvPrefix(NON_INTERACTIVE_ENV, "unix");
|
|
50730
51488
|
output.args.command = `${envPrefix} ${command}`;
|
|
50731
|
-
log(`[${
|
|
51489
|
+
log(`[${HOOK_NAME7}] Prepended non-interactive env vars to git command`, {
|
|
50732
51490
|
sessionID: input.sessionID,
|
|
50733
51491
|
envPrefix
|
|
50734
51492
|
});
|
|
@@ -50877,7 +51635,9 @@ function createPostCompactionDegradationMonitor(args) {
|
|
|
50877
51635
|
variant: "warning",
|
|
50878
51636
|
duration: 5000
|
|
50879
51637
|
}
|
|
50880
|
-
}).catch(() => {
|
|
51638
|
+
}).catch((err) => {
|
|
51639
|
+
log("[preemptive-compaction] Degradation toast failed:", err);
|
|
51640
|
+
});
|
|
50881
51641
|
await withTimeout2(client.session.summarize({
|
|
50882
51642
|
path: { id: sessionID },
|
|
50883
51643
|
body: { providerID: targetProviderID, modelID: targetModelID },
|
|
@@ -50935,6 +51695,7 @@ function createPostCompactionDegradationMonitor(args) {
|
|
|
50935
51695
|
|
|
50936
51696
|
// src/shared/context-limit-resolver.ts
|
|
50937
51697
|
var DEFAULT_ANTHROPIC_ACTUAL_LIMIT = 200000;
|
|
51698
|
+
var DEFAULT_NON_ANTHROPIC_FALLBACK_LIMIT = 128000;
|
|
50938
51699
|
function isAnthropicProvider2(providerID) {
|
|
50939
51700
|
const normalized = providerID.toLowerCase();
|
|
50940
51701
|
return normalized === "anthropic" || normalized === "google-vertex-anthropic" || normalized === "aws-bedrock-anthropic";
|
|
@@ -50955,7 +51716,7 @@ function resolveActualContextLimit(providerID, modelID, modelCacheState) {
|
|
|
50955
51716
|
return cachedLimit;
|
|
50956
51717
|
return DEFAULT_ANTHROPIC_ACTUAL_LIMIT;
|
|
50957
51718
|
}
|
|
50958
|
-
return modelCacheState?.modelContextLimitsCache?.get(`${providerID}/${modelID}`) ??
|
|
51719
|
+
return modelCacheState?.modelContextLimitsCache?.get(`${providerID}/${modelID}`) ?? DEFAULT_NON_ANTHROPIC_FALLBACK_LIMIT;
|
|
50959
51720
|
}
|
|
50960
51721
|
|
|
50961
51722
|
// src/hooks/preemptive-compaction-trigger.ts
|
|
@@ -51114,7 +51875,7 @@ function createPreemptiveCompactionHook(ctx, pluginConfig = {}, modelCacheState)
|
|
|
51114
51875
|
};
|
|
51115
51876
|
}
|
|
51116
51877
|
// src/hooks/prometheus-md-only/constants.ts
|
|
51117
|
-
var
|
|
51878
|
+
var HOOK_NAME8 = "prometheus-md-only";
|
|
51118
51879
|
var ORACLE_AGENT = "oracle";
|
|
51119
51880
|
var ALLOWED_EXTENSIONS = [".md"];
|
|
51120
51881
|
var BLOCKED_TOOLS = ["Write", "Edit", "write", "edit"];
|
|
@@ -51247,7 +52008,7 @@ function createOracleMdOnlyHook(ctx) {
|
|
|
51247
52008
|
const prompt = output.args.prompt;
|
|
51248
52009
|
if (prompt && !prompt.includes(SYSTEM_DIRECTIVE_PREFIX)) {
|
|
51249
52010
|
output.args.prompt = PLANNING_CONSULT_WARNING + prompt;
|
|
51250
|
-
log(`[${
|
|
52011
|
+
log(`[${HOOK_NAME8}] Injected read-only planning warning to ${toolName}`, {
|
|
51251
52012
|
sessionID: input.sessionID,
|
|
51252
52013
|
tool: toolName,
|
|
51253
52014
|
agent: agentName
|
|
@@ -51263,17 +52024,17 @@ function createOracleMdOnlyHook(ctx) {
|
|
|
51263
52024
|
return;
|
|
51264
52025
|
}
|
|
51265
52026
|
if (!isAllowedFile(filePath, ctx.directory)) {
|
|
51266
|
-
log(`[${
|
|
52027
|
+
log(`[${HOOK_NAME8}] Blocked: Oracle can only write to .matrixx/*.md`, {
|
|
51267
52028
|
sessionID: input.sessionID,
|
|
51268
52029
|
tool: toolName,
|
|
51269
52030
|
filePath,
|
|
51270
52031
|
agent: agentName
|
|
51271
52032
|
});
|
|
51272
|
-
throw new Error(`[${
|
|
52033
|
+
throw new Error(`[${HOOK_NAME8}] ${getAgentDisplayName("oracle")} can only write/edit .md files inside .matrixx/ directory. ` + `Attempted to modify: ${filePath}. ` + `${getAgentDisplayName("oracle")} is a READ-ONLY planner. Use /start-work to execute the plan. ` + `APOLOGIZE TO THE USER, REMIND OF YOUR PLAN WRITING PROCESSES, TELL USER WHAT YOU WILL GOING TO DO AS THE PROCESS, WRITE THE PLAN`);
|
|
51273
52034
|
}
|
|
51274
52035
|
const normalizedPath = filePath.toLowerCase().replace(/\\/g, "/");
|
|
51275
52036
|
if (normalizedPath.includes(".matrixx/plans/") || normalizedPath.includes(".matrixx\\plans\\")) {
|
|
51276
|
-
log(`[${
|
|
52037
|
+
log(`[${HOOK_NAME8}] Injecting workflow reminder for plan write`, {
|
|
51277
52038
|
sessionID: input.sessionID,
|
|
51278
52039
|
tool: toolName,
|
|
51279
52040
|
filePath,
|
|
@@ -51281,7 +52042,7 @@ function createOracleMdOnlyHook(ctx) {
|
|
|
51281
52042
|
});
|
|
51282
52043
|
output.message = (output.message || "") + PROMETHEUS_WORKFLOW_REMINDER;
|
|
51283
52044
|
}
|
|
51284
|
-
log(`[${
|
|
52045
|
+
log(`[${HOOK_NAME8}] Allowed: .matrixx/*.md write permitted`, {
|
|
51285
52046
|
sessionID: input.sessionID,
|
|
51286
52047
|
tool: toolName,
|
|
51287
52048
|
filePath,
|
|
@@ -52014,9 +52775,6 @@ async function resizeImage(base64DataUrl, mimeType, target, deps = {}) {
|
|
|
52014
52775
|
// src/hooks/read-image-resizer/hook.ts
|
|
52015
52776
|
var SUPPORTED_IMAGE_MIMES = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
|
|
52016
52777
|
var TOKEN_DIVISOR = 750;
|
|
52017
|
-
function isReadTool2(toolName) {
|
|
52018
|
-
return toolName.toLowerCase() === "read";
|
|
52019
|
-
}
|
|
52020
52778
|
function asRecord(value) {
|
|
52021
52779
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
52022
52780
|
return null;
|
|
@@ -52100,7 +52858,7 @@ function resolveFilename(attachment, index) {
|
|
|
52100
52858
|
function createReadImageResizerHook(_ctx) {
|
|
52101
52859
|
return {
|
|
52102
52860
|
"tool.execute.after": async (input, output) => {
|
|
52103
|
-
if (!
|
|
52861
|
+
if (!isReadTool(input.tool)) {
|
|
52104
52862
|
return;
|
|
52105
52863
|
}
|
|
52106
52864
|
const sessionModel = getSessionModel(input.sessionID);
|
|
@@ -52837,7 +53595,7 @@ var RETRYABLE_ERROR_PATTERNS = [
|
|
|
52837
53595
|
/(?:^|\s)503(?:\s|$)/,
|
|
52838
53596
|
/(?:^|\s)529(?:\s|$)/
|
|
52839
53597
|
];
|
|
52840
|
-
var
|
|
53598
|
+
var HOOK_NAME9 = "runtime-fallback";
|
|
52841
53599
|
|
|
52842
53600
|
// src/hooks/runtime-fallback/fallback-models.ts
|
|
52843
53601
|
init_logger();
|
|
@@ -52928,7 +53686,7 @@ function getRawFallbackModelsForSession(sessionID, agent, pluginConfig) {
|
|
|
52928
53686
|
if (result)
|
|
52929
53687
|
return result;
|
|
52930
53688
|
}
|
|
52931
|
-
log(`[${
|
|
53689
|
+
log(`[${HOOK_NAME9}] No category/agent fallback models resolved for session`, { sessionID, agent });
|
|
52932
53690
|
return;
|
|
52933
53691
|
}
|
|
52934
53692
|
|
|
@@ -52957,21 +53715,21 @@ function findNextAvailableFallback(state3, fallbackModels, cooldownSeconds) {
|
|
|
52957
53715
|
if (!isModelInCooldown(candidate, state3, cooldownSeconds)) {
|
|
52958
53716
|
return candidate;
|
|
52959
53717
|
}
|
|
52960
|
-
log(`[${
|
|
53718
|
+
log(`[${HOOK_NAME9}] Skipping fallback model in cooldown`, { model: candidate, index: i });
|
|
52961
53719
|
}
|
|
52962
53720
|
return;
|
|
52963
53721
|
}
|
|
52964
53722
|
function prepareFallback(sessionID, state3, fallbackModels, config2) {
|
|
52965
53723
|
if (state3.attemptCount >= config2.max_fallback_attempts) {
|
|
52966
|
-
log(`[${
|
|
53724
|
+
log(`[${HOOK_NAME9}] Max fallback attempts reached`, { sessionID, attempts: state3.attemptCount });
|
|
52967
53725
|
return { success: false, error: "Max fallback attempts reached", maxAttemptsReached: true };
|
|
52968
53726
|
}
|
|
52969
53727
|
const nextModel = findNextAvailableFallback(state3, fallbackModels, config2.cooldown_seconds);
|
|
52970
53728
|
if (!nextModel) {
|
|
52971
|
-
log(`[${
|
|
53729
|
+
log(`[${HOOK_NAME9}] No available fallback models`, { sessionID });
|
|
52972
53730
|
return { success: false, error: "No available fallback models (all in cooldown or exhausted)" };
|
|
52973
53731
|
}
|
|
52974
|
-
log(`[${
|
|
53732
|
+
log(`[${HOOK_NAME9}] Preparing fallback`, {
|
|
52975
53733
|
sessionID,
|
|
52976
53734
|
from: state3.currentModel,
|
|
52977
53735
|
to: nextModel,
|
|
@@ -53049,7 +53807,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53049
53807
|
ctx,
|
|
53050
53808
|
config: config2,
|
|
53051
53809
|
options,
|
|
53052
|
-
sessionStates,
|
|
53810
|
+
sessionStates: sessionStates2,
|
|
53053
53811
|
sessionLastAccess,
|
|
53054
53812
|
sessionRetryInFlight,
|
|
53055
53813
|
sessionAwaitingFallbackResult,
|
|
@@ -53060,9 +53818,9 @@ function createAutoRetryHelpers(deps) {
|
|
|
53060
53818
|
const abortSessionRequest = async (sessionID, source) => {
|
|
53061
53819
|
try {
|
|
53062
53820
|
await ctx.client.session.abort({ path: { id: sessionID } });
|
|
53063
|
-
log(`[${
|
|
53821
|
+
log(`[${HOOK_NAME9}] Aborted in-flight session request (${source})`, { sessionID });
|
|
53064
53822
|
} catch (error51) {
|
|
53065
|
-
log(`[${
|
|
53823
|
+
log(`[${HOOK_NAME9}] Failed to abort in-flight session request (${source})`, {
|
|
53066
53824
|
sessionID,
|
|
53067
53825
|
error: String(error51)
|
|
53068
53826
|
});
|
|
@@ -53082,11 +53840,11 @@ function createAutoRetryHelpers(deps) {
|
|
|
53082
53840
|
return;
|
|
53083
53841
|
const timer = setTimeout(async () => {
|
|
53084
53842
|
sessionFallbackTimeouts.delete(sessionID);
|
|
53085
|
-
const state3 =
|
|
53843
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53086
53844
|
if (!state3)
|
|
53087
53845
|
return;
|
|
53088
53846
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
53089
|
-
log(`[${
|
|
53847
|
+
log(`[${HOOK_NAME9}] Overriding in-flight retry due to session timeout`, { sessionID });
|
|
53090
53848
|
}
|
|
53091
53849
|
await abortSessionRequest(sessionID, "session.timeout");
|
|
53092
53850
|
sessionRetryInFlight.delete(sessionID);
|
|
@@ -53096,7 +53854,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53096
53854
|
const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
|
|
53097
53855
|
if (fallbackModels.length === 0)
|
|
53098
53856
|
return;
|
|
53099
|
-
log(`[${
|
|
53857
|
+
log(`[${HOOK_NAME9}] Session fallback timeout reached`, {
|
|
53100
53858
|
sessionID,
|
|
53101
53859
|
timeoutSeconds: config2.timeout_seconds,
|
|
53102
53860
|
currentModel: state3.currentModel
|
|
@@ -53110,7 +53868,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53110
53868
|
};
|
|
53111
53869
|
const autoRetryWithFallback = async (sessionID, newModel, resolvedAgent, source) => {
|
|
53112
53870
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
53113
|
-
log(`[${
|
|
53871
|
+
log(`[${HOOK_NAME9}] Retry already in flight, skipping (${source})`, { sessionID });
|
|
53114
53872
|
return;
|
|
53115
53873
|
}
|
|
53116
53874
|
const agentSettings = resolvedAgent ? pluginConfig?.agents?.[resolvedAgent] : undefined;
|
|
@@ -53119,8 +53877,8 @@ function createAutoRetryHelpers(deps) {
|
|
|
53119
53877
|
reasoningEffort: agentSettings.reasoningEffort
|
|
53120
53878
|
} : undefined);
|
|
53121
53879
|
if (!retryModelPayload) {
|
|
53122
|
-
log(`[${
|
|
53123
|
-
const state3 =
|
|
53880
|
+
log(`[${HOOK_NAME9}] Invalid model format (missing provider prefix): ${newModel}`);
|
|
53881
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53124
53882
|
if (state3?.pendingFallbackModel) {
|
|
53125
53883
|
state3.pendingFallbackModel = undefined;
|
|
53126
53884
|
}
|
|
@@ -53135,7 +53893,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53135
53893
|
});
|
|
53136
53894
|
const retryParts = getLastUserRetryParts(messagesResp);
|
|
53137
53895
|
if (retryParts.length > 0) {
|
|
53138
|
-
log(`[${
|
|
53896
|
+
log(`[${HOOK_NAME9}] Auto-retrying with fallback model (${source})`, {
|
|
53139
53897
|
sessionID,
|
|
53140
53898
|
model: newModel
|
|
53141
53899
|
});
|
|
@@ -53154,16 +53912,16 @@ function createAutoRetryHelpers(deps) {
|
|
|
53154
53912
|
});
|
|
53155
53913
|
retryDispatched = true;
|
|
53156
53914
|
} else {
|
|
53157
|
-
log(`[${
|
|
53915
|
+
log(`[${HOOK_NAME9}] No user message found for auto-retry (${source})`, { sessionID });
|
|
53158
53916
|
}
|
|
53159
53917
|
} catch (retryError) {
|
|
53160
|
-
log(`[${
|
|
53918
|
+
log(`[${HOOK_NAME9}] Auto-retry failed (${source})`, { sessionID, error: String(retryError) });
|
|
53161
53919
|
} finally {
|
|
53162
53920
|
sessionRetryInFlight.delete(sessionID);
|
|
53163
53921
|
if (!retryDispatched) {
|
|
53164
53922
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
53165
53923
|
clearSessionFallbackTimeout(sessionID);
|
|
53166
|
-
const state3 =
|
|
53924
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53167
53925
|
if (state3?.pendingFallbackModel) {
|
|
53168
53926
|
state3.pendingFallbackModel = undefined;
|
|
53169
53927
|
}
|
|
@@ -53200,7 +53958,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53200
53958
|
let cleanedCount = 0;
|
|
53201
53959
|
for (const [sessionID, lastAccess] of sessionLastAccess.entries()) {
|
|
53202
53960
|
if (now - lastAccess > SESSION_TTL_MS) {
|
|
53203
|
-
|
|
53961
|
+
sessionStates2.delete(sessionID);
|
|
53204
53962
|
sessionLastAccess.delete(sessionID);
|
|
53205
53963
|
sessionRetryInFlight.delete(sessionID);
|
|
53206
53964
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
@@ -53211,7 +53969,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53211
53969
|
}
|
|
53212
53970
|
}
|
|
53213
53971
|
if (cleanedCount > 0) {
|
|
53214
|
-
log(`[${
|
|
53972
|
+
log(`[${HOOK_NAME9}] Cleaned up ${cleanedCount} stale session states`);
|
|
53215
53973
|
}
|
|
53216
53974
|
};
|
|
53217
53975
|
return {
|
|
@@ -53227,12 +53985,12 @@ function createAutoRetryHelpers(deps) {
|
|
|
53227
53985
|
// src/hooks/runtime-fallback/chat-message-handler.ts
|
|
53228
53986
|
init_logger();
|
|
53229
53987
|
function createChatMessageHandler(deps) {
|
|
53230
|
-
const { config: config2, sessionStates, sessionLastAccess } = deps;
|
|
53988
|
+
const { config: config2, sessionStates: sessionStates2, sessionLastAccess } = deps;
|
|
53231
53989
|
return async (input, output) => {
|
|
53232
53990
|
if (!config2.enabled)
|
|
53233
53991
|
return;
|
|
53234
53992
|
const { sessionID } = input;
|
|
53235
|
-
let state3 =
|
|
53993
|
+
let state3 = sessionStates2.get(sessionID);
|
|
53236
53994
|
if (!state3)
|
|
53237
53995
|
return;
|
|
53238
53996
|
sessionLastAccess.set(sessionID, Date.now());
|
|
@@ -53242,19 +54000,19 @@ function createChatMessageHandler(deps) {
|
|
|
53242
54000
|
state3.pendingFallbackModel = undefined;
|
|
53243
54001
|
return;
|
|
53244
54002
|
}
|
|
53245
|
-
log(`[${
|
|
54003
|
+
log(`[${HOOK_NAME9}] Detected manual model change, resetting fallback state`, {
|
|
53246
54004
|
sessionID,
|
|
53247
54005
|
from: state3.currentModel,
|
|
53248
54006
|
to: requestedModel
|
|
53249
54007
|
});
|
|
53250
54008
|
state3 = createFallbackState(requestedModel);
|
|
53251
|
-
|
|
54009
|
+
sessionStates2.set(sessionID, state3);
|
|
53252
54010
|
return;
|
|
53253
54011
|
}
|
|
53254
54012
|
if (state3.currentModel === state3.originalModel)
|
|
53255
54013
|
return;
|
|
53256
54014
|
const activeModel = state3.currentModel;
|
|
53257
|
-
log(`[${
|
|
54015
|
+
log(`[${HOOK_NAME9}] Applying fallback model override`, {
|
|
53258
54016
|
sessionID,
|
|
53259
54017
|
from: input.model,
|
|
53260
54018
|
to: activeModel
|
|
@@ -53441,7 +54199,7 @@ function resolveFallbackBootstrapModel(options) {
|
|
|
53441
54199
|
const agentConfig = options.resolvedAgent && agentConfigs ? agentConfigs[options.resolvedAgent] : undefined;
|
|
53442
54200
|
const agentModel = typeof agentConfig?.model === "string" ? agentConfig.model : undefined;
|
|
53443
54201
|
if (agentModel) {
|
|
53444
|
-
log(`[${
|
|
54202
|
+
log(`[${HOOK_NAME9}] Derived model from agent config for ${options.source}`, {
|
|
53445
54203
|
sessionID: options.sessionID,
|
|
53446
54204
|
agent: options.resolvedAgent,
|
|
53447
54205
|
model: agentModel
|
|
@@ -53452,7 +54210,7 @@ function resolveFallbackBootstrapModel(options) {
|
|
|
53452
54210
|
if (agentCategory) {
|
|
53453
54211
|
const agentCategoryModel = options.pluginConfig?.categories?.[agentCategory]?.model;
|
|
53454
54212
|
if (typeof agentCategoryModel === "string" && agentCategoryModel.length > 0) {
|
|
53455
|
-
log(`[${
|
|
54213
|
+
log(`[${HOOK_NAME9}] Derived model from agent category config for ${options.source}`, {
|
|
53456
54214
|
sessionID: options.sessionID,
|
|
53457
54215
|
agent: options.resolvedAgent,
|
|
53458
54216
|
category: agentCategory,
|
|
@@ -53464,7 +54222,7 @@ function resolveFallbackBootstrapModel(options) {
|
|
|
53464
54222
|
const sessionCategory = SessionCategoryRegistry.get(options.sessionID);
|
|
53465
54223
|
const categoryModel = sessionCategory ? options.pluginConfig?.categories?.[sessionCategory]?.model : undefined;
|
|
53466
54224
|
if (typeof categoryModel === "string" && categoryModel.length > 0) {
|
|
53467
|
-
log(`[${
|
|
54225
|
+
log(`[${HOOK_NAME9}] Derived model from session category config for ${options.source}`, {
|
|
53468
54226
|
sessionID: options.sessionID,
|
|
53469
54227
|
category: sessionCategory,
|
|
53470
54228
|
model: categoryModel
|
|
@@ -53486,13 +54244,15 @@ async function dispatchFallbackRetry(deps, helpers, options) {
|
|
|
53486
54244
|
variant: "warning",
|
|
53487
54245
|
duration: 5000
|
|
53488
54246
|
}
|
|
53489
|
-
}).catch(() => {
|
|
54247
|
+
}).catch((err) => {
|
|
54248
|
+
log("[runtime-fallback] Fallback notification toast failed:", err);
|
|
54249
|
+
});
|
|
53490
54250
|
}
|
|
53491
54251
|
if (result.success && result.newModel) {
|
|
53492
54252
|
await helpers.autoRetryWithFallback(options.sessionID, result.newModel, options.resolvedAgent, options.source);
|
|
53493
54253
|
return;
|
|
53494
54254
|
}
|
|
53495
|
-
log(`[${
|
|
54255
|
+
log(`[${HOOK_NAME9}] Fallback preparation failed`, {
|
|
53496
54256
|
sessionID: options.sessionID,
|
|
53497
54257
|
source: options.source,
|
|
53498
54258
|
error: result.error
|
|
@@ -53540,7 +54300,7 @@ function extractRetryAttempt(statusAttempt, message) {
|
|
|
53540
54300
|
function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
|
|
53541
54301
|
const {
|
|
53542
54302
|
pluginConfig,
|
|
53543
|
-
sessionStates,
|
|
54303
|
+
sessionStates: sessionStates2,
|
|
53544
54304
|
sessionLastAccess,
|
|
53545
54305
|
sessionRetryInFlight
|
|
53546
54306
|
} = deps;
|
|
@@ -53567,26 +54327,26 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
|
|
|
53567
54327
|
sessionStatusRetryKeys.set(sessionID, retryKey);
|
|
53568
54328
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
53569
54329
|
if (timeoutEnabled) {
|
|
53570
|
-
log(`[${
|
|
54330
|
+
log(`[${HOOK_NAME9}] Overriding in-flight retry due to provider auto-retry signal`, {
|
|
53571
54331
|
sessionID,
|
|
53572
54332
|
model
|
|
53573
54333
|
});
|
|
53574
54334
|
await helpers.abortSessionRequest(sessionID, "session.status.retry-signal");
|
|
53575
54335
|
sessionRetryInFlight.delete(sessionID);
|
|
53576
54336
|
} else {
|
|
53577
|
-
log(`[${
|
|
54337
|
+
log(`[${HOOK_NAME9}] session.status retry skipped - retry already in flight`, { sessionID });
|
|
53578
54338
|
return;
|
|
53579
54339
|
}
|
|
53580
54340
|
}
|
|
53581
54341
|
const resolvedAgent = await helpers.resolveAgentForSessionFromContext(sessionID, agent);
|
|
53582
54342
|
const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
|
|
53583
54343
|
if (fallbackModels.length === 0) {
|
|
53584
|
-
if (!
|
|
54344
|
+
if (!sessionStates2.has(sessionID)) {
|
|
53585
54345
|
sessionStatusRetryKeys.delete(sessionID);
|
|
53586
54346
|
}
|
|
53587
54347
|
return;
|
|
53588
54348
|
}
|
|
53589
|
-
let state3 =
|
|
54349
|
+
let state3 = sessionStates2.get(sessionID);
|
|
53590
54350
|
if (!state3) {
|
|
53591
54351
|
const initialModel = resolveFallbackBootstrapModel({
|
|
53592
54352
|
sessionID,
|
|
@@ -53597,29 +54357,29 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
|
|
|
53597
54357
|
});
|
|
53598
54358
|
if (!initialModel) {
|
|
53599
54359
|
sessionStatusRetryKeys.delete(sessionID);
|
|
53600
|
-
log(`[${
|
|
54360
|
+
log(`[${HOOK_NAME9}] session.status retry missing model info, cannot fallback`, { sessionID });
|
|
53601
54361
|
return;
|
|
53602
54362
|
}
|
|
53603
54363
|
state3 = createFallbackState(initialModel);
|
|
53604
|
-
|
|
54364
|
+
sessionStates2.set(sessionID, state3);
|
|
53605
54365
|
}
|
|
53606
54366
|
sessionLastAccess.set(sessionID, Date.now());
|
|
53607
54367
|
if (state3.pendingFallbackModel) {
|
|
53608
54368
|
if (timeoutEnabled) {
|
|
53609
|
-
log(`[${
|
|
54369
|
+
log(`[${HOOK_NAME9}] Clearing pending fallback due to provider auto-retry signal`, {
|
|
53610
54370
|
sessionID,
|
|
53611
54371
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
53612
54372
|
});
|
|
53613
54373
|
state3.pendingFallbackModel = undefined;
|
|
53614
54374
|
} else {
|
|
53615
|
-
log(`[${
|
|
54375
|
+
log(`[${HOOK_NAME9}] session.status retry skipped (pending fallback in progress)`, {
|
|
53616
54376
|
sessionID,
|
|
53617
54377
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
53618
54378
|
});
|
|
53619
54379
|
return;
|
|
53620
54380
|
}
|
|
53621
54381
|
}
|
|
53622
|
-
log(`[${
|
|
54382
|
+
log(`[${HOOK_NAME9}] Detected provider auto-retry signal in session.status`, {
|
|
53623
54383
|
sessionID,
|
|
53624
54384
|
model: state3.currentModel,
|
|
53625
54385
|
retryAttempt: status.attempt
|
|
@@ -53637,13 +54397,13 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
|
|
|
53637
54397
|
|
|
53638
54398
|
// src/hooks/runtime-fallback/event-handler.ts
|
|
53639
54399
|
function createEventHandler(deps, helpers) {
|
|
53640
|
-
const { config: config2, pluginConfig, sessionStates, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionFallbackTimeouts, sessionStatusRetryKeys } = deps;
|
|
54400
|
+
const { config: config2, pluginConfig, sessionStates: sessionStates2, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionFallbackTimeouts, sessionStatusRetryKeys } = deps;
|
|
53641
54401
|
const sessionStatusHandler = createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys);
|
|
53642
54402
|
const cancelledSessions = new Set;
|
|
53643
54403
|
const resetRetryState = (sessionID) => {
|
|
53644
|
-
const state3 =
|
|
54404
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53645
54405
|
if (state3) {
|
|
53646
|
-
|
|
54406
|
+
sessionStates2.set(sessionID, createFallbackState(state3.originalModel));
|
|
53647
54407
|
}
|
|
53648
54408
|
sessionRetryInFlight.delete(sessionID);
|
|
53649
54409
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
@@ -53655,8 +54415,8 @@ function createEventHandler(deps, helpers) {
|
|
|
53655
54415
|
const sessionID = sessionInfo?.id;
|
|
53656
54416
|
const model = sessionInfo?.model;
|
|
53657
54417
|
if (sessionID && model) {
|
|
53658
|
-
log(`[${
|
|
53659
|
-
|
|
54418
|
+
log(`[${HOOK_NAME9}] Session created with model`, { sessionID, model });
|
|
54419
|
+
sessionStates2.set(sessionID, createFallbackState(model));
|
|
53660
54420
|
sessionLastAccess.set(sessionID, Date.now());
|
|
53661
54421
|
}
|
|
53662
54422
|
};
|
|
@@ -53664,9 +54424,9 @@ function createEventHandler(deps, helpers) {
|
|
|
53664
54424
|
const sessionInfo = props?.info;
|
|
53665
54425
|
const sessionID = sessionInfo?.id;
|
|
53666
54426
|
if (sessionID) {
|
|
53667
|
-
log(`[${
|
|
54427
|
+
log(`[${HOOK_NAME9}] Cleaning up session state`, { sessionID });
|
|
53668
54428
|
cancelledSessions.delete(sessionID);
|
|
53669
|
-
|
|
54429
|
+
sessionStates2.delete(sessionID);
|
|
53670
54430
|
sessionLastAccess.delete(sessionID);
|
|
53671
54431
|
sessionRetryInFlight.delete(sessionID);
|
|
53672
54432
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
@@ -53684,7 +54444,7 @@ function createEventHandler(deps, helpers) {
|
|
|
53684
54444
|
}
|
|
53685
54445
|
cancelledSessions.add(sessionID);
|
|
53686
54446
|
resetRetryState(sessionID);
|
|
53687
|
-
log(`[${
|
|
54447
|
+
log(`[${HOOK_NAME9}] Cleared fallback retry state on session.stop`, { sessionID });
|
|
53688
54448
|
};
|
|
53689
54449
|
const handleMessageUpdated = (props) => {
|
|
53690
54450
|
const info = props?.info;
|
|
@@ -53700,23 +54460,23 @@ function createEventHandler(deps, helpers) {
|
|
|
53700
54460
|
return;
|
|
53701
54461
|
if (cancelledSessions.has(sessionID)) {
|
|
53702
54462
|
resetRetryState(sessionID);
|
|
53703
|
-
log(`[${
|
|
54463
|
+
log(`[${HOOK_NAME9}] Cleared fallback retry state for cancelled session on idle`, { sessionID });
|
|
53704
54464
|
return;
|
|
53705
54465
|
}
|
|
53706
54466
|
if (sessionAwaitingFallbackResult.has(sessionID)) {
|
|
53707
|
-
log(`[${
|
|
54467
|
+
log(`[${HOOK_NAME9}] session.idle while awaiting fallback result; keeping timeout armed`, { sessionID });
|
|
53708
54468
|
return;
|
|
53709
54469
|
}
|
|
53710
54470
|
const hadTimeout = sessionFallbackTimeouts.has(sessionID);
|
|
53711
54471
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
53712
54472
|
sessionRetryInFlight.delete(sessionID);
|
|
53713
54473
|
sessionStatusRetryKeys.delete(sessionID);
|
|
53714
|
-
const state3 =
|
|
54474
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53715
54475
|
if (state3?.pendingFallbackModel) {
|
|
53716
54476
|
state3.pendingFallbackModel = undefined;
|
|
53717
54477
|
}
|
|
53718
54478
|
if (hadTimeout) {
|
|
53719
|
-
log(`[${
|
|
54479
|
+
log(`[${HOOK_NAME9}] Cleared fallback timeout after session completion`, { sessionID });
|
|
53720
54480
|
}
|
|
53721
54481
|
};
|
|
53722
54482
|
const handleSessionError = async (props) => {
|
|
@@ -53724,18 +54484,18 @@ function createEventHandler(deps, helpers) {
|
|
|
53724
54484
|
const error51 = props?.error;
|
|
53725
54485
|
const agent = props?.agent;
|
|
53726
54486
|
if (!sessionID) {
|
|
53727
|
-
log(`[${
|
|
54487
|
+
log(`[${HOOK_NAME9}] session.error without sessionID, skipping`);
|
|
53728
54488
|
return;
|
|
53729
54489
|
}
|
|
53730
54490
|
const resolvedAgent = await helpers.resolveAgentForSessionFromContext(sessionID, agent);
|
|
53731
54491
|
if (isAbortError2(error51)) {
|
|
53732
54492
|
cancelledSessions.add(sessionID);
|
|
53733
54493
|
resetRetryState(sessionID);
|
|
53734
|
-
log(`[${
|
|
54494
|
+
log(`[${HOOK_NAME9}] session.error matched cancellation; cleared retry state`, { sessionID, resolvedAgent });
|
|
53735
54495
|
return;
|
|
53736
54496
|
}
|
|
53737
54497
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
53738
|
-
log(`[${
|
|
54498
|
+
log(`[${HOOK_NAME9}] session.error skipped - retry in flight`, {
|
|
53739
54499
|
sessionID,
|
|
53740
54500
|
retryInFlight: true
|
|
53741
54501
|
});
|
|
@@ -53743,7 +54503,7 @@ function createEventHandler(deps, helpers) {
|
|
|
53743
54503
|
}
|
|
53744
54504
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
53745
54505
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
53746
|
-
log(`[${
|
|
54506
|
+
log(`[${HOOK_NAME9}] session.error received`, {
|
|
53747
54507
|
sessionID,
|
|
53748
54508
|
agent,
|
|
53749
54509
|
resolvedAgent,
|
|
@@ -53752,7 +54512,7 @@ function createEventHandler(deps, helpers) {
|
|
|
53752
54512
|
errorType: classifyErrorType(error51)
|
|
53753
54513
|
});
|
|
53754
54514
|
if (!isRetryableError(error51, config2.retry_on_errors)) {
|
|
53755
|
-
log(`[${
|
|
54515
|
+
log(`[${HOOK_NAME9}] Error not retryable, skipping fallback`, {
|
|
53756
54516
|
sessionID,
|
|
53757
54517
|
retryable: false,
|
|
53758
54518
|
statusCode: extractStatusCode(error51, config2.retry_on_errors),
|
|
@@ -53761,10 +54521,10 @@ function createEventHandler(deps, helpers) {
|
|
|
53761
54521
|
});
|
|
53762
54522
|
return;
|
|
53763
54523
|
}
|
|
53764
|
-
let state3 =
|
|
54524
|
+
let state3 = sessionStates2.get(sessionID);
|
|
53765
54525
|
const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
|
|
53766
54526
|
if (fallbackModels.length === 0) {
|
|
53767
|
-
log(`[${
|
|
54527
|
+
log(`[${HOOK_NAME9}] No fallback models configured`, { sessionID, agent });
|
|
53768
54528
|
return;
|
|
53769
54529
|
}
|
|
53770
54530
|
if (!state3) {
|
|
@@ -53776,11 +54536,11 @@ function createEventHandler(deps, helpers) {
|
|
|
53776
54536
|
pluginConfig
|
|
53777
54537
|
});
|
|
53778
54538
|
if (!initialModel) {
|
|
53779
|
-
log(`[${
|
|
54539
|
+
log(`[${HOOK_NAME9}] No model info available, cannot fallback`, { sessionID });
|
|
53780
54540
|
return;
|
|
53781
54541
|
}
|
|
53782
54542
|
state3 = createFallbackState(initialModel);
|
|
53783
|
-
|
|
54543
|
+
sessionStates2.set(sessionID, state3);
|
|
53784
54544
|
sessionLastAccess.set(sessionID, Date.now());
|
|
53785
54545
|
} else {
|
|
53786
54546
|
sessionLastAccess.set(sessionID, Date.now());
|
|
@@ -53892,7 +54652,7 @@ function hasVisibleAssistantResponse(extractAutoRetrySignalFn) {
|
|
|
53892
54652
|
|
|
53893
54653
|
// src/hooks/runtime-fallback/message-update-handler.ts
|
|
53894
54654
|
function createMessageUpdateHandler(deps, helpers) {
|
|
53895
|
-
const { ctx, config: config2, pluginConfig, sessionStates, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionStatusRetryKeys } = deps;
|
|
54655
|
+
const { ctx, config: config2, pluginConfig, sessionStates: sessionStates2, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionStatusRetryKeys } = deps;
|
|
53896
54656
|
const checkVisibleResponse = hasVisibleAssistantResponse(extractAutoRetrySignal);
|
|
53897
54657
|
return async (props) => {
|
|
53898
54658
|
const info = props?.info;
|
|
@@ -53916,7 +54676,7 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53916
54676
|
}
|
|
53917
54677
|
const hasVisible = await checkVisibleResponse(ctx, sessionID, info);
|
|
53918
54678
|
if (!hasVisible) {
|
|
53919
|
-
log(`[${
|
|
54679
|
+
log(`[${HOOK_NAME9}] Assistant update observed without visible final response; keeping fallback timeout`, {
|
|
53920
54680
|
sessionID,
|
|
53921
54681
|
model
|
|
53922
54682
|
});
|
|
@@ -53925,21 +54685,21 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53925
54685
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
53926
54686
|
sessionStatusRetryKeys.delete(sessionID);
|
|
53927
54687
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
53928
|
-
const state3 =
|
|
54688
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53929
54689
|
if (state3?.pendingFallbackModel) {
|
|
53930
54690
|
state3.pendingFallbackModel = undefined;
|
|
53931
54691
|
}
|
|
53932
|
-
log(`[${
|
|
54692
|
+
log(`[${HOOK_NAME9}] Assistant response observed; cleared fallback timeout`, { sessionID, model });
|
|
53933
54693
|
return;
|
|
53934
54694
|
}
|
|
53935
54695
|
if (sessionID && role === "assistant" && error51) {
|
|
53936
54696
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
53937
54697
|
if (sessionRetryInFlight.has(sessionID) && !retrySignal) {
|
|
53938
|
-
log(`[${
|
|
54698
|
+
log(`[${HOOK_NAME9}] message.updated fallback skipped (retry in flight)`, { sessionID });
|
|
53939
54699
|
return;
|
|
53940
54700
|
}
|
|
53941
54701
|
if (retrySignal && sessionRetryInFlight.has(sessionID) && timeoutEnabled) {
|
|
53942
|
-
log(`[${
|
|
54702
|
+
log(`[${HOOK_NAME9}] Overriding in-flight retry due to provider auto-retry signal`, {
|
|
53943
54703
|
sessionID,
|
|
53944
54704
|
model
|
|
53945
54705
|
});
|
|
@@ -53947,12 +54707,12 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53947
54707
|
sessionRetryInFlight.delete(sessionID);
|
|
53948
54708
|
}
|
|
53949
54709
|
if (retrySignal && timeoutEnabled) {
|
|
53950
|
-
log(`[${
|
|
54710
|
+
log(`[${HOOK_NAME9}] Detected provider auto-retry signal`, { sessionID, model });
|
|
53951
54711
|
}
|
|
53952
54712
|
if (!retrySignal) {
|
|
53953
54713
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
53954
54714
|
}
|
|
53955
|
-
log(`[${
|
|
54715
|
+
log(`[${HOOK_NAME9}] message.updated with assistant error`, {
|
|
53956
54716
|
sessionID,
|
|
53957
54717
|
model,
|
|
53958
54718
|
statusCode: extractStatusCode(error51, config2.retry_on_errors),
|
|
@@ -53960,7 +54720,7 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53960
54720
|
errorType: classifyErrorType(error51)
|
|
53961
54721
|
});
|
|
53962
54722
|
if (!isRetryableError(error51, config2.retry_on_errors)) {
|
|
53963
|
-
log(`[${
|
|
54723
|
+
log(`[${HOOK_NAME9}] message.updated error not retryable, skipping fallback`, {
|
|
53964
54724
|
sessionID,
|
|
53965
54725
|
statusCode: extractStatusCode(error51, config2.retry_on_errors),
|
|
53966
54726
|
errorName: extractErrorName(error51),
|
|
@@ -53968,7 +54728,7 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53968
54728
|
});
|
|
53969
54729
|
return;
|
|
53970
54730
|
}
|
|
53971
|
-
let state3 =
|
|
54731
|
+
let state3 = sessionStates2.get(sessionID);
|
|
53972
54732
|
const agent = info?.agent;
|
|
53973
54733
|
const resolvedAgent = await helpers.resolveAgentForSessionFromContext(sessionID, agent);
|
|
53974
54734
|
const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
|
|
@@ -53984,7 +54744,7 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53984
54744
|
pluginConfig
|
|
53985
54745
|
});
|
|
53986
54746
|
if (!initialModel) {
|
|
53987
|
-
log(`[${
|
|
54747
|
+
log(`[${HOOK_NAME9}] message.updated missing model info, cannot fallback`, {
|
|
53988
54748
|
sessionID,
|
|
53989
54749
|
errorName: extractErrorName(error51),
|
|
53990
54750
|
errorType: classifyErrorType(error51)
|
|
@@ -53992,19 +54752,19 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53992
54752
|
return;
|
|
53993
54753
|
}
|
|
53994
54754
|
state3 = createFallbackState(initialModel);
|
|
53995
|
-
|
|
54755
|
+
sessionStates2.set(sessionID, state3);
|
|
53996
54756
|
sessionLastAccess.set(sessionID, Date.now());
|
|
53997
54757
|
} else {
|
|
53998
54758
|
sessionLastAccess.set(sessionID, Date.now());
|
|
53999
54759
|
if (state3.pendingFallbackModel) {
|
|
54000
54760
|
if (retrySignal && timeoutEnabled) {
|
|
54001
|
-
log(`[${
|
|
54761
|
+
log(`[${HOOK_NAME9}] Clearing pending fallback due to provider auto-retry signal`, {
|
|
54002
54762
|
sessionID,
|
|
54003
54763
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
54004
54764
|
});
|
|
54005
54765
|
state3.pendingFallbackModel = undefined;
|
|
54006
54766
|
} else {
|
|
54007
|
-
log(`[${
|
|
54767
|
+
log(`[${HOOK_NAME9}] message.updated fallback skipped (pending fallback in progress)`, {
|
|
54008
54768
|
sessionID,
|
|
54009
54769
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
54010
54770
|
});
|
|
@@ -54348,6 +55108,7 @@ function createIdleNotificationScheduler(options) {
|
|
|
54348
55108
|
}
|
|
54349
55109
|
|
|
54350
55110
|
// src/hooks/session-notification-sender.ts
|
|
55111
|
+
init_logger();
|
|
54351
55112
|
import { platform } from "os";
|
|
54352
55113
|
|
|
54353
55114
|
// src/hooks/session-notification-formatting.ts
|
|
@@ -54375,6 +55136,7 @@ $Notifier.Show($Toast)
|
|
|
54375
55136
|
}
|
|
54376
55137
|
|
|
54377
55138
|
// src/hooks/session-notification-utils.ts
|
|
55139
|
+
init_logger();
|
|
54378
55140
|
async function findCommand2(commandName) {
|
|
54379
55141
|
try {
|
|
54380
55142
|
return Bun.which(commandName);
|
|
@@ -54406,14 +55168,26 @@ var getPaplayPath = createCommandFinder("paplay");
|
|
|
54406
55168
|
var getAplayPath = createCommandFinder("aplay");
|
|
54407
55169
|
function startBackgroundCheck2(platform) {
|
|
54408
55170
|
if (platform === "darwin") {
|
|
54409
|
-
getOsascriptPath().catch(() => {
|
|
54410
|
-
|
|
55171
|
+
getOsascriptPath().catch((err) => {
|
|
55172
|
+
log("[notification] Background check failed for osascript:", err);
|
|
55173
|
+
});
|
|
55174
|
+
getAfplayPath().catch((err) => {
|
|
55175
|
+
log("[notification] Background check failed for afplay:", err);
|
|
55176
|
+
});
|
|
54411
55177
|
} else if (platform === "linux") {
|
|
54412
|
-
getNotifySendPath().catch(() => {
|
|
54413
|
-
|
|
54414
|
-
|
|
55178
|
+
getNotifySendPath().catch((err) => {
|
|
55179
|
+
log("[notification] Background check failed for notify-send:", err);
|
|
55180
|
+
});
|
|
55181
|
+
getPaplayPath().catch((err) => {
|
|
55182
|
+
log("[notification] Background check failed for paplay:", err);
|
|
55183
|
+
});
|
|
55184
|
+
getAplayPath().catch((err) => {
|
|
55185
|
+
log("[notification] Background check failed for aplay:", err);
|
|
55186
|
+
});
|
|
54415
55187
|
} else if (platform === "win32") {
|
|
54416
|
-
getPowershellPath().catch(() => {
|
|
55188
|
+
getPowershellPath().catch((err) => {
|
|
55189
|
+
log("[notification] Background check failed for powershell:", err);
|
|
55190
|
+
});
|
|
54417
55191
|
}
|
|
54418
55192
|
}
|
|
54419
55193
|
|
|
@@ -54444,14 +55218,18 @@ async function sendSessionNotification(ctx, platform2, title, message) {
|
|
|
54444
55218
|
return;
|
|
54445
55219
|
const escapedTitle = escapeAppleScriptText(title);
|
|
54446
55220
|
const escapedMessage = escapeAppleScriptText(message);
|
|
54447
|
-
await ctx.$`${osascriptPath} -e ${`display notification "${escapedMessage}" with title "${escapedTitle}"`}`.catch(() => {
|
|
55221
|
+
await ctx.$`${osascriptPath} -e ${`display notification "${escapedMessage}" with title "${escapedTitle}"`}`.catch((err) => {
|
|
55222
|
+
log("[notification] macOS notification failed:", err);
|
|
55223
|
+
});
|
|
54448
55224
|
break;
|
|
54449
55225
|
}
|
|
54450
55226
|
case "linux": {
|
|
54451
55227
|
const notifySendPath = await getNotifySendPath();
|
|
54452
55228
|
if (!notifySendPath)
|
|
54453
55229
|
return;
|
|
54454
|
-
await ctx.$`${notifySendPath} ${title} ${message} 2>/dev/null`.catch(() => {
|
|
55230
|
+
await ctx.$`${notifySendPath} ${title} ${message} 2>/dev/null`.catch((err) => {
|
|
55231
|
+
log("[notification] Linux notification failed:", err);
|
|
55232
|
+
});
|
|
54455
55233
|
break;
|
|
54456
55234
|
}
|
|
54457
55235
|
case "win32": {
|
|
@@ -54459,7 +55237,9 @@ async function sendSessionNotification(ctx, platform2, title, message) {
|
|
|
54459
55237
|
if (!powershellPath)
|
|
54460
55238
|
return;
|
|
54461
55239
|
const toastScript = buildWindowsToastScript(title, message);
|
|
54462
|
-
await ctx.$`${powershellPath} -Command ${toastScript}`.catch(() => {
|
|
55240
|
+
await ctx.$`${powershellPath} -Command ${toastScript}`.catch((err) => {
|
|
55241
|
+
log("[notification] Windows notification failed:", err);
|
|
55242
|
+
});
|
|
54463
55243
|
break;
|
|
54464
55244
|
}
|
|
54465
55245
|
}
|
|
@@ -54470,17 +55250,23 @@ async function playSessionNotificationSound(ctx, platform2, soundPath) {
|
|
|
54470
55250
|
const afplayPath = await getAfplayPath();
|
|
54471
55251
|
if (!afplayPath)
|
|
54472
55252
|
return;
|
|
54473
|
-
ctx.$`${afplayPath} ${soundPath}`.catch(() => {
|
|
55253
|
+
ctx.$`${afplayPath} ${soundPath}`.catch((err) => {
|
|
55254
|
+
log("[notification] macOS sound playback failed:", err);
|
|
55255
|
+
});
|
|
54474
55256
|
break;
|
|
54475
55257
|
}
|
|
54476
55258
|
case "linux": {
|
|
54477
55259
|
const paplayPath = await getPaplayPath();
|
|
54478
55260
|
if (paplayPath) {
|
|
54479
|
-
ctx.$`${paplayPath} ${soundPath} 2>/dev/null`.catch(() => {
|
|
55261
|
+
ctx.$`${paplayPath} ${soundPath} 2>/dev/null`.catch((err) => {
|
|
55262
|
+
log("[notification] Linux sound playback (paplay) failed:", err);
|
|
55263
|
+
});
|
|
54480
55264
|
} else {
|
|
54481
55265
|
const aplayPath = await getAplayPath();
|
|
54482
55266
|
if (aplayPath) {
|
|
54483
|
-
ctx.$`${aplayPath} ${soundPath} 2>/dev/null`.catch(() => {
|
|
55267
|
+
ctx.$`${aplayPath} ${soundPath} 2>/dev/null`.catch((err) => {
|
|
55268
|
+
log("[notification] Linux sound playback (aplay) failed:", err);
|
|
55269
|
+
});
|
|
54484
55270
|
}
|
|
54485
55271
|
}
|
|
54486
55272
|
break;
|
|
@@ -54490,7 +55276,9 @@ async function playSessionNotificationSound(ctx, platform2, soundPath) {
|
|
|
54490
55276
|
if (!powershellPath)
|
|
54491
55277
|
return;
|
|
54492
55278
|
const escaped = escapePowerShellSingleQuotedText(soundPath);
|
|
54493
|
-
ctx.$`${powershellPath} -Command ${`(New-Object Media.SoundPlayer '${escaped}').PlaySync()`}`.catch(() => {
|
|
55279
|
+
ctx.$`${powershellPath} -Command ${`(New-Object Media.SoundPlayer '${escaped}').PlaySync()`}`.catch((err) => {
|
|
55280
|
+
log("[notification] Windows sound playback failed:", err);
|
|
55281
|
+
});
|
|
54494
55282
|
break;
|
|
54495
55283
|
}
|
|
54496
55284
|
}
|
|
@@ -54908,7 +55696,9 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
54908
55696
|
if (onAbortCallback) {
|
|
54909
55697
|
onAbortCallback(sessionID);
|
|
54910
55698
|
}
|
|
54911
|
-
await ctx.client.session.abort({ path: { id: sessionID } }).catch(() => {
|
|
55699
|
+
await ctx.client.session.abort({ path: { id: sessionID } }).catch((err) => {
|
|
55700
|
+
log("[session-recovery] Session abort failed:", err);
|
|
55701
|
+
});
|
|
54912
55702
|
const messagesResp = await ctx.client.session.messages({
|
|
54913
55703
|
path: { id: sessionID },
|
|
54914
55704
|
query: { directory: ctx.directory }
|
|
@@ -54937,7 +55727,9 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
54937
55727
|
variant: "warning",
|
|
54938
55728
|
duration: 3000
|
|
54939
55729
|
}
|
|
54940
|
-
}).catch(() => {
|
|
55730
|
+
}).catch((err) => {
|
|
55731
|
+
log("[session-recovery] Recovery toast failed:", err);
|
|
55732
|
+
});
|
|
54941
55733
|
let success2 = false;
|
|
54942
55734
|
if (errorType === "tool_result_missing") {
|
|
54943
55735
|
success2 = await recoverToolResultMissing(ctx.client, sessionID, failedMsg);
|
|
@@ -54978,7 +55770,7 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
54978
55770
|
}
|
|
54979
55771
|
// src/hooks/start-work/start-work-hook.ts
|
|
54980
55772
|
init_logger();
|
|
54981
|
-
var
|
|
55773
|
+
var HOOK_NAME10 = "start-work";
|
|
54982
55774
|
var KEYWORD_PATTERN = /\b(ultrawork|ulw)\b/gi;
|
|
54983
55775
|
function extractUserRequestPlanName(promptText) {
|
|
54984
55776
|
const userRequestMatch = promptText.match(/<user-request>\s*([\s\S]*?)\s*<\/user-request>/i);
|
|
@@ -55008,7 +55800,7 @@ function createStartWorkHook(ctx) {
|
|
|
55008
55800
|
if (!isStartWorkCommand) {
|
|
55009
55801
|
return;
|
|
55010
55802
|
}
|
|
55011
|
-
log(`[${
|
|
55803
|
+
log(`[${HOOK_NAME10}] Processing start-work command`, {
|
|
55012
55804
|
sessionID: input.sessionID
|
|
55013
55805
|
});
|
|
55014
55806
|
updateSessionAgent(input.sessionID, "architect");
|
|
@@ -55018,7 +55810,7 @@ function createStartWorkHook(ctx) {
|
|
|
55018
55810
|
let contextInfo = "";
|
|
55019
55811
|
const explicitPlanName = extractUserRequestPlanName(promptText);
|
|
55020
55812
|
if (explicitPlanName) {
|
|
55021
|
-
log(`[${
|
|
55813
|
+
log(`[${HOOK_NAME10}] Explicit plan name requested: ${explicitPlanName}`, {
|
|
55022
55814
|
sessionID: input.sessionID
|
|
55023
55815
|
});
|
|
55024
55816
|
const allPlans = findOraclePlans(ctx.directory);
|
|
@@ -55159,7 +55951,7 @@ Ask the user which plan to work on. Present the options above and wait for their
|
|
|
55159
55951
|
---
|
|
55160
55952
|
${contextInfo}`;
|
|
55161
55953
|
}
|
|
55162
|
-
log(`[${
|
|
55954
|
+
log(`[${HOOK_NAME10}] Context injected`, {
|
|
55163
55955
|
sessionID: input.sessionID,
|
|
55164
55956
|
hasExistingState: !!existingState
|
|
55165
55957
|
});
|
|
@@ -55168,16 +55960,16 @@ ${contextInfo}`;
|
|
|
55168
55960
|
}
|
|
55169
55961
|
// src/hooks/stop-continuation-guard/hook.ts
|
|
55170
55962
|
init_logger();
|
|
55171
|
-
var
|
|
55963
|
+
var HOOK_NAME11 = "stop-continuation-guard";
|
|
55172
55964
|
function createStopContinuationGuardHook(_ctx, options) {
|
|
55173
55965
|
const stoppedSessions = new Set;
|
|
55174
55966
|
const stop = (sessionID) => {
|
|
55175
55967
|
stoppedSessions.add(sessionID);
|
|
55176
|
-
log(`[${
|
|
55968
|
+
log(`[${HOOK_NAME11}] Continuation stopped for session`, { sessionID });
|
|
55177
55969
|
if (options?.backgroundManager) {
|
|
55178
55970
|
const cancelled = options.backgroundManager.cancelAllForSession(sessionID);
|
|
55179
55971
|
if (cancelled > 0) {
|
|
55180
|
-
log(`[${
|
|
55972
|
+
log(`[${HOOK_NAME11}] Cancelled ${cancelled} background task(s) for session`, { sessionID });
|
|
55181
55973
|
}
|
|
55182
55974
|
}
|
|
55183
55975
|
};
|
|
@@ -55186,7 +55978,7 @@ function createStopContinuationGuardHook(_ctx, options) {
|
|
|
55186
55978
|
};
|
|
55187
55979
|
const clear = (sessionID) => {
|
|
55188
55980
|
stoppedSessions.delete(sessionID);
|
|
55189
|
-
log(`[${
|
|
55981
|
+
log(`[${HOOK_NAME11}] Continuation guard cleared for session`, { sessionID });
|
|
55190
55982
|
};
|
|
55191
55983
|
const event = async ({
|
|
55192
55984
|
event: event2
|
|
@@ -55196,7 +55988,7 @@ function createStopContinuationGuardHook(_ctx, options) {
|
|
|
55196
55988
|
const sessionInfo = props?.info;
|
|
55197
55989
|
if (sessionInfo?.id) {
|
|
55198
55990
|
clear(sessionInfo.id);
|
|
55199
|
-
log(`[${
|
|
55991
|
+
log(`[${HOOK_NAME11}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
|
|
55200
55992
|
}
|
|
55201
55993
|
}
|
|
55202
55994
|
};
|
|
@@ -55205,7 +55997,7 @@ function createStopContinuationGuardHook(_ctx, options) {
|
|
|
55205
55997
|
}) => {
|
|
55206
55998
|
if (sessionID && stoppedSessions.has(sessionID)) {
|
|
55207
55999
|
clear(sessionID);
|
|
55208
|
-
log(`[${
|
|
56000
|
+
log(`[${HOOK_NAME11}] Cleared stop state on new user message`, { sessionID });
|
|
55209
56001
|
}
|
|
55210
56002
|
};
|
|
55211
56003
|
return {
|
|
@@ -55217,7 +56009,7 @@ function createStopContinuationGuardHook(_ctx, options) {
|
|
|
55217
56009
|
};
|
|
55218
56010
|
}
|
|
55219
56011
|
// src/hooks/task-notepad/constants.ts
|
|
55220
|
-
var
|
|
56012
|
+
var HOOK_NAME12 = "task-notepad";
|
|
55221
56013
|
var PLAN_DIR_NAME = ".matrixx";
|
|
55222
56014
|
var PLANS_SUBDIR = "plans";
|
|
55223
56015
|
var NOTEPAD_DIR_NAME = "notepads";
|
|
@@ -55246,7 +56038,7 @@ function createTaskNotepadHook(ctx) {
|
|
|
55246
56038
|
const plan = resolveActivePlan(ctx.directory);
|
|
55247
56039
|
if (inProgress) {
|
|
55248
56040
|
if (!plan) {
|
|
55249
|
-
log(`[${
|
|
56041
|
+
log(`[${HOOK_NAME12}] No active plan, skipping notepad creation`, {
|
|
55250
56042
|
sessionID: input.sessionID
|
|
55251
56043
|
});
|
|
55252
56044
|
} else {
|
|
@@ -55273,7 +56065,7 @@ async function fetchTodos(ctx, sessionID) {
|
|
|
55273
56065
|
}
|
|
55274
56066
|
return [];
|
|
55275
56067
|
} catch (err) {
|
|
55276
|
-
log(`[${
|
|
56068
|
+
log(`[${HOOK_NAME12}] ${TODO_FETCH_ERROR_FRAGMENT}`, {
|
|
55277
56069
|
sessionID,
|
|
55278
56070
|
error: String(err)
|
|
55279
56071
|
});
|
|
@@ -55318,7 +56110,7 @@ function ensureNotepad(directory, planName, todo) {
|
|
|
55318
56110
|
mkdirSync12(dirname15(notepadPath), { recursive: true });
|
|
55319
56111
|
const content = renderNotepad(todo);
|
|
55320
56112
|
writeFileSync16(notepadPath, content, "utf-8");
|
|
55321
|
-
log(`[${
|
|
56113
|
+
log(`[${HOOK_NAME12}] Created task notepad`, { plan: planName, taskID: todo.id });
|
|
55322
56114
|
}
|
|
55323
56115
|
function appendCompletion(directory, planName, todo) {
|
|
55324
56116
|
const notepadPath = notepadPathFor(directory, planName, todo.id);
|
|
@@ -55334,7 +56126,7 @@ function appendCompletion(directory, planName, todo) {
|
|
|
55334
56126
|
- status: completed
|
|
55335
56127
|
`;
|
|
55336
56128
|
writeFileSync16(notepadPath, updated, "utf-8");
|
|
55337
|
-
log(`[${
|
|
56129
|
+
log(`[${HOOK_NAME12}] Appended completion to notepad`, { plan: planName, taskID: todo.id });
|
|
55338
56130
|
}
|
|
55339
56131
|
function notepadPathFor(directory, planName, taskID) {
|
|
55340
56132
|
return join54(directory, PLAN_DIR_NAME, NOTEPAD_DIR_NAME, planName, TASKS_SUBDIR, `${taskID}${NOTEPAD_EXTENSION}`);
|
|
@@ -55916,7 +56708,7 @@ function createThinkingBlockValidatorHook() {
|
|
|
55916
56708
|
init_logger();
|
|
55917
56709
|
|
|
55918
56710
|
// src/hooks/todo-continuation-enforcer/constants.ts
|
|
55919
|
-
var
|
|
56711
|
+
var HOOK_NAME13 = "todo-continuation-enforcer";
|
|
55920
56712
|
var DEFAULT_SKIP_AGENTS = ["oracle", "compaction"];
|
|
55921
56713
|
var CONTINUATION_PROMPT2 = `${createSystemDirective(SystemDirectiveTypes.TODO_CONTINUATION)}
|
|
55922
56714
|
|
|
@@ -55982,12 +56774,12 @@ async function injectContinuation(args) {
|
|
|
55982
56774
|
} = args;
|
|
55983
56775
|
const state3 = sessionStateStore.getExistingState(sessionID);
|
|
55984
56776
|
if (state3?.isRecovering) {
|
|
55985
|
-
log(`[${
|
|
56777
|
+
log(`[${HOOK_NAME13}] Skipped injection: in recovery`, { sessionID });
|
|
55986
56778
|
return;
|
|
55987
56779
|
}
|
|
55988
56780
|
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
|
|
55989
56781
|
if (hasRunningBgTasks) {
|
|
55990
|
-
log(`[${
|
|
56782
|
+
log(`[${HOOK_NAME13}] Skipped injection: background tasks running`, { sessionID });
|
|
55991
56783
|
return;
|
|
55992
56784
|
}
|
|
55993
56785
|
let todos = [];
|
|
@@ -55995,12 +56787,12 @@ async function injectContinuation(args) {
|
|
|
55995
56787
|
const response = await ctx.client.session.todo({ path: { id: sessionID } });
|
|
55996
56788
|
todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
|
|
55997
56789
|
} catch (error51) {
|
|
55998
|
-
log(`[${
|
|
56790
|
+
log(`[${HOOK_NAME13}] Failed to fetch todos`, { sessionID, error: String(error51) });
|
|
55999
56791
|
return;
|
|
56000
56792
|
}
|
|
56001
56793
|
const freshIncompleteCount = getIncompleteCount(todos);
|
|
56002
56794
|
if (freshIncompleteCount === 0) {
|
|
56003
|
-
log(`[${
|
|
56795
|
+
log(`[${HOOK_NAME13}] Skipped injection: no incomplete todos`, { sessionID });
|
|
56004
56796
|
return;
|
|
56005
56797
|
}
|
|
56006
56798
|
let agentName = resolvedInfo?.agent;
|
|
@@ -56023,11 +56815,11 @@ async function injectContinuation(args) {
|
|
|
56023
56815
|
tools = tools ?? previousMessage?.tools;
|
|
56024
56816
|
}
|
|
56025
56817
|
if (agentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(agentName))) {
|
|
56026
|
-
log(`[${
|
|
56818
|
+
log(`[${HOOK_NAME13}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
|
|
56027
56819
|
return;
|
|
56028
56820
|
}
|
|
56029
56821
|
if (!hasWritePermission(tools)) {
|
|
56030
|
-
log(`[${
|
|
56822
|
+
log(`[${HOOK_NAME13}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
|
|
56031
56823
|
return;
|
|
56032
56824
|
}
|
|
56033
56825
|
const incompleteTodos = todos.filter((todo) => todo.status !== "completed" && todo.status !== "cancelled");
|
|
@@ -56044,7 +56836,7 @@ ${todoList}`;
|
|
|
56044
56836
|
injectionState.inFlight = true;
|
|
56045
56837
|
}
|
|
56046
56838
|
try {
|
|
56047
|
-
log(`[${
|
|
56839
|
+
log(`[${HOOK_NAME13}] Injecting continuation`, {
|
|
56048
56840
|
sessionID,
|
|
56049
56841
|
agent: agentName,
|
|
56050
56842
|
model,
|
|
@@ -56059,14 +56851,14 @@ ${todoList}`;
|
|
|
56059
56851
|
},
|
|
56060
56852
|
query: { directory: ctx.directory }
|
|
56061
56853
|
});
|
|
56062
|
-
log(`[${
|
|
56854
|
+
log(`[${HOOK_NAME13}] Injection successful`, { sessionID });
|
|
56063
56855
|
if (injectionState) {
|
|
56064
56856
|
injectionState.inFlight = false;
|
|
56065
56857
|
injectionState.lastInjectedAt = Date.now();
|
|
56066
56858
|
injectionState.consecutiveFailures = 0;
|
|
56067
56859
|
}
|
|
56068
56860
|
} catch (error51) {
|
|
56069
|
-
log(`[${
|
|
56861
|
+
log(`[${HOOK_NAME13}] Injection failed`, { sessionID, error: String(error51) });
|
|
56070
56862
|
if (injectionState) {
|
|
56071
56863
|
injectionState.inFlight = false;
|
|
56072
56864
|
injectionState.lastInjectedAt = Date.now();
|
|
@@ -56084,7 +56876,9 @@ async function showCountdownToast(ctx, seconds, incompleteCount) {
|
|
|
56084
56876
|
variant: "warning",
|
|
56085
56877
|
duration: TOAST_DURATION_MS
|
|
56086
56878
|
}
|
|
56087
|
-
}).catch(() => {
|
|
56879
|
+
}).catch((err) => {
|
|
56880
|
+
log("[todo-continuation] Countdown toast failed:", err);
|
|
56881
|
+
});
|
|
56088
56882
|
}
|
|
56089
56883
|
function startCountdown(args) {
|
|
56090
56884
|
const {
|
|
@@ -56118,7 +56912,7 @@ function startCountdown(args) {
|
|
|
56118
56912
|
sessionStateStore
|
|
56119
56913
|
});
|
|
56120
56914
|
}, COUNTDOWN_SECONDS * 1000);
|
|
56121
|
-
log(`[${
|
|
56915
|
+
log(`[${HOOK_NAME13}] Countdown started`, {
|
|
56122
56916
|
sessionID,
|
|
56123
56917
|
seconds: COUNTDOWN_SECONDS,
|
|
56124
56918
|
incompleteCount
|
|
@@ -56135,16 +56929,16 @@ async function handleSessionIdle(args) {
|
|
|
56135
56929
|
skipAgents = DEFAULT_SKIP_AGENTS,
|
|
56136
56930
|
isContinuationStopped
|
|
56137
56931
|
} = args;
|
|
56138
|
-
log(`[${
|
|
56932
|
+
log(`[${HOOK_NAME13}] session.idle`, { sessionID });
|
|
56139
56933
|
const state3 = sessionStateStore.getState(sessionID);
|
|
56140
56934
|
if (state3.isRecovering) {
|
|
56141
|
-
log(`[${
|
|
56935
|
+
log(`[${HOOK_NAME13}] Skipped: in recovery`, { sessionID });
|
|
56142
56936
|
return;
|
|
56143
56937
|
}
|
|
56144
56938
|
if (state3.abortDetectedAt) {
|
|
56145
56939
|
const timeSinceAbort = Date.now() - state3.abortDetectedAt;
|
|
56146
56940
|
if (timeSinceAbort < ABORT_WINDOW_MS) {
|
|
56147
|
-
log(`[${
|
|
56941
|
+
log(`[${HOOK_NAME13}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
|
|
56148
56942
|
state3.abortDetectedAt = undefined;
|
|
56149
56943
|
return;
|
|
56150
56944
|
}
|
|
@@ -56152,7 +56946,7 @@ async function handleSessionIdle(args) {
|
|
|
56152
56946
|
}
|
|
56153
56947
|
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
|
|
56154
56948
|
if (hasRunningBgTasks) {
|
|
56155
|
-
log(`[${
|
|
56949
|
+
log(`[${HOOK_NAME13}] Skipped: background tasks running`, { sessionID });
|
|
56156
56950
|
return;
|
|
56157
56951
|
}
|
|
56158
56952
|
try {
|
|
@@ -56162,42 +56956,42 @@ async function handleSessionIdle(args) {
|
|
|
56162
56956
|
});
|
|
56163
56957
|
const messages = normalizeSDKResponse(messagesResp, []);
|
|
56164
56958
|
if (isLastAssistantMessageAborted(messages)) {
|
|
56165
|
-
log(`[${
|
|
56959
|
+
log(`[${HOOK_NAME13}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
|
|
56166
56960
|
return;
|
|
56167
56961
|
}
|
|
56168
56962
|
} catch (error51) {
|
|
56169
|
-
log(`[${
|
|
56963
|
+
log(`[${HOOK_NAME13}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
|
|
56170
56964
|
}
|
|
56171
56965
|
let todos = [];
|
|
56172
56966
|
try {
|
|
56173
56967
|
const response = await ctx.client.session.todo({ path: { id: sessionID } });
|
|
56174
56968
|
todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
|
|
56175
56969
|
} catch (error51) {
|
|
56176
|
-
log(`[${
|
|
56970
|
+
log(`[${HOOK_NAME13}] Todo fetch failed`, { sessionID, error: String(error51) });
|
|
56177
56971
|
return;
|
|
56178
56972
|
}
|
|
56179
56973
|
if (!todos || todos.length === 0) {
|
|
56180
|
-
log(`[${
|
|
56974
|
+
log(`[${HOOK_NAME13}] No todos`, { sessionID });
|
|
56181
56975
|
return;
|
|
56182
56976
|
}
|
|
56183
56977
|
const incompleteCount = getIncompleteCount(todos);
|
|
56184
56978
|
if (incompleteCount === 0) {
|
|
56185
|
-
log(`[${
|
|
56979
|
+
log(`[${HOOK_NAME13}] All todos complete`, { sessionID, total: todos.length });
|
|
56186
56980
|
return;
|
|
56187
56981
|
}
|
|
56188
56982
|
if (state3.inFlight) {
|
|
56189
|
-
log(`[${
|
|
56983
|
+
log(`[${HOOK_NAME13}] Skipped: injection in flight`, { sessionID });
|
|
56190
56984
|
return;
|
|
56191
56985
|
}
|
|
56192
56986
|
if (state3.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES && state3.lastInjectedAt && Date.now() - state3.lastInjectedAt >= FAILURE_RESET_WINDOW_MS) {
|
|
56193
56987
|
state3.consecutiveFailures = 0;
|
|
56194
|
-
log(`[${
|
|
56988
|
+
log(`[${HOOK_NAME13}] Reset consecutive failures after recovery window`, {
|
|
56195
56989
|
sessionID,
|
|
56196
56990
|
failureResetWindowMs: FAILURE_RESET_WINDOW_MS
|
|
56197
56991
|
});
|
|
56198
56992
|
}
|
|
56199
56993
|
if (state3.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
56200
|
-
log(`[${
|
|
56994
|
+
log(`[${HOOK_NAME13}] Skipped: max consecutive failures reached`, {
|
|
56201
56995
|
sessionID,
|
|
56202
56996
|
consecutiveFailures: state3.consecutiveFailures,
|
|
56203
56997
|
maxConsecutiveFailures: MAX_CONSECUTIVE_FAILURES
|
|
@@ -56206,7 +57000,7 @@ async function handleSessionIdle(args) {
|
|
|
56206
57000
|
}
|
|
56207
57001
|
const effectiveCooldown = CONTINUATION_COOLDOWN_MS2 * 2 ** Math.min(state3.consecutiveFailures, 5);
|
|
56208
57002
|
if (state3.lastInjectedAt && Date.now() - state3.lastInjectedAt < effectiveCooldown) {
|
|
56209
|
-
log(`[${
|
|
57003
|
+
log(`[${HOOK_NAME13}] Skipped: cooldown active`, {
|
|
56210
57004
|
sessionID,
|
|
56211
57005
|
effectiveCooldown,
|
|
56212
57006
|
consecutiveFailures: state3.consecutiveFailures
|
|
@@ -56236,20 +57030,20 @@ async function handleSessionIdle(args) {
|
|
|
56236
57030
|
}
|
|
56237
57031
|
}
|
|
56238
57032
|
} catch (error51) {
|
|
56239
|
-
log(`[${
|
|
57033
|
+
log(`[${HOOK_NAME13}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
|
|
56240
57034
|
}
|
|
56241
|
-
log(`[${
|
|
57035
|
+
log(`[${HOOK_NAME13}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
|
|
56242
57036
|
const resolvedAgentName = resolvedInfo?.agent;
|
|
56243
57037
|
if (resolvedAgentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
|
|
56244
|
-
log(`[${
|
|
57038
|
+
log(`[${HOOK_NAME13}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
|
|
56245
57039
|
return;
|
|
56246
57040
|
}
|
|
56247
57041
|
if (hasCompactionMessage && !resolvedInfo?.agent) {
|
|
56248
|
-
log(`[${
|
|
57042
|
+
log(`[${HOOK_NAME13}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
|
|
56249
57043
|
return;
|
|
56250
57044
|
}
|
|
56251
57045
|
if (isContinuationStopped?.(sessionID)) {
|
|
56252
|
-
log(`[${
|
|
57046
|
+
log(`[${HOOK_NAME13}] Skipped: continuation stopped for session`, { sessionID });
|
|
56253
57047
|
return;
|
|
56254
57048
|
}
|
|
56255
57049
|
startCountdown({
|
|
@@ -56279,7 +57073,7 @@ function handleNonIdleEvent(args) {
|
|
|
56279
57073
|
if (state3?.countdownStartedAt) {
|
|
56280
57074
|
const elapsed = Date.now() - state3.countdownStartedAt;
|
|
56281
57075
|
if (elapsed < COUNTDOWN_GRACE_PERIOD_MS) {
|
|
56282
|
-
log(`[${
|
|
57076
|
+
log(`[${HOOK_NAME13}] Ignoring user message in grace period`, { sessionID, elapsed });
|
|
56283
57077
|
return;
|
|
56284
57078
|
}
|
|
56285
57079
|
}
|
|
@@ -56323,7 +57117,7 @@ function handleNonIdleEvent(args) {
|
|
|
56323
57117
|
const sessionInfo = properties?.info;
|
|
56324
57118
|
if (sessionInfo?.id) {
|
|
56325
57119
|
sessionStateStore.cleanup(sessionInfo.id);
|
|
56326
|
-
log(`[${
|
|
57120
|
+
log(`[${HOOK_NAME13}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
|
|
56327
57121
|
}
|
|
56328
57122
|
return;
|
|
56329
57123
|
}
|
|
@@ -56348,10 +57142,10 @@ function createTodoContinuationHandler(args) {
|
|
|
56348
57142
|
if (error51?.name === "MessageAbortedError" || error51?.name === "AbortError") {
|
|
56349
57143
|
const state3 = sessionStateStore.getState(sessionID);
|
|
56350
57144
|
state3.abortDetectedAt = Date.now();
|
|
56351
|
-
log(`[${
|
|
57145
|
+
log(`[${HOOK_NAME13}] Abort detected via session.error`, { sessionID, errorName: error51.name });
|
|
56352
57146
|
}
|
|
56353
57147
|
sessionStateStore.cancelCountdown(sessionID);
|
|
56354
|
-
log(`[${
|
|
57148
|
+
log(`[${HOOK_NAME13}] session.error`, { sessionID });
|
|
56355
57149
|
return;
|
|
56356
57150
|
}
|
|
56357
57151
|
if (event.type === "session.idle") {
|
|
@@ -56466,13 +57260,13 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
56466
57260
|
const state3 = sessionStateStore.getState(sessionID);
|
|
56467
57261
|
state3.isRecovering = true;
|
|
56468
57262
|
sessionStateStore.cancelCountdown(sessionID);
|
|
56469
|
-
log(`[${
|
|
57263
|
+
log(`[${HOOK_NAME13}] Session marked as recovering`, { sessionID });
|
|
56470
57264
|
};
|
|
56471
57265
|
const markRecoveryComplete = (sessionID) => {
|
|
56472
57266
|
const state3 = sessionStateStore.getExistingState(sessionID);
|
|
56473
57267
|
if (state3) {
|
|
56474
57268
|
state3.isRecovering = false;
|
|
56475
|
-
log(`[${
|
|
57269
|
+
log(`[${HOOK_NAME13}] Session recovery complete`, { sessionID });
|
|
56476
57270
|
}
|
|
56477
57271
|
};
|
|
56478
57272
|
const handler = createTodoContinuationHandler({
|
|
@@ -56484,7 +57278,7 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
56484
57278
|
});
|
|
56485
57279
|
const cancelAllCountdowns = () => {
|
|
56486
57280
|
sessionStateStore.cancelAllCountdowns();
|
|
56487
|
-
log(`[${
|
|
57281
|
+
log(`[${HOOK_NAME13}] All countdowns cancelled`);
|
|
56488
57282
|
};
|
|
56489
57283
|
return {
|
|
56490
57284
|
handler,
|
|
@@ -56744,7 +57538,7 @@ This is a reminder only. No automatic action was taken.`;
|
|
|
56744
57538
|
}
|
|
56745
57539
|
// src/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.ts
|
|
56746
57540
|
init_logger();
|
|
56747
|
-
var
|
|
57541
|
+
var HOOK_NAME14 = "unstable-agent-babysitter";
|
|
56748
57542
|
var DEFAULT_TIMEOUT_MS = 120000;
|
|
56749
57543
|
var COOLDOWN_MS = 5 * 60 * 1000;
|
|
56750
57544
|
async function resolveMainSessionTarget(ctx, sessionID) {
|
|
@@ -56764,7 +57558,7 @@ async function resolveMainSessionTarget(ctx, sessionID) {
|
|
|
56764
57558
|
}
|
|
56765
57559
|
}
|
|
56766
57560
|
} catch (error51) {
|
|
56767
|
-
log(`[${
|
|
57561
|
+
log(`[${HOOK_NAME14}] Failed to resolve main session agent`, { sessionID, error: String(error51) });
|
|
56768
57562
|
}
|
|
56769
57563
|
return { agent, model };
|
|
56770
57564
|
}
|
|
@@ -56797,7 +57591,7 @@ async function getThinkingSummary(ctx, sessionID) {
|
|
|
56797
57591
|
return combined;
|
|
56798
57592
|
return `${combined.slice(0, THINKING_SUMMARY_MAX_CHARS)}...`;
|
|
56799
57593
|
} catch (error51) {
|
|
56800
|
-
log(`[${
|
|
57594
|
+
log(`[${HOOK_NAME14}] Failed to fetch thinking summary`, { sessionID, error: String(error51) });
|
|
56801
57595
|
return null;
|
|
56802
57596
|
}
|
|
56803
57597
|
}
|
|
@@ -56846,9 +57640,9 @@ function createUnstableAgentBabysitterHook(ctx, options) {
|
|
|
56846
57640
|
query: { directory: ctx.directory }
|
|
56847
57641
|
});
|
|
56848
57642
|
reminderCooldowns.set(task.id, now);
|
|
56849
|
-
log(`[${
|
|
57643
|
+
log(`[${HOOK_NAME14}] Reminder injected`, { taskId: task.id, sessionID: mainSessionID });
|
|
56850
57644
|
} catch (error51) {
|
|
56851
|
-
log(`[${
|
|
57645
|
+
log(`[${HOOK_NAME14}] Reminder injection failed`, { taskId: task.id, error: String(error51) });
|
|
56852
57646
|
}
|
|
56853
57647
|
}
|
|
56854
57648
|
};
|
|
@@ -57014,8 +57808,8 @@ function createWriteExistingFileGuardHook(ctx) {
|
|
|
57014
57808
|
}
|
|
57015
57809
|
const resolvedPath = normalize(isAbsolute5(filePath) ? filePath : resolve7(ctx.directory, filePath));
|
|
57016
57810
|
if (existsSync40(resolvedPath)) {
|
|
57017
|
-
const
|
|
57018
|
-
const isMatrixMarkdown = resolvedPath.startsWith(
|
|
57811
|
+
const matrixxRoot = join55(ctx.directory, ".matrixx") + sep;
|
|
57812
|
+
const isMatrixMarkdown = resolvedPath.startsWith(matrixxRoot) && resolvedPath.endsWith(".md");
|
|
57019
57813
|
if (isMatrixMarkdown) {
|
|
57020
57814
|
log("[write-existing-file-guard] Allowing .matrixx/*.md overwrite", {
|
|
57021
57815
|
sessionID: input.sessionID,
|
|
@@ -57103,7 +57897,7 @@ function createContinuationHooks(args) {
|
|
|
57103
57897
|
}
|
|
57104
57898
|
}
|
|
57105
57899
|
const backgroundNotificationHook = isHookEnabled("background-notification") ? safeHook("background-notification", () => createBackgroundNotificationHook(backgroundManager)) : null;
|
|
57106
|
-
const
|
|
57900
|
+
const architectHook = isHookEnabled("architect") ? safeHook("architect", () => createArchitectHook(ctx, {
|
|
57107
57901
|
directory: ctx.directory,
|
|
57108
57902
|
backgroundManager,
|
|
57109
57903
|
isContinuationStopped: (sessionID) => stopContinuationGuard?.isStopped(sessionID) ?? false,
|
|
@@ -57116,7 +57910,7 @@ function createContinuationHooks(args) {
|
|
|
57116
57910
|
todoContinuationEnforcer,
|
|
57117
57911
|
unstableAgentBabysitter,
|
|
57118
57912
|
backgroundNotificationHook,
|
|
57119
|
-
|
|
57913
|
+
architectHook
|
|
57120
57914
|
};
|
|
57121
57915
|
}
|
|
57122
57916
|
|
|
@@ -58105,12 +58899,18 @@ class LSPClient extends LSPClientConnection {
|
|
|
58105
58899
|
});
|
|
58106
58900
|
}
|
|
58107
58901
|
}
|
|
58902
|
+
// src/tools/lsp/lsp-server.ts
|
|
58903
|
+
init_logger();
|
|
58904
|
+
|
|
58108
58905
|
// src/tools/lsp/lsp-manager-process-cleanup.ts
|
|
58906
|
+
init_logger();
|
|
58109
58907
|
function registerLspManagerProcessCleanup(options) {
|
|
58110
58908
|
const syncCleanup = () => {
|
|
58111
58909
|
for (const [, managed] of options.getClients()) {
|
|
58112
58910
|
try {
|
|
58113
|
-
managed.client.stop().catch(() => {
|
|
58911
|
+
managed.client.stop().catch((err) => {
|
|
58912
|
+
log("[lsp] Sync cleanup failed:", err);
|
|
58913
|
+
});
|
|
58114
58914
|
} catch {}
|
|
58115
58915
|
}
|
|
58116
58916
|
options.clearClients();
|
|
@@ -58119,17 +58919,26 @@ function registerLspManagerProcessCleanup(options) {
|
|
|
58119
58919
|
const asyncCleanup = async () => {
|
|
58120
58920
|
const stopPromises = [];
|
|
58121
58921
|
for (const [, managed] of options.getClients()) {
|
|
58122
|
-
stopPromises.push(managed.client.stop().catch(() => {
|
|
58922
|
+
stopPromises.push(managed.client.stop().catch((err) => {
|
|
58923
|
+
log("[lsp] Async cleanup failed:", err);
|
|
58924
|
+
return;
|
|
58925
|
+
}));
|
|
58123
58926
|
}
|
|
58124
58927
|
await Promise.allSettled(stopPromises);
|
|
58125
58928
|
options.clearClients();
|
|
58126
58929
|
options.clearCleanupInterval();
|
|
58127
58930
|
};
|
|
58128
58931
|
process.on("exit", syncCleanup);
|
|
58129
|
-
process.on("SIGINT", () => void asyncCleanup().catch(() => {
|
|
58130
|
-
|
|
58932
|
+
process.on("SIGINT", () => void asyncCleanup().catch((err) => {
|
|
58933
|
+
log("[lsp] Signal handler cleanup failed:", err);
|
|
58934
|
+
}));
|
|
58935
|
+
process.on("SIGTERM", () => void asyncCleanup().catch((err) => {
|
|
58936
|
+
log("[lsp] Signal handler cleanup failed:", err);
|
|
58937
|
+
}));
|
|
58131
58938
|
if (process.platform === "win32") {
|
|
58132
|
-
process.on("SIGBREAK", () => void asyncCleanup().catch(() => {
|
|
58939
|
+
process.on("SIGBREAK", () => void asyncCleanup().catch((err) => {
|
|
58940
|
+
log("[lsp] Signal handler cleanup failed:", err);
|
|
58941
|
+
}));
|
|
58133
58942
|
}
|
|
58134
58943
|
}
|
|
58135
58944
|
|
|
@@ -58299,7 +59108,9 @@ class LSPServerManager {
|
|
|
58299
59108
|
}
|
|
58300
59109
|
}).catch(() => {
|
|
58301
59110
|
this.clients.delete(key);
|
|
58302
|
-
client.stop().catch(() => {
|
|
59111
|
+
client.stop().catch((err) => {
|
|
59112
|
+
log("[lsp] Warmup client stop failed:", err);
|
|
59113
|
+
});
|
|
58303
59114
|
});
|
|
58304
59115
|
}
|
|
58305
59116
|
releaseClient(root, serverId) {
|
|
@@ -71157,6 +71968,7 @@ import { existsSync as existsSync48 } from "fs";
|
|
|
71157
71968
|
var {spawn: spawn9 } = globalThis.Bun;
|
|
71158
71969
|
|
|
71159
71970
|
// src/tools/ast-grep/cli-binary-path-resolution.ts
|
|
71971
|
+
init_logger();
|
|
71160
71972
|
import { existsSync as existsSync47 } from "fs";
|
|
71161
71973
|
|
|
71162
71974
|
// src/tools/ast-grep/language-support.ts
|
|
@@ -72783,6 +73595,9 @@ function createHandoffTools(ctx) {
|
|
|
72783
73595
|
});
|
|
72784
73596
|
return { handoff };
|
|
72785
73597
|
}
|
|
73598
|
+
// src/tools/interactive-bash/tools.ts
|
|
73599
|
+
init_logger();
|
|
73600
|
+
|
|
72786
73601
|
// src/tools/interactive-bash/constants.ts
|
|
72787
73602
|
var DEFAULT_TIMEOUT_MS5 = 60000;
|
|
72788
73603
|
var BLOCKED_TMUX_SUBCOMMANDS = [
|
|
@@ -72884,7 +73699,9 @@ The Bash tool can execute these commands directly. Do NOT retry with interactive
|
|
|
72884
73699
|
const timeoutError = new Error(`Timeout after ${DEFAULT_TIMEOUT_MS5}ms`);
|
|
72885
73700
|
try {
|
|
72886
73701
|
proc.kill();
|
|
72887
|
-
proc.exited.catch(() => {
|
|
73702
|
+
proc.exited.catch((err) => {
|
|
73703
|
+
log("[interactive-bash] Process cleanup failed:", err);
|
|
73704
|
+
});
|
|
72888
73705
|
} catch {}
|
|
72889
73706
|
reject(timeoutError);
|
|
72890
73707
|
}, DEFAULT_TIMEOUT_MS5);
|
|
@@ -74591,7 +75408,7 @@ ${textContent || "(No text output)"}`;
|
|
|
74591
75408
|
}
|
|
74592
75409
|
|
|
74593
75410
|
// src/tools/background-task/create-background-output.ts
|
|
74594
|
-
var
|
|
75411
|
+
var MOUSE_AGENT = getAgentDisplayName("mouse");
|
|
74595
75412
|
function resolveToolCallID(ctx) {
|
|
74596
75413
|
if (typeof ctx.callID === "string" && ctx.callID.trim() !== "")
|
|
74597
75414
|
return ctx.callID;
|
|
@@ -74602,7 +75419,7 @@ function resolveToolCallID(ctx) {
|
|
|
74602
75419
|
return;
|
|
74603
75420
|
}
|
|
74604
75421
|
function formatResolvedTitle(task) {
|
|
74605
|
-
const label = task.agent ===
|
|
75422
|
+
const label = task.agent === MOUSE_AGENT && task.category ? task.category : task.agent;
|
|
74606
75423
|
return `${label} - ${task.description}`;
|
|
74607
75424
|
}
|
|
74608
75425
|
function isTaskActiveStatus(status) {
|
|
@@ -74713,6 +75530,376 @@ function createBackgroundOutput(manager, client2) {
|
|
|
74713
75530
|
}
|
|
74714
75531
|
// src/tools/background-task/create-background-task.ts
|
|
74715
75532
|
init_logger();
|
|
75533
|
+
// src/tools/assembly/constants.ts
|
|
75534
|
+
var DEFAULT_VOTERS = 3;
|
|
75535
|
+
var DEFAULT_ROUNDS = 2;
|
|
75536
|
+
var VOTER_TIMEOUT_MS = 120000;
|
|
75537
|
+
var POLL_INTERVAL_MS = 1000;
|
|
75538
|
+
var DEFAULT_PROVIDERS = [
|
|
75539
|
+
{ providerID: "anthropic", modelID: "claude-sonnet-4-20250514" },
|
|
75540
|
+
{ providerID: "openai", modelID: "gpt-4o" },
|
|
75541
|
+
{ providerID: "google", modelID: "gemini-2.5-pro" }
|
|
75542
|
+
];
|
|
75543
|
+
|
|
75544
|
+
// src/tools/assembly/provider-selector.ts
|
|
75545
|
+
function selectProviders(count, modelsOverride, config4) {
|
|
75546
|
+
if (modelsOverride && modelsOverride.length > 0) {
|
|
75547
|
+
return modelsOverride.slice(0, count).map((m) => {
|
|
75548
|
+
const parts = m.split(":");
|
|
75549
|
+
if (parts.length >= 2) {
|
|
75550
|
+
return { providerID: parts[0], modelID: parts.slice(1).join(":") };
|
|
75551
|
+
}
|
|
75552
|
+
return { providerID: "custom", modelID: m };
|
|
75553
|
+
});
|
|
75554
|
+
}
|
|
75555
|
+
const configuredProviders = config4?.providers ?? DEFAULT_PROVIDERS;
|
|
75556
|
+
if (configuredProviders.length === 0) {
|
|
75557
|
+
return DEFAULT_PROVIDERS.slice(0, count);
|
|
75558
|
+
}
|
|
75559
|
+
if (configuredProviders.length <= count) {
|
|
75560
|
+
return configuredProviders;
|
|
75561
|
+
}
|
|
75562
|
+
const selected = [];
|
|
75563
|
+
const usedProviderIds = new Set;
|
|
75564
|
+
for (const p of configuredProviders) {
|
|
75565
|
+
if (selected.length >= count)
|
|
75566
|
+
break;
|
|
75567
|
+
if (!usedProviderIds.has(p.providerID)) {
|
|
75568
|
+
selected.push(p);
|
|
75569
|
+
usedProviderIds.add(p.providerID);
|
|
75570
|
+
}
|
|
75571
|
+
}
|
|
75572
|
+
if (selected.length < count) {
|
|
75573
|
+
for (const p of configuredProviders) {
|
|
75574
|
+
if (selected.length >= count)
|
|
75575
|
+
break;
|
|
75576
|
+
if (!selected.includes(p)) {
|
|
75577
|
+
selected.push(p);
|
|
75578
|
+
}
|
|
75579
|
+
}
|
|
75580
|
+
}
|
|
75581
|
+
return selected;
|
|
75582
|
+
}
|
|
75583
|
+
|
|
75584
|
+
// src/tools/assembly/synthesizer.ts
|
|
75585
|
+
async function synthesizeResults(manager, question, voterResults, round, parentSessionID, parentMessageID) {
|
|
75586
|
+
const voterContext = voterResults.map((v, i) => `--- Voter ${i + 1} (${v.modelLabel}) ---
|
|
75587
|
+
Status: ${v.status}
|
|
75588
|
+
${v.status === "completed" ? v.reasoning : `Error: ${v.error}`}`).join(`
|
|
75589
|
+
|
|
75590
|
+
`);
|
|
75591
|
+
const synthesisPrompt = `You are the synthesis agent. Multiple AI voters have independently analyzed a question. Your task is to synthesize their reasoning into a unified consensus.
|
|
75592
|
+
|
|
75593
|
+
## Question
|
|
75594
|
+
${question}
|
|
75595
|
+
|
|
75596
|
+
## Voter Analyses
|
|
75597
|
+
${voterContext}
|
|
75598
|
+
|
|
75599
|
+
## Synthesis Round
|
|
75600
|
+
This is round #${round}.
|
|
75601
|
+
|
|
75602
|
+
## Your Task
|
|
75603
|
+
1. Identify points of agreement across all voters
|
|
75604
|
+
2. Identify points of disagreement or divergence
|
|
75605
|
+
3. Weigh the strength of evidence and reasoning from each
|
|
75606
|
+
4. Produce a unified consensus with confidence level
|
|
75607
|
+
|
|
75608
|
+
## Output Format
|
|
75609
|
+
## Consensus
|
|
75610
|
+
[Your unified answer/conclusion]
|
|
75611
|
+
|
|
75612
|
+
## Confidence Level
|
|
75613
|
+
[High/Medium/Low - with rationale]
|
|
75614
|
+
|
|
75615
|
+
## Key Agreements
|
|
75616
|
+
- [Point 1]
|
|
75617
|
+
- [Point 2]
|
|
75618
|
+
|
|
75619
|
+
## Key Disagreements
|
|
75620
|
+
- [Point 1]
|
|
75621
|
+
- [Point 2]
|
|
75622
|
+
`;
|
|
75623
|
+
const synthesisTask = await manager.launch({
|
|
75624
|
+
description: `Assembly synthesis round ${round}`,
|
|
75625
|
+
prompt: synthesisPrompt,
|
|
75626
|
+
agent: "mouse",
|
|
75627
|
+
parentSessionID,
|
|
75628
|
+
parentMessageID,
|
|
75629
|
+
model: {
|
|
75630
|
+
providerID: "anthropic",
|
|
75631
|
+
modelID: "claude-sonnet-4-20250514",
|
|
75632
|
+
temperature: 0.1
|
|
75633
|
+
}
|
|
75634
|
+
});
|
|
75635
|
+
const deadline = Date.now() + 120000;
|
|
75636
|
+
while (Date.now() < deadline) {
|
|
75637
|
+
const current = manager.getTask(synthesisTask.id);
|
|
75638
|
+
if (!current)
|
|
75639
|
+
break;
|
|
75640
|
+
if (current.status === "completed") {
|
|
75641
|
+
return parseSynthesis(current.result ?? "", round);
|
|
75642
|
+
}
|
|
75643
|
+
if (current.status === "error" || current.status === "cancelled" || current.status === "interrupt") {
|
|
75644
|
+
return {
|
|
75645
|
+
round,
|
|
75646
|
+
consensus: `Synthesis failed: ${current.error ?? "Unknown"}`,
|
|
75647
|
+
confidence: "low",
|
|
75648
|
+
disagreements: voterResults.filter((v) => v.status === "completed").map((v) => v.modelLabel)
|
|
75649
|
+
};
|
|
75650
|
+
}
|
|
75651
|
+
await delay4(POLL_INTERVAL_MS);
|
|
75652
|
+
}
|
|
75653
|
+
return {
|
|
75654
|
+
round,
|
|
75655
|
+
consensus: "Synthesis timed out",
|
|
75656
|
+
confidence: "low",
|
|
75657
|
+
disagreements: voterResults.filter((v) => v.status === "completed").map((v) => v.modelLabel)
|
|
75658
|
+
};
|
|
75659
|
+
}
|
|
75660
|
+
function parseSynthesis(text, round) {
|
|
75661
|
+
const confidenceMatch = text.match(/## Confidence Level\s*\n([^\n]+)/);
|
|
75662
|
+
const confidenceRaw = (confidenceMatch?.[1] ?? "medium").toLowerCase();
|
|
75663
|
+
const confidence = confidenceRaw.includes("high") ? "high" : confidenceRaw.includes("low") ? "low" : "medium";
|
|
75664
|
+
const consensusMatch = text.match(/## Consensus\s*\n([\s\S]*?)(?=\n## Confidence Level|\n## Key Agreements|\n$)/);
|
|
75665
|
+
const consensus = consensusMatch?.[1]?.trim() ?? text;
|
|
75666
|
+
const disagreements = [];
|
|
75667
|
+
const disagreementsMatch = text.match(/## Key Disagreements\s*\n([\s\S]*?)(?=\n## |$)/);
|
|
75668
|
+
if (disagreementsMatch) {
|
|
75669
|
+
const lines = disagreementsMatch[1].trim().split(`
|
|
75670
|
+
`);
|
|
75671
|
+
for (const line of lines) {
|
|
75672
|
+
const trimmed = line.replace(/^[-*]\s*/, "").trim();
|
|
75673
|
+
if (trimmed)
|
|
75674
|
+
disagreements.push(trimmed);
|
|
75675
|
+
}
|
|
75676
|
+
}
|
|
75677
|
+
return { round, consensus, confidence, disagreements };
|
|
75678
|
+
}
|
|
75679
|
+
function delay4(ms) {
|
|
75680
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
75681
|
+
}
|
|
75682
|
+
|
|
75683
|
+
// src/tools/assembly/voter-spawner.ts
|
|
75684
|
+
async function spawnVoters(manager, question, providers, parentSessionID, parentMessageID) {
|
|
75685
|
+
const tasks = providers.map((provider, index) => manager.launch({
|
|
75686
|
+
description: `Assembly voter ${index + 1}`,
|
|
75687
|
+
prompt: `You are voter #${index + 1} in a multi-model assembly process. Analyze the following question independently and provide your reasoning. Be thorough and consider multiple angles.
|
|
75688
|
+
|
|
75689
|
+
Question: ${question}
|
|
75690
|
+
|
|
75691
|
+
Output your analysis in this format:
|
|
75692
|
+
## Reasoning
|
|
75693
|
+
[Your detailed step-by-step reasoning]
|
|
75694
|
+
|
|
75695
|
+
## Conclusion
|
|
75696
|
+
[Your final answer/conclusion]
|
|
75697
|
+
|
|
75698
|
+
## Confidence
|
|
75699
|
+
[High/Medium/Low - with justification]`,
|
|
75700
|
+
agent: "mouse",
|
|
75701
|
+
parentSessionID,
|
|
75702
|
+
parentMessageID,
|
|
75703
|
+
model: {
|
|
75704
|
+
providerID: provider.providerID,
|
|
75705
|
+
modelID: provider.modelID,
|
|
75706
|
+
temperature: 0.3
|
|
75707
|
+
}
|
|
75708
|
+
}));
|
|
75709
|
+
const launchedTasks = await Promise.all(tasks);
|
|
75710
|
+
const results = await Promise.all(launchedTasks.map(async (task, index) => {
|
|
75711
|
+
const provider = providers[index];
|
|
75712
|
+
const deadline = Date.now() + VOTER_TIMEOUT_MS;
|
|
75713
|
+
while (Date.now() < deadline) {
|
|
75714
|
+
const current = manager.getTask(task.id);
|
|
75715
|
+
if (!current)
|
|
75716
|
+
break;
|
|
75717
|
+
if (current.status === "completed") {
|
|
75718
|
+
return {
|
|
75719
|
+
voterIndex: index,
|
|
75720
|
+
modelLabel: `${provider.providerID}:${provider.modelID}`,
|
|
75721
|
+
reasoning: current.result ?? "",
|
|
75722
|
+
status: "completed"
|
|
75723
|
+
};
|
|
75724
|
+
}
|
|
75725
|
+
if (current.status === "error") {
|
|
75726
|
+
return {
|
|
75727
|
+
voterIndex: index,
|
|
75728
|
+
modelLabel: `${provider.providerID}:${provider.modelID}`,
|
|
75729
|
+
reasoning: "",
|
|
75730
|
+
status: "error",
|
|
75731
|
+
error: current.error ?? "Unknown error"
|
|
75732
|
+
};
|
|
75733
|
+
}
|
|
75734
|
+
if (current.status === "cancelled" || current.status === "interrupt") {
|
|
75735
|
+
return {
|
|
75736
|
+
voterIndex: index,
|
|
75737
|
+
modelLabel: `${provider.providerID}:${provider.modelID}`,
|
|
75738
|
+
reasoning: "",
|
|
75739
|
+
status: "error",
|
|
75740
|
+
error: `Voter ${index + 1} was ${current.status}`
|
|
75741
|
+
};
|
|
75742
|
+
}
|
|
75743
|
+
await delay5(POLL_INTERVAL_MS);
|
|
75744
|
+
}
|
|
75745
|
+
return {
|
|
75746
|
+
voterIndex: index,
|
|
75747
|
+
modelLabel: `${provider.providerID}:${provider.modelID}`,
|
|
75748
|
+
reasoning: "",
|
|
75749
|
+
status: "timeout",
|
|
75750
|
+
error: `Voter ${index + 1} timed out after ${VOTER_TIMEOUT_MS}ms`
|
|
75751
|
+
};
|
|
75752
|
+
}));
|
|
75753
|
+
return results;
|
|
75754
|
+
}
|
|
75755
|
+
function delay5(ms) {
|
|
75756
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
75757
|
+
}
|
|
75758
|
+
|
|
75759
|
+
// src/tools/assembly/assembly-executor.ts
|
|
75760
|
+
async function executeAssembly(input) {
|
|
75761
|
+
const { manager, args, config: config4, sessionID, messageID } = input;
|
|
75762
|
+
const voterCount = args.voters ?? config4?.default_voters ?? DEFAULT_VOTERS;
|
|
75763
|
+
const roundCount = args.rounds ?? config4?.default_rounds ?? DEFAULT_ROUNDS;
|
|
75764
|
+
const providers = selectProviders(voterCount, args.models, config4);
|
|
75765
|
+
let currentVoterResults = [];
|
|
75766
|
+
const synthesisRounds = [];
|
|
75767
|
+
for (let round = 0;round < roundCount; round++) {
|
|
75768
|
+
if (round === 0) {
|
|
75769
|
+
currentVoterResults = await spawnVoters(manager, args.question, providers, sessionID, messageID);
|
|
75770
|
+
}
|
|
75771
|
+
const synthesis = await synthesizeResults(manager, args.question, currentVoterResults, round + 1, sessionID, messageID);
|
|
75772
|
+
synthesisRounds.push(synthesis);
|
|
75773
|
+
if (synthesis.confidence === "high" && round < roundCount - 1) {
|
|
75774
|
+
break;
|
|
75775
|
+
}
|
|
75776
|
+
if (round < roundCount - 1) {
|
|
75777
|
+
currentVoterResults = currentVoterResults.map((v, _i) => ({
|
|
75778
|
+
...v,
|
|
75779
|
+
reasoning: `${v.reasoning}
|
|
75780
|
+
|
|
75781
|
+
--- Round ${round + 1} Synthesis Feedback ---
|
|
75782
|
+
${synthesis.consensus}`
|
|
75783
|
+
}));
|
|
75784
|
+
}
|
|
75785
|
+
}
|
|
75786
|
+
const finalSynthesis = synthesisRounds[synthesisRounds.length - 1];
|
|
75787
|
+
return {
|
|
75788
|
+
question: args.question,
|
|
75789
|
+
voters: currentVoterResults,
|
|
75790
|
+
rounds: synthesisRounds,
|
|
75791
|
+
finalConsensus: formatFinalConsensus(args.question, finalSynthesis, currentVoterResults)
|
|
75792
|
+
};
|
|
75793
|
+
}
|
|
75794
|
+
function formatFinalConsensus(question, final, voterResults) {
|
|
75795
|
+
const completed = voterResults.filter((v) => v.status === "completed");
|
|
75796
|
+
const errored = voterResults.filter((v) => v.status !== "completed");
|
|
75797
|
+
let output = `# Assembly Result
|
|
75798
|
+
|
|
75799
|
+
`;
|
|
75800
|
+
output += `**Question**: ${question}
|
|
75801
|
+
`;
|
|
75802
|
+
output += `**Confidence**: ${final.confidence}
|
|
75803
|
+
`;
|
|
75804
|
+
output += `**Voters**: ${completed.length}/${voterResults.length} completed`;
|
|
75805
|
+
if (errored.length > 0) {
|
|
75806
|
+
output += ` (${errored.map((v) => `${v.voterIndex + 1}: ${v.error}`).join(", ")})`;
|
|
75807
|
+
}
|
|
75808
|
+
output += `
|
|
75809
|
+
|
|
75810
|
+
## Final Consensus
|
|
75811
|
+
|
|
75812
|
+
${final.consensus}
|
|
75813
|
+
|
|
75814
|
+
`;
|
|
75815
|
+
if (final.disagreements.length > 0) {
|
|
75816
|
+
output += `## Disagreements
|
|
75817
|
+
|
|
75818
|
+
`;
|
|
75819
|
+
for (const d of final.disagreements) {
|
|
75820
|
+
output += `- ${d}
|
|
75821
|
+
`;
|
|
75822
|
+
}
|
|
75823
|
+
output += `
|
|
75824
|
+
`;
|
|
75825
|
+
}
|
|
75826
|
+
output += `## Per-Voter Reasoning
|
|
75827
|
+
|
|
75828
|
+
`;
|
|
75829
|
+
for (const v of voterResults) {
|
|
75830
|
+
output += `### Voter ${v.voterIndex + 1} (${v.modelLabel})
|
|
75831
|
+
`;
|
|
75832
|
+
output += `Status: ${v.status}
|
|
75833
|
+
`;
|
|
75834
|
+
if (v.status === "completed") {
|
|
75835
|
+
output += `
|
|
75836
|
+
${v.reasoning}
|
|
75837
|
+
|
|
75838
|
+
`;
|
|
75839
|
+
} else {
|
|
75840
|
+
output += `Error: ${v.error}
|
|
75841
|
+
|
|
75842
|
+
`;
|
|
75843
|
+
}
|
|
75844
|
+
}
|
|
75845
|
+
return output;
|
|
75846
|
+
}
|
|
75847
|
+
|
|
75848
|
+
// src/tools/assembly/tool-description.ts
|
|
75849
|
+
var TOOL_DESCRIPTION = `
|
|
75850
|
+
Spawns multiple AI agents (voters) with different model providers to independently analyze a question or decision, then synthesizes their reasoning into a unified consensus.
|
|
75851
|
+
|
|
75852
|
+
## When to use
|
|
75853
|
+
- Making critical decisions that benefit from multiple perspectives
|
|
75854
|
+
- Evaluating complex tradeoffs where bias reduction matters
|
|
75855
|
+
- Validating architectural choices through multi-model reasoning
|
|
75856
|
+
- When you need confidence scoring on analysis
|
|
75857
|
+
|
|
75858
|
+
## How it works
|
|
75859
|
+
1. **Voter selection**: Auto-selects N models from configured providers (or use your override)
|
|
75860
|
+
2. **Independent voting**: Each voter independently analyzes the question
|
|
75861
|
+
3. **Synthesis**: All voter outputs are fed to a synthesis round that identifies agreement, disagreement, and confidence
|
|
75862
|
+
4. **Multi-round**: Optionally repeat synthesis for deeper convergence
|
|
75863
|
+
|
|
75864
|
+
## Output format
|
|
75865
|
+
Returns a structured consensus with:
|
|
75866
|
+
- Unified answer/conclusion
|
|
75867
|
+
- Confidence level (high/medium/low)
|
|
75868
|
+
- Key disagreements between voters
|
|
75869
|
+
- Per-voter reasoning traces
|
|
75870
|
+
`;
|
|
75871
|
+
|
|
75872
|
+
// src/tools/assembly/types.ts
|
|
75873
|
+
var AssemblyArgsSchema = exports_external.object({
|
|
75874
|
+
question: exports_external.string().min(1).describe("The question or decision to reach consensus on"),
|
|
75875
|
+
voters: exports_external.number().int().min(2).max(5).optional().describe("Number of voters (default: 3)"),
|
|
75876
|
+
rounds: exports_external.number().int().min(1).max(3).optional().describe("Number of synthesis rounds (default: 2)"),
|
|
75877
|
+
models: exports_external.array(exports_external.string()).optional().describe("Override auto-selected models (e.g., ['anthropic:claude-sonnet-4-20250514', 'openai:gpt-4o'])")
|
|
75878
|
+
});
|
|
75879
|
+
|
|
75880
|
+
// src/tools/assembly/tools.ts
|
|
75881
|
+
function createAssemblyTool(options) {
|
|
75882
|
+
return tool({
|
|
75883
|
+
description: TOOL_DESCRIPTION,
|
|
75884
|
+
args: {
|
|
75885
|
+
question: tool.schema.string().describe("The question or decision to reach consensus on"),
|
|
75886
|
+
voters: tool.schema.number().optional().describe("Number of voters (2-5, default: 3)"),
|
|
75887
|
+
rounds: tool.schema.number().optional().describe("Number of synthesis rounds (1-3, default: 2)"),
|
|
75888
|
+
models: tool.schema.array(tool.schema.string()).optional().describe("Override auto-selected models")
|
|
75889
|
+
},
|
|
75890
|
+
async execute(args, toolContext) {
|
|
75891
|
+
const parsed = AssemblyArgsSchema.parse(args);
|
|
75892
|
+
const output = await executeAssembly({
|
|
75893
|
+
manager: options.manager,
|
|
75894
|
+
args: parsed,
|
|
75895
|
+
config: options.pluginConfig?.assembly,
|
|
75896
|
+
sessionID: toolContext.sessionID,
|
|
75897
|
+
messageID: toolContext.messageID
|
|
75898
|
+
});
|
|
75899
|
+
return output.finalConsensus;
|
|
75900
|
+
}
|
|
75901
|
+
});
|
|
75902
|
+
}
|
|
74716
75903
|
// src/tools/delegate-agent/constants.ts
|
|
74717
75904
|
var ALLOWED_AGENTS = [
|
|
74718
75905
|
"trinity",
|
|
@@ -74812,7 +75999,7 @@ Use \`background_output\` tool with task_id="${task.id}" to check progress:
|
|
|
74812
75999
|
// src/tools/delegate-agent/completion-poller.ts
|
|
74813
76000
|
async function waitForCompletion(sessionID, toolContext, ctx) {
|
|
74814
76001
|
log(`[delegate_agent] Polling for completion...`);
|
|
74815
|
-
const
|
|
76002
|
+
const POLL_INTERVAL_MS2 = 500;
|
|
74816
76003
|
const MAX_POLL_TIME_MS = 5 * 60 * 1000;
|
|
74817
76004
|
const pollStart = Date.now();
|
|
74818
76005
|
let lastMsgCount = 0;
|
|
@@ -74823,7 +76010,7 @@ async function waitForCompletion(sessionID, toolContext, ctx) {
|
|
|
74823
76010
|
log(`[delegate_agent] Aborted by user`);
|
|
74824
76011
|
throw new Error("Task aborted.");
|
|
74825
76012
|
}
|
|
74826
|
-
await new Promise((resolve10) => setTimeout(resolve10,
|
|
76013
|
+
await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS2));
|
|
74827
76014
|
const statusResult = await ctx.client.session.status();
|
|
74828
76015
|
const allStatuses = normalizeSDKResponse(statusResult, {});
|
|
74829
76016
|
const sessionStatus = allStatuses[sessionID];
|
|
@@ -75117,7 +76304,7 @@ ${task.agent ? `subagent: ${task.agent}
|
|
|
75117
76304
|
}
|
|
75118
76305
|
}
|
|
75119
76306
|
// src/tools/delegate-task/timing.ts
|
|
75120
|
-
var
|
|
76307
|
+
var POLL_INTERVAL_MS2 = 1000;
|
|
75121
76308
|
var MIN_STABILITY_TIME_MS = 1e4;
|
|
75122
76309
|
var STABILITY_POLLS_REQUIRED = 3;
|
|
75123
76310
|
var WAIT_FOR_SESSION_INTERVAL_MS = 100;
|
|
@@ -75126,7 +76313,7 @@ var MAX_POLL_TIME_MS = 10 * 60 * 1000;
|
|
|
75126
76313
|
var SESSION_CONTINUATION_STABILITY_MS = 5000;
|
|
75127
76314
|
function getTimingConfig() {
|
|
75128
76315
|
return {
|
|
75129
|
-
POLL_INTERVAL_MS,
|
|
76316
|
+
POLL_INTERVAL_MS: POLL_INTERVAL_MS2,
|
|
75130
76317
|
MIN_STABILITY_TIME_MS,
|
|
75131
76318
|
STABILITY_POLLS_REQUIRED,
|
|
75132
76319
|
WAIT_FOR_SESSION_INTERVAL_MS,
|
|
@@ -75370,11 +76557,11 @@ function parseModelString2(model) {
|
|
|
75370
76557
|
}
|
|
75371
76558
|
|
|
75372
76559
|
// src/tools/delegate-task/mouse-agent.ts
|
|
75373
|
-
var
|
|
76560
|
+
var MOUSE_AGENT2 = "mouse";
|
|
75374
76561
|
|
|
75375
76562
|
// src/tools/delegate-task/category-resolver.ts
|
|
75376
76563
|
async function resolveCategoryExecution(args, executorCtx, inheritedModel, systemDefaultModel) {
|
|
75377
|
-
const { client: client2, userCategories,
|
|
76564
|
+
const { client: client2, userCategories, mouseModel } = executorCtx;
|
|
75378
76565
|
const availableModels = await getAvailableModelsForDelegateTask(client2);
|
|
75379
76566
|
const categoryName = args.category;
|
|
75380
76567
|
const enabledCategories = mergeCategories(userCategories);
|
|
@@ -75419,16 +76606,16 @@ Available categories: ${allCategoryNames}`
|
|
|
75419
76606
|
let actualModel;
|
|
75420
76607
|
let modelInfo;
|
|
75421
76608
|
let categoryModel;
|
|
75422
|
-
const overrideModel =
|
|
76609
|
+
const overrideModel = mouseModel;
|
|
75423
76610
|
const explicitCategoryModel = userCategories?.[args.category]?.model;
|
|
75424
76611
|
if (!requirement) {
|
|
75425
|
-
actualModel = explicitCategoryModel ??
|
|
76612
|
+
actualModel = explicitCategoryModel ?? resolved.model ?? overrideModel;
|
|
75426
76613
|
if (actualModel) {
|
|
75427
|
-
modelInfo = explicitCategoryModel
|
|
76614
|
+
modelInfo = explicitCategoryModel ? { model: actualModel, type: "user-defined", source: "override" } : resolved.model ? { model: actualModel, type: "system-default", source: "system-default" } : { model: actualModel, type: "user-defined", source: "override" };
|
|
75428
76615
|
}
|
|
75429
76616
|
} else {
|
|
75430
76617
|
const resolution = resolveModelForDelegateTask({
|
|
75431
|
-
userModel: explicitCategoryModel
|
|
76618
|
+
userModel: explicitCategoryModel,
|
|
75432
76619
|
categoryDefaultModel: resolved.model,
|
|
75433
76620
|
fallbackChain: requirement.fallbackChain,
|
|
75434
76621
|
availableModels,
|
|
@@ -75448,7 +76635,7 @@ Available categories: ${allCategoryNames}`
|
|
|
75448
76635
|
error: `Invalid model format "${actualModel}". Expected "provider/model" format (e.g., "anthropic/claude-sonnet-4-5").`
|
|
75449
76636
|
};
|
|
75450
76637
|
}
|
|
75451
|
-
const type = explicitCategoryModel
|
|
76638
|
+
const type = explicitCategoryModel ? "user-defined" : systemDefaultModel && actualModel === systemDefaultModel ? "system-default" : "category-default";
|
|
75452
76639
|
const source = type === "user-defined" ? "override" : type === "system-default" ? "system-default" : "category-default";
|
|
75453
76640
|
modelInfo = { model: actualModel, type, source };
|
|
75454
76641
|
const parsedModel = parseModelString2(actualModel);
|
|
@@ -75484,7 +76671,7 @@ Available categories: ${categoryNames.join(", ")}`
|
|
|
75484
76671
|
const unstableModel = actualModel?.toLowerCase();
|
|
75485
76672
|
const isUnstableAgent = resolved.config.is_unstable_agent === true || (unstableModel ? unstableModel.includes("gemini") || unstableModel.includes("minimax") : false);
|
|
75486
76673
|
return {
|
|
75487
|
-
agentToUse:
|
|
76674
|
+
agentToUse: MOUSE_AGENT2,
|
|
75488
76675
|
categoryModel,
|
|
75489
76676
|
categoryPromptAppend,
|
|
75490
76677
|
modelInfo,
|
|
@@ -75544,11 +76731,11 @@ async function resolveSubagentExecution(args, executorCtx, parentAgent, category
|
|
|
75544
76731
|
return { agentToUse: "", categoryModel: undefined, error: `Agent name cannot be empty.` };
|
|
75545
76732
|
}
|
|
75546
76733
|
const agentName = args.subagent_type.trim();
|
|
75547
|
-
if (agentName.toLowerCase() ===
|
|
76734
|
+
if (agentName.toLowerCase() === MOUSE_AGENT2.toLowerCase()) {
|
|
75548
76735
|
return {
|
|
75549
76736
|
agentToUse: "",
|
|
75550
76737
|
categoryModel: undefined,
|
|
75551
|
-
error: `Cannot use subagent_type="${
|
|
76738
|
+
error: `Cannot use subagent_type="${MOUSE_AGENT2}" directly. Use category parameter instead (e.g., ${categoryExamples}).
|
|
75552
76739
|
|
|
75553
76740
|
Mouse is spawned automatically when you specify a category. Pick the appropriate category for your task domain.`
|
|
75554
76741
|
};
|
|
@@ -75629,6 +76816,8 @@ Create the work plan directly - that's your job as the planning agent.`
|
|
|
75629
76816
|
return { agentToUse, categoryModel };
|
|
75630
76817
|
}
|
|
75631
76818
|
// src/features/task-toast-manager/manager.ts
|
|
76819
|
+
init_logger();
|
|
76820
|
+
|
|
75632
76821
|
class TaskToastManager {
|
|
75633
76822
|
tasks = new Map;
|
|
75634
76823
|
client;
|
|
@@ -75748,7 +76937,9 @@ class TaskToastManager {
|
|
|
75748
76937
|
variant: "info",
|
|
75749
76938
|
duration: running.length + queued.length > 2 ? 5000 : 3000
|
|
75750
76939
|
}
|
|
75751
|
-
}).catch(() => {
|
|
76940
|
+
}).catch((err) => {
|
|
76941
|
+
log("[task-toast] Toast failed:", err);
|
|
76942
|
+
});
|
|
75752
76943
|
}
|
|
75753
76944
|
showCompletionToast(task) {
|
|
75754
76945
|
const tuiClient = this.client;
|
|
@@ -75770,7 +76961,9 @@ Still running: ${remaining.length} | Queued: ${queued.length}`;
|
|
|
75770
76961
|
variant: "success",
|
|
75771
76962
|
duration: 5000
|
|
75772
76963
|
}
|
|
75773
|
-
}).catch(() => {
|
|
76964
|
+
}).catch((err) => {
|
|
76965
|
+
log("[task-toast] Toast failed:", err);
|
|
76966
|
+
});
|
|
75774
76967
|
}
|
|
75775
76968
|
}
|
|
75776
76969
|
var instance = null;
|
|
@@ -76245,18 +77438,19 @@ async function sendSyncPrompt(client2, input, deps = sendSyncPromptDeps) {
|
|
|
76245
77438
|
try {
|
|
76246
77439
|
await deps.promptWithModelSuggestionRetry(client2, promptArgs);
|
|
76247
77440
|
} catch (promptError) {
|
|
77441
|
+
let lastError = promptError;
|
|
76248
77442
|
if (isOracleAgent2(input.agentToUse) && isUnexpectedEofError(promptError)) {
|
|
76249
77443
|
try {
|
|
76250
77444
|
await deps.promptSyncWithModelSuggestionRetry(client2, promptArgs);
|
|
76251
77445
|
return null;
|
|
76252
77446
|
} catch (oracleRetryError) {
|
|
76253
|
-
|
|
77447
|
+
lastError = oracleRetryError;
|
|
76254
77448
|
}
|
|
76255
77449
|
}
|
|
76256
77450
|
if (input.toastManager && input.taskId !== undefined) {
|
|
76257
77451
|
input.toastManager.removeTask(input.taskId);
|
|
76258
77452
|
}
|
|
76259
|
-
const errorMessage =
|
|
77453
|
+
const errorMessage = lastError instanceof Error ? lastError.message : String(lastError);
|
|
76260
77454
|
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
|
|
76261
77455
|
return formatDetailedError(new Error(`Agent "${input.agentToUse}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`), {
|
|
76262
77456
|
operation: "Send prompt to agent",
|
|
@@ -76266,7 +77460,7 @@ async function sendSyncPrompt(client2, input, deps = sendSyncPromptDeps) {
|
|
|
76266
77460
|
category: input.args.category
|
|
76267
77461
|
});
|
|
76268
77462
|
}
|
|
76269
|
-
return formatDetailedError(
|
|
77463
|
+
return formatDetailedError(lastError, {
|
|
76270
77464
|
operation: "Send prompt",
|
|
76271
77465
|
args: input.args,
|
|
76272
77466
|
sessionID: input.sessionID,
|
|
@@ -76429,7 +77623,9 @@ session_id: ${sessionID}
|
|
|
76429
77623
|
} finally {
|
|
76430
77624
|
if (syncSessionID) {
|
|
76431
77625
|
subagentSessions.delete(syncSessionID);
|
|
76432
|
-
client2.session.abort({ path: { id: syncSessionID } }).catch(() => {
|
|
77626
|
+
client2.session.abort({ path: { id: syncSessionID } }).catch((err) => {
|
|
77627
|
+
log("[delegate-task] Sync session abort failed:", err);
|
|
77628
|
+
});
|
|
76433
77629
|
}
|
|
76434
77630
|
}
|
|
76435
77631
|
}
|
|
@@ -76685,13 +77881,13 @@ Prompts MUST be in English.`;
|
|
|
76685
77881
|
async execute(args, toolContext) {
|
|
76686
77882
|
const ctx = toolContext;
|
|
76687
77883
|
if (args.category) {
|
|
76688
|
-
if (args.subagent_type && args.subagent_type !==
|
|
77884
|
+
if (args.subagent_type && args.subagent_type !== MOUSE_AGENT2) {
|
|
76689
77885
|
log("[task] category provided - overriding subagent_type to mouse", {
|
|
76690
77886
|
category: args.category,
|
|
76691
77887
|
subagent_type: args.subagent_type
|
|
76692
77888
|
});
|
|
76693
77889
|
}
|
|
76694
|
-
args.subagent_type =
|
|
77890
|
+
args.subagent_type = MOUSE_AGENT2;
|
|
76695
77891
|
}
|
|
76696
77892
|
await ctx.metadata?.({
|
|
76697
77893
|
title: args.description
|
|
@@ -78734,7 +79930,7 @@ function createSessionHooks(args) {
|
|
|
78734
79930
|
const delegateTaskRetry = isHookEnabled("delegate-task-retry") ? safeHook("delegate-task-retry", () => createDelegateTaskRetryHook(ctx)) : null;
|
|
78735
79931
|
const startWork = isHookEnabled("start-work") ? safeHook("start-work", () => createStartWorkHook(ctx)) : null;
|
|
78736
79932
|
const prometheusMdOnly = isHookEnabled("prometheus-md-only") ? safeHook("prometheus-md-only", () => createOracleMdOnlyHook(ctx)) : null;
|
|
78737
|
-
const
|
|
79933
|
+
const mouseNotepad = isHookEnabled("mouse-notepad") ? safeHook("mouse-notepad", () => createMouseNotepadHook(ctx)) : null;
|
|
78738
79934
|
const questionLabelTruncator = createQuestionLabelTruncatorHook();
|
|
78739
79935
|
const taskResumeInfo = createTaskResumeInfoHook();
|
|
78740
79936
|
const anthropicEffort = isHookEnabled("anthropic-effort") ? safeHook("anthropic-effort", () => createAnthropicEffortHook()) : null;
|
|
@@ -78758,7 +79954,7 @@ function createSessionHooks(args) {
|
|
|
78758
79954
|
delegateTaskRetry,
|
|
78759
79955
|
startWork,
|
|
78760
79956
|
prometheusMdOnly,
|
|
78761
|
-
|
|
79957
|
+
mouseNotepad,
|
|
78762
79958
|
questionLabelTruncator,
|
|
78763
79959
|
taskResumeInfo,
|
|
78764
79960
|
anthropicEffort,
|
|
@@ -79156,10 +80352,6 @@ class ConcurrencyManager {
|
|
|
79156
80352
|
return this.queues.get(model)?.length ?? 0;
|
|
79157
80353
|
}
|
|
79158
80354
|
}
|
|
79159
|
-
// src/features/background-agent/manager.ts
|
|
79160
|
-
import { existsSync as existsSync57, readdirSync as readdirSync19, readFileSync as readFileSync39 } from "fs";
|
|
79161
|
-
import { join as join72 } from "path";
|
|
79162
|
-
|
|
79163
80355
|
// src/features/background-agent/constants.ts
|
|
79164
80356
|
var TASK_TTL_MS = 30 * 60 * 1000;
|
|
79165
80357
|
var MIN_STABILITY_TIME_MS2 = 10 * 1000;
|
|
@@ -79174,6 +80366,59 @@ var POLLING_INTERVAL_MS = 3000;
|
|
|
79174
80366
|
var PRUNE_THROTTLE_MS = 30000;
|
|
79175
80367
|
var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
|
|
79176
80368
|
|
|
80369
|
+
// src/features/background-agent/error-helpers.ts
|
|
80370
|
+
function formatDuration2(start, end) {
|
|
80371
|
+
const duration5 = (end ?? new Date).getTime() - start.getTime();
|
|
80372
|
+
const seconds = Math.floor(duration5 / 1000);
|
|
80373
|
+
const minutes = Math.floor(seconds / 60);
|
|
80374
|
+
const hours = Math.floor(minutes / 60);
|
|
80375
|
+
if (hours > 0) {
|
|
80376
|
+
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
80377
|
+
}
|
|
80378
|
+
if (minutes > 0) {
|
|
80379
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
80380
|
+
}
|
|
80381
|
+
return `${seconds}s`;
|
|
80382
|
+
}
|
|
80383
|
+
function getErrorText(error95) {
|
|
80384
|
+
if (!error95)
|
|
80385
|
+
return "";
|
|
80386
|
+
if (typeof error95 === "string")
|
|
80387
|
+
return error95;
|
|
80388
|
+
if (error95 instanceof Error) {
|
|
80389
|
+
return `${error95.name}: ${error95.message}`;
|
|
80390
|
+
}
|
|
80391
|
+
if (typeof error95 === "object" && error95 !== null) {
|
|
80392
|
+
if ("message" in error95 && typeof error95.message === "string") {
|
|
80393
|
+
return error95.message;
|
|
80394
|
+
}
|
|
80395
|
+
if ("name" in error95 && typeof error95.name === "string") {
|
|
80396
|
+
return error95.name;
|
|
80397
|
+
}
|
|
80398
|
+
}
|
|
80399
|
+
return "";
|
|
80400
|
+
}
|
|
80401
|
+
function isAbortedSessionError(error95) {
|
|
80402
|
+
const message = getErrorText(error95);
|
|
80403
|
+
return message.toLowerCase().includes("aborted");
|
|
80404
|
+
}
|
|
80405
|
+
function isRecord5(value) {
|
|
80406
|
+
return typeof value === "object" && value !== null;
|
|
80407
|
+
}
|
|
80408
|
+
function getSessionErrorMessage(properties) {
|
|
80409
|
+
const errorRaw = properties.error;
|
|
80410
|
+
if (!isRecord5(errorRaw))
|
|
80411
|
+
return;
|
|
80412
|
+
const dataRaw = errorRaw.data;
|
|
80413
|
+
if (isRecord5(dataRaw)) {
|
|
80414
|
+
const message2 = dataRaw.message;
|
|
80415
|
+
if (typeof message2 === "string")
|
|
80416
|
+
return message2;
|
|
80417
|
+
}
|
|
80418
|
+
const message = errorRaw.message;
|
|
80419
|
+
return typeof message === "string" ? message : undefined;
|
|
80420
|
+
}
|
|
80421
|
+
|
|
79177
80422
|
// src/features/background-agent/loop-detector.ts
|
|
79178
80423
|
function resolveCircuitBreakerSettings(config4) {
|
|
79179
80424
|
return {
|
|
@@ -79238,6 +80483,145 @@ function detectRepetitiveToolUse(window) {
|
|
|
79238
80483
|
};
|
|
79239
80484
|
}
|
|
79240
80485
|
|
|
80486
|
+
// src/features/background-agent/message-dir.ts
|
|
80487
|
+
import { existsSync as existsSync57, readdirSync as readdirSync19, readFileSync as readFileSync39 } from "fs";
|
|
80488
|
+
import { join as join72 } from "path";
|
|
80489
|
+
var MESSAGE_DIR_CACHE_CAP = 1000;
|
|
80490
|
+
var messageDirCache = new Map;
|
|
80491
|
+
function computeMessageDir(sessionID) {
|
|
80492
|
+
if (!existsSync57(MESSAGE_STORAGE))
|
|
80493
|
+
return null;
|
|
80494
|
+
const directPath = join72(MESSAGE_STORAGE, sessionID);
|
|
80495
|
+
if (existsSync57(directPath))
|
|
80496
|
+
return directPath;
|
|
80497
|
+
for (const dir of readdirSync19(MESSAGE_STORAGE)) {
|
|
80498
|
+
const sessionPath = join72(MESSAGE_STORAGE, dir, sessionID);
|
|
80499
|
+
if (existsSync57(sessionPath))
|
|
80500
|
+
return sessionPath;
|
|
80501
|
+
}
|
|
80502
|
+
return null;
|
|
80503
|
+
}
|
|
80504
|
+
function getMessageDir2(sessionID) {
|
|
80505
|
+
const cached3 = messageDirCache.get(sessionID);
|
|
80506
|
+
if (cached3 !== undefined) {
|
|
80507
|
+
messageDirCache.delete(sessionID);
|
|
80508
|
+
messageDirCache.set(sessionID, cached3);
|
|
80509
|
+
return cached3;
|
|
80510
|
+
}
|
|
80511
|
+
const result = computeMessageDir(sessionID);
|
|
80512
|
+
if (messageDirCache.size >= MESSAGE_DIR_CACHE_CAP) {
|
|
80513
|
+
const oldest = messageDirCache.keys().next().value;
|
|
80514
|
+
if (oldest !== undefined)
|
|
80515
|
+
messageDirCache.delete(oldest);
|
|
80516
|
+
}
|
|
80517
|
+
messageDirCache.set(sessionID, result);
|
|
80518
|
+
return result;
|
|
80519
|
+
}
|
|
80520
|
+
function isCompactionAgent(agent) {
|
|
80521
|
+
return agent?.trim().toLowerCase() === "compaction";
|
|
80522
|
+
}
|
|
80523
|
+
function hasFullAgentAndModel(message) {
|
|
80524
|
+
return !!message.agent && !isCompactionAgent(message.agent) && !!message.model?.providerID && !!message.model?.modelID;
|
|
80525
|
+
}
|
|
80526
|
+
function hasPartialAgentOrModel(message) {
|
|
80527
|
+
const hasAgent = !!message.agent && !isCompactionAgent(message.agent);
|
|
80528
|
+
const hasModel = !!message.model?.providerID && !!message.model?.modelID;
|
|
80529
|
+
return hasAgent || hasModel;
|
|
80530
|
+
}
|
|
80531
|
+
function findNearestMessageExcludingCompaction(messageDir) {
|
|
80532
|
+
try {
|
|
80533
|
+
const files = readdirSync19(messageDir).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
80534
|
+
const parsedByFile = new Map;
|
|
80535
|
+
for (const file3 of files) {
|
|
80536
|
+
try {
|
|
80537
|
+
const content = readFileSync39(join72(messageDir, file3), "utf-8");
|
|
80538
|
+
const parsed = JSON.parse(content);
|
|
80539
|
+
parsedByFile.set(file3, parsed);
|
|
80540
|
+
if (hasFullAgentAndModel(parsed)) {
|
|
80541
|
+
return parsed;
|
|
80542
|
+
}
|
|
80543
|
+
} catch {}
|
|
80544
|
+
}
|
|
80545
|
+
for (const parsed of parsedByFile.values()) {
|
|
80546
|
+
if (hasPartialAgentOrModel(parsed)) {
|
|
80547
|
+
return parsed;
|
|
80548
|
+
}
|
|
80549
|
+
}
|
|
80550
|
+
return null;
|
|
80551
|
+
} catch {
|
|
80552
|
+
return null;
|
|
80553
|
+
}
|
|
80554
|
+
}
|
|
80555
|
+
|
|
80556
|
+
// src/features/background-agent/notification-builder.ts
|
|
80557
|
+
function buildCompletionNotification(task, allComplete, completedTasks, remainingCount, duration5, errorInfo) {
|
|
80558
|
+
if (allComplete) {
|
|
80559
|
+
const completedTasksText = completedTasks.map((t) => `- \`${t.id}\`: ${t.description}`).join(`
|
|
80560
|
+
`);
|
|
80561
|
+
return `<system-reminder>
|
|
80562
|
+
[ALL BACKGROUND TASKS COMPLETE]
|
|
80563
|
+
|
|
80564
|
+
**Completed:**
|
|
80565
|
+
${completedTasksText || `- \`${task.id}\`: ${task.description}`}
|
|
80566
|
+
|
|
80567
|
+
Use \`background_output(task_id="<id>")\` to retrieve each result.
|
|
80568
|
+
</system-reminder>`;
|
|
80569
|
+
}
|
|
80570
|
+
const statusText = task.status === "completed" ? "COMPLETED" : task.status === "interrupt" ? "INTERRUPTED" : "CANCELLED";
|
|
80571
|
+
return `<system-reminder>
|
|
80572
|
+
[BACKGROUND TASK ${statusText}]
|
|
80573
|
+
**ID:** \`${task.id}\`
|
|
80574
|
+
**Description:** ${task.description}
|
|
80575
|
+
**Duration:** ${duration5}${errorInfo}
|
|
80576
|
+
|
|
80577
|
+
**${remainingCount} task${remainingCount === 1 ? "" : "s"} still in progress.** You WILL be notified when ALL complete.
|
|
80578
|
+
Do NOT poll - continue productive work.
|
|
80579
|
+
|
|
80580
|
+
Use \`background_output(task_id="${task.id}")\` to retrieve this result when ready.
|
|
80581
|
+
</system-reminder>`;
|
|
80582
|
+
}
|
|
80583
|
+
async function resolveAgentAndModel(client2, task, enableParentSessionNotifications) {
|
|
80584
|
+
let agent = task.parentAgent;
|
|
80585
|
+
let model;
|
|
80586
|
+
if (!enableParentSessionNotifications) {
|
|
80587
|
+
return { agent, model };
|
|
80588
|
+
}
|
|
80589
|
+
try {
|
|
80590
|
+
const messagesResp = await client2.session.messages({
|
|
80591
|
+
path: { id: task.parentSessionID }
|
|
80592
|
+
});
|
|
80593
|
+
const messages = normalizeSDKResponse(messagesResp, []);
|
|
80594
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
80595
|
+
const info = messages[i].info;
|
|
80596
|
+
if (isCompactionAgent(info?.agent)) {
|
|
80597
|
+
continue;
|
|
80598
|
+
}
|
|
80599
|
+
if (info?.agent || info?.model || info?.modelID && info?.providerID) {
|
|
80600
|
+
agent = info.agent ?? task.parentAgent;
|
|
80601
|
+
model = info.model ?? (info.providerID && info.modelID ? {
|
|
80602
|
+
providerID: info.providerID,
|
|
80603
|
+
modelID: info.modelID
|
|
80604
|
+
} : undefined);
|
|
80605
|
+
break;
|
|
80606
|
+
}
|
|
80607
|
+
}
|
|
80608
|
+
} catch (error95) {
|
|
80609
|
+
if (isAbortedSessionError(error95)) {
|
|
80610
|
+
log("[background-agent] Parent session aborted while loading messages; using messageDir fallback:", {
|
|
80611
|
+
taskParentSessionID: task.parentSessionID
|
|
80612
|
+
});
|
|
80613
|
+
}
|
|
80614
|
+
const messageDir = getMessageDir2(task.parentSessionID);
|
|
80615
|
+
const currentMessage = messageDir ? findNearestMessageExcludingCompaction(messageDir) : null;
|
|
80616
|
+
agent = currentMessage?.agent ?? task.parentAgent;
|
|
80617
|
+
model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? {
|
|
80618
|
+
providerID: currentMessage.model.providerID,
|
|
80619
|
+
modelID: currentMessage.model.modelID
|
|
80620
|
+
} : undefined;
|
|
80621
|
+
}
|
|
80622
|
+
return { agent, model };
|
|
80623
|
+
}
|
|
80624
|
+
|
|
79241
80625
|
// src/features/background-agent/task-history.ts
|
|
79242
80626
|
var MAX_ENTRIES_PER_PARENT = 100;
|
|
79243
80627
|
|
|
@@ -79544,9 +80928,7 @@ class BackgroundManager {
|
|
|
79544
80928
|
this.concurrencyManager.release(existingTask.concurrencyKey);
|
|
79545
80929
|
existingTask.concurrencyKey = undefined;
|
|
79546
80930
|
}
|
|
79547
|
-
this.
|
|
79548
|
-
path: { id: sessionID }
|
|
79549
|
-
}).catch(() => {});
|
|
80931
|
+
this.abortSessionQuietly(sessionID, "error-recovery");
|
|
79550
80932
|
this.markForNotification(existingTask);
|
|
79551
80933
|
this.cleanupPendingByParent(existingTask);
|
|
79552
80934
|
this.enqueueNotificationForParent(existingTask.parentSessionID, () => this.notifyParentSession(existingTask)).catch((err) => {
|
|
@@ -79750,9 +81132,7 @@ class BackgroundManager {
|
|
|
79750
81132
|
existingTask.concurrencyKey = undefined;
|
|
79751
81133
|
}
|
|
79752
81134
|
if (existingTask.sessionID) {
|
|
79753
|
-
this.
|
|
79754
|
-
path: { id: existingTask.sessionID }
|
|
79755
|
-
}).catch(() => {});
|
|
81135
|
+
this.abortSessionQuietly(existingTask.sessionID, "resume-error");
|
|
79756
81136
|
}
|
|
79757
81137
|
this.markForNotification(existingTask);
|
|
79758
81138
|
this.cleanupPendingByParent(existingTask);
|
|
@@ -79907,7 +81287,7 @@ class BackgroundManager {
|
|
|
79907
81287
|
const task = this.findBySession(sessionID);
|
|
79908
81288
|
if (task?.status !== "running")
|
|
79909
81289
|
return;
|
|
79910
|
-
const errorMessage = props ?
|
|
81290
|
+
const errorMessage = props ? getSessionErrorMessage(props) : undefined;
|
|
79911
81291
|
task.status = "error";
|
|
79912
81292
|
task.error = errorMessage ?? "Session error";
|
|
79913
81293
|
task.completedAt = new Date;
|
|
@@ -80047,6 +81427,14 @@ class BackgroundManager {
|
|
|
80047
81427
|
}
|
|
80048
81428
|
}
|
|
80049
81429
|
}
|
|
81430
|
+
abortSessionQuietly(sessionID, context) {
|
|
81431
|
+
this.client.session.abort({ path: { id: sessionID } }).catch((err) => {
|
|
81432
|
+
log(`[background-agent] Session abort failed${context ? ` (${context})` : ""}:`, {
|
|
81433
|
+
sessionID,
|
|
81434
|
+
error: err
|
|
81435
|
+
});
|
|
81436
|
+
});
|
|
81437
|
+
}
|
|
80050
81438
|
async cancelTask(taskId, options) {
|
|
80051
81439
|
const task = this.tasks.get(taskId);
|
|
80052
81440
|
if (!task || task.status !== "running" && task.status !== "pending") {
|
|
@@ -80091,9 +81479,7 @@ class BackgroundManager {
|
|
|
80091
81479
|
}
|
|
80092
81480
|
this.cleanupPendingByParent(task);
|
|
80093
81481
|
if (abortSession && task.sessionID) {
|
|
80094
|
-
this.
|
|
80095
|
-
path: { id: task.sessionID }
|
|
80096
|
-
}).catch(() => {});
|
|
81482
|
+
this.abortSessionQuietly(task.sessionID, "cancel");
|
|
80097
81483
|
}
|
|
80098
81484
|
if (options?.skipNotification) {
|
|
80099
81485
|
const toastManager = getTaskToastManager();
|
|
@@ -80208,9 +81594,7 @@ class BackgroundManager {
|
|
|
80208
81594
|
this.idleDeferralTimers.delete(task.id);
|
|
80209
81595
|
}
|
|
80210
81596
|
if (task.sessionID) {
|
|
80211
|
-
this.
|
|
80212
|
-
path: { id: task.sessionID }
|
|
80213
|
-
}).catch(() => {});
|
|
81597
|
+
this.abortSessionQuietly(task.sessionID, "complete");
|
|
80214
81598
|
}
|
|
80215
81599
|
try {
|
|
80216
81600
|
await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task));
|
|
@@ -80221,7 +81605,7 @@ class BackgroundManager {
|
|
|
80221
81605
|
return true;
|
|
80222
81606
|
}
|
|
80223
81607
|
async notifyParentSession(task) {
|
|
80224
|
-
const duration5 =
|
|
81608
|
+
const duration5 = formatDuration2(task.startedAt ?? new Date, task.completedAt);
|
|
80225
81609
|
log("[background-agent] notifyParentSession called for task:", task.id);
|
|
80226
81610
|
const toastManager = getTaskToastManager();
|
|
80227
81611
|
if (toastManager) {
|
|
@@ -80245,68 +81629,16 @@ class BackgroundManager {
|
|
|
80245
81629
|
allComplete = true;
|
|
80246
81630
|
}
|
|
80247
81631
|
const completedTasks = allComplete ? Array.from(this.tasks.values()).filter((t) => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending") : [];
|
|
80248
|
-
const statusText = task.status === "completed" ? "COMPLETED" : task.status === "interrupt" ? "INTERRUPTED" : "CANCELLED";
|
|
80249
81632
|
const errorInfo = task.error ? `
|
|
80250
81633
|
**Error:** ${task.error}` : "";
|
|
80251
|
-
|
|
80252
|
-
|
|
80253
|
-
|
|
80254
|
-
|
|
80255
|
-
|
|
80256
|
-
|
|
80257
|
-
|
|
80258
|
-
**Completed:**
|
|
80259
|
-
${completedTasksText || `- \`${task.id}\`: ${task.description}`}
|
|
80260
|
-
|
|
80261
|
-
Use \`background_output(task_id="<id>")\` to retrieve each result.
|
|
80262
|
-
</system-reminder>`;
|
|
80263
|
-
} else {
|
|
80264
|
-
notification = `<system-reminder>
|
|
80265
|
-
[BACKGROUND TASK ${statusText}]
|
|
80266
|
-
**ID:** \`${task.id}\`
|
|
80267
|
-
**Description:** ${task.description}
|
|
80268
|
-
**Duration:** ${duration5}${errorInfo}
|
|
80269
|
-
|
|
80270
|
-
**${remainingCount} task${remainingCount === 1 ? "" : "s"} still in progress.** You WILL be notified when ALL complete.
|
|
80271
|
-
Do NOT poll - continue productive work.
|
|
80272
|
-
|
|
80273
|
-
Use \`background_output(task_id="${task.id}")\` to retrieve this result when ready.
|
|
80274
|
-
</system-reminder>`;
|
|
80275
|
-
}
|
|
80276
|
-
let agent = task.parentAgent;
|
|
80277
|
-
let model;
|
|
81634
|
+
const notification = buildCompletionNotification(task, allComplete, completedTasks, remainingCount, duration5, errorInfo);
|
|
81635
|
+
const { agent, model } = await resolveAgentAndModel(this.client, task, this.enableParentSessionNotifications);
|
|
81636
|
+
log("[background-agent] notifyParentSession context:", {
|
|
81637
|
+
taskId: task.id,
|
|
81638
|
+
resolvedAgent: agent,
|
|
81639
|
+
resolvedModel: model
|
|
81640
|
+
});
|
|
80278
81641
|
if (this.enableParentSessionNotifications) {
|
|
80279
|
-
try {
|
|
80280
|
-
const messagesResp = await this.client.session.messages({ path: { id: task.parentSessionID } });
|
|
80281
|
-
const messages = normalizeSDKResponse(messagesResp, []);
|
|
80282
|
-
for (let i = messages.length - 1;i >= 0; i--) {
|
|
80283
|
-
const info = messages[i].info;
|
|
80284
|
-
if (isCompactionAgent(info?.agent)) {
|
|
80285
|
-
continue;
|
|
80286
|
-
}
|
|
80287
|
-
if (info?.agent || info?.model || info?.modelID && info?.providerID) {
|
|
80288
|
-
agent = info.agent ?? task.parentAgent;
|
|
80289
|
-
model = info.model ?? (info.providerID && info.modelID ? { providerID: info.providerID, modelID: info.modelID } : undefined);
|
|
80290
|
-
break;
|
|
80291
|
-
}
|
|
80292
|
-
}
|
|
80293
|
-
} catch (error95) {
|
|
80294
|
-
if (this.isAbortedSessionError(error95)) {
|
|
80295
|
-
log("[background-agent] Parent session aborted while loading messages; using messageDir fallback:", {
|
|
80296
|
-
taskId: task.id,
|
|
80297
|
-
parentSessionID: task.parentSessionID
|
|
80298
|
-
});
|
|
80299
|
-
}
|
|
80300
|
-
const messageDir = getMessageDir2(task.parentSessionID);
|
|
80301
|
-
const currentMessage = messageDir ? findNearestMessageExcludingCompaction(messageDir) : null;
|
|
80302
|
-
agent = currentMessage?.agent ?? task.parentAgent;
|
|
80303
|
-
model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
|
|
80304
|
-
}
|
|
80305
|
-
log("[background-agent] notifyParentSession context:", {
|
|
80306
|
-
taskId: task.id,
|
|
80307
|
-
resolvedAgent: agent,
|
|
80308
|
-
resolvedModel: model
|
|
80309
|
-
});
|
|
80310
81642
|
try {
|
|
80311
81643
|
await this.client.session.promptAsync({
|
|
80312
81644
|
path: { id: task.parentSessionID },
|
|
@@ -80324,7 +81656,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80324
81656
|
noReply: !allComplete
|
|
80325
81657
|
});
|
|
80326
81658
|
} catch (error95) {
|
|
80327
|
-
if (
|
|
81659
|
+
if (isAbortedSessionError(error95)) {
|
|
80328
81660
|
log("[background-agent] Parent session aborted while sending notification; continuing cleanup:", {
|
|
80329
81661
|
taskId: task.id,
|
|
80330
81662
|
parentSessionID: task.parentSessionID
|
|
@@ -80359,56 +81691,6 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80359
81691
|
}
|
|
80360
81692
|
}
|
|
80361
81693
|
}
|
|
80362
|
-
formatDuration(start, end) {
|
|
80363
|
-
const duration5 = (end ?? new Date).getTime() - start.getTime();
|
|
80364
|
-
const seconds = Math.floor(duration5 / 1000);
|
|
80365
|
-
const minutes = Math.floor(seconds / 60);
|
|
80366
|
-
const hours = Math.floor(minutes / 60);
|
|
80367
|
-
if (hours > 0) {
|
|
80368
|
-
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
80369
|
-
} else if (minutes > 0) {
|
|
80370
|
-
return `${minutes}m ${seconds % 60}s`;
|
|
80371
|
-
}
|
|
80372
|
-
return `${seconds}s`;
|
|
80373
|
-
}
|
|
80374
|
-
isAbortedSessionError(error95) {
|
|
80375
|
-
const message = this.getErrorText(error95);
|
|
80376
|
-
return message.toLowerCase().includes("aborted");
|
|
80377
|
-
}
|
|
80378
|
-
getErrorText(error95) {
|
|
80379
|
-
if (!error95)
|
|
80380
|
-
return "";
|
|
80381
|
-
if (typeof error95 === "string")
|
|
80382
|
-
return error95;
|
|
80383
|
-
if (error95 instanceof Error) {
|
|
80384
|
-
return `${error95.name}: ${error95.message}`;
|
|
80385
|
-
}
|
|
80386
|
-
if (typeof error95 === "object" && error95 !== null) {
|
|
80387
|
-
if ("message" in error95 && typeof error95.message === "string") {
|
|
80388
|
-
return error95.message;
|
|
80389
|
-
}
|
|
80390
|
-
if ("name" in error95 && typeof error95.name === "string") {
|
|
80391
|
-
return error95.name;
|
|
80392
|
-
}
|
|
80393
|
-
}
|
|
80394
|
-
return "";
|
|
80395
|
-
}
|
|
80396
|
-
isRecord(value) {
|
|
80397
|
-
return typeof value === "object" && value !== null;
|
|
80398
|
-
}
|
|
80399
|
-
getSessionErrorMessage(properties) {
|
|
80400
|
-
const errorRaw = properties.error;
|
|
80401
|
-
if (!this.isRecord(errorRaw))
|
|
80402
|
-
return;
|
|
80403
|
-
const dataRaw = errorRaw.data;
|
|
80404
|
-
if (this.isRecord(dataRaw)) {
|
|
80405
|
-
const message2 = dataRaw.message;
|
|
80406
|
-
if (typeof message2 === "string")
|
|
80407
|
-
return message2;
|
|
80408
|
-
}
|
|
80409
|
-
const message = errorRaw.message;
|
|
80410
|
-
return typeof message === "string" ? message : undefined;
|
|
80411
|
-
}
|
|
80412
81694
|
hasRunningTasks() {
|
|
80413
81695
|
for (const task of this.tasks.values()) {
|
|
80414
81696
|
if (task.status === "running")
|
|
@@ -80505,7 +81787,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80505
81787
|
this.concurrencyManager.release(task.concurrencyKey);
|
|
80506
81788
|
task.concurrencyKey = undefined;
|
|
80507
81789
|
}
|
|
80508
|
-
this.
|
|
81790
|
+
this.abortSessionQuietly(sessionID, "stale-no-progress");
|
|
80509
81791
|
log(`[background-agent] Task ${task.id} interrupted: no progress since start`);
|
|
80510
81792
|
try {
|
|
80511
81793
|
await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task));
|
|
@@ -80531,7 +81813,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80531
81813
|
this.concurrencyManager.release(task.concurrencyKey);
|
|
80532
81814
|
task.concurrencyKey = undefined;
|
|
80533
81815
|
}
|
|
80534
|
-
this.
|
|
81816
|
+
this.abortSessionQuietly(sessionID, "stale-timeout");
|
|
80535
81817
|
log(`[background-agent] Task ${task.id} interrupted: stale timeout`);
|
|
80536
81818
|
try {
|
|
80537
81819
|
await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task));
|
|
@@ -80601,9 +81883,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80601
81883
|
this.stopPolling();
|
|
80602
81884
|
for (const task of this.tasks.values()) {
|
|
80603
81885
|
if (task.status === "running" && task.sessionID) {
|
|
80604
|
-
this.
|
|
80605
|
-
path: { id: task.sessionID }
|
|
80606
|
-
}).catch(() => {});
|
|
81886
|
+
this.abortSessionQuietly(task.sessionID, "shutdown");
|
|
80607
81887
|
}
|
|
80608
81888
|
}
|
|
80609
81889
|
if (this.onShutdown) {
|
|
@@ -80642,13 +81922,17 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80642
81922
|
return operation();
|
|
80643
81923
|
}
|
|
80644
81924
|
const previous = this.notificationQueueByParent.get(parentSessionID) ?? Promise.resolve();
|
|
80645
|
-
const current = previous.catch(() => {
|
|
81925
|
+
const current = previous.catch((err) => {
|
|
81926
|
+
log("[background-agent] Previous notification failed, continuing queue:", err);
|
|
81927
|
+
}).then(operation);
|
|
80646
81928
|
this.notificationQueueByParent.set(parentSessionID, current);
|
|
80647
81929
|
current.finally(() => {
|
|
80648
81930
|
if (this.notificationQueueByParent.get(parentSessionID) === current) {
|
|
80649
81931
|
this.notificationQueueByParent.delete(parentSessionID);
|
|
80650
81932
|
}
|
|
80651
|
-
}).catch(() => {
|
|
81933
|
+
}).catch((err) => {
|
|
81934
|
+
log("[background-agent] Notification queue cleanup failed:", err);
|
|
81935
|
+
});
|
|
80652
81936
|
return current;
|
|
80653
81937
|
}
|
|
80654
81938
|
}
|
|
@@ -80663,72 +81947,6 @@ function registerProcessSignal(signal, handler, exitAfter) {
|
|
|
80663
81947
|
process.on(signal, listener);
|
|
80664
81948
|
return listener;
|
|
80665
81949
|
}
|
|
80666
|
-
function computeMessageDir(sessionID) {
|
|
80667
|
-
if (!existsSync57(MESSAGE_STORAGE))
|
|
80668
|
-
return null;
|
|
80669
|
-
const directPath = join72(MESSAGE_STORAGE, sessionID);
|
|
80670
|
-
if (existsSync57(directPath))
|
|
80671
|
-
return directPath;
|
|
80672
|
-
for (const dir of readdirSync19(MESSAGE_STORAGE)) {
|
|
80673
|
-
const sessionPath = join72(MESSAGE_STORAGE, dir, sessionID);
|
|
80674
|
-
if (existsSync57(sessionPath))
|
|
80675
|
-
return sessionPath;
|
|
80676
|
-
}
|
|
80677
|
-
return null;
|
|
80678
|
-
}
|
|
80679
|
-
var MESSAGE_DIR_CACHE_CAP = 1000;
|
|
80680
|
-
var messageDirCache = new Map;
|
|
80681
|
-
function getMessageDir2(sessionID) {
|
|
80682
|
-
const cached3 = messageDirCache.get(sessionID);
|
|
80683
|
-
if (cached3 !== undefined) {
|
|
80684
|
-
messageDirCache.delete(sessionID);
|
|
80685
|
-
messageDirCache.set(sessionID, cached3);
|
|
80686
|
-
return cached3;
|
|
80687
|
-
}
|
|
80688
|
-
const result = computeMessageDir(sessionID);
|
|
80689
|
-
if (messageDirCache.size >= MESSAGE_DIR_CACHE_CAP) {
|
|
80690
|
-
const oldest = messageDirCache.keys().next().value;
|
|
80691
|
-
if (oldest !== undefined)
|
|
80692
|
-
messageDirCache.delete(oldest);
|
|
80693
|
-
}
|
|
80694
|
-
messageDirCache.set(sessionID, result);
|
|
80695
|
-
return result;
|
|
80696
|
-
}
|
|
80697
|
-
function isCompactionAgent(agent) {
|
|
80698
|
-
return agent?.trim().toLowerCase() === "compaction";
|
|
80699
|
-
}
|
|
80700
|
-
function hasFullAgentAndModel(message) {
|
|
80701
|
-
return !!message.agent && !isCompactionAgent(message.agent) && !!message.model?.providerID && !!message.model?.modelID;
|
|
80702
|
-
}
|
|
80703
|
-
function hasPartialAgentOrModel(message) {
|
|
80704
|
-
const hasAgent = !!message.agent && !isCompactionAgent(message.agent);
|
|
80705
|
-
const hasModel = !!message.model?.providerID && !!message.model?.modelID;
|
|
80706
|
-
return hasAgent || hasModel;
|
|
80707
|
-
}
|
|
80708
|
-
function findNearestMessageExcludingCompaction(messageDir) {
|
|
80709
|
-
try {
|
|
80710
|
-
const files = readdirSync19(messageDir).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
80711
|
-
const parsedByFile = new Map;
|
|
80712
|
-
for (const file3 of files) {
|
|
80713
|
-
try {
|
|
80714
|
-
const content = readFileSync39(join72(messageDir, file3), "utf-8");
|
|
80715
|
-
const parsed = JSON.parse(content);
|
|
80716
|
-
parsedByFile.set(file3, parsed);
|
|
80717
|
-
if (hasFullAgentAndModel(parsed)) {
|
|
80718
|
-
return parsed;
|
|
80719
|
-
}
|
|
80720
|
-
} catch {}
|
|
80721
|
-
}
|
|
80722
|
-
for (const parsed of parsedByFile.values()) {
|
|
80723
|
-
if (hasPartialAgentOrModel(parsed)) {
|
|
80724
|
-
return parsed;
|
|
80725
|
-
}
|
|
80726
|
-
}
|
|
80727
|
-
return null;
|
|
80728
|
-
} catch {
|
|
80729
|
-
return null;
|
|
80730
|
-
}
|
|
80731
|
-
}
|
|
80732
81950
|
// src/features/background-agent/state.ts
|
|
80733
81951
|
class TaskStateManager {
|
|
80734
81952
|
tasks = new Map;
|
|
@@ -80901,6 +82119,7 @@ class TaskStateManager {
|
|
|
80901
82119
|
}
|
|
80902
82120
|
}
|
|
80903
82121
|
// src/features/skill-mcp-manager/cleanup.ts
|
|
82122
|
+
init_logger();
|
|
80904
82123
|
async function closeManagedClient(managed) {
|
|
80905
82124
|
try {
|
|
80906
82125
|
await managed.client.close();
|
|
@@ -80921,7 +82140,9 @@ function registerProcessCleanup(state3) {
|
|
|
80921
82140
|
state3.pendingConnections.clear();
|
|
80922
82141
|
};
|
|
80923
82142
|
const register = (signal) => {
|
|
80924
|
-
const listener = () => void cleanup().catch(() => {
|
|
82143
|
+
const listener = () => void cleanup().catch((err) => {
|
|
82144
|
+
log("[skill-mcp] Signal handler cleanup failed:", err);
|
|
82145
|
+
});
|
|
80925
82146
|
state3.cleanupHandlers.push({ signal, listener });
|
|
80926
82147
|
process.on(signal, listener);
|
|
80927
82148
|
};
|
|
@@ -80944,7 +82165,9 @@ function startCleanupTimer(state3) {
|
|
|
80944
82165
|
if (state3.cleanupInterval)
|
|
80945
82166
|
return;
|
|
80946
82167
|
state3.cleanupInterval = setInterval(() => {
|
|
80947
|
-
cleanupIdleClients(state3).catch(() => {
|
|
82168
|
+
cleanupIdleClients(state3).catch((err) => {
|
|
82169
|
+
log("[skill-mcp] Idle client cleanup failed:", err);
|
|
82170
|
+
});
|
|
80948
82171
|
}, 60000);
|
|
80949
82172
|
state3.cleanupInterval.unref();
|
|
80950
82173
|
}
|
|
@@ -84601,13 +85824,13 @@ class StreamableHTTPClientTransport {
|
|
|
84601
85824
|
this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));
|
|
84602
85825
|
return;
|
|
84603
85826
|
}
|
|
84604
|
-
const
|
|
85827
|
+
const delay6 = this._getNextReconnectionDelay(attemptCount);
|
|
84605
85828
|
this._reconnectionTimeout = setTimeout(() => {
|
|
84606
85829
|
this._startOrAuthSse(options).catch((error95) => {
|
|
84607
85830
|
this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error95 instanceof Error ? error95.message : String(error95)}`));
|
|
84608
85831
|
this._scheduleReconnection(options, attemptCount + 1);
|
|
84609
85832
|
});
|
|
84610
|
-
},
|
|
85833
|
+
}, delay6);
|
|
84611
85834
|
}
|
|
84612
85835
|
_handleSseStream(stream, options, isReconnectable) {
|
|
84613
85836
|
if (!stream) {
|
|
@@ -84918,7 +86141,7 @@ async function getOrRegisterClient(options) {
|
|
|
84918
86141
|
}
|
|
84919
86142
|
}
|
|
84920
86143
|
function parseRegistrationResponse(data) {
|
|
84921
|
-
if (!
|
|
86144
|
+
if (!isRecord6(data))
|
|
84922
86145
|
return null;
|
|
84923
86146
|
const clientId = data.client_id;
|
|
84924
86147
|
if (typeof clientId !== "string" || clientId.length === 0)
|
|
@@ -84929,7 +86152,7 @@ function parseRegistrationResponse(data) {
|
|
|
84929
86152
|
}
|
|
84930
86153
|
return { clientId };
|
|
84931
86154
|
}
|
|
84932
|
-
function
|
|
86155
|
+
function isRecord6(value) {
|
|
84933
86156
|
return typeof value === "object" && value !== null;
|
|
84934
86157
|
}
|
|
84935
86158
|
|
|
@@ -86958,9 +88181,9 @@ function buildOracleSection(agents) {
|
|
|
86958
88181
|
const useWhen = oracleAgent.metadata.useWhen || [];
|
|
86959
88182
|
const avoidWhen = oracleAgent.metadata.avoidWhen || [];
|
|
86960
88183
|
return `<Oracle_Usage>
|
|
86961
|
-
## Oracle \u2014
|
|
88184
|
+
## Oracle \u2014 Plan Builder
|
|
86962
88185
|
|
|
86963
|
-
Oracle is
|
|
88186
|
+
Oracle is the Plan Builder \u2014 creates detailed, structured work plans from user requests. Use oracle when you need comprehensive planning before implementation.
|
|
86964
88187
|
|
|
86965
88188
|
### WHEN to Consult:
|
|
86966
88189
|
|
|
@@ -86975,7 +88198,7 @@ ${avoidWhen.map((w) => `- ${w}`).join(`
|
|
|
86975
88198
|
`)}
|
|
86976
88199
|
|
|
86977
88200
|
### Usage Pattern:
|
|
86978
|
-
|
|
88201
|
+
Oracle creates plans, not code. Invoke oracle to decompose complex requests into structured work plans with clear steps, then execute the plan yourself or delegate to workers.
|
|
86979
88202
|
|
|
86980
88203
|
**Exception**: This is the ONLY case where you announce before acting. For all other work, start immediately without status updates.
|
|
86981
88204
|
</Oracle_Usage>`;
|
|
@@ -87011,9 +88234,9 @@ ${patterns.join(`
|
|
|
87011
88234
|
}
|
|
87012
88235
|
|
|
87013
88236
|
// src/agents/architect/default.ts
|
|
87014
|
-
var
|
|
88237
|
+
var ARCHITECT_SYSTEM_PROMPT = `
|
|
87015
88238
|
<identity>
|
|
87016
|
-
You are
|
|
88239
|
+
You are Architect - the Master Orchestrator from Matrixx.
|
|
87017
88240
|
|
|
87018
88241
|
In Greek mythology, Atlas holds up the celestial heavens. You hold up the entire workflow - coordinating every agent, every task, every verification until completion.
|
|
87019
88242
|
|
|
@@ -87410,14 +88633,14 @@ You are the QA gate. Subagents lie. Verify EVERYTHING.
|
|
|
87410
88633
|
- **Use \`session_id="{session_id}"\` for retries, fixes, and follow-ups**
|
|
87411
88634
|
</critical_overrides>
|
|
87412
88635
|
`;
|
|
87413
|
-
function
|
|
87414
|
-
return
|
|
88636
|
+
function getDefaultArchitectPrompt() {
|
|
88637
|
+
return ARCHITECT_SYSTEM_PROMPT;
|
|
87415
88638
|
}
|
|
87416
88639
|
|
|
87417
88640
|
// src/agents/architect/gpt.ts
|
|
87418
|
-
var
|
|
88641
|
+
var ARCHITECT_GPT_SYSTEM_PROMPT = `
|
|
87419
88642
|
<identity>
|
|
87420
|
-
You are
|
|
88643
|
+
You are Architect - Master Orchestrator from Matrixx.
|
|
87421
88644
|
Role: Conductor, not musician. General, not soldier.
|
|
87422
88645
|
You DELEGATE, COORDINATE, and VERIFY. You NEVER write code yourself.
|
|
87423
88646
|
</identity>
|
|
@@ -87763,8 +88986,8 @@ You are the QA gate. Subagents lie. Verify EVERYTHING.
|
|
|
87763
88986
|
- Do NOT expand task scope; if you notice new work, call it out as optional
|
|
87764
88987
|
</user_updates_spec>
|
|
87765
88988
|
`;
|
|
87766
|
-
function
|
|
87767
|
-
return
|
|
88989
|
+
function getGptArchitectPrompt() {
|
|
88990
|
+
return ARCHITECT_GPT_SYSTEM_PROMPT;
|
|
87768
88991
|
}
|
|
87769
88992
|
|
|
87770
88993
|
// src/agents/architect/prompt-section-builder.ts
|
|
@@ -87884,19 +89107,19 @@ ${agentRows.join(`
|
|
|
87884
89107
|
|
|
87885
89108
|
// src/agents/architect/agent.ts
|
|
87886
89109
|
var MODE = "primary";
|
|
87887
|
-
function
|
|
89110
|
+
function getArchitectPromptSource(model) {
|
|
87888
89111
|
if (model && isGptModel(model)) {
|
|
87889
89112
|
return "gpt";
|
|
87890
89113
|
}
|
|
87891
89114
|
return "default";
|
|
87892
89115
|
}
|
|
87893
|
-
function
|
|
87894
|
-
const source =
|
|
89116
|
+
function getArchitectPrompt(model) {
|
|
89117
|
+
const source = getArchitectPromptSource(model);
|
|
87895
89118
|
switch (source) {
|
|
87896
89119
|
case "gpt":
|
|
87897
|
-
return
|
|
89120
|
+
return getGptArchitectPrompt();
|
|
87898
89121
|
default:
|
|
87899
|
-
return
|
|
89122
|
+
return getDefaultArchitectPrompt();
|
|
87900
89123
|
}
|
|
87901
89124
|
}
|
|
87902
89125
|
function buildDynamicOrchestratorPrompt(ctx) {
|
|
@@ -87914,16 +89137,16 @@ function buildDynamicOrchestratorPrompt(ctx) {
|
|
|
87914
89137
|
const decisionMatrix = buildDecisionMatrix(agents, userCategories);
|
|
87915
89138
|
const skillsSection = buildSkillsSection(skills);
|
|
87916
89139
|
const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, skills);
|
|
87917
|
-
const basePrompt =
|
|
89140
|
+
const basePrompt = getArchitectPrompt(model);
|
|
87918
89141
|
return basePrompt.replace("{CATEGORY_SECTION}", categorySection).replace("{AGENT_SECTION}", agentSection).replace("{DECISION_MATRIX}", decisionMatrix).replace("{SKILLS_SECTION}", skillsSection).replace("{{CATEGORY_SKILLS_DELEGATION_GUIDE}}", categorySkillsGuide);
|
|
87919
89142
|
}
|
|
87920
|
-
function
|
|
89143
|
+
function createArchitectAgent(ctx) {
|
|
87921
89144
|
const restrictions = createAgentToolRestrictions([
|
|
87922
89145
|
"task",
|
|
87923
89146
|
"delegate_agent"
|
|
87924
89147
|
]);
|
|
87925
89148
|
const baseConfig = {
|
|
87926
|
-
description: "Orchestrates work via task() to complete ALL tasks in a todo list until fully done. (
|
|
89149
|
+
description: "Orchestrates work via task() to complete ALL tasks in a todo list until fully done. (Architect - Matrixx)",
|
|
87927
89150
|
mode: MODE,
|
|
87928
89151
|
...ctx.model ? { model: ctx.model } : {},
|
|
87929
89152
|
temperature: 0.1,
|
|
@@ -87933,11 +89156,11 @@ function createAtlasAgent(ctx) {
|
|
|
87933
89156
|
};
|
|
87934
89157
|
return baseConfig;
|
|
87935
89158
|
}
|
|
87936
|
-
|
|
87937
|
-
var
|
|
89159
|
+
createArchitectAgent.mode = MODE;
|
|
89160
|
+
var architectPromptMetadata = {
|
|
87938
89161
|
category: "advisor",
|
|
87939
89162
|
cost: "EXPENSIVE",
|
|
87940
|
-
promptAlias: "
|
|
89163
|
+
promptAlias: "Architect",
|
|
87941
89164
|
triggers: [
|
|
87942
89165
|
{
|
|
87943
89166
|
domain: "Todo list orchestration",
|
|
@@ -88059,8 +89282,8 @@ function getFirstFallbackModel(requirement) {
|
|
|
88059
89282
|
};
|
|
88060
89283
|
}
|
|
88061
89284
|
|
|
88062
|
-
// src/agents/builtin-agents/
|
|
88063
|
-
function
|
|
89285
|
+
// src/agents/builtin-agents/architect-agent.ts
|
|
89286
|
+
function maybeCreateArchitectConfig(input) {
|
|
88064
89287
|
const {
|
|
88065
89288
|
disabledAgents,
|
|
88066
89289
|
agentOverrides,
|
|
@@ -88076,25 +89299,25 @@ function maybeCreateAtlasConfig(input) {
|
|
|
88076
89299
|
if (disabledAgents.includes("architect"))
|
|
88077
89300
|
return;
|
|
88078
89301
|
const orchestratorOverride = agentOverrides.architect;
|
|
88079
|
-
const
|
|
88080
|
-
const
|
|
89302
|
+
const architectRequirement = AGENT_MODEL_REQUIREMENTS.architect;
|
|
89303
|
+
const architectResolution = applyModelResolution({
|
|
88081
89304
|
uiSelectedModel: orchestratorOverride?.model ? undefined : uiSelectedModel,
|
|
88082
89305
|
userModel: orchestratorOverride?.model,
|
|
88083
|
-
requirement:
|
|
89306
|
+
requirement: architectRequirement,
|
|
88084
89307
|
availableModels,
|
|
88085
89308
|
systemDefaultModel
|
|
88086
89309
|
});
|
|
88087
|
-
if (!
|
|
89310
|
+
if (!architectResolution)
|
|
88088
89311
|
return;
|
|
88089
|
-
const { model:
|
|
88090
|
-
let orchestratorConfig =
|
|
88091
|
-
model:
|
|
89312
|
+
const { model: architectModel, variant: architectResolvedVariant } = architectResolution;
|
|
89313
|
+
let orchestratorConfig = createArchitectAgent({
|
|
89314
|
+
model: architectModel,
|
|
88092
89315
|
availableAgents,
|
|
88093
89316
|
availableSkills,
|
|
88094
89317
|
userCategories
|
|
88095
89318
|
});
|
|
88096
|
-
if (
|
|
88097
|
-
orchestratorConfig = { ...orchestratorConfig, variant:
|
|
89319
|
+
if (architectResolvedVariant) {
|
|
89320
|
+
orchestratorConfig = { ...orchestratorConfig, variant: architectResolvedVariant };
|
|
88098
89321
|
}
|
|
88099
89322
|
orchestratorConfig = applyOverrides(orchestratorConfig, orchestratorOverride, mergedCategories, directory);
|
|
88100
89323
|
return orchestratorConfig;
|
|
@@ -88223,6 +89446,8 @@ function collectPendingBuiltinAgents(input) {
|
|
|
88223
89446
|
const pendingAgentConfigs = new Map;
|
|
88224
89447
|
for (const [name, source] of Object.entries(agentSources)) {
|
|
88225
89448
|
const agentName = name;
|
|
89449
|
+
if (!source)
|
|
89450
|
+
continue;
|
|
88226
89451
|
if (agentName === "morpheus")
|
|
88227
89452
|
continue;
|
|
88228
89453
|
if (agentName === "keymaker")
|
|
@@ -88269,6 +89494,14 @@ function collectPendingBuiltinAgents(input) {
|
|
|
88269
89494
|
});
|
|
88270
89495
|
}
|
|
88271
89496
|
}
|
|
89497
|
+
const oracleMetadata = agentMetadata.oracle;
|
|
89498
|
+
if (oracleMetadata) {
|
|
89499
|
+
availableAgents.push({
|
|
89500
|
+
name: "oracle",
|
|
89501
|
+
description: "Plan Builder agent. Creates detailed, structured work plans from user requests. (Oracle - Matrixx)",
|
|
89502
|
+
metadata: oracleMetadata
|
|
89503
|
+
});
|
|
89504
|
+
}
|
|
88272
89505
|
return { pendingAgentConfigs, availableAgents };
|
|
88273
89506
|
}
|
|
88274
89507
|
|
|
@@ -89386,7 +90619,7 @@ function createMorpheusAgent(model, availableAgents, availableToolNames, availab
|
|
|
89386
90619
|
}
|
|
89387
90620
|
createMorpheusAgent.mode = MODE3;
|
|
89388
90621
|
|
|
89389
|
-
// src/agents/builtin-agents/
|
|
90622
|
+
// src/agents/builtin-agents/morpheus-agent.ts
|
|
89390
90623
|
function maybeCreateMorpheusConfig(input) {
|
|
89391
90624
|
const {
|
|
89392
90625
|
disabledAgents,
|
|
@@ -89403,25 +90636,25 @@ function maybeCreateMorpheusConfig(input) {
|
|
|
89403
90636
|
useTaskSystem
|
|
89404
90637
|
} = input;
|
|
89405
90638
|
const morpheusOverride = agentOverrides.morpheus;
|
|
89406
|
-
const
|
|
90639
|
+
const morpheusRequirement = AGENT_MODEL_REQUIREMENTS.morpheus;
|
|
89407
90640
|
const hasMorpheusExplicitConfig = morpheusOverride !== undefined;
|
|
89408
|
-
const meetsMorpheusAnyModelRequirement = !
|
|
90641
|
+
const meetsMorpheusAnyModelRequirement = !morpheusRequirement?.requiresAnyModel || hasMorpheusExplicitConfig || isFirstRunNoCache || isAnyFallbackModelAvailable(morpheusRequirement.fallbackChain, availableModels);
|
|
89409
90642
|
if (disabledAgents.includes("morpheus") || !meetsMorpheusAnyModelRequirement)
|
|
89410
90643
|
return;
|
|
89411
|
-
let
|
|
90644
|
+
let morpheusResolution = applyModelResolution({
|
|
89412
90645
|
uiSelectedModel: morpheusOverride?.model ? undefined : uiSelectedModel,
|
|
89413
90646
|
userModel: morpheusOverride?.model,
|
|
89414
|
-
requirement:
|
|
90647
|
+
requirement: morpheusRequirement,
|
|
89415
90648
|
availableModels,
|
|
89416
90649
|
systemDefaultModel
|
|
89417
90650
|
});
|
|
89418
90651
|
if (isFirstRunNoCache && !morpheusOverride?.model && !uiSelectedModel) {
|
|
89419
|
-
|
|
90652
|
+
morpheusResolution = getFirstFallbackModel(morpheusRequirement);
|
|
89420
90653
|
}
|
|
89421
|
-
if (!
|
|
90654
|
+
if (!morpheusResolution)
|
|
89422
90655
|
return;
|
|
89423
|
-
const { model:
|
|
89424
|
-
let morpheusConfig = createMorpheusAgent(
|
|
90656
|
+
const { model: morpheusModel, variant: morpheusResolvedVariant } = morpheusResolution;
|
|
90657
|
+
let morpheusConfig = createMorpheusAgent(morpheusModel, availableAgents, undefined, availableSkills, availableCategories, useTaskSystem);
|
|
89425
90658
|
if (morpheusResolvedVariant) {
|
|
89426
90659
|
morpheusConfig = { ...morpheusConfig, variant: morpheusResolvedVariant };
|
|
89427
90660
|
}
|
|
@@ -89626,7 +90859,7 @@ createMultimodalLookerAgent.mode = MODE5;
|
|
|
89626
90859
|
function sanitizeMarkdownTableCell(value) {
|
|
89627
90860
|
return value.replace(/\r?\n/g, " ").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
89628
90861
|
}
|
|
89629
|
-
function
|
|
90862
|
+
function isRecord7(value) {
|
|
89630
90863
|
return typeof value === "object" && value !== null;
|
|
89631
90864
|
}
|
|
89632
90865
|
function parseRegisteredAgentSummaries(input) {
|
|
@@ -89634,7 +90867,7 @@ function parseRegisteredAgentSummaries(input) {
|
|
|
89634
90867
|
return [];
|
|
89635
90868
|
const result = [];
|
|
89636
90869
|
for (const item of input) {
|
|
89637
|
-
if (!
|
|
90870
|
+
if (!isRecord7(item))
|
|
89638
90871
|
continue;
|
|
89639
90872
|
const name = typeof item.name === "string" ? item.name : undefined;
|
|
89640
90873
|
if (!name)
|
|
@@ -89673,7 +90906,7 @@ var MODE6 = "subagent";
|
|
|
89673
90906
|
var ORACLE_PROMPT_METADATA = {
|
|
89674
90907
|
category: "advisor",
|
|
89675
90908
|
cost: "EXPENSIVE",
|
|
89676
|
-
promptAlias: "
|
|
90909
|
+
promptAlias: "Consultant",
|
|
89677
90910
|
triggers: [
|
|
89678
90911
|
{ domain: "Architecture decisions", trigger: "Multi-system tradeoffs, unfamiliar patterns" },
|
|
89679
90912
|
{ domain: "Self-review", trigger: "After completing significant implementation" },
|
|
@@ -89695,6 +90928,31 @@ var ORACLE_PROMPT_METADATA = {
|
|
|
89695
90928
|
"Things you can infer from existing code patterns"
|
|
89696
90929
|
]
|
|
89697
90930
|
};
|
|
90931
|
+
var ORACLE_PLAN_BUILDER_METADATA = {
|
|
90932
|
+
category: "advisor",
|
|
90933
|
+
cost: "EXPENSIVE",
|
|
90934
|
+
keyTrigger: "Complex feature implementation, refactoring, or architecture design \u2192 consult Oracle for planning first",
|
|
90935
|
+
triggers: [
|
|
90936
|
+
{ domain: "Architecture decisions", trigger: "Multi-system tradeoffs, unfamiliar patterns" },
|
|
90937
|
+
{ domain: "Work planning", trigger: "Complex feature implementation, refactoring" },
|
|
90938
|
+
{ domain: "Complex task breakdown", trigger: "Large ambiguous requests needing decomposition" },
|
|
90939
|
+
{ domain: "Self-review", trigger: "After completing significant implementation" }
|
|
90940
|
+
],
|
|
90941
|
+
useWhen: [
|
|
90942
|
+
"Complex architecture design",
|
|
90943
|
+
"Work planning and task decomposition",
|
|
90944
|
+
"Large ambiguous requests needing structure",
|
|
90945
|
+
"After completing significant work for review",
|
|
90946
|
+
"Multi-system tradeoffs and integration planning"
|
|
90947
|
+
],
|
|
90948
|
+
avoidWhen: [
|
|
90949
|
+
"Simple file operations (use direct tools)",
|
|
90950
|
+
"Questions answerable from code you've read",
|
|
90951
|
+
"Trivial decisions (variable names, formatting)",
|
|
90952
|
+
"Quick debugging tasks (try yourself first)",
|
|
90953
|
+
"Things you can infer from existing code patterns"
|
|
90954
|
+
]
|
|
90955
|
+
};
|
|
89698
90956
|
var ORACLE_SYSTEM_PROMPT = `You are a strategic technical advisor with deep reasoning capabilities, operating as a specialized consultant within an AI-assisted development environment.
|
|
89699
90957
|
|
|
89700
90958
|
<context>
|
|
@@ -89805,7 +91063,7 @@ Before finalizing answers on architecture, security, or performance:
|
|
|
89805
91063
|
<delivery>
|
|
89806
91064
|
Your response goes directly to the user with no intermediate processing. Make your final message self-contained: a clear recommendation they can act on immediately, covering both what to do and why.
|
|
89807
91065
|
</delivery>`;
|
|
89808
|
-
function
|
|
91066
|
+
function createMerovingianAgent(model) {
|
|
89809
91067
|
const restrictions = createAgentToolRestrictions([
|
|
89810
91068
|
"write",
|
|
89811
91069
|
"edit",
|
|
@@ -89813,7 +91071,7 @@ function createOracleAgent(model) {
|
|
|
89813
91071
|
"task"
|
|
89814
91072
|
]);
|
|
89815
91073
|
const base = {
|
|
89816
|
-
description: "Read-only consultation agent. High-IQ reasoning specialist for debugging hard problems and high-difficulty architecture design. (
|
|
91074
|
+
description: "Read-only consultation agent. High-IQ reasoning specialist for debugging hard problems and high-difficulty architecture design. (Merovingian - Matrixx)",
|
|
89817
91075
|
mode: MODE6,
|
|
89818
91076
|
model,
|
|
89819
91077
|
temperature: 0.1,
|
|
@@ -89825,7 +91083,7 @@ function createOracleAgent(model) {
|
|
|
89825
91083
|
}
|
|
89826
91084
|
return { ...base, thinking: { type: "enabled", budgetTokens: 16000 } };
|
|
89827
91085
|
}
|
|
89828
|
-
|
|
91086
|
+
createMerovingianAgent.mode = MODE6;
|
|
89829
91087
|
|
|
89830
91088
|
// src/agents/operator.ts
|
|
89831
91089
|
var MODE7 = "subagent";
|
|
@@ -91142,25 +92400,26 @@ createExploreAgent.mode = MODE12;
|
|
|
91142
92400
|
var agentSources = {
|
|
91143
92401
|
morpheus: createMorpheusAgent,
|
|
91144
92402
|
keymaker: createKeymakerAgent,
|
|
91145
|
-
merovingian:
|
|
92403
|
+
merovingian: createMerovingianAgent,
|
|
91146
92404
|
operator: createLibrarianAgent,
|
|
91147
92405
|
trinity: createExploreAgent,
|
|
91148
92406
|
construct: createMultimodalLookerAgent,
|
|
91149
92407
|
seraph: createSeraphAgent,
|
|
91150
92408
|
smith: createSmithAgent,
|
|
91151
|
-
architect:
|
|
92409
|
+
architect: createArchitectAgent,
|
|
91152
92410
|
cipher: createCipherAgent,
|
|
91153
92411
|
sentinel: createSentinelAgent,
|
|
91154
92412
|
sati: createSatiAgent
|
|
91155
92413
|
};
|
|
91156
92414
|
var agentMetadata = {
|
|
91157
92415
|
merovingian: ORACLE_PROMPT_METADATA,
|
|
92416
|
+
oracle: ORACLE_PLAN_BUILDER_METADATA,
|
|
91158
92417
|
operator: LIBRARIAN_PROMPT_METADATA,
|
|
91159
92418
|
trinity: EXPLORE_PROMPT_METADATA,
|
|
91160
92419
|
construct: MULTIMODAL_LOOKER_PROMPT_METADATA,
|
|
91161
92420
|
seraph: seraphPromptMetadata,
|
|
91162
92421
|
smith: smithPromptMetadata,
|
|
91163
|
-
architect:
|
|
92422
|
+
architect: architectPromptMetadata,
|
|
91164
92423
|
cipher: CIPHER_PROMPT_METADATA,
|
|
91165
92424
|
sentinel: SENTINEL_PROMPT_METADATA,
|
|
91166
92425
|
sati: SATI_PROMPT_METADATA
|
|
@@ -91247,7 +92506,7 @@ async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, dir
|
|
|
91247
92506
|
for (const [name, config4] of pendingAgentConfigs) {
|
|
91248
92507
|
result[name] = config4;
|
|
91249
92508
|
}
|
|
91250
|
-
const
|
|
92509
|
+
const architectConfig = maybeCreateArchitectConfig({
|
|
91251
92510
|
disabledAgents,
|
|
91252
92511
|
agentOverrides,
|
|
91253
92512
|
uiSelectedModel,
|
|
@@ -91259,8 +92518,8 @@ async function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, dir
|
|
|
91259
92518
|
directory,
|
|
91260
92519
|
userCategories: categories
|
|
91261
92520
|
});
|
|
91262
|
-
if (
|
|
91263
|
-
result.architect =
|
|
92521
|
+
if (architectConfig) {
|
|
92522
|
+
result.architect = architectConfig;
|
|
91264
92523
|
}
|
|
91265
92524
|
return result;
|
|
91266
92525
|
}
|
|
@@ -94382,7 +95641,7 @@ function createToolRegistry(args) {
|
|
|
94382
95641
|
client: ctx.client,
|
|
94383
95642
|
directory: ctx.directory,
|
|
94384
95643
|
userCategories: pluginConfig.categories,
|
|
94385
|
-
|
|
95644
|
+
mouseModel: pluginConfig.agents?.mouse?.model,
|
|
94386
95645
|
browserProvider: skillContext.browserProvider,
|
|
94387
95646
|
disabledSkills: skillContext.disabledSkills,
|
|
94388
95647
|
availableCategories,
|
|
@@ -94431,6 +95690,11 @@ function createToolRegistry(args) {
|
|
|
94431
95690
|
} : {};
|
|
94432
95691
|
const hashlineEnabled = pluginConfig.experimental?.hashline_edit ?? false;
|
|
94433
95692
|
const hashlineToolsRecord = hashlineEnabled ? { edit: createHashlineEditTool(ctx) } : {};
|
|
95693
|
+
const assemblyEnabled = pluginConfig.assembly?.enabled !== false;
|
|
95694
|
+
const assemblyTool = assemblyEnabled ? createAssemblyTool({
|
|
95695
|
+
manager: managers.backgroundManager,
|
|
95696
|
+
pluginConfig
|
|
95697
|
+
}) : null;
|
|
94434
95698
|
const allTools = {
|
|
94435
95699
|
...builtinTools,
|
|
94436
95700
|
...createGrepTools(ctx),
|
|
@@ -94447,7 +95711,8 @@ function createToolRegistry(args) {
|
|
|
94447
95711
|
slashcommand: slashcommandTool,
|
|
94448
95712
|
interactive_bash,
|
|
94449
95713
|
...taskToolsRecord,
|
|
94450
|
-
...hashlineToolsRecord
|
|
95714
|
+
...hashlineToolsRecord,
|
|
95715
|
+
...assemblyTool ? { assembly: assemblyTool } : {}
|
|
94451
95716
|
};
|
|
94452
95717
|
const filteredTools = filterDisabledTools(allTools, pluginConfig.disabled_tools);
|
|
94453
95718
|
return {
|
|
@@ -94871,7 +96136,9 @@ var BuiltinSkillNameSchema = exports_external.enum([
|
|
|
94871
96136
|
"review-work",
|
|
94872
96137
|
"quality-gate",
|
|
94873
96138
|
"software-dev",
|
|
94874
|
-
"matrixx-self-config"
|
|
96139
|
+
"matrixx-self-config",
|
|
96140
|
+
"ulw-research",
|
|
96141
|
+
"remove-ai-slops"
|
|
94875
96142
|
]);
|
|
94876
96143
|
// src/config/schema/internal/permission.ts
|
|
94877
96144
|
var PermissionValueSchema = exports_external.enum(["ask", "allow", "deny"]);
|
|
@@ -94937,6 +96204,17 @@ var AgentOverridesSchema = exports_external.object({
|
|
|
94937
96204
|
sentinel: AgentOverrideConfigSchema.optional(),
|
|
94938
96205
|
sati: AgentOverrideConfigSchema.optional()
|
|
94939
96206
|
});
|
|
96207
|
+
// src/config/schema/assembly.ts
|
|
96208
|
+
var AssemblyConfigSchema = exports_external.object({
|
|
96209
|
+
providers: exports_external.array(exports_external.object({
|
|
96210
|
+
providerID: exports_external.string(),
|
|
96211
|
+
modelID: exports_external.string()
|
|
96212
|
+
})).optional(),
|
|
96213
|
+
default_voters: exports_external.number().int().min(2).max(5).optional(),
|
|
96214
|
+
default_rounds: exports_external.number().int().min(1).max(3).optional(),
|
|
96215
|
+
timeout_ms: exports_external.number().int().min(1e4).max(300000).optional(),
|
|
96216
|
+
enabled: exports_external.boolean().default(false)
|
|
96217
|
+
});
|
|
94940
96218
|
// src/config/schema/babysitting.ts
|
|
94941
96219
|
var BabysittingConfigSchema = exports_external.object({
|
|
94942
96220
|
timeout_ms: exports_external.number().default(120000)
|
|
@@ -95016,7 +96294,11 @@ var BuiltinCommandNameSchema = exports_external.enum([
|
|
|
95016
96294
|
"start-work",
|
|
95017
96295
|
"stop-continuation",
|
|
95018
96296
|
"profile",
|
|
95019
|
-
"end-ultrawork"
|
|
96297
|
+
"end-ultrawork",
|
|
96298
|
+
"handoff",
|
|
96299
|
+
"research",
|
|
96300
|
+
"assembly",
|
|
96301
|
+
"ultrawork"
|
|
95020
96302
|
]);
|
|
95021
96303
|
// src/config/schema/comment-checker.ts
|
|
95022
96304
|
var CommentCheckerConfigSchema = exports_external.object({
|
|
@@ -95066,11 +96348,10 @@ var HookNameSchema = exports_external.enum([
|
|
|
95066
96348
|
"edit-error-recovery",
|
|
95067
96349
|
"delegate-task-retry",
|
|
95068
96350
|
"prometheus-md-only",
|
|
95069
|
-
"
|
|
96351
|
+
"mouse-notepad",
|
|
95070
96352
|
"start-work",
|
|
95071
96353
|
"architect",
|
|
95072
96354
|
"unstable-agent-babysitter",
|
|
95073
|
-
"task-reminder",
|
|
95074
96355
|
"task-resume-info",
|
|
95075
96356
|
"stop-continuation-guard",
|
|
95076
96357
|
"tasks-todowrite-disabler",
|
|
@@ -95271,6 +96552,7 @@ var MatrixxConfigSchema = exports_external.object({
|
|
|
95271
96552
|
websearch: WebsearchConfigSchema.optional(),
|
|
95272
96553
|
tmux: TmuxConfigSchema.optional(),
|
|
95273
96554
|
morpheus: MorpheusConfigSchema.optional(),
|
|
96555
|
+
assembly: AssemblyConfigSchema.optional(),
|
|
95274
96556
|
security: SecurityConfigSchema.optional(),
|
|
95275
96557
|
runtime_fallback: RuntimeFallbackConfigSchema.optional(),
|
|
95276
96558
|
agent_definitions: AgentDefinitionsConfigSchema.optional(),
|
|
@@ -95418,6 +96700,7 @@ function loadPluginConfig(directory, ctx) {
|
|
|
95418
96700
|
}
|
|
95419
96701
|
|
|
95420
96702
|
// src/plugin/chat-message.ts
|
|
96703
|
+
init_logger();
|
|
95421
96704
|
function isStartWorkHookOutput(value) {
|
|
95422
96705
|
if (typeof value !== "object" || value === null)
|
|
95423
96706
|
return false;
|
|
@@ -95471,7 +96754,9 @@ function createChatMessageHandler2(args) {
|
|
|
95471
96754
|
variant: "warning",
|
|
95472
96755
|
duration: 6000
|
|
95473
96756
|
}
|
|
95474
|
-
}).catch(() => {
|
|
96757
|
+
}).catch((err) => {
|
|
96758
|
+
log("[plugin] Provider cache toast failed:", err);
|
|
96759
|
+
});
|
|
95475
96760
|
}
|
|
95476
96761
|
if (hooks2.matrixLoop) {
|
|
95477
96762
|
const parts = output.parts;
|
|
@@ -95498,11 +96783,11 @@ function createChatMessageHandler2(args) {
|
|
|
95498
96783
|
}
|
|
95499
96784
|
|
|
95500
96785
|
// src/plugin/chat-params.ts
|
|
95501
|
-
function
|
|
96786
|
+
function isRecord8(value) {
|
|
95502
96787
|
return typeof value === "object" && value !== null;
|
|
95503
96788
|
}
|
|
95504
96789
|
function buildChatParamsInput(raw) {
|
|
95505
|
-
if (!
|
|
96790
|
+
if (!isRecord8(raw))
|
|
95506
96791
|
return null;
|
|
95507
96792
|
const sessionID = raw.sessionID;
|
|
95508
96793
|
const agent = raw.agent;
|
|
@@ -95513,11 +96798,11 @@ function buildChatParamsInput(raw) {
|
|
|
95513
96798
|
return null;
|
|
95514
96799
|
if (typeof agent !== "string")
|
|
95515
96800
|
return null;
|
|
95516
|
-
if (!
|
|
96801
|
+
if (!isRecord8(model))
|
|
95517
96802
|
return null;
|
|
95518
|
-
if (!
|
|
96803
|
+
if (!isRecord8(provider))
|
|
95519
96804
|
return null;
|
|
95520
|
-
if (!
|
|
96805
|
+
if (!isRecord8(message))
|
|
95521
96806
|
return null;
|
|
95522
96807
|
const providerID = model.providerID;
|
|
95523
96808
|
const modelID = model.modelID;
|
|
@@ -95538,12 +96823,12 @@ function buildChatParamsInput(raw) {
|
|
|
95538
96823
|
};
|
|
95539
96824
|
}
|
|
95540
96825
|
function isChatParamsOutput(raw) {
|
|
95541
|
-
if (!
|
|
96826
|
+
if (!isRecord8(raw))
|
|
95542
96827
|
return false;
|
|
95543
|
-
if (!
|
|
96828
|
+
if (!isRecord8(raw.options)) {
|
|
95544
96829
|
raw.options = {};
|
|
95545
96830
|
}
|
|
95546
|
-
return
|
|
96831
|
+
return isRecord8(raw.options);
|
|
95547
96832
|
}
|
|
95548
96833
|
function createChatParamsHandler(args) {
|
|
95549
96834
|
return async (input, output) => {
|
|
@@ -95561,6 +96846,9 @@ function createChatParamsHandler(args) {
|
|
|
95561
96846
|
};
|
|
95562
96847
|
}
|
|
95563
96848
|
|
|
96849
|
+
// src/plugin/event.ts
|
|
96850
|
+
init_logger();
|
|
96851
|
+
|
|
95564
96852
|
// src/plugin/recent-synthetic-idles.ts
|
|
95565
96853
|
function pruneRecentSyntheticIdles(args) {
|
|
95566
96854
|
const { recentSyntheticIdles, recentRealIdles, now, dedupWindowMs } = args;
|
|
@@ -95618,7 +96906,7 @@ function createEventHandler2(args) {
|
|
|
95618
96906
|
await Promise.resolve(hooks2.matrixLoop?.event?.(input));
|
|
95619
96907
|
await Promise.resolve(hooks2.stopContinuationGuard?.event?.(input));
|
|
95620
96908
|
await Promise.resolve(hooks2.compactionTodoPreserver?.event?.(input));
|
|
95621
|
-
await Promise.resolve(hooks2.
|
|
96909
|
+
await Promise.resolve(hooks2.architectHook?.handler?.(input));
|
|
95622
96910
|
};
|
|
95623
96911
|
const recentSyntheticIdles = new Map;
|
|
95624
96912
|
const recentRealIdles = new Map;
|
|
@@ -95704,7 +96992,9 @@ function createEventHandler2(args) {
|
|
|
95704
96992
|
path: { id: sessionID },
|
|
95705
96993
|
body: { parts: [{ type: "text", text: "continue" }] },
|
|
95706
96994
|
query: { directory: ctx.directory }
|
|
95707
|
-
}).catch(() => {
|
|
96995
|
+
}).catch((err) => {
|
|
96996
|
+
log("[plugin] Session prompt failed:", err);
|
|
96997
|
+
});
|
|
95708
96998
|
}
|
|
95709
96999
|
}
|
|
95710
97000
|
}
|
|
@@ -95803,7 +97093,7 @@ function createToolExecuteAfterHandler2(args) {
|
|
|
95803
97093
|
await hooks2.interactiveBashSession?.["tool.execute.after"]?.(input, output);
|
|
95804
97094
|
await hooks2.editErrorRecovery?.["tool.execute.after"]?.(input, output);
|
|
95805
97095
|
await hooks2.delegateTaskRetry?.["tool.execute.after"]?.(input, output);
|
|
95806
|
-
await hooks2.
|
|
97096
|
+
await hooks2.architectHook?.["tool.execute.after"]?.(input, output);
|
|
95807
97097
|
await hooks2.taskResumeInfo?.["tool.execute.after"]?.(input, output);
|
|
95808
97098
|
await hooks2.hashlineReadEnhancer?.["tool.execute.after"]?.(input, output);
|
|
95809
97099
|
await hooks2.jsonErrorRecovery?.["tool.execute.after"]?.(input, output);
|
|
@@ -95818,6 +97108,25 @@ function createToolExecuteAfterHandler2(args) {
|
|
|
95818
97108
|
};
|
|
95819
97109
|
}
|
|
95820
97110
|
|
|
97111
|
+
// src/features/assembly-state/manager.ts
|
|
97112
|
+
init_logger();
|
|
97113
|
+
var HOOK_NAME15 = "assembly-state";
|
|
97114
|
+
var disabledSessions = new Set;
|
|
97115
|
+
function isAssemblyDisabled(sessionID) {
|
|
97116
|
+
return disabledSessions.has(sessionID);
|
|
97117
|
+
}
|
|
97118
|
+
function disableAssembly(sessionID) {
|
|
97119
|
+
disabledSessions.add(sessionID);
|
|
97120
|
+
log(`[${HOOK_NAME15}] Assembly disabled for session`, { sessionID });
|
|
97121
|
+
disabledSessions.add(sessionID);
|
|
97122
|
+
log(`[${HOOK_NAME15}] Consensus disabled for session`, { sessionID });
|
|
97123
|
+
}
|
|
97124
|
+
function enableAssembly(sessionID) {
|
|
97125
|
+
disabledSessions.delete(sessionID);
|
|
97126
|
+
log(`[${HOOK_NAME15}] Assembly enabled for session`, { sessionID });
|
|
97127
|
+
disabledSessions.delete(sessionID);
|
|
97128
|
+
log(`[${HOOK_NAME15}] Consensus enabled for session`, { sessionID });
|
|
97129
|
+
}
|
|
95821
97130
|
// src/plugin/session-agent-resolver.ts
|
|
95822
97131
|
async function resolveSessionAgent(client2, sessionId, timeoutMs = 5000) {
|
|
95823
97132
|
try {
|
|
@@ -95862,8 +97171,8 @@ function createToolExecuteBeforeHandler2(args) {
|
|
|
95862
97171
|
const nonInteractiveEnvHook = hooks2.nonInteractiveEnv?.["tool.execute.before"];
|
|
95863
97172
|
const bashFileReadGuardHook = hooks2.bashFileReadGuard?.["tool.execute.before"];
|
|
95864
97173
|
const questionLabelTruncatorHook = hooks2.questionLabelTruncator?.["tool.execute.before"];
|
|
95865
|
-
const
|
|
95866
|
-
const
|
|
97174
|
+
const mouseNotepadHook = hooks2.mouseNotepad?.["tool.execute.before"];
|
|
97175
|
+
const architectHookHook = hooks2.architectHook?.["tool.execute.before"];
|
|
95867
97176
|
return async (input, output) => {
|
|
95868
97177
|
await Promise.all([
|
|
95869
97178
|
qualityGateHook?.(input, output),
|
|
@@ -95895,8 +97204,8 @@ function createToolExecuteBeforeHandler2(args) {
|
|
|
95895
97204
|
await bashFileReadGuardHook?.(input, output);
|
|
95896
97205
|
await questionLabelTruncatorHook?.(input, output);
|
|
95897
97206
|
await prometheusMdOnlyHook?.(input, output);
|
|
95898
|
-
await
|
|
95899
|
-
await
|
|
97207
|
+
await mouseNotepadHook?.(input, output);
|
|
97208
|
+
await architectHookHook?.(input, output);
|
|
95900
97209
|
if (resolvePromise) {
|
|
95901
97210
|
const resolvedAgent = await resolvePromise;
|
|
95902
97211
|
output.args.subagent_type = resolvedAgent ?? "continue";
|
|
@@ -95940,6 +97249,32 @@ function createToolExecuteBeforeHandler2(args) {
|
|
|
95940
97249
|
sessionID
|
|
95941
97250
|
});
|
|
95942
97251
|
}
|
|
97252
|
+
if (command === "assembly" && sessionID) {
|
|
97253
|
+
const subcommand = rawCommand?.replace(/^\/?assembly\s*/i, "").trim().toLowerCase();
|
|
97254
|
+
if (subcommand === "disable" || subcommand === "off") {
|
|
97255
|
+
disableAssembly(sessionID);
|
|
97256
|
+
} else if (subcommand === "enable" || subcommand === "on") {
|
|
97257
|
+
enableAssembly(sessionID);
|
|
97258
|
+
} else {
|
|
97259
|
+
const state3 = isAssemblyDisabled(sessionID) ? "disabled" : "enabled";
|
|
97260
|
+
log("[assembly] Assembly state check", { sessionID, state: state3 });
|
|
97261
|
+
}
|
|
97262
|
+
}
|
|
97263
|
+
if (command === "ultrawork" && sessionID) {
|
|
97264
|
+
const subcommand = rawCommand?.replace(/^\/?ultrawork\s*/i, "").trim().toLowerCase();
|
|
97265
|
+
if (subcommand === "disable" || subcommand === "off") {
|
|
97266
|
+
disableUltrawork(sessionID);
|
|
97267
|
+
} else if (subcommand === "enable" || subcommand === "on") {
|
|
97268
|
+
enableUltrawork(sessionID);
|
|
97269
|
+
} else {
|
|
97270
|
+
const state3 = getUltraworkState(sessionID) ?? "default (keyword-triggered)";
|
|
97271
|
+
log("[ultrawork] Ultrawork state check", { sessionID, state: state3 });
|
|
97272
|
+
}
|
|
97273
|
+
}
|
|
97274
|
+
if (command === "end-ultrawork" && sessionID) {
|
|
97275
|
+
disableUltrawork(sessionID);
|
|
97276
|
+
log("[end-ultrawork] Ultrawork disabled via /end-ultrawork", { sessionID });
|
|
97277
|
+
}
|
|
95943
97278
|
}
|
|
95944
97279
|
};
|
|
95945
97280
|
}
|