@animalabs/connectome-host 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.7.2 — 2026-07-27
6
+
7
+ ### Added
8
+
9
+ - **`LLM_CALLS_FULL_PAYLOADS` env flag** — retain the raw request on every
10
+ llm-call log entry, not only on refusal/error. Debugging aid; off by
11
+ default (the logs grow gigabytes fast with it on).
12
+
5
13
  ## 0.7.1 — 2026-07-26
6
14
 
7
15
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@animalabs/connectome-host",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "General-purpose agent TUI host with recipe-based configuration",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -43,6 +43,11 @@ export type ProviderCallObserver = (record: ProviderCallRecord) => void;
43
43
  * system-prompt-append uses). */
44
44
  const OAUTH_SYSTEM_IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";
45
45
 
46
+ /** Truthy env-flag parse: unset/''/'0'/'false' (any case) are off. */
47
+ function envFlag(value: string | undefined): boolean {
48
+ return value !== undefined && value !== '' && value !== '0' && value.toLowerCase() !== 'false';
49
+ }
50
+
46
51
  export class LoggingAnthropicAdapter extends AnthropicAdapter {
47
52
  private readonly logPath: string;
48
53
  private readonly getReasoning?: ReasoningGetter;
@@ -50,6 +55,14 @@ export class LoggingAnthropicAdapter extends AnthropicAdapter {
50
55
  /** True when authenticated with an OAuth/Bearer token instead of an API
51
56
  * key; requests then need the identity block prepended (see above). */
52
57
  private readonly oauthMode: boolean;
58
+ /** LLM_CALLS_FULL_PAYLOADS=1: retain the full raw request on EVERY call,
59
+ * not just refusals/errors. Purpose: pass/refusal contrast corpora — a
60
+ * refusal's payload is only interpretable next to the passing payloads
61
+ * around it (classifier-forensics finding, 2026-07-27: verdicts are a
62
+ * near-deterministic function of the payload, so adjacent pairs isolate
63
+ * the differentiator). Costs disk, not memory (the raw request is already
64
+ * captured per-call for the summary); pair with llm-calls rotation. */
65
+ private readonly fullPayloads: boolean = envFlag(process.env.LLM_CALLS_FULL_PAYLOADS);
53
66
 
54
67
  constructor(
55
68
  config: ConstructorParameters<typeof AnthropicAdapter>[0],
@@ -206,6 +219,7 @@ export class LoggingAnthropicAdapter extends AnthropicAdapter {
206
219
  }
207
220
 
208
221
  private refusalRawRequest(response: ProviderResponse, rawRequest: unknown): unknown {
222
+ if (this.fullPayloads) return rawRequest;
209
223
  const raw = (response as { raw?: { stop_reason?: string } }).raw;
210
224
  return raw?.stop_reason === 'refusal' ? rawRequest : undefined;
211
225
  }
@@ -100,6 +100,39 @@ describe('LoggingAnthropicAdapter request logging', () => {
100
100
  expect(internals.refusalRawRequest(refusal, rawRequest)).toBe(rawRequest);
101
101
  });
102
102
 
103
+ test('LLM_CALLS_FULL_PAYLOADS=1 retains the raw request on every call', () => {
104
+ const prev = process.env.LLM_CALLS_FULL_PAYLOADS;
105
+ process.env.LLM_CALLS_FULL_PAYLOADS = '1';
106
+ try {
107
+ const full = new LoggingAnthropicAdapter({ apiKey: 'test' }, '/dev/null');
108
+ const fi = full as unknown as typeof internals;
109
+ const rawRequest = { messages: ['forensic context'] };
110
+ const success = { raw: { stop_reason: 'end_turn' } } as unknown as ProviderResponse;
111
+ const refusal = { raw: { stop_reason: 'refusal' } } as unknown as ProviderResponse;
112
+ expect(fi.refusalRawRequest(success, rawRequest)).toBe(rawRequest);
113
+ expect(fi.refusalRawRequest(refusal, rawRequest)).toBe(rawRequest);
114
+ } finally {
115
+ if (prev === undefined) delete process.env.LLM_CALLS_FULL_PAYLOADS;
116
+ else process.env.LLM_CALLS_FULL_PAYLOADS = prev;
117
+ }
118
+ });
119
+
120
+ test('LLM_CALLS_FULL_PAYLOADS off-values keep refusal-only behavior', () => {
121
+ const prev = process.env.LLM_CALLS_FULL_PAYLOADS;
122
+ try {
123
+ for (const off of ['', '0', 'false', 'FALSE']) {
124
+ process.env.LLM_CALLS_FULL_PAYLOADS = off;
125
+ const a = new LoggingAnthropicAdapter({ apiKey: 'test' }, '/dev/null');
126
+ const ai = a as unknown as typeof internals;
127
+ const success = { raw: { stop_reason: 'end_turn' } } as unknown as ProviderResponse;
128
+ expect(ai.refusalRawRequest(success, { m: 1 })).toBeUndefined();
129
+ }
130
+ } finally {
131
+ if (prev === undefined) delete process.env.LLM_CALLS_FULL_PAYLOADS;
132
+ else process.env.LLM_CALLS_FULL_PAYLOADS = prev;
133
+ }
134
+ });
135
+
103
136
  test('forwards authoritative billing buckets from the provider response', () => {
104
137
  const calls: ProviderCallRecord[] = [];
105
138
  const observed = new LoggingAnthropicAdapter(