@lannguyensi/harness 0.54.0 → 0.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/cli/adopt/derive.js +1 -0
  3. package/dist/cli/adopt/derive.js.map +1 -1
  4. package/dist/cli/doctor/format.js +8 -0
  5. package/dist/cli/doctor/format.js.map +1 -1
  6. package/dist/cli/doctor/index.js +16 -0
  7. package/dist/cli/doctor/index.js.map +1 -1
  8. package/dist/cli/doctor/types.d.ts +16 -0
  9. package/dist/cli/doctor/types.js.map +1 -1
  10. package/dist/cli/doctor/ug-inflight.d.ts +35 -0
  11. package/dist/cli/doctor/ug-inflight.js +57 -0
  12. package/dist/cli/doctor/ug-inflight.js.map +1 -0
  13. package/dist/cli/gc/index.d.ts +2 -1
  14. package/dist/cli/gc/index.js +196 -9
  15. package/dist/cli/gc/index.js.map +1 -1
  16. package/dist/cli/index.js +42 -5
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/cli/pack/auto-approve-path.d.ts +11 -0
  19. package/dist/cli/pack/auto-approve-path.js +4 -2
  20. package/dist/cli/pack/auto-approve-path.js.map +1 -1
  21. package/dist/cli/pack/hook-bootstrap.d.ts +90 -0
  22. package/dist/cli/pack/hook-bootstrap.js +162 -0
  23. package/dist/cli/pack/hook-bootstrap.js.map +1 -1
  24. package/dist/cli/pack/hook-pre-tool-use.js +164 -10
  25. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
  26. package/dist/cli/pack/hook-subagent-start.d.ts +21 -0
  27. package/dist/cli/pack/hook-subagent-start.js +71 -0
  28. package/dist/cli/pack/hook-subagent-start.js.map +1 -0
  29. package/dist/cli/pack/hook-subagent-stop.d.ts +19 -0
  30. package/dist/cli/pack/hook-subagent-stop.js +41 -0
  31. package/dist/cli/pack/hook-subagent-stop.js.map +1 -0
  32. package/dist/io/read-regular-file.d.ts +33 -2
  33. package/dist/io/read-regular-file.js +41 -8
  34. package/dist/io/read-regular-file.js.map +1 -1
  35. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.d.ts +34 -4
  36. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +34 -3
  37. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js.map +1 -1
  38. package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +2 -1
  39. package/dist/policy-packs/builtin/understanding-before-execution/index.js +9 -0
  40. package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -1
  41. package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.d.ts +135 -0
  42. package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.js +619 -0
  43. package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.js.map +1 -0
  44. package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js +25 -11
  45. package/dist/policy-packs/builtin/understanding-before-execution/lifecycle.js.map +1 -1
  46. package/dist/policy-packs/builtin/understanding-before-execution.d.ts +4 -4
  47. package/dist/policy-packs/builtin/understanding-before-execution.js +42 -1
  48. package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
  49. package/dist/runtime/read-only-bash.d.ts +33 -0
  50. package/dist/runtime/read-only-bash.js +291 -7
  51. package/dist/runtime/read-only-bash.js.map +1 -1
  52. package/dist/runtime/risk-classifier.js +16 -12
  53. package/dist/runtime/risk-classifier.js.map +1 -1
  54. package/dist/schema/hooks.d.ts +13 -13
  55. package/dist/schema/hooks.js +1 -0
  56. package/dist/schema/hooks.js.map +1 -1
  57. package/dist/schema/index.d.ts +24 -24
  58. package/dist/schema/policies.d.ts +19 -19
  59. package/package.json +1 -1
@@ -0,0 +1,619 @@
1
+ // Signed in-flight records for Agent-tool subagents (slice 1 of the
2
+ // subagent-gate work, docs/decisions/2026-08-27-ug-auto-mode-approval.md
3
+ // "Invariants", "Threat model", "Delegation marker shape").
4
+ //
5
+ // THE PROBLEM THIS CLOSES. An Agent-tool subagent shares its parent's
6
+ // `session_id`; it has no way to call `harness approve understanding`
7
+ // itself. The understanding gate's own boundary-expiry mechanism
8
+ // (`applyPostToolUseExpiry`) deletes the parent's session marker the
9
+ // moment the parent crosses a task boundary, even while a subagent it
10
+ // spawned is still mid-flight — stranding that subagent with no
11
+ // approval it can present. An in-flight record is what lets a
12
+ // subagent keep the approval its parent held at the moment the
13
+ // subagent STARTED, independent of what happens to the parent's own
14
+ // marker afterwards.
15
+ //
16
+ // WHY ITS OWN DIRECTORY, NOT `.approvals/`. Exactly the delegation
17
+ // precedent (`delegation-markers.ts`'s module header): a record is
18
+ // evidence that a parent was once approved, not itself an approval a
19
+ // human granted, so it stays out of every `.approvals/` scan (the
20
+ // doctor `approvedBy`-prefix listing, `checkApprovalMarker`'s own
21
+ // directory) and out of `.delegations/` (a different artifact kind,
22
+ // scoped to `claude -p` children, not Agent-tool subagents). A record
23
+ // copied onto either of those directories fails signature verification
24
+ // there on markerId mismatch, mirroring the delegation module's own
25
+ // two-way argument.
26
+ //
27
+ // CONTAINMENT: `verifyInflightRecord` lstats both the `.inflight/` root
28
+ // and the session directory before ever touching the leaf file, so a
29
+ // symlinked root or session directory (reached only by following a link
30
+ // out of `.inflight/`) reads as no record rather than being followed.
31
+ // `.approvals/`'s own reader has the same pre-existing gap (it lstats
32
+ // neither its root nor a session-scoped subdirectory the same way); left
33
+ // as a follow-up there rather than fixed in this module.
34
+ //
35
+ // WHY EVERY BINDING TRAVELS INSIDE `approvedBy`. `signMarker` signs
36
+ // exactly the tuple `(markerId, approvedAt, approvedBy,
37
+ // reportContentHash)`; adding a signed field is a `SIGNING_ALG` bump.
38
+ // So the one thing this record signs beyond the marker id and the
39
+ // issue timestamp — which parent authority actually matched — rides in
40
+ // `approvedBy` as `inflight:<agentType>:parent=<task|session>`, the
41
+ // same shape choice `delegation-markers.ts` and the auto-approval path
42
+ // already made for their own extra bindings.
43
+ //
44
+ // SIGNATURE SCOPE, DELIBERATELY NARROW. The signed tuple binds
45
+ // `markerId` (which is itself `inflight:<sessionId>:<agentId>`),
46
+ // `approvedAt`, `approvedBy`, and a `null` report hash; it does NOT
47
+ // cover the `sessionId`/`agentId` fields stored in the body for
48
+ // convenience. A verifier that only recomputed the signature could
49
+ // therefore be fooled by a record left in place with its own body
50
+ // fields edited to claim a different agent id than the path it lives
51
+ // at — the signature over `inflight:<sessionId>:<agentId>` would still
52
+ // verify because `markerId` is derived from the REQUESTED ids, not
53
+ // from the body. `verifyInflightRecord` closes that gap with an
54
+ // explicit equality check between the body's own `sessionId`/`agentId`
55
+ // and the ids the caller is asking about, independent of the signature
56
+ // check: a record copied onto a different id's path recomputes the
57
+ // SAME signature (the markerId is derived from the requested ids, which
58
+ // still match its new path), so this equality check is the only thing
59
+ // that catches that shape of forgery.
60
+ //
61
+ // UNSIGNED CONVENIENCE FIELDS ARE NEVER LOAD-BEARING ON THEIR OWN. The
62
+ // body also carries `startedAt`, `agentType` and `parentSource` outside
63
+ // the signed tuple, for display and for readers (`listInflightRecords`,
64
+ // `harness gc`) that intentionally skip signature verification because
65
+ // they are deciding retention, not trust. `verifyInflightRecord` treats
66
+ // every one of those unsigned fields as untrustworthy on its own: it
67
+ // requires `startedAt` to equal the SIGNED `approvedAt` byte-for-byte
68
+ // (the writer always sets them to the same instant), and it requires
69
+ // `agentType`/`parentSource` to equal the values parsed back out of the
70
+ // SIGNED `approvedBy` string. Either disagreement is `forged`, never a
71
+ // silent fallback to the signed value — an editable field that could
72
+ // diverge from its signed twin without consequence would be exactly the
73
+ // same hole as trusting the unsigned field outright. Staleness itself is
74
+ // therefore computed from `approvedAt` (signed), not `startedAt`
75
+ // (unsigned): reviving an expired record by rewriting its unsigned
76
+ // timestamp is closed by verification failing outright, not merely by
77
+ // picking "the right" field to trust.
78
+ import * as fs from "node:fs";
79
+ import * as path from "node:path";
80
+ import { atomicWriteFile } from "../../../io/atomic-write.js";
81
+ import { readRegularFileRejectingSymlink } from "../../../io/read-regular-file.js";
82
+ import { safeJsonParse } from "../../../io/safe-json-parse.js";
83
+ import { signMarker, verifyMarkerSignature } from "../../../runtime/approval-signing.js";
84
+ import { rejectMalformedSessionId } from "../../../runtime/reject-malformed-session-id.js";
85
+ /** Directory holding in-flight records, a sibling of `.approvals/` and `.delegations/` under `generatedDir`. */
86
+ export const INFLIGHT_RECORD_DIRNAME = ".inflight";
87
+ /** Default staleness window: a record older than this is treated as dead weight even though nothing ever cleared it. */
88
+ export const DEFAULT_INFLIGHT_STALE_AFTER_MS = 24 * 60 * 60 * 1000;
89
+ /** Prefix of the signed markerId, mirroring `delegation-<child-sid>`. */
90
+ const INFLIGHT_MARKER_ID_PREFIX = "inflight:";
91
+ /**
92
+ * An agent id is a value Claude Code hands the hook, never one the
93
+ * repository's own path-traversal-sensitive session ids need to share
94
+ * a shape with, but it lands in `path.join` the same way, so it gets
95
+ * its own narrow allowlist rather than reusing
96
+ * `rejectMalformedSessionId`'s looser "no `/`, `\`, `..`" rule: 1..128
97
+ * characters, first character alphanumeric (so it can never literally
98
+ * be `.` or `..`), every other character one of
99
+ * `[A-Za-z0-9._-]` (no control characters, no path separators).
100
+ */
101
+ const AGENT_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
102
+ /** Rejects an agent id outside the allowlist above. Throws; never returns a value. */
103
+ export function rejectMalformedAgentId(agentId) {
104
+ if (agentId.length === 0) {
105
+ throw new Error("agentId is empty");
106
+ }
107
+ if (agentId.length > 128) {
108
+ throw new Error(`agentId exceeds 128 characters (got ${agentId.length})`);
109
+ }
110
+ if (!AGENT_ID_RE.test(agentId)) {
111
+ throw new Error(`agentId must start with an alphanumeric character and contain only letters, digits, ".", "_", or "-": ${JSON.stringify(agentId)}`);
112
+ }
113
+ }
114
+ /**
115
+ * The signed markerId for a record: `inflight:<sessionId>:<agentId>`.
116
+ * Binding both ids into the signed payload is what stops a validly
117
+ * signed record from being copied onto another agent id, or another
118
+ * session, and still verifying.
119
+ */
120
+ export function inflightMarkerIdFor(sessionId, agentId) {
121
+ return `${INFLIGHT_MARKER_ID_PREFIX}${sessionId}:${agentId}`;
122
+ }
123
+ /**
124
+ * Filesystem path of a subagent's in-flight record:
125
+ * `<generatedDir>/.inflight/<sessionId>/<agentId>`. Validates both ids
126
+ * before they reach `path.join`, the same defensive posture every
127
+ * other marker path function in this pack takes.
128
+ */
129
+ export function inflightRecordPathFor(generatedDir, sessionId, agentId) {
130
+ rejectMalformedSessionId(sessionId);
131
+ rejectMalformedAgentId(agentId);
132
+ return path.join(generatedDir, INFLIGHT_RECORD_DIRNAME, sessionId, agentId);
133
+ }
134
+ /**
135
+ * `agentType` is a Claude-Code-supplied label (e.g. `general-purpose`)
136
+ * that rides inside the signed `approvedBy` string as a `:`-delimited
137
+ * segment, so it gets the narrowest allowlist that keeps it from ever
138
+ * introducing a `:` (which `parseInflightApprovedBy` relies on to find
139
+ * the `:parent=` boundary) or a control character: 1..64 characters,
140
+ * first character alphanumeric, every other character one of
141
+ * `[A-Za-z0-9._-]`.
142
+ */
143
+ const AGENT_TYPE_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
144
+ /**
145
+ * Clock-skew tolerance for a record's signed `approvedAt`: an approval
146
+ * timestamp more than this far in the future cannot come from a clock
147
+ * this process trusts, so it is never matched — but unlike a forgery it
148
+ * is not evidence of tampering (a clock can simply be wrong), so it is
149
+ * reported as `stale`, the same bucket `harness gc` and the doctor
150
+ * listing already sweep on the aged side of the window.
151
+ */
152
+ const INFLIGHT_FUTURE_SKEW_MS = 5 * 60 * 1000;
153
+ /** The exact shape `writeInflightRecord` signs into `approvedBy`. */
154
+ const INFLIGHT_APPROVED_BY_RE = /^inflight:([A-Za-z0-9][A-Za-z0-9._-]{0,63}):parent=(task|session)$/;
155
+ /**
156
+ * Parse a signed `approvedBy` string back into the bindings it carries.
157
+ * `null` on anything that does not match the exact shape
158
+ * `writeInflightRecord` produces — this is the reader half of "every
159
+ * binding travels inside `approvedBy`" (see the module header): a
160
+ * value the writer could not have produced is never trusted as a source
161
+ * of bindings, even though its bytes passed HMAC verification (a valid
162
+ * signature only proves the bytes were signed by this key, not that
163
+ * they match the shape a caller can safely destructure).
164
+ */
165
+ function parseInflightApprovedBy(approvedBy) {
166
+ const match = INFLIGHT_APPROVED_BY_RE.exec(approvedBy);
167
+ if (match === null)
168
+ return null;
169
+ const [, agentType, parentSource] = match;
170
+ return { agentType: agentType, parentSource: parentSource };
171
+ }
172
+ /**
173
+ * Write a signed in-flight record, but only when `parent` names a
174
+ * MATCHED operator approval — a record is a copy of authority the
175
+ * parent already had, never a new grant, so an unmatched parent
176
+ * refuses outright with nothing written: the guard below is the entire
177
+ * enforcement point, not a redundant belt-and-suspenders check on top
178
+ * of some other gate. Atomic and mode 0600, like every other marker
179
+ * write in this pack.
180
+ */
181
+ export function writeInflightRecord(opts) {
182
+ if (!opts.parent.matched || opts.parent.source === null) {
183
+ return {
184
+ ok: false,
185
+ reason: "parent_not_approved",
186
+ detail: "refusing to write an in-flight record: the parent has no matched operator approval",
187
+ };
188
+ }
189
+ if (!AGENT_TYPE_RE.test(opts.agentType)) {
190
+ return {
191
+ ok: false,
192
+ reason: "malformed_agent_type",
193
+ detail: `agentType must be 1-64 characters, first character alphanumeric, every other character one of letters, digits, ".", "_", or "-": ${JSON.stringify(opts.agentType)}`,
194
+ };
195
+ }
196
+ let filePath;
197
+ try {
198
+ filePath = inflightRecordPathFor(opts.generatedDir, opts.sessionId, opts.agentId);
199
+ }
200
+ catch (err) {
201
+ return {
202
+ ok: false,
203
+ reason: "invalid-input",
204
+ detail: err instanceof Error ? err.message : String(err),
205
+ };
206
+ }
207
+ const parentSource = opts.parent.source;
208
+ const startedAt = (opts.now ?? new Date()).toISOString();
209
+ const markerId = inflightMarkerIdFor(opts.sessionId, opts.agentId);
210
+ const signed = signMarker(opts.generatedDir, markerId, {
211
+ approvedAt: startedAt,
212
+ approvedBy: `inflight:${opts.agentType}:parent=${parentSource}`,
213
+ reportContentHash: null,
214
+ });
215
+ const body = {
216
+ sessionId: opts.sessionId,
217
+ agentId: opts.agentId,
218
+ agentType: opts.agentType,
219
+ startedAt,
220
+ parentSource,
221
+ parentDetail: opts.parent.detail,
222
+ ...signed,
223
+ };
224
+ try {
225
+ atomicWriteFile(filePath, `${JSON.stringify(body, null, 2)}\n`, { mode: 0o600 });
226
+ }
227
+ catch (err) {
228
+ return {
229
+ ok: false,
230
+ reason: "write-failed",
231
+ detail: err instanceof Error ? err.message : String(err),
232
+ };
233
+ }
234
+ return { ok: true, filePath };
235
+ }
236
+ /**
237
+ * Consult the in-flight record for `(sessionId, agentId)`. Checks, in
238
+ * order: the `.inflight/` root and the session directory are real
239
+ * directories, not symlinks (module header, "CONTAINMENT"); the
240
+ * requested `agentId` names an EXACT directory entry, not merely a path
241
+ * that resolves (guards a case-insensitive filesystem); the file is
242
+ * present and JSON-readable; its signature verifies against
243
+ * `inflight:<sessionId>:<agentId>` RECOMPUTED FROM THE REQUESTED ids
244
+ * (never from anything stored in the body); its own `sessionId`/`agentId`
245
+ * fields agree with those requested ids (the check the signature alone
246
+ * cannot make, see the module header); it is not older than the
247
+ * staleness window.
248
+ *
249
+ * Fail-closed shape mirrors `verifyDelegation`: every refusal names a
250
+ * distinct reason, `forged` and `stale` are reachable only from the
251
+ * branches documented above, and there is no branch that turns a
252
+ * missing, malformed, moved, or aged record into `matched: true`.
253
+ */
254
+ export function verifyInflightRecord(generatedDir, sessionId, agentId, opts = {}) {
255
+ let filePath;
256
+ try {
257
+ filePath = inflightRecordPathFor(generatedDir, sessionId, agentId);
258
+ }
259
+ catch (err) {
260
+ return {
261
+ matched: false,
262
+ forged: false,
263
+ stale: false,
264
+ detail: `invalid sessionId/agentId for in-flight record: ${err instanceof Error ? err.message : String(err)}`,
265
+ };
266
+ }
267
+ // lstat the root and the session directory before ever reading the
268
+ // leaf, mirroring `listInflightRecords`' own guard: a `readdirSync` or
269
+ // `readFileSync` reached only by following a symlink out of
270
+ // `.inflight/` would let a linked root or session directory smuggle a
271
+ // record from elsewhere on disk. Absent, a symlink, or not a directory
272
+ // all read as "no record" here, never `forged`: none of them is
273
+ // evidence of a tampered signature, only that the expected containment
274
+ // does not hold.
275
+ const rootDir = path.join(generatedDir, INFLIGHT_RECORD_DIRNAME);
276
+ const sessionDir = path.join(rootDir, sessionId);
277
+ for (const dir of [rootDir, sessionDir]) {
278
+ let dirStat;
279
+ try {
280
+ dirStat = fs.lstatSync(dir);
281
+ }
282
+ catch {
283
+ return { matched: false, forged: false, stale: false, detail: `no in-flight record at ${filePath}` };
284
+ }
285
+ if (!dirStat.isDirectory()) {
286
+ return {
287
+ matched: false,
288
+ forged: false,
289
+ stale: false,
290
+ detail: `no in-flight record at ${filePath} (${dir} is not a plain directory)`,
291
+ };
292
+ }
293
+ }
294
+ // The requested agentId must name an EXACT entry in the session
295
+ // directory, not merely a path `path.join` can resolve to. On a
296
+ // case-insensitive filesystem the read below would happily open a
297
+ // differently-cased sibling's record; its signature — recomputed from
298
+ // the REQUESTED, case-variant id — would then fail to verify and be
299
+ // classified forged, even though nothing was tampered with. A
300
+ // case-variant request simply names no record; `readdirSync` lists
301
+ // literal entry names regardless of the filesystem's own case
302
+ // sensitivity, so this is the one check that can tell the two apart.
303
+ //
304
+ // ONLY the agentId segment gets this exact-entry check. The sessionId
305
+ // segment (the `sessionDir` lstat above) inherits whatever the
306
+ // underlying filesystem does with `path.join` + `lstatSync`: on a
307
+ // case-insensitive volume a case-variant sessionId still resolves to
308
+ // the same directory, so the lstat passes, and the record is read and
309
+ // signature-checked against the REQUESTED (case-variant) sessionId —
310
+ // which the SIGNED body never matches, so it comes back `forged`, not
311
+ // "no record". This is the same fail-closed outcome the module header
312
+ // argues for generally; it is merely a less precise diagnostic than
313
+ // the agentId case gets (see the ADR's "TTL, cwd, and subagents"
314
+ // section for the write side that never produces one anyway: session
315
+ // ids are runtime-generated UUIDs, not operator-chosen strings, so a
316
+ // case collision is not a realistic write-time input).
317
+ let sessionEntries;
318
+ try {
319
+ sessionEntries = fs.readdirSync(sessionDir);
320
+ }
321
+ catch {
322
+ return { matched: false, forged: false, stale: false, detail: `no in-flight record at ${filePath}` };
323
+ }
324
+ if (!sessionEntries.includes(agentId)) {
325
+ return {
326
+ matched: false,
327
+ forged: false,
328
+ stale: false,
329
+ detail: `no in-flight record at ${filePath} (no exact entry named ${JSON.stringify(agentId)} in ${sessionDir})`,
330
+ };
331
+ }
332
+ const read = readRegularFileRejectingSymlink(filePath);
333
+ if (read.kind === "missing") {
334
+ return { matched: false, forged: false, stale: false, detail: `no in-flight record at ${filePath}` };
335
+ }
336
+ if (read.kind === "symlink") {
337
+ return {
338
+ matched: false,
339
+ forged: false,
340
+ stale: false,
341
+ detail: `in-flight record is a symlink, refusing for safety: ${filePath}`,
342
+ };
343
+ }
344
+ if (read.kind === "not-regular") {
345
+ return {
346
+ matched: false,
347
+ forged: false,
348
+ stale: false,
349
+ detail: `in-flight record path is not a regular file: ${filePath}`,
350
+ };
351
+ }
352
+ if (read.kind === "unreadable") {
353
+ return {
354
+ matched: false,
355
+ forged: false,
356
+ stale: false,
357
+ detail: `in-flight record at ${filePath} exists but could not be read (I/O error); treating as absent since its signature cannot be verified`,
358
+ };
359
+ }
360
+ const parsed = safeJsonParse(read.content);
361
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
362
+ return {
363
+ matched: false,
364
+ forged: false,
365
+ stale: false,
366
+ detail: `unparsable in-flight record body at ${filePath}`,
367
+ };
368
+ }
369
+ const body = parsed;
370
+ const markerId = inflightMarkerIdFor(sessionId, agentId);
371
+ const verification = verifyMarkerSignature(generatedDir, markerId, body);
372
+ if (!verification.ok) {
373
+ if (verification.kind === "key-unavailable") {
374
+ // Fail-closed I/O problem, not evidence of tampering — same
375
+ // distinction `verifyDelegation` draws for its own `unreadable`.
376
+ return {
377
+ matched: false,
378
+ forged: false,
379
+ stale: false,
380
+ detail: `in-flight record at ${filePath} could not be verified: ${verification.reason}`,
381
+ };
382
+ }
383
+ return {
384
+ matched: false,
385
+ forged: true,
386
+ stale: false,
387
+ detail: `forged/unsigned in-flight record rejected: ${verification.reason} (${filePath})`,
388
+ };
389
+ }
390
+ const bodySessionId = typeof body["sessionId"] === "string" ? body["sessionId"] : undefined;
391
+ const bodyAgentId = typeof body["agentId"] === "string" ? body["agentId"] : undefined;
392
+ if (bodySessionId !== sessionId || bodyAgentId !== agentId) {
393
+ return {
394
+ matched: false,
395
+ forged: true,
396
+ stale: false,
397
+ detail: `in-flight record at ${filePath} disagrees with its own path (body sessionId=${JSON.stringify(bodySessionId)}, agentId=${JSON.stringify(bodyAgentId)}, expected ${sessionId}/${agentId})`,
398
+ };
399
+ }
400
+ // `verifyMarkerSignature` already rejected a non-string/empty
401
+ // `approvedAt` before returning `ok: true`, so this cast is safe; the
402
+ // finite-instant check below still guards against a signed-but-garbage
403
+ // value (e.g. a non-date string that happens to be non-empty).
404
+ const approvedAt = body["approvedAt"];
405
+ const approvedAtMs = Date.parse(approvedAt);
406
+ if (!Number.isFinite(approvedAtMs)) {
407
+ return {
408
+ matched: false,
409
+ forged: true,
410
+ stale: false,
411
+ detail: `in-flight record at ${filePath} has a signed approvedAt that is not a valid instant: ${JSON.stringify(approvedAt)}`,
412
+ };
413
+ }
414
+ // The unsigned `startedAt` convenience field must agree with the
415
+ // SIGNED `approvedAt` byte-for-byte (the writer always sets them to
416
+ // the same instant). Divergence means the unsigned copy was edited
417
+ // after signing — the shape that would revive an aged, genuinely
418
+ // stale record by rewriting only its unsigned timestamp.
419
+ const startedAt = typeof body["startedAt"] === "string" ? body["startedAt"] : undefined;
420
+ if (startedAt !== approvedAt) {
421
+ return {
422
+ matched: false,
423
+ forged: true,
424
+ stale: false,
425
+ detail: `in-flight record at ${filePath} has an unsigned startedAt that disagrees with its signed approvedAt (startedAt=${JSON.stringify(startedAt)}, approvedAt=${JSON.stringify(approvedAt)})`,
426
+ };
427
+ }
428
+ // `approvedBy` is likewise guaranteed a non-empty string by
429
+ // `verifyMarkerSignature`. Its parsed bindings must agree with the
430
+ // unsigned `agentType`/`parentSource` fields the body carries for
431
+ // display — an attacker who could edit only the unsigned pair (the
432
+ // signed `approvedBy` unchanged) would otherwise reach the success
433
+ // detail below with a body/approvedBy that disagree.
434
+ const approvedBy = body["approvedBy"];
435
+ const bindings = parseInflightApprovedBy(approvedBy);
436
+ const bodyAgentType = typeof body["agentType"] === "string" ? body["agentType"] : undefined;
437
+ const bodyParentSource = typeof body["parentSource"] === "string" ? body["parentSource"] : undefined;
438
+ if (bindings === null || bindings.agentType !== bodyAgentType || bindings.parentSource !== bodyParentSource) {
439
+ return {
440
+ matched: false,
441
+ forged: true,
442
+ stale: false,
443
+ detail: `in-flight record at ${filePath} has an approvedBy binding that disagrees with its own body (approvedBy=${JSON.stringify(approvedBy)}, body agentType=${JSON.stringify(bodyAgentType)}, parentSource=${JSON.stringify(bodyParentSource)})`,
444
+ };
445
+ }
446
+ const nowMs = (opts.now ?? new Date()).getTime();
447
+ const staleAfterMs = opts.staleAfterMs ?? DEFAULT_INFLIGHT_STALE_AFTER_MS;
448
+ // A clock this process trusts would never see its own signed approval
449
+ // land more than a few minutes in the future; treat it like staleness
450
+ // (not a forgery) since a clock skew is not evidence of tampering.
451
+ if (approvedAtMs - nowMs > INFLIGHT_FUTURE_SKEW_MS) {
452
+ return {
453
+ matched: false,
454
+ forged: false,
455
+ stale: true,
456
+ detail: `in-flight record at ${filePath} is dated more than ${INFLIGHT_FUTURE_SKEW_MS / 60_000} minutes in the future: approved at ${approvedAt}`,
457
+ };
458
+ }
459
+ // `nowMs - approvedAtMs > staleAfterMs`, not the reverse: swapping the
460
+ // comparison would mark a fresh record stale and a genuinely stale one
461
+ // fresh.
462
+ if (nowMs - approvedAtMs > staleAfterMs) {
463
+ return {
464
+ matched: false,
465
+ forged: false,
466
+ stale: true,
467
+ detail: `in-flight record at ${filePath} is stale: approved at ${approvedAt}`,
468
+ };
469
+ }
470
+ return {
471
+ matched: true,
472
+ forged: false,
473
+ stale: false,
474
+ detail: `in-flight record matched: agent ${agentId} (${bindings.agentType}), parent=${bindings.parentSource}, approved at ${approvedAt}`,
475
+ };
476
+ }
477
+ /**
478
+ * Remove a subagent's in-flight record, then best-effort remove its
479
+ * now-possibly-empty session directory. Never throws when the record,
480
+ * or the session directory, is already gone — mirrors
481
+ * `clearApprovalMarker` / `clearTaskApprovalMarker`.
482
+ */
483
+ export function clearInflightRecord(generatedDir, sessionId, agentId) {
484
+ let filePath;
485
+ try {
486
+ filePath = inflightRecordPathFor(generatedDir, sessionId, agentId);
487
+ }
488
+ catch {
489
+ return;
490
+ }
491
+ try {
492
+ fs.rmSync(filePath);
493
+ }
494
+ catch {
495
+ /* already gone */
496
+ }
497
+ try {
498
+ fs.rmdirSync(path.dirname(filePath));
499
+ }
500
+ catch {
501
+ /* not empty, or already gone */
502
+ }
503
+ }
504
+ /**
505
+ * Read-only audit listing of every in-flight record on disk, mirroring
506
+ * `buildUgDelegations`'s own shape: no signature check (this is
507
+ * evidence for `harness doctor` and `harness gc`, not a trust
508
+ * decision), tolerant of a missing `.inflight/` directory (empty
509
+ * result rather than an error), and an entry gc/doctor cannot make
510
+ * sense of is `skipped` rather than silently dropped.
511
+ *
512
+ * lstat the root itself first, not a bare `readdirSync`-then-catch: a
513
+ * `readdirSync` on a symlinked `.inflight/` would happily follow it and
514
+ * list whatever real directory it points at, the same trap
515
+ * `sweepInflightRecords` in `src/cli/gc/index.ts` already guards
516
+ * against one level up. A symlinked or non-directory root reads as
517
+ * absent here too, for the same reason `buildUgInflight` already lstats
518
+ * before calling this function: it must not report on records reached
519
+ * only by following a link out of `.inflight/`.
520
+ */
521
+ export function listInflightRecords(generatedDir, now, staleAfterMs) {
522
+ const dir = path.join(generatedDir, INFLIGHT_RECORD_DIRNAME);
523
+ const nowMs = (now ?? new Date()).getTime();
524
+ const effectiveStaleAfterMs = staleAfterMs ?? DEFAULT_INFLIGHT_STALE_AFTER_MS;
525
+ let rootStat;
526
+ try {
527
+ rootStat = fs.lstatSync(dir);
528
+ }
529
+ catch {
530
+ return { total: 0, stale: 0, sessions: [], skipped: [] };
531
+ }
532
+ if (!rootStat.isDirectory()) {
533
+ return { total: 0, stale: 0, sessions: [], skipped: [] };
534
+ }
535
+ let sessionDirents;
536
+ try {
537
+ sessionDirents = fs.readdirSync(dir, { withFileTypes: true });
538
+ }
539
+ catch {
540
+ return { total: 0, stale: 0, sessions: [], skipped: [] };
541
+ }
542
+ let total = 0;
543
+ let stale = 0;
544
+ const sessions = [];
545
+ const skipped = [];
546
+ for (const sessionDirent of sessionDirents) {
547
+ const sessionPath = path.join(dir, sessionDirent.name);
548
+ if (!sessionDirent.isDirectory()) {
549
+ skipped.push(sessionPath);
550
+ continue;
551
+ }
552
+ try {
553
+ rejectMalformedSessionId(sessionDirent.name);
554
+ }
555
+ catch {
556
+ skipped.push(sessionPath);
557
+ continue;
558
+ }
559
+ let agentDirents;
560
+ try {
561
+ agentDirents = fs.readdirSync(sessionPath, { withFileTypes: true });
562
+ }
563
+ catch {
564
+ skipped.push(sessionPath);
565
+ continue;
566
+ }
567
+ const agentIds = [];
568
+ for (const agentDirent of agentDirents) {
569
+ const full = path.join(sessionPath, agentDirent.name);
570
+ if (!agentDirent.isFile()) {
571
+ skipped.push(full);
572
+ continue;
573
+ }
574
+ try {
575
+ rejectMalformedAgentId(agentDirent.name);
576
+ }
577
+ catch {
578
+ skipped.push(full);
579
+ continue;
580
+ }
581
+ const read = readRegularFileRejectingSymlink(full);
582
+ if (read.kind !== "ok") {
583
+ skipped.push(full);
584
+ continue;
585
+ }
586
+ const parsed = safeJsonParse(read.content);
587
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
588
+ skipped.push(full);
589
+ continue;
590
+ }
591
+ const body = parsed;
592
+ // No signature check here (audit listing, not a trust decision —
593
+ // see the function doc), so this reads the SAME `approvedAt`
594
+ // field `verifyInflightRecord` trusts only once it has verified
595
+ // the signature: an unsigned `startedAt` copy is never consulted,
596
+ // by either reader, for a staleness decision.
597
+ const approvedAt = typeof body["approvedAt"] === "string" ? body["approvedAt"] : undefined;
598
+ const approvedAtMs = approvedAt !== undefined ? Date.parse(approvedAt) : NaN;
599
+ if (approvedAt === undefined || !Number.isFinite(approvedAtMs)) {
600
+ skipped.push(full);
601
+ continue;
602
+ }
603
+ total += 1;
604
+ agentIds.push(agentDirent.name);
605
+ // Aged past the window, or dated implausibly far in the future
606
+ // (clock skew, or a tampered field this listing does not verify):
607
+ // both count as `stale` here, mirroring `verifyInflightRecord`'s
608
+ // own future-skew handling.
609
+ if (nowMs - approvedAtMs > effectiveStaleAfterMs || approvedAtMs - nowMs > INFLIGHT_FUTURE_SKEW_MS) {
610
+ stale += 1;
611
+ }
612
+ }
613
+ if (agentIds.length > 0) {
614
+ sessions.push({ sessionId: sessionDirent.name, agentIds });
615
+ }
616
+ }
617
+ return { total, stale, sessions, skipped };
618
+ }
619
+ //# sourceMappingURL=inflight-records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inflight-records.js","sourceRoot":"","sources":["../../../../src/policy-packs/builtin/understanding-before-execution/inflight-records.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,yEAAyE;AACzE,4DAA4D;AAC5D,EAAE;AACF,sEAAsE;AACtE,sEAAsE;AACtE,iEAAiE;AACjE,qEAAqE;AACrE,sEAAsE;AACtE,gEAAgE;AAChE,8DAA8D;AAC9D,+DAA+D;AAC/D,oEAAoE;AACpE,qBAAqB;AACrB,EAAE;AACF,mEAAmE;AACnE,mEAAmE;AACnE,qEAAqE;AACrE,kEAAkE;AAClE,kEAAkE;AAClE,oEAAoE;AACpE,sEAAsE;AACtE,uEAAuE;AACvE,oEAAoE;AACpE,oBAAoB;AACpB,EAAE;AACF,wEAAwE;AACxE,qEAAqE;AACrE,wEAAwE;AACxE,sEAAsE;AACtE,sEAAsE;AACtE,yEAAyE;AACzE,yDAAyD;AACzD,EAAE;AACF,oEAAoE;AACpE,wDAAwD;AACxD,sEAAsE;AACtE,kEAAkE;AAClE,uEAAuE;AACvE,oEAAoE;AACpE,uEAAuE;AACvE,6CAA6C;AAC7C,EAAE;AACF,+DAA+D;AAC/D,iEAAiE;AACjE,oEAAoE;AACpE,gEAAgE;AAChE,mEAAmE;AACnE,kEAAkE;AAClE,qEAAqE;AACrE,uEAAuE;AACvE,mEAAmE;AACnE,gEAAgE;AAChE,uEAAuE;AACvE,uEAAuE;AACvE,mEAAmE;AACnE,wEAAwE;AACxE,sEAAsE;AACtE,sCAAsC;AACtC,EAAE;AACF,uEAAuE;AACvE,wEAAwE;AACxE,wEAAwE;AACxE,uEAAuE;AACvE,wEAAwE;AACxE,qEAAqE;AACrE,sEAAsE;AACtE,qEAAqE;AACrE,wEAAwE;AACxE,uEAAuE;AACvE,qEAAqE;AACrE,wEAAwE;AACxE,yEAAyE;AACzE,iEAAiE;AACjE,mEAAmE;AACnE,sEAAsE;AACtE,sCAAsC;AAEtC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,+BAA+B,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AACzF,OAAO,EAAE,wBAAwB,EAAE,MAAM,iDAAiD,CAAC;AAG3F,gHAAgH;AAChH,MAAM,CAAC,MAAM,uBAAuB,GAAG,WAAW,CAAC;AAEnD,wHAAwH;AACxH,MAAM,CAAC,MAAM,+BAA+B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEnE,yEAAyE;AACzE,MAAM,yBAAyB,GAAG,WAAW,CAAC;AAE9C;;;;;;;;;GASG;AACH,MAAM,WAAW,GAAG,oCAAoC,CAAC;AAEzD,sFAAsF;AACtF,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,uCAAuC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,yGAAyG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CACnI,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAAiB,EAAE,OAAe;IACpE,OAAO,GAAG,yBAAyB,GAAG,SAAS,IAAI,OAAO,EAAE,CAAC;AAC/D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,YAAoB,EAAE,SAAiB,EAAE,OAAe;IAC5F,wBAAwB,CAAC,SAAS,CAAC,CAAC;IACpC,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,uBAAuB,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAC9E,CAAC;AAKD;;;;;;;;GAQG;AACH,MAAM,aAAa,GAAG,mCAAmC,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,uBAAuB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAE9C,qEAAqE;AACrE,MAAM,uBAAuB,GAAG,oEAAoE,CAAC;AAErG;;;;;;;;;GASG;AACH,SAAS,uBAAuB,CAC9B,UAAkB;IAElB,MAAM,KAAK,GAAG,uBAAuB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,SAAS,EAAE,YAAY,CAAC,GAAG,KAAK,CAAC;IAC1C,OAAO,EAAE,SAAS,EAAE,SAAmB,EAAE,YAAY,EAAE,YAAoC,EAAE,CAAC;AAChG,CAAC;AAsBD;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAgC;IAClE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QACxD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,qBAAqB;YAC7B,MAAM,EAAE,oFAAoF;SAC7F,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACxC,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,sBAAsB;YAC9B,MAAM,EAAE,oIAAoI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE;SAC7K,CAAC;IACJ,CAAC;IACD,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,qBAAqB,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACpF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,eAAe;YACvB,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACzD,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAyB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC9D,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACzD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE;QACrD,UAAU,EAAE,SAAS;QACrB,UAAU,EAAE,YAAY,IAAI,CAAC,SAAS,WAAW,YAAY,EAAE;QAC/D,iBAAiB,EAAE,IAAI;KACxB,CAAC,CAAC;IACH,MAAM,IAAI,GAAG;QACX,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS;QACT,YAAY;QACZ,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;QAChC,GAAG,MAAM;KACV,CAAC;IACF,IAAI,CAAC;QACH,eAAe,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACnF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,cAAc;YACtB,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACzD,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC;AAyBD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,oBAAoB,CAClC,YAAoB,EACpB,SAAiB,EACjB,OAAe,EACf,OAAoC,EAAE;IAEtC,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,qBAAqB,CAAC,YAAY,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,mDACN,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CACjD,EAAE;SACH,CAAC;IACJ,CAAC;IAED,mEAAmE;IACnE,uEAAuE;IACvE,4DAA4D;IAC5D,sEAAsE;IACtE,uEAAuE;IACvE,gEAAgE;IAChE,uEAAuE;IACvE,iBAAiB;IACjB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,uBAAuB,CAAC,CAAC;IACjE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACjD,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC;QACxC,IAAI,OAAiB,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,QAAQ,EAAE,EAAE,CAAC;QACvG,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;YAC3B,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,KAAK;gBACb,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,0BAA0B,QAAQ,KAAK,GAAG,4BAA4B;aAC/E,CAAC;QACJ,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,gEAAgE;IAChE,kEAAkE;IAClE,sEAAsE;IACtE,oEAAoE;IACpE,8DAA8D;IAC9D,mEAAmE;IACnE,8DAA8D;IAC9D,qEAAqE;IACrE,EAAE;IACF,sEAAsE;IACtE,+DAA+D;IAC/D,kEAAkE;IAClE,qEAAqE;IACrE,sEAAsE;IACtE,qEAAqE;IACrE,sEAAsE;IACtE,sEAAsE;IACtE,oEAAoE;IACpE,iEAAiE;IACjE,qEAAqE;IACrE,qEAAqE;IACrE,uDAAuD;IACvD,IAAI,cAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,cAAc,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,QAAQ,EAAE,EAAE,CAAC;IACvG,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,0BAA0B,QAAQ,0BAA0B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,UAAU,GAAG;SAChH,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,+BAA+B,CAAC,QAAQ,CAAC,CAAC;IACvD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,QAAQ,EAAE,EAAE,CAAC;IACvG,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,uDAAuD,QAAQ,EAAE;SAC1E,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAChC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,gDAAgD,QAAQ,EAAE;SACnE,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAC/B,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,uBAAuB,QAAQ,sGAAsG;SAC9I,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3C,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,uCAAuC,QAAQ,EAAE;SAC1D,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,MAAiC,CAAC;IAE/C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,YAAY,GAAG,qBAAqB,CAAC,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IACzE,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC;QACrB,IAAI,YAAY,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAC5C,4DAA4D;YAC5D,iEAAiE;YACjE,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,KAAK;gBACb,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,uBAAuB,QAAQ,2BAA2B,YAAY,CAAC,MAAM,EAAE;aACxF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,8CAA8C,YAAY,CAAC,MAAM,KAAK,QAAQ,GAAG;SAC1F,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5F,MAAM,WAAW,GAAG,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtF,IAAI,aAAa,KAAK,SAAS,IAAI,WAAW,KAAK,OAAO,EAAE,CAAC;QAC3D,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,KAAK;YACZ,MAAM,EACJ,uBAAuB,QAAQ,gDAAgD,IAAI,CAAC,SAAS,CAC3F,aAAa,CACd,aAAa,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,cAAc,SAAS,IAAI,OAAO,GAAG;SACjF,CAAC;IACJ,CAAC;IAED,8DAA8D;IAC9D,sEAAsE;IACtE,uEAAuE;IACvE,+DAA+D;IAC/D,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAW,CAAC;IAChD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACnC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,uBAAuB,QAAQ,yDAAyD,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE;SAC7H,CAAC;IACJ,CAAC;IAED,iEAAiE;IACjE,oEAAoE;IACpE,mEAAmE;IACnE,iEAAiE;IACjE,yDAAyD;IACzD,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxF,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QAC7B,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,uBAAuB,QAAQ,mFAAmF,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG;SACjM,CAAC;IACJ,CAAC;IAED,4DAA4D;IAC5D,mEAAmE;IACnE,kEAAkE;IAClE,mEAAmE;IACnE,mEAAmE;IACnE,qDAAqD;IACrD,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAW,CAAC;IAChD,MAAM,QAAQ,GAAG,uBAAuB,CAAC,UAAU,CAAC,CAAC;IACrD,MAAM,aAAa,GAAG,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5F,MAAM,gBAAgB,GAAG,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACrG,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,SAAS,KAAK,aAAa,IAAI,QAAQ,CAAC,YAAY,KAAK,gBAAgB,EAAE,CAAC;QAC5G,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,uBAAuB,QAAQ,2EAA2E,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,kBAAkB,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,GAAG;SACnP,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IACjD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,+BAA+B,CAAC;IAE1E,sEAAsE;IACtE,sEAAsE;IACtE,mEAAmE;IACnE,IAAI,YAAY,GAAG,KAAK,GAAG,uBAAuB,EAAE,CAAC;QACnD,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,uBAAuB,QAAQ,uBAAuB,uBAAuB,GAAG,MAAM,uCAAuC,UAAU,EAAE;SAClJ,CAAC;IACJ,CAAC;IAED,uEAAuE;IACvE,uEAAuE;IACvE,SAAS;IACT,IAAI,KAAK,GAAG,YAAY,GAAG,YAAY,EAAE,CAAC;QACxC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,uBAAuB,QAAQ,0BAA0B,UAAU,EAAE;SAC9E,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,KAAK;QACb,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,mCAAmC,OAAO,KAAK,QAAQ,CAAC,SAAS,aAAa,QAAQ,CAAC,YAAY,iBAAiB,UAAU,EAAE;KACzI,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAoB,EAAE,SAAiB,EAAE,OAAe;IAC1F,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACH,QAAQ,GAAG,qBAAqB,CAAC,YAAY,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,IAAI,CAAC;QACH,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;IACD,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,gCAAgC;IAClC,CAAC;AACH,CAAC;AAkBD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,mBAAmB,CACjC,YAAoB,EACpB,GAAU,EACV,YAAqB;IAErB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,uBAAuB,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IAC5C,MAAM,qBAAqB,GAAG,YAAY,IAAI,+BAA+B,CAAC;IAE9E,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC3D,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QAC5B,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC3D,CAAC;IAED,IAAI,cAA2B,CAAC;IAChC,IAAI,CAAC;QACH,cAAc,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC3D,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,QAAQ,GAAqD,EAAE,CAAC;IACtE,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,aAAa,IAAI,cAAc,EAAE,CAAC;QAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,EAAE,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC1B,SAAS;QACX,CAAC;QACD,IAAI,CAAC;YACH,wBAAwB,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC1B,SAAS;QACX,CAAC;QAED,IAAI,YAAyB,CAAC;QAC9B,IAAI,CAAC;YACH,YAAY,GAAG,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC1B,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;YACtD,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC1B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnB,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,sBAAsB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAC3C,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnB,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,+BAA+B,CAAC,IAAI,CAAC,CAAC;YACnD,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBACvB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnB,SAAS;YACX,CAAC;YACD,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3C,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3E,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnB,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,MAAiC,CAAC;YAC/C,iEAAiE;YACjE,6DAA6D;YAC7D,gEAAgE;YAChE,kEAAkE;YAClE,8CAA8C;YAC9C,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC3F,MAAM,YAAY,GAAG,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAC7E,IAAI,UAAU,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC/D,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnB,SAAS;YACX,CAAC;YACD,KAAK,IAAI,CAAC,CAAC;YACX,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAChC,+DAA+D;YAC/D,kEAAkE;YAClE,iEAAiE;YACjE,4BAA4B;YAC5B,IAAI,KAAK,GAAG,YAAY,GAAG,qBAAqB,IAAI,YAAY,GAAG,KAAK,GAAG,uBAAuB,EAAE,CAAC;gBACnG,KAAK,IAAI,CAAC,CAAC;YACb,CAAC;QACH,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC7C,CAAC"}