@lanjak/council 0.0.0-stage → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +522 -2
  2. package/dist/artifacts.d.ts +24 -0
  3. package/dist/artifacts.js +227 -0
  4. package/dist/artifacts.js.map +1 -0
  5. package/dist/blocks.d.ts +12 -0
  6. package/dist/blocks.js +197 -0
  7. package/dist/blocks.js.map +1 -0
  8. package/dist/chairman.d.ts +7 -0
  9. package/dist/chairman.js +51 -0
  10. package/dist/chairman.js.map +1 -0
  11. package/dist/cli.d.ts +2 -0
  12. package/dist/cli.js +110 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/commands/debate.d.ts +15 -0
  15. package/dist/commands/debate.js +172 -0
  16. package/dist/commands/debate.js.map +1 -0
  17. package/dist/commands/home.d.ts +1 -0
  18. package/dist/commands/home.js +27 -0
  19. package/dist/commands/home.js.map +1 -0
  20. package/dist/commands/hook.d.ts +3 -0
  21. package/dist/commands/hook.js +138 -0
  22. package/dist/commands/hook.js.map +1 -0
  23. package/dist/commands/plan.d.ts +6 -0
  24. package/dist/commands/plan.js +23 -0
  25. package/dist/commands/plan.js.map +1 -0
  26. package/dist/commands/review.d.ts +18 -0
  27. package/dist/commands/review.js +37 -0
  28. package/dist/commands/review.js.map +1 -0
  29. package/dist/commands/setup.d.ts +1 -0
  30. package/dist/commands/setup.js +23 -0
  31. package/dist/commands/setup.js.map +1 -0
  32. package/dist/config.d.ts +17 -0
  33. package/dist/config.js +101 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/council.d.ts +3 -0
  36. package/dist/council.js +62 -0
  37. package/dist/council.js.map +1 -0
  38. package/dist/debate/loop.d.ts +28 -0
  39. package/dist/debate/loop.js +190 -0
  40. package/dist/debate/loop.js.map +1 -0
  41. package/dist/debate/prompts.d.ts +10 -0
  42. package/dist/debate/prompts.js +47 -0
  43. package/dist/debate/prompts.js.map +1 -0
  44. package/dist/debate/render.d.ts +22 -0
  45. package/dist/debate/render.js +112 -0
  46. package/dist/debate/render.js.map +1 -0
  47. package/dist/debate/session.d.ts +7 -0
  48. package/dist/debate/session.js +85 -0
  49. package/dist/debate/session.js.map +1 -0
  50. package/dist/errors.d.ts +5 -0
  51. package/dist/errors.js +18 -0
  52. package/dist/errors.js.map +1 -0
  53. package/dist/exit.d.ts +7 -0
  54. package/dist/exit.js +13 -0
  55. package/dist/exit.js.map +1 -0
  56. package/dist/hooks/gate.d.ts +12 -0
  57. package/dist/hooks/gate.js +41 -0
  58. package/dist/hooks/gate.js.map +1 -0
  59. package/dist/hooks/payload.d.ts +8 -0
  60. package/dist/hooks/payload.js +63 -0
  61. package/dist/hooks/payload.js.map +1 -0
  62. package/dist/hooks/state.d.ts +6 -0
  63. package/dist/hooks/state.js +73 -0
  64. package/dist/hooks/state.js.map +1 -0
  65. package/dist/index.d.ts +16 -0
  66. package/dist/index.js +16 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/input.d.ts +14 -0
  69. package/dist/input.js +30 -0
  70. package/dist/input.js.map +1 -0
  71. package/dist/output.d.ts +3 -0
  72. package/dist/output.js +80 -0
  73. package/dist/output.js.map +1 -0
  74. package/dist/prompts.d.ts +3 -0
  75. package/dist/prompts.js +48 -0
  76. package/dist/prompts.js.map +1 -0
  77. package/dist/providers/anthropic.d.ts +12 -0
  78. package/dist/providers/anthropic.js +98 -0
  79. package/dist/providers/anthropic.js.map +1 -0
  80. package/dist/providers/base.d.ts +37 -0
  81. package/dist/providers/base.js +7 -0
  82. package/dist/providers/base.js.map +1 -0
  83. package/dist/providers/constants.d.ts +2 -0
  84. package/dist/providers/constants.js +11 -0
  85. package/dist/providers/constants.js.map +1 -0
  86. package/dist/providers/index.d.ts +6 -0
  87. package/dist/providers/index.js +20 -0
  88. package/dist/providers/index.js.map +1 -0
  89. package/dist/providers/openai-compatible.d.ts +12 -0
  90. package/dist/providers/openai-compatible.js +68 -0
  91. package/dist/providers/openai-compatible.js.map +1 -0
  92. package/dist/runners/plan.d.ts +14 -0
  93. package/dist/runners/plan.js +45 -0
  94. package/dist/runners/plan.js.map +1 -0
  95. package/dist/runners/review.d.ts +14 -0
  96. package/dist/runners/review.js +47 -0
  97. package/dist/runners/review.js.map +1 -0
  98. package/dist/stdin.d.ts +9 -0
  99. package/dist/stdin.js +51 -0
  100. package/dist/stdin.js.map +1 -0
  101. package/dist/telemetry.d.ts +5 -0
  102. package/dist/telemetry.js +135 -0
  103. package/dist/telemetry.js.map +1 -0
  104. package/dist/types.d.ts +114 -0
  105. package/dist/types.js +2 -0
  106. package/dist/types.js.map +1 -0
  107. package/dist/verdict.d.ts +2 -0
  108. package/dist/verdict.js +12 -0
  109. package/dist/verdict.js.map +1 -0
  110. package/package.json +54 -4
  111. package/skills/council/SKILL.md +82 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { ProviderConfig, RunControls } from '../types.js';
2
+ export interface ChatOptions extends RunControls {
3
+ prompt: string;
4
+ system?: string;
5
+ model: string;
6
+ temperature?: number;
7
+ jsonMode?: boolean;
8
+ /** W3C traceparent sent to the gateway so its span joins the caller trace. */
9
+ traceparent?: string;
10
+ }
11
+ export interface ChatResult {
12
+ content: string;
13
+ reasoning?: string;
14
+ usage?: {
15
+ inputTokens: number;
16
+ outputTokens: number;
17
+ totalTokens: number;
18
+ };
19
+ }
20
+ export interface AuthResult {
21
+ available: boolean;
22
+ authenticated: boolean;
23
+ detail: string;
24
+ }
25
+ export interface ProviderCapabilities {
26
+ supportsReasoning: boolean;
27
+ supportsJsonMode: boolean;
28
+ }
29
+ export declare abstract class LLMProvider {
30
+ protected config: ProviderConfig;
31
+ abstract readonly name: string;
32
+ abstract readonly displayName: string;
33
+ abstract readonly capabilities: ProviderCapabilities;
34
+ constructor(config: ProviderConfig);
35
+ abstract checkAuth(): Promise<AuthResult>;
36
+ abstract chat(options: ChatOptions): Promise<ChatResult>;
37
+ }
@@ -0,0 +1,7 @@
1
+ export class LLMProvider {
2
+ config;
3
+ constructor(config) {
4
+ this.config = config;
5
+ }
6
+ }
7
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/providers/base.ts"],"names":[],"mappings":"AA6BA,MAAM,OAAgB,WAAW;IAKT;IAAtB,YAAsB,MAAsB;QAAtB,WAAM,GAAN,MAAM,CAAgB;IAAG,CAAC;CAIjD"}
@@ -0,0 +1,2 @@
1
+ export declare const DEFAULT_OPENAI_MAX_TOKENS = 32768;
2
+ export declare const DEFAULT_ANTHROPIC_MAX_TOKENS = 8192;
@@ -0,0 +1,11 @@
1
+ // Reasoning models spend a large share of their completion budget on hidden
2
+ // thinking tokens, so a low cap truncates the visible answer mid-sentence.
3
+ // mimo-v2.5-pro and deepseek-v4-pro both support 65536; default generously and
4
+ // let per-call maxTokens or ${PREFIX}_MAX_TOKENS override the default.
5
+ export const DEFAULT_OPENAI_MAX_TOKENS = 32768;
6
+ // Anthropic's max output tokens is model-specific and historically lower
7
+ // than the OpenAI-compatible default for some models; a shared default
8
+ // risks every request failing until a user overrides ${PREFIX}_MAX_TOKENS.
9
+ // 8192 is safe across current Claude models while still generous.
10
+ export const DEFAULT_ANTHROPIC_MAX_TOKENS = 8192;
11
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/providers/constants.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,2EAA2E;AAC3E,+EAA+E;AAC/E,uEAAuE;AACvE,MAAM,CAAC,MAAM,yBAAyB,GAAG,KAAK,CAAC;AAE/C,yEAAyE;AACzE,uEAAuE;AACvE,2EAA2E;AAC3E,kEAAkE;AAClE,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { LLMProvider } from './base.js';
2
+ import { OpenAICompatibleProvider } from './openai-compatible.js';
3
+ import { AnthropicProvider } from './anthropic.js';
4
+ import type { ProviderConfig } from '../types.js';
5
+ export declare function loadProvider(name: string, config: ProviderConfig): LLMProvider;
6
+ export { LLMProvider, OpenAICompatibleProvider, AnthropicProvider };
@@ -0,0 +1,20 @@
1
+ import { LLMProvider } from './base.js';
2
+ import { OpenAICompatibleProvider } from './openai-compatible.js';
3
+ import { AnthropicProvider } from './anthropic.js';
4
+ import { CouncilError } from '../errors.js';
5
+ export function loadProvider(name, config) {
6
+ // Normalize here too, not just in config.ts's env parsing: ProviderConfig
7
+ // can be constructed directly by library consumers who bypass loadConfig
8
+ // (and its literal-union type is only a compile-time guarantee, not a
9
+ // runtime one for callers using `any` or plain JS).
10
+ const kind = (config.kind ?? 'openai').toString().trim().toLowerCase();
11
+ if (kind === 'anthropic') {
12
+ return new AnthropicProvider({ ...config, name });
13
+ }
14
+ if (kind === 'openai') {
15
+ return new OpenAICompatibleProvider({ ...config, name });
16
+ }
17
+ throw new CouncilError(`Unrecognized provider kind "${kind}" for "${name}"`, 'BAD_KIND');
18
+ }
19
+ export { LLMProvider, OpenAICompatibleProvider, AnthropicProvider };
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,MAAsB;IAC/D,0EAA0E;IAC1E,yEAAyE;IACzE,sEAAsE;IACtE,oDAAoD;IACpD,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACvE,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACzB,OAAO,IAAI,iBAAiB,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO,IAAI,wBAAwB,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,IAAI,YAAY,CAAC,+BAA+B,IAAI,UAAU,IAAI,GAAG,EAAE,UAAU,CAAC,CAAC;AAC3F,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1,12 @@
1
+ import OpenAI from 'openai';
2
+ import { LLMProvider, ChatOptions, ChatResult, AuthResult, ProviderCapabilities } from './base.js';
3
+ import type { ProviderConfig } from '../types.js';
4
+ export declare class OpenAICompatibleProvider extends LLMProvider {
5
+ readonly name: string;
6
+ readonly displayName: string;
7
+ readonly capabilities: ProviderCapabilities;
8
+ protected client: OpenAI;
9
+ constructor(config: ProviderConfig);
10
+ checkAuth(): Promise<AuthResult>;
11
+ chat(options: ChatOptions): Promise<ChatResult>;
12
+ }
@@ -0,0 +1,68 @@
1
+ import OpenAI from 'openai';
2
+ import { LLMProvider } from './base.js';
3
+ import { DEFAULT_OPENAI_MAX_TOKENS } from './constants.js';
4
+ export class OpenAICompatibleProvider 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: true };
14
+ this.client = new OpenAI({ apiKey: config.apiKey, baseURL: config.baseURL });
15
+ }
16
+ async checkAuth() {
17
+ if (!this.config.baseURL) {
18
+ return {
19
+ available: true,
20
+ authenticated: false,
21
+ detail: `${this.displayName} base URL is not set`,
22
+ };
23
+ }
24
+ if (!this.config.apiKey) {
25
+ return {
26
+ available: true,
27
+ authenticated: false,
28
+ detail: `${this.displayName} API key is not set`,
29
+ };
30
+ }
31
+ return { available: true, authenticated: true, detail: `${this.displayName} API key is set` };
32
+ }
33
+ async chat(options) {
34
+ const messages = [];
35
+ if (options.system) {
36
+ messages.push({ role: 'system', content: options.system });
37
+ }
38
+ messages.push({ role: 'user', content: options.prompt });
39
+ // Temperature is opt-in (call option or <PREFIX>_TEMPERATURE env): some
40
+ // endpoints reject any value but their own default (kimi k3 only accepts
41
+ // 1), so when nothing is configured the field is omitted entirely.
42
+ const temperature = options.temperature ?? this.config.temperature;
43
+ const effort = options.effort ?? this.config.effort;
44
+ const completion = await this.client.chat.completions.create({
45
+ model: options.model,
46
+ // This SDK predates xhigh; compatible endpoints accept it on the wire.
47
+ ...(effort !== undefined ? { reasoning_effort: effort } : {}),
48
+ messages,
49
+ ...(temperature !== undefined ? { temperature } : {}),
50
+ max_tokens: options.maxTokens ?? this.config.maxTokens ?? DEFAULT_OPENAI_MAX_TOKENS,
51
+ response_format: options.jsonMode ? { type: 'json_object' } : undefined,
52
+ }, options.traceparent ? { headers: { traceparent: options.traceparent } } : undefined);
53
+ const choice = completion.choices[0];
54
+ const content = choice?.message?.content ?? '';
55
+ // Reasoning models (mimo-v2.5-pro, deepseek-v4-pro) return their chain of
56
+ // thought in a non-standard `reasoning_content` field the SDK doesn't type.
57
+ const reasoning = choice?.message?.reasoning_content ?? undefined;
58
+ const usage = completion.usage
59
+ ? {
60
+ inputTokens: completion.usage.prompt_tokens,
61
+ outputTokens: completion.usage.completion_tokens,
62
+ totalTokens: completion.usage.total_tokens,
63
+ }
64
+ : undefined;
65
+ return { content, reasoning, usage };
66
+ }
67
+ }
68
+ //# sourceMappingURL=openai-compatible.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-compatible.js","sourceRoot":"","sources":["../../src/providers/openai-compatible.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type CouncilConfig } from '../config.js';
2
+ import type { CouncilOutput, RunControls } from '../types.js';
3
+ export interface PlanOptions extends RunControls {
4
+ prompt: string;
5
+ models?: string[];
6
+ config?: CouncilConfig;
7
+ files?: string[];
8
+ diff?: {
9
+ range?: string;
10
+ };
11
+ stdinContent?: string;
12
+ capBytes?: number;
13
+ }
14
+ export declare function runPlan(opts: PlanOptions): Promise<CouncilOutput>;
@@ -0,0 +1,45 @@
1
+ import { loadConfig, resolveModels } from '../config.js';
2
+ import { runCouncil } from '../council.js';
3
+ import { createTraceId } from '../telemetry.js';
4
+ import { synthesize } from '../chairman.js';
5
+ import { buildPrompt } from '../input.js';
6
+ import { CouncilError } from '../errors.js';
7
+ import { newBlockKey, aggregateBlocks, stripBlockFooter } from '../blocks.js';
8
+ import { planSystemPrompt } from '../prompts.js';
9
+ export async function runPlan(opts) {
10
+ const traceId = createTraceId();
11
+ const config = opts.config ?? loadConfig();
12
+ const models = resolveModels(opts.models, config.providers);
13
+ const prompt = buildPrompt(opts.prompt, opts);
14
+ const blockKey = newBlockKey();
15
+ const systemPrompt = planSystemPrompt(blockKey);
16
+ const judges = await runCouncil(config, {
17
+ prompt: prompt.text,
18
+ mode: 'plan',
19
+ models,
20
+ systemPrompt,
21
+ blockKey,
22
+ maxTokens: opts.maxTokens,
23
+ effort: opts.effort,
24
+ }, traceId);
25
+ const availableCount = judges.filter((j) => j.status === 'success').length;
26
+ if (availableCount === 0) {
27
+ throw new CouncilError('All providers unavailable', 'NO_QUORUM');
28
+ }
29
+ // The judges array keeps the raw response; only the prose synthesis drops the
30
+ // footer, and only for judges whose footer parsed cleanly.
31
+ const forSynthesis = judges.map((j) => j.blockParse?.status === 'parsed' && j.response !== undefined
32
+ ? { ...j, response: stripBlockFooter(j.response, blockKey) }
33
+ : j);
34
+ return {
35
+ prompt: prompt.text,
36
+ mode: 'plan',
37
+ judges,
38
+ synthesis: synthesize({ prompt: prompt.text, mode: 'plan', models, systemPrompt }, forSynthesis),
39
+ availableCount,
40
+ totalCount: models.length,
41
+ warnings: prompt.warnings,
42
+ ...aggregateBlocks(judges),
43
+ };
44
+ }
45
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/runners/plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAsB,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAajD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAiB;IAC7C,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAEhD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE;QACtC,MAAM,EAAE,MAAM,CAAC,IAAI;QACnB,IAAI,EAAE,MAAM;QACZ,MAAM;QACN,YAAY;QACZ,QAAQ;QACR,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,EAAE,OAAO,CAAC,CAAC;IACZ,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IAC3E,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,YAAY,CAAC,2BAA2B,EAAE,WAAW,CAAC,CAAC;IACnE,CAAC;IAED,8EAA8E;IAC9E,2DAA2D;IAC3D,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACpC,CAAC,CAAC,UAAU,EAAE,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS;QAC3D,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE;QAC5D,CAAC,CAAC,CAAC,CACN,CAAC;IAEF,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,IAAI;QACnB,IAAI,EAAE,MAAM;QACZ,MAAM;QACN,SAAS,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,YAAY,CAAC;QAChG,cAAc;QACd,UAAU,EAAE,MAAM,CAAC,MAAM;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,eAAe,CAAC,MAAM,CAAC;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { type CouncilConfig } from '../config.js';
2
+ import type { CouncilOutput, RunControls } from '../types.js';
3
+ export interface ReviewOptions extends RunControls {
4
+ prompt: string;
5
+ models?: string[];
6
+ config?: CouncilConfig;
7
+ files?: string[];
8
+ diff?: {
9
+ range?: string;
10
+ };
11
+ stdinContent?: string;
12
+ capBytes?: number;
13
+ }
14
+ export declare function runReview(opts: ReviewOptions): Promise<CouncilOutput>;
@@ -0,0 +1,47 @@
1
+ import { loadConfig, resolveModels } from '../config.js';
2
+ import { runCouncil } from '../council.js';
3
+ import { createTraceId } from '../telemetry.js';
4
+ import { synthesize } from '../chairman.js';
5
+ import { buildPrompt } from '../input.js';
6
+ import { CouncilError } from '../errors.js';
7
+ import { newBlockKey, aggregateBlocks, stripBlockFooter } from '../blocks.js';
8
+ import { reviewSystemPrompt } from '../prompts.js';
9
+ // Pure: no process.argv, no process.exit, no stdout. Warnings ride on the
10
+ // result rather than being printed.
11
+ export async function runReview(opts) {
12
+ const traceId = createTraceId();
13
+ const config = opts.config ?? loadConfig();
14
+ const models = resolveModels(opts.models, config.providers);
15
+ const prompt = buildPrompt(opts.prompt, opts);
16
+ const blockKey = newBlockKey();
17
+ const systemPrompt = reviewSystemPrompt(blockKey);
18
+ const judges = await runCouncil(config, {
19
+ prompt: prompt.text,
20
+ mode: 'review',
21
+ models,
22
+ systemPrompt,
23
+ blockKey,
24
+ maxTokens: opts.maxTokens,
25
+ effort: opts.effort,
26
+ }, traceId);
27
+ const availableCount = judges.filter((j) => j.status === 'success').length;
28
+ if (availableCount === 0) {
29
+ throw new CouncilError('All providers unavailable', 'NO_QUORUM');
30
+ }
31
+ // The judges array keeps the raw response; only the prose synthesis drops the
32
+ // footer, and only for judges whose footer parsed cleanly.
33
+ const forSynthesis = judges.map((j) => j.blockParse?.status === 'parsed' && j.response !== undefined
34
+ ? { ...j, response: stripBlockFooter(j.response, blockKey) }
35
+ : j);
36
+ return {
37
+ prompt: prompt.text,
38
+ mode: 'review',
39
+ judges,
40
+ synthesis: synthesize({ prompt: prompt.text, mode: 'review', models, systemPrompt }, forSynthesis),
41
+ availableCount,
42
+ totalCount: models.length,
43
+ warnings: prompt.warnings,
44
+ ...aggregateBlocks(judges),
45
+ };
46
+ }
47
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/runners/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAsB,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAanD,0EAA0E;AAC1E,oCAAoC;AACpC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAmB;IACjD,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAE9C,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,YAAY,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAElD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE;QACtC,MAAM,EAAE,MAAM,CAAC,IAAI;QACnB,IAAI,EAAE,QAAQ;QACd,MAAM;QACN,YAAY;QACZ,QAAQ;QACR,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,EAAE,OAAO,CAAC,CAAC;IACZ,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IAC3E,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,YAAY,CAAC,2BAA2B,EAAE,WAAW,CAAC,CAAC;IACnE,CAAC;IAED,8EAA8E;IAC9E,2DAA2D;IAC3D,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACpC,CAAC,CAAC,UAAU,EAAE,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS;QAC3D,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE;QAC5D,CAAC,CAAC,CAAC,CACN,CAAC;IAEF,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,IAAI;QACnB,IAAI,EAAE,QAAQ;QACd,MAAM;QACN,SAAS,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,YAAY,CAAC;QAClG,cAAc;QACd,UAAU,EAAE,MAAM,CAAC,MAAM;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,eAAe,CAAC,MAAM,CAAC;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Readable } from 'node:stream';
2
+ export interface ReadStdinOptions {
3
+ firstByteTimeoutMs?: number;
4
+ }
5
+ export declare const DEFAULT_STDIN_TIMEOUT_MS = 2000;
6
+ export declare function readStdinPayloadFrom(stream: Readable & {
7
+ isTTY?: boolean;
8
+ }, firstByteTimeoutMs?: number): Promise<string>;
9
+ export declare function readStdinPayload(options?: ReadStdinOptions): Promise<string>;
package/dist/stdin.js ADDED
@@ -0,0 +1,51 @@
1
+ export const DEFAULT_STDIN_TIMEOUT_MS = 2000;
2
+ // Deadline applies to the FIRST byte only. After data starts arriving we read
3
+ // to EOF with no cutoff, so a slow harness mid-payload is never truncated.
4
+ export function readStdinPayloadFrom(stream, firstByteTimeoutMs = DEFAULT_STDIN_TIMEOUT_MS) {
5
+ if (stream.isTTY)
6
+ return Promise.resolve('');
7
+ return new Promise((resolve) => {
8
+ let chunks = [];
9
+ let gotFirstByte = false;
10
+ const timer = setTimeout(() => {
11
+ if (!gotFirstByte) {
12
+ cleanup();
13
+ resolve('');
14
+ }
15
+ }, firstByteTimeoutMs);
16
+ function onData(chunk) {
17
+ gotFirstByte = true;
18
+ chunks.push(chunk);
19
+ }
20
+ function onEnd() {
21
+ cleanup();
22
+ resolve(Buffer.concat(chunks).toString('utf8'));
23
+ }
24
+ function onError() {
25
+ cleanup();
26
+ resolve(chunks.length > 0 ? Buffer.concat(chunks).toString('utf8') : '');
27
+ }
28
+ function cleanup() {
29
+ clearTimeout(timer);
30
+ stream.off('data', onData);
31
+ stream.off('end', onEnd);
32
+ stream.off('error', onError);
33
+ }
34
+ stream.on('data', onData);
35
+ stream.on('end', onEnd);
36
+ stream.on('error', onError);
37
+ });
38
+ }
39
+ export function readStdinPayload(options = {}) {
40
+ const timeout = options.firstByteTimeoutMs ??
41
+ parsePositiveInt(process.env.COUNCIL_HOOK_STDIN_TIMEOUT_MS) ??
42
+ DEFAULT_STDIN_TIMEOUT_MS;
43
+ return readStdinPayloadFrom(process.stdin, timeout);
44
+ }
45
+ function parsePositiveInt(value) {
46
+ if (!value)
47
+ return undefined;
48
+ const n = Number.parseInt(value, 10);
49
+ return Number.isInteger(n) && n > 0 ? n : undefined;
50
+ }
51
+ //# sourceMappingURL=stdin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdin.js","sourceRoot":"","sources":["../src/stdin.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAE7C,8EAA8E;AAC9E,2EAA2E;AAC3E,MAAM,UAAU,oBAAoB,CAClC,MAAsC,EACtC,qBAA6B,wBAAwB;IAErD,IAAI,MAAM,CAAC,KAAK;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAE7C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,MAAM,GAAa,EAAE,CAAC;QAC1B,IAAI,YAAY,GAAG,KAAK,CAAC;QAEzB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,OAAO,EAAE,CAAC;gBACV,OAAO,CAAC,EAAE,CAAC,CAAC;YACd,CAAC;QACH,CAAC,EAAE,kBAAkB,CAAC,CAAC;QAEvB,SAAS,MAAM,CAAC,KAAa;YAC3B,YAAY,GAAG,IAAI,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QACD,SAAS,KAAK;YACZ,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAClD,CAAC;QACD,SAAS,OAAO;YACd,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3E,CAAC;QACD,SAAS,OAAO;YACd,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACzB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC/B,CAAC;QAED,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACxB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAA4B,EAAE;IAC7D,MAAM,OAAO,GACX,OAAO,CAAC,kBAAkB;QAC1B,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC;QAC3D,wBAAwB,CAAC;IAC3B,OAAO,oBAAoB,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAyB;IACjD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACtD,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ChatOptions, ChatResult, LLMProvider } from './providers/base.js';
2
+ export declare function createTraceId(): string | undefined;
3
+ export declare function flushTelemetry(): Promise<void>;
4
+ /** provider.chat with a traceparent header and one context event afterwards. */
5
+ export declare function tracedChat(provider: LLMProvider, role: string, options: ChatOptions, traceId: string | undefined): Promise<ChatResult>;
@@ -0,0 +1,135 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { hostname } from 'node:os';
3
+ // Context-only telemetry. One OTLP log record per model call: ids, role, model,
4
+ // device, outcome, duration. Never prompt or response text, tokens or cost
5
+ // (the gateway is authoritative for those).
6
+ export function createTraceId() {
7
+ return otlpConfig() ? randomBytes(16).toString('hex') : undefined;
8
+ }
9
+ const pending = new Set();
10
+ export async function flushTelemetry() {
11
+ if (!pending.size)
12
+ return;
13
+ let timer;
14
+ try {
15
+ await Promise.race([
16
+ Promise.all([...pending]),
17
+ new Promise((resolve) => { timer = setTimeout(resolve, 1500); }),
18
+ ]);
19
+ }
20
+ finally {
21
+ clearTimeout(timer);
22
+ }
23
+ }
24
+ // Parses the OTEL "k=v,k=v" list format used by the headers and resource variables.
25
+ function pairs(raw) {
26
+ const out = {};
27
+ for (const pair of (raw ?? '').split(',')) {
28
+ const i = pair.indexOf('=');
29
+ if (i > 0) {
30
+ const value = pair.slice(i + 1).trim();
31
+ try {
32
+ out[pair.slice(0, i).trim()] = decodeURIComponent(value);
33
+ }
34
+ catch {
35
+ out[pair.slice(0, i).trim()] = value;
36
+ }
37
+ }
38
+ }
39
+ return out;
40
+ }
41
+ // Off unless an OTLP endpoint is set, like the OpenTelemetry SDKs.
42
+ function otlpConfig() {
43
+ if (process.env.OTEL_SDK_DISABLED === 'true')
44
+ return null;
45
+ const base = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
46
+ const url = process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT ?? (base ? `${base.replace(/\/$/, '')}/v1/logs` : undefined);
47
+ if (!url)
48
+ return null;
49
+ return { url, headers: pairs(process.env.OTEL_EXPORTER_OTLP_HEADERS) };
50
+ }
51
+ function outcomeOf(err) {
52
+ const status = err?.status;
53
+ if (status === 429)
54
+ return 'rate_limited';
55
+ const name = err?.name ?? '';
56
+ if (status === 408 || /timeout/i.test(name))
57
+ return 'timeout';
58
+ return 'error';
59
+ }
60
+ // Fire-and-forget: short timeout, swallows every error.
61
+ async function emit(traceId, spanId, role, model, outcome, durationMs) {
62
+ try {
63
+ const cfg = otlpConfig();
64
+ if (!cfg)
65
+ return;
66
+ const str = (key, value) => ({ key, value: { stringValue: value } });
67
+ const host = hostname();
68
+ const body = {
69
+ resourceLogs: [
70
+ {
71
+ resource: {
72
+ attributes: Object.entries({
73
+ 'service.name': 'council',
74
+ 'host.name': host,
75
+ ...pairs(process.env.OTEL_RESOURCE_ATTRIBUTES),
76
+ }).map(([k, v]) => str(k, v)),
77
+ },
78
+ scopeLogs: [
79
+ {
80
+ scope: { name: 'council' },
81
+ logRecords: [
82
+ {
83
+ timeUnixNano: String(BigInt(Date.now()) * 1000000n),
84
+ severityNumber: outcome === 'ok' ? 9 : 17,
85
+ traceId,
86
+ spanId,
87
+ attributes: [
88
+ str('event.name', 'council.call'),
89
+ str('harness', 'council'),
90
+ str('purpose', role),
91
+ str('gen_ai.request.model', model),
92
+ str('device', host),
93
+ str('outcome', outcome),
94
+ { key: 'duration_ms', value: { intValue: String(Math.round(durationMs)) } },
95
+ ],
96
+ },
97
+ ],
98
+ },
99
+ ],
100
+ },
101
+ ],
102
+ };
103
+ const response = await fetch(cfg.url, {
104
+ method: 'POST',
105
+ headers: { 'Content-Type': 'application/json', ...cfg.headers },
106
+ body: JSON.stringify(body),
107
+ signal: AbortSignal.timeout(1500),
108
+ });
109
+ await response.body?.cancel();
110
+ }
111
+ catch {
112
+ /* telemetry must never fail the command */
113
+ }
114
+ }
115
+ /** provider.chat with a traceparent header and one context event afterwards. */
116
+ export async function tracedChat(provider, role, options, traceId) {
117
+ if (!traceId || !otlpConfig())
118
+ return provider.chat(options);
119
+ const spanId = randomBytes(8).toString('hex');
120
+ const start = Date.now();
121
+ let outcome = 'ok';
122
+ try {
123
+ return await provider.chat({ ...options, traceparent: `00-${traceId}-${spanId}-01` });
124
+ }
125
+ catch (err) {
126
+ outcome = outcomeOf(err);
127
+ throw err;
128
+ }
129
+ finally {
130
+ const send = emit(traceId, spanId, role, options.model, outcome, Date.now() - start);
131
+ pending.add(send);
132
+ void send.finally(() => pending.delete(send));
133
+ }
134
+ }
135
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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"}