mixdog 0.9.96 → 0.9.99
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/README.md +0 -17
- package/package.json +16 -8
- package/scripts/build-tui.mjs +6 -6
- package/scripts/{channel-daemon-stub.mjs → daemon-stub.mjs} +18 -12
- 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 +43 -32
- 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 +11 -10
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +4 -13
- package/src/runtime/agent/orchestrator/agent-trace.mjs +19 -5
- package/src/runtime/agent/orchestrator/mcp/client.mjs +81 -33
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +30 -3
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +3 -6
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +23 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +8 -6
- package/src/runtime/agent/orchestrator/session/compact.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +22 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +42 -26
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -3
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +44 -6
- package/src/runtime/agent/orchestrator/session/manager/prefetch-bridge.mjs +12 -6
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +38 -2
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +3 -6
- 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 +59 -42
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +12 -22
- package/src/runtime/agent/orchestrator/tools/builtin/binary-file.mjs +92 -19
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +103 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +80 -30
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +10 -30
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +41 -22
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +55 -11
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +55 -18
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -0
- package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/lib/pwsh-standby-pool.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +94 -105
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -1
- package/src/runtime/attachments/pdf-extract.mjs +83 -0
- package/src/runtime/attachments/store.mjs +515 -0
- package/src/runtime/attachments/store.test.mjs +188 -0
- package/src/runtime/channels/lib/config.mjs +33 -33
- package/src/runtime/channels/lib/inbound-handler.mjs +7 -7
- package/src/runtime/channels/lib/interaction-handlers.mjs +4 -194
- 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 +5 -34
- 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 +4 -70
- package/src/runtime/channels/lib/worker-main.mjs +39 -58
- 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 +72 -211
- package/src/runtime/memory/lib/agent-ipc.mjs +9 -8
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +2 -2
- package/src/runtime/memory/lib/http-router.mjs +1 -6
- package/src/runtime/memory/lib/http-wire.mjs +39 -4
- package/src/runtime/memory/lib/memory-config-flags.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +4 -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/session-ingest.mjs +1 -1
- package/src/runtime/search/index.mjs +2 -2
- package/src/runtime/shared/abort-race.mjs +70 -0
- package/src/runtime/shared/child-spawn-gate.mjs +101 -63
- package/src/runtime/shared/config.mjs +11 -24
- package/src/runtime/shared/json-metrics.mjs +94 -0
- 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 +6 -5
- 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 -22
- package/src/runtime/shared/tool-workload-gates.mjs +29 -0
- package/src/runtime/shared/turn-snapshot.mjs +30 -2
- package/src/runtime/shared/turn-worktree-snapshot.mjs +19 -0
- package/src/session-runtime/channel-config-api.mjs +5 -5
- 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/prewarm.mjs +1 -1
- package/src/session-runtime/provider-models.mjs +12 -2
- package/src/session-runtime/quick-model-rows.mjs +38 -7
- package/src/session-runtime/quick-search-models.mjs +15 -17
- package/src/session-runtime/remote-control.mjs +6 -7
- package/src/session-runtime/runtime-core.mjs +61 -32
- package/src/session-runtime/runtime-tunables.mjs +9 -6
- package/src/session-runtime/self-update.mjs +1 -1
- package/src/session-runtime/session-lifecycle.mjs +36 -15
- package/src/session-runtime/session-title.mjs +28 -4
- package/src/session-runtime/session-turn-api.mjs +55 -15
- package/src/session-runtime/settings-api.mjs +7 -7
- package/src/standalone/agent-dispatch-broker.mjs +1 -1
- package/src/standalone/channel-admin.mjs +42 -53
- package/src/standalone/{channel-daemon-client.mjs → channel-client.mjs} +16 -18
- package/src/standalone/{channel-daemon-transport.mjs → channel-transport.mjs} +73 -58
- package/src/standalone/channel-worker.mjs +124 -647
- package/src/standalone/{backend-daemon.mjs → daemon.mjs} +190 -132
- package/src/standalone/memory-runtime-proxy.mjs +58 -1
- package/src/standalone/{engine-daemon-client.mjs → session-client.mjs} +151 -86
- package/src/standalone/session-protocol.mjs +9 -4
- 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} +192 -157
- package/src/standalone/{engine-daemon-transport.mjs → session-transport.mjs} +142 -63
- package/src/standalone/session-wire.mjs +55 -0
- package/src/tui/App.jsx +4 -4
- package/src/tui/app/app-format.mjs +1 -1
- package/src/tui/app/channel-pickers.mjs +41 -41
- package/src/tui/app/input-parsers.mjs +2 -2
- package/src/tui/app/onboarding-steps.mjs +2 -2
- package/src/tui/app/project-picker.mjs +8 -8
- package/src/tui/app/prompt-submit.mjs +15 -17
- package/src/tui/app/settings-picker.mjs +25 -25
- package/src/tui/app/use-prompt-handlers.mjs +7 -3
- package/src/tui/app/use-prompt-queue-history.mjs +1 -1
- package/src/tui/app/use-transcript-scroll.mjs +1 -1
- package/src/tui/components/ToolExecution.jsx +1 -1
- package/src/tui/components/prompt-input/restore-policy.mjs +1 -1
- package/src/tui/components/tool-execution/surface-detail.mjs +1 -1
- package/src/tui/dist/index.mjs +403 -170
- package/src/tui/hooks/useSession.mjs +16 -0
- package/src/tui/index.jsx +11 -11
- package/src/tui/paste-attachments.mjs +13 -7
- package/src/tui/prompt-history-store.mjs +52 -4
- package/src/tui/{engine → session}/agent-envelope.mjs +3 -3
- package/src/tui/{engine → session}/agent-job-feed.mjs +9 -5
- 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 +28 -14
- 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 +8 -8
- package/src/tui/{engine → session}/render-timing.mjs +2 -2
- package/src/tui/{engine → session}/session-api-ext.mjs +42 -21
- package/src/tui/{engine → session}/session-api.mjs +17 -61
- package/src/tui/{engine → session}/session-flow.mjs +5 -3
- 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 +4 -4
- package/src/tui/{engine → session}/tool-result-status.mjs +5 -5
- package/src/tui/{engine → session}/tool-result-text.mjs +3 -3
- package/src/tui/{engine → session}/transcript-spill.mjs +34 -34
- package/src/tui/{engine → session}/turn.mjs +29 -107
- package/src/tui/{engine-local-session.mjs → session-local.mjs} +72 -78
- package/src/tui/session.mjs +22 -0
- 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-protocol.mjs +0 -32
- 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,15 +1,15 @@
|
|
|
1
|
-
// Transcript spill buffer +
|
|
1
|
+
// Transcript spill buffer + session runtime item state, extracted from session-local.mjs.
|
|
2
2
|
/**
|
|
3
|
-
* src/tui/
|
|
3
|
+
* src/tui/session-local.mjs - the session runtime<->React bridge (React-free).
|
|
4
4
|
*
|
|
5
5
|
* Runs mixdog's session manager outside React and exposes a tiny subscribable
|
|
6
6
|
* store. The React/ink layer consumes it via useSyncExternalStore
|
|
7
|
-
* (see hooks/
|
|
7
|
+
* (see hooks/useSession.mjs).
|
|
8
8
|
*
|
|
9
|
-
* Pure/stateless helpers live in ./
|
|
9
|
+
* Pure/stateless helpers live in ./session/* (boot-profile, session-stats,
|
|
10
10
|
* labels, notice-text, tool-result-text, tool-call-fields, agent-envelope,
|
|
11
11
|
* queue-helpers) and are re-exported here so the public surface is unchanged.
|
|
12
|
-
* This file keeps the stateful
|
|
12
|
+
* This file keeps the stateful session store + notification plan.
|
|
13
13
|
*/
|
|
14
14
|
import { performance } from 'node:perf_hooks';
|
|
15
15
|
import { mkdtempSync, readFileSync, readdirSync, rmSync, statSync, watch, writeFileSync } from 'node:fs';
|
|
@@ -32,8 +32,8 @@ import { presentErrorText } from '../../runtime/shared/err-text.mjs';
|
|
|
32
32
|
import { sessionPath } from '../../runtime/agent/orchestrator/session/store/paths-heartbeat.mjs';
|
|
33
33
|
import { listThemes, getThemeSetting, setThemeSetting } from '../theme.mjs';
|
|
34
34
|
import { resetAllStreamingMarkdownStablePrefixes } from '../markdown/streaming-markdown.mjs';
|
|
35
|
-
import { bootProfile } from '../
|
|
36
|
-
import { createSessionStats, applyUsageDelta } from '../
|
|
35
|
+
import { bootProfile } from '../session/boot-profile.mjs';
|
|
36
|
+
import { createSessionStats, applyUsageDelta } from '../session/session-stats.mjs';
|
|
37
37
|
import {
|
|
38
38
|
pickVerb,
|
|
39
39
|
pickDoneVerb,
|
|
@@ -41,26 +41,26 @@ import {
|
|
|
41
41
|
compactEventLabel,
|
|
42
42
|
compactEventDetail,
|
|
43
43
|
projectNameFromPath,
|
|
44
|
-
} from '../
|
|
45
|
-
import { polishNoticeText } from '../
|
|
44
|
+
} from '../session/labels.mjs';
|
|
45
|
+
import { polishNoticeText } from '../session/notice-text.mjs';
|
|
46
46
|
import {
|
|
47
47
|
toolResultText,
|
|
48
48
|
toolAggregateDetailFallback,
|
|
49
49
|
toolGroupedDisplayFallback,
|
|
50
50
|
toolErrorDisplay,
|
|
51
|
-
} from '../
|
|
51
|
+
} from '../session/tool-result-text.mjs';
|
|
52
52
|
import {
|
|
53
53
|
toolCallId,
|
|
54
54
|
toolResultCallId,
|
|
55
55
|
toolCallName,
|
|
56
56
|
toolCallArgs,
|
|
57
|
-
} from '../
|
|
57
|
+
} from '../session/tool-call-fields.mjs';
|
|
58
58
|
import {
|
|
59
59
|
parseBackgroundTaskEnvelope,
|
|
60
60
|
parseSyntheticAgentMessage,
|
|
61
61
|
toolResultStatus,
|
|
62
62
|
isErrorToolStatus,
|
|
63
|
-
} from '../
|
|
63
|
+
} from '../session/agent-envelope.mjs';
|
|
64
64
|
import {
|
|
65
65
|
queuePriorityValue,
|
|
66
66
|
defaultQueuePriority,
|
|
@@ -74,39 +74,39 @@ import {
|
|
|
74
74
|
mergePastedImages,
|
|
75
75
|
mergePastedTexts,
|
|
76
76
|
callCommitCallbacks,
|
|
77
|
-
} from '../
|
|
77
|
+
} from '../session/queue-helpers.mjs';
|
|
78
78
|
import {
|
|
79
79
|
resolveTuiRuntimeNotificationDelivery,
|
|
80
|
-
} from '../
|
|
81
|
-
import { yieldToRenderer } from '../
|
|
80
|
+
} from '../session/notification-plan.mjs';
|
|
81
|
+
import { yieldToRenderer } from '../session/render-timing.mjs';
|
|
82
82
|
import {
|
|
83
83
|
aggregateRawResult,
|
|
84
84
|
aggregateBucketForCategory,
|
|
85
85
|
aggregateSummaries,
|
|
86
86
|
assignAggregateSummaryOrder,
|
|
87
|
-
} from '../
|
|
88
|
-
import { createToolApproval } from '../
|
|
89
|
-
import { createToolCardResults } from '../
|
|
90
|
-
import { createAgentJobFeed } from '../
|
|
91
|
-
import { appendAgentResponseTail } from '../
|
|
87
|
+
} from '../session/tool-result-status.mjs';
|
|
88
|
+
import { createToolApproval } from '../session/tool-approval.mjs';
|
|
89
|
+
import { createToolCardResults } from '../session/tool-card-results.mjs';
|
|
90
|
+
import { createAgentJobFeed } from '../session/agent-job-feed.mjs';
|
|
91
|
+
import { appendAgentResponseTail } from '../session/agent-response-tail.mjs';
|
|
92
92
|
import {
|
|
93
93
|
appendTuiSteeringPersist,
|
|
94
94
|
dropTuiSteeringPersist,
|
|
95
95
|
drainTuiSteeringPersist,
|
|
96
96
|
flushTuiSteeringPersist,
|
|
97
|
-
} from '../
|
|
98
|
-
import { createContextState } from '../
|
|
99
|
-
import { recomputePromptHistory } from '../
|
|
97
|
+
} from '../session/tui-steering-persist.mjs';
|
|
98
|
+
import { createContextState } from '../session/context-state.mjs';
|
|
99
|
+
import { recomputePromptHistory } from '../session/prompt-history.mjs';
|
|
100
100
|
import {
|
|
101
101
|
appendPromptHistory,
|
|
102
102
|
buildMergedPromptHistory,
|
|
103
103
|
loadPromptHistory,
|
|
104
104
|
} from '../prompt-history-store.mjs';
|
|
105
|
-
import { createSessionFlow } from '../
|
|
106
|
-
import { createRunTurn } from '../
|
|
107
|
-
import {
|
|
108
|
-
import { createFrameBatchedStorePublisher } from '../
|
|
109
|
-
import { createLiveShare, liveSharePipePath } from '../
|
|
105
|
+
import { createSessionFlow } from '../session/session-flow.mjs';
|
|
106
|
+
import { createRunTurn } from '../session/turn.mjs';
|
|
107
|
+
import { createSessionApi } from '../session/session-api.mjs';
|
|
108
|
+
import { createFrameBatchedStorePublisher } from '../session/frame-batched-store.mjs';
|
|
109
|
+
import { createLiveShare, liveSharePipePath } from '../session/live-share.mjs';
|
|
110
110
|
import { displayModelName } from '../../ui/model-display.mjs';
|
|
111
111
|
|
|
112
112
|
export const TUI_DEBUG = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_TUI_DEBUG || ''));
|
|
@@ -484,14 +484,14 @@ export function refillTranscriptViewOverlap(viewItems, previousLiveItems, nextLi
|
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
// Re-export the shared tool-result/notification helpers so importers (and tests)
|
|
487
|
-
// keep resolving them from
|
|
487
|
+
// keep resolving them from session-local.mjs unchanged.
|
|
488
488
|
export { toolResultText, toolAggregateDetailFallback, toolGroupedDisplayFallback };
|
|
489
489
|
export { parseBackgroundTaskEnvelope };
|
|
490
|
-
// Re-export the pure notification delivery plan (moved to ./
|
|
491
|
-
// so importers/tests keep resolving resolveTuiRuntimeNotificationDelivery from
|
|
490
|
+
// Re-export the pure notification delivery plan (moved to ./session/notification-plan.mjs)
|
|
491
|
+
// so importers/tests keep resolving resolveTuiRuntimeNotificationDelivery from session-local.mjs.
|
|
492
492
|
export { resolveTuiRuntimeNotificationDelivery };
|
|
493
493
|
|
|
494
|
-
export function
|
|
494
|
+
export function replaceSessionItemsState({
|
|
495
495
|
state,
|
|
496
496
|
items,
|
|
497
497
|
itemIndexById,
|
|
@@ -513,9 +513,9 @@ export function replaceEngineItemsState({
|
|
|
513
513
|
};
|
|
514
514
|
}
|
|
515
515
|
|
|
516
|
-
// Shared by the live
|
|
516
|
+
// Shared by the live session runtime and focused transcript tests so revision/tail
|
|
517
517
|
// regressions exercise the exact mutation implementation used in production.
|
|
518
|
-
export function
|
|
518
|
+
export function createSessionItemMutators({
|
|
519
519
|
getState,
|
|
520
520
|
set,
|
|
521
521
|
itemIndexById,
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* src/tui/
|
|
2
|
+
* src/tui/session/turn.mjs - lead TUI turn session runtime (createRunTurn). Extracted from session-local.mjs.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { aggregateToolCategoryEntries, aggregateDoneCategories, classifyToolCategory, formatAggregateDetail, summarizeToolResult } from '../../runtime/shared/tool-surface.mjs';
|
|
5
5
|
import { applyUsageDelta } from './session-stats.mjs';
|
|
6
6
|
import { pickVerb, pickDoneVerb, compactEventLabel, compactEventDetail } from './labels.mjs';
|
|
7
7
|
import { toolResultText, toolErrorDisplay, stripShellExitHeader } from './tool-result-text.mjs';
|
|
8
8
|
import { toolCallId, toolResultCallId, toolCallName, toolCallArgs } from './tool-call-fields.mjs';
|
|
9
9
|
import { promptDisplayText, STEERING_SUPPRESSED_DISPLAY } from './queue-helpers.mjs';
|
|
10
10
|
import { TUI_FRAME_MS, yieldToRenderer } from './render-timing.mjs';
|
|
11
|
-
import { createTurnWatchdog } from './turn-watchdog.mjs';
|
|
12
11
|
import { aggregateRawResult, aggregateBucketForCategory, aggregateSummaries, assignAggregateSummaryOrder, failureDetailText, toolCallOutcome } from './tool-result-status.mjs';
|
|
13
12
|
|
|
14
13
|
export const STREAM_BATCH_INTERVAL_MS = TUI_FRAME_MS;
|
|
15
14
|
|
|
16
15
|
export function createRunTurn(bag) {
|
|
17
16
|
const {
|
|
18
|
-
runtime, nextId, tuiDebug,
|
|
17
|
+
runtime, nextId, tuiDebug, flags, pending, itemIndexById, getState, set, flushEmit, flushEmitImmediate, pushItem, appendItems, patchItem, replaceItems, updateStreamingTail: updateStreamingTailFromStore, settleStreamingTail: settleStreamingTailFromStore, clearStreamingTail: clearStreamingTailFromStore, pushNotice, pushUserOrSyntheticItem, markToolCallActive, markToolCallDone, clearActiveToolSummary, agentStatusState, routeState, transcriptRouteMetadata, syncContextStats, denyAllToolApprovals, requestToolApproval, patchToolCardResult, flushToolResults, flushDeferredExecutionPendingResumeKick, drainPendingSteering,
|
|
19
18
|
} = bag;
|
|
20
19
|
// Small fallbacks keep isolated createRunTurn harnesses source-compatible;
|
|
21
|
-
// the real
|
|
20
|
+
// the real session runtime supplies atomic implementations that also maintain revision.
|
|
22
21
|
const updateStreamingTail = updateStreamingTailFromStore || ((id, patch = {}, extra = {}) => {
|
|
23
22
|
set({
|
|
24
23
|
streamingTail: { ...(getState().streamingTail || {}), ...patch, kind: 'assistant', id, streaming: true },
|
|
@@ -48,11 +47,8 @@ export function createRunTurn(bag) {
|
|
|
48
47
|
const turnTranscriptMeta = { ...baseTranscriptMeta, completionVerb };
|
|
49
48
|
flags.pendingTranscriptMeta = null;
|
|
50
49
|
const { at: _userItemAt, ...turnRouteMeta } = turnTranscriptMeta;
|
|
51
|
-
// Per-turn epoch
|
|
52
|
-
//
|
|
53
|
-
// already started a new turn that reuses the per-session mutex — can detect
|
|
54
|
-
// it is stale and skip all shared-getState() writes (busy, flags.activePromptRestore,
|
|
55
|
-
// turndone, drain kick). Neutralizes the stale unwind without touching the mutex.
|
|
50
|
+
// Per-turn epoch prevents a disposed/replaced surface from publishing a
|
|
51
|
+
// terminal snapshot after another turn has become the visible owner.
|
|
56
52
|
const turnEpoch = ++flags.leadTurnEpoch;
|
|
57
53
|
const isCurrentTurn = () => !flags.disposed && flags.leadTurnEpoch === turnEpoch;
|
|
58
54
|
const inputBaseline = getState().stats.inputTokens;
|
|
@@ -79,80 +75,8 @@ export function createRunTurn(bag) {
|
|
|
79
75
|
let assistantText = '';
|
|
80
76
|
let currentAssistantId = null;
|
|
81
77
|
|
|
82
|
-
tuiDebug(`runTurn start turn=${turnIndex} pending=${pending.length}
|
|
83
|
-
|
|
84
|
-
// If this turn stops making observable progress (provider call stuck),
|
|
85
|
-
// trip after the cap: abort the in-flight run via the existing interrupt
|
|
86
|
-
// path (runtime.abort, the same one Esc uses), which rejects the pending
|
|
87
|
-
// runtime.ask() with a SessionClosedError so the normal cancelled-turn
|
|
88
|
-
// teardown runs. Keep this progress-based rather than total wall-clock:
|
|
89
|
-
// long multi-tool turns can legitimately exceed 5m while still producing
|
|
90
|
-
// model/tool events.
|
|
91
|
-
let watchdogGraceTimer = null;
|
|
92
|
-
const configuredLeadToolMaxMs = Number(process.env.MIXDOG_LEAD_TOOL_MAX_MS);
|
|
93
|
-
const leadToolMaxMs = Number.isFinite(configuredLeadToolMaxMs) && configuredLeadToolMaxMs > 0
|
|
94
|
-
? configuredLeadToolMaxMs
|
|
95
|
-
: 30 * 60 * 1000;
|
|
96
|
-
// Scheduling/liveness lives in turn-watchdog.mjs; the trip ACTION stays here
|
|
97
|
-
// because it unwinds this turn's UI state.
|
|
98
|
-
const watchdog = createTurnWatchdog({
|
|
99
|
-
runtime,
|
|
100
|
-
timeoutMs: LEAD_TURN_TIMEOUT_MS,
|
|
101
|
-
toolMaxMs: leadToolMaxMs,
|
|
102
|
-
isCurrentTurn,
|
|
103
|
-
onTrip: ({ idleMs, lastProgressLabel }) => {
|
|
104
|
-
if (_batchTimer !== null) {
|
|
105
|
-
clearTimeout(_batchTimer);
|
|
106
|
-
_batchTimer = null;
|
|
107
|
-
}
|
|
108
|
-
_pendingTextFlush = false;
|
|
109
|
-
_pendingThinkFlush = false;
|
|
110
|
-
_pendingThinkingLastEndedAt = 0;
|
|
111
|
-
const elapsed = Date.now() - startedAt;
|
|
112
|
-
tuiDebug(`runTurn WATCHDOG TRIP turn=${turnIndex} elapsedMs=${elapsed} idleMs=${idleMs} lastProgress=${lastProgressLabel} — aborting stuck turn`);
|
|
113
|
-
pushNotice(`Turn timed out after ${Math.round(idleMs / 1000)}s idle (last progress: ${lastProgressLabel}) — aborting stuck request. Input will be released shortly if abort does not unwind.`, 'warn', { transcript: true });
|
|
114
|
-
try { runtime.abort('watchdog'); } catch {}
|
|
115
|
-
// Belt-and-suspenders: if runtime.abort() did not reject runtime.ask()
|
|
116
|
-
// (unwind starved), hard-release the turn after a short grace so the
|
|
117
|
-
// React store is never left with busy=true and no drain in flight.
|
|
118
|
-
watchdogGraceTimer = setTimeout(() => {
|
|
119
|
-
if (flags.disposed) return;
|
|
120
|
-
if (!getState().busy) return;
|
|
121
|
-
if (flags.leadTurnEpoch !== turnEpoch) return; // a newer turn already owns the store
|
|
122
|
-
tuiDebug(`runTurn WATCHDOG FORCE-RELEASE turn=${turnIndex} — abort unwind starved`);
|
|
123
|
-
denyAllToolApprovals('turn timed out');
|
|
124
|
-
clearActiveToolSummary();
|
|
125
|
-
flushToolResults([], toolCards, cardByCallId, toolGroups, resultsDone, { finalize: true, cancelled: true });
|
|
126
|
-
finalizeToolHeaders();
|
|
127
|
-
clearDeferredTimers();
|
|
128
|
-
flags.flushDeferredBeforeImmediatePush = null;
|
|
129
|
-
if (currentAssistantId && currentAssistantText.trim()) {
|
|
130
|
-
settleStreamingTail(currentAssistantId, { text: currentAssistantText });
|
|
131
|
-
} else {
|
|
132
|
-
clearStreamingTail(currentAssistantId);
|
|
133
|
-
}
|
|
134
|
-
// Bump the epoch FIRST so this (still-stuck) turn's later finally becomes
|
|
135
|
-
// a no-op for shared getState() and cannot corrupt the turn we hand off to.
|
|
136
|
-
flags.leadTurnEpoch++;
|
|
137
|
-
set({ busy: false, spinner: null, thinking: null, lastTurn: null });
|
|
138
|
-
flags.activePromptRestore = null;
|
|
139
|
-
if (flags.draining) flags.draining = false;
|
|
140
|
-
if (pending.length > 0) void drain();
|
|
141
|
-
// busy→false here bypasses both the normal turn-end flush and the
|
|
142
|
-
// drain-finally flush, so a deferred completion kick would never re-arm.
|
|
143
|
-
// Fire it explicitly (idempotent: guarded by deferred flag + !busy).
|
|
144
|
-
flushDeferredExecutionPendingResumeKick();
|
|
145
|
-
flushEmitImmediate?.();
|
|
146
|
-
}, 5_000);
|
|
147
|
-
watchdogGraceTimer.unref?.();
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
const clearWatchdog = () => {
|
|
151
|
-
watchdog.clear();
|
|
152
|
-
if (watchdogGraceTimer) { clearTimeout(watchdogGraceTimer); watchdogGraceTimer = null; }
|
|
153
|
-
};
|
|
154
|
-
const markTurnProgress = (label) => watchdog.markProgress(label);
|
|
155
|
-
watchdog.arm();
|
|
78
|
+
tuiDebug(`runTurn start turn=${turnIndex} pending=${pending.length}`);
|
|
79
|
+
const markTurnProgress = () => isCurrentTurn();
|
|
156
80
|
let currentAssistantText = '';
|
|
157
81
|
// Segments sealed as their own assistant item(s) this turn via
|
|
158
82
|
// commitAssistantSegment (e.g. a tool preamble, then a no-newline tail
|
|
@@ -585,11 +509,11 @@ export function createRunTurn(bag) {
|
|
|
585
509
|
let _lastNewlineIdx = -1; // offset of the last completed-line '\n' found so far
|
|
586
510
|
let _emittedNewlineIdx = -2; // newline offset backing _emittedVisibleText (-2 forces first compute)
|
|
587
511
|
let _emittedVisibleText = ''; // cached visible slice for the current newline offset
|
|
588
|
-
//
|
|
512
|
+
// Session runtime-local streaming scalars. Neither responseLength nor thinkingText is
|
|
589
513
|
// rendered per-token by any consumer: App reads getState().thinking only as a
|
|
590
514
|
// boolean (App.jsx `!!(getState().thinking || liveSpinner?.thinking)`) and the
|
|
591
515
|
// Spinner takes outputTokens, not responseLength. So we keep these growing
|
|
592
|
-
// values in
|
|
516
|
+
// values in session runtime-local vars and publish to the store only on a visible
|
|
593
517
|
// transition (thinking on↔off), a completed visible text line, tool/usage
|
|
594
518
|
// updates, or finalization — not on every 8ms streaming flush.
|
|
595
519
|
let _publishedThinkingActive = false; // last thinking boolean pushed to store
|
|
@@ -690,7 +614,7 @@ export function createRunTurn(bag) {
|
|
|
690
614
|
// reads the thinking flag + timing anchors — none of them render the
|
|
691
615
|
// growing thinkingText. So publish the thinking boolean only on the
|
|
692
616
|
// OFF→ON transition (or when compacting toggles the flag), not on every
|
|
693
|
-
// 8ms reasoning chunk. The full thinkingText stays
|
|
617
|
+
// 8ms reasoning chunk. The full thinkingText stays session runtime-local and is
|
|
694
618
|
// emitted at finalize via the normal spinner/thinking teardown.
|
|
695
619
|
const nextThinkingActive = !compactingActive;
|
|
696
620
|
// Skip the push when the published thinking boolean is unchanged: neither
|
|
@@ -909,10 +833,11 @@ export function createRunTurn(bag) {
|
|
|
909
833
|
const bucket = aggregateBucketForCategory(category, { agentBatch });
|
|
910
834
|
const callId = toolCallId(c);
|
|
911
835
|
const callKey = callId || `__tool_${toolCards.length}_${i}`;
|
|
912
|
-
// The old App scan counted multi-pattern calls via
|
|
913
|
-
//
|
|
836
|
+
// The old App scan counted multi-pattern calls via category work
|
|
837
|
+
// units, not a flat 1. Derive the same
|
|
914
838
|
// count here so the incremental Explore/Search summary matches.
|
|
915
|
-
const
|
|
839
|
+
const categoryEntries = aggregateToolCategoryEntries(name, args, category);
|
|
840
|
+
const activeCount = categoryEntries.reduce((total, entry) => total + Number(entry.count || 1), 0);
|
|
916
841
|
// Track Explore/Search calls as active for the incremental prompt-
|
|
917
842
|
// line summary; cleared when their result lands or the turn ends.
|
|
918
843
|
markToolCallActive(callKey, category, activeCount, Date.now());
|
|
@@ -961,15 +886,16 @@ export function createRunTurn(bag) {
|
|
|
961
886
|
continue;
|
|
962
887
|
}
|
|
963
888
|
|
|
964
|
-
const categoryEntry = aggregateToolCategoryEntry(name, args, category);
|
|
965
889
|
const aggregateCard = ensureAggregateCard(bucket);
|
|
966
890
|
if (shellAfterEdit) aggregateCard.verifyShell = true;
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
891
|
+
for (const categoryEntry of categoryEntries) {
|
|
892
|
+
if (!aggregateCard.categories.has(categoryEntry.key)) aggregateCard.categoryOrder.push(categoryEntry.key);
|
|
893
|
+
const prevCategory = aggregateCard.categories.get(categoryEntry.key);
|
|
894
|
+
aggregateCard.categories.set(categoryEntry.key, {
|
|
895
|
+
...categoryEntry,
|
|
896
|
+
count: Number(prevCategory?.count || 0) + Number(categoryEntry.count || 1),
|
|
897
|
+
});
|
|
898
|
+
}
|
|
973
899
|
aggregateCard.calls.set(callKey, { name, args, category, summary: null, summarySeq: null, isError: false, isCallError: false, isExitError: false, exitCode: null, resultText: null, resolved: false, completedEarly: false });
|
|
974
900
|
touchedAggregates.add(aggregateCard);
|
|
975
901
|
const card = { itemId: aggregateCard.itemId, callId: callKey, done: false, aggregate: aggregateCard };
|
|
@@ -1164,7 +1090,7 @@ export function createRunTurn(bag) {
|
|
|
1164
1090
|
flushStreamBatch();
|
|
1165
1091
|
},
|
|
1166
1092
|
onReasoningDelta: (chunk) => {
|
|
1167
|
-
if (!isCurrentTurn()
|
|
1093
|
+
if (!isCurrentTurn()) return;
|
|
1168
1094
|
if (String(chunk ?? '')) {
|
|
1169
1095
|
if (!markTurnProgress('reasoning-delta')) return;
|
|
1170
1096
|
}
|
|
@@ -1281,13 +1207,9 @@ export function createRunTurn(bag) {
|
|
|
1281
1207
|
} finally {
|
|
1282
1208
|
const isStaleUnwind = !isCurrentTurn();
|
|
1283
1209
|
if (!isStaleUnwind) denyAllToolApprovals(cancelled ? 'turn cancelled' : 'turn finished');
|
|
1284
|
-
// Turn is unwinding normally (or via abort) — cancel the idle watchdog and
|
|
1285
|
-
// its force-release grace so they never fire on a live turn.
|
|
1286
|
-
clearWatchdog();
|
|
1287
1210
|
clearLiveTailTimer();
|
|
1288
|
-
//
|
|
1289
|
-
//
|
|
1290
|
-
// unwind must NOT write shared getState() or it corrupts that turn.
|
|
1211
|
+
// A replaced/disposed surface must not write shared state owned by the
|
|
1212
|
+
// current visible turn.
|
|
1291
1213
|
let closingItems = [];
|
|
1292
1214
|
if (deferredEntries.length) {
|
|
1293
1215
|
if (!isStaleUnwind) {
|
|
@@ -1307,7 +1229,7 @@ export function createRunTurn(bag) {
|
|
|
1307
1229
|
if (!isStaleUnwind) flags.flushDeferredBeforeImmediatePush = null;
|
|
1308
1230
|
closeThinkingSegment();
|
|
1309
1231
|
if (isStaleUnwind) {
|
|
1310
|
-
tuiDebug(`runTurn STALE UNWIND turn=${turnIndex} —
|
|
1232
|
+
tuiDebug(`runTurn STALE UNWIND turn=${turnIndex} — skipping shared UI/state writes`);
|
|
1311
1233
|
} else {
|
|
1312
1234
|
if (currentAssistantId && getState().streamingTail?.id === currentAssistantId) {
|
|
1313
1235
|
if (currentAssistantText.trim()) {
|
|
@@ -1323,7 +1245,7 @@ export function createRunTurn(bag) {
|
|
|
1323
1245
|
flags.activePromptRestore = null;
|
|
1324
1246
|
const elapsedMs = Date.now() - startedAt;
|
|
1325
1247
|
const thinkingElapsedMs = thinkingStartedAt ? accumulatedThinkingMs : 0;
|
|
1326
|
-
// responseLength is
|
|
1248
|
+
// responseLength is session runtime-local now (not pushed per-token), so compute the
|
|
1327
1249
|
// fallback from the live accumulator instead of the possibly-stale
|
|
1328
1250
|
// getState().spinner.responseLength. Final-only / non-streaming turns never
|
|
1329
1251
|
// accumulate `assistantText` (only currentAssistantText is set at the
|
|
@@ -1374,7 +1296,7 @@ export function createRunTurn(bag) {
|
|
|
1374
1296
|
flushEmit?.();
|
|
1375
1297
|
_publishedThinkingActive = false; // turn teardown cleared getState().thinking
|
|
1376
1298
|
const finalStatus = cancelled ? 'cancelled' : (failed ? 'failed' : 'done');
|
|
1377
|
-
tuiDebug(`runTurn end turn=${turnIndex} status=${finalStatus} elapsedMs=${Date.now() - startedAt}
|
|
1299
|
+
tuiDebug(`runTurn end turn=${turnIndex} status=${finalStatus} elapsedMs=${Date.now() - startedAt} pending=${pending.length}`);
|
|
1378
1300
|
return finalStatus;
|
|
1379
1301
|
}
|
|
1380
1302
|
|