mixdog 0.9.95 → 0.9.97
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/LICENSES/Apache-2.0.txt +201 -0
- package/LICENSES/MIT.txt +56 -0
- package/LICENSES/codex-NOTICE.txt +6 -0
- package/NOTICE.md +46 -9
- package/README.md +0 -17
- package/package.json +16 -12
- package/scripts/.tmp-coverage-report.mjs +96 -0
- package/scripts/build-tui.mjs +17 -1
- package/scripts/{channel-daemon-stub.mjs → daemon-stub.mjs} +18 -12
- package/scripts/fixtures/patch-replay-corpus.json +98 -0
- package/src/defaults/mixdog-config.template.json +5 -8
- package/src/defaults/skills/setup/SKILL.md +1 -1
- package/src/lib/keychain-cjs.cjs +1 -1
- package/src/lib/mixdog-debug.cjs +2 -3
- package/src/rules/agent/30-explorer.md +7 -4
- package/src/rules/lead/01-general.md +1 -2
- package/src/rules/lead/lead-tool.md +0 -3
- package/src/rules/shared/01-tool.md +21 -29
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +18 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +31 -1
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +6 -17
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +12 -1
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +4 -13
- package/src/runtime/agent/orchestrator/config.mjs +246 -67
- package/src/runtime/agent/orchestrator/mcp/client.mjs +104 -41
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +23 -0
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +113 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +32 -11
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +89 -2
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +19 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +144 -39
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +22 -8
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/openai-transport-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +3 -4
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +3 -6
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +67 -7
- package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +291 -0
- package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +36 -10
- package/src/runtime/agent/orchestrator/session/compact.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +22 -33
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +0 -24
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +21 -29
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +7 -10
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +45 -25
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -3
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +142 -84
- package/src/runtime/agent/orchestrator/session/manager/prefetch-bridge.mjs +12 -6
- package/src/runtime/agent/orchestrator/session/manager/runtime-loaders.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +31 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +38 -2
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +29 -9
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/result-classification.mjs +65 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +86 -8
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +71 -42
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +4 -33
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -5
- package/src/runtime/agent/orchestrator/stall-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +35 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +116 -26
- package/src/runtime/agent/orchestrator/tools/builtin/binary-file.mjs +92 -19
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +8 -9
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +6 -15
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +12 -26
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -33
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +42 -45
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +27 -9
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +7 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +17 -39
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +73 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +0 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +3 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +30 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +142 -26
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +47 -19
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/lib/pwsh-standby-pool.mjs +83 -13
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +39 -1
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +94 -20
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +16 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +108 -12
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +12 -11
- package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +63 -9
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +2 -2
- package/src/runtime/channels/lib/config.mjs +34 -34
- package/src/runtime/channels/lib/inbound-handler.mjs +8 -8
- package/src/runtime/channels/lib/interaction-handlers.mjs +6 -6
- package/src/runtime/channels/lib/memory-client.mjs +31 -157
- package/src/runtime/channels/lib/output-forwarder.mjs +16 -16
- package/src/runtime/channels/lib/owned-runtime.mjs +60 -60
- package/src/runtime/channels/lib/owner-heartbeat.mjs +1 -1
- package/src/runtime/channels/lib/{backend-dispatch.mjs → provider-dispatch.mjs} +6 -6
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -30
- package/src/runtime/channels/lib/scheduler.mjs +2 -2
- package/src/runtime/channels/lib/status-snapshot.mjs +3 -3
- package/src/runtime/channels/lib/tool-dispatch.mjs +4 -4
- package/src/runtime/channels/lib/worker-bootstrap.mjs +1 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +7 -7
- package/src/runtime/channels/lib/worker-main.mjs +38 -38
- package/src/runtime/channels/{backends → providers}/discord-access.mjs +1 -1
- package/src/runtime/channels/{backends → providers}/discord-gateway.mjs +1 -1
- package/src/runtime/channels/{backends → providers}/discord.mjs +3 -3
- package/src/runtime/channels/{backends → providers}/telegram.mjs +12 -12
- package/src/runtime/channels/tool-defs.mjs +1 -1
- package/src/runtime/memory/index.mjs +76 -248
- package/src/runtime/memory/lib/agent-ipc.mjs +146 -81
- package/src/runtime/memory/lib/compact-vector-cache.mjs +88 -0
- package/src/runtime/memory/lib/core-memory-store.mjs +18 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +13 -34
- package/src/runtime/memory/lib/embedding-provider.mjs +47 -26
- package/src/runtime/memory/lib/embedding-worker.mjs +30 -34
- package/src/runtime/memory/lib/http-router.mjs +9 -6
- package/src/runtime/memory/lib/ko-morph.mjs +3 -20
- package/src/runtime/memory/lib/memory-action-handlers.mjs +2 -1
- package/src/runtime/memory/lib/memory-config-flags.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -0
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -2
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +4 -1
- package/src/runtime/memory/lib/memory-cycle3.mjs +3 -1
- package/src/runtime/memory/lib/memory-daemon-lifecycle.mjs +5 -0
- package/src/runtime/memory/lib/memory-log.mjs +1 -1
- package/src/runtime/memory/lib/memory-process-lock.mjs +5 -60
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/pg/process.mjs +32 -9
- package/src/runtime/memory/lib/session-ingest.mjs +1 -1
- package/src/runtime/memory/tool-defs.mjs +1 -5
- package/src/runtime/search/index.mjs +2 -2
- package/src/runtime/search/tool-defs.mjs +2 -2
- package/src/runtime/shared/abort-race.mjs +70 -0
- package/src/runtime/shared/agent-route-config.mjs +119 -0
- package/src/runtime/shared/atomic-file.mjs +80 -4
- package/src/runtime/shared/channel-notification-routing.mjs +2 -2
- package/src/runtime/shared/child-guardian.mjs +204 -108
- package/src/runtime/shared/child-spawn-gate.mjs +139 -72
- package/src/runtime/shared/config.mjs +120 -47
- package/src/runtime/shared/llm/index.mjs +1 -1
- package/src/runtime/shared/owner-fair-gate.mjs +134 -0
- package/src/runtime/shared/resource-admission.mjs +150 -100
- package/src/runtime/shared/stream-progress.mjs +6 -0
- package/src/runtime/shared/tool-execution-owner.mjs +12 -0
- package/src/runtime/shared/tool-result-summary.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +91 -27
- package/src/runtime/shared/tool-workload-gates.mjs +29 -0
- package/src/runtime/shared/turn-snapshot.mjs +47 -6
- package/src/runtime/shared/turn-worktree-snapshot.mjs +14 -2
- package/src/session-runtime/channel-config-api.mjs +5 -5
- package/src/session-runtime/config-helpers.mjs +39 -37
- package/src/session-runtime/config-lifecycle.mjs +27 -27
- package/src/session-runtime/lifecycle-api.mjs +22 -3
- package/src/session-runtime/memory-runtime-prewarm.mjs +15 -0
- package/src/session-runtime/model-route-api.mjs +7 -5
- package/src/session-runtime/prewarm.mjs +1 -1
- package/src/session-runtime/provider-auth-api.mjs +0 -7
- package/src/session-runtime/provider-models.mjs +1 -1
- package/src/session-runtime/remote-control.mjs +6 -7
- package/src/session-runtime/runtime-core.mjs +63 -39
- package/src/session-runtime/runtime-tunables.mjs +4 -4
- package/src/session-runtime/self-update.mjs +1 -1
- package/src/session-runtime/session-lifecycle.mjs +45 -17
- package/src/session-runtime/session-title.mjs +1 -1
- package/src/session-runtime/session-turn-api.mjs +96 -24
- package/src/session-runtime/settings-api.mjs +12 -24
- package/src/session-runtime/tool-defs.mjs +3 -3
- package/src/session-runtime/workflow-agents-api.mjs +55 -81
- package/src/session-runtime/workflow.mjs +14 -24
- package/src/standalone/agent-dispatch-broker.mjs +198 -0
- package/src/standalone/agent-tool/helpers.mjs +2 -2
- package/src/standalone/agent-tool/spawn-flow.mjs +2 -0
- package/src/standalone/agent-tool/spawn-preset.mjs +11 -21
- package/src/standalone/agent-tool/tool-def.mjs +0 -14
- package/src/standalone/channel-admin.mjs +53 -64
- package/src/standalone/{channel-daemon-client.mjs → channel-client.mjs} +33 -19
- package/src/standalone/{channel-daemon-transport.mjs → channel-transport.mjs} +127 -46
- package/src/standalone/channel-worker.mjs +124 -647
- package/src/standalone/{backend-daemon.mjs → daemon.mjs} +270 -154
- package/src/standalone/explore-tool.mjs +187 -70
- package/src/standalone/fair-call-scheduler.mjs +264 -0
- package/src/standalone/memory-runtime-proxy.mjs +58 -1
- package/src/standalone/session-client.mjs +1134 -0
- package/src/standalone/session-protocol.mjs +192 -0
- package/src/standalone/session-runtime-pool.mjs +393 -0
- package/src/standalone/session-runtime-record.mjs +20 -0
- package/src/standalone/session-runtime-worker.mjs +203 -0
- package/src/standalone/{engine-daemon-service.mjs → session-service.mjs} +481 -224
- package/src/standalone/{engine-daemon-transport.mjs → session-transport.mjs} +390 -121
- package/src/standalone/session-wire.mjs +55 -0
- package/src/tui/App.jsx +34 -21
- package/src/tui/app/app-format.mjs +1 -1
- package/src/tui/app/app-view.jsx +5 -2
- package/src/tui/app/channel-pickers.mjs +41 -41
- package/src/tui/app/core-memory-picker.mjs +19 -19
- package/src/tui/app/doctor.mjs +5 -11
- package/src/tui/app/input-parsers.mjs +2 -2
- package/src/tui/app/message-selector.mjs +103 -0
- package/src/tui/app/onboarding-steps.mjs +3 -4
- package/src/tui/app/project-picker.mjs +80 -56
- package/src/tui/app/prompt-submit.mjs +48 -43
- package/src/tui/app/resume-picker.mjs +2 -2
- package/src/tui/app/route-pickers.mjs +3 -1
- package/src/tui/app/settings-picker.mjs +31 -33
- package/src/tui/app/transcript-window.mjs +38 -0
- package/src/tui/app/use-prompt-draft-flow.mjs +5 -7
- package/src/tui/app/use-prompt-handlers.mjs +86 -21
- package/src/tui/app/use-prompt-hint.mjs +3 -2
- package/src/tui/app/use-prompt-queue-history.mjs +10 -3
- package/src/tui/app/use-transcript-scroll.mjs +10 -3
- package/src/tui/app/use-transcript-window.mjs +50 -17
- package/src/tui/components/PromptInput.jsx +71 -38
- package/src/tui/components/Spinner.jsx +1 -1
- package/src/tui/components/ToolExecution.jsx +1 -1
- package/src/tui/components/prompt-input/edit-helpers.mjs +9 -11
- package/src/tui/components/prompt-input/escape-policy.mjs +42 -0
- package/src/tui/components/prompt-input/immediate-render.mjs +0 -10
- package/src/tui/components/prompt-input/interrupt-policy.mjs +10 -0
- package/src/tui/components/prompt-input/restore-policy.mjs +10 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +1 -1
- package/src/tui/dist/index.mjs +1031 -1676
- package/src/tui/hooks/useSession.mjs +16 -0
- package/src/tui/index.jsx +12 -13
- package/src/tui/{engine → session}/agent-envelope.mjs +3 -3
- package/src/tui/{engine → session}/agent-job-feed.mjs +4 -4
- package/src/tui/{engine → session}/boot-profile.mjs +2 -2
- package/src/tui/{engine → session}/context-state.mjs +3 -3
- package/src/tui/{engine → session}/labels.mjs +2 -2
- package/src/tui/{engine → session}/live-share.mjs +38 -4
- package/src/tui/{engine → session}/notice-text.mjs +2 -2
- package/src/tui/{engine → session}/notification-plan.mjs +3 -3
- package/src/tui/session/oauth-flows.mjs +140 -0
- package/src/tui/{engine → session}/prompt-history.mjs +3 -3
- package/src/tui/{engine → session}/queue-helpers.mjs +2 -2
- package/src/tui/{engine → session}/render-timing.mjs +2 -2
- package/src/tui/{engine → session}/session-api-ext.mjs +30 -20
- package/src/tui/{engine → session}/session-api.mjs +110 -93
- package/src/tui/{engine → session}/session-flow.mjs +8 -2
- package/src/tui/{engine → session}/session-stats.mjs +3 -3
- package/src/tui/{engine → session}/tool-approval.mjs +3 -3
- package/src/tui/{engine → session}/tool-call-fields.mjs +2 -2
- package/src/tui/{engine → session}/tool-card-results.mjs +8 -6
- package/src/tui/{engine → session}/tool-result-status.mjs +5 -5
- package/src/tui/{engine → session}/tool-result-text.mjs +13 -3
- package/src/tui/{engine → session}/transcript-spill.mjs +34 -34
- package/src/tui/{engine → session}/turn.mjs +38 -176
- package/src/tui/{engine-local-session.mjs → session-local.mjs} +103 -103
- package/src/tui/session.mjs +22 -0
- package/src/tui/spinner-meta.mjs +4 -4
- package/src/tui/spinner-verbs.mjs +51 -3
- package/src/ui/statusline-segments.mjs +1 -1
- package/src/ui/statusline.mjs +1 -1
- package/vendor/ink/build/components/App.js +10 -1
- package/src/session-runtime/memory-daemon-probe.mjs +0 -61
- package/src/standalone/engine-daemon-client.mjs +0 -894
- package/src/standalone/engine-daemon-local-bridge.mjs +0 -20
- package/src/standalone/engine-daemon-protocol.mjs +0 -33
- package/src/tui/engine/turn-watchdog.mjs +0 -92
- package/src/tui/engine.mjs +0 -22
- package/src/tui/hooks/useEngine.mjs +0 -16
- /package/src/runtime/agent/orchestrator/session/compact/{engine.mjs → runner.mjs} +0 -0
- /package/src/runtime/channels/{backends → providers}/discord-attachments.mjs +0 -0
- /package/src/tui/{engine → session}/agent-response-tail.mjs +0 -0
- /package/src/tui/{engine → session}/frame-batched-store.mjs +0 -0
- /package/src/tui/{engine → session}/tui-steering-persist.mjs +0 -0
|
@@ -83,6 +83,8 @@ function createDiffTracker(meta = {}) {
|
|
|
83
83
|
return {
|
|
84
84
|
ownerSessionId: clean(meta.ownerSessionId) || null,
|
|
85
85
|
ownerGeneration: Number(meta.ownerGeneration) || 0,
|
|
86
|
+
worktreeRequest: clean(meta.worktreeRequest),
|
|
87
|
+
worktreeContended: false,
|
|
86
88
|
valid: true,
|
|
87
89
|
sealed: false,
|
|
88
90
|
revision: 0,
|
|
@@ -336,13 +338,31 @@ export async function beginTurnSnapshot(_worktree, sessionId) {
|
|
|
336
338
|
generation,
|
|
337
339
|
agents: new Map(),
|
|
338
340
|
});
|
|
339
|
-
const
|
|
341
|
+
const worktreeRequest = pathKey(_worktree);
|
|
342
|
+
const tracker = resetDiffTracker(ownerSessionId, { worktreeRequest });
|
|
340
343
|
trimTurnCache();
|
|
341
344
|
if (!tracker) return;
|
|
345
|
+
for (const [otherSessionId, other] of _diffTrackersBySession) {
|
|
346
|
+
if (otherSessionId === ownerSessionId || other.sealed) continue;
|
|
347
|
+
if (!worktreeRequest || other.worktreeRequest !== worktreeRequest) continue;
|
|
348
|
+
tracker.worktreeContended = true;
|
|
349
|
+
other.worktreeContended = true;
|
|
350
|
+
}
|
|
342
351
|
try {
|
|
343
352
|
const snapshot = await createTurnWorktreeSnapshot(_worktree);
|
|
344
353
|
if (_diffTrackersBySession.get(ownerSessionId) === tracker) {
|
|
345
354
|
tracker.worktreeSnapshot = snapshot;
|
|
355
|
+
if (snapshot?.root) {
|
|
356
|
+
for (const [otherSessionId, other] of _diffTrackersBySession) {
|
|
357
|
+
if (otherSessionId === ownerSessionId) continue;
|
|
358
|
+
if (other.worktreeSnapshot?.root !== snapshot.root) continue;
|
|
359
|
+
// A whole-worktree baseline cannot attribute concurrent mutations
|
|
360
|
+
// to either session. Mark both turns permanently contended and fall
|
|
361
|
+
// back to their exact session-owned mutation trackers.
|
|
362
|
+
tracker.worktreeContended = true;
|
|
363
|
+
other.worktreeContended = true;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
346
366
|
}
|
|
347
367
|
} catch {
|
|
348
368
|
// Git is optional. Exact apply_patch tracking remains the fallback.
|
|
@@ -421,7 +441,7 @@ export function completeAgentTurnReview(handle, patches = []) {
|
|
|
421
441
|
export async function completeTurnSnapshot(sessionId) {
|
|
422
442
|
const tracker = _diffTrackersBySession.get(clean(sessionId));
|
|
423
443
|
if (!tracker) return false;
|
|
424
|
-
if (tracker.worktreeSnapshot) {
|
|
444
|
+
if (tracker.worktreeSnapshot && !tracker.worktreeContended) {
|
|
425
445
|
try { await refreshTurnWorktreeSnapshot(tracker.worktreeSnapshot); } catch {}
|
|
426
446
|
}
|
|
427
447
|
tracker.sealed = true;
|
|
@@ -429,16 +449,34 @@ export async function completeTurnSnapshot(sessionId) {
|
|
|
429
449
|
return true;
|
|
430
450
|
}
|
|
431
451
|
|
|
452
|
+
/** Abandon an aborted Lead turn without waiting for optional Git snapshot IO. */
|
|
453
|
+
export function cancelTurnSnapshot(sessionId) {
|
|
454
|
+
const ownerSessionId = clean(sessionId);
|
|
455
|
+
if (!ownerSessionId) return false;
|
|
456
|
+
let removed = false;
|
|
457
|
+
for (const [trackedSessionId, tracker] of _diffTrackersBySession) {
|
|
458
|
+
if (trackedSessionId !== ownerSessionId && tracker.ownerSessionId !== ownerSessionId) continue;
|
|
459
|
+
releaseDiffTrackerContent(tracker);
|
|
460
|
+
_diffTrackersBySession.delete(trackedSessionId);
|
|
461
|
+
removed = true;
|
|
462
|
+
}
|
|
463
|
+
if (_turnsBySession.delete(ownerSessionId)) removed = true;
|
|
464
|
+
return removed;
|
|
465
|
+
}
|
|
466
|
+
|
|
432
467
|
/** Return the authoritative Lead net diff plus attributed child reviews. */
|
|
433
|
-
export async function getTurnReviewDiff(_worktree, sessionId) {
|
|
468
|
+
export async function getTurnReviewDiff(_worktree, sessionId, options = {}) {
|
|
434
469
|
if (DISABLED) return { supported: false, files: [], patch: '', agents: [] };
|
|
435
470
|
const ownerSessionId = clean(sessionId);
|
|
436
471
|
const turn = _turnsBySession.get(ownerSessionId);
|
|
437
472
|
const tracker = _diffTrackersBySession.get(ownerSessionId);
|
|
438
|
-
|
|
439
|
-
|
|
473
|
+
const isolatedWorktreeSnapshot = tracker?.worktreeContended
|
|
474
|
+
? null
|
|
475
|
+
: tracker?.worktreeSnapshot || null;
|
|
476
|
+
if (isolatedWorktreeSnapshot && !tracker.sealed && options.refresh !== false) {
|
|
477
|
+
try { await refreshTurnWorktreeSnapshot(isolatedWorktreeSnapshot); } catch {}
|
|
440
478
|
}
|
|
441
|
-
const snapshot =
|
|
479
|
+
const snapshot = isolatedWorktreeSnapshot;
|
|
442
480
|
return {
|
|
443
481
|
supported: true,
|
|
444
482
|
files: snapshot?.files || [],
|
|
@@ -455,6 +493,9 @@ export async function getTurnReviewDiff(_worktree, sessionId) {
|
|
|
455
493
|
export async function revertTurnReviewFile(_worktree, sessionId, file) {
|
|
456
494
|
const ownerSessionId = clean(sessionId);
|
|
457
495
|
const tracker = _diffTrackersBySession.get(ownerSessionId);
|
|
496
|
+
if (tracker?.worktreeContended) {
|
|
497
|
+
throw new Error('turn review revert is unavailable while sessions share a worktree');
|
|
498
|
+
}
|
|
458
499
|
if (!tracker?.worktreeSnapshot) throw new Error('turn worktree snapshot is unavailable');
|
|
459
500
|
await revertTurnWorktreeFile(tracker.worktreeSnapshot, file);
|
|
460
501
|
return await getTurnReviewDiff(_worktree, ownerSessionId);
|
|
@@ -43,15 +43,17 @@ function runGit(args, {
|
|
|
43
43
|
allowFailure = false,
|
|
44
44
|
} = {}) {
|
|
45
45
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
46
|
+
const hasInput = (typeof input === 'string' || Buffer.isBuffer(input)) && input.length > 0;
|
|
46
47
|
const child = spawn('git', args, {
|
|
47
48
|
cwd,
|
|
48
49
|
windowsHide: true,
|
|
49
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
50
|
+
stdio: [hasInput ? 'pipe' : 'ignore', 'pipe', 'pipe'],
|
|
50
51
|
});
|
|
51
52
|
const stdout = [];
|
|
52
53
|
const stderr = [];
|
|
53
54
|
let stdoutBytes = 0;
|
|
54
55
|
let stderrBytes = 0;
|
|
56
|
+
let stdinError = null;
|
|
55
57
|
let settled = false;
|
|
56
58
|
const finish = (error, result) => {
|
|
57
59
|
if (settled) return;
|
|
@@ -93,9 +95,19 @@ function runGit(args, {
|
|
|
93
95
|
finish(commandError(args, result.stderr, result.code));
|
|
94
96
|
return;
|
|
95
97
|
}
|
|
98
|
+
if (stdinError && result.code === 0) {
|
|
99
|
+
finish(stdinError);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
96
102
|
finish(null, result);
|
|
97
103
|
});
|
|
98
|
-
child.stdin
|
|
104
|
+
if (child.stdin) {
|
|
105
|
+
// A short-lived git command can close before Node flushes stdin. Keep
|
|
106
|
+
// that transport race inside this promise instead of emitting an
|
|
107
|
+
// unhandled EPIPE that terminates the entire release validator.
|
|
108
|
+
child.stdin.on('error', (error) => { stdinError = error; });
|
|
109
|
+
child.stdin.end(input);
|
|
110
|
+
}
|
|
99
111
|
});
|
|
100
112
|
}
|
|
101
113
|
|
|
@@ -15,10 +15,10 @@ import { runScheduleSession } from '../runtime/shared/schedule-session-run.mjs';
|
|
|
15
15
|
|
|
16
16
|
// Channel/webhook/schedule config surface. Extracted verbatim from the runtime
|
|
17
17
|
// API object; the mutating admin helpers are imported directly here and the
|
|
18
|
-
// runtime injects only the closure-owned callbacks (
|
|
18
|
+
// runtime injects only the closure-owned callbacks (channel provider flush, channel
|
|
19
19
|
// worker handle, soft reload).
|
|
20
20
|
export function createChannelConfigApi({
|
|
21
|
-
|
|
21
|
+
flushChannelProviderSave,
|
|
22
22
|
channels,
|
|
23
23
|
reloadChannelsSoon,
|
|
24
24
|
ensureAutomationRuntime = () => {},
|
|
@@ -26,10 +26,10 @@ export function createChannelConfigApi({
|
|
|
26
26
|
}) {
|
|
27
27
|
return {
|
|
28
28
|
async getChannelSetup() {
|
|
29
|
-
// Flush a pending debounced
|
|
29
|
+
// Flush a pending debounced channel provider switch first so setup readers
|
|
30
30
|
// (Settings → Channel Setting, remote toggles) never observe the
|
|
31
|
-
// previous
|
|
32
|
-
try { await
|
|
31
|
+
// previous provider during the 150ms debounce window.
|
|
32
|
+
try { await flushChannelProviderSave(); } catch {}
|
|
33
33
|
// Bot tokens live in the OS keychain. Reading them before the batch
|
|
34
34
|
// prewarm lands costs one SYNCHRONOUS PowerShell DPAPI decrypt each
|
|
35
35
|
// (measured 971ms + 579ms on a cold desktop boot, blocking the whole
|
|
@@ -7,11 +7,13 @@ import { routeFastKey, fastPreferenceFor } from './model-capabilities.mjs';
|
|
|
7
7
|
export const DEFAULT_PROVIDER = 'anthropic-oauth';
|
|
8
8
|
export const DEFAULT_MODEL = '';
|
|
9
9
|
|
|
10
|
-
// Resolve
|
|
11
|
-
//
|
|
10
|
+
// Resolve a provider-less MAIN selector against the selected Main preset.
|
|
11
|
+
// DEFAULT_PROVIDER is only the unconfigured bootstrap fallback (model remains
|
|
12
|
+
// empty); agent/search override resolution never calls this to synthesize one.
|
|
12
13
|
export function makeResolveDefaultProvider(isKnownProvider) {
|
|
13
14
|
return function resolveDefaultProvider(config) {
|
|
14
|
-
const
|
|
15
|
+
const selectedMain = findPreset(config, config?.default);
|
|
16
|
+
const configured = clean(selectedMain?.provider);
|
|
15
17
|
if (configured && isKnownProvider(configured)) return configured;
|
|
16
18
|
return DEFAULT_PROVIDER;
|
|
17
19
|
};
|
|
@@ -46,16 +48,18 @@ export function makeResolveRoute(resolveDefaultProvider) {
|
|
|
46
48
|
if (explicitModel && !explicitProvider) {
|
|
47
49
|
const preset = findPreset(config, explicitModel);
|
|
48
50
|
if (preset) {
|
|
49
|
-
const p = clean(preset.provider)
|
|
51
|
+
const p = clean(preset.provider);
|
|
50
52
|
const m = clean(preset.model) || DEFAULT_MODEL;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
if (p && m) {
|
|
54
|
+
const saved = modelSettingsFor(config, p, m);
|
|
55
|
+
return {
|
|
56
|
+
provider: p,
|
|
57
|
+
model: m,
|
|
58
|
+
preset,
|
|
59
|
+
effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort ?? preset.effort),
|
|
60
|
+
fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : (preset.fast === true || fastPreferenceFor(config, p, m))),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
|
|
@@ -63,16 +67,18 @@ export function makeResolveRoute(resolveDefaultProvider) {
|
|
|
63
67
|
const defaultKey = config?.default;
|
|
64
68
|
const preset = findPreset(config, defaultKey);
|
|
65
69
|
if (preset) {
|
|
66
|
-
const p = clean(preset.provider)
|
|
70
|
+
const p = clean(preset.provider);
|
|
67
71
|
const m = clean(preset.model) || DEFAULT_MODEL;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
if (p && m) {
|
|
73
|
+
const saved = modelSettingsFor(config, p, m);
|
|
74
|
+
return {
|
|
75
|
+
provider: p,
|
|
76
|
+
model: m,
|
|
77
|
+
preset,
|
|
78
|
+
effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort ?? preset.effort),
|
|
79
|
+
fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : (preset.fast === true || fastPreferenceFor(config, p, m))),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
76
82
|
}
|
|
77
83
|
}
|
|
78
84
|
|
|
@@ -197,14 +203,14 @@ export function resolveAgentTerminalReapMs(config, provider) {
|
|
|
197
203
|
// fall back to the provider's default; else the global 1h default.
|
|
198
204
|
export function resolveAutoClearIdleMs(config, provider) {
|
|
199
205
|
const raw = config?.autoClear && typeof config.autoClear === 'object' ? config.autoClear : {};
|
|
200
|
-
const idleMs = Number(raw.idleMs
|
|
206
|
+
const idleMs = Number(raw.idleMs);
|
|
201
207
|
if (Number.isFinite(idleMs) && idleMs > 0) return Math.max(60_000, Math.round(idleMs));
|
|
202
|
-
return autoClearIdleMsForProvider(provider, raw.providerIdleMs
|
|
208
|
+
return autoClearIdleMsForProvider(provider, raw.providerIdleMs);
|
|
203
209
|
}
|
|
204
210
|
|
|
205
211
|
export function normalizeSystemShellConfig(value = {}) {
|
|
206
212
|
const raw = value && typeof value === 'object' ? value : {};
|
|
207
|
-
const command = clean(raw.command
|
|
213
|
+
const command = clean(raw.command);
|
|
208
214
|
const envCommand = clean(process.env.MIXDOG_SHELL);
|
|
209
215
|
return {
|
|
210
216
|
command,
|
|
@@ -227,7 +233,7 @@ export function normalizeSystemShellCommand(value) {
|
|
|
227
233
|
|
|
228
234
|
export function normalizeAutoClearConfig(value = {}) {
|
|
229
235
|
const raw = value && typeof value === 'object' ? value : {};
|
|
230
|
-
const idleMs = Number(raw.idleMs
|
|
236
|
+
const idleMs = Number(raw.idleMs);
|
|
231
237
|
const hasExplicitIdle = Number.isFinite(idleMs) && idleMs > 0;
|
|
232
238
|
const rawMinPct = Number(raw.minContextPercent);
|
|
233
239
|
const minContextPercent = Number.isFinite(rawMinPct)
|
|
@@ -241,7 +247,7 @@ export function normalizeAutoClearConfig(value = {}) {
|
|
|
241
247
|
enabled: raw.enabled !== false,
|
|
242
248
|
idleMs: hasExplicitIdle ? Math.max(60_000, Math.round(idleMs)) : null,
|
|
243
249
|
custom: hasExplicitIdle,
|
|
244
|
-
providerIdleMs: normalizeAutoClearProviderIdleMs(raw.providerIdleMs
|
|
250
|
+
providerIdleMs: normalizeAutoClearProviderIdleMs(raw.providerIdleMs),
|
|
245
251
|
minContextPercent,
|
|
246
252
|
};
|
|
247
253
|
}
|
|
@@ -254,16 +260,15 @@ export function normalizeCompactTypeSetting(value, fallback = 'recall-fasttrack'
|
|
|
254
260
|
return fallback;
|
|
255
261
|
}
|
|
256
262
|
|
|
257
|
-
export function normalizeCompactionConfig(value = {}
|
|
263
|
+
export function normalizeCompactionConfig(value = {}) {
|
|
258
264
|
const raw = value && typeof value === 'object' ? value : {};
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
void memoryEnabled;
|
|
265
|
+
const compactType = normalizeCompactTypeSetting(raw.type, 'recall-fasttrack');
|
|
266
|
+
const next = { ...raw };
|
|
267
|
+
delete next.compactType;
|
|
268
|
+
delete next.compact_type;
|
|
269
|
+
delete next.enabled;
|
|
265
270
|
return {
|
|
266
|
-
...
|
|
271
|
+
...next,
|
|
267
272
|
auto: raw.auto !== false && raw.enabled !== false,
|
|
268
273
|
type: compactType,
|
|
269
274
|
compactType,
|
|
@@ -284,9 +289,6 @@ export function moduleEnabled(configLike, name, fallback = true) {
|
|
|
284
289
|
export function recapEnabled(configLike, fallback = true) {
|
|
285
290
|
const entry = configLike?.recap;
|
|
286
291
|
if (entry && typeof entry === 'object' && entry.enabled === false) return false;
|
|
287
|
-
// Legacy fallback: pre-migration configs may still carry modules.memory=false.
|
|
288
|
-
if (configLike?.modules?.memory === false) return false;
|
|
289
|
-
if (moduleEnabled(configLike, 'memory', true) === false) return false;
|
|
290
292
|
return fallback !== false;
|
|
291
293
|
}
|
|
292
294
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// same-tick readers see fresh state, and DEBOUNCE the disk write so a burst of
|
|
12
12
|
// toggles collapses into one persist. Three independent debounce channels:
|
|
13
13
|
// - config save (cfgMod.saveConfig, agent-section serialize)
|
|
14
|
-
// -
|
|
14
|
+
// - channel provider save (channel-admin setChannelProvider, file-locked RMW)
|
|
15
15
|
// - outputStyle (sharedCfgMod.updateConfig whole-root RMW — cfgMod.saveConfig
|
|
16
16
|
// only serializes agent-section fields, so a top-level
|
|
17
17
|
// outputStyle would never reach disk via that path)
|
|
@@ -30,7 +30,7 @@ export function createConfigLifecycle({
|
|
|
30
30
|
// shared modules / helpers
|
|
31
31
|
cfgMod,
|
|
32
32
|
sharedCfgMod,
|
|
33
|
-
|
|
33
|
+
setChannelProviderAsync,
|
|
34
34
|
setConfiguredShell,
|
|
35
35
|
normalizeSystemShellConfig,
|
|
36
36
|
normalizeSearchRouteConfig,
|
|
@@ -188,40 +188,40 @@ export function createConfigLifecycle({
|
|
|
188
188
|
return adopted;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
// --- debounced
|
|
192
|
-
let
|
|
193
|
-
let
|
|
194
|
-
let
|
|
191
|
+
// --- debounced channel provider switch ----------------------------------------------
|
|
192
|
+
let pendingChannelProvider = null;
|
|
193
|
+
let channelProviderSaveTimer = null;
|
|
194
|
+
let channelProviderFlushInFlight = null;
|
|
195
195
|
|
|
196
|
-
async function
|
|
197
|
-
while (
|
|
198
|
-
const name =
|
|
196
|
+
async function runChannelProviderFlushAsync() {
|
|
197
|
+
while (pendingChannelProvider !== null) {
|
|
198
|
+
const name = pendingChannelProvider;
|
|
199
199
|
try {
|
|
200
|
-
await
|
|
200
|
+
await setChannelProviderAsync(name);
|
|
201
201
|
} catch (err) {
|
|
202
|
-
process.stderr.write(`[channels] async
|
|
203
|
-
if (
|
|
202
|
+
process.stderr.write(`[channels] async setChannelProvider failed: ${err?.message || err}\n`);
|
|
203
|
+
if (pendingChannelProvider === name) pendingChannelProvider = null;
|
|
204
204
|
break;
|
|
205
205
|
}
|
|
206
|
-
if (
|
|
206
|
+
if (pendingChannelProvider === name) pendingChannelProvider = null;
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
function
|
|
211
|
-
if (
|
|
212
|
-
const start = () =>
|
|
213
|
-
const p =
|
|
214
|
-
|
|
215
|
-
const clear = () => { if (
|
|
210
|
+
function flushChannelProviderSaveAsync() {
|
|
211
|
+
if (channelProviderSaveTimer) { clearTimeout(channelProviderSaveTimer); channelProviderSaveTimer = null; }
|
|
212
|
+
const start = () => runChannelProviderFlushAsync();
|
|
213
|
+
const p = channelProviderFlushInFlight ? channelProviderFlushInFlight.then(start, start) : start();
|
|
214
|
+
channelProviderFlushInFlight = p;
|
|
215
|
+
const clear = () => { if (channelProviderFlushInFlight === p) channelProviderFlushInFlight = null; };
|
|
216
216
|
p.then(clear, clear);
|
|
217
217
|
return p;
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
function
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
220
|
+
function scheduleChannelProviderSave(name) {
|
|
221
|
+
pendingChannelProvider = name;
|
|
222
|
+
if (channelProviderSaveTimer) clearTimeout(channelProviderSaveTimer);
|
|
223
|
+
channelProviderSaveTimer = setTimeout(() => { flushChannelProviderSaveAsync(); }, CONFIG_SAVE_DEBOUNCE_MS);
|
|
224
|
+
channelProviderSaveTimer.unref?.();
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
// --- debounced skills.disabled persist -------------------------------------
|
|
@@ -326,7 +326,7 @@ export function createConfigLifecycle({
|
|
|
326
326
|
async function flushAllConfigSavesAsync() {
|
|
327
327
|
await Promise.all([
|
|
328
328
|
flushConfigSaveAsync(),
|
|
329
|
-
|
|
329
|
+
flushChannelProviderSaveAsync(),
|
|
330
330
|
flushOutputStyleSaveAsync(),
|
|
331
331
|
]);
|
|
332
332
|
// The shared config layer also tracks writes started directly by channel,
|
|
@@ -420,8 +420,8 @@ export function createConfigLifecycle({
|
|
|
420
420
|
// Every lifecycle flush uses the async lock path. A synchronous waiter on
|
|
421
421
|
// the same lock would block the event loop needed by an in-flight async
|
|
422
422
|
// writer, so callers must await this before a dependent read/start.
|
|
423
|
-
|
|
424
|
-
|
|
423
|
+
flushChannelProviderSave: flushChannelProviderSaveAsync,
|
|
424
|
+
scheduleChannelProviderSave,
|
|
425
425
|
flushSkillsSave,
|
|
426
426
|
scheduleSkillsSave,
|
|
427
427
|
flushOutputStyleSave,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
getStoreDir,
|
|
16
16
|
listSessionHeartbeatMtimes,
|
|
17
17
|
} from '../runtime/agent/orchestrator/session/store/paths-heartbeat.mjs';
|
|
18
|
+
import { SessionClosedError } from '../runtime/agent/orchestrator/session/manager/session-errors.mjs';
|
|
18
19
|
|
|
19
20
|
export function resolveResumeCwd(session, currentCwd) {
|
|
20
21
|
const desktop = session?.desktopSession;
|
|
@@ -52,7 +53,7 @@ export function createLifecycleApi(deps) {
|
|
|
52
53
|
beginRoutePreparation, clearRoutePreparation,
|
|
53
54
|
pushTranscriptRebind,
|
|
54
55
|
notificationListeners, remoteStateListeners, desktopSession,
|
|
55
|
-
disposeSessionTitles,
|
|
56
|
+
disposeSessionTitles, abortActiveTurns, getReservedSessionId,
|
|
56
57
|
} = deps;
|
|
57
58
|
const closeSurfaceSession = (session, reason, options) => {
|
|
58
59
|
if (!session?.id) return false;
|
|
@@ -168,6 +169,14 @@ export function createLifecycleApi(deps) {
|
|
|
168
169
|
// of reaping every session's jobs. CLI exit paths never set this.
|
|
169
170
|
const keepBackgroundWork = options?.keepBackgroundWork === true;
|
|
170
171
|
setCloseRequested(true);
|
|
172
|
+
const closingTurnId = getSession()?.id || getReservedSessionId?.() || 'pending';
|
|
173
|
+
try {
|
|
174
|
+
abortActiveTurns?.(new SessionClosedError(
|
|
175
|
+
closingTurnId,
|
|
176
|
+
`runtime close (reason=${reason})`,
|
|
177
|
+
reason,
|
|
178
|
+
));
|
|
179
|
+
} catch {}
|
|
171
180
|
try { stopSelfUpdateBootCheck?.(); } catch {}
|
|
172
181
|
try { disposeSessionTitles?.(); } catch {}
|
|
173
182
|
// Self-update now stages in the background and swaps on the next clean
|
|
@@ -335,8 +344,18 @@ export function createLifecycleApi(deps) {
|
|
|
335
344
|
},
|
|
336
345
|
abort(reason = 'cli-abort') {
|
|
337
346
|
const session = getSession();
|
|
338
|
-
|
|
339
|
-
|
|
347
|
+
const sessionId = session?.id || getReservedSessionId?.() || 'pending';
|
|
348
|
+
const abortError = new SessionClosedError(
|
|
349
|
+
sessionId,
|
|
350
|
+
`runtime abort (reason=${reason})`,
|
|
351
|
+
reason,
|
|
352
|
+
);
|
|
353
|
+
let outerAborted = false;
|
|
354
|
+
try { outerAborted = abortActiveTurns?.(abortError) === true; } catch {}
|
|
355
|
+
const managerAborted = session?.id
|
|
356
|
+
? mgr.abortSessionTurn(session.id, reason)
|
|
357
|
+
: false;
|
|
358
|
+
return outerAborted || managerAborted;
|
|
340
359
|
},
|
|
341
360
|
listSessions(options = {}) {
|
|
342
361
|
return listLeadSessions(options);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Eager in-process memory initialization for remote sessions. Channel ingest
|
|
2
|
+
// shares the daemon-owned proxy, so readiness is the isolated process/port
|
|
3
|
+
// singleflight returned by getMemoryModule.
|
|
4
|
+
|
|
5
|
+
export function prewarmMemoryRuntime({ getMemoryModule, bootProfile }) {
|
|
6
|
+
void (async () => {
|
|
7
|
+
try {
|
|
8
|
+
await new Promise((resolve) => setImmediate(resolve))
|
|
9
|
+
await getMemoryModule()
|
|
10
|
+
bootProfile('channels:memory-prewarm-ready')
|
|
11
|
+
} catch (error) {
|
|
12
|
+
bootProfile('channels:memory-prewarm-failed', { error: error?.message || String(error) })
|
|
13
|
+
}
|
|
14
|
+
})()
|
|
15
|
+
}
|
|
@@ -64,11 +64,13 @@ export function createModelRouteApi(deps) {
|
|
|
64
64
|
return await collectSearchProviderModels({ force: options.force === true || options.refresh === true });
|
|
65
65
|
},
|
|
66
66
|
async setSearchRoute(next) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
let selectedRoute = clean(next?.provider)
|
|
68
|
+
? normalizeSearchRouteConfig(next)
|
|
69
|
+
: normalizeSearchRouteConfig({
|
|
70
|
+
provider: SEARCH_DEFAULT_PROVIDER,
|
|
71
|
+
model: SEARCH_DEFAULT_MODEL,
|
|
72
|
+
...(next?.toolType ? { toolType: next.toolType } : {}),
|
|
73
|
+
});
|
|
72
74
|
if (!selectedRoute) throw new Error('search route requires provider and model');
|
|
73
75
|
if (isDefaultSearchRouteConfig(selectedRoute)) {
|
|
74
76
|
await awaitKeychainPrewarm();
|
|
@@ -132,7 +132,7 @@ export function createPrewarmSchedulers({
|
|
|
132
132
|
if (isCloseRequested()) return;
|
|
133
133
|
// Channels-module and remote toggles gate MESSAGING; automation
|
|
134
134
|
// (enabled schedules/webhooks) keeps the worker boot alive — its
|
|
135
|
-
//
|
|
135
|
+
// channel provider runs headless when messaging is off or unconfigured.
|
|
136
136
|
const automation = await hasActiveAutomation().catch(() => false);
|
|
137
137
|
if (!channelsEnabled() && !automation) {
|
|
138
138
|
bootProfile('channels:start-disabled');
|
|
@@ -193,12 +193,5 @@ export function createProviderAuthApi({
|
|
|
193
193
|
quick: options.quick === true,
|
|
194
194
|
});
|
|
195
195
|
},
|
|
196
|
-
async setDefaultProvider(provider) {
|
|
197
|
-
const requested = clean(provider);
|
|
198
|
-
if (!requested) throw new Error('provider is required');
|
|
199
|
-
if (!isKnownProvider(requested)) throw new Error(`unknown provider "${provider}"`);
|
|
200
|
-
saveConfigAndAdopt({ ...getConfig(), defaultProvider: requested });
|
|
201
|
-
return requested;
|
|
202
|
-
},
|
|
203
196
|
};
|
|
204
197
|
}
|
|
@@ -16,7 +16,7 @@ const PROVIDER_MODELS_PROFILE_ENABLED = /^(1|true|yes|on)$/i.test(String(
|
|
|
16
16
|
));
|
|
17
17
|
|
|
18
18
|
// Raw provider model lists are process-global because the provider registry is
|
|
19
|
-
// process-global in the
|
|
19
|
+
// process-global in the daemon. Session runtimes keep only the cheap
|
|
20
20
|
// hydrated projection (saved effort/fast/current-route ordering). This stops
|
|
21
21
|
// every open pane from repeating the same provider list walk after startup.
|
|
22
22
|
let sharedCatalogRevision = -1;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Remote mode is opt-in per session — only a session that turns it on boots the
|
|
3
3
|
// channel worker and contends for the machine-global bridge seat. Extracted
|
|
4
4
|
// from runtime-core, which injects the mutable session/remote state it owns.
|
|
5
|
-
import {
|
|
5
|
+
import { prewarmMemoryRuntime } from './memory-runtime-prewarm.mjs';
|
|
6
6
|
|
|
7
7
|
export function createRemoteControl({
|
|
8
8
|
getSession,
|
|
@@ -16,7 +16,7 @@ export function createRemoteControl({
|
|
|
16
16
|
channels,
|
|
17
17
|
channelsEnabled,
|
|
18
18
|
hasActiveAutomation,
|
|
19
|
-
|
|
19
|
+
flushChannelProviderSave,
|
|
20
20
|
invokeChannelStart,
|
|
21
21
|
createCurrentSession,
|
|
22
22
|
ensureRemoteTranscriptWriter,
|
|
@@ -31,9 +31,8 @@ export function createRemoteControl({
|
|
|
31
31
|
function startRemote() {
|
|
32
32
|
setRemoteEnabled(true);
|
|
33
33
|
setRemoteSessionId(getSession()?.id || null);
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
startMemoryDaemonEagerly({ getMemoryModule, bootProfile });
|
|
34
|
+
// Warm the daemon-hosted memory module before the first remote ingest.
|
|
35
|
+
prewarmMemoryRuntime({ getMemoryModule, bootProfile });
|
|
37
36
|
// Publish the session record + transcript file BEFORE the worker's
|
|
38
37
|
// activate-time discovery polls, so forwarding binds to THIS terminal
|
|
39
38
|
// instead of a stale neighbour. A no-op in lazy mode; the turn-start rebind
|
|
@@ -54,9 +53,9 @@ export function createRemoteControl({
|
|
|
54
53
|
bootProfile('channels:start-disabled');
|
|
55
54
|
return isRemoteEnabled();
|
|
56
55
|
}
|
|
57
|
-
// A
|
|
56
|
+
// A channel provider switch may still be writing; drain it before the worker
|
|
58
57
|
// reads config rather than racing a sync lock wait.
|
|
59
|
-
try { await
|
|
58
|
+
try { await flushChannelProviderSave(); } catch { /* best-effort */ }
|
|
60
59
|
// Yield before the create/transcript/fork chain so Ink's queued render
|
|
61
60
|
// and input handling are not starved by this detached chain.
|
|
62
61
|
await new Promise((resolve) => setImmediate(resolve));
|