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,360 @@
|
|
|
1
|
+
function integer(value, fallback = 0) {
|
|
2
|
+
if (!Number.isFinite(value))
|
|
3
|
+
return fallback;
|
|
4
|
+
return Math.trunc(value);
|
|
5
|
+
}
|
|
6
|
+
function selectedIndex(value, count, empty = -1) {
|
|
7
|
+
if (count <= 0)
|
|
8
|
+
return empty;
|
|
9
|
+
return Math.max(0, Math.min(count - 1, integer(value)));
|
|
10
|
+
}
|
|
11
|
+
function dashboardSelectedIndex(value, count) {
|
|
12
|
+
if (count <= 0)
|
|
13
|
+
return -1;
|
|
14
|
+
const normalized = integer(value, -1);
|
|
15
|
+
return normalized < 0 ? -1 : Math.min(count - 1, normalized);
|
|
16
|
+
}
|
|
17
|
+
function range(count) {
|
|
18
|
+
return { min: count > 0 ? 1 : 0, max: count };
|
|
19
|
+
}
|
|
20
|
+
function options(labels, selection) {
|
|
21
|
+
const selected = selectedIndex(selection, labels.length);
|
|
22
|
+
return {
|
|
23
|
+
options: labels.map((option, index) => ({
|
|
24
|
+
...option,
|
|
25
|
+
index: index + 1,
|
|
26
|
+
selected: index === selected,
|
|
27
|
+
})),
|
|
28
|
+
selectedIndex: selected,
|
|
29
|
+
range: range(labels.length),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function permissionRuleDescription(rule) {
|
|
33
|
+
const bashPrefix = /^Bash\((.*?)(?::\*| \*)\)$/u.exec(rule)?.[1]?.trim();
|
|
34
|
+
return bashPrefix ? `Any Bash command starting with ${bashPrefix}` : undefined;
|
|
35
|
+
}
|
|
36
|
+
function permissionScopeLabel(scope) {
|
|
37
|
+
return scope === 'local'
|
|
38
|
+
? 'From project local settings'
|
|
39
|
+
: scope === 'project'
|
|
40
|
+
? 'From project settings'
|
|
41
|
+
: 'From user settings';
|
|
42
|
+
}
|
|
43
|
+
export function projectTuiPermissionSurface(input) {
|
|
44
|
+
switch (input.kind) {
|
|
45
|
+
case 'tool-request': {
|
|
46
|
+
const selected = selectedIndex(input.selection, input.model.options.length);
|
|
47
|
+
return {
|
|
48
|
+
kind: input.kind,
|
|
49
|
+
heading: input.model.title,
|
|
50
|
+
tool: input.model,
|
|
51
|
+
options: input.model.options,
|
|
52
|
+
selectedIndex: selected,
|
|
53
|
+
range: range(input.model.options.length),
|
|
54
|
+
feedbackMode: input.feedbackMode,
|
|
55
|
+
feedback: input.feedback,
|
|
56
|
+
...(input.ruleEditor === undefined
|
|
57
|
+
? {}
|
|
58
|
+
: { ruleEditor: input.ruleEditor }),
|
|
59
|
+
actions: [
|
|
60
|
+
{
|
|
61
|
+
label: input.feedbackMode ? 'Enter to submit' : 'Enter to select',
|
|
62
|
+
key: 'Enter',
|
|
63
|
+
usesSelectionRange: !input.feedbackMode,
|
|
64
|
+
},
|
|
65
|
+
{ label: 'Tab to amend', key: 'Tab' },
|
|
66
|
+
],
|
|
67
|
+
cancellation: 'Escape to cancel',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
case 'recovery-request': {
|
|
71
|
+
const projected = options([{ label: 'Yes' }, { label: 'No' }], input.selection);
|
|
72
|
+
return {
|
|
73
|
+
kind: input.kind,
|
|
74
|
+
heading: input.heading ?? 'Retry interrupted tool?',
|
|
75
|
+
display: input.display,
|
|
76
|
+
question: 'Do you want to proceed?',
|
|
77
|
+
...projected,
|
|
78
|
+
feedbackMode: input.feedbackMode,
|
|
79
|
+
feedback: input.feedback,
|
|
80
|
+
feedbackPlaceholder: projected.selectedIndex === 0
|
|
81
|
+
? 'tell Praxis what to do next'
|
|
82
|
+
: 'tell Praxis what to do differently',
|
|
83
|
+
actions: [
|
|
84
|
+
{
|
|
85
|
+
label: input.feedbackMode ? 'Enter to submit' : 'Enter to confirm',
|
|
86
|
+
key: 'Enter',
|
|
87
|
+
usesSelectionRange: !input.feedbackMode,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
label: input.feedbackMode
|
|
91
|
+
? 'Tab to collapse'
|
|
92
|
+
: 'Tab to add feedback',
|
|
93
|
+
key: 'Tab',
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
cancellation: 'Escape to cancel',
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
case 'permission-dashboard': {
|
|
100
|
+
const tabs = [
|
|
101
|
+
'Recently denied',
|
|
102
|
+
'Allow',
|
|
103
|
+
'Ask',
|
|
104
|
+
'Deny',
|
|
105
|
+
'Workspace',
|
|
106
|
+
];
|
|
107
|
+
const tabIndex = selectedIndex(input.tabIndex, tabs.length);
|
|
108
|
+
const behavior = ['allow', 'ask', 'deny'][tabIndex - 1];
|
|
109
|
+
const normalizedQuery = input.query.trim().toLowerCase();
|
|
110
|
+
const matchingRules = behavior
|
|
111
|
+
? input.rules.filter((rule) => rule.behavior === behavior &&
|
|
112
|
+
(!normalizedQuery ||
|
|
113
|
+
rule.rule.toLowerCase().includes(normalizedQuery) ||
|
|
114
|
+
rule.scope.includes(normalizedQuery)))
|
|
115
|
+
: [];
|
|
116
|
+
const originalWorkspace = input.workspaceDirectories.find((directory) => directory.original);
|
|
117
|
+
const additionalWorkspaces = input.workspaceDirectories.filter((directory) => !directory.original);
|
|
118
|
+
let rows;
|
|
119
|
+
let rowCount;
|
|
120
|
+
if (tabIndex === 0) {
|
|
121
|
+
rows = input.recentDenied.map((action, index) => ({
|
|
122
|
+
index: index + 1,
|
|
123
|
+
label: action.display,
|
|
124
|
+
description: action.reason,
|
|
125
|
+
selected: index ===
|
|
126
|
+
dashboardSelectedIndex(input.selectedIndex, input.recentDenied.length),
|
|
127
|
+
id: action.id,
|
|
128
|
+
status: action.id === input.retryingDeniedId ? 'retrying' : 'denied',
|
|
129
|
+
}));
|
|
130
|
+
rowCount = rows.length;
|
|
131
|
+
}
|
|
132
|
+
else if (tabIndex === 4) {
|
|
133
|
+
rows = additionalWorkspaces.map((directory, index) => ({
|
|
134
|
+
index: index + 1,
|
|
135
|
+
label: directory.path,
|
|
136
|
+
selected: index ===
|
|
137
|
+
dashboardSelectedIndex(input.selectedIndex, additionalWorkspaces.length + 1),
|
|
138
|
+
original: false,
|
|
139
|
+
}));
|
|
140
|
+
rowCount = additionalWorkspaces.length + 1;
|
|
141
|
+
rows.push({
|
|
142
|
+
index: rowCount,
|
|
143
|
+
label: 'Add directory…',
|
|
144
|
+
selected: dashboardSelectedIndex(input.selectedIndex, rowCount) ===
|
|
145
|
+
rowCount - 1,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
rows = matchingRules.map((rule, index) => ({
|
|
150
|
+
index: index + 2,
|
|
151
|
+
label: rule.rule,
|
|
152
|
+
description: permissionScopeLabel(rule.scope),
|
|
153
|
+
selected: index + 1 ===
|
|
154
|
+
dashboardSelectedIndex(input.selectedIndex, matchingRules.length + 1),
|
|
155
|
+
}));
|
|
156
|
+
rows.unshift({
|
|
157
|
+
index: 1,
|
|
158
|
+
label: 'Add a new rule…',
|
|
159
|
+
selected: dashboardSelectedIndex(input.selectedIndex, matchingRules.length + 1) === 0,
|
|
160
|
+
});
|
|
161
|
+
rowCount = rows.length;
|
|
162
|
+
}
|
|
163
|
+
const normalizedSelected = dashboardSelectedIndex(input.selectedIndex, rowCount);
|
|
164
|
+
rows = rows.map((row, index) => ({
|
|
165
|
+
...row,
|
|
166
|
+
selected: index === normalizedSelected,
|
|
167
|
+
}));
|
|
168
|
+
const description = tabIndex === 0
|
|
169
|
+
? input.recentDenied.length === 0
|
|
170
|
+
? 'Commands denied by the auto mode classifier will appear here.'
|
|
171
|
+
: 'Commands recently denied by the auto mode classifier.'
|
|
172
|
+
: tabIndex === 1
|
|
173
|
+
? "Praxis Code won't ask before using allowed tools."
|
|
174
|
+
: tabIndex === 2
|
|
175
|
+
? 'Praxis Code will always ask for confirmation before using these tools.'
|
|
176
|
+
: tabIndex === 3
|
|
177
|
+
? 'Praxis Code will reject requests to use denied tools.'
|
|
178
|
+
: 'Praxis Code can read files in the workspace, and make edits when auto-accept edits is on.';
|
|
179
|
+
const emptyState = tabIndex === 0 && input.recentDenied.length === 0
|
|
180
|
+
? 'No recent denials.'
|
|
181
|
+
: tabIndex >= 1 && tabIndex <= 3 && matchingRules.length === 0
|
|
182
|
+
? 'No matching permission rules.'
|
|
183
|
+
: undefined;
|
|
184
|
+
return {
|
|
185
|
+
kind: input.kind,
|
|
186
|
+
heading: 'Permissions',
|
|
187
|
+
tabs: tabs.map((label, index) => ({
|
|
188
|
+
index,
|
|
189
|
+
label,
|
|
190
|
+
current: index === tabIndex,
|
|
191
|
+
})),
|
|
192
|
+
tabIndex,
|
|
193
|
+
description,
|
|
194
|
+
query: input.query,
|
|
195
|
+
rows,
|
|
196
|
+
...(emptyState ? { emptyState } : {}),
|
|
197
|
+
...(tabIndex === 4 && originalWorkspace
|
|
198
|
+
? {
|
|
199
|
+
originalWorkspace: {
|
|
200
|
+
index: 0,
|
|
201
|
+
label: originalWorkspace.path,
|
|
202
|
+
description: 'Original working directory',
|
|
203
|
+
selected: false,
|
|
204
|
+
original: true,
|
|
205
|
+
},
|
|
206
|
+
}
|
|
207
|
+
: {}),
|
|
208
|
+
range: range(rowCount),
|
|
209
|
+
actions: tabIndex === 0
|
|
210
|
+
? input.recentDenied.length === 0
|
|
211
|
+
? [
|
|
212
|
+
{
|
|
213
|
+
label: '←/→ to switch',
|
|
214
|
+
screenReaderLabel: 'Use left/right arrows to switch tabs',
|
|
215
|
+
},
|
|
216
|
+
]
|
|
217
|
+
: [
|
|
218
|
+
{
|
|
219
|
+
label: 'Enter to approve',
|
|
220
|
+
key: 'Enter',
|
|
221
|
+
usesSelectionRange: true,
|
|
222
|
+
},
|
|
223
|
+
{ label: 'r to retry', key: 'r' },
|
|
224
|
+
{
|
|
225
|
+
label: '↑/↓ to navigate',
|
|
226
|
+
screenReaderLabel: 'Use up/down arrows to navigate',
|
|
227
|
+
},
|
|
228
|
+
]
|
|
229
|
+
: normalizedSelected >= 0
|
|
230
|
+
? [
|
|
231
|
+
{
|
|
232
|
+
label: '↑/↓ navigate',
|
|
233
|
+
screenReaderLabel: 'Use up/down arrows to navigate',
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
label: 'Enter to select',
|
|
237
|
+
key: 'Enter',
|
|
238
|
+
usesSelectionRange: true,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
label: '←/→ to switch',
|
|
242
|
+
screenReaderLabel: 'Use left/right arrows to switch tabs',
|
|
243
|
+
},
|
|
244
|
+
]
|
|
245
|
+
: [
|
|
246
|
+
{
|
|
247
|
+
label: '←/→ to switch',
|
|
248
|
+
screenReaderLabel: 'Use left/right arrows to switch tabs',
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
label: '↓ to select',
|
|
252
|
+
screenReaderLabel: 'Use down arrow to select',
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
cancellation: 'Escape to cancel',
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
case 'permission-rule-input':
|
|
259
|
+
return {
|
|
260
|
+
kind: input.kind,
|
|
261
|
+
heading: `Add ${input.behavior} permission rule`,
|
|
262
|
+
behavior: input.behavior,
|
|
263
|
+
value: input.value,
|
|
264
|
+
description: 'Permission rules are a tool name, optionally followed by a specifier in parentheses.\ne.g., WebFetch or Bash(ls *)',
|
|
265
|
+
placeholder: 'Enter permission rule…',
|
|
266
|
+
actions: [{ label: 'Enter to submit', key: 'Enter' }],
|
|
267
|
+
cancellation: 'Escape to cancel',
|
|
268
|
+
};
|
|
269
|
+
case 'permission-scope': {
|
|
270
|
+
const projected = options([
|
|
271
|
+
{
|
|
272
|
+
label: 'Project settings (local)',
|
|
273
|
+
description: `Saved in ${input.settingsDirectory}/settings.local.json`,
|
|
274
|
+
value: 'local',
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
label: 'Project settings',
|
|
278
|
+
description: `Checked in at ${input.settingsDirectory}/settings.json`,
|
|
279
|
+
value: 'project',
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
label: 'User settings',
|
|
283
|
+
description: `Saved in ~/${input.settingsDirectory}/settings.json`,
|
|
284
|
+
value: 'user',
|
|
285
|
+
},
|
|
286
|
+
], input.selectedIndex);
|
|
287
|
+
return {
|
|
288
|
+
kind: input.kind,
|
|
289
|
+
heading: 'Where should this rule be saved?',
|
|
290
|
+
behavior: input.behavior,
|
|
291
|
+
rule: input.rule,
|
|
292
|
+
description: permissionRuleDescription(input.rule)
|
|
293
|
+
? `${input.rule} · ${permissionRuleDescription(input.rule)}`
|
|
294
|
+
: input.rule,
|
|
295
|
+
...projected,
|
|
296
|
+
actions: [
|
|
297
|
+
{
|
|
298
|
+
label: 'Enter to select',
|
|
299
|
+
key: 'Enter',
|
|
300
|
+
usesSelectionRange: true,
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
cancellation: 'Escape to cancel',
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
case 'permission-delete': {
|
|
307
|
+
const projected = options([{ label: 'Yes' }, { label: 'No' }], input.selectedIndex);
|
|
308
|
+
const ruleDescription = permissionRuleDescription(input.rule.rule);
|
|
309
|
+
return {
|
|
310
|
+
kind: input.kind,
|
|
311
|
+
heading: `Delete ${input.rule.behavior === 'allow' ? 'allowed' : input.rule.behavior === 'deny' ? 'denied' : 'ask'} tool?`,
|
|
312
|
+
rule: input.rule.rule,
|
|
313
|
+
...(ruleDescription ? { description: ruleDescription } : {}),
|
|
314
|
+
scope: permissionScopeLabel(input.rule.scope),
|
|
315
|
+
question: 'Are you sure you want to delete this permission rule?',
|
|
316
|
+
...projected,
|
|
317
|
+
actions: [
|
|
318
|
+
{
|
|
319
|
+
label: 'Enter to select',
|
|
320
|
+
key: 'Enter',
|
|
321
|
+
usesSelectionRange: true,
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
cancellation: 'Escape to cancel',
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
case 'workspace-directory-input':
|
|
328
|
+
return {
|
|
329
|
+
kind: input.kind,
|
|
330
|
+
heading: 'Add directory to workspace',
|
|
331
|
+
description: 'Praxis Code will be able to read files in this directory and make edits when auto-accept edits is on.\n\nEnter the path to the directory:',
|
|
332
|
+
value: input.value,
|
|
333
|
+
placeholder: 'Directory path…',
|
|
334
|
+
actions: [
|
|
335
|
+
{ label: 'Tab to complete', key: 'Tab' },
|
|
336
|
+
{ label: 'Enter to add', key: 'Enter' },
|
|
337
|
+
],
|
|
338
|
+
cancellation: 'Escape to cancel',
|
|
339
|
+
};
|
|
340
|
+
case 'workspace-directory-delete': {
|
|
341
|
+
const projected = options([{ label: 'Yes' }, { label: 'No' }], input.selectedIndex);
|
|
342
|
+
return {
|
|
343
|
+
kind: input.kind,
|
|
344
|
+
heading: 'Remove directory from workspace?',
|
|
345
|
+
path: input.path,
|
|
346
|
+
description: 'Praxis Code will no longer have access to files in this directory.',
|
|
347
|
+
...projected,
|
|
348
|
+
actions: [
|
|
349
|
+
{
|
|
350
|
+
label: 'Enter to confirm',
|
|
351
|
+
key: 'Enter',
|
|
352
|
+
usesSelectionRange: true,
|
|
353
|
+
},
|
|
354
|
+
],
|
|
355
|
+
cancellation: 'Escape to cancel',
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
//# sourceMappingURL=permission-surface-model.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { TuiPermissionSurfaceModel } from './permission-surface-model.js';
|
|
2
|
+
export declare function PermissionSurface({ model, width, screenReader, }: {
|
|
3
|
+
model: TuiPermissionSurfaceModel;
|
|
4
|
+
width: number;
|
|
5
|
+
screenReader: boolean;
|
|
6
|
+
}): import("react").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=permission-surface.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { DialogFrame } from './claude-style.js';
|
|
4
|
+
import { ToolPermissionDialog } from './tool-permission.js';
|
|
5
|
+
import { useTuiTheme } from './theme.js';
|
|
6
|
+
function selectionPrefix(selected, screenReader) {
|
|
7
|
+
if (screenReader)
|
|
8
|
+
return selected ? 'Selected: ' : '';
|
|
9
|
+
return selected ? '❯ ' : ' ';
|
|
10
|
+
}
|
|
11
|
+
function optionsBlock(options, screenReader, selectedRow) {
|
|
12
|
+
return options.map((option) => (_jsxs(Text, { ...(option.selected ? selectedRow : {}), children: [selectionPrefix(option.selected, screenReader), option.index, ". ", option.label, option.description ? `: ${option.description}` : ''] }, `${option.index}-${option.label}`)));
|
|
13
|
+
}
|
|
14
|
+
function rowsBlock(rows, screenReader, selectedRow) {
|
|
15
|
+
return rows.map((row) => (_jsxs(Text, { ...(row.selected ? selectedRow : {}), children: [selectionPrefix(row.selected, screenReader), row.index, ".", ' ', row.status === 'retrying'
|
|
16
|
+
? screenReader
|
|
17
|
+
? 'Retrying: '
|
|
18
|
+
: '✔ '
|
|
19
|
+
: row.status === 'denied'
|
|
20
|
+
? screenReader
|
|
21
|
+
? 'Denied: '
|
|
22
|
+
: '✘ '
|
|
23
|
+
: '', row.label, row.status === 'retrying' ? ' (retry)' : '', row.description ? ` ${row.description}` : ''] }, `${row.index}-${row.id ?? row.label}`)));
|
|
24
|
+
}
|
|
25
|
+
function Footer({ model, screenReader, }) {
|
|
26
|
+
const enter = 'range' in model && model.range.max > 0
|
|
27
|
+
? `Enter selection [1-${model.range.max}]`
|
|
28
|
+
: (model.actions[0]?.label ?? 'Enter');
|
|
29
|
+
const completeActions = model.actions.map((action) => screenReader
|
|
30
|
+
? action.usesSelectionRange === true &&
|
|
31
|
+
'range' in model &&
|
|
32
|
+
model.range.max > 0
|
|
33
|
+
? enter
|
|
34
|
+
: (action.screenReaderLabel ?? action.label)
|
|
35
|
+
: action.label);
|
|
36
|
+
return (_jsx(Text, { dimColor: true, children: screenReader
|
|
37
|
+
? `${completeActions.join(' · ')} · ${model.cancellation}`
|
|
38
|
+
: `${completeActions.join(' · ')} · ${model.cancellation.replace('Escape', 'Esc')}` }));
|
|
39
|
+
}
|
|
40
|
+
function Dashboard({ model, width, screenReader, }) {
|
|
41
|
+
const theme = useTuiTheme();
|
|
42
|
+
return (_jsxs(Box, { flexDirection: "column", width: Math.min(100, width), children: [!screenReader ? (_jsx(Text, { dimColor: true, children: '─'.repeat(Math.min(100, width)) })) : null, _jsx(Text, { ...theme.text.navigation, bold: true, children: model.heading }), _jsx(Text, { children: model.tabs.map((tab) => (_jsxs(Text, { ...(tab.current ? theme.text.selectedTab : {}), children: [screenReader && tab.current ? 'Current tab: ' : '', tab.label, ' '] }, tab.label))) }), _jsx(Text, { children: " " }), _jsx(Text, { children: model.description }), model.emptyState ? _jsx(Text, { dimColor: true, children: model.emptyState }) : null, model.tabIndex >= 1 && model.tabIndex <= 3 ? (_jsx(Box, { borderStyle: screenReader ? undefined : 'round', paddingX: 1, marginY: 1, children: _jsxs(Text, { ...(model.query ? {} : { dimColor: true }), children: [screenReader ? 'Search: ' : '⌕ ', model.query || 'Search…'] }) })) : (_jsx(Text, { children: " " })), model.originalWorkspace ? (_jsxs(Text, { children: [screenReader ? '' : ' - ', model.originalWorkspace.label, " (", model.originalWorkspace.description, ")"] })) : null, rowsBlock(model.rows, screenReader, theme.text.selectedRow), _jsx(Text, { children: " " }), _jsx(Footer, { model: model, screenReader: screenReader })] }));
|
|
43
|
+
}
|
|
44
|
+
function InputSurface({ heading, description, value, placeholder, model, screenReader, }) {
|
|
45
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: heading }), description.split('\n').map((line, index) => (_jsx(Text, { children: line || ' ' }, `${index}-${line}`))), _jsx(Box, { borderStyle: screenReader ? undefined : 'round', paddingX: screenReader ? 0 : 1, children: _jsx(Text, { ...(value ? {} : { dimColor: true }), children: value || placeholder }) }), _jsx(Footer, { model: model, screenReader: screenReader })] }));
|
|
46
|
+
}
|
|
47
|
+
export function PermissionSurface({ model, width, screenReader, }) {
|
|
48
|
+
const theme = useTuiTheme();
|
|
49
|
+
switch (model.kind) {
|
|
50
|
+
case 'tool-request':
|
|
51
|
+
return (_jsx(Box, { flexDirection: "column", width: Math.min(100, width), children: _jsx(ToolPermissionDialog, { model: model.tool, selection: model.selectedIndex, feedbackMode: model.feedbackMode, feedback: model.feedback, ...(model.ruleEditor === undefined
|
|
52
|
+
? {}
|
|
53
|
+
: { ruleEditor: model.ruleEditor }), screenReader: screenReader }) }));
|
|
54
|
+
case 'recovery-request':
|
|
55
|
+
return (_jsxs(DialogFrame, { title: model.heading, screenReader: screenReader, children: [_jsx(Box, { flexDirection: "column", paddingX: screenReader ? 0 : 1, paddingY: screenReader ? 0 : 1, children: _jsx(Text, { bold: true, children: model.display }) }), _jsx(Text, { children: model.question }), optionsBlock(model.options, screenReader, theme.text.selectedRow), model.feedbackMode ? (_jsxs(Text, { children: [screenReader ? 'Feedback: ' : '› ', model.feedback || model.feedbackPlaceholder] })) : null, _jsx(Footer, { model: model, screenReader: screenReader })] }));
|
|
56
|
+
case 'permission-dashboard':
|
|
57
|
+
return (_jsx(Dashboard, { model: model, width: width, screenReader: screenReader }));
|
|
58
|
+
case 'permission-rule-input':
|
|
59
|
+
case 'workspace-directory-input':
|
|
60
|
+
return (_jsx(InputSurface, { heading: model.heading, description: model.description, value: model.value, placeholder: model.placeholder, model: model, screenReader: screenReader }));
|
|
61
|
+
case 'permission-scope':
|
|
62
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: model.heading }), _jsx(Text, { dimColor: true, children: model.description }), optionsBlock(model.options, screenReader, theme.text.selectedRow), _jsx(Footer, { model: model, screenReader: screenReader })] }));
|
|
63
|
+
case 'permission-delete':
|
|
64
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: screenReader ? undefined : 'round', ...theme.surface.decision, paddingX: screenReader ? 0 : 1, marginTop: 1, children: [_jsx(Text, { ...theme.text.heading, ...theme.text.warning, bold: true, children: model.heading }), _jsx(Text, { children: model.rule }), model.description ? _jsx(Text, { dimColor: true, children: model.description }) : null, _jsx(Text, { dimColor: true, children: model.scope }), _jsx(Text, { children: model.question }), optionsBlock(model.options, screenReader, theme.text.selectedRow), _jsx(Footer, { model: model, screenReader: screenReader })] }));
|
|
65
|
+
case 'workspace-directory-delete':
|
|
66
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: model.heading }), _jsx(Text, { children: model.path }), _jsx(Text, { children: model.description }), optionsBlock(model.options, screenReader, theme.text.selectedRow), _jsx(Footer, { model: model, screenReader: screenReader })] }));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=permission-surface.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { TuiRendererMode } from './tui-view-model.js';
|
|
2
|
+
export type TuiPresentationKind = 'fullscreen' | 'classic' | 'screen-reader';
|
|
3
|
+
export interface TuiTerminalViewport {
|
|
4
|
+
readonly columns: number;
|
|
5
|
+
readonly rows: number | undefined;
|
|
6
|
+
readonly revision: number;
|
|
7
|
+
readonly source: 'terminal' | 'override' | 'fallback';
|
|
8
|
+
}
|
|
9
|
+
export interface TuiPresentationEnvironment {
|
|
10
|
+
readonly kind: TuiPresentationKind;
|
|
11
|
+
readonly viewport: TuiTerminalViewport;
|
|
12
|
+
readonly fixedViewport: boolean;
|
|
13
|
+
readonly screenReader: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface TuiPresentationEnvironmentInput {
|
|
16
|
+
readonly renderer: TuiRendererMode;
|
|
17
|
+
readonly screenReader: boolean;
|
|
18
|
+
readonly viewportOverride?: Readonly<{
|
|
19
|
+
columns?: number;
|
|
20
|
+
rows?: number;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export interface TuiInkRenderOptions {
|
|
24
|
+
readonly incrementalRendering: boolean;
|
|
25
|
+
readonly alternateScreen: boolean;
|
|
26
|
+
readonly isScreenReaderEnabled: boolean;
|
|
27
|
+
readonly maxFps: number;
|
|
28
|
+
readonly concurrent: false;
|
|
29
|
+
}
|
|
30
|
+
export declare function useTuiPresentationEnvironment(input: TuiPresentationEnvironmentInput): TuiPresentationEnvironment;
|
|
31
|
+
export declare function tuiInkRenderOptions(renderer: TuiRendererMode, screenReader: boolean): TuiInkRenderOptions;
|
|
32
|
+
//# sourceMappingURL=presentation-environment.d.ts.map
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useStdout } from 'ink';
|
|
3
|
+
function normalizeDimension(value) {
|
|
4
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
|
|
5
|
+
return undefined;
|
|
6
|
+
const normalized = Math.floor(value);
|
|
7
|
+
return normalized > 0 ? normalized : undefined;
|
|
8
|
+
}
|
|
9
|
+
function initialViewport(stdout, override) {
|
|
10
|
+
const overrideColumns = normalizeDimension(override?.columns);
|
|
11
|
+
const overrideRows = normalizeDimension(override?.rows);
|
|
12
|
+
if (override !== undefined) {
|
|
13
|
+
return {
|
|
14
|
+
columns: overrideColumns ?? 80,
|
|
15
|
+
rows: overrideRows,
|
|
16
|
+
revision: 0,
|
|
17
|
+
source: 'override',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const columns = normalizeDimension(stdout.columns);
|
|
21
|
+
return {
|
|
22
|
+
columns: columns ?? 80,
|
|
23
|
+
rows: normalizeDimension(stdout.rows),
|
|
24
|
+
revision: 0,
|
|
25
|
+
source: columns === undefined ? 'fallback' : 'terminal',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function sameViewport(left, right) {
|
|
29
|
+
return (left.columns === right.columns &&
|
|
30
|
+
left.rows === right.rows &&
|
|
31
|
+
left.source === right.source);
|
|
32
|
+
}
|
|
33
|
+
function publishViewport(current, next) {
|
|
34
|
+
if (sameViewport(current, next))
|
|
35
|
+
return current;
|
|
36
|
+
return { ...next, revision: current.revision + 1 };
|
|
37
|
+
}
|
|
38
|
+
function resolveTuiPresentationKind(renderer, screenReader) {
|
|
39
|
+
if (screenReader)
|
|
40
|
+
return 'screen-reader';
|
|
41
|
+
return renderer === 'fullscreen' ? 'fullscreen' : 'classic';
|
|
42
|
+
}
|
|
43
|
+
export function useTuiPresentationEnvironment(input) {
|
|
44
|
+
const { stdout } = useStdout();
|
|
45
|
+
const overrideColumns = input.viewportOverride?.columns;
|
|
46
|
+
const overrideRows = input.viewportOverride?.rows;
|
|
47
|
+
const hasOverride = input.viewportOverride !== undefined;
|
|
48
|
+
const [viewport, setViewport] = useState(() => initialViewport(stdout, input.viewportOverride));
|
|
49
|
+
const publishedViewport = useRef(viewport);
|
|
50
|
+
const resizeTimer = useRef(undefined);
|
|
51
|
+
const pendingViewport = useRef(undefined);
|
|
52
|
+
const mounted = useRef(true);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
mounted.current = true;
|
|
55
|
+
const normalizedOverrideColumns = normalizeDimension(overrideColumns);
|
|
56
|
+
const normalizedOverrideRows = normalizeDimension(overrideRows);
|
|
57
|
+
if (hasOverride) {
|
|
58
|
+
pendingViewport.current = undefined;
|
|
59
|
+
setViewport((current) => {
|
|
60
|
+
const next = publishViewport(current, {
|
|
61
|
+
columns: normalizedOverrideColumns ?? 80,
|
|
62
|
+
rows: normalizedOverrideRows,
|
|
63
|
+
source: 'override',
|
|
64
|
+
});
|
|
65
|
+
publishedViewport.current = next;
|
|
66
|
+
return next;
|
|
67
|
+
});
|
|
68
|
+
return () => {
|
|
69
|
+
mounted.current = false;
|
|
70
|
+
if (resizeTimer.current !== undefined) {
|
|
71
|
+
clearTimeout(resizeTimer.current);
|
|
72
|
+
resizeTimer.current = undefined;
|
|
73
|
+
}
|
|
74
|
+
pendingViewport.current = undefined;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const resize = () => {
|
|
78
|
+
const columns = normalizeDimension(stdout.columns);
|
|
79
|
+
const rows = normalizeDimension(stdout.rows);
|
|
80
|
+
const lastPublished = publishedViewport.current;
|
|
81
|
+
pendingViewport.current = {
|
|
82
|
+
columns: columns ?? lastPublished.columns,
|
|
83
|
+
rows: rows ?? lastPublished.rows,
|
|
84
|
+
source: 'terminal',
|
|
85
|
+
};
|
|
86
|
+
if (resizeTimer.current !== undefined)
|
|
87
|
+
return;
|
|
88
|
+
resizeTimer.current = setTimeout(() => {
|
|
89
|
+
resizeTimer.current = undefined;
|
|
90
|
+
const pending = pendingViewport.current;
|
|
91
|
+
pendingViewport.current = undefined;
|
|
92
|
+
if (!mounted.current || pending === undefined)
|
|
93
|
+
return;
|
|
94
|
+
setViewport((current) => {
|
|
95
|
+
const next = publishViewport(current, pending);
|
|
96
|
+
publishedViewport.current = next;
|
|
97
|
+
return next;
|
|
98
|
+
});
|
|
99
|
+
}, 33);
|
|
100
|
+
};
|
|
101
|
+
stdout.on('resize', resize);
|
|
102
|
+
return () => {
|
|
103
|
+
mounted.current = false;
|
|
104
|
+
stdout.off('resize', resize);
|
|
105
|
+
if (resizeTimer.current !== undefined) {
|
|
106
|
+
clearTimeout(resizeTimer.current);
|
|
107
|
+
resizeTimer.current = undefined;
|
|
108
|
+
}
|
|
109
|
+
pendingViewport.current = undefined;
|
|
110
|
+
};
|
|
111
|
+
}, [hasOverride, overrideColumns, overrideRows, stdout]);
|
|
112
|
+
const screenReader = input.screenReader;
|
|
113
|
+
const kind = resolveTuiPresentationKind(input.renderer, screenReader);
|
|
114
|
+
return {
|
|
115
|
+
kind,
|
|
116
|
+
viewport,
|
|
117
|
+
fixedViewport: kind === 'fullscreen' && viewport.rows !== undefined,
|
|
118
|
+
screenReader,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export function tuiInkRenderOptions(renderer, screenReader) {
|
|
122
|
+
const kind = resolveTuiPresentationKind(renderer, screenReader);
|
|
123
|
+
if (kind === 'screen-reader') {
|
|
124
|
+
return {
|
|
125
|
+
incrementalRendering: false,
|
|
126
|
+
alternateScreen: false,
|
|
127
|
+
isScreenReaderEnabled: true,
|
|
128
|
+
maxFps: 30,
|
|
129
|
+
concurrent: false,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (kind === 'fullscreen') {
|
|
133
|
+
return {
|
|
134
|
+
incrementalRendering: false,
|
|
135
|
+
alternateScreen: true,
|
|
136
|
+
isScreenReaderEnabled: false,
|
|
137
|
+
maxFps: 30,
|
|
138
|
+
concurrent: false,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
incrementalRendering: true,
|
|
143
|
+
alternateScreen: false,
|
|
144
|
+
isScreenReaderEnabled: false,
|
|
145
|
+
maxFps: 30,
|
|
146
|
+
concurrent: false,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=presentation-environment.js.map
|
|
@@ -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
|
|
62
|
+
status,
|
|
54
63
|
label: snapshot.name ?? snapshot.description,
|
|
55
64
|
output: snapshot.result?.text ?? snapshot.error ?? '',
|
|
56
65
|
startedAtMs: snapshot.startedAt,
|