vigthoria-cli 1.13.18 → 1.13.22

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.
Files changed (185) hide show
  1. package/completions/_vigthoria +49 -0
  2. package/completions/vigthoria.bash +6 -0
  3. package/completions/vigthoria.fish +41 -0
  4. package/dist/commands/auth.d.ts +1 -0
  5. package/dist/commands/auth.js +84 -135
  6. package/dist/commands/background.d.ts +0 -2
  7. package/dist/commands/background.js +26 -34
  8. package/dist/commands/bridge.js +45 -41
  9. package/dist/commands/cancel.d.ts +1 -0
  10. package/dist/commands/cancel.js +22 -16
  11. package/dist/commands/chat.d.ts +5 -42
  12. package/dist/commands/chat.js +462 -1122
  13. package/dist/commands/coding-registration.d.ts +4 -0
  14. package/dist/commands/coding-registration.js +24 -0
  15. package/dist/commands/config.js +32 -42
  16. package/dist/commands/conversation-registration.d.ts +4 -0
  17. package/dist/commands/conversation-registration.js +122 -0
  18. package/dist/commands/deploy.d.ts +8 -2
  19. package/dist/commands/deploy.js +92 -90
  20. package/dist/commands/device.d.ts +1 -2
  21. package/dist/commands/device.js +37 -21
  22. package/dist/commands/doctor-registration.d.ts +4 -0
  23. package/dist/commands/doctor-registration.js +200 -0
  24. package/dist/commands/edit.js +22 -33
  25. package/dist/commands/explain.js +7 -10
  26. package/dist/commands/fork.d.ts +1 -2
  27. package/dist/commands/fork.js +72 -22
  28. package/dist/commands/game.d.ts +1 -1
  29. package/dist/commands/game.js +17 -6
  30. package/dist/commands/generate.js +7 -9
  31. package/dist/commands/history.js +11 -7
  32. package/dist/commands/hub.d.ts +2 -2
  33. package/dist/commands/hub.js +34 -35
  34. package/dist/commands/legion.d.ts +0 -1
  35. package/dist/commands/legion.js +62 -79
  36. package/dist/commands/music.d.ts +1 -2
  37. package/dist/commands/music.js +62 -27
  38. package/dist/commands/platform-registration.d.ts +4 -0
  39. package/dist/commands/platform-registration.js +45 -0
  40. package/dist/commands/preview.d.ts +0 -1
  41. package/dist/commands/preview.js +82 -35
  42. package/dist/commands/product-run-registration.d.ts +4 -0
  43. package/dist/commands/product-run-registration.js +490 -0
  44. package/dist/commands/replay.d.ts +1 -2
  45. package/dist/commands/replay.js +15 -11
  46. package/dist/commands/repo.d.ts +14 -7
  47. package/dist/commands/repo.js +245 -196
  48. package/dist/commands/review.js +9 -12
  49. package/dist/commands/security.d.ts +1 -2
  50. package/dist/commands/security.js +12 -6
  51. package/dist/commands/update-registration.d.ts +3 -0
  52. package/dist/commands/update-registration.js +338 -0
  53. package/dist/commands/v4-registration.d.ts +4 -0
  54. package/dist/commands/v4-registration.js +24 -0
  55. package/dist/commands/v4.d.ts +1 -1
  56. package/dist/commands/v4.js +46 -8
  57. package/dist/commands/wallet.d.ts +1 -3
  58. package/dist/commands/wallet.js +18 -45
  59. package/dist/commands/workflow.d.ts +2 -0
  60. package/dist/commands/workflow.js +52 -39
  61. package/dist/index.js +122 -1498
  62. package/dist/utils/agent-stream-state.d.ts +27 -0
  63. package/dist/utils/agent-stream-state.js +93 -0
  64. package/dist/utils/agentRoute.d.ts +2 -1
  65. package/dist/utils/agentRoute.js +22 -9
  66. package/dist/utils/agentRunOutcome.d.ts +16 -0
  67. package/dist/utils/agentRunOutcome.js +37 -2
  68. package/dist/utils/api-client-factory.d.ts +8 -0
  69. package/dist/utils/api-client-factory.js +8 -0
  70. package/dist/utils/api.d.ts +45 -269
  71. package/dist/utils/api.js +664 -3210
  72. package/dist/utils/auth-session.d.ts +41 -0
  73. package/dist/utils/auth-session.js +224 -0
  74. package/dist/utils/brain-hub-client.d.ts +0 -4
  75. package/dist/utils/brain-hub-client.js +6 -3
  76. package/dist/utils/bridge-client.js +2 -0
  77. package/dist/utils/capability-health-service.d.ts +48 -0
  78. package/dist/utils/capability-health-service.js +64 -0
  79. package/dist/utils/chat-prompt-policy.d.ts +18 -0
  80. package/dist/utils/chat-prompt-policy.js +61 -0
  81. package/dist/utils/chat-tool-call-parser.d.ts +11 -0
  82. package/dist/utils/chat-tool-call-parser.js +71 -0
  83. package/dist/utils/cli-state.d.ts +22 -4
  84. package/dist/utils/cli-state.js +105 -47
  85. package/dist/utils/code-operations-service.d.ts +104 -0
  86. package/dist/utils/code-operations-service.js +837 -0
  87. package/dist/utils/codebase-indexer.d.ts +1 -0
  88. package/dist/utils/codebase-indexer.js +28 -8
  89. package/dist/utils/command-contract.d.ts +61 -0
  90. package/dist/utils/command-contract.js +149 -0
  91. package/dist/utils/command-policy.d.ts +10 -0
  92. package/dist/utils/command-policy.js +54 -0
  93. package/dist/utils/config.d.ts +13 -5
  94. package/dist/utils/config.js +227 -99
  95. package/dist/utils/context-ranker.js +11 -2
  96. package/dist/utils/context-state.d.ts +39 -0
  97. package/dist/utils/context-state.js +52 -0
  98. package/dist/utils/deckEvents.d.ts +0 -5
  99. package/dist/utils/deckEvents.js +3 -2
  100. package/dist/utils/desktop-bridge-client.js +2 -0
  101. package/dist/utils/direct-output-policy.d.ts +3 -0
  102. package/dist/utils/direct-output-policy.js +76 -0
  103. package/dist/utils/durable-json.d.ts +57 -0
  104. package/dist/utils/durable-json.js +319 -0
  105. package/dist/utils/fastAgentRouter.d.ts +6 -7
  106. package/dist/utils/fastAgentRouter.js +21 -30
  107. package/dist/utils/files.js +21 -13
  108. package/dist/utils/frontend-preview-service.d.ts +76 -0
  109. package/dist/utils/frontend-preview-service.js +482 -0
  110. package/dist/utils/goaEvents.d.ts +0 -4
  111. package/dist/utils/goaEvents.js +5 -2
  112. package/dist/utils/logger.d.ts +2 -1
  113. package/dist/utils/logger.js +27 -20
  114. package/dist/utils/mcp-context-client.d.ts +24 -0
  115. package/dist/utils/mcp-context-client.js +58 -0
  116. package/dist/utils/model-governance.d.ts +18 -0
  117. package/dist/utils/model-governance.js +75 -0
  118. package/dist/utils/model-transport-service.d.ts +44 -0
  119. package/dist/utils/model-transport-service.js +111 -0
  120. package/dist/utils/mutation-journal.d.ts +42 -0
  121. package/dist/utils/mutation-journal.js +387 -0
  122. package/dist/utils/network-policy.d.ts +24 -0
  123. package/dist/utils/network-policy.js +202 -0
  124. package/dist/utils/operator-client.d.ts +45 -0
  125. package/dist/utils/operator-client.js +159 -0
  126. package/dist/utils/post-write-validator.js +7 -9
  127. package/dist/utils/preview-screenshot-adapter.d.ts +13 -0
  128. package/dist/utils/preview-screenshot-adapter.js +36 -0
  129. package/dist/utils/process-policy.d.ts +23 -0
  130. package/dist/utils/process-policy.js +284 -0
  131. package/dist/utils/project-memory.d.ts +13 -3
  132. package/dist/utils/project-memory.js +70 -17
  133. package/dist/utils/prompt-workspace-resolver.d.ts +14 -0
  134. package/dist/utils/prompt-workspace-resolver.js +64 -0
  135. package/dist/utils/release-install.d.ts +11 -0
  136. package/dist/utils/release-install.js +155 -0
  137. package/dist/utils/release-policy.d.ts +64 -0
  138. package/dist/utils/release-policy.js +293 -0
  139. package/dist/utils/requestIntent.js +22 -3
  140. package/dist/utils/runtime-capability.d.ts +8 -0
  141. package/dist/utils/runtime-capability.js +16 -0
  142. package/dist/utils/secret-policy.d.ts +20 -0
  143. package/dist/utils/secret-policy.js +160 -0
  144. package/dist/utils/session.d.ts +34 -2
  145. package/dist/utils/session.js +354 -110
  146. package/dist/utils/tool-approval-service.d.ts +17 -0
  147. package/dist/utils/tool-approval-service.js +78 -0
  148. package/dist/utils/tool-capability-providers.d.ts +57 -0
  149. package/dist/utils/tool-capability-providers.js +57 -0
  150. package/dist/utils/tools.d.ts +2 -23
  151. package/dist/utils/tools.js +216 -508
  152. package/dist/utils/update-policy.d.ts +18 -0
  153. package/dist/utils/update-policy.js +137 -0
  154. package/dist/utils/v3-agent-client.d.ts +40 -0
  155. package/dist/utils/v3-agent-client.js +165 -0
  156. package/dist/utils/v3-workspace-service.d.ts +28 -0
  157. package/dist/utils/v3-workspace-service.js +279 -0
  158. package/dist/utils/vigflow-client.d.ts +95 -0
  159. package/dist/utils/vigflow-client.js +105 -0
  160. package/dist/utils/workspace-boundary.d.ts +45 -0
  161. package/dist/utils/workspace-boundary.js +377 -0
  162. package/dist/utils/workspace-brain-service.d.ts +2 -0
  163. package/dist/utils/workspace-brain-service.js +11 -1
  164. package/dist/utils/workspace-cache.js +37 -15
  165. package/dist/utils/workspace-stream.js +35 -10
  166. package/install.ps1 +86 -143
  167. package/install.sh +76 -81
  168. package/package.json +44 -9
  169. package/release-policy.json +43 -0
  170. package/scripts/release/LOCAL_MACHINE_USER_VERIFICATION.md +1 -1
  171. package/scripts/release/generate-completions.mjs +24 -0
  172. package/scripts/release/install-release.mjs +10 -0
  173. package/scripts/release/publish-cli-release.mjs +116 -0
  174. package/scripts/release/publish-cli-release.sh +2 -70
  175. package/scripts/release/resolve-release-manifest.mjs +26 -0
  176. package/scripts/release/test-balanced-model-live.sh +42 -0
  177. package/scripts/release/validate-live-service-gates.sh +90 -0
  178. package/scripts/release/validate-no-go-gates.sh +135 -172
  179. package/scripts/release/verify-runtime-consistency.mjs +4 -1
  180. package/dist/commands/index.d.ts +0 -12
  181. package/dist/commands/index.js +0 -194
  182. package/dist/commands/update.d.ts +0 -9
  183. package/dist/commands/update.js +0 -301
  184. package/dist/utils/templateInstantPath.d.ts +0 -18
  185. package/dist/utils/templateInstantPath.js +0 -228
@@ -0,0 +1,293 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createPublicKey, verify as verifySignature } from 'node:crypto';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { CliCommandError } from './command-contract.js';
6
+ import { compareSemanticVersions } from './update-policy.js';
7
+ const SHA256 = /^[a-f0-9]{64}$/;
8
+ const SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
9
+ const TOP_LEVEL_FIELDS = new Set(['schemaVersion', 'channels']);
10
+ const ENTRY_FIELDS = new Set(['version', 'url', 'sha256', 'size', 'archiveFilename', 'packageName', 'packageVersion', 'publishedAt', 'signature', 'sbom', 'provenance', 'notes']);
11
+ const SIGNATURE_FIELDS = new Set(['algorithm', 'keyId', 'url']);
12
+ const EVIDENCE_FIELDS = new Set(['url', 'sha256']);
13
+ function fail(message, code = 'RELEASE_MANIFEST_INVALID') {
14
+ throw new CliCommandError(message, { code, category: 'configuration' });
15
+ }
16
+ function policyPath() {
17
+ const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));
18
+ const candidates = [
19
+ path.resolve(moduleDirectory, '../../release-policy.json'),
20
+ path.resolve(moduleDirectory, '../release-policy.json'),
21
+ path.resolve(process.cwd(), 'release-policy.json'),
22
+ ];
23
+ const resolved = candidates.find((candidate) => fs.existsSync(candidate));
24
+ if (!resolved)
25
+ return fail('The packaged release-policy.json file is missing.', 'RELEASE_POLICY_MISSING');
26
+ return resolved;
27
+ }
28
+ let cachedPolicy = null;
29
+ export function getReleasePolicy() {
30
+ if (cachedPolicy)
31
+ return cachedPolicy;
32
+ const raw = JSON.parse(fs.readFileSync(policyPath(), 'utf8'));
33
+ if (raw.schemaVersion !== 1 || raw.manifest?.schemaVersion !== 1 || raw.signature?.algorithm !== 'Ed25519') {
34
+ return fail('Unsupported release policy schema.', 'RELEASE_POLICY_UNSUPPORTED');
35
+ }
36
+ cachedPolicy = Object.freeze(raw);
37
+ return cachedPolicy;
38
+ }
39
+ function assertExactFields(value, allowed, label) {
40
+ for (const field of Object.keys(value)) {
41
+ if (!allowed.has(field))
42
+ fail(`${label} contains unknown field: ${field}`);
43
+ }
44
+ }
45
+ export function assertReleaseUrl(raw, kind = 'artifact') {
46
+ const policy = getReleasePolicy();
47
+ if (typeof raw !== 'string' || !raw || /[\\\u0000-\u001f\u007f]/.test(raw))
48
+ fail(`Invalid ${kind} URL.`);
49
+ let url;
50
+ try {
51
+ url = new URL(raw);
52
+ }
53
+ catch {
54
+ return fail(`Invalid ${kind} URL.`);
55
+ }
56
+ if (url.protocol !== 'https:' || url.username || url.password || (url.port && Number(url.port) !== policy.origins.port)) {
57
+ fail(`${kind} URL must use HTTPS without credentials on the policy port.`);
58
+ }
59
+ if (!policy.origins.allowedHosts.includes(url.hostname.toLowerCase()))
60
+ fail(`${kind} URL host is not allowlisted.`);
61
+ if (kind === 'manifest' && url.toString() !== policy.origins.manifest)
62
+ fail('Manifest URL does not match the canonical release policy origin.');
63
+ return url;
64
+ }
65
+ function parseEvidence(raw, label) {
66
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
67
+ return fail(`${label} metadata is required.`);
68
+ const record = raw;
69
+ assertExactFields(record, EVIDENCE_FIELDS, label);
70
+ const url = assertReleaseUrl(record.url, 'evidence').toString();
71
+ const sha256 = String(record.sha256 || '').toLowerCase();
72
+ if (!SHA256.test(sha256))
73
+ fail(`${label} SHA-256 is malformed.`);
74
+ return { url, sha256 };
75
+ }
76
+ export function parseReleaseManifest(input, channel = 'stable', serializedBytes) {
77
+ const policy = getReleasePolicy();
78
+ if (serializedBytes !== undefined && serializedBytes > policy.manifest.maximumBytes)
79
+ fail('Release manifest exceeds the policy size limit.');
80
+ if (!input || typeof input !== 'object' || Array.isArray(input))
81
+ return fail('Release manifest must be a JSON object.');
82
+ const manifest = input;
83
+ assertExactFields(manifest, TOP_LEVEL_FIELDS, 'Release manifest');
84
+ if (manifest.schemaVersion !== policy.manifest.schemaVersion)
85
+ fail('Release manifest schemaVersion is missing or unsupported.');
86
+ if (!policy.manifest.allowedChannels.includes(channel))
87
+ fail(`Release channel is not allowed: ${channel}`);
88
+ if (!manifest.channels || typeof manifest.channels !== 'object' || Array.isArray(manifest.channels))
89
+ fail('Release manifest channels object is required.');
90
+ const channels = manifest.channels;
91
+ for (const name of Object.keys(channels))
92
+ if (!policy.manifest.allowedChannels.includes(name))
93
+ fail(`Release manifest contains unknown channel: ${name}`);
94
+ for (const name of policy.manifest.requiredChannels)
95
+ if (!(name in channels))
96
+ fail(`Release manifest is missing required channel: ${name}`);
97
+ const raw = channels[channel];
98
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
99
+ return fail(`Release manifest has no entry for channel ${channel}.`);
100
+ const entry = raw;
101
+ assertExactFields(entry, ENTRY_FIELDS, `Release channel ${channel}`);
102
+ const version = String(entry.version || '');
103
+ const packageVersion = String(entry.packageVersion || '');
104
+ const archiveFilename = String(entry.archiveFilename || '');
105
+ const packageName = String(entry.packageName || '');
106
+ const sha256 = String(entry.sha256 || '').toLowerCase();
107
+ const size = Number(entry.size);
108
+ if (!SEMVER.test(version) || compareSemanticVersions(version, version) !== 0)
109
+ fail('Release version is invalid.');
110
+ if (version !== packageVersion)
111
+ fail('Release version conflicts with packageVersion.');
112
+ if (packageName !== policy.packageName)
113
+ fail('Release packageName conflicts with policy.');
114
+ if (!SHA256.test(sha256))
115
+ fail('Release artifact SHA-256 is malformed.');
116
+ if (!Number.isSafeInteger(size) || size <= 0 || size > policy.artifact.maximumBytes)
117
+ fail('Release artifact size is invalid or exceeds policy.');
118
+ if (!new RegExp(policy.artifact.filenamePattern).test(archiveFilename) || archiveFilename !== `${policy.packageName}-${version}.tgz`)
119
+ fail('Release archive filename conflicts with version identity.');
120
+ const artifactUrl = assertReleaseUrl(entry.url, 'artifact');
121
+ if (path.posix.basename(artifactUrl.pathname) !== archiveFilename)
122
+ fail('Release URL filename conflicts with archiveFilename.');
123
+ if (!policy.channels[channel].allowPrerelease && version.includes('-'))
124
+ fail(`Prerelease versions are forbidden on channel ${channel}.`);
125
+ const publishedAt = String(entry.publishedAt || '');
126
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(publishedAt) || !Number.isFinite(Date.parse(publishedAt)))
127
+ fail('Release publishedAt is invalid.');
128
+ if (!entry.signature || typeof entry.signature !== 'object' || Array.isArray(entry.signature))
129
+ return fail('Detached signature metadata is required.');
130
+ const signature = entry.signature;
131
+ assertExactFields(signature, SIGNATURE_FIELDS, 'Release signature');
132
+ const algorithm = String(signature.algorithm || '');
133
+ const keyId = String(signature.keyId || '');
134
+ if (algorithm !== policy.signature.algorithm)
135
+ fail('Release signature algorithm conflicts with policy.');
136
+ if (!Object.hasOwn(policy.signature.trustedKeys, keyId))
137
+ fail('Release signature key is not trusted.');
138
+ const signatureUrl = assertReleaseUrl(signature.url, 'signature');
139
+ if (path.posix.basename(signatureUrl.pathname) !== `${archiveFilename}.sig`)
140
+ fail('Detached signature filename conflicts with archive identity.');
141
+ return {
142
+ version,
143
+ url: artifactUrl.toString(),
144
+ sha256,
145
+ size,
146
+ archiveFilename,
147
+ packageName,
148
+ packageVersion,
149
+ publishedAt,
150
+ signature: { algorithm: 'Ed25519', keyId, url: signatureUrl.toString() },
151
+ sbom: parseEvidence(entry.sbom, 'SBOM'),
152
+ provenance: parseEvidence(entry.provenance, 'Provenance'),
153
+ ...(typeof entry.notes === 'string' ? { notes: entry.notes } : {}),
154
+ };
155
+ }
156
+ export function parseReleaseManifestText(text, channel = 'stable') {
157
+ const bytes = Buffer.byteLength(text, 'utf8');
158
+ if (bytes > getReleasePolicy().manifest.maximumBytes)
159
+ fail('Release manifest exceeds the policy size limit.');
160
+ let index = 0;
161
+ const whitespace = () => { while (/\s/.test(text[index] || ''))
162
+ index += 1; };
163
+ const parseString = () => {
164
+ whitespace();
165
+ if (text[index] !== '"')
166
+ fail('Release manifest JSON is malformed.');
167
+ const start = index++;
168
+ let escaped = false;
169
+ while (index < text.length) {
170
+ const character = text[index++];
171
+ if (escaped) {
172
+ escaped = false;
173
+ continue;
174
+ }
175
+ if (character === '\\') {
176
+ escaped = true;
177
+ continue;
178
+ }
179
+ if (character === '"') {
180
+ try {
181
+ return JSON.parse(text.slice(start, index));
182
+ }
183
+ catch {
184
+ return fail('Release manifest JSON string is malformed.');
185
+ }
186
+ }
187
+ if (character.charCodeAt(0) < 0x20)
188
+ fail('Release manifest JSON string contains a control character.');
189
+ }
190
+ return fail('Release manifest JSON string is unterminated.');
191
+ };
192
+ const parseValue = () => {
193
+ whitespace();
194
+ if (text[index] === '{') {
195
+ index += 1;
196
+ const keys = new Set();
197
+ whitespace();
198
+ if (text[index] === '}') {
199
+ index += 1;
200
+ return;
201
+ }
202
+ while (index < text.length) {
203
+ const key = parseString();
204
+ if (keys.has(key))
205
+ fail(`Release manifest contains duplicate field: ${key}`);
206
+ keys.add(key);
207
+ whitespace();
208
+ if (text[index++] !== ':')
209
+ fail('Release manifest JSON object is malformed.');
210
+ parseValue();
211
+ whitespace();
212
+ const separator = text[index++];
213
+ if (separator === '}')
214
+ return;
215
+ if (separator !== ',')
216
+ fail('Release manifest JSON object is malformed.');
217
+ }
218
+ fail('Release manifest JSON object is unterminated.');
219
+ }
220
+ if (text[index] === '[') {
221
+ index += 1;
222
+ whitespace();
223
+ if (text[index] === ']') {
224
+ index += 1;
225
+ return;
226
+ }
227
+ while (index < text.length) {
228
+ parseValue();
229
+ whitespace();
230
+ const separator = text[index++];
231
+ if (separator === ']')
232
+ return;
233
+ if (separator !== ',')
234
+ fail('Release manifest JSON array is malformed.');
235
+ }
236
+ fail('Release manifest JSON array is unterminated.');
237
+ }
238
+ if (text[index] === '"') {
239
+ parseString();
240
+ return;
241
+ }
242
+ const remainder = text.slice(index);
243
+ const primitive = /^(?:true|false|null|-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)/.exec(remainder);
244
+ if (!primitive)
245
+ fail('Release manifest JSON value is malformed.');
246
+ index += primitive[0].length;
247
+ };
248
+ parseValue();
249
+ whitespace();
250
+ if (index !== text.length)
251
+ fail('Release manifest JSON has trailing content.');
252
+ let parsed;
253
+ try {
254
+ parsed = JSON.parse(text);
255
+ }
256
+ catch {
257
+ return fail('Release manifest JSON is malformed.');
258
+ }
259
+ return parseReleaseManifest(parsed, channel, bytes);
260
+ }
261
+ function canonicalize(value) {
262
+ if (Array.isArray(value))
263
+ return `[${value.map(canonicalize).join(',')}]`;
264
+ if (value && typeof value === 'object') {
265
+ const record = value;
266
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalize(record[key])}`).join(',')}}`;
267
+ }
268
+ return JSON.stringify(value);
269
+ }
270
+ export function releaseSignaturePayload(channel, entry) {
271
+ return Buffer.from(canonicalize({ channel, entry }), 'utf8');
272
+ }
273
+ export function verifyReleaseSignature(channel, entry, detachedBase64, trustedKeys) {
274
+ const keys = trustedKeys || getReleasePolicy().signature.trustedKeys;
275
+ const publicKey = keys[entry.signature.keyId];
276
+ if (!publicKey)
277
+ fail('Detached signature key is not trusted.', 'RELEASE_SIGNATURE_UNTRUSTED');
278
+ const encoded = String(detachedBase64 || '').trim();
279
+ if (!/^[A-Za-z0-9+/]+={0,2}$/.test(encoded))
280
+ fail('Detached signature encoding is malformed.', 'RELEASE_SIGNATURE_INVALID');
281
+ const signature = Buffer.from(encoded, 'base64');
282
+ if (signature.length !== 64 || !verifySignature(null, releaseSignaturePayload(channel, entry), createPublicKey(publicKey), signature)) {
283
+ fail('Detached release signature verification failed.', 'RELEASE_SIGNATURE_INVALID');
284
+ }
285
+ }
286
+ export function assertReleaseTransition(currentVersion, entry, channel, allowDowngrade = false) {
287
+ const channelPolicy = getReleasePolicy().channels[channel];
288
+ if (!channelPolicy)
289
+ fail(`Release channel is not allowed: ${channel}`);
290
+ if (compareSemanticVersions(entry.version, currentVersion) < 0 && !(allowDowngrade && channelPolicy.allowDowngrade)) {
291
+ fail(`Downgrade from ${currentVersion} to ${entry.version} is prohibited.`, 'RELEASE_DOWNGRADE_PROHIBITED');
292
+ }
293
+ }
@@ -4,12 +4,13 @@
4
4
  */
5
5
  const CLI_SHAPING_BLOCK = /(?:^|\n\n)Platform:\s*(?:Windows|macOS|Linux)\.[\s\S]*?(?=\n\n[A-Z][^\n]{0,40}:|\s*$)/i;
6
6
  const READONLY_SHAPING_BLOCK = /(?:^|\n\n)(?:Read-only analysis mode is active\.|Diagnostic mode is active\.)[\s\S]*?(?=\n\n[A-Z][^\n]{0,40}:|\s*$)/i;
7
- const BUILD_VERBS = /\b(build|create|make|implement|complete|redesign|overhaul|rebuild|transform|polish|art-direct|fix|repair|edit|modify|write|generate|add|finish|scaffold|develop|update|change|refactor|want|need|require|looking for|let's|let us|we need|we should|erstelle|erstellen|schreib|schreibe|bearbeite)\b/i;
7
+ const BUILD_VERBS = /\b(build|create|make|implement|complete|redesign|overhaul|rebuild|transform|polish|refine|art-direct|fix|repair|correct|edit|modify|write|generate|add|finish|scaffold|develop|update|change|refactor|want|need|require|looking for|let's|let us|we need|we should|erstelle|erstellen|schreib|schreibe|bearbeite)\b/i;
8
8
  const ARTIFACT_NOUNS = /\b(file|files|project|game|spiel|app|website|html5|frontend|component|feature|code|workspace|repo|clone|replica|remake|page|site|design|showcase|screen|button|popup|modal|alert|canvas|sprite|level|levels|enemy|enemies|character|npc|world|scene|script|module|api|database|config|landing|dashboard|platformer|arcade|playable|collectible|scoreboard|leaderboard|pitfall|pacman|rogue|html|css|javascript|typescript)\b/i;
9
9
  /** Canvas/playable games only — bare "html5" or "website" must NOT match. */
10
10
  const GAME_INTENT = /\b(game|spiel|playable|babylon(?:\.js|js)?|vge|webgl|3d\s+(?:game|runner|prototype|world|experience)|html5\s+(?:game|canvas)|html5\s+game|canvas\s+game|game\s+canvas|arcade|platformer|side[- ]?scroller|pitfall|pac[- ]?man|tetris|snake|breakout|pong|roguelike|metroidvania|tower\s+defense|clone\s+of\s+(?:a\s+)?(?:game|pitfall|pac|mario|zelda|arcade)|gameplay|collectible|boss\s+fight|level\s+design|sprite\s+sheet|wild\s+(?:boar|pig|forest)|forest\s+pig)\b/i;
11
11
  const WEB_PAGE_INTENT = /\b(website|web\s*site|webpage|web\s*page|landing\s+page|home\s*page|index\.html|html\s+page|static\s+page|single\s+page|popup|alert|modal|hello\s+world)\b/i;
12
12
  const WEB_SHOWCASE_INTENT = /\b(web[- ]?design\s+showcase|frontend|marketing\s+site|dashboard|saas)\b/i;
13
+ const COMPREHENSIVE_WEB_BUILD = /\b(?:redesign|overhaul|rebuild|transform|art[- ]direct|enterprise[- ]grade|premium\s+(?:website|web|design)|showcase\s+templates?|five\s+(?:distinct\s+)?(?:pages?|templates?))\b/i;
13
14
  const CLONE_BUILD = /\bclone\s+of\b|\b(build|create|make)\s+(?:a|an|the|us|me)?\s*(?:clone|replica|remake|port)\b/i;
14
15
  const READ_ONLY_INTENT = /\b(analy[sz]e|analyse|analysis|audit|review|inspect|proof|understand|summari[sz]e|scan|read[\s-]?only|where we left|what is|how does|show me|tell me|identify\s+gaps?|gap\s+analysis|production\s+blockers?)\b/i;
15
16
  const EXPLICIT_WRITE_VERBS = /\b(implement|write|edit|modify|update|redesign|overhaul|rebuild|transform|polish|art-direct|fix|repair|create|build|generate|scaffold|deploy|refactor|develop|integrate|correct|apply|patch)\b/i;
@@ -17,7 +18,7 @@ const TRIVIAL_HTML_PAGE = /\b(hello\s*world|simple\s+(?:html|web|page|site)|html
17
18
  /** User explicitly grants write permission after an analysis or confirmation gate. */
18
19
  const WRITE_PERMISSION_GRANT = /\b(?:hereby\s+confirm|i\s+confirm|confirm(?:ed)?\s+that|give\s+(?:you|permission)|granting\s+permission|allow\s+(?:you|me\s+to)|you\s+(?:may|can|have\s+permission\s+to)|permission\s+to|authorized?\s+to|approve(?:d)?\s+(?:the\s+)?(?:changes?|writes?|edits?)|go\s+ahead\s+and(?:\s+write|\s+apply|\s+make|\s+do)?|do\s+all\s+the\s+changes|apply\s+(?:the\s+)?(?:changes?|fixes?|fix)|proceed\s+with\s+(?:the\s+)?(?:changes?|fixes?|implementation|writes?)|make\s+(?:the\s+)?changes|please\s+write)\b/i;
19
20
  /** Fix/correct/repair syntax or errors — write path without requiring artifact nouns. */
20
- const FIX_CORRECT_INTENT = /\b(correct|fix|repair|resolve|patch|address|remedy)\b[\s\S]{0,48}\b(syntax|errors?|bugs?|issues?|mistakes?|typos?)\b|\b(syntax|errors?|bugs?|issues?)\b[\s\S]{0,48}\b(correct|fix|repair|resolve|patch)\b/i;
21
+ const FIX_CORRECT_INTENT = /\b(correct|fix|repair|resolve|patch|address|remedy)\b[\s\S]{0,48}\b(syntax|errors?|bugs?|issues?|defects?|mistakes?|typos?)\b|\b(syntax|errors?|bugs?|issues?|defects?)\b[\s\S]{0,48}\b(correct|fix|repair|resolve|patch)\b/i;
21
22
  /** Read-only audit: check/review/find issues without an paired fix/implement verb nearby. */
22
23
  const READ_ONLY_CHECK = /\b(check|verify|audit|inspect|review|investigate|look\s+for|find|scan|validate|examine)\b[\s\S]{0,60}\b(syntax|errors?|bugs?|issues?|missing|gaps?|functionality)\b/i;
23
24
  /** Strip CLI-appended platform / mode shaping so it cannot flip quality profiles. */
@@ -77,6 +78,14 @@ export function isAgentContinuePrompt(prompt) {
77
78
  if (isBuiltContinuePrompt(text) || isBuiltRetryPrompt(text)) {
78
79
  return false;
79
80
  }
81
+ const concreteFileScope = /(?<![A-Za-z0-9_.-])[A-Za-z0-9_./\\-]+\.[A-Za-z0-9]{1,16}(?![A-Za-z0-9_.-])/.test(text);
82
+ const detailedMutationFollowUp = EXPLICIT_WRITE_VERBS.test(text) && (concreteFileScope || text.split(/\s+/).length > 20);
83
+ if (detailedMutationFollowUp) {
84
+ // A user may naturally say "continue" while also supplying new defect
85
+ // evidence, exact file ownership, or revised acceptance criteria. That is
86
+ // a new scoped follow-up, not shorthand for replaying the stored prompt.
87
+ return false;
88
+ }
80
89
  if (/^\/(?:continue|contiune|contiunue)$/i.test(text)) {
81
90
  return true;
82
91
  }
@@ -128,7 +137,7 @@ export function isTrivialHtmlPageRequest(prompt) {
128
137
  if (text.length > 420) {
129
138
  return false;
130
139
  }
131
- if (/\b(multi[- ]?page|full\s+saas|dashboard|e[\s-]?commerce|auth|database|backend|api|microservice)\b/i.test(text)) {
140
+ if (/\b(multi[- ]?page|five\s+pages?|showcase|enterprise(?:[- ]grade)?|animated?|motion|redesign|overhaul|existing\s+(?:site|website|project)|full\s+saas|dashboard|e[\s-]?commerce|auth|database|backend|api|microservice)\b/i.test(text)) {
132
141
  return false;
133
142
  }
134
143
  return TRIVIAL_HTML_PAGE.test(text)
@@ -247,6 +256,16 @@ export function inferAgentTaskType(prompt) {
247
256
  }
248
257
  return 'implementation';
249
258
  }
259
+ // A comprehensive website redesign remains a web-build even when the brief
260
+ // also says to fix/repair defects found during the audit. Treating the repair
261
+ // verb as globally dominant collapses the required markup/style/interaction
262
+ // decomposition into a narrow three-step bug-fix workflow.
263
+ if (hasWebPageIntent(prompt)
264
+ && hasBuildVerb(prompt)
265
+ && COMPREHENSIVE_WEB_BUILD.test(text)
266
+ && !hasGameIntent(prompt)) {
267
+ return 'web-build';
268
+ }
250
269
  if (hasFixOrCorrectIntent(prompt)) {
251
270
  if (hasGameIntent(prompt)) {
252
271
  return 'game-build';
@@ -0,0 +1,8 @@
1
+ export type RuntimeCapabilities = {
2
+ loopbackServices: boolean;
3
+ localV3Agent: boolean;
4
+ };
5
+ /** Explicit operator grants only; never infer trust from hostname, cwd, OS, or container state. */
6
+ export declare function resolveRuntimeCapabilities(environment?: NodeJS.ProcessEnv): RuntimeCapabilities;
7
+ export declare function hasLoopbackServiceCapability(environment?: NodeJS.ProcessEnv): boolean;
8
+ export declare function hasLocalV3AgentCapability(environment?: NodeJS.ProcessEnv): boolean;
@@ -0,0 +1,16 @@
1
+ /** Explicit operator grants only; never infer trust from hostname, cwd, OS, or container state. */
2
+ export function resolveRuntimeCapabilities(environment = process.env) {
3
+ const loopbackServices = environment.VIGTHORIA_ALLOW_LOCAL_SERVICES === '1'
4
+ || environment.VIGTHORIA_ALLOW_LOOPBACK === '1'
5
+ || environment.VIGTHORIA_LOCAL_TEST_MODE === '1';
6
+ return {
7
+ loopbackServices,
8
+ localV3Agent: loopbackServices && environment.VIGTHORIA_ALLOW_LOCAL_V3_AGENT === '1',
9
+ };
10
+ }
11
+ export function hasLoopbackServiceCapability(environment = process.env) {
12
+ return resolveRuntimeCapabilities(environment).loopbackServices;
13
+ }
14
+ export function hasLocalV3AgentCapability(environment = process.env) {
15
+ return resolveRuntimeCapabilities(environment).localV3Agent;
16
+ }
@@ -0,0 +1,20 @@
1
+ export interface OutboundScanResult<T> {
2
+ value: T;
3
+ findings: string[];
4
+ }
5
+ export declare class OutboundContextError extends Error {
6
+ readonly findings: string[];
7
+ constructor(findings: string[]);
8
+ }
9
+ export declare function isSensitivePath(pathValue: string): boolean;
10
+ export declare function containsHighConfidenceSecret(input: string): boolean;
11
+ export declare function redactSensitiveText(input: string): string;
12
+ export declare function redactSensitiveValue(value: unknown, seen?: WeakSet<object>): unknown;
13
+ /**
14
+ * Final production-output guard. Commands should still redact at their own
15
+ * boundaries, but this prevents an overlooked remote error or JSON object
16
+ * from writing credentials directly to stdout/stderr.
17
+ */
18
+ export declare function installConsoleRedaction(): void;
19
+ export declare function scanOutboundContext<T>(input: T): OutboundScanResult<T>;
20
+ export declare function safeChildProcessEnv(source?: NodeJS.ProcessEnv, additions?: Record<string, string>): NodeJS.ProcessEnv;
@@ -0,0 +1,160 @@
1
+ const REDACTED = '[REDACTED]';
2
+ const SENSITIVE_SEGMENTS = new Set([
3
+ '.git', '.ssh', '.aws', '.azure', '.kube', '.gnupg', '.vigthoria',
4
+ 'credentials', 'credential', 'secrets', 'secret',
5
+ ]);
6
+ const SENSITIVE_BASENAMES = new Set([
7
+ '.env', '.npmrc', '.pypirc', '.netrc', '.git-credentials',
8
+ 'credentials.json', 'credential.json', 'secrets.json', 'secret.json',
9
+ 'auth.json', 'session.json', 'tokens.json', 'token.json',
10
+ 'id_rsa', 'id_dsa', 'id_ecdsa', 'id_ed25519',
11
+ ]);
12
+ const SENSITIVE_EXTENSIONS = new Set([
13
+ '.pem', '.key', '.p12', '.pfx', '.jks', '.keystore', '.crtkey', '.ovpn',
14
+ ]);
15
+ const SECRET_PATTERNS = [
16
+ /-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/i,
17
+ /\bAKIA[0-9A-Z]{16}\b/,
18
+ /\b(?:ghp|gho|ghu|ghs|github_pat)_[A-Za-z0-9_]{20,}\b/,
19
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/,
20
+ /\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}\b/,
21
+ /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/,
22
+ /\b(?:password|passwd|pwd|client_secret|private_key|api[_-]?key|access[_-]?token|refresh[_-]?token|auth[_-]?token)\s*[:=]\s*["']?[A-Za-z0-9_./+=:-]{8,}/i,
23
+ ];
24
+ const SENSITIVE_KEY = /(?:^|[_-])(?:authorization|cookie|password|passwd|pwd|secret|private[_-]?key|api[_-]?key|access[_-]?token|refresh[_-]?token|auth[_-]?token|session)(?:$|[_-])/i;
25
+ const SENSITIVE_ENV_KEY = /(?:TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|COOKIE|SESSION|PRIVATE_KEY|API_KEY|AUTH|AWS_|AZURE_|GITHUB_|GITLAB_|NPM_|SSH_)/i;
26
+ export class OutboundContextError extends Error {
27
+ findings;
28
+ constructor(findings) {
29
+ super(`Outbound context contains prohibited secret material (${findings.join(', ')})`);
30
+ this.name = 'OutboundContextError';
31
+ this.findings = findings;
32
+ }
33
+ }
34
+ export function isSensitivePath(pathValue) {
35
+ const normalized = String(pathValue || '').replace(/\\/g, '/').replace(/^\.\//, '').toLowerCase();
36
+ if (!normalized)
37
+ return false;
38
+ const segments = normalized.split('/').filter(Boolean);
39
+ const basename = segments.at(-1) || '';
40
+ if (segments.some((segment) => SENSITIVE_SEGMENTS.has(segment)))
41
+ return true;
42
+ if (SENSITIVE_BASENAMES.has(basename))
43
+ return true;
44
+ if (basename.startsWith('.env.') || basename.endsWith('.env'))
45
+ return true;
46
+ if (/^(?:credentials?|secrets?)[._-]/.test(basename))
47
+ return true;
48
+ if (/^(?:tokens?|auth|sessions?)\.(?:json|db|sqlite|sqlite3|yaml|yml)$/i.test(basename))
49
+ return true;
50
+ return SENSITIVE_EXTENSIONS.has(basename.slice(basename.lastIndexOf('.')));
51
+ }
52
+ export function containsHighConfidenceSecret(input) {
53
+ const value = String(input || '').replace(/\[REDACTED\]/g, '');
54
+ return SECRET_PATTERNS.some((pattern) => pattern.test(value));
55
+ }
56
+ export function redactSensitiveText(input) {
57
+ return String(input || '')
58
+ .replace(/-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/gi, REDACTED)
59
+ .replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, `Bearer ${REDACTED}`)
60
+ .replace(/\bAKIA[0-9A-Z]{16}\b/g, REDACTED)
61
+ .replace(/\b(?:ghp|gho|ghu|ghs|github_pat)_[A-Za-z0-9_]{20,}\b/g, REDACTED)
62
+ .replace(/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, REDACTED)
63
+ .replace(/\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}\b/g, REDACTED)
64
+ .replace(/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, REDACTED)
65
+ .replace(/([?&](?:token|access_token|refresh_token|api_key|apikey|key|secret|signature)=)[^&#\s]+/gi, `$1${REDACTED}`)
66
+ .replace(/\b((?:password|passwd|pwd|token|access_token|refresh_token|api_key|apikey|client_secret|private_key|secret|signature)\s*[:=]\s*)["']?[^,;\s"']+["']?/gi, `$1${REDACTED}`)
67
+ .replace(/((?:authorization|cookie|x-api-key|x-service-key)\s*[:=]\s*)[^,;\s]+/gi, `$1${REDACTED}`)
68
+ .replace(/(vigthoria-auth-token=)[^;\s]+/gi, `$1${REDACTED}`);
69
+ }
70
+ export function redactSensitiveValue(value, seen = new WeakSet()) {
71
+ if (typeof value === 'string')
72
+ return redactSensitiveText(value);
73
+ if (value instanceof Error)
74
+ return `${value.name}: ${redactSensitiveText(value.message)}`;
75
+ if (!value || typeof value !== 'object')
76
+ return value;
77
+ if (seen.has(value))
78
+ return '[Circular]';
79
+ seen.add(value);
80
+ if (Array.isArray(value))
81
+ return value.map((entry) => redactSensitiveValue(entry, seen));
82
+ const output = {};
83
+ for (const [key, entry] of Object.entries(value)) {
84
+ output[key] = SENSITIVE_KEY.test(key) ? REDACTED : redactSensitiveValue(entry, seen);
85
+ }
86
+ return output;
87
+ }
88
+ let consoleRedactionInstalled = false;
89
+ /**
90
+ * Final production-output guard. Commands should still redact at their own
91
+ * boundaries, but this prevents an overlooked remote error or JSON object
92
+ * from writing credentials directly to stdout/stderr.
93
+ */
94
+ export function installConsoleRedaction() {
95
+ if (consoleRedactionInstalled)
96
+ return;
97
+ consoleRedactionInstalled = true;
98
+ for (const method of ['log', 'info', 'warn', 'error', 'debug']) {
99
+ const original = console[method].bind(console);
100
+ console[method] = ((...args) => {
101
+ original(...args.map((value) => redactSensitiveValue(value)));
102
+ });
103
+ }
104
+ }
105
+ export function scanOutboundContext(input) {
106
+ const findings = new Set();
107
+ const seen = new WeakSet();
108
+ const visit = (value, location, depth) => {
109
+ if (depth > 24)
110
+ throw new OutboundContextError([`${location}: depth limit exceeded`]);
111
+ if (typeof value === 'string') {
112
+ if (!/\s/.test(value) && isSensitivePath(value)) {
113
+ findings.add(`${location}: sensitive path value excluded`);
114
+ return REDACTED;
115
+ }
116
+ if (containsHighConfidenceSecret(value))
117
+ findings.add(`${location}: secret value redacted`);
118
+ return redactSensitiveText(value);
119
+ }
120
+ if (!value || typeof value !== 'object')
121
+ return value;
122
+ if (seen.has(value))
123
+ throw new OutboundContextError([`${location}: cyclic value`]);
124
+ seen.add(value);
125
+ if (Array.isArray(value))
126
+ return value.map((entry, index) => visit(entry, `${location}[${index}]`, depth + 1));
127
+ const output = {};
128
+ for (const [key, entry] of Object.entries(value)) {
129
+ const child = `${location}.${key}`;
130
+ if (isSensitivePath(key)) {
131
+ findings.add(`${child}: sensitive path excluded`);
132
+ continue;
133
+ }
134
+ if (SENSITIVE_KEY.test(key)) {
135
+ findings.add(`${child}: credential field redacted`);
136
+ output[key] = REDACTED;
137
+ continue;
138
+ }
139
+ output[key] = visit(entry, child, depth + 1);
140
+ }
141
+ return output;
142
+ };
143
+ const value = visit(input, '$', 0);
144
+ if (containsHighConfidenceSecret(JSON.stringify(value))) {
145
+ throw new OutboundContextError(['residual secret after redaction']);
146
+ }
147
+ return { value, findings: [...findings].sort() };
148
+ }
149
+ export function safeChildProcessEnv(source = process.env, additions = {}) {
150
+ const output = {};
151
+ for (const [key, value] of Object.entries(source)) {
152
+ if (value !== undefined && !SENSITIVE_ENV_KEY.test(key))
153
+ output[key] = value;
154
+ }
155
+ for (const [key, value] of Object.entries(additions)) {
156
+ if (!SENSITIVE_ENV_KEY.test(key))
157
+ output[key] = value;
158
+ }
159
+ return output;
160
+ }
@@ -2,7 +2,7 @@
2
2
  * Session Manager - Persist and resume conversations
3
3
  * Similar to Vigthoria's session persistence
4
4
  */
5
- import { ChatMessage } from './api.js';
5
+ import type { ChatMessage } from './api.js';
6
6
  export type AuthState = {
7
7
  token: string | null;
8
8
  expiresAt: number | null;
@@ -40,16 +40,40 @@ export interface Session {
40
40
  project: string;
41
41
  model: string;
42
42
  messages: ChatMessage[];
43
+ /** Stable event identities aligned one-to-one with messages. */
44
+ messageIds?: string[];
43
45
  memorySummary?: string;
44
46
  compactedAt?: string | null;
45
47
  createdAt: string;
46
48
  updatedAt: string;
47
49
  agentMode: boolean;
48
50
  operatorMode?: boolean;
51
+ projectId?: string;
49
52
  lastAgentRunOutcome?: Record<string, unknown> | null;
53
+ activeAgentRun?: AgentExecutionCheckpoint | null;
50
54
  }
55
+ export type AgentExecutionStatus = 'submitted' | 'retry' | 'continue' | 'running' | 'completed' | 'failed' | 'cancelled';
56
+ export interface AgentExecutionCheckpoint {
57
+ executionId: string;
58
+ parentExecutionId?: string | null;
59
+ contextId: string;
60
+ prompt: string;
61
+ originalPrompt: string;
62
+ workspacePath: string;
63
+ userTier: string | null;
64
+ taskType: string;
65
+ workflowType: string;
66
+ status: AgentExecutionStatus;
67
+ submittedAt: string;
68
+ updatedAt: string;
69
+ failedTaskIds: string[];
70
+ unfinishedTaskIds: string[];
71
+ error?: string | null;
72
+ }
73
+ export declare function assertAgentStatusTransition(from: AgentExecutionStatus, to: AgentExecutionStatus): void;
51
74
  export declare class SessionManager {
52
75
  private sessionsDir;
76
+ private indexStore;
53
77
  private readonly compactThreshold;
54
78
  private readonly retainRecentMessages;
55
79
  /** Keep at most this many session files on disk per CLI install. */
@@ -58,7 +82,12 @@ export declare class SessionManager {
58
82
  private readonly retentionWindowMs;
59
83
  /** Avoid running prune on every save — at most once per CLI process. */
60
84
  private prunedThisProcess;
61
- constructor();
85
+ constructor(stateRoot?: string);
86
+ private workspaceSessionsDir;
87
+ private sessionStore;
88
+ private ensureMessageIds;
89
+ private readSessionFile;
90
+ private readSessionsFromDir;
62
91
  private ensureDir;
63
92
  /**
64
93
  * Prune sessions older than the retention window AND cap the number of
@@ -70,6 +99,7 @@ export declare class SessionManager {
70
99
  * Generate unique session ID
71
100
  */
72
101
  private generateId;
102
+ private getProjectId;
73
103
  /**
74
104
  * Create a new session
75
105
  */
@@ -79,6 +109,7 @@ export declare class SessionManager {
79
109
  * read by other local users on a shared POSIX host.
80
110
  */
81
111
  save(session: Session): void;
112
+ private mergeConcurrentSession;
82
113
  /**
83
114
  * Load session by ID
84
115
  */
@@ -101,6 +132,7 @@ export declare class SessionManager {
101
132
  * Clear all sessions
102
133
  */
103
134
  clearAll(): number;
135
+ markCancelledByContext(contextId: string): number;
104
136
  /**
105
137
  * Add message to session
106
138
  */