@vibecheckai/cli 3.8.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -98
  2. package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -318
  3. package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -484
  4. package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -418
  5. package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -333
  6. package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +634 -622
  7. package/bin/runners/lib/agent-firewall/intent/index.js +102 -102
  8. package/bin/runners/lib/agent-firewall/intent/schema.js +352 -352
  9. package/bin/runners/lib/agent-firewall/intent/store.js +283 -283
  10. package/bin/runners/lib/agent-firewall/interceptor/base.js +7 -3
  11. package/bin/runners/lib/engine/ast-cache.js +210 -210
  12. package/bin/runners/lib/engine/auth-extractor.js +211 -211
  13. package/bin/runners/lib/engine/billing-extractor.js +112 -112
  14. package/bin/runners/lib/engine/enforcement-extractor.js +100 -100
  15. package/bin/runners/lib/engine/env-extractor.js +207 -207
  16. package/bin/runners/lib/engine/express-extractor.js +208 -208
  17. package/bin/runners/lib/engine/extractors.js +849 -849
  18. package/bin/runners/lib/engine/index.js +207 -207
  19. package/bin/runners/lib/engine/repo-index.js +514 -514
  20. package/bin/runners/lib/engine/types.js +124 -124
  21. package/bin/runners/runIntent.js +906 -906
  22. package/bin/runners/runPacks.js +2089 -2089
  23. package/bin/runners/runReality.js +178 -1
  24. package/bin/runners/runShield.js +1282 -1282
  25. package/mcp-server/handlers/index.ts +2 -2
  26. package/mcp-server/handlers/tool-handler.ts +47 -8
  27. package/mcp-server/lib/executor.ts +5 -5
  28. package/mcp-server/lib/index.ts +14 -4
  29. package/mcp-server/lib/sandbox.test.ts +4 -4
  30. package/mcp-server/lib/sandbox.ts +2 -2
  31. package/mcp-server/package.json +1 -1
  32. package/mcp-server/registry.test.ts +18 -12
  33. package/mcp-server/tsconfig.json +1 -0
  34. package/package.json +2 -1
@@ -1,283 +1,283 @@
1
- /**
2
- * Intent Store - Persistent Intent Storage
3
- *
4
- * ═══════════════════════════════════════════════════════════════════════════════
5
- * AGENT FIREWALL™ - INTENT PERSISTENCE
6
- * ═══════════════════════════════════════════════════════════════════════════════
7
- *
8
- * Stores intent declarations for session and audit purposes.
9
- * Intent is immutable once stored - updates create new versions.
10
- *
11
- * @module intent/store
12
- * @version 2.0.0
13
- */
14
-
15
- "use strict";
16
-
17
- const fs = require("fs");
18
- const path = require("path");
19
- const { verifyIntentIntegrity, createBlockingIntent } = require("./schema");
20
-
21
- /**
22
- * Intent Store - manages intent persistence
23
- */
24
- class IntentStore {
25
- constructor(projectRoot) {
26
- this.projectRoot = projectRoot;
27
- this.intentDir = path.join(projectRoot, ".vibecheck", "intents");
28
- this.currentIntentPath = path.join(this.intentDir, "current.json");
29
- this.historyDir = path.join(this.intentDir, "history");
30
- }
31
-
32
- /**
33
- * Ensure storage directories exist
34
- */
35
- ensureDirectories() {
36
- if (!fs.existsSync(this.intentDir)) {
37
- fs.mkdirSync(this.intentDir, { recursive: true });
38
- }
39
- if (!fs.existsSync(this.historyDir)) {
40
- fs.mkdirSync(this.historyDir, { recursive: true });
41
- }
42
- }
43
-
44
- /**
45
- * Store a new intent (or update)
46
- * @param {Object} intent - Intent to store
47
- * @returns {Object} Storage result
48
- */
49
- store(intent) {
50
- this.ensureDirectories();
51
-
52
- // Verify intent integrity
53
- const verification = verifyIntentIntegrity(intent);
54
- if (!verification.valid) {
55
- return {
56
- success: false,
57
- error: `INTENT_INTEGRITY_FAILED: ${verification.reason}`,
58
- };
59
- }
60
-
61
- // Archive current intent if exists
62
- const current = this.getCurrent();
63
- if (current && current.hash !== intent.hash) {
64
- this.archive(current);
65
- }
66
-
67
- // Write new current intent
68
- const data = {
69
- intent,
70
- stored_at: new Date().toISOString(),
71
- verified: true,
72
- };
73
-
74
- fs.writeFileSync(this.currentIntentPath, JSON.stringify(data, null, 2));
75
-
76
- return {
77
- success: true,
78
- hash: intent.hash,
79
- path: this.currentIntentPath,
80
- };
81
- }
82
-
83
- /**
84
- * Get current intent (returns blocking intent if none exists)
85
- * @param {Object} options - Options
86
- * @param {boolean} options.allowMissing - Don't return blocking intent if missing
87
- * @returns {Object|null} Current intent or blocking intent
88
- */
89
- getCurrent(options = {}) {
90
- if (!fs.existsSync(this.currentIntentPath)) {
91
- if (options.allowMissing) {
92
- return null;
93
- }
94
- // Return blocking intent when no intent declared
95
- return createBlockingIntent();
96
- }
97
-
98
- try {
99
- const data = JSON.parse(fs.readFileSync(this.currentIntentPath, "utf-8"));
100
- const intent = data.intent;
101
-
102
- // Verify integrity on load
103
- const verification = verifyIntentIntegrity(intent);
104
- if (!verification.valid) {
105
- console.error(`[IntentStore] INTEGRITY VIOLATION: ${verification.reason}`);
106
- // Return blocking intent on integrity failure
107
- return createBlockingIntent();
108
- }
109
-
110
- return intent;
111
- } catch (error) {
112
- console.error(`[IntentStore] Error loading intent: ${error.message}`);
113
- if (options.allowMissing) {
114
- return null;
115
- }
116
- return createBlockingIntent();
117
- }
118
- }
119
-
120
- /**
121
- * Check if an intent is currently declared
122
- * @returns {boolean} True if intent exists
123
- */
124
- hasIntent() {
125
- return fs.existsSync(this.currentIntentPath);
126
- }
127
-
128
- /**
129
- * Archive an intent to history
130
- * @param {Object} intent - Intent to archive
131
- */
132
- archive(intent) {
133
- this.ensureDirectories();
134
-
135
- const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
136
- const archivePath = path.join(this.historyDir, `${timestamp}_${intent.hash.slice(0, 8)}.json`);
137
-
138
- const data = {
139
- intent,
140
- archived_at: new Date().toISOString(),
141
- };
142
-
143
- fs.writeFileSync(archivePath, JSON.stringify(data, null, 2));
144
- }
145
-
146
- /**
147
- * Get intent history
148
- * @param {number} limit - Maximum number of intents to return
149
- * @returns {Object[]} Array of historical intents
150
- */
151
- getHistory(limit = 10) {
152
- if (!fs.existsSync(this.historyDir)) {
153
- return [];
154
- }
155
-
156
- const files = fs.readdirSync(this.historyDir)
157
- .filter(f => f.endsWith(".json"))
158
- .sort()
159
- .reverse()
160
- .slice(0, limit);
161
-
162
- return files.map(file => {
163
- try {
164
- const data = JSON.parse(fs.readFileSync(path.join(this.historyDir, file), "utf-8"));
165
- return {
166
- ...data.intent,
167
- archived_at: data.archived_at,
168
- };
169
- } catch {
170
- return null;
171
- }
172
- }).filter(Boolean);
173
- }
174
-
175
- /**
176
- * Clear current intent
177
- * @returns {boolean} Success
178
- */
179
- clear() {
180
- const current = this.getCurrent({ allowMissing: true });
181
- if (current) {
182
- this.archive(current);
183
- }
184
-
185
- if (fs.existsSync(this.currentIntentPath)) {
186
- fs.unlinkSync(this.currentIntentPath);
187
- }
188
-
189
- return true;
190
- }
191
-
192
- /**
193
- * Get intent by hash (from history)
194
- * @param {string} hash - Intent hash
195
- * @returns {Object|null} Intent or null
196
- */
197
- getByHash(hash) {
198
- // Check current
199
- const current = this.getCurrent({ allowMissing: true });
200
- if (current && current.hash === hash) {
201
- return current;
202
- }
203
-
204
- // Check history
205
- if (!fs.existsSync(this.historyDir)) {
206
- return null;
207
- }
208
-
209
- const files = fs.readdirSync(this.historyDir);
210
- for (const file of files) {
211
- if (file.includes(hash.slice(0, 8))) {
212
- try {
213
- const data = JSON.parse(fs.readFileSync(path.join(this.historyDir, file), "utf-8"));
214
- if (data.intent.hash === hash) {
215
- return data.intent;
216
- }
217
- } catch {
218
- continue;
219
- }
220
- }
221
- }
222
-
223
- return null;
224
- }
225
- }
226
-
227
- /**
228
- * Session-scoped intent tracking
229
- */
230
- class SessionIntentTracker {
231
- constructor() {
232
- this.sessions = new Map();
233
- }
234
-
235
- /**
236
- * Set intent for a session
237
- * @param {string} sessionId - Session identifier
238
- * @param {Object} intent - Intent object
239
- */
240
- setIntent(sessionId, intent) {
241
- this.sessions.set(sessionId, {
242
- intent,
243
- set_at: new Date().toISOString(),
244
- locked: true, // Immutable by default
245
- });
246
- }
247
-
248
- /**
249
- * Get intent for a session
250
- * @param {string} sessionId - Session identifier
251
- * @returns {Object|null} Intent or null
252
- */
253
- getIntent(sessionId) {
254
- const session = this.sessions.get(sessionId);
255
- return session?.intent || null;
256
- }
257
-
258
- /**
259
- * Check if session has intent
260
- * @param {string} sessionId - Session identifier
261
- * @returns {boolean} True if intent exists
262
- */
263
- hasIntent(sessionId) {
264
- return this.sessions.has(sessionId);
265
- }
266
-
267
- /**
268
- * Clear session intent
269
- * @param {string} sessionId - Session identifier
270
- */
271
- clearIntent(sessionId) {
272
- this.sessions.delete(sessionId);
273
- }
274
- }
275
-
276
- // Global session tracker
277
- const globalSessionTracker = new SessionIntentTracker();
278
-
279
- module.exports = {
280
- IntentStore,
281
- SessionIntentTracker,
282
- globalSessionTracker,
283
- };
1
+ /**
2
+ * Intent Store - Persistent Intent Storage
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * AGENT FIREWALL™ - INTENT PERSISTENCE
6
+ * ═══════════════════════════════════════════════════════════════════════════════
7
+ *
8
+ * Stores intent declarations for session and audit purposes.
9
+ * Intent is immutable once stored - updates create new versions.
10
+ *
11
+ * @module intent/store
12
+ * @version 2.0.0
13
+ */
14
+
15
+ "use strict";
16
+
17
+ const fs = require("fs");
18
+ const path = require("path");
19
+ const { verifyIntentIntegrity, createBlockingIntent } = require("./schema");
20
+
21
+ /**
22
+ * Intent Store - manages intent persistence
23
+ */
24
+ class IntentStore {
25
+ constructor(projectRoot) {
26
+ this.projectRoot = projectRoot;
27
+ this.intentDir = path.join(projectRoot, ".vibecheck", "intents");
28
+ this.currentIntentPath = path.join(this.intentDir, "current.json");
29
+ this.historyDir = path.join(this.intentDir, "history");
30
+ }
31
+
32
+ /**
33
+ * Ensure storage directories exist
34
+ */
35
+ ensureDirectories() {
36
+ if (!fs.existsSync(this.intentDir)) {
37
+ fs.mkdirSync(this.intentDir, { recursive: true });
38
+ }
39
+ if (!fs.existsSync(this.historyDir)) {
40
+ fs.mkdirSync(this.historyDir, { recursive: true });
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Store a new intent (or update)
46
+ * @param {Object} intent - Intent to store
47
+ * @returns {Object} Storage result
48
+ */
49
+ store(intent) {
50
+ this.ensureDirectories();
51
+
52
+ // Verify intent integrity
53
+ const verification = verifyIntentIntegrity(intent);
54
+ if (!verification.valid) {
55
+ return {
56
+ success: false,
57
+ error: `INTENT_INTEGRITY_FAILED: ${verification.reason}`,
58
+ };
59
+ }
60
+
61
+ // Archive current intent if exists (use allowMissing to avoid archiving blocking intent)
62
+ const current = this.getCurrent({ allowMissing: true });
63
+ if (current && current.hash !== intent.hash) {
64
+ this.archive(current);
65
+ }
66
+
67
+ // Write new current intent
68
+ const data = {
69
+ intent,
70
+ stored_at: new Date().toISOString(),
71
+ verified: true,
72
+ };
73
+
74
+ fs.writeFileSync(this.currentIntentPath, JSON.stringify(data, null, 2));
75
+
76
+ return {
77
+ success: true,
78
+ hash: intent.hash,
79
+ path: this.currentIntentPath,
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Get current intent (returns blocking intent if none exists)
85
+ * @param {Object} options - Options
86
+ * @param {boolean} options.allowMissing - Don't return blocking intent if missing
87
+ * @returns {Object|null} Current intent or blocking intent
88
+ */
89
+ getCurrent(options = {}) {
90
+ if (!fs.existsSync(this.currentIntentPath)) {
91
+ if (options.allowMissing) {
92
+ return null;
93
+ }
94
+ // Return blocking intent when no intent declared
95
+ return createBlockingIntent();
96
+ }
97
+
98
+ try {
99
+ const data = JSON.parse(fs.readFileSync(this.currentIntentPath, "utf-8"));
100
+ const intent = data.intent;
101
+
102
+ // Verify integrity on load
103
+ const verification = verifyIntentIntegrity(intent);
104
+ if (!verification.valid) {
105
+ console.error(`[IntentStore] INTEGRITY VIOLATION: ${verification.reason}`);
106
+ // Return blocking intent on integrity failure
107
+ return createBlockingIntent();
108
+ }
109
+
110
+ return intent;
111
+ } catch (error) {
112
+ console.error(`[IntentStore] Error loading intent: ${error.message}`);
113
+ if (options.allowMissing) {
114
+ return null;
115
+ }
116
+ return createBlockingIntent();
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Check if an intent is currently declared
122
+ * @returns {boolean} True if intent exists
123
+ */
124
+ hasIntent() {
125
+ return fs.existsSync(this.currentIntentPath);
126
+ }
127
+
128
+ /**
129
+ * Archive an intent to history
130
+ * @param {Object} intent - Intent to archive
131
+ */
132
+ archive(intent) {
133
+ this.ensureDirectories();
134
+
135
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
136
+ const archivePath = path.join(this.historyDir, `${timestamp}_${intent.hash.slice(0, 8)}.json`);
137
+
138
+ const data = {
139
+ intent,
140
+ archived_at: new Date().toISOString(),
141
+ };
142
+
143
+ fs.writeFileSync(archivePath, JSON.stringify(data, null, 2));
144
+ }
145
+
146
+ /**
147
+ * Get intent history
148
+ * @param {number} limit - Maximum number of intents to return
149
+ * @returns {Object[]} Array of historical intents
150
+ */
151
+ getHistory(limit = 10) {
152
+ if (!fs.existsSync(this.historyDir)) {
153
+ return [];
154
+ }
155
+
156
+ const files = fs.readdirSync(this.historyDir)
157
+ .filter(f => f.endsWith(".json"))
158
+ .sort()
159
+ .reverse()
160
+ .slice(0, limit);
161
+
162
+ return files.map(file => {
163
+ try {
164
+ const data = JSON.parse(fs.readFileSync(path.join(this.historyDir, file), "utf-8"));
165
+ return {
166
+ ...data.intent,
167
+ archived_at: data.archived_at,
168
+ };
169
+ } catch {
170
+ return null;
171
+ }
172
+ }).filter(Boolean);
173
+ }
174
+
175
+ /**
176
+ * Clear current intent
177
+ * @returns {boolean} Success
178
+ */
179
+ clear() {
180
+ const current = this.getCurrent({ allowMissing: true });
181
+ if (current) {
182
+ this.archive(current);
183
+ }
184
+
185
+ if (fs.existsSync(this.currentIntentPath)) {
186
+ fs.unlinkSync(this.currentIntentPath);
187
+ }
188
+
189
+ return true;
190
+ }
191
+
192
+ /**
193
+ * Get intent by hash (from history)
194
+ * @param {string} hash - Intent hash
195
+ * @returns {Object|null} Intent or null
196
+ */
197
+ getByHash(hash) {
198
+ // Check current
199
+ const current = this.getCurrent({ allowMissing: true });
200
+ if (current && current.hash === hash) {
201
+ return current;
202
+ }
203
+
204
+ // Check history
205
+ if (!fs.existsSync(this.historyDir)) {
206
+ return null;
207
+ }
208
+
209
+ const files = fs.readdirSync(this.historyDir);
210
+ for (const file of files) {
211
+ if (file.includes(hash.slice(0, 8))) {
212
+ try {
213
+ const data = JSON.parse(fs.readFileSync(path.join(this.historyDir, file), "utf-8"));
214
+ if (data.intent.hash === hash) {
215
+ return data.intent;
216
+ }
217
+ } catch {
218
+ continue;
219
+ }
220
+ }
221
+ }
222
+
223
+ return null;
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Session-scoped intent tracking
229
+ */
230
+ class SessionIntentTracker {
231
+ constructor() {
232
+ this.sessions = new Map();
233
+ }
234
+
235
+ /**
236
+ * Set intent for a session
237
+ * @param {string} sessionId - Session identifier
238
+ * @param {Object} intent - Intent object
239
+ */
240
+ setIntent(sessionId, intent) {
241
+ this.sessions.set(sessionId, {
242
+ intent,
243
+ set_at: new Date().toISOString(),
244
+ locked: true, // Immutable by default
245
+ });
246
+ }
247
+
248
+ /**
249
+ * Get intent for a session
250
+ * @param {string} sessionId - Session identifier
251
+ * @returns {Object|null} Intent or null
252
+ */
253
+ getIntent(sessionId) {
254
+ const session = this.sessions.get(sessionId);
255
+ return session?.intent || null;
256
+ }
257
+
258
+ /**
259
+ * Check if session has intent
260
+ * @param {string} sessionId - Session identifier
261
+ * @returns {boolean} True if intent exists
262
+ */
263
+ hasIntent(sessionId) {
264
+ return this.sessions.has(sessionId);
265
+ }
266
+
267
+ /**
268
+ * Clear session intent
269
+ * @param {string} sessionId - Session identifier
270
+ */
271
+ clearIntent(sessionId) {
272
+ this.sessions.delete(sessionId);
273
+ }
274
+ }
275
+
276
+ // Global session tracker
277
+ const globalSessionTracker = new SessionIntentTracker();
278
+
279
+ module.exports = {
280
+ IntentStore,
281
+ SessionIntentTracker,
282
+ globalSessionTracker,
283
+ };
@@ -114,13 +114,17 @@ async function interceptFileWrite({
114
114
  }
115
115
 
116
116
  // Return interception result
117
+ // In observe mode, always allow but still report violations
118
+ const isObserveMode = policy.mode === "observe";
117
119
  return {
118
- allowed: verdict.decision === "ALLOW",
119
- verdict: verdict.decision,
120
+ allowed: isObserveMode || verdict.decision === "ALLOW",
121
+ verdict: isObserveMode && verdict.decision !== "ALLOW" ? "ALLOW" : verdict.decision,
120
122
  violations: verdict.violations,
121
123
  unblockPlan,
122
124
  packetId: packet.id,
123
- message: verdict.message
125
+ message: isObserveMode && verdict.violations?.length > 0
126
+ ? `[OBSERVE MODE] Would have blocked: ${verdict.message}`
127
+ : verdict.message
124
128
  };
125
129
  }
126
130