mixdog 1.0.7 → 1.0.9
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 +2 -1
- package/scripts/lib/run-node-tests.mjs +13 -0
- package/scripts/session-efficiency-diag.mjs +283 -0
- package/src/defaults/skills/computer-use/SKILL.md +3 -3
- package/src/defaults/skills/local-provider/SKILL.md +2 -1
- package/src/defaults/skills/setup/SKILL.md +4 -4
- package/src/defaults/skills/setup/references/actions.md +1 -3
- package/src/defaults/skills/setup/references/surfaces.md +2 -3
- package/src/headless-exec.mjs +1 -0
- package/src/rules/shared/70-delivery.md +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +8 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +18 -3
- package/src/runtime/agent/orchestrator/providers/client-version-store.mjs +50 -0
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +27 -4
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +9 -5
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-models.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +16 -0
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +0 -20
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +12 -10
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +23 -9
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +15 -6
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +7 -0
- package/src/runtime/channels/lib/voice-transcription.mjs +30 -4
- package/src/runtime/computer-bridge/action-schema.mjs +3 -3
- package/src/runtime/computer-bridge/actions.mjs +4 -0
- package/src/runtime/computer-bridge/bridge-env.test-support.mjs +22 -0
- package/src/runtime/computer-bridge/client.mjs +10 -183
- package/src/runtime/computer-bridge/core-actions.mjs +2 -1
- package/src/runtime/computer-bridge/error-recovery.mjs +46 -34
- package/src/runtime/computer-bridge/result-canonical.mjs +181 -0
- package/src/runtime/local-provider/asset-installer.mjs +20 -10
- package/src/runtime/local-provider/catalog.mjs +4 -7
- package/src/runtime/local-provider/data/manifest.json +14 -1
- package/src/runtime/local-provider/hardware.mjs +36 -5
- package/src/runtime/local-provider/resumable-installations.mjs +2 -2
- package/src/runtime/local-provider/server.mjs +1 -1
- package/src/runtime/memory/index.mjs +2 -4
- package/src/runtime/memory/lib/core-memory-file.mjs +3 -8
- package/src/runtime/memory/lib/core-memory-store.mjs +14 -61
- package/src/runtime/memory/lib/cycle-scheduler/backlog-probe.mjs +5 -9
- package/src/runtime/memory/lib/cycle1/cycle1-plan.mjs +7 -0
- package/src/runtime/memory/lib/cycle1/cycle1-rows.mjs +43 -16
- package/src/runtime/memory/lib/cycle1/cycle1-window.mjs +11 -2
- package/src/runtime/memory/lib/embedding-provider.mjs +2 -5
- package/src/runtime/memory/lib/embedding-reindex.mjs +1 -5
- package/src/runtime/memory/lib/http-router/lifecycle-routes.mjs +0 -1
- package/src/runtime/memory/lib/memory-action-handlers/maintenance-actions.mjs +1 -1
- package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +4 -22
- package/src/runtime/memory/lib/memory.mjs +79 -32
- package/src/runtime/memory/lib/query-maintenance-handlers.mjs +0 -2
- package/src/runtime/office/com/com-adapter.mjs +3 -0
- package/src/runtime/office/pdf/pdf-adapter.mjs +50 -14
- package/src/runtime/office/pdf/pdf-render.mjs +2 -6
- package/src/runtime/office/pdf/pdf-security.mjs +8 -2
- package/src/runtime/office/portable/font-provisioner.mjs +28 -0
- package/src/runtime/office/portable/portable-docx-edits.mjs +28 -1
- package/src/runtime/office/portable/portable-soffice.mjs +44 -7
- package/src/runtime/shared/llm/anthropic-betas.mjs +7 -1
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +1 -1
- package/src/runtime/shared/llm/default-context-window.mjs +42 -0
- package/src/runtime/shared/llm/model-catalog-projection.mjs +2 -0
- package/src/runtime/shared/llm/model-catalog.mjs +2 -0
- package/src/runtime/shared/plugin-paths.mjs +9 -2
- package/src/runtime/shared/worker-exec-argv.mjs +25 -0
- package/src/runtime/web-search/lib/formatter.mjs +43 -18
- package/src/session-runtime/automation-agents.mjs +88 -0
- package/src/session-runtime/automation-workflow.mjs +19 -6
- package/src/session-runtime/boot/apis.mjs +0 -1
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +14 -12
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +4 -25
- package/src/session-runtime/model-recency.mjs +10 -2
- package/src/session-runtime/model-route/set-route.mjs +7 -1
- package/src/session-runtime/resource-api.mjs +8 -2
- package/src/session-runtime/resource-plugins-api.mjs +14 -2
- package/src/session-runtime/route-state.mjs +6 -2
- package/src/session-runtime/schedule-session-run.mjs +7 -1
- package/src/session-runtime/services/agent-tool/render.mjs +0 -5
- package/src/session-runtime/settings-builtin-tools-api.mjs +0 -16
- package/src/session-runtime/setup-tool/executor.mjs +7 -13
- package/src/session-runtime/setup-tool/tool-defs.mjs +2 -3
- package/src/session-runtime/tool-policy-refresh.mjs +0 -2
- package/src/session-runtime/webhook-session-run.mjs +10 -11
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +5 -0
- package/src/standalone/daemon-agent-control.mjs +8 -1
- package/src/standalone/daemon.mjs +4 -0
- package/src/tui/dist/index.mjs +3 -2
- package/src/tui/session/agent-job-feed/notification-router.mjs +7 -0
- package/src/tui/session/live-share.mjs +16 -1
- package/src/tui/session/notification-plan.mjs +4 -0
- package/src/session-runtime/bridge-first-use-gate.mjs +0 -72
|
@@ -1,41 +1,22 @@
|
|
|
1
|
-
// Browser Use and Computer Use: the environment kill switch, the
|
|
2
|
-
//
|
|
1
|
+
// Browser Use and Computer Use: the environment kill switch, then the bridge
|
|
2
|
+
// client. Neither asks the user before a call.
|
|
3
3
|
import { executeBrowserTool } from '../../runtime/browser-bridge/client.mjs';
|
|
4
4
|
import { executeComputerTool } from '../../runtime/computer-bridge/client.mjs';
|
|
5
|
-
import { createBridgeFirstUseGate } from '../bridge-first-use-gate.mjs';
|
|
6
5
|
import { featureEnvOverride } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
|
|
7
6
|
import { createCallerContextResolvers } from './caller-context.mjs';
|
|
8
7
|
|
|
9
8
|
export function createBridgeToolHandlers({ rt }) {
|
|
10
|
-
const bridgeFirstUseGate = createBridgeFirstUseGate({ getConfig: () => rt.config });
|
|
11
9
|
const { sessionIdFor, signalFor } = createCallerContextResolvers(rt);
|
|
12
10
|
|
|
13
|
-
// Browser Use and Computer Use ask the user once per session before their
|
|
14
|
-
// first live call; the answer is the tool result when it is no.
|
|
15
|
-
const firstUseDenial = async (name, args, callerCtx, callerCwd) => {
|
|
16
|
-
const denial = await bridgeFirstUseGate({
|
|
17
|
-
name,
|
|
18
|
-
args,
|
|
19
|
-
cwd: callerCwd,
|
|
20
|
-
sessionId: sessionIdFor(callerCtx),
|
|
21
|
-
toolCallId: callerCtx?.toolCallId || null,
|
|
22
|
-
toolApprovalHook: callerCtx?.toolApprovalHook,
|
|
23
|
-
invocationSource: callerCtx?.invocationSource,
|
|
24
|
-
});
|
|
25
|
-
return denial ? { content: [{ type: 'text', text: denial }], isError: true } : null;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
11
|
const environmentDisabled = (callerCtx, feature) =>
|
|
29
12
|
callerCtx?.invocationSource === 'model-tool' && featureEnvOverride(feature) === false;
|
|
30
13
|
|
|
31
14
|
// `browser` and `browser_devtools` are one bridge; the tool name only
|
|
32
15
|
// scopes which actions the validator admits.
|
|
33
|
-
const browser = async (args, { name, callerCtx
|
|
16
|
+
const browser = async (args, { name, callerCtx }) => {
|
|
34
17
|
if (environmentDisabled(callerCtx, 'MIXDOG_FEATURE_BROWSER')) {
|
|
35
18
|
throw new Error('the browser tool is disabled in this environment');
|
|
36
19
|
}
|
|
37
|
-
const denied = await firstUseDenial(name, args, callerCtx, callerCwd);
|
|
38
|
-
if (denied) return denied;
|
|
39
20
|
return await executeBrowserTool(args, {
|
|
40
21
|
tool: name,
|
|
41
22
|
sessionId: sessionIdFor(callerCtx),
|
|
@@ -44,12 +25,10 @@ export function createBridgeToolHandlers({ rt }) {
|
|
|
44
25
|
});
|
|
45
26
|
};
|
|
46
27
|
|
|
47
|
-
const computer = async (args, {
|
|
28
|
+
const computer = async (args, { callerCtx, callerCwd }) => {
|
|
48
29
|
if (environmentDisabled(callerCtx, 'MIXDOG_FEATURE_COMPUTER')) {
|
|
49
30
|
throw new Error('the computer tool is disabled in this environment');
|
|
50
31
|
}
|
|
51
|
-
const denied = await firstUseDenial(name, args, callerCtx, callerCwd);
|
|
52
|
-
if (denied) return denied;
|
|
53
32
|
return await executeComputerTool(args, {
|
|
54
33
|
sessionId: sessionIdFor(callerCtx),
|
|
55
34
|
cwd: callerCwd,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// construction. Pure except for the
|
|
3
3
|
// injected route provider (for sort priority) and webSearchCapableFor predicate.
|
|
4
4
|
import { clean } from '../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
5
|
+
import { contextWindowRange } from '../runtime/shared/llm/default-context-window.mjs';
|
|
5
6
|
|
|
6
7
|
function parsedProviderModelVersion(id) {
|
|
7
8
|
const text = clean(id).toLowerCase();
|
|
@@ -106,14 +107,21 @@ export function isSelectableLlmModel(model) {
|
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
export function providerModelCacheRow(name, m, webSearchCapableFor) {
|
|
110
|
+
// The pickers read the default window and the slider's range straight off
|
|
111
|
+
// this row, so a default lowered by range has to be stated here.
|
|
112
|
+
const range = contextWindowRange({
|
|
113
|
+
provider: name,
|
|
114
|
+
contextWindow: m.contextWindow,
|
|
115
|
+
maxContextWindow: m.maxContextWindow,
|
|
116
|
+
});
|
|
109
117
|
return {
|
|
110
118
|
id: m.id,
|
|
111
119
|
provider: name,
|
|
112
120
|
display: m.display || m.name || m.id,
|
|
113
121
|
created: typeof m.created === 'number' ? m.created : null,
|
|
114
122
|
releaseDate: m.releaseDate || null,
|
|
115
|
-
contextWindow: m.contextWindow,
|
|
116
|
-
maxContextWindow: m.maxContextWindow || null,
|
|
123
|
+
contextWindow: range.defaultWindow || m.contextWindow,
|
|
124
|
+
maxContextWindow: range.maxWindow > range.defaultWindow ? range.maxWindow : m.maxContextWindow || null,
|
|
117
125
|
outputTokens: m.outputTokens || null,
|
|
118
126
|
family: m.family || null,
|
|
119
127
|
tier: m.tier || null,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
|
|
14
14
|
import { getModelMetadataSync } from '../../runtime/agent/orchestrator/providers/model-catalog.mjs';
|
|
15
15
|
import { workflowPresetId } from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
|
|
16
|
+
import { resolveRouteContextState } from '../route-state.mjs';
|
|
16
17
|
import { writeStatuslineRoute } from '../statusline-route.mjs';
|
|
17
18
|
import { sessionHasRouteHistory, shouldRecreateEmptySessionForRouteChange } from '../../runtime/agent/orchestrator/runtime-core/session-route-policy.mjs';
|
|
18
19
|
import { rebuildDeferredToolSurfaceForProvider } from '../../runtime/agent/orchestrator/runtime-core/tool-catalog.mjs';
|
|
@@ -52,7 +53,12 @@ async function resolveSelectedRoute(deps, next) {
|
|
|
52
53
|
selectedRoute.modelParameters
|
|
53
54
|
);
|
|
54
55
|
return {
|
|
55
|
-
selectedRoute: {
|
|
56
|
+
selectedRoute: {
|
|
57
|
+
...selectedRoute,
|
|
58
|
+
fast: fastCapable ? selectedRoute.fast === true : false,
|
|
59
|
+
// Lets the saved model settings tell a chosen window from the default.
|
|
60
|
+
contextDefaultPercent: resolveRouteContextState(selectedRoute, modelMeta).contextDefaultPercent,
|
|
61
|
+
},
|
|
56
62
|
fastCapable,
|
|
57
63
|
modelMeta,
|
|
58
64
|
};
|
|
@@ -8,7 +8,7 @@ import { createExtensionSync } from './resource-extension-sync.mjs';
|
|
|
8
8
|
import { createScopeDecorators } from './resource-scope-decorate.mjs';
|
|
9
9
|
import { createMcpResourceApi } from './resource-mcp-api.mjs';
|
|
10
10
|
import { createSkillsResourceApi } from './resource-skills-api.mjs';
|
|
11
|
-
import { createPluginsResourceApi } from './resource-plugins-api.mjs';
|
|
11
|
+
import { createPluginsResourceApi, findRegisteredPlugin } from './resource-plugins-api.mjs';
|
|
12
12
|
import { createRecallResourceApi } from './resource-recall-api.mjs';
|
|
13
13
|
|
|
14
14
|
export function createResourceApi(deps) {
|
|
@@ -27,8 +27,14 @@ export function createResourceApi(deps) {
|
|
|
27
27
|
if (!EXTENSION_SCOPE_KINDS.includes(scopeKind)) {
|
|
28
28
|
throw new Error(`extension scope kind must be one of ${EXTENSION_SCOPE_KINDS.join(', ')}`);
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
let key = clean(name);
|
|
31
31
|
if (!key) throw new Error('extension name is required');
|
|
32
|
+
// Plugin scopes are keyed by registry id; a name must resolve to it.
|
|
33
|
+
if (scopeKind === 'plugins') {
|
|
34
|
+
const plugin = findRegisteredPlugin(pluginsStatus, key);
|
|
35
|
+
if (!plugin) throw new Error(`plugin not registered: ${key}`);
|
|
36
|
+
key = clean(plugin.id || plugin.name);
|
|
37
|
+
}
|
|
32
38
|
let list = [projects];
|
|
33
39
|
if (Array.isArray(projects)) list = projects;
|
|
34
40
|
else if (projects == null) list = [];
|
|
@@ -10,6 +10,15 @@ import { pluginMcpServerName, pluginServerMatcher } from '../runtime/agent/orche
|
|
|
10
10
|
|
|
11
11
|
const pluginKey = (plugin) => clean(plugin.id || plugin.name || plugin);
|
|
12
12
|
|
|
13
|
+
/** The registered status row a caller means by `plugin` (row, id, name, or
|
|
14
|
+
* title), or null. Callers such as the setup tool pass only a name, while
|
|
15
|
+
* MCP entry names and scope keys come from the registered record. */
|
|
16
|
+
export function findRegisteredPlugin(pluginsStatus, plugin) {
|
|
17
|
+
const key = pluginKey(plugin);
|
|
18
|
+
if (!key) return null;
|
|
19
|
+
return (pluginsStatus()?.plugins || []).find((row) => [row.id, row.name, row.title].map(clean).includes(key)) || null;
|
|
20
|
+
}
|
|
21
|
+
|
|
13
22
|
export function createPluginsResourceApi({ deps, sync, decorate }) {
|
|
14
23
|
const {
|
|
15
24
|
getConfig,
|
|
@@ -52,8 +61,9 @@ export function createPluginsResourceApi({ deps, sync, decorate }) {
|
|
|
52
61
|
return { plugin: updated, status: pluginsStatus() };
|
|
53
62
|
},
|
|
54
63
|
async setPluginEnabled(plugin = {}, enabled = true) {
|
|
64
|
+
const owner = findRegisteredPlugin(pluginsStatus, plugin) || plugin;
|
|
55
65
|
const updated = registrySetPluginEnabled(pluginKey(plugin), enabled, { dataDir: pluginData() });
|
|
56
|
-
await rewritePluginServers(
|
|
66
|
+
await rewritePluginServers(owner, (servers, owned) => {
|
|
57
67
|
const next = {};
|
|
58
68
|
for (const [name, value] of Object.entries(servers)) {
|
|
59
69
|
const config = value && typeof value === 'object' && !Array.isArray(value) ? { ...value } : value;
|
|
@@ -69,8 +79,10 @@ export function createPluginsResourceApi({ deps, sync, decorate }) {
|
|
|
69
79
|
return { plugin: updated, status: pluginsStatus() };
|
|
70
80
|
},
|
|
71
81
|
async removePlugin(plugin = {}) {
|
|
82
|
+
// Resolve before the registry forgets the plugin and its manifest name.
|
|
83
|
+
const owner = findRegisteredPlugin(pluginsStatus, plugin) || plugin;
|
|
72
84
|
const removed = registryRemovePlugin(pluginKey(plugin), { dataDir: pluginData() });
|
|
73
|
-
await rewritePluginServers(
|
|
85
|
+
await rewritePluginServers(owner, (servers, owned) => {
|
|
74
86
|
const next = { ...servers };
|
|
75
87
|
for (const name of Object.keys(next)) {
|
|
76
88
|
if (owned(name)) delete next[name];
|
|
@@ -5,6 +5,7 @@ import { clean, hasOwn } from '../runtime/agent/orchestrator/runtime-core/sessio
|
|
|
5
5
|
import { coerceEffortFor } from '../runtime/agent/orchestrator/runtime-core/effort.mjs';
|
|
6
6
|
import { fastCapableFor } from '../runtime/agent/orchestrator/runtime-core/model-capabilities.mjs';
|
|
7
7
|
import { providerCachedModelMetadataSync } from '../runtime/agent/orchestrator/providers/provider-catalog-cache.mjs';
|
|
8
|
+
import { contextWindowRange } from '../runtime/shared/llm/default-context-window.mjs';
|
|
8
9
|
|
|
9
10
|
export function resolveRouteEffortState(targetRoute = {}, modelMeta = null) {
|
|
10
11
|
const requested = hasOwn(targetRoute, 'effort') ? targetRoute.effort : targetRoute.preset?.effort || null;
|
|
@@ -49,8 +50,11 @@ export function resolveRouteContextState(targetRoute = {}, modelMeta = null, win
|
|
|
49
50
|
Number(modelMeta?.contextWindow) > 0 || Number(modelMeta?.maxContextWindow) > 0
|
|
50
51
|
? modelMeta
|
|
51
52
|
: windowLookup?.(clean(targetRoute?.provider), clean(targetRoute?.model)) || modelMeta;
|
|
52
|
-
const defaultWindow =
|
|
53
|
-
|
|
53
|
+
const { defaultWindow, maxWindow } = contextWindowRange({
|
|
54
|
+
provider: clean(targetRoute?.provider),
|
|
55
|
+
contextWindow: windowMeta?.contextWindow,
|
|
56
|
+
maxContextWindow: windowMeta?.maxContextWindow,
|
|
57
|
+
});
|
|
54
58
|
if (!maxWindow) {
|
|
55
59
|
return { contextPercent: undefined, contextDefaultPercent: undefined, selectedContextWindow: undefined };
|
|
56
60
|
}
|
|
@@ -11,6 +11,7 @@ import { createSession } from '../runtime/agent/orchestrator/session/manager/ses
|
|
|
11
11
|
import { askSession } from '../runtime/agent/orchestrator/session/manager/ask-session.mjs';
|
|
12
12
|
import { modelRouteFields, parseScheduleModelRef } from '../runtime/shared/schedule-model-ref.mjs';
|
|
13
13
|
import { automationWorkflowOpts } from './automation-workflow.mjs';
|
|
14
|
+
import { runAutomationTurns } from './automation-agents.mjs';
|
|
14
15
|
import { automationPromptContent } from '../runtime/shared/automation-attachments.mjs';
|
|
15
16
|
|
|
16
17
|
/** Resolve schedule.model into a {provider,model,effort?,fast?} route. */
|
|
@@ -58,6 +59,11 @@ export async function runScheduleSession(schedule, { config = null, prompt: prom
|
|
|
58
59
|
// title/preview derive from it, so no "[Scheduled task: …]" header noise.
|
|
59
60
|
// Stored attachments ride along as composer-style content parts.
|
|
60
61
|
const content = automationPromptContent(prompt, schedule.attachments);
|
|
61
|
-
|
|
62
|
+
// Agents the run delegates to report back; each result is the next turn.
|
|
63
|
+
const result = await runAutomationTurns(
|
|
64
|
+
session.id,
|
|
65
|
+
() => askSession(session.id, content, null, null, cwd || undefined),
|
|
66
|
+
(completions) => askSession(session.id, completions, null, null, cwd || undefined)
|
|
67
|
+
);
|
|
62
68
|
return { sessionId: session.id, result: String(result?.content || '') };
|
|
63
69
|
}
|
|
@@ -17,13 +17,8 @@ export function abnormalEmptyFinishError(result, agent) {
|
|
|
17
17
|
if (!reason) return null;
|
|
18
18
|
const iterations = result?.iterations ?? 0;
|
|
19
19
|
const toolCallsTotal = result?.toolCallsTotal ?? 0;
|
|
20
|
-
const maxLoopIterations = result?.maxLoopIterations ?? 0;
|
|
21
20
|
const stopReason = result?.stopReason ?? result?.stop_reason ?? null;
|
|
22
21
|
switch (reason) {
|
|
23
|
-
case 'iteration_cap':
|
|
24
|
-
// Preserve failure reporting for stored results from older runtimes.
|
|
25
|
-
// Current execution no longer applies a loop iteration ceiling.
|
|
26
|
-
return `agent '${agent}' hit the loop iteration ceiling (${maxLoopIterations} iterations, ${toolCallsTotal} tool calls) without producing a final answer`;
|
|
27
22
|
case 'truncated':
|
|
28
23
|
return `agent '${agent}' response was truncated (stopReason=${stopReason}) before a final answer (${iterations} iterations, ${toolCallsTotal} tool calls)`;
|
|
29
24
|
case 'empty':
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
// Built-in tool modules: the enable/install toggles (web search, memory, git,
|
|
2
2
|
// office, tidy, local provider), the recap switch and the Code Tidy card.
|
|
3
3
|
import {
|
|
4
|
-
BRIDGE_FIRST_USE_IDS,
|
|
5
4
|
INSTALLABLE_BUILTIN_IDS,
|
|
6
5
|
builtinFeatureActive,
|
|
7
|
-
builtinFirstUseApproval,
|
|
8
6
|
builtinInstalled,
|
|
9
|
-
setBuiltinFirstUseApprovalInConfig,
|
|
10
7
|
setBuiltinInstalledInConfig,
|
|
11
8
|
} from '../runtime/agent/orchestrator/runtime-core/builtin-features.mjs';
|
|
12
9
|
import { LOCAL_PROVIDER_ID } from '../runtime/local-provider/managed-runtime.mjs';
|
|
@@ -115,19 +112,6 @@ export function createBuiltinToolSettings(
|
|
|
115
112
|
await refreshEmptySessionToolPolicy?.();
|
|
116
113
|
return this.getToolModuleSettings();
|
|
117
114
|
},
|
|
118
|
-
/** Once-per-session approval before the first Browser Use or Computer Use
|
|
119
|
-
* call. The setting is read at each call, so it applies at once. */
|
|
120
|
-
async setBridgeFirstUseApproval(name, enabled) {
|
|
121
|
-
if (!BRIDGE_FIRST_USE_IDS.includes(name)) {
|
|
122
|
-
throw new TypeError('First-use approval applies to browser or computer.');
|
|
123
|
-
}
|
|
124
|
-
saveConfigAndAdopt(setBuiltinFirstUseApprovalInConfig({ ...getConfig() }, name, enabled !== false));
|
|
125
|
-
return {
|
|
126
|
-
name,
|
|
127
|
-
firstUseApproval: builtinFirstUseApproval(getConfig(), name),
|
|
128
|
-
appliesTo: 'the next first use in any session, including this one',
|
|
129
|
-
};
|
|
130
|
-
},
|
|
131
115
|
async setBuiltinToolEnabled(name, enabled) {
|
|
132
116
|
if (name !== 'git' && name !== 'office' && name !== 'tidy' && name !== 'localProvider') {
|
|
133
117
|
throw new TypeError('Built-in tool must be git, office, tidy, or localProvider.');
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* reconnects, and the empty-session tool-policy refresh all apply exactly as
|
|
4
4
|
* they do for a UI click. The facade is read lazily because runtime-core
|
|
5
5
|
* registers the tool executor before it finishes assembling the API object. */
|
|
6
|
-
import { builtinFeatureActive
|
|
6
|
+
import { builtinFeatureActive } from '../../runtime/agent/orchestrator/runtime-core/builtin-features.mjs';
|
|
7
7
|
import { ORCHESTRATION_MODES } from '../../runtime/shared/orchestration.mjs';
|
|
8
8
|
import { SETUP_DESKTOP_DOMAINS, SETUP_HANDOFFS } from './settings-contract.mjs';
|
|
9
9
|
import { executeExtendedSetupAction, publicAutomation, validateMcpInput } from './extended-actions.mjs';
|
|
@@ -253,14 +253,8 @@ const SETUP_STATUS_READERS = {
|
|
|
253
253
|
const config = getConfig();
|
|
254
254
|
return {
|
|
255
255
|
...featureFlags(rt),
|
|
256
|
-
browser: {
|
|
257
|
-
|
|
258
|
-
firstUseApproval: builtinFirstUseApproval(config, 'browser'),
|
|
259
|
-
},
|
|
260
|
-
computer: {
|
|
261
|
-
active: builtinFeatureActive(config, 'computer'),
|
|
262
|
-
firstUseApproval: builtinFirstUseApproval(config, 'computer'),
|
|
263
|
-
},
|
|
256
|
+
browser: { active: builtinFeatureActive(config, 'browser') },
|
|
257
|
+
computer: { active: builtinFeatureActive(config, 'computer') },
|
|
264
258
|
};
|
|
265
259
|
},
|
|
266
260
|
shell: (rt) => rt.getSystemShell?.() || {},
|
|
@@ -398,10 +392,6 @@ const SETUP_ACTION_HANDLERS = {
|
|
|
398
392
|
if (DESKTOP_HOSTED_FEATURES.includes(name)) return requestDesktop(args);
|
|
399
393
|
return rt.setBuiltinToolEnabled(name, requireBoolean(args.enabled));
|
|
400
394
|
},
|
|
401
|
-
set_first_use_approval: (rt, args) => {
|
|
402
|
-
const name = requireEnum(args.name, ['browser', 'computer'], 'name');
|
|
403
|
-
return rt.setBridgeFirstUseApproval(name, requireBoolean(args.enabled));
|
|
404
|
-
},
|
|
405
395
|
install_builtin: async (rt, args, { requestDesktop }) => {
|
|
406
396
|
const name = requireEnum(args.name, SETUP_INSTALLABLE_FEATURES, 'name');
|
|
407
397
|
if (DESKTOP_HOSTED_FEATURES.includes(name)) return requestDesktop(args);
|
|
@@ -596,6 +586,10 @@ export function createSetupToolExecutor({ getApi, getConfig, notifySessionUi, ge
|
|
|
596
586
|
const result = await execute(args, options);
|
|
597
587
|
if (READ_ONLY_SETUP_ACTIONS.has(args.action)) return JSON.stringify(result ?? {}, null, 2);
|
|
598
588
|
await flushSettings?.();
|
|
589
|
+
// Attached surfaces re-read what changed instead of waiting for their
|
|
590
|
+
// next open or cache expiry.
|
|
591
|
+
const sessionId = clean(getSessionId?.());
|
|
592
|
+
if (sessionId) notifySessionUi?.(sessionId, 'Settings changed', { kind: 'setup-changed', action: args.action });
|
|
599
593
|
return setupMutationReceipt(args.action, result);
|
|
600
594
|
},
|
|
601
595
|
};
|
|
@@ -78,7 +78,6 @@ export const SETUP_ACTION_FIELDS = Object.freeze({
|
|
|
78
78
|
set_recap_enabled: 'enabled',
|
|
79
79
|
set_web_search_enabled: 'enabled',
|
|
80
80
|
set_builtin_enabled: 'name enabled',
|
|
81
|
-
set_first_use_approval: 'name enabled',
|
|
82
81
|
install_builtin: 'name',
|
|
83
82
|
install_local_model: 'modelId',
|
|
84
83
|
start_local_installation: 'phase modelId?',
|
|
@@ -209,11 +208,11 @@ export const SETUP_TOOL_DEFS = Object.freeze([
|
|
|
209
208
|
enabled: {
|
|
210
209
|
type: 'boolean',
|
|
211
210
|
description:
|
|
212
|
-
'Boolean setters (set_*_enabled,
|
|
211
|
+
'Boolean setters (set_*_enabled, set_compaction, set_auto_update, set_mcp_enabled, set_plugin_enabled).',
|
|
213
212
|
},
|
|
214
213
|
name: {
|
|
215
214
|
type: 'string',
|
|
216
|
-
description: `set_builtin_enabled: ${SETUP_BUILTIN_TOGGLE_FEATURES.join('|')}; install_builtin also accepts memory. Memory toggles use set_memory_enabled.
|
|
215
|
+
description: `set_builtin_enabled: ${SETUP_BUILTIN_TOGGLE_FEATURES.join('|')}; install_builtin also accepts memory. Memory toggles use set_memory_enabled. revoke_linked_device: client id from status connection, not its display name; set_developer_option: option id from status developer; read_definition/delete_definition: the definition id; else MCP/plugin/provider id.`,
|
|
217
216
|
},
|
|
218
217
|
accountId: {
|
|
219
218
|
type: 'string',
|
|
@@ -71,7 +71,6 @@ export function createToolPolicyRefresh({
|
|
|
71
71
|
getDataDir,
|
|
72
72
|
modelStandaloneTools,
|
|
73
73
|
featureDisallowedTools,
|
|
74
|
-
memoryToolsEnabled,
|
|
75
74
|
loadCoreMemoryContext,
|
|
76
75
|
activeWorkflowContext,
|
|
77
76
|
invalidatePreSessionToolSurface,
|
|
@@ -94,7 +93,6 @@ export function createToolPolicyRefresh({
|
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
async function leadCoreMemoryContext() {
|
|
97
|
-
if (!memoryToolsEnabled()) return '';
|
|
98
96
|
try {
|
|
99
97
|
return await loadCoreMemoryContext();
|
|
100
98
|
} catch {
|
|
@@ -10,12 +10,16 @@ import { createSession } from '../runtime/agent/orchestrator/session/manager/ses
|
|
|
10
10
|
import { askSession } from '../runtime/agent/orchestrator/session/manager/ask-session.mjs';
|
|
11
11
|
import { modelRouteFields, parseScheduleModelRef } from '../runtime/shared/schedule-model-ref.mjs';
|
|
12
12
|
import { automationWorkflowOpts } from './automation-workflow.mjs';
|
|
13
|
+
import { runAutomationTurns } from './automation-agents.mjs';
|
|
13
14
|
import { automationPromptContent } from '../runtime/shared/automation-attachments.mjs';
|
|
14
15
|
|
|
15
16
|
// Webhook payloads are authenticated transport data, not trusted user intent.
|
|
16
17
|
// Keep their sessions on the deterministic read-only bundle even when a
|
|
17
|
-
// preset or workflow would otherwise resolve to full tools.
|
|
18
|
-
|
|
18
|
+
// preset or workflow would otherwise resolve to full tools. `tools:mcp` adds
|
|
19
|
+
// only the automation scope's internal tools — the agent tool, offered when the
|
|
20
|
+
// user's orchestration mode allows delegation (user decision: automations may
|
|
21
|
+
// delegate); the scope connects no MCP servers.
|
|
22
|
+
export const WEBHOOK_SESSION_TOOLS = Object.freeze(['tools:readonly', 'tools:mcp']);
|
|
19
23
|
|
|
20
24
|
/** Endpoint model ref wins; the maintenance.webhook route is the fallback. */
|
|
21
25
|
function webhookRoute(modelRef) {
|
|
@@ -66,14 +70,9 @@ export async function runWebhookSession({
|
|
|
66
70
|
// askOpts.signal is the parent-abort link: it is cascaded onto the turn
|
|
67
71
|
// controller, so aborting it stops the in-flight provider/tool work instead
|
|
68
72
|
// of leaving a timed-out delivery's session running with live side effects.
|
|
69
|
-
const
|
|
70
|
-
session.id,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
null,
|
|
74
|
-
projectCwd || undefined,
|
|
75
|
-
undefined,
|
|
76
|
-
signal ? { signal } : {}
|
|
77
|
-
);
|
|
73
|
+
const ask = (turnContent) =>
|
|
74
|
+
askSession(session.id, turnContent, null, null, projectCwd || undefined, undefined, signal ? { signal } : {});
|
|
75
|
+
// Agents the run delegates to report back; each result is the next turn.
|
|
76
|
+
const result = await runAutomationTurns(session.id, () => ask(content), ask);
|
|
78
77
|
return { sessionId: session.id, result: String(result?.content || '') };
|
|
79
78
|
}
|
|
@@ -73,6 +73,11 @@ export function createAgentRouteApi(deps) {
|
|
|
73
73
|
const keepOff = requested.disabled === true;
|
|
74
74
|
if (keepOff && !(clean(requested.provider) && clean(requested.model))) {
|
|
75
75
|
saveDisabled(id, true);
|
|
76
|
+
// An explicitly empty provider still asks to drop the override, as it
|
|
77
|
+
// does for an agent that stays on: off and inheriting Main.
|
|
78
|
+
if (hasOwn(requested, 'provider') && !clean(requested.provider)) {
|
|
79
|
+
return { ...clearOverride(id), id, disabled: true };
|
|
80
|
+
}
|
|
76
81
|
return { ...stored, id, disabled: true };
|
|
77
82
|
}
|
|
78
83
|
if (requested.disabled === false && Object.keys(requested).length === 1) {
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
// this control exists — the host takes `execute` as a constructor option), and
|
|
9
9
|
// `cwd`. Output: { canonicalAgentTool, execute }.
|
|
10
10
|
|
|
11
|
+
import { deliverAutomationCompletion } from '../session-runtime/automation-agents.mjs';
|
|
12
|
+
|
|
11
13
|
export function createCanonicalAgentControl({ getSessionService, getSessionRuntimeHost, cwd }) {
|
|
12
14
|
let canonicalAgentToolPromise = null;
|
|
13
15
|
|
|
@@ -32,7 +34,12 @@ export function createCanonicalAgentControl({ getSessionService, getSessionRunti
|
|
|
32
34
|
isKeychainPrewarmReady: () => true,
|
|
33
35
|
sessionSurface: sessionService.agentSurface,
|
|
34
36
|
notifySessionCompletion(ownerSessionId, text, meta = {}) {
|
|
35
|
-
|
|
37
|
+
// A schedule/webhook run owns no session runtime; its own
|
|
38
|
+
// agent-result waiter takes the completions it is owed.
|
|
39
|
+
return (
|
|
40
|
+
getSessionRuntimeHost()?.notifySessionCompletion?.(ownerSessionId, text, meta) === true ||
|
|
41
|
+
deliverAutomationCompletion(ownerSessionId, text, meta)
|
|
42
|
+
);
|
|
36
43
|
},
|
|
37
44
|
});
|
|
38
45
|
})
|
|
@@ -71,6 +71,7 @@ import { createLagProfiler } from './daemon-lag-profiler.mjs';
|
|
|
71
71
|
import { createChannelsRuntimeLoader } from './daemon-channels-loader.mjs';
|
|
72
72
|
import { createDesktopRuntime } from './daemon-desktop-runtime.mjs';
|
|
73
73
|
import { createCanonicalAgentControl } from './daemon-agent-control.mjs';
|
|
74
|
+
import { registerAutomationAgentTool } from '../session-runtime/automation-agents.mjs';
|
|
74
75
|
import {
|
|
75
76
|
compareRuntimeVersions,
|
|
76
77
|
SESSION_CAPABILITY_FINGERPRINT,
|
|
@@ -666,6 +667,9 @@ async function main() {
|
|
|
666
667
|
getSessionRuntimeHost: () => sessionRuntimeHost,
|
|
667
668
|
cwd: CWD,
|
|
668
669
|
});
|
|
670
|
+
// Schedules and webhooks run headless here; their sessions reach the same
|
|
671
|
+
// canonical agent control through the automation tool scope.
|
|
672
|
+
registerAutomationAgentTool(agentControl.execute);
|
|
669
673
|
const requestedRuntimeMode = String(process.env.MIXDOG_SESSION_RUNTIME_MODE || '')
|
|
670
674
|
.trim()
|
|
671
675
|
.toLowerCase();
|
package/src/tui/dist/index.mjs
CHANGED
|
@@ -44194,7 +44194,6 @@ import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:pa
|
|
|
44194
44194
|
import { homedir } from "node:os";
|
|
44195
44195
|
import { dirname, join } from "node:path";
|
|
44196
44196
|
import { fileURLToPath } from "node:url";
|
|
44197
|
-
var DEFAULT_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
44198
44197
|
var homeBase = null;
|
|
44199
44198
|
var homePath = null;
|
|
44200
44199
|
var dataBase = null;
|
|
@@ -47687,7 +47686,9 @@ var LITELLM_FLAG_FIELDS = [
|
|
|
47687
47686
|
"supports_web_search",
|
|
47688
47687
|
"supports_websearch",
|
|
47689
47688
|
"supports_prompt_caching",
|
|
47690
|
-
"supports_reasoning"
|
|
47689
|
+
"supports_reasoning",
|
|
47690
|
+
// Reader: supportsAnthropicFastMode (anthropic-betas.mjs).
|
|
47691
|
+
"supports_fast_mode"
|
|
47691
47692
|
];
|
|
47692
47693
|
var LITELLM_STRING_FIELDS = ["mode", "litellm_provider", "reasoning_content_field"];
|
|
47693
47694
|
function projectLitellmRow(row) {
|
|
@@ -15,8 +15,15 @@ export function createNotificationRouter({
|
|
|
15
15
|
modelNotification,
|
|
16
16
|
}) {
|
|
17
17
|
let uiOpenSeq = 0;
|
|
18
|
+
let setupChangeSeq = 0;
|
|
18
19
|
const uiActions = {
|
|
19
20
|
'setup-ui': (delivery) => set({ setupUiRequest: { id: delivery.id, at: Date.now() } }),
|
|
21
|
+
'setup-changed': (delivery) => {
|
|
22
|
+
// Monotonic like ui-open: consecutive changes by the same action must
|
|
23
|
+
// each refresh the attached surfaces.
|
|
24
|
+
setupChangeSeq += 1;
|
|
25
|
+
set({ setupChanged: { action: delivery.setupAction, seq: setupChangeSeq, at: Date.now() } });
|
|
26
|
+
},
|
|
20
27
|
'ui-open': (delivery) => {
|
|
21
28
|
// Monotonic seq: the same command requested twice must fire twice, and
|
|
22
29
|
// the consuming App effect keys on the object identity + seq.
|
|
@@ -13,9 +13,17 @@
|
|
|
13
13
|
* pipe is down. Ownership/promotion semantics are unchanged — the pipe is a
|
|
14
14
|
* transport, never a second writer.
|
|
15
15
|
*/
|
|
16
|
+
import { createHash } from 'node:crypto';
|
|
17
|
+
import { join } from 'node:path';
|
|
16
18
|
import { createOwnerLeg } from './live-share/owner.mjs';
|
|
17
19
|
import { createViewerLeg } from './live-share/viewer.mjs';
|
|
18
20
|
|
|
21
|
+
// A unix socket path is a fixed sun_path buffer: 104 bytes on macOS and the
|
|
22
|
+
// BSDs, 108 on Linux, NUL included. A desktop session id alone is 64 hex, so
|
|
23
|
+
// the path beside its session file runs past the macOS limit and listen()
|
|
24
|
+
// failed there for every desktop session.
|
|
25
|
+
const UNIX_SOCKET_PATH_MAX_BYTES = 103;
|
|
26
|
+
|
|
19
27
|
// Session entry is latency-sensitive: the owner pipe can be a few event-loop
|
|
20
28
|
// turns behind the viewer resume. Retry locally instead of waiting for the
|
|
21
29
|
// coarse 3s session runtime safety tick.
|
|
@@ -23,7 +31,14 @@ const LIVE_CONNECT_RETRY_MIN_MS = 10;
|
|
|
23
31
|
const LIVE_CONNECT_RETRY_MAX_MS = 160;
|
|
24
32
|
|
|
25
33
|
export function liveSharePipePath(sessionId, sessionFilePath) {
|
|
26
|
-
|
|
34
|
+
if (process.platform === 'win32') return `\\\\.\\pipe\\mixdog-live-${sessionId}`;
|
|
35
|
+
const beside = `${sessionFilePath}.live.sock`;
|
|
36
|
+
if (Buffer.byteLength(beside) <= UNIX_SOCKET_PATH_MAX_BYTES) return beside;
|
|
37
|
+
// Owner and viewer derive the same short name from the same session file.
|
|
38
|
+
// /tmp, not os.tmpdir(): TMPDIR is itself long on macOS and may differ
|
|
39
|
+
// between the desktop daemon and a terminal, which would split the pair.
|
|
40
|
+
const digest = createHash('sha256').update(beside).digest('hex').slice(0, 24);
|
|
41
|
+
return join('/tmp', `mixdog-live-${process.getuid()}-${digest}.sock`);
|
|
27
42
|
}
|
|
28
43
|
|
|
29
44
|
export function createLiveShare({
|
|
@@ -89,6 +89,10 @@ export function resolveTuiRuntimeNotificationDelivery(event, text) {
|
|
|
89
89
|
if (meta.kind === 'setup-ui') {
|
|
90
90
|
return meta.id ? { action: 'setup-ui', id: String(meta.id) } : { action: 'ignore' };
|
|
91
91
|
}
|
|
92
|
+
if (meta.kind === 'setup-changed') {
|
|
93
|
+
const setupAction = String(meta.action || '').trim();
|
|
94
|
+
return setupAction ? { action: 'setup-changed', setupAction } : { action: 'ignore' };
|
|
95
|
+
}
|
|
92
96
|
if (meta.kind === 'ui-open') {
|
|
93
97
|
const command = String(meta.command || '')
|
|
94
98
|
.trim()
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
// One approval per session before the first live browser or desktop call.
|
|
2
|
-
//
|
|
3
|
-
// Claude Code asks per app and per session before it controls a screen; here
|
|
4
|
-
// the ask is per capability (Browser Use, Computer Use), answered once, and
|
|
5
|
-
// remembered for the session in memory only, so a restart asks again. Without
|
|
6
|
-
// an approval UI (headless runs, agent-owned sessions) nobody can answer, so
|
|
7
|
-
// the gate stands aside and the workflow's own policies govern.
|
|
8
|
-
import { approvalGranted, approvalReason } from '../runtime/agent/orchestrator/session/loop/tool-helpers.mjs';
|
|
9
|
-
import { builtinFirstUseApproval } from '../runtime/agent/orchestrator/runtime-core/builtin-features.mjs';
|
|
10
|
-
|
|
11
|
-
const FAMILY_BY_TOOL = Object.freeze({
|
|
12
|
-
browser: 'browser',
|
|
13
|
-
browser_devtools: 'browser',
|
|
14
|
-
computer: 'computer',
|
|
15
|
-
});
|
|
16
|
-
const FAMILY_LABEL = Object.freeze({
|
|
17
|
-
browser: 'Browser Use',
|
|
18
|
-
computer: 'Computer Use',
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export function bridgeToolFamily(name) {
|
|
22
|
-
return FAMILY_BY_TOOL[String(name || '')] || null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function callSummary(args) {
|
|
26
|
-
const action = typeof args?.action === 'string' ? args.action : '';
|
|
27
|
-
const input = args?.input && typeof args.input === 'object' ? args.input : args;
|
|
28
|
-
const target = input?.url || input?.app || input?.window_id || input?.tab || '';
|
|
29
|
-
return (
|
|
30
|
-
[action, target]
|
|
31
|
-
.filter(Boolean)
|
|
32
|
-
.map((part) => String(part).slice(0, 120))
|
|
33
|
-
.join(' ') || 'call'
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** `getConfig` reads the live profile; the returned `gate` resolves to null
|
|
38
|
-
* when the call may proceed, or to the error text the model receives. */
|
|
39
|
-
export function createBridgeFirstUseGate({ getConfig }) {
|
|
40
|
-
const granted = new Set();
|
|
41
|
-
return async function gate({ name, args, cwd, sessionId, toolCallId, toolApprovalHook, invocationSource }) {
|
|
42
|
-
const family = bridgeToolFamily(name);
|
|
43
|
-
if (!family || invocationSource !== 'model-tool') return null;
|
|
44
|
-
if (typeof toolApprovalHook !== 'function') return null;
|
|
45
|
-
if (!builtinFirstUseApproval(getConfig?.(), family)) return null;
|
|
46
|
-
const key = `${String(sessionId || '')}:${family}`;
|
|
47
|
-
if (granted.has(key)) return null;
|
|
48
|
-
const label = FAMILY_LABEL[family];
|
|
49
|
-
const reason = `first ${label} call in this session (${callSummary(args)}); allowing it covers the rest of the session`;
|
|
50
|
-
let approval;
|
|
51
|
-
try {
|
|
52
|
-
approval = await toolApprovalHook({
|
|
53
|
-
name,
|
|
54
|
-
args,
|
|
55
|
-
cwd,
|
|
56
|
-
sessionId: sessionId || null,
|
|
57
|
-
toolCallId: toolCallId || null,
|
|
58
|
-
reason,
|
|
59
|
-
});
|
|
60
|
-
} catch (error) {
|
|
61
|
-
return `Error: ${label} first-use approval failed: ${error?.message || String(error || 'approval failed')}`;
|
|
62
|
-
}
|
|
63
|
-
if (!approvalGranted(approval)) {
|
|
64
|
-
return (
|
|
65
|
-
`Error: ${label} was not approved for this session: ${approvalReason(approval, 'the user declined')}. ` +
|
|
66
|
-
'Do not retry it; reach the goal another way or ask the user.'
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
granted.add(key);
|
|
70
|
-
return null;
|
|
71
|
-
};
|
|
72
|
-
}
|