@lapis-notes/ai 0.1.0
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 +6 -0
- package/LICENSE.md +8 -0
- package/README.md +20 -0
- package/dist/ai-plugin.d.ts +88 -0
- package/dist/ai-plugin.js +742 -0
- package/dist/bootstrap/build.d.ts +15 -0
- package/dist/bootstrap/build.js +28 -0
- package/dist/bootstrap/folder-instructions.d.ts +4 -0
- package/dist/bootstrap/folder-instructions.js +49 -0
- package/dist/bootstrap/format.d.ts +2 -0
- package/dist/bootstrap/format.js +46 -0
- package/dist/bootstrap/instructions.d.ts +1 -0
- package/dist/bootstrap/instructions.js +8 -0
- package/dist/bootstrap/types.d.ts +36 -0
- package/dist/bootstrap/types.js +3 -0
- package/dist/catalog/ai-catalog-panel.svelte +742 -0
- package/dist/catalog/ai-catalog-panel.svelte.d.ts +13 -0
- package/dist/catalog/ai-catalog-view-type.d.ts +1 -0
- package/dist/catalog/ai-catalog-view-type.js +1 -0
- package/dist/catalog/ai-catalog-view.d.ts +22 -0
- package/dist/catalog/ai-catalog-view.js +48 -0
- package/dist/catalog/filter.d.ts +3 -0
- package/dist/catalog/filter.js +24 -0
- package/dist/catalog/inventory.d.ts +21 -0
- package/dist/catalog/inventory.js +255 -0
- package/dist/catalog/tree.d.ts +12 -0
- package/dist/catalog/tree.js +60 -0
- package/dist/catalog/types.d.ts +44 -0
- package/dist/catalog/types.js +1 -0
- package/dist/chat/ai-approval-card.svelte +54 -0
- package/dist/chat/ai-approval-card.svelte.d.ts +9 -0
- package/dist/chat/ai-chat-panel.svelte +1288 -0
- package/dist/chat/ai-chat-panel.svelte.d.ts +50 -0
- package/dist/chat/ai-inventory-result.css +55 -0
- package/dist/chat/ai-inventory-result.svelte +63 -0
- package/dist/chat/ai-inventory-result.svelte.d.ts +5 -0
- package/dist/chat/ai-question-card.svelte +132 -0
- package/dist/chat/ai-question-card.svelte.d.ts +9 -0
- package/dist/chat/ai-view-bootstrap.d.ts +13 -0
- package/dist/chat/ai-view-bootstrap.js +86 -0
- package/dist/chat/ai-view-panel.svelte +89 -0
- package/dist/chat/ai-view-panel.svelte.d.ts +13 -0
- package/dist/chat/ai-view-type.d.ts +1 -0
- package/dist/chat/ai-view-type.js +1 -0
- package/dist/chat/ai-view.d.ts +60 -0
- package/dist/chat/ai-view.js +121 -0
- package/dist/chat/chat-controller.svelte.d.ts +82 -0
- package/dist/chat/chat-controller.svelte.js +1926 -0
- package/dist/chat/chat-items.d.ts +88 -0
- package/dist/chat/chat-items.js +9 -0
- package/dist/chat/chat-mentions.d.ts +15 -0
- package/dist/chat/chat-mentions.js +77 -0
- package/dist/chat/chat-result-views.d.ts +5 -0
- package/dist/chat/chat-result-views.js +15 -0
- package/dist/chat/chat-session.d.ts +28 -0
- package/dist/chat/chat-session.js +50 -0
- package/dist/chat/chat-status.d.ts +1 -0
- package/dist/chat/chat-status.js +3 -0
- package/dist/chat/chat-time.d.ts +18 -0
- package/dist/chat/chat-time.js +90 -0
- package/dist/chat/chat-tool-display.d.ts +17 -0
- package/dist/chat/chat-tool-display.js +213 -0
- package/dist/chat/chat-tool-identity.d.ts +4 -0
- package/dist/chat/chat-tool-identity.js +42 -0
- package/dist/chat/chat-trace.d.ts +6 -0
- package/dist/chat/chat-trace.js +256 -0
- package/dist/chat/live-runtime-unavailable.d.ts +1 -0
- package/dist/chat/live-runtime-unavailable.js +1 -0
- package/dist/commands/agent.d.ts +8 -0
- package/dist/commands/agent.js +38 -0
- package/dist/commands/catalog.d.ts +18 -0
- package/dist/commands/catalog.js +115 -0
- package/dist/commands/discovery.d.ts +36 -0
- package/dist/commands/discovery.js +151 -0
- package/dist/commands/groups.d.ts +16 -0
- package/dist/commands/groups.js +121 -0
- package/dist/commands/inspect.d.ts +18 -0
- package/dist/commands/inspect.js +29 -0
- package/dist/commands/markdown.d.ts +21 -0
- package/dist/commands/markdown.js +117 -0
- package/dist/commands/parser.d.ts +4 -0
- package/dist/commands/parser.js +24 -0
- package/dist/commands/reserved.d.ts +4 -0
- package/dist/commands/reserved.js +107 -0
- package/dist/commands/router.d.ts +17 -0
- package/dist/commands/router.js +170 -0
- package/dist/commands/seed.d.ts +4 -0
- package/dist/commands/seed.js +8 -0
- package/dist/commands/types.d.ts +54 -0
- package/dist/commands/types.js +1 -0
- package/dist/commands/user-agents-node.d.ts +3 -0
- package/dist/commands/user-agents-node.js +52 -0
- package/dist/commands/user-agents.d.ts +22 -0
- package/dist/commands/user-agents.js +40 -0
- package/dist/conversations/approval-grants.d.ts +20 -0
- package/dist/conversations/approval-grants.js +75 -0
- package/dist/conversations/binding-state.d.ts +6 -0
- package/dist/conversations/binding-state.js +35 -0
- package/dist/conversations/context-handoff.d.ts +25 -0
- package/dist/conversations/context-handoff.js +207 -0
- package/dist/conversations/conversation-index-coordinator.d.ts +18 -0
- package/dist/conversations/conversation-index-coordinator.js +81 -0
- package/dist/conversations/conversation-index.d.ts +16 -0
- package/dist/conversations/conversation-index.js +125 -0
- package/dist/conversations/conversation-locator.d.ts +2 -0
- package/dist/conversations/conversation-locator.js +20 -0
- package/dist/conversations/conversation-palette.d.ts +18 -0
- package/dist/conversations/conversation-palette.js +29 -0
- package/dist/conversations/conversation-repository.d.ts +39 -0
- package/dist/conversations/conversation-repository.js +213 -0
- package/dist/conversations/handoff-summary.d.ts +48 -0
- package/dist/conversations/handoff-summary.js +269 -0
- package/dist/conversations/hashes.d.ts +5 -0
- package/dist/conversations/hashes.js +40 -0
- package/dist/conversations/history-creation-scope.d.ts +12 -0
- package/dist/conversations/history-creation-scope.js +44 -0
- package/dist/conversations/jsonl.d.ts +10 -0
- package/dist/conversations/jsonl.js +41 -0
- package/dist/conversations/memory-transcript-store.d.ts +15 -0
- package/dist/conversations/memory-transcript-store.js +119 -0
- package/dist/conversations/paths.d.ts +14 -0
- package/dist/conversations/paths.js +92 -0
- package/dist/conversations/redaction.d.ts +13 -0
- package/dist/conversations/redaction.js +94 -0
- package/dist/conversations/reveal-scope.d.ts +3 -0
- package/dist/conversations/reveal-scope.js +8 -0
- package/dist/conversations/scope-resolver.d.ts +11 -0
- package/dist/conversations/scope-resolver.js +18 -0
- package/dist/conversations/scope-tree.d.ts +10 -0
- package/dist/conversations/scope-tree.js +71 -0
- package/dist/conversations/transcript-projection.d.ts +9 -0
- package/dist/conversations/transcript-projection.js +409 -0
- package/dist/conversations/transcript-store.d.ts +24 -0
- package/dist/conversations/transcript-store.js +27 -0
- package/dist/conversations/types.d.ts +224 -0
- package/dist/conversations/types.js +12 -0
- package/dist/conversations/validation.d.ts +4 -0
- package/dist/conversations/validation.js +383 -0
- package/dist/conversations/vault-transcript-store.d.ts +22 -0
- package/dist/conversations/vault-transcript-store.js +219 -0
- package/dist/core/event-queue.d.ts +6 -0
- package/dist/core/event-queue.js +39 -0
- package/dist/core/types.d.ts +241 -0
- package/dist/core/types.js +50 -0
- package/dist/history/ai-history-panel.svelte +937 -0
- package/dist/history/ai-history-panel.svelte.d.ts +17 -0
- package/dist/history/ai-history-view-type.d.ts +1 -0
- package/dist/history/ai-history-view-type.js +1 -0
- package/dist/history/ai-history-view.d.ts +26 -0
- package/dist/history/ai-history-view.js +51 -0
- package/dist/history/conversation-history-tree.d.ts +15 -0
- package/dist/history/conversation-history-tree.js +71 -0
- package/dist/host/agent-workspace.d.ts +6 -0
- package/dist/host/agent-workspace.js +16 -0
- package/dist/host/create-host-runtimes.d.ts +2 -0
- package/dist/host/create-host-runtimes.js +14 -0
- package/dist/host/desktop-process-host.d.ts +6 -0
- package/dist/host/desktop-process-host.js +74 -0
- package/dist/host/host-runtime-availability.d.ts +1 -0
- package/dist/host/host-runtime-availability.js +3 -0
- package/dist/host/process-host.d.ts +31 -0
- package/dist/host/process-host.js +6 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +49 -0
- package/dist/jsonl/ai-jsonl-preview.d.ts +27 -0
- package/dist/jsonl/ai-jsonl-preview.js +68 -0
- package/dist/jsonl/ai-jsonl-view-type.d.ts +1 -0
- package/dist/jsonl/ai-jsonl-view-type.js +1 -0
- package/dist/jsonl/ai-jsonl-view.d.ts +16 -0
- package/dist/jsonl/ai-jsonl-view.js +57 -0
- package/dist/jsonl/ai-jsonl-view.svelte +260 -0
- package/dist/jsonl/ai-jsonl-view.svelte.d.ts +9 -0
- package/dist/memory/candidate-extractor.d.ts +25 -0
- package/dist/memory/candidate-extractor.js +191 -0
- package/dist/memory/forget-confirmation.d.ts +3 -0
- package/dist/memory/forget-confirmation.js +45 -0
- package/dist/memory/hashes.d.ts +5 -0
- package/dist/memory/hashes.js +37 -0
- package/dist/memory/memory-ingestion-coordinator.d.ts +19 -0
- package/dist/memory/memory-ingestion-coordinator.js +108 -0
- package/dist/memory/memory-record-store.d.ts +62 -0
- package/dist/memory/memory-record-store.js +369 -0
- package/dist/memory/memory-scheduler.d.ts +21 -0
- package/dist/memory/memory-scheduler.js +143 -0
- package/dist/memory/memory-tools.d.ts +61 -0
- package/dist/memory/memory-tools.js +99 -0
- package/dist/memory/native-memory-service.d.ts +72 -0
- package/dist/memory/native-memory-service.js +1256 -0
- package/dist/memory/paths.d.ts +13 -0
- package/dist/memory/paths.js +60 -0
- package/dist/memory/runtime-consolidation-provider.d.ts +18 -0
- package/dist/memory/runtime-consolidation-provider.js +154 -0
- package/dist/memory/types.d.ts +139 -0
- package/dist/memory/types.js +1 -0
- package/dist/providers/acp-model-provider.d.ts +11 -0
- package/dist/providers/acp-model-provider.js +99 -0
- package/dist/providers/codex-model-catalog.d.ts +7 -0
- package/dist/providers/codex-model-catalog.js +179 -0
- package/dist/providers/codex-model-provider.d.ts +12 -0
- package/dist/providers/codex-model-provider.js +42 -0
- package/dist/providers/model-provider.d.ts +26 -0
- package/dist/providers/model-provider.js +26 -0
- package/dist/public-components.d.ts +3 -0
- package/dist/public-components.js +3 -0
- package/dist/registry/runtime-registry.d.ts +11 -0
- package/dist/registry/runtime-registry.js +71 -0
- package/dist/registry/select-runtime.d.ts +9 -0
- package/dist/registry/select-runtime.js +31 -0
- package/dist/runtime-adapters.d.ts +16 -0
- package/dist/runtime-adapters.js +9 -0
- package/dist/runtimes/acp/acp-event-mapper.d.ts +56 -0
- package/dist/runtimes/acp/acp-event-mapper.js +237 -0
- package/dist/runtimes/acp/acp-runtime.d.ts +46 -0
- package/dist/runtimes/acp/acp-runtime.js +165 -0
- package/dist/runtimes/acp/desktop-acp-backend.d.ts +16 -0
- package/dist/runtimes/acp/desktop-acp-backend.js +243 -0
- package/dist/runtimes/codex/app-server-protocol.d.ts +28 -0
- package/dist/runtimes/codex/app-server-protocol.js +274 -0
- package/dist/runtimes/codex/codex-runtime.d.ts +24 -0
- package/dist/runtimes/codex/codex-runtime.js +318 -0
- package/dist/runtimes/fake/fake-runtime.d.ts +51 -0
- package/dist/runtimes/fake/fake-runtime.js +257 -0
- package/dist/sessions/plugin-data.d.ts +8 -0
- package/dist/sessions/plugin-data.js +29 -0
- package/dist/sessions/session-store.d.ts +47 -0
- package/dist/sessions/session-store.js +117 -0
- package/dist/settings/acp-agents.d.ts +8 -0
- package/dist/settings/acp-agents.js +11 -0
- package/dist/settings/ai-settings-tab.d.ts +8 -0
- package/dist/settings/ai-settings-tab.js +249 -0
- package/dist/settings/ai-settings.d.ts +43 -0
- package/dist/settings/ai-settings.js +147 -0
- package/dist/settings/app-tool-setting-rows.d.ts +16 -0
- package/dist/settings/app-tool-setting-rows.js +30 -0
- package/dist/settings/register-ai-settings.d.ts +3 -0
- package/dist/settings/register-ai-settings.js +393 -0
- package/dist/skills/activation.d.ts +2 -0
- package/dist/skills/activation.js +20 -0
- package/dist/skills/bundled/lapis-notes.d.ts +3 -0
- package/dist/skills/bundled/lapis-notes.js +52 -0
- package/dist/skills/bundled/research.d.ts +6 -0
- package/dist/skills/bundled/research.js +29 -0
- package/dist/skills/discovery.d.ts +21 -0
- package/dist/skills/discovery.js +227 -0
- package/dist/skills/manifest.d.ts +3 -0
- package/dist/skills/manifest.js +39 -0
- package/dist/skills/parser.d.ts +12 -0
- package/dist/skills/parser.js +124 -0
- package/dist/skills/registry.d.ts +21 -0
- package/dist/skills/registry.js +89 -0
- package/dist/skills/seed.d.ts +5 -0
- package/dist/skills/seed.js +18 -0
- package/dist/skills/skill-tools.d.ts +8 -0
- package/dist/skills/skill-tools.js +113 -0
- package/dist/skills/types.d.ts +67 -0
- package/dist/skills/types.js +1 -0
- package/dist/styles.css +514 -0
- package/dist/tools/app-tool-host.d.ts +47 -0
- package/dist/tools/app-tool-host.js +419 -0
- package/dist/tools/desktop-app-tool-bridge.d.ts +23 -0
- package/dist/tools/desktop-app-tool-bridge.js +223 -0
- package/dist/tools/mcp-server-registry.d.ts +7 -0
- package/dist/tools/mcp-server-registry.js +30 -0
- package/manifest.json +34 -0
- package/package.json +83 -0
- package/styles.css +1 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MemoryScope } from "./types";
|
|
2
|
+
export declare const AI_MEMORY_EPISODIC_PROVIDER_ID = "ai-memory-episodic";
|
|
3
|
+
export declare const AI_MEMORY_CURATED_PROVIDER_ID = "ai-memory-curated";
|
|
4
|
+
export declare const AI_MEMORY_PATH_PREFIX = "ai-memory";
|
|
5
|
+
export declare function decodeMemoryPathSegment(value: string): string;
|
|
6
|
+
export declare function conversationMemoryScope(scopeDir: string): MemoryScope;
|
|
7
|
+
export declare function memoryScopeKey(scope: MemoryScope): string;
|
|
8
|
+
export declare function episodicScopePrefix(scope: MemoryScope): string;
|
|
9
|
+
export declare function episodicMemoryPath(scope: MemoryScope, conversationId: string, entryId: string): string;
|
|
10
|
+
export declare function curatedScopePrefix(scope: MemoryScope): string;
|
|
11
|
+
export declare function curatedMemoryPath(scope: MemoryScope, memoryId: string): string;
|
|
12
|
+
export declare function currentMemoryScopes(scopeDir: string): MemoryScope[];
|
|
13
|
+
export declare function scopeProximity(scope: MemoryScope, currentScopeDir: string): number;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const AI_MEMORY_EPISODIC_PROVIDER_ID = "ai-memory-episodic";
|
|
2
|
+
export const AI_MEMORY_CURATED_PROVIDER_ID = "ai-memory-curated";
|
|
3
|
+
export const AI_MEMORY_PATH_PREFIX = "ai-memory";
|
|
4
|
+
function encodeSegment(value) {
|
|
5
|
+
return encodeURIComponent(value).replaceAll("%", "~");
|
|
6
|
+
}
|
|
7
|
+
export function decodeMemoryPathSegment(value) {
|
|
8
|
+
return decodeURIComponent(value.replaceAll("~", "%"));
|
|
9
|
+
}
|
|
10
|
+
export function conversationMemoryScope(scopeDir) {
|
|
11
|
+
return scopeDir
|
|
12
|
+
? { kind: "project", projectDir: scopeDir }
|
|
13
|
+
: { kind: "workspace" };
|
|
14
|
+
}
|
|
15
|
+
export function memoryScopeKey(scope) {
|
|
16
|
+
return scope.kind === "project"
|
|
17
|
+
? `project:${scope.projectDir}`
|
|
18
|
+
: `${scope.kind}:`;
|
|
19
|
+
}
|
|
20
|
+
export function episodicScopePrefix(scope) {
|
|
21
|
+
if (scope.kind === "project") {
|
|
22
|
+
return `${AI_MEMORY_PATH_PREFIX}/episodic/project/${encodeSegment(scope.projectDir)}`;
|
|
23
|
+
}
|
|
24
|
+
return `${AI_MEMORY_PATH_PREFIX}/episodic/${scope.kind}/_`;
|
|
25
|
+
}
|
|
26
|
+
export function episodicMemoryPath(scope, conversationId, entryId) {
|
|
27
|
+
return `${episodicScopePrefix(scope)}/${encodeSegment(conversationId)}/${encodeSegment(entryId)}`;
|
|
28
|
+
}
|
|
29
|
+
export function curatedScopePrefix(scope) {
|
|
30
|
+
if (scope.kind === "project") {
|
|
31
|
+
return `${AI_MEMORY_PATH_PREFIX}/curated/project/${encodeSegment(scope.projectDir)}`;
|
|
32
|
+
}
|
|
33
|
+
return `${AI_MEMORY_PATH_PREFIX}/curated/${scope.kind}/_`;
|
|
34
|
+
}
|
|
35
|
+
export function curatedMemoryPath(scope, memoryId) {
|
|
36
|
+
return `${curatedScopePrefix(scope)}/${encodeSegment(memoryId)}`;
|
|
37
|
+
}
|
|
38
|
+
export function currentMemoryScopes(scopeDir) {
|
|
39
|
+
const scopes = [{ kind: "user" }, { kind: "workspace" }];
|
|
40
|
+
if (!scopeDir)
|
|
41
|
+
return scopes;
|
|
42
|
+
const segments = scopeDir.split("/");
|
|
43
|
+
const projects = [];
|
|
44
|
+
for (let index = segments.length; index > 0 && projects.length < 8; index -= 1) {
|
|
45
|
+
projects.push({ kind: "project", projectDir: segments.slice(0, index).join("/") });
|
|
46
|
+
}
|
|
47
|
+
return [...scopes, ...projects];
|
|
48
|
+
}
|
|
49
|
+
export function scopeProximity(scope, currentScopeDir) {
|
|
50
|
+
if (scope.kind === "user")
|
|
51
|
+
return 0.75;
|
|
52
|
+
if (scope.kind === "workspace")
|
|
53
|
+
return currentScopeDir ? 0.8 : 1;
|
|
54
|
+
if (scope.projectDir === currentScopeDir)
|
|
55
|
+
return 1;
|
|
56
|
+
if (!currentScopeDir.startsWith(`${scope.projectDir}/`))
|
|
57
|
+
return 0.35;
|
|
58
|
+
const distance = currentScopeDir.split("/").length - scope.projectDir.split("/").length;
|
|
59
|
+
return Math.max(0.45, 0.9 - distance * 0.08);
|
|
60
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AgentRequest, AgentRuntime } from "../core/types";
|
|
2
|
+
import type { ConsolidationProposal, GroundedConsolidationInput, MemoryConsolidationProvider } from "./types";
|
|
3
|
+
export type RuntimeConsolidationConfiguration = {
|
|
4
|
+
runtimeId: "acp" | "codex-native";
|
|
5
|
+
agent: "codex" | "cursor";
|
|
6
|
+
model: string;
|
|
7
|
+
};
|
|
8
|
+
export type RuntimeMemoryConsolidationProviderOptions = {
|
|
9
|
+
configuration: () => RuntimeConsolidationConfiguration;
|
|
10
|
+
resolveRuntime: (request: AgentRequest) => Promise<AgentRuntime>;
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
};
|
|
13
|
+
export declare class RuntimeMemoryConsolidationProvider implements MemoryConsolidationProvider {
|
|
14
|
+
#private;
|
|
15
|
+
private readonly options;
|
|
16
|
+
constructor(options: RuntimeMemoryConsolidationProviderOptions);
|
|
17
|
+
propose(input: GroundedConsolidationInput, signal?: AbortSignal): Promise<ConsolidationProposal>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
const MAX_INPUT_CHARS = 32_000;
|
|
2
|
+
const MAX_OUTPUT_CHARS = 64_000;
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
4
|
+
export class RuntimeMemoryConsolidationProvider {
|
|
5
|
+
options;
|
|
6
|
+
#timeoutMs;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
10
|
+
}
|
|
11
|
+
async propose(input, signal) {
|
|
12
|
+
const configuration = this.options.configuration();
|
|
13
|
+
if (!configuration.model.trim()) {
|
|
14
|
+
throw new Error("Memory consolidation requires a pinned model.");
|
|
15
|
+
}
|
|
16
|
+
const prompt = consolidationPrompt(input);
|
|
17
|
+
const request = {
|
|
18
|
+
prompt: "",
|
|
19
|
+
agent: configuration.agent,
|
|
20
|
+
model: {
|
|
21
|
+
provider: configuration.agent,
|
|
22
|
+
model: configuration.model.trim(),
|
|
23
|
+
},
|
|
24
|
+
thinking: "low",
|
|
25
|
+
metadata: {
|
|
26
|
+
runtime: configuration.runtimeId,
|
|
27
|
+
purpose: "memory-consolidation",
|
|
28
|
+
},
|
|
29
|
+
mcpServers: [],
|
|
30
|
+
restricted: true,
|
|
31
|
+
requireApprovals: false,
|
|
32
|
+
requirePolicyAmendments: false,
|
|
33
|
+
};
|
|
34
|
+
const runtime = await this.options.resolveRuntime(request);
|
|
35
|
+
const session = await runtime.start(request);
|
|
36
|
+
const deadline = new AbortController();
|
|
37
|
+
const timer = setTimeout(() => deadline.abort(new Error("Memory consolidation timed out.")), this.#timeoutMs);
|
|
38
|
+
const abort = () => deadline.abort(signal?.reason);
|
|
39
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
40
|
+
try {
|
|
41
|
+
return await collectProposal(session, prompt, deadline.signal);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
clearTimeout(timer);
|
|
45
|
+
signal?.removeEventListener("abort", abort);
|
|
46
|
+
await session.close().catch(() => undefined);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function collectProposal(session, prompt, signal) {
|
|
51
|
+
let output = "";
|
|
52
|
+
let completed = false;
|
|
53
|
+
const cancelOnAbort = () => void session.cancel?.().catch(() => undefined);
|
|
54
|
+
signal.addEventListener("abort", cancelOnAbort, { once: true });
|
|
55
|
+
const send = session.send(prompt);
|
|
56
|
+
const events = session.events()[Symbol.asyncIterator]();
|
|
57
|
+
try {
|
|
58
|
+
while (true) {
|
|
59
|
+
const next = await nextEvent(events, signal);
|
|
60
|
+
if (next.done)
|
|
61
|
+
break;
|
|
62
|
+
const event = next.value;
|
|
63
|
+
assertReadOnlyEvent(event);
|
|
64
|
+
if (event.type === "text") {
|
|
65
|
+
output += event.text;
|
|
66
|
+
if (output.length > MAX_OUTPUT_CHARS) {
|
|
67
|
+
await session.cancel?.().catch(() => undefined);
|
|
68
|
+
throw new Error("Memory consolidation output exceeded its limit.");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (event.type === "error")
|
|
72
|
+
throw event.error;
|
|
73
|
+
if (event.type === "completed") {
|
|
74
|
+
completed = true;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
await send;
|
|
79
|
+
if (!completed)
|
|
80
|
+
throw new Error("Memory consolidation ended without completion.");
|
|
81
|
+
return parseProposal(output);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
await session.cancel?.().catch(() => undefined);
|
|
85
|
+
await send.catch(() => undefined);
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
signal.removeEventListener("abort", cancelOnAbort);
|
|
90
|
+
await events.return?.().catch(() => undefined);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function nextEvent(events, signal) {
|
|
94
|
+
if (signal.aborted)
|
|
95
|
+
return Promise.reject(signal.reason);
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const onAbort = () => reject(signal.reason);
|
|
98
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
99
|
+
void events.next().then((next) => {
|
|
100
|
+
signal.removeEventListener("abort", onAbort);
|
|
101
|
+
resolve(next);
|
|
102
|
+
}, (error) => {
|
|
103
|
+
signal.removeEventListener("abort", onAbort);
|
|
104
|
+
reject(error);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function assertReadOnlyEvent(event) {
|
|
109
|
+
if (event.type === "tool.start" ||
|
|
110
|
+
event.type === "tool.end" ||
|
|
111
|
+
event.type === "command.start" ||
|
|
112
|
+
event.type === "command.end" ||
|
|
113
|
+
event.type === "file.changed" ||
|
|
114
|
+
event.type === "permission.request" ||
|
|
115
|
+
event.type === "question.request") {
|
|
116
|
+
throw new Error(`Memory consolidation aborted on forbidden runtime event: ${event.type}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function consolidationPrompt(input) {
|
|
120
|
+
const payload = JSON.stringify({
|
|
121
|
+
scope: input.scope,
|
|
122
|
+
candidates: input.candidates.slice(0, 10).map((candidate) => ({
|
|
123
|
+
id: candidate.id,
|
|
124
|
+
claim: candidate.claim.slice(0, 2_000),
|
|
125
|
+
evidence: candidate.evidence.slice(0, 20).map((evidence) => ({
|
|
126
|
+
conversationId: evidence.conversationId,
|
|
127
|
+
entryId: evidence.entryId,
|
|
128
|
+
entryHash: evidence.entryHash,
|
|
129
|
+
observedAt: evidence.observedAt,
|
|
130
|
+
})),
|
|
131
|
+
})),
|
|
132
|
+
});
|
|
133
|
+
if (payload.length > MAX_INPUT_CHARS) {
|
|
134
|
+
throw new Error("Memory consolidation input exceeded its limit.");
|
|
135
|
+
}
|
|
136
|
+
return [
|
|
137
|
+
"You are a restricted memory consolidation processor.",
|
|
138
|
+
"Return exactly one JSON object and no markdown or commentary.",
|
|
139
|
+
"Use only the supplied candidates. Do not invent candidate IDs or evidence.",
|
|
140
|
+
"The object must be {\"memories\":[{\"candidateIds\":[string],\"kind\":\"preference\"|\"fact\"|\"decision\"|\"procedure\"|\"constraint\",\"scope\":MemoryScope,\"importance\":1|2|3|4|5,\"triggers\":[string],\"summary\":string,\"supersessionKey\"?:string}]}.",
|
|
141
|
+
"Input:",
|
|
142
|
+
payload,
|
|
143
|
+
].join("\n");
|
|
144
|
+
}
|
|
145
|
+
function parseProposal(output) {
|
|
146
|
+
const parsed = JSON.parse(output.trim());
|
|
147
|
+
if (!isObject(parsed) || !Array.isArray(parsed.memories)) {
|
|
148
|
+
throw new Error("Memory consolidation returned an invalid JSON shape.");
|
|
149
|
+
}
|
|
150
|
+
return parsed;
|
|
151
|
+
}
|
|
152
|
+
function isObject(value) {
|
|
153
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
154
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { ConversationLocation } from "../conversations/types";
|
|
2
|
+
import type { AgentContextBlock } from "../core/types";
|
|
3
|
+
export type MemoryScope = {
|
|
4
|
+
kind: "user";
|
|
5
|
+
} | {
|
|
6
|
+
kind: "workspace";
|
|
7
|
+
} | {
|
|
8
|
+
kind: "project";
|
|
9
|
+
projectDir: string;
|
|
10
|
+
};
|
|
11
|
+
export type MemoryOriginClass = "owner" | "agent" | "untrusted" | "system";
|
|
12
|
+
export type MemoryEvidenceRef = {
|
|
13
|
+
conversationId: string;
|
|
14
|
+
entryId: string;
|
|
15
|
+
entryHash: string;
|
|
16
|
+
observedAt: string;
|
|
17
|
+
scopeDirAtObservation: string;
|
|
18
|
+
};
|
|
19
|
+
export type DurableMemoryKind = "preference" | "fact" | "decision" | "procedure" | "constraint";
|
|
20
|
+
export type DurableMemoryRecord = {
|
|
21
|
+
schemaVersion: 1;
|
|
22
|
+
id: string;
|
|
23
|
+
revision: number;
|
|
24
|
+
kind: DurableMemoryKind;
|
|
25
|
+
scope: MemoryScope;
|
|
26
|
+
status: "active" | "retracted";
|
|
27
|
+
supersessionKey?: string;
|
|
28
|
+
importance: 1 | 2 | 3 | 4 | 5;
|
|
29
|
+
triggers: string[];
|
|
30
|
+
summary: string;
|
|
31
|
+
evidence: MemoryEvidenceRef[];
|
|
32
|
+
createdBy: "owner-ui" | "deterministic-promotion" | "consolidator";
|
|
33
|
+
createdAt: string;
|
|
34
|
+
updatedAt: string;
|
|
35
|
+
previousRevisionHash?: string;
|
|
36
|
+
};
|
|
37
|
+
export type MemoryContext = {
|
|
38
|
+
scopeDir: string;
|
|
39
|
+
conversationId?: string;
|
|
40
|
+
agentBindingId?: string;
|
|
41
|
+
runId?: string;
|
|
42
|
+
};
|
|
43
|
+
export type MemorySearchQuery = {
|
|
44
|
+
query: string;
|
|
45
|
+
scope?: "current" | "workspace";
|
|
46
|
+
corpus?: "curated" | "episodic" | "all";
|
|
47
|
+
limit?: number;
|
|
48
|
+
since?: string;
|
|
49
|
+
before?: string;
|
|
50
|
+
includeSuperseded?: boolean;
|
|
51
|
+
};
|
|
52
|
+
export type MemoryCorpus = "curated" | "episodic";
|
|
53
|
+
export type MemorySearchResult = {
|
|
54
|
+
ref: string;
|
|
55
|
+
corpus: MemoryCorpus;
|
|
56
|
+
snippet: string;
|
|
57
|
+
kind: DurableMemoryKind | "episode";
|
|
58
|
+
scope: MemoryScope;
|
|
59
|
+
observedAt: string;
|
|
60
|
+
effectiveTrust: MemoryOriginClass;
|
|
61
|
+
status: "active" | "retracted";
|
|
62
|
+
score: number;
|
|
63
|
+
evidence: MemoryEvidenceRef[];
|
|
64
|
+
};
|
|
65
|
+
export type MemoryEvidenceMessage = {
|
|
66
|
+
id: string;
|
|
67
|
+
type: "message" | "tool";
|
|
68
|
+
role?: "user" | "assistant";
|
|
69
|
+
text: string;
|
|
70
|
+
observedAt: string;
|
|
71
|
+
originClass: MemoryOriginClass;
|
|
72
|
+
hash: string;
|
|
73
|
+
verification: "matching" | "modified";
|
|
74
|
+
};
|
|
75
|
+
export type MemoryDetail = {
|
|
76
|
+
ref: string;
|
|
77
|
+
corpus: MemoryCorpus;
|
|
78
|
+
status: "available" | "moved" | "missing" | "ambiguous" | "modified";
|
|
79
|
+
result?: MemorySearchResult;
|
|
80
|
+
currentLocation?: ConversationLocation;
|
|
81
|
+
evidenceMessages: MemoryEvidenceMessage[];
|
|
82
|
+
history?: DurableMemoryRecord[];
|
|
83
|
+
};
|
|
84
|
+
export type MemoryRebuildResult = {
|
|
85
|
+
conversations: number;
|
|
86
|
+
episodes: number;
|
|
87
|
+
skipped: number;
|
|
88
|
+
inconsistent: number;
|
|
89
|
+
};
|
|
90
|
+
export type ConsolidationPreview = {
|
|
91
|
+
scope: MemoryScope;
|
|
92
|
+
candidateIds: string[];
|
|
93
|
+
proposals: number;
|
|
94
|
+
};
|
|
95
|
+
export type ConsolidationResult = ConsolidationPreview & {
|
|
96
|
+
written: number;
|
|
97
|
+
needsReview: number;
|
|
98
|
+
};
|
|
99
|
+
export type MemoryForgetPreview = {
|
|
100
|
+
conversationId: string;
|
|
101
|
+
episodeRefs: string[];
|
|
102
|
+
candidateIds: string[];
|
|
103
|
+
memoryIds: string[];
|
|
104
|
+
};
|
|
105
|
+
export type MemoryForgetResult = MemoryForgetPreview & {
|
|
106
|
+
exclusionPath: string;
|
|
107
|
+
retracted: number;
|
|
108
|
+
needsReview: number;
|
|
109
|
+
};
|
|
110
|
+
export type GroundedConsolidationInput = {
|
|
111
|
+
scope: MemoryScope;
|
|
112
|
+
candidates: Array<{
|
|
113
|
+
id: string;
|
|
114
|
+
claim: string;
|
|
115
|
+
evidence: MemoryEvidenceRef[];
|
|
116
|
+
}>;
|
|
117
|
+
};
|
|
118
|
+
export type ConsolidationProposal = {
|
|
119
|
+
memories: Array<Pick<DurableMemoryRecord, "kind" | "scope" | "importance" | "triggers" | "summary"> & {
|
|
120
|
+
candidateIds: string[];
|
|
121
|
+
supersessionKey?: string;
|
|
122
|
+
}>;
|
|
123
|
+
};
|
|
124
|
+
export interface MemoryConsolidationProvider {
|
|
125
|
+
propose(input: GroundedConsolidationInput, signal?: AbortSignal): Promise<ConsolidationProposal>;
|
|
126
|
+
}
|
|
127
|
+
export interface MemoryService {
|
|
128
|
+
search(query: MemorySearchQuery, context: MemoryContext, signal?: AbortSignal): Promise<MemorySearchResult[]>;
|
|
129
|
+
get(ref: string, context: MemoryContext, signal?: AbortSignal): Promise<MemoryDetail>;
|
|
130
|
+
ingestConversation(location: ConversationLocation): Promise<void>;
|
|
131
|
+
previewConsolidation(scope: MemoryScope): Promise<ConsolidationPreview>;
|
|
132
|
+
consolidate(scope: MemoryScope): Promise<ConsolidationResult>;
|
|
133
|
+
rebuild(scope?: MemoryScope): Promise<MemoryRebuildResult>;
|
|
134
|
+
previewForgetConversation(location: ConversationLocation): Promise<MemoryForgetPreview>;
|
|
135
|
+
forgetConversation(location: ConversationLocation): Promise<MemoryForgetResult>;
|
|
136
|
+
}
|
|
137
|
+
export interface AutomaticMemoryRecall {
|
|
138
|
+
recall(query: string, context: MemoryContext, signal?: AbortSignal): Promise<AgentContextBlock[]>;
|
|
139
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ModelRef } from "../core/types";
|
|
2
|
+
import type { ModelProvider, ProviderAuthStatus } from "./model-provider";
|
|
3
|
+
export declare class AcpModelProvider implements ModelProvider {
|
|
4
|
+
#private;
|
|
5
|
+
readonly id: string;
|
|
6
|
+
constructor(id: string, options?: {
|
|
7
|
+
workspace?: string;
|
|
8
|
+
});
|
|
9
|
+
listModels(): Promise<ModelRef[]>;
|
|
10
|
+
authStatus(): Promise<ProviderAuthStatus>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { getNativeDesktopBridge, getNativeDesktopCapability, hasNativeDesktopCapability, } from "@lapis-notes/api/desktop-native";
|
|
2
|
+
export class AcpModelProvider {
|
|
3
|
+
id;
|
|
4
|
+
#workspace;
|
|
5
|
+
constructor(id, options = {}) {
|
|
6
|
+
this.id = id;
|
|
7
|
+
this.#workspace = options.workspace;
|
|
8
|
+
}
|
|
9
|
+
async listModels() {
|
|
10
|
+
if (!hasNativeDesktopCapability("agent-runtime"))
|
|
11
|
+
return [];
|
|
12
|
+
const bridge = getNativeDesktopBridge();
|
|
13
|
+
if (!bridge)
|
|
14
|
+
return [];
|
|
15
|
+
const payload = { agent: this.id, workspace: this.#workspace };
|
|
16
|
+
const catalog = getNativeDesktopCapability("agent-runtime")?.details?.deferredModels ===
|
|
17
|
+
true
|
|
18
|
+
? await deferredModelCatalog(bridge, payload)
|
|
19
|
+
: await bridge.invoke("desktop_agent_acp_models", payload);
|
|
20
|
+
const current = catalog.currentModel?.trim();
|
|
21
|
+
const entries = new Map((catalog.entries ?? []).map((entry) => [entry.id, entry]));
|
|
22
|
+
return [...new Set(catalog.models ?? [])]
|
|
23
|
+
.map((model) => model.trim())
|
|
24
|
+
.filter(Boolean)
|
|
25
|
+
.map((model) => {
|
|
26
|
+
const entry = entries.get(model);
|
|
27
|
+
return {
|
|
28
|
+
provider: this.id,
|
|
29
|
+
model,
|
|
30
|
+
...(entry?.label ? { displayName: entry.label } : {}),
|
|
31
|
+
...(entry?.badges?.length ? { badges: entry.badges } : {}),
|
|
32
|
+
isDefault: model === current,
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async authStatus() {
|
|
37
|
+
if (!hasNativeDesktopCapability("agent-runtime")) {
|
|
38
|
+
return {
|
|
39
|
+
authenticated: false,
|
|
40
|
+
label: this.id,
|
|
41
|
+
detail: "Live model listing requires the desktop agent-runtime capability.",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const models = await this.listModels();
|
|
46
|
+
return models.length > 0
|
|
47
|
+
? { authenticated: true, label: this.id }
|
|
48
|
+
: {
|
|
49
|
+
authenticated: false,
|
|
50
|
+
label: this.id,
|
|
51
|
+
detail: `${this.id} did not return a model catalog.`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return {
|
|
56
|
+
authenticated: false,
|
|
57
|
+
label: this.id,
|
|
58
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function deferredModelCatalog(bridge, payload) {
|
|
64
|
+
if (!bridge.onAgentRuntimeEvent) {
|
|
65
|
+
throw new Error("The desktop agent host cannot deliver model catalogs.");
|
|
66
|
+
}
|
|
67
|
+
const requestId = crypto.randomUUID();
|
|
68
|
+
let unsubscribe = () => { };
|
|
69
|
+
let timeoutId;
|
|
70
|
+
const result = new Promise((resolve, reject) => {
|
|
71
|
+
unsubscribe = bridge.onAgentRuntimeEvent((event) => {
|
|
72
|
+
if (event.sessionId !== requestId ||
|
|
73
|
+
event.runId !== "model-catalog" ||
|
|
74
|
+
event.event.type !== "event") {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const detail = event.event.event;
|
|
78
|
+
if (detail?.type === "model_catalog") {
|
|
79
|
+
resolve(detail.catalog);
|
|
80
|
+
}
|
|
81
|
+
else if (detail?.type === "model_catalog_error") {
|
|
82
|
+
reject(new Error(String(detail.message ?? "Model discovery failed.")));
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
timeoutId = setTimeout(() => reject(new Error("Desktop ACP model discovery timed out.")), 60_000);
|
|
86
|
+
});
|
|
87
|
+
try {
|
|
88
|
+
const started = await bridge.invoke("desktop_agent_acp_models", { ...payload, requestId });
|
|
89
|
+
if (started.requestId !== requestId) {
|
|
90
|
+
throw new Error("The desktop agent host returned a different model request id.");
|
|
91
|
+
}
|
|
92
|
+
return await result;
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
if (timeoutId)
|
|
96
|
+
clearTimeout(timeoutId);
|
|
97
|
+
unsubscribe();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ModelRef } from "../core/types";
|
|
2
|
+
import type { AgentProcessHost } from "../host/process-host";
|
|
3
|
+
export declare function normalizeCodexModelList(result: unknown): ModelRef[];
|
|
4
|
+
export declare function listCodexModelsFromHost(host: AgentProcessHost, options?: {
|
|
5
|
+
cwd?: string;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}): Promise<ModelRef[]>;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
function asRecord(value) {
|
|
2
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
3
|
+
? value
|
|
4
|
+
: {};
|
|
5
|
+
}
|
|
6
|
+
function stringValue(value) {
|
|
7
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
8
|
+
}
|
|
9
|
+
function withTimeout(promise, timeoutMs, message) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const timer = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
12
|
+
promise.then((value) => {
|
|
13
|
+
clearTimeout(timer);
|
|
14
|
+
resolve(value);
|
|
15
|
+
}, (error) => {
|
|
16
|
+
clearTimeout(timer);
|
|
17
|
+
reject(error);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export function normalizeCodexModelList(result) {
|
|
22
|
+
const data = asRecord(result).data;
|
|
23
|
+
if (!Array.isArray(data))
|
|
24
|
+
return [];
|
|
25
|
+
const models = [];
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
for (const entry of data) {
|
|
28
|
+
const record = asRecord(entry);
|
|
29
|
+
const model = stringValue(record.model) ?? stringValue(record.id);
|
|
30
|
+
if (!model || seen.has(model))
|
|
31
|
+
continue;
|
|
32
|
+
seen.add(model);
|
|
33
|
+
const supportedThinking = Array.isArray(record.supportedReasoningEfforts)
|
|
34
|
+
? record.supportedReasoningEfforts
|
|
35
|
+
.map((option) => {
|
|
36
|
+
const effort = stringValue(asRecord(option).reasoningEffort);
|
|
37
|
+
if (effort === "none")
|
|
38
|
+
return "off";
|
|
39
|
+
if (effort === "low" || effort === "medium" || effort === "high") {
|
|
40
|
+
return effort;
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
})
|
|
44
|
+
.filter((value) => value !== undefined)
|
|
45
|
+
: undefined;
|
|
46
|
+
const displayName = stringValue(record.displayName);
|
|
47
|
+
const description = stringValue(record.description);
|
|
48
|
+
models.push({
|
|
49
|
+
provider: "codex",
|
|
50
|
+
model,
|
|
51
|
+
...(displayName ? { displayName } : {}),
|
|
52
|
+
...(description ? { description } : {}),
|
|
53
|
+
...(record.isDefault === true ? { isDefault: true } : {}),
|
|
54
|
+
...(supportedThinking && supportedThinking.length > 0
|
|
55
|
+
? { supportedThinking: [...new Set(supportedThinking)] }
|
|
56
|
+
: {}),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return models;
|
|
60
|
+
}
|
|
61
|
+
export async function listCodexModelsFromHost(host, options = {}) {
|
|
62
|
+
if (!host.available)
|
|
63
|
+
return [];
|
|
64
|
+
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
65
|
+
const process = await withTimeout(host.spawn({
|
|
66
|
+
command: "codex",
|
|
67
|
+
args: ["app-server", "--stdio"],
|
|
68
|
+
cwd: options.cwd,
|
|
69
|
+
}), timeoutMs, "Codex model catalog spawn timed out");
|
|
70
|
+
const rpc = new CodexCatalogRpc(process);
|
|
71
|
+
try {
|
|
72
|
+
return await withTimeout((async () => {
|
|
73
|
+
await rpc.request("initialize", {
|
|
74
|
+
clientInfo: {
|
|
75
|
+
name: "lapis_ai_model_catalog",
|
|
76
|
+
title: "Lapis AI Model Catalog",
|
|
77
|
+
version: "0.0.1",
|
|
78
|
+
},
|
|
79
|
+
capabilities: {},
|
|
80
|
+
});
|
|
81
|
+
await rpc.notify("initialized", {});
|
|
82
|
+
const models = [];
|
|
83
|
+
let cursor = null;
|
|
84
|
+
const seenCursors = new Set();
|
|
85
|
+
do {
|
|
86
|
+
const result = await rpc.request("model/list", {
|
|
87
|
+
limit: 100,
|
|
88
|
+
includeHidden: false,
|
|
89
|
+
...(cursor ? { cursor } : {}),
|
|
90
|
+
});
|
|
91
|
+
models.push(...normalizeCodexModelList(result));
|
|
92
|
+
const nextCursor = stringValue(asRecord(result).nextCursor) ?? null;
|
|
93
|
+
if (!nextCursor || seenCursors.has(nextCursor))
|
|
94
|
+
break;
|
|
95
|
+
seenCursors.add(nextCursor);
|
|
96
|
+
cursor = nextCursor;
|
|
97
|
+
} while (cursor);
|
|
98
|
+
return models;
|
|
99
|
+
})(), timeoutMs, "Codex model catalog request timed out");
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
await rpc.close();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
class CodexCatalogRpc {
|
|
106
|
+
process;
|
|
107
|
+
#id = 1;
|
|
108
|
+
#buffer = "";
|
|
109
|
+
#pending = new Map();
|
|
110
|
+
#consume;
|
|
111
|
+
constructor(process) {
|
|
112
|
+
this.process = process;
|
|
113
|
+
this.#consume = this.#pump();
|
|
114
|
+
}
|
|
115
|
+
async request(method, params) {
|
|
116
|
+
const id = this.#id++;
|
|
117
|
+
const result = new Promise((resolve, reject) => {
|
|
118
|
+
this.#pending.set(id, { resolve, reject });
|
|
119
|
+
});
|
|
120
|
+
try {
|
|
121
|
+
await this.process.write(`${JSON.stringify({ id, method, params })}\n`);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
this.#pending.delete(id);
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
async notify(method, params) {
|
|
130
|
+
await this.process.write(`${JSON.stringify({ method, params })}\n`);
|
|
131
|
+
}
|
|
132
|
+
async close() {
|
|
133
|
+
for (const [id, pending] of this.#pending) {
|
|
134
|
+
this.#pending.delete(id);
|
|
135
|
+
pending.reject(new Error("Codex model catalog closed."));
|
|
136
|
+
}
|
|
137
|
+
await this.process.kill();
|
|
138
|
+
await this.#consume;
|
|
139
|
+
}
|
|
140
|
+
async #pump() {
|
|
141
|
+
try {
|
|
142
|
+
for await (const message of this.process.messages()) {
|
|
143
|
+
if (message.type !== "stdout")
|
|
144
|
+
continue;
|
|
145
|
+
this.#buffer += message.data;
|
|
146
|
+
const lines = this.#buffer.split("\n");
|
|
147
|
+
this.#buffer = lines.pop() ?? "";
|
|
148
|
+
for (const line of lines)
|
|
149
|
+
this.#handleLine(line);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
/* process closed */
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
#handleLine(line) {
|
|
157
|
+
if (!line.trim())
|
|
158
|
+
return;
|
|
159
|
+
let parsed;
|
|
160
|
+
try {
|
|
161
|
+
parsed = JSON.parse(line);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (parsed.id === undefined || parsed.method)
|
|
167
|
+
return;
|
|
168
|
+
const pending = this.#pending.get(Number(parsed.id));
|
|
169
|
+
if (!pending)
|
|
170
|
+
return;
|
|
171
|
+
this.#pending.delete(Number(parsed.id));
|
|
172
|
+
if (parsed.error) {
|
|
173
|
+
const record = asRecord(parsed.error);
|
|
174
|
+
pending.reject(new Error(stringValue(record.message) ?? "Codex model/list failed"));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
pending.resolve(parsed.result);
|
|
178
|
+
}
|
|
179
|
+
}
|