opencode-matrixx 2.6.9 → 2.6.10
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 +1 -1
- package/dist/cli.js +2 -1
- package/dist/config/schema/hooks.d.ts +1 -0
- package/dist/config/schema/matrixx-config.d.ts +1 -0
- package/dist/create-hooks.d.ts +1 -0
- package/dist/hooks/document-reader-guard/constants.d.ts +5 -0
- package/dist/hooks/document-reader-guard/hook.d.ts +2 -0
- package/dist/hooks/document-reader-guard/index.d.ts +2 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/json-error-recovery/hook.d.ts +1 -1
- package/dist/index.js +816 -525
- package/dist/matrixx.schema.json +1 -0
- package/dist/mcp/types.d.ts +0 -1
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
- package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
- package/dist/tools/github-search/cli.d.ts +7 -0
- package/dist/tools/github-search/constants.d.ts +6 -0
- package/dist/tools/github-search/index.d.ts +1 -0
- package/dist/tools/github-search/result-formatter.d.ts +2 -0
- package/dist/tools/github-search/result-formatter.test.d.ts +1 -0
- package/dist/tools/github-search/tools.d.ts +3 -0
- package/dist/tools/github-search/types.d.ts +20 -0
- package/dist/tools/index.d.ts +1 -0
- package/package.json +1 -1
- package/dist/mcp/white-rabbit.d.ts +0 -6
package/dist/index.js
CHANGED
|
@@ -4540,7 +4540,7 @@ var require_picomatch2 = __commonJS(function(exports, module) {
|
|
|
4540
4540
|
});
|
|
4541
4541
|
|
|
4542
4542
|
// src/hooks/matrix-loop/constants.ts
|
|
4543
|
-
var
|
|
4543
|
+
var HOOK_NAME7 = "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;
|
|
4544
4544
|
|
|
4545
4545
|
// src/hooks/matrix-loop/storage.ts
|
|
4546
4546
|
var exports_storage = {};
|
|
@@ -29559,7 +29559,7 @@ var init_ulw_research = __esm(() => {
|
|
|
29559
29559
|
"glob",
|
|
29560
29560
|
"look_at",
|
|
29561
29561
|
"websearch_web_search_exa",
|
|
29562
|
-
"
|
|
29562
|
+
"github_search",
|
|
29563
29563
|
"context7_resolve-library-id",
|
|
29564
29564
|
"context7_query-docs"
|
|
29565
29565
|
],
|
|
@@ -29670,7 +29670,7 @@ task(
|
|
|
29670
29670
|
prompt="""
|
|
29671
29671
|
Search open source repositories for implementations related to: <CURRENT_LEAD>
|
|
29672
29672
|
|
|
29673
|
-
Use
|
|
29673
|
+
Use github_search to find real-world code patterns.
|
|
29674
29674
|
Focus on:
|
|
29675
29675
|
- Production implementations in similar projects
|
|
29676
29676
|
- Reference implementations or canonical examples
|
|
@@ -48167,7 +48167,7 @@ var TARGET_TOOLS = new Set([
|
|
|
48167
48167
|
"context7_query-docs",
|
|
48168
48168
|
"websearch_web_search_exa",
|
|
48169
48169
|
"context7_get-library-docs",
|
|
48170
|
-
"
|
|
48170
|
+
"github_search"
|
|
48171
48171
|
]);
|
|
48172
48172
|
var AGENT_TOOLS = new Set([
|
|
48173
48173
|
"task"
|
|
@@ -55863,6 +55863,80 @@ function createPostReadInjectorHook(ctx, options) {
|
|
|
55863
55863
|
function createDirectoryAgentsInjectorHook(ctx) {
|
|
55864
55864
|
return createPostReadInjectorHook(ctx, { trackedTools: ["read"] });
|
|
55865
55865
|
}
|
|
55866
|
+
// src/hooks/document-reader-guard/constants.ts
|
|
55867
|
+
var HOOK_NAME5 = "document-reader-guard";
|
|
55868
|
+
var BINARY_EXTENSIONS = [
|
|
55869
|
+
".pdf",
|
|
55870
|
+
".docx",
|
|
55871
|
+
".doc",
|
|
55872
|
+
".xlsx",
|
|
55873
|
+
".xls",
|
|
55874
|
+
".pptx",
|
|
55875
|
+
".ppt",
|
|
55876
|
+
".jpg",
|
|
55877
|
+
".jpeg",
|
|
55878
|
+
".png",
|
|
55879
|
+
".gif",
|
|
55880
|
+
".webp",
|
|
55881
|
+
".mp3",
|
|
55882
|
+
".wav"
|
|
55883
|
+
];
|
|
55884
|
+
var BLOCKED_PATTERNS = [
|
|
55885
|
+
/\bpdftotext\b/,
|
|
55886
|
+
/\bpdfinfo\b/,
|
|
55887
|
+
/\bpandoc\b.*\.(pdf|docx?|xlsx?|pptx?)/,
|
|
55888
|
+
/\blibreoffice\b|\bsoffice\b/,
|
|
55889
|
+
/\bcat\s+[^|&;]*\.(pdf|docx?|xlsx?|pptx?|jpe?g|png|gif|webp|mp3|wav)/,
|
|
55890
|
+
/\bhead\s+[^|&;]*\.(pdf|docx?|xlsx?|pptx?|jpe?g|png|gif|webp|mp3|wav)/,
|
|
55891
|
+
/\btail\s+[^|&;]*\.(pdf|docx?|xlsx?|pptx?|jpe?g|png|gif|webp|mp3|wav)/,
|
|
55892
|
+
/\bless\s+[^|&;]*\.(pdf|docx?|xlsx?|pptx?|jpe?g|png|gif|webp|mp3|wav)/,
|
|
55893
|
+
/\bpython3?\b.*(fitz|pymupdf|pdfminer|pdfplumber|python-pptx|\bdocx\b|openpyxl|pillow|\bPIL\b)/,
|
|
55894
|
+
/\bpython3?\b[^|&;]*\.(pdf|docx?|xlsx?|pptx?|jpe?g|png|gif|webp|mp3|wav)/,
|
|
55895
|
+
/\bmarkitdown\b/
|
|
55896
|
+
];
|
|
55897
|
+
var READ_BLOCK_MESSAGE = "[document-reader-guard] BLOCKED: Use document_reader__convert_to_markdown (document_reader MCP, Microsoft MarkItDown) for binary documents \u2014 generic Read cannot parse them. " + "Call convert_to_markdown with an absolute URI like file:///absolute/path/to/report.pdf, then summarize \u2014 do not dump raw output. " + "For context isolation delegate to the construct subagent so the main context stays clean.";
|
|
55898
|
+
var BASH_BLOCK_MESSAGE = "[document-reader-guard] BLOCKED: Do not read documents via bash CLIs (pdftotext, pdfinfo, pandoc, libreoffice, cat/head/tail, python fitz/pypdf/pdfminer/pdfplumber/docx/openpyxl, markitdown CLI). " + "Use document_reader__convert_to_markdown with file:///absolute/path instead \u2014 delegate to construct for summarization.";
|
|
55899
|
+
// src/hooks/document-reader-guard/hook.ts
|
|
55900
|
+
function getFilePath(args) {
|
|
55901
|
+
const raw = args?.filePath ?? args?.path ?? args?.file;
|
|
55902
|
+
return typeof raw === "string" ? raw : undefined;
|
|
55903
|
+
}
|
|
55904
|
+
function isBinaryDocument(filePath) {
|
|
55905
|
+
const normalized = filePath.toLowerCase().replace(/\\/g, "/").split("?")[0].split("#")[0];
|
|
55906
|
+
return BINARY_EXTENSIONS.some((ext) => normalized.endsWith(ext));
|
|
55907
|
+
}
|
|
55908
|
+
function createDocumentReaderGuardHook(ctx) {
|
|
55909
|
+
return {
|
|
55910
|
+
"tool.execute.before": async (input, output) => {
|
|
55911
|
+
const tool = input.tool?.toLowerCase();
|
|
55912
|
+
if (tool !== "read" && tool !== "bash")
|
|
55913
|
+
return;
|
|
55914
|
+
if (tool === "read") {
|
|
55915
|
+
const filePath = getFilePath(output.args);
|
|
55916
|
+
if (!filePath || !isBinaryDocument(filePath))
|
|
55917
|
+
return;
|
|
55918
|
+
log(`[${HOOK_NAME5}] BLOCKED generic Read on binary document \u2014 use document_reader MCP`, {
|
|
55919
|
+
sessionID: input.sessionID,
|
|
55920
|
+
tool: input.tool,
|
|
55921
|
+
filePath
|
|
55922
|
+
});
|
|
55923
|
+
throw new Error(READ_BLOCK_MESSAGE);
|
|
55924
|
+
}
|
|
55925
|
+
const args = output.args;
|
|
55926
|
+
const cmd = args?.command;
|
|
55927
|
+
if (!cmd)
|
|
55928
|
+
return;
|
|
55929
|
+
if (!BLOCKED_PATTERNS.some((rx) => rx.test(cmd)))
|
|
55930
|
+
return;
|
|
55931
|
+
ctx.directory;
|
|
55932
|
+
log(`[${HOOK_NAME5}] BLOCKED bash document read`, {
|
|
55933
|
+
sessionID: input.sessionID,
|
|
55934
|
+
command: cmd.slice(0, 120)
|
|
55935
|
+
});
|
|
55936
|
+
throw new Error(BASH_BLOCK_MESSAGE);
|
|
55937
|
+
}
|
|
55938
|
+
};
|
|
55939
|
+
}
|
|
55866
55940
|
// src/hooks/edit-error-recovery/hook.ts
|
|
55867
55941
|
var EDIT_ERROR_PATTERNS = [
|
|
55868
55942
|
"oldString and newString must be different",
|
|
@@ -59571,7 +59645,7 @@ var JSON_ERROR_TOOL_EXCLUDE_LIST = [
|
|
|
59571
59645
|
"grep",
|
|
59572
59646
|
"webfetch",
|
|
59573
59647
|
"look_at",
|
|
59574
|
-
"
|
|
59648
|
+
"github_search",
|
|
59575
59649
|
"websearch_web_search_exa"
|
|
59576
59650
|
];
|
|
59577
59651
|
var JSON_ERROR_PATTERNS = [
|
|
@@ -59616,18 +59690,18 @@ ${JSON_ERROR_REMINDER}`;
|
|
|
59616
59690
|
}
|
|
59617
59691
|
// src/features/ultrawork-state/manager.ts
|
|
59618
59692
|
init_logger();
|
|
59619
|
-
var
|
|
59693
|
+
var HOOK_NAME6 = "ultrawork-state";
|
|
59620
59694
|
var sessionStates = new Map;
|
|
59621
59695
|
function getUltraworkState(sessionID) {
|
|
59622
59696
|
return sessionStates.get(sessionID);
|
|
59623
59697
|
}
|
|
59624
59698
|
function enableUltrawork(sessionID) {
|
|
59625
59699
|
sessionStates.set(sessionID, "enabled");
|
|
59626
|
-
log(`[${
|
|
59700
|
+
log(`[${HOOK_NAME6}] Ultrawork enabled for session`, { sessionID });
|
|
59627
59701
|
}
|
|
59628
59702
|
function disableUltrawork(sessionID) {
|
|
59629
59703
|
sessionStates.set(sessionID, "disabled");
|
|
59630
|
-
log(`[${
|
|
59704
|
+
log(`[${HOOK_NAME6}] Ultrawork disabled for session`, { sessionID });
|
|
59631
59705
|
}
|
|
59632
59706
|
// src/hooks/keyword-detector/hook.ts
|
|
59633
59707
|
function createKeywordDetectorHook(ctx, _collector) {
|
|
@@ -59760,7 +59834,7 @@ function createLoopStateController(options) {
|
|
|
59760
59834
|
};
|
|
59761
59835
|
const success2 = writeState(directory, state3, stateDir);
|
|
59762
59836
|
if (success2) {
|
|
59763
|
-
log(`[${
|
|
59837
|
+
log(`[${HOOK_NAME7}] Loop started`, {
|
|
59764
59838
|
sessionID,
|
|
59765
59839
|
maxIterations: state3.max_iterations,
|
|
59766
59840
|
completionPromise: state3.completion_promise
|
|
@@ -59775,7 +59849,7 @@ function createLoopStateController(options) {
|
|
|
59775
59849
|
}
|
|
59776
59850
|
const success2 = clearState(directory, stateDir);
|
|
59777
59851
|
if (success2) {
|
|
59778
|
-
log(`[${
|
|
59852
|
+
log(`[${HOOK_NAME7}] Loop cancelled`, { sessionID, iteration: state3.iteration });
|
|
59779
59853
|
}
|
|
59780
59854
|
return success2;
|
|
59781
59855
|
},
|
|
@@ -59852,7 +59926,7 @@ async function detectCompletionInSessionMessages(ctx, options) {
|
|
|
59852
59926
|
return false;
|
|
59853
59927
|
} catch (err) {
|
|
59854
59928
|
setTimeout(() => {
|
|
59855
|
-
log(`[${
|
|
59929
|
+
log(`[${HOOK_NAME7}] Session messages check failed`, {
|
|
59856
59930
|
sessionID: options.sessionID,
|
|
59857
59931
|
error: String(err)
|
|
59858
59932
|
});
|
|
@@ -59933,7 +60007,7 @@ async function verifyCompletion(ctx, options) {
|
|
|
59933
60007
|
query: { directory: options.directory }
|
|
59934
60008
|
});
|
|
59935
60009
|
if (createResult.error || !createResult.data?.id) {
|
|
59936
|
-
log(`[${
|
|
60010
|
+
log(`[${HOOK_NAME7}] Verification session creation failed`);
|
|
59937
60011
|
return { verified: true };
|
|
59938
60012
|
}
|
|
59939
60013
|
verifySessionID = createResult.data.id;
|
|
@@ -59963,16 +60037,16 @@ async function verifyCompletion(ctx, options) {
|
|
|
59963
60037
|
return parseVerificationResponse(assistantText);
|
|
59964
60038
|
}
|
|
59965
60039
|
} catch {
|
|
59966
|
-
log(`[${
|
|
60040
|
+
log(`[${HOOK_NAME7}] Verification poll error`);
|
|
59967
60041
|
}
|
|
59968
60042
|
await new Promise((resolve4) => {
|
|
59969
60043
|
setTimeout(resolve4, pollIntervalMs);
|
|
59970
60044
|
});
|
|
59971
60045
|
}
|
|
59972
|
-
log(`[${
|
|
60046
|
+
log(`[${HOOK_NAME7}] Verification timed out, assuming pass`);
|
|
59973
60047
|
return { verified: true };
|
|
59974
60048
|
} catch (err) {
|
|
59975
|
-
log(`[${
|
|
60049
|
+
log(`[${HOOK_NAME7}] Verification error: ${String(err)}`);
|
|
59976
60050
|
return { verified: true };
|
|
59977
60051
|
} finally {
|
|
59978
60052
|
if (verifySessionID) {
|
|
@@ -60048,7 +60122,7 @@ async function injectContinuationPrompt(ctx, options) {
|
|
|
60048
60122
|
|
|
60049
60123
|
// src/hooks/matrix-loop/matrix-loop-event-handler.ts
|
|
60050
60124
|
async function handleVerificationFailure(ctx, options, state3, sessionID, reason, failedCount) {
|
|
60051
|
-
log(`[${
|
|
60125
|
+
log(`[${HOOK_NAME7}] Verification failed`, { sessionID, reason });
|
|
60052
60126
|
const newState = options.loopState.incrementIteration();
|
|
60053
60127
|
if (newState) {
|
|
60054
60128
|
newState.verification_failed_count = failedCount + 1;
|
|
@@ -60066,7 +60140,7 @@ ${buildContinuationPrompt(newState ?? state3)}`,
|
|
|
60066
60140
|
apiTimeoutMs: options.apiTimeoutMs
|
|
60067
60141
|
});
|
|
60068
60142
|
} catch (err) {
|
|
60069
|
-
log(`[${
|
|
60143
|
+
log(`[${HOOK_NAME7}] Failed to inject verification feedback`, { sessionID, error: String(err) });
|
|
60070
60144
|
}
|
|
60071
60145
|
}
|
|
60072
60146
|
function createMatrixLoopEventHandler(ctx, options) {
|
|
@@ -60078,13 +60152,13 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
60078
60152
|
if (!sessionID)
|
|
60079
60153
|
return;
|
|
60080
60154
|
if (inFlightSessions.has(sessionID)) {
|
|
60081
|
-
log(`[${
|
|
60155
|
+
log(`[${HOOK_NAME7}] Skipped: handler in flight`, { sessionID });
|
|
60082
60156
|
return;
|
|
60083
60157
|
}
|
|
60084
60158
|
inFlightSessions.add(sessionID);
|
|
60085
60159
|
try {
|
|
60086
60160
|
if (options.sessionRecovery.isRecovering(sessionID)) {
|
|
60087
|
-
log(`[${
|
|
60161
|
+
log(`[${HOOK_NAME7}] Skipped: in recovery`, { sessionID });
|
|
60088
60162
|
return;
|
|
60089
60163
|
}
|
|
60090
60164
|
const state3 = options.loopState.getState();
|
|
@@ -60096,11 +60170,11 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
60096
60170
|
const exists = await options.checkSessionExists(state3.session_id);
|
|
60097
60171
|
if (!exists) {
|
|
60098
60172
|
options.loopState.clear();
|
|
60099
|
-
log(`[${
|
|
60173
|
+
log(`[${HOOK_NAME7}] Cleared orphaned state from deleted session`, { orphanedSessionId: state3.session_id, currentSessionId: sessionID });
|
|
60100
60174
|
return;
|
|
60101
60175
|
}
|
|
60102
60176
|
} catch (err) {
|
|
60103
|
-
log(`[${
|
|
60177
|
+
log(`[${HOOK_NAME7}] Failed to check session existence`, { sessionId: state3.session_id, error: String(err) });
|
|
60104
60178
|
}
|
|
60105
60179
|
}
|
|
60106
60180
|
return;
|
|
@@ -60119,7 +60193,7 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
60119
60193
|
messages = Array.isArray(messagesResponse) ? messagesResponse : Array.isArray(responseData) ? responseData : [];
|
|
60120
60194
|
} catch (err) {
|
|
60121
60195
|
setTimeout(() => {
|
|
60122
|
-
log(`[${
|
|
60196
|
+
log(`[${HOOK_NAME7}] Session messages fetch failed`, { sessionID, error: String(err) });
|
|
60123
60197
|
}, 0);
|
|
60124
60198
|
messages = [];
|
|
60125
60199
|
}
|
|
@@ -60132,7 +60206,7 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
60132
60206
|
preFetchedMessages: messages
|
|
60133
60207
|
});
|
|
60134
60208
|
if (completionViaTranscript || completionViaApi) {
|
|
60135
|
-
log(`[${
|
|
60209
|
+
log(`[${HOOK_NAME7}] Completion detected!`, { sessionID, iteration: state3.iteration, promise: state3.completion_promise, detectedVia: completionViaTranscript ? "transcript_file" : "session_messages_api" });
|
|
60136
60210
|
if (options.verification?.enabled === true) {
|
|
60137
60211
|
const maxRetries = options.verification?.maxRetries ?? DEFAULT_VERIFICATION_MAX_RETRIES;
|
|
60138
60212
|
const failedCount = state3.verification_failed_count ?? 0;
|
|
@@ -60167,7 +60241,7 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
60167
60241
|
return;
|
|
60168
60242
|
}
|
|
60169
60243
|
if (state3.iteration >= state3.max_iterations) {
|
|
60170
|
-
log(`[${
|
|
60244
|
+
log(`[${HOOK_NAME7}] Max iterations reached`, { sessionID, iteration: state3.iteration, max: state3.max_iterations });
|
|
60171
60245
|
options.loopState.clear();
|
|
60172
60246
|
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) => {
|
|
60173
60247
|
log("[matrix-loop] Max iterations toast failed:", err);
|
|
@@ -60176,17 +60250,17 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
60176
60250
|
}
|
|
60177
60251
|
const newState = options.loopState.incrementIteration();
|
|
60178
60252
|
if (!newState) {
|
|
60179
|
-
log(`[${
|
|
60253
|
+
log(`[${HOOK_NAME7}] Failed to increment iteration`, { sessionID });
|
|
60180
60254
|
return;
|
|
60181
60255
|
}
|
|
60182
|
-
log(`[${
|
|
60256
|
+
log(`[${HOOK_NAME7}] Continuing loop`, { sessionID, iteration: newState.iteration, max: newState.max_iterations });
|
|
60183
60257
|
await ctx.client.tui.showToast({ body: { title: "Matrix Loop", message: `Iteration ${newState.iteration}/${newState.max_iterations}`, variant: "info", duration: 2000 } }).catch((err) => {
|
|
60184
60258
|
log("[matrix-loop] Iteration toast failed:", err);
|
|
60185
60259
|
});
|
|
60186
60260
|
try {
|
|
60187
60261
|
await injectContinuationPrompt(ctx, { sessionID, prompt: buildContinuationPrompt(newState), directory: options.directory, apiTimeoutMs: options.apiTimeoutMs });
|
|
60188
60262
|
} catch (err) {
|
|
60189
|
-
log(`[${
|
|
60263
|
+
log(`[${HOOK_NAME7}] Failed to inject continuation`, { sessionID, error: String(err) });
|
|
60190
60264
|
}
|
|
60191
60265
|
return;
|
|
60192
60266
|
} finally {
|
|
@@ -60200,7 +60274,7 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
60200
60274
|
const state3 = options.loopState.getState();
|
|
60201
60275
|
if (state3?.session_id === sessionInfo.id) {
|
|
60202
60276
|
options.loopState.clear();
|
|
60203
|
-
log(`[${
|
|
60277
|
+
log(`[${HOOK_NAME7}] Session deleted, loop cleared`, { sessionID: sessionInfo.id });
|
|
60204
60278
|
}
|
|
60205
60279
|
options.sessionRecovery.clear(sessionInfo.id);
|
|
60206
60280
|
return;
|
|
@@ -60213,7 +60287,7 @@ function createMatrixLoopEventHandler(ctx, options) {
|
|
|
60213
60287
|
const state3 = options.loopState.getState();
|
|
60214
60288
|
if (state3?.session_id === sessionID) {
|
|
60215
60289
|
options.loopState.clear();
|
|
60216
|
-
log(`[${
|
|
60290
|
+
log(`[${HOOK_NAME7}] User aborted, loop cleared`, { sessionID });
|
|
60217
60291
|
}
|
|
60218
60292
|
options.sessionRecovery.clear(sessionID);
|
|
60219
60293
|
}
|
|
@@ -60260,7 +60334,7 @@ function createMatrixLoopHook(ctx, options) {
|
|
|
60260
60334
|
// src/hooks/matrix-loop/index.ts
|
|
60261
60335
|
init_storage();
|
|
60262
60336
|
// src/hooks/mouse-notepad/constants.ts
|
|
60263
|
-
var
|
|
60337
|
+
var HOOK_NAME8 = "mouse-notepad";
|
|
60264
60338
|
var NOTEPAD_DIRECTIVE = `
|
|
60265
60339
|
<Work_Context>
|
|
60266
60340
|
## Notepad Location (for recording learnings)
|
|
@@ -60307,14 +60381,14 @@ function createMouseNotepadHook(ctx) {
|
|
|
60307
60381
|
return;
|
|
60308
60382
|
}
|
|
60309
60383
|
output.args.prompt = NOTEPAD_DIRECTIVE + prompt;
|
|
60310
|
-
log(`[${
|
|
60384
|
+
log(`[${HOOK_NAME8}] Injected notepad directive to task`, {
|
|
60311
60385
|
sessionID: input.sessionID
|
|
60312
60386
|
});
|
|
60313
60387
|
}
|
|
60314
60388
|
};
|
|
60315
60389
|
}
|
|
60316
60390
|
// src/hooks/non-interactive-env/constants.ts
|
|
60317
|
-
var
|
|
60391
|
+
var HOOK_NAME9 = "non-interactive-env";
|
|
60318
60392
|
var NON_INTERACTIVE_ENV = {
|
|
60319
60393
|
CI: "true",
|
|
60320
60394
|
DEBIAN_FRONTEND: "noninteractive",
|
|
@@ -60405,7 +60479,7 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
60405
60479
|
}
|
|
60406
60480
|
const envPrefix = buildEnvPrefix(NON_INTERACTIVE_ENV, "unix");
|
|
60407
60481
|
output.args.command = `${envPrefix} ${command}`;
|
|
60408
|
-
log(`[${
|
|
60482
|
+
log(`[${HOOK_NAME9}] Prepended non-interactive env vars to git command`, {
|
|
60409
60483
|
sessionID: input.sessionID,
|
|
60410
60484
|
envPrefix
|
|
60411
60485
|
});
|
|
@@ -60413,7 +60487,7 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
60413
60487
|
};
|
|
60414
60488
|
}
|
|
60415
60489
|
// src/hooks/oracle-md-only/constants.ts
|
|
60416
|
-
var
|
|
60490
|
+
var HOOK_NAME10 = "oracle-md-only";
|
|
60417
60491
|
var ORACLE_AGENT = "oracle";
|
|
60418
60492
|
var ALLOWED_EXTENSIONS = [".md"];
|
|
60419
60493
|
var BLOCKED_TOOLS = ["Write", "Edit", "write", "edit"];
|
|
@@ -60546,7 +60620,7 @@ function createOracleMdOnlyHook(ctx) {
|
|
|
60546
60620
|
const prompt = output.args.prompt;
|
|
60547
60621
|
if (prompt && !prompt.includes(SYSTEM_DIRECTIVE_PREFIX)) {
|
|
60548
60622
|
output.args.prompt = PLANNING_CONSULT_WARNING + prompt;
|
|
60549
|
-
log(`[${
|
|
60623
|
+
log(`[${HOOK_NAME10}] Injected read-only planning warning to ${toolName}`, {
|
|
60550
60624
|
sessionID: input.sessionID,
|
|
60551
60625
|
tool: toolName,
|
|
60552
60626
|
agent: agentName
|
|
@@ -60562,17 +60636,17 @@ function createOracleMdOnlyHook(ctx) {
|
|
|
60562
60636
|
return;
|
|
60563
60637
|
}
|
|
60564
60638
|
if (!isAllowedFile(filePath, ctx.directory)) {
|
|
60565
|
-
log(`[${
|
|
60639
|
+
log(`[${HOOK_NAME10}] Blocked: Oracle can only write to .matrixx/*.md`, {
|
|
60566
60640
|
sessionID: input.sessionID,
|
|
60567
60641
|
tool: toolName,
|
|
60568
60642
|
filePath,
|
|
60569
60643
|
agent: agentName
|
|
60570
60644
|
});
|
|
60571
|
-
throw new Error(`[${
|
|
60645
|
+
throw new Error(`[${HOOK_NAME10}] ${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`);
|
|
60572
60646
|
}
|
|
60573
60647
|
const normalizedPath = filePath.toLowerCase().replace(/\\/g, "/");
|
|
60574
60648
|
if (normalizedPath.includes(".matrixx/plans/") || normalizedPath.includes(".matrixx\\plans\\")) {
|
|
60575
|
-
log(`[${
|
|
60649
|
+
log(`[${HOOK_NAME10}] Injecting workflow reminder for plan write`, {
|
|
60576
60650
|
sessionID: input.sessionID,
|
|
60577
60651
|
tool: toolName,
|
|
60578
60652
|
filePath,
|
|
@@ -60580,7 +60654,7 @@ function createOracleMdOnlyHook(ctx) {
|
|
|
60580
60654
|
});
|
|
60581
60655
|
output.message = (output.message || "") + ORACLE_WORKFLOW_REMINDER;
|
|
60582
60656
|
}
|
|
60583
|
-
log(`[${
|
|
60657
|
+
log(`[${HOOK_NAME10}] Allowed: .matrixx/*.md write permitted`, {
|
|
60584
60658
|
sessionID: input.sessionID,
|
|
60585
60659
|
tool: toolName,
|
|
60586
60660
|
filePath,
|
|
@@ -60919,7 +60993,7 @@ var TaskDeleteInputSchema = exports_external.object({
|
|
|
60919
60993
|
});
|
|
60920
60994
|
|
|
60921
60995
|
// src/hooks/plan-persister/hook.ts
|
|
60922
|
-
var
|
|
60996
|
+
var HOOK_NAME11 = "plan-persister";
|
|
60923
60997
|
function createPlanPersister(ctx, options) {
|
|
60924
60998
|
const { directory } = options;
|
|
60925
60999
|
const capture = async (sessionID) => {
|
|
@@ -60954,7 +61028,7 @@ function createPlanPersister(ctx, options) {
|
|
|
60954
61028
|
const response = await ctx.client.session.todo({ path: { id: sessionID } });
|
|
60955
61029
|
todos = normalizeTodos(response);
|
|
60956
61030
|
} catch (err) {
|
|
60957
|
-
log(`[${
|
|
61031
|
+
log(`[${HOOK_NAME11}] Failed to fetch todos`, { sessionID, error: String(err) });
|
|
60958
61032
|
return;
|
|
60959
61033
|
}
|
|
60960
61034
|
}
|
|
@@ -60977,9 +61051,9 @@ function createPlanPersister(ctx, options) {
|
|
|
60977
61051
|
ensurePlanDir(directory);
|
|
60978
61052
|
const ok = atomicWrite(planPath, finalContent);
|
|
60979
61053
|
if (ok) {
|
|
60980
|
-
log(`[${
|
|
61054
|
+
log(`[${HOOK_NAME11}] Plan file updated`, { planPath, completed, total });
|
|
60981
61055
|
} else {
|
|
60982
|
-
log(`[${
|
|
61056
|
+
log(`[${HOOK_NAME11}] Failed to write plan file`, { planPath });
|
|
60983
61057
|
}
|
|
60984
61058
|
};
|
|
60985
61059
|
const event = async ({ event: evt }) => {
|
|
@@ -62341,7 +62415,7 @@ var RETRYABLE_ERROR_PATTERNS = [
|
|
|
62341
62415
|
/(?:^|\s)503(?:\s|$)/,
|
|
62342
62416
|
/(?:^|\s)529(?:\s|$)/
|
|
62343
62417
|
];
|
|
62344
|
-
var
|
|
62418
|
+
var HOOK_NAME12 = "runtime-fallback";
|
|
62345
62419
|
|
|
62346
62420
|
// src/hooks/runtime-fallback/fallback-models.ts
|
|
62347
62421
|
init_logger();
|
|
@@ -62430,7 +62504,7 @@ function getRawFallbackModelsForSession(sessionID, agent, pluginConfig) {
|
|
|
62430
62504
|
if (result)
|
|
62431
62505
|
return result;
|
|
62432
62506
|
}
|
|
62433
|
-
log(`[${
|
|
62507
|
+
log(`[${HOOK_NAME12}] No category/agent fallback models resolved for session`, { sessionID, agent });
|
|
62434
62508
|
return;
|
|
62435
62509
|
}
|
|
62436
62510
|
|
|
@@ -62459,21 +62533,21 @@ function findNextAvailableFallback(state3, fallbackModels, cooldownSeconds) {
|
|
|
62459
62533
|
if (!isModelInCooldown(candidate, state3, cooldownSeconds)) {
|
|
62460
62534
|
return candidate;
|
|
62461
62535
|
}
|
|
62462
|
-
log(`[${
|
|
62536
|
+
log(`[${HOOK_NAME12}] Skipping fallback model in cooldown`, { model: candidate, index: i });
|
|
62463
62537
|
}
|
|
62464
62538
|
return;
|
|
62465
62539
|
}
|
|
62466
62540
|
function prepareFallback(sessionID, state3, fallbackModels, config2) {
|
|
62467
62541
|
if (state3.attemptCount >= config2.max_fallback_attempts) {
|
|
62468
|
-
log(`[${
|
|
62542
|
+
log(`[${HOOK_NAME12}] Max fallback attempts reached`, { sessionID, attempts: state3.attemptCount });
|
|
62469
62543
|
return { success: false, error: "Max fallback attempts reached", maxAttemptsReached: true };
|
|
62470
62544
|
}
|
|
62471
62545
|
const nextModel = findNextAvailableFallback(state3, fallbackModels, config2.cooldown_seconds);
|
|
62472
62546
|
if (!nextModel) {
|
|
62473
|
-
log(`[${
|
|
62547
|
+
log(`[${HOOK_NAME12}] No available fallback models`, { sessionID });
|
|
62474
62548
|
return { success: false, error: "No available fallback models (all in cooldown or exhausted)" };
|
|
62475
62549
|
}
|
|
62476
|
-
log(`[${
|
|
62550
|
+
log(`[${HOOK_NAME12}] Preparing fallback`, {
|
|
62477
62551
|
sessionID,
|
|
62478
62552
|
from: state3.currentModel,
|
|
62479
62553
|
to: nextModel,
|
|
@@ -62559,9 +62633,9 @@ function createAutoRetryHelpers(deps) {
|
|
|
62559
62633
|
const abortSessionRequest = async (sessionID, source) => {
|
|
62560
62634
|
try {
|
|
62561
62635
|
await ctx.client.session.abort({ path: { id: sessionID } });
|
|
62562
|
-
log(`[${
|
|
62636
|
+
log(`[${HOOK_NAME12}] Aborted in-flight session request (${source})`, { sessionID });
|
|
62563
62637
|
} catch (error51) {
|
|
62564
|
-
log(`[${
|
|
62638
|
+
log(`[${HOOK_NAME12}] Failed to abort in-flight session request (${source})`, {
|
|
62565
62639
|
sessionID,
|
|
62566
62640
|
error: String(error51)
|
|
62567
62641
|
});
|
|
@@ -62585,7 +62659,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
62585
62659
|
if (!state3)
|
|
62586
62660
|
return;
|
|
62587
62661
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
62588
|
-
log(`[${
|
|
62662
|
+
log(`[${HOOK_NAME12}] Overriding in-flight retry due to session timeout`, { sessionID });
|
|
62589
62663
|
}
|
|
62590
62664
|
await abortSessionRequest(sessionID, "session.timeout");
|
|
62591
62665
|
sessionRetryInFlight.delete(sessionID);
|
|
@@ -62595,7 +62669,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
62595
62669
|
const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
|
|
62596
62670
|
if (fallbackModels.length === 0)
|
|
62597
62671
|
return;
|
|
62598
|
-
log(`[${
|
|
62672
|
+
log(`[${HOOK_NAME12}] Session fallback timeout reached`, {
|
|
62599
62673
|
sessionID,
|
|
62600
62674
|
timeoutSeconds: config2.timeout_seconds,
|
|
62601
62675
|
currentModel: state3.currentModel
|
|
@@ -62609,7 +62683,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
62609
62683
|
};
|
|
62610
62684
|
const autoRetryWithFallback = async (sessionID, newModel, resolvedAgent, source) => {
|
|
62611
62685
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
62612
|
-
log(`[${
|
|
62686
|
+
log(`[${HOOK_NAME12}] Retry already in flight, skipping (${source})`, { sessionID });
|
|
62613
62687
|
return;
|
|
62614
62688
|
}
|
|
62615
62689
|
const agentSettings = resolvedAgent ? pluginConfig?.agents?.[resolvedAgent] : undefined;
|
|
@@ -62618,7 +62692,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
62618
62692
|
reasoningEffort: agentSettings.reasoningEffort
|
|
62619
62693
|
} : undefined);
|
|
62620
62694
|
if (!retryModelPayload) {
|
|
62621
|
-
log(`[${
|
|
62695
|
+
log(`[${HOOK_NAME12}] Invalid model format (missing provider prefix): ${newModel}`);
|
|
62622
62696
|
const state3 = sessionStates2.get(sessionID);
|
|
62623
62697
|
if (state3?.pendingFallbackModel) {
|
|
62624
62698
|
state3.pendingFallbackModel = undefined;
|
|
@@ -62634,7 +62708,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
62634
62708
|
});
|
|
62635
62709
|
const retryParts = getLastUserRetryParts(messagesResp);
|
|
62636
62710
|
if (retryParts.length > 0) {
|
|
62637
|
-
log(`[${
|
|
62711
|
+
log(`[${HOOK_NAME12}] Auto-retrying with fallback model (${source})`, {
|
|
62638
62712
|
sessionID,
|
|
62639
62713
|
model: newModel
|
|
62640
62714
|
});
|
|
@@ -62653,10 +62727,10 @@ function createAutoRetryHelpers(deps) {
|
|
|
62653
62727
|
});
|
|
62654
62728
|
retryDispatched = true;
|
|
62655
62729
|
} else {
|
|
62656
|
-
log(`[${
|
|
62730
|
+
log(`[${HOOK_NAME12}] No user message found for auto-retry (${source})`, { sessionID });
|
|
62657
62731
|
}
|
|
62658
62732
|
} catch (retryError) {
|
|
62659
|
-
log(`[${
|
|
62733
|
+
log(`[${HOOK_NAME12}] Auto-retry failed (${source})`, { sessionID, error: String(retryError) });
|
|
62660
62734
|
} finally {
|
|
62661
62735
|
sessionRetryInFlight.delete(sessionID);
|
|
62662
62736
|
if (!retryDispatched) {
|
|
@@ -62710,7 +62784,7 @@ function createAutoRetryHelpers(deps) {
|
|
|
62710
62784
|
}
|
|
62711
62785
|
}
|
|
62712
62786
|
if (cleanedCount > 0) {
|
|
62713
|
-
log(`[${
|
|
62787
|
+
log(`[${HOOK_NAME12}] Cleaned up ${cleanedCount} stale session states`);
|
|
62714
62788
|
}
|
|
62715
62789
|
};
|
|
62716
62790
|
return {
|
|
@@ -62741,7 +62815,7 @@ function createChatMessageHandler(deps) {
|
|
|
62741
62815
|
state3.pendingFallbackModel = undefined;
|
|
62742
62816
|
return;
|
|
62743
62817
|
}
|
|
62744
|
-
log(`[${
|
|
62818
|
+
log(`[${HOOK_NAME12}] Detected manual model change, resetting fallback state`, {
|
|
62745
62819
|
sessionID,
|
|
62746
62820
|
from: state3.currentModel,
|
|
62747
62821
|
to: requestedModel
|
|
@@ -62753,7 +62827,7 @@ function createChatMessageHandler(deps) {
|
|
|
62753
62827
|
if (state3.currentModel === state3.originalModel)
|
|
62754
62828
|
return;
|
|
62755
62829
|
const activeModel = state3.currentModel;
|
|
62756
|
-
log(`[${
|
|
62830
|
+
log(`[${HOOK_NAME12}] Applying fallback model override`, {
|
|
62757
62831
|
sessionID,
|
|
62758
62832
|
from: input.model,
|
|
62759
62833
|
to: activeModel
|
|
@@ -62940,7 +63014,7 @@ function resolveFallbackBootstrapModel(options) {
|
|
|
62940
63014
|
const agentConfig = options.resolvedAgent && agentConfigs ? agentConfigs[options.resolvedAgent] : undefined;
|
|
62941
63015
|
const agentModel = typeof agentConfig?.model === "string" ? agentConfig.model : undefined;
|
|
62942
63016
|
if (agentModel) {
|
|
62943
|
-
log(`[${
|
|
63017
|
+
log(`[${HOOK_NAME12}] Derived model from agent config for ${options.source}`, {
|
|
62944
63018
|
sessionID: options.sessionID,
|
|
62945
63019
|
agent: options.resolvedAgent,
|
|
62946
63020
|
model: agentModel
|
|
@@ -62951,7 +63025,7 @@ function resolveFallbackBootstrapModel(options) {
|
|
|
62951
63025
|
if (agentCategory) {
|
|
62952
63026
|
const agentCategoryModel = options.pluginConfig?.categories?.[agentCategory]?.model;
|
|
62953
63027
|
if (typeof agentCategoryModel === "string" && agentCategoryModel.length > 0) {
|
|
62954
|
-
log(`[${
|
|
63028
|
+
log(`[${HOOK_NAME12}] Derived model from agent category config for ${options.source}`, {
|
|
62955
63029
|
sessionID: options.sessionID,
|
|
62956
63030
|
agent: options.resolvedAgent,
|
|
62957
63031
|
category: agentCategory,
|
|
@@ -62963,7 +63037,7 @@ function resolveFallbackBootstrapModel(options) {
|
|
|
62963
63037
|
const sessionCategory = SessionCategoryRegistry.get(options.sessionID);
|
|
62964
63038
|
const categoryModel = sessionCategory ? options.pluginConfig?.categories?.[sessionCategory]?.model : undefined;
|
|
62965
63039
|
if (typeof categoryModel === "string" && categoryModel.length > 0) {
|
|
62966
|
-
log(`[${
|
|
63040
|
+
log(`[${HOOK_NAME12}] Derived model from session category config for ${options.source}`, {
|
|
62967
63041
|
sessionID: options.sessionID,
|
|
62968
63042
|
category: sessionCategory,
|
|
62969
63043
|
model: categoryModel
|
|
@@ -62993,7 +63067,7 @@ async function dispatchFallbackRetry(deps, helpers, options) {
|
|
|
62993
63067
|
await helpers.autoRetryWithFallback(options.sessionID, result.newModel, options.resolvedAgent, options.source);
|
|
62994
63068
|
return;
|
|
62995
63069
|
}
|
|
62996
|
-
log(`[${
|
|
63070
|
+
log(`[${HOOK_NAME12}] Fallback preparation failed`, {
|
|
62997
63071
|
sessionID: options.sessionID,
|
|
62998
63072
|
source: options.source,
|
|
62999
63073
|
error: result.error
|
|
@@ -63046,14 +63120,14 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
|
|
|
63046
63120
|
sessionStatusRetryKeys.set(sessionID, retryKey);
|
|
63047
63121
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
63048
63122
|
if (timeoutEnabled) {
|
|
63049
|
-
log(`[${
|
|
63123
|
+
log(`[${HOOK_NAME12}] Overriding in-flight retry due to provider auto-retry signal`, {
|
|
63050
63124
|
sessionID,
|
|
63051
63125
|
model
|
|
63052
63126
|
});
|
|
63053
63127
|
await helpers.abortSessionRequest(sessionID, "session.status.retry-signal");
|
|
63054
63128
|
sessionRetryInFlight.delete(sessionID);
|
|
63055
63129
|
} else {
|
|
63056
|
-
log(`[${
|
|
63130
|
+
log(`[${HOOK_NAME12}] session.status retry skipped - retry already in flight`, { sessionID });
|
|
63057
63131
|
return;
|
|
63058
63132
|
}
|
|
63059
63133
|
}
|
|
@@ -63076,7 +63150,7 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
|
|
|
63076
63150
|
});
|
|
63077
63151
|
if (!initialModel) {
|
|
63078
63152
|
sessionStatusRetryKeys.delete(sessionID);
|
|
63079
|
-
log(`[${
|
|
63153
|
+
log(`[${HOOK_NAME12}] session.status retry missing model info, cannot fallback`, { sessionID });
|
|
63080
63154
|
return;
|
|
63081
63155
|
}
|
|
63082
63156
|
state3 = createFallbackState(initialModel);
|
|
@@ -63085,20 +63159,20 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
|
|
|
63085
63159
|
sessionLastAccess.set(sessionID, Date.now());
|
|
63086
63160
|
if (state3.pendingFallbackModel) {
|
|
63087
63161
|
if (timeoutEnabled) {
|
|
63088
|
-
log(`[${
|
|
63162
|
+
log(`[${HOOK_NAME12}] Clearing pending fallback due to provider auto-retry signal`, {
|
|
63089
63163
|
sessionID,
|
|
63090
63164
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
63091
63165
|
});
|
|
63092
63166
|
state3.pendingFallbackModel = undefined;
|
|
63093
63167
|
} else {
|
|
63094
|
-
log(`[${
|
|
63168
|
+
log(`[${HOOK_NAME12}] session.status retry skipped (pending fallback in progress)`, {
|
|
63095
63169
|
sessionID,
|
|
63096
63170
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
63097
63171
|
});
|
|
63098
63172
|
return;
|
|
63099
63173
|
}
|
|
63100
63174
|
}
|
|
63101
|
-
log(`[${
|
|
63175
|
+
log(`[${HOOK_NAME12}] Detected provider auto-retry signal in session.status`, {
|
|
63102
63176
|
sessionID,
|
|
63103
63177
|
model: state3.currentModel,
|
|
63104
63178
|
retryAttempt: status.attempt
|
|
@@ -63134,7 +63208,7 @@ function createEventHandler(deps, helpers) {
|
|
|
63134
63208
|
const sessionID = sessionInfo?.id;
|
|
63135
63209
|
const model = sessionInfo?.model;
|
|
63136
63210
|
if (sessionID && model) {
|
|
63137
|
-
log(`[${
|
|
63211
|
+
log(`[${HOOK_NAME12}] Session created with model`, { sessionID, model });
|
|
63138
63212
|
sessionStates2.set(sessionID, createFallbackState(model));
|
|
63139
63213
|
sessionLastAccess.set(sessionID, Date.now());
|
|
63140
63214
|
}
|
|
@@ -63143,7 +63217,7 @@ function createEventHandler(deps, helpers) {
|
|
|
63143
63217
|
const sessionInfo = props?.info;
|
|
63144
63218
|
const sessionID = sessionInfo?.id;
|
|
63145
63219
|
if (sessionID) {
|
|
63146
|
-
log(`[${
|
|
63220
|
+
log(`[${HOOK_NAME12}] Cleaning up session state`, { sessionID });
|
|
63147
63221
|
cancelledSessions.delete(sessionID);
|
|
63148
63222
|
sessionStates2.delete(sessionID);
|
|
63149
63223
|
sessionLastAccess.delete(sessionID);
|
|
@@ -63163,7 +63237,7 @@ function createEventHandler(deps, helpers) {
|
|
|
63163
63237
|
}
|
|
63164
63238
|
cancelledSessions.add(sessionID);
|
|
63165
63239
|
resetRetryState(sessionID);
|
|
63166
|
-
log(`[${
|
|
63240
|
+
log(`[${HOOK_NAME12}] Cleared fallback retry state on session.stop`, { sessionID });
|
|
63167
63241
|
};
|
|
63168
63242
|
const handleMessageUpdated = (props) => {
|
|
63169
63243
|
const info = props?.info;
|
|
@@ -63179,11 +63253,11 @@ function createEventHandler(deps, helpers) {
|
|
|
63179
63253
|
return;
|
|
63180
63254
|
if (cancelledSessions.has(sessionID)) {
|
|
63181
63255
|
resetRetryState(sessionID);
|
|
63182
|
-
log(`[${
|
|
63256
|
+
log(`[${HOOK_NAME12}] Cleared fallback retry state for cancelled session on idle`, { sessionID });
|
|
63183
63257
|
return;
|
|
63184
63258
|
}
|
|
63185
63259
|
if (sessionAwaitingFallbackResult.has(sessionID)) {
|
|
63186
|
-
log(`[${
|
|
63260
|
+
log(`[${HOOK_NAME12}] session.idle while awaiting fallback result; keeping timeout armed`, { sessionID });
|
|
63187
63261
|
return;
|
|
63188
63262
|
}
|
|
63189
63263
|
const hadTimeout = sessionFallbackTimeouts.has(sessionID);
|
|
@@ -63195,7 +63269,7 @@ function createEventHandler(deps, helpers) {
|
|
|
63195
63269
|
state3.pendingFallbackModel = undefined;
|
|
63196
63270
|
}
|
|
63197
63271
|
if (hadTimeout) {
|
|
63198
|
-
log(`[${
|
|
63272
|
+
log(`[${HOOK_NAME12}] Cleared fallback timeout after session completion`, { sessionID });
|
|
63199
63273
|
}
|
|
63200
63274
|
};
|
|
63201
63275
|
const handleSessionError = async (props) => {
|
|
@@ -63203,18 +63277,18 @@ function createEventHandler(deps, helpers) {
|
|
|
63203
63277
|
const error51 = props?.error;
|
|
63204
63278
|
const agent = props?.agent;
|
|
63205
63279
|
if (!sessionID) {
|
|
63206
|
-
log(`[${
|
|
63280
|
+
log(`[${HOOK_NAME12}] session.error without sessionID, skipping`);
|
|
63207
63281
|
return;
|
|
63208
63282
|
}
|
|
63209
63283
|
const resolvedAgent = await helpers.resolveAgentForSessionFromContext(sessionID, agent);
|
|
63210
63284
|
if (isAbortError(error51)) {
|
|
63211
63285
|
cancelledSessions.add(sessionID);
|
|
63212
63286
|
resetRetryState(sessionID);
|
|
63213
|
-
log(`[${
|
|
63287
|
+
log(`[${HOOK_NAME12}] session.error matched cancellation; cleared retry state`, { sessionID, resolvedAgent });
|
|
63214
63288
|
return;
|
|
63215
63289
|
}
|
|
63216
63290
|
if (sessionRetryInFlight.has(sessionID)) {
|
|
63217
|
-
log(`[${
|
|
63291
|
+
log(`[${HOOK_NAME12}] session.error skipped - retry in flight`, {
|
|
63218
63292
|
sessionID,
|
|
63219
63293
|
retryInFlight: true
|
|
63220
63294
|
});
|
|
@@ -63222,7 +63296,7 @@ function createEventHandler(deps, helpers) {
|
|
|
63222
63296
|
}
|
|
63223
63297
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
63224
63298
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
63225
|
-
log(`[${
|
|
63299
|
+
log(`[${HOOK_NAME12}] session.error received`, {
|
|
63226
63300
|
sessionID,
|
|
63227
63301
|
agent,
|
|
63228
63302
|
resolvedAgent,
|
|
@@ -63231,7 +63305,7 @@ function createEventHandler(deps, helpers) {
|
|
|
63231
63305
|
errorType: classifyErrorType(error51)
|
|
63232
63306
|
});
|
|
63233
63307
|
if (!isRetryableError(error51, config2.retry_on_errors)) {
|
|
63234
|
-
log(`[${
|
|
63308
|
+
log(`[${HOOK_NAME12}] Error not retryable, skipping fallback`, {
|
|
63235
63309
|
sessionID,
|
|
63236
63310
|
retryable: false,
|
|
63237
63311
|
statusCode: extractStatusCode(error51, config2.retry_on_errors),
|
|
@@ -63243,7 +63317,7 @@ function createEventHandler(deps, helpers) {
|
|
|
63243
63317
|
let state3 = sessionStates2.get(sessionID);
|
|
63244
63318
|
const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
|
|
63245
63319
|
if (fallbackModels.length === 0) {
|
|
63246
|
-
log(`[${
|
|
63320
|
+
log(`[${HOOK_NAME12}] No fallback models configured`, { sessionID, agent });
|
|
63247
63321
|
return;
|
|
63248
63322
|
}
|
|
63249
63323
|
if (!state3) {
|
|
@@ -63255,7 +63329,7 @@ function createEventHandler(deps, helpers) {
|
|
|
63255
63329
|
pluginConfig
|
|
63256
63330
|
});
|
|
63257
63331
|
if (!initialModel) {
|
|
63258
|
-
log(`[${
|
|
63332
|
+
log(`[${HOOK_NAME12}] No model info available, cannot fallback`, { sessionID });
|
|
63259
63333
|
return;
|
|
63260
63334
|
}
|
|
63261
63335
|
state3 = createFallbackState(initialModel);
|
|
@@ -63395,7 +63469,7 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
63395
63469
|
}
|
|
63396
63470
|
const hasVisible = await checkVisibleResponse(ctx, sessionID, info);
|
|
63397
63471
|
if (!hasVisible) {
|
|
63398
|
-
log(`[${
|
|
63472
|
+
log(`[${HOOK_NAME12}] Assistant update observed without visible final response; keeping fallback timeout`, {
|
|
63399
63473
|
sessionID,
|
|
63400
63474
|
model
|
|
63401
63475
|
});
|
|
@@ -63408,17 +63482,17 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
63408
63482
|
if (state3?.pendingFallbackModel) {
|
|
63409
63483
|
state3.pendingFallbackModel = undefined;
|
|
63410
63484
|
}
|
|
63411
|
-
log(`[${
|
|
63485
|
+
log(`[${HOOK_NAME12}] Assistant response observed; cleared fallback timeout`, { sessionID, model });
|
|
63412
63486
|
return;
|
|
63413
63487
|
}
|
|
63414
63488
|
if (sessionID && role === "assistant" && error51) {
|
|
63415
63489
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
63416
63490
|
if (sessionRetryInFlight.has(sessionID) && !retrySignal) {
|
|
63417
|
-
log(`[${
|
|
63491
|
+
log(`[${HOOK_NAME12}] message.updated fallback skipped (retry in flight)`, { sessionID });
|
|
63418
63492
|
return;
|
|
63419
63493
|
}
|
|
63420
63494
|
if (retrySignal && sessionRetryInFlight.has(sessionID) && timeoutEnabled) {
|
|
63421
|
-
log(`[${
|
|
63495
|
+
log(`[${HOOK_NAME12}] Overriding in-flight retry due to provider auto-retry signal`, {
|
|
63422
63496
|
sessionID,
|
|
63423
63497
|
model
|
|
63424
63498
|
});
|
|
@@ -63426,12 +63500,12 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
63426
63500
|
sessionRetryInFlight.delete(sessionID);
|
|
63427
63501
|
}
|
|
63428
63502
|
if (retrySignal && timeoutEnabled) {
|
|
63429
|
-
log(`[${
|
|
63503
|
+
log(`[${HOOK_NAME12}] Detected provider auto-retry signal`, { sessionID, model });
|
|
63430
63504
|
}
|
|
63431
63505
|
if (!retrySignal) {
|
|
63432
63506
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
63433
63507
|
}
|
|
63434
|
-
log(`[${
|
|
63508
|
+
log(`[${HOOK_NAME12}] message.updated with assistant error`, {
|
|
63435
63509
|
sessionID,
|
|
63436
63510
|
model,
|
|
63437
63511
|
statusCode: extractStatusCode(error51, config2.retry_on_errors),
|
|
@@ -63439,7 +63513,7 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
63439
63513
|
errorType: classifyErrorType(error51)
|
|
63440
63514
|
});
|
|
63441
63515
|
if (!isRetryableError(error51, config2.retry_on_errors)) {
|
|
63442
|
-
log(`[${
|
|
63516
|
+
log(`[${HOOK_NAME12}] message.updated error not retryable, skipping fallback`, {
|
|
63443
63517
|
sessionID,
|
|
63444
63518
|
statusCode: extractStatusCode(error51, config2.retry_on_errors),
|
|
63445
63519
|
errorName: extractErrorName(error51),
|
|
@@ -63463,7 +63537,7 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
63463
63537
|
pluginConfig
|
|
63464
63538
|
});
|
|
63465
63539
|
if (!initialModel) {
|
|
63466
|
-
log(`[${
|
|
63540
|
+
log(`[${HOOK_NAME12}] message.updated missing model info, cannot fallback`, {
|
|
63467
63541
|
sessionID,
|
|
63468
63542
|
errorName: extractErrorName(error51),
|
|
63469
63543
|
errorType: classifyErrorType(error51)
|
|
@@ -63477,13 +63551,13 @@ function createMessageUpdateHandler(deps, helpers) {
|
|
|
63477
63551
|
sessionLastAccess.set(sessionID, Date.now());
|
|
63478
63552
|
if (state3.pendingFallbackModel) {
|
|
63479
63553
|
if (retrySignal && timeoutEnabled) {
|
|
63480
|
-
log(`[${
|
|
63554
|
+
log(`[${HOOK_NAME12}] Clearing pending fallback due to provider auto-retry signal`, {
|
|
63481
63555
|
sessionID,
|
|
63482
63556
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
63483
63557
|
});
|
|
63484
63558
|
state3.pendingFallbackModel = undefined;
|
|
63485
63559
|
} else {
|
|
63486
|
-
log(`[${
|
|
63560
|
+
log(`[${HOOK_NAME12}] message.updated fallback skipped (pending fallback in progress)`, {
|
|
63487
63561
|
sessionID,
|
|
63488
63562
|
pendingFallbackModel: state3.pendingFallbackModel
|
|
63489
63563
|
});
|
|
@@ -64489,7 +64563,7 @@ function createSessionRecoveryHook(ctx, options) {
|
|
|
64489
64563
|
}
|
|
64490
64564
|
// src/hooks/start-work/start-work-hook.ts
|
|
64491
64565
|
init_logger();
|
|
64492
|
-
var
|
|
64566
|
+
var HOOK_NAME13 = "start-work";
|
|
64493
64567
|
var KEYWORD_PATTERN = /\b(ultrawork|ulw)\b/gi;
|
|
64494
64568
|
function extractUserRequestPlanName(promptText) {
|
|
64495
64569
|
const userRequestMatch = promptText.match(/<user-request>\s*([\s\S]*?)\s*<\/user-request>/i);
|
|
@@ -64519,7 +64593,7 @@ function createStartWorkHook(ctx) {
|
|
|
64519
64593
|
if (!isStartWorkCommand) {
|
|
64520
64594
|
return;
|
|
64521
64595
|
}
|
|
64522
|
-
log(`[${
|
|
64596
|
+
log(`[${HOOK_NAME13}] Processing start-work command`, {
|
|
64523
64597
|
sessionID: input.sessionID
|
|
64524
64598
|
});
|
|
64525
64599
|
updateSessionAgent(input.sessionID, "architect");
|
|
@@ -64529,7 +64603,7 @@ function createStartWorkHook(ctx) {
|
|
|
64529
64603
|
let contextInfo = "";
|
|
64530
64604
|
const explicitPlanName = extractUserRequestPlanName(promptText);
|
|
64531
64605
|
if (explicitPlanName) {
|
|
64532
|
-
log(`[${
|
|
64606
|
+
log(`[${HOOK_NAME13}] Explicit plan name requested: ${explicitPlanName}`, {
|
|
64533
64607
|
sessionID: input.sessionID
|
|
64534
64608
|
});
|
|
64535
64609
|
const allPlans = findOraclePlans(ctx.directory);
|
|
@@ -64670,7 +64744,7 @@ Ask the user which plan to work on. Present the options above and wait for their
|
|
|
64670
64744
|
---
|
|
64671
64745
|
${contextInfo}`;
|
|
64672
64746
|
}
|
|
64673
|
-
log(`[${
|
|
64747
|
+
log(`[${HOOK_NAME13}] Context injected`, {
|
|
64674
64748
|
sessionID: input.sessionID,
|
|
64675
64749
|
hasExistingState: !!existingState
|
|
64676
64750
|
});
|
|
@@ -64756,7 +64830,7 @@ function isQuestionTool(properties) {
|
|
|
64756
64830
|
|
|
64757
64831
|
// src/hooks/stop-continuation-guard/hook.ts
|
|
64758
64832
|
init_logger();
|
|
64759
|
-
var
|
|
64833
|
+
var HOOK_NAME14 = "stop-continuation-guard";
|
|
64760
64834
|
function createStopContinuationGuardHook(ctx, options) {
|
|
64761
64835
|
const stoppedSessions = new Set;
|
|
64762
64836
|
const isSessionAwaitingUser = async (sessionID) => {
|
|
@@ -64769,22 +64843,22 @@ function createStopContinuationGuardHook(ctx, options) {
|
|
|
64769
64843
|
});
|
|
64770
64844
|
return hasPendingQuestionMessage(normalizeSDKResponse(resp, []));
|
|
64771
64845
|
} catch (error51) {
|
|
64772
|
-
log(`[${
|
|
64846
|
+
log(`[${HOOK_NAME14}] Messages fetch failed, assuming not awaiting user`, { sessionID, error: String(error51) });
|
|
64773
64847
|
return false;
|
|
64774
64848
|
}
|
|
64775
64849
|
};
|
|
64776
64850
|
const stop = async (sessionID) => {
|
|
64777
64851
|
stoppedSessions.add(sessionID);
|
|
64778
|
-
log(`[${
|
|
64852
|
+
log(`[${HOOK_NAME14}] Continuation stopped for session`, { sessionID });
|
|
64779
64853
|
if (!options?.backgroundManager)
|
|
64780
64854
|
return;
|
|
64781
64855
|
if (await isSessionAwaitingUser(sessionID)) {
|
|
64782
|
-
log(`[${
|
|
64856
|
+
log(`[${HOOK_NAME14}] Background cancellation suppressed: awaiting user`, { sessionID });
|
|
64783
64857
|
return;
|
|
64784
64858
|
}
|
|
64785
64859
|
const cancelled = options.backgroundManager.cancelAllForSession(sessionID);
|
|
64786
64860
|
if (cancelled > 0) {
|
|
64787
|
-
log(`[${
|
|
64861
|
+
log(`[${HOOK_NAME14}] Cancelled ${cancelled} background task(s) for session`, { sessionID });
|
|
64788
64862
|
}
|
|
64789
64863
|
};
|
|
64790
64864
|
const isStopped = (sessionID) => {
|
|
@@ -64792,7 +64866,7 @@ function createStopContinuationGuardHook(ctx, options) {
|
|
|
64792
64866
|
};
|
|
64793
64867
|
const clear = (sessionID) => {
|
|
64794
64868
|
stoppedSessions.delete(sessionID);
|
|
64795
|
-
log(`[${
|
|
64869
|
+
log(`[${HOOK_NAME14}] Continuation guard cleared for session`, { sessionID });
|
|
64796
64870
|
};
|
|
64797
64871
|
const event = async ({
|
|
64798
64872
|
event: event2
|
|
@@ -64802,7 +64876,7 @@ function createStopContinuationGuardHook(ctx, options) {
|
|
|
64802
64876
|
const sessionInfo = props?.info;
|
|
64803
64877
|
if (sessionInfo?.id) {
|
|
64804
64878
|
clear(sessionInfo.id);
|
|
64805
|
-
log(`[${
|
|
64879
|
+
log(`[${HOOK_NAME14}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
|
|
64806
64880
|
}
|
|
64807
64881
|
}
|
|
64808
64882
|
};
|
|
@@ -64811,7 +64885,7 @@ function createStopContinuationGuardHook(ctx, options) {
|
|
|
64811
64885
|
}) => {
|
|
64812
64886
|
if (sessionID && stoppedSessions.has(sessionID)) {
|
|
64813
64887
|
clear(sessionID);
|
|
64814
|
-
log(`[${
|
|
64888
|
+
log(`[${HOOK_NAME14}] Cleared stop state on new user message`, { sessionID });
|
|
64815
64889
|
}
|
|
64816
64890
|
};
|
|
64817
64891
|
return {
|
|
@@ -64826,7 +64900,7 @@ function createStopContinuationGuardHook(ctx, options) {
|
|
|
64826
64900
|
init_logger();
|
|
64827
64901
|
|
|
64828
64902
|
// src/hooks/task-continuation-enforcer/constants.ts
|
|
64829
|
-
var
|
|
64903
|
+
var HOOK_NAME15 = "task-continuation-enforcer";
|
|
64830
64904
|
var DEFAULT_SKIP_AGENTS = ["oracle", "compaction"];
|
|
64831
64905
|
var EXPLORER_AGENTS = ["trinity", "operator"];
|
|
64832
64906
|
function hasNonExplorerBgTasks(tasks) {
|
|
@@ -64973,21 +65047,21 @@ async function injectContinuation(args) {
|
|
|
64973
65047
|
config: config2
|
|
64974
65048
|
} = args;
|
|
64975
65049
|
if (subagentSessions.has(sessionID)) {
|
|
64976
|
-
log(`[${
|
|
65050
|
+
log(`[${HOOK_NAME15}] Skipped injection: subagent session`, { sessionID });
|
|
64977
65051
|
return;
|
|
64978
65052
|
}
|
|
64979
65053
|
const state3 = sessionStateStore.getExistingState(sessionID);
|
|
64980
65054
|
if (state3?.isRecovering) {
|
|
64981
|
-
log(`[${
|
|
65055
|
+
log(`[${HOOK_NAME15}] Skipped injection: in recovery`, { sessionID });
|
|
64982
65056
|
return;
|
|
64983
65057
|
}
|
|
64984
65058
|
if (isAwaitingUser(state3)) {
|
|
64985
|
-
log(`[${
|
|
65059
|
+
log(`[${HOOK_NAME15}] Skipped injection: awaiting user`, { sessionID });
|
|
64986
65060
|
return;
|
|
64987
65061
|
}
|
|
64988
65062
|
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
|
|
64989
65063
|
if (hasRunningBgTasks) {
|
|
64990
|
-
log(`[${
|
|
65064
|
+
log(`[${HOOK_NAME15}] Skipped injection: background tasks running`, { sessionID });
|
|
64991
65065
|
return;
|
|
64992
65066
|
}
|
|
64993
65067
|
const earlyInjectionState = sessionStateStore.getExistingState(sessionID);
|
|
@@ -64995,7 +65069,7 @@ async function injectContinuation(args) {
|
|
|
64995
65069
|
const now = Date.now();
|
|
64996
65070
|
const elapsedMs = now - earlyInjectionState.lastInjectedAt;
|
|
64997
65071
|
if (elapsedMs < CONTINUATION_COOLDOWN_MS3) {
|
|
64998
|
-
log(`[${
|
|
65072
|
+
log(`[${HOOK_NAME15}] Skipped injection: cooldown active`, {
|
|
64999
65073
|
sessionID,
|
|
65000
65074
|
cooldownMs: CONTINUATION_COOLDOWN_MS3,
|
|
65001
65075
|
elapsedMs
|
|
@@ -65013,10 +65087,10 @@ async function injectContinuation(args) {
|
|
|
65013
65087
|
if (!existsSync42(taskDir)) {
|
|
65014
65088
|
const hadNonExplorerBgTasks = backgroundManager ? hasNonExplorerBgTasks(backgroundManager.getTasksByParentSession(sessionID)) : false;
|
|
65015
65089
|
if (hadNonExplorerBgTasks) {
|
|
65016
|
-
log(`[${
|
|
65090
|
+
log(`[${HOOK_NAME15}] Bootstrap injection: no task dir (hadNonExplorerBgTasks)`, { sessionID, taskDir });
|
|
65017
65091
|
isBootstrap = true;
|
|
65018
65092
|
} else {
|
|
65019
|
-
log(`[${
|
|
65093
|
+
log(`[${HOOK_NAME15}] Skipped injection: no task dir`, { sessionID, taskDir });
|
|
65020
65094
|
return;
|
|
65021
65095
|
}
|
|
65022
65096
|
total = 0;
|
|
@@ -65036,16 +65110,16 @@ async function injectContinuation(args) {
|
|
|
65036
65110
|
if (total === 0) {
|
|
65037
65111
|
const hadNonExplorerBgTasks = backgroundManager ? hasNonExplorerBgTasks(backgroundManager.getTasksByParentSession(sessionID)) : false;
|
|
65038
65112
|
if (hadNonExplorerBgTasks) {
|
|
65039
|
-
log(`[${
|
|
65113
|
+
log(`[${HOOK_NAME15}] Bootstrap injection: no tasks (hadNonExplorerBgTasks)`, { sessionID });
|
|
65040
65114
|
isBootstrap = true;
|
|
65041
65115
|
} else {
|
|
65042
|
-
log(`[${
|
|
65116
|
+
log(`[${HOOK_NAME15}] Skipped injection: no tasks`, { sessionID });
|
|
65043
65117
|
return;
|
|
65044
65118
|
}
|
|
65045
65119
|
}
|
|
65046
65120
|
}
|
|
65047
65121
|
} catch (error51) {
|
|
65048
|
-
log(`[${
|
|
65122
|
+
log(`[${HOOK_NAME15}] Failed to fetch tasks`, { sessionID, error: String(error51) });
|
|
65049
65123
|
return;
|
|
65050
65124
|
}
|
|
65051
65125
|
const stateForBootstrap = sessionStateStore.getExistingState(sessionID);
|
|
@@ -65056,7 +65130,7 @@ async function injectContinuation(args) {
|
|
|
65056
65130
|
const staleAfterMs = getStaleAfterMs(config2);
|
|
65057
65131
|
const freshIncompleteCount = isBootstrap ? 1 : filterFreshIncompleteTasks(getIncompleteTasks(filteredTasks), taskDir, staleAfterMs).length;
|
|
65058
65132
|
if (!isBootstrap && freshIncompleteCount === 0) {
|
|
65059
|
-
log(`[${
|
|
65133
|
+
log(`[${HOOK_NAME15}] Skipped injection: only stale tasks remain`, { sessionID, total });
|
|
65060
65134
|
return;
|
|
65061
65135
|
}
|
|
65062
65136
|
let agentName = resolvedInfo?.agent;
|
|
@@ -65079,11 +65153,11 @@ async function injectContinuation(args) {
|
|
|
65079
65153
|
tools = tools ?? previousMessage?.tools;
|
|
65080
65154
|
}
|
|
65081
65155
|
if (agentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(agentName))) {
|
|
65082
|
-
log(`[${
|
|
65156
|
+
log(`[${HOOK_NAME15}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
|
|
65083
65157
|
return;
|
|
65084
65158
|
}
|
|
65085
65159
|
if (!hasWritePermission(tools)) {
|
|
65086
|
-
log(`[${
|
|
65160
|
+
log(`[${HOOK_NAME15}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
|
|
65087
65161
|
return;
|
|
65088
65162
|
}
|
|
65089
65163
|
let prompt;
|
|
@@ -65117,11 +65191,11 @@ ${taskList}${staleNote}`;
|
|
|
65117
65191
|
if (injectionState) {
|
|
65118
65192
|
injectionState.inFlight = false;
|
|
65119
65193
|
}
|
|
65120
|
-
log(`[${
|
|
65194
|
+
log(`[${HOOK_NAME15}] Skipped injection: awaiting user (race)`, { sessionID });
|
|
65121
65195
|
return;
|
|
65122
65196
|
}
|
|
65123
65197
|
try {
|
|
65124
|
-
log(`[${
|
|
65198
|
+
log(`[${HOOK_NAME15}] Injecting continuation`, {
|
|
65125
65199
|
sessionID,
|
|
65126
65200
|
agent: agentName,
|
|
65127
65201
|
model,
|
|
@@ -65136,14 +65210,14 @@ ${taskList}${staleNote}`;
|
|
|
65136
65210
|
},
|
|
65137
65211
|
query: { directory: ctx.directory }
|
|
65138
65212
|
});
|
|
65139
|
-
log(`[${
|
|
65213
|
+
log(`[${HOOK_NAME15}] Injection successful`, { sessionID });
|
|
65140
65214
|
if (injectionState) {
|
|
65141
65215
|
injectionState.inFlight = false;
|
|
65142
65216
|
injectionState.lastInjectedAt = Date.now();
|
|
65143
65217
|
injectionState.consecutiveFailures = 0;
|
|
65144
65218
|
}
|
|
65145
65219
|
} catch (error51) {
|
|
65146
|
-
log(`[${
|
|
65220
|
+
log(`[${HOOK_NAME15}] Injection failed`, { sessionID, error: String(error51) });
|
|
65147
65221
|
if (injectionState) {
|
|
65148
65222
|
injectionState.inFlight = false;
|
|
65149
65223
|
injectionState.lastInjectedAt = Date.now();
|
|
@@ -65178,7 +65252,7 @@ function startCountdown(args) {
|
|
|
65178
65252
|
} = args;
|
|
65179
65253
|
const state3 = sessionStateStore.getState(sessionID);
|
|
65180
65254
|
if (state3.countdownTimer || state3.countdownInterval) {
|
|
65181
|
-
log(`[${
|
|
65255
|
+
log(`[${HOOK_NAME15}] Skipped: countdown already active`, { sessionID });
|
|
65182
65256
|
return;
|
|
65183
65257
|
}
|
|
65184
65258
|
let secondsRemaining = COUNTDOWN_SECONDS;
|
|
@@ -65202,7 +65276,7 @@ function startCountdown(args) {
|
|
|
65202
65276
|
config: config2
|
|
65203
65277
|
});
|
|
65204
65278
|
}, COUNTDOWN_SECONDS * 1000);
|
|
65205
|
-
log(`[${
|
|
65279
|
+
log(`[${HOOK_NAME15}] Countdown started`, {
|
|
65206
65280
|
sessionID,
|
|
65207
65281
|
seconds: COUNTDOWN_SECONDS,
|
|
65208
65282
|
incompleteCount
|
|
@@ -65220,36 +65294,36 @@ async function handleSessionIdle(args) {
|
|
|
65220
65294
|
isContinuationStopped,
|
|
65221
65295
|
config: config2
|
|
65222
65296
|
} = args;
|
|
65223
|
-
log(`[${
|
|
65297
|
+
log(`[${HOOK_NAME15}] session.idle`, { sessionID });
|
|
65224
65298
|
if (subagentSessions.has(sessionID)) {
|
|
65225
|
-
log(`[${
|
|
65299
|
+
log(`[${HOOK_NAME15}] Skipped: subagent session`, { sessionID });
|
|
65226
65300
|
return;
|
|
65227
65301
|
}
|
|
65228
65302
|
const state3 = sessionStateStore.getState(sessionID);
|
|
65229
65303
|
if (state3.countdownTimer || state3.countdownInterval) {
|
|
65230
|
-
log(`[${
|
|
65304
|
+
log(`[${HOOK_NAME15}] Skipped: countdown already active`, { sessionID });
|
|
65231
65305
|
return;
|
|
65232
65306
|
}
|
|
65233
65307
|
if (state3.isRecovering) {
|
|
65234
|
-
log(`[${
|
|
65308
|
+
log(`[${HOOK_NAME15}] Skipped: in recovery`, { sessionID });
|
|
65235
65309
|
return;
|
|
65236
65310
|
}
|
|
65237
65311
|
if (state3.abortDetectedAt) {
|
|
65238
65312
|
const timeSinceAbort = Date.now() - state3.abortDetectedAt;
|
|
65239
65313
|
if (timeSinceAbort < ABORT_WINDOW_MS) {
|
|
65240
|
-
log(`[${
|
|
65314
|
+
log(`[${HOOK_NAME15}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
|
|
65241
65315
|
state3.abortDetectedAt = undefined;
|
|
65242
65316
|
return;
|
|
65243
65317
|
}
|
|
65244
65318
|
state3.abortDetectedAt = undefined;
|
|
65245
65319
|
}
|
|
65246
65320
|
if (isAwaitingUser(state3)) {
|
|
65247
|
-
log(`[${
|
|
65321
|
+
log(`[${HOOK_NAME15}] Skipped: awaiting user`, { sessionID });
|
|
65248
65322
|
return;
|
|
65249
65323
|
}
|
|
65250
65324
|
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
|
|
65251
65325
|
if (hasRunningBgTasks) {
|
|
65252
|
-
log(`[${
|
|
65326
|
+
log(`[${HOOK_NAME15}] Skipped: background tasks running`, { sessionID });
|
|
65253
65327
|
return;
|
|
65254
65328
|
}
|
|
65255
65329
|
try {
|
|
@@ -65259,15 +65333,15 @@ async function handleSessionIdle(args) {
|
|
|
65259
65333
|
});
|
|
65260
65334
|
const messages = normalizeSDKResponse(messagesResp, []);
|
|
65261
65335
|
if (isLastAssistantMessageAborted(messages)) {
|
|
65262
|
-
log(`[${
|
|
65336
|
+
log(`[${HOOK_NAME15}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
|
|
65263
65337
|
return;
|
|
65264
65338
|
}
|
|
65265
65339
|
if (isAwaitingUser(state3, messages)) {
|
|
65266
|
-
log(`[${
|
|
65340
|
+
log(`[${HOOK_NAME15}] Skipped: awaiting user (pending question)`, { sessionID });
|
|
65267
65341
|
return;
|
|
65268
65342
|
}
|
|
65269
65343
|
} catch (error51) {
|
|
65270
|
-
log(`[${
|
|
65344
|
+
log(`[${HOOK_NAME15}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
|
|
65271
65345
|
}
|
|
65272
65346
|
let incompleteCount = 0;
|
|
65273
65347
|
let total = 0;
|
|
@@ -65278,12 +65352,12 @@ async function handleSessionIdle(args) {
|
|
|
65278
65352
|
if (!existsSync43(taskDir)) {
|
|
65279
65353
|
const hadNonExplorerBgTasks = backgroundManager ? hasNonExplorerBgTasks(backgroundManager.getTasksByParentSession(sessionID)) : false;
|
|
65280
65354
|
if (hadNonExplorerBgTasks) {
|
|
65281
|
-
log(`[${
|
|
65355
|
+
log(`[${HOOK_NAME15}] No task dir \u2014 bootstrap (hadBgTasks)`, { sessionID, taskDir });
|
|
65282
65356
|
isBootstrap = true;
|
|
65283
65357
|
total = 0;
|
|
65284
65358
|
incompleteCount = 1;
|
|
65285
65359
|
} else {
|
|
65286
|
-
log(`[${
|
|
65360
|
+
log(`[${HOOK_NAME15}] No task dir`, { sessionID, taskDir });
|
|
65287
65361
|
return;
|
|
65288
65362
|
}
|
|
65289
65363
|
} else {
|
|
@@ -65303,11 +65377,11 @@ async function handleSessionIdle(args) {
|
|
|
65303
65377
|
if (total === 0) {
|
|
65304
65378
|
const hadNonExplorerBgTasks = backgroundManager ? hasNonExplorerBgTasks(backgroundManager.getTasksByParentSession(sessionID)) : false;
|
|
65305
65379
|
if (hadNonExplorerBgTasks) {
|
|
65306
|
-
log(`[${
|
|
65380
|
+
log(`[${HOOK_NAME15}] No tasks \u2014 bootstrap (hadNonExplorerBgTasks)`, { sessionID });
|
|
65307
65381
|
isBootstrap = true;
|
|
65308
65382
|
incompleteCount = 1;
|
|
65309
65383
|
} else {
|
|
65310
|
-
log(`[${
|
|
65384
|
+
log(`[${HOOK_NAME15}] No tasks`, { sessionID });
|
|
65311
65385
|
return;
|
|
65312
65386
|
}
|
|
65313
65387
|
} else {
|
|
@@ -65317,7 +65391,7 @@ async function handleSessionIdle(args) {
|
|
|
65317
65391
|
const staleIncompleteCount = incompleteTasks.filter((t) => isTaskStale(join52(taskDir, `${t.id}.json`), staleAfterMs)).length;
|
|
65318
65392
|
const activeIncompleteCount = incompleteCount - staleIncompleteCount;
|
|
65319
65393
|
if (activeIncompleteCount === 0 && staleIncompleteCount > 0) {
|
|
65320
|
-
log(`[${
|
|
65394
|
+
log(`[${HOOK_NAME15}] Skipped: only stale tasks remain`, {
|
|
65321
65395
|
sessionID,
|
|
65322
65396
|
staleIncompleteCount,
|
|
65323
65397
|
staleAfterMs
|
|
@@ -65327,11 +65401,11 @@ async function handleSessionIdle(args) {
|
|
|
65327
65401
|
}
|
|
65328
65402
|
}
|
|
65329
65403
|
} catch (error51) {
|
|
65330
|
-
log(`[${
|
|
65404
|
+
log(`[${HOOK_NAME15}] Task fetch failed`, { sessionID, error: String(error51) });
|
|
65331
65405
|
return;
|
|
65332
65406
|
}
|
|
65333
65407
|
if (!isBootstrap && incompleteCount === 0) {
|
|
65334
|
-
log(`[${
|
|
65408
|
+
log(`[${HOOK_NAME15}] All tasks complete`, { sessionID, total });
|
|
65335
65409
|
return;
|
|
65336
65410
|
}
|
|
65337
65411
|
if (isBootstrap) {
|
|
@@ -65339,18 +65413,18 @@ async function handleSessionIdle(args) {
|
|
|
65339
65413
|
s._bootstrap = true;
|
|
65340
65414
|
}
|
|
65341
65415
|
if (state3.inFlight) {
|
|
65342
|
-
log(`[${
|
|
65416
|
+
log(`[${HOOK_NAME15}] Skipped: injection in flight`, { sessionID });
|
|
65343
65417
|
return;
|
|
65344
65418
|
}
|
|
65345
65419
|
if ((state3.consecutiveFailures ?? 0) >= MAX_CONSECUTIVE_FAILURES && state3.lastInjectedAt && Date.now() - state3.lastInjectedAt >= FAILURE_RESET_WINDOW_MS) {
|
|
65346
65420
|
state3.consecutiveFailures = 0;
|
|
65347
|
-
log(`[${
|
|
65421
|
+
log(`[${HOOK_NAME15}] Reset consecutive failures after recovery window`, {
|
|
65348
65422
|
sessionID,
|
|
65349
65423
|
failureResetWindowMs: FAILURE_RESET_WINDOW_MS
|
|
65350
65424
|
});
|
|
65351
65425
|
}
|
|
65352
65426
|
if ((state3.consecutiveFailures ?? 0) >= MAX_CONSECUTIVE_FAILURES) {
|
|
65353
|
-
log(`[${
|
|
65427
|
+
log(`[${HOOK_NAME15}] Skipped: max consecutive failures reached`, {
|
|
65354
65428
|
sessionID,
|
|
65355
65429
|
consecutiveFailures: state3.consecutiveFailures ?? 0,
|
|
65356
65430
|
maxConsecutiveFailures: MAX_CONSECUTIVE_FAILURES
|
|
@@ -65359,7 +65433,7 @@ async function handleSessionIdle(args) {
|
|
|
65359
65433
|
}
|
|
65360
65434
|
const effectiveCooldown = CONTINUATION_COOLDOWN_MS3 * 2 ** Math.min(state3.consecutiveFailures ?? 0, 5);
|
|
65361
65435
|
if (state3.lastInjectedAt && Date.now() - state3.lastInjectedAt < effectiveCooldown) {
|
|
65362
|
-
log(`[${
|
|
65436
|
+
log(`[${HOOK_NAME15}] Skipped: cooldown active`, {
|
|
65363
65437
|
sessionID,
|
|
65364
65438
|
effectiveCooldown,
|
|
65365
65439
|
consecutiveFailures: state3.consecutiveFailures ?? 0
|
|
@@ -65389,24 +65463,24 @@ async function handleSessionIdle(args) {
|
|
|
65389
65463
|
}
|
|
65390
65464
|
}
|
|
65391
65465
|
} catch (error51) {
|
|
65392
|
-
log(`[${
|
|
65466
|
+
log(`[${HOOK_NAME15}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
|
|
65393
65467
|
}
|
|
65394
|
-
log(`[${
|
|
65468
|
+
log(`[${HOOK_NAME15}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
|
|
65395
65469
|
const resolvedAgentName = resolvedInfo?.agent;
|
|
65396
65470
|
if (resolvedAgentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
|
|
65397
|
-
log(`[${
|
|
65471
|
+
log(`[${HOOK_NAME15}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
|
|
65398
65472
|
return;
|
|
65399
65473
|
}
|
|
65400
65474
|
if (hasCompactionMessage && !resolvedInfo?.agent) {
|
|
65401
|
-
log(`[${
|
|
65475
|
+
log(`[${HOOK_NAME15}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
|
|
65402
65476
|
return;
|
|
65403
65477
|
}
|
|
65404
65478
|
if (isContinuationStopped?.(sessionID)) {
|
|
65405
|
-
log(`[${
|
|
65479
|
+
log(`[${HOOK_NAME15}] Skipped: continuation stopped for session`, { sessionID });
|
|
65406
65480
|
return;
|
|
65407
65481
|
}
|
|
65408
65482
|
if (isAwaitingUser(state3)) {
|
|
65409
|
-
log(`[${
|
|
65483
|
+
log(`[${HOOK_NAME15}] Skipped: awaiting user (race)`, { sessionID });
|
|
65410
65484
|
return;
|
|
65411
65485
|
}
|
|
65412
65486
|
startCountdown({
|
|
@@ -65439,7 +65513,7 @@ function handleNonIdleEvent(args) {
|
|
|
65439
65513
|
if (state3?.countdownStartedAt) {
|
|
65440
65514
|
const elapsed = Date.now() - state3.countdownStartedAt;
|
|
65441
65515
|
if (elapsed < COUNTDOWN_GRACE_PERIOD_MS) {
|
|
65442
|
-
log(`[${
|
|
65516
|
+
log(`[${HOOK_NAME15}] Ignoring user message in grace period`, { sessionID, elapsed });
|
|
65443
65517
|
return;
|
|
65444
65518
|
}
|
|
65445
65519
|
}
|
|
@@ -65485,7 +65559,7 @@ function handleNonIdleEvent(args) {
|
|
|
65485
65559
|
const sessionInfo = properties?.info;
|
|
65486
65560
|
if (sessionInfo?.id) {
|
|
65487
65561
|
sessionStateStore.cleanup(sessionInfo.id);
|
|
65488
|
-
log(`[${
|
|
65562
|
+
log(`[${HOOK_NAME15}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
|
|
65489
65563
|
}
|
|
65490
65564
|
return;
|
|
65491
65565
|
}
|
|
@@ -65511,10 +65585,10 @@ function createTaskContinuationHandler(args) {
|
|
|
65511
65585
|
if (error51?.name === "MessageAbortedError" || error51?.name === "AbortError") {
|
|
65512
65586
|
const state3 = sessionStateStore.getState(sessionID);
|
|
65513
65587
|
state3.abortDetectedAt = Date.now();
|
|
65514
|
-
log(`[${
|
|
65588
|
+
log(`[${HOOK_NAME15}] Abort detected via session.error`, { sessionID, errorName: error51.name });
|
|
65515
65589
|
}
|
|
65516
65590
|
sessionStateStore.cancelCountdown(sessionID);
|
|
65517
|
-
log(`[${
|
|
65591
|
+
log(`[${HOOK_NAME15}] session.error`, { sessionID });
|
|
65518
65592
|
return;
|
|
65519
65593
|
}
|
|
65520
65594
|
if (event.type === "session.idle") {
|
|
@@ -65634,13 +65708,13 @@ function createTaskContinuationEnforcer(ctx, options = {}) {
|
|
|
65634
65708
|
const state3 = sessionStateStore.getState(sessionID);
|
|
65635
65709
|
state3.isRecovering = true;
|
|
65636
65710
|
sessionStateStore.cancelCountdown(sessionID);
|
|
65637
|
-
log(`[${
|
|
65711
|
+
log(`[${HOOK_NAME15}] Session marked as recovering`, { sessionID });
|
|
65638
65712
|
};
|
|
65639
65713
|
const markRecoveryComplete = (sessionID) => {
|
|
65640
65714
|
const state3 = sessionStateStore.getExistingState(sessionID);
|
|
65641
65715
|
if (state3) {
|
|
65642
65716
|
state3.isRecovering = false;
|
|
65643
|
-
log(`[${
|
|
65717
|
+
log(`[${HOOK_NAME15}] Session recovery complete`, { sessionID });
|
|
65644
65718
|
}
|
|
65645
65719
|
};
|
|
65646
65720
|
const handler = createTaskContinuationHandler({
|
|
@@ -65653,7 +65727,7 @@ function createTaskContinuationEnforcer(ctx, options = {}) {
|
|
|
65653
65727
|
});
|
|
65654
65728
|
const cancelAllCountdowns = () => {
|
|
65655
65729
|
sessionStateStore.cancelAllCountdowns();
|
|
65656
|
-
log(`[${
|
|
65730
|
+
log(`[${HOOK_NAME15}] All countdowns cancelled`);
|
|
65657
65731
|
};
|
|
65658
65732
|
return {
|
|
65659
65733
|
handler,
|
|
@@ -65664,8 +65738,8 @@ function createTaskContinuationEnforcer(ctx, options = {}) {
|
|
|
65664
65738
|
};
|
|
65665
65739
|
}
|
|
65666
65740
|
// src/hooks/task-edit-guard/constants.ts
|
|
65667
|
-
var
|
|
65668
|
-
var
|
|
65741
|
+
var HOOK_NAME16 = "task-edit-guard";
|
|
65742
|
+
var BLOCKED_PATTERNS2 = [
|
|
65669
65743
|
/sed\s+.*\.matrixx\/plans/,
|
|
65670
65744
|
/sed\s+.*\.matrixx\/tasks/,
|
|
65671
65745
|
/python3?\s+.*\.matrixx\/plans/,
|
|
@@ -65698,7 +65772,7 @@ function createTaskEditGuardHook(ctx) {
|
|
|
65698
65772
|
if (filePath) {
|
|
65699
65773
|
const normalized = filePath.toLowerCase().replace(/\\/g, "/");
|
|
65700
65774
|
if (normalized.includes(".matrixx/plans")) {
|
|
65701
|
-
log(`[${
|
|
65775
|
+
log(`[${HOOK_NAME16}] BLOCKED generic Write/Edit to .matrixx/plans \u2014 use plan_*`, {
|
|
65702
65776
|
sessionID: input.sessionID,
|
|
65703
65777
|
tool: input.tool,
|
|
65704
65778
|
filePath
|
|
@@ -65714,7 +65788,7 @@ function createTaskEditGuardHook(ctx) {
|
|
|
65714
65788
|
if (filePath) {
|
|
65715
65789
|
const normalized = filePath.toLowerCase().replace(/\\/g, "/");
|
|
65716
65790
|
if (normalized.includes(".matrixx/plans")) {
|
|
65717
|
-
log(`[${
|
|
65791
|
+
log(`[${HOOK_NAME16}] BLOCKED generic Read to .matrixx/plans \u2014 use plan_read`, {
|
|
65718
65792
|
sessionID: input.sessionID,
|
|
65719
65793
|
tool: input.tool,
|
|
65720
65794
|
filePath
|
|
@@ -65732,15 +65806,15 @@ function createTaskEditGuardHook(ctx) {
|
|
|
65732
65806
|
return;
|
|
65733
65807
|
const lower = cmd.toLowerCase();
|
|
65734
65808
|
if (lower.includes("grep") && !lower.includes("sed") && !lower.includes("python")) {
|
|
65735
|
-
const isOnlyGrep =
|
|
65809
|
+
const isOnlyGrep = BLOCKED_PATTERNS2.every((rx) => !rx.test(cmd));
|
|
65736
65810
|
if (isOnlyGrep)
|
|
65737
65811
|
return;
|
|
65738
65812
|
}
|
|
65739
|
-
const hit =
|
|
65813
|
+
const hit = BLOCKED_PATTERNS2.some((rx) => rx.test(cmd));
|
|
65740
65814
|
if (!hit)
|
|
65741
65815
|
return;
|
|
65742
65816
|
ctx.directory;
|
|
65743
|
-
log(`[${
|
|
65817
|
+
log(`[${HOOK_NAME16}] BLOCKED raw bash edit`, {
|
|
65744
65818
|
sessionID: input.sessionID,
|
|
65745
65819
|
command: cmd.slice(0, 120)
|
|
65746
65820
|
});
|
|
@@ -65749,7 +65823,7 @@ function createTaskEditGuardHook(ctx) {
|
|
|
65749
65823
|
};
|
|
65750
65824
|
}
|
|
65751
65825
|
// src/hooks/task-notepad/constants.ts
|
|
65752
|
-
var
|
|
65826
|
+
var HOOK_NAME17 = "task-notepad";
|
|
65753
65827
|
var PLAN_DIR_NAME = ".matrixx";
|
|
65754
65828
|
var PLANS_SUBDIR = "plans";
|
|
65755
65829
|
var NOTEPAD_DIR_NAME = "notepads";
|
|
@@ -65778,7 +65852,7 @@ function createTaskNotepadHook(ctx) {
|
|
|
65778
65852
|
const plan = resolveActivePlan(ctx.directory);
|
|
65779
65853
|
if (inProgress) {
|
|
65780
65854
|
if (!plan) {
|
|
65781
|
-
log(`[${
|
|
65855
|
+
log(`[${HOOK_NAME17}] No active plan, skipping notepad creation`, {
|
|
65782
65856
|
sessionID: input.sessionID
|
|
65783
65857
|
});
|
|
65784
65858
|
} else {
|
|
@@ -65805,7 +65879,7 @@ async function fetchTodos(ctx, sessionID) {
|
|
|
65805
65879
|
}
|
|
65806
65880
|
return [];
|
|
65807
65881
|
} catch (err) {
|
|
65808
|
-
log(`[${
|
|
65882
|
+
log(`[${HOOK_NAME17}] ${TODO_FETCH_ERROR_FRAGMENT}`, {
|
|
65809
65883
|
sessionID,
|
|
65810
65884
|
error: String(err)
|
|
65811
65885
|
});
|
|
@@ -65850,7 +65924,7 @@ function ensureNotepad(directory, planName, todo) {
|
|
|
65850
65924
|
mkdirSync16(dirname11(notepadPath), { recursive: true });
|
|
65851
65925
|
const content = renderNotepad(todo);
|
|
65852
65926
|
writeFileSync18(notepadPath, content, "utf-8");
|
|
65853
|
-
log(`[${
|
|
65927
|
+
log(`[${HOOK_NAME17}] Created task notepad`, { plan: planName, taskID: todo.id });
|
|
65854
65928
|
}
|
|
65855
65929
|
function appendCompletion(directory, planName, todo) {
|
|
65856
65930
|
const notepadPath = notepadPathFor(directory, planName, todo.id);
|
|
@@ -65866,7 +65940,7 @@ function appendCompletion(directory, planName, todo) {
|
|
|
65866
65940
|
- status: completed
|
|
65867
65941
|
`;
|
|
65868
65942
|
writeFileSync18(notepadPath, updated, "utf-8");
|
|
65869
|
-
log(`[${
|
|
65943
|
+
log(`[${HOOK_NAME17}] Appended completion to notepad`, { plan: planName, taskID: todo.id });
|
|
65870
65944
|
}
|
|
65871
65945
|
function notepadPathFor(directory, planName, taskID) {
|
|
65872
65946
|
return join53(directory, PLAN_DIR_NAME, NOTEPAD_DIR_NAME, planName, TASKS_SUBDIR, `${taskID}${NOTEPAD_EXTENSION}`);
|
|
@@ -66453,7 +66527,7 @@ function createThinkingBlockValidatorHook() {
|
|
|
66453
66527
|
init_logger();
|
|
66454
66528
|
|
|
66455
66529
|
// src/hooks/todo-continuation-enforcer/constants.ts
|
|
66456
|
-
var
|
|
66530
|
+
var HOOK_NAME18 = "todo-continuation-enforcer";
|
|
66457
66531
|
var DEFAULT_SKIP_AGENTS2 = ["oracle", "compaction"];
|
|
66458
66532
|
var CONTINUATION_PROMPT3 = `${createSystemDirective(SystemDirectiveTypes.TODO_CONTINUATION)}
|
|
66459
66533
|
|
|
@@ -66518,21 +66592,21 @@ async function injectContinuation2(args) {
|
|
|
66518
66592
|
sessionStateStore
|
|
66519
66593
|
} = args;
|
|
66520
66594
|
if (subagentSessions.has(sessionID)) {
|
|
66521
|
-
log(`[${
|
|
66595
|
+
log(`[${HOOK_NAME18}] Skipped injection: subagent session`, { sessionID });
|
|
66522
66596
|
return;
|
|
66523
66597
|
}
|
|
66524
66598
|
const state3 = sessionStateStore.getExistingState(sessionID);
|
|
66525
66599
|
if (state3?.isRecovering) {
|
|
66526
|
-
log(`[${
|
|
66600
|
+
log(`[${HOOK_NAME18}] Skipped injection: in recovery`, { sessionID });
|
|
66527
66601
|
return;
|
|
66528
66602
|
}
|
|
66529
66603
|
if (isAwaitingUser(state3)) {
|
|
66530
|
-
log(`[${
|
|
66604
|
+
log(`[${HOOK_NAME18}] Skipped injection: awaiting user`, { sessionID });
|
|
66531
66605
|
return;
|
|
66532
66606
|
}
|
|
66533
66607
|
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
|
|
66534
66608
|
if (hasRunningBgTasks) {
|
|
66535
|
-
log(`[${
|
|
66609
|
+
log(`[${HOOK_NAME18}] Skipped injection: background tasks running`, { sessionID });
|
|
66536
66610
|
return;
|
|
66537
66611
|
}
|
|
66538
66612
|
let todos = [];
|
|
@@ -66540,12 +66614,12 @@ async function injectContinuation2(args) {
|
|
|
66540
66614
|
const response = await ctx.client.session.todo({ path: { id: sessionID } });
|
|
66541
66615
|
todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
|
|
66542
66616
|
} catch (error51) {
|
|
66543
|
-
log(`[${
|
|
66617
|
+
log(`[${HOOK_NAME18}] Failed to fetch todos`, { sessionID, error: String(error51) });
|
|
66544
66618
|
return;
|
|
66545
66619
|
}
|
|
66546
66620
|
const freshIncompleteCount = getIncompleteCount(todos);
|
|
66547
66621
|
if (freshIncompleteCount === 0) {
|
|
66548
|
-
log(`[${
|
|
66622
|
+
log(`[${HOOK_NAME18}] Skipped injection: no incomplete todos`, { sessionID });
|
|
66549
66623
|
return;
|
|
66550
66624
|
}
|
|
66551
66625
|
let agentName = resolvedInfo?.agent;
|
|
@@ -66568,11 +66642,11 @@ async function injectContinuation2(args) {
|
|
|
66568
66642
|
tools = tools ?? previousMessage?.tools;
|
|
66569
66643
|
}
|
|
66570
66644
|
if (agentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(agentName))) {
|
|
66571
|
-
log(`[${
|
|
66645
|
+
log(`[${HOOK_NAME18}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
|
|
66572
66646
|
return;
|
|
66573
66647
|
}
|
|
66574
66648
|
if (!hasWritePermission2(tools)) {
|
|
66575
|
-
log(`[${
|
|
66649
|
+
log(`[${HOOK_NAME18}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
|
|
66576
66650
|
return;
|
|
66577
66651
|
}
|
|
66578
66652
|
const incompleteTodos = todos.filter((todo) => todo.status !== "completed" && todo.status !== "cancelled");
|
|
@@ -66589,11 +66663,11 @@ ${todoList}`;
|
|
|
66589
66663
|
injectionState.inFlight = true;
|
|
66590
66664
|
}
|
|
66591
66665
|
if (isAwaitingUser(sessionStateStore.getExistingState(sessionID))) {
|
|
66592
|
-
log(`[${
|
|
66666
|
+
log(`[${HOOK_NAME18}] Skipped injection: awaiting user (race)`, { sessionID });
|
|
66593
66667
|
return;
|
|
66594
66668
|
}
|
|
66595
66669
|
try {
|
|
66596
|
-
log(`[${
|
|
66670
|
+
log(`[${HOOK_NAME18}] Injecting continuation`, {
|
|
66597
66671
|
sessionID,
|
|
66598
66672
|
agent: agentName,
|
|
66599
66673
|
model,
|
|
@@ -66608,14 +66682,14 @@ ${todoList}`;
|
|
|
66608
66682
|
},
|
|
66609
66683
|
query: { directory: ctx.directory }
|
|
66610
66684
|
});
|
|
66611
|
-
log(`[${
|
|
66685
|
+
log(`[${HOOK_NAME18}] Injection successful`, { sessionID });
|
|
66612
66686
|
if (injectionState) {
|
|
66613
66687
|
injectionState.inFlight = false;
|
|
66614
66688
|
injectionState.lastInjectedAt = Date.now();
|
|
66615
66689
|
injectionState.consecutiveFailures = 0;
|
|
66616
66690
|
}
|
|
66617
66691
|
} catch (error51) {
|
|
66618
|
-
log(`[${
|
|
66692
|
+
log(`[${HOOK_NAME18}] Injection failed`, { sessionID, error: String(error51) });
|
|
66619
66693
|
if (injectionState) {
|
|
66620
66694
|
injectionState.inFlight = false;
|
|
66621
66695
|
injectionState.lastInjectedAt = Date.now();
|
|
@@ -66649,7 +66723,7 @@ function startCountdown2(args) {
|
|
|
66649
66723
|
} = args;
|
|
66650
66724
|
const state3 = sessionStateStore.getState(sessionID);
|
|
66651
66725
|
if (state3.countdownTimer || state3.countdownInterval) {
|
|
66652
|
-
log(`[${
|
|
66726
|
+
log(`[${HOOK_NAME18}] Skipped: countdown already active`, { sessionID });
|
|
66653
66727
|
return;
|
|
66654
66728
|
}
|
|
66655
66729
|
let secondsRemaining = COUNTDOWN_SECONDS2;
|
|
@@ -66672,7 +66746,7 @@ function startCountdown2(args) {
|
|
|
66672
66746
|
sessionStateStore
|
|
66673
66747
|
});
|
|
66674
66748
|
}, COUNTDOWN_SECONDS2 * 1000);
|
|
66675
|
-
log(`[${
|
|
66749
|
+
log(`[${HOOK_NAME18}] Countdown started`, {
|
|
66676
66750
|
sessionID,
|
|
66677
66751
|
seconds: COUNTDOWN_SECONDS2,
|
|
66678
66752
|
incompleteCount
|
|
@@ -66689,36 +66763,36 @@ async function handleSessionIdle2(args) {
|
|
|
66689
66763
|
skipAgents = DEFAULT_SKIP_AGENTS2,
|
|
66690
66764
|
isContinuationStopped
|
|
66691
66765
|
} = args;
|
|
66692
|
-
log(`[${
|
|
66766
|
+
log(`[${HOOK_NAME18}] session.idle`, { sessionID });
|
|
66693
66767
|
if (subagentSessions.has(sessionID)) {
|
|
66694
|
-
log(`[${
|
|
66768
|
+
log(`[${HOOK_NAME18}] Skipped: subagent session`, { sessionID });
|
|
66695
66769
|
return;
|
|
66696
66770
|
}
|
|
66697
66771
|
const state3 = sessionStateStore.getState(sessionID);
|
|
66698
66772
|
if (state3.countdownTimer || state3.countdownInterval) {
|
|
66699
|
-
log(`[${
|
|
66773
|
+
log(`[${HOOK_NAME18}] Skipped: countdown already active`, { sessionID });
|
|
66700
66774
|
return;
|
|
66701
66775
|
}
|
|
66702
66776
|
if (state3.isRecovering) {
|
|
66703
|
-
log(`[${
|
|
66777
|
+
log(`[${HOOK_NAME18}] Skipped: in recovery`, { sessionID });
|
|
66704
66778
|
return;
|
|
66705
66779
|
}
|
|
66706
66780
|
if (state3.abortDetectedAt) {
|
|
66707
66781
|
const timeSinceAbort = Date.now() - state3.abortDetectedAt;
|
|
66708
66782
|
if (timeSinceAbort < ABORT_WINDOW_MS2) {
|
|
66709
|
-
log(`[${
|
|
66783
|
+
log(`[${HOOK_NAME18}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
|
|
66710
66784
|
state3.abortDetectedAt = undefined;
|
|
66711
66785
|
return;
|
|
66712
66786
|
}
|
|
66713
66787
|
state3.abortDetectedAt = undefined;
|
|
66714
66788
|
}
|
|
66715
66789
|
if (isAwaitingUser(state3)) {
|
|
66716
|
-
log(`[${
|
|
66790
|
+
log(`[${HOOK_NAME18}] Skipped: awaiting user`, { sessionID });
|
|
66717
66791
|
return;
|
|
66718
66792
|
}
|
|
66719
66793
|
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
|
|
66720
66794
|
if (hasRunningBgTasks) {
|
|
66721
|
-
log(`[${
|
|
66795
|
+
log(`[${HOOK_NAME18}] Skipped: background tasks running`, { sessionID });
|
|
66722
66796
|
return;
|
|
66723
66797
|
}
|
|
66724
66798
|
try {
|
|
@@ -66728,46 +66802,46 @@ async function handleSessionIdle2(args) {
|
|
|
66728
66802
|
});
|
|
66729
66803
|
const messages = normalizeSDKResponse(messagesResp, []);
|
|
66730
66804
|
if (isLastAssistantMessageAborted2(messages)) {
|
|
66731
|
-
log(`[${
|
|
66805
|
+
log(`[${HOOK_NAME18}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
|
|
66732
66806
|
return;
|
|
66733
66807
|
}
|
|
66734
66808
|
if (isAwaitingUser(state3, messages)) {
|
|
66735
|
-
log(`[${
|
|
66809
|
+
log(`[${HOOK_NAME18}] Skipped: awaiting user (pending question)`, { sessionID });
|
|
66736
66810
|
return;
|
|
66737
66811
|
}
|
|
66738
66812
|
} catch (error51) {
|
|
66739
|
-
log(`[${
|
|
66813
|
+
log(`[${HOOK_NAME18}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
|
|
66740
66814
|
}
|
|
66741
66815
|
let todos = [];
|
|
66742
66816
|
try {
|
|
66743
66817
|
const response = await ctx.client.session.todo({ path: { id: sessionID } });
|
|
66744
66818
|
todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
|
|
66745
66819
|
} catch (error51) {
|
|
66746
|
-
log(`[${
|
|
66820
|
+
log(`[${HOOK_NAME18}] Todo fetch failed`, { sessionID, error: String(error51) });
|
|
66747
66821
|
return;
|
|
66748
66822
|
}
|
|
66749
66823
|
if (!todos || todos.length === 0) {
|
|
66750
|
-
log(`[${
|
|
66824
|
+
log(`[${HOOK_NAME18}] No todos`, { sessionID });
|
|
66751
66825
|
return;
|
|
66752
66826
|
}
|
|
66753
66827
|
const incompleteCount = getIncompleteCount(todos);
|
|
66754
66828
|
if (incompleteCount === 0) {
|
|
66755
|
-
log(`[${
|
|
66829
|
+
log(`[${HOOK_NAME18}] All todos complete`, { sessionID, total: todos.length });
|
|
66756
66830
|
return;
|
|
66757
66831
|
}
|
|
66758
66832
|
if (state3.inFlight) {
|
|
66759
|
-
log(`[${
|
|
66833
|
+
log(`[${HOOK_NAME18}] Skipped: injection in flight`, { sessionID });
|
|
66760
66834
|
return;
|
|
66761
66835
|
}
|
|
66762
66836
|
if (state3.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES2 && state3.lastInjectedAt && Date.now() - state3.lastInjectedAt >= FAILURE_RESET_WINDOW_MS2) {
|
|
66763
66837
|
state3.consecutiveFailures = 0;
|
|
66764
|
-
log(`[${
|
|
66838
|
+
log(`[${HOOK_NAME18}] Reset consecutive failures after recovery window`, {
|
|
66765
66839
|
sessionID,
|
|
66766
66840
|
failureResetWindowMs: FAILURE_RESET_WINDOW_MS2
|
|
66767
66841
|
});
|
|
66768
66842
|
}
|
|
66769
66843
|
if (state3.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES2) {
|
|
66770
|
-
log(`[${
|
|
66844
|
+
log(`[${HOOK_NAME18}] Skipped: max consecutive failures reached`, {
|
|
66771
66845
|
sessionID,
|
|
66772
66846
|
consecutiveFailures: state3.consecutiveFailures,
|
|
66773
66847
|
maxConsecutiveFailures: MAX_CONSECUTIVE_FAILURES2
|
|
@@ -66776,7 +66850,7 @@ async function handleSessionIdle2(args) {
|
|
|
66776
66850
|
}
|
|
66777
66851
|
const effectiveCooldown = CONTINUATION_COOLDOWN_MS4 * 2 ** Math.min(state3.consecutiveFailures, 5);
|
|
66778
66852
|
if (state3.lastInjectedAt && Date.now() - state3.lastInjectedAt < effectiveCooldown) {
|
|
66779
|
-
log(`[${
|
|
66853
|
+
log(`[${HOOK_NAME18}] Skipped: cooldown active`, {
|
|
66780
66854
|
sessionID,
|
|
66781
66855
|
effectiveCooldown,
|
|
66782
66856
|
consecutiveFailures: state3.consecutiveFailures
|
|
@@ -66806,24 +66880,24 @@ async function handleSessionIdle2(args) {
|
|
|
66806
66880
|
}
|
|
66807
66881
|
}
|
|
66808
66882
|
} catch (error51) {
|
|
66809
|
-
log(`[${
|
|
66883
|
+
log(`[${HOOK_NAME18}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
|
|
66810
66884
|
}
|
|
66811
|
-
log(`[${
|
|
66885
|
+
log(`[${HOOK_NAME18}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
|
|
66812
66886
|
const resolvedAgentName = resolvedInfo?.agent;
|
|
66813
66887
|
if (resolvedAgentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
|
|
66814
|
-
log(`[${
|
|
66888
|
+
log(`[${HOOK_NAME18}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
|
|
66815
66889
|
return;
|
|
66816
66890
|
}
|
|
66817
66891
|
if (hasCompactionMessage && !resolvedInfo?.agent) {
|
|
66818
|
-
log(`[${
|
|
66892
|
+
log(`[${HOOK_NAME18}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
|
|
66819
66893
|
return;
|
|
66820
66894
|
}
|
|
66821
66895
|
if (isContinuationStopped?.(sessionID)) {
|
|
66822
|
-
log(`[${
|
|
66896
|
+
log(`[${HOOK_NAME18}] Skipped: continuation stopped for session`, { sessionID });
|
|
66823
66897
|
return;
|
|
66824
66898
|
}
|
|
66825
66899
|
if (isAwaitingUser(state3)) {
|
|
66826
|
-
log(`[${
|
|
66900
|
+
log(`[${HOOK_NAME18}] Skipped: awaiting user (race)`, { sessionID });
|
|
66827
66901
|
return;
|
|
66828
66902
|
}
|
|
66829
66903
|
startCountdown2({
|
|
@@ -66855,7 +66929,7 @@ function handleNonIdleEvent2(args) {
|
|
|
66855
66929
|
if (state3?.countdownStartedAt) {
|
|
66856
66930
|
const elapsed = Date.now() - state3.countdownStartedAt;
|
|
66857
66931
|
if (elapsed < COUNTDOWN_GRACE_PERIOD_MS2) {
|
|
66858
|
-
log(`[${
|
|
66932
|
+
log(`[${HOOK_NAME18}] Ignoring user message in grace period`, { sessionID, elapsed });
|
|
66859
66933
|
return;
|
|
66860
66934
|
}
|
|
66861
66935
|
}
|
|
@@ -66901,7 +66975,7 @@ function handleNonIdleEvent2(args) {
|
|
|
66901
66975
|
const sessionInfo = properties?.info;
|
|
66902
66976
|
if (sessionInfo?.id) {
|
|
66903
66977
|
sessionStateStore.cleanup(sessionInfo.id);
|
|
66904
|
-
log(`[${
|
|
66978
|
+
log(`[${HOOK_NAME18}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
|
|
66905
66979
|
}
|
|
66906
66980
|
return;
|
|
66907
66981
|
}
|
|
@@ -66926,10 +67000,10 @@ function createTodoContinuationHandler(args) {
|
|
|
66926
67000
|
if (error51?.name === "MessageAbortedError" || error51?.name === "AbortError") {
|
|
66927
67001
|
const state3 = sessionStateStore.getState(sessionID);
|
|
66928
67002
|
state3.abortDetectedAt = Date.now();
|
|
66929
|
-
log(`[${
|
|
67003
|
+
log(`[${HOOK_NAME18}] Abort detected via session.error`, { sessionID, errorName: error51.name });
|
|
66930
67004
|
}
|
|
66931
67005
|
sessionStateStore.cancelCountdown(sessionID);
|
|
66932
|
-
log(`[${
|
|
67006
|
+
log(`[${HOOK_NAME18}] session.error`, { sessionID });
|
|
66933
67007
|
return;
|
|
66934
67008
|
}
|
|
66935
67009
|
if (event.type === "session.idle") {
|
|
@@ -67047,13 +67121,13 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
67047
67121
|
const state3 = sessionStateStore.getState(sessionID);
|
|
67048
67122
|
state3.isRecovering = true;
|
|
67049
67123
|
sessionStateStore.cancelCountdown(sessionID);
|
|
67050
|
-
log(`[${
|
|
67124
|
+
log(`[${HOOK_NAME18}] Session marked as recovering`, { sessionID });
|
|
67051
67125
|
};
|
|
67052
67126
|
const markRecoveryComplete = (sessionID) => {
|
|
67053
67127
|
const state3 = sessionStateStore.getExistingState(sessionID);
|
|
67054
67128
|
if (state3) {
|
|
67055
67129
|
state3.isRecovering = false;
|
|
67056
|
-
log(`[${
|
|
67130
|
+
log(`[${HOOK_NAME18}] Session recovery complete`, { sessionID });
|
|
67057
67131
|
}
|
|
67058
67132
|
};
|
|
67059
67133
|
const handler = createTodoContinuationHandler({
|
|
@@ -67065,7 +67139,7 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
67065
67139
|
});
|
|
67066
67140
|
const cancelAllCountdowns = () => {
|
|
67067
67141
|
sessionStateStore.cancelAllCountdowns();
|
|
67068
|
-
log(`[${
|
|
67142
|
+
log(`[${HOOK_NAME18}] All countdowns cancelled`);
|
|
67069
67143
|
};
|
|
67070
67144
|
return {
|
|
67071
67145
|
handler,
|
|
@@ -67299,7 +67373,7 @@ This is a reminder only. No automatic action was taken.`;
|
|
|
67299
67373
|
}
|
|
67300
67374
|
// src/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.ts
|
|
67301
67375
|
init_logger();
|
|
67302
|
-
var
|
|
67376
|
+
var HOOK_NAME19 = "unstable-agent-babysitter";
|
|
67303
67377
|
var DEFAULT_TIMEOUT_MS = 120000;
|
|
67304
67378
|
var COOLDOWN_MS = 5 * 60 * 1000;
|
|
67305
67379
|
async function resolveMainSessionTarget(ctx, sessionID) {
|
|
@@ -67319,7 +67393,7 @@ async function resolveMainSessionTarget(ctx, sessionID) {
|
|
|
67319
67393
|
}
|
|
67320
67394
|
}
|
|
67321
67395
|
} catch (error51) {
|
|
67322
|
-
log(`[${
|
|
67396
|
+
log(`[${HOOK_NAME19}] Failed to resolve main session agent`, { sessionID, error: String(error51) });
|
|
67323
67397
|
}
|
|
67324
67398
|
return { agent, model };
|
|
67325
67399
|
}
|
|
@@ -67352,7 +67426,7 @@ async function getThinkingSummary(ctx, sessionID) {
|
|
|
67352
67426
|
return combined;
|
|
67353
67427
|
return `${combined.slice(0, THINKING_SUMMARY_MAX_CHARS)}...`;
|
|
67354
67428
|
} catch (error51) {
|
|
67355
|
-
log(`[${
|
|
67429
|
+
log(`[${HOOK_NAME19}] Failed to fetch thinking summary`, { sessionID, error: String(error51) });
|
|
67356
67430
|
return null;
|
|
67357
67431
|
}
|
|
67358
67432
|
}
|
|
@@ -67401,9 +67475,9 @@ function createUnstableAgentBabysitterHook(ctx, options) {
|
|
|
67401
67475
|
query: { directory: ctx.directory }
|
|
67402
67476
|
});
|
|
67403
67477
|
reminderCooldowns.set(task.id, now);
|
|
67404
|
-
log(`[${
|
|
67478
|
+
log(`[${HOOK_NAME19}] Reminder injected`, { taskId: task.id, sessionID: mainSessionID });
|
|
67405
67479
|
} catch (error51) {
|
|
67406
|
-
log(`[${
|
|
67480
|
+
log(`[${HOOK_NAME19}] Reminder injection failed`, { taskId: task.id, error: String(error51) });
|
|
67407
67481
|
}
|
|
67408
67482
|
}
|
|
67409
67483
|
};
|
|
@@ -84356,8 +84430,13 @@ function createBddValidateContractTool() {
|
|
|
84356
84430
|
}
|
|
84357
84431
|
});
|
|
84358
84432
|
}
|
|
84359
|
-
// src/tools/
|
|
84360
|
-
import {
|
|
84433
|
+
// src/tools/github-search/cli.ts
|
|
84434
|
+
import { spawn as spawn11, spawnSync as spawnSync3 } from "child_process";
|
|
84435
|
+
import { existsSync as existsSync59, mkdirSync as mkdirSync17 } from "fs";
|
|
84436
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
84437
|
+
import { join as join62 } from "path";
|
|
84438
|
+
|
|
84439
|
+
// src/tools/grep/cli.ts
|
|
84361
84440
|
var {spawn: spawn10 } = globalThis.Bun;
|
|
84362
84441
|
|
|
84363
84442
|
// src/tools/grep/constants.ts
|
|
@@ -84552,8 +84631,367 @@ var RG_SAFETY_FLAGS = [
|
|
|
84552
84631
|
];
|
|
84553
84632
|
var GREP_SAFETY_FLAGS = ["-n", "-H", "--color=never"];
|
|
84554
84633
|
|
|
84555
|
-
// src/tools/
|
|
84634
|
+
// src/tools/grep/cli.ts
|
|
84635
|
+
function buildRgArgs(options) {
|
|
84636
|
+
const args = [
|
|
84637
|
+
...RG_SAFETY_FLAGS,
|
|
84638
|
+
`--max-depth=${Math.min(options.maxDepth ?? DEFAULT_MAX_DEPTH, DEFAULT_MAX_DEPTH)}`,
|
|
84639
|
+
`--max-filesize=${options.maxFilesize ?? DEFAULT_MAX_FILESIZE}`,
|
|
84640
|
+
`--max-count=${Math.min(options.maxCount ?? DEFAULT_MAX_COUNT, DEFAULT_MAX_COUNT)}`,
|
|
84641
|
+
`--max-columns=${Math.min(options.maxColumns ?? DEFAULT_MAX_COLUMNS, DEFAULT_MAX_COLUMNS)}`
|
|
84642
|
+
];
|
|
84643
|
+
if (options.context !== undefined && options.context > 0) {
|
|
84644
|
+
args.push(`-C${Math.min(options.context, 10)}`);
|
|
84645
|
+
}
|
|
84646
|
+
if (options.caseSensitive)
|
|
84647
|
+
args.push("--case-sensitive");
|
|
84648
|
+
if (options.wholeWord)
|
|
84649
|
+
args.push("-w");
|
|
84650
|
+
if (options.fixedStrings)
|
|
84651
|
+
args.push("-F");
|
|
84652
|
+
if (options.multiline)
|
|
84653
|
+
args.push("-U");
|
|
84654
|
+
if (options.hidden)
|
|
84655
|
+
args.push("--hidden");
|
|
84656
|
+
if (options.noIgnore)
|
|
84657
|
+
args.push("--no-ignore");
|
|
84658
|
+
if (options.fileType?.length) {
|
|
84659
|
+
for (const type of options.fileType) {
|
|
84660
|
+
args.push(`--type=${type}`);
|
|
84661
|
+
}
|
|
84662
|
+
}
|
|
84663
|
+
if (options.globs) {
|
|
84664
|
+
for (const glob of options.globs) {
|
|
84665
|
+
args.push(`--glob=${glob}`);
|
|
84666
|
+
}
|
|
84667
|
+
}
|
|
84668
|
+
if (options.excludeGlobs) {
|
|
84669
|
+
for (const glob of options.excludeGlobs) {
|
|
84670
|
+
args.push(`--glob=!${glob}`);
|
|
84671
|
+
}
|
|
84672
|
+
}
|
|
84673
|
+
return args;
|
|
84674
|
+
}
|
|
84675
|
+
function buildGrepArgs(options) {
|
|
84676
|
+
const args = [...GREP_SAFETY_FLAGS, "-r"];
|
|
84677
|
+
if (options.context !== undefined && options.context > 0) {
|
|
84678
|
+
args.push(`-C${Math.min(options.context, 10)}`);
|
|
84679
|
+
}
|
|
84680
|
+
if (!options.caseSensitive)
|
|
84681
|
+
args.push("-i");
|
|
84682
|
+
if (options.wholeWord)
|
|
84683
|
+
args.push("-w");
|
|
84684
|
+
if (options.fixedStrings)
|
|
84685
|
+
args.push("-F");
|
|
84686
|
+
if (options.globs?.length) {
|
|
84687
|
+
for (const glob of options.globs) {
|
|
84688
|
+
args.push(`--include=${glob}`);
|
|
84689
|
+
}
|
|
84690
|
+
}
|
|
84691
|
+
if (options.excludeGlobs?.length) {
|
|
84692
|
+
for (const glob of options.excludeGlobs) {
|
|
84693
|
+
args.push(`--exclude=${glob}`);
|
|
84694
|
+
}
|
|
84695
|
+
}
|
|
84696
|
+
args.push("--exclude-dir=.git", "--exclude-dir=node_modules");
|
|
84697
|
+
return args;
|
|
84698
|
+
}
|
|
84699
|
+
function buildArgs(options, backend) {
|
|
84700
|
+
return backend === "rg" ? buildRgArgs(options) : buildGrepArgs(options);
|
|
84701
|
+
}
|
|
84702
|
+
function parseOutput(output) {
|
|
84703
|
+
if (!output.trim())
|
|
84704
|
+
return [];
|
|
84705
|
+
const matches = [];
|
|
84706
|
+
const lines = output.split(`
|
|
84707
|
+
`);
|
|
84708
|
+
for (const line of lines) {
|
|
84709
|
+
if (!line.trim())
|
|
84710
|
+
continue;
|
|
84711
|
+
const match = line.match(/^(.+?):(\d+):(.*)$/);
|
|
84712
|
+
if (match) {
|
|
84713
|
+
matches.push({
|
|
84714
|
+
file: match[1],
|
|
84715
|
+
line: parseInt(match[2], 10),
|
|
84716
|
+
text: match[3]
|
|
84717
|
+
});
|
|
84718
|
+
}
|
|
84719
|
+
}
|
|
84720
|
+
return matches;
|
|
84721
|
+
}
|
|
84722
|
+
async function runRg(options) {
|
|
84723
|
+
const cli = resolveGrepCli();
|
|
84724
|
+
const args = buildArgs(options, cli.backend);
|
|
84725
|
+
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3);
|
|
84726
|
+
if (cli.backend === "rg") {
|
|
84727
|
+
args.push("--", options.pattern);
|
|
84728
|
+
} else {
|
|
84729
|
+
args.push("-e", options.pattern);
|
|
84730
|
+
}
|
|
84731
|
+
const paths = options.paths?.length ? options.paths : ["."];
|
|
84732
|
+
args.push(...paths);
|
|
84733
|
+
const proc = spawn10([cli.path, ...args], {
|
|
84734
|
+
stdout: "pipe",
|
|
84735
|
+
stderr: "pipe"
|
|
84736
|
+
});
|
|
84737
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
84738
|
+
const id = setTimeout(() => {
|
|
84739
|
+
proc.kill();
|
|
84740
|
+
reject(new Error(`Search timeout after ${timeout}ms`));
|
|
84741
|
+
}, timeout);
|
|
84742
|
+
proc.exited.then(() => clearTimeout(id));
|
|
84743
|
+
});
|
|
84744
|
+
try {
|
|
84745
|
+
const stdout = await Promise.race([new Response(proc.stdout).text(), timeoutPromise]);
|
|
84746
|
+
const stderr = await new Response(proc.stderr).text();
|
|
84747
|
+
const exitCode = await proc.exited;
|
|
84748
|
+
const truncated = stdout.length >= DEFAULT_MAX_OUTPUT_BYTES2;
|
|
84749
|
+
const outputToProcess = truncated ? stdout.substring(0, DEFAULT_MAX_OUTPUT_BYTES2) : stdout;
|
|
84750
|
+
if (exitCode > 1 && stderr.trim()) {
|
|
84751
|
+
return {
|
|
84752
|
+
matches: [],
|
|
84753
|
+
totalMatches: 0,
|
|
84754
|
+
filesSearched: 0,
|
|
84755
|
+
truncated: false,
|
|
84756
|
+
error: stderr.trim()
|
|
84757
|
+
};
|
|
84758
|
+
}
|
|
84759
|
+
const matches = parseOutput(outputToProcess);
|
|
84760
|
+
const filesSearched = new Set(matches.map((m) => m.file)).size;
|
|
84761
|
+
return {
|
|
84762
|
+
matches,
|
|
84763
|
+
totalMatches: matches.length,
|
|
84764
|
+
filesSearched,
|
|
84765
|
+
truncated
|
|
84766
|
+
};
|
|
84767
|
+
} catch (e) {
|
|
84768
|
+
return {
|
|
84769
|
+
matches: [],
|
|
84770
|
+
totalMatches: 0,
|
|
84771
|
+
filesSearched: 0,
|
|
84772
|
+
truncated: false,
|
|
84773
|
+
error: e instanceof Error ? e.message : String(e)
|
|
84774
|
+
};
|
|
84775
|
+
}
|
|
84776
|
+
}
|
|
84777
|
+
|
|
84778
|
+
// src/tools/github-search/constants.ts
|
|
84556
84779
|
var DEFAULT_TIMEOUT_MS4 = 60000;
|
|
84780
|
+
var DEFAULT_MAX_OUTPUT_CHARS = 20000;
|
|
84781
|
+
var MAX_LIMIT = 30;
|
|
84782
|
+
var CLONE_DEPTH = 1;
|
|
84783
|
+
var TMP_PREFIX = "matrixx-gh-";
|
|
84784
|
+
|
|
84785
|
+
// src/tools/github-search/cli.ts
|
|
84786
|
+
function findExecutable2(name) {
|
|
84787
|
+
const cmd = process.platform === "win32" ? "where" : "which";
|
|
84788
|
+
try {
|
|
84789
|
+
const result = spawnSync3(cmd, [name], { encoding: "utf-8", timeout: 5000 });
|
|
84790
|
+
if (result.status === 0 && result.stdout.trim()) {
|
|
84791
|
+
return result.stdout.trim().split(`
|
|
84792
|
+
`)[0]?.trim() ?? null;
|
|
84793
|
+
}
|
|
84794
|
+
} catch {}
|
|
84795
|
+
return null;
|
|
84796
|
+
}
|
|
84797
|
+
async function runCmd(cmd, args, cwd, timeout) {
|
|
84798
|
+
const proc = spawn11(cmd, args, { cwd, timeout });
|
|
84799
|
+
let stdout = "";
|
|
84800
|
+
let stderr = "";
|
|
84801
|
+
proc.stdout?.on("data", (d) => {
|
|
84802
|
+
stdout += d.toString();
|
|
84803
|
+
});
|
|
84804
|
+
proc.stderr?.on("data", (d) => {
|
|
84805
|
+
stderr += d.toString();
|
|
84806
|
+
});
|
|
84807
|
+
const exitCode = await new Promise((resolve8) => {
|
|
84808
|
+
const timer = setTimeout(() => {
|
|
84809
|
+
try {
|
|
84810
|
+
proc.kill();
|
|
84811
|
+
} catch {}
|
|
84812
|
+
resolve8(124);
|
|
84813
|
+
}, timeout);
|
|
84814
|
+
proc.on("close", (code) => {
|
|
84815
|
+
clearTimeout(timer);
|
|
84816
|
+
resolve8(code ?? 0);
|
|
84817
|
+
});
|
|
84818
|
+
proc.on("error", () => {
|
|
84819
|
+
clearTimeout(timer);
|
|
84820
|
+
resolve8(127);
|
|
84821
|
+
});
|
|
84822
|
+
});
|
|
84823
|
+
return { stdout, stderr, exitCode };
|
|
84824
|
+
}
|
|
84825
|
+
function sanitizeRepo(repo) {
|
|
84826
|
+
return repo.replace(/[^a-zA-Z0-9-_]/g, "-").slice(0, 80);
|
|
84827
|
+
}
|
|
84828
|
+
function parseGhSearchOutput(stdout, fallbackRepo) {
|
|
84829
|
+
const trimmed = stdout.trim();
|
|
84830
|
+
if (!trimmed)
|
|
84831
|
+
return [];
|
|
84832
|
+
try {
|
|
84833
|
+
const parsed = JSON.parse(trimmed);
|
|
84834
|
+
const items = Array.isArray(parsed) ? parsed : [];
|
|
84835
|
+
return items.map((item) => ({
|
|
84836
|
+
repo: item.repository?.nameWithOwner ?? fallbackRepo,
|
|
84837
|
+
path: item.path ?? "",
|
|
84838
|
+
sha: item.sha,
|
|
84839
|
+
url: item.url
|
|
84840
|
+
}));
|
|
84841
|
+
} catch {
|
|
84842
|
+
return [];
|
|
84843
|
+
}
|
|
84844
|
+
}
|
|
84845
|
+
async function codeSearch(query, repo, language, limit) {
|
|
84846
|
+
const gh = findExecutable2("gh");
|
|
84847
|
+
if (!gh) {
|
|
84848
|
+
return {
|
|
84849
|
+
hits: [],
|
|
84850
|
+
truncated: false,
|
|
84851
|
+
mode: "code-search",
|
|
84852
|
+
error: "gh CLI not found in PATH. Install from https://cli.github.com and authenticate with `gh auth login`."
|
|
84853
|
+
};
|
|
84854
|
+
}
|
|
84855
|
+
const parts = [query];
|
|
84856
|
+
if (language)
|
|
84857
|
+
parts.push(`language:${language}`);
|
|
84858
|
+
if (repo)
|
|
84859
|
+
parts.push(`repo:${repo}`);
|
|
84860
|
+
const searchQuery = parts.join(" ");
|
|
84861
|
+
const capped = Math.min(Math.max(limit, 1), MAX_LIMIT);
|
|
84862
|
+
const args = ["search", "code", searchQuery, "--limit", String(capped), "--json", "repository,path,sha,url"];
|
|
84863
|
+
const { stdout, stderr, exitCode } = await runCmd(gh, args, process.cwd(), DEFAULT_TIMEOUT_MS4);
|
|
84864
|
+
if (exitCode !== 0) {
|
|
84865
|
+
const hint = /auth|login|token/i.test(stderr) ? " Run `gh auth login` and retry." : "";
|
|
84866
|
+
return {
|
|
84867
|
+
hits: [],
|
|
84868
|
+
truncated: false,
|
|
84869
|
+
mode: "code-search",
|
|
84870
|
+
error: `gh search code failed: ${stderr.trim() || `exit ${exitCode}`}.${hint}`
|
|
84871
|
+
};
|
|
84872
|
+
}
|
|
84873
|
+
const hits = parseGhSearchOutput(stdout, repo ?? "").slice(0, capped);
|
|
84874
|
+
return { hits, truncated: false, mode: "code-search" };
|
|
84875
|
+
}
|
|
84876
|
+
async function cloneAndSearch(query, repo) {
|
|
84877
|
+
const gh = findExecutable2("gh");
|
|
84878
|
+
const git = findExecutable2("git");
|
|
84879
|
+
const cloneBin = gh ?? git;
|
|
84880
|
+
if (!cloneBin) {
|
|
84881
|
+
return {
|
|
84882
|
+
hits: [],
|
|
84883
|
+
truncated: false,
|
|
84884
|
+
mode: "clone-search",
|
|
84885
|
+
error: "Neither gh nor git found in PATH. Install GitHub CLI (https://cli.github.com) or git."
|
|
84886
|
+
};
|
|
84887
|
+
}
|
|
84888
|
+
const dest = join62(tmpdir5(), `${TMP_PREFIX}${sanitizeRepo(repo)}`);
|
|
84889
|
+
if (!existsSync59(dest)) {
|
|
84890
|
+
mkdirSync17(dest, { recursive: true });
|
|
84891
|
+
const cloneArgs = gh ? ["repo", "clone", repo, dest, "--", "--depth", String(CLONE_DEPTH)] : ["clone", "--depth", String(CLONE_DEPTH), `https://github.com/${repo}.git`, dest];
|
|
84892
|
+
const cloned = await runCmd(cloneBin, cloneArgs, process.cwd(), DEFAULT_TIMEOUT_MS4);
|
|
84893
|
+
if (cloned.exitCode !== 0) {
|
|
84894
|
+
return {
|
|
84895
|
+
hits: [],
|
|
84896
|
+
truncated: false,
|
|
84897
|
+
mode: "clone-search",
|
|
84898
|
+
error: `Clone failed for ${repo}: ${cloned.stderr.trim() || `exit ${cloned.exitCode}`}`
|
|
84899
|
+
};
|
|
84900
|
+
}
|
|
84901
|
+
}
|
|
84902
|
+
const shaRes = await runCmd("git", ["rev-parse", "HEAD"], dest, 15000);
|
|
84903
|
+
const sha = shaRes.exitCode === 0 ? shaRes.stdout.trim() : undefined;
|
|
84904
|
+
const rgResult = await runRg({ pattern: query, paths: [dest], context: 0 });
|
|
84905
|
+
if (rgResult.error) {
|
|
84906
|
+
return { hits: [], truncated: false, mode: "clone-search", error: rgResult.error };
|
|
84907
|
+
}
|
|
84908
|
+
const hits = rgResult.matches.map((m) => {
|
|
84909
|
+
const rel = m.file.startsWith(dest) ? m.file.slice(dest.length + 1) : m.file;
|
|
84910
|
+
return {
|
|
84911
|
+
repo,
|
|
84912
|
+
path: rel,
|
|
84913
|
+
sha,
|
|
84914
|
+
line: m.line,
|
|
84915
|
+
text: m.text,
|
|
84916
|
+
url: sha ? `https://github.com/${repo}/blob/${sha}/${rel}#L${m.line}` : undefined
|
|
84917
|
+
};
|
|
84918
|
+
});
|
|
84919
|
+
return { hits, truncated: rgResult.truncated, mode: "clone-search" };
|
|
84920
|
+
}
|
|
84921
|
+
async function githubSearch(opts) {
|
|
84922
|
+
const query = opts.query.trim();
|
|
84923
|
+
if (!query) {
|
|
84924
|
+
return { hits: [], truncated: false, mode: "code-search", error: "query must not be empty" };
|
|
84925
|
+
}
|
|
84926
|
+
if (opts.repo) {
|
|
84927
|
+
return cloneAndSearch(query, opts.repo.trim());
|
|
84928
|
+
}
|
|
84929
|
+
return codeSearch(query, undefined, opts.language?.trim() || undefined, opts.limit ?? 10);
|
|
84930
|
+
}
|
|
84931
|
+
|
|
84932
|
+
// src/tools/github-search/result-formatter.ts
|
|
84933
|
+
function formatGithubSearchResult(result) {
|
|
84934
|
+
if (result.error) {
|
|
84935
|
+
return `Error: ${result.error}`;
|
|
84936
|
+
}
|
|
84937
|
+
if (result.hits.length === 0) {
|
|
84938
|
+
return "No matches found";
|
|
84939
|
+
}
|
|
84940
|
+
const lines = [];
|
|
84941
|
+
lines.push(`Found ${result.hits.length} match(es) [${result.mode}]`);
|
|
84942
|
+
lines.push("");
|
|
84943
|
+
for (const hit of result.hits) {
|
|
84944
|
+
const loc = hit.line ? `${hit.path}#L${hit.line}` : hit.path;
|
|
84945
|
+
lines.push(`${hit.repo} ${loc}`);
|
|
84946
|
+
if (hit.url)
|
|
84947
|
+
lines.push(` ${hit.url}`);
|
|
84948
|
+
if (hit.text?.trim())
|
|
84949
|
+
lines.push(` ${hit.text.trim().slice(0, 300)}`);
|
|
84950
|
+
lines.push("");
|
|
84951
|
+
}
|
|
84952
|
+
let out = lines.join(`
|
|
84953
|
+
`);
|
|
84954
|
+
if (out.length > DEFAULT_MAX_OUTPUT_CHARS) {
|
|
84955
|
+
out = `${out.slice(0, DEFAULT_MAX_OUTPUT_CHARS)}
|
|
84956
|
+
[Output truncated due to size limit]`;
|
|
84957
|
+
} else if (result.truncated) {
|
|
84958
|
+
out += "[Output truncated due to size limit]";
|
|
84959
|
+
}
|
|
84960
|
+
return out;
|
|
84961
|
+
}
|
|
84962
|
+
|
|
84963
|
+
// src/tools/github-search/tools.ts
|
|
84964
|
+
function createGithubSearchTools(_ctx) {
|
|
84965
|
+
const github_search = tool({
|
|
84966
|
+
description: "Search public GitHub code using local CLIs only (gh, git, rg). " + "No third-party remote services. " + "Without repo: runs `gh search code` for discovery. " + "With repo (owner/name): clones shallow to TMPDIR and searches locally with permalinks. " + "Requires gh CLI + auth for discovery (`gh auth login`).",
|
|
84967
|
+
args: {
|
|
84968
|
+
query: tool.schema.string().describe('Code pattern to search for (e.g. "useQuery(", "staleTime:")'),
|
|
84969
|
+
repo: tool.schema.string().optional().describe('Repository in owner/name format for deep local search (e.g. "tanstack/query")'),
|
|
84970
|
+
language: tool.schema.string().optional().describe('Language filter for discovery mode (e.g. "TypeScript")'),
|
|
84971
|
+
limit: tool.schema.number().optional().describe(`Max results for discovery mode (1-${MAX_LIMIT}, default 10)`)
|
|
84972
|
+
},
|
|
84973
|
+
execute: async (args) => {
|
|
84974
|
+
try {
|
|
84975
|
+
const result = await githubSearch({
|
|
84976
|
+
query: args.query,
|
|
84977
|
+
repo: args.repo,
|
|
84978
|
+
language: args.language,
|
|
84979
|
+
limit: args.limit
|
|
84980
|
+
});
|
|
84981
|
+
return formatGithubSearchResult(result);
|
|
84982
|
+
} catch (e) {
|
|
84983
|
+
return `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
84984
|
+
}
|
|
84985
|
+
}
|
|
84986
|
+
});
|
|
84987
|
+
return { github_search };
|
|
84988
|
+
}
|
|
84989
|
+
// src/tools/glob/cli.ts
|
|
84990
|
+
import { stat } from "fs/promises";
|
|
84991
|
+
var {spawn: spawn12 } = globalThis.Bun;
|
|
84992
|
+
|
|
84993
|
+
// src/tools/glob/constants.ts
|
|
84994
|
+
var DEFAULT_TIMEOUT_MS5 = 60000;
|
|
84557
84995
|
var DEFAULT_LIMIT = 100;
|
|
84558
84996
|
var DEFAULT_MAX_DEPTH2 = 20;
|
|
84559
84997
|
var DEFAULT_MAX_OUTPUT_BYTES3 = 10 * 1024 * 1024;
|
|
@@ -84564,7 +85002,7 @@ var RG_FILES_FLAGS = [
|
|
|
84564
85002
|
];
|
|
84565
85003
|
|
|
84566
85004
|
// src/tools/glob/cli.ts
|
|
84567
|
-
function
|
|
85005
|
+
function buildRgArgs2(options) {
|
|
84568
85006
|
const args = [
|
|
84569
85007
|
...RG_FILES_FLAGS,
|
|
84570
85008
|
`--max-depth=${Math.min(options.maxDepth ?? DEFAULT_MAX_DEPTH2, DEFAULT_MAX_DEPTH2)}`
|
|
@@ -84616,14 +85054,14 @@ async function getFileMtime(filePath) {
|
|
|
84616
85054
|
}
|
|
84617
85055
|
async function runRgFiles(options, resolvedCli) {
|
|
84618
85056
|
const cli = resolvedCli ?? resolveGrepCli();
|
|
84619
|
-
const timeout = Math.min(options.timeout ??
|
|
85057
|
+
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS5, DEFAULT_TIMEOUT_MS5);
|
|
84620
85058
|
const limit = Math.min(options.limit ?? DEFAULT_LIMIT, DEFAULT_LIMIT);
|
|
84621
85059
|
const isRg = cli.backend === "rg";
|
|
84622
85060
|
const isWindows = process.platform === "win32";
|
|
84623
85061
|
let command;
|
|
84624
85062
|
let cwd;
|
|
84625
85063
|
if (isRg) {
|
|
84626
|
-
const args =
|
|
85064
|
+
const args = buildRgArgs2(options);
|
|
84627
85065
|
const paths = options.paths?.length ? options.paths : ["."];
|
|
84628
85066
|
args.push(...paths);
|
|
84629
85067
|
command = [cli.path, ...args];
|
|
@@ -84637,7 +85075,7 @@ async function runRgFiles(options, resolvedCli) {
|
|
|
84637
85075
|
cwd = paths[0] || ".";
|
|
84638
85076
|
command = [cli.path, ...args];
|
|
84639
85077
|
}
|
|
84640
|
-
const proc =
|
|
85078
|
+
const proc = spawn12(command, {
|
|
84641
85079
|
stdout: "pipe",
|
|
84642
85080
|
stderr: "pipe",
|
|
84643
85081
|
cwd
|
|
@@ -84746,151 +85184,6 @@ function createGlobTools(ctx) {
|
|
|
84746
85184
|
});
|
|
84747
85185
|
return { glob };
|
|
84748
85186
|
}
|
|
84749
|
-
// src/tools/grep/cli.ts
|
|
84750
|
-
var {spawn: spawn11 } = globalThis.Bun;
|
|
84751
|
-
function buildRgArgs2(options) {
|
|
84752
|
-
const args = [
|
|
84753
|
-
...RG_SAFETY_FLAGS,
|
|
84754
|
-
`--max-depth=${Math.min(options.maxDepth ?? DEFAULT_MAX_DEPTH, DEFAULT_MAX_DEPTH)}`,
|
|
84755
|
-
`--max-filesize=${options.maxFilesize ?? DEFAULT_MAX_FILESIZE}`,
|
|
84756
|
-
`--max-count=${Math.min(options.maxCount ?? DEFAULT_MAX_COUNT, DEFAULT_MAX_COUNT)}`,
|
|
84757
|
-
`--max-columns=${Math.min(options.maxColumns ?? DEFAULT_MAX_COLUMNS, DEFAULT_MAX_COLUMNS)}`
|
|
84758
|
-
];
|
|
84759
|
-
if (options.context !== undefined && options.context > 0) {
|
|
84760
|
-
args.push(`-C${Math.min(options.context, 10)}`);
|
|
84761
|
-
}
|
|
84762
|
-
if (options.caseSensitive)
|
|
84763
|
-
args.push("--case-sensitive");
|
|
84764
|
-
if (options.wholeWord)
|
|
84765
|
-
args.push("-w");
|
|
84766
|
-
if (options.fixedStrings)
|
|
84767
|
-
args.push("-F");
|
|
84768
|
-
if (options.multiline)
|
|
84769
|
-
args.push("-U");
|
|
84770
|
-
if (options.hidden)
|
|
84771
|
-
args.push("--hidden");
|
|
84772
|
-
if (options.noIgnore)
|
|
84773
|
-
args.push("--no-ignore");
|
|
84774
|
-
if (options.fileType?.length) {
|
|
84775
|
-
for (const type of options.fileType) {
|
|
84776
|
-
args.push(`--type=${type}`);
|
|
84777
|
-
}
|
|
84778
|
-
}
|
|
84779
|
-
if (options.globs) {
|
|
84780
|
-
for (const glob of options.globs) {
|
|
84781
|
-
args.push(`--glob=${glob}`);
|
|
84782
|
-
}
|
|
84783
|
-
}
|
|
84784
|
-
if (options.excludeGlobs) {
|
|
84785
|
-
for (const glob of options.excludeGlobs) {
|
|
84786
|
-
args.push(`--glob=!${glob}`);
|
|
84787
|
-
}
|
|
84788
|
-
}
|
|
84789
|
-
return args;
|
|
84790
|
-
}
|
|
84791
|
-
function buildGrepArgs(options) {
|
|
84792
|
-
const args = [...GREP_SAFETY_FLAGS, "-r"];
|
|
84793
|
-
if (options.context !== undefined && options.context > 0) {
|
|
84794
|
-
args.push(`-C${Math.min(options.context, 10)}`);
|
|
84795
|
-
}
|
|
84796
|
-
if (!options.caseSensitive)
|
|
84797
|
-
args.push("-i");
|
|
84798
|
-
if (options.wholeWord)
|
|
84799
|
-
args.push("-w");
|
|
84800
|
-
if (options.fixedStrings)
|
|
84801
|
-
args.push("-F");
|
|
84802
|
-
if (options.globs?.length) {
|
|
84803
|
-
for (const glob of options.globs) {
|
|
84804
|
-
args.push(`--include=${glob}`);
|
|
84805
|
-
}
|
|
84806
|
-
}
|
|
84807
|
-
if (options.excludeGlobs?.length) {
|
|
84808
|
-
for (const glob of options.excludeGlobs) {
|
|
84809
|
-
args.push(`--exclude=${glob}`);
|
|
84810
|
-
}
|
|
84811
|
-
}
|
|
84812
|
-
args.push("--exclude-dir=.git", "--exclude-dir=node_modules");
|
|
84813
|
-
return args;
|
|
84814
|
-
}
|
|
84815
|
-
function buildArgs(options, backend) {
|
|
84816
|
-
return backend === "rg" ? buildRgArgs2(options) : buildGrepArgs(options);
|
|
84817
|
-
}
|
|
84818
|
-
function parseOutput(output) {
|
|
84819
|
-
if (!output.trim())
|
|
84820
|
-
return [];
|
|
84821
|
-
const matches = [];
|
|
84822
|
-
const lines = output.split(`
|
|
84823
|
-
`);
|
|
84824
|
-
for (const line of lines) {
|
|
84825
|
-
if (!line.trim())
|
|
84826
|
-
continue;
|
|
84827
|
-
const match = line.match(/^(.+?):(\d+):(.*)$/);
|
|
84828
|
-
if (match) {
|
|
84829
|
-
matches.push({
|
|
84830
|
-
file: match[1],
|
|
84831
|
-
line: parseInt(match[2], 10),
|
|
84832
|
-
text: match[3]
|
|
84833
|
-
});
|
|
84834
|
-
}
|
|
84835
|
-
}
|
|
84836
|
-
return matches;
|
|
84837
|
-
}
|
|
84838
|
-
async function runRg(options) {
|
|
84839
|
-
const cli = resolveGrepCli();
|
|
84840
|
-
const args = buildArgs(options, cli.backend);
|
|
84841
|
-
const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3);
|
|
84842
|
-
if (cli.backend === "rg") {
|
|
84843
|
-
args.push("--", options.pattern);
|
|
84844
|
-
} else {
|
|
84845
|
-
args.push("-e", options.pattern);
|
|
84846
|
-
}
|
|
84847
|
-
const paths = options.paths?.length ? options.paths : ["."];
|
|
84848
|
-
args.push(...paths);
|
|
84849
|
-
const proc = spawn11([cli.path, ...args], {
|
|
84850
|
-
stdout: "pipe",
|
|
84851
|
-
stderr: "pipe"
|
|
84852
|
-
});
|
|
84853
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
84854
|
-
const id = setTimeout(() => {
|
|
84855
|
-
proc.kill();
|
|
84856
|
-
reject(new Error(`Search timeout after ${timeout}ms`));
|
|
84857
|
-
}, timeout);
|
|
84858
|
-
proc.exited.then(() => clearTimeout(id));
|
|
84859
|
-
});
|
|
84860
|
-
try {
|
|
84861
|
-
const stdout = await Promise.race([new Response(proc.stdout).text(), timeoutPromise]);
|
|
84862
|
-
const stderr = await new Response(proc.stderr).text();
|
|
84863
|
-
const exitCode = await proc.exited;
|
|
84864
|
-
const truncated = stdout.length >= DEFAULT_MAX_OUTPUT_BYTES2;
|
|
84865
|
-
const outputToProcess = truncated ? stdout.substring(0, DEFAULT_MAX_OUTPUT_BYTES2) : stdout;
|
|
84866
|
-
if (exitCode > 1 && stderr.trim()) {
|
|
84867
|
-
return {
|
|
84868
|
-
matches: [],
|
|
84869
|
-
totalMatches: 0,
|
|
84870
|
-
filesSearched: 0,
|
|
84871
|
-
truncated: false,
|
|
84872
|
-
error: stderr.trim()
|
|
84873
|
-
};
|
|
84874
|
-
}
|
|
84875
|
-
const matches = parseOutput(outputToProcess);
|
|
84876
|
-
const filesSearched = new Set(matches.map((m) => m.file)).size;
|
|
84877
|
-
return {
|
|
84878
|
-
matches,
|
|
84879
|
-
totalMatches: matches.length,
|
|
84880
|
-
filesSearched,
|
|
84881
|
-
truncated
|
|
84882
|
-
};
|
|
84883
|
-
} catch (e) {
|
|
84884
|
-
return {
|
|
84885
|
-
matches: [],
|
|
84886
|
-
totalMatches: 0,
|
|
84887
|
-
filesSearched: 0,
|
|
84888
|
-
truncated: false,
|
|
84889
|
-
error: e instanceof Error ? e.message : String(e)
|
|
84890
|
-
};
|
|
84891
|
-
}
|
|
84892
|
-
}
|
|
84893
|
-
|
|
84894
85187
|
// src/tools/grep/result-formatter.ts
|
|
84895
85188
|
function formatGrepResult(result) {
|
|
84896
85189
|
if (result.error) {
|
|
@@ -84992,17 +85285,17 @@ var HandoffSchema = exports_external.object({
|
|
|
84992
85285
|
sections: HandoffSectionSchema
|
|
84993
85286
|
});
|
|
84994
85287
|
// src/features/handoff/storage.ts
|
|
84995
|
-
import { existsSync as
|
|
84996
|
-
import { join as
|
|
85288
|
+
import { existsSync as existsSync60, mkdirSync as mkdirSync18, readFileSync as readFileSync40, renameSync as renameSync4, writeFileSync as writeFileSync22 } from "fs";
|
|
85289
|
+
import { join as join63 } from "path";
|
|
84997
85290
|
function getHandoffFilePath(directory) {
|
|
84998
|
-
return
|
|
85291
|
+
return join63(directory, HANDOFF_FILE_PATH);
|
|
84999
85292
|
}
|
|
85000
85293
|
function getHandoffConsumedFilePath(directory) {
|
|
85001
|
-
return
|
|
85294
|
+
return join63(directory, HANDOFF_CONSUMED_FILE_PATH);
|
|
85002
85295
|
}
|
|
85003
85296
|
function handoffFileExists(directory) {
|
|
85004
85297
|
try {
|
|
85005
|
-
return
|
|
85298
|
+
return existsSync60(getHandoffFilePath(directory));
|
|
85006
85299
|
} catch {
|
|
85007
85300
|
return false;
|
|
85008
85301
|
}
|
|
@@ -85010,7 +85303,7 @@ function handoffFileExists(directory) {
|
|
|
85010
85303
|
function readHandoffFile(directory) {
|
|
85011
85304
|
const filePath = getHandoffFilePath(directory);
|
|
85012
85305
|
try {
|
|
85013
|
-
if (!
|
|
85306
|
+
if (!existsSync60(filePath))
|
|
85014
85307
|
return null;
|
|
85015
85308
|
return readFileSync40(filePath, "utf-8");
|
|
85016
85309
|
} catch {
|
|
@@ -85036,7 +85329,7 @@ function writeHandoffFile(directory, content) {
|
|
|
85036
85329
|
}
|
|
85037
85330
|
function ensureHandoffDir(directory) {
|
|
85038
85331
|
try {
|
|
85039
|
-
|
|
85332
|
+
mkdirSync18(join63(directory, ".matrixx"), { recursive: true });
|
|
85040
85333
|
return true;
|
|
85041
85334
|
} catch {
|
|
85042
85335
|
return false;
|
|
@@ -85191,15 +85484,15 @@ ${body}
|
|
|
85191
85484
|
}
|
|
85192
85485
|
|
|
85193
85486
|
// src/tools/handoff/list-handler.ts
|
|
85194
|
-
import { existsSync as
|
|
85195
|
-
import { join as
|
|
85487
|
+
import { existsSync as existsSync61, readdirSync as readdirSync19, statSync as statSync12 } from "fs";
|
|
85488
|
+
import { join as join64 } from "path";
|
|
85196
85489
|
var MATRIX_DIR = ".matrixx";
|
|
85197
85490
|
function isHandoffFile(name) {
|
|
85198
85491
|
return name === "handoff.md" || name.startsWith("handoff.consumed");
|
|
85199
85492
|
}
|
|
85200
85493
|
function handleList(directory) {
|
|
85201
|
-
const matrixxDir =
|
|
85202
|
-
if (!
|
|
85494
|
+
const matrixxDir = join64(directory, MATRIX_DIR);
|
|
85495
|
+
if (!existsSync61(matrixxDir)) {
|
|
85203
85496
|
return "No handoffs found in .matrixx/";
|
|
85204
85497
|
}
|
|
85205
85498
|
let entries;
|
|
@@ -85216,7 +85509,7 @@ function handleList(directory) {
|
|
|
85216
85509
|
lines.push(`Handoff files in ${matrixxDir}:`);
|
|
85217
85510
|
lines.push("");
|
|
85218
85511
|
for (const name of handoffs) {
|
|
85219
|
-
const fullPath =
|
|
85512
|
+
const fullPath = join64(matrixxDir, name);
|
|
85220
85513
|
let size = "?";
|
|
85221
85514
|
let mtime = "?";
|
|
85222
85515
|
try {
|
|
@@ -85313,7 +85606,7 @@ function createHandoffTools(ctx) {
|
|
|
85313
85606
|
init_logger();
|
|
85314
85607
|
|
|
85315
85608
|
// src/tools/interactive-bash/constants.ts
|
|
85316
|
-
var
|
|
85609
|
+
var DEFAULT_TIMEOUT_MS6 = 60000;
|
|
85317
85610
|
var BLOCKED_TMUX_SUBCOMMANDS = [
|
|
85318
85611
|
"capture-pane",
|
|
85319
85612
|
"capturep",
|
|
@@ -85410,7 +85703,7 @@ The Bash tool can execute these commands directly. Do NOT retry with interactive
|
|
|
85410
85703
|
});
|
|
85411
85704
|
const timeoutPromise = new Promise((_, reject) => {
|
|
85412
85705
|
const id = setTimeout(() => {
|
|
85413
|
-
const timeoutError = new Error(`Timeout after ${
|
|
85706
|
+
const timeoutError = new Error(`Timeout after ${DEFAULT_TIMEOUT_MS6}ms`);
|
|
85414
85707
|
try {
|
|
85415
85708
|
proc.kill();
|
|
85416
85709
|
proc.exited.catch((err) => {
|
|
@@ -85418,7 +85711,7 @@ The Bash tool can execute these commands directly. Do NOT retry with interactive
|
|
|
85418
85711
|
});
|
|
85419
85712
|
} catch {}
|
|
85420
85713
|
reject(timeoutError);
|
|
85421
|
-
},
|
|
85714
|
+
}, DEFAULT_TIMEOUT_MS6);
|
|
85422
85715
|
proc.exited.then(() => clearTimeout(id)).catch(() => clearTimeout(id));
|
|
85423
85716
|
});
|
|
85424
85717
|
const [stdout, stderr, exitCode] = await Promise.race([
|
|
@@ -85440,9 +85733,9 @@ The Bash tool can execute these commands directly. Do NOT retry with interactive
|
|
|
85440
85733
|
}
|
|
85441
85734
|
});
|
|
85442
85735
|
// src/tools/pdf-extract-figures/tools.ts
|
|
85443
|
-
import { existsSync as
|
|
85444
|
-
import { tmpdir as
|
|
85445
|
-
import { join as
|
|
85736
|
+
import { existsSync as existsSync62, mkdtempSync, writeFileSync as writeFileSync23 } from "fs";
|
|
85737
|
+
import { tmpdir as tmpdir6 } from "os";
|
|
85738
|
+
import { join as join65 } from "path";
|
|
85446
85739
|
var PYTHON_SCRIPT = `#!/usr/bin/env python3
|
|
85447
85740
|
import sys
|
|
85448
85741
|
import json
|
|
@@ -85544,7 +85837,7 @@ function validateArgs(args) {
|
|
|
85544
85837
|
if (!args.file_path) {
|
|
85545
85838
|
return "Error: 'file_path' is required.";
|
|
85546
85839
|
}
|
|
85547
|
-
if (!
|
|
85840
|
+
if (!existsSync62(args.file_path)) {
|
|
85548
85841
|
return `Error: File not found: ${args.file_path}`;
|
|
85549
85842
|
}
|
|
85550
85843
|
if (!args.file_path.toLowerCase().endsWith(".pdf")) {
|
|
@@ -85593,11 +85886,11 @@ async function runExtraction(args) {
|
|
|
85593
85886
|
}
|
|
85594
85887
|
let tmpDir;
|
|
85595
85888
|
try {
|
|
85596
|
-
tmpDir = mkdtempSync(
|
|
85889
|
+
tmpDir = mkdtempSync(join65(tmpdir6(), "matrixx-pdf-extract-"));
|
|
85597
85890
|
} catch {
|
|
85598
85891
|
return "Error: Could not create temporary directory.";
|
|
85599
85892
|
}
|
|
85600
|
-
const scriptPath =
|
|
85893
|
+
const scriptPath = join65(tmpDir, "extract_pdf_figures.py");
|
|
85601
85894
|
writeFileSync23(scriptPath, PYTHON_SCRIPT);
|
|
85602
85895
|
const payload = buildArgsPayload(args);
|
|
85603
85896
|
log("[pdf_extract_figures] Running extraction", { file_path: args.file_path, payload });
|
|
@@ -85645,11 +85938,11 @@ function createPdfExtractFiguresTool() {
|
|
|
85645
85938
|
return { pdf_extract_figures };
|
|
85646
85939
|
}
|
|
85647
85940
|
// src/tools/plan/plan-create.ts
|
|
85648
|
-
import { existsSync as
|
|
85941
|
+
import { existsSync as existsSync63 } from "fs";
|
|
85649
85942
|
import { basename as basename7 } from "path";
|
|
85650
85943
|
|
|
85651
85944
|
// src/tools/plan/types.ts
|
|
85652
|
-
import { basename as basename6, isAbsolute as isAbsolute5, join as
|
|
85945
|
+
import { basename as basename6, isAbsolute as isAbsolute5, join as join66, relative as relative4, resolve as resolve8 } from "path";
|
|
85653
85946
|
|
|
85654
85947
|
// src/tools/plan/constants.ts
|
|
85655
85948
|
var PLANS_DIR3 = ".matrixx/plans";
|
|
@@ -85675,8 +85968,8 @@ function validatePlanFilePath(filePath, directory) {
|
|
|
85675
85968
|
if (!PLAN_FILENAME_KEBAB_REGEX.test(base)) {
|
|
85676
85969
|
return { error: `fileName must match kebab-case ^[a-z0-9-]+\\.md$: ${base}` };
|
|
85677
85970
|
}
|
|
85678
|
-
const resolved = isAbsolute5(filePath) ? resolve8(filePath) : resolve8(
|
|
85679
|
-
const plansDir = resolve8(
|
|
85971
|
+
const resolved = isAbsolute5(filePath) ? resolve8(filePath) : resolve8(join66(directory, filePath));
|
|
85972
|
+
const plansDir = resolve8(join66(directory, PLANS_DIR3));
|
|
85680
85973
|
const relToPlans = relative4(plansDir, resolved);
|
|
85681
85974
|
if (relToPlans.startsWith("..") || isAbsolute5(relToPlans)) {
|
|
85682
85975
|
return { error: `filePath must be inside ${PLANS_DIR3}: ${filePath}` };
|
|
@@ -85719,7 +86012,7 @@ Uses atomicWrite (.tmp.pid+rename) + upsertMetadataComment. Warns if file alread
|
|
|
85719
86012
|
}
|
|
85720
86013
|
const resolved = validation.resolved;
|
|
85721
86014
|
ensurePlanDir(directory);
|
|
85722
|
-
if (
|
|
86015
|
+
if (existsSync63(resolved)) {
|
|
85723
86016
|
return JSON.stringify({
|
|
85724
86017
|
error: "file_exists",
|
|
85725
86018
|
message: `Plan file already exists: ${resolved}. Use plan_read + plan_update to modify.`,
|
|
@@ -85750,7 +86043,7 @@ Uses atomicWrite (.tmp.pid+rename) + upsertMetadataComment. Warns if file alread
|
|
|
85750
86043
|
});
|
|
85751
86044
|
}
|
|
85752
86045
|
// src/tools/plan/plan-delete.ts
|
|
85753
|
-
import { existsSync as
|
|
86046
|
+
import { existsSync as existsSync64, unlinkSync as unlinkSync10 } from "fs";
|
|
85754
86047
|
function createPlanDeleteTool(ctx) {
|
|
85755
86048
|
return tool({
|
|
85756
86049
|
description: `Delete a plan file under .matrixx/plans/*.md. Scoped unlink inside PLANS_DIR, refuses outside paths.`,
|
|
@@ -85766,7 +86059,7 @@ function createPlanDeleteTool(ctx) {
|
|
|
85766
86059
|
return JSON.stringify({ error: "invalid_file_path", message: validation.error });
|
|
85767
86060
|
}
|
|
85768
86061
|
const resolved = validation.resolved;
|
|
85769
|
-
if (!
|
|
86062
|
+
if (!existsSync64(resolved)) {
|
|
85770
86063
|
return JSON.stringify({ error: "file_not_found", message: `File not found: ${resolved}` });
|
|
85771
86064
|
}
|
|
85772
86065
|
unlinkSync10(resolved);
|
|
@@ -85779,8 +86072,8 @@ function createPlanDeleteTool(ctx) {
|
|
|
85779
86072
|
});
|
|
85780
86073
|
}
|
|
85781
86074
|
// src/tools/plan/plan-list.ts
|
|
85782
|
-
import { existsSync as
|
|
85783
|
-
import { join as
|
|
86075
|
+
import { existsSync as existsSync65, readdirSync as readdirSync20, statSync as statSync13 } from "fs";
|
|
86076
|
+
import { join as join67 } from "path";
|
|
85784
86077
|
function createPlanListTool(ctx) {
|
|
85785
86078
|
return tool({
|
|
85786
86079
|
description: `List plan files under .matrixx/plans/*.md. Filters *.md with kebab-case, sorts by mtime.`,
|
|
@@ -85788,13 +86081,13 @@ function createPlanListTool(ctx) {
|
|
|
85788
86081
|
execute: async (_args, context) => {
|
|
85789
86082
|
try {
|
|
85790
86083
|
const directory = resolveDirectory(context?.directory, ctx?.directory);
|
|
85791
|
-
const plansDir =
|
|
85792
|
-
if (!
|
|
86084
|
+
const plansDir = join67(directory, PLANS_DIR3);
|
|
86085
|
+
if (!existsSync65(plansDir)) {
|
|
85793
86086
|
return JSON.stringify({ plans: [] });
|
|
85794
86087
|
}
|
|
85795
86088
|
const entries = readdirSync20(plansDir).filter((f) => f.endsWith(".md") && PLAN_FILENAME_KEBAB_REGEX.test(f));
|
|
85796
86089
|
const plans = entries.map((fileName) => {
|
|
85797
|
-
const filePath =
|
|
86090
|
+
const filePath = join67(plansDir, fileName);
|
|
85798
86091
|
try {
|
|
85799
86092
|
const stat2 = statSync13(filePath);
|
|
85800
86093
|
return {
|
|
@@ -85817,7 +86110,7 @@ function createPlanListTool(ctx) {
|
|
|
85817
86110
|
});
|
|
85818
86111
|
}
|
|
85819
86112
|
// src/tools/plan/plan-read.ts
|
|
85820
|
-
import { existsSync as
|
|
86113
|
+
import { existsSync as existsSync66, statSync as statSync14 } from "fs";
|
|
85821
86114
|
function createPlanReadTool(ctx) {
|
|
85822
86115
|
return tool({
|
|
85823
86116
|
description: `Read a plan file from .matrixx/plans/*.md with hashline-tagged output (1#AB|content) + lsp-style. Enforces scoping and ${MAX_PLAN_FILE_BYTES2} byte cap.`,
|
|
@@ -85833,7 +86126,7 @@ function createPlanReadTool(ctx) {
|
|
|
85833
86126
|
return JSON.stringify({ error: "invalid_file_path", message: validation.error });
|
|
85834
86127
|
}
|
|
85835
86128
|
const resolved = validation.resolved;
|
|
85836
|
-
if (!
|
|
86129
|
+
if (!existsSync66(resolved)) {
|
|
85837
86130
|
return JSON.stringify({ error: "file_not_found", message: `File not found: ${resolved}` });
|
|
85838
86131
|
}
|
|
85839
86132
|
try {
|
|
@@ -85865,7 +86158,7 @@ function createPlanReadTool(ctx) {
|
|
|
85865
86158
|
});
|
|
85866
86159
|
}
|
|
85867
86160
|
// src/tools/plan/plan-update.ts
|
|
85868
|
-
import { existsSync as
|
|
86161
|
+
import { existsSync as existsSync67 } from "fs";
|
|
85869
86162
|
|
|
85870
86163
|
// src/features/tool-metadata-store/store.ts
|
|
85871
86164
|
var pendingStore = new Map;
|
|
@@ -86928,7 +87221,7 @@ function createPlanUpdateTool(ctx) {
|
|
|
86928
87221
|
return JSON.stringify({ error: "invalid_file_path", message: validation.error });
|
|
86929
87222
|
}
|
|
86930
87223
|
const resolved = validation.resolved;
|
|
86931
|
-
if (!
|
|
87224
|
+
if (!existsSync67(resolved)) {
|
|
86932
87225
|
return JSON.stringify({ error: "file_not_found", message: `File not found: ${resolved}` });
|
|
86933
87226
|
}
|
|
86934
87227
|
const result = await executeHashlineEditTool({ filePath: resolved, edits }, context, ctx);
|
|
@@ -86941,17 +87234,17 @@ function createPlanUpdateTool(ctx) {
|
|
|
86941
87234
|
});
|
|
86942
87235
|
}
|
|
86943
87236
|
// src/tools/preset/tools.ts
|
|
86944
|
-
import { existsSync as
|
|
87237
|
+
import { existsSync as existsSync68, mkdirSync as mkdirSync19, readFileSync as readFileSync41, writeFileSync as writeFileSync24 } from "fs";
|
|
86945
87238
|
import { homedir as homedir10 } from "os";
|
|
86946
|
-
import { dirname as dirname15, join as
|
|
87239
|
+
import { dirname as dirname15, join as join68 } from "path";
|
|
86947
87240
|
|
|
86948
87241
|
// src/features/preset-state/manager.ts
|
|
86949
87242
|
init_logger();
|
|
86950
|
-
var
|
|
87243
|
+
var HOOK_NAME20 = "preset-state";
|
|
86951
87244
|
var presetBySession = new Map;
|
|
86952
87245
|
function setSessionPreset(sessionID, presetName) {
|
|
86953
87246
|
presetBySession.set(sessionID, presetName);
|
|
86954
|
-
log(`[${
|
|
87247
|
+
log(`[${HOOK_NAME20}] Preset set for session`, { sessionID, presetName });
|
|
86955
87248
|
}
|
|
86956
87249
|
function getSessionPreset(sessionID) {
|
|
86957
87250
|
return presetBySession.get(sessionID);
|
|
@@ -86962,14 +87255,14 @@ var ACTIONS = ["list", "show", "set"];
|
|
|
86962
87255
|
var SCOPES = ["global", "project"];
|
|
86963
87256
|
function resolveConfigPath(scope, directory) {
|
|
86964
87257
|
if (scope === "global") {
|
|
86965
|
-
return
|
|
87258
|
+
return join68(homedir10(), ".config", "opencode", "matrixx.jsonc");
|
|
86966
87259
|
}
|
|
86967
|
-
return
|
|
87260
|
+
return join68(directory ?? process.cwd(), ".opencode", "matrixx.jsonc");
|
|
86968
87261
|
}
|
|
86969
87262
|
function persistActivePreset(presetName, scope, directory) {
|
|
86970
87263
|
const configPath = resolveConfigPath(scope, directory);
|
|
86971
87264
|
let raw = {};
|
|
86972
|
-
if (
|
|
87265
|
+
if (existsSync68(configPath)) {
|
|
86973
87266
|
const content = readFileSync41(configPath, "utf-8");
|
|
86974
87267
|
const parsed = parseJsoncSafe(content);
|
|
86975
87268
|
if (parsed.errors.length > 0) {
|
|
@@ -86980,7 +87273,7 @@ function persistActivePreset(presetName, scope, directory) {
|
|
|
86980
87273
|
}
|
|
86981
87274
|
raw = parsed.data ?? {};
|
|
86982
87275
|
} else {
|
|
86983
|
-
|
|
87276
|
+
mkdirSync19(dirname15(configPath), { recursive: true });
|
|
86984
87277
|
}
|
|
86985
87278
|
raw.active_preset = presetName;
|
|
86986
87279
|
writeFileSync24(configPath, `${JSON.stringify(raw, null, 2)}
|
|
@@ -87083,9 +87376,9 @@ function createPresetTool(options) {
|
|
|
87083
87376
|
return { preset };
|
|
87084
87377
|
}
|
|
87085
87378
|
// src/tools/session-manager/constants.ts
|
|
87086
|
-
import { join as
|
|
87087
|
-
var TODO_DIR =
|
|
87088
|
-
var TRANSCRIPT_DIR2 =
|
|
87379
|
+
import { join as join69 } from "path";
|
|
87380
|
+
var TODO_DIR = join69(getOpenCodeConfigDir({ binary: "opencode" }), "todos");
|
|
87381
|
+
var TRANSCRIPT_DIR2 = join69(getOpenCodeConfigDir({ binary: "opencode" }), "transcripts");
|
|
87089
87382
|
var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
|
|
87090
87383
|
|
|
87091
87384
|
Returns a list of available session IDs with metadata including message count, date range, and agents used.
|
|
@@ -87157,9 +87450,9 @@ Agents Used: build, oracle, librarian
|
|
|
87157
87450
|
Has Todos: Yes (12 items, 8 completed)
|
|
87158
87451
|
Has Transcript: Yes (234 entries)`;
|
|
87159
87452
|
// src/tools/session-manager/storage.ts
|
|
87160
|
-
import { existsSync as
|
|
87453
|
+
import { existsSync as existsSync69 } from "fs";
|
|
87161
87454
|
import { readdir, readFile as readFile3 } from "fs/promises";
|
|
87162
|
-
import { join as
|
|
87455
|
+
import { join as join70 } from "path";
|
|
87163
87456
|
var sdkClient = null;
|
|
87164
87457
|
function setStorageClient(client2) {
|
|
87165
87458
|
sdkClient = client2;
|
|
@@ -87178,7 +87471,7 @@ async function getMainSessions(options) {
|
|
|
87178
87471
|
return [];
|
|
87179
87472
|
}
|
|
87180
87473
|
}
|
|
87181
|
-
if (!
|
|
87474
|
+
if (!existsSync69(SESSION_STORAGE))
|
|
87182
87475
|
return [];
|
|
87183
87476
|
const sessions = [];
|
|
87184
87477
|
try {
|
|
@@ -87186,13 +87479,13 @@ async function getMainSessions(options) {
|
|
|
87186
87479
|
for (const projectDir of projectDirs) {
|
|
87187
87480
|
if (!projectDir.isDirectory())
|
|
87188
87481
|
continue;
|
|
87189
|
-
const projectPath =
|
|
87482
|
+
const projectPath = join70(SESSION_STORAGE, projectDir.name);
|
|
87190
87483
|
const sessionFiles = await readdir(projectPath);
|
|
87191
87484
|
for (const file3 of sessionFiles) {
|
|
87192
87485
|
if (!file3.endsWith(".json"))
|
|
87193
87486
|
continue;
|
|
87194
87487
|
try {
|
|
87195
|
-
const content = await readFile3(
|
|
87488
|
+
const content = await readFile3(join70(projectPath, file3), "utf-8");
|
|
87196
87489
|
const meta3 = JSON.parse(content);
|
|
87197
87490
|
if (meta3.parentID)
|
|
87198
87491
|
continue;
|
|
@@ -87217,7 +87510,7 @@ async function getAllSessions() {
|
|
|
87217
87510
|
return [];
|
|
87218
87511
|
}
|
|
87219
87512
|
}
|
|
87220
|
-
if (!
|
|
87513
|
+
if (!existsSync69(MESSAGE_STORAGE))
|
|
87221
87514
|
return [];
|
|
87222
87515
|
const sessions = [];
|
|
87223
87516
|
async function scanDirectory(dir) {
|
|
@@ -87225,7 +87518,7 @@ async function getAllSessions() {
|
|
|
87225
87518
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
87226
87519
|
for (const entry of entries) {
|
|
87227
87520
|
if (entry.isDirectory()) {
|
|
87228
|
-
const sessionPath =
|
|
87521
|
+
const sessionPath = join70(dir, entry.name);
|
|
87229
87522
|
const files = await readdir(sessionPath);
|
|
87230
87523
|
if (files.some((f) => f.endsWith(".json"))) {
|
|
87231
87524
|
sessions.push(entry.name);
|
|
@@ -87286,7 +87579,7 @@ async function readSessionMessages(sessionID) {
|
|
|
87286
87579
|
}
|
|
87287
87580
|
}
|
|
87288
87581
|
const messageDir = getMessageDir(sessionID);
|
|
87289
|
-
if (!messageDir || !
|
|
87582
|
+
if (!messageDir || !existsSync69(messageDir))
|
|
87290
87583
|
return [];
|
|
87291
87584
|
const messages2 = [];
|
|
87292
87585
|
try {
|
|
@@ -87295,7 +87588,7 @@ async function readSessionMessages(sessionID) {
|
|
|
87295
87588
|
if (!file3.endsWith(".json"))
|
|
87296
87589
|
continue;
|
|
87297
87590
|
try {
|
|
87298
|
-
const content = await readFile3(
|
|
87591
|
+
const content = await readFile3(join70(messageDir, file3), "utf-8");
|
|
87299
87592
|
const meta3 = JSON.parse(content);
|
|
87300
87593
|
const parts = await readParts2(meta3.id);
|
|
87301
87594
|
messages2.push({
|
|
@@ -87319,8 +87612,8 @@ async function readSessionMessages(sessionID) {
|
|
|
87319
87612
|
});
|
|
87320
87613
|
}
|
|
87321
87614
|
async function readParts2(messageID) {
|
|
87322
|
-
const partDir =
|
|
87323
|
-
if (!
|
|
87615
|
+
const partDir = join70(PART_STORAGE, messageID);
|
|
87616
|
+
if (!existsSync69(partDir))
|
|
87324
87617
|
return [];
|
|
87325
87618
|
const parts = [];
|
|
87326
87619
|
try {
|
|
@@ -87329,7 +87622,7 @@ async function readParts2(messageID) {
|
|
|
87329
87622
|
if (!file3.endsWith(".json"))
|
|
87330
87623
|
continue;
|
|
87331
87624
|
try {
|
|
87332
|
-
const content = await readFile3(
|
|
87625
|
+
const content = await readFile3(join70(partDir, file3), "utf-8");
|
|
87333
87626
|
parts.push(JSON.parse(content));
|
|
87334
87627
|
} catch {}
|
|
87335
87628
|
}
|
|
@@ -87353,14 +87646,14 @@ async function readSessionTodos(sessionID) {
|
|
|
87353
87646
|
return [];
|
|
87354
87647
|
}
|
|
87355
87648
|
}
|
|
87356
|
-
if (!
|
|
87649
|
+
if (!existsSync69(TODO_DIR))
|
|
87357
87650
|
return [];
|
|
87358
87651
|
try {
|
|
87359
87652
|
const allFiles = await readdir(TODO_DIR);
|
|
87360
87653
|
const todoFiles = allFiles.filter((f) => f.includes(sessionID) && f.endsWith(".json"));
|
|
87361
87654
|
for (const file3 of todoFiles) {
|
|
87362
87655
|
try {
|
|
87363
|
-
const content = await readFile3(
|
|
87656
|
+
const content = await readFile3(join70(TODO_DIR, file3), "utf-8");
|
|
87364
87657
|
const data = JSON.parse(content);
|
|
87365
87658
|
if (Array.isArray(data)) {
|
|
87366
87659
|
return data.map((item) => ({
|
|
@@ -87378,10 +87671,10 @@ async function readSessionTodos(sessionID) {
|
|
|
87378
87671
|
return [];
|
|
87379
87672
|
}
|
|
87380
87673
|
async function readSessionTranscript(sessionID) {
|
|
87381
|
-
if (!
|
|
87674
|
+
if (!existsSync69(TRANSCRIPT_DIR2))
|
|
87382
87675
|
return 0;
|
|
87383
|
-
const transcriptFile =
|
|
87384
|
-
if (!
|
|
87676
|
+
const transcriptFile = join70(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
|
|
87677
|
+
if (!existsSync69(transcriptFile))
|
|
87385
87678
|
return 0;
|
|
87386
87679
|
try {
|
|
87387
87680
|
const content = await readFile3(transcriptFile, "utf-8");
|
|
@@ -87881,17 +88174,17 @@ function createSkillTool(options = {}) {
|
|
|
87881
88174
|
}
|
|
87882
88175
|
var skill = createSkillTool();
|
|
87883
88176
|
// src/tools/slashcommand/command-discovery.ts
|
|
87884
|
-
import { existsSync as
|
|
87885
|
-
import { basename as basename8, join as
|
|
88177
|
+
import { existsSync as existsSync70, readdirSync as readdirSync21, readFileSync as readFileSync42 } from "fs";
|
|
88178
|
+
import { basename as basename8, join as join71 } from "path";
|
|
87886
88179
|
function discoverCommandsFromDir2(commandsDir, scope) {
|
|
87887
|
-
if (!
|
|
88180
|
+
if (!existsSync70(commandsDir))
|
|
87888
88181
|
return [];
|
|
87889
88182
|
const entries = readdirSync21(commandsDir, { withFileTypes: true });
|
|
87890
88183
|
const commands2 = [];
|
|
87891
88184
|
for (const entry of entries) {
|
|
87892
88185
|
if (!isMarkdownFile(entry))
|
|
87893
88186
|
continue;
|
|
87894
|
-
const commandPath =
|
|
88187
|
+
const commandPath = join71(commandsDir, entry.name);
|
|
87895
88188
|
const commandName = basename8(entry.name, ".md");
|
|
87896
88189
|
try {
|
|
87897
88190
|
const content = readFileSync42(commandPath, "utf-8");
|
|
@@ -87918,8 +88211,8 @@ function discoverCommandsFromDir2(commandsDir, scope) {
|
|
|
87918
88211
|
}
|
|
87919
88212
|
function discoverCommandsSync(directory) {
|
|
87920
88213
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
87921
|
-
const opencodeGlobalDir =
|
|
87922
|
-
const opencodeProjectDir =
|
|
88214
|
+
const opencodeGlobalDir = join71(configDir, "command");
|
|
88215
|
+
const opencodeProjectDir = join71(directory ?? process.cwd(), ".opencode", "command");
|
|
87923
88216
|
const opencodeGlobalCommands = discoverCommandsFromDir2(opencodeGlobalDir, "opencode");
|
|
87924
88217
|
const opencodeProjectCommands = discoverCommandsFromDir2(opencodeProjectDir, "opencode-project");
|
|
87925
88218
|
const builtinCommandsMap = loadBuiltinCommands();
|
|
@@ -88723,11 +89016,11 @@ function createBackgroundOutput(manager, client2) {
|
|
|
88723
89016
|
});
|
|
88724
89017
|
}
|
|
88725
89018
|
// src/tools/background-task/create-background-revive.ts
|
|
88726
|
-
import { existsSync as
|
|
89019
|
+
import { existsSync as existsSync72 } from "fs";
|
|
88727
89020
|
|
|
88728
89021
|
// src/features/background-agent/handle-index.ts
|
|
88729
|
-
import { existsSync as
|
|
88730
|
-
import { dirname as dirname17, join as
|
|
89022
|
+
import { existsSync as existsSync71, mkdirSync as mkdirSync20, readdirSync as readdirSync22, readFileSync as readFileSync43, renameSync as renameSync5, unlinkSync as unlinkSync11, writeFileSync as writeFileSync25 } from "fs";
|
|
89023
|
+
import { dirname as dirname17, join as join72 } from "path";
|
|
88731
89024
|
|
|
88732
89025
|
// src/features/background-agent/constants.ts
|
|
88733
89026
|
var TASK_TTL_MS = 30 * 60 * 1000;
|
|
@@ -88778,14 +89071,14 @@ var BgHandleSchema = exports_external.object({
|
|
|
88778
89071
|
completedAt: exports_external.number().optional()
|
|
88779
89072
|
}).strict();
|
|
88780
89073
|
function getBgHandleDir(directory) {
|
|
88781
|
-
return
|
|
89074
|
+
return join72(directory, ".matrixx", BG_HANDLE_DIR_NAME);
|
|
88782
89075
|
}
|
|
88783
89076
|
function getHandlePath(directory, taskId) {
|
|
88784
|
-
return
|
|
89077
|
+
return join72(getBgHandleDir(directory), `${taskId}.json`);
|
|
88785
89078
|
}
|
|
88786
89079
|
function ensureDir3(dirPath) {
|
|
88787
|
-
if (!
|
|
88788
|
-
|
|
89080
|
+
if (!existsSync71(dirPath)) {
|
|
89081
|
+
mkdirSync20(dirPath, { recursive: true });
|
|
88789
89082
|
}
|
|
88790
89083
|
}
|
|
88791
89084
|
function toHandle(task) {
|
|
@@ -88815,7 +89108,7 @@ function writeHandle(directory, task) {
|
|
|
88815
89108
|
renameSync5(tempPath, filePath);
|
|
88816
89109
|
} catch (error95) {
|
|
88817
89110
|
try {
|
|
88818
|
-
if (
|
|
89111
|
+
if (existsSync71(tempPath)) {
|
|
88819
89112
|
unlinkSync11(tempPath);
|
|
88820
89113
|
}
|
|
88821
89114
|
} catch {}
|
|
@@ -88824,7 +89117,7 @@ function writeHandle(directory, task) {
|
|
|
88824
89117
|
}
|
|
88825
89118
|
function readHandleFile(filePath) {
|
|
88826
89119
|
try {
|
|
88827
|
-
if (!
|
|
89120
|
+
if (!existsSync71(filePath)) {
|
|
88828
89121
|
return null;
|
|
88829
89122
|
}
|
|
88830
89123
|
const parsed = JSON.parse(readFileSync43(filePath, "utf-8"));
|
|
@@ -88836,7 +89129,7 @@ function readHandleFile(filePath) {
|
|
|
88836
89129
|
}
|
|
88837
89130
|
function readHandles(directory) {
|
|
88838
89131
|
const dir = getBgHandleDir(directory);
|
|
88839
|
-
if (!
|
|
89132
|
+
if (!existsSync71(dir)) {
|
|
88840
89133
|
return [];
|
|
88841
89134
|
}
|
|
88842
89135
|
const handles = [];
|
|
@@ -88844,7 +89137,7 @@ function readHandles(directory) {
|
|
|
88844
89137
|
if (!name.startsWith(BG_HANDLE_FILE_PREFIX) || !name.endsWith(".json")) {
|
|
88845
89138
|
continue;
|
|
88846
89139
|
}
|
|
88847
|
-
const handle = readHandleFile(
|
|
89140
|
+
const handle = readHandleFile(join72(dir, name));
|
|
88848
89141
|
if (handle) {
|
|
88849
89142
|
handles.push(handle);
|
|
88850
89143
|
}
|
|
@@ -88853,7 +89146,7 @@ function readHandles(directory) {
|
|
|
88853
89146
|
}
|
|
88854
89147
|
function sweepStaleHandles(directory, ttlMs = TASK_TTL_MS, now = Date.now()) {
|
|
88855
89148
|
const dir = getBgHandleDir(directory);
|
|
88856
|
-
if (!
|
|
89149
|
+
if (!existsSync71(dir)) {
|
|
88857
89150
|
return 0;
|
|
88858
89151
|
}
|
|
88859
89152
|
let removed = 0;
|
|
@@ -88861,7 +89154,7 @@ function sweepStaleHandles(directory, ttlMs = TASK_TTL_MS, now = Date.now()) {
|
|
|
88861
89154
|
if (!name.startsWith(BG_HANDLE_FILE_PREFIX) || !name.endsWith(".json")) {
|
|
88862
89155
|
continue;
|
|
88863
89156
|
}
|
|
88864
|
-
const filePath =
|
|
89157
|
+
const filePath = join72(dir, name);
|
|
88865
89158
|
const handle = readHandleFile(filePath);
|
|
88866
89159
|
const timestamp = handle?.completedAt ?? handle?.startedAt ?? handle?.queuedAt;
|
|
88867
89160
|
if (handle && timestamp !== undefined && now - timestamp <= ttlMs) {
|
|
@@ -88929,7 +89222,7 @@ Revive one with background_revive(taskId="<id>", prompt="<new instruction>").`;
|
|
|
88929
89222
|
}
|
|
88930
89223
|
function resolveFailure(identifier, message, directory) {
|
|
88931
89224
|
if (message.includes("not found for revive")) {
|
|
88932
|
-
const handleOnDisk =
|
|
89225
|
+
const handleOnDisk = existsSync72(getHandlePath(directory, identifier));
|
|
88933
89226
|
return { reason: handleOnDisk ? "unknown-task" : "expired", detail: message };
|
|
88934
89227
|
}
|
|
88935
89228
|
if (message.includes("still active"))
|
|
@@ -89344,9 +89637,9 @@ function createAssemblyTool(options) {
|
|
|
89344
89637
|
});
|
|
89345
89638
|
}
|
|
89346
89639
|
// src/tools/dcp-switch-profile/tools.ts
|
|
89347
|
-
import { existsSync as
|
|
89640
|
+
import { existsSync as existsSync73, writeFileSync as writeFileSync26 } from "fs";
|
|
89348
89641
|
import { homedir as homedir11 } from "os";
|
|
89349
|
-
import { join as
|
|
89642
|
+
import { join as join73 } from "path";
|
|
89350
89643
|
|
|
89351
89644
|
// src/config/schema/dcp.ts
|
|
89352
89645
|
var DcpCompressOverrideSchema = exports_external.object({
|
|
@@ -89546,11 +89839,11 @@ var DcpConfigSchema = exports_external.object({
|
|
|
89546
89839
|
});
|
|
89547
89840
|
|
|
89548
89841
|
// src/tools/dcp-switch-profile/tools.ts
|
|
89549
|
-
var DCP_PLUGIN_DIR =
|
|
89550
|
-
var DCP_SYMLINK =
|
|
89842
|
+
var DCP_PLUGIN_DIR = join73(homedir11(), ".config", "opencode", "node_modules", "@tarquinen", "opencode-dcp");
|
|
89843
|
+
var DCP_SYMLINK = join73(homedir11(), ".config", "opencode", "dcp.jsonc");
|
|
89551
89844
|
var VALID_PROFILES = ["economy", "balanced", "performance", "ultimate", "brutal"];
|
|
89552
89845
|
function checkDcpInstalled() {
|
|
89553
|
-
if (!
|
|
89846
|
+
if (!existsSync73(DCP_PLUGIN_DIR)) {
|
|
89554
89847
|
return `DCP is not installed at ${DCP_PLUGIN_DIR}. Install it with: npm install --prefix ~/.config/opencode @tarquinen/opencode-dcp`;
|
|
89555
89848
|
}
|
|
89556
89849
|
return null;
|
|
@@ -92671,8 +92964,8 @@ Original error: ${createResult.error}`;
|
|
|
92671
92964
|
});
|
|
92672
92965
|
}
|
|
92673
92966
|
// src/tools/task/task-cleanup.ts
|
|
92674
|
-
import { existsSync as
|
|
92675
|
-
import { join as
|
|
92967
|
+
import { existsSync as existsSync74, readdirSync as readdirSync23, readFileSync as readFileSync44, unlinkSync as unlinkSync12 } from "fs";
|
|
92968
|
+
import { join as join74 } from "path";
|
|
92676
92969
|
init_logger();
|
|
92677
92970
|
function parseOlderThan(value) {
|
|
92678
92971
|
const match = value.trim().match(/^(\d+)(d|h|m)$/);
|
|
@@ -92732,14 +93025,14 @@ Returns counts: {deleted, remaining, deletedIds}`,
|
|
|
92732
93025
|
}
|
|
92733
93026
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
92734
93027
|
const taskDir = getTaskDir(config4, directory);
|
|
92735
|
-
if (!
|
|
93028
|
+
if (!existsSync74(taskDir)) {
|
|
92736
93029
|
return JSON.stringify({ deleted: 0, remaining: 0, deletedIds: [] });
|
|
92737
93030
|
}
|
|
92738
93031
|
const files = readdirSync23(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-"));
|
|
92739
93032
|
const total = files.length;
|
|
92740
93033
|
const deletedIds = [];
|
|
92741
93034
|
for (const file3 of files) {
|
|
92742
|
-
const filePath =
|
|
93035
|
+
const filePath = join74(taskDir, file3);
|
|
92743
93036
|
let raw;
|
|
92744
93037
|
try {
|
|
92745
93038
|
const content = readFileSync44(filePath, "utf-8");
|
|
@@ -92776,7 +93069,7 @@ Returns counts: {deleted, remaining, deletedIds}`,
|
|
|
92776
93069
|
}
|
|
92777
93070
|
// src/tools/task/task-create.ts
|
|
92778
93071
|
import { readdirSync as readdirSync24, statSync as statSync15 } from "fs";
|
|
92779
|
-
import { join as
|
|
93072
|
+
import { join as join75 } from "path";
|
|
92780
93073
|
init_logger();
|
|
92781
93074
|
function createTaskCreateTool(config4, ctx) {
|
|
92782
93075
|
return tool({
|
|
@@ -92845,7 +93138,7 @@ async function handleCreate2(args, config4, ctx, context) {
|
|
|
92845
93138
|
projectRoot: directory
|
|
92846
93139
|
};
|
|
92847
93140
|
const validatedTask = TaskObjectSchema.parse(task);
|
|
92848
|
-
writeJsonAtomic(
|
|
93141
|
+
writeJsonAtomic(join75(taskDir, `${taskId}.json`), validatedTask);
|
|
92849
93142
|
return JSON.stringify({
|
|
92850
93143
|
task: {
|
|
92851
93144
|
id: validatedTask.id,
|
|
@@ -92874,7 +93167,7 @@ function findDuplicateTask(taskDir, subject, directory) {
|
|
|
92874
93167
|
return null;
|
|
92875
93168
|
}
|
|
92876
93169
|
for (const file3 of files) {
|
|
92877
|
-
const filePath =
|
|
93170
|
+
const filePath = join75(taskDir, file3);
|
|
92878
93171
|
let existing;
|
|
92879
93172
|
try {
|
|
92880
93173
|
existing = readJsonSafe(filePath, TaskObjectSchema);
|
|
@@ -92905,7 +93198,7 @@ function findDuplicateTask(taskDir, subject, directory) {
|
|
|
92905
93198
|
return null;
|
|
92906
93199
|
}
|
|
92907
93200
|
// src/tools/task/task-get.ts
|
|
92908
|
-
import { join as
|
|
93201
|
+
import { join as join76 } from "path";
|
|
92909
93202
|
function parseTaskId(id) {
|
|
92910
93203
|
if (!TASK_ID_PATTERN.test(id))
|
|
92911
93204
|
return null;
|
|
@@ -92931,7 +93224,7 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
92931
93224
|
}
|
|
92932
93225
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
92933
93226
|
const taskDir = getTaskDir(config4, directory);
|
|
92934
|
-
const taskPath =
|
|
93227
|
+
const taskPath = join76(taskDir, `${taskId}.json`);
|
|
92935
93228
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
92936
93229
|
return JSON.stringify({ task: task ?? null });
|
|
92937
93230
|
} catch (error95) {
|
|
@@ -92944,8 +93237,8 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
92944
93237
|
});
|
|
92945
93238
|
}
|
|
92946
93239
|
// src/tools/task/task-list.ts
|
|
92947
|
-
import { existsSync as
|
|
92948
|
-
import { join as
|
|
93240
|
+
import { existsSync as existsSync75, readdirSync as readdirSync25 } from "fs";
|
|
93241
|
+
import { join as join77 } from "path";
|
|
92949
93242
|
function createTaskList(config4, ctx) {
|
|
92950
93243
|
return tool({
|
|
92951
93244
|
description: `[TRACKING \u2014 local progress records only. Spawns nothing, executes nothing.]
|
|
@@ -92964,7 +93257,7 @@ Optional \`parentID\` filter: when provided, returns only tasks whose parentID m
|
|
|
92964
93257
|
const parentID = args.parentID;
|
|
92965
93258
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
92966
93259
|
const taskDir = getTaskDir(config4, directory);
|
|
92967
|
-
if (!
|
|
93260
|
+
if (!existsSync75(taskDir)) {
|
|
92968
93261
|
return JSON.stringify({ tasks: [] });
|
|
92969
93262
|
}
|
|
92970
93263
|
const files = readdirSync25(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-")).map((f) => f.replace(".json", ""));
|
|
@@ -92973,7 +93266,7 @@ Optional \`parentID\` filter: when provided, returns only tasks whose parentID m
|
|
|
92973
93266
|
}
|
|
92974
93267
|
const allTasks = [];
|
|
92975
93268
|
for (const fileId of files) {
|
|
92976
|
-
const task = readJsonSafe(
|
|
93269
|
+
const task = readJsonSafe(join77(taskDir, `${fileId}.json`), TaskObjectSchema);
|
|
92977
93270
|
if (task) {
|
|
92978
93271
|
allTasks.push(task);
|
|
92979
93272
|
}
|
|
@@ -93002,7 +93295,7 @@ Optional \`parentID\` filter: when provided, returns only tasks whose parentID m
|
|
|
93002
93295
|
});
|
|
93003
93296
|
}
|
|
93004
93297
|
// src/tools/task/task-update.ts
|
|
93005
|
-
import { join as
|
|
93298
|
+
import { join as join78 } from "path";
|
|
93006
93299
|
function parseTaskId2(id) {
|
|
93007
93300
|
if (!TASK_ID_PATTERN.test(id))
|
|
93008
93301
|
return null;
|
|
@@ -93049,7 +93342,7 @@ async function handleUpdate(args, config4, ctx, context) {
|
|
|
93049
93342
|
return JSON.stringify({ error: "task_lock_unavailable" });
|
|
93050
93343
|
}
|
|
93051
93344
|
try {
|
|
93052
|
-
const taskPath =
|
|
93345
|
+
const taskPath = join78(taskDir, `${taskId}.json`);
|
|
93053
93346
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
93054
93347
|
if (!task) {
|
|
93055
93348
|
return JSON.stringify({ error: "task_not_found" });
|
|
@@ -93227,6 +93520,7 @@ function createToolGuardHooks(args) {
|
|
|
93227
93520
|
const qualityGate = isHookEnabled("quality-gate") ? safeHook("quality-gate", () => createQualityGateHook()) : null;
|
|
93228
93521
|
const taskNotepad = isHookEnabled("task-notepad") ? safeHook("task-notepad", () => createTaskNotepadHook(ctx)) : null;
|
|
93229
93522
|
const taskEditGuard = isHookEnabled("task-edit-guard") ? safeHook("task-edit-guard", () => createTaskEditGuardHook(ctx)) : null;
|
|
93523
|
+
const documentReaderGuard = isHookEnabled("document-reader-guard") ? safeHook("document-reader-guard", () => createDocumentReaderGuardHook(ctx)) : null;
|
|
93230
93524
|
const evolutionWatcher = evolutionEnabled && isHookEnabled("evolution-watcher") ? safeHook("evolution-watcher", () => createEvolutionWatcherHook(ctx, pluginConfig.evolution)) : null;
|
|
93231
93525
|
return {
|
|
93232
93526
|
backgroundTaskBlocker,
|
|
@@ -93249,6 +93543,7 @@ function createToolGuardHooks(args) {
|
|
|
93249
93543
|
qualityGate,
|
|
93250
93544
|
taskNotepad,
|
|
93251
93545
|
taskEditGuard,
|
|
93546
|
+
documentReaderGuard,
|
|
93252
93547
|
evolutionWatcher
|
|
93253
93548
|
};
|
|
93254
93549
|
}
|
|
@@ -93813,19 +94108,19 @@ function detectRepetitiveToolUse(window2) {
|
|
|
93813
94108
|
}
|
|
93814
94109
|
|
|
93815
94110
|
// src/features/background-agent/message-dir.ts
|
|
93816
|
-
import { existsSync as
|
|
93817
|
-
import { join as
|
|
94111
|
+
import { existsSync as existsSync76, readdirSync as readdirSync26, readFileSync as readFileSync45 } from "fs";
|
|
94112
|
+
import { join as join79 } from "path";
|
|
93818
94113
|
var MESSAGE_DIR_CACHE_CAP = 1000;
|
|
93819
94114
|
var messageDirCache = new Map;
|
|
93820
94115
|
function computeMessageDir(sessionID) {
|
|
93821
|
-
if (!
|
|
94116
|
+
if (!existsSync76(MESSAGE_STORAGE))
|
|
93822
94117
|
return null;
|
|
93823
|
-
const directPath =
|
|
93824
|
-
if (
|
|
94118
|
+
const directPath = join79(MESSAGE_STORAGE, sessionID);
|
|
94119
|
+
if (existsSync76(directPath))
|
|
93825
94120
|
return directPath;
|
|
93826
94121
|
for (const dir of readdirSync26(MESSAGE_STORAGE)) {
|
|
93827
|
-
const sessionPath =
|
|
93828
|
-
if (
|
|
94122
|
+
const sessionPath = join79(MESSAGE_STORAGE, dir, sessionID);
|
|
94123
|
+
if (existsSync76(sessionPath))
|
|
93829
94124
|
return sessionPath;
|
|
93830
94125
|
}
|
|
93831
94126
|
return null;
|
|
@@ -93863,7 +94158,7 @@ function findNearestMessageExcludingCompaction(messageDir) {
|
|
|
93863
94158
|
const parsedByFile = new Map;
|
|
93864
94159
|
for (const file3 of files) {
|
|
93865
94160
|
try {
|
|
93866
|
-
const content = readFileSync45(
|
|
94161
|
+
const content = readFileSync45(join79(messageDir, file3), "utf-8");
|
|
93867
94162
|
const parsed = JSON.parse(content);
|
|
93868
94163
|
parsedByFile.set(file3, parsed);
|
|
93869
94164
|
if (hasFullAgentAndModel(parsed)) {
|
|
@@ -96505,12 +96800,12 @@ function decideCloseAction(state3, sessionId, sessionMappings) {
|
|
|
96505
96800
|
return { type: "close", paneId: mapping.paneId, sessionId };
|
|
96506
96801
|
}
|
|
96507
96802
|
// src/features/tmux-subagent/pane-state-querier.ts
|
|
96508
|
-
var {spawn:
|
|
96803
|
+
var {spawn: spawn13 } = globalThis.Bun;
|
|
96509
96804
|
async function queryWindowState(sourcePaneId) {
|
|
96510
96805
|
const tmux2 = await getTmuxPath();
|
|
96511
96806
|
if (!tmux2)
|
|
96512
96807
|
return null;
|
|
96513
|
-
const proc =
|
|
96808
|
+
const proc = spawn13([
|
|
96514
96809
|
tmux2,
|
|
96515
96810
|
"list-panes",
|
|
96516
96811
|
"-t",
|
|
@@ -98418,7 +98713,7 @@ function createBddContractAgent(model) {
|
|
|
98418
98713
|
createBddContractAgent.mode = MODE2;
|
|
98419
98714
|
|
|
98420
98715
|
// src/agents/builtin-agents/resolve-file-uri.ts
|
|
98421
|
-
import { existsSync as
|
|
98716
|
+
import { existsSync as existsSync77, readFileSync as readFileSync47 } from "fs";
|
|
98422
98717
|
import { homedir as homedir12 } from "os";
|
|
98423
98718
|
import { isAbsolute as isAbsolute6, resolve as resolve9 } from "path";
|
|
98424
98719
|
function resolvePromptAppend(promptAppend, configDir) {
|
|
@@ -98433,7 +98728,7 @@ function resolvePromptAppend(promptAppend, configDir) {
|
|
|
98433
98728
|
} catch {
|
|
98434
98729
|
return `[WARNING: Malformed file URI (invalid percent-encoding): ${promptAppend}]`;
|
|
98435
98730
|
}
|
|
98436
|
-
if (!
|
|
98731
|
+
if (!existsSync77(filePath)) {
|
|
98437
98732
|
return `[WARNING: Could not resolve file URI: ${promptAppend}]`;
|
|
98438
98733
|
}
|
|
98439
98734
|
try {
|
|
@@ -100054,7 +100349,7 @@ var CONSTRUCT_PROMPT_METADATA = {
|
|
|
100054
100349
|
triggers: []
|
|
100055
100350
|
};
|
|
100056
100351
|
function createConstructAgent(model) {
|
|
100057
|
-
const restrictions = createAgentToolAllowlist(["read"]);
|
|
100352
|
+
const restrictions = createAgentToolAllowlist(["read", "document_reader__convert_to_markdown"]);
|
|
100058
100353
|
return {
|
|
100059
100354
|
description: "Analyze media files (PDFs, images, diagrams) that require interpretation beyond raw text. Extracts specific information or summaries from documents, describes visual content. Use when you need analyzed/extracted data rather than literal file contents. (Multimodal-Looker - Matrixx)",
|
|
100060
100355
|
mode: MODE6,
|
|
@@ -100078,7 +100373,7 @@ When NOT to use you:
|
|
|
100078
100373
|
|
|
100079
100374
|
How you work:
|
|
100080
100375
|
1. Receive a file path and a goal describing what to extract
|
|
100081
|
-
2.
|
|
100376
|
+
2. For PDF/DOCX/XLSX/PPTX/media call document_reader__convert_to_markdown with file:///absolute/path, otherwise use Read
|
|
100082
100377
|
3. Return ONLY the relevant extracted information
|
|
100083
100378
|
4. The main agent never processes the raw file - you save context tokens
|
|
100084
100379
|
|
|
@@ -100444,7 +100739,7 @@ context7_query-docs(libraryId: id, query: "specific topic")
|
|
|
100444
100739
|
Tool 1: context7_resolve-library-id("library-name")
|
|
100445
100740
|
\u2192 then context7_query-docs(libraryId: id, query: "specific-topic")
|
|
100446
100741
|
Tool 2: webfetch(relevant_pages_from_sitemap) // Targeted, not random
|
|
100447
|
-
Tool 3:
|
|
100742
|
+
Tool 3: github_search(query: "usage pattern", language: "TypeScript")
|
|
100448
100743
|
\`\`\`
|
|
100449
100744
|
|
|
100450
100745
|
**Output**: Summarize findings with links to official docs (versioned if applicable) and real-world examples.
|
|
@@ -100474,7 +100769,7 @@ Step 4: Construct permalink
|
|
|
100474
100769
|
**Parallel acceleration (4+ calls)**:
|
|
100475
100770
|
\`\`\`
|
|
100476
100771
|
Tool 1: gh repo clone owner/repo \${TMPDIR:-/tmp}/repo -- --depth 1
|
|
100477
|
-
Tool 2:
|
|
100772
|
+
Tool 2: github_search(query: "function_name", repo: "owner/repo")
|
|
100478
100773
|
Tool 3: gh api repos/owner/repo/commits/HEAD --jq '.sha'
|
|
100479
100774
|
Tool 4: context7_get-library-docs(id, topic: "relevant-api")
|
|
100480
100775
|
\`\`\`
|
|
@@ -100513,8 +100808,8 @@ Tool 1: context7_resolve-library-id \u2192 context7_query-docs
|
|
|
100513
100808
|
Tool 2: webfetch(targeted_doc_pages_from_sitemap)
|
|
100514
100809
|
|
|
100515
100810
|
// Code Search
|
|
100516
|
-
Tool 3:
|
|
100517
|
-
Tool 4:
|
|
100811
|
+
Tool 3: github_search(query: "pattern1", language: "TypeScript")
|
|
100812
|
+
Tool 4: github_search(query: "pattern2", repo: "owner/repo")
|
|
100518
100813
|
|
|
100519
100814
|
// Source Analysis
|
|
100520
100815
|
Tool 5: gh repo clone owner/repo \${TMPDIR:-/tmp}/repo -- --depth 1
|
|
@@ -100570,7 +100865,7 @@ https://github.com/tanstack/query/blob/abc123def/packages/react-query/src/useQue
|
|
|
100570
100865
|
| **Sitemap Discovery** | webfetch | \`webfetch(docs_url + "/sitemap.xml")\` to understand doc structure |
|
|
100571
100866
|
| **Read Doc Page** | webfetch | \`webfetch(specific_doc_page)\` for targeted documentation |
|
|
100572
100867
|
| **Latest Info** | websearch_exa | \`websearch_exa_web_search_exa("query ${new Date().getFullYear()}")\` |
|
|
100573
|
-
| **Fast Code Search** |
|
|
100868
|
+
| **Fast Code Search** | github_search | \`github_search(query, language, repo, limit)\` |
|
|
100574
100869
|
| **Deep Code Search** | gh CLI | \`gh search code "query" --repo owner/repo\` |
|
|
100575
100870
|
| **Clone Repo** | gh CLI | \`gh repo clone owner/repo \${TMPDIR:-/tmp}/name -- --depth 1\` |
|
|
100576
100871
|
| **Issues/PRs** | gh CLI | \`gh search issues/prs "query" --repo owner/repo\` |
|
|
@@ -100606,16 +100901,16 @@ Use OS-appropriate temp directory:
|
|
|
100606
100901
|
**Doc Discovery is SEQUENTIAL** (websearch \u2192 version check \u2192 sitemap \u2192 investigate).
|
|
100607
100902
|
**Main phase is PARALLEL** once you know where to look.
|
|
100608
100903
|
|
|
100609
|
-
**Always vary queries** when using
|
|
100904
|
+
**Always vary queries** when using github_search:
|
|
100610
100905
|
\`\`\`
|
|
100611
100906
|
// GOOD: Different angles
|
|
100612
|
-
|
|
100613
|
-
|
|
100614
|
-
|
|
100907
|
+
github_search(query: "useQuery(", language: "TypeScript")
|
|
100908
|
+
github_search(query: "queryOptions", language: "TypeScript")
|
|
100909
|
+
github_search(query: "staleTime:", language: "TypeScript")
|
|
100615
100910
|
|
|
100616
100911
|
// BAD: Same pattern
|
|
100617
|
-
|
|
100618
|
-
|
|
100912
|
+
github_search(query: "useQuery")
|
|
100913
|
+
github_search(query: "useQuery")
|
|
100619
100914
|
\`\`\`
|
|
100620
100915
|
|
|
100621
100916
|
---
|
|
@@ -100625,7 +100920,7 @@ white_rabbit_searchGitHub(query: "useQuery")
|
|
|
100625
100920
|
| Failure | Recovery Action |
|
|
100626
100921
|
|---------|-----------------|
|
|
100627
100922
|
| context7 not found | Clone repo, read source + README directly |
|
|
100628
|
-
|
|
|
100923
|
+
| github_search no results | Broaden query, try concept instead of exact name |
|
|
100629
100924
|
| gh API rate limit | Use cloned repo in temp directory |
|
|
100630
100925
|
| Repo not found | Search for forks or mirrors |
|
|
100631
100926
|
| Sitemap not found | Try \`/sitemap-0.xml\`, \`/sitemap_index.xml\`, or fetch docs index page and parse navigation |
|
|
@@ -100636,7 +100931,7 @@ white_rabbit_searchGitHub(query: "useQuery")
|
|
|
100636
100931
|
|
|
100637
100932
|
## COMMUNICATION RULES
|
|
100638
100933
|
|
|
100639
|
-
1. **NO TOOL NAMES**: Say "I'll search the codebase" not "I'll use
|
|
100934
|
+
1. **NO TOOL NAMES**: Say "I'll search the codebase" not "I'll use github_search"
|
|
100640
100935
|
2. **NO PREAMBLE**: Answer directly, skip "I'll help you with..."
|
|
100641
100936
|
3. **ALWAYS CITE**: Every code claim needs a permalink
|
|
100642
100937
|
4. **USE MARKDOWN**: Code blocks with language identifiers
|
|
@@ -101613,6 +101908,7 @@ Use the right tool for the job:
|
|
|
101613
101908
|
- **Text patterns** (strings, comments, logs): ctx_search (indexed KB) -> ctx_batch_execute / ctx_execute for multi-file analysis. ast_grep_search for structural patterns. grep/glob only as fallback when ctx_* unavailable.
|
|
101614
101909
|
- **File patterns** (find by name/extension): ctx_search (indexed KB) -> LSP / ast_grep_search. glob only as fallback when ctx_* unavailable.
|
|
101615
101910
|
- **History/evolution** (when added, who changed): git commands
|
|
101911
|
+
- **OSS reference** (how others implement X in public repos): github_search (local gh/git/rg CLIs, no third-party services)
|
|
101616
101912
|
|
|
101617
101913
|
Flood with parallel calls. Cross-validate findings across multiple tools.`
|
|
101618
101914
|
};
|
|
@@ -104052,16 +104348,8 @@ function createWebsearchConfig(config4) {
|
|
|
104052
104348
|
}
|
|
104053
104349
|
var websearch = createWebsearchConfig();
|
|
104054
104350
|
|
|
104055
|
-
// src/mcp/white-rabbit.ts
|
|
104056
|
-
var white_rabbit = {
|
|
104057
|
-
type: "remote",
|
|
104058
|
-
url: "https://mcp.grep.app",
|
|
104059
|
-
enabled: true,
|
|
104060
|
-
oauth: false
|
|
104061
|
-
};
|
|
104062
|
-
|
|
104063
104351
|
// src/mcp/types.ts
|
|
104064
|
-
var McpNameSchema = exports_external.enum(["websearch", "context7", "
|
|
104352
|
+
var McpNameSchema = exports_external.enum(["websearch", "context7", "document_reader"]);
|
|
104065
104353
|
var AnyMcpNameSchema = exports_external.string().min(1);
|
|
104066
104354
|
|
|
104067
104355
|
// src/mcp/index.ts
|
|
@@ -104110,9 +104398,6 @@ function createBuiltinMcps(disabledMcps = [], config4, options = {}) {
|
|
|
104110
104398
|
if (!disabledMcps.includes("context7")) {
|
|
104111
104399
|
mcps.context7 = context7;
|
|
104112
104400
|
}
|
|
104113
|
-
if (!disabledMcps.includes("white_rabbit")) {
|
|
104114
|
-
mcps.white_rabbit = white_rabbit;
|
|
104115
|
-
}
|
|
104116
104401
|
if (!disabledMcps.includes("document_reader")) {
|
|
104117
104402
|
if (commandAvailable("uvx")) {
|
|
104118
104403
|
mcps.document_reader = document_reader;
|
|
@@ -104213,7 +104498,7 @@ function applyToolConfig(params) {
|
|
|
104213
104498
|
const denyTaskTools = !isTaskSystem ? { "task_*": "deny", task: "deny" } : {};
|
|
104214
104499
|
params.config.tools = {
|
|
104215
104500
|
...params.config.tools,
|
|
104216
|
-
|
|
104501
|
+
github_search: false,
|
|
104217
104502
|
LspHover: false,
|
|
104218
104503
|
LspCodeActions: false,
|
|
104219
104504
|
LspCodeActionResolve: false,
|
|
@@ -104225,7 +104510,11 @@ function applyToolConfig(params) {
|
|
|
104225
104510
|
const questionPermission = isCliRunMode ? "deny" : "allow";
|
|
104226
104511
|
const operator = agentByKey(params.agentResult, "operator");
|
|
104227
104512
|
if (operator) {
|
|
104228
|
-
operator.permission = { ...operator.permission,
|
|
104513
|
+
operator.permission = { ...operator.permission, github_search: "allow" };
|
|
104514
|
+
}
|
|
104515
|
+
const trinity = agentByKey(params.agentResult, "trinity");
|
|
104516
|
+
if (trinity) {
|
|
104517
|
+
trinity.permission = { ...trinity.permission, github_search: "allow" };
|
|
104229
104518
|
}
|
|
104230
104519
|
const construct = agentByKey(params.agentResult, "construct");
|
|
104231
104520
|
if (construct) {
|
|
@@ -104476,6 +104765,7 @@ function createToolRegistry(args) {
|
|
|
104476
104765
|
const allTools = {
|
|
104477
104766
|
...builtinTools,
|
|
104478
104767
|
...createGrepTools(ctx),
|
|
104768
|
+
...createGithubSearchTools(ctx),
|
|
104479
104769
|
...createGlobTools(ctx),
|
|
104480
104770
|
...createAstGrepTools(ctx),
|
|
104481
104771
|
...createSessionManagerTools(ctx),
|
|
@@ -104942,6 +105232,7 @@ var HookNameEnum = exports_external.enum([
|
|
|
104942
105232
|
"context-mode-enforcer",
|
|
104943
105233
|
"context-mode-enforcer",
|
|
104944
105234
|
"task-edit-guard",
|
|
105235
|
+
"document-reader-guard",
|
|
104945
105236
|
"failure-counter"
|
|
104946
105237
|
]);
|
|
104947
105238
|
var HookNameSchema = exports_external.union([
|
|
@@ -105776,22 +106067,22 @@ function createToolExecuteAfterHandler2(args) {
|
|
|
105776
106067
|
|
|
105777
106068
|
// src/features/assembly-state/manager.ts
|
|
105778
106069
|
init_logger();
|
|
105779
|
-
var
|
|
106070
|
+
var HOOK_NAME21 = "assembly-state";
|
|
105780
106071
|
var disabledSessions = new Set;
|
|
105781
106072
|
function isAssemblyDisabled(sessionID) {
|
|
105782
106073
|
return disabledSessions.has(sessionID);
|
|
105783
106074
|
}
|
|
105784
106075
|
function disableAssembly(sessionID) {
|
|
105785
106076
|
disabledSessions.add(sessionID);
|
|
105786
|
-
log(`[${
|
|
106077
|
+
log(`[${HOOK_NAME21}] Assembly disabled for session`, { sessionID });
|
|
105787
106078
|
disabledSessions.add(sessionID);
|
|
105788
|
-
log(`[${
|
|
106079
|
+
log(`[${HOOK_NAME21}] Consensus disabled for session`, { sessionID });
|
|
105789
106080
|
}
|
|
105790
106081
|
function enableAssembly(sessionID) {
|
|
105791
106082
|
disabledSessions.delete(sessionID);
|
|
105792
|
-
log(`[${
|
|
106083
|
+
log(`[${HOOK_NAME21}] Assembly enabled for session`, { sessionID });
|
|
105793
106084
|
disabledSessions.delete(sessionID);
|
|
105794
|
-
log(`[${
|
|
106085
|
+
log(`[${HOOK_NAME21}] Consensus enabled for session`, { sessionID });
|
|
105795
106086
|
}
|
|
105796
106087
|
// src/plugin/session-agent-resolver.ts
|
|
105797
106088
|
async function resolveSessionAgent(client2, sessionId, timeoutMs = 5000) {
|