night-orch 0.10.0 → 0.11.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/dist/ai/anthropic.d.ts +12 -0
- package/dist/ai/anthropic.d.ts.map +1 -0
- package/dist/ai/anthropic.js +136 -0
- package/dist/ai/anthropic.js.map +1 -0
- package/dist/ai/errors.d.ts +46 -0
- package/dist/ai/errors.d.ts.map +1 -0
- package/dist/ai/errors.js +65 -0
- package/dist/ai/errors.js.map +1 -0
- package/dist/ai/factory.d.ts +20 -0
- package/dist/ai/factory.d.ts.map +1 -0
- package/dist/ai/factory.js +41 -0
- package/dist/ai/factory.js.map +1 -0
- package/dist/ai/json-extract.d.ts +20 -0
- package/dist/ai/json-extract.d.ts.map +1 -0
- package/dist/ai/json-extract.js +92 -0
- package/dist/ai/json-extract.js.map +1 -0
- package/dist/ai/ledger.d.ts +60 -0
- package/dist/ai/ledger.d.ts.map +1 -0
- package/dist/ai/ledger.js +115 -0
- package/dist/ai/ledger.js.map +1 -0
- package/dist/ai/openrouter.d.ts +12 -0
- package/dist/ai/openrouter.d.ts.map +1 -0
- package/dist/ai/openrouter.js +121 -0
- package/dist/ai/openrouter.js.map +1 -0
- package/dist/ai/types.d.ts +66 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +21 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +1 -0
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +3 -0
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/web.d.ts +4 -0
- package/dist/cli/commands/web.d.ts.map +1 -1
- package/dist/cli/commands/web.js +1 -0
- package/dist/cli/commands/web.js.map +1 -1
- package/dist/cli/index.js +4 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/config/schema.d.ts +338 -120
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +104 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/discovery/triage.d.ts +19 -0
- package/dist/discovery/triage.d.ts.map +1 -1
- package/dist/discovery/triage.js +71 -0
- package/dist/discovery/triage.js.map +1 -1
- package/dist/labels/manager.d.ts +2 -2
- package/dist/labels/manager.d.ts.map +1 -1
- package/dist/labels/manager.js.map +1 -1
- package/dist/labels/transitions.d.ts +17 -4
- package/dist/labels/transitions.d.ts.map +1 -1
- package/dist/labels/transitions.js +15 -2
- package/dist/labels/transitions.js.map +1 -1
- package/dist/loop/checkpoint-schema.d.ts +118 -0
- package/dist/loop/checkpoint-schema.d.ts.map +1 -0
- package/dist/loop/checkpoint-schema.js +120 -0
- package/dist/loop/checkpoint-schema.js.map +1 -0
- package/dist/loop/checkpoint.d.ts +13 -0
- package/dist/loop/checkpoint.d.ts.map +1 -1
- package/dist/loop/checkpoint.js +41 -24
- package/dist/loop/checkpoint.js.map +1 -1
- package/dist/loop/cost/budget.d.ts +50 -0
- package/dist/loop/cost/budget.d.ts.map +1 -0
- package/dist/loop/cost/budget.js +44 -0
- package/dist/loop/cost/budget.js.map +1 -0
- package/dist/loop/cost/overrides.d.ts +28 -0
- package/dist/loop/cost/overrides.d.ts.map +1 -0
- package/dist/loop/cost/overrides.js +75 -0
- package/dist/loop/cost/overrides.js.map +1 -0
- package/dist/loop/cost/query.d.ts +46 -0
- package/dist/loop/cost/query.d.ts.map +1 -0
- package/dist/loop/cost/query.js +160 -0
- package/dist/loop/cost/query.js.map +1 -0
- package/dist/loop/cost/recorder.d.ts +55 -0
- package/dist/loop/cost/recorder.d.ts.map +1 -0
- package/dist/loop/cost/recorder.js +64 -0
- package/dist/loop/cost/recorder.js.map +1 -0
- package/dist/loop/cost.d.ts +44 -133
- package/dist/loop/cost.d.ts.map +1 -1
- package/dist/loop/cost.js +44 -382
- package/dist/loop/cost.js.map +1 -1
- package/dist/loop/decision.d.ts +28 -0
- package/dist/loop/decision.d.ts.map +1 -1
- package/dist/loop/decision.js +133 -15
- package/dist/loop/decision.js.map +1 -1
- package/dist/loop/engine.d.ts.map +1 -1
- package/dist/loop/engine.js +122 -40
- package/dist/loop/engine.js.map +1 -1
- package/dist/loop/state.d.ts +180 -0
- package/dist/loop/state.d.ts.map +1 -0
- package/dist/loop/state.js +202 -0
- package/dist/loop/state.js.map +1 -0
- package/dist/loop/step-executor.d.ts +1 -1
- package/dist/loop/step-executor.d.ts.map +1 -1
- package/dist/loop/step-executor.js +33 -6
- package/dist/loop/step-executor.js.map +1 -1
- package/dist/loop/types.d.ts +18 -1
- package/dist/loop/types.d.ts.map +1 -1
- package/dist/mcp/tools/admin.d.ts.map +1 -1
- package/dist/mcp/tools/admin.js +4 -3
- package/dist/mcp/tools/admin.js.map +1 -1
- package/dist/metrics/collectors.d.ts +3 -0
- package/dist/metrics/collectors.d.ts.map +1 -1
- package/dist/metrics/collectors.js +46 -0
- package/dist/metrics/collectors.js.map +1 -1
- package/dist/metrics/service.d.ts +3 -0
- package/dist/metrics/service.d.ts.map +1 -1
- package/dist/metrics/service.js +21 -0
- package/dist/metrics/service.js.map +1 -1
- package/dist/notify/channels/webpush.d.ts +19 -0
- package/dist/notify/channels/webpush.d.ts.map +1 -0
- package/dist/notify/channels/webpush.js +157 -0
- package/dist/notify/channels/webpush.js.map +1 -0
- package/dist/notify/factory.d.ts +2 -1
- package/dist/notify/factory.d.ts.map +1 -1
- package/dist/notify/factory.js +21 -1
- package/dist/notify/factory.js.map +1 -1
- package/dist/ops/continue.js +48 -46
- package/dist/ops/continue.js.map +1 -1
- package/dist/ops/cost-reset.d.ts +12 -6
- package/dist/ops/cost-reset.d.ts.map +1 -1
- package/dist/ops/cost-reset.js +27 -11
- package/dist/ops/cost-reset.js.map +1 -1
- package/dist/ops/cost-resume.js +21 -20
- package/dist/ops/cost-resume.js.map +1 -1
- package/dist/ops/rebase-and-check.d.ts.map +1 -1
- package/dist/ops/rebase-and-check.js +26 -24
- package/dist/ops/rebase-and-check.js.map +1 -1
- package/dist/ops/retry.d.ts.map +1 -1
- package/dist/ops/retry.js +35 -40
- package/dist/ops/retry.js.map +1 -1
- package/dist/poller/attempt-dispatcher.d.ts +51 -0
- package/dist/poller/attempt-dispatcher.d.ts.map +1 -0
- package/dist/poller/attempt-dispatcher.js +470 -0
- package/dist/poller/attempt-dispatcher.js.map +1 -0
- package/dist/poller/discovery-scheduler.d.ts +37 -0
- package/dist/poller/discovery-scheduler.d.ts.map +1 -0
- package/dist/poller/discovery-scheduler.js +36 -0
- package/dist/poller/discovery-scheduler.js.map +1 -0
- package/dist/poller/error-recovery.d.ts +94 -0
- package/dist/poller/error-recovery.d.ts.map +1 -0
- package/dist/poller/error-recovery.js +130 -0
- package/dist/poller/error-recovery.js.map +1 -0
- package/dist/poller/notify-dispatcher.d.ts +44 -0
- package/dist/poller/notify-dispatcher.d.ts.map +1 -0
- package/dist/poller/notify-dispatcher.js +40 -0
- package/dist/poller/notify-dispatcher.js.map +1 -0
- package/dist/poller/reaction-processor.d.ts +30 -0
- package/dist/poller/reaction-processor.d.ts.map +1 -0
- package/dist/poller/reaction-processor.js +41 -0
- package/dist/poller/reaction-processor.js.map +1 -0
- package/dist/publishing/pr-body.d.ts +25 -0
- package/dist/publishing/pr-body.d.ts.map +1 -1
- package/dist/publishing/pr-body.js +98 -0
- package/dist/publishing/pr-body.js.map +1 -1
- package/dist/runner/helpers.d.ts +11 -2
- package/dist/runner/helpers.d.ts.map +1 -1
- package/dist/runner/helpers.js +29 -13
- package/dist/runner/helpers.js.map +1 -1
- package/dist/runner/poller.d.ts +17 -0
- package/dist/runner/poller.d.ts.map +1 -1
- package/dist/runner/poller.js +119 -590
- package/dist/runner/poller.js.map +1 -1
- package/dist/runner/run-finalizer.d.ts.map +1 -1
- package/dist/runner/run-finalizer.js +13 -2
- package/dist/runner/run-finalizer.js.map +1 -1
- package/dist/settings/definitions/loop.d.ts.map +1 -1
- package/dist/settings/definitions/loop.js +24 -0
- package/dist/settings/definitions/loop.js.map +1 -1
- package/dist/state/attempts.d.ts +110 -0
- package/dist/state/attempts.d.ts.map +1 -0
- package/dist/state/attempts.js +171 -0
- package/dist/state/attempts.js.map +1 -0
- package/dist/state/db.d.ts.map +1 -1
- package/dist/state/db.js +10 -0
- package/dist/state/db.js.map +1 -1
- package/dist/state/migrations/023-attempt-columns.d.ts +30 -0
- package/dist/state/migrations/023-attempt-columns.d.ts.map +1 -0
- package/dist/state/migrations/023-attempt-columns.js +48 -0
- package/dist/state/migrations/023-attempt-columns.js.map +1 -0
- package/dist/state/migrations/024-attempts-head-index.d.ts +22 -0
- package/dist/state/migrations/024-attempts-head-index.d.ts.map +1 -0
- package/dist/state/migrations/024-attempts-head-index.js +33 -0
- package/dist/state/migrations/024-attempts-head-index.js.map +1 -0
- package/dist/state/migrations/025-cost-token-source.d.ts +28 -0
- package/dist/state/migrations/025-cost-token-source.d.ts.map +1 -0
- package/dist/state/migrations/025-cost-token-source.js +37 -0
- package/dist/state/migrations/025-cost-token-source.js.map +1 -0
- package/dist/state/migrations/026-checkpoint-quarantine.d.ts +26 -0
- package/dist/state/migrations/026-checkpoint-quarantine.d.ts.map +1 -0
- package/dist/state/migrations/026-checkpoint-quarantine.js +43 -0
- package/dist/state/migrations/026-checkpoint-quarantine.js.map +1 -0
- package/dist/state/migrations/027-push-subscriptions.d.ts +21 -0
- package/dist/state/migrations/027-push-subscriptions.d.ts.map +1 -0
- package/dist/state/migrations/027-push-subscriptions.js +35 -0
- package/dist/state/migrations/027-push-subscriptions.js.map +1 -0
- package/dist/state/runs.d.ts +33 -0
- package/dist/state/runs.d.ts.map +1 -1
- package/dist/state/runs.js +97 -2
- package/dist/state/runs.js.map +1 -1
- package/dist/state/stats.d.ts +20 -0
- package/dist/state/stats.d.ts.map +1 -1
- package/dist/state/stats.js +36 -0
- package/dist/state/stats.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +5 -0
- package/dist/utils/logger.js.map +1 -1
- package/dist/web/auth.d.ts +43 -0
- package/dist/web/auth.d.ts.map +1 -0
- package/dist/web/auth.js +143 -0
- package/dist/web/auth.js.map +1 -0
- package/dist/web/routes/api-auth.d.ts +21 -0
- package/dist/web/routes/api-auth.d.ts.map +1 -0
- package/dist/web/routes/api-auth.js +93 -0
- package/dist/web/routes/api-auth.js.map +1 -0
- package/dist/web/routes/api-push.d.ts +22 -0
- package/dist/web/routes/api-push.d.ts.map +1 -0
- package/dist/web/routes/api-push.js +74 -0
- package/dist/web/routes/api-push.js.map +1 -0
- package/dist/web/routes/api-runs.d.ts.map +1 -1
- package/dist/web/routes/api-runs.js +68 -0
- package/dist/web/routes/api-runs.js.map +1 -1
- package/dist/web/server.d.ts +12 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +32 -2
- package/dist/web/server.js.map +1 -1
- package/dist/workers/claude.d.ts +9 -0
- package/dist/workers/claude.d.ts.map +1 -1
- package/dist/workers/claude.js +9 -1
- package/dist/workers/claude.js.map +1 -1
- package/dist/workers/codex.d.ts +10 -0
- package/dist/workers/codex.d.ts.map +1 -1
- package/dist/workers/codex.js +10 -1
- package/dist/workers/codex.js.map +1 -1
- package/dist/workers/env.d.ts.map +1 -1
- package/dist/workers/env.js +18 -0
- package/dist/workers/env.js.map +1 -1
- package/dist/workers/errors.d.ts +103 -4
- package/dist/workers/errors.d.ts.map +1 -1
- package/dist/workers/errors.js +126 -6
- package/dist/workers/errors.js.map +1 -1
- package/dist/workers/parsers/reviewer-salvage.d.ts +4 -0
- package/dist/workers/parsers/reviewer-salvage.d.ts.map +1 -0
- package/dist/workers/parsers/reviewer-salvage.js +98 -0
- package/dist/workers/parsers/reviewer-salvage.js.map +1 -0
- package/package.json +3 -1
- package/web/dist/assets/index-DJDwanCU.css +1 -0
- package/web/dist/assets/index-bpbQ4hE9.js +17 -0
- package/web/dist/index.html +5 -3
- package/web/dist/sw.js +57 -0
- package/web/dist/assets/index-C9i2jSzd.js +0 -17
- package/web/dist/assets/index-Cbn2Kj-i.css +0 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ZodSchema } from 'zod';
|
|
2
|
+
import type { AiClient, AiRequest, AiResponse } from './types.js';
|
|
3
|
+
export declare class AnthropicClient implements AiClient {
|
|
4
|
+
readonly model: string;
|
|
5
|
+
private readonly apiKey;
|
|
6
|
+
readonly provider: "anthropic";
|
|
7
|
+
constructor(model: string, apiKey: string);
|
|
8
|
+
complete(req: AiRequest): Promise<AiResponse>;
|
|
9
|
+
completeStructured<T>(req: AiRequest, schema: ZodSchema<T>): Promise<T>;
|
|
10
|
+
private throwForStatus;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/ai/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AACpC,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AA4CjE,qBAAa,eAAgB,YAAW,QAAQ;IAI5C,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,QAAQ,CAAC,QAAQ,EAAG,WAAW,CAAS;gBAG7B,KAAK,EAAE,MAAM,EACL,MAAM,EAAE,MAAM;IAG3B,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAsF7C,kBAAkB,CAAC,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAS7E,OAAO,CAAC,cAAc;CAmBvB"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { AiAuthError, AiInvalidResponseError, AiRateLimitError, AiTransientError, } from './errors.js';
|
|
2
|
+
import { extractAndValidateJson } from './json-extract.js';
|
|
3
|
+
/**
|
|
4
|
+
* Hand-rolled Anthropic Messages API client.
|
|
5
|
+
*
|
|
6
|
+
* Uses `fetch` directly instead of `@anthropic-ai/sdk` to avoid a
|
|
7
|
+
* new runtime dependency — the Messages API is a single POST
|
|
8
|
+
* endpoint and the SDK's value-add (streaming, batching, tool-use)
|
|
9
|
+
* isn't needed for the stateless classification / generation work
|
|
10
|
+
* the internal AI layer does.
|
|
11
|
+
*
|
|
12
|
+
* Reference: https://docs.anthropic.com/en/api/messages
|
|
13
|
+
*/
|
|
14
|
+
const ANTHROPIC_MESSAGES_URL = 'https://api.anthropic.com/v1/messages';
|
|
15
|
+
const ANTHROPIC_API_VERSION = '2023-06-01';
|
|
16
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
17
|
+
const DEFAULT_MAX_TOKENS = 1024;
|
|
18
|
+
export class AnthropicClient {
|
|
19
|
+
model;
|
|
20
|
+
apiKey;
|
|
21
|
+
provider = 'anthropic';
|
|
22
|
+
constructor(model, apiKey) {
|
|
23
|
+
this.model = model;
|
|
24
|
+
this.apiKey = apiKey;
|
|
25
|
+
}
|
|
26
|
+
async complete(req) {
|
|
27
|
+
const timeoutMs = req.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
|
|
30
|
+
let response;
|
|
31
|
+
try {
|
|
32
|
+
response = await fetch(ANTHROPIC_MESSAGES_URL, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: {
|
|
35
|
+
'content-type': 'application/json',
|
|
36
|
+
'x-api-key': this.apiKey,
|
|
37
|
+
'anthropic-version': ANTHROPIC_API_VERSION,
|
|
38
|
+
},
|
|
39
|
+
body: JSON.stringify({
|
|
40
|
+
model: this.model,
|
|
41
|
+
max_tokens: req.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
42
|
+
temperature: req.temperature ?? 0,
|
|
43
|
+
system: req.system,
|
|
44
|
+
messages: [{ role: 'user', content: req.user }],
|
|
45
|
+
}),
|
|
46
|
+
signal: controller.signal,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
if (err instanceof Error && err.name === 'AbortError') {
|
|
51
|
+
throw new AiTransientError(this.provider, this.model, `request timed out after ${timeoutMs}ms`);
|
|
52
|
+
}
|
|
53
|
+
throw new AiTransientError(this.provider, this.model, err instanceof Error ? err.message : String(err));
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
clearTimeout(timeoutHandle);
|
|
57
|
+
}
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
const bodyText = await response.text().catch(() => '');
|
|
60
|
+
this.throwForStatus(response.status, bodyText);
|
|
61
|
+
}
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = (await response.json());
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
throw new AiInvalidResponseError(this.provider, this.model, `response body was not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
68
|
+
}
|
|
69
|
+
const text = (parsed.content ?? [])
|
|
70
|
+
.filter((b) => b.type === 'text' && typeof b.text === 'string')
|
|
71
|
+
.map((b) => b.text ?? '')
|
|
72
|
+
.join('');
|
|
73
|
+
if (!text) {
|
|
74
|
+
throw new AiInvalidResponseError(this.provider, this.model, 'response contained no text content blocks', JSON.stringify(parsed));
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
text,
|
|
78
|
+
resolvedModel: parsed.model ?? this.model,
|
|
79
|
+
finishReason: mapStopReason(parsed.stop_reason),
|
|
80
|
+
usage: {
|
|
81
|
+
promptTokens: (parsed.usage.input_tokens ?? 0) +
|
|
82
|
+
(parsed.usage.cache_creation_input_tokens ?? 0),
|
|
83
|
+
completionTokens: parsed.usage.output_tokens ?? 0,
|
|
84
|
+
...(parsed.usage.cache_read_input_tokens
|
|
85
|
+
? { cacheReadTokens: parsed.usage.cache_read_input_tokens }
|
|
86
|
+
: {}),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async completeStructured(req, schema) {
|
|
91
|
+
const augmented = {
|
|
92
|
+
...req,
|
|
93
|
+
system: `${req.system}\n\nReply with ONLY a JSON object. Do not wrap it in markdown code fences or add any prose.`,
|
|
94
|
+
};
|
|
95
|
+
const response = await this.complete(augmented);
|
|
96
|
+
return extractAndValidateJson(response.text, schema, this.provider, this.model);
|
|
97
|
+
}
|
|
98
|
+
throwForStatus(status, body) {
|
|
99
|
+
const snippet = body.slice(0, 500);
|
|
100
|
+
if (status === 401 || status === 403) {
|
|
101
|
+
throw new AiAuthError(this.provider, this.model, `HTTP ${status}: ${snippet}`);
|
|
102
|
+
}
|
|
103
|
+
if (status === 429) {
|
|
104
|
+
const retryAfterMs = parseRetryAfter(body);
|
|
105
|
+
throw new AiRateLimitError(this.provider, this.model, `HTTP 429: ${snippet}`, retryAfterMs);
|
|
106
|
+
}
|
|
107
|
+
if (status >= 500 && status < 600) {
|
|
108
|
+
throw new AiTransientError(this.provider, this.model, `HTTP ${status}: ${snippet}`, status);
|
|
109
|
+
}
|
|
110
|
+
throw new AiTransientError(this.provider, this.model, `HTTP ${status}: ${snippet}`, status);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function mapStopReason(raw) {
|
|
114
|
+
if (raw === 'max_tokens')
|
|
115
|
+
return 'length';
|
|
116
|
+
if (raw === 'end_turn' || raw === 'stop_sequence')
|
|
117
|
+
return 'stop';
|
|
118
|
+
if (raw === null)
|
|
119
|
+
return 'stop';
|
|
120
|
+
return 'stop';
|
|
121
|
+
}
|
|
122
|
+
function parseRetryAfter(body) {
|
|
123
|
+
// Anthropic sometimes surfaces retry hints in the error body.
|
|
124
|
+
// Fall back to undefined — callers shouldn't rely on this.
|
|
125
|
+
try {
|
|
126
|
+
const parsed = JSON.parse(body);
|
|
127
|
+
if (parsed.error && typeof parsed.error.retry_after === 'number') {
|
|
128
|
+
return parsed.error.retry_after * 1000;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// ignore
|
|
133
|
+
}
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/ai/anthropic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed errors for the Phase 3 AI layer.
|
|
3
|
+
*
|
|
4
|
+
* The hierarchy mirrors the shape of R2's `WorkerError` family so
|
|
5
|
+
* consumers can classify failures the same way across CLI workers
|
|
6
|
+
* and direct-API calls:
|
|
7
|
+
*
|
|
8
|
+
* - `AiAuthError` → operator must fix credentials
|
|
9
|
+
* - `AiRateLimitError` → transient, caller may retry with backoff
|
|
10
|
+
* - `AiInvalidResponseError` → provider returned something we
|
|
11
|
+
* couldn't parse (schema violation, truncated JSON, etc.)
|
|
12
|
+
* - `AiTransientError` → network blip, 5xx, timeout — safe to retry
|
|
13
|
+
* - `AiError` (base) → everything else
|
|
14
|
+
*
|
|
15
|
+
* Each subclass carries `provider` + `model` context so error
|
|
16
|
+
* logs and cost-report drilldowns can point at the specific
|
|
17
|
+
* endpoint that misbehaved.
|
|
18
|
+
*/
|
|
19
|
+
export declare abstract class AiError extends Error {
|
|
20
|
+
readonly provider: string;
|
|
21
|
+
readonly model: string;
|
|
22
|
+
abstract readonly code: string;
|
|
23
|
+
constructor(provider: string, model: string, message: string);
|
|
24
|
+
}
|
|
25
|
+
export declare class AiAuthError extends AiError {
|
|
26
|
+
readonly code: "AI_AUTH_FAILURE";
|
|
27
|
+
constructor(provider: string, model: string, detail: string);
|
|
28
|
+
}
|
|
29
|
+
export declare class AiRateLimitError extends AiError {
|
|
30
|
+
readonly retryAfterMs?: number | undefined;
|
|
31
|
+
readonly code: "AI_RATE_LIMIT";
|
|
32
|
+
constructor(provider: string, model: string, detail: string, retryAfterMs?: number | undefined);
|
|
33
|
+
}
|
|
34
|
+
export declare class AiInvalidResponseError extends AiError {
|
|
35
|
+
readonly rawResponse?: string | undefined;
|
|
36
|
+
readonly code: "AI_INVALID_RESPONSE";
|
|
37
|
+
constructor(provider: string, model: string, detail: string, rawResponse?: string | undefined);
|
|
38
|
+
}
|
|
39
|
+
export declare class AiTransientError extends AiError {
|
|
40
|
+
readonly httpStatus?: number | undefined;
|
|
41
|
+
readonly code: "AI_TRANSIENT_FAILURE";
|
|
42
|
+
constructor(provider: string, model: string, detail: string, httpStatus?: number | undefined);
|
|
43
|
+
}
|
|
44
|
+
export declare function isAiError(err: unknown): err is AiError;
|
|
45
|
+
export declare function isTransientAiError(err: unknown): err is AiTransientError | AiRateLimitError;
|
|
46
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/ai/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,8BAAsB,OAAQ,SAAQ,KAAK;aAIvB,QAAQ,EAAE,MAAM;aAChB,KAAK,EAAE,MAAM;IAJ/B,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;gBAGZ,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EAC7B,OAAO,EAAE,MAAM;CAKlB;AAED,qBAAa,WAAY,SAAQ,OAAO;IACtC,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAS;gBAC9B,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAG5D;AAED,qBAAa,gBAAiB,SAAQ,OAAO;aAMzB,YAAY,CAAC,EAAE,MAAM;IALvC,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAS;gBAEtC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACE,YAAY,CAAC,EAAE,MAAM,YAAA;CAIxC;AAED,qBAAa,sBAAuB,SAAQ,OAAO;aAM/B,WAAW,CAAC,EAAE,MAAM;IALtC,QAAQ,CAAC,IAAI,EAAG,qBAAqB,CAAS;gBAE5C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACE,WAAW,CAAC,EAAE,MAAM,YAAA;CAIvC;AAED,qBAAa,gBAAiB,SAAQ,OAAO;aAMzB,UAAU,CAAC,EAAE,MAAM;IALrC,QAAQ,CAAC,IAAI,EAAG,sBAAsB,CAAS;gBAE7C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACE,UAAU,CAAC,EAAE,MAAM,YAAA;CAItC;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,OAAO,CAEtD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,gBAAgB,GAAG,gBAAgB,CAE3F"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed errors for the Phase 3 AI layer.
|
|
3
|
+
*
|
|
4
|
+
* The hierarchy mirrors the shape of R2's `WorkerError` family so
|
|
5
|
+
* consumers can classify failures the same way across CLI workers
|
|
6
|
+
* and direct-API calls:
|
|
7
|
+
*
|
|
8
|
+
* - `AiAuthError` → operator must fix credentials
|
|
9
|
+
* - `AiRateLimitError` → transient, caller may retry with backoff
|
|
10
|
+
* - `AiInvalidResponseError` → provider returned something we
|
|
11
|
+
* couldn't parse (schema violation, truncated JSON, etc.)
|
|
12
|
+
* - `AiTransientError` → network blip, 5xx, timeout — safe to retry
|
|
13
|
+
* - `AiError` (base) → everything else
|
|
14
|
+
*
|
|
15
|
+
* Each subclass carries `provider` + `model` context so error
|
|
16
|
+
* logs and cost-report drilldowns can point at the specific
|
|
17
|
+
* endpoint that misbehaved.
|
|
18
|
+
*/
|
|
19
|
+
export class AiError extends Error {
|
|
20
|
+
provider;
|
|
21
|
+
model;
|
|
22
|
+
constructor(provider, model, message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.provider = provider;
|
|
25
|
+
this.model = model;
|
|
26
|
+
this.name = this.constructor.name;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class AiAuthError extends AiError {
|
|
30
|
+
code = 'AI_AUTH_FAILURE';
|
|
31
|
+
constructor(provider, model, detail) {
|
|
32
|
+
super(provider, model, `${provider} authentication failure: ${detail}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class AiRateLimitError extends AiError {
|
|
36
|
+
retryAfterMs;
|
|
37
|
+
code = 'AI_RATE_LIMIT';
|
|
38
|
+
constructor(provider, model, detail, retryAfterMs) {
|
|
39
|
+
super(provider, model, `${provider} rate-limited: ${detail}`);
|
|
40
|
+
this.retryAfterMs = retryAfterMs;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export class AiInvalidResponseError extends AiError {
|
|
44
|
+
rawResponse;
|
|
45
|
+
code = 'AI_INVALID_RESPONSE';
|
|
46
|
+
constructor(provider, model, detail, rawResponse) {
|
|
47
|
+
super(provider, model, `${provider} returned invalid response: ${detail}`);
|
|
48
|
+
this.rawResponse = rawResponse;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export class AiTransientError extends AiError {
|
|
52
|
+
httpStatus;
|
|
53
|
+
code = 'AI_TRANSIENT_FAILURE';
|
|
54
|
+
constructor(provider, model, detail, httpStatus) {
|
|
55
|
+
super(provider, model, `${provider} transient failure: ${detail}`);
|
|
56
|
+
this.httpStatus = httpStatus;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function isAiError(err) {
|
|
60
|
+
return err instanceof AiError;
|
|
61
|
+
}
|
|
62
|
+
export function isTransientAiError(err) {
|
|
63
|
+
return err instanceof AiTransientError || err instanceof AiRateLimitError;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/ai/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,OAAgB,OAAQ,SAAQ,KAAK;IAIvB;IACA;IAFlB,YACkB,QAAgB,EAChB,KAAa,EAC7B,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAA;QAJE,aAAQ,GAAR,QAAQ,CAAQ;QAChB,UAAK,GAAL,KAAK,CAAQ;QAI7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;IACnC,CAAC;CACF;AAED,MAAM,OAAO,WAAY,SAAQ,OAAO;IAC7B,IAAI,GAAG,iBAA0B,CAAA;IAC1C,YAAY,QAAgB,EAAE,KAAa,EAAE,MAAc;QACzD,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,QAAQ,4BAA4B,MAAM,EAAE,CAAC,CAAA;IACzE,CAAC;CACF;AAED,MAAM,OAAO,gBAAiB,SAAQ,OAAO;IAMzB;IALT,IAAI,GAAG,eAAwB,CAAA;IACxC,YACE,QAAgB,EAChB,KAAa,EACb,MAAc,EACE,YAAqB;QAErC,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,QAAQ,kBAAkB,MAAM,EAAE,CAAC,CAAA;QAF7C,iBAAY,GAAZ,YAAY,CAAS;IAGvC,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,OAAO;IAM/B;IALT,IAAI,GAAG,qBAA8B,CAAA;IAC9C,YACE,QAAgB,EAChB,KAAa,EACb,MAAc,EACE,WAAoB;QAEpC,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,QAAQ,+BAA+B,MAAM,EAAE,CAAC,CAAA;QAF1D,gBAAW,GAAX,WAAW,CAAS;IAGtC,CAAC;CACF;AAED,MAAM,OAAO,gBAAiB,SAAQ,OAAO;IAMzB;IALT,IAAI,GAAG,sBAA+B,CAAA;IAC/C,YACE,QAAgB,EAChB,KAAa,EACb,MAAc,EACE,UAAmB;QAEnC,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,QAAQ,uBAAuB,MAAM,EAAE,CAAC,CAAA;QAFlD,eAAU,GAAV,UAAU,CAAS;IAGrC,CAAC;CACF;AAED,MAAM,UAAU,SAAS,CAAC,GAAY;IACpC,OAAO,GAAG,YAAY,OAAO,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,GAAY;IAC7C,OAAO,GAAG,YAAY,gBAAgB,IAAI,GAAG,YAAY,gBAAgB,CAAA;AAC3E,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Config } from '../config/schema.js';
|
|
2
|
+
import type { AiClient } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build an `AiClient` from the `ai.internal` config block, or
|
|
5
|
+
* return `null` when the layer isn't configured.
|
|
6
|
+
*
|
|
7
|
+
* Returns `null` (and logs a warning) rather than throwing when:
|
|
8
|
+
* - Provider/model/apiKeyEnv are not set (intentional — layer is
|
|
9
|
+
* off and every consumer falls through to its pre-Phase-3
|
|
10
|
+
* behavior)
|
|
11
|
+
* - The configured `apiKeyEnv` var is missing from the
|
|
12
|
+
* environment (operator has enabled a feature but didn't export
|
|
13
|
+
* the key; the per-feature consumer should skip gracefully
|
|
14
|
+
* rather than block every run)
|
|
15
|
+
*
|
|
16
|
+
* Throws only for programmer errors (invalid provider value, which
|
|
17
|
+
* the schema validation should catch first).
|
|
18
|
+
*/
|
|
19
|
+
export declare function createAiClient(config: Config): AiClient | null;
|
|
20
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/ai/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAK1C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CA0B9D"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { AnthropicClient } from './anthropic.js';
|
|
2
|
+
import { OpenRouterClient } from './openrouter.js';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
4
|
+
/**
|
|
5
|
+
* Build an `AiClient` from the `ai.internal` config block, or
|
|
6
|
+
* return `null` when the layer isn't configured.
|
|
7
|
+
*
|
|
8
|
+
* Returns `null` (and logs a warning) rather than throwing when:
|
|
9
|
+
* - Provider/model/apiKeyEnv are not set (intentional — layer is
|
|
10
|
+
* off and every consumer falls through to its pre-Phase-3
|
|
11
|
+
* behavior)
|
|
12
|
+
* - The configured `apiKeyEnv` var is missing from the
|
|
13
|
+
* environment (operator has enabled a feature but didn't export
|
|
14
|
+
* the key; the per-feature consumer should skip gracefully
|
|
15
|
+
* rather than block every run)
|
|
16
|
+
*
|
|
17
|
+
* Throws only for programmer errors (invalid provider value, which
|
|
18
|
+
* the schema validation should catch first).
|
|
19
|
+
*/
|
|
20
|
+
export function createAiClient(config) {
|
|
21
|
+
const { internal } = config.ai;
|
|
22
|
+
if (!internal.provider || !internal.model || !internal.apiKeyEnv) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const apiKey = process.env[internal.apiKeyEnv];
|
|
26
|
+
if (!apiKey) {
|
|
27
|
+
logger.warn({ apiKeyEnv: internal.apiKeyEnv, provider: internal.provider }, 'ai.internal.apiKeyEnv not set in environment — direct-LLM features disabled');
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
switch (internal.provider) {
|
|
31
|
+
case 'anthropic':
|
|
32
|
+
return new AnthropicClient(internal.model, apiKey);
|
|
33
|
+
case 'openrouter':
|
|
34
|
+
return new OpenRouterClient(internal.model, apiKey);
|
|
35
|
+
default: {
|
|
36
|
+
const _exhaustive = internal.provider;
|
|
37
|
+
throw new Error(`Unknown ai.internal.provider: ${String(_exhaustive)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/ai/factory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAE3C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,EAAE,CAAA;IAE9B,IAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;QACjE,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;IAC9C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,CAAC,IAAI,CACT,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAC9D,6EAA6E,CAC9E,CAAA;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,QAAQ,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAC1B,KAAK,WAAW;YACd,OAAO,IAAI,eAAe,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QACpD,KAAK,YAAY;YACf,OAAO,IAAI,gBAAgB,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QACrD,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,WAAW,GAAU,QAAQ,CAAC,QAAQ,CAAA;YAC5C,MAAM,IAAI,KAAK,CAAC,iCAAiC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;QACzE,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ZodSchema } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Extract and validate the first top-level JSON object from a
|
|
4
|
+
* model response. Tolerates:
|
|
5
|
+
* - Pure JSON (`{...}`)
|
|
6
|
+
* - JSON wrapped in fenced code blocks (` ```json ... ``` `)
|
|
7
|
+
* - JSON preceded or followed by prose ("Here's the JSON: { ... }
|
|
8
|
+
* Hope this helps!")
|
|
9
|
+
* - Models that emit the JSON inside an "answer" text block
|
|
10
|
+
*
|
|
11
|
+
* Rejects (throws `AiInvalidResponseError`) when:
|
|
12
|
+
* - No balanced `{...}` region exists in the response
|
|
13
|
+
* - The extracted region isn't valid JSON
|
|
14
|
+
* - The parsed object doesn't match the provided schema
|
|
15
|
+
*
|
|
16
|
+
* Used by `AiClient.completeStructured` for triage, reviewer parse
|
|
17
|
+
* fallback, and any future structured-generation task.
|
|
18
|
+
*/
|
|
19
|
+
export declare function extractAndValidateJson<T>(raw: string, schema: ZodSchema<T>, provider: string, model: string): T;
|
|
20
|
+
//# sourceMappingURL=json-extract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-extract.d.ts","sourceRoot":"","sources":["../../src/ai/json-extract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AAGpC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,CAAC,CAqCH"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { AiInvalidResponseError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Extract and validate the first top-level JSON object from a
|
|
4
|
+
* model response. Tolerates:
|
|
5
|
+
* - Pure JSON (`{...}`)
|
|
6
|
+
* - JSON wrapped in fenced code blocks (` ```json ... ``` `)
|
|
7
|
+
* - JSON preceded or followed by prose ("Here's the JSON: { ... }
|
|
8
|
+
* Hope this helps!")
|
|
9
|
+
* - Models that emit the JSON inside an "answer" text block
|
|
10
|
+
*
|
|
11
|
+
* Rejects (throws `AiInvalidResponseError`) when:
|
|
12
|
+
* - No balanced `{...}` region exists in the response
|
|
13
|
+
* - The extracted region isn't valid JSON
|
|
14
|
+
* - The parsed object doesn't match the provided schema
|
|
15
|
+
*
|
|
16
|
+
* Used by `AiClient.completeStructured` for triage, reviewer parse
|
|
17
|
+
* fallback, and any future structured-generation task.
|
|
18
|
+
*/
|
|
19
|
+
export function extractAndValidateJson(raw, schema, provider, model) {
|
|
20
|
+
const candidate = extractFirstJsonObject(raw);
|
|
21
|
+
if (candidate === null) {
|
|
22
|
+
throw new AiInvalidResponseError(provider, model, 'no JSON object found in response', raw);
|
|
23
|
+
}
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = JSON.parse(candidate);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
throw new AiInvalidResponseError(provider, model, `JSON parse failed: ${err instanceof Error ? err.message : String(err)}`, raw);
|
|
30
|
+
}
|
|
31
|
+
const validation = schema.safeParse(parsed);
|
|
32
|
+
if (!validation.success) {
|
|
33
|
+
const issues = validation.error.issues
|
|
34
|
+
.map((i) => `${i.path.join('.') || '<root>'}: ${i.message}`)
|
|
35
|
+
.join('; ');
|
|
36
|
+
throw new AiInvalidResponseError(provider, model, `schema validation failed: ${issues}`, raw);
|
|
37
|
+
}
|
|
38
|
+
return validation.data;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find the first balanced `{...}` region in `raw`. Uses a simple
|
|
42
|
+
* brace-counter that tolerates nested objects and strings (strings
|
|
43
|
+
* are parsed carefully to avoid counting `{` inside quoted text).
|
|
44
|
+
*
|
|
45
|
+
* Returns the raw substring (suitable for `JSON.parse`) or `null`
|
|
46
|
+
* when no balanced region exists.
|
|
47
|
+
*/
|
|
48
|
+
function extractFirstJsonObject(raw) {
|
|
49
|
+
// Fast path: strip fenced code block markers so the brace scan
|
|
50
|
+
// sees only content.
|
|
51
|
+
const trimmed = raw
|
|
52
|
+
.replace(/^\s*```(?:json)?\s*/i, '')
|
|
53
|
+
.replace(/\s*```\s*$/i, '')
|
|
54
|
+
.trim();
|
|
55
|
+
const start = trimmed.indexOf('{');
|
|
56
|
+
if (start < 0)
|
|
57
|
+
return null;
|
|
58
|
+
let depth = 0;
|
|
59
|
+
let inString = false;
|
|
60
|
+
let escape = false;
|
|
61
|
+
for (let i = start; i < trimmed.length; i++) {
|
|
62
|
+
const ch = trimmed[i];
|
|
63
|
+
if (escape) {
|
|
64
|
+
escape = false;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (inString) {
|
|
68
|
+
if (ch === '\\') {
|
|
69
|
+
escape = true;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (ch === '"')
|
|
73
|
+
inString = false;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (ch === '"') {
|
|
77
|
+
inString = true;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (ch === '{') {
|
|
81
|
+
depth++;
|
|
82
|
+
}
|
|
83
|
+
else if (ch === '}') {
|
|
84
|
+
depth--;
|
|
85
|
+
if (depth === 0) {
|
|
86
|
+
return trimmed.slice(start, i + 1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=json-extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-extract.js","sourceRoot":"","sources":["../../src/ai/json-extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ZodSchema } from 'zod';
|
|
2
|
+
import type { CostTracker } from '../loop/cost.js';
|
|
3
|
+
import type { AiClient, AiRequest, AiResponse, AiProvider } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Phase 3: cost-recording wrapper around an `AiClient`.
|
|
6
|
+
*
|
|
7
|
+
* Every direct-API call records its token usage through the R4
|
|
8
|
+
* cost ledger so internal AI spend shows up alongside CLI worker
|
|
9
|
+
* spend in `/api/cost/health`, `run_cost_entries`, and the
|
|
10
|
+
* `nightorch_cost_token_source_total` Prometheus counter. The
|
|
11
|
+
* consumer passes in a `runId` and a `stepId` so the row gets
|
|
12
|
+
* attributed to the right attempt and phase, tagged
|
|
13
|
+
* `worker_type='internal-ai'` and `token_source='measured_api'`.
|
|
14
|
+
*
|
|
15
|
+
* The wrapper also handles the "call from somewhere that doesn't
|
|
16
|
+
* have a runId" case — discovery-time triage classifies issues
|
|
17
|
+
* before an attempt exists, so passing `runId: null` records the
|
|
18
|
+
* usage to a sentinel bookkeeping row (the ledger accepts
|
|
19
|
+
* per-worker-type rows without a stepId). Those rows show up in
|
|
20
|
+
* `/api/cost/health` aggregates but don't inflate any particular
|
|
21
|
+
* run's per-attempt cost.
|
|
22
|
+
*/
|
|
23
|
+
export declare class LedgerRecordingAiClient implements AiClient {
|
|
24
|
+
private readonly inner;
|
|
25
|
+
private readonly costTracker;
|
|
26
|
+
private readonly getContext;
|
|
27
|
+
readonly provider: AiProvider;
|
|
28
|
+
readonly model: string;
|
|
29
|
+
constructor(inner: AiClient, costTracker: CostTracker, getContext: () => AiCallContext);
|
|
30
|
+
complete(req: AiRequest): Promise<AiResponse>;
|
|
31
|
+
completeStructured<T>(req: AiRequest, schema: ZodSchema<T>): Promise<T>;
|
|
32
|
+
private record;
|
|
33
|
+
/**
|
|
34
|
+
* Cheap cost estimator that reuses the project's default
|
|
35
|
+
* per-token rates. This is intentionally loose — for precise
|
|
36
|
+
* accounting the operator should add a per-model entry to
|
|
37
|
+
* `cost.pricing.models` keyed by `pricingModelKey`, and a future
|
|
38
|
+
* Phase 3+ pass can wire this through `estimateWorkerCost`. For
|
|
39
|
+
* now the recorder still captures exact tokens; only the USD
|
|
40
|
+
* amount is approximate.
|
|
41
|
+
*/
|
|
42
|
+
private estimateCost;
|
|
43
|
+
}
|
|
44
|
+
/** Shape the caller provides to tag a recording. */
|
|
45
|
+
export interface AiCallContext {
|
|
46
|
+
/** Attempt id to attribute the ledger row to. Null when the
|
|
47
|
+
* call happens before any attempt has been created (e.g.
|
|
48
|
+
* discovery-time triage). */
|
|
49
|
+
runId: string | null;
|
|
50
|
+
/** Short phase/step identifier — `'triage'`, `'pr-body'`,
|
|
51
|
+
* `'reviewer-salvage'`, etc. */
|
|
52
|
+
stepId: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Convenience wrapper: given a raw `AiClient` and a
|
|
56
|
+
* `CostTracker`, return a client that records usage through the
|
|
57
|
+
* ledger using a context getter supplied by the caller.
|
|
58
|
+
*/
|
|
59
|
+
export declare function withLedger(inner: AiClient, costTracker: CostTracker, getContext: () => AiCallContext): AiClient;
|
|
60
|
+
//# sourceMappingURL=ledger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/ai/ledger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AACpC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAG7E;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,uBAAwB,YAAW,QAAQ;IAKpD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAN7B,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;gBAGH,KAAK,EAAE,QAAQ,EACf,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,MAAM,aAAa;IAM5C,QAAQ,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAM7C,kBAAkB,CAAC,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAgB7E,OAAO,CAAC,MAAM;IA2Cd;;;;;;;;OAQG;IACH,OAAO,CAAC,YAAY;CAYrB;AAED,oDAAoD;AACpD,MAAM,WAAW,aAAa;IAC5B;;iCAE6B;IAC7B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;oCACgC;IAChC,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,QAAQ,EACf,WAAW,EAAE,WAAW,EACxB,UAAU,EAAE,MAAM,aAAa,GAC9B,QAAQ,CAEV"}
|