mixdog 0.9.151 → 0.9.152
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 +110 -68
- package/package.json +3 -2
- package/scripts/tool-stress.mjs +0 -2
- package/src/defaults/skills/setup/SKILL.md +18 -27
- package/src/headless-exec.mjs +3 -10
- package/src/headless-exec.test.mjs +2 -7
- package/src/runtime/agent/orchestrator/config.mjs +6 -66
- package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +153 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +49 -45
- package/src/runtime/agent/orchestrator/mcp/client.mjs +242 -15
- package/src/runtime/agent/orchestrator/mcp/features.test.mjs +66 -0
- package/src/runtime/agent/orchestrator/mcp/security.test.mjs +36 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +54 -33
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +3 -8
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +10 -2
- package/src/runtime/agent/orchestrator/providers/lib/provider-replay.mjs +50 -0
- package/src/runtime/agent/orchestrator/providers/lib/sse-framing.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +3 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +35 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +22 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +0 -2
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +4 -7
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +235 -0
- package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +719 -0
- package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +399 -39
- package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +26 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +24 -1
- package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +161 -0
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +31 -18
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +556 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +457 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +105 -186
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +115 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +14 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -10
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +30 -14
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +15 -4
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +73 -15
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +76 -2
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +24 -683
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +64 -72
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +2 -5
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.mjs +75 -0
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +24 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +24 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +12 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +35 -21
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +28 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +54 -0
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
- package/src/runtime/browser-bridge/client.mjs +103 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +42 -0
- package/src/runtime/computer-bridge/client.mjs +101 -0
- package/src/runtime/computer-bridge/tool-defs.mjs +53 -0
- package/src/runtime/media/store.mjs +18 -8
- package/src/runtime/media/store.test.mjs +14 -0
- package/src/runtime/memory/index.mjs +1 -1
- package/src/runtime/memory/lib/transcript-ingest.mjs +8 -6
- package/src/runtime/memory/lib/transcript-ingest.test.mjs +52 -0
- package/src/runtime/shared/child-spawn-gate.mjs +17 -0
- package/src/runtime/shared/child-spawn-remote.mjs +69 -7
- package/src/runtime/shared/child-spawn-remote.test.mjs +124 -0
- package/src/runtime/shared/pristine-execution-contract.json +2 -1
- package/src/runtime/shared/provider-api-key.mjs +1 -0
- package/src/runtime/shared/session-runtime-health.mjs +91 -0
- package/src/runtime/shared/session-runtime-health.test.mjs +53 -0
- package/src/runtime/shared/skill-document.mjs +86 -0
- package/src/runtime/shared/skill-document.test.mjs +81 -0
- package/src/runtime/shared/tool-surface.mjs +18 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +1 -0
- package/src/runtime/shared/turn-snapshot.mjs +32 -9
- package/src/runtime/shared/user-cwd.mjs +33 -14
- package/src/runtime/shared/user-cwd.test.mjs +43 -0
- package/src/session-runtime/cwd-plugins.mjs +26 -26
- package/src/session-runtime/cwd-plugins.test.mjs +75 -0
- package/src/session-runtime/cwd-tool-routing.test.mjs +5 -0
- package/src/session-runtime/global-extensions.mjs +26 -0
- package/src/session-runtime/global-extensions.test.mjs +28 -0
- package/src/session-runtime/goal-runtime.mjs +721 -0
- package/src/session-runtime/goal-runtime.test.mjs +108 -0
- package/src/session-runtime/lifecycle-api.mjs +18 -4
- package/src/session-runtime/lifecycle-api.test.mjs +21 -0
- package/src/session-runtime/mcp-glue.mjs +49 -47
- package/src/session-runtime/plugin-mcp-standard.test.mjs +177 -0
- package/src/session-runtime/plugin-mcp.mjs +96 -22
- package/src/session-runtime/prewarm.mjs +1 -10
- package/src/session-runtime/resource-api-global.test.mjs +69 -0
- package/src/session-runtime/resource-api.mjs +173 -58
- package/src/session-runtime/runtime-core.mjs +148 -20
- package/src/session-runtime/runtime-tunables.mjs +6 -8
- package/src/session-runtime/session-turn-api.mjs +22 -11
- package/src/session-runtime/skills-api.mjs +79 -25
- package/src/session-runtime/skills-api.test.mjs +99 -0
- package/src/session-runtime/tool-catalog-schema.mjs +0 -5
- package/src/session-runtime/tool-catalog.mjs +4 -13
- package/src/session-runtime/tool-policy-surface.test.mjs +33 -11
- package/src/session-runtime/tool-surface.mjs +12 -0
- package/src/standalone/agent-dispatch-broker.mjs +5 -9
- package/src/standalone/agent-tool/notify.mjs +6 -8
- package/src/standalone/agent-tool/notify.test.mjs +37 -0
- package/src/standalone/daemon.mjs +31 -15
- package/src/standalone/hook-bus/config.mjs +1 -0
- package/src/standalone/plugin-admin.mjs +17 -0
- package/src/standalone/plugin-admin.test.mjs +7 -0
- package/src/standalone/provider-admin.mjs +1 -0
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-runtime-agent-control-client.mjs +133 -0
- package/src/standalone/session-runtime-agent-control-client.test.mjs +71 -0
- package/src/standalone/session-runtime-dispatch-cancel.test.mjs +139 -0
- package/src/standalone/session-runtime-host-factory.mjs +17 -0
- package/src/standalone/session-runtime-host-factory.test.mjs +31 -0
- package/src/standalone/session-runtime-host-health.test.mjs +5 -0
- package/src/standalone/session-runtime-host.mjs +696 -58
- package/src/standalone/session-runtime-inline-host.mjs +351 -0
- package/src/standalone/session-runtime-inline-host.test.mjs +83 -0
- package/src/standalone/session-runtime-provider-cooldown.mjs +78 -0
- package/src/standalone/session-runtime-provider-cooldown.test.mjs +80 -0
- package/src/standalone/session-runtime-shard-host.test.mjs +569 -0
- package/src/standalone/session-runtime-shard-router.mjs +149 -0
- package/src/standalone/session-runtime-shard-router.test.mjs +101 -0
- package/src/standalone/session-runtime-worker.mjs +476 -18
- package/src/standalone/session-service.mjs +81 -6
- package/src/tui/app/app-view.jsx +1 -0
- package/src/tui/app/core-memory-picker.mjs +2 -0
- package/src/tui/app/extension-pickers.mjs +1 -1
- package/src/tui/app/maintenance-pickers.mjs +3 -3
- package/src/tui/app/model-options.mjs +2 -0
- package/src/tui/app/model-picker.mjs +14 -4
- package/src/tui/app/panel-handoff.test.mjs +311 -0
- package/src/tui/app/project-picker.mjs +1 -0
- package/src/tui/app/route-pickers.mjs +21 -6
- package/src/tui/app/settings-picker.mjs +26 -3
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch.mjs +73 -22
- package/src/tui/app/theme-effort-pickers.mjs +7 -2
- package/src/tui/components/Picker.jsx +10 -1
- package/src/tui/dist/index.mjs +163 -44
- package/src/tui/session/context-state.mjs +1 -0
- package/src/tui/session/goal-continuation.mjs +110 -0
- package/src/tui/session/goal-continuation.test.mjs +84 -0
- package/src/tui/session/queue-helpers.mjs +5 -2
- package/src/tui/session/session-action-surface.test.mjs +108 -0
- package/src/tui/session/session-api-ext.mjs +39 -8
- package/src/tui/session/session-api.mjs +58 -0
- package/src/tui/session/session-flow.mjs +6 -0
- package/src/tui/session/turn.mjs +1 -0
- package/src/tui/session-local.mjs +15 -1
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +0 -301
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
mkdtempSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
rmSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from 'node:fs';
|
|
10
|
+
import { tmpdir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import test from 'node:test';
|
|
13
|
+
|
|
14
|
+
import * as contextMod from '../runtime/agent/orchestrator/context/collect.mjs';
|
|
15
|
+
import { parseSkillDocument } from '../runtime/shared/skill-document.mjs';
|
|
16
|
+
import { createSkillsApi } from './skills-api.mjs';
|
|
17
|
+
|
|
18
|
+
test('edits existing skills without dropping optional metadata or sibling resources', () => {
|
|
19
|
+
const root = mkdtempSync(join(tmpdir(), 'mixdog-skill-edit-'));
|
|
20
|
+
const previousDataDir = process.env.MIXDOG_DATA_DIR;
|
|
21
|
+
process.env.MIXDOG_DATA_DIR = join(root, 'data');
|
|
22
|
+
try {
|
|
23
|
+
const cwd = join(root, 'project');
|
|
24
|
+
const originalDir = join(process.env.MIXDOG_DATA_DIR, 'skills', 'existing-skill');
|
|
25
|
+
mkdirSync(join(originalDir, 'scripts'), { recursive: true });
|
|
26
|
+
writeFileSync(join(originalDir, 'SKILL.md'), [
|
|
27
|
+
'---',
|
|
28
|
+
'name: existing-skill',
|
|
29
|
+
'description: Use when editing an existing skill.',
|
|
30
|
+
'license: MIT',
|
|
31
|
+
'metadata:',
|
|
32
|
+
' author: example',
|
|
33
|
+
'allowed-tools:',
|
|
34
|
+
' - shell',
|
|
35
|
+
'---',
|
|
36
|
+
'',
|
|
37
|
+
'# Original instructions',
|
|
38
|
+
'',
|
|
39
|
+
].join('\n'));
|
|
40
|
+
writeFileSync(join(originalDir, 'scripts', 'check.py'), 'print("ok")\n');
|
|
41
|
+
|
|
42
|
+
const api = createSkillsApi({ contextMod, getCwd: () => cwd });
|
|
43
|
+
const saved = api.saveSkillDocument({
|
|
44
|
+
originalName: 'existing-skill',
|
|
45
|
+
name: 'renamed-skill',
|
|
46
|
+
description: 'Use when the renamed skill is needed.',
|
|
47
|
+
instructions: '# Updated instructions',
|
|
48
|
+
});
|
|
49
|
+
const renamedDir = join(process.env.MIXDOG_DATA_DIR, 'skills', 'renamed-skill');
|
|
50
|
+
const parsed = parseSkillDocument(readFileSync(saved.filePath, 'utf8'));
|
|
51
|
+
|
|
52
|
+
assert.equal(existsSync(originalDir), false);
|
|
53
|
+
assert.equal(saved.filePath, join(renamedDir, 'SKILL.md'));
|
|
54
|
+
assert.equal(existsSync(join(renamedDir, 'scripts', 'check.py')), true);
|
|
55
|
+
assert.equal(parsed.name, 'renamed-skill');
|
|
56
|
+
assert.equal(parsed.description, 'Use when the renamed skill is needed.');
|
|
57
|
+
assert.equal(parsed.body, '# Updated instructions\n');
|
|
58
|
+
assert.equal(parsed.frontmatter.license, 'MIT');
|
|
59
|
+
assert.deepEqual(parsed.frontmatter.metadata, { author: 'example' });
|
|
60
|
+
assert.deepEqual(parsed.frontmatter['allowed-tools'], ['shell']);
|
|
61
|
+
assert.ok(api.skillsStatus().skills.some((skill) => skill.name === 'renamed-skill'));
|
|
62
|
+
} finally {
|
|
63
|
+
contextMod.invalidateSkillsCache(join(root, 'project'));
|
|
64
|
+
if (previousDataDir === undefined) delete process.env.MIXDOG_DATA_DIR;
|
|
65
|
+
else process.env.MIXDOG_DATA_DIR = previousDataDir;
|
|
66
|
+
rmSync(root, { recursive: true, force: true });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('creates new global skills with all three standard fields', () => {
|
|
71
|
+
const root = mkdtempSync(join(tmpdir(), 'mixdog-skill-create-'));
|
|
72
|
+
const previousDataDir = process.env.MIXDOG_DATA_DIR;
|
|
73
|
+
process.env.MIXDOG_DATA_DIR = join(root, 'data');
|
|
74
|
+
try {
|
|
75
|
+
const cwd = join(root, 'project');
|
|
76
|
+
const api = createSkillsApi({ contextMod, getCwd: () => cwd });
|
|
77
|
+
const created = api.addGlobalSkill({
|
|
78
|
+
name: 'new-skill',
|
|
79
|
+
description: 'Use when creating a standard skill.',
|
|
80
|
+
instructions: '# Instructions\n\nDo the work.',
|
|
81
|
+
});
|
|
82
|
+
const parsed = parseSkillDocument(readFileSync(created.filePath, 'utf8'));
|
|
83
|
+
|
|
84
|
+
assert.equal(parsed.name, 'new-skill');
|
|
85
|
+
assert.equal(parsed.description, 'Use when creating a standard skill.');
|
|
86
|
+
assert.equal(parsed.body, '# Instructions\n\nDo the work.\n');
|
|
87
|
+
assert.equal(created.filePath, join(process.env.MIXDOG_DATA_DIR, 'skills', 'new-skill', 'SKILL.md'));
|
|
88
|
+
assert.throws(() => api.addGlobalSkill({
|
|
89
|
+
name: 'Invalid_Name',
|
|
90
|
+
description: 'Use when invalid.',
|
|
91
|
+
instructions: '# Instructions',
|
|
92
|
+
}), /lowercase letters/);
|
|
93
|
+
} finally {
|
|
94
|
+
contextMod.invalidateSkillsCache(join(root, 'project'));
|
|
95
|
+
if (previousDataDir === undefined) delete process.env.MIXDOG_DATA_DIR;
|
|
96
|
+
else process.env.MIXDOG_DATA_DIR = previousDataDir;
|
|
97
|
+
rmSync(root, { recursive: true, force: true });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
@@ -141,11 +141,6 @@ export function parseToolSelection(value) {
|
|
|
141
141
|
.filter(Boolean);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
export function parseToolSearchQuerySelection(query) {
|
|
145
|
-
const match = clean(query).match(/^select\s*:\s*(.+)$/i);
|
|
146
|
-
return match ? parseToolSelection(match[1]) : [];
|
|
147
|
-
}
|
|
148
|
-
|
|
149
144
|
export function routeToolRank(name) {
|
|
150
145
|
const index = ROUTE_TOOL_ORDER.indexOf(clean(name));
|
|
151
146
|
return index === -1 ? Number.MAX_SAFE_INTEGER : index;
|
|
@@ -25,9 +25,9 @@ import {
|
|
|
25
25
|
MEASURED_TOOL_USAGE,
|
|
26
26
|
READONLY_TOOL_NAMES,
|
|
27
27
|
} from './tool-catalog-data.mjs';
|
|
28
|
-
import { toolKind, measuredToolUsage, parseToolSelection,
|
|
28
|
+
import { toolKind, measuredToolUsage, parseToolSelection, routeToolRank, sortedCatalogByMeasuredUsage, activeToolForSurface, deferredProviderMode, nativeProviderFamily } from './tool-catalog-schema.mjs';
|
|
29
29
|
import { filterModelEditTools } from '../runtime/shared/edit-tool-dialect.mjs';
|
|
30
|
-
export { toolKind, toolSchemaBucket, estimateToolSchemaBreakdown, measuredToolUsage, parseToolSelection,
|
|
30
|
+
export { toolKind, toolSchemaBucket, estimateToolSchemaBreakdown, measuredToolUsage, parseToolSelection, sortedCatalogByMeasuredUsage } from './tool-catalog-schema.mjs';
|
|
31
31
|
export { resolveProviderRequestTools, snapshotProviderRequestTools } from './provider-request-snapshot.mjs';
|
|
32
32
|
export {
|
|
33
33
|
DEFERRED_DEFAULT_FULL_TOOLS,
|
|
@@ -686,18 +686,9 @@ export function selectDeferredTools(session, names, mode) {
|
|
|
686
686
|
return { added, already, blocked, missing, native };
|
|
687
687
|
}
|
|
688
688
|
|
|
689
|
-
// Collect the exact deferred-tool names
|
|
690
|
-
// names[] → primary loader input
|
|
691
|
-
// select / "select:" → legacy alias (parseToolSelection strips the prefix)
|
|
692
|
-
// query "select:a,b" → legacy query-side loader
|
|
693
|
-
// A free-text `query` is NOT a search anymore: it yields no names, and the
|
|
694
|
-
// caller is steered back to names[].
|
|
689
|
+
// Collect the exact deferred-tool names requested by the loader.
|
|
695
690
|
function parseLoadToolNames(args = {}) {
|
|
696
|
-
|
|
697
|
-
if (fromNames.length) return fromNames;
|
|
698
|
-
const fromSelect = parseToolSelection(args.select);
|
|
699
|
-
if (fromSelect.length) return fromSelect;
|
|
700
|
-
return parseToolSearchQuerySelection(args.query);
|
|
691
|
+
return parseToolSelection(args.names);
|
|
701
692
|
}
|
|
702
693
|
|
|
703
694
|
// Split live MCP servers into "still connecting" (pending) and "failed" so the
|
|
@@ -121,17 +121,15 @@ test('shared tool rules keep workflow and shell-boundary anchors', () => {
|
|
|
121
121
|
// Advisory drift check: update these anchors when the rule text
|
|
122
122
|
// intentionally changes.
|
|
123
123
|
const full = buildSharedToolContent({ PLUGIN_ROOT: join(process.cwd(), 'src') });
|
|
124
|
-
assert.match(full, /
|
|
125
|
-
assert.match(full, /
|
|
126
|
-
assert.match(full, /
|
|
127
|
-
assert.match(full, /
|
|
128
|
-
assert.match(full, /
|
|
124
|
+
assert.match(full, /Determine the required outcome and missing evidence/i);
|
|
125
|
+
assert.match(full, /Before exploration or implementation, consult prior work, current external\s+information, or repository state only when needed/i);
|
|
126
|
+
assert.match(full, /Minimize tool turns by batching only calls that are independently necessary/i);
|
|
127
|
+
assert.match(full, /A call whose necessity or scope can change after\s+another result waits for that result/i);
|
|
128
|
+
assert.match(full, /Respect tool\/schema limits, never omit required fanout/i);
|
|
129
129
|
assert.match(full, /A check runs at the strictness the task requires; never raise a tool's own\s+severity beyond it/i);
|
|
130
|
-
assert.match(full, /Cost is counted in\s+rounds, not calls/i);
|
|
131
|
-
assert.match(full, /defer a call only when its target or arguments require an\s+earlier result/i);
|
|
132
130
|
assert.match(full, /Route each evidence facet once to its primary owner/i);
|
|
133
|
-
assert.match(full, /enumeration is not a prerequisite
|
|
134
|
-
assert.match(full, /apply\s+
|
|
131
|
+
assert.match(full, /A summary,\s+overview, or enumeration is not a prerequisite to an operation whose complete\s+inputs are already known/i);
|
|
132
|
+
assert.match(full, /apply one analysis\s+to many targets as one parameterized call/i);
|
|
135
133
|
assert.match(full, /Evidence or artifacts available only through program execution, calculation,\s+data transformation, generated output, or unsupported-format decoding→`shell`/i);
|
|
136
134
|
assert.match(full, /an already-open shell is never a routing reason/i);
|
|
137
135
|
assert.match(full, /Route the missing evidence to its primary owner/i);
|
|
@@ -144,9 +142,9 @@ test('shared tool rules keep workflow and shell-boundary anchors', () => {
|
|
|
144
142
|
assert.match(full, /literal, regex, or text location→`grep`;\s+known-file content, range, or image→`read`/i);
|
|
145
143
|
assert.match(full, /is never re-found, re-derived, or\s+re-verified at any granularity/i);
|
|
146
144
|
assert.match(full, /Mine each returned result fully before opening the next round/i);
|
|
147
|
-
assert.match(full, /
|
|
145
|
+
assert.match(full, /Stop exploring once sufficient evidence determines the next action required\s+by the request/i);
|
|
148
146
|
assert.match(full, /Enter Verification only after all planned work is complete/i);
|
|
149
|
-
assert.match(full, /
|
|
147
|
+
assert.match(full, /Use an umbrella suite only when explicitly requested or required by the\s+documented project or release process/i);
|
|
150
148
|
assert.match(full, /If verification fails, collect all failures, leave Verification/i);
|
|
151
149
|
assert.match(full, /A successful verification closes the task unless later changes affect it/i);
|
|
152
150
|
assert.doesNotMatch(full, /affected failed checks once/i);
|
|
@@ -235,6 +233,30 @@ test('modelStandaloneTools hides agent and disabled web-search/memory tools', ()
|
|
|
235
233
|
assert.deepEqual(modelStandaloneTools().map((tool) => tool.name), ['read']);
|
|
236
234
|
});
|
|
237
235
|
|
|
236
|
+
test('activateTools refreshes a stale session catalog before selecting new runtime tools', () => {
|
|
237
|
+
const read = { name: 'read', annotations: { readOnlyHint: true } };
|
|
238
|
+
const updateGoal = { name: 'update_goal', annotations: { readOnlyHint: false } };
|
|
239
|
+
const session = {
|
|
240
|
+
provider: 'grok-oauth',
|
|
241
|
+
messages: [{ role: 'system', content: '# Existing session' }],
|
|
242
|
+
tools: [read],
|
|
243
|
+
deferredToolCatalog: [read],
|
|
244
|
+
deferredCallableTools: ['read'],
|
|
245
|
+
deferredSelectedTools: ['read'],
|
|
246
|
+
deferredToolBp2Applied: true,
|
|
247
|
+
};
|
|
248
|
+
const surface = surfaceFor({ session, standalone: [read, updateGoal] });
|
|
249
|
+
const result = surface.activateTools(['update_goal']);
|
|
250
|
+
assert.deepEqual(result.missing, []);
|
|
251
|
+
assert.equal(
|
|
252
|
+
new Set([
|
|
253
|
+
...session.tools.map((tool) => tool.name),
|
|
254
|
+
...(session.deferredCallableTools || []),
|
|
255
|
+
]).has('update_goal'),
|
|
256
|
+
true,
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
|
|
238
260
|
test('empty session refresh strips denied tools and BP1 routes', async () => {
|
|
239
261
|
const session = {
|
|
240
262
|
id: 'sess_empty',
|
|
@@ -81,8 +81,20 @@ export function createToolSurface({
|
|
|
81
81
|
return surface;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
function activateTools(names) {
|
|
85
|
+
const session = getSession();
|
|
86
|
+
if (!session) return null;
|
|
87
|
+
const surfaceMode = deferredSurfaceModeForLead(mode);
|
|
88
|
+
applyDeferredToolSurface(session, surfaceMode, modelStandaloneTools(), {
|
|
89
|
+
provider: getRoute().provider,
|
|
90
|
+
model: getRoute().model,
|
|
91
|
+
});
|
|
92
|
+
return selectDeferredTools(session, names, surfaceMode);
|
|
93
|
+
}
|
|
94
|
+
|
|
84
95
|
return {
|
|
85
96
|
modelStandaloneTools,
|
|
97
|
+
activateTools,
|
|
86
98
|
invalidatePreSessionToolSurface() {
|
|
87
99
|
preSessionSurface = null;
|
|
88
100
|
},
|
|
@@ -34,11 +34,9 @@ function dispatchSignature(agent, params) {
|
|
|
34
34
|
* The memory process keeps PG, embeddings, and recall, and forwards only
|
|
35
35
|
* cycle LLM calls here. The broker owns admission (fair scheduler), callId
|
|
36
36
|
* idempotency, and cancellation; actual agent execution is delegated through
|
|
37
|
-
* dispatchAgent to the
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* the fair scheduler starts calls in parallel unless an operator explicitly
|
|
41
|
-
* configures a finite active limit.
|
|
37
|
+
* dispatchAgent to the session runtime host. A singleton is NOT a serial lane:
|
|
38
|
+
* every call gets its own AbortController and the fair scheduler starts calls
|
|
39
|
+
* in parallel unless an operator explicitly configures a finite active limit.
|
|
42
40
|
*/
|
|
43
41
|
export function createAgentDispatchBroker({
|
|
44
42
|
dispatchAgent,
|
|
@@ -99,10 +97,8 @@ export function createAgentDispatchBroker({
|
|
|
99
97
|
const run = async () => {
|
|
100
98
|
if (controller.signal.aborted) throw abortError(controller.signal);
|
|
101
99
|
const timeout = Number(params.timeout);
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
// memory-cycle agents returns to the OS at the worker's next memory
|
|
105
|
-
// recycle instead of accumulating in daemon commit forever.
|
|
100
|
+
// The host owns whether execution is in-process or isolated; the broker
|
|
101
|
+
// keeps scheduling, cancellation, and call identity transport-neutral.
|
|
106
102
|
return dispatchAgent({
|
|
107
103
|
dispatchId: id,
|
|
108
104
|
agent,
|
|
@@ -50,15 +50,13 @@ export function createNotify(mgr, { notifySessionCompletion } = {}) {
|
|
|
50
50
|
|
|
51
51
|
function workerNotifyFn(workerSessionId, notifyContext = {}) {
|
|
52
52
|
const workerId = clean(workerSessionId);
|
|
53
|
-
const ownerSessionId = clean(notifyContext?.callerSessionId || notifyContext?.sessionId);
|
|
54
53
|
return (text, meta = {}) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return ownerSessionId ? ownerDelivered : workerDelivered;
|
|
54
|
+
// Tool completions produced inside a Subagent belong to that Subagent's
|
|
55
|
+
// session. Mirroring them to the owner made every promoted shell command
|
|
56
|
+
// appear as a second Lead-level completion card. Agent task completion
|
|
57
|
+
// has its own owner delivery path below; only that terminal handoff
|
|
58
|
+
// crosses the parent boundary.
|
|
59
|
+
return workerId ? enqueueCompletionMessage(workerId, text, meta) : false;
|
|
62
60
|
};
|
|
63
61
|
}
|
|
64
62
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { createNotify } from './notify.mjs';
|
|
5
|
+
|
|
6
|
+
test('Subagent tool completion stays in the worker session', () => {
|
|
7
|
+
const queued = [];
|
|
8
|
+
const ownerDeliveries = [];
|
|
9
|
+
const { workerNotifyFn } = createNotify({
|
|
10
|
+
enqueuePendingMessage(sessionId, message) {
|
|
11
|
+
queued.push({ sessionId, message });
|
|
12
|
+
return 1;
|
|
13
|
+
},
|
|
14
|
+
}, {
|
|
15
|
+
notifySessionCompletion(sessionId, text, meta) {
|
|
16
|
+
ownerDeliveries.push({ sessionId, text, meta });
|
|
17
|
+
return true;
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const delivered = workerNotifyFn('sess_worker', {
|
|
22
|
+
callerSessionId: 'sess_lead',
|
|
23
|
+
})(
|
|
24
|
+
'Async shell task job_test (completed, exit 0) finished.\n\nresult',
|
|
25
|
+
{
|
|
26
|
+
type: 'shell_task_result',
|
|
27
|
+
execution_surface: 'shell',
|
|
28
|
+
execution_id: 'job_test',
|
|
29
|
+
status: 'completed',
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
assert.equal(delivered, true);
|
|
34
|
+
assert.equal(ownerDeliveries.length, 0);
|
|
35
|
+
assert.equal(queued.length, 1);
|
|
36
|
+
assert.equal(queued[0].sessionId, 'sess_worker');
|
|
37
|
+
});
|
|
@@ -59,7 +59,7 @@ import { createChannelSessionRouter } from './channel-session-router.mjs';
|
|
|
59
59
|
import { createSessionTransport } from './session-transport.mjs';
|
|
60
60
|
import { createSessionService } from './session-service.mjs';
|
|
61
61
|
import { createSessionProtocolClient } from './session-protocol.mjs';
|
|
62
|
-
import {
|
|
62
|
+
import { createDaemonSessionRuntimeHost } from './session-runtime-host-factory.mjs';
|
|
63
63
|
import { getStandaloneMemoryRuntime } from './memory-runtime-proxy.mjs';
|
|
64
64
|
import {
|
|
65
65
|
compareRuntimeVersions,
|
|
@@ -244,10 +244,10 @@ const eventLoopDelay = monitorEventLoopDelay({ resolution: 20 });
|
|
|
244
244
|
eventLoopDelay.enable();
|
|
245
245
|
let eventLoopLagTimer = null;
|
|
246
246
|
|
|
247
|
-
function
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
247
|
+
function registerMemoryRuntimeLazy() {
|
|
248
|
+
// Register the shared proxy immediately so daemon shutdown owns its
|
|
249
|
+
// lifecycle, but keep the isolated process off the startup path. A real
|
|
250
|
+
// memory call or the post-connect idle warmup starts the exact singleton.
|
|
251
251
|
if (process.env.MIXDOG_DAEMON_SKIP_MEMORY === '1') {
|
|
252
252
|
log('memory runtime skipped (MIXDOG_DAEMON_SKIP_MEMORY=1)');
|
|
253
253
|
return;
|
|
@@ -259,9 +259,7 @@ function startMemoryRuntimeEarly() {
|
|
|
259
259
|
dataDir: DATA_DIR,
|
|
260
260
|
cwd: CWD,
|
|
261
261
|
});
|
|
262
|
-
|
|
263
|
-
.then(() => log('memory runtime ready in isolated process'))
|
|
264
|
-
.catch((e) => log(`memory.start failed (non-fatal): ${e?.message || e}`));
|
|
262
|
+
log('memory runtime registered for lazy start');
|
|
265
263
|
} catch (e) { log(`memory.start setup failed (non-fatal): ${e?.message || e}`); }
|
|
266
264
|
}
|
|
267
265
|
|
|
@@ -414,11 +412,10 @@ async function main() {
|
|
|
414
412
|
process.exit(0);
|
|
415
413
|
}
|
|
416
414
|
process.on('exit', () => { try { releaseSingletonOwner(OWNER_PATH, process.pid); } catch {} });
|
|
417
|
-
|
|
415
|
+
registerMemoryRuntimeLazy();
|
|
418
416
|
agentDispatchBroker = createAgentDispatchBroker({
|
|
419
|
-
// Memory-cycle agents
|
|
420
|
-
//
|
|
421
|
-
// them, so its commit stays flat across cycles.
|
|
417
|
+
// Memory-cycle agents use the same lazy provider/orchestrator graph as
|
|
418
|
+
// session actors; it stays unloaded until a real cycle requests it.
|
|
422
419
|
dispatchAgent: (payload, options) => {
|
|
423
420
|
if (!sessionRuntimeHost) throw new Error('session runtime host is not ready');
|
|
424
421
|
return sessionRuntimeHost.agentDispatch(payload, options);
|
|
@@ -580,10 +577,14 @@ async function main() {
|
|
|
580
577
|
};
|
|
581
578
|
let sessionRuntimePrewarmStarted = false;
|
|
582
579
|
let sessionRuntimePrewarmPromise = null;
|
|
583
|
-
sessionRuntimeHost =
|
|
580
|
+
sessionRuntimeHost = createDaemonSessionRuntimeHost({
|
|
584
581
|
cwd: CWD,
|
|
585
582
|
log,
|
|
586
583
|
});
|
|
584
|
+
// Codex-style runtime: daemon routing and independent async session actors
|
|
585
|
+
// share one V8 isolate and module graph. CPU-heavy work stays in bounded
|
|
586
|
+
// native helpers/worker pools instead of duplicating the whole runtime.
|
|
587
|
+
log(`session runtime mode=${sessionRuntimeHost.status.mode}`);
|
|
587
588
|
function prewarmSessionRuntime() {
|
|
588
589
|
if (sessionRuntimePrewarmPromise) return sessionRuntimePrewarmPromise;
|
|
589
590
|
sessionRuntimePrewarmStarted = true;
|
|
@@ -592,7 +593,7 @@ async function main() {
|
|
|
592
593
|
.catch((error) => log(`image pipeline prewarm failed (non-fatal): ${error?.message || error}`));
|
|
593
594
|
sessionRuntimePrewarmPromise = sessionRuntimeHost.prewarm()
|
|
594
595
|
.then(() => {
|
|
595
|
-
log('session runtime
|
|
596
|
+
log('session runtime/agent-loop/keychain prewarm ready');
|
|
596
597
|
return true;
|
|
597
598
|
})
|
|
598
599
|
.catch((error) => {
|
|
@@ -605,6 +606,8 @@ async function main() {
|
|
|
605
606
|
}
|
|
606
607
|
sessionService = createSessionService({
|
|
607
608
|
createSessionRuntime: (options) => sessionRuntimeHost.create(options),
|
|
609
|
+
subscribeExternalSessionStates: (listener) =>
|
|
610
|
+
sessionRuntimeHost.subscribeAgentSessionStates(listener),
|
|
608
611
|
sessionExists: async (sessionId) => {
|
|
609
612
|
const store = await desktopRuntime.loadSessionStore();
|
|
610
613
|
return store.storedSessionExists?.(sessionId) === true;
|
|
@@ -670,7 +673,9 @@ async function main() {
|
|
|
670
673
|
...eventLoopStatus(),
|
|
671
674
|
}),
|
|
672
675
|
onClientsEmpty: () => { maybeSelfShutdown('no live session clients'); },
|
|
673
|
-
onClientRegistered: () => {
|
|
676
|
+
onClientRegistered: () => {
|
|
677
|
+
void prewarmSessionRuntime();
|
|
678
|
+
},
|
|
674
679
|
onClientDropped: (token) => { try { sessionService.releaseClient(token); } catch {} },
|
|
675
680
|
onUpgradeRequested: requestDaemonReplacement,
|
|
676
681
|
});
|
|
@@ -720,6 +725,17 @@ async function main() {
|
|
|
720
725
|
if (status.eventLoopP99Ms >= 250) {
|
|
721
726
|
log(`event-loop lag p95=${status.eventLoopP95Ms}ms p99=${status.eventLoopP99Ms}ms max=${status.eventLoopMaxMs}ms`);
|
|
722
727
|
}
|
|
728
|
+
// Legacy external runtime hosts may still report shard-local lag. The
|
|
729
|
+
// production in-process host is already covered by daemon loop telemetry.
|
|
730
|
+
const shards = sessionRuntimeHost?.status?.shards || [];
|
|
731
|
+
const lagging = shards.filter((shard) => Number(shard?.lag?.p99Ms) >= 250 || shard?.degraded);
|
|
732
|
+
if (lagging.length > 0) {
|
|
733
|
+
log(`session runtime shard lag ${lagging.map((shard) => (
|
|
734
|
+
`#${shard.index}${shard.degraded ? '(quarantined)' : ''}`
|
|
735
|
+
+ ` p95=${shard.lag?.p95Ms ?? -1}ms p99=${shard.lag?.p99Ms ?? -1}ms`
|
|
736
|
+
+ ` max=${shard.lag?.maxMs ?? -1}ms runtimes=${shard.runtimes}`
|
|
737
|
+
)).join(' ')}`);
|
|
738
|
+
}
|
|
723
739
|
eventLoopDelay.reset();
|
|
724
740
|
}, 30_000);
|
|
725
741
|
eventLoopLagTimer.unref?.();
|
|
@@ -81,6 +81,7 @@ function pluginHookConfigEntries(dataDir) {
|
|
|
81
81
|
}
|
|
82
82
|
const entries = [];
|
|
83
83
|
for (const plugin of plugins || []) {
|
|
84
|
+
if (plugin?.enabled === false) continue;
|
|
84
85
|
const root = String(plugin?.root || '').trim();
|
|
85
86
|
if (!root || !existsSync(root)) continue;
|
|
86
87
|
const id = cleanHookId(plugin.id || plugin.name || plugin.title || root.split(/[\\/]/).pop());
|
|
@@ -242,6 +242,7 @@ function enrichEntry(entry) {
|
|
|
242
242
|
const name = clean(manifest.name) || clean(manifest.id) || clean(entry.name) || displayNameFromUrl(entry.source);
|
|
243
243
|
return {
|
|
244
244
|
...entry,
|
|
245
|
+
enabled: entry.enabled !== false,
|
|
245
246
|
name,
|
|
246
247
|
title: clean(manifest.title) || clean(manifest.displayName) || clean(entry.title) || name,
|
|
247
248
|
version: clean(manifest.version) || clean(entry.version) || null,
|
|
@@ -280,6 +281,7 @@ export function addPlugin(sourceInput, { dataDir = resolvePluginData(), name } =
|
|
|
280
281
|
title: clean(manifest.title) || clean(manifest.displayName) || clean(name) || displayNameFromUrl(normalized.displaySource),
|
|
281
282
|
version: clean(manifest.version) || null,
|
|
282
283
|
description: clean(manifest.description),
|
|
284
|
+
enabled: true,
|
|
283
285
|
installedAt: nowIso(),
|
|
284
286
|
updatedAt: nowIso(),
|
|
285
287
|
};
|
|
@@ -338,6 +340,21 @@ export function updatePlugin(idOrName, { dataDir = resolvePluginData() } = {}) {
|
|
|
338
340
|
});
|
|
339
341
|
}
|
|
340
342
|
|
|
343
|
+
export function setPluginEnabled(idOrName, enabled, { dataDir = resolvePluginData() } = {}) {
|
|
344
|
+
const key = clean(idOrName);
|
|
345
|
+
if (!key) throw new Error('plugin id/name is required');
|
|
346
|
+
return mutateRegistry(dataDir, (registry) => {
|
|
347
|
+
const index = pluginIndex(registry, key);
|
|
348
|
+
if (index < 0) throw new Error(`plugin not registered: ${key}`);
|
|
349
|
+
const next = {
|
|
350
|
+
...registry.plugins[index],
|
|
351
|
+
enabled: enabled !== false,
|
|
352
|
+
};
|
|
353
|
+
registry.plugins[index] = next;
|
|
354
|
+
return enrichEntry(next);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
341
358
|
export function removePlugin(idOrName, { dataDir = resolvePluginData() } = {}) {
|
|
342
359
|
const key = clean(idOrName);
|
|
343
360
|
if (!key) throw new Error('plugin id/name is required');
|
|
@@ -18,7 +18,9 @@ import {
|
|
|
18
18
|
_publishManagedPluginRoot,
|
|
19
19
|
addPlugin,
|
|
20
20
|
listRegisteredPlugins,
|
|
21
|
+
setPluginEnabled,
|
|
21
22
|
} from './plugin-admin.mjs';
|
|
23
|
+
import { hookConfigEntries } from './hook-bus/config.mjs';
|
|
22
24
|
|
|
23
25
|
test('plugin registry persists atomically with owner-only permissions', () => {
|
|
24
26
|
const root = mkdtempSync(join(tmpdir(), 'mixdog-plugin-registry-'));
|
|
@@ -33,6 +35,11 @@ test('plugin registry persists atomically with owner-only permissions', () => {
|
|
|
33
35
|
const registry = JSON.parse(readFileSync(registryPath, 'utf8'));
|
|
34
36
|
assert.equal(registry.plugins.length, 1);
|
|
35
37
|
assert.equal(listRegisteredPlugins({ dataDir })[0].name, 'local-test');
|
|
38
|
+
assert.equal(listRegisteredPlugins({ dataDir })[0].enabled, true);
|
|
39
|
+
assert.equal(hookConfigEntries(dataDir, root).some((entry) => entry.sourceType === 'plugin'), true);
|
|
40
|
+
setPluginEnabled('local-test', false, { dataDir });
|
|
41
|
+
assert.equal(listRegisteredPlugins({ dataDir })[0].enabled, false);
|
|
42
|
+
assert.equal(hookConfigEntries(dataDir, root).some((entry) => entry.sourceType === 'plugin'), false);
|
|
36
43
|
if (process.platform !== 'win32') {
|
|
37
44
|
assert.equal(statSync(registryPath).mode & 0o777, 0o600);
|
|
38
45
|
}
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
|
|
45
45
|
const API_PROVIDERS = Object.freeze([
|
|
46
46
|
Object.freeze({ id: 'opencode-go', name: 'OpenCode Go API', env: 'OPENCODE_API_KEY', url: 'https://opencode.ai' }),
|
|
47
|
+
Object.freeze({ id: 'openrouter', name: 'OpenRouter', env: 'OPENROUTER_API_KEY', url: 'https://openrouter.ai/keys' }),
|
|
47
48
|
Object.freeze({ id: 'openai', name: 'OpenAI API', env: 'OPENAI_API_KEY', url: 'https://platform.openai.com/api-keys' }),
|
|
48
49
|
Object.freeze({ id: 'anthropic', name: 'Anthropic API', env: 'ANTHROPIC_API_KEY', url: 'https://console.anthropic.com/settings/keys' }),
|
|
49
50
|
Object.freeze({ id: 'gemini', name: 'Gemini API', env: 'GEMINI_API_KEY', url: 'https://aistudio.google.com/apikey' }),
|
|
@@ -15,6 +15,7 @@ export const SESSION_READ_ACTIONS = Object.freeze([
|
|
|
15
15
|
'getCompactionSettings',
|
|
16
16
|
'getDisabledSkills',
|
|
17
17
|
'getMediaJob',
|
|
18
|
+
'getMcpServerConfig',
|
|
18
19
|
'getOnboardingStatus',
|
|
19
20
|
'getOAuthProviderLoginStatus',
|
|
20
21
|
'getOutputStyle',
|
|
@@ -79,6 +80,7 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
|
|
|
79
80
|
'deleteWorkflow',
|
|
80
81
|
'enablePluginMcp',
|
|
81
82
|
'forgetProviderAuth',
|
|
83
|
+
'goalControl',
|
|
82
84
|
'inheritFrom',
|
|
83
85
|
'loginOAuthProvider',
|
|
84
86
|
'loginOpenCodeGoUsage',
|
|
@@ -107,10 +109,12 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
|
|
|
107
109
|
'runScheduleNow',
|
|
108
110
|
'runUpdateNow',
|
|
109
111
|
'saveAgentDefinition',
|
|
112
|
+
'saveMcpServer',
|
|
110
113
|
'saveOpenAIUsageSessionKey',
|
|
111
114
|
'saveOpenCodeGoUsageAuth',
|
|
112
115
|
'saveProviderApiKey',
|
|
113
116
|
'saveSchedule',
|
|
117
|
+
'saveSkill',
|
|
114
118
|
'saveWebhook',
|
|
115
119
|
'saveWorkflowPack',
|
|
116
120
|
'selectTools',
|
|
@@ -129,6 +133,7 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
|
|
|
129
133
|
'setMemoryToolsEnabled',
|
|
130
134
|
'setModel',
|
|
131
135
|
'setOutputStyle',
|
|
136
|
+
'setPluginEnabled',
|
|
132
137
|
'setProfile',
|
|
133
138
|
'setProgressHint',
|
|
134
139
|
'setRecapEnabled',
|