throughline 0.10.0 → 0.10.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/package.json +1 -1
  3. package/src/auditor-context.mjs +2 -1
  4. package/src/cli/codex-handoff-start.mjs +5 -23
  5. package/src/cli/codex-summarize.mjs +2 -1
  6. package/src/cli/codex-visibility-smoke.mjs +5 -4
  7. package/src/cli/grok-continue.mjs +8 -21
  8. package/src/cli/runtime-errors.test.mjs +1 -1
  9. package/src/codex-capture.mjs +14 -18
  10. package/src/codex-handoff-model-smoke.mjs +1 -1
  11. package/src/codex-sidecar.mjs +1 -1
  12. package/src/completed-turn-receipts.mjs +2 -29
  13. package/src/completed-turn-receipts.test.mjs +1 -1
  14. package/src/haiku-summarizer.mjs +1 -1
  15. package/src/handoff-record.mjs +3 -1
  16. package/src/hosts/claude.mjs +27 -0
  17. package/src/hosts/codex.mjs +28 -0
  18. package/src/hosts/grok.mjs +96 -0
  19. package/src/{hook-envelope.test.mjs → hosts/grok.test.mjs} +1 -1
  20. package/src/hosts/identity.mjs +71 -0
  21. package/src/hosts/identity.test.mjs +65 -0
  22. package/src/hosts/index.mjs +37 -0
  23. package/src/os/macos-terminal.mjs +44 -0
  24. package/src/os/open-url.mjs +16 -0
  25. package/src/os/paths.mjs +12 -0
  26. package/src/os/shell.mjs +16 -0
  27. package/src/os/windows-acl.mjs +57 -0
  28. package/src/project-path.mjs +4 -4
  29. package/src/prompt-submit.mjs +23 -55
  30. package/src/prompt-submit.test.mjs +27 -10
  31. package/src/runtime-error-hook.test.mjs +1 -1
  32. package/src/runtime-error-store.mjs +3 -40
  33. package/src/runtime-error-store.test.mjs +1 -1
  34. package/src/session-start.mjs +12 -4
  35. package/src/state-file.mjs +6 -5
  36. package/src/token-monitor.mjs +7 -6
  37. package/src/turn-processor.mjs +2 -2
  38. package/src/hook-envelope.mjs +0 -51
  39. /package/src/{portable-spawn-sync.mjs → os/portable-spawn-sync.mjs} +0 -0
  40. /package/src/{portable-spawn-sync.test.mjs → os/portable-spawn-sync.test.mjs} +0 -0
  41. /package/src/{windows-acl-test-helper.mjs → os/windows-acl-test-helper.mjs} +0 -0
@@ -28,6 +28,7 @@ import { buildCodexMonitorUsage } from './codex-usage.mjs';
28
28
  import { listCodexThreadCandidates } from './codex-thread-index.mjs';
29
29
  import { readLatestUsage } from './transcript-usage.mjs';
30
30
  import { startSizeQuery } from './terminal-size.mjs';
31
+ import { CODEX_HOST, CODEX_SESSION_PREFIX, codexSessionIdToThreadId } from './hosts/identity.mjs';
31
32
 
32
33
  const REFRESH_MS = 1000;
33
34
  // データ変化が無くても N ms ごとに再描画して「(24m ago)」表示を進める。
@@ -292,7 +293,7 @@ export function resolveColumns() {
292
293
  function formatLine({ state, usage, isActive, now = Date.now() }) {
293
294
  const project = basename(state.projectPath || '?');
294
295
  const shortId = formatShortSessionId(state);
295
- const host = state.host === 'codex' ? 'Codex' : state.host === 'unknown' ? 'Unknown' : 'Claude';
296
+ const host = state.host === CODEX_HOST ? 'Codex' : state.host === 'unknown' ? 'Unknown' : 'Claude';
296
297
  const tokens = usage?.tokens ?? 0;
297
298
  const max = usage?.contextWindowSize ?? 200_000;
298
299
  const ratio = max > 0 ? tokens / max : 0;
@@ -337,8 +338,8 @@ function formatLine({ state, usage, isActive, now = Date.now() }) {
337
338
 
338
339
  function formatShortSessionId(state) {
339
340
  const sessionId = String(state?.sessionId ?? '');
340
- if (state?.host === 'codex' && sessionId.startsWith('codex:')) {
341
- return sessionId.slice('codex:'.length, 'codex:'.length + 8);
341
+ if (state?.host === CODEX_HOST && sessionId.startsWith(CODEX_SESSION_PREFIX)) {
342
+ return (codexSessionIdToThreadId(sessionId) ?? '').slice(0, 8);
342
343
  }
343
344
  return sessionId.slice(0, 8);
344
345
  }
@@ -372,7 +373,7 @@ function withLiveActivity(state, now = Date.now()) {
372
373
  }
373
374
 
374
375
  function resolveMonitorUsage(state) {
375
- if (state.host === 'codex' && state.rolloutPath) {
376
+ if (state.host === CODEX_HOST && state.rolloutPath) {
376
377
  return buildCodexMonitorUsage(state.rolloutPath) ?? state.usage ?? null;
377
378
  }
378
379
  if (state.transcriptPath) {
@@ -410,8 +411,8 @@ function discoverCodexSessionStates(args = {}, cwd = process.cwd()) {
410
411
 
411
412
  lastCodexDiscoveryError = null;
412
413
  return candidates.map((candidate) => ({
413
- sessionId: `codex:${candidate.id}`,
414
- host: 'codex',
414
+ sessionId: `${CODEX_SESSION_PREFIX}${candidate.id}`,
415
+ host: CODEX_HOST,
415
416
  projectPath: normalizeProjectPath(candidate.cwd ?? cwd),
416
417
  transcriptPath: null,
417
418
  rolloutPath: candidate.rolloutPath,
@@ -46,7 +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 { normalizeHookPayload } from './hook-envelope.mjs';
49
+ import { hostAdapterForSessionId, normalizeHookPayload } from './hosts/index.mjs';
50
50
 
51
51
  /** 直近 N ターンは bodies を生で残し、それより古いものだけ L1 要約する。 */
52
52
  export const L2_WINDOW = 20;
@@ -205,7 +205,7 @@ export async function run() {
205
205
  process.stderr.write(`[vscode-task] ${msg}\n`);
206
206
  }
207
207
 
208
- if (!session_id.startsWith('grok:')) {
208
+ if (hostAdapterForSessionId(session_id).waitsForStopTranscriptFlush) {
209
209
  await waitForClaudeStopTranscriptFlush({
210
210
  transcriptPath: transcript_path,
211
211
  lastAssistantMessage: last_assistant_message,
@@ -1,51 +0,0 @@
1
- // Grok sends Claude-compatible hook commands with a camelCase wire
2
- // (sessionId, hookEventName) and no session_id. Throughline treats that
3
- // envelope as host=grok: normalize to the Claude snake_case contract and
4
- // prefix session ids so they never mix with Claude predecessor search.
5
- import { homedir } from 'node:os';
6
- import { join } from 'node:path';
7
-
8
- export const GROK_SESSION_PREFIX = 'grok:';
9
-
10
- export function isGrokEnvelope(payload) {
11
- return payload !== null
12
- && typeof payload === 'object'
13
- && typeof payload.sessionId === 'string'
14
- && payload.sessionId.length > 0
15
- && typeof payload.hookEventName === 'string'
16
- && payload.hookEventName.length > 0
17
- && !Object.hasOwn(payload, 'session_id');
18
- }
19
-
20
- export function grokBareSessionId(sessionId) {
21
- if (typeof sessionId !== 'string' || sessionId.length === 0) return null;
22
- return sessionId.startsWith(GROK_SESSION_PREFIX)
23
- ? sessionId.slice(GROK_SESSION_PREFIX.length)
24
- : sessionId;
25
- }
26
-
27
- export function deriveGrokChatHistoryPath(projectPath, sessionId, { home = homedir() } = {}) {
28
- const bare = grokBareSessionId(sessionId);
29
- if (!projectPath || !bare) return null;
30
- return join(home, '.grok', 'sessions', encodeURIComponent(projectPath), bare, 'chat_history.jsonl');
31
- }
32
-
33
- export function normalizeHookPayload(payload, { home = homedir() } = {}) {
34
- if (!isGrokEnvelope(payload)) return payload;
35
- const cwd = typeof payload.cwd === 'string' && payload.cwd.length > 0
36
- ? payload.cwd
37
- : (typeof payload.workspaceRoot === 'string' ? payload.workspaceRoot : undefined);
38
- // Live Grok Stop sets transcriptPath to updates.jsonl (sessionUpdate frames,
39
- // no user/assistant rows). L2 lives in chat_history.jsonl only.
40
- const transcriptPath = deriveGrokChatHistoryPath(cwd, payload.sessionId, { home });
41
- return {
42
- ...payload,
43
- session_id: `${GROK_SESSION_PREFIX}${payload.sessionId}`,
44
- cwd,
45
- source: payload.source,
46
- prompt: payload.prompt,
47
- hook_event_name: payload.hookEventName,
48
- transcript_path: transcriptPath,
49
- last_assistant_message: payload.lastAssistantMessage,
50
- };
51
- }