praxis-agent 0.38.0 → 0.40.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 (198) hide show
  1. package/README.md +44 -67
  2. package/dist/application/background-agent-manager.js +7 -4
  3. package/dist/application/background-bash-manager.d.ts +2 -2
  4. package/dist/application/background-bash-manager.js +6 -6
  5. package/dist/application/claude-project-purge.js +36 -74
  6. package/dist/application/native-session-transcript.d.ts +14 -3
  7. package/dist/application/native-session-transcript.js +30 -9
  8. package/dist/application/scheduled-prompt-manager.d.ts +2 -2
  9. package/dist/application/scheduled-prompt-manager.js +3 -3
  10. package/dist/application/session-memory.d.ts +2 -5
  11. package/dist/application/session-memory.js +4 -7
  12. package/dist/application/session-service.d.ts +19 -32
  13. package/dist/application/session-service.js +1085 -1785
  14. package/dist/application/session-worktree.d.ts +2 -3
  15. package/dist/application/session-worktree.js +3 -3
  16. package/dist/application/subagent-service.d.ts +2 -5
  17. package/dist/application/subagent-service.js +152 -319
  18. package/dist/application/top-level-agent-manager.js +19 -37
  19. package/dist/application/transcript-projection.js +18 -2
  20. package/dist/application/workflow-manager.d.ts +1 -1
  21. package/dist/application/workflow-manager.js +11 -19
  22. package/dist/cli/controls.d.ts +10 -6
  23. package/dist/cli/controls.js +28 -6
  24. package/dist/cli/interactive.d.ts +6 -4
  25. package/dist/cli/interactive.js +98 -82
  26. package/dist/cli/protocol.d.ts +1 -1
  27. package/dist/cli/protocol.js +0 -13
  28. package/dist/cli/tui/ansi-frame-renderer.d.ts +24 -0
  29. package/dist/cli/tui/ansi-frame-renderer.js +120 -0
  30. package/dist/cli/tui/ansi-surface.d.ts +26 -0
  31. package/dist/cli/tui/ansi-surface.js +187 -0
  32. package/dist/cli/tui/ansi-theme.d.ts +3 -0
  33. package/dist/cli/tui/ansi-theme.js +83 -0
  34. package/dist/cli/tui/claude-style.d.ts +2 -4
  35. package/dist/cli/tui/claude-style.js +6 -7
  36. package/dist/cli/tui/config-settings.js +5 -14
  37. package/dist/cli/tui/doctor-dashboard.js +1 -1
  38. package/dist/cli/tui/hook-settings.d.ts +2 -2
  39. package/dist/cli/tui/kernel/tui-effect-runner.d.ts +21 -0
  40. package/dist/cli/tui/kernel/tui-effect-runner.js +90 -0
  41. package/dist/cli/tui/kernel/tui-runtime-kernel.d.ts +23 -0
  42. package/dist/cli/tui/kernel/tui-runtime-kernel.js +35 -0
  43. package/dist/cli/tui/kernel/tui-store.d.ts +22 -0
  44. package/dist/cli/tui/kernel/tui-store.js +37 -0
  45. package/dist/cli/tui/mcp-panel.d.ts +1 -3
  46. package/dist/cli/tui/mcp-panel.js +5 -8
  47. package/dist/cli/tui/memory-files.d.ts +3 -5
  48. package/dist/cli/tui/memory-files.js +12 -30
  49. package/dist/cli/tui/permission-settings.d.ts +6 -9
  50. package/dist/cli/tui/permission-settings.js +9 -15
  51. package/dist/cli/tui/praxis-logo.d.ts +5 -0
  52. package/dist/cli/tui/praxis-logo.js +17 -0
  53. package/dist/cli/tui/sandbox-settings.d.ts +3 -5
  54. package/dist/cli/tui/sandbox-settings.js +5 -9
  55. package/dist/cli/tui/slash-commands.js +1 -1
  56. package/dist/cli/tui/status-line.d.ts +5 -5
  57. package/dist/cli/tui/status-line.js +12 -30
  58. package/dist/cli/tui/terminal-setup.js +2 -4
  59. package/dist/cli/tui/theme.d.ts +2 -2
  60. package/dist/cli/tui/tool-permission.d.ts +1 -1
  61. package/dist/cli/tui/tool-permission.js +13 -15
  62. package/dist/cli/tui/transcript-viewport.d.ts +1 -0
  63. package/dist/cli/tui/transcript-viewport.js +3 -0
  64. package/dist/cli/tui/tui-focus-stack.d.ts +19 -0
  65. package/dist/cli/tui/tui-focus-stack.js +72 -0
  66. package/dist/cli/tui/tui-row-ir.d.ts +19 -0
  67. package/dist/cli/tui/tui-row-ir.js +126 -0
  68. package/dist/cli/tui/tui-screen-model.d.ts +2 -0
  69. package/dist/cli/tui/tui-screen-model.js +6 -0
  70. package/dist/cli-runtime.d.ts +1 -1
  71. package/dist/cli-runtime.js +94 -397
  72. package/dist/{compatibility/claude → core}/agent-color.d.ts +1 -2
  73. package/dist/core/project-memory.d.ts +4 -3
  74. package/dist/core/project-memory.js +2 -4
  75. package/dist/core/transcript-event.d.ts +4 -0
  76. package/dist/core/transcript-event.js +11 -1
  77. package/dist/extensions/claude-extensions.d.ts +5 -6
  78. package/dist/extensions/claude-extensions.js +13 -27
  79. package/dist/extensions/claude-init-command.d.ts +2 -3
  80. package/dist/extensions/claude-init-command.js +7 -70
  81. package/dist/hooks/claude-hooks.d.ts +4 -4
  82. package/dist/maintenance/doctor-diagnostic.d.ts +0 -1
  83. package/dist/maintenance/doctor-diagnostic.js +0 -4
  84. package/dist/maintenance/doctor.d.ts +8 -4
  85. package/dist/maintenance/doctor.js +10 -63
  86. package/dist/mcp/claude-mcp-management.d.ts +3 -3
  87. package/dist/mcp/claude-mcp-management.js +16 -37
  88. package/dist/mcp/claude-mcp-oauth.js +1 -1
  89. package/dist/mcp/claude-mcp-tools.d.ts +4 -4
  90. package/dist/mcp/claude-mcp-tools.js +1 -3
  91. package/dist/{compatibility/claude → native}/compaction.d.ts +4 -4
  92. package/dist/{compatibility/claude → native}/context.d.ts +6 -2
  93. package/dist/{compatibility/claude → native}/context.js +21 -9
  94. package/dist/native/file-history.d.ts +14 -0
  95. package/dist/{compatibility/claude → native}/fork.d.ts +3 -3
  96. package/dist/{compatibility/claude → native}/fork.js +4 -4
  97. package/dist/{compatibility/claude → native}/history.d.ts +6 -6
  98. package/dist/{compatibility/claude → native}/interruption.d.ts +3 -3
  99. package/dist/{compatibility/claude → native}/ownership.d.ts +17 -17
  100. package/dist/{compatibility/claude → native}/ownership.js +17 -17
  101. package/dist/{compatibility/claude → native}/paths.d.ts +8 -8
  102. package/dist/{compatibility/claude → native}/paths.js +23 -26
  103. package/dist/{compatibility/claude → native}/pr-links.d.ts +2 -2
  104. package/dist/{compatibility/claude → native}/projection.d.ts +8 -8
  105. package/dist/native/schema.d.ts +14 -0
  106. package/dist/native/schema.js +24 -0
  107. package/dist/{compatibility/claude → native}/session-metadata.d.ts +6 -6
  108. package/dist/{compatibility/claude → native}/sidechain.d.ts +4 -4
  109. package/dist/{compatibility/claude → native}/tool-links.d.ts +5 -5
  110. package/dist/{compatibility/claude → native}/translation.d.ts +10 -10
  111. package/dist/{compatibility/claude → native}/translation.js +17 -7
  112. package/dist/permissions/bash-path-safety.js +2 -4
  113. package/dist/permissions/bypass-immune-paths.d.ts +0 -2
  114. package/dist/permissions/bypass-immune-paths.js +2 -2
  115. package/dist/permissions/claude-auto-classifier.d.ts +2 -2
  116. package/dist/permissions/claude-permission-resolver.d.ts +3 -3
  117. package/dist/permissions/claude-permission-resolver.js +9 -13
  118. package/dist/persistence/claude-cost-state-store.js +1 -2
  119. package/dist/persistence/claude-workflow-store.d.ts +26 -4
  120. package/dist/persistence/claude-workflow-store.js +47 -2
  121. package/dist/persistence/data-plane-adapter.d.ts +1 -2
  122. package/dist/persistence/data-plane.d.ts +2 -3
  123. package/dist/persistence/data-plane.js +15 -12
  124. package/dist/persistence/in-memory-sidechain-store.d.ts +24 -7
  125. package/dist/persistence/in-memory-sidechain-store.js +23 -19
  126. package/dist/persistence/in-memory-transcript-store.d.ts +13 -6
  127. package/dist/persistence/in-memory-transcript-store.js +38 -58
  128. package/dist/persistence/native-resources.js +63 -7
  129. package/dist/persistence/{claude-scheduled-task-store.d.ts → native-scheduled-task-store.d.ts} +10 -10
  130. package/dist/persistence/{claude-scheduled-task-store.js → native-scheduled-task-store.js} +14 -14
  131. package/dist/persistence/native-transcript-reader.d.ts +6 -6
  132. package/dist/persistence/native-transcript-reader.js +2 -2
  133. package/dist/persistence/native-transcript-store.d.ts +9 -0
  134. package/dist/persistence/native-transcript-store.js +47 -5
  135. package/dist/persistence/subagent-lifecycle-store.js +3 -4
  136. package/dist/persistence/unknown-cost-sidecar.d.ts +2 -2
  137. package/dist/persistence/unknown-cost-sidecar.js +2 -2
  138. package/dist/platform/project-memory-paths.d.ts +3 -4
  139. package/dist/platform/project-memory-paths.js +4 -5
  140. package/dist/plugins/claude-plugin-marketplace.js +3 -2
  141. package/dist/plugins/claude-plugin-mcpb.js +3 -4
  142. package/dist/plugins/claude-plugin-runtime.d.ts +6 -6
  143. package/dist/providers/anthropic-prompt-cache.d.ts +1 -1
  144. package/dist/providers/anthropic-prompt-cache.js +3 -26
  145. package/dist/sandbox/claude-sandbox-settings.d.ts +5 -6
  146. package/dist/sandbox/claude-sandbox-settings.js +5 -5
  147. package/dist/tools/claude-interactive-tools.d.ts +1 -2
  148. package/dist/tools/claude-interactive-tools.js +3 -1
  149. package/dist/tools/claude-scheduled-tools.js +6 -7
  150. package/dist/tools/claude-workflow-tools.d.ts +0 -2
  151. package/dist/tools/claude-workflow-tools.js +4 -4
  152. package/dist/tools/claude-worktree-tools.js +3 -5
  153. package/dist/tools/local-tools.d.ts +2 -1
  154. package/dist/tools/local-tools.js +1 -4
  155. package/dist/tools/team-lead-tools.d.ts +1 -15
  156. package/dist/tools/team-lead-tools.js +2 -124
  157. package/package.json +4 -67
  158. package/dist/compatibility/claude/data-plane-adapter.d.ts +0 -9
  159. package/dist/compatibility/claude/data-plane-adapter.js +0 -44
  160. package/dist/compatibility/claude/file-history.d.ts +0 -14
  161. package/dist/compatibility/claude/schema.d.ts +0 -26
  162. package/dist/compatibility/claude/schema.js +0 -1071
  163. package/dist/compatibility/claude/shared-resources.d.ts +0 -27
  164. package/dist/compatibility/claude/shared-resources.js +0 -459
  165. package/dist/compatibility/claude/team.d.ts +0 -77
  166. package/dist/compatibility/claude/team.js +0 -219
  167. package/dist/compatibility/claude/transcript-codec.d.ts +0 -21
  168. package/dist/compatibility/claude/transcript-codec.js +0 -518
  169. package/dist/persistence/claude-migration.d.ts +0 -5
  170. package/dist/persistence/claude-migration.js +0 -225
  171. package/dist/persistence/claude-session-index.d.ts +0 -39
  172. package/dist/persistence/claude-session-index.js +0 -175
  173. package/dist/persistence/claude-sidechain-store.d.ts +0 -18
  174. package/dist/persistence/claude-sidechain-store.js +0 -123
  175. package/dist/persistence/claude-transcript-store.d.ts +0 -90
  176. package/dist/persistence/claude-transcript-store.js +0 -469
  177. package/dist/persistence/native-transcript-migration.d.ts +0 -37
  178. package/dist/persistence/native-transcript-migration.js +0 -430
  179. package/dist/platform/claude-version.d.ts +0 -5
  180. package/dist/platform/claude-version.js +0 -18
  181. /package/dist/{compatibility/claude → core}/agent-color.js +0 -0
  182. /package/dist/{compatibility/claude → native}/compaction.js +0 -0
  183. /package/dist/{compatibility/claude → native}/dynamic-context.d.ts +0 -0
  184. /package/dist/{compatibility/claude → native}/dynamic-context.js +0 -0
  185. /package/dist/{compatibility/claude → native}/file-history.js +0 -0
  186. /package/dist/{compatibility/claude → native}/file-resources.d.ts +0 -0
  187. /package/dist/{compatibility/claude → native}/file-resources.js +0 -0
  188. /package/dist/{compatibility/claude → native}/history.js +0 -0
  189. /package/dist/{compatibility/claude → native}/interruption.js +0 -0
  190. /package/dist/{compatibility/claude → native}/pr-links.js +0 -0
  191. /package/dist/{compatibility/claude → native}/projection.js +0 -0
  192. /package/dist/{compatibility/claude → native}/session-metadata.js +0 -0
  193. /package/dist/{compatibility/claude → native}/sidechain.js +0 -0
  194. /package/dist/{compatibility/claude → native}/tool-links.js +0 -0
  195. /package/dist/{compatibility/claude → native}/workflow-replay.d.ts +0 -0
  196. /package/dist/{compatibility/claude → native}/workflow-replay.js +0 -0
  197. /package/dist/{compatibility/claude → native}/workflow.d.ts +0 -0
  198. /package/dist/{compatibility/claude → native}/workflow.js +0 -0
package/README.md CHANGED
@@ -17,15 +17,10 @@ Anthropic/OpenAI-compatible model access. Praxis deliberately excludes
17
17
  accounts, organizations, billing, managed enterprise policy, remote control,
18
18
  IDE surfaces, and telemetry control planes.
19
19
 
20
- Claude Code 2.1.208 remains the architecture, design, and observable
21
- compatibility baseline for included and required single-user developer
22
- capabilities. Only entries classified `required` block developer-core
23
- closure; `deferred` entries are optional and demand-driven. Exclusions cover
24
- the existing enterprise, authentication, hosted, and client surfaces plus the
25
- explicitly classified subscription-bound integration, campaign, hidden
26
- maintainer diagnostic, and build-experimental commands. A similar Praxis
27
- surface is not a substitute for the corresponding Claude command or runtime
28
- contract.
20
+ Claude Code 2.1.208 remains a clean-room behavioral reference for the
21
+ developer-facing CLI surface. It is not a runtime dependency or a data source:
22
+ Praxis runs one native data plane and does not read or write Claude Code
23
+ sessions, configuration, or compatibility directories.
29
24
 
30
25
  ## Requirements
31
26
 
@@ -34,13 +29,12 @@ contract.
34
29
  - [`ripgrep`](https://github.com/BurntSushi/ripgrep) (`rg`) for the Grep tool
35
30
  - an API key and model ID for an Anthropic or OpenAI-compatible provider
36
31
 
37
- Praxis does not use Claude subscription authentication. Claude Code
38
- interoperability covers local sessions, configuration, permissions, memory,
39
- skills, hooks, agents, plugins, and MCP data.
32
+ Praxis does not use Claude subscription authentication. Claude-shaped message,
33
+ tool, and CLI protocol forms remain supported where they are part of the
34
+ public surface, while all persisted state is Praxis-native.
40
35
 
41
- Claude Team compatibility is explicit and fail-closed: supported delete/send
42
- wire shapes route through native Team operations, while lossy or unsupported
43
- Team surfaces are rejected rather than silently simplified.
36
+ Team operations are local-only and opt-in. Unsupported or lossy Team payloads
37
+ are rejected rather than silently simplified.
44
38
 
45
39
  ## Install
46
40
 
@@ -89,14 +83,15 @@ praxis doctor
89
83
 
90
84
  See
91
85
  [Getting Started](https://github.com/Forest-Isle/Praxis/blob/main/docs/GETTING_STARTED.md)
92
- for provider setup, shared Claude state, permissions, updating, and
86
+ for provider setup, shared Praxis state, permissions, updating, and
93
87
  troubleshooting. Run `praxis --help` for the authoritative command surface.
94
88
 
95
89
  ## What Praxis provides
96
90
 
97
91
  - **Local agent runtime** — Claude-style responsive TUI with a fixed fullscreen
98
92
  viewport, non-shrinking composer/status area, and complete bounded welcome
99
- surface, plus a shared-command
93
+ surface with the reusable P-loop + spark Praxis mark and ANSI/no-color
94
+ fallbacks, plus a shared-command
100
95
  slash palette, tabbed help and shortcut surfaces, searchable resume picker,
101
96
  restored active-branch conversation history, streaming and expandable
102
97
  thinking, grouped multi-file reads, globally expandable tool results,
@@ -111,12 +106,34 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
111
106
  skills/hooks flow, provider-free per-session `/color` prompt-bar styling,
112
107
  `/mcp`, `/memory` shared instruction and auto-memory
113
108
  access, and live extension-reload controls,
109
+ plus a framework-free runtime kernel that atomically publishes streaming
110
+ frames and keeps status/busy transitions deterministic, with a unified
111
+ framework-free TuiStore that retains runtime and composer state together,
112
+ with a shared semantic transcript Row IR for renderer-neutral layout and
113
+ stable incremental updates,
114
+ backed by the authoritative grapheme/Markdown viewport layout so narrow and
115
+ Unicode-heavy transcripts wrap consistently across renderers,
116
+ and an independent ANSI fullscreen frame renderer with alternate-screen
117
+ lifecycle, synchronized output, and dirty-row diffing,
118
+ opt-in TTY wiring with automatic Ink fallback on renderer failure,
119
+ with complex overlays and dialogs intentionally remaining on the mature Ink
120
+ surface until their semantic ANSI projections are complete,
121
+ while welcome and identity intros also remain on Ink for a complete first
122
+ frame,
123
+ and a pure FocusStack that centralizes overlay/dialog precedence and Esc
124
+ cancellation routing,
125
+ plus a generation-aware effect runner that drops stale asynchronous results
126
+ and aborts cleanly on replacement or unmount,
127
+ with ANSI fullscreen text styles derived from the active semantic theme and
128
+ automatic no-color/screen-reader suppression,
114
129
  cursor/history composer, provider-free `/cost` usage and pricing summaries,
130
+ with a hermetic PTY smoke covering real `runInteractive` ANSI entry,
131
+ resize-safe lifecycle, and Ctrl-C restoration,
115
132
  interactive `/doctor` diagnostics, per-session model/effort/permission controls,
116
133
  context/status/skill/task dashboards, prompt stash and continuation shortcuts,
117
134
  filterable `@` file and agent references, composer undo, `Ctrl+G` external
118
135
  editing, shared `/keybindings` creation/editing and supported-action remapping,
119
- shared built-in and Claude-compatible custom `/theme` profiles with immediate
136
+ shared built-in and custom `/theme` profiles with immediate
120
137
  semantic recoloring, token editing/reset, deletion, and persisted syntax
121
138
  toggles across transcript code and diff views,
122
139
  shared runtime preferences for reduced motion, spinner tips, progress and
@@ -174,16 +191,12 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
174
191
  Lead Decision queue with provenance. Coordinator leads are restricted to
175
192
  orchestration, and custom Team agents receive no MCP capability. The native
176
193
  CLI also exposes `praxis team status`, `logs`, and `attach` in human or JSON
177
- form; durable-local attach does not require tmux. The Claude Team adapter is
178
- removable and currently limited to fixture-verified delete/send; Claude create
179
- is decoded but rejected when the payload cannot represent native roster/task
180
- claims,
181
- shutdown, and plan-response shapes; native task, notification, context, and
182
- Team resume/inbox seams are implemented, while their Claude-facing zero-skip
183
- qualification remains explicitly separate.
184
- - **Claude-compatible ecosystem** — shared instructions with recursive `@`
194
+ form; durable-local attach does not require tmux. Native task, notification,
195
+ context, and Team resume/inbox seams are implemented with fail-closed
196
+ validation for unsupported payloads.
197
+ - **Native resource ecosystem** — shared Praxis instructions with recursive `@`
185
198
  imports, memory, skills, commands, agents, hooks, settings, MCP servers,
186
- plugins, and transcript data.
199
+ plugins, and append-only `praxis.transcript` JSONL sessions under `~/.praxis`.
187
200
  - **Provider-neutral models** — native Anthropic Messages and OpenAI-compatible
188
201
  streaming adapters with explicit capability checks and metering controls.
189
202
 
@@ -191,39 +204,6 @@ Detailed feature status and executable evidence live in the
191
204
  [parity matrix](https://github.com/Forest-Isle/Praxis/blob/main/docs/PARITY_MATRIX.md),
192
205
  not in this entry-point README.
193
206
 
194
- ## Claude Code interoperability
195
-
196
- Praxis uses an independent local data plane by default:
197
-
198
- ```text
199
- Praxis ─── ~/.praxis (or PRAXIS_HOME)
200
- ```
201
-
202
- Use `praxis --data-plane claude` when you need the legacy shared Claude Code
203
- layout (`~/.claude` or `CLAUDE_CONFIG_DIR`). Praxis can resume Claude Code
204
- sessions in that mode, and Claude Code can resume compatible sessions written
205
- there. Every semver-like Claude Code producer version is
206
- structurally validated and is read/write compatible when its entry shape is
207
- supported; schema adapters are selected from transcript entry structure rather
208
- than an installed or fixed producer version, and malformed or unsupported
209
- shapes fail closed before any write. Each transcript record keeps its original
210
- producer version, so supported shapes may be mixed across versions in one
211
- session. Native fork creation is a separate restricted lossless copy path that
212
- preserves each existing source record's producer version, so it can copy
213
- specific black-box-verified foreign shapes such as the observed Claude Code
214
- 2.1.233 records; unsupported record shapes and unverified versions still fail
215
- closed and remain read-only. Maintainers can prove mixed-version Claude JSONL interoperability with
216
- `npm run test:cross-version-session-compat`, `test:cross-version-fork-compat`,
217
- `test:cross-version-sidechain-compat`, `test:cross-version-compaction-compat`,
218
- and `test:cross-version-resume-at-compat`, covering linear resume, native fork,
219
- foreground sidechain, compaction, and `--resume-session-at` branch projection.
220
- Each requires `PRAXIS_CLAUDE_BINARY` (Claude Code 2.1.208) and
221
- `PRAXIS_CLAUDE_CROSS_VERSION_BINARY` (a different Claude Code version).
222
-
223
- See the
224
- [compatibility contract](https://github.com/Forest-Isle/Praxis/blob/main/docs/COMPATIBILITY.md)
225
- for exact shared data, version boundaries, exclusions, and verification gates.
226
-
227
207
  ## Documentation
228
208
 
229
209
  | Need | Document |
@@ -268,15 +248,12 @@ npm ci
268
248
  npm run check
269
249
  ```
270
250
 
271
- `npm run build:native` compiles the implemented Praxis-owned core, provider,
272
- native transcript, and session profile without the Claude compatibility adapter.
251
+ `npm run build:native` compiles the Praxis-owned core, provider, native
252
+ transcript, and session profile.
273
253
  `npm run test:native:deletion` adds an emitted-output deletion gate. These are
274
254
  implemented profile checks, not qualification of the full native package.
275
- Native transcript migration remains explicit and recoverable. The built-CLI
276
- smoke gate `npm run verify:native-migration-cli` exercises all-session dry-run,
277
- publication, text/JSON idempotence, rollback, and Claude-data-plane exclusion.
278
- The isolated active-stream regression proof is available through
279
- `npm run test:performance:active-stream`; the product p95 budget remains 50 ms.
255
+ `npm run test:performance` runs the native projection scaling and regression
256
+ budgets; the retained-render p95 budget remains 50 ms.
280
257
  `npm run check` also enforces the corresponding source dependency direction.
281
258
  `npm run test:core-completion` runs the 56-story #402 audit and reports
282
259
  implemented, qualified, blocked, deferred, and out-of-scope states separately;
@@ -1,8 +1,11 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { isTerminalLifecycleState, } from '../core/agent-orchestration.js';
3
- import { isClaudeAgentId } from '../compatibility/claude/sidechain.js';
4
3
  const MAX_TIMEOUT_MS = 600_000;
5
4
  const DEFAULT_CLOSE_DRAIN_MS = 5_000;
5
+ const NATIVE_AGENT_ID_PATTERN = /^a[0-9a-f]{16}$/u;
6
+ function isNativeAgentId(agentId) {
7
+ return NATIVE_AGENT_ID_PATTERN.test(agentId);
8
+ }
6
9
  export class BackgroundAgentRunError extends Error {
7
10
  result;
8
11
  constructor(message, result, cause) {
@@ -37,7 +40,7 @@ function notificationWireStatus(status) {
37
40
  return null;
38
41
  }
39
42
  function assertAgentId(agentId) {
40
- if (!isClaudeAgentId(agentId)) {
43
+ if (!isNativeAgentId(agentId)) {
41
44
  throw new Error(`Invalid background agent ID: ${agentId}`);
42
45
  }
43
46
  }
@@ -625,7 +628,7 @@ export class BackgroundAgentManager {
625
628
  return { task, created: true };
626
629
  }
627
630
  resolveOptional(identifier) {
628
- if (isClaudeAgentId(identifier))
631
+ if (isNativeAgentId(identifier))
629
632
  return identifier;
630
633
  return this.names.get(identifier);
631
634
  }
@@ -669,7 +672,7 @@ export class BackgroundAgentManager {
669
672
  }
670
673
  resolveRequired(identifier) {
671
674
  const resolved = this.resolveOptional(identifier);
672
- if (!isClaudeAgentId(identifier) &&
675
+ if (!isNativeAgentId(identifier) &&
673
676
  !/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/u.test(identifier)) {
674
677
  assertAgentId(identifier);
675
678
  }
@@ -32,8 +32,8 @@ export interface BackgroundBashToolResult {
32
32
  task?: Record<string, unknown>;
33
33
  };
34
34
  }
35
- export declare function claudeBackgroundTaskParent(cwd: string): string;
36
- export declare function claudeBackgroundTaskRoot(cwd: string, sessionId: string): string;
35
+ export declare function nativeBackgroundTaskParent(cwd: string): string;
36
+ export declare function nativeBackgroundTaskRoot(cwd: string, sessionId: string): string;
37
37
  export declare class BackgroundBashManager {
38
38
  private readonly options;
39
39
  private readonly tasks;
@@ -1,16 +1,16 @@
1
1
  import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
2
2
  import { resolve } from 'node:path';
3
- import { sanitizeClaudeProjectPath } from '../compatibility/claude/paths.js';
3
+ import { sanitizeProjectPath } from '../platform/project-path-key.js';
4
4
  import { writeFileAtomically } from '../platform/atomic-write.js';
5
5
  import { BoundedProcessRunner, } from '../platform/bounded-process-runner.js';
6
6
  import { commandShell, commandShellArguments, } from '../platform/command-shell.js';
7
7
  import { assertBackgroundBashTaskId, backgroundBashTaskIdFromStateFile, createBackgroundBashTaskId, } from './background-task-id.js';
8
- export function claudeBackgroundTaskParent(cwd) {
8
+ export function nativeBackgroundTaskParent(cwd) {
9
9
  const uid = process.getuid?.() ?? 'unknown';
10
- return resolve('/tmp', `claude-${uid}`, sanitizeClaudeProjectPath(resolve(cwd)));
10
+ return resolve('/tmp', `praxis-${uid}`, sanitizeProjectPath(resolve(cwd)));
11
11
  }
12
- export function claudeBackgroundTaskRoot(cwd, sessionId) {
13
- return resolve(claudeBackgroundTaskParent(cwd), sessionId, 'tasks');
12
+ export function nativeBackgroundTaskRoot(cwd, sessionId) {
13
+ return resolve(nativeBackgroundTaskParent(cwd), sessionId, 'tasks');
14
14
  }
15
15
  function parsePersistedTask(source, taskId, outputFile) {
16
16
  let value;
@@ -65,7 +65,7 @@ export class BackgroundBashManager {
65
65
  cwd: options.cwd,
66
66
  maxOutputBytes: options.maxOutputBytes ?? 128 * 1024,
67
67
  });
68
- this.outputRoot = claudeBackgroundTaskRoot(options.cwd, options.sessionId);
68
+ this.outputRoot = nativeBackgroundTaskRoot(options.cwd, options.sessionId);
69
69
  this.sessionStateRoot = resolve(options.stateRoot, options.sessionId);
70
70
  }
71
71
  has(taskId) {
@@ -1,9 +1,10 @@
1
1
  import { lstat, readFile, readdir, realpath, rm } from 'node:fs/promises';
2
2
  import { homedir } from 'node:os';
3
3
  import { basename, dirname, isAbsolute, join, relative, resolve, sep, } from 'node:path';
4
- import { isClaudeSessionId, sanitizeClaudeProjectPath, } from '../compatibility/claude/paths.js';
5
- import { resolveClaudeProjectIdentity } from '../compatibility/claude/shared-resources.js';
4
+ import { isSessionId } from '../core/session.js';
6
5
  import { writeFileAtomically } from '../platform/atomic-write.js';
6
+ import { resolveProjectIdentity } from '../platform/project-identity.js';
7
+ import { sanitizeProjectPath } from '../platform/project-path-key.js';
7
8
  const MAX_ATOMIC_UPDATE_RETRIES = 8;
8
9
  function errorCode(error) {
9
10
  return error.code;
@@ -182,17 +183,17 @@ async function discoverSessionIds(projectRoot) {
182
183
  for (const entry of entries) {
183
184
  if (entry.isFile() && entry.name.endsWith('.jsonl')) {
184
185
  const sessionId = entry.name.slice(0, -'.jsonl'.length);
185
- if (isClaudeSessionId(sessionId))
186
+ if (isSessionId(sessionId))
186
187
  sessionIds.add(sessionId);
187
188
  }
188
- if (entry.isDirectory() && isClaudeSessionId(entry.name)) {
189
+ if (entry.isDirectory() && isSessionId(entry.name)) {
189
190
  sessionIds.add(entry.name);
190
191
  }
191
192
  }
192
193
  return [...sessionIds].sort();
193
194
  }
194
- async function discoverProjectRoots(configRoot, targetPaths, dataPlane) {
195
- const projectsRoot = join(configRoot, dataPlane === 'native' ? 'sessions' : 'projects');
195
+ async function discoverProjectRoots(configRoot, targetPaths) {
196
+ const projectsRoot = join(configRoot, 'sessions');
196
197
  if (!(await pathExists(projectsRoot)))
197
198
  return [];
198
199
  await assertSafePurgePath(configRoot, projectsRoot);
@@ -206,8 +207,8 @@ async function discoverProjectRoots(configRoot, targetPaths, dataPlane) {
206
207
  }
207
208
  throw error;
208
209
  }
209
- const targetKeys = targetPaths.map(sanitizeClaudeProjectPath);
210
- const worktreeSegment = dataPlane === 'native' ? '--praxis-worktrees-' : '--claude-worktrees-';
210
+ const targetKeys = targetPaths.map(sanitizeProjectPath);
211
+ const worktreeSegment = '--praxis-worktrees-';
211
212
  return entries
212
213
  .filter((entry) => targetKeys.some((targetKey) => entry.name === targetKey ||
213
214
  entry.name.startsWith(`${targetKey}${worktreeSegment}`)))
@@ -231,32 +232,18 @@ async function addExistingPath(items, configRoot, item) {
231
232
  items.push(item);
232
233
  }
233
234
  export async function planClaudeProjectPurge(options) {
235
+ void options.dataPlane;
234
236
  if (options.all && options.path !== undefined) {
235
237
  throw new Error('Cannot specify both a path and --all');
236
238
  }
237
239
  const configuredRoot = options.configRoot ??
238
- (process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude'));
240
+ (process.env.PRAXIS_HOME || join(homedir(), '.praxis'));
239
241
  const configRoot = await canonicalPath(configuredRoot);
240
- const dataPlane = options.dataPlane ?? 'claude';
241
- const homeDirectory = options.homeDirectory ?? homedir();
242
- const legacyClaudeStatePath = resolve(homeDirectory, '.claude.json');
243
- const legacyClaudeConfigRoot = await canonicalPath(resolve(homeDirectory, '.claude'));
244
- const defaultStatePath = dataPlane === 'native'
245
- ? join(configRoot, 'state.json')
246
- : options.configRoot !== undefined ||
247
- Boolean(process.env.CLAUDE_CONFIG_DIR)
248
- ? join(configRoot, '.claude.json')
249
- : legacyClaudeStatePath;
242
+ const defaultStatePath = join(configRoot, 'state.json');
250
243
  const statePath = resolve(options.statePath ?? defaultStatePath);
251
- const usesLegacyDefaultClaudeState = dataPlane === 'claude' &&
252
- !process.env.CLAUDE_CONFIG_DIR &&
253
- configRoot === legacyClaudeConfigRoot &&
254
- statePath === legacyClaudeStatePath;
255
244
  const canonicalStateParent = await canonicalPath(dirname(statePath));
256
- if (!usesLegacyDefaultClaudeState) {
257
- assertLexicallyContained(configRoot, join(canonicalStateParent, basename(statePath)));
258
- }
259
- await assertMutableRegularFile(statePath, usesLegacyDefaultClaudeState ? canonicalStateParent : configRoot);
245
+ assertLexicallyContained(configRoot, join(canonicalStateParent, basename(statePath)));
246
+ await assertMutableRegularFile(statePath, configRoot);
260
247
  const state = parseState(await readOptionalFile(statePath), statePath);
261
248
  const projects = stateProjects(state);
262
249
  const items = [];
@@ -264,30 +251,22 @@ export async function planClaudeProjectPurge(options) {
264
251
  const roots = [
265
252
  {
266
253
  kind: 'project',
267
- name: dataPlane === 'native' ? 'sessions' : 'projects',
268
- description: dataPlane === 'native'
269
- ? 'all project transcripts'
270
- : 'all project transcripts and memory',
254
+ name: 'sessions',
255
+ description: 'all project transcripts',
256
+ },
257
+ {
258
+ kind: 'memory',
259
+ name: 'memory',
260
+ description: 'all project memory',
261
+ },
262
+ {
263
+ kind: 'scheduled',
264
+ name: 'scheduled',
265
+ description: 'all project scheduled prompts',
271
266
  },
272
- ...(dataPlane === 'native'
273
- ? [
274
- {
275
- kind: 'memory',
276
- name: 'memory',
277
- description: 'all project memory',
278
- },
279
- {
280
- kind: 'scheduled',
281
- name: 'scheduled',
282
- description: 'all project scheduled prompts',
283
- },
284
- ]
285
- : []),
286
267
  {
287
268
  kind: 'memory',
288
- name: dataPlane === 'native'
289
- ? join('state', 'project-memory')
290
- : join('praxis', 'project-memory'),
269
+ name: join('state', 'project-memory'),
291
270
  description: 'all private Project-memory operational state',
292
271
  },
293
272
  {
@@ -330,9 +309,6 @@ export async function planClaudeProjectPurge(options) {
330
309
  mode: 'all',
331
310
  configRoot,
332
311
  statePath,
333
- ...(usesLegacyDefaultClaudeState
334
- ? { allowStateOutsideConfigRoot: true }
335
- : {}),
336
312
  projectRootPaths: [],
337
313
  sessionIds: [],
338
314
  items,
@@ -341,10 +317,8 @@ export async function planClaudeProjectPurge(options) {
341
317
  const requestedPath = resolve(options.path ?? options.cwd);
342
318
  const targetPath = await canonicalPath(requestedPath);
343
319
  const targetPaths = [...new Set([requestedPath, targetPath])];
344
- const projectIdentity = await resolveClaudeProjectIdentity({
345
- cwd: targetPath,
346
- });
347
- const projectRootPaths = await discoverProjectRoots(configRoot, targetPaths, dataPlane);
320
+ const projectIdentity = await resolveProjectIdentity(targetPath);
321
+ const projectRootPaths = await discoverProjectRoots(configRoot, targetPaths);
348
322
  const sessionIdSet = new Set();
349
323
  for (const projectRoot of projectRootPaths) {
350
324
  await assertSafePurgePath(configRoot, projectRoot);
@@ -359,26 +333,17 @@ export async function planClaudeProjectPurge(options) {
359
333
  await addExistingPath(items, configRoot, removePathItem('file-history', join(configRoot, 'file-history', sessionId), `file edit history for session ${sessionId}`));
360
334
  }
361
335
  const projectKeys = new Set([
362
- ...targetPaths.map(sanitizeClaudeProjectPath),
336
+ ...targetPaths.map(sanitizeProjectPath),
363
337
  ...projectRootPaths.map((path) => basename(path)),
364
- sanitizeClaudeProjectPath(projectIdentity),
338
+ sanitizeProjectPath(projectIdentity),
365
339
  ]);
366
- if (dataPlane === 'native') {
367
- for (const projectKey of projectKeys) {
368
- await addExistingPath(items, configRoot, removePathItem('memory', join(configRoot, 'memory', projectKey), `memory for project ${projectKey}`));
369
- await addExistingPath(items, configRoot, removePathItem('memory', join(configRoot, 'state', 'project-memory', projectKey), `private Project-memory state for project ${projectKey}`));
370
- await addExistingPath(items, configRoot, removePathItem('scheduled', join(configRoot, 'scheduled', `${projectKey}.json`), `scheduled prompts for project ${projectKey}`));
371
- }
372
- }
373
- else {
374
- for (const projectKey of projectKeys) {
375
- await addExistingPath(items, configRoot, removePathItem('memory', join(configRoot, 'praxis', 'project-memory', projectKey), `private Project-memory state for project ${projectKey}`));
376
- }
340
+ for (const projectKey of projectKeys) {
341
+ await addExistingPath(items, configRoot, removePathItem('memory', join(configRoot, 'memory', projectKey), `memory for project ${projectKey}`));
342
+ await addExistingPath(items, configRoot, removePathItem('memory', join(configRoot, 'state', 'project-memory', projectKey), `private Project-memory state for project ${projectKey}`));
343
+ await addExistingPath(items, configRoot, removePathItem('scheduled', join(configRoot, 'scheduled', `${projectKey}.json`), `scheduled prompts for project ${projectKey}`));
377
344
  }
378
345
  for (const projectRoot of projectRootPaths) {
379
- await addExistingPath(items, configRoot, removePathItem('project', projectRoot, dataPlane === 'native'
380
- ? 'project transcripts'
381
- : 'project transcripts and memory'));
346
+ await addExistingPath(items, configRoot, removePathItem('project', projectRoot, 'project transcripts'));
382
347
  }
383
348
  const historyPath = join(configRoot, 'history.jsonl');
384
349
  const historyExists = await pathExists(historyPath);
@@ -420,9 +385,6 @@ export async function planClaudeProjectPurge(options) {
420
385
  mode: 'project',
421
386
  configRoot,
422
387
  statePath,
423
- ...(usesLegacyDefaultClaudeState
424
- ? { allowStateOutsideConfigRoot: true }
425
- : {}),
426
388
  targetPath,
427
389
  projectIdentity,
428
390
  projectRootPaths,
@@ -1,6 +1,10 @@
1
1
  import type { ModelMessage, ModelToolCall, ToolExecutionResult } from '../core/runtime.js';
2
- import type { TranscriptMessagesEvent } from '../core/transcript-event.js';
3
- import type { NativeTranscriptStore } from '../persistence/native-transcript-store.js';
2
+ import type { TranscriptEvent, TranscriptMessagesEvent } from '../core/transcript-event.js';
3
+ import type { NativeTranscriptLease, NativeTranscriptLeaseResult } from '../persistence/native-transcript-store.js';
4
+ /** Minimal transcript-store contract required by the native session facade. */
5
+ export interface NativeSessionTranscriptStore {
6
+ withLease<T>(operation: (lease: NativeTranscriptLease) => Promise<T>): Promise<NativeTranscriptLeaseResult<T>>;
7
+ }
4
8
  export type NativeTranscriptActivation = {
5
9
  readonly kind: 'start';
6
10
  } | {
@@ -19,6 +23,11 @@ export interface NativeCompactionAppend {
19
23
  readonly postTokens: number;
20
24
  readonly durationMs: number;
21
25
  readonly preservedMessages?: readonly ModelMessage[];
26
+ /** Optional parent in the active branch when compacting a selected range. */
27
+ readonly logicalParentId?: string;
28
+ readonly direction?: 'from' | 'up_to';
29
+ readonly messagesSummarized?: number;
30
+ readonly preservePrefix?: boolean;
22
31
  }
23
32
  export type NativeInterruption = {
24
33
  readonly kind: 'complete' | 'none';
@@ -35,6 +44,8 @@ export type NativeInterruption = {
35
44
  readonly callIds: readonly string[];
36
45
  };
37
46
  export interface NativeSessionTranscriptLease {
47
+ /** Events on the currently selected active branch, in parent order. */
48
+ activeEvents(): TranscriptEvent[];
38
49
  activeMessages(): ModelMessage[];
39
50
  interruption(): NativeInterruption;
40
51
  beginToolExecution(callId: string): Promise<void>;
@@ -56,7 +67,7 @@ export interface NativeForkOptions {
56
67
  }
57
68
  export interface NativeSessionTranscriptOptions {
58
69
  readonly sessionId: string;
59
- readonly store: NativeTranscriptStore;
70
+ readonly store: NativeSessionTranscriptStore;
60
71
  readonly createId?: () => string;
61
72
  readonly now?: () => string;
62
73
  }
@@ -83,6 +83,14 @@ export class NativeSessionTranscript {
83
83
  const initial = prepared;
84
84
  const currentId = selectedId ?? initial.records.at(-1)?.event.id ?? null;
85
85
  const records = [...initial.records];
86
+ const usedIds = new Set(records.map((record) => record.event.id));
87
+ const createUniqueId = () => {
88
+ let id = this.createId();
89
+ while (usedIds.has(id))
90
+ id = this.createId();
91
+ usedIds.add(id);
92
+ return id;
93
+ };
86
94
  let tail = currentId === null
87
95
  ? initial.tail
88
96
  : { ...initial.tail, branchParentId: currentId };
@@ -94,6 +102,7 @@ export class NativeSessionTranscript {
94
102
  return result;
95
103
  };
96
104
  const lease = {
105
+ activeEvents: () => activeEvents(records.map((record) => record.event), activeId ?? undefined).slice(),
97
106
  activeMessages: () => projectActiveMessages(records.map((record) => record.event), activeId ?? undefined).slice(),
98
107
  interruption: () => {
99
108
  const events = activeEvents(records.map((record) => record.event), activeId ?? undefined);
@@ -166,7 +175,7 @@ export class NativeSessionTranscript {
166
175
  throw new Error(`Native tool call already claimed: ${callId}`);
167
176
  const event = {
168
177
  kind: 'tool-execution-started',
169
- id: this.createId(),
178
+ id: createUniqueId(),
170
179
  parentId: activeId,
171
180
  sessionId: this.sessionId,
172
181
  timestamp: this.now(),
@@ -196,7 +205,7 @@ export class NativeSessionTranscript {
196
205
  throw new Error(`Native tool call is already resolved: ${callId}`);
197
206
  const event = {
198
207
  kind: 'messages',
199
- id: this.createId(),
208
+ id: createUniqueId(),
200
209
  parentId: activeId,
201
210
  sessionId: this.sessionId,
202
211
  timestamp: this.now(),
@@ -232,7 +241,7 @@ export class NativeSessionTranscript {
232
241
  throw new Error('native transcript cannot append empty messages');
233
242
  const event = {
234
243
  kind: 'messages',
235
- id: this.createId(),
244
+ id: createUniqueId(),
236
245
  parentId: activeId,
237
246
  sessionId: this.sessionId,
238
247
  timestamp: this.now(),
@@ -266,19 +275,28 @@ export class NativeSessionTranscript {
266
275
  const events = records.map((record) => record.event);
267
276
  if (unresolvedActiveToolCallIds(projectActiveMessages(events, activeId)).length > 0)
268
277
  throw new Error('Cannot compact a native transcript with unresolved tool calls');
269
- const boundaryId = this.createId();
270
- const summaryId = this.createId();
278
+ const boundaryId = createUniqueId();
279
+ const summaryId = createUniqueId();
271
280
  const boundary = {
272
281
  kind: 'context-boundary',
273
282
  id: boundaryId,
274
283
  parentId: null,
275
284
  sessionId: this.sessionId,
276
285
  timestamp: this.now(),
277
- logicalParentId: activeId,
286
+ logicalParentId: input.logicalParentId ?? activeId,
278
287
  trigger: input.trigger,
279
288
  preTokens: input.preTokens,
280
289
  postTokens: input.postTokens,
281
290
  durationMs: input.durationMs,
291
+ ...(input.direction === undefined
292
+ ? {}
293
+ : { direction: input.direction }),
294
+ ...(input.messagesSummarized === undefined
295
+ ? {}
296
+ : { messagesSummarized: input.messagesSummarized }),
297
+ ...(input.preservePrefix === undefined
298
+ ? {}
299
+ : { preservePrefix: input.preservePrefix }),
282
300
  };
283
301
  const summary = {
284
302
  kind: 'context-summary',
@@ -294,9 +312,12 @@ export class NativeSessionTranscript {
294
312
  if (message.role !== 'assistant')
295
313
  continue;
296
314
  for (const call of message.toolCalls ?? [])
297
- replayToolCallIds.set(call.id, this.createId());
315
+ replayToolCallIds.set(call.id, createUniqueId());
298
316
  }
299
- const replaySuffix = suffix.map((message) => {
317
+ const replaySuffix = suffix
318
+ .filter((message) => message.role !== 'tool' ||
319
+ replayToolCallIds.has(message.toolCallId))
320
+ .map((message) => {
300
321
  if (message.role === 'assistant')
301
322
  return {
302
323
  ...message,
@@ -320,7 +341,7 @@ export class NativeSessionTranscript {
320
341
  const suffixEvent = replaySuffix.length
321
342
  ? {
322
343
  kind: 'messages',
323
- id: this.createId(),
344
+ id: createUniqueId(),
324
345
  parentId: summaryId,
325
346
  sessionId: this.sessionId,
326
347
  timestamp: this.now(),
@@ -1,4 +1,4 @@
1
- import { type ClaudeScheduledTask } from '../persistence/claude-scheduled-task-store.js';
1
+ import { type NativeScheduledTask } from '../persistence/native-scheduled-task-store.js';
2
2
  export declare const MAX_JOBS = 50;
3
3
  export declare class ScheduledJobLimitError extends Error {
4
4
  readonly maxJobs: number;
@@ -24,7 +24,7 @@ export interface ScheduledPrompt {
24
24
  id: string;
25
25
  prompt: string;
26
26
  }
27
- export interface ListedScheduledPrompt extends ClaudeScheduledTask {
27
+ export interface ListedScheduledPrompt extends NativeScheduledTask {
28
28
  durable: boolean;
29
29
  }
30
30
  export interface DynamicWakeupResult {
@@ -2,7 +2,7 @@ import { execFile } from 'node:child_process';
2
2
  import { randomBytes } from 'node:crypto';
3
3
  import { promisify } from 'node:util';
4
4
  import { CronExpressionParser } from 'cron-parser';
5
- import { ClaudeScheduledTaskLimitError, ClaudeScheduledTaskStore, } from '../persistence/claude-scheduled-task-store.js';
5
+ import { NativeScheduledTaskLimitError, NativeScheduledTaskStore, } from '../persistence/native-scheduled-task-store.js';
6
6
  const execFileAsync = promisify(execFile);
7
7
  const RECURRING_LIFETIME_MS = 7 * 24 * 60 * 60 * 1_000;
8
8
  const MIN_DYNAMIC_WAKEUP_SECONDS = 60;
@@ -111,7 +111,7 @@ export class ScheduledPromptManager {
111
111
  initialization;
112
112
  closed = false;
113
113
  constructor(options) {
114
- this.store = new ClaudeScheduledTaskStore({
114
+ this.store = new NativeScheduledTaskStore({
115
115
  filePath: options.filePath,
116
116
  lockFile: options.lockFile,
117
117
  });
@@ -145,7 +145,7 @@ export class ScheduledPromptManager {
145
145
  task = await this.store.create(base, { maxJobs: MAX_JOBS });
146
146
  }
147
147
  catch (error) {
148
- if (error instanceof ClaudeScheduledTaskLimitError) {
148
+ if (error instanceof NativeScheduledTaskLimitError) {
149
149
  throw new ScheduledJobLimitError(MAX_JOBS);
150
150
  }
151
151
  throw error;