praxis-agent 0.35.0 → 0.37.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.
- package/README.md +20 -2
- package/dist/application/background-agent-manager.d.ts +16 -6
- package/dist/application/background-agent-manager.js +139 -97
- package/dist/application/context-engine.d.ts +56 -0
- package/dist/application/context-engine.js +129 -0
- package/dist/application/durable-follow-up.d.ts +14 -0
- package/dist/application/durable-follow-up.js +52 -0
- package/dist/application/native-session-transcript.d.ts +72 -0
- package/dist/application/native-session-transcript.js +351 -0
- package/dist/application/native-sidechain-transcript.d.ts +34 -0
- package/dist/application/native-sidechain-transcript.js +156 -0
- package/dist/application/session-service.d.ts +23 -4
- package/dist/application/session-service.js +1130 -494
- package/dist/application/subagent-service.d.ts +5 -1
- package/dist/application/subagent-service.js +749 -320
- package/dist/application/team-agent-runtime.d.ts +29 -0
- package/dist/application/team-agent-runtime.js +169 -0
- package/dist/application/team-capability.d.ts +27 -0
- package/dist/application/team-capability.js +41 -0
- package/dist/application/team-lead-operations.d.ts +38 -0
- package/dist/application/team-lead-operations.js +155 -0
- package/dist/application/team-mailbox.d.ts +54 -0
- package/dist/application/team-mailbox.js +196 -0
- package/dist/application/team-manager.d.ts +113 -0
- package/dist/application/team-manager.js +681 -0
- package/dist/application/team-workspace.d.ts +29 -0
- package/dist/application/team-workspace.js +197 -0
- package/dist/application/top-level-agent-manager.d.ts +1 -0
- package/dist/application/top-level-agent-manager.js +1025 -403
- package/dist/application/transcript-projection.d.ts +35 -0
- package/dist/application/transcript-projection.js +139 -0
- package/dist/application/turn-lifecycle.d.ts +2 -0
- package/dist/application/turn-memory-coordinator.d.ts +43 -0
- package/dist/application/turn-memory-coordinator.js +98 -0
- package/dist/cli/agents-dashboard.js +10 -5
- package/dist/cli/interactive.d.ts +7 -0
- package/dist/cli/interactive.js +645 -334
- package/dist/cli/tui/claude-style.d.ts +8 -25
- package/dist/cli/tui/claude-style.js +299 -277
- package/dist/cli/tui/decision-surface-model.d.ts +71 -0
- package/dist/cli/tui/decision-surface-model.js +131 -0
- package/dist/cli/tui/decision-surface.d.ts +7 -0
- package/dist/cli/tui/decision-surface.js +41 -0
- package/dist/cli/tui/help-surface-model.d.ts +68 -0
- package/dist/cli/tui/help-surface-model.js +131 -0
- package/dist/cli/tui/mcp-panel.js +1 -2
- package/dist/cli/tui/permission-surface-model.d.ts +195 -0
- package/dist/cli/tui/permission-surface-model.js +360 -0
- package/dist/cli/tui/permission-surface.d.ts +7 -0
- package/dist/cli/tui/permission-surface.js +69 -0
- package/dist/cli/tui/presentation-environment.d.ts +32 -0
- package/dist/cli/tui/presentation-environment.js +149 -0
- package/dist/cli/tui/task-panel.js +10 -1
- package/dist/cli/tui/theme.d.ts +46 -3
- package/dist/cli/tui/theme.js +414 -56
- package/dist/cli/tui/tool-permission.js +16 -8
- package/dist/cli/tui/transcript-presentation.d.ts +19 -1
- package/dist/cli/tui/transcript-presentation.js +26 -6
- package/dist/cli/tui/transcript-viewport.d.ts +18 -0
- package/dist/cli/tui/transcript-viewport.js +517 -111
- package/dist/cli/tui/transcript-window-model.d.ts +108 -0
- package/dist/cli/tui/transcript-window-model.js +772 -0
- package/dist/cli/tui/tui-interaction-router.d.ts +78 -0
- package/dist/cli/tui/tui-interaction-router.js +127 -0
- package/dist/cli/tui/tui-screen-model.d.ts +114 -0
- package/dist/cli/tui/tui-screen-model.js +210 -0
- package/dist/cli/tui/tui-view-model.d.ts +2 -49
- package/dist/cli/tui/tui-view-model.js +1 -68
- package/dist/cli-runtime.d.ts +4 -2
- package/dist/cli-runtime.js +397 -32
- package/dist/compatibility/claude/transcript-codec.js +6 -0
- package/dist/core/agent-orchestration.d.ts +31 -0
- package/dist/core/agent-orchestration.js +235 -0
- package/dist/core/permission-policy.d.ts +4 -0
- package/dist/core/permission-policy.js +21 -0
- package/dist/core/runtime.d.ts +6 -0
- package/dist/core/runtime.js +20 -2
- package/dist/core/team-mailbox.d.ts +54 -0
- package/dist/core/team-mailbox.js +212 -0
- package/dist/core/team-ownership.d.ts +84 -0
- package/dist/core/team-ownership.js +505 -0
- package/dist/core/tool-execution-scheduler.js +6 -1
- package/dist/core/transcript-event.d.ts +5 -1
- package/dist/core/transcript-event.js +12 -0
- package/dist/persistence/claude-job-store.d.ts +58 -0
- package/dist/persistence/claude-job-store.js +483 -1
- package/dist/persistence/claude-transcript-store.d.ts +2 -1
- package/dist/persistence/claude-transcript-store.js +2 -10
- package/dist/persistence/native-team-mailbox.d.ts +68 -0
- package/dist/persistence/native-team-mailbox.js +542 -0
- package/dist/persistence/native-team-store.d.ts +34 -0
- package/dist/persistence/native-team-store.js +257 -0
- package/dist/persistence/native-transcript-reader.d.ts +53 -0
- package/dist/persistence/native-transcript-reader.js +283 -0
- package/dist/persistence/native-transcript-store.d.ts +69 -0
- package/dist/persistence/native-transcript-store.js +321 -0
- package/dist/persistence/subagent-lifecycle-store.d.ts +41 -8
- package/dist/persistence/subagent-lifecycle-store.js +373 -146
- package/dist/persistence/transcript-file-append.d.ts +2 -0
- package/dist/persistence/transcript-file-append.js +10 -0
- package/dist/platform/exclusive-file-lease.d.ts +3 -0
- package/dist/platform/exclusive-file-lease.js +3 -0
- package/dist/tools/claude-capabilities.d.ts +4 -0
- package/dist/tools/claude-capabilities.js +18 -2
- package/dist/tools/team-lead-tools.d.ts +19 -0
- package/dist/tools/team-lead-tools.js +479 -0
- package/dist/tools/team-member-tools.d.ts +21 -0
- package/dist/tools/team-member-tools.js +259 -0
- package/package.json +1 -1
- package/dist/cli/tui/fullscreen-renderer.d.ts +0 -14
- package/dist/cli/tui/fullscreen-renderer.js +0 -13
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type ComposerKeyProjection } from './composer-key-router.js';
|
|
2
|
+
import type { ComposerEditorState } from './composer-editor.js';
|
|
3
|
+
export type TuiScrollIntent = 'page-older' | 'page-newer' | 'half-page-older' | 'half-page-newer' | 'line-older' | 'line-newer' | 'none';
|
|
4
|
+
export type TuiCancellationTarget = 'permission' | 'plan-approval' | 'question' | 'elicitation' | 'elicitation-url-waiting' | 'elicitation-options' | 'file-picker' | 'command-palette';
|
|
5
|
+
export type TuiInteractionLayer = {
|
|
6
|
+
readonly kind: 'none';
|
|
7
|
+
} | {
|
|
8
|
+
readonly kind: 'pending-prefix';
|
|
9
|
+
} | {
|
|
10
|
+
readonly kind: 'cancelable';
|
|
11
|
+
readonly target: TuiCancellationTarget;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: 'delegated';
|
|
14
|
+
readonly target: 'session-picker' | 'menu';
|
|
15
|
+
};
|
|
16
|
+
export type TuiCallerIntent = 'none' | 'toggle-shortcuts' | 'open-agents' | 'implicit-newline';
|
|
17
|
+
export interface TuiInteractionInput {
|
|
18
|
+
globalIntent: 'none' | 'exit' | 'suspend';
|
|
19
|
+
scrollIntent: TuiScrollIntent;
|
|
20
|
+
action: string | undefined;
|
|
21
|
+
composerKey: ComposerKeyProjection;
|
|
22
|
+
timestamp: number;
|
|
23
|
+
callerIntent: TuiCallerIntent;
|
|
24
|
+
}
|
|
25
|
+
export interface TuiInteractionSnapshot {
|
|
26
|
+
suspensionPending: boolean;
|
|
27
|
+
exitConfirmationArmed: boolean;
|
|
28
|
+
layer: TuiInteractionLayer;
|
|
29
|
+
busy: boolean;
|
|
30
|
+
viewport: {
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
offset: number;
|
|
33
|
+
maxOffset: number;
|
|
34
|
+
pageRows: number;
|
|
35
|
+
};
|
|
36
|
+
composer: {
|
|
37
|
+
mode: 'readline' | 'vim-insert' | 'vim-normal';
|
|
38
|
+
editor: ComposerEditorState;
|
|
39
|
+
lastCancelAtMs: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export type TuiInteractionEffect = {
|
|
43
|
+
readonly kind: 'arm-exit-confirmation';
|
|
44
|
+
} | {
|
|
45
|
+
readonly kind: 'dismiss-exit-confirmation';
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: 'exit-application';
|
|
48
|
+
} | {
|
|
49
|
+
readonly kind: 'request-process-suspension';
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: 'cancel-tui-layer';
|
|
52
|
+
readonly target: TuiCancellationTarget;
|
|
53
|
+
} | {
|
|
54
|
+
readonly kind: 'set-transcript-scroll-offset';
|
|
55
|
+
readonly offset: number;
|
|
56
|
+
} | {
|
|
57
|
+
readonly kind: 'interrupt-turn';
|
|
58
|
+
} | {
|
|
59
|
+
readonly kind: 'set-vim-insert-mode';
|
|
60
|
+
readonly insert: boolean;
|
|
61
|
+
} | {
|
|
62
|
+
readonly kind: 'set-composer-editor';
|
|
63
|
+
readonly editor: ComposerEditorState;
|
|
64
|
+
} | {
|
|
65
|
+
readonly kind: 'clear-composer';
|
|
66
|
+
} | {
|
|
67
|
+
readonly kind: 'record-composer-cancel';
|
|
68
|
+
readonly timestamp: number;
|
|
69
|
+
};
|
|
70
|
+
export type TuiInteractionResult = {
|
|
71
|
+
readonly disposition: 'handled';
|
|
72
|
+
readonly effects: readonly TuiInteractionEffect[];
|
|
73
|
+
} | {
|
|
74
|
+
readonly disposition: 'delegated';
|
|
75
|
+
readonly effects: readonly TuiInteractionEffect[];
|
|
76
|
+
};
|
|
77
|
+
export declare function routeTuiInteraction(snapshot: TuiInteractionSnapshot, input: TuiInteractionInput): TuiInteractionResult;
|
|
78
|
+
//# sourceMappingURL=tui-interaction-router.d.ts.map
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { routeComposerKey, } from './composer-key-router.js';
|
|
2
|
+
const handled = (effects = []) => ({
|
|
3
|
+
disposition: 'handled',
|
|
4
|
+
effects,
|
|
5
|
+
});
|
|
6
|
+
const delegated = (effects = []) => ({
|
|
7
|
+
disposition: 'delegated',
|
|
8
|
+
effects,
|
|
9
|
+
});
|
|
10
|
+
function finiteNonNegativeInteger(value) {
|
|
11
|
+
return Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
|
|
12
|
+
}
|
|
13
|
+
function isEscape(key) {
|
|
14
|
+
return key.escape || key.value === '\u001B';
|
|
15
|
+
}
|
|
16
|
+
function canUseCancelWindow(timestamp, previous) {
|
|
17
|
+
return Number.isFinite(timestamp) && Number.isFinite(previous);
|
|
18
|
+
}
|
|
19
|
+
export function routeTuiInteraction(snapshot, input) {
|
|
20
|
+
if (input.globalIntent === 'exit') {
|
|
21
|
+
return snapshot.exitConfirmationArmed
|
|
22
|
+
? handled([{ kind: 'exit-application' }])
|
|
23
|
+
: handled([{ kind: 'clear-composer' }, { kind: 'arm-exit-confirmation' }]);
|
|
24
|
+
}
|
|
25
|
+
if (input.globalIntent === 'suspend') {
|
|
26
|
+
return snapshot.suspensionPending
|
|
27
|
+
? handled()
|
|
28
|
+
: handled([{ kind: 'request-process-suspension' }]);
|
|
29
|
+
}
|
|
30
|
+
const confirmationEffects = snapshot.exitConfirmationArmed
|
|
31
|
+
? [{ kind: 'dismiss-exit-confirmation' }]
|
|
32
|
+
: [];
|
|
33
|
+
if (snapshot.layer.kind === 'pending-prefix')
|
|
34
|
+
return handled(confirmationEffects);
|
|
35
|
+
if (snapshot.layer.kind === 'cancelable') {
|
|
36
|
+
if (isEscape(input.composerKey) || input.action === 'chat:cancel') {
|
|
37
|
+
return handled([
|
|
38
|
+
...confirmationEffects,
|
|
39
|
+
{ kind: 'cancel-tui-layer', target: snapshot.layer.target },
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
return delegated(confirmationEffects);
|
|
43
|
+
}
|
|
44
|
+
if (snapshot.layer.kind === 'delegated')
|
|
45
|
+
return delegated(confirmationEffects);
|
|
46
|
+
const viewport = snapshot.viewport;
|
|
47
|
+
if (viewport.enabled && input.scrollIntent !== 'none') {
|
|
48
|
+
const pageRows = finiteNonNegativeInteger(viewport.pageRows);
|
|
49
|
+
const offset = finiteNonNegativeInteger(viewport.offset);
|
|
50
|
+
const maxOffset = finiteNonNegativeInteger(viewport.maxOffset);
|
|
51
|
+
const halfPage = Math.max(1, Math.trunc(pageRows / 2));
|
|
52
|
+
const movement = input.scrollIntent === 'page-older' || input.scrollIntent === 'page-newer'
|
|
53
|
+
? pageRows
|
|
54
|
+
: input.scrollIntent === 'half-page-older' ||
|
|
55
|
+
input.scrollIntent === 'half-page-newer'
|
|
56
|
+
? halfPage
|
|
57
|
+
: 1;
|
|
58
|
+
const older = input.scrollIntent === 'page-older' ||
|
|
59
|
+
input.scrollIntent === 'half-page-older' ||
|
|
60
|
+
input.scrollIntent === 'line-older';
|
|
61
|
+
const nextOffset = Math.min(maxOffset, Math.max(0, offset + (older ? movement : -movement)));
|
|
62
|
+
return handled([
|
|
63
|
+
...confirmationEffects,
|
|
64
|
+
{ kind: 'set-transcript-scroll-offset', offset: nextOffset },
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
if (snapshot.busy) {
|
|
68
|
+
if (input.action === 'app:toggleTranscript' ||
|
|
69
|
+
input.action === 'task:background') {
|
|
70
|
+
return delegated(confirmationEffects);
|
|
71
|
+
}
|
|
72
|
+
if (input.action === 'chat:cancel')
|
|
73
|
+
return handled([...confirmationEffects, { kind: 'interrupt-turn' }]);
|
|
74
|
+
return handled(confirmationEffects);
|
|
75
|
+
}
|
|
76
|
+
if (input.callerIntent !== 'none')
|
|
77
|
+
return delegated(confirmationEffects);
|
|
78
|
+
if (snapshot.composer.mode === 'vim-normal') {
|
|
79
|
+
if (input.composerKey.value === 'i' || input.composerKey.value === 'a') {
|
|
80
|
+
return handled([
|
|
81
|
+
...confirmationEffects,
|
|
82
|
+
{ kind: 'set-vim-insert-mode', insert: true },
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
if (input.composerKey.left || input.composerKey.right) {
|
|
86
|
+
const transition = routeComposerKey(snapshot.composer.editor, input.composerKey);
|
|
87
|
+
if (transition.kind === 'edit')
|
|
88
|
+
return handled([
|
|
89
|
+
...confirmationEffects,
|
|
90
|
+
{ kind: 'set-composer-editor', editor: transition.editor },
|
|
91
|
+
]);
|
|
92
|
+
}
|
|
93
|
+
return handled(confirmationEffects);
|
|
94
|
+
}
|
|
95
|
+
if (snapshot.composer.mode === 'vim-insert' && isEscape(input.composerKey)) {
|
|
96
|
+
return handled([
|
|
97
|
+
...confirmationEffects,
|
|
98
|
+
{ kind: 'set-vim-insert-mode', insert: false },
|
|
99
|
+
]);
|
|
100
|
+
}
|
|
101
|
+
if (input.action === 'chat:cancel') {
|
|
102
|
+
const timestamp = input.timestamp;
|
|
103
|
+
const cancelDelta = timestamp - snapshot.composer.lastCancelAtMs;
|
|
104
|
+
const shouldClear = canUseCancelWindow(timestamp, snapshot.composer.lastCancelAtMs) &&
|
|
105
|
+
cancelDelta >= 0 &&
|
|
106
|
+
cancelDelta <= 500;
|
|
107
|
+
return handled([
|
|
108
|
+
...confirmationEffects,
|
|
109
|
+
...(shouldClear ? [{ kind: 'clear-composer' }] : []),
|
|
110
|
+
...(Number.isFinite(timestamp)
|
|
111
|
+
? [{ kind: 'record-composer-cancel', timestamp }]
|
|
112
|
+
: []),
|
|
113
|
+
]);
|
|
114
|
+
}
|
|
115
|
+
if (input.action !== undefined)
|
|
116
|
+
return delegated(confirmationEffects);
|
|
117
|
+
const transition = routeComposerKey(snapshot.composer.editor, input.composerKey);
|
|
118
|
+
if (transition.kind === 'edit')
|
|
119
|
+
return handled([
|
|
120
|
+
...confirmationEffects,
|
|
121
|
+
{ kind: 'set-composer-editor', editor: transition.editor },
|
|
122
|
+
]);
|
|
123
|
+
if (transition.kind === 'cancel')
|
|
124
|
+
return handled([...confirmationEffects, { kind: 'clear-composer' }]);
|
|
125
|
+
return delegated(confirmationEffects);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=tui-interaction-router.js.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { TranscriptItem } from './transcript-presentation.js';
|
|
2
|
+
import { type TranscriptPresentationEntry } from './transcript-presentation.js';
|
|
3
|
+
import { appendTuiHistory, createTuiHistoryChange, type TuiHistoryChange } from './transcript-window-model.js';
|
|
4
|
+
import type { TuiPresentationEnvironment } from './presentation-environment.js';
|
|
5
|
+
export { appendTuiHistory, createTuiHistoryChange };
|
|
6
|
+
export type { TuiHistoryChange };
|
|
7
|
+
export type TuiRendererMode = 'default' | 'fullscreen';
|
|
8
|
+
/**
|
|
9
|
+
* Inputs for the pure TUI view projection. `history` is the live transcript
|
|
10
|
+
* that grows while the session runs; `initialHistory` is the transcript loaded
|
|
11
|
+
* before rendering and decides the resumed/fresh identity. `resume` is true
|
|
12
|
+
* only when the session was opened through a resume option.
|
|
13
|
+
*/
|
|
14
|
+
export interface TuiViewInput {
|
|
15
|
+
readonly initialHistory: readonly TranscriptItem[];
|
|
16
|
+
readonly history: readonly TranscriptItem[];
|
|
17
|
+
readonly resume: boolean;
|
|
18
|
+
readonly fixedViewport: boolean;
|
|
19
|
+
readonly screenReader: boolean;
|
|
20
|
+
readonly rows: number | undefined;
|
|
21
|
+
readonly width: number;
|
|
22
|
+
readonly scrollOffset: number;
|
|
23
|
+
readonly detailedTranscript: boolean;
|
|
24
|
+
readonly historyChange?: TuiHistoryChange;
|
|
25
|
+
}
|
|
26
|
+
export interface TuiViewModel {
|
|
27
|
+
readonly transcriptEntries: readonly TranscriptPresentationEntry[];
|
|
28
|
+
readonly transcriptPageRows: number;
|
|
29
|
+
readonly maxTranscriptScrollOffset: number;
|
|
30
|
+
readonly resumed: boolean;
|
|
31
|
+
readonly freshSession: boolean;
|
|
32
|
+
readonly hasConversationHistory: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare function projectTuiView(input: TuiViewInput, previous?: TuiViewModel): TuiViewModel;
|
|
35
|
+
export interface TuiScreenSurfaceModels {
|
|
36
|
+
readonly sessionPicker: unknown;
|
|
37
|
+
readonly priority: unknown;
|
|
38
|
+
readonly secondary: unknown;
|
|
39
|
+
readonly overlay: unknown;
|
|
40
|
+
}
|
|
41
|
+
export interface TuiScreenInput<Surfaces extends TuiScreenSurfaceModels = TuiScreenSurfaceModels> {
|
|
42
|
+
readonly presentation: TuiPresentationEnvironment;
|
|
43
|
+
readonly conversation: {
|
|
44
|
+
readonly initialHistory: readonly TranscriptItem[];
|
|
45
|
+
readonly history: readonly TranscriptItem[];
|
|
46
|
+
readonly historyChange?: TuiHistoryChange;
|
|
47
|
+
readonly resumeRequested: boolean;
|
|
48
|
+
readonly scrollOffset: number;
|
|
49
|
+
readonly detailed: boolean;
|
|
50
|
+
readonly activeText: string;
|
|
51
|
+
readonly activeThinking: string;
|
|
52
|
+
};
|
|
53
|
+
readonly sessionId: string | null;
|
|
54
|
+
readonly surfaces: {
|
|
55
|
+
readonly sessionPicker?: Surfaces['sessionPicker'];
|
|
56
|
+
readonly priority?: Surfaces['priority'];
|
|
57
|
+
readonly secondary?: Surfaces['secondary'];
|
|
58
|
+
readonly overlays: readonly Surfaces['overlay'][];
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export interface TuiTranscriptScreenModel {
|
|
62
|
+
readonly entries: readonly TranscriptPresentationEntry[];
|
|
63
|
+
readonly pageRows: number;
|
|
64
|
+
readonly maxScrollOffset: number;
|
|
65
|
+
readonly scrollOffset: number;
|
|
66
|
+
readonly readingMode: 'normal' | 'audit' | 'screen-reader';
|
|
67
|
+
readonly active: {
|
|
68
|
+
readonly text: string;
|
|
69
|
+
readonly thinking: string;
|
|
70
|
+
readonly visible: boolean;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export type TuiScreenForeground<Surfaces extends TuiScreenSurfaceModels> = {
|
|
74
|
+
readonly kind: 'priority';
|
|
75
|
+
readonly surface: Surfaces['priority'];
|
|
76
|
+
} | {
|
|
77
|
+
readonly kind: 'secondary';
|
|
78
|
+
readonly surface: Surfaces['secondary'];
|
|
79
|
+
} | {
|
|
80
|
+
readonly kind: 'compose';
|
|
81
|
+
readonly overlays: readonly Surfaces['overlay'][];
|
|
82
|
+
};
|
|
83
|
+
export type TuiScreenBody<Surfaces extends TuiScreenSurfaceModels> = {
|
|
84
|
+
readonly kind: 'session-picker';
|
|
85
|
+
readonly surface: Surfaces['sessionPicker'];
|
|
86
|
+
} | {
|
|
87
|
+
readonly kind: 'conversation';
|
|
88
|
+
readonly intro: 'welcome' | 'identity' | 'none';
|
|
89
|
+
readonly sessionLabel?: string;
|
|
90
|
+
readonly resumed: boolean;
|
|
91
|
+
readonly freshSession: boolean;
|
|
92
|
+
readonly hasConversationHistory: boolean;
|
|
93
|
+
readonly transcript: TuiTranscriptScreenModel;
|
|
94
|
+
readonly foreground: TuiScreenForeground<Surfaces>;
|
|
95
|
+
};
|
|
96
|
+
export interface TuiScreenModel<Surfaces extends TuiScreenSurfaceModels = TuiScreenSurfaceModels> {
|
|
97
|
+
readonly presentation: TuiPresentationEnvironment;
|
|
98
|
+
readonly body: TuiScreenBody<Surfaces>;
|
|
99
|
+
}
|
|
100
|
+
export declare function projectTuiScreen<Surfaces extends TuiScreenSurfaceModels>(input: TuiScreenInput<Surfaces>, previous?: TuiScreenModel<Surfaces>): TuiScreenModel<Surfaces>;
|
|
101
|
+
export interface TuiRendererInput {
|
|
102
|
+
configured: TuiRendererMode;
|
|
103
|
+
explicitlyConfigured: boolean;
|
|
104
|
+
interactiveTty: boolean;
|
|
105
|
+
screenReader: boolean;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Resolves the renderer for an interactive session. Fullscreen is the default
|
|
109
|
+
* for interactive TTY execution; classic remains the fallback for screen-reader
|
|
110
|
+
* and non-interactive paths, and an explicit renderer configuration is always
|
|
111
|
+
* honored over the default.
|
|
112
|
+
*/
|
|
113
|
+
export declare function resolveTuiRenderer(input: TuiRendererInput): TuiRendererMode;
|
|
114
|
+
//# sourceMappingURL=tui-screen-model.d.ts.map
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { FULLSCREEN_TRANSCRIPT_RESERVED_ROWS } from './transcript-viewport.js';
|
|
2
|
+
import { appendTuiHistory, createTuiHistoryChange, projectTranscriptWindow, } from './transcript-window-model.js';
|
|
3
|
+
export { appendTuiHistory, createTuiHistoryChange };
|
|
4
|
+
/**
|
|
5
|
+
* Pure, deterministic TUI projection. It classifies the session identity
|
|
6
|
+
* (fresh/resumed/started) and projects the rendered transcript exactly as the
|
|
7
|
+
* fullscreen renderer needs it, preserving TranscriptItem identity and order
|
|
8
|
+
* and the fullscreen suffix/window behavior. It never writes to transcripts or
|
|
9
|
+
* alters runtime events.
|
|
10
|
+
*/
|
|
11
|
+
const transcriptState = Symbol('transcript-window-state');
|
|
12
|
+
const classificationState = Symbol('tui-classification-state');
|
|
13
|
+
const selectionState = Symbol('tui-selection-state');
|
|
14
|
+
export function projectTuiView(input, previous) {
|
|
15
|
+
// Startup diagnostics are useful before the first prompt, but they are not
|
|
16
|
+
// conversation history and must not suppress the new-session welcome panel.
|
|
17
|
+
// Only real user/assistant transcript entries start a conversation; every
|
|
18
|
+
// other kind (thinking, context, tool, shell, notices, results, and so on)
|
|
19
|
+
// is operational bookkeeping that must not hide the fresh-session welcome.
|
|
20
|
+
const isRealConversation = (item) => item.kind === 'user' || item.kind === 'assistant';
|
|
21
|
+
const priorState = previous?.[transcriptState];
|
|
22
|
+
const priorClassification = previous?.[classificationState];
|
|
23
|
+
// Fullscreen projects only the newest transcript tail that fits the fixed
|
|
24
|
+
// viewport, leaving the composer/status chrome intact and keeping the active
|
|
25
|
+
// stream visible. Classic and screen-reader modes always render the full
|
|
26
|
+
// history exactly as before.
|
|
27
|
+
const mode = input.screenReader
|
|
28
|
+
? 'screen-reader'
|
|
29
|
+
: input.detailedTranscript
|
|
30
|
+
? 'audit'
|
|
31
|
+
: 'normal';
|
|
32
|
+
const transcriptPageRows = Math.max(2, (input.rows ?? 0) - FULLSCREEN_TRANSCRIPT_RESERVED_ROWS);
|
|
33
|
+
const retained = projectTranscriptWindow({
|
|
34
|
+
history: input.history,
|
|
35
|
+
mode,
|
|
36
|
+
width: input.width,
|
|
37
|
+
pageRows: transcriptPageRows,
|
|
38
|
+
scrollOffset: input.scrollOffset,
|
|
39
|
+
revision: input.historyChange?.revision ?? 0,
|
|
40
|
+
bounded: input.fixedViewport && !input.screenReader,
|
|
41
|
+
}, priorState, input.historyChange);
|
|
42
|
+
const classificationCompatible = previous !== undefined &&
|
|
43
|
+
priorClassification !== undefined &&
|
|
44
|
+
priorState !== undefined &&
|
|
45
|
+
priorClassification.history === priorState.history &&
|
|
46
|
+
priorClassification.revision === priorState.revision &&
|
|
47
|
+
priorClassification.initialHistory === input.initialHistory &&
|
|
48
|
+
priorClassification.resume === input.resume;
|
|
49
|
+
const sameClassification = classificationCompatible && retained.transition === 'same';
|
|
50
|
+
const appendedClassification = classificationCompatible && retained.transition === 'append';
|
|
51
|
+
// The original loaded transcript decides whether the session was resumed,
|
|
52
|
+
// separately from the live history that grows while the session runs.
|
|
53
|
+
const resumed = sameClassification || appendedClassification
|
|
54
|
+
? priorClassification.resumed
|
|
55
|
+
: input.resume && input.initialHistory.some(isRealConversation);
|
|
56
|
+
const hasConversationHistory = sameClassification
|
|
57
|
+
? priorClassification.hasConversationHistory
|
|
58
|
+
: appendedClassification
|
|
59
|
+
? priorClassification.hasConversationHistory ||
|
|
60
|
+
input.history.slice(priorState.history.length).some(isRealConversation)
|
|
61
|
+
: input.history.some(isRealConversation);
|
|
62
|
+
// A session is resumed only when it was opened through `resume` and the
|
|
63
|
+
// original transcript already contained real conversation content. Supplying
|
|
64
|
+
// a session ID alone with an empty transcript keeps the session fresh, so the
|
|
65
|
+
// full welcome panel renders and the compact identity stays hidden until real
|
|
66
|
+
// conversation content appears.
|
|
67
|
+
const freshSession = !resumed && !hasConversationHistory;
|
|
68
|
+
const fullEntries = retained.allEntries;
|
|
69
|
+
const maxTranscriptScrollOffset = retained.maxOffset;
|
|
70
|
+
const projectedEntries = input.fixedViewport && !input.screenReader ? retained.entries : fullEntries;
|
|
71
|
+
const priorSelection = previous?.[selectionState];
|
|
72
|
+
const transcriptEntries = priorSelection &&
|
|
73
|
+
priorSelection.offset === input.scrollOffset &&
|
|
74
|
+
priorSelection.width === input.width &&
|
|
75
|
+
priorSelection.mode === mode &&
|
|
76
|
+
priorSelection.fixedViewport === input.fixedViewport &&
|
|
77
|
+
priorSelection.screenReader === input.screenReader &&
|
|
78
|
+
priorSelection.pageRows === transcriptPageRows &&
|
|
79
|
+
priorState === retained.state
|
|
80
|
+
? priorSelection.entries
|
|
81
|
+
: projectedEntries;
|
|
82
|
+
const result = {
|
|
83
|
+
transcriptEntries,
|
|
84
|
+
transcriptPageRows,
|
|
85
|
+
maxTranscriptScrollOffset,
|
|
86
|
+
resumed,
|
|
87
|
+
freshSession,
|
|
88
|
+
hasConversationHistory,
|
|
89
|
+
};
|
|
90
|
+
Object.defineProperty(result, transcriptState, {
|
|
91
|
+
value: retained.state,
|
|
92
|
+
enumerable: false,
|
|
93
|
+
});
|
|
94
|
+
Object.defineProperty(result, classificationState, {
|
|
95
|
+
value: {
|
|
96
|
+
initialHistory: input.initialHistory,
|
|
97
|
+
resume: input.resume,
|
|
98
|
+
history: input.history,
|
|
99
|
+
revision: retained.state.revision,
|
|
100
|
+
resumed,
|
|
101
|
+
hasConversationHistory,
|
|
102
|
+
},
|
|
103
|
+
enumerable: false,
|
|
104
|
+
});
|
|
105
|
+
Object.defineProperty(result, selectionState, {
|
|
106
|
+
value: {
|
|
107
|
+
offset: input.scrollOffset,
|
|
108
|
+
width: input.width,
|
|
109
|
+
mode,
|
|
110
|
+
fixedViewport: input.fixedViewport,
|
|
111
|
+
screenReader: input.screenReader,
|
|
112
|
+
pageRows: transcriptPageRows,
|
|
113
|
+
entries: transcriptEntries,
|
|
114
|
+
},
|
|
115
|
+
enumerable: false,
|
|
116
|
+
});
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
const screenViewState = Symbol('tui-screen-view-state');
|
|
120
|
+
export function projectTuiScreen(input, previous) {
|
|
121
|
+
const conversation = input.conversation;
|
|
122
|
+
const priorView = previous?.[screenViewState];
|
|
123
|
+
const view = projectTuiView({
|
|
124
|
+
initialHistory: conversation.initialHistory,
|
|
125
|
+
history: conversation.history,
|
|
126
|
+
resume: conversation.resumeRequested,
|
|
127
|
+
fixedViewport: input.presentation.fixedViewport,
|
|
128
|
+
screenReader: input.presentation.screenReader,
|
|
129
|
+
rows: input.presentation.viewport.rows,
|
|
130
|
+
width: input.presentation.viewport.columns,
|
|
131
|
+
scrollOffset: conversation.scrollOffset,
|
|
132
|
+
detailedTranscript: conversation.detailed,
|
|
133
|
+
...(conversation.historyChange === undefined
|
|
134
|
+
? {}
|
|
135
|
+
: { historyChange: conversation.historyChange }),
|
|
136
|
+
}, priorView);
|
|
137
|
+
const readingMode = input.presentation.screenReader
|
|
138
|
+
? 'screen-reader'
|
|
139
|
+
: conversation.detailed
|
|
140
|
+
? 'audit'
|
|
141
|
+
: 'normal';
|
|
142
|
+
const scrollOffset = Math.min(Math.max(0, conversation.scrollOffset), view.maxTranscriptScrollOffset);
|
|
143
|
+
const transcript = {
|
|
144
|
+
entries: view.transcriptEntries,
|
|
145
|
+
pageRows: view.transcriptPageRows,
|
|
146
|
+
maxScrollOffset: view.maxTranscriptScrollOffset,
|
|
147
|
+
scrollOffset,
|
|
148
|
+
readingMode,
|
|
149
|
+
active: {
|
|
150
|
+
text: conversation.activeText,
|
|
151
|
+
thinking: conversation.activeThinking,
|
|
152
|
+
visible: scrollOffset === 0,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
let body;
|
|
156
|
+
if (input.surfaces.sessionPicker !== undefined) {
|
|
157
|
+
body = { kind: 'session-picker', surface: input.surfaces.sessionPicker };
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
const intro = input.presentation.screenReader
|
|
161
|
+
? 'none'
|
|
162
|
+
: view.freshSession
|
|
163
|
+
? 'welcome'
|
|
164
|
+
: !view.resumed && view.hasConversationHistory
|
|
165
|
+
? 'identity'
|
|
166
|
+
: 'none';
|
|
167
|
+
const foreground = input.surfaces.priority !== undefined
|
|
168
|
+
? { kind: 'priority', surface: input.surfaces.priority }
|
|
169
|
+
: input.surfaces.secondary !== undefined
|
|
170
|
+
? { kind: 'secondary', surface: input.surfaces.secondary }
|
|
171
|
+
: { kind: 'compose', overlays: input.surfaces.overlays };
|
|
172
|
+
body = {
|
|
173
|
+
kind: 'conversation',
|
|
174
|
+
intro,
|
|
175
|
+
...(input.sessionId === null
|
|
176
|
+
? {}
|
|
177
|
+
: { sessionLabel: input.sessionId.slice(0, 8) }),
|
|
178
|
+
resumed: view.resumed,
|
|
179
|
+
freshSession: view.freshSession,
|
|
180
|
+
hasConversationHistory: view.hasConversationHistory,
|
|
181
|
+
transcript,
|
|
182
|
+
foreground,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const result = {
|
|
186
|
+
presentation: input.presentation,
|
|
187
|
+
body,
|
|
188
|
+
};
|
|
189
|
+
Object.defineProperty(result, screenViewState, {
|
|
190
|
+
value: view,
|
|
191
|
+
enumerable: false,
|
|
192
|
+
});
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Resolves the renderer for an interactive session. Fullscreen is the default
|
|
197
|
+
* for interactive TTY execution; classic remains the fallback for screen-reader
|
|
198
|
+
* and non-interactive paths, and an explicit renderer configuration is always
|
|
199
|
+
* honored over the default.
|
|
200
|
+
*/
|
|
201
|
+
export function resolveTuiRenderer(input) {
|
|
202
|
+
if (input.screenReader)
|
|
203
|
+
return 'default';
|
|
204
|
+
if (!input.interactiveTty)
|
|
205
|
+
return 'default';
|
|
206
|
+
if (input.explicitlyConfigured)
|
|
207
|
+
return input.configured;
|
|
208
|
+
return 'fullscreen';
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=tui-screen-model.js.map
|
|
@@ -1,50 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type TuiRendererMode = 'default' | 'fullscreen';
|
|
4
|
-
/**
|
|
5
|
-
* Inputs for the pure TUI view projection. `history` is the live transcript
|
|
6
|
-
* that grows while the session runs; `initialHistory` is the transcript loaded
|
|
7
|
-
* before rendering and decides the resumed/fresh identity. `resume` is true
|
|
8
|
-
* only when the session was opened through a resume option.
|
|
9
|
-
*/
|
|
10
|
-
export interface TuiViewInput {
|
|
11
|
-
initialHistory: readonly TranscriptItem[];
|
|
12
|
-
history: readonly TranscriptItem[];
|
|
13
|
-
resume: boolean;
|
|
14
|
-
fixedViewport: boolean;
|
|
15
|
-
screenReader: boolean;
|
|
16
|
-
rows: number | undefined;
|
|
17
|
-
width: number;
|
|
18
|
-
scrollOffset: number;
|
|
19
|
-
detailedTranscript: boolean;
|
|
20
|
-
}
|
|
21
|
-
export interface TuiViewModel {
|
|
22
|
-
transcriptEntries: readonly TranscriptPresentationEntry[];
|
|
23
|
-
transcriptPageRows: number;
|
|
24
|
-
maxTranscriptScrollOffset: number;
|
|
25
|
-
resumed: boolean;
|
|
26
|
-
freshSession: boolean;
|
|
27
|
-
hasConversationHistory: boolean;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Pure, deterministic TUI projection. It classifies the session identity
|
|
31
|
-
* (fresh/resumed/started) and projects the rendered transcript exactly as the
|
|
32
|
-
* fullscreen renderer needs it, preserving TranscriptItem identity and order
|
|
33
|
-
* and the fullscreen suffix/window behavior. It never writes to transcripts or
|
|
34
|
-
* alters runtime events.
|
|
35
|
-
*/
|
|
36
|
-
export declare function projectTuiView(input: TuiViewInput): TuiViewModel;
|
|
37
|
-
export interface TuiRendererInput {
|
|
38
|
-
configured: TuiRendererMode;
|
|
39
|
-
explicitlyConfigured: boolean;
|
|
40
|
-
interactiveTty: boolean;
|
|
41
|
-
screenReader: boolean;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Resolves the renderer for an interactive session. Fullscreen is the default
|
|
45
|
-
* for interactive TTY execution; classic remains the fallback for screen-reader
|
|
46
|
-
* and non-interactive paths, and an explicit renderer configuration is always
|
|
47
|
-
* honored over the default.
|
|
48
|
-
*/
|
|
49
|
-
export declare function resolveTuiRenderer(input: TuiRendererInput): TuiRendererMode;
|
|
1
|
+
export { appendTuiHistory, createTuiHistoryChange, projectTuiView, resolveTuiRenderer, } from './tui-screen-model.js';
|
|
2
|
+
export type { TuiHistoryChange, TuiRendererInput, TuiRendererMode, TuiViewInput, TuiViewModel, } from './tui-screen-model.js';
|
|
50
3
|
//# sourceMappingURL=tui-view-model.d.ts.map
|
|
@@ -1,69 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import { FULLSCREEN_TRANSCRIPT_RESERVED_ROWS, projectTranscriptPresentationTail, projectTranscriptPresentationWindow, transcriptPresentationLineCount, } from './transcript-viewport.js';
|
|
3
|
-
/**
|
|
4
|
-
* Pure, deterministic TUI projection. It classifies the session identity
|
|
5
|
-
* (fresh/resumed/started) and projects the rendered transcript exactly as the
|
|
6
|
-
* fullscreen renderer needs it, preserving TranscriptItem identity and order
|
|
7
|
-
* and the fullscreen suffix/window behavior. It never writes to transcripts or
|
|
8
|
-
* alters runtime events.
|
|
9
|
-
*/
|
|
10
|
-
export function projectTuiView(input) {
|
|
11
|
-
// Startup diagnostics are useful before the first prompt, but they are not
|
|
12
|
-
// conversation history and must not suppress the new-session welcome panel.
|
|
13
|
-
// Only real user/assistant transcript entries start a conversation; every
|
|
14
|
-
// other kind (thinking, context, tool, shell, notices, results, and so on)
|
|
15
|
-
// is operational bookkeeping that must not hide the fresh-session welcome.
|
|
16
|
-
const isRealConversation = (item) => item.kind === 'user' || item.kind === 'assistant';
|
|
17
|
-
// The original loaded transcript decides whether the session was resumed,
|
|
18
|
-
// separately from the live history that grows while the session runs.
|
|
19
|
-
const resumed = input.resume && input.initialHistory.some(isRealConversation);
|
|
20
|
-
const hasConversationHistory = input.history.some(isRealConversation);
|
|
21
|
-
// A session is resumed only when it was opened through `resume` and the
|
|
22
|
-
// original transcript already contained real conversation content. Supplying
|
|
23
|
-
// a session ID alone with an empty transcript keeps the session fresh, so the
|
|
24
|
-
// full welcome panel renders and the compact identity stays hidden until real
|
|
25
|
-
// conversation content appears.
|
|
26
|
-
const freshSession = !resumed && !hasConversationHistory;
|
|
27
|
-
// Fullscreen projects only the newest transcript tail that fits the fixed
|
|
28
|
-
// viewport, leaving the composer/status chrome intact and keeping the active
|
|
29
|
-
// stream visible. Classic and screen-reader modes always render the full
|
|
30
|
-
// history exactly as before.
|
|
31
|
-
const mode = input.screenReader
|
|
32
|
-
? 'screen-reader'
|
|
33
|
-
: input.detailedTranscript
|
|
34
|
-
? 'audit'
|
|
35
|
-
: 'normal';
|
|
36
|
-
const fullEntries = projectTranscriptPresentation(input.history, mode);
|
|
37
|
-
const transcriptPageRows = Math.max(2, (input.rows ?? 0) - FULLSCREEN_TRANSCRIPT_RESERVED_ROWS);
|
|
38
|
-
const maxTranscriptScrollOffset = Math.max(0, transcriptPresentationLineCount(fullEntries, input.width, mode) -
|
|
39
|
-
transcriptPageRows);
|
|
40
|
-
const transcriptEntries = input.fixedViewport && !input.screenReader
|
|
41
|
-
? input.scrollOffset > 0
|
|
42
|
-
? projectTranscriptPresentationWindow(fullEntries, transcriptPageRows, input.width, input.scrollOffset, mode)
|
|
43
|
-
: projectTranscriptPresentationTail(fullEntries, transcriptPageRows, input.width, mode)
|
|
44
|
-
: fullEntries;
|
|
45
|
-
return {
|
|
46
|
-
transcriptEntries,
|
|
47
|
-
transcriptPageRows,
|
|
48
|
-
maxTranscriptScrollOffset,
|
|
49
|
-
resumed,
|
|
50
|
-
freshSession,
|
|
51
|
-
hasConversationHistory,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Resolves the renderer for an interactive session. Fullscreen is the default
|
|
56
|
-
* for interactive TTY execution; classic remains the fallback for screen-reader
|
|
57
|
-
* and non-interactive paths, and an explicit renderer configuration is always
|
|
58
|
-
* honored over the default.
|
|
59
|
-
*/
|
|
60
|
-
export function resolveTuiRenderer(input) {
|
|
61
|
-
if (input.screenReader)
|
|
62
|
-
return 'default';
|
|
63
|
-
if (!input.interactiveTty)
|
|
64
|
-
return 'default';
|
|
65
|
-
if (input.explicitlyConfigured)
|
|
66
|
-
return input.configured;
|
|
67
|
-
return 'fullscreen';
|
|
68
|
-
}
|
|
1
|
+
export { appendTuiHistory, createTuiHistoryChange, projectTuiView, resolveTuiRenderer, } from './tui-screen-model.js';
|
|
69
2
|
//# sourceMappingURL=tui-view-model.js.map
|