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
|
@@ -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,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import {
|
|
2
3
|
AGENT_PROVIDER_ENV,
|
|
3
4
|
SECRET_ACCOUNTS,
|
|
@@ -156,9 +157,14 @@ function availableOAuthProvider(id) {
|
|
|
156
157
|
return oauth && isOAuthProviderAvailable(id) ? oauth : null;
|
|
157
158
|
}
|
|
158
159
|
|
|
159
|
-
export function isKnownProvider(provider) {
|
|
160
|
+
export function isKnownProvider(provider, config = {}) {
|
|
160
161
|
const id = String(provider || '').trim();
|
|
161
|
-
return id !== '' && (
|
|
162
|
+
return id !== '' && (
|
|
163
|
+
API_PROVIDER_IDS.has(id) ||
|
|
164
|
+
BUILTIN_PROVIDER_IDS.has(id) ||
|
|
165
|
+
!!availableOAuthProvider(id) ||
|
|
166
|
+
(isCustomProviderId(id) && isCustomProviderConfig(config.providers?.[id]) && config.providers[id].enabled !== false)
|
|
167
|
+
);
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
function updateConfigProvider(cfgMod, providerId, patch) {
|
|
@@ -275,11 +281,57 @@ function oauthProviderSetup(p, providers, checkSecrets) {
|
|
|
275
281
|
};
|
|
276
282
|
}
|
|
277
283
|
|
|
284
|
+
const CUSTOM_ID_RE = /^custom-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
285
|
+
const CUSTOM_TEST_TIMEOUT_MS = 20_000;
|
|
286
|
+
const CUSTOM_DISCOVERY_TIMEOUT_MS = 15_000;
|
|
287
|
+
|
|
288
|
+
export function isCustomProviderConfig(entry) {
|
|
289
|
+
return Boolean(entry) && typeof entry === 'object' && entry.type === 'custom';
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function isCustomProviderId(id) {
|
|
293
|
+
return CUSTOM_ID_RE.test(String(id || ''));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const loadCustomProviderModule = () => import('../../runtime/agent/orchestrator/providers/custom-provider.mjs');
|
|
297
|
+
|
|
298
|
+
// Secret-free public view of a custom provider. Never carries an API key.
|
|
299
|
+
function customProviderRow(id, entry, checkSecrets = true) {
|
|
300
|
+
const configuredEnabled = entry.enabled !== false;
|
|
301
|
+
const stored = checkSecrets ? hasStoredSecret(SECRET_ACCOUNTS.agentApiKey(id)) : false;
|
|
302
|
+
return {
|
|
303
|
+
id,
|
|
304
|
+
name: entry.name,
|
|
305
|
+
protocol: entry.protocol,
|
|
306
|
+
baseURL: entry.baseURL,
|
|
307
|
+
models: Array.isArray(entry.models) ? entry.models.map((m) => ({ ...m })) : [],
|
|
308
|
+
custom: true,
|
|
309
|
+
group: 'api',
|
|
310
|
+
type: 'api-key',
|
|
311
|
+
enabled: configuredEnabled,
|
|
312
|
+
authenticated: checkSecrets ? stored : configuredEnabled,
|
|
313
|
+
stored,
|
|
314
|
+
env: false,
|
|
315
|
+
envName: null,
|
|
316
|
+
status: stored ? 'Set' : configuredEnabled ? 'No Key' : 'Off',
|
|
317
|
+
detail: stored ? 'stored in keychain' : 'custom endpoint',
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function customProviderRows(providers, checkSecrets) {
|
|
322
|
+
return Object.entries(providers)
|
|
323
|
+
.filter(([id, entry]) => isCustomProviderId(id) && isCustomProviderConfig(entry))
|
|
324
|
+
.map(([id, entry]) => customProviderRow(id, entry, checkSecrets));
|
|
325
|
+
}
|
|
326
|
+
|
|
278
327
|
export async function providerSetup(config = {}, options = {}) {
|
|
279
328
|
const providers = config.providers || {};
|
|
280
329
|
const checkSecrets = options?.checkSecrets !== false;
|
|
281
330
|
return {
|
|
282
|
-
api:
|
|
331
|
+
api: [
|
|
332
|
+
...API_PROVIDERS.map((p) => apiProviderSetup(p, providers, checkSecrets)),
|
|
333
|
+
...customProviderRows(providers, checkSecrets),
|
|
334
|
+
],
|
|
283
335
|
oauth: availableOAuthProviders().map((p) => oauthProviderSetup(p, providers, checkSecrets)),
|
|
284
336
|
local: [builtInLocalProviderSetup(config, options)],
|
|
285
337
|
};
|
|
@@ -322,6 +374,16 @@ function oauthProviderStatusRow(p, config) {
|
|
|
322
374
|
export function providerStatus(config = {}) {
|
|
323
375
|
return [
|
|
324
376
|
...API_PROVIDERS.map((p) => apiProviderStatusRow(p, config)),
|
|
377
|
+
...customProviderRows(config.providers || {}, true).map((row) => ({
|
|
378
|
+
id: row.id,
|
|
379
|
+
type: 'api-key',
|
|
380
|
+
enabled: row.enabled,
|
|
381
|
+
authenticated: row.authenticated,
|
|
382
|
+
stored: row.stored,
|
|
383
|
+
env: false,
|
|
384
|
+
envName: null,
|
|
385
|
+
label: row.name,
|
|
386
|
+
})),
|
|
325
387
|
...availableOAuthProviders().map((p) => oauthProviderStatusRow(p, config)),
|
|
326
388
|
];
|
|
327
389
|
}
|
|
@@ -525,6 +587,142 @@ export function saveProviderApiKey(cfgMod, provider, secret) {
|
|
|
525
587
|
return { provider: id, type: 'api-key', authenticated: true };
|
|
526
588
|
}
|
|
527
589
|
|
|
590
|
+
function sanitizeCustomError(error, secrets = []) {
|
|
591
|
+
let text = String(error?.message || error || 'request failed');
|
|
592
|
+
for (const secret of secrets) if (secret) text = text.split(secret).join('[redacted]');
|
|
593
|
+
text = text
|
|
594
|
+
.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [redacted]')
|
|
595
|
+
.replace(/\b(?:sk|key|tok)[-_][A-Za-z0-9_-]{8,}/gi, '[redacted]')
|
|
596
|
+
.replace(/(api[-_]?key|authorization|token)(["']?\s*[:=]\s*["']?)[^\s"',}]+/gi, '$1$2[redacted]')
|
|
597
|
+
.replace(/[\r\n]+/g, ' ')
|
|
598
|
+
.trim();
|
|
599
|
+
if (text.length > 300) text = `${text.slice(0, 300)}…`;
|
|
600
|
+
return new Error(text || 'request failed');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
async function withTimeout(run, ms, label) {
|
|
604
|
+
const controller = new AbortController();
|
|
605
|
+
let timer;
|
|
606
|
+
const timeout = new Promise((_, reject) => {
|
|
607
|
+
timer = setTimeout(() => {
|
|
608
|
+
controller.abort();
|
|
609
|
+
reject(new Error(`${label} timed out after ${Math.round(ms / 1000)}s`));
|
|
610
|
+
}, ms);
|
|
611
|
+
});
|
|
612
|
+
try {
|
|
613
|
+
return await Promise.race([run(controller.signal), timeout]);
|
|
614
|
+
} finally {
|
|
615
|
+
clearTimeout(timer);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function existingCustomEntry(cfgMod, id) {
|
|
620
|
+
const entry = cfgMod.loadConfig().providers?.[id];
|
|
621
|
+
if (!isCustomProviderId(id) || !isCustomProviderConfig(entry)) throw new Error(`unknown custom provider "${id}"`);
|
|
622
|
+
return entry;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function inputObject(input) {
|
|
626
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new TypeError('custom provider is invalid');
|
|
627
|
+
return input;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Validated, secret-free config for `input`; the key (typed, else stored for an
|
|
631
|
+
// existing provider) is resolved separately and never enters the config.
|
|
632
|
+
async function resolveCustomInput(cfgMod, rawInput, options) {
|
|
633
|
+
const input = inputObject(rawInput);
|
|
634
|
+
const id = input.id === undefined || input.id === null || input.id === '' ? null : String(input.id);
|
|
635
|
+
if (id) existingCustomEntry(cfgMod, id);
|
|
636
|
+
const { normalizeCustomProviderConfig } = await loadCustomProviderModule();
|
|
637
|
+
const config = normalizeCustomProviderConfig(input, options);
|
|
638
|
+
const typed = typeof input.apiKey === 'string' ? input.apiKey.trim() : '';
|
|
639
|
+
const apiKey = typed || (id ? getAgentApiKey(id) || '' : '');
|
|
640
|
+
return { id, config, typed, apiKey };
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
export async function saveCustomProvider(cfgMod, rawInput) {
|
|
644
|
+
const { id: existingId, config, typed, apiKey } = await resolveCustomInput(cfgMod, rawInput, { requireModels: false });
|
|
645
|
+
if (!apiKey) throw new Error('API key is required');
|
|
646
|
+
const id = existingId || `custom-${randomUUID()}`;
|
|
647
|
+
const entry = {
|
|
648
|
+
type: 'custom',
|
|
649
|
+
name: config.name,
|
|
650
|
+
protocol: config.protocol,
|
|
651
|
+
baseURL: config.baseURL,
|
|
652
|
+
models: config.models,
|
|
653
|
+
enabled: true,
|
|
654
|
+
};
|
|
655
|
+
const account = SECRET_ACCOUNTS.agentApiKey(id);
|
|
656
|
+
const previousKey = typed && existingId ? getAgentApiKey(id) : null;
|
|
657
|
+
if (typed) saveSecret(account, typed);
|
|
658
|
+
try {
|
|
659
|
+
const current = cfgMod.loadConfig();
|
|
660
|
+
cfgMod.saveConfig({ ...current, providers: { ...(current.providers || {}), [id]: entry } }, { baseConfig: current });
|
|
661
|
+
} catch (error) {
|
|
662
|
+
if (typed) {
|
|
663
|
+
if (previousKey) saveSecret(account, previousKey);
|
|
664
|
+
else deleteSecret(account);
|
|
665
|
+
}
|
|
666
|
+
throw error;
|
|
667
|
+
}
|
|
668
|
+
return customProviderRow(id, entry);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
export function removeCustomProvider(cfgMod, rawId) {
|
|
672
|
+
const id = String(rawId || '').trim();
|
|
673
|
+
existingCustomEntry(cfgMod, id);
|
|
674
|
+
const current = cfgMod.loadConfig();
|
|
675
|
+
const { [id]: _removed, ...providers } = current.providers || {};
|
|
676
|
+
cfgMod.saveConfig({ ...current, providers }, { baseConfig: current });
|
|
677
|
+
deleteSecret(SECRET_ACCOUNTS.agentApiKey(id));
|
|
678
|
+
return { provider: id, type: 'custom', removed: true };
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
export async function testCustomProvider(cfgMod, rawInput) {
|
|
682
|
+
const { id, config, apiKey } = await resolveCustomInput(cfgMod, rawInput, { requireModels: false });
|
|
683
|
+
try {
|
|
684
|
+
const models = config.models.length ? config.models : (await discoverCustomProviderModels(cfgMod, rawInput)).models;
|
|
685
|
+
const model = models[0]?.id;
|
|
686
|
+
if (!model) throw new Error('No models were found for this provider');
|
|
687
|
+
const { createCustomProvider } = await loadCustomProviderModule();
|
|
688
|
+
const provider = await createCustomProvider(id || 'custom-test', { ...config, apiKey });
|
|
689
|
+
await withTimeout(
|
|
690
|
+
(signal) => provider.send([{ role: 'user', content: 'Reply with OK.' }], model, [], { signal, maxTokens: 16 }),
|
|
691
|
+
CUSTOM_TEST_TIMEOUT_MS,
|
|
692
|
+
'connection test'
|
|
693
|
+
);
|
|
694
|
+
return { ok: true };
|
|
695
|
+
} catch (error) {
|
|
696
|
+
throw sanitizeCustomError(error, [apiKey]);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
export async function discoverCustomProviderModels(cfgMod, rawInput) {
|
|
701
|
+
const { id, config, apiKey } = await resolveCustomInput(cfgMod, rawInput, { requireModels: false });
|
|
702
|
+
try {
|
|
703
|
+
const { createCustomProvider } = await loadCustomProviderModule();
|
|
704
|
+
const provider = await createCustomProvider(id || 'custom-discovery', { ...config, models: [], apiKey });
|
|
705
|
+
const listed = await withTimeout(() => provider.listModels(), CUSTOM_DISCOVERY_TIMEOUT_MS, 'model discovery');
|
|
706
|
+
const seen = new Set();
|
|
707
|
+
const models = [];
|
|
708
|
+
for (const item of Array.isArray(listed) ? listed : []) {
|
|
709
|
+
const modelId = String(item?.id || '').trim();
|
|
710
|
+
if (!modelId || seen.has(modelId)) continue;
|
|
711
|
+
seen.add(modelId);
|
|
712
|
+
const model = { id: modelId };
|
|
713
|
+
const name = String(item.name || item.display || '').trim();
|
|
714
|
+
if (name && name !== modelId) model.name = name;
|
|
715
|
+
if (Number(item.contextWindow) > 0) model.contextWindow = Math.floor(Number(item.contextWindow));
|
|
716
|
+
const out = Number(item.maxOutputTokens ?? item.outputTokens);
|
|
717
|
+
if (out > 0) model.maxOutputTokens = Math.floor(out);
|
|
718
|
+
models.push(model);
|
|
719
|
+
}
|
|
720
|
+
return { models };
|
|
721
|
+
} catch (error) {
|
|
722
|
+
throw sanitizeCustomError(error, [apiKey]);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
528
726
|
export function saveOpenAIUsageSessionKey(cfgMod, secret) {
|
|
529
727
|
const value = String(secret || '').trim();
|
|
530
728
|
if (!value) throw new Error('OpenAI usage session key is required for credit lookup');
|
|
@@ -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();
|
|
@@ -93,6 +93,10 @@ export const SESSION_CONFIGURE_ACTIONS = Object.freeze([
|
|
|
93
93
|
'deleteMediaAsset',
|
|
94
94
|
'deleteMediaAssets',
|
|
95
95
|
'deleteSchedule',
|
|
96
|
+
'saveCustomProvider',
|
|
97
|
+
'removeCustomProvider',
|
|
98
|
+
'testCustomProvider',
|
|
99
|
+
'discoverCustomProviderModels',
|
|
96
100
|
'deleteSession',
|
|
97
101
|
'deleteWebhook',
|
|
98
102
|
'deleteWorkflow',
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { theme } from '../theme.mjs';
|
|
9
9
|
import { displayModelName } from '../../runtime/shared/model-display.mjs';
|
|
10
|
+
import { isRollingModelAlias } from '../../runtime/shared/model-alias.mjs';
|
|
10
11
|
|
|
11
12
|
export const parsedModelVersion = (id) => {
|
|
12
13
|
const text = String(id || '').toLowerCase();
|
|
@@ -147,7 +148,15 @@ export const normalizeModelOptions = (models) => {
|
|
|
147
148
|
const providerModels = [];
|
|
148
149
|
for (const [family, group] of families.entries()) {
|
|
149
150
|
const limit = modelFamilyLimit(provider, family);
|
|
150
|
-
|
|
151
|
+
// Version limits must not hide rolling routes, even when several
|
|
152
|
+
// vendors' "~..." IDs share the fallback family.
|
|
153
|
+
let versions = 0;
|
|
154
|
+
providerModels.push(
|
|
155
|
+
...group
|
|
156
|
+
.slice()
|
|
157
|
+
.sort(compareModelRecency)
|
|
158
|
+
.filter((model) => isRollingModelAlias(model.id) || versions++ < limit)
|
|
159
|
+
);
|
|
151
160
|
}
|
|
152
161
|
normalized.push(...providerModels.sort(compareModelRecency));
|
|
153
162
|
}
|
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) {
|
|
@@ -55202,6 +55203,11 @@ function createMaintenancePickers({
|
|
|
55202
55203
|
};
|
|
55203
55204
|
}
|
|
55204
55205
|
|
|
55206
|
+
// src/runtime/shared/model-alias.mjs
|
|
55207
|
+
function isRollingModelAlias(id) {
|
|
55208
|
+
return /-latest$/i.test(String(id || "").trim());
|
|
55209
|
+
}
|
|
55210
|
+
|
|
55205
55211
|
// src/tui/app/model-options.mjs
|
|
55206
55212
|
var parsedModelVersion = (id) => {
|
|
55207
55213
|
const text = String(id || "").toLowerCase();
|
|
@@ -55321,7 +55327,10 @@ var normalizeModelOptions = (models) => {
|
|
|
55321
55327
|
const providerModels = [];
|
|
55322
55328
|
for (const [family, group] of families.entries()) {
|
|
55323
55329
|
const limit = modelFamilyLimit(provider, family);
|
|
55324
|
-
|
|
55330
|
+
let versions = 0;
|
|
55331
|
+
providerModels.push(
|
|
55332
|
+
...group.slice().sort(compareModelRecency).filter((model) => isRollingModelAlias(model.id) || versions++ < limit)
|
|
55333
|
+
);
|
|
55325
55334
|
}
|
|
55326
55335
|
normalized.push(...providerModels.sort(compareModelRecency));
|
|
55327
55336
|
}
|
|
@@ -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()
|
|
@@ -159,6 +159,18 @@ export function createSessionIntegrationsApi(bag, { oauthFlows }) {
|
|
|
159
159
|
pushNotice(`provider api key saved: ${result.provider}`, 'info');
|
|
160
160
|
return true;
|
|
161
161
|
},
|
|
162
|
+
saveCustomProvider: announced(
|
|
163
|
+
(input) => runtime.saveCustomProvider(input),
|
|
164
|
+
(result) => `custom provider saved: ${result.id}`
|
|
165
|
+
),
|
|
166
|
+
removeCustomProvider: async (id) => {
|
|
167
|
+
const result = await runtime.removeCustomProvider(id);
|
|
168
|
+
refreshRouteStats();
|
|
169
|
+
pushNotice(`custom provider removed: ${result.provider}`, 'info');
|
|
170
|
+
return result;
|
|
171
|
+
},
|
|
172
|
+
testCustomProvider: (input) => runtime.testCustomProvider(input),
|
|
173
|
+
discoverCustomProviderModels: (input) => runtime.discoverCustomProviderModels(input),
|
|
162
174
|
saveOpenCodeGoUsageAuth: (opts) => {
|
|
163
175
|
runtime.saveOpenCodeGoUsageAuth(opts);
|
|
164
176
|
pushNotice('OpenCode Go usage auth saved', 'info');
|