@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.
- package/LICENSE +202 -0
- package/dist/index.d.ts +2909 -0
- package/dist/index.js +3885 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
package/dist/index.d.ts
ADDED
|
@@ -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 };
|