@coreplane/switchboard 1.260.4 → 1.260.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/deploy/cloudflare/package.json +2 -2
- package/dist/assets/deploy/cloudflare-docs/package.json +1 -1
- package/dist/assets/deploy/cloudflare-memory/package.json +2 -2
- package/dist/assets/deploy/cloudflare-memory/worker.ts +222 -11
- package/dist/assets/deploy/cloudflare-resident/package.json +2 -2
- package/dist/assets/deploy/cloudflare-resident/worker.ts +124 -40
- package/dist/assets/deploy/cloudflare-sandbox/package.json +2 -2
- package/dist/assets/package-lock.json +712 -97
- package/dist/assets/package.json +4 -4
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/core/budgets.ts +10 -0
- package/dist/assets/src/core/coordinator/contract.ts +5 -0
- package/dist/assets/src/core/coordinator/driver.ts +155 -29
- package/dist/assets/src/core/pipelineStanding.ts +2 -0
- package/dist/assets/src/core/plane/decide.ts +19 -2
- package/dist/assets/src/core/provider.ts +380 -0
- package/dist/assets/src/core/refusal.ts +2 -1
- package/dist/assets/src/core/runEvents.ts +8 -0
- package/dist/assets/src/core/runLedger/decisions.ts +10 -1
- package/dist/assets/src/core/runLedger/types.ts +13 -1
- package/dist/assets/src/core/runRecord.ts +31 -0
- package/dist/assets/src/core/ship/contract.ts +2 -0
- package/dist/assets/src/core/ship/coordinator.ts +515 -30
- package/dist/assets/src/core/types.ts +6 -0
- package/dist/assets/src/execution/residentRebind.ts +15 -0
- package/dist/assets/src/execution/residentReuse.ts +23 -2
- package/dist/assets/web/dist/.vite/manifest.json +67 -67
- package/dist/assets/web/dist/assets/{DeliveryPage-BaemWKW8.js → DeliveryPage-BUJckSuW.js} +1 -1
- package/dist/assets/web/dist/assets/{HomePage-fgKQHLaO.js → HomePage-CBQpcnaW.js} +1 -1
- package/dist/assets/web/dist/assets/{PendingTurnRow-vuWiDzvz.js → PendingTurnRow-DIC86uWA.js} +1 -1
- package/dist/assets/web/dist/assets/{PlanePage-DXMJf8FB.js → PlanePage-CfrbMh8t.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentDetailPage-DdC8yU_S.js → ResidentDetailPage-C-SgOUTl.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentsIndexPage-CSM-KODv.js → ResidentsIndexPage-CiDhvXok.js} +1 -1
- package/dist/assets/web/dist/assets/RunFoldRow-DZ8huwUU.js +1 -0
- package/dist/assets/web/dist/assets/{RunRoutePage-jo6NZxsP.js → RunRoutePage-Cp6wicYc.js} +3 -3
- package/dist/assets/web/dist/assets/{RunsIndexPage-Dx1Y4sSq.js → RunsIndexPage-BPXtfwwx.js} +1 -1
- package/dist/assets/web/dist/assets/{ScheduledPage-BuB_NZyR.js → ScheduledPage-DbvP48HI.js} +1 -1
- package/dist/assets/web/dist/assets/{SettingsPage-Cn96jB6Q.js → SettingsPage-B87lZaiN.js} +1 -1
- package/dist/assets/web/dist/assets/{SilentTurn-D2cJfpEq.js → SilentTurn-CPED_XCV.js} +1 -1
- package/dist/assets/web/dist/assets/{StatusDot-DDHUCPVJ.js → StatusDot-CFLn-DZ2.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-B4VIHkAI.js → Tooltip-Dx9YBVEx.js} +1 -1
- package/dist/assets/web/dist/assets/{UnitRoutePage-C-2DfjLI.js → UnitRoutePage-d4A2X_o7.js} +1 -1
- package/dist/assets/web/dist/assets/budgets-BQgcCyvb.js +1 -0
- package/dist/assets/web/dist/assets/{dist-Cf1CmCSK.js → dist-Cn51vWpw.js} +11 -11
- package/dist/assets/web/dist/assets/{indexRow-Dxg7C8s9.js → indexRow-BsWcHHVh.js} +1 -1
- package/dist/assets/web/dist/assets/{main-DxPqaFu9.js → main-DYwRHh0F.js} +2 -2
- package/dist/assets/web/dist/assets/{sseReplay-BzoeVxZB.js → sseReplay-DGRDRLua.js} +1 -1
- package/dist/cli.js +2550 -924
- package/package.json +4 -4
- package/dist/assets/web/dist/assets/RunFoldRow-Csik3wD6.js +0 -1
- package/dist/assets/web/dist/assets/budgets-KNNkT4PZ.js +0 -1
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"@cloudflare/containers": "^0.3.7"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@cloudflare/workers-types": "^5.
|
|
24
|
+
"@cloudflare/workers-types": "^5.20260914.1",
|
|
25
25
|
"typescript": "^5.9.3",
|
|
26
26
|
"vitest": "^5.0.0",
|
|
27
|
-
"wrangler": "^4.
|
|
27
|
+
"wrangler": "^4.131.2"
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@cloudflare/vitest-pool-workers": "^0.22.0",
|
|
20
|
-
"@cloudflare/workers-types": "^5.
|
|
20
|
+
"@cloudflare/workers-types": "^5.20260914.1",
|
|
21
21
|
"typescript": "^5.9.3",
|
|
22
22
|
"vitest": "^4.1.11",
|
|
23
|
-
"wrangler": "^4.
|
|
23
|
+
"wrangler": "^4.131.2"
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -76,8 +76,10 @@ import {
|
|
|
76
76
|
phaseTransition,
|
|
77
77
|
reclaimPhase,
|
|
78
78
|
selectReclaim,
|
|
79
|
+
unreadInbox,
|
|
79
80
|
} from "../../src/core/runLedger/decisions.ts";
|
|
80
|
-
import { intakeReceiptRetentionMs, minutesToMs, PLANE } from "../../src/core/budgets.ts";
|
|
81
|
+
import { INTAKE_DELIVERY_CLAIM_MS, intakeReceiptRetentionMs, minutesToMs, PLANE } from "../../src/core/budgets.ts";
|
|
82
|
+
import { PROVIDER_FAILURE_CAUSES, type ProviderFailureCause } from "../../src/core/provider.ts";
|
|
81
83
|
import { holdBackgroundTask } from "./backgroundTasks.ts";
|
|
82
84
|
import {
|
|
83
85
|
causeOfClose,
|
|
@@ -1696,6 +1698,12 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1696
1698
|
);
|
|
1697
1699
|
CREATE INDEX IF NOT EXISTS intake_thread ON intake_receipts(thread_key, decided_at);
|
|
1698
1700
|
CREATE INDEX IF NOT EXISTS intake_prune ON intake_receipts(prune_after);
|
|
1701
|
+
CREATE TABLE IF NOT EXISTS intake_deliveries (
|
|
1702
|
+
receipt_key TEXT PRIMARY KEY,
|
|
1703
|
+
poster TEXT NOT NULL,
|
|
1704
|
+
claim_until INTEGER NOT NULL,
|
|
1705
|
+
delivered INTEGER NOT NULL
|
|
1706
|
+
);
|
|
1699
1707
|
`);
|
|
1700
1708
|
// The coordinator's parent records (run-history item 49): one row per
|
|
1701
1709
|
// instance, written by the bot at the instance's creation and read by the
|
|
@@ -1718,6 +1726,14 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1718
1726
|
updated_at INTEGER NOT NULL,
|
|
1719
1727
|
PRIMARY KEY (instance_id, unit)
|
|
1720
1728
|
);
|
|
1729
|
+
CREATE TABLE IF NOT EXISTS decision_record_reservations (
|
|
1730
|
+
repo TEXT NOT NULL,
|
|
1731
|
+
task_key TEXT NOT NULL,
|
|
1732
|
+
number TEXT NOT NULL,
|
|
1733
|
+
created_at INTEGER NOT NULL,
|
|
1734
|
+
PRIMARY KEY (repo, task_key),
|
|
1735
|
+
UNIQUE (repo, number)
|
|
1736
|
+
);
|
|
1721
1737
|
`);
|
|
1722
1738
|
// The thread events of a unit-owned thread (record 0051's reply-as-event rule): a
|
|
1723
1739
|
// sibling table of the unit rows, never a field on them — `putUnits`
|
|
@@ -1786,6 +1802,7 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1786
1802
|
side TEXT NOT NULL,
|
|
1787
1803
|
reported_at INTEGER NOT NULL,
|
|
1788
1804
|
generation TEXT NOT NULL,
|
|
1805
|
+
cause TEXT,
|
|
1789
1806
|
PRIMARY KEY (resident, name)
|
|
1790
1807
|
);
|
|
1791
1808
|
CREATE TABLE IF NOT EXISTS plane_endings (
|
|
@@ -1795,6 +1812,13 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1795
1812
|
at INTEGER NOT NULL
|
|
1796
1813
|
);
|
|
1797
1814
|
`);
|
|
1815
|
+
const planeLevelColumns = new Set(
|
|
1816
|
+
this.sql
|
|
1817
|
+
.exec<{ name: string }>(`PRAGMA table_info(plane_levels)`)
|
|
1818
|
+
.toArray()
|
|
1819
|
+
.map((column) => column.name),
|
|
1820
|
+
);
|
|
1821
|
+
if (!planeLevelColumns.has("cause")) this.sql.exec(`ALTER TABLE plane_levels ADD COLUMN cause TEXT`);
|
|
1798
1822
|
}
|
|
1799
1823
|
|
|
1800
1824
|
// ---- the orchestration plane (record 0064; orchestration-plane.md) ----------
|
|
@@ -1874,9 +1898,14 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1874
1898
|
.map((r) => [r.run_id, r.seq]),
|
|
1875
1899
|
);
|
|
1876
1900
|
const levels = this.sql
|
|
1877
|
-
.exec<{
|
|
1878
|
-
|
|
1879
|
-
|
|
1901
|
+
.exec<{
|
|
1902
|
+
resident: string;
|
|
1903
|
+
name: string;
|
|
1904
|
+
side: string;
|
|
1905
|
+
reported_at: number;
|
|
1906
|
+
generation: string;
|
|
1907
|
+
cause: string | null;
|
|
1908
|
+
}>(`SELECT * FROM plane_levels`)
|
|
1880
1909
|
.toArray()
|
|
1881
1910
|
.map((r): PlaneLevelRow => ({
|
|
1882
1911
|
resident: r.resident,
|
|
@@ -1884,6 +1913,9 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1884
1913
|
side: r.side as PlaneLevelRow["side"],
|
|
1885
1914
|
reportedAt: r.reported_at,
|
|
1886
1915
|
generation: r.generation,
|
|
1916
|
+
...(r.cause !== null && (PROVIDER_FAILURE_CAUSES as readonly string[]).includes(r.cause)
|
|
1917
|
+
? { cause: r.cause as ProviderFailureCause }
|
|
1918
|
+
: {}),
|
|
1887
1919
|
}));
|
|
1888
1920
|
return { queue, liveThreads, liveRuns, inboxSeqs, reservations, openWindows, levels };
|
|
1889
1921
|
}
|
|
@@ -1945,12 +1977,13 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1945
1977
|
this.sql.exec(`DELETE FROM plane_windows WHERE kind = ?`, w.window);
|
|
1946
1978
|
} else if (w.table === "plane_levels") {
|
|
1947
1979
|
this.sql.exec(
|
|
1948
|
-
`INSERT OR REPLACE INTO plane_levels (resident, name, side, reported_at, generation) VALUES (?, ?, ?, ?, ?)`,
|
|
1980
|
+
`INSERT OR REPLACE INTO plane_levels (resident, name, side, reported_at, generation, cause) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
1949
1981
|
w.row.resident,
|
|
1950
1982
|
w.row.name,
|
|
1951
1983
|
w.row.side,
|
|
1952
1984
|
w.row.reportedAt,
|
|
1953
1985
|
w.row.generation,
|
|
1986
|
+
w.row.cause ?? null,
|
|
1954
1987
|
);
|
|
1955
1988
|
} else if (w.table === "plane_findings") {
|
|
1956
1989
|
// A finding (record 0064, "Endings and the watches"): keyed by watch
|
|
@@ -2104,14 +2137,20 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
2104
2137
|
planeLevel(
|
|
2105
2138
|
post:
|
|
2106
2139
|
| { resident: string; name: "seat" | "memory" | "drain"; side: "below" | "above"; generation: string }
|
|
2107
|
-
| { provider: string; name: "provider"; side: "up" | "down" },
|
|
2140
|
+
| { provider: string; name: "provider"; side: "up" | "down"; cause?: ProviderFailureCause },
|
|
2108
2141
|
now: number,
|
|
2109
2142
|
): { admitted: number } {
|
|
2110
2143
|
// The model proxy's provider level (record 0064): `up` re-issues every
|
|
2111
2144
|
// held turn parked on the provider — the steers land as inbox writes in the
|
|
2112
2145
|
// decider's transaction — and walks anything queued on `provider_up`.
|
|
2113
2146
|
if (post.name === "provider") {
|
|
2114
|
-
const r = this.planeApply({
|
|
2147
|
+
const r = this.planeApply({
|
|
2148
|
+
kind: "provider_level",
|
|
2149
|
+
at: now,
|
|
2150
|
+
provider: post.provider,
|
|
2151
|
+
level: post.side,
|
|
2152
|
+
...(post.side === "down" && post.cause !== undefined ? { cause: post.cause } : {}),
|
|
2153
|
+
});
|
|
2115
2154
|
const admitted = r.effects.filter((e) => e.kind === "admit").length;
|
|
2116
2155
|
console.log(`[plane/level] provider ${post.provider} ${post.side} — ${admitted} admission(s)`);
|
|
2117
2156
|
return { admitted };
|
|
@@ -2601,6 +2640,71 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
2601
2640
|
return out;
|
|
2602
2641
|
}
|
|
2603
2642
|
|
|
2643
|
+
// ---- decision-record reservations (agent-ship item 16) ---------------------
|
|
2644
|
+
|
|
2645
|
+
/** One atomic durable allocation across bot processes. Existing unit and run
|
|
2646
|
+
* rows seed the claim set for reservations written before this ledger existed. */
|
|
2647
|
+
async reserveDecisionRecord(
|
|
2648
|
+
repo: string,
|
|
2649
|
+
taskKey: string,
|
|
2650
|
+
claimed: string[],
|
|
2651
|
+
existing: string | undefined,
|
|
2652
|
+
now: number,
|
|
2653
|
+
): Promise<{ number: string }> {
|
|
2654
|
+
let number = "";
|
|
2655
|
+
this.ctx.storage.transactionSync(() => {
|
|
2656
|
+
const prior = this.sql
|
|
2657
|
+
.exec<{ number: string }>(
|
|
2658
|
+
`SELECT number FROM decision_record_reservations WHERE repo = ? AND task_key = ?`,
|
|
2659
|
+
repo,
|
|
2660
|
+
taskKey,
|
|
2661
|
+
)
|
|
2662
|
+
.toArray()[0];
|
|
2663
|
+
if (prior !== undefined) {
|
|
2664
|
+
number = prior.number;
|
|
2665
|
+
return;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
const used = new Set(claimed);
|
|
2669
|
+
for (const row of this.sql
|
|
2670
|
+
.exec<{ number: string }>(`SELECT number FROM decision_record_reservations WHERE repo = ?`, repo)
|
|
2671
|
+
.toArray())
|
|
2672
|
+
used.add(row.number);
|
|
2673
|
+
for (const row of this.sql
|
|
2674
|
+
.exec<{ instance_json: string; unit_json: string }>(
|
|
2675
|
+
`SELECT i.json AS instance_json, u.json AS unit_json
|
|
2676
|
+
FROM coordinator_units u JOIN coordinator_instances i ON i.instance_id = u.instance_id`,
|
|
2677
|
+
)
|
|
2678
|
+
.toArray()) {
|
|
2679
|
+
const instance = JSON.parse(row.instance_json) as CoordinatorInstance;
|
|
2680
|
+
const unit = JSON.parse(row.unit_json) as CoordinatorUnit;
|
|
2681
|
+
if (instance.repo === repo && unit.record !== undefined) used.add(unit.record);
|
|
2682
|
+
}
|
|
2683
|
+
for (const row of this.sql
|
|
2684
|
+
.exec<{ summary_json: string }>(`SELECT summary_json FROM runs WHERE repo = ?`, repo)
|
|
2685
|
+
.toArray()) {
|
|
2686
|
+
const record = JSON.parse(row.summary_json) as { record?: unknown };
|
|
2687
|
+
if (typeof record.record === "string" && /^\d{4}$/.test(record.record)) used.add(record.record);
|
|
2688
|
+
}
|
|
2689
|
+
for (const row of this.sql.exec<{ meta_json: string }>(`SELECT meta_json FROM live_runs`).toArray()) {
|
|
2690
|
+
const meta = JSON.parse(row.meta_json) as { repo?: unknown; record?: unknown };
|
|
2691
|
+
if (meta.repo === repo && typeof meta.record === "string" && /^\d{4}$/.test(meta.record)) used.add(meta.record);
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
const highest = [...used].reduce((max, value) => (/^\d{4}$/.test(value) ? Math.max(max, Number(value)) : max), 0);
|
|
2695
|
+
number = existing ?? String(highest + 1).padStart(4, "0");
|
|
2696
|
+
if (!/^\d{4}$/.test(number)) throw new Error(`decision-record numbers exhausted for ${repo}`);
|
|
2697
|
+
this.sql.exec(
|
|
2698
|
+
`INSERT INTO decision_record_reservations (repo, task_key, number, created_at) VALUES (?, ?, ?, ?)`,
|
|
2699
|
+
repo,
|
|
2700
|
+
taskKey,
|
|
2701
|
+
number,
|
|
2702
|
+
now,
|
|
2703
|
+
);
|
|
2704
|
+
});
|
|
2705
|
+
return { number };
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2604
2708
|
// ---- the units of the plan an instance runs (run-history item 50) -----------
|
|
2605
2709
|
|
|
2606
2710
|
/** Each row replaced whole under its (instance, unit); a replace keeps the row's place. */
|
|
@@ -3153,15 +3257,22 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
3153
3257
|
.exec<{ json: string }>(`SELECT json FROM run_steps WHERE run_id = ? ORDER BY step DESC LIMIT 1`, row.runId)
|
|
3154
3258
|
.toArray()[0];
|
|
3155
3259
|
const lastStep = stepRow ? (JSON.parse(stepRow.json) as StepRecord) : null;
|
|
3156
|
-
|
|
3260
|
+
// Past the cursor, plus the deferred rows at or below it: the lowest
|
|
3261
|
+
// deferred seq bounds the read, and the record's predicate filters.
|
|
3262
|
+
const unread = unreadInbox(lastStep);
|
|
3263
|
+
const from = Math.min(
|
|
3264
|
+
lastStep?.inboxConsumedSeq ?? 0,
|
|
3265
|
+
...(lastStep?.inboxDeferredSeqs ?? []).map((s) => s - 1),
|
|
3266
|
+
);
|
|
3157
3267
|
const inbox = this.sql
|
|
3158
3268
|
.exec<{ seq: number; json: string }>(
|
|
3159
3269
|
`SELECT seq, json FROM run_inbox WHERE run_id = ? AND seq > ? ORDER BY seq ASC`,
|
|
3160
3270
|
row.runId,
|
|
3161
|
-
|
|
3271
|
+
from,
|
|
3162
3272
|
)
|
|
3163
3273
|
.toArray()
|
|
3164
|
-
.map((r) => ({ seq: r.seq, message: JSON.parse(r.json) as Record<string, unknown> }))
|
|
3274
|
+
.map((r) => ({ seq: r.seq, message: JSON.parse(r.json) as Record<string, unknown> }))
|
|
3275
|
+
.filter(unread);
|
|
3165
3276
|
const jobs = this.sql
|
|
3166
3277
|
.exec<{ kind: string; json: string }>(`SELECT kind, json FROM run_jobs WHERE run_id = ?`, row.runId)
|
|
3167
3278
|
.toArray()
|
|
@@ -3223,6 +3334,48 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
3223
3334
|
return this.intakeRow(key) ?? null;
|
|
3224
3335
|
}
|
|
3225
3336
|
|
|
3337
|
+
/** The receipt key and poster become one durable, atomic right to publish.
|
|
3338
|
+
* A dead claimant can be replaced only after its bounded claim expires. */
|
|
3339
|
+
async claimIntakeDelivery(key: string, poster: string, claimedAt: number): Promise<boolean> {
|
|
3340
|
+
let claimed = false;
|
|
3341
|
+
this.ctx.storage.transactionSync(() => {
|
|
3342
|
+
if (this.intakeRow(key)?.providerFailure === undefined) return;
|
|
3343
|
+
const existing = this.sql
|
|
3344
|
+
.exec<{ claim_until: number; delivered: number }>(
|
|
3345
|
+
`SELECT claim_until, delivered FROM intake_deliveries WHERE receipt_key = ?`,
|
|
3346
|
+
key,
|
|
3347
|
+
)
|
|
3348
|
+
.toArray()[0];
|
|
3349
|
+
if (existing?.delivered === 1 || (existing !== undefined && existing.claim_until > claimedAt)) return;
|
|
3350
|
+
this.sql.exec(
|
|
3351
|
+
`INSERT INTO intake_deliveries (receipt_key, poster, claim_until, delivered) VALUES (?, ?, ?, 0)
|
|
3352
|
+
ON CONFLICT(receipt_key) DO UPDATE SET poster = excluded.poster, claim_until = excluded.claim_until, delivered = 0`,
|
|
3353
|
+
key,
|
|
3354
|
+
poster,
|
|
3355
|
+
claimedAt + INTAKE_DELIVERY_CLAIM_MS,
|
|
3356
|
+
);
|
|
3357
|
+
claimed = true;
|
|
3358
|
+
});
|
|
3359
|
+
return claimed;
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3362
|
+
async finishIntakeDelivery(key: string, poster: string, delivered: boolean): Promise<void> {
|
|
3363
|
+
this.ctx.storage.transactionSync(() => {
|
|
3364
|
+
if (delivered)
|
|
3365
|
+
this.sql.exec(
|
|
3366
|
+
`UPDATE intake_deliveries SET delivered = 1 WHERE receipt_key = ? AND poster = ? AND delivered = 0`,
|
|
3367
|
+
key,
|
|
3368
|
+
poster,
|
|
3369
|
+
);
|
|
3370
|
+
else
|
|
3371
|
+
this.sql.exec(
|
|
3372
|
+
`DELETE FROM intake_deliveries WHERE receipt_key = ? AND poster = ? AND delivered = 0`,
|
|
3373
|
+
key,
|
|
3374
|
+
poster,
|
|
3375
|
+
);
|
|
3376
|
+
});
|
|
3377
|
+
}
|
|
3378
|
+
|
|
3226
3379
|
/** A thread's receipts, or the receipts since an instant, oldest first. */
|
|
3227
3380
|
async listIntake(query: IntakeQuery): Promise<IntakeReceipt[]> {
|
|
3228
3381
|
const clauses: string[] = [];
|
|
@@ -3574,6 +3727,10 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
3574
3727
|
receipts = this.sql
|
|
3575
3728
|
.exec<{ n: number }>(`SELECT COUNT(*) AS n FROM intake_receipts WHERE prune_after <= ?`, now)
|
|
3576
3729
|
.one().n;
|
|
3730
|
+
this.sql.exec(
|
|
3731
|
+
`DELETE FROM intake_deliveries WHERE receipt_key IN (SELECT key FROM intake_receipts WHERE prune_after <= ?)`,
|
|
3732
|
+
now,
|
|
3733
|
+
);
|
|
3577
3734
|
this.sql.exec(`DELETE FROM intake_receipts WHERE prune_after <= ?`, now);
|
|
3578
3735
|
// The sessions no kept run names any more (session-log item 7): decided
|
|
3579
3736
|
// here, on the rows this transaction leaves; dropped after it.
|
|
@@ -4955,6 +5112,7 @@ const LEDGER_ROUTES = new Set([
|
|
|
4955
5112
|
"/runs/coordinator/events/list",
|
|
4956
5113
|
"/runs/coordinator/events/mark-consumed",
|
|
4957
5114
|
"/runs/coordinator/wake",
|
|
5115
|
+
"/runs/decision-record/reserve",
|
|
4958
5116
|
"/runs/claim",
|
|
4959
5117
|
"/runs/heartbeat",
|
|
4960
5118
|
"/runs/append",
|
|
@@ -4972,6 +5130,8 @@ const LEDGER_ROUTES = new Set([
|
|
|
4972
5130
|
"/runs/live-events",
|
|
4973
5131
|
"/runs/intake",
|
|
4974
5132
|
"/runs/intake/read",
|
|
5133
|
+
"/runs/intake/delivery/claim",
|
|
5134
|
+
"/runs/intake/delivery/finish",
|
|
4975
5135
|
"/runs/intake/list",
|
|
4976
5136
|
"/runs/transcript/owner",
|
|
4977
5137
|
"/runs/transcript/write",
|
|
@@ -5125,7 +5285,22 @@ async function handlePlane(pathname: string, body: unknown, env: Env): Promise<R
|
|
|
5125
5285
|
if (typeof b.provider !== "string" || b.provider.length === 0)
|
|
5126
5286
|
return json({ error: "provider must be a non-empty string" }, 400);
|
|
5127
5287
|
if (b.side !== "up" && b.side !== "down") return json({ error: "side must be up or down" }, 400);
|
|
5128
|
-
|
|
5288
|
+
if (
|
|
5289
|
+
b.cause !== undefined &&
|
|
5290
|
+
(typeof b.cause !== "string" || !(PROVIDER_FAILURE_CAUSES as readonly string[]).includes(b.cause))
|
|
5291
|
+
)
|
|
5292
|
+
return json({ error: `cause must be one of ${PROVIDER_FAILURE_CAUSES.join(", ")}` }, 400);
|
|
5293
|
+
return json(
|
|
5294
|
+
await stub.planeLevel(
|
|
5295
|
+
{
|
|
5296
|
+
provider: b.provider,
|
|
5297
|
+
name: "provider",
|
|
5298
|
+
side: b.side,
|
|
5299
|
+
...(b.side === "down" && typeof b.cause === "string" ? { cause: b.cause as ProviderFailureCause } : {}),
|
|
5300
|
+
},
|
|
5301
|
+
now,
|
|
5302
|
+
),
|
|
5303
|
+
);
|
|
5129
5304
|
}
|
|
5130
5305
|
// A resident's level report (record 0064): forwarded by the bot from the levels a
|
|
5131
5306
|
// resident answer carried, or from the registry's drain outbox.
|
|
@@ -5317,6 +5492,12 @@ function parseStep(v: unknown): Validated<StepRecord> {
|
|
|
5317
5492
|
return invalid(`record.${k} must be a non-negative integer`);
|
|
5318
5493
|
}
|
|
5319
5494
|
}
|
|
5495
|
+
if (
|
|
5496
|
+
s.inboxDeferredSeqs !== undefined &&
|
|
5497
|
+
(!Array.isArray(s.inboxDeferredSeqs) ||
|
|
5498
|
+
!s.inboxDeferredSeqs.every((seq) => typeof seq === "number" && Number.isInteger(seq) && seq > 0))
|
|
5499
|
+
)
|
|
5500
|
+
return invalid("record.inboxDeferredSeqs must be an array of positive integers");
|
|
5320
5501
|
if (!Array.isArray(s.inFlight)) return invalid("record.inFlight must be an array");
|
|
5321
5502
|
for (const c of s.inFlight) {
|
|
5322
5503
|
const call = c as Record<string, unknown>;
|
|
@@ -5567,6 +5748,21 @@ async function handleLedger(pathname: string, body: unknown, env: Env): Promise<
|
|
|
5567
5748
|
return json(r);
|
|
5568
5749
|
}
|
|
5569
5750
|
|
|
5751
|
+
// Decision-record allocation is one atomic write on the same durable object
|
|
5752
|
+
// that holds unit and run rows, so a bot restart cannot forget a claim.
|
|
5753
|
+
if (pathname === "/runs/decision-record/reserve") {
|
|
5754
|
+
if (typeof b.repo !== "string" || !REPO_SLUG.test(b.repo)) return json({ error: "repo must be owner/name" }, 400);
|
|
5755
|
+
if (typeof b.taskKey !== "string" || !/^[0-9a-f]{16}$/.test(b.taskKey))
|
|
5756
|
+
return json({ error: "taskKey must be a decision-record task key" }, 400);
|
|
5757
|
+
if (!Array.isArray(b.claimed) || !b.claimed.every((number) => typeof number === "string" && /^\d{4}$/.test(number)))
|
|
5758
|
+
return json({ error: "claimed must be an array of decision-record numbers" }, 400);
|
|
5759
|
+
if (b.existing !== undefined && (typeof b.existing !== "string" || !/^\d{4}$/.test(b.existing)))
|
|
5760
|
+
return json({ error: "existing must be a decision-record number" }, 400);
|
|
5761
|
+
return json(
|
|
5762
|
+
await stub.reserveDecisionRecord(b.repo, b.taskKey, b.claimed as string[], b.existing as string | undefined, now),
|
|
5763
|
+
);
|
|
5764
|
+
}
|
|
5765
|
+
|
|
5570
5766
|
// The coordinator's parent records (run-history item 49): the record whole,
|
|
5571
5767
|
// validated by the shared contract; a read by instance id.
|
|
5572
5768
|
if (pathname === "/runs/coordinator/put") {
|
|
@@ -5662,6 +5858,21 @@ async function handleLedger(pathname: string, body: unknown, env: Env): Promise<
|
|
|
5662
5858
|
}
|
|
5663
5859
|
|
|
5664
5860
|
// The intake receipts (run-history item 59): keyed by the message, not a run.
|
|
5861
|
+
if (pathname === "/runs/intake/delivery/claim" || pathname === "/runs/intake/delivery/finish") {
|
|
5862
|
+
const receiptKey = b.key;
|
|
5863
|
+
if (typeof receiptKey !== "string" || receiptKey.length === 0 || receiptKey.length > 256)
|
|
5864
|
+
return json({ error: "key must be a non-empty string of at most 256 characters" }, 400);
|
|
5865
|
+
if (typeof b.poster !== "string" || b.poster.length === 0 || b.poster.length > 256)
|
|
5866
|
+
return json({ error: "poster must be a non-empty string of at most 256 characters" }, 400);
|
|
5867
|
+
if (pathname === "/runs/intake/delivery/claim") {
|
|
5868
|
+
if (typeof b.claimedAt !== "number" || !Number.isFinite(b.claimedAt))
|
|
5869
|
+
return json({ error: "claimedAt must be a number" }, 400);
|
|
5870
|
+
return json({ claimed: await stub.claimIntakeDelivery(receiptKey, b.poster, b.claimedAt) });
|
|
5871
|
+
}
|
|
5872
|
+
if (typeof b.delivered !== "boolean") return json({ error: "delivered must be a boolean" }, 400);
|
|
5873
|
+
await stub.finishIntakeDelivery(receiptKey, b.poster, b.delivered);
|
|
5874
|
+
return json({ ok: true });
|
|
5875
|
+
}
|
|
5665
5876
|
if (pathname === "/runs/intake" || pathname === "/runs/intake/read") {
|
|
5666
5877
|
const receiptKey = b.key;
|
|
5667
5878
|
if (typeof receiptKey !== "string" || receiptKey.length === 0 || receiptKey.length > 256)
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"@cloudflare/sandbox": "0.13.0-next.751.1"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@cloudflare/workers-types": "^5.
|
|
24
|
+
"@cloudflare/workers-types": "^5.20260914.1",
|
|
25
25
|
"typescript": "^5.9.3",
|
|
26
26
|
"vitest": "^5.0.0",
|
|
27
|
-
"wrangler": "^4.
|
|
27
|
+
"wrangler": "^4.131.2"
|
|
28
28
|
}
|
|
29
29
|
}
|