Skip to content

Commit b81128d

Browse files
committed
chore: release 0.24.0
1 parent 086c6c6 commit b81128d

File tree

15 files changed

+195
-49
lines changed

15 files changed

+195
-49
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [0.24.0] - 2025-11-23
4+
### 🛠 Claude Agent SDK & Streaming
5+
- Bumped `@anthropic-ai/claude-agent-sdk` support to the latest `0.1.50` with a safe range `<0.1.99`.
6+
- Default Claude adapter now loads local/project/user `.claude` settings via `settingSources` so SDK runs mirror the CLI (fixes credential/config loading when a workingDirectory is provided).
7+
- Added a live streaming test that validates partial message handling end-to-end against a `.claude` workspace.
8+
39
## [0.23.0] - 2025-11-23
410
### 🧩 Codex SDK Compatibility
511
- Broadened `@openai/codex-sdk` peer/dev range to `>=0.60.0 <0.80.0` in the Codex adapter so consumers can install newer 0.6x releases (e.g., 0.63+).

examples/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@headless-coder-sdk/examples-tests",
33
"private": true,
4-
"version": "0.23.0",
4+
"version": "0.24.0",
55
"type": "module",
66
"main": "src/codex-web-calculator.test.ts",
77
"files": [

package-lock.json

Lines changed: 20 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "headless-coder-sdk",
33
"private": true,
4-
"version": "0.23.0",
4+
"version": "0.24.0",
55
"type": "module",
66
"workspaces": [
77
"packages/*",
@@ -15,7 +15,7 @@
1515
"acp:e2e": "npm run e2e --workspace packages/acp-server"
1616
},
1717
"devDependencies": {
18-
"@anthropic-ai/claude-agent-sdk": "^0.1.46",
18+
"@anthropic-ai/claude-agent-sdk": "^0.1.50",
1919
"@types/node": "^20.12.7",
2020
"tsup": "^8.5.0",
2121
"tsx": "^4.19.1",

packages/acp-server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@headless-coder-sdk/acp-server",
3-
"version": "0.23.0",
3+
"version": "0.24.0",
44
"type": "module",
55
"scripts": {
66
"dev": "next dev -p 8000",

packages/claude-adapter/package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@headless-coder-sdk/claude-adapter",
3-
"version": "0.23.0",
3+
"version": "0.24.0",
44
"type": "module",
55
"main": "./dist/index.cjs",
66
"module": "./dist/index.js",
@@ -19,11 +19,12 @@
1919
"LICENSE"
2020
],
2121
"scripts": {
22-
"build": "tsup --config tsup.config.ts"
22+
"build": "tsup --config tsup.config.ts",
23+
"test": "tsx --test test/*.test.ts"
2324
},
2425
"peerDependencies": {
25-
"@anthropic-ai/claude-agent-sdk": ">=0.1.46",
26-
"@headless-coder-sdk/core": "^0.23.0"
26+
"@anthropic-ai/claude-agent-sdk": ">=0.1.50 <0.1.99",
27+
"@headless-coder-sdk/core": "^0.24.0"
2728
},
2829
"devDependencies": {
2930
"typescript": "^5.4.0",

packages/claude-adapter/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ export class ClaudeAdapter implements HeadlessCoder {
164164
private buildOptions(state: ClaudeThreadState, runOpts?: RunOpts, useNativeStructuredOutput?: boolean): Options {
165165
const startOpts = state.opts ?? {};
166166
const resumeId = state.resume ? state.sessionId : undefined;
167+
const settingSources = startOpts.settingSources ?? ['local', 'project', 'user'];
167168
const permissionMode: PermissionMode | undefined =
168169
(startOpts.permissionMode as PermissionMode | undefined) ?? (startOpts.yolo ? 'bypassPermissions' : undefined);
169170
const outputFormat =
@@ -185,6 +186,7 @@ export class ClaudeAdapter implements HeadlessCoder {
185186
permissionMode,
186187
permissionPromptToolName: startOpts.permissionPromptToolName,
187188
outputFormat,
189+
settingSources,
188190
};
189191
}
190192

@@ -650,3 +652,8 @@ function buildClaudeResultErrorMessage(result: any): string {
650652
'Claude run failed';
651653
return `Claude run failed: ${summary}`;
652654
}
655+
656+
/**
657+
* @internal Exported for testing stream event normalization only.
658+
*/
659+
export const __normalizeClaudeStreamMessage = normalizeClaudeStreamMessage;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import assert from 'node:assert/strict';
2+
import test from 'node:test';
3+
4+
import { CODER_NAME, __normalizeClaudeStreamMessage as normalize } from '../src/index.js';
5+
6+
const wrap = (event: Record<string, unknown>) => ({
7+
type: 'stream_event',
8+
session_id: 'sess-123',
9+
event,
10+
});
11+
12+
test('content_block_delta text_delta emits assistant delta message', () => {
13+
const events = normalize(
14+
wrap({
15+
type: 'content_block_delta',
16+
delta: { type: 'text_delta', text: 'Hello' },
17+
}),
18+
'sess-123',
19+
);
20+
21+
assert.equal(events.length, 1);
22+
const [evt] = events;
23+
assert.equal(evt.type, 'message');
24+
assert.equal(evt.provider, CODER_NAME);
25+
assert.equal(evt.delta, true);
26+
assert.equal(evt.text, 'Hello');
27+
});
28+
29+
test('content_block_start tool_use emits tool_use event', () => {
30+
const events = normalize(
31+
wrap({
32+
type: 'content_block_start',
33+
content_block: { type: 'tool_use', id: 'call-1', name: 'bash', input: { cmd: 'echo hi' } },
34+
}),
35+
'sess-123',
36+
);
37+
38+
assert.equal(events.length, 1);
39+
const [evt] = events;
40+
assert.equal(evt.type, 'tool_use');
41+
assert.equal(evt.provider, CODER_NAME);
42+
assert.equal(evt.callId, 'call-1');
43+
assert.equal(evt.name, 'bash');
44+
assert.deepEqual(evt.args, { cmd: 'echo hi' });
45+
});
46+
47+
test('message_delta usage is surfaced as usage event', () => {
48+
const usage = { input_tokens: 10, output_tokens: 5 };
49+
const events = normalize(
50+
wrap({
51+
type: 'message_delta',
52+
usage,
53+
}),
54+
'sess-123',
55+
);
56+
57+
assert.equal(events.length, 1);
58+
const [evt] = events;
59+
assert.equal(evt.type, 'usage');
60+
assert.equal(evt.provider, CODER_NAME);
61+
assert.deepEqual(evt.stats, usage);
62+
});
63+
64+
test('message_stop emits done', () => {
65+
const events = normalize(
66+
wrap({
67+
type: 'message_stop',
68+
}),
69+
'sess-123',
70+
);
71+
72+
assert.equal(events.length, 1);
73+
const [evt] = events;
74+
assert.equal(evt.type, 'done');
75+
assert.equal(evt.provider, CODER_NAME);
76+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import assert from 'node:assert/strict';
2+
import fs from 'node:fs';
3+
import os from 'node:os';
4+
import path from 'node:path';
5+
import test from 'node:test';
6+
7+
import { createHeadlessClaude } from '../src/index.js';
8+
9+
const CONFIG_SOURCE = process.env.CLAUDE_STREAM_CONFIG_SOURCE;
10+
11+
if (!CONFIG_SOURCE) {
12+
test.skip('claude streaming integration (requires CLAUDE_STREAM_CONFIG_SOURCE)', () => {});
13+
} else {
14+
test(
15+
'claude streams partial messages and completes',
16+
{ timeout: 120_000 },
17+
async () => {
18+
const workspace = '/tmp/headless-coder-sdk/test_claude_stream';
19+
const configDest = path.join(workspace, '.claude');
20+
fs.rmSync(configDest, { recursive: true, force: true });
21+
fs.cpSync(CONFIG_SOURCE, configDest, { recursive: true });
22+
assert(fs.existsSync(configDest), 'expected .claude directory in workspace');
23+
24+
const coder = createHeadlessClaude({ permissionMode: 'bypassPermissions' });
25+
const thread = await coder.startThread({ workingDirectory: workspace });
26+
27+
const events: any[] = [];
28+
for await (const evt of thread.runStreamed('Say hello briefly and mention your model.', { streamPartialMessages: true })) {
29+
events.push(evt);
30+
if (evt.type === 'done') break; // stop after completion
31+
}
32+
33+
const messages = events.filter(e => e.type === 'message');
34+
assert(messages.length > 0, 'expected at least one assistant message');
35+
36+
const combined = messages.map(m => m.text ?? '').join(' ');
37+
assert(
38+
!combined.toLowerCase().includes('credit balance is too low'),
39+
'Claude returned credit error; top up credits to run this test',
40+
);
41+
assert(
42+
/hello/i.test(combined),
43+
`expected assistant text to include "hello", got: "${combined || '[empty]'}"`,
44+
);
45+
46+
const hasDelta = messages.some(m => m.delta === true);
47+
assert(hasDelta, 'expected at least one delta message from streaming');
48+
49+
const usage = events.find(e => e.type === 'usage');
50+
assert(usage, 'expected a usage event');
51+
52+
assert(events.some(e => e.type === 'done'), 'expected a done event');
53+
},
54+
);
55+
}

packages/codex-adapter/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@headless-coder-sdk/codex-adapter",
3-
"version": "0.23.0",
3+
"version": "0.24.0",
44
"type": "module",
55
"main": "./dist/index.cjs",
66
"module": "./dist/index.js",
@@ -22,7 +22,7 @@
2222
"build": "tsup --config tsup.config.ts"
2323
},
2424
"peerDependencies": {
25-
"@headless-coder-sdk/core": "^0.23.0",
25+
"@headless-coder-sdk/core": "^0.24.0",
2626
"@openai/codex-sdk": ">=0.60.0 <0.80.0"
2727
},
2828
"devDependencies": {

0 commit comments

Comments
 (0)