maka-agent 0.2.0-dev.37.20260915 → 0.2.0-dev.39.20260916
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/dist/pi-tui-pickers.js +18 -4
- package/dist/pi-tui-runner.js +310 -76
- package/dist/runtime-host-installed-update-bootstrap.js +3 -1
- package/dist/runtime-host-run-command.js +18 -4
- package/dist/runtime-host-session-channel.js +8 -17
- package/dist/runtime-host-session-driver.js +5 -13
- package/dist/runtime-host-tui-command.js +3 -3
- package/dist/runtime-host-tui-context.js +20 -0
- package/dist/tui-copy-catalog.js +81 -3
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/external-session.js +43 -0
- package/node_modules/@maka/core/dist/model-metadata.js +15 -6
- package/node_modules/@maka/core/dist/provider-registry.js +15 -0
- package/node_modules/@maka/core/dist/recall.js +956 -0
- package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +11 -15
- package/node_modules/@maka/core/dist/runtime-policy.js +1 -1
- package/node_modules/@maka/core/dist/session-name.js +2 -2
- package/node_modules/@maka/core/dist/session.js +16 -0
- package/node_modules/@maka/core/dist/text-sanitize.js +2 -2
- package/node_modules/@maka/core/dist/tool-result-record-schema.js +7 -0
- package/node_modules/@maka/core/dist/usage-record-schema.js +0 -1
- package/node_modules/@maka/core/package.json +1 -1
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +46 -22
- package/node_modules/@maka/runtime/dist/builtin-tools.js +3 -3
- package/node_modules/@maka/runtime/dist/commandcode-cli-language-model.js +836 -0
- package/node_modules/@maka/runtime/dist/continuation-replay.js +3 -1
- package/node_modules/@maka/runtime/dist/memory-extraction.js +7 -1
- package/node_modules/@maka/runtime/dist/model-factory.js +19 -0
- package/node_modules/@maka/runtime/dist/model-fetcher.js +38 -81
- package/node_modules/@maka/runtime/dist/model-history.js +30 -3
- package/node_modules/@maka/runtime/dist/model-runtime.js +8 -0
- package/node_modules/@maka/runtime/dist/provider-error-classification.js +1 -0
- package/node_modules/@maka/runtime/dist/read-page.js +3 -3
- package/node_modules/@maka/runtime/dist/recall-candidates.js +53 -0
- package/node_modules/@maka/runtime/dist/recall-tools.js +210 -0
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +18 -14
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +3 -1
- package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +56 -44
- package/node_modules/@maka/runtime/dist/runtime-read-model.js +1 -2
- package/node_modules/@maka/runtime/dist/session-manager.js +52 -1
- package/node_modules/@maka/runtime/dist/session-recap.js +6 -5
- package/node_modules/@maka/runtime/dist/test-connection.js +59 -2
- package/node_modules/@maka/runtime/dist/tool-availability.js +4 -0
- package/node_modules/@maka/runtime/dist/tool-runtime.js +8 -40
- package/node_modules/@maka/runtime/dist/workers/filesystem-worker.js +2 -2
- package/node_modules/@maka/runtime/package.json +3 -2
- package/node_modules/@maka/runtime-host/dist/client/connect-or-spawn.js +19 -14
- package/node_modules/@maka/runtime-host/dist/client/connection.js +1 -7
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +26 -100
- package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +6 -5
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +5 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -1
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +21 -5
- package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +25 -103
- package/node_modules/@maka/runtime-host/dist/protocol/usage-pricing.js +19 -1
- package/node_modules/@maka/runtime-host/dist/protocol/usage-screen.js +304 -0
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +4 -9
- package/node_modules/@maka/runtime-host/dist/server/connection-session.js +0 -5
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +70 -14
- package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +69 -40
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +10 -0
- package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +22 -3
- package/node_modules/@maka/runtime-host/dist/server/operation-dispatcher.js +15 -2
- package/node_modules/@maka/runtime-host/dist/server/plan-coordinator.js +41 -11
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +5 -7
- package/node_modules/@maka/runtime-host/dist/server/sandbox-boundary-graph-wake.js +3 -5
- package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +289 -476
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +24 -11
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +11 -189
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +58 -177
- package/node_modules/@maka/runtime-host/dist/server/usage-pricing-coordinator.js +41 -1
- package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +292 -72
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +348 -124
- package/node_modules/@maka/storage/dist/execution-stores.js +20 -2
- package/node_modules/@maka/storage/dist/external-session-importer.js +25 -3
- package/node_modules/@maka/storage/dist/offset-external-session-catalog.js +69 -0
- package/node_modules/@maka/storage/dist/opencode-session-adapter.js +278 -73
- package/node_modules/@maka/storage/dist/operational-state-store.js +18 -5
- package/node_modules/@maka/storage/dist/plan-authority.js +48 -0
- package/node_modules/@maka/storage/dist/recall-fold.js +65 -0
- package/node_modules/@maka/storage/dist/runtime-event-persistence.js +2 -0
- package/node_modules/@maka/storage/dist/runtime-transcript-query.js +69 -124
- package/node_modules/@maka/storage/dist/session-store.js +10 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +97 -5
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +139 -0
- package/node_modules/@maka/storage/dist/sqlite-usage-schema.js +57 -1
- package/node_modules/@maka/storage/dist/usage-screen.js +289 -0
- package/node_modules/@maka/storage/dist/usage-stores.js +8 -3
- package/node_modules/@maka/storage/package.json +0 -1
- package/package.json +1 -1
- package/node_modules/@maka/core/dist/foreign-session.js +0 -596
- package/node_modules/@maka/runtime/dist/history-tools.js +0 -429
- package/node_modules/@maka/storage/dist/foreign-session-store.js +0 -641
package/dist/pi-tui-pickers.js
CHANGED
|
@@ -558,6 +558,7 @@ export class SessionSearchOverlay {
|
|
|
558
558
|
list;
|
|
559
559
|
selectedValue;
|
|
560
560
|
scopeLabel;
|
|
561
|
+
notice;
|
|
561
562
|
constructor(tui, input) {
|
|
562
563
|
this.tui = tui;
|
|
563
564
|
this.input = input;
|
|
@@ -565,14 +566,22 @@ export class SessionSearchOverlay {
|
|
|
565
566
|
this.choices = input.choices;
|
|
566
567
|
this.filtered = input.choices;
|
|
567
568
|
this.scopeLabel = input.scopeLabel;
|
|
569
|
+
this.notice = input.notice;
|
|
568
570
|
this.list = this.buildList();
|
|
569
571
|
this.searchEditor = new Editor(tui, editorTheme(), { paddingX: 0 });
|
|
570
572
|
this.searchEditor.onChange = (text) => this.applyQuery(text);
|
|
571
573
|
}
|
|
572
|
-
updateChoices(choices, scopeLabel) {
|
|
574
|
+
updateChoices(choices, scopeLabel, notice) {
|
|
573
575
|
this.choices = choices;
|
|
574
576
|
this.scopeLabel = scopeLabel;
|
|
575
|
-
this.
|
|
577
|
+
this.notice = notice;
|
|
578
|
+
if (this.input.onQuery) {
|
|
579
|
+
this.filtered = choices;
|
|
580
|
+
this.list = this.buildList();
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
this.applyQuery(this.searchEditor.getText());
|
|
584
|
+
}
|
|
576
585
|
}
|
|
577
586
|
buildList() {
|
|
578
587
|
const list = new SelectList(this.filtered.map(({ item }) => item), 10, selectListTheme(), {
|
|
@@ -594,6 +603,10 @@ export class SessionSearchOverlay {
|
|
|
594
603
|
}
|
|
595
604
|
applyQuery(text) {
|
|
596
605
|
const query = text.trim().toLocaleLowerCase();
|
|
606
|
+
if (this.input.onQuery) {
|
|
607
|
+
this.input.onQuery(query);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
597
610
|
this.filtered = query
|
|
598
611
|
? this.choices.filter((choice) => choice.searchText.includes(query))
|
|
599
612
|
: this.choices;
|
|
@@ -628,13 +641,14 @@ export class SessionSearchOverlay {
|
|
|
628
641
|
}
|
|
629
642
|
this.searchEditor.focused = true;
|
|
630
643
|
return [
|
|
631
|
-
padLine(`${this.copy.resumeSessionTitle} ${ansi.accent(this.scopeLabel)}`, safeWidth),
|
|
644
|
+
padLine(`${this.input.title ?? this.copy.resumeSessionTitle} ${ansi.accent(this.scopeLabel)}`, safeWidth),
|
|
632
645
|
padLine(ansi.dim(this.copy.sessionSearchHint), safeWidth),
|
|
633
646
|
padLine('', safeWidth),
|
|
634
647
|
...this.renderFieldRow(safeWidth),
|
|
635
648
|
padLine('', safeWidth),
|
|
649
|
+
...(this.notice ? [padLine(ansi.dim(this.notice), safeWidth)] : []),
|
|
636
650
|
...(this.filtered.length === 0
|
|
637
|
-
? [padLine(ansi.dim(this.copy.noMatchingSessions), safeWidth)]
|
|
651
|
+
? [padLine(ansi.dim(this.input.emptyText ?? this.copy.noMatchingSessions), safeWidth)]
|
|
638
652
|
: this.list.render(safeWidth).map((line) => formatPickerItemLine(line, safeWidth))),
|
|
639
653
|
padLine(ansi.accent('-'.repeat(safeWidth)), safeWidth),
|
|
640
654
|
];
|
package/dist/pi-tui-runner.js
CHANGED
|
@@ -25,7 +25,6 @@ import { projectRevisionLinkedSessionTree } from '@maka/core/session-revisions';
|
|
|
25
25
|
import { slashCommandsForSurface, } from '@maka/core/slash-command-catalog';
|
|
26
26
|
import { latestAssistantModelId, } from '@maka/core/session';
|
|
27
27
|
import { defineUiMessageCatalog, formatUiMessage, resolveUiMessageCatalog, } from '@maka/core/ui-locale';
|
|
28
|
-
import { buildForeignSessionHandoffMessage, foreignSessionHandoffDisplayText, foreignSourceLabel, } from '@maka/core/foreign-session';
|
|
29
28
|
import { listApiKeyOnboardableProviders } from './onboarding-catalog.js';
|
|
30
29
|
import { AUTO_RECAP_DISPLAY_LIMIT_BYTES, shouldAutoRecap } from './session-recap.js';
|
|
31
30
|
import { MakaSkillHighlightEditor } from './skill-highlight-editor.js';
|
|
@@ -54,6 +53,37 @@ import { formatMakaResumeCommand } from './cli-invocation.js';
|
|
|
54
53
|
import { goalAttachedNoticeText, goalPausedNoticeText, goalStatusLabel, goalSummaryLines, isLiveGoalStatus, } from './pi-goal.js';
|
|
55
54
|
import { getTuiPrimaryGuidance } from './tui-primary-guidance.js';
|
|
56
55
|
import { TUI_COPY_RESOURCES } from './tui-copy-catalog.js';
|
|
56
|
+
function externalSourceLabel(adapterId) {
|
|
57
|
+
switch (adapterId) {
|
|
58
|
+
case 'claude-code':
|
|
59
|
+
return 'Claude Code';
|
|
60
|
+
case 'codex':
|
|
61
|
+
return 'Codex';
|
|
62
|
+
case 'opencode':
|
|
63
|
+
return 'OpenCode';
|
|
64
|
+
default:
|
|
65
|
+
return adapterId;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function externalImportErrorCode(error) {
|
|
69
|
+
if (typeof error !== 'object' || error === null)
|
|
70
|
+
return 'commit_outcome_unknown';
|
|
71
|
+
const value = error;
|
|
72
|
+
if (value.operation !== undefined && value.operation !== 'external-session.import') {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
if (value.mode === 'command' && value.dispatch === 'not_dispatched')
|
|
76
|
+
return undefined;
|
|
77
|
+
if (value.mode === 'command' && value.dispatch === 'dispatched') {
|
|
78
|
+
return 'commit_outcome_unknown';
|
|
79
|
+
}
|
|
80
|
+
if (value.code === 'commit_outcome_unknown' ||
|
|
81
|
+
value.code === 'model_unavailable' ||
|
|
82
|
+
value.code === 'source_unreadable') {
|
|
83
|
+
return value.code;
|
|
84
|
+
}
|
|
85
|
+
return value.code === undefined ? 'commit_outcome_unknown' : undefined;
|
|
86
|
+
}
|
|
57
87
|
export function resolveTaskbarProgress(setting, environment = {
|
|
58
88
|
platform: process.platform,
|
|
59
89
|
override: process.env.MAKA_TASKBAR_PROGRESS,
|
|
@@ -96,6 +126,27 @@ const TUI_REWIND_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()(TUI_COP
|
|
|
96
126
|
const TUI_SKILLS_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()(TUI_COPY_RESOURCES.skills));
|
|
97
127
|
const TUI_CONNECTION_IDENTITY_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()(TUI_COPY_RESOURCES['connection-identity']));
|
|
98
128
|
const TUI_SESSION_ACTIONS_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()(TUI_COPY_RESOURCES['session-actions']));
|
|
129
|
+
/**
|
|
130
|
+
* What the exhausted bound is called, in the user's language.
|
|
131
|
+
*
|
|
132
|
+
* The limit kinds are protocol tokens; a reader told the session exceeds the
|
|
133
|
+
* `record_bytes` limit has been handed an implementation detail instead of an
|
|
134
|
+
* explanation.
|
|
135
|
+
*/
|
|
136
|
+
function externalImportLimitLabel(copy, kind) {
|
|
137
|
+
switch (kind) {
|
|
138
|
+
case 'transcript_bytes':
|
|
139
|
+
return copy.externalImportLimitTranscriptBytes;
|
|
140
|
+
case 'record_bytes':
|
|
141
|
+
return copy.externalImportLimitRecordBytes;
|
|
142
|
+
case 'records':
|
|
143
|
+
return copy.externalImportLimitRecords;
|
|
144
|
+
case 'converted_bytes':
|
|
145
|
+
return copy.externalImportLimitConvertedBytes;
|
|
146
|
+
case 'messages':
|
|
147
|
+
return copy.externalImportLimitMessages;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
99
150
|
function sessionConnectionIdentityNotice(session, identities, locale) {
|
|
100
151
|
if (!identities)
|
|
101
152
|
return undefined;
|
|
@@ -2445,14 +2496,258 @@ export async function runMakaPiTui(input) {
|
|
|
2445
2496
|
throw error;
|
|
2446
2497
|
}
|
|
2447
2498
|
};
|
|
2499
|
+
const openImportedExternalSession = async (sessionId, failureText) => {
|
|
2500
|
+
try {
|
|
2501
|
+
await switchSession(sessionId);
|
|
2502
|
+
}
|
|
2503
|
+
catch {
|
|
2504
|
+
state.entries.push({ kind: 'notice', level: 'error', text: failureText });
|
|
2505
|
+
requestRender();
|
|
2506
|
+
return;
|
|
2507
|
+
}
|
|
2508
|
+
try {
|
|
2509
|
+
await discardCurrentSidePair();
|
|
2510
|
+
}
|
|
2511
|
+
catch (error) {
|
|
2512
|
+
reportError(error);
|
|
2513
|
+
}
|
|
2514
|
+
};
|
|
2515
|
+
const importExternalSession = async (adapterId, source) => {
|
|
2516
|
+
if (!input.externalSessions)
|
|
2517
|
+
return;
|
|
2518
|
+
const copy = TUI_SESSION_ACTIONS_COPY[locale];
|
|
2519
|
+
if (source.importState.isImporting) {
|
|
2520
|
+
state.entries.push({
|
|
2521
|
+
kind: 'notice',
|
|
2522
|
+
level: 'error',
|
|
2523
|
+
text: copy.externalUnavailable,
|
|
2524
|
+
});
|
|
2525
|
+
requestRender();
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
let importedSessionId;
|
|
2529
|
+
try {
|
|
2530
|
+
const result = await input.externalSessions.importSession({
|
|
2531
|
+
adapterId,
|
|
2532
|
+
sourceSessionId: source.id,
|
|
2533
|
+
});
|
|
2534
|
+
if (result.kind === 'source_limit_exceeded') {
|
|
2535
|
+
state.entries.push({
|
|
2536
|
+
kind: 'notice',
|
|
2537
|
+
level: 'error',
|
|
2538
|
+
text: formatUiMessage(copy.externalImportLimit, {
|
|
2539
|
+
kind: externalImportLimitLabel(copy, result.limit.kind),
|
|
2540
|
+
max: result.limit.max,
|
|
2541
|
+
}, locale),
|
|
2542
|
+
});
|
|
2543
|
+
return;
|
|
2544
|
+
}
|
|
2545
|
+
importedSessionId = result.session.id;
|
|
2546
|
+
}
|
|
2547
|
+
catch (error) {
|
|
2548
|
+
const code = externalImportErrorCode(error);
|
|
2549
|
+
if (code === 'model_unavailable') {
|
|
2550
|
+
state.entries.push({
|
|
2551
|
+
kind: 'notice',
|
|
2552
|
+
level: 'error',
|
|
2553
|
+
text: copy.externalImportModelUnavailable,
|
|
2554
|
+
});
|
|
2555
|
+
return;
|
|
2556
|
+
}
|
|
2557
|
+
if (code === 'source_unreadable') {
|
|
2558
|
+
state.entries.push({
|
|
2559
|
+
kind: 'notice',
|
|
2560
|
+
level: 'error',
|
|
2561
|
+
text: copy.externalImportSourceUnreadable,
|
|
2562
|
+
});
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
if (code !== 'commit_outcome_unknown') {
|
|
2566
|
+
state.entries.push({ kind: 'notice', level: 'error', text: copy.externalImportFailed });
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
state.entries.push({ kind: 'notice', level: 'error', text: copy.externalImportUncertain });
|
|
2570
|
+
return;
|
|
2571
|
+
}
|
|
2572
|
+
await openImportedExternalSession(importedSessionId, formatUiMessage(copy.externalOpenFailed, { sessionId: importedSessionId }, locale));
|
|
2573
|
+
};
|
|
2574
|
+
const showExternalSessionPage = async (adapterId, initialScope) => {
|
|
2575
|
+
if (!input.externalSessions || closed)
|
|
2576
|
+
return;
|
|
2577
|
+
const copy = TUI_SESSION_ACTIONS_COPY[locale];
|
|
2578
|
+
let scope = initialScope;
|
|
2579
|
+
let query = '';
|
|
2580
|
+
let sessions = [];
|
|
2581
|
+
let nextCursor = null;
|
|
2582
|
+
let revision = 0;
|
|
2583
|
+
let overlay;
|
|
2584
|
+
let search;
|
|
2585
|
+
let byValue = new Map();
|
|
2586
|
+
const closeOverlay = () => overlay?.hide();
|
|
2587
|
+
const toggleScope = () => {
|
|
2588
|
+
const alternate = input.externalSessions
|
|
2589
|
+
?.listScopes()
|
|
2590
|
+
.find((candidate) => candidate !== scope);
|
|
2591
|
+
if (!alternate)
|
|
2592
|
+
return;
|
|
2593
|
+
scope = alternate;
|
|
2594
|
+
void load(false);
|
|
2595
|
+
};
|
|
2596
|
+
const render = () => {
|
|
2597
|
+
// SelectList has no disabled-row contract. A Host-owned in-flight import
|
|
2598
|
+
// disables another import, but it does not disable opening an already
|
|
2599
|
+
// published task.
|
|
2600
|
+
const selectable = sessions.filter((session) => !session.importState.isImporting || session.importState.importedSessionIds.length > 0);
|
|
2601
|
+
byValue = new Map(selectable.map((session) => [`external:${adapterId}:${session.id}`, session]));
|
|
2602
|
+
const choices = selectable.map((session) => ({
|
|
2603
|
+
item: {
|
|
2604
|
+
value: `external:${adapterId}:${session.id}`,
|
|
2605
|
+
label: session.name,
|
|
2606
|
+
description: [
|
|
2607
|
+
session.hostCwd,
|
|
2608
|
+
session.importState.importedCount > 0
|
|
2609
|
+
? formatUiMessage(copy.externalImportedCount, { count: session.importState.importedCount }, locale)
|
|
2610
|
+
: undefined,
|
|
2611
|
+
]
|
|
2612
|
+
.filter(Boolean)
|
|
2613
|
+
.join(' · '),
|
|
2614
|
+
},
|
|
2615
|
+
searchText: '',
|
|
2616
|
+
}));
|
|
2617
|
+
if (nextCursor) {
|
|
2618
|
+
choices.push({
|
|
2619
|
+
item: { value: 'external:load-more', label: copy.externalLoadMore },
|
|
2620
|
+
searchText: '',
|
|
2621
|
+
});
|
|
2622
|
+
}
|
|
2623
|
+
const alternateScope = input.externalSessions
|
|
2624
|
+
?.listScopes()
|
|
2625
|
+
.find((candidate) => candidate !== scope);
|
|
2626
|
+
if (alternateScope) {
|
|
2627
|
+
choices.push({
|
|
2628
|
+
item: {
|
|
2629
|
+
value: 'external:toggle-workspace',
|
|
2630
|
+
label: alternateScope === 'all' ? copy.externalAllWorkspaces : copy.externalCurrentWorkspace,
|
|
2631
|
+
},
|
|
2632
|
+
searchText: '',
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
const scopeLabel = scope === 'all' ? copy.externalAllWorkspaces : copy.externalCurrentWorkspace;
|
|
2636
|
+
const notice = sessions.length > 0 && selectable.length === 0 ? copy.externalUnavailable : undefined;
|
|
2637
|
+
if (search) {
|
|
2638
|
+
search.updateChoices(choices, scopeLabel, notice);
|
|
2639
|
+
search.invalidate();
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2642
|
+
search = new SessionSearchOverlay(tui, {
|
|
2643
|
+
locale,
|
|
2644
|
+
choices,
|
|
2645
|
+
scopeLabel,
|
|
2646
|
+
title: formatUiMessage(copy.externalSessionTitle, { source: externalSourceLabel(adapterId) }, locale),
|
|
2647
|
+
emptyText: sessions.length === 0 ? copy.externalEmpty : copy.externalUnavailable,
|
|
2648
|
+
notice,
|
|
2649
|
+
onQuery: (text) => {
|
|
2650
|
+
query = text;
|
|
2651
|
+
void load(false);
|
|
2652
|
+
},
|
|
2653
|
+
onSelect: (item) => {
|
|
2654
|
+
if (item.value === 'external:load-more' && nextCursor) {
|
|
2655
|
+
void load(true, nextCursor);
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2658
|
+
if (item.value === 'external:toggle-workspace' && alternateScope) {
|
|
2659
|
+
toggleScope();
|
|
2660
|
+
return;
|
|
2661
|
+
}
|
|
2662
|
+
const source = byValue.get(item.value);
|
|
2663
|
+
if (!source)
|
|
2664
|
+
return;
|
|
2665
|
+
closeOverlay();
|
|
2666
|
+
if (busy || turnRunning) {
|
|
2667
|
+
state.entries.push({ kind: 'notice', level: 'error', text: copy.externalImportBusy });
|
|
2668
|
+
requestRender();
|
|
2669
|
+
return;
|
|
2670
|
+
}
|
|
2671
|
+
const latestImportedSessionId = source.importState.importedSessionIds[0];
|
|
2672
|
+
if (latestImportedSessionId === undefined) {
|
|
2673
|
+
void runControl(() => importExternalSession(adapterId, source));
|
|
2674
|
+
return;
|
|
2675
|
+
}
|
|
2676
|
+
const actionTitle = formatUiMessage(copy.externalImportedActionsTitle, { source: source.name }, locale);
|
|
2677
|
+
const actions = [
|
|
2678
|
+
{ value: 'open-latest', label: copy.externalOpenLatestImported },
|
|
2679
|
+
...(!source.importState.isImporting
|
|
2680
|
+
? [{ value: 'import-again', label: copy.externalImportAgain }]
|
|
2681
|
+
: []),
|
|
2682
|
+
];
|
|
2683
|
+
showSelectPicker(actionTitle, source.name, actions, (action) => {
|
|
2684
|
+
if (busy || turnRunning) {
|
|
2685
|
+
state.entries.push({
|
|
2686
|
+
kind: 'notice',
|
|
2687
|
+
level: 'error',
|
|
2688
|
+
text: copy.externalImportBusy,
|
|
2689
|
+
});
|
|
2690
|
+
requestRender();
|
|
2691
|
+
return;
|
|
2692
|
+
}
|
|
2693
|
+
if (action.value === 'import-again') {
|
|
2694
|
+
void runControl(() => importExternalSession(adapterId, source));
|
|
2695
|
+
return;
|
|
2696
|
+
}
|
|
2697
|
+
void runControl(() => openImportedExternalSession(latestImportedSessionId, formatUiMessage(copy.externalOpenLatestFailed, { sessionId: latestImportedSessionId }, locale)));
|
|
2698
|
+
}, { minPrimaryColumnWidth: 24, maxPrimaryColumnWidth: 48 });
|
|
2699
|
+
},
|
|
2700
|
+
onCancel: closeOverlay,
|
|
2701
|
+
onToggleScope: toggleScope,
|
|
2702
|
+
});
|
|
2703
|
+
overlay = showBottomPicker(search);
|
|
2704
|
+
};
|
|
2705
|
+
const load = async (append, cursor) => {
|
|
2706
|
+
const requestRevision = ++revision;
|
|
2707
|
+
try {
|
|
2708
|
+
const page = await input.externalSessions.listSessions({
|
|
2709
|
+
adapterId,
|
|
2710
|
+
scope,
|
|
2711
|
+
...(cursor ? { cursor } : {}),
|
|
2712
|
+
...(query ? { text: query } : {}),
|
|
2713
|
+
});
|
|
2714
|
+
if (requestRevision !== revision || closed || turnRunning)
|
|
2715
|
+
return;
|
|
2716
|
+
sessions = append ? [...sessions, ...page.sessions] : page.sessions;
|
|
2717
|
+
nextCursor = page.nextCursor;
|
|
2718
|
+
render();
|
|
2719
|
+
}
|
|
2720
|
+
catch {
|
|
2721
|
+
if (requestRevision !== revision)
|
|
2722
|
+
return;
|
|
2723
|
+
state.entries.push({ kind: 'notice', level: 'error', text: copy.externalCatalogFailed });
|
|
2724
|
+
requestRender();
|
|
2725
|
+
}
|
|
2726
|
+
};
|
|
2727
|
+
await load(false);
|
|
2728
|
+
};
|
|
2729
|
+
const showExternalSourcePicker = (adapterIds) => {
|
|
2730
|
+
if (!input.externalSessions)
|
|
2731
|
+
return;
|
|
2732
|
+
const initialScope = input.externalSessions.listScopes()[0];
|
|
2733
|
+
if (adapterIds.length === 1) {
|
|
2734
|
+
void showExternalSessionPage(adapterIds[0], initialScope);
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2737
|
+
const copy = TUI_SESSION_ACTIONS_COPY[locale];
|
|
2738
|
+
showSelectPicker(copy.externalSourceTitle, copy.externalSourceTitle, adapterIds.map((adapterId) => ({
|
|
2739
|
+
value: adapterId,
|
|
2740
|
+
label: externalSourceLabel(adapterId),
|
|
2741
|
+
})), (item) => void showExternalSessionPage(item.value, initialScope), { minPrimaryColumnWidth: 20, maxPrimaryColumnWidth: 40 });
|
|
2742
|
+
};
|
|
2448
2743
|
const showSessionList = async () => {
|
|
2449
2744
|
const sessions = await input.driver.listSessions();
|
|
2450
2745
|
const sessionTree = projectRevisionLinkedSessionTree(sessions, input.driver.getSessionId() ?? undefined);
|
|
2451
2746
|
const projectedSessions = flattenLinkedSessionTree(sessionTree.roots, sessionTree.childrenByParentId);
|
|
2452
|
-
// Maka-session availability and
|
|
2747
|
+
// Maka-session availability and Host source discovery are independent I/O; run
|
|
2453
2748
|
// them concurrently so the picker's open latency is the slower of the two,
|
|
2454
2749
|
// not their sum.
|
|
2455
|
-
const [availabilityEntries,
|
|
2750
|
+
const [availabilityEntries, externalSourceQuery] = await Promise.all([
|
|
2456
2751
|
Promise.all(sessions.map(async (session) => {
|
|
2457
2752
|
return [
|
|
2458
2753
|
session.id,
|
|
@@ -2460,36 +2755,18 @@ export async function runMakaPiTui(input) {
|
|
|
2460
2755
|
(await inspectSessionResumeAvailability(session)),
|
|
2461
2756
|
];
|
|
2462
2757
|
})),
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
// selection would silently no-op on importForeignSession's busy guard.
|
|
2467
|
-
input.foreignSessions && !turnRunning
|
|
2468
|
-
? input.foreignSessions.listSessions({ cwd }).then((summaries) => ({ summaries }), (error) => ({ error }))
|
|
2469
|
-
: Promise.resolve({ summaries: [] }),
|
|
2758
|
+
input.externalSessions && !turnRunning
|
|
2759
|
+
? input.externalSessions.listSources().then((adapterIds) => ({ adapterIds }), () => ({ error: true }))
|
|
2760
|
+
: Promise.resolve({ adapterIds: [] }),
|
|
2470
2761
|
]);
|
|
2471
2762
|
const availability = new Map(availabilityEntries);
|
|
2472
|
-
|
|
2473
|
-
// prefixed select value so they never collide with Maka session ids. A scan
|
|
2474
|
-
// error is surfaced (not silently swallowed): degrade to no rows but tell
|
|
2475
|
-
// the user why, so a real store bug isn't mistaken for "no sessions".
|
|
2476
|
-
// Deliberate #2672 exception: the interpolated detail is a local
|
|
2477
|
-
// file-scan diagnostic (not backend copy) and the TUI has no log channel
|
|
2478
|
-
// to carry it, so dropping it would hide the only debugging signal.
|
|
2479
|
-
const foreignByValue = new Map();
|
|
2480
|
-
if ('error' in foreignScan) {
|
|
2481
|
-
const detail = foreignScan.error instanceof Error ? foreignScan.error.message : String(foreignScan.error);
|
|
2763
|
+
if ('error' in externalSourceQuery) {
|
|
2482
2764
|
state.entries.push({
|
|
2483
2765
|
kind: 'notice',
|
|
2484
2766
|
level: 'error',
|
|
2485
|
-
text:
|
|
2767
|
+
text: TUI_SESSION_ACTIONS_COPY[locale].externalCatalogFailed,
|
|
2486
2768
|
});
|
|
2487
2769
|
}
|
|
2488
|
-
else {
|
|
2489
|
-
for (const summary of foreignScan.summaries) {
|
|
2490
|
-
foreignByValue.set(`foreign:${summary.source}:${summary.id}`, summary);
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
2770
|
let overlay;
|
|
2494
2771
|
let sessionSearch;
|
|
2495
2772
|
const renderScope = () => {
|
|
@@ -2525,16 +2802,14 @@ export async function runMakaPiTui(input) {
|
|
|
2525
2802
|
.toLocaleLowerCase(),
|
|
2526
2803
|
};
|
|
2527
2804
|
});
|
|
2528
|
-
|
|
2529
|
-
// belong to this project) so a Tab toggle never makes them vanish.
|
|
2530
|
-
for (const [value, summary] of foreignByValue) {
|
|
2805
|
+
if ('adapterIds' in externalSourceQuery && externalSourceQuery.adapterIds.length > 0) {
|
|
2531
2806
|
choices.push({
|
|
2532
2807
|
item: {
|
|
2533
|
-
value,
|
|
2534
|
-
label:
|
|
2535
|
-
description:
|
|
2808
|
+
value: 'external:import',
|
|
2809
|
+
label: TUI_SESSION_ACTIONS_COPY[locale].externalImport,
|
|
2810
|
+
description: TUI_SESSION_ACTIONS_COPY[locale].externalImportDescription,
|
|
2536
2811
|
},
|
|
2537
|
-
searchText:
|
|
2812
|
+
searchText: TUI_SESSION_ACTIONS_COPY[locale].externalImport.toLocaleLowerCase(),
|
|
2538
2813
|
});
|
|
2539
2814
|
}
|
|
2540
2815
|
const closeOverlay = () => {
|
|
@@ -2542,10 +2817,9 @@ export async function runMakaPiTui(input) {
|
|
|
2542
2817
|
overlay?.hide();
|
|
2543
2818
|
};
|
|
2544
2819
|
const onSelect = (item) => {
|
|
2545
|
-
|
|
2546
|
-
if (foreign) {
|
|
2820
|
+
if (item.value === 'external:import' && 'adapterIds' in externalSourceQuery) {
|
|
2547
2821
|
closeOverlay();
|
|
2548
|
-
|
|
2822
|
+
showExternalSourcePicker(externalSourceQuery.adapterIds);
|
|
2549
2823
|
return;
|
|
2550
2824
|
}
|
|
2551
2825
|
if (availability.get(item.value)?.available === false)
|
|
@@ -2654,46 +2928,6 @@ export async function runMakaPiTui(input) {
|
|
|
2654
2928
|
requestRender();
|
|
2655
2929
|
return true;
|
|
2656
2930
|
};
|
|
2657
|
-
// Import a foreign (Claude Code / Codex) session: read its digest, open a
|
|
2658
|
-
// fresh Maka session, and seed the first turn with an untrusted handoff
|
|
2659
|
-
// envelope. Mirrors submitPreparedUserPrompt: claim `busy` + an activity lease
|
|
2660
|
-
// SYNCHRONOUSLY before the async read so no other turn (a Goal auto-
|
|
2661
|
-
// continuation, or a user Enter) can start during it and make the import a
|
|
2662
|
-
// silent no-op. runAgentTurn re-asserts busy for the turn; on any failure the
|
|
2663
|
-
// finally releases the lease. The handoff is the model-facing `sendText`; a
|
|
2664
|
-
// short line shows in the transcript.
|
|
2665
|
-
const importForeignSession = async (summary) => {
|
|
2666
|
-
if (busy || input.foreignSessions === undefined)
|
|
2667
|
-
return;
|
|
2668
|
-
busy = true;
|
|
2669
|
-
const activity = beginActivity();
|
|
2670
|
-
editor.disableSubmit = true;
|
|
2671
|
-
let handedOff = false;
|
|
2672
|
-
try {
|
|
2673
|
-
const digest = await input.foreignSessions.readDigest(summary);
|
|
2674
|
-
if (closed)
|
|
2675
|
-
return;
|
|
2676
|
-
if (!(await newSession()))
|
|
2677
|
-
return;
|
|
2678
|
-
submitMessage(foreignSessionHandoffDisplayText(digest), 'current_turn', {
|
|
2679
|
-
modelText: buildForeignSessionHandoffMessage(digest),
|
|
2680
|
-
});
|
|
2681
|
-
handedOff = true;
|
|
2682
|
-
}
|
|
2683
|
-
catch (error) {
|
|
2684
|
-
if (closed)
|
|
2685
|
-
return;
|
|
2686
|
-
reportError(error);
|
|
2687
|
-
}
|
|
2688
|
-
finally {
|
|
2689
|
-
if (!handedOff) {
|
|
2690
|
-
busy = false;
|
|
2691
|
-
editor.disableSubmit = false;
|
|
2692
|
-
requestRender();
|
|
2693
|
-
}
|
|
2694
|
-
activity.finish();
|
|
2695
|
-
}
|
|
2696
|
-
};
|
|
2697
2931
|
const showHelp = () => {
|
|
2698
2932
|
// Derive the command list from the registry so /help never drifts from the
|
|
2699
2933
|
// real commands. Keybindings are not commands, so they are listed by hand.
|
|
@@ -51,7 +51,8 @@ export async function runRuntimeHostInstalledUpdateBootstrap(input, overrides =
|
|
|
51
51
|
if (!(await stat(coordinatorCliPath)).isFile()) {
|
|
52
52
|
throw new Error('The copied Maka update coordinator has no CLI entry point');
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
// The copied entry must outlive the coordinator that is executing it.
|
|
55
|
+
const exitCode = await deps.runCoordinator({
|
|
55
56
|
coordinatorCliPath,
|
|
56
57
|
rootPath: input.rootPath,
|
|
57
58
|
archivePath,
|
|
@@ -66,6 +67,7 @@ export async function runRuntimeHostInstalledUpdateBootstrap(input, overrides =
|
|
|
66
67
|
allowInterruptActiveTasks: input.allowInterruptActiveTasks,
|
|
67
68
|
...(input.expectedSource ? { expectedSource: input.expectedSource } : {}),
|
|
68
69
|
});
|
|
70
|
+
return exitCode;
|
|
69
71
|
}
|
|
70
72
|
finally {
|
|
71
73
|
await rm(temporaryRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
@@ -347,7 +347,10 @@ class RuntimeHostRunRuntime {
|
|
|
347
347
|
const active = this.#activeTurn;
|
|
348
348
|
if (!active || active.sessionId !== sessionId || active.turnId !== turnId)
|
|
349
349
|
return;
|
|
350
|
-
|
|
350
|
+
// A read of a running Turn stops wherever the transcript has been
|
|
351
|
+
// committed, so it can restore what the live stream missed but never
|
|
352
|
+
// proves that what the stream already delivered is gone.
|
|
353
|
+
acceptStoredTurn(active.outcome, messages, turnId);
|
|
351
354
|
}
|
|
352
355
|
#waitForGraphTurnTerminal(turnId) {
|
|
353
356
|
if (this.#closed)
|
|
@@ -409,6 +412,7 @@ class TurnOutcomeClassifier {
|
|
|
409
412
|
#outcomeId;
|
|
410
413
|
#unresolvedSandboxFailures = new Set();
|
|
411
414
|
#finalOutput;
|
|
415
|
+
#finalOutputFromLive = false;
|
|
412
416
|
#terminal;
|
|
413
417
|
constructor(outcomeId) {
|
|
414
418
|
this.#outcomeId = outcomeId;
|
|
@@ -418,7 +422,14 @@ class TurnOutcomeClassifier {
|
|
|
418
422
|
case undefined:
|
|
419
423
|
return;
|
|
420
424
|
case 'output':
|
|
425
|
+
// A subscriber is delivered in Host order, so the stream's last answer
|
|
426
|
+
// is the Turn's last answer. A transcript read stops wherever the Host
|
|
427
|
+
// had committed, so it can supply an answer the stream never carried
|
|
428
|
+
// but can never overrule one it did.
|
|
429
|
+
if (observation.source === 'stored' && this.#finalOutputFromLive)
|
|
430
|
+
return;
|
|
421
431
|
this.#finalOutput = observation.text;
|
|
432
|
+
this.#finalOutputFromLive = observation.source === 'live';
|
|
422
433
|
return;
|
|
423
434
|
case 'terminal':
|
|
424
435
|
if (observation.update === 'replace' || this.#terminal === undefined) {
|
|
@@ -459,7 +470,7 @@ class TurnOutcomeClassifier {
|
|
|
459
470
|
}
|
|
460
471
|
function observationFromSessionEvent(event) {
|
|
461
472
|
if (event.type === 'text_complete' && event.text.trim().length > 0) {
|
|
462
|
-
return { kind: 'output', text: event.text };
|
|
473
|
+
return { kind: 'output', text: event.text, source: 'live' };
|
|
463
474
|
}
|
|
464
475
|
if (event.type === 'error') {
|
|
465
476
|
return {
|
|
@@ -484,7 +495,7 @@ function observationFromSessionEvent(event) {
|
|
|
484
495
|
}
|
|
485
496
|
function observationFromStoredMessage(message) {
|
|
486
497
|
if (message.type === 'assistant' && message.text.trim().length > 0) {
|
|
487
|
-
return { kind: 'output', text: message.text };
|
|
498
|
+
return { kind: 'output', text: message.text, source: 'stored' };
|
|
488
499
|
}
|
|
489
500
|
if (message.type === 'turn_state' && message.status === 'completed') {
|
|
490
501
|
return { kind: 'terminal', update: 'replace', status: 'completed' };
|
|
@@ -556,11 +567,14 @@ function outcomeFromStoredTurn(messages, turnId) {
|
|
|
556
567
|
}
|
|
557
568
|
function classifierFromStoredTurn(messages, turnId, outcomeId) {
|
|
558
569
|
const classifier = new TurnOutcomeClassifier(outcomeId);
|
|
570
|
+
acceptStoredTurn(classifier, messages, turnId);
|
|
571
|
+
return classifier;
|
|
572
|
+
}
|
|
573
|
+
function acceptStoredTurn(classifier, messages, turnId) {
|
|
559
574
|
for (const message of messages) {
|
|
560
575
|
if (message.turnId === turnId)
|
|
561
576
|
classifier.accept(observationFromStoredMessage(message));
|
|
562
577
|
}
|
|
563
|
-
return classifier;
|
|
564
578
|
}
|
|
565
579
|
class NonInteractiveInteractionController {
|
|
566
580
|
#driver;
|