opencode-matrixx-v2 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/doctor/checks/context-mode.d.ts +9 -0
- package/dist/cli.js +22 -3
- package/dist/hooks/auto-update-checker/hook/mcp-startup-toast.d.ts +5 -0
- package/dist/hooks/category-skill-reminder/detector.d.ts +13 -0
- package/dist/hooks/category-skill-reminder/formatter.d.ts +5 -0
- package/dist/hooks/category-skill-reminder/hook.d.ts +6 -2
- package/dist/index.js +344 -148
- package/dist/mcps/context7.d.ts +12 -0
- package/dist/mcps/index.d.ts +3 -0
- package/dist/mcps/mcp-startup-notice.d.ts +5 -0
- package/dist/mcps/mcp.transform.d.ts +2 -0
- package/dist/plugin/setup-domains.d.ts +3 -0
- package/dist/tools/delegate-task/sync-session-poller.d.ts +15 -0
- package/package.json +2 -2
|
@@ -26,5 +26,14 @@ export declare function evaluateContextModeToolCompat(toolNames: string[]): Chec
|
|
|
26
26
|
* those sources instead of warning.
|
|
27
27
|
*/
|
|
28
28
|
export declare function evaluateContextModeCwd(server: unknown, source?: "mcp" | "local" | "registry" | string): CheckResult;
|
|
29
|
+
export declare const CONTEXT_MODE_VERSION_FLOOR = "2.1.0";
|
|
30
|
+
/**
|
|
31
|
+
* Pure floor check: major.minor numeric compare against 2.1.
|
|
32
|
+
* Prerelease suffixes (e.g. 2.1.0-next) are stripped before parsing;
|
|
33
|
+
* null/unparsable versions fail-open to false (never blocks doctor).
|
|
34
|
+
*/
|
|
35
|
+
export declare function isContextModeVersionBelowFloor(version: string | null | undefined): boolean;
|
|
36
|
+
/** Pure evaluator for the discipline version floor. Below 2.1.0 warns. */
|
|
37
|
+
export declare function evaluateContextModeVersion(version: string | null | undefined): CheckResult;
|
|
29
38
|
export declare const contextModeCwdCheck: DoctorCheck;
|
|
30
39
|
export declare const contextModeCheck: DoctorCheck;
|
package/dist/cli.js
CHANGED
|
@@ -7235,6 +7235,22 @@ function loadContextModeMcpServer() {
|
|
|
7235
7235
|
}
|
|
7236
7236
|
return;
|
|
7237
7237
|
}
|
|
7238
|
+
var CONTEXT_MODE_VERSION_FLOOR = "2.1.0";
|
|
7239
|
+
function isContextModeVersionBelowFloor(version) {
|
|
7240
|
+
if (typeof version !== "string" || version.length === 0)
|
|
7241
|
+
return false;
|
|
7242
|
+
const core = version.replace(/^[vV]/, "").split("-")[0] ?? "";
|
|
7243
|
+
const parts = core.split(".");
|
|
7244
|
+
const major = Number(parts[0]);
|
|
7245
|
+
const minor = parts.length > 1 ? Number(parts[1]) : 0;
|
|
7246
|
+
if (!Number.isFinite(major) || !Number.isFinite(minor))
|
|
7247
|
+
return false;
|
|
7248
|
+
if (major < 2)
|
|
7249
|
+
return true;
|
|
7250
|
+
if (major > 2)
|
|
7251
|
+
return false;
|
|
7252
|
+
return minor < 1;
|
|
7253
|
+
}
|
|
7238
7254
|
var contextModeCwdCheck = {
|
|
7239
7255
|
name: "context-mode-cwd",
|
|
7240
7256
|
category: "integrations",
|
|
@@ -7269,13 +7285,16 @@ Cache dir: ${cacheDir} ${hasCache ? "(exists)" : "(not found \u2014 will be crea
|
|
|
7269
7285
|
const substituteLine = discipline.path ? `Substitute-File: ${discipline.path}${discipline.version ? ` (v${discipline.version})` : ""}` : "Substitute-File: not found \u2014 fallback table in use; enforcer treats grep/glob as unblocked";
|
|
7270
7286
|
const ctxTools = isCtxToolsAvailable();
|
|
7271
7287
|
const ctxToolsLine = ctxTools ? "Ctx-Tools: available" : "Ctx-Tools: unavailable \u2014 enforcer fail-opens (warn-only)";
|
|
7288
|
+
const belowFloor = isContextModeVersionBelowFloor(discipline.version);
|
|
7289
|
+
const floorLine = belowFloor ? `Version-Floor: v${discipline.version} below supported floor v${CONTEXT_MODE_VERSION_FLOOR} \u2014 upgrade context-mode to >=${CONTEXT_MODE_VERSION_FLOOR} (any 2.1.x)` : null;
|
|
7272
7290
|
return {
|
|
7273
7291
|
name: "context-mode-integration",
|
|
7274
|
-
status: !discipline.path || !ctxTools ? "warn" : "pass",
|
|
7275
|
-
message: `context-mode plugin registered${hasCache ? ", cache present" : " (cache not yet created)"}${discipline.version ? `, discipline v${discipline.version}` : ""}`,
|
|
7292
|
+
status: !discipline.path || !ctxTools || belowFloor ? "warn" : "pass",
|
|
7293
|
+
message: `context-mode plugin registered${hasCache ? ", cache present" : " (cache not yet created)"}${discipline.version ? `, discipline v${discipline.version}` : ""}${belowFloor ? ` (below floor v${CONTEXT_MODE_VERSION_FLOOR})` : ""}`,
|
|
7276
7294
|
detail: `Cache: ${cacheDir}
|
|
7277
7295
|
${substituteLine}
|
|
7278
|
-
${ctxToolsLine}
|
|
7296
|
+
${ctxToolsLine}${floorLine ? `
|
|
7297
|
+
${floorLine}` : ""}
|
|
7279
7298
|
Plan tools: plan_create/read/update/list/delete (unconditional)`
|
|
7280
7299
|
};
|
|
7281
7300
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface NudgeInput {
|
|
2
|
+
tool: string;
|
|
3
|
+
sessionID: string;
|
|
4
|
+
agent?: string;
|
|
5
|
+
toolCallCount: number;
|
|
6
|
+
delegationUsed: boolean;
|
|
7
|
+
reminderShown: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface NudgeEvent {
|
|
10
|
+
skill: string;
|
|
11
|
+
reason: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function detectNudge(input: NudgeInput): NudgeEvent | null;
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import type { AvailableSkill } from "../../agents/dynamic-agent-prompt-builder";
|
|
2
2
|
export declare function buildReminderMessage(availableSkills: AvailableSkill[]): string;
|
|
3
|
+
export interface NudgeMessageEvent {
|
|
4
|
+
skill: string;
|
|
5
|
+
reason: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function buildNudgeMessage(event: NudgeMessageEvent): string;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { AvailableSkill } from "../../agents/dynamic-agent-prompt-builder";
|
|
2
2
|
import type { MatrixxContext } from "../../plugin/context";
|
|
3
|
+
export type { NudgeEvent, NudgeInput } from "./detector";
|
|
4
|
+
export { detectNudge } from "./detector";
|
|
3
5
|
interface ToolExecuteInput {
|
|
4
6
|
tool: string;
|
|
5
7
|
sessionID: string;
|
|
@@ -11,9 +13,11 @@ interface ToolExecuteOutput {
|
|
|
11
13
|
output: string;
|
|
12
14
|
metadata: unknown;
|
|
13
15
|
}
|
|
16
|
+
export interface CategorySkillReminderOptions {
|
|
17
|
+
now?: () => number;
|
|
18
|
+
}
|
|
14
19
|
import type { V2Event } from "../../plugin/context";
|
|
15
|
-
export declare function createCategorySkillReminderHook(ctx: MatrixxContext, availableSkills?: AvailableSkill[]): {
|
|
20
|
+
export declare function createCategorySkillReminderHook(ctx: MatrixxContext, availableSkills?: AvailableSkill[], options?: CategorySkillReminderOptions): {
|
|
16
21
|
"tool.execute.after": (input: ToolExecuteInput, output: ToolExecuteOutput) => Promise<void>;
|
|
17
22
|
event: (event: V2Event) => Promise<void>;
|
|
18
23
|
};
|
|
19
|
-
export {};
|
package/dist/index.js
CHANGED
|
@@ -26192,7 +26192,7 @@ function buildInlineConfig(profile, options) {
|
|
|
26192
26192
|
const commands = merged.commands ?? {};
|
|
26193
26193
|
const manualMode = merged.manualMode ?? {};
|
|
26194
26194
|
return {
|
|
26195
|
-
$schema: "https://raw.githubusercontent.com/Opencode-DCP/opencode-dynamic-context-pruning/v3.
|
|
26195
|
+
$schema: "https://raw.githubusercontent.com/Opencode-DCP/opencode-dynamic-context-pruning/v3.2.0/dcp.schema.json",
|
|
26196
26196
|
enabled: true,
|
|
26197
26197
|
autoUpdate: merged.autoUpdate ?? false,
|
|
26198
26198
|
debug: merged.debug ?? false,
|
|
@@ -33784,13 +33784,17 @@ async function registerCommandTransform(command, entries, deps) {
|
|
|
33784
33784
|
}
|
|
33785
33785
|
|
|
33786
33786
|
// src/mcps/context7.ts
|
|
33787
|
-
|
|
33788
|
-
|
|
33789
|
-
|
|
33790
|
-
|
|
33791
|
-
|
|
33792
|
-
|
|
33793
|
-
}
|
|
33787
|
+
function buildContext7Entry(input = {}) {
|
|
33788
|
+
const key = input.context7ApiKey;
|
|
33789
|
+
return {
|
|
33790
|
+
type: "remote",
|
|
33791
|
+
url: "https://mcp.context7.com/mcp",
|
|
33792
|
+
enabled: true,
|
|
33793
|
+
headers: key ? { Authorization: `Bearer ${key}` } : undefined,
|
|
33794
|
+
oauth: false
|
|
33795
|
+
};
|
|
33796
|
+
}
|
|
33797
|
+
var context7 = buildContext7Entry();
|
|
33794
33798
|
|
|
33795
33799
|
// src/mcps/document-reader.ts
|
|
33796
33800
|
var document_reader = {
|
|
@@ -33810,7 +33814,7 @@ function buildBuiltinMcpEntries(input) {
|
|
|
33810
33814
|
const entries = {};
|
|
33811
33815
|
const failures = [];
|
|
33812
33816
|
if (!disabledMcps.includes("context7")) {
|
|
33813
|
-
entries.context7 =
|
|
33817
|
+
entries.context7 = buildContext7Entry({ context7ApiKey: input.context7ApiKey || undefined });
|
|
33814
33818
|
}
|
|
33815
33819
|
if (!disabledMcps.includes("document_reader")) {
|
|
33816
33820
|
if (input.uvxAvailable) {
|
|
@@ -37962,7 +37966,7 @@ What is the user asking about?
|
|
|
37962
37966
|
|
|
37963
37967
|
// src/skills/skills/document-reader.ts
|
|
37964
37968
|
var DOCUMENT_READER_SKILL_NAME = "document-reader";
|
|
37965
|
-
var DOCUMENT_READER_SKILL_DESCRIPTION = "Use when reading PDFs, extracting content from documents, or analyzing office files (DOCX, XLSX, PPTX) \u2014 document conversion via document_reader MCP
|
|
37969
|
+
var DOCUMENT_READER_SKILL_DESCRIPTION = "Use when reading PDFs, extracting content from documents, or analyzing office files (DOCX, XLSX, PPTX) \u2014 document conversion via document_reader MCP document_reader__convert_to_markdown supporting file: and https: URIs for local and remote files. Related: ulw-research, dev-browser.";
|
|
37966
37970
|
var documentReaderSkill = {
|
|
37967
37971
|
name: DOCUMENT_READER_SKILL_NAME,
|
|
37968
37972
|
description: DOCUMENT_READER_SKILL_DESCRIPTION,
|
|
@@ -38009,7 +38013,7 @@ uri: "https://example.com/data.xlsx"
|
|
|
38009
38013
|
|
|
38010
38014
|
## MUST NOT DO
|
|
38011
38015
|
|
|
38012
|
-
- Do NOT guess file contents \u2014 always call \`
|
|
38016
|
+
- Do NOT guess file contents \u2014 always call \`document_reader__convert_to_markdown\` first
|
|
38013
38017
|
- Do NOT use the Read tool for PDFs or Office files \u2014 it cannot parse binary formats
|
|
38014
38018
|
- Do NOT pass a relative path like \`file://./report.pdf\` \u2014 always use absolute paths
|
|
38015
38019
|
`
|
|
@@ -47681,7 +47685,7 @@ task(
|
|
|
47681
47685
|
Research documentation and knowledge base files related to: <CURRENT_LEAD>
|
|
47682
47686
|
|
|
47683
47687
|
Read AGENTS.md files, README.md, doc/ directories, and inline documentation.
|
|
47684
|
-
For library/framework docs delegate via task(operator) for context7 resolve\u2192query (
|
|
47688
|
+
For library/framework docs delegate via task(operator) for context7 resolve\u2192query (context7_resolve-library-id then context7_query-docs); do NOT call context7 or websearch directly.
|
|
47685
47689
|
|
|
47686
47690
|
OUTPUT: Structured findings from docs with source attribution.
|
|
47687
47691
|
Flag any claims that contradict other sources.
|
|
@@ -56639,7 +56643,30 @@ var startupFailures = [];
|
|
|
56639
56643
|
function setMcpStartupFailures(failures) {
|
|
56640
56644
|
startupFailures = failures;
|
|
56641
56645
|
}
|
|
56646
|
+
function getMcpStartupFailures() {
|
|
56647
|
+
return startupFailures;
|
|
56648
|
+
}
|
|
56642
56649
|
|
|
56650
|
+
// src/mcps/mcp-startup-notice.ts
|
|
56651
|
+
var MCP_UVX_INSTALL_FIX = "curl -LsSf https://astral.sh/uv/install.sh | sh";
|
|
56652
|
+
var MCP_UVX_WARM_FIX = "uvx --from markitdown-mcp markitdown-mcp --help";
|
|
56653
|
+
function buildFailureLine(failure) {
|
|
56654
|
+
if (failure.name === "document_reader") {
|
|
56655
|
+
return "[mcp-startup] document_reader MCP is disabled: uvx (uv package manager) not found in PATH, " + `so document_reader__convert_to_markdown is unavailable.
|
|
56656
|
+
` + `\u2192 fix: Install uv/uvx: ${MCP_UVX_INSTALL_FIX}
|
|
56657
|
+
` + `Then warm cache: ${MCP_UVX_WARM_FIX}`;
|
|
56658
|
+
}
|
|
56659
|
+
return `[mcp-startup] ${failure.name} MCP is disabled: ${failure.error}`;
|
|
56660
|
+
}
|
|
56661
|
+
function buildMcpStartupNotice(failures) {
|
|
56662
|
+
if (failures.length === 0)
|
|
56663
|
+
return;
|
|
56664
|
+
return failures.map(buildFailureLine).join(`
|
|
56665
|
+
`);
|
|
56666
|
+
}
|
|
56667
|
+
function getMcpStartupNotice() {
|
|
56668
|
+
return buildMcpStartupNotice(getMcpStartupFailures());
|
|
56669
|
+
}
|
|
56643
56670
|
// src/plugin-handlers/mcp-config-handler.ts
|
|
56644
56671
|
function captureUserDisabledMcps(userMcp) {
|
|
56645
56672
|
const disabled = new Set;
|
|
@@ -56656,9 +56683,11 @@ async function applyMcpConfig(params) {
|
|
|
56656
56683
|
const disabledMcps = params.pluginConfig.disabled_mcps ?? [];
|
|
56657
56684
|
const userMcp = params.config.mcp;
|
|
56658
56685
|
const userDisabledMcps = captureUserDisabledMcps(userMcp);
|
|
56686
|
+
const context7ApiKey = process.env.CONTEXT7_API_KEY || undefined;
|
|
56659
56687
|
const { entries: builtinMcps, failures } = buildBuiltinMcpEntries({
|
|
56660
56688
|
disabledMcps,
|
|
56661
|
-
uvxAvailable: isCommandAvailable("uvx")
|
|
56689
|
+
uvxAvailable: isCommandAvailable("uvx"),
|
|
56690
|
+
context7ApiKey
|
|
56662
56691
|
});
|
|
56663
56692
|
const merged = {
|
|
56664
56693
|
...builtinMcps,
|
|
@@ -56678,6 +56707,9 @@ async function applyMcpConfig(params) {
|
|
|
56678
56707
|
const summary = failures.map((f) => `${f.name}: ${f.error}`).join("; ");
|
|
56679
56708
|
log(`[mcp-config] MCP startup issues detected: ${summary}`, failures);
|
|
56680
56709
|
setMcpStartupFailures(failures);
|
|
56710
|
+
const notice = buildMcpStartupNotice(failures);
|
|
56711
|
+
if (notice)
|
|
56712
|
+
log(notice);
|
|
56681
56713
|
}
|
|
56682
56714
|
params.config.mcp = merged;
|
|
56683
56715
|
}
|
|
@@ -72286,88 +72318,6 @@ function describeLastMessageParts(messages) {
|
|
|
72286
72318
|
return textOf(last).length > 0 ? "text" : "none";
|
|
72287
72319
|
}
|
|
72288
72320
|
|
|
72289
|
-
// src/tools/delegate-task/sync-result-fetcher.ts
|
|
72290
|
-
function messageText(msg) {
|
|
72291
|
-
const textParts = contentOf(msg).filter((p) => p.type === "text" || p.type === "reasoning");
|
|
72292
|
-
const raw = textParts.map((p) => p.text ?? "").filter(Boolean).join(`
|
|
72293
|
-
`);
|
|
72294
|
-
return raw.replace(/<analysis>[\s\S]*?<\/analysis>/gi, "").trim();
|
|
72295
|
-
}
|
|
72296
|
-
function messageFinalText(msg) {
|
|
72297
|
-
const text = messageText(msg);
|
|
72298
|
-
const trimmed = text.trim();
|
|
72299
|
-
if (trimmed.startsWith("```") && trimmed.endsWith("```")) {
|
|
72300
|
-
const inner = trimmed.slice(3, -3).trim();
|
|
72301
|
-
const firstNewline = inner.indexOf(`
|
|
72302
|
-
`);
|
|
72303
|
-
if (firstNewline > 0 && firstNewline < 100) {
|
|
72304
|
-
return inner.slice(firstNewline + 1).trim();
|
|
72305
|
-
}
|
|
72306
|
-
return inner;
|
|
72307
|
-
}
|
|
72308
|
-
return trimmed;
|
|
72309
|
-
}
|
|
72310
|
-
function extractTaggedDeliverable(messages, tag) {
|
|
72311
|
-
const tagStart = `<${tag}>`;
|
|
72312
|
-
const tagEnd = `</${tag}>`;
|
|
72313
|
-
for (let i = messages.length - 1;i >= 0; i--) {
|
|
72314
|
-
const msg = messages[i];
|
|
72315
|
-
if (!isAssistant(msg))
|
|
72316
|
-
continue;
|
|
72317
|
-
const text = messageText(msg);
|
|
72318
|
-
const startIdx = text.indexOf(tagStart);
|
|
72319
|
-
if (startIdx === -1)
|
|
72320
|
-
continue;
|
|
72321
|
-
const endIdx = text.indexOf(tagEnd, startIdx + tagStart.length);
|
|
72322
|
-
if (endIdx === -1)
|
|
72323
|
-
continue;
|
|
72324
|
-
return {
|
|
72325
|
-
message: msg,
|
|
72326
|
-
text: text.slice(startIdx + tagStart.length, endIdx).trim()
|
|
72327
|
-
};
|
|
72328
|
-
}
|
|
72329
|
-
return;
|
|
72330
|
-
}
|
|
72331
|
-
async function fetchSyncResult(client, sessionID, anchorMessageCount, options) {
|
|
72332
|
-
const messages = await fetchSessionMessages(client, sessionID);
|
|
72333
|
-
const messagesAfterAnchor = anchorMessageCount !== undefined ? messages.slice(anchorMessageCount) : messages;
|
|
72334
|
-
if (anchorMessageCount !== undefined && messagesAfterAnchor.length === 0) {
|
|
72335
|
-
return {
|
|
72336
|
-
ok: false,
|
|
72337
|
-
error: `Session completed with no new messages after the anchor. Observed: ${describeMessageShape(messages)}. ${DIAGNOSTIC_CAUSES}
|
|
72338
|
-
|
|
72339
|
-
Session ID: ${sessionID}`
|
|
72340
|
-
};
|
|
72341
|
-
}
|
|
72342
|
-
if (options?.deliverableTag) {
|
|
72343
|
-
const tagged = extractTaggedDeliverable(messagesAfterAnchor, options.deliverableTag);
|
|
72344
|
-
if (tagged) {
|
|
72345
|
-
const textContent = options.finalTextOnly ? messageFinalText(tagged.message) : tagged.text;
|
|
72346
|
-
return { ok: true, textContent };
|
|
72347
|
-
}
|
|
72348
|
-
}
|
|
72349
|
-
const assistantMessages = messagesAfterAnchor.filter((m) => isAssistant(m)).sort((a, b) => timeOf(b) - timeOf(a));
|
|
72350
|
-
const lastMessage = assistantMessages[0];
|
|
72351
|
-
if (anchorMessageCount !== undefined && !lastMessage) {
|
|
72352
|
-
return {
|
|
72353
|
-
ok: false,
|
|
72354
|
-
error: `Session completed with no new assistant message. Observed: ${describeMessageShape(messagesAfterAnchor)}. ${DIAGNOSTIC_CAUSES}
|
|
72355
|
-
|
|
72356
|
-
Session ID: ${sessionID}`
|
|
72357
|
-
};
|
|
72358
|
-
}
|
|
72359
|
-
if (!lastMessage) {
|
|
72360
|
-
return {
|
|
72361
|
-
ok: false,
|
|
72362
|
-
error: `No assistant response found. Observed: ${describeMessageShape(messages)}. ${DIAGNOSTIC_CAUSES}
|
|
72363
|
-
|
|
72364
|
-
Session ID: ${sessionID}`
|
|
72365
|
-
};
|
|
72366
|
-
}
|
|
72367
|
-
const textContent = options?.finalTextOnly ? messageFinalText(lastMessage) : messageText(lastMessage);
|
|
72368
|
-
return { ok: true, textContent };
|
|
72369
|
-
}
|
|
72370
|
-
|
|
72371
72321
|
// src/shared/poll-timeout-outcome.ts
|
|
72372
72322
|
var POLL_TIMEOUT_PREFIX = "Poll timeout reached";
|
|
72373
72323
|
var LAST_ASSISTANT_TEXT_CAP = 2000;
|
|
@@ -72417,6 +72367,26 @@ function createdAtOf(message) {
|
|
|
72417
72367
|
const created = timeOf(message);
|
|
72418
72368
|
return created === 0 ? undefined : created;
|
|
72419
72369
|
}
|
|
72370
|
+
function completedOf(message) {
|
|
72371
|
+
const completed = message?.time?.completed;
|
|
72372
|
+
return typeof completed === "number" && completed > 0 ? completed : undefined;
|
|
72373
|
+
}
|
|
72374
|
+
function isStreamingAssistant(message, messages) {
|
|
72375
|
+
if (!message || !isAssistant(message))
|
|
72376
|
+
return false;
|
|
72377
|
+
const hostPublishesCompletion = messages.some((candidate) => isAssistant(candidate) && completedOf(candidate) !== undefined);
|
|
72378
|
+
if (!hostPublishesCompletion)
|
|
72379
|
+
return false;
|
|
72380
|
+
return completedOf(message) === undefined;
|
|
72381
|
+
}
|
|
72382
|
+
function strippedAssistantText(message) {
|
|
72383
|
+
return textOf(message).replace(/<analysis>[\s\S]*?<\/analysis>/gi, "").trim();
|
|
72384
|
+
}
|
|
72385
|
+
function lastAssistantHasText(message) {
|
|
72386
|
+
if (!message)
|
|
72387
|
+
return false;
|
|
72388
|
+
return strippedAssistantText(message).length > 0;
|
|
72389
|
+
}
|
|
72420
72390
|
function isSessionComplete(messages) {
|
|
72421
72391
|
let lastUser;
|
|
72422
72392
|
let lastAssistant;
|
|
@@ -72434,6 +72404,8 @@ function isSessionComplete(messages) {
|
|
|
72434
72404
|
return false;
|
|
72435
72405
|
if (NON_TERMINAL_FINISH_REASONS.has(finish))
|
|
72436
72406
|
return false;
|
|
72407
|
+
if (isStreamingAssistant(lastAssistant, messages))
|
|
72408
|
+
return false;
|
|
72437
72409
|
if (!lastUser)
|
|
72438
72410
|
return false;
|
|
72439
72411
|
const userTime = createdAtOf(lastUser);
|
|
@@ -72473,12 +72445,7 @@ function hasToolResultAfterAssistant(messages) {
|
|
|
72473
72445
|
return userId > assistantId;
|
|
72474
72446
|
}
|
|
72475
72447
|
function hasAssistantContent(messages) {
|
|
72476
|
-
return messages.some((m) =>
|
|
72477
|
-
if (!isAssistant(m))
|
|
72478
|
-
return false;
|
|
72479
|
-
const stripped = textOf(m).replace(/<analysis>[\s\S]*?<\/analysis>/gi, "").trim();
|
|
72480
|
-
return stripped.length > 0;
|
|
72481
|
-
});
|
|
72448
|
+
return messages.some((m) => isAssistant(m) && strippedAssistantText(m).length > 0);
|
|
72482
72449
|
}
|
|
72483
72450
|
async function pollSyncSession(ctx, client, input) {
|
|
72484
72451
|
const syncTiming = getTimingConfig();
|
|
@@ -72531,6 +72498,12 @@ Session ID: ${input.sessionID}`;
|
|
|
72531
72498
|
break;
|
|
72532
72499
|
}
|
|
72533
72500
|
const lastAssistant = [...msgs].reverse().find((m) => isAssistant(m));
|
|
72501
|
+
if (isStreamingAssistant(lastAssistant, msgs)) {
|
|
72502
|
+
idleStallCount = 0;
|
|
72503
|
+
lastMsgCount = 0;
|
|
72504
|
+
stableIdlePolls = 0;
|
|
72505
|
+
continue;
|
|
72506
|
+
}
|
|
72534
72507
|
const hasText = hasAssistantContent(msgs);
|
|
72535
72508
|
if (!finishOf(lastAssistant) && hasText) {
|
|
72536
72509
|
const elapsed = Date.now() - pollStart;
|
|
@@ -72585,7 +72558,7 @@ Session ID: ${input.sessionID}`;
|
|
|
72585
72558
|
break;
|
|
72586
72559
|
}
|
|
72587
72560
|
}
|
|
72588
|
-
if (lastAssistant && msgs.length > 0 && Date.now() - pollStart >= syncTiming.MIN_STABILITY_TIME_MS) {
|
|
72561
|
+
if (lastAssistant && msgs.length > 0 && Date.now() - pollStart >= syncTiming.MIN_STABILITY_TIME_MS && !isStreamingAssistant(lastAssistant, msgs) && lastAssistantHasText(lastAssistant)) {
|
|
72589
72562
|
const currentMsgCount = msgs.length;
|
|
72590
72563
|
if (currentMsgCount === lastMsgCount) {
|
|
72591
72564
|
stableIdlePolls++;
|
|
@@ -72613,7 +72586,7 @@ Session ID: ${input.sessionID}`;
|
|
|
72613
72586
|
let lastAssistantText;
|
|
72614
72587
|
try {
|
|
72615
72588
|
const timeoutMsgs = await fetchSessionMessages(client, input.sessionID);
|
|
72616
|
-
const lastAssistant = [...timeoutMsgs].reverse().find((m) => isAssistant(m));
|
|
72589
|
+
const lastAssistant = [...timeoutMsgs].reverse().find((m) => isAssistant(m) && !isStreamingAssistant(m, timeoutMsgs));
|
|
72617
72590
|
const textParts = contentOf(lastAssistant).filter((p) => p.type === "text" && p.text).map((p) => p.text);
|
|
72618
72591
|
if (textParts.length > 0)
|
|
72619
72592
|
lastAssistantText = textParts.join(`
|
|
@@ -72629,6 +72602,99 @@ Session ID: ${input.sessionID}`;
|
|
|
72629
72602
|
});
|
|
72630
72603
|
}
|
|
72631
72604
|
|
|
72605
|
+
// src/tools/delegate-task/sync-result-fetcher.ts
|
|
72606
|
+
function messageText(msg) {
|
|
72607
|
+
const textParts = contentOf(msg).filter((p) => p.type === "text" || p.type === "reasoning");
|
|
72608
|
+
const raw = textParts.map((p) => p.text ?? "").filter(Boolean).join(`
|
|
72609
|
+
`);
|
|
72610
|
+
return raw.replace(/<analysis>[\s\S]*?<\/analysis>/gi, "").trim();
|
|
72611
|
+
}
|
|
72612
|
+
function messageFinalText(msg) {
|
|
72613
|
+
const text = messageText(msg);
|
|
72614
|
+
const trimmed = text.trim();
|
|
72615
|
+
if (trimmed.startsWith("```") && trimmed.endsWith("```")) {
|
|
72616
|
+
const inner = trimmed.slice(3, -3).trim();
|
|
72617
|
+
const firstNewline = inner.indexOf(`
|
|
72618
|
+
`);
|
|
72619
|
+
if (firstNewline > 0 && firstNewline < 100) {
|
|
72620
|
+
return inner.slice(firstNewline + 1).trim();
|
|
72621
|
+
}
|
|
72622
|
+
return inner;
|
|
72623
|
+
}
|
|
72624
|
+
return trimmed;
|
|
72625
|
+
}
|
|
72626
|
+
function extractTaggedDeliverable(messages, tag) {
|
|
72627
|
+
const tagStart = `<${tag}>`;
|
|
72628
|
+
const tagEnd = `</${tag}>`;
|
|
72629
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
72630
|
+
const msg = messages[i];
|
|
72631
|
+
if (!isAssistant(msg))
|
|
72632
|
+
continue;
|
|
72633
|
+
const text = messageText(msg);
|
|
72634
|
+
const startIdx = text.indexOf(tagStart);
|
|
72635
|
+
if (startIdx === -1)
|
|
72636
|
+
continue;
|
|
72637
|
+
const endIdx = text.indexOf(tagEnd, startIdx + tagStart.length);
|
|
72638
|
+
if (endIdx === -1)
|
|
72639
|
+
continue;
|
|
72640
|
+
return {
|
|
72641
|
+
message: msg,
|
|
72642
|
+
text: text.slice(startIdx + tagStart.length, endIdx).trim()
|
|
72643
|
+
};
|
|
72644
|
+
}
|
|
72645
|
+
return;
|
|
72646
|
+
}
|
|
72647
|
+
async function fetchSyncResult(client, sessionID, anchorMessageCount, options) {
|
|
72648
|
+
const messages = await fetchSessionMessages(client, sessionID);
|
|
72649
|
+
const messagesAfterAnchor = anchorMessageCount !== undefined ? messages.slice(anchorMessageCount) : messages;
|
|
72650
|
+
if (anchorMessageCount !== undefined && messagesAfterAnchor.length === 0) {
|
|
72651
|
+
return {
|
|
72652
|
+
ok: false,
|
|
72653
|
+
error: `Session completed with no new messages after the anchor. Observed: ${describeMessageShape(messages)}. ${DIAGNOSTIC_CAUSES}
|
|
72654
|
+
|
|
72655
|
+
Session ID: ${sessionID}`
|
|
72656
|
+
};
|
|
72657
|
+
}
|
|
72658
|
+
const assistantMessages = messagesAfterAnchor.filter((m) => isAssistant(m)).sort((a, b) => timeOf(b) - timeOf(a));
|
|
72659
|
+
const newestAssistant = assistantMessages[0];
|
|
72660
|
+
const hostPublishesCompletion = messages.some((message) => isAssistant(message) && completedOf(message) !== undefined);
|
|
72661
|
+
if (hostPublishesCompletion && isStreamingAssistant(newestAssistant, messages)) {
|
|
72662
|
+
return {
|
|
72663
|
+
ok: false,
|
|
72664
|
+
error: `Session is still streaming its newest assistant message; the result is not complete yet. ${DIAGNOSTIC_CAUSES}
|
|
72665
|
+
|
|
72666
|
+
Session ID: ${sessionID}`
|
|
72667
|
+
};
|
|
72668
|
+
}
|
|
72669
|
+
const candidates = hostPublishesCompletion ? assistantMessages.filter((m) => completedOf(m) !== undefined) : assistantMessages;
|
|
72670
|
+
if (options?.deliverableTag) {
|
|
72671
|
+
const tagged = extractTaggedDeliverable(candidates, options.deliverableTag);
|
|
72672
|
+
if (tagged) {
|
|
72673
|
+
const textContent = options.finalTextOnly ? messageFinalText(tagged.message) : tagged.text;
|
|
72674
|
+
return { ok: true, textContent };
|
|
72675
|
+
}
|
|
72676
|
+
}
|
|
72677
|
+
const lastMessage = candidates.find((m) => messageText(m).length > 0) ?? candidates[0];
|
|
72678
|
+
if (anchorMessageCount !== undefined && !lastMessage) {
|
|
72679
|
+
return {
|
|
72680
|
+
ok: false,
|
|
72681
|
+
error: `Session completed with no new assistant message. Observed: ${describeMessageShape(messagesAfterAnchor)}. ${DIAGNOSTIC_CAUSES}
|
|
72682
|
+
|
|
72683
|
+
Session ID: ${sessionID}`
|
|
72684
|
+
};
|
|
72685
|
+
}
|
|
72686
|
+
if (!lastMessage) {
|
|
72687
|
+
return {
|
|
72688
|
+
ok: false,
|
|
72689
|
+
error: `No assistant response found. Observed: ${describeMessageShape(messages)}. ${DIAGNOSTIC_CAUSES}
|
|
72690
|
+
|
|
72691
|
+
Session ID: ${sessionID}`
|
|
72692
|
+
};
|
|
72693
|
+
}
|
|
72694
|
+
const textContent = options?.finalTextOnly ? messageFinalText(lastMessage) : messageText(lastMessage);
|
|
72695
|
+
return { ok: true, textContent };
|
|
72696
|
+
}
|
|
72697
|
+
|
|
72632
72698
|
// src/tools/delegate-task/sync-continuation-deps.ts
|
|
72633
72699
|
var syncContinuationDeps = {
|
|
72634
72700
|
pollSyncSession,
|
|
@@ -72727,11 +72793,20 @@ Session ID: ${continuationID}`;
|
|
|
72727
72793
|
return result.error;
|
|
72728
72794
|
}
|
|
72729
72795
|
const duration = formatDuration(startTime);
|
|
72796
|
+
const textContent = result.textContent.trim();
|
|
72797
|
+
if (textContent.length === 0) {
|
|
72798
|
+
return `Task returned no readable output (empty assistant text).
|
|
72799
|
+
|
|
72800
|
+
Resume the same session instead of spawning a new one:
|
|
72801
|
+
task(session_id="${continuationID}", prompt="...")
|
|
72802
|
+
|
|
72803
|
+
Session ID: ${continuationID}`;
|
|
72804
|
+
}
|
|
72730
72805
|
return `Task continued and completed in ${duration}.
|
|
72731
72806
|
|
|
72732
72807
|
---
|
|
72733
72808
|
|
|
72734
|
-
${
|
|
72809
|
+
${textContent}
|
|
72735
72810
|
|
|
72736
72811
|
<task_metadata>
|
|
72737
72812
|
session_id: ${continuationID}
|
|
@@ -73031,13 +73106,22 @@ async function executeSyncTask(args, ctx, executorCtx, parentContext, agentToUse
|
|
|
73031
73106
|
}
|
|
73032
73107
|
didSucceed = true;
|
|
73033
73108
|
const duration = formatDuration(startTime);
|
|
73109
|
+
const textContent = result.textContent.trim();
|
|
73110
|
+
if (textContent.length === 0) {
|
|
73111
|
+
return `Task returned no readable output (empty assistant text).
|
|
73112
|
+
|
|
73113
|
+
Resume the same session instead of spawning a new one:
|
|
73114
|
+
task(session_id="${sessionID}", prompt="...")
|
|
73115
|
+
|
|
73116
|
+
Session ID: ${sessionID}`;
|
|
73117
|
+
}
|
|
73034
73118
|
return `Task completed in ${duration}.
|
|
73035
73119
|
|
|
73036
73120
|
Agent: ${agentToUse}${args.category ? ` (category: ${args.category})` : ""}
|
|
73037
73121
|
|
|
73038
73122
|
---
|
|
73039
73123
|
|
|
73040
|
-
${
|
|
73124
|
+
${textContent}
|
|
73041
73125
|
|
|
73042
73126
|
<task_metadata>
|
|
73043
73127
|
session_id: ${sessionID}
|
|
@@ -75958,6 +76042,14 @@ async function updateAndShowConnectedProvidersCacheStatus(_ctx) {
|
|
|
75958
76042
|
}
|
|
75959
76043
|
}
|
|
75960
76044
|
|
|
76045
|
+
// src/hooks/auto-update-checker/hook/mcp-startup-toast.ts
|
|
76046
|
+
async function showMcpStartupNoticeIfAny() {
|
|
76047
|
+
const notice = getMcpStartupNotice();
|
|
76048
|
+
if (!notice)
|
|
76049
|
+
return;
|
|
76050
|
+
notify("warn", notice);
|
|
76051
|
+
}
|
|
76052
|
+
|
|
75961
76053
|
// src/hooks/auto-update-checker/hook/model-cache-warning.ts
|
|
75962
76054
|
async function showModelCacheWarningIfNeeded(_ctx) {
|
|
75963
76055
|
if (isModelCacheAvailable())
|
|
@@ -76010,6 +76102,9 @@ v${latestVersion} available. Restart OpenCode to apply.` : "OpenCode is now on S
|
|
|
76010
76102
|
return;
|
|
76011
76103
|
hasChecked = true;
|
|
76012
76104
|
setTimeout(async () => {
|
|
76105
|
+
showMcpStartupNoticeIfAny().catch((err) => {
|
|
76106
|
+
log("[auto-update-checker] MCP startup notice failed:", err);
|
|
76107
|
+
});
|
|
76013
76108
|
const cachedVersion = getCachedVersion();
|
|
76014
76109
|
const localDevVersion = getLocalDevVersion(ctx.directory);
|
|
76015
76110
|
const displayVersion = localDevVersion ?? cachedVersion;
|
|
@@ -76114,6 +76209,51 @@ function createBashFileReadGuardHook() {
|
|
|
76114
76209
|
}
|
|
76115
76210
|
};
|
|
76116
76211
|
}
|
|
76212
|
+
// src/hooks/category-skill-reminder/detector.ts
|
|
76213
|
+
var TARGET_AGENTS = new Set([
|
|
76214
|
+
"morpheus",
|
|
76215
|
+
"mouse",
|
|
76216
|
+
"niobe"
|
|
76217
|
+
]);
|
|
76218
|
+
var DELEGATABLE_WORK_TOOLS = new Set([
|
|
76219
|
+
"edit",
|
|
76220
|
+
"write",
|
|
76221
|
+
"bash",
|
|
76222
|
+
"shell",
|
|
76223
|
+
"read",
|
|
76224
|
+
"grep",
|
|
76225
|
+
"glob"
|
|
76226
|
+
]);
|
|
76227
|
+
var NUDGE_THRESHOLD = 3;
|
|
76228
|
+
var NUDGE_SKILL = "construct";
|
|
76229
|
+
function isTargetAgent(sessionID, inputAgent) {
|
|
76230
|
+
const agent = getSessionAgent(sessionID) ?? inputAgent;
|
|
76231
|
+
if (!agent)
|
|
76232
|
+
return false;
|
|
76233
|
+
const agentKey = getAgentConfigKey(agent);
|
|
76234
|
+
return TARGET_AGENTS.has(agentKey) || agentKey.includes("morpheus") || agentKey.includes("niobe");
|
|
76235
|
+
}
|
|
76236
|
+
function detectNudge(input) {
|
|
76237
|
+
try {
|
|
76238
|
+
if (subagentSessions.has(input.sessionID))
|
|
76239
|
+
return null;
|
|
76240
|
+
if (!isTargetAgent(input.sessionID, input.agent))
|
|
76241
|
+
return null;
|
|
76242
|
+
if (!DELEGATABLE_WORK_TOOLS.has(input.tool.toLowerCase()))
|
|
76243
|
+
return null;
|
|
76244
|
+
if (input.delegationUsed || input.reminderShown)
|
|
76245
|
+
return null;
|
|
76246
|
+
if (input.toolCallCount < NUDGE_THRESHOLD)
|
|
76247
|
+
return null;
|
|
76248
|
+
return {
|
|
76249
|
+
skill: NUDGE_SKILL,
|
|
76250
|
+
reason: `delegatable work without delegation (${input.toolCallCount} ${input.tool} calls)`
|
|
76251
|
+
};
|
|
76252
|
+
} catch {
|
|
76253
|
+
return null;
|
|
76254
|
+
}
|
|
76255
|
+
}
|
|
76256
|
+
|
|
76117
76257
|
// src/hooks/category-skill-reminder/formatter.ts
|
|
76118
76258
|
function formatSkillNames(skills, limit) {
|
|
76119
76259
|
if (skills.length === 0)
|
|
@@ -76149,12 +76289,12 @@ function buildReminderMessage(availableSkills) {
|
|
|
76149
76289
|
}
|
|
76150
76290
|
|
|
76151
76291
|
// src/hooks/category-skill-reminder/hook.ts
|
|
76152
|
-
var
|
|
76292
|
+
var TARGET_AGENTS2 = new Set([
|
|
76153
76293
|
"morpheus",
|
|
76154
76294
|
"mouse",
|
|
76155
76295
|
"niobe"
|
|
76156
76296
|
]);
|
|
76157
|
-
var
|
|
76297
|
+
var DELEGATABLE_WORK_TOOLS2 = new Set([
|
|
76158
76298
|
"edit",
|
|
76159
76299
|
"write",
|
|
76160
76300
|
"bash",
|
|
@@ -76167,6 +76307,7 @@ var DELEGATION_TOOLS = new Set([
|
|
|
76167
76307
|
"task",
|
|
76168
76308
|
"subagent"
|
|
76169
76309
|
]);
|
|
76310
|
+
var DEDUP_WINDOW_MS2 = 60000;
|
|
76170
76311
|
function hashSnippet(value) {
|
|
76171
76312
|
let hash = 5381;
|
|
76172
76313
|
for (let i = 0;i < value.length; i++) {
|
|
@@ -76174,9 +76315,11 @@ function hashSnippet(value) {
|
|
|
76174
76315
|
}
|
|
76175
76316
|
return (hash >>> 0).toString(16);
|
|
76176
76317
|
}
|
|
76177
|
-
function createCategorySkillReminderHook(ctx, availableSkills = []) {
|
|
76318
|
+
function createCategorySkillReminderHook(ctx, availableSkills = [], options = {}) {
|
|
76178
76319
|
const sessionStates = new Map;
|
|
76320
|
+
const lastNudgeAt = new Map;
|
|
76179
76321
|
const reminderMessage = buildReminderMessage(availableSkills);
|
|
76322
|
+
const now = options.now ?? Date.now;
|
|
76180
76323
|
function getOrCreateState(sessionID) {
|
|
76181
76324
|
if (!sessionStates.has(sessionID)) {
|
|
76182
76325
|
sessionStates.set(sessionID, {
|
|
@@ -76187,58 +76330,103 @@ function createCategorySkillReminderHook(ctx, availableSkills = []) {
|
|
|
76187
76330
|
}
|
|
76188
76331
|
return sessionStates.get(sessionID);
|
|
76189
76332
|
}
|
|
76333
|
+
function clearSessionState(sessionID) {
|
|
76334
|
+
sessionStates.delete(sessionID);
|
|
76335
|
+
}
|
|
76336
|
+
function clearSessionDedup(sessionID) {
|
|
76337
|
+
const prefix = `${sessionID}:`;
|
|
76338
|
+
for (const key of lastNudgeAt.keys()) {
|
|
76339
|
+
if (key.startsWith(prefix))
|
|
76340
|
+
lastNudgeAt.delete(key);
|
|
76341
|
+
}
|
|
76342
|
+
}
|
|
76190
76343
|
function isTargetAgent(sessionID, inputAgent) {
|
|
76191
76344
|
const agent = getSessionAgent(sessionID) ?? inputAgent;
|
|
76192
76345
|
if (!agent)
|
|
76193
76346
|
return false;
|
|
76194
76347
|
const agentKey = getAgentConfigKey(agent);
|
|
76195
|
-
return
|
|
76348
|
+
return TARGET_AGENTS2.has(agentKey) || agentKey.includes("morpheus") || agentKey.includes("niobe");
|
|
76196
76349
|
}
|
|
76197
76350
|
const toolExecuteAfter = async (input, output) => {
|
|
76198
|
-
|
|
76199
|
-
|
|
76200
|
-
|
|
76201
|
-
|
|
76202
|
-
|
|
76203
|
-
|
|
76204
|
-
|
|
76205
|
-
|
|
76206
|
-
|
|
76207
|
-
|
|
76208
|
-
|
|
76209
|
-
|
|
76351
|
+
try {
|
|
76352
|
+
const { tool, sessionID } = input;
|
|
76353
|
+
const toolLower = tool.toLowerCase();
|
|
76354
|
+
if (subagentSessions.has(sessionID))
|
|
76355
|
+
return;
|
|
76356
|
+
if (!isTargetAgent(sessionID, input.agent)) {
|
|
76357
|
+
return;
|
|
76358
|
+
}
|
|
76359
|
+
const state = getOrCreateState(sessionID);
|
|
76360
|
+
if (DELEGATION_TOOLS.has(toolLower)) {
|
|
76361
|
+
state.delegationUsed = true;
|
|
76362
|
+
log("[category-skill-reminder] Delegation tool used", { sessionID, tool });
|
|
76363
|
+
return;
|
|
76364
|
+
}
|
|
76365
|
+
if (!DELEGATABLE_WORK_TOOLS2.has(toolLower)) {
|
|
76366
|
+
return;
|
|
76367
|
+
}
|
|
76368
|
+
state.toolCallCount++;
|
|
76369
|
+
let nudge = null;
|
|
76370
|
+
try {
|
|
76371
|
+
nudge = detectNudge({
|
|
76372
|
+
tool,
|
|
76373
|
+
sessionID,
|
|
76374
|
+
agent: input.agent,
|
|
76375
|
+
toolCallCount: state.toolCallCount,
|
|
76376
|
+
delegationUsed: state.delegationUsed,
|
|
76377
|
+
reminderShown: state.reminderShown
|
|
76378
|
+
});
|
|
76379
|
+
} catch {
|
|
76380
|
+
nudge = null;
|
|
76381
|
+
}
|
|
76382
|
+
if (nudge) {
|
|
76383
|
+
const dedupKey = `${sessionID}:${toolLower}:${nudge.skill}`;
|
|
76384
|
+
const lastAt = lastNudgeAt.get(dedupKey);
|
|
76385
|
+
const current = now();
|
|
76386
|
+
if (lastAt !== undefined && current - lastAt < DEDUP_WINDOW_MS2) {
|
|
76387
|
+
return;
|
|
76388
|
+
}
|
|
76389
|
+
lastNudgeAt.set(dedupKey, current);
|
|
76390
|
+
output.output += reminderMessage;
|
|
76391
|
+
state.reminderShown = true;
|
|
76392
|
+
log("[category-skill-reminder] Reminder injected", {
|
|
76393
|
+
sessionID,
|
|
76394
|
+
toolCallCount: state.toolCallCount
|
|
76395
|
+
});
|
|
76396
|
+
try {
|
|
76397
|
+
await notifyInjection(ctx, {
|
|
76398
|
+
sessionID,
|
|
76399
|
+
injector: "category-skill-reminder",
|
|
76400
|
+
hash: hashSnippet(reminderMessage),
|
|
76401
|
+
title: "Context injection",
|
|
76402
|
+
message: "Delegation guidance applied"
|
|
76403
|
+
});
|
|
76404
|
+
} catch {
|
|
76405
|
+
return;
|
|
76406
|
+
}
|
|
76407
|
+
}
|
|
76408
|
+
} catch {
|
|
76210
76409
|
return;
|
|
76211
76410
|
}
|
|
76212
|
-
state.toolCallCount++;
|
|
76213
|
-
if (state.toolCallCount >= 3 && !state.delegationUsed && !state.reminderShown) {
|
|
76214
|
-
output.output += reminderMessage;
|
|
76215
|
-
state.reminderShown = true;
|
|
76216
|
-
log("[category-skill-reminder] Reminder injected", {
|
|
76217
|
-
sessionID,
|
|
76218
|
-
toolCallCount: state.toolCallCount
|
|
76219
|
-
});
|
|
76220
|
-
await notifyInjection(ctx, {
|
|
76221
|
-
sessionID,
|
|
76222
|
-
injector: "category-skill-reminder",
|
|
76223
|
-
hash: hashSnippet(reminderMessage),
|
|
76224
|
-
title: "Context injection",
|
|
76225
|
-
message: "Delegation guidance applied"
|
|
76226
|
-
});
|
|
76227
|
-
}
|
|
76228
76411
|
};
|
|
76229
76412
|
const eventHandler = async (event) => {
|
|
76230
|
-
|
|
76231
|
-
|
|
76232
|
-
|
|
76233
|
-
|
|
76234
|
-
|
|
76413
|
+
try {
|
|
76414
|
+
const props = event.data;
|
|
76415
|
+
if (event.type === "session.deleted") {
|
|
76416
|
+
const sessionInfo = props?.info;
|
|
76417
|
+
if (sessionInfo?.id) {
|
|
76418
|
+
clearSessionState(sessionInfo.id);
|
|
76419
|
+
clearSessionDedup(sessionInfo.id);
|
|
76420
|
+
}
|
|
76235
76421
|
}
|
|
76236
|
-
|
|
76237
|
-
|
|
76238
|
-
|
|
76239
|
-
|
|
76240
|
-
|
|
76422
|
+
if (event.type === "session.compacted") {
|
|
76423
|
+
const sessionID = props?.sessionID ?? props?.info?.id;
|
|
76424
|
+
if (sessionID) {
|
|
76425
|
+
clearSessionState(sessionID);
|
|
76426
|
+
}
|
|
76241
76427
|
}
|
|
76428
|
+
} catch {
|
|
76429
|
+
return;
|
|
76242
76430
|
}
|
|
76243
76431
|
};
|
|
76244
76432
|
return {
|
|
@@ -79254,7 +79442,7 @@ var BLOCKED_PATTERNS = [
|
|
|
79254
79442
|
/\bpython3?\b[^|&;]*\.(pdf|docx?|xlsx?|pptx?|jpe?g|png|gif|webp|mp3|wav)/,
|
|
79255
79443
|
/\bmarkitdown\b/
|
|
79256
79444
|
];
|
|
79257
|
-
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
|
|
79445
|
+
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 document_reader__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.";
|
|
79258
79446
|
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.";
|
|
79259
79447
|
|
|
79260
79448
|
// src/hooks/document-reader-guard/hook.ts
|
|
@@ -92503,6 +92691,13 @@ async function guard(label, fn) {
|
|
|
92503
92691
|
function asRecord8(value) {
|
|
92504
92692
|
return value !== null && typeof value === "object" ? value : {};
|
|
92505
92693
|
}
|
|
92694
|
+
function hideNativeSubagentTool(editor) {
|
|
92695
|
+
try {
|
|
92696
|
+
editor.remove("subagent");
|
|
92697
|
+
} catch {
|
|
92698
|
+
log("[matrixx] native subagent tool not present, skipping hide");
|
|
92699
|
+
}
|
|
92700
|
+
}
|
|
92506
92701
|
async function registerDomainTools(input) {
|
|
92507
92702
|
const { ctx, bridge, directory, worktree } = input;
|
|
92508
92703
|
const tools = bridge.tool;
|
|
@@ -92548,6 +92743,7 @@ async function registerDomainTools(input) {
|
|
|
92548
92743
|
tool.input = override.parameters;
|
|
92549
92744
|
});
|
|
92550
92745
|
}
|
|
92746
|
+
hideNativeSubagentTool(editor);
|
|
92551
92747
|
}));
|
|
92552
92748
|
return registration ? [registration] : [];
|
|
92553
92749
|
}
|
package/dist/mcps/context7.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
export interface BuildContext7EntryInput {
|
|
2
|
+
context7ApiKey?: string;
|
|
3
|
+
}
|
|
4
|
+
export declare function buildContext7Entry(input?: BuildContext7EntryInput): {
|
|
5
|
+
type: "remote";
|
|
6
|
+
url: string;
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
headers: {
|
|
9
|
+
Authorization: string;
|
|
10
|
+
} | undefined;
|
|
11
|
+
oauth: false;
|
|
12
|
+
};
|
|
1
13
|
export declare const context7: {
|
|
2
14
|
type: "remote";
|
|
3
15
|
url: string;
|
package/dist/mcps/index.d.ts
CHANGED
|
@@ -10,10 +10,13 @@ import { type BuiltinMcpEntry } from "./mcp.transform";
|
|
|
10
10
|
import { type McpCreationFailure } from "./mcp-validator";
|
|
11
11
|
export type { McpTransformDomain, McpTransformEditor, RegisterMcpTransformResult, } from "./mcp.transform";
|
|
12
12
|
export { type BuiltinMcpEntry, buildBuiltinMcpEntries, registerMcpTransform, toV2McpEntry } from "./mcp.transform";
|
|
13
|
+
export { buildMcpStartupNotice, getMcpStartupNotice, MCP_UVX_INSTALL_FIX, MCP_UVX_WARM_FIX } from "./mcp-startup-notice";
|
|
13
14
|
export type { McpCreationFailure } from "./mcp-validator";
|
|
14
15
|
export { type McpName, McpNameSchema } from "./types";
|
|
15
16
|
export interface CreateBuiltinMcpsOptions {
|
|
16
17
|
isCommandAvailable?: (command: string) => boolean;
|
|
18
|
+
/** Overrides `process.env.CONTEXT7_API_KEY`; resolved at the edge, never in the builder. */
|
|
19
|
+
context7ApiKey?: string;
|
|
17
20
|
}
|
|
18
21
|
export interface CreateBuiltinMcpsResult {
|
|
19
22
|
mcps: Record<string, BuiltinMcpEntry>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { McpCreationFailure } from "./mcp-validator";
|
|
2
|
+
export declare const MCP_UVX_INSTALL_FIX = "curl -LsSf https://astral.sh/uv/install.sh | sh";
|
|
3
|
+
export declare const MCP_UVX_WARM_FIX = "uvx --from markitdown-mcp markitdown-mcp --help";
|
|
4
|
+
export declare function buildMcpStartupNotice(failures: McpCreationFailure[]): string | undefined;
|
|
5
|
+
export declare function getMcpStartupNotice(): string | undefined;
|
|
@@ -23,6 +23,8 @@ export interface BuildBuiltinMcpEntriesInput {
|
|
|
23
23
|
disabledMcps?: string[];
|
|
24
24
|
/** Resolved by the caller (e.g. `isCommandAvailable("uvx")`); never spawned here. */
|
|
25
25
|
uvxAvailable: boolean;
|
|
26
|
+
/** Resolved by the caller from `process.env.CONTEXT7_API_KEY` at the edge; never read here. */
|
|
27
|
+
context7ApiKey?: string;
|
|
26
28
|
}
|
|
27
29
|
export interface BuildBuiltinMcpEntriesResult {
|
|
28
30
|
entries: Record<string, BuiltinMcpEntry>;
|
|
@@ -104,6 +104,9 @@ export interface DomainRegistration {
|
|
|
104
104
|
* the empty task response). Join text parts; non-text parts contribute nothing.
|
|
105
105
|
*/
|
|
106
106
|
export declare function hookOutputText(content: unknown): string | undefined;
|
|
107
|
+
export declare function hideNativeSubagentTool(editor: {
|
|
108
|
+
remove(id: string): void;
|
|
109
|
+
}): void;
|
|
107
110
|
/**
|
|
108
111
|
* Applies the assembled `Hooks` onto the V2 context.
|
|
109
112
|
* Returns the registration set whose `dispose` tears everything down.
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import type { SessionMessage } from "../../shared/session-message";
|
|
2
2
|
import type { NativeClient, ToolContextWithMetadata } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* `time.completed` from the flat message shape. The shared `session-message`
|
|
5
|
+
* surface is frozen (see tests/shared/no-v1-shape.test.ts), so this accessor
|
|
6
|
+
* lives with its only consumer instead of graduating into `shared/`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function completedOf(message: unknown): number | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* OpenCode V2 publishes `time.completed` on assistant messages: the row is
|
|
11
|
+
* created when a step starts and completed when it ends, so an assistant
|
|
12
|
+
* message without `time.completed` is still streaming. Older payloads (and
|
|
13
|
+
* test corpora) never carry the field — when no assistant message in the
|
|
14
|
+
* observed set carries it, the host does not expose completion and every
|
|
15
|
+
* message must be treated as settled.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isStreamingAssistant(message: SessionMessage | undefined, messages: SessionMessage[]): boolean;
|
|
3
18
|
export declare function isSessionComplete(messages: SessionMessage[]): boolean;
|
|
4
19
|
export declare function hasToolResultAfterAssistant(messages: SessionMessage[]): boolean;
|
|
5
20
|
export declare function hasAssistantContent(messages: SessionMessage[]): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-matrixx-v2",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"@code-yeongyu/comment-checker": "^0.6.1",
|
|
79
79
|
"@cucumber/gherkin": "^34.0.0",
|
|
80
80
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
81
|
-
"context-mode": "2.
|
|
81
|
+
"context-mode": "2.1.*",
|
|
82
82
|
"js-yaml": "^4.1.1",
|
|
83
83
|
"jsonc-parser": "^3.3.1",
|
|
84
84
|
"picomatch": "^4.0.2",
|