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
|
@@ -12,13 +12,16 @@
|
|
|
12
12
|
* cache-node routing, so mixdog mirrors both.
|
|
13
13
|
*/
|
|
14
14
|
import os from 'node:os';
|
|
15
|
+
import { readLastKnownVersion, rememberLastKnownVersion } from './client-version-store.mjs';
|
|
15
16
|
|
|
16
|
-
// Offline fallback only; live value refreshes from npm (24h TTL
|
|
17
|
+
// Offline fallback only; live value refreshes from npm (24h TTL) and the last
|
|
18
|
+
// live answer is kept on disk, so the floor is only a first-run default.
|
|
17
19
|
// The backend gates model exposure AND per-request model access on the client
|
|
18
20
|
// version (gpt-6-sol/luna require >= 0.155.0 per the published model catalog,
|
|
19
21
|
// verified 2026-09-22), so keep this at the current release when bumping.
|
|
20
22
|
const CODEX_CLIENT_VERSION_FLOOR = '0.155.1';
|
|
21
23
|
const VERSION_TTL_MS = 24 * 60 * 60_000;
|
|
24
|
+
const LAST_KNOWN_KEY = 'codex-cli';
|
|
22
25
|
let _cache = { value: null, fetchedAt: 0 };
|
|
23
26
|
let _refreshInFlight = null;
|
|
24
27
|
|
|
@@ -32,14 +35,16 @@ async function _refresh() {
|
|
|
32
35
|
const v = String(j?.version || '').trim();
|
|
33
36
|
if (/^\d+\.\d+\.\d+/.test(v)) {
|
|
34
37
|
_cache = { value: v, fetchedAt: Date.now() };
|
|
38
|
+
rememberLastKnownVersion(LAST_KNOWN_KEY, v);
|
|
35
39
|
return v;
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
} catch {
|
|
39
43
|
/* offline — keep floor */
|
|
40
44
|
}
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
const fallback = _offlineVersion();
|
|
46
|
+
_cache = { value: fallback, fetchedAt: Date.now() };
|
|
47
|
+
return fallback;
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
/**
|
|
@@ -50,7 +55,25 @@ async function _refresh() {
|
|
|
50
55
|
function codexClientVersionSync() {
|
|
51
56
|
if (_cacheFresh()) return _cache.value;
|
|
52
57
|
_ensureRefresh();
|
|
53
|
-
return _cache.value ||
|
|
58
|
+
return _cache.value || _offlineVersion();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The newest version this machine has seen, never below the shipped floor.
|
|
62
|
+
function _offlineVersion() {
|
|
63
|
+
const lastKnown = readLastKnownVersion(LAST_KNOWN_KEY);
|
|
64
|
+
const newer = lastKnown && _compareVersion(lastKnown, CODEX_CLIENT_VERSION_FLOOR) > 0;
|
|
65
|
+
return newer ? lastKnown : CODEX_CLIENT_VERSION_FLOOR;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Numeric x.y.z order; a pre-release suffix on the patch part is ignored.
|
|
69
|
+
function _compareVersion(a, b) {
|
|
70
|
+
const parts = (version) => String(version).split('.').map((part) => Number.parseInt(part, 10) || 0);
|
|
71
|
+
const [pa, pb] = [parts(a), parts(b)];
|
|
72
|
+
for (let i = 0; i < 3; i += 1) {
|
|
73
|
+
const delta = (pa[i] || 0) - (pb[i] || 0);
|
|
74
|
+
if (delta) return delta;
|
|
75
|
+
}
|
|
76
|
+
return 0;
|
|
54
77
|
}
|
|
55
78
|
|
|
56
79
|
function _cacheFresh() {
|
|
@@ -4,13 +4,17 @@ import { createRemoteVersionSource } from './npm-cli-version.mjs';
|
|
|
4
4
|
// documented version endpoint; the official installer script embeds the
|
|
5
5
|
// current build (…/lab/<build>/…), parsed best-effort. Any format change or
|
|
6
6
|
// failure falls back to the floor.
|
|
7
|
-
export const CURSOR_CLIENT_VERSION_FLOOR = 'cli-2026.
|
|
7
|
+
export const CURSOR_CLIENT_VERSION_FLOOR = 'cli-2026.10.01-e373342';
|
|
8
8
|
const BUILD_PATTERN = /downloads\.cursor\.com\/lab\/(\d{4}\.\d{2}\.\d{2}-[0-9a-f]{6,40})\//;
|
|
9
9
|
|
|
10
|
-
const live = createRemoteVersionSource(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const live = createRemoteVersionSource(
|
|
11
|
+
'https://cursor.com/install',
|
|
12
|
+
async (res) => {
|
|
13
|
+
const match = (await res.text()).match(BUILD_PATTERN);
|
|
14
|
+
return match ? `cli-${match[1]}` : null;
|
|
15
|
+
},
|
|
16
|
+
{ persistKey: 'cursor-cli' }
|
|
17
|
+
);
|
|
14
18
|
let envVersion;
|
|
15
19
|
|
|
16
20
|
function envOverride() {
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { normalizeAnthropicEffortInput } from './anthropic-effort.mjs';
|
|
2
|
+
import { codexModelSupportsEffortUpdates } from './openai-oauth-catalog.mjs';
|
|
2
3
|
|
|
3
4
|
export const EFFORT_CONFIGURATION_BETA = 'mid-conversation-output-config-2026-07-01';
|
|
4
|
-
const ANTHROPIC_MODELS = new Set([
|
|
5
|
+
const ANTHROPIC_MODELS = new Set([
|
|
6
|
+
'claude-fable-5-1',
|
|
7
|
+
'claude-mythos-5-1',
|
|
8
|
+
'claude-opus-5',
|
|
9
|
+
'claude-opus-5-5',
|
|
10
|
+
'claude-sonnet-5-5',
|
|
11
|
+
]);
|
|
5
12
|
// https://developers.openai.com/api/docs/guides/reasoning#change-reasoning-mid-conversation
|
|
6
|
-
// — the GPT-6 model family, standard single-agent mode.
|
|
7
|
-
|
|
13
|
+
// — the GPT-6 model family, standard single-agent mode. The public API
|
|
14
|
+
// publishes no per-model flag, so this list is its whole answer; the OAuth
|
|
15
|
+
// backend's catalog carries one and only falls back here without it.
|
|
16
|
+
const OPENAI_MODELS = new Set(['gpt-6-astra', 'gpt-6-sol', 'gpt-6-luna', 'gpt-6-1-sol']);
|
|
8
17
|
const OPENAI_PROVIDERS = new Set(['openai', 'openai-oauth']);
|
|
9
18
|
const ANTHROPIC_PROVIDERS = new Set(['anthropic', 'anthropic-oauth']);
|
|
10
19
|
const EFFORTS = new Set(['minimal', 'low', 'medium', 'high', 'xhigh', 'max']);
|
|
@@ -35,11 +44,16 @@ function modelKey(model) {
|
|
|
35
44
|
.replace(/\./g, '-');
|
|
36
45
|
}
|
|
37
46
|
|
|
47
|
+
function openAiSupportsEffortUpdates(provider, model, id) {
|
|
48
|
+
const declared = provider === 'openai-oauth' ? codexModelSupportsEffortUpdates(String(model || '').trim()) : null;
|
|
49
|
+
return declared ?? OPENAI_MODELS.has(id);
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
// Explicit protocol capabilities, not a prediction about future model families.
|
|
39
53
|
export function effortConfigurationMode(provider, model, opts = {}) {
|
|
40
54
|
const id = modelKey(model);
|
|
41
55
|
if (opts.effortConfigurationEnabled === false || Number(opts.thinkingBudgetTokens) > 0) return null;
|
|
42
|
-
if (OPENAI_PROVIDERS.has(provider) &&
|
|
56
|
+
if (OPENAI_PROVIDERS.has(provider) && openAiSupportsEffortUpdates(provider, model, id)) {
|
|
43
57
|
const parameters = opts.modelParameters || {};
|
|
44
58
|
const mode = opts.reasoning?.mode ?? parameters.reasoning_mode ?? parameters.mode ?? 'standard';
|
|
45
59
|
if (mode !== 'standard' || opts.multiAgent === true || parameters.multi_agent === true) return null;
|
|
@@ -3,9 +3,9 @@ import { createNpmVersionSource, maxSemver } from './npm-cli-version.mjs';
|
|
|
3
3
|
// Grok CLI client version for the cli-chat-proxy version gate (HTTP 426).
|
|
4
4
|
// Effective = env override, else max(floor, learned-from-426, live npm latest
|
|
5
5
|
// of the official @xai-official/grok CLI).
|
|
6
|
-
export const GROK_CLI_VERSION_FLOOR = '0.
|
|
6
|
+
export const GROK_CLI_VERSION_FLOOR = '1.0.46';
|
|
7
7
|
|
|
8
|
-
const live = createNpmVersionSource('@xai-official/grok');
|
|
8
|
+
const live = createNpmVersionSource('@xai-official/grok', { persistKey: 'grok-cli' });
|
|
9
9
|
let envVersion;
|
|
10
10
|
let learned = null;
|
|
11
11
|
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
export const MODELS = [
|
|
4
4
|
{ id: 'claude-opus-5-5', name: 'Claude Opus 5.5', provider: 'anthropic', family: 'opus', contextWindow: 1000000 },
|
|
5
5
|
{ id: 'claude-fable-5-1', name: 'Claude Fable 5.1', provider: 'anthropic', family: 'fable', contextWindow: 1000000 },
|
|
6
|
-
{
|
|
6
|
+
{
|
|
7
|
+
id: 'claude-sonnet-5-5',
|
|
8
|
+
name: 'Claude Sonnet 5.5',
|
|
9
|
+
provider: 'anthropic',
|
|
10
|
+
family: 'sonnet',
|
|
11
|
+
contextWindow: 1000000,
|
|
12
|
+
},
|
|
7
13
|
];
|
|
8
14
|
export const ANTHROPIC_VERSION = '2023-06-01';
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
// Sync accessor never blocks (kicks a background refresh); warm() is the
|
|
4
4
|
// awaitable cold-start path. Failures resolve to null so callers fall back to
|
|
5
5
|
// their floor. MIXDOG_DISABLE_LIVE_CLI_VERSIONS=1 turns the lookup off.
|
|
6
|
+
//
|
|
7
|
+
// A source given a `persistKey` also remembers its last live answer on disk,
|
|
8
|
+
// so a new process (or an offline one) starts from the newest version this
|
|
9
|
+
// machine has seen rather than from a floor that only moves with a release.
|
|
10
|
+
|
|
11
|
+
import { readLastKnownVersion, rememberLastKnownVersion } from './client-version-store.mjs';
|
|
6
12
|
|
|
7
13
|
const SEMVER = /^\d+\.\d+\.\d+$/;
|
|
8
14
|
const DEFAULT_TTL_MS = 24 * 60 * 60_000;
|
|
@@ -43,10 +49,22 @@ export function createNpmVersionSource(pkg, options) {
|
|
|
43
49
|
* Generic source: GET `url`, `parse(res)` returns the version string or null
|
|
44
50
|
* (unrecognised format). Same TTL/in-flight/timeout/disable rules as npm.
|
|
45
51
|
*/
|
|
46
|
-
export function createRemoteVersionSource(
|
|
52
|
+
export function createRemoteVersionSource(
|
|
53
|
+
url,
|
|
54
|
+
parse,
|
|
55
|
+
{ ttlMs = DEFAULT_TTL_MS, timeoutMs = TIMEOUT_MS, persistKey = null } = {}
|
|
56
|
+
) {
|
|
47
57
|
let value = null;
|
|
48
58
|
let expiresAt = 0;
|
|
49
59
|
let inFlight = null;
|
|
60
|
+
let lastKnownLoaded = false;
|
|
61
|
+
|
|
62
|
+
// Stale on purpose: it answers until the refresh lands, never instead of it.
|
|
63
|
+
function loadLastKnown() {
|
|
64
|
+
if (lastKnownLoaded) return;
|
|
65
|
+
lastKnownLoaded = true;
|
|
66
|
+
if (!value) value = readLastKnownVersion(persistKey);
|
|
67
|
+
}
|
|
50
68
|
|
|
51
69
|
async function refresh() {
|
|
52
70
|
let next = null;
|
|
@@ -56,7 +74,10 @@ export function createRemoteVersionSource(url, parse, { ttlMs = DEFAULT_TTL_MS,
|
|
|
56
74
|
} catch {
|
|
57
75
|
/* offline or slow — keep previous/floor */
|
|
58
76
|
}
|
|
59
|
-
if (next)
|
|
77
|
+
if (next) {
|
|
78
|
+
value = next;
|
|
79
|
+
rememberLastKnownVersion(persistKey, next);
|
|
80
|
+
}
|
|
60
81
|
expiresAt = Date.now() + (next ? ttlMs : FAILURE_TTL_MS);
|
|
61
82
|
return value;
|
|
62
83
|
}
|
|
@@ -76,11 +97,13 @@ export function createRemoteVersionSource(url, parse, { ttlMs = DEFAULT_TTL_MS,
|
|
|
76
97
|
return {
|
|
77
98
|
/** Cached live version or null; refreshes in the background when stale. */
|
|
78
99
|
sync() {
|
|
100
|
+
loadLastKnown();
|
|
79
101
|
if (!fresh()) ensureRefresh();
|
|
80
102
|
return value;
|
|
81
103
|
},
|
|
82
104
|
/** Awaitable, never rejects; bounded by the fetch timeout. */
|
|
83
105
|
warm() {
|
|
106
|
+
loadLastKnown();
|
|
84
107
|
return fresh() ? Promise.resolve(value) : ensureRefresh();
|
|
85
108
|
},
|
|
86
109
|
};
|
|
@@ -154,7 +154,13 @@ function openAIOAuthState() {
|
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
function grokOAuthState() {
|
|
157
|
-
|
|
157
|
+
// Same account/path precedence as the token store (getOwnTokenPath): a login
|
|
158
|
+
// lands in the selected account's pool file, never the legacy root file.
|
|
159
|
+
const paths = [
|
|
160
|
+
boundProviderAuthPath('grok-oauth') ||
|
|
161
|
+
process.env.GROK_OAUTH_CREDENTIALS_PATH ||
|
|
162
|
+
join(resolvePluginData(), 'grok-oauth.json'),
|
|
163
|
+
];
|
|
158
164
|
return memoProbe('grok-oauth', paths, () =>
|
|
159
165
|
resolveProbeState(paths, (docs) => docs.some((own) => !!(own?.access_token && own?.refresh_token)))
|
|
160
166
|
);
|
|
@@ -86,6 +86,10 @@ export function _normalizeCodexModel(m) {
|
|
|
86
86
|
supportVerbosity: m?.support_verbosity === true,
|
|
87
87
|
defaultVerbosity: m?.default_verbosity || null,
|
|
88
88
|
supportsReasoningSummaries: m?.supports_reasoning_summaries === true,
|
|
89
|
+
// Absent on an older catalog: the caller then falls back to its own list.
|
|
90
|
+
...(typeof m?.supports_reasoning_effort_updates === 'boolean'
|
|
91
|
+
? { supportsReasoningEffortUpdates: m.supports_reasoning_effort_updates }
|
|
92
|
+
: {}),
|
|
89
93
|
...(typeof m?.supports_image_generation === 'boolean'
|
|
90
94
|
? { supportsImageGeneration: m.supports_image_generation }
|
|
91
95
|
: {}),
|
|
@@ -14,6 +14,7 @@ import { appendAgentTrace } from '../agent-trace.mjs';
|
|
|
14
14
|
import { resolveProviderCacheKey, resolveProviderPromptCacheLane } from '../agent-runtime/cache-strategy.mjs';
|
|
15
15
|
import { shouldFallbackTransport } from './retry-classifier.mjs';
|
|
16
16
|
import { envFlag as _envFlag } from '../../../shared/env.mjs';
|
|
17
|
+
import { getModelsDevRowSync } from '../../../shared/llm/model-catalog.mjs';
|
|
17
18
|
import {
|
|
18
19
|
traceHash,
|
|
19
20
|
stableTraceStringify,
|
|
@@ -161,6 +162,13 @@ export function xaiModelSupportsReasoningEffort(model) {
|
|
|
161
162
|
.toLowerCase();
|
|
162
163
|
if (!id) return true;
|
|
163
164
|
if (!id.startsWith('grok')) return true;
|
|
165
|
+
// The catalog answers first: a listed model takes the parameter exactly
|
|
166
|
+
// when it publishes effort values (grok-4.3 does, grok-4.20-*-reasoning
|
|
167
|
+
// does not), so a new release needs no edit here.
|
|
168
|
+
const listed = getModelsDevRowSync(id, 'xai');
|
|
169
|
+
if (listed) {
|
|
170
|
+
return (listed.reasoning_options || []).some((option) => option?.type === 'effort' && option.values?.length > 0);
|
|
171
|
+
}
|
|
164
172
|
// grok-4.5 / grok-4.6 and anything newer in that line.
|
|
165
173
|
return /grok[-_]?4\.(?:[5-9]|\d{2,})/.test(id);
|
|
166
174
|
}
|
|
@@ -1,13 +1,27 @@
|
|
|
1
|
+
import { getModelMetadataSync } from '../../../shared/llm/model-catalog.mjs';
|
|
2
|
+
|
|
1
3
|
// Public Responses contracts are independent of the OAuth backend.
|
|
2
4
|
// Match documented model IDs and dated snapshots, not unknown future families.
|
|
3
5
|
// GPT-5.6 and later: prompt_cache_options caching and Fast mode.
|
|
4
|
-
const GPT_56_PLUS_MODELS =
|
|
6
|
+
const GPT_56_PLUS_MODELS =
|
|
7
|
+
/^(?:gpt-6-(?:astra|sol|luna)|gpt-6\.1-sol|gpt-5\.6-(?:sol|terra|luna))(?:-\d{4}-\d{2}-\d{2})?$/;
|
|
8
|
+
|
|
9
|
+
// OpenAI bills prompt-cache writes from GPT-5.6 on, and exactly those models
|
|
10
|
+
// take prompt_cache_options and Fast mode. The id pattern is the offline
|
|
11
|
+
// floor; a published cache-write price admits a later model without an edit
|
|
12
|
+
// here, and a family no catalog lists yet stays out.
|
|
13
|
+
// https://developers.openai.com/api/docs/guides/prompt-caching
|
|
14
|
+
function isGpt56PlusModel(model) {
|
|
15
|
+
const id = String(model || '').trim();
|
|
16
|
+
if (!id) return false;
|
|
17
|
+
return GPT_56_PLUS_MODELS.test(id) || getModelMetadataSync(id, 'openai')?.cacheWriteCostPerM > 0;
|
|
18
|
+
}
|
|
5
19
|
// Earlier models documented for Priority processing (now Fast mode).
|
|
6
20
|
const EARLIER_FAST_MODELS = /^gpt-5\.(?:5|4|4-mini)(?:-\d{4}|$)/;
|
|
7
21
|
|
|
8
22
|
export function openAiDirectSupportsFast(model) {
|
|
9
23
|
const id = String(model?.id || model || '').trim();
|
|
10
|
-
return
|
|
24
|
+
return isGpt56PlusModel(id) || EARLIER_FAST_MODELS.test(id);
|
|
11
25
|
}
|
|
12
26
|
|
|
13
27
|
export function applyOpenAIDirectCachePolicy(body, model, storeResponses) {
|
|
@@ -17,7 +31,7 @@ export function applyOpenAIDirectCachePolicy(body, model, storeResponses) {
|
|
|
17
31
|
// Preserve the existing opt-out: no explicit cache-retention hint when
|
|
18
32
|
// response storage is disabled. The provider's default cache still applies.
|
|
19
33
|
if (!storeResponses) return body;
|
|
20
|
-
if (
|
|
34
|
+
if (isGpt56PlusModel(model)) {
|
|
21
35
|
body.prompt_cache_options = { ttl: '30m' };
|
|
22
36
|
} else {
|
|
23
37
|
body.prompt_cache_retention = '24h';
|
|
@@ -21,7 +21,7 @@ import { CODEX_OAUTH_ORIGINATOR, codexModelsUrl } from './openai-codex-endpoints
|
|
|
21
21
|
import { _normalizeCodexModel, _markLatestCodex } from './openai-codex-model.mjs';
|
|
22
22
|
|
|
23
23
|
const CODEX_MODEL_CACHE_TTL_MS = 24 * 60 * 60_000;
|
|
24
|
-
const CODEX_MODEL_CACHE_SCHEMA_VERSION =
|
|
24
|
+
const CODEX_MODEL_CACHE_SCHEMA_VERSION = 6;
|
|
25
25
|
const CATALOG_FETCH_TIMEOUT_MS = 10_000;
|
|
26
26
|
|
|
27
27
|
// In-memory mirror of the on-disk catalog, same pattern as anthropic-oauth.
|
|
@@ -59,6 +59,12 @@ export function codexCatalogHas(id) {
|
|
|
59
59
|
return _mirror.some((m) => m.id === id);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/** The catalog's own answer, or null when it has none for this model. */
|
|
63
|
+
export function codexModelSupportsEffortUpdates(id) {
|
|
64
|
+
const supported = findCachedCodexModel(id)?.supportsReasoningEffortUpdates;
|
|
65
|
+
return typeof supported === 'boolean' ? supported : null;
|
|
66
|
+
}
|
|
67
|
+
|
|
62
68
|
export function codexModelSupportsServiceTier(id, serviceTier) {
|
|
63
69
|
return modelSupportsServiceTier(findCachedCodexModel(id), serviceTier);
|
|
64
70
|
}
|
|
@@ -469,6 +469,22 @@ const CONTEXT_OVERFLOW_PATTERNS = [
|
|
|
469
469
|
/context[_ ]length[_ ]exceeded/i,
|
|
470
470
|
/prompt is too long/i,
|
|
471
471
|
/reduce the length of (?:the )?(?:messages|input|prompt)/i,
|
|
472
|
+
// Wordings other backends use for the same refusal, each tied to a context
|
|
473
|
+
// or input-length noun so a tokens-per-minute rate limit never matches.
|
|
474
|
+
/prompt too long/i, // z.ai, Ollama
|
|
475
|
+
/prompt exceeds max length/i, // z.ai CN endpoint
|
|
476
|
+
/input is too long for requested model/i, // Amazon Bedrock
|
|
477
|
+
/input token count.*exceeds the maximum/i, // Google
|
|
478
|
+
/maximum prompt length is \d+/i, // xAI
|
|
479
|
+
/exceeds (?:the )?maximum allowed input length/i, // OpenRouter relays
|
|
480
|
+
/is longer than the model'?s context length/i, // Together AI
|
|
481
|
+
/exceeds the available context size/i, // llama.cpp server
|
|
482
|
+
/greater than the context length/i, // LM Studio
|
|
483
|
+
/context window exceeds limit/i, // MiniMax
|
|
484
|
+
/exceeded model token limit/i, // Kimi
|
|
485
|
+
/too large for model with \d+ maximum context length/i, // Mistral
|
|
486
|
+
/range of input length should be/i, // DashScope / Qwen
|
|
487
|
+
/model_context_window_exceeded/i, // z.ai finish reason surfaced as text
|
|
472
488
|
];
|
|
473
489
|
|
|
474
490
|
/**
|
|
@@ -148,26 +148,6 @@ export function localGitToolsActive(configLike, { gitAvailable } = {}) {
|
|
|
148
148
|
);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
/** Capabilities that ask the user once per session before their first live
|
|
152
|
-
* call: the desktop and the browser are the user's, and one approval at the
|
|
153
|
-
* moment of first use is how the user learns the model reached for them. */
|
|
154
|
-
export const BRIDGE_FIRST_USE_IDS = Object.freeze(['browser', 'computer']);
|
|
155
|
-
|
|
156
|
-
/** On unless the profile turns it off for that capability;
|
|
157
|
-
* MIXDOG_BRIDGE_FIRST_USE_APPROVAL overrides per process (headless, bench). */
|
|
158
|
-
export function builtinFirstUseApproval(configLike, id) {
|
|
159
|
-
return (
|
|
160
|
-
featureEnvOverride('MIXDOG_BRIDGE_FIRST_USE_APPROVAL') ?? configLike?.builtins?.[id]?.firstUseApproval !== false
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export function setBuiltinFirstUseApprovalInConfig(configLike, id, enabled) {
|
|
165
|
-
const next = { ...(configLike || {}) };
|
|
166
|
-
next.builtins = { ...(next.builtins || {}) };
|
|
167
|
-
next.builtins[id] = { ...(next.builtins[id] || {}), firstUseApproval: enabled !== false };
|
|
168
|
-
return next;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
151
|
export function setBuiltinInstalledInConfig(configLike, id, installed = true) {
|
|
172
152
|
const next = { ...(configLike || {}) };
|
|
173
153
|
next.builtins = { ...(next.builtins || {}) };
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { clean, hasOwn } from './session-text.mjs';
|
|
6
6
|
import { modelSupportsServiceTier } from '../providers/model-service-tiers.mjs';
|
|
7
7
|
import { openAiDirectSupportsFast } from '../providers/openai-direct-request.mjs';
|
|
8
|
+
import { supportsAnthropicFastMode } from '../../../shared/llm/anthropic-betas.mjs';
|
|
8
9
|
|
|
9
10
|
const FAST_CAPABLE_PROVIDERS = new Set([
|
|
10
11
|
'anthropic',
|
|
@@ -52,16 +53,11 @@ function geminiModelSupportsHostedWebSearch(model) {
|
|
|
52
53
|
function anthropicModelSupportsHostedWebSearch(model) {
|
|
53
54
|
const id = clean(model?.id || model).toLowerCase();
|
|
54
55
|
if (!id) return false;
|
|
55
|
-
const match = id.match(/^claude-(opus|sonnet|haiku)-(\d+)(?:[-.](\d+))?/);
|
|
56
|
+
const match = id.match(/^claude-(opus|sonnet|haiku|fable|mythos)-(\d+)(?:[-.](\d+))?/);
|
|
56
57
|
if (!match) return false;
|
|
57
58
|
return (Number(match[2]) || 0) >= 4;
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
function anthropicModelMetaSupportsFast(model) {
|
|
61
|
-
const id = clean(model?.id || model).toLowerCase();
|
|
62
|
-
return /^claude-(opus|sonnet)/.test(id);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
61
|
export function fastCapableFor(provider, model, effort = null, modelParameters = {}) {
|
|
66
62
|
const p = clean(provider);
|
|
67
63
|
if (!FAST_CAPABLE_PROVIDERS.has(p)) return false;
|
|
@@ -82,7 +78,7 @@ export function fastCapableFor(provider, model, effort = null, modelParameters =
|
|
|
82
78
|
}
|
|
83
79
|
if (p === 'openai') return openAiDirectSupportsFast(model);
|
|
84
80
|
if (p === 'openai-oauth') return modelSupportsServiceTier(model, 'priority');
|
|
85
|
-
if (p === 'anthropic' || p === 'anthropic-oauth') return
|
|
81
|
+
if (p === 'anthropic' || p === 'anthropic-oauth') return supportsAnthropicFastMode(clean(model?.id || model));
|
|
86
82
|
return false;
|
|
87
83
|
}
|
|
88
84
|
|
|
@@ -127,9 +123,15 @@ export function saveModelSettings(cfgMod, route, { fastCapable = true, baseConfi
|
|
|
127
123
|
} else {
|
|
128
124
|
delete nextSetting.modelParameters;
|
|
129
125
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
126
|
+
// Only a window the user moved off the model's default is a setting. The
|
|
127
|
+
// pickers send the default stop with every selection; storing it froze each
|
|
128
|
+
// model at whatever its default was that day, so a later default never
|
|
129
|
+
// reached anyone who had already selected the model.
|
|
130
|
+
const requestedPercent = Number(route.contextPercent);
|
|
131
|
+
const contextPercent = Math.round(requestedPercent / 10) * 10;
|
|
132
|
+
const isDefault = contextPercent === Number(route.contextDefaultPercent);
|
|
133
|
+
if (requestedPercent >= 10 && requestedPercent <= 100 && !isDefault) {
|
|
134
|
+
nextSetting.contextPercent = contextPercent;
|
|
133
135
|
} else {
|
|
134
136
|
delete nextSetting.contextPercent;
|
|
135
137
|
}
|
|
@@ -119,7 +119,9 @@ function summarySendOpts(opts) {
|
|
|
119
119
|
effort: 'low',
|
|
120
120
|
effortConfiguration: undefined,
|
|
121
121
|
effortConfigurationEnabled: false,
|
|
122
|
-
|
|
122
|
+
// Fast mode is opt-in (it needs separate account credits); the summary
|
|
123
|
+
// follows the route/session choice and never enables it on its own.
|
|
124
|
+
fast: opts.fast ?? opts.sendOpts?.fast ?? false,
|
|
123
125
|
maxOutputTokens: opts.maxOutputTokens || SUMMARY_OUTPUT_TOKENS,
|
|
124
126
|
providerState: undefined,
|
|
125
127
|
onToolCall: undefined,
|
|
@@ -54,7 +54,7 @@ export async function resolveCompactionRoute({
|
|
|
54
54
|
const contextWindow =
|
|
55
55
|
sameModel && positiveInt(sessionRef?.contextWindow)
|
|
56
56
|
? positiveInt(sessionRef.contextWindow)
|
|
57
|
-
: resolveSessionContextMeta(selectedProvider, selectedModel).contextWindow;
|
|
57
|
+
: resolveSessionContextMeta(selectedProvider, selectedModel, {}, { wholeWindow: true }).contextWindow;
|
|
58
58
|
return {
|
|
59
59
|
provider: selectedProvider,
|
|
60
60
|
providerName,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// resolution.
|
|
3
3
|
|
|
4
4
|
import { getModelMetadataSync } from '../../providers/model-catalog.mjs';
|
|
5
|
+
import { contextWindowRange } from '../../../../shared/llm/default-context-window.mjs';
|
|
5
6
|
import { positiveInt } from '../../../../shared/numbers.mjs';
|
|
6
7
|
|
|
7
8
|
// Family-pattern fallback used only when the provider and external catalogs
|
|
@@ -96,11 +97,12 @@ function providerRawContextWindow(info, catalogInfo) {
|
|
|
96
97
|
if (catalogWindow && fromCache !== catalogWindow) return catalogWindow;
|
|
97
98
|
return fromCache || null;
|
|
98
99
|
}
|
|
99
|
-
|
|
100
|
+
// `wholeWindow` asks for everything the model can take in (the summary route
|
|
101
|
+
// budgets its own input that way) instead of the window a session starts with.
|
|
102
|
+
export function resolveSessionContextMeta(provider, model, seed = {}, { wholeWindow = false } = {}) {
|
|
100
103
|
const info = typeof provider?.getCachedModelInfo === 'function' ? provider.getCachedModelInfo(model) : null;
|
|
101
104
|
const catalogInfo = getModelMetadataSync(model, providerNameOf(provider));
|
|
102
|
-
const
|
|
103
|
-
positiveInt(seed.selectedContextWindow) ||
|
|
105
|
+
const servedContextWindow =
|
|
104
106
|
providerRawContextWindow(info, catalogInfo) ||
|
|
105
107
|
positiveInt(catalogInfo?.contextWindow) ||
|
|
106
108
|
positiveInt(catalogInfo?.maxContextWindow) ||
|
|
@@ -110,6 +112,16 @@ export function resolveSessionContextMeta(provider, model, seed = {}) {
|
|
|
110
112
|
positiveInt(seed.raw_context_window) ||
|
|
111
113
|
positiveInt(seed.contextWindow) ||
|
|
112
114
|
guessContextWindow(model, providerNameOf(provider));
|
|
115
|
+
// A session that records no selection starts from the model's default
|
|
116
|
+
// window, the one the picker shows. A recorded percentage without its window
|
|
117
|
+
// (a session older than selectedContextWindow, a route saved before the
|
|
118
|
+
// catalog warmed) is still the user's choice and keeps the served window.
|
|
119
|
+
const recordsSelection = wholeWindow || boundedPercent(seed.contextPercent) !== null;
|
|
120
|
+
const requestedContextWindow =
|
|
121
|
+
positiveInt(seed.selectedContextWindow) ||
|
|
122
|
+
(recordsSelection
|
|
123
|
+
? servedContextWindow
|
|
124
|
+
: contextWindowRange({ provider: providerNameOf(provider), contextWindow: servedContextWindow }).defaultWindow);
|
|
113
125
|
// A managed runtime's allocated capacity also bounds restored selections
|
|
114
126
|
// and catalog metadata; raising a slider cannot allocate server memory.
|
|
115
127
|
const runtimeContextWindow = positiveInt(info?.runtimeContextWindow);
|
|
@@ -13,7 +13,12 @@ import { buildProviderCacheOpts, cacheCapabilityForProvider } from '../../agent-
|
|
|
13
13
|
import { normalizeAutoClearConfig, resolveAutoClearIdleMs } from '../../runtime-core/config-helpers.mjs';
|
|
14
14
|
import { _buildBaseRules } from './rules-cache.mjs';
|
|
15
15
|
import { composeSessionSystem, seedSessionMessages } from './session-prompt-composition.mjs';
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
_prepareResumeTools,
|
|
18
|
+
delegationDisabled,
|
|
19
|
+
honoredSchemaAllowlist,
|
|
20
|
+
resolveSessionToolSurface,
|
|
21
|
+
} from './session-tool-surface.mjs';
|
|
17
22
|
import { _forgetPreparedResume, _preparedResumeMatches, _readPreparedResume } from './prepared-resume-cache.mjs';
|
|
18
23
|
import {
|
|
19
24
|
filterModelEditToolNames,
|
|
@@ -175,6 +180,7 @@ export function createSession(opts) {
|
|
|
175
180
|
logSessionSurface(surface);
|
|
176
181
|
const contextMeta = resolveSessionContextMeta(provider, modelName, {
|
|
177
182
|
selectedContextWindow: opts.selectedContextWindow,
|
|
183
|
+
contextPercent: route.contextPercent,
|
|
178
184
|
});
|
|
179
185
|
const session = buildSessionRecord({
|
|
180
186
|
opts,
|
|
@@ -215,7 +221,10 @@ export function _refreshSessionRuleVariantsForModel(session, previousModel, prev
|
|
|
215
221
|
...(getHiddenAgent(session?.agent || null) ? ['Skill'] : []),
|
|
216
222
|
...(delegationDisabled(session, isAgentOwner(session)) ? ['agent'] : []),
|
|
217
223
|
];
|
|
218
|
-
const allowTools =
|
|
224
|
+
const allowTools = honoredSchemaAllowlist(session?.schemaAllowedTools, {
|
|
225
|
+
ownerIsAgent: isAgentOwner(session),
|
|
226
|
+
agent: session?.agent || null,
|
|
227
|
+
});
|
|
219
228
|
const previousRules = _buildBaseRules({
|
|
220
229
|
omitTools: [...deny, unusedModelEditToolName(previousModel)],
|
|
221
230
|
allowTools,
|
|
@@ -63,7 +63,11 @@ function buildShellEnvironmentContext(opts, ownerIsAgent, toolsForRouting) {
|
|
|
63
63
|
// four system blocks; environmentTailContext is the persisted env tail.
|
|
64
64
|
export function composeSessionSystem(opts, { profile, providerName, modelName, surface }) {
|
|
65
65
|
const { ownerIsAgent, resolvedAgent, skills } = surface;
|
|
66
|
-
|
|
66
|
+
// A tool-free hidden role (a one-shot classifier or title writer) carries
|
|
67
|
+
// only its own role rules: the shared tool policy and the worker conduct
|
|
68
|
+
// rules describe work it is offered no tool for.
|
|
69
|
+
const toolFreeRole = ownerIsAgent && surface.schemaAllowedTools?.length === 0;
|
|
70
|
+
const skipAgentRules = opts.skipAgentRules === true || toolFreeRole;
|
|
67
71
|
const injectedRules = skipAgentRules
|
|
68
72
|
? ''
|
|
69
73
|
: _buildBaseRules({
|
|
@@ -29,6 +29,19 @@ function resolveToolSpec(toolPreset, profile, ownerIsAgent) {
|
|
|
29
29
|
return Array.isArray(profile?.tools) ? profile.tools : toolPreset;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// The schema allowlist a session honours. Agent-owned sessions share one
|
|
33
|
+
// schema, so an allowlist handed down by a caller never narrows them — except
|
|
34
|
+
// the profile the session's own hidden role declares (agents.json
|
|
35
|
+
// toolSchemaProfile). Without that exception a tool-free maintenance role was
|
|
36
|
+
// sent the whole Agent schema on every one-shot call while the dispatch gate
|
|
37
|
+
// refused each of those tools (usage ledger 2026-09-30..10-05: 4104 cycle1
|
|
38
|
+
// calls at ~8.5K prompt tokens, about two thirds of it the 12-tool schema).
|
|
39
|
+
export function honoredSchemaAllowlist(schemaAllowedTools, { ownerIsAgent = false, agent = null } = {}) {
|
|
40
|
+
if (!Array.isArray(schemaAllowedTools)) return null;
|
|
41
|
+
if (ownerIsAgent && !getHiddenAgent(agent)) return null;
|
|
42
|
+
return schemaAllowedTools;
|
|
43
|
+
}
|
|
44
|
+
|
|
32
45
|
// Every tool omitted by a caller schema allowlist is omitted from BP1 too.
|
|
33
46
|
// The model never receives guidance for a tool it cannot call, and a new
|
|
34
47
|
// process-wide built-in cannot leak into a narrow profile's prompt.
|
|
@@ -48,13 +61,17 @@ export function resolveSessionToolSurface(opts, { profile, toolPreset, modelName
|
|
|
48
61
|
const resolvedAgent = opts.agent || opts.role || profile?.taskType || null;
|
|
49
62
|
const hiddenAgent = getHiddenAgent(resolvedAgent);
|
|
50
63
|
const isRetrievalAgent = hiddenAgent?.kind === 'retrieval';
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
//
|
|
54
|
-
//
|
|
64
|
+
const hasCallerAllow = Array.isArray(opts.schemaAllowedTools);
|
|
65
|
+
const schemaAllowedTools = honoredSchemaAllowlist(opts.schemaAllowedTools, { ownerIsAgent, agent: resolvedAgent });
|
|
66
|
+
// Lead and Agent share the same cwd-scoped Skill inventory. A session that
|
|
67
|
+
// is offered no tool cannot load a skill, so it is not sent the manifest.
|
|
68
|
+
const skills = opts.skipSkills || schemaAllowedTools?.length === 0 ? [] : collectPromptSkillsCached(opts.cwd);
|
|
69
|
+
// BP1 shared tool policy ships to every role that is offered tools (Lead,
|
|
70
|
+
// workers, retrieval): its anti-spiral clauses (one anchor is enough, never
|
|
55
71
|
// repeat equivalent patterns/scopes, plausible hit → stop) are exactly
|
|
56
72
|
// what narrow retrieval roles need. Role docs
|
|
57
|
-
// override role-inapplicable entries.
|
|
73
|
+
// override role-inapplicable entries. A tool-free role is sent none of it
|
|
74
|
+
// (session-prompt-composition.mjs).
|
|
58
75
|
const sessionDeny = [
|
|
59
76
|
...(Array.isArray(opts.disallowedTools) ? opts.disallowedTools : []),
|
|
60
77
|
...(delegationDisabled(opts, ownerIsAgent) ? ['agent'] : []),
|
|
@@ -70,8 +87,6 @@ export function resolveSessionToolSurface(opts, { profile, toolPreset, modelName
|
|
|
70
87
|
cwd: opts.cwd || null,
|
|
71
88
|
});
|
|
72
89
|
|
|
73
|
-
const hasCallerAllow = Array.isArray(opts.schemaAllowedTools);
|
|
74
|
-
const schemaAllowedTools = ownerIsAgent || !hasCallerAllow ? null : opts.schemaAllowedTools;
|
|
75
90
|
const tools = finalizeSessionToolList(toolsForRouting, {
|
|
76
91
|
schemaAllowedTools,
|
|
77
92
|
disallowedTools: sessionDeny,
|
|
@@ -121,8 +136,7 @@ export function _prepareResumeTools(session, preset) {
|
|
|
121
136
|
toolSpec,
|
|
122
137
|
ownerIsAgent,
|
|
123
138
|
tools: finalizeSessionToolList(toolsForRouting, {
|
|
124
|
-
schemaAllowedTools:
|
|
125
|
-
!ownerIsAgent && Array.isArray(session.schemaAllowedTools) ? session.schemaAllowedTools : null,
|
|
139
|
+
schemaAllowedTools: honoredSchemaAllowlist(session.schemaAllowedTools, { ownerIsAgent, agent: session.agent }),
|
|
126
140
|
disallowedTools: [
|
|
127
141
|
...(Array.isArray(session.disallowedTools) ? session.disallowedTools : []),
|
|
128
142
|
...(delegationDisabled(session, ownerIsAgent) ? ['agent'] : []),
|