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

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 +68 -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
@@ -37,7 +37,7 @@ vi.mock('@robota-sdk/agent-framework', async (importOriginal) => {
37
37
  import { TuiInteractionChannel } from '../TuiInteractionChannel.js';
38
38
 
39
39
  /** The session's working directory: private to this run, never a fixed name under /tmp. */
40
- const PROJECT_DIR = mkdtempSync(join(tmpdir(), 'robota-tui-init-failure-'));
40
+ const PROJECT_DIR = mkdtempSync(join(tmpdir(), 'agent-test-tui-init-failure-'));
41
41
  afterAll(() => rmSync(PROJECT_DIR, { recursive: true, force: true }));
42
42
 
43
43
  describe('TuiInteractionChannel init failure surfacing', () => {
@@ -346,7 +346,7 @@ describe('TuiStateManager', () => {
346
346
  expect(mgr.executionWorkspaceSnapshot?.selectedEntryId).toBe('task:agent_1');
347
347
  });
348
348
 
349
- // ── Display order: Tool → Robota ──────────────────────────────
349
+ // ── Display order: Tool → Fixture Agent ──────────────────────────────
350
350
 
351
351
  it('streaming state is cleared on complete (tools moved to messages)', () => {
352
352
  const mgr = new TuiStateManager();
@@ -432,7 +432,7 @@ describe('TuiStateManager', () => {
432
432
  expect(mgr.history[2]!.type).toBe('assistant');
433
433
  });
434
434
 
435
- it('after abort: streaming cleared, messages synced with tool → robota → system', () => {
435
+ it('after abort: streaming cleared, messages synced with tool → agent → system', () => {
436
436
  const mgr = new TuiStateManager();
437
437
 
438
438
  mgr.onThinking(true);
@@ -327,13 +327,13 @@ describe('WireTuiChannel', () => {
327
327
  });
328
328
  const pending = channel.getSnapshot().pendingUserAction;
329
329
  expect(pending).toMatchObject({ title: 'Name?' });
330
- channel.resolveUserAction(pending!, { type: 'answer', values: [], text: 'Robota' });
330
+ channel.resolveUserAction(pending!, { type: 'answer', values: [], text: 'Fixture Agent' });
331
331
  await flush();
332
332
  expect(link.sent).toEqual([
333
333
  {
334
334
  type: 'ask-response',
335
335
  id: 'a1',
336
- response: { type: 'answer', values: [], text: 'Robota' },
336
+ response: { type: 'answer', values: [], text: 'Fixture Agent' },
337
337
  },
338
338
  ]);
339
339
  await channel.stop();
@@ -11,7 +11,7 @@ import {
11
11
  resolveKeybindingInput,
12
12
  } from '../keybinding-registry.js';
13
13
 
14
- const SOURCE = '/tmp/robota-keybindings.json';
14
+ const SOURCE = '/tmp/agent-test-keybindings.json';
15
15
 
16
16
  function parse(document: unknown) {
17
17
  return parseKeybindingsDocument(JSON.stringify(document), SOURCE);
@@ -217,7 +217,7 @@ describe('contextual keybinding registry', () => {
217
217
  $defs: Record<string, { properties?: Record<string, unknown> }>;
218
218
  };
219
219
 
220
- expect(schema.$id).toBe('https://docs.robota.io/schemas/keybindings.schema.json');
220
+ expect(schema.$id).toBe('https://docs.robota.io//schemas/keybindings.schema.json');
221
221
  expect(Object.keys(schema.properties.bindings.properties)).toEqual(
222
222
  Object.keys(KEYBINDING_ACTIONS),
223
223
  );
@@ -11,7 +11,7 @@ import {
11
11
  const roots: string[] = [];
12
12
 
13
13
  async function temporaryRoot(): Promise<string> {
14
- const root = await mkdtemp(join(tmpdir(), 'robota-keybindings-'));
14
+ const root = await mkdtemp(join(tmpdir(), 'agent-test-keybindings-'));
15
15
  roots.push(root);
16
16
  return root;
17
17
  }
@@ -34,6 +34,13 @@ afterEach(async () => {
34
34
  });
35
35
 
36
36
  describe('Node keybindings source', () => {
37
+ it('omits the schema when the host has no documentation URL', async () => {
38
+ const root = await temporaryRoot();
39
+ const source = createNodeKeybindingsSource({ filePath: join(root, 'keys.json') });
40
+ expect(JSON.parse(await readFile(await source.ensureFile(), 'utf8'))).toEqual({ version: 1, bindings: {} });
41
+ source.dispose();
42
+ });
43
+
37
44
  it('uses only the host-selected file and schema when creating the sparse document', async () => {
38
45
  const root = await temporaryRoot();
39
46
  const filePath = join(root, 'host-config', 'keys.json');
@@ -25,15 +25,15 @@ export interface IKeybindingsSource extends IKeybindingsFilePort {
25
25
 
26
26
  export interface INodeKeybindingsSourceOptions {
27
27
  readonly filePath: string;
28
- readonly schemaUrl: string;
28
+ readonly schemaUrl?: string;
29
29
  readonly onDiagnostic?: (diagnostic: IKeybindingDiagnostic) => void;
30
30
  }
31
31
 
32
- function defaultDocument(schemaUrl: string) {
33
- return { $schema: schemaUrl, version: 1 as const, bindings: {} };
32
+ function defaultDocument(schemaUrl: string | undefined) {
33
+ return { ...(schemaUrl === undefined ? {} : { $schema: schemaUrl }), version: 1 as const, bindings: {} };
34
34
  }
35
35
 
36
- function defaultSnapshot(filePath: string, schemaUrl: string): IKeybindingSnapshot {
36
+ function defaultSnapshot(filePath: string, schemaUrl: string | undefined): IKeybindingSnapshot {
37
37
  const parsed = parseKeybindingsDocument(JSON.stringify(defaultDocument(schemaUrl)), filePath);
38
38
  if (!parsed.ok) throw new Error(parsed.diagnostic.message);
39
39
  return parsed.snapshot;
@@ -41,7 +41,7 @@ function defaultSnapshot(filePath: string, schemaUrl: string): IKeybindingSnapsh
41
41
 
42
42
  class NodeKeybindingsSource implements IKeybindingsSource {
43
43
  readonly filePath: string;
44
- readonly #schemaUrl: string;
44
+ readonly #schemaUrl: string | undefined;
45
45
  readonly #onDiagnostic: ((diagnostic: IKeybindingDiagnostic) => void) | undefined;
46
46
  readonly #listeners = new Set<(snapshot: IKeybindingSnapshot) => void>();
47
47
  #snapshot: IKeybindingSnapshot;
@@ -52,7 +52,7 @@ class NodeKeybindingsSource implements IKeybindingsSource {
52
52
 
53
53
  constructor(options: INodeKeybindingsSourceOptions) {
54
54
  if (options.filePath.trim() === '') throw new Error('Keybindings file path must not be empty.');
55
- if (options.schemaUrl.trim() === '') throw new Error('Keybindings schema URL must not be empty.');
55
+ if (options.schemaUrl?.trim() === '') throw new Error('Keybindings schema URL must not be empty.');
56
56
  this.filePath = options.filePath;
57
57
  this.#schemaUrl = options.schemaUrl;
58
58
  this.#onDiagnostic = options.onDiagnostic;
package/src/render.tsx CHANGED
@@ -101,9 +101,10 @@ export interface IRenderOptions {
101
101
  contributionSources?: readonly IContributionSource[];
102
102
  skillRoots?: readonly ISkillRootDescriptor[];
103
103
  userSettingsSources?: readonly INodeHostSettingsSource[];
104
+ environment?: Readonly<Record<string, string | undefined>>;
104
105
  /**
105
106
  * CLI-083 (issue #2287) — the org policy, forwarded to the session so `blockedCommands` is
106
- * enforced on the plain `robota` path as well as under `--serve`.
107
+ * enforced on the direct CLI path as well as under `--serve`.
107
108
  *
108
109
  * DECLARED, not merely spread through. The shell forwards this with
109
110
  * `...(orgPolicy === null ? {} : { orgPolicy })`, and a spread bypasses TypeScript's
@@ -186,6 +187,7 @@ export interface IRenderOptions {
186
187
  defaultTools?: readonly IToolWithEventService[];
187
188
  /** The sandbox the shell tools run under, so the session can let a confined command skip the prompt. */
188
189
  sandboxClient?: ICreateSessionOptions['sandboxClient'];
190
+ toolExecutionPolicy?: ICreateSessionOptions['toolExecutionPolicy'];
189
191
  commandModules?: readonly ICommandModule[];
190
192
  commandHostAdapters?: ICommandHostAdapters;
191
193
  shellExec?: TShellExecFn;
@@ -305,6 +307,7 @@ export function toChannelOptions(
305
307
  ...(options.userSettingsSources !== undefined
306
308
  ? { userSettingsSources: options.userSettingsSources }
307
309
  : {}),
310
+ ...(options.environment !== undefined ? { environment: options.environment } : {}),
308
311
  ...(options.orgPolicy !== undefined ? { orgPolicy: options.orgPolicy } : {}),
309
312
  ...(options.externalEventVerifierFactory !== undefined
310
313
  ? { externalEventVerifierFactory: options.externalEventVerifierFactory }
@@ -351,6 +354,9 @@ export function toChannelOptions(
351
354
  : {}),
352
355
  ...(options.additionalTools !== undefined ? { additionalTools: options.additionalTools } : {}),
353
356
  ...(options.defaultTools !== undefined ? { defaultTools: options.defaultTools } : {}),
357
+ ...(options.toolExecutionPolicy !== undefined
358
+ ? { toolExecutionPolicy: options.toolExecutionPolicy }
359
+ : {}),
354
360
  ...(options.sandboxClient !== undefined ? { sandboxClient: options.sandboxClient } : {}),
355
361
  commandModules: options.commandModules,
356
362
  commandHostAdapters: options.commandHostAdapters,
@@ -41,7 +41,7 @@ export function supportsImeCursorPositioning(
41
41
  * CLI-2004 — may this process write OSC 133 shell-integration marks at agent-turn boundaries?
42
42
  *
43
43
  * The marks are otherwise harmless: every emulator surveyed discards an unknown OSC sequence, so
44
- * this gate carries DOCUMENTED negatives, not a capability probe. Robota's support table (the TUI
44
+ * this gate carries DOCUMENTED negatives, not a capability probe. The support table (the TUI
45
45
  * package's docs/SPEC.md) is the user-facing half of the same list.
46
46
  *
47
47
  * Precedence:
@@ -80,6 +80,7 @@ export interface ITuiInteractionChannelOptions {
80
80
  contributionSources?: ICreateSessionOptions['contributionSources'];
81
81
  skillRoots?: ICreateSessionOptions['skillRoots'];
82
82
  userSettingsSources?: readonly INodeHostSettingsSource[];
83
+ environment?: Readonly<Record<string, string | undefined>>;
83
84
  /**
84
85
  * Explicit authority- and permission-backed edit checkpoint capability for this channel's one
85
86
  * session. Never shared with another channel's session: a store holds one turn in progress.
@@ -131,6 +132,7 @@ export interface ITuiInteractionChannelOptions {
131
132
  defaultTools?: readonly IToolWithEventService[];
132
133
  /** The sandbox the shell tools run under, so the session can let a confined command skip the prompt. */
133
134
  sandboxClient?: ICreateSessionOptions['sandboxClient'];
135
+ toolExecutionPolicy?: ICreateSessionOptions['toolExecutionPolicy'];
134
136
  commandModules?: readonly ICommandModule[];
135
137
  commandHostAdapters?: ICommandHostAdapters;
136
138
  shellExec?: TShellExecFn;
@@ -52,6 +52,7 @@ export function buildTuiSessionOptions(
52
52
  ...(opts.userSettingsSources !== undefined
53
53
  ? { userSettingsSources: opts.userSettingsSources }
54
54
  : {}),
55
+ ...(opts.environment !== undefined ? { environment: opts.environment } : {}),
55
56
  ...(opts.editCheckpointStore !== undefined
56
57
  ? { editCheckpointStore: opts.editCheckpointStore }
57
58
  : {}),
@@ -98,6 +99,9 @@ export function buildTuiSessionOptions(
98
99
  ...(opts.pluginDirectories !== undefined ? { pluginDirectories: opts.pluginDirectories } : {}),
99
100
  ...(opts.additionalTools !== undefined ? { additionalTools: opts.additionalTools } : {}),
100
101
  ...(opts.defaultTools !== undefined ? { defaultTools: opts.defaultTools } : {}),
102
+ ...(opts.toolExecutionPolicy !== undefined
103
+ ? { toolExecutionPolicy: opts.toolExecutionPolicy }
104
+ : {}),
101
105
  ...(opts.sandboxClient !== undefined ? { sandboxClient: opts.sandboxClient } : {}),
102
106
  commandModules: opts.commandModules,
103
107
  commandHostAdapters: opts.commandHostAdapters,
@@ -65,7 +65,7 @@ describe('extractToolCalls', () => {
65
65
 
66
66
  it('truncates long argument values with middle ellipsis', () => {
67
67
  const longPath =
68
- '/Users/jungyoun/Documents/dev/robota/packages/agent-sdk/src/plugins/very-long-directory-name/file.ts';
68
+ '/Users/example/robota/workspace/packages/agent-sdk/src/plugins/very-long-directory-name/file.ts';
69
69
  const history = [
70
70
  {
71
71
  role: 'assistant',