praxis-agent 0.44.0 → 0.45.1
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
CHANGED
|
@@ -101,7 +101,8 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
|
|
|
101
101
|
## What Praxis provides
|
|
102
102
|
|
|
103
103
|
- **Local agent runtime** — C+ Quiet Operator responsive TUI with a linear
|
|
104
|
-
|
|
104
|
+
`❯` user / `⏺` assistant conversation, `✻` thinking activity, and `!` shell
|
|
105
|
+
composer grammar, compact stable tool rows, responsive density,
|
|
105
106
|
terminal-native background, and a minimal composer/status row. Interactive
|
|
106
107
|
surfaces share the same presentation across terminals, with English
|
|
107
108
|
permission/configuration choices and a taught `❯` / Up/Down / Enter / Esc
|
|
@@ -139,8 +140,9 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
|
|
|
139
140
|
provider-free `/terminal-setup` diagnostics and repeatable Shift+Enter setup
|
|
140
141
|
for supported local terminals,
|
|
141
142
|
`Ctrl+V` text/image clipboard paste, `Ctrl+Z` shell suspension and `fg`
|
|
142
|
-
recovery, permission-gated `!` shell turns
|
|
143
|
-
|
|
143
|
+
recovery, permission-gated provider-free `!` shell turns that persist shell
|
|
144
|
+
input/output for later ordinary prompts without creating an assistant turn,
|
|
145
|
+
navigable current/per-turn Git diff views, semantic plan/question decision panels with complete
|
|
144
146
|
screen-reader actions, semantic screen projection across selectable surfaces,
|
|
145
147
|
deterministic resize-aware URL/form elicitation rendering, and measured
|
|
146
148
|
context budgets; print mode,
|
|
@@ -210,6 +212,22 @@ Detailed feature status and executable evidence live in the
|
|
|
210
212
|
[parity matrix](https://github.com/Forest-Isle/Praxis/blob/main/docs/PARITY_MATRIX.md),
|
|
211
213
|
not in this entry-point README.
|
|
212
214
|
|
|
215
|
+
## Native data plane
|
|
216
|
+
|
|
217
|
+
Praxis defaults to an independent local native data plane:
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
Praxis ─── ~/.praxis (or PRAXIS_HOME)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
All sessions, memory, tasks, scheduled tasks, resources, and private state live
|
|
224
|
+
under `~/.praxis` (or `PRAXIS_HOME`). The authoritative transcript is
|
|
225
|
+
append-only `praxis.transcript` v1 JSONL; legacy Claude transcripts, indexes,
|
|
226
|
+
sidechains, and migration/recovery paths have been removed. `CLAUDE_CONFIG_DIR`
|
|
227
|
+
does not participate in native runs, and legacy directories are neither read
|
|
228
|
+
nor written. Claude-shaped messages and tool fields describe protocol shape
|
|
229
|
+
only; they do not change Praxis data ownership.
|
|
230
|
+
|
|
213
231
|
## Documentation
|
|
214
232
|
|
|
215
233
|
| Need | Document |
|
|
@@ -2592,7 +2592,9 @@ export class ClaudeSessionService {
|
|
|
2592
2592
|
});
|
|
2593
2593
|
}
|
|
2594
2594
|
const initialPricing = this.options.pricing?.resolve(provider.model ?? 'praxis/provider');
|
|
2595
|
-
if (
|
|
2595
|
+
if (shellCommand === undefined &&
|
|
2596
|
+
this.options.maxBudgetUsd !== undefined &&
|
|
2597
|
+
!initialPricing) {
|
|
2596
2598
|
throw new Error(`Cannot enforce --max-budget-usd: no pricing is configured for model ${provider.model ?? 'praxis/provider'}`);
|
|
2597
2599
|
}
|
|
2598
2600
|
const shellInputUuid = shellCommand === undefined ? null : randomUUID();
|
|
@@ -3699,7 +3701,9 @@ export class ClaudeSessionService {
|
|
|
3699
3701
|
};
|
|
3700
3702
|
},
|
|
3701
3703
|
});
|
|
3702
|
-
|
|
3704
|
+
if (shellCommand === undefined) {
|
|
3705
|
+
await contextEngine.prepare(contextTransitionPort(pendingUserMessages), signal);
|
|
3706
|
+
}
|
|
3703
3707
|
for (const [index, message] of expandedMessages.entries()) {
|
|
3704
3708
|
if (shellCommand !== undefined)
|
|
3705
3709
|
break;
|
|
@@ -3783,6 +3787,9 @@ export class ClaudeSessionService {
|
|
|
3783
3787
|
}
|
|
3784
3788
|
let shellUsage = { inputTokens: 0, outputTokens: 0 };
|
|
3785
3789
|
let shellModelUsage;
|
|
3790
|
+
let shellDurationApiMs;
|
|
3791
|
+
let shellDurationApiWithoutRetriesMs;
|
|
3792
|
+
let shellToolDurationMs;
|
|
3786
3793
|
if (shellCommand !== undefined) {
|
|
3787
3794
|
const call = {
|
|
3788
3795
|
id: `shell_${randomUUID().replaceAll('-', '')}`,
|
|
@@ -3822,6 +3829,10 @@ export class ClaudeSessionService {
|
|
|
3822
3829
|
throw error;
|
|
3823
3830
|
}
|
|
3824
3831
|
shellUsage = shellResult.usage ?? shellUsage;
|
|
3832
|
+
shellDurationApiMs = shellResult.durationApiMs;
|
|
3833
|
+
shellDurationApiWithoutRetriesMs =
|
|
3834
|
+
shellResult.durationApiWithoutRetriesMs;
|
|
3835
|
+
shellToolDurationMs = shellResult.durationToolMs;
|
|
3825
3836
|
if (!shellResult.isError) {
|
|
3826
3837
|
shellModelUsage = shellResult.modelUsage;
|
|
3827
3838
|
foregroundLineChanges.add(shellResult);
|
|
@@ -3872,7 +3883,74 @@ export class ClaudeSessionService {
|
|
|
3872
3883
|
isError: shellResult.isError,
|
|
3873
3884
|
});
|
|
3874
3885
|
}
|
|
3875
|
-
if (
|
|
3886
|
+
if (shellCommand !== undefined) {
|
|
3887
|
+
const tracker = this.sessionCostTrackers.get(sessionId);
|
|
3888
|
+
if (!tracker) {
|
|
3889
|
+
throw new Error(`Session cost tracker is not active for session ${sessionId}`);
|
|
3890
|
+
}
|
|
3891
|
+
const totalUsage = mergeUsage(recoveryUsage, shellUsage);
|
|
3892
|
+
const turnModelUsage = mergeSessionRawModelUsage(recoveryModelUsage, shellModelUsage);
|
|
3893
|
+
let rawCostUsd;
|
|
3894
|
+
if (turnModelUsage) {
|
|
3895
|
+
for (const [model, usage] of Object.entries(turnModelUsage)) {
|
|
3896
|
+
const pricing = this.options.pricing?.resolve(model);
|
|
3897
|
+
const costUsd = pricing
|
|
3898
|
+
? usageCostUsd(usage, pricing)
|
|
3899
|
+
: undefined;
|
|
3900
|
+
if (costUsd !== undefined)
|
|
3901
|
+
rawCostUsd = (rawCostUsd ?? 0) + costUsd;
|
|
3902
|
+
tracker.recordTurn({
|
|
3903
|
+
model,
|
|
3904
|
+
usage,
|
|
3905
|
+
...(costUsd === undefined ? {} : { costUsd }),
|
|
3906
|
+
...(usage.webSearchRequests === undefined
|
|
3907
|
+
? {}
|
|
3908
|
+
: { webSearchRequests: usage.webSearchRequests }),
|
|
3909
|
+
});
|
|
3910
|
+
}
|
|
3911
|
+
}
|
|
3912
|
+
let combinedToolDurationMs = 0;
|
|
3913
|
+
for (const recoveryResult of recoveryResults) {
|
|
3914
|
+
combinedToolDurationMs = addToolDuration(recoveryResult.durationToolMs, combinedToolDurationMs);
|
|
3915
|
+
}
|
|
3916
|
+
combinedToolDurationMs = addToolDuration(shellToolDurationMs, combinedToolDurationMs);
|
|
3917
|
+
tracker.recordDurations({
|
|
3918
|
+
...(shellDurationApiMs === undefined
|
|
3919
|
+
? {}
|
|
3920
|
+
: { apiDurationMs: shellDurationApiMs }),
|
|
3921
|
+
...(shellDurationApiWithoutRetriesMs === undefined
|
|
3922
|
+
? {}
|
|
3923
|
+
: {
|
|
3924
|
+
apiDurationWithoutRetriesMs: shellDurationApiWithoutRetriesMs,
|
|
3925
|
+
}),
|
|
3926
|
+
...(combinedToolDurationMs === 0
|
|
3927
|
+
? {}
|
|
3928
|
+
: { toolDurationMs: combinedToolDurationMs }),
|
|
3929
|
+
});
|
|
3930
|
+
if (foregroundLineChanges.linesAdded !== 0 ||
|
|
3931
|
+
foregroundLineChanges.linesRemoved !== 0) {
|
|
3932
|
+
tracker.recordLineChanges({
|
|
3933
|
+
...(foregroundLineChanges.linesAdded === 0
|
|
3934
|
+
? {}
|
|
3935
|
+
: { linesAdded: foregroundLineChanges.linesAdded }),
|
|
3936
|
+
...(foregroundLineChanges.linesRemoved === 0
|
|
3937
|
+
? {}
|
|
3938
|
+
: { linesRemoved: foregroundLineChanges.linesRemoved }),
|
|
3939
|
+
});
|
|
3940
|
+
}
|
|
3941
|
+
turnCompleted = true;
|
|
3942
|
+
return {
|
|
3943
|
+
sessionId,
|
|
3944
|
+
text: '',
|
|
3945
|
+
usage: totalUsage,
|
|
3946
|
+
...(rawCostUsd === undefined ? {} : { costUsd: rawCostUsd }),
|
|
3947
|
+
...(turnModelUsage ? { modelUsage: { ...turnModelUsage } } : {}),
|
|
3948
|
+
...(shellDurationApiMs === undefined
|
|
3949
|
+
? {}
|
|
3950
|
+
: { durationApiMs: shellDurationApiMs }),
|
|
3951
|
+
};
|
|
3952
|
+
}
|
|
3953
|
+
if (shellCommand === undefined && budget) {
|
|
3876
3954
|
await contextEngine.prepare(contextTransitionPort([], currentTurnUserMessages ?? []), signal);
|
|
3877
3955
|
budget.assertFits(budget.evaluate([
|
|
3878
3956
|
...contextMessages,
|
|
@@ -3911,7 +3989,9 @@ export class ClaudeSessionService {
|
|
|
3911
3989
|
];
|
|
3912
3990
|
}
|
|
3913
3991
|
await refreshRuntimeContext?.();
|
|
3914
|
-
|
|
3992
|
+
if (shellCommand === undefined) {
|
|
3993
|
+
await contextEngine.prepare(contextTransitionPort([], currentTurnUserMessages ?? []), signal);
|
|
3994
|
+
}
|
|
3915
3995
|
runtimeRequest.stableSystemMessageCount = stableSystemMessageCount;
|
|
3916
3996
|
return [
|
|
3917
3997
|
...contextMessages,
|
package/dist/cli/interactive.js
CHANGED
|
@@ -3240,7 +3240,9 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
3240
3240
|
streamingFrameRef.current?.resetText();
|
|
3241
3241
|
streamingFrameRef.current?.resetThinking();
|
|
3242
3242
|
streamingFrameRef.current?.flush();
|
|
3243
|
-
|
|
3243
|
+
if (shellCommand === undefined) {
|
|
3244
|
+
append({ kind: 'assistant', text: result.text });
|
|
3245
|
+
}
|
|
3244
3246
|
if (turnMutatedFilesRef.current) {
|
|
3245
3247
|
try {
|
|
3246
3248
|
const snapshot = await loadDiffSnapshot();
|
|
@@ -272,18 +272,18 @@ export function projectQuietFrame(input) {
|
|
|
272
272
|
: cleanActiveTail(body.transcript.active.text, viewportRows === undefined ? 200 : Math.max(1, viewportRows - 2));
|
|
273
273
|
const activeText = activeTail.lines;
|
|
274
274
|
if (activeText.some(({ text }) => Boolean(text)))
|
|
275
|
-
activeText.forEach(({ text: part, start }, index) => lines.push(createQuietFrameRow(`quiet:active:text:${start}`, `${index === 0 ? '
|
|
275
|
+
activeText.forEach(({ text: part, start }, index) => lines.push(createQuietFrameRow(`quiet:active:text:${start}`, `${index === 0 ? '⏺ ' : ' '}${index === 0 && activeTail.hidden ? '… ' : ''}${part}`, 'active', 'body', `${index === 0 ? 'Praxis: ' : ''}${part}`)));
|
|
276
276
|
const thinking = screenReader
|
|
277
277
|
? cleanActiveTail(body.transcript.active.thinking, Number.POSITIVE_INFINITY)
|
|
278
278
|
: cleanActiveTail(body.transcript.active.thinking, viewportRows === undefined ? 200 : Math.max(1, viewportRows - 2));
|
|
279
279
|
if (thinking.lines.some(({ text }) => Boolean(text)))
|
|
280
|
-
thinking.lines.forEach(({ text: part, start }, index) => lines.push(createQuietFrameRow(`quiet:active:thinking:${start}`, `${index === 0 ? '
|
|
280
|
+
thinking.lines.forEach(({ text: part, start }, index) => lines.push(createQuietFrameRow(`quiet:active:thinking:${start}`, `${index === 0 ? '✻ ' : ' '}${part}`, 'active', 'muted', `${index === 0 ? 'Thinking: ' : ''}${part}`)));
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
283
|
const focus = input.focusRows.length > 0
|
|
284
284
|
? input.focusRows.map((item) => withRegion(item, 'focus'))
|
|
285
285
|
: [
|
|
286
|
-
createQuietFrameRow('quiet:composer', `${input.shellMode ? '
|
|
286
|
+
createQuietFrameRow('quiet:composer', `${input.shellMode ? '! ' : '❯ '}${clean(input.composerText)}`, 'composer', 'input', `${input.shellMode ? 'Shell' : 'Composer'} input field`),
|
|
287
287
|
];
|
|
288
288
|
lines.push(...focus);
|
|
289
289
|
const statusText = `${input.busy ? 'Working · ' : ''}${clean(input.status) || (input.busy ? 'In progress' : 'Ready')}`;
|
|
@@ -302,7 +302,7 @@ export function projectQuietFrame(input) {
|
|
|
302
302
|
const compact = [...retained, ...focusRows, status];
|
|
303
303
|
lines.splice(0, lines.length, ...compact);
|
|
304
304
|
}
|
|
305
|
-
const composerPrefix =
|
|
305
|
+
const composerPrefix = input.shellMode ? '! ' : '❯ ';
|
|
306
306
|
if (!screenReader) {
|
|
307
307
|
for (let index = 0; index < lines.length; index += 1) {
|
|
308
308
|
const row = lines[index];
|
|
@@ -497,7 +497,7 @@ function estimateRenderedTranscriptEntryLines(entry, width, mode) {
|
|
|
497
497
|
if (item.kind === 'user')
|
|
498
498
|
return (1 +
|
|
499
499
|
(mode === 'screen-reader' ? 1 : 0) +
|
|
500
|
-
wrappedLineCount(`${mode === 'screen-reader' ? 'You: ' : '
|
|
500
|
+
wrappedLineCount(`${mode === 'screen-reader' ? 'You: ' : '❯ '}${item.text}`, width));
|
|
501
501
|
if (item.kind === 'assistant')
|
|
502
502
|
return (1 +
|
|
503
503
|
(mode === 'screen-reader' ? 1 : 0) +
|
|
@@ -512,7 +512,7 @@ function estimateRenderedTranscriptEntryLines(entry, width, mode) {
|
|
|
512
512
|
: 0));
|
|
513
513
|
const heading = mode === 'screen-reader'
|
|
514
514
|
? 'Thinking:Thought for a moment'
|
|
515
|
-
:
|
|
515
|
+
: '✻ Thought for a moment';
|
|
516
516
|
return (1 +
|
|
517
517
|
wrappedLineCount(heading, width) +
|
|
518
518
|
wrappedLineCount(item.text, mode === 'screen-reader' ? width : markdownContentWidth(width)));
|
|
@@ -667,7 +667,7 @@ function entryViewportRows(entry, width, mode) {
|
|
|
667
667
|
return [
|
|
668
668
|
'',
|
|
669
669
|
...(mode === 'screen-reader' ? [''] : []),
|
|
670
|
-
...textVisualRows(`${mode === 'screen-reader' ? 'You: ' : '
|
|
670
|
+
...textVisualRows(`${mode === 'screen-reader' ? 'You: ' : '❯ '}${item.text}`, width),
|
|
671
671
|
];
|
|
672
672
|
if (item.kind === 'assistant')
|
|
673
673
|
return [
|
|
@@ -679,7 +679,7 @@ function entryViewportRows(entry, width, mode) {
|
|
|
679
679
|
const summary = item.text.replace(/\s+/gu, ' ').trim();
|
|
680
680
|
const heading = mode === 'screen-reader'
|
|
681
681
|
? 'Thinking:Thought for a moment'
|
|
682
|
-
: `✻ Thought for a moment${summary ? ` · ${summary.slice(0, 160)}` : ''}`;
|
|
682
|
+
: `✻ Thought for a moment${mode === 'normal' && summary ? ` · ${summary.slice(0, 160)}` : ''}`;
|
|
683
683
|
if (mode === 'normal')
|
|
684
684
|
return [
|
|
685
685
|
'',
|
|
@@ -818,7 +818,7 @@ function assistantMarkdownProjectionRows(text, width, mode) {
|
|
|
818
818
|
fenceAfter: false,
|
|
819
819
|
fenceLabel: 'code',
|
|
820
820
|
};
|
|
821
|
-
const prefixed = textVisualRows(
|
|
821
|
+
const prefixed = textVisualRows(`⏺ ${first.text.trimEnd()}`, width).map((rowText) => ({ ...first, text: rowText }));
|
|
822
822
|
return [...prefixed, ...rows.slice(1)];
|
|
823
823
|
}
|
|
824
824
|
export function createTranscriptEntryViewportIndex(entry, width, mode) {
|