@basou/core 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2909 @@
1
+ import { z } from 'zod';
2
+ import { ChildProcess } from 'node:child_process';
3
+
4
+ /**
5
+ * Allowed ID type prefixes for Basou entities.
6
+ *
7
+ * Frozen at runtime so that mutating the exported array cannot diverge from
8
+ * the validation set used internally. The single source of truth for both
9
+ * the `IdPrefix` type and runtime prefix checks.
10
+ */
11
+ declare const ID_PREFIXES: readonly ["ws", "task", "ses", "evt", "appr", "decision"];
12
+ /**
13
+ * Type prefix used for Basou entity IDs.
14
+ * Format: `<prefix>_<26-char ULID>`, e.g. `ws_01HXABCDEF1234567890ABCDEF`.
15
+ */
16
+ type IdPrefix = (typeof ID_PREFIXES)[number];
17
+ /**
18
+ * A Basou entity ID as a template literal type.
19
+ *
20
+ * `PrefixedId<"ses">` narrows to ``ses_${string}`` so a session schema can
21
+ * preserve the prefix in its inferred type beyond runtime validation.
22
+ */
23
+ type PrefixedId<P extends IdPrefix = IdPrefix> = `${P}_${string}`;
24
+ /**
25
+ * Generate a Crockford Base32 ULID.
26
+ *
27
+ * The result is a 26-character, lexicographically time-sortable identifier.
28
+ * Multiple calls within the same millisecond are strictly increasing for the
29
+ * lifetime of the current process.
30
+ *
31
+ * NOTE: `seedTime` is forwarded to the underlying monotonic factory and is
32
+ * NOT a deterministic seed: repeated calls with the same `seedTime` still
33
+ * return strictly increasing values, because the factory increments its
34
+ * internal counter on each call.
35
+ *
36
+ * @param seedTime Optional millisecond timestamp passed to the monotonic
37
+ * factory. Useful for ordered generation in tests; not deterministic.
38
+ */
39
+ declare function ulid(seedTime?: number): string;
40
+ /**
41
+ * Generate a prefixed Basou ID, e.g. `ses_01HXABCDEF1234567890ABCDEF`.
42
+ *
43
+ * The return type preserves the prefix as a template literal type so that
44
+ * downstream zod schemas can narrow an `IdPrefix` parameter through the API.
45
+ *
46
+ * Throws if `prefix` is not one of {@link ID_PREFIXES}. The runtime guard
47
+ * defends against JavaScript callers and casted TypeScript that bypass the
48
+ * compile-time `IdPrefix` constraint.
49
+ */
50
+ declare function prefixedUlid<P extends IdPrefix>(prefix: P): PrefixedId<P>;
51
+ /**
52
+ * Check whether the given string is a valid prefixed Basou ID.
53
+ *
54
+ * Returns true only if the string has shape `<prefix>_<ULID>` where prefix is
55
+ * one of {@link ID_PREFIXES} and the trailing 26 characters form a valid
56
+ * Crockford Base32 ULID. Validation combines a strict shape regex (to enforce
57
+ * the 0-7 leading char and the I/L/O/U exclusion) with the npm `ulid`
58
+ * library's `isValid` for forward compatibility.
59
+ *
60
+ * NOTE: This validates the prefix is known. Schemas that require a specific
61
+ * prefix (e.g. only `ses_*` for a session ID) must add their own narrowing.
62
+ */
63
+ declare function isValidPrefixedId(value: string): boolean;
64
+
65
+ /**
66
+ * Schema version literal pinned to "0.1.0" for Basou v0.1.
67
+ * Reused across every entity schema so inferred types narrow to the literal.
68
+ */
69
+ declare const SchemaVersionSchema: z.ZodLiteral<"0.1.0">;
70
+ /**
71
+ * ISO 8601 timestamp with explicit timezone offset (e.g. `+09:00`).
72
+ *
73
+ * The spec samples include offsets, so the default zod `.datetime()` (which
74
+ * rejects offsets) is insufficient; `{ offset: true }` is required.
75
+ */
76
+ declare const IsoTimestampSchema: z.ZodString;
77
+ /** Workspace ID schema: validates `ws_<26-char ULID>`. */
78
+ declare const WorkspaceIdSchema: z.ZodString & z.ZodType<`ws_${string}`, string, z.core.$ZodTypeInternals<`ws_${string}`, string>>;
79
+ /** Task ID schema: validates `task_<26-char ULID>`. */
80
+ declare const TaskIdSchema: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
81
+ /** Session ID schema: validates `ses_<26-char ULID>`. */
82
+ declare const SessionIdSchema: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
83
+ /** Event ID schema: validates `evt_<26-char ULID>`. */
84
+ declare const EventIdSchema: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
85
+ /** Approval ID schema: validates `appr_<26-char ULID>`. */
86
+ declare const ApprovalIdSchema: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
87
+ /** Decision ID schema: validates `decision_<26-char ULID>`. */
88
+ declare const DecisionIdSchema: z.ZodString & z.ZodType<`decision_${string}`, string, z.core.$ZodTypeInternals<`decision_${string}`, string>>;
89
+ /**
90
+ * Risk level vocabulary fixed by the spec. Adapters MUST emit one of these
91
+ * four values; arbitrary strings are rejected at schema parse time.
92
+ */
93
+ declare const RiskLevelSchema: z.ZodEnum<{
94
+ low: "low";
95
+ medium: "medium";
96
+ high: "high";
97
+ critical: "critical";
98
+ }>;
99
+ /** Inferred runtime type for {@link RiskLevelSchema}. */
100
+ type RiskLevel = z.infer<typeof RiskLevelSchema>;
101
+ /**
102
+ * Source attribution for events (e.g. "claude-code-adapter",
103
+ * "git-capability", "terminal-recording", "local-cli", "human"). Free-form
104
+ * non-empty string in v0.1; a stricter enum may be introduced post-v0.1.
105
+ */
106
+ declare const EventSourceSchema: z.ZodString;
107
+
108
+ /**
109
+ * Schema for `.basou/manifest.yaml`. The minimal manifest carries
110
+ * schema_version, basou_version, workspace metadata, project info, enabled
111
+ * capabilities, approval policy, adapter config, and git policy. The
112
+ * `adapters."claude-code"` key uses a hyphen; downstream code accesses it
113
+ * via bracket notation.
114
+ */
115
+ declare const ManifestSchema: z.ZodObject<{
116
+ schema_version: z.ZodLiteral<"0.1.0">;
117
+ basou_version: z.ZodLiteral<"0.1.0">;
118
+ workspace: z.ZodObject<{
119
+ id: z.ZodString & z.ZodType<`ws_${string}`, string, z.core.$ZodTypeInternals<`ws_${string}`, string>>;
120
+ name: z.ZodString;
121
+ created_at: z.ZodString;
122
+ updated_at: z.ZodString;
123
+ }, z.core.$strip>;
124
+ project: z.ZodObject<{
125
+ name: z.ZodOptional<z.ZodString>;
126
+ description: z.ZodOptional<z.ZodString>;
127
+ repository_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
128
+ }, z.core.$strip>;
129
+ capabilities: z.ZodObject<{
130
+ enabled: z.ZodArray<z.ZodString>;
131
+ }, z.core.$strip>;
132
+ approval: z.ZodObject<{
133
+ required_for: z.ZodOptional<z.ZodArray<z.ZodString>>;
134
+ default_risk_level: z.ZodEnum<{
135
+ low: "low";
136
+ medium: "medium";
137
+ high: "high";
138
+ critical: "critical";
139
+ }>;
140
+ }, z.core.$strip>;
141
+ adapters: z.ZodObject<{
142
+ "claude-code": z.ZodObject<{
143
+ enabled: z.ZodBoolean;
144
+ config_path: z.ZodOptional<z.ZodString>;
145
+ }, z.core.$strip>;
146
+ }, z.core.$strip>;
147
+ git: z.ZodObject<{
148
+ events_log: z.ZodDefault<z.ZodEnum<{
149
+ ignore: "ignore";
150
+ commit: "commit";
151
+ }>>;
152
+ }, z.core.$strip>;
153
+ }, z.core.$strip>;
154
+ /** Inferred runtime type for {@link ManifestSchema}. */
155
+ type Manifest = z.infer<typeof ManifestSchema>;
156
+
157
+ /**
158
+ * Schema for `.basou/status.json` — a forward-incompat cache of the current
159
+ * workspace state.
160
+ *
161
+ * Each level uses `.strict()` so unknown keys are rejected rather than
162
+ * silently stripped. A v0.1 reader that encounters a future-shape
163
+ * `status.json` therefore fails parsing instead of returning a partially
164
+ * empty snapshot; callers regenerate by calling `buildStatusSnapshot` +
165
+ * `writeStatus` rather than trying to migrate.
166
+ */
167
+ declare const StatusSchema: z.ZodObject<{
168
+ schema_version: z.ZodLiteral<"0.1.0">;
169
+ generated_at: z.ZodString;
170
+ workspace: z.ZodObject<{
171
+ id: z.ZodString & z.ZodType<`ws_${string}`, string, z.core.$ZodTypeInternals<`ws_${string}`, string>>;
172
+ name: z.ZodString;
173
+ basou_version: z.ZodLiteral<"0.1.0">;
174
+ }, z.core.$strict>;
175
+ directories_present: z.ZodObject<{
176
+ sessions: z.ZodBoolean;
177
+ tasks: z.ZodBoolean;
178
+ approvals_pending: z.ZodBoolean;
179
+ approvals_resolved: z.ZodBoolean;
180
+ logs: z.ZodBoolean;
181
+ raw: z.ZodBoolean;
182
+ tmp: z.ZodBoolean;
183
+ }, z.core.$strict>;
184
+ }, z.core.$strict>;
185
+ /** Inferred runtime type for {@link StatusSchema}. */
186
+ type StatusSnapshot = z.infer<typeof StatusSchema>;
187
+
188
+ /** Session lifecycle states. */
189
+ declare const SessionStatusSchema: z.ZodEnum<{
190
+ initialized: "initialized";
191
+ running: "running";
192
+ waiting_approval: "waiting_approval";
193
+ completed: "completed";
194
+ failed: "failed";
195
+ interrupted: "interrupted";
196
+ imported: "imported";
197
+ archived: "archived";
198
+ }>;
199
+ /** Inferred runtime type for {@link SessionStatusSchema}. */
200
+ type SessionStatus = z.infer<typeof SessionStatusSchema>;
201
+ /** Source kind that produced the session. */
202
+ declare const SessionSourceKindSchema: z.ZodEnum<{
203
+ "claude-code-adapter": "claude-code-adapter";
204
+ human: "human";
205
+ import: "import";
206
+ terminal: "terminal";
207
+ }>;
208
+ /** Inferred runtime type for {@link SessionSourceKindSchema}. */
209
+ type SessionSourceKind = z.infer<typeof SessionSourceKindSchema>;
210
+ /**
211
+ * Schema for `.basou/sessions/<session_id>/session.yaml`. The minimal
212
+ * session document carries the actual fields nested under the outer
213
+ * `session:` key.
214
+ */
215
+ declare const SessionSchema: z.ZodObject<{
216
+ schema_version: z.ZodLiteral<"0.1.0">;
217
+ session: z.ZodObject<{
218
+ id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
219
+ label: z.ZodOptional<z.ZodString>;
220
+ task_id: z.ZodOptional<z.ZodNullable<z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>>>;
221
+ workspace_id: z.ZodString & z.ZodType<`ws_${string}`, string, z.core.$ZodTypeInternals<`ws_${string}`, string>>;
222
+ source: z.ZodObject<{
223
+ kind: z.ZodEnum<{
224
+ "claude-code-adapter": "claude-code-adapter";
225
+ human: "human";
226
+ import: "import";
227
+ terminal: "terminal";
228
+ }>;
229
+ version: z.ZodLiteral<"0.1.0">;
230
+ }, z.core.$strip>;
231
+ started_at: z.ZodString;
232
+ ended_at: z.ZodOptional<z.ZodString>;
233
+ status: z.ZodEnum<{
234
+ initialized: "initialized";
235
+ running: "running";
236
+ waiting_approval: "waiting_approval";
237
+ completed: "completed";
238
+ failed: "failed";
239
+ interrupted: "interrupted";
240
+ imported: "imported";
241
+ archived: "archived";
242
+ }>;
243
+ working_directory: z.ZodString;
244
+ invocation: z.ZodObject<{
245
+ command: z.ZodString;
246
+ args: z.ZodDefault<z.ZodArray<z.ZodString>>;
247
+ exit_code: z.ZodNullable<z.ZodNumber>;
248
+ }, z.core.$strip>;
249
+ related_files: z.ZodDefault<z.ZodArray<z.ZodString>>;
250
+ events_log: z.ZodDefault<z.ZodString>;
251
+ summary: z.ZodOptional<z.ZodNullable<z.ZodString>>;
252
+ }, z.core.$strip>;
253
+ }, z.core.$strip>;
254
+ /** Inferred runtime type for {@link SessionSchema}. */
255
+ type Session = z.infer<typeof SessionSchema>;
256
+
257
+ /**
258
+ * Task lifecycle states.
259
+ *
260
+ * The storage layer's `ALLOWED_TRANSITIONS` map (= source of truth in
261
+ * `tasks.ts`) is the authoritative graph; the comment below is a snapshot.
262
+ * `planned` reaches `done` / `cancelled` directly so tasks completed (or
263
+ * abandoned) outside an explicit in-progress phase can close in a single
264
+ * CLI call:
265
+ *
266
+ * planned → {in_progress | done | cancelled}
267
+ * in_progress → {done | cancelled}
268
+ * done / cancelled = terminal
269
+ *
270
+ * Self-edges are rejected so the audit trail stays monotonic.
271
+ */
272
+ declare const TaskStatusSchema: z.ZodEnum<{
273
+ planned: "planned";
274
+ in_progress: "in_progress";
275
+ done: "done";
276
+ cancelled: "cancelled";
277
+ }>;
278
+ /** Inferred runtime type for {@link TaskStatusSchema}. */
279
+ type TaskStatus = z.infer<typeof TaskStatusSchema>;
280
+ /**
281
+ * Schema for the YAML front matter of `.basou/tasks/<task_id>.md`.
282
+ *
283
+ * The markdown body after the front matter is intentionally NOT modelled
284
+ * here — it is free-form user-edited content. The storage layer splits
285
+ * the file into `task` (this schema) and `body` (the trailing string).
286
+ */
287
+ declare const TaskSchema: z.ZodObject<{
288
+ schema_version: z.ZodLiteral<"0.1.0">;
289
+ task: z.ZodObject<{
290
+ id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
291
+ title: z.ZodString;
292
+ label: z.ZodOptional<z.ZodString>;
293
+ status: z.ZodEnum<{
294
+ planned: "planned";
295
+ in_progress: "in_progress";
296
+ done: "done";
297
+ cancelled: "cancelled";
298
+ }>;
299
+ created_at: z.ZodString;
300
+ updated_at: z.ZodString;
301
+ workspace_id: z.ZodString & z.ZodType<`ws_${string}`, string, z.core.$ZodTypeInternals<`ws_${string}`, string>>;
302
+ created_in_session: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
303
+ linked_sessions: z.ZodDefault<z.ZodArray<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
304
+ }, z.core.$strip>;
305
+ }, z.core.$strip>;
306
+ /** Inferred runtime type for {@link TaskSchema}. */
307
+ type Task = z.infer<typeof TaskSchema>;
308
+
309
+ /**
310
+ * Lifecycle states of a Basou approval. The status is stored directly on
311
+ * the approval YAML (flat shape) so that pending → resolved transitions
312
+ * are atomic-move + in-place rewrites rather than schema-variant swaps.
313
+ */
314
+ declare const ApprovalStatusSchema: z.ZodEnum<{
315
+ pending: "pending";
316
+ approved: "approved";
317
+ rejected: "rejected";
318
+ expired: "expired";
319
+ }>;
320
+ /** Inferred runtime type for {@link ApprovalStatusSchema}. */
321
+ type ApprovalStatus = z.infer<typeof ApprovalStatusSchema>;
322
+ /**
323
+ * Schema for `.basou/approvals/{pending,resolved}/<approval_id>.yaml`.
324
+ *
325
+ * The schema is intentionally flat (one shape regardless of `status`) so
326
+ * that pending and resolved YAMLs share the same parser. Required vs.
327
+ * optional semantics by status (e.g. `rejection_reason` MUST be set when
328
+ * `status === "rejected"`) are enforced at the CLI orchestration layer
329
+ * rather than here, mirroring the approval event variants in
330
+ * `event.schema.ts`.
331
+ *
332
+ * The `action` field is `{ kind: string }` with `passthrough()` so that
333
+ * adapter-defined keys (e.g. `command`, `path`, `target_url`) survive the
334
+ * round-trip without being stripped — matching the approval_requested
335
+ * event variant.
336
+ */
337
+ declare const ApprovalSchema: z.ZodObject<{
338
+ schema_version: z.ZodLiteral<"0.1.0">;
339
+ id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
340
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
341
+ created_at: z.ZodString;
342
+ status: z.ZodEnum<{
343
+ pending: "pending";
344
+ approved: "approved";
345
+ rejected: "rejected";
346
+ expired: "expired";
347
+ }>;
348
+ risk_level: z.ZodEnum<{
349
+ low: "low";
350
+ medium: "medium";
351
+ high: "high";
352
+ critical: "critical";
353
+ }>;
354
+ action: z.ZodObject<{
355
+ kind: z.ZodString;
356
+ }, z.core.$loose>;
357
+ reason: z.ZodString;
358
+ expires_at: z.ZodDefault<z.ZodNullable<z.ZodString>>;
359
+ resolver: z.ZodDefault<z.ZodNullable<z.ZodString>>;
360
+ resolved_at: z.ZodDefault<z.ZodNullable<z.ZodString>>;
361
+ note: z.ZodDefault<z.ZodNullable<z.ZodString>>;
362
+ rejection_reason: z.ZodDefault<z.ZodNullable<z.ZodString>>;
363
+ }, z.core.$strip>;
364
+ /** Inferred runtime type for {@link ApprovalSchema}. */
365
+ type Approval = z.infer<typeof ApprovalSchema>;
366
+
367
+ declare const SessionStartedEventSchema: z.ZodObject<{
368
+ schema_version: z.ZodLiteral<"0.1.0">;
369
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
370
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
371
+ occurred_at: z.ZodString;
372
+ source: z.ZodString;
373
+ type: z.ZodLiteral<"session_started">;
374
+ }, z.core.$strip>;
375
+ declare const SessionEndedEventSchema: z.ZodObject<{
376
+ schema_version: z.ZodLiteral<"0.1.0">;
377
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
378
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
379
+ occurred_at: z.ZodString;
380
+ source: z.ZodString;
381
+ type: z.ZodLiteral<"session_ended">;
382
+ exit_code: z.ZodOptional<z.ZodNumber>;
383
+ }, z.core.$strip>;
384
+ declare const SessionStatusChangedEventSchema: z.ZodObject<{
385
+ schema_version: z.ZodLiteral<"0.1.0">;
386
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
387
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
388
+ occurred_at: z.ZodString;
389
+ source: z.ZodString;
390
+ type: z.ZodLiteral<"session_status_changed">;
391
+ from: z.ZodString;
392
+ to: z.ZodString;
393
+ }, z.core.$strip>;
394
+ declare const ApprovalRequestedEventSchema: z.ZodObject<{
395
+ schema_version: z.ZodLiteral<"0.1.0">;
396
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
397
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
398
+ occurred_at: z.ZodString;
399
+ source: z.ZodString;
400
+ type: z.ZodLiteral<"approval_requested">;
401
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
402
+ expires_at: z.ZodDefault<z.ZodNullable<z.ZodString>>;
403
+ risk_level: z.ZodEnum<{
404
+ low: "low";
405
+ medium: "medium";
406
+ high: "high";
407
+ critical: "critical";
408
+ }>;
409
+ action: z.ZodObject<{
410
+ kind: z.ZodString;
411
+ }, z.core.$loose>;
412
+ reason: z.ZodString;
413
+ status: z.ZodLiteral<"pending">;
414
+ }, z.core.$strip>;
415
+ declare const ApprovalApprovedEventSchema: z.ZodObject<{
416
+ schema_version: z.ZodLiteral<"0.1.0">;
417
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
418
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
419
+ occurred_at: z.ZodString;
420
+ source: z.ZodString;
421
+ type: z.ZodLiteral<"approval_approved">;
422
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
423
+ resolver: z.ZodOptional<z.ZodString>;
424
+ note: z.ZodOptional<z.ZodNullable<z.ZodString>>;
425
+ }, z.core.$strip>;
426
+ declare const ApprovalRejectedEventSchema: z.ZodObject<{
427
+ schema_version: z.ZodLiteral<"0.1.0">;
428
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
429
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
430
+ occurred_at: z.ZodString;
431
+ source: z.ZodString;
432
+ type: z.ZodLiteral<"approval_rejected">;
433
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
434
+ resolver: z.ZodOptional<z.ZodString>;
435
+ reason: z.ZodString;
436
+ }, z.core.$strip>;
437
+ declare const ApprovalExpiredEventSchema: z.ZodObject<{
438
+ schema_version: z.ZodLiteral<"0.1.0">;
439
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
440
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
441
+ occurred_at: z.ZodString;
442
+ source: z.ZodString;
443
+ type: z.ZodLiteral<"approval_expired">;
444
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
445
+ }, z.core.$strip>;
446
+ declare const CommandExecutedEventSchema: z.ZodObject<{
447
+ schema_version: z.ZodLiteral<"0.1.0">;
448
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
449
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
450
+ occurred_at: z.ZodString;
451
+ source: z.ZodString;
452
+ type: z.ZodLiteral<"command_executed">;
453
+ command: z.ZodString;
454
+ args: z.ZodArray<z.ZodString>;
455
+ cwd: z.ZodString;
456
+ exit_code: z.ZodNullable<z.ZodNumber>;
457
+ signal: z.ZodOptional<z.ZodNullable<z.ZodString>>;
458
+ received_signal: z.ZodOptional<z.ZodNullable<z.ZodString>>;
459
+ duration_ms: z.ZodNumber;
460
+ }, z.core.$strip>;
461
+ declare const GitSnapshotEventSchema: z.ZodObject<{
462
+ schema_version: z.ZodLiteral<"0.1.0">;
463
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
464
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
465
+ occurred_at: z.ZodString;
466
+ source: z.ZodString;
467
+ type: z.ZodLiteral<"git_snapshot">;
468
+ head: z.ZodString;
469
+ branch: z.ZodString;
470
+ dirty: z.ZodBoolean;
471
+ staged: z.ZodArray<z.ZodString>;
472
+ unstaged: z.ZodArray<z.ZodString>;
473
+ untracked: z.ZodArray<z.ZodString>;
474
+ ahead: z.ZodOptional<z.ZodNumber>;
475
+ behind: z.ZodOptional<z.ZodNumber>;
476
+ }, z.core.$strip>;
477
+ declare const FileChangedEventSchema: z.ZodObject<{
478
+ schema_version: z.ZodLiteral<"0.1.0">;
479
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
480
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
481
+ occurred_at: z.ZodString;
482
+ source: z.ZodString;
483
+ type: z.ZodLiteral<"file_changed">;
484
+ path: z.ZodString;
485
+ change_type: z.ZodEnum<{
486
+ added: "added";
487
+ modified: "modified";
488
+ deleted: "deleted";
489
+ renamed: "renamed";
490
+ }>;
491
+ old_path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
492
+ }, z.core.$strip>;
493
+ declare const DecisionRecordedEventSchema: z.ZodObject<{
494
+ schema_version: z.ZodLiteral<"0.1.0">;
495
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
496
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
497
+ occurred_at: z.ZodString;
498
+ source: z.ZodString;
499
+ type: z.ZodLiteral<"decision_recorded">;
500
+ decision_id: z.ZodString & z.ZodType<`decision_${string}`, string, z.core.$ZodTypeInternals<`decision_${string}`, string>>;
501
+ title: z.ZodString;
502
+ rationale: z.ZodOptional<z.ZodNullable<z.ZodString>>;
503
+ alternatives: z.ZodOptional<z.ZodArray<z.ZodString>>;
504
+ rejected_reason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
505
+ linked_events: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>>>;
506
+ linked_files: z.ZodOptional<z.ZodArray<z.ZodString>>;
507
+ }, z.core.$strip>;
508
+ declare const TaskCreatedEventSchema: z.ZodObject<{
509
+ schema_version: z.ZodLiteral<"0.1.0">;
510
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
511
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
512
+ occurred_at: z.ZodString;
513
+ source: z.ZodString;
514
+ type: z.ZodLiteral<"task_created">;
515
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
516
+ title: z.ZodString;
517
+ }, z.core.$strip>;
518
+ declare const TaskStatusChangedEventSchema: z.ZodObject<{
519
+ schema_version: z.ZodLiteral<"0.1.0">;
520
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
521
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
522
+ occurred_at: z.ZodString;
523
+ source: z.ZodString;
524
+ type: z.ZodLiteral<"task_status_changed">;
525
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
526
+ from: z.ZodString;
527
+ to: z.ZodString;
528
+ }, z.core.$strip>;
529
+ declare const TaskReconciledEventSchema: z.ZodObject<{
530
+ schema_version: z.ZodLiteral<"0.1.0">;
531
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
532
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
533
+ occurred_at: z.ZodString;
534
+ source: z.ZodString;
535
+ type: z.ZodLiteral<"task_reconciled">;
536
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
537
+ removed_created_in_session: z.ZodDefault<z.ZodNullable<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
538
+ created_in_session_replacement: z.ZodDefault<z.ZodNullable<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
539
+ removed_linked_sessions: z.ZodDefault<z.ZodArray<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
540
+ }, z.core.$strict>;
541
+ declare const TaskLinkageRefreshedEventSchema: z.ZodObject<{
542
+ schema_version: z.ZodLiteral<"0.1.0">;
543
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
544
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
545
+ occurred_at: z.ZodString;
546
+ source: z.ZodString;
547
+ type: z.ZodLiteral<"task_linkage_refreshed">;
548
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
549
+ added_linked_sessions: z.ZodDefault<z.ZodArray<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
550
+ removed_linked_sessions: z.ZodDefault<z.ZodArray<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
551
+ final_count: z.ZodOptional<z.ZodNumber>;
552
+ }, z.core.$strict>;
553
+ declare const TaskDeletedEventSchema: z.ZodObject<{
554
+ schema_version: z.ZodLiteral<"0.1.0">;
555
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
556
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
557
+ occurred_at: z.ZodString;
558
+ source: z.ZodString;
559
+ type: z.ZodLiteral<"task_deleted">;
560
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
561
+ title: z.ZodString;
562
+ }, z.core.$strict>;
563
+ declare const TaskArchivedEventSchema: z.ZodObject<{
564
+ schema_version: z.ZodLiteral<"0.1.0">;
565
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
566
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
567
+ occurred_at: z.ZodString;
568
+ source: z.ZodString;
569
+ type: z.ZodLiteral<"task_archived">;
570
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
571
+ title: z.ZodString;
572
+ }, z.core.$strict>;
573
+ declare const NoteAddedEventSchema: z.ZodObject<{
574
+ schema_version: z.ZodLiteral<"0.1.0">;
575
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
576
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
577
+ occurred_at: z.ZodString;
578
+ source: z.ZodString;
579
+ type: z.ZodLiteral<"note_added">;
580
+ body: z.ZodString;
581
+ }, z.core.$strip>;
582
+ declare const AdapterOutputEventSchema: z.ZodObject<{
583
+ schema_version: z.ZodLiteral<"0.1.0">;
584
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
585
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
586
+ occurred_at: z.ZodString;
587
+ source: z.ZodString;
588
+ type: z.ZodLiteral<"adapter_output">;
589
+ stream: z.ZodEnum<{
590
+ stdout: "stdout";
591
+ stderr: "stderr";
592
+ }>;
593
+ summary: z.ZodString;
594
+ raw_ref: z.ZodString;
595
+ redacted: z.ZodOptional<z.ZodBoolean>;
596
+ }, z.core.$strict>;
597
+ /**
598
+ * Discriminated union of every Basou v0.1 event type. The `type` literal
599
+ * narrows TypeScript to the appropriate variant. The `adapter_output`
600
+ * variant is uniquely strict to bar raw adapter bodies.
601
+ */
602
+ declare const EventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
603
+ schema_version: z.ZodLiteral<"0.1.0">;
604
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
605
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
606
+ occurred_at: z.ZodString;
607
+ source: z.ZodString;
608
+ type: z.ZodLiteral<"session_started">;
609
+ }, z.core.$strip>, z.ZodObject<{
610
+ schema_version: z.ZodLiteral<"0.1.0">;
611
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
612
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
613
+ occurred_at: z.ZodString;
614
+ source: z.ZodString;
615
+ type: z.ZodLiteral<"session_ended">;
616
+ exit_code: z.ZodOptional<z.ZodNumber>;
617
+ }, z.core.$strip>, z.ZodObject<{
618
+ schema_version: z.ZodLiteral<"0.1.0">;
619
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
620
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
621
+ occurred_at: z.ZodString;
622
+ source: z.ZodString;
623
+ type: z.ZodLiteral<"session_status_changed">;
624
+ from: z.ZodString;
625
+ to: z.ZodString;
626
+ }, z.core.$strip>, z.ZodObject<{
627
+ schema_version: z.ZodLiteral<"0.1.0">;
628
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
629
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
630
+ occurred_at: z.ZodString;
631
+ source: z.ZodString;
632
+ type: z.ZodLiteral<"approval_requested">;
633
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
634
+ expires_at: z.ZodDefault<z.ZodNullable<z.ZodString>>;
635
+ risk_level: z.ZodEnum<{
636
+ low: "low";
637
+ medium: "medium";
638
+ high: "high";
639
+ critical: "critical";
640
+ }>;
641
+ action: z.ZodObject<{
642
+ kind: z.ZodString;
643
+ }, z.core.$loose>;
644
+ reason: z.ZodString;
645
+ status: z.ZodLiteral<"pending">;
646
+ }, z.core.$strip>, z.ZodObject<{
647
+ schema_version: z.ZodLiteral<"0.1.0">;
648
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
649
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
650
+ occurred_at: z.ZodString;
651
+ source: z.ZodString;
652
+ type: z.ZodLiteral<"approval_approved">;
653
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
654
+ resolver: z.ZodOptional<z.ZodString>;
655
+ note: z.ZodOptional<z.ZodNullable<z.ZodString>>;
656
+ }, z.core.$strip>, z.ZodObject<{
657
+ schema_version: z.ZodLiteral<"0.1.0">;
658
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
659
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
660
+ occurred_at: z.ZodString;
661
+ source: z.ZodString;
662
+ type: z.ZodLiteral<"approval_rejected">;
663
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
664
+ resolver: z.ZodOptional<z.ZodString>;
665
+ reason: z.ZodString;
666
+ }, z.core.$strip>, z.ZodObject<{
667
+ schema_version: z.ZodLiteral<"0.1.0">;
668
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
669
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
670
+ occurred_at: z.ZodString;
671
+ source: z.ZodString;
672
+ type: z.ZodLiteral<"approval_expired">;
673
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
674
+ }, z.core.$strip>, z.ZodObject<{
675
+ schema_version: z.ZodLiteral<"0.1.0">;
676
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
677
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
678
+ occurred_at: z.ZodString;
679
+ source: z.ZodString;
680
+ type: z.ZodLiteral<"command_executed">;
681
+ command: z.ZodString;
682
+ args: z.ZodArray<z.ZodString>;
683
+ cwd: z.ZodString;
684
+ exit_code: z.ZodNullable<z.ZodNumber>;
685
+ signal: z.ZodOptional<z.ZodNullable<z.ZodString>>;
686
+ received_signal: z.ZodOptional<z.ZodNullable<z.ZodString>>;
687
+ duration_ms: z.ZodNumber;
688
+ }, z.core.$strip>, z.ZodObject<{
689
+ schema_version: z.ZodLiteral<"0.1.0">;
690
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
691
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
692
+ occurred_at: z.ZodString;
693
+ source: z.ZodString;
694
+ type: z.ZodLiteral<"git_snapshot">;
695
+ head: z.ZodString;
696
+ branch: z.ZodString;
697
+ dirty: z.ZodBoolean;
698
+ staged: z.ZodArray<z.ZodString>;
699
+ unstaged: z.ZodArray<z.ZodString>;
700
+ untracked: z.ZodArray<z.ZodString>;
701
+ ahead: z.ZodOptional<z.ZodNumber>;
702
+ behind: z.ZodOptional<z.ZodNumber>;
703
+ }, z.core.$strip>, z.ZodObject<{
704
+ schema_version: z.ZodLiteral<"0.1.0">;
705
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
706
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
707
+ occurred_at: z.ZodString;
708
+ source: z.ZodString;
709
+ type: z.ZodLiteral<"file_changed">;
710
+ path: z.ZodString;
711
+ change_type: z.ZodEnum<{
712
+ added: "added";
713
+ modified: "modified";
714
+ deleted: "deleted";
715
+ renamed: "renamed";
716
+ }>;
717
+ old_path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
718
+ }, z.core.$strip>, z.ZodObject<{
719
+ schema_version: z.ZodLiteral<"0.1.0">;
720
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
721
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
722
+ occurred_at: z.ZodString;
723
+ source: z.ZodString;
724
+ type: z.ZodLiteral<"decision_recorded">;
725
+ decision_id: z.ZodString & z.ZodType<`decision_${string}`, string, z.core.$ZodTypeInternals<`decision_${string}`, string>>;
726
+ title: z.ZodString;
727
+ rationale: z.ZodOptional<z.ZodNullable<z.ZodString>>;
728
+ alternatives: z.ZodOptional<z.ZodArray<z.ZodString>>;
729
+ rejected_reason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
730
+ linked_events: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>>>;
731
+ linked_files: z.ZodOptional<z.ZodArray<z.ZodString>>;
732
+ }, z.core.$strip>, z.ZodObject<{
733
+ schema_version: z.ZodLiteral<"0.1.0">;
734
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
735
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
736
+ occurred_at: z.ZodString;
737
+ source: z.ZodString;
738
+ type: z.ZodLiteral<"task_created">;
739
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
740
+ title: z.ZodString;
741
+ }, z.core.$strip>, z.ZodObject<{
742
+ schema_version: z.ZodLiteral<"0.1.0">;
743
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
744
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
745
+ occurred_at: z.ZodString;
746
+ source: z.ZodString;
747
+ type: z.ZodLiteral<"task_status_changed">;
748
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
749
+ from: z.ZodString;
750
+ to: z.ZodString;
751
+ }, z.core.$strip>, z.ZodObject<{
752
+ schema_version: z.ZodLiteral<"0.1.0">;
753
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
754
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
755
+ occurred_at: z.ZodString;
756
+ source: z.ZodString;
757
+ type: z.ZodLiteral<"task_reconciled">;
758
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
759
+ removed_created_in_session: z.ZodDefault<z.ZodNullable<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
760
+ created_in_session_replacement: z.ZodDefault<z.ZodNullable<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
761
+ removed_linked_sessions: z.ZodDefault<z.ZodArray<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
762
+ }, z.core.$strict>, z.ZodObject<{
763
+ schema_version: z.ZodLiteral<"0.1.0">;
764
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
765
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
766
+ occurred_at: z.ZodString;
767
+ source: z.ZodString;
768
+ type: z.ZodLiteral<"task_linkage_refreshed">;
769
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
770
+ added_linked_sessions: z.ZodDefault<z.ZodArray<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
771
+ removed_linked_sessions: z.ZodDefault<z.ZodArray<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
772
+ final_count: z.ZodOptional<z.ZodNumber>;
773
+ }, z.core.$strict>, z.ZodObject<{
774
+ schema_version: z.ZodLiteral<"0.1.0">;
775
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
776
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
777
+ occurred_at: z.ZodString;
778
+ source: z.ZodString;
779
+ type: z.ZodLiteral<"task_deleted">;
780
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
781
+ title: z.ZodString;
782
+ }, z.core.$strict>, z.ZodObject<{
783
+ schema_version: z.ZodLiteral<"0.1.0">;
784
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
785
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
786
+ occurred_at: z.ZodString;
787
+ source: z.ZodString;
788
+ type: z.ZodLiteral<"task_archived">;
789
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
790
+ title: z.ZodString;
791
+ }, z.core.$strict>, z.ZodObject<{
792
+ schema_version: z.ZodLiteral<"0.1.0">;
793
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
794
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
795
+ occurred_at: z.ZodString;
796
+ source: z.ZodString;
797
+ type: z.ZodLiteral<"note_added">;
798
+ body: z.ZodString;
799
+ }, z.core.$strip>, z.ZodObject<{
800
+ schema_version: z.ZodLiteral<"0.1.0">;
801
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
802
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
803
+ occurred_at: z.ZodString;
804
+ source: z.ZodString;
805
+ type: z.ZodLiteral<"adapter_output">;
806
+ stream: z.ZodEnum<{
807
+ stdout: "stdout";
808
+ stderr: "stderr";
809
+ }>;
810
+ summary: z.ZodString;
811
+ raw_ref: z.ZodString;
812
+ redacted: z.ZodOptional<z.ZodBoolean>;
813
+ }, z.core.$strict>], "type">;
814
+ /** Inferred runtime type for any Basou event. */
815
+ type Event = z.infer<typeof EventSchema>;
816
+ /** Narrowed runtime type for the `session_started` event variant. */
817
+ type SessionStartedEvent = z.infer<typeof SessionStartedEventSchema>;
818
+ /** Narrowed runtime type for the `session_ended` event variant. */
819
+ type SessionEndedEvent = z.infer<typeof SessionEndedEventSchema>;
820
+ /** Narrowed runtime type for the `session_status_changed` event variant. */
821
+ type SessionStatusChangedEvent = z.infer<typeof SessionStatusChangedEventSchema>;
822
+ /** Narrowed runtime type for the `approval_requested` event variant. */
823
+ type ApprovalRequestedEvent = z.infer<typeof ApprovalRequestedEventSchema>;
824
+ /** Narrowed runtime type for the `approval_approved` event variant. */
825
+ type ApprovalApprovedEvent = z.infer<typeof ApprovalApprovedEventSchema>;
826
+ /** Narrowed runtime type for the `approval_rejected` event variant. */
827
+ type ApprovalRejectedEvent = z.infer<typeof ApprovalRejectedEventSchema>;
828
+ /** Narrowed runtime type for the `approval_expired` event variant. */
829
+ type ApprovalExpiredEvent = z.infer<typeof ApprovalExpiredEventSchema>;
830
+ /** Narrowed runtime type for the `command_executed` event variant. */
831
+ type CommandExecutedEvent = z.infer<typeof CommandExecutedEventSchema>;
832
+ /** Narrowed runtime type for the `git_snapshot` event variant. */
833
+ type GitSnapshotEvent = z.infer<typeof GitSnapshotEventSchema>;
834
+ /** Narrowed runtime type for the `file_changed` event variant. */
835
+ type FileChangedEvent = z.infer<typeof FileChangedEventSchema>;
836
+ /** Narrowed runtime type for the `decision_recorded` event variant. */
837
+ type DecisionRecordedEvent = z.infer<typeof DecisionRecordedEventSchema>;
838
+ /** Narrowed runtime type for the `task_created` event variant. */
839
+ type TaskCreatedEvent = z.infer<typeof TaskCreatedEventSchema>;
840
+ /** Narrowed runtime type for the `task_status_changed` event variant. */
841
+ type TaskStatusChangedEvent = z.infer<typeof TaskStatusChangedEventSchema>;
842
+ /** Narrowed runtime type for the `task_reconciled` event variant (.strict()). */
843
+ type TaskReconciledEvent = z.infer<typeof TaskReconciledEventSchema>;
844
+ /** Narrowed runtime type for the `task_linkage_refreshed` event variant (.strict()). */
845
+ type TaskLinkageRefreshedEvent = z.infer<typeof TaskLinkageRefreshedEventSchema>;
846
+ /** Narrowed runtime type for the `task_deleted` event variant (.strict()). */
847
+ type TaskDeletedEvent = z.infer<typeof TaskDeletedEventSchema>;
848
+ /** Narrowed runtime type for the `task_archived` event variant (.strict()). */
849
+ type TaskArchivedEvent = z.infer<typeof TaskArchivedEventSchema>;
850
+ /** Narrowed runtime type for the `note_added` event variant. */
851
+ type NoteAddedEvent = z.infer<typeof NoteAddedEventSchema>;
852
+ /** Narrowed runtime type for the `adapter_output` event variant (.strict()). */
853
+ type AdapterOutputEvent = z.infer<typeof AdapterOutputEventSchema>;
854
+
855
+ declare const SessionInnerImportSchema: z.ZodObject<{
856
+ id: z.ZodOptional<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>;
857
+ label: z.ZodOptional<z.ZodString>;
858
+ task_id: z.ZodOptional<z.ZodNullable<z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>>>;
859
+ workspace_id: z.ZodString & z.ZodType<`ws_${string}`, string, z.core.$ZodTypeInternals<`ws_${string}`, string>>;
860
+ source: z.ZodObject<{
861
+ kind: z.ZodEnum<{
862
+ "claude-code-adapter": "claude-code-adapter";
863
+ human: "human";
864
+ import: "import";
865
+ terminal: "terminal";
866
+ }>;
867
+ version: z.ZodLiteral<"0.1.0">;
868
+ }, z.core.$strip>;
869
+ started_at: z.ZodString;
870
+ ended_at: z.ZodOptional<z.ZodString>;
871
+ status: z.ZodEnum<{
872
+ initialized: "initialized";
873
+ running: "running";
874
+ waiting_approval: "waiting_approval";
875
+ completed: "completed";
876
+ failed: "failed";
877
+ interrupted: "interrupted";
878
+ imported: "imported";
879
+ archived: "archived";
880
+ }>;
881
+ working_directory: z.ZodString;
882
+ invocation: z.ZodObject<{
883
+ command: z.ZodString;
884
+ args: z.ZodArray<z.ZodString>;
885
+ exit_code: z.ZodNullable<z.ZodNumber>;
886
+ }, z.core.$strip>;
887
+ related_files: z.ZodDefault<z.ZodArray<z.ZodString>>;
888
+ events_log: z.ZodOptional<z.ZodString>;
889
+ summary: z.ZodOptional<z.ZodNullable<z.ZodString>>;
890
+ }, z.core.$strict>;
891
+ /**
892
+ * Schema for the round-trip JSON payload accepted by `basou session import
893
+ * --format json`. The top level is `.strict()`; unknown keys at the outer
894
+ * envelope are rejected.
895
+ */
896
+ declare const SessionImportPayloadSchema: z.ZodObject<{
897
+ schema_version: z.ZodString;
898
+ session: z.ZodObject<{
899
+ id: z.ZodOptional<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>;
900
+ label: z.ZodOptional<z.ZodString>;
901
+ task_id: z.ZodOptional<z.ZodNullable<z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>>>;
902
+ workspace_id: z.ZodString & z.ZodType<`ws_${string}`, string, z.core.$ZodTypeInternals<`ws_${string}`, string>>;
903
+ source: z.ZodObject<{
904
+ kind: z.ZodEnum<{
905
+ "claude-code-adapter": "claude-code-adapter";
906
+ human: "human";
907
+ import: "import";
908
+ terminal: "terminal";
909
+ }>;
910
+ version: z.ZodLiteral<"0.1.0">;
911
+ }, z.core.$strip>;
912
+ started_at: z.ZodString;
913
+ ended_at: z.ZodOptional<z.ZodString>;
914
+ status: z.ZodEnum<{
915
+ initialized: "initialized";
916
+ running: "running";
917
+ waiting_approval: "waiting_approval";
918
+ completed: "completed";
919
+ failed: "failed";
920
+ interrupted: "interrupted";
921
+ imported: "imported";
922
+ archived: "archived";
923
+ }>;
924
+ working_directory: z.ZodString;
925
+ invocation: z.ZodObject<{
926
+ command: z.ZodString;
927
+ args: z.ZodArray<z.ZodString>;
928
+ exit_code: z.ZodNullable<z.ZodNumber>;
929
+ }, z.core.$strip>;
930
+ related_files: z.ZodDefault<z.ZodArray<z.ZodString>>;
931
+ events_log: z.ZodOptional<z.ZodString>;
932
+ summary: z.ZodOptional<z.ZodNullable<z.ZodString>>;
933
+ }, z.core.$strict>;
934
+ events: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
935
+ schema_version: z.ZodLiteral<"0.1.0">;
936
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
937
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
938
+ occurred_at: z.ZodString;
939
+ source: z.ZodString;
940
+ type: z.ZodLiteral<"session_started">;
941
+ }, z.core.$strip>, z.ZodObject<{
942
+ schema_version: z.ZodLiteral<"0.1.0">;
943
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
944
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
945
+ occurred_at: z.ZodString;
946
+ source: z.ZodString;
947
+ type: z.ZodLiteral<"session_ended">;
948
+ exit_code: z.ZodOptional<z.ZodNumber>;
949
+ }, z.core.$strip>, z.ZodObject<{
950
+ schema_version: z.ZodLiteral<"0.1.0">;
951
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
952
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
953
+ occurred_at: z.ZodString;
954
+ source: z.ZodString;
955
+ type: z.ZodLiteral<"session_status_changed">;
956
+ from: z.ZodString;
957
+ to: z.ZodString;
958
+ }, z.core.$strip>, z.ZodObject<{
959
+ schema_version: z.ZodLiteral<"0.1.0">;
960
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
961
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
962
+ occurred_at: z.ZodString;
963
+ source: z.ZodString;
964
+ type: z.ZodLiteral<"approval_requested">;
965
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
966
+ expires_at: z.ZodDefault<z.ZodNullable<z.ZodString>>;
967
+ risk_level: z.ZodEnum<{
968
+ low: "low";
969
+ medium: "medium";
970
+ high: "high";
971
+ critical: "critical";
972
+ }>;
973
+ action: z.ZodObject<{
974
+ kind: z.ZodString;
975
+ }, z.core.$loose>;
976
+ reason: z.ZodString;
977
+ status: z.ZodLiteral<"pending">;
978
+ }, z.core.$strip>, z.ZodObject<{
979
+ schema_version: z.ZodLiteral<"0.1.0">;
980
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
981
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
982
+ occurred_at: z.ZodString;
983
+ source: z.ZodString;
984
+ type: z.ZodLiteral<"approval_approved">;
985
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
986
+ resolver: z.ZodOptional<z.ZodString>;
987
+ note: z.ZodOptional<z.ZodNullable<z.ZodString>>;
988
+ }, z.core.$strip>, z.ZodObject<{
989
+ schema_version: z.ZodLiteral<"0.1.0">;
990
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
991
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
992
+ occurred_at: z.ZodString;
993
+ source: z.ZodString;
994
+ type: z.ZodLiteral<"approval_rejected">;
995
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
996
+ resolver: z.ZodOptional<z.ZodString>;
997
+ reason: z.ZodString;
998
+ }, z.core.$strip>, z.ZodObject<{
999
+ schema_version: z.ZodLiteral<"0.1.0">;
1000
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1001
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1002
+ occurred_at: z.ZodString;
1003
+ source: z.ZodString;
1004
+ type: z.ZodLiteral<"approval_expired">;
1005
+ approval_id: z.ZodString & z.ZodType<`appr_${string}`, string, z.core.$ZodTypeInternals<`appr_${string}`, string>>;
1006
+ }, z.core.$strip>, z.ZodObject<{
1007
+ schema_version: z.ZodLiteral<"0.1.0">;
1008
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1009
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1010
+ occurred_at: z.ZodString;
1011
+ source: z.ZodString;
1012
+ type: z.ZodLiteral<"command_executed">;
1013
+ command: z.ZodString;
1014
+ args: z.ZodArray<z.ZodString>;
1015
+ cwd: z.ZodString;
1016
+ exit_code: z.ZodNullable<z.ZodNumber>;
1017
+ signal: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1018
+ received_signal: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1019
+ duration_ms: z.ZodNumber;
1020
+ }, z.core.$strip>, z.ZodObject<{
1021
+ schema_version: z.ZodLiteral<"0.1.0">;
1022
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1023
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1024
+ occurred_at: z.ZodString;
1025
+ source: z.ZodString;
1026
+ type: z.ZodLiteral<"git_snapshot">;
1027
+ head: z.ZodString;
1028
+ branch: z.ZodString;
1029
+ dirty: z.ZodBoolean;
1030
+ staged: z.ZodArray<z.ZodString>;
1031
+ unstaged: z.ZodArray<z.ZodString>;
1032
+ untracked: z.ZodArray<z.ZodString>;
1033
+ ahead: z.ZodOptional<z.ZodNumber>;
1034
+ behind: z.ZodOptional<z.ZodNumber>;
1035
+ }, z.core.$strip>, z.ZodObject<{
1036
+ schema_version: z.ZodLiteral<"0.1.0">;
1037
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1038
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1039
+ occurred_at: z.ZodString;
1040
+ source: z.ZodString;
1041
+ type: z.ZodLiteral<"file_changed">;
1042
+ path: z.ZodString;
1043
+ change_type: z.ZodEnum<{
1044
+ added: "added";
1045
+ modified: "modified";
1046
+ deleted: "deleted";
1047
+ renamed: "renamed";
1048
+ }>;
1049
+ old_path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1050
+ }, z.core.$strip>, z.ZodObject<{
1051
+ schema_version: z.ZodLiteral<"0.1.0">;
1052
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1053
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1054
+ occurred_at: z.ZodString;
1055
+ source: z.ZodString;
1056
+ type: z.ZodLiteral<"decision_recorded">;
1057
+ decision_id: z.ZodString & z.ZodType<`decision_${string}`, string, z.core.$ZodTypeInternals<`decision_${string}`, string>>;
1058
+ title: z.ZodString;
1059
+ rationale: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1060
+ alternatives: z.ZodOptional<z.ZodArray<z.ZodString>>;
1061
+ rejected_reason: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1062
+ linked_events: z.ZodOptional<z.ZodArray<z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>>>;
1063
+ linked_files: z.ZodOptional<z.ZodArray<z.ZodString>>;
1064
+ }, z.core.$strip>, z.ZodObject<{
1065
+ schema_version: z.ZodLiteral<"0.1.0">;
1066
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1067
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1068
+ occurred_at: z.ZodString;
1069
+ source: z.ZodString;
1070
+ type: z.ZodLiteral<"task_created">;
1071
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
1072
+ title: z.ZodString;
1073
+ }, z.core.$strip>, z.ZodObject<{
1074
+ schema_version: z.ZodLiteral<"0.1.0">;
1075
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1076
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1077
+ occurred_at: z.ZodString;
1078
+ source: z.ZodString;
1079
+ type: z.ZodLiteral<"task_status_changed">;
1080
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
1081
+ from: z.ZodString;
1082
+ to: z.ZodString;
1083
+ }, z.core.$strip>, z.ZodObject<{
1084
+ schema_version: z.ZodLiteral<"0.1.0">;
1085
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1086
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1087
+ occurred_at: z.ZodString;
1088
+ source: z.ZodString;
1089
+ type: z.ZodLiteral<"task_reconciled">;
1090
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
1091
+ removed_created_in_session: z.ZodDefault<z.ZodNullable<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
1092
+ created_in_session_replacement: z.ZodDefault<z.ZodNullable<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
1093
+ removed_linked_sessions: z.ZodDefault<z.ZodArray<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
1094
+ }, z.core.$strict>, z.ZodObject<{
1095
+ schema_version: z.ZodLiteral<"0.1.0">;
1096
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1097
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1098
+ occurred_at: z.ZodString;
1099
+ source: z.ZodString;
1100
+ type: z.ZodLiteral<"task_linkage_refreshed">;
1101
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
1102
+ added_linked_sessions: z.ZodDefault<z.ZodArray<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
1103
+ removed_linked_sessions: z.ZodDefault<z.ZodArray<z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
1104
+ final_count: z.ZodOptional<z.ZodNumber>;
1105
+ }, z.core.$strict>, z.ZodObject<{
1106
+ schema_version: z.ZodLiteral<"0.1.0">;
1107
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1108
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1109
+ occurred_at: z.ZodString;
1110
+ source: z.ZodString;
1111
+ type: z.ZodLiteral<"task_deleted">;
1112
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
1113
+ title: z.ZodString;
1114
+ }, z.core.$strict>, z.ZodObject<{
1115
+ schema_version: z.ZodLiteral<"0.1.0">;
1116
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1117
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1118
+ occurred_at: z.ZodString;
1119
+ source: z.ZodString;
1120
+ type: z.ZodLiteral<"task_archived">;
1121
+ task_id: z.ZodString & z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
1122
+ title: z.ZodString;
1123
+ }, z.core.$strict>, z.ZodObject<{
1124
+ schema_version: z.ZodLiteral<"0.1.0">;
1125
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1126
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1127
+ occurred_at: z.ZodString;
1128
+ source: z.ZodString;
1129
+ type: z.ZodLiteral<"note_added">;
1130
+ body: z.ZodString;
1131
+ }, z.core.$strip>, z.ZodObject<{
1132
+ schema_version: z.ZodLiteral<"0.1.0">;
1133
+ id: z.ZodString & z.ZodType<`evt_${string}`, string, z.core.$ZodTypeInternals<`evt_${string}`, string>>;
1134
+ session_id: z.ZodString & z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>;
1135
+ occurred_at: z.ZodString;
1136
+ source: z.ZodString;
1137
+ type: z.ZodLiteral<"adapter_output">;
1138
+ stream: z.ZodEnum<{
1139
+ stdout: "stdout";
1140
+ stderr: "stderr";
1141
+ }>;
1142
+ summary: z.ZodString;
1143
+ raw_ref: z.ZodString;
1144
+ redacted: z.ZodOptional<z.ZodBoolean>;
1145
+ }, z.core.$strict>], "type">>;
1146
+ }, z.core.$strict>;
1147
+ /** Inferred runtime type for {@link SessionImportPayloadSchema}. */
1148
+ type SessionImportPayload = z.infer<typeof SessionImportPayloadSchema>;
1149
+ /** Inferred runtime type for {@link SessionInnerImportSchema}. */
1150
+ type SessionInnerImportInput = z.infer<typeof SessionInnerImportSchema>;
1151
+
1152
+ /**
1153
+ * Absolute paths to the standard `.basou/` directory layout, derived from a
1154
+ * given repository root. The shape mirrors the canonical `.basou/` tree
1155
+ * (see `docs/spec/workspace.md`). `root` is the `.basou/` directory itself
1156
+ * (i.e. `repositoryRoot/.basou`).
1157
+ *
1158
+ * `files` exposes the well-known top-level files inside `.basou/`. Each path
1159
+ * is computed but not created — they are written by their respective
1160
+ * subsystems (e.g. `writeManifest` for `manifest.yaml`).
1161
+ *
1162
+ * All fields are deeply readonly; consumers must not mutate the returned
1163
+ * object.
1164
+ */
1165
+ type BasouPaths = {
1166
+ readonly root: string;
1167
+ readonly sessions: string;
1168
+ readonly tasks: string;
1169
+ readonly approvals: {
1170
+ readonly pending: string;
1171
+ readonly resolved: string;
1172
+ };
1173
+ readonly locks: string;
1174
+ readonly logs: string;
1175
+ readonly raw: string;
1176
+ readonly tmp: string;
1177
+ readonly files: {
1178
+ readonly manifest: string;
1179
+ readonly status: string;
1180
+ readonly handoff: string;
1181
+ readonly decisions: string;
1182
+ };
1183
+ };
1184
+ /**
1185
+ * Compute absolute paths to the standard `.basou/` directory layout under
1186
+ * `repositoryRoot`. Pure: performs no I/O and is safe to call before the
1187
+ * directory exists.
1188
+ *
1189
+ * @param repositoryRoot Absolute path to the git repository root (the
1190
+ * parent directory of `.basou/`). Caller is responsible for resolving
1191
+ * `process.cwd()` or running `git rev-parse --show-toplevel` upstream;
1192
+ * this function does not validate that the path exists or is a git
1193
+ * repository.
1194
+ */
1195
+ declare function basouPaths(repositoryRoot: string): BasouPaths;
1196
+ /**
1197
+ * Create the standard `.basou/` directory layout under `repositoryRoot`.
1198
+ *
1199
+ * Idempotent: a no-op on an already-initialized layout. Returns the resolved
1200
+ * {@link BasouPaths} so callers can immediately use them.
1201
+ *
1202
+ * Throws if `repositoryRoot/.basou` (or any required subdirectory) exists
1203
+ * but is not a directory, or if filesystem permissions prevent creation.
1204
+ * All thrown error messages are pathless; the original native error is
1205
+ * attached as `cause` for diagnostics.
1206
+ *
1207
+ * @param repositoryRoot Absolute path to the git repository root. See
1208
+ * {@link basouPaths} for the contract on this parameter.
1209
+ */
1210
+ declare function ensureBasouDirectory(repositoryRoot: string): Promise<BasouPaths>;
1211
+
1212
+ /** Which side of `.basou/approvals/` an approval YAML lives on. */
1213
+ type ApprovalLocation = "pending" | "resolved";
1214
+ /** Result returned by {@link loadApproval}: the parsed approval and where it was found. */
1215
+ type LoadedApproval = {
1216
+ approval: Approval;
1217
+ location: ApprovalLocation;
1218
+ };
1219
+ /**
1220
+ * Locate and load the approval YAML for `approvalId`. Searches resolved
1221
+ * first so that a duplicated YAML (the crash-window scenario where both
1222
+ * pending and resolved exist for the same id) returns the resolved-side
1223
+ * record — matching the dedupe rule used by `approval list` and
1224
+ * `resolveApprovalId`. Returns null if neither directory contains the
1225
+ * YAML. Throws with a pathless message on read or schema-validation
1226
+ * failure.
1227
+ */
1228
+ declare function loadApproval(paths: BasouPaths, approvalId: string): Promise<LoadedApproval | null>;
1229
+ /**
1230
+ * Enumerate approval IDs by inspecting `<id>.yaml` filenames in pending
1231
+ * and resolved. ENOENT on either directory is treated as empty (e.g. a
1232
+ * workspace that has no resolved approvals yet). YAML parse and schema
1233
+ * validation are NOT performed; callers that need the parsed approval
1234
+ * should use {@link loadApproval} per ID.
1235
+ */
1236
+ declare function enumerateApprovals(paths: BasouPaths): Promise<{
1237
+ pending: string[];
1238
+ resolved: string[];
1239
+ }>;
1240
+ /**
1241
+ * Return true when an approval is in `pending` state and its `expires_at`
1242
+ * timestamp has elapsed. Used by `basou approval list` / `show` to surface
1243
+ * a `(expired)` label without mutating the YAML file. Approval expiry uses
1244
+ * lazy-evaluation semantics; actual `approval_expired` event firing is
1245
+ * deferred to a later step.
1246
+ *
1247
+ * `now` is taken as a parameter so a single CLI invocation can share one
1248
+ * "now" across every record it inspects (avoids boundary races where two
1249
+ * reads of `Date.now()` straddle an expiry instant).
1250
+ */
1251
+ declare function isLazyExpired(approval: Approval, now: Date): boolean;
1252
+
1253
+ /**
1254
+ * Read a YAML file as `unknown`. Caller MUST validate via a zod schema.
1255
+ *
1256
+ * Throws Error with pathless message and the original native error attached
1257
+ * as `cause` for I/O failures and YAML parse errors. All fs and parse exits
1258
+ * go through fixed messages so absolute paths cannot leak via `error.message`.
1259
+ */
1260
+ declare function readYamlFile(filePath: string): Promise<unknown>;
1261
+ /**
1262
+ * Write a value as YAML using {@link atomicReplace} for crash-resistant
1263
+ * atomicity. The shared helper handles the tmp-file + rename sequence,
1264
+ * `wx` collision guard, and best-effort tmp cleanup on failure. This
1265
+ * wrapper adds the YAML serialisation and the pathless error vocabulary.
1266
+ */
1267
+ declare function writeYamlFile(filePath: string, value: unknown): Promise<void>;
1268
+ /**
1269
+ * Atomically create a new YAML file. Like {@link writeYamlFile} but
1270
+ * delegates to {@link atomicCreate} so a pre-existing target fails with
1271
+ * EEXIST instead of being silently overwritten.
1272
+ *
1273
+ * Used by `basou approval approve` / `reject` to write the resolved-side
1274
+ * YAML, so a concurrent resolver cannot overwrite an already-resolved
1275
+ * approval.
1276
+ *
1277
+ * Throws `Error("Failed to write YAML file", { cause })` on failure; if
1278
+ * `cause.code === "EEXIST"` the caller can detect a target-exists race.
1279
+ */
1280
+ declare function linkYamlFile(filePath: string, value: unknown): Promise<void>;
1281
+ /**
1282
+ * Overwrite an existing YAML file atomically. Like {@link writeYamlFile}
1283
+ * but with a distinct pathless message label, used for files that
1284
+ * legitimately need in-place mutation (e.g. session.yaml's status /
1285
+ * ended_at lifecycle updates).
1286
+ */
1287
+ declare function overwriteYamlFile(filePath: string, value: unknown): Promise<void>;
1288
+
1289
+ /**
1290
+ * Inputs for {@link createManifest}. Optional fields drop out of the
1291
+ * resulting Manifest entirely (they are not emitted as `null`/`undefined`
1292
+ * in YAML); pass `null` for `repositoryUrl` to keep an explicit `null`.
1293
+ */
1294
+ type CreateManifestInput = {
1295
+ workspaceName: string;
1296
+ projectName?: string;
1297
+ projectDescription?: string;
1298
+ repositoryUrl?: string | null;
1299
+ /** Override for tests; defaults to `new Date()`. */
1300
+ now?: Date;
1301
+ /** Override for tests; defaults to a freshly generated `ws_<ULID>`. */
1302
+ workspaceId?: PrefixedId<"ws">;
1303
+ };
1304
+ /**
1305
+ * Build a fresh Manifest object that satisfies the manifest schema's
1306
+ * minimum shape. Performs no I/O. Returned object is parse-validated by
1307
+ * `ManifestSchema`.
1308
+ */
1309
+ declare function createManifest(input: CreateManifestInput): Manifest;
1310
+ /**
1311
+ * Write a Manifest to `paths.files.manifest`. Re-validates via
1312
+ * `ManifestSchema` before serialization.
1313
+ *
1314
+ * Refuses to overwrite an existing manifest unless `force: true`.
1315
+ */
1316
+ declare function writeManifest(paths: BasouPaths, manifest: Manifest, options?: {
1317
+ force?: boolean;
1318
+ }): Promise<void>;
1319
+ /**
1320
+ * Read and parse a Manifest from `paths.files.manifest`. Throws if the file
1321
+ * is missing or contents fail `ManifestSchema` validation.
1322
+ */
1323
+ declare function readManifest(paths: BasouPaths): Promise<Manifest>;
1324
+
1325
+ type AppendBasouGitignoreResult = {
1326
+ /** True if the block was appended (or the file was newly created). */
1327
+ readonly appended: boolean;
1328
+ };
1329
+ /**
1330
+ * Append Basou's default `.gitignore` block to `repositoryRoot/.gitignore`.
1331
+ *
1332
+ * The block contents are derived from the Basou v0.1 specification (the
1333
+ * standard ignore + commit recommendations). Callers must pass an absolute
1334
+ * path to a Git repository root.
1335
+ *
1336
+ * Behavior:
1337
+ * - If `.gitignore` does not exist, it is created with the Basou block.
1338
+ * - If a line starting with `# Basou - default ignore` is already present,
1339
+ * the file is left untouched and `appended: false` is returned
1340
+ * (idempotent).
1341
+ * - If `.gitignore` is a symlink, the link is followed and the target file
1342
+ * is updated. Symlinks are not rejected.
1343
+ *
1344
+ * On I/O failure throws Error with a pathless message
1345
+ * (`Failed to read .gitignore` / `Failed to write .gitignore`) and the
1346
+ * original native error attached as `cause`.
1347
+ */
1348
+ declare function appendBasouGitignore(repositoryRoot: string): Promise<AppendBasouGitignoreResult>;
1349
+
1350
+ /**
1351
+ * The two lock scopes basou uses. `task` guards the read-modify-write window
1352
+ * around a single `task.md`; `session` guards the events.jsonl append plus
1353
+ * surrounding `session.yaml` mutation for a single session. Two scopes use
1354
+ * different lockfile names so they never collide on disk.
1355
+ */
1356
+ type LockScope = "task" | "session";
1357
+ type LockHandle = {
1358
+ /**
1359
+ * Release the lock by unlinking the lockfile. Best-effort: any unlink error
1360
+ * is swallowed so a doubled release does not raise, and disk state never
1361
+ * holds a stranded lockfile after the caller's `finally` block.
1362
+ */
1363
+ release: () => Promise<void>;
1364
+ };
1365
+ /**
1366
+ * Acquire an advisory lock at `<paths.locks>/<scope>_<id>.lock` for the
1367
+ * lifetime of the returned handle. Lockfile body records the holder's pid
1368
+ * and acquire timestamp so a competitor can detect stale locks left by a
1369
+ * SIGINT'd CLI run and recover automatically.
1370
+ *
1371
+ * Acquisition strategy:
1372
+ * 1. {@link atomicCreate} the lockfile (POSIX link(2) + EEXIST).
1373
+ * 2. On EEXIST, probe the existing lockfile via {@link isStaleLock}.
1374
+ * - If stale (= holder pid is dead or lock is older than
1375
+ * {@link STALE_LOCK_MAX_AGE_MS}), `unlink` the stale file and retry
1376
+ * the atomic create once.
1377
+ * - If still EEXIST after the retry (= another competitor won the race),
1378
+ * throw `"Lock is held by another process"`.
1379
+ * - If the holder is alive, throw `"Lock is held by another process"`
1380
+ * without retrying.
1381
+ *
1382
+ * The caller MUST call `release()` (typically from a `finally` block); the
1383
+ * `process.exit()` path or a fatal crash relies on stale-lock detection on
1384
+ * the next acquire to recover.
1385
+ */
1386
+ declare function acquireLock(paths: BasouPaths, scope: LockScope, resourceId: string): Promise<LockHandle>;
1387
+
1388
+ /**
1389
+ * Walk the cause chain (up to `depth` levels) looking for an Error whose
1390
+ * errno-style `code` matches `code`. Returns true on the first match.
1391
+ * Resilient to wrapper depth changes so that ENOENT detection survives
1392
+ * future error-wrapping refactors.
1393
+ */
1394
+ declare function findErrorCode(error: unknown, code: string, depth?: number): boolean;
1395
+
1396
+ /**
1397
+ * Refuse to operate on `.basou` if it is a symlink or not a directory. This
1398
+ * prevents `writeStatus` from being tricked into writing `status.json`
1399
+ * outside the repository root via a swapped `.basou` symlink. Mirrors
1400
+ * `ensureBasouDirectory`'s lstat-based guard.
1401
+ *
1402
+ * If `.basou` is absent the underlying ENOENT is propagated (wrapped) so
1403
+ * callers can map it to "workspace not initialized" via `findErrorCode`.
1404
+ *
1405
+ * Note: this is a baseline safety net, not a TOCTOU fix — the directory
1406
+ * could still be replaced between this check and the subsequent write. The
1407
+ * goal is to detect already-swapped symlinks, not to race-proof the
1408
+ * filesystem.
1409
+ */
1410
+ declare function assertBasouRootSafe(rootPath: string): Promise<void>;
1411
+ /**
1412
+ * Build a StatusSnapshot from a manifest plus the path layout, observing
1413
+ * each subdirectory's presence via `lstat`. Read-only with respect to the
1414
+ * workspace state; writes nothing. The result is re-validated by
1415
+ * `StatusSchema.parse` before being returned.
1416
+ *
1417
+ * @param input.now Override for testing; defaults to `new Date()`.
1418
+ */
1419
+ declare function buildStatusSnapshot(input: {
1420
+ manifest: Manifest;
1421
+ paths: BasouPaths;
1422
+ now?: Date;
1423
+ }): Promise<StatusSnapshot>;
1424
+ /**
1425
+ * Atomically write a StatusSnapshot to `paths.files.status`.
1426
+ *
1427
+ * Re-validates via `StatusSchema.parse` before any file I/O, so an invalid
1428
+ * snapshot throws synchronously and never overwrites the existing
1429
+ * `status.json`. Delegates the tmp-file + rename pass to {@link atomicReplace}.
1430
+ *
1431
+ * **Precondition**: callers MUST invoke {@link assertBasouRootSafe} on
1432
+ * `paths.root` first to ensure `.basou` is a real directory and not a
1433
+ * swapped symlink. `writeStatus` does not redo this guard — it trusts the
1434
+ * caller — so a direct invocation without the guard could write
1435
+ * `status.json` outside the repository root.
1436
+ */
1437
+ declare function writeStatus(paths: BasouPaths, snapshot: StatusSnapshot): Promise<void>;
1438
+ /**
1439
+ * Read `.basou/status.json` for the current schema_version (0.1.0). This
1440
+ * is a cache reader only; cross-version migration is not supported here.
1441
+ * Older or newer status.json shapes will fail `StatusSchema.parse` —
1442
+ * callers regenerate by calling `buildStatusSnapshot` + `writeStatus`.
1443
+ */
1444
+ declare function readStatus(paths: BasouPaths): Promise<StatusSnapshot>;
1445
+
1446
+ /**
1447
+ * Recoverable warning surfaced via {@link ReplayOptions.onWarning}. The replay
1448
+ * generator never throws on these — it skips the offending line and continues.
1449
+ *
1450
+ * `partial_trailing_line` indicates the events.jsonl did not end with `\n` and
1451
+ * the unterminated tail parsed as a complete event. The line is dropped
1452
+ * instead of yielded so consumers cannot accidentally observe a
1453
+ * partially-written record.
1454
+ */
1455
+ type ReplayWarning = {
1456
+ kind: "partial_trailing_line";
1457
+ line: number;
1458
+ } | {
1459
+ kind: "malformed_json";
1460
+ line: number;
1461
+ cause: unknown;
1462
+ } | {
1463
+ kind: "schema_violation";
1464
+ line: number;
1465
+ cause: unknown;
1466
+ };
1467
+ type ReplayOptions = {
1468
+ /**
1469
+ * Hook to receive recoverable warnings (partial line / malformed JSON /
1470
+ * schema violation). When omitted, warnings are silently dropped — callers
1471
+ * that want to surface them (e.g. CLI orchestration) MUST provide this hook.
1472
+ */
1473
+ onWarning?: (warning: ReplayWarning) => void;
1474
+ };
1475
+ /**
1476
+ * Stream events from `<sessionDir>/events.jsonl` line by line.
1477
+ *
1478
+ * Behavior:
1479
+ * - ENOENT or empty file: yields nothing without warning.
1480
+ * - I/O error: throws `Error("Failed to read events.jsonl")` with the native
1481
+ * error attached as `cause`. The thrown message never embeds an absolute
1482
+ * path (pathless contract).
1483
+ * - Trailing partial line that parses as a valid event: dropped silently when
1484
+ * {@link ReplayOptions.onWarning} is omitted; otherwise reported as
1485
+ * `partial_trailing_line`. A trailing partial line that fails JSON parsing
1486
+ * is reported as `malformed_json` instead.
1487
+ * - Malformed JSON / schema violation: skipped, with the corresponding
1488
+ * warning when a hook is provided.
1489
+ *
1490
+ * Single-writer-per-session is assumed (see `event-writer.ts` JSDoc on
1491
+ * {@link appendEvent}). Concurrent writers may interleave lines beyond
1492
+ * `PIPE_BUF` and are not recovered here in v0.1.
1493
+ */
1494
+ declare function replayEvents(sessionDir: string, options?: ReplayOptions): AsyncGenerator<Event, void, void>;
1495
+ /**
1496
+ * Eager array helper: collect every event from {@link replayEvents} into
1497
+ * memory. Convenience for callers that need the full list in one structure
1498
+ * (e.g. `basou session show` rendering).
1499
+ */
1500
+ declare function readAllEvents(sessionDir: string, options?: ReplayOptions): Promise<Event[]>;
1501
+
1502
+ /**
1503
+ * Threshold above which a still-`running` session with no `session_ended`
1504
+ * event is flagged suspect.
1505
+ *
1506
+ * 24h: long enough that an active long-running session will not be flagged,
1507
+ * short enough that an abandoned process is surfaced within a working day.
1508
+ * Tunable via CLI option in a later step (continuation backlog #23).
1509
+ */
1510
+ declare const STUCK_THRESHOLD_MS: number;
1511
+ type SuspectReason = "events_say_ended_but_yaml_running" | "running_no_end_event";
1512
+ type SessionEntry = {
1513
+ sessionId: string;
1514
+ session: Session;
1515
+ suspect: boolean;
1516
+ suspectReason: SuspectReason | null;
1517
+ };
1518
+ /**
1519
+ * Per-session degradation reason emitted by {@link loadSessionEntries.onSkip}.
1520
+ *
1521
+ * - `session_yaml_missing` (ENOENT) and `session_yaml_invalid` (parse or schema
1522
+ * failure) both omit the entry from the result.
1523
+ * - `events_jsonl_unreadable` still pushes the entry with `suspect=false` so
1524
+ * the session row remains visible to the caller; only the suspect check is
1525
+ * degraded. Matches the existing CLI behaviour at
1526
+ * `packages/cli/src/commands/session.ts` (suspect-check stderr warning).
1527
+ */
1528
+ type SessionSkipReason = "session_yaml_missing" | "session_yaml_invalid" | "events_jsonl_unreadable";
1529
+ type LoadSessionEntriesOptions = {
1530
+ /**
1531
+ * Single `now` shared across every {@link classifySuspect} call so that
1532
+ * sessions classified back-to-back observe the same instant. Avoids
1533
+ * boundary races where a session at age ≈ 24h would flip between calls.
1534
+ */
1535
+ now: Date;
1536
+ onWarning?: (warning: ReplayWarning, sessionId: string) => void;
1537
+ onSkip?: (sessionId: string, reason: SessionSkipReason) => void;
1538
+ };
1539
+ /**
1540
+ * List session directory names under `paths.sessions`, ULID ascending.
1541
+ *
1542
+ * - Returns `[]` when the sessions directory does not exist (empty workspace
1543
+ * or pre-init state).
1544
+ * - Throws `Error("Failed to enumerate sessions", { cause })` on other I/O.
1545
+ * - Only directories are returned (`.gitkeep` and other files are filtered).
1546
+ *
1547
+ * Sort order is `Array.prototype.sort()` default (Unicode code-point
1548
+ * compare). ULIDs are Crockford base32 in uppercase, so the natural sort
1549
+ * is also chronological session-start order.
1550
+ */
1551
+ declare function enumerateSessionDirs(paths: BasouPaths): Promise<string[]>;
1552
+ /**
1553
+ * Read and validate `<paths.sessions>/<sessionId>/session.yaml`.
1554
+ *
1555
+ * - Re-throws the yaml-store fixed-message `"YAML file not found"` for
1556
+ * ENOENT so the caller can branch on it.
1557
+ * - Throws `Error("Failed to read session.yaml", { cause })` for parse
1558
+ * failures and schema violations (cause is either the YAML parser error
1559
+ * or the zod error).
1560
+ */
1561
+ declare function readSessionYaml(paths: BasouPaths, sessionId: string): Promise<Session>;
1562
+ /**
1563
+ * Classify a `running` session as suspect using one of two rules:
1564
+ *
1565
+ * - Rule A (`events_say_ended_but_yaml_running`): events.jsonl contains a
1566
+ * `session_ended` event but the session.yaml is still `running`. The
1567
+ * session ended cleanly in the event log but the YAML write was lost or
1568
+ * never reached.
1569
+ * - Rule B (`running_no_end_event`): no `session_ended` event and the last
1570
+ * event is older than {@link STUCK_THRESHOLD_MS}. The process likely
1571
+ * crashed or was killed.
1572
+ *
1573
+ * Sessions that are not `running` are never suspect.
1574
+ *
1575
+ * I/O failure on events.jsonl is re-thrown unwrapped so the caller can
1576
+ * degrade with a warning instead of treating the session as healthy. The
1577
+ * caller is also responsible for surfacing replay warnings via `onWarning`.
1578
+ */
1579
+ declare function classifySuspect(paths: BasouPaths, sessionId: string, session: Session, now: Date, onWarning?: (warning: ReplayWarning) => void): Promise<{
1580
+ suspect: boolean;
1581
+ suspectReason: SuspectReason | null;
1582
+ }>;
1583
+ /**
1584
+ * High-level helper that enumerates session dirs, reads each `session.yaml`,
1585
+ * and classifies suspect for `running` sessions in one pass.
1586
+ *
1587
+ * Per-session degradations are surfaced via `options.onSkip`:
1588
+ * - `session_yaml_missing` (ENOENT) and `session_yaml_invalid` (parse or
1589
+ * schema violation): the entry is omitted from the result.
1590
+ * - `events_jsonl_unreadable`: the entry is still pushed with `suspect=false`
1591
+ * so callers can render the session row plus a CLI-side warning.
1592
+ *
1593
+ * `options.now` is taken once and threaded into every {@link classifySuspect}
1594
+ * call so age comparisons are consistent across sessions.
1595
+ */
1596
+ declare function loadSessionEntries(paths: BasouPaths, options: LoadSessionEntriesOptions): Promise<SessionEntry[]>;
1597
+
1598
+ /** Marker line that begins the auto-generated region. */
1599
+ declare const GENERATED_START = "<!-- BASOU:GENERATED:START -->";
1600
+ /** Marker line that ends the auto-generated region. */
1601
+ declare const GENERATED_END = "<!-- BASOU:GENERATED:END -->";
1602
+ /**
1603
+ * Result of parsing a markdown body for the BASOU:GENERATED marker region.
1604
+ *
1605
+ * The spec mandates strict line-level matching (see
1606
+ * `docs/spec/generated-markdown.md#102-marker-convention`): a marker is
1607
+ * only recognized when an entire line is exactly the marker string.
1608
+ * Leading/trailing whitespace, comment compression, and BOM are treated as
1609
+ * legacy formats (`no_markers`) so that re-generation refuses to silently
1610
+ * overwrite a mismatched manual edit.
1611
+ */
1612
+ type MarkerSection = {
1613
+ kind: "ok";
1614
+ before: string;
1615
+ generated: string;
1616
+ after: string;
1617
+ } | {
1618
+ kind: "no_markers";
1619
+ } | {
1620
+ kind: "missing_start";
1621
+ } | {
1622
+ kind: "missing_end";
1623
+ } | {
1624
+ kind: "multiple_pairs";
1625
+ } | {
1626
+ kind: "wrong_order";
1627
+ };
1628
+ /**
1629
+ * Read a markdown file as UTF-8 text. Returns `null` when the file does not
1630
+ * exist; throws `Error("Failed to read markdown file", { cause })` for other
1631
+ * I/O failures (pathless contract — never embed the absolute path in the
1632
+ * thrown `message`).
1633
+ */
1634
+ declare function readMarkdownFile(filePath: string): Promise<string | null>;
1635
+ /**
1636
+ * Atomically write a markdown body via {@link atomicReplace}. The shared
1637
+ * helper handles the tmp-file + rename sequence, `wx` collision guard, and
1638
+ * best-effort tmp cleanup on failure.
1639
+ *
1640
+ * On any failure the original error is re-thrown as
1641
+ * `Error("Failed to write markdown file", { cause })` (pathless contract).
1642
+ */
1643
+ declare function writeMarkdownFile(filePath: string, body: string): Promise<void>;
1644
+ /**
1645
+ * Parse a markdown body and identify the BASOU:GENERATED marker region.
1646
+ *
1647
+ * Returns one of six `kind` discriminants:
1648
+ * - `ok`: exactly one START line followed by exactly one END line in the
1649
+ * correct order. `before` / `generated` / `after` slice the original
1650
+ * text by character offsets so CRLF / LF are preserved verbatim outside
1651
+ * the marker region.
1652
+ * - `no_markers`: both START and END absent (legacy file / fresh write).
1653
+ * - `missing_start` / `missing_end`: exactly one of the pair is present.
1654
+ * - `multiple_pairs`: more than one START or END line.
1655
+ * - `wrong_order`: END appears before START.
1656
+ *
1657
+ * Matching is strict: leading/trailing whitespace, BOM, and comment
1658
+ * compression (`<!--BASOU:...-->`) all bypass the marker and are treated
1659
+ * as legacy content.
1660
+ */
1661
+ declare function parseMarkers(content: string): MarkerSection;
1662
+ /**
1663
+ * Build the final markdown body by replacing the BASOU:GENERATED region.
1664
+ *
1665
+ * - `existing === null` (no file yet): return `<START>\n<generated>\n<END>\n`.
1666
+ * - existing parses to `ok`: replace the marked region and keep everything
1667
+ * before START and after END untouched (preserving manual additions).
1668
+ * - any other parse result: throw a pathless error referencing `fileLabel`.
1669
+ *
1670
+ * The caller passes `fileLabel` (e.g. `"handoff.md"` or `"decisions.md"`)
1671
+ * so the error message is informative without leaking an absolute path.
1672
+ */
1673
+ declare function renderWithMarkers(existing: string | null, generated: string, fileLabel: string): string;
1674
+
1675
+ /**
1676
+ * Options for {@link importSessionFromJson}. All fields are optional.
1677
+ *
1678
+ * - `labelOverride` / `taskIdOverride` come from the CLI `--label` / `--task`
1679
+ * flags and win over the corresponding fields on the input payload.
1680
+ * - `dryRun` skips disk writes entirely and returns a preview result.
1681
+ */
1682
+ type ImportSessionOptions = {
1683
+ labelOverride?: string;
1684
+ taskIdOverride?: string;
1685
+ dryRun?: boolean;
1686
+ };
1687
+ /**
1688
+ * Result of a successful import. `finalStatus` is always the literal
1689
+ * `"imported"` (per the import-session lifecycle policy); `finalSourceKind`
1690
+ * mirrors the input's `session.source.kind` so round-trip imports preserve
1691
+ * provenance.
1692
+ *
1693
+ * `pathSanitizeReport` summarises how many path-shaped fields the importer
1694
+ * rewrote on the way in: `related_files[]` entries plus a single boolean
1695
+ * for `working_directory`. The CLI wrapper surfaces this as a one-line
1696
+ * stderr warning when the total is non-zero so the operator sees that
1697
+ * machine-private prefixes were stripped.
1698
+ */
1699
+ type ImportSessionResult = {
1700
+ sessionId: PrefixedId<"ses">;
1701
+ eventCount: number;
1702
+ finalStatus: SessionStatus;
1703
+ finalSourceKind: SessionSourceKind;
1704
+ pathSanitizeReport: {
1705
+ relatedFiles: number;
1706
+ workingDirectoryRewritten: boolean;
1707
+ };
1708
+ };
1709
+ /**
1710
+ * Import a round-trip JSON payload into `.basou/sessions/<new>/`. The caller
1711
+ * MUST validate the payload against {@link SessionImportPayloadSchema} first
1712
+ * and gate the `schema_version === "0.1.0"` literal check externally; this
1713
+ * function trusts both invariants.
1714
+ *
1715
+ * On success a fresh session ID is minted and a complete
1716
+ * `session.yaml` + `events.jsonl` pair is written atomically. On any post-
1717
+ * mkdir failure the session directory is removed best-effort so partial
1718
+ * imports do not leave `session_yaml_missing` half-states behind.
1719
+ *
1720
+ * Throws `Error` with one of the fixed messages enumerated by the import contract
1721
+ * §"Error messages" table; the original native error is attached as `cause`
1722
+ * for `--verbose` rendering.
1723
+ */
1724
+ declare function importSessionFromJson(paths: BasouPaths, manifest: Manifest, payload: SessionImportPayload, options: ImportSessionOptions): Promise<ImportSessionResult>;
1725
+
1726
+ /**
1727
+ * Thrown when the ad-hoc session was fully written to disk (4 lifecycle
1728
+ * events + N target events plus the initial `session.yaml`) but the final
1729
+ * `session.yaml` update to status `completed` failed. The caller can read
1730
+ * `sessionId` / `targetEventIds` to emit a retry-duplicate-prevention
1731
+ * warning, since the target events themselves are already persisted in
1732
+ * `events.jsonl`.
1733
+ *
1734
+ * `targetEventIds` is an array because a single ad-hoc session may carry
1735
+ * multiple target events (e.g. `task new --status done` fires both
1736
+ * `task_created` and `task_status_changed`). Callers that need a single
1737
+ * anchor id should use `targetEventIds[0]`, which by convention is the
1738
+ * primary event for the operation.
1739
+ */
1740
+ declare class FailedToFinalizeError extends Error {
1741
+ readonly sessionId: PrefixedId<"ses">;
1742
+ readonly targetEventIds: ReadonlyArray<PrefixedId<"evt">>;
1743
+ constructor(sessionId: PrefixedId<"ses">, targetEventIds: ReadonlyArray<PrefixedId<"evt">>, cause: unknown);
1744
+ }
1745
+ type CreateAdHocSessionInput = {
1746
+ paths: BasouPaths;
1747
+ manifest: Manifest;
1748
+ /** Pre-built session label (caller is responsible for truncation). */
1749
+ label: string;
1750
+ /** ISO timestamp shared across the 5 lifecycle/target events. */
1751
+ occurredAt: string;
1752
+ sessionSource: SessionSourceKind;
1753
+ workingDirectory: string;
1754
+ invocation: {
1755
+ command: string;
1756
+ args: string[];
1757
+ };
1758
+ /**
1759
+ * Optional task id to link this ad-hoc session to. When provided, both the
1760
+ * initial and the final `session.yaml` writes embed `task_id` so the
1761
+ * single-session-to-single-task invariant (see
1762
+ * `docs/spec/workspace.md#21-confirmed-invariants`) holds for task-flavoured
1763
+ * ad-hoc paths (`basou task new` / `task status` without `--session`).
1764
+ * Defaults to `null` so existing callers (decision / note) are unchanged.
1765
+ */
1766
+ taskId?: PrefixedId<"task">;
1767
+ /**
1768
+ * Builds the variant-specific target events. Each builder receives the
1769
+ * freshly minted session id and a freshly minted event id (one per
1770
+ * builder) so callers can fill in cross-reference fields (`decision_id`,
1771
+ * `body`, ...) without owning ID generation.
1772
+ *
1773
+ * The most common case is a single-element array (`[builder]`) for the
1774
+ * one-target-event flows (`basou decision record`, `basou session note`,
1775
+ * `basou task new --status planned`, `basou task status`,
1776
+ * `basou task reconcile`). Two-element arrays are used by
1777
+ * `basou task new --status done|cancelled` to emit `task_created` plus
1778
+ * an immediate `task_status_changed` in the same atomic bulk write.
1779
+ *
1780
+ * Must be non-empty; an empty array is rejected at the start of
1781
+ * {@link createAdHocSessionWithEvent}.
1782
+ */
1783
+ targetEventBuilders: ReadonlyArray<(sessionId: PrefixedId<"ses">, eventId: PrefixedId<"evt">) => Event>;
1784
+ };
1785
+ type CreateAdHocSessionResult = {
1786
+ sessionId: PrefixedId<"ses">;
1787
+ /**
1788
+ * Target event IDs in the order their builders were supplied. Length
1789
+ * equals `input.targetEventBuilders.length`. Callers that conceptually
1790
+ * have a single anchor event should use `targetEventIds[0]`.
1791
+ */
1792
+ targetEventIds: PrefixedId<"evt">[];
1793
+ /**
1794
+ * Lifecycle event IDs in chronological order:
1795
+ * `[started, status→running, status→completed, ended]`.
1796
+ * Target event IDs are reported separately in {@link targetEventIds}.
1797
+ */
1798
+ lifecycleEventIds: PrefixedId<"evt">[];
1799
+ };
1800
+ /**
1801
+ * Atomically create a fresh ad-hoc session that produces one or more target
1802
+ * events then immediately closes itself. The session lifecycle
1803
+ * (`initialized → running → completed`, see
1804
+ * `docs/spec/terminal-and-import.md#62-transition-diagram`) is honored:
1805
+ * `4 + N` events are
1806
+ * written in one bulk atomic pass (where N = number of target builders) and
1807
+ * `session.yaml` is written twice (`initialized` → `completed`).
1808
+ *
1809
+ * The single-target case (N = 1) covers `basou decision record`,
1810
+ * `basou session note`, `basou task new --status planned|in_progress`,
1811
+ * `basou task status`, and `basou task reconcile`. The two-target case
1812
+ * (N = 2) covers `basou task new --status done|cancelled` which fires
1813
+ * `task_created` followed immediately by `task_status_changed (planned → terminal)`
1814
+ * so the audit trail captures the implicit transition.
1815
+ *
1816
+ * Failures during `mkdir`, the initial `session.yaml` write, or the bulk
1817
+ * `events.jsonl` write trigger a best-effort `rm -rf` of the session
1818
+ * directory so partial ad-hoc sessions do not pollute the workspace.
1819
+ *
1820
+ * A failure on the final `session.yaml` status update is fatal but the
1821
+ * session directory is NOT cleaned up — `events.jsonl` is consistent and
1822
+ * carries the full lifecycle trail, so callers can reconcile manually. The
1823
+ * thrown {@link FailedToFinalizeError} carries the `sessionId` and
1824
+ * `targetEventIds` so the CLI layer can warn the user not to re-run the
1825
+ * command and duplicate the target events.
1826
+ *
1827
+ * Direct (non-CLI) callers are self-defended by zod boundary parses on
1828
+ * `sessionSource` and the initial session record.
1829
+ */
1830
+ declare function createAdHocSessionWithEvent(input: CreateAdHocSessionInput): Promise<CreateAdHocSessionResult>;
1831
+ type AttachableStatus = "initialized" | "running" | "waiting_approval";
1832
+ type AppendEventToExistingInput = {
1833
+ paths: BasouPaths;
1834
+ /** Already resolved via `resolveSessionId`; parsed at boundary again. */
1835
+ sessionId: PrefixedId<"ses">;
1836
+ attachableStatuses?: ReadonlySet<AttachableStatus>;
1837
+ eventBuilder: (eventId: PrefixedId<"evt">) => Event;
1838
+ };
1839
+ type AppendEventToExistingResult = {
1840
+ eventId: PrefixedId<"evt">;
1841
+ sessionStatus: SessionStatus;
1842
+ };
1843
+ /**
1844
+ * Read `session.yaml`, verify the session is in an attachable state, and
1845
+ * append a single event to its `events.jsonl`. `session.yaml` is NOT modified
1846
+ * so the caller can safely append `decision_recorded` / `note_added` without
1847
+ * mutating `related_files`, `summary`, or the session status.
1848
+ *
1849
+ * Race note: the status check and the event append are not atomic.
1850
+ * Between them another writer (e.g. `basou run claude-code` ending its
1851
+ * session) can flip the YAML to `completed` and append `session_ended`.
1852
+ * v0.1 accepts this race; the `events_say_ended_but_yaml_running`-style
1853
+ * suspect rule surfaces the inconsistency. Per-session locking is
1854
+ * deferred to a v0.3+ follow-up.
1855
+ */
1856
+ declare function appendEventToExistingSession(input: AppendEventToExistingInput): Promise<AppendEventToExistingResult>;
1857
+
1858
+ type TaskDocument = {
1859
+ /** Parsed + zod-validated front matter. */
1860
+ task: Task;
1861
+ /** Raw markdown body after the closing front matter delimiter. */
1862
+ body: string;
1863
+ };
1864
+ /**
1865
+ * Read and validate `<paths.tasks>/<taskId>.md`. Returns the parsed front
1866
+ * matter (Task) plus the markdown body string. Error contract:
1867
+ *
1868
+ * - ENOENT → throw `"Task file not found"`.
1869
+ * - format violation → throw `"Invalid task file format"`.
1870
+ * - YAML parse / schema violation → throw `"Failed to read task file"`.
1871
+ * - any other I/O failure → throw `"Failed to read task file"` with cause.
1872
+ */
1873
+ declare function readTaskFile(paths: BasouPaths, taskId: string): Promise<TaskDocument>;
1874
+ type WriteTaskFileMode = "create" | "overwrite";
1875
+ /**
1876
+ * Atomically write `<paths.tasks>/<taskId>.md`.
1877
+ *
1878
+ * `mode: "create"` delegates to {@link atomicCreate} so a pre-existing file
1879
+ * fails fast with EEXIST → `"Task file already exists"`.
1880
+ * `mode: "overwrite"` delegates to {@link atomicReplace} and silently
1881
+ * replaces any prior file.
1882
+ *
1883
+ * The serialised body is structured as:
1884
+ *
1885
+ * ---\n
1886
+ * <yaml>\n
1887
+ * ---\n
1888
+ * \n
1889
+ * <body>\n (only when body is non-empty)
1890
+ */
1891
+ declare function writeTaskFile(paths: BasouPaths, taskId: string, doc: TaskDocument, options: {
1892
+ mode: WriteTaskFileMode;
1893
+ }): Promise<void>;
1894
+ /**
1895
+ * Enumerate task ids by listing `<paths.tasks>/`. Filenames that do not
1896
+ * match the `<task_id>.md` shape, or that decode to a non-conforming task
1897
+ * id (per `TaskIdSchema`), are silently skipped — they are surfaced via
1898
+ * the caller's `options.onSkip` hook in {@link loadTaskEntries} so list
1899
+ * commands can show a warning row.
1900
+ *
1901
+ * Returns ids in ULID-ascending order (filename sort matches ULID order).
1902
+ * Empty directory or ENOENT → `[]`. Other I/O failures throw
1903
+ * `"Failed to enumerate tasks"`.
1904
+ */
1905
+ declare function enumerateTaskIds(paths: BasouPaths): Promise<string[]>;
1906
+ /**
1907
+ * Enumerate task ids inside `<paths.tasks>/archive/`. Returns `[]` when the
1908
+ * archive directory does not exist (= no task has ever been archived).
1909
+ * Filtering / ordering rules mirror {@link enumerateTaskIds}.
1910
+ */
1911
+ declare function enumerateArchivedTaskIds(paths: BasouPaths): Promise<string[]>;
1912
+ /**
1913
+ * Read a task.md file looking in the main tasks directory first and falling
1914
+ * back to `<paths.tasks>/archive/` if the file is missing there. Returns the
1915
+ * parsed document plus a flag indicating whether the hit came from the
1916
+ * archive dir. Useful for `basou task show` which surfaces archived tasks
1917
+ * read-only without requiring the operator to opt in.
1918
+ *
1919
+ * Error contract matches {@link readTaskFile} — only the lookup location
1920
+ * differs.
1921
+ */
1922
+ declare function readTaskFileWithArchiveFallback(paths: BasouPaths, taskId: string): Promise<{
1923
+ doc: TaskDocument;
1924
+ archived: boolean;
1925
+ }>;
1926
+ type TaskSkipReason = "task_file_invalid" | "task_file_unreadable";
1927
+ type LoadTaskEntriesOptions = {
1928
+ onSkip?: (taskId: string, reason: TaskSkipReason) => void;
1929
+ };
1930
+ /**
1931
+ * Read every task.md under `<paths.tasks>/` and return the valid documents,
1932
+ * skipping malformed / unreadable files with an `onSkip` callback for each.
1933
+ *
1934
+ * Returned entries are sorted ascending by `task.created_at` (internal asc;
1935
+ * the CLI layer reverses for newest-first display).
1936
+ */
1937
+ declare function loadTaskEntries(paths: BasouPaths, options?: LoadTaskEntriesOptions): Promise<TaskDocument[]>;
1938
+ /**
1939
+ * Thrown when the task event (`task_created` / `task_status_changed`) was
1940
+ * fully persisted to events.jsonl but the accompanying `task.md` write
1941
+ * failed. The caller is responsible for surfacing a "do not rerun"
1942
+ * warning — re-running the same CLI invocation would duplicate the event
1943
+ * in events.jsonl.
1944
+ *
1945
+ * Reconciliation (= regenerating the missing task.md from events) is a
1946
+ * v0.2 follow-up (= `task reconcile` family).
1947
+ */
1948
+ /**
1949
+ * `phase` identifies which staged write failed after the event commit:
1950
+ * - `create`: task.md create write (ad-hoc or attach path)
1951
+ * - `overwrite`: task.md overwrite during a status change
1952
+ * - `link-session`: session.yaml `task_id` update during the attach path
1953
+ * (split out so CLI warnings describe the actual unsafe artefact
1954
+ * instead of always saying "task.md creation failed")
1955
+ * - `reconcile`: task.md overwrite during `basou task reconcile --write`
1956
+ * after the `task_reconciled` event was persisted
1957
+ * - `reconcile-finalize`: ad-hoc reconcile session finalize failed (=
1958
+ * `FailedToFinalizeError` caught and re-classified)
1959
+ * - `reconcile-concurrent`: task.md was modified between the pre-write
1960
+ * snapshot and the post-event re-read; the operator is told to re-run
1961
+ * reconcile rather than overwrite a stale snapshot
1962
+ */
1963
+ type TaskWriteAfterEventPhase = "create" | "overwrite" | "link-session" | "reconcile" | "reconcile-finalize" | "reconcile-concurrent" | "linkage-refresh" | "linkage-refresh-finalize" | "linkage-refresh-concurrent" | "delete" | "archive";
1964
+ declare class TaskWriteAfterEventError extends Error {
1965
+ readonly taskId: PrefixedId<"task">;
1966
+ readonly eventId: PrefixedId<"evt">;
1967
+ readonly sessionId: PrefixedId<"ses">;
1968
+ readonly phase: TaskWriteAfterEventPhase;
1969
+ constructor(args: {
1970
+ taskId: PrefixedId<"task">;
1971
+ eventId: PrefixedId<"evt">;
1972
+ sessionId: PrefixedId<"ses">;
1973
+ phase: TaskWriteAfterEventPhase;
1974
+ cause: unknown;
1975
+ });
1976
+ }
1977
+ type CreateAdHocTaskInput = {
1978
+ mode: "ad-hoc";
1979
+ paths: BasouPaths;
1980
+ manifest: Manifest;
1981
+ occurredAt: string;
1982
+ taskId: PrefixedId<"task">;
1983
+ title: string;
1984
+ label?: string;
1985
+ initialStatus: TaskStatus;
1986
+ description: string;
1987
+ workingDirectory: string;
1988
+ /**
1989
+ * Optional override for `task.md.updated_at` when `initialStatus` is a
1990
+ * terminal value (done / cancelled). Lets the operator backdate a
1991
+ * retroactively-recorded completed task so `task.md` reflects the actual
1992
+ * completion moment while `events.jsonl` keeps recording time. Ignored
1993
+ * for non-terminal statuses.
1994
+ */
1995
+ completedAt?: string;
1996
+ };
1997
+ type AttachTaskInput = {
1998
+ mode: "attach";
1999
+ paths: BasouPaths;
2000
+ occurredAt: string;
2001
+ sessionId: PrefixedId<"ses">;
2002
+ taskId: PrefixedId<"task">;
2003
+ title: string;
2004
+ label?: string;
2005
+ initialStatus: TaskStatus;
2006
+ description: string;
2007
+ attachableStatuses?: ReadonlySet<AttachableStatus>;
2008
+ /** See {@link CreateAdHocTaskInput.completedAt}. */
2009
+ completedAt?: string;
2010
+ };
2011
+ type CreateTaskInput = CreateAdHocTaskInput | AttachTaskInput;
2012
+ type CreateTaskResult = {
2013
+ taskId: PrefixedId<"task">;
2014
+ eventId: PrefixedId<"evt">;
2015
+ sessionId: PrefixedId<"ses">;
2016
+ sessionStatus: SessionStatus;
2017
+ };
2018
+ /**
2019
+ * Create a new task: fires a single `task_created` event and writes
2020
+ * `.basou/tasks/<taskId>.md` with status = `initialStatus`.
2021
+ *
2022
+ * Ad-hoc path: a fresh ad-hoc session is minted (5-event bulk write,
2023
+ * `task_created` as the target event, session.yaml.task_id pinned to the
2024
+ * new task).
2025
+ *
2026
+ * Attach path: the target session's `task_id` is validated against the
2027
+ * session ⇆ task anchor invariant (see
2028
+ * `docs/spec/workspace.md#21-confirmed-invariants`; null → updated to the
2029
+ * new task; existing X → rejected since X is already owned). If validation
2030
+ * passes, the event is appended to events.jsonl and session.yaml's
2031
+ * `task_id` is updated to the new task.
2032
+ *
2033
+ * Race window (v0.1 accepts): stage 2 writes the event, stage 3 writes
2034
+ * task.md. A failure on stage 3 leaves events.jsonl ahead of task.md;
2035
+ * {@link TaskWriteAfterEventError} surfaces this with a "do not rerun"
2036
+ * warning so the operator can reconcile manually until the v0.2 reconcile
2037
+ * flow arrives.
2038
+ */
2039
+ declare function createTaskWithEvent(input: CreateTaskInput): Promise<CreateTaskResult>;
2040
+ type UpdateAdHocTaskStatusInput = {
2041
+ mode: "ad-hoc";
2042
+ paths: BasouPaths;
2043
+ manifest: Manifest;
2044
+ occurredAt: string;
2045
+ taskId: PrefixedId<"task">;
2046
+ newStatus: TaskStatus;
2047
+ workingDirectory: string;
2048
+ };
2049
+ type AttachUpdateTaskStatusInput = {
2050
+ mode: "attach";
2051
+ paths: BasouPaths;
2052
+ occurredAt: string;
2053
+ sessionId: PrefixedId<"ses">;
2054
+ taskId: PrefixedId<"task">;
2055
+ newStatus: TaskStatus;
2056
+ attachableStatuses?: ReadonlySet<AttachableStatus>;
2057
+ };
2058
+ type UpdateTaskStatusInput = UpdateAdHocTaskStatusInput | AttachUpdateTaskStatusInput;
2059
+ type UpdateTaskStatusResult = {
2060
+ taskId: PrefixedId<"task">;
2061
+ eventId: PrefixedId<"evt">;
2062
+ sessionId: PrefixedId<"ses">;
2063
+ sessionStatus: SessionStatus;
2064
+ previousStatus: TaskStatus;
2065
+ newStatus: TaskStatus;
2066
+ };
2067
+ /**
2068
+ * Fire a `task_status_changed` event and overwrite the task.md front matter
2069
+ * with the new status / `updated_at` / appended-but-deduped `linked_sessions`.
2070
+ *
2071
+ * Validates the transition BEFORE any event write so a rejected transition
2072
+ * leaves events.jsonl untouched. The canonical edge set lives in
2073
+ * {@link ALLOWED_TRANSITIONS}; the current shape is:
2074
+ * planned → {in_progress, done, cancelled}
2075
+ * in_progress → {done, cancelled}
2076
+ * done / cancelled are terminal (= idempotent same-state is rejected too).
2077
+ */
2078
+ declare function updateTaskStatusWithEvent(input: UpdateTaskStatusInput): Promise<UpdateTaskStatusResult>;
2079
+ /**
2080
+ * Single-task audit result. Always returned by {@link reconcileTask} regardless
2081
+ * of mode: in dry-run the `clean` / `broken*` fields describe what would change
2082
+ * and `reconcileSession` is `null`; in write mode the same fields describe
2083
+ * what did change and `reconcileSession` carries the minted ad-hoc session +
2084
+ * `task_reconciled` event ids.
2085
+ *
2086
+ * Broken `linked_sessions[]` entries are deduplicated against the same session
2087
+ * id appearing more than once in the source task.md (hand-edit defence).
2088
+ */
2089
+ type ReconcileResult = {
2090
+ taskId: PrefixedId<"task">;
2091
+ clean: boolean;
2092
+ brokenCreatedInSession: PrefixedId<"ses"> | null;
2093
+ brokenLinkedSessions: PrefixedId<"ses">[];
2094
+ reconcileSession: {
2095
+ sessionId: PrefixedId<"ses">;
2096
+ eventId: PrefixedId<"evt">;
2097
+ } | null;
2098
+ };
2099
+ /**
2100
+ * Per-task failure record collected by {@link reconcileAllTasks}. The scan
2101
+ * keeps running on isolated failures so one bad task does not freeze the
2102
+ * batch; the CLI layer renders this list and exits 1 if any entry is present.
2103
+ *
2104
+ * `phase` is populated only for {@link TaskWriteAfterEventError}; for any
2105
+ * other error class it is `null` and the operator must use `--verbose` to
2106
+ * surface the cause chain.
2107
+ */
2108
+ type ReconcileFailure = {
2109
+ taskId: PrefixedId<"task">;
2110
+ errorClass: string;
2111
+ phase: TaskWriteAfterEventPhase | null;
2112
+ };
2113
+ /**
2114
+ * Batch audit result. Order follows `enumerateTaskIds(paths)` (ULID-ascending).
2115
+ * `scanned` is the number of readable task.md files processed (= excludes
2116
+ * malformed task.md from the count so an integrity-broken file does not
2117
+ * pad the total).
2118
+ */
2119
+ type ReconcileAllResult = {
2120
+ results: ReconcileResult[];
2121
+ failed: ReconcileFailure[];
2122
+ scanned: number;
2123
+ };
2124
+ type ReconcileTaskInput = {
2125
+ taskId: PrefixedId<"task">;
2126
+ occurredAt: string;
2127
+ workingDirectory: string;
2128
+ write: boolean;
2129
+ /**
2130
+ * Whether the caller invoked reconcile against a single task (`--task <id>`)
2131
+ * or as part of a full scan. The ad-hoc reconcile session records the form
2132
+ * on its `invocation.args` so audit trails distinguish targeted repairs
2133
+ * from sweeps:
2134
+ * - `"single"` -> `["--task", <taskId>, "--write"]`
2135
+ * - `"all"` -> `["--write"]` (= the operator typed no task id, so the
2136
+ * scan-wide intent is preserved instead of synthesising one per task)
2137
+ * Defaults to `"single"` so direct callers (tests, programmatic uses) keep
2138
+ * the targeted form without an explicit argument.
2139
+ */
2140
+ scope?: "single" | "all";
2141
+ /**
2142
+ * Test-only hook: the test runner uses this to mutate the task file
2143
+ * from outside the reconcile flow between the pre-write snapshot and
2144
+ * the post-event re-read, simulating a concurrent edit so the
2145
+ * `reconcile-concurrent` branch can be exercised deterministically.
2146
+ * Production callers leave it undefined.
2147
+ */
2148
+ _onPhaseCompleted?: (phase: "phase-4-snapshot" | "phase-5-bulk-write") => Promise<void>;
2149
+ };
2150
+ type ReconcileAllTasksInput = {
2151
+ /**
2152
+ * Per-task timestamp factory. Each reconciled task gets a fresh ISO string
2153
+ * so concurrent ad-hoc sessions do not collide on `occurred_at`. The CLI
2154
+ * layer wires this to `ctx.nowProvider().toISOString()`.
2155
+ */
2156
+ occurredAt: () => string;
2157
+ workingDirectory: string;
2158
+ write: boolean;
2159
+ };
2160
+ type ReconcileAllTasksOptions = {
2161
+ /**
2162
+ * When true the result includes clean tasks (= no broken refs). The CLI
2163
+ * layer leaves this false so the human output only mentions tasks that
2164
+ * actually changed.
2165
+ */
2166
+ includeClean?: boolean;
2167
+ };
2168
+ /**
2169
+ * Audit a single task's session references. In `write: false` mode this is a
2170
+ * pure read-only report (no events, no task.md change). In `write: true` mode,
2171
+ * if any broken reference is found, mint an ad-hoc reconcile session, fire
2172
+ * `task_reconciled`, and overwrite task.md with the repaired refs.
2173
+ *
2174
+ * The broken `created_in_session` field is REPLACED with the new reconcile
2175
+ * session id rather than nulled out — `TaskSchema.created_in_session` is
2176
+ * non-nullable, so dropping it would leave the file schema-invalid.
2177
+ * The old broken id is preserved on the event payload via
2178
+ * `removed_created_in_session` for audit.
2179
+ *
2180
+ * Stages — failures after stage 5 surface a phase-specific
2181
+ * {@link TaskWriteAfterEventError} so the CLI can render a tailored "do not
2182
+ * rerun" hint:
2183
+ * 1. Boundary parse
2184
+ * 2. Read task.md AND snapshot its mtime/hash from the same raw bytes,
2185
+ * then detect broken refs (sharing the raw bytes here closes the
2186
+ * readTaskFile-then-snapshot race window).
2187
+ * 3. Early return when clean (no event fired, no overwrite)
2188
+ * 4. (no separate stage anymore — snapshot is taken at stage 2)
2189
+ * 5. Mint ad-hoc session + `task_reconciled` event (catch
2190
+ * `FailedToFinalizeError` → `phase: "reconcile-finalize"`)
2191
+ * 6. Re-snapshot task.md; if changed since stage 2 →
2192
+ * `phase: "reconcile-concurrent"`
2193
+ * 7. Overwrite task.md; failure → `phase: "reconcile"`
2194
+ */
2195
+ declare function reconcileTask(paths: BasouPaths, manifest: Manifest, input: ReconcileTaskInput): Promise<ReconcileResult>;
2196
+ /**
2197
+ * Reconcile every task in `.basou/tasks/`. Continues on per-task failures so
2198
+ * an isolated {@link TaskWriteAfterEventError} does not stop the batch.
2199
+ * Malformed task.md files are skipped silently and excluded from `scanned`.
2200
+ */
2201
+ declare function reconcileAllTasks(paths: BasouPaths, manifest: Manifest, input: ReconcileAllTasksInput, options?: ReconcileAllTasksOptions): Promise<ReconcileAllResult>;
2202
+ /**
2203
+ * Single-task linkage refresh result. In `write: false` mode this is a pure
2204
+ * dry-run report (no event, no task.md change); `addedLinkedSessions` and
2205
+ * `removedLinkedSessions` describe what would change. In `write: true` mode
2206
+ * the same fields describe what did change and `refreshSession` carries the
2207
+ * ad-hoc session + `task_linkage_refreshed` event ids that were minted.
2208
+ *
2209
+ * `clean === true` means the existing `task.md.linked_sessions[]` already
2210
+ * matches the union of `session.yaml.task_id` matches plus the anchor
2211
+ * (`created_in_session`) — no event fired, no overwrite.
2212
+ */
2213
+ type RefreshLinkageResult = {
2214
+ taskId: PrefixedId<"task">;
2215
+ clean: boolean;
2216
+ addedLinkedSessions: PrefixedId<"ses">[];
2217
+ removedLinkedSessions: PrefixedId<"ses">[];
2218
+ /** Number of entries in `linked_sessions[]` after the refresh would run. */
2219
+ finalCount: number;
2220
+ refreshSession: {
2221
+ sessionId: PrefixedId<"ses">;
2222
+ eventId: PrefixedId<"evt">;
2223
+ } | null;
2224
+ };
2225
+ type RefreshLinkageInput = {
2226
+ taskId: PrefixedId<"task">;
2227
+ occurredAt: string;
2228
+ workingDirectory: string;
2229
+ write: boolean;
2230
+ };
2231
+ /**
2232
+ * Refresh `task.md.linked_sessions[]` so it matches the union of
2233
+ * `session.yaml.task_id` references in the workspace plus the
2234
+ * `created_in_session` anchor. In `write: false` this is a pure read-only
2235
+ * report; in `write: true` the diff is recorded as a
2236
+ * `task_linkage_refreshed` event inside a fresh ad-hoc session and the
2237
+ * task.md is overwritten with the new snapshot.
2238
+ *
2239
+ * Stages mirror `reconcileTask` so the operator gets the same
2240
+ * "do-not-rerun" hint shape on partial failure:
2241
+ * 1. Boundary parse
2242
+ * 2. Read task.md AND snapshot its mtime/hash from the same raw bytes
2243
+ * 3. Detect linkage delta (= scan workspace session.yaml)
2244
+ * 4. Early return when clean
2245
+ * 5. Mint ad-hoc session + `task_linkage_refreshed` event (catch
2246
+ * `FailedToFinalizeError` → `phase: "linkage-refresh-finalize"`)
2247
+ * 6. Re-snapshot task.md; if changed since stage 2 →
2248
+ * `phase: "linkage-refresh-concurrent"`
2249
+ * 7. Overwrite task.md; failure → `phase: "linkage-refresh"`
2250
+ *
2251
+ * The refresh event is distinct from `task_reconciled` (= broken-ref
2252
+ * cleanup, `.strict()` with broken-ref-specific fields) so each event
2253
+ * carries a single, focused audit story. Reusing `task_reconciled` here
2254
+ * would either redefine its semantics or require widening its strict
2255
+ * schema, both of which break replay determinism for older events.
2256
+ */
2257
+ declare function refreshTaskLinkedSessions(paths: BasouPaths, manifest: Manifest, input: RefreshLinkageInput): Promise<RefreshLinkageResult>;
2258
+ type EditTaskInput = {
2259
+ paths: BasouPaths;
2260
+ taskId: PrefixedId<"task">;
2261
+ /** New title; rejected when empty. Undefined leaves the field unchanged. */
2262
+ title?: string;
2263
+ /**
2264
+ * New status; routed through transition rules so the call rejects
2265
+ * invalid edges (e.g. `done -> planned`). Undefined leaves the field
2266
+ * unchanged.
2267
+ */
2268
+ newStatus?: TaskStatus;
2269
+ occurredAt: string;
2270
+ /**
2271
+ * Required when {@link newStatus} is provided — the status change fires
2272
+ * a `task_status_changed` event in a fresh ad-hoc session, which needs
2273
+ * a Manifest to seed the new session record. Title-only edits ignore
2274
+ * this field.
2275
+ */
2276
+ manifest?: Manifest;
2277
+ /** Working directory for the ad-hoc status-change session. */
2278
+ workingDirectory?: string;
2279
+ };
2280
+ type EditTaskResult = {
2281
+ taskId: PrefixedId<"task">;
2282
+ titleUpdated: boolean;
2283
+ statusUpdated: boolean;
2284
+ /** When {@link statusUpdated} is true, the previous status before the edit. */
2285
+ previousStatus: TaskStatus | null;
2286
+ /** When {@link statusUpdated} is true, the new status. */
2287
+ newStatus: TaskStatus | null;
2288
+ /** ad-hoc session minted when status was changed; null for title-only edits. */
2289
+ statusChangeSession: {
2290
+ sessionId: PrefixedId<"ses">;
2291
+ eventId: PrefixedId<"evt">;
2292
+ } | null;
2293
+ };
2294
+ /**
2295
+ * Update one or both of the user-editable fields on a task.md.
2296
+ *
2297
+ * - `title`: in-place overwrite of `task.md` only. v0.1 does not emit a
2298
+ * `task_title_changed` event — title changes are storage-level metadata
2299
+ * maintenance, not part of the audit trail.
2300
+ * - `newStatus`: routed through {@link updateTaskStatusWithEvent} so the
2301
+ * ALLOWED_TRANSITIONS gate is honored and a `task_status_changed` event is
2302
+ * appended to the audit trail.
2303
+ *
2304
+ * When both are supplied the status change runs first (= event committed)
2305
+ * and then the title overwrite runs against the freshly updated task.md
2306
+ * (= same `updated_at` from the status change). A failure of the
2307
+ * subsequent title overwrite leaves the status change committed; the
2308
+ * status-change side of an edit is the only side with an event, so the
2309
+ * audit trail is consistent regardless.
2310
+ */
2311
+ declare function editTask(input: EditTaskInput): Promise<EditTaskResult>;
2312
+ type DeleteTaskInput = {
2313
+ paths: BasouPaths;
2314
+ manifest: Manifest;
2315
+ taskId: PrefixedId<"task">;
2316
+ occurredAt: string;
2317
+ workingDirectory: string;
2318
+ };
2319
+ type DeleteTaskResult = {
2320
+ taskId: PrefixedId<"task">;
2321
+ title: string;
2322
+ sessionId: PrefixedId<"ses">;
2323
+ eventId: PrefixedId<"evt">;
2324
+ };
2325
+ /**
2326
+ * Hard-delete a task.md file with a `task_deleted` audit event.
2327
+ *
2328
+ * Sequence:
2329
+ * 1. Read task.md to capture the current title (which goes onto the
2330
+ * event payload so the audit record is self-describing even after
2331
+ * the file is gone).
2332
+ * 2. Mint an ad-hoc session, fire `task_deleted` as the target event.
2333
+ * The session's `task_id` is intentionally NOT pinned to the
2334
+ * to-be-deleted task — otherwise the audit session would carry a
2335
+ * broken reference the moment we unlink the file.
2336
+ * 3. Unlink `<paths.tasks>/<task_id>.md`.
2337
+ *
2338
+ * Failure of step 3 after the event is committed surfaces as a
2339
+ * {@link TaskWriteAfterEventError} with `phase: "delete"`; the operator
2340
+ * is told the event is durable but task.md still exists, and that a
2341
+ * manual `rm` (or a rerun) is required.
2342
+ *
2343
+ * v0.1 contract: no tombstone, no recovery. Restoring a deleted task is
2344
+ * not supported; the event payload (`task_id` + `title`) is the only
2345
+ * persistent record after the unlink succeeds.
2346
+ */
2347
+ declare function deleteTask(input: DeleteTaskInput): Promise<DeleteTaskResult>;
2348
+ type ArchiveTaskInput = {
2349
+ paths: BasouPaths;
2350
+ manifest: Manifest;
2351
+ taskId: PrefixedId<"task">;
2352
+ occurredAt: string;
2353
+ workingDirectory: string;
2354
+ };
2355
+ type ArchiveTaskResult = {
2356
+ taskId: PrefixedId<"task">;
2357
+ title: string;
2358
+ sessionId: PrefixedId<"ses">;
2359
+ eventId: PrefixedId<"evt">;
2360
+ };
2361
+ /**
2362
+ * Move a task.md file from `<paths.tasks>/<id>.md` to
2363
+ * `<paths.tasks>/archive/<id>.md` with a `task_archived` audit event.
2364
+ *
2365
+ * Sequence:
2366
+ * 1. Read task.md to capture the current title and existing content.
2367
+ * 2. Mint an ad-hoc session, fire `task_archived` as the target event.
2368
+ * The session's `task_id` IS pinned to the archived task — unlike
2369
+ * `task_deleted`, the task continues to exist (just at a new path),
2370
+ * so the session-task linkage stays a valid forward reference.
2371
+ * 3. Append the audit session to the task's `linked_sessions[]` and
2372
+ * overwrite the source task.md so the snapshot reflects the archive
2373
+ * session before the move.
2374
+ * 4. Ensure the archive directory exists.
2375
+ * 5. Rename main/<id>.md to archive/<id>.md (= atomic on the same fs).
2376
+ *
2377
+ * Failure modes after step 2 surface as
2378
+ * {@link TaskWriteAfterEventError} with `phase: "archive"`; the operator
2379
+ * is told the event is durable but the on-disk move is incomplete and
2380
+ * must be resolved manually (typically by rerunning `task archive`).
2381
+ */
2382
+ declare function archiveTask(input: ArchiveTaskInput): Promise<ArchiveTaskResult>;
2383
+
2384
+ /**
2385
+ * Resolve a possibly-truncated session id prefix to a full session id by
2386
+ * scanning `<paths.sessions>/`. Existing message contract (carried over
2387
+ * from `packages/cli/src/commands/session.ts` 経由の Step 12 実装) is
2388
+ * preserved exactly so callers that grep stderr keep working:
2389
+ *
2390
+ * - `"Session id is empty"`
2391
+ * - `"Session not found: <input>"`
2392
+ * - `"Ambiguous session id '<input>': matched <N> sessions. Disambiguate
2393
+ * with a longer prefix."`
2394
+ */
2395
+ declare function resolveSessionId(paths: BasouPaths, input: string): Promise<string>;
2396
+ /**
2397
+ * Resolve a possibly-truncated task id prefix to a full task id by scanning
2398
+ * `<paths.tasks>/`. Mirrors {@link resolveSessionId} with the noun changed
2399
+ * to `task` in every error message.
2400
+ *
2401
+ * `options.includeArchived` extends the scan to `<paths.tasks>/archive/` so
2402
+ * read-only commands (e.g. `basou task show`) can address tasks that were
2403
+ * archived by `basou task archive`. Defaults to `false` so destructive flows
2404
+ * (status change, edit, delete, archive itself) cannot operate on archived
2405
+ * tasks accidentally.
2406
+ */
2407
+ declare function resolveTaskId(paths: BasouPaths, input: string, options?: {
2408
+ includeArchived?: boolean;
2409
+ }): Promise<string>;
2410
+
2411
+ /**
2412
+ * Options for {@link sanitizePath}. Both `workingDirectory` and `homedir`
2413
+ * are absolute POSIX paths the caller has already resolved (typically via
2414
+ * `process.cwd()` and `os.homedir()`). Callers are responsible for passing
2415
+ * fully normalised values; the sanitizer normalises them again internally
2416
+ * so a trailing slash or `.`-segment does not corrupt the prefix match.
2417
+ */
2418
+ type SanitizePathOptions = {
2419
+ /**
2420
+ * The session's working directory (= the `working_directory` field the
2421
+ * caller is about to write). Paths under this directory are rewritten
2422
+ * relative to it so the operator-private absolute prefix never leaks
2423
+ * into the workspace's persistent state.
2424
+ */
2425
+ workingDirectory: string;
2426
+ /**
2427
+ * The operator's home directory. Paths under this directory (but NOT
2428
+ * under `workingDirectory`) are rewritten with a `~/` prefix.
2429
+ */
2430
+ homedir: string;
2431
+ };
2432
+ /**
2433
+ * Rewrite an absolute path into a workspace-friendly form so the persisted
2434
+ * state of `.basou/` does not leak the operator's machine layout:
2435
+ *
2436
+ * 1. Paths under `opts.workingDirectory` become repository-relative
2437
+ * (e.g. `<wd>/src/x.ts` → `src/x.ts`, `<wd>` itself → `.`).
2438
+ * 2. Paths under `opts.homedir` (but not workingDirectory) become
2439
+ * tilde-prefixed (`/Users/u/notes/x.md` → `~/notes/x.md`,
2440
+ * `/Users/u` → `~`).
2441
+ * 3. Anything else — relative paths, system paths under `/etc/*`,
2442
+ * `..`-escapes from either base, paths that simply do not share a
2443
+ * prefix with either option — is returned verbatim (after `..`
2444
+ * normalisation). The sanitizer is intentionally non-redacting on
2445
+ * system paths so an operator who deliberately recorded a system
2446
+ * file (e.g. `/etc/hosts`) is not silently stripped of context.
2447
+ *
2448
+ * Hardening:
2449
+ * - A null byte in the input is rejected with `Invalid path: contains
2450
+ * null byte` (= POSIX path APIs treat \0 as terminator and any path
2451
+ * containing one is malformed; we never accept it on the write side).
2452
+ * - `..` segments are resolved purely (no fs access) so the prefix
2453
+ * match cannot be defeated by `<wd>/../escape/x.ts` masquerading as
2454
+ * workingDirectory-internal.
2455
+ * - Backslashes are folded to forward slashes so a Windows-style input
2456
+ * can still be matched against POSIX bases. v0.3 targets macOS /
2457
+ * Linux only; full Windows support is a v0.4+ task.
2458
+ */
2459
+ declare function sanitizePath(rawPath: string, opts: SanitizePathOptions): string;
2460
+ /**
2461
+ * Sanitize the `working_directory` field itself. This is a distinct entry
2462
+ * point because the field's own value is the workingDirectory of every
2463
+ * `related_files[]` entry written alongside it — running it through
2464
+ * {@link sanitizePath} with `opts.workingDirectory = rawPath` would
2465
+ * collapse the result to `"."` and lose the homedir-relative form the
2466
+ * spec requires.
2467
+ *
2468
+ * Strategy: bypass the workingDirectory rule entirely by passing a
2469
+ * sentinel that no real path can match. The homedir rule (rule 2) and
2470
+ * the preserve-as-is rule (rule 3) still apply, so:
2471
+ * - `/Users/u/projects/foo` → `~/projects/foo`
2472
+ * - `/Users/u` → `~`
2473
+ * - `/srv/work` → `/srv/work` (preserved, off-tree)
2474
+ *
2475
+ * Callers should still pass the live `homedir` so the rewrite uses the
2476
+ * real operator-private prefix.
2477
+ */
2478
+ declare function sanitizeWorkingDirectory(rawPath: string, opts: Pick<SanitizePathOptions, "homedir">): string;
2479
+ /** Result of {@link sanitizeRelatedFiles}. */
2480
+ type SanitizeRelatedFilesResult = {
2481
+ /** Sanitized path list (same length as the input). */
2482
+ sanitized: string[];
2483
+ /** Number of entries whose sanitized form differs from the input. */
2484
+ mutationCount: number;
2485
+ };
2486
+ /**
2487
+ * Apply {@link sanitizePath} to every entry of a `related_files[]` array
2488
+ * and report how many entries actually changed shape so callers (e.g. the
2489
+ * session-import CLI) can surface a single-line warning. The helper does
2490
+ * not deduplicate — callers already collect related_files into a Set
2491
+ * before serialising.
2492
+ */
2493
+ declare function sanitizeRelatedFiles(paths: ReadonlyArray<string>, opts: SanitizePathOptions): SanitizeRelatedFilesResult;
2494
+
2495
+ /** Input contract for {@link renderHandoff}. */
2496
+ type HandoffRendererInput = {
2497
+ paths: BasouPaths;
2498
+ /** ISO timestamp embedded in the generated body header. Caller-provided for testability. */
2499
+ nowIso: string;
2500
+ /** Forwarded to {@link replayEvents} / {@link loadSessionEntries} per session. */
2501
+ onWarning?: (warning: ReplayWarning, sessionId: string) => void;
2502
+ /**
2503
+ * Per-session degradation reasons (missing/invalid session.yaml or
2504
+ * unreadable events.jsonl). The CLI maps `events_jsonl_unreadable` to the
2505
+ * existing suspect-check stderr wording to keep the user-facing surface
2506
+ * consistent with `basou session list`.
2507
+ */
2508
+ onSessionSkip?: (sessionId: string, reason: SessionSkipReason) => void;
2509
+ /**
2510
+ * Per-task degradation reasons (invalid front matter / unreadable file).
2511
+ * Surfaced so the CLI can warn the operator about a malformed task.md
2512
+ * without aborting the handoff render.
2513
+ */
2514
+ onTaskSkip?: (taskId: string, reason: TaskSkipReason) => void;
2515
+ /** Maximum related_files entries to display before `... +N more`. Default 20. */
2516
+ relatedFilesLimit?: number;
2517
+ };
2518
+ type HandoffRendererResult = {
2519
+ /** Generated body WITHOUT BASOU:GENERATED markers (markdown-store wraps them). */
2520
+ body: string;
2521
+ sessionCount: number;
2522
+ decisionCount: number;
2523
+ pendingApprovalsCount: number;
2524
+ suspectCount: number;
2525
+ /** Total number of task.md files successfully loaded. */
2526
+ taskCount: number;
2527
+ /** Tasks whose status is `planned` or `in_progress` (= shown in 次に実行すべき作業). */
2528
+ pendingTaskCount: number;
2529
+ };
2530
+ /**
2531
+ * Render the body of `handoff.md` from the current workspace state.
2532
+ *
2533
+ * The renderer is a pure function (no I/O beyond {@link replayEvents} /
2534
+ * {@link loadSessionEntries} / {@link enumerateApprovals}). It assembles the
2535
+ * the spec's `handoff.md` sections in order:
2536
+ *
2537
+ * 1. `現在の状態`: latest live session (status not archived, source not import).
2538
+ * 2. `直近の変更ファイル`: union of `related_files` across sessions, dedup +
2539
+ * sorted asc + truncated to `relatedFilesLimit` (default 20).
2540
+ * 3. `直近の判断`: latest `decision_recorded` event (chronological).
2541
+ * 4. `未決事項`: pending-approval count + suspect-session count.
2542
+ * 5. `次に読むべきファイル`: `.basou/decisions.md` + top-3 related files
2543
+ * (the same `displayedFiles` source is intentionally reused in two
2544
+ * sections — overview vs. resume context).
2545
+ * 6. `次に実行すべき作業`: placeholder until task events land.
2546
+ * 7. `セッション一覧`: all sessions newest first with inline suspect labels.
2547
+ *
2548
+ * Session enumeration goes through {@link loadSessionEntries} so the set of
2549
+ * sessions whose `decision_recorded` events we replay matches the
2550
+ * decisions renderer.
2551
+ */
2552
+ declare function renderHandoff(input: HandoffRendererInput): Promise<HandoffRendererResult>;
2553
+
2554
+ type DecisionsRendererInput = {
2555
+ paths: BasouPaths;
2556
+ nowIso: string;
2557
+ onWarning?: (warning: ReplayWarning, sessionId: string) => void;
2558
+ onSessionSkip?: (sessionId: string, reason: SessionSkipReason) => void;
2559
+ };
2560
+ type DecisionsRendererResult = {
2561
+ /** Generated body WITHOUT BASOU:GENERATED markers. */
2562
+ body: string;
2563
+ decisionCount: number;
2564
+ };
2565
+ /**
2566
+ * Render the body of `decisions.md` from `decision_recorded` events across
2567
+ * every healthy session in the workspace.
2568
+ *
2569
+ * Session enumeration goes through {@link loadSessionEntries} (the same path
2570
+ * the handoff renderer uses) so that `session.yaml`-broken sessions are
2571
+ * skipped in BOTH outputs and the handoff's `decisionCount` summary stays
2572
+ * consistent with the number of sections rendered here.
2573
+ *
2574
+ * Order: `occurred_at` ascending with `decisionId` (= ULID) as tie-breaker.
2575
+ * Both fields are monotonic, so the result is a stable cross-session
2576
+ * timeline.
2577
+ *
2578
+ * The decision rich fields (rationale / alternatives / rejected_reason /
2579
+ * linked_events / linked_files) are rendered when the event carries them.
2580
+ * `linked_events` and `linked_files` are OPAQUE references: the schema only
2581
+ * validates the SHAPE, not existence — references that cannot be resolved
2582
+ * to a known event id or an existing file on disk are surfaced inline as
2583
+ * `(missing)` so cross-workspace round-trips never reject parse-time.
2584
+ */
2585
+ declare function renderDecisions(input: DecisionsRendererInput): Promise<DecisionsRendererResult>;
2586
+
2587
+ /**
2588
+ * Internal abstraction over child-process execution.
2589
+ *
2590
+ * The v0.1 implementation is intentionally minimal:
2591
+ * - Optional UTF-8 stdout/stderr capture (`capture: "buffer"`, default) or
2592
+ * pass-through to the parent's stdio (`capture: "none"`).
2593
+ * - No stream callbacks for partial chunks.
2594
+ * - No event emission. Callers wire any event flow separately.
2595
+ *
2596
+ * The boundary is internal: ProcessRunner is not part of the public
2597
+ * adapter surface. Adapters do not import or instantiate it directly;
2598
+ * CLI / Core orchestration owns construction and invocation.
2599
+ */
2600
+ /**
2601
+ * Output capture mode.
2602
+ *
2603
+ * - `"buffer"` (default): pipe stdout/stderr to the runner and accumulate
2604
+ * the full UTF-8 string into {@link RunResult}.
2605
+ * - `"none"`: inherit the parent's stdio. The child writes directly to the
2606
+ * parent terminal in real time and {@link RunResult.stdout} /
2607
+ * {@link RunResult.stderr} are empty strings. `stdin` cannot be combined
2608
+ * with `"none"` because the child has no writable stdin pipe.
2609
+ */
2610
+ type CaptureMode = "buffer" | "none";
2611
+ type RunOptions = {
2612
+ /**
2613
+ * Working directory for the child process. Required: callers resolve
2614
+ * the workspace root themselves; the runner does not validate cwd
2615
+ * existence and surfaces native spawn errors via classification.
2616
+ */
2617
+ readonly cwd: string;
2618
+ /**
2619
+ * Environment variables for the child. When omitted, the parent's
2620
+ * `process.env` is inherited verbatim. Callers wanting a sanitized
2621
+ * environment must build it explicitly.
2622
+ */
2623
+ readonly env?: NodeJS.ProcessEnv;
2624
+ /**
2625
+ * External cancellation. Aborting the signal triggers a two-stage
2626
+ * kill (SIGTERM, then SIGKILL after a short grace period).
2627
+ */
2628
+ readonly signal?: AbortSignal;
2629
+ /**
2630
+ * Internal timeout in milliseconds. Must be a positive finite number.
2631
+ * Triggers the same two-stage kill as `signal`.
2632
+ */
2633
+ readonly timeout_ms?: number;
2634
+ /**
2635
+ * Optional input written to the child's stdin. The pipe is closed
2636
+ * after the value is written. Incompatible with `capture: "none"`.
2637
+ */
2638
+ readonly stdin?: string | Buffer;
2639
+ /**
2640
+ * Output capture mode. Defaults to `"buffer"`. See {@link CaptureMode}.
2641
+ */
2642
+ readonly capture?: CaptureMode;
2643
+ /**
2644
+ * Invoked synchronously immediately after the child has been spawned,
2645
+ * before the runner waits for completion. Callers use this to retain a
2646
+ * reference for parent-side cleanup (e.g. an `exit` hook that SIGKILLs
2647
+ * the child if the parent is forcibly terminated). The runner takes no
2648
+ * action if the callback throws.
2649
+ */
2650
+ readonly onSpawn?: (child: ChildProcess) => void;
2651
+ };
2652
+ type RunResult = {
2653
+ readonly command: string;
2654
+ readonly args: readonly string[];
2655
+ readonly cwd: string;
2656
+ /** `null` when the process was killed by a signal. */
2657
+ readonly exit_code: number | null;
2658
+ readonly signal: NodeJS.Signals | null;
2659
+ readonly stdout: string;
2660
+ readonly stderr: string;
2661
+ /** ISO 8601 timestamp captured before spawn. */
2662
+ readonly started_at: string;
2663
+ /** ISO 8601 timestamp captured on the `close` event. */
2664
+ readonly ended_at: string;
2665
+ readonly duration_ms: number;
2666
+ readonly pid: number | null;
2667
+ };
2668
+ type ProcessRunner = {
2669
+ run(command: string, args: readonly string[], options: RunOptions): Promise<RunResult>;
2670
+ };
2671
+
2672
+ /**
2673
+ * Spawn-based ProcessRunner implementation.
2674
+ *
2675
+ * Behavior:
2676
+ * - `shell: false` and `detached: false`. The process group is not
2677
+ * detached, but the OS does not guarantee the child is reaped when
2678
+ * the parent terminates abruptly; callers handle SIGINT/SIGTERM/exit
2679
+ * hooks themselves.
2680
+ * - `capture: "buffer"` (default): `stdio: ['pipe', 'pipe', 'pipe']`,
2681
+ * stdout / stderr are decoded as UTF-8 and accumulated as full
2682
+ * strings (no streaming callbacks).
2683
+ * - `capture: "none"`: `stdio: ['inherit', 'inherit', 'inherit']`, the
2684
+ * child writes directly to the parent terminal in real time and
2685
+ * `RunResult.stdout` / `stderr` are empty strings. `stdin` is
2686
+ * incompatible with this mode (the child has no writable stdin pipe)
2687
+ * and the combination is rejected before spawn.
2688
+ * - `timeout_ms` and `AbortSignal` both trigger a two-stage kill:
2689
+ * `SIGTERM`, then `SIGKILL` after `DEFAULT_KILL_GRACE_MS` (5_000 ms).
2690
+ * - A non-zero `exit_code` does not throw; it is returned via
2691
+ * `RunResult`. Spawn-time errors throw with a pathless message and
2692
+ * the original error attached as `cause`.
2693
+ *
2694
+ * Error message contract: messages never include `cwd` or absolute
2695
+ * command paths. The original errno (and any nested wrapping) is
2696
+ * preserved on `Error.cause`, allowing callers to classify with
2697
+ * `findErrorCode` when needed.
2698
+ */
2699
+ declare class ChildProcessRunner implements ProcessRunner {
2700
+ run(command: string, args: readonly string[], options: RunOptions): Promise<RunResult>;
2701
+ }
2702
+
2703
+ /**
2704
+ * Payload subset of `git_snapshot` event, mechanically derived from the
2705
+ * zod-inferred event type. The wrapping event-shape fields
2706
+ * (schema_version, id, session_id, occurred_at, source, type) are added by
2707
+ * the caller (session lifecycle in later steps) when constructing the
2708
+ * event, so the schema remains the single source of truth.
2709
+ *
2710
+ * `ahead` / `behind` are omitted when there is no remote or no upstream
2711
+ * tracking; the schema declares both as optional non-negative integers.
2712
+ */
2713
+ type GitSnapshot = Omit<GitSnapshotEvent, "schema_version" | "id" | "session_id" | "occurred_at" | "source" | "type">;
2714
+ /**
2715
+ * Resolve the absolute path of the Git repository root that contains `cwd`.
2716
+ * Equivalent to `git rev-parse --show-toplevel`.
2717
+ *
2718
+ * Throws `Error("Git executable not found in PATH. Install git first.")`
2719
+ * with the spawn error attached as `cause` when git itself is missing.
2720
+ * Throws `Error("Not a git repository")` (without command-specific suffix)
2721
+ * when `cwd` is not inside a repository — callers MAY wrap with their own
2722
+ * "Run 'git init' first, then re-run 'basou XXX'." suffix.
2723
+ *
2724
+ * Pathless contract: the thrown message never embeds `cwd` or any absolute
2725
+ * path; native errors are kept on `error.cause` for verbose surfacing.
2726
+ */
2727
+ declare function resolveRepositoryRoot(cwd: string): Promise<string>;
2728
+ /**
2729
+ * Read `remote.origin.url` from the local repository config. Returns
2730
+ * `undefined` if the remote is unset, the value is empty, or the lookup
2731
+ * fails for any reason (best-effort).
2732
+ *
2733
+ * The `--local` scope is critical: callers MUST NOT pick up the developer's
2734
+ * global remote.origin.url, which could leak the wrong repository URL into
2735
+ * `manifest.yaml`.
2736
+ */
2737
+ declare function tryRemoteUrl(repositoryRoot: string): Promise<string | undefined>;
2738
+ /**
2739
+ * Build a {@link GitSnapshot} for the repository at `repositoryRoot`. The
2740
+ * caller is responsible for ensuring `repositoryRoot` is the canonical root
2741
+ * (typically obtained via {@link resolveRepositoryRoot}); this function
2742
+ * verifies repo membership via `git rev-parse --is-inside-work-tree` to
2743
+ * distinguish a non-git directory from an empty repository.
2744
+ *
2745
+ * Edge cases:
2746
+ * - **non-git directory**: throws `Error("Not a git repository")`
2747
+ * - **empty repo (no commits)**: throws `Error("No commits in repository")`
2748
+ * - **detached HEAD**: `branch = "HEAD"`, `head = commit hash`,
2749
+ * `ahead`/`behind` omitted
2750
+ * - **no remote / no upstream tracking**: `ahead`/`behind` omitted
2751
+ *
2752
+ * Pathless contract preserved on every throw path.
2753
+ */
2754
+ declare function getSnapshot(repositoryRoot: string): Promise<GitSnapshot>;
2755
+
2756
+ /**
2757
+ * Status classification used by the `file_changed` event schema. Limited to
2758
+ * the four classes that simple-git's `git diff --name-status` reliably
2759
+ * surfaces; copy / unmerged / typechange entries are intentionally dropped
2760
+ * to keep the event payload shape narrow.
2761
+ */
2762
+ type FileChangeStatus = "added" | "modified" | "deleted" | "renamed";
2763
+ /**
2764
+ * Single file-level change observed between two refs. `old_path` is set
2765
+ * only for `renamed` entries (the previous path of the file).
2766
+ */
2767
+ type FileChange = {
2768
+ path: string;
2769
+ old_path?: string;
2770
+ status: FileChangeStatus;
2771
+ };
2772
+ /**
2773
+ * Result of {@link getDiff}. The `changed_files` array is in git's natural
2774
+ * `--name-status` order; callers requiring deterministic ordering should
2775
+ * sort by `path` themselves.
2776
+ */
2777
+ type DiffResult = {
2778
+ changed_files: FileChange[];
2779
+ };
2780
+ /**
2781
+ * Compute the file-level diff between two git refs.
2782
+ *
2783
+ * Returns a list of changed file paths classified by status (added /
2784
+ * modified / deleted / renamed). Diff content is intentionally NOT
2785
+ * returned — `file_changed` events record paths only, and raw diff bodies
2786
+ * are excluded so the trace cannot inadvertently leak source code that may
2787
+ * be sensitive. Use `git show <ref>` to obtain the underlying diff.
2788
+ *
2789
+ * Pathless contract: every thrown message is a fixed string from the set
2790
+ * {`Not a git repository`, `Git executable not found in PATH. Install git
2791
+ * first.`, `Invalid ref`, `Failed to compute git diff`}; native errors are
2792
+ * preserved on `Error.cause`.
2793
+ *
2794
+ * Special cases:
2795
+ * - `baseRef === headRef` short-circuits to an empty result
2796
+ * - copy / unmerged / typechange / unknown status codes are skipped
2797
+ *
2798
+ * @param repoRoot absolute path to the git repository root
2799
+ * @param baseRef base ref (e.g. session-start HEAD sha)
2800
+ * @param headRef head ref (e.g. session-end HEAD sha)
2801
+ */
2802
+ declare function getDiff(repoRoot: string, baseRef: string, headRef: string): Promise<DiffResult>;
2803
+
2804
+ /**
2805
+ * Static metadata identifying the claude-code adapter as the session source.
2806
+ * Consumed by the CLI orchestration when populating `session.yaml.source`
2807
+ * and event `source` fields. The literal `kind` is part of the wire format
2808
+ * defined by the session schema; do not change without coordinated schema
2809
+ * migration.
2810
+ */
2811
+ declare const claudeCodeAdapterMetadata: {
2812
+ readonly kind: "claude-code-adapter";
2813
+ readonly version: "0.1.0";
2814
+ };
2815
+ /**
2816
+ * Lookup predicate used by {@link resolveClaudeCodeCommand} to decide
2817
+ * whether a candidate executable is reachable on PATH. Exposed as a
2818
+ * parameter so tests can substitute a deterministic mock; production
2819
+ * callers should omit it and rely on the default `which`-based lookup.
2820
+ */
2821
+ type CommandLookup = (command: string) => Promise<boolean>;
2822
+ /**
2823
+ * Resolve the Claude Code CLI executable name. Tries `claude-code` first
2824
+ * and falls back to `claude`; the first candidate found on PATH wins.
2825
+ *
2826
+ * Throws a fixed-message Error when neither candidate is reachable, so
2827
+ * callers can present a single user-facing prompt to install the CLI.
2828
+ *
2829
+ * @throws Error("Claude Code CLI not found in PATH. Install claude-code (or claude) first.")
2830
+ */
2831
+ declare function resolveClaudeCodeCommand(lookup?: CommandLookup): Promise<{
2832
+ command: string;
2833
+ }>;
2834
+ /**
2835
+ * Stub for the future `adapter_output` summary generator.
2836
+ *
2837
+ * v0.1 Step 11 keeps `capture: "none"` and intentionally does not emit
2838
+ * `adapter_output` events, so this hook has no production callers yet.
2839
+ * The signature is committed so that Step 12+ can implement raw_ref
2840
+ * generation without retrofitting the adapter scaffold.
2841
+ *
2842
+ * @throws Error - always; not implemented in v0.1 Step 11.
2843
+ */
2844
+ declare function summarizeAdapterOutput(_stream: "stdout" | "stderr", _raw: string): string;
2845
+
2846
+ /**
2847
+ * Append a single Basou event to `<sessionDir>/events.jsonl`.
2848
+ *
2849
+ * The event is validated against the discriminated union {@link EventSchema}
2850
+ * before being serialized as a single JSONL line (UTF-8, terminated by `\n`).
2851
+ * Validation enforces the per-variant contract (required fields, source
2852
+ * vocabulary, strict variants such as `adapter_output`).
2853
+ *
2854
+ * Atomicity: writes go through `appendFile` which uses `O_APPEND`. Lines up
2855
+ * to `PIPE_BUF` bytes (Linux 4096 / macOS 512) are written atomically by the
2856
+ * kernel; longer lines may interleave with concurrent writers and are not
2857
+ * recovered here. v0.1 assumes a single writer per session, so partial-line
2858
+ * recovery is delegated to the read side (event replay) when introduced.
2859
+ *
2860
+ * Throws if validation fails or the underlying append errors. The thrown
2861
+ * Error message is pathless; the original error is attached as `cause`.
2862
+ *
2863
+ * @param sessionDir absolute path to `.basou/sessions/<session_id>/`
2864
+ * @param event unknown payload to validate and append
2865
+ */
2866
+ declare function appendEvent(sessionDir: string, event: unknown): Promise<void>;
2867
+ /**
2868
+ * Write `events.jsonl` in one atomic tmp+rename pass via {@link atomicReplace},
2869
+ * validating every event against {@link EventSchema} before any disk I/O so
2870
+ * a payload that fails validation never leaves a partial file behind.
2871
+ *
2872
+ * The helper is used by the round-trip importer (`session-import.ts`) and the
2873
+ * ad-hoc session orchestrator (`ad-hoc-session.ts`) where a small, fixed batch
2874
+ * of events must land together or not at all. Zero events produces a
2875
+ * zero-byte file so the session_yaml `events_log` pointer remains valid.
2876
+ *
2877
+ * Throws `"Invalid Basou event payload"` (same fixed message as
2878
+ * {@link appendEvent}) on validation failure, or `"Failed to write
2879
+ * events.jsonl"` on a disk I/O failure. The original native error is attached
2880
+ * as `cause`.
2881
+ */
2882
+ declare function writeEventsBulk(sessionDir: string, events: Event[]): Promise<void>;
2883
+
2884
+ /**
2885
+ * Parse a unit-suffixed duration string (e.g. `30s`, `5m`, `1h`, `100ms`)
2886
+ * into milliseconds.
2887
+ *
2888
+ * Rejects formats that cannot represent a positive, finite millisecond
2889
+ * value: malformed inputs, zero, leading-zero values, and computations that
2890
+ * overflow to `Infinity`. The returned number is always a positive integer.
2891
+ *
2892
+ * Supported units: `ms` (milliseconds), `s` (seconds), `m` (minutes),
2893
+ * `h` (hours).
2894
+ *
2895
+ * @param input duration string with required unit suffix
2896
+ * @returns duration in milliseconds (positive, finite)
2897
+ * @throws Error with message
2898
+ * `Invalid duration: <input>. Expected format: <positive-integer><unit> where unit is ms/s/m/h`
2899
+ * for format errors, or `Duration overflow: <input>` for non-finite results.
2900
+ */
2901
+ declare function parseDuration(input: string): number;
2902
+
2903
+ /**
2904
+ * Version of the `@basou/core` package, aligned with `manifest.yaml`'s
2905
+ * `basou_version` field as defined in the Basou v0.1 specification.
2906
+ */
2907
+ declare const BASOU_CORE_VERSION = "0.1.0";
2908
+
2909
+ export { type AdapterOutputEvent, type AppendBasouGitignoreResult, type AppendEventToExistingInput, type AppendEventToExistingResult, type Approval, type ApprovalApprovedEvent, type ApprovalExpiredEvent, ApprovalIdSchema, type ApprovalLocation, type ApprovalRejectedEvent, type ApprovalRequestedEvent, ApprovalSchema, type ApprovalStatus, ApprovalStatusSchema, type ArchiveTaskInput, type ArchiveTaskResult, type AttachTaskInput, type AttachUpdateTaskStatusInput, type AttachableStatus, BASOU_CORE_VERSION, type BasouPaths, type CaptureMode, ChildProcessRunner, type CommandExecutedEvent, type CommandLookup, type CreateAdHocSessionInput, type CreateAdHocSessionResult, type CreateAdHocTaskInput, type CreateManifestInput, type CreateTaskInput, type CreateTaskResult, DecisionIdSchema, type DecisionRecordedEvent, type DecisionsRendererInput, type DecisionsRendererResult, type DeleteTaskInput, type DeleteTaskResult, type DiffResult, type EditTaskInput, type EditTaskResult, type Event, EventIdSchema, EventSchema, EventSourceSchema, FailedToFinalizeError, type FileChange, type FileChangeStatus, type FileChangedEvent, GENERATED_END, GENERATED_START, type GitSnapshot, type GitSnapshotEvent, type HandoffRendererInput, type HandoffRendererResult, ID_PREFIXES, type IdPrefix, type ImportSessionOptions, type ImportSessionResult, IsoTimestampSchema, type LoadSessionEntriesOptions, type LoadTaskEntriesOptions, type LoadedApproval, type LockHandle, type LockScope, type Manifest, ManifestSchema, type MarkerSection, type NoteAddedEvent, type PrefixedId, type ProcessRunner, type ReconcileAllResult, type ReconcileAllTasksInput, type ReconcileAllTasksOptions, type ReconcileFailure, type ReconcileResult, type ReconcileTaskInput, type RefreshLinkageInput, type RefreshLinkageResult, type ReplayOptions, type ReplayWarning, type RiskLevel, RiskLevelSchema, type RunOptions, type RunResult, STUCK_THRESHOLD_MS, type SanitizePathOptions, type SanitizeRelatedFilesResult, SchemaVersionSchema, type Session, type SessionEndedEvent, type SessionEntry, SessionIdSchema, type SessionImportPayload, SessionImportPayloadSchema, type SessionInnerImportInput, SessionInnerImportSchema, SessionSchema, type SessionSkipReason, type SessionSourceKind, SessionSourceKindSchema, type SessionStartedEvent, type SessionStatus, type SessionStatusChangedEvent, SessionStatusSchema, StatusSchema, type StatusSnapshot, type SuspectReason, type Task, type TaskArchivedEvent, type TaskCreatedEvent, type TaskDeletedEvent, type TaskDocument, TaskIdSchema, type TaskLinkageRefreshedEvent, type TaskReconciledEvent, TaskSchema, type TaskSkipReason, type TaskStatus, type TaskStatusChangedEvent, TaskStatusSchema, TaskWriteAfterEventError, type TaskWriteAfterEventPhase, type UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, WorkspaceIdSchema, type WriteTaskFileMode, acquireLock, appendBasouGitignore, appendEvent, appendEventToExistingSession, archiveTask, assertBasouRootSafe, basouPaths, buildStatusSnapshot, classifySuspect, claudeCodeAdapterMetadata, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, findErrorCode, getDiff, getSnapshot, importSessionFromJson, isLazyExpired, isValidPrefixedId, linkYamlFile, loadApproval, loadSessionEntries, loadTaskEntries, overwriteYamlFile, parseDuration, parseMarkers, prefixedUlid, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, reconcileAllTasks, reconcileTask, refreshTaskLinkedSessions, renderDecisions, renderHandoff, renderWithMarkers, replayEvents, resolveClaudeCodeCommand, resolveRepositoryRoot, resolveSessionId, resolveTaskId, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, summarizeAdapterOutput, tryRemoteUrl, ulid, updateTaskStatusWithEvent, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };