maka-agent 0.2.0-dev.42.20260918 → 0.2.0-dev.44.20260920
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 +12 -6
- 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/mcp-capability-provider.js +7 -0
- package/dist/pi-tui-runner.js +57 -6
- package/dist/runtime-host-tui-command.js +1 -1
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/chat-model-choice.js +3 -0
- package/node_modules/@maka/core/dist/connection-usage.js +27 -0
- package/node_modules/@maka/core/dist/external-session.js +4 -3
- package/node_modules/@maka/core/dist/llm-connections.js +3 -21
- package/node_modules/@maka/core/dist/model-catalog.js +8 -20
- package/node_modules/@maka/core/dist/model-metadata.generated.js +304 -304
- package/node_modules/@maka/core/dist/model-thinking.js +16 -1
- package/node_modules/@maka/core/dist/models-dev-projection.js +0 -1
- package/node_modules/@maka/core/dist/provider-auth.js +9 -1
- package/node_modules/@maka/core/dist/provider-registry.js +7 -51
- package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +7 -0
- package/node_modules/@maka/core/dist/runtime-policy/model-catalog-entry-codec.js +14 -1
- package/node_modules/@maka/core/dist/settings.js +2 -3
- package/node_modules/@maka/core/dist/skill-locations.js +29 -0
- package/node_modules/@maka/core/package.json +2 -0
- package/node_modules/@maka/mcp/dist/index.js +22 -0
- package/node_modules/@maka/mcp/dist/oauth.js +4 -2
- package/node_modules/@maka/mcp/dist/tool-progress.js +45 -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/opencode-session-header.js +2 -3
- package/node_modules/@maka/runtime/dist/session-manager.js +1 -44
- package/node_modules/@maka/runtime/dist/skills-discovery.js +17 -23
- package/node_modules/@maka/runtime/dist/test-connection.js +3 -66
- package/node_modules/@maka/runtime/package.json +1 -0
- package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +44 -9
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +33 -3
- package/node_modules/@maka/runtime-host/dist/protocol/connection-effects.js +132 -1
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +13 -3
- package/node_modules/@maka/runtime-host/dist/protocol/message.js +10 -0
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -1
- package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +1 -1
- package/node_modules/@maka/runtime-host/dist/protocol/turn.js +0 -33
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/bootstrap-runtime-policy.js +31 -104
- 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 +4 -5
- package/node_modules/@maka/runtime-host/dist/server/interactive-turn-coordinator.js +1 -16
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +46 -28
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +5 -16
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +21 -12
- package/node_modules/@maka/runtime-host/dist/server/session-turn-access-request-coordinator.js +7 -17
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +26 -28
- package/node_modules/@maka/storage/dist/runtime-policy/connection-catalog-document.js +0 -62
- package/node_modules/@maka/storage/dist/runtime-policy/coordinator.js +34 -4
- package/node_modules/@maka/storage/dist/runtime-policy-stores.js +2 -1
- 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')
|
|
@@ -76,6 +76,13 @@ export function createMcpCapabilityProvider(manager) {
|
|
|
76
76
|
return projectMcpResult(await manager.callTool(binding, frame.arguments, {
|
|
77
77
|
signal: options.signal,
|
|
78
78
|
requestInteraction: options.requestInteraction,
|
|
79
|
+
...(options.progress
|
|
80
|
+
? {
|
|
81
|
+
onProgress: (current, total) => {
|
|
82
|
+
options.progress?.(current, total);
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
: {}),
|
|
79
86
|
}));
|
|
80
87
|
},
|
|
81
88
|
};
|
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();
|
|
@@ -50,7 +50,7 @@ export async function runRuntimeHostTui(input) {
|
|
|
50
50
|
throw error;
|
|
51
51
|
const configured = await runFirstRunOnboarding(input.clientDataRoot, input.workspaceRoot, input.cwd, input.locale, input.hostProfileId);
|
|
52
52
|
if (!configured)
|
|
53
|
-
|
|
53
|
+
return 0;
|
|
54
54
|
context = await createRuntimeHostTuiContext(contextInput);
|
|
55
55
|
}
|
|
56
56
|
let ownerAction;
|
|
Binary file
|
|
@@ -37,6 +37,9 @@ export function buildChatModelChoices(connections) {
|
|
|
37
37
|
...(provider.authKind === 'oauth_token' ? {} : { connectionName: connection.name }),
|
|
38
38
|
isDefault: entry.isDefault,
|
|
39
39
|
thinkingLevels: entry.thinkingLevels,
|
|
40
|
+
...(entry.defaultThinkingLevel === undefined
|
|
41
|
+
? {}
|
|
42
|
+
: { defaultThinkingLevel: entry.defaultThinkingLevel }),
|
|
40
43
|
supportsVision: entry.supportsVision,
|
|
41
44
|
...(entry.contextWindow !== undefined ? { contextWindow: entry.contextWindow } : {}),
|
|
42
45
|
...(declaredWindow !== undefined ? { declaredContextWindow: declaredWindow } : {}),
|
|
@@ -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
|
+
}
|
|
@@ -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
|
/**
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { CODEX_SUBSCRIPTION_UNSUPPORTED_CHATGPT_MODELS } from './codex-model-compatibility.js';
|
|
20
|
-
import { CATALOG_PROVIDER_TYPES,
|
|
20
|
+
import { CATALOG_PROVIDER_TYPES, PROVIDER_REGISTRY, RECOMMENDED_PROVIDER_TYPES, providerDefaultsOf, providerFallbackModelIds, providerMenuLabel, } from './provider-registry.js';
|
|
21
21
|
export { CODEX_SUBSCRIPTION_UNSUPPORTED_CHATGPT_MODELS };
|
|
22
|
-
export { CATALOG_PROVIDER_TYPES,
|
|
22
|
+
export { CATALOG_PROVIDER_TYPES, PROVIDER_REGISTRY, RECOMMENDED_PROVIDER_TYPES, providerDefaultsOf, providerFallbackModelIds, providerMenuLabel, };
|
|
23
23
|
export function isRelayProviderType(providerType) {
|
|
24
24
|
return providerType === 'openai-compatible' || providerType === 'openai-responses-compatible';
|
|
25
25
|
}
|
|
@@ -69,7 +69,7 @@ export function connectionEnabledModelIds(connection) {
|
|
|
69
69
|
* 3. the Host's entry says the connection can hold a chat on it.
|
|
70
70
|
*
|
|
71
71
|
* (3) already subsumes what clients used to re-derive locally: a retired
|
|
72
|
-
* provider
|
|
72
|
+
* provider and a model whose metadata says
|
|
73
73
|
* it cannot chat are all non-offerable before a client sees them. A client
|
|
74
74
|
* re-testing any of those against its OWN registry answers for a build that is
|
|
75
75
|
* not the one running the send.
|
|
@@ -135,12 +135,6 @@ export function authorizeConnectionModel(connection, modelId) {
|
|
|
135
135
|
const model = modelId.trim();
|
|
136
136
|
if (!model || !connectionEnabledModelIds(connection).includes(model))
|
|
137
137
|
return undefined;
|
|
138
|
-
// The one veto: quarantined ids fail in a shape the send cannot surface
|
|
139
|
-
// (e.g. a billed 200 with an empty completion), so the request settling it
|
|
140
|
-
// is not available as the arbiter. See ProviderDefaults.brokenModelIds.
|
|
141
|
-
if (providerDefaultsOf(connection.providerType)?.brokenModelIds?.includes(model)) {
|
|
142
|
-
return undefined;
|
|
143
|
-
}
|
|
144
138
|
// The observed row wins wherever it exists: it carries wire metadata such as
|
|
145
139
|
// `apiProtocol`, and capabilities, which a synthesized entry cannot. Absent
|
|
146
140
|
// capabilities already mean "unknown", not "unsupported".
|
|
@@ -268,18 +262,6 @@ export function reconcileConnectionAfterModelFetch(connection, models, options)
|
|
|
268
262
|
}),
|
|
269
263
|
};
|
|
270
264
|
}
|
|
271
|
-
/**
|
|
272
|
-
* The models a connection created without an explicit selection starts with,
|
|
273
|
-
* or undefined when the provider seeds nothing. Derived from the provider's
|
|
274
|
-
* shipped baseline rather than listed a second time: the two can then never
|
|
275
|
-
* disagree about what "all of them" means.
|
|
276
|
-
*/
|
|
277
|
-
export function defaultEnabledModelIdsWhenOmitted(providerType) {
|
|
278
|
-
const defaults = providerDefaultsOf(providerType);
|
|
279
|
-
if (!defaults?.enableShippedModelsByDefault)
|
|
280
|
-
return undefined;
|
|
281
|
-
return providerFallbackModelIds(defaults);
|
|
282
|
-
}
|
|
283
265
|
export function providerAuthRequiresSecret(providerType) {
|
|
284
266
|
const authKind = providerDefaultsOf(providerType)?.authKind;
|
|
285
267
|
return authKind === 'api_key' || authKind === 'oauth_token';
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
|
-
import { applyModelOverride, resolveModelLimits } from './model-thinking.js';
|
|
19
|
+
import { applyModelOverride, defaultThinkingLevelForConnection, resolveModelLimits, } from './model-thinking.js';
|
|
20
20
|
import { CODEX_SUBSCRIPTION_UNSUPPORTED_CHATGPT_MODELS, PROVIDER_REGISTRY, providerDefaultsOf, providerFallbackModelIds, providerSupportsModelDiscovery, } from './llm-connections.js';
|
|
21
21
|
import { lookupModelMetadata, resolveModelVisionSupport } from './model-metadata.js';
|
|
22
22
|
import { thinkingVariantsForConnection, } from './model-thinking.js';
|
|
@@ -95,36 +95,22 @@ export function buildConnectionModelCatalogEntries(input) {
|
|
|
95
95
|
if (!defaults)
|
|
96
96
|
return [];
|
|
97
97
|
const supportsModelDiscovery = providerSupportsModelDiscovery(connection.providerType);
|
|
98
|
-
// Quarantined ids never surface as offerable entries — from any source,
|
|
99
|
-
// including inventories stored or selections made before the quarantine —
|
|
100
|
-
// mirroring the `authorizeConnectionModel` veto.
|
|
101
|
-
const broken = new Set(defaults.brokenModelIds ?? []);
|
|
102
98
|
const fallbackModels = providerFallbackModelIds(defaults);
|
|
103
|
-
|
|
104
|
-
// re-enter the catalog either. `models` and `enabledModelIds` are filtered
|
|
105
|
-
// below, but a broken default reaches `makeMissingDefaultEntry` unfiltered and
|
|
106
|
-
// would be re-added as a selectable `provider_default` row — picker-visible
|
|
107
|
-
// and default-capable while `authorizeConnectionModel` vetoes the same id. A
|
|
108
|
-
// reachable persisted state: the id was picker-visible before the quarantine.
|
|
109
|
-
// Dropping it leaves the connection with no valid default (readiness reports
|
|
110
|
-
// `missing_model`), which is what a model that can no longer send warrants.
|
|
111
|
-
const defaultModel = broken.has((connection.defaultModel ?? '').trim())
|
|
112
|
-
? undefined
|
|
113
|
-
: connection.defaultModel;
|
|
99
|
+
const defaultModel = connection.defaultModel;
|
|
114
100
|
const fallbackModelIds = new Set(fallbackModels);
|
|
115
|
-
const projectedModelsById = new Map((connection.models ?? []).
|
|
101
|
+
const projectedModelsById = new Map((connection.models ?? []).map((model) => [model.id, model]));
|
|
116
102
|
// Fallback providers have no live inventory, but a projected connection can
|
|
117
103
|
// still carry enabled model-facts entries that are absent from the static
|
|
118
104
|
// list. Keep both sets in the catalog so those user-declared models retain
|
|
119
105
|
// their metadata.
|
|
120
106
|
const models = supportsModelDiscovery
|
|
121
|
-
? connection.models
|
|
107
|
+
? connection.models
|
|
122
108
|
: [
|
|
123
109
|
...fallbackModels.map((id) => projectedModelsById.get(id) ?? {
|
|
124
110
|
id,
|
|
125
111
|
...displayNameForKnownModel(connection.providerType, id),
|
|
126
112
|
}),
|
|
127
|
-
...(connection.models ?? []).filter((model) => !
|
|
113
|
+
...(connection.models ?? []).filter((model) => !fallbackModelIds.has(model.id)),
|
|
128
114
|
];
|
|
129
115
|
return buildModelCatalogEntries({
|
|
130
116
|
providerType: connection.providerType,
|
|
@@ -143,7 +129,7 @@ export function buildConnectionModelCatalogEntries(input) {
|
|
|
143
129
|
savedModelIds: [
|
|
144
130
|
...(connection.enabledModelIds ?? []),
|
|
145
131
|
...Object.keys(connection.modelOverrides ?? {}),
|
|
146
|
-
]
|
|
132
|
+
],
|
|
147
133
|
});
|
|
148
134
|
}
|
|
149
135
|
/**
|
|
@@ -239,6 +225,7 @@ function makeEntry(ctx, model, overrides = {}) {
|
|
|
239
225
|
capabilities,
|
|
240
226
|
...(modalities !== undefined ? { modalities } : {}),
|
|
241
227
|
});
|
|
228
|
+
const defaultThinkingLevel = defaultThinkingLevelForConnection(thinkingContext, normalizedModel.id);
|
|
242
229
|
return {
|
|
243
230
|
id: normalizedModel.id,
|
|
244
231
|
...displayNameForModel(input.providerType, normalizedModel),
|
|
@@ -253,6 +240,7 @@ function makeEntry(ctx, model, overrides = {}) {
|
|
|
253
240
|
? {}
|
|
254
241
|
: { compactionThreshold: input.modelOverrides[normalizedModel.id].compactionThreshold }),
|
|
255
242
|
thinkingLevels: thinkingVariantsForConnection(thinkingContext, normalizedModel.id),
|
|
243
|
+
...(defaultThinkingLevel === undefined ? {} : { defaultThinkingLevel }),
|
|
256
244
|
...limits,
|
|
257
245
|
...(defaults.contextWindow === undefined
|
|
258
246
|
? {}
|