myshell-tools 1.0.0 → 2.0.0-alpha.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 (150) 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 +106 -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/core/assess.d.ts +25 -0
  14. package/dist/core/assess.js +142 -0
  15. package/dist/core/assess.js.map +1 -0
  16. package/dist/core/classify.d.ts +19 -0
  17. package/dist/core/classify.js +80 -0
  18. package/dist/core/classify.js.map +1 -0
  19. package/dist/core/escalate.d.ts +32 -0
  20. package/dist/core/escalate.js +57 -0
  21. package/dist/core/escalate.js.map +1 -0
  22. package/dist/core/index.d.ts +13 -0
  23. package/dist/core/index.js +12 -0
  24. package/dist/core/index.js.map +1 -0
  25. package/dist/core/orchestrate.d.ts +42 -0
  26. package/dist/core/orchestrate.js +439 -0
  27. package/dist/core/orchestrate.js.map +1 -0
  28. package/dist/core/policy.d.ts +9 -0
  29. package/dist/core/policy.js +27 -0
  30. package/dist/core/policy.js.map +1 -0
  31. package/dist/core/prompt.d.ts +26 -0
  32. package/dist/core/prompt.js +125 -0
  33. package/dist/core/prompt.js.map +1 -0
  34. package/dist/core/review.d.ts +46 -0
  35. package/dist/core/review.js +148 -0
  36. package/dist/core/review.js.map +1 -0
  37. package/dist/core/route.d.ts +28 -0
  38. package/dist/core/route.js +52 -0
  39. package/dist/core/route.js.map +1 -0
  40. package/dist/core/types.d.ts +141 -0
  41. package/dist/core/types.js +14 -0
  42. package/dist/core/types.js.map +1 -0
  43. package/dist/infra/atomic.d.ts +53 -0
  44. package/dist/infra/atomic.js +171 -0
  45. package/dist/infra/atomic.js.map +1 -0
  46. package/dist/infra/clock.d.ts +9 -0
  47. package/dist/infra/clock.js +15 -0
  48. package/dist/infra/clock.js.map +1 -0
  49. package/dist/infra/index.d.ts +9 -0
  50. package/dist/infra/index.js +7 -0
  51. package/dist/infra/index.js.map +1 -0
  52. package/dist/infra/ledger.d.ts +49 -0
  53. package/dist/infra/ledger.js +90 -0
  54. package/dist/infra/ledger.js.map +1 -0
  55. package/dist/infra/paths.d.ts +28 -0
  56. package/dist/infra/paths.js +38 -0
  57. package/dist/infra/paths.js.map +1 -0
  58. package/dist/infra/pricing.d.ts +47 -0
  59. package/dist/infra/pricing.js +151 -0
  60. package/dist/infra/pricing.js.map +1 -0
  61. package/dist/infra/session.d.ts +28 -0
  62. package/dist/infra/session.js +61 -0
  63. package/dist/infra/session.js.map +1 -0
  64. package/dist/interface/render.d.ts +27 -0
  65. package/dist/interface/render.js +134 -0
  66. package/dist/interface/render.js.map +1 -0
  67. package/dist/interface/repl.d.ts +23 -0
  68. package/dist/interface/repl.js +90 -0
  69. package/dist/interface/repl.js.map +1 -0
  70. package/dist/interface/run.d.ts +20 -0
  71. package/dist/interface/run.js +31 -0
  72. package/dist/interface/run.js.map +1 -0
  73. package/dist/providers/claude-parse.d.ts +24 -0
  74. package/dist/providers/claude-parse.js +113 -0
  75. package/dist/providers/claude-parse.js.map +1 -0
  76. package/dist/providers/claude.d.ts +45 -0
  77. package/dist/providers/claude.js +122 -0
  78. package/dist/providers/claude.js.map +1 -0
  79. package/dist/providers/codex-parse.d.ts +32 -0
  80. package/dist/providers/codex-parse.js +145 -0
  81. package/dist/providers/codex-parse.js.map +1 -0
  82. package/dist/providers/codex.d.ts +44 -0
  83. package/dist/providers/codex.js +124 -0
  84. package/dist/providers/codex.js.map +1 -0
  85. package/dist/providers/detect.d.ts +49 -0
  86. package/dist/providers/detect.js +125 -0
  87. package/dist/providers/detect.js.map +1 -0
  88. package/dist/providers/errors.d.ts +49 -0
  89. package/dist/providers/errors.js +189 -0
  90. package/dist/providers/errors.js.map +1 -0
  91. package/dist/providers/index.d.ts +9 -0
  92. package/dist/providers/index.js +7 -0
  93. package/dist/providers/index.js.map +1 -0
  94. package/dist/providers/port.d.ts +74 -0
  95. package/dist/providers/port.js +16 -0
  96. package/dist/providers/port.js.map +1 -0
  97. package/dist/providers/registry.d.ts +21 -0
  98. package/dist/providers/registry.js +34 -0
  99. package/dist/providers/registry.js.map +1 -0
  100. package/dist/ui/banner.d.ts +19 -0
  101. package/dist/ui/banner.js +32 -0
  102. package/dist/ui/banner.js.map +1 -0
  103. package/dist/ui/spinner.d.ts +27 -0
  104. package/dist/ui/spinner.js +67 -0
  105. package/dist/ui/spinner.js.map +1 -0
  106. package/dist/ui/theme.d.ts +32 -0
  107. package/dist/ui/theme.js +56 -0
  108. package/dist/ui/theme.js.map +1 -0
  109. package/package.json +55 -49
  110. package/data/orchestrator.json +0 -113
  111. package/src/auth/recovery.mjs +0 -328
  112. package/src/auth/refresh.mjs +0 -373
  113. package/src/chef.mjs +0 -348
  114. package/src/cli/doctor.mjs +0 -568
  115. package/src/cli/reset.mjs +0 -447
  116. package/src/cli/status.mjs +0 -379
  117. package/src/cli.mjs +0 -429
  118. package/src/commands/doctor.mjs +0 -375
  119. package/src/commands/help.mjs +0 -324
  120. package/src/commands/status.mjs +0 -331
  121. package/src/monitor/health.mjs +0 -486
  122. package/src/monitor/performance.mjs +0 -442
  123. package/src/monitor/report.mjs +0 -535
  124. package/src/orchestrator/classify.mjs +0 -391
  125. package/src/orchestrator/confidence.mjs +0 -151
  126. package/src/orchestrator/handoffs.mjs +0 -231
  127. package/src/orchestrator/review.mjs +0 -222
  128. package/src/providers/balance.mjs +0 -201
  129. package/src/providers/claude.mjs +0 -236
  130. package/src/providers/codex.mjs +0 -255
  131. package/src/providers/detect.mjs +0 -185
  132. package/src/providers/errors.mjs +0 -373
  133. package/src/providers/select.mjs +0 -162
  134. package/src/repl-enhanced.mjs +0 -417
  135. package/src/repl.mjs +0 -321
  136. package/src/state/archive.mjs +0 -366
  137. package/src/state/atomic.mjs +0 -116
  138. package/src/state/cleanup.mjs +0 -440
  139. package/src/state/recovery.mjs +0 -461
  140. package/src/state/session.mjs +0 -147
  141. package/src/ui/errors.mjs +0 -456
  142. package/src/ui/formatter.mjs +0 -327
  143. package/src/ui/icons.mjs +0 -318
  144. package/src/ui/progress.mjs +0 -468
  145. package/templates/prompts/confidence-format.txt +0 -14
  146. package/templates/prompts/ic-with-feedback.txt +0 -41
  147. package/templates/prompts/ic.txt +0 -13
  148. package/templates/prompts/manager-review.txt +0 -40
  149. package/templates/prompts/manager.txt +0 -14
  150. 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
- }