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
@@ -0,0 +1,125 @@
1
+ /**
2
+ * src/core/prompt.ts — typed, tier-specific prompt builders.
3
+ *
4
+ * Each tier gets a professional role prompt that instructs the model to perform
5
+ * real work and end its response with a structured JSON confidence envelope on
6
+ * its own line. The envelope is the only source of confidence data — no keyword
7
+ * heuristics, no fabricated numbers.
8
+ *
9
+ * Pure module: no I/O, no time, no randomness.
10
+ */
11
+ // ---------------------------------------------------------------------------
12
+ // Envelope schema (documented here for parser/prompt alignment)
13
+ // ---------------------------------------------------------------------------
14
+ //
15
+ // Every model response MUST end with exactly this JSON object on its own line:
16
+ //
17
+ // {"confidence": <0.0-1.0>, "escalate": <true|false>, "reason": "<string>", "needs_review": <true|false>}
18
+ //
19
+ // confidence : float [0,1] — the model's self-assessed probability of correctness
20
+ // escalate : true → the model requests a higher-tier pass
21
+ // reason : brief human-readable justification for the confidence / escalation
22
+ // needs_review: true → the model recommends cross-provider review
23
+ //
24
+ // The assess() function in assess.ts parses this envelope. If the envelope is
25
+ // absent or malformed, confidence is recorded as null (never fabricated).
26
+ // ---------------------------------------------------------------------------
27
+ // Tier role prompts
28
+ // ---------------------------------------------------------------------------
29
+ const WORKER_SYSTEM = `\
30
+ You are a precise, efficient worker-tier assistant. Your role is to perform
31
+ well-scoped, read-oriented tasks: searching codebases, listing files, looking up
32
+ definitions, reading documentation, and answering factual questions about the
33
+ current project.
34
+
35
+ Guidelines:
36
+ - Be concise and direct. Do not pad responses with unnecessary explanation.
37
+ - Quote exact file paths, line numbers, and symbols when they are available.
38
+ - If the task requires writing or modifying files, note that explicitly and
39
+ recommend escalating to an IC-tier run.
40
+ - Do not speculate beyond what is directly observable in the project.
41
+
42
+ After completing the task, append EXACTLY the following JSON object on its own
43
+ line at the very end of your response (no trailing text after it):
44
+ {"confidence": <0.0-1.0>, "escalate": <true|false>, "reason": "<one sentence>", "needs_review": <true|false>}
45
+
46
+ Set confidence to your honest estimate of correctness (1.0 = certain, 0.0 = no
47
+ idea). Set escalate to true if the task requires a higher-tier model. Set
48
+ needs_review to true if you are uncertain and an independent check would help.`;
49
+ const IC_SYSTEM = `\
50
+ You are a skilled individual-contributor (IC) engineer. Your role is to
51
+ implement, refactor, debug, and explain code. You work directly in the project's
52
+ file tree, make targeted edits, run available tools, and produce clean,
53
+ well-reasoned output.
54
+
55
+ Guidelines:
56
+ - Read the relevant files before making changes.
57
+ - Make the smallest correct change that satisfies the task; avoid scope creep.
58
+ - If you modify files, describe each change with file path, what changed, and why.
59
+ - If you run commands (tests, builds), report the exact output.
60
+ - If the task involves high-stakes areas (auth, secrets, payments, deployments),
61
+ err on the side of caution and set needs_review to true.
62
+
63
+ After completing the task, append EXACTLY the following JSON object on its own
64
+ line at the very end of your response (no trailing text after it):
65
+ {"confidence": <0.0-1.0>, "escalate": <true|false>, "reason": "<one sentence>", "needs_review": <true|false>}
66
+
67
+ Set confidence to your honest estimate of correctness (1.0 = certain, 0.0 = no
68
+ idea). Set escalate to true if the task is beyond IC scope (e.g. requires
69
+ cross-cutting architectural decisions). Set needs_review to true if an
70
+ independent reviewer would meaningfully reduce risk.`;
71
+ const MANAGER_SYSTEM = `\
72
+ You are a senior-manager / staff-engineer-tier reviewer and architect. Your role
73
+ is to evaluate code, plans, and proposals; identify systemic risks; design
74
+ solutions; and produce authoritative recommendations.
75
+
76
+ Guidelines:
77
+ - Ground every finding in specific, file-anchored evidence (file path + line
78
+ range). Vague assertions are not acceptable.
79
+ - For security/threat-model reviews, enumerate each threat class, its severity
80
+ (Critical/High/Medium/Low), and a concrete mitigation.
81
+ - For architectural reviews, identify coupling, missing abstractions, and failure
82
+ modes — not just style issues.
83
+ - Produce a structured verdict with a clear APPROVE / REQUEST_CHANGES /
84
+ ESCALATE recommendation.
85
+ - If you identify a critical defect, set escalate to true and explain why
86
+ immediate attention is required.
87
+
88
+ After completing the review or plan, append EXACTLY the following JSON object on
89
+ its own line at the very end of your response (no trailing text after it):
90
+ {"confidence": <0.0-1.0>, "escalate": <true|false>, "reason": "<one sentence>", "needs_review": <true|false>}
91
+
92
+ Set confidence to your honest estimate that your analysis is complete and correct
93
+ (1.0 = certain, 0.0 = severely incomplete). Set escalate to true only if the
94
+ situation warrants immediate human or higher-tier intervention.`;
95
+ // ---------------------------------------------------------------------------
96
+ // Public API
97
+ // ---------------------------------------------------------------------------
98
+ /**
99
+ * Build the full prompt string to deliver to a model for the given tier.
100
+ *
101
+ * The system instructions are prepended to the raw task so the model receives
102
+ * role context before the user request.
103
+ *
104
+ * When `managerNotes` is provided (IC retry after a reviewer's `revise` verdict),
105
+ * the prompt is extended with a REVIEWER FEEDBACK section so the IC can address
106
+ * the specific feedback on its next attempt.
107
+ *
108
+ * @param tier - The orchestration tier that will handle the task.
109
+ * @param task - The raw user task description.
110
+ * @param managerNotes - Optional feedback from a cross-vendor reviewer to be addressed.
111
+ */
112
+ export function buildPrompt(tier, task, managerNotes) {
113
+ const system = TIER_PROMPTS[tier];
114
+ let prompt = `${system}\n\n---\n\nTask:\n${task}`;
115
+ if (managerNotes !== undefined && managerNotes.trim().length > 0) {
116
+ prompt += `\n\nREVIEWER FEEDBACK:\n${managerNotes.trim()}\nAddress this specifically.`;
117
+ }
118
+ return prompt;
119
+ }
120
+ const TIER_PROMPTS = {
121
+ worker: WORKER_SYSTEM,
122
+ ic: IC_SYSTEM,
123
+ manager: MANAGER_SYSTEM,
124
+ };
125
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/core/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,8EAA8E;AAC9E,gEAAgE;AAChE,8EAA8E;AAC9E,EAAE;AACF,+EAA+E;AAC/E,EAAE;AACF,4GAA4G;AAC5G,EAAE;AACF,mFAAmF;AACnF,8DAA8D;AAC9D,mFAAmF;AACnF,mEAAmE;AACnE,EAAE;AACF,+EAA+E;AAC/E,0EAA0E;AAE1E,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;+EAmByD,CAAC;AAEhF,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;qDAqBmC,CAAC;AAEtD,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;gEAuByC,CAAC;AAEjE,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAAC,IAAU,EAAE,IAAY,EAAE,YAAqB;IACzE,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,MAAM,GAAG,GAAG,MAAM,qBAAqB,IAAI,EAAE,CAAC;IAClD,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,2BAA2B,YAAY,CAAC,IAAI,EAAE,8BAA8B,CAAC;IACzF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,YAAY,GAAyB;IACzC,MAAM,EAAE,aAAa;IACrB,EAAE,EAAE,SAAS;IACb,OAAO,EAAE,cAAc;CACxB,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * src/core/review.ts — pure review prompt builder and verdict parser.
3
+ *
4
+ * Provides utilities for building manager-style review prompts and parsing
5
+ * the structured review verdict returned by the reviewing model.
6
+ *
7
+ * Honesty Contract: parseReviewVerdict never throws and never fabricates a
8
+ * verdict — on any parse failure it defaults to fail-open `approve` with
9
+ * confidence null so a broken reviewer cannot block the user.
10
+ *
11
+ * Pure module: no I/O, no time, no randomness.
12
+ */
13
+ /**
14
+ * The structured verdict returned by a reviewing model.
15
+ *
16
+ * - `approve` : IC output is acceptable; proceed to final.
17
+ * - `revise` : IC output needs changes; retry IC with the reviewer's notes.
18
+ * - `escalate` : The issue is beyond IC scope; escalate to manager tier.
19
+ */
20
+ export interface ReviewVerdict {
21
+ readonly verdict: 'approve' | 'revise' | 'escalate';
22
+ readonly notes: string;
23
+ readonly confidence: number | null;
24
+ }
25
+ /**
26
+ * Build a manager-style prompt asking the reviewer to critically assess the
27
+ * IC's work for correctness, quality, security, and completeness.
28
+ *
29
+ * The prompt instructs the model to end its response with a JSON verdict
30
+ * envelope on its own line:
31
+ * {"verdict": "approve|revise|escalate", "notes": "...", "confidence": 0.0-1.0}
32
+ *
33
+ * @param task - The original user task description.
34
+ * @param icOutput - The IC's full output text being reviewed.
35
+ */
36
+ export declare function buildReviewPrompt(task: string, icOutput: string): string;
37
+ /**
38
+ * Robustly parse the trailing JSON verdict envelope from a reviewer's output.
39
+ *
40
+ * Fail-open contract: if the envelope is absent or malformed, returns
41
+ * `{ verdict: 'approve', notes: '', confidence: null }`. A broken reviewer
42
+ * must never block the user. This function NEVER throws.
43
+ *
44
+ * @param output - The full text output from the reviewing model.
45
+ */
46
+ export declare function parseReviewVerdict(output: string): ReviewVerdict;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * src/core/review.ts — pure review prompt builder and verdict parser.
3
+ *
4
+ * Provides utilities for building manager-style review prompts and parsing
5
+ * the structured review verdict returned by the reviewing model.
6
+ *
7
+ * Honesty Contract: parseReviewVerdict never throws and never fabricates a
8
+ * verdict — on any parse failure it defaults to fail-open `approve` with
9
+ * confidence null so a broken reviewer cannot block the user.
10
+ *
11
+ * Pure module: no I/O, no time, no randomness.
12
+ */
13
+ // ---------------------------------------------------------------------------
14
+ // Prompt builder
15
+ // ---------------------------------------------------------------------------
16
+ /**
17
+ * Build a manager-style prompt asking the reviewer to critically assess the
18
+ * IC's work for correctness, quality, security, and completeness.
19
+ *
20
+ * The prompt instructs the model to end its response with a JSON verdict
21
+ * envelope on its own line:
22
+ * {"verdict": "approve|revise|escalate", "notes": "...", "confidence": 0.0-1.0}
23
+ *
24
+ * @param task - The original user task description.
25
+ * @param icOutput - The IC's full output text being reviewed.
26
+ */
27
+ export function buildReviewPrompt(task, icOutput) {
28
+ return `\
29
+ You are a senior-manager / staff-engineer reviewer performing a critical quality gate.
30
+
31
+ You are reviewing the work of an individual-contributor (IC) engineer who was given the
32
+ following task. Your job is to identify any issues with correctness, quality, security,
33
+ or completeness in the IC's output before it reaches the user.
34
+
35
+ Original task:
36
+ ${task}
37
+
38
+ IC output to review:
39
+ ${icOutput}
40
+
41
+ Review checklist (assess each dimension):
42
+ 1. CORRECTNESS — Does the output actually solve the task? Are there logic errors, off-by-ones,
43
+ or wrong assumptions?
44
+ 2. QUALITY — Is the code/output clean, idiomatic, and maintainable? Are there obvious smells?
45
+ 3. SECURITY — Are there any injection risks, secret leaks, missing input validation, or
46
+ privilege-escalation paths?
47
+ 4. COMPLETENESS — Does the output address all parts of the task, or does it miss edge cases?
48
+
49
+ For any finding, anchor it to a specific file path and line range when applicable.
50
+
51
+ After your review, append EXACTLY the following JSON object on its own line at the very end
52
+ of your response (no trailing text after it):
53
+ {"verdict": "approve|revise|escalate", "notes": "<specific, file-anchored feedback>", "confidence": <0.0-1.0>}
54
+
55
+ verdict choices:
56
+ approve — the IC output is correct, complete, and safe; ship it.
57
+ revise — the IC output has fixable issues; provide actionable notes so the IC can retry.
58
+ escalate — the task requires architectural judgement or has critical defects beyond IC scope.
59
+
60
+ confidence: your honest estimate that your review is complete and correct (1.0 = certain).`;
61
+ }
62
+ const VALID_VERDICTS = new Set(['approve', 'revise', 'escalate']);
63
+ /** Fail-open default used whenever the envelope is absent or malformed. */
64
+ const FAIL_OPEN = { verdict: 'approve', notes: '', confidence: null };
65
+ /**
66
+ * Attempt to extract the last JSON object from `text` that contains a `verdict` key.
67
+ * Returns null if no valid envelope is found.
68
+ */
69
+ function extractVerdictEnvelope(text) {
70
+ const candidates = [];
71
+ let i = 0;
72
+ while (i < text.length) {
73
+ const start = text.indexOf('{', i);
74
+ if (start === -1)
75
+ break;
76
+ let depth = 0;
77
+ let j = start;
78
+ let foundClose = false;
79
+ while (j < text.length) {
80
+ if (text[j] === '{') {
81
+ depth++;
82
+ }
83
+ else if (text[j] === '}') {
84
+ depth--;
85
+ if (depth === 0) {
86
+ foundClose = true;
87
+ break;
88
+ }
89
+ }
90
+ j++;
91
+ }
92
+ if (foundClose) {
93
+ const candidate = text.slice(start, j + 1);
94
+ try {
95
+ const parsed = JSON.parse(candidate);
96
+ if (parsed !== null &&
97
+ typeof parsed === 'object' &&
98
+ !Array.isArray(parsed) &&
99
+ 'verdict' in parsed) {
100
+ candidates.push(parsed);
101
+ }
102
+ }
103
+ catch {
104
+ // Not valid JSON — skip
105
+ }
106
+ }
107
+ i = start + 1;
108
+ }
109
+ if (candidates.length === 0)
110
+ return null;
111
+ return candidates[candidates.length - 1] ?? null;
112
+ }
113
+ /**
114
+ * Robustly parse the trailing JSON verdict envelope from a reviewer's output.
115
+ *
116
+ * Fail-open contract: if the envelope is absent or malformed, returns
117
+ * `{ verdict: 'approve', notes: '', confidence: null }`. A broken reviewer
118
+ * must never block the user. This function NEVER throws.
119
+ *
120
+ * @param output - The full text output from the reviewing model.
121
+ */
122
+ export function parseReviewVerdict(output) {
123
+ // Guard: never throw on any input
124
+ if (typeof output !== 'string')
125
+ return FAIL_OPEN;
126
+ let envelope;
127
+ try {
128
+ envelope = extractVerdictEnvelope(output);
129
+ }
130
+ catch {
131
+ return FAIL_OPEN;
132
+ }
133
+ if (envelope === null)
134
+ return FAIL_OPEN;
135
+ // Validate verdict — must be one of the three allowed values
136
+ if (typeof envelope.verdict !== 'string' || !VALID_VERDICTS.has(envelope.verdict)) {
137
+ return FAIL_OPEN;
138
+ }
139
+ const verdict = envelope.verdict;
140
+ const notes = typeof envelope.notes === 'string' ? envelope.notes.trim() : '';
141
+ // confidence is optional — null when absent or invalid
142
+ let confidence = null;
143
+ if (typeof envelope.confidence === 'number' && isFinite(envelope.confidence)) {
144
+ confidence = Math.min(1, Math.max(0, envelope.confidence));
145
+ }
146
+ return { verdict, notes, confidence };
147
+ }
148
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/core/review.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * src/core/route.ts — pure cost-aware routing decision.
3
+ *
4
+ * Given a tier, the set of currently available provider IDs, and the active
5
+ * Policy, selects the concrete provider+model that should handle the work.
6
+ *
7
+ * No I/O, no time, no randomness. Pricing data is pure reference data imported
8
+ * from infra/pricing (permitted by the purity guard because pricing.ts itself
9
+ * imports no fs/path/child_process).
10
+ */
11
+ import type { Tier, RouteDecision, Policy } from './types.js';
12
+ import type { ProviderId } from '../providers/port.js';
13
+ /**
14
+ * Resolve a {@link RouteDecision} for the given tier.
15
+ *
16
+ * Algorithm:
17
+ * 1. Walk `policy.providerOrderByTier[tier]` in order.
18
+ * 2. For the first provider that is present in `available`, resolve the
19
+ * cheapest model for that provider+tier via `getCheapestForTier`.
20
+ * 3. If none of the policy-preferred providers are available but `available`
21
+ * is non-empty, fall back to the globally cheapest model for that tier.
22
+ * 4. If `available` is empty, throw — there is nothing to route to.
23
+ *
24
+ * @param tier - The orchestration tier to route.
25
+ * @param available - Provider IDs that are currently reachable.
26
+ * @param policy - Active routing policy (from `DEFAULT_POLICY` or overrides).
27
+ */
28
+ export declare function route(tier: Tier, available: ProviderId[], policy: Policy): RouteDecision;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * src/core/route.ts — pure cost-aware routing decision.
3
+ *
4
+ * Given a tier, the set of currently available provider IDs, and the active
5
+ * Policy, selects the concrete provider+model that should handle the work.
6
+ *
7
+ * No I/O, no time, no randomness. Pricing data is pure reference data imported
8
+ * from infra/pricing (permitted by the purity guard because pricing.ts itself
9
+ * imports no fs/path/child_process).
10
+ */
11
+ import { getCheapestForTier } from '../infra/pricing.js';
12
+ /**
13
+ * Resolve a {@link RouteDecision} for the given tier.
14
+ *
15
+ * Algorithm:
16
+ * 1. Walk `policy.providerOrderByTier[tier]` in order.
17
+ * 2. For the first provider that is present in `available`, resolve the
18
+ * cheapest model for that provider+tier via `getCheapestForTier`.
19
+ * 3. If none of the policy-preferred providers are available but `available`
20
+ * is non-empty, fall back to the globally cheapest model for that tier.
21
+ * 4. If `available` is empty, throw — there is nothing to route to.
22
+ *
23
+ * @param tier - The orchestration tier to route.
24
+ * @param available - Provider IDs that are currently reachable.
25
+ * @param policy - Active routing policy (from `DEFAULT_POLICY` or overrides).
26
+ */
27
+ export function route(tier, available, policy) {
28
+ if (available.length === 0) {
29
+ throw new Error(`route: no providers available for tier "${tier}" — start at least one provider`);
30
+ }
31
+ const preferredOrder = policy.providerOrderByTier[tier];
32
+ // Walk the preferred order and pick the first available provider.
33
+ for (const preferred of preferredOrder) {
34
+ if (available.includes(preferred)) {
35
+ const pricing = getCheapestForTier(tier, [preferred]);
36
+ return {
37
+ tier,
38
+ provider: preferred,
39
+ model: pricing.model,
40
+ };
41
+ }
42
+ }
43
+ // None of the policy-preferred providers are available; fall back to the
44
+ // globally cheapest model across all available providers.
45
+ const fallback = getCheapestForTier(tier, available);
46
+ return {
47
+ tier,
48
+ provider: fallback.provider,
49
+ model: fallback.model,
50
+ };
51
+ }
52
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/core/route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,KAAK,CACnB,IAAU,EACV,SAAuB,EACvB,MAAc;IAEd,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,2CAA2C,IAAI,iCAAiC,CACjF,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAExD,kEAAkE;IAClE,KAAK,MAAM,SAAS,IAAI,cAAc,EAAE,CAAC;QACvC,IAAI,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAClC,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACtD,OAAO;gBACL,IAAI;gBACJ,QAAQ,EAAE,SAAS;gBACnB,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACrD,OAAO;QACL,IAAI;QACJ,QAAQ,EAAE,QAAQ,CAAC,QAAsB;QACzC,KAAK,EAAE,QAAQ,CAAC,KAAK;KACtB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * src/core/types.ts — shared types and ports for the orchestration core.
3
+ *
4
+ * This is the type hub. The pure core imports only types here (plus the
5
+ * Provider port). I/O is reached exclusively through the injected port
6
+ * interfaces below (Clock, SessionWriter, LedgerWriter), which infra
7
+ * implements — that is what keeps `src/core/` free of fs/child_process while
8
+ * remaining fully testable with fakes.
9
+ *
10
+ * Purity rule (enforced by test/arch/guards.test.ts): core code must obtain all
11
+ * time, ids, and randomness from the injected `Clock`, never from Date/Math.
12
+ */
13
+ import type { Provider, ProviderId, SandboxLevel } from '../providers/port.js';
14
+ export type Tier = 'worker' | 'ic' | 'manager';
15
+ export type Risk = 'low' | 'medium' | 'high' | 'critical';
16
+ export interface Classification {
17
+ readonly tier: Tier;
18
+ readonly risk: Risk;
19
+ /** Human-readable reason the classifier chose this tier/risk. */
20
+ readonly rationale: string;
21
+ }
22
+ /** A concrete routing decision: which provider+model runs a tier. */
23
+ export interface RouteDecision {
24
+ readonly tier: Tier;
25
+ readonly provider: ProviderId;
26
+ readonly model: string;
27
+ }
28
+ /**
29
+ * Result of assessing a model's output for real, verifiable signals.
30
+ * `confidence` is null when the model emitted no parseable confidence envelope —
31
+ * we never fabricate a number (Honesty Contract).
32
+ */
33
+ export interface Assessment {
34
+ readonly confidence: number | null;
35
+ readonly escalate: boolean;
36
+ readonly reason: string;
37
+ readonly needsReview: boolean;
38
+ }
39
+ export interface Clock {
40
+ /** Epoch milliseconds. */
41
+ now(): number;
42
+ /** ISO-8601 timestamp string. */
43
+ isoNow(): string;
44
+ /** A unique identifier (uuid-like). */
45
+ uuid(): string;
46
+ /** A float in [0, 1). */
47
+ random(): number;
48
+ }
49
+ export interface SessionEntry {
50
+ readonly timestamp: string;
51
+ readonly role: 'user' | 'assistant' | 'system';
52
+ readonly content: string;
53
+ readonly tier?: Tier;
54
+ readonly provider?: ProviderId;
55
+ readonly model?: string;
56
+ readonly confidence?: number | null;
57
+ readonly costUsd?: number;
58
+ readonly durationMs?: number;
59
+ }
60
+ export interface SessionWriter {
61
+ readonly id: string;
62
+ append(entry: SessionEntry): Promise<void>;
63
+ }
64
+ export interface LedgerEntry {
65
+ readonly timestamp: string;
66
+ readonly sessionId: string;
67
+ readonly taskId: string;
68
+ readonly provider: ProviderId;
69
+ readonly model: string;
70
+ readonly tier: Tier;
71
+ readonly inputTokens: number;
72
+ readonly outputTokens: number;
73
+ readonly cachedInputTokens: number;
74
+ readonly usd: number;
75
+ readonly durationMs: number;
76
+ readonly success: boolean;
77
+ }
78
+ export interface LedgerWriter {
79
+ record(entry: LedgerEntry): Promise<void>;
80
+ }
81
+ export interface Policy {
82
+ /** Hard cap on tier attempts per task (loop/cost guard). */
83
+ readonly maxAttempts: number;
84
+ /** Escalate when self-reported confidence is strictly below this, indexed by risk. */
85
+ readonly escalateBelowConfidence: Record<Risk, number>;
86
+ /** Ordered provider preference per tier; route() honours availability. */
87
+ readonly providerOrderByTier: Record<Tier, readonly ProviderId[]>;
88
+ }
89
+ export interface OrchestrateDeps {
90
+ /** Available providers, keyed by id. Absent key = provider unavailable. */
91
+ readonly providers: Partial<Record<ProviderId, Provider>>;
92
+ readonly clock: Clock;
93
+ readonly session: SessionWriter;
94
+ readonly ledger: LedgerWriter;
95
+ readonly policy: Policy;
96
+ readonly cwd: string;
97
+ readonly sandbox: SandboxLevel;
98
+ readonly timeoutMs: number;
99
+ }
100
+ /**
101
+ * High-level events emitted by orchestrate(). The interface/render layer
102
+ * consumes these; every field is a real measurement (no fabricated values).
103
+ */
104
+ export type CoreEvent = {
105
+ readonly type: 'classified';
106
+ readonly classification: Classification;
107
+ } | {
108
+ readonly type: 'tier-start';
109
+ readonly tier: Tier;
110
+ readonly provider: ProviderId;
111
+ readonly model: string;
112
+ readonly attempt: number;
113
+ } | {
114
+ readonly type: 'provider-event';
115
+ readonly tier: Tier;
116
+ readonly event: import('../providers/port.js').ProviderEvent;
117
+ } | {
118
+ readonly type: 'tier-done';
119
+ readonly tier: Tier;
120
+ readonly success: boolean;
121
+ readonly confidence: number | null;
122
+ readonly costUsd: number;
123
+ readonly durationMs: number;
124
+ } | {
125
+ readonly type: 'escalate';
126
+ readonly from: Tier;
127
+ readonly to: Tier;
128
+ readonly reason: string;
129
+ } | {
130
+ readonly type: 'notice';
131
+ readonly level: 'info' | 'warn' | 'error';
132
+ readonly message: string;
133
+ } | {
134
+ readonly type: 'final';
135
+ readonly success: boolean;
136
+ readonly output: string;
137
+ readonly tier: Tier;
138
+ readonly totalCostUsd: number;
139
+ readonly sessionId: string;
140
+ readonly attempts: number;
141
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * src/core/types.ts — shared types and ports for the orchestration core.
3
+ *
4
+ * This is the type hub. The pure core imports only types here (plus the
5
+ * Provider port). I/O is reached exclusively through the injected port
6
+ * interfaces below (Clock, SessionWriter, LedgerWriter), which infra
7
+ * implements — that is what keeps `src/core/` free of fs/child_process while
8
+ * remaining fully testable with fakes.
9
+ *
10
+ * Purity rule (enforced by test/arch/guards.test.ts): core code must obtain all
11
+ * time, ids, and randomness from the injected `Clock`, never from Date/Math.
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * atomic.ts — Atomic file operations for safe concurrent access
3
+ * Ported from myshell-tools/src/state/atomic.mjs with the following fixes:
4
+ * - Full TypeScript strict typing
5
+ * - Async (fs/promises) throughout
6
+ * - Async backoff instead of CPU-spinning lock acquisition
7
+ * - O(1) JSONL append via fs.appendFile (no read-then-rewrite)
8
+ */
9
+ export interface LockOptions {
10
+ /** How long to keep trying before giving up (default: 5 000 ms) */
11
+ timeoutMs?: number;
12
+ /** Age at which an existing lock is considered stale and may be stolen (default: 10 000 ms) */
13
+ staleMs?: number;
14
+ }
15
+ export declare class LockTimeoutError extends Error {
16
+ constructor(lockPath: string, timeoutMs: number);
17
+ }
18
+ export declare class AtomicWriteError extends Error {
19
+ constructor(filePath: string, cause: unknown);
20
+ }
21
+ /**
22
+ * Acquire a `.lock` file using `O_EXCL` for atomic creation.
23
+ * Retries with exponential backoff until `timeoutMs` is reached.
24
+ * Steals locks whose mtime is older than `staleMs`.
25
+ *
26
+ * Throws `LockTimeoutError` if the lock cannot be acquired in time.
27
+ */
28
+ export declare function acquireLock(lockPath: string, opts?: LockOptions): Promise<void>;
29
+ /**
30
+ * Release a lock file previously acquired with `acquireLock`.
31
+ * Silently ignores a missing lock file (idempotent).
32
+ */
33
+ export declare function releaseLock(lockPath: string): Promise<void>;
34
+ /**
35
+ * Convenience wrapper: acquire the lock, run `fn`, then always release.
36
+ * Re-throws any error from `fn` after releasing the lock.
37
+ */
38
+ export declare function withLock<T>(lockPath: string, fn: () => Promise<T>, opts?: LockOptions): Promise<T>;
39
+ /**
40
+ * Atomically write `data` to `filePath` using a tmp-file + rename strategy.
41
+ * The tmp file lives in the same directory to avoid cross-device rename issues.
42
+ */
43
+ export declare function atomicWrite(filePath: string, data: string): Promise<void>;
44
+ /**
45
+ * Atomically append a single JSONL entry to `filePath`.
46
+ *
47
+ * This is O(1) — it uses `fs.appendFile` rather than reading the entire file
48
+ * and rewriting it on every call. The caller is responsible for holding a lock
49
+ * when concurrent appends must be strictly ordered.
50
+ *
51
+ * Creates `filePath` if it does not exist.
52
+ */
53
+ export declare function atomicAppendJSONL(filePath: string, entry: unknown): Promise<void>;