mixdog 1.0.8 → 1.0.10
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/config-providers-merge.mjs +8 -0
- 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/custom-provider.mjs +163 -0
- 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/model-list-sanitize.mjs +23 -4
- 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-models.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +1 -28
- 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/provider-base-url.mjs +24 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +14 -4
- 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/config-helpers.mjs +3 -2
- 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 +22 -1
- 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/model-alias.mjs +5 -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 +6 -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/provider-auth/login.mjs +26 -1
- package/src/session-runtime/quick-model-rows/provider-rows.mjs +17 -2
- 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/services/provider-admin.mjs +201 -3
- 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/standalone/session-protocol.mjs +4 -0
- package/src/tui/app/model-options.mjs +10 -1
- package/src/tui/dist/index.mjs +12 -3
- 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/tui/session/session-api/integrations.mjs +12 -0
- package/src/session-runtime/bridge-first-use-gate.mjs +0 -72
|
@@ -10,7 +10,14 @@ import { homedir } from 'node:os';
|
|
|
10
10
|
import { dirname, join } from 'node:path';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
// Resolved on first use, not at load: the desktop service bundle (CJS) carries
|
|
14
|
+
// this module through the model catalog, and import.meta is empty there, so
|
|
15
|
+
// computing it at load crashed the whole bundle before any caller needed it.
|
|
16
|
+
let defaultRoot = null;
|
|
17
|
+
function packagedRoot() {
|
|
18
|
+
defaultRoot ??= join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
19
|
+
return defaultRoot;
|
|
20
|
+
}
|
|
14
21
|
|
|
15
22
|
// The defaults are joins over values that almost never change; they are hot
|
|
16
23
|
// (every session path resolves the data dir), so each default join is reused
|
|
@@ -32,7 +39,7 @@ export function mixdogHome() {
|
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
export function mixdogRoot() {
|
|
35
|
-
return process.env.MIXDOG_ROOT ||
|
|
42
|
+
return process.env.MIXDOG_ROOT || packagedRoot();
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
export function resolvePluginData() {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// The parent's flags a worker thread may inherit. A worker rejects any flag that
|
|
2
|
+
// governs the process itself (heap sizes, inspector, TLS, test-runner settings)
|
|
3
|
+
// with ERR_WORKER_INVALID_EXEC_ARGV, and Node 24's test runner hands a child its
|
|
4
|
+
// whole option table in execArgv (--use-system-ca, --tls-cipher-list, ...), so a
|
|
5
|
+
// list of known-bad flags kept failing. Only what loads and resolves modules,
|
|
6
|
+
// and what governs warnings, carries over.
|
|
7
|
+
const INHERITED_FLAG =
|
|
8
|
+
/^--(?:import|require|loader|experimental-loader|conditions|enable-source-maps|preserve-symlinks(?:-main)?|no-warnings|disable-warning|trace-warnings|no-deprecation|(?:no-)?experimental-[a-z0-9-]+)(?:=|$)/;
|
|
9
|
+
const SHORT_WITH_VALUE = new Set(['-r', '-C']);
|
|
10
|
+
|
|
11
|
+
export function workerExecArgv(execArgv = process.execArgv) {
|
|
12
|
+
const source = Array.isArray(execArgv) ? execArgv.map(String) : [];
|
|
13
|
+
const kept = [];
|
|
14
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
15
|
+
const argument = source[index];
|
|
16
|
+
const short = SHORT_WITH_VALUE.has(argument);
|
|
17
|
+
if (!short && !INHERITED_FLAG.test(argument)) continue;
|
|
18
|
+
kept.push(argument);
|
|
19
|
+
// A flag written apart from its value (`--import tsx`) keeps the value too.
|
|
20
|
+
const takesValue =
|
|
21
|
+
short || /^--(?:import|require|loader|experimental-loader|conditions|disable-warning)$/.test(argument);
|
|
22
|
+
if (takesValue && index + 1 < source.length) kept.push(source[++index]);
|
|
23
|
+
}
|
|
24
|
+
return kept;
|
|
25
|
+
}
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
* Response formatter — strips metadata, returns human-readable text.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
// Whether `text` names exactly this URL: an occurrence that runs on into a
|
|
6
|
+
// longer URL (`…/docs` inside `…/docs/guide`) is a different page.
|
|
7
|
+
function textCitesUrl(text, url) {
|
|
8
|
+
for (let at = text.indexOf(url); at !== -1; at = text.indexOf(url, at + 1)) {
|
|
9
|
+
const rest = text.slice(at + url.length, at + url.length + 2);
|
|
10
|
+
if (!/^(?:[A-Za-z0-9/?#&=%_~-]|\.[A-Za-z0-9])/.test(rest)) return true;
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
5
15
|
function formatWebSearchResults(data) {
|
|
6
16
|
// data may be the full jsonText payload: { tool, providers, response, cache, ... }
|
|
7
17
|
const response = data.response || data;
|
|
@@ -20,25 +30,40 @@ function formatWebSearchResults(data) {
|
|
|
20
30
|
const SNIPPET_CAP = 600;
|
|
21
31
|
const ANSWER_CAP = 4000;
|
|
22
32
|
const clip = (text, cap) => (text.length > cap ? `${text.slice(0, cap)}…` : text);
|
|
23
|
-
|
|
33
|
+
const shownAnswer = answer ? clip(answer, ANSWER_CAP) : '';
|
|
34
|
+
if (shownAnswer) blocks.push(shownAnswer);
|
|
24
35
|
if (!results.length) return blocks.join('\n\n');
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
// A native search route reports the pages a query reached without their
|
|
37
|
+
// titles, and the adapters label each one with that query. A label shared by
|
|
38
|
+
// several URLs names the search, not a page, so such a source prints as its
|
|
39
|
+
// URL alone — and not at all when the answer above already cites that URL.
|
|
40
|
+
// Measured on stored results: every source had this shape and 66% of their
|
|
41
|
+
// URLs were already in the answer.
|
|
42
|
+
const urlsByTitle = new Map();
|
|
43
|
+
for (const r of results) {
|
|
44
|
+
if (!r.title || !r.url) continue;
|
|
45
|
+
if (!urlsByTitle.has(r.title)) urlsByTitle.set(r.title, new Set());
|
|
46
|
+
urlsByTitle.get(r.title).add(r.url);
|
|
47
|
+
}
|
|
48
|
+
const entries = [];
|
|
49
|
+
for (const r of results) {
|
|
50
|
+
const url = r.url || '';
|
|
51
|
+
const date = r.publishedDate || '';
|
|
52
|
+
const snippet = clip((r.snippet || '').trim(), SNIPPET_CAP);
|
|
53
|
+
const urlPart = [url, date].filter(Boolean).join(' — ');
|
|
54
|
+
const queryLabelled =
|
|
55
|
+
!r.title || r.title === url || r.source === 'web_search_call' || urlsByTitle.get(r.title)?.size > 1;
|
|
56
|
+
if (url && !snippet && queryLabelled) {
|
|
57
|
+
if (!textCitesUrl(shownAnswer, url)) entries.push(`${entries.length + 1}. ${urlPart}`);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const title = clip(r.title || '(no title)', TITLE_CAP);
|
|
61
|
+
const lines = [`${entries.length + 1}. ${title}`];
|
|
62
|
+
if (urlPart) lines.push(` ${urlPart}`);
|
|
63
|
+
if (snippet && snippet !== title && snippet !== url) lines.push(` ${snippet}`);
|
|
64
|
+
entries.push(lines.join('\n'));
|
|
65
|
+
}
|
|
66
|
+
if (entries.length) blocks.push(entries.join('\n\n'));
|
|
42
67
|
return blocks.join('\n\n');
|
|
43
68
|
}
|
|
44
69
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent delegation for automation sessions (schedules, webhooks). They run
|
|
3
|
+
* headless in the daemon, so no session runtime registers tools for them or
|
|
4
|
+
* receives their agents' completions. The daemon's canonical agent control is
|
|
5
|
+
* exposed under a dedicated internal-tool scope instead, and the completions of
|
|
6
|
+
* the agents a run starts come back here, where the run turns them into the
|
|
7
|
+
* conversation's next turn — what a Lead in a window does when one arrives.
|
|
8
|
+
*/
|
|
9
|
+
import { setInternalToolsProvider } from '../runtime/agent/orchestrator/internal-tools.mjs';
|
|
10
|
+
import { AGENT_TOOL } from './services/agent-tool/tool-def.mjs';
|
|
11
|
+
|
|
12
|
+
export const AUTOMATION_TOOL_SCOPE = 'automation';
|
|
13
|
+
// How long a run waits for the next result of an agent it started; past it the
|
|
14
|
+
// run ends with the conversation as it stands.
|
|
15
|
+
const AGENT_RESULT_WAIT_MS = 60 * 60_000;
|
|
16
|
+
|
|
17
|
+
const runs = new Map(); // sessionId → { pending: Set<taskId>, ready: string[], wake }
|
|
18
|
+
|
|
19
|
+
function resultText(result) {
|
|
20
|
+
return typeof result === 'string' ? result : JSON.stringify(result ?? '');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Registers the agent tool for automation sessions; returns its disposer. */
|
|
24
|
+
export function registerAutomationAgentTool(executeAgentControl) {
|
|
25
|
+
return setInternalToolsProvider({
|
|
26
|
+
scopeId: AUTOMATION_TOOL_SCOPE,
|
|
27
|
+
tools: [AGENT_TOOL],
|
|
28
|
+
async executor(_name, args, callerCtx = {}) {
|
|
29
|
+
const result = await executeAgentControl(args, {
|
|
30
|
+
callerCwd: callerCtx.callerCwd,
|
|
31
|
+
invocationSource: 'model-tool',
|
|
32
|
+
callerSessionId: callerCtx.callerSessionId || null,
|
|
33
|
+
clientHostPid: process.pid,
|
|
34
|
+
signal: callerCtx.signal,
|
|
35
|
+
});
|
|
36
|
+
// A spawn/send acknowledgement ("agent task: <id>", "status: running")
|
|
37
|
+
// is a result still to come for this run.
|
|
38
|
+
const text = resultText(result);
|
|
39
|
+
const taskId = /agent task: ([\w.:-]+)/.exec(text)?.[1];
|
|
40
|
+
const run = runs.get(String(callerCtx.callerSessionId || ''));
|
|
41
|
+
if (run && taskId && /status: running/.test(text)) run.pending.add(taskId);
|
|
42
|
+
return result;
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Hands an agent completion to the automation run that started the task; false when none did. */
|
|
48
|
+
export function deliverAutomationCompletion(sessionId, text, meta = {}) {
|
|
49
|
+
const run = runs.get(String(sessionId || ''));
|
|
50
|
+
const taskId = String(meta?.execution_id || '');
|
|
51
|
+
if (!run?.pending.has(taskId)) return false;
|
|
52
|
+
// The header-only preview precedes the body-carrying completion.
|
|
53
|
+
if (meta?.model_visible === false) return true;
|
|
54
|
+
run.pending.delete(taskId);
|
|
55
|
+
run.ready.push(String(text || ''));
|
|
56
|
+
run.wake?.();
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Runs an automation conversation to its end: the first turn, then one more
|
|
62
|
+
* turn per batch of agent results while agents it started are still out.
|
|
63
|
+
* Resolves with the last turn's result.
|
|
64
|
+
*/
|
|
65
|
+
export async function runAutomationTurns(sessionId, firstTurn, nextTurn, { waitMs = AGENT_RESULT_WAIT_MS } = {}) {
|
|
66
|
+
const run = { pending: new Set(), ready: [], wake: null };
|
|
67
|
+
runs.set(sessionId, run);
|
|
68
|
+
try {
|
|
69
|
+
let result = await firstTurn();
|
|
70
|
+
while (run.pending.size > 0 || run.ready.length > 0) {
|
|
71
|
+
if (run.ready.length === 0) {
|
|
72
|
+
const arrived = await new Promise((resolve) => {
|
|
73
|
+
const timer = setTimeout(() => resolve(false), waitMs);
|
|
74
|
+
run.wake = () => {
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
resolve(true);
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
run.wake = null;
|
|
80
|
+
if (!arrived) break;
|
|
81
|
+
}
|
|
82
|
+
result = await nextTurn(run.ready.splice(0).join('\n\n'));
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
} finally {
|
|
86
|
+
runs.delete(sessionId);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -10,6 +10,9 @@ import { createWorkflowHelpers } from '../runtime/agent/orchestrator/runtime-cor
|
|
|
10
10
|
import { STANDALONE_ROOT, STANDALONE_DATA_DIR } from './runtime-paths.mjs';
|
|
11
11
|
import { readMarkdownDocument, normalizeAgentPermissionOrNone } from '../runtime/shared/markdown-frontmatter.mjs';
|
|
12
12
|
import { IMPLICIT_APPROVAL_MODE } from '../runtime/agent/orchestrator/session/approval-mode.mjs';
|
|
13
|
+
import { loadConfig } from '../runtime/agent/orchestrator/config.mjs';
|
|
14
|
+
import { configuredOrchestrationMode } from '../runtime/shared/orchestration.mjs';
|
|
15
|
+
import { AUTOMATION_TOOL_SCOPE } from './automation-agents.mjs';
|
|
13
16
|
|
|
14
17
|
let _helpers = null;
|
|
15
18
|
function helpers() {
|
|
@@ -26,21 +29,31 @@ function helpers() {
|
|
|
26
29
|
|
|
27
30
|
/** Non-interactive createSession opts, plus workflow context when the id resolves. */
|
|
28
31
|
export function automationWorkflowOpts(workflowId) {
|
|
29
|
-
const
|
|
32
|
+
const config = loadConfig({ secrets: false });
|
|
33
|
+
// The user's orchestration setting governs an automation exactly as it does a
|
|
34
|
+
// New task: a workflow id alone never turns delegation on, and the agent
|
|
35
|
+
// tool reaches the run through the automation tool scope.
|
|
36
|
+
const baseOpts = {
|
|
37
|
+
approvalMode: IMPLICIT_APPROVAL_MODE,
|
|
38
|
+
orchestrationMode: configuredOrchestrationMode(config),
|
|
39
|
+
mcpScopeId: AUTOMATION_TOOL_SCOPE,
|
|
40
|
+
};
|
|
30
41
|
const id = String(workflowId || '').trim();
|
|
31
|
-
if (!id) return
|
|
42
|
+
if (!id) return baseOpts;
|
|
32
43
|
try {
|
|
33
44
|
const h = helpers();
|
|
34
45
|
const pack = h.loadWorkflowPack(undefined, id);
|
|
35
|
-
if (!pack) return
|
|
36
|
-
|
|
46
|
+
if (!pack) return baseOpts;
|
|
47
|
+
// The whole config, so disabled agents and the orchestration mode are the
|
|
48
|
+
// user's, with only the active workflow swapped for the automation's own.
|
|
49
|
+
const resolved = h.activeWorkflowContext({ ...config, workflow: { ...config.workflow, active: id } }, undefined);
|
|
37
50
|
return {
|
|
38
|
-
...
|
|
51
|
+
...baseOpts,
|
|
39
52
|
workflow: resolved.summary,
|
|
40
53
|
workflowContext: resolved.context,
|
|
41
54
|
orchestrationMode: resolved.orchestrationMode,
|
|
42
55
|
};
|
|
43
56
|
} catch {
|
|
44
|
-
return
|
|
57
|
+
return baseOpts;
|
|
45
58
|
}
|
|
46
59
|
}
|
|
@@ -107,7 +107,6 @@ function wireToolPolicyRefresh(boot) {
|
|
|
107
107
|
getDataDir: () => dataDirOf(cfgMod),
|
|
108
108
|
modelStandaloneTools: boot.modelStandaloneTools,
|
|
109
109
|
featureDisallowedTools: boot.featureDisallowedTools,
|
|
110
|
-
memoryToolsEnabled: boot.memoryToolsEnabledFn,
|
|
111
110
|
loadCoreMemoryContext: boot.loadCoreMemoryContext,
|
|
112
111
|
activeWorkflowContext: workflowHelpers.activeWorkflowContext,
|
|
113
112
|
invalidatePreSessionToolSurface: boot.invalidatePreSessionToolSurface,
|
|
@@ -274,6 +273,12 @@ function providerAuthApiFor(boot) {
|
|
|
274
273
|
getUsageDashboard: boot.getUsageDashboard,
|
|
275
274
|
consumeCodexRateLimitResetCredit: boot.consumeCodexRateLimitResetCredit,
|
|
276
275
|
collectProviderModels: boot.collectProviderModels,
|
|
276
|
+
// A deleted custom provider must not stay reachable through the live route
|
|
277
|
+
// or the registry; stored routes naming it are filtered by the picker.
|
|
278
|
+
onCustomProviderRemoved: (id) => {
|
|
279
|
+
reg.disableProvider?.(id);
|
|
280
|
+
if (rt.route?.provider === id) rt.route = { ...rt.route, provider: '', model: '', preset: null };
|
|
281
|
+
},
|
|
277
282
|
});
|
|
278
283
|
}
|
|
279
284
|
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
// cwd-plugins/core-memory-context.mjs — the user-curated core-memory block
|
|
2
2
|
// injected into new sessions, read from the memory runtime's atomic snapshot.
|
|
3
|
-
import { featureEnvOverride
|
|
3
|
+
import { featureEnvOverride } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
|
|
4
4
|
import { readSessionCoreMemoryPayload } from '../../runtime/memory/lib/core-memory-file.mjs';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
// Budget for the whole injected block in UTF-8 bytes: bytes track token cost
|
|
7
|
+
// more evenly across scripts than characters do.
|
|
8
|
+
const CORE_MEMORY_MAX_BYTES = 32 * 1024;
|
|
9
|
+
const TRUNCATED_MARK = '\n- ...';
|
|
10
|
+
|
|
11
|
+
export function createCoreMemoryContext({ getCurrentCwd, bootProfile, clean, cfgMod, STANDALONE_DATA_DIR }) {
|
|
7
12
|
function formatCoreMemoryLines(payload = {}) {
|
|
8
13
|
const seen = new Set();
|
|
9
14
|
const lines = [];
|
|
@@ -14,21 +19,18 @@ export function createCoreMemoryContext({ getCurrentCwd, getConfig, bootProfile,
|
|
|
14
19
|
if (seen.has(key)) continue;
|
|
15
20
|
seen.add(key);
|
|
16
21
|
lines.push(`- ${text}`);
|
|
17
|
-
if (lines.length >= 40) break;
|
|
18
22
|
}
|
|
19
23
|
const out = lines.join('\n');
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
if (Buffer.byteLength(out, 'utf8') <= CORE_MEMORY_MAX_BYTES) return out;
|
|
25
|
+
const budget = CORE_MEMORY_MAX_BYTES - Buffer.byteLength(TRUNCATED_MARK, 'utf8');
|
|
26
|
+
// A byte cut can split a multi-byte character; drop its replacement chars.
|
|
27
|
+
const head = Buffer.from(out, 'utf8').subarray(0, budget).toString('utf8').replace(/\uFFFD+$/, '');
|
|
28
|
+
return `${head.replace(/\s+\S*$/, '')}${TRUNCATED_MARK}`;
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
async function loadCoreMemoryContext() {
|
|
25
|
-
// User-curated core memory injects into new sessions
|
|
26
|
-
//
|
|
27
|
-
// core-memory block — the profile injects regardless.
|
|
28
|
-
if (!(featureEnvOverride('MIXDOG_FEATURE_MEMORY') ?? memoryToolsEnabled(getConfig(), true))) {
|
|
29
|
-
bootProfile('core-memory:disabled');
|
|
30
|
-
return '';
|
|
31
|
-
}
|
|
32
|
+
// User-curated core memory always injects into new sessions: the Memory
|
|
33
|
+
// install marker and toggle gate only the model-facing memory/recall tools.
|
|
32
34
|
// Explicit opt-out (MIXDOG_BOOT_CORE_MEMORY=0/false/no/off) skips this
|
|
33
35
|
// file-backed prompt block. Recall and memory tools remain available.
|
|
34
36
|
if (featureEnvOverride('MIXDOG_BOOT_CORE_MEMORY') === false) {
|
|
@@ -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,9 +8,13 @@ import {
|
|
|
8
8
|
saveOpenAIUsageSessionKey,
|
|
9
9
|
saveOpenCodeGoUsageAuth,
|
|
10
10
|
saveProviderApiKey,
|
|
11
|
+
saveCustomProvider,
|
|
12
|
+
removeCustomProvider,
|
|
13
|
+
testCustomProvider,
|
|
14
|
+
discoverCustomProviderModels,
|
|
11
15
|
} from '../services/provider-admin.mjs';
|
|
12
16
|
|
|
13
|
-
export function createLoginApi({ cfgMod, awaitKeychainPrewarm, reloadFullConfig }, refresh) {
|
|
17
|
+
export function createLoginApi({ cfgMod, awaitKeychainPrewarm, reloadFullConfig, onCustomProviderRemoved }, refresh) {
|
|
14
18
|
async function completeOAuthLogin(login) {
|
|
15
19
|
return {
|
|
16
20
|
...login,
|
|
@@ -69,6 +73,27 @@ export function createLoginApi({ cfgMod, awaitKeychainPrewarm, reloadFullConfig
|
|
|
69
73
|
refresh.afterUsageAuthChange();
|
|
70
74
|
return result;
|
|
71
75
|
},
|
|
76
|
+
async saveCustomProvider(input) {
|
|
77
|
+
await awaitKeychainPrewarm();
|
|
78
|
+
const result = await saveCustomProvider(cfgMod, input);
|
|
79
|
+
refresh.afterCredentialChange();
|
|
80
|
+
return result;
|
|
81
|
+
},
|
|
82
|
+
async removeCustomProvider(providerId) {
|
|
83
|
+
await awaitKeychainPrewarm();
|
|
84
|
+
const result = removeCustomProvider(cfgMod, providerId);
|
|
85
|
+
onCustomProviderRemoved?.(result.provider);
|
|
86
|
+
refresh.afterCredentialChange();
|
|
87
|
+
return result;
|
|
88
|
+
},
|
|
89
|
+
async testCustomProvider(input) {
|
|
90
|
+
await awaitKeychainPrewarm();
|
|
91
|
+
return await testCustomProvider(cfgMod, input);
|
|
92
|
+
},
|
|
93
|
+
async discoverCustomProviderModels(input) {
|
|
94
|
+
await awaitKeychainPrewarm();
|
|
95
|
+
return await discoverCustomProviderModels(cfgMod, input);
|
|
96
|
+
},
|
|
72
97
|
forgetProviderAuth(providerId, accountId) {
|
|
73
98
|
const result = forgetProviderAuth(cfgMod, providerId, accountId);
|
|
74
99
|
refresh.afterCredentialChange();
|
|
@@ -46,7 +46,7 @@ export function createQuickProviderRows({
|
|
|
46
46
|
const addRoute = (routeLike = {}) => {
|
|
47
47
|
const provider = clean(routeLike.provider);
|
|
48
48
|
const model = clean(routeLike.model);
|
|
49
|
-
if (!provider || !model) return;
|
|
49
|
+
if (!provider || !model || isDeletedCustom(provider)) return;
|
|
50
50
|
const meta = metadataFor(provider, model);
|
|
51
51
|
addModel(provider, {
|
|
52
52
|
id: model,
|
|
@@ -64,8 +64,23 @@ export function createQuickProviderRows({
|
|
|
64
64
|
});
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
// A custom-* id with no live custom config entry was deleted: stale
|
|
68
|
+
// routes must not bring it back into the picker.
|
|
69
|
+
const customProviders = pickerConfig.providers || {};
|
|
70
|
+
const isDeletedCustom = (provider) =>
|
|
71
|
+
provider.startsWith('custom-') &&
|
|
72
|
+
!(customProviders[provider]?.type === 'custom' && customProviders[provider].enabled !== false);
|
|
73
|
+
for (const [provider, providerConfig] of Object.entries(customProviders)) {
|
|
68
74
|
if (!providerConfig?.enabled) continue;
|
|
75
|
+
if (providerConfig.type === 'custom') {
|
|
76
|
+
// Configured models are listed at once, before any catalog refresh.
|
|
77
|
+
for (const model of providerConfig.models || []) {
|
|
78
|
+
addModel(provider, {
|
|
79
|
+
...model,
|
|
80
|
+
outputTokens: model.maxOutputTokens || null,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
69
84
|
for (const model of providerCachedModelsSync(provider)) addModel(provider, model);
|
|
70
85
|
}
|
|
71
86
|
addRoute(route);
|
|
@@ -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
|
}
|