@ashlr/hub 3.7.0 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +257 -0
- package/dist/build-identity.json +2 -2
- package/dist/cli/resource-profile.js +95 -21
- package/dist/cli/resource-profile.js.map +1 -1
- package/dist/core/policy/local-only.d.ts +142 -2
- package/dist/core/policy/local-only.js +204 -15
- package/dist/core/policy/local-only.js.map +1 -1
- package/dist/core/resources/native-profile.d.ts +56 -0
- package/dist/core/resources/native-profile.js +368 -6
- package/dist/core/resources/native-profile.js.map +1 -1
- package/dist/core/run/budget.d.ts +22 -8
- package/dist/core/run/budget.js +80 -12
- package/dist/core/run/budget.js.map +1 -1
- package/dist/core/run/model-catalog.js +8 -1
- package/dist/core/run/model-catalog.js.map +1 -1
- package/dist/core/run/sandboxed-engine.js +3 -3
- package/dist/core/run/sandboxed-engine.js.map +1 -1
- package/dist/core/universe/builtins/preparation/manifest.json +1 -1
- package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +69 -10
- package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +68 -9
- package/dist/core/verse/adapters/claude.d.ts +49 -2
- package/dist/core/verse/adapters/claude.js +317 -56
- package/dist/core/verse/adapters/claude.js.map +1 -1
- package/dist/core/verse/adapters/codex.d.ts +81 -5
- package/dist/core/verse/adapters/codex.js +409 -47
- package/dist/core/verse/adapters/codex.js.map +1 -1
- package/dist/core/verse/adapters/grok.d.ts +13 -1
- package/dist/core/verse/adapters/grok.js +28 -2
- package/dist/core/verse/adapters/grok.js.map +1 -1
- package/dist/core/verse/adapters/index.d.ts +32 -0
- package/dist/core/verse/adapters/index.js +2 -0
- package/dist/core/verse/adapters/index.js.map +1 -1
- package/dist/core/verse/codex-rollout.d.ts +284 -0
- package/dist/core/verse/codex-rollout.js +786 -0
- package/dist/core/verse/codex-rollout.js.map +1 -0
- package/dist/core/verse/context-fit.d.ts +63 -0
- package/dist/core/verse/context-fit.js +355 -0
- package/dist/core/verse/context-fit.js.map +1 -0
- package/dist/core/verse/context-math.d.ts +215 -0
- package/dist/core/verse/context-math.js +365 -0
- package/dist/core/verse/context-math.js.map +1 -0
- package/dist/core/verse/local-models.d.ts +137 -1
- package/dist/core/verse/local-models.js +209 -5
- package/dist/core/verse/local-models.js.map +1 -1
- package/dist/core/verse/model-windows.d.ts +217 -0
- package/dist/core/verse/model-windows.js +616 -0
- package/dist/core/verse/model-windows.js.map +1 -0
- package/dist/core/verse/preferences.d.ts +123 -0
- package/dist/core/verse/preferences.js +403 -0
- package/dist/core/verse/preferences.js.map +1 -0
- package/dist/core/verse/project-memory.d.ts +112 -0
- package/dist/core/verse/project-memory.js +295 -0
- package/dist/core/verse/project-memory.js.map +1 -0
- package/dist/core/verse/seats.d.ts +106 -4
- package/dist/core/verse/seats.js +291 -89
- package/dist/core/verse/seats.js.map +1 -1
- package/dist/core/verse/session-engine.d.ts +117 -2
- package/dist/core/verse/session-engine.js +763 -65
- package/dist/core/verse/session-engine.js.map +1 -1
- package/dist/core/verse/session-handoff.d.ts +94 -0
- package/dist/core/verse/session-handoff.js +551 -0
- package/dist/core/verse/session-handoff.js.map +1 -0
- package/dist/core/verse/session-search.d.ts +63 -0
- package/dist/core/verse/session-search.js +213 -0
- package/dist/core/verse/session-search.js.map +1 -0
- package/dist/core/verse/session-store.d.ts +16 -2
- package/dist/core/verse/session-store.js +80 -6
- package/dist/core/verse/session-store.js.map +1 -1
- package/dist/core/verse/types.d.ts +333 -1
- package/dist/core/verse/types.js +54 -2
- package/dist/core/verse/types.js.map +1 -1
- package/dist/core/verse/verse-api.d.ts +40 -5
- package/dist/core/verse/verse-api.js +724 -25
- package/dist/core/verse/verse-api.js.map +1 -1
- package/dist/core/web/api.d.ts +14 -4
- package/dist/core/web/api.js +26 -11
- package/dist/core/web/api.js.map +1 -1
- package/dist/core/web/public/next/assets/{App-vtaHB9mp.js → App-XURmiAAp.js} +5 -5
- package/dist/core/web/public/next/assets/{App-vtaHB9mp.js.map → App-XURmiAAp.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ApprovalsSection-B-bFJ0I9.js → ApprovalsSection-BriUzoZc.js} +2 -2
- package/dist/core/web/public/next/assets/{ApprovalsSection-B-bFJ0I9.js.map → ApprovalsSection-BriUzoZc.js.map} +1 -1
- package/dist/core/web/public/next/assets/AutonomySection-C4GBsQVc.js +2 -0
- package/dist/core/web/public/next/assets/{AutonomySection-DoqmsCjw.js.map → AutonomySection-C4GBsQVc.js.map} +1 -1
- package/dist/core/web/public/next/assets/ChatSection-BpOBOUvg.css +1 -0
- package/dist/core/web/public/next/assets/ChatSection-hlgPBRco.js +84 -0
- package/dist/core/web/public/next/assets/ChatSection-hlgPBRco.js.map +1 -0
- package/dist/core/web/public/next/assets/ConfirmDialog-BdQYI-Ng.js +2 -0
- package/dist/core/web/public/next/assets/ConfirmDialog-BdQYI-Ng.js.map +1 -0
- package/dist/core/web/public/next/assets/{Dialog-CPM9JxBc.js → Dialog-BxdIfmFH.js} +2 -2
- package/dist/core/web/public/next/assets/{Dialog-CPM9JxBc.js.map → Dialog-BxdIfmFH.js.map} +1 -1
- package/dist/core/web/public/next/assets/{DiffViewer-B84qVef9.js → DiffViewer-DmeRWuc4.js} +2 -2
- package/dist/core/web/public/next/assets/{DiffViewer-B84qVef9.js.map → DiffViewer-DmeRWuc4.js.map} +1 -1
- package/dist/core/web/public/next/assets/{McpSection-CEUpV_5X.js → McpSection-q34T7Tho.js} +2 -2
- package/dist/core/web/public/next/assets/{McpSection-CEUpV_5X.js.map → McpSection-q34T7Tho.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MutationTokenDialog-DA8t97XT.js → MutationTokenDialog-CFAkwOEt.js} +2 -2
- package/dist/core/web/public/next/assets/{MutationTokenDialog-DA8t97XT.js.map → MutationTokenDialog-CFAkwOEt.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-Bq7Vyud0.js → ResourcePoolConsoleApp-DHc8GFOY.js} +4 -4
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-Bq7Vyud0.js.map → ResourcePoolConsoleApp-DHc8GFOY.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-Badio_uO.js → RouteErrorBoundary-ZL9F4qUn.js} +2 -2
- package/dist/core/web/public/next/assets/{RouteErrorBoundary-Badio_uO.js.map → RouteErrorBoundary-ZL9F4qUn.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Meter-CLcr-4wS.css → Segmented-8cf0Fypn.css} +1 -1
- package/dist/core/web/public/next/assets/{Meter--h3MmZS-.js → Segmented-DbRjVye3.js} +2 -2
- package/dist/core/web/public/next/assets/Segmented-DbRjVye3.js.map +1 -0
- package/dist/core/web/public/next/assets/SettingsSection-nJGGGYqv.js +2 -0
- package/dist/core/web/public/next/assets/{SettingsSection-BwrpQlpP.js.map → SettingsSection-nJGGGYqv.js.map} +1 -1
- package/dist/core/web/public/next/assets/UniverseConsoleApp-6GJydgo4.js +2 -0
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-_FSohAdN.js.map → UniverseConsoleApp-6GJydgo4.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseView-DqcXbed4.js → UniverseView-DLDtNYL8.js} +2 -2
- package/dist/core/web/public/next/assets/{UniverseView-DqcXbed4.js.map → UniverseView-DLDtNYL8.js.map} +1 -1
- package/dist/core/web/public/next/assets/UsageSection-Cf9d9iFH.js +2 -0
- package/dist/core/web/public/next/assets/UsageSection-Cf9d9iFH.js.map +1 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-DsdKQoTH.css +1 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-y0uOHixd.js +3 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-y0uOHixd.js.map +1 -0
- package/dist/core/web/public/next/assets/{charts-CTZJ-JrH.js → charts-CVooqJWN.js} +2 -2
- package/dist/core/web/public/next/assets/{charts-CTZJ-JrH.js.map → charts-CVooqJWN.js.map} +1 -1
- package/dist/core/web/public/next/assets/context-model-Bqv8DVuH.js +2 -0
- package/dist/core/web/public/next/assets/context-model-Bqv8DVuH.js.map +1 -0
- package/dist/core/web/public/next/assets/global-eaY9GyX3.js +2 -0
- package/dist/core/web/public/next/assets/global-eaY9GyX3.js.map +1 -0
- package/dist/core/web/public/next/assets/{icons-D9vlf85u.js → icons-vex8rclC.js} +2 -2
- package/dist/core/web/public/next/assets/{icons-D9vlf85u.js.map → icons-vex8rclC.js.map} +1 -1
- package/dist/core/web/public/next/assets/{index-CZamKRUg.js → index-D2Eik-zt.js} +3 -3
- package/dist/core/web/public/next/assets/{index-CZamKRUg.js.map → index-D2Eik-zt.js.map} +1 -1
- package/dist/core/web/public/next/assets/{queries-B8eBnFox.js → queries-bAYU0VNy.js} +2 -2
- package/dist/core/web/public/next/assets/{queries-B8eBnFox.js.map → queries-bAYU0VNy.js.map} +1 -1
- package/dist/core/web/public/next/assets/use-guarded-action-C7QrP7Yp.js +2 -0
- package/dist/core/web/public/next/assets/{use-guarded-action-bzrVVAYq.js.map → use-guarded-action-C7QrP7Yp.js.map} +1 -1
- package/dist/core/web/public/next/assets/verse-model-TfDk9cky.js +2 -0
- package/dist/core/web/public/next/assets/verse-model-TfDk9cky.js.map +1 -0
- package/dist/core/web/public/next/assets/{workspace-model-v0a1gl-C.js → workspace-model-CHol5RVp.js} +2 -2
- package/dist/core/web/public/next/assets/{workspace-model-v0a1gl-C.js.map → workspace-model-CHol5RVp.js.map} +1 -1
- package/dist/core/web/public/next/index.html +1 -1
- package/dist/release-dependency-inventory.json +1 -1
- package/docs/README.md +1 -0
- package/package.json +1 -1
- package/dist/core/web/public/next/assets/AutonomySection-DoqmsCjw.js +0 -2
- package/dist/core/web/public/next/assets/ChatSection-BfnLe5dT.css +0 -1
- package/dist/core/web/public/next/assets/ChatSection-vNPIJffd.js +0 -80
- package/dist/core/web/public/next/assets/ChatSection-vNPIJffd.js.map +0 -1
- package/dist/core/web/public/next/assets/ConfirmDialog-Dd2APQhB.js +0 -2
- package/dist/core/web/public/next/assets/ConfirmDialog-Dd2APQhB.js.map +0 -1
- package/dist/core/web/public/next/assets/Meter--h3MmZS-.js.map +0 -1
- package/dist/core/web/public/next/assets/SettingsSection-BwrpQlpP.js +0 -2
- package/dist/core/web/public/next/assets/UniverseConsoleApp-_FSohAdN.js +0 -2
- package/dist/core/web/public/next/assets/UsageSection-Bq6Daaby.js +0 -2
- package/dist/core/web/public/next/assets/UsageSection-Bq6Daaby.js.map +0 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-CMkCizvL.css +0 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-CsIQv9id.js +0 -3
- package/dist/core/web/public/next/assets/VerseConsoleApp-CsIQv9id.js.map +0 -1
- package/dist/core/web/public/next/assets/global-D6bTxIRn.js +0 -2
- package/dist/core/web/public/next/assets/global-D6bTxIRn.js.map +0 -1
- package/dist/core/web/public/next/assets/use-guarded-action-bzrVVAYq.js +0 -2
- package/dist/core/web/public/next/assets/verse-model-CvtK6vP6.js +0 -2
- package/dist/core/web/public/next/assets/verse-model-CvtK6vP6.js.map +0 -1
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared project memory — one private directory per project that EVERY seat
|
|
3
|
+
* working on that project reads (and, where the CLI can be granted a writable
|
|
4
|
+
* root, maintains):
|
|
5
|
+
*
|
|
6
|
+
* <verse root>/memory/<basename-slug>-<sha256(realpath)[0:12]>/MEMORY.md
|
|
7
|
+
* (default verse root: ~/.ashlr/verse — dir 0700, files 0600)
|
|
8
|
+
*
|
|
9
|
+
* WHY VERSE OWNS IT rather than each CLI's native memory: every seat runs in an
|
|
10
|
+
* isolated home (`native-profiles/<seat>/native-state`), so Claude's auto
|
|
11
|
+
* memory, Codex's `memories` and Grok's `memory/` are per ACCOUNT, and none of
|
|
12
|
+
* them crosses engines. A plain Markdown file in a directory Verse controls is
|
|
13
|
+
* the one surface all four engines can be pointed at, costs nothing (no
|
|
14
|
+
* background extraction model runs — those spend automatically), and stays
|
|
15
|
+
* OUTSIDE the repository, so nothing an agent writes here lands in a diff.
|
|
16
|
+
* (docs/VERSE-CONTEXT.md, research frontier-native-memory.)
|
|
17
|
+
*
|
|
18
|
+
* HOW A SESSION SEES IT: `prepareProjectMemory` returns `{dir, block,
|
|
19
|
+
* writable}`, which the API snapshots into the private launch record at
|
|
20
|
+
* creation. The adapters send the SAME `block` on every turn (claude/local:
|
|
21
|
+
* `--append-system-prompt`; codex/grok per their adapters) — byte-identical, so
|
|
22
|
+
* the provider's prompt cache survives turn after turn. The block carries the
|
|
23
|
+
* MEMORY.md of the moment the chat began; agents that can reach `dir` read the
|
|
24
|
+
* live file for anything newer.
|
|
25
|
+
*
|
|
26
|
+
* SAFETY
|
|
27
|
+
* - The directory key is a hash of the canonical path, so two projects with
|
|
28
|
+
* the same basename never share memory and a path cannot traverse out.
|
|
29
|
+
* - MEMORY.md is read without following symlinks: an agent that replaced it
|
|
30
|
+
* with a link to ~/.ssh/id_ed25519 would otherwise have that file echoed to
|
|
31
|
+
* the UI and into every future system prompt.
|
|
32
|
+
* - The block is `scrubSecrets`'d and capped at VERSE_MEMORY_BLOCK_MAX_BYTES.
|
|
33
|
+
* The instructions tell agents never to store secrets; the scrub is the
|
|
34
|
+
* backstop, not the policy.
|
|
35
|
+
* - The block is ARGV-SAFE: C0/C1 control characters (NUL above all) are
|
|
36
|
+
* stripped. The block rides on argv (`--append-system-prompt=…`,
|
|
37
|
+
* `--rules=…`) and Node's spawn throws ERR_INVALID_ARG_VALUE for any argv
|
|
38
|
+
* element containing NUL. Agents write MEMORY.md directly (not through
|
|
39
|
+
* `writeProjectMemory`'s NUL check), and the block is pinned into the
|
|
40
|
+
* launch record — so one stray NUL would otherwise make every new chat in
|
|
41
|
+
* the project unable to start, permanently.
|
|
42
|
+
*
|
|
43
|
+
* Spends nothing and starts no process.
|
|
44
|
+
*/
|
|
45
|
+
import { createHash } from 'node:crypto';
|
|
46
|
+
import { lstatSync, readdirSync } from 'node:fs';
|
|
47
|
+
import { basename, join } from 'node:path';
|
|
48
|
+
import { scrubSecrets } from '../util/scrub.js';
|
|
49
|
+
import { canonicalProjectPath, defaultVerseRoot, ensurePrivateDirectory, loadVersePreferences, memoryEnabledFor, readPrivateFileCapped, utf8Prefix, VerseServiceError, writePrivateFileAtomic, } from './preferences.js';
|
|
50
|
+
import { VERSE_MEMORY_BLOCK_MAX_BYTES, VERSE_MEMORY_MAX_BYTES } from './types.js';
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Constants
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
export const VERSE_MEMORY_DIRNAME = 'memory';
|
|
55
|
+
export const VERSE_MEMORY_FILE = 'MEMORY.md';
|
|
56
|
+
/** Hard cap on the system-prompt block, instructions included. */
|
|
57
|
+
export { VERSE_MEMORY_BLOCK_MAX_BYTES }; // defined in types.ts so the browser mirrors it exactly
|
|
58
|
+
/** Longest MEMORY.md the UI is shown; agents can outgrow the write cap, and a view must not truncate silently below it. */
|
|
59
|
+
const MEMORY_READ_CAP_BYTES = 4 * VERSE_MEMORY_MAX_BYTES;
|
|
60
|
+
/** Names listed from the memory directory. */
|
|
61
|
+
const MAX_LISTED_FILES = 200;
|
|
62
|
+
const SLUG_MAX_CHARS = 40;
|
|
63
|
+
/** Marker appended where the snapshot in the block was cut. */
|
|
64
|
+
export const VERSE_MEMORY_TRUNCATION_MARKER = `[… ${VERSE_MEMORY_FILE} continues — read the file for the rest]`;
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Locating a project's memory
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
function slugOf(name) {
|
|
69
|
+
const slug = name
|
|
70
|
+
.normalize('NFKD')
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
73
|
+
.replace(/^-+|-+$/g, '')
|
|
74
|
+
.slice(0, SLUG_MAX_CHARS)
|
|
75
|
+
.replace(/-+$/g, '');
|
|
76
|
+
return slug.length > 0 ? slug : 'project';
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The memory directory for a project. Pure path arithmetic (no I/O beyond the
|
|
80
|
+
* realpath canonicalisation); does not create anything.
|
|
81
|
+
*
|
|
82
|
+
* @param root the Verse store root (default `~/.ashlr/verse`), the same root
|
|
83
|
+
* the session engine and preferences use.
|
|
84
|
+
*/
|
|
85
|
+
export function projectMemoryDir(projectPath, root = defaultVerseRoot()) {
|
|
86
|
+
const canonical = canonicalProjectPath(projectPath);
|
|
87
|
+
const hash = createHash('sha256').update(canonical).digest('hex').slice(0, 12);
|
|
88
|
+
return join(root, VERSE_MEMORY_DIRNAME, `${slugOf(basename(canonical))}-${hash}`);
|
|
89
|
+
}
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// The system-prompt block
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
/**
|
|
94
|
+
* Every C0 control except TAB (\x09) and LF (\x0A), plus DEL and the C1 range.
|
|
95
|
+
* CR is included: callers normalise CRLF to LF first, and a lone CR left in a
|
|
96
|
+
* system prompt only garbles how the text renders.
|
|
97
|
+
*/
|
|
98
|
+
// eslint-disable-next-line no-control-regex
|
|
99
|
+
const UNSAFE_CONTROL_CHARS = /[\u0000-\u0008\u000B-\u001F\u007F-\u009F]/g;
|
|
100
|
+
/**
|
|
101
|
+
* Text made safe to pass as (part of) an argv element and to show as prose:
|
|
102
|
+
* CRLF → LF, then every control character except `\n` and `\t` removed.
|
|
103
|
+
* Exported so the engine can repair a snapshot pinned before this existed
|
|
104
|
+
* (a launch record carrying a NUL must be healed, not rejected — rejecting it
|
|
105
|
+
* would brick the session it belongs to).
|
|
106
|
+
*/
|
|
107
|
+
export function stripUnsafeControlChars(text) {
|
|
108
|
+
return text.replace(/\r\n?/g, '\n').replace(UNSAFE_CONTROL_CHARS, '');
|
|
109
|
+
}
|
|
110
|
+
function byteLength(text) {
|
|
111
|
+
return Buffer.byteLength(text, 'utf8');
|
|
112
|
+
}
|
|
113
|
+
/** Cut `text` to at most `maxBytes`, preferring a line boundary, never splitting a character. */
|
|
114
|
+
function cutToBytes(text, maxBytes) {
|
|
115
|
+
if (byteLength(text) <= maxBytes)
|
|
116
|
+
return text;
|
|
117
|
+
const prefix = utf8Prefix(Buffer.from(text, 'utf8').subarray(0, Math.max(0, maxBytes)));
|
|
118
|
+
const lastNewline = prefix.lastIndexOf('\n');
|
|
119
|
+
// Only back up to a line boundary when that keeps most of the budget.
|
|
120
|
+
return lastNewline >= prefix.length * 0.6 ? prefix.slice(0, lastNewline) : prefix;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The fixed instructions. SHORT and DIRECTIVE on purpose: it rides on every
|
|
124
|
+
* turn's system prompt, so each sentence is paid for again and again (cheaply,
|
|
125
|
+
* from cache — which is why it must never vary between turns of one chat).
|
|
126
|
+
*/
|
|
127
|
+
function memoryInstructions(dir, projectName, writable) {
|
|
128
|
+
const file = join(dir, VERSE_MEMORY_FILE);
|
|
129
|
+
const lines = [
|
|
130
|
+
'# Shared project memory (Ashlr Verse)',
|
|
131
|
+
'',
|
|
132
|
+
`Every AI seat that works on "${projectName}" shares one memory directory, kept outside the repository:`,
|
|
133
|
+
` ${dir}`,
|
|
134
|
+
`Its index is ${file}.`,
|
|
135
|
+
'',
|
|
136
|
+
];
|
|
137
|
+
if (writable) {
|
|
138
|
+
lines.push(`- Before substantial work, read ${VERSE_MEMORY_FILE}: the file on disk may be newer than the snapshot below.`, `- Keep ${VERSE_MEMORY_FILE} a concise index (at most 200 lines) of DURABLE facts, each with its reason: decisions and why they were made, conventions, gotchas, and the status of the current plan. Put detail in other files in the same directory and link them from the index.`, '- Update it at milestones (a decision made, a plan step finished, a gotcha found), not after every edit. Edit in place and delete facts that are no longer true.', '- Never store secrets, credentials, tokens or personal data there.');
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
lines.push('- This seat may only READ the memory: rely on the snapshot below (and the directory, if your tools can reach it). Do not try to write there.', '- If you learn something durable — a decision and its reason, a convention, a gotcha, plan status — say so in your reply so it can be recorded.', '- Never repeat secrets, credentials, tokens or personal data from it.');
|
|
142
|
+
}
|
|
143
|
+
lines.push('', `## ${VERSE_MEMORY_FILE} when this chat began`, '');
|
|
144
|
+
return lines.join('\n');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Deterministic: the same (dir, content, writable) always yields the same
|
|
148
|
+
* bytes, so a block snapshotted into a launch record keeps the prompt prefix
|
|
149
|
+
* cache-stable for the whole conversation.
|
|
150
|
+
*/
|
|
151
|
+
export function renderMemoryBlock(input) {
|
|
152
|
+
// The project name is a directory basename: legal on disk with control
|
|
153
|
+
// bytes in it, never legal on argv with a NUL. `dir` is a real path (no NUL
|
|
154
|
+
// possible) and must stay byte-exact, so it is left alone.
|
|
155
|
+
const head = memoryInstructions(input.dir, stripUnsafeControlChars(input.projectName), input.writable);
|
|
156
|
+
// Strip BEFORE scrubbing and trimming, so a control byte can neither split a
|
|
157
|
+
// secret the scrubber would have matched nor survive at an edge.
|
|
158
|
+
const body = scrubSecrets(stripUnsafeControlChars(input.content)).trim();
|
|
159
|
+
if (body.length === 0) {
|
|
160
|
+
return cutToBytes(`${head}(empty — nothing recorded yet)`, VERSE_MEMORY_BLOCK_MAX_BYTES);
|
|
161
|
+
}
|
|
162
|
+
const budget = VERSE_MEMORY_BLOCK_MAX_BYTES - byteLength(head);
|
|
163
|
+
if (byteLength(body) <= budget)
|
|
164
|
+
return `${head}${body}`;
|
|
165
|
+
const room = budget - byteLength(`\n${VERSE_MEMORY_TRUNCATION_MARKER}`);
|
|
166
|
+
if (room <= 0)
|
|
167
|
+
return cutToBytes(head, VERSE_MEMORY_BLOCK_MAX_BYTES);
|
|
168
|
+
return `${head}${cutToBytes(body, room).trimEnd()}\n${VERSE_MEMORY_TRUNCATION_MARKER}`;
|
|
169
|
+
}
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// Public API
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
function ensureMemoryDir(dir, root) {
|
|
174
|
+
ensurePrivateDirectory(root);
|
|
175
|
+
ensurePrivateDirectory(join(root, VERSE_MEMORY_DIRNAME));
|
|
176
|
+
ensurePrivateDirectory(dir);
|
|
177
|
+
}
|
|
178
|
+
/** MEMORY.md, safely: regular file only, never through a symlink. */
|
|
179
|
+
function readMemoryFile(dir) {
|
|
180
|
+
const file = readPrivateFileCapped(join(dir, VERSE_MEMORY_FILE), MEMORY_READ_CAP_BYTES);
|
|
181
|
+
return file ? { text: file.text, bytes: file.bytes, mtimeMs: file.mtimeMs } : null;
|
|
182
|
+
}
|
|
183
|
+
/** Everything else the agents keep beside MEMORY.md (names only; dirs end in '/'). */
|
|
184
|
+
function listOtherFiles(dir) {
|
|
185
|
+
let names;
|
|
186
|
+
try {
|
|
187
|
+
names = readdirSync(dir);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
const out = [];
|
|
193
|
+
for (const name of names.sort()) {
|
|
194
|
+
if (out.length >= MAX_LISTED_FILES)
|
|
195
|
+
break;
|
|
196
|
+
if (name === VERSE_MEMORY_FILE)
|
|
197
|
+
continue;
|
|
198
|
+
// Our own atomic-write temporaries (and any other dot-temp) are not content.
|
|
199
|
+
if (name.startsWith('.') && name.endsWith('.tmp'))
|
|
200
|
+
continue;
|
|
201
|
+
try {
|
|
202
|
+
const stat = lstatSync(join(dir, name));
|
|
203
|
+
if (stat.isDirectory())
|
|
204
|
+
out.push(`${name}/`);
|
|
205
|
+
else if (stat.isFile())
|
|
206
|
+
out.push(name);
|
|
207
|
+
// Symlinks, sockets, fifos are not listed: nothing here should be one.
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Vanished between readdir and lstat.
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return out;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Create the project's memory directory (0700) and render the block a new
|
|
217
|
+
* session will carry. Called once, at session creation; the result is pinned
|
|
218
|
+
* in the launch record (`VerseSeatLaunch.memory`).
|
|
219
|
+
*
|
|
220
|
+
* `writable` is the SEAT's capability, decided by the caller from the engine:
|
|
221
|
+
* claude/local (`--add-dir`) and codex (writable root) can write; grok, which
|
|
222
|
+
* reaches only its `--cwd`, cannot.
|
|
223
|
+
*/
|
|
224
|
+
export function prepareProjectMemory(projectPath, opts) {
|
|
225
|
+
const root = opts.root ?? defaultVerseRoot();
|
|
226
|
+
const canonical = canonicalProjectPath(projectPath);
|
|
227
|
+
const dir = projectMemoryDir(canonical, root);
|
|
228
|
+
ensureMemoryDir(dir, root);
|
|
229
|
+
const current = readMemoryFile(dir);
|
|
230
|
+
const block = renderMemoryBlock({
|
|
231
|
+
dir,
|
|
232
|
+
projectName: basename(canonical) || canonical,
|
|
233
|
+
content: current?.text ?? '',
|
|
234
|
+
writable: opts.writable,
|
|
235
|
+
});
|
|
236
|
+
return { dir, block, writable: opts.writable };
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* The project's memory as the UI shows it. Never creates anything; a project
|
|
240
|
+
* with no memory yet reads as empty with `updatedAt: null`.
|
|
241
|
+
*
|
|
242
|
+
* `enabled` is passed in (the API resolves it from preferences) so this stays
|
|
243
|
+
* a pure read of the directory.
|
|
244
|
+
*/
|
|
245
|
+
export function readProjectMemory(projectPath, enabled, root = defaultVerseRoot()) {
|
|
246
|
+
const canonical = canonicalProjectPath(projectPath);
|
|
247
|
+
const dir = projectMemoryDir(canonical, root);
|
|
248
|
+
let dirOk = false;
|
|
249
|
+
try {
|
|
250
|
+
const stat = lstatSync(dir);
|
|
251
|
+
dirOk = stat.isDirectory() && !stat.isSymbolicLink();
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
dirOk = false;
|
|
255
|
+
}
|
|
256
|
+
if (!dirOk) {
|
|
257
|
+
return { projectPath: canonical, enabled, content: '', bytes: 0, updatedAt: null, files: [] };
|
|
258
|
+
}
|
|
259
|
+
const file = readMemoryFile(dir);
|
|
260
|
+
return {
|
|
261
|
+
projectPath: canonical,
|
|
262
|
+
enabled,
|
|
263
|
+
content: file?.text ?? '',
|
|
264
|
+
bytes: file?.bytes ?? 0,
|
|
265
|
+
updatedAt: file ? new Date(file.mtimeMs).toISOString() : null,
|
|
266
|
+
files: listOtherFiles(dir),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Replace MEMORY.md with the operator's text (atomic, 0600). An empty string
|
|
271
|
+
* clears it. Rejects content over VERSE_MEMORY_MAX_BYTES (VERSE_TOO_LARGE)
|
|
272
|
+
* rather than truncating what the operator wrote, and NUL bytes (VERSE_INVALID).
|
|
273
|
+
*
|
|
274
|
+
* Allowed whether or not memory is enabled for the project — it is the
|
|
275
|
+
* operator's own file — and `enabled` in the result reports the preference.
|
|
276
|
+
*/
|
|
277
|
+
export function writeProjectMemory(projectPath, content, root = defaultVerseRoot()) {
|
|
278
|
+
if (typeof content !== 'string') {
|
|
279
|
+
throw new VerseServiceError('VERSE_INVALID', 'content must be a string');
|
|
280
|
+
}
|
|
281
|
+
if (content.includes('\0')) {
|
|
282
|
+
throw new VerseServiceError('VERSE_INVALID', 'content must not contain NUL bytes');
|
|
283
|
+
}
|
|
284
|
+
const bytes = byteLength(content);
|
|
285
|
+
if (bytes > VERSE_MEMORY_MAX_BYTES) {
|
|
286
|
+
throw new VerseServiceError('VERSE_TOO_LARGE', `memory is ${bytes} bytes; the limit is ${VERSE_MEMORY_MAX_BYTES}`);
|
|
287
|
+
}
|
|
288
|
+
const canonical = canonicalProjectPath(projectPath);
|
|
289
|
+
const dir = projectMemoryDir(canonical, root);
|
|
290
|
+
ensureMemoryDir(dir, root);
|
|
291
|
+
writePrivateFileAtomic(join(dir, VERSE_MEMORY_FILE), content);
|
|
292
|
+
const enabled = memoryEnabledFor(loadVersePreferences(root), canonical);
|
|
293
|
+
return readProjectMemory(canonical, enabled, root);
|
|
294
|
+
}
|
|
295
|
+
//# sourceMappingURL=project-memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-memory.js","sourceRoot":"","sources":["../../../src/core/verse/project-memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,9 +30,23 @@ import type { AshlrConfig } from '../types.js';
|
|
|
30
30
|
import { type ClaudeUsageResult } from '../fabric/claude-usage.js';
|
|
31
31
|
import type { VerseSeatLaunch } from './session-engine.js';
|
|
32
32
|
import { type VerseAccountCollector, type VerseAccountRecord } from './accounts.js';
|
|
33
|
-
import { type VerseOllamaModelDetail } from './local-models.js';
|
|
33
|
+
import { contextWindowFromTagSuffix, type VerseLocalWindow, type VerseOllamaModelDetail, type VerseOllamaServerDefault } from './local-models.js';
|
|
34
34
|
import { type VerseBootstrap, type VerseEngine, type VerseModelOption, type VerseSeat, type VerseLocalDispatch, type VerseSeatCapacity, type VerseSeatHealth, type VerseSeatUsability } from './types.js';
|
|
35
35
|
type NativeEngine = Exclude<VerseEngine, 'local'>;
|
|
36
|
+
/**
|
|
37
|
+
* The BUILT-IN lists: what a native seat offers when nothing better is known
|
|
38
|
+
* (a Claude seat whose CLI version could not be read, a codex/grok seat that
|
|
39
|
+
* has not fetched its own catalog yet). Real seats are built per account by
|
|
40
|
+
* {@link nativeSeatModels} from the seat's own catalog and pinned binary —
|
|
41
|
+
* see core/verse/model-windows.ts, which holds every window and the evidence
|
|
42
|
+
* for it. Exported for tests and for callers that need the documented
|
|
43
|
+
* defaults without a discovery pass.
|
|
44
|
+
*
|
|
45
|
+
* Never hard-code a model list here again. Two past failures this replaced:
|
|
46
|
+
* Claude models all inherited the CLI's 200k "unknown model" window while the
|
|
47
|
+
* CLI ran six of them at 1M, and `claude-opus-5.5` (dotted) was offered even
|
|
48
|
+
* though every Claude Code binary resolves that spelling to Opus 5.
|
|
49
|
+
*/
|
|
36
50
|
export declare const VERSE_NATIVE_MODELS: Readonly<Record<NativeEngine, readonly VerseModelOption[]>>;
|
|
37
51
|
/**
|
|
38
52
|
* LEGACY FALLBACK ONLY. Local seat visibility is now decided by the model's
|
|
@@ -65,6 +79,23 @@ export interface VerseSeatDiscoveryOptions {
|
|
|
65
79
|
* Tests pass it explicitly; the server never does.
|
|
66
80
|
*/
|
|
67
81
|
collector?: VerseAccountCollector | null;
|
|
82
|
+
/**
|
|
83
|
+
* Where Claude Code keeps one file per installed version (default
|
|
84
|
+
* `~/.local/share/claude/versions`). Read only to tell an operator that a
|
|
85
|
+
* newer binary than their seat's pin is already installed.
|
|
86
|
+
*/
|
|
87
|
+
claudeVersionsRoot?: string;
|
|
88
|
+
/**
|
|
89
|
+
* llama-server's origin for the per-slot window probe on the llama-server
|
|
90
|
+
* lane. Default: `resolveLlamaServerOrigin(cfg)`. Tests pass it so a real
|
|
91
|
+
* llama-server on this machine can never leak into a hermetic run.
|
|
92
|
+
*/
|
|
93
|
+
llamaServerOrigin?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Ollama's unpinned-request default context. `undefined` reads it from the
|
|
96
|
+
* environment / server log ({@link readOllamaServerDefault}).
|
|
97
|
+
*/
|
|
98
|
+
ollamaServerDefault?: VerseOllamaServerDefault | null;
|
|
68
99
|
}
|
|
69
100
|
export interface VerseSeatDiscovery {
|
|
70
101
|
/** Public, JSON-safe seats. */
|
|
@@ -99,6 +130,56 @@ export declare function resolveOllamaBaseUrl(cfg: AshlrConfig, explicit?: string
|
|
|
99
130
|
* everywhere lands back on the working default.
|
|
100
131
|
*/
|
|
101
132
|
export declare function resolveVerseLocalDispatch(cfg: AshlrConfig, explicit?: VerseLocalDispatch): VerseLocalDispatch;
|
|
133
|
+
/**
|
|
134
|
+
* What a native seat's profile says about the CLI it is pinned to. PRIVATE:
|
|
135
|
+
* `nativeStatePath` and `executable` are machine paths and never reach a seat
|
|
136
|
+
* as-is; only the derived version, catalog-derived models and home-relative
|
|
137
|
+
* remediation text do.
|
|
138
|
+
*/
|
|
139
|
+
export interface VerseSeatProfile {
|
|
140
|
+
/** The profile directory (`<…>/native-profiles/<account>`). */
|
|
141
|
+
directory: string;
|
|
142
|
+
/** The provider CLI's pinned home: CODEX_HOME / GROK_HOME / CLAUDE_CONFIG_DIR. */
|
|
143
|
+
nativeStatePath: string | null;
|
|
144
|
+
/** The one binary the launcher execs. */
|
|
145
|
+
executable: string | null;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Locate and read `<profile>/profile.json` from an account's launcher argv —
|
|
149
|
+
* the same derivation `mcp-seat-view.resolveAccountStateRoots` uses
|
|
150
|
+
* (`…/launcher.mjs` ⇒ its directory's `profile.json`), but lifting the
|
|
151
|
+
* executable too, which the seat needs to know which CLI version it runs.
|
|
152
|
+
*
|
|
153
|
+
* A profile whose `provider` disagrees with the account is IGNORED rather
|
|
154
|
+
* than half-trusted: reading a grok catalog for a codex seat would be worse
|
|
155
|
+
* than reading none. Returns null whenever anything is missing.
|
|
156
|
+
*/
|
|
157
|
+
export declare function readSeatProfile(command: readonly string[], provider: NativeEngine): VerseSeatProfile | null;
|
|
158
|
+
/**
|
|
159
|
+
* The operator command that points a prepared native profile at another CLI
|
|
160
|
+
* binary (src/cli/resources.ts routes `resources` → `profile` → `repin`). It is
|
|
161
|
+
* a constant because the seat note is copy-paste remediation: a spelling that
|
|
162
|
+
* does not route (`ashlr resource-profile …` never existed) is worse than no
|
|
163
|
+
* hint, and tests pin the exact text through this one name.
|
|
164
|
+
*/
|
|
165
|
+
export declare const VERSE_REPIN_COMMAND = "ashlr resources profile repin";
|
|
166
|
+
export declare const VERSE_CATALOG_PENDING_NOTE = "Model list is Verse's built-in list until this seat's first turn fetches its own catalog.";
|
|
167
|
+
/**
|
|
168
|
+
* One native seat's models, pinned CLI version and notes.
|
|
169
|
+
*
|
|
170
|
+
* Every source is the SEAT's: its own catalog file under its profile's
|
|
171
|
+
* `nativeStatePath` and the binary its launcher execs. The unpinned global
|
|
172
|
+
* homes (`~/.codex`, `~/.grok`, `~/.claude`) belong to a different account or
|
|
173
|
+
* binary and are never read — codex-a and codex-b run different CLI versions
|
|
174
|
+
* with different catalogs on this very machine.
|
|
175
|
+
*/
|
|
176
|
+
export declare function nativeSeatModels(provider: NativeEngine, profile: VerseSeatProfile | null, opts?: {
|
|
177
|
+
claudeVersionsRoot?: string;
|
|
178
|
+
}): {
|
|
179
|
+
models: VerseModelOption[];
|
|
180
|
+
cliVersion: string | null;
|
|
181
|
+
notes: string[];
|
|
182
|
+
};
|
|
102
183
|
/**
|
|
103
184
|
* THE V2.1 BUG THIS SECTION EXISTS TO FIX.
|
|
104
185
|
*
|
|
@@ -204,10 +285,32 @@ export declare function preferredLocalTags(cfg: AshlrConfig): string[];
|
|
|
204
285
|
* without tool support stays hidden no matter how preferred its tag is.
|
|
205
286
|
*/
|
|
206
287
|
export declare function localSeatPreferenceRank(tag: string, preferred: readonly string[]): number;
|
|
207
|
-
/**
|
|
208
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Re-exported: the tag-suffix parser now lives beside the one local window
|
|
290
|
+
* resolver in local-models.ts (and accepts `-ctx64k`, which it used to miss).
|
|
291
|
+
*/
|
|
292
|
+
export { contextWindowFromTagSuffix };
|
|
209
293
|
/** `qwen3-coder-next:ctx64k` → "Qwen3-Coder-Next (local)". */
|
|
210
294
|
export declare function localSeatLabel(tag: string): string;
|
|
295
|
+
/**
|
|
296
|
+
* Plain-language caveats for one local seat's window, or none. Only the cases
|
|
297
|
+
* where the number on screen is weaker than it looks get a note: a figure
|
|
298
|
+
* Ollama actually allocates (pinned num_ctx, resident instance, its own
|
|
299
|
+
* default, llama-server's slot) needs no apology.
|
|
300
|
+
*/
|
|
301
|
+
export declare function localWindowNotes(resolved: VerseLocalWindow, lane: VerseLocalDispatch, slotReported: boolean): string[];
|
|
302
|
+
/**
|
|
303
|
+
* Why a local window cannot host a Claude Code session, or null when it can.
|
|
304
|
+
*
|
|
305
|
+
* Local turns run Claude Code with `CLAUDE_CODE_MAX_CONTEXT_TOKENS=<window>`
|
|
306
|
+
* (the window must be stated: the CLI otherwise assumes 200k and overflows
|
|
307
|
+
* the runner). The CLI then reserves up to 20k for the reply and compacts
|
|
308
|
+
* 13k below that, so a 32,768 window compacts at 0 — below its own ~15k fixed
|
|
309
|
+
* prompt — and every turn is blocked or loops through compaction. Such a seat
|
|
310
|
+
* is LISTED with this reason (the operator learns why, and which tag would
|
|
311
|
+
* work) rather than offered as runnable or silently hidden.
|
|
312
|
+
*/
|
|
313
|
+
export declare function localWindowUnusableReason(window: number): string | null;
|
|
211
314
|
/**
|
|
212
315
|
* Discover every seat available to this server. Never throws; native seats
|
|
213
316
|
* and local seats degrade independently.
|
|
@@ -229,4 +332,3 @@ export declare function discoverSeats(cfg: AshlrConfig, opts?: VerseSeatDiscover
|
|
|
229
332
|
* Ollama tag has no subscription window.
|
|
230
333
|
*/
|
|
231
334
|
export declare function refreshSeatTelemetry(cfg: AshlrConfig, discovery: VerseSeatDiscovery, opts?: VerseSeatDiscoveryOptions): VerseSeatDiscovery;
|
|
232
|
-
export {};
|