mixdog 0.9.87 → 0.9.89

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.
Files changed (167) hide show
  1. package/package.json +13 -7
  2. package/scripts/tool-failures.mjs +60 -24
  3. package/src/agents/heavy-worker/AGENT.md +3 -4
  4. package/src/agents/worker/AGENT.md +1 -2
  5. package/src/cli.mjs +8 -0
  6. package/src/defaults/cycle3-review-prompt.md +4 -4
  7. package/src/rules/agent/00-core.md +3 -0
  8. package/src/rules/agent/42-cycle3-agent.md +5 -6
  9. package/src/rules/lead/01-general.md +2 -0
  10. package/src/rules/shared/01-tool.md +29 -35
  11. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +3 -51
  12. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +59 -0
  13. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -2
  14. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +67 -0
  15. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +57 -4
  16. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +2 -3
  17. package/src/runtime/agent/orchestrator/config.mjs +29 -11
  18. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -1
  19. package/src/runtime/agent/orchestrator/internal-agents.mjs +1 -1
  20. package/src/runtime/agent/orchestrator/mcp/client.mjs +0 -24
  21. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +3 -3
  22. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +25 -17
  23. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +321 -22
  24. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +35 -22
  25. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +28 -23
  26. package/src/runtime/agent/orchestrator/providers/gemini.mjs +10 -2
  27. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +0 -1
  28. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +0 -1
  29. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +2 -5
  30. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +27 -0
  31. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +23 -0
  32. package/src/runtime/agent/orchestrator/providers/lib/stream-outcome.mjs +329 -0
  33. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +23 -1
  34. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +107 -20
  35. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +4 -3
  36. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +124 -12
  37. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +19 -2
  38. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +1 -1
  40. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +110 -75
  41. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +96 -113
  42. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +160 -11
  43. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +18 -1
  44. package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +147 -117
  45. package/src/runtime/agent/orchestrator/session/loop/stop-hooks.mjs +88 -0
  46. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +50 -8
  47. package/src/runtime/agent/orchestrator/session/loop/termination.mjs +25 -0
  48. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +1 -1
  49. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +47 -6
  50. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +485 -24
  51. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +100 -2
  52. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -0
  53. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +80 -14
  54. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +142 -2
  55. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +128 -32
  56. package/src/runtime/agent/orchestrator/session/manager.mjs +5 -1
  57. package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +77 -4
  58. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +70 -39
  59. package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +91 -0
  60. package/src/runtime/agent/orchestrator/session/store/listing.mjs +141 -64
  61. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +253 -0
  62. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +211 -28
  63. package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +313 -0
  64. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +622 -53
  65. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +43 -10
  66. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +53 -8
  67. package/src/runtime/agent/orchestrator/session/store/summary-rebuild-worker.mjs +20 -0
  68. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +113 -9
  69. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +2 -0
  70. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +476 -52
  71. package/src/runtime/agent/orchestrator/session/store.mjs +721 -196
  72. package/src/runtime/agent/orchestrator/session/token-native.mjs +2 -4
  73. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +19 -0
  74. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +4 -6
  75. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  76. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +146 -90
  77. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +6 -0
  78. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +93 -17
  79. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -0
  80. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +7 -7
  81. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +6 -1
  82. package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +17 -1
  83. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +85 -5
  84. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +52 -0
  85. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +54 -51
  86. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +14 -15
  87. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +132 -8
  88. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +86 -3
  89. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +6 -1
  90. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +210 -38
  91. package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +23 -1
  92. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +87 -19
  93. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
  94. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -8
  95. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +12 -2
  96. package/src/runtime/media/lanes.mjs +2 -2
  97. package/src/runtime/media/renditions.mjs +101 -17
  98. package/src/runtime/media/renditions.test.mjs +54 -0
  99. package/src/runtime/media/store.mjs +111 -43
  100. package/src/runtime/media/store.test.mjs +53 -11
  101. package/src/runtime/memory/index.mjs +26 -50
  102. package/src/runtime/memory/lib/core-memory-candidates.mjs +2 -7
  103. package/src/runtime/memory/lib/core-memory-store.mjs +2 -9
  104. package/src/runtime/memory/lib/cycle-scheduler.mjs +10 -0
  105. package/src/runtime/memory/lib/embedding-provider.mjs +3 -3
  106. package/src/runtime/memory/lib/embedding-worker.mjs +60 -15
  107. package/src/runtime/memory/lib/http-router.mjs +4 -0
  108. package/src/runtime/memory/lib/ko-morph.mjs +49 -6
  109. package/src/runtime/memory/lib/memory-action-handlers.mjs +21 -18
  110. package/src/runtime/memory/lib/memory-cycle2.mjs +3 -3
  111. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -3
  112. package/src/runtime/memory/lib/memory-recall-store.mjs +24 -0
  113. package/src/runtime/memory/lib/query-handlers.mjs +26 -28
  114. package/src/runtime/memory/tool-defs.mjs +3 -3
  115. package/src/runtime/shared/atomic-file.mjs +5 -1
  116. package/src/runtime/shared/child-guardian.mjs +73 -2
  117. package/src/runtime/shared/provider-api-key.mjs +22 -5
  118. package/src/runtime/shared/tool-execution-contract.mjs +53 -0
  119. package/src/session-runtime/config-helpers.mjs +6 -5
  120. package/src/session-runtime/lifecycle-api.mjs +16 -4
  121. package/src/session-runtime/media-api.mjs +31 -16
  122. package/src/session-runtime/prewarm.mjs +2 -24
  123. package/src/session-runtime/runtime-core.mjs +33 -15
  124. package/src/session-runtime/runtime-tunables.mjs +0 -3
  125. package/src/session-runtime/session-lifecycle.mjs +0 -6
  126. package/src/session-runtime/session-title.mjs +228 -0
  127. package/src/session-runtime/session-turn-api.mjs +11 -7
  128. package/src/session-runtime/workflow-agents-api.mjs +16 -0
  129. package/src/session-runtime/workflow.mjs +7 -5
  130. package/src/standalone/agent-tool/job-views.mjs +90 -36
  131. package/src/standalone/agent-tool/spawn-flow.mjs +25 -216
  132. package/src/standalone/agent-tool/worker-index.mjs +8 -0
  133. package/src/standalone/agent-tool/worker-rows.mjs +2 -1
  134. package/src/standalone/agent-tool.mjs +17 -17
  135. package/src/standalone/agent-watchdog-registry.mjs +19 -2
  136. package/src/standalone/channel-daemon.mjs +8 -0
  137. package/src/standalone/explore-tool.mjs +1 -1
  138. package/src/standalone/memory-runtime-proxy.mjs +4 -7
  139. package/src/standalone/projects.mjs +4 -1
  140. package/src/standalone/usage-dashboard.mjs +25 -3
  141. package/src/tui/App.jsx +5 -3
  142. package/src/tui/app/resume-picker.mjs +7 -2
  143. package/src/tui/app/route-pickers.mjs +11 -117
  144. package/src/tui/app/slash-dispatch.mjs +13 -3
  145. package/src/tui/app/transcript-row-estimate.mjs +1 -1
  146. package/src/tui/app/transcript-window.mjs +17 -93
  147. package/src/tui/app/use-transcript-scroll.mjs +32 -2
  148. package/src/tui/app/use-transcript-window.mjs +64 -114
  149. package/src/tui/components/Markdown.jsx +19 -2
  150. package/src/tui/components/Message.jsx +8 -3
  151. package/src/tui/components/ToolExecution.jsx +7 -3
  152. package/src/tui/dist/index.mjs +243 -223
  153. package/src/tui/engine/live-share.mjs +15 -5
  154. package/src/tui/engine/render-timing.mjs +2 -2
  155. package/src/tui/engine/session-api-ext.mjs +74 -4
  156. package/src/tui/engine/session-flow.mjs +3 -1
  157. package/src/tui/engine.mjs +2 -2
  158. package/src/tui/hooks/useSharedTick.mjs +0 -2
  159. package/src/tui/index.jsx +5 -5
  160. package/src/ui/statusline-agents.mjs +44 -11
  161. package/src/vendor/statusline/bin/statusline-route.mjs +26 -3
  162. package/src/workflows/default/WORKFLOW.md +3 -3
  163. package/src/workflows/solo/WORKFLOW.md +5 -3
  164. package/src/runtime/media/index.mjs +0 -18
  165. package/src/runtime/memory/lib/embedding-warmup.mjs +0 -68
  166. package/src/standalone/agent-shard/shard-child.mjs +0 -300
  167. package/src/standalone/agent-shard/shard-pool.mjs +0 -443
@@ -70,8 +70,18 @@ export function createLiveShare({
70
70
  onRemoteAbort,
71
71
  onOwnerClosed,
72
72
  viewerApply,
73
+ viewerRetryMinMs = LIVE_CONNECT_RETRY_MIN_MS,
74
+ viewerRetryMaxMs = LIVE_CONNECT_RETRY_MAX_MS,
73
75
  }) {
74
76
  let disposed = false;
77
+ const clientRetryMinimumMs = Math.max(
78
+ LIVE_CONNECT_RETRY_MIN_MS,
79
+ Number(viewerRetryMinMs) || LIVE_CONNECT_RETRY_MIN_MS,
80
+ );
81
+ const clientRetryMaximumMs = Math.max(
82
+ clientRetryMinimumMs,
83
+ Number(viewerRetryMaxMs) || LIVE_CONNECT_RETRY_MAX_MS,
84
+ );
75
85
  // ---- owner: pipe server + delta publisher ----
76
86
  let server = null;
77
87
  let serverId = '';
@@ -335,7 +345,7 @@ export function createLiveShare({
335
345
  let lastSyncRequestAt = 0;
336
346
  let clientRetryTimer = null;
337
347
  let clientRetryId = '';
338
- let clientRetryDelayMs = LIVE_CONNECT_RETRY_MIN_MS;
348
+ let clientRetryDelayMs = clientRetryMinimumMs;
339
349
  const viewerSyncWaiters = new Set();
340
350
 
341
351
  const settleViewerSync = (id, synced) => {
@@ -429,8 +439,8 @@ export function createLiveShare({
429
439
  const target = String(id || '');
430
440
  if (disposed || !target || client || clientRetryTimer || String(viewerSessionId() || '') !== target) return;
431
441
  const delay = clientRetryDelayMs;
432
- clientRetryDelayMs = Math.min(LIVE_CONNECT_RETRY_MAX_MS, Math.max(
433
- LIVE_CONNECT_RETRY_MIN_MS,
442
+ clientRetryDelayMs = Math.min(clientRetryMaximumMs, Math.max(
443
+ clientRetryMinimumMs,
434
444
  clientRetryDelayMs * 2,
435
445
  ));
436
446
  clientRetryId = target;
@@ -481,7 +491,7 @@ export function createLiveShare({
481
491
 
482
492
  const stopClient = () => {
483
493
  clearClientRetry();
484
- clientRetryDelayMs = LIVE_CONNECT_RETRY_MIN_MS;
494
+ clientRetryDelayMs = clientRetryMinimumMs;
485
495
  const closing = client;
486
496
  const closingId = clientId;
487
497
  const wasUp = clientUp;
@@ -520,7 +530,7 @@ export function createLiveShare({
520
530
  if (client !== socket) return;
521
531
  clientUp = true;
522
532
  clearClientRetry();
523
- clientRetryDelayMs = LIVE_CONNECT_RETRY_MIN_MS;
533
+ clientRetryDelayMs = clientRetryMinimumMs;
524
534
  });
525
535
  const down = (ownerClosed) => {
526
536
  const wasUp = clientUp && client === socket;
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Extracted from engine.mjs (no behavior change originally).
5
5
  *
6
- * Ink renders through a maxFps throttle (60fps in index.jsx, ≈16.7ms). A plain
6
+ * Ink renders through a maxFps throttle (120fps in index.jsx, ≈8.3ms). A plain
7
7
  * setImmediate only yields to the event loop; if Ink already painted within the
8
8
  * current throttle window, the next paint may still be queued and our following
9
9
  * transcript mutation can coalesce into the same visible frame. When we
@@ -20,7 +20,7 @@
20
20
  const RENDER_ACK_FALLBACK_MS = 32;
21
21
  const RENDER_ACK_HANG_GUARD_MS = 250;
22
22
  const RENDER_SETTLE_IDLE_MS = 64;
23
- export const TUI_RENDER_FPS = 60;
23
+ export const TUI_RENDER_FPS = 120;
24
24
  // Ink uses Math.ceil(1000 / maxFps); share that exact cadence with the store.
25
25
  export const TUI_FRAME_MS = Math.ceil(1000 / TUI_RENDER_FPS);
26
26
 
@@ -9,6 +9,7 @@ import { flushTuiSteeringPersist } from './tui-steering-persist.mjs';
9
9
  import { getVoiceStatus, toggleVoice } from '../lib/voice-setup.mjs';
10
10
  import { aggregateToolCategoryEntry, aggregateDoneCategories, classifyToolCategory, formatAggregateDetail, summarizeToolResult } from '../../runtime/shared/tool-surface.mjs';
11
11
  import { aggregateBucketForCategory, aggregateRawResult, failureDetailText, shellCommandExitCode } from './tool-result-status.mjs';
12
+ import { isInternalTranscriptDisplayText } from '../../runtime/shared/tool-execution-contract.mjs';
12
13
 
13
14
  export function restoredTranscriptMetadata(message) {
14
15
  const value = message?.meta?.transcript;
@@ -221,9 +222,7 @@ function restoredUserTranscriptItems(message, nextId) {
221
222
  const text = (typeof message?.content === 'string'
222
223
  ? message.content
223
224
  : toolResultText(message?.content)).trim();
224
- if (/^<(?:system-reminder|skill|memory-context|mcp-instructions|available-deferred-tools|event)\b/i.test(text)) {
225
- return [];
226
- }
225
+ if (isInternalTranscriptDisplayText(text)) return [];
227
226
  if (!text) return [];
228
227
  const synthetic = parseSyntheticAgentMessage(text);
229
228
  if (!synthetic) {
@@ -311,6 +310,55 @@ export function restoreTranscriptItems(messages, {
311
310
  return restored.length > limit ? restored.slice(-limit) : restored;
312
311
  }
313
312
 
313
+ export function sessionContextSnapshotProjection(session, contextStatus) {
314
+ if (!contextStatus) return {};
315
+ const usedSource = String(contextStatus.usedSource || '').toLowerCase();
316
+ const estimatedTokens = Math.max(0, Number(
317
+ contextStatus.currentEstimatedTokens
318
+ ?? contextStatus.usedTokens
319
+ ?? 0,
320
+ ));
321
+ const lastApiRequestTokens = Math.max(0, Number(
322
+ contextStatus.lastApiRequestTokens
323
+ ?? contextStatus.usage?.lastContextTokens
324
+ ?? 0,
325
+ ));
326
+ return {
327
+ stats: {
328
+ currentContextTokens: usedSource === 'last_api_request'
329
+ ? lastApiRequestTokens
330
+ : 0,
331
+ currentEstimatedContextTokens: estimatedTokens,
332
+ currentContextSource: usedSource === 'last_api_request'
333
+ ? 'last_api_request'
334
+ : estimatedTokens > 0 ? 'estimated' : null,
335
+ currentContextUpdatedAt: Date.now(),
336
+ },
337
+ contextWindow: Math.max(
338
+ 0,
339
+ Number(session?.contextWindow || contextStatus.effectiveContextWindow || 0),
340
+ ),
341
+ rawContextWindow: Math.max(
342
+ 0,
343
+ Number(session?.rawContextWindow || contextStatus.rawContextWindow || 0),
344
+ ),
345
+ effectiveContextWindowPercent: Number(
346
+ session?.effectiveContextWindowPercent
347
+ ?? contextStatus.effectiveContextWindowPercent
348
+ ?? 0,
349
+ ),
350
+ displayContextWindow: Math.max(0, Number(contextStatus.contextWindow || 0)),
351
+ compactBoundaryTokens: Math.max(
352
+ 0,
353
+ Number(contextStatus.compaction?.boundaryTokens || 0),
354
+ ),
355
+ autoCompactTokenLimit: Math.max(
356
+ 0,
357
+ Number(contextStatus.compaction?.triggerTokens || 0),
358
+ ),
359
+ };
360
+ }
361
+
314
362
  export function createEngineApiB(bag) {
315
363
  const {
316
364
  runtime, nextId, flags, lifecycle, listeners, getState, set, flushEmitImmediate, disposeEmit, replaceItems, pushNotice, removeNotice, setProgressHint, clearToastTimers, disposeTranscriptSpill, routeState, syncContextStats, finishToolApproval, denyAllToolApprovals, restoreLeadSteeringFromDisk, resetStats, clearUiActivityBeforeContextSync, resetTuiForPendingSessionReset, snapshotTuiBeforeSessionReset, restoreTuiAfterFailedSessionReset, commitTuiSessionReset, resetStatsAndSyncContext,
@@ -565,6 +613,15 @@ export function createEngineApiB(bag) {
565
613
  getUsageDashboard: async (options = {}) => {
566
614
  return await runtime.getUsageDashboard?.(options);
567
615
  },
616
+ consumeCodexRateLimitResetCredit: async (options = {}) => {
617
+ // Desktop capability parity: without this delegation the engine surface
618
+ // rejects the sidebar's reset-credit invoke as unsupported even though
619
+ // the session runtime implements it.
620
+ if (typeof runtime.consumeCodexRateLimitResetCredit !== 'function') {
621
+ throw new Error('Codex reset is unavailable');
622
+ }
623
+ return await runtime.consumeCodexRateLimitResetCredit(options);
624
+ },
568
625
  getTurnReviewDiff: async () => {
569
626
  return (await runtime.getTurnReviewDiff?.()) ?? { supported: false, files: [], patch: '' };
570
627
  },
@@ -693,6 +750,7 @@ export function createEngineApiB(bag) {
693
750
  listMediaLanes: () => runtime.listMediaLanes?.(),
694
751
  listMediaAssets: (options) => runtime.listMediaAssets?.(options),
695
752
  readMediaAsset: (id, options) => runtime.readMediaAsset?.(id, options),
753
+ cacheMediaThumbnail: (id, input) => runtime.cacheMediaThumbnail?.(id, input),
696
754
  resolveMediaFile: (id, options) => runtime.resolveMediaFile?.(id, options),
697
755
  getMediaJob: (id) => runtime.getMediaJob?.(id),
698
756
  listMediaJobs: () => runtime.listMediaJobs?.(),
@@ -702,7 +760,7 @@ export function createEngineApiB(bag) {
702
760
  cancelMediaJob: (id) => runtime.cancelMediaJob?.(id),
703
761
  deleteMediaAsset: (id) => runtime.deleteMediaAsset?.(id),
704
762
  openMediaAsset: (id) => runtime.openMediaAsset?.(id),
705
- openMediaFolder: () => runtime.openMediaFolder?.(),
763
+ openMediaFolder: (id) => runtime.openMediaFolder?.(id),
706
764
  setWebhookConfig: async (patch) => {
707
765
  const result = await runtime.setWebhookConfig(patch);
708
766
  pushNotice('webhook config updated', 'info');
@@ -800,6 +858,9 @@ export function createEngineApiB(bag) {
800
858
  listSessions: (options) => {
801
859
  return runtime.listSessions(options);
802
860
  },
861
+ renameSessionTitle: (id, title) => {
862
+ return runtime.renameSessionTitle?.(id, title) ?? false;
863
+ },
803
864
  // Desktop sidebar watcher hook: EngineHost fs.watches this directory so
804
865
  // heartbeat sidecar create/delete pushes instant working/dot updates.
805
866
  // Without it the watcher silently no-ops and the sidebar falls back to
@@ -898,6 +959,10 @@ export function createEngineApiB(bag) {
898
959
  sessionId: null,
899
960
  stats: { ...getState().stats },
900
961
  });
962
+ // Publish the blank session boundary before runtime session creation can
963
+ // block on disk/provider work. Otherwise the old transcript remains the
964
+ // last committed React snapshot until the async command completes.
965
+ flushEmitImmediate();
901
966
  try {
902
967
  await runtime.newSession();
903
968
  clearUiActivityBeforeContextSync();
@@ -912,6 +977,9 @@ export function createEngineApiB(bag) {
912
977
  } finally {
913
978
  flags.pendingSessionReset = false;
914
979
  set({ commandBusy: false });
980
+ // Match resume's atomic handoff: callers (and the forced terminal
981
+ // repaint triggered by /new) must observe the completed empty session.
982
+ flushEmitImmediate();
915
983
  }
916
984
  },
917
985
  // Desktop split panes: read-only transcript + route peek of an idle
@@ -922,6 +990,7 @@ export function createEngineApiB(bag) {
922
990
  if (!session) return null;
923
991
  const sessionId = String(session.id || id);
924
992
  const limit = Number(options.transcriptItemLimit);
993
+ const contextStatus = runtime.contextStatusForSession?.(session) || null;
925
994
  return {
926
995
  sessionId,
927
996
  items: restoreTranscriptItems(session.messages, {
@@ -935,6 +1004,7 @@ export function createEngineApiB(bag) {
935
1004
  cwd: session.cwd || '',
936
1005
  desktopSession: session.desktopSession || null,
937
1006
  workflow: session.workflow || null,
1007
+ ...sessionContextSnapshotProjection(session, contextStatus),
938
1008
  };
939
1009
  },
940
1010
  resume: async (id, options = {}) => {
@@ -357,7 +357,9 @@ export function createSessionFlow(bag) {
357
357
  if (isQueuedEntryVisible(entry)) restored.push(entry);
358
358
  }
359
359
  if (restored.length > 0) set({ queued: [...getState().queued, ...restored] });
360
- void drain();
360
+ // Recovery must not create a user turn by itself. An idle reconnect leaves
361
+ // the restored rows visible/editable in the queue; a real active-turn
362
+ // boundary or later submit will invoke the normal drain path.
361
363
  }
362
364
 
363
365
  async function autoClearBeforeSubmit() {
@@ -23,8 +23,8 @@ import {
23
23
  summarizeToolResult,
24
24
  } from '../runtime/shared/tool-surface.mjs';
25
25
  import {
26
+ isInternalTranscriptDisplayText,
26
27
  isModelVisibleToolCompletionWrapper,
27
- isLikelyToolCompletionWrapper,
28
28
  } from '../runtime/shared/tool-execution-contract.mjs';
29
29
  import { isLateToolAnnouncement } from '../session-runtime/session-text.mjs';
30
30
  import { presentErrorText } from '../runtime/shared/err-text.mjs';
@@ -624,7 +624,7 @@ export async function createEngineSession({
624
624
  // the text is known to have been synthesized by the runtime, not typed
625
625
  // by the user. Direct user submissions always go through the strict
626
626
  // detector only, same as before this change.
627
- if (origin === 'injected' && isLikelyToolCompletionWrapper(text)) return;
627
+ if (origin === 'injected' && isInternalTranscriptDisplayText(text)) return;
628
628
  if (isModelVisibleToolCompletionWrapper(text)) return;
629
629
  // Late-MCP deferred-tool announcement (model-visible <system-reminder>):
630
630
  // keep it in model context, but render NOTHING user-facing — not even the
@@ -99,5 +99,3 @@ export function useSharedTick(intervalMs, isActive = true, onTick = null) {
99
99
  };
100
100
  }, [intervalMs, isActive]);
101
101
  }
102
-
103
- export default useSharedTick;
package/src/tui/index.jsx CHANGED
@@ -621,11 +621,11 @@ export async function runTui({ provider, model, toolMode, remote, forceOnboardin
621
621
  if (mouseTracking && typeof instance.getSelectionRows === 'function') {
622
622
  store.getRenderSelectionRows = instance.getSelectionRows;
623
623
  }
624
- // [mixdog] One-shot full clear+repaint. The app's mouse handler fires it on
625
- // every button press under Windows Terminal to dismiss WT's persistent
626
- // NATIVE (shift+drag) selection overlay, which survives incremental
627
- // repaints and would otherwise sit on top of the app-drawn selection.
628
- if (mouseTracking && typeof instance.forceFullRepaint === 'function') {
624
+ // [mixdog] One-shot full clear+repaint. Mouse selection uses it to dismiss
625
+ // WT's native highlight; session replacement also uses it to erase physical
626
+ // rows from a taller outgoing transcript, so expose it independently of
627
+ // whether mouse tracking is enabled.
628
+ if (typeof instance.forceFullRepaint === 'function') {
629
629
  store.forceRenderRepaint = instance.forceFullRepaint;
630
630
  }
631
631
  await waitUntilExit();
@@ -12,13 +12,16 @@ import { classifyToolCategory } from '../runtime/shared/tool-surface.mjs';
12
12
  import { num, GRN, R, B } from './statusline-format.mjs';
13
13
 
14
14
  const DEFAULT_HIDDEN_STATUSLINE_AGENTS = Object.freeze(['explorer', 'cycle1-agent', 'cycle2-agent', 'cycle3-agent', 'scheduler-task', 'webhook-handler']);
15
+ const TERMINAL_AGENT_STATUS = /idle|done|complete|success|closed|error|fail|cancel|killed|timeout/i;
16
+ const ACTIVE_AGENT_STATUS = /^(?:connecting|requesting|streaming|tool[-_\s]?running|running|queued|pending|starting)$/i;
17
+ const QUEUED_AGENT_STATUS = /^(?:queued|pending|starting)$/i;
15
18
  let _hiddenStatuslineAgents = null;
16
19
 
17
20
  function normalizeAgentWorkerForStatusline(worker = {}) {
18
21
  const tag = String(worker.tag || worker.agent || worker.name || '').trim();
19
22
  if (!tag) return null;
20
- const statusText = String(worker.stage || worker.status || '').toLowerCase();
21
- const status = isTerminalBridgeStatus(statusText) ? 'idle' : 'running';
23
+ const statuses = agentStatusValues(worker);
24
+ const status = isActiveAgentEntry(statuses) ? 'running' : 'idle';
22
25
  return {
23
26
  tag,
24
27
  status,
@@ -32,14 +35,15 @@ function normalizeAgentWorkerForStatusline(worker = {}) {
32
35
  }
33
36
 
34
37
  function normalizeAgentJobForStatusline(job = {}) {
35
- const statusText = String(job.status || job.stage || '').toLowerCase();
36
- if (!statusText) return null;
38
+ const statuses = agentStatusValues(job);
39
+ if (!statuses.length) return null;
40
+ const statusText = statuses[0].toLowerCase();
37
41
  const taskId = String(job.task_id || job.taskId || '').trim();
38
42
  const tag = String(job.tag || job.agent || job.type || taskId || '').trim();
39
43
  if (!tag && !taskId) return null;
40
44
  const startedAtMs = timeMs(job.startedAt);
41
45
  const finishedAtMs = timeMs(job.finishedAt);
42
- if (isTerminalBridgeStatus(statusText) && finishedAtMs > 0) {
46
+ if (isTerminalAgentEntry(statuses) && finishedAtMs > 0) {
43
47
  return {
44
48
  tag,
45
49
  taskId,
@@ -54,11 +58,11 @@ function normalizeAgentJobForStatusline(job = {}) {
54
58
  model: job.model || null,
55
59
  };
56
60
  }
57
- if (!/running/.test(statusText)) return null;
61
+ if (!isActiveAgentEntry(statuses)) return null;
58
62
  return {
59
63
  tag,
60
64
  taskId,
61
- status: 'running',
65
+ status: isQueuedAgentEntry(statuses) ? 'queued' : 'running',
62
66
  startedAtMs,
63
67
  agent: job.agent || null,
64
68
  stage: job.stage || job.workerStatus || job.status || null,
@@ -70,10 +74,13 @@ function normalizeAgentJobForStatusline(job = {}) {
70
74
 
71
75
  export function agentStatuslinePayload(agentWorkers = [], agentJobs = []) {
72
76
  const byTag = new Map();
77
+ const workerByTag = new Map();
73
78
  const finishedJobs = [];
74
79
  for (const worker of Array.isArray(agentWorkers) ? agentWorkers : []) {
75
80
  const row = normalizeAgentWorkerForStatusline(worker);
76
- if (row) byTag.set(row.tag, row);
81
+ if (!row) continue;
82
+ workerByTag.set(row.tag, row);
83
+ byTag.set(row.tag, row);
77
84
  }
78
85
  for (const job of Array.isArray(agentJobs) ? agentJobs : []) {
79
86
  const row = normalizeAgentJobForStatusline(job);
@@ -82,8 +89,13 @@ export function agentStatuslinePayload(agentWorkers = [], agentJobs = []) {
82
89
  finishedJobs.push(row);
83
90
  continue;
84
91
  }
85
- const prev = byTag.get(row.tag);
86
- byTag.set(row.tag, { ...(prev || {}), ...row, status: 'running' });
92
+ const worker = workerByTag.get(row.tag);
93
+ // Once a worker exists it is authoritative over its spawn job. Active
94
+ // workers already represent the work, while terminal/inactive workers
95
+ // suppress a lagging `running` job. Only a genuinely queued reuse may
96
+ // replace an inactive worker until the new worker starts.
97
+ if (worker && (worker.status === 'running' || row.status !== 'queued')) continue;
98
+ byTag.set(row.tag, row);
87
99
  }
88
100
  const workers = [...byTag.values()];
89
101
  return {
@@ -179,7 +191,28 @@ export function activeHiddenAgentWorkers({ sessionId = '', clientHostPid = 0 } =
179
191
  }
180
192
 
181
193
  function isTerminalBridgeStatus(statusText) {
182
- return /idle|done|complete|success|closed|error|fail|cancel|killed|timeout/.test(String(statusText || '').toLowerCase());
194
+ return TERMINAL_AGENT_STATUS.test(String(statusText || ''));
195
+ }
196
+
197
+ function agentStatusValues(value = {}) {
198
+ return [value.stage, value.status]
199
+ .map((status) => String(status || '').trim())
200
+ .filter((status, index, values) => Boolean(status) && values.indexOf(status) === index);
201
+ }
202
+
203
+ function isTerminalAgentEntry(statuses = []) {
204
+ return statuses.some((status) => isTerminalBridgeStatus(status));
205
+ }
206
+
207
+ function isActiveAgentEntry(statuses = []) {
208
+ return statuses.length > 0
209
+ && !isTerminalAgentEntry(statuses)
210
+ && statuses.some((status) => ACTIVE_AGENT_STATUS.test(status));
211
+ }
212
+
213
+ function isQueuedAgentEntry(statuses = []) {
214
+ return isActiveAgentEntry(statuses)
215
+ && statuses.every((status) => QUEUED_AGENT_STATUS.test(status));
183
216
  }
184
217
 
185
218
  // Agent-side web search activity for the L2 "Web Searching" segment: any live
@@ -723,6 +723,20 @@ export function formatGatewayLimitSegments(status, fmt) {
723
723
  const maxWindows = COLS >= 120 ? 3 : COLS >= 80 ? 2 : 1;
724
724
  const routeSpend = num(status.routeSpend?.costUsd, null);
725
725
  const routeSpendLabel = cleanString(status.routeSpend?.label) || (COLS >= 120 ? 'SESS' : 'S');
726
+ // Fixed columns for the numeric fields of the quota segments: the percentage
727
+ // and the `↻ <reset>` time are right-aligned inside their own segment so the
728
+ // separators stop drifting horizontally as the values change width
729
+ // (`5%` → `35%` → `100%`, `7:59` → `07:59`). Disabled below 80 columns so the
730
+ // narrow/truncating layout keeps its current byte-for-byte behavior.
731
+ const alignColumns = COLS >= 80;
732
+ const PCT_COLUMN_WIDTH = 4; // '100%'
733
+ const RESET_COLUMN_WIDTH = 5; // 'HH:MM'
734
+ const visibleLength = (text) => String(text ?? '').replace(/\x1b\[[0-9;]*m/g, '').length;
735
+ const padLeftVisible = (text, width) => {
736
+ const value = String(text ?? '');
737
+ const pad = Math.max(0, Number(width) - visibleLength(value));
738
+ return pad > 0 ? `${' '.repeat(pad)}${value}` : value;
739
+ };
726
740
  const windowResetText = (window) => {
727
741
  if (isLocalEstimateWindow(window)) return '';
728
742
  const at = num(window?.resetAt, null);
@@ -733,9 +747,14 @@ export function formatGatewayLimitSegments(status, fmt) {
733
747
  const pad = (n) => String(n).padStart(2, '0');
734
748
  return `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
735
749
  };
750
+ const shownWindows = windows.slice(0, maxWindows);
751
+ // Each segment right-aligns its own time to the HH:MM column width; the rarer
752
+ // `MM-DD HH:MM` form (reset more than 24h out) simply overflows its own
753
+ // column instead of padding the neighbouring segments with dead space.
754
+ const resetColumnWidth = alignColumns ? RESET_COLUMN_WIDTH : 0;
736
755
  const withReset = (segment, window) => {
737
756
  const reset = windowResetText(window);
738
- return reset ? `${segment} ${D}↻ ${reset}${R}` : segment;
757
+ return reset ? `${segment} ${D}↻ ${padLeftVisible(reset, resetColumnWidth)}${R}` : segment;
739
758
  };
740
759
  const addRouteSpend = () => {
741
760
  if (routeSpend !== null && routeSpend > 0) {
@@ -757,7 +776,7 @@ export function formatGatewayLimitSegments(status, fmt) {
757
776
  if (addApiBalance()) return segments;
758
777
  if (segments.length) return segments;
759
778
  }
760
- for (const w of windows.slice(0, maxWindows)) {
779
+ for (const w of shownWindows) {
761
780
  const label = String(w?.label || 'USE').toUpperCase();
762
781
  const pct = num(w?.usedPct, null);
763
782
  const remaining = isOpenAiOAuth ? null : num(w?.remainingUsd, null);
@@ -781,7 +800,11 @@ export function formatGatewayLimitSegments(status, fmt) {
781
800
  } else if (usedCredits !== null && limitCredits !== null) {
782
801
  segments.push(withReset(`${D}${label}${R} ${estimated ? 'est ' : ''}${compactNumber(usedCredits)}/${compactNumber(limitCredits)}`, w));
783
802
  } else if (pct !== null) {
784
- segments.push(withReset(`${D}${label}${R} ${estimated ? 'est ' : ''}${colourPct(Math.round(pct))}`, w));
803
+ const pctText = colourPct(Math.round(pct));
804
+ segments.push(withReset(
805
+ `${D}${label}${R} ${estimated ? 'est ' : ''}${alignColumns ? padLeftVisible(pctText, PCT_COLUMN_WIDTH) : pctText}`,
806
+ w,
807
+ ));
785
808
  }
786
809
  }
787
810
  addRouteSpend();
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  id: default
3
- name: Default
4
- description: "Fan-out parallel delegation."
3
+ name: Cowork
4
+ description: "Parallel delegation."
5
5
  agents: worker, heavy-worker, reviewer, debugger, maintainer
6
6
  ---
7
7
 
8
- # Default
8
+ # Cowork
9
9
 
10
10
  Lead is the orchestrator: consult the user and build the plan together.
11
11
  Before the user explicitly approves the latest plan, work is read-only
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  id: solo
3
3
  name: Solo
4
- description: "Lead handles everything directly."
4
+ description: "Lead works alone."
5
5
  agents:
6
6
  ---
7
7
 
@@ -15,9 +15,11 @@ changed request resets planning; a scope change requires fresh approval.
15
15
  On approval, Lead executes all work itself — never spawn, send, or delegate
16
16
  to agents. Complete in-scope fixes without reapproval.
17
17
 
18
- Verify directly: check and fix until clean, or report the blocker.
18
+ Verification is single-pass: run the one check that most directly proves the
19
+ deliverable exactly once; a pass is final. Iterate only on a failing check,
20
+ re-running just that check after each fix, or report the blocker.
19
21
 
20
- Report the verified result against the approved plan. Build, deploy, commit,
22
+ Report the result and its proving check against the approved plan. Build, deploy, commit,
21
23
  and push happen only on an explicit user request.
22
24
 
23
25
  On direction change, pause and re-consult the user.
@@ -1,18 +0,0 @@
1
- /**
2
- * Media studio surface (image / video generation).
3
- *
4
- * Single entry point for the session runtime: lane catalog, job control, and
5
- * the on-disk asset store. Adapters are imported lazily by the job runner so a
6
- * boot that never generates never pays for them.
7
- */
8
- export { MEDIA_KINDS, listMediaLanes, getMediaLane } from './lanes.mjs';
9
- export { startMediaJob, getMediaJob, listMediaJobs, cancelMediaJob } from './jobs.mjs';
10
- export {
11
- listMediaAssets,
12
- readMediaAsset,
13
- resolveMediaFile,
14
- deleteMediaAsset,
15
- mediaAssetPath,
16
- openMediaAsset,
17
- openMediaFolder,
18
- } from './store.mjs';
@@ -1,68 +0,0 @@
1
- // Boot-edge embedding warmup queue, extracted from index.mjs (pass 2).
2
- //
3
- // ONNX session creation on the embedding worker thread is CPU-heavy, so it must
4
- // not overlap the worker's own init (DB open, schema, cycle wiring). The warmup
5
- // is queued during _initStore and fired at the _initRuntime completion edge, so
6
- // it starts the instant boot's CPU-heavy work is done — no magic-number delay.
7
- //
8
- // This is a small pure factory: index.mjs owns the DATA_DIR-derived meta path
9
- // and the embedding-provider imports, injecting them here as callbacks. No
10
- // import-time side effects.
11
- export function createEmbeddingWarmup({
12
- canStart,
13
- warmup,
14
- getDims,
15
- persistMeta,
16
- log = () => {},
17
- }) {
18
- let _pending = null
19
- // Fired latch: an init() retry after the early (PG-wait-edge) fire must not
20
- // re-schedule and run the warmup twice (double ONNX session create +
21
- // double embedding-meta persist). reset() clears it for true re-inits.
22
- let _fired = false
23
-
24
- // Queue the warmup; fireDeferred() runs it once boot completes. Re-queuing
25
- // overwrites any prior pending closure (matches the original single-slot
26
- // module var). MIXDOG_EMBED_WARMUP=0 (checked via canStart) disables it.
27
- function schedule(metaPath, metaKey) {
28
- if (_fired) return
29
- if (!canStart()) {
30
- // Silent skip here previously made "warmup never ran" boots
31
- // indistinguishable from failed warmups. One line, boot-edge only.
32
- log('[memory-service] embedding warmup skipped (disabled by env/secondary mode) — recall stays lexical until first embed\n')
33
- return
34
- }
35
- _pending = () => {
36
- const startedAt = Date.now()
37
- log('[memory-service] embedding warmup start\n')
38
- warmup()
39
- .then(() => {
40
- log(`[memory-service] embedding warmup ready in ${Date.now() - startedAt}ms\n`)
41
- const measured = Number(getDims())
42
- try {
43
- persistMeta(metaPath, { ...metaKey, dims: measured })
44
- } catch (e) {
45
- log(`[memory-service] could not persist embedding-meta: ${e?.message || e}\n`)
46
- }
47
- })
48
- .catch(err => {
49
- log(`[memory-service] background warmup failed: ${err?.message || err}\n`)
50
- })
51
- }
52
- }
53
-
54
- function fireDeferred() {
55
- const fire = _pending
56
- if (!fire) return
57
- _pending = null
58
- _fired = true
59
- fire()
60
- }
61
-
62
- function reset() {
63
- _pending = null
64
- _fired = false
65
- }
66
-
67
- return { schedule, fireDeferred, reset }
68
- }