@lanjak/council 0.0.0-stage → 0.5.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/README.md +512 -2
- package/dist/artifacts.d.ts +24 -0
- package/dist/artifacts.js +227 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/blocks.d.ts +12 -0
- package/dist/blocks.js +197 -0
- package/dist/blocks.js.map +1 -0
- package/dist/chairman.d.ts +7 -0
- package/dist/chairman.js +51 -0
- package/dist/chairman.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +109 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/debate.d.ts +15 -0
- package/dist/commands/debate.js +172 -0
- package/dist/commands/debate.js.map +1 -0
- package/dist/commands/home.d.ts +1 -0
- package/dist/commands/home.js +27 -0
- package/dist/commands/home.js.map +1 -0
- package/dist/commands/hook.d.ts +3 -0
- package/dist/commands/hook.js +138 -0
- package/dist/commands/hook.js.map +1 -0
- package/dist/commands/plan.d.ts +6 -0
- package/dist/commands/plan.js +23 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/review.d.ts +18 -0
- package/dist/commands/review.js +37 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +23 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +101 -0
- package/dist/config.js.map +1 -0
- package/dist/council.d.ts +3 -0
- package/dist/council.js +61 -0
- package/dist/council.js.map +1 -0
- package/dist/debate/loop.d.ts +28 -0
- package/dist/debate/loop.js +188 -0
- package/dist/debate/loop.js.map +1 -0
- package/dist/debate/prompts.d.ts +10 -0
- package/dist/debate/prompts.js +47 -0
- package/dist/debate/prompts.js.map +1 -0
- package/dist/debate/render.d.ts +22 -0
- package/dist/debate/render.js +112 -0
- package/dist/debate/render.js.map +1 -0
- package/dist/debate/session.d.ts +7 -0
- package/dist/debate/session.js +85 -0
- package/dist/debate/session.js.map +1 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +18 -0
- package/dist/errors.js.map +1 -0
- package/dist/exit.d.ts +7 -0
- package/dist/exit.js +13 -0
- package/dist/exit.js.map +1 -0
- package/dist/hooks/gate.d.ts +12 -0
- package/dist/hooks/gate.js +41 -0
- package/dist/hooks/gate.js.map +1 -0
- package/dist/hooks/payload.d.ts +8 -0
- package/dist/hooks/payload.js +63 -0
- package/dist/hooks/payload.js.map +1 -0
- package/dist/hooks/state.d.ts +6 -0
- package/dist/hooks/state.js +73 -0
- package/dist/hooks/state.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/input.d.ts +14 -0
- package/dist/input.js +30 -0
- package/dist/input.js.map +1 -0
- package/dist/output.d.ts +3 -0
- package/dist/output.js +80 -0
- package/dist/output.js.map +1 -0
- package/dist/prompts.d.ts +3 -0
- package/dist/prompts.js +48 -0
- package/dist/prompts.js.map +1 -0
- package/dist/providers/anthropic.d.ts +12 -0
- package/dist/providers/anthropic.js +98 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/base.d.ts +35 -0
- package/dist/providers/base.js +7 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/constants.d.ts +2 -0
- package/dist/providers/constants.js +11 -0
- package/dist/providers/constants.js.map +1 -0
- package/dist/providers/index.d.ts +6 -0
- package/dist/providers/index.js +20 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +12 -0
- package/dist/providers/openai-compatible.js +68 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/runners/plan.d.ts +14 -0
- package/dist/runners/plan.js +43 -0
- package/dist/runners/plan.js.map +1 -0
- package/dist/runners/review.d.ts +14 -0
- package/dist/runners/review.js +45 -0
- package/dist/runners/review.js.map +1 -0
- package/dist/stdin.d.ts +9 -0
- package/dist/stdin.js +51 -0
- package/dist/stdin.js.map +1 -0
- package/dist/types.d.ts +114 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/verdict.d.ts +2 -0
- package/dist/verdict.js +12 -0
- package/dist/verdict.js.map +1 -0
- package/package.json +54 -4
- package/skills/council/SKILL.md +82 -0
package/dist/errors.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export class CouncilError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
constructor(message, code) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.name = 'CouncilError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function formatError(error) {
|
|
10
|
+
if (error instanceof CouncilError) {
|
|
11
|
+
return `error: ${error.code}: ${error.message}\nhelp: npx -y @lanjak/council setup`;
|
|
12
|
+
}
|
|
13
|
+
if (error instanceof Error) {
|
|
14
|
+
return `error: ${error.message}\nhelp: npx -y @lanjak/council --help`;
|
|
15
|
+
}
|
|
16
|
+
return `error: ${String(error)}\nhelp: npx -y @lanjak/council --help`;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,YAAa,SAAQ,KAAK;IAGnB;IAFlB,YACE,OAAe,EACC,IAAY;QAE5B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,SAAI,GAAJ,IAAI,CAAQ;QAG5B,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;QAClC,OAAO,UAAU,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,sCAAsC,CAAC;IACtF,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,UAAU,KAAK,CAAC,OAAO,uCAAuC,CAAC;IACxE,CAAC;IACD,OAAO,UAAU,MAAM,CAAC,KAAK,CAAC,uCAAuC,CAAC;AACxE,CAAC"}
|
package/dist/exit.d.ts
ADDED
package/dist/exit.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Exit codes 0 (success), 1 (CouncilError) and 2 (Commander) are already
|
|
2
|
+
// taken, so blocks and unknowns claim 3 and 4. Default stays 0 so existing
|
|
3
|
+
// scripts are unaffected.
|
|
4
|
+
export function exitCodeFor(output, options) {
|
|
5
|
+
const onBlock = options.failOnBlock === true || options.strict === true;
|
|
6
|
+
const onUnknown = options.failOnUnknown === true || options.strict === true;
|
|
7
|
+
if (onBlock && output.blocked)
|
|
8
|
+
return 3;
|
|
9
|
+
if (onUnknown && output.unknownJudges.length > 0)
|
|
10
|
+
return 4;
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=exit.js.map
|
package/dist/exit.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exit.js","sourceRoot":"","sources":["../src/exit.ts"],"names":[],"mappings":"AAQA,yEAAyE;AACzE,2EAA2E;AAC3E,0BAA0B;AAC1B,MAAM,UAAU,WAAW,CAAC,MAAqB,EAAE,OAA0B;IAC3E,MAAM,OAAO,GAAG,OAAO,CAAC,WAAW,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC;IACxE,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC;IAE5E,IAAI,OAAO,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC;IACxC,IAAI,SAAS,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IAC3D,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { JudgeResult } from '../types.js';
|
|
2
|
+
export type Verdict = 'pass' | 'concerns' | 'fail';
|
|
3
|
+
export interface GateDecision {
|
|
4
|
+
outcome: 'block' | 'pass' | 'fail-open';
|
|
5
|
+
reason: string;
|
|
6
|
+
verdicts: {
|
|
7
|
+
provider: string;
|
|
8
|
+
verdict: Verdict | 'error';
|
|
9
|
+
}[];
|
|
10
|
+
}
|
|
11
|
+
export declare function parseVerdict(response: string): Verdict;
|
|
12
|
+
export declare function decideGate(judges: JudgeResult[]): GateDecision;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function parseVerdict(response) {
|
|
2
|
+
const lines = response.split('\n').map((l) => l.trim()).filter((l) => l.length > 0);
|
|
3
|
+
const last = lines[lines.length - 1] ?? '';
|
|
4
|
+
const match = /^VERDICT:\s*(pass|concerns|fail)\s*$/i.exec(last);
|
|
5
|
+
if (!match)
|
|
6
|
+
return 'concerns';
|
|
7
|
+
return match[1].toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
export function decideGate(judges) {
|
|
10
|
+
const verdicts = judges.map((j) => ({
|
|
11
|
+
provider: j.provider,
|
|
12
|
+
verdict: (j.status === 'success' ? parseVerdict(j.response ?? '') : 'error'),
|
|
13
|
+
}));
|
|
14
|
+
const responding = verdicts.filter((v) => v.verdict !== 'error');
|
|
15
|
+
const errored = verdicts.filter((v) => v.verdict === 'error');
|
|
16
|
+
// Fail open when the response pool is too thin to mean anything: none
|
|
17
|
+
// responded, or at most half of the configured judges did.
|
|
18
|
+
if (responding.length === 0 || responding.length <= judges.length / 2) {
|
|
19
|
+
return {
|
|
20
|
+
outcome: 'fail-open',
|
|
21
|
+
reason: errored.length > 0
|
|
22
|
+
? `quorum not met (${responding.length} of ${judges.length} responded; errored: ${errored.map((v) => v.provider).join(', ')})`
|
|
23
|
+
: `quorum not met (${responding.length} of ${judges.length} responded)`,
|
|
24
|
+
verdicts,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const fails = responding.filter((v) => v.verdict === 'fail').length;
|
|
28
|
+
if (fails > responding.length / 2) {
|
|
29
|
+
return {
|
|
30
|
+
outcome: 'block',
|
|
31
|
+
reason: `${fails} of ${responding.length} responding judges returned VERDICT: fail`,
|
|
32
|
+
verdicts,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
outcome: 'pass',
|
|
37
|
+
reason: `${fails} of ${responding.length} responding judges returned VERDICT: fail`,
|
|
38
|
+
verdicts,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/hooks/gate.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,MAAM,KAAK,GAAG,uCAAuC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjE,IAAI,CAAC,KAAK;QAAE,OAAO,UAAU,CAAC;IAC9B,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAa,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAqB;IAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAClC,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAsB;KAClG,CAAC,CAAC,CAAC;IAEJ,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IAE9D,sEAAsE;IACtE,2DAA2D;IAC3D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtE,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,MAAM,EACJ,OAAO,CAAC,MAAM,GAAG,CAAC;gBAChB,CAAC,CAAC,mBAAmB,UAAU,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM,wBAAwB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBAC9H,CAAC,CAAC,mBAAmB,UAAU,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM,aAAa;YAC3E,QAAQ;SACT,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IACpE,IAAI,KAAK,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO;YACL,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,GAAG,KAAK,OAAO,UAAU,CAAC,MAAM,2CAA2C;YACnF,QAAQ;SACT,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,MAAM;QACf,MAAM,EAAE,GAAG,KAAK,OAAO,UAAU,CAAC,MAAM,2CAA2C;QACnF,QAAQ;KACT,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Best-effort parsing of whatever JSON a harness pipes in. Unknown shapes and
|
|
2
|
+
// non-JSON input yield an empty payload - never an error. Key candidates are
|
|
3
|
+
// scanned loosely; this is a documented maintenance surface, not a contract.
|
|
4
|
+
export function parseHookPayload(stdinJson) {
|
|
5
|
+
let obj;
|
|
6
|
+
try {
|
|
7
|
+
const parsed = JSON.parse(stdinJson);
|
|
8
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
9
|
+
return {};
|
|
10
|
+
obj = parsed;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
const payload = {};
|
|
16
|
+
payload.sessionId =
|
|
17
|
+
str(obj.session_id) ??
|
|
18
|
+
str(obj.sessionId) ??
|
|
19
|
+
str(record(obj.session)?.id);
|
|
20
|
+
payload.cwd =
|
|
21
|
+
str(obj.cwd) ??
|
|
22
|
+
str(record(obj.workspace)?.cwd);
|
|
23
|
+
payload.event =
|
|
24
|
+
str(obj.hook_event_name) ??
|
|
25
|
+
str(obj.event);
|
|
26
|
+
payload.harness =
|
|
27
|
+
str(obj.harness) ??
|
|
28
|
+
str(obj.agent) ??
|
|
29
|
+
(obj.hook_event_name !== undefined ? 'claude-code' : undefined);
|
|
30
|
+
const files = collectFiles(obj);
|
|
31
|
+
if (files.length > 0)
|
|
32
|
+
payload.files = files;
|
|
33
|
+
return payload;
|
|
34
|
+
}
|
|
35
|
+
function collectFiles(obj) {
|
|
36
|
+
const out = [];
|
|
37
|
+
const push = (v) => {
|
|
38
|
+
if (typeof v === 'string' && v.length > 0)
|
|
39
|
+
out.push(v);
|
|
40
|
+
};
|
|
41
|
+
const pushAll = (v) => {
|
|
42
|
+
if (Array.isArray(v))
|
|
43
|
+
v.forEach(push);
|
|
44
|
+
};
|
|
45
|
+
const toolInput = record(obj.tool_input) ?? record(obj.toolInput);
|
|
46
|
+
push(toolInput?.file_path);
|
|
47
|
+
push(toolInput?.filePath);
|
|
48
|
+
pushAll(toolInput?.files);
|
|
49
|
+
push(obj.file);
|
|
50
|
+
push(obj.path);
|
|
51
|
+
pushAll(obj.files);
|
|
52
|
+
pushAll(obj.paths);
|
|
53
|
+
return [...new Set(out)];
|
|
54
|
+
}
|
|
55
|
+
function record(v) {
|
|
56
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v)
|
|
57
|
+
? v
|
|
58
|
+
: undefined;
|
|
59
|
+
}
|
|
60
|
+
function str(v) {
|
|
61
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload.js","sourceRoot":"","sources":["../../src/hooks/payload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { HookPayload } from './payload.js';
|
|
2
|
+
export declare function resolveSessionKey(payload: HookPayload, warn?: (msg: string) => void): string;
|
|
3
|
+
export declare function recordEdit(key: string, file: string, cwd: string, env?: NodeJS.ProcessEnv): void;
|
|
4
|
+
export declare function pendingEdits(key: string, env?: NodeJS.ProcessEnv): string[];
|
|
5
|
+
export declare function clearSession(key: string, env?: NodeJS.ProcessEnv): void;
|
|
6
|
+
export declare function stateFilePath(key: string, env?: NodeJS.ProcessEnv, project?: string): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
export function resolveSessionKey(payload, warn = defaultWarn) {
|
|
6
|
+
const harness = sanitize(payload.harness ?? 'unknown');
|
|
7
|
+
if (payload.sessionId) {
|
|
8
|
+
return `${harness}-${sanitize(payload.sessionId)}`;
|
|
9
|
+
}
|
|
10
|
+
const cwd = payload.cwd ?? process.cwd();
|
|
11
|
+
const hash = crypto.createHash('sha1').update(cwd).digest('hex').slice(0, 8);
|
|
12
|
+
warn(`council: session id missing from hook payload, using cwd-based key; ` +
|
|
13
|
+
`edit tracking is shared across sessions in ${cwd}`);
|
|
14
|
+
return `${harness}-cwd-${hash}`;
|
|
15
|
+
}
|
|
16
|
+
export function recordEdit(key, file, cwd, env = process.env) {
|
|
17
|
+
const entry = { file: path.resolve(cwd, file), cwd, ts: Date.now() };
|
|
18
|
+
const target = stateFilePath(key, env);
|
|
19
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
20
|
+
const fd = fs.openSync(target, 'a');
|
|
21
|
+
try {
|
|
22
|
+
fs.writeSync(fd, JSON.stringify(entry) + '\n');
|
|
23
|
+
fs.fsyncSync(fd); // durable before exit: the stop hook may fire immediately after
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
fs.closeSync(fd);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function pendingEdits(key, env = process.env) {
|
|
30
|
+
const targets = [stateFilePath(key, env), stateFilePath(key, env, 'council-axi')];
|
|
31
|
+
const seen = new Set();
|
|
32
|
+
const out = [];
|
|
33
|
+
for (const target of targets) {
|
|
34
|
+
if (!fs.existsSync(target))
|
|
35
|
+
continue;
|
|
36
|
+
for (const line of fs.readFileSync(target, 'utf8').split('\n')) {
|
|
37
|
+
if (line.trim().length === 0)
|
|
38
|
+
continue;
|
|
39
|
+
let entry;
|
|
40
|
+
try {
|
|
41
|
+
const parsed = JSON.parse(line);
|
|
42
|
+
entry = parsed;
|
|
43
|
+
if (typeof entry.file !== 'string')
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
continue; // killed-process partial write
|
|
48
|
+
}
|
|
49
|
+
if (!seen.has(entry.file)) {
|
|
50
|
+
seen.add(entry.file);
|
|
51
|
+
out.push(entry.file);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
export function clearSession(key, env = process.env) {
|
|
58
|
+
fs.rmSync(stateFilePath(key, env), { force: true });
|
|
59
|
+
fs.rmSync(stateFilePath(key, env, 'council-axi'), { force: true });
|
|
60
|
+
}
|
|
61
|
+
export function stateFilePath(key, env = process.env, project = 'council') {
|
|
62
|
+
const base = env.XDG_STATE_HOME && env.XDG_STATE_HOME.length > 0
|
|
63
|
+
? env.XDG_STATE_HOME
|
|
64
|
+
: path.join(os.homedir(), '.local', 'state');
|
|
65
|
+
return path.join(base, project, `${sanitize(key)}.jsonl`);
|
|
66
|
+
}
|
|
67
|
+
function sanitize(text) {
|
|
68
|
+
return text.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
69
|
+
}
|
|
70
|
+
function defaultWarn(msg) {
|
|
71
|
+
console.error(msg);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/hooks/state.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { runReview, type ReviewOptions } from './runners/review.js';
|
|
2
|
+
export { runPlan, type PlanOptions } from './runners/plan.js';
|
|
3
|
+
export { runDebate, activeParticipants, roundOrder, CALLER, type DebateRunOptions, type DebateProgress } from './debate/loop.js';
|
|
4
|
+
export { buildDebateOutput, renderDebateTOON } from './debate/render.js';
|
|
5
|
+
export { parseHardBlocks, aggregateBlocks, newBlockKey, KEY_LABEL, type BlockAggregate } from './blocks.js';
|
|
6
|
+
export { parseVerdict } from './verdict.js';
|
|
7
|
+
export { renderTOON } from './output.js';
|
|
8
|
+
export { exitCodeFor, type ExitPolicyOptions } from './exit.js';
|
|
9
|
+
export { CouncilError } from './errors.js';
|
|
10
|
+
/**
|
|
11
|
+
* UNSTABLE. Configuration is env-var only today and is being reworked as part
|
|
12
|
+
* of the provider config-file work (backlog item 4 / tracker T-015). This
|
|
13
|
+
* export will change shape. Do not depend on it in long-lived code.
|
|
14
|
+
*/
|
|
15
|
+
export { loadConfig, resolveModels, type CouncilConfig } from './config.js';
|
|
16
|
+
export type { AttributedHardBlock, BlockParse, CouncilOutput, CouncilRequest, DebateOutput, DebateRound, DebateSession, DebateTurn, Effort, RunControls, HardBlock, JudgeResult, ProviderConfig, Severity, UnknownJudge, UnparseableReason, Verdict, } from './types.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { runReview } from './runners/review.js';
|
|
2
|
+
export { runPlan } from './runners/plan.js';
|
|
3
|
+
export { runDebate, activeParticipants, roundOrder, CALLER } from './debate/loop.js';
|
|
4
|
+
export { buildDebateOutput, renderDebateTOON } from './debate/render.js';
|
|
5
|
+
export { parseHardBlocks, aggregateBlocks, newBlockKey, KEY_LABEL } from './blocks.js';
|
|
6
|
+
export { parseVerdict } from './verdict.js';
|
|
7
|
+
export { renderTOON } from './output.js';
|
|
8
|
+
export { exitCodeFor } from './exit.js';
|
|
9
|
+
export { CouncilError } from './errors.js';
|
|
10
|
+
/**
|
|
11
|
+
* UNSTABLE. Configuration is env-var only today and is being reworked as part
|
|
12
|
+
* of the provider config-file work (backlog item 4 / tracker T-015). This
|
|
13
|
+
* export will change shape. Do not depend on it in long-lived code.
|
|
14
|
+
*/
|
|
15
|
+
export { loadConfig, resolveModels } from './config.js';
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAsB,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAoB,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,EAA8C,MAAM,kBAAkB,CAAC;AACjI,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEzE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,SAAS,EAAuB,MAAM,aAAa,CAAC;AAC5G,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAA0B,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,aAAa,EAAsB,MAAM,aAAa,CAAC"}
|
package/dist/input.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ArtifactInput {
|
|
2
|
+
files?: string[];
|
|
3
|
+
diff?: {
|
|
4
|
+
range?: string;
|
|
5
|
+
};
|
|
6
|
+
stdinContent?: string;
|
|
7
|
+
capBytes?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function buildPrompt(prompt: string, input: ArtifactInput): {
|
|
10
|
+
text: string;
|
|
11
|
+
warnings: string[];
|
|
12
|
+
};
|
|
13
|
+
export declare function parseModelList(csv: string | undefined): string[] | undefined;
|
|
14
|
+
export declare function parseCapBytesOverride(): number | undefined;
|
package/dist/input.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { assembleArtifacts, formatArtifactPreamble } from './artifacts.js';
|
|
2
|
+
// Synchronous by design: the library accepts already-read content, so nothing
|
|
3
|
+
// here touches process.stdin. The CLI reads stdin and passes stdinContent.
|
|
4
|
+
export function buildPrompt(prompt, input) {
|
|
5
|
+
const bundle = assembleArtifacts({
|
|
6
|
+
files: input.files,
|
|
7
|
+
diff: input.diff,
|
|
8
|
+
stdin: input.stdinContent,
|
|
9
|
+
capBytes: input.capBytes,
|
|
10
|
+
});
|
|
11
|
+
const text = bundle.blocks.length > 0 ? formatArtifactPreamble(bundle) + prompt : prompt;
|
|
12
|
+
return { text, warnings: bundle.warnings };
|
|
13
|
+
}
|
|
14
|
+
// Pure split-and-trim. Deliberately does NOT drop empty entries: resolveModels
|
|
15
|
+
// is the sole owner of the "no list supplied" versus "a list was supplied but
|
|
16
|
+
// named nothing" distinction, and it needs to see the raw entries to tell them
|
|
17
|
+
// apart.
|
|
18
|
+
export function parseModelList(csv) {
|
|
19
|
+
if (csv === undefined)
|
|
20
|
+
return undefined;
|
|
21
|
+
return csv.split(',').map((m) => m.trim());
|
|
22
|
+
}
|
|
23
|
+
export function parseCapBytesOverride() {
|
|
24
|
+
const raw = process.env.COUNCIL_MAX_ARTIFACT_BYTES;
|
|
25
|
+
if (!raw)
|
|
26
|
+
return undefined;
|
|
27
|
+
const n = Number.parseInt(raw, 10);
|
|
28
|
+
return Number.isInteger(n) && n > 0 ? n : undefined;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.js","sourceRoot":"","sources":["../src/input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAS3E,8EAA8E;AAC9E,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CACzB,MAAc,EACd,KAAoB;IAEpB,MAAM,MAAM,GAAG,iBAAiB,CAAC;QAC/B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE,KAAK,CAAC,YAAY;QACzB,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IACzF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC7C,CAAC;AAED,+EAA+E;AAC/E,8EAA8E;AAC9E,+EAA+E;AAC/E,SAAS;AACT,MAAM,UAAU,cAAc,CAAC,GAAuB;IACpD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;IACnD,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACtD,CAAC"}
|
package/dist/output.d.ts
ADDED
package/dist/output.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export function renderTOON(output) {
|
|
2
|
+
const lines = [];
|
|
3
|
+
lines.push(`council[${output.mode}]: "${previewPrompt(output.prompt)}"`);
|
|
4
|
+
lines.push(`judges: ${output.availableCount} of ${output.totalCount} responded`);
|
|
5
|
+
lines.push(`judges[${output.judges.length}]{provider,model,status,verdict}:`);
|
|
6
|
+
for (const judge of output.judges) {
|
|
7
|
+
const verdict = judge.status === 'success'
|
|
8
|
+
? firstLine(judge.response ?? '')
|
|
9
|
+
: judge.error?.message ?? 'unavailable';
|
|
10
|
+
lines.push(` ${judge.provider},${judge.model},${judge.status},${escapeComma(verdict)}`);
|
|
11
|
+
}
|
|
12
|
+
lines.push(`blocked: ${output.blocked ? 'yes' : 'no'}`);
|
|
13
|
+
pushBlockSection(lines, 'blocks', output.hardBlocks);
|
|
14
|
+
pushBlockSection(lines, 'diagnostic', output.diagnosticBlocks);
|
|
15
|
+
if (output.unknownJudges.length > 0) {
|
|
16
|
+
lines.push(`unknown[${output.unknownJudges.length}]:`);
|
|
17
|
+
for (const u of output.unknownJudges) {
|
|
18
|
+
lines.push(` ${u.provider}: ${u.reason}${u.detail ? ` (${u.detail})` : ''}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const skipped = output.judges.filter((j) => j.status !== 'success');
|
|
22
|
+
if (skipped.length > 0) {
|
|
23
|
+
lines.push(`skipped[${skipped.length}]:`);
|
|
24
|
+
for (const judge of skipped) {
|
|
25
|
+
lines.push(` ${judge.provider}: ${judge.error?.message ?? 'unavailable'}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (output.warnings && output.warnings.length > 0) {
|
|
29
|
+
lines.push(`warnings[${output.warnings.length}]:`);
|
|
30
|
+
for (const w of output.warnings) {
|
|
31
|
+
lines.push(` ${w}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
lines.push('synthesis:');
|
|
35
|
+
for (const line of output.synthesis.split('\n')) {
|
|
36
|
+
lines.push(` ${line}`);
|
|
37
|
+
}
|
|
38
|
+
const help = [];
|
|
39
|
+
if (skipped.length > 0) {
|
|
40
|
+
help.push('Run `npx -y @lanjak/council setup` to check provider authentication');
|
|
41
|
+
const responding = output.judges.filter((j) => j.status === 'success').map((j) => j.provider);
|
|
42
|
+
if (responding.length > 0) {
|
|
43
|
+
help.push(`Run \`npx -y @lanjak/council ${output.mode} "<prompt>" --models ${responding.join(',')}\` to use only responding judges`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const providers = output.judges.map((j) => j.provider).join(',');
|
|
48
|
+
help.push(`Run \`npx -y @lanjak/council ${output.mode} "<prompt>" --models ${providers}\``);
|
|
49
|
+
}
|
|
50
|
+
lines.push(`help[${help.length}]:`);
|
|
51
|
+
for (const h of help) {
|
|
52
|
+
lines.push(` ${h}`);
|
|
53
|
+
}
|
|
54
|
+
return lines.join('\n');
|
|
55
|
+
}
|
|
56
|
+
function firstLine(text) {
|
|
57
|
+
return text.split('\n')[0]?.trim() ?? '';
|
|
58
|
+
}
|
|
59
|
+
function escapeComma(text) {
|
|
60
|
+
return text.replace(/,/g, '\\,');
|
|
61
|
+
}
|
|
62
|
+
// Echo only a short preview of the prompt. A review often embeds an entire
|
|
63
|
+
// artifact (a 71KB plan, say), and dumping it back verbatim on this line made
|
|
64
|
+
// the prompt the bulk of the output.
|
|
65
|
+
export function previewPrompt(text) {
|
|
66
|
+
const PREVIEW_LEN = 200;
|
|
67
|
+
const normalized = text.replace(/\s+/g, ' ').replace(/"/g, '\\"').trim();
|
|
68
|
+
if (normalized.length <= PREVIEW_LEN)
|
|
69
|
+
return normalized;
|
|
70
|
+
return `${normalized.slice(0, PREVIEW_LEN)}... (${normalized.length} chars total)`;
|
|
71
|
+
}
|
|
72
|
+
function pushBlockSection(lines, label, blocks) {
|
|
73
|
+
if (blocks.length === 0)
|
|
74
|
+
return;
|
|
75
|
+
lines.push(`${label}[${blocks.length}]{provider,severity,title,location}:`);
|
|
76
|
+
for (const b of blocks) {
|
|
77
|
+
lines.push(` ${b.provider},${b.severity},${escapeComma(b.title)},${escapeComma(b.location ?? '')}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { KEY_LABEL } from './blocks.js';
|
|
2
|
+
// A hard block is defined narrowly on purpose. Everything listed here gates,
|
|
3
|
+
// so a judge that files nits as blockers makes the gate useless.
|
|
4
|
+
export function blockFooterInstruction(key) {
|
|
5
|
+
return [
|
|
6
|
+
'After your prose, end your response with a machine-readable footer.',
|
|
7
|
+
'',
|
|
8
|
+
'A hard block is a defect that must be fixed before this change ships:',
|
|
9
|
+
'incorrect behaviour, a security hole, data loss, or a broken contract.',
|
|
10
|
+
'Style preferences, naming, and suggestions for future work are NOT hard',
|
|
11
|
+
'blocks - leave those in your prose.',
|
|
12
|
+
'',
|
|
13
|
+
'The footer must be the last thing you write, and must start with this',
|
|
14
|
+
'exact key line:',
|
|
15
|
+
'',
|
|
16
|
+
'```',
|
|
17
|
+
`${KEY_LABEL}: ${key}`,
|
|
18
|
+
'BLOCKERS: 1',
|
|
19
|
+
'BLOCKER: critical | the admin route has no auth check | src/api.ts:42',
|
|
20
|
+
'```',
|
|
21
|
+
'',
|
|
22
|
+
'Rules:',
|
|
23
|
+
`- Always emit the "${KEY_LABEL}: ${key}" line, exactly as shown.`,
|
|
24
|
+
'- If you found no hard blocks, write "BLOCKERS: none" and no BLOCKER lines.',
|
|
25
|
+
'- Otherwise write "BLOCKERS: <n>" followed by exactly n BLOCKER lines.',
|
|
26
|
+
'- Each BLOCKER line is: severity | short title | location',
|
|
27
|
+
'- severity is "critical" or "high". location is optional.',
|
|
28
|
+
'- Your prose must not contradict the footer.',
|
|
29
|
+
].join('\n');
|
|
30
|
+
}
|
|
31
|
+
export function reviewSystemPrompt(key) {
|
|
32
|
+
return [
|
|
33
|
+
'You are reviewing an artifact as part of an adversarial review council.',
|
|
34
|
+
'Be adversarial: hunt for defects, question assumptions, and say plainly',
|
|
35
|
+
'what is wrong rather than hedging. Keep your response concise.',
|
|
36
|
+
'',
|
|
37
|
+
blockFooterInstruction(key),
|
|
38
|
+
].join('\n');
|
|
39
|
+
}
|
|
40
|
+
export function planSystemPrompt(key) {
|
|
41
|
+
return [
|
|
42
|
+
'You are reviewing a plan or decision. Be adversarial: question assumptions,',
|
|
43
|
+
'identify risks, and suggest alternatives. Keep your response concise.',
|
|
44
|
+
'',
|
|
45
|
+
blockFooterInstruction(key),
|
|
46
|
+
].join('\n');
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,6EAA6E;AAC7E,iEAAiE;AACjE,MAAM,UAAU,sBAAsB,CAAC,GAAW;IAChD,OAAO;QACL,qEAAqE;QACrE,EAAE;QACF,uEAAuE;QACvE,wEAAwE;QACxE,yEAAyE;QACzE,qCAAqC;QACrC,EAAE;QACF,uEAAuE;QACvE,iBAAiB;QACjB,EAAE;QACF,KAAK;QACL,GAAG,SAAS,KAAK,GAAG,EAAE;QACtB,aAAa;QACb,uEAAuE;QACvE,KAAK;QACL,EAAE;QACF,QAAQ;QACR,sBAAsB,SAAS,KAAK,GAAG,2BAA2B;QAClE,6EAA6E;QAC7E,wEAAwE;QACxE,2DAA2D;QAC3D,2DAA2D;QAC3D,8CAA8C;KAC/C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,OAAO;QACL,yEAAyE;QACzE,yEAAyE;QACzE,gEAAgE;QAChE,EAAE;QACF,sBAAsB,CAAC,GAAG,CAAC;KAC5B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO;QACL,6EAA6E;QAC7E,uEAAuE;QACvE,EAAE;QACF,sBAAsB,CAAC,GAAG,CAAC;KAC5B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import { LLMProvider, ChatOptions, ChatResult, AuthResult, ProviderCapabilities } from './base.js';
|
|
3
|
+
import type { ProviderConfig } from '../types.js';
|
|
4
|
+
export declare class AnthropicProvider extends LLMProvider {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly displayName: string;
|
|
7
|
+
readonly capabilities: ProviderCapabilities;
|
|
8
|
+
protected client: Anthropic;
|
|
9
|
+
constructor(config: ProviderConfig);
|
|
10
|
+
checkAuth(): Promise<AuthResult>;
|
|
11
|
+
chat(options: ChatOptions): Promise<ChatResult>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import { LLMProvider } from './base.js';
|
|
3
|
+
import { DEFAULT_ANTHROPIC_MAX_TOKENS } from './constants.js';
|
|
4
|
+
export class AnthropicProvider extends LLMProvider {
|
|
5
|
+
name;
|
|
6
|
+
displayName;
|
|
7
|
+
capabilities;
|
|
8
|
+
client;
|
|
9
|
+
constructor(config) {
|
|
10
|
+
super(config);
|
|
11
|
+
this.name = config.name ?? 'unknown';
|
|
12
|
+
this.displayName = config.displayName ?? config.name ?? 'Unknown Provider';
|
|
13
|
+
this.capabilities = { supportsReasoning: false, supportsJsonMode: false };
|
|
14
|
+
// config.apiKey is always a string (config.ts defaults it to '' when
|
|
15
|
+
// unset, never undefined), so passing it straight through means the SDK
|
|
16
|
+
// never falls back to an ambient ANTHROPIC_API_KEY from the process
|
|
17
|
+
// environment - the per-judge key stays authoritative.
|
|
18
|
+
this.client = new Anthropic({
|
|
19
|
+
apiKey: config.apiKey,
|
|
20
|
+
...(config.baseURL ? { baseURL: config.baseURL } : {}),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async checkAuth() {
|
|
24
|
+
// Unlike OpenAICompatibleProvider, baseURL is not required here: the
|
|
25
|
+
// Anthropic SDK has a built-in default endpoint (api.anthropic.com), so
|
|
26
|
+
// the simplest valid config omits ${PREFIX}_BASE_URL entirely.
|
|
27
|
+
if (!this.config.apiKey) {
|
|
28
|
+
return {
|
|
29
|
+
available: true,
|
|
30
|
+
authenticated: false,
|
|
31
|
+
detail: `${this.displayName} API key is not set`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return { available: true, authenticated: true, detail: `${this.displayName} API key is set` };
|
|
35
|
+
}
|
|
36
|
+
async chat(options) {
|
|
37
|
+
const temperature = options.temperature ?? this.config.temperature;
|
|
38
|
+
const system = options.system?.trim() ? options.system : undefined;
|
|
39
|
+
// Uses the streaming helper (accumulated into one final message) even
|
|
40
|
+
// though this provider never exposes streaming to its own callers: the
|
|
41
|
+
// SDK's non-streaming create() throws client-side, before any network
|
|
42
|
+
// call, whenever max_tokens is large enough that the request could
|
|
43
|
+
// plausibly run past 10 minutes (see calculateNonstreamingTimeout in
|
|
44
|
+
// @anthropic-ai/sdk's client.js) - a value like 65536, which some
|
|
45
|
+
// OpenAI-compatible reasoning models may use,
|
|
46
|
+
// trips this every time (confirmed in production: every Anthropic-kind
|
|
47
|
+
// provider failed once max_tokens was set that high). stream().
|
|
48
|
+
// finalMessage() never takes the non-streaming path, so it isn't
|
|
49
|
+
// subject to that guard, while still resolving to the one accumulated
|
|
50
|
+
// Message - same shape create() would have returned.
|
|
51
|
+
const response = await this.client.messages
|
|
52
|
+
.stream({
|
|
53
|
+
model: options.model,
|
|
54
|
+
...((options.effort ?? this.config.effort) !== undefined
|
|
55
|
+
? { output_config: { effort: options.effort ?? this.config.effort } }
|
|
56
|
+
: {}),
|
|
57
|
+
max_tokens: options.maxTokens ?? this.config.maxTokens ?? DEFAULT_ANTHROPIC_MAX_TOKENS,
|
|
58
|
+
messages: [{ role: 'user', content: options.prompt }],
|
|
59
|
+
...(system ? { system } : {}),
|
|
60
|
+
...(temperature !== undefined ? { temperature } : {}),
|
|
61
|
+
})
|
|
62
|
+
.finalMessage();
|
|
63
|
+
// Non-text blocks (tool_use, thinking, etc.) are intentionally dropped:
|
|
64
|
+
// no tool use is requested and extended thinking is off by design (see
|
|
65
|
+
// spec Non-goals), so only `text` blocks are ever expected in practice.
|
|
66
|
+
const content = response.content
|
|
67
|
+
.filter((block) => block.type === 'text')
|
|
68
|
+
.map((block) => block.text)
|
|
69
|
+
.join('\n');
|
|
70
|
+
// response.usage is a required field on Anthropic's Message type (never
|
|
71
|
+
// undefined), unlike OpenAI's optional `completion.usage` - no presence
|
|
72
|
+
// check needed here, only per-field ?? 0 fallbacks since the individual
|
|
73
|
+
// numeric fields are nullable.
|
|
74
|
+
//
|
|
75
|
+
// Anthropic's own docs define total input as the SUM of input_tokens,
|
|
76
|
+
// cache_creation_input_tokens, and cache_read_input_tokens (these are
|
|
77
|
+
// NOT overlapping/subset values - input_tokens already excludes cached
|
|
78
|
+
// tokens). totalTokens below reflects that real total. Note this means
|
|
79
|
+
// totalTokens can exceed inputTokens + outputTokens for a cached
|
|
80
|
+
// request, unlike the OpenAI-compatible provider where they're always
|
|
81
|
+
// equal - inputTokens intentionally stays the raw (cache-excluding)
|
|
82
|
+
// input_tokens field, matching Anthropic's own field semantics, rather
|
|
83
|
+
// than folding cache tokens into it to preserve an invariant that
|
|
84
|
+
// nothing downstream actually relies on (checked: no code in this repo
|
|
85
|
+
// computes inputTokens + outputTokens instead of using totalTokens
|
|
86
|
+
// directly).
|
|
87
|
+
const usage = {
|
|
88
|
+
inputTokens: response.usage.input_tokens ?? 0,
|
|
89
|
+
outputTokens: response.usage.output_tokens ?? 0,
|
|
90
|
+
totalTokens: (response.usage.input_tokens ?? 0) +
|
|
91
|
+
(response.usage.output_tokens ?? 0) +
|
|
92
|
+
(response.usage.cache_creation_input_tokens ?? 0) +
|
|
93
|
+
(response.usage.cache_read_input_tokens ?? 0),
|
|
94
|
+
};
|
|
95
|
+
return { content, usage };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=anthropic.js.map
|