@lapis-notes/ai 0.1.1 → 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 +25 -1
- 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 +20 -7
- package/registry-content/overview.md +10 -0
- package/registry.json +221 -0
|
@@ -69,6 +69,7 @@ export class AiChatController {
|
|
|
69
69
|
#onComposerDefaults;
|
|
70
70
|
#refreshSkills = false;
|
|
71
71
|
#activeRecallAbort;
|
|
72
|
+
#controllerContextReady = new WeakSet();
|
|
72
73
|
#pendingHandoffs = new WeakMap();
|
|
73
74
|
#approvalGrants = [];
|
|
74
75
|
#followedScope;
|
|
@@ -466,32 +467,90 @@ export class AiChatController {
|
|
|
466
467
|
return;
|
|
467
468
|
throw new Error("Agent session did not start.");
|
|
468
469
|
}
|
|
469
|
-
const
|
|
470
|
-
this
|
|
471
|
-
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
470
|
+
const session = this.session;
|
|
471
|
+
const sharedPreparation = this.runtime.capabilities().preparesContext === true;
|
|
472
|
+
const location = this.location ? { ...this.location } : null;
|
|
473
|
+
const bindingId = this.#activeBindingId;
|
|
474
|
+
const authorized = () => !this.#isAbandoned(turnId) &&
|
|
475
|
+
this.session === session &&
|
|
476
|
+
this.location?.conversationId === location?.conversationId;
|
|
477
|
+
const prepareContext = async (signal) => {
|
|
478
|
+
if (!authorized())
|
|
479
|
+
throw new Error("Conversation context is no longer active.");
|
|
480
|
+
const recallAbort = new AbortController();
|
|
481
|
+
const abort = () => recallAbort.abort();
|
|
482
|
+
if (signal.aborted)
|
|
483
|
+
abort();
|
|
484
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
485
|
+
this.#activeRecallAbort?.abort();
|
|
486
|
+
this.#activeRecallAbort = recallAbort;
|
|
487
|
+
try {
|
|
488
|
+
let handoff = this.#pendingHandoffs.get(session);
|
|
489
|
+
if (sharedPreparation && this.repository && location && bindingId) {
|
|
490
|
+
const current = await this.repository.read(location);
|
|
491
|
+
const bindings = reduceAgentBindings(current.agents);
|
|
492
|
+
const binding = bindings.find((item) => item.id === bindingId);
|
|
493
|
+
handoff = await buildConversationContextHandoff(current.transcript, {
|
|
494
|
+
conversationId: current.metadata.id,
|
|
495
|
+
targetBindingId: bindingId,
|
|
496
|
+
...(this.#controllerContextReady.has(session) &&
|
|
497
|
+
binding?.context
|
|
498
|
+
? {
|
|
499
|
+
after: {
|
|
500
|
+
entryId: binding.context.throughEntryId,
|
|
501
|
+
entryHash: binding.context.throughEntryHash,
|
|
502
|
+
},
|
|
503
|
+
}
|
|
504
|
+
: {}),
|
|
505
|
+
bindings,
|
|
506
|
+
summaries: current.agents.filter((record) => record.type === "handoff.summary.created"),
|
|
507
|
+
});
|
|
508
|
+
const context = this.#sessionContexts.get(session);
|
|
509
|
+
if (context) {
|
|
510
|
+
context.handoff = handoff;
|
|
511
|
+
if (context.pendingSwitch?.type === "agent.switch")
|
|
512
|
+
Object.assign(context.pendingSwitch, {
|
|
513
|
+
handoffId: handoff?.handoffId,
|
|
514
|
+
handoffMode: handoff?.mode,
|
|
515
|
+
handoffThroughEntryId: handoff?.throughEntryId,
|
|
516
|
+
omittedEntryCount: handoff?.omittedEntryCount,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const recalledBlocks = await this.#memoryRecall
|
|
521
|
+
?.recall(text, {
|
|
522
|
+
scopeDir: location?.scopeDir ?? this.directoryContext,
|
|
523
|
+
conversationId: location?.conversationId,
|
|
524
|
+
agentBindingId: bindingId,
|
|
525
|
+
runId: `turn-${turnId}`,
|
|
526
|
+
}, recallAbort.signal)
|
|
527
|
+
.catch(() => []);
|
|
528
|
+
if (recallAbort.signal.aborted || !authorized())
|
|
529
|
+
throw new Error("Conversation preparation was cancelled.");
|
|
530
|
+
this.#pendingHandoffs.delete(session);
|
|
531
|
+
return [
|
|
532
|
+
...(handoff ? [handoff.block] : []),
|
|
533
|
+
...(recalledBlocks ?? []),
|
|
534
|
+
];
|
|
535
|
+
}
|
|
536
|
+
finally {
|
|
537
|
+
signal.removeEventListener("abort", abort);
|
|
538
|
+
if (this.#activeRecallAbort === recallAbort)
|
|
539
|
+
this.#activeRecallAbort = undefined;
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
if (sharedPreparation) {
|
|
543
|
+
await session.send(text, {
|
|
544
|
+
prepareContext,
|
|
545
|
+
authorizeContext: async () => authorized(),
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
const blocks = await prepareContext(new AbortController().signal);
|
|
550
|
+
if (this.#isAbandoned(turnId))
|
|
551
|
+
return;
|
|
552
|
+
await session.send(text, { contextBlocks: blocks });
|
|
482
553
|
}
|
|
483
|
-
if (this.#isAbandoned(turnId))
|
|
484
|
-
return;
|
|
485
|
-
const handoff = this.#pendingHandoffs.get(this.session);
|
|
486
|
-
const contextBlocks = [
|
|
487
|
-
...(handoff ? [handoff.block] : []),
|
|
488
|
-
...(recalledBlocks ?? []),
|
|
489
|
-
];
|
|
490
|
-
await this.session.send(text, {
|
|
491
|
-
contextBlocks,
|
|
492
|
-
});
|
|
493
|
-
if (handoff)
|
|
494
|
-
this.#pendingHandoffs.delete(this.session);
|
|
495
554
|
if (!this.repository)
|
|
496
555
|
await this.#persist();
|
|
497
556
|
}
|
|
@@ -753,6 +812,8 @@ export class AiChatController {
|
|
|
753
812
|
};
|
|
754
813
|
try {
|
|
755
814
|
for await (const event of session.events()) {
|
|
815
|
+
if (event.type === "completed")
|
|
816
|
+
this.#controllerContextReady.add(session);
|
|
756
817
|
if ((event.type === "tool.start" || event.type === "tool.end") &&
|
|
757
818
|
event.server === APP_TOOL_MCP_SERVER_NAME) {
|
|
758
819
|
continue;
|
|
@@ -1402,10 +1463,10 @@ export class AiChatController {
|
|
|
1402
1463
|
}
|
|
1403
1464
|
const followedScope = this.#followedScope ?? this.directoryContext;
|
|
1404
1465
|
const input = this.#followedScope !== undefined || this.directoryContext
|
|
1405
|
-
?
|
|
1466
|
+
? this.#createConversation?.(followedScope) ?? {
|
|
1406
1467
|
scopeDir: followedScope,
|
|
1407
|
-
}
|
|
1408
|
-
:
|
|
1468
|
+
}
|
|
1469
|
+
: this.#createConversation?.() ?? { scopeDir: "" };
|
|
1409
1470
|
const created = await this.repository.create(input);
|
|
1410
1471
|
this.location = created.location;
|
|
1411
1472
|
this.conversationStatus = created.metadata.status;
|
|
@@ -1465,7 +1526,7 @@ export class AiChatController {
|
|
|
1465
1526
|
this.appToolsUnavailableReason =
|
|
1466
1527
|
descriptor.status === "runtime-unavailable" && runtime.id === "fake"
|
|
1467
1528
|
? null
|
|
1468
|
-
:
|
|
1529
|
+
: descriptor.unavailableReason ?? null;
|
|
1469
1530
|
return descriptor;
|
|
1470
1531
|
}
|
|
1471
1532
|
catch (error) {
|
|
@@ -1667,6 +1728,10 @@ export class AiChatController {
|
|
|
1667
1728
|
...request,
|
|
1668
1729
|
appToolSession,
|
|
1669
1730
|
skillSnapshot,
|
|
1731
|
+
metadata: {
|
|
1732
|
+
...request.metadata,
|
|
1733
|
+
...(await this.#sessionBootstrapMetadata(snapshot.location.scopeDir, snapshot.metadata.launchContext?.notePath, snapshot.metadata.id, appToolSession?.tools ?? [], skillSnapshot?.skills ?? [])),
|
|
1734
|
+
},
|
|
1670
1735
|
});
|
|
1671
1736
|
const configured = await this.#configureBinding(resumed, compatibleBinding, request);
|
|
1672
1737
|
if (!configured)
|
|
@@ -1,17 +1,7 @@
|
|
|
1
|
+
import { isOneLineToolAlert, presentToolPayload, toolCallTarget } from "@lapismd/design-core/ai/chat";
|
|
1
2
|
import type { AiChatItem } from "./chat-items";
|
|
2
3
|
export type AiChatToolItem = Extract<AiChatItem, {
|
|
3
4
|
type: "tool";
|
|
4
5
|
}>;
|
|
5
|
-
export
|
|
6
|
-
export type PresentedToolPayload = {
|
|
7
|
-
code: string;
|
|
8
|
-
language: ToolPayloadLanguage;
|
|
9
|
-
};
|
|
10
|
-
export type ToolPayloadHint = {
|
|
11
|
-
toolName?: string;
|
|
12
|
-
input?: string;
|
|
13
|
-
};
|
|
14
|
-
export declare function presentToolPayload(value?: string, hint?: ToolPayloadHint): PresentedToolPayload | undefined;
|
|
15
|
-
export declare function isOneLineAlert(payload: PresentedToolPayload): boolean;
|
|
16
|
-
export declare function toolCallTarget(input?: string, server?: string): string | undefined;
|
|
6
|
+
export { isOneLineToolAlert as isOneLineAlert, presentToolPayload, toolCallTarget, };
|
|
17
7
|
export declare function toolCallStatus(state: AiChatToolItem["state"]): "complete" | "error" | "running";
|
|
@@ -1,70 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
"path",
|
|
4
|
-
"file",
|
|
5
|
-
"query",
|
|
6
|
-
"url",
|
|
7
|
-
"pattern",
|
|
8
|
-
"target",
|
|
9
|
-
];
|
|
10
|
-
const PRIMARY_KEYS = [
|
|
11
|
-
"output",
|
|
12
|
-
"content",
|
|
13
|
-
"text",
|
|
14
|
-
"result",
|
|
15
|
-
"stdout",
|
|
16
|
-
"aggregated",
|
|
17
|
-
];
|
|
18
|
-
const METADATA_KEYS = new Set([
|
|
19
|
-
"ok",
|
|
20
|
-
"status",
|
|
21
|
-
"success",
|
|
22
|
-
"isError",
|
|
23
|
-
"server",
|
|
24
|
-
"tool",
|
|
25
|
-
"name",
|
|
26
|
-
]);
|
|
27
|
-
const STRUCTURAL_SIBLINGS = new Set([
|
|
28
|
-
"path",
|
|
29
|
-
"command",
|
|
30
|
-
"query",
|
|
31
|
-
"file",
|
|
32
|
-
"url",
|
|
33
|
-
"pattern",
|
|
34
|
-
"target",
|
|
35
|
-
"locations",
|
|
36
|
-
]);
|
|
37
|
-
const RUNNER_TOOL_NAMES = new Set([
|
|
38
|
-
"bash",
|
|
39
|
-
"sh",
|
|
40
|
-
"shell",
|
|
41
|
-
"exec",
|
|
42
|
-
"command",
|
|
43
|
-
"terminal",
|
|
44
|
-
"run",
|
|
45
|
-
]);
|
|
46
|
-
const MAX_PARSE_DEPTH = 3;
|
|
47
|
-
export function presentToolPayload(value, hint) {
|
|
48
|
-
if (value == null || value === "")
|
|
49
|
-
return undefined;
|
|
50
|
-
const extracted = extractDisplayValue(value, 0);
|
|
51
|
-
if (extracted == null || extracted === "")
|
|
52
|
-
return undefined;
|
|
53
|
-
if (isRecord(extracted) || Array.isArray(extracted)) {
|
|
54
|
-
return { code: JSON.stringify(extracted, null, 2), language: "json" };
|
|
55
|
-
}
|
|
56
|
-
const text = String(extracted);
|
|
57
|
-
if (!text)
|
|
58
|
-
return undefined;
|
|
59
|
-
return { code: text, language: detectTextLanguage(text, hint) };
|
|
60
|
-
}
|
|
61
|
-
export function isOneLineAlert(payload) {
|
|
62
|
-
return payload.language === "plaintext" && !payload.code.includes("\n");
|
|
63
|
-
}
|
|
64
|
-
export function toolCallTarget(input, server) {
|
|
65
|
-
const summary = summarizeToolInput(input);
|
|
66
|
-
return summary || server || undefined;
|
|
67
|
-
}
|
|
1
|
+
import { isOneLineToolAlert, presentToolPayload, toolCallTarget, } from "@lapismd/design-core/ai/chat";
|
|
2
|
+
export { isOneLineToolAlert as isOneLineAlert, presentToolPayload, toolCallTarget, };
|
|
68
3
|
export function toolCallStatus(state) {
|
|
69
4
|
if (state === "completed")
|
|
70
5
|
return "complete";
|
|
@@ -72,142 +7,3 @@ export function toolCallStatus(state) {
|
|
|
72
7
|
return "error";
|
|
73
8
|
return "running";
|
|
74
9
|
}
|
|
75
|
-
function extractDisplayValue(value, depth) {
|
|
76
|
-
const parsed = tryParseJson(value);
|
|
77
|
-
if (parsed === undefined)
|
|
78
|
-
return value;
|
|
79
|
-
if (typeof parsed === "string") {
|
|
80
|
-
if (depth + 1 >= MAX_PARSE_DEPTH)
|
|
81
|
-
return parsed;
|
|
82
|
-
return extractDisplayValue(parsed, depth + 1);
|
|
83
|
-
}
|
|
84
|
-
if (Array.isArray(parsed) || !isRecord(parsed))
|
|
85
|
-
return parsed;
|
|
86
|
-
const mcpText = mcpContentText(parsed);
|
|
87
|
-
if (mcpText != null)
|
|
88
|
-
return extractDisplayValue(mcpText, depth + 1);
|
|
89
|
-
const unwrapped = unwrapEnvelope(parsed);
|
|
90
|
-
if (unwrapped !== undefined)
|
|
91
|
-
return extractDisplayValue(unwrapped, depth + 1);
|
|
92
|
-
return parsed;
|
|
93
|
-
}
|
|
94
|
-
function tryParseJson(value) {
|
|
95
|
-
if (typeof value !== "string")
|
|
96
|
-
return value;
|
|
97
|
-
const trimmed = value.trim();
|
|
98
|
-
if (!trimmed)
|
|
99
|
-
return undefined;
|
|
100
|
-
if (!trimmed.startsWith("{") &&
|
|
101
|
-
!trimmed.startsWith("[") &&
|
|
102
|
-
!trimmed.startsWith('"')) {
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
|
105
|
-
try {
|
|
106
|
-
return JSON.parse(trimmed);
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
return undefined;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
function unwrapEnvelope(record) {
|
|
113
|
-
const keys = Object.keys(record);
|
|
114
|
-
const primary = PRIMARY_KEYS.find((key) => record[key] != null && record[key] !== "");
|
|
115
|
-
if (!primary)
|
|
116
|
-
return undefined;
|
|
117
|
-
const rest = keys.filter((key) => key !== primary);
|
|
118
|
-
if (rest.some((key) => !METADATA_KEYS.has(key)))
|
|
119
|
-
return undefined;
|
|
120
|
-
return record[primary];
|
|
121
|
-
}
|
|
122
|
-
function mcpContentText(record) {
|
|
123
|
-
if (!Array.isArray(record.content) || record.content.length === 0) {
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
const rest = Object.keys(record).filter((key) => key !== "content");
|
|
127
|
-
if (rest.some((key) => STRUCTURAL_SIBLINGS.has(key)))
|
|
128
|
-
return undefined;
|
|
129
|
-
if (rest.some((key) => !METADATA_KEYS.has(key)))
|
|
130
|
-
return undefined;
|
|
131
|
-
const parts = record.content.flatMap((entry) => {
|
|
132
|
-
if (typeof entry === "string" && entry)
|
|
133
|
-
return [entry];
|
|
134
|
-
if (isRecord(entry) && typeof entry.text === "string" && entry.text) {
|
|
135
|
-
return [entry.text];
|
|
136
|
-
}
|
|
137
|
-
return [];
|
|
138
|
-
});
|
|
139
|
-
return parts.length > 0 ? parts.join("\n") : undefined;
|
|
140
|
-
}
|
|
141
|
-
function detectTextLanguage(text, hint) {
|
|
142
|
-
if (isRunnerToolName(hint?.toolName))
|
|
143
|
-
return "bash";
|
|
144
|
-
if (hasCommandInput(hint?.input))
|
|
145
|
-
return "bash";
|
|
146
|
-
if (text.includes("\n"))
|
|
147
|
-
return "bash";
|
|
148
|
-
if (looksLikeCli(text))
|
|
149
|
-
return "bash";
|
|
150
|
-
return "plaintext";
|
|
151
|
-
}
|
|
152
|
-
function isRunnerToolName(name) {
|
|
153
|
-
if (!name)
|
|
154
|
-
return false;
|
|
155
|
-
const normalized = name.trim().toLowerCase();
|
|
156
|
-
if (RUNNER_TOOL_NAMES.has(normalized))
|
|
157
|
-
return true;
|
|
158
|
-
const leaf = normalized.split(/[:/]/u).at(-1);
|
|
159
|
-
return leaf != null && RUNNER_TOOL_NAMES.has(leaf);
|
|
160
|
-
}
|
|
161
|
-
function hasCommandInput(input) {
|
|
162
|
-
if (!input)
|
|
163
|
-
return false;
|
|
164
|
-
try {
|
|
165
|
-
const parsed = JSON.parse(input);
|
|
166
|
-
return (isRecord(parsed) &&
|
|
167
|
-
typeof parsed.command === "string" &&
|
|
168
|
-
parsed.command.trim() !== "");
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
function looksLikeCli(text) {
|
|
175
|
-
const trimmed = text.trim();
|
|
176
|
-
return (/^[$%] /u.test(trimmed) ||
|
|
177
|
-
/^error:/imu.test(trimmed) ||
|
|
178
|
-
/\b(?:stderr|exit \d+)\b/iu.test(trimmed) ||
|
|
179
|
-
/\|/.test(trimmed));
|
|
180
|
-
}
|
|
181
|
-
function summarizeToolInput(input) {
|
|
182
|
-
if (!input)
|
|
183
|
-
return undefined;
|
|
184
|
-
try {
|
|
185
|
-
const parsed = JSON.parse(input);
|
|
186
|
-
if (typeof parsed === "string" && parsed.trim())
|
|
187
|
-
return parsed.trim();
|
|
188
|
-
if (!isRecord(parsed))
|
|
189
|
-
return undefined;
|
|
190
|
-
for (const key of SUMMARY_KEYS) {
|
|
191
|
-
const value = parsed[key];
|
|
192
|
-
if (typeof value === "string" && value.trim())
|
|
193
|
-
return value.trim();
|
|
194
|
-
}
|
|
195
|
-
const locations = parsed.locations;
|
|
196
|
-
if (Array.isArray(locations)) {
|
|
197
|
-
const first = locations[0];
|
|
198
|
-
if (isRecord(first) && typeof first.path === "string" && first.path) {
|
|
199
|
-
return first.path;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
catch {
|
|
204
|
-
const line = input.trim().split("\n")[0] ?? "";
|
|
205
|
-
if (!line)
|
|
206
|
-
return undefined;
|
|
207
|
-
return line.length > 80 ? `${line.slice(0, 77)}…` : line;
|
|
208
|
-
}
|
|
209
|
-
return undefined;
|
|
210
|
-
}
|
|
211
|
-
function isRecord(value) {
|
|
212
|
-
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
213
|
-
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ModelRef } from "../core/types";
|
|
2
|
+
import type { AcpAgentId } from "../settings/acp-agents";
|
|
3
|
+
import type { AiPluginSettings } from "../settings/ai-settings";
|
|
4
|
+
export type ComposerModelCatalogState = {
|
|
5
|
+
agent: AcpAgentId;
|
|
6
|
+
runtime: AiPluginSettings["defaultRuntime"];
|
|
7
|
+
models: ModelRef[];
|
|
8
|
+
model: string;
|
|
9
|
+
loading: boolean;
|
|
10
|
+
error: string | null;
|
|
11
|
+
};
|
|
12
|
+
/** Each selection owns its result; a previous harness cannot overwrite it. */
|
|
13
|
+
export declare class ComposerModelCatalog {
|
|
14
|
+
#private;
|
|
15
|
+
readonly load: (agent: AcpAgentId, runtime: AiPluginSettings["defaultRuntime"]) => Promise<ModelRef[]>;
|
|
16
|
+
readonly update: (state: ComposerModelCatalogState) => void;
|
|
17
|
+
constructor(load: (agent: AcpAgentId, runtime: AiPluginSettings["defaultRuntime"]) => Promise<ModelRef[]>, update: (state: ComposerModelCatalogState) => void);
|
|
18
|
+
refresh(agent: AcpAgentId, runtime: AiPluginSettings["defaultRuntime"], saved: string): Promise<void>;
|
|
19
|
+
cancel(): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Each selection owns its result; a previous harness cannot overwrite it. */
|
|
2
|
+
export class ComposerModelCatalog {
|
|
3
|
+
load;
|
|
4
|
+
update;
|
|
5
|
+
#generation = 0;
|
|
6
|
+
constructor(load, update) {
|
|
7
|
+
this.load = load;
|
|
8
|
+
this.update = update;
|
|
9
|
+
}
|
|
10
|
+
async refresh(agent, runtime, saved) {
|
|
11
|
+
const generation = ++this.#generation;
|
|
12
|
+
const state = {
|
|
13
|
+
agent,
|
|
14
|
+
runtime,
|
|
15
|
+
models: [],
|
|
16
|
+
model: saved,
|
|
17
|
+
loading: true,
|
|
18
|
+
error: null,
|
|
19
|
+
};
|
|
20
|
+
this.update(state);
|
|
21
|
+
try {
|
|
22
|
+
const models = (await this.load(agent, runtime)).filter((model) => model.provider === agent || !model.provider);
|
|
23
|
+
if (generation !== this.#generation)
|
|
24
|
+
return;
|
|
25
|
+
const model = models.some((entry) => entry.model === saved)
|
|
26
|
+
? saved
|
|
27
|
+
: ((models.find((entry) => entry.isDefault) ?? models[0])?.model ??
|
|
28
|
+
saved);
|
|
29
|
+
this.update({ ...state, models, model, loading: false });
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (generation !== this.#generation)
|
|
33
|
+
return;
|
|
34
|
+
this.update({
|
|
35
|
+
...state,
|
|
36
|
+
loading: false,
|
|
37
|
+
error: error instanceof Error ? error.message : String(error),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
cancel() {
|
|
42
|
+
this.#generation++;
|
|
43
|
+
}
|
|
44
|
+
}
|
package/dist/commands/agent.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AcpAgentId } from "../settings/acp-agents";
|
|
2
2
|
export type ComposerAgentSelection = {
|
|
3
3
|
agent: AcpAgentId;
|
|
4
|
-
runtimePreference: "acp" | "codex-native" | "fake";
|
|
4
|
+
runtimePreference: "acp" | "codex-native" | "opencode-native" | "fake";
|
|
5
5
|
label: string;
|
|
6
6
|
};
|
|
7
7
|
export declare function composerAgentLabel(agent?: string, runtimePreference?: string): string;
|
package/dist/commands/agent.js
CHANGED
|
@@ -21,6 +21,9 @@ const KNOWN = {
|
|
|
21
21
|
runtimePreference: "codex-native",
|
|
22
22
|
label: "Codex Native",
|
|
23
23
|
},
|
|
24
|
+
opencode: { agent: "opencode", runtimePreference: "acp", label: "OpenCode ACP" },
|
|
25
|
+
"opencode-acp": { agent: "opencode", runtimePreference: "acp", label: "OpenCode ACP" },
|
|
26
|
+
"opencode-native": { agent: "opencode", runtimePreference: "opencode-native", label: "OpenCode Native" },
|
|
24
27
|
fake: { agent: "codex", runtimePreference: "fake", label: "Fake" },
|
|
25
28
|
};
|
|
26
29
|
export function composerAgentLabel(agent, runtimePreference) {
|
|
@@ -28,7 +31,9 @@ export function composerAgentLabel(agent, runtimePreference) {
|
|
|
28
31
|
return "Fake";
|
|
29
32
|
if (runtimePreference === "codex-native")
|
|
30
33
|
return "Codex Native";
|
|
31
|
-
|
|
34
|
+
if (runtimePreference === "opencode-native")
|
|
35
|
+
return "OpenCode Native";
|
|
36
|
+
return agent === "cursor" ? "Cursor ACP" : agent === "opencode" ? "OpenCode ACP" : "Codex ACP";
|
|
32
37
|
}
|
|
33
38
|
export function parseAgentCommand(raw) {
|
|
34
39
|
const name = raw.trim().toLowerCase();
|
|
@@ -1,25 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { AgentBindingCreatedRecord, HandoffSummaryCreatedRecord, HandoffProjectionMode, TranscriptEntry } from "./types";
|
|
3
|
-
export declare const MAX_CONTEXT_HANDOFF_TOKENS = 12000;
|
|
4
|
-
export declare const MAX_CONTEXT_HANDOFF_CODE_POINTS: number;
|
|
5
|
-
export type ConversationContextCursor = {
|
|
6
|
-
entryId: string;
|
|
7
|
-
entryHash: string;
|
|
8
|
-
};
|
|
9
|
-
export type ConversationContextHandoff = {
|
|
10
|
-
block: ConversationHandoffContextBlock;
|
|
11
|
-
throughEntryId: string;
|
|
12
|
-
throughEntryHash: string;
|
|
13
|
-
handoffId: string;
|
|
14
|
-
mode: HandoffProjectionMode;
|
|
15
|
-
omittedEntryCount: number;
|
|
16
|
-
};
|
|
17
|
-
export type ConversationContextHandoffOptions = {
|
|
18
|
-
conversationId?: string;
|
|
19
|
-
targetBindingId?: string;
|
|
20
|
-
after?: ConversationContextCursor;
|
|
21
|
-
bindings?: readonly AgentBindingCreatedRecord[];
|
|
22
|
-
summaries?: readonly HandoffSummaryCreatedRecord[];
|
|
23
|
-
maxTokens?: number;
|
|
24
|
-
};
|
|
25
|
-
export declare function buildConversationContextHandoff(transcript: readonly TranscriptEntry[], options?: ConversationContextHandoffOptions | number): Promise<ConversationContextHandoff | undefined>;
|
|
1
|
+
export { buildConversationContextHandoff, MAX_CONTEXT_HANDOFF_TOKENS, MAX_CONTEXT_HANDOFF_CODE_POINTS, type ConversationContextCursor, type ConversationContextHandoff, type ConversationContextHandoffOptions, } from "@lapismd/ai-controller/memory/lapis";
|