@gmickel/gno 2.4.0 → 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 +69 -6
- package/assets/skill/README.md +5 -1
- package/assets/skill/SKILL.md +98 -1
- package/assets/skill/cli-reference.md +140 -0
- package/assets/skill/examples.md +45 -0
- package/assets/skill/mcp-reference.md +62 -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.4.0.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 +358 -24
- package/spec/compiled-context.md +68 -0
- package/spec/mcp.md +223 -2
- package/spec/output-schemas/capture-receipt.schema.json +3 -0
- package/spec/output-schemas/compiled-context-check.schema.json +44 -0
- package/spec/output-schemas/compiled-context-file.schema.json +165 -0
- package/spec/output-schemas/compiled-context-preview.schema.json +142 -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/app/compiled-context-files.ts +361 -0
- package/src/app/compiled-context.ts +240 -0
- package/src/app/context-surface.ts +7 -2
- package/src/cli/commands/ask.ts +14 -2
- package/src/cli/commands/audit.ts +4 -0
- package/src/cli/commands/capture.ts +55 -96
- package/src/cli/commands/context-compiled.ts +136 -0
- 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/errors.ts +10 -3
- package/src/cli/program.ts +458 -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/compiled-context.ts +254 -0
- package/src/core/context-budget.ts +2 -10
- package/src/core/file-lock.ts +22 -5
- package/src/core/folder-setup-planning.ts +2 -1
- package/src/core/memory-remember.ts +233 -122
- package/src/core/memory-types.ts +11 -0
- package/src/core/network-boundary-inventory.ts +16 -0
- package/src/core/request-receipts.ts +671 -0
- package/src/core/setup-receipt.ts +27 -20
- package/src/core/typed-metadata.ts +4 -0
- package/src/core/validation.ts +9 -2
- package/src/core/windows-private-path.ts +96 -0
- package/src/index.ts +11 -2
- package/src/ingestion/compiled-context.ts +15 -0
- package/src/ingestion/sync.ts +40 -8
- package/src/ingestion/walker.ts +5 -4
- package/src/llm/nodeLlamaCpp/simulator-install.ts +6 -2
- package/src/mcp/context.ts +8 -0
- package/src/mcp/http-egress.ts +15 -3
- package/src/mcp/http-transport.ts +2 -0
- package/src/mcp/retrieval-warnings.ts +24 -0
- package/src/mcp/tools/ask.ts +14 -1
- package/src/mcp/tools/capture.ts +87 -83
- package/src/mcp/tools/context.ts +46 -2
- package/src/mcp/tools/index.ts +119 -7
- package/src/mcp/tools/memory-remember.ts +7 -0
- package/src/mcp/tools/memory-shared.ts +7 -1
- package/src/mcp/tools/query.ts +3 -1
- package/src/mcp/tools/request-status.ts +73 -0
- package/src/mcp/tools/search.ts +3 -1
- package/src/mcp/tools/sessions.ts +208 -0
- package/src/mcp/tools/vsearch.ts +3 -1
- package/src/sdk/client.ts +252 -89
- package/src/sdk/index.ts +13 -0
- package/src/sdk/types.ts +82 -3
- package/src/serve/capture-service.ts +98 -32
- package/src/serve/compiled-context.ts +84 -0
- package/src/serve/config-sync.ts +3 -2
- package/src/serve/public/app.tsx +15 -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 +4 -0
- package/src/serve/public/pages/CompiledContext.tsx +364 -0
- package/src/serve/public/pages/Dashboard.tsx +17 -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 +248 -3
- package/src/serve/session-automation.ts +146 -0
- package/src/serve/spa-production-build.ts +6 -5
- 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 +68 -16
- package/src/store/sqlite/scoped-index.ts +9 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.4.0.zip.sha256 +0 -1
|
@@ -9,7 +9,11 @@ import type { Collection, Config } from "../../config/types";
|
|
|
9
9
|
import type { SyncResult } from "../../ingestion";
|
|
10
10
|
import type { SearchResults } from "../../pipeline/types";
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
DEFAULT_INDEX_NAME,
|
|
14
|
+
decorateUriForIndex,
|
|
15
|
+
getIndexDbPath,
|
|
16
|
+
} from "../../app/constants";
|
|
13
17
|
import {
|
|
14
18
|
getConfigPaths,
|
|
15
19
|
isInitialized,
|
|
@@ -17,6 +21,7 @@ import {
|
|
|
17
21
|
writeConfigWarningsToStderr,
|
|
18
22
|
} from "../../config";
|
|
19
23
|
import { SqliteAdapter } from "../../store/sqlite/adapter";
|
|
24
|
+
import { assertCliSessionBinding } from "../session-binding";
|
|
20
25
|
|
|
21
26
|
/**
|
|
22
27
|
* Result of CLI store initialization.
|
|
@@ -90,6 +95,14 @@ export async function initStore(
|
|
|
90
95
|
};
|
|
91
96
|
}
|
|
92
97
|
|
|
98
|
+
// Every index a command opens (including one named by a `?index=` URI)
|
|
99
|
+
// honours the session-archive config/index binding.
|
|
100
|
+
await assertCliSessionBinding(
|
|
101
|
+
options.configPath,
|
|
102
|
+
options.indexName ?? DEFAULT_INDEX_NAME,
|
|
103
|
+
config
|
|
104
|
+
);
|
|
105
|
+
|
|
93
106
|
// Ensure data directory exists (may have been deleted by reset)
|
|
94
107
|
const { ensureDirectories } = await import("../../config");
|
|
95
108
|
await ensureDirectories();
|
package/src/cli/errors.ts
CHANGED
|
@@ -15,7 +15,9 @@ export type CliErrorCode =
|
|
|
15
15
|
| "NOT_RUNNING"
|
|
16
16
|
| "BUSY"
|
|
17
17
|
| "AUDIT_FINDINGS"
|
|
18
|
-
| "AUDIT_PARTIAL"
|
|
18
|
+
| "AUDIT_PARTIAL"
|
|
19
|
+
| "CONTEXT_STALE"
|
|
20
|
+
| "CONTEXT_CONFLICT";
|
|
19
21
|
|
|
20
22
|
export interface CliErrorOptions {
|
|
21
23
|
details?: Record<string, unknown>;
|
|
@@ -72,10 +74,15 @@ export function exitCodeFor(err: CliError): 1 | 2 | 3 | 4 | 5 {
|
|
|
72
74
|
if (err.code === "VALIDATION") {
|
|
73
75
|
return 1;
|
|
74
76
|
}
|
|
75
|
-
if (err.code === "NOT_RUNNING") {
|
|
77
|
+
if (err.code === "NOT_RUNNING" || err.code === "CONTEXT_STALE") {
|
|
76
78
|
return 3;
|
|
77
79
|
}
|
|
78
|
-
if (
|
|
80
|
+
if (
|
|
81
|
+
err.code === "BUSY" ||
|
|
82
|
+
err.code === "AUDIT_FINDINGS" ||
|
|
83
|
+
err.code === "CONTEXT_CONFLICT"
|
|
84
|
+
)
|
|
85
|
+
return 4;
|
|
79
86
|
if (err.code === "AUDIT_PARTIAL") return 5;
|
|
80
87
|
return 2;
|
|
81
88
|
}
|
package/src/cli/program.ts
CHANGED
|
@@ -344,7 +344,7 @@ export function createProgram(): Command {
|
|
|
344
344
|
.option("--no-pager", "disable automatic paging of long output");
|
|
345
345
|
|
|
346
346
|
// Resolve globals ONCE before any command runs (ensures consistency)
|
|
347
|
-
program.hook("preAction", (thisCommand) => {
|
|
347
|
+
program.hook("preAction", async (thisCommand) => {
|
|
348
348
|
const rootOpts = thisCommand.optsWithGlobals();
|
|
349
349
|
const globals = parseGlobalOptions(rootOpts);
|
|
350
350
|
if (!isValidIndexName(globals.index)) {
|
|
@@ -355,6 +355,8 @@ export function createProgram(): Command {
|
|
|
355
355
|
}
|
|
356
356
|
applyGlobalOptions(globals);
|
|
357
357
|
globalState.current = globals;
|
|
358
|
+
const { assertCliSessionBinding } = await import("./session-binding");
|
|
359
|
+
await assertCliSessionBinding(globals.config, globals.index);
|
|
358
360
|
});
|
|
359
361
|
|
|
360
362
|
// Wire command groups
|
|
@@ -362,6 +364,7 @@ export function createProgram(): Command {
|
|
|
362
364
|
wireOnboardingCommands(program);
|
|
363
365
|
wireCaptureCommand(program);
|
|
364
366
|
wireMemoryCommands(program);
|
|
367
|
+
wireSessionsCommands(program);
|
|
365
368
|
wireManagementCommands(program);
|
|
366
369
|
wireTraceCommands(program);
|
|
367
370
|
wirePublishCommand(program);
|
|
@@ -1874,6 +1877,10 @@ function wireCaptureCommand(program: Command): void {
|
|
|
1874
1877
|
.option("--source-author <author>", "source author")
|
|
1875
1878
|
.option("--source-date <date>", "source observed date/time")
|
|
1876
1879
|
.option("--source-id <id>", "source external id")
|
|
1880
|
+
.option(
|
|
1881
|
+
"--request-id <id>",
|
|
1882
|
+
"retry identity: reuse it to retry this same capture after a lost response"
|
|
1883
|
+
)
|
|
1877
1884
|
.option("--json", "JSON output")
|
|
1878
1885
|
.action(
|
|
1879
1886
|
async (contentParts: string[], cmdOpts: Record<string, unknown>) => {
|
|
@@ -1902,6 +1909,7 @@ function wireCaptureCommand(program: Command): void {
|
|
|
1902
1909
|
sourceAuthor: cmdOpts.sourceAuthor as string | undefined,
|
|
1903
1910
|
sourceDate: cmdOpts.sourceDate as string | undefined,
|
|
1904
1911
|
sourceId: cmdOpts.sourceId as string | undefined,
|
|
1912
|
+
requestId: cmdOpts.requestId as string | undefined,
|
|
1905
1913
|
});
|
|
1906
1914
|
const output = formatCaptureReceipt(receipt, {
|
|
1907
1915
|
json: format === "json",
|
|
@@ -1910,6 +1918,27 @@ function wireCaptureCommand(program: Command): void {
|
|
|
1910
1918
|
await writeOutput(output, format);
|
|
1911
1919
|
}
|
|
1912
1920
|
);
|
|
1921
|
+
|
|
1922
|
+
program
|
|
1923
|
+
.command("request-status <request-id>")
|
|
1924
|
+
.description(
|
|
1925
|
+
"Show whether a capture/remember request ID is pending, committed, or unknown"
|
|
1926
|
+
)
|
|
1927
|
+
.option("--json", "JSON output")
|
|
1928
|
+
.action(async (requestId: string, cmdOpts: Record<string, unknown>) => {
|
|
1929
|
+
const format = getFormat(cmdOpts);
|
|
1930
|
+
const globals = getGlobals();
|
|
1931
|
+
const { formatRequestStatusOutput, requestStatus } =
|
|
1932
|
+
await import("./commands/request-status");
|
|
1933
|
+
const result = await requestStatus({
|
|
1934
|
+
requestId,
|
|
1935
|
+
indexName: globals.index,
|
|
1936
|
+
});
|
|
1937
|
+
await writeOutput(
|
|
1938
|
+
formatRequestStatusOutput(result, { json: format === "json" }),
|
|
1939
|
+
format
|
|
1940
|
+
);
|
|
1941
|
+
});
|
|
1913
1942
|
}
|
|
1914
1943
|
|
|
1915
1944
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -1957,6 +1986,10 @@ function wireMemoryCommands(program: Command): void {
|
|
|
1957
1986
|
"--session <id>",
|
|
1958
1987
|
"session identity (default: $GNO_MEMORY_SESSION or ppid:<pid>)"
|
|
1959
1988
|
)
|
|
1989
|
+
.option(
|
|
1990
|
+
"--request-id <id>",
|
|
1991
|
+
"retry identity: reuse it to retry this same write after a lost response"
|
|
1992
|
+
)
|
|
1960
1993
|
.option("--json", "JSON output")
|
|
1961
1994
|
.action(async (text: string, cmdOpts: Record<string, unknown>) => {
|
|
1962
1995
|
const format = getFormat(cmdOpts);
|
|
@@ -1979,6 +2012,7 @@ function wireMemoryCommands(program: Command): void {
|
|
|
1979
2012
|
source: cmdOpts.source as string | undefined,
|
|
1980
2013
|
caller: cmdOpts.caller as string | undefined,
|
|
1981
2014
|
session: cmdOpts.session as string | undefined,
|
|
2015
|
+
requestId: cmdOpts.requestId as string | undefined,
|
|
1982
2016
|
});
|
|
1983
2017
|
await writeOutput(
|
|
1984
2018
|
formatRememberResult(result, {
|
|
@@ -2038,6 +2072,358 @@ function wireMemoryCommands(program: Command): void {
|
|
|
2038
2072
|
});
|
|
2039
2073
|
}
|
|
2040
2074
|
|
|
2075
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2076
|
+
// Session archive commands (manual import only)
|
|
2077
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2078
|
+
|
|
2079
|
+
function wireSessionsCommands(program: Command): void {
|
|
2080
|
+
const sessionsCmd = program
|
|
2081
|
+
.command("sessions")
|
|
2082
|
+
.description(
|
|
2083
|
+
"Discover and manually import local agent sessions into a dedicated archive"
|
|
2084
|
+
);
|
|
2085
|
+
const context = () => {
|
|
2086
|
+
const globals = getGlobals();
|
|
2087
|
+
return { configPath: globals.config, indexName: globals.index };
|
|
2088
|
+
};
|
|
2089
|
+
const asJson = (cmdOpts: Record<string, unknown>) =>
|
|
2090
|
+
getFormat(cmdOpts) === "json";
|
|
2091
|
+
|
|
2092
|
+
sessionsCmd
|
|
2093
|
+
.command("discover", { isDefault: true })
|
|
2094
|
+
.description("Preview supported local session sources (never imports)")
|
|
2095
|
+
.option("--json", "JSON output")
|
|
2096
|
+
.action(async (cmdOpts: Record<string, unknown>) => {
|
|
2097
|
+
const { discoverSessions, formatDiscovery } =
|
|
2098
|
+
await import("./commands/sessions");
|
|
2099
|
+
const result = await discoverSessions(context());
|
|
2100
|
+
await writeOutput(
|
|
2101
|
+
formatDiscovery(result, asJson(cmdOpts)),
|
|
2102
|
+
getFormat(cmdOpts)
|
|
2103
|
+
);
|
|
2104
|
+
});
|
|
2105
|
+
|
|
2106
|
+
sessionsCmd
|
|
2107
|
+
.command("init")
|
|
2108
|
+
.description("Create or extend the dedicated archive config/index pair")
|
|
2109
|
+
.option(
|
|
2110
|
+
"--archive <dir>",
|
|
2111
|
+
"absolute archive directory (outside the curated vault)"
|
|
2112
|
+
)
|
|
2113
|
+
.option("--collection <name>", "archive collection to create")
|
|
2114
|
+
.option("--json", "JSON output")
|
|
2115
|
+
.action(async (cmdOpts: Record<string, unknown>) => {
|
|
2116
|
+
const { initSessions } = await import("./commands/sessions");
|
|
2117
|
+
const result = await initSessions(context(), {
|
|
2118
|
+
archive: cmdOpts.archive as string | undefined,
|
|
2119
|
+
collection: cmdOpts.collection as string | undefined,
|
|
2120
|
+
});
|
|
2121
|
+
await writeOutput(
|
|
2122
|
+
asJson(cmdOpts)
|
|
2123
|
+
? JSON.stringify(result, null, 2)
|
|
2124
|
+
: `Session archive ${result.created ? "created" : "ready"}: ${result.archiveRoot} (collection ${result.collection}, index ${result.index}, config ${result.configPath})`,
|
|
2125
|
+
getFormat(cmdOpts)
|
|
2126
|
+
);
|
|
2127
|
+
});
|
|
2128
|
+
|
|
2129
|
+
const sourceCmd = sessionsCmd
|
|
2130
|
+
.command("source")
|
|
2131
|
+
.description("Register or unregister owner session sources");
|
|
2132
|
+
sourceCmd
|
|
2133
|
+
.command("add <id>")
|
|
2134
|
+
.description("Register a harness session root for manual import")
|
|
2135
|
+
.option("--harness <harness>", "codex | claude-code | openclaw | hermes")
|
|
2136
|
+
.option("--path <path>", "absolute harness session root, file or database")
|
|
2137
|
+
.option("--collection <name>", "default archive collection")
|
|
2138
|
+
.option(
|
|
2139
|
+
"--project <prefix=collection>",
|
|
2140
|
+
"owner-approved working-directory mapping (repeatable)",
|
|
2141
|
+
collectRepeatableValue,
|
|
2142
|
+
[]
|
|
2143
|
+
)
|
|
2144
|
+
.option("--json", "JSON output")
|
|
2145
|
+
.action(async (id: string, cmdOpts: Record<string, unknown>) => {
|
|
2146
|
+
const { addSource } = await import("./commands/sessions");
|
|
2147
|
+
const result = await addSource(context(), {
|
|
2148
|
+
id,
|
|
2149
|
+
harness: cmdOpts.harness as string | undefined,
|
|
2150
|
+
path: cmdOpts.path as string | undefined,
|
|
2151
|
+
collection: cmdOpts.collection as string | undefined,
|
|
2152
|
+
projects: cmdOpts.project as string[],
|
|
2153
|
+
});
|
|
2154
|
+
await writeOutput(
|
|
2155
|
+
asJson(cmdOpts)
|
|
2156
|
+
? JSON.stringify(result, null, 2)
|
|
2157
|
+
: `Source ${result.id} registered. Nothing was imported.`,
|
|
2158
|
+
getFormat(cmdOpts)
|
|
2159
|
+
);
|
|
2160
|
+
});
|
|
2161
|
+
sourceCmd
|
|
2162
|
+
.command("remove <id>")
|
|
2163
|
+
.description("Unregister a source (its archive is retained)")
|
|
2164
|
+
.option("--json", "JSON output")
|
|
2165
|
+
.action(async (id: string, cmdOpts: Record<string, unknown>) => {
|
|
2166
|
+
const { removeSource } = await import("./commands/sessions");
|
|
2167
|
+
const result = await removeSource(context(), id);
|
|
2168
|
+
await writeOutput(
|
|
2169
|
+
asJson(cmdOpts)
|
|
2170
|
+
? JSON.stringify(result, null, 2)
|
|
2171
|
+
: `Source ${result.id} removed; archived sessions were retained.`,
|
|
2172
|
+
getFormat(cmdOpts)
|
|
2173
|
+
);
|
|
2174
|
+
});
|
|
2175
|
+
|
|
2176
|
+
sessionsCmd
|
|
2177
|
+
.command("import [paths...]")
|
|
2178
|
+
.description("Import selected sources or paths into the archive (manual)")
|
|
2179
|
+
.option("--source <id>", "registered source to import")
|
|
2180
|
+
.option(
|
|
2181
|
+
"--collection <name>",
|
|
2182
|
+
"destination archive collection (path imports)"
|
|
2183
|
+
)
|
|
2184
|
+
.option("--format <harness>", "codex | claude-code | openclaw | hermes")
|
|
2185
|
+
.option("--dry-run", "parse and report without writing archive or index")
|
|
2186
|
+
.option("--limit <n>", "maximum changed units processed this run")
|
|
2187
|
+
.option("--json", "JSON output")
|
|
2188
|
+
.action(async (paths: string[], cmdOpts: Record<string, unknown>) => {
|
|
2189
|
+
const { formatImportReceipt, importSessions } =
|
|
2190
|
+
await import("./commands/sessions");
|
|
2191
|
+
const receipt = await importSessions(context(), {
|
|
2192
|
+
paths,
|
|
2193
|
+
source: cmdOpts.source as string | undefined,
|
|
2194
|
+
collection: cmdOpts.collection as string | undefined,
|
|
2195
|
+
format: cmdOpts.format as string | undefined,
|
|
2196
|
+
dryRun: Boolean(cmdOpts.dryRun),
|
|
2197
|
+
limit: cmdOpts.limit,
|
|
2198
|
+
});
|
|
2199
|
+
await writeOutput(
|
|
2200
|
+
formatImportReceipt(receipt, asJson(cmdOpts)),
|
|
2201
|
+
getFormat(cmdOpts)
|
|
2202
|
+
);
|
|
2203
|
+
if (receipt.status === "failed") {
|
|
2204
|
+
const unsupportedOnly =
|
|
2205
|
+
receipt.counts.unsupported > 0 &&
|
|
2206
|
+
receipt.counts.failed === 0 &&
|
|
2207
|
+
receipt.counts.incomplete === 0;
|
|
2208
|
+
throw unsupportedOnly
|
|
2209
|
+
? new CliError(
|
|
2210
|
+
"VALIDATION",
|
|
2211
|
+
"No selected unit is a supported session format; see receipt.",
|
|
2212
|
+
{ details: { sessionsCode: "SESSIONS_UNSUPPORTED_FORMAT" } }
|
|
2213
|
+
)
|
|
2214
|
+
: new CliError("RUNTIME", "Session import failed; see receipt.", {
|
|
2215
|
+
details: { sessionsCode: "SESSIONS_IMPORT_FAILED" },
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
|
|
2220
|
+
sessionsCmd
|
|
2221
|
+
.command("status")
|
|
2222
|
+
.description("Show archive, source and checkpoint status")
|
|
2223
|
+
.option("--json", "JSON output")
|
|
2224
|
+
.action(async (cmdOpts: Record<string, unknown>) => {
|
|
2225
|
+
const { formatStatus, sessionsStatus } =
|
|
2226
|
+
await import("./commands/sessions");
|
|
2227
|
+
const result = await sessionsStatus(context());
|
|
2228
|
+
await writeOutput(
|
|
2229
|
+
formatStatus(result, asJson(cmdOpts)),
|
|
2230
|
+
getFormat(cmdOpts)
|
|
2231
|
+
);
|
|
2232
|
+
});
|
|
2233
|
+
|
|
2234
|
+
wireSessionsAutomationCommands(sessionsCmd, context, asJson);
|
|
2235
|
+
|
|
2236
|
+
sessionsCmd
|
|
2237
|
+
.command("prune")
|
|
2238
|
+
.description(
|
|
2239
|
+
"Preview (or --apply) removal of archives whose source is gone"
|
|
2240
|
+
)
|
|
2241
|
+
.option("--source <id>", "source to prune")
|
|
2242
|
+
.option("--apply", "delete the previewed archive files")
|
|
2243
|
+
.option("--json", "JSON output")
|
|
2244
|
+
.action(async (cmdOpts: Record<string, unknown>) => {
|
|
2245
|
+
const { formatPrune, pruneSessions } =
|
|
2246
|
+
await import("./commands/sessions");
|
|
2247
|
+
const result = await pruneSessions(context(), {
|
|
2248
|
+
source: cmdOpts.source as string | undefined,
|
|
2249
|
+
apply: Boolean(cmdOpts.apply),
|
|
2250
|
+
});
|
|
2251
|
+
await writeOutput(
|
|
2252
|
+
formatPrune(result, asJson(cmdOpts)),
|
|
2253
|
+
getFormat(cmdOpts)
|
|
2254
|
+
);
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
/** Opt-in automation: profiles, explicit trigger switches, run-now, host hook. */
|
|
2259
|
+
function wireSessionsAutomationCommands(
|
|
2260
|
+
sessionsCmd: Command,
|
|
2261
|
+
context: () => { configPath?: string; indexName: string },
|
|
2262
|
+
asJson: (cmdOpts: Record<string, unknown>) => boolean
|
|
2263
|
+
): void {
|
|
2264
|
+
const automationCmd = sessionsCmd
|
|
2265
|
+
.command("automation")
|
|
2266
|
+
.description(
|
|
2267
|
+
"Opt-in hooks and daemon schedules for registered sources (off until enabled)"
|
|
2268
|
+
);
|
|
2269
|
+
|
|
2270
|
+
automationCmd
|
|
2271
|
+
.command("set <profile>")
|
|
2272
|
+
.description(
|
|
2273
|
+
"Create or reconfigure a profile (sources, cadence, budget); enables nothing"
|
|
2274
|
+
)
|
|
2275
|
+
.option(
|
|
2276
|
+
"--source <id>",
|
|
2277
|
+
"registered source to include (repeatable)",
|
|
2278
|
+
collectRepeatableValue,
|
|
2279
|
+
[]
|
|
2280
|
+
)
|
|
2281
|
+
.option("--cadence <n>", "elapsed schedule cadence <n>s|m|h|d (min 1m)")
|
|
2282
|
+
.option("--limit <n>", "changed units per source per run")
|
|
2283
|
+
.option("--retries <n>", "automatic retries after a failed run")
|
|
2284
|
+
.option("--json", "JSON output")
|
|
2285
|
+
.action(async (id: string, cmdOpts: Record<string, unknown>) => {
|
|
2286
|
+
const { formatAutomationPreview, setAutomation } =
|
|
2287
|
+
await import("./commands/sessions");
|
|
2288
|
+
const preview = await setAutomation(context(), id, {
|
|
2289
|
+
sources: cmdOpts.source as string[],
|
|
2290
|
+
cadence: cmdOpts.cadence as string | undefined,
|
|
2291
|
+
limit: cmdOpts.limit,
|
|
2292
|
+
retries: cmdOpts.retries,
|
|
2293
|
+
});
|
|
2294
|
+
await writeOutput(
|
|
2295
|
+
formatAutomationPreview(preview, asJson(cmdOpts)),
|
|
2296
|
+
getFormat(cmdOpts)
|
|
2297
|
+
);
|
|
2298
|
+
});
|
|
2299
|
+
|
|
2300
|
+
automationCmd
|
|
2301
|
+
.command("preview <profile>")
|
|
2302
|
+
.description(
|
|
2303
|
+
"Show sources, destinations, hook command, schedule and daemon prerequisite"
|
|
2304
|
+
)
|
|
2305
|
+
.option("--settings <path>", "Claude Code settings file to preview")
|
|
2306
|
+
.option("--json", "JSON output")
|
|
2307
|
+
.action(async (id: string, cmdOpts: Record<string, unknown>) => {
|
|
2308
|
+
const { formatAutomationPreview, previewAutomation } =
|
|
2309
|
+
await import("./commands/sessions");
|
|
2310
|
+
const preview = await previewAutomation(context(), id, {
|
|
2311
|
+
settings: cmdOpts.settings as string | undefined,
|
|
2312
|
+
});
|
|
2313
|
+
await writeOutput(
|
|
2314
|
+
formatAutomationPreview(preview, asJson(cmdOpts)),
|
|
2315
|
+
getFormat(cmdOpts)
|
|
2316
|
+
);
|
|
2317
|
+
});
|
|
2318
|
+
|
|
2319
|
+
automationCmd
|
|
2320
|
+
.command("enable <profile>")
|
|
2321
|
+
.description(
|
|
2322
|
+
"Explicitly switch on a host hook and/or the daemon schedule for a profile"
|
|
2323
|
+
)
|
|
2324
|
+
.option("--hook <harness>", "install the owned hook (claude-code)")
|
|
2325
|
+
.option(
|
|
2326
|
+
"--settings <path>",
|
|
2327
|
+
"Claude Code settings file (default: $CLAUDE_CONFIG_DIR or ~/.claude settings.json)"
|
|
2328
|
+
)
|
|
2329
|
+
.option("--schedule", "run on the daemon's elapsed cadence")
|
|
2330
|
+
.option("--cadence <n>", "elapsed cadence <n>s|m|h|d (min 1m)")
|
|
2331
|
+
.option("--json", "JSON output")
|
|
2332
|
+
.action(async (id: string, cmdOpts: Record<string, unknown>) => {
|
|
2333
|
+
const { enableAutomationCli, formatAutomationPreview } =
|
|
2334
|
+
await import("./commands/sessions");
|
|
2335
|
+
const preview = await enableAutomationCli(context(), id, {
|
|
2336
|
+
hook: cmdOpts.hook as string | undefined,
|
|
2337
|
+
settings: cmdOpts.settings as string | undefined,
|
|
2338
|
+
schedule: Boolean(cmdOpts.schedule),
|
|
2339
|
+
cadence: cmdOpts.cadence as string | undefined,
|
|
2340
|
+
});
|
|
2341
|
+
await writeOutput(
|
|
2342
|
+
formatAutomationPreview(preview, asJson(cmdOpts)),
|
|
2343
|
+
getFormat(cmdOpts)
|
|
2344
|
+
);
|
|
2345
|
+
});
|
|
2346
|
+
|
|
2347
|
+
automationCmd
|
|
2348
|
+
.command("disable <profile>")
|
|
2349
|
+
.description(
|
|
2350
|
+
"Pause: switch triggers off, remove the owned hook entry, clear pending work"
|
|
2351
|
+
)
|
|
2352
|
+
.option("--hook", "only the hook")
|
|
2353
|
+
.option("--schedule", "only the schedule")
|
|
2354
|
+
.option("--json", "JSON output")
|
|
2355
|
+
.action(async (id: string, cmdOpts: Record<string, unknown>) => {
|
|
2356
|
+
const { disableAutomationCli, formatAutomationChange } =
|
|
2357
|
+
await import("./commands/sessions");
|
|
2358
|
+
const change = await disableAutomationCli(context(), id, {
|
|
2359
|
+
hook: Boolean(cmdOpts.hook),
|
|
2360
|
+
schedule: Boolean(cmdOpts.schedule),
|
|
2361
|
+
});
|
|
2362
|
+
await writeOutput(
|
|
2363
|
+
formatAutomationChange(change, asJson(cmdOpts)),
|
|
2364
|
+
getFormat(cmdOpts)
|
|
2365
|
+
);
|
|
2366
|
+
});
|
|
2367
|
+
|
|
2368
|
+
automationCmd
|
|
2369
|
+
.command("remove <profile>")
|
|
2370
|
+
.description(
|
|
2371
|
+
"Uninstall owned integrations and delete the profile (archive retained)"
|
|
2372
|
+
)
|
|
2373
|
+
.option("--json", "JSON output")
|
|
2374
|
+
.action(async (id: string, cmdOpts: Record<string, unknown>) => {
|
|
2375
|
+
const { formatAutomationChange, removeAutomation } =
|
|
2376
|
+
await import("./commands/sessions");
|
|
2377
|
+
const change = await removeAutomation(context(), id);
|
|
2378
|
+
await writeOutput(
|
|
2379
|
+
formatAutomationChange(change, asJson(cmdOpts)),
|
|
2380
|
+
getFormat(cmdOpts)
|
|
2381
|
+
);
|
|
2382
|
+
});
|
|
2383
|
+
|
|
2384
|
+
automationCmd
|
|
2385
|
+
.command("run <profile>")
|
|
2386
|
+
.description("Run a profile now through the manual importer")
|
|
2387
|
+
.option("--json", "JSON output")
|
|
2388
|
+
.action(async (id: string, cmdOpts: Record<string, unknown>) => {
|
|
2389
|
+
const { formatAutomationRun, runAutomation } =
|
|
2390
|
+
await import("./commands/sessions");
|
|
2391
|
+
const result = await runAutomation(context(), id);
|
|
2392
|
+
await writeOutput(
|
|
2393
|
+
formatAutomationRun(result, asJson(cmdOpts)),
|
|
2394
|
+
getFormat(cmdOpts)
|
|
2395
|
+
);
|
|
2396
|
+
if (result.outcome === "failed" && result.reason === "busy") {
|
|
2397
|
+
throw new CliError(
|
|
2398
|
+
"BUSY",
|
|
2399
|
+
"The archive is busy (another import or index writer); the run is recorded; a running daemon retries it, or run it again once the archive is free.",
|
|
2400
|
+
{ details: { sessionsCode: "SESSIONS_BUSY" } }
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
if (result.outcome === "failed") {
|
|
2404
|
+
throw new CliError(
|
|
2405
|
+
"RUNTIME",
|
|
2406
|
+
`Automation run failed (${result.reason ?? "unknown"}); see gno sessions status.`,
|
|
2407
|
+
{ details: { sessionsCode: "SESSIONS_IMPORT_FAILED" } }
|
|
2408
|
+
);
|
|
2409
|
+
}
|
|
2410
|
+
});
|
|
2411
|
+
|
|
2412
|
+
sessionsCmd
|
|
2413
|
+
.command("hook <harness>")
|
|
2414
|
+
.description(
|
|
2415
|
+
"Host hook entrypoint: durably mark a profile pending (installed by automation enable)"
|
|
2416
|
+
)
|
|
2417
|
+
.option("--profile <id>", "automation profile")
|
|
2418
|
+
.action(async (harness: string, cmdOpts: Record<string, unknown>) => {
|
|
2419
|
+
const { runSessionsHook } = await import("./commands/sessions");
|
|
2420
|
+
const line = await runSessionsHook(context(), harness, {
|
|
2421
|
+
profile: cmdOpts.profile as string | undefined,
|
|
2422
|
+
});
|
|
2423
|
+
process.stdout.write(`${line}\n`);
|
|
2424
|
+
});
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2041
2427
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
2042
2428
|
// Retrieval Commands (get, multi-get, ls)
|
|
2043
2429
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -2169,6 +2555,7 @@ function wireRetrievalCommands(program: Command): void {
|
|
|
2169
2555
|
const { ls, formatLs } = await import("./commands/ls");
|
|
2170
2556
|
const result = await ls(scope, {
|
|
2171
2557
|
configPath: globals.config,
|
|
2558
|
+
indexName: globals.index,
|
|
2172
2559
|
limit: cmdOpts.limit as number | undefined,
|
|
2173
2560
|
offset: cmdOpts.offset as number | undefined,
|
|
2174
2561
|
json: format === "json",
|
|
@@ -2803,6 +3190,76 @@ function wireManagementCommands(program: Command): void {
|
|
|
2803
3190
|
await writeOutput(output, format === "json" ? "json" : "md");
|
|
2804
3191
|
});
|
|
2805
3192
|
|
|
3193
|
+
const compiledCmd = contextCmd
|
|
3194
|
+
.command("compiled")
|
|
3195
|
+
.description("Compile, check and refresh verified project context");
|
|
3196
|
+
for (const operation of ["preview", "compile", "check", "refresh"] as const) {
|
|
3197
|
+
const command = compiledCmd
|
|
3198
|
+
.command(
|
|
3199
|
+
operation === "check" || operation === "refresh"
|
|
3200
|
+
? `${operation} <file>`
|
|
3201
|
+
: operation
|
|
3202
|
+
)
|
|
3203
|
+
.description(`${operation} a separate managed context artifact`)
|
|
3204
|
+
.option("--json", "JSON output");
|
|
3205
|
+
if (operation === "preview" || operation === "compile") {
|
|
3206
|
+
command
|
|
3207
|
+
.requiredOption("--capsule <file>", "explicit Capsule JSON file")
|
|
3208
|
+
.requiredOption("--budget <tokens>", "whole-output token budget")
|
|
3209
|
+
.option("--bytes <bytes>", "whole-output byte budget");
|
|
3210
|
+
}
|
|
3211
|
+
if (operation === "compile")
|
|
3212
|
+
command.requiredOption(
|
|
3213
|
+
"--output <file>",
|
|
3214
|
+
"new *.gno-context.md artifact"
|
|
3215
|
+
);
|
|
3216
|
+
if (operation === "check")
|
|
3217
|
+
command.option("--capsule <file>", "relocated Capsule JSON file");
|
|
3218
|
+
if (operation === "refresh")
|
|
3219
|
+
command.requiredOption(
|
|
3220
|
+
"--capsule-output <file>",
|
|
3221
|
+
"explicit refreshed Capsule destination"
|
|
3222
|
+
);
|
|
3223
|
+
command.action(async (...args: unknown[]) => {
|
|
3224
|
+
const file =
|
|
3225
|
+
operation === "check" || operation === "refresh"
|
|
3226
|
+
? (args[0] as string)
|
|
3227
|
+
: undefined;
|
|
3228
|
+
const opts = args[file === undefined ? 0 : 1] as Record<string, unknown>;
|
|
3229
|
+
const globals = getGlobals();
|
|
3230
|
+
const { contextCompiled } = await import("./commands/context-compiled");
|
|
3231
|
+
const result = await contextCompiled(operation, {
|
|
3232
|
+
configPath: globals.config,
|
|
3233
|
+
indexName: globals.index,
|
|
3234
|
+
capsulePath: opts.capsule as string | undefined,
|
|
3235
|
+
capsuleOutputPath: opts.capsuleOutput as string | undefined,
|
|
3236
|
+
outputPath: file ?? (opts.output as string | undefined),
|
|
3237
|
+
budgetTokens:
|
|
3238
|
+
opts.budget === undefined
|
|
3239
|
+
? undefined
|
|
3240
|
+
: parseContextInteger("budget", opts.budget),
|
|
3241
|
+
budgetBytes:
|
|
3242
|
+
opts.bytes === undefined
|
|
3243
|
+
? undefined
|
|
3244
|
+
: parseContextInteger("bytes", opts.bytes),
|
|
3245
|
+
json: Boolean(opts.json || globals.json),
|
|
3246
|
+
});
|
|
3247
|
+
await writeOutput(
|
|
3248
|
+
result.output,
|
|
3249
|
+
opts.json || globals.json ? "json" : "terminal"
|
|
3250
|
+
);
|
|
3251
|
+
if (result.status && result.status !== "current") {
|
|
3252
|
+
const code =
|
|
3253
|
+
result.status === "stale"
|
|
3254
|
+
? "CONTEXT_STALE"
|
|
3255
|
+
: result.status === "conflict"
|
|
3256
|
+
? "CONTEXT_CONFLICT"
|
|
3257
|
+
: "RUNTIME";
|
|
3258
|
+
throw new CliError(code, result.status, { silent: true });
|
|
3259
|
+
}
|
|
3260
|
+
});
|
|
3261
|
+
}
|
|
3262
|
+
|
|
2806
3263
|
contextCmd
|
|
2807
3264
|
.command("verify <file>")
|
|
2808
3265
|
.description("Verify a saved Context Capsule without rebuilding it")
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-wide enforcement of the session-archive config/index binding.
|
|
3
|
+
*
|
|
4
|
+
* Runs before every command and again whenever a command opens an index
|
|
5
|
+
* (`initStore`, which also covers indexes named by a `?index=` URI): a config
|
|
6
|
+
* bound to a session archive index cannot be used with another index, and an
|
|
7
|
+
* archive index cannot be opened with a different config.
|
|
8
|
+
*
|
|
9
|
+
* @module src/cli/session-binding
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Config } from "../config/types";
|
|
13
|
+
|
|
14
|
+
import { getIndexDbPath } from "../app/constants";
|
|
15
|
+
import { getConfigPaths, loadConfig } from "../config";
|
|
16
|
+
import { assertSessionBinding } from "../sessions/binding";
|
|
17
|
+
import { SessionsError } from "../sessions/types";
|
|
18
|
+
import { CliError } from "./errors";
|
|
19
|
+
|
|
20
|
+
export async function assertCliSessionBinding(
|
|
21
|
+
configPath: string | undefined,
|
|
22
|
+
indexName: string,
|
|
23
|
+
loadedConfig?: Config
|
|
24
|
+
): Promise<void> {
|
|
25
|
+
const path = configPath ?? getConfigPaths().configFile;
|
|
26
|
+
let config = loadedConfig;
|
|
27
|
+
if (!config) {
|
|
28
|
+
if (!(await Bun.file(path).exists())) return;
|
|
29
|
+
const loaded = await loadConfig(path);
|
|
30
|
+
// Unreadable configs are reported by the command itself.
|
|
31
|
+
if (!loaded.ok) return;
|
|
32
|
+
config = loaded.value;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
await assertSessionBinding({
|
|
36
|
+
config,
|
|
37
|
+
configPath: path,
|
|
38
|
+
indexName,
|
|
39
|
+
dbPath: getIndexDbPath(indexName),
|
|
40
|
+
});
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error instanceof SessionsError) {
|
|
43
|
+
throw new CliError("VALIDATION", error.message, {
|
|
44
|
+
details: { sessionsCode: error.code },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/config/types.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { z } from "zod";
|
|
|
12
12
|
import { URI_PREFIX } from "../app/constants";
|
|
13
13
|
import { JsonlFieldMappingSchema } from "../converters/adapters/jsonl/config";
|
|
14
14
|
import { MCP_TOOL_PROFILES } from "../mcp/tool-profile";
|
|
15
|
+
import { SessionsConfigSchema } from "../sessions/config";
|
|
15
16
|
import { ChunkingConfigSchema, type ChunkingParams } from "./chunking";
|
|
16
17
|
import { RetrievalTraceConfigSchema } from "./retrieval-traces";
|
|
17
18
|
|
|
@@ -614,6 +615,13 @@ export const ConfigSchema = z.object({
|
|
|
614
615
|
/** Bounded project-aware retrieval affinity. */
|
|
615
616
|
projectAffinity: ProjectAffinityConfigSchema.optional(),
|
|
616
617
|
|
|
618
|
+
/**
|
|
619
|
+
* Session-archive binding. Present only in a dedicated archive config; it
|
|
620
|
+
* binds the file to one index name and archive root and lists
|
|
621
|
+
* owner-registered session sources.
|
|
622
|
+
*/
|
|
623
|
+
sessions: SessionsConfigSchema.optional(),
|
|
624
|
+
|
|
617
625
|
/** Machine-local, timestamp-free project profile provenance. */
|
|
618
626
|
projectProfileBindings: z
|
|
619
627
|
.array(ProjectProfileBindingSchema)
|