mixdog 0.9.45 → 0.9.46
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/package.json +5 -2
- package/scripts/agent-dispatch-abort-compose-test.mjs +31 -0
- package/scripts/agent-model-liveness-test.mjs +618 -0
- package/scripts/agent-trace-io-test.mjs +68 -4
- package/scripts/bench-run.mjs +30 -3
- package/scripts/compact-pressure-test.mjs +187 -4
- package/scripts/compact-prior-context-flatten-test.mjs +252 -0
- package/scripts/compact-trigger-migration-smoke.mjs +8 -6
- package/scripts/compacted-placeholder-scrub-test.mjs +20 -34
- package/scripts/context-mcp-metering-test.mjs +75 -0
- package/scripts/explore-prompt-policy-test.mjs +15 -16
- package/scripts/explore-timeout-cancel-test.mjs +345 -0
- package/scripts/headless-pristine-execution-test.mjs +614 -0
- package/scripts/internal-comms-smoke.mjs +15 -6
- package/scripts/live-worker-smoke.mjs +1 -1
- package/scripts/memory-core-input-test.mjs +137 -0
- package/scripts/memory-rule-contract-test.mjs +5 -5
- package/scripts/openai-oauth-refresh-race-test.mjs +120 -0
- package/scripts/openai-oauth-ws-1006-retry-test.mjs +26 -0
- package/scripts/parent-abort-link-test.mjs +22 -0
- package/scripts/provider-toolcall-test.mjs +22 -0
- package/scripts/reactive-compact-persist-smoke.mjs +8 -4
- package/scripts/session-bench.mjs +3 -70
- package/scripts/task-bench.mjs +0 -2
- package/scripts/terminal-bench-isolation-guards-test.mjs +102 -0
- package/scripts/tool-smoke.mjs +21 -21
- package/scripts/tool-tui-presentation-test.mjs +68 -0
- package/scripts/tui-transcript-jitter-harness-entry.jsx +91 -10
- package/src/app.mjs +28 -103
- package/src/cli.mjs +17 -13
- package/src/headless-command.mjs +139 -0
- package/src/headless-role.mjs +121 -10
- package/src/help.mjs +4 -1
- package/src/rules/agent/00-common.md +3 -3
- package/src/rules/agent/00-core.md +8 -9
- package/src/rules/agent/20-skip-protocol.md +2 -3
- package/src/rules/agent/30-explorer.md +50 -56
- package/src/rules/agent/40-cycle1-agent.md +10 -12
- package/src/rules/agent/41-cycle2-agent.md +12 -9
- package/src/rules/agent/42-cycle3-agent.md +4 -6
- package/src/rules/lead/01-general.md +5 -6
- package/src/rules/lead/02-channels.md +1 -1
- package/src/rules/lead/lead-brief.md +14 -17
- package/src/rules/lead/lead-tool.md +3 -3
- package/src/rules/shared/01-tool.md +41 -43
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +46 -10
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +44 -31
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +18 -3
- package/src/runtime/agent/orchestrator/config.mjs +96 -30
- package/src/runtime/agent/orchestrator/context/collect.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +18 -5
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +6 -6
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +123 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +148 -30
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +5 -7
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +62 -14
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +141 -19
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -4
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +19 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +85 -17
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -8
- package/src/runtime/agent/orchestrator/providers/registry.mjs +47 -17
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +159 -20
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +83 -10
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +75 -7
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +4 -374
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +0 -75
- package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +0 -5
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +32 -16
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +86 -15
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +30 -27
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +1 -14
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +21 -27
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +1 -3
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -51
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +26 -0
- package/src/runtime/memory/index.mjs +0 -1
- package/src/runtime/memory/lib/core-memory-store.mjs +44 -24
- package/src/runtime/memory/lib/http-router.mjs +0 -193
- package/src/runtime/memory/lib/memory-action-handlers.mjs +37 -11
- package/src/runtime/memory/tool-defs.mjs +5 -6
- package/src/runtime/shared/config.mjs +11 -34
- package/src/runtime/shared/pristine-execution-contract.json +75 -0
- package/src/runtime/shared/pristine-execution.mjs +356 -0
- package/src/runtime/shared/provider-api-key.mjs +43 -0
- package/src/runtime/shared/provider-auth-binding.mjs +21 -0
- package/src/session-runtime/context-status.mjs +61 -13
- package/src/session-runtime/mcp-glue.mjs +29 -2
- package/src/session-runtime/plugin-mcp.mjs +7 -0
- package/src/session-runtime/resource-api.mjs +38 -5
- package/src/session-runtime/runtime-core.mjs +5 -1
- package/src/session-runtime/session-turn-api.mjs +14 -2
- package/src/session-runtime/settings-api.mjs +5 -0
- package/src/session-runtime/tool-catalog.mjs +13 -2
- package/src/session-runtime/tool-defs.mjs +1 -3
- package/src/standalone/agent-task-status.mjs +50 -11
- package/src/standalone/agent-tool/tool-def.mjs +1 -1
- package/src/standalone/explore-tool.mjs +257 -49
- package/src/standalone/seeds.mjs +1 -0
- package/src/tui/App.jsx +23 -10
- package/src/tui/app/use-transcript-scroll.mjs +4 -3
- package/src/tui/app/use-transcript-window.mjs +12 -21
- package/src/tui/components/ContextPanel.jsx +19 -25
- package/src/tui/dist/index.mjs +77 -65
- package/src/tui/engine/agent-envelope.mjs +16 -5
- package/src/tui/engine/labels.mjs +1 -1
- package/src/workflows/default/WORKFLOW.md +21 -51
- package/src/workflows/solo/WORKFLOW.md +12 -17
- package/src/workflows/solo-review/WORKFLOW.md +0 -47
|
@@ -6,13 +6,19 @@
|
|
|
6
6
|
import { resolve } from 'node:path';
|
|
7
7
|
import { statSync } from 'node:fs';
|
|
8
8
|
import { clean } from './session-text.mjs';
|
|
9
|
-
import { readProjectMcpServers } from './plugin-mcp.mjs';
|
|
9
|
+
import { normalizeMcpProjectPathKey, readProjectMcpServers } from './plugin-mcp.mjs';
|
|
10
10
|
|
|
11
11
|
// Cache project-local `.mcp.json` reads by path + mtime so repeated mcpStatus()
|
|
12
12
|
// calls skip existsSync+readFileSync+JSON.parse when the file is unchanged.
|
|
13
13
|
// Invalidated automatically on any mtime change (or create/delete via mtime=0).
|
|
14
14
|
const projectMcpCache = new Map();
|
|
15
15
|
const PROJECT_MCP_CACHE_MAX = 32;
|
|
16
|
+
export function invalidateProjectMcpCache(cwd) {
|
|
17
|
+
projectMcpCache.delete(resolve(cwd || '.', '.mcp.json'));
|
|
18
|
+
}
|
|
19
|
+
function mcpDisabled() {
|
|
20
|
+
return /^(?:1|true|on|yes)$/i.test(String(process.env.MIXDOG_DISABLE_MCP || ''));
|
|
21
|
+
}
|
|
16
22
|
function cachedProjectMcpServers(cwd) {
|
|
17
23
|
const path = resolve(cwd || '.', '.mcp.json');
|
|
18
24
|
let mtimeMs = 0;
|
|
@@ -48,12 +54,23 @@ export function createMcpGlue({
|
|
|
48
54
|
// (precedence: project > user config). `sources[name]` records each server's
|
|
49
55
|
// origin ('config' | 'project') for status reporting.
|
|
50
56
|
function resolveEffectiveMcpServers() {
|
|
57
|
+
// Benchmark/embedding guard: do not even inspect project `.mcp.json`.
|
|
58
|
+
if (mcpDisabled()) return { servers: {}, sources: {} };
|
|
51
59
|
const config = getConfig();
|
|
52
60
|
const configured = config?.mcpServers && typeof config.mcpServers === 'object'
|
|
53
61
|
? config.mcpServers
|
|
54
62
|
: {};
|
|
63
|
+
const projectKey = normalizeMcpProjectPathKey(getCurrentCwd());
|
|
64
|
+
const overrides = config?.mcpProjectOverrides?.[projectKey];
|
|
65
|
+
const foldedConfigured = {};
|
|
66
|
+
for (const [name, cfg] of Object.entries(configured)) {
|
|
67
|
+
const override = overrides?.[name];
|
|
68
|
+
foldedConfigured[name] = typeof override?.enabled === 'boolean'
|
|
69
|
+
? { ...cfg, enabled: override.enabled }
|
|
70
|
+
: cfg;
|
|
71
|
+
}
|
|
55
72
|
const project = cachedProjectMcpServers(getCurrentCwd());
|
|
56
|
-
const servers = { ...
|
|
73
|
+
const servers = { ...foldedConfigured, ...project };
|
|
57
74
|
const sources = {};
|
|
58
75
|
for (const name of Object.keys(configured)) sources[name] = 'config';
|
|
59
76
|
for (const name of Object.keys(project)) sources[name] = 'project';
|
|
@@ -61,6 +78,9 @@ export function createMcpGlue({
|
|
|
61
78
|
}
|
|
62
79
|
|
|
63
80
|
function mcpStatus() {
|
|
81
|
+
if (mcpDisabled()) {
|
|
82
|
+
return { servers: [], configuredCount: 0, connectedCount: 0, failedCount: 0 };
|
|
83
|
+
}
|
|
64
84
|
const { servers: configured, sources } = resolveEffectiveMcpServers();
|
|
65
85
|
const connected = new Map((mcpClient.getMcpServerStatus?.() || []).map((row) => [row.name, row]));
|
|
66
86
|
const failures = new Map((state.mcpFailures || []).map((row) => [row.name, row]));
|
|
@@ -129,6 +149,13 @@ export function createMcpGlue({
|
|
|
129
149
|
}
|
|
130
150
|
|
|
131
151
|
async function connectConfiguredMcp({ reset = false, only = null, enabled = true } = {}) {
|
|
152
|
+
if (mcpDisabled()) {
|
|
153
|
+
++state.mcpConnectGeneration;
|
|
154
|
+
state.mcpFailures = [];
|
|
155
|
+
if (only) await mcpClient.disconnectMcpServer?.(only);
|
|
156
|
+
else await mcpClient.disconnectAll?.();
|
|
157
|
+
return mcpStatus();
|
|
158
|
+
}
|
|
132
159
|
// Scoped single-server toggle: non-superseding. It must NEVER cancel a
|
|
133
160
|
// pending full {reset} (cwd-change/boot). So do not bump the generation;
|
|
134
161
|
// just wait for any in-flight run, then bail if a newer full reset has
|
|
@@ -4,6 +4,13 @@ import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
|
4
4
|
import { clean } from './session-text.mjs';
|
|
5
5
|
import { readJsonSafe } from './fs-utils.mjs';
|
|
6
6
|
|
|
7
|
+
// Config keys must compare the same cwd spelling on read and write. Windows
|
|
8
|
+
// paths are case-insensitive, so canonicalize their resolved form to lowercase.
|
|
9
|
+
export function normalizeMcpProjectPathKey(cwd) {
|
|
10
|
+
const path = resolve(cwd || '.');
|
|
11
|
+
return process.platform === 'win32' ? path.toLowerCase() : path;
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
// Project-local MCP ingress: read `.mcp.json` from the project root and return
|
|
8
15
|
// a cleaned { name: cfg } map. Best-effort — never throws. Accepts either the
|
|
9
16
|
// standard `{ mcpServers: {...} }` shape or a bare name->cfg map. Self-ref
|
|
@@ -18,7 +18,9 @@ import {
|
|
|
18
18
|
pluginMcpEnableScript,
|
|
19
19
|
resolveContainedPluginPath,
|
|
20
20
|
setProjectMcpServerEnabled,
|
|
21
|
+
normalizeMcpProjectPathKey,
|
|
21
22
|
} from './plugin-mcp.mjs';
|
|
23
|
+
import { invalidateProjectMcpCache } from './mcp-glue.mjs';
|
|
22
24
|
|
|
23
25
|
// MCP servers, skills, plugins, hooks, and memory/recall surfaces. Extracted
|
|
24
26
|
// verbatim from the runtime API object; stateless helpers are imported directly
|
|
@@ -137,7 +139,19 @@ export function createResourceApi(deps) {
|
|
|
137
139
|
throw new Error(`MCP server not configured: ${serverName}`);
|
|
138
140
|
}
|
|
139
141
|
delete current[serverName];
|
|
140
|
-
|
|
142
|
+
const currentOverrides = nextConfig.mcpProjectOverrides && typeof nextConfig.mcpProjectOverrides === 'object'
|
|
143
|
+
? nextConfig.mcpProjectOverrides
|
|
144
|
+
: {};
|
|
145
|
+
const mcpProjectOverrides = {};
|
|
146
|
+
for (const [projectKey, serverOverrides] of Object.entries(currentOverrides)) {
|
|
147
|
+
if (!serverOverrides || typeof serverOverrides !== 'object' || Array.isArray(serverOverrides)) continue;
|
|
148
|
+
const nextServerOverrides = { ...serverOverrides };
|
|
149
|
+
delete nextServerOverrides[serverName];
|
|
150
|
+
if (Object.keys(nextServerOverrides).length > 0) {
|
|
151
|
+
mcpProjectOverrides[projectKey] = nextServerOverrides;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
saveConfigAndAdopt({ ...nextConfig, mcpServers: current, mcpProjectOverrides });
|
|
141
155
|
const status = await connectConfiguredMcp({ reset: true });
|
|
142
156
|
invalidatePreSessionToolSurface();
|
|
143
157
|
const session = getSession();
|
|
@@ -152,11 +166,13 @@ export function createResourceApi(deps) {
|
|
|
152
166
|
// A project-local `.mcp.json` entry WINS over config for this name, so the
|
|
153
167
|
// durable toggle must land in whichever file actually drives the server.
|
|
154
168
|
// For project-sourced servers, persist the `enabled` flag into `.mcp.json`
|
|
155
|
-
//
|
|
169
|
+
// then explicitly invalidate the project cache (mtime granularity is not
|
|
170
|
+
// reliable for same-tick writes), before running the same
|
|
156
171
|
// background connect/recreate chain used for config servers.
|
|
157
172
|
const shadowRow = mcpStatus().servers.find((s) => s.name === serverName);
|
|
158
173
|
if (shadowRow && shadowRow.source === 'project') {
|
|
159
174
|
setProjectMcpServerEnabled(getCurrentCwd(), serverName, want);
|
|
175
|
+
invalidateProjectMcpCache(getCurrentCwd());
|
|
160
176
|
return scheduleMcpToggle(serverName, want);
|
|
161
177
|
}
|
|
162
178
|
const nextConfig = { ...getConfig() };
|
|
@@ -166,12 +182,29 @@ export function createResourceApi(deps) {
|
|
|
166
182
|
if (!Object.prototype.hasOwnProperty.call(current, serverName)) {
|
|
167
183
|
throw new Error(`MCP server not configured: ${serverName}`);
|
|
168
184
|
}
|
|
169
|
-
//
|
|
185
|
+
// Keep the global server definition single-source; only this project's
|
|
186
|
+
// enabled override is adopted + persisted synchronously (fast), then
|
|
170
187
|
// hand the heavy connect/close/recreate to the per-server background
|
|
171
188
|
// chain. Return that chain's promise so callers can settle the picker on
|
|
172
189
|
// completion, but the store no longer blocks on it.
|
|
173
|
-
|
|
174
|
-
|
|
190
|
+
const projectKey = normalizeMcpProjectPathKey(getCurrentCwd());
|
|
191
|
+
const currentOverrides = nextConfig.mcpProjectOverrides && typeof nextConfig.mcpProjectOverrides === 'object'
|
|
192
|
+
? nextConfig.mcpProjectOverrides
|
|
193
|
+
: {};
|
|
194
|
+
const projectOverrides = currentOverrides[projectKey] && typeof currentOverrides[projectKey] === 'object'
|
|
195
|
+
? currentOverrides[projectKey]
|
|
196
|
+
: {};
|
|
197
|
+
cfgMod.markMcpProjectOverrideDirty(projectKey, serverName, want);
|
|
198
|
+
saveConfigAndAdopt({
|
|
199
|
+
...nextConfig,
|
|
200
|
+
mcpProjectOverrides: {
|
|
201
|
+
...currentOverrides,
|
|
202
|
+
[projectKey]: {
|
|
203
|
+
...projectOverrides,
|
|
204
|
+
[serverName]: { enabled: want },
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
});
|
|
175
208
|
return scheduleMcpToggle(serverName, want);
|
|
176
209
|
},
|
|
177
210
|
skillsStatus() {
|
|
@@ -1051,7 +1051,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1051
1051
|
const toolsStartedAt = performance.now();
|
|
1052
1052
|
const standaloneTools = [
|
|
1053
1053
|
TOOL_SEARCH_TOOL,
|
|
1054
|
-
SKILL_TOOL,
|
|
1054
|
+
...(envFlag('MIXDOG_DISABLE_SKILLS') ? [] : [SKILL_TOOL]),
|
|
1055
1055
|
CWD_TOOL,
|
|
1056
1056
|
SESSION_MANAGE_TOOL,
|
|
1057
1057
|
EXPLORE_TOOL,
|
|
@@ -1141,6 +1141,10 @@ export async function createMixdogSessionRuntime({
|
|
|
1141
1141
|
routingSessionId: callerSessionId,
|
|
1142
1142
|
clientHostPid: callerCtx?.clientHostPid || session?.clientHostPid || process.pid,
|
|
1143
1143
|
notifyFn: notifyFnForSession(callerSessionId),
|
|
1144
|
+
// Cascade caller cancellation (ESC / owner abort) into the explore
|
|
1145
|
+
// fan-out so every child dispatch tears down immediately — same
|
|
1146
|
+
// signal the adjacent native search already forwards.
|
|
1147
|
+
signal: callerCtx?.signal || session?.controller?.signal,
|
|
1144
1148
|
});
|
|
1145
1149
|
}
|
|
1146
1150
|
if (name === 'cwd') {
|
|
@@ -11,6 +11,18 @@ import {
|
|
|
11
11
|
} from './tool-catalog.mjs';
|
|
12
12
|
import { getMcpTools } from '../runtime/agent/orchestrator/mcp/client.mjs';
|
|
13
13
|
|
|
14
|
+
export function splitToolStatusCounts(rows) {
|
|
15
|
+
const list = Array.isArray(rows) ? rows : [];
|
|
16
|
+
const regular = list.filter((row) => row?.kind !== 'mcp' && row?.kind !== 'skill');
|
|
17
|
+
const mcp = list.filter((row) => row?.kind === 'mcp');
|
|
18
|
+
return {
|
|
19
|
+
count: regular.length,
|
|
20
|
+
activeCount: regular.filter((row) => row.active).length,
|
|
21
|
+
mcpToolCount: mcp.length,
|
|
22
|
+
activeMcpToolCount: mcp.filter((row) => row.active).length,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
14
26
|
// Turn execution (ask) + session-manage/tool-surface/agent surfaces. Extracted
|
|
15
27
|
// verbatim from the runtime API object; stateless helpers are imported directly
|
|
16
28
|
// and the runtime injects live getters/setters for the mutable session/mode/
|
|
@@ -311,13 +323,13 @@ export function createSessionTurnApi(deps) {
|
|
|
311
323
|
const activeNames = new Set((surface?.tools || []).map((tool) => tool?.name).filter(Boolean));
|
|
312
324
|
const needle = clean(query).toLowerCase();
|
|
313
325
|
const rows = catalog.map((tool) => toolRow(tool, activeNames)).filter((row) => row.name);
|
|
326
|
+
const counts = splitToolStatusCounts(rows);
|
|
314
327
|
const tools = needle
|
|
315
328
|
? rows.filter((row) => toolSearchMatches(row, needle))
|
|
316
329
|
: rows;
|
|
317
330
|
return {
|
|
318
331
|
mode: getMode(),
|
|
319
|
-
|
|
320
|
-
activeCount: rows.filter((row) => row.active).length,
|
|
332
|
+
...counts,
|
|
321
333
|
tools,
|
|
322
334
|
activeTools: sortedNamesByMeasuredUsage(activeNames),
|
|
323
335
|
discoveredTools: sortedNamesByMeasuredUsage(surface?.deferredDiscoveredTools || []),
|
|
@@ -189,6 +189,11 @@ export function createSettingsApi({
|
|
|
189
189
|
next.type = compactType;
|
|
190
190
|
next.compactType = compactType;
|
|
191
191
|
}
|
|
192
|
+
// These controls apply only to main/user recall-fasttrack sessions;
|
|
193
|
+
// agent-owned semantic sessions retain their existing `buffer*` policy.
|
|
194
|
+
for (const key of ['mainBufferTokens', 'mainBuffer', 'mainBufferPercent', 'mainBufferPct', 'mainBufferRatio', 'mainBufferFraction']) {
|
|
195
|
+
if (hasOwn(input, key)) next[key] = input[key];
|
|
196
|
+
}
|
|
192
197
|
const nextConfig = { ...config };
|
|
193
198
|
nextConfig.compaction = normalizeCompactionConfig(next, { memoryEnabled: memoryEnabled() });
|
|
194
199
|
saveConfigAndAdopt(nextConfig);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// tool_search ranking + auto-selection, and the session tool-surface
|
|
3
3
|
// application/selection logic. Pure module (session objects passed in).
|
|
4
4
|
import { clean, LATE_TOOL_ANNOUNCEMENT_SENTINEL } from './session-text.mjs';
|
|
5
|
-
import { estimateToolSchemaTokens } from '../runtime/agent/orchestrator/session/context-utils.mjs';
|
|
5
|
+
import { estimateToolSchemaTokens, toolSchemaSignature } from '../runtime/agent/orchestrator/session/context-utils.mjs';
|
|
6
6
|
import { applyInitialDeferredToolManifestToBp1, buildDeferredToolManifest } from '../runtime/agent/orchestrator/context/collect.mjs';
|
|
7
7
|
import { getMcpServerInstructionsMap } from '../runtime/agent/orchestrator/mcp/client.mjs';
|
|
8
8
|
import {
|
|
@@ -40,7 +40,12 @@ function sameToolSchemaEntries(cached, tools) {
|
|
|
40
40
|
|| entry.inputSchema !== tool?.inputSchema
|
|
41
41
|
|| entry.input_schema !== tool?.input_schema
|
|
42
42
|
|| entry.parameters !== tool?.parameters
|
|
43
|
-
|| entry.schema !== tool?.schema
|
|
43
|
+
|| entry.schema !== tool?.schema
|
|
44
|
+
|| entry.deferLoading !== tool?.deferLoading
|
|
45
|
+
|| entry.defer_loading !== tool?.defer_loading
|
|
46
|
+
|| entry.annotationsMixdogKind !== tool?.annotations?.mixdogKind
|
|
47
|
+
|| entry.annotationsAgentHidden !== tool?.annotations?.agentHidden
|
|
48
|
+
|| entry.wireSignature !== toolSchemaSignature([tool])) return false;
|
|
44
49
|
}
|
|
45
50
|
return true;
|
|
46
51
|
}
|
|
@@ -54,6 +59,11 @@ function toolSchemaEntry(tool) {
|
|
|
54
59
|
input_schema: tool?.input_schema,
|
|
55
60
|
parameters: tool?.parameters,
|
|
56
61
|
schema: tool?.schema,
|
|
62
|
+
deferLoading: tool?.deferLoading,
|
|
63
|
+
defer_loading: tool?.defer_loading,
|
|
64
|
+
annotationsMixdogKind: tool?.annotations?.mixdogKind,
|
|
65
|
+
annotationsAgentHidden: tool?.annotations?.agentHidden,
|
|
66
|
+
wireSignature: toolSchemaSignature([tool]),
|
|
57
67
|
};
|
|
58
68
|
}
|
|
59
69
|
export const DEFERRED_DEFAULT_FULL_TOOLS = Object.freeze([
|
|
@@ -143,6 +153,7 @@ export function toolSchemaBucket(tool) {
|
|
|
143
153
|
const kind = toolKind(tool);
|
|
144
154
|
if (kind === 'mcp') return 'mcp';
|
|
145
155
|
if (kind === 'skill') return 'skills';
|
|
156
|
+
if (kind === 'control') return 'control';
|
|
146
157
|
if (name === 'memory' || name === 'recall' || name.includes('memory')) return 'memory';
|
|
147
158
|
if (name === 'search' || name === 'web_fetch') return 'web';
|
|
148
159
|
if (['read', 'grep', 'find', 'glob', 'list', 'code_graph', 'explore'].includes(name)) return 'code';
|
|
@@ -13,13 +13,11 @@ export const TOOL_SEARCH_TOOL = {
|
|
|
13
13
|
openWorldHint: false,
|
|
14
14
|
agentHidden: true,
|
|
15
15
|
},
|
|
16
|
-
description: '
|
|
16
|
+
description: 'Load deferred tools by exact name via names[]; reports loaded/already-active/missing plus MCP servers still connecting or failed. Deferred tools also auto-load when called directly.',
|
|
17
17
|
inputSchema: {
|
|
18
18
|
type: 'object',
|
|
19
19
|
properties: {
|
|
20
20
|
names: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], description: 'Exact deferred tool names/aliases to load.' },
|
|
21
|
-
select: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], description: 'Legacy alias for names (accepts "select:a,b").' },
|
|
22
|
-
query: { type: 'string', description: 'Legacy: only "select:a,b" is honored (mapped to names); free-text keyword queries are rejected — this tool does not search.' },
|
|
23
21
|
},
|
|
24
22
|
additionalProperties: false,
|
|
25
23
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
agentWatchdogPolicyActive,
|
|
3
3
|
evaluateAgentWatchdogAbort,
|
|
4
|
+
resolveEffectiveToolRunningCeilingMs,
|
|
4
5
|
} from '../runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs';
|
|
5
6
|
|
|
6
7
|
const ACTIVE_RUNTIME_STAGES = new Set([
|
|
@@ -18,10 +19,26 @@ function positiveSeconds(now, ts) {
|
|
|
18
19
|
return Math.max(0, Math.floor((now - n) / 1000));
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export function formatAgentWatchdogSummary(policy) {
|
|
22
|
+
export function formatAgentWatchdogSummary(policy, snapshot = null) {
|
|
22
23
|
if (!policy || !agentWatchdogPolicyActive(policy)) return null;
|
|
24
|
+
const transportMs = policy.firstTransportMs ?? policy.firstResponseMs ?? 0;
|
|
25
|
+
const semanticMs = policy.firstSemanticMs ?? policy.firstVisibleCeilingMs ?? 0;
|
|
26
|
+
if (snapshot) {
|
|
27
|
+
if (snapshot.waitingForTransport && transportMs > 0) {
|
|
28
|
+
return `armed transport=${Math.round(transportMs / 1000)}s`;
|
|
29
|
+
}
|
|
30
|
+
if ((snapshot.waitingForFirstSemantic ?? snapshot.waitingForFirstActivity) && semanticMs > 0) {
|
|
31
|
+
return `armed semantic=${Math.round(semanticMs / 1000)}s`;
|
|
32
|
+
}
|
|
33
|
+
if (snapshot.stage === 'tool_running' && policy.toolRunningMs > 0) {
|
|
34
|
+
const effectiveMs = resolveEffectiveToolRunningCeilingMs(snapshot, policy);
|
|
35
|
+
return `armed tool=${Math.round(effectiveMs / 1000)}s`;
|
|
36
|
+
}
|
|
37
|
+
if (policy.idleStaleMs > 0) return `armed idle=${Math.round(policy.idleStaleMs / 1000)}s`;
|
|
38
|
+
}
|
|
23
39
|
const parts = [];
|
|
24
|
-
if (
|
|
40
|
+
if (transportMs > 0) parts.push(`transport=${Math.round(transportMs / 1000)}s`);
|
|
41
|
+
if (semanticMs > 0) parts.push(`semantic=${Math.round(semanticMs / 1000)}s`);
|
|
25
42
|
if (policy.idleStaleMs > 0) parts.push(`idle=${Math.round(policy.idleStaleMs / 1000)}s`);
|
|
26
43
|
if (policy.toolRunningMs > 0) parts.push(`tool=${Math.round(policy.toolRunningMs / 1000)}s`);
|
|
27
44
|
return parts.length ? `armed ${parts.join(' ')}` : null;
|
|
@@ -55,7 +72,7 @@ export function buildAgentTaskProgressFields({
|
|
|
55
72
|
const stage = cleanStage(runtimeStage || snapshot?.stage || sessionStatus || 'unknown');
|
|
56
73
|
const workerStage = stage;
|
|
57
74
|
const silentFor = resolveSilentForSeconds(now, snapshot, runtime);
|
|
58
|
-
const watchdog = formatAgentWatchdogSummary(policy);
|
|
75
|
+
const watchdog = formatAgentWatchdogSummary(policy, snapshot);
|
|
59
76
|
const queued = Number.isFinite(Number(queuedFollowups)) && Number(queuedFollowups) > 0
|
|
60
77
|
? Math.floor(Number(queuedFollowups))
|
|
61
78
|
: null;
|
|
@@ -96,13 +113,24 @@ function cleanStage(value) {
|
|
|
96
113
|
}
|
|
97
114
|
|
|
98
115
|
function describeLastProgress({ stage, snapshot, runtime, silentFor, lastToolCall, now }) {
|
|
99
|
-
if (snapshot?.
|
|
116
|
+
if (snapshot?.waitingForTransport) return 'awaiting model transport';
|
|
117
|
+
if (snapshot?.waitingForFirstSemantic ?? snapshot?.waitingForFirstActivity) {
|
|
118
|
+
return 'transport active; awaiting first model event';
|
|
119
|
+
}
|
|
100
120
|
if (stage === 'tool_running') {
|
|
101
121
|
const tool = cleanStage(lastToolCall);
|
|
102
122
|
return tool && tool !== 'unknown' ? `tool: ${tool}` : 'tool running';
|
|
103
123
|
}
|
|
104
124
|
if (stage === 'connecting' || stage === 'requesting') return 'connecting to model';
|
|
105
125
|
if (stage === 'streaming') {
|
|
126
|
+
if (snapshot?.lastSemanticKind === 'tool') return 'tool protocol progress';
|
|
127
|
+
if (snapshot?.lastSemanticKind === 'text') return 'visible model text';
|
|
128
|
+
if (snapshot?.lastSemanticKind === 'reasoning') {
|
|
129
|
+
return snapshot?.lastVisibleTextAt
|
|
130
|
+
? 'model reasoning (hidden; visible output previously emitted)'
|
|
131
|
+
: 'model reasoning (hidden; no visible output yet)';
|
|
132
|
+
}
|
|
133
|
+
if (snapshot?.hasFirstSemantic) return 'model active (no visible output yet)';
|
|
106
134
|
const streamSilent = positiveSeconds(
|
|
107
135
|
now,
|
|
108
136
|
snapshot?.lastStreamDeltaAt || runtime?.lastStreamDeltaAt || 0,
|
|
@@ -149,9 +177,25 @@ function describeAgentDiagnostic({
|
|
|
149
177
|
return `idle, ${queued} follow-up${queued === 1 ? '' : 's'} queued`;
|
|
150
178
|
}
|
|
151
179
|
|
|
152
|
-
if (
|
|
180
|
+
if (policy) {
|
|
181
|
+
const abortErr = evaluateAgentWatchdogAbort(snapshot, now, policy);
|
|
182
|
+
if (abortErr) return `stale: ${abortErr.message.replace(/^agent /i, '')}`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (snapshot?.waitingForTransport) return 'waiting for model transport';
|
|
186
|
+
if (snapshot?.waitingForFirstSemantic ?? snapshot?.waitingForFirstActivity) {
|
|
187
|
+
return 'transport healthy; waiting for first semantic model event';
|
|
188
|
+
}
|
|
153
189
|
|
|
154
190
|
if (stage === 'streaming') {
|
|
191
|
+
if (snapshot?.lastSemanticKind === 'tool') return 'tool protocol active';
|
|
192
|
+
if (snapshot?.lastSemanticKind === 'text') return 'visible text streaming';
|
|
193
|
+
if (snapshot?.lastSemanticKind === 'reasoning') {
|
|
194
|
+
return snapshot?.lastVisibleTextAt
|
|
195
|
+
? 'hidden reasoning active; visible output previously emitted'
|
|
196
|
+
: 'hidden reasoning active; no visible output yet';
|
|
197
|
+
}
|
|
198
|
+
if (snapshot?.hasFirstSemantic) return 'model active; no visible output yet';
|
|
155
199
|
const streamSilent = positiveSeconds(
|
|
156
200
|
now,
|
|
157
201
|
snapshot?.lastStreamDeltaAt || 0,
|
|
@@ -171,12 +215,7 @@ function describeAgentDiagnostic({
|
|
|
171
215
|
now,
|
|
172
216
|
policy,
|
|
173
217
|
);
|
|
174
|
-
if (abortErr) {
|
|
175
|
-
return `stale: ${abortErr.message.replace(/^agent /i, '')}`;
|
|
176
|
-
}
|
|
177
|
-
if (silentFor >= 30) {
|
|
178
|
-
return `stale: no stream/tool progress for ${silentFor}s`;
|
|
179
|
-
}
|
|
218
|
+
if (abortErr) return `stale: ${abortErr.message.replace(/^agent /i, '')}`;
|
|
180
219
|
}
|
|
181
220
|
|
|
182
221
|
if (normalizedTask === 'running' && (stage === 'idle' || stage === 'done')) {
|
|
@@ -35,7 +35,7 @@ export const AGENT_TOOL = {
|
|
|
35
35
|
openWorldHint: true,
|
|
36
36
|
agentHidden: true,
|
|
37
37
|
},
|
|
38
|
-
description: 'Delegate scoped work
|
|
38
|
+
description: 'Delegate scoped work; handoffs always start background tasks (task ids return immediately). Distinct tags for independent scopes; spawn/send with the same tag reuses the live session for the same scope. Wait for the completion notification; do not call status/read after spawn (manual recovery only).',
|
|
39
39
|
inputSchema: {
|
|
40
40
|
type: 'object',
|
|
41
41
|
properties: {
|