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.
- package/CHANGELOG.md +44 -69
- package/LICENSE +21 -21
- package/README.md +178 -318
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +106 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/cost.d.ts +36 -0
- package/dist/commands/cost.js +103 -0
- package/dist/commands/cost.js.map +1 -0
- package/dist/commands/doctor.d.ts +36 -0
- package/dist/commands/doctor.js +115 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/core/assess.d.ts +25 -0
- package/dist/core/assess.js +142 -0
- package/dist/core/assess.js.map +1 -0
- package/dist/core/classify.d.ts +19 -0
- package/dist/core/classify.js +80 -0
- package/dist/core/classify.js.map +1 -0
- package/dist/core/escalate.d.ts +32 -0
- package/dist/core/escalate.js +57 -0
- package/dist/core/escalate.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.js +12 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/orchestrate.d.ts +42 -0
- package/dist/core/orchestrate.js +439 -0
- package/dist/core/orchestrate.js.map +1 -0
- package/dist/core/policy.d.ts +9 -0
- package/dist/core/policy.js +27 -0
- package/dist/core/policy.js.map +1 -0
- package/dist/core/prompt.d.ts +26 -0
- package/dist/core/prompt.js +125 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/review.d.ts +46 -0
- package/dist/core/review.js +148 -0
- package/dist/core/review.js.map +1 -0
- package/dist/core/route.d.ts +28 -0
- package/dist/core/route.js +52 -0
- package/dist/core/route.js.map +1 -0
- package/dist/core/types.d.ts +141 -0
- package/dist/core/types.js +14 -0
- package/dist/core/types.js.map +1 -0
- package/dist/infra/atomic.d.ts +53 -0
- package/dist/infra/atomic.js +171 -0
- package/dist/infra/atomic.js.map +1 -0
- package/dist/infra/clock.d.ts +9 -0
- package/dist/infra/clock.js +15 -0
- package/dist/infra/clock.js.map +1 -0
- package/dist/infra/index.d.ts +9 -0
- package/dist/infra/index.js +7 -0
- package/dist/infra/index.js.map +1 -0
- package/dist/infra/ledger.d.ts +49 -0
- package/dist/infra/ledger.js +90 -0
- package/dist/infra/ledger.js.map +1 -0
- package/dist/infra/paths.d.ts +28 -0
- package/dist/infra/paths.js +38 -0
- package/dist/infra/paths.js.map +1 -0
- package/dist/infra/pricing.d.ts +47 -0
- package/dist/infra/pricing.js +151 -0
- package/dist/infra/pricing.js.map +1 -0
- package/dist/infra/session.d.ts +28 -0
- package/dist/infra/session.js +61 -0
- package/dist/infra/session.js.map +1 -0
- package/dist/interface/render.d.ts +27 -0
- package/dist/interface/render.js +134 -0
- package/dist/interface/render.js.map +1 -0
- package/dist/interface/repl.d.ts +23 -0
- package/dist/interface/repl.js +90 -0
- package/dist/interface/repl.js.map +1 -0
- package/dist/interface/run.d.ts +20 -0
- package/dist/interface/run.js +31 -0
- package/dist/interface/run.js.map +1 -0
- package/dist/providers/claude-parse.d.ts +24 -0
- package/dist/providers/claude-parse.js +113 -0
- package/dist/providers/claude-parse.js.map +1 -0
- package/dist/providers/claude.d.ts +45 -0
- package/dist/providers/claude.js +122 -0
- package/dist/providers/claude.js.map +1 -0
- package/dist/providers/codex-parse.d.ts +32 -0
- package/dist/providers/codex-parse.js +145 -0
- package/dist/providers/codex-parse.js.map +1 -0
- package/dist/providers/codex.d.ts +44 -0
- package/dist/providers/codex.js +124 -0
- package/dist/providers/codex.js.map +1 -0
- package/dist/providers/detect.d.ts +49 -0
- package/dist/providers/detect.js +125 -0
- package/dist/providers/detect.js.map +1 -0
- package/dist/providers/errors.d.ts +49 -0
- package/dist/providers/errors.js +189 -0
- package/dist/providers/errors.js.map +1 -0
- package/dist/providers/index.d.ts +9 -0
- package/dist/providers/index.js +7 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/port.d.ts +74 -0
- package/dist/providers/port.js +16 -0
- package/dist/providers/port.js.map +1 -0
- package/dist/providers/registry.d.ts +21 -0
- package/dist/providers/registry.js +34 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/ui/banner.d.ts +19 -0
- package/dist/ui/banner.js +32 -0
- package/dist/ui/banner.js.map +1 -0
- package/dist/ui/spinner.d.ts +27 -0
- package/dist/ui/spinner.js +67 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/ui/theme.d.ts +32 -0
- package/dist/ui/theme.js +56 -0
- package/dist/ui/theme.js.map +1 -0
- package/package.json +55 -49
- package/data/orchestrator.json +0 -113
- package/src/auth/recovery.mjs +0 -328
- package/src/auth/refresh.mjs +0 -373
- package/src/chef.mjs +0 -348
- package/src/cli/doctor.mjs +0 -568
- package/src/cli/reset.mjs +0 -447
- package/src/cli/status.mjs +0 -379
- package/src/cli.mjs +0 -429
- package/src/commands/doctor.mjs +0 -375
- package/src/commands/help.mjs +0 -324
- package/src/commands/status.mjs +0 -331
- package/src/monitor/health.mjs +0 -486
- package/src/monitor/performance.mjs +0 -442
- package/src/monitor/report.mjs +0 -535
- package/src/orchestrator/classify.mjs +0 -391
- package/src/orchestrator/confidence.mjs +0 -151
- package/src/orchestrator/handoffs.mjs +0 -231
- package/src/orchestrator/review.mjs +0 -222
- package/src/providers/balance.mjs +0 -201
- package/src/providers/claude.mjs +0 -236
- package/src/providers/codex.mjs +0 -255
- package/src/providers/detect.mjs +0 -185
- package/src/providers/errors.mjs +0 -373
- package/src/providers/select.mjs +0 -162
- package/src/repl-enhanced.mjs +0 -417
- package/src/repl.mjs +0 -321
- package/src/state/archive.mjs +0 -366
- package/src/state/atomic.mjs +0 -116
- package/src/state/cleanup.mjs +0 -440
- package/src/state/recovery.mjs +0 -461
- package/src/state/session.mjs +0 -147
- package/src/ui/errors.mjs +0 -456
- package/src/ui/formatter.mjs +0 -327
- package/src/ui/icons.mjs +0 -318
- package/src/ui/progress.mjs +0 -468
- package/templates/prompts/confidence-format.txt +0 -14
- package/templates/prompts/ic-with-feedback.txt +0 -41
- package/templates/prompts/ic.txt +0 -13
- package/templates/prompts/manager-review.txt +0 -40
- package/templates/prompts/manager.txt +0 -14
- 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>;
|