mixdog 1.0.4 → 1.0.6
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 +117 -434
- package/package.json +18 -17
- package/scripts/gate-local.mjs +93 -0
- package/scripts/lib/gate-scheduler.mjs +24 -0
- package/scripts/lib/run-node-tests.mjs +5 -1
- package/scripts/release-paths.mjs +22 -1
- package/scripts/test-direct.mjs +2 -1
- package/scripts/test.mjs +3 -1
- package/src/defaults/agents.json +0 -12
- package/src/defaults/mixdog-config.template.json +1 -2
- package/src/defaults/skills/docx/SKILL.md +7 -4
- package/src/defaults/skills/docx/references/html.md +46 -0
- package/src/defaults/skills/docx/references/native-authoring.md +18 -4
- package/src/defaults/skills/pdf/SKILL.md +10 -2
- package/src/defaults/skills/pdf/references/html.md +64 -0
- package/src/defaults/skills/pptx/references/html.md +1 -1
- package/src/defaults/skills/xlsx/SKILL.md +7 -3
- package/src/defaults/skills/xlsx/references/html.md +40 -0
- package/src/defaults/skills/xlsx/references/report-design.md +21 -4
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
- package/src/runtime/agent/orchestrator/internal-agents.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +6 -5
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +50 -9
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +28 -1
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +34 -8
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/transcript-restore/agent-envelope.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +1 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +3 -1
- package/src/runtime/attachments/pdf-extract.mjs +7 -3
- package/src/runtime/computer-bridge/client.mjs +1 -1
- package/src/runtime/memory/index.mjs +4 -15
- package/src/runtime/memory/lib/core-memory-store.mjs +1 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +0 -2
- package/src/runtime/memory/lib/cycle-scheduler/backlog-probe.mjs +3 -11
- package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +1 -3
- package/src/runtime/memory/lib/cycle-scheduler/scheduled-enqueue.mjs +1 -11
- package/src/runtime/memory/lib/cycle-scheduler/tick-loop.mjs +0 -2
- package/src/runtime/memory/lib/cycle-scheduler.mjs +4 -13
- package/src/runtime/memory/lib/cycle-signatures.mjs +0 -6
- package/src/runtime/memory/lib/cycle1/cycle1-rows.mjs +1 -1
- package/src/runtime/memory/lib/cycle1/cycle1-window.mjs +2 -2
- package/src/runtime/memory/lib/embedding-reindex.mjs +1 -1
- package/src/runtime/memory/lib/http-router/lifecycle-routes.mjs +0 -1
- package/src/runtime/memory/lib/memory-action-handlers/cycle-actions.mjs +7 -52
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +6 -22
- package/src/runtime/memory/lib/memory-action-handlers/cycle-rebuild-action.mjs +10 -17
- package/src/runtime/memory/lib/memory-action-handlers/maintenance-actions.mjs +1 -3
- package/src/runtime/memory/lib/memory-action-handlers/manage-actions.mjs +2 -4
- package/src/runtime/memory/lib/memory-action-handlers.mjs +2 -4
- package/src/runtime/memory/lib/memory-cycle-requests.mjs +1 -1
- package/src/runtime/memory/lib/{memory-cycle2-shared.mjs → memory-cycle-shared.mjs} +14 -16
- package/src/runtime/memory/lib/memory-cycle.mjs +3 -2
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
- package/src/runtime/memory/lib/memory-embed.mjs +14 -1
- package/src/runtime/memory/lib/memory-ops-policy.mjs +8 -11
- package/src/runtime/memory/lib/memory-schema/entries.mjs +0 -1
- package/src/runtime/memory/lib/memory.mjs +2 -6
- package/src/runtime/memory/lib/pg/adapter.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +1 -1
- package/src/runtime/memory/lib/query-hybrid-search.mjs +1 -1
- package/src/runtime/memory/lib/query-maintenance-handlers.mjs +0 -6
- package/src/runtime/memory/lib/recall-format.mjs +1 -1
- package/src/runtime/office/authoring/docx-html-build.mjs +375 -0
- package/src/runtime/office/authoring/docx-html-measure.mjs +561 -0
- package/src/runtime/office/authoring/docx-html-runner.mjs +102 -0
- package/src/runtime/office/authoring/html-browser.mjs +61 -0
- package/src/runtime/office/authoring/html-document-author.mjs +181 -0
- package/src/runtime/office/authoring/html-source-drift.mjs +36 -0
- package/src/runtime/office/authoring/pdf-author-action.mjs +266 -0
- package/src/runtime/office/authoring/pdf-html-charts.mjs +379 -0
- package/src/runtime/office/authoring/pdf-html-runner.mjs +317 -0
- package/src/runtime/office/authoring/pptx-author-action.mjs +44 -22
- package/src/runtime/office/authoring/pptx-author-session.mjs +12 -6
- package/src/runtime/office/authoring/pptx-html-build.mjs +23 -4
- package/src/runtime/office/authoring/pptx-html-measure.mjs +38 -44
- package/src/runtime/office/authoring/pptx-html-runner.mjs +18 -3
- package/src/runtime/office/authoring/pptx-receipt.mjs +39 -31
- package/src/runtime/office/authoring/pptx-review-artifacts.mjs +9 -1
- package/src/runtime/office/authoring/pptx-script-runner.mjs +6 -2
- package/src/runtime/office/authoring/xlsx-html-build.mjs +365 -0
- package/src/runtime/office/authoring/xlsx-html-measure.mjs +257 -0
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +139 -0
- package/src/runtime/office/capabilities-catalog.mjs +8 -0
- package/src/runtime/office/capabilities-signatures.mjs +18 -6
- package/src/runtime/office/capabilities.mjs +26 -1
- package/src/runtime/office/com/office-com-host.ps1 +133 -17
- package/src/runtime/office/core/office-actions-batch.mjs +16 -1
- package/src/runtime/office/core/office-actions-inspect.mjs +3 -2
- package/src/runtime/office/core/office-actions-lifecycle.mjs +6 -2
- package/src/runtime/office/core/office-actions-render.mjs +4 -1
- package/src/runtime/office/core/office-finalize/review-stage.mjs +15 -2
- package/src/runtime/office/core/office-qa/design-review-stage.mjs +9 -0
- package/src/runtime/office/core/office-session-dispatch.mjs +3 -0
- package/src/runtime/office/core/office-sessionless-actions.mjs +7 -2
- package/src/runtime/office/core/office-sessions.mjs +19 -2
- package/src/runtime/office/pdf/pdf-writer.mjs +1 -1
- package/src/runtime/office/portable/docx-formatting.mjs +21 -0
- package/src/runtime/office/portable/ooxml-validator.mjs +22 -1
- package/src/runtime/office/portable/portable-chart.mjs +10 -2
- package/src/runtime/office/portable/portable-docx-edits.mjs +12 -6
- package/src/runtime/office/portable/portable-docx-operations.mjs +75 -1
- package/src/runtime/office/portable/portable-docx-xml.mjs +49 -6
- package/src/runtime/office/portable/portable-docx.mjs +2 -0
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +2 -0
- package/src/runtime/office/portable/portable-validation.mjs +6 -3
- package/src/runtime/office/portable/portable-xlsx-cell-values.mjs +87 -0
- package/src/runtime/office/portable/portable-xlsx-charts.mjs +55 -13
- package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +1 -1
- package/src/runtime/office/portable/xlsx-contract.mjs +82 -3
- package/src/runtime/office/quality/assurance-checklist.mjs +2 -2
- package/src/runtime/office/quality/assurance-rendered.mjs +27 -1
- package/src/runtime/office/quality/assurance-structure-docx.mjs +44 -0
- package/src/runtime/office/quality/assurance-structure-pptx.mjs +24 -6
- package/src/runtime/office/quality/assurance-structure-xlsx.mjs +50 -1
- package/src/runtime/office/quality/design-review-critique.mjs +3 -2
- package/src/runtime/office/quality/document-brief.mjs +44 -0
- package/src/runtime/office/quality/inline-audit.mjs +7 -2
- package/src/runtime/office/quality/quality-pipeline.mjs +9 -1
- package/src/runtime/office/tool-defs.mjs +2 -2
- package/src/runtime/shared/llm/quota-value-estimate.mjs +317 -0
- package/src/runtime/shared/llm/usage-ledger-quota.mjs +266 -63
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +35 -0
- package/src/runtime/shared/llm/usage-ledger-worker.mjs +2 -1
- package/src/runtime/shared/llm/usage-ledger.mjs +10 -1
- package/src/runtime/shared/provider-accounts.mjs +11 -1
- package/src/runtime/shared/statusline/statusline-segments.mjs +4 -4
- package/src/runtime/shared/tool-execution-contract.mjs +1 -1
- package/src/runtime/shared/wait-until.test-support.mjs +33 -0
- package/src/session-runtime/boot/apis.mjs +1 -1
- package/src/session-runtime/services/channel-admin.mjs +4 -3
- package/src/session-runtime/services/usage-stats-model.mjs +22 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +6 -1
- package/src/session-runtime/turn/session-ops.mjs +15 -9
- package/src/session-runtime/usage-stats-api.mjs +34 -2
- package/src/standalone/agent-dispatch-broker.mjs +1 -1
- package/src/standalone/local-session-runtime.mjs +488 -0
- package/src/standalone/session-protocol.mjs +1 -0
- package/src/standalone/session-runtime-inline-host.mjs +1 -1
- package/src/tui/hooks/useSession.mjs +1 -1
- package/src/tui/session/session-api/commands.mjs +1 -1
- package/src/tui/session/session-api/integrations.mjs +3 -0
- package/src/tui/session/session-flow/auto-clear.mjs +6 -7
- package/src/tui/session/tool-card-results.mjs +1 -1
- package/src/tui/session/turn.mjs +5 -0
- package/src/tui/session-local.mjs +3 -488
- package/src/tui/session.mjs +1 -1
- package/src/ui/statusline-agents.mjs +0 -1
- package/src/rules/agent/41-cycle2-agent.md +0 -24
- package/src/runtime/memory/lib/cycle-scheduler/cycle2-runs.mjs +0 -120
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +0 -110
- package/src/runtime/memory/lib/memory-cycle2-quarantine.mjs +0 -99
- package/src/runtime/memory/lib/memory-cycle2-review.mjs +0 -240
- package/src/runtime/memory/lib/memory-cycle2.mjs +0 -191
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon-hosted session store shared by terminal, desktop and remote clients.
|
|
3
|
+
*
|
|
4
|
+
* Runs mixdog's session manager inside the singleton daemon and exposes the
|
|
5
|
+
* subscribable store projected to clients. React/ink consumes the matching
|
|
6
|
+
* remote view through useSyncExternalStore (src/tui/hooks/useSession.mjs).
|
|
7
|
+
*
|
|
8
|
+
* Product TUI/CLI callers use src/tui/session.mjs, never this implementation.
|
|
9
|
+
* The singleton daemon and explicit parity tests are the only callers.
|
|
10
|
+
*/
|
|
11
|
+
import { performance } from 'node:perf_hooks';
|
|
12
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
13
|
+
import { bootProfile } from '../tui/session/boot-profile.mjs';
|
|
14
|
+
import { createGoalContinuation } from '../tui/session/goal-continuation.mjs';
|
|
15
|
+
import { createToolApproval } from '../tui/session/tool-approval.mjs';
|
|
16
|
+
import { createToolCardResults } from '../tui/session/tool-card-results.mjs';
|
|
17
|
+
import { createAgentJobFeed } from '../tui/session/agent-job-feed.mjs';
|
|
18
|
+
import { createContextState } from '../tui/session/context-state.mjs';
|
|
19
|
+
import { createSessionFlow } from '../tui/session/session-flow.mjs';
|
|
20
|
+
import { createRunTurn } from '../tui/session/turn.mjs';
|
|
21
|
+
import { createSessionApi } from '../tui/session/session-api.mjs';
|
|
22
|
+
import { createSessionDraftStore } from '../tui/session/draft-store.mjs';
|
|
23
|
+
import { createInitialSessionState } from '../tui/session/initial-state.mjs';
|
|
24
|
+
import { createActiveToolTracker } from '../tui/session/active-tool-summary.mjs';
|
|
25
|
+
import { createTranscriptStore } from '../tui/session/transcript-store.mjs';
|
|
26
|
+
import { createTranscriptIntake } from '../tui/session/transcript-intake.mjs';
|
|
27
|
+
import { createNoticeSurface } from '../tui/session/notice-surface.mjs';
|
|
28
|
+
import { attachCrossSurfaceShare } from '../tui/session/cross-surface-share.mjs';
|
|
29
|
+
import { createTranscriptRouteMetadata } from '../runtime/shared/transcript-metadata.mjs';
|
|
30
|
+
import { formatDoctorReport, runDoctorChecks } from '../tui/app/doctor.mjs';
|
|
31
|
+
// tuiDebug is an opt-in diagnostic trace for the hang chain (runTurn start/end,
|
|
32
|
+
// busy-queue enqueue/drain). Quiet by default so it can never tear through
|
|
33
|
+
// the alternate-screen render; enable with MIXDOG_TUI_DEBUG=1.
|
|
34
|
+
import { tuiDebug, nextId, createTranscriptSpillBuffer } from '../tui/session/transcript-spill.mjs';
|
|
35
|
+
|
|
36
|
+
const SESSION_RUNTIME_MODULE = '../mixdog-session-runtime.mjs';
|
|
37
|
+
|
|
38
|
+
// The runtime graph is imported lazily, but that import (measured ~250ms) used
|
|
39
|
+
// to land inside the FIRST session runtime creation, which desktop performs while it
|
|
40
|
+
// holds its transition lock — so the user's first navigation paid for it. Hosts
|
|
41
|
+
// can start it during their own idle startup instead; the promise is shared, so
|
|
42
|
+
// the create path either awaits an in-flight preload or does the import itself.
|
|
43
|
+
let sessionRuntimeModulePromise = null;
|
|
44
|
+
function importSessionRuntimeModule() {
|
|
45
|
+
sessionRuntimeModulePromise ??= import(SESSION_RUNTIME_MODULE);
|
|
46
|
+
return sessionRuntimeModulePromise;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function preloadSessionRuntimeModule() {
|
|
50
|
+
return importSessionRuntimeModule().catch(() => {
|
|
51
|
+
// A real load failure surfaces on the authoritative create path.
|
|
52
|
+
sessionRuntimeModulePromise = null;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let agentLoopPrewarmPromise = null;
|
|
57
|
+
export function preloadAgentLoopRuntime() {
|
|
58
|
+
agentLoopPrewarmPromise ??= import('../runtime/agent/orchestrator/session/manager/runtime-loaders.mjs').then(
|
|
59
|
+
(module) => module.prewarmAgentLoop()
|
|
60
|
+
);
|
|
61
|
+
void agentLoopPrewarmPromise.catch(() => {
|
|
62
|
+
// Reservation-time prewarm retries through the same shared loader.
|
|
63
|
+
agentLoopPrewarmPromise = null;
|
|
64
|
+
});
|
|
65
|
+
return agentLoopPrewarmPromise;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Memory-runtime attach (PG proxy + embed warmup) measured ~6.5s cold and is
|
|
69
|
+
// the largest first-turn blocker: loadCoreMemoryContext races it with a 2s
|
|
70
|
+
// cap, but a cold attach stalls the event loop hard enough that even that
|
|
71
|
+
// timer fires seconds late (probe: core-memory settle at ~6.0s). Warm the
|
|
72
|
+
// EXACT shared proxy instance the session create path resolves —
|
|
73
|
+
// getStandaloneMemoryRuntime caches by entry+dataDir, so the first turn finds
|
|
74
|
+
// it initialized. Honors the same MIXDOG_BOOT_CORE_MEMORY opt-out.
|
|
75
|
+
let memoryRuntimePrewarmPromise = null;
|
|
76
|
+
export function preloadMemoryRuntime() {
|
|
77
|
+
const bootFlag = String(process.env.MIXDOG_BOOT_CORE_MEMORY ?? '')
|
|
78
|
+
.trim()
|
|
79
|
+
.toLowerCase();
|
|
80
|
+
if (bootFlag === '0' || bootFlag === 'false' || bootFlag === 'no' || bootFlag === 'off') {
|
|
81
|
+
return Promise.resolve();
|
|
82
|
+
}
|
|
83
|
+
memoryRuntimePrewarmPromise ??= (async () => {
|
|
84
|
+
const [{ getStandaloneMemoryRuntime }, { getPluginData }, { fileURLToPath }] = await Promise.all([
|
|
85
|
+
import('../session-runtime/services/memory-runtime-proxy.mjs'),
|
|
86
|
+
import('../runtime/agent/orchestrator/config.mjs'),
|
|
87
|
+
import('node:url'),
|
|
88
|
+
]);
|
|
89
|
+
const runtime = getStandaloneMemoryRuntime({
|
|
90
|
+
entry: fileURLToPath(new URL('../runtime/memory/index.mjs', import.meta.url)),
|
|
91
|
+
dataDir: process.env.MIXDOG_DATA_DIR || getPluginData?.() || undefined,
|
|
92
|
+
});
|
|
93
|
+
await runtime.init();
|
|
94
|
+
})();
|
|
95
|
+
void memoryRuntimePrewarmPromise.catch(() => {
|
|
96
|
+
// Opportunistic: loadCoreMemoryContext retries through the same proxy.
|
|
97
|
+
memoryRuntimePrewarmPromise = null;
|
|
98
|
+
});
|
|
99
|
+
return memoryRuntimePrewarmPromise;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Windows keychain reads go through a DPAPI PowerShell host whose cold start
|
|
103
|
+
// dominates a packaged boot (measured ~1.5s). The runtime batches every secret
|
|
104
|
+
// into one call, but it only starts that batch when the runtime is created —
|
|
105
|
+
// late enough that the first context switch waits on it. Hosts can start the
|
|
106
|
+
// same batch while their window is still coming up.
|
|
107
|
+
let keychainPrewarmPromise = null;
|
|
108
|
+
export function preloadKeychainSecrets() {
|
|
109
|
+
keychainPrewarmPromise ??= (async () => {
|
|
110
|
+
try {
|
|
111
|
+
const { createRequire } = await import('node:module');
|
|
112
|
+
const require = createRequire(import.meta.url);
|
|
113
|
+
const keychain = require('../lib/keychain-cjs.cjs');
|
|
114
|
+
await keychain.prewarmSecrets?.();
|
|
115
|
+
} catch {
|
|
116
|
+
// Prewarm is opportunistic; the runtime still warms on its own path.
|
|
117
|
+
}
|
|
118
|
+
})();
|
|
119
|
+
return keychainPrewarmPromise;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const TOOL_APPROVAL_TIMEOUT_MS = (() => {
|
|
123
|
+
const value = Number(process.env.MIXDOG_TOOL_APPROVAL_TIMEOUT_MS);
|
|
124
|
+
return Number.isFinite(value) && value > 0 ? Math.max(1000, Math.round(value)) : 120_000;
|
|
125
|
+
})();
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
cleanupStaleTranscriptSpillDirs,
|
|
129
|
+
createTranscriptSpillBuffer,
|
|
130
|
+
refillTranscriptViewOverlap,
|
|
131
|
+
replaceSessionItemsState,
|
|
132
|
+
createSessionItemMutators,
|
|
133
|
+
TRANSCRIPT_LIVE_ITEM_CAP,
|
|
134
|
+
TRANSCRIPT_SPILL_CHUNK_ITEMS,
|
|
135
|
+
} from '../tui/session/transcript-spill.mjs';
|
|
136
|
+
export { parseBackgroundTaskEnvelope } from '../tui/session/agent-envelope.mjs';
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The 2s runtime pulse picks up runtime-derived state that changes without a
|
|
140
|
+
* notification: the route (model/effort/context window/cwd/Goal), the context
|
|
141
|
+
* gauge stats, and agent status. It publishes ONLY when one of those values
|
|
142
|
+
* actually differs from the draft or the published snapshot. Re-publishing an
|
|
143
|
+
* unchanged session minted a fresh snapshot every 2s, which the session
|
|
144
|
+
* service projected as a new revision and which kept its idle-projection
|
|
145
|
+
* release from ever firing for a watched session.
|
|
146
|
+
*/
|
|
147
|
+
export function createRuntimePulse({
|
|
148
|
+
flags,
|
|
149
|
+
liveShareMirroring,
|
|
150
|
+
getState,
|
|
151
|
+
getPublishedState,
|
|
152
|
+
set,
|
|
153
|
+
emit,
|
|
154
|
+
routeState,
|
|
155
|
+
agentStatusState,
|
|
156
|
+
syncContextStats,
|
|
157
|
+
}) {
|
|
158
|
+
const publishChanged = (patch) => {
|
|
159
|
+
const draftState = getState();
|
|
160
|
+
const published = getPublishedState();
|
|
161
|
+
const changed = {};
|
|
162
|
+
let dirty = false;
|
|
163
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
164
|
+
// syncContextStats stages its fields straight into the draft without
|
|
165
|
+
// scheduling a frame, so a value equal to the draft may still be
|
|
166
|
+
// unpublished.
|
|
167
|
+
if (isDeepStrictEqual(draftState[key], value) && isDeepStrictEqual(published[key], value)) continue;
|
|
168
|
+
changed[key] = value;
|
|
169
|
+
dirty = true;
|
|
170
|
+
}
|
|
171
|
+
if (!dirty) return false;
|
|
172
|
+
if (!set(changed)) emit();
|
|
173
|
+
return true;
|
|
174
|
+
};
|
|
175
|
+
return () => {
|
|
176
|
+
if (flags.disposed) return;
|
|
177
|
+
if (flags.pendingSessionReset) return;
|
|
178
|
+
// Attached viewer with a live pipe: the owner's frames are authoritative
|
|
179
|
+
// for stats/agent/tool state. Recomputing them locally here would stomp
|
|
180
|
+
// the mirror with this process's empty registries every 2s.
|
|
181
|
+
if (liveShareMirroring()) {
|
|
182
|
+
publishChanged({ ...routeState() });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
syncContextStats({ allowEstimated: true });
|
|
186
|
+
publishChanged({
|
|
187
|
+
...routeState(),
|
|
188
|
+
stats: { ...getState().stats },
|
|
189
|
+
...agentStatusState(),
|
|
190
|
+
});
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function createLocalSessionRuntime({
|
|
195
|
+
provider: providerName,
|
|
196
|
+
model,
|
|
197
|
+
effort,
|
|
198
|
+
fast,
|
|
199
|
+
modelParameters,
|
|
200
|
+
toolMode = 'full',
|
|
201
|
+
remote = false,
|
|
202
|
+
cwd,
|
|
203
|
+
desktopSession,
|
|
204
|
+
sessionProfile,
|
|
205
|
+
executeAgentControl,
|
|
206
|
+
} = {}) {
|
|
207
|
+
const startedAt = performance.now();
|
|
208
|
+
bootProfile('session:create:start', { provider: providerName, model, toolMode, remote });
|
|
209
|
+
// Silence provider/session diagnostics so they cannot tear through the
|
|
210
|
+
// alternate-screen React/ink render.
|
|
211
|
+
process.env.MIXDOG_QUIET_PROVIDER_LOG = '1';
|
|
212
|
+
process.env.MIXDOG_QUIET_SESSION_LOG = '1';
|
|
213
|
+
process.env.MIXDOG_QUIET_MCP_LOG = '1';
|
|
214
|
+
process.env.MIXDOG_QUIET_MEMORY_LOG = '1';
|
|
215
|
+
process.env.MIXDOG_PATCH_NATIVE_PREWARM ??= '0';
|
|
216
|
+
|
|
217
|
+
const importStartedAt = performance.now();
|
|
218
|
+
const { createMixdogSessionRuntime } = await importSessionRuntimeModule();
|
|
219
|
+
bootProfile('session-runtime:imported', { ms: (performance.now() - importStartedAt).toFixed(1) });
|
|
220
|
+
const runtime = await createMixdogSessionRuntime({
|
|
221
|
+
provider: providerName,
|
|
222
|
+
model,
|
|
223
|
+
effort,
|
|
224
|
+
fast,
|
|
225
|
+
modelParameters,
|
|
226
|
+
toolMode,
|
|
227
|
+
remote,
|
|
228
|
+
...(cwd ? { cwd } : {}),
|
|
229
|
+
...(desktopSession ? { desktopSession } : {}),
|
|
230
|
+
...(sessionProfile && typeof sessionProfile === 'object' ? { sessionProfile } : {}),
|
|
231
|
+
...(typeof executeAgentControl === 'function' ? { executeAgentControl } : {}),
|
|
232
|
+
});
|
|
233
|
+
bootProfile('session:create:runtime-ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
234
|
+
const runtimeCwd = runtime.cwd || process.cwd();
|
|
235
|
+
const stateStartedAt = performance.now();
|
|
236
|
+
const flags = {
|
|
237
|
+
disposed: false,
|
|
238
|
+
draining: false,
|
|
239
|
+
autoClearRunning: false,
|
|
240
|
+
pendingSessionReset: false,
|
|
241
|
+
lastUserActivityAt: Date.now(),
|
|
242
|
+
leadTurnEpoch: 0,
|
|
243
|
+
activePromptRestore: null,
|
|
244
|
+
pushingFromDeferredEntry: false,
|
|
245
|
+
flushDeferredBeforeImmediatePush: null,
|
|
246
|
+
pendingTranscriptMeta: null,
|
|
247
|
+
};
|
|
248
|
+
const lifecycle = {
|
|
249
|
+
runtimePulseTimer: null,
|
|
250
|
+
unsubscribeRuntimeNotifications: null,
|
|
251
|
+
unsubscribeAgentStatus: null,
|
|
252
|
+
unsubscribeRemoteState: null,
|
|
253
|
+
};
|
|
254
|
+
const pending = [];
|
|
255
|
+
const pendingNotificationKeys = new Set();
|
|
256
|
+
const displayedExecutionNotificationKeys = new Set();
|
|
257
|
+
const bag = {};
|
|
258
|
+
// The one synchronous draft every collaborator reads through; the draft
|
|
259
|
+
// store below publishes immutable snapshots from it once per frame.
|
|
260
|
+
const draft = { state: null };
|
|
261
|
+
// Bound once the goal-continuation controller exists: route publications
|
|
262
|
+
// read the Goal through its archive mask, never the raw record.
|
|
263
|
+
let visibleGoalStatus = null;
|
|
264
|
+
// Route/context/agent-status derivations live in src/tui/session/context-state.mjs.
|
|
265
|
+
// getState()/updateState() are late-bound to the draft so they serve both
|
|
266
|
+
// the pre-publication seed below and every later pulse.
|
|
267
|
+
const { autoClearState, agentStatusState, baseRouteState, routeState, syncContextStats } = createContextState({
|
|
268
|
+
runtime,
|
|
269
|
+
getState: () => draft.state,
|
|
270
|
+
updateState: (patch) => {
|
|
271
|
+
draft.state = { ...draft.state, ...patch };
|
|
272
|
+
},
|
|
273
|
+
getPendingSessionReset: () => flags.pendingSessionReset,
|
|
274
|
+
getVisibleGoal: () => (visibleGoalStatus ? visibleGoalStatus() : runtime.goalStatus?.() || null),
|
|
275
|
+
});
|
|
276
|
+
draft.state = createInitialSessionState({ runtime, runtimeCwd, baseRouteState });
|
|
277
|
+
bootProfile('session:route-state-ready', { ms: (performance.now() - stateStartedAt).toFixed(1) });
|
|
278
|
+
bootProfile('session:state-ready', { ms: (performance.now() - stateStartedAt).toFixed(1) });
|
|
279
|
+
const contextStartedAt = performance.now();
|
|
280
|
+
syncContextStats({ allowEstimated: true });
|
|
281
|
+
bootProfile('session:context-ready', { ms: (performance.now() - contextStartedAt).toFixed(1) });
|
|
282
|
+
const listeners = new Set();
|
|
283
|
+
const store = createSessionDraftStore({
|
|
284
|
+
draft,
|
|
285
|
+
listeners,
|
|
286
|
+
isDisposed: () => flags.disposed,
|
|
287
|
+
onBusyReleased: () => {
|
|
288
|
+
void bag.drain?.();
|
|
289
|
+
bag.scheduleGoalContinuation?.();
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
const { getState, getPublishedState, set, flushEmit, flushEmitImmediate } = store;
|
|
293
|
+
|
|
294
|
+
const itemIndexById = new Map();
|
|
295
|
+
const transcriptSpill = createTranscriptSpillBuffer();
|
|
296
|
+
const { markToolCallActive, markToolCallDone, clearActiveToolSummary, resetActiveToolCalls } =
|
|
297
|
+
createActiveToolTracker({ getState, set });
|
|
298
|
+
const {
|
|
299
|
+
restoreOlderTranscript,
|
|
300
|
+
restoreNewerTranscript,
|
|
301
|
+
replaceItems,
|
|
302
|
+
pushItem,
|
|
303
|
+
appendItems,
|
|
304
|
+
patchItem,
|
|
305
|
+
updateStreamingTail,
|
|
306
|
+
settleStreamingTail,
|
|
307
|
+
clearStreamingTail,
|
|
308
|
+
} = createTranscriptStore({
|
|
309
|
+
draft,
|
|
310
|
+
store,
|
|
311
|
+
flags,
|
|
312
|
+
transcriptSpill,
|
|
313
|
+
itemIndexById,
|
|
314
|
+
onBulkReplace: resetActiveToolCalls,
|
|
315
|
+
});
|
|
316
|
+
const transcriptRouteMetadata = (at = Date.now()) => createTranscriptRouteMetadata(runtime.session, routeState(), at);
|
|
317
|
+
const { upsertSyntheticToolItem, pushUserOrSyntheticItem, pushAsyncAgentResponse } = createTranscriptIntake({
|
|
318
|
+
getState,
|
|
319
|
+
flags,
|
|
320
|
+
pushItem,
|
|
321
|
+
patchItem,
|
|
322
|
+
transcriptRouteMetadata,
|
|
323
|
+
});
|
|
324
|
+
const { pushToast, pushNotice, removeNotice, setProgressHint, clearToastTimers } = createNoticeSurface({
|
|
325
|
+
getState,
|
|
326
|
+
set,
|
|
327
|
+
isDisposed: () => flags.disposed,
|
|
328
|
+
pushItem,
|
|
329
|
+
replaceItems,
|
|
330
|
+
});
|
|
331
|
+
const { presentNextToolApproval, finishToolApproval, denyAllToolApprovals, requestToolApproval } = createToolApproval(
|
|
332
|
+
{
|
|
333
|
+
getState,
|
|
334
|
+
set,
|
|
335
|
+
nextId,
|
|
336
|
+
getDisposed: () => flags.disposed,
|
|
337
|
+
timeoutMs: TOOL_APPROVAL_TIMEOUT_MS,
|
|
338
|
+
}
|
|
339
|
+
);
|
|
340
|
+
lifecycle.runtimePulseTimer = setInterval(
|
|
341
|
+
createRuntimePulse({
|
|
342
|
+
flags,
|
|
343
|
+
liveShareMirroring: () => bag.liveShareMirroring?.() === true,
|
|
344
|
+
getState,
|
|
345
|
+
getPublishedState,
|
|
346
|
+
set,
|
|
347
|
+
emit: store.emit,
|
|
348
|
+
routeState,
|
|
349
|
+
agentStatusState,
|
|
350
|
+
syncContextStats,
|
|
351
|
+
}),
|
|
352
|
+
2000
|
|
353
|
+
);
|
|
354
|
+
lifecycle.runtimePulseTimer.unref?.();
|
|
355
|
+
|
|
356
|
+
const {
|
|
357
|
+
kickExecutionPendingResume,
|
|
358
|
+
flushDeferredExecutionPendingResumeKick,
|
|
359
|
+
scheduleExecutionPendingResumeKick,
|
|
360
|
+
discardExecutionPendingResume,
|
|
361
|
+
updateAgentJobCard,
|
|
362
|
+
buildAgentJobCardPatch,
|
|
363
|
+
subscribeRuntimeNotifications,
|
|
364
|
+
clearExecutionDedupState,
|
|
365
|
+
} = createAgentJobFeed({
|
|
366
|
+
runtime,
|
|
367
|
+
getState,
|
|
368
|
+
set,
|
|
369
|
+
nextId,
|
|
370
|
+
getDisposed: () => flags.disposed,
|
|
371
|
+
patchItem,
|
|
372
|
+
enqueue: (...args) => bag.enqueue(...args),
|
|
373
|
+
drain: (...args) => bag.drain(...args),
|
|
374
|
+
pushUserOrSyntheticItem,
|
|
375
|
+
pushAsyncAgentResponse,
|
|
376
|
+
makeQueueEntry: (...args) => bag.makeQueueEntry(...args),
|
|
377
|
+
getPending: () => pending,
|
|
378
|
+
agentStatusState,
|
|
379
|
+
displayedExecutionNotificationKeys,
|
|
380
|
+
pushNotice,
|
|
381
|
+
itemIndexById,
|
|
382
|
+
});
|
|
383
|
+
lifecycle.unsubscribeRuntimeNotifications = subscribeRuntimeNotifications();
|
|
384
|
+
if (typeof runtime.onAgentStatusChange === 'function') {
|
|
385
|
+
lifecycle.unsubscribeAgentStatus = runtime.onAgentStatusChange(() => {
|
|
386
|
+
if (flags.disposed || flags.pendingSessionReset || bag.liveShareMirroring?.()) return;
|
|
387
|
+
set({ ...agentStatusState({ force: true }) });
|
|
388
|
+
bag.scheduleGoalContinuation?.();
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const { patchToolCardResult, flushToolResults } = createToolCardResults({
|
|
393
|
+
getState,
|
|
394
|
+
set,
|
|
395
|
+
patchItem,
|
|
396
|
+
markToolCallDone,
|
|
397
|
+
updateAgentJobCard,
|
|
398
|
+
buildAgentJobCardPatch,
|
|
399
|
+
agentStatusState,
|
|
400
|
+
itemIndexById,
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
Object.assign(bag, {
|
|
404
|
+
runtime,
|
|
405
|
+
nextId,
|
|
406
|
+
tuiDebug,
|
|
407
|
+
flags,
|
|
408
|
+
lifecycle,
|
|
409
|
+
pending,
|
|
410
|
+
pendingNotificationKeys,
|
|
411
|
+
displayedExecutionNotificationKeys,
|
|
412
|
+
clearExecutionDedupState,
|
|
413
|
+
listeners,
|
|
414
|
+
itemIndexById,
|
|
415
|
+
getState,
|
|
416
|
+
getPublishedState,
|
|
417
|
+
set,
|
|
418
|
+
flushEmit,
|
|
419
|
+
flushEmitImmediate,
|
|
420
|
+
disposeEmit: store.disposeEmit,
|
|
421
|
+
pushItem,
|
|
422
|
+
appendItems,
|
|
423
|
+
patchItem,
|
|
424
|
+
replaceItems,
|
|
425
|
+
restoreOlderTranscript,
|
|
426
|
+
restoreNewerTranscript,
|
|
427
|
+
updateStreamingTail,
|
|
428
|
+
settleStreamingTail,
|
|
429
|
+
clearStreamingTail,
|
|
430
|
+
pushToast,
|
|
431
|
+
pushNotice,
|
|
432
|
+
removeNotice,
|
|
433
|
+
setProgressHint,
|
|
434
|
+
pushUserOrSyntheticItem,
|
|
435
|
+
pushAsyncAgentResponse,
|
|
436
|
+
upsertSyntheticToolItem,
|
|
437
|
+
markToolCallActive,
|
|
438
|
+
markToolCallDone,
|
|
439
|
+
clearActiveToolSummary,
|
|
440
|
+
clearToastTimers,
|
|
441
|
+
autoClearState,
|
|
442
|
+
agentStatusState,
|
|
443
|
+
baseRouteState,
|
|
444
|
+
routeState,
|
|
445
|
+
transcriptRouteMetadata,
|
|
446
|
+
syncContextStats,
|
|
447
|
+
disposeTranscriptSpill: () => transcriptSpill.dispose(),
|
|
448
|
+
snapshotTranscriptSpill: () => transcriptSpill.snapshot(),
|
|
449
|
+
restoreTranscriptSpill: (snapshot) => transcriptSpill.restoreSnapshot(snapshot),
|
|
450
|
+
releaseTranscriptSpill: (snapshot) => transcriptSpill.releaseSnapshot(snapshot),
|
|
451
|
+
presentNextToolApproval,
|
|
452
|
+
finishToolApproval,
|
|
453
|
+
denyAllToolApprovals,
|
|
454
|
+
requestToolApproval,
|
|
455
|
+
patchToolCardResult,
|
|
456
|
+
flushToolResults,
|
|
457
|
+
kickExecutionPendingResume,
|
|
458
|
+
flushDeferredExecutionPendingResumeKick,
|
|
459
|
+
scheduleExecutionPendingResumeKick,
|
|
460
|
+
discardExecutionPendingResume,
|
|
461
|
+
updateAgentJobCard,
|
|
462
|
+
subscribeRuntimeNotifications,
|
|
463
|
+
});
|
|
464
|
+
Object.assign(bag, createSessionFlow(bag));
|
|
465
|
+
Object.assign(
|
|
466
|
+
bag,
|
|
467
|
+
createGoalContinuation({
|
|
468
|
+
runtime,
|
|
469
|
+
flags,
|
|
470
|
+
getState,
|
|
471
|
+
set,
|
|
472
|
+
getPending: () => pending,
|
|
473
|
+
enqueue: (...args) => bag.enqueue(...args),
|
|
474
|
+
})
|
|
475
|
+
);
|
|
476
|
+
visibleGoalStatus = bag.visibleGoalStatus;
|
|
477
|
+
bag.runTurn = createRunTurn(bag);
|
|
478
|
+
bag.doctor = { runDoctorChecks, formatDoctorReport };
|
|
479
|
+
const api = createSessionApi(bag);
|
|
480
|
+
attachCrossSurfaceShare({ runtime, api, bag, flags, getState, getPublishedState, listeners, set });
|
|
481
|
+
void Promise.resolve(bag.restoreLeadSteeringFromDisk())
|
|
482
|
+
.catch(() => {})
|
|
483
|
+
.finally(() => {
|
|
484
|
+
bag.refreshGoalState?.();
|
|
485
|
+
bag.scheduleGoalContinuation?.();
|
|
486
|
+
});
|
|
487
|
+
return api;
|
|
488
|
+
}
|
|
@@ -15,7 +15,7 @@ export function createInlineSessionRuntimeHost({
|
|
|
15
15
|
cwd = process.cwd(),
|
|
16
16
|
log = () => {},
|
|
17
17
|
measureBootPhase = async (_phase, task) => await task(),
|
|
18
|
-
loadLocalModule = () => import('
|
|
18
|
+
loadLocalModule = () => import('./local-session-runtime.mjs'),
|
|
19
19
|
loadAgentGraph = () =>
|
|
20
20
|
Promise.all([
|
|
21
21
|
import('../runtime/agent/orchestrator/config.mjs'),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/tui/hooks/useSession.mjs — subscribe React to the session store.
|
|
3
3
|
*
|
|
4
|
-
* The store (session-
|
|
4
|
+
* The store (local-session-runtime.mjs) lives outside React and emits immutable snapshots;
|
|
5
5
|
* useSyncExternalStore re-renders the tree whenever a snapshot changes. This
|
|
6
6
|
* keeps the agentLoop fully decoupled from React's lifecycle.
|
|
7
7
|
*/
|
|
@@ -17,7 +17,7 @@ export function createSessionCommandsApi(bag) {
|
|
|
17
17
|
routeState,
|
|
18
18
|
syncContextStats,
|
|
19
19
|
replaceItems,
|
|
20
|
-
// Injected by the store composer (session-
|
|
20
|
+
// Injected by the store composer (local-session-runtime.mjs): the doctor checks
|
|
21
21
|
// live in the app layer, which the session layer must not import.
|
|
22
22
|
doctor,
|
|
23
23
|
} = bag;
|
|
@@ -68,6 +68,9 @@ export function createSessionIntegrationsApi(bag, { oauthFlows }) {
|
|
|
68
68
|
getQuotaHistory: async (options = {}) => {
|
|
69
69
|
return await runtime.getQuotaHistory?.(options);
|
|
70
70
|
},
|
|
71
|
+
getSessionUsage: async () => {
|
|
72
|
+
return await runtime.getSessionUsage?.();
|
|
73
|
+
},
|
|
71
74
|
consumeCodexRateLimitResetCredit: async (options = {}) => {
|
|
72
75
|
// Desktop capability parity: without this delegation the session runtime
|
|
73
76
|
// surface rejects the sidebar's reset-credit invoke as unsupported even
|
|
@@ -57,6 +57,7 @@ export function createAutoClearOps(bag, { reset, kickDrain }) {
|
|
|
57
57
|
// Only an actual compaction resets the visible transcript; an unchanged
|
|
58
58
|
// success keeps it. The model transcript always belongs to runtime.compact.
|
|
59
59
|
function applyAutoClearUi(result) {
|
|
60
|
+
if (result.changed === false) return false;
|
|
60
61
|
const compactChanged = result.changed !== false;
|
|
61
62
|
if (compactChanged) {
|
|
62
63
|
reset.resetStats();
|
|
@@ -68,6 +69,7 @@ export function createAutoClearOps(bag, { reset, kickDrain }) {
|
|
|
68
69
|
stats: { ...getState().stats },
|
|
69
70
|
});
|
|
70
71
|
pushItem({ kind: 'statusdone', id: nextId(), label: 'Auto-clear complete' });
|
|
72
|
+
return true;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
// Flush a deferred cleared-session UI sync once the active turn has settled.
|
|
@@ -77,8 +79,7 @@ export function createAutoClearOps(bag, { reset, kickDrain }) {
|
|
|
77
79
|
const { result } = flags.pendingClearedSessionUi;
|
|
78
80
|
flags.pendingClearedSessionUi = null;
|
|
79
81
|
flags.autoClearInFlight = false;
|
|
80
|
-
applyAutoClearUi(result);
|
|
81
|
-
pushNotice(LATE_COMPLETION_NOTICE, 'info');
|
|
82
|
+
if (applyAutoClearUi(result)) pushNotice(LATE_COMPLETION_NOTICE, 'info');
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
function onLateCompact(lateResult) {
|
|
@@ -86,8 +87,7 @@ export function createAutoClearOps(bag, { reset, kickDrain }) {
|
|
|
86
87
|
// Do not wipe items/queued or force busy=false mid-turn.
|
|
87
88
|
flags.pendingClearedSessionUi = { result: lateResult };
|
|
88
89
|
} else {
|
|
89
|
-
applyAutoClearUi(lateResult);
|
|
90
|
-
pushNotice(LATE_COMPLETION_NOTICE, 'info');
|
|
90
|
+
if (applyAutoClearUi(lateResult)) pushNotice(LATE_COMPLETION_NOTICE, 'info');
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -95,7 +95,7 @@ export function createAutoClearOps(bag, { reset, kickDrain }) {
|
|
|
95
95
|
// late completion can reset the UI. Never follow compact with clear:
|
|
96
96
|
// that would discard conversation/execution records the compactor kept.
|
|
97
97
|
async function compactWithinTimeout(compactTimeoutMs) {
|
|
98
|
-
const compactPromise = runtime.compact().then((result) => {
|
|
98
|
+
const compactPromise = runtime.compact({ requireReduction: true }).then((result) => {
|
|
99
99
|
if (!result) throw new Error('no active session');
|
|
100
100
|
if (result.error) throw new Error(result.error);
|
|
101
101
|
return result;
|
|
@@ -161,8 +161,7 @@ export function createAutoClearOps(bag, { reset, kickDrain }) {
|
|
|
161
161
|
// Without this, long idle clears can look like a frozen prompt followed by
|
|
162
162
|
// an already-complete status row.
|
|
163
163
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
164
|
-
applyAutoClearUi(await compactWithinTimeout(compactTimeoutMs));
|
|
165
|
-
return true;
|
|
164
|
+
return applyAutoClearUi(await compactWithinTimeout(compactTimeoutMs));
|
|
166
165
|
} catch (error) {
|
|
167
166
|
const message = presentErrorText(error, { surface: 'compact' });
|
|
168
167
|
pushItem({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/tui/session/tool-card-results.mjs — the tool-card result state machine
|
|
3
3
|
* (patchToolCardResult + flushToolResults) for the session runtime
|
|
4
|
-
* (session-
|
|
4
|
+
* (local-session-runtime.mjs), as a dependency-injection factory.
|
|
5
5
|
*
|
|
6
6
|
* These handlers own the per-turn accounting that reflects tool results into
|
|
7
7
|
* store items: aggregate cards, non-aggregate/legacy agent-job cards, grouped
|
package/src/tui/session/turn.mjs
CHANGED
|
@@ -408,6 +408,11 @@ function turnDoneItem({ turn, stream, cards, getState, nextId }) {
|
|
|
408
408
|
id: nextId(),
|
|
409
409
|
elapsedMs: Date.now() - turn.startedAt,
|
|
410
410
|
status: turnOutcome(turn.cancelled, turn.failed),
|
|
411
|
+
// Notifications resolve this exact terminal assistant item, never a
|
|
412
|
+
// session preview or a tool preamble from earlier in the turn.
|
|
413
|
+
...(turn.finishedNormally && stream.currentAssistantId != null
|
|
414
|
+
? { finalAssistantId: stream.currentAssistantId }
|
|
415
|
+
: {}),
|
|
411
416
|
outputTokens: finalOutputTokens(stream, getState().spinner?.outputTokens),
|
|
412
417
|
thinkingElapsedMs: stream.thinkingStartedAt ? stream.accumulatedThinkingMs : 0,
|
|
413
418
|
toolCount: cards.toolCards.length,
|