@coreplane/switchboard 1.260.4 → 1.260.5
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 +205 -8
- 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 +5 -0
- package/dist/assets/src/core/coordinator/contract.ts +5 -0
- package/dist/assets/src/core/coordinator/driver.ts +114 -25
- 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 +1 -0
- package/dist/assets/src/core/runEvents.ts +8 -0
- package/dist/assets/src/core/runLedger/types.ts +10 -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 +487 -25
- 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-Cf1joUyA.js} +1 -1
- package/dist/assets/web/dist/assets/{HomePage-fgKQHLaO.js → HomePage-DZEAnCCv.js} +1 -1
- package/dist/assets/web/dist/assets/{PendingTurnRow-vuWiDzvz.js → PendingTurnRow-4B8JwETU.js} +1 -1
- package/dist/assets/web/dist/assets/{PlanePage-DXMJf8FB.js → PlanePage-CHU2szQs.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentDetailPage-DdC8yU_S.js → ResidentDetailPage-L5SRcN_O.js} +1 -1
- package/dist/assets/web/dist/assets/{ResidentsIndexPage-CSM-KODv.js → ResidentsIndexPage-BIFf8qyE.js} +1 -1
- package/dist/assets/web/dist/assets/RunFoldRow-DzPIshJx.js +1 -0
- package/dist/assets/web/dist/assets/{RunRoutePage-jo6NZxsP.js → RunRoutePage-Cru5IVux.js} +3 -3
- package/dist/assets/web/dist/assets/{RunsIndexPage-Dx1Y4sSq.js → RunsIndexPage-CBkdPAdj.js} +1 -1
- package/dist/assets/web/dist/assets/{ScheduledPage-BuB_NZyR.js → ScheduledPage-D_R55BDk.js} +1 -1
- package/dist/assets/web/dist/assets/{SettingsPage-Cn96jB6Q.js → SettingsPage-DlmZvAvf.js} +1 -1
- package/dist/assets/web/dist/assets/{SilentTurn-D2cJfpEq.js → SilentTurn-PWMDMbMG.js} +1 -1
- package/dist/assets/web/dist/assets/{StatusDot-DDHUCPVJ.js → StatusDot-Bq0mYMFo.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-B4VIHkAI.js → Tooltip-Bs6zd7yE.js} +1 -1
- package/dist/assets/web/dist/assets/{UnitRoutePage-C-2DfjLI.js → UnitRoutePage-BMGEZCvn.js} +1 -1
- package/dist/assets/web/dist/assets/budgets-0szUBIMv.js +1 -0
- package/dist/assets/web/dist/assets/{dist-Cf1CmCSK.js → dist-EkKVbIM-.js} +11 -11
- package/dist/assets/web/dist/assets/{indexRow-Dxg7C8s9.js → indexRow-5s3hNzZ9.js} +1 -1
- package/dist/assets/web/dist/assets/{main-DxPqaFu9.js → main-Disot0n_.js} +2 -2
- package/dist/assets/web/dist/assets/{sseReplay-BzoeVxZB.js → sseReplay-9gcMpylx.js} +1 -1
- package/dist/cli.js +1855 -401
- 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
|
}
|
|
@@ -77,7 +77,8 @@ import {
|
|
|
77
77
|
reclaimPhase,
|
|
78
78
|
selectReclaim,
|
|
79
79
|
} from "../../src/core/runLedger/decisions.ts";
|
|
80
|
-
import { intakeReceiptRetentionMs, minutesToMs, PLANE } from "../../src/core/budgets.ts";
|
|
80
|
+
import { INTAKE_DELIVERY_CLAIM_MS, intakeReceiptRetentionMs, minutesToMs, PLANE } from "../../src/core/budgets.ts";
|
|
81
|
+
import { PROVIDER_FAILURE_CAUSES, type ProviderFailureCause } from "../../src/core/provider.ts";
|
|
81
82
|
import { holdBackgroundTask } from "./backgroundTasks.ts";
|
|
82
83
|
import {
|
|
83
84
|
causeOfClose,
|
|
@@ -1696,6 +1697,12 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1696
1697
|
);
|
|
1697
1698
|
CREATE INDEX IF NOT EXISTS intake_thread ON intake_receipts(thread_key, decided_at);
|
|
1698
1699
|
CREATE INDEX IF NOT EXISTS intake_prune ON intake_receipts(prune_after);
|
|
1700
|
+
CREATE TABLE IF NOT EXISTS intake_deliveries (
|
|
1701
|
+
receipt_key TEXT PRIMARY KEY,
|
|
1702
|
+
poster TEXT NOT NULL,
|
|
1703
|
+
claim_until INTEGER NOT NULL,
|
|
1704
|
+
delivered INTEGER NOT NULL
|
|
1705
|
+
);
|
|
1699
1706
|
`);
|
|
1700
1707
|
// The coordinator's parent records (run-history item 49): one row per
|
|
1701
1708
|
// instance, written by the bot at the instance's creation and read by the
|
|
@@ -1718,6 +1725,14 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1718
1725
|
updated_at INTEGER NOT NULL,
|
|
1719
1726
|
PRIMARY KEY (instance_id, unit)
|
|
1720
1727
|
);
|
|
1728
|
+
CREATE TABLE IF NOT EXISTS decision_record_reservations (
|
|
1729
|
+
repo TEXT NOT NULL,
|
|
1730
|
+
task_key TEXT NOT NULL,
|
|
1731
|
+
number TEXT NOT NULL,
|
|
1732
|
+
created_at INTEGER NOT NULL,
|
|
1733
|
+
PRIMARY KEY (repo, task_key),
|
|
1734
|
+
UNIQUE (repo, number)
|
|
1735
|
+
);
|
|
1721
1736
|
`);
|
|
1722
1737
|
// The thread events of a unit-owned thread (record 0051's reply-as-event rule): a
|
|
1723
1738
|
// sibling table of the unit rows, never a field on them — `putUnits`
|
|
@@ -1786,6 +1801,7 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1786
1801
|
side TEXT NOT NULL,
|
|
1787
1802
|
reported_at INTEGER NOT NULL,
|
|
1788
1803
|
generation TEXT NOT NULL,
|
|
1804
|
+
cause TEXT,
|
|
1789
1805
|
PRIMARY KEY (resident, name)
|
|
1790
1806
|
);
|
|
1791
1807
|
CREATE TABLE IF NOT EXISTS plane_endings (
|
|
@@ -1795,6 +1811,13 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1795
1811
|
at INTEGER NOT NULL
|
|
1796
1812
|
);
|
|
1797
1813
|
`);
|
|
1814
|
+
const planeLevelColumns = new Set(
|
|
1815
|
+
this.sql
|
|
1816
|
+
.exec<{ name: string }>(`PRAGMA table_info(plane_levels)`)
|
|
1817
|
+
.toArray()
|
|
1818
|
+
.map((column) => column.name),
|
|
1819
|
+
);
|
|
1820
|
+
if (!planeLevelColumns.has("cause")) this.sql.exec(`ALTER TABLE plane_levels ADD COLUMN cause TEXT`);
|
|
1798
1821
|
}
|
|
1799
1822
|
|
|
1800
1823
|
// ---- the orchestration plane (record 0064; orchestration-plane.md) ----------
|
|
@@ -1874,9 +1897,14 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1874
1897
|
.map((r) => [r.run_id, r.seq]),
|
|
1875
1898
|
);
|
|
1876
1899
|
const levels = this.sql
|
|
1877
|
-
.exec<{
|
|
1878
|
-
|
|
1879
|
-
|
|
1900
|
+
.exec<{
|
|
1901
|
+
resident: string;
|
|
1902
|
+
name: string;
|
|
1903
|
+
side: string;
|
|
1904
|
+
reported_at: number;
|
|
1905
|
+
generation: string;
|
|
1906
|
+
cause: string | null;
|
|
1907
|
+
}>(`SELECT * FROM plane_levels`)
|
|
1880
1908
|
.toArray()
|
|
1881
1909
|
.map((r): PlaneLevelRow => ({
|
|
1882
1910
|
resident: r.resident,
|
|
@@ -1884,6 +1912,9 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1884
1912
|
side: r.side as PlaneLevelRow["side"],
|
|
1885
1913
|
reportedAt: r.reported_at,
|
|
1886
1914
|
generation: r.generation,
|
|
1915
|
+
...(r.cause !== null && (PROVIDER_FAILURE_CAUSES as readonly string[]).includes(r.cause)
|
|
1916
|
+
? { cause: r.cause as ProviderFailureCause }
|
|
1917
|
+
: {}),
|
|
1887
1918
|
}));
|
|
1888
1919
|
return { queue, liveThreads, liveRuns, inboxSeqs, reservations, openWindows, levels };
|
|
1889
1920
|
}
|
|
@@ -1945,12 +1976,13 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
1945
1976
|
this.sql.exec(`DELETE FROM plane_windows WHERE kind = ?`, w.window);
|
|
1946
1977
|
} else if (w.table === "plane_levels") {
|
|
1947
1978
|
this.sql.exec(
|
|
1948
|
-
`INSERT OR REPLACE INTO plane_levels (resident, name, side, reported_at, generation) VALUES (?, ?, ?, ?, ?)`,
|
|
1979
|
+
`INSERT OR REPLACE INTO plane_levels (resident, name, side, reported_at, generation, cause) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
1949
1980
|
w.row.resident,
|
|
1950
1981
|
w.row.name,
|
|
1951
1982
|
w.row.side,
|
|
1952
1983
|
w.row.reportedAt,
|
|
1953
1984
|
w.row.generation,
|
|
1985
|
+
w.row.cause ?? null,
|
|
1954
1986
|
);
|
|
1955
1987
|
} else if (w.table === "plane_findings") {
|
|
1956
1988
|
// A finding (record 0064, "Endings and the watches"): keyed by watch
|
|
@@ -2104,14 +2136,20 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
2104
2136
|
planeLevel(
|
|
2105
2137
|
post:
|
|
2106
2138
|
| { resident: string; name: "seat" | "memory" | "drain"; side: "below" | "above"; generation: string }
|
|
2107
|
-
| { provider: string; name: "provider"; side: "up" | "down" },
|
|
2139
|
+
| { provider: string; name: "provider"; side: "up" | "down"; cause?: ProviderFailureCause },
|
|
2108
2140
|
now: number,
|
|
2109
2141
|
): { admitted: number } {
|
|
2110
2142
|
// The model proxy's provider level (record 0064): `up` re-issues every
|
|
2111
2143
|
// held turn parked on the provider — the steers land as inbox writes in the
|
|
2112
2144
|
// decider's transaction — and walks anything queued on `provider_up`.
|
|
2113
2145
|
if (post.name === "provider") {
|
|
2114
|
-
const r = this.planeApply({
|
|
2146
|
+
const r = this.planeApply({
|
|
2147
|
+
kind: "provider_level",
|
|
2148
|
+
at: now,
|
|
2149
|
+
provider: post.provider,
|
|
2150
|
+
level: post.side,
|
|
2151
|
+
...(post.side === "down" && post.cause !== undefined ? { cause: post.cause } : {}),
|
|
2152
|
+
});
|
|
2115
2153
|
const admitted = r.effects.filter((e) => e.kind === "admit").length;
|
|
2116
2154
|
console.log(`[plane/level] provider ${post.provider} ${post.side} — ${admitted} admission(s)`);
|
|
2117
2155
|
return { admitted };
|
|
@@ -2601,6 +2639,71 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
2601
2639
|
return out;
|
|
2602
2640
|
}
|
|
2603
2641
|
|
|
2642
|
+
// ---- decision-record reservations (agent-ship item 16) ---------------------
|
|
2643
|
+
|
|
2644
|
+
/** One atomic durable allocation across bot processes. Existing unit and run
|
|
2645
|
+
* rows seed the claim set for reservations written before this ledger existed. */
|
|
2646
|
+
async reserveDecisionRecord(
|
|
2647
|
+
repo: string,
|
|
2648
|
+
taskKey: string,
|
|
2649
|
+
claimed: string[],
|
|
2650
|
+
existing: string | undefined,
|
|
2651
|
+
now: number,
|
|
2652
|
+
): Promise<{ number: string }> {
|
|
2653
|
+
let number = "";
|
|
2654
|
+
this.ctx.storage.transactionSync(() => {
|
|
2655
|
+
const prior = this.sql
|
|
2656
|
+
.exec<{ number: string }>(
|
|
2657
|
+
`SELECT number FROM decision_record_reservations WHERE repo = ? AND task_key = ?`,
|
|
2658
|
+
repo,
|
|
2659
|
+
taskKey,
|
|
2660
|
+
)
|
|
2661
|
+
.toArray()[0];
|
|
2662
|
+
if (prior !== undefined) {
|
|
2663
|
+
number = prior.number;
|
|
2664
|
+
return;
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
const used = new Set(claimed);
|
|
2668
|
+
for (const row of this.sql
|
|
2669
|
+
.exec<{ number: string }>(`SELECT number FROM decision_record_reservations WHERE repo = ?`, repo)
|
|
2670
|
+
.toArray())
|
|
2671
|
+
used.add(row.number);
|
|
2672
|
+
for (const row of this.sql
|
|
2673
|
+
.exec<{ instance_json: string; unit_json: string }>(
|
|
2674
|
+
`SELECT i.json AS instance_json, u.json AS unit_json
|
|
2675
|
+
FROM coordinator_units u JOIN coordinator_instances i ON i.instance_id = u.instance_id`,
|
|
2676
|
+
)
|
|
2677
|
+
.toArray()) {
|
|
2678
|
+
const instance = JSON.parse(row.instance_json) as CoordinatorInstance;
|
|
2679
|
+
const unit = JSON.parse(row.unit_json) as CoordinatorUnit;
|
|
2680
|
+
if (instance.repo === repo && unit.record !== undefined) used.add(unit.record);
|
|
2681
|
+
}
|
|
2682
|
+
for (const row of this.sql
|
|
2683
|
+
.exec<{ summary_json: string }>(`SELECT summary_json FROM runs WHERE repo = ?`, repo)
|
|
2684
|
+
.toArray()) {
|
|
2685
|
+
const record = JSON.parse(row.summary_json) as { record?: unknown };
|
|
2686
|
+
if (typeof record.record === "string" && /^\d{4}$/.test(record.record)) used.add(record.record);
|
|
2687
|
+
}
|
|
2688
|
+
for (const row of this.sql.exec<{ meta_json: string }>(`SELECT meta_json FROM live_runs`).toArray()) {
|
|
2689
|
+
const meta = JSON.parse(row.meta_json) as { repo?: unknown; record?: unknown };
|
|
2690
|
+
if (meta.repo === repo && typeof meta.record === "string" && /^\d{4}$/.test(meta.record)) used.add(meta.record);
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
const highest = [...used].reduce((max, value) => (/^\d{4}$/.test(value) ? Math.max(max, Number(value)) : max), 0);
|
|
2694
|
+
number = existing ?? String(highest + 1).padStart(4, "0");
|
|
2695
|
+
if (!/^\d{4}$/.test(number)) throw new Error(`decision-record numbers exhausted for ${repo}`);
|
|
2696
|
+
this.sql.exec(
|
|
2697
|
+
`INSERT INTO decision_record_reservations (repo, task_key, number, created_at) VALUES (?, ?, ?, ?)`,
|
|
2698
|
+
repo,
|
|
2699
|
+
taskKey,
|
|
2700
|
+
number,
|
|
2701
|
+
now,
|
|
2702
|
+
);
|
|
2703
|
+
});
|
|
2704
|
+
return { number };
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2604
2707
|
// ---- the units of the plan an instance runs (run-history item 50) -----------
|
|
2605
2708
|
|
|
2606
2709
|
/** Each row replaced whole under its (instance, unit); a replace keeps the row's place. */
|
|
@@ -3223,6 +3326,48 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
3223
3326
|
return this.intakeRow(key) ?? null;
|
|
3224
3327
|
}
|
|
3225
3328
|
|
|
3329
|
+
/** The receipt key and poster become one durable, atomic right to publish.
|
|
3330
|
+
* A dead claimant can be replaced only after its bounded claim expires. */
|
|
3331
|
+
async claimIntakeDelivery(key: string, poster: string, claimedAt: number): Promise<boolean> {
|
|
3332
|
+
let claimed = false;
|
|
3333
|
+
this.ctx.storage.transactionSync(() => {
|
|
3334
|
+
if (this.intakeRow(key)?.providerFailure === undefined) return;
|
|
3335
|
+
const existing = this.sql
|
|
3336
|
+
.exec<{ claim_until: number; delivered: number }>(
|
|
3337
|
+
`SELECT claim_until, delivered FROM intake_deliveries WHERE receipt_key = ?`,
|
|
3338
|
+
key,
|
|
3339
|
+
)
|
|
3340
|
+
.toArray()[0];
|
|
3341
|
+
if (existing?.delivered === 1 || (existing !== undefined && existing.claim_until > claimedAt)) return;
|
|
3342
|
+
this.sql.exec(
|
|
3343
|
+
`INSERT INTO intake_deliveries (receipt_key, poster, claim_until, delivered) VALUES (?, ?, ?, 0)
|
|
3344
|
+
ON CONFLICT(receipt_key) DO UPDATE SET poster = excluded.poster, claim_until = excluded.claim_until, delivered = 0`,
|
|
3345
|
+
key,
|
|
3346
|
+
poster,
|
|
3347
|
+
claimedAt + INTAKE_DELIVERY_CLAIM_MS,
|
|
3348
|
+
);
|
|
3349
|
+
claimed = true;
|
|
3350
|
+
});
|
|
3351
|
+
return claimed;
|
|
3352
|
+
}
|
|
3353
|
+
|
|
3354
|
+
async finishIntakeDelivery(key: string, poster: string, delivered: boolean): Promise<void> {
|
|
3355
|
+
this.ctx.storage.transactionSync(() => {
|
|
3356
|
+
if (delivered)
|
|
3357
|
+
this.sql.exec(
|
|
3358
|
+
`UPDATE intake_deliveries SET delivered = 1 WHERE receipt_key = ? AND poster = ? AND delivered = 0`,
|
|
3359
|
+
key,
|
|
3360
|
+
poster,
|
|
3361
|
+
);
|
|
3362
|
+
else
|
|
3363
|
+
this.sql.exec(
|
|
3364
|
+
`DELETE FROM intake_deliveries WHERE receipt_key = ? AND poster = ? AND delivered = 0`,
|
|
3365
|
+
key,
|
|
3366
|
+
poster,
|
|
3367
|
+
);
|
|
3368
|
+
});
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3226
3371
|
/** A thread's receipts, or the receipts since an instant, oldest first. */
|
|
3227
3372
|
async listIntake(query: IntakeQuery): Promise<IntakeReceipt[]> {
|
|
3228
3373
|
const clauses: string[] = [];
|
|
@@ -3574,6 +3719,10 @@ export class RunHistoryDO extends DurableObject<Env> {
|
|
|
3574
3719
|
receipts = this.sql
|
|
3575
3720
|
.exec<{ n: number }>(`SELECT COUNT(*) AS n FROM intake_receipts WHERE prune_after <= ?`, now)
|
|
3576
3721
|
.one().n;
|
|
3722
|
+
this.sql.exec(
|
|
3723
|
+
`DELETE FROM intake_deliveries WHERE receipt_key IN (SELECT key FROM intake_receipts WHERE prune_after <= ?)`,
|
|
3724
|
+
now,
|
|
3725
|
+
);
|
|
3577
3726
|
this.sql.exec(`DELETE FROM intake_receipts WHERE prune_after <= ?`, now);
|
|
3578
3727
|
// The sessions no kept run names any more (session-log item 7): decided
|
|
3579
3728
|
// here, on the rows this transaction leaves; dropped after it.
|
|
@@ -4955,6 +5104,7 @@ const LEDGER_ROUTES = new Set([
|
|
|
4955
5104
|
"/runs/coordinator/events/list",
|
|
4956
5105
|
"/runs/coordinator/events/mark-consumed",
|
|
4957
5106
|
"/runs/coordinator/wake",
|
|
5107
|
+
"/runs/decision-record/reserve",
|
|
4958
5108
|
"/runs/claim",
|
|
4959
5109
|
"/runs/heartbeat",
|
|
4960
5110
|
"/runs/append",
|
|
@@ -4972,6 +5122,8 @@ const LEDGER_ROUTES = new Set([
|
|
|
4972
5122
|
"/runs/live-events",
|
|
4973
5123
|
"/runs/intake",
|
|
4974
5124
|
"/runs/intake/read",
|
|
5125
|
+
"/runs/intake/delivery/claim",
|
|
5126
|
+
"/runs/intake/delivery/finish",
|
|
4975
5127
|
"/runs/intake/list",
|
|
4976
5128
|
"/runs/transcript/owner",
|
|
4977
5129
|
"/runs/transcript/write",
|
|
@@ -5125,7 +5277,22 @@ async function handlePlane(pathname: string, body: unknown, env: Env): Promise<R
|
|
|
5125
5277
|
if (typeof b.provider !== "string" || b.provider.length === 0)
|
|
5126
5278
|
return json({ error: "provider must be a non-empty string" }, 400);
|
|
5127
5279
|
if (b.side !== "up" && b.side !== "down") return json({ error: "side must be up or down" }, 400);
|
|
5128
|
-
|
|
5280
|
+
if (
|
|
5281
|
+
b.cause !== undefined &&
|
|
5282
|
+
(typeof b.cause !== "string" || !(PROVIDER_FAILURE_CAUSES as readonly string[]).includes(b.cause))
|
|
5283
|
+
)
|
|
5284
|
+
return json({ error: `cause must be one of ${PROVIDER_FAILURE_CAUSES.join(", ")}` }, 400);
|
|
5285
|
+
return json(
|
|
5286
|
+
await stub.planeLevel(
|
|
5287
|
+
{
|
|
5288
|
+
provider: b.provider,
|
|
5289
|
+
name: "provider",
|
|
5290
|
+
side: b.side,
|
|
5291
|
+
...(b.side === "down" && typeof b.cause === "string" ? { cause: b.cause as ProviderFailureCause } : {}),
|
|
5292
|
+
},
|
|
5293
|
+
now,
|
|
5294
|
+
),
|
|
5295
|
+
);
|
|
5129
5296
|
}
|
|
5130
5297
|
// A resident's level report (record 0064): forwarded by the bot from the levels a
|
|
5131
5298
|
// resident answer carried, or from the registry's drain outbox.
|
|
@@ -5567,6 +5734,21 @@ async function handleLedger(pathname: string, body: unknown, env: Env): Promise<
|
|
|
5567
5734
|
return json(r);
|
|
5568
5735
|
}
|
|
5569
5736
|
|
|
5737
|
+
// Decision-record allocation is one atomic write on the same durable object
|
|
5738
|
+
// that holds unit and run rows, so a bot restart cannot forget a claim.
|
|
5739
|
+
if (pathname === "/runs/decision-record/reserve") {
|
|
5740
|
+
if (typeof b.repo !== "string" || !REPO_SLUG.test(b.repo)) return json({ error: "repo must be owner/name" }, 400);
|
|
5741
|
+
if (typeof b.taskKey !== "string" || !/^[0-9a-f]{16}$/.test(b.taskKey))
|
|
5742
|
+
return json({ error: "taskKey must be a decision-record task key" }, 400);
|
|
5743
|
+
if (!Array.isArray(b.claimed) || !b.claimed.every((number) => typeof number === "string" && /^\d{4}$/.test(number)))
|
|
5744
|
+
return json({ error: "claimed must be an array of decision-record numbers" }, 400);
|
|
5745
|
+
if (b.existing !== undefined && (typeof b.existing !== "string" || !/^\d{4}$/.test(b.existing)))
|
|
5746
|
+
return json({ error: "existing must be a decision-record number" }, 400);
|
|
5747
|
+
return json(
|
|
5748
|
+
await stub.reserveDecisionRecord(b.repo, b.taskKey, b.claimed as string[], b.existing as string | undefined, now),
|
|
5749
|
+
);
|
|
5750
|
+
}
|
|
5751
|
+
|
|
5570
5752
|
// The coordinator's parent records (run-history item 49): the record whole,
|
|
5571
5753
|
// validated by the shared contract; a read by instance id.
|
|
5572
5754
|
if (pathname === "/runs/coordinator/put") {
|
|
@@ -5662,6 +5844,21 @@ async function handleLedger(pathname: string, body: unknown, env: Env): Promise<
|
|
|
5662
5844
|
}
|
|
5663
5845
|
|
|
5664
5846
|
// The intake receipts (run-history item 59): keyed by the message, not a run.
|
|
5847
|
+
if (pathname === "/runs/intake/delivery/claim" || pathname === "/runs/intake/delivery/finish") {
|
|
5848
|
+
const receiptKey = b.key;
|
|
5849
|
+
if (typeof receiptKey !== "string" || receiptKey.length === 0 || receiptKey.length > 256)
|
|
5850
|
+
return json({ error: "key must be a non-empty string of at most 256 characters" }, 400);
|
|
5851
|
+
if (typeof b.poster !== "string" || b.poster.length === 0 || b.poster.length > 256)
|
|
5852
|
+
return json({ error: "poster must be a non-empty string of at most 256 characters" }, 400);
|
|
5853
|
+
if (pathname === "/runs/intake/delivery/claim") {
|
|
5854
|
+
if (typeof b.claimedAt !== "number" || !Number.isFinite(b.claimedAt))
|
|
5855
|
+
return json({ error: "claimedAt must be a number" }, 400);
|
|
5856
|
+
return json({ claimed: await stub.claimIntakeDelivery(receiptKey, b.poster, b.claimedAt) });
|
|
5857
|
+
}
|
|
5858
|
+
if (typeof b.delivered !== "boolean") return json({ error: "delivered must be a boolean" }, 400);
|
|
5859
|
+
await stub.finishIntakeDelivery(receiptKey, b.poster, b.delivered);
|
|
5860
|
+
return json({ ok: true });
|
|
5861
|
+
}
|
|
5665
5862
|
if (pathname === "/runs/intake" || pathname === "/runs/intake/read") {
|
|
5666
5863
|
const receiptKey = b.key;
|
|
5667
5864
|
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
|
}
|