@atolis-hq/wake 0.3.86 → 0.3.88

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.
@@ -108,4 +108,4 @@ export function resolveWakeVersion(options = {}) {
108
108
  return `g${headHash.slice(0, 7)}`;
109
109
  return '0.1.0-dev';
110
110
  }
111
- export const wakeVersion = "g7341a7f";
111
+ export const wakeVersion = "gead96c0";
@@ -1,4 +1,4 @@
1
- import { appendFile, mkdir, readdir, readFile, stat } from 'node:fs/promises';
1
+ import { appendFile, mkdir, open, readdir, readFile, stat, writeFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { isDeepStrictEqual } from 'node:util';
4
4
  import { decodeEventEnvelope, InProcessJournalChangeSignal, WrongExpectedSequenceError, } from '../../kernel/index.js';
@@ -69,16 +69,60 @@ export class FileEventJournal {
69
69
  const file = `${day}.jsonl`;
70
70
  await appendFile(join(directory, file), newEnvelopes.map((event) => JSON.stringify(event)).join('\n') + '\n', 'utf8');
71
71
  await this.extendCache(file, current, newEnvelopes);
72
+ // The manifest is derived data. The event append is authoritative, so
73
+ // an index-write failure must not turn a successfully recorded event
74
+ // into a failed append.
75
+ await this.persistManifest().catch(() => undefined);
72
76
  this.changeSignalSource.notify();
73
77
  }
74
78
  return finalizedEnvelopes;
75
79
  });
76
80
  }
81
+ // Reads via the persisted per-segment manifest when the in-memory cache is
82
+ // cold (fresh process, or an on-disk change this instance didn't make),
83
+ // parsing only the segment files that can hold matching events instead of
84
+ // the entire history. A missing or stale manifest degrades to scan()'s
85
+ // full parse rather than to incorrect data; scan() then rebuilds it.
77
86
  async readStream(stream) {
78
- return (await this.scan()).filter((event) => key(event.stream) === key(stream));
87
+ const streamKey = key(stream);
88
+ const entries = await this.readCurrentEntries();
89
+ if (this.cached !== undefined && sameEntries(this.cached.entries, entries))
90
+ return this.cached.events.filter((event) => key(event.stream) === streamKey);
91
+ const manifest = await this.loadManifest();
92
+ if (manifest !== undefined && isCompleteIndex(manifest, entries)) {
93
+ try {
94
+ return await this.parseIndexedEntries(manifest.segments.flatMap((segment) => segment.events
95
+ .filter((event) => event.stream === streamKey)
96
+ .map((event) => ({ ...event, file: segment.file }))));
97
+ }
98
+ catch {
99
+ // The segment stats guarded the common stale-index case. If an index
100
+ // is nevertheless internally inconsistent, the JSONL remains the
101
+ // authority and retains the original read semantics.
102
+ }
103
+ }
104
+ return (await this.scan(entries)).filter((event) => key(event.stream) === streamKey);
79
105
  }
80
106
  async readAll(after, limit = Number.POSITIVE_INFINITY) {
81
- return (await this.scan()).filter((event) => event.globalPosition > after).slice(0, limit);
107
+ const entries = await this.readCurrentEntries();
108
+ if (this.cached !== undefined && sameEntries(this.cached.entries, entries))
109
+ return this.cached.events.filter((event) => event.globalPosition > after).slice(0, limit);
110
+ const manifest = await this.loadManifest();
111
+ if (manifest !== undefined && isCompleteIndex(manifest, entries)) {
112
+ try {
113
+ return await this.parseIndexedEntries(manifest.segments
114
+ .flatMap((segment) => segment.events
115
+ .filter((event) => event.globalPosition > after)
116
+ .map((event) => ({ ...event, file: segment.file })))
117
+ .slice(0, limit));
118
+ }
119
+ catch {
120
+ // See readStream(): never let a derived index change journal reads.
121
+ }
122
+ }
123
+ return (await this.scan(entries))
124
+ .filter((event) => event.globalPosition > after)
125
+ .slice(0, limit);
82
126
  }
83
127
  async readLatest(beforeGlobalPosition, limit = Number.POSITIVE_INFINITY) {
84
128
  const events = await this.scan();
@@ -104,19 +148,76 @@ export class FileEventJournal {
104
148
  const entries = priorEntries.some((entry) => entry.file === file)
105
149
  ? priorEntries.map((entry) => (entry.file === file ? updatedEntry : entry))
106
150
  : [...priorEntries, updatedEntry];
107
- this.cached = { entries, events: [...priorEvents, ...newEnvelopes] };
151
+ const priorSegments = this.cached?.segments ?? [];
152
+ const existingSegment = priorSegments.find((segment) => segment.file === file);
153
+ let offset = existingSegment?.size ?? 0;
154
+ const indexedEvents = newEnvelopes.map((event) => {
155
+ const length = Buffer.byteLength(`${JSON.stringify(event)}\n`);
156
+ const indexed = {
157
+ globalPosition: event.globalPosition,
158
+ stream: key(event.stream),
159
+ offset,
160
+ length,
161
+ };
162
+ offset += length;
163
+ return indexed;
164
+ });
165
+ const segments = existingSegment
166
+ ? priorSegments.map((segment) => segment.file === file
167
+ ? {
168
+ ...updatedEntry,
169
+ startPosition: segment.startPosition,
170
+ count: segment.count + newEnvelopes.length,
171
+ events: [...segment.events, ...indexedEvents],
172
+ }
173
+ : segment)
174
+ : [
175
+ ...priorSegments,
176
+ {
177
+ ...updatedEntry,
178
+ startPosition: priorEvents.length + 1,
179
+ count: newEnvelopes.length,
180
+ events: indexedEvents,
181
+ },
182
+ ];
183
+ this.cached = { entries, events: [...priorEvents, ...newEnvelopes], segments };
108
184
  }
109
- scan() {
185
+ manifestPath() {
186
+ return join(this.root, 'events', 'index-manifest.json');
187
+ }
188
+ // Persisted alongside the segments, so any reader of the same on-disk
189
+ // journal — not just this instance — can skip straight to the segments
190
+ // that can hold what it's looking for on a cold cache. append() extends its
191
+ // contents from the concrete envelopes it has just written while locked.
192
+ async persistManifest() {
193
+ if (this.cached === undefined)
194
+ return;
195
+ const manifest = { segments: this.cached.segments };
196
+ await writeFile(this.manifestPath(), JSON.stringify(manifest), 'utf8');
197
+ }
198
+ async loadManifest() {
199
+ try {
200
+ const raw = await readFile(this.manifestPath(), 'utf8');
201
+ const parsed = JSON.parse(raw);
202
+ return isPersistedIndex(parsed) ? parsed : undefined;
203
+ }
204
+ catch {
205
+ // Missing, corrupt, or foreign-shaped index: degrade to a full scan,
206
+ // which is rebuilt by the next append.
207
+ return undefined;
208
+ }
209
+ }
210
+ scan(precomputedEntries) {
110
211
  if (this.inFlightScan !== undefined)
111
212
  return this.inFlightScan;
112
- const run = this.scanUncoalesced().finally(() => {
213
+ const run = this.scanUncoalesced(precomputedEntries).finally(() => {
113
214
  if (this.inFlightScan === run)
114
215
  this.inFlightScan = undefined;
115
216
  });
116
217
  this.inFlightScan = run;
117
218
  return run;
118
219
  }
119
- async scanUncoalesced() {
220
+ async readCurrentEntries() {
120
221
  const directory = join(this.root, 'events');
121
222
  let files;
122
223
  try {
@@ -129,17 +230,69 @@ export class FileEventJournal {
129
230
  return [];
130
231
  throw error;
131
232
  }
132
- const entries = await Promise.all(files.map(async (file) => {
233
+ return Promise.all(files.map(async (file) => {
133
234
  const info = await stat(join(directory, file));
134
235
  return { file, size: info.size, mtimeMs: info.mtimeMs };
135
236
  }));
237
+ }
238
+ // Reads exactly the indexed JSONL records. Byte offsets make tail reads and
239
+ // stream reads proportional to the matching events, not segment size.
240
+ async parseIndexedEntries(entries) {
241
+ const directory = join(this.root, 'events');
242
+ const events = [];
243
+ const grouped = new Map();
244
+ for (const entry of entries) {
245
+ const group = grouped.get(entry.file) ?? [];
246
+ group.push(entry);
247
+ grouped.set(entry.file, group);
248
+ }
249
+ for (const [file, indexedEvents] of grouped) {
250
+ const handle = await open(join(directory, file), 'r');
251
+ try {
252
+ for (const indexed of indexedEvents) {
253
+ const buffer = Buffer.alloc(indexed.length);
254
+ const { bytesRead } = await handle.read(buffer, 0, indexed.length, indexed.offset);
255
+ events.push(this.decodeIndexedRecord(file, indexed, buffer, bytesRead));
256
+ }
257
+ }
258
+ finally {
259
+ await handle.close();
260
+ }
261
+ }
262
+ return events;
263
+ }
264
+ decodeIndexedRecord(file, indexed, buffer, bytesRead) {
265
+ if (bytesRead !== indexed.length || buffer.at(-1) !== 10)
266
+ throw new Error(`Incomplete indexed record in ${file}`);
267
+ let input;
268
+ try {
269
+ input = JSON.parse(buffer.toString('utf8'));
270
+ const event = decodeEventEnvelope(input);
271
+ validateEnvelope(event, indexed.globalPosition);
272
+ if (key(event.stream) !== indexed.stream)
273
+ throw new Error('Indexed stream mismatch');
274
+ return event;
275
+ }
276
+ catch (error) {
277
+ const context = eventContext(input);
278
+ throw new Error(`Corrupt indexed event at ${file}:${indexed.globalPosition}${context}: ${error.message}`, { cause: error });
279
+ }
280
+ }
281
+ async scanUncoalesced(precomputedEntries) {
282
+ const directory = join(this.root, 'events');
283
+ const entries = precomputedEntries ?? (await this.readCurrentEntries());
136
284
  if (this.cached !== undefined && sameEntries(this.cached.entries, entries))
137
285
  return this.cached.events;
138
286
  const events = [];
139
- for (const file of files) {
287
+ const segments = [];
288
+ for (const entry of entries) {
289
+ const { file } = entry;
140
290
  const raw = await readFile(join(directory, file), 'utf8');
141
291
  if (raw.length > 0 && !raw.endsWith('\n'))
142
292
  throw new Error(`Incomplete trailing line in ${file}`);
293
+ const startPosition = events.length + 1;
294
+ const indexedEvents = [];
295
+ let offset = 0;
143
296
  for (const [index, line] of raw.split('\n').slice(0, -1).entries()) {
144
297
  let input;
145
298
  try {
@@ -147,17 +300,78 @@ export class FileEventJournal {
147
300
  const event = decodeEventEnvelope(input);
148
301
  validateEnvelope(event, events.length + 1);
149
302
  events.push(event);
303
+ indexedEvents.push({
304
+ globalPosition: event.globalPosition,
305
+ stream: key(event.stream),
306
+ offset,
307
+ length: Buffer.byteLength(`${line}\n`),
308
+ });
309
+ offset += Buffer.byteLength(`${line}\n`);
150
310
  }
151
311
  catch (error) {
152
312
  const context = eventContext(input);
153
313
  throw new Error(`Corrupt event at ${file}:${index + 1}${context}: ${error.message}`, { cause: error });
154
314
  }
155
315
  }
316
+ segments.push({
317
+ ...entry,
318
+ startPosition,
319
+ count: events.length - startPosition + 1,
320
+ events: indexedEvents,
321
+ });
156
322
  }
157
- this.cached = { entries, events };
323
+ this.cached = { entries, events, segments };
158
324
  return events;
159
325
  }
160
326
  }
327
+ function isPersistedIndex(value) {
328
+ return (typeof value === 'object' &&
329
+ value !== null &&
330
+ Array.isArray(value.segments) &&
331
+ value.segments.every(isSegmentInfo));
332
+ }
333
+ function isSegmentInfo(value) {
334
+ if (typeof value !== 'object' || value === null)
335
+ return false;
336
+ const segment = value;
337
+ return (typeof segment.file === 'string' &&
338
+ typeof segment.size === 'number' &&
339
+ typeof segment.mtimeMs === 'number' &&
340
+ typeof segment.startPosition === 'number' &&
341
+ typeof segment.count === 'number' &&
342
+ Array.isArray(segment.events) &&
343
+ segment.events.every(isIndexedEvent));
344
+ }
345
+ function isIndexedEvent(value) {
346
+ if (typeof value !== 'object' || value === null)
347
+ return false;
348
+ const event = value;
349
+ return (typeof event.globalPosition === 'number' &&
350
+ typeof event.stream === 'string' &&
351
+ typeof event.offset === 'number' &&
352
+ typeof event.length === 'number');
353
+ }
354
+ function isCompleteIndex(index, entries) {
355
+ if (!sameEntries(index.segments, entries))
356
+ return false;
357
+ let position = 1;
358
+ return index.segments.every((segment) => {
359
+ let offset = 0;
360
+ const complete = segment.startPosition === position &&
361
+ segment.count === segment.events.length &&
362
+ segment.events.every((event) => {
363
+ const matches = event.globalPosition === position &&
364
+ event.offset === offset &&
365
+ Number.isSafeInteger(event.length) &&
366
+ event.length > 0;
367
+ position += 1;
368
+ offset += event.length;
369
+ return matches;
370
+ }) &&
371
+ offset === segment.size;
372
+ return complete;
373
+ });
374
+ }
161
375
  function sameEntries(a, b) {
162
376
  return (a.length === b.length &&
163
377
  a.every((entry, index) => {
@@ -0,0 +1 @@
1
+ ._shell_1rpcj_1{grid-template-rows:auto auto auto 1fr;min-height:100vh;display:grid}._header_1rpcj_6{align-items:center;gap:var(--space-2);padding:.55rem var(--space-4);background:var(--brand);color:var(--ink-inverse);display:flex}._brand_1rpcj_14{align-items:center;gap:var(--space-2);color:var(--ink-inverse);font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-weight:700;font-size:var(--text-lg);letter-spacing:.01em;text-decoration:none;display:inline-flex}._brandLogo_1rpcj_25{width:1.75rem;height:1.75rem}._brandVersion_1rpcj_29{color:#ffffffb3;font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-size:.78rem;font-weight:400}._statusBand_1rpcj_35{align-items:center;gap:var(--space-4);padding:.45rem var(--space-4);background:var(--brand-dark);font-size:var(--text-sm);color:var(--ink-inverse);border-top:1px solid #0000002e;flex-wrap:wrap;display:flex}._nav_1rpcj_46{padding:.4rem var(--space-4) 0;background:var(--brand-darker);scrollbar-width:none;-ms-overflow-style:none;border-bottom:1px solid var(--border);gap:.25rem;display:flex;overflow-x:auto}._nav_1rpcj_46::-webkit-scrollbar{display:none}._nav_1rpcj_46 a{color:var(--nav-ink-idle);white-space:nowrap;transition:color var(--motion-fast) ease;border-bottom:2px solid #0000;margin-bottom:-1px;padding:.4rem .7rem .45rem;text-decoration:none}._nav_1rpcj_46 a:hover{color:var(--ink-inverse);text-decoration:none}._nav_1rpcj_46 a[aria-current=page]{color:var(--accent-light);border-bottom-color:var(--accent);font-weight:700}._main_1rpcj_77{width:100%;padding:var(--space-6) var(--space-4);margin:0}._pageHeader_1rpcj_82{justify-content:space-between;align-items:end;gap:var(--space-4);margin-bottom:var(--space-5);display:flex}._pageHeader_1rpcj_82 h1{margin:0}._panel_1rpcj_92{background:var(--surface-panel);border:1px solid var(--border);border-radius:var(--radius);padding:var(--space-4);box-shadow:var(--shadow)}._badge_1rpcj_99{background:var(--surface-card);color:var(--ink);border-radius:999px;align-items:center;gap:.4rem;padding:.2rem .55rem;font-size:.85rem;font-weight:700;display:inline-flex}._badge_1rpcj_99:before{content:"";background:currentColor;border-radius:50%;flex:none;width:.55rem;height:.55rem}._badge_1rpcj_99>span{align-items:center;gap:.3rem;display:inline-flex}._good_1rpcj_123{color:var(--good)}._warning_1rpcj_126{color:var(--warning)}._bad_1rpcj_99{color:var(--bad)}._cold_1rpcj_132{color:#7dd3fc;background:#0e74902e;box-shadow:inset 0 0 0 1px #7dd3fc59}._button_1rpcj_137{appearance:none;border:1px solid var(--border-strong);border-radius:var(--radius-sm);background:var(--surface-card);color:var(--ink);font:inherit;font-size:var(--text-sm);cursor:pointer;padding:.22rem .55rem;font-weight:600}._button_1rpcj_137:hover:not(:disabled){border-color:var(--accent-light);color:var(--accent-light)}._button_1rpcj_137:disabled{opacity:.62;cursor:wait}._secondary_1rpcj_157{background:0 0}._tableWrap_1rpcj_160{overflow-x:auto}._table_1rpcj_160{border-collapse:collapse;width:100%;font-size:var(--text-sm)}._table_1rpcj_160 caption{text-align:left;font-weight:700;font-size:var(--text-sm);padding-bottom:.5rem}._table_1rpcj_160 th,._table_1rpcj_160 td{text-align:left;border-bottom:1px solid var(--border);vertical-align:top;padding:.25rem .45rem;line-height:1.3}._table_1rpcj_160 th{color:var(--ink-muted);font-weight:600;font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.03em}._state_1rpcj_189{border:1px dashed var(--border);border-radius:var(--radius);padding:var(--space-5);color:var(--ink-muted)}._error_1rpcj_195{border-color:var(--bad);color:var(--bad)}._stale_1rpcj_199{color:var(--warning);font-size:.85rem}._statusActions_1rpcj_203{align-items:center;gap:.6rem;display:flex}._tickButton_1rpcj_208{border:1px solid var(--accent);color:var(--accent-light);background:#2dd4bf24;font-weight:700}._tickButton_1rpcj_208:hover:not(:disabled){border-color:var(--accent-light);background:#2dd4bf3d}._pauseControl_1rpcj_218{border:1px solid var(--bad);color:var(--warning);border-radius:var(--radius-sm);font-size:var(--text-sm);background:#dc262629;align-items:center;gap:.4rem;padding:.2rem .55rem;font-weight:700;display:inline-flex}._pauseControl_1rpcj_218 button{appearance:none;border:1px solid var(--bad);border-radius:var(--radius-sm);color:inherit;font:inherit;font-size:var(--text-xs);cursor:pointer;background:0 0;padding:.1rem .4rem;font-weight:700}._pauseControl_1rpcj_218 button:hover:not(:disabled){background:#dc26263d}._pauseControl_1rpcj_218 button:disabled{opacity:.62;cursor:wait}._pagination_1rpcj_249{justify-content:flex-end;gap:.6rem;margin-block:1rem;display:flex}._live_1rpcj_255{min-height:1.4rem}._header_1rpcj_6 ._live_1rpcj_255{color:var(--warning)}._json_1rpcj_261{background:var(--surface-inset);color:var(--ink);padding:var(--space-3);border-radius:var(--radius-sm);font-family:var(--font-mono);font-size:.75rem;overflow:auto}._chip_1rpcj_270{background:var(--border);color:var(--ink);border-radius:var(--radius-sm);font-size:var(--text-xs);align-items:center;gap:.25rem;margin-right:.2rem;padding:.05rem .35rem;display:inline-flex}._chipOutline_1rpcj_281{border:1px solid var(--border-strong);color:var(--ink-muted);background:0 0}._chipOutline_1rpcj_281._good_1rpcj_123{color:var(--good)}._chipOutline_1rpcj_281._warning_1rpcj_126{color:var(--warning)}._chipOutline_1rpcj_281._bad_1rpcj_99{color:var(--bad)}._chipOutline_1rpcj_281._cold_1rpcj_132{color:#7dd3fc}._tile_1rpcj_298{background:var(--surface-panel);border-radius:var(--radius);min-width:120px;padding:.6rem .9rem}._tileValue_1rpcj_304{font-size:1.3rem;font-weight:700}._tileLabel_1rpcj_308{color:var(--ink-muted);font-size:var(--text-xs);text-transform:uppercase}@media (width<=42rem){._header_1rpcj_6{padding-inline:var(--space-3)}._nav_1rpcj_46{padding-inline:var(--space-2)}._main_1rpcj_77{width:100%;padding:var(--space-4) var(--space-2) 0}._pageHeader_1rpcj_82{flex-direction:column;align-items:start}}._boardControls_a415m_1{justify-content:flex-end;margin-bottom:.75rem;display:flex}._boardControls_a415m_1 label{color:var(--ink-muted);font-size:var(--text-sm);align-items:center;gap:.4rem;font-weight:600;display:flex}._boardControls_a415m_1 select{background:var(--surface-panel);border:1px solid var(--border);border-radius:var(--radius-sm);min-height:2.1rem;color:var(--ink);font:inherit;padding-inline:.5rem}._boardControls_a415m_1 select:focus{border-color:var(--accent);outline:none;box-shadow:0 0 0 3px #2dd4bf26}._swimlanes_a415m_28{gap:1rem;display:grid}._swimlaneHeader_a415m_32{font-size:var(--text-sm);text-transform:uppercase;letter-spacing:.04em;color:var(--ink-muted);margin:0 0 .4rem;font-weight:700}._swimlaneToggle_a415m_40{justify-content:space-between;align-items:center;gap:var(--space-2);width:100%;color:inherit;cursor:pointer;font:inherit;text-align:left;background:0 0;border:0;padding:0;display:flex}._swimlaneToggle_a415m_40:hover,._swimlaneToggle_a415m_40:focus-visible{color:var(--accent-light)}._swimlaneToggle_a415m_40:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}._board_a415m_1{grid-template-columns:repeat(5,minmax(0,1fr));align-items:stretch;gap:.6rem;display:grid;overflow-x:auto}._column_a415m_69{background:var(--surface-panel);border-radius:var(--radius);flex-direction:column;min-height:200px;padding:.5rem;display:flex}._columnHeader_a415m_77{justify-content:space-between;align-items:center;gap:var(--space-2);margin:.2rem .4rem .5rem;display:flex}._column_a415m_69 h2{font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.04em;color:var(--ink-muted);margin:0}._cards_a415m_91{gap:.5rem;margin:0;padding:0;list-style:none;display:grid}._card_a415m_91{background:var(--surface-card);border:1px solid var(--border);border-left:3px solid var(--border-strong);font-size:var(--text-sm);border-radius:.5rem;overflow:hidden}._card_a415m_91:hover,._card_a415m_91:focus-within{border-color:var(--accent)}._cardLink_a415m_110{min-height:100%;color:var(--ink);transition:background var(--motion-fast) ease;padding:.5rem;text-decoration:none;display:block}._cardLink_a415m_110:hover{background:var(--surface-inset);text-decoration:none}._cardLink_a415m_110:focus-visible{outline:2px solid var(--focus-ring);outline-offset:-2px}._cardRef_a415m_126{color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-xs);margin-bottom:.15rem;display:block}._cardTitle_a415m_133{color:var(--ink);margin-bottom:.25rem;font-weight:600;display:block}._cardMeta_a415m_139{flex-wrap:wrap;align-items:center;gap:.3rem;margin-top:.2rem;display:flex}._metaIcon_a415m_146{flex:none;width:.75rem;height:.75rem}._cardStats_a415m_151{color:var(--ink-muted);margin-top:.3rem;font-size:.7rem}._childRun_a415m_156{border-radius:var(--radius-sm);background:var(--surface-inset);border:1px solid var(--border);color:var(--ink-muted);font-size:var(--text-xs);grid-template-columns:auto 1fr;align-items:center;gap:.25rem .45rem;margin-top:.45rem;padding:.4rem;display:grid}._childRunDot_a415m_169{background:var(--accent);border-radius:50%;width:.48rem;height:.48rem;box-shadow:0 0 0 3px #2dd4bf24}._childRunTitle_a415m_176{color:var(--ink);font-weight:650}._childRunMeta_a415m_180{color:var(--ink-muted)}._columnToggle_a415m_183{border:1px solid var(--border-strong);border-radius:var(--radius-sm);width:1.65rem;height:1.65rem;color:var(--ink-muted);cursor:pointer;font-size:var(--text-base);background:0 0;flex:none;justify-content:center;align-items:center;line-height:1;display:none}._finishedColumnToggle_a415m_198{display:inline-flex}._columnToggle_a415m_183:hover{border-color:var(--accent);color:var(--accent-light)}._filters_a415m_205{flex-wrap:wrap;align-items:end;gap:.6rem;margin-bottom:1rem;display:flex}._filters_a415m_205 label{color:var(--ink-muted);font-weight:600;font-size:var(--text-sm);gap:.25rem;display:grid}._filters_a415m_205 input,._filters_a415m_205 select{background:var(--surface-panel);border:1px solid var(--border);border-radius:var(--radius-sm);min-height:2.35rem;color:var(--ink);font:inherit;padding-inline:.55rem}._filters_a415m_205 input:focus,._filters_a415m_205 select:focus{border-color:var(--accent);outline:none;box-shadow:0 0 0 3px #2dd4bf26}._detail_a415m_235{gap:var(--space-3);display:grid}._detail_a415m_235>h2{margin:0}._summary_a415m_242{font-size:var(--text-sm);grid-template-columns:max-content 1fr;align-items:baseline;gap:.35rem .7rem;margin:0;display:grid}._summary_a415m_242 dt{color:var(--ink-muted);margin:0}._summary_a415m_242 dd{overflow-wrap:anywhere;min-width:0;margin:0}._overviewLayout_a415m_259{gap:var(--space-5);grid-template-columns:minmax(0,2fr) minmax(0,1fr);align-items:start;display:grid}._overviewSidebar_a415m_265{gap:var(--space-3);grid-column:2;min-width:0;display:grid}._overviewMain_a415m_271{grid-area:1/1;min-width:0}._sidebarSectionTitle_a415m_276{font-size:var(--text-sm)}._resourceList_a415m_279{margin:0 0 var(--space-4);gap:var(--space-2);flex-direction:column;padding:0;list-style:none;display:flex}._resourceCard_a415m_287{padding:var(--space-2) var(--space-3);border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--surface-card);color:inherit;flex-direction:column;gap:.35rem;text-decoration:none;display:flex}a._resourceCard_a415m_287:hover{border-color:var(--accent);text-decoration:none}._resourceCardTop_a415m_302{align-items:center;gap:var(--space-2);display:flex}._resourceCardIcon_a415m_307{color:var(--ink-muted);flex:none}._resourceCardTitle_a415m_311{min-width:0;font-weight:600;font-size:var(--text-sm);text-overflow:ellipsis;white-space:nowrap;flex:auto;overflow:hidden}._resourceCardExt_a415m_320{color:var(--ink-muted);opacity:0;flex:none}._resourceCard_a415m_287:hover ._resourceCardExt_a415m_320{opacity:1}._resourceCardMeta_a415m_328{font-size:var(--text-sm);padding-left:calc(18px + var(--space-2));flex-wrap:wrap;align-items:baseline;gap:.4rem;display:flex}._resourceId_a415m_336{color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-xs);overflow-wrap:anywhere}._eventList_a415m_342{flex-direction:column;gap:.45rem;margin:1rem 0;padding:0;list-style:none;display:flex}._event_a415m_342{background:var(--surface-card);border:1px solid var(--border);border-radius:var(--radius-sm);min-height:2.25rem;overflow:hidden}._event_a415m_342:hover{border-color:var(--border-strong)}._event_a415m_342 time{color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-xs)}._eventType_a415m_365{font-weight:650;font-size:var(--text-sm);text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}._eventId_a415m_373{color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-xs);text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}._toolbar_a415m_382{flex-wrap:wrap;align-items:center;gap:.7rem;display:flex}._modalBackdrop_a415m_388{background:var(--brand-darker);padding:var(--space-4);z-index:10;place-items:center;display:grid;position:fixed;inset:0}._modal_a415m_388{background:var(--surface-panel);border:1px solid var(--border-strong);box-shadow:var(--shadow);color:var(--ink);border-radius:var(--radius);width:100%;max-width:none;height:100%;max-height:none;padding:var(--space-4);overflow:auto}._modalHeader_a415m_411{justify-content:space-between;align-items:center;gap:var(--space-3);margin-bottom:var(--space-3);display:flex}._modalRef_a415m_418{color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-sm)}._modalClose_a415m_423{appearance:none;border:1px solid var(--border-strong);border-radius:var(--radius-sm);background:var(--surface-card);color:var(--ink);cursor:pointer;font:inherit;font-size:var(--text-sm);padding:.35rem .7rem;font-weight:700}._modalClose_a415m_423:hover{border-color:var(--accent-light);color:var(--accent-light)}._tiles_a415m_439{margin-bottom:var(--space-4);flex-wrap:wrap;gap:.6rem;display:flex}@media (width<=42rem){._overviewLayout_a415m_259{grid-template-columns:minmax(0,1fr)}._overviewSidebar_a415m_265,._overviewMain_a415m_271{grid-area:auto/1}._board_a415m_1{display:block;overflow:visible}._column_a415m_69{min-height:unset;margin-bottom:.75rem}._columnToggle_a415m_183{display:inline-flex}._eventId_a415m_373{display:none}._modalBackdrop_a415m_388{background:0 0;padding:0;display:block;position:static}._modal_a415m_388{box-shadow:none;border:0;width:auto;height:auto;max-height:none;padding:0}}@media (prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important}}._transcript_a415m_490{margin:var(--space-3) 0 0;padding:0;list-style:none}._transcriptEntry_a415m_495{background:var(--surface-inset);border:1px solid var(--border);border-radius:var(--radius-sm);margin-bottom:.75rem;overflow:hidden}._transcriptHead_a415m_502{gap:var(--space-3);color:var(--ink-muted);background:var(--surface-panel);border-bottom:1px solid var(--border);font-family:var(--font-mono);font-size:var(--text-xs);padding:.45rem .6rem;display:flex}._transcriptText_a415m_512{white-space:pre-wrap;font-family:var(--font-mono);background:0 0;margin:0;padding:.6rem;font-size:.76rem;overflow-x:auto}._transcriptLayout_a415m_521{gap:var(--space-4);margin-top:var(--space-3);grid-template-columns:minmax(15rem,22rem) minmax(0,1fr);display:grid}._transcriptGroups_a415m_527{align-content:start;align-self:start;gap:var(--space-2);margin:0;padding:0;list-style:none;display:grid}._transcriptGroup_a415m_527{width:100%;padding:var(--space-2);color:var(--ink);text-align:left;background:var(--surface-card);border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;font:inherit;font-size:var(--text-xs);overflow-wrap:anywhere;gap:.2rem;display:grid}._transcriptGroup_a415m_527:hover,._transcriptGroup_a415m_527[aria-pressed=true]{border-color:var(--accent);background:var(--surface-inset)}._transcriptConversation_a415m_556{min-width:0}._transcriptFilters_a415m_559{align-items:center;gap:var(--space-3);flex-wrap:wrap;display:flex}._transcriptFilters_a415m_559 label{align-items:center;gap:var(--space-2);color:var(--ink-muted);font-size:var(--text-sm);display:flex}._transcriptFilters_a415m_559 select{color:var(--ink);background:var(--surface-panel);border:1px solid var(--border);border-radius:var(--radius-sm);font:inherit}._transcriptInput_a415m_579,._transcriptAgent_a415m_580{border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden}._transcriptInput_a415m_579{background:var(--surface-inset);border-left:3px solid var(--accent);margin-left:12%}._transcriptAgent_a415m_580{background:var(--surface-card);border-left:3px solid var(--border-strong);margin-right:12%}._transcriptRunSeparator_a415m_595{align-items:center;gap:var(--space-2);margin:var(--space-3) 0;color:var(--ink-muted);font-family:var(--font-mono);font-size:var(--text-xs);display:flex}._transcriptRunSeparator_a415m_595:before,._transcriptRunSeparator_a415m_595:after{content:"";background:var(--border);flex:1;height:1px}._eventButton_a415m_612{width:100%;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;grid-template-columns:minmax(11rem,14rem) minmax(0,1fr) minmax(8rem,18rem) 2.4rem;align-items:center;gap:.65rem;padding:.45rem .65rem;display:grid}._eventType_a415m_365{text-overflow:clip;white-space:normal;overflow-wrap:anywhere;overflow:visible}@media (width<=42rem){._transcriptLayout_a415m_521{grid-template-columns:minmax(0,1fr)}._transcriptInput_a415m_579,._transcriptAgent_a415m_580{margin-inline:0}._eventButton_a415m_612{grid-template-rows:auto auto;grid-template-columns:minmax(0,1fr) 2.2rem}._eventType_a415m_365{text-overflow:ellipsis;white-space:nowrap;overflow-wrap:normal;grid-area:2/1/auto/-1;overflow:hidden}}._eventChevron_a415m_652{color:var(--ink-muted);justify-self:end}._eventPayload_a415m_656{border-top:1px solid var(--border);overflow-x:auto}._eventPayload_a415m_656 pre{padding:var(--space-3);white-space:pre;margin:0}._tabs_a415m_665{padding:.4rem var(--space-4) 0;border-bottom:1px solid var(--border);background:0 0;gap:.25rem;margin:0;display:flex}._tabs_a415m_665 button{color:var(--nav-ink-idle);font:inherit;white-space:nowrap;cursor:pointer;transition:color var(--motion-fast) ease;background:0 0;border:0;border-bottom:2px solid #0000;margin-bottom:-1px;padding:.4rem .7rem .45rem}._tabs_a415m_665 button:hover{color:var(--ink-inverse)}._tabs_a415m_665 button[aria-selected=true]{color:var(--accent-light);border-bottom-color:var(--accent);font-weight:700}._actionBar_a415m_694{gap:var(--space-2);flex-wrap:wrap;align-items:center;display:flex}._dangerButton_a415m_700{border-color:var(--bad);color:var(--bad)}:root{--wake-ink-950:#14161a;--wake-ink-900:#1a1d23;--wake-ink-850:#22262e;--wake-ink-800:#101216;--wake-ink-700:#2c313a;--wake-ink-600:#3a4150;--wake-grey-300:#cbd5e1;--wake-grey-400:#9aa2ad;--wake-grey-100:#e8e8e8;--wake-white:#fff;--wake-teal-700:#103a37;--wake-teal-650:#134e4a;--wake-teal-600:#0f766e;--wake-mint-400:#2dd4bf;--wake-mint-300:#5eead4;--wake-green-300:#7fe3a3;--wake-green-900:#1f3d2c;--wake-blue-300:#7fb3ff;--wake-blue-900:#1f3350;--wake-purple-300:#c79bff;--wake-purple-900:#2d1f50;--wake-amber-300:#ffcf7f;--wake-amber-900:#4a3510;--wake-red-300:#ff8f7f;--wake-red-900:#3d1f1f;--wake-slate-400:#9aa2ad;--wake-slate-900:#252830;--surface:var(--wake-ink-950);--surface-panel:var(--wake-ink-900);--surface-card:var(--wake-ink-850);--surface-inset:var(--wake-ink-800);--border:var(--wake-ink-700);--border-strong:var(--wake-ink-600);--ink:var(--wake-grey-100);--ink-muted:var(--wake-grey-400);--ink-inverse:var(--wake-white);--brand:var(--wake-teal-600);--brand-dark:var(--wake-teal-650);--brand-darker:var(--wake-teal-700);--nav-ink-idle:var(--wake-grey-300);--accent:var(--wake-mint-400);--accent-light:var(--wake-mint-300);--good:var(--wake-green-300);--warning:var(--wake-amber-300);--bad:var(--wake-red-300);--cond-ready-fg:var(--wake-green-300);--cond-ready-bg:var(--wake-green-900);--cond-scheduled-fg:var(--wake-blue-300);--cond-scheduled-bg:var(--wake-blue-900);--cond-active-fg:var(--wake-purple-300);--cond-active-bg:var(--wake-purple-900);--cond-needs-input-fg:var(--wake-amber-300);--cond-needs-input-bg:var(--wake-amber-900);--cond-error-fg:var(--wake-red-300);--cond-error-bg:var(--wake-red-900);--cond-finished-fg:var(--wake-slate-400);--cond-finished-bg:var(--wake-slate-900);--space-2:.5rem;--space-3:.75rem;--space-4:1rem;--space-5:1.5rem;--space-6:2rem;--radius:.65rem;--radius-sm:.4rem;--shadow:0 1px 3px #00000059;--focus-ring:var(--wake-mint-300);--font-sans:Inter, ui-sans-serif, system-ui, sans-serif;--font-mono:ui-monospace, SFMono-Regular, Consolas, monospace;--text-xs:.72rem;--text-sm:.8rem;--text-base:1rem;--text-lg:1.15rem;--content-width:76rem;--motion-fast:.12s}*{box-sizing:border-box}html{color:var(--ink);background:var(--surface);font-family:var(--font-sans);line-height:1.5}body{margin:0}a{color:var(--accent-light);text-underline-offset:.18em;text-decoration:none}a:hover{text-decoration:underline}button,input,select{font:inherit}:focus-visible{outline:3px solid var(--focus-ring);outline-offset:3px}@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}