opencode-matrixx 2.1.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 +129 -18
- package/dist/agents/builtin-agents/model-resolution.d.ts +1 -1
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/merovingian.d.ts +2 -2
- package/dist/config/index.d.ts +2 -2
- package/dist/config/schema/agent-names.d.ts +3 -17
- 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 +0 -1
- package/dist/config/schema/matrixx-config.d.ts +16 -1
- package/dist/config/schema/security.d.ts +0 -5
- package/dist/config/schema.d.ts +1 -0
- 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/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 +1 -2
- 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/auto-update-checker/cache.d.ts +0 -2
- package/dist/hooks/auto-update-checker/index.d.ts +1 -1
- package/dist/hooks/interactive-bash-session/parser.d.ts +0 -6
- package/dist/index.js +1824 -539
- 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/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 -30691
- 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/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 = {};
|
|
@@ -18968,6 +18968,7 @@ function getCurrentPaneId() {
|
|
|
18968
18968
|
var {spawn: spawn4 } = globalThis.Bun;
|
|
18969
18969
|
|
|
18970
18970
|
// src/tools/interactive-bash/tmux-path-resolver.ts
|
|
18971
|
+
init_logger();
|
|
18971
18972
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
18972
18973
|
var tmuxPath = null;
|
|
18973
18974
|
var initPromise = null;
|
|
@@ -19022,7 +19023,9 @@ function getCachedTmuxPath() {
|
|
|
19022
19023
|
function startBackgroundCheck() {
|
|
19023
19024
|
if (!initPromise) {
|
|
19024
19025
|
initPromise = getTmuxPath();
|
|
19025
|
-
initPromise.catch(() => {
|
|
19026
|
+
initPromise.catch((err) => {
|
|
19027
|
+
log("[interactive-bash] Background tmux path resolution failed:", err);
|
|
19028
|
+
});
|
|
19026
19029
|
}
|
|
19027
19030
|
}
|
|
19028
19031
|
|
|
@@ -19252,6 +19255,13 @@ async function spawnTmuxPane(sessionId, description, config, serverUrl, targetPa
|
|
|
19252
19255
|
}
|
|
19253
19256
|
return { success: true, paneId };
|
|
19254
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
|
+
}
|
|
19255
19265
|
// src/shared/transcript-path.ts
|
|
19256
19266
|
import { join as join15 } from "path";
|
|
19257
19267
|
var TRANSCRIPT_DIR = join15(getClaudeConfigDir(), "transcripts");
|
|
@@ -19406,6 +19416,9 @@ function createAgentUsageReminderHook(_ctx) {
|
|
|
19406
19416
|
event: eventHandler
|
|
19407
19417
|
};
|
|
19408
19418
|
}
|
|
19419
|
+
// src/hooks/anthropic-context-window-limit-recovery/executor.ts
|
|
19420
|
+
init_logger();
|
|
19421
|
+
|
|
19409
19422
|
// src/hooks/anthropic-context-window-limit-recovery/aggressive-truncation-strategy.ts
|
|
19410
19423
|
init_logger();
|
|
19411
19424
|
|
|
@@ -20270,7 +20283,9 @@ async function runAggressiveTruncationStrategy(params) {
|
|
|
20270
20283
|
variant: aggressiveResult.sufficient ? "success" : "warning",
|
|
20271
20284
|
duration: 4000
|
|
20272
20285
|
}
|
|
20273
|
-
}).catch(() => {
|
|
20286
|
+
}).catch((err) => {
|
|
20287
|
+
log("[auto-compact] Toast failed:", err);
|
|
20288
|
+
});
|
|
20274
20289
|
log("[auto-compact] aggressive truncation completed", aggressiveResult);
|
|
20275
20290
|
if (aggressiveResult.sufficient) {
|
|
20276
20291
|
clearSessionState(params.autoCompactState, params.sessionID);
|
|
@@ -20284,7 +20299,9 @@ async function runAggressiveTruncationStrategy(params) {
|
|
|
20284
20299
|
},
|
|
20285
20300
|
query: { directory: params.directory }
|
|
20286
20301
|
});
|
|
20287
|
-
} catch {
|
|
20302
|
+
} catch (err) {
|
|
20303
|
+
log("[auto-compact] Failed to send continue prompt:", err);
|
|
20304
|
+
}
|
|
20288
20305
|
}, 500);
|
|
20289
20306
|
return { handled: true, nextTruncateAttempt };
|
|
20290
20307
|
}
|
|
@@ -20295,6 +20312,12 @@ async function runAggressiveTruncationStrategy(params) {
|
|
|
20295
20312
|
});
|
|
20296
20313
|
return { handled: false, nextTruncateAttempt };
|
|
20297
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
|
+
|
|
20298
20321
|
// src/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.ts
|
|
20299
20322
|
var IGNORE_TYPES2 = new Set(["thinking", "redacted_thinking", "meta"]);
|
|
20300
20323
|
var TOOL_TYPES2 = new Set(["tool", "tool_use", "tool_result"]);
|
|
@@ -20441,7 +20464,9 @@ async function fixEmptyMessages(params) {
|
|
|
20441
20464
|
variant: "error",
|
|
20442
20465
|
duration: 5000
|
|
20443
20466
|
}
|
|
20444
|
-
}).catch(() => {
|
|
20467
|
+
}).catch((err) => {
|
|
20468
|
+
log("[auto-compact] Toast failed:", err);
|
|
20469
|
+
});
|
|
20445
20470
|
return false;
|
|
20446
20471
|
}
|
|
20447
20472
|
if (result.fixed) {
|
|
@@ -20452,7 +20477,9 @@ async function fixEmptyMessages(params) {
|
|
|
20452
20477
|
variant: "warning",
|
|
20453
20478
|
duration: 3000
|
|
20454
20479
|
}
|
|
20455
|
-
}).catch(() => {
|
|
20480
|
+
}).catch((err) => {
|
|
20481
|
+
log("[auto-compact] Toast failed:", err);
|
|
20482
|
+
});
|
|
20456
20483
|
}
|
|
20457
20484
|
return result.fixed;
|
|
20458
20485
|
}
|
|
@@ -20482,7 +20509,9 @@ async function fixEmptyMessages(params) {
|
|
|
20482
20509
|
variant: "error",
|
|
20483
20510
|
duration: 5000
|
|
20484
20511
|
}
|
|
20485
|
-
}).catch(() => {
|
|
20512
|
+
}).catch((err) => {
|
|
20513
|
+
log("[auto-compact] Toast failed:", err);
|
|
20514
|
+
});
|
|
20486
20515
|
return false;
|
|
20487
20516
|
}
|
|
20488
20517
|
for (const messageID of emptyMessageIds) {
|
|
@@ -20507,7 +20536,9 @@ async function fixEmptyMessages(params) {
|
|
|
20507
20536
|
variant: "warning",
|
|
20508
20537
|
duration: 3000
|
|
20509
20538
|
}
|
|
20510
|
-
}).catch(() => {
|
|
20539
|
+
}).catch((err) => {
|
|
20540
|
+
log("[auto-compact] Toast failed:", err);
|
|
20541
|
+
});
|
|
20511
20542
|
}
|
|
20512
20543
|
return fixed;
|
|
20513
20544
|
}
|
|
@@ -20538,7 +20569,9 @@ async function runSummarizeRetryStrategy(params) {
|
|
|
20538
20569
|
variant: "error",
|
|
20539
20570
|
duration: 1e4
|
|
20540
20571
|
}
|
|
20541
|
-
}).catch(() => {
|
|
20572
|
+
}).catch((err) => {
|
|
20573
|
+
log("[auto-compact] Toast failed:", err);
|
|
20574
|
+
});
|
|
20542
20575
|
return;
|
|
20543
20576
|
}
|
|
20544
20577
|
}
|
|
@@ -20561,7 +20594,9 @@ async function runSummarizeRetryStrategy(params) {
|
|
|
20561
20594
|
variant: "warning",
|
|
20562
20595
|
duration: 3000
|
|
20563
20596
|
}
|
|
20564
|
-
}).catch(() => {
|
|
20597
|
+
}).catch((err) => {
|
|
20598
|
+
log("[auto-compact] Toast failed:", err);
|
|
20599
|
+
});
|
|
20565
20600
|
const summarizeBody = { providerID, modelID, auto: true };
|
|
20566
20601
|
await params.client.session.summarize({
|
|
20567
20602
|
path: { id: params.sessionID },
|
|
@@ -20585,7 +20620,9 @@ async function runSummarizeRetryStrategy(params) {
|
|
|
20585
20620
|
variant: "warning",
|
|
20586
20621
|
duration: 3000
|
|
20587
20622
|
}
|
|
20588
|
-
}).catch(() => {
|
|
20623
|
+
}).catch((err) => {
|
|
20624
|
+
log("[auto-compact] Toast failed:", err);
|
|
20625
|
+
});
|
|
20589
20626
|
}
|
|
20590
20627
|
}
|
|
20591
20628
|
clearSessionState(params.autoCompactState, params.sessionID);
|
|
@@ -20596,7 +20633,9 @@ async function runSummarizeRetryStrategy(params) {
|
|
|
20596
20633
|
variant: "error",
|
|
20597
20634
|
duration: 5000
|
|
20598
20635
|
}
|
|
20599
|
-
}).catch(() => {
|
|
20636
|
+
}).catch((err) => {
|
|
20637
|
+
log("[auto-compact] Toast failed:", err);
|
|
20638
|
+
});
|
|
20600
20639
|
}
|
|
20601
20640
|
// src/hooks/anthropic-context-window-limit-recovery/executor.ts
|
|
20602
20641
|
async function executeCompact(sessionID, msg, autoCompactState, client, directory, experimental) {
|
|
@@ -20608,7 +20647,9 @@ async function executeCompact(sessionID, msg, autoCompactState, client, director
|
|
|
20608
20647
|
variant: "warning",
|
|
20609
20648
|
duration: 5000
|
|
20610
20649
|
}
|
|
20611
|
-
}).catch(() => {
|
|
20650
|
+
}).catch((err) => {
|
|
20651
|
+
log("[auto-compact] Toast failed:", err);
|
|
20652
|
+
});
|
|
20612
20653
|
return;
|
|
20613
20654
|
}
|
|
20614
20655
|
autoCompactState.compactionInProgress.add(sessionID);
|
|
@@ -20884,7 +20925,9 @@ function createAnthropicContextWindowLimitRecoveryHook(ctx, options) {
|
|
|
20884
20925
|
variant: "warning",
|
|
20885
20926
|
duration: 3000
|
|
20886
20927
|
}
|
|
20887
|
-
}).catch(() => {
|
|
20928
|
+
}).catch((err) => {
|
|
20929
|
+
log("[auto-compact] Toast failed:", err);
|
|
20930
|
+
});
|
|
20888
20931
|
const timeoutID = setTimeout(() => {
|
|
20889
20932
|
pendingCompactionTimeoutBySession.delete(sessionID);
|
|
20890
20933
|
executeCompact(sessionID, { providerID, modelID }, autoCompactState, ctx.client, ctx.directory, experimental);
|
|
@@ -20935,7 +20978,9 @@ function createAnthropicContextWindowLimitRecoveryHook(ctx, options) {
|
|
|
20935
20978
|
variant: "warning",
|
|
20936
20979
|
duration: 3000
|
|
20937
20980
|
}
|
|
20938
|
-
}).catch(() => {
|
|
20981
|
+
}).catch((err) => {
|
|
20982
|
+
log("[auto-compact] Toast failed:", err);
|
|
20983
|
+
});
|
|
20939
20984
|
await executeCompact(sessionID, { providerID, modelID }, autoCompactState, ctx.client, ctx.directory, experimental);
|
|
20940
20985
|
}
|
|
20941
20986
|
};
|
|
@@ -21884,6 +21929,22 @@ function findSlashCommandPartIndex(parts) {
|
|
|
21884
21929
|
import { existsSync as existsSync22, readdirSync as readdirSync10, readFileSync as readFileSync17 } from "fs";
|
|
21885
21930
|
import { basename as basename3, dirname as dirname4, join as join31 } from "path";
|
|
21886
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
|
+
|
|
21887
21948
|
// src/features/builtin-commands/templates/end-ultrawork.ts
|
|
21888
21949
|
var END_ULTRAWORK_TEMPLATE = `Deactivate ultrawork mode for the current session.
|
|
21889
21950
|
|
|
@@ -23452,6 +23513,51 @@ $ARGUMENTS
|
|
|
23452
23513
|
</user-request>
|
|
23453
23514
|
`;
|
|
23454
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
|
+
|
|
23455
23561
|
// src/features/builtin-commands/templates/start-work.ts
|
|
23456
23562
|
var START_WORK_TEMPLATE = `You are starting a Morpheus work session.
|
|
23457
23563
|
|
|
@@ -23541,6 +23647,22 @@ After running this command:
|
|
|
23541
23647
|
|
|
23542
23648
|
Use this when you need to pause automated continuation and take manual control.`;
|
|
23543
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
|
+
|
|
23544
23666
|
// src/features/builtin-commands/commands.ts
|
|
23545
23667
|
var BUILTIN_COMMAND_DEFINITIONS = {
|
|
23546
23668
|
"init-deep": {
|
|
@@ -23667,6 +23789,39 @@ ${END_ULTRAWORK_TEMPLATE}
|
|
|
23667
23789
|
$ARGUMENTS
|
|
23668
23790
|
</user-request>`,
|
|
23669
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]"
|
|
23670
23825
|
}
|
|
23671
23826
|
};
|
|
23672
23827
|
function loadBuiltinCommands(disabledCommands) {
|
|
@@ -30266,17 +30421,306 @@ Fix these inline when possible. For bulk fixes, run \`bun run lint:fix\`.
|
|
|
30266
30421
|
| Report done without running verification | Never trust \u2014 always verify |
|
|
30267
30422
|
`
|
|
30268
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
|
+
};
|
|
30269
30713
|
// src/features/builtin-skills/skills/review-work.ts
|
|
30270
30714
|
var REVIEW_WORK_SKILL_NAME = "review-work";
|
|
30271
|
-
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'.";
|
|
30272
30716
|
var reviewWorkSkill = {
|
|
30273
30717
|
name: REVIEW_WORK_SKILL_NAME,
|
|
30274
30718
|
description: REVIEW_WORK_SKILL_DESCRIPTION,
|
|
30275
30719
|
template: `# Review Work - 5-Agent Parallel Review Orchestrator
|
|
30276
30720
|
|
|
30277
|
-
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.
|
|
30278
30722
|
|
|
30279
|
-
The
|
|
30723
|
+
The 6 agents cover complementary concerns - together they form a comprehensive review that no single reviewer could match:
|
|
30280
30724
|
|
|
30281
30725
|
| # | Agent | Type | Role | Focus Level |
|
|
30282
30726
|
|---|-------|------|------|-------------|
|
|
@@ -30285,6 +30729,7 @@ The 5 agents cover complementary concerns - together they form a comprehensive r
|
|
|
30285
30729
|
| 3 | Code Reviewer | Oracle | Is the code well-written? | MAIN |
|
|
30286
30730
|
| 4 | Security Auditor | Oracle | Is it secure? | SUB |
|
|
30287
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 |
|
|
30288
30733
|
|
|
30289
30734
|
---
|
|
30290
30735
|
|
|
@@ -30618,9 +31063,54 @@ OUTPUT FORMAT:
|
|
|
30618
31063
|
|
|
30619
31064
|
---
|
|
30620
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
|
+
|
|
30621
31111
|
## Phase 2: Wait & Collect
|
|
30622
31112
|
|
|
30623
|
-
After launching all
|
|
31113
|
+
After launching all 6 agents in one turn, **end your response**. Wait for system notifications as each agent completes.
|
|
30624
31114
|
|
|
30625
31115
|
Collect via \`background_output(task_id="...")\` as each completes. Track verdicts:
|
|
30626
31116
|
|
|
@@ -30631,14 +31121,15 @@ Collect via \`background_output(task_id="...")\` as each completes. Track verdic
|
|
|
30631
31121
|
| 3. Code Quality | pending | - |
|
|
30632
31122
|
| 4. Security | pending | - |
|
|
30633
31123
|
| 5. Context Mining | pending | - |
|
|
31124
|
+
| 6. AI Slop Detection | pending | - |
|
|
30634
31125
|
|
|
30635
|
-
Do NOT deliver the final report until ALL
|
|
31126
|
+
Do NOT deliver the final report until ALL 6 have completed.
|
|
30636
31127
|
|
|
30637
31128
|
---
|
|
30638
31129
|
|
|
30639
31130
|
## Phase 3: Deliver Verdict
|
|
30640
31131
|
|
|
30641
|
-
ALL
|
|
31132
|
+
ALL 6 returned PASS \u2192 **REVIEW PASSED**
|
|
30642
31133
|
ANY returned FAIL \u2192 **REVIEW FAILED**
|
|
30643
31134
|
|
|
30644
31135
|
Final report format:
|
|
@@ -30655,6 +31146,7 @@ Final report format:
|
|
|
30655
31146
|
| 3 | Code Quality | Oracle | PASS/FAIL | HIGH/MED/LOW |
|
|
30656
31147
|
| 4 | Security (supplementary) | Oracle | PASS/FAIL | Severity |
|
|
30657
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 |
|
|
30658
31150
|
|
|
30659
31151
|
## Blocking Issues
|
|
30660
31152
|
[Aggregated from all agents - deduplicated, prioritized]
|
|
@@ -31842,6 +32334,252 @@ NEVER write tests AFTER implementation \u2014 write them BEFORE
|
|
|
31842
32334
|
\`\`\`
|
|
31843
32335
|
`
|
|
31844
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
|
+
};
|
|
31845
32583
|
// src/features/builtin-skills/skills.ts
|
|
31846
32584
|
function createBuiltinSkills(options = {}) {
|
|
31847
32585
|
const { browserProvider = "playwright", disabledSkills: rawDisabledSkills } = options;
|
|
@@ -31854,7 +32592,7 @@ function createBuiltinSkills(options = {}) {
|
|
|
31854
32592
|
} else {
|
|
31855
32593
|
browserSkill = playwrightSkill;
|
|
31856
32594
|
}
|
|
31857
|
-
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];
|
|
31858
32596
|
if (!disabledSkills) {
|
|
31859
32597
|
return skills;
|
|
31860
32598
|
}
|
|
@@ -32555,67 +33293,6 @@ function revertPinnedVersion(configPath, failedVersion, originalEntry) {
|
|
|
32555
33293
|
// src/hooks/auto-update-checker/hook.ts
|
|
32556
33294
|
init_logger();
|
|
32557
33295
|
|
|
32558
|
-
// src/cli/config-manager/config-context.ts
|
|
32559
|
-
var configContext = null;
|
|
32560
|
-
function getConfigContext() {
|
|
32561
|
-
if (!configContext) {
|
|
32562
|
-
if (true) {
|
|
32563
|
-
console.warn("[config-context] getConfigContext() called before initConfigContext(); defaulting to CLI paths.");
|
|
32564
|
-
}
|
|
32565
|
-
const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null });
|
|
32566
|
-
configContext = { binary: "opencode", version: null, paths };
|
|
32567
|
-
}
|
|
32568
|
-
return configContext;
|
|
32569
|
-
}
|
|
32570
|
-
function getConfigDir() {
|
|
32571
|
-
return getConfigContext().paths.configDir;
|
|
32572
|
-
}
|
|
32573
|
-
// src/cli/config-manager/bun-install.ts
|
|
32574
|
-
var BUN_INSTALL_TIMEOUT_SECONDS = 60;
|
|
32575
|
-
var BUN_INSTALL_TIMEOUT_MS = BUN_INSTALL_TIMEOUT_SECONDS * 1000;
|
|
32576
|
-
async function runBunInstall() {
|
|
32577
|
-
const result = await runBunInstallWithDetails();
|
|
32578
|
-
return result.success;
|
|
32579
|
-
}
|
|
32580
|
-
async function runBunInstallWithDetails() {
|
|
32581
|
-
try {
|
|
32582
|
-
const proc = Bun.spawn(["bun", "install"], {
|
|
32583
|
-
cwd: getConfigDir(),
|
|
32584
|
-
stdout: "inherit",
|
|
32585
|
-
stderr: "inherit"
|
|
32586
|
-
});
|
|
32587
|
-
let timeoutId;
|
|
32588
|
-
const timeoutPromise = new Promise((resolve3) => {
|
|
32589
|
-
timeoutId = setTimeout(() => resolve3("timeout"), BUN_INSTALL_TIMEOUT_MS);
|
|
32590
|
-
});
|
|
32591
|
-
const exitPromise = proc.exited.then(() => "completed");
|
|
32592
|
-
const result = await Promise.race([exitPromise, timeoutPromise]);
|
|
32593
|
-
clearTimeout(timeoutId);
|
|
32594
|
-
if (result === "timeout") {
|
|
32595
|
-
try {
|
|
32596
|
-
proc.kill();
|
|
32597
|
-
} catch {}
|
|
32598
|
-
return {
|
|
32599
|
-
success: false,
|
|
32600
|
-
timedOut: true,
|
|
32601
|
-
error: `bun install timed out after ${BUN_INSTALL_TIMEOUT_SECONDS} seconds. Try running manually: cd ${getConfigDir()} && bun i`
|
|
32602
|
-
};
|
|
32603
|
-
}
|
|
32604
|
-
if (proc.exitCode !== 0) {
|
|
32605
|
-
return {
|
|
32606
|
-
success: false,
|
|
32607
|
-
error: `bun install failed with exit code ${proc.exitCode}`
|
|
32608
|
-
};
|
|
32609
|
-
}
|
|
32610
|
-
return { success: true };
|
|
32611
|
-
} catch (err) {
|
|
32612
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
32613
|
-
return {
|
|
32614
|
-
success: false,
|
|
32615
|
-
error: `bun install failed: ${message}. Is bun installed? Try: curl -fsSL https://bun.sh/install | bash`
|
|
32616
|
-
};
|
|
32617
|
-
}
|
|
32618
|
-
}
|
|
32619
33296
|
// src/hooks/auto-update-checker/hook/background-update-check.ts
|
|
32620
33297
|
init_logger();
|
|
32621
33298
|
|
|
@@ -32629,7 +33306,9 @@ async function showUpdateAvailableToast(ctx, latestVersion, getToastMessage) {
|
|
|
32629
33306
|
variant: "info",
|
|
32630
33307
|
duration: 8000
|
|
32631
33308
|
}
|
|
32632
|
-
}).catch(() => {
|
|
33309
|
+
}).catch((err) => {
|
|
33310
|
+
log("[auto-update] Update available toast failed:", err);
|
|
33311
|
+
});
|
|
32633
33312
|
log(`[auto-update-checker] Update available toast shown: v${latestVersion}`);
|
|
32634
33313
|
}
|
|
32635
33314
|
async function showAutoUpdatedToast(ctx, oldVersion, newVersion) {
|
|
@@ -32641,14 +33320,36 @@ Restart OpenCode to apply.`,
|
|
|
32641
33320
|
variant: "success",
|
|
32642
33321
|
duration: 8000
|
|
32643
33322
|
}
|
|
32644
|
-
}).catch(() => {
|
|
33323
|
+
}).catch((err) => {
|
|
33324
|
+
log("[auto-update] Auto-updated toast failed:", err);
|
|
33325
|
+
});
|
|
32645
33326
|
log(`[auto-update-checker] Auto-updated toast shown: v${oldVersion} \u2192 v${newVersion}`);
|
|
32646
33327
|
}
|
|
32647
33328
|
|
|
32648
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;
|
|
32649
33332
|
async function runBunInstallSafe() {
|
|
32650
33333
|
try {
|
|
32651
|
-
|
|
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;
|
|
32652
33353
|
} catch (err) {
|
|
32653
33354
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
32654
33355
|
log("[auto-update-checker] bun install error:", errorMessage);
|
|
@@ -32719,7 +33420,9 @@ ${errorMessages}`,
|
|
|
32719
33420
|
variant: "error",
|
|
32720
33421
|
duration: 1e4
|
|
32721
33422
|
}
|
|
32722
|
-
}).catch(() => {
|
|
33423
|
+
}).catch((err) => {
|
|
33424
|
+
log("[auto-update] Config errors toast failed:", err);
|
|
33425
|
+
});
|
|
32723
33426
|
log(`[auto-update-checker] Config load errors shown: ${errors.length} error(s)`);
|
|
32724
33427
|
clearConfigLoadErrors();
|
|
32725
33428
|
}
|
|
@@ -32728,7 +33431,9 @@ ${errorMessages}`,
|
|
|
32728
33431
|
init_logger();
|
|
32729
33432
|
async function updateAndShowConnectedProvidersCacheStatus(ctx) {
|
|
32730
33433
|
const hadCache = hasConnectedProvidersCache();
|
|
32731
|
-
updateConnectedProvidersCache(ctx.client).catch(() => {
|
|
33434
|
+
updateConnectedProvidersCache(ctx.client).catch((err) => {
|
|
33435
|
+
log("[auto-update] Provider cache update failed:", err);
|
|
33436
|
+
});
|
|
32732
33437
|
if (!hadCache) {
|
|
32733
33438
|
await ctx.client.tui.showToast({
|
|
32734
33439
|
body: {
|
|
@@ -32737,7 +33442,9 @@ async function updateAndShowConnectedProvidersCacheStatus(ctx) {
|
|
|
32737
33442
|
variant: "info",
|
|
32738
33443
|
duration: 8000
|
|
32739
33444
|
}
|
|
32740
|
-
}).catch(() => {
|
|
33445
|
+
}).catch((err) => {
|
|
33446
|
+
log("[auto-update] Providers toast failed:", err);
|
|
33447
|
+
});
|
|
32741
33448
|
log("[auto-update-checker] Connected providers cache toast shown (first run)");
|
|
32742
33449
|
} else {
|
|
32743
33450
|
log("[auto-update-checker] Connected providers cache exists, updating in background");
|
|
@@ -32756,7 +33463,9 @@ async function showModelCacheWarningIfNeeded(ctx) {
|
|
|
32756
33463
|
variant: "warning",
|
|
32757
33464
|
duration: 1e4
|
|
32758
33465
|
}
|
|
32759
|
-
}).catch(() => {
|
|
33466
|
+
}).catch((err) => {
|
|
33467
|
+
log("[auto-update] Model cache warning toast failed:", err);
|
|
33468
|
+
});
|
|
32760
33469
|
log("[auto-update-checker] Model cache warning shown");
|
|
32761
33470
|
}
|
|
32762
33471
|
|
|
@@ -32764,6 +33473,7 @@ async function showModelCacheWarningIfNeeded(ctx) {
|
|
|
32764
33473
|
init_logger();
|
|
32765
33474
|
|
|
32766
33475
|
// src/hooks/auto-update-checker/hook/spinner-toast.ts
|
|
33476
|
+
init_logger();
|
|
32767
33477
|
var SISYPHUS_SPINNER = ["\xB7", "\u2022", "\u25CF", "\u25CB", "\u25CC", "\u25E6", " "];
|
|
32768
33478
|
async function showSpinnerToast(ctx, version, message) {
|
|
32769
33479
|
const totalDuration = 5000;
|
|
@@ -32778,7 +33488,9 @@ async function showSpinnerToast(ctx, version, message) {
|
|
|
32778
33488
|
variant: "info",
|
|
32779
33489
|
duration: frameInterval + 50
|
|
32780
33490
|
}
|
|
32781
|
-
}).catch(() => {
|
|
33491
|
+
}).catch((err) => {
|
|
33492
|
+
log("[auto-update] Spinner toast failed:", err);
|
|
33493
|
+
});
|
|
32782
33494
|
await new Promise((resolve3) => setTimeout(resolve3, frameInterval));
|
|
32783
33495
|
}
|
|
32784
33496
|
}
|
|
@@ -32830,13 +33542,17 @@ v${latestVersion} available. Restart OpenCode to apply.` : "OpenCode is now on S
|
|
|
32830
33542
|
await updateAndShowConnectedProvidersCacheStatus(ctx);
|
|
32831
33543
|
if (localDevVersion) {
|
|
32832
33544
|
if (showStartupToast) {
|
|
32833
|
-
showLocalDevToast(ctx, displayVersion, isMorpheusEnabled).catch(() => {
|
|
33545
|
+
showLocalDevToast(ctx, displayVersion, isMorpheusEnabled).catch((err) => {
|
|
33546
|
+
log("[auto-update] Startup toast failed:", err);
|
|
33547
|
+
});
|
|
32834
33548
|
}
|
|
32835
33549
|
log("[auto-update-checker] Local development mode");
|
|
32836
33550
|
return;
|
|
32837
33551
|
}
|
|
32838
33552
|
if (showStartupToast) {
|
|
32839
|
-
showVersionToast(ctx, displayVersion, getToastMessage(false)).catch(() => {
|
|
33553
|
+
showVersionToast(ctx, displayVersion, getToastMessage(false)).catch((err) => {
|
|
33554
|
+
log("[auto-update] Version toast failed:", err);
|
|
33555
|
+
});
|
|
32840
33556
|
}
|
|
32841
33557
|
runBackgroundUpdateCheck(ctx, autoUpdate, getToastMessage).catch((err) => {
|
|
32842
33558
|
log("[auto-update-checker] Background update check failed:", err);
|
|
@@ -48741,9 +49457,6 @@ function cleanupStaleEntries() {
|
|
|
48741
49457
|
}
|
|
48742
49458
|
}
|
|
48743
49459
|
}
|
|
48744
|
-
function isWriteTool(toolName) {
|
|
48745
|
-
return toolName.toLowerCase() === "write";
|
|
48746
|
-
}
|
|
48747
49460
|
function extractFilePath(args) {
|
|
48748
49461
|
const path10 = args.path ?? args.filePath ?? args.file_path;
|
|
48749
49462
|
return typeof path10 === "string" ? path10 : undefined;
|
|
@@ -48815,12 +49528,6 @@ var CONTENT_CLOSE_TAG = "</content>";
|
|
|
48815
49528
|
var FILE_OPEN_TAG = "<file>";
|
|
48816
49529
|
var FILE_CLOSE_TAG = "</file>";
|
|
48817
49530
|
var OPENCODE_LINE_TRUNCATION_SUFFIX = "... (line truncated to 2000 chars)";
|
|
48818
|
-
function isReadTool(toolName) {
|
|
48819
|
-
return toolName.toLowerCase() === "read";
|
|
48820
|
-
}
|
|
48821
|
-
function isWriteTool2(toolName) {
|
|
48822
|
-
return toolName.toLowerCase() === "write";
|
|
48823
|
-
}
|
|
48824
49531
|
function shouldProcess(config2) {
|
|
48825
49532
|
return config2.hashline_edit?.enabled ?? false;
|
|
48826
49533
|
}
|
|
@@ -48941,7 +49648,7 @@ function createHashlineReadEnhancerHook(_ctx, config2) {
|
|
|
48941
49648
|
return {
|
|
48942
49649
|
"tool.execute.after": async (input, output) => {
|
|
48943
49650
|
if (!isReadTool(input.tool)) {
|
|
48944
|
-
if (
|
|
49651
|
+
if (isWriteTool(input.tool) && typeof output.output === "string" && shouldProcess(config2)) {
|
|
48945
49652
|
await appendWriteHashlineOutput(output);
|
|
48946
49653
|
}
|
|
48947
49654
|
return;
|
|
@@ -48956,6 +49663,9 @@ function createHashlineReadEnhancerHook(_ctx, config2) {
|
|
|
48956
49663
|
}
|
|
48957
49664
|
};
|
|
48958
49665
|
}
|
|
49666
|
+
// src/hooks/interactive-bash-session/hook.ts
|
|
49667
|
+
init_logger();
|
|
49668
|
+
|
|
48959
49669
|
// src/hooks/interactive-bash-session/constants.ts
|
|
48960
49670
|
import { join as join46 } from "path";
|
|
48961
49671
|
var INTERACTIVE_BASH_SESSION_STORAGE = join46(OPENCODE_STORAGE, "interactive-bash-session");
|
|
@@ -49135,7 +49845,9 @@ function createInteractiveBashSessionHook(ctx) {
|
|
|
49135
49845
|
async function killAllTrackedSessionsLocal(state3) {
|
|
49136
49846
|
await killAllTrackedSessions(state3);
|
|
49137
49847
|
for (const sessionId of subagentSessions) {
|
|
49138
|
-
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
|
+
});
|
|
49139
49851
|
}
|
|
49140
49852
|
}
|
|
49141
49853
|
const toolExecuteAfter = async (input, output) => {
|
|
@@ -49964,6 +50676,21 @@ function detectKeywordsWithType(text, agentName, modelID) {
|
|
|
49964
50676
|
function extractPromptText3(parts) {
|
|
49965
50677
|
return parts.filter((p) => p.type === "text").map((p) => p.text || "").join(" ");
|
|
49966
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
|
+
}
|
|
49967
50694
|
// src/hooks/keyword-detector/hook.ts
|
|
49968
50695
|
function createKeywordDetectorHook(ctx, _collector) {
|
|
49969
50696
|
return {
|
|
@@ -49999,8 +50726,15 @@ function createKeywordDetectorHook(ctx, _collector) {
|
|
|
49999
50726
|
return;
|
|
50000
50727
|
}
|
|
50001
50728
|
}
|
|
50729
|
+
const ultraworkState = getUltraworkState(input.sessionID);
|
|
50002
50730
|
const hasUltrawork = detectedKeywords.some((k) => k.type === "ultrawork");
|
|
50003
|
-
|
|
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) {
|
|
50004
50738
|
log(`[keyword-detector] Ultrawork mode activated`, { sessionID: input.sessionID });
|
|
50005
50739
|
if (output.message.variant === undefined) {
|
|
50006
50740
|
output.message.variant = "max";
|
|
@@ -50088,7 +50822,7 @@ function createLoopStateController(options) {
|
|
|
50088
50822
|
};
|
|
50089
50823
|
const success2 = writeState(directory, state3, stateDir);
|
|
50090
50824
|
if (success2) {
|
|
50091
|
-
log(`[${
|
|
50825
|
+
log(`[${HOOK_NAME5}] Loop started`, {
|
|
50092
50826
|
sessionID,
|
|
50093
50827
|
maxIterations: state3.max_iterations,
|
|
50094
50828
|
completionPromise: state3.completion_promise
|
|
@@ -50103,7 +50837,7 @@ function createLoopStateController(options) {
|
|
|
50103
50837
|
}
|
|
50104
50838
|
const success2 = clearState(directory, stateDir);
|
|
50105
50839
|
if (success2) {
|
|
50106
|
-
log(`[${
|
|
50840
|
+
log(`[${HOOK_NAME5}] Loop cancelled`, { sessionID, iteration: state3.iteration });
|
|
50107
50841
|
}
|
|
50108
50842
|
return success2;
|
|
50109
50843
|
},
|
|
@@ -50199,7 +50933,7 @@ async function detectCompletionInSessionMessages(ctx, options) {
|
|
|
50199
50933
|
return false;
|
|
50200
50934
|
} catch (err) {
|
|
50201
50935
|
setTimeout(() => {
|
|
50202
|
-
log(`[${
|
|
50936
|
+
log(`[${HOOK_NAME5}] Session messages check failed`, {
|
|
50203
50937
|
sessionID: options.sessionID,
|
|
50204
50938
|
error: String(err)
|
|
50205
50939
|
});
|
|
@@ -50280,7 +51014,7 @@ async function verifyCompletion(ctx, options) {
|
|
|
50280
51014
|
query: { directory: options.directory }
|
|
50281
51015
|
});
|
|
50282
51016
|
if (createResult.error || !createResult.data?.id) {
|
|
50283
|
-
log(`[${
|
|
51017
|
+
log(`[${HOOK_NAME5}] Verification session creation failed`);
|
|
50284
51018
|
return { verified: true };
|
|
50285
51019
|
}
|
|
50286
51020
|
verifySessionID = createResult.data.id;
|
|
@@ -50310,20 +51044,22 @@ async function verifyCompletion(ctx, options) {
|
|
|
50310
51044
|
return parseVerificationResponse(assistantText);
|
|
50311
51045
|
}
|
|
50312
51046
|
} catch {
|
|
50313
|
-
log(`[${
|
|
51047
|
+
log(`[${HOOK_NAME5}] Verification poll error`);
|
|
50314
51048
|
}
|
|
50315
51049
|
await new Promise((resolve5) => {
|
|
50316
51050
|
setTimeout(resolve5, pollIntervalMs);
|
|
50317
51051
|
});
|
|
50318
51052
|
}
|
|
50319
|
-
log(`[${
|
|
51053
|
+
log(`[${HOOK_NAME5}] Verification timed out, assuming pass`);
|
|
50320
51054
|
return { verified: true };
|
|
50321
51055
|
} catch (err) {
|
|
50322
|
-
log(`[${
|
|
51056
|
+
log(`[${HOOK_NAME5}] Verification error: ${String(err)}`);
|
|
50323
51057
|
return { verified: true };
|
|
50324
51058
|
} finally {
|
|
50325
51059
|
if (verifySessionID) {
|
|
50326
|
-
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
|
+
});
|
|
50327
51063
|
}
|
|
50328
51064
|
}
|
|
50329
51065
|
}
|
|
@@ -50393,7 +51129,7 @@ async function injectContinuationPrompt(ctx, options) {
|
|
|
50393
51129
|
|
|
50394
51130
|
// src/hooks/matrix-loop/matrix-loop-event-handler.ts
|
|
50395
51131
|
async function handleVerificationFailure(ctx, options, state3, sessionID, reason, failedCount) {
|
|
50396
|
-
log(`[${
|
|
51132
|
+
log(`[${HOOK_NAME5}] Verification failed`, { sessionID, reason });
|
|
50397
51133
|
const newState = options.loopState.incrementIteration();
|
|
50398
51134
|
if (newState) {
|
|
50399
51135
|
newState.verification_failed_count = failedCount + 1;
|
|
@@ -50411,7 +51147,7 @@ ${buildContinuationPrompt(newState ?? state3)}`,
|
|
|
50411
51147
|
apiTimeoutMs: options.apiTimeoutMs
|
|
50412
51148
|
});
|
|
50413
51149
|
} catch (err) {
|
|
50414
|
-
log(`[${
|
|
51150
|
+
log(`[${HOOK_NAME5}] Failed to inject verification feedback`, { sessionID, error: String(err) });
|
|
50415
51151
|
}
|
|
50416
51152
|
}
|
|
50417
51153
|
function createMatrixLoopEventHandler(ctx, options) {
|
|
@@ -50423,13 +51159,13 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50423
51159
|
if (!sessionID)
|
|
50424
51160
|
return;
|
|
50425
51161
|
if (inFlightSessions.has(sessionID)) {
|
|
50426
|
-
log(`[${
|
|
51162
|
+
log(`[${HOOK_NAME5}] Skipped: handler in flight`, { sessionID });
|
|
50427
51163
|
return;
|
|
50428
51164
|
}
|
|
50429
51165
|
inFlightSessions.add(sessionID);
|
|
50430
51166
|
try {
|
|
50431
51167
|
if (options.sessionRecovery.isRecovering(sessionID)) {
|
|
50432
|
-
log(`[${
|
|
51168
|
+
log(`[${HOOK_NAME5}] Skipped: in recovery`, { sessionID });
|
|
50433
51169
|
return;
|
|
50434
51170
|
}
|
|
50435
51171
|
const state3 = options.loopState.getState();
|
|
@@ -50441,11 +51177,11 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50441
51177
|
const exists = await options.checkSessionExists(state3.session_id);
|
|
50442
51178
|
if (!exists) {
|
|
50443
51179
|
options.loopState.clear();
|
|
50444
|
-
log(`[${
|
|
51180
|
+
log(`[${HOOK_NAME5}] Cleared orphaned state from deleted session`, { orphanedSessionId: state3.session_id, currentSessionId: sessionID });
|
|
50445
51181
|
return;
|
|
50446
51182
|
}
|
|
50447
51183
|
} catch (err) {
|
|
50448
|
-
log(`[${
|
|
51184
|
+
log(`[${HOOK_NAME5}] Failed to check session existence`, { sessionId: state3.session_id, error: String(err) });
|
|
50449
51185
|
}
|
|
50450
51186
|
}
|
|
50451
51187
|
return;
|
|
@@ -50464,7 +51200,7 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50464
51200
|
messages = Array.isArray(messagesResponse) ? messagesResponse : Array.isArray(responseData) ? responseData : [];
|
|
50465
51201
|
} catch (err) {
|
|
50466
51202
|
setTimeout(() => {
|
|
50467
|
-
log(`[${
|
|
51203
|
+
log(`[${HOOK_NAME5}] Session messages fetch failed`, { sessionID, error: String(err) });
|
|
50468
51204
|
}, 0);
|
|
50469
51205
|
messages = [];
|
|
50470
51206
|
}
|
|
@@ -50477,13 +51213,15 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50477
51213
|
preFetchedMessages: messages
|
|
50478
51214
|
});
|
|
50479
51215
|
if (completionViaTranscript || completionViaApi) {
|
|
50480
|
-
log(`[${
|
|
51216
|
+
log(`[${HOOK_NAME5}] Completion detected!`, { sessionID, iteration: state3.iteration, promise: state3.completion_promise, detectedVia: completionViaTranscript ? "transcript_file" : "session_messages_api" });
|
|
50481
51217
|
if (options.verification?.enabled === true) {
|
|
50482
51218
|
const maxRetries = options.verification?.maxRetries ?? DEFAULT_VERIFICATION_MAX_RETRIES;
|
|
50483
51219
|
const failedCount = state3.verification_failed_count ?? 0;
|
|
50484
51220
|
if (failedCount >= maxRetries) {
|
|
50485
51221
|
options.loopState.clear();
|
|
50486
|
-
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
|
+
});
|
|
50487
51225
|
return;
|
|
50488
51226
|
}
|
|
50489
51227
|
const result = await verifyCompletion(ctx, {
|
|
@@ -50504,26 +51242,32 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50504
51242
|
options.loopState.clear();
|
|
50505
51243
|
const title = state3.ultrawork ? "ULTRAWORK LOOP COMPLETE!" : "Matrix Loop Complete!";
|
|
50506
51244
|
const message = state3.ultrawork ? `JUST ULW ULW! Task completed after ${state3.iteration} iteration(s)` : `Task completed after ${state3.iteration} iteration(s)`;
|
|
50507
|
-
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
|
+
});
|
|
50508
51248
|
return;
|
|
50509
51249
|
}
|
|
50510
51250
|
if (state3.iteration >= state3.max_iterations) {
|
|
50511
|
-
log(`[${
|
|
51251
|
+
log(`[${HOOK_NAME5}] Max iterations reached`, { sessionID, iteration: state3.iteration, max: state3.max_iterations });
|
|
50512
51252
|
options.loopState.clear();
|
|
50513
|
-
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
|
+
});
|
|
50514
51256
|
return;
|
|
50515
51257
|
}
|
|
50516
51258
|
const newState = options.loopState.incrementIteration();
|
|
50517
51259
|
if (!newState) {
|
|
50518
|
-
log(`[${
|
|
51260
|
+
log(`[${HOOK_NAME5}] Failed to increment iteration`, { sessionID });
|
|
50519
51261
|
return;
|
|
50520
51262
|
}
|
|
50521
|
-
log(`[${
|
|
50522
|
-
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
|
+
});
|
|
50523
51267
|
try {
|
|
50524
51268
|
await injectContinuationPrompt(ctx, { sessionID, prompt: buildContinuationPrompt(newState), directory: options.directory, apiTimeoutMs: options.apiTimeoutMs });
|
|
50525
51269
|
} catch (err) {
|
|
50526
|
-
log(`[${
|
|
51270
|
+
log(`[${HOOK_NAME5}] Failed to inject continuation`, { sessionID, error: String(err) });
|
|
50527
51271
|
}
|
|
50528
51272
|
return;
|
|
50529
51273
|
} finally {
|
|
@@ -50537,7 +51281,7 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50537
51281
|
const state3 = options.loopState.getState();
|
|
50538
51282
|
if (state3?.session_id === sessionInfo.id) {
|
|
50539
51283
|
options.loopState.clear();
|
|
50540
|
-
log(`[${
|
|
51284
|
+
log(`[${HOOK_NAME5}] Session deleted, loop cleared`, { sessionID: sessionInfo.id });
|
|
50541
51285
|
}
|
|
50542
51286
|
options.sessionRecovery.clear(sessionInfo.id);
|
|
50543
51287
|
return;
|
|
@@ -50550,7 +51294,7 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
50550
51294
|
const state3 = options.loopState.getState();
|
|
50551
51295
|
if (state3?.session_id === sessionID) {
|
|
50552
51296
|
options.loopState.clear();
|
|
50553
|
-
log(`[${
|
|
51297
|
+
log(`[${HOOK_NAME5}] User aborted, loop cleared`, { sessionID });
|
|
50554
51298
|
}
|
|
50555
51299
|
options.sessionRecovery.clear(sessionID);
|
|
50556
51300
|
}
|
|
@@ -50597,7 +51341,7 @@ function createMatrixLoopHook(ctx, options) {
|
|
|
50597
51341
|
// src/hooks/matrix-loop/index.ts
|
|
50598
51342
|
init_storage();
|
|
50599
51343
|
// src/hooks/mouse-notepad/constants.ts
|
|
50600
|
-
var
|
|
51344
|
+
var HOOK_NAME6 = "mouse-notepad";
|
|
50601
51345
|
var NOTEPAD_DIRECTIVE = `
|
|
50602
51346
|
<Work_Context>
|
|
50603
51347
|
## Notepad Location (for recording learnings)
|
|
@@ -50644,14 +51388,14 @@ function createMouseNotepadHook(ctx) {
|
|
|
50644
51388
|
return;
|
|
50645
51389
|
}
|
|
50646
51390
|
output.args.prompt = NOTEPAD_DIRECTIVE + prompt;
|
|
50647
|
-
log(`[${
|
|
51391
|
+
log(`[${HOOK_NAME6}] Injected notepad directive to task`, {
|
|
50648
51392
|
sessionID: input.sessionID
|
|
50649
51393
|
});
|
|
50650
51394
|
}
|
|
50651
51395
|
};
|
|
50652
51396
|
}
|
|
50653
51397
|
// src/hooks/non-interactive-env/constants.ts
|
|
50654
|
-
var
|
|
51398
|
+
var HOOK_NAME7 = "non-interactive-env";
|
|
50655
51399
|
var NON_INTERACTIVE_ENV = {
|
|
50656
51400
|
CI: "true",
|
|
50657
51401
|
DEBIAN_FRONTEND: "noninteractive",
|
|
@@ -50742,7 +51486,7 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
50742
51486
|
}
|
|
50743
51487
|
const envPrefix = buildEnvPrefix(NON_INTERACTIVE_ENV, "unix");
|
|
50744
51488
|
output.args.command = `${envPrefix} ${command}`;
|
|
50745
|
-
log(`[${
|
|
51489
|
+
log(`[${HOOK_NAME7}] Prepended non-interactive env vars to git command`, {
|
|
50746
51490
|
sessionID: input.sessionID,
|
|
50747
51491
|
envPrefix
|
|
50748
51492
|
});
|
|
@@ -50891,7 +51635,9 @@ function createPostCompactionDegradationMonitor(args) {
|
|
|
50891
51635
|
variant: "warning",
|
|
50892
51636
|
duration: 5000
|
|
50893
51637
|
}
|
|
50894
|
-
}).catch(() => {
|
|
51638
|
+
}).catch((err) => {
|
|
51639
|
+
log("[preemptive-compaction] Degradation toast failed:", err);
|
|
51640
|
+
});
|
|
50895
51641
|
await withTimeout2(client.session.summarize({
|
|
50896
51642
|
path: { id: sessionID },
|
|
50897
51643
|
body: { providerID: targetProviderID, modelID: targetModelID },
|
|
@@ -51129,7 +51875,7 @@ function createPreemptiveCompactionHook(ctx, pluginConfig = {}, modelCacheState)
|
|
|
51129
51875
|
};
|
|
51130
51876
|
}
|
|
51131
51877
|
// src/hooks/prometheus-md-only/constants.ts
|
|
51132
|
-
var
|
|
51878
|
+
var HOOK_NAME8 = "prometheus-md-only";
|
|
51133
51879
|
var ORACLE_AGENT = "oracle";
|
|
51134
51880
|
var ALLOWED_EXTENSIONS = [".md"];
|
|
51135
51881
|
var BLOCKED_TOOLS = ["Write", "Edit", "write", "edit"];
|
|
@@ -51262,7 +52008,7 @@ function createOracleMdOnlyHook(ctx) {
|
|
|
51262
52008
|
const prompt = output.args.prompt;
|
|
51263
52009
|
if (prompt && !prompt.includes(SYSTEM_DIRECTIVE_PREFIX)) {
|
|
51264
52010
|
output.args.prompt = PLANNING_CONSULT_WARNING + prompt;
|
|
51265
|
-
log(`[${
|
|
52011
|
+
log(`[${HOOK_NAME8}] Injected read-only planning warning to ${toolName}`, {
|
|
51266
52012
|
sessionID: input.sessionID,
|
|
51267
52013
|
tool: toolName,
|
|
51268
52014
|
agent: agentName
|
|
@@ -51278,17 +52024,17 @@ function createOracleMdOnlyHook(ctx) {
|
|
|
51278
52024
|
return;
|
|
51279
52025
|
}
|
|
51280
52026
|
if (!isAllowedFile(filePath, ctx.directory)) {
|
|
51281
|
-
log(`[${
|
|
52027
|
+
log(`[${HOOK_NAME8}] Blocked: Oracle can only write to .matrixx/*.md`, {
|
|
51282
52028
|
sessionID: input.sessionID,
|
|
51283
52029
|
tool: toolName,
|
|
51284
52030
|
filePath,
|
|
51285
52031
|
agent: agentName
|
|
51286
52032
|
});
|
|
51287
|
-
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`);
|
|
51288
52034
|
}
|
|
51289
52035
|
const normalizedPath = filePath.toLowerCase().replace(/\\/g, "/");
|
|
51290
52036
|
if (normalizedPath.includes(".matrixx/plans/") || normalizedPath.includes(".matrixx\\plans\\")) {
|
|
51291
|
-
log(`[${
|
|
52037
|
+
log(`[${HOOK_NAME8}] Injecting workflow reminder for plan write`, {
|
|
51292
52038
|
sessionID: input.sessionID,
|
|
51293
52039
|
tool: toolName,
|
|
51294
52040
|
filePath,
|
|
@@ -51296,7 +52042,7 @@ function createOracleMdOnlyHook(ctx) {
|
|
|
51296
52042
|
});
|
|
51297
52043
|
output.message = (output.message || "") + PROMETHEUS_WORKFLOW_REMINDER;
|
|
51298
52044
|
}
|
|
51299
|
-
log(`[${
|
|
52045
|
+
log(`[${HOOK_NAME8}] Allowed: .matrixx/*.md write permitted`, {
|
|
51300
52046
|
sessionID: input.sessionID,
|
|
51301
52047
|
tool: toolName,
|
|
51302
52048
|
filePath,
|
|
@@ -52029,9 +52775,6 @@ async function resizeImage(base64DataUrl, mimeType, target, deps = {}) {
|
|
|
52029
52775
|
// src/hooks/read-image-resizer/hook.ts
|
|
52030
52776
|
var SUPPORTED_IMAGE_MIMES = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
|
|
52031
52777
|
var TOKEN_DIVISOR = 750;
|
|
52032
|
-
function isReadTool2(toolName) {
|
|
52033
|
-
return toolName.toLowerCase() === "read";
|
|
52034
|
-
}
|
|
52035
52778
|
function asRecord(value) {
|
|
52036
52779
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
52037
52780
|
return null;
|
|
@@ -52115,7 +52858,7 @@ function resolveFilename(attachment, index) {
|
|
|
52115
52858
|
function createReadImageResizerHook(_ctx) {
|
|
52116
52859
|
return {
|
|
52117
52860
|
"tool.execute.after": async (input, output) => {
|
|
52118
|
-
if (!
|
|
52861
|
+
if (!isReadTool(input.tool)) {
|
|
52119
52862
|
return;
|
|
52120
52863
|
}
|
|
52121
52864
|
const sessionModel = getSessionModel(input.sessionID);
|
|
@@ -52852,7 +53595,7 @@ var RETRYABLE_ERROR_PATTERNS = [
|
|
|
52852
53595
|
/(?:^|\s)503(?:\s|$)/,
|
|
52853
53596
|
/(?:^|\s)529(?:\s|$)/
|
|
52854
53597
|
];
|
|
52855
|
-
var
|
|
53598
|
+
var HOOK_NAME9 = "runtime-fallback";
|
|
52856
53599
|
|
|
52857
53600
|
// src/hooks/runtime-fallback/fallback-models.ts
|
|
52858
53601
|
init_logger();
|
|
@@ -52943,7 +53686,7 @@ function getRawFallbackModelsForSession(sessionID, agent, pluginConfig) {
|
|
|
52943
53686
|
if (result)
|
|
52944
53687
|
return result;
|
|
52945
53688
|
}
|
|
52946
|
-
log(`[${
|
|
53689
|
+
log(`[${HOOK_NAME9}] No category/agent fallback models resolved for session`, { sessionID, agent });
|
|
52947
53690
|
return;
|
|
52948
53691
|
}
|
|
52949
53692
|
|
|
@@ -52972,21 +53715,21 @@ function findNextAvailableFallback(state3, fallbackModels, cooldownSeconds) {
|
|
|
52972
53715
|
if (!isModelInCooldown(candidate, state3, cooldownSeconds)) {
|
|
52973
53716
|
return candidate;
|
|
52974
53717
|
}
|
|
52975
|
-
log(`[${
|
|
53718
|
+
log(`[${HOOK_NAME9}] Skipping fallback model in cooldown`, { model: candidate, index: i });
|
|
52976
53719
|
}
|
|
52977
53720
|
return;
|
|
52978
53721
|
}
|
|
52979
53722
|
function prepareFallback(sessionID, state3, fallbackModels, config2) {
|
|
52980
53723
|
if (state3.attemptCount >= config2.max_fallback_attempts) {
|
|
52981
|
-
log(`[${
|
|
53724
|
+
log(`[${HOOK_NAME9}] Max fallback attempts reached`, { sessionID, attempts: state3.attemptCount });
|
|
52982
53725
|
return { success: false, error: "Max fallback attempts reached", maxAttemptsReached: true };
|
|
52983
53726
|
}
|
|
52984
53727
|
const nextModel = findNextAvailableFallback(state3, fallbackModels, config2.cooldown_seconds);
|
|
52985
53728
|
if (!nextModel) {
|
|
52986
|
-
log(`[${
|
|
53729
|
+
log(`[${HOOK_NAME9}] No available fallback models`, { sessionID });
|
|
52987
53730
|
return { success: false, error: "No available fallback models (all in cooldown or exhausted)" };
|
|
52988
53731
|
}
|
|
52989
|
-
log(`[${
|
|
53732
|
+
log(`[${HOOK_NAME9}] Preparing fallback`, {
|
|
52990
53733
|
sessionID,
|
|
52991
53734
|
from: state3.currentModel,
|
|
52992
53735
|
to: nextModel,
|
|
@@ -53064,7 +53807,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53064
53807
|
ctx,
|
|
53065
53808
|
config: config2,
|
|
53066
53809
|
options,
|
|
53067
|
-
sessionStates,
|
|
53810
|
+
sessionStates: sessionStates2,
|
|
53068
53811
|
sessionLastAccess,
|
|
53069
53812
|
sessionRetryInFlight,
|
|
53070
53813
|
sessionAwaitingFallbackResult,
|
|
@@ -53075,9 +53818,9 @@ function createAutoRetryHelpers(deps) {
|
|
|
53075
53818
|
const abortSessionRequest = async (sessionID, source) => {
|
|
53076
53819
|
try {
|
|
53077
53820
|
await ctx.client.session.abort({ path: { id: sessionID } });
|
|
53078
|
-
log(`[${
|
|
53821
|
+
log(`[${HOOK_NAME9}] Aborted in-flight session request (${source})`, { sessionID });
|
|
53079
53822
|
} catch (error51) {
|
|
53080
|
-
log(`[${
|
|
53823
|
+
log(`[${HOOK_NAME9}] Failed to abort in-flight session request (${source})`, {
|
|
53081
53824
|
sessionID,
|
|
53082
53825
|
error: String(error51)
|
|
53083
53826
|
});
|
|
@@ -53097,11 +53840,11 @@ function createAutoRetryHelpers(deps) {
|
|
|
53097
53840
|
return;
|
|
53098
53841
|
const timer = setTimeout(async () => {
|
|
53099
53842
|
sessionFallbackTimeouts.delete(sessionID);
|
|
53100
|
-
const state3 =
|
|
53843
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53101
53844
|
if (!state3)
|
|
53102
53845
|
return;
|
|
53103
53846
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
53104
|
-
log(`[${
|
|
53847
|
+
log(`[${HOOK_NAME9}] Overriding in-flight retry due to session timeout`, { sessionID });
|
|
53105
53848
|
}
|
|
53106
53849
|
await abortSessionRequest(sessionID, "session.timeout");
|
|
53107
53850
|
sessionRetryInFlight.delete(sessionID);
|
|
@@ -53111,7 +53854,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53111
53854
|
const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
|
|
53112
53855
|
if (fallbackModels.length === 0)
|
|
53113
53856
|
return;
|
|
53114
|
-
log(`[${
|
|
53857
|
+
log(`[${HOOK_NAME9}] Session fallback timeout reached`, {
|
|
53115
53858
|
sessionID,
|
|
53116
53859
|
timeoutSeconds: config2.timeout_seconds,
|
|
53117
53860
|
currentModel: state3.currentModel
|
|
@@ -53125,7 +53868,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53125
53868
|
};
|
|
53126
53869
|
const autoRetryWithFallback = async (sessionID, newModel, resolvedAgent, source) => {
|
|
53127
53870
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
53128
|
-
log(`[${
|
|
53871
|
+
log(`[${HOOK_NAME9}] Retry already in flight, skipping (${source})`, { sessionID });
|
|
53129
53872
|
return;
|
|
53130
53873
|
}
|
|
53131
53874
|
const agentSettings = resolvedAgent ? pluginConfig?.agents?.[resolvedAgent] : undefined;
|
|
@@ -53134,8 +53877,8 @@ function createAutoRetryHelpers(deps) {
|
|
|
53134
53877
|
reasoningEffort: agentSettings.reasoningEffort
|
|
53135
53878
|
} : undefined);
|
|
53136
53879
|
if (!retryModelPayload) {
|
|
53137
|
-
log(`[${
|
|
53138
|
-
const state3 =
|
|
53880
|
+
log(`[${HOOK_NAME9}] Invalid model format (missing provider prefix): ${newModel}`);
|
|
53881
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53139
53882
|
if (state3?.pendingFallbackModel) {
|
|
53140
53883
|
state3.pendingFallbackModel = undefined;
|
|
53141
53884
|
}
|
|
@@ -53150,7 +53893,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53150
53893
|
});
|
|
53151
53894
|
const retryParts = getLastUserRetryParts(messagesResp);
|
|
53152
53895
|
if (retryParts.length > 0) {
|
|
53153
|
-
log(`[${
|
|
53896
|
+
log(`[${HOOK_NAME9}] Auto-retrying with fallback model (${source})`, {
|
|
53154
53897
|
sessionID,
|
|
53155
53898
|
model: newModel
|
|
53156
53899
|
});
|
|
@@ -53169,16 +53912,16 @@ function createAutoRetryHelpers(deps) {
|
|
|
53169
53912
|
});
|
|
53170
53913
|
retryDispatched = true;
|
|
53171
53914
|
} else {
|
|
53172
|
-
log(`[${
|
|
53915
|
+
log(`[${HOOK_NAME9}] No user message found for auto-retry (${source})`, { sessionID });
|
|
53173
53916
|
}
|
|
53174
53917
|
} catch (retryError) {
|
|
53175
|
-
log(`[${
|
|
53918
|
+
log(`[${HOOK_NAME9}] Auto-retry failed (${source})`, { sessionID, error: String(retryError) });
|
|
53176
53919
|
} finally {
|
|
53177
53920
|
sessionRetryInFlight.delete(sessionID);
|
|
53178
53921
|
if (!retryDispatched) {
|
|
53179
53922
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
53180
53923
|
clearSessionFallbackTimeout(sessionID);
|
|
53181
|
-
const state3 =
|
|
53924
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53182
53925
|
if (state3?.pendingFallbackModel) {
|
|
53183
53926
|
state3.pendingFallbackModel = undefined;
|
|
53184
53927
|
}
|
|
@@ -53215,7 +53958,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53215
53958
|
let cleanedCount = 0;
|
|
53216
53959
|
for (const [sessionID, lastAccess] of sessionLastAccess.entries()) {
|
|
53217
53960
|
if (now - lastAccess > SESSION_TTL_MS) {
|
|
53218
|
-
|
|
53961
|
+
sessionStates2.delete(sessionID);
|
|
53219
53962
|
sessionLastAccess.delete(sessionID);
|
|
53220
53963
|
sessionRetryInFlight.delete(sessionID);
|
|
53221
53964
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
@@ -53226,7 +53969,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
53226
53969
|
}
|
|
53227
53970
|
}
|
|
53228
53971
|
if (cleanedCount > 0) {
|
|
53229
|
-
log(`[${
|
|
53972
|
+
log(`[${HOOK_NAME9}] Cleaned up ${cleanedCount} stale session states`);
|
|
53230
53973
|
}
|
|
53231
53974
|
};
|
|
53232
53975
|
return {
|
|
@@ -53242,12 +53985,12 @@ function createAutoRetryHelpers(deps) {
|
|
|
53242
53985
|
// src/hooks/runtime-fallback/chat-message-handler.ts
|
|
53243
53986
|
init_logger();
|
|
53244
53987
|
function createChatMessageHandler(deps) {
|
|
53245
|
-
const { config: config2, sessionStates, sessionLastAccess } = deps;
|
|
53988
|
+
const { config: config2, sessionStates: sessionStates2, sessionLastAccess } = deps;
|
|
53246
53989
|
return async (input, output) => {
|
|
53247
53990
|
if (!config2.enabled)
|
|
53248
53991
|
return;
|
|
53249
53992
|
const { sessionID } = input;
|
|
53250
|
-
let state3 =
|
|
53993
|
+
let state3 = sessionStates2.get(sessionID);
|
|
53251
53994
|
if (!state3)
|
|
53252
53995
|
return;
|
|
53253
53996
|
sessionLastAccess.set(sessionID, Date.now());
|
|
@@ -53257,19 +54000,19 @@ function createChatMessageHandler(deps) {
|
|
|
53257
54000
|
state3.pendingFallbackModel = undefined;
|
|
53258
54001
|
return;
|
|
53259
54002
|
}
|
|
53260
|
-
log(`[${
|
|
54003
|
+
log(`[${HOOK_NAME9}] Detected manual model change, resetting fallback state`, {
|
|
53261
54004
|
sessionID,
|
|
53262
54005
|
from: state3.currentModel,
|
|
53263
54006
|
to: requestedModel
|
|
53264
54007
|
});
|
|
53265
54008
|
state3 = createFallbackState(requestedModel);
|
|
53266
|
-
|
|
54009
|
+
sessionStates2.set(sessionID, state3);
|
|
53267
54010
|
return;
|
|
53268
54011
|
}
|
|
53269
54012
|
if (state3.currentModel === state3.originalModel)
|
|
53270
54013
|
return;
|
|
53271
54014
|
const activeModel = state3.currentModel;
|
|
53272
|
-
log(`[${
|
|
54015
|
+
log(`[${HOOK_NAME9}] Applying fallback model override`, {
|
|
53273
54016
|
sessionID,
|
|
53274
54017
|
from: input.model,
|
|
53275
54018
|
to: activeModel
|
|
@@ -53456,7 +54199,7 @@ function resolveFallbackBootstrapModel(options) {
|
|
|
53456
54199
|
const agentConfig = options.resolvedAgent && agentConfigs ? agentConfigs[options.resolvedAgent] : undefined;
|
|
53457
54200
|
const agentModel = typeof agentConfig?.model === "string" ? agentConfig.model : undefined;
|
|
53458
54201
|
if (agentModel) {
|
|
53459
|
-
log(`[${
|
|
54202
|
+
log(`[${HOOK_NAME9}] Derived model from agent config for ${options.source}`, {
|
|
53460
54203
|
sessionID: options.sessionID,
|
|
53461
54204
|
agent: options.resolvedAgent,
|
|
53462
54205
|
model: agentModel
|
|
@@ -53467,7 +54210,7 @@ function resolveFallbackBootstrapModel(options) {
|
|
|
53467
54210
|
if (agentCategory) {
|
|
53468
54211
|
const agentCategoryModel = options.pluginConfig?.categories?.[agentCategory]?.model;
|
|
53469
54212
|
if (typeof agentCategoryModel === "string" && agentCategoryModel.length > 0) {
|
|
53470
|
-
log(`[${
|
|
54213
|
+
log(`[${HOOK_NAME9}] Derived model from agent category config for ${options.source}`, {
|
|
53471
54214
|
sessionID: options.sessionID,
|
|
53472
54215
|
agent: options.resolvedAgent,
|
|
53473
54216
|
category: agentCategory,
|
|
@@ -53479,7 +54222,7 @@ function resolveFallbackBootstrapModel(options) {
|
|
|
53479
54222
|
const sessionCategory = SessionCategoryRegistry.get(options.sessionID);
|
|
53480
54223
|
const categoryModel = sessionCategory ? options.pluginConfig?.categories?.[sessionCategory]?.model : undefined;
|
|
53481
54224
|
if (typeof categoryModel === "string" && categoryModel.length > 0) {
|
|
53482
|
-
log(`[${
|
|
54225
|
+
log(`[${HOOK_NAME9}] Derived model from session category config for ${options.source}`, {
|
|
53483
54226
|
sessionID: options.sessionID,
|
|
53484
54227
|
category: sessionCategory,
|
|
53485
54228
|
model: categoryModel
|
|
@@ -53501,13 +54244,15 @@ async function dispatchFallbackRetry(deps, helpers, options) {
|
|
|
53501
54244
|
variant: "warning",
|
|
53502
54245
|
duration: 5000
|
|
53503
54246
|
}
|
|
53504
|
-
}).catch(() => {
|
|
54247
|
+
}).catch((err) => {
|
|
54248
|
+
log("[runtime-fallback] Fallback notification toast failed:", err);
|
|
54249
|
+
});
|
|
53505
54250
|
}
|
|
53506
54251
|
if (result.success && result.newModel) {
|
|
53507
54252
|
await helpers.autoRetryWithFallback(options.sessionID, result.newModel, options.resolvedAgent, options.source);
|
|
53508
54253
|
return;
|
|
53509
54254
|
}
|
|
53510
|
-
log(`[${
|
|
54255
|
+
log(`[${HOOK_NAME9}] Fallback preparation failed`, {
|
|
53511
54256
|
sessionID: options.sessionID,
|
|
53512
54257
|
source: options.source,
|
|
53513
54258
|
error: result.error
|
|
@@ -53555,7 +54300,7 @@ function extractRetryAttempt(statusAttempt, message) {
|
|
|
53555
54300
|
function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
|
|
53556
54301
|
const {
|
|
53557
54302
|
pluginConfig,
|
|
53558
|
-
sessionStates,
|
|
54303
|
+
sessionStates: sessionStates2,
|
|
53559
54304
|
sessionLastAccess,
|
|
53560
54305
|
sessionRetryInFlight
|
|
53561
54306
|
} = deps;
|
|
@@ -53582,26 +54327,26 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
|
|
|
53582
54327
|
sessionStatusRetryKeys.set(sessionID, retryKey);
|
|
53583
54328
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
53584
54329
|
if (timeoutEnabled) {
|
|
53585
|
-
log(`[${
|
|
54330
|
+
log(`[${HOOK_NAME9}] Overriding in-flight retry due to provider auto-retry signal`, {
|
|
53586
54331
|
sessionID,
|
|
53587
54332
|
model
|
|
53588
54333
|
});
|
|
53589
54334
|
await helpers.abortSessionRequest(sessionID, "session.status.retry-signal");
|
|
53590
54335
|
sessionRetryInFlight.delete(sessionID);
|
|
53591
54336
|
} else {
|
|
53592
|
-
log(`[${
|
|
54337
|
+
log(`[${HOOK_NAME9}] session.status retry skipped - retry already in flight`, { sessionID });
|
|
53593
54338
|
return;
|
|
53594
54339
|
}
|
|
53595
54340
|
}
|
|
53596
54341
|
const resolvedAgent = await helpers.resolveAgentForSessionFromContext(sessionID, agent);
|
|
53597
54342
|
const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
|
|
53598
54343
|
if (fallbackModels.length === 0) {
|
|
53599
|
-
if (!
|
|
54344
|
+
if (!sessionStates2.has(sessionID)) {
|
|
53600
54345
|
sessionStatusRetryKeys.delete(sessionID);
|
|
53601
54346
|
}
|
|
53602
54347
|
return;
|
|
53603
54348
|
}
|
|
53604
|
-
let state3 =
|
|
54349
|
+
let state3 = sessionStates2.get(sessionID);
|
|
53605
54350
|
if (!state3) {
|
|
53606
54351
|
const initialModel = resolveFallbackBootstrapModel({
|
|
53607
54352
|
sessionID,
|
|
@@ -53612,29 +54357,29 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
|
|
|
53612
54357
|
});
|
|
53613
54358
|
if (!initialModel) {
|
|
53614
54359
|
sessionStatusRetryKeys.delete(sessionID);
|
|
53615
|
-
log(`[${
|
|
54360
|
+
log(`[${HOOK_NAME9}] session.status retry missing model info, cannot fallback`, { sessionID });
|
|
53616
54361
|
return;
|
|
53617
54362
|
}
|
|
53618
54363
|
state3 = createFallbackState(initialModel);
|
|
53619
|
-
|
|
54364
|
+
sessionStates2.set(sessionID, state3);
|
|
53620
54365
|
}
|
|
53621
54366
|
sessionLastAccess.set(sessionID, Date.now());
|
|
53622
54367
|
if (state3.pendingFallbackModel) {
|
|
53623
54368
|
if (timeoutEnabled) {
|
|
53624
|
-
log(`[${
|
|
54369
|
+
log(`[${HOOK_NAME9}] Clearing pending fallback due to provider auto-retry signal`, {
|
|
53625
54370
|
sessionID,
|
|
53626
54371
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
53627
54372
|
});
|
|
53628
54373
|
state3.pendingFallbackModel = undefined;
|
|
53629
54374
|
} else {
|
|
53630
|
-
log(`[${
|
|
54375
|
+
log(`[${HOOK_NAME9}] session.status retry skipped (pending fallback in progress)`, {
|
|
53631
54376
|
sessionID,
|
|
53632
54377
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
53633
54378
|
});
|
|
53634
54379
|
return;
|
|
53635
54380
|
}
|
|
53636
54381
|
}
|
|
53637
|
-
log(`[${
|
|
54382
|
+
log(`[${HOOK_NAME9}] Detected provider auto-retry signal in session.status`, {
|
|
53638
54383
|
sessionID,
|
|
53639
54384
|
model: state3.currentModel,
|
|
53640
54385
|
retryAttempt: status.attempt
|
|
@@ -53652,13 +54397,13 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
|
|
|
53652
54397
|
|
|
53653
54398
|
// src/hooks/runtime-fallback/event-handler.ts
|
|
53654
54399
|
function createEventHandler(deps, helpers) {
|
|
53655
|
-
const { config: config2, pluginConfig, sessionStates, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionFallbackTimeouts, sessionStatusRetryKeys } = deps;
|
|
54400
|
+
const { config: config2, pluginConfig, sessionStates: sessionStates2, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionFallbackTimeouts, sessionStatusRetryKeys } = deps;
|
|
53656
54401
|
const sessionStatusHandler = createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys);
|
|
53657
54402
|
const cancelledSessions = new Set;
|
|
53658
54403
|
const resetRetryState = (sessionID) => {
|
|
53659
|
-
const state3 =
|
|
54404
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53660
54405
|
if (state3) {
|
|
53661
|
-
|
|
54406
|
+
sessionStates2.set(sessionID, createFallbackState(state3.originalModel));
|
|
53662
54407
|
}
|
|
53663
54408
|
sessionRetryInFlight.delete(sessionID);
|
|
53664
54409
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
@@ -53670,8 +54415,8 @@ function createEventHandler(deps, helpers) {
|
|
|
53670
54415
|
const sessionID = sessionInfo?.id;
|
|
53671
54416
|
const model = sessionInfo?.model;
|
|
53672
54417
|
if (sessionID && model) {
|
|
53673
|
-
log(`[${
|
|
53674
|
-
|
|
54418
|
+
log(`[${HOOK_NAME9}] Session created with model`, { sessionID, model });
|
|
54419
|
+
sessionStates2.set(sessionID, createFallbackState(model));
|
|
53675
54420
|
sessionLastAccess.set(sessionID, Date.now());
|
|
53676
54421
|
}
|
|
53677
54422
|
};
|
|
@@ -53679,9 +54424,9 @@ function createEventHandler(deps, helpers) {
|
|
|
53679
54424
|
const sessionInfo = props?.info;
|
|
53680
54425
|
const sessionID = sessionInfo?.id;
|
|
53681
54426
|
if (sessionID) {
|
|
53682
|
-
log(`[${
|
|
54427
|
+
log(`[${HOOK_NAME9}] Cleaning up session state`, { sessionID });
|
|
53683
54428
|
cancelledSessions.delete(sessionID);
|
|
53684
|
-
|
|
54429
|
+
sessionStates2.delete(sessionID);
|
|
53685
54430
|
sessionLastAccess.delete(sessionID);
|
|
53686
54431
|
sessionRetryInFlight.delete(sessionID);
|
|
53687
54432
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
@@ -53699,7 +54444,7 @@ function createEventHandler(deps, helpers) {
|
|
|
53699
54444
|
}
|
|
53700
54445
|
cancelledSessions.add(sessionID);
|
|
53701
54446
|
resetRetryState(sessionID);
|
|
53702
|
-
log(`[${
|
|
54447
|
+
log(`[${HOOK_NAME9}] Cleared fallback retry state on session.stop`, { sessionID });
|
|
53703
54448
|
};
|
|
53704
54449
|
const handleMessageUpdated = (props) => {
|
|
53705
54450
|
const info = props?.info;
|
|
@@ -53715,23 +54460,23 @@ function createEventHandler(deps, helpers) {
|
|
|
53715
54460
|
return;
|
|
53716
54461
|
if (cancelledSessions.has(sessionID)) {
|
|
53717
54462
|
resetRetryState(sessionID);
|
|
53718
|
-
log(`[${
|
|
54463
|
+
log(`[${HOOK_NAME9}] Cleared fallback retry state for cancelled session on idle`, { sessionID });
|
|
53719
54464
|
return;
|
|
53720
54465
|
}
|
|
53721
54466
|
if (sessionAwaitingFallbackResult.has(sessionID)) {
|
|
53722
|
-
log(`[${
|
|
54467
|
+
log(`[${HOOK_NAME9}] session.idle while awaiting fallback result; keeping timeout armed`, { sessionID });
|
|
53723
54468
|
return;
|
|
53724
54469
|
}
|
|
53725
54470
|
const hadTimeout = sessionFallbackTimeouts.has(sessionID);
|
|
53726
54471
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
53727
54472
|
sessionRetryInFlight.delete(sessionID);
|
|
53728
54473
|
sessionStatusRetryKeys.delete(sessionID);
|
|
53729
|
-
const state3 =
|
|
54474
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53730
54475
|
if (state3?.pendingFallbackModel) {
|
|
53731
54476
|
state3.pendingFallbackModel = undefined;
|
|
53732
54477
|
}
|
|
53733
54478
|
if (hadTimeout) {
|
|
53734
|
-
log(`[${
|
|
54479
|
+
log(`[${HOOK_NAME9}] Cleared fallback timeout after session completion`, { sessionID });
|
|
53735
54480
|
}
|
|
53736
54481
|
};
|
|
53737
54482
|
const handleSessionError = async (props) => {
|
|
@@ -53739,18 +54484,18 @@ function createEventHandler(deps, helpers) {
|
|
|
53739
54484
|
const error51 = props?.error;
|
|
53740
54485
|
const agent = props?.agent;
|
|
53741
54486
|
if (!sessionID) {
|
|
53742
|
-
log(`[${
|
|
54487
|
+
log(`[${HOOK_NAME9}] session.error without sessionID, skipping`);
|
|
53743
54488
|
return;
|
|
53744
54489
|
}
|
|
53745
54490
|
const resolvedAgent = await helpers.resolveAgentForSessionFromContext(sessionID, agent);
|
|
53746
54491
|
if (isAbortError2(error51)) {
|
|
53747
54492
|
cancelledSessions.add(sessionID);
|
|
53748
54493
|
resetRetryState(sessionID);
|
|
53749
|
-
log(`[${
|
|
54494
|
+
log(`[${HOOK_NAME9}] session.error matched cancellation; cleared retry state`, { sessionID, resolvedAgent });
|
|
53750
54495
|
return;
|
|
53751
54496
|
}
|
|
53752
54497
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
53753
|
-
log(`[${
|
|
54498
|
+
log(`[${HOOK_NAME9}] session.error skipped - retry in flight`, {
|
|
53754
54499
|
sessionID,
|
|
53755
54500
|
retryInFlight: true
|
|
53756
54501
|
});
|
|
@@ -53758,7 +54503,7 @@ function createEventHandler(deps, helpers) {
|
|
|
53758
54503
|
}
|
|
53759
54504
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
53760
54505
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
53761
|
-
log(`[${
|
|
54506
|
+
log(`[${HOOK_NAME9}] session.error received`, {
|
|
53762
54507
|
sessionID,
|
|
53763
54508
|
agent,
|
|
53764
54509
|
resolvedAgent,
|
|
@@ -53767,7 +54512,7 @@ function createEventHandler(deps, helpers) {
|
|
|
53767
54512
|
errorType: classifyErrorType(error51)
|
|
53768
54513
|
});
|
|
53769
54514
|
if (!isRetryableError(error51, config2.retry_on_errors)) {
|
|
53770
|
-
log(`[${
|
|
54515
|
+
log(`[${HOOK_NAME9}] Error not retryable, skipping fallback`, {
|
|
53771
54516
|
sessionID,
|
|
53772
54517
|
retryable: false,
|
|
53773
54518
|
statusCode: extractStatusCode(error51, config2.retry_on_errors),
|
|
@@ -53776,10 +54521,10 @@ function createEventHandler(deps, helpers) {
|
|
|
53776
54521
|
});
|
|
53777
54522
|
return;
|
|
53778
54523
|
}
|
|
53779
|
-
let state3 =
|
|
54524
|
+
let state3 = sessionStates2.get(sessionID);
|
|
53780
54525
|
const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
|
|
53781
54526
|
if (fallbackModels.length === 0) {
|
|
53782
|
-
log(`[${
|
|
54527
|
+
log(`[${HOOK_NAME9}] No fallback models configured`, { sessionID, agent });
|
|
53783
54528
|
return;
|
|
53784
54529
|
}
|
|
53785
54530
|
if (!state3) {
|
|
@@ -53791,11 +54536,11 @@ function createEventHandler(deps, helpers) {
|
|
|
53791
54536
|
pluginConfig
|
|
53792
54537
|
});
|
|
53793
54538
|
if (!initialModel) {
|
|
53794
|
-
log(`[${
|
|
54539
|
+
log(`[${HOOK_NAME9}] No model info available, cannot fallback`, { sessionID });
|
|
53795
54540
|
return;
|
|
53796
54541
|
}
|
|
53797
54542
|
state3 = createFallbackState(initialModel);
|
|
53798
|
-
|
|
54543
|
+
sessionStates2.set(sessionID, state3);
|
|
53799
54544
|
sessionLastAccess.set(sessionID, Date.now());
|
|
53800
54545
|
} else {
|
|
53801
54546
|
sessionLastAccess.set(sessionID, Date.now());
|
|
@@ -53907,7 +54652,7 @@ function hasVisibleAssistantResponse(extractAutoRetrySignalFn) {
|
|
|
53907
54652
|
|
|
53908
54653
|
// src/hooks/runtime-fallback/message-update-handler.ts
|
|
53909
54654
|
function createMessageUpdateHandler(deps, helpers) {
|
|
53910
|
-
const { ctx, config: config2, pluginConfig, sessionStates, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionStatusRetryKeys } = deps;
|
|
54655
|
+
const { ctx, config: config2, pluginConfig, sessionStates: sessionStates2, sessionLastAccess, sessionRetryInFlight, sessionAwaitingFallbackResult, sessionStatusRetryKeys } = deps;
|
|
53911
54656
|
const checkVisibleResponse = hasVisibleAssistantResponse(extractAutoRetrySignal);
|
|
53912
54657
|
return async (props) => {
|
|
53913
54658
|
const info = props?.info;
|
|
@@ -53931,7 +54676,7 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53931
54676
|
}
|
|
53932
54677
|
const hasVisible = await checkVisibleResponse(ctx, sessionID, info);
|
|
53933
54678
|
if (!hasVisible) {
|
|
53934
|
-
log(`[${
|
|
54679
|
+
log(`[${HOOK_NAME9}] Assistant update observed without visible final response; keeping fallback timeout`, {
|
|
53935
54680
|
sessionID,
|
|
53936
54681
|
model
|
|
53937
54682
|
});
|
|
@@ -53940,21 +54685,21 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53940
54685
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
53941
54686
|
sessionStatusRetryKeys.delete(sessionID);
|
|
53942
54687
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
53943
|
-
const state3 =
|
|
54688
|
+
const state3 = sessionStates2.get(sessionID);
|
|
53944
54689
|
if (state3?.pendingFallbackModel) {
|
|
53945
54690
|
state3.pendingFallbackModel = undefined;
|
|
53946
54691
|
}
|
|
53947
|
-
log(`[${
|
|
54692
|
+
log(`[${HOOK_NAME9}] Assistant response observed; cleared fallback timeout`, { sessionID, model });
|
|
53948
54693
|
return;
|
|
53949
54694
|
}
|
|
53950
54695
|
if (sessionID && role === "assistant" && error51) {
|
|
53951
54696
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
53952
54697
|
if (sessionRetryInFlight.has(sessionID) && !retrySignal) {
|
|
53953
|
-
log(`[${
|
|
54698
|
+
log(`[${HOOK_NAME9}] message.updated fallback skipped (retry in flight)`, { sessionID });
|
|
53954
54699
|
return;
|
|
53955
54700
|
}
|
|
53956
54701
|
if (retrySignal && sessionRetryInFlight.has(sessionID) && timeoutEnabled) {
|
|
53957
|
-
log(`[${
|
|
54702
|
+
log(`[${HOOK_NAME9}] Overriding in-flight retry due to provider auto-retry signal`, {
|
|
53958
54703
|
sessionID,
|
|
53959
54704
|
model
|
|
53960
54705
|
});
|
|
@@ -53962,12 +54707,12 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53962
54707
|
sessionRetryInFlight.delete(sessionID);
|
|
53963
54708
|
}
|
|
53964
54709
|
if (retrySignal && timeoutEnabled) {
|
|
53965
|
-
log(`[${
|
|
54710
|
+
log(`[${HOOK_NAME9}] Detected provider auto-retry signal`, { sessionID, model });
|
|
53966
54711
|
}
|
|
53967
54712
|
if (!retrySignal) {
|
|
53968
54713
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
53969
54714
|
}
|
|
53970
|
-
log(`[${
|
|
54715
|
+
log(`[${HOOK_NAME9}] message.updated with assistant error`, {
|
|
53971
54716
|
sessionID,
|
|
53972
54717
|
model,
|
|
53973
54718
|
statusCode: extractStatusCode(error51, config2.retry_on_errors),
|
|
@@ -53975,7 +54720,7 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53975
54720
|
errorType: classifyErrorType(error51)
|
|
53976
54721
|
});
|
|
53977
54722
|
if (!isRetryableError(error51, config2.retry_on_errors)) {
|
|
53978
|
-
log(`[${
|
|
54723
|
+
log(`[${HOOK_NAME9}] message.updated error not retryable, skipping fallback`, {
|
|
53979
54724
|
sessionID,
|
|
53980
54725
|
statusCode: extractStatusCode(error51, config2.retry_on_errors),
|
|
53981
54726
|
errorName: extractErrorName(error51),
|
|
@@ -53983,7 +54728,7 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53983
54728
|
});
|
|
53984
54729
|
return;
|
|
53985
54730
|
}
|
|
53986
|
-
let state3 =
|
|
54731
|
+
let state3 = sessionStates2.get(sessionID);
|
|
53987
54732
|
const agent = info?.agent;
|
|
53988
54733
|
const resolvedAgent = await helpers.resolveAgentForSessionFromContext(sessionID, agent);
|
|
53989
54734
|
const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
|
|
@@ -53999,7 +54744,7 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
53999
54744
|
pluginConfig
|
|
54000
54745
|
});
|
|
54001
54746
|
if (!initialModel) {
|
|
54002
|
-
log(`[${
|
|
54747
|
+
log(`[${HOOK_NAME9}] message.updated missing model info, cannot fallback`, {
|
|
54003
54748
|
sessionID,
|
|
54004
54749
|
errorName: extractErrorName(error51),
|
|
54005
54750
|
errorType: classifyErrorType(error51)
|
|
@@ -54007,19 +54752,19 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
54007
54752
|
return;
|
|
54008
54753
|
}
|
|
54009
54754
|
state3 = createFallbackState(initialModel);
|
|
54010
|
-
|
|
54755
|
+
sessionStates2.set(sessionID, state3);
|
|
54011
54756
|
sessionLastAccess.set(sessionID, Date.now());
|
|
54012
54757
|
} else {
|
|
54013
54758
|
sessionLastAccess.set(sessionID, Date.now());
|
|
54014
54759
|
if (state3.pendingFallbackModel) {
|
|
54015
54760
|
if (retrySignal && timeoutEnabled) {
|
|
54016
|
-
log(`[${
|
|
54761
|
+
log(`[${HOOK_NAME9}] Clearing pending fallback due to provider auto-retry signal`, {
|
|
54017
54762
|
sessionID,
|
|
54018
54763
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
54019
54764
|
});
|
|
54020
54765
|
state3.pendingFallbackModel = undefined;
|
|
54021
54766
|
} else {
|
|
54022
|
-
log(`[${
|
|
54767
|
+
log(`[${HOOK_NAME9}] message.updated fallback skipped (pending fallback in progress)`, {
|
|
54023
54768
|
sessionID,
|
|
54024
54769
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
54025
54770
|
});
|
|
@@ -54363,6 +55108,7 @@ function createIdleNotificationScheduler(options) {
|
|
|
54363
55108
|
}
|
|
54364
55109
|
|
|
54365
55110
|
// src/hooks/session-notification-sender.ts
|
|
55111
|
+
init_logger();
|
|
54366
55112
|
import { platform } from "os";
|
|
54367
55113
|
|
|
54368
55114
|
// src/hooks/session-notification-formatting.ts
|
|
@@ -54390,6 +55136,7 @@ $Notifier.Show($Toast)
|
|
|
54390
55136
|
}
|
|
54391
55137
|
|
|
54392
55138
|
// src/hooks/session-notification-utils.ts
|
|
55139
|
+
init_logger();
|
|
54393
55140
|
async function findCommand2(commandName) {
|
|
54394
55141
|
try {
|
|
54395
55142
|
return Bun.which(commandName);
|
|
@@ -54421,14 +55168,26 @@ var getPaplayPath = createCommandFinder("paplay");
|
|
|
54421
55168
|
var getAplayPath = createCommandFinder("aplay");
|
|
54422
55169
|
function startBackgroundCheck2(platform) {
|
|
54423
55170
|
if (platform === "darwin") {
|
|
54424
|
-
getOsascriptPath().catch(() => {
|
|
54425
|
-
|
|
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
|
+
});
|
|
54426
55177
|
} else if (platform === "linux") {
|
|
54427
|
-
getNotifySendPath().catch(() => {
|
|
54428
|
-
|
|
54429
|
-
|
|
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
|
+
});
|
|
54430
55187
|
} else if (platform === "win32") {
|
|
54431
|
-
getPowershellPath().catch(() => {
|
|
55188
|
+
getPowershellPath().catch((err) => {
|
|
55189
|
+
log("[notification] Background check failed for powershell:", err);
|
|
55190
|
+
});
|
|
54432
55191
|
}
|
|
54433
55192
|
}
|
|
54434
55193
|
|
|
@@ -54459,14 +55218,18 @@ async function sendSessionNotification(ctx, platform2, title, message) {
|
|
|
54459
55218
|
return;
|
|
54460
55219
|
const escapedTitle = escapeAppleScriptText(title);
|
|
54461
55220
|
const escapedMessage = escapeAppleScriptText(message);
|
|
54462
|
-
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
|
+
});
|
|
54463
55224
|
break;
|
|
54464
55225
|
}
|
|
54465
55226
|
case "linux": {
|
|
54466
55227
|
const notifySendPath = await getNotifySendPath();
|
|
54467
55228
|
if (!notifySendPath)
|
|
54468
55229
|
return;
|
|
54469
|
-
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
|
+
});
|
|
54470
55233
|
break;
|
|
54471
55234
|
}
|
|
54472
55235
|
case "win32": {
|
|
@@ -54474,7 +55237,9 @@ async function sendSessionNotification(ctx, platform2, title, message) {
|
|
|
54474
55237
|
if (!powershellPath)
|
|
54475
55238
|
return;
|
|
54476
55239
|
const toastScript = buildWindowsToastScript(title, message);
|
|
54477
|
-
await ctx.$`${powershellPath} -Command ${toastScript}`.catch(() => {
|
|
55240
|
+
await ctx.$`${powershellPath} -Command ${toastScript}`.catch((err) => {
|
|
55241
|
+
log("[notification] Windows notification failed:", err);
|
|
55242
|
+
});
|
|
54478
55243
|
break;
|
|
54479
55244
|
}
|
|
54480
55245
|
}
|
|
@@ -54485,17 +55250,23 @@ async function playSessionNotificationSound(ctx, platform2, soundPath) {
|
|
|
54485
55250
|
const afplayPath = await getAfplayPath();
|
|
54486
55251
|
if (!afplayPath)
|
|
54487
55252
|
return;
|
|
54488
|
-
ctx.$`${afplayPath} ${soundPath}`.catch(() => {
|
|
55253
|
+
ctx.$`${afplayPath} ${soundPath}`.catch((err) => {
|
|
55254
|
+
log("[notification] macOS sound playback failed:", err);
|
|
55255
|
+
});
|
|
54489
55256
|
break;
|
|
54490
55257
|
}
|
|
54491
55258
|
case "linux": {
|
|
54492
55259
|
const paplayPath = await getPaplayPath();
|
|
54493
55260
|
if (paplayPath) {
|
|
54494
|
-
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
|
+
});
|
|
54495
55264
|
} else {
|
|
54496
55265
|
const aplayPath = await getAplayPath();
|
|
54497
55266
|
if (aplayPath) {
|
|
54498
|
-
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
|
+
});
|
|
54499
55270
|
}
|
|
54500
55271
|
}
|
|
54501
55272
|
break;
|
|
@@ -54505,7 +55276,9 @@ async function playSessionNotificationSound(ctx, platform2, soundPath) {
|
|
|
54505
55276
|
if (!powershellPath)
|
|
54506
55277
|
return;
|
|
54507
55278
|
const escaped = escapePowerShellSingleQuotedText(soundPath);
|
|
54508
|
-
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
|
+
});
|
|
54509
55282
|
break;
|
|
54510
55283
|
}
|
|
54511
55284
|
}
|
|
@@ -54923,7 +55696,9 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
54923
55696
|
if (onAbortCallback) {
|
|
54924
55697
|
onAbortCallback(sessionID);
|
|
54925
55698
|
}
|
|
54926
|
-
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
|
+
});
|
|
54927
55702
|
const messagesResp = await ctx.client.session.messages({
|
|
54928
55703
|
path: { id: sessionID },
|
|
54929
55704
|
query: { directory: ctx.directory }
|
|
@@ -54952,7 +55727,9 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
54952
55727
|
variant: "warning",
|
|
54953
55728
|
duration: 3000
|
|
54954
55729
|
}
|
|
54955
|
-
}).catch(() => {
|
|
55730
|
+
}).catch((err) => {
|
|
55731
|
+
log("[session-recovery] Recovery toast failed:", err);
|
|
55732
|
+
});
|
|
54956
55733
|
let success2 = false;
|
|
54957
55734
|
if (errorType === "tool_result_missing") {
|
|
54958
55735
|
success2 = await recoverToolResultMissing(ctx.client, sessionID, failedMsg);
|
|
@@ -54993,7 +55770,7 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
54993
55770
|
}
|
|
54994
55771
|
// src/hooks/start-work/start-work-hook.ts
|
|
54995
55772
|
init_logger();
|
|
54996
|
-
var
|
|
55773
|
+
var HOOK_NAME10 = "start-work";
|
|
54997
55774
|
var KEYWORD_PATTERN = /\b(ultrawork|ulw)\b/gi;
|
|
54998
55775
|
function extractUserRequestPlanName(promptText) {
|
|
54999
55776
|
const userRequestMatch = promptText.match(/<user-request>\s*([\s\S]*?)\s*<\/user-request>/i);
|
|
@@ -55023,7 +55800,7 @@ function createStartWorkHook(ctx) {
|
|
|
55023
55800
|
if (!isStartWorkCommand) {
|
|
55024
55801
|
return;
|
|
55025
55802
|
}
|
|
55026
|
-
log(`[${
|
|
55803
|
+
log(`[${HOOK_NAME10}] Processing start-work command`, {
|
|
55027
55804
|
sessionID: input.sessionID
|
|
55028
55805
|
});
|
|
55029
55806
|
updateSessionAgent(input.sessionID, "architect");
|
|
@@ -55033,7 +55810,7 @@ function createStartWorkHook(ctx) {
|
|
|
55033
55810
|
let contextInfo = "";
|
|
55034
55811
|
const explicitPlanName = extractUserRequestPlanName(promptText);
|
|
55035
55812
|
if (explicitPlanName) {
|
|
55036
|
-
log(`[${
|
|
55813
|
+
log(`[${HOOK_NAME10}] Explicit plan name requested: ${explicitPlanName}`, {
|
|
55037
55814
|
sessionID: input.sessionID
|
|
55038
55815
|
});
|
|
55039
55816
|
const allPlans = findOraclePlans(ctx.directory);
|
|
@@ -55174,7 +55951,7 @@ Ask the user which plan to work on. Present the options above and wait for their
|
|
|
55174
55951
|
---
|
|
55175
55952
|
${contextInfo}`;
|
|
55176
55953
|
}
|
|
55177
|
-
log(`[${
|
|
55954
|
+
log(`[${HOOK_NAME10}] Context injected`, {
|
|
55178
55955
|
sessionID: input.sessionID,
|
|
55179
55956
|
hasExistingState: !!existingState
|
|
55180
55957
|
});
|
|
@@ -55183,16 +55960,16 @@ ${contextInfo}`;
|
|
|
55183
55960
|
}
|
|
55184
55961
|
// src/hooks/stop-continuation-guard/hook.ts
|
|
55185
55962
|
init_logger();
|
|
55186
|
-
var
|
|
55963
|
+
var HOOK_NAME11 = "stop-continuation-guard";
|
|
55187
55964
|
function createStopContinuationGuardHook(_ctx, options) {
|
|
55188
55965
|
const stoppedSessions = new Set;
|
|
55189
55966
|
const stop = (sessionID) => {
|
|
55190
55967
|
stoppedSessions.add(sessionID);
|
|
55191
|
-
log(`[${
|
|
55968
|
+
log(`[${HOOK_NAME11}] Continuation stopped for session`, { sessionID });
|
|
55192
55969
|
if (options?.backgroundManager) {
|
|
55193
55970
|
const cancelled = options.backgroundManager.cancelAllForSession(sessionID);
|
|
55194
55971
|
if (cancelled > 0) {
|
|
55195
|
-
log(`[${
|
|
55972
|
+
log(`[${HOOK_NAME11}] Cancelled ${cancelled} background task(s) for session`, { sessionID });
|
|
55196
55973
|
}
|
|
55197
55974
|
}
|
|
55198
55975
|
};
|
|
@@ -55201,7 +55978,7 @@ function createStopContinuationGuardHook(_ctx, options) {
|
|
|
55201
55978
|
};
|
|
55202
55979
|
const clear = (sessionID) => {
|
|
55203
55980
|
stoppedSessions.delete(sessionID);
|
|
55204
|
-
log(`[${
|
|
55981
|
+
log(`[${HOOK_NAME11}] Continuation guard cleared for session`, { sessionID });
|
|
55205
55982
|
};
|
|
55206
55983
|
const event = async ({
|
|
55207
55984
|
event: event2
|
|
@@ -55211,7 +55988,7 @@ function createStopContinuationGuardHook(_ctx, options) {
|
|
|
55211
55988
|
const sessionInfo = props?.info;
|
|
55212
55989
|
if (sessionInfo?.id) {
|
|
55213
55990
|
clear(sessionInfo.id);
|
|
55214
|
-
log(`[${
|
|
55991
|
+
log(`[${HOOK_NAME11}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
|
|
55215
55992
|
}
|
|
55216
55993
|
}
|
|
55217
55994
|
};
|
|
@@ -55220,7 +55997,7 @@ function createStopContinuationGuardHook(_ctx, options) {
|
|
|
55220
55997
|
}) => {
|
|
55221
55998
|
if (sessionID && stoppedSessions.has(sessionID)) {
|
|
55222
55999
|
clear(sessionID);
|
|
55223
|
-
log(`[${
|
|
56000
|
+
log(`[${HOOK_NAME11}] Cleared stop state on new user message`, { sessionID });
|
|
55224
56001
|
}
|
|
55225
56002
|
};
|
|
55226
56003
|
return {
|
|
@@ -55232,7 +56009,7 @@ function createStopContinuationGuardHook(_ctx, options) {
|
|
|
55232
56009
|
};
|
|
55233
56010
|
}
|
|
55234
56011
|
// src/hooks/task-notepad/constants.ts
|
|
55235
|
-
var
|
|
56012
|
+
var HOOK_NAME12 = "task-notepad";
|
|
55236
56013
|
var PLAN_DIR_NAME = ".matrixx";
|
|
55237
56014
|
var PLANS_SUBDIR = "plans";
|
|
55238
56015
|
var NOTEPAD_DIR_NAME = "notepads";
|
|
@@ -55261,7 +56038,7 @@ function createTaskNotepadHook(ctx) {
|
|
|
55261
56038
|
const plan = resolveActivePlan(ctx.directory);
|
|
55262
56039
|
if (inProgress) {
|
|
55263
56040
|
if (!plan) {
|
|
55264
|
-
log(`[${
|
|
56041
|
+
log(`[${HOOK_NAME12}] No active plan, skipping notepad creation`, {
|
|
55265
56042
|
sessionID: input.sessionID
|
|
55266
56043
|
});
|
|
55267
56044
|
} else {
|
|
@@ -55288,7 +56065,7 @@ async function fetchTodos(ctx, sessionID) {
|
|
|
55288
56065
|
}
|
|
55289
56066
|
return [];
|
|
55290
56067
|
} catch (err) {
|
|
55291
|
-
log(`[${
|
|
56068
|
+
log(`[${HOOK_NAME12}] ${TODO_FETCH_ERROR_FRAGMENT}`, {
|
|
55292
56069
|
sessionID,
|
|
55293
56070
|
error: String(err)
|
|
55294
56071
|
});
|
|
@@ -55333,7 +56110,7 @@ function ensureNotepad(directory, planName, todo) {
|
|
|
55333
56110
|
mkdirSync12(dirname15(notepadPath), { recursive: true });
|
|
55334
56111
|
const content = renderNotepad(todo);
|
|
55335
56112
|
writeFileSync16(notepadPath, content, "utf-8");
|
|
55336
|
-
log(`[${
|
|
56113
|
+
log(`[${HOOK_NAME12}] Created task notepad`, { plan: planName, taskID: todo.id });
|
|
55337
56114
|
}
|
|
55338
56115
|
function appendCompletion(directory, planName, todo) {
|
|
55339
56116
|
const notepadPath = notepadPathFor(directory, planName, todo.id);
|
|
@@ -55349,7 +56126,7 @@ function appendCompletion(directory, planName, todo) {
|
|
|
55349
56126
|
- status: completed
|
|
55350
56127
|
`;
|
|
55351
56128
|
writeFileSync16(notepadPath, updated, "utf-8");
|
|
55352
|
-
log(`[${
|
|
56129
|
+
log(`[${HOOK_NAME12}] Appended completion to notepad`, { plan: planName, taskID: todo.id });
|
|
55353
56130
|
}
|
|
55354
56131
|
function notepadPathFor(directory, planName, taskID) {
|
|
55355
56132
|
return join54(directory, PLAN_DIR_NAME, NOTEPAD_DIR_NAME, planName, TASKS_SUBDIR, `${taskID}${NOTEPAD_EXTENSION}`);
|
|
@@ -55931,7 +56708,7 @@ function createThinkingBlockValidatorHook() {
|
|
|
55931
56708
|
init_logger();
|
|
55932
56709
|
|
|
55933
56710
|
// src/hooks/todo-continuation-enforcer/constants.ts
|
|
55934
|
-
var
|
|
56711
|
+
var HOOK_NAME13 = "todo-continuation-enforcer";
|
|
55935
56712
|
var DEFAULT_SKIP_AGENTS = ["oracle", "compaction"];
|
|
55936
56713
|
var CONTINUATION_PROMPT2 = `${createSystemDirective(SystemDirectiveTypes.TODO_CONTINUATION)}
|
|
55937
56714
|
|
|
@@ -55997,12 +56774,12 @@ async function injectContinuation(args) {
|
|
|
55997
56774
|
} = args;
|
|
55998
56775
|
const state3 = sessionStateStore.getExistingState(sessionID);
|
|
55999
56776
|
if (state3?.isRecovering) {
|
|
56000
|
-
log(`[${
|
|
56777
|
+
log(`[${HOOK_NAME13}] Skipped injection: in recovery`, { sessionID });
|
|
56001
56778
|
return;
|
|
56002
56779
|
}
|
|
56003
56780
|
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
|
|
56004
56781
|
if (hasRunningBgTasks) {
|
|
56005
|
-
log(`[${
|
|
56782
|
+
log(`[${HOOK_NAME13}] Skipped injection: background tasks running`, { sessionID });
|
|
56006
56783
|
return;
|
|
56007
56784
|
}
|
|
56008
56785
|
let todos = [];
|
|
@@ -56010,12 +56787,12 @@ async function injectContinuation(args) {
|
|
|
56010
56787
|
const response = await ctx.client.session.todo({ path: { id: sessionID } });
|
|
56011
56788
|
todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
|
|
56012
56789
|
} catch (error51) {
|
|
56013
|
-
log(`[${
|
|
56790
|
+
log(`[${HOOK_NAME13}] Failed to fetch todos`, { sessionID, error: String(error51) });
|
|
56014
56791
|
return;
|
|
56015
56792
|
}
|
|
56016
56793
|
const freshIncompleteCount = getIncompleteCount(todos);
|
|
56017
56794
|
if (freshIncompleteCount === 0) {
|
|
56018
|
-
log(`[${
|
|
56795
|
+
log(`[${HOOK_NAME13}] Skipped injection: no incomplete todos`, { sessionID });
|
|
56019
56796
|
return;
|
|
56020
56797
|
}
|
|
56021
56798
|
let agentName = resolvedInfo?.agent;
|
|
@@ -56038,11 +56815,11 @@ async function injectContinuation(args) {
|
|
|
56038
56815
|
tools = tools ?? previousMessage?.tools;
|
|
56039
56816
|
}
|
|
56040
56817
|
if (agentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(agentName))) {
|
|
56041
|
-
log(`[${
|
|
56818
|
+
log(`[${HOOK_NAME13}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
|
|
56042
56819
|
return;
|
|
56043
56820
|
}
|
|
56044
56821
|
if (!hasWritePermission(tools)) {
|
|
56045
|
-
log(`[${
|
|
56822
|
+
log(`[${HOOK_NAME13}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
|
|
56046
56823
|
return;
|
|
56047
56824
|
}
|
|
56048
56825
|
const incompleteTodos = todos.filter((todo) => todo.status !== "completed" && todo.status !== "cancelled");
|
|
@@ -56059,7 +56836,7 @@ ${todoList}`;
|
|
|
56059
56836
|
injectionState.inFlight = true;
|
|
56060
56837
|
}
|
|
56061
56838
|
try {
|
|
56062
|
-
log(`[${
|
|
56839
|
+
log(`[${HOOK_NAME13}] Injecting continuation`, {
|
|
56063
56840
|
sessionID,
|
|
56064
56841
|
agent: agentName,
|
|
56065
56842
|
model,
|
|
@@ -56074,14 +56851,14 @@ ${todoList}`;
|
|
|
56074
56851
|
},
|
|
56075
56852
|
query: { directory: ctx.directory }
|
|
56076
56853
|
});
|
|
56077
|
-
log(`[${
|
|
56854
|
+
log(`[${HOOK_NAME13}] Injection successful`, { sessionID });
|
|
56078
56855
|
if (injectionState) {
|
|
56079
56856
|
injectionState.inFlight = false;
|
|
56080
56857
|
injectionState.lastInjectedAt = Date.now();
|
|
56081
56858
|
injectionState.consecutiveFailures = 0;
|
|
56082
56859
|
}
|
|
56083
56860
|
} catch (error51) {
|
|
56084
|
-
log(`[${
|
|
56861
|
+
log(`[${HOOK_NAME13}] Injection failed`, { sessionID, error: String(error51) });
|
|
56085
56862
|
if (injectionState) {
|
|
56086
56863
|
injectionState.inFlight = false;
|
|
56087
56864
|
injectionState.lastInjectedAt = Date.now();
|
|
@@ -56099,7 +56876,9 @@ async function showCountdownToast(ctx, seconds, incompleteCount) {
|
|
|
56099
56876
|
variant: "warning",
|
|
56100
56877
|
duration: TOAST_DURATION_MS
|
|
56101
56878
|
}
|
|
56102
|
-
}).catch(() => {
|
|
56879
|
+
}).catch((err) => {
|
|
56880
|
+
log("[todo-continuation] Countdown toast failed:", err);
|
|
56881
|
+
});
|
|
56103
56882
|
}
|
|
56104
56883
|
function startCountdown(args) {
|
|
56105
56884
|
const {
|
|
@@ -56133,7 +56912,7 @@ function startCountdown(args) {
|
|
|
56133
56912
|
sessionStateStore
|
|
56134
56913
|
});
|
|
56135
56914
|
}, COUNTDOWN_SECONDS * 1000);
|
|
56136
|
-
log(`[${
|
|
56915
|
+
log(`[${HOOK_NAME13}] Countdown started`, {
|
|
56137
56916
|
sessionID,
|
|
56138
56917
|
seconds: COUNTDOWN_SECONDS,
|
|
56139
56918
|
incompleteCount
|
|
@@ -56150,16 +56929,16 @@ async function handleSessionIdle(args) {
|
|
|
56150
56929
|
skipAgents = DEFAULT_SKIP_AGENTS,
|
|
56151
56930
|
isContinuationStopped
|
|
56152
56931
|
} = args;
|
|
56153
|
-
log(`[${
|
|
56932
|
+
log(`[${HOOK_NAME13}] session.idle`, { sessionID });
|
|
56154
56933
|
const state3 = sessionStateStore.getState(sessionID);
|
|
56155
56934
|
if (state3.isRecovering) {
|
|
56156
|
-
log(`[${
|
|
56935
|
+
log(`[${HOOK_NAME13}] Skipped: in recovery`, { sessionID });
|
|
56157
56936
|
return;
|
|
56158
56937
|
}
|
|
56159
56938
|
if (state3.abortDetectedAt) {
|
|
56160
56939
|
const timeSinceAbort = Date.now() - state3.abortDetectedAt;
|
|
56161
56940
|
if (timeSinceAbort < ABORT_WINDOW_MS) {
|
|
56162
|
-
log(`[${
|
|
56941
|
+
log(`[${HOOK_NAME13}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
|
|
56163
56942
|
state3.abortDetectedAt = undefined;
|
|
56164
56943
|
return;
|
|
56165
56944
|
}
|
|
@@ -56167,7 +56946,7 @@ async function handleSessionIdle(args) {
|
|
|
56167
56946
|
}
|
|
56168
56947
|
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
|
|
56169
56948
|
if (hasRunningBgTasks) {
|
|
56170
|
-
log(`[${
|
|
56949
|
+
log(`[${HOOK_NAME13}] Skipped: background tasks running`, { sessionID });
|
|
56171
56950
|
return;
|
|
56172
56951
|
}
|
|
56173
56952
|
try {
|
|
@@ -56177,42 +56956,42 @@ async function handleSessionIdle(args) {
|
|
|
56177
56956
|
});
|
|
56178
56957
|
const messages = normalizeSDKResponse(messagesResp, []);
|
|
56179
56958
|
if (isLastAssistantMessageAborted(messages)) {
|
|
56180
|
-
log(`[${
|
|
56959
|
+
log(`[${HOOK_NAME13}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
|
|
56181
56960
|
return;
|
|
56182
56961
|
}
|
|
56183
56962
|
} catch (error51) {
|
|
56184
|
-
log(`[${
|
|
56963
|
+
log(`[${HOOK_NAME13}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
|
|
56185
56964
|
}
|
|
56186
56965
|
let todos = [];
|
|
56187
56966
|
try {
|
|
56188
56967
|
const response = await ctx.client.session.todo({ path: { id: sessionID } });
|
|
56189
56968
|
todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
|
|
56190
56969
|
} catch (error51) {
|
|
56191
|
-
log(`[${
|
|
56970
|
+
log(`[${HOOK_NAME13}] Todo fetch failed`, { sessionID, error: String(error51) });
|
|
56192
56971
|
return;
|
|
56193
56972
|
}
|
|
56194
56973
|
if (!todos || todos.length === 0) {
|
|
56195
|
-
log(`[${
|
|
56974
|
+
log(`[${HOOK_NAME13}] No todos`, { sessionID });
|
|
56196
56975
|
return;
|
|
56197
56976
|
}
|
|
56198
56977
|
const incompleteCount = getIncompleteCount(todos);
|
|
56199
56978
|
if (incompleteCount === 0) {
|
|
56200
|
-
log(`[${
|
|
56979
|
+
log(`[${HOOK_NAME13}] All todos complete`, { sessionID, total: todos.length });
|
|
56201
56980
|
return;
|
|
56202
56981
|
}
|
|
56203
56982
|
if (state3.inFlight) {
|
|
56204
|
-
log(`[${
|
|
56983
|
+
log(`[${HOOK_NAME13}] Skipped: injection in flight`, { sessionID });
|
|
56205
56984
|
return;
|
|
56206
56985
|
}
|
|
56207
56986
|
if (state3.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES && state3.lastInjectedAt && Date.now() - state3.lastInjectedAt >= FAILURE_RESET_WINDOW_MS) {
|
|
56208
56987
|
state3.consecutiveFailures = 0;
|
|
56209
|
-
log(`[${
|
|
56988
|
+
log(`[${HOOK_NAME13}] Reset consecutive failures after recovery window`, {
|
|
56210
56989
|
sessionID,
|
|
56211
56990
|
failureResetWindowMs: FAILURE_RESET_WINDOW_MS
|
|
56212
56991
|
});
|
|
56213
56992
|
}
|
|
56214
56993
|
if (state3.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
56215
|
-
log(`[${
|
|
56994
|
+
log(`[${HOOK_NAME13}] Skipped: max consecutive failures reached`, {
|
|
56216
56995
|
sessionID,
|
|
56217
56996
|
consecutiveFailures: state3.consecutiveFailures,
|
|
56218
56997
|
maxConsecutiveFailures: MAX_CONSECUTIVE_FAILURES
|
|
@@ -56221,7 +57000,7 @@ async function handleSessionIdle(args) {
|
|
|
56221
57000
|
}
|
|
56222
57001
|
const effectiveCooldown = CONTINUATION_COOLDOWN_MS2 * 2 ** Math.min(state3.consecutiveFailures, 5);
|
|
56223
57002
|
if (state3.lastInjectedAt && Date.now() - state3.lastInjectedAt < effectiveCooldown) {
|
|
56224
|
-
log(`[${
|
|
57003
|
+
log(`[${HOOK_NAME13}] Skipped: cooldown active`, {
|
|
56225
57004
|
sessionID,
|
|
56226
57005
|
effectiveCooldown,
|
|
56227
57006
|
consecutiveFailures: state3.consecutiveFailures
|
|
@@ -56251,20 +57030,20 @@ async function handleSessionIdle(args) {
|
|
|
56251
57030
|
}
|
|
56252
57031
|
}
|
|
56253
57032
|
} catch (error51) {
|
|
56254
|
-
log(`[${
|
|
57033
|
+
log(`[${HOOK_NAME13}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
|
|
56255
57034
|
}
|
|
56256
|
-
log(`[${
|
|
57035
|
+
log(`[${HOOK_NAME13}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
|
|
56257
57036
|
const resolvedAgentName = resolvedInfo?.agent;
|
|
56258
57037
|
if (resolvedAgentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
|
|
56259
|
-
log(`[${
|
|
57038
|
+
log(`[${HOOK_NAME13}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
|
|
56260
57039
|
return;
|
|
56261
57040
|
}
|
|
56262
57041
|
if (hasCompactionMessage && !resolvedInfo?.agent) {
|
|
56263
|
-
log(`[${
|
|
57042
|
+
log(`[${HOOK_NAME13}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
|
|
56264
57043
|
return;
|
|
56265
57044
|
}
|
|
56266
57045
|
if (isContinuationStopped?.(sessionID)) {
|
|
56267
|
-
log(`[${
|
|
57046
|
+
log(`[${HOOK_NAME13}] Skipped: continuation stopped for session`, { sessionID });
|
|
56268
57047
|
return;
|
|
56269
57048
|
}
|
|
56270
57049
|
startCountdown({
|
|
@@ -56294,7 +57073,7 @@ function handleNonIdleEvent(args) {
|
|
|
56294
57073
|
if (state3?.countdownStartedAt) {
|
|
56295
57074
|
const elapsed = Date.now() - state3.countdownStartedAt;
|
|
56296
57075
|
if (elapsed < COUNTDOWN_GRACE_PERIOD_MS) {
|
|
56297
|
-
log(`[${
|
|
57076
|
+
log(`[${HOOK_NAME13}] Ignoring user message in grace period`, { sessionID, elapsed });
|
|
56298
57077
|
return;
|
|
56299
57078
|
}
|
|
56300
57079
|
}
|
|
@@ -56338,7 +57117,7 @@ function handleNonIdleEvent(args) {
|
|
|
56338
57117
|
const sessionInfo = properties?.info;
|
|
56339
57118
|
if (sessionInfo?.id) {
|
|
56340
57119
|
sessionStateStore.cleanup(sessionInfo.id);
|
|
56341
|
-
log(`[${
|
|
57120
|
+
log(`[${HOOK_NAME13}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
|
|
56342
57121
|
}
|
|
56343
57122
|
return;
|
|
56344
57123
|
}
|
|
@@ -56363,10 +57142,10 @@ function createTodoContinuationHandler(args) {
|
|
|
56363
57142
|
if (error51?.name === "MessageAbortedError" || error51?.name === "AbortError") {
|
|
56364
57143
|
const state3 = sessionStateStore.getState(sessionID);
|
|
56365
57144
|
state3.abortDetectedAt = Date.now();
|
|
56366
|
-
log(`[${
|
|
57145
|
+
log(`[${HOOK_NAME13}] Abort detected via session.error`, { sessionID, errorName: error51.name });
|
|
56367
57146
|
}
|
|
56368
57147
|
sessionStateStore.cancelCountdown(sessionID);
|
|
56369
|
-
log(`[${
|
|
57148
|
+
log(`[${HOOK_NAME13}] session.error`, { sessionID });
|
|
56370
57149
|
return;
|
|
56371
57150
|
}
|
|
56372
57151
|
if (event.type === "session.idle") {
|
|
@@ -56481,13 +57260,13 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
56481
57260
|
const state3 = sessionStateStore.getState(sessionID);
|
|
56482
57261
|
state3.isRecovering = true;
|
|
56483
57262
|
sessionStateStore.cancelCountdown(sessionID);
|
|
56484
|
-
log(`[${
|
|
57263
|
+
log(`[${HOOK_NAME13}] Session marked as recovering`, { sessionID });
|
|
56485
57264
|
};
|
|
56486
57265
|
const markRecoveryComplete = (sessionID) => {
|
|
56487
57266
|
const state3 = sessionStateStore.getExistingState(sessionID);
|
|
56488
57267
|
if (state3) {
|
|
56489
57268
|
state3.isRecovering = false;
|
|
56490
|
-
log(`[${
|
|
57269
|
+
log(`[${HOOK_NAME13}] Session recovery complete`, { sessionID });
|
|
56491
57270
|
}
|
|
56492
57271
|
};
|
|
56493
57272
|
const handler = createTodoContinuationHandler({
|
|
@@ -56499,7 +57278,7 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
56499
57278
|
});
|
|
56500
57279
|
const cancelAllCountdowns = () => {
|
|
56501
57280
|
sessionStateStore.cancelAllCountdowns();
|
|
56502
|
-
log(`[${
|
|
57281
|
+
log(`[${HOOK_NAME13}] All countdowns cancelled`);
|
|
56503
57282
|
};
|
|
56504
57283
|
return {
|
|
56505
57284
|
handler,
|
|
@@ -56759,7 +57538,7 @@ This is a reminder only. No automatic action was taken.`;
|
|
|
56759
57538
|
}
|
|
56760
57539
|
// src/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.ts
|
|
56761
57540
|
init_logger();
|
|
56762
|
-
var
|
|
57541
|
+
var HOOK_NAME14 = "unstable-agent-babysitter";
|
|
56763
57542
|
var DEFAULT_TIMEOUT_MS = 120000;
|
|
56764
57543
|
var COOLDOWN_MS = 5 * 60 * 1000;
|
|
56765
57544
|
async function resolveMainSessionTarget(ctx, sessionID) {
|
|
@@ -56779,7 +57558,7 @@ async function resolveMainSessionTarget(ctx, sessionID) {
|
|
|
56779
57558
|
}
|
|
56780
57559
|
}
|
|
56781
57560
|
} catch (error51) {
|
|
56782
|
-
log(`[${
|
|
57561
|
+
log(`[${HOOK_NAME14}] Failed to resolve main session agent`, { sessionID, error: String(error51) });
|
|
56783
57562
|
}
|
|
56784
57563
|
return { agent, model };
|
|
56785
57564
|
}
|
|
@@ -56812,7 +57591,7 @@ async function getThinkingSummary(ctx, sessionID) {
|
|
|
56812
57591
|
return combined;
|
|
56813
57592
|
return `${combined.slice(0, THINKING_SUMMARY_MAX_CHARS)}...`;
|
|
56814
57593
|
} catch (error51) {
|
|
56815
|
-
log(`[${
|
|
57594
|
+
log(`[${HOOK_NAME14}] Failed to fetch thinking summary`, { sessionID, error: String(error51) });
|
|
56816
57595
|
return null;
|
|
56817
57596
|
}
|
|
56818
57597
|
}
|
|
@@ -56861,9 +57640,9 @@ function createUnstableAgentBabysitterHook(ctx, options) {
|
|
|
56861
57640
|
query: { directory: ctx.directory }
|
|
56862
57641
|
});
|
|
56863
57642
|
reminderCooldowns.set(task.id, now);
|
|
56864
|
-
log(`[${
|
|
57643
|
+
log(`[${HOOK_NAME14}] Reminder injected`, { taskId: task.id, sessionID: mainSessionID });
|
|
56865
57644
|
} catch (error51) {
|
|
56866
|
-
log(`[${
|
|
57645
|
+
log(`[${HOOK_NAME14}] Reminder injection failed`, { taskId: task.id, error: String(error51) });
|
|
56867
57646
|
}
|
|
56868
57647
|
}
|
|
56869
57648
|
};
|
|
@@ -58120,12 +58899,18 @@ class LSPClient extends LSPClientConnection {
|
|
|
58120
58899
|
});
|
|
58121
58900
|
}
|
|
58122
58901
|
}
|
|
58902
|
+
// src/tools/lsp/lsp-server.ts
|
|
58903
|
+
init_logger();
|
|
58904
|
+
|
|
58123
58905
|
// src/tools/lsp/lsp-manager-process-cleanup.ts
|
|
58906
|
+
init_logger();
|
|
58124
58907
|
function registerLspManagerProcessCleanup(options) {
|
|
58125
58908
|
const syncCleanup = () => {
|
|
58126
58909
|
for (const [, managed] of options.getClients()) {
|
|
58127
58910
|
try {
|
|
58128
|
-
managed.client.stop().catch(() => {
|
|
58911
|
+
managed.client.stop().catch((err) => {
|
|
58912
|
+
log("[lsp] Sync cleanup failed:", err);
|
|
58913
|
+
});
|
|
58129
58914
|
} catch {}
|
|
58130
58915
|
}
|
|
58131
58916
|
options.clearClients();
|
|
@@ -58134,17 +58919,26 @@ function registerLspManagerProcessCleanup(options) {
|
|
|
58134
58919
|
const asyncCleanup = async () => {
|
|
58135
58920
|
const stopPromises = [];
|
|
58136
58921
|
for (const [, managed] of options.getClients()) {
|
|
58137
|
-
stopPromises.push(managed.client.stop().catch(() => {
|
|
58922
|
+
stopPromises.push(managed.client.stop().catch((err) => {
|
|
58923
|
+
log("[lsp] Async cleanup failed:", err);
|
|
58924
|
+
return;
|
|
58925
|
+
}));
|
|
58138
58926
|
}
|
|
58139
58927
|
await Promise.allSettled(stopPromises);
|
|
58140
58928
|
options.clearClients();
|
|
58141
58929
|
options.clearCleanupInterval();
|
|
58142
58930
|
};
|
|
58143
58931
|
process.on("exit", syncCleanup);
|
|
58144
|
-
process.on("SIGINT", () => void asyncCleanup().catch(() => {
|
|
58145
|
-
|
|
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
|
+
}));
|
|
58146
58938
|
if (process.platform === "win32") {
|
|
58147
|
-
process.on("SIGBREAK", () => void asyncCleanup().catch(() => {
|
|
58939
|
+
process.on("SIGBREAK", () => void asyncCleanup().catch((err) => {
|
|
58940
|
+
log("[lsp] Signal handler cleanup failed:", err);
|
|
58941
|
+
}));
|
|
58148
58942
|
}
|
|
58149
58943
|
}
|
|
58150
58944
|
|
|
@@ -58314,7 +59108,9 @@ class LSPServerManager {
|
|
|
58314
59108
|
}
|
|
58315
59109
|
}).catch(() => {
|
|
58316
59110
|
this.clients.delete(key);
|
|
58317
|
-
client.stop().catch(() => {
|
|
59111
|
+
client.stop().catch((err) => {
|
|
59112
|
+
log("[lsp] Warmup client stop failed:", err);
|
|
59113
|
+
});
|
|
58318
59114
|
});
|
|
58319
59115
|
}
|
|
58320
59116
|
releaseClient(root, serverId) {
|
|
@@ -71172,6 +71968,7 @@ import { existsSync as existsSync48 } from "fs";
|
|
|
71172
71968
|
var {spawn: spawn9 } = globalThis.Bun;
|
|
71173
71969
|
|
|
71174
71970
|
// src/tools/ast-grep/cli-binary-path-resolution.ts
|
|
71971
|
+
init_logger();
|
|
71175
71972
|
import { existsSync as existsSync47 } from "fs";
|
|
71176
71973
|
|
|
71177
71974
|
// src/tools/ast-grep/language-support.ts
|
|
@@ -72798,6 +73595,9 @@ function createHandoffTools(ctx) {
|
|
|
72798
73595
|
});
|
|
72799
73596
|
return { handoff };
|
|
72800
73597
|
}
|
|
73598
|
+
// src/tools/interactive-bash/tools.ts
|
|
73599
|
+
init_logger();
|
|
73600
|
+
|
|
72801
73601
|
// src/tools/interactive-bash/constants.ts
|
|
72802
73602
|
var DEFAULT_TIMEOUT_MS5 = 60000;
|
|
72803
73603
|
var BLOCKED_TMUX_SUBCOMMANDS = [
|
|
@@ -72899,7 +73699,9 @@ The Bash tool can execute these commands directly. Do NOT retry with interactive
|
|
|
72899
73699
|
const timeoutError = new Error(`Timeout after ${DEFAULT_TIMEOUT_MS5}ms`);
|
|
72900
73700
|
try {
|
|
72901
73701
|
proc.kill();
|
|
72902
|
-
proc.exited.catch(() => {
|
|
73702
|
+
proc.exited.catch((err) => {
|
|
73703
|
+
log("[interactive-bash] Process cleanup failed:", err);
|
|
73704
|
+
});
|
|
72903
73705
|
} catch {}
|
|
72904
73706
|
reject(timeoutError);
|
|
72905
73707
|
}, DEFAULT_TIMEOUT_MS5);
|
|
@@ -74728,6 +75530,376 @@ function createBackgroundOutput(manager, client2) {
|
|
|
74728
75530
|
}
|
|
74729
75531
|
// src/tools/background-task/create-background-task.ts
|
|
74730
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
|
+
}
|
|
74731
75903
|
// src/tools/delegate-agent/constants.ts
|
|
74732
75904
|
var ALLOWED_AGENTS = [
|
|
74733
75905
|
"trinity",
|
|
@@ -74827,7 +75999,7 @@ Use \`background_output\` tool with task_id="${task.id}" to check progress:
|
|
|
74827
75999
|
// src/tools/delegate-agent/completion-poller.ts
|
|
74828
76000
|
async function waitForCompletion(sessionID, toolContext, ctx) {
|
|
74829
76001
|
log(`[delegate_agent] Polling for completion...`);
|
|
74830
|
-
const
|
|
76002
|
+
const POLL_INTERVAL_MS2 = 500;
|
|
74831
76003
|
const MAX_POLL_TIME_MS = 5 * 60 * 1000;
|
|
74832
76004
|
const pollStart = Date.now();
|
|
74833
76005
|
let lastMsgCount = 0;
|
|
@@ -74838,7 +76010,7 @@ async function waitForCompletion(sessionID, toolContext, ctx) {
|
|
|
74838
76010
|
log(`[delegate_agent] Aborted by user`);
|
|
74839
76011
|
throw new Error("Task aborted.");
|
|
74840
76012
|
}
|
|
74841
|
-
await new Promise((resolve10) => setTimeout(resolve10,
|
|
76013
|
+
await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS2));
|
|
74842
76014
|
const statusResult = await ctx.client.session.status();
|
|
74843
76015
|
const allStatuses = normalizeSDKResponse(statusResult, {});
|
|
74844
76016
|
const sessionStatus = allStatuses[sessionID];
|
|
@@ -75132,7 +76304,7 @@ ${task.agent ? `subagent: ${task.agent}
|
|
|
75132
76304
|
}
|
|
75133
76305
|
}
|
|
75134
76306
|
// src/tools/delegate-task/timing.ts
|
|
75135
|
-
var
|
|
76307
|
+
var POLL_INTERVAL_MS2 = 1000;
|
|
75136
76308
|
var MIN_STABILITY_TIME_MS = 1e4;
|
|
75137
76309
|
var STABILITY_POLLS_REQUIRED = 3;
|
|
75138
76310
|
var WAIT_FOR_SESSION_INTERVAL_MS = 100;
|
|
@@ -75141,7 +76313,7 @@ var MAX_POLL_TIME_MS = 10 * 60 * 1000;
|
|
|
75141
76313
|
var SESSION_CONTINUATION_STABILITY_MS = 5000;
|
|
75142
76314
|
function getTimingConfig() {
|
|
75143
76315
|
return {
|
|
75144
|
-
POLL_INTERVAL_MS,
|
|
76316
|
+
POLL_INTERVAL_MS: POLL_INTERVAL_MS2,
|
|
75145
76317
|
MIN_STABILITY_TIME_MS,
|
|
75146
76318
|
STABILITY_POLLS_REQUIRED,
|
|
75147
76319
|
WAIT_FOR_SESSION_INTERVAL_MS,
|
|
@@ -75644,6 +76816,8 @@ Create the work plan directly - that's your job as the planning agent.`
|
|
|
75644
76816
|
return { agentToUse, categoryModel };
|
|
75645
76817
|
}
|
|
75646
76818
|
// src/features/task-toast-manager/manager.ts
|
|
76819
|
+
init_logger();
|
|
76820
|
+
|
|
75647
76821
|
class TaskToastManager {
|
|
75648
76822
|
tasks = new Map;
|
|
75649
76823
|
client;
|
|
@@ -75763,7 +76937,9 @@ class TaskToastManager {
|
|
|
75763
76937
|
variant: "info",
|
|
75764
76938
|
duration: running.length + queued.length > 2 ? 5000 : 3000
|
|
75765
76939
|
}
|
|
75766
|
-
}).catch(() => {
|
|
76940
|
+
}).catch((err) => {
|
|
76941
|
+
log("[task-toast] Toast failed:", err);
|
|
76942
|
+
});
|
|
75767
76943
|
}
|
|
75768
76944
|
showCompletionToast(task) {
|
|
75769
76945
|
const tuiClient = this.client;
|
|
@@ -75785,7 +76961,9 @@ Still running: ${remaining.length} | Queued: ${queued.length}`;
|
|
|
75785
76961
|
variant: "success",
|
|
75786
76962
|
duration: 5000
|
|
75787
76963
|
}
|
|
75788
|
-
}).catch(() => {
|
|
76964
|
+
}).catch((err) => {
|
|
76965
|
+
log("[task-toast] Toast failed:", err);
|
|
76966
|
+
});
|
|
75789
76967
|
}
|
|
75790
76968
|
}
|
|
75791
76969
|
var instance = null;
|
|
@@ -76260,18 +77438,19 @@ async function sendSyncPrompt(client2, input, deps = sendSyncPromptDeps) {
|
|
|
76260
77438
|
try {
|
|
76261
77439
|
await deps.promptWithModelSuggestionRetry(client2, promptArgs);
|
|
76262
77440
|
} catch (promptError) {
|
|
77441
|
+
let lastError = promptError;
|
|
76263
77442
|
if (isOracleAgent2(input.agentToUse) && isUnexpectedEofError(promptError)) {
|
|
76264
77443
|
try {
|
|
76265
77444
|
await deps.promptSyncWithModelSuggestionRetry(client2, promptArgs);
|
|
76266
77445
|
return null;
|
|
76267
77446
|
} catch (oracleRetryError) {
|
|
76268
|
-
|
|
77447
|
+
lastError = oracleRetryError;
|
|
76269
77448
|
}
|
|
76270
77449
|
}
|
|
76271
77450
|
if (input.toastManager && input.taskId !== undefined) {
|
|
76272
77451
|
input.toastManager.removeTask(input.taskId);
|
|
76273
77452
|
}
|
|
76274
|
-
const errorMessage =
|
|
77453
|
+
const errorMessage = lastError instanceof Error ? lastError.message : String(lastError);
|
|
76275
77454
|
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
|
|
76276
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.`), {
|
|
76277
77456
|
operation: "Send prompt to agent",
|
|
@@ -76281,7 +77460,7 @@ async function sendSyncPrompt(client2, input, deps = sendSyncPromptDeps) {
|
|
|
76281
77460
|
category: input.args.category
|
|
76282
77461
|
});
|
|
76283
77462
|
}
|
|
76284
|
-
return formatDetailedError(
|
|
77463
|
+
return formatDetailedError(lastError, {
|
|
76285
77464
|
operation: "Send prompt",
|
|
76286
77465
|
args: input.args,
|
|
76287
77466
|
sessionID: input.sessionID,
|
|
@@ -76444,7 +77623,9 @@ session_id: ${sessionID}
|
|
|
76444
77623
|
} finally {
|
|
76445
77624
|
if (syncSessionID) {
|
|
76446
77625
|
subagentSessions.delete(syncSessionID);
|
|
76447
|
-
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
|
+
});
|
|
76448
77629
|
}
|
|
76449
77630
|
}
|
|
76450
77631
|
}
|
|
@@ -79171,10 +80352,6 @@ class ConcurrencyManager {
|
|
|
79171
80352
|
return this.queues.get(model)?.length ?? 0;
|
|
79172
80353
|
}
|
|
79173
80354
|
}
|
|
79174
|
-
// src/features/background-agent/manager.ts
|
|
79175
|
-
import { existsSync as existsSync57, readdirSync as readdirSync19, readFileSync as readFileSync39 } from "fs";
|
|
79176
|
-
import { join as join72 } from "path";
|
|
79177
|
-
|
|
79178
80355
|
// src/features/background-agent/constants.ts
|
|
79179
80356
|
var TASK_TTL_MS = 30 * 60 * 1000;
|
|
79180
80357
|
var MIN_STABILITY_TIME_MS2 = 10 * 1000;
|
|
@@ -79189,6 +80366,59 @@ var POLLING_INTERVAL_MS = 3000;
|
|
|
79189
80366
|
var PRUNE_THROTTLE_MS = 30000;
|
|
79190
80367
|
var TASK_CLEANUP_DELAY_MS = 10 * 60 * 1000;
|
|
79191
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
|
+
|
|
79192
80422
|
// src/features/background-agent/loop-detector.ts
|
|
79193
80423
|
function resolveCircuitBreakerSettings(config4) {
|
|
79194
80424
|
return {
|
|
@@ -79253,6 +80483,145 @@ function detectRepetitiveToolUse(window) {
|
|
|
79253
80483
|
};
|
|
79254
80484
|
}
|
|
79255
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
|
+
|
|
79256
80625
|
// src/features/background-agent/task-history.ts
|
|
79257
80626
|
var MAX_ENTRIES_PER_PARENT = 100;
|
|
79258
80627
|
|
|
@@ -79559,9 +80928,7 @@ class BackgroundManager {
|
|
|
79559
80928
|
this.concurrencyManager.release(existingTask.concurrencyKey);
|
|
79560
80929
|
existingTask.concurrencyKey = undefined;
|
|
79561
80930
|
}
|
|
79562
|
-
this.
|
|
79563
|
-
path: { id: sessionID }
|
|
79564
|
-
}).catch(() => {});
|
|
80931
|
+
this.abortSessionQuietly(sessionID, "error-recovery");
|
|
79565
80932
|
this.markForNotification(existingTask);
|
|
79566
80933
|
this.cleanupPendingByParent(existingTask);
|
|
79567
80934
|
this.enqueueNotificationForParent(existingTask.parentSessionID, () => this.notifyParentSession(existingTask)).catch((err) => {
|
|
@@ -79765,9 +81132,7 @@ class BackgroundManager {
|
|
|
79765
81132
|
existingTask.concurrencyKey = undefined;
|
|
79766
81133
|
}
|
|
79767
81134
|
if (existingTask.sessionID) {
|
|
79768
|
-
this.
|
|
79769
|
-
path: { id: existingTask.sessionID }
|
|
79770
|
-
}).catch(() => {});
|
|
81135
|
+
this.abortSessionQuietly(existingTask.sessionID, "resume-error");
|
|
79771
81136
|
}
|
|
79772
81137
|
this.markForNotification(existingTask);
|
|
79773
81138
|
this.cleanupPendingByParent(existingTask);
|
|
@@ -79922,7 +81287,7 @@ class BackgroundManager {
|
|
|
79922
81287
|
const task = this.findBySession(sessionID);
|
|
79923
81288
|
if (task?.status !== "running")
|
|
79924
81289
|
return;
|
|
79925
|
-
const errorMessage = props ?
|
|
81290
|
+
const errorMessage = props ? getSessionErrorMessage(props) : undefined;
|
|
79926
81291
|
task.status = "error";
|
|
79927
81292
|
task.error = errorMessage ?? "Session error";
|
|
79928
81293
|
task.completedAt = new Date;
|
|
@@ -80062,6 +81427,14 @@ class BackgroundManager {
|
|
|
80062
81427
|
}
|
|
80063
81428
|
}
|
|
80064
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
|
+
}
|
|
80065
81438
|
async cancelTask(taskId, options) {
|
|
80066
81439
|
const task = this.tasks.get(taskId);
|
|
80067
81440
|
if (!task || task.status !== "running" && task.status !== "pending") {
|
|
@@ -80106,9 +81479,7 @@ class BackgroundManager {
|
|
|
80106
81479
|
}
|
|
80107
81480
|
this.cleanupPendingByParent(task);
|
|
80108
81481
|
if (abortSession && task.sessionID) {
|
|
80109
|
-
this.
|
|
80110
|
-
path: { id: task.sessionID }
|
|
80111
|
-
}).catch(() => {});
|
|
81482
|
+
this.abortSessionQuietly(task.sessionID, "cancel");
|
|
80112
81483
|
}
|
|
80113
81484
|
if (options?.skipNotification) {
|
|
80114
81485
|
const toastManager = getTaskToastManager();
|
|
@@ -80223,9 +81594,7 @@ class BackgroundManager {
|
|
|
80223
81594
|
this.idleDeferralTimers.delete(task.id);
|
|
80224
81595
|
}
|
|
80225
81596
|
if (task.sessionID) {
|
|
80226
|
-
this.
|
|
80227
|
-
path: { id: task.sessionID }
|
|
80228
|
-
}).catch(() => {});
|
|
81597
|
+
this.abortSessionQuietly(task.sessionID, "complete");
|
|
80229
81598
|
}
|
|
80230
81599
|
try {
|
|
80231
81600
|
await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task));
|
|
@@ -80236,7 +81605,7 @@ class BackgroundManager {
|
|
|
80236
81605
|
return true;
|
|
80237
81606
|
}
|
|
80238
81607
|
async notifyParentSession(task) {
|
|
80239
|
-
const duration5 =
|
|
81608
|
+
const duration5 = formatDuration2(task.startedAt ?? new Date, task.completedAt);
|
|
80240
81609
|
log("[background-agent] notifyParentSession called for task:", task.id);
|
|
80241
81610
|
const toastManager = getTaskToastManager();
|
|
80242
81611
|
if (toastManager) {
|
|
@@ -80260,68 +81629,16 @@ class BackgroundManager {
|
|
|
80260
81629
|
allComplete = true;
|
|
80261
81630
|
}
|
|
80262
81631
|
const completedTasks = allComplete ? Array.from(this.tasks.values()).filter((t) => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending") : [];
|
|
80263
|
-
const statusText = task.status === "completed" ? "COMPLETED" : task.status === "interrupt" ? "INTERRUPTED" : "CANCELLED";
|
|
80264
81632
|
const errorInfo = task.error ? `
|
|
80265
81633
|
**Error:** ${task.error}` : "";
|
|
80266
|
-
|
|
80267
|
-
|
|
80268
|
-
|
|
80269
|
-
|
|
80270
|
-
|
|
80271
|
-
|
|
80272
|
-
|
|
80273
|
-
**Completed:**
|
|
80274
|
-
${completedTasksText || `- \`${task.id}\`: ${task.description}`}
|
|
80275
|
-
|
|
80276
|
-
Use \`background_output(task_id="<id>")\` to retrieve each result.
|
|
80277
|
-
</system-reminder>`;
|
|
80278
|
-
} else {
|
|
80279
|
-
notification = `<system-reminder>
|
|
80280
|
-
[BACKGROUND TASK ${statusText}]
|
|
80281
|
-
**ID:** \`${task.id}\`
|
|
80282
|
-
**Description:** ${task.description}
|
|
80283
|
-
**Duration:** ${duration5}${errorInfo}
|
|
80284
|
-
|
|
80285
|
-
**${remainingCount} task${remainingCount === 1 ? "" : "s"} still in progress.** You WILL be notified when ALL complete.
|
|
80286
|
-
Do NOT poll - continue productive work.
|
|
80287
|
-
|
|
80288
|
-
Use \`background_output(task_id="${task.id}")\` to retrieve this result when ready.
|
|
80289
|
-
</system-reminder>`;
|
|
80290
|
-
}
|
|
80291
|
-
let agent = task.parentAgent;
|
|
80292
|
-
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
|
+
});
|
|
80293
81641
|
if (this.enableParentSessionNotifications) {
|
|
80294
|
-
try {
|
|
80295
|
-
const messagesResp = await this.client.session.messages({ path: { id: task.parentSessionID } });
|
|
80296
|
-
const messages = normalizeSDKResponse(messagesResp, []);
|
|
80297
|
-
for (let i = messages.length - 1;i >= 0; i--) {
|
|
80298
|
-
const info = messages[i].info;
|
|
80299
|
-
if (isCompactionAgent(info?.agent)) {
|
|
80300
|
-
continue;
|
|
80301
|
-
}
|
|
80302
|
-
if (info?.agent || info?.model || info?.modelID && info?.providerID) {
|
|
80303
|
-
agent = info.agent ?? task.parentAgent;
|
|
80304
|
-
model = info.model ?? (info.providerID && info.modelID ? { providerID: info.providerID, modelID: info.modelID } : undefined);
|
|
80305
|
-
break;
|
|
80306
|
-
}
|
|
80307
|
-
}
|
|
80308
|
-
} catch (error95) {
|
|
80309
|
-
if (this.isAbortedSessionError(error95)) {
|
|
80310
|
-
log("[background-agent] Parent session aborted while loading messages; using messageDir fallback:", {
|
|
80311
|
-
taskId: task.id,
|
|
80312
|
-
parentSessionID: task.parentSessionID
|
|
80313
|
-
});
|
|
80314
|
-
}
|
|
80315
|
-
const messageDir = getMessageDir2(task.parentSessionID);
|
|
80316
|
-
const currentMessage = messageDir ? findNearestMessageExcludingCompaction(messageDir) : null;
|
|
80317
|
-
agent = currentMessage?.agent ?? task.parentAgent;
|
|
80318
|
-
model = currentMessage?.model?.providerID && currentMessage?.model?.modelID ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID } : undefined;
|
|
80319
|
-
}
|
|
80320
|
-
log("[background-agent] notifyParentSession context:", {
|
|
80321
|
-
taskId: task.id,
|
|
80322
|
-
resolvedAgent: agent,
|
|
80323
|
-
resolvedModel: model
|
|
80324
|
-
});
|
|
80325
81642
|
try {
|
|
80326
81643
|
await this.client.session.promptAsync({
|
|
80327
81644
|
path: { id: task.parentSessionID },
|
|
@@ -80339,7 +81656,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80339
81656
|
noReply: !allComplete
|
|
80340
81657
|
});
|
|
80341
81658
|
} catch (error95) {
|
|
80342
|
-
if (
|
|
81659
|
+
if (isAbortedSessionError(error95)) {
|
|
80343
81660
|
log("[background-agent] Parent session aborted while sending notification; continuing cleanup:", {
|
|
80344
81661
|
taskId: task.id,
|
|
80345
81662
|
parentSessionID: task.parentSessionID
|
|
@@ -80374,56 +81691,6 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80374
81691
|
}
|
|
80375
81692
|
}
|
|
80376
81693
|
}
|
|
80377
|
-
formatDuration(start, end) {
|
|
80378
|
-
const duration5 = (end ?? new Date).getTime() - start.getTime();
|
|
80379
|
-
const seconds = Math.floor(duration5 / 1000);
|
|
80380
|
-
const minutes = Math.floor(seconds / 60);
|
|
80381
|
-
const hours = Math.floor(minutes / 60);
|
|
80382
|
-
if (hours > 0) {
|
|
80383
|
-
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
80384
|
-
} else if (minutes > 0) {
|
|
80385
|
-
return `${minutes}m ${seconds % 60}s`;
|
|
80386
|
-
}
|
|
80387
|
-
return `${seconds}s`;
|
|
80388
|
-
}
|
|
80389
|
-
isAbortedSessionError(error95) {
|
|
80390
|
-
const message = this.getErrorText(error95);
|
|
80391
|
-
return message.toLowerCase().includes("aborted");
|
|
80392
|
-
}
|
|
80393
|
-
getErrorText(error95) {
|
|
80394
|
-
if (!error95)
|
|
80395
|
-
return "";
|
|
80396
|
-
if (typeof error95 === "string")
|
|
80397
|
-
return error95;
|
|
80398
|
-
if (error95 instanceof Error) {
|
|
80399
|
-
return `${error95.name}: ${error95.message}`;
|
|
80400
|
-
}
|
|
80401
|
-
if (typeof error95 === "object" && error95 !== null) {
|
|
80402
|
-
if ("message" in error95 && typeof error95.message === "string") {
|
|
80403
|
-
return error95.message;
|
|
80404
|
-
}
|
|
80405
|
-
if ("name" in error95 && typeof error95.name === "string") {
|
|
80406
|
-
return error95.name;
|
|
80407
|
-
}
|
|
80408
|
-
}
|
|
80409
|
-
return "";
|
|
80410
|
-
}
|
|
80411
|
-
isRecord(value) {
|
|
80412
|
-
return typeof value === "object" && value !== null;
|
|
80413
|
-
}
|
|
80414
|
-
getSessionErrorMessage(properties) {
|
|
80415
|
-
const errorRaw = properties.error;
|
|
80416
|
-
if (!this.isRecord(errorRaw))
|
|
80417
|
-
return;
|
|
80418
|
-
const dataRaw = errorRaw.data;
|
|
80419
|
-
if (this.isRecord(dataRaw)) {
|
|
80420
|
-
const message2 = dataRaw.message;
|
|
80421
|
-
if (typeof message2 === "string")
|
|
80422
|
-
return message2;
|
|
80423
|
-
}
|
|
80424
|
-
const message = errorRaw.message;
|
|
80425
|
-
return typeof message === "string" ? message : undefined;
|
|
80426
|
-
}
|
|
80427
81694
|
hasRunningTasks() {
|
|
80428
81695
|
for (const task of this.tasks.values()) {
|
|
80429
81696
|
if (task.status === "running")
|
|
@@ -80520,7 +81787,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80520
81787
|
this.concurrencyManager.release(task.concurrencyKey);
|
|
80521
81788
|
task.concurrencyKey = undefined;
|
|
80522
81789
|
}
|
|
80523
|
-
this.
|
|
81790
|
+
this.abortSessionQuietly(sessionID, "stale-no-progress");
|
|
80524
81791
|
log(`[background-agent] Task ${task.id} interrupted: no progress since start`);
|
|
80525
81792
|
try {
|
|
80526
81793
|
await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task));
|
|
@@ -80546,7 +81813,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80546
81813
|
this.concurrencyManager.release(task.concurrencyKey);
|
|
80547
81814
|
task.concurrencyKey = undefined;
|
|
80548
81815
|
}
|
|
80549
|
-
this.
|
|
81816
|
+
this.abortSessionQuietly(sessionID, "stale-timeout");
|
|
80550
81817
|
log(`[background-agent] Task ${task.id} interrupted: stale timeout`);
|
|
80551
81818
|
try {
|
|
80552
81819
|
await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task));
|
|
@@ -80616,9 +81883,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80616
81883
|
this.stopPolling();
|
|
80617
81884
|
for (const task of this.tasks.values()) {
|
|
80618
81885
|
if (task.status === "running" && task.sessionID) {
|
|
80619
|
-
this.
|
|
80620
|
-
path: { id: task.sessionID }
|
|
80621
|
-
}).catch(() => {});
|
|
81886
|
+
this.abortSessionQuietly(task.sessionID, "shutdown");
|
|
80622
81887
|
}
|
|
80623
81888
|
}
|
|
80624
81889
|
if (this.onShutdown) {
|
|
@@ -80657,13 +81922,17 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|
|
80657
81922
|
return operation();
|
|
80658
81923
|
}
|
|
80659
81924
|
const previous = this.notificationQueueByParent.get(parentSessionID) ?? Promise.resolve();
|
|
80660
|
-
const current = previous.catch(() => {
|
|
81925
|
+
const current = previous.catch((err) => {
|
|
81926
|
+
log("[background-agent] Previous notification failed, continuing queue:", err);
|
|
81927
|
+
}).then(operation);
|
|
80661
81928
|
this.notificationQueueByParent.set(parentSessionID, current);
|
|
80662
81929
|
current.finally(() => {
|
|
80663
81930
|
if (this.notificationQueueByParent.get(parentSessionID) === current) {
|
|
80664
81931
|
this.notificationQueueByParent.delete(parentSessionID);
|
|
80665
81932
|
}
|
|
80666
|
-
}).catch(() => {
|
|
81933
|
+
}).catch((err) => {
|
|
81934
|
+
log("[background-agent] Notification queue cleanup failed:", err);
|
|
81935
|
+
});
|
|
80667
81936
|
return current;
|
|
80668
81937
|
}
|
|
80669
81938
|
}
|
|
@@ -80678,72 +81947,6 @@ function registerProcessSignal(signal, handler, exitAfter) {
|
|
|
80678
81947
|
process.on(signal, listener);
|
|
80679
81948
|
return listener;
|
|
80680
81949
|
}
|
|
80681
|
-
function computeMessageDir(sessionID) {
|
|
80682
|
-
if (!existsSync57(MESSAGE_STORAGE))
|
|
80683
|
-
return null;
|
|
80684
|
-
const directPath = join72(MESSAGE_STORAGE, sessionID);
|
|
80685
|
-
if (existsSync57(directPath))
|
|
80686
|
-
return directPath;
|
|
80687
|
-
for (const dir of readdirSync19(MESSAGE_STORAGE)) {
|
|
80688
|
-
const sessionPath = join72(MESSAGE_STORAGE, dir, sessionID);
|
|
80689
|
-
if (existsSync57(sessionPath))
|
|
80690
|
-
return sessionPath;
|
|
80691
|
-
}
|
|
80692
|
-
return null;
|
|
80693
|
-
}
|
|
80694
|
-
var MESSAGE_DIR_CACHE_CAP = 1000;
|
|
80695
|
-
var messageDirCache = new Map;
|
|
80696
|
-
function getMessageDir2(sessionID) {
|
|
80697
|
-
const cached3 = messageDirCache.get(sessionID);
|
|
80698
|
-
if (cached3 !== undefined) {
|
|
80699
|
-
messageDirCache.delete(sessionID);
|
|
80700
|
-
messageDirCache.set(sessionID, cached3);
|
|
80701
|
-
return cached3;
|
|
80702
|
-
}
|
|
80703
|
-
const result = computeMessageDir(sessionID);
|
|
80704
|
-
if (messageDirCache.size >= MESSAGE_DIR_CACHE_CAP) {
|
|
80705
|
-
const oldest = messageDirCache.keys().next().value;
|
|
80706
|
-
if (oldest !== undefined)
|
|
80707
|
-
messageDirCache.delete(oldest);
|
|
80708
|
-
}
|
|
80709
|
-
messageDirCache.set(sessionID, result);
|
|
80710
|
-
return result;
|
|
80711
|
-
}
|
|
80712
|
-
function isCompactionAgent(agent) {
|
|
80713
|
-
return agent?.trim().toLowerCase() === "compaction";
|
|
80714
|
-
}
|
|
80715
|
-
function hasFullAgentAndModel(message) {
|
|
80716
|
-
return !!message.agent && !isCompactionAgent(message.agent) && !!message.model?.providerID && !!message.model?.modelID;
|
|
80717
|
-
}
|
|
80718
|
-
function hasPartialAgentOrModel(message) {
|
|
80719
|
-
const hasAgent = !!message.agent && !isCompactionAgent(message.agent);
|
|
80720
|
-
const hasModel = !!message.model?.providerID && !!message.model?.modelID;
|
|
80721
|
-
return hasAgent || hasModel;
|
|
80722
|
-
}
|
|
80723
|
-
function findNearestMessageExcludingCompaction(messageDir) {
|
|
80724
|
-
try {
|
|
80725
|
-
const files = readdirSync19(messageDir).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
80726
|
-
const parsedByFile = new Map;
|
|
80727
|
-
for (const file3 of files) {
|
|
80728
|
-
try {
|
|
80729
|
-
const content = readFileSync39(join72(messageDir, file3), "utf-8");
|
|
80730
|
-
const parsed = JSON.parse(content);
|
|
80731
|
-
parsedByFile.set(file3, parsed);
|
|
80732
|
-
if (hasFullAgentAndModel(parsed)) {
|
|
80733
|
-
return parsed;
|
|
80734
|
-
}
|
|
80735
|
-
} catch {}
|
|
80736
|
-
}
|
|
80737
|
-
for (const parsed of parsedByFile.values()) {
|
|
80738
|
-
if (hasPartialAgentOrModel(parsed)) {
|
|
80739
|
-
return parsed;
|
|
80740
|
-
}
|
|
80741
|
-
}
|
|
80742
|
-
return null;
|
|
80743
|
-
} catch {
|
|
80744
|
-
return null;
|
|
80745
|
-
}
|
|
80746
|
-
}
|
|
80747
81950
|
// src/features/background-agent/state.ts
|
|
80748
81951
|
class TaskStateManager {
|
|
80749
81952
|
tasks = new Map;
|
|
@@ -80916,6 +82119,7 @@ class TaskStateManager {
|
|
|
80916
82119
|
}
|
|
80917
82120
|
}
|
|
80918
82121
|
// src/features/skill-mcp-manager/cleanup.ts
|
|
82122
|
+
init_logger();
|
|
80919
82123
|
async function closeManagedClient(managed) {
|
|
80920
82124
|
try {
|
|
80921
82125
|
await managed.client.close();
|
|
@@ -80936,7 +82140,9 @@ function registerProcessCleanup(state3) {
|
|
|
80936
82140
|
state3.pendingConnections.clear();
|
|
80937
82141
|
};
|
|
80938
82142
|
const register = (signal) => {
|
|
80939
|
-
const listener = () => void cleanup().catch(() => {
|
|
82143
|
+
const listener = () => void cleanup().catch((err) => {
|
|
82144
|
+
log("[skill-mcp] Signal handler cleanup failed:", err);
|
|
82145
|
+
});
|
|
80940
82146
|
state3.cleanupHandlers.push({ signal, listener });
|
|
80941
82147
|
process.on(signal, listener);
|
|
80942
82148
|
};
|
|
@@ -80959,7 +82165,9 @@ function startCleanupTimer(state3) {
|
|
|
80959
82165
|
if (state3.cleanupInterval)
|
|
80960
82166
|
return;
|
|
80961
82167
|
state3.cleanupInterval = setInterval(() => {
|
|
80962
|
-
cleanupIdleClients(state3).catch(() => {
|
|
82168
|
+
cleanupIdleClients(state3).catch((err) => {
|
|
82169
|
+
log("[skill-mcp] Idle client cleanup failed:", err);
|
|
82170
|
+
});
|
|
80963
82171
|
}, 60000);
|
|
80964
82172
|
state3.cleanupInterval.unref();
|
|
80965
82173
|
}
|
|
@@ -84616,13 +85824,13 @@ class StreamableHTTPClientTransport {
|
|
|
84616
85824
|
this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));
|
|
84617
85825
|
return;
|
|
84618
85826
|
}
|
|
84619
|
-
const
|
|
85827
|
+
const delay6 = this._getNextReconnectionDelay(attemptCount);
|
|
84620
85828
|
this._reconnectionTimeout = setTimeout(() => {
|
|
84621
85829
|
this._startOrAuthSse(options).catch((error95) => {
|
|
84622
85830
|
this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error95 instanceof Error ? error95.message : String(error95)}`));
|
|
84623
85831
|
this._scheduleReconnection(options, attemptCount + 1);
|
|
84624
85832
|
});
|
|
84625
|
-
},
|
|
85833
|
+
}, delay6);
|
|
84626
85834
|
}
|
|
84627
85835
|
_handleSseStream(stream, options, isReconnectable) {
|
|
84628
85836
|
if (!stream) {
|
|
@@ -84933,7 +86141,7 @@ async function getOrRegisterClient(options) {
|
|
|
84933
86141
|
}
|
|
84934
86142
|
}
|
|
84935
86143
|
function parseRegistrationResponse(data) {
|
|
84936
|
-
if (!
|
|
86144
|
+
if (!isRecord6(data))
|
|
84937
86145
|
return null;
|
|
84938
86146
|
const clientId = data.client_id;
|
|
84939
86147
|
if (typeof clientId !== "string" || clientId.length === 0)
|
|
@@ -84944,7 +86152,7 @@ function parseRegistrationResponse(data) {
|
|
|
84944
86152
|
}
|
|
84945
86153
|
return { clientId };
|
|
84946
86154
|
}
|
|
84947
|
-
function
|
|
86155
|
+
function isRecord6(value) {
|
|
84948
86156
|
return typeof value === "object" && value !== null;
|
|
84949
86157
|
}
|
|
84950
86158
|
|
|
@@ -88290,7 +89498,7 @@ function collectPendingBuiltinAgents(input) {
|
|
|
88290
89498
|
if (oracleMetadata) {
|
|
88291
89499
|
availableAgents.push({
|
|
88292
89500
|
name: "oracle",
|
|
88293
|
-
description: "
|
|
89501
|
+
description: "Plan Builder agent. Creates detailed, structured work plans from user requests. (Oracle - Matrixx)",
|
|
88294
89502
|
metadata: oracleMetadata
|
|
88295
89503
|
});
|
|
88296
89504
|
}
|
|
@@ -89651,7 +90859,7 @@ createMultimodalLookerAgent.mode = MODE5;
|
|
|
89651
90859
|
function sanitizeMarkdownTableCell(value) {
|
|
89652
90860
|
return value.replace(/\r?\n/g, " ").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
89653
90861
|
}
|
|
89654
|
-
function
|
|
90862
|
+
function isRecord7(value) {
|
|
89655
90863
|
return typeof value === "object" && value !== null;
|
|
89656
90864
|
}
|
|
89657
90865
|
function parseRegisteredAgentSummaries(input) {
|
|
@@ -89659,7 +90867,7 @@ function parseRegisteredAgentSummaries(input) {
|
|
|
89659
90867
|
return [];
|
|
89660
90868
|
const result = [];
|
|
89661
90869
|
for (const item of input) {
|
|
89662
|
-
if (!
|
|
90870
|
+
if (!isRecord7(item))
|
|
89663
90871
|
continue;
|
|
89664
90872
|
const name = typeof item.name === "string" ? item.name : undefined;
|
|
89665
90873
|
if (!name)
|
|
@@ -89723,6 +90931,7 @@ var ORACLE_PROMPT_METADATA = {
|
|
|
89723
90931
|
var ORACLE_PLAN_BUILDER_METADATA = {
|
|
89724
90932
|
category: "advisor",
|
|
89725
90933
|
cost: "EXPENSIVE",
|
|
90934
|
+
keyTrigger: "Complex feature implementation, refactoring, or architecture design \u2192 consult Oracle for planning first",
|
|
89726
90935
|
triggers: [
|
|
89727
90936
|
{ domain: "Architecture decisions", trigger: "Multi-system tradeoffs, unfamiliar patterns" },
|
|
89728
90937
|
{ domain: "Work planning", trigger: "Complex feature implementation, refactoring" },
|
|
@@ -89854,7 +91063,7 @@ Before finalizing answers on architecture, security, or performance:
|
|
|
89854
91063
|
<delivery>
|
|
89855
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.
|
|
89856
91065
|
</delivery>`;
|
|
89857
|
-
function
|
|
91066
|
+
function createMerovingianAgent(model) {
|
|
89858
91067
|
const restrictions = createAgentToolRestrictions([
|
|
89859
91068
|
"write",
|
|
89860
91069
|
"edit",
|
|
@@ -89862,7 +91071,7 @@ function createOracleAgent(model) {
|
|
|
89862
91071
|
"task"
|
|
89863
91072
|
]);
|
|
89864
91073
|
const base = {
|
|
89865
|
-
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)",
|
|
89866
91075
|
mode: MODE6,
|
|
89867
91076
|
model,
|
|
89868
91077
|
temperature: 0.1,
|
|
@@ -89874,7 +91083,7 @@ function createOracleAgent(model) {
|
|
|
89874
91083
|
}
|
|
89875
91084
|
return { ...base, thinking: { type: "enabled", budgetTokens: 16000 } };
|
|
89876
91085
|
}
|
|
89877
|
-
|
|
91086
|
+
createMerovingianAgent.mode = MODE6;
|
|
89878
91087
|
|
|
89879
91088
|
// src/agents/operator.ts
|
|
89880
91089
|
var MODE7 = "subagent";
|
|
@@ -91191,7 +92400,7 @@ createExploreAgent.mode = MODE12;
|
|
|
91191
92400
|
var agentSources = {
|
|
91192
92401
|
morpheus: createMorpheusAgent,
|
|
91193
92402
|
keymaker: createKeymakerAgent,
|
|
91194
|
-
merovingian:
|
|
92403
|
+
merovingian: createMerovingianAgent,
|
|
91195
92404
|
operator: createLibrarianAgent,
|
|
91196
92405
|
trinity: createExploreAgent,
|
|
91197
92406
|
construct: createMultimodalLookerAgent,
|
|
@@ -94481,6 +95690,11 @@ function createToolRegistry(args) {
|
|
|
94481
95690
|
} : {};
|
|
94482
95691
|
const hashlineEnabled = pluginConfig.experimental?.hashline_edit ?? false;
|
|
94483
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;
|
|
94484
95698
|
const allTools = {
|
|
94485
95699
|
...builtinTools,
|
|
94486
95700
|
...createGrepTools(ctx),
|
|
@@ -94497,7 +95711,8 @@ function createToolRegistry(args) {
|
|
|
94497
95711
|
slashcommand: slashcommandTool,
|
|
94498
95712
|
interactive_bash,
|
|
94499
95713
|
...taskToolsRecord,
|
|
94500
|
-
...hashlineToolsRecord
|
|
95714
|
+
...hashlineToolsRecord,
|
|
95715
|
+
...assemblyTool ? { assembly: assemblyTool } : {}
|
|
94501
95716
|
};
|
|
94502
95717
|
const filteredTools = filterDisabledTools(allTools, pluginConfig.disabled_tools);
|
|
94503
95718
|
return {
|
|
@@ -94921,7 +96136,9 @@ var BuiltinSkillNameSchema = exports_external.enum([
|
|
|
94921
96136
|
"review-work",
|
|
94922
96137
|
"quality-gate",
|
|
94923
96138
|
"software-dev",
|
|
94924
|
-
"matrixx-self-config"
|
|
96139
|
+
"matrixx-self-config",
|
|
96140
|
+
"ulw-research",
|
|
96141
|
+
"remove-ai-slops"
|
|
94925
96142
|
]);
|
|
94926
96143
|
// src/config/schema/internal/permission.ts
|
|
94927
96144
|
var PermissionValueSchema = exports_external.enum(["ask", "allow", "deny"]);
|
|
@@ -94987,6 +96204,17 @@ var AgentOverridesSchema = exports_external.object({
|
|
|
94987
96204
|
sentinel: AgentOverrideConfigSchema.optional(),
|
|
94988
96205
|
sati: AgentOverrideConfigSchema.optional()
|
|
94989
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
|
+
});
|
|
94990
96218
|
// src/config/schema/babysitting.ts
|
|
94991
96219
|
var BabysittingConfigSchema = exports_external.object({
|
|
94992
96220
|
timeout_ms: exports_external.number().default(120000)
|
|
@@ -95066,7 +96294,11 @@ var BuiltinCommandNameSchema = exports_external.enum([
|
|
|
95066
96294
|
"start-work",
|
|
95067
96295
|
"stop-continuation",
|
|
95068
96296
|
"profile",
|
|
95069
|
-
"end-ultrawork"
|
|
96297
|
+
"end-ultrawork",
|
|
96298
|
+
"handoff",
|
|
96299
|
+
"research",
|
|
96300
|
+
"assembly",
|
|
96301
|
+
"ultrawork"
|
|
95070
96302
|
]);
|
|
95071
96303
|
// src/config/schema/comment-checker.ts
|
|
95072
96304
|
var CommentCheckerConfigSchema = exports_external.object({
|
|
@@ -95120,7 +96352,6 @@ var HookNameSchema = exports_external.enum([
|
|
|
95120
96352
|
"start-work",
|
|
95121
96353
|
"architect",
|
|
95122
96354
|
"unstable-agent-babysitter",
|
|
95123
|
-
"task-reminder",
|
|
95124
96355
|
"task-resume-info",
|
|
95125
96356
|
"stop-continuation-guard",
|
|
95126
96357
|
"tasks-todowrite-disabler",
|
|
@@ -95321,6 +96552,7 @@ var MatrixxConfigSchema = exports_external.object({
|
|
|
95321
96552
|
websearch: WebsearchConfigSchema.optional(),
|
|
95322
96553
|
tmux: TmuxConfigSchema.optional(),
|
|
95323
96554
|
morpheus: MorpheusConfigSchema.optional(),
|
|
96555
|
+
assembly: AssemblyConfigSchema.optional(),
|
|
95324
96556
|
security: SecurityConfigSchema.optional(),
|
|
95325
96557
|
runtime_fallback: RuntimeFallbackConfigSchema.optional(),
|
|
95326
96558
|
agent_definitions: AgentDefinitionsConfigSchema.optional(),
|
|
@@ -95468,6 +96700,7 @@ function loadPluginConfig(directory, ctx) {
|
|
|
95468
96700
|
}
|
|
95469
96701
|
|
|
95470
96702
|
// src/plugin/chat-message.ts
|
|
96703
|
+
init_logger();
|
|
95471
96704
|
function isStartWorkHookOutput(value) {
|
|
95472
96705
|
if (typeof value !== "object" || value === null)
|
|
95473
96706
|
return false;
|
|
@@ -95521,7 +96754,9 @@ function createChatMessageHandler2(args) {
|
|
|
95521
96754
|
variant: "warning",
|
|
95522
96755
|
duration: 6000
|
|
95523
96756
|
}
|
|
95524
|
-
}).catch(() => {
|
|
96757
|
+
}).catch((err) => {
|
|
96758
|
+
log("[plugin] Provider cache toast failed:", err);
|
|
96759
|
+
});
|
|
95525
96760
|
}
|
|
95526
96761
|
if (hooks2.matrixLoop) {
|
|
95527
96762
|
const parts = output.parts;
|
|
@@ -95548,11 +96783,11 @@ function createChatMessageHandler2(args) {
|
|
|
95548
96783
|
}
|
|
95549
96784
|
|
|
95550
96785
|
// src/plugin/chat-params.ts
|
|
95551
|
-
function
|
|
96786
|
+
function isRecord8(value) {
|
|
95552
96787
|
return typeof value === "object" && value !== null;
|
|
95553
96788
|
}
|
|
95554
96789
|
function buildChatParamsInput(raw) {
|
|
95555
|
-
if (!
|
|
96790
|
+
if (!isRecord8(raw))
|
|
95556
96791
|
return null;
|
|
95557
96792
|
const sessionID = raw.sessionID;
|
|
95558
96793
|
const agent = raw.agent;
|
|
@@ -95563,11 +96798,11 @@ function buildChatParamsInput(raw) {
|
|
|
95563
96798
|
return null;
|
|
95564
96799
|
if (typeof agent !== "string")
|
|
95565
96800
|
return null;
|
|
95566
|
-
if (!
|
|
96801
|
+
if (!isRecord8(model))
|
|
95567
96802
|
return null;
|
|
95568
|
-
if (!
|
|
96803
|
+
if (!isRecord8(provider))
|
|
95569
96804
|
return null;
|
|
95570
|
-
if (!
|
|
96805
|
+
if (!isRecord8(message))
|
|
95571
96806
|
return null;
|
|
95572
96807
|
const providerID = model.providerID;
|
|
95573
96808
|
const modelID = model.modelID;
|
|
@@ -95588,12 +96823,12 @@ function buildChatParamsInput(raw) {
|
|
|
95588
96823
|
};
|
|
95589
96824
|
}
|
|
95590
96825
|
function isChatParamsOutput(raw) {
|
|
95591
|
-
if (!
|
|
96826
|
+
if (!isRecord8(raw))
|
|
95592
96827
|
return false;
|
|
95593
|
-
if (!
|
|
96828
|
+
if (!isRecord8(raw.options)) {
|
|
95594
96829
|
raw.options = {};
|
|
95595
96830
|
}
|
|
95596
|
-
return
|
|
96831
|
+
return isRecord8(raw.options);
|
|
95597
96832
|
}
|
|
95598
96833
|
function createChatParamsHandler(args) {
|
|
95599
96834
|
return async (input, output) => {
|
|
@@ -95611,6 +96846,9 @@ function createChatParamsHandler(args) {
|
|
|
95611
96846
|
};
|
|
95612
96847
|
}
|
|
95613
96848
|
|
|
96849
|
+
// src/plugin/event.ts
|
|
96850
|
+
init_logger();
|
|
96851
|
+
|
|
95614
96852
|
// src/plugin/recent-synthetic-idles.ts
|
|
95615
96853
|
function pruneRecentSyntheticIdles(args) {
|
|
95616
96854
|
const { recentSyntheticIdles, recentRealIdles, now, dedupWindowMs } = args;
|
|
@@ -95754,7 +96992,9 @@ function createEventHandler2(args) {
|
|
|
95754
96992
|
path: { id: sessionID },
|
|
95755
96993
|
body: { parts: [{ type: "text", text: "continue" }] },
|
|
95756
96994
|
query: { directory: ctx.directory }
|
|
95757
|
-
}).catch(() => {
|
|
96995
|
+
}).catch((err) => {
|
|
96996
|
+
log("[plugin] Session prompt failed:", err);
|
|
96997
|
+
});
|
|
95758
96998
|
}
|
|
95759
96999
|
}
|
|
95760
97000
|
}
|
|
@@ -95868,6 +97108,25 @@ function createToolExecuteAfterHandler2(args) {
|
|
|
95868
97108
|
};
|
|
95869
97109
|
}
|
|
95870
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
|
+
}
|
|
95871
97130
|
// src/plugin/session-agent-resolver.ts
|
|
95872
97131
|
async function resolveSessionAgent(client2, sessionId, timeoutMs = 5000) {
|
|
95873
97132
|
try {
|
|
@@ -95990,6 +97249,32 @@ function createToolExecuteBeforeHandler2(args) {
|
|
|
95990
97249
|
sessionID
|
|
95991
97250
|
});
|
|
95992
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
|
+
}
|
|
95993
97278
|
}
|
|
95994
97279
|
};
|
|
95995
97280
|
}
|