maka-agent 0.2.0-dev.23.20260906 → 0.2.0-dev.25.20260908
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 +38 -19
- package/README.zh-CN.md +34 -19
- package/dist/acp/maka-acp-agent.js +2 -1
- package/dist/acp/session-configuration.js +143 -0
- package/dist/acp/session-registry.js +90 -4
- package/dist/cli-core.js +2 -0
- package/dist/pi-transcript-tools.js +6 -0
- package/dist/pi-transcript.js +5 -12
- package/dist/pi-tui-layout.js +56 -10
- package/dist/pi-tui-pickers.js +102 -1
- package/dist/pi-tui-runner.js +230 -46
- package/dist/pi-tui-todo.js +221 -0
- package/dist/pi-tui-transcript-viewer.js +134 -7
- package/dist/runtime-host-cli-context.js +224 -78
- package/dist/runtime-host-cli-installation.js +8 -2
- package/dist/runtime-host-cli.js +31 -1
- package/dist/runtime-host-handoff-surface.js +106 -0
- package/dist/runtime-host-installed-update-bootstrap.js +13 -0
- package/dist/runtime-host-installed-update-coordinator.js +33 -0
- package/dist/runtime-host-lifecycle-transaction.js +9 -1
- package/dist/runtime-host-local-handoff.js +48 -5
- package/dist/runtime-host-local-managed-activation.js +84 -0
- package/dist/runtime-host-local-source-retirement.js +18 -2
- package/dist/runtime-host-managed-lifecycle-manager.js +1 -0
- package/dist/runtime-host-operator-retirement.js +50 -0
- package/dist/runtime-host-service-manager.js +3 -3
- package/dist/runtime-host-session-channel.js +13 -0
- package/dist/runtime-host-session-driver.js +46 -26
- package/dist/runtime-host-session-update.js +62 -0
- package/dist/runtime-host-tui-command.js +38 -73
- package/dist/runtime-host-tui-context.js +1 -0
- package/dist/runtime-host-tui-owner.js +226 -0
- package/dist/tui-copy-catalog.js +219 -6
- package/dist/tui-host-owner-copy.js +24 -0
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/agent-run.js +1 -0
- package/node_modules/@maka/core/dist/attachments.js +3 -0
- package/node_modules/@maka/core/dist/canonical-json.js +27 -0
- package/node_modules/@maka/core/dist/canonical-runtime-event.js +3 -1
- package/node_modules/@maka/core/dist/capabilities.js +35 -0
- package/node_modules/@maka/core/dist/computer-use.js +0 -1
- package/node_modules/@maka/core/dist/display-redaction.js +18 -0
- package/node_modules/@maka/core/dist/llm-connections.js +13 -0
- package/node_modules/@maka/core/dist/model-call-attempt.js +2 -0
- package/node_modules/@maka/core/dist/model-failure.js +40 -0
- package/node_modules/@maka/core/dist/model-metadata.generated.js +4 -4
- package/node_modules/@maka/core/dist/model-metadata.js +7 -3
- package/node_modules/@maka/core/dist/model-web-search.js +5 -4
- package/node_modules/@maka/core/dist/provider-registry.js +45 -4
- package/node_modules/@maka/core/dist/record-schema.js +9 -2
- package/node_modules/@maka/core/dist/redaction.js +35 -0
- package/node_modules/@maka/core/dist/run-composition.js +77 -2
- package/node_modules/@maka/core/dist/runtime-boundary.js +31 -11
- package/node_modules/@maka/core/dist/runtime-event.js +37 -6
- package/node_modules/@maka/core/dist/runtime-handoff.js +98 -0
- package/node_modules/@maka/core/dist/runtime-invocation.js +1 -1
- package/node_modules/@maka/core/dist/runtime-logical-execution.js +112 -0
- package/node_modules/@maka/core/dist/session.js +84 -56
- package/node_modules/@maka/core/dist/slash-command-catalog.js +3 -0
- package/node_modules/@maka/core/dist/tool-result-archive-evidence.js +20 -0
- package/node_modules/@maka/core/dist/tool-result-record-schema.js +2 -1
- package/node_modules/@maka/core/dist/usage-record-schema.js +26 -35
- package/node_modules/@maka/core/package.json +5 -0
- package/node_modules/@maka/eval/dist/experiment-directory.js +4 -13
- package/node_modules/@maka/mcp/dist/index.js +131 -1
- package/node_modules/@maka/runtime/dist/agent-run-inspect.js +0 -13
- package/node_modules/@maka/runtime/dist/agent-run.js +351 -105
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +29 -13
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +109 -34
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +184 -183
- package/node_modules/@maka/runtime/dist/archive-read-tool.js +2 -2
- package/node_modules/@maka/runtime/dist/context-budget-helpers.js +0 -16
- package/node_modules/@maka/runtime/dist/context-budget.js +14 -9
- package/node_modules/@maka/runtime/dist/conversation-copy.js +158 -15
- package/node_modules/@maka/runtime/dist/github-copilot-oauth-enrollment.js +3 -3
- package/node_modules/@maka/runtime/dist/goal-continuation.js +36 -4
- package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +18 -9
- package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +30 -22
- package/node_modules/@maka/runtime/dist/history-compaction.js +29 -4
- package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +174 -0
- package/node_modules/@maka/runtime/dist/mcp-tools.js +7 -2
- package/node_modules/@maka/runtime/dist/message-authority.js +9 -0
- package/node_modules/@maka/runtime/dist/model-adapter.js +19 -98
- package/node_modules/@maka/runtime/dist/model-factory.js +19 -81
- package/node_modules/@maka/runtime/dist/model-fetcher.js +49 -2
- package/node_modules/@maka/runtime/dist/model-history.js +0 -112
- package/node_modules/@maka/runtime/dist/model-runtime.js +85 -111
- package/node_modules/@maka/runtime/dist/native-web-search-tool.js +2 -1
- package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +2 -2
- package/node_modules/@maka/runtime/dist/opencode-session-header.js +4 -2
- package/node_modules/@maka/runtime/dist/plugin-runtime.js +20 -10
- package/node_modules/@maka/runtime/dist/plugin-tool-service.js +205 -0
- package/node_modules/@maka/runtime/dist/provider-error-classification.js +84 -123
- package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +76 -109
- package/node_modules/@maka/runtime/dist/request-shape.js +23 -0
- package/node_modules/@maka/runtime/dist/run-handoff-gate.js +97 -0
- package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +83 -55
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +121 -159
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +157 -112
- package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +160 -79
- package/node_modules/@maka/runtime/dist/runtime-read-model.js +30 -89
- package/node_modules/@maka/runtime/dist/runtime-resume.js +80 -4
- package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +5 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +171 -288
- package/node_modules/@maka/runtime/dist/session-projection-helpers.js +0 -27
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +21 -0
- package/node_modules/@maka/runtime/dist/stream-graph-supervisor-tools.js +0 -5
- package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +3 -4
- package/node_modules/@maka/runtime/dist/test-connection.js +16 -17
- package/node_modules/@maka/runtime/dist/tool-activation-identity.js +41 -0
- package/node_modules/@maka/runtime/dist/tool-availability.js +11 -1
- package/node_modules/@maka/runtime/dist/tool-result-archive-encoding.js +38 -0
- package/node_modules/@maka/runtime/dist/tool-result-archive-resource.js +63 -1
- package/node_modules/@maka/runtime/dist/tool-result-archive-transition.js +42 -12
- package/node_modules/@maka/runtime/dist/tool-result-archive.js +27 -4
- package/node_modules/@maka/runtime/dist/tool-runtime.js +3 -61
- package/node_modules/@maka/runtime/package.json +2 -0
- package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +35 -0
- package/node_modules/@maka/runtime-host/dist/client/connect-or-spawn.js +9 -1
- package/node_modules/@maka/runtime-host/dist/client/connection.js +3 -0
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +179 -0
- package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +275 -0
- package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +26 -5
- package/node_modules/@maka/runtime-host/dist/client/index.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/launcher.js +3 -0
- package/node_modules/@maka/runtime-host/dist/client/remote-compatibility-error.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +46 -5
- package/node_modules/@maka/runtime-host/dist/control/endpoint.js +1 -3
- package/node_modules/@maka/runtime-host/dist/node-error.js +22 -0
- package/node_modules/@maka/runtime-host/dist/operator/index.js +2 -1
- package/node_modules/@maka/runtime-host/dist/operator/managed-deployment.js +1 -3
- package/node_modules/@maka/runtime-host/dist/operator/retirement-cancellation.js +20 -0
- package/node_modules/@maka/runtime-host/dist/peer-mesh/limits.js +0 -1
- package/node_modules/@maka/runtime-host/dist/peer-mesh/model.js +1 -1
- package/node_modules/@maka/runtime-host/dist/peer-reachability/publisher.js +1 -3
- package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +21 -12
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +29 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/plan.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +27 -5
- package/node_modules/@maka/runtime-host/dist/protocol/project-catalog.js +2 -2
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +40 -20
- package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +0 -5
- package/node_modules/@maka/runtime-host/dist/protocol/session-turns.js +29 -58
- package/node_modules/@maka/runtime-host/dist/protocol/turn.js +11 -1
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +31 -2
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +1 -0
- package/node_modules/@maka/runtime-host/dist/server/agent-graph-coordinator.js +0 -3
- package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +19 -4
- package/node_modules/@maka/runtime-host/dist/server/connection-session.js +7 -6
- package/node_modules/@maka/runtime-host/dist/server/execution-artifacts.js +32 -32
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +144 -33
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +5 -5
- package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +45 -16
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +40 -1
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +96 -17
- package/node_modules/@maka/runtime-host/dist/server/host-residency-registry.js +13 -7
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-admission-registry.js +3 -1
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +124 -62
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +47 -22
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +76 -6
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +325 -73
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +37 -2
- package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +45 -3
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +50 -7
- package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +87 -25
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +5 -2
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +12 -55
- package/node_modules/@maka/runtime-host/dist/server/session-sidecar-purge.js +1 -16
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +0 -1
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +460 -70
- package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/storage-maintenance.js +109 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +20 -2
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +13 -4
- package/node_modules/@maka/runtime-host/dist/server/workhub-message-attachments.js +46 -0
- package/node_modules/@maka/storage/dist/artifact-store.js +21 -10
- package/node_modules/@maka/storage/dist/artifact-stores.js +1 -1
- package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +0 -5
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +284 -198
- package/node_modules/@maka/storage/dist/context-offload-snapshot.js +325 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +9 -7
- package/node_modules/@maka/storage/dist/file-session-repository.js +1012 -0
- package/node_modules/@maka/storage/dist/git-exec.js +61 -0
- package/node_modules/@maka/storage/dist/git-worktree-child-executor.js +8 -35
- package/node_modules/@maka/storage/dist/goal-authority.js +2 -2
- package/node_modules/@maka/storage/dist/interaction-store.js +2 -2
- package/node_modules/@maka/storage/dist/opencode-session-adapter.js +0 -4
- package/node_modules/@maka/storage/dist/operational-state-backup.js +9 -5
- package/node_modules/@maka/storage/dist/project-catalog.js +2 -23
- package/node_modules/@maka/storage/dist/runtime-event-invariants.js +2 -2
- package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
- package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +8 -3
- package/node_modules/@maka/storage/dist/runtime-transcript-query.js +308 -0
- package/node_modules/@maka/storage/dist/scheduled-task-store.js +249 -283
- package/node_modules/@maka/storage/dist/session-bundle-manifest.js +5 -17
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +26 -5
- package/node_modules/@maka/storage/dist/session-copy-cleanup.js +2 -1
- package/node_modules/@maka/storage/dist/session-message-projection.js +15 -0
- package/node_modules/@maka/storage/dist/session-repository.js +767 -0
- package/node_modules/@maka/storage/dist/session-store.js +25 -99
- package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +10 -3
- package/node_modules/@maka/storage/dist/sqlite-context-offload-store.js +24 -8
- package/node_modules/@maka/storage/dist/sqlite-core-execution-schema.js +7 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +44 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +130 -27
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +109 -892
- package/node_modules/@maka/storage/dist/storage-id.js +28 -0
- package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +138 -0
- package/node_modules/@maka/storage/dist/workspace-identity.js +2 -15
- package/node_modules/@maka/storage/dist/workspace-version-authority-internal.js +1 -8
- package/node_modules/@maka/storage/package.json +2 -0
- package/package.json +1 -1
package/dist/pi-tui-runner.js
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { randomUUID } from 'node:crypto';
|
|
20
|
-
import { basename } from 'node:path';
|
|
21
20
|
import { Key, ProcessTerminal, SelectList, TuiMainScreen, isKeyRelease, isKeyRepeat, matchesKey, } from '@earendil-works/pi-tui';
|
|
21
|
+
import { CurrentTodoStore, TodoOverlay, renderTodoIndicator } from './pi-tui-todo.js';
|
|
22
22
|
import { isThinkingLevel } from '@maka/core/model-thinking';
|
|
23
23
|
import { deriveConnectionSlug, } from '@maka/core/llm-connections';
|
|
24
24
|
import { projectRevisionLinkedSessionTree } from '@maka/core/session-revisions';
|
|
@@ -41,6 +41,7 @@ import { MakaAutocompleteAboveEditorComponent } from './tui-autocomplete-layout.
|
|
|
41
41
|
import { TranscriptViewerOverlay } from './pi-tui-transcript-viewer.js';
|
|
42
42
|
import { copyToClipboard } from './tui-clipboard.js';
|
|
43
43
|
import { getTuiCopyCopy, lastAssistantText, serializeTranscriptText } from './tui-copy-command.js';
|
|
44
|
+
import { getTuiHostOwnerCopy } from './tui-host-owner-copy.js';
|
|
44
45
|
import { McpManagementOverlay } from './pi-tui-mcp-status.js';
|
|
45
46
|
import { createShellRunElapsedTicker } from './shell-run-elapsed-ticker.js';
|
|
46
47
|
import { createShellRunHydrationController } from './shell-run-hydration.js';
|
|
@@ -48,7 +49,7 @@ import { CTX_REFRESH_DEBOUNCE_MS, createCtxRefresher, scheduleCtxRefreshTimeout,
|
|
|
48
49
|
import { sessionStatusBadge } from './tui-session-status.js';
|
|
49
50
|
import { AttentionController, DISABLE_FOCUS_REPORTING, ENABLE_FOCUS_REPORTING, FOCUS_IN_SEQUENCE, FOCUS_OUT_SEQUENCE, } from './tui-attention.js';
|
|
50
51
|
import { MakaActivityStripComponent, MakaPendingQueueComponent, MakaPiLayoutComponent, MakaStatusLineComponent, MakaTranscriptComponent, } from './pi-tui-layout.js';
|
|
51
|
-
import { MakaAutocompleteProvider, DirectoryPickerOverlay, ModelSearchOverlay, OnboardingWizard, PickerOverlay, UserQuestionOverlay, modelChoiceConnectionLabels, getTuiPickerCopy, modelPickerItems, onboardingFailureMessage, permissionModePickerItems, skillPickerItems, thinkingLevelPickerItems, } from './pi-tui-pickers.js';
|
|
52
|
+
import { MakaAutocompleteProvider, DirectoryPickerOverlay, ModelSearchOverlay, SessionSearchOverlay, OnboardingWizard, PickerOverlay, UserQuestionOverlay, modelChoiceConnectionLabels, getTuiPickerCopy, modelPickerItems, onboardingFailureMessage, permissionModePickerItems, skillPickerItems, thinkingLevelPickerItems, } from './pi-tui-pickers.js';
|
|
52
53
|
import { formatMakaResumeCommand } from './cli-invocation.js';
|
|
53
54
|
import { goalAttachedNoticeText, goalPausedNoticeText, goalStatusLabel, goalSummaryLines, isLiveGoalStatus, } from './pi-goal.js';
|
|
54
55
|
import { getTuiPrimaryGuidance } from './tui-primary-guidance.js';
|
|
@@ -211,6 +212,20 @@ export async function runMakaPiTui(input) {
|
|
|
211
212
|
// True while the /session picker is open mid-turn: Escape must close the
|
|
212
213
|
// overlay, not arm the double-Escape interrupt for the running Turn (#3380).
|
|
213
214
|
let sessionPickerOverlayOpen = false;
|
|
215
|
+
let transcriptOverlay;
|
|
216
|
+
let todoOverlay;
|
|
217
|
+
const closeTodoOverlay = () => {
|
|
218
|
+
todoOverlay?.hide();
|
|
219
|
+
todoOverlay = undefined;
|
|
220
|
+
};
|
|
221
|
+
let transcriptViewer;
|
|
222
|
+
let transcriptViewerSessionId;
|
|
223
|
+
const resetTranscriptViewer = () => {
|
|
224
|
+
transcriptOverlay?.hide();
|
|
225
|
+
transcriptOverlay = undefined;
|
|
226
|
+
transcriptViewer = undefined;
|
|
227
|
+
transcriptViewerSessionId = undefined;
|
|
228
|
+
};
|
|
214
229
|
let lastTurnEscapeAt = 0;
|
|
215
230
|
let lastIdleEscapeAt = 0;
|
|
216
231
|
let lastIdleCtrlCAt = 0;
|
|
@@ -317,6 +332,34 @@ export async function runMakaPiTui(input) {
|
|
|
317
332
|
const activityStrip = new MakaActivityStripComponent(metadata);
|
|
318
333
|
const pendingQueue = new MakaPendingQueueComponent(state, locale);
|
|
319
334
|
const statusLine = new MakaStatusLineComponent(metadata);
|
|
335
|
+
const currentTodo = new CurrentTodoStore({
|
|
336
|
+
read: async (sessionId) => {
|
|
337
|
+
if (!input.driver.queryTodo)
|
|
338
|
+
throw new Error('Todo query unavailable');
|
|
339
|
+
return input.driver.queryTodo(sessionId);
|
|
340
|
+
},
|
|
341
|
+
}, () => {
|
|
342
|
+
if (!closed)
|
|
343
|
+
tui.requestRender();
|
|
344
|
+
});
|
|
345
|
+
const syncTodoSession = () => {
|
|
346
|
+
currentTodo.setSession(input.driver.getSessionId() ?? undefined);
|
|
347
|
+
};
|
|
348
|
+
const unsubscribeTodoChanges = input.driver.subscribeTodoChanges?.((sessionId) => {
|
|
349
|
+
if (sessionId !== input.driver.getSessionId())
|
|
350
|
+
return;
|
|
351
|
+
syncTodoSession();
|
|
352
|
+
void currentTodo.refresh();
|
|
353
|
+
});
|
|
354
|
+
const todoIndicator = {
|
|
355
|
+
invalidate() { },
|
|
356
|
+
render(width) {
|
|
357
|
+
if (!input.driver.queryTodo)
|
|
358
|
+
return [];
|
|
359
|
+
const line = renderTodoIndicator(currentTodo.getState(), { locale, width });
|
|
360
|
+
return line === undefined ? [] : [line];
|
|
361
|
+
},
|
|
362
|
+
};
|
|
320
363
|
// Use the vendor editor's full 20-item autocomplete capacity. Larger command
|
|
321
364
|
// catalogs remain scrollable and keep an exact position/total counter.
|
|
322
365
|
const editor = new MakaSkillHighlightEditor(tui, editorTheme(), {
|
|
@@ -325,7 +368,7 @@ export async function runMakaPiTui(input) {
|
|
|
325
368
|
});
|
|
326
369
|
let refreshEditorCwd;
|
|
327
370
|
const editorSurface = new MakaAutocompleteAboveEditorComponent(editor);
|
|
328
|
-
const layout = new MakaPiLayoutComponent(state, transcript, activityStrip, pendingQueue, editorSurface, statusLine, terminal);
|
|
371
|
+
const layout = new MakaPiLayoutComponent(state, transcript, activityStrip, pendingQueue, editorSurface, statusLine, terminal, todoIndicator);
|
|
329
372
|
const attention = new AttentionController(terminal, {
|
|
330
373
|
baseTitle: input.title,
|
|
331
374
|
...(input.attentionLongTurnThresholdMs !== undefined
|
|
@@ -607,6 +650,9 @@ export async function runMakaPiTui(input) {
|
|
|
607
650
|
unsubscribeModelCatalogChanges?.();
|
|
608
651
|
unsubscribeSessionTitleChanges();
|
|
609
652
|
unsubscribeGoalChanges?.();
|
|
653
|
+
unsubscribeTodoChanges?.();
|
|
654
|
+
currentTodo.dispose();
|
|
655
|
+
closeTodoOverlay();
|
|
610
656
|
void sideConversation?.stopParentObserver?.();
|
|
611
657
|
unsubscribeStartedTurns();
|
|
612
658
|
unsubscribeResolvedInteractions();
|
|
@@ -632,10 +678,8 @@ export async function runMakaPiTui(input) {
|
|
|
632
678
|
rejectClosed(error);
|
|
633
679
|
else
|
|
634
680
|
resolveClosed();
|
|
635
|
-
//
|
|
636
|
-
//
|
|
637
|
-
if (!interruptRequested)
|
|
638
|
-
void input.driver.stop().catch(() => { });
|
|
681
|
+
// Closing a viewport is not a Turn or Host stop. Explicit interrupt keys
|
|
682
|
+
// retain their own stop path; an attached Host continues independently.
|
|
639
683
|
};
|
|
640
684
|
const handleProcessExit = (exitCode, error) => {
|
|
641
685
|
process.exitCode = exitCode;
|
|
@@ -1210,6 +1254,7 @@ export async function runMakaPiTui(input) {
|
|
|
1210
1254
|
});
|
|
1211
1255
|
}
|
|
1212
1256
|
const adoptSessionMetadata = (summary, announceIdentity = true) => {
|
|
1257
|
+
syncTodoSession();
|
|
1213
1258
|
cwd = summary.cwd ?? cwd;
|
|
1214
1259
|
setSessionTitle(summary.name);
|
|
1215
1260
|
model = summary.model;
|
|
@@ -1298,6 +1343,8 @@ export async function runMakaPiTui(input) {
|
|
|
1298
1343
|
// `messages`. Shared by switchSession and rewindToTurn so both land the same
|
|
1299
1344
|
// runner state (model/connection/thinking/transcript/scroll).
|
|
1300
1345
|
const applySwitchResult = async ({ summary, messages, activeTurn, }) => {
|
|
1346
|
+
resetTranscriptViewer();
|
|
1347
|
+
closeTodoOverlay();
|
|
1301
1348
|
adoptSessionMetadata(summary, false);
|
|
1302
1349
|
replaceTranscript(messages);
|
|
1303
1350
|
syncInteractionOverlays();
|
|
@@ -1857,6 +1904,11 @@ export async function runMakaPiTui(input) {
|
|
|
1857
1904
|
formOverlay = showBottomPicker(formOverlayComponent);
|
|
1858
1905
|
};
|
|
1859
1906
|
const syncInteractionOverlays = () => {
|
|
1907
|
+
if (state.pendingInteraction) {
|
|
1908
|
+
closeTodoOverlay();
|
|
1909
|
+
transcriptOverlay?.hide();
|
|
1910
|
+
transcriptOverlay = undefined;
|
|
1911
|
+
}
|
|
1860
1912
|
syncUserQuestionOverlay();
|
|
1861
1913
|
syncFormOverlay();
|
|
1862
1914
|
};
|
|
@@ -2306,45 +2358,58 @@ export async function runMakaPiTui(input) {
|
|
|
2306
2358
|
foreignByValue.set(`foreign:${summary.source}:${summary.id}`, summary);
|
|
2307
2359
|
}
|
|
2308
2360
|
}
|
|
2361
|
+
let overlay;
|
|
2362
|
+
let sessionSearch;
|
|
2309
2363
|
const renderScope = () => {
|
|
2310
2364
|
const visibleSessions = sessionListScope === 'current'
|
|
2311
2365
|
? projectedSessions.filter(({ session }) => session.cwd === cwd)
|
|
2312
2366
|
: projectedSessions;
|
|
2313
|
-
const
|
|
2367
|
+
const choices = visibleSessions.map(({ session, depth }) => {
|
|
2314
2368
|
const state = availability.get(session.id);
|
|
2315
2369
|
const statusBadge = sessionStatusBadge(session, locale);
|
|
2316
2370
|
const statusDetail = statusBadge ? ` · ${statusBadge}` : '';
|
|
2317
|
-
const location = sessionListScope === 'all' && session.cwd ? ` ${
|
|
2371
|
+
const location = sessionListScope === 'all' && session.cwd ? ` ${session.cwd}` : '';
|
|
2318
2372
|
const childDetail = session.subagentRuntime
|
|
2319
2373
|
? ` subagent:${session.subagentRuntime.profile}`
|
|
2320
2374
|
: '';
|
|
2321
|
-
|
|
2375
|
+
const item = {
|
|
2322
2376
|
value: session.id,
|
|
2323
2377
|
label: `${depth > 0 ? `${' '.repeat(depth - 1)}↳ ` : ''}${session.name || session.id}`,
|
|
2324
2378
|
description: state?.available === false
|
|
2325
2379
|
? `${shortSessionId(session.id)}${statusDetail} ${state.reason}`
|
|
2326
2380
|
: `${shortSessionId(session.id)}${statusDetail}${location}${childDetail} ${session.llmConnectionSlug} ${session.model}`,
|
|
2327
2381
|
};
|
|
2382
|
+
return {
|
|
2383
|
+
item,
|
|
2384
|
+
searchText: [
|
|
2385
|
+
session.name,
|
|
2386
|
+
session.id,
|
|
2387
|
+
session.cwd,
|
|
2388
|
+
session.model,
|
|
2389
|
+
session.llmConnectionSlug,
|
|
2390
|
+
]
|
|
2391
|
+
.filter(Boolean)
|
|
2392
|
+
.join(' ')
|
|
2393
|
+
.toLocaleLowerCase(),
|
|
2394
|
+
};
|
|
2328
2395
|
});
|
|
2329
2396
|
// Foreign sessions are cwd-scoped; show them in both scope views (they
|
|
2330
2397
|
// belong to this project) so a Tab toggle never makes them vanish.
|
|
2331
2398
|
for (const [value, summary] of foreignByValue) {
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2399
|
+
choices.push({
|
|
2400
|
+
item: {
|
|
2401
|
+
value,
|
|
2402
|
+
label: summary.title,
|
|
2403
|
+
description: `↩ resume from ${foreignSourceLabel(summary.source)}`,
|
|
2404
|
+
},
|
|
2405
|
+
searchText: `${summary.title} ${summary.id} ${summary.cwd} ${summary.source}`.toLocaleLowerCase(),
|
|
2336
2406
|
});
|
|
2337
2407
|
}
|
|
2338
|
-
const list = new SelectList(items, 10, selectListTheme(), {
|
|
2339
|
-
minPrimaryColumnWidth: 20,
|
|
2340
|
-
maxPrimaryColumnWidth: Math.max(20, terminal.columns - 30),
|
|
2341
|
-
});
|
|
2342
|
-
let overlay;
|
|
2343
2408
|
const closeOverlay = () => {
|
|
2344
2409
|
sessionPickerOverlayOpen = false;
|
|
2345
2410
|
overlay?.hide();
|
|
2346
2411
|
};
|
|
2347
|
-
|
|
2412
|
+
const onSelect = (item) => {
|
|
2348
2413
|
const foreign = foreignByValue.get(item.value);
|
|
2349
2414
|
if (foreign) {
|
|
2350
2415
|
closeOverlay();
|
|
@@ -2356,21 +2421,27 @@ export async function runMakaPiTui(input) {
|
|
|
2356
2421
|
closeOverlay();
|
|
2357
2422
|
void goToSession(item.value);
|
|
2358
2423
|
};
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2424
|
+
const scopeLabel = sessionListScope === 'current'
|
|
2425
|
+
? pickerCopy.sessionScopeCurrent
|
|
2426
|
+
: pickerCopy.sessionScopeAll;
|
|
2427
|
+
if (sessionSearch) {
|
|
2428
|
+
sessionSearch.updateChoices(choices, scopeLabel);
|
|
2429
|
+
sessionSearch.invalidate();
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
2432
|
+
sessionSearch = new SessionSearchOverlay(tui, {
|
|
2433
|
+
locale,
|
|
2434
|
+
choices,
|
|
2435
|
+
scopeLabel,
|
|
2436
|
+
onSelect,
|
|
2437
|
+
onCancel: closeOverlay,
|
|
2438
|
+
onToggleScope: () => {
|
|
2368
2439
|
sessionListScope = sessionListScope === 'current' ? 'all' : 'current';
|
|
2369
|
-
overlay?.hide();
|
|
2370
2440
|
renderScope();
|
|
2371
|
-
return true;
|
|
2372
2441
|
},
|
|
2373
|
-
})
|
|
2442
|
+
});
|
|
2443
|
+
sessionPickerOverlayOpen = true;
|
|
2444
|
+
overlay = showBottomPicker(sessionSearch);
|
|
2374
2445
|
};
|
|
2375
2446
|
renderScope();
|
|
2376
2447
|
};
|
|
@@ -2428,6 +2499,9 @@ export async function runMakaPiTui(input) {
|
|
|
2428
2499
|
requestRender();
|
|
2429
2500
|
return false;
|
|
2430
2501
|
}
|
|
2502
|
+
resetTranscriptViewer();
|
|
2503
|
+
closeTodoOverlay();
|
|
2504
|
+
syncTodoSession();
|
|
2431
2505
|
// A fresh session is not bound by the previous one's boundary. Falling back
|
|
2432
2506
|
// to the *current* label would keep the previous Session's mode, including
|
|
2433
2507
|
// Auto while a changed Host default creates with full access; the launch
|
|
@@ -2509,20 +2583,42 @@ export async function runMakaPiTui(input) {
|
|
|
2509
2583
|
requestRender();
|
|
2510
2584
|
};
|
|
2511
2585
|
const showTranscriptViewer = () => {
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
const
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2586
|
+
if (state.pendingInteraction)
|
|
2587
|
+
return;
|
|
2588
|
+
const sessionId = input.driver.getSessionId() ?? undefined;
|
|
2589
|
+
if (!transcriptViewer || transcriptViewerSessionId !== sessionId) {
|
|
2590
|
+
transcriptViewerSessionId = sessionId;
|
|
2591
|
+
transcriptViewer = new TranscriptViewerOverlay({
|
|
2592
|
+
renderTranscript: transcript.createDocumentRenderer(),
|
|
2593
|
+
locale,
|
|
2594
|
+
viewportRows: () => terminal.rows,
|
|
2595
|
+
onClose: () => {
|
|
2596
|
+
transcriptOverlay?.hide();
|
|
2597
|
+
transcriptOverlay = undefined;
|
|
2598
|
+
},
|
|
2599
|
+
onChange: () => tui.requestRender(),
|
|
2600
|
+
});
|
|
2601
|
+
}
|
|
2602
|
+
transcriptOverlay = tui.showOverlay(transcriptViewer, {
|
|
2521
2603
|
anchor: 'top-left',
|
|
2522
2604
|
width: '100%',
|
|
2523
2605
|
maxHeight: '100%',
|
|
2524
2606
|
});
|
|
2525
2607
|
};
|
|
2608
|
+
const showTodo = () => {
|
|
2609
|
+
if (state.pendingInteraction)
|
|
2610
|
+
return;
|
|
2611
|
+
syncTodoSession();
|
|
2612
|
+
void currentTodo.refresh();
|
|
2613
|
+
closeTodoOverlay();
|
|
2614
|
+
todoOverlay = tui.showOverlay(new TodoOverlay({
|
|
2615
|
+
locale,
|
|
2616
|
+
getState: () => input.driver.queryTodo ? currentTodo.getState() : { status: 'error', items: [] },
|
|
2617
|
+
viewportRows: () => terminal.rows,
|
|
2618
|
+
onClose: closeTodoOverlay,
|
|
2619
|
+
onChange: () => tui.requestRender(),
|
|
2620
|
+
}), { anchor: 'top-left', width: '100%', maxHeight: '100%' });
|
|
2621
|
+
};
|
|
2526
2622
|
const showMcpStatus = () => {
|
|
2527
2623
|
let overlay;
|
|
2528
2624
|
const viewer = new McpManagementOverlay({
|
|
@@ -2943,7 +3039,78 @@ export async function runMakaPiTui(input) {
|
|
|
2943
3039
|
notice('Goal cleared.');
|
|
2944
3040
|
}
|
|
2945
3041
|
};
|
|
3042
|
+
const runHostCommand = async (parts) => {
|
|
3043
|
+
const copy = getTuiHostOwnerCopy(locale);
|
|
3044
|
+
if (!input.hostControl)
|
|
3045
|
+
throw new Error(copy.unavailable);
|
|
3046
|
+
const action = parts[1] ?? 'status';
|
|
3047
|
+
if (action === 'status' && parts.length <= 2) {
|
|
3048
|
+
const text = await input.hostControl.status();
|
|
3049
|
+
if (!closed) {
|
|
3050
|
+
state.entries.push({ kind: 'notice', level: 'info', text });
|
|
3051
|
+
requestRender();
|
|
3052
|
+
}
|
|
3053
|
+
return;
|
|
3054
|
+
}
|
|
3055
|
+
if (action !== 'stop' && action !== 'restart' && action !== 'update') {
|
|
3056
|
+
throw new Error(copy.usage);
|
|
3057
|
+
}
|
|
3058
|
+
const targets = parts.slice(2);
|
|
3059
|
+
if (targets.length > (action === 'update' ? 1 : 0)) {
|
|
3060
|
+
throw new Error(copy.usage);
|
|
3061
|
+
}
|
|
3062
|
+
const accepted = await input.hostControl.prepare({
|
|
3063
|
+
action,
|
|
3064
|
+
...(targets[0] ? { target: targets[0] } : {}),
|
|
3065
|
+
}, (detail) => new Promise((resolve) => {
|
|
3066
|
+
if (closed)
|
|
3067
|
+
return resolve('cancel');
|
|
3068
|
+
showSelectPicker(copy.pickerTitle, 'cancel', [
|
|
3069
|
+
{
|
|
3070
|
+
value: 'cancel',
|
|
3071
|
+
label: copy.cancel,
|
|
3072
|
+
description: copy.cancelDescription,
|
|
3073
|
+
},
|
|
3074
|
+
{
|
|
3075
|
+
value: 'safe',
|
|
3076
|
+
label: copy.safe,
|
|
3077
|
+
description: copy.safeDescription,
|
|
3078
|
+
},
|
|
3079
|
+
...(action === 'update'
|
|
3080
|
+
? []
|
|
3081
|
+
: [
|
|
3082
|
+
{
|
|
3083
|
+
value: 'interrupt',
|
|
3084
|
+
label: copy.interrupt,
|
|
3085
|
+
description: copy.interruptDescription,
|
|
3086
|
+
},
|
|
3087
|
+
]),
|
|
3088
|
+
], (item) => resolve(item.value), {
|
|
3089
|
+
minPrimaryColumnWidth: 16,
|
|
3090
|
+
maxPrimaryColumnWidth: 26,
|
|
3091
|
+
selectedIndex: 0,
|
|
3092
|
+
notice: detail,
|
|
3093
|
+
onCancel: () => resolve('cancel'),
|
|
3094
|
+
});
|
|
3095
|
+
}));
|
|
3096
|
+
if (accepted)
|
|
3097
|
+
beginGracefulClose();
|
|
3098
|
+
};
|
|
2946
3099
|
const slashCommandHandlers = {
|
|
3100
|
+
host: {
|
|
3101
|
+
description: primaryGuidance.commands.host,
|
|
3102
|
+
midTurn: 'local',
|
|
3103
|
+
run: (parts) => {
|
|
3104
|
+
void runHostCommand(parts).catch(reportError);
|
|
3105
|
+
},
|
|
3106
|
+
},
|
|
3107
|
+
update: {
|
|
3108
|
+
description: primaryGuidance.commands.update,
|
|
3109
|
+
midTurn: 'local',
|
|
3110
|
+
run: (parts) => {
|
|
3111
|
+
void runHostCommand(['/host', 'update', ...parts.slice(1)]).catch(reportError);
|
|
3112
|
+
},
|
|
3113
|
+
},
|
|
2947
3114
|
context: {
|
|
2948
3115
|
description: primaryGuidance.commands.context,
|
|
2949
3116
|
// Read-only diagnostics, but runControl-gated: mid-turn it would
|
|
@@ -3241,6 +3408,22 @@ export async function runMakaPiTui(input) {
|
|
|
3241
3408
|
showTranscriptViewer();
|
|
3242
3409
|
},
|
|
3243
3410
|
},
|
|
3411
|
+
todo: {
|
|
3412
|
+
description: primaryGuidance.commands.todo,
|
|
3413
|
+
midTurn: 'local',
|
|
3414
|
+
run: (parts) => {
|
|
3415
|
+
if (parts.length !== 1) {
|
|
3416
|
+
state.entries.push({
|
|
3417
|
+
kind: 'notice',
|
|
3418
|
+
level: 'error',
|
|
3419
|
+
text: TUI_COPY_RESOURCES.todo[locale].usage,
|
|
3420
|
+
});
|
|
3421
|
+
requestRender();
|
|
3422
|
+
return;
|
|
3423
|
+
}
|
|
3424
|
+
showTodo();
|
|
3425
|
+
},
|
|
3426
|
+
},
|
|
3244
3427
|
permissions: {
|
|
3245
3428
|
description: primaryGuidance.commands.permissions,
|
|
3246
3429
|
midTurn: 'refuse',
|
|
@@ -3535,9 +3718,8 @@ export async function runMakaPiTui(input) {
|
|
|
3535
3718
|
if (!matchesKey(data, Key.escape))
|
|
3536
3719
|
lastIdleEscapeAt = 0;
|
|
3537
3720
|
if (matchesKey(data, Key.ctrl('o')) && !isKeyRepeat(data)) {
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
}
|
|
3721
|
+
showTranscriptViewer();
|
|
3722
|
+
return { consume: true };
|
|
3541
3723
|
}
|
|
3542
3724
|
if (matchesKey(data, Key.ctrl('t')) && !isKeyRepeat(data)) {
|
|
3543
3725
|
if (handleExpansionToggleKey('thinking')) {
|
|
@@ -3648,11 +3830,11 @@ export async function runMakaPiTui(input) {
|
|
|
3648
3830
|
// re-wrapping) a full clear would wipe the scrollback the user scrolls through.
|
|
3649
3831
|
// Differential rendering clears the vacated rows without the wipe.
|
|
3650
3832
|
//
|
|
3651
|
-
// The
|
|
3833
|
+
// The live Ctrl+T thinking toggle is viewport-anchored for the same reason: an
|
|
3652
3834
|
// entry above the live viewport lives in terminal scrollback, which cannot
|
|
3653
3835
|
// be rewritten, so resizing it would push pi-tui's differential renderer
|
|
3654
3836
|
// into a scrollback-clearing full redraw (its `firstChanged < viewportTop`
|
|
3655
|
-
// path).
|
|
3837
|
+
// path). It therefore retargets only entries inside the viewport;
|
|
3656
3838
|
// see entryInLiveViewport in pi-transcript.ts (#1097). A block whose own
|
|
3657
3839
|
// expansion pushed its head above the viewport can consequently not be
|
|
3658
3840
|
// collapsed by the next press (#1134): the toggle still flips the default
|
|
@@ -3661,6 +3843,7 @@ export async function runMakaPiTui(input) {
|
|
|
3661
3843
|
// again within EXPANSION_COLLAPSE_CONFIRM_WINDOW_MS applies the collapsed
|
|
3662
3844
|
// default to those blocks too and pays one scrollback-clearing full redraw
|
|
3663
3845
|
// (requestRender(true)), re-anchoring the viewport at the tail.
|
|
3846
|
+
// Ctrl+O instead opens a detached reader whose details never resize live rows.
|
|
3664
3847
|
tui.setClearOnShrink(false);
|
|
3665
3848
|
tui.addChild(layout);
|
|
3666
3849
|
tui.setFocus(editorSurface);
|
|
@@ -3702,6 +3885,7 @@ export async function runMakaPiTui(input) {
|
|
|
3702
3885
|
}
|
|
3703
3886
|
});
|
|
3704
3887
|
}
|
|
3888
|
+
syncTodoSession();
|
|
3705
3889
|
return closedPromise;
|
|
3706
3890
|
}
|
|
3707
3891
|
const BOTTOM_PICKER_MARGIN_ROWS = 4;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { Key, isKeyRelease, isKeyRepeat, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from '@earendil-works/pi-tui';
|
|
20
|
+
import { projectSessionTodoItemsForDisplay, } from '@maka/core/session-todo';
|
|
21
|
+
import { ansi } from './tui-ansi.js';
|
|
22
|
+
import { TUI_COPY_RESOURCES } from './tui-copy-catalog.js';
|
|
23
|
+
/** Owns only the current session; late queries cannot cross session changes or disposal. */
|
|
24
|
+
export class CurrentTodoStore {
|
|
25
|
+
reader;
|
|
26
|
+
onChange;
|
|
27
|
+
snapshot = { status: 'idle', items: [] };
|
|
28
|
+
generation = 0;
|
|
29
|
+
disposed = false;
|
|
30
|
+
pending;
|
|
31
|
+
refreshAgain = false;
|
|
32
|
+
constructor(reader, onChange = () => undefined) {
|
|
33
|
+
this.reader = reader;
|
|
34
|
+
this.onChange = onChange;
|
|
35
|
+
}
|
|
36
|
+
getState() {
|
|
37
|
+
return this.snapshot;
|
|
38
|
+
}
|
|
39
|
+
setSession(sessionId) {
|
|
40
|
+
if (this.disposed || this.snapshot.sessionId === sessionId)
|
|
41
|
+
return;
|
|
42
|
+
this.generation++;
|
|
43
|
+
this.pending = undefined;
|
|
44
|
+
this.refreshAgain = false;
|
|
45
|
+
this.publish({ status: 'idle', ...(sessionId ? { sessionId } : {}), items: [] });
|
|
46
|
+
if (sessionId)
|
|
47
|
+
void this.refresh();
|
|
48
|
+
}
|
|
49
|
+
refresh() {
|
|
50
|
+
const { sessionId } = this.snapshot;
|
|
51
|
+
if (this.disposed || !sessionId)
|
|
52
|
+
return Promise.resolve(false);
|
|
53
|
+
if (this.pending) {
|
|
54
|
+
this.refreshAgain = true;
|
|
55
|
+
return this.pending;
|
|
56
|
+
}
|
|
57
|
+
const pending = this.load(sessionId, this.generation).finally(() => {
|
|
58
|
+
if (this.pending !== pending)
|
|
59
|
+
return;
|
|
60
|
+
this.pending = undefined;
|
|
61
|
+
if (this.refreshAgain) {
|
|
62
|
+
this.refreshAgain = false;
|
|
63
|
+
void this.refresh();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
this.pending = pending;
|
|
67
|
+
return pending;
|
|
68
|
+
}
|
|
69
|
+
dispose() {
|
|
70
|
+
this.disposed = true;
|
|
71
|
+
this.generation++;
|
|
72
|
+
this.pending = undefined;
|
|
73
|
+
this.refreshAgain = false;
|
|
74
|
+
this.snapshot = { status: 'idle', items: [] };
|
|
75
|
+
}
|
|
76
|
+
async load(sessionId, generation) {
|
|
77
|
+
// Keep current data visible during background refreshes, without a second cache.
|
|
78
|
+
if (this.snapshot.status !== 'ready' && this.snapshot.status !== 'empty') {
|
|
79
|
+
this.publish({ status: 'loading', sessionId, items: [] });
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const result = await this.reader.read(sessionId);
|
|
83
|
+
if (generation !== this.generation)
|
|
84
|
+
return false;
|
|
85
|
+
const items = projectSessionTodoItemsForDisplay(result.items);
|
|
86
|
+
this.publish({ status: items.length ? 'ready' : 'empty', sessionId, items });
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
if (generation !== this.generation)
|
|
90
|
+
return false;
|
|
91
|
+
this.publish({ status: 'error', sessionId, items: [] });
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
publish(snapshot) {
|
|
96
|
+
this.snapshot = snapshot;
|
|
97
|
+
this.onChange();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Render the one-line current Todo indicator. Empty and initial states stay hidden. */
|
|
101
|
+
export function renderTodoIndicator(snapshot, input) {
|
|
102
|
+
const copy = TUI_COPY_RESOURCES.todo[input.locale];
|
|
103
|
+
if (snapshot.status === 'idle' || snapshot.status === 'loading' || snapshot.status === 'empty') {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
if (snapshot.status === 'error')
|
|
107
|
+
return fitLine(`${copy.unavailable} · ${copy.open}`, input.width);
|
|
108
|
+
const current = snapshot.items.find((item) => item.status === 'in_progress');
|
|
109
|
+
const completed = snapshot.items.filter((item) => item.status === 'completed').length;
|
|
110
|
+
const suffix = `${copy.progress} ${completed}/${snapshot.items.length} · ${copy.open}`;
|
|
111
|
+
const prefix = current ? `${ansi.accent('◐')} ` : '';
|
|
112
|
+
const available = input.width - visibleWidth(prefix) - visibleWidth(suffix) - (current ? 3 : 1);
|
|
113
|
+
if (!current)
|
|
114
|
+
return fitLine(suffix, input.width);
|
|
115
|
+
if (available < 1)
|
|
116
|
+
return fitLine(suffix, input.width);
|
|
117
|
+
const content = truncateToWidth(current.content, Math.max(1, available), '…');
|
|
118
|
+
return fitLine(`${prefix}${content} · ${suffix}`, input.width);
|
|
119
|
+
}
|
|
120
|
+
/** Read-only, scrollable view of the current Todo projection. */
|
|
121
|
+
export class TodoOverlay {
|
|
122
|
+
input;
|
|
123
|
+
top = 0;
|
|
124
|
+
bodyRows = 1;
|
|
125
|
+
width = 80;
|
|
126
|
+
constructor(input) {
|
|
127
|
+
this.input = input;
|
|
128
|
+
}
|
|
129
|
+
invalidate() { }
|
|
130
|
+
handleInput(data) {
|
|
131
|
+
if (isKeyRelease(data))
|
|
132
|
+
return;
|
|
133
|
+
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl('c'))) {
|
|
134
|
+
if (!isKeyRepeat(data))
|
|
135
|
+
this.input.onClose();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (matchesKey(data, Key.up))
|
|
139
|
+
this.scrollBy(-1);
|
|
140
|
+
else if (matchesKey(data, Key.down))
|
|
141
|
+
this.scrollBy(1);
|
|
142
|
+
else if (matchesKey(data, Key.pageUp))
|
|
143
|
+
this.scrollBy(-this.bodyRows);
|
|
144
|
+
else if (matchesKey(data, Key.pageDown))
|
|
145
|
+
this.scrollBy(this.bodyRows);
|
|
146
|
+
else if (matchesKey(data, Key.home)) {
|
|
147
|
+
this.scrollTo(0);
|
|
148
|
+
this.input.onChange?.();
|
|
149
|
+
}
|
|
150
|
+
else if (matchesKey(data, Key.end)) {
|
|
151
|
+
this.scrollTo(Number.MAX_SAFE_INTEGER);
|
|
152
|
+
this.input.onChange?.();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
render(width) {
|
|
156
|
+
const safeWidth = Math.max(1, width);
|
|
157
|
+
this.width = safeWidth;
|
|
158
|
+
const copy = TUI_COPY_RESOURCES.todo[this.input.locale];
|
|
159
|
+
const viewport = Math.max(1, Math.floor(this.input.viewportRows()));
|
|
160
|
+
const showFooter = viewport > 2;
|
|
161
|
+
this.bodyRows = Math.max(0, viewport - (showFooter ? 2 : 1));
|
|
162
|
+
const snapshot = this.input.getState();
|
|
163
|
+
const rows = this.rows(snapshot, safeWidth);
|
|
164
|
+
const maxTop = Math.max(0, rows.length - this.bodyRows);
|
|
165
|
+
this.top = Math.min(this.top, maxTop);
|
|
166
|
+
const visible = rows.slice(this.top, this.top + this.bodyRows);
|
|
167
|
+
const completed = snapshot.items.filter((item) => item.status === 'completed').length;
|
|
168
|
+
const summary = snapshot.status === 'ready'
|
|
169
|
+
? ` · ${completed}/${snapshot.items.length} ${copy.markedComplete}`
|
|
170
|
+
: '';
|
|
171
|
+
const position = rows.length > this.bodyRows && this.bodyRows > 0
|
|
172
|
+
? ` · ${this.top + 1}-${this.top + visible.length}/${rows.length}`
|
|
173
|
+
: '';
|
|
174
|
+
const header = padLine(`${ansi.bold(copy.title)}${summary}${ansi.dim(position)}`, safeWidth);
|
|
175
|
+
const body = [
|
|
176
|
+
...visible,
|
|
177
|
+
...Array.from({ length: this.bodyRows - visible.length }, () => ' '.repeat(safeWidth)),
|
|
178
|
+
];
|
|
179
|
+
if (!showFooter)
|
|
180
|
+
return [header, ...body];
|
|
181
|
+
const footer = padLine(ansi.dim(copy.hint), safeWidth);
|
|
182
|
+
return [header, ...body, footer];
|
|
183
|
+
}
|
|
184
|
+
rows(snapshot, width) {
|
|
185
|
+
const copy = TUI_COPY_RESOURCES.todo[this.input.locale];
|
|
186
|
+
if (snapshot.status === 'loading')
|
|
187
|
+
return [padLine(copy.loading, width)];
|
|
188
|
+
if (snapshot.status === 'error')
|
|
189
|
+
return [padLine(copy.unavailable, width)];
|
|
190
|
+
if (snapshot.status === 'idle' || snapshot.status === 'empty')
|
|
191
|
+
return [padLine(copy.empty, width)];
|
|
192
|
+
return snapshot.items.flatMap((item) => {
|
|
193
|
+
const symbol = item.status === 'completed'
|
|
194
|
+
? ansi.green('✓')
|
|
195
|
+
: item.status === 'in_progress'
|
|
196
|
+
? ansi.accent('●')
|
|
197
|
+
: ansi.muted('○');
|
|
198
|
+
return wrapTodoItem(`${symbol} `, item.content, width);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
scrollBy(delta) {
|
|
202
|
+
this.scrollTo(this.top + delta);
|
|
203
|
+
this.input.onChange?.();
|
|
204
|
+
}
|
|
205
|
+
scrollTo(top) {
|
|
206
|
+
const rows = this.rows(this.input.getState(), this.width);
|
|
207
|
+
this.top = Math.max(0, Math.min(top, Math.max(0, rows.length - this.bodyRows)));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function wrapTodoItem(prefix, content, width) {
|
|
211
|
+
const lines = wrapTextWithAnsi(content, Math.max(1, width - 2));
|
|
212
|
+
return (lines.length ? lines : ['']).map((line, index) => padLine(`${index === 0 ? prefix : ' '}${line}`, width));
|
|
213
|
+
}
|
|
214
|
+
function fitLine(text, width) {
|
|
215
|
+
return truncateToWidth(text, Math.max(1, width), '…');
|
|
216
|
+
}
|
|
217
|
+
function padLine(text, width) {
|
|
218
|
+
const safeWidth = Math.max(1, width);
|
|
219
|
+
const trimmed = fitLine(text, safeWidth);
|
|
220
|
+
return `${trimmed}${' '.repeat(Math.max(0, safeWidth - visibleWidth(trimmed)))}`;
|
|
221
|
+
}
|