throughline 0.14.1 → 0.14.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
|
@@ -10,6 +10,20 @@ shipped to npm but were not individually tagged on GitHub.
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.14.2] — 2026-10-05
|
|
14
|
+
|
|
15
|
+
### 修正
|
|
16
|
+
|
|
17
|
+
- WindowsのClaude Codeの自動継続で、後継へ記憶が入らない不具合を直す。Claude CodeはWindowsのhookをGit Bashで走らせ、起動したprojectの場所を`C:/Users/…`の形で渡す。0.14.1までは、圧縮を止めるhookがこの形のまま印(baton)を残していた。後継の最初の指示は`C:\Users\…`の形で印を探すので見つからず、記憶を注入しなかった。継続の指示は届くので、後継は記憶の無いまま動いた。配送の結果は`unknown`(`handoff_delivery_timeout`)になった。
|
|
18
|
+
- 印と引き継ぎの記録に残すprojectの場所を、そのOSの書き方にそろえる。macOSとLinuxの動作は変わらない。
|
|
19
|
+
- 「ここまでにしたこと」の道具の対象を作業フォルダからの相対で書く処理も、Windowsで効くようになる。
|
|
20
|
+
|
|
21
|
+
### 確認した範囲
|
|
22
|
+
|
|
23
|
+
- Windows 11(10.0.26200)のClaude Code 2.1.289、Haiku 4.5。対話の会話から始めて、4回連続で引き継いで作業を完了した。読み直しも読み飛ばしも無く、どの会話でも自動圧縮は走っていない。0.14.1では、同じ条件で1回目の後継に記憶が入らなかった。
|
|
24
|
+
- 圧縮を止める・道具を止める・`claude --bg`で後継を立てる・受け口(named pipe)へ継続の指示を送る、の4つは、0.14.1の時点からWindowsで動いていた。
|
|
25
|
+
- Claude Desktopの画面から始めた会話、WindowsでのHaiku以外のモデル、subagentが動いている最中の引き継ぎは確かめていない。
|
|
26
|
+
|
|
13
27
|
## [0.14.1] — 2026-10-05
|
|
14
28
|
|
|
15
29
|
### 修正
|
|
@@ -1696,7 +1710,8 @@ two attempts, instrument first instead of patching again.
|
|
|
1696
1710
|
|
|
1697
1711
|
---
|
|
1698
1712
|
|
|
1699
|
-
[Unreleased]: https://github.com/kitepon/Throughline/compare/v0.14.
|
|
1713
|
+
[Unreleased]: https://github.com/kitepon/Throughline/compare/v0.14.2...HEAD
|
|
1714
|
+
[0.14.2]: https://github.com/kitepon/Throughline/compare/v0.14.1...v0.14.2
|
|
1700
1715
|
[0.14.1]: https://github.com/kitepon/Throughline/compare/v0.14.0...v0.14.1
|
|
1701
1716
|
[0.14.0]: https://github.com/kitepon/Throughline/compare/v0.13.0...v0.14.0
|
|
1702
1717
|
[0.13.0]: https://github.com/kitepon/Throughline/compare/v0.12.9...v0.13.0
|
package/README.ja.md
CHANGED
|
@@ -69,7 +69,7 @@ throughline auto-handoff status --host claude --operation <handoff-id> --json
|
|
|
69
69
|
|
|
70
70
|
後継の立ち上げや配送に失敗した時は、固定の理由を記録して止まります。結果が不明な指示は再送しません。圧縮を止めた後にモデルが道具を呼ばずにターンを終えた時は、後継を立てません。印は残るので、1時間以内に開いた新しい会話が記憶を引き継ぎます。
|
|
71
71
|
|
|
72
|
-
有効にした端末では、Claudeの道具の呼び出しのたびに`PreToolUse`フックが1回走ります。Linux
|
|
72
|
+
有効にした端末では、Claudeの道具の呼び出しのたびに`PreToolUse`フックが1回走ります。Linux・macOS・WindowsのClaude Code 2.1.289(macOSはClaude Desktop同梱の2.1.286も)で、端末から始めた会話を実機確認済みです。Claude Desktopの画面から始めた会話は未検証です。[実測と検証範囲](https://github.com/kitepon/Throughline/blob/main/evidence/2026-10-05-claude-auto-handoff.md)を参照してください。
|
|
73
73
|
|
|
74
74
|
## 所有境界
|
|
75
75
|
|
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ throughline auto-handoff status --host claude --operation <handoff-id> --json
|
|
|
68
68
|
|
|
69
69
|
後継の立ち上げや配送に失敗した時は、固定の理由を記録して止まります。結果が不明な指示は再送しません。圧縮を止めた後にモデルが道具を呼ばずにターンを終えた時は、後継を立てません。印は残るので、1時間以内に開いた新しい会話が記憶を引き継ぎます。
|
|
70
70
|
|
|
71
|
-
有効にした端末では、Claudeの道具の呼び出しのたびに`PreToolUse`フックが1回走ります。Linux
|
|
71
|
+
有効にした端末では、Claudeの道具の呼び出しのたびに`PreToolUse`フックが1回走ります。Linux・macOS・WindowsのClaude Code 2.1.289(macOSはClaude Desktop同梱の2.1.286も)で、端末から始めた会話を実機確認済みです。Claude Desktopの画面から始めた会話は未検証です。[実測と検証範囲](https://github.com/kitepon/Throughline/blob/main/evidence/2026-10-05-claude-auto-handoff.md)を参照してください。
|
|
72
72
|
|
|
73
73
|
## Ownership boundary
|
|
74
74
|
|
|
@@ -30,6 +30,8 @@ Claude Desktop の新しい会話は、画面を開いただけでは process
|
|
|
30
30
|
|
|
31
31
|
1. **圧縮を止める**: `PreCompact` hook(`throughline pre-compact`、matcher 無し)が、`trigger: "auto"` で自動継続が
|
|
32
32
|
有効な project の時だけ、`/tl` と同じ印(baton)と引き継ぎの記録を残し、exit code 2 で圧縮を止める。
|
|
33
|
+
印の project は、会話を起動した場所(`CLAUDE_PROJECT_DIR`)を、その OS の書き方にそろえたもの(0.14.2)。
|
|
34
|
+
hook の cwd は Bash の `cd` に追従するので使わない。後継は起動した場所で立ち、その cwd で印を探す。
|
|
33
35
|
手動の `/compact`、無効な project、subagent の中の圧縮(payload に `agent_id`)、script から起動した会話
|
|
34
36
|
(`CLAUDE_CODE_ENTRYPOINT` が `sdk-` で始まる)では止めない。
|
|
35
37
|
2. **旧い会話を止める**: `PreToolUse` hook(`throughline pre-tool-use`、全ての道具)が、記録のある会話の次の道具を、
|
|
@@ -101,4 +103,7 @@ Claude Desktop の新しい会話は、画面を開いただけでは process
|
|
|
101
103
|
- macOS 27.0。端末の会話(2.1.289)と、Claude Desktop が同梱する本体(2.1.286)を端末から起動した会話から始めた。
|
|
102
104
|
0.14.1 で、Haiku 4.5 は4回、Opus 5.5 は7回、連続で引き継いで完了した。読み直しも読み飛ばしも無い。
|
|
103
105
|
- どの会話にも圧縮の記録は無く、止めた道具は実行されていなかった。
|
|
104
|
-
-
|
|
106
|
+
- Windows 11。端末の会話(2.1.289)から始めた。0.14.2 で、Haiku 4.5 は4回、連続で引き継いで完了した。
|
|
107
|
+
0.14.1 までは、印(baton)に残す project の場所が `C:/…` の形で、後継が `C:\…` の形で探して見つけられなかった。
|
|
108
|
+
印と記録に残す場所は、その OS の書き方にそろえる(0.14.2)。
|
|
109
|
+
- Claude Desktop の画面から始めた会話、Fable、subagent が動いている最中の引き継ぎは確かめていない。
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { closeSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
20
20
|
import { homedir } from 'node:os';
|
|
21
|
-
import { basename, dirname, join, resolve } from 'node:path';
|
|
21
|
+
import { basename, dirname, join, posix, resolve, win32 } from 'node:path';
|
|
22
22
|
import { randomUUID } from 'node:crypto';
|
|
23
23
|
import { fileURLToPath } from 'node:url';
|
|
24
24
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
@@ -159,6 +159,17 @@ function unsupportedEntrypoint(env) {
|
|
|
159
159
|
return typeof env.CLAUDE_CODE_ENTRYPOINT === 'string' && env.CLAUDE_CODE_ENTRYPOINT.startsWith('sdk-');
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
/**
|
|
163
|
+
* 会話を起動した project の場所を、その OS の書き方にそろえる。
|
|
164
|
+
*
|
|
165
|
+
* Windows の Claude Code は hook を Git Bash で走らせ、CLAUDE_PROJECT_DIR を `C:/Users/…` の形で渡す。
|
|
166
|
+
* hook の payload の cwd は `C:\Users\…` で、後継の最初の指示はこちらで印(baton)を探す。
|
|
167
|
+
* 書き方が違うと、後継が印を見つけられず、記憶が入らない。
|
|
168
|
+
*/
|
|
169
|
+
function nativeProjectPath(projectPath, platform) {
|
|
170
|
+
return (platform === 'win32' ? win32 : posix).resolve(projectPath);
|
|
171
|
+
}
|
|
172
|
+
|
|
162
173
|
/**
|
|
163
174
|
* PreCompact hook の本体。自動圧縮で、その project が有効な時だけ、印と記録を残して圧縮を止める。
|
|
164
175
|
*
|
|
@@ -177,6 +188,7 @@ export function requestClaudeAutoHandoff({
|
|
|
177
188
|
config = readClaudeAutoHandoffConfig(),
|
|
178
189
|
now = Date.now(),
|
|
179
190
|
dir = claudeAutoHandoffDir(),
|
|
191
|
+
platform = process.platform,
|
|
180
192
|
}) {
|
|
181
193
|
const sessionId = payload?.session_id;
|
|
182
194
|
if (typeof sessionId !== 'string' || !sessionId) throw new Error('Missing session_id in PreCompact payload');
|
|
@@ -185,7 +197,8 @@ export function requestClaudeAutoHandoff({
|
|
|
185
197
|
const path = recordPath(sessionId, dir);
|
|
186
198
|
const existing = readClaudeAutoHandoff(sessionId, dir);
|
|
187
199
|
// 有効判定は会話を起動した project で行う。hook の cwd は Bash の cd に追従する。
|
|
188
|
-
const projectPath =
|
|
200
|
+
const projectPath = nativeProjectPath(
|
|
201
|
+
claudeHostAdapter.completionProjectPath({ cwd: payload.cwd ?? process.cwd(), env }), platform);
|
|
189
202
|
|
|
190
203
|
if (payload.trigger !== 'auto') {
|
|
191
204
|
// 人が /compact を選んだ。まだ止めていない記録は取り下げる。
|
|
@@ -57,6 +57,8 @@ function request(dir, overrides = {}) {
|
|
|
57
57
|
payload: { session_id: SESSION, trigger: 'auto', cwd: '/work/app', transcript_path: overrides.transcriptPath ?? null,
|
|
58
58
|
...overrides.payload },
|
|
59
59
|
env: overrides.env ?? {}, config: overrides.config ?? ENABLED, openDb: () => db, dir, now: overrides.now ?? 1_000,
|
|
60
|
+
// 試験の project は POSIX の書き方(/work/app)。Windows の CI でも同じ値で比べる
|
|
61
|
+
platform: 'linux',
|
|
60
62
|
});
|
|
61
63
|
return { result, db };
|
|
62
64
|
}
|
|
@@ -84,6 +86,20 @@ test('PreCompact: 自動圧縮で有効な時だけ、印と記録を残して
|
|
|
84
86
|
assert.deepEqual([started.status, started.projectPath], ['requested', '/work/app']);
|
|
85
87
|
}));
|
|
86
88
|
|
|
89
|
+
test('PreCompact: Windows の `C:/…` の形の project は、後継が印を探す時の書き方(`C:\\…`)にそろえて残す', () => withDir(dir => {
|
|
90
|
+
const db = makeBatonDb();
|
|
91
|
+
const result = requestClaudeAutoHandoff({
|
|
92
|
+
// Windows の Git Bash で走る hook には、project の場所が `C:/…` の形で渡る(実機では CLAUDE_PROJECT_DIR)
|
|
93
|
+
payload: { session_id: SESSION, trigger: 'auto', cwd: 'C:/Users/k/proj/' },
|
|
94
|
+
env: {}, platform: 'win32',
|
|
95
|
+
config: { enabled: true, projects: [] }, openDb: () => db, dir, now: 1_000,
|
|
96
|
+
});
|
|
97
|
+
assert.equal(result.status, 'requested');
|
|
98
|
+
assert.equal(result.projectPath, 'C:\\Users\\k\\proj');
|
|
99
|
+
assert.equal(db.prepare('SELECT project_path FROM handoff_batons').get().project_path, 'C:\\Users\\k\\proj');
|
|
100
|
+
assert.equal(readClaudeAutoHandoff(SESSION, dir).project_path, 'C:\\Users\\k\\proj');
|
|
101
|
+
}));
|
|
102
|
+
|
|
87
103
|
test('PreCompact: 手動・無効・対象外のproject・scriptから起動した会話・subagent では圧縮を止めない', () => withDir(dir => {
|
|
88
104
|
for (const [overrides, reason] of [
|
|
89
105
|
[{ payload: { trigger: 'manual' } }, 'manual_compact'],
|
|
@@ -272,7 +288,7 @@ test('worker: 止めた時点の依頼とモデルを transcript から写し、
|
|
|
272
288
|
const run = async (sessionId, transcriptPath, toolUseId) => {
|
|
273
289
|
const db = makeBatonDb();
|
|
274
290
|
requestClaudeAutoHandoff({ payload: { session_id: sessionId, trigger: 'auto', cwd: '/work/app', transcript_path: transcriptPath },
|
|
275
|
-
env: {}, config: ENABLED, openDb: () => db, dir });
|
|
291
|
+
env: {}, config: ENABLED, openDb: () => db, dir, platform: 'linux' });
|
|
276
292
|
await stopClaudeTurnForHandoff({ payload: { session_id: sessionId, cwd: '/work/app', transcript_path: transcriptPath,
|
|
277
293
|
tool_use_id: toolUseId }, dir, launchWorker: async () => {} });
|
|
278
294
|
const calls = [];
|
|
@@ -381,7 +397,7 @@ test('worker: 止めたターンを DB へ取り込み、ここまでにした
|
|
|
381
397
|
]));
|
|
382
398
|
const db = makeMemoryDb();
|
|
383
399
|
requestClaudeAutoHandoff({ payload: { session_id: SESSION, trigger: 'auto', cwd: '/work/app', transcript_path: transcript },
|
|
384
|
-
env: {}, config: ENABLED, openDb: () => db, dir });
|
|
400
|
+
env: {}, config: ENABLED, openDb: () => db, dir, platform: 'linux' });
|
|
385
401
|
// hook は並んで走る。記録に残る止めた道具は、応答の中の2つ目のこともある
|
|
386
402
|
await stopClaudeTurnForHandoff({ payload: { session_id: SESSION, cwd: '/work/app', transcript_path: transcript, tool_use_id: 't5' },
|
|
387
403
|
dir, launchWorker: async () => {} });
|
|
@@ -428,7 +444,7 @@ test('worker: 止めたターンを取り込めなくても後継を立て、取
|
|
|
428
444
|
writeFileSync(transcript, jsonl([user('依頼', '2026-10-04T03:00:00Z'), assistant('途中です。', '2026-10-04T03:00:02Z'), toolUse('toolu_1', 'Read')]));
|
|
429
445
|
const db = makeBatonDb(); // bodies も sessions も無い
|
|
430
446
|
requestClaudeAutoHandoff({ payload: { session_id: SESSION, trigger: 'auto', cwd: '/work/app', transcript_path: transcript },
|
|
431
|
-
env: {}, config: ENABLED, openDb: () => db, dir });
|
|
447
|
+
env: {}, config: ENABLED, openDb: () => db, dir, platform: 'linux' });
|
|
432
448
|
await stopClaudeTurnForHandoff({ payload: { session_id: SESSION, cwd: '/work/app', transcript_path: transcript, tool_use_id: 'toolu_1' },
|
|
433
449
|
dir, launchWorker: async () => {} });
|
|
434
450
|
const record = await runClaudeAutoHandoffWorker(SESSION, { dir, env: {}, pollMs: 10, targetTimeoutMs: 300, transcriptTimeoutMs: 300,
|