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
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* src/tui/
|
|
2
|
+
* src/tui/session-local.mjs - daemon-hosted local session store.
|
|
3
3
|
*
|
|
4
|
-
* Runs mixdog's session manager inside the singleton
|
|
4
|
+
* Runs mixdog's session manager inside the singleton daemon and exposes the
|
|
5
5
|
* subscribable store projected to clients. React/ink consumes the matching
|
|
6
|
-
* remote view through useSyncExternalStore (see hooks/
|
|
6
|
+
* remote view through useSyncExternalStore (see hooks/useSession.mjs).
|
|
7
7
|
*
|
|
8
|
-
* Product TUI/CLI callers use ./
|
|
9
|
-
* The singleton
|
|
8
|
+
* Product TUI/CLI callers use ./session.mjs and never load this module directly.
|
|
9
|
+
* The singleton daemon and explicit parity tests are the only callers.
|
|
10
10
|
*/
|
|
11
11
|
import { performance } from 'node:perf_hooks';
|
|
12
12
|
import { mkdtempSync, readFileSync, readdirSync, rmSync, statSync, watch, writeFileSync } from 'node:fs';
|
|
@@ -29,8 +29,8 @@ import { presentErrorText } from '../runtime/shared/err-text.mjs';
|
|
|
29
29
|
import { sessionPath } from '../runtime/agent/orchestrator/session/store/paths-heartbeat.mjs';
|
|
30
30
|
import { listThemes, getThemeSetting, setThemeSetting } from './theme.mjs';
|
|
31
31
|
import { resetAllStreamingMarkdownStablePrefixes } from './markdown/streaming-markdown.mjs';
|
|
32
|
-
import { bootProfile } from './
|
|
33
|
-
import { createSessionStats, applyUsageDelta } from './
|
|
32
|
+
import { bootProfile } from './session/boot-profile.mjs';
|
|
33
|
+
import { createSessionStats, applyUsageDelta } from './session/session-stats.mjs';
|
|
34
34
|
import {
|
|
35
35
|
pickVerb,
|
|
36
36
|
pickDoneVerb,
|
|
@@ -38,27 +38,27 @@ import {
|
|
|
38
38
|
compactEventLabel,
|
|
39
39
|
compactEventDetail,
|
|
40
40
|
projectNameFromPath,
|
|
41
|
-
} from './
|
|
42
|
-
import { polishNoticeText } from './
|
|
41
|
+
} from './session/labels.mjs';
|
|
42
|
+
import { polishNoticeText } from './session/notice-text.mjs';
|
|
43
43
|
import {
|
|
44
44
|
toolResultText,
|
|
45
45
|
toolAggregateDetailFallback,
|
|
46
46
|
toolGroupedDisplayFallback,
|
|
47
47
|
toolErrorDisplay,
|
|
48
|
-
} from './
|
|
48
|
+
} from './session/tool-result-text.mjs';
|
|
49
49
|
import {
|
|
50
50
|
toolCallId,
|
|
51
51
|
toolResultCallId,
|
|
52
52
|
toolCallName,
|
|
53
53
|
toolCallArgs,
|
|
54
|
-
} from './
|
|
54
|
+
} from './session/tool-call-fields.mjs';
|
|
55
55
|
import {
|
|
56
56
|
buildExecutionResponseToolItem,
|
|
57
57
|
parseBackgroundTaskEnvelope,
|
|
58
58
|
parseSyntheticAgentMessage,
|
|
59
59
|
toolResultStatus,
|
|
60
60
|
isErrorToolStatus,
|
|
61
|
-
} from './
|
|
61
|
+
} from './session/agent-envelope.mjs';
|
|
62
62
|
import {
|
|
63
63
|
queuePriorityValue,
|
|
64
64
|
defaultQueuePriority,
|
|
@@ -72,50 +72,50 @@ import {
|
|
|
72
72
|
mergePastedImages,
|
|
73
73
|
mergePastedTexts,
|
|
74
74
|
callCommitCallbacks,
|
|
75
|
-
} from './
|
|
75
|
+
} from './session/queue-helpers.mjs';
|
|
76
76
|
import {
|
|
77
77
|
resolveTuiRuntimeNotificationDelivery,
|
|
78
|
-
} from './
|
|
78
|
+
} from './session/notification-plan.mjs';
|
|
79
79
|
import {
|
|
80
80
|
TUI_FRAME_MS,
|
|
81
81
|
cancelRenderAlignedStoreFlush,
|
|
82
82
|
scheduleRenderAlignedStoreFlush,
|
|
83
83
|
yieldToRenderer,
|
|
84
|
-
} from './
|
|
84
|
+
} from './session/render-timing.mjs';
|
|
85
85
|
import {
|
|
86
86
|
aggregateRawResult,
|
|
87
87
|
aggregateBucketForCategory,
|
|
88
88
|
aggregateSummaries,
|
|
89
89
|
assignAggregateSummaryOrder,
|
|
90
|
-
} from './
|
|
91
|
-
import { createToolApproval } from './
|
|
92
|
-
import { createToolCardResults } from './
|
|
93
|
-
import { createAgentJobFeed } from './
|
|
94
|
-
import { appendAgentResponseTail } from './
|
|
90
|
+
} from './session/tool-result-status.mjs';
|
|
91
|
+
import { createToolApproval } from './session/tool-approval.mjs';
|
|
92
|
+
import { createToolCardResults } from './session/tool-card-results.mjs';
|
|
93
|
+
import { createAgentJobFeed } from './session/agent-job-feed.mjs';
|
|
94
|
+
import { appendAgentResponseTail } from './session/agent-response-tail.mjs';
|
|
95
95
|
import {
|
|
96
96
|
appendTuiSteeringPersist,
|
|
97
97
|
dropTuiSteeringPersist,
|
|
98
98
|
drainTuiSteeringPersist,
|
|
99
99
|
flushTuiSteeringPersist,
|
|
100
|
-
} from './
|
|
101
|
-
import { createContextState } from './
|
|
102
|
-
import { recomputePromptHistory } from './
|
|
100
|
+
} from './session/tui-steering-persist.mjs';
|
|
101
|
+
import { createContextState } from './session/context-state.mjs';
|
|
102
|
+
import { recomputePromptHistory } from './session/prompt-history.mjs';
|
|
103
103
|
import {
|
|
104
104
|
appendPromptHistory,
|
|
105
105
|
buildMergedPromptHistory,
|
|
106
106
|
loadPromptHistory,
|
|
107
107
|
} from './prompt-history-store.mjs';
|
|
108
|
-
import { createSessionFlow } from './
|
|
109
|
-
import { createRunTurn } from './
|
|
110
|
-
import {
|
|
111
|
-
import { createFrameBatchedStorePublisher } from './
|
|
112
|
-
import { createLiveShare, liveSharePipePath } from './
|
|
108
|
+
import { createSessionFlow } from './session/session-flow.mjs';
|
|
109
|
+
import { createRunTurn } from './session/turn.mjs';
|
|
110
|
+
import { createSessionApi } from './session/session-api.mjs';
|
|
111
|
+
import { createFrameBatchedStorePublisher } from './session/frame-batched-store.mjs';
|
|
112
|
+
import { createLiveShare, forwardViewerSubmit, liveSharePipePath } from './session/live-share.mjs';
|
|
113
113
|
import { displayModelName } from '../ui/model-display.mjs';
|
|
114
114
|
|
|
115
115
|
const SESSION_RUNTIME_MODULE = '../mixdog-session-runtime.mjs';
|
|
116
116
|
|
|
117
117
|
// The runtime graph is imported lazily, but that import (measured ~250ms) used
|
|
118
|
-
// to land inside the FIRST
|
|
118
|
+
// to land inside the FIRST session runtime creation, which desktop performs while it
|
|
119
119
|
// holds its transition lock — so the user's first navigation paid for it. Hosts
|
|
120
120
|
// can start it during their own idle startup instead; the promise is shared, so
|
|
121
121
|
// the create path either awaits an in-flight preload or does the import itself.
|
|
@@ -132,6 +132,17 @@ export function preloadSessionRuntimeModule() {
|
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
let agentLoopPrewarmPromise = null;
|
|
136
|
+
export function preloadAgentLoopRuntime() {
|
|
137
|
+
agentLoopPrewarmPromise ??= import(
|
|
138
|
+
'../runtime/agent/orchestrator/session/manager/runtime-loaders.mjs'
|
|
139
|
+
).then((module) => module.prewarmAgentLoop());
|
|
140
|
+
void agentLoopPrewarmPromise.catch(() => {
|
|
141
|
+
// Reservation-time prewarm retries through the same shared loader.
|
|
142
|
+
agentLoopPrewarmPromise = null;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
135
146
|
// Windows keychain reads go through a DPAPI PowerShell host whose cold start
|
|
136
147
|
// dominates a packaged boot (measured ~1.5s). The runtime batches every secret
|
|
137
148
|
// into one call, but it only starts that batch when the runtime is created —
|
|
@@ -155,26 +166,14 @@ const TOOL_APPROVAL_TIMEOUT_MS = (() => {
|
|
|
155
166
|
return Number.isFinite(value) && value > 0 ? Math.max(1000, Math.round(value)) : 120_000;
|
|
156
167
|
})();
|
|
157
168
|
|
|
158
|
-
// Idle cap for a single lead TUI turn: the timer is reset by observable model,
|
|
159
|
-
// tool, usage, stage, compact, or steering progress. A provider call that stops
|
|
160
|
-
// producing progress otherwise leaves runTurn holding busy=true with no unwind,
|
|
161
|
-
// so submit() only ever queues and the UI is permanently input-dead. On trip we
|
|
162
|
-
// abort through the SAME interrupt path Esc uses; if that does not unwind, a
|
|
163
|
-
// short grace path force-releases busy=false and drains the queue. Default is
|
|
164
|
-
// intentionally finite (5min). Env-overridable for long local experiments.
|
|
165
|
-
const LEAD_TURN_TIMEOUT_MS = (() => {
|
|
166
|
-
const value = Number(process.env.MIXDOG_LEAD_TURN_TIMEOUT_MS);
|
|
167
|
-
return Number.isFinite(value) && value > 0 ? Math.max(10_000, Math.round(value)) : 5 * 60_000;
|
|
168
|
-
})();
|
|
169
|
-
|
|
170
169
|
// Opt-in diagnostic trace for the hang chain (runTurn start/end, busy-queue
|
|
171
|
-
// enqueue/drain
|
|
170
|
+
// enqueue/drain). Quiet by default so it can never tear through
|
|
172
171
|
// the alternate-screen render; enable with MIXDOG_TUI_DEBUG=1.
|
|
173
|
-
import { tuiDebug, nextId, cleanupStaleTranscriptSpillDirs, createTranscriptSpillBuffer, refillTranscriptViewOverlap,
|
|
174
|
-
export { cleanupStaleTranscriptSpillDirs, createTranscriptSpillBuffer, refillTranscriptViewOverlap,
|
|
175
|
-
export { parseBackgroundTaskEnvelope } from './
|
|
172
|
+
import { tuiDebug, nextId, cleanupStaleTranscriptSpillDirs, createTranscriptSpillBuffer, refillTranscriptViewOverlap, replaceSessionItemsState, createSessionItemMutators, TRANSCRIPT_LIVE_ITEM_CAP, TRANSCRIPT_SPILL_CHUNK_ITEMS } from './session/transcript-spill.mjs';
|
|
173
|
+
export { cleanupStaleTranscriptSpillDirs, createTranscriptSpillBuffer, refillTranscriptViewOverlap, replaceSessionItemsState, createSessionItemMutators, TRANSCRIPT_LIVE_ITEM_CAP, TRANSCRIPT_SPILL_CHUNK_ITEMS } from './session/transcript-spill.mjs';
|
|
174
|
+
export { parseBackgroundTaskEnvelope } from './session/agent-envelope.mjs';
|
|
176
175
|
|
|
177
|
-
export async function
|
|
176
|
+
export async function createLocalSessionRuntime({
|
|
178
177
|
provider: providerName,
|
|
179
178
|
model,
|
|
180
179
|
toolMode = 'full',
|
|
@@ -183,7 +182,7 @@ export async function createLocalEngineSession({
|
|
|
183
182
|
desktopSession,
|
|
184
183
|
} = {}) {
|
|
185
184
|
const startedAt = performance.now();
|
|
186
|
-
bootProfile('
|
|
185
|
+
bootProfile('session:create:start', { provider: providerName, model, toolMode, remote });
|
|
187
186
|
// Silence provider/session diagnostics so they cannot tear through the
|
|
188
187
|
// alternate-screen React/ink render.
|
|
189
188
|
process.env.MIXDOG_QUIET_PROVIDER_LOG = '1';
|
|
@@ -203,7 +202,7 @@ export async function createLocalEngineSession({
|
|
|
203
202
|
...(cwd ? { cwd } : {}),
|
|
204
203
|
...(desktopSession ? { desktopSession } : {}),
|
|
205
204
|
});
|
|
206
|
-
bootProfile('
|
|
205
|
+
bootProfile('session:create:runtime-ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
207
206
|
const runtimeCwd = runtime.cwd || process.cwd();
|
|
208
207
|
const stateStartedAt = performance.now();
|
|
209
208
|
const flags = {
|
|
@@ -224,7 +223,7 @@ export async function createLocalEngineSession({
|
|
|
224
223
|
const displayedExecutionNotificationKeys = new Set();
|
|
225
224
|
const bag = {};
|
|
226
225
|
let state;
|
|
227
|
-
// Route/context/agent-status derivations live in ./
|
|
226
|
+
// Route/context/agent-status derivations live in ./session/context-state.mjs.
|
|
228
227
|
// getState()/getPendingSessionReset() are late-bound to the `state` and
|
|
229
228
|
// `pendingSessionReset` closures declared below; the sync helpers mutate
|
|
230
229
|
// state.stats / display fields IN PLACE exactly as the old inline versions
|
|
@@ -266,7 +265,7 @@ export async function createLocalEngineSession({
|
|
|
266
265
|
toolApproval: null,
|
|
267
266
|
lastTurn: null,
|
|
268
267
|
stats: createSessionStats(),
|
|
269
|
-
// Incremental derivations published by the
|
|
268
|
+
// Incremental derivations published by the session runtime so App does not scan all
|
|
270
269
|
// transcript items on every change:
|
|
271
270
|
// - activeToolSummary: running Explore/Search active counts + earliest
|
|
272
271
|
// startedAt for the prompt-line status (replaces App.jsx O(n) items scan).
|
|
@@ -285,17 +284,17 @@ export async function createLocalEngineSession({
|
|
|
285
284
|
cwd: runtimeCwd,
|
|
286
285
|
themeEpoch: 0,
|
|
287
286
|
};
|
|
288
|
-
bootProfile('
|
|
289
|
-
bootProfile('
|
|
287
|
+
bootProfile('session:route-state-ready', { ms: (performance.now() - stateStartedAt).toFixed(1) });
|
|
288
|
+
bootProfile('session:state-ready', { ms: (performance.now() - stateStartedAt).toFixed(1) });
|
|
290
289
|
const contextStartedAt = performance.now();
|
|
291
290
|
syncContextStats({ allowEstimated: true });
|
|
292
|
-
bootProfile('
|
|
291
|
+
bootProfile('session:context-ready', { ms: (performance.now() - contextStartedAt).toFixed(1) });
|
|
293
292
|
const listeners = new Set();
|
|
294
293
|
// React/useSyncExternalStore reads only this immutable published snapshot.
|
|
295
|
-
// `state` remains the
|
|
294
|
+
// `state` remains the session runtime's synchronous draft until a frame flush swaps
|
|
296
295
|
// the complete draft (including its single revision bump) into this slot.
|
|
297
296
|
let publishedState = process.env.NODE_ENV === 'production' ? state : Object.freeze(state);
|
|
298
|
-
// The mutable
|
|
297
|
+
// The mutable session runtime draft must never be the object exposed to React.
|
|
299
298
|
state = { ...state, stats: { ...state.stats } };
|
|
300
299
|
// Mutations stay synchronous, but React publications are frame-coalesced.
|
|
301
300
|
// structureRevision is committed by the publisher exactly once immediately
|
|
@@ -305,7 +304,7 @@ export async function createLocalEngineSession({
|
|
|
305
304
|
publishState: (next) => {
|
|
306
305
|
publishedState = process.env.NODE_ENV === 'production' ? next : Object.freeze(next);
|
|
307
306
|
// Detach the next draft, including the only intentionally mutable nested
|
|
308
|
-
// record, so
|
|
307
|
+
// record, so internal draft writes cannot mutate the publication.
|
|
309
308
|
state = { ...next, stats: { ...next.stats } };
|
|
310
309
|
},
|
|
311
310
|
listeners,
|
|
@@ -413,7 +412,7 @@ export async function createLocalEngineSession({
|
|
|
413
412
|
// discards the old transcript, so drop any tracked active tool calls.
|
|
414
413
|
activeToolCalls.clear();
|
|
415
414
|
const structureRevision = state.structureRevision;
|
|
416
|
-
state =
|
|
415
|
+
state = replaceSessionItemsState({
|
|
417
416
|
state,
|
|
418
417
|
items: liveItems,
|
|
419
418
|
itemIndexById,
|
|
@@ -430,7 +429,7 @@ export async function createLocalEngineSession({
|
|
|
430
429
|
...transcriptHistoryFlags(),
|
|
431
430
|
},
|
|
432
431
|
});
|
|
433
|
-
//
|
|
432
|
+
// replaceSessionItemsState retains its standalone/test contract. In the live
|
|
434
433
|
// store, defer its revision increment to the frame publication boundary.
|
|
435
434
|
state = { ...state, structureRevision };
|
|
436
435
|
publisher.markStructureChange();
|
|
@@ -446,7 +445,7 @@ export async function createLocalEngineSession({
|
|
|
446
445
|
// (App.jsx recentPromptHistory useMemo). It only changes when a user item is
|
|
447
446
|
// appended, so rebuild it there and on bulk item swaps, publishing to
|
|
448
447
|
// state.promptHistoryList. Pure derivation now lives in
|
|
449
|
-
// ./
|
|
448
|
+
// ./session/prompt-history.mjs (recomputePromptHistory); callers still pass the
|
|
450
449
|
// NEW items array explicitly and publish via set().
|
|
451
450
|
// --- Active-tool summary (Explore/Search) maintained incrementally ---
|
|
452
451
|
// App previously scanned every transcript item on every change to derive the
|
|
@@ -576,7 +575,7 @@ export async function createLocalEngineSession({
|
|
|
576
575
|
}
|
|
577
576
|
return set({ streamingTail: null, ...extra });
|
|
578
577
|
};
|
|
579
|
-
const { patchItem, settleStreamingTail } =
|
|
578
|
+
const { patchItem, settleStreamingTail } = createSessionItemMutators({
|
|
580
579
|
getState: () => state,
|
|
581
580
|
set,
|
|
582
581
|
itemIndexById,
|
|
@@ -823,7 +822,7 @@ export async function createLocalEngineSession({
|
|
|
823
822
|
|
|
824
823
|
|
|
825
824
|
Object.assign(bag, {
|
|
826
|
-
runtime, nextId, tuiDebug,
|
|
825
|
+
runtime, nextId, tuiDebug,
|
|
827
826
|
flags, lifecycle, pending, pendingNotificationKeys, displayedExecutionNotificationKeys, clearExecutionDedupState, listeners, itemIndexById,
|
|
828
827
|
getState: () => state, getPublishedState: () => publishedState,
|
|
829
828
|
set, flushEmit, flushEmitImmediate, disposeEmit: publisher.dispose,
|
|
@@ -842,7 +841,7 @@ export async function createLocalEngineSession({
|
|
|
842
841
|
});
|
|
843
842
|
Object.assign(bag, createSessionFlow(bag));
|
|
844
843
|
bag.runTurn = createRunTurn(bag);
|
|
845
|
-
const api =
|
|
844
|
+
const api = createSessionApi(bag);
|
|
846
845
|
// Cross-surface share: presence + the durable pending spool remain the
|
|
847
846
|
// ownership/base layer; a local pipe layer (live-share) streams frame
|
|
848
847
|
// deltas so co-open surfaces mirror each other in real time.
|
|
@@ -869,14 +868,10 @@ export async function createLocalEngineSession({
|
|
|
869
868
|
const injected = (await runtime.takeRemoteInjections?.()) || [];
|
|
870
869
|
if (injected.length === 0) return;
|
|
871
870
|
for (const item of injected) {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
bag.enqueue(text, id ? { id } : {});
|
|
877
|
-
} else {
|
|
878
|
-
bag.enqueue(item);
|
|
879
|
-
}
|
|
871
|
+
if (!item || typeof item !== 'object' || (item.text == null && item.content == null)) continue;
|
|
872
|
+
const text = item.text ?? item.content;
|
|
873
|
+
const id = typeof item.id === 'string' && item.id.trim() ? item.id.trim() : undefined;
|
|
874
|
+
bag.enqueue(text, id ? { id } : {});
|
|
880
875
|
}
|
|
881
876
|
void bag.drain();
|
|
882
877
|
})().catch(() => { /* the watch/tick pair retries */ });
|
|
@@ -895,7 +890,7 @@ export async function createLocalEngineSession({
|
|
|
895
890
|
getPublishedState: () => publishedState,
|
|
896
891
|
listeners,
|
|
897
892
|
onRemoteSubmit: (text, meta) => {
|
|
898
|
-
// A refusal must be REPORTED, never silent. This
|
|
893
|
+
// A refusal must be REPORTED, never silent. This session runtime can be unable to
|
|
899
894
|
// take a foreign prompt (disposed, or it became an attached viewer
|
|
900
895
|
// itself); swallowing it here is what made a submitted message vanish
|
|
901
896
|
// with no transcript row and no error (user: 입력이 씹힘). The ack sent
|
|
@@ -948,41 +943,46 @@ export async function createLocalEngineSession({
|
|
|
948
943
|
// owner frames own stats/agent/tool state (see runtimePulseTimer above).
|
|
949
944
|
bag.liveShareMirroring = () => state.sessionRemoteAttached && liveShare.viewerConnected();
|
|
950
945
|
// Live viewer submits ride the owner's pipe (instant user bubble + shared
|
|
951
|
-
// streaming); the durable spool
|
|
946
|
+
// streaming); the durable spool remains the fallback. Returns null when this
|
|
947
|
+
// surface is NOT an attached viewer, so the local session runtime keeps the prompt.
|
|
948
|
+
const viewerSubmitIntake = (prompt, options = {}) => {
|
|
949
|
+
if (!state.sessionRemoteAttached) return null;
|
|
950
|
+
const text = String(promptDisplayText(prompt, options) || '').trim();
|
|
951
|
+
if (!text) return { accepted: false };
|
|
952
|
+
return {
|
|
953
|
+
accepted: forwardViewerSubmit({
|
|
954
|
+
text,
|
|
955
|
+
options,
|
|
956
|
+
share: liveShare,
|
|
957
|
+
// Writing to the owner's spool instead of starting a fake local turn
|
|
958
|
+
// that would render an error/synthetic assistant message here.
|
|
959
|
+
spool: (submissionId) => runtime.enqueueRemoteAttachedPrompt?.({
|
|
960
|
+
content: prompt,
|
|
961
|
+
text,
|
|
962
|
+
id: submissionId,
|
|
963
|
+
}) === true,
|
|
964
|
+
}),
|
|
965
|
+
};
|
|
966
|
+
};
|
|
952
967
|
if (typeof api.submit === 'function') {
|
|
953
968
|
const baseSubmit = api.submit;
|
|
954
969
|
api.submit = (prompt, options = {}) => {
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
text: text,
|
|
969
|
-
id: submissionId,
|
|
970
|
-
}) === true;
|
|
971
|
-
// A pipe WRITE is not a delivery: the owner may refuse the prompt or
|
|
972
|
-
// the socket may die between write and read. sendSubmit stays
|
|
973
|
-
// synchronous (the store contract), but an unacknowledged submit is
|
|
974
|
-
// re-delivered through the durable spool — late, never lost.
|
|
975
|
-
if (text && liveShare.sendSubmit(text, {
|
|
976
|
-
id: submissionId,
|
|
977
|
-
submittedAt: options.submittedAt,
|
|
978
|
-
onUndelivered: spoolFallback,
|
|
979
|
-
})) return true;
|
|
980
|
-
return spoolFallback();
|
|
981
|
-
}
|
|
982
|
-
return baseSubmit(prompt, options);
|
|
970
|
+
const forwarded = viewerSubmitIntake(prompt, options);
|
|
971
|
+
return forwarded ? forwarded.accepted : baseSubmit(prompt, options);
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
// The daemon's intake boundary is submitAsync (session-service prefers
|
|
975
|
+
// it over submit). Leaving it unwrapped let a daemon-hosted VIEWER — the
|
|
976
|
+
// desktop pane on a session the terminal owns — queue the prompt locally, so
|
|
977
|
+
// its own user row stood beside the owner's mirrored twin.
|
|
978
|
+
if (typeof api.submitAsync === 'function') {
|
|
979
|
+
const baseSubmitAsync = api.submitAsync;
|
|
980
|
+
api.submitAsync = async (prompt, options = {}) => {
|
|
981
|
+
const forwarded = viewerSubmitIntake(prompt, options);
|
|
982
|
+
return forwarded ? forwarded.accepted : baseSubmitAsync(prompt, options);
|
|
983
983
|
};
|
|
984
984
|
}
|
|
985
|
-
// Viewer stop button: the local
|
|
985
|
+
// Viewer stop button: the local session runtime has no in-flight turn to cancel —
|
|
986
986
|
// forward the interrupt to the owner over the pipe. Falls back to the local
|
|
987
987
|
// abort (no-op safe) when the pipe is down.
|
|
988
988
|
if (typeof api.abort === 'function') {
|
|
@@ -1010,7 +1010,7 @@ export async function createLocalEngineSession({
|
|
|
1010
1010
|
reconcileLiveShareNow();
|
|
1011
1011
|
if (method === 'resume' && result === true && state.sessionRemoteAttached) {
|
|
1012
1012
|
const id = String(state.sessionId || '');
|
|
1013
|
-
//
|
|
1013
|
+
// The session projection holds renderer publications across resume. Keep that hold
|
|
1014
1014
|
// until the owner's first FULL frame replaces the persisted transcript,
|
|
1015
1015
|
// then synchronously publish the complete draft before getState().
|
|
1016
1016
|
// A healthy local owner serves its full frame on connect within tens
|
|
@@ -1025,7 +1025,7 @@ export async function createLocalEngineSession({
|
|
|
1025
1025
|
return result;
|
|
1026
1026
|
};
|
|
1027
1027
|
}
|
|
1028
|
-
// Cover
|
|
1028
|
+
// Cover session runtimes whose runtime already has a session at construction time;
|
|
1029
1029
|
// do not wait for a lifecycle method or the 3s safety pulse to open the pipe.
|
|
1030
1030
|
reconcileLiveShareNow();
|
|
1031
1031
|
// Instant input pickup: watch the shared pending spool so an attached
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI session facade.
|
|
3
|
+
*
|
|
4
|
+
* Product clients receive only a daemon-owned session projection. The large
|
|
5
|
+
* in-process store is isolated in session-local.mjs and is not exposed
|
|
6
|
+
* from this client facade.
|
|
7
|
+
*/
|
|
8
|
+
export {
|
|
9
|
+
cleanupStaleTranscriptSpillDirs,
|
|
10
|
+
createTranscriptSpillBuffer,
|
|
11
|
+
refillTranscriptViewOverlap,
|
|
12
|
+
replaceSessionItemsState,
|
|
13
|
+
createSessionItemMutators,
|
|
14
|
+
TRANSCRIPT_LIVE_ITEM_CAP,
|
|
15
|
+
TRANSCRIPT_SPILL_CHUNK_ITEMS,
|
|
16
|
+
} from './session/transcript-spill.mjs';
|
|
17
|
+
export { parseBackgroundTaskEnvelope } from './session/agent-envelope.mjs';
|
|
18
|
+
|
|
19
|
+
export async function createSessionRuntime(options = {}) {
|
|
20
|
+
const { createSession } = await import('../standalone/session-client.mjs');
|
|
21
|
+
return await createSession(options);
|
|
22
|
+
}
|
package/src/tui/spinner-meta.mjs
CHANGED
|
@@ -9,9 +9,6 @@
|
|
|
9
9
|
* for 30s. One builder, one answer.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
/** Short turns stay quiet: elapsed/token meta only after this long. */
|
|
13
|
-
export const SHOW_TOKENS_AFTER_MS = 30_000;
|
|
14
|
-
|
|
15
12
|
const compactNumberFormatter = new Intl.NumberFormat('en-US', {
|
|
16
13
|
notation: 'compact',
|
|
17
14
|
maximumFractionDigits: 1,
|
|
@@ -62,7 +59,10 @@ export function buildSpinnerMeta({
|
|
|
62
59
|
const thinkingText = spinnerThinkingLabel({ thinking, thinkingSince, thinkingMs, effort });
|
|
63
60
|
return {
|
|
64
61
|
tokensText: tokens > 0 ? `${formatSpinnerTokens(tokens)} tokens` : '',
|
|
65
|
-
|
|
62
|
+
// The count appears with the FIRST token (user: 30초가 아니라 바로).
|
|
63
|
+
// Elapsed was never gated, so the byline used to grow a second field
|
|
64
|
+
// mid-turn for no reason the reader could see; now both arrive together.
|
|
65
|
+
showTokens: tokens > 0,
|
|
66
66
|
thinkingText,
|
|
67
67
|
thinkingActive: Boolean(thinking || Number(thinkingSince) > 0),
|
|
68
68
|
};
|
|
@@ -48,6 +48,40 @@ export const SPINNER_VERBS = [
|
|
|
48
48
|
/** How long one phrase holds before the pool advances. */
|
|
49
49
|
export const SPINNER_VERB_ROTATE_MS = 30_000;
|
|
50
50
|
|
|
51
|
+
/** Odds that a 30s boundary actually swaps the phrase (user: 바꿀지 말지만
|
|
52
|
+
* 랜덤 돌리면 되잖아). Below 1 the window stretches: ~half the boundaries
|
|
53
|
+
* hold, so a phrase lives 30s, 60s, 90s… and the rhythm stops being a
|
|
54
|
+
* metronome. */
|
|
55
|
+
export const SPINNER_VERB_CHANGE_ODDS = 0.5;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Deterministic coin for ONE (turn, boundary) pair.
|
|
59
|
+
*
|
|
60
|
+
* Math.random() is not an option here: spinnerVerbFor runs on every render
|
|
61
|
+
* tick (~1/s) and the TUI and the desktop each call it for themselves, so a
|
|
62
|
+
* real RNG would reroll the word every second AND let the two surfaces
|
|
63
|
+
* disagree. An integer hash keeps each boundary's answer fixed forever while
|
|
64
|
+
* making the SEQUENCE of answers unpredictable — which is the part that reads
|
|
65
|
+
* as random.
|
|
66
|
+
*/
|
|
67
|
+
function boundarySwaps(anchor, boundary) {
|
|
68
|
+
let h = (Math.floor(anchor / 1000) + Math.imul(boundary, 0x9e3779b1)) | 0;
|
|
69
|
+
h = Math.imul(h ^ (h >>> 16), 0x21f0aaad);
|
|
70
|
+
h = Math.imul(h ^ (h >>> 15), 0x735a2d97);
|
|
71
|
+
h = (h ^ (h >>> 15)) >>> 0;
|
|
72
|
+
return h / 0x100000000 < SPINNER_VERB_CHANGE_ODDS;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Past this many boundaries (~34h) every boundary simply swaps; the walk is
|
|
76
|
+
* re-counted each render and must not grow without bound. */
|
|
77
|
+
const MAX_COUNTED_BOUNDARIES = 4096;
|
|
78
|
+
|
|
79
|
+
/** A coin streak can hold a phrase for a very long time (measured: up to 9
|
|
80
|
+
* minutes over 300 turns). After this many consecutive holds the next
|
|
81
|
+
* boundary swaps regardless of its coin, capping one phrase at 4 windows
|
|
82
|
+
* (~2 min) while leaving the 30/60/90/120s variety intact. */
|
|
83
|
+
const MAX_HELD_BOUNDARIES = 3;
|
|
84
|
+
|
|
51
85
|
/**
|
|
52
86
|
* Modes that describe a STATE rather than ongoing work: they replace the pool
|
|
53
87
|
* phrase for as long as the mode lasts, then the rotation resumes. Everything
|
|
@@ -64,14 +98,28 @@ export const SPINNER_MODE_OVERRIDE_VERBS = {
|
|
|
64
98
|
/**
|
|
65
99
|
* The phrase for a turn at a point in time. Derived from the turn's
|
|
66
100
|
* `startedAt` so the TUI and the desktop show the SAME word at the same
|
|
67
|
-
* second, and so mode flips cannot change it — only
|
|
101
|
+
* second, and so mode flips cannot change it — only a 30s boundary can, and
|
|
102
|
+
* only when that boundary's coin says so.
|
|
68
103
|
*/
|
|
69
104
|
export function spinnerVerbFor(startedAt = 0, now = 0, pool = SPINNER_VERBS) {
|
|
70
105
|
const list = Array.isArray(pool) && pool.length ? pool : SPINNER_VERBS;
|
|
71
106
|
const anchor = Math.max(0, Number(startedAt) || 0);
|
|
72
107
|
const elapsed = anchor > 0 ? Math.max(0, (Number(now) || 0) - anchor) : 0;
|
|
73
|
-
const
|
|
74
|
-
|
|
108
|
+
const boundaries = Math.floor(elapsed / SPINNER_VERB_ROTATE_MS);
|
|
109
|
+
// Only the boundaries that FIRED advance the pool; a held boundary leaves
|
|
110
|
+
// the phrase exactly where it was, which is what stretches the window.
|
|
111
|
+
const counted = Math.min(boundaries, MAX_COUNTED_BOUNDARIES);
|
|
112
|
+
let swaps = boundaries - counted;
|
|
113
|
+
let held = 0;
|
|
114
|
+
for (let boundary = 1; boundary <= counted; boundary += 1) {
|
|
115
|
+
if (held >= MAX_HELD_BOUNDARIES || boundarySwaps(anchor, boundary)) {
|
|
116
|
+
swaps += 1;
|
|
117
|
+
held = 0;
|
|
118
|
+
} else {
|
|
119
|
+
held += 1;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const seed = Math.floor(anchor / 1000) + swaps;
|
|
75
123
|
return list[((seed * 7) + 3) % list.length];
|
|
76
124
|
}
|
|
77
125
|
|
|
@@ -54,7 +54,7 @@ export function shellJobsStatus({ clientHostPid, sessionId } = {}) {
|
|
|
54
54
|
return (scope && value.sessions?.[scope]) || EMPTY_SHELL_JOBS_SESSION;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
// Memory cycle L2 segment source. The memory
|
|
57
|
+
// Memory cycle L2 segment source. The daemon-hosted memory runtime writes
|
|
58
58
|
// data/memory-cycle-state.json on every cycle start/finish (index.mjs
|
|
59
59
|
// _writeCycleStateFile) — no HTTP call from the statusline path. Cached at
|
|
60
60
|
// the same 1s cadence as the shell segment. Shows a single unified "Memory"
|
package/src/ui/statusline.mjs
CHANGED
|
@@ -351,7 +351,7 @@ function renderNativeStatusline({
|
|
|
351
351
|
], agentJobs);
|
|
352
352
|
const { runningWorkers } = classifyAgentWorkers(agentPayload.workers);
|
|
353
353
|
// Shell segment scope: one host process can own MANY sessions' jobs (the
|
|
354
|
-
// desktop pools every pane's engine; the daemon
|
|
354
|
+
// desktop pools every pane's engine; the daemon hosts every
|
|
355
355
|
// terminal's session), so the owner-wide aggregate would show one
|
|
356
356
|
// terminal's running shell on every other terminal. Render this session's
|
|
357
357
|
// own jobs whenever we know which session we are; only a session-less
|
|
@@ -42,7 +42,8 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
42
42
|
const readableListenerRef = useRef(undefined);
|
|
43
43
|
const inputParserRef = useRef(createInputParser());
|
|
44
44
|
const pendingInputFlushRef = useRef(undefined);
|
|
45
|
-
//
|
|
45
|
+
// Only partial multi-byte escape sequences wait. A raw read containing one
|
|
46
|
+
// bare ESC is an explicit cancel gesture and must dispatch in this turn.
|
|
46
47
|
const pendingInputFlushDelayMilliseconds = 20;
|
|
47
48
|
const clearPendingInputFlush = useCallback(() => {
|
|
48
49
|
if (!pendingInputFlushRef.current) {
|
|
@@ -233,10 +234,18 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
233
234
|
let chunk;
|
|
234
235
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
|
235
236
|
while ((chunk = stdin.read()) !== null) {
|
|
237
|
+
const hadPendingEscape = inputParserRef.current.hasPendingEscape();
|
|
238
|
+
const bareEscapeChunk = !hadPendingEscape && String(chunk) === escape;
|
|
236
239
|
const inputEvents = inputParserRef.current.push(chunk);
|
|
237
240
|
for (const event of inputEvents) {
|
|
238
241
|
dispatchParsedEvent(event);
|
|
239
242
|
}
|
|
243
|
+
if (bareEscapeChunk && inputParserRef.current.hasPendingEscape()) {
|
|
244
|
+
const escapeEvents = inputParserRef.current.flushPendingEscape();
|
|
245
|
+
for (const event of escapeEvents ?? []) {
|
|
246
|
+
dispatchParsedEvent(event);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
240
249
|
}
|
|
241
250
|
if (inputParserRef.current.hasPendingEscape()) {
|
|
242
251
|
schedulePendingInputFlush();
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// Eager memory-daemon boot for remote sessions. The channels worker forwards
|
|
2
|
-
// transcript ingests to the memory HTTP service, so its port must be published
|
|
3
|
-
// (and actually listening) before the worker sends its first ingest — otherwise
|
|
4
|
-
// early channel traffic finds no port and gets buffered. Extracted from
|
|
5
|
-
// runtime-core's startRemote, which now just fires it.
|
|
6
|
-
import * as httpMod from 'node:http';
|
|
7
|
-
|
|
8
|
-
const HEALTH_ATTEMPTS = 30;
|
|
9
|
-
const HEALTH_RETRY_MS = 500;
|
|
10
|
-
const HEALTH_TIMEOUT_MS = 1500;
|
|
11
|
-
|
|
12
|
-
function healthOk(port) {
|
|
13
|
-
return new Promise((resolve) => {
|
|
14
|
-
const request = httpMod.request(
|
|
15
|
-
{ hostname: '127.0.0.1', port, path: '/health', timeout: HEALTH_TIMEOUT_MS },
|
|
16
|
-
(response) => {
|
|
17
|
-
let body = '';
|
|
18
|
-
response.on('data', (chunk) => { body += chunk; });
|
|
19
|
-
response.on('end', () => {
|
|
20
|
-
try { resolve(JSON.parse(body)?.status === 'ok'); } catch { resolve(false); }
|
|
21
|
-
});
|
|
22
|
-
},
|
|
23
|
-
);
|
|
24
|
-
request.on('error', () => resolve(false));
|
|
25
|
-
request.on('timeout', () => { request.destroy(); resolve(false); });
|
|
26
|
-
request.end();
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** Detached, never throws: resolving the module only hands back a handle, so
|
|
31
|
-
* this awaits start() and then polls /health until the daemon is provably
|
|
32
|
-
* reachable (or the failure is profiled). */
|
|
33
|
-
export function startMemoryDaemonEagerly({ getMemoryModule, bootProfile }) {
|
|
34
|
-
void (async () => {
|
|
35
|
-
try {
|
|
36
|
-
// Yield one tick first: the module resolve + daemon fork below is a long
|
|
37
|
-
// synchronous chain that would otherwise starve Ink's queued render and
|
|
38
|
-
// keypress handling.
|
|
39
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
40
|
-
const module = await getMemoryModule();
|
|
41
|
-
const started = typeof module?.start === 'function' ? await module.start() : null;
|
|
42
|
-
const port = started?.port;
|
|
43
|
-
if (!port) {
|
|
44
|
-
bootProfile('channels:memory-eager-init-failed', { error: 'no port from start()' });
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
for (let attempt = 0; attempt < HEALTH_ATTEMPTS; attempt += 1) {
|
|
48
|
-
try {
|
|
49
|
-
if (await healthOk(port)) {
|
|
50
|
-
bootProfile('channels:memory-eager-init-ready', { port });
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
} catch { /* probe failure is retried below */ }
|
|
54
|
-
await new Promise((resolve) => setTimeout(resolve, HEALTH_RETRY_MS));
|
|
55
|
-
}
|
|
56
|
-
bootProfile('channels:memory-eager-init-failed', { error: `health not ok after retries (port ${port})` });
|
|
57
|
-
} catch (error) {
|
|
58
|
-
bootProfile('channels:memory-eager-init-failed', { error: error?.message || String(error) });
|
|
59
|
-
}
|
|
60
|
-
})();
|
|
61
|
-
}
|