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.
- package/completions/_vigthoria +49 -0
- package/completions/vigthoria.bash +6 -0
- package/completions/vigthoria.fish +41 -0
- package/dist/commands/auth.d.ts +1 -0
- package/dist/commands/auth.js +84 -135
- package/dist/commands/background.d.ts +0 -2
- package/dist/commands/background.js +26 -34
- package/dist/commands/bridge.js +45 -41
- package/dist/commands/cancel.d.ts +1 -0
- package/dist/commands/cancel.js +22 -16
- package/dist/commands/chat.d.ts +5 -42
- package/dist/commands/chat.js +462 -1122
- package/dist/commands/coding-registration.d.ts +4 -0
- package/dist/commands/coding-registration.js +24 -0
- package/dist/commands/config.js +32 -42
- package/dist/commands/conversation-registration.d.ts +4 -0
- package/dist/commands/conversation-registration.js +122 -0
- package/dist/commands/deploy.d.ts +8 -2
- package/dist/commands/deploy.js +92 -90
- package/dist/commands/device.d.ts +1 -2
- package/dist/commands/device.js +37 -21
- package/dist/commands/doctor-registration.d.ts +4 -0
- package/dist/commands/doctor-registration.js +200 -0
- package/dist/commands/edit.js +22 -33
- package/dist/commands/explain.js +7 -10
- package/dist/commands/fork.d.ts +1 -2
- package/dist/commands/fork.js +72 -22
- package/dist/commands/game.d.ts +1 -1
- package/dist/commands/game.js +17 -6
- package/dist/commands/generate.js +7 -9
- package/dist/commands/history.js +11 -7
- package/dist/commands/hub.d.ts +2 -2
- package/dist/commands/hub.js +34 -35
- package/dist/commands/legion.d.ts +0 -1
- package/dist/commands/legion.js +62 -79
- package/dist/commands/music.d.ts +1 -2
- package/dist/commands/music.js +62 -27
- package/dist/commands/platform-registration.d.ts +4 -0
- package/dist/commands/platform-registration.js +45 -0
- package/dist/commands/preview.d.ts +0 -1
- package/dist/commands/preview.js +82 -35
- package/dist/commands/product-run-registration.d.ts +4 -0
- package/dist/commands/product-run-registration.js +490 -0
- package/dist/commands/replay.d.ts +1 -2
- package/dist/commands/replay.js +15 -11
- package/dist/commands/repo.d.ts +14 -7
- package/dist/commands/repo.js +245 -196
- package/dist/commands/review.js +9 -12
- package/dist/commands/security.d.ts +1 -2
- package/dist/commands/security.js +12 -6
- package/dist/commands/update-registration.d.ts +3 -0
- package/dist/commands/update-registration.js +338 -0
- package/dist/commands/v4-registration.d.ts +4 -0
- package/dist/commands/v4-registration.js +24 -0
- package/dist/commands/v4.d.ts +1 -1
- package/dist/commands/v4.js +46 -8
- package/dist/commands/wallet.d.ts +1 -3
- package/dist/commands/wallet.js +18 -45
- package/dist/commands/workflow.d.ts +2 -0
- package/dist/commands/workflow.js +52 -39
- package/dist/index.js +122 -1498
- package/dist/utils/agent-stream-state.d.ts +27 -0
- package/dist/utils/agent-stream-state.js +93 -0
- package/dist/utils/agentRoute.d.ts +2 -1
- package/dist/utils/agentRoute.js +22 -9
- package/dist/utils/agentRunOutcome.d.ts +16 -0
- package/dist/utils/agentRunOutcome.js +37 -2
- package/dist/utils/api-client-factory.d.ts +8 -0
- package/dist/utils/api-client-factory.js +8 -0
- package/dist/utils/api.d.ts +45 -269
- package/dist/utils/api.js +664 -3210
- package/dist/utils/auth-session.d.ts +41 -0
- package/dist/utils/auth-session.js +224 -0
- package/dist/utils/brain-hub-client.d.ts +0 -4
- package/dist/utils/brain-hub-client.js +6 -3
- package/dist/utils/bridge-client.js +2 -0
- package/dist/utils/capability-health-service.d.ts +48 -0
- package/dist/utils/capability-health-service.js +64 -0
- package/dist/utils/chat-prompt-policy.d.ts +18 -0
- package/dist/utils/chat-prompt-policy.js +61 -0
- package/dist/utils/chat-tool-call-parser.d.ts +11 -0
- package/dist/utils/chat-tool-call-parser.js +71 -0
- package/dist/utils/cli-state.d.ts +22 -4
- package/dist/utils/cli-state.js +105 -47
- package/dist/utils/code-operations-service.d.ts +104 -0
- package/dist/utils/code-operations-service.js +837 -0
- package/dist/utils/codebase-indexer.d.ts +1 -0
- package/dist/utils/codebase-indexer.js +28 -8
- package/dist/utils/command-contract.d.ts +61 -0
- package/dist/utils/command-contract.js +149 -0
- package/dist/utils/command-policy.d.ts +10 -0
- package/dist/utils/command-policy.js +54 -0
- package/dist/utils/config.d.ts +13 -5
- package/dist/utils/config.js +227 -99
- package/dist/utils/context-ranker.js +11 -2
- package/dist/utils/context-state.d.ts +39 -0
- package/dist/utils/context-state.js +52 -0
- package/dist/utils/deckEvents.d.ts +0 -5
- package/dist/utils/deckEvents.js +3 -2
- package/dist/utils/desktop-bridge-client.js +2 -0
- package/dist/utils/direct-output-policy.d.ts +3 -0
- package/dist/utils/direct-output-policy.js +76 -0
- package/dist/utils/durable-json.d.ts +57 -0
- package/dist/utils/durable-json.js +319 -0
- package/dist/utils/fastAgentRouter.d.ts +6 -7
- package/dist/utils/fastAgentRouter.js +21 -30
- package/dist/utils/files.js +21 -13
- package/dist/utils/frontend-preview-service.d.ts +76 -0
- package/dist/utils/frontend-preview-service.js +482 -0
- package/dist/utils/goaEvents.d.ts +0 -4
- package/dist/utils/goaEvents.js +5 -2
- package/dist/utils/logger.d.ts +2 -1
- package/dist/utils/logger.js +27 -20
- package/dist/utils/mcp-context-client.d.ts +24 -0
- package/dist/utils/mcp-context-client.js +58 -0
- package/dist/utils/model-governance.d.ts +18 -0
- package/dist/utils/model-governance.js +75 -0
- package/dist/utils/model-transport-service.d.ts +44 -0
- package/dist/utils/model-transport-service.js +111 -0
- package/dist/utils/mutation-journal.d.ts +42 -0
- package/dist/utils/mutation-journal.js +387 -0
- package/dist/utils/network-policy.d.ts +24 -0
- package/dist/utils/network-policy.js +202 -0
- package/dist/utils/operator-client.d.ts +45 -0
- package/dist/utils/operator-client.js +159 -0
- package/dist/utils/post-write-validator.js +7 -9
- package/dist/utils/preview-screenshot-adapter.d.ts +13 -0
- package/dist/utils/preview-screenshot-adapter.js +36 -0
- package/dist/utils/process-policy.d.ts +23 -0
- package/dist/utils/process-policy.js +284 -0
- package/dist/utils/project-memory.d.ts +13 -3
- package/dist/utils/project-memory.js +70 -17
- package/dist/utils/prompt-workspace-resolver.d.ts +14 -0
- package/dist/utils/prompt-workspace-resolver.js +64 -0
- package/dist/utils/release-install.d.ts +11 -0
- package/dist/utils/release-install.js +155 -0
- package/dist/utils/release-policy.d.ts +64 -0
- package/dist/utils/release-policy.js +293 -0
- package/dist/utils/requestIntent.js +22 -3
- package/dist/utils/runtime-capability.d.ts +8 -0
- package/dist/utils/runtime-capability.js +16 -0
- package/dist/utils/secret-policy.d.ts +20 -0
- package/dist/utils/secret-policy.js +160 -0
- package/dist/utils/session.d.ts +34 -2
- package/dist/utils/session.js +354 -110
- package/dist/utils/tool-approval-service.d.ts +17 -0
- package/dist/utils/tool-approval-service.js +78 -0
- package/dist/utils/tool-capability-providers.d.ts +57 -0
- package/dist/utils/tool-capability-providers.js +57 -0
- package/dist/utils/tools.d.ts +2 -23
- package/dist/utils/tools.js +216 -508
- package/dist/utils/update-policy.d.ts +18 -0
- package/dist/utils/update-policy.js +137 -0
- package/dist/utils/v3-agent-client.d.ts +40 -0
- package/dist/utils/v3-agent-client.js +165 -0
- package/dist/utils/v3-workspace-service.d.ts +28 -0
- package/dist/utils/v3-workspace-service.js +279 -0
- package/dist/utils/vigflow-client.d.ts +95 -0
- package/dist/utils/vigflow-client.js +105 -0
- package/dist/utils/workspace-boundary.d.ts +45 -0
- package/dist/utils/workspace-boundary.js +377 -0
- package/dist/utils/workspace-brain-service.d.ts +2 -0
- package/dist/utils/workspace-brain-service.js +11 -1
- package/dist/utils/workspace-cache.js +37 -15
- package/dist/utils/workspace-stream.js +35 -10
- package/install.ps1 +86 -143
- package/install.sh +76 -81
- package/package.json +44 -9
- package/release-policy.json +43 -0
- package/scripts/release/LOCAL_MACHINE_USER_VERIFICATION.md +1 -1
- package/scripts/release/generate-completions.mjs +24 -0
- package/scripts/release/install-release.mjs +10 -0
- package/scripts/release/publish-cli-release.mjs +116 -0
- package/scripts/release/publish-cli-release.sh +2 -70
- package/scripts/release/resolve-release-manifest.mjs +26 -0
- package/scripts/release/test-balanced-model-live.sh +42 -0
- package/scripts/release/validate-live-service-gates.sh +90 -0
- package/scripts/release/validate-no-go-gates.sh +135 -172
- package/scripts/release/verify-runtime-consistency.mjs +4 -1
- package/dist/commands/index.d.ts +0 -12
- package/dist/commands/index.js +0 -194
- package/dist/commands/update.d.ts +0 -9
- package/dist/commands/update.js +0 -301
- package/dist/utils/templateInstantPath.d.ts +0 -18
- package/dist/utils/templateInstantPath.js +0 -228
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { scanOutboundContext } from './secret-policy.js';
|
|
2
|
+
export class OperatorClientError extends Error {
|
|
3
|
+
category;
|
|
4
|
+
constructor(message, category) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.category = category;
|
|
7
|
+
this.name = 'OperatorClientError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class OperatorClient {
|
|
11
|
+
dependencies;
|
|
12
|
+
constructor(dependencies) {
|
|
13
|
+
this.dependencies = dependencies;
|
|
14
|
+
}
|
|
15
|
+
formatResponse(data = {}) {
|
|
16
|
+
if (typeof data.answer === 'string' && data.answer.trim())
|
|
17
|
+
return data.answer.trim();
|
|
18
|
+
if (typeof data.result === 'string' && data.result.trim())
|
|
19
|
+
return data.result.trim();
|
|
20
|
+
const lines = [];
|
|
21
|
+
if (data.summary)
|
|
22
|
+
lines.push(String(data.summary).trim());
|
|
23
|
+
if (Array.isArray(data.completed_agents) && data.completed_agents.length > 0) {
|
|
24
|
+
lines.push(`Completed agents: ${data.completed_agents.join(', ')}`);
|
|
25
|
+
}
|
|
26
|
+
if (data.details) {
|
|
27
|
+
const detail = JSON.stringify(data.details, null, 2);
|
|
28
|
+
if (detail && detail !== '{}')
|
|
29
|
+
lines.push(detail.length > 12_000 ? `${detail.slice(0, 12_000)}\n\n[Operator output truncated]` : detail);
|
|
30
|
+
}
|
|
31
|
+
return lines.filter(Boolean).join('\n\n').trim() || 'Operator workflow completed.';
|
|
32
|
+
}
|
|
33
|
+
async run(input) {
|
|
34
|
+
const errors = [];
|
|
35
|
+
for (const baseUrl of this.dependencies.getBaseUrls()) {
|
|
36
|
+
const controller = new AbortController();
|
|
37
|
+
const timeoutId = input.timeoutMs > 0 ? setTimeout(() => controller.abort(), input.timeoutMs) : null;
|
|
38
|
+
try {
|
|
39
|
+
const context = input.executionContext;
|
|
40
|
+
const body = scanOutboundContext({
|
|
41
|
+
prompt: input.message,
|
|
42
|
+
task: input.message,
|
|
43
|
+
raw_prompt: context.rawPrompt || null,
|
|
44
|
+
model: input.requestedModel,
|
|
45
|
+
preferred_model: input.resolvedModel,
|
|
46
|
+
context_id: context.contextId,
|
|
47
|
+
trace_id: context.traceId,
|
|
48
|
+
mcp_context_id: context.mcpContextId || null,
|
|
49
|
+
context: {
|
|
50
|
+
workspace: { path: input.workspaceAlias },
|
|
51
|
+
workspace_path: input.workspaceAlias,
|
|
52
|
+
workspace_summary: input.workspaceSummary,
|
|
53
|
+
model: input.resolvedModel,
|
|
54
|
+
history: context.history || [],
|
|
55
|
+
executionSurface: context.executionSurface || 'cli',
|
|
56
|
+
clientSurface: context.clientSurface || 'cli',
|
|
57
|
+
contextId: context.contextId,
|
|
58
|
+
traceId: context.traceId,
|
|
59
|
+
mcpContextId: context.mcpContextId || null,
|
|
60
|
+
mcp_context_id: context.mcpContextId || null,
|
|
61
|
+
rawPrompt: context.rawPrompt || null,
|
|
62
|
+
requestStartedAt: context.requestStartedAt,
|
|
63
|
+
vigthoriaBrain: context.vigthoriaBrain || null,
|
|
64
|
+
vigthoria_brain: context.vigthoriaBrain || null,
|
|
65
|
+
},
|
|
66
|
+
workflow_type: context.workflowType || 'full',
|
|
67
|
+
options: { stream: true, save_to_vigflow: context.savePlanToVigFlow === true },
|
|
68
|
+
}).value;
|
|
69
|
+
const token = this.dependencies.getAuthToken();
|
|
70
|
+
const response = await this.dependencies.fetch(`${baseUrl}/api/operator/stream`, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'Content-Type': 'application/json', Accept: 'text/event-stream',
|
|
74
|
+
...(context.mcpContextId ? { 'X-MCP-Context-Id': String(context.mcpContextId) } : {}),
|
|
75
|
+
...(token ? { Authorization: `Bearer ${token}`, Cookie: `vigthoria-auth-token=${token}` } : {}),
|
|
76
|
+
},
|
|
77
|
+
body: JSON.stringify(body), signal: controller.signal,
|
|
78
|
+
});
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
const detail = await response.text().catch(() => '');
|
|
81
|
+
throw new Error(`Operator stream ${response.status}: ${this.dependencies.sanitizeError(detail).slice(0, 200)}`);
|
|
82
|
+
}
|
|
83
|
+
const contentType = response.headers.get('content-type')?.toLowerCase() || '';
|
|
84
|
+
const isEventStream = contentType.includes('text/event-stream');
|
|
85
|
+
if (!isEventStream || !response.body || typeof response.body.getReader !== 'function') {
|
|
86
|
+
const payload = await response.json();
|
|
87
|
+
const contextId = payload.context_id || response.headers.get('x-context-id') || context.contextId || null;
|
|
88
|
+
const mcpContextId = payload.mcp_context_id || response.headers.get('x-mcp-context-id') || context.mcpContextId || null;
|
|
89
|
+
return { content: this.formatResponse(payload), workflowId: payload.workflow_id || null, contextId, backendUrl: baseUrl,
|
|
90
|
+
savedWorkflow: payload.saved_workflow || null, metadata: { source: 'operator', mode: 'operator', contextId, mcpContextId } };
|
|
91
|
+
}
|
|
92
|
+
const reader = response.body.getReader();
|
|
93
|
+
const decoder = new TextDecoder();
|
|
94
|
+
let buffer = '';
|
|
95
|
+
let workflowId = null;
|
|
96
|
+
let contextId = response.headers.get('x-context-id') || context.contextId || null;
|
|
97
|
+
let mcpContextId = response.headers.get('x-mcp-context-id') || context.mcpContextId || null;
|
|
98
|
+
let result = null;
|
|
99
|
+
let savedWorkflow = null;
|
|
100
|
+
while (true) {
|
|
101
|
+
const { done, value } = await reader.read();
|
|
102
|
+
if (done)
|
|
103
|
+
break;
|
|
104
|
+
buffer += decoder.decode(value, { stream: true });
|
|
105
|
+
const chunks = buffer.split('\n\n');
|
|
106
|
+
buffer = chunks.pop() || '';
|
|
107
|
+
for (const chunk of chunks) {
|
|
108
|
+
let eventName = 'message';
|
|
109
|
+
const dataLines = [];
|
|
110
|
+
for (const line of chunk.split('\n')) {
|
|
111
|
+
if (line.startsWith('event:'))
|
|
112
|
+
eventName = line.slice(6).trim();
|
|
113
|
+
else if (line.startsWith('data:'))
|
|
114
|
+
dataLines.push(line.slice(5).trim());
|
|
115
|
+
}
|
|
116
|
+
if (dataLines.length === 0)
|
|
117
|
+
continue;
|
|
118
|
+
let payload;
|
|
119
|
+
try {
|
|
120
|
+
payload = JSON.parse(dataLines.join('\n'));
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
payload = { raw: dataLines.join('\n') };
|
|
124
|
+
}
|
|
125
|
+
if (payload.workflow_id && !workflowId)
|
|
126
|
+
workflowId = payload.workflow_id;
|
|
127
|
+
if (!contextId && typeof payload.context_id === 'string' && payload.context_id.trim())
|
|
128
|
+
contextId = payload.context_id.trim();
|
|
129
|
+
if (!mcpContextId && typeof payload.mcp_context_id === 'string' && payload.mcp_context_id.trim())
|
|
130
|
+
mcpContextId = payload.mcp_context_id.trim();
|
|
131
|
+
if (!savedWorkflow && payload.saved_workflow && typeof payload.saved_workflow === 'object')
|
|
132
|
+
savedWorkflow = payload.saved_workflow;
|
|
133
|
+
try {
|
|
134
|
+
input.onStreamEvent?.({ type: eventName, ...payload, context_id: payload.context_id || contextId, mcp_context_id: payload.mcp_context_id || mcpContextId });
|
|
135
|
+
}
|
|
136
|
+
catch { }
|
|
137
|
+
if (eventName === 'result')
|
|
138
|
+
result = payload;
|
|
139
|
+
if (eventName === 'error')
|
|
140
|
+
throw new Error(payload.error || payload.message || 'Operator workflow failed');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return { content: this.formatResponse(result || {}), workflowId, contextId, backendUrl: baseUrl, savedWorkflow,
|
|
144
|
+
metadata: { source: 'operator', mode: 'operator', contextId, mcpContextId, savedWorkflowId: savedWorkflow?.id || null } };
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (error?.name === 'AbortError' || error?.code === 'ABORT_ERR') {
|
|
148
|
+
throw new OperatorClientError(`Operator workflow timed out after ${Math.round(input.timeoutMs / 60_000)} minute(s). You can increase the timeout with VIGTHORIA_OPERATOR_TIMEOUT_MS.`, 'timeout');
|
|
149
|
+
}
|
|
150
|
+
errors.push(`${baseUrl}: ${error?.message || String(error)}`);
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
if (timeoutId)
|
|
154
|
+
clearTimeout(timeoutId);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
throw new OperatorClientError(`Operator workflow failed on all endpoints: ${errors.join(' | ')}`, 'model_backend');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
import { execFileSync } from 'node:child_process';
|
|
12
12
|
import fs from 'node:fs';
|
|
13
13
|
import path from 'node:path';
|
|
14
|
+
import { safeChildProcessEnv } from './secret-policy.js';
|
|
14
15
|
function commandExists(cmd, args = ['--version']) {
|
|
15
16
|
try {
|
|
16
|
-
execFileSync(cmd, args, { stdio: 'pipe', timeout: 4000, windowsHide: true });
|
|
17
|
+
execFileSync(cmd, args, { stdio: 'pipe', timeout: 4000, windowsHide: true, env: safeChildProcessEnv() });
|
|
17
18
|
return true;
|
|
18
19
|
}
|
|
19
20
|
catch {
|
|
@@ -25,22 +26,19 @@ function hasTsConfig(dir) {
|
|
|
25
26
|
}
|
|
26
27
|
/**
|
|
27
28
|
* Resolve the best available tsc binary for the project.
|
|
28
|
-
* Prefers local node_modules/.bin/tsc
|
|
29
|
+
* Prefers local node_modules/.bin/tsc, then an already installed global tsc.
|
|
30
|
+
* It deliberately has no npx fallback: npx may fetch and execute a package,
|
|
31
|
+
* which would violate offline mode and the process approval boundary.
|
|
29
32
|
*/
|
|
30
33
|
function resolveTscBin(workspacePath) {
|
|
31
34
|
const localTsc = path.join(workspacePath, 'node_modules', '.bin', 'tsc');
|
|
32
35
|
if (fs.existsSync(localTsc))
|
|
33
36
|
return { cmd: localTsc, args: ["--noEmit", "--skipLibCheck"] };
|
|
34
37
|
try {
|
|
35
|
-
execFileSync("tsc", ["--version"], { stdio: "pipe", timeout: 4000, windowsHide: true });
|
|
38
|
+
execFileSync("tsc", ["--version"], { stdio: "pipe", timeout: 4000, windowsHide: true, env: safeChildProcessEnv() });
|
|
36
39
|
return { cmd: "tsc", args: ["--noEmit", "--skipLibCheck"] };
|
|
37
40
|
}
|
|
38
41
|
catch { /* tsc not available */ }
|
|
39
|
-
try {
|
|
40
|
-
execFileSync("npx", ["--version"], { stdio: "pipe", timeout: 4000, windowsHide: true });
|
|
41
|
-
return { cmd: "npx", args: ["--yes", "tsc", "--noEmit", "--skipLibCheck"] };
|
|
42
|
-
}
|
|
43
|
-
catch { /* npx not available */ }
|
|
44
42
|
return null;
|
|
45
43
|
}
|
|
46
44
|
function hasRealTestScript(dir) {
|
|
@@ -65,7 +63,7 @@ export async function runPostWriteValidation(workspacePath) {
|
|
|
65
63
|
if (!workspacePath || !fs.existsSync(workspacePath))
|
|
66
64
|
return [];
|
|
67
65
|
const results = [];
|
|
68
|
-
const opts = { cwd: workspacePath, stdio: 'pipe', windowsHide: true };
|
|
66
|
+
const opts = { cwd: workspacePath, stdio: 'pipe', windowsHide: true, env: safeChildProcessEnv() };
|
|
69
67
|
// 1. TypeScript type check
|
|
70
68
|
const tscBin = hasTsConfig(workspacePath) ? resolveTscBin(workspacePath) : null;
|
|
71
69
|
if (tscBin) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type PreviewScreenshotResult = {
|
|
2
|
+
captured: boolean;
|
|
3
|
+
error?: string;
|
|
4
|
+
};
|
|
5
|
+
export interface PreviewScreenshotPort {
|
|
6
|
+
capture(entryAbsolutePath: string, screenshotPath: string): Promise<PreviewScreenshotResult>;
|
|
7
|
+
}
|
|
8
|
+
export declare class OptionalPuppeteerScreenshotAdapter implements PreviewScreenshotPort {
|
|
9
|
+
private readonly loadPuppeteer;
|
|
10
|
+
private readonly environment;
|
|
11
|
+
constructor(loadPuppeteer?: () => Promise<unknown>, environment?: NodeJS.ProcessEnv);
|
|
12
|
+
capture(entryAbsolutePath: string, screenshotPath: string): Promise<PreviewScreenshotResult>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { pathToFileURL } from 'node:url';
|
|
2
|
+
import { redactSensitiveText } from './secret-policy.js';
|
|
3
|
+
export class OptionalPuppeteerScreenshotAdapter {
|
|
4
|
+
loadPuppeteer;
|
|
5
|
+
environment;
|
|
6
|
+
constructor(loadPuppeteer = () => import('puppeteer'), environment = process.env) {
|
|
7
|
+
this.loadPuppeteer = loadPuppeteer;
|
|
8
|
+
this.environment = environment;
|
|
9
|
+
}
|
|
10
|
+
async capture(entryAbsolutePath, screenshotPath) {
|
|
11
|
+
if (this.environment.VIGTHORIA_DISABLE_PREVIEW_SCREENSHOT === '1') {
|
|
12
|
+
return { captured: false, error: 'screenshot capture explicitly unavailable' };
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const loaded = await this.loadPuppeteer().catch(() => null);
|
|
16
|
+
const puppeteer = (loaded?.default || loaded);
|
|
17
|
+
if (!puppeteer || typeof puppeteer.launch !== 'function') {
|
|
18
|
+
return { captured: false, error: 'puppeteer optional adapter is not installed' };
|
|
19
|
+
}
|
|
20
|
+
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] });
|
|
21
|
+
try {
|
|
22
|
+
const page = await browser.newPage();
|
|
23
|
+
await page.setViewport({ width: 1440, height: 960, deviceScaleFactor: 1 });
|
|
24
|
+
await page.goto(pathToFileURL(entryAbsolutePath).toString(), { waitUntil: 'networkidle0', timeout: 20_000 });
|
|
25
|
+
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
await browser.close();
|
|
29
|
+
}
|
|
30
|
+
return { captured: true };
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
return { captured: false, error: redactSensitiveText(error instanceof Error ? error.message : String(error)) };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare class ProcessPolicyError extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
constructor(message: string, code?: string);
|
|
4
|
+
}
|
|
5
|
+
export interface ParsedProcess {
|
|
6
|
+
executable: string;
|
|
7
|
+
args: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface ApprovalScope {
|
|
10
|
+
key: string;
|
|
11
|
+
tool: string;
|
|
12
|
+
workspace: string;
|
|
13
|
+
destination: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function assertProcessNetworkAllowed(executable: string, args: readonly string[]): void;
|
|
16
|
+
/** Parse quoting only. No shell expansion, substitution, redirection, or operators. */
|
|
17
|
+
export declare function parseArgumentVector(command: string): string[];
|
|
18
|
+
export declare function parseSupportedProcess(command: string): ParsedProcess;
|
|
19
|
+
export declare function parseGitArguments(value: string): string[];
|
|
20
|
+
export declare function hardenGitArguments(args: readonly string[]): string[];
|
|
21
|
+
export declare function safeGitEnvironment(source?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
22
|
+
export declare function resolveAutoApproval(explicitAutoApprove: unknown, _jsonMode: unknown): boolean;
|
|
23
|
+
export declare function buildApprovalScope(tool: string, args: Record<string, string>, workspacePath: string): ApprovalScope;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { assertContainedAbsolutePath } from './workspace-boundary.js';
|
|
4
|
+
import { isOfflineNetworkMode } from './network-policy.js';
|
|
5
|
+
import { safeChildProcessEnv } from './secret-policy.js';
|
|
6
|
+
export class ProcessPolicyError extends Error {
|
|
7
|
+
code;
|
|
8
|
+
constructor(message, code = 'PROCESS_POLICY_VIOLATION') {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.name = 'ProcessPolicyError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const SUPPORTED_EXECUTABLES = new Map([
|
|
15
|
+
['node', process.execPath],
|
|
16
|
+
['npm', process.platform === 'win32' ? 'npm.cmd' : 'npm'],
|
|
17
|
+
['npm.cmd', 'npm.cmd'],
|
|
18
|
+
['pnpm', process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm'],
|
|
19
|
+
['pnpm.cmd', 'pnpm.cmd'],
|
|
20
|
+
['yarn', process.platform === 'win32' ? 'yarn.cmd' : 'yarn'],
|
|
21
|
+
['yarn.cmd', 'yarn.cmd'],
|
|
22
|
+
['bun', process.platform === 'win32' ? 'bun.exe' : 'bun'],
|
|
23
|
+
['bun.exe', 'bun.exe'],
|
|
24
|
+
['tsc', process.platform === 'win32' ? 'tsc.cmd' : 'tsc'],
|
|
25
|
+
['tsc.cmd', 'tsc.cmd'],
|
|
26
|
+
['eslint', process.platform === 'win32' ? 'eslint.cmd' : 'eslint'],
|
|
27
|
+
['eslint.cmd', 'eslint.cmd'],
|
|
28
|
+
['prettier', process.platform === 'win32' ? 'prettier.cmd' : 'prettier'],
|
|
29
|
+
['prettier.cmd', 'prettier.cmd'],
|
|
30
|
+
['python', 'python'],
|
|
31
|
+
['python.exe', 'python.exe'],
|
|
32
|
+
['python3', 'python3'],
|
|
33
|
+
['pytest', process.platform === 'win32' ? 'pytest.exe' : 'pytest'],
|
|
34
|
+
['pytest.exe', 'pytest.exe'],
|
|
35
|
+
['go', process.platform === 'win32' ? 'go.exe' : 'go'],
|
|
36
|
+
['go.exe', 'go.exe'],
|
|
37
|
+
['cargo', process.platform === 'win32' ? 'cargo.exe' : 'cargo'],
|
|
38
|
+
['cargo.exe', 'cargo.exe'],
|
|
39
|
+
['rustc', process.platform === 'win32' ? 'rustc.exe' : 'rustc'],
|
|
40
|
+
['rustc.exe', 'rustc.exe'],
|
|
41
|
+
['make', process.platform === 'win32' ? 'make.exe' : 'make'],
|
|
42
|
+
['make.exe', 'make.exe'],
|
|
43
|
+
['cmake', process.platform === 'win32' ? 'cmake.exe' : 'cmake'],
|
|
44
|
+
['cmake.exe', 'cmake.exe'],
|
|
45
|
+
['rg', process.platform === 'win32' ? 'rg.exe' : 'rg'],
|
|
46
|
+
['rg.exe', 'rg.exe'],
|
|
47
|
+
]);
|
|
48
|
+
const ALLOWED_GIT_SUBCOMMANDS = new Set([
|
|
49
|
+
'add', 'blame', 'branch', 'checkout', 'commit', 'describe', 'diff', 'diff-tree',
|
|
50
|
+
'for-each-ref', 'grep', 'log', 'ls-files', 'ls-tree', 'merge-base', 'mv',
|
|
51
|
+
'name-rev', 'reset', 'restore', 'rev-list', 'rev-parse', 'rm', 'show',
|
|
52
|
+
'show-ref', 'status', 'switch', 'tag', 'update-index',
|
|
53
|
+
]);
|
|
54
|
+
const NETWORK_PACKAGE_ACTIONS = {
|
|
55
|
+
npm: new Set(['add', 'ci', 'exec', 'i', 'install', 'publish', 'update', 'view']),
|
|
56
|
+
'npm.cmd': new Set(['add', 'ci', 'exec', 'i', 'install', 'publish', 'update', 'view']),
|
|
57
|
+
pnpm: new Set(['add', 'dlx', 'fetch', 'i', 'install', 'publish', 'update']),
|
|
58
|
+
'pnpm.cmd': new Set(['add', 'dlx', 'fetch', 'i', 'install', 'publish', 'update']),
|
|
59
|
+
yarn: new Set(['add', 'dlx', 'install', 'publish', 'upgrade']),
|
|
60
|
+
'yarn.cmd': new Set(['add', 'dlx', 'install', 'publish', 'upgrade']),
|
|
61
|
+
bun: new Set(['add', 'install', 'publish', 'update', 'x']),
|
|
62
|
+
'bun.exe': new Set(['add', 'install', 'publish', 'update', 'x']),
|
|
63
|
+
cargo: new Set(['fetch', 'install', 'publish', 'update']),
|
|
64
|
+
'cargo.exe': new Set(['fetch', 'install', 'publish', 'update']),
|
|
65
|
+
go: new Set(['get', 'install']),
|
|
66
|
+
'go.exe': new Set(['get', 'install']),
|
|
67
|
+
};
|
|
68
|
+
const ALWAYS_NETWORK_EXECUTABLES = new Set([
|
|
69
|
+
'curl', 'curl.exe', 'scp', 'scp.exe', 'ssh', 'ssh.exe', 'wget', 'wget.exe',
|
|
70
|
+
]);
|
|
71
|
+
export function assertProcessNetworkAllowed(executable, args) {
|
|
72
|
+
if (!isOfflineNetworkMode())
|
|
73
|
+
return;
|
|
74
|
+
const base = path.basename(executable).toLowerCase();
|
|
75
|
+
if (ALWAYS_NETWORK_EXECUTABLES.has(base)) {
|
|
76
|
+
throw new ProcessPolicyError(`Offline mode prohibits network process: ${base}`, 'OFFLINE_NETWORK_PROCESS');
|
|
77
|
+
}
|
|
78
|
+
const action = String(args[0] || '').toLowerCase();
|
|
79
|
+
if (NETWORK_PACKAGE_ACTIONS[base]?.has(action)) {
|
|
80
|
+
throw new ProcessPolicyError(`Offline mode prohibits network-capable process: ${base} ${action}`, 'OFFLINE_NETWORK_PROCESS');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Parse quoting only. No shell expansion, substitution, redirection, or operators. */
|
|
84
|
+
export function parseArgumentVector(command) {
|
|
85
|
+
const input = String(command || '').trim();
|
|
86
|
+
if (!input || /[\0\r\n]/.test(input)) {
|
|
87
|
+
throw new ProcessPolicyError('Command is empty or contains control characters', 'INVALID_COMMAND');
|
|
88
|
+
}
|
|
89
|
+
if (input.includes('`') || input.includes('$(')) {
|
|
90
|
+
throw new ProcessPolicyError('Command substitution is not supported', 'SHELL_SYNTAX_REJECTED');
|
|
91
|
+
}
|
|
92
|
+
const tokens = [];
|
|
93
|
+
let token = '';
|
|
94
|
+
let quote = null;
|
|
95
|
+
let escaping = false;
|
|
96
|
+
let tokenStarted = false;
|
|
97
|
+
for (const character of input) {
|
|
98
|
+
if (escaping) {
|
|
99
|
+
token += character;
|
|
100
|
+
tokenStarted = true;
|
|
101
|
+
escaping = false;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (character === '\\' && quote !== "'") {
|
|
105
|
+
escaping = true;
|
|
106
|
+
tokenStarted = true;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (quote) {
|
|
110
|
+
if (character === quote)
|
|
111
|
+
quote = null;
|
|
112
|
+
else
|
|
113
|
+
token += character;
|
|
114
|
+
tokenStarted = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (character === "'" || character === '"') {
|
|
118
|
+
quote = character;
|
|
119
|
+
tokenStarted = true;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (/\s/.test(character)) {
|
|
123
|
+
if (tokenStarted)
|
|
124
|
+
tokens.push(token);
|
|
125
|
+
token = '';
|
|
126
|
+
tokenStarted = false;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (';&|<>'.includes(character)) {
|
|
130
|
+
throw new ProcessPolicyError('Shell operators and redirections are not supported', 'SHELL_SYNTAX_REJECTED');
|
|
131
|
+
}
|
|
132
|
+
token += character;
|
|
133
|
+
tokenStarted = true;
|
|
134
|
+
}
|
|
135
|
+
if (escaping || quote) {
|
|
136
|
+
throw new ProcessPolicyError('Command contains an unfinished escape or quote', 'INVALID_COMMAND');
|
|
137
|
+
}
|
|
138
|
+
if (tokenStarted)
|
|
139
|
+
tokens.push(token);
|
|
140
|
+
if (tokens.length === 0)
|
|
141
|
+
throw new ProcessPolicyError('Command has no executable', 'INVALID_COMMAND');
|
|
142
|
+
return tokens;
|
|
143
|
+
}
|
|
144
|
+
function rejectUnsafeProcessArguments(executable, args) {
|
|
145
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(executable)) {
|
|
146
|
+
throw new ProcessPolicyError('Environment assignment prefixes are not supported', 'UNSUPPORTED_EXECUTABLE');
|
|
147
|
+
}
|
|
148
|
+
for (const arg of args) {
|
|
149
|
+
const slashArg = arg.replace(/\\/g, '/');
|
|
150
|
+
if (path.isAbsolute(arg)
|
|
151
|
+
|| /^[a-zA-Z]:\//.test(slashArg)
|
|
152
|
+
|| slashArg.startsWith('//')
|
|
153
|
+
|| /(?:^|[=/])\.\.(?:\/|$)/.test(slashArg)
|
|
154
|
+
|| /(?:^|=)(?:\/|[a-zA-Z]:\/)/.test(slashArg)
|
|
155
|
+
|| /^file:/i.test(arg)) {
|
|
156
|
+
throw new ProcessPolicyError(`Process argument escapes the workspace contract: ${arg}`, 'UNSAFE_PROCESS_ARGUMENT');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const base = path.basename(executable).toLowerCase();
|
|
160
|
+
if (base === 'node' && args.some((arg) => ['-e', '--eval', '-p', '--print', '-r', '--require', '--import'].includes(arg))) {
|
|
161
|
+
throw new ProcessPolicyError('Node evaluation and preload flags are not supported', 'UNSAFE_PROCESS_ARGUMENT');
|
|
162
|
+
}
|
|
163
|
+
if ((base === 'python' || base === 'python.exe' || base === 'python3') && args.some((arg) => arg === '-c')) {
|
|
164
|
+
throw new ProcessPolicyError('Python command evaluation is not supported', 'UNSAFE_PROCESS_ARGUMENT');
|
|
165
|
+
}
|
|
166
|
+
if ((base === 'npm' || base === 'npm.cmd') && ['exec', 'publish'].includes(String(args[0] || '').toLowerCase())) {
|
|
167
|
+
throw new ProcessPolicyError(`npm ${args[0]} is not supported`, 'UNSAFE_PROCESS_ARGUMENT');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
export function parseSupportedProcess(command) {
|
|
171
|
+
const tokens = parseArgumentVector(command);
|
|
172
|
+
const requested = path.basename(tokens[0]).toLowerCase();
|
|
173
|
+
const executable = SUPPORTED_EXECUTABLES.get(requested);
|
|
174
|
+
if (!executable) {
|
|
175
|
+
throw new ProcessPolicyError(`Unsupported executable: ${tokens[0]}`, 'UNSUPPORTED_EXECUTABLE');
|
|
176
|
+
}
|
|
177
|
+
const args = tokens.slice(1);
|
|
178
|
+
rejectUnsafeProcessArguments(requested, args);
|
|
179
|
+
assertProcessNetworkAllowed(requested, args);
|
|
180
|
+
return { executable, args };
|
|
181
|
+
}
|
|
182
|
+
export function parseGitArguments(value) {
|
|
183
|
+
const args = parseArgumentVector(value);
|
|
184
|
+
if (args[0]?.toLowerCase() === 'git')
|
|
185
|
+
args.shift();
|
|
186
|
+
if (args.length === 0)
|
|
187
|
+
throw new ProcessPolicyError('Git subcommand is required', 'INVALID_GIT_ARGUMENTS');
|
|
188
|
+
const forbidden = /^(?:-c|--config-env|--exec-path|--git-dir|--work-tree|--namespace|--upload-pack|--receive-pack|--extcmd|--helper)(?:=|$)/i;
|
|
189
|
+
if (args.some((arg) => forbidden.test(arg))) {
|
|
190
|
+
throw new ProcessPolicyError('Git configuration, helper, and alternate-worktree injection flags are not supported', 'UNSAFE_GIT_ARGUMENT');
|
|
191
|
+
}
|
|
192
|
+
if (['difftool', 'mergetool', 'credential', 'daemon', 'shell'].includes(args[0].toLowerCase()) || args.some((arg) => /^ext::/i.test(arg))) {
|
|
193
|
+
throw new ProcessPolicyError('Git helper-execution subcommands and ext transports are not supported', 'UNSAFE_GIT_ARGUMENT');
|
|
194
|
+
}
|
|
195
|
+
if (!ALLOWED_GIT_SUBCOMMANDS.has(args[0].toLowerCase())) {
|
|
196
|
+
throw new ProcessPolicyError(`Unsupported Git subcommand: ${args[0]}`, 'UNSUPPORTED_GIT_SUBCOMMAND');
|
|
197
|
+
}
|
|
198
|
+
rejectUnsafeProcessArguments('git', args);
|
|
199
|
+
return args;
|
|
200
|
+
}
|
|
201
|
+
export function hardenGitArguments(args) {
|
|
202
|
+
const nullDevice = process.platform === 'win32' ? 'NUL' : '/dev/null';
|
|
203
|
+
const hardened = [
|
|
204
|
+
'-c', `core.hooksPath=${nullDevice}`,
|
|
205
|
+
'-c', 'core.fsmonitor=false',
|
|
206
|
+
'-c', 'diff.external=',
|
|
207
|
+
...args,
|
|
208
|
+
];
|
|
209
|
+
const subcommandIndex = 6;
|
|
210
|
+
const subcommand = String(hardened[subcommandIndex] || '').toLowerCase();
|
|
211
|
+
if (['diff', 'log', 'show'].includes(subcommand)) {
|
|
212
|
+
hardened.splice(subcommandIndex + 1, 0, '--no-ext-diff', '--no-textconv');
|
|
213
|
+
}
|
|
214
|
+
return hardened;
|
|
215
|
+
}
|
|
216
|
+
export function safeGitEnvironment(source = process.env) {
|
|
217
|
+
const nullDevice = process.platform === 'win32' ? 'NUL' : '/dev/null';
|
|
218
|
+
return {
|
|
219
|
+
...safeChildProcessEnv(source),
|
|
220
|
+
GIT_CONFIG_NOSYSTEM: '1',
|
|
221
|
+
GIT_CONFIG_GLOBAL: nullDevice,
|
|
222
|
+
GIT_PAGER: 'cat',
|
|
223
|
+
PAGER: 'cat',
|
|
224
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
export function resolveAutoApproval(explicitAutoApprove, _jsonMode) {
|
|
228
|
+
return explicitAutoApprove === true;
|
|
229
|
+
}
|
|
230
|
+
function canonicalArgs(tool, args) {
|
|
231
|
+
const normalized = {};
|
|
232
|
+
for (const key of Object.keys(args).sort())
|
|
233
|
+
normalized[key] = String(args[key]).trim();
|
|
234
|
+
if (tool === 'bash' && args.command) {
|
|
235
|
+
const process = parseSupportedProcess(args.command);
|
|
236
|
+
normalized.command = [process.executable, ...process.args];
|
|
237
|
+
}
|
|
238
|
+
if (tool === 'git' && args.args)
|
|
239
|
+
normalized.args = parseGitArguments(args.args);
|
|
240
|
+
return normalized;
|
|
241
|
+
}
|
|
242
|
+
function approvalDestination(tool, args, workspace) {
|
|
243
|
+
for (const key of ['path', 'cwd', 'working_dir']) {
|
|
244
|
+
if (!args[key])
|
|
245
|
+
continue;
|
|
246
|
+
try {
|
|
247
|
+
return assertContainedAbsolutePath(workspace, path.isAbsolute(args[key]) ? args[key] : path.join(workspace, args[key]), {
|
|
248
|
+
allowMissing: true,
|
|
249
|
+
allowRoot: true,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return `rejected:${key}`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (args.url) {
|
|
257
|
+
try {
|
|
258
|
+
return new URL(args.url).origin;
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return 'invalid-url';
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (tool === 'ssh_exec')
|
|
265
|
+
return `ssh:${String(args.host || '').toLowerCase()}`;
|
|
266
|
+
if (tool === 'repo')
|
|
267
|
+
return `repo:${args.action || ''}:${args.project || ''}:${args.username || ''}`;
|
|
268
|
+
if (tool === 'git') {
|
|
269
|
+
const gitArgs = parseGitArguments(args.args || '');
|
|
270
|
+
return `git:${gitArgs[0]}:${gitArgs.find((arg, index) => index > 0 && !arg.startsWith('-')) || ''}`;
|
|
271
|
+
}
|
|
272
|
+
return workspace;
|
|
273
|
+
}
|
|
274
|
+
export function buildApprovalScope(tool, args, workspacePath) {
|
|
275
|
+
const workspace = path.resolve(workspacePath);
|
|
276
|
+
const destination = approvalDestination(tool, args, workspace);
|
|
277
|
+
const payload = JSON.stringify({ tool, args: canonicalArgs(tool, args), workspace, destination });
|
|
278
|
+
return {
|
|
279
|
+
key: createHash('sha256').update(payload).digest('hex'),
|
|
280
|
+
tool,
|
|
281
|
+
workspace,
|
|
282
|
+
destination,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* compact facts that can be retrieved for follow-up prompts without replaying
|
|
6
6
|
* full chat history or noisy tool traces.
|
|
7
7
|
*/
|
|
8
|
-
import { ChatMessage } from './api.js';
|
|
8
|
+
import type { ChatMessage } from './api.js';
|
|
9
9
|
export type ProjectMemoryType = 'fact' | 'decision' | 'task' | 'file' | 'preference' | 'validation' | 'issue';
|
|
10
10
|
export interface ProjectMemoryItem {
|
|
11
11
|
id: string;
|
|
@@ -18,11 +18,15 @@ export interface ProjectMemoryItem {
|
|
|
18
18
|
weight: number;
|
|
19
19
|
}
|
|
20
20
|
export interface ProjectMemoryBrain {
|
|
21
|
-
version:
|
|
21
|
+
version: 2;
|
|
22
22
|
workspaceName: string;
|
|
23
23
|
updatedAt: string;
|
|
24
|
+
privacyMode: 'local-only';
|
|
24
25
|
items: ProjectMemoryItem[];
|
|
25
26
|
}
|
|
27
|
+
interface ProjectMemoryOptions {
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
}
|
|
26
30
|
interface RememberOptions {
|
|
27
31
|
source?: string;
|
|
28
32
|
mode?: string;
|
|
@@ -33,9 +37,12 @@ export declare class ProjectMemoryService {
|
|
|
33
37
|
private workspacePath;
|
|
34
38
|
private memoryDir;
|
|
35
39
|
private brainPath;
|
|
36
|
-
|
|
40
|
+
private brainStore;
|
|
41
|
+
private enabled;
|
|
42
|
+
constructor(workspacePath?: string, options?: ProjectMemoryOptions);
|
|
37
43
|
getMemoryDir(): string;
|
|
38
44
|
loadBrain(): ProjectMemoryBrain;
|
|
45
|
+
private emptyBrain;
|
|
39
46
|
saveBrain(brain: ProjectMemoryBrain): void;
|
|
40
47
|
remember(type: ProjectMemoryType, text: string, options?: RememberOptions): void;
|
|
41
48
|
rememberConversation(messages: ChatMessage[], options?: RememberOptions): void;
|
|
@@ -46,11 +53,14 @@ export declare class ProjectMemoryService {
|
|
|
46
53
|
itemCount: number;
|
|
47
54
|
updatedAt: string;
|
|
48
55
|
typeCounts: Record<string, number>;
|
|
56
|
+
enabled: boolean;
|
|
57
|
+
privacyMode: 'local-only';
|
|
49
58
|
};
|
|
50
59
|
private createItem;
|
|
51
60
|
private inferUserMemoryType;
|
|
52
61
|
private dedupeAndTrim;
|
|
53
62
|
private writeMarkdownViews;
|
|
63
|
+
private atomicWrite;
|
|
54
64
|
private headingForType;
|
|
55
65
|
}
|
|
56
66
|
export {};
|