praxis-agent 0.34.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- 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/session-service.d.ts +8 -5
- package/dist/application/session-service.js +1824 -1728
- package/dist/application/subagent-service.js +385 -193
- 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 +33 -0
- package/dist/application/transcript-projection.js +110 -0
- package/dist/application/turn-lifecycle.d.ts +40 -0
- package/dist/application/turn-lifecycle.js +36 -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.js +279 -209
- package/dist/cli/protocol.js +5 -2
- package/dist/cli/tui/claude-style.d.ts +5 -48
- package/dist/cli/tui/claude-style.js +167 -197
- 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 +9 -5
- package/dist/cli/tui/transcript-presentation.d.ts +89 -0
- package/dist/cli/tui/transcript-presentation.js +169 -0
- package/dist/cli/tui/transcript-viewport.d.ts +5 -34
- package/dist/cli/tui/transcript-viewport.js +639 -158
- 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-view-model.d.ts +6 -2
- package/dist/cli/tui/tui-view-model.js +23 -6
- package/dist/cli-runtime.d.ts +2 -2
- package/dist/compatibility/claude/context.d.ts +2 -2
- package/dist/compatibility/claude/context.js +2 -14
- package/dist/compatibility/claude/data-plane-adapter.d.ts +9 -0
- package/dist/compatibility/claude/data-plane-adapter.js +44 -0
- package/dist/compatibility/claude/paths.js +2 -2
- package/dist/compatibility/claude/shared-resources.d.ts +7 -38
- package/dist/compatibility/claude/transcript-codec.d.ts +21 -0
- package/dist/compatibility/claude/transcript-codec.js +512 -0
- package/dist/core/agent-orchestration.d.ts +31 -0
- package/dist/core/agent-orchestration.js +235 -0
- package/dist/core/context.d.ts +25 -7
- package/dist/core/context.js +46 -0
- package/dist/core/prompt-composer.d.ts +8 -16
- package/dist/core/prompt-composer.js +65 -25
- package/dist/core/resources.d.ts +39 -0
- package/dist/core/resources.js +2 -0
- package/dist/core/runtime.d.ts +6 -1
- package/dist/core/runtime.js +15 -1
- package/dist/core/session.d.ts +2 -0
- package/dist/core/session.js +5 -0
- package/dist/core/transcript-codec.d.ts +70 -0
- package/dist/core/transcript-codec.js +60 -0
- package/dist/core/transcript-event.d.ts +33 -0
- package/dist/core/transcript-event.js +198 -0
- package/dist/persistence/claude-job-store.d.ts +58 -0
- package/dist/persistence/claude-job-store.js +483 -1
- package/dist/persistence/data-plane-adapter.d.ts +32 -0
- package/dist/persistence/data-plane-adapter.js +2 -0
- package/dist/persistence/data-plane.d.ts +4 -20
- package/dist/persistence/data-plane.js +10 -47
- package/dist/persistence/native-data-plane-adapter.d.ts +9 -0
- package/dist/persistence/native-data-plane-adapter.js +39 -0
- package/dist/persistence/native-resources.d.ts +4 -4
- package/dist/persistence/native-transcript-codec.d.ts +13 -0
- package/dist/persistence/native-transcript-codec.js +102 -0
- package/dist/persistence/native-transcript-reader.d.ts +53 -0
- package/dist/persistence/native-transcript-reader.js +283 -0
- package/dist/persistence/subagent-lifecycle-store.d.ts +41 -8
- package/dist/persistence/subagent-lifecycle-store.js +373 -146
- package/dist/platform/exclusive-file-lease.d.ts +3 -0
- package/dist/platform/exclusive-file-lease.js +3 -0
- package/dist/tools/claude-user-message.d.ts +0 -1
- package/dist/tools/claude-user-message.js +0 -1
- package/package.json +6 -2
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/** Provider- and persistence-independent lifecycle contract for one Agent. */
|
|
2
|
+
export const TERMINAL_LIFECYCLE_STATES = [
|
|
3
|
+
'completed',
|
|
4
|
+
'failed',
|
|
5
|
+
'cancelled',
|
|
6
|
+
'orphaned',
|
|
7
|
+
];
|
|
8
|
+
const transitions = {
|
|
9
|
+
queued: ['running', 'cancelling', 'failed'],
|
|
10
|
+
running: ['waiting', 'completed', 'failed', 'cancelling'],
|
|
11
|
+
waiting: ['running', 'completed', 'failed', 'cancelling'],
|
|
12
|
+
cancelling: ['cancelled'],
|
|
13
|
+
completed: [],
|
|
14
|
+
failed: [],
|
|
15
|
+
cancelled: [],
|
|
16
|
+
orphaned: [],
|
|
17
|
+
};
|
|
18
|
+
export function isTerminalLifecycleState(state) {
|
|
19
|
+
return TERMINAL_LIFECYCLE_STATES.includes(state);
|
|
20
|
+
}
|
|
21
|
+
function isRecord(value) {
|
|
22
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
function parseOwner(value) {
|
|
25
|
+
if (!isRecord(value))
|
|
26
|
+
throw new Error('Invalid lifecycle owner');
|
|
27
|
+
const owner = value;
|
|
28
|
+
const token = owner.token;
|
|
29
|
+
const pid = owner.pid;
|
|
30
|
+
const acquiredAt = owner.acquiredAt;
|
|
31
|
+
if (typeof token !== 'string' ||
|
|
32
|
+
token.trim().length === 0 ||
|
|
33
|
+
!Number.isSafeInteger(pid) ||
|
|
34
|
+
Number(pid) <= 0 ||
|
|
35
|
+
typeof acquiredAt !== 'string' ||
|
|
36
|
+
Number.isNaN(Date.parse(acquiredAt))) {
|
|
37
|
+
throw new Error('Invalid lifecycle owner');
|
|
38
|
+
}
|
|
39
|
+
return Object.freeze({
|
|
40
|
+
token: token,
|
|
41
|
+
pid: pid,
|
|
42
|
+
acquiredAt: acquiredAt,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** The single invariant parser used by every lifecycle mutation and store. */
|
|
46
|
+
export function parseAgentLifecycleSnapshot(value) {
|
|
47
|
+
if (!isRecord(value))
|
|
48
|
+
throw new Error('Invalid lifecycle snapshot');
|
|
49
|
+
const generation = value.generation;
|
|
50
|
+
const revision = value.revision;
|
|
51
|
+
const state = value.state;
|
|
52
|
+
const previousOwnerToken = value.previousOwnerToken;
|
|
53
|
+
const acceptance = value.acceptance;
|
|
54
|
+
const terminalAt = value.terminalAt;
|
|
55
|
+
if (!Number.isSafeInteger(generation) || Number(generation) <= 0) {
|
|
56
|
+
throw new Error('Lifecycle generation must be a positive integer');
|
|
57
|
+
}
|
|
58
|
+
if (!Number.isSafeInteger(revision) || Number(revision) < 0) {
|
|
59
|
+
throw new Error('Lifecycle revision must be a nonnegative integer');
|
|
60
|
+
}
|
|
61
|
+
if (![
|
|
62
|
+
'queued',
|
|
63
|
+
'running',
|
|
64
|
+
'waiting',
|
|
65
|
+
'completed',
|
|
66
|
+
'failed',
|
|
67
|
+
'cancelling',
|
|
68
|
+
'cancelled',
|
|
69
|
+
'orphaned',
|
|
70
|
+
].includes(String(state))) {
|
|
71
|
+
throw new Error(`Invalid lifecycle state: ${String(value.state)}`);
|
|
72
|
+
}
|
|
73
|
+
if (previousOwnerToken !== null &&
|
|
74
|
+
(typeof previousOwnerToken !== 'string' ||
|
|
75
|
+
previousOwnerToken.trim().length === 0)) {
|
|
76
|
+
throw new Error('Invalid previous lifecycle owner token');
|
|
77
|
+
}
|
|
78
|
+
if (!['pending', 'accepted', 'rejected'].includes(String(acceptance))) {
|
|
79
|
+
throw new Error('Invalid lifecycle acceptance');
|
|
80
|
+
}
|
|
81
|
+
if (isTerminalLifecycleState(state)) {
|
|
82
|
+
if (value.owner !== null || terminalAt === null) {
|
|
83
|
+
throw new Error('Terminal lifecycle must be ownerless and timestamped');
|
|
84
|
+
}
|
|
85
|
+
if (typeof terminalAt !== 'string' ||
|
|
86
|
+
Number.isNaN(Date.parse(terminalAt))) {
|
|
87
|
+
throw new Error('Invalid lifecycle terminal timestamp');
|
|
88
|
+
}
|
|
89
|
+
if (acceptance !== 'pending' && state !== 'completed') {
|
|
90
|
+
throw new Error('Only completed lifecycle may be accepted or rejected');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (value.owner === null ||
|
|
94
|
+
terminalAt !== null ||
|
|
95
|
+
acceptance !== 'pending') {
|
|
96
|
+
throw new Error('Nonterminal lifecycle must be owned, pending, and untimestamped');
|
|
97
|
+
}
|
|
98
|
+
const owner = value.owner === null ? null : parseOwner(value.owner);
|
|
99
|
+
return Object.freeze({
|
|
100
|
+
generation: generation,
|
|
101
|
+
revision: revision,
|
|
102
|
+
state: state,
|
|
103
|
+
owner,
|
|
104
|
+
previousOwnerToken: previousOwnerToken,
|
|
105
|
+
terminalAt: terminalAt,
|
|
106
|
+
acceptance: acceptance,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
export function createAgentLifecycle(owner, generation = 1) {
|
|
110
|
+
parseOwner(owner);
|
|
111
|
+
return parseAgentLifecycleSnapshot({
|
|
112
|
+
generation,
|
|
113
|
+
revision: 0,
|
|
114
|
+
state: 'queued',
|
|
115
|
+
owner,
|
|
116
|
+
previousOwnerToken: null,
|
|
117
|
+
terminalAt: null,
|
|
118
|
+
acceptance: 'pending',
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function requireCurrentOwner(current, token) {
|
|
122
|
+
if (current.owner === null || current.owner.token !== token) {
|
|
123
|
+
throw new Error('Lifecycle execution owner token is stale or missing');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export function transitionLifecycle(current, nextState, ownerToken, terminalAt = new Date().toISOString()) {
|
|
127
|
+
parseAgentLifecycleSnapshot(current);
|
|
128
|
+
requireCurrentOwner(current, ownerToken);
|
|
129
|
+
if (!transitions[current.state].includes(nextState)) {
|
|
130
|
+
throw new Error(`Illegal lifecycle transition: ${current.state} -> ${nextState}`);
|
|
131
|
+
}
|
|
132
|
+
const terminal = isTerminalLifecycleState(nextState);
|
|
133
|
+
if (terminal && Number.isNaN(Date.parse(terminalAt))) {
|
|
134
|
+
throw new Error('Invalid lifecycle terminal timestamp');
|
|
135
|
+
}
|
|
136
|
+
return parseAgentLifecycleSnapshot({
|
|
137
|
+
generation: current.generation,
|
|
138
|
+
revision: current.revision + 1,
|
|
139
|
+
state: nextState,
|
|
140
|
+
owner: terminal ? null : current.owner,
|
|
141
|
+
previousOwnerToken: terminal
|
|
142
|
+
? (current.owner?.token ?? null)
|
|
143
|
+
: current.previousOwnerToken,
|
|
144
|
+
terminalAt: terminal ? terminalAt : null,
|
|
145
|
+
acceptance: 'pending',
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function requireFreshOwner(current, owner) {
|
|
149
|
+
parseOwner(owner);
|
|
150
|
+
if (owner.token === current.previousOwnerToken) {
|
|
151
|
+
throw new Error('Lifecycle generation requires a fresh owner token');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/** Rotate a queued execution owner during a planned parent-to-worker handoff. */
|
|
155
|
+
export function transferLifecycleOwner(current, currentOwnerToken, nextOwner) {
|
|
156
|
+
parseAgentLifecycleSnapshot(current);
|
|
157
|
+
requireCurrentOwner(current, currentOwnerToken);
|
|
158
|
+
if (current.state !== 'queued') {
|
|
159
|
+
throw new Error(`Cannot transfer lifecycle owner from ${current.state}`);
|
|
160
|
+
}
|
|
161
|
+
parseOwner(nextOwner);
|
|
162
|
+
if (nextOwner.token === currentOwnerToken) {
|
|
163
|
+
throw new Error('Lifecycle handoff requires a different owner token');
|
|
164
|
+
}
|
|
165
|
+
requireFreshOwner(current, nextOwner);
|
|
166
|
+
return parseAgentLifecycleSnapshot({
|
|
167
|
+
...current,
|
|
168
|
+
revision: current.revision + 1,
|
|
169
|
+
owner: nextOwner,
|
|
170
|
+
previousOwnerToken: currentOwnerToken,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
export function continueLifecycle(current, owner) {
|
|
174
|
+
parseAgentLifecycleSnapshot(current);
|
|
175
|
+
if (!['completed', 'failed', 'cancelled'].includes(current.state)) {
|
|
176
|
+
throw new Error(`Cannot continue lifecycle from ${current.state}`);
|
|
177
|
+
}
|
|
178
|
+
requireFreshOwner(current, owner);
|
|
179
|
+
return parseAgentLifecycleSnapshot({
|
|
180
|
+
generation: current.generation + 1,
|
|
181
|
+
revision: current.revision + 1,
|
|
182
|
+
state: 'queued',
|
|
183
|
+
owner,
|
|
184
|
+
previousOwnerToken: current.previousOwnerToken,
|
|
185
|
+
terminalAt: null,
|
|
186
|
+
acceptance: 'pending',
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
export function recoverLifecycle(current, owner) {
|
|
190
|
+
parseAgentLifecycleSnapshot(current);
|
|
191
|
+
if (current.state !== 'orphaned') {
|
|
192
|
+
throw new Error(`Cannot recover lifecycle from ${current.state}`);
|
|
193
|
+
}
|
|
194
|
+
requireFreshOwner(current, owner);
|
|
195
|
+
return parseAgentLifecycleSnapshot({
|
|
196
|
+
generation: current.generation + 1,
|
|
197
|
+
revision: current.revision + 1,
|
|
198
|
+
state: 'queued',
|
|
199
|
+
owner,
|
|
200
|
+
previousOwnerToken: current.previousOwnerToken,
|
|
201
|
+
terminalAt: null,
|
|
202
|
+
acceptance: 'pending',
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/** Adapter-proven owner loss is intentionally separate from ordinary transitions. */
|
|
206
|
+
export function markLifecycleOrphaned(current, ownerToken, terminalAt = new Date().toISOString()) {
|
|
207
|
+
parseAgentLifecycleSnapshot(current);
|
|
208
|
+
requireCurrentOwner(current, ownerToken);
|
|
209
|
+
if (!['queued', 'running', 'waiting', 'cancelling'].includes(current.state)) {
|
|
210
|
+
throw new Error(`Cannot orphan lifecycle from ${current.state}`);
|
|
211
|
+
}
|
|
212
|
+
if (Number.isNaN(Date.parse(terminalAt)))
|
|
213
|
+
throw new Error('Invalid lifecycle terminal timestamp');
|
|
214
|
+
return parseAgentLifecycleSnapshot({
|
|
215
|
+
generation: current.generation,
|
|
216
|
+
revision: current.revision + 1,
|
|
217
|
+
state: 'orphaned',
|
|
218
|
+
owner: null,
|
|
219
|
+
previousOwnerToken: ownerToken,
|
|
220
|
+
terminalAt,
|
|
221
|
+
acceptance: 'pending',
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
export function acceptLifecycle(current, generation = current.generation, acceptance = 'accepted') {
|
|
225
|
+
parseAgentLifecycleSnapshot(current);
|
|
226
|
+
if (current.state !== 'completed' || generation !== current.generation) {
|
|
227
|
+
throw new Error('Only the completed current generation may be accepted');
|
|
228
|
+
}
|
|
229
|
+
return parseAgentLifecycleSnapshot({
|
|
230
|
+
...current,
|
|
231
|
+
revision: current.revision + 1,
|
|
232
|
+
acceptance,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=agent-orchestration.js.map
|
package/dist/core/context.d.ts
CHANGED
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
import type { ModelMessage } from './runtime.js';
|
|
2
|
-
import type { PromptCompositionMode, PromptSection } from './prompt-composer.js';
|
|
3
2
|
export type SystemContextMessage = Extract<ModelMessage, {
|
|
4
3
|
role: 'system';
|
|
5
4
|
}>;
|
|
6
|
-
export
|
|
5
|
+
export type ContextSectionStability = 'static' | 'session' | 'volatile';
|
|
6
|
+
export type ContextSectionPlacement = 'system' | 'first-user';
|
|
7
|
+
export type ContextCompositionMode = 'default' | 'custom' | 'bare' | 'agent' | 'subagent';
|
|
8
|
+
export interface ContextSection {
|
|
9
|
+
id: string;
|
|
10
|
+
content: string;
|
|
11
|
+
placement: ContextSectionPlacement;
|
|
12
|
+
stability: ContextSectionStability;
|
|
13
|
+
}
|
|
14
|
+
export interface ContextSnapshot {
|
|
15
|
+
sections: readonly ContextSection[];
|
|
16
|
+
}
|
|
17
|
+
export interface ContextProjection {
|
|
7
18
|
systemMessages: readonly SystemContextMessage[];
|
|
8
19
|
firstUserMessageContext?: string;
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
stableSystemSectionCount: number;
|
|
21
|
+
}
|
|
22
|
+
export interface TurnContextInputs {
|
|
23
|
+
planMode?: string;
|
|
24
|
+
sessionMemory?: string;
|
|
25
|
+
briefOutput?: boolean;
|
|
26
|
+
structuredOutput?: boolean;
|
|
11
27
|
}
|
|
12
28
|
export interface ContextAssemblyOptions {
|
|
13
29
|
cwd?: string;
|
|
14
30
|
lifecycleId?: string;
|
|
15
|
-
mode?:
|
|
31
|
+
mode?: ContextCompositionMode;
|
|
16
32
|
baseSystemPrompt?: string;
|
|
17
|
-
|
|
33
|
+
turn?: TurnContextInputs;
|
|
18
34
|
}
|
|
19
35
|
export type ContextInvalidationReason = 'clear' | 'compact' | 'fork' | 'resource-reload' | 'restore' | 'tool-pool' | 'cwd' | 'worktree';
|
|
20
36
|
export interface ContextInvalidationOptions {
|
|
@@ -22,8 +38,10 @@ export interface ContextInvalidationOptions {
|
|
|
22
38
|
reason: ContextInvalidationReason;
|
|
23
39
|
}
|
|
24
40
|
export interface ContextAssembler {
|
|
25
|
-
assemble(options?: ContextAssemblyOptions): Promise<
|
|
41
|
+
assemble(options?: ContextAssemblyOptions): Promise<ContextSnapshot>;
|
|
26
42
|
invalidate?(options: ContextInvalidationOptions): void;
|
|
27
43
|
}
|
|
44
|
+
export declare function createContextSnapshot(sections: readonly ContextSection[]): ContextSnapshot;
|
|
45
|
+
export declare function projectContextSnapshot(snapshot: ContextSnapshot): ContextProjection;
|
|
28
46
|
export declare function injectFirstUserMessageContext(messages: readonly ModelMessage[], context: string | undefined): ModelMessage[];
|
|
29
47
|
//# sourceMappingURL=context.d.ts.map
|
package/dist/core/context.js
CHANGED
|
@@ -1,3 +1,49 @@
|
|
|
1
|
+
function validateContextSections(sections) {
|
|
2
|
+
const identities = new Set();
|
|
3
|
+
let volatileSystemSeen = false;
|
|
4
|
+
for (const section of sections) {
|
|
5
|
+
if (!section.id.trim())
|
|
6
|
+
throw new Error('Context section id is required');
|
|
7
|
+
if (!section.content.trim()) {
|
|
8
|
+
throw new Error(`Context section ${section.id} content is required`);
|
|
9
|
+
}
|
|
10
|
+
if (identities.has(section.id)) {
|
|
11
|
+
throw new Error(`Duplicate context section ${section.id}`);
|
|
12
|
+
}
|
|
13
|
+
identities.add(section.id);
|
|
14
|
+
if (section.placement === 'system') {
|
|
15
|
+
if (section.stability === 'volatile')
|
|
16
|
+
volatileSystemSeen = true;
|
|
17
|
+
else if (volatileSystemSeen) {
|
|
18
|
+
throw new Error('Non-volatile system sections cannot follow volatile sections');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function createContextSnapshot(sections) {
|
|
24
|
+
validateContextSections(sections);
|
|
25
|
+
return { sections: sections.map((section) => ({ ...section })) };
|
|
26
|
+
}
|
|
27
|
+
export function projectContextSnapshot(snapshot) {
|
|
28
|
+
const sections = snapshot.sections;
|
|
29
|
+
validateContextSections(sections);
|
|
30
|
+
const systemSections = sections.filter((section) => section.placement === 'system');
|
|
31
|
+
const firstVolatileSystemIndex = systemSections.findIndex((section) => section.stability === 'volatile');
|
|
32
|
+
const firstUserMessageContext = sections
|
|
33
|
+
.filter((section) => section.placement === 'first-user')
|
|
34
|
+
.map((section) => section.content)
|
|
35
|
+
.join('\n\n');
|
|
36
|
+
return {
|
|
37
|
+
systemMessages: systemSections.map((section) => ({
|
|
38
|
+
role: 'system',
|
|
39
|
+
content: section.content,
|
|
40
|
+
})),
|
|
41
|
+
stableSystemSectionCount: firstVolatileSystemIndex < 0
|
|
42
|
+
? systemSections.length
|
|
43
|
+
: firstVolatileSystemIndex,
|
|
44
|
+
...(firstUserMessageContext ? { firstUserMessageContext } : {}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
1
47
|
export function injectFirstUserMessageContext(messages, context) {
|
|
2
48
|
if (context === undefined)
|
|
3
49
|
return [...messages];
|
|
@@ -1,27 +1,19 @@
|
|
|
1
|
-
import type
|
|
2
|
-
export type PromptSectionStability = '
|
|
3
|
-
export type PromptSectionPlacement = '
|
|
4
|
-
export type PromptCompositionMode =
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
content: string;
|
|
8
|
-
stability: PromptSectionStability;
|
|
9
|
-
placement: PromptSectionPlacement;
|
|
10
|
-
}
|
|
11
|
-
export interface PromptComposition {
|
|
12
|
-
sections: readonly PromptSection[];
|
|
13
|
-
systemMessages: readonly SystemContextMessage[];
|
|
14
|
-
firstUserMessageContext?: string;
|
|
15
|
-
stableSystemSectionCount: number;
|
|
16
|
-
}
|
|
1
|
+
import { type ContextAssembler, type ContextAssemblyOptions, type ContextCompositionMode, type ContextSection, type ContextSnapshot, type TurnContextInputs } from './context.js';
|
|
2
|
+
export type PromptSectionStability = ContextSection['stability'];
|
|
3
|
+
export type PromptSectionPlacement = ContextSection['placement'];
|
|
4
|
+
export type PromptCompositionMode = ContextCompositionMode;
|
|
5
|
+
export type PromptSection = ContextSection;
|
|
6
|
+
export type PromptComposition = ContextSnapshot;
|
|
17
7
|
export interface PromptCompositionOptions {
|
|
18
8
|
mode: PromptCompositionMode;
|
|
19
9
|
baseSystemPrompt?: string;
|
|
20
10
|
appendSystemPrompt?: string;
|
|
21
11
|
sessionSections?: readonly PromptSection[];
|
|
22
12
|
tailSections?: readonly PromptSection[];
|
|
13
|
+
turn?: TurnContextInputs;
|
|
23
14
|
}
|
|
24
15
|
export declare class PromptComposer {
|
|
25
16
|
compose(options: PromptCompositionOptions): PromptComposition;
|
|
26
17
|
}
|
|
18
|
+
export declare function assembleContextSnapshot(assembler: ContextAssembler | undefined, options?: ContextAssemblyOptions): Promise<ContextSnapshot>;
|
|
27
19
|
//# sourceMappingURL=prompt-composer.d.ts.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createContextSnapshot, } from './context.js';
|
|
1
2
|
const PRODUCT_POLICY = `# Praxis
|
|
2
3
|
|
|
3
4
|
You are Praxis, a local CLI coding agent. Work until the user's request is genuinely handled or a concrete blocker requires their input.
|
|
@@ -9,6 +10,8 @@ You are Praxis, a local CLI coding agent. Work until the user's request is genui
|
|
|
9
10
|
- Summarize or clear bulky intermediate results once their useful facts have been retained.
|
|
10
11
|
- Treat the context window as bounded: preserve decisions and evidence before reducing detail.
|
|
11
12
|
- Keep responses concise and use the user's language unless they request otherwise.`;
|
|
13
|
+
const BRIEF_OUTPUT_POLICY = 'When brief mode is enabled, SendUserMessage is the primary user-visible reply channel. Use it for the answer, progress checkpoints, and blockers. Set status to normal for a direct reply and proactive for an unsolicited update.';
|
|
14
|
+
const STRUCTURED_OUTPUT_POLICY = 'You MUST call StructuredOutput exactly once at the end with a value matching the requested JSON Schema.';
|
|
12
15
|
function baseSection(mode, content) {
|
|
13
16
|
if (mode === 'bare')
|
|
14
17
|
return undefined;
|
|
@@ -51,6 +54,52 @@ function normalizedSections(sections, group) {
|
|
|
51
54
|
return { ...section };
|
|
52
55
|
});
|
|
53
56
|
}
|
|
57
|
+
function turnSections(turn) {
|
|
58
|
+
if (!turn)
|
|
59
|
+
return [];
|
|
60
|
+
return [
|
|
61
|
+
...(turn.planMode?.trim()
|
|
62
|
+
? [
|
|
63
|
+
{
|
|
64
|
+
id: 'plan-mode',
|
|
65
|
+
content: turn.planMode,
|
|
66
|
+
placement: 'system',
|
|
67
|
+
stability: 'volatile',
|
|
68
|
+
},
|
|
69
|
+
]
|
|
70
|
+
: []),
|
|
71
|
+
...(turn.sessionMemory?.trim()
|
|
72
|
+
? [
|
|
73
|
+
{
|
|
74
|
+
id: 'session-memory',
|
|
75
|
+
content: turn.sessionMemory,
|
|
76
|
+
placement: 'system',
|
|
77
|
+
stability: 'volatile',
|
|
78
|
+
},
|
|
79
|
+
]
|
|
80
|
+
: []),
|
|
81
|
+
...(turn.briefOutput
|
|
82
|
+
? [
|
|
83
|
+
{
|
|
84
|
+
id: 'brief-output',
|
|
85
|
+
content: BRIEF_OUTPUT_POLICY,
|
|
86
|
+
placement: 'system',
|
|
87
|
+
stability: 'volatile',
|
|
88
|
+
},
|
|
89
|
+
]
|
|
90
|
+
: []),
|
|
91
|
+
...(turn.structuredOutput
|
|
92
|
+
? [
|
|
93
|
+
{
|
|
94
|
+
id: 'structured-output',
|
|
95
|
+
content: STRUCTURED_OUTPUT_POLICY,
|
|
96
|
+
placement: 'system',
|
|
97
|
+
stability: 'volatile',
|
|
98
|
+
},
|
|
99
|
+
]
|
|
100
|
+
: []),
|
|
101
|
+
];
|
|
102
|
+
}
|
|
54
103
|
export class PromptComposer {
|
|
55
104
|
compose(options) {
|
|
56
105
|
const base = baseSection(options.mode, options.baseSystemPrompt);
|
|
@@ -63,37 +112,28 @@ export class PromptComposer {
|
|
|
63
112
|
stability: 'session',
|
|
64
113
|
}
|
|
65
114
|
: undefined;
|
|
66
|
-
const tailSections = normalizedSections(options.tailSections ?? [], 'tailSections');
|
|
115
|
+
const tailSections = normalizedSections([...(options.tailSections ?? []), ...turnSections(options.turn)], 'tailSections');
|
|
67
116
|
const sections = [
|
|
68
117
|
...(base ? [base] : []),
|
|
69
118
|
...sessionSections,
|
|
70
119
|
...(append ? [append] : []),
|
|
71
120
|
...tailSections,
|
|
72
121
|
];
|
|
73
|
-
|
|
74
|
-
for (const section of sections) {
|
|
75
|
-
if (identities.has(section.id)) {
|
|
76
|
-
throw new Error(`Duplicate prompt section ${section.id}`);
|
|
77
|
-
}
|
|
78
|
-
identities.add(section.id);
|
|
79
|
-
}
|
|
80
|
-
const systemSections = sections.filter((section) => section.placement === 'system');
|
|
81
|
-
const firstVolatileSystemIndex = systemSections.findIndex((section) => section.stability === 'volatile');
|
|
82
|
-
const firstUserMessageContext = sections
|
|
83
|
-
.filter((section) => section.placement === 'first-user')
|
|
84
|
-
.map((section) => section.content)
|
|
85
|
-
.join('\n\n');
|
|
86
|
-
return {
|
|
87
|
-
sections,
|
|
88
|
-
systemMessages: systemSections.map((section) => ({
|
|
89
|
-
role: 'system',
|
|
90
|
-
content: section.content,
|
|
91
|
-
})),
|
|
92
|
-
stableSystemSectionCount: firstVolatileSystemIndex < 0
|
|
93
|
-
? systemSections.length
|
|
94
|
-
: firstVolatileSystemIndex,
|
|
95
|
-
...(firstUserMessageContext ? { firstUserMessageContext } : {}),
|
|
96
|
-
};
|
|
122
|
+
return createContextSnapshot(sections);
|
|
97
123
|
}
|
|
98
124
|
}
|
|
125
|
+
const defaultContextAssembler = {
|
|
126
|
+
async assemble(options = {}) {
|
|
127
|
+
return new PromptComposer().compose({
|
|
128
|
+
mode: options.mode ?? 'bare',
|
|
129
|
+
...(options.baseSystemPrompt === undefined
|
|
130
|
+
? {}
|
|
131
|
+
: { baseSystemPrompt: options.baseSystemPrompt }),
|
|
132
|
+
...(options.turn === undefined ? {} : { turn: options.turn }),
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
export async function assembleContextSnapshot(assembler, options = {}) {
|
|
137
|
+
return (assembler ?? defaultContextAssembler).assemble(options);
|
|
138
|
+
}
|
|
99
139
|
//# sourceMappingURL=prompt-composer.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type ResourceScope = 'local' | 'project' | 'user';
|
|
2
|
+
export interface TextResource {
|
|
3
|
+
path: string;
|
|
4
|
+
scope: ResourceScope;
|
|
5
|
+
content: string;
|
|
6
|
+
importedFrom?: string;
|
|
7
|
+
importRoot?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface JsonResource {
|
|
10
|
+
path: string;
|
|
11
|
+
scope: ResourceScope;
|
|
12
|
+
value: unknown;
|
|
13
|
+
plugin?: true;
|
|
14
|
+
pluginName?: string;
|
|
15
|
+
pluginSource?: string;
|
|
16
|
+
environment?: Readonly<Record<string, string>>;
|
|
17
|
+
sensitiveValues?: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
export interface ContextResources {
|
|
20
|
+
instructions: TextResource[];
|
|
21
|
+
conditionalRules: ConditionalRule[];
|
|
22
|
+
memoryIndex: TextResource | null;
|
|
23
|
+
}
|
|
24
|
+
export interface ConditionalRule extends TextResource {
|
|
25
|
+
baseDirectory: string;
|
|
26
|
+
content: string;
|
|
27
|
+
globs: string[];
|
|
28
|
+
rawContent: string;
|
|
29
|
+
}
|
|
30
|
+
export interface SharedResources {
|
|
31
|
+
instructions: TextResource[];
|
|
32
|
+
memory: TextResource[];
|
|
33
|
+
skills: TextResource[];
|
|
34
|
+
commands: TextResource[];
|
|
35
|
+
agents: TextResource[];
|
|
36
|
+
settings: JsonResource[];
|
|
37
|
+
mcp: JsonResource[];
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=resources.d.ts.map
|
package/dist/core/runtime.d.ts
CHANGED
|
@@ -151,7 +151,7 @@ export interface ModelProvider {
|
|
|
151
151
|
}
|
|
152
152
|
export type RuntimeEvent = {
|
|
153
153
|
type: 'state';
|
|
154
|
-
state: Exclude<RuntimeState, 'idle'
|
|
154
|
+
state: Exclude<RuntimeState, 'idle'>;
|
|
155
155
|
} | {
|
|
156
156
|
type: 'text-delta';
|
|
157
157
|
delta: string;
|
|
@@ -419,6 +419,11 @@ export declare function permissionDecisionSource(decision: PermissionDecision):
|
|
|
419
419
|
export declare function annotateAutoModePermissionOutcome<T extends PermissionDecision>(decision: T, outcome: AutoModePermissionOutcome): T;
|
|
420
420
|
export declare function autoModePermissionOutcome(decision: PermissionDecision): AutoModePermissionOutcome | undefined;
|
|
421
421
|
export interface AgentRunObserver {
|
|
422
|
+
modelRequestCompleted?(input: {
|
|
423
|
+
usage: ModelUsage;
|
|
424
|
+
messages: readonly ModelMessage[];
|
|
425
|
+
tools: readonly ModelToolDefinition[];
|
|
426
|
+
}): Promise<void>;
|
|
422
427
|
assistantCompleted(message: {
|
|
423
428
|
content: string;
|
|
424
429
|
thinkingBlocks?: readonly ModelThinkingBlock[];
|
package/dist/core/runtime.js
CHANGED
|
@@ -368,6 +368,7 @@ export class AgentRuntime {
|
|
|
368
368
|
let textBytes = 0;
|
|
369
369
|
const thinkingBlocks = [];
|
|
370
370
|
let turnUsage = emptyUsage();
|
|
371
|
+
let turnUsageSeen = false;
|
|
371
372
|
let streaming = false;
|
|
372
373
|
const toolCalls = [];
|
|
373
374
|
let terminalReason;
|
|
@@ -477,9 +478,10 @@ export class AgentRuntime {
|
|
|
477
478
|
terminalReason = event.reason;
|
|
478
479
|
this.emit(event);
|
|
479
480
|
}
|
|
480
|
-
else {
|
|
481
|
+
else if (event.type === 'usage') {
|
|
481
482
|
activeAttemptHasPresentation = true;
|
|
482
483
|
turnUsage = event.usage;
|
|
484
|
+
turnUsageSeen = true;
|
|
483
485
|
this.emit(event);
|
|
484
486
|
}
|
|
485
487
|
}
|
|
@@ -528,6 +530,18 @@ export class AgentRuntime {
|
|
|
528
530
|
toolCalls.length > 0) {
|
|
529
531
|
return failScheduledTurn(new ModelProviderError(`Provider reported ${terminalReason} with completed tool calls`, { retryable: false }));
|
|
530
532
|
}
|
|
533
|
+
if (turnUsageSeen) {
|
|
534
|
+
try {
|
|
535
|
+
await request.observer?.modelRequestCompleted?.({
|
|
536
|
+
usage: turnUsage,
|
|
537
|
+
messages: providerRequest.messages,
|
|
538
|
+
tools: definitions,
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
catch (error) {
|
|
542
|
+
return failScheduledTurn(error);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
531
545
|
if (request.collectMetrics) {
|
|
532
546
|
const turnApiDurationWithoutRetriesMsResolved = turnApiDurationWithoutRetriesMs ?? turnApiDurationMs;
|
|
533
547
|
durationApiWithoutRetriesMs = addApiDurationMetric(turnApiDurationWithoutRetriesMsResolved, durationApiWithoutRetriesMs, 'durationApiWithoutRetriesMs');
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { TranscriptEvent } from './transcript-event.js';
|
|
2
|
+
export type TranscriptCodecDiagnostic = {
|
|
3
|
+
kind: 'unsupported-version';
|
|
4
|
+
lineNumber: number | null;
|
|
5
|
+
byteOffset: number;
|
|
6
|
+
message: string;
|
|
7
|
+
schemaVersion: string | number;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'corrupt-line';
|
|
10
|
+
lineNumber: number | null;
|
|
11
|
+
byteOffset: number;
|
|
12
|
+
message: string;
|
|
13
|
+
schemaVersion?: string | number;
|
|
14
|
+
} | {
|
|
15
|
+
kind: 'unsupported-event';
|
|
16
|
+
lineNumber: number | null;
|
|
17
|
+
byteOffset: number;
|
|
18
|
+
message: string;
|
|
19
|
+
eventKind?: string;
|
|
20
|
+
};
|
|
21
|
+
export interface OpaquePreservationToken {
|
|
22
|
+
readonly __opaqueToken: unique symbol;
|
|
23
|
+
}
|
|
24
|
+
type TokenPayload = {
|
|
25
|
+
owner: symbol;
|
|
26
|
+
raw: string;
|
|
27
|
+
eventFingerprint: string;
|
|
28
|
+
};
|
|
29
|
+
export type PreservationTokenSource = {
|
|
30
|
+
owner: symbol;
|
|
31
|
+
raw: string;
|
|
32
|
+
event: TranscriptEvent;
|
|
33
|
+
};
|
|
34
|
+
export declare function createPreservationToken(owner: symbol, source: PreservationTokenSource): OpaquePreservationToken;
|
|
35
|
+
export declare function readPreservationToken(token: OpaquePreservationToken, owner: symbol): TokenPayload | null;
|
|
36
|
+
export interface DecodedTranscriptRecord {
|
|
37
|
+
readonly event: TranscriptEvent;
|
|
38
|
+
readonly preservation?: OpaquePreservationToken;
|
|
39
|
+
}
|
|
40
|
+
export type TranscriptLineResult = {
|
|
41
|
+
ok: true;
|
|
42
|
+
record: DecodedTranscriptRecord;
|
|
43
|
+
} | {
|
|
44
|
+
ok: false;
|
|
45
|
+
issue: TranscriptCodecDiagnostic;
|
|
46
|
+
};
|
|
47
|
+
export type TranscriptEncodeResult = {
|
|
48
|
+
ok: true;
|
|
49
|
+
line: string;
|
|
50
|
+
} | {
|
|
51
|
+
ok: false;
|
|
52
|
+
issue: TranscriptCodecDiagnostic;
|
|
53
|
+
};
|
|
54
|
+
export interface TranscriptDocumentResult {
|
|
55
|
+
readonly records: readonly DecodedTranscriptRecord[];
|
|
56
|
+
readonly issue: TranscriptCodecDiagnostic | null;
|
|
57
|
+
readonly validPrefixByteLength: number;
|
|
58
|
+
}
|
|
59
|
+
export interface TranscriptCodec {
|
|
60
|
+
readonly id: string;
|
|
61
|
+
readonly version: string | number;
|
|
62
|
+
readonly writeMode: 'read-only' | 'read-write';
|
|
63
|
+
decodeLine(line: string, lineNumber?: number, byteOffset?: number): TranscriptLineResult;
|
|
64
|
+
encodeLine(event: TranscriptEvent, preservation?: OpaquePreservationToken): TranscriptEncodeResult;
|
|
65
|
+
decodeDocument(source: string | Uint8Array): TranscriptDocumentResult;
|
|
66
|
+
}
|
|
67
|
+
export declare function diagnostic(kind: TranscriptCodecDiagnostic['kind'], message: string, byteOffset: number, lineNumber: number | null, extra?: Partial<TranscriptCodecDiagnostic>): TranscriptCodecDiagnostic;
|
|
68
|
+
export declare function decodeDocumentWith(codec: TranscriptCodec, source: string | Uint8Array): TranscriptDocumentResult;
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=transcript-codec.d.ts.map
|