throughline 0.9.0 → 0.9.1
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 +23 -0
- package/README.ja.md +13 -0
- package/README.md +23 -9
- package/codex/skills/throughline/SKILL.md +17 -1
- package/docs/00_overview.md +7 -2
- package/docs/16_readonly_handoff_context_plan.md +9 -0
- package/docs/adr/0020-windows-ci-release-latency.md +23 -0
- package/package.json +1 -1
- package/src/hook-entrypoints.test.mjs +67 -2
- package/src/hook-envelope.mjs +12 -0
- package/src/prompt-submit.mjs +2 -0
- package/src/session-start.mjs +2 -0
- package/src/turn-processor.mjs +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,29 @@ shipped to npm but were not individually tagged on GitHub.
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.9.1] — 2026-08-14
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Claude-facing SessionStart, UserPromptSubmit, and Stop entrypoints now ignore
|
|
18
|
+
non-Claude camelCase envelopes immediately after JSON parsing and before any
|
|
19
|
+
database, state, VS Code task, handoff, transcript, or runtime-error side
|
|
20
|
+
effect. The boundary requires non-empty `sessionId` and `hookEventName` and
|
|
21
|
+
the absence of Claude's `session_id`; it does not convert payloads or add a
|
|
22
|
+
Grok transcript reader.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- CI now uses the shared factory workflow for the maintained native and WSL2
|
|
27
|
+
environments.
|
|
28
|
+
|
|
29
|
+
### Documentation
|
|
30
|
+
|
|
31
|
+
- Synchronized the current README, Codex skill, contributor entrypoint, docs
|
|
32
|
+
overview, and implementation plan around the v0.9.0 read-only handoff-context
|
|
33
|
+
contract. Historical ADRs, archived plans, and RAG source records remain
|
|
34
|
+
unchanged as point-in-time evidence.
|
|
35
|
+
|
|
13
36
|
## [0.9.0] — 2026-08-04
|
|
14
37
|
|
|
15
38
|
### Added
|
package/README.ja.md
CHANGED
|
@@ -343,6 +343,19 @@ Throughline state をまだ書いていない現在セッションも表示で
|
|
|
343
343
|
| `throughline status` | DB 統計表示 (sessions / skeletons / bodies / details) |
|
|
344
344
|
| `throughline --version` | インストール済みバージョンを表示 |
|
|
345
345
|
|
|
346
|
+
### ローカルlauncher向けread-only handoff context
|
|
347
|
+
|
|
348
|
+
通常handoffを実行せず、同一端末のlauncherからThroughline記憶だけを使う場合は次を呼ぶ:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
throughline handoff-context --session codex:<thread-id> --json
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
成功時の`throughline.handoff_context.v1`は`schema`、`status`、`sessionId`、`context`だけを返す。
|
|
355
|
+
`context`はSessionStartと同じ予算付き継承文脈で、DB作成・migration・baton消費・session merge・
|
|
356
|
+
latest session推測・`sessions.merged_into`変更・L1/L2/L3 rowの所属変更は行わない。AItermは任意の
|
|
357
|
+
別vendor portable forkでこの境界を使う。Observer feedはcompleted-turn projectionであり代替ではない。
|
|
358
|
+
|
|
346
359
|
スラッシュコマンド (Claude Code 内でユーザーが叩く):
|
|
347
360
|
|
|
348
361
|
| コマンド | 役割 |
|
package/README.md
CHANGED
|
@@ -765,15 +765,12 @@ entry to the `tasks` array yourself:
|
|
|
765
765
|
|
|
766
766
|
## Commands
|
|
767
767
|
|
|
768
|
-
**v0.
|
|
769
|
-
`
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
行いません。`throughline@0.6.3`、tag / GitHub Release、公開 CI run
|
|
775
|
-
`29284655280`(9/9 green)を確認済みです。npm registry artifact の shasum は
|
|
776
|
-
`4f3fcd2598a75f026358dae7f3eb3165242b580b` です。
|
|
768
|
+
**v0.9.0 was published on 2026-08-04.** It adds the versioned, read-only
|
|
769
|
+
`handoff-context` boundary for local launchers. The command opens only an
|
|
770
|
+
existing database and leaves baton state, session ownership, and memory rows
|
|
771
|
+
unchanged. Existing factory diagnostics, Observer, runtime-error, capture, and
|
|
772
|
+
normal handoff behavior remain available under the same explicit-failure and
|
|
773
|
+
local-only contracts.
|
|
777
774
|
|
|
778
775
|
| Command | What it does |
|
|
779
776
|
| ---------------------------------------------- | ------------------------------------------------------------ |
|
|
@@ -822,6 +819,23 @@ aggregate は collection が既定OFFで、canonical dotagents config の
|
|
|
822
819
|
| `throughline status` | Print DB statistics (sessions, skeletons, bodies, details) |
|
|
823
820
|
| `throughline --version` | Print the installed version |
|
|
824
821
|
|
|
822
|
+
### Read-only handoff context for local launchers
|
|
823
|
+
|
|
824
|
+
Use this boundary when a local launcher needs Throughline memory without
|
|
825
|
+
performing a normal handoff:
|
|
826
|
+
|
|
827
|
+
```bash
|
|
828
|
+
throughline handoff-context --session codex:<thread-id> --json
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
The successful `throughline.handoff_context.v1` object contains only `schema`,
|
|
832
|
+
`status`, `sessionId`, and `context`. The context is the same budgeted
|
|
833
|
+
inheritance text used by SessionStart. The command does not create or migrate a
|
|
834
|
+
database, consume a baton, merge sessions, infer a latest session, change
|
|
835
|
+
`sessions.merged_into`, or reassign L1/L2/L3 rows. AIterm uses this boundary for
|
|
836
|
+
its optional cross-vendor portable fork; the Observer feed is a separate
|
|
837
|
+
completed-turn projection and is not a substitute.
|
|
838
|
+
|
|
825
839
|
Slash commands (invoked by the user in Claude Code):
|
|
826
840
|
|
|
827
841
|
| Command | What it does |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: throughline
|
|
3
|
-
description: Use when the user asks to use Throughline from Codex, continue or restore Throughline memory, prepare a new Codex thread handoff, summarize a captured Codex session, or check whether the Throughline Codex Stop hook captured the current session. Hide long Throughline command details behind this workflow.
|
|
3
|
+
description: Use when the user asks to use Throughline from Codex, continue or restore Throughline memory, export read-only handoff context for a local launcher, prepare a new Codex thread handoff, summarize a captured Codex session, or check whether the Throughline Codex Stop hook captured the current session. Hide long Throughline command details behind this workflow.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Throughline
|
|
@@ -112,6 +112,22 @@ throughline codex-summarize --session codex:<current-thread-id> --json
|
|
|
112
112
|
Codex-primary summarization uses the Codex CLI backend. Do not claim it fell
|
|
113
113
|
back to Claude Haiku.
|
|
114
114
|
|
|
115
|
+
### "export memory" / "portable fork context"
|
|
116
|
+
|
|
117
|
+
Run:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
throughline handoff-context --session <exact-session-id> --json
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
This is a local read-only export for another launcher. It requires an exact
|
|
124
|
+
session id and returns `schema`, `status`, `sessionId`, and `context`. It does
|
|
125
|
+
not create or migrate the database, consume a baton, merge sessions, move
|
|
126
|
+
memory rows, or change `sessions.merged_into`. Do not substitute the Observer
|
|
127
|
+
completed-turn feed, infer the latest session, or read the SQLite database
|
|
128
|
+
directly. If the command fails or returns no context, report that failure
|
|
129
|
+
instead of falling back to a different memory source.
|
|
130
|
+
|
|
115
131
|
### "trim" / "rewind" / "rollback" / "context cleanup"
|
|
116
132
|
|
|
117
133
|
Default to the same fresh-thread handoff flow as bare `$throughline` when the
|
package/docs/00_overview.md
CHANGED
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
| [10_transcript_injection_plan.md](10_transcript_injection_plan.md) | transcript injection 検証計画と v0.5 実機結果 |
|
|
19
19
|
| [11_codex_monitor_implementation_plan.md](11_codex_monitor_implementation_plan.md) | Codex monitor 対応の実装記録 |
|
|
20
20
|
| [13_native_factory_diagnostics_plan.md](13_native_factory_diagnostics_plan.md) | native factory read-only readiness 診断の実装記録 |
|
|
21
|
-
| [14_observer_completed_turn_feed_plan.md](14_observer_completed_turn_feed_plan.md) | Observer向けcompleted-only read / wait CLI
|
|
21
|
+
| [14_observer_completed_turn_feed_plan.md](14_observer_completed_turn_feed_plan.md) | Observer向けcompleted-only read / wait CLIの完了済み設計・受入記録。v0.7.0で公開済み |
|
|
22
22
|
| [15_windows_ci_release_latency_plan.md](15_windows_ci_release_latency_plan.md) | Windows CI 18分の原因、ACL安全網を維持した短縮、release gateの受入条件 |
|
|
23
|
-
| [16_readonly_handoff_context_plan.md](16_readonly_handoff_context_plan.md) | DB所有権を変更せずSessionStartと同じ記憶を返す、ローカルランチャー向けread-only I/F |
|
|
23
|
+
| [16_readonly_handoff_context_plan.md](16_readonly_handoff_context_plan.md) | DB所有権を変更せずSessionStartと同じ記憶を返す、ローカルランチャー向けread-only I/F。v0.9.0で公開済み |
|
|
24
24
|
| [BUGHUB_RUNTIME_ERROR_STORE_PLAN.md](BUGHUB_RUNTIME_ERROR_STORE_PLAN.md) | local runtime error aggregate store の契約と実装 TODO |
|
|
25
25
|
|
|
26
26
|
## Supporting Records
|
|
@@ -44,6 +44,11 @@ ThroughlineはClaude Stop receiptとCodex rolloutの`task_complete`だけからc
|
|
|
44
44
|
ObserverがDB、WAL、rolloutを直接監視するfallbackは持たない。waitは最大3600秒で、`changed`、`timeout`、
|
|
45
45
|
`resync_required`、`ambiguous_parent`を返す。
|
|
46
46
|
|
|
47
|
+
portable contextの公開境界は`throughline handoff-context --session <id> --json`である。既存DBを
|
|
48
|
+
read-onlyで開き、SessionStartと同じbudgeted inheritance contextを返すが、baton、merge、
|
|
49
|
+
`sessions.merged_into`、L1/L2/L3 rowの`session_id`は変更しない。Observer境界とは用途もschemaも別で、
|
|
50
|
+
AIterm v0.23.0はこのCLIだけをconsumer境界として使う。
|
|
51
|
+
|
|
47
52
|
## Entrypoints
|
|
48
53
|
|
|
49
54
|
- [../CLAUDE.md](../CLAUDE.md): AI 作業者向けの正本。
|
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
実行 ToDo、依存、状態、完了証拠の正本は Lattice plan `readonly-handoff-context` とする。
|
|
10
10
|
|
|
11
|
+
## 完了
|
|
12
|
+
|
|
13
|
+
2026-08-04に`throughline@0.9.0`としてnpm、tag、GitHub Release、global installまで公開した。
|
|
14
|
+
focused契約testと全回帰は729 pass/1 skip/0 fail。AIterm v0.23.0の代表cross-vendor smokeでは
|
|
15
|
+
Codex source memoryをClaudeへ注入し、前後でsource session、`sessions.merged_into`、L1/L2/L3 row所属が
|
|
16
|
+
完全一致することを確認した。公開後の現行ドキュメント全域監査は、Latticeの終端ToDoを再openして
|
|
17
|
+
README、作業者入口、配布Codex skill、docs索引、計画、CHANGELOGへ同期した。変更Markdownの
|
|
18
|
+
相対リンク監査とCLI help/handoff-contextのfocused test 5/5を通過した。
|
|
19
|
+
|
|
11
20
|
## 契約
|
|
12
21
|
|
|
13
22
|
- `throughline handoff-context --session <id> --json` は session を明示必須とする。
|
|
@@ -54,3 +54,26 @@ read-only refuterによる敵対的検証ではP0はなく、次のP1を設計
|
|
|
54
54
|
- matrix縮小
|
|
55
55
|
- npm自動publish、credential保管
|
|
56
56
|
- release番号だけを根拠にした性能成功扱い
|
|
57
|
+
|
|
58
|
+
## Terminal audit evidence
|
|
59
|
+
|
|
60
|
+
2026-08-08 に公開後の終端監査を再確認し、次を受け入れた。
|
|
61
|
+
|
|
62
|
+
- 公開commit `df215fcaeb6d09d13bfbf5389c6f7c98a995b25c` は現在の `origin/main` の祖先で、
|
|
63
|
+
annotated tag `v0.8.7` は同commitへ解決する。
|
|
64
|
+
- GitHub Actions run `29726067549` は公開commitに対して9/9 greenである。Windows jobは
|
|
65
|
+
2分46秒/2分53秒/2分59秒、unit test stepは2分14秒/2分26秒/2分38秒で、
|
|
66
|
+
5分SLOと3分目標を満たした。
|
|
67
|
+
- GitHub Release `v0.8.7` はdraft/prereleaseではなく公開済みで、公開commit、CI run、
|
|
68
|
+
Windows実測、npm shasumを保持する。
|
|
69
|
+
- npm registryの `throughline@0.8.7` はshasum
|
|
70
|
+
`35a50f6878095d0881e75ebfb1da097a8da937c8`、integrity
|
|
71
|
+
`sha512-7Z/Mz0FRT2bJaxOLl+M8AYlNp5AhmaouQYeiLQ5BKu94+iyo/p1l1vrOhlFHWMlNSv4a89mGYtEAzolPZGRKtA==`
|
|
72
|
+
を返す。registryから再取得したtarballも同じSHA-1で、212 entries、package version `0.8.7`
|
|
73
|
+
を確認した。
|
|
74
|
+
- 元のrelease sessionではregistry版global install、`throughline --version = 0.8.7`、
|
|
75
|
+
managed hooks/skillの再install、migration、`doctor --codex` exit 0まで受け入れた。
|
|
76
|
+
現在のglobal installは後続release `0.9.0`へ正当に更新済みのため、監査目的のdowngradeは行わない。
|
|
77
|
+
|
|
78
|
+
以上により、製品受入は2026-07-20時点で完了しており、遅延していたLattice terminal-auditの
|
|
79
|
+
記録を証拠付きで閉じてよいと裁定する。
|
package/package.json
CHANGED
|
@@ -34,10 +34,10 @@ function childEnv(home) {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function runNode(args, { home, cwd = REPO_ROOT, input = '' }) {
|
|
37
|
+
function runNode(args, { home, cwd = REPO_ROOT, input = '', env = {} }) {
|
|
38
38
|
return spawnSync(process.execPath, args, {
|
|
39
39
|
cwd,
|
|
40
|
-
env: childEnv(home),
|
|
40
|
+
env: { ...childEnv(home), ...env },
|
|
41
41
|
input,
|
|
42
42
|
encoding: 'utf8',
|
|
43
43
|
});
|
|
@@ -74,6 +74,71 @@ test('hook modules can be imported without executing their hook body', () => {
|
|
|
74
74
|
}
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
+
test('Grok camelCase envelopes are unsupported no-op before Throughline side effects [GF04T]', () => {
|
|
78
|
+
const home = makeTempHome();
|
|
79
|
+
const project = makeTempProject();
|
|
80
|
+
const common = {
|
|
81
|
+
sessionId: 'grok-session',
|
|
82
|
+
cwd: project,
|
|
83
|
+
workspaceRoot: project,
|
|
84
|
+
timestamp: '2026-08-14T00:00:00Z',
|
|
85
|
+
permissionMode: 'default',
|
|
86
|
+
};
|
|
87
|
+
const cases = [
|
|
88
|
+
{
|
|
89
|
+
event: 'SessionStart',
|
|
90
|
+
entrypoint: 'src/session-start.mjs',
|
|
91
|
+
input: { ...common, hookEventName: 'session_start', source: 'startup' },
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
event: 'UserPromptSubmit',
|
|
95
|
+
entrypoint: 'src/prompt-submit.mjs',
|
|
96
|
+
input: { ...common, hookEventName: 'user_prompt_submit', prompt: 'hello' },
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
event: 'Stop',
|
|
100
|
+
entrypoint: 'src/turn-processor.mjs',
|
|
101
|
+
input: {
|
|
102
|
+
...common,
|
|
103
|
+
hookEventName: 'stop',
|
|
104
|
+
reason: 'end_turn',
|
|
105
|
+
stopHookActive: false,
|
|
106
|
+
lastAssistantMessage: 'done',
|
|
107
|
+
backgroundTasks: [],
|
|
108
|
+
sessionCrons: [],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const results = cases.map(({ event, entrypoint, input }) => {
|
|
115
|
+
const result = runNode([join(REPO_ROOT, entrypoint)], {
|
|
116
|
+
home,
|
|
117
|
+
cwd: project,
|
|
118
|
+
input: JSON.stringify(input),
|
|
119
|
+
env: { THROUGHLINE_NO_VSCODE: '0', TERM_PROGRAM: 'vscode' },
|
|
120
|
+
});
|
|
121
|
+
return { event, status: result.status, stdout: result.stdout, stderr: result.stderr };
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
assert.deepEqual(
|
|
125
|
+
{
|
|
126
|
+
results,
|
|
127
|
+
throughlineStateExists: existsSync(join(home, '.throughline')),
|
|
128
|
+
vscodeTaskExists: existsSync(join(project, '.vscode', 'tasks.json')),
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
results: cases.map(({ event }) => ({ event, status: 0, stdout: '', stderr: '' })),
|
|
132
|
+
throughlineStateExists: false,
|
|
133
|
+
vscodeTaskExists: false,
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
} finally {
|
|
137
|
+
rmSync(project, { recursive: true, force: true });
|
|
138
|
+
rmSync(home, { recursive: true, force: true });
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
77
142
|
test('prompt-submit subprocess writes a /tl baton into an isolated DB', () => {
|
|
78
143
|
const home = makeTempHome();
|
|
79
144
|
const project = makeTempProject();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Grok can invoke Claude-compatible hook commands with its camelCase wire.
|
|
2
|
+
// Throughline does not support Grok as a host, so this envelope is ignored
|
|
3
|
+
// before DB, state, VS Code task, transcript, or runtime-error side effects.
|
|
4
|
+
export function isUnsupportedNonClaudeEnvelope(payload) {
|
|
5
|
+
return payload !== null
|
|
6
|
+
&& typeof payload === 'object'
|
|
7
|
+
&& typeof payload.sessionId === 'string'
|
|
8
|
+
&& payload.sessionId.length > 0
|
|
9
|
+
&& typeof payload.hookEventName === 'string'
|
|
10
|
+
&& payload.hookEventName.length > 0
|
|
11
|
+
&& !Object.hasOwn(payload, 'session_id');
|
|
12
|
+
}
|
package/src/prompt-submit.mjs
CHANGED
|
@@ -42,6 +42,7 @@ import { join, dirname } from 'node:path';
|
|
|
42
42
|
import { homedir } from 'node:os';
|
|
43
43
|
import { pathToFileURL } from 'node:url';
|
|
44
44
|
import { recordRuntimeErrorBestEffort } from './runtime-error-store.mjs';
|
|
45
|
+
import { isUnsupportedNonClaudeEnvelope } from './hook-envelope.mjs';
|
|
45
46
|
|
|
46
47
|
// Phase 0-5 spike marker (SessionStart の spike-inject.flag とは別)
|
|
47
48
|
const PROMPT_SPIKE_MARKER_PATH = join(homedir(), '.throughline', 'spike-prompt.flag');
|
|
@@ -143,6 +144,7 @@ export async function run() {
|
|
|
143
144
|
});
|
|
144
145
|
|
|
145
146
|
const payload = JSON.parse(raw);
|
|
147
|
+
if (isUnsupportedNonClaudeEnvelope(payload)) return;
|
|
146
148
|
const { session_id, cwd, prompt } = payload;
|
|
147
149
|
|
|
148
150
|
// VSCode 新規プロジェクトへの tasks.json 自動プロビジョニング。
|
package/src/session-start.mjs
CHANGED
|
@@ -32,6 +32,7 @@ import { logDecision } from './decision-log.mjs';
|
|
|
32
32
|
import { existsSync } from 'node:fs';
|
|
33
33
|
import { pathToFileURL } from 'node:url';
|
|
34
34
|
import { recordRuntimeErrorBestEffort } from './runtime-error-store.mjs';
|
|
35
|
+
import { isUnsupportedNonClaudeEnvelope } from './hook-envelope.mjs';
|
|
35
36
|
|
|
36
37
|
const ENV_DISABLE_AUTO_HANDOFF = 'THROUGHLINE_DISABLE_AUTO_HANDOFF';
|
|
37
38
|
|
|
@@ -91,6 +92,7 @@ export async function run() {
|
|
|
91
92
|
});
|
|
92
93
|
|
|
93
94
|
const payload = JSON.parse(raw);
|
|
95
|
+
if (isUnsupportedNonClaudeEnvelope(payload)) return;
|
|
94
96
|
const { session_id, cwd, source, transcript_path } = payload;
|
|
95
97
|
|
|
96
98
|
if (!session_id) throw new Error('Missing session_id in SessionStart payload');
|
package/src/turn-processor.mjs
CHANGED
|
@@ -46,6 +46,7 @@ import { readLatestUsage } from './transcript-usage.mjs';
|
|
|
46
46
|
import { pathToFileURL } from 'node:url';
|
|
47
47
|
import { recordRuntimeErrorBestEffort } from './runtime-error-store.mjs';
|
|
48
48
|
import { writeCompletedTurnReceipt } from './completed-turn-receipts.mjs';
|
|
49
|
+
import { isUnsupportedNonClaudeEnvelope } from './hook-envelope.mjs';
|
|
49
50
|
|
|
50
51
|
/** 直近 N ターンは bodies を生で残し、それより古いものだけ L1 要約する。 */
|
|
51
52
|
export const L2_WINDOW = 20;
|
|
@@ -191,6 +192,7 @@ export async function run() {
|
|
|
191
192
|
});
|
|
192
193
|
|
|
193
194
|
const payload = JSON.parse(raw || '{}');
|
|
195
|
+
if (isUnsupportedNonClaudeEnvelope(payload)) return;
|
|
194
196
|
const { session_id, transcript_path, cwd, last_assistant_message } = payload;
|
|
195
197
|
if (!session_id) throw new Error('Missing session_id in Stop payload');
|
|
196
198
|
|