@lapis-notes/ai 0.1.2 → 0.1.5
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/CHANGELOG.md +24 -0
- package/LICENSE.md +3 -7
- package/README.md +1 -1
- package/dist/ai-plugin.d.ts +2 -2
- package/dist/ai-plugin.js +28 -11
- package/dist/catalog/ai-catalog-panel.svelte +20 -3
- package/dist/chat/ai-chat-panel.svelte +187 -87
- package/dist/chat/ai-chat-panel.svelte.d.ts +2 -0
- package/dist/chat/ai-view-bootstrap.d.ts +1 -0
- package/dist/chat/ai-view-bootstrap.js +32 -12
- package/dist/chat/ai-view-panel.svelte +14 -0
- package/dist/chat/ai-view.d.ts +1 -1
- package/dist/chat/chat-controller.svelte.d.ts +1 -1
- package/dist/chat/chat-controller.svelte.js +94 -29
- package/dist/chat/chat-tool-display.d.ts +2 -12
- package/dist/chat/chat-tool-display.js +2 -206
- package/dist/chat/composer-model-catalog.d.ts +20 -0
- package/dist/chat/composer-model-catalog.js +44 -0
- package/dist/commands/agent.d.ts +1 -1
- package/dist/commands/agent.js +6 -1
- package/dist/conversations/context-handoff.d.ts +1 -25
- package/dist/conversations/context-handoff.js +1 -207
- package/dist/conversations/hashes.d.ts +1 -5
- package/dist/conversations/hashes.js +1 -40
- package/dist/core/types.d.ts +1 -241
- package/dist/core/types.js +1 -50
- package/dist/history/ai-history-panel.svelte +4 -1
- package/dist/host/controller-connection.d.ts +14 -0
- package/dist/host/controller-connection.js +62 -0
- package/dist/host/controller-runtime.d.ts +13 -0
- package/dist/host/controller-runtime.js +112 -0
- package/dist/host/create-host-runtimes.d.ts +2 -1
- package/dist/host/create-host-runtimes.js +5 -7
- package/dist/index.d.ts +1 -1
- package/dist/memory/candidate-extractor.d.ts +2 -25
- package/dist/memory/candidate-extractor.js +1 -191
- package/dist/memory/hashes.d.ts +1 -5
- package/dist/memory/hashes.js +1 -37
- package/dist/memory/memory-record-store.d.ts +3 -48
- package/dist/memory/memory-record-store.js +16 -220
- package/dist/memory/memory-scheduler.d.ts +1 -21
- package/dist/memory/memory-scheduler.js +1 -143
- package/dist/memory/native-memory-service.d.ts +1 -72
- package/dist/memory/native-memory-service.js +1 -1256
- package/dist/memory/paths.d.ts +1 -13
- package/dist/memory/paths.js +1 -60
- package/dist/memory/runtime-consolidation-provider.d.ts +2 -18
- package/dist/memory/runtime-consolidation-provider.js +1 -154
- package/dist/memory/types.d.ts +1 -139
- package/dist/providers/acp-model-provider.d.ts +8 -3
- package/dist/providers/acp-model-provider.js +21 -49
- package/dist/providers/model-provider.d.ts +3 -2
- package/dist/providers/model-provider.js +15 -2
- package/dist/runtime-adapters.d.ts +3 -0
- package/dist/runtime-adapters.js +2 -0
- package/dist/settings/acp-agents.d.ts +4 -1
- package/dist/settings/acp-agents.js +5 -1
- package/dist/settings/ai-settings-tab.js +8 -6
- package/dist/settings/ai-settings.d.ts +4 -4
- package/dist/settings/ai-settings.js +5 -3
- package/dist/settings/register-ai-settings.js +27 -14
- package/dist/tools/app-tool-host.d.ts +22 -2
- package/dist/tools/app-tool-host.js +51 -4
- package/dist/tools/desktop-app-tool-bridge.d.ts +4 -1
- package/dist/tools/desktop-app-tool-bridge.js +94 -157
- package/manifest.json +20 -2
- package/package.json +17 -7
- package/registry-content/overview.md +10 -0
- package/registry.json +211 -2
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { onMount, untrack } from "svelte";
|
|
2
|
+
import { onDestroy, onMount, untrack } from "svelte";
|
|
3
3
|
import type { App, WorkspaceLeaf } from "@lapis-notes/api";
|
|
4
4
|
import { MarkdownEmbed } from "@lapis-notes/markdown/embed";
|
|
5
5
|
import * as Chat from "@lapismd/design-core/ai/chat";
|
|
6
6
|
import { Reasoning } from "@lapismd/design-core/ai/experimental";
|
|
7
7
|
import { Button } from "@lapismd/design-core/shadcn/button";
|
|
8
|
-
import { CodeBlock } from "@lapismd/design-core/shadcn/code-block";
|
|
9
8
|
import * as CommandView from "@lapismd/design-core/shadcn/command-view";
|
|
10
9
|
import * as DropdownMenu from "@lapismd/design-core/shadcn/dropdown-menu";
|
|
11
10
|
import * as Empty from "@lapismd/design-core/shadcn/empty";
|
|
@@ -92,6 +91,11 @@
|
|
|
92
91
|
import type { AutomaticMemoryRecall } from "../memory/types";
|
|
93
92
|
import type { HandoffSummaryCoordinator } from "../conversations/handoff-summary";
|
|
94
93
|
|
|
94
|
+
import {
|
|
95
|
+
ComposerModelCatalog,
|
|
96
|
+
type ComposerModelCatalogState,
|
|
97
|
+
} from "./composer-model-catalog";
|
|
98
|
+
|
|
95
99
|
let {
|
|
96
100
|
app,
|
|
97
101
|
runtime,
|
|
@@ -122,6 +126,7 @@
|
|
|
122
126
|
fileSearch,
|
|
123
127
|
models = [],
|
|
124
128
|
modelCatalogError = null,
|
|
129
|
+
loadModels,
|
|
125
130
|
settings,
|
|
126
131
|
onSettingsChange,
|
|
127
132
|
}: {
|
|
@@ -158,6 +163,10 @@
|
|
|
158
163
|
fileSearch?: ComposerSearchSource;
|
|
159
164
|
models?: ModelRef[];
|
|
160
165
|
modelCatalogError?: string | null;
|
|
166
|
+
loadModels?: (
|
|
167
|
+
agent: AcpAgentId,
|
|
168
|
+
runtime: AiPluginSettings["defaultRuntime"],
|
|
169
|
+
) => Promise<ModelRef[]>;
|
|
161
170
|
settings?: Partial<AiPluginSettings>;
|
|
162
171
|
onSettingsChange?: (
|
|
163
172
|
patch: Partial<AiPluginSettings>,
|
|
@@ -196,7 +205,9 @@
|
|
|
196
205
|
skillSnapshots,
|
|
197
206
|
slashRouter,
|
|
198
207
|
appToolHost,
|
|
199
|
-
memoryRecall: settings?.memoryAutomaticRecall
|
|
208
|
+
memoryRecall: settings?.memoryAutomaticRecall
|
|
209
|
+
? memoryRecall
|
|
210
|
+
: undefined,
|
|
200
211
|
handoffSummaries,
|
|
201
212
|
skillContext,
|
|
202
213
|
readVaultText: app
|
|
@@ -207,18 +218,52 @@
|
|
|
207
218
|
: undefined,
|
|
208
219
|
onComposerDefaults: ({ agent, runtimePreference }) => {
|
|
209
220
|
if (runtimePreference === "fake") {
|
|
221
|
+
catalog.cancel();
|
|
222
|
+
catalogState = {
|
|
223
|
+
agent,
|
|
224
|
+
runtime: "fake",
|
|
225
|
+
models: [],
|
|
226
|
+
model: localModel ?? "",
|
|
227
|
+
loading: false,
|
|
228
|
+
error: null,
|
|
229
|
+
};
|
|
210
230
|
localAgent = agent;
|
|
211
231
|
localRuntime = "fake";
|
|
212
|
-
persistComposerDefaults(
|
|
232
|
+
persistComposerDefaults(
|
|
233
|
+
agent,
|
|
234
|
+
"fake",
|
|
235
|
+
localModel,
|
|
236
|
+
selectedThinking,
|
|
237
|
+
);
|
|
213
238
|
return;
|
|
214
239
|
}
|
|
215
240
|
changeAgent(agent, runtimePreference);
|
|
216
241
|
},
|
|
217
242
|
}),
|
|
218
243
|
);
|
|
244
|
+
let catalogState = $state<ComposerModelCatalogState | null>(null);
|
|
245
|
+
const catalog = new ComposerModelCatalog(
|
|
246
|
+
(agent, runtime) => loadModels?.(agent, runtime) ?? Promise.resolve([]),
|
|
247
|
+
(state) => {
|
|
248
|
+
catalogState = state;
|
|
249
|
+
if (!state.loading && !state.error) {
|
|
250
|
+
localModel = state.model;
|
|
251
|
+
persistComposerDefaults(
|
|
252
|
+
state.agent,
|
|
253
|
+
state.runtime === "auto" ? "acp" : state.runtime,
|
|
254
|
+
state.model,
|
|
255
|
+
selectedThinking,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
);
|
|
260
|
+
onDestroy(() => catalog.cancel());
|
|
261
|
+
const catalogModels = $derived(catalogState?.models ?? models);
|
|
219
262
|
let draft = $state("");
|
|
220
263
|
let localAgent = $state<AcpAgentId | null>(null);
|
|
221
|
-
let localRuntime = $state<
|
|
264
|
+
let localRuntime = $state<
|
|
265
|
+
"acp" | "codex-native" | "opencode-native" | "fake" | null
|
|
266
|
+
>(null);
|
|
222
267
|
let localModel = $state<string | null>(null);
|
|
223
268
|
let localThinking = $state<AiThinkingLevel | null>(null);
|
|
224
269
|
let attachments = $state<{ path: string; name: string }[]>([]);
|
|
@@ -243,9 +288,11 @@
|
|
|
243
288
|
localRuntime ??
|
|
244
289
|
(settings?.defaultRuntime === "codex-native"
|
|
245
290
|
? "codex-native"
|
|
246
|
-
: settings?.defaultRuntime === "
|
|
247
|
-
? "
|
|
248
|
-
: "
|
|
291
|
+
: settings?.defaultRuntime === "opencode-native"
|
|
292
|
+
? "opencode-native"
|
|
293
|
+
: settings?.defaultRuntime === "fake"
|
|
294
|
+
? "fake"
|
|
295
|
+
: "acp"),
|
|
249
296
|
);
|
|
250
297
|
const selectedModel = $derived(
|
|
251
298
|
localModel ?? settings?.defaultModel ?? DEFAULT_AI_SETTINGS.defaultModel,
|
|
@@ -253,8 +300,63 @@
|
|
|
253
300
|
const selectedThinking = $derived(
|
|
254
301
|
localThinking ?? settings?.thinking ?? DEFAULT_AI_SETTINGS.thinking,
|
|
255
302
|
);
|
|
303
|
+
// Settings notifications also reach a mounted composer. Own menu updates
|
|
304
|
+
// already match the local harness and must not trigger a second discovery.
|
|
305
|
+
let observedDefaults: {
|
|
306
|
+
agent: AcpAgentId;
|
|
307
|
+
runtime: typeof selectedRuntime;
|
|
308
|
+
model: string;
|
|
309
|
+
thinking: AiThinkingLevel;
|
|
310
|
+
} | null = null;
|
|
311
|
+
$effect(() => {
|
|
312
|
+
if (initializing) return;
|
|
313
|
+
const next = {
|
|
314
|
+
agent: normalizeAcpAgent(settings?.acpAgent),
|
|
315
|
+
runtime:
|
|
316
|
+
settings?.defaultRuntime === "codex-native"
|
|
317
|
+
? ("codex-native" as const)
|
|
318
|
+
: settings?.defaultRuntime === "opencode-native"
|
|
319
|
+
? ("opencode-native" as const)
|
|
320
|
+
: settings?.defaultRuntime === "fake"
|
|
321
|
+
? ("fake" as const)
|
|
322
|
+
: ("acp" as const),
|
|
323
|
+
model: settings?.defaultModel ?? DEFAULT_AI_SETTINGS.defaultModel,
|
|
324
|
+
thinking: settings?.thinking ?? DEFAULT_AI_SETTINGS.thinking,
|
|
325
|
+
};
|
|
326
|
+
untrack(() => {
|
|
327
|
+
const previous = observedDefaults;
|
|
328
|
+
observedDefaults = next;
|
|
329
|
+
if (!previous) return;
|
|
330
|
+
const harnessChanged =
|
|
331
|
+
previous.agent !== next.agent || previous.runtime !== next.runtime;
|
|
332
|
+
if (
|
|
333
|
+
harnessChanged &&
|
|
334
|
+
((localAgent ?? previous.agent) !== next.agent ||
|
|
335
|
+
(localRuntime ?? previous.runtime) !== next.runtime)
|
|
336
|
+
) {
|
|
337
|
+
localAgent = next.agent;
|
|
338
|
+
localRuntime = next.runtime;
|
|
339
|
+
localModel = next.model;
|
|
340
|
+
catalog.cancel();
|
|
341
|
+
if (next.runtime === "fake") {
|
|
342
|
+
catalogState = {
|
|
343
|
+
...next,
|
|
344
|
+
runtime: "fake",
|
|
345
|
+
models: [],
|
|
346
|
+
loading: false,
|
|
347
|
+
error: null,
|
|
348
|
+
};
|
|
349
|
+
} else if (loadModels) {
|
|
350
|
+
void catalog.refresh(next.agent, next.runtime, next.model);
|
|
351
|
+
}
|
|
352
|
+
} else if (previous.model !== next.model) {
|
|
353
|
+
localModel = next.model;
|
|
354
|
+
}
|
|
355
|
+
if (previous.thinking !== next.thinking) localThinking = next.thinking;
|
|
356
|
+
});
|
|
357
|
+
});
|
|
256
358
|
const modelOptions = $derived.by<ModelRef[]>(() => {
|
|
257
|
-
const available = catalogModelsForAgent(selectedAgent,
|
|
359
|
+
const available = catalogModelsForAgent(selectedAgent, catalogModels);
|
|
258
360
|
if (
|
|
259
361
|
selectedModel &&
|
|
260
362
|
!available.some((model) => model.model === selectedModel)
|
|
@@ -299,21 +401,26 @@
|
|
|
299
401
|
}
|
|
300
402
|
return triggers;
|
|
301
403
|
});
|
|
302
|
-
const agentLabels = $derived.by(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
404
|
+
const agentLabels = $derived.by(
|
|
405
|
+
() =>
|
|
406
|
+
new Map(
|
|
407
|
+
controller.bindings.map((binding) => {
|
|
408
|
+
const label =
|
|
409
|
+
binding.runtime === "codex-native"
|
|
410
|
+
? "Codex Native"
|
|
411
|
+
: binding.runtime === "opencode-native"
|
|
412
|
+
? "OpenCode Native"
|
|
413
|
+
: binding.agent === "cursor"
|
|
414
|
+
? "Cursor ACP"
|
|
415
|
+
: binding.agent === "opencode"
|
|
416
|
+
? "OpenCode ACP"
|
|
417
|
+
: "Codex ACP";
|
|
418
|
+
return [
|
|
419
|
+
binding.id,
|
|
420
|
+
binding.model?.model ? `${label} · ${binding.model.model}` : label,
|
|
421
|
+
] as const;
|
|
422
|
+
}),
|
|
423
|
+
),
|
|
317
424
|
);
|
|
318
425
|
const timeline = $derived(
|
|
319
426
|
groupChatItemsByDate(controller.items, new Date(), agentLabels),
|
|
@@ -340,8 +447,9 @@
|
|
|
340
447
|
);
|
|
341
448
|
const composerError = $derived(
|
|
342
449
|
controller.error ??
|
|
343
|
-
|
|
344
|
-
|
|
450
|
+
(catalogState
|
|
451
|
+
? catalogState.error
|
|
452
|
+
: (modelCatalogError ?? unavailableReason)) ??
|
|
345
453
|
controller.appToolsUnavailableReason,
|
|
346
454
|
);
|
|
347
455
|
const composerStatus = $derived<ComposerStatus | undefined>(
|
|
@@ -379,8 +487,8 @@
|
|
|
379
487
|
});
|
|
380
488
|
|
|
381
489
|
async function submit(prompt: string): Promise<void> {
|
|
382
|
-
if (initializing) return;
|
|
383
|
-
const selected = catalogModelsForAgent(selectedAgent,
|
|
490
|
+
if (initializing || catalogState?.loading) return;
|
|
491
|
+
const selected = catalogModelsForAgent(selectedAgent, catalogModels).find(
|
|
384
492
|
(model) => model.model === selectedModel,
|
|
385
493
|
);
|
|
386
494
|
const extra = attachments.map((file) => file.path);
|
|
@@ -406,7 +514,7 @@
|
|
|
406
514
|
|
|
407
515
|
function persistComposerDefaults(
|
|
408
516
|
agent: AcpAgentId,
|
|
409
|
-
runtimePreference: "acp" | "codex-native" | "fake",
|
|
517
|
+
runtimePreference: "acp" | "codex-native" | "opencode-native" | "fake",
|
|
410
518
|
model: string | null,
|
|
411
519
|
thinking: AiThinkingLevel,
|
|
412
520
|
): void {
|
|
@@ -420,7 +528,7 @@
|
|
|
420
528
|
|
|
421
529
|
function changeAgent(
|
|
422
530
|
agent: AcpAgentId,
|
|
423
|
-
runtimePreference: "acp" | "codex-native",
|
|
531
|
+
runtimePreference: "acp" | "codex-native" | "opencode-native",
|
|
424
532
|
): void {
|
|
425
533
|
localAgent = agent;
|
|
426
534
|
localRuntime = runtimePreference;
|
|
@@ -430,13 +538,19 @@
|
|
|
430
538
|
catalogModelsForAgent(agent, models).find((model) => model.isDefault)
|
|
431
539
|
?.model ||
|
|
432
540
|
catalogModelsForAgent(agent, models)[0]?.model ||
|
|
433
|
-
|
|
541
|
+
"";
|
|
434
542
|
persistComposerDefaults(
|
|
435
543
|
agent,
|
|
436
544
|
runtimePreference,
|
|
437
545
|
localModel,
|
|
438
546
|
selectedThinking,
|
|
439
547
|
);
|
|
548
|
+
if (loadModels)
|
|
549
|
+
void catalog.refresh(
|
|
550
|
+
agent,
|
|
551
|
+
runtimePreference,
|
|
552
|
+
configured ?? localModel ?? "",
|
|
553
|
+
);
|
|
440
554
|
}
|
|
441
555
|
|
|
442
556
|
function changeModel(value: string): void {
|
|
@@ -549,7 +663,9 @@
|
|
|
549
663
|
function toolCallProps(item: AiChatToolItem) {
|
|
550
664
|
const hint = { toolName: item.name, input: item.input };
|
|
551
665
|
const presentedError =
|
|
552
|
-
item.state === "error"
|
|
666
|
+
item.state === "error"
|
|
667
|
+
? presentToolPayload(item.output, hint)
|
|
668
|
+
: undefined;
|
|
553
669
|
return {
|
|
554
670
|
id: item.toolId,
|
|
555
671
|
name: item.name,
|
|
@@ -689,18 +805,8 @@
|
|
|
689
805
|
{@const detail = call.data as
|
|
690
806
|
| { input?: string; output?: string; error?: string; name?: string }
|
|
691
807
|
| undefined}
|
|
692
|
-
{@const hint = {
|
|
693
|
-
toolName: detail?.name ?? call.name,
|
|
694
|
-
input: detail?.input,
|
|
695
|
-
}}
|
|
696
808
|
{@const ResultView = resolveToolResultView(app, detail?.name ?? call.name)}
|
|
697
809
|
{@const parsedOutput = parseToolResultPayload(detail?.output)}
|
|
698
|
-
{@const inputPayload = presentToolPayload(detail?.input)}
|
|
699
|
-
{@const outputPayload = presentToolPayload(detail?.output, hint)}
|
|
700
|
-
{@const errorPayload = presentToolPayload(detail?.error, hint)}
|
|
701
|
-
{@const showErrorBlock = Boolean(
|
|
702
|
-
errorPayload && !isOneLineAlert(errorPayload),
|
|
703
|
-
)}
|
|
704
810
|
{#if ResultView && app && parsedOutput != null && !detail?.error}
|
|
705
811
|
<ResultView
|
|
706
812
|
{app}
|
|
@@ -711,41 +817,12 @@
|
|
|
711
817
|
state="completed"
|
|
712
818
|
/>
|
|
713
819
|
{:else}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
size="sm"
|
|
721
|
-
width="full"
|
|
722
|
-
isWrapped
|
|
723
|
-
maxHeight="16rem"
|
|
724
|
-
/>
|
|
725
|
-
{/if}
|
|
726
|
-
{#if outputPayload}
|
|
727
|
-
<CodeBlock
|
|
728
|
-
code={outputPayload.code}
|
|
729
|
-
language={outputPayload.language}
|
|
730
|
-
title="Output"
|
|
731
|
-
size="sm"
|
|
732
|
-
width="full"
|
|
733
|
-
isWrapped
|
|
734
|
-
maxHeight="16rem"
|
|
735
|
-
/>
|
|
736
|
-
{/if}
|
|
737
|
-
{#if showErrorBlock && errorPayload}
|
|
738
|
-
<CodeBlock
|
|
739
|
-
code={errorPayload.code}
|
|
740
|
-
language={errorPayload.language}
|
|
741
|
-
title="Error"
|
|
742
|
-
size="sm"
|
|
743
|
-
width="full"
|
|
744
|
-
isWrapped
|
|
745
|
-
maxHeight="16rem"
|
|
746
|
-
/>
|
|
747
|
-
{/if}
|
|
748
|
-
</div>
|
|
820
|
+
<Chat.ToolCallDetail
|
|
821
|
+
input={detail?.input}
|
|
822
|
+
output={detail?.output}
|
|
823
|
+
error={detail?.error}
|
|
824
|
+
toolName={detail?.name ?? call.name}
|
|
825
|
+
/>
|
|
749
826
|
{/if}
|
|
750
827
|
{/snippet}
|
|
751
828
|
|
|
@@ -754,6 +831,7 @@
|
|
|
754
831
|
data-ui-component="ai-chat-panel"
|
|
755
832
|
data-testid="ai-chat-panel"
|
|
756
833
|
data-initializing={initializing}
|
|
834
|
+
data-model-catalog-loading={catalogState?.loading ?? false}
|
|
757
835
|
>
|
|
758
836
|
<Chat.Layout density="compact" {isEmpty} aria-label="AI chat">
|
|
759
837
|
{#snippet emptyState()}
|
|
@@ -786,7 +864,9 @@
|
|
|
786
864
|
<Chat.Composer
|
|
787
865
|
bind:value={draft}
|
|
788
866
|
placeholder="Ask anything…"
|
|
789
|
-
disabled={initializing ||
|
|
867
|
+
disabled={initializing ||
|
|
868
|
+
Boolean(catalogState?.loading) ||
|
|
869
|
+
controller.busy}
|
|
790
870
|
interactiveDrawerWhenDisabled={Boolean(pendingInteraction)}
|
|
791
871
|
isStopShown={controller.busy}
|
|
792
872
|
status={composerStatus}
|
|
@@ -890,7 +970,9 @@
|
|
|
890
970
|
value={`${item.label} ${item.id}`}
|
|
891
971
|
onSelect={() => addAttachment(item)}
|
|
892
972
|
>
|
|
893
|
-
<CommandView.ItemLabel
|
|
973
|
+
<CommandView.ItemLabel
|
|
974
|
+
>{item.label}</CommandView.ItemLabel
|
|
975
|
+
>
|
|
894
976
|
</CommandView.Item>
|
|
895
977
|
{/each}
|
|
896
978
|
</CommandView.List>
|
|
@@ -1029,15 +1111,29 @@
|
|
|
1029
1111
|
<DropdownMenu.RadioItem
|
|
1030
1112
|
value="acp:codex"
|
|
1031
1113
|
onclick={() => changeAgent("codex", "acp")}
|
|
1032
|
-
|
|
1114
|
+
>Codex ACP</DropdownMenu.RadioItem
|
|
1115
|
+
>
|
|
1033
1116
|
<DropdownMenu.RadioItem
|
|
1034
1117
|
value="acp:cursor"
|
|
1035
1118
|
onclick={() => changeAgent("cursor", "acp")}
|
|
1036
|
-
|
|
1119
|
+
>Cursor ACP</DropdownMenu.RadioItem
|
|
1120
|
+
>
|
|
1037
1121
|
<DropdownMenu.RadioItem
|
|
1038
1122
|
value="codex-native:codex"
|
|
1039
1123
|
onclick={() => changeAgent("codex", "codex-native")}
|
|
1040
|
-
|
|
1124
|
+
>Codex Native</DropdownMenu.RadioItem
|
|
1125
|
+
>
|
|
1126
|
+
<DropdownMenu.RadioItem
|
|
1127
|
+
value="acp:opencode"
|
|
1128
|
+
onclick={() => changeAgent("opencode", "acp")}
|
|
1129
|
+
>OpenCode ACP</DropdownMenu.RadioItem
|
|
1130
|
+
>
|
|
1131
|
+
<DropdownMenu.RadioItem
|
|
1132
|
+
value="opencode-native:opencode"
|
|
1133
|
+
onclick={() =>
|
|
1134
|
+
changeAgent("opencode", "opencode-native")}
|
|
1135
|
+
>OpenCode Native</DropdownMenu.RadioItem
|
|
1136
|
+
>
|
|
1041
1137
|
</DropdownMenu.RadioGroup>
|
|
1042
1138
|
</DropdownMenu.SubContent>
|
|
1043
1139
|
</DropdownMenu.Sub>
|
|
@@ -1047,7 +1143,13 @@
|
|
|
1047
1143
|
Model
|
|
1048
1144
|
</DropdownMenu.SubTrigger>
|
|
1049
1145
|
<DropdownMenu.SubContent>
|
|
1050
|
-
{#if
|
|
1146
|
+
{#if catalogState?.loading}
|
|
1147
|
+
<DropdownMenu.Group
|
|
1148
|
+
><DropdownMenu.Item disabled
|
|
1149
|
+
>Loading models…</DropdownMenu.Item
|
|
1150
|
+
></DropdownMenu.Group
|
|
1151
|
+
>
|
|
1152
|
+
{:else if modelOptions.length > 0}
|
|
1051
1153
|
<DropdownMenu.RadioGroup value={selectedModel}>
|
|
1052
1154
|
{#each modelOptions as option (option.model)}
|
|
1053
1155
|
<DropdownMenu.RadioItem
|
|
@@ -1154,8 +1256,7 @@
|
|
|
1154
1256
|
<Chat.ToolCalls
|
|
1155
1257
|
calls={entry.items.map((item) => ({
|
|
1156
1258
|
...toolCallProps(item),
|
|
1157
|
-
detail:
|
|
1158
|
-
item.input || item.output ? toolDetail : undefined,
|
|
1259
|
+
detail: item.input || item.output ? toolDetail : undefined,
|
|
1159
1260
|
}))}
|
|
1160
1261
|
defaultExpanded={false}
|
|
1161
1262
|
/>
|
|
@@ -1255,8 +1356,7 @@
|
|
|
1255
1356
|
</Chat.MessageMetadata>
|
|
1256
1357
|
{/snippet}
|
|
1257
1358
|
</Chat.Message>
|
|
1258
|
-
{:else if entry.item.type === "status" &&
|
|
1259
|
-
entry.item.layout === "inventory"}
|
|
1359
|
+
{:else if entry.item.type === "status" && entry.item.layout === "inventory"}
|
|
1260
1360
|
<div
|
|
1261
1361
|
class="ai-chat-panel__command-notice"
|
|
1262
1362
|
data-testid="ai-chat-command-notice"
|
|
@@ -4,6 +4,7 @@ import type { AgentRequest, AgentRuntime, ModelRef, McpServerContribution } from
|
|
|
4
4
|
import type { AgentSessionStore } from "../sessions/session-store";
|
|
5
5
|
import type { ConversationRepository, CreateConversationInput } from "../conversations/conversation-repository";
|
|
6
6
|
import type { ConversationLocation } from "../conversations/types";
|
|
7
|
+
import { type AcpAgentId } from "../settings/acp-agents";
|
|
7
8
|
import { type AiPluginSettings } from "../settings/ai-settings";
|
|
8
9
|
import type { AppToolBridgeCoordinator } from "../tools/desktop-app-tool-bridge";
|
|
9
10
|
import type { SkillRegistry, SkillSnapshotStore } from "../skills/registry";
|
|
@@ -42,6 +43,7 @@ type $$ComponentProps = {
|
|
|
42
43
|
fileSearch?: ComposerSearchSource;
|
|
43
44
|
models?: ModelRef[];
|
|
44
45
|
modelCatalogError?: string | null;
|
|
46
|
+
loadModels?: (agent: AcpAgentId, runtime: AiPluginSettings["defaultRuntime"]) => Promise<ModelRef[]>;
|
|
45
47
|
settings?: Partial<AiPluginSettings>;
|
|
46
48
|
onSettingsChange?: (patch: Partial<AiPluginSettings>) => void | Promise<void>;
|
|
47
49
|
};
|
|
@@ -11,3 +11,4 @@ export type AiViewBootstrap = {
|
|
|
11
11
|
};
|
|
12
12
|
export declare function initialAiViewBootstrap(host: AiViewHost): AiViewBootstrap;
|
|
13
13
|
export declare function prepareAiViewBootstrap(host: AiViewHost, initialLocation: ConversationLocation | null, mcpServers: McpServerContribution[]): Promise<AiViewBootstrap>;
|
|
14
|
+
export declare function applyAiViewSettingsPatch(settings: AiPluginSettings, patch: Partial<AiPluginSettings>, current: AiPluginSettings): AiPluginSettings;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ACP_AGENT_IDS } from "../settings/acp-agents";
|
|
2
1
|
export function initialAiViewBootstrap(host) {
|
|
3
2
|
return {
|
|
4
3
|
runtime: host.fallbackRuntime(),
|
|
@@ -13,32 +12,36 @@ export async function prepareAiViewBootstrap(host, initialLocation, mcpServers)
|
|
|
13
12
|
const conversation = initialLocation
|
|
14
13
|
? host.conversations.read(initialLocation)
|
|
15
14
|
: Promise.resolve(null);
|
|
16
|
-
const
|
|
17
|
-
const [snapshot, settledCatalogs] = await Promise.all([
|
|
18
|
-
conversation.catch(() => null),
|
|
19
|
-
Promise.allSettled(catalogs),
|
|
20
|
-
]);
|
|
15
|
+
const snapshot = await conversation.catch(() => null);
|
|
21
16
|
if (snapshot) {
|
|
22
17
|
const binding = snapshot.agents.find((record) => record.type === "binding.created" &&
|
|
23
18
|
record.id === snapshot.metadata.activeAgentBindingId);
|
|
24
19
|
if (binding?.type === "binding.created") {
|
|
25
|
-
const agent = binding.agent === "cursor"
|
|
20
|
+
const agent = binding.agent === "cursor"
|
|
21
|
+
? "cursor"
|
|
22
|
+
: binding.agent === "opencode"
|
|
23
|
+
? "opencode"
|
|
24
|
+
: "codex";
|
|
26
25
|
settings = {
|
|
27
26
|
...settings,
|
|
28
27
|
acpAgent: agent,
|
|
29
28
|
defaultRuntime: binding.runtime === "codex-native"
|
|
30
29
|
? "codex-native"
|
|
31
|
-
: binding.runtime === "
|
|
32
|
-
? "
|
|
33
|
-
: "
|
|
30
|
+
: binding.runtime === "opencode-native"
|
|
31
|
+
? "opencode-native"
|
|
32
|
+
: binding.runtime === "fake"
|
|
33
|
+
? "fake"
|
|
34
|
+
: "acp",
|
|
34
35
|
defaultModel: binding.model?.model ?? settings.defaultModels[agent],
|
|
35
36
|
thinking: binding.thinking ?? settings.thinking,
|
|
36
37
|
};
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
|
-
const
|
|
40
|
+
const [selectedCatalog] = await Promise.allSettled([
|
|
41
|
+
host.models.listModels(settings.acpAgent, settings.defaultRuntime),
|
|
42
|
+
]);
|
|
43
|
+
const models = selectedCatalog?.status === "fulfilled" ? selectedCatalog.value : [];
|
|
40
44
|
let modelCatalogError = null;
|
|
41
|
-
const selectedCatalog = settledCatalogs[ACP_AGENT_IDS.indexOf(settings.acpAgent)];
|
|
42
45
|
const agentModels = models.filter((model) => model.provider === settings.acpAgent);
|
|
43
46
|
if (selectedCatalog?.status === "rejected") {
|
|
44
47
|
modelCatalogError = errorMessage(selectedCatalog.reason);
|
|
@@ -56,6 +59,7 @@ export async function prepareAiViewBootstrap(host, initialLocation, mcpServers)
|
|
|
56
59
|
try {
|
|
57
60
|
runtime = await host.selectRuntime({
|
|
58
61
|
prompt: "",
|
|
62
|
+
workspace: host.workspace,
|
|
59
63
|
mcpServers,
|
|
60
64
|
agent: settings.acpAgent,
|
|
61
65
|
model: { provider: settings.acpAgent, model: settings.defaultModel },
|
|
@@ -84,3 +88,19 @@ export async function prepareAiViewBootstrap(host, initialLocation, mcpServers)
|
|
|
84
88
|
function errorMessage(error) {
|
|
85
89
|
return error instanceof Error ? error.message : String(error);
|
|
86
90
|
}
|
|
91
|
+
export function applyAiViewSettingsPatch(settings, patch, current) {
|
|
92
|
+
const acpAgent = patch.acpAgent ?? settings.acpAgent;
|
|
93
|
+
const defaultModels = { ...settings.defaultModels, ...patch.defaultModels };
|
|
94
|
+
if (patch.defaultModel !== undefined)
|
|
95
|
+
defaultModels[acpAgent] = patch.defaultModel;
|
|
96
|
+
return {
|
|
97
|
+
...settings,
|
|
98
|
+
...patch,
|
|
99
|
+
acpAgent,
|
|
100
|
+
defaultModels,
|
|
101
|
+
defaultModel: patch.defaultModel ??
|
|
102
|
+
(patch.acpAgent !== undefined
|
|
103
|
+
? current.defaultModels[acpAgent]
|
|
104
|
+
: (patch.defaultModels?.[acpAgent] ?? settings.defaultModel)),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type { AiViewHost } from "./ai-view";
|
|
7
7
|
import {
|
|
8
8
|
initialAiViewBootstrap,
|
|
9
|
+
applyAiViewSettingsPatch,
|
|
9
10
|
prepareAiViewBootstrap,
|
|
10
11
|
type AiViewBootstrap,
|
|
11
12
|
} from "./ai-view-bootstrap";
|
|
@@ -34,6 +35,17 @@
|
|
|
34
35
|
|
|
35
36
|
onMount(() => {
|
|
36
37
|
let disposed = false;
|
|
38
|
+
const unsubscribe = host.subscribeSettings?.((patch) => {
|
|
39
|
+
if (disposed || !preparedBootstrap) return;
|
|
40
|
+
preparedBootstrap = {
|
|
41
|
+
...preparedBootstrap,
|
|
42
|
+
settings: applyAiViewSettingsPatch(
|
|
43
|
+
preparedBootstrap.settings,
|
|
44
|
+
patch,
|
|
45
|
+
host.getSettings(),
|
|
46
|
+
),
|
|
47
|
+
};
|
|
48
|
+
});
|
|
37
49
|
void prepareAiViewBootstrap(host, initialLocation, mcpServers)
|
|
38
50
|
.then((prepared) => {
|
|
39
51
|
if (disposed) return;
|
|
@@ -51,6 +63,7 @@
|
|
|
51
63
|
});
|
|
52
64
|
return () => {
|
|
53
65
|
disposed = true;
|
|
66
|
+
unsubscribe?.();
|
|
54
67
|
};
|
|
55
68
|
});
|
|
56
69
|
</script>
|
|
@@ -83,6 +96,7 @@
|
|
|
83
96
|
{workspaceLeaf}
|
|
84
97
|
fileSearch={host.searchVaultFiles}
|
|
85
98
|
models={bootstrap.models}
|
|
99
|
+
loadModels={(agent, runtime) => host.models.listModels(agent, runtime)}
|
|
86
100
|
modelCatalogError={bootstrap.modelCatalogError}
|
|
87
101
|
settings={bootstrap.settings}
|
|
88
102
|
onSettingsChange={(patch) => host.updateSettings(patch)}
|
package/dist/chat/ai-view.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export type AiViewHost = {
|
|
|
31
31
|
updateSettings(patch: Partial<AiPluginSettings>): Promise<void>;
|
|
32
32
|
subscribeSettings?(listener: (patch: Partial<AiPluginSettings>) => void): () => void;
|
|
33
33
|
models: {
|
|
34
|
-
listModels(provider: string): Promise<ModelRef[]>;
|
|
34
|
+
listModels(provider: string, runtime?: string): Promise<ModelRef[]>;
|
|
35
35
|
};
|
|
36
36
|
workspace?: string;
|
|
37
37
|
};
|
|
@@ -57,7 +57,7 @@ export declare class AiChatController {
|
|
|
57
57
|
handoffSummaries?: Pick<HandoffSummaryCoordinator, "afterTerminal">;
|
|
58
58
|
onComposerDefaults?: (next: {
|
|
59
59
|
agent: AcpAgentId;
|
|
60
|
-
runtimePreference: "acp" | "codex-native" | "fake";
|
|
60
|
+
runtimePreference: "acp" | "codex-native" | "opencode-native" | "fake";
|
|
61
61
|
}) => void;
|
|
62
62
|
});
|
|
63
63
|
restore(): Promise<void>;
|