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,259 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { realpath } from 'node:fs/promises';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { analyzeBashStructure, normalizeBashWrapperArgv, } from '../permissions/bash-ast.js';
|
|
5
|
+
import { validateBashPathSafety } from '../permissions/bash-path-safety.js';
|
|
6
|
+
import { shellInputIsReadOnly } from '../permissions/permission-updates.js';
|
|
7
|
+
import { isPathWithin } from '../platform/path-containment.js';
|
|
8
|
+
const READ_ONLY_TOOLS = new Set(['Read', 'Glob', 'Grep', 'Bash']);
|
|
9
|
+
const WRITE_TOOLS = new Set([...READ_ONLY_TOOLS, 'Write', 'Edit']);
|
|
10
|
+
const SAFE_GIT_COMMANDS = new Set([
|
|
11
|
+
'status',
|
|
12
|
+
'diff',
|
|
13
|
+
'log',
|
|
14
|
+
'show',
|
|
15
|
+
'rev-parse',
|
|
16
|
+
'ls-files',
|
|
17
|
+
'grep',
|
|
18
|
+
'blame',
|
|
19
|
+
]);
|
|
20
|
+
const FORBIDDEN_COMMANDS = new Set(['rm', 'rmdir', 'unlink', 'shred']);
|
|
21
|
+
const PRIVILEGE_WRAPPERS = new Set([
|
|
22
|
+
'sudo',
|
|
23
|
+
'su',
|
|
24
|
+
'doas',
|
|
25
|
+
'pkexec',
|
|
26
|
+
'runuser',
|
|
27
|
+
'setpriv',
|
|
28
|
+
]);
|
|
29
|
+
const DYNAMIC_COMMANDS = new Set(['eval', 'source', '.', 'xargs']);
|
|
30
|
+
const INLINE_INTERPRETERS = new Set([
|
|
31
|
+
'sh',
|
|
32
|
+
'bash',
|
|
33
|
+
'zsh',
|
|
34
|
+
'dash',
|
|
35
|
+
'fish',
|
|
36
|
+
'ksh',
|
|
37
|
+
'python',
|
|
38
|
+
'python3',
|
|
39
|
+
'perl',
|
|
40
|
+
'ruby',
|
|
41
|
+
'node',
|
|
42
|
+
'deno',
|
|
43
|
+
'php',
|
|
44
|
+
'pwsh',
|
|
45
|
+
'powershell',
|
|
46
|
+
]);
|
|
47
|
+
function deny(message) {
|
|
48
|
+
throw new Error(`Team tool denied: ${message}`);
|
|
49
|
+
}
|
|
50
|
+
async function canonical(path) {
|
|
51
|
+
try {
|
|
52
|
+
return await realpath(path);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error.code === 'ENOENT')
|
|
56
|
+
return resolve(path);
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function canonicalTarget(root, value) {
|
|
61
|
+
if (typeof value !== 'string' || value.trim() === '')
|
|
62
|
+
deny('path is required');
|
|
63
|
+
const candidate = resolve(root, value);
|
|
64
|
+
try {
|
|
65
|
+
return await realpath(candidate);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error.code !== 'ENOENT')
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
let parent = dirname(candidate);
|
|
72
|
+
while (parent !== dirname(parent)) {
|
|
73
|
+
try {
|
|
74
|
+
const parentCanonical = await realpath(parent);
|
|
75
|
+
return resolve(parentCanonical, candidate.slice(parent.length + 1));
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (error.code !== 'ENOENT')
|
|
79
|
+
throw error;
|
|
80
|
+
parent = dirname(parent);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return resolve(candidate);
|
|
84
|
+
}
|
|
85
|
+
function within(root, candidate) {
|
|
86
|
+
return isPathWithin(root, candidate);
|
|
87
|
+
}
|
|
88
|
+
function toolInputPaths(call) {
|
|
89
|
+
if (call.name === 'Read' || call.name === 'Write' || call.name === 'Edit')
|
|
90
|
+
return [call.input.file_path];
|
|
91
|
+
if (call.name === 'Glob' || call.name === 'Grep')
|
|
92
|
+
return call.input.path === undefined ? [] : [call.input.path];
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
export class TeamMemberToolRegistry {
|
|
96
|
+
options;
|
|
97
|
+
allowed;
|
|
98
|
+
canonicalCwdPromise;
|
|
99
|
+
constructor(options) {
|
|
100
|
+
this.options = options;
|
|
101
|
+
this.allowed =
|
|
102
|
+
options.access === 'read-only' ? READ_ONLY_TOOLS : WRITE_TOOLS;
|
|
103
|
+
this.canonicalCwdPromise = canonical(options.cwd);
|
|
104
|
+
}
|
|
105
|
+
definitions() {
|
|
106
|
+
return this.options.base
|
|
107
|
+
.definitions()
|
|
108
|
+
.filter((definition) => this.allowed.has(definition.name));
|
|
109
|
+
}
|
|
110
|
+
schedulingPolicy(call) {
|
|
111
|
+
this.validateCallShape(call);
|
|
112
|
+
const policy = this.options.base.schedulingPolicy?.(call);
|
|
113
|
+
return policy ?? { concurrency: 'concurrent' };
|
|
114
|
+
}
|
|
115
|
+
async prepare(call, context) {
|
|
116
|
+
await this.validate(call, context);
|
|
117
|
+
const prepared = await this.options.base.prepare(call, {
|
|
118
|
+
...context,
|
|
119
|
+
cwd: await this.canonicalCwdPromise,
|
|
120
|
+
});
|
|
121
|
+
await this.validate(prepared, {
|
|
122
|
+
...context,
|
|
123
|
+
cwd: await this.canonicalCwdPromise,
|
|
124
|
+
});
|
|
125
|
+
return prepared;
|
|
126
|
+
}
|
|
127
|
+
async execute(call, context) {
|
|
128
|
+
await this.validate(call, context);
|
|
129
|
+
return this.options.base.execute(call, {
|
|
130
|
+
...context,
|
|
131
|
+
cwd: await this.canonicalCwdPromise,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
validateCallShape(call) {
|
|
135
|
+
if (!call || typeof call.name !== 'string' || !this.allowed.has(call.name))
|
|
136
|
+
deny(`tool '${call?.name ?? ''}' is unavailable to this member`);
|
|
137
|
+
if (!call.input ||
|
|
138
|
+
typeof call.input !== 'object' ||
|
|
139
|
+
Array.isArray(call.input))
|
|
140
|
+
deny('tool input must be an object');
|
|
141
|
+
}
|
|
142
|
+
async validate(call, context) {
|
|
143
|
+
this.validateCallShape(call);
|
|
144
|
+
const root = await this.canonicalCwdPromise;
|
|
145
|
+
if ((await canonical(context.cwd)) !== root)
|
|
146
|
+
deny('execution cwd is outside the assigned workspace');
|
|
147
|
+
for (const value of toolInputPaths(call)) {
|
|
148
|
+
const target = await canonicalTarget(root, value);
|
|
149
|
+
if (!within(root, target))
|
|
150
|
+
deny('path is outside the assigned workspace');
|
|
151
|
+
}
|
|
152
|
+
if (call.name !== 'Bash')
|
|
153
|
+
return;
|
|
154
|
+
if (typeof call.input.command !== 'string')
|
|
155
|
+
deny('Bash command must be a string');
|
|
156
|
+
if (call.input.dangerouslyDisableSandbox === true)
|
|
157
|
+
deny('dangerouslyDisableSandbox is not allowed');
|
|
158
|
+
const command = call.input.command;
|
|
159
|
+
if (this.options.access === 'read-only') {
|
|
160
|
+
if (!shellInputIsReadOnly(command))
|
|
161
|
+
deny('read-only members may only run read-only Bash');
|
|
162
|
+
const safety = validateBashPathSafety(command, {
|
|
163
|
+
cwd: root,
|
|
164
|
+
homeDirectory: homedir(),
|
|
165
|
+
readRoots: [root],
|
|
166
|
+
writeRoots: [root],
|
|
167
|
+
permissionMode: 'acceptEdits',
|
|
168
|
+
});
|
|
169
|
+
if (!safety.safe)
|
|
170
|
+
deny(safety.reason);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const analysis = analyzeBashStructure(command);
|
|
174
|
+
if (!analysis.parsed)
|
|
175
|
+
deny(analysis.reason);
|
|
176
|
+
for (const entry of analysis.commands) {
|
|
177
|
+
const normalized = normalizeBashWrapperArgv(entry.argv);
|
|
178
|
+
if (!normalized.ok)
|
|
179
|
+
deny(normalized.reason);
|
|
180
|
+
this.validateNormalizedCommand(normalized.argv);
|
|
181
|
+
}
|
|
182
|
+
const safety = validateBashPathSafety(command, {
|
|
183
|
+
cwd: root,
|
|
184
|
+
homeDirectory: homedir(),
|
|
185
|
+
readRoots: [root],
|
|
186
|
+
writeRoots: [root],
|
|
187
|
+
permissionMode: 'acceptEdits',
|
|
188
|
+
});
|
|
189
|
+
if (!safety.safe)
|
|
190
|
+
deny(safety.reason);
|
|
191
|
+
}
|
|
192
|
+
validateNormalizedCommand(argv) {
|
|
193
|
+
let normalized = [...argv];
|
|
194
|
+
if (normalized[0] === 'command') {
|
|
195
|
+
let index = 1;
|
|
196
|
+
if (normalized[index] === '--')
|
|
197
|
+
index += 1;
|
|
198
|
+
if (normalized[index] === '-v' || normalized[index] === '-V')
|
|
199
|
+
return;
|
|
200
|
+
if ((normalized[index] ?? '').startsWith('-'))
|
|
201
|
+
deny('command wrapper cannot be statically resolved');
|
|
202
|
+
normalized = normalized.slice(index);
|
|
203
|
+
}
|
|
204
|
+
if (normalized[0] === 'env')
|
|
205
|
+
deny('env wrapper has no statically resolvable command');
|
|
206
|
+
const command = normalized[0];
|
|
207
|
+
if (!command)
|
|
208
|
+
deny('empty Bash command');
|
|
209
|
+
const base = command === 'git' ? command : (command.split('/').pop() ?? command);
|
|
210
|
+
if (PRIVILEGE_WRAPPERS.has(base) || DYNAMIC_COMMANDS.has(base))
|
|
211
|
+
deny(`command wrapper '${base}' is unavailable`);
|
|
212
|
+
if (base === 'find' &&
|
|
213
|
+
argv.some((value) => /^-(?:exec|execdir|delete)$/u.test(value)))
|
|
214
|
+
deny('find execution and deletion flags are unavailable');
|
|
215
|
+
if (INLINE_INTERPRETERS.has(base) && this.hasInlineInterpreter(argv))
|
|
216
|
+
deny(`inline interpreter '${base}' is unavailable`);
|
|
217
|
+
if (FORBIDDEN_COMMANDS.has(base))
|
|
218
|
+
deny(`destructive command '${base}' is unavailable`);
|
|
219
|
+
if (base === 'git') {
|
|
220
|
+
const subcommand = gitSubcommand(normalized);
|
|
221
|
+
if (!subcommand || !SAFE_GIT_COMMANDS.has(subcommand))
|
|
222
|
+
deny(`git operation '${subcommand ?? ''}' is unavailable`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
hasInlineInterpreter(argv) {
|
|
226
|
+
return argv.some((value, index) => {
|
|
227
|
+
if (index === 0)
|
|
228
|
+
return false;
|
|
229
|
+
return [
|
|
230
|
+
'-c',
|
|
231
|
+
'-e',
|
|
232
|
+
'--eval',
|
|
233
|
+
'--execute',
|
|
234
|
+
'-Command',
|
|
235
|
+
'-EncodedCommand',
|
|
236
|
+
].includes(value);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function gitSubcommand(argv) {
|
|
241
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
242
|
+
const value = argv[index] ?? '';
|
|
243
|
+
if (value === '--')
|
|
244
|
+
return argv[index + 1];
|
|
245
|
+
if (value === '-C' || value === '-c' || value === '--config-env') {
|
|
246
|
+
index += 1;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (value.startsWith('--git-dir=') ||
|
|
250
|
+
value.startsWith('--work-tree=') ||
|
|
251
|
+
value.startsWith('--namespace='))
|
|
252
|
+
continue;
|
|
253
|
+
if (value.startsWith('-'))
|
|
254
|
+
continue;
|
|
255
|
+
return value;
|
|
256
|
+
}
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=team-member-tools.js.map
|
package/package.json
CHANGED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { TuiRendererMode } from './tui-view-model.js';
|
|
2
|
-
/** Stable terminal-frame policy for the fullscreen renderer. */
|
|
3
|
-
export interface FullscreenInkRenderOptions {
|
|
4
|
-
incrementalRendering: boolean;
|
|
5
|
-
alternateScreen: boolean;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* The Ink render options for an interactive session. Fullscreen uses a stable
|
|
9
|
-
* terminal-frame policy: incremental rendering is disabled (every frame is a
|
|
10
|
-
* full redraw that erases the previous viewport) while classic and screen-reader
|
|
11
|
-
* modes keep their current behavior.
|
|
12
|
-
*/
|
|
13
|
-
export declare function fullscreenInkRenderOptions(currentRenderer: TuiRendererMode, axScreenReader: boolean | undefined): FullscreenInkRenderOptions;
|
|
14
|
-
//# sourceMappingURL=fullscreen-renderer.d.ts.map
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Ink render options for an interactive session. Fullscreen uses a stable
|
|
3
|
-
* terminal-frame policy: incremental rendering is disabled (every frame is a
|
|
4
|
-
* full redraw that erases the previous viewport) while classic and screen-reader
|
|
5
|
-
* modes keep their current behavior.
|
|
6
|
-
*/
|
|
7
|
-
export function fullscreenInkRenderOptions(currentRenderer, axScreenReader) {
|
|
8
|
-
return {
|
|
9
|
-
incrementalRendering: currentRenderer !== 'fullscreen' && !axScreenReader,
|
|
10
|
-
alternateScreen: currentRenderer === 'fullscreen',
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=fullscreen-renderer.js.map
|