@rigour-labs/core 5.5.4 → 6.0.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.
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Transactional execution — worktree START → verify → COMMIT | DISCARD.
3
+ */
4
+ import { randomUUID } from 'crypto';
5
+ import fs from 'fs-extra';
6
+ import path from 'path';
7
+ import { execa } from 'execa';
8
+ import { hashPolicy } from './policy-hash.js';
9
+ import { assertWritesInScope } from './scope-enforcement.js';
10
+ const DEFAULT_BUDGETS = {
11
+ maxFiles: 20,
12
+ maxRetries: 3,
13
+ maxDurationMs: 30 * 60 * 1000,
14
+ };
15
+ export class TransactionRunner {
16
+ cwd;
17
+ record;
18
+ constructor(cwd, opts) {
19
+ this.cwd = cwd;
20
+ const budgets = { ...DEFAULT_BUDGETS, ...opts.budgets };
21
+ this.record = {
22
+ id: randomUUID(),
23
+ agentId: opts.agentId,
24
+ scope: opts.scope,
25
+ status: 'START',
26
+ budgets,
27
+ filesChanged: [],
28
+ capabilitiesIssued: [],
29
+ decisions: [],
30
+ startedAt: new Date().toISOString(),
31
+ policyHash: opts.policyHash ?? hashPolicy({ scope: opts.scope, budgets }),
32
+ };
33
+ }
34
+ static fromRecord(cwd, record) {
35
+ const runner = new TransactionRunner(cwd, {
36
+ agentId: record.agentId,
37
+ scope: record.scope,
38
+ budgets: record.budgets,
39
+ policyHash: record.policyHash,
40
+ });
41
+ runner.record = { ...record, decisions: [...record.decisions], filesChanged: [...record.filesChanged] };
42
+ return runner;
43
+ }
44
+ getRecord() {
45
+ return { ...this.record, decisions: [...this.record.decisions], filesChanged: [...this.record.filesChanged] };
46
+ }
47
+ setStatus(status) {
48
+ this.record.status = status;
49
+ }
50
+ addDecision(ev) {
51
+ this.record.decisions.push(ev);
52
+ }
53
+ /**
54
+ * Create an ephemeral git worktree for isolated writes.
55
+ */
56
+ async start() {
57
+ const worktreesRoot = path.join(this.cwd, '.rigour', 'worktrees');
58
+ await fs.ensureDir(worktreesRoot);
59
+ const worktreePath = path.join(worktreesRoot, this.record.id);
60
+ const branch = `rigour-tx-${this.record.id.slice(0, 8)}`;
61
+ try {
62
+ await execa('git', ['rev-parse', '--is-inside-work-tree'], { cwd: this.cwd });
63
+ await execa('git', ['worktree', 'add', '-b', branch, worktreePath, 'HEAD'], {
64
+ cwd: this.cwd,
65
+ shell: false,
66
+ });
67
+ this.record.worktreePath = worktreePath;
68
+ }
69
+ catch {
70
+ // Fallback: copy-less sandbox dir (no git) — still tracks commit/discard via marker
71
+ await fs.ensureDir(worktreePath);
72
+ this.record.worktreePath = worktreePath;
73
+ await fs.writeJson(path.join(worktreePath, '.rigour-tx.json'), {
74
+ id: this.record.id,
75
+ base: this.cwd,
76
+ });
77
+ }
78
+ this.setStatus('RUNNING');
79
+ await this.persist();
80
+ return this.getRecord();
81
+ }
82
+ async noteFileChange(relPath) {
83
+ const scopeEv = await assertWritesInScope(this.cwd, [relPath], this.record.agentId);
84
+ this.addDecision(scopeEv);
85
+ if (scopeEv.decision !== 'allow') {
86
+ return scopeEv;
87
+ }
88
+ if (!this.record.filesChanged.includes(relPath)) {
89
+ this.record.filesChanged.push(relPath);
90
+ }
91
+ if (this.record.filesChanged.length > this.record.budgets.maxFiles) {
92
+ const deny = {
93
+ decision: 'deny',
94
+ reason: `File budget exceeded (${this.record.budgets.maxFiles})`,
95
+ ruleId: 'budget.files',
96
+ policyHash: this.record.policyHash,
97
+ timestamp: new Date().toISOString(),
98
+ };
99
+ this.addDecision(deny);
100
+ return deny;
101
+ }
102
+ const elapsed = Date.now() - Date.parse(this.record.startedAt);
103
+ if (elapsed > this.record.budgets.maxDurationMs) {
104
+ const deny = {
105
+ decision: 'deny',
106
+ reason: 'Time budget exceeded',
107
+ ruleId: 'budget.time',
108
+ policyHash: this.record.policyHash,
109
+ timestamp: new Date().toISOString(),
110
+ };
111
+ this.addDecision(deny);
112
+ return deny;
113
+ }
114
+ await this.persist();
115
+ return scopeEv;
116
+ }
117
+ async syncWorktreeChanges() {
118
+ const root = this.record.worktreePath || this.cwd;
119
+ const files = await listChangedFiles(root);
120
+ for (const f of files) {
121
+ const ev = await this.noteFileChange(f);
122
+ if (ev.decision !== 'allow') {
123
+ throw Object.assign(new Error(ev.reason), { evaluation: ev, code: 'FIREWALL_DENY' });
124
+ }
125
+ }
126
+ return [...this.record.filesChanged];
127
+ }
128
+ getWorktreePath() {
129
+ return this.record.worktreePath;
130
+ }
131
+ async verify(runGates) {
132
+ this.setStatus('VERIFYING');
133
+ const gateResults = await runGates();
134
+ await this.persist();
135
+ return { ok: gateResults.status === 'PASS', gateResults };
136
+ }
137
+ async commit() {
138
+ this.setStatus('COMMIT');
139
+ this.record.finishedAt = new Date().toISOString();
140
+ // Worktree branch is left for operator merge; attestation binds treeDigest/commitSha
141
+ await this.persist();
142
+ return this.getRecord();
143
+ }
144
+ async discard() {
145
+ this.setStatus('DISCARD');
146
+ this.record.finishedAt = new Date().toISOString();
147
+ const wt = this.record.worktreePath;
148
+ if (wt && await fs.pathExists(wt)) {
149
+ try {
150
+ await execa('git', ['worktree', 'remove', '--force', wt], { cwd: this.cwd, shell: false });
151
+ }
152
+ catch {
153
+ await fs.remove(wt);
154
+ }
155
+ try {
156
+ const branch = `rigour-tx-${this.record.id.slice(0, 8)}`;
157
+ await execa('git', ['branch', '-D', branch], { cwd: this.cwd, shell: false, reject: false });
158
+ }
159
+ catch {
160
+ // ignore
161
+ }
162
+ }
163
+ await this.persist();
164
+ return this.getRecord();
165
+ }
166
+ async persist() {
167
+ const dir = path.join(this.cwd, '.rigour', 'transactions');
168
+ await fs.ensureDir(dir);
169
+ await fs.writeJson(path.join(dir, `${this.record.id}.json`), this.record, { spaces: 2 });
170
+ await fs.writeJson(path.join(this.cwd, '.rigour', 'transaction-current.json'), this.record, { spaces: 2 });
171
+ }
172
+ }
173
+ export async function listChangedFiles(dir) {
174
+ try {
175
+ const { stdout: diffOut } = await execa('git', ['diff', '--name-only', 'HEAD'], {
176
+ cwd: dir,
177
+ shell: false,
178
+ });
179
+ const { stdout: untrackedOut } = await execa('git', ['ls-files', '--others', '--exclude-standard'], { cwd: dir, shell: false });
180
+ const files = [
181
+ ...diffOut.split('\n'),
182
+ ...untrackedOut.split('\n'),
183
+ ].map(s => s.trim()).filter(Boolean);
184
+ return [...new Set(files)];
185
+ }
186
+ catch {
187
+ return [];
188
+ }
189
+ }
190
+ export async function loadCurrentTransaction(cwd) {
191
+ const p = path.join(cwd, '.rigour', 'transaction-current.json');
192
+ if (!(await fs.pathExists(p)))
193
+ return null;
194
+ try {
195
+ return await fs.readJson(p);
196
+ }
197
+ catch {
198
+ return null;
199
+ }
200
+ }
201
+ export async function listTransactions(cwd) {
202
+ const dir = path.join(cwd, '.rigour', 'transactions');
203
+ if (!(await fs.pathExists(dir)))
204
+ return [];
205
+ const files = (await fs.readdir(dir)).filter(f => f.endsWith('.json'));
206
+ const out = [];
207
+ for (const f of files) {
208
+ try {
209
+ out.push(await fs.readJson(path.join(dir, f)));
210
+ }
211
+ catch {
212
+ // skip
213
+ }
214
+ }
215
+ return out.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
216
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Typed / argv-allowlisted command runner.
3
+ * Replaces unrestricted shell:true for agent-facing execution.
4
+ */
5
+ import type { PolicyEvaluation, TypedCommand } from './types.js';
6
+ /**
7
+ * Conservative allowlist — no general interpreters (node/python/npx) that accept -e/-c.
8
+ */
9
+ export declare const DEFAULT_ALLOWED_BINS: Set<string>;
10
+ export declare function parseCommandLine(command: string): TypedCommand | null;
11
+ export declare function evaluateTypedCommand(command: string, allowedBins?: Set<string>): PolicyEvaluation;
12
+ export declare function runTypedCommand(command: string, cwd: string, allowedBins?: Set<string>): Promise<{
13
+ stdout: string;
14
+ stderr: string;
15
+ evaluation: PolicyEvaluation;
16
+ }>;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Typed / argv-allowlisted command runner.
3
+ * Replaces unrestricted shell:true for agent-facing execution.
4
+ */
5
+ import { execa } from 'execa';
6
+ import { hashPolicy } from './policy-hash.js';
7
+ /**
8
+ * Conservative allowlist — no general interpreters (node/python/npx) that accept -e/-c.
9
+ */
10
+ export const DEFAULT_ALLOWED_BINS = new Set([
11
+ 'npm',
12
+ 'pnpm',
13
+ 'yarn',
14
+ 'tsc',
15
+ 'vitest',
16
+ 'jest',
17
+ 'pytest',
18
+ 'go',
19
+ 'cargo',
20
+ 'git',
21
+ 'rigour',
22
+ 'echo',
23
+ 'cat',
24
+ 'ls',
25
+ 'pwd',
26
+ 'true',
27
+ 'false',
28
+ ]);
29
+ const DENY_ARG_PATTERNS = [
30
+ /rm\s+-rf/i,
31
+ /--force/i,
32
+ /push\s+--force/i,
33
+ /force-with-lease/i,
34
+ /curl.*(-d|--data)/i,
35
+ /wget\s/i,
36
+ /(^|\s)-e(\s|=|$)/,
37
+ /(^|\s)--eval(\s|=|$)/,
38
+ /\bnpm\s+exec\b/i,
39
+ /\bnpx\b/i,
40
+ /\byarn\s+dlx\b/i,
41
+ /\bpnpm\s+dlx\b/i,
42
+ ];
43
+ const GIT_DENY_SUBCOMMANDS = new Set(['push', 'reset', 'clean', 'rebase', 'filter-branch']);
44
+ export function parseCommandLine(command) {
45
+ const trimmed = command.trim();
46
+ if (!trimmed)
47
+ return null;
48
+ if (/[;|&`$<>]/.test(trimmed) || trimmed.includes('\n')) {
49
+ return null;
50
+ }
51
+ const parts = trimmed.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g);
52
+ if (!parts || parts.length === 0)
53
+ return null;
54
+ const unquote = (s) => {
55
+ if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
56
+ return s.slice(1, -1);
57
+ }
58
+ return s;
59
+ };
60
+ const [bin, ...args] = parts.map(unquote);
61
+ return { bin, args };
62
+ }
63
+ export function evaluateTypedCommand(command, allowedBins = DEFAULT_ALLOWED_BINS) {
64
+ const policyHash = hashPolicy({ allowedBins: [...allowedBins].sort() });
65
+ const parsed = parseCommandLine(command);
66
+ if (!parsed) {
67
+ return {
68
+ decision: 'deny',
69
+ reason: 'Command contains shell metacharacters or could not be parsed as argv',
70
+ ruleId: 'shell.no-meta',
71
+ policyHash,
72
+ timestamp: new Date().toISOString(),
73
+ };
74
+ }
75
+ const binBase = parsed.bin.split(/[/\\]/).pop() || parsed.bin;
76
+ if (!allowedBins.has(binBase) && !allowedBins.has(parsed.bin)) {
77
+ return {
78
+ decision: 'deny',
79
+ reason: `Binary "${binBase}" is not on the allowlist`,
80
+ ruleId: 'shell.allowlist',
81
+ policyHash,
82
+ timestamp: new Date().toISOString(),
83
+ };
84
+ }
85
+ const full = `${parsed.bin} ${parsed.args.join(' ')}`;
86
+ for (const re of DENY_ARG_PATTERNS) {
87
+ if (re.test(full)) {
88
+ return {
89
+ decision: 'deny',
90
+ reason: `Command matched deny pattern: ${re}`,
91
+ ruleId: 'shell.deny-pattern',
92
+ policyHash,
93
+ timestamp: new Date().toISOString(),
94
+ };
95
+ }
96
+ }
97
+ if (binBase === 'git') {
98
+ const sub = parsed.args.find(a => !a.startsWith('-'));
99
+ if (sub && GIT_DENY_SUBCOMMANDS.has(sub)) {
100
+ return {
101
+ decision: 'deny',
102
+ reason: `git ${sub} requires explicit capability (destructive git mutation)`,
103
+ ruleId: 'git.destructive',
104
+ policyHash,
105
+ timestamp: new Date().toISOString(),
106
+ };
107
+ }
108
+ }
109
+ return {
110
+ decision: 'allow',
111
+ reason: `Allowlisted binary ${binBase}`,
112
+ ruleId: 'shell.allow',
113
+ policyHash,
114
+ timestamp: new Date().toISOString(),
115
+ };
116
+ }
117
+ export async function runTypedCommand(command, cwd, allowedBins) {
118
+ const evaluation = evaluateTypedCommand(command, allowedBins);
119
+ if (evaluation.decision !== 'allow') {
120
+ throw Object.assign(new Error(evaluation.reason), { evaluation, code: 'FIREWALL_DENY' });
121
+ }
122
+ const parsed = parseCommandLine(command);
123
+ const { stdout, stderr } = await execa(parsed.bin, parsed.args, {
124
+ cwd,
125
+ shell: false,
126
+ reject: false,
127
+ });
128
+ return { stdout: stdout ?? '', stderr: stderr ?? '', evaluation };
129
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Agent Transaction Firewall — shared types.
3
+ * Deterministic allow/deny decisions; no LLM on the hot path.
4
+ */
5
+ export type FirewallDecision = 'allow' | 'deny' | 'timeout-deny' | 'scope-violation';
6
+ export type CapabilityAction = 'filesystem.write' | 'filesystem.read' | 'shell.exec' | 'mcp.call' | 'git.mutate' | 'secret.lease' | 'network.egress' | 'deploy';
7
+ export interface CapabilityGrant {
8
+ id: string;
9
+ action: CapabilityAction;
10
+ resource: string;
11
+ constraints?: Record<string, unknown>;
12
+ expiresAt: number;
13
+ agentId?: string;
14
+ taskId?: string;
15
+ policyHash: string;
16
+ used: boolean;
17
+ }
18
+ export interface PolicyEvaluation {
19
+ decision: FirewallDecision;
20
+ reason: string;
21
+ ruleId?: string;
22
+ capabilityId?: string;
23
+ policyHash: string;
24
+ timestamp: string;
25
+ }
26
+ export interface TypedCommand {
27
+ bin: string;
28
+ args: string[];
29
+ cwd?: string;
30
+ }
31
+ export interface TransactionBudgets {
32
+ maxFiles: number;
33
+ maxRetries: number;
34
+ maxDurationMs: number;
35
+ maxCostUsd?: number;
36
+ }
37
+ export type TransactionStatus = 'START' | 'RUNNING' | 'VERIFYING' | 'COMMIT' | 'DISCARD';
38
+ export interface TransactionRecord {
39
+ id: string;
40
+ agentId?: string;
41
+ scope: string[];
42
+ status: TransactionStatus;
43
+ worktreePath?: string;
44
+ budgets: TransactionBudgets;
45
+ filesChanged: string[];
46
+ capabilitiesIssued: string[];
47
+ decisions: PolicyEvaluation[];
48
+ startedAt: string;
49
+ finishedAt?: string;
50
+ policyHash: string;
51
+ }
52
+ export interface AttestationBundle {
53
+ version: 1;
54
+ transactionId: string;
55
+ agentId?: string;
56
+ userId?: string;
57
+ scope: string[];
58
+ policyHash: string;
59
+ capabilities: string[];
60
+ filesUsed: string[];
61
+ toolsUsed: string[];
62
+ gateResults: {
63
+ status: string;
64
+ score?: number;
65
+ failedGates: string[];
66
+ };
67
+ overrides: string[];
68
+ artifactDigest: string;
69
+ /** git commit SHA at attestation time */
70
+ commitSha?: string;
71
+ /** git tree object id (HEAD^{tree}) */
72
+ treeDigest?: string;
73
+ signedAt: string;
74
+ signature: string;
75
+ }
76
+ export interface AdversarialCase {
77
+ id: string;
78
+ description: string;
79
+ proposedAction: {
80
+ action: CapabilityAction;
81
+ resource: string;
82
+ args?: Record<string, unknown>;
83
+ command?: string;
84
+ };
85
+ expectedDecision: FirewallDecision;
86
+ agentScope?: string[];
87
+ allowlist?: string[];
88
+ }
89
+ export interface AdversarialCaseResult {
90
+ caseId: string;
91
+ expected: FirewallDecision;
92
+ actual: FirewallDecision;
93
+ passed: boolean;
94
+ reason: string;
95
+ suggestedRule?: string;
96
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Agent Transaction Firewall — shared types.
3
+ * Deterministic allow/deny decisions; no LLM on the hot path.
4
+ */
5
+ export {};
@@ -13,6 +13,8 @@ interface CheckerOptions {
13
13
  files: string[];
14
14
  timeout_ms?: number;
15
15
  block_on_failure?: boolean;
16
+ /** Bound writer identity — required when agent scopes are registered */
17
+ agentId?: string;
16
18
  }
17
19
  /**
18
20
  * Run fast gates on a set of files.
@@ -12,6 +12,7 @@ import path from 'path';
12
12
  import yaml from 'yaml';
13
13
  import { ConfigSchema } from '../types/index.js';
14
14
  import { scanInputForCredentials } from './input-validator.js';
15
+ import { evaluateWriteScope, loadAgentScopesFromDisk } from '../firewall/scope-enforcement.js';
15
16
  const JS_TS_PATTERN = /\.(ts|tsx|js|jsx|mts|mjs)$/;
16
17
  /**
17
18
  * Check if a file matches any ignore pattern from rigour.yml.
@@ -92,14 +93,17 @@ function checkFile(content, relPath, cwd, config) {
92
93
  */
93
94
  export async function runHookChecker(options) {
94
95
  const start = Date.now();
95
- const { cwd, files, timeout_ms = 5000 } = options;
96
+ const { cwd, files, timeout_ms = 5000, agentId } = options;
96
97
  const failures = [];
98
+ let timedOut = false;
97
99
  try {
98
100
  const config = await loadConfig(cwd);
99
101
  const deadline = start + timeout_ms;
100
102
  const ignorePatterns = config.ignore ?? [];
103
+ const agentScopes = await loadAgentScopesFromDisk(cwd);
101
104
  for (const filePath of files) {
102
105
  if (Date.now() > deadline) {
106
+ timedOut = true;
103
107
  break;
104
108
  }
105
109
  const resolved = await resolveFile(filePath, cwd);
@@ -110,9 +114,29 @@ export async function runHookChecker(options) {
110
114
  if (isIgnored(resolved.relPath, ignorePatterns)) {
111
115
  continue;
112
116
  }
117
+ if (agentScopes.length > 0) {
118
+ const scopeEv = evaluateWriteScope(cwd, resolved.relPath, agentScopes, agentId);
119
+ if (scopeEv.decision !== 'allow') {
120
+ failures.push({
121
+ gate: 'agent-scope',
122
+ file: resolved.relPath,
123
+ message: scopeEv.reason,
124
+ severity: 'critical',
125
+ });
126
+ continue;
127
+ }
128
+ }
113
129
  const fileFailures = checkFile(resolved.content, resolved.relPath, cwd, config);
114
130
  failures.push(...fileFailures);
115
131
  }
132
+ if (timedOut) {
133
+ failures.push({
134
+ gate: 'hook-timeout',
135
+ file: '',
136
+ message: `Hook checker exceeded ${timeout_ms}ms before all files were scanned (fail-closed)`,
137
+ severity: 'critical',
138
+ });
139
+ }
116
140
  return {
117
141
  status: failures.length > 0 ? 'fail' : 'pass',
118
142
  failures,
@@ -89,7 +89,11 @@ async function main() {
89
89
  process.stdout.write(EMPTY_RESULT);
90
90
  return;
91
91
  }
92
- const result = await runHookChecker({ cwd, files });
92
+ const result = await runHookChecker({
93
+ cwd,
94
+ files,
95
+ agentId: process.env.RIGOUR_AGENT_ID || undefined,
96
+ });
93
97
  process.stdout.write(JSON.stringify(result));
94
98
  if (result.status === 'fail') {
95
99
  logFailures(result.failures);
@@ -82,6 +82,7 @@ process.stdin.on('end', async () => {
82
82
  const result = await runHookChecker({
83
83
  cwd: process.cwd(),
84
84
  files: [payload.file_path],
85
+ agentId: process.env.RIGOUR_AGENT_ID || undefined,
85
86
  });
86
87
 
87
88
  // Write result to stdout for Cursor to consume
@@ -148,6 +149,7 @@ process.stdin.on('end', async () => {
148
149
  const result = await runHookChecker({
149
150
  cwd: process.cwd(),
150
151
  files: [filePath],
152
+ agentId: process.env.RIGOUR_AGENT_ID || undefined,
151
153
  });
152
154
 
153
155
  if (result.status === 'fail') {
@@ -206,6 +208,7 @@ process.stdin.on('end', async () => {
206
208
  const result = await runHookChecker({
207
209
  cwd: process.cwd(),
208
210
  files: [payload.file_path],
211
+ agentId: process.env.RIGOUR_AGENT_ID || undefined,
209
212
  });
210
213
 
211
214
  if (result.status === 'fail') {
package/dist/index.d.ts CHANGED
@@ -36,4 +36,5 @@ export * from './context/cache-engine.js';
36
36
  export * from './context/context-session.js';
37
37
  export * from './context/index-bridge.js';
38
38
  export * from './services/context-telemetry-service.js';
39
+ export * from './firewall/index.js';
39
40
  export { recordContextEvent, recordModelUsage, recordCheckpointMetric, getContextEvents, getModelUsages, getCheckpointMetrics, } from './storage/index.js';
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ export * from './context/cache-engine.js';
37
37
  export * from './context/context-session.js';
38
38
  export * from './context/index-bridge.js';
39
39
  export * from './services/context-telemetry-service.js';
40
+ export * from './firewall/index.js';
40
41
  export { recordContextEvent, recordModelUsage, recordCheckpointMetric, getContextEvents, getModelUsages, getCheckpointMetrics, } from './storage/index.js';
41
42
  // Pattern Index is intentionally NOT exported here to prevent
42
43
  // native dependency issues (sharp/transformers) from leaking into
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigour-labs/core",
3
- "version": "5.5.4",
3
+ "version": "6.0.0",
4
4
  "description": "AI-native quality gate engine with local Bayesian learning. AST analysis, drift detection, Fix Packet generation, and agent self-healing across TypeScript, JavaScript, Python, Go, Ruby, and C#.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://rigour.run",
@@ -66,11 +66,11 @@
66
66
  "@xenova/transformers": "^2.17.2",
67
67
  "sqlite3": "^5.1.7",
68
68
  "openai": "^4.104.0",
69
- "@rigour-labs/brain-darwin-arm64": "5.5.4",
70
- "@rigour-labs/brain-darwin-x64": "5.5.4",
71
- "@rigour-labs/brain-linux-arm64": "5.5.4",
72
- "@rigour-labs/brain-linux-x64": "5.5.4",
73
- "@rigour-labs/brain-win-x64": "5.5.4"
69
+ "@rigour-labs/brain-darwin-arm64": "6.0.0",
70
+ "@rigour-labs/brain-linux-arm64": "6.0.0",
71
+ "@rigour-labs/brain-darwin-x64": "6.0.0",
72
+ "@rigour-labs/brain-win-x64": "6.0.0",
73
+ "@rigour-labs/brain-linux-x64": "6.0.0"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@types/fs-extra": "^11.0.4",