Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Merge remote-tracking branch 'origin/main' into workspace-chat | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them. | 4 | import { answered, capReached, describeError, stoppedShort, tellStopped, withinTimeCap } from "./lifecycle.ts"; |
| Merge remote-tracking branch 'origin/main' into workspace-chat | 5 | |
| 6 | const quiet = { waitMs: 0 }; | |
| 7 | ||
| 8 | test("a stop that is told the first time is told once and logs nothing", async () => { | |
| 9 | const logged: unknown[][] = []; | |
| 10 | let calls = 0; | |
| 11 | const told = await tellStopped("checks", async () => void calls++, { ...quiet, log: (...data) => logged.push(data) }); | |
| 12 | assert.equal(told, true); | |
| 13 | assert.equal(calls, 1); | |
| 14 | assert.deepEqual(logged, []); | |
| 15 | }); | |
| 16 | ||
| 17 | test("a stop that fails once is tried again, and gets through", async () => { | |
| 18 | const logged: unknown[][] = []; | |
| 19 | let calls = 0; | |
| 20 | const told = await tellStopped( | |
| 21 | "queue", | |
| 22 | async () => { | |
| 23 | calls++; | |
| 24 | if (calls === 1) throw new Error("report_queue failed with status 500"); | |
| 25 | }, | |
| 26 | { ...quiet, log: (...data) => logged.push(data) }, | |
| 27 | ); | |
| 28 | assert.equal(told, true); | |
| 29 | assert.equal(calls, 2); | |
| 30 | assert.deepEqual(logged, []); | |
| 31 | }); | |
| 32 | ||
| 33 | test("a stop that cannot be told never throws, and is logged with what and why", async () => { | |
| 34 | const logged: unknown[][] = []; | |
| 35 | let calls = 0; | |
| 36 | const told = await tellStopped( | |
| 37 | "actions", | |
| 38 | async () => { | |
| 39 | calls++; | |
| 40 | throw new Error("job_report answered 503"); | |
| 41 | }, | |
| 42 | { ...quiet, log: (...data) => logged.push(data) }, | |
| 43 | ); | |
| 44 | assert.equal(told, false); | |
| 45 | assert.equal(calls, 2); | |
| 46 | assert.deepEqual(logged, [["sandbox stop not reported", "actions", "job_report answered 503"]]); | |
| 47 | }); | |
| 48 | ||
| 49 | test("whatever is thrown, even nothing, is logged as one line", async () => { | |
| 50 | const logged: unknown[][] = []; | |
| 51 | await tellStopped("plan", () => Promise.reject(undefined), { ...quiet, attempts: 1, log: (...data) => logged.push(data) }); | |
| 52 | assert.deepEqual(logged, [["sandbox stop not reported", "plan", "no reason given"]]); | |
| 53 | assert.equal(describeError("gone"), "gone"); | |
| 54 | assert.equal(describeError({ code: 7 }), '{"code":7}'); | |
| 55 | assert.equal(describeError(new TypeError("bad")), "bad"); | |
| 56 | }); | |
| 57 | ||
| 58 | test("a service that cannot answer is a failure; one that refuses is not", async () => { | |
| 59 | await answered("job_report", new Response("{}", { status: 200 })); | |
| 60 | // The deployments service's answer for a build that already finished. | |
| 61 | await answered("deploy fail", new Response("{}", { status: 404 })); | |
| 62 | await answered("job_report", new Response("{}", { status: 409 })); | |
| 63 | await assert.rejects(answered("job_report", new Response("worker threw", { status: 500 })), /job_report answered 500: worker threw/); | |
| 64 | await assert.rejects(answered("deploy fail", new Response("", { status: 503 })), /^Error: deploy fail answered 503$/); | |
| 65 | await assert.rejects(answered("job_report", new Response("slow down", { status: 429 })), /429/); | |
| 66 | }); | |
| 67 | ||
| 68 | test("a run inside its time cap is not stopped for inactivity", () => { | |
| 69 | const started = Date.UTC(2026, 9, 8, 12, 0, 0); | |
| 70 | const minute = 60_000; | |
| 71 | const grace = 180; | |
| 72 | // A 240-minute cap outlives the 100-minute sleepAfter. | |
| 73 | assert.equal(withinTimeCap(started, 240, started + 100 * minute, grace), true); | |
| 74 | assert.equal(withinTimeCap(started, 240, started + 242 * minute, grace), true); | |
| 75 | // Past the cap and its alarm's grace: the library may stop it. | |
| 76 | assert.equal(withinTimeCap(started, 240, started + 243 * minute, grace), false); | |
| 77 | assert.equal(withinTimeCap(started, 90, started + 100 * minute, grace), false); | |
| 78 | }); | |
| 79 | ||
| 80 | test("a sandbox with no cap or no start is stopped for inactivity as before", () => { | |
| 81 | const now = Date.now(); | |
| 82 | assert.equal(withinTimeCap(undefined, 90, now, 180), false); | |
| 83 | assert.equal(withinTimeCap(now - 1000, undefined, now, 180), false); | |
| 84 | assert.equal(withinTimeCap(now - 1000, null, now, 180), false); | |
| 85 | assert.equal(withinTimeCap(now - 1000, 0, now, 180), false); | |
| 86 | }); | |
| Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them. | 87 | |
| 88 | test("an earlier attempt's time cap does not stop the attempt running now", () => { | |
| 89 | const minute = 60_000; | |
| 90 | // Desktop release, Linux job: attempt 1 started 12:47:17 with a 60-minute | |
| 91 | // cap, so its alarm fired at 13:50:17, four minutes into attempt 3 in the | |
| 92 | // same Durable Object. | |
| 93 | const attempt3 = Date.UTC(2026, 9, 12, 13, 46, 15); | |
| 94 | const alarm = Date.UTC(2026, 9, 12, 13, 50, 17); | |
| 95 | assert.equal(capReached(attempt3, 60, alarm), false); | |
| 96 | // Its own alarm, at its cap and the alarm's grace, does stop it. | |
| 97 | assert.equal(capReached(attempt3, 60, attempt3 + 63 * minute), true); | |
| 98 | assert.equal(capReached(attempt3, 60, attempt3 + 60 * minute), true); | |
| 99 | assert.equal(capReached(attempt3, 60, attempt3 + 60 * minute - 1), false); | |
| 100 | }); | |
| 101 | ||
| 102 | test("a sandbox with no run or no cap is never stopped for time", () => { | |
| 103 | const now = Date.now(); | |
| 104 | assert.equal(capReached(undefined, 60, now), false); | |
| 105 | assert.equal(capReached(now - 120 * 60_000, undefined, now), false); | |
| 106 | assert.equal(capReached(now - 120 * 60_000, 0, now), false); | |
| 107 | }); | |
| 108 | ||
| 109 | test("a sandbox g1t stopped fails even when its exit code says 0", () => { | |
| 110 | assert.equal(stoppedShort(0, null), false); | |
| 111 | assert.equal(stoppedShort(0, "It ran into its time cap of 60 minutes."), true); | |
| 112 | assert.equal(stoppedShort(137, null), true); | |
| 113 | assert.equal(stoppedShort(1, "why"), true); | |
| 114 | }); |
This file's history is long; its oldest lines are credited to the oldest commit read.