@linzumi/cli 1.0.275 → 1.0.276

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/README.md +10 -9
  2. package/dist/{impl-ts/cloud-supervisor.mjs → cloud-supervisor.mjs} +78 -78
  3. package/dist/hosted-coding-account-worker.js +25 -0
  4. package/dist/index.js +1609 -3
  5. package/dist/mcp-server.mjs +174 -168
  6. package/dist/{impl-res/native → native}/darwin/manifest.json +5 -2
  7. package/dist/remote-codex-harness-worker.js +757 -0
  8. package/dist/tcp-tunnel-cli.mjs +25 -69
  9. package/dist/topics-cli.mjs +137 -13
  10. package/package.json +9 -16
  11. package/scripts/build.mjs +33 -356
  12. package/scripts/bump-cli-version.mjs +35 -9
  13. package/scripts/check-package-size-budget.mjs +13 -61
  14. package/dist/impl-res/cloud-supervisor.mjs +0 -281
  15. package/dist/impl-res/index.js +0 -1546
  16. package/dist/impl-res/mcp-server.mjs +0 -461
  17. package/dist/impl-ts/assets/linzumi-logo.svg +0 -1
  18. package/dist/impl-ts/crossHarnessFailover.mjs +0 -14
  19. package/dist/impl-ts/index.js +0 -2617
  20. package/dist/impl-ts/mcp-server.mjs +0 -455
  21. package/dist/impl-ts/native/darwin/manifest.json +0 -21
  22. package/dist/impl-ts/native/darwin/preview-process-identity +0 -0
  23. package/dist/impl-ts/tcp-tunnel-cli.mjs +0 -93
  24. package/scripts/build-differential-entry-impl-res.mjs +0 -64
  25. package/scripts/build-differential-entry.mjs +0 -180
  26. package/scripts/build-onboarding-demo-evidence.mjs +0 -186
  27. package/scripts/build-tui-demo-evidence.mjs +0 -107
  28. package/scripts/diagnose-claude-probe.ts +0 -198
  29. package/scripts/differentialMutations.mjs +0 -114
  30. package/scripts/f5_live_cross_harness_failover_e2e.mjs +0 -239
  31. package/scripts/golden-chaos-harness.mjs +0 -1420
  32. package/scripts/normalize-agent-replay-fixture.ts +0 -281
  33. package/scripts/promote-gstack-claude-smoke-fixture.ts +0 -891
  34. package/scripts/record-agent-replay-fixtures.ts +0 -610
  35. package/scripts/replay-agent-backend-browser.ts +0 -198
  36. package/scripts/replay-agent-backend.ts +0 -1961
  37. package/scripts/replay-agent-fixture.ts +0 -326
  38. package/scripts/typecheck.mjs +0 -42
  39. /package/dist/{impl-res/assets → assets}/linzumi-logo.svg +0 -0
  40. /package/dist/{impl-res/crossHarnessFailover.mjs → crossHarnessFailover.mjs} +0 -0
  41. /package/dist/{impl-res/native → native}/darwin/preview-process-identity +0 -0
@@ -1,610 +0,0 @@
1
- /*
2
- - Date: 2026-06-29
3
- Spec: plans/2026-06-29-claude-code-raw-replay-harness-plan.md
4
- Relationship: Automatic raw fixture capture entrypoint. It drives real Codex
5
- app-server and Claude Code SDK sessions against the prepared fixture
6
- playground and lets the existing live recorder write provider-native NDJSON.
7
-
8
- - Date: 2026-07-02
9
- Spec: plans/2026-07-02-claude-plan-mode-observability-v2-implementation-plan.md
10
- Relationship: Adds the claude-plan-mode capture lane - a real Claude Code
11
- session started in permissionMode 'plan' with hook events on, auto-approving
12
- the ExitPlanMode plan review so the fixture records the full observable
13
- plan-mode lifecycle (status permissionMode frames, ExitPlanMode call/result).
14
- */
15
- import { mkdtemp, cp, mkdir, readFile, writeFile, rm } from 'node:fs/promises';
16
- import { tmpdir } from 'node:os';
17
- import { join, resolve, dirname } from 'node:path';
18
- import { fileURLToPath } from 'node:url';
19
- import {
20
- query as claudeQuery,
21
- type PermissionMode,
22
- } from '@anthropic-ai/claude-agent-sdk';
23
- import {
24
- connectCodexAppServer,
25
- startCodexAppServer,
26
- } from '../src/codexAppServer';
27
- import { recordLiveAgentReplay } from '../src/agentReplayLiveRecorder';
28
- import { type JsonObject, type JsonValue } from '../src/protocol';
29
- import { preflightGstackClaudeSmokeSetup } from '../src/gstackReplayPreflight';
30
-
31
- type Provider = 'codex' | 'claude' | 'gstack-claude-smoke' | 'claude-plan-mode';
32
-
33
- type CaptureOptions = {
34
- readonly providers: readonly Provider[];
35
- readonly packageRoot: string;
36
- readonly playgroundPath: string;
37
- readonly outDir: string;
38
- readonly exactRaw: boolean;
39
- readonly keepWorkdirs: boolean;
40
- readonly codexBin: string;
41
- readonly codexApprovalPolicy: string | undefined;
42
- readonly codexSandbox: string | undefined;
43
- readonly claudePermissionMode: PermissionMode | undefined;
44
- readonly claudeModel: string | undefined;
45
- readonly gstackSkillDir: string | undefined;
46
- readonly gstackCli: string | undefined;
47
- readonly preflightOnly: boolean;
48
- readonly preflightJson: boolean;
49
- };
50
-
51
- type StartedCodex = Awaited<ReturnType<typeof startCodexAppServer>>;
52
-
53
- type CodexClient = Awaited<ReturnType<typeof connectCodexAppServer>>;
54
-
55
- type PreflightResult =
56
- | { readonly type: 'none' }
57
- | {
58
- readonly type: 'gstack';
59
- readonly setup: Exclude<
60
- Awaited<ReturnType<typeof preflightGstackClaudeSmokeSetup>>,
61
- { readonly type: 'error' }
62
- >;
63
- };
64
-
65
- const thisFile = fileURLToPath(import.meta.url);
66
- const packageRoot = resolve(dirname(thisFile), '..');
67
-
68
- async function main(): Promise<void> {
69
- const argv = process.argv.slice(2);
70
- if (argv.includes('--help')) {
71
- printHelp();
72
- return;
73
- }
74
-
75
- const options = parseArgs(argv);
76
- await mkdir(options.outDir, { recursive: true });
77
-
78
- for (const provider of options.providers) {
79
- const runDir = await prepareRunDirectory(options, provider);
80
- let preflight: PreflightResult = { type: 'none' };
81
- try {
82
- preflight = await preflightCaptureProvider(options, provider, runDir);
83
- } catch (error) {
84
- if (!options.keepWorkdirs) {
85
- await rm(dirname(runDir), { recursive: true, force: true });
86
- }
87
-
88
- throw error;
89
- }
90
-
91
- if (options.preflightOnly) {
92
- writePreflightOk(options, provider, runDir, preflight);
93
-
94
- switch (options.keepWorkdirs) {
95
- case true:
96
- process.stdout.write(`${provider}: workdir ${runDir}\n`);
97
- break;
98
- case false:
99
- await rm(dirname(runDir), { recursive: true, force: true });
100
- break;
101
- }
102
-
103
- continue;
104
- }
105
- const fixturePath = join(options.outDir, `${provider}.ndjson`);
106
- await writeFile(fixturePath, '', 'utf8');
107
- process.env.LINZUMI_AGENT_REPLAY_RECORD_FILE = fixturePath;
108
- process.env.LINZUMI_AGENT_REPLAY_EXACT_RAW = options.exactRaw ? '1' : '0';
109
-
110
- const prompt = await fixturePrompt(options.playgroundPath, provider);
111
- recordLiveAgentReplay({
112
- provider: provider === 'codex' ? 'codex-app-server' : 'claude-code-sdk',
113
- captureKind: 'session-marker',
114
- rawValue: {
115
- type: 'capture_started',
116
- provider,
117
- cwd: runDir,
118
- sourcePlayground: options.playgroundPath,
119
- },
120
- });
121
-
122
- switch (provider) {
123
- case 'codex':
124
- await runCodexCapture(options, runDir, prompt);
125
- break;
126
- case 'claude':
127
- await runClaudeCapture(options, runDir, prompt, 'generic');
128
- break;
129
- case 'gstack-claude-smoke':
130
- await runClaudeCapture(options, runDir, prompt, 'gstack');
131
- break;
132
- case 'claude-plan-mode':
133
- await runClaudeCapture(options, runDir, prompt, 'plan-mode');
134
- break;
135
- }
136
-
137
- recordLiveAgentReplay({
138
- provider: provider === 'codex' ? 'codex-app-server' : 'claude-code-sdk',
139
- captureKind: 'session-marker',
140
- rawValue: {
141
- type: 'capture_completed',
142
- provider,
143
- cwd: runDir,
144
- keptWorkdir: options.keepWorkdirs,
145
- },
146
- });
147
-
148
- process.stdout.write(`${provider}: wrote ${fixturePath}\n`);
149
-
150
- switch (options.keepWorkdirs) {
151
- case true:
152
- process.stdout.write(`${provider}: workdir ${runDir}\n`);
153
- break;
154
- case false:
155
- await rm(dirname(runDir), { recursive: true, force: true });
156
- break;
157
- }
158
- }
159
- }
160
-
161
- function printHelp(): void {
162
- process.stdout.write(
163
- `Usage: pnpm run record:agent-fixtures -- [options]\n\nOptions:\n --provider codex|claude|gstack-claude-smoke|both\n Providers to run. Default: both\n --out-dir <path> Directory for NDJSON fixtures. Default: /tmp/linzumi-agent-replay-*\n --playground <path> Prepared playground to copy. Default: checked-in fixture playground\n --exact-raw Preserve raw provider payloads except data omitted by the provider itself\n --keep-workdirs Print and leave copied playground workdirs for inspection\n --preflight-only Check provider-specific local setup without starting an agent session\n --preflight-json Emit machine-readable JSON for preflight-only success/failure\n --codex-bin <path> Codex executable. Default: codex\n --codex-approval-policy <value> Forwarded to Codex app-server thread/turn start\n --codex-sandbox <value> Forwarded to Codex app-server thread/turn start\n --claude-permission-mode <value> Forwarded to Claude SDK query options\n --claude-model <model> Forwarded to Claude SDK query options. Can also use LINZUMI_AGENT_REPLAY_CLAUDE_MODEL\n --gstack-skill-dir <path> Explicit gstack Claude skill directory. Can also use LINZUMI_AGENT_REPLAY_GSTACK_SKILL_DIR\n --gstack-cli <path> Explicit gstack CLI path/name. Can also use LINZUMI_AGENT_REPLAY_GSTACK_CLI\n`
164
- );
165
- }
166
-
167
- function parseArgs(args: readonly string[]): CaptureOptions {
168
- const rawProvider = argValue(args, '--provider') ?? 'both';
169
- const providers = providersFromArg(rawProvider);
170
- const outDir = resolve(
171
- argValue(args, '--out-dir') ??
172
- join(tmpdir(), `linzumi-agent-replay-${Date.now()}`)
173
- );
174
- const playgroundPath = resolve(
175
- argValue(args, '--playground') ??
176
- join(packageRoot, 'test/fixtures/agent-fixture-playground')
177
- );
178
-
179
- return {
180
- providers,
181
- packageRoot,
182
- playgroundPath,
183
- outDir,
184
- exactRaw: flag(args, '--exact-raw'),
185
- keepWorkdirs: flag(args, '--keep-workdirs'),
186
- preflightOnly: flag(args, '--preflight-only'),
187
- preflightJson: flag(args, '--preflight-json'),
188
- codexBin:
189
- argValue(args, '--codex-bin') ??
190
- process.env.LINZUMI_AGENT_REPLAY_CODEX_BIN ??
191
- 'codex',
192
- codexApprovalPolicy:
193
- argValue(args, '--codex-approval-policy') ??
194
- process.env.LINZUMI_AGENT_REPLAY_CODEX_APPROVAL_POLICY,
195
- codexSandbox:
196
- argValue(args, '--codex-sandbox') ??
197
- process.env.LINZUMI_AGENT_REPLAY_CODEX_SANDBOX,
198
- claudePermissionMode: claudePermissionModeFromArg(
199
- argValue(args, '--claude-permission-mode') ??
200
- process.env.LINZUMI_AGENT_REPLAY_CLAUDE_PERMISSION_MODE
201
- ),
202
- claudeModel:
203
- argValue(args, '--claude-model') ??
204
- process.env.LINZUMI_AGENT_REPLAY_CLAUDE_MODEL,
205
- gstackSkillDir:
206
- argValue(args, '--gstack-skill-dir') ??
207
- process.env.LINZUMI_AGENT_REPLAY_GSTACK_SKILL_DIR,
208
- gstackCli:
209
- argValue(args, '--gstack-cli') ??
210
- process.env.LINZUMI_AGENT_REPLAY_GSTACK_CLI,
211
- };
212
- }
213
-
214
- function providersFromArg(value: string): readonly Provider[] {
215
- switch (value) {
216
- case 'both':
217
- return ['codex', 'claude'];
218
- case 'codex':
219
- return ['codex'];
220
- case 'claude':
221
- return ['claude'];
222
- case 'gstack-claude-smoke':
223
- return ['gstack-claude-smoke'];
224
- case 'claude-plan-mode':
225
- return ['claude-plan-mode'];
226
- default:
227
- throw new Error(`unsupported --provider value: ${value}`);
228
- }
229
- }
230
-
231
- function claudePermissionModeFromArg(
232
- value: string | undefined
233
- ): PermissionMode | undefined {
234
- switch (value) {
235
- case undefined:
236
- return undefined;
237
- case 'default':
238
- case 'acceptEdits':
239
- case 'bypassPermissions':
240
- case 'plan':
241
- case 'dontAsk':
242
- case 'auto':
243
- return value;
244
- default:
245
- throw new Error(`unsupported Claude permission mode: ${value}`);
246
- }
247
- }
248
-
249
- function flag(args: readonly string[], name: string): boolean {
250
- return args.includes(name);
251
- }
252
-
253
- function argValue(args: readonly string[], name: string): string | undefined {
254
- const index = args.indexOf(name);
255
-
256
- if (index === -1) {
257
- return undefined;
258
- }
259
-
260
- const value = args[index + 1];
261
-
262
- if (value === undefined || value.startsWith('--')) {
263
- throw new Error(`${name} requires a value`);
264
- }
265
-
266
- return value;
267
- }
268
-
269
- async function prepareRunDirectory(
270
- options: CaptureOptions,
271
- provider: Provider
272
- ): Promise<string> {
273
- const base = await mkdtemp(join(tmpdir(), `linzumi-${provider}-fixture-`));
274
- const runDir = join(base, 'agent-fixture-playground');
275
- await cp(options.playgroundPath, runDir, { recursive: true });
276
- return runDir;
277
- }
278
-
279
- function fixturePromptHeading(provider: Provider): string {
280
- switch (provider) {
281
- case 'codex':
282
- return 'Codex Prompt';
283
- case 'claude':
284
- return 'Claude Code Prompt';
285
- case 'gstack-claude-smoke':
286
- return 'Gstack Claude Smoke Prompt';
287
- case 'claude-plan-mode':
288
- return 'Claude Plan Mode Prompt';
289
- }
290
- }
291
-
292
- async function preflightCaptureProvider(
293
- options: CaptureOptions,
294
- provider: Provider,
295
- cwd: string
296
- ): Promise<PreflightResult> {
297
- switch (provider) {
298
- case 'codex':
299
- case 'claude':
300
- case 'claude-plan-mode':
301
- return { type: 'none' };
302
- case 'gstack-claude-smoke':
303
- return {
304
- type: 'gstack',
305
- setup: await preflightGstackClaudeSmoke(options, cwd),
306
- };
307
- }
308
- }
309
-
310
- async function preflightGstackClaudeSmoke(
311
- options: CaptureOptions,
312
- cwd: string
313
- ): Promise<
314
- Exclude<
315
- Awaited<ReturnType<typeof preflightGstackClaudeSmokeSetup>>,
316
- { readonly type: 'error' }
317
- >
318
- > {
319
- const result = await preflightGstackClaudeSmokeSetup({
320
- cwd,
321
- env: {
322
- ...process.env,
323
- ...(options.gstackSkillDir === undefined
324
- ? {}
325
- : { LINZUMI_AGENT_REPLAY_GSTACK_SKILL_DIR: options.gstackSkillDir }),
326
- ...(options.gstackCli === undefined
327
- ? {}
328
- : { LINZUMI_AGENT_REPLAY_GSTACK_CLI: options.gstackCli }),
329
- },
330
- });
331
-
332
- switch (result.type) {
333
- case 'ok':
334
- return result;
335
- case 'error':
336
- writePreflightError(options, 'gstack-claude-smoke', cwd, result);
337
- throw new Error(result.message);
338
- }
339
- }
340
-
341
- function writePreflightOk(
342
- options: CaptureOptions,
343
- provider: Provider,
344
- cwd: string,
345
- preflight: PreflightResult
346
- ): void {
347
- if (options.preflightJson) {
348
- process.stdout.write(
349
- `${JSON.stringify(preflightOkJson(provider, cwd, preflight), null, 2)}\n`
350
- );
351
- return;
352
- }
353
-
354
- process.stdout.write(`${provider}: preflight ok (${cwd})\n`);
355
- }
356
-
357
- function preflightOkJson(
358
- provider: Provider,
359
- cwd: string,
360
- preflight: PreflightResult
361
- ): JsonObject {
362
- switch (preflight.type) {
363
- case 'none':
364
- return { provider, status: 'ok', cwd };
365
- case 'gstack':
366
- return {
367
- provider,
368
- status: 'ok',
369
- cwd,
370
- setup: {
371
- skillDir: preflight.setup.skillDir,
372
- cliPath: preflight.setup.cliPath,
373
- checkedSkillDirs: preflight.setup.checkedSkillDirs,
374
- checkedCliNames: preflight.setup.checkedCliNames,
375
- runtimeProfile: preflight.setup.runtimeProfile,
376
- includeHookEvents: preflight.setup.includeHookEvents,
377
- preservesClaudeTasks: preflight.setup.preservesClaudeTasks,
378
- },
379
- };
380
- }
381
- }
382
-
383
- function writePreflightError(
384
- options: CaptureOptions,
385
- provider: Provider,
386
- cwd: string,
387
- result: Exclude<
388
- Awaited<ReturnType<typeof preflightGstackClaudeSmokeSetup>>,
389
- { readonly type: 'ok' }
390
- >
391
- ): void {
392
- if (!options.preflightJson) {
393
- return;
394
- }
395
-
396
- process.stdout.write(
397
- `${JSON.stringify(
398
- {
399
- provider,
400
- status: 'failed',
401
- cwd,
402
- reason: result.message,
403
- missing: result.missing,
404
- checkedSkillDirs: result.checkedSkillDirs,
405
- checkedCliNames: result.checkedCliNames,
406
- cliPath: result.cliPath ?? null,
407
- },
408
- null,
409
- 2
410
- )}\n`
411
- );
412
- }
413
-
414
- async function fixturePrompt(
415
- path: string,
416
- provider: Provider
417
- ): Promise<string> {
418
- const markdown = await readFile(join(path, 'FIXTURE_PROMPTS.md'), 'utf8');
419
- const heading = fixturePromptHeading(provider);
420
- const sectionStart = markdown.indexOf(`## ${heading}`);
421
-
422
- if (sectionStart === -1) {
423
- throw new Error(`could not find ${heading} in FIXTURE_PROMPTS.md`);
424
- }
425
-
426
- const fenceStartMarker = '```text\n';
427
- const fenceEndMarker = '\n```';
428
- const codeFenceStart = markdown.indexOf(fenceStartMarker, sectionStart);
429
-
430
- if (codeFenceStart === -1) {
431
- throw new Error(
432
- `could not find text fence for ${heading} in FIXTURE_PROMPTS.md`
433
- );
434
- }
435
-
436
- const bodyStart = codeFenceStart + fenceStartMarker.length;
437
- const bodyEnd = markdown.indexOf(fenceEndMarker, bodyStart);
438
-
439
- if (bodyEnd === -1) {
440
- throw new Error(
441
- `could not find closing text fence for ${heading} in FIXTURE_PROMPTS.md`
442
- );
443
- }
444
-
445
- return markdown.slice(bodyStart, bodyEnd);
446
- }
447
-
448
- async function runCodexCapture(
449
- options: CaptureOptions,
450
- cwd: string,
451
- prompt: string
452
- ): Promise<void> {
453
- let started: StartedCodex | undefined;
454
- let client: CodexClient | undefined;
455
-
456
- try {
457
- started = await startCodexAppServer(options.codexBin, cwd);
458
- client = await connectCodexAppServer(started.url);
459
- const completion = codexTurnCompletion(client);
460
- const threadResponse = await client.request('thread/start', {
461
- cwd,
462
- serviceName: 'kandan-local-runner',
463
- personality: 'pragmatic',
464
- ...(options.codexApprovalPolicy === undefined
465
- ? {}
466
- : { approvalPolicy: options.codexApprovalPolicy }),
467
- ...(options.codexSandbox === undefined
468
- ? {}
469
- : { sandbox: options.codexSandbox }),
470
- });
471
- const threadId = responseId(threadResponse, ['thread', 'id']);
472
-
473
- if (threadId === undefined) {
474
- throw new Error('thread/start response did not include thread.id');
475
- }
476
-
477
- const turnResponse = await client.request('turn/start', {
478
- threadId,
479
- input: [{ type: 'text', text: prompt }],
480
- ...(options.codexApprovalPolicy === undefined
481
- ? {}
482
- : { approvalPolicy: options.codexApprovalPolicy }),
483
- ...(options.codexSandbox === undefined
484
- ? {}
485
- : { sandbox: options.codexSandbox }),
486
- });
487
- const turnId = responseId(turnResponse, ['turn', 'id']);
488
-
489
- if (turnId === undefined) {
490
- throw new Error('turn/start response did not include turn.id');
491
- }
492
-
493
- await completion;
494
- } finally {
495
- client?.close();
496
- started?.stop();
497
- }
498
- }
499
-
500
- function codexTurnCompletion(client: CodexClient): Promise<void> {
501
- return new Promise((resolve, reject) => {
502
- client.onNotification((message) => {
503
- switch (message.method) {
504
- case 'turn/completed':
505
- case 'turn/failed':
506
- case 'turn/aborted':
507
- resolve();
508
- break;
509
- default:
510
- break;
511
- }
512
- });
513
- client.onClose((error) => reject(error));
514
- });
515
- }
516
-
517
- function responseId(
518
- response: JsonValue,
519
- path: readonly string[]
520
- ): string | undefined {
521
- if (!isJsonObject(response) || !isJsonObject(response.result)) {
522
- return undefined;
523
- }
524
-
525
- const first = response.result[path[0]];
526
- if (!isJsonObject(first)) {
527
- return undefined;
528
- }
529
-
530
- const value = first[path[1]];
531
- return typeof value === 'string' ? value : undefined;
532
- }
533
-
534
- async function runClaudeCapture(
535
- options: CaptureOptions,
536
- cwd: string,
537
- prompt: string,
538
- profile: 'generic' | 'gstack' | 'plan-mode'
539
- ): Promise<void> {
540
- const stream = claudeQuery({
541
- prompt,
542
- options: {
543
- cwd,
544
- persistSession: true,
545
- tools: { type: 'preset', preset: 'claude_code' },
546
- includePartialMessages: true,
547
- ...(options.claudeModel === undefined
548
- ? {}
549
- : { model: options.claudeModel }),
550
- ...(profile === 'gstack'
551
- ? { includeHookEvents: true, env: gstackClaudeCaptureEnv(process.env) }
552
- : {}),
553
- // Plan-mode lane: start IN plan mode so the fixture records the status
554
- // permissionMode frames and the ExitPlanMode call/result lifecycle. The
555
- // canUseTool bridge auto-approves everything (the playground is a
556
- // throwaway temp copy and plan mode already blocks writes), so the plan
557
- // review resolves deterministically without a human.
558
- ...(profile === 'plan-mode'
559
- ? {
560
- permissionMode: 'plan' as PermissionMode,
561
- includeHookEvents: true,
562
- canUseTool: async (
563
- _toolName: string,
564
- input: Record<string, unknown>
565
- ) => ({ behavior: 'allow' as const, updatedInput: input }),
566
- }
567
- : {}),
568
- settingSources: ['user', 'project', 'local'],
569
- systemPrompt: { type: 'preset', preset: 'claude_code' },
570
- ...(options.claudePermissionMode === undefined || profile === 'plan-mode'
571
- ? {}
572
- : {
573
- permissionMode: options.claudePermissionMode,
574
- ...(options.claudePermissionMode === 'bypassPermissions'
575
- ? { allowDangerouslySkipPermissions: true }
576
- : {}),
577
- }),
578
- },
579
- });
580
-
581
- for await (const message of stream) {
582
- recordLiveAgentReplay({
583
- provider: 'claude-code-sdk',
584
- captureKind: 'sdk-message',
585
- rawValue: message as JsonValue,
586
- });
587
- }
588
- }
589
-
590
- function gstackClaudeCaptureEnv(
591
- env: NodeJS.ProcessEnv
592
- ): Record<string, string> {
593
- return Object.fromEntries(
594
- Object.entries(env).filter(
595
- (entry): entry is [string, string] =>
596
- entry[1] !== undefined && entry[0] !== 'CLAUDE_CODE_ENABLE_TASKS'
597
- )
598
- );
599
- }
600
-
601
- function isJsonObject(value: JsonValue | undefined): value is JsonObject {
602
- return typeof value === 'object' && value !== null && !Array.isArray(value);
603
- }
604
-
605
- main().catch((error) => {
606
- process.stderr.write(
607
- `${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`
608
- );
609
- process.exitCode = 1;
610
- });