@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
package/src/sdk/client.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
GnoCaptureOptions,
|
|
22
22
|
GnoCaptureResult,
|
|
23
23
|
GnoClient,
|
|
24
|
+
GnoRequestStatusResult,
|
|
24
25
|
GnoCreateFolderOptions,
|
|
25
26
|
GnoCreateFolderResult,
|
|
26
27
|
GnoCreateNoteOptions,
|
|
@@ -49,6 +50,11 @@ import type {
|
|
|
49
50
|
GnoRenameNoteApplyOptions,
|
|
50
51
|
GnoRenameNoteOptions,
|
|
51
52
|
GnoSearchOptions,
|
|
53
|
+
GnoSessionsAutomationRunResult,
|
|
54
|
+
GnoSessionsDiscovery,
|
|
55
|
+
GnoSessionsImportInput,
|
|
56
|
+
GnoSessionsImportReceipt,
|
|
57
|
+
GnoSessionsStatus,
|
|
52
58
|
GnoUpdateOptions,
|
|
53
59
|
GnoVectorSearchOptions,
|
|
54
60
|
KnowledgeChangesResult,
|
|
@@ -90,17 +96,17 @@ import { buildVerifiedAsk } from "../app/verified-ask";
|
|
|
90
96
|
import {
|
|
91
97
|
buildContentTypeBoostStatus,
|
|
92
98
|
ConfigSchema,
|
|
99
|
+
getConfigPaths,
|
|
93
100
|
loadConfig,
|
|
94
101
|
normalizeConfigContentTypes,
|
|
95
102
|
normalizeContentTypes,
|
|
96
103
|
} from "../config";
|
|
97
104
|
import {
|
|
98
|
-
|
|
99
|
-
type CapturePlan,
|
|
105
|
+
CaptureSyncError,
|
|
100
106
|
listCaptureDiskRelPaths,
|
|
101
107
|
planCapture,
|
|
102
108
|
} from "../core/capture";
|
|
103
|
-
import {
|
|
109
|
+
import { publishCapture } from "../core/capture-publish";
|
|
104
110
|
import { projectCollectionEgressPolicy } from "../core/collection-egress-policy-projection";
|
|
105
111
|
import { CollectionEgressPolicyService } from "../core/collection-egress-policy-service";
|
|
106
112
|
import { applyConfigChange } from "../core/config-mutation";
|
|
@@ -140,6 +146,12 @@ import {
|
|
|
140
146
|
ProjectAffinityInputError,
|
|
141
147
|
resolveRemoteProjectAffinity,
|
|
142
148
|
} from "../core/project-affinity-surface";
|
|
149
|
+
import {
|
|
150
|
+
localRequestLedger,
|
|
151
|
+
readRequestStatus,
|
|
152
|
+
RequestReceiptError,
|
|
153
|
+
type RequestReceiptErrorCode,
|
|
154
|
+
} from "../core/request-receipts";
|
|
143
155
|
import { RetrievalTraceManagementService } from "../core/retrieval-trace-management";
|
|
144
156
|
import {
|
|
145
157
|
finishRetrievalTraceAfterError,
|
|
@@ -169,7 +181,6 @@ import {
|
|
|
169
181
|
normalizeMetadataPredicate,
|
|
170
182
|
type MetadataPredicate,
|
|
171
183
|
} from "../core/typed-metadata";
|
|
172
|
-
import { writeLeasePath } from "../core/write-lease";
|
|
173
184
|
import {
|
|
174
185
|
defaultSyncService,
|
|
175
186
|
type SyncResult,
|
|
@@ -193,6 +204,10 @@ import { searchHybrid } from "../pipeline/hybrid";
|
|
|
193
204
|
import { RequestHydration } from "../pipeline/hydration";
|
|
194
205
|
import { searchBm25 } from "../pipeline/search";
|
|
195
206
|
import { searchVectorWithEmbedding } from "../pipeline/vsearch";
|
|
207
|
+
import { runAutomationProfile } from "../sessions/automation";
|
|
208
|
+
import { assertSessionBinding } from "../sessions/binding";
|
|
209
|
+
import { SessionsService } from "../sessions/service";
|
|
210
|
+
import { SESSIONS_VALIDATION_CODES, SessionsError } from "../sessions/types";
|
|
196
211
|
import { SqliteAdapter } from "../store/sqlite/adapter";
|
|
197
212
|
import { openScopedIndexStore } from "../store/sqlite/scoped-index";
|
|
198
213
|
import { createVectorIndexPort } from "../store/vector";
|
|
@@ -314,6 +329,19 @@ async function resolveClientState(
|
|
|
314
329
|
options.indexName ?? DEFAULT_INDEX_NAME
|
|
315
330
|
);
|
|
316
331
|
const dbPath = options.dbPath ?? getIndexDbPath(indexName);
|
|
332
|
+
try {
|
|
333
|
+
await assertSessionBinding({
|
|
334
|
+
config,
|
|
335
|
+
configPath:
|
|
336
|
+
configSource === "inline"
|
|
337
|
+
? "<inline-config>"
|
|
338
|
+
: (options.configPath ?? getConfigPaths().configFile),
|
|
339
|
+
indexName,
|
|
340
|
+
dbPath,
|
|
341
|
+
});
|
|
342
|
+
} catch (cause) {
|
|
343
|
+
throw toSessionsSdkError(cause);
|
|
344
|
+
}
|
|
317
345
|
await mkdir(dirname(dbPath), { recursive: true });
|
|
318
346
|
|
|
319
347
|
const store = new SqliteAdapter();
|
|
@@ -337,6 +365,16 @@ async function resolveClientState(
|
|
|
337
365
|
};
|
|
338
366
|
}
|
|
339
367
|
|
|
368
|
+
/** Map a sessions error onto the SDK family; the sessions code survives in `details.code`. */
|
|
369
|
+
function toSessionsSdkError(cause: unknown): unknown {
|
|
370
|
+
if (!(cause instanceof SessionsError)) return cause;
|
|
371
|
+
return sdkError(
|
|
372
|
+
SESSIONS_VALIDATION_CODES.has(cause.code) ? "VALIDATION" : "RUNTIME",
|
|
373
|
+
cause.message,
|
|
374
|
+
{ cause, details: { code: cause.code } }
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
|
|
340
378
|
/** SDK error family per memory code; exhaustive so a new code fails to compile. */
|
|
341
379
|
const MEMORY_ERROR_TO_SDK: Readonly<Record<MemoryErrorCode, GnoSdkErrorCode>> =
|
|
342
380
|
{
|
|
@@ -363,9 +401,28 @@ const MEMORY_ERROR_TO_SDK: Readonly<Record<MemoryErrorCode, GnoSdkErrorCode>> =
|
|
|
363
401
|
MEMORY_QUERY_FAILED: "RUNTIME",
|
|
364
402
|
};
|
|
365
403
|
|
|
404
|
+
const REQUEST_ERROR_TO_SDK: Record<RequestReceiptErrorCode, GnoSdkErrorCode> = {
|
|
405
|
+
REQUEST_ID_INVALID: "VALIDATION",
|
|
406
|
+
REQUEST_ID_CONFLICT: "VALIDATION",
|
|
407
|
+
REQUEST_EXPIRED: "VALIDATION",
|
|
408
|
+
REQUEST_PENDING: "RUNTIME",
|
|
409
|
+
REQUEST_RECOVERY_CONFLICT: "RUNTIME",
|
|
410
|
+
REQUEST_CAPACITY_EXHAUSTED: "RUNTIME",
|
|
411
|
+
REQUEST_LEDGER_UNAVAILABLE: "RUNTIME",
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
/** Request receipt errors carry their stable code in `details.code`. */
|
|
415
|
+
function toRequestSdkError(cause: unknown): unknown {
|
|
416
|
+
if (!(cause instanceof RequestReceiptError)) return cause;
|
|
417
|
+
return sdkError(REQUEST_ERROR_TO_SDK[cause.code], cause.message, {
|
|
418
|
+
cause,
|
|
419
|
+
details: { code: cause.code },
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
366
423
|
/** Map a core MemoryError onto the SDK error family; the memory code survives in `details.code`. */
|
|
367
424
|
function toMemorySdkError(cause: unknown): unknown {
|
|
368
|
-
if (!(cause instanceof MemoryError)) return cause;
|
|
425
|
+
if (!(cause instanceof MemoryError)) return toRequestSdkError(cause);
|
|
369
426
|
return sdkError(MEMORY_ERROR_TO_SDK[cause.code], cause.message, {
|
|
370
427
|
cause,
|
|
371
428
|
details: { code: cause.code },
|
|
@@ -1345,6 +1402,8 @@ class GnoClientImpl implements GnoClient {
|
|
|
1345
1402
|
requestedIndexName: resolution.value.indexName,
|
|
1346
1403
|
config: this.config,
|
|
1347
1404
|
configPath: this.configPath,
|
|
1405
|
+
}).catch((cause: unknown) => {
|
|
1406
|
+
throw toSessionsSdkError(cause);
|
|
1348
1407
|
});
|
|
1349
1408
|
try {
|
|
1350
1409
|
const result = await getDocumentByRef(
|
|
@@ -1384,6 +1443,8 @@ class GnoClientImpl implements GnoClient {
|
|
|
1384
1443
|
requestedIndexName: resolution.value.indexName,
|
|
1385
1444
|
config: this.config,
|
|
1386
1445
|
configPath: this.configPath,
|
|
1446
|
+
}).catch((cause: unknown) => {
|
|
1447
|
+
throw toSessionsSdkError(cause);
|
|
1387
1448
|
});
|
|
1388
1449
|
try {
|
|
1389
1450
|
const result = await multiGetDocuments(
|
|
@@ -1790,7 +1851,8 @@ class GnoClientImpl implements GnoClient {
|
|
|
1790
1851
|
store: this.store,
|
|
1791
1852
|
config: this.config,
|
|
1792
1853
|
collections: this.config.collections,
|
|
1793
|
-
lockPath:
|
|
1854
|
+
lockPath: this.requestLedger().lockPath,
|
|
1855
|
+
requests: this.requestLedger(),
|
|
1794
1856
|
embedPort: ports.embedPort,
|
|
1795
1857
|
vectorIndex: ports.vectorIndex,
|
|
1796
1858
|
})
|
|
@@ -1802,6 +1864,62 @@ class GnoClientImpl implements GnoClient {
|
|
|
1802
1864
|
}
|
|
1803
1865
|
}
|
|
1804
1866
|
|
|
1867
|
+
private sessionsService(): SessionsService {
|
|
1868
|
+
this.assertOpen();
|
|
1869
|
+
return new SessionsService({
|
|
1870
|
+
config: this.config,
|
|
1871
|
+
configPath: this.configPath ?? getConfigPaths().configFile,
|
|
1872
|
+
indexName: this.indexName,
|
|
1873
|
+
store: this.store,
|
|
1874
|
+
});
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
async sessionsStatus(): Promise<GnoSessionsStatus> {
|
|
1878
|
+
try {
|
|
1879
|
+
return await this.sessionsService().status();
|
|
1880
|
+
} catch (cause) {
|
|
1881
|
+
throw toSessionsSdkError(cause);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
async discoverSessions(): Promise<GnoSessionsDiscovery> {
|
|
1886
|
+
try {
|
|
1887
|
+
return await this.sessionsService().discover();
|
|
1888
|
+
} catch (cause) {
|
|
1889
|
+
throw toSessionsSdkError(cause);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
async importSessions(
|
|
1894
|
+
input: GnoSessionsImportInput
|
|
1895
|
+
): Promise<GnoSessionsImportReceipt> {
|
|
1896
|
+
try {
|
|
1897
|
+
// The SDK runs in the owner's process, so explicit paths are allowed.
|
|
1898
|
+
return await this.sessionsService().import(input, { allowPaths: true });
|
|
1899
|
+
} catch (cause) {
|
|
1900
|
+
throw toSessionsSdkError(cause);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
async runSessionsAutomation(input: {
|
|
1905
|
+
profileId: string;
|
|
1906
|
+
}): Promise<GnoSessionsAutomationRunResult> {
|
|
1907
|
+
this.assertOpen();
|
|
1908
|
+
try {
|
|
1909
|
+
return await runAutomationProfile(
|
|
1910
|
+
{
|
|
1911
|
+
configPath: this.configPath ?? getConfigPaths().configFile,
|
|
1912
|
+
indexName: this.indexName,
|
|
1913
|
+
store: this.store,
|
|
1914
|
+
},
|
|
1915
|
+
input.profileId,
|
|
1916
|
+
{ trigger: "manual" }
|
|
1917
|
+
);
|
|
1918
|
+
} catch (cause) {
|
|
1919
|
+
throw toSessionsSdkError(cause);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1805
1923
|
async remember(input: GnoRememberInput): Promise<GnoRememberResult> {
|
|
1806
1924
|
return this.withMemoryService(input?.collection, (service) =>
|
|
1807
1925
|
service.remember(input)
|
|
@@ -1823,98 +1941,76 @@ class GnoClientImpl implements GnoClient {
|
|
|
1823
1941
|
`Collection not found: ${options.collection}`
|
|
1824
1942
|
);
|
|
1825
1943
|
}
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
});
|
|
1832
|
-
}
|
|
1833
|
-
const { overwrite: _unsupportedOverwrite, ...captureOptions } =
|
|
1834
|
-
options as GnoCaptureOptions & { overwrite?: unknown };
|
|
1944
|
+
const {
|
|
1945
|
+
overwrite: _unsupportedOverwrite,
|
|
1946
|
+
requestId,
|
|
1947
|
+
...captureOptions
|
|
1948
|
+
} = options as GnoCaptureOptions & { overwrite?: unknown };
|
|
1835
1949
|
if (_unsupportedOverwrite !== undefined) {
|
|
1836
1950
|
throw sdkError(
|
|
1837
1951
|
"VALIDATION",
|
|
1838
1952
|
"overwrite is not supported by client.capture(); use collisionPolicy instead"
|
|
1839
1953
|
);
|
|
1840
1954
|
}
|
|
1955
|
+
const input = { ...captureOptions, collection: collection.name };
|
|
1841
1956
|
|
|
1842
|
-
let plan: CapturePlan;
|
|
1843
1957
|
try {
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1958
|
+
const published = await publishCapture({
|
|
1959
|
+
collection,
|
|
1960
|
+
store: this.store,
|
|
1961
|
+
lockPath: this.requestLedger().lockPath,
|
|
1962
|
+
reportSyncFailure: true,
|
|
1963
|
+
config: this.config,
|
|
1964
|
+
plan: async () => {
|
|
1965
|
+
const existingList = await this.store.listDocuments(collection.name);
|
|
1966
|
+
if (!existingList.ok) {
|
|
1967
|
+
throw sdkError("STORE", existingList.error.message, {
|
|
1968
|
+
cause: existingList.error.cause,
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
try {
|
|
1972
|
+
return planCapture({
|
|
1973
|
+
input,
|
|
1974
|
+
existingRelPaths: existingList.value.map((doc) => doc.relPath),
|
|
1975
|
+
diskRelPaths: await listCaptureDiskRelPaths(collection.path),
|
|
1976
|
+
});
|
|
1977
|
+
} catch (error) {
|
|
1978
|
+
throw sdkError(
|
|
1979
|
+
"VALIDATION",
|
|
1980
|
+
error instanceof Error ? error.message : String(error)
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1848
1983
|
},
|
|
1849
|
-
|
|
1850
|
-
|
|
1984
|
+
request:
|
|
1985
|
+
requestId === undefined
|
|
1986
|
+
? undefined
|
|
1987
|
+
: { ...this.requestLedger(), requestId, input },
|
|
1851
1988
|
});
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
)
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
if (plan.openedExisting) {
|
|
1861
|
-
const existingDoc = await this.store.getDocument(
|
|
1862
|
-
collection.name,
|
|
1863
|
-
plan.relPath
|
|
1864
|
-
);
|
|
1865
|
-
if (!existingDoc.ok) {
|
|
1866
|
-
throw sdkError("STORE", existingDoc.error.message, {
|
|
1867
|
-
cause: existingDoc.error.cause,
|
|
1989
|
+
return published.request
|
|
1990
|
+
? { ...published.receipt, request: published.request }
|
|
1991
|
+
: published.receipt;
|
|
1992
|
+
} catch (cause) {
|
|
1993
|
+
if (cause instanceof CaptureSyncError) {
|
|
1994
|
+
throw sdkError("RUNTIME", cause.message, {
|
|
1995
|
+
cause,
|
|
1996
|
+
details: { code: cause.code, absPath: cause.absPath },
|
|
1868
1997
|
});
|
|
1869
1998
|
}
|
|
1870
|
-
|
|
1871
|
-
plan,
|
|
1872
|
-
absPath: fullPath,
|
|
1873
|
-
docid: existingDoc.value?.docid,
|
|
1874
|
-
sync: existingDoc.value
|
|
1875
|
-
? { status: "completed" }
|
|
1876
|
-
: {
|
|
1877
|
-
status: "skipped",
|
|
1878
|
-
reason: "Existing file is not indexed yet.",
|
|
1879
|
-
},
|
|
1880
|
-
});
|
|
1999
|
+
throw toRequestSdkError(cause);
|
|
1881
2000
|
}
|
|
2001
|
+
}
|
|
1882
2002
|
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
this.config
|
|
1895
|
-
)
|
|
1896
|
-
);
|
|
1897
|
-
const syncResult = syncResults[0];
|
|
1898
|
-
const docResult = await this.store.getDocument(
|
|
1899
|
-
collection.name,
|
|
1900
|
-
plan.relPath
|
|
1901
|
-
);
|
|
1902
|
-
const docid = docResult.ok ? docResult.value?.docid : undefined;
|
|
1903
|
-
return buildCaptureReceipt({
|
|
1904
|
-
plan,
|
|
1905
|
-
absPath: fullPath,
|
|
1906
|
-
docid: syncResult?.docid ?? docid,
|
|
1907
|
-
sync:
|
|
1908
|
-
syncResult?.status === "error"
|
|
1909
|
-
? {
|
|
1910
|
-
status: "failed",
|
|
1911
|
-
error:
|
|
1912
|
-
syncResult.errorMessage ??
|
|
1913
|
-
syncResult.errorCode ??
|
|
1914
|
-
"Unknown sync error",
|
|
1915
|
-
}
|
|
1916
|
-
: { status: "completed" },
|
|
1917
|
-
});
|
|
2003
|
+
private requestLedger() {
|
|
2004
|
+
return localRequestLedger(this.dbPath);
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
async requestStatus(requestId: string): Promise<GnoRequestStatusResult> {
|
|
2008
|
+
this.assertOpen();
|
|
2009
|
+
try {
|
|
2010
|
+
return await readRequestStatus({ ...this.requestLedger(), requestId });
|
|
2011
|
+
} catch (cause) {
|
|
2012
|
+
throw toRequestSdkError(cause);
|
|
2013
|
+
}
|
|
1918
2014
|
}
|
|
1919
2015
|
|
|
1920
2016
|
async createFolder(
|
package/src/sdk/index.ts
CHANGED
|
@@ -62,10 +62,16 @@ export type {
|
|
|
62
62
|
GnoRecallResult,
|
|
63
63
|
GnoRememberInput,
|
|
64
64
|
GnoRememberResult,
|
|
65
|
+
GnoRequestStatusResult,
|
|
65
66
|
GnoRenameNoteApplyOptions,
|
|
66
67
|
GnoRenameNoteOptions,
|
|
67
68
|
GnoProjectHintOptions,
|
|
68
69
|
GnoSearchOptions,
|
|
70
|
+
GnoSessionsDiscovery,
|
|
71
|
+
GnoSessionsImportInput,
|
|
72
|
+
GnoSessionsImportReceipt,
|
|
73
|
+
GnoSessionsAutomationRunResult,
|
|
74
|
+
GnoSessionsStatus,
|
|
69
75
|
GnoSkippedDocument,
|
|
70
76
|
GnoUpdateOptions,
|
|
71
77
|
GnoVectorSearchOptions,
|
package/src/sdk/types.ts
CHANGED
|
@@ -67,6 +67,10 @@ import type {
|
|
|
67
67
|
} from "../core/memory";
|
|
68
68
|
import type { NoteCollisionPolicy } from "../core/note-creation";
|
|
69
69
|
import type { NotePresetId } from "../core/note-presets";
|
|
70
|
+
import type {
|
|
71
|
+
RequestReceiptInfo,
|
|
72
|
+
RequestStatusResult,
|
|
73
|
+
} from "../core/request-receipts";
|
|
70
74
|
import type {
|
|
71
75
|
RetrievalTraceDeleteResult,
|
|
72
76
|
RetrievalTraceDetail,
|
|
@@ -94,6 +98,13 @@ import type {
|
|
|
94
98
|
SearchOptions,
|
|
95
99
|
SearchResults,
|
|
96
100
|
} from "../pipeline/types";
|
|
101
|
+
import type { SessionImportInput } from "../sessions/service";
|
|
102
|
+
import type {
|
|
103
|
+
SessionAutomationRunResult,
|
|
104
|
+
SessionImportReceipt,
|
|
105
|
+
SessionsDiscovery,
|
|
106
|
+
SessionsStatus,
|
|
107
|
+
} from "../sessions/types";
|
|
97
108
|
import type { IndexStatus } from "../store/types";
|
|
98
109
|
|
|
99
110
|
export type {
|
|
@@ -263,11 +274,25 @@ export interface GnoCreateNoteResult {
|
|
|
263
274
|
createdWithSuffix?: boolean;
|
|
264
275
|
}
|
|
265
276
|
|
|
266
|
-
export interface GnoCaptureOptions extends Omit<CaptureInput, "overwrite"> {
|
|
277
|
+
export interface GnoCaptureOptions extends Omit<CaptureInput, "overwrite"> {
|
|
278
|
+
/** Opt-in retry identity; reuse it only to retry this same capture. */
|
|
279
|
+
requestId?: string;
|
|
280
|
+
}
|
|
267
281
|
|
|
268
|
-
export type GnoCaptureResult = CaptureReceipt
|
|
282
|
+
export type GnoCaptureResult = CaptureReceipt & {
|
|
283
|
+
/** Present when the call carried a requestId. */
|
|
284
|
+
request?: RequestReceiptInfo;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
/** Lookup of a capture/remember request ID in the local-owner namespace. */
|
|
288
|
+
export type GnoRequestStatusResult = RequestStatusResult;
|
|
269
289
|
|
|
270
290
|
/** Shared memory contract (identical on CLI, MCP, REST, and SDK). */
|
|
291
|
+
export type GnoSessionsStatus = SessionsStatus;
|
|
292
|
+
export type GnoSessionsDiscovery = SessionsDiscovery;
|
|
293
|
+
export type GnoSessionsImportReceipt = SessionImportReceipt;
|
|
294
|
+
export type GnoSessionsImportInput = SessionImportInput;
|
|
295
|
+
export type GnoSessionsAutomationRunResult = SessionAutomationRunResult;
|
|
271
296
|
export type GnoRememberInput = RememberInput;
|
|
272
297
|
export type GnoRememberResult = RememberResult;
|
|
273
298
|
export type GnoRecallInput = RecallInput;
|
|
@@ -414,6 +439,11 @@ export interface GnoClient {
|
|
|
414
439
|
embed(options?: GnoEmbedOptions): Promise<GnoEmbedResult>;
|
|
415
440
|
index(options?: GnoIndexOptions): Promise<GnoIndexResult>;
|
|
416
441
|
capture(options: GnoCaptureOptions): Promise<GnoCaptureResult>;
|
|
442
|
+
/**
|
|
443
|
+
* Look up a capture/remember `requestId` before retrying it: pending,
|
|
444
|
+
* committed (with a content-free result pointer), expired, or not_found.
|
|
445
|
+
*/
|
|
446
|
+
requestStatus(requestId: string): Promise<GnoRequestStatusResult>;
|
|
417
447
|
/**
|
|
418
448
|
* Store one fact in a memory-managed collection, or propose candidates when
|
|
419
449
|
* `decision` is omitted. Requires caller + session identity and explicit
|
|
@@ -425,6 +455,28 @@ export interface GnoClient {
|
|
|
425
455
|
* The result carries a content-free fencing receipt.
|
|
426
456
|
*/
|
|
427
457
|
recall(input: GnoRecallInput): Promise<GnoRecallResult>;
|
|
458
|
+
/**
|
|
459
|
+
* Session-archive status for a client opened on the dedicated archive
|
|
460
|
+
* config/index pair. Throws VALIDATION (`details.code`
|
|
461
|
+
* SESSIONS_NOT_CONFIGURED) on any other config.
|
|
462
|
+
*/
|
|
463
|
+
sessionsStatus(): Promise<GnoSessionsStatus>;
|
|
464
|
+
/** Preview supported local session sources on this host. Never imports. */
|
|
465
|
+
discoverSessions(): Promise<GnoSessionsDiscovery>;
|
|
466
|
+
/**
|
|
467
|
+
* Manually import a registered source (`sourceId`) or explicit local paths
|
|
468
|
+
* (`paths` + `collection`) into the archive. Returns the shared receipt.
|
|
469
|
+
*/
|
|
470
|
+
importSessions(
|
|
471
|
+
input: GnoSessionsImportInput
|
|
472
|
+
): Promise<GnoSessionsImportReceipt>;
|
|
473
|
+
/**
|
|
474
|
+
* Run one configured automation profile now through the manual importer.
|
|
475
|
+
* Hooks and schedules are enabled only from the CLI or a same-host browser.
|
|
476
|
+
*/
|
|
477
|
+
runSessionsAutomation(input: {
|
|
478
|
+
profileId: string;
|
|
479
|
+
}): Promise<GnoSessionsAutomationRunResult>;
|
|
428
480
|
createNote(options: GnoCreateNoteOptions): Promise<GnoCreateNoteResult>;
|
|
429
481
|
createFolder(options: GnoCreateFolderOptions): Promise<GnoCreateFolderResult>;
|
|
430
482
|
previewRenameNote(
|
|
@@ -12,7 +12,13 @@ import { basename, dirname, join as pathJoin } from "node:path";
|
|
|
12
12
|
|
|
13
13
|
import type { Collection, Config } from "../config/types";
|
|
14
14
|
import type { PreparedBrowserClip } from "../core/browser-clip";
|
|
15
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
CaptureInput,
|
|
17
|
+
CapturePlan,
|
|
18
|
+
CaptureReceipt,
|
|
19
|
+
CaptureSource,
|
|
20
|
+
SyncCapturedFileResult,
|
|
21
|
+
} from "../core/capture";
|
|
16
22
|
import type { JobManager } from "../core/job-manager";
|
|
17
23
|
import type { SqliteAdapter } from "../store/sqlite/adapter";
|
|
18
24
|
import type { ClipperIdempotencyPlan } from "../store/sqlite/clipper-store-types";
|
|
@@ -30,12 +36,13 @@ import {
|
|
|
30
36
|
hashCaptureContent,
|
|
31
37
|
listCaptureDiskRelPaths,
|
|
32
38
|
planCapture,
|
|
33
|
-
syncCapturedFile,
|
|
34
39
|
} from "../core/capture";
|
|
40
|
+
import { publishCapture } from "../core/capture-publish";
|
|
35
41
|
import { writeCapturePlanFile } from "../core/capture-write";
|
|
36
42
|
import { MCP_ERRORS } from "../core/errors";
|
|
37
43
|
import { withWriteLock } from "../core/file-lock";
|
|
38
44
|
import { recordContentMutation } from "../core/mutation-generations";
|
|
45
|
+
import { localRequestLedger } from "../core/request-receipts";
|
|
39
46
|
import { DEFAULT_LOCK_WAIT_MS, writeLeasePath } from "../core/write-lease";
|
|
40
47
|
import {
|
|
41
48
|
type CollectionSyncResult,
|
|
@@ -331,6 +338,37 @@ const executeCaptureAsJob = async (
|
|
|
331
338
|
};
|
|
332
339
|
};
|
|
333
340
|
|
|
341
|
+
/** Shared leased publication wired to the resident watch/event hooks. */
|
|
342
|
+
const residentPublication = (
|
|
343
|
+
context: ResidentCaptureContext,
|
|
344
|
+
store: SqliteAdapter,
|
|
345
|
+
collection: Collection,
|
|
346
|
+
dependencies: ResidentCaptureDependencies
|
|
347
|
+
) => ({
|
|
348
|
+
collection,
|
|
349
|
+
store,
|
|
350
|
+
lockPath: resolveCaptureLockPath(context, dependencies),
|
|
351
|
+
lockWaitMs: dependencies.lockWaitMs ?? DEFAULT_LOCK_WAIT_MS,
|
|
352
|
+
config: context.config,
|
|
353
|
+
syncPaths: dependencies.syncPaths,
|
|
354
|
+
beforeWrite: (absPath: string) => context.watchService?.suppress(absPath),
|
|
355
|
+
afterSync: (synced: SyncCapturedFileResult, receipt: CaptureReceipt) => {
|
|
356
|
+
if (synced.result) {
|
|
357
|
+
recordContentMutation(synced.result, context.markContentMutation);
|
|
358
|
+
}
|
|
359
|
+
if (!receipt.openedExisting) {
|
|
360
|
+
emitCaptureCreated(context, collection, receipt.relPath);
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const residentCaptureStatus = (receipt: CaptureReceipt): number =>
|
|
366
|
+
receipt.collisionPolicyResult === "conflict"
|
|
367
|
+
? HTTP_CONFLICT
|
|
368
|
+
: receipt.openedExisting
|
|
369
|
+
? HTTP_OK
|
|
370
|
+
: HTTP_CREATED;
|
|
371
|
+
|
|
334
372
|
/**
|
|
335
373
|
* Write + lexical sync under the shared write lease; the response is sent
|
|
336
374
|
* only once the capture is retrievable. Throws `CaptureSyncError` when the
|
|
@@ -343,37 +381,65 @@ const executeCaptureAwaitingSync = async (
|
|
|
343
381
|
planned: Extract<ResidentCapturePlanResult, { ok: true }>,
|
|
344
382
|
dependencies: ResidentCaptureDependencies
|
|
345
383
|
): Promise<{ body: unknown; status: number }> => {
|
|
346
|
-
const {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
384
|
+
const { receipt } = await publishCapture({
|
|
385
|
+
...residentPublication(context, store, planned.collection, dependencies),
|
|
386
|
+
plan: async () => planned.plan,
|
|
387
|
+
});
|
|
388
|
+
return { body: receipt, status: HTTP_CREATED };
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
/** A resident plan rejection, carried through the leased planner. */
|
|
392
|
+
export class ResidentCapturePlanError extends Error {
|
|
393
|
+
readonly shape: Extract<ResidentCapturePlanResult, { ok: false }>;
|
|
394
|
+
|
|
395
|
+
constructor(shape: Extract<ResidentCapturePlanResult, { ok: false }>) {
|
|
396
|
+
super(shape.message);
|
|
397
|
+
this.name = "ResidentCapturePlanError";
|
|
398
|
+
this.shape = shape;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* `/api/capture` with a request ID: planning moves under the lease and the
|
|
404
|
+
* request-receipt service replays, finishes, or admits the capture.
|
|
405
|
+
*/
|
|
406
|
+
export const executeRequestedResidentCapture = async (
|
|
407
|
+
context: ResidentCaptureContext,
|
|
408
|
+
store: SqliteAdapter,
|
|
409
|
+
input: CaptureInput,
|
|
410
|
+
requestId: string,
|
|
411
|
+
dependencies: ResidentCaptureDependencies = {}
|
|
412
|
+
): Promise<{ body: unknown; status: number }> => {
|
|
413
|
+
const collection = context.config.collections.find(
|
|
414
|
+
(candidate) =>
|
|
415
|
+
candidate.name.toLowerCase() === input.collection.toLowerCase()
|
|
376
416
|
);
|
|
417
|
+
if (!collection) {
|
|
418
|
+
throw new ResidentCapturePlanError({
|
|
419
|
+
ok: false,
|
|
420
|
+
code: "NOT_FOUND",
|
|
421
|
+
message: `Collection not found: ${input.collection}`,
|
|
422
|
+
status: 404,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
const normalized = { ...input, collection: collection.name };
|
|
426
|
+
const published = await publishCapture({
|
|
427
|
+
...residentPublication(context, store, collection, dependencies),
|
|
428
|
+
plan: async () => {
|
|
429
|
+
const planned = await planResidentCapture(context, store, normalized);
|
|
430
|
+
if (!planned.ok) throw new ResidentCapturePlanError(planned);
|
|
431
|
+
return planned.plan;
|
|
432
|
+
},
|
|
433
|
+
request: {
|
|
434
|
+
...localRequestLedger(store.getDbPath()),
|
|
435
|
+
requestId,
|
|
436
|
+
input: normalized,
|
|
437
|
+
},
|
|
438
|
+
});
|
|
439
|
+
return {
|
|
440
|
+
body: { ...published.receipt, request: published.request },
|
|
441
|
+
status: residentCaptureStatus(published.receipt),
|
|
442
|
+
};
|
|
377
443
|
};
|
|
378
444
|
|
|
379
445
|
/**
|
package/src/serve/config-sync.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type MutationResult,
|
|
17
17
|
} from "../core/config-mutation";
|
|
18
18
|
import { withContentTypeRules } from "../ingestion";
|
|
19
|
+
import { watchedCollections } from "../sessions/config";
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Apply a config mutation atomically with serialization.
|
|
@@ -56,7 +57,7 @@ export async function applyConfigChange(
|
|
|
56
57
|
|
|
57
58
|
if (result.ok) {
|
|
58
59
|
ctxHolder.watchService?.updateCollections(
|
|
59
|
-
ctxHolder.config
|
|
60
|
+
watchedCollections(ctxHolder.config),
|
|
60
61
|
withContentTypeRules({}, ctxHolder.config)
|
|
61
62
|
);
|
|
62
63
|
}
|
|
@@ -86,7 +87,7 @@ export async function applyConfigChangeTyped<T>(
|
|
|
86
87
|
|
|
87
88
|
if (result.ok) {
|
|
88
89
|
ctxHolder.watchService?.updateCollections(
|
|
89
|
-
ctxHolder.config
|
|
90
|
+
watchedCollections(ctxHolder.config),
|
|
90
91
|
withContentTypeRules({}, ctxHolder.config)
|
|
91
92
|
);
|
|
92
93
|
}
|