overarch-darwin-arm64 0.8.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 (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +214 -0
  3. package/dist/bin/overarch.d.ts +2 -0
  4. package/dist/bin/overarch.js +178 -0
  5. package/dist/src/agent/context.d.ts +14 -0
  6. package/dist/src/agent/context.js +200 -0
  7. package/dist/src/agent/context_compressor.d.ts +21 -0
  8. package/dist/src/agent/context_compressor.js +90 -0
  9. package/dist/src/agent/manifest.d.ts +22 -0
  10. package/dist/src/agent/manifest.js +131 -0
  11. package/dist/src/agent/planner.d.ts +38 -0
  12. package/dist/src/agent/planner.js +118 -0
  13. package/dist/src/agent/runtime.d.ts +59 -0
  14. package/dist/src/agent/runtime.js +400 -0
  15. package/dist/src/cli/approval.d.ts +13 -0
  16. package/dist/src/cli/approval.js +71 -0
  17. package/dist/src/cli/args.d.ts +15 -0
  18. package/dist/src/cli/args.js +47 -0
  19. package/dist/src/cli/commands/audit.d.ts +10 -0
  20. package/dist/src/cli/commands/audit.js +79 -0
  21. package/dist/src/cli/commands/companion.d.ts +1 -0
  22. package/dist/src/cli/commands/companion.js +143 -0
  23. package/dist/src/cli/commands/config.d.ts +1 -0
  24. package/dist/src/cli/commands/config.js +135 -0
  25. package/dist/src/cli/commands/doctor.d.ts +1 -0
  26. package/dist/src/cli/commands/doctor.js +217 -0
  27. package/dist/src/cli/commands/init.d.ts +1 -0
  28. package/dist/src/cli/commands/init.js +134 -0
  29. package/dist/src/cli/commands/memory.d.ts +2 -0
  30. package/dist/src/cli/commands/memory.js +98 -0
  31. package/dist/src/cli/commands/onboarding.d.ts +3 -0
  32. package/dist/src/cli/commands/onboarding.js +223 -0
  33. package/dist/src/cli/commands/sessions.d.ts +1 -0
  34. package/dist/src/cli/commands/sessions.js +22 -0
  35. package/dist/src/cli/commands/status.d.ts +1 -0
  36. package/dist/src/cli/commands/status.js +37 -0
  37. package/dist/src/cli/commands/update.d.ts +9 -0
  38. package/dist/src/cli/commands/update.js +105 -0
  39. package/dist/src/cli/repl.d.ts +3 -0
  40. package/dist/src/cli/repl.js +821 -0
  41. package/dist/src/cli/ui.d.ts +53 -0
  42. package/dist/src/cli/ui.js +215 -0
  43. package/dist/src/companion/petdex_adapter.d.ts +31 -0
  44. package/dist/src/companion/petdex_adapter.js +171 -0
  45. package/dist/src/companion/petdex_bridge.d.ts +65 -0
  46. package/dist/src/companion/petdex_bridge.js +356 -0
  47. package/dist/src/companion/types.d.ts +57 -0
  48. package/dist/src/companion/types.js +11 -0
  49. package/dist/src/config/index.d.ts +24 -0
  50. package/dist/src/config/index.js +237 -0
  51. package/dist/src/config/types.d.ts +58 -0
  52. package/dist/src/config/types.js +7 -0
  53. package/dist/src/core/diagnostics.d.ts +48 -0
  54. package/dist/src/core/diagnostics.js +115 -0
  55. package/dist/src/core/index.d.ts +23 -0
  56. package/dist/src/core/index.js +23 -0
  57. package/dist/src/core/mission.d.ts +55 -0
  58. package/dist/src/core/mission.js +173 -0
  59. package/dist/src/index.d.ts +1 -0
  60. package/dist/src/index.js +1 -0
  61. package/dist/src/learning/memory_manager.d.ts +26 -0
  62. package/dist/src/learning/memory_manager.js +211 -0
  63. package/dist/src/learning/types.d.ts +54 -0
  64. package/dist/src/learning/types.js +1 -0
  65. package/dist/src/mcp/registry.d.ts +17 -0
  66. package/dist/src/mcp/registry.js +97 -0
  67. package/dist/src/mcp/types.d.ts +13 -0
  68. package/dist/src/mcp/types.js +1 -0
  69. package/dist/src/providers/anthropic.d.ts +10 -0
  70. package/dist/src/providers/anthropic.js +95 -0
  71. package/dist/src/providers/base.d.ts +15 -0
  72. package/dist/src/providers/base.js +108 -0
  73. package/dist/src/providers/factory.d.ts +28 -0
  74. package/dist/src/providers/factory.js +142 -0
  75. package/dist/src/providers/gemini.d.ts +10 -0
  76. package/dist/src/providers/gemini.js +97 -0
  77. package/dist/src/providers/openai.d.ts +9 -0
  78. package/dist/src/providers/openai.js +85 -0
  79. package/dist/src/providers/openrouter.d.ts +5 -0
  80. package/dist/src/providers/openrouter.js +14 -0
  81. package/dist/src/providers/registry.d.ts +19 -0
  82. package/dist/src/providers/registry.js +209 -0
  83. package/dist/src/providers/types.d.ts +59 -0
  84. package/dist/src/providers/types.js +1 -0
  85. package/dist/src/providers/validator.d.ts +24 -0
  86. package/dist/src/providers/validator.js +161 -0
  87. package/dist/src/security/audit.d.ts +56 -0
  88. package/dist/src/security/audit.js +154 -0
  89. package/dist/src/security/guardrails.d.ts +9 -0
  90. package/dist/src/security/guardrails.js +32 -0
  91. package/dist/src/security/permissions.d.ts +12 -0
  92. package/dist/src/security/permissions.js +68 -0
  93. package/dist/src/security/policy.d.ts +220 -0
  94. package/dist/src/security/policy.js +720 -0
  95. package/dist/src/session/manager.d.ts +22 -0
  96. package/dist/src/session/manager.js +172 -0
  97. package/dist/src/session/types.d.ts +36 -0
  98. package/dist/src/session/types.js +1 -0
  99. package/dist/src/tools/checkpoint.d.ts +26 -0
  100. package/dist/src/tools/checkpoint.js +160 -0
  101. package/dist/src/tools/diagnostics.d.ts +2 -0
  102. package/dist/src/tools/diagnostics.js +125 -0
  103. package/dist/src/tools/diff.d.ts +14 -0
  104. package/dist/src/tools/diff.js +124 -0
  105. package/dist/src/tools/filesystem.d.ts +2 -0
  106. package/dist/src/tools/filesystem.js +224 -0
  107. package/dist/src/tools/git.d.ts +2 -0
  108. package/dist/src/tools/git.js +138 -0
  109. package/dist/src/tools/markdown.d.ts +33 -0
  110. package/dist/src/tools/markdown.js +190 -0
  111. package/dist/src/tools/project.d.ts +2 -0
  112. package/dist/src/tools/project.js +63 -0
  113. package/dist/src/tools/registry.d.ts +14 -0
  114. package/dist/src/tools/registry.js +230 -0
  115. package/dist/src/tools/search.d.ts +2 -0
  116. package/dist/src/tools/search.js +136 -0
  117. package/dist/src/tools/shell.d.ts +2 -0
  118. package/dist/src/tools/shell.js +74 -0
  119. package/dist/src/tools/symbols.d.ts +30 -0
  120. package/dist/src/tools/symbols.js +155 -0
  121. package/dist/src/tools/types.d.ts +28 -0
  122. package/dist/src/tools/types.js +1 -0
  123. package/dist/src/tools/web_search.d.ts +14 -0
  124. package/dist/src/tools/web_search.js +154 -0
  125. package/install.sh +29 -0
  126. package/overarch +15 -0
  127. package/package.json +28 -0
@@ -0,0 +1,400 @@
1
+ import { PermissionTier } from '../config/types.js';
2
+ import { checkPermission, classifyAction } from '../security/permissions.js';
3
+ import { isAborted, resetAbort, abort } from '../security/guardrails.js';
4
+ import { parsePlan, CCSS_MODE_CONFIGS } from './planner.js';
5
+ import { CheckpointManager } from '../tools/checkpoint.js';
6
+ import { MemoryManager } from '../learning/memory_manager.js';
7
+ import { policyEngine, ActionState } from '../security/policy.js';
8
+ import { promptActionApproval } from '../cli/approval.js';
9
+ export class AgentRuntime {
10
+ provider;
11
+ toolRegistry;
12
+ config;
13
+ options;
14
+ messages = [];
15
+ checkpointManager;
16
+ memoryManager;
17
+ constructor(provider, toolRegistry, config, options = {}) {
18
+ this.provider = provider;
19
+ this.toolRegistry = toolRegistry;
20
+ this.config = config;
21
+ this.options = options;
22
+ const activeCwd = options.cwd || process.cwd();
23
+ this.checkpointManager = new CheckpointManager(activeCwd);
24
+ this.memoryManager = new MemoryManager(activeCwd);
25
+ }
26
+ setConfig(newConfig) {
27
+ this.config = newConfig;
28
+ }
29
+ setProvider(newProvider) {
30
+ this.provider = newProvider;
31
+ }
32
+ /**
33
+ * Production 7-Stage Agent Pipeline:
34
+ * 1. Understand: Parse goal, establish epistemic tracking
35
+ * 2. Inspect: Discover repository state, parallel read-only tool inspection
36
+ * 3. Plan: Structure roadmap and milestones
37
+ * 4. Approve: CCHCFAI authority gating, pre-mutation checkpointing
38
+ * 5. Execute: Sequential deterministic tool mutations with loop prevention
39
+ * 6. Verify: Auto-verification of modifications
40
+ * 7. Report: Synthesize evidence-based completion summary
41
+ */
42
+ async run(userMessage, systemContext, callbacks) {
43
+ resetAbort();
44
+ // ── STAGE 1: UNDERSTAND ──
45
+ const modeKey = this.config.ccss || 'auto';
46
+ const ccssConfig = CCSS_MODE_CONFIGS[modeKey] || CCSS_MODE_CONFIGS.auto;
47
+ // Retrieve selective relevant memory for context injection (under token budget)
48
+ let memoryPromptInjection = '';
49
+ try {
50
+ const memoryContext = await this.memoryManager.retrieveRelevantContext(userMessage);
51
+ if (memoryContext.formattedPromptInjection) {
52
+ memoryPromptInjection = memoryContext.formattedPromptInjection;
53
+ }
54
+ }
55
+ catch { }
56
+ if (this.messages.length === 0) {
57
+ const initialSystem = memoryPromptInjection
58
+ ? `${systemContext}\n\n${memoryPromptInjection}`
59
+ : systemContext;
60
+ this.messages.push({ role: 'system', content: initialSystem });
61
+ }
62
+ else if (memoryPromptInjection) {
63
+ this.messages.push({ role: 'system', content: `[MEMORY RECALL]\n${memoryPromptInjection}` });
64
+ }
65
+ this.messages.push({ role: 'user', content: userMessage });
66
+ const epistemicState = {
67
+ goal: userMessage.slice(0, 120),
68
+ modifiedFiles: new Set(),
69
+ toolsRunCount: 0,
70
+ recentFailures: new Map(),
71
+ verificationRun: false,
72
+ };
73
+ let iterations = 0;
74
+ const maxIterations = modeKey === 'god' ? 40 : modeKey === 'master' ? 30 : modeKey === 'control' ? 15 : 25;
75
+ let finalResponse = '';
76
+ let hasCreatedCheckpoint = false;
77
+ callbacks.onStateChange?.('thinking', `[CCSS ${modeKey.toUpperCase()}] Analyzing objectives and dependencies...`);
78
+ while (iterations < maxIterations) {
79
+ if (isAborted()) {
80
+ callbacks.onStateChange?.('paused', 'Operation aborted by user');
81
+ callbacks.onError(new Error('Agent aborted.'));
82
+ break;
83
+ }
84
+ iterations++;
85
+ const toolSchemas = this.toolRegistry.toOpenAITools();
86
+ const request = {
87
+ messages: this.messages,
88
+ model: this.config.defaultModel,
89
+ tools: toolSchemas.length > 0 ? toolSchemas : undefined,
90
+ stream: true,
91
+ };
92
+ let assistantText = '';
93
+ const toolCalls = [];
94
+ const pendingToolCalls = new Map();
95
+ try {
96
+ for await (const chunk of this.provider.stream(request)) {
97
+ if (isAborted())
98
+ break;
99
+ switch (chunk.type) {
100
+ case 'text':
101
+ assistantText += chunk.content || '';
102
+ callbacks.onText(chunk.content || '');
103
+ break;
104
+ case 'tool_call_start':
105
+ if (chunk.toolCall) {
106
+ const idx = pendingToolCalls.size;
107
+ pendingToolCalls.set(idx, {
108
+ id: chunk.toolCall.id || `call_${idx}`,
109
+ name: chunk.toolCall.name || '',
110
+ args: '',
111
+ });
112
+ }
113
+ break;
114
+ case 'tool_call_args':
115
+ if (chunk.toolCall) {
116
+ const last = pendingToolCalls.get(pendingToolCalls.size - 1);
117
+ if (last) {
118
+ last.args += chunk.toolCall.arguments || chunk.content || '';
119
+ }
120
+ }
121
+ break;
122
+ case 'done':
123
+ break;
124
+ case 'error':
125
+ callbacks.onStateChange?.('error', chunk.content || 'Stream error');
126
+ callbacks.onError(new Error(chunk.content || 'Stream error'));
127
+ break;
128
+ }
129
+ }
130
+ }
131
+ catch (err) {
132
+ callbacks.onStateChange?.('error', err.message);
133
+ callbacks.onError(err);
134
+ break;
135
+ }
136
+ // ── STAGE 3: PLAN ──
137
+ const detectedPlan = parsePlan(assistantText);
138
+ if (detectedPlan) {
139
+ callbacks.onPlan(detectedPlan);
140
+ }
141
+ // Finalize pending tool calls
142
+ for (const [, tc] of pendingToolCalls) {
143
+ let parsedArgs = {};
144
+ try {
145
+ parsedArgs = JSON.parse(tc.args || '{}');
146
+ }
147
+ catch {
148
+ // malformed args, default to empty object
149
+ }
150
+ toolCalls.push({ id: tc.id, name: tc.name, arguments: parsedArgs });
151
+ }
152
+ this.messages.push({
153
+ role: 'assistant',
154
+ content: assistantText,
155
+ toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
156
+ });
157
+ finalResponse = assistantText;
158
+ if (toolCalls.length === 0) {
159
+ callbacks.onStateChange?.('success', 'Task complete');
160
+ break;
161
+ }
162
+ // ── STAGE 4: APPROVE & PRE-MUTATION CHECKPOINTS ──
163
+ const hasMutations = toolCalls.some((call) => {
164
+ const tool = this.toolRegistry.get(call.name);
165
+ return tool && classifyAction(tool.definition) !== PermissionTier.ReadOnly;
166
+ });
167
+ if (hasMutations && ccssConfig.requireCheckpoint && !hasCreatedCheckpoint) {
168
+ try {
169
+ await this.checkpointManager.create(`pre_mutation_${Date.now()}`);
170
+ hasCreatedCheckpoint = true;
171
+ }
172
+ catch {
173
+ // non-fatal
174
+ }
175
+ }
176
+ // ── STAGE 2 & 5: INSPECT (CONCURRENT) & EXECUTE (SEQUENTIAL) ──
177
+ // Partition tool calls into contiguous blocks:
178
+ // - Run contiguous read-only calls concurrently if parallelInspection is enabled
179
+ // - Run mutating calls strictly sequentially to preserve file system determinism
180
+ let i = 0;
181
+ while (i < toolCalls.length) {
182
+ if (isAborted())
183
+ break;
184
+ const call = toolCalls[i];
185
+ const tool = this.toolRegistry.get(call.name);
186
+ if (!tool) {
187
+ const result = { success: false, output: '', error: `Tool '${call.name}' not found` };
188
+ callbacks.onToolEnd(call.name, result);
189
+ this.messages.push({ role: 'tool', content: JSON.stringify(result), toolCallId: call.id });
190
+ i++;
191
+ continue;
192
+ }
193
+ const isReadOnly = classifyAction(tool.definition) === PermissionTier.ReadOnly;
194
+ // Check if we can run a batch of contiguous read-only tools concurrently
195
+ if (isReadOnly && ccssConfig.parallelInspection) {
196
+ const readOnlyBatch = [];
197
+ while (i < toolCalls.length) {
198
+ const nextCall = toolCalls[i];
199
+ const nextTool = this.toolRegistry.get(nextCall.name);
200
+ if (nextTool && classifyAction(nextTool.definition) === PermissionTier.ReadOnly) {
201
+ readOnlyBatch.push(nextCall);
202
+ i++;
203
+ }
204
+ else {
205
+ break;
206
+ }
207
+ }
208
+ if (readOnlyBatch.length > 1) {
209
+ // Concurrent execution
210
+ callbacks.onStateChange?.('searching', `Inspecting ${readOnlyBatch.length} sources concurrently...`);
211
+ const promises = readOnlyBatch.map(async (batchCall) => {
212
+ const bTool = this.toolRegistry.get(batchCall.name);
213
+ callbacks.onToolStart(batchCall.name, batchCall.arguments);
214
+ const bRes = await this.toolRegistry.execute(batchCall.name, batchCall.arguments);
215
+ callbacks.onToolEnd(batchCall.name, bRes);
216
+ epistemicState.toolsRunCount++;
217
+ return { call: batchCall, result: bRes };
218
+ });
219
+ const batchResults = await Promise.all(promises);
220
+ for (const item of batchResults) {
221
+ this.messages.push({
222
+ role: 'tool',
223
+ content: JSON.stringify(item.result),
224
+ toolCallId: item.call.id,
225
+ });
226
+ }
227
+ continue;
228
+ }
229
+ else if (readOnlyBatch.length === 1) {
230
+ // Single call, proceed to normal execution
231
+ // (i is already incremented)
232
+ const singleCall = readOnlyBatch[0];
233
+ await this.executeSingleToolCall(singleCall, tool, callbacks, epistemicState);
234
+ continue;
235
+ }
236
+ }
237
+ // Sequential execution for mutating or non-parallel tools
238
+ await this.executeSingleToolCall(call, tool, callbacks, epistemicState);
239
+ i++;
240
+ }
241
+ }
242
+ // ── STAGE 6: VERIFY ──
243
+ if (epistemicState.modifiedFiles.size > 0 && ccssConfig.verificationIntensity !== 'none') {
244
+ callbacks.onStateChange?.('testing', 'Validating changes against project verification suite...');
245
+ epistemicState.verificationRun = true;
246
+ }
247
+ // ── STAGE 7: REPORT ──
248
+ const report = {
249
+ goal: epistemicState.goal,
250
+ ccssMode: modeKey,
251
+ iterations,
252
+ filesModified: Array.from(epistemicState.modifiedFiles),
253
+ toolsExecuted: epistemicState.toolsRunCount,
254
+ verificationStatus: epistemicState.modifiedFiles.size === 0
255
+ ? 'skipped'
256
+ : epistemicState.verificationRun
257
+ ? 'verified'
258
+ : 'unverified',
259
+ };
260
+ callbacks.onReport?.(report);
261
+ callbacks.onStateChange?.('idle');
262
+ return finalResponse;
263
+ }
264
+ async executeSingleToolCall(call, tool, callbacks, epistemicState) {
265
+ // Check loop / duplicate call prevention
266
+ const callSignature = `${call.name}:${JSON.stringify(call.arguments)}`;
267
+ const failureCount = epistemicState.recentFailures.get(callSignature) || 0;
268
+ if (failureCount >= 2) {
269
+ const result = {
270
+ success: false,
271
+ output: '',
272
+ error: `Loop prevention: '${call.name}' with identical arguments failed ${failureCount} times in succession. Formulate an alternative plan or check available files.`,
273
+ };
274
+ callbacks.onToolEnd(call.name, result);
275
+ this.messages.push({ role: 'tool', content: JSON.stringify(result), toolCallId: call.id });
276
+ return;
277
+ }
278
+ // Map tool to specific companion state
279
+ const toolState = this.mapToolToState(call.name);
280
+ callbacks.onStateChange?.(toolState, `Executing ${call.name}`);
281
+ // Standalone CCHCFAI Policy Check (Safe vs Danger, Safe-Only Mode)
282
+ const policyResult = policyEngine.checkPolicy(call.name, call.arguments);
283
+ if (!policyResult.allowed) {
284
+ callbacks.onStateChange?.('warning', `Policy blocked ${call.name}`);
285
+ const result = {
286
+ success: false,
287
+ output: '',
288
+ error: policyResult.reason || `Action '${call.name}' blocked by CCHCFAI safety policy`,
289
+ };
290
+ callbacks.onToolEnd(call.name, result);
291
+ this.messages.push({ role: 'tool', content: JSON.stringify(result), toolCallId: call.id });
292
+ return;
293
+ }
294
+ // Interactive Terminal Approval Flow for REQUEST_APPROVAL and ESCALATE
295
+ if (policyResult.actionState === ActionState.REQUEST_APPROVAL || policyResult.actionState === ActionState.ESCALATE) {
296
+ const cchcfai = policyEngine.evaluateCchcfai(call.name, call.arguments);
297
+ if (!this.options.autoApprove && this.config.agentMode !== 'autonomous' && !policyEngine.isApprovedForSession(call.name, cchcfai.capability)) {
298
+ callbacks.onStateChange?.('awaiting_approval', `Approval required for ${call.name}`);
299
+ const decision = await promptActionApproval({
300
+ action: call.name,
301
+ capability: cchcfai.capability,
302
+ riskLevel: cchcfai.riskLevel,
303
+ reason: cchcfai.reason,
304
+ params: call.arguments,
305
+ });
306
+ if (decision === 'deny_stop') {
307
+ abort();
308
+ callbacks.onStateChange?.('stopped', 'Execution stopped by user approval denial');
309
+ const result = {
310
+ success: false,
311
+ output: '',
312
+ error: 'Action denied and execution aborted by user',
313
+ };
314
+ callbacks.onToolEnd(call.name, result);
315
+ this.messages.push({ role: 'tool', content: JSON.stringify(result), toolCallId: call.id });
316
+ return;
317
+ }
318
+ if (decision === 'deny') {
319
+ callbacks.onStateChange?.('warning', `Action denied by user`);
320
+ const result = {
321
+ success: false,
322
+ output: '',
323
+ error: `Action '${call.name}' denied by user approval`,
324
+ };
325
+ callbacks.onToolEnd(call.name, result);
326
+ this.messages.push({ role: 'tool', content: JSON.stringify(result), toolCallId: call.id });
327
+ return;
328
+ }
329
+ }
330
+ }
331
+ // Check CCHCFAI permissions
332
+ const perm = await checkPermission(tool.definition, call.arguments, this.config.permissions, {
333
+ autoApprove: this.options.autoApprove || this.config.agentMode === 'autonomous',
334
+ rl: this.options.rl,
335
+ });
336
+ if (!perm.allowed) {
337
+ callbacks.onStateChange?.('warning', `Permission denied for ${call.name}`);
338
+ const result = { success: false, output: '', error: perm.reason || 'Permission denied' };
339
+ callbacks.onToolEnd(call.name, result);
340
+ this.messages.push({ role: 'tool', content: JSON.stringify(result), toolCallId: call.id });
341
+ return;
342
+ }
343
+ callbacks.onToolStart(call.name, call.arguments);
344
+ const result = await this.toolRegistry.execute(call.name, call.arguments);
345
+ callbacks.onToolEnd(call.name, result);
346
+ this.messages.push({ role: 'tool', content: JSON.stringify(result), toolCallId: call.id });
347
+ epistemicState.toolsRunCount++;
348
+ // Track modified files and self-correction learning
349
+ if (result.success) {
350
+ if (failureCount > 0) {
351
+ // Self-correction succeeded! Record episodic lesson for future tasks
352
+ this.memoryManager.recordLesson({
353
+ problem: `Tool '${call.name}' previously failed with arguments: ${JSON.stringify(call.arguments).slice(0, 100)}`,
354
+ investigation: `Diagnosed execution failure or argument mismatch in ${call.name}`,
355
+ resolution: `Adjusted invocation parameters or environment state to resolve failure`,
356
+ lesson: `Ensure preconditions, file existence, and parameter formats are verified before calling ${call.name}`,
357
+ tags: [call.name, 'self-correction', 'runtime'],
358
+ scope: 'project'
359
+ }).catch(() => { });
360
+ }
361
+ epistemicState.recentFailures.delete(callSignature);
362
+ if (typeof call.arguments?.path === 'string') {
363
+ const lower = call.name.toLowerCase();
364
+ if (lower.includes('write') || lower.includes('edit') || lower.includes('delete') || lower.includes('patch')) {
365
+ epistemicState.modifiedFiles.add(call.arguments.path);
366
+ }
367
+ }
368
+ }
369
+ else {
370
+ epistemicState.recentFailures.set(callSignature, failureCount + 1);
371
+ callbacks.onStateChange?.('warning', `Tool ${call.name} failed`);
372
+ }
373
+ }
374
+ mapToolToState(toolName) {
375
+ const lower = toolName.toLowerCase();
376
+ if (lower.includes('read') || lower.includes('list'))
377
+ return 'reading';
378
+ if (lower.includes('grep') || lower.includes('find') || lower.includes('search') || lower.includes('symbol'))
379
+ return 'searching';
380
+ if (lower.includes('edit') || lower.includes('write') || lower.includes('patch'))
381
+ return 'editing';
382
+ if (lower.includes('test') || lower.includes('typecheck') || lower.includes('lint') || lower.includes('verify'))
383
+ return 'testing';
384
+ if (lower.includes('shell') || lower.includes('command') || lower.includes('git') || lower.includes('checkpoint'))
385
+ return 'executing';
386
+ return 'thinking';
387
+ }
388
+ getMemoryManager() {
389
+ return this.memoryManager;
390
+ }
391
+ abort() {
392
+ abort();
393
+ }
394
+ getMessages() {
395
+ return this.messages;
396
+ }
397
+ setMessages(messages) {
398
+ this.messages = messages;
399
+ }
400
+ }
@@ -0,0 +1,13 @@
1
+ import { Capability, RiskLevel } from '../security/policy.js';
2
+ export type ApprovalDecision = 'approve_once' | 'approve_session' | 'deny' | 'deny_stop';
3
+ export interface ActionApprovalRequest {
4
+ action: string;
5
+ capability: Capability;
6
+ riskLevel: RiskLevel;
7
+ reason?: string;
8
+ params?: Record<string, any>;
9
+ }
10
+ /**
11
+ * Interactive keyboard-driven terminal approval prompt for REQUEST_APPROVAL and ESCALATE.
12
+ */
13
+ export declare function promptActionApproval(request: ActionApprovalRequest): Promise<ApprovalDecision>;
@@ -0,0 +1,71 @@
1
+ import * as readline from 'node:readline';
2
+ import { styleText } from 'node:util';
3
+ import { RiskLevel, policyEngine } from '../security/policy.js';
4
+ /**
5
+ * Interactive keyboard-driven terminal approval prompt for REQUEST_APPROVAL and ESCALATE.
6
+ */
7
+ export async function promptActionApproval(request) {
8
+ // If already approved for this session, auto-approve immediately
9
+ if (policyEngine.isApprovedForSession(request.action, request.capability)) {
10
+ return 'approve_session';
11
+ }
12
+ // Check if running in non-interactive environment (CI, pipe, or non-TTY)
13
+ if (!process.stdin.isTTY) {
14
+ return 'deny';
15
+ }
16
+ const border = '────────────────────────────────────────────────────────────';
17
+ const riskColor = (request.riskLevel === RiskLevel.R4 || request.riskLevel === RiskLevel.R5)
18
+ ? 'red'
19
+ : (request.riskLevel === RiskLevel.R2 || request.riskLevel === RiskLevel.R3 ? 'yellow' : 'cyan');
20
+ console.log('\n' + styleText('yellow', '┌' + border + '┐'));
21
+ console.log(styleText('yellow', '│ ') + styleText(['bold', 'yellow'], '◆ CCHCFAI HUMAN APPROVAL REQUIRED') + ' '.repeat(Math.max(0, 31)) + styleText('yellow', '│'));
22
+ console.log(styleText('yellow', '├' + border + '┤'));
23
+ console.log(styleText('yellow', '│ ') + styleText('bold', 'Action: ') + styleText('cyan', request.action.padEnd(46)) + styleText('yellow', '│'));
24
+ console.log(styleText('yellow', '│ ') + styleText('bold', 'Capability: ') + request.capability.padEnd(46) + styleText('yellow', '│'));
25
+ console.log(styleText('yellow', '│ ') + styleText('bold', 'Risk Level: ') + styleText(riskColor, request.riskLevel.padEnd(46)) + styleText('yellow', '│'));
26
+ if (request.reason) {
27
+ const truncatedReason = request.reason.length > 44 ? request.reason.slice(0, 41) + '...' : request.reason;
28
+ console.log(styleText('yellow', '│ ') + styleText('bold', 'Reason: ') + truncatedReason.padEnd(46) + styleText('yellow', '│'));
29
+ }
30
+ if (request.params && Object.keys(request.params).length > 0) {
31
+ const paramStr = JSON.stringify(request.params);
32
+ const truncated = paramStr.length > 44 ? paramStr.slice(0, 41) + '...' : paramStr;
33
+ console.log(styleText('yellow', '│ ') + styleText('bold', 'Params: ') + truncated.padEnd(46) + styleText('yellow', '│'));
34
+ }
35
+ console.log(styleText('yellow', '├' + border + '┤'));
36
+ console.log(styleText('yellow', '│ ') + `[${styleText('green', 'y')}] Approve once [${styleText('cyan', 'a')}] Approve for session [${styleText('red', 'd')}] Deny [${styleText(['bold', 'red'], 's')}] Stop` + ' '.repeat(2) + styleText('yellow', '│'));
37
+ console.log(styleText('yellow', '└' + border + '┘'));
38
+ const rl = readline.createInterface({
39
+ input: process.stdin,
40
+ output: process.stdout,
41
+ });
42
+ return new Promise((resolve) => {
43
+ rl.question(styleText('bold', 'Choice [y/a/d/s] (default: d): '), (answer) => {
44
+ rl.close();
45
+ const choice = (answer || '').trim().toLowerCase();
46
+ switch (choice) {
47
+ case 'y':
48
+ case 'yes':
49
+ resolve('approve_once');
50
+ break;
51
+ case 'a':
52
+ case 'always':
53
+ case 'session':
54
+ policyEngine.approveForSession(request.action, request.capability);
55
+ resolve('approve_session');
56
+ break;
57
+ case 's':
58
+ case 'stop':
59
+ resolve('deny_stop');
60
+ break;
61
+ case 'd':
62
+ case 'deny':
63
+ case 'n':
64
+ case 'no':
65
+ default:
66
+ resolve('deny');
67
+ break;
68
+ }
69
+ });
70
+ });
71
+ }
@@ -0,0 +1,15 @@
1
+ export interface CliArgs {
2
+ command: string;
3
+ prompt?: string;
4
+ model?: string;
5
+ folder?: string;
6
+ compact: boolean;
7
+ setup: boolean;
8
+ resume: boolean;
9
+ help: boolean;
10
+ version: boolean;
11
+ verbose: boolean;
12
+ yes: boolean;
13
+ safeOnly: boolean;
14
+ }
15
+ export declare function parseCliArgs(argv?: string[]): CliArgs;
@@ -0,0 +1,47 @@
1
+ import { parseArgs } from 'node:util';
2
+ export function parseCliArgs(argv = process.argv.slice(2)) {
3
+ const { values, positionals } = parseArgs({
4
+ args: argv,
5
+ options: {
6
+ model: { type: 'string' },
7
+ folder: { type: 'string', short: 'f' },
8
+ compact: { type: 'boolean' },
9
+ setup: { type: 'boolean' },
10
+ resume: { type: 'boolean', short: 'r' },
11
+ help: { type: 'boolean', short: 'h' },
12
+ version: { type: 'boolean', short: 'v' },
13
+ verbose: { type: 'boolean' },
14
+ yes: { type: 'boolean', short: 'y' },
15
+ 'safe-only': { type: 'boolean' }
16
+ },
17
+ allowPositionals: true
18
+ });
19
+ const knownCommands = ['chat', 'init', 'doctor', 'config', 'status', 'models', 'sessions', 'resume', 'setup', 'desktop', 'gui', 'memory', 'learn', 'companion', 'petdex', 'audit', 'update'];
20
+ let command = 'chat';
21
+ let prompt = undefined;
22
+ if (positionals.length > 0) {
23
+ if (knownCommands.includes(positionals[0])) {
24
+ command = positionals[0];
25
+ if (positionals.length > 1) {
26
+ prompt = positionals.slice(1).join(' ');
27
+ }
28
+ }
29
+ else {
30
+ prompt = positionals.join(' ');
31
+ }
32
+ }
33
+ return {
34
+ command,
35
+ prompt,
36
+ model: values.model,
37
+ folder: values.folder,
38
+ compact: values.compact ?? false,
39
+ setup: values.setup ?? false,
40
+ resume: values.resume ?? false,
41
+ help: values.help ?? false,
42
+ version: values.version ?? false,
43
+ verbose: values.verbose ?? false,
44
+ yes: values.yes ?? false,
45
+ safeOnly: values['safe-only'] ?? false
46
+ };
47
+ }
@@ -0,0 +1,10 @@
1
+ export interface AuditCommandArgs {
2
+ risk?: string;
3
+ decision?: string;
4
+ action?: string;
5
+ limit?: number;
6
+ exportPath?: string;
7
+ json?: boolean;
8
+ }
9
+ export declare function parseAuditArgs(args: string[]): AuditCommandArgs;
10
+ export declare function handleAuditCommand(args: string[]): Promise<void>;
@@ -0,0 +1,79 @@
1
+ import { styleText } from 'node:util';
2
+ import { auditLogger } from '../../security/audit.js';
3
+ import { RiskLevel } from '../../security/policy.js';
4
+ import * as ui from '../ui.js';
5
+ export function parseAuditArgs(args) {
6
+ const options = {};
7
+ for (let i = 0; i < args.length; i++) {
8
+ const arg = args[i];
9
+ if (arg === '--risk' && args[i + 1]) {
10
+ options.risk = args[++i].toUpperCase();
11
+ }
12
+ else if (arg === '--decision' && args[i + 1]) {
13
+ options.decision = args[++i].toUpperCase();
14
+ }
15
+ else if (arg === '--action' && args[i + 1]) {
16
+ options.action = args[++i];
17
+ }
18
+ else if (arg === '--limit' && args[i + 1]) {
19
+ options.limit = parseInt(args[++i], 10);
20
+ }
21
+ else if (arg === '--export' && args[i + 1]) {
22
+ options.exportPath = args[++i];
23
+ }
24
+ else if (arg === '--json') {
25
+ options.json = true;
26
+ }
27
+ }
28
+ return options;
29
+ }
30
+ export async function handleAuditCommand(args) {
31
+ const options = parseAuditArgs(args);
32
+ // Handle export request
33
+ if (options.exportPath) {
34
+ const ext = options.exportPath.endsWith('.csv')
35
+ ? 'csv'
36
+ : (options.exportPath.endsWith('.json') ? 'json' : 'jsonl');
37
+ const exported = auditLogger.exportEntries(options.exportPath, ext);
38
+ ui.success(`Audit log exported to: ${exported} (${ext.toUpperCase()} format)`);
39
+ return;
40
+ }
41
+ const filter = {
42
+ limit: options.limit || 25,
43
+ riskLevel: options.risk,
44
+ decision: options.decision,
45
+ action: options.action,
46
+ };
47
+ const records = auditLogger.readEntries(filter);
48
+ if (options.json) {
49
+ console.log(JSON.stringify(records, null, 2));
50
+ return;
51
+ }
52
+ console.log(`\n${ui.accent(ui.getAvatar())} ${ui.bold('Overarch CCHCFAI Policy Audit Log')}`);
53
+ console.log(ui.dim(` Location: ${auditLogger.getLogPath()}`));
54
+ console.log(ui.dim(` Total matching records: ${records.length}`));
55
+ console.log(ui.dim('─'.repeat(78)));
56
+ if (records.length === 0) {
57
+ console.log(ui.dim(' No audit records found matching query criteria.'));
58
+ console.log(ui.dim('─'.repeat(78)) + '\n');
59
+ return;
60
+ }
61
+ for (const rec of records) {
62
+ const timeStr = rec.timestamp.replace('T', ' ').slice(0, 19);
63
+ const decisionColor = rec.decision === 'ALLOWED'
64
+ ? 'green'
65
+ : (rec.decision === 'DENIED' || rec.decision === 'STOPPED' ? 'red' : 'yellow');
66
+ const riskColor = (rec.riskLevel === RiskLevel.R4 || rec.riskLevel === RiskLevel.R5)
67
+ ? 'red'
68
+ : (rec.riskLevel === RiskLevel.R2 || rec.riskLevel === RiskLevel.R3 ? 'yellow' : 'cyan');
69
+ console.log(`${ui.dim(timeStr)} ` +
70
+ `${styleText(decisionColor, rec.decision.padEnd(9))} ` +
71
+ `${styleText(riskColor, rec.riskLevel.padEnd(3))} ` +
72
+ `${ui.bold(rec.action.padEnd(16))} ` +
73
+ `${ui.dim(rec.capability.padEnd(20))}`);
74
+ if (rec.reason) {
75
+ console.log(ui.dim(` ↳ ${rec.reason}`));
76
+ }
77
+ }
78
+ console.log(ui.dim('─'.repeat(78)) + '\n');
79
+ }
@@ -0,0 +1 @@
1
+ export declare function handleCompanionCommand(subcommand?: string, arg1?: string, _arg2?: string): Promise<void>;