@gmickel/gno 2.5.1 → 2.6.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 +75 -1
- package/assets/skill/cli-reference.md +123 -0
- package/assets/skill/examples.md +30 -0
- package/assets/skill/mcp-reference.md +52 -0
- 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.6.0.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.6.0.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +2 -1
- package/spec/cli.md +347 -24
- package/spec/mcp.md +198 -2
- package/spec/output-schemas/capture-receipt.schema.json +3 -0
- package/spec/output-schemas/mcp-capture-result.schema.json +3 -0
- package/spec/output-schemas/memory-remember.schema.json +8 -2
- package/spec/output-schemas/request-status.schema.json +113 -0
- 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/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/ls.ts +3 -0
- package/src/cli/commands/memory.ts +12 -3
- 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/program.ts +388 -1
- 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 +3 -0
- package/src/core/memory-remember.ts +233 -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/index.ts +9 -0
- package/src/mcp/context.ts +8 -0
- package/src/mcp/http-egress.ts +4 -0
- package/src/mcp/http-transport.ts +2 -0
- package/src/mcp/tools/capture.ts +87 -83
- package/src/mcp/tools/index.ts +66 -0
- package/src/mcp/tools/memory-remember.ts +7 -0
- 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/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/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/components/sessions/snippet.tsx +53 -0
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/hooks/use-api.ts +10 -2
- package/src/serve/public/lib/request-intent.ts +69 -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 +10 -0
- package/src/serve/public/pages/DocView.tsx +15 -1
- package/src/serve/public/pages/DocumentEditor.tsx +139 -96
- package/src/serve/public/pages/Sessions.tsx +350 -0
- package/src/serve/resident-runtime.ts +43 -3
- 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 +205 -1
- package/src/serve/session-automation.ts +146 -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/sqlite/adapter.ts +54 -15
- package/src/store/sqlite/scoped-index.ts +9 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.5.1.zip.sha256 +0 -1
package/src/mcp/context.ts
CHANGED
|
@@ -66,11 +66,14 @@ export interface ToolContextSnapshot {
|
|
|
66
66
|
* session id, so this is what keeps two modern callers apart.
|
|
67
67
|
*/
|
|
68
68
|
requestIdentity?: string;
|
|
69
|
+
/** Stable request-receipt namespace of the authorized HTTP identity. */
|
|
70
|
+
requestNamespace?: string;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
export interface RequestScope {
|
|
72
74
|
egress: NonNullable<ToolContextSnapshot["egress"]>;
|
|
73
75
|
requestIdentity?: string;
|
|
76
|
+
requestNamespace?: string;
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
export interface ToolContext {
|
|
@@ -107,6 +110,8 @@ export interface ToolContext {
|
|
|
107
110
|
getEgressContext?: () => ToolContextSnapshot["egress"];
|
|
108
111
|
/** Per-caller identity of the current request; absent on stdio. */
|
|
109
112
|
getRequestIdentity?: () => string | undefined;
|
|
113
|
+
/** Request-receipt namespace of the current HTTP caller; absent on stdio. */
|
|
114
|
+
getRequestNamespace?: () => string | undefined;
|
|
110
115
|
runWithEgressContext?<T>(
|
|
111
116
|
egress: NonNullable<ToolContextSnapshot["egress"]>,
|
|
112
117
|
operation: () => Promise<T>,
|
|
@@ -216,6 +221,7 @@ export function createToolContext(
|
|
|
216
221
|
requestSnapshot.getStore()?.egress?.authorizationEpoch?.value,
|
|
217
222
|
getEgressContext: () => requestSnapshot.getStore()?.egress,
|
|
218
223
|
getRequestIdentity: () => requestSnapshot.getStore()?.requestIdentity,
|
|
224
|
+
getRequestNamespace: () => requestSnapshot.getStore()?.requestNamespace,
|
|
219
225
|
runWithEgressContext<T>(
|
|
220
226
|
egress: NonNullable<ToolContextSnapshot["egress"]>,
|
|
221
227
|
operation: () => Promise<T>,
|
|
@@ -228,6 +234,7 @@ export function createToolContext(
|
|
|
228
234
|
collections: config.collections,
|
|
229
235
|
egress,
|
|
230
236
|
requestIdentity: scope?.requestIdentity,
|
|
237
|
+
requestNamespace: scope?.requestNamespace,
|
|
231
238
|
},
|
|
232
239
|
operation
|
|
233
240
|
);
|
|
@@ -241,6 +248,7 @@ export function createToolContext(
|
|
|
241
248
|
collections: config.collections,
|
|
242
249
|
egress: current?.egress,
|
|
243
250
|
requestIdentity: current?.requestIdentity,
|
|
251
|
+
requestNamespace: current?.requestNamespace,
|
|
244
252
|
},
|
|
245
253
|
operation
|
|
246
254
|
);
|
package/src/mcp/http-egress.ts
CHANGED
|
@@ -57,8 +57,12 @@ export const MCP_HTTP_EGRESS_TOOLS = {
|
|
|
57
57
|
gno_remember: "source",
|
|
58
58
|
gno_remove_collection: "metadata",
|
|
59
59
|
gno_rename_note: "metadata",
|
|
60
|
+
gno_request_status: "metadata",
|
|
60
61
|
gno_search: "snippet",
|
|
61
62
|
gno_section: "metadata",
|
|
63
|
+
gno_sessions_automation_run: "metadata",
|
|
64
|
+
gno_sessions_import: "metadata",
|
|
65
|
+
gno_sessions_status: "metadata",
|
|
62
66
|
gno_similar: "snippet",
|
|
63
67
|
gno_peek: "metadata",
|
|
64
68
|
gno_status: "metadata",
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
|
|
16
16
|
import { MCP_SERVER_NAME, VERSION } from "../app/constants";
|
|
17
17
|
import { EgressDeniedError } from "../core/egress-enforcement";
|
|
18
|
+
import { httpMcpRequestNamespace } from "../core/request-receipts";
|
|
18
19
|
import { withInferenceScope } from "../llm/inference-scope";
|
|
19
20
|
import { createMcpServerSurface, type ToolContext } from "./context";
|
|
20
21
|
import {
|
|
@@ -445,6 +446,7 @@ export class HttpMcpTransport {
|
|
|
445
446
|
this.#runtime.mcpContext.serverInstanceId,
|
|
446
447
|
context.identity
|
|
447
448
|
),
|
|
449
|
+
requestNamespace: httpMcpRequestNamespace(context.identity),
|
|
448
450
|
}
|
|
449
451
|
)
|
|
450
452
|
: await handle();
|
package/src/mcp/tools/capture.ts
CHANGED
|
@@ -4,33 +4,36 @@
|
|
|
4
4
|
* @module src/mcp/tools/capture
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
// node:fs/promises for mkdir (no Bun equivalent for structure ops)
|
|
8
|
-
import { mkdir } from "node:fs/promises";
|
|
9
7
|
// node:path for path utils (no Bun path utils)
|
|
10
|
-
import {
|
|
8
|
+
import { extname } from "node:path";
|
|
11
9
|
|
|
12
10
|
import type { NoteCollisionPolicy } from "../../core/note-creation";
|
|
13
11
|
import type { NotePresetId } from "../../core/note-presets";
|
|
14
12
|
import type { ToolContext } from "../server";
|
|
15
13
|
|
|
16
14
|
import {
|
|
17
|
-
buildCaptureReceipt,
|
|
18
15
|
CaptureSyncError,
|
|
19
|
-
ensureCapturedFileIndexed,
|
|
20
16
|
listCaptureDiskRelPaths,
|
|
21
17
|
planCapture,
|
|
22
|
-
syncCapturedFile,
|
|
23
18
|
type CaptureInput as SharedCaptureInput,
|
|
19
|
+
type CapturePlan,
|
|
24
20
|
type CaptureReceipt,
|
|
25
|
-
type SyncCapturedFileResult,
|
|
26
21
|
} from "../../core/capture";
|
|
27
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
publishCapture,
|
|
24
|
+
type PublishedCapture,
|
|
25
|
+
} from "../../core/capture-publish";
|
|
28
26
|
import { MCP_ERRORS } from "../../core/errors";
|
|
29
|
-
import { withWriteLock } from "../../core/file-lock";
|
|
30
27
|
import { recordContentMutation } from "../../core/mutation-generations";
|
|
28
|
+
import {
|
|
29
|
+
formatRequestReceiptLine,
|
|
30
|
+
type RequestReceiptInfo,
|
|
31
|
+
requestLedgerPath,
|
|
32
|
+
} from "../../core/request-receipts";
|
|
31
33
|
import { normalizeCollectionName } from "../../core/validation";
|
|
32
34
|
import { DEFAULT_LOCK_WAIT_MS } from "../../core/write-lease";
|
|
33
35
|
import { runTool, type ToolResult } from "./index";
|
|
36
|
+
import { mcpRequestNamespace, rethrowRequestError } from "./request-status";
|
|
34
37
|
|
|
35
38
|
interface CaptureInput extends Omit<
|
|
36
39
|
SharedCaptureInput,
|
|
@@ -39,6 +42,7 @@ interface CaptureInput extends Omit<
|
|
|
39
42
|
path?: string;
|
|
40
43
|
collisionPolicy?: NoteCollisionPolicy;
|
|
41
44
|
presetId?: NotePresetId;
|
|
45
|
+
requestId?: string;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
type McpCaptureResult = CaptureReceipt & {
|
|
@@ -46,6 +50,7 @@ type McpCaptureResult = CaptureReceipt & {
|
|
|
46
50
|
absPath: string;
|
|
47
51
|
overwritten: boolean;
|
|
48
52
|
serverInstanceId: string;
|
|
53
|
+
request?: RequestReceiptInfo;
|
|
49
54
|
};
|
|
50
55
|
|
|
51
56
|
const SENSITIVE_SUBPATHS = new Set([
|
|
@@ -86,6 +91,7 @@ function formatCaptureResult(result: McpCaptureResult): string {
|
|
|
86
91
|
if (result.tags.length > 0) {
|
|
87
92
|
lines.push(`Tags: ${result.tags.join(", ")}`);
|
|
88
93
|
}
|
|
94
|
+
if (result.request) lines.push(formatRequestReceiptLine(result.request));
|
|
89
95
|
return lines.join("\n");
|
|
90
96
|
}
|
|
91
97
|
|
|
@@ -112,7 +118,7 @@ function rethrowCaptureError(error: unknown): never {
|
|
|
112
118
|
if (error instanceof CaptureSyncError) {
|
|
113
119
|
throw new Error(`${error.code}: ${error.message}`);
|
|
114
120
|
}
|
|
115
|
-
|
|
121
|
+
return rethrowRequestError(error);
|
|
116
122
|
}
|
|
117
123
|
|
|
118
124
|
export function handleCapture(
|
|
@@ -140,83 +146,81 @@ export function handleCapture(
|
|
|
140
146
|
// Write + lexical sync complete under the shared write lease: the tool
|
|
141
147
|
// succeeds only once the capture is retrievable (v1.38 contention
|
|
142
148
|
// contract: wait for the lease, LOCKED when it stays busy).
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
diskRelPaths: await listCaptureDiskRelPaths(collection.path),
|
|
157
|
-
});
|
|
158
|
-
} catch (error) {
|
|
159
|
-
const message =
|
|
160
|
-
error instanceof Error ? error.message : String(error);
|
|
161
|
-
throw new Error(`${MCP_ERRORS.INVALID_INPUT.code}: ${message}`);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
assertNotSensitive(plan.relPath);
|
|
165
|
-
|
|
166
|
-
const absPath = join(collection.path, plan.relPath);
|
|
167
|
-
const syncInput = {
|
|
168
|
-
collection,
|
|
169
|
-
store: ctx.store,
|
|
170
|
-
relPath: plan.relPath,
|
|
171
|
-
absPath,
|
|
172
|
-
config: ctx.config,
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
let synced: SyncCapturedFileResult;
|
|
176
|
-
let overwritten = false;
|
|
177
|
-
try {
|
|
178
|
-
if (plan.openedExisting) {
|
|
179
|
-
synced = await ensureCapturedFileIndexed(syncInput);
|
|
180
|
-
} else {
|
|
181
|
-
overwritten =
|
|
182
|
-
(await Bun.file(absPath).exists()) && args.overwrite === true;
|
|
183
|
-
await mkdir(dirname(absPath), { recursive: true });
|
|
184
|
-
await writeCapturePlanFile(plan, absPath);
|
|
185
|
-
synced = await syncCapturedFile(syncInput);
|
|
149
|
+
const input = buildSharedInput(args, collection.name);
|
|
150
|
+
let published: PublishedCapture;
|
|
151
|
+
try {
|
|
152
|
+
published = await publishCapture({
|
|
153
|
+
collection,
|
|
154
|
+
store: ctx.store,
|
|
155
|
+
lockPath: ctx.writeLockPath,
|
|
156
|
+
lockWaitMs: DEFAULT_LOCK_WAIT_MS,
|
|
157
|
+
config: ctx.config,
|
|
158
|
+
plan: async () => {
|
|
159
|
+
const existingDocs = await ctx.store.listDocuments(collectionName);
|
|
160
|
+
if (!existingDocs.ok) {
|
|
161
|
+
throw new Error(existingDocs.error.message);
|
|
186
162
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
163
|
+
let plan: CapturePlan;
|
|
164
|
+
try {
|
|
165
|
+
plan = planCapture({
|
|
166
|
+
input,
|
|
167
|
+
existingRelPaths: existingDocs.value.map((doc) => doc.relPath),
|
|
168
|
+
diskRelPaths: await listCaptureDiskRelPaths(collection.path),
|
|
169
|
+
});
|
|
170
|
+
} catch (error) {
|
|
171
|
+
const message =
|
|
172
|
+
error instanceof Error ? error.message : String(error);
|
|
173
|
+
throw new Error(`${MCP_ERRORS.INVALID_INPUT.code}: ${message}`);
|
|
174
|
+
}
|
|
175
|
+
assertNotSensitive(plan.relPath);
|
|
176
|
+
return plan;
|
|
177
|
+
},
|
|
178
|
+
afterSync: async (synced, receipt) => {
|
|
179
|
+
if (synced.result) {
|
|
180
|
+
recordContentMutation(synced.result, ctx.markContentMutation);
|
|
181
|
+
}
|
|
182
|
+
const isMarkdown =
|
|
183
|
+
receipt.relPath.endsWith(".md") ||
|
|
184
|
+
receipt.relPath.endsWith(".markdown");
|
|
185
|
+
if (
|
|
186
|
+
!isMarkdown &&
|
|
187
|
+
!receipt.openedExisting &&
|
|
188
|
+
receipt.tags.length > 0
|
|
189
|
+
) {
|
|
190
|
+
const tagResult = await ctx.store.setDocTags(
|
|
191
|
+
synced.documentId,
|
|
192
|
+
receipt.tags,
|
|
193
|
+
"user"
|
|
205
194
|
);
|
|
195
|
+
if (!tagResult.ok) {
|
|
196
|
+
console.error(
|
|
197
|
+
`[MCP] Warning: Document created but tags not stored: ${tagResult.error.message}`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
206
200
|
}
|
|
207
|
-
}
|
|
201
|
+
},
|
|
202
|
+
request:
|
|
203
|
+
args.requestId === undefined
|
|
204
|
+
? undefined
|
|
205
|
+
: {
|
|
206
|
+
ledgerPath: requestLedgerPath(ctx.store.getDbPath()),
|
|
207
|
+
namespace: mcpRequestNamespace(ctx),
|
|
208
|
+
requestId: args.requestId,
|
|
209
|
+
input,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
} catch (error) {
|
|
213
|
+
rethrowCaptureError(error);
|
|
214
|
+
}
|
|
208
215
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
},
|
|
218
|
-
DEFAULT_LOCK_WAIT_MS
|
|
219
|
-
);
|
|
216
|
+
return {
|
|
217
|
+
...published.receipt,
|
|
218
|
+
docid: published.receipt.docid ?? "",
|
|
219
|
+
absPath: published.receipt.absPath ?? "",
|
|
220
|
+
overwritten: published.receipt.overwritten ?? false,
|
|
221
|
+
serverInstanceId: ctx.serverInstanceId,
|
|
222
|
+
...(published.request ? { request: published.request } : {}),
|
|
223
|
+
} as McpCaptureResult;
|
|
220
224
|
},
|
|
221
225
|
formatCaptureResult
|
|
222
226
|
);
|
package/src/mcp/tools/index.ts
CHANGED
|
@@ -101,6 +101,12 @@ import { handleMultiGet } from "./multi-get";
|
|
|
101
101
|
import { handlePeek, PEEK_MCP_ANNOTATIONS } from "./peek";
|
|
102
102
|
import { handleQuery, handleQueryDiagnose } from "./query";
|
|
103
103
|
import { handleRemoveCollection } from "./remove-collection";
|
|
104
|
+
import {
|
|
105
|
+
handleRequestStatus,
|
|
106
|
+
REQUEST_STATUS_MCP_ANNOTATIONS,
|
|
107
|
+
requestIdInputSchema,
|
|
108
|
+
requestStatusInputSchema,
|
|
109
|
+
} from "./request-status";
|
|
104
110
|
import { handleSearch } from "./search";
|
|
105
111
|
import {
|
|
106
112
|
handleSection,
|
|
@@ -108,6 +114,17 @@ import {
|
|
|
108
114
|
sectionInputSchema,
|
|
109
115
|
sectionOutputSchema,
|
|
110
116
|
} from "./sections";
|
|
117
|
+
import {
|
|
118
|
+
handleSessionsAutomationRun,
|
|
119
|
+
handleSessionsImport,
|
|
120
|
+
handleSessionsStatus,
|
|
121
|
+
SESSIONS_AUTOMATION_RUN_MCP_ANNOTATIONS,
|
|
122
|
+
SESSIONS_IMPORT_MCP_ANNOTATIONS,
|
|
123
|
+
SESSIONS_STATUS_MCP_ANNOTATIONS,
|
|
124
|
+
sessionsAutomationRunInputSchema,
|
|
125
|
+
sessionsImportInputSchema,
|
|
126
|
+
sessionsStatusInputSchema,
|
|
127
|
+
} from "./sessions";
|
|
111
128
|
import { handleStatus } from "./status";
|
|
112
129
|
import { handleSync } from "./sync";
|
|
113
130
|
import {
|
|
@@ -175,6 +192,8 @@ export const MCP_TOOL_DESCRIPTIONS = {
|
|
|
175
192
|
ask: "Generate one answer from a deterministic Context Capsule, verify every substantive claim against exact retained spans, and abstain unless support coverage is complete. Read-only; returns the Capsule, freshness receipt, claim verdicts, gaps, and evidence IDs.",
|
|
176
193
|
recall:
|
|
177
194
|
"Recall current facts from a memory-managed collection for explicit scopes. Call before answering about the user's preferences, decisions, people, or prior work, and before gno_remember to find the predecessor of a changed fact. Returns at most 8 facts within 512 tokens by default, each with text, scopes, provenance, gno:// cite, and content hash, plus a content-free receipt; superseded facts are excluded. Pass the receipt to gno_remember when a stored fact derives from this recall. An empty result names the command that stores the first fact.",
|
|
195
|
+
requestStatus:
|
|
196
|
+
"Look up a write request ID sent with gno_capture or gno_remember before retrying it. Returns pending, committed (with the result uri and hashes), expired, or not_found within this caller's own namespace; never returns note or fact content. Committed: do not resend. Pending: retry the same request ID later. not_found: nothing was accepted under that ID.",
|
|
178
197
|
remember:
|
|
179
198
|
"Store one fact in a memory-managed collection under explicit scopes. Call when the user states a durable preference, decision, or fact worth recalling later; use gno_capture for documents and file edits for existing notes. Without decision it returns likely matches and writes nothing; decision=add writes a new fact; decision=supersede replaces predecessorUri after a hash check, one successor per fact. Exact duplicates return the existing record. Text that replays a recall receipt span or declares a gno:// origin is rejected. The fact is lexically searchable when the call returns.",
|
|
180
199
|
} as const;
|
|
@@ -366,6 +385,7 @@ export const captureInputSchema = z.object({
|
|
|
366
385
|
.describe(
|
|
367
386
|
"Structured provenance metadata written under source frontmatter"
|
|
368
387
|
),
|
|
388
|
+
requestId: requestIdInputSchema.optional(),
|
|
369
389
|
});
|
|
370
390
|
|
|
371
391
|
const addCollectionInputSchema = z.object({
|
|
@@ -1141,6 +1161,17 @@ export function registerTools(server: McpServer, ctx: ToolContext): void {
|
|
|
1141
1161
|
})
|
|
1142
1162
|
);
|
|
1143
1163
|
|
|
1164
|
+
registerTool(
|
|
1165
|
+
"gno_sessions_status",
|
|
1166
|
+
{
|
|
1167
|
+
description:
|
|
1168
|
+
"Status of this session-archive instance: archive collections, owner-registered session sources (by ID, no host paths), pending/incomplete/failed units, last import, and opt-in automation profiles (enabled hook/schedule, daemon availability, pending/running/partial/failed, last success, next due, recovery action). Only available when the server runs on a dedicated session-archive config/index pair; nothing is imported unless the owner enabled it.",
|
|
1169
|
+
inputSchema: sessionsStatusInputSchema,
|
|
1170
|
+
annotations: SESSIONS_STATUS_MCP_ANNOTATIONS,
|
|
1171
|
+
},
|
|
1172
|
+
() => handleSessionsStatus(ctx)
|
|
1173
|
+
);
|
|
1174
|
+
|
|
1144
1175
|
registerTool(
|
|
1145
1176
|
"gno_search",
|
|
1146
1177
|
{
|
|
@@ -1501,6 +1532,28 @@ export function registerTools(server: McpServer, ctx: ToolContext): void {
|
|
|
1501
1532
|
})
|
|
1502
1533
|
);
|
|
1503
1534
|
|
|
1535
|
+
registerTool(
|
|
1536
|
+
"gno_sessions_import",
|
|
1537
|
+
{
|
|
1538
|
+
description:
|
|
1539
|
+
"Manually import one owner-registered session source into the session archive and sync it for search. Takes a source ID only (never a host path); dryRun=true reports without writing. Returns a receipt with imported/updated/unchanged/skipped/incomplete/failed counts, lexical readiness and embedding backlog; partial imports stay visible and are retried by the next call.",
|
|
1540
|
+
inputSchema: sessionsImportInputSchema,
|
|
1541
|
+
annotations: SESSIONS_IMPORT_MCP_ANNOTATIONS,
|
|
1542
|
+
},
|
|
1543
|
+
(args) => handleSessionsImport(args, ctx)
|
|
1544
|
+
);
|
|
1545
|
+
|
|
1546
|
+
registerTool(
|
|
1547
|
+
"gno_sessions_automation_run",
|
|
1548
|
+
{
|
|
1549
|
+
description:
|
|
1550
|
+
"Run one owner-configured session automation profile now: imports its registered sources through the manual importer and records the outcome in automation status. Takes a profile ID only; it cannot enable hooks or schedules, add sources or change destinations. Returns the run outcome (complete, up_to_date, partial, failed, or not_started with a reason such as busy) and one import receipt per source.",
|
|
1551
|
+
inputSchema: sessionsAutomationRunInputSchema,
|
|
1552
|
+
annotations: SESSIONS_AUTOMATION_RUN_MCP_ANNOTATIONS,
|
|
1553
|
+
},
|
|
1554
|
+
(args) => handleSessionsAutomationRun(args, ctx)
|
|
1555
|
+
);
|
|
1556
|
+
|
|
1504
1557
|
registerTool(
|
|
1505
1558
|
"gno_capture",
|
|
1506
1559
|
{
|
|
@@ -1513,6 +1566,19 @@ export function registerTools(server: McpServer, ctx: ToolContext): void {
|
|
|
1513
1566
|
(args) => handleCapture(args, ctx)
|
|
1514
1567
|
);
|
|
1515
1568
|
|
|
1569
|
+
registerTool(
|
|
1570
|
+
"gno_request_status",
|
|
1571
|
+
{
|
|
1572
|
+
description: describe(
|
|
1573
|
+
"gno_request_status",
|
|
1574
|
+
MCP_TOOL_DESCRIPTIONS.requestStatus
|
|
1575
|
+
),
|
|
1576
|
+
inputSchema: requestStatusInputSchema,
|
|
1577
|
+
annotations: REQUEST_STATUS_MCP_ANNOTATIONS,
|
|
1578
|
+
},
|
|
1579
|
+
(args) => handleRequestStatus(args, ctx)
|
|
1580
|
+
);
|
|
1581
|
+
|
|
1516
1582
|
registerTool(
|
|
1517
1583
|
"gno_add_collection",
|
|
1518
1584
|
{
|
|
@@ -14,6 +14,7 @@ import type { ToolContext } from "../server";
|
|
|
14
14
|
|
|
15
15
|
import { type RememberResult } from "../../core/memory";
|
|
16
16
|
import { MEMORY_MAX_FACT_BYTES } from "../../core/memory-record";
|
|
17
|
+
import { formatRequestReceiptLine } from "../../core/request-receipts";
|
|
17
18
|
import { runTool, type ToolResult } from "./index";
|
|
18
19
|
import {
|
|
19
20
|
createMcpMemoryService,
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
resolveMcpMemoryIdentity,
|
|
23
24
|
rethrowMemoryError,
|
|
24
25
|
} from "./memory-shared";
|
|
26
|
+
import { requestIdInputSchema } from "./request-status";
|
|
25
27
|
|
|
26
28
|
export const REMEMBER_MCP_ANNOTATIONS = {
|
|
27
29
|
readOnlyHint: false,
|
|
@@ -92,6 +94,7 @@ export const rememberInputSchema = z.object({
|
|
|
92
94
|
.min(1)
|
|
93
95
|
.optional()
|
|
94
96
|
.describe("Free-text evidence for the fact (where it came from)"),
|
|
97
|
+
requestId: requestIdInputSchema.optional(),
|
|
95
98
|
});
|
|
96
99
|
|
|
97
100
|
export type RememberToolInput = z.infer<typeof rememberInputSchema>;
|
|
@@ -132,6 +135,9 @@ export function formatRememberResult(result: RememberResult): string {
|
|
|
132
135
|
break;
|
|
133
136
|
}
|
|
134
137
|
lines.push(matching);
|
|
138
|
+
if ("request" in result && result.request) {
|
|
139
|
+
lines.push(formatRequestReceiptLine(result.request));
|
|
140
|
+
}
|
|
135
141
|
return lines.join("\n");
|
|
136
142
|
}
|
|
137
143
|
|
|
@@ -163,6 +169,7 @@ export function handleRemember(
|
|
|
163
169
|
receipt: args.receipt,
|
|
164
170
|
derivedFrom: args.derivedFrom,
|
|
165
171
|
source: args.source,
|
|
172
|
+
requestId: args.requestId,
|
|
166
173
|
});
|
|
167
174
|
} catch (error) {
|
|
168
175
|
return rethrowMemoryError(error);
|
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
MemoryService,
|
|
20
20
|
} from "../../core/memory";
|
|
21
21
|
import { MEMORY_MAX_SCOPES } from "../../core/memory-record";
|
|
22
|
+
import { requestLedgerPath } from "../../core/request-receipts";
|
|
23
|
+
import { mcpRequestNamespace, rethrowRequestError } from "./request-status";
|
|
22
24
|
|
|
23
25
|
/** Caller name when the MCP client sent no implementation name. */
|
|
24
26
|
const DEFAULT_MCP_CALLER = "mcp";
|
|
@@ -74,6 +76,10 @@ export function createMcpMemoryService(ctx: ToolContext): MemoryService {
|
|
|
74
76
|
config: ctx.config,
|
|
75
77
|
collections: ctx.collections,
|
|
76
78
|
lockPath: ctx.writeLockPath,
|
|
79
|
+
requests: {
|
|
80
|
+
ledgerPath: requestLedgerPath(ctx.store.getDbPath()),
|
|
81
|
+
namespace: mcpRequestNamespace(ctx),
|
|
82
|
+
},
|
|
77
83
|
});
|
|
78
84
|
}
|
|
79
85
|
|
|
@@ -82,5 +88,5 @@ export function rethrowMemoryError(error: unknown): never {
|
|
|
82
88
|
if (error instanceof MemoryError) {
|
|
83
89
|
throw new Error(`${error.code}: ${error.message}`);
|
|
84
90
|
}
|
|
85
|
-
|
|
91
|
+
return rethrowRequestError(error);
|
|
86
92
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP request receipts: the caller's namespace, error rethrow, and the
|
|
3
|
+
* bounded `gno_request_status` lookup.
|
|
4
|
+
*
|
|
5
|
+
* @module src/mcp/tools/request-status
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
import type { ToolContext } from "../server";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
formatRequestStatus,
|
|
14
|
+
LOCAL_OWNER_NAMESPACE,
|
|
15
|
+
readRequestStatus,
|
|
16
|
+
RequestReceiptError,
|
|
17
|
+
requestLedgerPath,
|
|
18
|
+
} from "../../core/request-receipts";
|
|
19
|
+
import { runTool, type ToolResult } from "./index";
|
|
20
|
+
|
|
21
|
+
export const REQUEST_STATUS_MCP_ANNOTATIONS = {
|
|
22
|
+
readOnlyHint: true,
|
|
23
|
+
destructiveHint: false,
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
openWorldHint: false,
|
|
26
|
+
} as const;
|
|
27
|
+
|
|
28
|
+
export const requestIdInputSchema = z
|
|
29
|
+
.string()
|
|
30
|
+
.describe(
|
|
31
|
+
"Optional caller request ID (1-128 of A-Z a-z 0-9 . _ : -). Reuse the same ID when retrying the same write after a lost response; a new intent needs a new ID"
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export const requestStatusInputSchema = z.object({
|
|
35
|
+
requestId: z
|
|
36
|
+
.string()
|
|
37
|
+
.describe("Request ID previously sent with gno_capture or gno_remember"),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/** HTTP callers get their authorized identity's namespace; stdio is the local owner. */
|
|
41
|
+
export function mcpRequestNamespace(ctx: ToolContext): string {
|
|
42
|
+
return ctx.getRequestNamespace?.() ?? LOCAL_OWNER_NAMESPACE;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Re-throw a request receipt error in the `CODE: message` shape runTool parses. */
|
|
46
|
+
export function rethrowRequestError(error: unknown): never {
|
|
47
|
+
if (error instanceof RequestReceiptError) {
|
|
48
|
+
throw new Error(`${error.code}: ${error.message}`);
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function handleRequestStatus(
|
|
54
|
+
args: z.infer<typeof requestStatusInputSchema>,
|
|
55
|
+
ctx: ToolContext
|
|
56
|
+
): Promise<ToolResult> {
|
|
57
|
+
return runTool(
|
|
58
|
+
ctx,
|
|
59
|
+
"gno_request_status",
|
|
60
|
+
async () => {
|
|
61
|
+
try {
|
|
62
|
+
return await readRequestStatus({
|
|
63
|
+
ledgerPath: requestLedgerPath(ctx.store.getDbPath()),
|
|
64
|
+
namespace: mcpRequestNamespace(ctx),
|
|
65
|
+
requestId: args.requestId,
|
|
66
|
+
});
|
|
67
|
+
} catch (error) {
|
|
68
|
+
return rethrowRequestError(error);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
formatRequestStatus
|
|
72
|
+
);
|
|
73
|
+
}
|