praxis-agent 0.35.0 → 0.36.0

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 (38) hide show
  1. package/README.md +5 -1
  2. package/dist/application/background-agent-manager.d.ts +16 -6
  3. package/dist/application/background-agent-manager.js +139 -97
  4. package/dist/application/context-engine.d.ts +56 -0
  5. package/dist/application/context-engine.js +129 -0
  6. package/dist/application/session-service.d.ts +8 -4
  7. package/dist/application/session-service.js +528 -434
  8. package/dist/application/subagent-service.js +358 -177
  9. package/dist/application/top-level-agent-manager.d.ts +1 -0
  10. package/dist/application/top-level-agent-manager.js +1025 -403
  11. package/dist/application/transcript-projection.d.ts +33 -0
  12. package/dist/application/transcript-projection.js +110 -0
  13. package/dist/application/turn-lifecycle.d.ts +2 -0
  14. package/dist/application/turn-memory-coordinator.d.ts +43 -0
  15. package/dist/application/turn-memory-coordinator.js +98 -0
  16. package/dist/cli/agents-dashboard.js +10 -5
  17. package/dist/cli/interactive.js +264 -204
  18. package/dist/cli/tui/claude-style.js +154 -130
  19. package/dist/cli/tui/task-panel.js +10 -1
  20. package/dist/cli/tui/theme.d.ts +46 -3
  21. package/dist/cli/tui/theme.js +414 -56
  22. package/dist/cli/tui/tool-permission.js +9 -5
  23. package/dist/cli/tui/tui-interaction-router.d.ts +78 -0
  24. package/dist/cli/tui/tui-interaction-router.js +127 -0
  25. package/dist/cli-runtime.d.ts +2 -2
  26. package/dist/core/agent-orchestration.d.ts +31 -0
  27. package/dist/core/agent-orchestration.js +235 -0
  28. package/dist/core/runtime.d.ts +5 -0
  29. package/dist/core/runtime.js +15 -1
  30. package/dist/persistence/claude-job-store.d.ts +58 -0
  31. package/dist/persistence/claude-job-store.js +483 -1
  32. package/dist/persistence/native-transcript-reader.d.ts +53 -0
  33. package/dist/persistence/native-transcript-reader.js +283 -0
  34. package/dist/persistence/subagent-lifecycle-store.d.ts +41 -8
  35. package/dist/persistence/subagent-lifecycle-store.js +373 -146
  36. package/dist/platform/exclusive-file-lease.d.ts +3 -0
  37. package/dist/platform/exclusive-file-lease.js +3 -0
  38. package/package.json +1 -1
@@ -47,10 +47,19 @@ export function projectBackgroundBashTask(snapshot) {
47
47
  };
48
48
  }
49
49
  export function projectBackgroundAgentTask(snapshot) {
50
+ const status = snapshot.status === 'cancelled'
51
+ ? 'stopped'
52
+ : snapshot.status === 'orphaned'
53
+ ? 'interrupted'
54
+ : snapshot.status === 'queued' ||
55
+ snapshot.status === 'waiting' ||
56
+ snapshot.status === 'cancelling'
57
+ ? 'running'
58
+ : snapshot.status;
50
59
  return {
51
60
  id: snapshot.agentId,
52
61
  kind: 'agent',
53
- status: snapshot.status,
62
+ status,
54
63
  label: snapshot.name ?? snapshot.description,
55
64
  output: snapshot.result?.text ?? snapshot.error ?? '',
56
65
  startedAtMs: snapshot.startedAt,
@@ -36,20 +36,63 @@ export interface TuiPalette {
36
36
  syntaxTheme: 'Monokai Extended' | 'GitHub' | 'ansi';
37
37
  syntax: TuiSyntaxPalette;
38
38
  }
39
- export type TuiSyntaxToken = 'text' | 'keyword' | 'identifier' | 'string';
39
+ /** Ink-compatible text attributes owned by the Praxis semantic theme. */
40
+ export interface TuiTextStyle {
41
+ readonly color?: string;
42
+ readonly backgroundColor?: string;
43
+ readonly bold?: boolean;
44
+ readonly dimColor?: boolean;
45
+ readonly italic?: boolean;
46
+ readonly underline?: boolean;
47
+ readonly inverse?: boolean;
48
+ }
49
+ /** Ink-compatible surface attributes owned by the Praxis semantic theme. */
50
+ export interface TuiSurfaceStyle {
51
+ readonly borderColor?: string;
52
+ readonly backgroundColor?: string;
53
+ }
54
+ export declare const TUI_TEXT_ROLES: readonly ["body", "productIdentity", "heading", "navigation", "selectedRow", "selectedTab", "focusMarker", "inputMarker", "inputCursor", "muted", "link", "active", "info", "success", "warning", "permission", "error", "diffAdded", "diffRemoved", "shellMode"];
55
+ export type TuiTextRole = (typeof TUI_TEXT_ROLES)[number];
56
+ export declare const TUI_SURFACE_ROLES: readonly ["neutralBorder", "separator", "selectedRow", "selectedTab", "input", "decision", "error"];
57
+ export type TuiSurfaceRole = (typeof TUI_SURFACE_ROLES)[number];
58
+ export interface TuiSemanticTheme {
59
+ readonly profile: TuiTheme;
60
+ readonly dark: boolean;
61
+ readonly ansiOnly: boolean;
62
+ readonly terminalColorCapability: TerminalColorCapability;
63
+ readonly syntaxHighlightingDisabled: boolean;
64
+ readonly screenReader: boolean;
65
+ readonly noColor: boolean;
66
+ readonly syntaxTheme: TuiPalette['syntaxTheme'];
67
+ readonly text: Readonly<Record<TuiTextRole, TuiTextStyle>>;
68
+ readonly surface: Readonly<Record<TuiSurfaceRole, TuiSurfaceStyle>>;
69
+ syntax(token: TuiSyntaxToken, change?: 'added' | 'removed'): Readonly<{
70
+ color?: string;
71
+ backgroundColor?: string;
72
+ }>;
73
+ session(agentColor: AgentColorName): string | undefined;
74
+ }
75
+ export interface TuiThemePresentationOptions {
76
+ environment?: TerminalEnvironment;
77
+ screenReader?: boolean;
78
+ }
79
+ export type TuiSyntaxToken = 'text' | 'keyword' | 'identifier' | 'string' | 'addedHighlight';
40
80
  export type TerminalColorCapability = 'ansi16' | 'ansi256' | 'truecolor';
41
81
  type TerminalEnvironment = Readonly<Record<string, string | undefined>>;
42
- export declare function tuiSyntaxStyle(palette: TuiPalette, token: TuiSyntaxToken, change?: 'added' | 'removed'): {
82
+ export declare function tuiSyntaxStyle(palette: TuiPalette | TuiSemanticTheme, token: TuiSyntaxToken, change?: 'added' | 'removed'): {
43
83
  color?: string;
44
84
  backgroundColor?: string;
45
85
  };
46
86
  export declare const DEFAULT_TUI_THEME_SETTINGS: TuiThemeSettings;
47
87
  export declare function terminalColorCapability(environment?: TerminalEnvironment): TerminalColorCapability;
48
88
  export declare function tuiPalette(profile: TuiTheme, syntaxHighlightingDisabled?: boolean, environment?: TerminalEnvironment, customTheme?: TuiCustomTheme): TuiPalette;
49
- export declare function TuiThemeProvider({ settings, children, }: {
89
+ export declare function resolveTuiTheme(settings: TuiThemeSettings, options?: TuiThemePresentationOptions): TuiSemanticTheme;
90
+ export declare function TuiThemeProvider({ settings, screenReader, children, }: {
50
91
  settings: TuiThemeSettings;
92
+ screenReader?: boolean;
51
93
  children: ReactNode;
52
94
  }): import("react").JSX.Element;
53
95
  export declare function useTuiPalette(): TuiPalette;
96
+ export declare function useTuiTheme(): TuiSemanticTheme;
54
97
  export {};
55
98
  //# sourceMappingURL=theme.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { createContext, useContext } from 'react';
2
+ import { createContext, useContext, useMemo } from 'react';
3
3
  export const TUI_THEMES = [
4
4
  'auto',
5
5
  'dark',
@@ -9,16 +9,55 @@ export const TUI_THEMES = [
9
9
  'dark-ansi',
10
10
  'light-ansi',
11
11
  ];
12
+ export const TUI_TEXT_ROLES = [
13
+ 'body',
14
+ 'productIdentity',
15
+ 'heading',
16
+ 'navigation',
17
+ 'selectedRow',
18
+ 'selectedTab',
19
+ 'focusMarker',
20
+ 'inputMarker',
21
+ 'inputCursor',
22
+ 'muted',
23
+ 'link',
24
+ 'active',
25
+ 'info',
26
+ 'success',
27
+ 'warning',
28
+ 'permission',
29
+ 'error',
30
+ 'diffAdded',
31
+ 'diffRemoved',
32
+ 'shellMode',
33
+ ];
34
+ export const TUI_SURFACE_ROLES = [
35
+ 'neutralBorder',
36
+ 'separator',
37
+ 'selectedRow',
38
+ 'selectedTab',
39
+ 'input',
40
+ 'decision',
41
+ 'error',
42
+ ];
12
43
  export function tuiSyntaxStyle(palette, token, change) {
44
+ if (typeof palette.syntax === 'function')
45
+ return palette.syntax(token, change);
13
46
  if (palette.syntaxHighlightingDisabled)
14
47
  return {};
48
+ const syntax = palette.syntax;
49
+ if (token === 'addedHighlight') {
50
+ return syntax.addedHighlight === undefined
51
+ ? {}
52
+ : { backgroundColor: syntax.addedHighlight };
53
+ }
15
54
  const backgroundColor = change === 'added'
16
- ? palette.syntax.addedBackground
55
+ ? syntax.addedBackground
17
56
  : change === 'removed'
18
- ? palette.syntax.removedBackground
57
+ ? syntax.removedBackground
19
58
  : undefined;
20
59
  return {
21
- color: palette.syntax[token],
60
+ color: syntax[token],
22
61
  ...(backgroundColor === undefined ? {} : { backgroundColor }),
23
62
  };
24
63
  }
@@ -114,6 +153,42 @@ export function terminalColorCapability(environment = process.env) {
114
153
  return /256color/iu.test(environment.TERM ?? '') ? 'ansi256' : 'ansi16';
115
154
  }
116
155
  const ANSI_256_LEVELS = [0, 95, 135, 175, 215, 255];
156
+ const ANSI_16_RGB = [
157
+ [0, 0, 0],
158
+ [128, 0, 0],
159
+ [0, 128, 0],
160
+ [128, 128, 0],
161
+ [0, 0, 128],
162
+ [128, 0, 128],
163
+ [0, 128, 128],
164
+ [192, 192, 192],
165
+ [128, 128, 128],
166
+ [255, 0, 0],
167
+ [0, 255, 0],
168
+ [255, 255, 0],
169
+ [0, 0, 255],
170
+ [255, 0, 255],
171
+ [0, 255, 255],
172
+ [255, 255, 255],
173
+ ];
174
+ const ANSI_16_NAMES = [
175
+ 'black',
176
+ 'red',
177
+ 'green',
178
+ 'yellow',
179
+ 'blue',
180
+ 'magenta',
181
+ 'cyan',
182
+ 'white',
183
+ 'blackBright',
184
+ 'redBright',
185
+ 'greenBright',
186
+ 'yellowBright',
187
+ 'blueBright',
188
+ 'magentaBright',
189
+ 'cyanBright',
190
+ 'whiteBright',
191
+ ];
117
192
  const ansi256ColorCache = new Map();
118
193
  const ANSI_16_COLORS = {
119
194
  '#ffffff': 'whiteBright',
@@ -123,6 +198,7 @@ const ANSI_16_COLORS = {
123
198
  '#5f0000': 'black',
124
199
  '#005f00': 'black',
125
200
  '#008700': 'green',
201
+ '#16a34a': 'green',
126
202
  '#d97757': 'redBright',
127
203
  '#b8a1ff': 'whiteBright',
128
204
  '#5fafff': 'cyanBright',
@@ -142,8 +218,115 @@ const ANSI_16_COLORS = {
142
218
  '#875f00': 'red',
143
219
  '#585858': 'black',
144
220
  };
145
- function ansi16Color(hex) {
146
- return ANSI_16_COLORS[hex.toLowerCase()] ?? hex;
221
+ function ansi16Color(value) {
222
+ const normalized = normalizeAnsiName(value);
223
+ const directAnsi = /^ansi256\((\d+)\)$/iu.exec(normalized);
224
+ if (directAnsi) {
225
+ const index = Number(directAnsi[1]);
226
+ if (index >= 0 && index < ANSI_16_NAMES.length)
227
+ return ANSI_16_NAMES[index] ?? 'black';
228
+ }
229
+ const known = ANSI_16_COLORS[normalized.toLowerCase()];
230
+ if (known)
231
+ return known;
232
+ const hex = hexFromRgb(normalized) ?? ansi256ToHex(normalized);
233
+ const match = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/iu.exec(hex ?? normalized);
234
+ if (!match)
235
+ return normalized;
236
+ const rgb = [
237
+ Number.parseInt(match[1] ?? '0', 16),
238
+ Number.parseInt(match[2] ?? '0', 16),
239
+ Number.parseInt(match[3] ?? '0', 16),
240
+ ];
241
+ let bestIndex = 0;
242
+ let bestDistance = Number.POSITIVE_INFINITY;
243
+ for (const [index, candidate] of ANSI_16_RGB.entries()) {
244
+ const distance = candidate.reduce((total, component, componentIndex) => total + ((rgb[componentIndex] ?? 0) - component) ** 2, 0);
245
+ if (distance < bestDistance) {
246
+ bestDistance = distance;
247
+ bestIndex = index;
248
+ }
249
+ }
250
+ return ANSI_16_NAMES[bestIndex] ?? 'black';
251
+ }
252
+ function hexFromRgb(value) {
253
+ const match = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/iu.exec(value);
254
+ if (!match)
255
+ return undefined;
256
+ return `#${[match[1], match[2], match[3]]
257
+ .map((component) => Number(component).toString(16).padStart(2, '0'))
258
+ .join('')}`;
259
+ }
260
+ function ansi256ToHex(value) {
261
+ const match = /^ansi256\((\d+)\)$/iu.exec(value);
262
+ if (!match)
263
+ return undefined;
264
+ const index = Number(match[1]);
265
+ if (index < 0 || index > 255)
266
+ return undefined;
267
+ if (index < 16) {
268
+ const [red, green, blue] = ANSI_16_RGB[index] ?? [];
269
+ if (red === undefined || green === undefined || blue === undefined)
270
+ return undefined;
271
+ return `#${[red, green, blue]
272
+ .map((component) => component.toString(16).padStart(2, '0'))
273
+ .join('')}`;
274
+ }
275
+ if (index >= 232) {
276
+ const level = 8 + (index - 232) * 10;
277
+ return `#${[level, level, level]
278
+ .map((component) => component.toString(16).padStart(2, '0'))
279
+ .join('')}`;
280
+ }
281
+ const offset = index - 16;
282
+ const red = Math.floor(offset / 36);
283
+ const green = Math.floor((offset % 36) / 6);
284
+ const blue = offset % 6;
285
+ const levels = [0, 95, 135, 175, 215, 255];
286
+ return `#${[levels[red], levels[green], levels[blue]]
287
+ .map((component) => (component ?? 0).toString(16).padStart(2, '0'))
288
+ .join('')}`;
289
+ }
290
+ function normalizeAnsiName(value) {
291
+ return value.startsWith('ansi:') ? value.slice('ansi:'.length) : value;
292
+ }
293
+ function adaptEffectiveColor(value, capability) {
294
+ const normalized = normalizeAnsiName(value);
295
+ if (capability === 'truecolor')
296
+ return normalized;
297
+ if (capability === 'ansi256' &&
298
+ /^ansi256\((?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\)$/u.test(normalized))
299
+ return normalized;
300
+ const rgb = hexFromRgb(normalized);
301
+ const hex = rgb ?? ansi256ToHex(normalized) ?? normalized;
302
+ if (capability === 'ansi16') {
303
+ if (!hex.startsWith('#'))
304
+ return hex;
305
+ return ansi16Color(hex);
306
+ }
307
+ if (hex.startsWith('#'))
308
+ return ansi256Color(hex);
309
+ return hex;
310
+ }
311
+ function projectPalette(palette, capability) {
312
+ const adapt = (value) => adaptEffectiveColor(value, capability);
313
+ return {
314
+ ...palette,
315
+ brand: adapt(palette.brand),
316
+ accent: adapt(palette.accent),
317
+ info: adapt(palette.info),
318
+ link: adapt(palette.link),
319
+ error: adapt(palette.error),
320
+ success: adapt(palette.success),
321
+ warning: adapt(palette.warning),
322
+ muted: adapt(palette.muted),
323
+ selectionText: adapt(palette.selectionText),
324
+ sessionColors: Object.fromEntries(Object.entries(palette.sessionColors).map(([name, value]) => [
325
+ name,
326
+ adapt(value),
327
+ ])),
328
+ syntax: adaptSyntaxColors(palette.syntax, adapt),
329
+ };
147
330
  }
148
331
  function ansi256Color(hex) {
149
332
  const cached = ansi256ColorCache.get(hex);
@@ -201,7 +384,7 @@ function adaptSyntaxColors(syntax, adapt) {
201
384
  : { addedHighlight: adapt(syntax.addedHighlight) }),
202
385
  };
203
386
  }
204
- export function tuiPalette(profile, syntaxHighlightingDisabled = false, environment = process.env, customTheme) {
387
+ function resolveRawPalette(profile, syntaxHighlightingDisabled = false, environment = process.env, customTheme) {
205
388
  const dark = profile === 'auto'
206
389
  ? automaticDark(environment)
207
390
  : profile === 'dark' ||
@@ -235,16 +418,6 @@ export function tuiPalette(profile, syntaxHighlightingDisabled = false, environm
235
418
  }
236
419
  : {}),
237
420
  };
238
- const terminalCapability = terminalColorCapability(environment);
239
- const adaptAutoColor = terminalCapability === 'ansi16'
240
- ? ansi16Color
241
- : terminalCapability === 'ansi256'
242
- ? ansi256Color
243
- : (color) => color;
244
- const syntax = profile === 'auto'
245
- ? adaptSyntaxColors(syntaxBase, adaptAutoColor)
246
- : syntaxBase;
247
- const autoColor = (color) => profile === 'auto' ? adaptAutoColor(color) : color;
248
421
  const sessionColors = ansiOnly
249
422
  ? dark
250
423
  ? DARK_ANSI_SESSION_COLORS
@@ -254,14 +427,14 @@ export function tuiPalette(profile, syntaxHighlightingDisabled = false, environm
254
427
  ? DARK_DALTONIZED_SESSION_COLORS
255
428
  : LIGHT_DALTONIZED_SESSION_COLORS
256
429
  : {
257
- red: autoColor(NORMAL_SESSION_COLORS.red),
258
- blue: autoColor(NORMAL_SESSION_COLORS.blue),
259
- green: autoColor(NORMAL_SESSION_COLORS.green),
260
- yellow: autoColor(NORMAL_SESSION_COLORS.yellow),
261
- purple: autoColor(NORMAL_SESSION_COLORS.purple),
262
- orange: autoColor(NORMAL_SESSION_COLORS.orange),
263
- pink: autoColor(NORMAL_SESSION_COLORS.pink),
264
- cyan: autoColor(NORMAL_SESSION_COLORS.cyan),
430
+ red: NORMAL_SESSION_COLORS.red,
431
+ blue: NORMAL_SESSION_COLORS.blue,
432
+ green: NORMAL_SESSION_COLORS.green,
433
+ yellow: NORMAL_SESSION_COLORS.yellow,
434
+ purple: NORMAL_SESSION_COLORS.purple,
435
+ orange: NORMAL_SESSION_COLORS.orange,
436
+ pink: NORMAL_SESSION_COLORS.pink,
437
+ cyan: NORMAL_SESSION_COLORS.cyan,
265
438
  };
266
439
  const palette = {
267
440
  profile,
@@ -269,31 +442,17 @@ export function tuiPalette(profile, syntaxHighlightingDisabled = false, environm
269
442
  ansiOnly,
270
443
  syntaxHighlightingDisabled,
271
444
  sessionColors,
272
- brand: ansiOnly ? 'redBright' : autoColor('#D97757'),
273
- accent: ansiOnly
274
- ? 'magentaBright'
275
- : daltonized
276
- ? dark
277
- ? '#5fd7ff'
278
- : '#005f87'
279
- : dark
280
- ? autoColor('#B8A1FF')
281
- : autoColor('#875faf'),
282
- info: ansiOnly ? 'cyanBright' : autoColor(dark ? '#5fd7ff' : '#005f87'),
283
- link: ansiOnly ? 'blueBright' : autoColor(dark ? '#5fafff' : '#005faf'),
284
- error: ansiOnly ? 'redBright' : autoColor(dark ? '#ff5f5f' : '#af0000'),
285
- success: ansiOnly ? 'greenBright' : autoColor(dark ? '#5fd75f' : '#008700'),
286
- warning: ansiOnly
287
- ? 'yellowBright'
288
- : autoColor(dark ? '#ffd75f' : '#875f00'),
289
- muted: ansiOnly
290
- ? dark
291
- ? 'white'
292
- : 'black'
293
- : autoColor(dark ? '#a8a8a8' : '#585858'),
445
+ brand: ansiOnly ? 'redBright' : '#D97757',
446
+ accent: ansiOnly ? 'redBright' : '#D97757',
447
+ info: ansiOnly ? 'cyanBright' : dark ? '#5fd7ff' : '#005f87',
448
+ link: ansiOnly ? 'blueBright' : dark ? '#5fafff' : '#005faf',
449
+ error: ansiOnly ? 'redBright' : dark ? '#ff5f5f' : '#af0000',
450
+ success: ansiOnly ? 'greenBright' : dark ? '#5fd75f' : '#008700',
451
+ warning: ansiOnly ? 'yellowBright' : dark ? '#ffd75f' : '#875f00',
452
+ muted: ansiOnly ? (dark ? 'white' : 'black') : dark ? '#a8a8a8' : '#585858',
294
453
  selectionText: dark ? 'black' : 'white',
295
454
  syntaxTheme: ansiOnly ? 'ansi' : dark ? 'Monokai Extended' : 'GitHub',
296
- syntax,
455
+ syntax: syntaxBase,
297
456
  };
298
457
  if (customTheme) {
299
458
  const overrides = customTheme.overrides;
@@ -313,22 +472,221 @@ export function tuiPalette(profile, syntaxHighlightingDisabled = false, environm
313
472
  keyword: override('claudeBlue_FOR_SYSTEM_SPINNER', palette.syntax.keyword),
314
473
  identifier: override('suggestion', palette.syntax.identifier),
315
474
  string: override('success', palette.syntax.string),
316
- removedBackground: override('diffRemoved', palette.syntax.removedBackground ?? ''),
317
- addedBackground: override('diffAdded', palette.syntax.addedBackground ?? ''),
318
- addedHighlight: override('diffAddedWord', palette.syntax.addedHighlight ?? ''),
475
+ ...(palette.syntax.removedBackground !== undefined ||
476
+ overrides.diffRemoved !== undefined
477
+ ? {
478
+ removedBackground: override('diffRemoved', palette.syntax.removedBackground ?? ''),
479
+ }
480
+ : {}),
481
+ ...(palette.syntax.addedBackground !== undefined ||
482
+ overrides.diffAdded !== undefined
483
+ ? {
484
+ addedBackground: override('diffAdded', palette.syntax.addedBackground ?? ''),
485
+ }
486
+ : {}),
487
+ ...(palette.syntax.addedHighlight !== undefined ||
488
+ overrides.diffAddedWord !== undefined
489
+ ? {
490
+ addedHighlight: override('diffAddedWord', palette.syntax.addedHighlight ?? ''),
491
+ }
492
+ : {}),
319
493
  };
320
494
  palette.profile = customTheme.base;
321
495
  }
322
496
  return palette;
323
497
  }
324
- const TuiPaletteContext = createContext(tuiPalette('auto'));
325
- export function TuiThemeProvider({ settings, children, }) {
326
- const baseTheme = TUI_THEMES.includes(settings.theme)
498
+ export function tuiPalette(profile, syntaxHighlightingDisabled = false, environment = process.env, customTheme) {
499
+ return resolvedThemeBundle({
500
+ theme: profile,
501
+ syntaxHighlightingDisabled,
502
+ ...(customTheme === undefined ? {} : { customTheme }),
503
+ }, { environment }).palette;
504
+ }
505
+ function frozen(value) {
506
+ return Object.freeze(value);
507
+ }
508
+ function semanticText(color, options, attributes = {}) {
509
+ const withoutBackground = { ...attributes };
510
+ delete withoutBackground.backgroundColor;
511
+ if (color === undefined ||
512
+ options.screenReader ||
513
+ options.environment?.NO_COLOR !== undefined)
514
+ return frozen(withoutBackground);
515
+ return frozen({ color, ...attributes });
516
+ }
517
+ function semanticSurface(colors, options) {
518
+ if (options.screenReader || options.environment?.NO_COLOR !== undefined)
519
+ return frozen({});
520
+ return frozen(colors);
521
+ }
522
+ const EMPTY_STYLE = Object.freeze({});
523
+ function buildSemanticTheme(settings, palette, options = {}, capability = palette.ansiOnly
524
+ ? 'ansi16'
525
+ : terminalColorCapability(options.environment ?? process.env), diffColors = {
526
+ added: palette.success,
527
+ removed: palette.error,
528
+ }) {
529
+ const environment = options.environment ?? process.env;
530
+ const noColor = environment.NO_COLOR !== undefined;
531
+ const screenReader = options.screenReader === true;
532
+ const suppressColor = noColor || screenReader;
533
+ const productColor = palette.brand;
534
+ const focusColor = palette.accent;
535
+ const infoColor = palette.info;
536
+ const linkColor = palette.link;
537
+ const errorColor = palette.error;
538
+ const successColor = palette.success;
539
+ const warningColor = palette.warning;
540
+ const mutedColor = palette.muted;
541
+ const selectionTextColor = palette.selectionText;
542
+ const syntaxPalette = palette.syntax;
543
+ const sessionColors = palette.sessionColors;
544
+ const color = (value) => suppressColor ? undefined : value;
545
+ const diffAdded = diffColors.added;
546
+ const diffRemoved = diffColors.removed;
547
+ const text = {
548
+ body: semanticText(undefined, options),
549
+ productIdentity: semanticText(color(productColor), options, {
550
+ bold: true,
551
+ }),
552
+ heading: semanticText(undefined, options, { bold: true }),
553
+ navigation: semanticText(color(focusColor), options, { bold: true }),
554
+ selectedRow: semanticText(color(selectionTextColor), options, {
555
+ ...(suppressColor ? {} : { backgroundColor: focusColor }),
556
+ bold: true,
557
+ }),
558
+ selectedTab: semanticText(color(selectionTextColor), options, {
559
+ ...(suppressColor ? {} : { backgroundColor: focusColor }),
560
+ bold: true,
561
+ }),
562
+ focusMarker: semanticText(color(focusColor), options, { bold: true }),
563
+ inputMarker: semanticText(color(focusColor), options),
564
+ inputCursor: frozen(suppressColor ? {} : { inverse: true }),
565
+ muted: semanticText(color(mutedColor), options, { dimColor: true }),
566
+ link: semanticText(color(linkColor), options, { underline: true }),
567
+ active: semanticText(color(infoColor), options, { bold: true }),
568
+ info: semanticText(color(infoColor), options),
569
+ success: semanticText(color(successColor), options),
570
+ warning: semanticText(color(warningColor), options),
571
+ permission: semanticText(color(warningColor), options, { bold: true }),
572
+ error: semanticText(color(errorColor), options, { bold: true }),
573
+ diffAdded: semanticText(color(diffAdded), options),
574
+ diffRemoved: semanticText(color(diffRemoved), options),
575
+ shellMode: semanticText(color(infoColor), options, { bold: true }),
576
+ };
577
+ const border = (value) => {
578
+ const resolved = color(value);
579
+ return resolved === undefined ? {} : { borderColor: resolved };
580
+ };
581
+ const background = (value) => {
582
+ const resolved = color(value);
583
+ return resolved === undefined ? {} : { backgroundColor: resolved };
584
+ };
585
+ const surface = {
586
+ neutralBorder: semanticSurface(border(mutedColor), options),
587
+ separator: semanticSurface(border(mutedColor), options),
588
+ selectedRow: semanticSurface(background(focusColor), options),
589
+ selectedTab: semanticSurface(background(focusColor), options),
590
+ input: semanticSurface(border(focusColor), options),
591
+ decision: semanticSurface(border(warningColor), options),
592
+ error: semanticSurface(border(errorColor), options),
593
+ };
594
+ const syntaxStyle = (token, change) => {
595
+ if (settings.syntaxHighlightingDisabled || suppressColor)
596
+ return EMPTY_STYLE;
597
+ const backgroundColor = change === 'added'
598
+ ? syntaxPalette.addedBackground
599
+ : change === 'removed'
600
+ ? syntaxPalette.removedBackground
601
+ : token === 'addedHighlight'
602
+ ? syntaxPalette.addedHighlight
603
+ : undefined;
604
+ if (token === 'addedHighlight')
605
+ return backgroundColor === undefined
606
+ ? frozen({})
607
+ : frozen({ backgroundColor });
608
+ return frozen({
609
+ color: syntaxPalette[token],
610
+ ...(backgroundColor === undefined ? {} : { backgroundColor }),
611
+ });
612
+ };
613
+ const syntaxStyles = new Map();
614
+ for (const token of [
615
+ 'text',
616
+ 'keyword',
617
+ 'identifier',
618
+ 'string',
619
+ 'addedHighlight',
620
+ ]) {
621
+ for (const change of [undefined, 'added', 'removed']) {
622
+ syntaxStyles.set(change === undefined ? token : `${token}:${change}`, syntaxStyle(token, change));
623
+ }
624
+ }
625
+ return {
626
+ profile: palette.profile,
627
+ dark: palette.dark,
628
+ ansiOnly: palette.ansiOnly,
629
+ terminalColorCapability: capability,
630
+ syntaxHighlightingDisabled: settings.syntaxHighlightingDisabled,
631
+ screenReader,
632
+ noColor,
633
+ syntaxTheme: palette.syntaxTheme,
634
+ text: frozen(text),
635
+ surface: frozen(surface),
636
+ syntax: (token, change) => syntaxStyles.get(change === undefined ? token : `${token}:${change}`) ??
637
+ EMPTY_STYLE,
638
+ session: (agentColor) => suppressColor ? undefined : sessionColors[agentColor],
639
+ };
640
+ }
641
+ function resolvedThemeBundle(settings, options = {}) {
642
+ const profile = TUI_THEMES.includes(settings.theme)
327
643
  ? settings.theme
328
- : 'dark';
329
- return (_jsx(TuiPaletteContext.Provider, { value: tuiPalette(baseTheme, settings.syntaxHighlightingDisabled, process.env, settings.customTheme), children: children }));
644
+ : (settings.customTheme?.base ?? 'dark');
645
+ const environment = options.environment ?? process.env;
646
+ const capability = profile.endsWith('-ansi')
647
+ ? 'ansi16'
648
+ : terminalColorCapability(environment);
649
+ const rawPalette = resolveRawPalette(profile, settings.syntaxHighlightingDisabled, environment, settings.customTheme);
650
+ const palette = projectPalette(rawPalette, capability);
651
+ const adapt = (value) => adaptEffectiveColor(value, capability);
652
+ return {
653
+ palette,
654
+ theme: buildSemanticTheme(settings, palette, options, capability, {
655
+ added: adapt(settings.customTheme?.overrides.diffAdded ?? rawPalette.success),
656
+ removed: adapt(settings.customTheme?.overrides.diffRemoved ?? rawPalette.error),
657
+ }),
658
+ };
659
+ }
660
+ export function resolveTuiTheme(settings, options = {}) {
661
+ return resolvedThemeBundle(settings, options).theme;
662
+ }
663
+ const TuiPaletteContext = createContext(tuiPalette('auto'));
664
+ const TuiThemeContext = createContext(resolveTuiTheme(DEFAULT_TUI_THEME_SETTINGS));
665
+ export function TuiThemeProvider({ settings, screenReader = false, children, }) {
666
+ const environment = process.env;
667
+ const environmentKey = [
668
+ environment.COLORFGBG,
669
+ environment.FORCE_COLOR,
670
+ environment.COLORTERM,
671
+ environment.TERM,
672
+ environment.TERM_PROGRAM,
673
+ environment.NO_COLOR,
674
+ ]
675
+ .map((value) => value ?? '')
676
+ .join('\u0000');
677
+ const bundle = useMemo(() => resolvedThemeBundle(settings, { environment, screenReader }), [
678
+ settings.theme,
679
+ settings.syntaxHighlightingDisabled,
680
+ settings.customTheme,
681
+ screenReader,
682
+ environmentKey,
683
+ ]);
684
+ return (_jsx(TuiThemeContext.Provider, { value: bundle.theme, children: _jsx(TuiPaletteContext.Provider, { value: bundle.palette, children: children }) }));
330
685
  }
331
686
  export function useTuiPalette() {
332
687
  return useContext(TuiPaletteContext);
333
688
  }
689
+ export function useTuiTheme() {
690
+ return useContext(TuiThemeContext);
691
+ }
334
692
  //# sourceMappingURL=theme.js.map
@@ -8,7 +8,7 @@ import { extractPermissionRules, permissionRuleValueToString, shellPermissionSug
8
8
  import { redactSensitiveText } from '../../platform/sensitive-data.js';
9
9
  import { resolveDataPlaneRoot, } from '../../persistence/data-plane.js';
10
10
  import { composerEditorSegments, } from './composer-editor.js';
11
- import { useTuiPalette } from './theme.js';
11
+ import { useTuiTheme } from './theme.js';
12
12
  function stringInput(call, name) {
13
13
  const value = call.input[name];
14
14
  return typeof value === 'string' ? value : undefined;
@@ -370,10 +370,12 @@ export function projectTuiToolPermission(call, cwd, sensitiveValues, decision, d
370
370
  });
371
371
  }
372
372
  export function ToolPermissionDialog({ model, selection, feedbackMode, feedback, ruleEditor, screenReader, }) {
373
- const palette = useTuiPalette();
374
- return (_jsxs(Box, { flexDirection: "column", borderStyle: screenReader ? undefined : 'round', borderColor: palette.warning, paddingX: screenReader ? 0 : 1, marginTop: 1, children: [_jsx(Text, { bold: true, color: palette.warning, children: model.title }), model.subtitle ? _jsx(Text, { dimColor: true, children: model.subtitle }) : null, _jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [model.detail.map((line, index) => {
373
+ const theme = useTuiTheme();
374
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: screenReader ? undefined : 'round', ...theme.surface.decision, paddingX: screenReader ? 0 : 1, marginTop: 1, children: [_jsx(Text, { ...theme.text.permission, children: model.title }), model.subtitle ? _jsx(Text, { dimColor: true, children: model.subtitle }) : null, _jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [model.detail.map((line, index) => {
375
375
  const content = `${line.prefix ? `${line.prefix} ` : ''}${line.text}`;
376
- return line.prefix ? (_jsx(Text, { color: line.prefix === '+' ? palette.success : palette.error, children: content }, `${index}-${line.prefix}`)) : (_jsx(Text, { children: content }, `${index}-`));
376
+ return line.prefix ? (_jsx(Text, { ...(line.prefix === '+'
377
+ ? theme.text.diffAdded
378
+ : theme.text.diffRemoved), children: content }, `${index}-${line.prefix}`)) : (_jsx(Text, { children: content }, `${index}-`));
377
379
  }), model.description ? _jsx(Text, { dimColor: true, children: model.description }) : null] }), model.explanation ? _jsx(Text, { dimColor: true, children: model.explanation }) : null, _jsx(Text, { children: model.question }), model.options.map((option, index) => {
378
380
  const selected = selection === index;
379
381
  const editor = option.editableRule
@@ -383,7 +385,9 @@ export function ToolPermissionDialog({ model, selection, feedbackMode, feedback,
383
385
  })
384
386
  : null;
385
387
  const segments = editor ? composerEditorSegments(editor) : null;
386
- return (_jsxs(Text, { bold: selected, ...(selected ? { color: palette.brand } : {}), children: [selected ? (screenReader ? 'Selected: ' : '❯ ') : ' ', index + 1, ". ", option.label, segments ? (screenReader || !selected ? (`: ${editor?.text ?? ''}`) : (_jsxs(Text, { children: [": ", segments.before, _jsx(Text, { inverse: true, children: segments.current ?? ' ' }), segments.after] }))) : null] }, `${option.action}-${index}`));
388
+ return (_jsxs(Text, { bold: selected, ...(selected ? theme.text.selectedRow : {}), children: [selected ? (screenReader ? 'Selected: ' : '❯ ') : ' ', index + 1, ". ", option.label, segments ? (screenReader || !selected ? (`: ${editor?.text ?? ''}`) : (_jsxs(Text, { children: [": ", segments.before, _jsx(Text, { ...theme.text.inputCursor, children: theme.noColor
389
+ ? `${segments.current ?? ' '}\u0332`
390
+ : (segments.current ?? ' ') }), segments.after] }))) : null] }, `${option.action}-${index}`));
387
391
  }), feedbackMode ? (_jsxs(Text, { children: ["\u203A", ' ', feedback ||
388
392
  (model.options[selection]?.action === 'deny'
389
393
  ? 'tell Praxis what to do differently'