praxis-agent 0.34.0 → 0.35.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.
Files changed (49) hide show
  1. package/README.md +4 -0
  2. package/dist/application/session-service.d.ts +0 -1
  3. package/dist/application/session-service.js +1619 -1617
  4. package/dist/application/subagent-service.js +27 -16
  5. package/dist/application/turn-lifecycle.d.ts +38 -0
  6. package/dist/application/turn-lifecycle.js +36 -0
  7. package/dist/cli/interactive.js +17 -7
  8. package/dist/cli/protocol.js +5 -2
  9. package/dist/cli/tui/claude-style.d.ts +5 -48
  10. package/dist/cli/tui/claude-style.js +19 -73
  11. package/dist/cli/tui/transcript-presentation.d.ts +89 -0
  12. package/dist/cli/tui/transcript-presentation.js +169 -0
  13. package/dist/cli/tui/transcript-viewport.d.ts +5 -34
  14. package/dist/cli/tui/transcript-viewport.js +639 -158
  15. package/dist/cli/tui/tui-view-model.d.ts +6 -2
  16. package/dist/cli/tui/tui-view-model.js +23 -6
  17. package/dist/compatibility/claude/context.d.ts +2 -2
  18. package/dist/compatibility/claude/context.js +2 -14
  19. package/dist/compatibility/claude/data-plane-adapter.d.ts +9 -0
  20. package/dist/compatibility/claude/data-plane-adapter.js +44 -0
  21. package/dist/compatibility/claude/paths.js +2 -2
  22. package/dist/compatibility/claude/shared-resources.d.ts +7 -38
  23. package/dist/compatibility/claude/transcript-codec.d.ts +21 -0
  24. package/dist/compatibility/claude/transcript-codec.js +512 -0
  25. package/dist/core/context.d.ts +25 -7
  26. package/dist/core/context.js +46 -0
  27. package/dist/core/prompt-composer.d.ts +8 -16
  28. package/dist/core/prompt-composer.js +65 -25
  29. package/dist/core/resources.d.ts +39 -0
  30. package/dist/core/resources.js +2 -0
  31. package/dist/core/runtime.d.ts +1 -1
  32. package/dist/core/session.d.ts +2 -0
  33. package/dist/core/session.js +5 -0
  34. package/dist/core/transcript-codec.d.ts +70 -0
  35. package/dist/core/transcript-codec.js +60 -0
  36. package/dist/core/transcript-event.d.ts +33 -0
  37. package/dist/core/transcript-event.js +198 -0
  38. package/dist/persistence/data-plane-adapter.d.ts +32 -0
  39. package/dist/persistence/data-plane-adapter.js +2 -0
  40. package/dist/persistence/data-plane.d.ts +4 -20
  41. package/dist/persistence/data-plane.js +10 -47
  42. package/dist/persistence/native-data-plane-adapter.d.ts +9 -0
  43. package/dist/persistence/native-data-plane-adapter.js +39 -0
  44. package/dist/persistence/native-resources.d.ts +4 -4
  45. package/dist/persistence/native-transcript-codec.d.ts +13 -0
  46. package/dist/persistence/native-transcript-codec.js +102 -0
  47. package/dist/tools/claude-user-message.d.ts +0 -1
  48. package/dist/tools/claude-user-message.js +0 -1
  49. package/package.json +6 -2
@@ -1,6 +1,10 @@
1
- import { homedir } from 'node:os';
2
- import { resolve } from 'node:path';
3
- import { isClaudeSessionId, sanitizeClaudeProjectPath, } from '../compatibility/claude/paths.js';
1
+ import { claudeDataPlaneAdapter } from '../compatibility/claude/data-plane-adapter.js';
2
+ import { nativeDataPlaneAdapter } from './native-data-plane-adapter.js';
3
+ function adapterFor(dataPlane) {
4
+ return dataPlane === 'native'
5
+ ? nativeDataPlaneAdapter
6
+ : claudeDataPlaneAdapter;
7
+ }
4
8
  export function resolveDataPlane(environment = process.env) {
5
9
  const value = environment.PRAXIS_DATA_PLANE?.trim();
6
10
  if (value === undefined || value === '')
@@ -12,55 +16,14 @@ export function resolveDataPlane(environment = process.env) {
12
16
  export function resolveDataPlaneRoot(options = {}) {
13
17
  const environment = options.environment ?? process.env;
14
18
  const dataPlane = options.dataPlane ?? resolveDataPlane(environment);
15
- const homeDirectory = options.homeDirectory ?? homedir();
16
- if (options.root?.trim())
17
- return resolve(options.root);
18
- if (dataPlane === 'native') {
19
- const configuredRoot = environment.PRAXIS_HOME;
20
- return resolve(configuredRoot?.trim()
21
- ? configuredRoot
22
- : resolve(homeDirectory, '.praxis'));
23
- }
24
- const configuredRoot = environment.CLAUDE_CONFIG_DIR;
25
- return resolve(configuredRoot?.trim() ? configuredRoot : resolve(homeDirectory, '.claude'));
19
+ return adapterFor(dataPlane).resolveRoot(options);
26
20
  }
27
21
  export function resolveDataPlanePaths(options) {
28
- if (!isClaudeSessionId(options.sessionId)) {
29
- throw new Error(`Invalid session ID: ${options.sessionId}`);
30
- }
31
22
  const environment = options.environment ?? process.env;
32
23
  const dataPlane = options.dataPlane ?? resolveDataPlane(environment);
33
- const root = resolveDataPlaneRoot({
34
- dataPlane,
35
- environment,
36
- ...(options.root === undefined ? {} : { root: options.root }),
37
- ...(options.homeDirectory === undefined
38
- ? {}
39
- : { homeDirectory: options.homeDirectory }),
40
- });
41
- const projectKey = sanitizeClaudeProjectPath(options.cwd);
42
- const projectRoot = dataPlane === 'native'
43
- ? resolve(root, 'sessions', projectKey)
44
- : resolve(root, 'projects', projectKey);
45
- const stateRoot = dataPlane === 'native' ? resolve(root, 'state') : resolve(root, 'praxis');
46
- return {
47
- dataPlane,
48
- root,
49
- projectRoot,
50
- sessionFile: resolve(projectRoot, `${options.sessionId}.jsonl`),
51
- taskRoot: dataPlane === 'native'
52
- ? resolve(root, 'tasks', options.sessionId)
53
- : resolve(root, 'tasks', options.sessionId),
54
- stateRoot,
55
- praxisRoot: stateRoot,
56
- memoryRoot: dataPlane === 'native'
57
- ? resolve(root, 'memory', projectKey)
58
- : resolve(root, 'projects', projectKey, 'memory'),
59
- };
24
+ return adapterFor(dataPlane).resolvePaths(options);
60
25
  }
61
26
  export function resolveScheduledTaskFile(options) {
62
- return options.dataPlane === 'native'
63
- ? resolve(options.root, 'scheduled', `${sanitizeClaudeProjectPath(options.cwd)}.json`)
64
- : resolve(options.cwd, '.claude', 'scheduled_tasks.json');
27
+ return adapterFor(options.dataPlane).resolveScheduledTaskFile(options);
65
28
  }
66
29
  //# sourceMappingURL=data-plane.js.map
@@ -0,0 +1,9 @@
1
+ import type { DataPlaneAdapter, DataPlaneAdapterOptions, DataPlanePaths, DataPlaneRootOptions, ScheduledTaskFileOptions } from './data-plane-adapter.js';
2
+ export declare class NativeDataPlaneAdapter implements DataPlaneAdapter {
3
+ readonly dataPlane: "native";
4
+ resolveRoot(options: DataPlaneRootOptions): string;
5
+ resolvePaths(options: DataPlaneAdapterOptions): DataPlanePaths;
6
+ resolveScheduledTaskFile(options: ScheduledTaskFileOptions): string;
7
+ }
8
+ export declare const nativeDataPlaneAdapter: NativeDataPlaneAdapter;
9
+ //# sourceMappingURL=native-data-plane-adapter.d.ts.map
@@ -0,0 +1,39 @@
1
+ import { homedir } from 'node:os';
2
+ import { resolve } from 'node:path';
3
+ import { isSessionId } from '../core/session.js';
4
+ import { sanitizeProjectPath } from '../platform/project-path-key.js';
5
+ export class NativeDataPlaneAdapter {
6
+ dataPlane = 'native';
7
+ resolveRoot(options) {
8
+ const environment = options.environment ?? process.env;
9
+ if (options.root?.trim())
10
+ return resolve(options.root);
11
+ const configuredRoot = environment.PRAXIS_HOME;
12
+ return resolve(configuredRoot?.trim()
13
+ ? configuredRoot
14
+ : resolve(options.homeDirectory ?? homedir(), '.praxis'));
15
+ }
16
+ resolvePaths(options) {
17
+ if (!isSessionId(options.sessionId)) {
18
+ throw new Error(`Invalid session ID: ${options.sessionId}`);
19
+ }
20
+ const root = this.resolveRoot(options);
21
+ const projectKey = sanitizeProjectPath(options.cwd);
22
+ const projectRoot = resolve(root, 'sessions', projectKey);
23
+ return {
24
+ dataPlane: 'native',
25
+ root,
26
+ projectRoot,
27
+ sessionFile: resolve(projectRoot, `${options.sessionId}.jsonl`),
28
+ taskRoot: resolve(root, 'tasks', options.sessionId),
29
+ stateRoot: resolve(root, 'state'),
30
+ praxisRoot: resolve(root, 'state'),
31
+ memoryRoot: resolve(root, 'memory', projectKey),
32
+ };
33
+ }
34
+ resolveScheduledTaskFile(options) {
35
+ return resolve(options.root, 'scheduled', `${sanitizeProjectPath(options.cwd)}.json`);
36
+ }
37
+ }
38
+ export const nativeDataPlaneAdapter = new NativeDataPlaneAdapter();
39
+ //# sourceMappingURL=native-data-plane-adapter.js.map
@@ -1,11 +1,11 @@
1
- import type { ClaudeContextResources, ClaudeJsonResource, ClaudeSharedResources } from '../compatibility/claude/shared-resources.js';
1
+ import type { ContextResources, JsonResource, SharedResources } from '../core/resources.js';
2
2
  export interface LoadNativeResourcesOptions {
3
3
  root: string;
4
4
  cwd: string;
5
5
  environment?: Readonly<Record<string, string | undefined>>;
6
6
  includeProjectMemory?: boolean;
7
7
  }
8
- export declare function loadNativeSettings({ root, cwd, }: LoadNativeResourcesOptions): Promise<ClaudeJsonResource[]>;
9
- export declare function loadNativeSharedResources({ root, cwd, environment, includeProjectMemory, }: LoadNativeResourcesOptions): Promise<ClaudeSharedResources>;
10
- export declare function loadNativeContextResources(options: LoadNativeResourcesOptions): Promise<ClaudeContextResources>;
8
+ export declare function loadNativeSettings({ root, cwd, }: LoadNativeResourcesOptions): Promise<JsonResource[]>;
9
+ export declare function loadNativeSharedResources({ root, cwd, environment, includeProjectMemory, }: LoadNativeResourcesOptions): Promise<SharedResources>;
10
+ export declare function loadNativeContextResources(options: LoadNativeResourcesOptions): Promise<ContextResources>;
11
11
  //# sourceMappingURL=native-resources.d.ts.map
@@ -0,0 +1,13 @@
1
+ import { type TranscriptEvent } from '../core/transcript-event.js';
2
+ import { type OpaquePreservationToken, type TranscriptCodec, type TranscriptDocumentResult, type TranscriptEncodeResult, type TranscriptLineResult } from '../core/transcript-codec.js';
3
+ export declare class NativeTranscriptCodec implements TranscriptCodec {
4
+ readonly id = "praxis.transcript";
5
+ readonly version: number;
6
+ readonly writeMode: 'read-only' | 'read-write';
7
+ constructor(version?: number);
8
+ decodeLine(line: string, lineNumber?: number, byteOffset?: number): TranscriptLineResult;
9
+ encodeLine(event: TranscriptEvent, preservation?: OpaquePreservationToken): TranscriptEncodeResult;
10
+ decodeDocument(source: string | Uint8Array): TranscriptDocumentResult;
11
+ }
12
+ export declare function createNativeTranscriptCodec(version?: number): NativeTranscriptCodec;
13
+ //# sourceMappingURL=native-transcript-codec.d.ts.map
@@ -0,0 +1,102 @@
1
+ import { isTranscriptEvent, } from '../core/transcript-event.js';
2
+ import { createPreservationToken, readPreservationToken, decodeDocumentWith, diagnostic, } from '../core/transcript-codec.js';
3
+ const TOKEN_ID = Symbol('praxis.native.transcript.v1');
4
+ export class NativeTranscriptCodec {
5
+ id = 'praxis.transcript';
6
+ version;
7
+ writeMode;
8
+ constructor(version = 1) {
9
+ this.version = version;
10
+ this.writeMode = version === 1 ? 'read-write' : 'read-only';
11
+ }
12
+ decodeLine(line, lineNumber = 1, byteOffset = 0) {
13
+ let value;
14
+ try {
15
+ value = JSON.parse(line);
16
+ }
17
+ catch {
18
+ return {
19
+ ok: false,
20
+ issue: diagnostic('corrupt-line', 'Invalid JSON transcript line', byteOffset, lineNumber, { schemaVersion: this.version }),
21
+ };
22
+ }
23
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
24
+ return {
25
+ ok: false,
26
+ issue: diagnostic('corrupt-line', 'Transcript line must be an object', byteOffset, lineNumber, { schemaVersion: this.version }),
27
+ };
28
+ const object = value;
29
+ if (object.schema !== this.id ||
30
+ (typeof object.version !== 'number' && typeof object.version !== 'string'))
31
+ return {
32
+ ok: false,
33
+ issue: diagnostic('corrupt-line', 'Invalid native transcript envelope', byteOffset, lineNumber, { schemaVersion: this.version }),
34
+ };
35
+ if (object.version !== 1)
36
+ return {
37
+ ok: false,
38
+ issue: diagnostic('unsupported-version', `Unsupported native transcript version ${String(object.version)}`, byteOffset, lineNumber, { schemaVersion: object.version }),
39
+ };
40
+ if (!isTranscriptEvent(object.event))
41
+ return {
42
+ ok: false,
43
+ issue: diagnostic('unsupported-event', 'Invalid or unsupported TranscriptEvent', byteOffset, lineNumber, typeof object.event === 'object' && object.event !== null
44
+ ? {
45
+ eventKind: String(object.event.kind ?? ''),
46
+ }
47
+ : {}),
48
+ };
49
+ return {
50
+ ok: true,
51
+ record: {
52
+ event: object.event,
53
+ preservation: createPreservationToken(TOKEN_ID, {
54
+ owner: TOKEN_ID,
55
+ raw: line,
56
+ event: object.event,
57
+ }),
58
+ },
59
+ };
60
+ }
61
+ encodeLine(event, preservation) {
62
+ if (this.writeMode === 'read-only')
63
+ return {
64
+ ok: false,
65
+ issue: diagnostic('unsupported-version', 'Native codec is read-only', 0, null, { schemaVersion: this.version }),
66
+ };
67
+ if (!isTranscriptEvent(event))
68
+ return {
69
+ ok: false,
70
+ issue: diagnostic('unsupported-event', 'Invalid TranscriptEvent', 0, null, typeof event === 'object' && event !== null
71
+ ? {
72
+ eventKind: String(event.kind ?? ''),
73
+ }
74
+ : {}),
75
+ };
76
+ if (preservation) {
77
+ const token = readPreservationToken(preservation, TOKEN_ID);
78
+ if (!token)
79
+ return {
80
+ ok: false,
81
+ issue: diagnostic('unsupported-event', 'Preservation token belongs to another codec', 0, null),
82
+ };
83
+ return {
84
+ ok: true,
85
+ line: token.eventFingerprint === JSON.stringify(event)
86
+ ? token.raw
87
+ : JSON.stringify({ schema: this.id, version: 1, event }),
88
+ };
89
+ }
90
+ return {
91
+ ok: true,
92
+ line: JSON.stringify({ schema: this.id, version: 1, event }),
93
+ };
94
+ }
95
+ decodeDocument(source) {
96
+ return decodeDocumentWith(this, source);
97
+ }
98
+ }
99
+ export function createNativeTranscriptCodec(version = 1) {
100
+ return new NativeTranscriptCodec(version);
101
+ }
102
+ //# sourceMappingURL=native-transcript-codec.js.map
@@ -4,7 +4,6 @@ export interface UserMessage {
4
4
  attachments: readonly string[];
5
5
  status: 'normal' | 'proactive';
6
6
  }
7
- export declare const CLAUDE_USER_MESSAGE_PROMPT = "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.";
8
7
  export declare class ClaudeUserMessageToolRegistry implements ToolRegistry {
9
8
  private readonly base;
10
9
  private readonly onMessage;
@@ -1,7 +1,6 @@
1
1
  import { stat } from 'node:fs/promises';
2
2
  import { extname, resolve } from 'node:path';
3
3
  import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
4
- export const CLAUDE_USER_MESSAGE_PROMPT = '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.';
5
4
  const DEFINITION = {
6
5
  name: 'SendUserMessage',
7
6
  description: 'Send a visible message to the user. Use this for progress updates, blockers, or completion notices when brief mode is enabled.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "praxis-agent",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "description": "Local-first, single-user general agent for the command line.",
5
5
  "license": "MIT",
6
6
  "author": "wuqisen",
@@ -30,12 +30,15 @@
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsc -p tsconfig.build.json",
33
- "check": "npm run format:check && npm run lint && npm run test:docs && npm run verify:release-automation && npm run check:boundaries && npm run typecheck && npm test && npm run build",
33
+ "check": "npm run format:check && npm run lint && npm run test:docs && npm run verify:release-automation && npm run check:boundaries && npm run typecheck && npm run build:native && npm test && npm run build",
34
34
  "check:boundaries": "node scripts/check-boundaries.mjs",
35
35
  "dev": "tsx src/cli.ts",
36
36
  "format": "prettier --write .",
37
37
  "format:check": "prettier --check .",
38
38
  "lint": "eslint .",
39
+ "clean:native": "node -e \"require('node:fs').rmSync('dist-native',{recursive:true,force:true})\"",
40
+ "prebuild:native": "npm run clean:native",
41
+ "build:native": "tsc -p tsconfig.native.json",
39
42
  "prebuild": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
40
43
  "prepack": "npm run build",
41
44
  "release:artifacts": "node scripts/build-release-artifacts.mjs",
@@ -63,6 +66,7 @@
63
66
  "test:mcp-resource-compat": "npm run build && node scripts/verify-mcp-resource-compatibility.mjs",
64
67
  "test:notebook-compat": "npm run build && node scripts/verify-notebook-compatibility.mjs",
65
68
  "test:context-compat": "npm run build && node scripts/verify-context-runtime.mjs",
69
+ "test:core-design-drift": "npm run build && node scripts/verify-core-design-drift.mjs",
66
70
  "test:cross-version-session-compat": "npm run build && node scripts/verify-cross-version-session-compatibility.mjs",
67
71
  "test:cross-version-resume-at-compat": "npm run build && node scripts/verify-cross-version-resume-at-compatibility.mjs",
68
72
  "test:cross-version-fork-compat": "npm run build && node scripts/verify-cross-version-fork-compatibility.mjs",