@xeno-corporation/xeno-agent-sdk 0.6.8 → 0.6.11

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 (57) hide show
  1. package/README.md +93 -15
  2. package/dist/agents/metafile-esm.json +1 -0
  3. package/dist/control-plane/index.d.ts +266 -1
  4. package/dist/control-plane/index.js +7 -4
  5. package/dist/control-plane/metafile-esm.json +1 -0
  6. package/dist/electron/index.d.ts +475 -150
  7. package/dist/electron/index.js +702 -167
  8. package/dist/electron/metafile-esm.json +1 -0
  9. package/dist/governance/index.d.ts +397 -0
  10. package/dist/governance/index.js +33 -0
  11. package/dist/governance/metafile-esm.json +1 -0
  12. package/dist/hooks/index.d.ts +50 -2
  13. package/dist/hooks/index.js +4 -4
  14. package/dist/hooks/metafile-esm.json +1 -0
  15. package/dist/index.d.ts +2789 -413
  16. package/dist/index.js +934 -319
  17. package/dist/mcp/index.d.ts +91 -2
  18. package/dist/mcp/metafile-esm.json +1 -0
  19. package/dist/metafile-esm.json +1 -0
  20. package/dist/providers/index.cjs +9 -3
  21. package/dist/providers/index.d.cts +158 -3
  22. package/dist/providers/index.d.ts +158 -3
  23. package/dist/providers/index.js +9 -3
  24. package/dist/providers/metafile-cjs.json +1 -0
  25. package/dist/providers/metafile-esm.json +1 -0
  26. package/dist/session/index.d.ts +110 -1
  27. package/dist/session/index.js +21 -17
  28. package/dist/session/metafile-esm.json +1 -0
  29. package/dist/soul/metafile-esm.json +1 -0
  30. package/dist/terminal/index.d.ts +55 -0
  31. package/dist/terminal/index.js +4 -0
  32. package/dist/terminal/metafile-esm.json +1 -0
  33. package/dist/ui/index.d.ts +203 -2953
  34. package/dist/ui/index.js +35 -32
  35. package/dist/ui/metafile-esm.json +1 -0
  36. package/dist/utils/index.d.ts +79 -1
  37. package/dist/utils/index.js +19 -18
  38. package/dist/utils/metafile-esm.json +1 -0
  39. package/ownership/CLEAN_ROOM_CONTRIBUTOR_GUIDE.md +63 -0
  40. package/ownership/audit.mjs +703 -0
  41. package/ownership/evidence/PROPRIETARY-COMMAND-1/implementation-record.md +77 -0
  42. package/ownership/evidence/PROPRIETARY-COMMAND-1/provenance-review.md +51 -0
  43. package/ownership/evidence/PROPRIETARY-MEDIA-1/implementation-record.md +131 -0
  44. package/ownership/evidence/PROPRIETARY-MEDIA-1/provenance-review.md +50 -0
  45. package/ownership/evidence/PROPRIETARY-PTY-1/implementation-record.md +86 -0
  46. package/ownership/evidence/PROPRIETARY-PTY-1/provenance-review.md +52 -0
  47. package/ownership/evidence/PROPRIETARY-RUNTIME-1/implementation-record.md +68 -0
  48. package/ownership/evidence/PROPRIETARY-RUNTIME-1/provenance-review.md +32 -0
  49. package/ownership/evidence/PROPRIETARY-TOOLCHAIN-1/implementation-record.md +53 -0
  50. package/ownership/evidence/PROPRIETARY-TOOLCHAIN-1/provenance-review.md +32 -0
  51. package/ownership/evidence/PROPRIETARY-UI-1/implementation-record.md +96 -0
  52. package/ownership/evidence/PROPRIETARY-UI-1/provenance-review.md +46 -0
  53. package/ownership/ownership-policy.json +214 -0
  54. package/ownership/ownership-policy.schema.json +186 -0
  55. package/ownership/templates/clean-room-implementation-record.md +70 -0
  56. package/ownership/templates/provenance-review.md +51 -0
  57. package/package.json +137 -114
package/README.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  Core runtime for XENO agent-enabled products. It provides the agent loop, tool execution, permissions, sessions, memory, audit logging, and provider integration used across the XENO platform.
4
4
 
5
+ ## Runtime Ownership
6
+
7
+ The ownership program targets P2 feature parity, but this checkout must be described by its generated audit rather than by the target. Run `npm run build && npm run compliance:ownership:artifacts` or inspect `docs/compliance/ownership-inventory.md`. Node remains an explicitly external host through P2, and development/build packages remain third-party build inputs. Do not claim that a release is fully proprietary, entirely Xeno-owned, or free of third-party runtime code unless the packaged release smoke prints the corresponding achieved level and the required provenance/counsel gates are complete.
8
+
9
+ The canonical policy and engineering clean-room controls are under `ownership/`.
10
+
5
11
  ## Vision
6
12
 
7
13
  Every XENO creative app (Pixel, Motion, Sound) has an AI agent embedded directly into the interface. Users open a sidebar, type what they want ("remove the background from this layer", "cut the silence from this podcast", "match-cut these two clips"), and the agent translates that into tool calls against the app's engine. One request can span multiple apps: "Create a product video from these photos with background music" triggers coordinated work across Pixel, Motion, and Sound simultaneously.
@@ -25,8 +31,8 @@ With agent SDK: User says "Create a product video" -> agents in all 3 apps c
25
31
  - **Session persistence** with checkpoints, transcript writing, and crash recovery
26
32
  - **Audit logging** in JSON-lines format (who, what, when, result)
27
33
  - **Delegation system**: planner/executor/reviewer sub-agent workflows
28
- - ESM-only, TypeScript 5.7.3, tsup build
29
- - Dependencies: chalk, glob, gray-matter
34
+ - ESM-only, TypeScript 7 native typechecking, TypeScript 6 compiler-API compatibility, tsup build
35
+ - Production dependencies: none
30
36
 
31
37
  ## Automation
32
38
 
@@ -65,7 +71,7 @@ src/
65
71
  |-----------|---------|-------------|
66
72
  | **Core Loop** | Agentic turn loop with tool dispatch, streaming, reducer | `AgentLoop`, `AgentLoopConfig` |
67
73
  | **Tools** | Registry pattern for tool registration + 6 built-in tools | `ToolRegistry`, `registry` |
68
- | **Security** | Permission engine with 4 modes, path sandboxing | `PermissionEngine`, `PermissionConfig` |
74
+ | **Security** | Permission engine, filesystem/shell policy, optional OS containment | `PermissionEngine`, `PolicyEnforcerConfig` |
69
75
  | **Session** | Persistence, checkpoints, transcripts, locks, recovery | `SessionManager`, `CheckpointManager` |
70
76
  | **Identity** | Persona loading and resolution across 4 levels | `IdentityLoader`, `IdentityResolver` |
71
77
  | **Memory** | Hierarchical memory with budget management | `MemoryManager`, `MemoryBudget` |
@@ -173,12 +179,71 @@ Messages are JSON-serializable for cross-process IPC. Timeout and retry are buil
173
179
 
174
180
  ### Safety Features
175
181
 
176
- - **Path sandboxing**: agents cannot access files outside configured boundaries
182
+ - **Policy enforcement**: canonical physical-path checks reject traversal through symlinks/junctions, UNC/device, extended-length, drive-relative, root-relative, alternate-data-stream, reserved-device, and ambiguous paths
183
+ - **Shell policy**: parsed PowerShell/Bash file operands, redirections, and path parameters enforce read/write capabilities; unresolved dynamic filesystem operands fail closed
184
+ - **Execution security levels**: `policy-only`, `process-hardened`, and `contained` are separate API contracts. The legacy `requireOsContainment: true` flag now means strict `contained` and never accepts process hardening as a substitute.
185
+ - **Windows process hardening**: `executionLevel: "process-hardened"` uses a reduced primary token, low integrity for read-only execution, an explicit inherited-handle allowlist, a minimized environment, and a kill-on-close Job Object. It does not isolate filesystem reads or network access and is for trusted workspaces only.
186
+ - **Fail-closed containment**: no platform currently advertises release-certified `contained` execution. A contained or untrusted request fails with `CONTAINMENT_UNAVAILABLE`; executable discovery is not treated as certification.
177
187
  - **Destructive action gates**: delete, overwrite, and shell commands require explicit approval
178
188
  - **Audit trail**: every tool call logged in JSON-lines format with trace IDs
179
189
  - **Risk classification**: each tool call classified as low/medium/high risk
180
190
  - **Permission rules**: per-tool, per-path override rules
181
191
 
192
+ The policy layer and Windows process hardening are defense in depth, not a claim that arbitrary untrusted code is contained. See [Security Boundaries](docs/SECURITY_BOUNDARIES.md) for the capability matrix and migration contract.
193
+
194
+ ### Managed Shell Processes
195
+
196
+ `UnifiedExecManager` is the canonical owner for pipe and PTY process lifecycles. `BackgroundProcessManager` remains the task-oriented compatibility facade and adds owner scoping, bounded file-backed output, same-process foreground-to-background promotion, completion events, input, resize, and human-writer leases.
197
+
198
+ - `Bash` accepts `tty: true` only with `run_in_background: true`.
199
+ - `TaskInput` writes to or waits on an interactive task. Input text is projected to byte-count/task metadata before audit, permission, callback, and transcript sinks.
200
+ - PTY support is adapter based. Hosts register an optional `XenoPtyAdapter`; missing capability returns `PTY_UNAVAILABLE` and never silently falls back to a pipe.
201
+ - App-server `exec.start` accepts `tty`, `cols`, and `rows`; `exec.resize` is owner scoped alongside write, output, list, and terminate.
202
+ - `SessionManager.recordDirectShellResult` persists bounded local-command output as user-role untrusted context with semantic metadata, not as a fabricated model tool call.
203
+
204
+ ### Managed Tool Operations
205
+
206
+ Tool calls now receive a stable operation ID, deadline, completion policy (`await`, `observe`, or `detach`), progress counters, terminal reason, and artifact evidence. Recovery snapshots are written atomically under the user control-plane directory, while redacted operation transitions are appended to a private JSONL ledger. Raw command text is not copied into operation lists or the ledger.
207
+
208
+ - A promotable foreground `Bash` command moves to the background by changing presentation on the same process. PID, task/process IDs, output cursor, and deadline are retained; the command is never re-executed.
209
+ - `TaskOutput({ task_id, offset })` remains a nonblocking compatibility read. `wait: true` adds an event-driven wait capped at 30 seconds and always reports explicit `Operation terminal: yes|no`, state, runtime, idle time, output delta, UTF-8-safe next offset, and suggested action.
210
+ - Finite model-created background work defaults to `await`. The parent turn suspends without another provider request and resumes from a durable continuation notification after terminal output arrives. User stop, detach, clear, interrupt, or rewind cancels stale automatic continuation.
211
+ - `expected_outputs` on `Bash` records generic file postconditions. Missing or unstable outputs fail the logical operation even after exit code 0; partial artifacts are still reported after timeout/failure.
212
+ - `ReadImage` preflights format, dimensions, and bytes. The dependency-free `xeno-owned-raster` path performs owned PNG/DEFLATE decode and encode, JPEG, GIF, VP8/VP8L WebP decode, bounded SVG software rasterization, and resizing; BMP and Netpbm inputs normalize through the same owned PNG path. Oversized supported inputs become owner-private bounded PNG previews without an ambient decoder or canvas fallback. Private previews are removed during session cleanup and originals stay unchanged. The exact approved profiles and explicit rejections are documented in [`docs/owned-media-matrix.md`](docs/owned-media-matrix.md).
213
+
214
+ Temporary rollback controls default to enabled: `XENO_TOOL_OPERATION_RUNTIME`, `XENO_AUTO_BACKGROUND_FOREGROUND_BASH`, `XENO_TASK_OUTPUT_WAIT`, `XENO_AUTO_TOOL_CONTINUATION`, and `XENO_READ_IMAGE_AUTO_PREVIEW`. Setting a control to `0` disables its optional behavior without deleting active tasks or persisted operation records.
215
+
216
+ On Windows, ordinary managed pipe tasks use the existing kill-on-close Job Object launcher when available and report `windows-job-object`; the explicit `windows-tree-fallback` uses tree termination but is not reported as verified Job ownership. Persistent PowerShell defaults to Windows PowerShell 5.1. Set `XENO_POWERSHELL_EXECUTABLE=pwsh.exe` to use PowerShell 7. Process ownership and cleanup are not claims of untrusted-code containment.
217
+
218
+ See [docs/managed-tool-operations.md](docs/managed-tool-operations.md) for the runtime and troubleshooting contract.
219
+
220
+ ### Agent Harness Reliability
221
+
222
+ - Tool inputs are JSON-schema validated by default; malformed calls are returned to the model as typed errors.
223
+ - Interrupted or orphaned tool exchanges are repaired before resumable history is sent back to a provider.
224
+ - Repeated identical failing tool calls are circuit-broken before they can loop indefinitely.
225
+ - Query lifecycle transitions are emitted as typed runtime events and guarded by idle, active-operation lease, and hard timeouts.
226
+ - Context overflow triggers one semantic compact-and-retry path. Old tool payloads are compacted before conversation history is dropped, message count has a hard cap, and preflight token estimates are model-aware with an injectable exact-tokenizer seam.
227
+ - The default tool registry demand-loads optional schemas through `ToolSearch` instead of advertising every full schema on every request.
228
+
229
+ ### Direct Provider Adapters
230
+
231
+ Provider-normalized deployments can continue using the default Xeno API transport. Direct integrations are available from the provider subpath:
232
+
233
+ ```typescript
234
+ import { createDirectProvider } from '@xeno-corporation/xeno-agent-sdk/providers'
235
+ import { createXenoAgent } from '@xeno-corporation/xeno-agent-sdk'
236
+
237
+ const provider = createDirectProvider({
238
+ kind: 'openai-responses', // 'anthropic' | 'openai-responses' | 'google' | 'openai-compatible'
239
+ apiKey: process.env.PROVIDER_API_KEY!,
240
+ })
241
+
242
+ const { agent } = await createXenoAgent({ provider })
243
+ ```
244
+
245
+ The adapters translate streaming text, images, parallel tool calls/results, usage, cancellation, and typed failures for Messages, Responses, Gemini, and explicitly profiled chat-compatible endpoints. Generic endpoints require a capability profile and local HTTP requires an explicit local-network endpoint profile. See [docs/PROVIDER_CONFORMANCE.md](docs/PROVIDER_CONFORMANCE.md).
246
+
182
247
  ## Session Persistence
183
248
 
184
249
  - Sessions have unique IDs with embedded timestamps
@@ -209,7 +274,7 @@ Memory is injected into the system prompt with configurable token budgets to avo
209
274
  | **xeno-rt** (local) | OpenAI-compatible API on localhost | Offline, privacy, no cost |
210
275
  | **Ollama** (local) | OpenAI-compatible API | Alternative local provider |
211
276
 
212
- The SDK is provider-agnostic. Any OpenAI-compatible endpoint works. Provider selection and fallback logic are handled by the config subsystem.
277
+ The SDK is provider-agnostic. OpenAI-compatible endpoints are supported through an explicit capability and endpoint-security profile; compatibility is verified by conformance fixtures rather than inferred from the endpoint label. Provider selection and fallback logic are handled by the config subsystem.
213
278
 
214
279
  ## Consumers
215
280
 
@@ -221,18 +286,30 @@ The SDK is provider-agnostic. Any OpenAI-compatible endpoint works. Provider sel
221
286
  | **xeno-sound** | Audio editing agent sidebar | "Master this podcast to -16 LUFS" |
222
287
  | **xeno-hub** | Orchestrator routing tasks between apps | "Create marketing materials" dispatches to Pixel + Motion + Sound |
223
288
 
224
- ## Planned: React Component Library
289
+ ## Xeno-Owned UI
225
290
 
226
- A separate `/react` or `/ui` export will provide pre-built React components for embedding the agent interface in Electron-based XENO apps:
291
+ The `/ui` subpath provides a framework-neutral controller, semantic accessibility view, and optional owned DOM renderer. It has no React runtime or peer dependency and remains isolated from the Node-only core entry point.
227
292
 
228
- - **ChatPanel** -- full agent sidebar with message history
229
- - **MessageBubble** -- individual messages (user, agent, system)
230
- - **ToolCallIndicator** -- real-time display of tool execution with status
231
- - **ThinkingState** -- streaming thinking/reasoning indicator
232
- - **PermissionPrompt** -- inline approval UI for destructive actions
233
- - **SessionSwitcher** -- switch between conversation sessions
293
+ ```ts
294
+ import {
295
+ createAgentUiController,
296
+ mountAgentUi,
297
+ } from "@xeno-corporation/xeno-agent-sdk/ui";
298
+
299
+ const controller = createAgentUiController({
300
+ agent: myAgentLoop,
301
+ enablePermissionRequests: true,
302
+ });
303
+ const view = mountAgentUi(document.querySelector("#agent")!, controller, {
304
+ agentName: "Xeno Agent",
305
+ });
306
+
307
+ // App shutdown:
308
+ view.dispose();
309
+ controller.dispose();
310
+ ```
234
311
 
235
- These components will NOT live in the core SDK (no DOM dependencies in core). They will be a separate package entry point that apps can import independently.
312
+ Framework integrations can subscribe to immutable controller snapshots and call `createAgentUiView` instead of mounting the DOM renderer. Renderer contract version `1` includes message streaming, tool state, token usage, cancellation, retry, permission decisions, safe markdown segments, keyboard submission, and accessible roles/live regions. See [the UI v1 migration guide](docs/migrations/sdk-ui-v1.md).
236
313
 
237
314
  ## Ecosystem Position
238
315
 
@@ -254,7 +331,8 @@ See [Full Ecosystem Report](../XENO%20CORPORATION%20-%20Full%20Ecosystem%20Repor
254
331
  ```bash
255
332
  npm install
256
333
  npm run build # tsup -> ESM output
257
- npm run typecheck # TypeScript 5.7.3 strict mode
334
+ npm run typecheck # TypeScript 7 native strict-mode check
335
+ npm run typecheck:compat # TypeScript 6 compatibility check for API-based tooling
258
336
  ```
259
337
 
260
338
  ## License
@@ -0,0 +1 @@
1
+ {"inputs":{"src/utils/yaml.ts":{"bytes":5506,"imports":[],"format":"esm"},"src/agents/definitions.ts":{"bytes":20613,"imports":[{"path":"fs","kind":"import-statement","external":true},{"path":"os","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"src/utils/yaml.ts","kind":"import-statement","original":"../utils/yaml.js"}],"format":"esm"},"src/agents/index.ts":{"bytes":34,"imports":[{"path":"src/agents/definitions.ts","kind":"import-statement","original":"./definitions.js"}],"format":"esm"}},"outputs":{"dist/agents/index.js":{"imports":[{"path":"fs","kind":"import-statement","external":true},{"path":"os","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true}],"exports":["AgentDefinitionLoader","AgentDefinitionResolver","createAgentDefinitionFile","createAgentDefinitionPromptSection","getProjectAgentDefinitionDirs","getProjectAgentDefinitionsDir","getUserAgentDefinitionsDir","isValidAgentDefinitionName","renderAgentDefinition","renderAgentDefinitions","resolveAgentDefinition","resolveAgentToolPolicy","scanAgentDefinitions","toAgentDefinitionMetadata","validateAgentDefinition"],"entryPoint":"src/agents/index.ts","inputs":{"src/agents/definitions.ts":{"bytesInOutput":8825},"src/utils/yaml.ts":{"bytesInOutput":2211},"src/agents/index.ts":{"bytesInOutput":0}},"bytes":11511}}}
@@ -61,6 +61,8 @@ interface AgentRunRecord {
61
61
  }
62
62
  interface AgentRunEvent {
63
63
  schemaVersion: 1;
64
+ eventId?: string;
65
+ sequence?: number;
64
66
  runId: string;
65
67
  type: string;
66
68
  timestamp: string;
@@ -90,6 +92,7 @@ declare function getAgentRunDir(runId: string): string;
90
92
  declare function createAgentRunId(date?: Date): string;
91
93
  declare class AgentRunStore {
92
94
  private readonly runsDir;
95
+ private readonly eventStore;
93
96
  constructor(runsDir?: string);
94
97
  create(input: AgentRunCreateInput): AgentRunRecord;
95
98
  get(runId: string): AgentRunRecord | null;
@@ -120,6 +123,268 @@ declare class AgentRunStore {
120
123
  private recordPath;
121
124
  private runDir;
122
125
  }
126
+ declare class AgentEventStore {
127
+ private readonly runsDir;
128
+ constructor(runsDir: string);
129
+ pathFor(runId: string): string;
130
+ append(runId: string, type: string, payload?: Record<string, unknown>, timestamp?: string): AgentRunEvent;
131
+ read(runId: string): AgentRunEvent[];
132
+ tail(runId: string, limit?: number): AgentRunEvent[];
133
+ }
134
+ interface AgentRunSpawnResult {
135
+ pid: number;
136
+ workerVersion?: string;
137
+ }
138
+ interface AgentRunControllerOptions {
139
+ store?: AgentRunStore;
140
+ spawn?: (record: AgentRunRecord) => AgentRunSpawnResult | Promise<AgentRunSpawnResult>;
141
+ stop?: (record: AgentRunRecord, reason: string) => void | Promise<void>;
142
+ pause?: (record: AgentRunRecord, reason: string) => void | Promise<void>;
143
+ resume?: (record: AgentRunRecord, reason: string) => void | Promise<void>;
144
+ }
145
+ declare class AgentRunController {
146
+ private readonly options;
147
+ readonly store: AgentRunStore;
148
+ constructor(options?: AgentRunControllerOptions);
149
+ create(input: AgentRunCreateInput): AgentRunRecord;
150
+ start(input: AgentRunCreateInput): Promise<AgentRunRecord>;
151
+ get(runId: string): AgentRunRecord | null;
152
+ list(options?: AgentRunListOptions): AgentRunRecord[];
153
+ events(runId: string, limit?: number): AgentRunEvent[];
154
+ stop(runId: string, reason?: string): Promise<AgentRunRecord | null>;
155
+ pause(runId: string, reason?: string): Promise<AgentRunRecord | null>;
156
+ resume(runId: string, reason?: string): Promise<AgentRunRecord | null>;
157
+ complete(runId: string, status: Extract<AgentRunStatus, "completed" | "failed" | "interrupted">, options?: {
158
+ reason?: string;
159
+ exitCode?: number | null;
160
+ usage?: AgentRunUsage;
161
+ }): AgentRunRecord | null;
162
+ }
163
+ declare const AGENT_DAEMON_PROTOCOL_VERSION: 1;
164
+ interface AgentDaemonRequest<T = unknown> {
165
+ protocolVersion: typeof AGENT_DAEMON_PROTOCOL_VERSION;
166
+ id: string;
167
+ method: string;
168
+ params: T;
169
+ authToken?: string;
170
+ }
171
+ interface AgentDaemonError {
172
+ code: string;
173
+ message: string;
174
+ }
175
+ type AgentDaemonResponse<T = unknown> = {
176
+ protocolVersion: 1;
177
+ id: string;
178
+ result: T;
179
+ } | {
180
+ protocolVersion: 1;
181
+ id: string;
182
+ error: AgentDaemonError;
183
+ };
184
+ type AgentDaemonHandler = (params: unknown, request: AgentDaemonRequest) => unknown | Promise<unknown>;
185
+ type AgentDaemonTransport = (request: AgentDaemonRequest) => AgentDaemonResponse | Promise<AgentDaemonResponse>;
186
+ declare class AgentDaemonServer {
187
+ private readonly options;
188
+ private readonly handlers;
189
+ constructor(options?: {
190
+ authToken?: string;
191
+ });
192
+ register(method: string, handler: AgentDaemonHandler): this;
193
+ registerController(controller: AgentRunController): this;
194
+ handle(request: AgentDaemonRequest): Promise<AgentDaemonResponse>;
195
+ private error;
196
+ }
197
+ declare class AgentDaemonClient {
198
+ private readonly transport;
199
+ private readonly options;
200
+ constructor(transport: AgentDaemonTransport, options?: {
201
+ authToken?: string;
202
+ });
203
+ request<TResult = unknown, TParams = unknown>(method: string, params: TParams): Promise<TResult>;
204
+ }
205
+ interface WorkflowNodeDefinition {
206
+ id: string;
207
+ dependsOn?: string[];
208
+ input?: unknown;
209
+ }
210
+ interface WorkflowDefinition {
211
+ schemaVersion: 1;
212
+ id: string;
213
+ name: string;
214
+ nodes: WorkflowNodeDefinition[];
215
+ maxConcurrency?: number;
216
+ failFast?: boolean;
217
+ }
218
+ type WorkflowNodeStatus = "pending" | "running" | "completed" | "failed" | "skipped";
219
+ type WorkflowRunStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
220
+ interface WorkflowRunNodeRecord {
221
+ status: WorkflowNodeStatus;
222
+ startedAt?: string;
223
+ completedAt?: string;
224
+ output?: unknown;
225
+ error?: string;
226
+ }
227
+ interface WorkflowRunRecord {
228
+ schemaVersion: 1;
229
+ runId: string;
230
+ workflowId: string;
231
+ status: WorkflowRunStatus;
232
+ createdAt: string;
233
+ updatedAt: string;
234
+ completedAt?: string;
235
+ nodes: Record<string, WorkflowRunNodeRecord>;
236
+ }
237
+ interface WorkflowEvent {
238
+ schemaVersion: 1;
239
+ eventId: string;
240
+ runId: string;
241
+ type: string;
242
+ timestamp: string;
243
+ nodeId?: string;
244
+ payload: Record<string, unknown>;
245
+ }
246
+ interface WorkflowPlan {
247
+ workflowId: string;
248
+ waves: string[][];
249
+ }
250
+ declare class WorkflowPlanner {
251
+ plan(definition: WorkflowDefinition): WorkflowPlan;
252
+ }
253
+ declare class WorkflowStore {
254
+ private readonly rootDir;
255
+ constructor(rootDir?: string);
256
+ saveDefinition(definition: WorkflowDefinition): WorkflowDefinition;
257
+ getDefinition(id: string): WorkflowDefinition | null;
258
+ listDefinitions(): WorkflowDefinition[];
259
+ createRun(definition: WorkflowDefinition): WorkflowRunRecord;
260
+ getRun(runId: string): WorkflowRunRecord | null;
261
+ updateRun(runId: string, updater: (record: WorkflowRunRecord) => WorkflowRunRecord): WorkflowRunRecord;
262
+ appendEvent(runId: string, type: string, payload: Record<string, unknown>, nodeId?: string): WorkflowEvent;
263
+ readEvents(runId: string): WorkflowEvent[];
264
+ private writeRun;
265
+ private definitionsDir;
266
+ private runsDir;
267
+ private runPath;
268
+ private eventsPath;
269
+ }
270
+ type WorkflowNodeExecutor = (node: WorkflowNodeDefinition, context: {
271
+ run: WorkflowRunRecord;
272
+ definition: WorkflowDefinition;
273
+ }) => unknown | Promise<unknown>;
274
+ declare class WorkflowRuntime {
275
+ private readonly store;
276
+ private readonly planner;
277
+ constructor(store?: WorkflowStore, planner?: WorkflowPlanner);
278
+ run(definition: WorkflowDefinition, executor: WorkflowNodeExecutor): Promise<WorkflowRunRecord>;
279
+ }
280
+ interface MonitorEvent {
281
+ schemaVersion: 1;
282
+ eventId: string;
283
+ sourceId: string;
284
+ type: string;
285
+ timestamp: string;
286
+ payload: Record<string, unknown>;
287
+ }
288
+ interface MonitorSource {
289
+ id: string;
290
+ start?(): void | Promise<void>;
291
+ poll(): Array<Omit<MonitorEvent, "schemaVersion" | "eventId" | "sourceId" | "timestamp"> & {
292
+ timestamp?: string;
293
+ }> | Promise<Array<Omit<MonitorEvent, "schemaVersion" | "eventId" | "sourceId" | "timestamp"> & {
294
+ timestamp?: string;
295
+ }>>;
296
+ stop?(): void | Promise<void>;
297
+ }
298
+ interface MonitorSnapshot {
299
+ sourceId: string;
300
+ running: boolean;
301
+ events: MonitorEvent[];
302
+ }
303
+ declare class MonitorManager {
304
+ private readonly maxEvents;
305
+ private readonly sources;
306
+ private readonly events;
307
+ constructor(maxEvents?: number);
308
+ register(source: MonitorSource): this;
309
+ start(sourceId: string, intervalMs?: number): Promise<MonitorSnapshot>;
310
+ poll(sourceId: string): Promise<MonitorEvent[]>;
311
+ stop(sourceId: string): Promise<MonitorSnapshot>;
312
+ stopAll(): Promise<void>;
313
+ snapshot(sourceId: string, limit?: number): MonitorSnapshot;
314
+ list(): Array<{
315
+ sourceId: string;
316
+ running: boolean;
317
+ }>;
318
+ private require;
319
+ }
320
+ declare class MonitorTool {
321
+ private readonly manager;
322
+ constructor(manager: MonitorManager);
323
+ execute(input: {
324
+ sourceId: string;
325
+ intervalMs?: number;
326
+ }): Promise<MonitorSnapshot>;
327
+ }
328
+ declare class MonitorStopTool {
329
+ private readonly manager;
330
+ constructor(manager: MonitorManager);
331
+ execute(input: {
332
+ sourceId: string;
333
+ }): Promise<MonitorSnapshot>;
334
+ }
335
+ interface UsageAttribution {
336
+ runId?: string;
337
+ sessionId?: string;
338
+ workflowRunId?: string;
339
+ agentId?: string;
340
+ provider?: string;
341
+ model?: string;
342
+ }
343
+ interface UsageEvent extends UsageAttribution {
344
+ schemaVersion: 1;
345
+ eventId: string;
346
+ timestamp: string;
347
+ inputTokens: number;
348
+ outputTokens: number;
349
+ cachedInputTokens?: number;
350
+ estimatedCostUsd?: number;
351
+ metadata?: Record<string, string | number | boolean>;
352
+ }
353
+ interface UsageQuery extends UsageAttribution {
354
+ from?: string;
355
+ to?: string;
356
+ }
357
+ interface UsageTotals {
358
+ inputTokens: number;
359
+ outputTokens: number;
360
+ totalTokens: number;
361
+ cachedInputTokens: number;
362
+ estimatedCostUsd: number;
363
+ events: number;
364
+ }
365
+ declare class UsageAccumulator {
366
+ private totalsValue;
367
+ add(event: Pick<UsageEvent, "inputTokens" | "outputTokens" | "cachedInputTokens" | "estimatedCostUsd">): this;
368
+ snapshot(): UsageTotals;
369
+ }
370
+ declare class UsageLedger {
371
+ private readonly path;
372
+ constructor(path?: string);
373
+ append(input: Omit<UsageEvent, "schemaVersion" | "eventId" | "timestamp"> & {
374
+ timestamp?: string;
375
+ }): UsageEvent;
376
+ query(query?: UsageQuery): UsageEvent[];
377
+ summarize(query?: UsageQuery): UsageTotals;
378
+ }
379
+ interface OtelUsageAdapter {
380
+ addCounter(name: string, value: number, attributes: Readonly<Record<string, string>>): void;
381
+ recordEvent?(name: string, attributes: Readonly<Record<string, string>>): void;
382
+ }
383
+ declare class OtelExporter {
384
+ private readonly adapter;
385
+ constructor(adapter: OtelUsageAdapter);
386
+ export(events: readonly UsageEvent[]): void;
387
+ }
123
388
  interface ControlPlaneLockRecord {
124
389
  schemaVersion: 1;
125
390
  name: string;
@@ -135,4 +400,4 @@ interface ControlPlaneLockHandle {
135
400
  release: () => void;
136
401
  }
137
402
  declare function acquireControlPlaneLock(path: string, name: string, version: string): ControlPlaneLockHandle;
138
- export { type AgentRunAgentDefinitionRef, type AgentRunCreateInput, type AgentRunEvent, type AgentRunListOptions, type AgentRunRecord, type AgentRunStatus, AgentRunStore, type AgentRunUsage, type ControlPlaneLockHandle, type ControlPlaneLockRecord, acquireControlPlaneLock, createAgentRunId, getAgentRunDir, getAgentRunStoreDir };
403
+ export { AGENT_DAEMON_PROTOCOL_VERSION, AgentDaemonClient, type AgentDaemonError, type AgentDaemonHandler, type AgentDaemonRequest, type AgentDaemonResponse, AgentDaemonServer, type AgentDaemonTransport, AgentEventStore, type AgentRunAgentDefinitionRef, AgentRunController, type AgentRunControllerOptions, type AgentRunCreateInput, type AgentRunEvent, type AgentRunListOptions, type AgentRunRecord, type AgentRunSpawnResult, type AgentRunStatus, AgentRunStore, type AgentRunUsage, type ControlPlaneLockHandle, type ControlPlaneLockRecord, type MonitorEvent, MonitorManager, type MonitorSnapshot, type MonitorSource, MonitorStopTool, MonitorTool, OtelExporter, type OtelUsageAdapter, UsageAccumulator, type UsageAttribution, type UsageEvent, UsageLedger, type UsageQuery, type UsageTotals, type WorkflowDefinition, type WorkflowEvent, type WorkflowNodeDefinition, type WorkflowNodeExecutor, type WorkflowNodeStatus, type WorkflowPlan, WorkflowPlanner, type WorkflowRunNodeRecord, type WorkflowRunRecord, type WorkflowRunStatus, WorkflowRuntime, WorkflowStore, acquireControlPlaneLock, createAgentRunId, getAgentRunDir, getAgentRunStoreDir };
@@ -1,4 +1,7 @@
1
- import{randomUUID as J}from"crypto";import{appendFileSync as M,existsSync as y,mkdirSync as l,readFileSync as L,readdirSync as U,renameSync as z,rmSync as j,writeFileSync as g}from"fs";import{homedir as q}from"os";import{basename as P,dirname as b,join as u,resolve as B}from"path";var m="0.6.4";import{mkdirSync as x,readFileSync as O,rmSync as v,writeFileSync as V}from"fs";import{hostname as k}from"os";import{dirname as _}from"path";function I(){return new Date().toISOString()}function N(){return new Date(Date.now()-Math.floor(process.uptime()*1e3)).toISOString()}function w(e){try{return JSON.parse(O(e,"utf8"))}catch{return null}}function T(e){if(!e||e<=0)return!1;try{return process.kill(e,0),!0}catch{return!1}}function F(e){return e.hostname!==k()?!0:T(e.pid)}function h(e,t){x(_(e),{recursive:!0}),V(e,JSON.stringify(t,null,2)+`
2
- `,{encoding:"utf8",flag:"wx"})}function $(e,t){let r=w(e);r?.pid!==t.pid||r.hostname!==t.hostname||v(e,{force:!0})}function S(e,t,r){let n=I(),s={schemaVersion:1,name:t,pid:process.pid,hostname:k(),processStartedAt:N(),version:r,acquiredAt:n,updatedAt:n};try{h(e,s)}catch(a){if(a.code!=="EEXIST")throw a;let c=w(e);if(c&&F(c))throw new Error(`${t} is already locked by PID ${c.pid} on ${c.hostname}.`);v(e,{force:!0}),h(e,s)}return{record:s,release:()=>{$(e,s)}}}var D=1,H=u(q(),".xeno-agent"),W=u(process.env.XENO_AGENT_HOME??H,"daemon"),A=u(W,"runs");function i(){return new Date().toISOString()}function G(e=new Date){return e.toISOString().replace(/[^0-9]/g,"").slice(0,14)}function R(e){return e.replace(/[^A-Za-z0-9_.-]/g,"_").slice(0,80)}function p(e){return B(e)}function f(e){return p(e).toLowerCase()}function C(e,t){let r=f(e),n=f(t);return r===n||r.startsWith(`${n}\\`)||r.startsWith(`${n}/`)}function K(e,t){return!!(f(e.cwd)===f(t)||e.git?.root&&C(t,e.git.root)||e.git?.worktree&&C(t,e.git.worktree))}function X(e){return R(Buffer.from(p(e)).toString("base64url"))}function Z(){return{input:0,output:0,total:0}}function d(e){try{return JSON.parse(L(e,"utf8"))}catch{return null}}function Q(e){return e instanceof Error&&/\balready locked by PID\b/.test(e.message)}function Y(e,t){l(b(e),{recursive:!0});let r=`${e}.${process.pid}.${Date.now()}.tmp`;g(r,JSON.stringify(t,null,2)+`
3
- `,"utf8"),z(r,e)}function tt(e){if(!e||e<=0)return!1;try{return process.kill(e,0),!0}catch{return!1}}function et(e){return e==="completed"||e==="failed"||e==="cancelled"||e==="interrupted"}function rt(e,t){return e.pinned!==t.pinned?e.pinned?-1:1:Date.parse(t.updatedAt)-Date.parse(e.updatedAt)}function Rt(){return A}function At(e){return u(A,e)}function nt(e=new Date){return`run_${G(e)}_${J().slice(0,8)}`}var E=class{constructor(t=A){this.runsDir=t;l(this.runsDir,{recursive:!0})}runsDir;create(t){let r=nt(),n=u(this.runsDir,r);l(n,{recursive:!0});let s=i(),a=u(n,"events.jsonl"),o={schemaVersion:D,runId:r,sessionId:r,rootRunId:r,cwd:p(t.cwd),workspaceId:X(t.cwd),prompt:t.prompt,...t.title?{title:t.title}:{},status:"starting",model:t.model,...t.effort?{effort:t.effort}:{},...t.agent?{agent:t.agent}:{},...t.git?{git:t.git}:{},permissionMode:t.permissionMode,...t.cliVersion?{createdByCliVersion:t.cliVersion}:{},createdBySdkVersion:t.sdkVersion??m,createdAt:s,updatedAt:s,lastActivityAt:s,pinned:!!t.pinned,attachedClients:0,...t.cliVersion?{cliVersion:t.cliVersion}:{},...t.sdkVersion?{sdkVersion:t.sdkVersion}:{},stdoutPath:u(n,"stdout.log"),stderrPath:u(n,"stderr.log"),eventsPath:a,usage:Z(),children:[],tags:[]};return g(o.stdoutPath,"","utf8"),g(o.stderrPath,"","utf8"),g(o.eventsPath,"","utf8"),this.write(o),this.appendEvent(r,"run_created",{cwd:o.cwd,model:o.model,...o.agent?{agent:o.agent}:{},...o.git?{git:o.git}:{},permissionMode:o.permissionMode,pinned:o.pinned}),o}get(t){let r=d(this.recordPath(t));return r?this.refreshRecordStatus(r):null}list(t={}){if(!y(this.runsDir))return[];let r=U(this.runsDir,{withFileTypes:!0}).filter(n=>n.isDirectory()).map(n=>{try{return this.get(n.name)}catch(s){if(Q(s))return d(this.recordPath(n.name));throw s}}).filter(n=>n!==null).filter(n=>t.all||!t.cwd||K(n,t.cwd)).filter(n=>!t.status||t.status.includes(n.status)).sort(rt);return typeof t.limit=="number"&&t.limit>0?r.slice(0,t.limit):r}markSpawned(t,r,n){return this.update(t,s=>({...s,status:"running",workerPid:r,...n?{workerVersion:n}:{},startedAt:s.startedAt??i(),lastActivityAt:i()}))}markStarted(t,r){return this.update(t,n=>({...n,status:"running",workerPid:r.pid,...r.workerVersion?{workerVersion:r.workerVersion}:{},...r.model?{model:r.model}:{},...r.effort?{effort:r.effort}:{},startedAt:n.startedAt??i(),lastActivityAt:i()}))}markCompleted(t,r){return this.update(t,n=>({...n,status:r.status,...r.statusReason?{statusReason:r.statusReason}:{},exitCode:r.exitCode??null,usage:r.usage??n.usage,endedAt:i(),lastActivityAt:i()}))}markCancelled(t,r){return this.update(t,n=>({...n,status:"cancelled",statusReason:r,endedAt:i(),lastActivityAt:i()}))}markPaused(t,r="Paused by user."){let n=this.update(t,s=>({...s,status:"paused",statusReason:r,lastActivityAt:i()}));return n&&this.appendEvent(t,"run_paused",{reason:r}),n}markResumed(t,r="Resumed by user."){let n=this.update(t,s=>{let a={...s,status:"running",statusReason:r,lastActivityAt:i()};return delete a.endedAt,delete a.exitCode,a});return n&&this.appendEvent(t,"run_resumed",{reason:r}),n}setPinned(t,r){let n=this.update(t,s=>({...s,pinned:r,lastActivityAt:i()}));return n&&this.appendEvent(t,r?"run_pinned":"run_unpinned",{pinned:r}),n}appendEvent(t,r,n={}){let s=d(this.recordPath(t)),a=s?.eventsPath??u(this.runsDir,t,"events.jsonl");l(b(a),{recursive:!0});let o={schemaVersion:D,runId:t,type:r,timestamp:i(),payload:n};M(a,JSON.stringify(o)+`
4
- `,"utf8"),s&&this.update(t,c=>({...c,lastActivityAt:o.timestamp}))}readEvents(t){let r=this.get(t);return!r||!y(r.eventsPath)?[]:L(r.eventsPath,"utf8").split(/\r?\n/).map(n=>n.trim()).filter(Boolean).map(n=>{try{return JSON.parse(n)}catch{return null}}).filter(n=>n!==null)}delete(t){j(u(this.runsDir,t),{recursive:!0,force:!0})}refreshRecordStatus(t){if(et(t.status))return t;if(t.workerPid&&!tt(t.workerPid)){let r={...t,status:"failed",statusReason:"Worker process is no longer running and did not record a terminal status.",endedAt:t.endedAt??i(),updatedAt:i()};return this.write(r),r}return t}update(t,r){let n=d(this.recordPath(t));if(!n)return null;let s=r(n);return s.updatedAt=i(),this.write(s),s}write(t){let r=S(u(this.runDir(t.runId),"run.lock"),`run:${t.runId}`,m);try{Y(this.recordPath(t.runId),t)}finally{r.release()}}recordPath(t){return u(this.runsDir,R(P(t)),"run.json")}runDir(t){return u(this.runsDir,R(P(t)))}};export{E as AgentRunStore,S as acquireControlPlaneLock,nt as createAgentRunId,At as getAgentRunDir,Rt as getAgentRunStoreDir};
1
+ import{randomUUID as Ee}from"crypto";import{existsSync as De,mkdirSync as b,readFileSync as xe,readdirSync as Ie,renameSync as Te,rmSync as Pe,writeFileSync as S}from"fs";import{homedir as be}from"os";import{basename as H,dirname as Oe,join as c,resolve as _e}from"path";var h="0.6.4";import{mkdirSync as ue,readFileSync as de,rmSync as $,writeFileSync as ce}from"fs";import{hostname as L}from"os";import{dirname as le}from"path";function pe(){return new Date().toISOString()}function ge(){return new Date(Date.now()-Math.floor(process.uptime()*1e3)).toISOString()}function F(n){try{return JSON.parse(de(n,"utf8"))}catch{return null}}function me(n){if(!n||n<=0)return!1;try{return process.kill(n,0),!0}catch{return!1}}function fe(n){return n.hostname!==L()?!0:me(n.pid)}function W(n,e){ue(le(n),{recursive:!0}),ce(n,JSON.stringify(e,null,2)+`
2
+ `,{encoding:"utf8",flag:"wx"})}function he(n,e){let t=F(n);t?.pid!==e.pid||t.hostname!==e.hostname||$(n,{force:!0})}function k(n,e,t){let r=pe(),o={schemaVersion:1,name:e,pid:process.pid,hostname:L(),processStartedAt:ge(),version:t,acquiredAt:r,updatedAt:r};try{W(n,o)}catch(i){if(i.code!=="EEXIST")throw i;let a=F(n);if(a&&fe(a))throw new Error(`${e} is already locked by PID ${a.pid} on ${a.hostname}.`);$(n,{force:!0}),W(n,o)}return{record:o,release:()=>{he(n,o)}}}import{randomUUID as Re}from"crypto";import{appendFileSync as we,existsSync as ve,mkdirSync as j,readFileSync as ke}from"fs";import{basename as Ae,dirname as ye,join as Se}from"path";function J(n){return Ae(n).replace(/[^A-Za-z0-9_.-]/g,"_").slice(0,96)}function q(n){return ve(n)?ke(n,"utf8").split(/\r?\n/).map(e=>e.trim()).filter(Boolean).map(e=>{try{return JSON.parse(e)}catch{return null}}).filter(e=>e!==null):[]}var A=class{constructor(e){this.runsDir=e;j(this.runsDir,{recursive:!0})}runsDir;pathFor(e){return Se(this.runsDir,J(e),"events.jsonl")}append(e,t,r={},o=new Date().toISOString()){let i=this.pathFor(e);j(ye(i),{recursive:!0});let s=k(`${i}.lock`,`events:${J(e)}`,h);try{let a=q(i).reduce((T,l,v)=>Math.max(T,l.sequence??v),-1)+1,p={schemaVersion:1,eventId:`event_${Re()}`,sequence:a,runId:e,type:t,timestamp:o,payload:r};return we(i,`${JSON.stringify(p)}
3
+ `,"utf8"),p}finally{s.release()}}read(e){return q(this.pathFor(e))}tail(e,t=100){let r=Math.max(0,Math.min(1e4,Math.trunc(t)));return r===0?[]:this.read(e).slice(-r)}};var Ce=1,Ue=c(be(),".xeno-agent"),Me=c(process.env.XENO_AGENT_HOME??Ue,"daemon"),_=c(Me,"runs");function d(){return new Date().toISOString()}function Ne(n=new Date){return n.toISOString().replace(/[^0-9]/g,"").slice(0,14)}function O(n){return n.replace(/[^A-Za-z0-9_.-]/g,"_").slice(0,80)}function C(n){return _e(n)}function E(n){return C(n).toLowerCase()}function z(n,e){let t=E(n),r=E(e);return t===r||t.startsWith(`${r}\\`)||t.startsWith(`${r}/`)}function Ve(n,e){return!!(E(n.cwd)===E(e)||n.git?.root&&z(e,n.git.root)||n.git?.worktree&&z(e,n.git.worktree))}function We(n){return O(Buffer.from(C(n)).toString("base64url"))}function $e(){return{input:0,output:0,total:0}}function y(n){try{return JSON.parse(xe(n,"utf8"))}catch{return null}}function Le(n){return n instanceof Error&&/\balready locked by PID\b/.test(n.message)}function Fe(n,e){b(Oe(n),{recursive:!0});let t=`${n}.${process.pid}.${Date.now()}.tmp`;S(t,JSON.stringify(e,null,2)+`
4
+ `,"utf8"),Te(t,n)}function je(n){if(!n||n<=0)return!1;try{return process.kill(n,0),!0}catch{return!1}}function Je(n){return n==="completed"||n==="failed"||n==="cancelled"||n==="interrupted"}function qe(n,e){return n.pinned!==e.pinned?n.pinned?-1:1:Date.parse(e.updatedAt)-Date.parse(n.updatedAt)}function bt(){return _}function Ot(n){return c(_,n)}function He(n=new Date){return`run_${Ne(n)}_${Ee().slice(0,8)}`}var D=class{constructor(e=_){this.runsDir=e;b(this.runsDir,{recursive:!0}),this.eventStore=new A(this.runsDir)}runsDir;eventStore;create(e){let t=He(),r=c(this.runsDir,t);b(r,{recursive:!0});let o=d(),i=c(r,"events.jsonl"),s={schemaVersion:Ce,runId:t,sessionId:t,rootRunId:t,cwd:C(e.cwd),workspaceId:We(e.cwd),prompt:e.prompt,...e.title?{title:e.title}:{},status:"starting",model:e.model,...e.effort?{effort:e.effort}:{},...e.agent?{agent:e.agent}:{},...e.git?{git:e.git}:{},permissionMode:e.permissionMode,...e.cliVersion?{createdByCliVersion:e.cliVersion}:{},createdBySdkVersion:e.sdkVersion??h,createdAt:o,updatedAt:o,lastActivityAt:o,pinned:!!e.pinned,attachedClients:0,...e.cliVersion?{cliVersion:e.cliVersion}:{},...e.sdkVersion?{sdkVersion:e.sdkVersion}:{},stdoutPath:c(r,"stdout.log"),stderrPath:c(r,"stderr.log"),eventsPath:i,usage:$e(),children:[],tags:[]};return S(s.stdoutPath,"","utf8"),S(s.stderrPath,"","utf8"),S(s.eventsPath,"","utf8"),this.write(s),this.appendEvent(t,"run_created",{cwd:s.cwd,model:s.model,...s.agent?{agent:s.agent}:{},...s.git?{git:s.git}:{},permissionMode:s.permissionMode,pinned:s.pinned}),s}get(e){let t=y(this.recordPath(e));return t?this.refreshRecordStatus(t):null}list(e={}){if(!De(this.runsDir))return[];let t=Ie(this.runsDir,{withFileTypes:!0}).filter(r=>r.isDirectory()).map(r=>{try{return this.get(r.name)}catch(o){if(Le(o))return y(this.recordPath(r.name));throw o}}).filter(r=>r!==null).filter(r=>e.all||!e.cwd||Ve(r,e.cwd)).filter(r=>!e.status||e.status.includes(r.status)).sort(qe);return typeof e.limit=="number"&&e.limit>0?t.slice(0,e.limit):t}markSpawned(e,t,r){return this.update(e,o=>({...o,status:"running",workerPid:t,...r?{workerVersion:r}:{},startedAt:o.startedAt??d(),lastActivityAt:d()}))}markStarted(e,t){return this.update(e,r=>({...r,status:"running",workerPid:t.pid,...t.workerVersion?{workerVersion:t.workerVersion}:{},...t.model?{model:t.model}:{},...t.effort?{effort:t.effort}:{},startedAt:r.startedAt??d(),lastActivityAt:d()}))}markCompleted(e,t){return this.update(e,r=>({...r,status:t.status,...t.statusReason?{statusReason:t.statusReason}:{},exitCode:t.exitCode??null,usage:t.usage??r.usage,endedAt:d(),lastActivityAt:d()}))}markCancelled(e,t){return this.update(e,r=>({...r,status:"cancelled",statusReason:t,endedAt:d(),lastActivityAt:d()}))}markPaused(e,t="Paused by user."){let r=this.update(e,o=>({...o,status:"paused",statusReason:t,lastActivityAt:d()}));return r&&this.appendEvent(e,"run_paused",{reason:t}),r}markResumed(e,t="Resumed by user."){let r=this.update(e,o=>{let i={...o,status:"running",statusReason:t,lastActivityAt:d()};return delete i.endedAt,delete i.exitCode,i});return r&&this.appendEvent(e,"run_resumed",{reason:t}),r}setPinned(e,t){let r=this.update(e,o=>({...o,pinned:t,lastActivityAt:d()}));return r&&this.appendEvent(e,t?"run_pinned":"run_unpinned",{pinned:t}),r}appendEvent(e,t,r={}){let o=y(this.recordPath(e)),i=this.eventStore.append(e,t,r);o&&this.update(e,s=>({...s,lastActivityAt:i.timestamp}))}readEvents(e){return this.get(e)?this.eventStore.read(e):[]}delete(e){Pe(c(this.runsDir,e),{recursive:!0,force:!0})}refreshRecordStatus(e){if(Je(e.status))return e;if(e.workerPid&&!je(e.workerPid)){let t={...e,status:"failed",statusReason:"Worker process is no longer running and did not record a terminal status.",endedAt:e.endedAt??d(),updatedAt:d()};return this.write(t),t}return e}update(e,t){let r=y(this.recordPath(e));if(!r)return null;let o=t(r);return o.updatedAt=d(),this.write(o),o}write(e){let t=k(c(this.runDir(e.runId),"run.lock"),`run:${e.runId}`,h);try{Fe(this.recordPath(e.runId),e)}finally{t.release()}}recordPath(e){return c(this.runsDir,O(H(e)),"run.json")}runDir(e){return c(this.runsDir,O(H(e)))}};function B(n){return n==="completed"||n==="failed"||n==="cancelled"||n==="interrupted"}var G=class{constructor(e={}){this.options=e;this.store=e.store??new D}options;store;create(e){return this.store.create(e)}async start(e){let t=this.create(e);if(!this.options.spawn)return t;try{let r=await this.options.spawn(t),o=this.store.markSpawned(t.runId,r.pid,r.workerVersion);if(!o)throw new Error(`Run disappeared during spawn: ${t.runId}`);return this.store.appendEvent(t.runId,"run_started",{pid:r.pid}),o}catch(r){let o=r instanceof Error?r.message:String(r);throw this.store.markCompleted(t.runId,{status:"failed",statusReason:o,exitCode:null}),this.store.appendEvent(t.runId,"run_start_failed",{reason:o}),r}}get(e){return this.store.get(e)}list(e={}){return this.store.list(e)}events(e,t){let r=this.store.readEvents(e);return t===void 0?r:r.slice(-Math.max(0,Math.trunc(t)))}async stop(e,t="Stopped by user."){let r=this.store.get(e);if(!r||B(r.status))return r;await this.options.stop?.(r,t);let o=this.store.markCancelled(e,t);return o&&this.store.appendEvent(e,"run_cancelled",{reason:t}),o}async pause(e,t="Paused by user."){let r=this.store.get(e);return!r||B(r.status)||r.status==="paused"?r:(await this.options.pause?.(r,t),this.store.markPaused(e,t))}async resume(e,t="Resumed by user."){let r=this.store.get(e);return!r||r.status!=="paused"?r:(await this.options.resume?.(r,t),this.store.markResumed(e,t))}complete(e,t,r={}){let o=this.store.markCompleted(e,{status:t,...r.reason?{statusReason:r.reason}:{},...r.exitCode!==void 0?{exitCode:r.exitCode}:{},...r.usage?{usage:r.usage}:{}});return o&&this.store.appendEvent(e,`run_${t}`,{...r.reason?{reason:r.reason}:{}}),o}};import{randomUUID as ze,timingSafeEqual as Be}from"crypto";var R=1;function Ge(n,e){if(!n)return!1;let t=Buffer.from(n),r=Buffer.from(e);return t.length===r.length&&Be(t,r)}var Q=class{constructor(e={}){this.options=e}options;handlers=new Map;register(e,t){let r=e.trim();if(!r)throw new Error("Daemon method name cannot be empty.");if(this.handlers.has(r))throw new Error(`Daemon method already registered: ${r}`);return this.handlers.set(r,t),this}registerController(e){return this.register("runs.start",t=>e.start(t)).register("runs.get",t=>e.get(t.runId)).register("runs.list",t=>e.list(t??{})).register("runs.events",t=>{let r=t;return e.events(r.runId,r.limit)}).register("runs.stop",t=>{let r=t;return e.stop(r.runId,r.reason)}).register("runs.pause",t=>{let r=t;return e.pause(r.runId,r.reason)}).register("runs.resume",t=>{let r=t;return e.resume(r.runId,r.reason)})}async handle(e){if(e.protocolVersion!==R)return this.error(e.id,"PROTOCOL_MISMATCH",`Unsupported protocol version: ${e.protocolVersion}`);if(this.options.authToken&&!Ge(e.authToken,this.options.authToken))return this.error(e.id,"UNAUTHORIZED","Daemon authentication failed.");let t=this.handlers.get(e.method);if(!t)return this.error(e.id,"METHOD_NOT_FOUND",`Unknown daemon method: ${e.method}`);try{return{protocolVersion:R,id:e.id,result:await t(e.params,e)}}catch(r){return this.error(e.id,"INTERNAL_ERROR",r instanceof Error?r.message:String(r))}}error(e,t,r){return{protocolVersion:R,id:e,error:{code:t,message:r}}}},X=class{constructor(e,t={}){this.transport=e;this.options=t}transport;options;async request(e,t){let r={protocolVersion:R,id:`request_${ze()}`,method:e,params:t,...this.options.authToken?{authToken:this.options.authToken}:{}},o=await this.transport(r);if(o.protocolVersion!==R||o.id!==r.id)throw new Error("Invalid daemon response identity.");if("error"in o){let i=new Error(o.error.message);throw i.code=o.error.code,i}return o.result}};import{randomUUID as Z}from"crypto";import{appendFileSync as Qe,existsSync as Xe,mkdirSync as I,readFileSync as ee,readdirSync as Ze,renameSync as Ke,writeFileSync as Ye}from"fs";import{homedir as et}from"os";import{basename as tt,dirname as te,join as g}from"path";function x(n){return tt(n).replace(/[^A-Za-z0-9_.-]/g,"_").slice(0,96)}function U(n){try{return JSON.parse(ee(n,"utf8"))}catch{return null}}function K(n,e){I(te(n),{recursive:!0});let t=`${n}.${process.pid}.${Date.now()}.tmp`;Ye(t,`${JSON.stringify(e,null,2)}
5
+ `,"utf8"),Ke(t,n)}var w=class{plan(e){if(e.schemaVersion!==1)throw new Error("Unsupported workflow schema version.");if(!e.id.trim()||!e.name.trim())throw new Error("Workflow id and name are required.");let t=new Map;for(let s of e.nodes){if(!s.id.trim())throw new Error("Workflow node id cannot be empty.");if(t.has(s.id))throw new Error(`Duplicate workflow node: ${s.id}`);t.set(s.id,s)}for(let s of e.nodes)for(let a of s.dependsOn??[]){if(!t.has(a))throw new Error(`Workflow node ${s.id} depends on unknown node ${a}.`);if(a===s.id)throw new Error(`Workflow node ${s.id} cannot depend on itself.`)}let r=new Set(t.keys()),o=new Set,i=[];for(;r.size>0;){let s=[...r].filter(a=>(t.get(a)?.dependsOn??[]).every(p=>o.has(p))).sort();if(s.length===0)throw new Error("Workflow dependency graph contains a cycle.");i.push(s);for(let a of s)r.delete(a),o.add(a)}return{workflowId:e.id,waves:i}}},M=class{constructor(e=g(process.env.XENO_AGENT_HOME??g(et(),".xeno-agent"),"workflows")){this.rootDir=e;I(this.definitionsDir(),{recursive:!0}),I(this.runsDir(),{recursive:!0})}rootDir;saveDefinition(e){return new w().plan(e),K(g(this.definitionsDir(),`${x(e.id)}.json`),e),e}getDefinition(e){return U(g(this.definitionsDir(),`${x(e)}.json`))}listDefinitions(){return Ze(this.definitionsDir(),{withFileTypes:!0}).filter(e=>e.isFile()&&e.name.endsWith(".json")).map(e=>U(g(this.definitionsDir(),e.name))).filter(e=>e!==null)}createRun(e){new w().plan(e);let t=new Date().toISOString(),r={schemaVersion:1,runId:`workflow_run_${Z()}`,workflowId:e.id,status:"queued",createdAt:t,updatedAt:t,nodes:Object.fromEntries(e.nodes.map(o=>[o.id,{status:"pending"}]))};return this.writeRun(r),this.appendEvent(r.runId,"workflow_created",{}),r}getRun(e){return U(this.runPath(e))}updateRun(e,t){let r=this.getRun(e);if(!r)throw new Error(`Workflow run not found: ${e}`);let o=t(r);return o.updatedAt=new Date().toISOString(),this.writeRun(o),o}appendEvent(e,t,r,o){let i={schemaVersion:1,eventId:`workflow_event_${Z()}`,runId:e,type:t,timestamp:new Date().toISOString(),...o?{nodeId:o}:{},payload:r},s=this.eventsPath(e);return I(te(s),{recursive:!0}),Qe(s,`${JSON.stringify(i)}
6
+ `,"utf8"),i}readEvents(e){let t=this.eventsPath(e);return Xe(t)?ee(t,"utf8").split(/\r?\n/).filter(Boolean).flatMap(r=>{try{return[JSON.parse(r)]}catch{return[]}}):[]}writeRun(e){K(this.runPath(e.runId),e)}definitionsDir(){return g(this.rootDir,"definitions")}runsDir(){return g(this.rootDir,"runs")}runPath(e){return g(this.runsDir(),x(e),"run.json")}eventsPath(e){return g(this.runsDir(),x(e),"events.jsonl")}},Y=class{constructor(e=new M,t=new w){this.store=e;this.planner=t}store;planner;async run(e,t){let r=this.planner.plan(e),o=this.store.createRun(e);o=this.store.updateRun(o.runId,a=>({...a,status:"running"})),this.store.appendEvent(o.runId,"workflow_started",{waves:r.waves.length});let i=Math.max(1,Math.trunc(e.maxConcurrency??4)),s=!1;for(let a of r.waves)for(let p=0;p<a.length;p+=i){let T=a.slice(p,p+i);await Promise.all(T.map(async l=>{let v=e.nodes.find(u=>u.id===l);if((v.dependsOn??[]).some(u=>this.store.getRun(o.runId)?.nodes[u]?.status!=="completed")||s&&e.failFast!==!1){this.store.updateRun(o.runId,u=>({...u,nodes:{...u.nodes,[l]:{status:"skipped",completedAt:new Date().toISOString()}}})),this.store.appendEvent(o.runId,"node_skipped",{},l);return}let P=new Date().toISOString();o=this.store.updateRun(o.runId,u=>({...u,nodes:{...u.nodes,[l]:{status:"running",startedAt:P}}})),this.store.appendEvent(o.runId,"node_started",{},l);try{let u=await t(v,{run:o,definition:e});this.store.updateRun(o.runId,f=>({...f,nodes:{...f.nodes,[l]:{status:"completed",startedAt:P,completedAt:new Date().toISOString(),output:u}}})),this.store.appendEvent(o.runId,"node_completed",{},l)}catch(u){s=!0;let f=u instanceof Error?u.message:String(u);this.store.updateRun(o.runId,V=>({...V,nodes:{...V.nodes,[l]:{status:"failed",startedAt:P,completedAt:new Date().toISOString(),error:f}}})),this.store.appendEvent(o.runId,"node_failed",{error:f},l)}}))}return o=this.store.updateRun(o.runId,a=>({...a,status:s?"failed":"completed",completedAt:new Date().toISOString()})),this.store.appendEvent(o.runId,s?"workflow_failed":"workflow_completed",{}),o}};import{randomUUID as rt}from"crypto";var re=class{constructor(e=1e4){this.maxEvents=e;if(!Number.isInteger(e)||e<1)throw new Error("maxEvents must be a positive integer.")}maxEvents;sources=new Map;events=[];register(e){if(!e.id.trim())throw new Error("Monitor source id cannot be empty.");if(this.sources.has(e.id))throw new Error(`Monitor source already registered: ${e.id}`);return this.sources.set(e.id,{source:e,running:!1}),this}async start(e,t=1e3){let r=this.require(e);if(r.running)return this.snapshot(e);await r.source.start?.(),r.running=!0,await this.poll(e);let o=Math.max(25,Math.trunc(t));return r.timer=setInterval(()=>{this.poll(e).catch(()=>{})},o),r.timer.unref?.(),this.snapshot(e)}async poll(e){let o=(await this.require(e).source.poll()).map(i=>({schemaVersion:1,eventId:`monitor_event_${rt()}`,sourceId:e,type:i.type,timestamp:i.timestamp??new Date().toISOString(),payload:i.payload}));return this.events.push(...o),this.events.length>this.maxEvents&&this.events.splice(0,this.events.length-this.maxEvents),o}async stop(e){let t=this.require(e);return t.timer&&clearInterval(t.timer),t.timer=void 0,t.running&&await t.source.stop?.(),t.running=!1,this.snapshot(e)}async stopAll(){await Promise.all([...this.sources.keys()].map(e=>this.stop(e)))}snapshot(e,t=100){let r=this.require(e);return{sourceId:e,running:r.running,events:this.events.filter(o=>o.sourceId===e).slice(-Math.max(0,Math.trunc(t)))}}list(){return[...this.sources].map(([e,t])=>({sourceId:e,running:t.running}))}require(e){let t=this.sources.get(e);if(!t)throw new Error(`Monitor source not found: ${e}`);return t}},ne=class{constructor(e){this.manager=e}manager;execute(e){return this.manager.start(e.sourceId,e.intervalMs)}},oe=class{constructor(e){this.manager=e}manager;execute(e){return this.manager.stop(e.sourceId)}};import{randomUUID as nt}from"crypto";import{appendFileSync as ot,existsSync as st,mkdirSync as it,readFileSync as at}from"fs";import{homedir as ut}from"os";import{dirname as dt,join as se}from"path";function m(n){return Number.isFinite(n)?Math.max(0,n??0):0}var N=class{totalsValue={inputTokens:0,outputTokens:0,totalTokens:0,cachedInputTokens:0,estimatedCostUsd:0,events:0};add(e){let t=m(e.inputTokens),r=m(e.outputTokens);return this.totalsValue.inputTokens+=t,this.totalsValue.outputTokens+=r,this.totalsValue.totalTokens+=t+r,this.totalsValue.cachedInputTokens+=m(e.cachedInputTokens),this.totalsValue.estimatedCostUsd+=m(e.estimatedCostUsd),this.totalsValue.events+=1,this}snapshot(){return{...this.totalsValue}}};function ct(n,e){if(["runId","sessionId","workflowRunId","agentId","provider","model"].some(o=>e[o]!==void 0&&n[o]!==e[o]))return!1;let r=Date.parse(n.timestamp);return!(e.from&&r<Date.parse(e.from)||e.to&&r>Date.parse(e.to))}var ie=class{path;constructor(e=se(process.env.XENO_AGENT_HOME??se(ut(),".xeno-agent"),"usage","events.jsonl")){this.path=e,it(dt(e),{recursive:!0})}append(e){let t={...e,schemaVersion:1,eventId:`usage_${nt()}`,timestamp:e.timestamp??new Date().toISOString(),inputTokens:m(e.inputTokens),outputTokens:m(e.outputTokens),...e.cachedInputTokens!==void 0?{cachedInputTokens:m(e.cachedInputTokens)}:{},...e.estimatedCostUsd!==void 0?{estimatedCostUsd:m(e.estimatedCostUsd)}:{}};return ot(this.path,`${JSON.stringify(t)}
7
+ `,"utf8"),t}query(e={}){return st(this.path)?at(this.path,"utf8").split(/\r?\n/).filter(Boolean).flatMap(t=>{try{let r=JSON.parse(t);return ct(r,e)?[r]:[]}catch{return[]}}):[]}summarize(e={}){let t=new N;for(let r of this.query(e))t.add(r);return t.snapshot()}},ae=class{constructor(e){this.adapter=e}adapter;export(e){for(let t of e){let r={...t.provider?{provider:t.provider}:{},...t.model?{model:t.model}:{}};this.adapter.addCounter("xeno.usage.input_tokens",t.inputTokens,r),this.adapter.addCounter("xeno.usage.output_tokens",t.outputTokens,r),t.cachedInputTokens&&this.adapter.addCounter("xeno.usage.cached_input_tokens",t.cachedInputTokens,r),t.estimatedCostUsd&&this.adapter.addCounter("xeno.usage.estimated_cost_usd",t.estimatedCostUsd,r),this.adapter.recordEvent?.("xeno.usage.recorded",r)}}};export{R as AGENT_DAEMON_PROTOCOL_VERSION,X as AgentDaemonClient,Q as AgentDaemonServer,A as AgentEventStore,G as AgentRunController,D as AgentRunStore,re as MonitorManager,oe as MonitorStopTool,ne as MonitorTool,ae as OtelExporter,N as UsageAccumulator,ie as UsageLedger,w as WorkflowPlanner,Y as WorkflowRuntime,M as WorkflowStore,k as acquireControlPlaneLock,He as createAgentRunId,Ot as getAgentRunDir,bt as getAgentRunStoreDir};
@@ -0,0 +1 @@
1
+ {"inputs":{"src/control-plane/types.ts":{"bytes":2358,"imports":[],"format":"esm"},"src/version.ts":{"bytes":36,"imports":[],"format":"esm"},"src/control-plane/lock.ts":{"bytes":2496,"imports":[{"path":"fs","kind":"import-statement","external":true},{"path":"os","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true}],"format":"esm"},"src/control-plane/event-store.ts":{"bytes":2302,"imports":[{"path":"crypto","kind":"import-statement","external":true},{"path":"fs","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"src/version.ts","kind":"import-statement","original":"../version.js"},{"path":"src/control-plane/lock.ts","kind":"import-statement","original":"./lock.js"}],"format":"esm"},"src/control-plane/run-store.ts":{"bytes":11654,"imports":[{"path":"crypto","kind":"import-statement","external":true},{"path":"fs","kind":"import-statement","external":true},{"path":"os","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"src/version.ts","kind":"import-statement","original":"../version.js"},{"path":"src/control-plane/lock.ts","kind":"import-statement","original":"./lock.js"},{"path":"src/control-plane/event-store.ts","kind":"import-statement","original":"./event-store.js"}],"format":"esm"},"src/control-plane/controller.ts":{"bytes":4113,"imports":[{"path":"src/control-plane/run-store.ts","kind":"import-statement","original":"./run-store.js"}],"format":"esm"},"src/control-plane/daemon.ts":{"bytes":4922,"imports":[{"path":"crypto","kind":"import-statement","external":true}],"format":"esm"},"src/control-plane/workflow.ts":{"bytes":10140,"imports":[{"path":"crypto","kind":"import-statement","external":true},{"path":"fs","kind":"import-statement","external":true},{"path":"os","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true}],"format":"esm"},"src/control-plane/monitor.ts":{"bytes":4008,"imports":[{"path":"crypto","kind":"import-statement","external":true}],"format":"esm"},"src/control-plane/usage.ts":{"bytes":5038,"imports":[{"path":"crypto","kind":"import-statement","external":true},{"path":"fs","kind":"import-statement","external":true},{"path":"os","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true}],"format":"esm"},"src/control-plane/index.ts":{"bytes":272,"imports":[{"path":"src/control-plane/types.ts","kind":"import-statement","original":"./types.js"},{"path":"src/control-plane/run-store.ts","kind":"import-statement","original":"./run-store.js"},{"path":"src/control-plane/event-store.ts","kind":"import-statement","original":"./event-store.js"},{"path":"src/control-plane/controller.ts","kind":"import-statement","original":"./controller.js"},{"path":"src/control-plane/daemon.ts","kind":"import-statement","original":"./daemon.js"},{"path":"src/control-plane/workflow.ts","kind":"import-statement","original":"./workflow.js"},{"path":"src/control-plane/monitor.ts","kind":"import-statement","original":"./monitor.js"},{"path":"src/control-plane/usage.ts","kind":"import-statement","original":"./usage.js"},{"path":"src/control-plane/lock.ts","kind":"import-statement","original":"./lock.js"}],"format":"esm"}},"outputs":{"dist/control-plane/index.js":{"imports":[{"path":"crypto","kind":"import-statement","external":true},{"path":"fs","kind":"import-statement","external":true},{"path":"os","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"fs","kind":"import-statement","external":true},{"path":"os","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"crypto","kind":"import-statement","external":true},{"path":"fs","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"crypto","kind":"import-statement","external":true},{"path":"crypto","kind":"import-statement","external":true},{"path":"fs","kind":"import-statement","external":true},{"path":"os","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true},{"path":"crypto","kind":"import-statement","external":true},{"path":"crypto","kind":"import-statement","external":true},{"path":"fs","kind":"import-statement","external":true},{"path":"os","kind":"import-statement","external":true},{"path":"path","kind":"import-statement","external":true}],"exports":["AGENT_DAEMON_PROTOCOL_VERSION","AgentDaemonClient","AgentDaemonServer","AgentEventStore","AgentRunController","AgentRunStore","MonitorManager","MonitorStopTool","MonitorTool","OtelExporter","UsageAccumulator","UsageLedger","WorkflowPlanner","WorkflowRuntime","WorkflowStore","acquireControlPlaneLock","createAgentRunId","getAgentRunDir","getAgentRunStoreDir"],"entryPoint":"src/control-plane/index.ts","inputs":{"src/control-plane/index.ts":{"bytesInOutput":0},"src/control-plane/run-store.ts":{"bytesInOutput":5353},"src/version.ts":{"bytesInOutput":14},"src/control-plane/lock.ts":{"bytesInOutput":1026},"src/control-plane/event-store.ts":{"bytesInOutput":1042},"src/control-plane/controller.ts":{"bytesInOutput":1686},"src/control-plane/daemon.ts":{"bytesInOutput":1936},"src/control-plane/workflow.ts":{"bytesInOutput":4740},"src/control-plane/monitor.ts":{"bytesInOutput":1815},"src/control-plane/usage.ts":{"bytesInOutput":2263}},"bytes":20309}}}