@gmickel/gno 2.5.1 → 2.7.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/README.md +60 -5
- package/assets/skill/README.md +5 -1
- package/assets/skill/SKILL.md +80 -4
- package/assets/skill/cli-reference.md +132 -2
- package/assets/skill/examples.md +30 -0
- package/assets/skill/mcp-reference.md +54 -1
- package/assets/skill/recipes/capture-and-file.md +6 -0
- package/assets/skill/recipes/memory-file-decision.md +6 -0
- package/assets/skill/recipes/memory-supersede-fact.md +5 -0
- package/assets/skill/recipes/session-evidence-lookup.md +98 -0
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/{gno-browser-clipper-v2.5.1.zip → gno-browser-clipper-v2.7.0.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.7.0.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +2 -1
- package/spec/cli.md +449 -35
- package/spec/mcp.md +234 -4
- package/spec/output-schemas/ask.schema.json +1 -1
- package/spec/output-schemas/capture-receipt.schema.json +4 -1
- package/spec/output-schemas/doctor.schema.json +88 -0
- package/spec/output-schemas/error.schema.json +11 -2
- package/spec/output-schemas/get.schema.json +1 -1
- package/spec/output-schemas/mcp-capture-result.schema.json +4 -2
- package/spec/output-schemas/memory-remember.schema.json +10 -4
- package/spec/output-schemas/multi-get.schema.json +4 -1
- package/spec/output-schemas/peek.schema.json +2 -9
- package/spec/output-schemas/request-status.schema.json +113 -0
- package/spec/output-schemas/resident-status.schema.json +22 -0
- package/spec/output-schemas/search-result.schema.json +1 -1
- package/spec/output-schemas/search-results.schema.json +1 -1
- package/spec/output-schemas/sessions-automation-run.schema.json +46 -0
- package/spec/output-schemas/sessions-discovery.schema.json +38 -0
- package/spec/output-schemas/sessions-import-receipt.schema.json +156 -0
- package/spec/output-schemas/sessions-status.schema.json +432 -0
- package/spec/output-schemas/status.schema.json +98 -0
- package/src/cli/commands/ask.ts +14 -2
- package/src/cli/commands/capture.ts +55 -96
- package/src/cli/commands/daemon.ts +41 -0
- package/src/cli/commands/doctor.ts +54 -20
- package/src/cli/commands/embed.ts +41 -3
- package/src/cli/commands/ls.ts +3 -0
- package/src/cli/commands/memory.ts +12 -3
- package/src/cli/commands/query.ts +5 -0
- package/src/cli/commands/request-status.ts +59 -0
- package/src/cli/commands/reset.ts +39 -5
- package/src/cli/commands/sessions.ts +713 -0
- package/src/cli/commands/shared.ts +14 -1
- package/src/cli/commands/status.ts +63 -5
- package/src/cli/commands/vec.ts +54 -0
- package/src/cli/detach.ts +29 -1
- package/src/cli/errors.ts +13 -9
- package/src/cli/program.ts +441 -2
- package/src/cli/session-binding.ts +49 -0
- package/src/config/types.ts +8 -0
- package/src/core/capture-publish.ts +239 -0
- package/src/core/capture-sync.ts +12 -2
- package/src/core/host-paths.ts +31 -0
- package/src/core/memory-remember.ts +234 -122
- package/src/core/memory-types.ts +11 -0
- package/src/core/network-boundary-inventory.ts +8 -0
- package/src/core/request-receipts.ts +671 -0
- package/src/core/shutdown-budget.ts +6 -0
- package/src/core/vector-partition-status.ts +52 -0
- package/src/embed/backlog.ts +124 -18
- package/src/embed/fingerprint.ts +6 -3
- package/src/embed/retry.ts +66 -27
- package/src/embed/variant-backlog.ts +15 -10
- package/src/embed/variant-retry.ts +31 -22
- package/src/index.ts +30 -2
- package/src/llm/native-worker/dispatcher.ts +2 -0
- package/src/llm/native-worker/embedding-identity.ts +42 -0
- package/src/llm/native-worker/protocol.ts +1 -0
- package/src/llm/types.ts +3 -0
- package/src/mcp/context.ts +17 -0
- package/src/mcp/http-egress.ts +4 -0
- package/src/mcp/http-transport.ts +2 -0
- package/src/mcp/resources/index.ts +6 -5
- package/src/mcp/tool-descriptions-core.ts +1 -1
- package/src/mcp/tools/capture.ts +87 -85
- package/src/mcp/tools/index.ts +77 -4
- package/src/mcp/tools/memory-remember.ts +8 -1
- package/src/mcp/tools/memory-shared.ts +7 -1
- package/src/mcp/tools/request-status.ts +73 -0
- package/src/mcp/tools/sessions.ts +208 -0
- package/src/mcp/tools/status.ts +4 -0
- package/src/pipeline/hybrid.ts +37 -7
- package/src/pipeline/vsearch.ts +14 -2
- package/src/sdk/client.ts +180 -84
- package/src/sdk/index.ts +6 -0
- package/src/sdk/types.ts +54 -2
- package/src/serve/capture-service.ts +98 -32
- package/src/serve/config-sync.ts +3 -2
- package/src/serve/embed-scheduler.ts +133 -19
- package/src/serve/host-path-redaction.ts +79 -0
- package/src/serve/public/app.tsx +4 -1
- package/src/serve/public/components/CaptureModal.tsx +26 -8
- package/src/serve/public/components/sessions/AutomationPanel.tsx +800 -0
- package/src/serve/public/components/sessions/ImportReceipt.tsx +238 -0
- package/src/serve/public/components/sessions/SessionSearch.tsx +286 -0
- package/src/serve/public/components/sessions/SourcesPanel.tsx +541 -0
- package/src/serve/public/components/sessions/api.ts +40 -0
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/hooks/use-api.ts +26 -3
- package/src/serve/public/lib/request-intent.ts +77 -0
- package/src/serve/public/lib/snippet.tsx +52 -0
- package/src/serve/public/lib/workspace-actions.ts +12 -1
- package/src/serve/public/lib/workspace-tabs.ts +2 -0
- package/src/serve/public/pages/Dashboard.tsx +22 -9
- package/src/serve/public/pages/DocView.tsx +25 -6
- package/src/serve/public/pages/DocumentEditor.tsx +224 -104
- package/src/serve/public/pages/Search.tsx +1 -41
- package/src/serve/public/pages/Sessions.tsx +350 -0
- package/src/serve/resident-runtime.ts +69 -4
- package/src/serve/resident-status.ts +13 -1
- package/src/serve/routes/api.ts +476 -147
- package/src/serve/routes/sessions.ts +766 -0
- package/src/serve/security.ts +9 -0
- package/src/serve/server.ts +215 -10
- package/src/serve/session-automation.ts +146 -0
- package/src/serve/status-model.ts +16 -0
- package/src/serve/status.ts +2 -0
- package/src/serve/watch-reconciliation-shared.ts +3 -0
- package/src/serve/watch-service-events.ts +3 -2
- package/src/serve/watch-service-run-flush.ts +35 -2
- package/src/serve/watch-service.ts +5 -0
- package/src/sessions/archive.ts +348 -0
- package/src/sessions/automation-state.ts +444 -0
- package/src/sessions/automation-status.ts +239 -0
- package/src/sessions/automation.ts +1169 -0
- package/src/sessions/binding.ts +105 -0
- package/src/sessions/claude-hook.ts +240 -0
- package/src/sessions/config.ts +176 -0
- package/src/sessions/format.ts +191 -0
- package/src/sessions/import-child-env.ts +8 -0
- package/src/sessions/import-child.ts +152 -0
- package/src/sessions/parsers/claude-code.ts +259 -0
- package/src/sessions/parsers/codex.ts +303 -0
- package/src/sessions/parsers/hermes.ts +248 -0
- package/src/sessions/parsers/openclaw.ts +496 -0
- package/src/sessions/parsers/shared.ts +184 -0
- package/src/sessions/sanitize.ts +222 -0
- package/src/sessions/service.ts +1533 -0
- package/src/sessions/setup.ts +477 -0
- package/src/sessions/sources.ts +518 -0
- package/src/sessions/state.ts +118 -0
- package/src/sessions/types.ts +457 -0
- package/src/store/migrations/031-runtime-independent-vectors.ts +29 -0
- package/src/store/migrations/032-vector-runtime-callers.ts +17 -0
- package/src/store/migrations/index.ts +4 -0
- package/src/store/sqlite/adapter.ts +76 -16
- package/src/store/sqlite/scoped-index.ts +9 -0
- package/src/store/types.ts +11 -1
- package/src/store/vector/lazy.ts +46 -43
- package/src/store/vector/runtime-compat.ts +651 -0
- package/src/store/vector/sqlite-vec.ts +20 -2
- package/src/store/vector/status.ts +276 -35
- package/src/store/vector/types.ts +2 -0
- package/src/store/vector/variant-search.ts +71 -23
- package/src/store/vector/variants.ts +49 -14
- package/browser-extension/artifacts/gno-browser-clipper-v2.5.1.zip.sha256 +0 -1
|
@@ -4,30 +4,28 @@
|
|
|
4
4
|
* @module src/cli/commands/capture
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
// node:fs/promises for mkdir (no Bun equivalent for recursive dir creation)
|
|
8
|
-
import { mkdir } from "node:fs/promises";
|
|
9
|
-
// node:path has no Bun path utilities
|
|
10
|
-
import { dirname, join } from "node:path";
|
|
11
|
-
|
|
12
7
|
import type {
|
|
13
|
-
|
|
8
|
+
CaptureInput,
|
|
14
9
|
CaptureReceipt,
|
|
15
10
|
CaptureSourceKind,
|
|
16
11
|
} from "../../core/capture";
|
|
17
12
|
import type { NoteCollisionPolicy } from "../../core/note-creation";
|
|
18
13
|
import type { NotePresetId } from "../../core/note-presets";
|
|
14
|
+
import type { RequestReceiptInfo } from "../../core/request-receipts";
|
|
19
15
|
|
|
20
16
|
import { getIndexDbPath } from "../../app/constants";
|
|
21
17
|
import {
|
|
22
|
-
buildCaptureReceipt,
|
|
23
18
|
listCaptureDiskRelPaths,
|
|
24
19
|
planCapture,
|
|
25
20
|
serializeCaptureReceipt,
|
|
26
21
|
} from "../../core/capture";
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
|
|
22
|
+
import { publishCapture } from "../../core/capture-publish";
|
|
23
|
+
import {
|
|
24
|
+
formatRequestReceiptLine,
|
|
25
|
+
localRequestLedger,
|
|
26
|
+
} from "../../core/request-receipts";
|
|
30
27
|
import { CliError } from "../errors";
|
|
28
|
+
import { requestErrorToCli } from "./request-status";
|
|
31
29
|
import { initStore } from "./shared";
|
|
32
30
|
|
|
33
31
|
export interface CaptureCliOptions {
|
|
@@ -49,6 +47,7 @@ export interface CaptureCliOptions {
|
|
|
49
47
|
sourceAuthor?: string;
|
|
50
48
|
sourceDate?: string;
|
|
51
49
|
sourceId?: string;
|
|
50
|
+
requestId?: string;
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
const SOURCE_KINDS = new Set<CaptureSourceKind>([
|
|
@@ -130,7 +129,7 @@ function validateCollisionPolicy(
|
|
|
130
129
|
|
|
131
130
|
export async function capture(
|
|
132
131
|
options: CaptureCliOptions
|
|
133
|
-
): Promise<CaptureReceipt> {
|
|
132
|
+
): Promise<CaptureReceipt & { request?: RequestReceiptInfo }> {
|
|
134
133
|
const storeInit = await initStore({
|
|
135
134
|
configPath: options.configPath,
|
|
136
135
|
indexName: options.indexName,
|
|
@@ -160,102 +159,61 @@ export async function capture(
|
|
|
160
159
|
|
|
161
160
|
validateCollisionPolicy(options.collisionPolicy);
|
|
162
161
|
const content = await readContent(options);
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
162
|
+
const input: CaptureInput = {
|
|
163
|
+
collection: collection.name,
|
|
164
|
+
content,
|
|
165
|
+
title: options.title,
|
|
166
|
+
relPath: options.path,
|
|
167
|
+
folderPath: options.folder,
|
|
168
|
+
collisionPolicy: options.collisionPolicy,
|
|
169
|
+
presetId: options.preset,
|
|
170
|
+
tags: parseTags(options.tags),
|
|
171
|
+
source: buildSource(options),
|
|
172
|
+
};
|
|
173
|
+
const ledger = localRequestLedger(getIndexDbPath(options.indexName));
|
|
169
174
|
try {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
175
|
+
const published = await publishCapture({
|
|
176
|
+
collection,
|
|
177
|
+
store,
|
|
178
|
+
lockPath: ledger.lockPath,
|
|
179
|
+
reportSyncFailure: true,
|
|
180
|
+
config,
|
|
181
|
+
plan: async () => {
|
|
182
|
+
const existingDocs = await store.listDocuments(collection.name);
|
|
183
|
+
if (!existingDocs.ok) {
|
|
184
|
+
throw new Error(existingDocs.error.message);
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
return planCapture({
|
|
188
|
+
input,
|
|
189
|
+
existingRelPaths: existingDocs.value.map((doc) => doc.relPath),
|
|
190
|
+
diskRelPaths: await listCaptureDiskRelPaths(collection.path),
|
|
191
|
+
});
|
|
192
|
+
} catch (error) {
|
|
193
|
+
throw new CliError(
|
|
194
|
+
"VALIDATION",
|
|
195
|
+
error instanceof Error ? error.message : String(error)
|
|
196
|
+
);
|
|
197
|
+
}
|
|
181
198
|
},
|
|
182
|
-
|
|
183
|
-
|
|
199
|
+
request:
|
|
200
|
+
options.requestId === undefined
|
|
201
|
+
? undefined
|
|
202
|
+
: { ...ledger, requestId: options.requestId, input },
|
|
184
203
|
});
|
|
204
|
+
return published.request
|
|
205
|
+
? { ...published.receipt, request: published.request }
|
|
206
|
+
: published.receipt;
|
|
185
207
|
} catch (error) {
|
|
186
|
-
throw
|
|
187
|
-
"VALIDATION",
|
|
188
|
-
error instanceof Error ? error.message : String(error)
|
|
189
|
-
);
|
|
208
|
+
throw requestErrorToCli(error);
|
|
190
209
|
}
|
|
191
|
-
const absPath = join(collection.path, plan.relPath);
|
|
192
|
-
|
|
193
|
-
const lockPath = join(
|
|
194
|
-
dirname(getIndexDbPath(options.indexName)),
|
|
195
|
-
".mcp-write.lock"
|
|
196
|
-
);
|
|
197
|
-
return await withWriteLock(lockPath, async () => {
|
|
198
|
-
if (plan.openedExisting) {
|
|
199
|
-
const existingDoc = await store.getDocument(
|
|
200
|
-
collection.name,
|
|
201
|
-
plan.relPath
|
|
202
|
-
);
|
|
203
|
-
if (!existingDoc.ok) {
|
|
204
|
-
throw new Error(existingDoc.error.message);
|
|
205
|
-
}
|
|
206
|
-
return buildCaptureReceipt({
|
|
207
|
-
plan,
|
|
208
|
-
absPath,
|
|
209
|
-
docid: existingDoc.value?.docid,
|
|
210
|
-
sync: existingDoc.value
|
|
211
|
-
? { status: "completed" }
|
|
212
|
-
: {
|
|
213
|
-
status: "skipped",
|
|
214
|
-
reason: "Existing file is not indexed yet.",
|
|
215
|
-
},
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
await mkdir(dirname(absPath), { recursive: true });
|
|
220
|
-
await writeCapturePlanFile(plan, absPath);
|
|
221
|
-
const syncResults = await defaultSyncService.syncFiles(
|
|
222
|
-
collection,
|
|
223
|
-
store,
|
|
224
|
-
[plan.relPath],
|
|
225
|
-
withContentTypeRules(
|
|
226
|
-
{
|
|
227
|
-
runUpdateCmd: false,
|
|
228
|
-
gitPull: false,
|
|
229
|
-
},
|
|
230
|
-
config
|
|
231
|
-
)
|
|
232
|
-
);
|
|
233
|
-
const syncResult = syncResults[0];
|
|
234
|
-
const docResult = await store.getDocument(collection.name, plan.relPath);
|
|
235
|
-
const docid = docResult.ok ? docResult.value?.docid : undefined;
|
|
236
|
-
return buildCaptureReceipt({
|
|
237
|
-
plan,
|
|
238
|
-
absPath,
|
|
239
|
-
docid: syncResult?.docid ?? docid,
|
|
240
|
-
sync:
|
|
241
|
-
syncResult?.status === "error"
|
|
242
|
-
? {
|
|
243
|
-
status: "failed",
|
|
244
|
-
error:
|
|
245
|
-
syncResult.errorMessage ??
|
|
246
|
-
syncResult.errorCode ??
|
|
247
|
-
"Unknown sync error",
|
|
248
|
-
}
|
|
249
|
-
: { status: "completed" },
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
210
|
} finally {
|
|
253
211
|
await store.close();
|
|
254
212
|
}
|
|
255
213
|
}
|
|
256
214
|
|
|
257
215
|
export function formatCaptureReceipt(
|
|
258
|
-
receipt: CaptureReceipt,
|
|
216
|
+
receipt: CaptureReceipt & { request?: RequestReceiptInfo },
|
|
259
217
|
options: { json?: boolean; quiet?: boolean } = {}
|
|
260
218
|
): string {
|
|
261
219
|
if (options.json) {
|
|
@@ -278,5 +236,6 @@ export function formatCaptureReceipt(
|
|
|
278
236
|
if (receipt.source.url) {
|
|
279
237
|
lines.push(`Source: ${receipt.source.url}`);
|
|
280
238
|
}
|
|
239
|
+
if (receipt.request) lines.push(formatRequestReceiptLine(receipt.request));
|
|
281
240
|
return lines.join("\n");
|
|
282
241
|
}
|
|
@@ -3,6 +3,7 @@ import type { HttpGatewayOverrides } from "../../mcp/http-security";
|
|
|
3
3
|
import type { BackgroundRuntimeResult } from "../../serve/background-runtime";
|
|
4
4
|
import type { FindingsPassResult } from "../../serve/findings-pass";
|
|
5
5
|
import type { ResidentRuntime } from "../../serve/resident-runtime";
|
|
6
|
+
import type { SessionAutomationRunResult } from "../../sessions/types";
|
|
6
7
|
|
|
7
8
|
import {
|
|
8
9
|
enforceCollectionEgress,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
import { startBackgroundRuntime } from "../../serve/background-runtime";
|
|
18
19
|
import { handleResidentStatus, handleStatus } from "../../serve/routes/api";
|
|
19
20
|
import { createMcpHttpGateway } from "../../serve/routes/mcp";
|
|
21
|
+
import { SessionsError } from "../../sessions/types";
|
|
20
22
|
|
|
21
23
|
export interface DaemonOptions extends HttpGatewayOverrides {
|
|
22
24
|
configPath?: string;
|
|
@@ -67,6 +69,31 @@ export function logFindingsPassResult(
|
|
|
67
69
|
);
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
/** Content-free: counts and reason codes only; clean no-ops stay silent. */
|
|
73
|
+
export function logSessionAutomationResult(
|
|
74
|
+
result: SessionAutomationRunResult,
|
|
75
|
+
logger: DaemonLogger,
|
|
76
|
+
options: { quiet?: boolean }
|
|
77
|
+
): void {
|
|
78
|
+
if (!result.ran) return;
|
|
79
|
+
if (result.outcome === "failed") {
|
|
80
|
+
logger.error(
|
|
81
|
+
`session automation ${result.profileId}: failed (${result.reason ?? "unknown"})`
|
|
82
|
+
);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (result.outcome === "up_to_date" || options.quiet) return;
|
|
86
|
+
let imported = 0;
|
|
87
|
+
let updated = 0;
|
|
88
|
+
for (const receipt of result.receipts) {
|
|
89
|
+
imported += receipt.counts.imported;
|
|
90
|
+
updated += receipt.counts.updated;
|
|
91
|
+
}
|
|
92
|
+
logger.log(
|
|
93
|
+
`session automation ${result.profileId}: ${result.outcome}${result.reason ? ` (${result.reason})` : ""}; ${imported} threads imported, ${updated} updated`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
70
97
|
function formatCollectionSyncSummary(result: CollectionSyncResult): string {
|
|
71
98
|
return `${result.collection}: ${result.filesAdded} added, ${result.filesUpdated} updated, ${result.filesUnchanged} unchanged, ${result.filesErrored} errors`;
|
|
72
99
|
}
|
|
@@ -175,6 +202,20 @@ export async function daemon(
|
|
|
175
202
|
quiet: options.quiet,
|
|
176
203
|
verbose: options.verbose,
|
|
177
204
|
}),
|
|
205
|
+
onSessionAutomationResult: (result) =>
|
|
206
|
+
logSessionAutomationResult(result, logger, { quiet: options.quiet }),
|
|
207
|
+
onSessionAutomationError: (error) => {
|
|
208
|
+
// Busy state (a run or state change in progress) is retried next tick.
|
|
209
|
+
if (error instanceof SessionsError && error.code === "SESSIONS_BUSY") {
|
|
210
|
+
if (options.verbose) {
|
|
211
|
+
logger.log("session automation: run in progress; next tick retries");
|
|
212
|
+
}
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
logger.error(
|
|
216
|
+
`session automation tick failed: ${error instanceof SessionsError ? error.code : "runtime error"}`
|
|
217
|
+
);
|
|
218
|
+
},
|
|
178
219
|
watchCallbacks: {
|
|
179
220
|
onSyncStart: ({ collection, relPaths }) => {
|
|
180
221
|
if (!options.quiet) {
|
|
@@ -12,6 +12,10 @@ import { arch, platform } from "node:os";
|
|
|
12
12
|
|
|
13
13
|
import type { Config } from "../../config/types";
|
|
14
14
|
import type { ActivationStatus } from "../../core/activation-status";
|
|
15
|
+
import type {
|
|
16
|
+
VectorPartitionStatus,
|
|
17
|
+
VectorRuntimeStatus,
|
|
18
|
+
} from "../../store/vector/status";
|
|
15
19
|
|
|
16
20
|
import { getIndexDbPath, getModelsCachePath } from "../../app/constants";
|
|
17
21
|
import {
|
|
@@ -26,6 +30,7 @@ import {
|
|
|
26
30
|
readFindingsRunStatus,
|
|
27
31
|
resolveFindingsSchedule,
|
|
28
32
|
} from "../../core/findings-run-state";
|
|
33
|
+
import { formatVectorPartitionLines } from "../../core/vector-partition-status";
|
|
29
34
|
import { getCodeChunkingStatus } from "../../ingestion/chunker";
|
|
30
35
|
import { ModelCache } from "../../llm/cache";
|
|
31
36
|
import { getActivePreset, resolveModelUri } from "../../llm/registry";
|
|
@@ -57,6 +62,9 @@ export interface DoctorCheck {
|
|
|
57
62
|
details?: string[];
|
|
58
63
|
/** Embedding fingerprint diagnostics for machine consumers */
|
|
59
64
|
embeddingFingerprint?: EmbeddingFingerprintHealth;
|
|
65
|
+
/** Vector partitions; `retrieval` marks the one status and search use */
|
|
66
|
+
vectorPartitions?: VectorPartitionStatus[];
|
|
67
|
+
vectorRuntime?: VectorRuntimeStatus;
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
export interface EmbeddingFingerprintGroup {
|
|
@@ -261,16 +269,18 @@ function describeFingerprintGroup(group: EmbeddingFingerprintGroup): string {
|
|
|
261
269
|
async function checkEmbeddingFingerprints(
|
|
262
270
|
config: Config,
|
|
263
271
|
indexName?: string
|
|
264
|
-
): Promise<DoctorCheck> {
|
|
272
|
+
): Promise<DoctorCheck[]> {
|
|
265
273
|
const dbPath = getIndexDbPath(indexName);
|
|
266
274
|
try {
|
|
267
275
|
await stat(dbPath);
|
|
268
276
|
} catch {
|
|
269
|
-
return
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
277
|
+
return [
|
|
278
|
+
{
|
|
279
|
+
name: "embedding-fingerprint",
|
|
280
|
+
status: "warn",
|
|
281
|
+
message: "Database not found. Run: gno init",
|
|
282
|
+
},
|
|
283
|
+
];
|
|
274
284
|
}
|
|
275
285
|
|
|
276
286
|
const store = new SqliteAdapter();
|
|
@@ -283,12 +293,14 @@ async function checkEmbeddingFingerprints(
|
|
|
283
293
|
config.busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS
|
|
284
294
|
);
|
|
285
295
|
if (!openResult.ok) {
|
|
286
|
-
return
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
296
|
+
return [
|
|
297
|
+
{
|
|
298
|
+
name: "embedding-fingerprint",
|
|
299
|
+
status: "warn",
|
|
300
|
+
message: `Fingerprint health unavailable: ${openResult.error.message}`,
|
|
301
|
+
details: ["Run: gno doctor --json", "Then run: gno embed"],
|
|
302
|
+
},
|
|
303
|
+
];
|
|
292
304
|
}
|
|
293
305
|
|
|
294
306
|
try {
|
|
@@ -297,12 +309,14 @@ async function checkEmbeddingFingerprints(
|
|
|
297
309
|
const currentFingerprint = getStoredEmbeddingFingerprint(db, model);
|
|
298
310
|
const statusResult = await store.getStatus({ embedModel: model });
|
|
299
311
|
if (!statusResult.ok) {
|
|
300
|
-
return
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
312
|
+
return [
|
|
313
|
+
{
|
|
314
|
+
name: "embedding-fingerprint",
|
|
315
|
+
status: "warn",
|
|
316
|
+
message: `Fingerprint health unavailable: ${statusResult.error.message}`,
|
|
317
|
+
details: ["Run: gno embed"],
|
|
318
|
+
},
|
|
319
|
+
];
|
|
306
320
|
}
|
|
307
321
|
|
|
308
322
|
const legacyChunks =
|
|
@@ -389,13 +403,33 @@ async function checkEmbeddingFingerprints(
|
|
|
389
403
|
}
|
|
390
404
|
}
|
|
391
405
|
|
|
392
|
-
|
|
406
|
+
const fingerprintCheck: DoctorCheck = {
|
|
393
407
|
name: "embedding-fingerprint",
|
|
394
408
|
status: hasWarnings ? "warn" : "ok",
|
|
395
409
|
message,
|
|
396
410
|
details: details.length > 0 ? details : undefined,
|
|
397
411
|
embeddingFingerprint: health,
|
|
398
412
|
};
|
|
413
|
+
const partitions = statusResult.value.vectorPartitions;
|
|
414
|
+
if (!partitions) return [fingerprintCheck];
|
|
415
|
+
const runtime = statusResult.value.vectorRuntime;
|
|
416
|
+
const partitionLines = formatVectorPartitionLines(partitions, runtime);
|
|
417
|
+
const retrieval = partitions.find((p) => p.retrieval);
|
|
418
|
+
return [
|
|
419
|
+
fingerprintCheck,
|
|
420
|
+
{
|
|
421
|
+
name: "vector-partitions",
|
|
422
|
+
status: partitionLines.length ? "warn" : "ok",
|
|
423
|
+
message: retrieval
|
|
424
|
+
? `this runtime's retrieval uses ${retrieval.id.slice(0, 12)} (${retrieval.state}, ${retrieval.owners} chunks, ${retrieval.provenance}); ${partitions.length - 1} other partition(s)`
|
|
425
|
+
: runtime?.state === "unavailable"
|
|
426
|
+
? `this runtime uses lexical retrieval only: ${runtime.reason}`
|
|
427
|
+
: `this runtime has not resolved a partition yet; ${partitions.length} partition(s)`,
|
|
428
|
+
details: partitionLines.length ? partitionLines.slice(1) : undefined,
|
|
429
|
+
vectorPartitions: partitions,
|
|
430
|
+
vectorRuntime: runtime,
|
|
431
|
+
},
|
|
432
|
+
];
|
|
399
433
|
} finally {
|
|
400
434
|
await store.close();
|
|
401
435
|
}
|
|
@@ -669,7 +703,7 @@ export async function doctor(
|
|
|
669
703
|
checks.push(checkCodeChunking());
|
|
670
704
|
|
|
671
705
|
// Embedding fingerprint freshness
|
|
672
|
-
checks.push(await checkEmbeddingFingerprints(config, options.indexName));
|
|
706
|
+
checks.push(...(await checkEmbeddingFingerprints(config, options.indexName)));
|
|
673
707
|
|
|
674
708
|
// Scheduled findings pass (daemon-only, opt-in)
|
|
675
709
|
checks.push(await checkFindingsPass(config, options.indexName));
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
import type { Database } from "bun:sqlite";
|
|
9
9
|
|
|
10
|
+
// node:readline/promises is the platform line-input API; Bun has no equivalent
|
|
11
|
+
import { createInterface } from "node:readline/promises";
|
|
12
|
+
|
|
10
13
|
import type { EmbeddingPort } from "../../llm/types";
|
|
11
14
|
import type { StoreResult } from "../../store/types";
|
|
12
15
|
|
|
@@ -77,8 +80,10 @@ export interface EmbedOptions extends CliWriteLeaseOptions {
|
|
|
77
80
|
force?: boolean;
|
|
78
81
|
/** Show what would be done without embedding */
|
|
79
82
|
dryRun?: boolean;
|
|
80
|
-
/** Skip confirmation prompts */
|
|
83
|
+
/** Skip confirmation prompts (never confirms a separate vector partition) */
|
|
81
84
|
yes?: boolean;
|
|
85
|
+
/** Explicitly confirm building a separate vector partition */
|
|
86
|
+
newPartition?: boolean;
|
|
82
87
|
/** Output as JSON */
|
|
83
88
|
json?: boolean;
|
|
84
89
|
/** Verbose error logging */
|
|
@@ -450,6 +455,24 @@ async function initEmbedContext(
|
|
|
450
455
|
return { ok: true, config, modelUri, store };
|
|
451
456
|
}
|
|
452
457
|
|
|
458
|
+
async function confirmSeparatePartition(message: string): Promise<boolean> {
|
|
459
|
+
process.stderr.write(`${message}\n`);
|
|
460
|
+
const prompt = createInterface({
|
|
461
|
+
input: process.stdin,
|
|
462
|
+
output: process.stderr,
|
|
463
|
+
});
|
|
464
|
+
try {
|
|
465
|
+
const answer = await prompt.question(
|
|
466
|
+
"Build the separate partition? [y/N] "
|
|
467
|
+
);
|
|
468
|
+
return /^(?:y|yes)$/i.test(answer.trim());
|
|
469
|
+
} catch {
|
|
470
|
+
return false;
|
|
471
|
+
} finally {
|
|
472
|
+
prompt.close();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
453
476
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
454
477
|
// Main Command
|
|
455
478
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -574,7 +597,7 @@ export async function embed(options: EmbedOptions = {}): Promise<EmbedResult> {
|
|
|
574
597
|
}
|
|
575
598
|
vectorIndex = vectorResult.value;
|
|
576
599
|
|
|
577
|
-
const
|
|
600
|
+
const backlogDeps = {
|
|
578
601
|
statsPort: stats,
|
|
579
602
|
embedPort,
|
|
580
603
|
vectorIndex,
|
|
@@ -582,7 +605,22 @@ export async function embed(options: EmbedOptions = {}): Promise<EmbedResult> {
|
|
|
582
605
|
collection: options.collection,
|
|
583
606
|
batchSize,
|
|
584
607
|
force,
|
|
585
|
-
|
|
608
|
+
allowNewPartition: options.newPartition,
|
|
609
|
+
};
|
|
610
|
+
let prepared = await prepareEmbeddingBacklog(backlogDeps);
|
|
611
|
+
if (
|
|
612
|
+
!prepared.ok &&
|
|
613
|
+
prepared.error.code === "VECTOR_PARTITION_FORK" &&
|
|
614
|
+
!dryRun &&
|
|
615
|
+
!options.json &&
|
|
616
|
+
!options.yes &&
|
|
617
|
+
process.stdin.isTTY &&
|
|
618
|
+
(await confirmSeparatePartition(prepared.error.message))
|
|
619
|
+
)
|
|
620
|
+
prepared = await prepareEmbeddingBacklog({
|
|
621
|
+
...backlogDeps,
|
|
622
|
+
allowNewPartition: true,
|
|
623
|
+
});
|
|
586
624
|
if (!prepared.ok)
|
|
587
625
|
return { success: false, error: prepared.error.message };
|
|
588
626
|
if (prepared.value.variantStore) {
|
package/src/cli/commands/ls.ts
CHANGED
|
@@ -14,6 +14,8 @@ import { initStore } from "./shared";
|
|
|
14
14
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
15
|
|
|
16
16
|
export interface LsCommandOptions {
|
|
17
|
+
/** Index name (defaults to "default"). */
|
|
18
|
+
indexName?: string;
|
|
17
19
|
/** Override config path */
|
|
18
20
|
configPath?: string;
|
|
19
21
|
/** Max results (default 20) */
|
|
@@ -111,6 +113,7 @@ export async function ls(
|
|
|
111
113
|
|
|
112
114
|
const initResult = await initStore({
|
|
113
115
|
configPath: options.configPath,
|
|
116
|
+
indexName: options.indexName,
|
|
114
117
|
syncConfig: false,
|
|
115
118
|
});
|
|
116
119
|
if (!initResult.ok) {
|
|
@@ -24,11 +24,15 @@ import {
|
|
|
24
24
|
type RememberInput,
|
|
25
25
|
type RememberResult,
|
|
26
26
|
} from "../../core/memory";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
formatRequestReceiptLine,
|
|
29
|
+
localRequestLedger,
|
|
30
|
+
} from "../../core/request-receipts";
|
|
28
31
|
import { LlmAdapter } from "../../llm/nodeLlamaCpp/adapter";
|
|
29
32
|
import { resolveModelUri } from "../../llm/registry";
|
|
30
33
|
import { createVectorIndexPort } from "../../store/vector";
|
|
31
34
|
import { CliError, type CliErrorCode } from "../errors";
|
|
35
|
+
import { requestErrorToCli } from "./request-status";
|
|
32
36
|
import { initStore } from "./shared";
|
|
33
37
|
|
|
34
38
|
/** Environment overrides for the identity defaults. */
|
|
@@ -62,6 +66,7 @@ export interface RememberCliOptions
|
|
|
62
66
|
receipt?: string;
|
|
63
67
|
derivedFrom?: string[];
|
|
64
68
|
source?: string;
|
|
69
|
+
requestId?: string;
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
export interface RecallCliOptions
|
|
@@ -254,7 +259,7 @@ const MEMORY_ERROR_TO_CLI: Record<MemoryErrorCode, CliErrorCode> = {
|
|
|
254
259
|
|
|
255
260
|
/** Map a core `MemoryError` onto the CLI error model (code carried in details). */
|
|
256
261
|
export function toCliError(error: unknown): unknown {
|
|
257
|
-
if (!(error instanceof MemoryError)) return error;
|
|
262
|
+
if (!(error instanceof MemoryError)) return requestErrorToCli(error);
|
|
258
263
|
return new CliError(MEMORY_ERROR_TO_CLI[error.code], error.message, {
|
|
259
264
|
details: { memoryCode: error.code },
|
|
260
265
|
});
|
|
@@ -322,11 +327,13 @@ async function openMemoryRuntime(
|
|
|
322
327
|
await embedResult.value.dispose();
|
|
323
328
|
}
|
|
324
329
|
}
|
|
330
|
+
const ledger = localRequestLedger(getIndexDbPath(options.indexName));
|
|
325
331
|
const service = new MemoryService({
|
|
326
332
|
store,
|
|
327
333
|
config,
|
|
328
334
|
collections,
|
|
329
|
-
lockPath:
|
|
335
|
+
lockPath: ledger.lockPath,
|
|
336
|
+
requests: ledger,
|
|
330
337
|
embedPort,
|
|
331
338
|
vectorIndex,
|
|
332
339
|
});
|
|
@@ -361,6 +368,7 @@ export async function remember(
|
|
|
361
368
|
? options.derivedFrom
|
|
362
369
|
: undefined,
|
|
363
370
|
source: options.source,
|
|
371
|
+
requestId: options.requestId,
|
|
364
372
|
};
|
|
365
373
|
return await runtime.service.remember(input);
|
|
366
374
|
} catch (error) {
|
|
@@ -457,6 +465,7 @@ export function formatRememberResult(
|
|
|
457
465
|
lines.push(`Sync: ${result.sync.status}`);
|
|
458
466
|
}
|
|
459
467
|
lines.push(`Matching: ${formatMatching(result.matching)}`);
|
|
468
|
+
if (result.request) lines.push(formatRequestReceiptLine(result.request));
|
|
460
469
|
return lines.join("\n");
|
|
461
470
|
}
|
|
462
471
|
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
createVectorIndexPort,
|
|
38
38
|
type VectorIndexPort,
|
|
39
39
|
} from "../../store/vector";
|
|
40
|
+
import { VECTOR_RUNTIME_INCOMPATIBLE } from "../../store/vector/variant-search";
|
|
40
41
|
import { getGlobals } from "../program";
|
|
41
42
|
import {
|
|
42
43
|
createProgressRenderer,
|
|
@@ -517,6 +518,10 @@ export function formatQuery(
|
|
|
517
518
|
}
|
|
518
519
|
// Output explain to stderr if present (async but best-effort)
|
|
519
520
|
outputExplainToStderr(result.data);
|
|
521
|
+
if (options.format !== "json")
|
|
522
|
+
for (const warning of result.data.meta.warnings ?? [])
|
|
523
|
+
if (warning.code === VECTOR_RUNTIME_INCOMPATIBLE)
|
|
524
|
+
process.stderr.write(`Warning: ${warning.message}\n`);
|
|
520
525
|
|
|
521
526
|
// Use shared formatter for consistent output
|
|
522
527
|
// Dynamic import to keep module loading fast
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gno request-status: look up a write request ID before retrying it, plus
|
|
3
|
+
* the CLI mapping for request receipt errors.
|
|
4
|
+
*
|
|
5
|
+
* @module src/cli/commands/request-status
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getIndexDbPath } from "../../app/constants";
|
|
9
|
+
import {
|
|
10
|
+
formatRequestStatus,
|
|
11
|
+
localRequestLedger,
|
|
12
|
+
readRequestStatus,
|
|
13
|
+
RequestReceiptError,
|
|
14
|
+
type RequestReceiptErrorCode,
|
|
15
|
+
type RequestStatusResult,
|
|
16
|
+
} from "../../core/request-receipts";
|
|
17
|
+
import { CliError, type CliErrorCode } from "../errors";
|
|
18
|
+
|
|
19
|
+
const REQUEST_ERROR_TO_CLI: Record<RequestReceiptErrorCode, CliErrorCode> = {
|
|
20
|
+
REQUEST_ID_INVALID: "VALIDATION",
|
|
21
|
+
REQUEST_ID_CONFLICT: "VALIDATION",
|
|
22
|
+
REQUEST_EXPIRED: "VALIDATION",
|
|
23
|
+
// Accepted and still in progress elsewhere: exit 4 like lease contention.
|
|
24
|
+
REQUEST_PENDING: "BUSY",
|
|
25
|
+
REQUEST_RECOVERY_CONFLICT: "RUNTIME",
|
|
26
|
+
REQUEST_CAPACITY_EXHAUSTED: "RUNTIME",
|
|
27
|
+
REQUEST_LEDGER_UNAVAILABLE: "RUNTIME",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Map a request receipt error onto the CLI error model (code in details). */
|
|
31
|
+
export function requestErrorToCli(error: unknown): unknown {
|
|
32
|
+
if (!(error instanceof RequestReceiptError)) return error;
|
|
33
|
+
return new CliError(REQUEST_ERROR_TO_CLI[error.code], error.message, {
|
|
34
|
+
details: { requestCode: error.code },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function requestStatus(options: {
|
|
39
|
+
requestId: string;
|
|
40
|
+
indexName?: string;
|
|
41
|
+
}): Promise<RequestStatusResult> {
|
|
42
|
+
try {
|
|
43
|
+
return await readRequestStatus({
|
|
44
|
+
...localRequestLedger(getIndexDbPath(options.indexName)),
|
|
45
|
+
requestId: options.requestId,
|
|
46
|
+
});
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw requestErrorToCli(error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function formatRequestStatusOutput(
|
|
53
|
+
result: RequestStatusResult,
|
|
54
|
+
options: { json?: boolean } = {}
|
|
55
|
+
): string {
|
|
56
|
+
return options.json
|
|
57
|
+
? JSON.stringify(result, null, 2)
|
|
58
|
+
: formatRequestStatus(result);
|
|
59
|
+
}
|