maka-agent 0.2.0-dev.41.20260918 → 0.2.0-dev.43.20260919
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/LICENSE +7 -4
- package/dist/acp/maka-acp-agent.js +17 -6
- package/dist/acp/session-event-mapper.js +6 -0
- package/dist/acp/session-interactions.js +520 -0
- package/dist/acp/session-registry.js +80 -9
- package/dist/acp/tool-event-mapper.js +60 -2
- package/dist/pi-tui-runner.js +57 -6
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/client-plugin-bridge.js +19 -0
- package/node_modules/@maka/core/dist/connection-usage.js +27 -0
- package/node_modules/@maka/core/dist/events.js +58 -7
- package/node_modules/@maka/core/dist/external-session.js +4 -3
- package/node_modules/@maka/core/dist/interaction.js +38 -4
- package/node_modules/@maka/core/dist/llm-connections.js +4 -0
- package/node_modules/@maka/core/dist/model-metadata.js +89 -0
- package/node_modules/@maka/core/dist/provider-auth.js +9 -1
- package/node_modules/@maka/core/dist/recall.js +198 -15
- package/node_modules/@maka/core/dist/redaction.js +8 -0
- package/node_modules/@maka/core/dist/session-reference.js +149 -0
- package/node_modules/@maka/core/dist/skill-locations.js +29 -0
- package/node_modules/@maka/core/package.json +4 -0
- package/node_modules/@maka/runtime/dist/commandcode-cli-language-model.js +27 -3
- package/node_modules/@maka/runtime/dist/connection-usage.js +232 -0
- package/node_modules/@maka/runtime/dist/model-factory.js +5 -3
- package/node_modules/@maka/runtime/dist/model-history.js +13 -2
- package/node_modules/@maka/runtime/dist/plugin-client-bridge-service.js +308 -0
- package/node_modules/@maka/runtime/dist/plugin-composition-loader.js +4 -3
- package/node_modules/@maka/runtime/dist/plugin-data-services.js +3 -3
- package/node_modules/@maka/runtime/dist/recall-tools.js +70 -2
- package/node_modules/@maka/runtime/dist/skills-discovery.js +17 -23
- package/node_modules/@maka/runtime/package.json +2 -0
- package/node_modules/@maka/runtime-host/dist/protocol/connection-effects.js +132 -1
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +11 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +6 -0
- package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +269 -0
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/connection-effect-coordinator.js +57 -1
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +50 -9
- package/node_modules/@maka/runtime-host/dist/server/extension-package-manifest.js +19 -1
- package/node_modules/@maka/runtime-host/dist/server/plugin-package-loader.js +88 -14
- package/node_modules/@maka/runtime-host/dist/server/plugin-package-store.js +22 -5
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +122 -1
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +120 -1
- package/node_modules/@maka/runtime-host/dist/server/recall-material-fetch.js +92 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +14 -1
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +17 -1
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +67 -3
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +9 -3
- package/node_modules/@maka/runtime-host/dist/server/workhub-target-execution-authority.js +161 -0
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +26 -28
- package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +34 -1
- package/node_modules/@maka/storage/dist/runtime-policy-stores.js +2 -0
- package/node_modules/@maka/storage/dist/session-store.js +5 -2
- package/package.json +1 -1
|
@@ -28,6 +28,7 @@ import { RuntimeHostSessionUpdateError, getRuntimeHostSession, requireRuntimeHos
|
|
|
28
28
|
import { AcpSessionConfigInputError, createAcpSessionConfigPatch, projectAcpSessionConfigOptions, validateAcpSessionConfigOptionRequest, } from './session-configuration.js';
|
|
29
29
|
import { AcpSessionEventMapper } from './session-event-mapper.js';
|
|
30
30
|
import { mapAcpPromptContent, publishAcpPromptAttachments } from './prompt-content.js';
|
|
31
|
+
import { AcpSessionInteractions } from './session-interactions.js';
|
|
31
32
|
const ACP_SESSION_CURSOR_MAX_BYTES = 8 * 1024;
|
|
32
33
|
const ADMISSION_QUERY_MAX_ATTEMPTS = 5;
|
|
33
34
|
const ADMISSION_QUERY_TIMEOUT_MS = 1_000;
|
|
@@ -39,6 +40,7 @@ export class AcpSessionRegistry {
|
|
|
39
40
|
#newTurnId;
|
|
40
41
|
#inFlightOperations = new Set();
|
|
41
42
|
#ownedSessionIds = new Set();
|
|
43
|
+
#attachmentInteractions = new Map();
|
|
42
44
|
#attachments = new Map();
|
|
43
45
|
#attachmentOpenControllers = new Map();
|
|
44
46
|
#attachmentConfigurations = new Map();
|
|
@@ -181,7 +183,7 @@ export class AcpSessionRegistry {
|
|
|
181
183
|
const connection = await this.#getConnection('subscription.open');
|
|
182
184
|
let attachment;
|
|
183
185
|
try {
|
|
184
|
-
attachment = await this.#ensureAttachment(params.sessionId, connection, context
|
|
186
|
+
attachment = await this.#ensureAttachment(params.sessionId, connection, context);
|
|
185
187
|
}
|
|
186
188
|
catch (error) {
|
|
187
189
|
if (active.cancelled)
|
|
@@ -274,6 +276,11 @@ export class AcpSessionRegistry {
|
|
|
274
276
|
active.projectionAbort.abort();
|
|
275
277
|
active.reconciliationAbort.abort();
|
|
276
278
|
active.transcript?.dispose();
|
|
279
|
+
this.#attachmentInteractions.get(active.sessionId)?.settleTurn(active.turnId);
|
|
280
|
+
const observed = active.attachment?.snapshot.rootTurn;
|
|
281
|
+
if (observed?.turnId === active.turnId && isRuntimeHostTerminalTurn(observed)) {
|
|
282
|
+
this.#attachmentInteractions.get(active.sessionId)?.terminalTurn(active.turnId);
|
|
283
|
+
}
|
|
277
284
|
active.finished = true;
|
|
278
285
|
this.#wake(active);
|
|
279
286
|
this.#removeActivePrompt(active);
|
|
@@ -366,6 +373,7 @@ export class AcpSessionRegistry {
|
|
|
366
373
|
}
|
|
367
374
|
active.projectionAbort.abort();
|
|
368
375
|
active.reconciliationAbort.abort();
|
|
376
|
+
this.#attachmentInteractions.get(active.sessionId)?.cancelTurn(active.turnId);
|
|
369
377
|
active.stopTask ??= this.#stopPromptWhenObservable(active);
|
|
370
378
|
await Promise.all([
|
|
371
379
|
active.mapper.flush().catch(() => undefined),
|
|
@@ -468,14 +476,14 @@ export class AcpSessionRegistry {
|
|
|
468
476
|
}, 'Runtime Host Turn admission could not be established; Stop could not be confirmed');
|
|
469
477
|
this.#wake(active);
|
|
470
478
|
}
|
|
471
|
-
async #ensureAttachment(sessionId, connection,
|
|
479
|
+
async #ensureAttachment(sessionId, connection, context) {
|
|
472
480
|
const existing = this.#attachments.get(sessionId);
|
|
473
481
|
if (existing)
|
|
474
482
|
return existing;
|
|
475
483
|
const openingController = new AbortController();
|
|
476
484
|
this.#attachmentOpenControllers.set(sessionId, openingController);
|
|
477
485
|
const configuration = {
|
|
478
|
-
notify,
|
|
486
|
+
notify: context.notify,
|
|
479
487
|
// Setters can outlive an absent or failed attachment. Their responses
|
|
480
488
|
// must precede refreshes delivered by the new attachment's queue.
|
|
481
489
|
tail: Promise.allSettled([...(this.#pendingConfigSets.get(sessionId) ?? [])]),
|
|
@@ -491,6 +499,50 @@ export class AcpSessionRegistry {
|
|
|
491
499
|
}
|
|
492
500
|
this.#retireFailedAttachment(sessionId, task, attachment, error);
|
|
493
501
|
};
|
|
502
|
+
const interactions = new AcpSessionInteractions({
|
|
503
|
+
sessionId,
|
|
504
|
+
connection,
|
|
505
|
+
client: context.interactions ?? {
|
|
506
|
+
capabilities: {},
|
|
507
|
+
requestPermission: async () => {
|
|
508
|
+
throw RequestError.methodNotFound('session/request_permission');
|
|
509
|
+
},
|
|
510
|
+
createElicitation: async () => {
|
|
511
|
+
throw RequestError.methodNotFound('elicitation/create');
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
onPending: async (pending) => {
|
|
515
|
+
for (const active of this.#activePrompts.get(sessionId) ?? []) {
|
|
516
|
+
if (active.turnId === pending.turnId && !active.cancelled) {
|
|
517
|
+
await active.mapper.pendingInteraction(pending);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
onAnswered: (answered, pending) => attachment?.publishInteractionAnswer(answered, pending),
|
|
522
|
+
onResolved: async (resolved, pending) => {
|
|
523
|
+
for (const active of this.#activePrompts.get(sessionId) ?? []) {
|
|
524
|
+
if (active.turnId === pending.turnId && !active.cancelled && !active.finished) {
|
|
525
|
+
await active.mapper.resolvedInteraction(resolved, pending);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
onFailure: (pending, error) => {
|
|
530
|
+
const active = [...(this.#activePrompts.get(sessionId) ?? [])].find((prompt) => prompt.turnId === pending.turnId && !prompt.finished);
|
|
531
|
+
if (active?.attachment) {
|
|
532
|
+
active.projectionFailure ??= error;
|
|
533
|
+
active.attachment.failTurn(active.turnId, error);
|
|
534
|
+
}
|
|
535
|
+
else if (!attachment)
|
|
536
|
+
failAttachment(error);
|
|
537
|
+
},
|
|
538
|
+
onCancelled: (pending) => {
|
|
539
|
+
for (const active of this.#activePrompts.get(sessionId) ?? []) {
|
|
540
|
+
if (active.turnId === pending.turnId)
|
|
541
|
+
void this.#cancelPrompt(active).catch(() => undefined);
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
});
|
|
545
|
+
this.#attachmentInteractions.set(sessionId, interactions);
|
|
494
546
|
task = RuntimeHostSessionChannel.open({
|
|
495
547
|
connection,
|
|
496
548
|
signal: openingController.signal,
|
|
@@ -501,6 +553,9 @@ export class AcpSessionRegistry {
|
|
|
501
553
|
onRuntimeResourceChanged: () => undefined,
|
|
502
554
|
onSnapshotChanged: (snapshot) => {
|
|
503
555
|
this.#wakeSession(sessionId);
|
|
556
|
+
if (snapshot.rootTurn && isRuntimeHostTerminalTurn(snapshot.rootTurn)) {
|
|
557
|
+
interactions.terminalTurn(snapshot.rootTurn.turnId);
|
|
558
|
+
}
|
|
504
559
|
if (configuration.metadataRevision === undefined) {
|
|
505
560
|
configuration.metadataRevision = snapshot.session.metadataRevision;
|
|
506
561
|
return;
|
|
@@ -534,15 +589,20 @@ export class AcpSessionRegistry {
|
|
|
534
589
|
}
|
|
535
590
|
},
|
|
536
591
|
onInteractionPending: (pending) => {
|
|
537
|
-
if (!
|
|
538
|
-
|
|
539
|
-
//
|
|
592
|
+
if (!interactions.fencesTurn(pending.turnId) &&
|
|
593
|
+
![...(this.#activePrompts.get(sessionId) ?? [])].some((active) => active.turnId === pending.turnId && active.dispatchStarted)) {
|
|
594
|
+
// Idle attachments may observe a Turn started by another client. Do not
|
|
595
|
+
// present its interaction through this ACP connection's client.
|
|
540
596
|
return;
|
|
541
597
|
}
|
|
542
|
-
|
|
543
|
-
|
|
598
|
+
void interactions.pending(pending);
|
|
599
|
+
},
|
|
600
|
+
onInteractionResolved: (pending) => {
|
|
601
|
+
if (!interactions.fencesTurn(pending.turnId) &&
|
|
602
|
+
![...(this.#activePrompts.get(sessionId) ?? [])].some((active) => active.turnId === pending.turnId && active.dispatchStarted))
|
|
603
|
+
return;
|
|
604
|
+
void interactions.resolved(pending);
|
|
544
605
|
},
|
|
545
|
-
onInteractionResolved: () => undefined,
|
|
546
606
|
onTranscriptSettlement: (turnId) => {
|
|
547
607
|
for (const active of this.#activePrompts.get(sessionId) ?? []) {
|
|
548
608
|
if (active.turnId === turnId)
|
|
@@ -583,6 +643,10 @@ export class AcpSessionRegistry {
|
|
|
583
643
|
return channel;
|
|
584
644
|
})
|
|
585
645
|
.catch((error) => {
|
|
646
|
+
interactions.close();
|
|
647
|
+
if (this.#attachmentInteractions.get(sessionId) === interactions) {
|
|
648
|
+
this.#attachmentInteractions.delete(sessionId);
|
|
649
|
+
}
|
|
586
650
|
if (this.#attachments.get(sessionId) === task) {
|
|
587
651
|
this.#attachments.delete(sessionId);
|
|
588
652
|
this.#attachmentConfigurations.delete(sessionId);
|
|
@@ -603,6 +667,8 @@ export class AcpSessionRegistry {
|
|
|
603
667
|
if (this.#attachments.get(sessionId) === task) {
|
|
604
668
|
this.#attachments.delete(sessionId);
|
|
605
669
|
this.#attachmentConfigurations.delete(sessionId);
|
|
670
|
+
this.#attachmentInteractions.get(sessionId)?.close();
|
|
671
|
+
this.#attachmentInteractions.delete(sessionId);
|
|
606
672
|
}
|
|
607
673
|
for (const active of this.#activePrompts.get(sessionId) ?? []) {
|
|
608
674
|
if (active.attachment !== attachment)
|
|
@@ -616,6 +682,8 @@ export class AcpSessionRegistry {
|
|
|
616
682
|
}
|
|
617
683
|
async #closeSession(sessionId, delivery) {
|
|
618
684
|
const cancellation = await this.#cancelSession(sessionId);
|
|
685
|
+
this.#attachmentInteractions.get(sessionId)?.close();
|
|
686
|
+
this.#attachmentInteractions.delete(sessionId);
|
|
619
687
|
const attachmentTask = this.#attachments.get(sessionId);
|
|
620
688
|
this.#attachments.delete(sessionId);
|
|
621
689
|
let closeError;
|
|
@@ -814,6 +882,9 @@ export class AcpSessionRegistry {
|
|
|
814
882
|
...(this.#activePrompts.get(sessionId) ?? []),
|
|
815
883
|
]);
|
|
816
884
|
const cancellations = [...sessionIds].map((sessionId) => this.#cancelSession(sessionId));
|
|
885
|
+
for (const interactions of this.#attachmentInteractions.values())
|
|
886
|
+
interactions.close();
|
|
887
|
+
this.#attachmentInteractions.clear();
|
|
817
888
|
const attachments = [...this.#attachments.values()];
|
|
818
889
|
this.#attachments.clear();
|
|
819
890
|
const configurations = [...this.#attachmentConfigurations.values()];
|
|
@@ -96,6 +96,59 @@ export class AcpToolEventMapper {
|
|
|
96
96
|
await this.#result(this.#ensure(message.turnId, message.toolUseId), message.isError, message.content, message.durationMs, false);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
+
async pendingInteraction(pending) {
|
|
100
|
+
const tool = this.#ensure(pending.turnId, interactionToolId(pending));
|
|
101
|
+
if (pending.request.kind === 'sandbox_boundary')
|
|
102
|
+
tool.synthetic = true;
|
|
103
|
+
if (tool.terminal)
|
|
104
|
+
return;
|
|
105
|
+
tool.status = 'pending';
|
|
106
|
+
tool.meta.interaction = {
|
|
107
|
+
id: pending.interactionId,
|
|
108
|
+
kind: pending.request.kind,
|
|
109
|
+
status: 'pending',
|
|
110
|
+
};
|
|
111
|
+
if (!tool.name)
|
|
112
|
+
tool.title = `Awaiting ${pending.request.kind.replaceAll('_', ' ')}`;
|
|
113
|
+
await this.#publish(tool);
|
|
114
|
+
}
|
|
115
|
+
async resolvedInteraction(resolved, pending) {
|
|
116
|
+
if (resolved.status === 'pending')
|
|
117
|
+
return;
|
|
118
|
+
const tool = this.#ensure(pending.turnId, interactionToolId(pending));
|
|
119
|
+
if (pending.request.kind === 'sandbox_boundary')
|
|
120
|
+
tool.synthetic = true;
|
|
121
|
+
const outcome = resolved.outcome;
|
|
122
|
+
tool.meta.interaction = {
|
|
123
|
+
id: pending.interactionId,
|
|
124
|
+
kind: pending.request.kind,
|
|
125
|
+
status: resolved.status,
|
|
126
|
+
...(outcome.kind === 'closure' ? { reason: outcome.reason } : {}),
|
|
127
|
+
...('decision' in outcome ? { decision: outcome.decision } : {}),
|
|
128
|
+
...('action' in outcome ? { action: outcome.action } : {}),
|
|
129
|
+
};
|
|
130
|
+
if (!tool.terminal) {
|
|
131
|
+
if (pending.request.kind === 'sandbox_boundary') {
|
|
132
|
+
tool.terminal = true;
|
|
133
|
+
tool.status =
|
|
134
|
+
outcome.kind === 'sandbox_boundary_decision' && outcome.decision === 'allow'
|
|
135
|
+
? 'completed'
|
|
136
|
+
: 'failed';
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
tool.status = 'in_progress';
|
|
140
|
+
}
|
|
141
|
+
const closure = outcome.kind === 'closure' ? `Interaction closed: ${outcome.reason}` : undefined;
|
|
142
|
+
if (closure && !tool.terminal) {
|
|
143
|
+
tool.progress ??= progressBuffer();
|
|
144
|
+
tool.progress.append(closure);
|
|
145
|
+
}
|
|
146
|
+
await this.#publish(tool, closure && pending.request.kind === 'sandbox_boundary'
|
|
147
|
+
? { content: textContent(closure) }
|
|
148
|
+
: {});
|
|
149
|
+
if (tool.terminal)
|
|
150
|
+
this.#release(tool);
|
|
151
|
+
}
|
|
99
152
|
/** Called only after authoritative turn settlement and transcript reconciliation. */
|
|
100
153
|
async finishTools(turnId, terminalStatus) {
|
|
101
154
|
let missingResult = false;
|
|
@@ -112,8 +165,8 @@ export class AcpToolEventMapper {
|
|
|
112
165
|
}
|
|
113
166
|
// A live result announcement promises a durable result. A start, delta or
|
|
114
167
|
// progress event alone does not: completed turns can contain unfinished
|
|
115
|
-
// tool calls, and
|
|
116
|
-
if (terminalStatus === 'completed' && tool.resultAnnounced)
|
|
168
|
+
// tool calls, and synthetic interaction cards have no tool result.
|
|
169
|
+
if (terminalStatus === 'completed' && !tool.synthetic && tool.resultAnnounced)
|
|
117
170
|
missingResult = true;
|
|
118
171
|
this.#release(tool);
|
|
119
172
|
}
|
|
@@ -308,6 +361,11 @@ function fixedStateChars(tool) {
|
|
|
308
361
|
function digestValue(value) {
|
|
309
362
|
return createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
310
363
|
}
|
|
364
|
+
function interactionToolId(pending) {
|
|
365
|
+
return pending.request.kind === 'sandbox_boundary'
|
|
366
|
+
? pending.interactionId
|
|
367
|
+
: pending.request.toolUseId;
|
|
368
|
+
}
|
|
311
369
|
function rawInput(toolName, args) {
|
|
312
370
|
// The shared transcript intentionally projects these two tools' arguments.
|
|
313
371
|
if (args === undefined || toolName === 'WriteStdin' || toolName === 'todo_write')
|
package/dist/pi-tui-runner.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { randomUUID } from 'node:crypto';
|
|
20
20
|
import { Key, ProcessTerminal, SelectList, TuiMainScreen, isKeyRelease, isKeyRepeat, matchesKey, } from '@earendil-works/pi-tui';
|
|
21
|
+
import { normalizeExternalSessionQueryText, } from '@maka/core/external-session';
|
|
21
22
|
import { CurrentTodoStore, TodoOverlay, renderTodoIndicator } from './pi-tui-todo.js';
|
|
22
23
|
import { isThinkingLevel } from '@maka/core/model-thinking';
|
|
23
24
|
import { deriveConnectionSlug } from '@maka/core/llm-connections';
|
|
@@ -53,6 +54,7 @@ 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';
|
|
55
56
|
import { TUI_COPY_RESOURCES } from './tui-copy-catalog.js';
|
|
57
|
+
const EXTERNAL_SESSION_SEARCH_DEBOUNCE_MS = 120;
|
|
56
58
|
function externalSourceLabel(adapterId) {
|
|
57
59
|
switch (adapterId) {
|
|
58
60
|
case 'claude-code':
|
|
@@ -203,6 +205,7 @@ export async function runMakaPiTui(input) {
|
|
|
203
205
|
let connectionIdentityNotice;
|
|
204
206
|
let busy = false;
|
|
205
207
|
let closed = false;
|
|
208
|
+
const cancelScheduledExternalSearches = new Set();
|
|
206
209
|
let currentActivityCompletion;
|
|
207
210
|
let permissionResponseInFlightRequestId = null;
|
|
208
211
|
// Session recap (issue #1055): an in-flight lock shared by manual and
|
|
@@ -718,6 +721,9 @@ export async function runMakaPiTui(input) {
|
|
|
718
721
|
attention.reset();
|
|
719
722
|
// Stop asking the terminal for focus reports before handing it back.
|
|
720
723
|
terminal.write(DISABLE_FOCUS_REPORTING);
|
|
724
|
+
for (const cancel of cancelScheduledExternalSearches)
|
|
725
|
+
cancel();
|
|
726
|
+
cancelScheduledExternalSearches.clear();
|
|
721
727
|
tui.stop();
|
|
722
728
|
};
|
|
723
729
|
const beginClose = (error) => {
|
|
@@ -2580,17 +2586,41 @@ export async function runMakaPiTui(input) {
|
|
|
2580
2586
|
let sessions = [];
|
|
2581
2587
|
let nextCursor = null;
|
|
2582
2588
|
let revision = 0;
|
|
2589
|
+
let normalizedQuery = normalizeExternalSessionQueryText(query);
|
|
2590
|
+
let cancelScheduledSearch;
|
|
2591
|
+
let pageClosed = false;
|
|
2583
2592
|
let overlay;
|
|
2584
2593
|
let search;
|
|
2585
2594
|
let byValue = new Map();
|
|
2586
|
-
const
|
|
2595
|
+
const dropScheduledSearch = () => {
|
|
2596
|
+
const hadScheduledSearch = cancelScheduledSearch !== undefined;
|
|
2597
|
+
if (cancelScheduledSearch)
|
|
2598
|
+
cancelScheduledExternalSearches.delete(cancelScheduledSearch);
|
|
2599
|
+
cancelScheduledSearch?.();
|
|
2600
|
+
cancelScheduledSearch = undefined;
|
|
2601
|
+
return hadScheduledSearch;
|
|
2602
|
+
};
|
|
2603
|
+
const resetCatalogPage = () => {
|
|
2604
|
+
sessions = [];
|
|
2605
|
+
nextCursor = null;
|
|
2606
|
+
byValue = new Map();
|
|
2607
|
+
render();
|
|
2608
|
+
};
|
|
2609
|
+
const closeOverlay = () => {
|
|
2610
|
+
pageClosed = true;
|
|
2611
|
+
dropScheduledSearch();
|
|
2612
|
+
revision += 1;
|
|
2613
|
+
overlay?.hide();
|
|
2614
|
+
};
|
|
2587
2615
|
const toggleScope = () => {
|
|
2588
2616
|
const alternate = input.externalSessions
|
|
2589
2617
|
?.listScopes()
|
|
2590
2618
|
.find((candidate) => candidate !== scope);
|
|
2591
2619
|
if (!alternate)
|
|
2592
2620
|
return;
|
|
2621
|
+
dropScheduledSearch();
|
|
2593
2622
|
scope = alternate;
|
|
2623
|
+
resetCatalogPage();
|
|
2594
2624
|
void load(false);
|
|
2595
2625
|
};
|
|
2596
2626
|
const render = () => {
|
|
@@ -2647,8 +2677,29 @@ export async function runMakaPiTui(input) {
|
|
|
2647
2677
|
emptyText: sessions.length === 0 ? copy.externalEmpty : copy.externalUnavailable,
|
|
2648
2678
|
notice,
|
|
2649
2679
|
onQuery: (text) => {
|
|
2680
|
+
const nextNormalizedQuery = normalizeExternalSessionQueryText(text);
|
|
2650
2681
|
query = text;
|
|
2651
|
-
|
|
2682
|
+
if (nextNormalizedQuery === normalizedQuery)
|
|
2683
|
+
return;
|
|
2684
|
+
normalizedQuery = nextNormalizedQuery;
|
|
2685
|
+
dropScheduledSearch();
|
|
2686
|
+
resetCatalogPage();
|
|
2687
|
+
// Retire an older in-flight response immediately. Waiting until the
|
|
2688
|
+
// debounce fires would let it repaint results for the previous query.
|
|
2689
|
+
const requestRevision = ++revision;
|
|
2690
|
+
let cancel;
|
|
2691
|
+
const handle = setTimeout(() => {
|
|
2692
|
+
if (cancel)
|
|
2693
|
+
cancelScheduledExternalSearches.delete(cancel);
|
|
2694
|
+
cancelScheduledSearch = undefined;
|
|
2695
|
+
if (closed || pageClosed)
|
|
2696
|
+
return;
|
|
2697
|
+
void load(false, undefined, requestRevision);
|
|
2698
|
+
}, EXTERNAL_SESSION_SEARCH_DEBOUNCE_MS);
|
|
2699
|
+
handle.unref();
|
|
2700
|
+
cancel = () => clearTimeout(handle);
|
|
2701
|
+
cancelScheduledSearch = cancel;
|
|
2702
|
+
cancelScheduledExternalSearches.add(cancel);
|
|
2652
2703
|
},
|
|
2653
2704
|
onSelect: (item) => {
|
|
2654
2705
|
if (item.value === 'external:load-more' && nextCursor) {
|
|
@@ -2702,8 +2753,8 @@ export async function runMakaPiTui(input) {
|
|
|
2702
2753
|
});
|
|
2703
2754
|
overlay = showBottomPicker(search);
|
|
2704
2755
|
};
|
|
2705
|
-
const load = async (append, cursor) => {
|
|
2706
|
-
const requestRevision = ++revision;
|
|
2756
|
+
const load = async (append, cursor, scheduledRevision) => {
|
|
2757
|
+
const requestRevision = scheduledRevision ?? ++revision;
|
|
2707
2758
|
try {
|
|
2708
2759
|
const page = await input.externalSessions.listSessions({
|
|
2709
2760
|
adapterId,
|
|
@@ -2711,14 +2762,14 @@ export async function runMakaPiTui(input) {
|
|
|
2711
2762
|
...(cursor ? { cursor } : {}),
|
|
2712
2763
|
...(query ? { text: query } : {}),
|
|
2713
2764
|
});
|
|
2714
|
-
if (requestRevision !== revision || closed || turnRunning)
|
|
2765
|
+
if (requestRevision !== revision || pageClosed || closed || turnRunning)
|
|
2715
2766
|
return;
|
|
2716
2767
|
sessions = append ? [...sessions, ...page.sessions] : page.sessions;
|
|
2717
2768
|
nextCursor = page.nextCursor;
|
|
2718
2769
|
render();
|
|
2719
2770
|
}
|
|
2720
2771
|
catch {
|
|
2721
|
-
if (requestRevision !== revision)
|
|
2772
|
+
if (requestRevision !== revision || pageClosed || closed)
|
|
2722
2773
|
return;
|
|
2723
2774
|
state.entries.push({ kind: 'notice', level: 'error', text: copy.externalCatalogFailed });
|
|
2724
2775
|
requestRender();
|
|
Binary file
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
/**
|
|
20
|
+
* The providers Maka can read account usage from. The single source of truth
|
|
21
|
+
* for both the settings section's visibility and the runtime dispatch, so a
|
|
22
|
+
* provider that gains a mapper is offered the card in the same change.
|
|
23
|
+
*/
|
|
24
|
+
const USAGE_PROVIDERS = new Set(['commandcode-go']);
|
|
25
|
+
export function providerReportsUsage(providerType) {
|
|
26
|
+
return USAGE_PROVIDERS.has(providerType);
|
|
27
|
+
}
|
|
@@ -79,7 +79,16 @@ export function hasMeaningfulMessageContent(content) {
|
|
|
79
79
|
(content.directoryReferences?.length ?? 0) > 0);
|
|
80
80
|
}
|
|
81
81
|
const ATTACHMENT_REF_SHAPE = defineObjectShape()(['kind', 'name', 'mimeType', 'bytes', 'ref'], []);
|
|
82
|
-
const QUOTE_REF_SHAPE = defineObjectShape()(['text'], [
|
|
82
|
+
const QUOTE_REF_SHAPE = defineObjectShape()(['text'], [
|
|
83
|
+
'label',
|
|
84
|
+
'sourceTurnId',
|
|
85
|
+
'sourceSessionId',
|
|
86
|
+
'sourceSessionName',
|
|
87
|
+
'sourceCapturedAt',
|
|
88
|
+
'sourceTruncated',
|
|
89
|
+
]);
|
|
90
|
+
const QUOTE_REF_SESSION_ID_MAX_LENGTH = 512;
|
|
91
|
+
const QUOTE_REF_SESSION_NAME_MAX_LENGTH = 200;
|
|
83
92
|
const INLINE_REFERENCE_SHAPE = defineObjectShape()(['kind', 'value', 'label', 'start'], []);
|
|
84
93
|
const INLINE_SKILL_REFERENCE_VALUE = /^\/skill:[A-Za-z0-9._-]+$/;
|
|
85
94
|
export const INLINE_REFERENCE_MAX_COUNT = 32;
|
|
@@ -113,6 +122,22 @@ export function normalizeMessageContent(content) {
|
|
|
113
122
|
text: quote.text,
|
|
114
123
|
...(quote.label !== undefined ? { label: quote.label } : {}),
|
|
115
124
|
...(quote.sourceTurnId !== undefined ? { sourceTurnId: quote.sourceTurnId } : {}),
|
|
125
|
+
...(quote.sourceSessionId !== undefined
|
|
126
|
+
? { sourceSessionId: quote.sourceSessionId }
|
|
127
|
+
: {}),
|
|
128
|
+
...(quote.sourceSessionName !== undefined
|
|
129
|
+
? { sourceSessionName: quote.sourceSessionName }
|
|
130
|
+
: {}),
|
|
131
|
+
...(quote.sourceCapturedAt !== undefined
|
|
132
|
+
? {
|
|
133
|
+
sourceCapturedAt: Object.is(quote.sourceCapturedAt, -0)
|
|
134
|
+
? 0
|
|
135
|
+
: quote.sourceCapturedAt,
|
|
136
|
+
}
|
|
137
|
+
: {}),
|
|
138
|
+
...(quote.sourceTruncated !== undefined
|
|
139
|
+
? { sourceTruncated: quote.sourceTruncated }
|
|
140
|
+
: {}),
|
|
116
141
|
})),
|
|
117
142
|
}
|
|
118
143
|
: {}),
|
|
@@ -205,11 +230,33 @@ export function isInlineReference(value) {
|
|
|
205
230
|
})));
|
|
206
231
|
}
|
|
207
232
|
export function isQuoteRef(value) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
233
|
+
const record = isRecord(value) ? value : undefined;
|
|
234
|
+
const sourceFields = record
|
|
235
|
+
? [
|
|
236
|
+
record.sourceSessionId,
|
|
237
|
+
record.sourceSessionName,
|
|
238
|
+
record.sourceCapturedAt,
|
|
239
|
+
record.sourceTruncated,
|
|
240
|
+
]
|
|
241
|
+
: [];
|
|
242
|
+
const hasSourceMetadata = sourceFields.some((field) => field !== undefined);
|
|
243
|
+
return (record !== undefined &&
|
|
244
|
+
hasExactShape(record, QUOTE_REF_SHAPE) &&
|
|
245
|
+
typeof record.text === 'string' &&
|
|
246
|
+
(record.label === undefined || typeof record.label === 'string') &&
|
|
247
|
+
(record.sourceTurnId === undefined || typeof record.sourceTurnId === 'string') &&
|
|
248
|
+
(!hasSourceMetadata ||
|
|
249
|
+
(typeof record.sourceSessionId === 'string' &&
|
|
250
|
+
record.sourceSessionId.length > 0 &&
|
|
251
|
+
record.sourceSessionId.length <= QUOTE_REF_SESSION_ID_MAX_LENGTH &&
|
|
252
|
+
typeof record.sourceSessionName === 'string' &&
|
|
253
|
+
record.sourceSessionName.length > 0 &&
|
|
254
|
+
record.sourceSessionName.length <= QUOTE_REF_SESSION_NAME_MAX_LENGTH &&
|
|
255
|
+
typeof record.sourceCapturedAt === 'number' &&
|
|
256
|
+
Number.isFinite(record.sourceCapturedAt) &&
|
|
257
|
+
record.sourceCapturedAt >= 0 &&
|
|
258
|
+
record.sourceCapturedAt <= 8.64e15 &&
|
|
259
|
+
typeof record.sourceTruncated === 'boolean')));
|
|
213
260
|
}
|
|
214
261
|
export function isAttachmentRef(value) {
|
|
215
262
|
return (isRecord(value) &&
|
|
@@ -339,7 +386,11 @@ function inlineReferencesEqual(left, right) {
|
|
|
339
386
|
function quoteRefsEqual(left, right) {
|
|
340
387
|
return (left.text === right.text &&
|
|
341
388
|
left.label === right.label &&
|
|
342
|
-
left.sourceTurnId === right.sourceTurnId
|
|
389
|
+
left.sourceTurnId === right.sourceTurnId &&
|
|
390
|
+
left.sourceSessionId === right.sourceSessionId &&
|
|
391
|
+
left.sourceSessionName === right.sourceSessionName &&
|
|
392
|
+
left.sourceCapturedAt === right.sourceCapturedAt &&
|
|
393
|
+
left.sourceTruncated === right.sourceTruncated);
|
|
343
394
|
}
|
|
344
395
|
function attachmentRefsEqual(left, right) {
|
|
345
396
|
if (left.kind !== right.kind ||
|
|
@@ -72,8 +72,8 @@ export function externalSessionMatchesQuery(summary, query = {}) {
|
|
|
72
72
|
const text = normalizeExternalSessionQueryText(query.text);
|
|
73
73
|
if (text === undefined)
|
|
74
74
|
return true;
|
|
75
|
-
//
|
|
76
|
-
//
|
|
75
|
+
// Source id, title, and path are the bounded summary fields a user can paste
|
|
76
|
+
// or remember about a conversation. All already sit on the summary, so
|
|
77
77
|
// matching costs no extra reads. Message content is deliberately excluded:
|
|
78
78
|
// it would mean opening every transcript on every keystroke.
|
|
79
79
|
//
|
|
@@ -86,7 +86,8 @@ export function externalSessionMatchesQuery(summary, query = {}) {
|
|
|
86
86
|
// literal backslash-n, and folding the term without the title would stop
|
|
87
87
|
// `\\n` from finding the very title it names. The path pair has no such
|
|
88
88
|
// ambiguity: a separator there is a separator.
|
|
89
|
-
return (normalizeExternalSessionMatchText(summary.
|
|
89
|
+
return (normalizeExternalSessionMatchText(summary.id).includes(text) ||
|
|
90
|
+
normalizeExternalSessionMatchText(summary.name).includes(text) ||
|
|
90
91
|
foldExternalSessionPathSeparators(normalizeExternalSessionMatchText(summary.cwd)).includes(foldExternalSessionPathSeparators(text)));
|
|
91
92
|
}
|
|
92
93
|
/**
|