ftown-bridge 0.19.37 → 0.19.39

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 (89) hide show
  1. package/dist/browser-access.js +2 -1
  2. package/dist/browser-access.js.map +1 -1
  3. package/dist/centrifugo-client.d.ts +1 -4
  4. package/dist/centrifugo-client.js +0 -29
  5. package/dist/centrifugo-client.js.map +1 -1
  6. package/dist/command-rpc.d.ts +1 -3
  7. package/dist/command-rpc.js +20 -74
  8. package/dist/command-rpc.js.map +1 -1
  9. package/dist/create-ftown-session.d.ts +0 -1
  10. package/dist/create-ftown-session.js +7 -13
  11. package/dist/create-ftown-session.js.map +1 -1
  12. package/dist/ftown-home.js +2 -2
  13. package/dist/ftown-sessions-cli.js +5 -340
  14. package/dist/ftown-sessions-cli.js.map +1 -1
  15. package/dist/harness-registry.d.ts +3 -22
  16. package/dist/harness-registry.js +3 -17
  17. package/dist/harness-registry.js.map +1 -1
  18. package/dist/index.js +10 -65
  19. package/dist/index.js.map +1 -1
  20. package/dist/install-ftown-skill.d.ts +5 -0
  21. package/dist/install-ftown-skill.js +91 -9
  22. package/dist/install-ftown-skill.js.map +1 -1
  23. package/dist/local-api-server.d.ts +1 -18
  24. package/dist/local-api-server.js +10 -108
  25. package/dist/local-api-server.js.map +1 -1
  26. package/dist/provider-env-store.js +1 -1
  27. package/dist/provider-env-store.js.map +1 -1
  28. package/dist/retired-automation.d.ts +14 -0
  29. package/dist/retired-automation.js +18 -0
  30. package/dist/retired-automation.js.map +1 -0
  31. package/dist/retired-skill-assets.d.ts +2 -0
  32. package/dist/retired-skill-assets.js +21 -0
  33. package/dist/retired-skill-assets.js.map +1 -0
  34. package/dist/session-controller.d.ts +2 -1
  35. package/dist/session-controller.js +5 -1
  36. package/dist/session-controller.js.map +1 -1
  37. package/dist/session-resurrection.d.ts +8 -2
  38. package/dist/session-resurrection.js +53 -17
  39. package/dist/session-resurrection.js.map +1 -1
  40. package/dist/solo/contract.d.ts +1 -1
  41. package/dist/solo/contract.js +2 -12
  42. package/dist/solo/contract.js.map +1 -1
  43. package/dist/solo/hub-manager.js +1 -1
  44. package/dist/terminal-pump.d.ts +1 -2
  45. package/dist/terminal-pump.js +1 -2
  46. package/dist/terminal-pump.js.map +1 -1
  47. package/dist/types.d.ts +4 -112
  48. package/dist/wire-types.d.ts +4 -48
  49. package/dist/wire-types.js +2 -0
  50. package/dist/wire-types.js.map +1 -1
  51. package/package.json +1 -2
  52. package/pi-extension/API.md +4 -12
  53. package/pi-extension/ftown.js +0 -138
  54. package/skills/ftown/SKILL.md +5 -27
  55. package/skills/ftown/agents/openai.yaml +2 -2
  56. package/skills/ftown/references/orchestrator.md +10 -65
  57. package/skills/ftown/references/sessions.md +3 -16
  58. package/skills/ftown/references/workflows.md +1 -3
  59. package/dist/loop-controller.d.ts +0 -87
  60. package/dist/loop-controller.js +0 -133
  61. package/dist/loop-controller.js.map +0 -1
  62. package/dist/loop-run-store.d.ts +0 -10
  63. package/dist/loop-run-store.js +0 -153
  64. package/dist/loop-run-store.js.map +0 -1
  65. package/dist/loop-schedule.d.ts +0 -18
  66. package/dist/loop-schedule.js +0 -35
  67. package/dist/loop-schedule.js.map +0 -1
  68. package/dist/loop-scheduler.d.ts +0 -148
  69. package/dist/loop-scheduler.js +0 -543
  70. package/dist/loop-scheduler.js.map +0 -1
  71. package/dist/loop-store.d.ts +0 -38
  72. package/dist/loop-store.js +0 -153
  73. package/dist/loop-store.js.map +0 -1
  74. package/dist/loop-validation.d.ts +0 -15
  75. package/dist/loop-validation.js +0 -91
  76. package/dist/loop-validation.js.map +0 -1
  77. package/skills/factory/SKILL.md +0 -198
  78. package/skills/factory/factory-template/bin/dispatch.py +0 -478
  79. package/skills/factory/factory-template/factory.yaml +0 -43
  80. package/skills/factory/factory-template/skills/_protocol.md +0 -105
  81. package/skills/factory/factory-template/skills/design.md +0 -260
  82. package/skills/factory/factory-template/skills/digest.md +0 -78
  83. package/skills/factory/factory-template/skills/groom.md +0 -243
  84. package/skills/factory/factory-template/skills/implement.md +0 -316
  85. package/skills/factory/factory-template/skills/pr.md +0 -221
  86. package/skills/factory/factory-template/skills/qa.md +0 -226
  87. package/skills/factory/factory-template/skills/review.md +0 -232
  88. package/skills/factory/factory-template/skills/triage.md +0 -157
  89. package/skills/ftown/references/loops.md +0 -93
@@ -1,543 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import { computeNextRun, isDue } from './loop-schedule.js';
3
- import { renderRawLogToText } from './terminal-manager.js';
4
- import { pruneLoopRunRecords, recordForSession, upsertLoopRunRecord, } from './loop-run-store.js';
5
- import { listLoops, mutateLoopRuntime } from './loop-store.js';
6
- /** Base tick cadence; also the finalize grace so a just-spawned PTY is not mistaken for exited. */
7
- export const LOOP_TICK_INTERVAL_MS = 30_000;
8
- const iso = (ms) => new Date(ms).toISOString();
9
- /**
10
- * Runs child_process.exec and normalizes the result to { stdout, stderr,
11
- * exitCode }. Never rejects — the exit code is the signal (a timeout maps to
12
- * 124, any other failure to the real exit code or 1).
13
- *
14
- * The hard budget is enforced by SIGKILL on the whole process GROUP, NOT by
15
- * exec's built-in `timeout`. exec's timeout only sends SIGTERM to the spawned
16
- * `/bin/sh`; a detached grandchild that keeps the stdout pipe open, or a child
17
- * that traps SIGTERM, would keep the exec callback from ever firing — and since
18
- * the scheduler awaits every flight inside a single re-entrancy-guarded tick,
19
- * ONE such flight would wedge the entire scheduler permanently. Spawning
20
- * `detached` makes the child its own group leader, so `kill(-pid, SIGKILL)`
21
- * takes down the whole tree and the flight can never exceed its budget.
22
- */
23
- export function runFlightCommand(command, cwd, timeoutMs = 30_000, extraEnv) {
24
- const env = extraEnv ? { ...process.env, ...extraEnv } : process.env;
25
- const MAX_CAPTURE = 1024 * 1024;
26
- return new Promise((resolve) => {
27
- let settled = false;
28
- let stdout = '';
29
- let stderr = '';
30
- let exitCode = null;
31
- let killedByTimeout = false;
32
- let exitGrace;
33
- // Own process group (detached ⇒ the sh is its own group leader) so the hard
34
- // timeout can SIGKILL the WHOLE tree, including a grandchild that outlived
35
- // its parent's SIGTERM or kept the stdout pipe open. This is what stops one
36
- // runaway flight from wedging the awaiting tick forever.
37
- const child = spawn('/bin/sh', ['-c', command], {
38
- cwd: cwd ?? process.cwd(),
39
- env,
40
- detached: true,
41
- });
42
- const settle = () => {
43
- if (settled)
44
- return;
45
- settled = true;
46
- clearTimeout(hardTimer);
47
- clearTimeout(exitGrace);
48
- // Release the pipe read-ends so a still-open grandchild write-end cannot
49
- // keep the bridge's event loop (or the test runner) alive after we return.
50
- child.stdout?.destroy();
51
- child.stderr?.destroy();
52
- child.unref?.();
53
- resolve({ stdout, stderr, exitCode: killedByTimeout ? 124 : (exitCode ?? 1) });
54
- };
55
- const recordExit = (code, signal) => {
56
- if (exitCode === null)
57
- exitCode = typeof code === 'number' ? code : signal ? 1 : 0;
58
- };
59
- child.stdout?.on('data', (d) => {
60
- if (stdout.length < MAX_CAPTURE)
61
- stdout += d.toString('utf8');
62
- });
63
- child.stderr?.on('data', (d) => {
64
- if (stderr.length < MAX_CAPTURE)
65
- stderr += d.toString('utf8');
66
- });
67
- child.on('error', () => {
68
- if (exitCode === null)
69
- exitCode = 1;
70
- settle();
71
- });
72
- // Settle on 'exit' (the shell terminated), NOT 'close' (all stdio closed):
73
- // a backgrounded/detached grandchild keeps the pipe open, so 'close' may
74
- // never come. 'close' still wins the race when it fires first (full stdout);
75
- // otherwise a short grace after 'exit' lets the parent's own output flush.
76
- child.on('exit', (code, signal) => {
77
- recordExit(code, signal);
78
- exitGrace = setTimeout(settle, 150);
79
- exitGrace.unref?.();
80
- });
81
- child.on('close', (code, signal) => {
82
- recordExit(code, signal);
83
- settle();
84
- });
85
- const hardTimer = setTimeout(() => {
86
- killedByTimeout = true;
87
- if (child.pid) {
88
- try {
89
- process.kill(-child.pid, 'SIGKILL'); // whole process group
90
- }
91
- catch {
92
- try {
93
- child.kill('SIGKILL');
94
- }
95
- catch {
96
- /* already gone */
97
- }
98
- }
99
- }
100
- settle();
101
- }, timeoutMs);
102
- hardTimer.unref?.();
103
- });
104
- }
105
- /** Byte-accurate tail: keep the last `maxBytes` of a (possibly huge) terminal log. */
106
- function truncateTail(text, maxBytes) {
107
- const buf = Buffer.from(text, 'utf8');
108
- if (buf.length <= maxBytes)
109
- return text;
110
- return buf.subarray(buf.length - maxBytes).toString('utf8');
111
- }
112
- /**
113
- * Resolve a finished run to ok/error. ONLY a cleanly `completed` run is 'ok';
114
- * a missing record (removed/lost) OR a store status still stuck at
115
- * running/pending at finalize time (the process died without a clean status
116
- * transition) is a crash ⇒ 'error'. Never reports a crashed run as success.
117
- */
118
- function resolveRunStatus(run) {
119
- return run?.status === 'completed' ? 'ok' : 'error';
120
- }
121
- /**
122
- * The scheduled-loops engine. On each 30s tick it FINALIZES each loop's
123
- * in-flight run(s) (Phase A) before deciding whether to FIRE a new one
124
- * (Phase B). All side effects go through injected collaborators so it is
125
- * unit-testable without a live bridge, real fs or real timers (mirrors
126
- * workflow-runner.ts).
127
- *
128
- * Persistence rule: the scheduler NEVER writes a whole detached Loop back
129
- * across an await. Every runtime-field change goes through
130
- * store.mutateLoopRuntime (fresh-read → mutate → save), so a loop deleted or
131
- * user-edited during a long flight is neither resurrected nor clobbered.
132
- */
133
- export class LoopScheduler {
134
- store;
135
- runner;
136
- centrifugo;
137
- userId;
138
- spawnSession;
139
- removeSession;
140
- loops;
141
- runRecords;
142
- runFlight;
143
- now;
144
- /** Re-entrancy guard: tick N+1 never overlaps N. */
145
- tickRunning = false;
146
- /** Set once start() runs (after reconcileOnStart). kick() no-ops before this so an
147
- * early run_loop_now cannot trigger an un-reconciled tick that stampedes overdue loops. */
148
- started = false;
149
- /** Per-loop in-memory fire lock, shared by tick + run_loop_now/kick. */
150
- firingLoops = new Set();
151
- /** loopId -> (runSessionId -> fire-time ms). Every run THIS process spawned, so under
152
- * overlapPolicy:'allow' each concurrent run is finalized/postflighted/maxRuntime-checked
153
- * independently — not just the newest. Rebuilt lazily from the persisted primary on restart. */
154
- inFlight = new Map();
155
- timer;
156
- constructor(deps) {
157
- this.store = deps.store;
158
- this.runner = deps.runner;
159
- this.centrifugo = deps.centrifugo;
160
- this.userId = deps.userId;
161
- this.spawnSession = deps.spawnSession;
162
- this.removeSession = deps.removeSession;
163
- this.loops = deps.loops ?? { listLoops, mutateLoopRuntime };
164
- this.runRecords = deps.runRecords ?? { upsertLoopRunRecord, pruneLoopRunRecords };
165
- this.runFlight = deps.runFlight ?? runFlightCommand;
166
- this.now = deps.now ?? (() => Date.now());
167
- }
168
- start() {
169
- this.started = true;
170
- if (this.timer)
171
- return;
172
- this.timer = setInterval(() => {
173
- void this.tick();
174
- }, LOOP_TICK_INTERVAL_MS);
175
- }
176
- stop() {
177
- if (this.timer) {
178
- clearInterval(this.timer);
179
- this.timer = undefined;
180
- }
181
- }
182
- /** Immediate, guarded, out-of-band tick (used by run_loop_now). No-op until start()
183
- * has run, so a kick that races startup cannot fire before reconcileOnStart. */
184
- kick() {
185
- if (!this.started)
186
- return;
187
- if (!this.tickRunning)
188
- void this.tick();
189
- }
190
- /** Drop scheduler tracking for a deleted loop and stop any run it left alive, so a
191
- * just-deleted loop never leaks a live AI session with nothing left to finalize it. */
192
- onLoopDeleted(loop) {
193
- const ids = new Set();
194
- const tracked = this.inFlight.get(loop.id);
195
- if (tracked)
196
- for (const id of tracked.keys())
197
- ids.add(id);
198
- if (loop.lastStatus === 'running' && loop.lastSessionId)
199
- ids.add(loop.lastSessionId);
200
- for (const id of ids) {
201
- if (this.runner.isRunning(id))
202
- this.runner.stop(id);
203
- }
204
- this.inFlight.delete(loop.id);
205
- }
206
- /**
207
- * Missed-schedule policy, run once before the first tick: for every loop whose
208
- * nextRunAt is missing or already past, recompute it from now (skip missed
209
- * occurrences; never stampede overdue loops). runNowRequested is preserved so a
210
- * manual override survives a restart and still fires on the first tick. A loop
211
- * with a corrupt persisted schedule is skipped here (logged) and reported as an
212
- * error on its first fire.
213
- */
214
- async reconcileOnStart(now = this.now()) {
215
- for (const loop of this.loops.listLoops()) {
216
- try {
217
- const overdue = loop.nextRunAt ? Date.parse(loop.nextRunAt) <= now : true;
218
- if (!overdue)
219
- continue;
220
- const nextRunMs = computeNextRun(loop.schedule, now); // may throw on a corrupt cron
221
- await this.persist(loop.id, (l) => {
222
- l.nextRunAt = iso(nextRunMs);
223
- l.updatedAt = iso(now);
224
- });
225
- }
226
- catch (err) {
227
- console.error(`[LoopScheduler] reconcile failed for loop ${loop.id}:`, err);
228
- }
229
- }
230
- }
231
- async tick(now = this.now()) {
232
- if (this.tickRunning)
233
- return; // tick N+1 never overlaps N
234
- this.tickRunning = true;
235
- try {
236
- for (const loop of this.loops.listLoops()) {
237
- try {
238
- await this.processLoop(loop, now);
239
- }
240
- catch (err) {
241
- // One bad loop must not kill the tick (mirrors resurrectSessions).
242
- console.error(`[LoopScheduler] loop ${loop.id} failed:`, err);
243
- }
244
- }
245
- }
246
- finally {
247
- this.tickRunning = false;
248
- }
249
- }
250
- async processLoop(loop, now) {
251
- await this.finalizePhase(loop, now); // Phase A — finalize before fire
252
- await this.firePhase(loop, now); // Phase B
253
- }
254
- /** Phase A: finalize each in-flight run once its PTY is confirmed gone (past grace)
255
- * or over its per-run maxRuntime budget. Under 'allow' this walks every tracked run,
256
- * not just the newest, so none is orphaned. */
257
- async finalizePhase(loop, now) {
258
- this.ensureTracked(loop);
259
- const tracked = this.inFlight.get(loop.id);
260
- if (!tracked || tracked.size === 0)
261
- return;
262
- // Snapshot: finalizeRun mutates the map while we iterate.
263
- for (const [runId, startedMs] of [...tracked]) {
264
- const running = this.runner.isRunning(runId);
265
- const elapsed = now - startedMs;
266
- if (running && loop.maxRuntimeMs && elapsed > loop.maxRuntimeMs) {
267
- this.runner.stop(runId);
268
- await this.finalizeRun(loop, now, runId, true);
269
- }
270
- else if (!running && elapsed >= LOOP_TICK_INTERVAL_MS) {
271
- await this.finalizeRun(loop, now, runId, false);
272
- }
273
- // else: still running, or still inside the grace window — leave it.
274
- }
275
- }
276
- /** Seed the persisted primary run into in-memory tracking after a restart (when this
277
- * process has spawned nothing yet for the loop), so a run left 'running' by a prior
278
- * process is still finalized. */
279
- ensureTracked(loop) {
280
- if (loop.lastStatus !== 'running' || !loop.lastSessionId)
281
- return;
282
- const existing = this.inFlight.get(loop.id);
283
- if (existing && existing.size > 0)
284
- return; // already tracking this process's run(s)
285
- const parsed = Date.parse(loop.lastRunAt ?? '');
286
- const startedMs = Number.isNaN(parsed) ? this.now() : parsed;
287
- const map = existing ?? new Map();
288
- map.set(loop.lastSessionId, startedMs);
289
- this.inFlight.set(loop.id, map);
290
- }
291
- track(loopId, sessionId, startedMs) {
292
- const map = this.inFlight.get(loopId) ?? new Map();
293
- map.set(sessionId, startedMs);
294
- this.inFlight.set(loopId, map);
295
- }
296
- /** Phase B: fire the loop if due, honoring the per-loop lock and the overlap policy. */
297
- async firePhase(loop, now) {
298
- if (!isDue(loop, now))
299
- return;
300
- if (this.firingLoops.has(loop.id))
301
- return;
302
- // A previous run that has EXITED but was not yet finalized (still inside the
303
- // grace window) must be finalized BEFORE we decide to fire. Otherwise a
304
- // skip-policy loop double-fires in the grace window (isRunning is already
305
- // false, so the overlap guard below misses it) and the just-finished run is
306
- // orphaned — its finalize/postflight never runs.
307
- if (loop.lastStatus === 'running' && loop.lastSessionId && !this.runner.isRunning(loop.lastSessionId)) {
308
- await this.finalizeRun(loop, now, loop.lastSessionId, false);
309
- const fresh = this.loops.listLoops().find((l) => l.id === loop.id);
310
- if (!fresh)
311
- return; // deleted during finalize
312
- loop = fresh;
313
- }
314
- // Overlap guard: a skip-policy loop whose previous run is STILL alive advances
315
- // its schedule only — no new fire, no skipCount bump (overlap-skip is not a
316
- // preflight-skip).
317
- if (loop.overlapPolicy === 'skip' &&
318
- loop.lastStatus === 'running' &&
319
- loop.lastSessionId &&
320
- this.runner.isRunning(loop.lastSessionId)) {
321
- await this.persist(loop.id, (l) => {
322
- try {
323
- l.nextRunAt = iso(computeNextRun(l.schedule, now));
324
- }
325
- catch {
326
- /* corrupt schedule surfaces as an error on the fire path, not here */
327
- }
328
- l.runNowRequested = false;
329
- l.updatedAt = iso(now);
330
- });
331
- return;
332
- }
333
- this.firingLoops.add(loop.id);
334
- try {
335
- await this.fireLoop(loop, now);
336
- }
337
- finally {
338
- this.firingLoops.delete(loop.id);
339
- }
340
- }
341
- /** Advance the schedule up front (so failures/skips never stampede), then preflight → flight. */
342
- async fireLoop(loop, now) {
343
- // Compute the next fire first so a persisted-corrupt schedule is reported as an
344
- // error (with a bounded backoff) instead of silently re-throwing every tick.
345
- let nextRunMs;
346
- try {
347
- nextRunMs = computeNextRun(loop.schedule, now);
348
- }
349
- catch (err) {
350
- await this.persist(loop.id, (l) => {
351
- l.lastRunAt = iso(now);
352
- l.nextRunAt = iso(now + LOOP_TICK_INTERVAL_MS); // bounded backoff — no stampede
353
- l.runNowRequested = false;
354
- l.lastStatus = 'error';
355
- l.updatedAt = iso(now);
356
- });
357
- console.error(`[LoopScheduler] bad schedule for loop ${loop.id}:`, err);
358
- return;
359
- }
360
- try {
361
- let preflightOut = '';
362
- if (loop.preflight) {
363
- const r = await this.runFlight(loop.preflight.command, loop.workdir, loop.preflight.timeoutMs);
364
- preflightOut = r.stdout;
365
- if (r.exitCode !== 0) {
366
- const skippedAt = iso(now);
367
- const reasonSource = r.stderr.trim() || r.stdout.trim();
368
- const reasonFull = `Preflight exited with code ${r.exitCode}.${reasonSource ? ` ${reasonSource}` : ''}`;
369
- const reason = reasonFull.length > 512 ? `${reasonFull.slice(0, 511)}…` : reasonFull;
370
- // ABORT: skip (not error), no session, no run-node.
371
- const skipped = await this.persist(loop.id, (l) => {
372
- l.lastRunAt = skippedAt;
373
- l.nextRunAt = iso(nextRunMs);
374
- l.runNowRequested = false;
375
- l.lastStatus = 'skipped';
376
- l.skipCount += 1;
377
- l.lastSkipAt = skippedAt;
378
- l.lastSkipReason = reason;
379
- l.updatedAt = skippedAt;
380
- });
381
- if (skipped && loop.postflight?.runOnSkip) {
382
- await this.runPostflight(loop, { status: 'skipped', sessionId: '', output: '' });
383
- }
384
- return;
385
- }
386
- }
387
- const task = loop.task.replaceAll('{{preflight}}', preflightOut);
388
- const spawnInput = {
389
- shellType: loop.harness,
390
- prompt: task,
391
- workingDir: loop.workdir,
392
- model: loop.model,
393
- env: preflightOut ? { FTOWN_PREFLIGHT_OUTPUT: preflightOut } : undefined,
394
- loopId: loop.id,
395
- suppressBriefing: true, // no child/orchestrator briefing paragraph in the task
396
- name: `${loop.name} · ${iso(now)}`,
397
- // parentSessionId intentionally omitted — loopId is the sole grouping key.
398
- };
399
- if (loop.harness === 'shell') {
400
- // Loop shell runs are one-shot jobs, not interactive terminals. Persist
401
- // the task as prompt metadata, but execute it as the command and suppress
402
- // typed injection so the shell exits when the job is done.
403
- spawnInput.command = task;
404
- spawnInput.initialInput = '';
405
- }
406
- const session = await this.spawnSession(spawnInput);
407
- const updated = await this.persist(loop.id, (l) => {
408
- l.lastRunAt = iso(now);
409
- l.nextRunAt = iso(nextRunMs);
410
- l.runNowRequested = false;
411
- l.lastSessionId = session.id;
412
- l.lastStatus = 'running';
413
- l.runCount += 1;
414
- l.updatedAt = iso(now);
415
- });
416
- if (!updated) {
417
- // The loop was deleted during preflight/spawn: do not resurrect it and do
418
- // not leave an orphan run that nothing would ever finalize or prune.
419
- this.runner.stop(session.id);
420
- await this.removeSession(session.id).catch(() => undefined);
421
- return;
422
- }
423
- this.runRecords.upsertLoopRunRecord(recordForSession(updated, session, iso(now)));
424
- this.track(loop.id, session.id, now);
425
- }
426
- catch (err) {
427
- // A failure after the schedule was computed: record error + persist so the
428
- // loop resumes its cadence instead of stampede-retrying every tick.
429
- await this.persist(loop.id, (l) => {
430
- l.lastRunAt = iso(now);
431
- l.nextRunAt = iso(nextRunMs);
432
- l.runNowRequested = false;
433
- l.lastStatus = 'error';
434
- l.updatedAt = iso(now);
435
- });
436
- console.error(`[LoopScheduler] fire failed for loop ${loop.id}:`, err);
437
- }
438
- }
439
- /** Resolve one finished run to ok/error, update the loop badge (only if this is the
440
- * loop's tracked/latest run), then run postflight + retention for it. */
441
- async finalizeRun(loop, now, runId, forcedError) {
442
- const tracked = this.inFlight.get(loop.id);
443
- const startedMs = tracked?.get(runId) ?? Date.parse(loop.lastRunAt ?? iso(now));
444
- if (tracked) {
445
- tracked.delete(runId);
446
- if (tracked.size === 0)
447
- this.inFlight.delete(loop.id);
448
- }
449
- const run = runId ? await this.store.loadSession(runId) : null;
450
- const status = forcedError ? 'error' : resolveRunStatus(run);
451
- const rawOutput = runId ? await this.store.loadTerminalLog(runId) : '';
452
- // The pty log is raw terminal traffic (repaints, cursor moves, erase codes);
453
- // replay it through a headless terminal so we persist what the screen showed.
454
- const fullOutput = await renderRawLogToText(rawOutput);
455
- const output = truncateTail(fullOutput, 65_536);
456
- const outputBytes = Buffer.byteLength(fullOutput, 'utf8');
457
- const tailBytes = Buffer.byteLength(output, 'utf8');
458
- // Only the loop's most-recently-STARTED run (lastSessionId) drives the badge;
459
- // an older overlapping 'allow' run finalizes silently (still postflight +
460
- // retention) without flipping the badge away from a newer run's state.
461
- const updated = await this.persist(loop.id, (l) => {
462
- if (l.lastSessionId === runId)
463
- l.lastStatus = status;
464
- l.updatedAt = iso(now);
465
- });
466
- void updated;
467
- const baseRecord = run
468
- ? recordForSession(loop, run, Number.isFinite(startedMs) ? iso(startedMs) : run.createdAt)
469
- : {
470
- id: runId,
471
- loopId: loop.id,
472
- bridgeId: loop.bridgeId,
473
- sessionId: runId,
474
- name: `${loop.name} · ${runId}`,
475
- status,
476
- startedAt: Number.isFinite(startedMs) ? iso(startedMs) : iso(now),
477
- updatedAt: iso(now),
478
- harness: loop.harness,
479
- workdir: loop.workdir,
480
- task: loop.task,
481
- model: loop.model,
482
- sessionStatus: undefined,
483
- };
484
- this.runRecords.upsertLoopRunRecord({
485
- ...baseRecord,
486
- status,
487
- sessionStatus: run?.status,
488
- errorReason: forcedError ? 'max_runtime_exceeded' : run?.errorReason,
489
- updatedAt: iso(now),
490
- finishedAt: iso(now),
491
- durationMs: Math.max(0, now - (Number.isFinite(startedMs) ? startedMs : now)),
492
- logTail: output,
493
- logBytes: outputBytes,
494
- logTruncated: outputBytes > tailBytes,
495
- });
496
- if (loop.postflight) {
497
- await this.runPostflight(loop, { status, sessionId: runId, output });
498
- }
499
- await this.pruneRuns(loop);
500
- }
501
- async runPostflight(loop, ctx) {
502
- if (!loop.postflight)
503
- return;
504
- await this.runFlight(loop.postflight.command, loop.workdir, loop.postflight.timeoutMs, {
505
- FTOWN_RUN_STATUS: ctx.status,
506
- FTOWN_RUN_SESSION_ID: ctx.sessionId,
507
- FTOWN_RUN_OUTPUT: ctx.output,
508
- });
509
- }
510
- /** Keep the newest N run-sessions for this loop; prune older finished ones. */
511
- async pruneRuns(loop) {
512
- const keep = loop.retention.autoClearAfterRuns;
513
- if (keep == null)
514
- return;
515
- const runs = (await this.store.listSessions())
516
- .filter((s) => s.loopId === loop.id)
517
- .sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
518
- for (const run of runs.slice(keep)) {
519
- if (this.runner.isRunning(run.id))
520
- continue;
521
- if (run.id === loop.lastSessionId)
522
- continue;
523
- await this.removeSession(run.id, { onlyIfFinished: true });
524
- }
525
- this.runRecords.pruneLoopRunRecords(loop.id, keep, [loop.lastSessionId, ...runs.filter((run) => this.runner.isRunning(run.id)).map((run) => run.id)]);
526
- }
527
- async persist(id, fn) {
528
- const updated = this.loops.mutateLoopRuntime(id, fn);
529
- if (updated)
530
- await this.publish(updated);
531
- return updated;
532
- }
533
- async publish(loop) {
534
- try {
535
- await this.centrifugo.publishLoopUpdate(this.userId, loop);
536
- }
537
- catch (err) {
538
- // A UI-sync failure must never break the scheduler (matches session create).
539
- console.error(`[LoopScheduler] Failed to publish loop update for ${loop.id}:`, err);
540
- }
541
- }
542
- }
543
- //# sourceMappingURL=loop-scheduler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loop-scheduler.js","sourceRoot":"","sources":["../src/loop-scheduler.ts"],"names":[],"mappings":"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"}
@@ -1,38 +0,0 @@
1
- import type { Loop, LoopDraft } from './types.js';
2
- /** Inject the instance ".ftown home" once at startup (see module doc). */
3
- export declare function configureLoopStoreHome(home: string | undefined): void;
4
- export declare function listLoops(): Loop[];
5
- export declare function getLoop(id: string): Loop | undefined;
6
- /**
7
- * Mint a Loop from a client draft: fresh id/timestamps, zeroed counters, and a
8
- * nextRunAt computed from now — even when created disabled, so re-enabling has a
9
- * target. Throws (via computeNextRun) on a malformed cron expression.
10
- */
11
- export declare function createLoop(draft: LoopDraft): Loop;
12
- /**
13
- * Merge `patch` over an existing loop. id + createdAt are immutable; updatedAt is
14
- * bumped. A schedule change recomputes nextRunAt from now (the old target is
15
- * stale). Returns null when no loop has that id.
16
- */
17
- export declare function updateLoop(id: string, patch: Partial<LoopDraft>): Loop | null;
18
- export declare function deleteLoop(id: string): boolean;
19
- /** Insert a loop, or replace the existing one with the same id, in place. */
20
- export declare function upsertLoop(loop: Loop): void;
21
- /** The scheduler-owned runtime fields a tick is allowed to mutate. Everything
22
- * else on a Loop (name, schedule, enabled, task, retention, …) is user-owned
23
- * and edited exclusively through updateLoop. */
24
- export type LoopRuntimeMutator = (loop: Loop) => void;
25
- /**
26
- * Atomically apply a scheduler-owned mutation: reload the loop FRESH from disk,
27
- * apply `fn`, then save. Returns the merged loop, or null when the id no longer
28
- * exists (deleted concurrently) — the caller then skips its publish so a deleted
29
- * loop is never resurrected.
30
- *
31
- * The scheduler holds a detached Loop snapshot across long awaits (a 20s
32
- * preflight, an in-process spawn). Writing that whole stale snapshot back would
33
- * (a) resurrect a loop deleted mid-flight and (b) clobber a concurrent
34
- * update_loop patch to user-owned fields. Because `fn` runs on the
35
- * freshly-loaded record and touches only runtime fields, both hazards are gone:
36
- * a delete wins (null), and an unrelated enabled/schedule/task patch survives.
37
- */
38
- export declare function mutateLoopRuntime(id: string, fn: LoopRuntimeMutator): Loop | null;