myshell-tools 1.0.0 → 2.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.
Files changed (153) hide show
  1. package/CHANGELOG.md +44 -69
  2. package/LICENSE +21 -21
  3. package/README.md +178 -318
  4. package/dist/cli.d.ts +8 -0
  5. package/dist/cli.js +130 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/cost.d.ts +36 -0
  8. package/dist/commands/cost.js +103 -0
  9. package/dist/commands/cost.js.map +1 -0
  10. package/dist/commands/doctor.d.ts +36 -0
  11. package/dist/commands/doctor.js +115 -0
  12. package/dist/commands/doctor.js.map +1 -0
  13. package/dist/commands/login.d.ts +20 -0
  14. package/dist/commands/login.js +60 -0
  15. package/dist/commands/login.js.map +1 -0
  16. package/dist/core/assess.d.ts +25 -0
  17. package/dist/core/assess.js +142 -0
  18. package/dist/core/assess.js.map +1 -0
  19. package/dist/core/classify.d.ts +19 -0
  20. package/dist/core/classify.js +80 -0
  21. package/dist/core/classify.js.map +1 -0
  22. package/dist/core/escalate.d.ts +32 -0
  23. package/dist/core/escalate.js +57 -0
  24. package/dist/core/escalate.js.map +1 -0
  25. package/dist/core/index.d.ts +13 -0
  26. package/dist/core/index.js +12 -0
  27. package/dist/core/index.js.map +1 -0
  28. package/dist/core/orchestrate.d.ts +42 -0
  29. package/dist/core/orchestrate.js +439 -0
  30. package/dist/core/orchestrate.js.map +1 -0
  31. package/dist/core/policy.d.ts +9 -0
  32. package/dist/core/policy.js +27 -0
  33. package/dist/core/policy.js.map +1 -0
  34. package/dist/core/prompt.d.ts +26 -0
  35. package/dist/core/prompt.js +125 -0
  36. package/dist/core/prompt.js.map +1 -0
  37. package/dist/core/review.d.ts +46 -0
  38. package/dist/core/review.js +148 -0
  39. package/dist/core/review.js.map +1 -0
  40. package/dist/core/route.d.ts +28 -0
  41. package/dist/core/route.js +52 -0
  42. package/dist/core/route.js.map +1 -0
  43. package/dist/core/types.d.ts +141 -0
  44. package/dist/core/types.js +14 -0
  45. package/dist/core/types.js.map +1 -0
  46. package/dist/infra/atomic.d.ts +53 -0
  47. package/dist/infra/atomic.js +171 -0
  48. package/dist/infra/atomic.js.map +1 -0
  49. package/dist/infra/clock.d.ts +9 -0
  50. package/dist/infra/clock.js +15 -0
  51. package/dist/infra/clock.js.map +1 -0
  52. package/dist/infra/index.d.ts +9 -0
  53. package/dist/infra/index.js +7 -0
  54. package/dist/infra/index.js.map +1 -0
  55. package/dist/infra/ledger.d.ts +49 -0
  56. package/dist/infra/ledger.js +90 -0
  57. package/dist/infra/ledger.js.map +1 -0
  58. package/dist/infra/paths.d.ts +28 -0
  59. package/dist/infra/paths.js +38 -0
  60. package/dist/infra/paths.js.map +1 -0
  61. package/dist/infra/pricing.d.ts +47 -0
  62. package/dist/infra/pricing.js +151 -0
  63. package/dist/infra/pricing.js.map +1 -0
  64. package/dist/infra/session.d.ts +28 -0
  65. package/dist/infra/session.js +61 -0
  66. package/dist/infra/session.js.map +1 -0
  67. package/dist/interface/render.d.ts +27 -0
  68. package/dist/interface/render.js +134 -0
  69. package/dist/interface/render.js.map +1 -0
  70. package/dist/interface/repl.d.ts +23 -0
  71. package/dist/interface/repl.js +90 -0
  72. package/dist/interface/repl.js.map +1 -0
  73. package/dist/interface/run.d.ts +20 -0
  74. package/dist/interface/run.js +31 -0
  75. package/dist/interface/run.js.map +1 -0
  76. package/dist/providers/claude-parse.d.ts +24 -0
  77. package/dist/providers/claude-parse.js +113 -0
  78. package/dist/providers/claude-parse.js.map +1 -0
  79. package/dist/providers/claude.d.ts +45 -0
  80. package/dist/providers/claude.js +122 -0
  81. package/dist/providers/claude.js.map +1 -0
  82. package/dist/providers/codex-parse.d.ts +32 -0
  83. package/dist/providers/codex-parse.js +145 -0
  84. package/dist/providers/codex-parse.js.map +1 -0
  85. package/dist/providers/codex.d.ts +44 -0
  86. package/dist/providers/codex.js +124 -0
  87. package/dist/providers/codex.js.map +1 -0
  88. package/dist/providers/detect.d.ts +49 -0
  89. package/dist/providers/detect.js +125 -0
  90. package/dist/providers/detect.js.map +1 -0
  91. package/dist/providers/errors.d.ts +49 -0
  92. package/dist/providers/errors.js +189 -0
  93. package/dist/providers/errors.js.map +1 -0
  94. package/dist/providers/index.d.ts +9 -0
  95. package/dist/providers/index.js +7 -0
  96. package/dist/providers/index.js.map +1 -0
  97. package/dist/providers/port.d.ts +74 -0
  98. package/dist/providers/port.js +16 -0
  99. package/dist/providers/port.js.map +1 -0
  100. package/dist/providers/registry.d.ts +21 -0
  101. package/dist/providers/registry.js +34 -0
  102. package/dist/providers/registry.js.map +1 -0
  103. package/dist/ui/banner.d.ts +19 -0
  104. package/dist/ui/banner.js +32 -0
  105. package/dist/ui/banner.js.map +1 -0
  106. package/dist/ui/spinner.d.ts +27 -0
  107. package/dist/ui/spinner.js +67 -0
  108. package/dist/ui/spinner.js.map +1 -0
  109. package/dist/ui/theme.d.ts +32 -0
  110. package/dist/ui/theme.js +56 -0
  111. package/dist/ui/theme.js.map +1 -0
  112. package/package.json +55 -49
  113. package/data/orchestrator.json +0 -113
  114. package/src/auth/recovery.mjs +0 -328
  115. package/src/auth/refresh.mjs +0 -373
  116. package/src/chef.mjs +0 -348
  117. package/src/cli/doctor.mjs +0 -568
  118. package/src/cli/reset.mjs +0 -447
  119. package/src/cli/status.mjs +0 -379
  120. package/src/cli.mjs +0 -429
  121. package/src/commands/doctor.mjs +0 -375
  122. package/src/commands/help.mjs +0 -324
  123. package/src/commands/status.mjs +0 -331
  124. package/src/monitor/health.mjs +0 -486
  125. package/src/monitor/performance.mjs +0 -442
  126. package/src/monitor/report.mjs +0 -535
  127. package/src/orchestrator/classify.mjs +0 -391
  128. package/src/orchestrator/confidence.mjs +0 -151
  129. package/src/orchestrator/handoffs.mjs +0 -231
  130. package/src/orchestrator/review.mjs +0 -222
  131. package/src/providers/balance.mjs +0 -201
  132. package/src/providers/claude.mjs +0 -236
  133. package/src/providers/codex.mjs +0 -255
  134. package/src/providers/detect.mjs +0 -185
  135. package/src/providers/errors.mjs +0 -373
  136. package/src/providers/select.mjs +0 -162
  137. package/src/repl-enhanced.mjs +0 -417
  138. package/src/repl.mjs +0 -321
  139. package/src/state/archive.mjs +0 -366
  140. package/src/state/atomic.mjs +0 -116
  141. package/src/state/cleanup.mjs +0 -440
  142. package/src/state/recovery.mjs +0 -461
  143. package/src/state/session.mjs +0 -147
  144. package/src/ui/errors.mjs +0 -456
  145. package/src/ui/formatter.mjs +0 -327
  146. package/src/ui/icons.mjs +0 -318
  147. package/src/ui/progress.mjs +0 -468
  148. package/templates/prompts/confidence-format.txt +0 -14
  149. package/templates/prompts/ic-with-feedback.txt +0 -41
  150. package/templates/prompts/ic.txt +0 -13
  151. package/templates/prompts/manager-review.txt +0 -40
  152. package/templates/prompts/manager.txt +0 -14
  153. package/templates/prompts/worker.txt +0 -12
@@ -1,373 +0,0 @@
1
- /**
2
- * refresh.mjs — OAuth token refresh system for Claude and Codex
3
- * Adapted from archive/dual-brain/install.mjs
4
- */
5
-
6
- import https from 'https';
7
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
8
- import { join, resolve, dirname } from 'path';
9
- import { atomicWriteJSON } from '../state/atomic.mjs';
10
-
11
- /**
12
- * HTTP POST form helper
13
- */
14
- function postForm(url, body) {
15
- return new Promise((resolve, reject) => {
16
- const payload = Buffer.from(new URLSearchParams(body).toString(), 'utf8');
17
- const req = https.request(url, {
18
- method: 'POST',
19
- headers: {
20
- 'Content-Type': 'application/x-www-form-urlencoded',
21
- 'Content-Length': payload.length,
22
- },
23
- timeout: 8000,
24
- }, (res) => {
25
- const chunks = [];
26
- res.on('data', (chunk) => chunks.push(chunk));
27
- res.on('end', () => {
28
- const raw = Buffer.concat(chunks).toString('utf8');
29
- if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
30
- try {
31
- resolve(JSON.parse(raw));
32
- } catch (err) {
33
- reject(err);
34
- }
35
- } else {
36
- reject(new Error(`HTTP ${res.statusCode || 0}: ${raw}`));
37
- }
38
- });
39
- });
40
- req.on('timeout', () => req.destroy(new Error('Request timeout')));
41
- req.on('error', reject);
42
- req.write(payload);
43
- req.end();
44
- });
45
- }
46
-
47
- /**
48
- * Safe JSON parsing with fallback
49
- */
50
- function safeParseJson(path) {
51
- try {
52
- return JSON.parse(readFileSync(path, 'utf8'));
53
- } catch {
54
- return null;
55
- }
56
- }
57
-
58
- /**
59
- * Decode JWT payload without verification
60
- */
61
- function decodeJwtPayload(token) {
62
- if (!token || typeof token !== 'string') return null;
63
- const parts = token.split('.');
64
- if (parts.length < 2) return null;
65
- try {
66
- return JSON.parse(Buffer.from(parts[1], 'base64url').toString());
67
- } catch {
68
- return null;
69
- }
70
- }
71
-
72
- /**
73
- * Compute expiration time in hours
74
- */
75
- function computeExpiresInHours(expiresAtMs) {
76
- if (!Number.isFinite(expiresAtMs)) return null;
77
- return Math.round(((expiresAtMs - Date.now()) / 3_600_000) * 10) / 10;
78
- }
79
-
80
- /**
81
- * Get potential Claude credential file paths
82
- */
83
- function getClaudeCredentialPaths(workspace = process.cwd()) {
84
- const configDir = process.env.CLAUDE_CONFIG_DIR;
85
- const home = process.env.HOME || '';
86
- return [
87
- configDir ? join(configDir, '.credentials.json') : null,
88
- join(home, '.claude', '.credentials.json'),
89
- join(home, '.claude', 'credentials.json'),
90
- resolve(workspace, '.replit-tools', '.claude-persistent', '.credentials.json'),
91
- ].filter(Boolean);
92
- }
93
-
94
- /**
95
- * Get Codex auth file path
96
- */
97
- function getCodexAuthPath() {
98
- const home = process.env.HOME || '';
99
- return join(home, '.codex', 'auth.json');
100
- }
101
-
102
- /**
103
- * Refresh Claude OAuth token
104
- */
105
- async function refreshClaudeToken() {
106
- const credPaths = getClaudeCredentialPaths();
107
-
108
- for (const credPath of credPaths) {
109
- if (!existsSync(credPath)) continue;
110
-
111
- try {
112
- const cred = safeParseJson(credPath);
113
- const oauth = cred?.claudeAiOauth;
114
- if (!oauth?.refreshToken) continue;
115
-
116
- const refreshed = await postForm('https://console.anthropic.com/v1/oauth/token', {
117
- grant_type: 'refresh_token',
118
- refresh_token: oauth.refreshToken,
119
- client_id: '9d1c250a-e61b-44d9-88ed-5944d1962f5e',
120
- });
121
-
122
- const nextOauth = {
123
- ...oauth,
124
- accessToken: refreshed.access_token || oauth.accessToken,
125
- refreshToken: refreshed.refresh_token || oauth.refreshToken,
126
- tokenType: refreshed.token_type || oauth.tokenType,
127
- scopes: refreshed.scope || oauth.scopes,
128
- expiresAt: Date.now() + ((refreshed.expires_in || 0) * 1000),
129
- };
130
-
131
- const updated = { ...cred, claudeAiOauth: nextOauth };
132
- atomicWriteJSON(credPath, updated);
133
-
134
- return {
135
- success: true,
136
- provider: 'claude',
137
- expiresInHours: computeExpiresInHours(nextOauth.expiresAt),
138
- refreshed: true
139
- };
140
- } catch (error) {
141
- return {
142
- success: false,
143
- provider: 'claude',
144
- action: 'reauth_required',
145
- error: error.message
146
- };
147
- }
148
- }
149
-
150
- return {
151
- success: false,
152
- provider: 'claude',
153
- action: 'no_credentials',
154
- error: 'No Claude credentials found'
155
- };
156
- }
157
-
158
- /**
159
- * Refresh OpenAI/Codex token
160
- */
161
- async function refreshOpenAIToken() {
162
- const authPath = getCodexAuthPath();
163
-
164
- if (!existsSync(authPath)) {
165
- return {
166
- success: false,
167
- provider: 'openai',
168
- action: 'no_credentials',
169
- error: 'No Codex auth file found'
170
- };
171
- }
172
-
173
- try {
174
- const auth = safeParseJson(authPath);
175
- const tokens = auth?.tokens || auth;
176
- const refreshToken = tokens?.refresh_token;
177
-
178
- if (!refreshToken) {
179
- return {
180
- success: false,
181
- provider: 'openai',
182
- action: 'no_refresh_token',
183
- error: 'No refresh token available'
184
- };
185
- }
186
-
187
- const refreshed = await postForm('https://auth.openai.com/oauth/token', {
188
- grant_type: 'refresh_token',
189
- refresh_token: refreshToken,
190
- client_id: 'app_EMoamEEZ73f0CkXaXp7hrann',
191
- });
192
-
193
- const updatedTokens = {
194
- ...tokens,
195
- access_token: refreshed.access_token || tokens.access_token,
196
- refresh_token: refreshed.refresh_token || tokens.refresh_token,
197
- id_token: refreshed.id_token || tokens.id_token,
198
- token_type: refreshed.token_type || tokens.token_type,
199
- scope: refreshed.scope || tokens.scope,
200
- };
201
-
202
- const updated = auth?.tokens ? { ...auth, tokens: updatedTokens } : updatedTokens;
203
- atomicWriteJSON(authPath, updated);
204
-
205
- const payload = decodeJwtPayload(updatedTokens.access_token);
206
- return {
207
- success: true,
208
- provider: 'openai',
209
- expiresInHours: payload?.exp ? computeExpiresInHours(payload.exp * 1000) : null,
210
- refreshed: true
211
- };
212
- } catch (error) {
213
- return {
214
- success: false,
215
- provider: 'openai',
216
- action: 'reauth_required',
217
- error: error.message
218
- };
219
- }
220
- }
221
-
222
- /**
223
- * Refresh all available tokens
224
- */
225
- export async function refreshTokens() {
226
- const results = [];
227
-
228
- try {
229
- const claudeResult = await refreshClaudeToken();
230
- results.push(claudeResult);
231
- } catch (error) {
232
- results.push({
233
- success: false,
234
- provider: 'claude',
235
- error: error.message,
236
- action: 'reauth_required'
237
- });
238
- }
239
-
240
- try {
241
- const openaiResult = await refreshOpenAIToken();
242
- results.push(openaiResult);
243
- } catch (error) {
244
- results.push({
245
- success: false,
246
- provider: 'openai',
247
- error: error.message,
248
- action: 'reauth_required'
249
- });
250
- }
251
-
252
- const successful = results.filter(r => r.success);
253
- const needReauth = results.filter(r => r.action === 'reauth_required');
254
-
255
- return {
256
- success: successful.length > 0,
257
- results,
258
- refreshed: successful.filter(r => r.refreshed),
259
- needReauth: needReauth,
260
- hasValidAuth: successful.length > 0
261
- };
262
- }
263
-
264
- /**
265
- * Get refresh state file path
266
- */
267
- function getRefreshStatePath() {
268
- const home = process.env.HOME || '';
269
- const dir = join(home, '.cortex', 'auth');
270
- if (!existsSync(dir)) {
271
- mkdirSync(dir, { recursive: true });
272
- }
273
- return join(dir, 'refresh-state.json');
274
- }
275
-
276
- /**
277
- * Save refresh state for background processing
278
- */
279
- export function saveRefreshState(results) {
280
- const statePath = getRefreshStatePath();
281
- const state = {
282
- lastRefresh: new Date().toISOString(),
283
- results,
284
- nextRefreshDue: new Date(Date.now() + (23 * 60 * 60 * 1000)).toISOString() // 23 hours
285
- };
286
-
287
- try {
288
- atomicWriteJSON(statePath, state);
289
- } catch (error) {
290
- console.warn('Failed to save refresh state:', error.message);
291
- }
292
- }
293
-
294
- /**
295
- * Load refresh state
296
- */
297
- export function loadRefreshState() {
298
- const statePath = getRefreshStatePath();
299
-
300
- if (!existsSync(statePath)) {
301
- return null;
302
- }
303
-
304
- try {
305
- return safeParseJson(statePath);
306
- } catch {
307
- return null;
308
- }
309
- }
310
-
311
- /**
312
- * Check if refresh is due (run every 24 hours)
313
- */
314
- export function isRefreshDue() {
315
- const state = loadRefreshState();
316
- if (!state?.nextRefreshDue) return true;
317
-
318
- return new Date() >= new Date(state.nextRefreshDue);
319
- }
320
-
321
- /**
322
- * Background token refresh with error handling
323
- */
324
- export async function backgroundRefresh() {
325
- if (!isRefreshDue()) {
326
- return { skipped: true, reason: 'Not due for refresh' };
327
- }
328
-
329
- try {
330
- const results = await refreshTokens();
331
- saveRefreshState(results.results);
332
-
333
- return {
334
- success: true,
335
- hasValidAuth: results.hasValidAuth,
336
- refreshed: results.refreshed.map(r => r.provider),
337
- needReauth: results.needReauth.map(r => r.provider)
338
- };
339
- } catch (error) {
340
- return {
341
- success: false,
342
- error: error.message
343
- };
344
- }
345
- }
346
-
347
- /**
348
- * Display refresh status to user
349
- */
350
- export function displayRefreshStatus(refreshResult) {
351
- if (refreshResult.skipped) {
352
- return; // Silent when not due
353
- }
354
-
355
- if (refreshResult.success) {
356
- if (refreshResult.refreshed.length > 0) {
357
- console.log(`🔄 Refreshed tokens: ${refreshResult.refreshed.join(', ')}`);
358
- }
359
-
360
- if (refreshResult.needReauth.length > 0) {
361
- console.log(`\n⚠️ Re-authentication required for: ${refreshResult.needReauth.join(', ')}`);
362
- for (const provider of refreshResult.needReauth) {
363
- if (provider === 'claude') {
364
- console.log(' Run: claude auth login');
365
- } else if (provider === 'openai') {
366
- console.log(' Run: codex login');
367
- }
368
- }
369
- }
370
- } else if (refreshResult.error) {
371
- console.log(`⚠️ Token refresh failed: ${refreshResult.error}`);
372
- }
373
- }
package/src/chef.mjs DELETED
@@ -1,348 +0,0 @@
1
- /**
2
- * chef.mjs — The three-tier hierarchical orchestration engine
3
- */
4
-
5
- import { executeClaude, buildClaudePrompt } from './providers/claude.mjs';
6
- import { executeCodex, buildCodexPrompt } from './providers/codex.mjs';
7
- import { classifyTask, shouldEscalate, selectModel } from './orchestrator/classify.mjs';
8
- import { addMessage, addHandoff } from './state/session.mjs';
9
- import { parseConfidence, shouldEscalateOnConfidence } from './orchestrator/confidence.mjs';
10
- import { logHandoff, logEscalation, logBounce, checkFailureLoop } from './orchestrator/handoffs.mjs';
11
- import { runManagerReview, shouldTriggerManagerReview } from './orchestrator/review.mjs';
12
- import { selectProvider, checkProviderHealth } from './providers/select.mjs';
13
- import { balanceProviderLoad } from './providers/balance.mjs';
14
- import { trackHandoff } from './monitor/performance.mjs';
15
-
16
- /**
17
- * Execute a task at a specific tier with intelligent provider selection
18
- */
19
- async function runTier(tier, task, context = {}) {
20
- const { availableModels, sessionId } = context;
21
-
22
- // Use intelligent provider selection with load balancing
23
- const model = selectProvider ?
24
- selectProvider(tier, { availableModels, sessionId }) :
25
- selectModel(tier, availableModels);
26
-
27
- if (!model) {
28
- return {
29
- success: false,
30
- error: `No available model for tier: ${tier}`,
31
- tier,
32
- model: null
33
- };
34
- }
35
-
36
- // Check provider health before executing
37
- if (checkProviderHealth) {
38
- const health = checkProviderHealth(model.provider);
39
- if (!health.available) {
40
- console.log(` ⚠️ ${model.provider} is degraded (${health.failures} recent failures)`);
41
- // Try to find alternative provider
42
- const tierModels = availableModels[tier] || [];
43
- const alternative = tierModels.find(m => m.provider !== model.provider);
44
- if (alternative) {
45
- console.log(` 🔄 Switching to ${alternative.provider}/${alternative.model}`);
46
- return runTier(tier, task, { ...context, availableModels: { [tier]: [alternative] } });
47
- }
48
- }
49
- }
50
-
51
- console.log(` ${tier.toUpperCase()} (${model.provider}/${model.model}): Working...`);
52
-
53
- const startTime = Date.now();
54
- let result;
55
- let prompt;
56
-
57
- try {
58
- // Build appropriate prompt for the provider and tier
59
- if (model.provider === 'claude') {
60
- prompt = buildClaudePrompt(tier, task, context);
61
- result = await executeClaude(model.bin, model.model, prompt, context.options);
62
- } else if (model.provider === 'codex') {
63
- prompt = buildCodexPrompt(tier, task, context);
64
- result = await executeCodex(model.bin, model.model, prompt, context.options);
65
- } else {
66
- return {
67
- success: false,
68
- error: `Unknown provider: ${model.provider}`,
69
- tier,
70
- model
71
- };
72
- }
73
-
74
- // Enhanced confidence parsing
75
- if (result.success && result.output) {
76
- const confidenceData = parseConfidence(result.output);
77
- result.confidence = confidenceData.confidence;
78
- result.escalate = confidenceData.escalate;
79
- result.reasoning = confidenceData.reason;
80
- result.needsReview = confidenceData.needsReview;
81
- result.structured = confidenceData.structured;
82
- }
83
-
84
- // Add metadata
85
- result.tier = tier;
86
- result.selectedModel = model;
87
- result.durationMs = Date.now() - startTime;
88
-
89
- // Log the handoff for audit trail
90
- if (logHandoff) {
91
- logHandoff('execute', 'user', tier, {
92
- providerfrom: 'user',
93
- providerTo: model.provider,
94
- confidenceOut: result.confidence,
95
- durationMs: result.durationMs,
96
- sessionId: context.sessionId,
97
- success: result.success
98
- });
99
- }
100
-
101
- // Track performance metrics
102
- trackHandoff({
103
- operation: 'execute',
104
- fromTier: 'user',
105
- toTier: tier,
106
- provider: model.provider,
107
- model: model.model,
108
- confidence: result.confidence,
109
- success: result.success,
110
- durationMs: result.durationMs,
111
- tokensUsed: result.usage?.total_tokens || null,
112
- prompt: task,
113
- output: result.output
114
- });
115
-
116
- return result;
117
-
118
- } catch (error) {
119
- const errorResult = {
120
- success: false,
121
- error: error.message,
122
- tier,
123
- model,
124
- output: '',
125
- durationMs: Date.now() - startTime
126
- };
127
-
128
- // Log failure for future routing decisions
129
- if (logHandoff) {
130
- logHandoff('execute_failed', 'user', tier, {
131
- providerFrom: 'user',
132
- providerTo: model.provider,
133
- durationMs: errorResult.durationMs,
134
- sessionId: context.sessionId,
135
- reason: error.message,
136
- success: false
137
- });
138
- }
139
-
140
- return errorResult;
141
- }
142
- }
143
-
144
- /**
145
- * The main orchestration function with Phase 2 smart routing enhancements
146
- */
147
- export async function chef(userMessage, context = {}) {
148
- console.log(`\nCortex AI Org Chart Processing: "${userMessage}"`);
149
-
150
- // Generate session ID for tracking
151
- const sessionId = context.sessionId || `session_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
152
- context.sessionId = sessionId;
153
-
154
- // Step 1: Enhanced task classification
155
- const classification = classifyTask(userMessage, context.fileContext);
156
- console.log(`Task Classification: ${classification.tier} tier, ${classification.risk} risk`);
157
- console.log(`Reason: ${classification.reason}`);
158
- console.log(`Confidence Estimate: ${(classification.confidence * 100).toFixed(0)}%`);
159
-
160
- // Check for failure loops before starting
161
- const taskHash = createTaskHash(userMessage);
162
- const failureCheck = checkFailureLoop ? checkFailureLoop(taskHash) : { isLoop: false };
163
-
164
- if (failureCheck.isLoop) {
165
- console.log(` ⚠️ Failure loop detected (${failureCheck.bounceCount} bounces, score: ${failureCheck.weightedScore.toFixed(1)})`);
166
- if (failureCheck.suggestion === 'escalate_to_manager') {
167
- classification.tier = 'manager';
168
- console.log(` ↗️ Auto-escalating to MANAGER due to failure loop`);
169
- }
170
- }
171
-
172
- // Step 2: Start with the classified tier (or IC as default)
173
- let currentTier = classification.tier === 'manager' ? 'manager' : 'ic';
174
- let attempts = 0;
175
- const maxAttempts = 3;
176
- let lastResult = null;
177
-
178
- while (attempts < maxAttempts) {
179
- attempts++;
180
- console.log(`\nAttempt ${attempts}: Starting at ${currentTier.toUpperCase()} tier`);
181
-
182
- // Execute at current tier
183
- const result = await runTier(currentTier, userMessage, {
184
- ...context,
185
- attempt: attempts,
186
- taskHash,
187
- managerNotes: context.managerNotes
188
- });
189
-
190
- if (!result.success) {
191
- console.log(` ❌ ${currentTier.toUpperCase()} failed: ${result.error}`);
192
-
193
- // Log escalation due to failure
194
- if (logEscalation && currentTier !== 'manager') {
195
- logEscalation(currentTier, 'manager', 'execution failure', {
196
- sessionId,
197
- attempt: attempts,
198
- taskHash,
199
- error: result.error
200
- });
201
- }
202
-
203
- if (currentTier !== 'manager') {
204
- console.log(` ↗️ ESCALATE → MANAGER (execution failure)`);
205
- addHandoff('escalate', currentTier, 'manager', 'execution failure');
206
- currentTier = 'manager';
207
- context.previous = result;
208
- continue;
209
- } else {
210
- return {
211
- success: false,
212
- output: result.error,
213
- tier: 'manager',
214
- totalAttempts: attempts,
215
- finalResult: result,
216
- sessionId
217
- };
218
- }
219
- }
220
-
221
- lastResult = result;
222
-
223
- // Log successful completion
224
- console.log(` ✓ ${currentTier.toUpperCase()} completed`);
225
- if (result.confidence !== null) {
226
- console.log(` Confidence: ${(result.confidence * 100).toFixed(0)}%`);
227
- }
228
-
229
- // Phase 2 Enhancement: Manager Review Pattern for high-stakes work
230
- if (currentTier === 'ic' && shouldTriggerManagerReview) {
231
- const reviewCheck = shouldTriggerManagerReview(userMessage, classification, result);
232
-
233
- if (reviewCheck.required) {
234
- console.log(` 🔍 Triggering manager review: ${reviewCheck.reason}`);
235
-
236
- const review = await runManagerReview(userMessage, result, context, runTier);
237
-
238
- if (review.verdict === 'bounce' && attempts < maxAttempts) {
239
- console.log(` ↩️ BOUNCE DOWN: Manager wants fixes (attempt ${attempts + 1})`);
240
-
241
- // Log bounce for audit trail
242
- if (logBounce) {
243
- logBounce('manager', 'ic', review.notes, attempts + 1, {
244
- sessionId,
245
- taskHash,
246
- originalConfidence: result.confidence,
247
- reviewConfidence: review.managerResult.confidence
248
- });
249
- }
250
-
251
- // Set up context for retry with manager feedback
252
- context.managerNotes = review.notes;
253
- context.attempt = attempts + 1;
254
-
255
- // Stay at IC tier but incorporate manager feedback
256
- continue; // This will increment attempts and retry
257
- } else if (review.verdict === 'escalate') {
258
- console.log(` ↗️ ESCALATE: Manager taking over directly`);
259
- currentTier = 'manager';
260
- context.previous = result;
261
- continue;
262
- } else if (review.verdict === 'approve') {
263
- console.log(` ✅ Manager approved IC work`);
264
- // Continue with approval
265
- }
266
- }
267
- }
268
-
269
- // Check if we should escalate based on confidence/risk
270
- const needsEscalation = shouldEscalate(result, classification) ||
271
- shouldEscalateOnConfidence(result.confidence, classification.risk, currentTier);
272
-
273
- if (needsEscalation && currentTier !== 'manager') {
274
- console.log(` ↗️ ESCALATE → MANAGER (${result.reasoning || 'low confidence'})`);
275
-
276
- if (logEscalation) {
277
- logEscalation(currentTier, 'manager', result.reasoning || 'low confidence', {
278
- sessionId,
279
- attempt: attempts,
280
- taskHash,
281
- confidence: result.confidence,
282
- riskLevel: classification.risk
283
- });
284
- }
285
-
286
- addHandoff('escalate', currentTier, 'manager', result.reasoning || 'low confidence', {
287
- confidence: result.confidence
288
- });
289
-
290
- currentTier = 'manager';
291
- context.previous = result;
292
- continue;
293
- }
294
-
295
- // Task completed successfully
296
- console.log(` 🎯 Task completed at ${currentTier.toUpperCase()} tier`);
297
-
298
- return {
299
- success: true,
300
- output: result.output,
301
- tier: currentTier,
302
- confidence: result.confidence,
303
- model: result.selectedModel,
304
- classification,
305
- totalAttempts: attempts,
306
- finalResult: result,
307
- sessionId,
308
- review: currentTier === 'ic' ? 'manager_approved' : 'direct_completion'
309
- };
310
- }
311
-
312
- return {
313
- success: false,
314
- output: 'Maximum escalation attempts reached',
315
- tier: currentTier,
316
- totalAttempts: attempts,
317
- error: 'Max attempts exceeded',
318
- sessionId,
319
- lastResult
320
- };
321
- }
322
-
323
- /**
324
- * Create a hash for task tracking across attempts
325
- */
326
- function createTaskHash(task) {
327
- // Simple hash for tracking related attempts
328
- return `task_${Buffer.from(task.slice(0, 100)).toString('base64').slice(0, 8)}`;
329
- }
330
-
331
- /**
332
- * Handle delegation to worker tier for simple tasks
333
- */
334
- export async function delegateToWorker(task, context = {}) {
335
- console.log(` ↘️ DELEGATE → WORKER`);
336
- addHandoff('delegate', 'ic', 'worker', 'simple task delegation');
337
-
338
- const result = await runTier('worker', task, context);
339
-
340
- if (!result.success || shouldEscalate(result, classifyTask(task))) {
341
- console.log(` ↗️ WORKER → IC (delegation failed or escalation needed)`);
342
- addHandoff('escalate', 'worker', 'ic', result.reasoning || 'worker task failed');
343
- return null; // Signal to retry at IC level
344
- }
345
-
346
- console.log(` ✓ WORKER completed delegation`);
347
- return result;
348
- }