@robota-sdk/agent-ui-terminal 3.0.0-beta.86 → 3.0.0-beta.87

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 (66) hide show
  1. package/CHANGELOG.md +54 -14
  2. package/README.md +2 -2
  3. package/dist/node/index.d.ts +8 -2
  4. package/dist/node/index.d.ts.map +1 -1
  5. package/dist/node/index.js +14 -14
  6. package/dist/node/index.js.map +1 -1
  7. package/package.json +19 -20
  8. package/src/SupervisedSessionView.tsx +12 -2
  9. package/src/__tests__/SupervisedSessionView.test.tsx +9 -5
  10. package/src/__tests__/TuiInteractionChannel.display-contract.test.ts +1 -1
  11. package/src/__tests__/TuiInteractionChannel.lifecycle.test.ts +1 -1
  12. package/src/__tests__/UpdateNotice.test.tsx +2 -2
  13. package/src/__tests__/attached-app-session-switch.test.tsx +1 -1
  14. package/src/__tests__/attention-bell-and-marks.test.ts +5 -5
  15. package/src/__tests__/channel-factory-integration.test.ts +3 -3
  16. package/src/__tests__/checkpoint-store-per-channel.test.ts +1 -1
  17. package/src/__tests__/cjk-fallback-render.test.tsx +2 -2
  18. package/src/__tests__/command-handoff-pty-e2e.test.ts +2 -2
  19. package/src/__tests__/command-result-handler.test.ts +3 -3
  20. package/src/__tests__/fork-attach.test.tsx +3 -3
  21. package/src/__tests__/helpers/terminal-profiles.ts +4 -4
  22. package/src/__tests__/humanize-tool-name.test.ts +1 -1
  23. package/src/__tests__/org-policy-reaches-the-channel.test.ts +5 -5
  24. package/src/__tests__/product-display-name.test.tsx +5 -5
  25. package/src/__tests__/pty/ask-user-question.ptytest.ts +2 -2
  26. package/src/__tests__/pty/background-work-switcher.ptytest.ts +3 -3
  27. package/src/__tests__/pty/behavior-2437-git.ptytest.ts +9 -9
  28. package/src/__tests__/pty/flag-tui.ptytest.ts +2 -2
  29. package/src/__tests__/pty/flow-2006-deep-link.ptytest.ts +53 -24
  30. package/src/__tests__/pty/ime-cursor-tmux.ptytest.ts +59 -14
  31. package/src/__tests__/pty/ime-cursor.ptytest.ts +4 -4
  32. package/src/__tests__/pty/isolated-home.ts +37 -4
  33. package/src/__tests__/pty/org-policy.ptytest.ts +3 -3
  34. package/src/__tests__/pty/provider-setup.ptytest.ts +2 -2
  35. package/src/__tests__/pty/pty-driver.ts +8 -6
  36. package/src/__tests__/pty/replay-conversation.ptytest.ts +2 -2
  37. package/src/__tests__/pty/screen-005-prompt-footers.ptytest.ts +2 -2
  38. package/src/__tests__/pty/screen-006-no-color.ptytest.ts +4 -4
  39. package/src/__tests__/pty/screen-010-scrollback.ptytest.ts +3 -3
  40. package/src/__tests__/pty/screen-1993-scrollback.ptytest.ts +2 -2
  41. package/src/__tests__/pty/screen-2002-themes.ptytest.ts +3 -3
  42. package/src/__tests__/pty/screen-2670-prepark.ptytest.ts +3 -3
  43. package/src/__tests__/pty/screen-reader-mode.ptytest.ts +16 -16
  44. package/src/__tests__/pty/session-attach.ptytest.ts +66 -28
  45. package/src/__tests__/pty/settings-screen.ptytest.ts +2 -2
  46. package/src/__tests__/pty/spawn-pty.ts +3 -3
  47. package/src/__tests__/pty/terminal-handoff.ptytest.ts +2 -2
  48. package/src/__tests__/pty/trust-prompt.ptytest.ts +5 -5
  49. package/src/__tests__/pty/tui-pty.ptytest.ts +2 -2
  50. package/src/__tests__/screen-reader-labels.test.ts +1 -1
  51. package/src/__tests__/screen-reader-pacing.test.ts +3 -3
  52. package/src/__tests__/screen-reader-render-options.test.ts +3 -3
  53. package/src/__tests__/session-switch-channel.test.tsx +1 -1
  54. package/src/__tests__/terminal-capabilities.test.ts +9 -9
  55. package/src/__tests__/terminal-handoff-pty-e2e.test.ts +1 -1
  56. package/src/__tests__/tui-channel-init-failure.test.ts +1 -1
  57. package/src/__tests__/tui-state-manager.test.ts +2 -2
  58. package/src/__tests__/wire-tui-channel.test.ts +2 -2
  59. package/src/keybindings/__tests__/keybinding-registry.test.ts +2 -2
  60. package/src/keybindings/__tests__/node-keybindings-source.test.ts +8 -1
  61. package/src/keybindings/node-keybindings-source.ts +6 -6
  62. package/src/render.tsx +7 -1
  63. package/src/terminal-capabilities.ts +1 -1
  64. package/src/tui-channel-options.ts +2 -0
  65. package/src/tui-session-options.ts +4 -0
  66. package/src/utils/__tests__/tool-call-extractor.test.ts +1 -1
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * SCREEN-010 TC-05: chat-window scrollback layout, on the real binary.
3
3
  *
4
- * Runs in the PTY vitest project against the BUILT robota CLI. Automates the former "manual" smoke
4
+ * Runs in the PTY vitest project against the BUILT CLI. Automates the former "manual" smoke
5
5
  * (per the never-ask-the-user-to-test rule): generates committed output larger than a small viewport
6
6
  * and asserts the committed history is emitted to the terminal's native scrollback while the input
7
7
  * prompt + status bar stay pinned at the bottom. Once committed content is in scrollback and the input
@@ -47,7 +47,7 @@ describe('SCREEN-010 chat-window scrollback layout (real binary)', () => {
47
47
  let session: IPtySession | undefined;
48
48
 
49
49
  beforeEach(() => {
50
- projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-scrollback-pty-')));
50
+ projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-scrollback-pty-')));
51
51
  writeTuiProviderSettings(projectDir);
52
52
  });
53
53
 
@@ -97,7 +97,7 @@ describe('SCREEN-010 chat-window scrollback layout (real binary)', () => {
97
97
  homeDir: join(projectDir, 'home'),
98
98
  rows: 16,
99
99
  args: ['--screen-reader'],
100
- env: { ROBOTA_SCREEN_READER_STARTUP_QUIET_MS: '0' },
100
+ env: { PRODUCT_SCREEN_READER_STARTUP_QUIET_MS: '0' },
101
101
  });
102
102
 
103
103
  await session.waitFor(/Type a message or \/help/, PROMPT_WAIT_MS);
@@ -56,7 +56,7 @@ function seedSession(homeDir: string, cwd: string): void {
56
56
  });
57
57
  }
58
58
  }
59
- const sessionsDir = join(homeDir, '.robota', 'sessions');
59
+ const sessionsDir = join(homeDir, 'state', 'sessions');
60
60
  mkdirSync(sessionsDir, { recursive: true });
61
61
  writeFileSync(
62
62
  join(sessionsDir, `${SESSION_ID}.json`),
@@ -81,7 +81,7 @@ describe('SCREEN-1993 TC-10: the resumed transcript lives in native scrollback',
81
81
  let session: IPtySession | undefined;
82
82
 
83
83
  beforeEach(() => {
84
- projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-1993-scrollback-')));
84
+ projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-1993-scrollback-')));
85
85
  writeTuiProviderSettings(projectDir);
86
86
  seedSession(join(projectDir, 'home'), projectDir);
87
87
  });
@@ -50,7 +50,7 @@ function snippetLine(raw: string): string {
50
50
  }
51
51
 
52
52
  function seedThemeFiles(homeDir: string): void {
53
- const themes = join(homeDir, '.robota', 'themes');
53
+ const themes = join(homeDir, 'state', 'themes');
54
54
  mkdirSync(themes, { recursive: true });
55
55
  writeFileSync(
56
56
  join(themes, 'mine.json'),
@@ -76,7 +76,7 @@ function seedThemeFiles(homeDir: string): void {
76
76
 
77
77
  const pluginVersionDir = join(
78
78
  homeDir,
79
- '.robota',
79
+ 'state',
80
80
  'plugins',
81
81
  'cache',
82
82
  'fixtures',
@@ -106,7 +106,7 @@ describe('SCREEN-2002 themes through the real binary', () => {
106
106
  let session: IPtySession | undefined;
107
107
 
108
108
  beforeEach(() => {
109
- projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-screen2002-pty-')));
109
+ projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-screen2002-pty-')));
110
110
  writeTuiProviderSettings(projectDir);
111
111
  homeDir = join(projectDir, 'home');
112
112
  seedThemeFiles(homeDir);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SCREEN-2670 TC-09 — the pre-write park on the BUILT robota binary, in a real PTY.
2
+ * SCREEN-2670 TC-09 — the pre-write park on the BUILT CLI binary, in a real PTY.
3
3
  *
4
4
  * A screen reader sees bytes and time, nothing else, so the claim is checked where both exist: the
5
5
  * raw PTY stream is sampled every few milliseconds and each growth is stamped. A COMMIT the park
@@ -157,7 +157,7 @@ function spawnScenario(projectDir: string, mode: 'park' | 'zero' | 'off'): IPtyS
157
157
  projectDir,
158
158
  homeDir: join(projectDir, 'home'),
159
159
  args: ['--session-log', REPLAY_FIXTURE, ...screenReader, '--name', 'prepark-scenario'],
160
- env: { ROBOTA_SCREEN_READER_STARTUP_QUIET_MS: '0', ROBOTA_SCREEN_READER_PREPARK_MS: prepark },
160
+ env: { PRODUCT_SCREEN_READER_STARTUP_QUIET_MS: '0', PRODUCT_SCREEN_READER_PREPARK_MS: prepark },
161
161
  });
162
162
  }
163
163
 
@@ -171,7 +171,7 @@ describe('SCREEN-2670 the pre-write park through the real binary', () => {
171
171
  let session: IPtySession | undefined;
172
172
 
173
173
  beforeEach(() => {
174
- projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-prepark-pty-')));
174
+ projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-prepark-pty-')));
175
175
  writeTuiProviderSettings(projectDir);
176
176
  });
177
177
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * CLI-2004 TC-11 / TC-12 — screen-reader mode on the BUILT robota binary, in a real PTY.
2
+ * CLI-2004 TC-11 / TC-12 — screen-reader mode on the BUILT CLI binary, in a real PTY.
3
3
  *
4
4
  * This is the only level at which the claim can be checked. A screen reader has no `aria-live`: the
5
5
  * ONLY channel to it is which bytes land in the terminal buffer, so a component test asserting on a
@@ -42,7 +42,7 @@ const CASE_TIMEOUT_MS = 90_000;
42
42
  * is the case where "the first line confirms the mode" is the claim being made.
43
43
  */
44
44
  function markOnboarded(homeDir: string): void {
45
- writeFileSync(join(homeDir, '.robota', 'onboarded'), new Date().toISOString());
45
+ writeFileSync(join(homeDir, 'state', 'onboarded'), new Date().toISOString());
46
46
  }
47
47
 
48
48
  describe('CLI-2004 screen-reader mode through the real binary', () => {
@@ -50,7 +50,7 @@ describe('CLI-2004 screen-reader mode through the real binary', () => {
50
50
  let session: IPtySession | undefined;
51
51
 
52
52
  beforeEach(() => {
53
- projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-screenreader-pty-')));
53
+ projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-screenreader-pty-')));
54
54
  writeTuiProviderSettings(projectDir);
55
55
  });
56
56
 
@@ -68,7 +68,7 @@ describe('CLI-2004 screen-reader mode through the real binary', () => {
68
68
  homeDir: join(projectDir, 'home'),
69
69
  args: ['--session-log', REPLAY_FIXTURE, '--screen-reader'],
70
70
  // The waits are exercised by their own unit test; a real one here only slows the fixture.
71
- env: { ROBOTA_SCREEN_READER_STARTUP_QUIET_MS: '0' },
71
+ env: { PRODUCT_SCREEN_READER_STARTUP_QUIET_MS: '0' },
72
72
  });
73
73
 
74
74
  await session.waitFor(/Type a message or \/help/, WAIT_MS);
@@ -100,7 +100,7 @@ describe('CLI-2004 the same fixture with the mode off', () => {
100
100
  let session: IPtySession | undefined;
101
101
 
102
102
  beforeEach(() => {
103
- projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-screenreader-pty-')));
103
+ projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-screenreader-pty-')));
104
104
  writeTuiProviderSettings(projectDir);
105
105
  });
106
106
 
@@ -117,7 +117,7 @@ describe('CLI-2004 the same fixture with the mode off', () => {
117
117
  projectDir,
118
118
  homeDir: join(projectDir, 'home'),
119
119
  args: ['--session-log', REPLAY_FIXTURE],
120
- env: { ROBOTA_TURN_MARKS: '1' },
120
+ env: { PRODUCT_TURN_MARKS: '1' },
121
121
  });
122
122
 
123
123
  await session.waitFor(/Type a message or \/help/, WAIT_MS);
@@ -136,12 +136,12 @@ describe('CLI-2004 the same fixture with the mode off', () => {
136
136
  );
137
137
  });
138
138
 
139
- describe('Robota turn-mark override through the real binary', () => {
139
+ describe('configured turn-mark override through the real binary', () => {
140
140
  let projectDir: string;
141
141
  let session: IPtySession | undefined;
142
142
 
143
143
  beforeEach(() => {
144
- projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-turn-marks-pty-')));
144
+ projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-turn-marks-pty-')));
145
145
  writeTuiProviderSettings(projectDir);
146
146
  });
147
147
 
@@ -152,8 +152,8 @@ describe('Robota turn-mark override through the real binary', () => {
152
152
 
153
153
  for (const [label, env, expected] of [
154
154
  ['WezTerm default', { TERM_PROGRAM: 'WezTerm' }, false],
155
- ['WezTerm opt-in', { TERM_PROGRAM: 'WezTerm', ROBOTA_TURN_MARKS: '1' }, true],
156
- ['TTY kill switch', { ROBOTA_TURN_MARKS: '0' }, false],
155
+ ['WezTerm opt-in', { TERM_PROGRAM: 'WezTerm', PRODUCT_TURN_MARKS: '1' }, true],
156
+ ['TTY kill switch', { PRODUCT_TURN_MARKS: '0' }, false],
157
157
  ] as const) {
158
158
  it(
159
159
  `${label} ${expected ? 'emits' : 'withholds'} OSC 133`,
@@ -162,7 +162,7 @@ describe('Robota turn-mark override through the real binary', () => {
162
162
  projectDir,
163
163
  homeDir: join(projectDir, 'home'),
164
164
  args: ['--screen-reader'],
165
- env: { ...env, ROBOTA_SCREEN_READER_STARTUP_QUIET_MS: '0' },
165
+ env: { ...env, PRODUCT_SCREEN_READER_STARTUP_QUIET_MS: '0' },
166
166
  });
167
167
  await session.waitFor(/Type a message or \/help/, WAIT_MS);
168
168
  expect(session.raw().includes(OSC_133_PROMPT_START)).toBe(expected);
@@ -177,7 +177,7 @@ describe('CLI-2004 the channel the mode was enabled through', () => {
177
177
  let session: IPtySession | undefined;
178
178
 
179
179
  beforeEach(() => {
180
- projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-screenreader-pty-')));
180
+ projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-screenreader-pty-')));
181
181
  writeTuiProviderSettings(projectDir);
182
182
  });
183
183
 
@@ -188,13 +188,13 @@ describe('CLI-2004 the channel the mode was enabled through', () => {
188
188
  });
189
189
 
190
190
  it(
191
- 'TC-12: ROBOTA_SCREEN_READER=1 with no flag announces the env channel first',
191
+ 'TC-12: PRODUCT_SCREEN_READER=1 with no flag announces the env channel first',
192
192
  async () => {
193
193
  markOnboarded(join(projectDir, 'home'));
194
194
  session = spawnTui({
195
195
  projectDir,
196
196
  homeDir: join(projectDir, 'home'),
197
- env: { ROBOTA_SCREEN_READER: '1', ROBOTA_SCREEN_READER_STARTUP_QUIET_MS: '0' },
197
+ env: { PRODUCT_SCREEN_READER: '1', PRODUCT_SCREEN_READER_STARTUP_QUIET_MS: '0' },
198
198
  });
199
199
 
200
200
  await session.waitFor(/Type a message or \/help/, WAIT_MS);
@@ -207,14 +207,14 @@ describe('CLI-2004 the channel the mode was enabled through', () => {
207
207
  );
208
208
 
209
209
  it(
210
- 'TC-12: --screen-reader beats ROBOTA_SCREEN_READER=0 and says so',
210
+ 'TC-12: --screen-reader beats PRODUCT_SCREEN_READER=0 and says so',
211
211
  async () => {
212
212
  markOnboarded(join(projectDir, 'home'));
213
213
  session = spawnTui({
214
214
  projectDir,
215
215
  homeDir: join(projectDir, 'home'),
216
216
  args: ['--screen-reader'],
217
- env: { ROBOTA_SCREEN_READER: '0', ROBOTA_SCREEN_READER_STARTUP_QUIET_MS: '0' },
217
+ env: { PRODUCT_SCREEN_READER: '0', PRODUCT_SCREEN_READER_STARTUP_QUIET_MS: '0' },
218
218
  });
219
219
 
220
220
  await session.waitFor(/Type a message or \/help/, WAIT_MS);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `robota session attach` end to end on the BUILT binary, in a real terminal: start a supervised
2
+ * `the CLI session attach command` end to end on the BUILT binary, in a real terminal: start a supervised
3
3
  * session, attach and confirm on the controlling terminal, submit, detach with `/exit`, see it still
4
4
  * running, attach again read-only and find the earlier prompt, detach with Ctrl-C, peek from
5
5
  * `session view` and land back in the view, then stop it.
@@ -24,7 +24,7 @@ import { spawnPty } from './spawn-pty.js';
24
24
  import type { IPtyRunSession } from './spawn-pty.js';
25
25
 
26
26
  const REPO_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '../../../../..');
27
- const ROBOTA_BIN = join(REPO_ROOT, 'packages/agent-cli/bin/robota.cjs');
27
+ const AGENT_BIN = join(REPO_ROOT, 'packages/agent-cli/bin/agent.cjs');
28
28
  const WAIT_MS = 20_000;
29
29
 
30
30
  const cleanups: (() => void)[] = [];
@@ -32,15 +32,24 @@ afterEach(() => {
32
32
  for (const cleanup of cleanups.splice(0).reverse()) cleanup();
33
33
  });
34
34
 
35
- function robota(args: readonly string[], cwd: string, env: NodeJS.ProcessEnv): { status: number | null; stdout: string; stderr: string } {
36
- const result = spawnSync(process.execPath, [ROBOTA_BIN, ...args], { cwd, env, encoding: 'utf8', timeout: WAIT_MS });
35
+ function agent(
36
+ args: readonly string[],
37
+ cwd: string,
38
+ env: NodeJS.ProcessEnv,
39
+ ): { status: number | null; stdout: string; stderr: string } {
40
+ const result = spawnSync(process.execPath, [AGENT_BIN, ...args], {
41
+ cwd,
42
+ env,
43
+ encoding: 'utf8',
44
+ timeout: WAIT_MS,
45
+ });
37
46
  return { status: result.status, stdout: result.stdout, stderr: result.stderr };
38
47
  }
39
48
 
40
49
  function attach(id: string, cwd: string, env: NodeJS.ProcessEnv, observe = false): IPtyRunSession {
41
50
  const session = spawnPty({
42
51
  command: process.execPath,
43
- args: [ROBOTA_BIN, 'session', 'attach', id, ...(observe ? ['--observe'] : [])],
52
+ args: [AGENT_BIN, 'session', 'attach', id, ...(observe ? ['--observe'] : [])],
44
53
  cwd,
45
54
  env,
46
55
  });
@@ -48,18 +57,19 @@ function attach(id: string, cwd: string, env: NodeJS.ProcessEnv, observe = false
48
57
  return session;
49
58
  }
50
59
 
51
- describe('robota session attach on the built binary', () => {
60
+ describe('the CLI session attach command on the built binary', () => {
52
61
  it('attaches, submits, detaches without stopping the session, and re-attaches', async () => {
53
- const project = realpathSync(mkdtempSync(join(tmpdir(), 'robota-attach-pty-')));
62
+ const project = realpathSync(mkdtempSync(join(tmpdir(), 'agent-attach-pty-')));
54
63
  // `/tmp` rather than the platform temp directory: the control socket path must stay short.
55
64
  const runtime = mkdtempSync('/tmp/rsa-');
56
65
  cleanups.push(() => rmSync(project, { recursive: true, force: true }));
57
66
  cleanups.push(() => rmSync(runtime, { recursive: true, force: true }));
58
67
  const home = createIsolatedHome();
59
- mkdirSync(join(home, '.robota'), { recursive: true });
60
- writeFileSync(join(home, '.robota', 'onboarded'), new Date().toISOString(), 'utf8');
68
+ const stateRoot = join(runtime, 's');
69
+ mkdirSync(stateRoot, { recursive: true });
70
+ writeFileSync(join(stateRoot, 'onboarded'), new Date().toISOString(), 'utf8');
61
71
  writeFileSync(
62
- join(home, '.robota', 'settings.json'),
72
+ join(stateRoot, 'settings.json'),
63
73
  JSON.stringify({
64
74
  currentProvider: 'anthropic',
65
75
  providers: {
@@ -73,65 +83,93 @@ describe('robota session attach on the built binary', () => {
73
83
  }),
74
84
  'utf8',
75
85
  );
76
- const env = createPtyEnv({ HOME: home, XDG_RUNTIME_DIR: runtime });
86
+ const env = createPtyEnv({
87
+ HOME: home,
88
+ XDG_RUNTIME_DIR: runtime,
89
+ PRODUCT_USER_STATE_DIR: stateRoot,
90
+ PRODUCT_CACHE_DIR: join(runtime, 'c'),
91
+ PRODUCT_LOG_DIR: join(runtime, 'l'),
92
+ });
77
93
 
78
94
  // Workspace trust is recorded for a repository, as a user would grant it before a background start.
79
95
  const git = (...args: string[]): void => {
80
96
  execFileSync('git', args, { cwd: project, env, stdio: ['ignore', 'ignore', 'pipe'] });
81
97
  };
82
98
  git('init', '-q', '-b', 'main');
83
- git('config', 'user.name', 'Robota Attach');
99
+ git('config', 'user.name', 'Attach Fixture');
84
100
  git('config', 'user.email', 'attach@example.invalid');
85
101
  writeFileSync(join(project, 'README.md'), 'fixture\n');
86
102
  git('add', '--', 'README.md');
87
103
  git('commit', '-q', '-m', 'chore: fixture');
88
- const trusted = robota(['trust', '--yes'], project, env);
104
+ const trusted = agent(['trust', '--yes'], project, env);
89
105
  expect(trusted.status, trusted.stderr).toBe(0);
90
- const started = robota(['session', 'start', '--background', '--name', 'pty-attach'], project, env);
106
+ const started = agent(
107
+ ['session', 'start', '--background', '--name', 'pty-attach'],
108
+ project,
109
+ env,
110
+ );
91
111
  expect(started.status, started.stderr).toBe(0);
92
112
  const id = /Supervised session: ([0-9a-f-]{36})/u.exec(started.stdout)?.[1];
93
113
  expect(id).toBeDefined();
94
- cleanups.push(() => { robota(['session', 'stop', id!], project, env); });
114
+ cleanups.push(() => {
115
+ agent(['session', 'stop', id!], project, env);
116
+ });
95
117
 
96
118
  const drive = attach(id!, project, env);
97
119
  await drive.waitFor('Attach? Type yes', WAIT_MS);
98
120
  expect(drive.snapshot()).toContain('drive (send prompts, answer its questions)');
99
121
  await drive.sendKeys('yes');
100
122
  await drive.pressEnter();
101
- await drive.waitFor('Attached to pty-attach — drive', WAIT_MS);
123
+ await drive.waitFor('Type a message or /help', WAIT_MS);
102
124
  await drive.sendKeys('hello from the pty');
103
125
  await drive.pressEnter();
104
- await drive.waitFor('Error:', WAIT_MS);
126
+ await drive.waitFor('Last turn failed', WAIT_MS);
105
127
  await drive.sendKeys('/exit');
106
128
  await drive.pressEnter();
107
- await drive.waitFor('keeps running', WAIT_MS);
108
129
  expect(await drive.expectExit(WAIT_MS)).toBe(0);
109
130
 
110
- const listed = robota(['session', 'list', '--format', 'json'], project, env);
111
- const supervised = (JSON.parse(listed.stdout) as {
112
- supervised: { sessions: { id: string; liveness: string; control: string }[] };
113
- }).supervised.sessions;
114
- expect(supervised).toContainEqual(expect.objectContaining({ id, liveness: 'alive', control: 'available' }));
131
+ const listed = agent(['session', 'list', '--format', 'json'], project, env);
132
+ const supervised = (
133
+ JSON.parse(listed.stdout) as {
134
+ supervised: { sessions: { id: string; liveness: string; control: string }[] };
135
+ }
136
+ ).supervised.sessions;
137
+ expect(supervised).toContainEqual(
138
+ expect.objectContaining({ id, liveness: 'alive', control: 'available' }),
139
+ );
115
140
 
116
141
  const observe = attach(id!, project, env, true);
117
142
  await observe.waitFor('Attach? Type yes', WAIT_MS);
118
143
  expect(observe.snapshot()).toContain('observe (read only)');
119
144
  await observe.sendKeys('yes');
120
145
  await observe.pressEnter();
121
- await observe.waitFor('Observing pty-attach — read only', WAIT_MS);
146
+ await observe.waitFor('Observing — read only', WAIT_MS);
122
147
  await observe.waitFor('hello from the pty', WAIT_MS);
123
148
  observe.write('\x03');
124
- await observe.waitFor('keeps running', WAIT_MS);
125
149
  expect(await observe.expectExit(WAIT_MS)).toBe(0);
150
+ const afterObserve = agent(['session', 'list', '--format', 'json'], project, env);
151
+ const remaining = (
152
+ JSON.parse(afterObserve.stdout) as {
153
+ supervised: { sessions: { id: string; liveness: string; control: string }[] };
154
+ }
155
+ ).supervised.sessions;
156
+ expect(remaining).toContainEqual(
157
+ expect.objectContaining({ id, liveness: 'alive', control: 'available' }),
158
+ );
126
159
 
127
160
  // From the session view: peek at the row, confirm there, detach, and land back in the view.
128
- const view = spawnPty({ command: process.execPath, args: [ROBOTA_BIN, 'session', 'view'], cwd: project, env });
161
+ const view = spawnPty({
162
+ command: process.execPath,
163
+ args: [AGENT_BIN, 'session', 'view'],
164
+ cwd: project,
165
+ env,
166
+ });
129
167
  cleanups.push(() => view.dispose());
130
168
  await view.waitFor('a Attach p Peek', WAIT_MS);
131
169
  view.write('p');
132
170
  await view.waitFor('to observe (read only)?', WAIT_MS);
133
171
  view.write('y');
134
- await view.waitFor('Observing pty-attach — read only', WAIT_MS);
172
+ await view.waitFor('Observing — read only', WAIT_MS);
135
173
  await view.waitFor('hello from the pty', WAIT_MS);
136
174
  const beforeDetach = view.outputOffset();
137
175
  view.write('\x1d');
@@ -139,6 +177,6 @@ describe('robota session attach on the built binary', () => {
139
177
  view.write('q');
140
178
  expect(await view.expectExit(WAIT_MS)).toBe(0);
141
179
 
142
- expect(robota(['session', 'stop', id!], project, env).status).toBe(0);
180
+ expect(agent(['session', 'stop', id!], project, env).status).toBe(0);
143
181
  }, 120_000);
144
182
  });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * CMD-004 Phase 2 Stage C (TC-04): `/settings` still opens the settings screen — now rendered from
3
3
  * the requester-routed `ui_intent` session event (the legacy `settings-tui-requested` effect
4
- * branch is deleted from the TUI). Observed through a real PTY against the BUILT robota binary
4
+ * branch is deleted from the TUI). Observed through a real PTY against the BUILT CLI binary
5
5
  * (`pnpm --filter @robota-sdk/agent-cli build`).
6
6
  */
7
7
 
@@ -20,7 +20,7 @@ describe('/settings opens the settings screen via ui_intent (CMD-004 Stage C)',
20
20
  let session: IPtySession | undefined;
21
21
 
22
22
  beforeEach(() => {
23
- projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-settings-pty-')));
23
+ projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-settings-pty-')));
24
24
  writeTuiProviderSettings(projectDir);
25
25
  });
26
26
 
@@ -6,7 +6,7 @@
6
6
  * in a user terminal) and exposes per-key paced input plus marker/exit waiting. Two convenience
7
7
  * spawners build on the core:
8
8
  *
9
- * - `spawnPty` — drive any command (e.g. the built robota CLI binary).
9
+ * - `spawnPty` — drive any command (e.g. the built CLI binary).
10
10
  * - `spawnPtyFixture` — drive a TSX fixture through the `tsx/esm` import hook (no build step),
11
11
  * the pattern used for focused source-level E2E.
12
12
  *
@@ -96,8 +96,8 @@ export function spawnPty(options: IPtyRunOptions): IPtyRunSession {
96
96
  rows: options.rows ?? 32,
97
97
  cwd: options.cwd,
98
98
  // HARNESS-025: the default HOME is a throwaway directory, never the developer's real one — a
99
- // PTY child that reads `~/.robota` (or any user rc) must not make the suite machine-dependent.
100
- env: options.env ?? createPtyEnv(),
99
+ // Configured state and HOME are both isolated so the run cannot depend on the developer profile.
100
+ env: createPtyEnv(options.env ?? {}),
101
101
  });
102
102
 
103
103
  pty.onData((data) => {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * TERM-003 / TEST-007: real-binary terminal-handoff evidence.
3
3
  *
4
- * Runs in the dedicated PTY vitest project (vitest.pty.config.ts) against the BUILT robota binary
4
+ * Runs in the dedicated PTY vitest project (vitest.pty.config.ts) against the BUILT CLI binary
5
5
  * (`pnpm --filter @robota-sdk/agent-cli build` first). Unlike the source-level fixture E2E
6
6
  * (`terminal-handoff-pty-e2e` / `command-handoff-pty-e2e`), this drives the WHOLE user path —
7
7
  * real CLI → real command pipeline → real `TerminalHandoffController` injected by `render.tsx` →
@@ -25,7 +25,7 @@ describe('terminal handoff through the real binary (TERM-003)', () => {
25
25
  let session: IPtySession | undefined;
26
26
 
27
27
  beforeEach(() => {
28
- projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-handoff-pty-')));
28
+ projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-handoff-pty-')));
29
29
  writeTuiProviderSettings(projectDir);
30
30
  });
31
31
 
@@ -37,14 +37,14 @@ interface IFixture {
37
37
  }
38
38
 
39
39
  function makeFixture(): IFixture {
40
- const root = realpathSync(mkdtempSync(join(tmpdir(), 'robota-trust-prompt-')));
40
+ const root = realpathSync(mkdtempSync(join(tmpdir(), 'agent-trust-prompt-')));
41
41
  const home = join(root, 'home');
42
42
  const repo = join(root, 'repo');
43
- mkdirSync(join(home, '.robota'), { recursive: true });
43
+ mkdirSync(join(home, 'state'), { recursive: true });
44
44
  // Skip the first-run welcome and provider setup: neither is what this scenario observes.
45
- writeFileSync(join(home, '.robota', 'onboarded'), new Date().toISOString(), 'utf8');
45
+ writeFileSync(join(home, 'state', 'onboarded'), new Date().toISOString(), 'utf8');
46
46
  writeFileSync(
47
- join(home, '.robota', 'settings.json'),
47
+ join(home, 'state', 'settings.json'),
48
48
  JSON.stringify({
49
49
  currentProvider: 'anthropic',
50
50
  providers: {
@@ -58,7 +58,7 @@ function makeFixture(): IFixture {
58
58
  }
59
59
 
60
60
  function trustedGrants(fixture: IFixture): number {
61
- const store = join(fixture.home, '.robota', 'workspace-trust.json');
61
+ const store = join(fixture.home, 'state', 'workspace-trust.json');
62
62
  if (!existsSync(store)) return 0;
63
63
  const parsed = JSON.parse(readFileSync(store, 'utf8')) as { grants: { state: string }[] };
64
64
  return parsed.grants.filter((grant) => grant.state === 'trusted').length;
@@ -2,7 +2,7 @@
2
2
  * Real-PTY TUI suites (CLI-074 TC-07/08).
3
3
  *
4
4
  * Runs in the dedicated PTY vitest project (vitest.pty.config.ts) against the
5
- * BUILT robota binary — `pnpm --filter @robota-sdk/agent-cli build` first.
5
+ * BUILT CLI binary — `pnpm --filter @robota-sdk/agent-cli build` first.
6
6
  */
7
7
 
8
8
  import { mkdtempSync, rmSync, realpathSync } from 'node:fs';
@@ -20,7 +20,7 @@ describe('TUI through a real PTY (CLI-074)', () => {
20
20
  let session: IPtySession | undefined;
21
21
 
22
22
  beforeEach(() => {
23
- projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-pty-')));
23
+ projectDir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-pty-')));
24
24
  writeTuiProviderSettings(projectDir);
25
25
  });
26
26
 
@@ -21,7 +21,7 @@ import {
21
21
  import type { TUniversalMessage } from '@robota-sdk/agent-core';
22
22
 
23
23
  /** Every provider package this repo ships, plus the vendor names behind them. */
24
- const VENDOR_WORDS = /(claude|anthropic|openai|gpt|gemini|google|robota|llama|mistral|copilot)/i;
24
+ const VENDOR_WORDS = /(claude|anthropic|openai|gpt|gemini|google|llama|mistral|copilot)/i;
25
25
 
26
26
  function renderLabel(role: TUniversalMessage['role'], enabled: boolean, driverId?: string): string {
27
27
  const label = React.createElement(
@@ -39,9 +39,9 @@ function collectWarnings(): { notes: string[]; warn: (message: string) => void }
39
39
  }
40
40
 
41
41
  describe('TC-20: resolvePacing', () => {
42
- it('ignores ambient Robota pacing variables without a host choice', () => {
43
- vi.stubEnv('ROBOTA_SCREEN_READER_STARTUP_QUIET_MS', '0');
44
- vi.stubEnv('ROBOTA_SCREEN_READER_PREPARK_MS', '0');
42
+ it('ignores ambient Fixture Agent pacing variables without a host choice', () => {
43
+ vi.stubEnv('PRODUCT_SCREEN_READER_STARTUP_QUIET_MS', '0');
44
+ vi.stubEnv('PRODUCT_SCREEN_READER_PREPARK_MS', '0');
45
45
  try {
46
46
  expect(resolvePacing({ enabled: true })).toEqual({
47
47
  startupQuietMs: DEFAULT_STARTUP_QUIET_MS,
@@ -44,7 +44,7 @@ vi.mock('ink', async (importOriginal) => {
44
44
 
45
45
  // The App boots a channel and starts I/O; the mode's threading is what is under test, not the App.
46
46
  vi.mock('../App.js', () => ({
47
- default: (): React.ReactElement => React.createElement('robota-app'),
47
+ default: (): React.ReactElement => React.createElement('agent-test-app'),
48
48
  }));
49
49
 
50
50
  function baseOptions(): IRenderOptions {
@@ -231,8 +231,8 @@ describe('SCREEN-2670 TC-05: with the mode off, render() is called with no stdou
231
231
  expect(Object.keys(inkRender.mock.calls[0]?.[1] ?? {})).not.toContain('stdout');
232
232
  });
233
233
 
234
- it('ignores an ambient Robota park override when the host supplies no park choice', async () => {
235
- vi.stubEnv('ROBOTA_SCREEN_READER_PREPARK_MS', '0');
234
+ it('ignores an ambient Fixture Agent park override when the host supplies no park choice', async () => {
235
+ vi.stubEnv('PRODUCT_SCREEN_READER_PREPARK_MS', '0');
236
236
  const { renderApp } = await import('../render.js');
237
237
  await renderApp({
238
238
  ...baseOptions(),
@@ -220,7 +220,7 @@ describe('App session-switch channel ownership (CLI-B11)', () => {
220
220
  let initialStartIntent: boolean;
221
221
 
222
222
  beforeEach(() => {
223
- cwd = mkdtempSync(join(tmpdir(), 'robota-b11-'));
223
+ cwd = mkdtempSync(join(tmpdir(), 'agent-b11-'));
224
224
  created = [];
225
225
  initialStartError = undefined;
226
226
  initialStartIntent = false;
@@ -58,7 +58,7 @@ describe('supportsImeCursorPositioning (CLI-062)', () => {
58
58
  });
59
59
 
60
60
  it('on for a plain interactive TTY, off for a non-TTY', () => {
61
- vi.stubEnv('ROBOTA_IME_CURSOR', undefined);
61
+ vi.stubEnv('PRODUCT_IME_CURSOR', undefined);
62
62
  vi.stubEnv('TERM_PROGRAM', undefined);
63
63
  setTty(true);
64
64
  expect(supportsImeCursorPositioning()).toBe(true);
@@ -67,30 +67,30 @@ describe('supportsImeCursorPositioning (CLI-062)', () => {
67
67
  });
68
68
 
69
69
  it('I5: Apple_Terminal is OFF by default (Korean-IME SIGSEGV is an Apple-side bug)', () => {
70
- vi.stubEnv('ROBOTA_IME_CURSOR', undefined);
70
+ vi.stubEnv('PRODUCT_IME_CURSOR', undefined);
71
71
  setTty(true);
72
72
  vi.stubEnv('TERM_PROGRAM', 'Apple_Terminal');
73
73
  expect(supportsImeCursorPositioning()).toBe(false);
74
74
  });
75
75
 
76
- it('I5: ROBOTA_IME_CURSOR=1 opts Apple_Terminal in explicitly', () => {
76
+ it('I5: PRODUCT_IME_CURSOR=1 opts Apple_Terminal in explicitly', () => {
77
77
  setTty(true);
78
78
  vi.stubEnv('TERM_PROGRAM', 'Apple_Terminal');
79
- vi.stubEnv('ROBOTA_IME_CURSOR', '1');
79
+ vi.stubEnv('PRODUCT_IME_CURSOR', '1');
80
80
  expect(supportsImeCursorPositioning({ override: true })).toBe(true);
81
81
  });
82
82
 
83
- it('ignores an ambient Robota override without a host choice', () => {
83
+ it('ignores an ambient Fixture Agent override without a host choice', () => {
84
84
  setTty(true);
85
85
  vi.stubEnv('TERM_PROGRAM', 'Apple_Terminal');
86
- vi.stubEnv('ROBOTA_IME_CURSOR', '1');
86
+ vi.stubEnv('PRODUCT_IME_CURSOR', '1');
87
87
  expect(supportsImeCursorPositioning()).toBe(false);
88
88
  });
89
89
 
90
- it('ROBOTA_IME_CURSOR=0 is a kill switch even on a capable terminal', () => {
90
+ it('PRODUCT_IME_CURSOR=0 is a kill switch even on a capable terminal', () => {
91
91
  setTty(true);
92
92
  vi.stubEnv('TERM_PROGRAM', undefined);
93
- vi.stubEnv('ROBOTA_IME_CURSOR', '0');
93
+ vi.stubEnv('PRODUCT_IME_CURSOR', '0');
94
94
  expect(supportsImeCursorPositioning({ override: false })).toBe(false);
95
95
  });
96
96
  });
@@ -122,7 +122,7 @@ describe('CLI-062 terminal matrix — supportsImeCursorPositioning per terminal'
122
122
  vi.stubEnv(key, undefined);
123
123
  }
124
124
  for (const [key, value] of Object.entries(profile.env)) vi.stubEnv(key, value);
125
- vi.stubEnv('ROBOTA_IME_CURSOR', override);
125
+ vi.stubEnv('PRODUCT_IME_CURSOR', override);
126
126
 
127
127
  expect(
128
128
  supportsImeCursorPositioning({
@@ -39,7 +39,7 @@ describe('terminal handoff PTY E2E', () => {
39
39
  it(
40
40
  'hands the real terminal to a child, receives its input, and resumes the TUI',
41
41
  async () => {
42
- const dir = realpathSync(mkdtempSync(join(tmpdir(), 'robota-handoff-pty-')));
42
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), 'agent-test-handoff-pty-')));
43
43
  tempDirs.push(dir);
44
44
  const outputPath = join(dir, 'result.json');
45
45