@hasna/todos 0.15.5 → 0.15.7

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 (43) hide show
  1. package/dist/cli/cloud-router.d.ts.map +1 -1
  2. package/dist/cli/commands/task-commands.d.ts.map +1 -1
  3. package/dist/cli/index.js +3086 -1332
  4. package/dist/contracts.js +213 -3
  5. package/dist/db/migrations.d.ts.map +1 -1
  6. package/dist/db/schema.d.ts.map +1 -1
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1950 -26
  10. package/dist/lib/comment-cursor.d.ts +40 -0
  11. package/dist/lib/comment-cursor.d.ts.map +1 -0
  12. package/dist/mcp/index.js +2096 -391
  13. package/dist/mcp.js +7 -3
  14. package/dist/project-registration/authority.d.ts +45 -0
  15. package/dist/project-registration/authority.d.ts.map +1 -0
  16. package/dist/project-registration/backend.d.ts +83 -0
  17. package/dist/project-registration/backend.d.ts.map +1 -0
  18. package/dist/project-registration/http.d.ts +24 -0
  19. package/dist/project-registration/http.d.ts.map +1 -0
  20. package/dist/project-registration/index.d.ts +8 -0
  21. package/dist/project-registration/index.d.ts.map +1 -0
  22. package/dist/project-registration/postgres.d.ts +30 -0
  23. package/dist/project-registration/postgres.d.ts.map +1 -0
  24. package/dist/project-registration/schema.d.ts +3 -0
  25. package/dist/project-registration/schema.d.ts.map +1 -0
  26. package/dist/project-registration/sqlite.d.ts +17 -0
  27. package/dist/project-registration/sqlite.d.ts.map +1 -0
  28. package/dist/project-registration/types.d.ts +155 -0
  29. package/dist/project-registration/types.d.ts.map +1 -0
  30. package/dist/project-registration.d.ts +2 -0
  31. package/dist/project-registration.d.ts.map +1 -0
  32. package/dist/project-registration.js +17689 -0
  33. package/dist/registry.d.ts +1 -1
  34. package/dist/registry.d.ts.map +1 -1
  35. package/dist/registry.js +220 -3
  36. package/dist/release-provenance.json +5 -5
  37. package/dist/server/cloud.d.ts +3 -0
  38. package/dist/server/cloud.d.ts.map +1 -1
  39. package/dist/server/index.js +4386 -2681
  40. package/dist/server/v1.d.ts +2 -1
  41. package/dist/server/v1.d.ts.map +1 -1
  42. package/dist/storage.js +206 -0
  43. package/package.json +7 -3
package/dist/mcp/index.js CHANGED
@@ -228,6 +228,206 @@ var init_types = __esm(() => {
228
228
  };
229
229
  });
230
230
 
231
+ // src/project-registration/schema.ts
232
+ function sqliteTodosProjectRegistrationSchemaSql() {
233
+ return `
234
+ CREATE TABLE IF NOT EXISTS todos_project_registration_receipts (
235
+ receipt_id TEXT PRIMARY KEY,
236
+ authority TEXT NOT NULL CHECK(authority = 'todos'),
237
+ route TEXT NOT NULL,
238
+ package_version TEXT NOT NULL,
239
+ authority_id TEXT NOT NULL,
240
+ tenant_id TEXT NOT NULL,
241
+ corpus_id TEXT NOT NULL,
242
+ operation_id TEXT NOT NULL,
243
+ step_id TEXT NOT NULL,
244
+ resource_kind TEXT NOT NULL CHECK(resource_kind IN ('project', 'task_list')),
245
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'inverse')),
246
+ target_selector TEXT NOT NULL,
247
+ idempotency_key TEXT NOT NULL,
248
+ request_digest TEXT NOT NULL,
249
+ precondition_digest TEXT NOT NULL,
250
+ normalized_call_digest TEXT NOT NULL,
251
+ outcome TEXT NOT NULL CHECK(outcome IN (
252
+ 'accepted', 'duplicate_of_accepted', 'terminal_nonacceptance'
253
+ )),
254
+ reason TEXT,
255
+ target_id TEXT,
256
+ result_revision TEXT,
257
+ result_digest TEXT,
258
+ duplicate_of_receipt_id TEXT,
259
+ accepted_receipt_id TEXT,
260
+ created_by_operation INTEGER NOT NULL CHECK(created_by_operation IN (0, 1)),
261
+ created_at TEXT NOT NULL
262
+ );
263
+
264
+ CREATE INDEX IF NOT EXISTS idx_todos_project_registration_receipts_lookup
265
+ ON todos_project_registration_receipts (
266
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
267
+ resource_kind, direction, idempotency_key
268
+ );
269
+ CREATE INDEX IF NOT EXISTS idx_todos_project_registration_receipts_step
270
+ ON todos_project_registration_receipts (
271
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
272
+ resource_kind, direction, outcome
273
+ );
274
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_todos_project_registration_receipts_accepted_step
275
+ ON todos_project_registration_receipts (
276
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
277
+ resource_kind, direction
278
+ )
279
+ WHERE outcome = 'accepted';
280
+ CREATE INDEX IF NOT EXISTS idx_todos_project_registration_receipts_target
281
+ ON todos_project_registration_receipts (
282
+ authority_id, tenant_id, corpus_id, resource_kind, target_id
283
+ );
284
+
285
+ CREATE TABLE IF NOT EXISTS todos_project_registration_bindings (
286
+ authority_id TEXT NOT NULL,
287
+ tenant_id TEXT NOT NULL,
288
+ corpus_id TEXT NOT NULL,
289
+ resource_kind TEXT NOT NULL CHECK(resource_kind IN ('project', 'task_list')),
290
+ target_selector TEXT NOT NULL,
291
+ operation_id TEXT NOT NULL,
292
+ step_id TEXT NOT NULL,
293
+ direction TEXT NOT NULL CHECK(direction = 'forward'),
294
+ idempotency_key TEXT NOT NULL,
295
+ request_digest TEXT NOT NULL,
296
+ precondition_digest TEXT NOT NULL,
297
+ normalized_call_digest TEXT NOT NULL,
298
+ state TEXT NOT NULL CHECK(state IN (
299
+ 'pending', 'accepted', 'terminal_nonacceptance', 'removed'
300
+ )),
301
+ target_id TEXT,
302
+ accepted_receipt_id TEXT,
303
+ result_revision TEXT,
304
+ result_digest TEXT,
305
+ removed_receipt_id TEXT,
306
+ created_at TEXT NOT NULL,
307
+ updated_at TEXT NOT NULL,
308
+ PRIMARY KEY(
309
+ authority_id, tenant_id, corpus_id, resource_kind, target_selector
310
+ ),
311
+ UNIQUE(accepted_receipt_id)
312
+ );
313
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_todos_project_registration_binding_target
314
+ ON todos_project_registration_bindings(
315
+ authority_id, tenant_id, corpus_id, resource_kind, target_id
316
+ )
317
+ WHERE target_id IS NOT NULL;
318
+
319
+ CREATE TRIGGER IF NOT EXISTS todos_project_registration_receipts_immutable_update
320
+ BEFORE UPDATE ON todos_project_registration_receipts
321
+ BEGIN
322
+ SELECT RAISE(ABORT, 'todos project registration receipts are immutable');
323
+ END;
324
+
325
+ CREATE TRIGGER IF NOT EXISTS todos_project_registration_receipts_immutable_delete
326
+ BEFORE DELETE ON todos_project_registration_receipts
327
+ BEGIN
328
+ SELECT RAISE(ABORT, 'todos project registration receipts are immutable');
329
+ END;
330
+ `;
331
+ }
332
+ function postgresTodosProjectRegistrationSchemaSql() {
333
+ return [
334
+ `CREATE TABLE IF NOT EXISTS todos_project_registration_receipts (
335
+ receipt_id text PRIMARY KEY,
336
+ authority text NOT NULL CHECK(authority = 'todos'),
337
+ route text NOT NULL,
338
+ package_version text NOT NULL,
339
+ authority_id text NOT NULL,
340
+ tenant_id text NOT NULL,
341
+ corpus_id text NOT NULL,
342
+ operation_id text NOT NULL,
343
+ step_id text NOT NULL,
344
+ resource_kind text NOT NULL CHECK(resource_kind IN ('project', 'task_list')),
345
+ direction text NOT NULL CHECK(direction IN ('forward', 'inverse')),
346
+ target_selector text NOT NULL,
347
+ idempotency_key text NOT NULL,
348
+ request_digest text NOT NULL,
349
+ precondition_digest text NOT NULL,
350
+ normalized_call_digest text NOT NULL,
351
+ outcome text NOT NULL CHECK(outcome IN (
352
+ 'accepted', 'duplicate_of_accepted', 'terminal_nonacceptance'
353
+ )),
354
+ reason text,
355
+ target_id text,
356
+ result_revision text,
357
+ result_digest text,
358
+ duplicate_of_receipt_id text,
359
+ accepted_receipt_id text,
360
+ created_by_operation boolean NOT NULL,
361
+ created_at timestamptz NOT NULL
362
+ )`,
363
+ `CREATE INDEX IF NOT EXISTS todos_project_registration_receipts_lookup_idx
364
+ ON todos_project_registration_receipts (
365
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
366
+ resource_kind, direction, idempotency_key
367
+ )`,
368
+ `CREATE INDEX IF NOT EXISTS todos_project_registration_receipts_step_idx
369
+ ON todos_project_registration_receipts (
370
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
371
+ resource_kind, direction, outcome
372
+ )`,
373
+ `CREATE UNIQUE INDEX IF NOT EXISTS todos_project_registration_receipts_accepted_step_uidx
374
+ ON todos_project_registration_receipts (
375
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
376
+ resource_kind, direction
377
+ )
378
+ WHERE outcome = 'accepted'`,
379
+ `CREATE INDEX IF NOT EXISTS todos_project_registration_receipts_target_idx
380
+ ON todos_project_registration_receipts (
381
+ authority_id, tenant_id, corpus_id, resource_kind, target_id
382
+ )`,
383
+ `CREATE TABLE IF NOT EXISTS todos_project_registration_bindings (
384
+ authority_id text NOT NULL,
385
+ tenant_id text NOT NULL,
386
+ corpus_id text NOT NULL,
387
+ resource_kind text NOT NULL CHECK(resource_kind IN ('project', 'task_list')),
388
+ target_selector text NOT NULL,
389
+ operation_id text NOT NULL,
390
+ step_id text NOT NULL,
391
+ direction text NOT NULL CHECK(direction = 'forward'),
392
+ idempotency_key text NOT NULL,
393
+ request_digest text NOT NULL,
394
+ precondition_digest text NOT NULL,
395
+ normalized_call_digest text NOT NULL,
396
+ state text NOT NULL CHECK(state IN (
397
+ 'pending', 'accepted', 'terminal_nonacceptance', 'removed'
398
+ )),
399
+ target_id text,
400
+ accepted_receipt_id text UNIQUE,
401
+ result_revision text,
402
+ result_digest text,
403
+ removed_receipt_id text,
404
+ created_at timestamptz NOT NULL,
405
+ updated_at timestamptz NOT NULL,
406
+ PRIMARY KEY(
407
+ authority_id, tenant_id, corpus_id, resource_kind, target_selector
408
+ )
409
+ )`,
410
+ `CREATE UNIQUE INDEX IF NOT EXISTS todos_project_registration_binding_target_uidx
411
+ ON todos_project_registration_bindings(
412
+ authority_id, tenant_id, corpus_id, resource_kind, target_id
413
+ )
414
+ WHERE target_id IS NOT NULL`,
415
+ `CREATE OR REPLACE FUNCTION todos_project_registration_receipts_immutable()
416
+ RETURNS trigger
417
+ LANGUAGE plpgsql
418
+ AS $$
419
+ BEGIN
420
+ RAISE EXCEPTION 'todos project registration receipts are immutable';
421
+ END;
422
+ $$`,
423
+ `DROP TRIGGER IF EXISTS todos_project_registration_receipts_immutable
424
+ ON todos_project_registration_receipts`,
425
+ `CREATE TRIGGER todos_project_registration_receipts_immutable
426
+ BEFORE UPDATE OR DELETE ON todos_project_registration_receipts
427
+ FOR EACH ROW EXECUTE FUNCTION todos_project_registration_receipts_immutable()`
428
+ ];
429
+ }
430
+
231
431
  // src/db/migrations.ts
232
432
  var MIGRATIONS;
233
433
  var init_migrations = __esm(() => {
@@ -1833,6 +2033,11 @@ var init_migrations = __esm(() => {
1833
2033
  INSERT OR IGNORE INTO _migrations (id) VALUES (68);
1834
2034
  COMMIT;
1835
2035
  PRAGMA foreign_keys = ON;
2036
+ `,
2037
+ `BEGIN;
2038
+ ${sqliteTodosProjectRegistrationSchemaSql()}
2039
+ INSERT OR IGNORE INTO _migrations (id) VALUES (69);
2040
+ COMMIT;
1836
2041
  `
1837
2042
  ];
1838
2043
  });
@@ -3035,6 +3240,7 @@ function ensureSchema(db) {
3035
3240
  )`);
3036
3241
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(prefix)");
3037
3242
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_api_keys_active ON api_keys(revoked_at, expires_at)");
3243
+ db.exec(sqliteTodosProjectRegistrationSchemaSql());
3038
3244
  ensureTable("pr_groups", `
3039
3245
  CREATE TABLE pr_groups (
3040
3246
  schema_version INTEGER NOT NULL DEFAULT 1,
@@ -20684,6 +20890,16 @@ async function cloudListProjects(client) {
20684
20890
  const envelope = res.raw;
20685
20891
  return Array.isArray(envelope?.projects) ? envelope.projects : res.items;
20686
20892
  }
20893
+ function unwrapProject(raw) {
20894
+ if (raw && typeof raw === "object" && "project" in raw) {
20895
+ return raw.project;
20896
+ }
20897
+ return raw;
20898
+ }
20899
+ async function cloudGetProjectById(client, id) {
20900
+ const raw = await client.get("projects", id);
20901
+ return raw == null ? null : unwrapProject(raw);
20902
+ }
20687
20903
  function cloudProjectSlug(value) {
20688
20904
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
20689
20905
  }
@@ -20790,29 +21006,63 @@ async function cloudListTaskLists(client, projectId) {
20790
21006
  return envelope.taskLists;
20791
21007
  return Array.isArray(raw) ? raw : [];
20792
21008
  }
21009
+ function unwrapTaskList(raw) {
21010
+ if (raw && typeof raw === "object" && "task_list" in raw) {
21011
+ return raw.task_list;
21012
+ }
21013
+ return raw;
21014
+ }
21015
+ async function cloudGetTaskList(client, id) {
21016
+ const raw = await client.get("task-lists", id);
21017
+ return raw == null ? null : unwrapTaskList(raw);
21018
+ }
21019
+ function resolveTaskListFromCandidates(lists, input) {
21020
+ const normalizedIdRef = input.toLowerCase();
21021
+ const matchGroups = [
21022
+ lists.filter((list) => list.id.toLowerCase() === normalizedIdRef),
21023
+ lists.filter((list) => list.slug === input),
21024
+ lists.filter((list) => list.id.toLowerCase().startsWith(normalizedIdRef))
21025
+ ];
21026
+ for (const matches of matchGroups) {
21027
+ if (matches.length === 1)
21028
+ return matches[0].id;
21029
+ if (matches.length > 1) {
21030
+ throw new Error(`Task list reference is ambiguous: "${input}"`);
21031
+ }
21032
+ }
21033
+ return null;
21034
+ }
21035
+ async function legacyProjectTaskLists(client, projectId) {
21036
+ const project = await cloudGetProjectById(client, projectId);
21037
+ if (!project?.task_list_id)
21038
+ return [];
21039
+ return (await cloudListTaskLists(client)).filter((list) => list.project_id == null && list.slug === project.task_list_id);
21040
+ }
20793
21041
  async function cloudResolveTaskListRef(client, ref, projectId) {
20794
21042
  const input = ref.trim();
20795
21043
  const normalizedIdRef = input.toLowerCase();
20796
21044
  if (UUID_RE.test(input) && !projectId)
20797
21045
  return normalizedIdRef;
20798
- const lists = await cloudListTaskLists(client, projectId);
20799
- const exactIds = lists.filter((list) => list.id.toLowerCase() === normalizedIdRef);
20800
- if (exactIds.length === 1)
20801
- return exactIds[0].id;
20802
- if (exactIds.length > 1) {
20803
- throw new Error(`Task list reference is ambiguous: "${input}"`);
20804
- }
20805
- const slugs = lists.filter((list) => list.slug === input);
20806
- if (slugs.length === 1)
20807
- return slugs[0].id;
20808
- if (slugs.length > 1) {
20809
- throw new Error(`Task list reference is ambiguous: "${input}"`);
20810
- }
20811
- const prefixes = lists.filter((list) => list.id.toLowerCase().startsWith(normalizedIdRef));
20812
- if (prefixes.length === 1)
20813
- return prefixes[0].id;
20814
- if (prefixes.length > 1) {
20815
- throw new Error(`Task list reference is ambiguous: "${input}"`);
21046
+ if (UUID_RE.test(input) && projectId) {
21047
+ const direct = await cloudGetTaskList(client, normalizedIdRef);
21048
+ if (direct?.id?.toLowerCase() === normalizedIdRef) {
21049
+ if (direct.project_id === projectId)
21050
+ return direct.id;
21051
+ if (direct.project_id == null) {
21052
+ const project = await cloudGetProjectById(client, projectId);
21053
+ if (project?.task_list_id === direct.slug)
21054
+ return direct.id;
21055
+ }
21056
+ throw new Error(`Task list not found: "${input}"`);
21057
+ }
21058
+ }
21059
+ const scopedMatch = resolveTaskListFromCandidates(await cloudListTaskLists(client, projectId), input);
21060
+ if (scopedMatch)
21061
+ return scopedMatch;
21062
+ if (projectId) {
21063
+ const legacyMatch = resolveTaskListFromCandidates(await legacyProjectTaskLists(client, projectId), input);
21064
+ if (legacyMatch)
21065
+ return legacyMatch;
20816
21066
  }
20817
21067
  throw new Error(`Task list not found: "${input}"`);
20818
21068
  }
@@ -34926,7 +35176,7 @@ var package_default;
34926
35176
  var init_package = __esm(() => {
34927
35177
  package_default = {
34928
35178
  name: "@hasna/todos",
34929
- version: "0.15.5",
35179
+ version: "0.15.7",
34930
35180
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
34931
35181
  type: "module",
34932
35182
  main: "dist/index.js",
@@ -34964,6 +35214,10 @@ var init_package = __esm(() => {
34964
35214
  "./testing": {
34965
35215
  types: "./dist/testing.d.ts",
34966
35216
  import: "./dist/testing.js"
35217
+ },
35218
+ "./project-registration": {
35219
+ types: "./dist/project-registration.d.ts",
35220
+ import: "./dist/project-registration.js"
34967
35221
  }
34968
35222
  },
34969
35223
  workspaces: [
@@ -34976,8 +35230,8 @@ var init_package = __esm(() => {
34976
35230
  "README.md"
34977
35231
  ],
34978
35232
  scripts: {
34979
- build: "rm -rf dist dashboard/dist && cd dashboard && bun install --frozen-lockfile && bun run build && cd .. && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts src/testing.ts --outdir dist --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && tsc --emitDeclarationOnly --outDir dist",
34980
- "build:server": "rm -rf dist && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts src/testing.ts --outdir dist --target bun --external '@hasna/contracts' --external '@hasna/contracts/*'",
35233
+ build: "rm -rf dist dashboard/dist && cd dashboard && bun install --frozen-lockfile && bun run build && cd .. && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts src/testing.ts src/project-registration.ts --outdir dist --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && tsc --emitDeclarationOnly --outDir dist",
35234
+ "build:server": "rm -rf dist && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts src/testing.ts src/project-registration.ts --outdir dist --target bun --external '@hasna/contracts' --external '@hasna/contracts/*'",
34981
35235
  migrate: "bun run src/server/index.ts migrate",
34982
35236
  "backfill:comment-redaction": "bun run src/server/index.ts redact-comments",
34983
35237
  "generate:sdk": "bun run scripts/generate-sdk.ts",
@@ -47723,6 +47977,1433 @@ var init_postgres = __esm(() => {
47723
47977
  init_types3();
47724
47978
  });
47725
47979
 
47980
+ // src/project-registration/types.ts
47981
+ var TODOS_PROJECT_REGISTRATION_ROUTE = "todos.project-registration.v1", TODOS_PROJECT_REGISTRATION_CALLER_ROUTE = "projects.full-registration.v1", TodosProjectRegistrationError;
47982
+ var init_types4 = __esm(() => {
47983
+ TodosProjectRegistrationError = class TodosProjectRegistrationError extends Error {
47984
+ code;
47985
+ details;
47986
+ constructor(code, message, details = {}) {
47987
+ super(message);
47988
+ this.code = code;
47989
+ this.details = details;
47990
+ this.name = "TodosProjectRegistrationError";
47991
+ }
47992
+ };
47993
+ });
47994
+
47995
+ // src/project-registration/postgres.ts
47996
+ function safeIdentifier(value, field) {
47997
+ if (!/^[a-z_][a-z0-9_]*$/.test(value)) {
47998
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", `${field} must be a safe PostgreSQL identifier`);
47999
+ }
48000
+ return value;
48001
+ }
48002
+ function normalizeTimestamp2(value) {
48003
+ return value instanceof Date ? value.toISOString() : new Date(String(value)).toISOString();
48004
+ }
48005
+ function parsePayload3(value) {
48006
+ if (typeof value === "string")
48007
+ return JSON.parse(value);
48008
+ return value;
48009
+ }
48010
+ function receiptFromRow(row) {
48011
+ return {
48012
+ ...row,
48013
+ authority: "todos",
48014
+ created_by_operation: Boolean(row["created_by_operation"]),
48015
+ created_at: normalizeTimestamp2(row["created_at"])
48016
+ };
48017
+ }
48018
+ function bindingFromRow(row) {
48019
+ return {
48020
+ ...row,
48021
+ created_at: normalizeTimestamp2(row["created_at"]),
48022
+ updated_at: normalizeTimestamp2(row["updated_at"])
48023
+ };
48024
+ }
48025
+
48026
+ class PostgresTodosProjectRegistrationTransaction {
48027
+ client;
48028
+ service;
48029
+ tableName;
48030
+ storage;
48031
+ constructor(client, service, tableName, cursorTableName) {
48032
+ this.client = client;
48033
+ this.service = service;
48034
+ this.tableName = tableName;
48035
+ this.storage = createPostgresTodosStorageAdapter({
48036
+ client,
48037
+ service,
48038
+ tableName,
48039
+ cursorTableName
48040
+ });
48041
+ }
48042
+ async lockStep(identity) {
48043
+ const key = [
48044
+ identity.authority_id,
48045
+ identity.tenant_id,
48046
+ identity.corpus_id,
48047
+ identity.operation_id,
48048
+ identity.step_id,
48049
+ identity.resource_kind,
48050
+ identity.direction
48051
+ ].join("\x1F");
48052
+ await this.client.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [key]);
48053
+ }
48054
+ async getReceiptForLookup(identity) {
48055
+ const result = await this.client.query(`
48056
+ SELECT * FROM todos_project_registration_receipts
48057
+ WHERE authority_id = $1 AND tenant_id = $2 AND corpus_id = $3
48058
+ AND operation_id = $4 AND step_id = $5 AND resource_kind = $6
48059
+ AND direction = $7 AND idempotency_key = $8 AND target_selector = $9
48060
+ ORDER BY CASE outcome
48061
+ WHEN 'terminal_nonacceptance' THEN 0
48062
+ WHEN 'duplicate_of_accepted' THEN 1
48063
+ ELSE 2
48064
+ END, created_at DESC, receipt_id DESC
48065
+ LIMIT 1
48066
+ `, [
48067
+ identity.authority_id,
48068
+ identity.tenant_id,
48069
+ identity.corpus_id,
48070
+ identity.operation_id,
48071
+ identity.step_id,
48072
+ identity.resource_kind,
48073
+ identity.direction,
48074
+ identity.idempotency_key,
48075
+ identity.target_selector
48076
+ ]);
48077
+ return result.rows[0] ? receiptFromRow(result.rows[0]) : null;
48078
+ }
48079
+ async getReceiptById(receiptId) {
48080
+ const result = await this.client.query("SELECT * FROM todos_project_registration_receipts WHERE receipt_id = $1 LIMIT 1", [receiptId]);
48081
+ return result.rows[0] ? receiptFromRow(result.rows[0]) : null;
48082
+ }
48083
+ async getAcceptedReceiptForStep(identity) {
48084
+ const result = await this.client.query(`
48085
+ SELECT * FROM todos_project_registration_receipts
48086
+ WHERE authority_id = $1 AND tenant_id = $2 AND corpus_id = $3
48087
+ AND operation_id = $4 AND step_id = $5 AND resource_kind = $6
48088
+ AND direction = $7 AND outcome = 'accepted'
48089
+ ORDER BY created_at ASC, receipt_id ASC
48090
+ LIMIT 1
48091
+ FOR UPDATE
48092
+ `, [
48093
+ identity.authority_id,
48094
+ identity.tenant_id,
48095
+ identity.corpus_id,
48096
+ identity.operation_id,
48097
+ identity.step_id,
48098
+ identity.resource_kind,
48099
+ identity.direction
48100
+ ]);
48101
+ return result.rows[0] ? receiptFromRow(result.rows[0]) : null;
48102
+ }
48103
+ async insertReceipt(receipt) {
48104
+ const result = await this.client.query(`
48105
+ INSERT INTO todos_project_registration_receipts (
48106
+ receipt_id, authority, route, package_version, authority_id, tenant_id,
48107
+ corpus_id, operation_id, step_id, resource_kind, direction,
48108
+ target_selector, idempotency_key, request_digest, precondition_digest,
48109
+ normalized_call_digest, outcome, reason, target_id, result_revision,
48110
+ result_digest, duplicate_of_receipt_id, accepted_receipt_id,
48111
+ created_by_operation, created_at
48112
+ ) VALUES (
48113
+ $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,
48114
+ $19,$20,$21,$22,$23,$24,$25
48115
+ )
48116
+ ON CONFLICT (receipt_id) DO NOTHING
48117
+ RETURNING receipt_id
48118
+ `, [
48119
+ receipt.receipt_id,
48120
+ receipt.authority,
48121
+ receipt.route,
48122
+ receipt.package_version,
48123
+ receipt.authority_id,
48124
+ receipt.tenant_id,
48125
+ receipt.corpus_id,
48126
+ receipt.operation_id,
48127
+ receipt.step_id,
48128
+ receipt.resource_kind,
48129
+ receipt.direction,
48130
+ receipt.target_selector,
48131
+ receipt.idempotency_key,
48132
+ receipt.request_digest,
48133
+ receipt.precondition_digest,
48134
+ receipt.normalized_call_digest,
48135
+ receipt.outcome,
48136
+ receipt.reason,
48137
+ receipt.target_id,
48138
+ receipt.result_revision,
48139
+ receipt.result_digest,
48140
+ receipt.duplicate_of_receipt_id,
48141
+ receipt.accepted_receipt_id,
48142
+ receipt.created_by_operation,
48143
+ receipt.created_at
48144
+ ]);
48145
+ return result.rows.length === 1;
48146
+ }
48147
+ async getBinding(scope, resourceKind, targetSelector) {
48148
+ const result = await this.client.query(`
48149
+ SELECT * FROM todos_project_registration_bindings
48150
+ WHERE authority_id = $1 AND tenant_id = $2 AND corpus_id = $3
48151
+ AND resource_kind = $4 AND target_selector = $5
48152
+ LIMIT 1
48153
+ FOR UPDATE
48154
+ `, [
48155
+ scope.authority_id,
48156
+ scope.tenant_id,
48157
+ scope.corpus_id,
48158
+ resourceKind,
48159
+ targetSelector
48160
+ ]);
48161
+ return result.rows[0] ? bindingFromRow(result.rows[0]) : null;
48162
+ }
48163
+ async claimBinding(binding) {
48164
+ const result = await this.client.query(`
48165
+ INSERT INTO todos_project_registration_bindings (
48166
+ authority_id, tenant_id, corpus_id, resource_kind, target_selector,
48167
+ operation_id, step_id, direction, idempotency_key, request_digest,
48168
+ precondition_digest, normalized_call_digest, state, target_id,
48169
+ accepted_receipt_id, result_revision, result_digest, removed_receipt_id,
48170
+ created_at, updated_at
48171
+ ) VALUES (
48172
+ $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,
48173
+ $18,$19,$20
48174
+ )
48175
+ ON CONFLICT (
48176
+ authority_id, tenant_id, corpus_id, resource_kind, target_selector
48177
+ ) DO NOTHING
48178
+ RETURNING target_selector
48179
+ `, [
48180
+ binding.authority_id,
48181
+ binding.tenant_id,
48182
+ binding.corpus_id,
48183
+ binding.resource_kind,
48184
+ binding.target_selector,
48185
+ binding.operation_id,
48186
+ binding.step_id,
48187
+ binding.direction,
48188
+ binding.idempotency_key,
48189
+ binding.request_digest,
48190
+ binding.precondition_digest,
48191
+ binding.normalized_call_digest,
48192
+ binding.state,
48193
+ binding.target_id,
48194
+ binding.accepted_receipt_id,
48195
+ binding.result_revision,
48196
+ binding.result_digest,
48197
+ binding.removed_receipt_id,
48198
+ binding.created_at,
48199
+ binding.updated_at
48200
+ ]);
48201
+ return result.rows.length === 1;
48202
+ }
48203
+ async setBindingAccepted(scope, resourceKind, targetSelector, update) {
48204
+ const result = await this.client.query(`
48205
+ UPDATE todos_project_registration_bindings
48206
+ SET state = 'accepted', target_id = $1, accepted_receipt_id = $2,
48207
+ result_revision = $3, result_digest = $4, updated_at = $5
48208
+ WHERE authority_id = $6 AND tenant_id = $7 AND corpus_id = $8
48209
+ AND resource_kind = $9 AND target_selector = $10 AND state = 'pending'
48210
+ RETURNING target_selector
48211
+ `, [
48212
+ update.target_id,
48213
+ update.accepted_receipt_id,
48214
+ update.result_revision,
48215
+ update.result_digest,
48216
+ update.updated_at,
48217
+ scope.authority_id,
48218
+ scope.tenant_id,
48219
+ scope.corpus_id,
48220
+ resourceKind,
48221
+ targetSelector
48222
+ ]);
48223
+ if (result.rows.length !== 1) {
48224
+ throw new Error("Todos project registration binding was not pending at acceptance");
48225
+ }
48226
+ }
48227
+ async setBindingTerminal(scope, resourceKind, targetSelector, updatedAt) {
48228
+ await this.client.query(`
48229
+ UPDATE todos_project_registration_bindings
48230
+ SET state = 'terminal_nonacceptance', updated_at = $1
48231
+ WHERE authority_id = $2 AND tenant_id = $3 AND corpus_id = $4
48232
+ AND resource_kind = $5 AND target_selector = $6 AND state = 'pending'
48233
+ `, [
48234
+ updatedAt,
48235
+ scope.authority_id,
48236
+ scope.tenant_id,
48237
+ scope.corpus_id,
48238
+ resourceKind,
48239
+ targetSelector
48240
+ ]);
48241
+ }
48242
+ async setBindingRemoved(scope, resourceKind, targetSelector, removedReceiptId, updatedAt) {
48243
+ const result = await this.client.query(`
48244
+ UPDATE todos_project_registration_bindings
48245
+ SET state = 'removed', removed_receipt_id = $1, updated_at = $2
48246
+ WHERE authority_id = $3 AND tenant_id = $4 AND corpus_id = $5
48247
+ AND resource_kind = $6 AND target_selector = $7 AND state = 'accepted'
48248
+ RETURNING target_selector
48249
+ `, [
48250
+ removedReceiptId,
48251
+ updatedAt,
48252
+ scope.authority_id,
48253
+ scope.tenant_id,
48254
+ scope.corpus_id,
48255
+ resourceKind,
48256
+ targetSelector
48257
+ ]);
48258
+ if (result.rows.length !== 1) {
48259
+ throw new Error("Todos project registration binding was not accepted at removal");
48260
+ }
48261
+ }
48262
+ async findProjectConflict(path, taskListSlug) {
48263
+ const result = await this.client.query(`
48264
+ SELECT payload FROM ${this.tableName}
48265
+ WHERE service = $1 AND object_type = 'projects' AND deleted_at IS NULL
48266
+ AND (payload->>'path' = $2 OR payload->>'task_list_id' = $3)
48267
+ ORDER BY payload->>'created_at' ASC, object_id ASC
48268
+ LIMIT 1
48269
+ `, [this.service, path, taskListSlug]);
48270
+ return result.rows[0] ? parsePayload3(result.rows[0].payload) : null;
48271
+ }
48272
+ async findTaskListConflict(projectId, slug) {
48273
+ const result = await this.client.query(`
48274
+ SELECT payload FROM ${this.tableName}
48275
+ WHERE service = $1 AND object_type = 'task_lists' AND deleted_at IS NULL
48276
+ AND payload->>'project_id' = $2 AND payload->>'slug' = $3
48277
+ ORDER BY payload->>'created_at' ASC, object_id ASC
48278
+ LIMIT 1
48279
+ `, [this.service, projectId, slug]);
48280
+ return result.rows[0] ? parsePayload3(result.rows[0].payload) : null;
48281
+ }
48282
+ async createProject(input) {
48283
+ return await this.storage.projects.create(input);
48284
+ }
48285
+ async createTaskList(input) {
48286
+ return await this.storage.taskLists.create(input);
48287
+ }
48288
+ async getProject(id) {
48289
+ return await this.storage.projects.get(id);
48290
+ }
48291
+ async getTaskList(id) {
48292
+ return await this.storage.taskLists.get(id);
48293
+ }
48294
+ async lockCompensationWrites() {
48295
+ await this.client.query(`LOCK TABLE ${this.tableName} IN SHARE ROW EXCLUSIVE MODE`);
48296
+ }
48297
+ async hasDependents(resourceKind, targetId) {
48298
+ const referencePredicate = resourceKind === "project" ? `(
48299
+ payload->>'project_id' = $2
48300
+ OR payload->>'active_project_id' = $2
48301
+ OR payload->>'assigned_from_project' = $2
48302
+ OR payload->>'external_project_id' = $2
48303
+ )` : "payload->>'task_list_id' = $2";
48304
+ const result = await this.client.query(`
48305
+ SELECT EXISTS (
48306
+ SELECT 1 FROM ${this.tableName}
48307
+ WHERE service = $1 AND deleted_at IS NULL
48308
+ AND ${referencePredicate}
48309
+ LIMIT 1
48310
+ ) AS exists
48311
+ `, [this.service, targetId]);
48312
+ return result.rows[0]?.exists === true;
48313
+ }
48314
+ async deleteProject(id) {
48315
+ return await this.storage.projects.delete(id);
48316
+ }
48317
+ async deleteTaskList(id) {
48318
+ return await this.storage.taskLists.delete(id);
48319
+ }
48320
+ }
48321
+
48322
+ class PostgresTodosProjectRegistrationBackend {
48323
+ client;
48324
+ kind = "postgresql";
48325
+ service;
48326
+ tableName;
48327
+ cursorTableName;
48328
+ schemaReady = null;
48329
+ constructor(client, options = {}) {
48330
+ this.client = client;
48331
+ this.service = options.service ?? "todos";
48332
+ this.tableName = safeIdentifier(options.tableName ?? "todos_sync_records", "tableName");
48333
+ this.cursorTableName = safeIdentifier(options.cursorTableName ?? "todos_sync_cursors", "cursorTableName");
48334
+ }
48335
+ async ensureSchema() {
48336
+ this.schemaReady ??= (async () => {
48337
+ for (const statement of postgresTodosSyncSchemaSql(this.tableName, this.cursorTableName)) {
48338
+ await this.client.query(statement);
48339
+ }
48340
+ for (const statement of postgresTodosProjectRegistrationSchemaSql()) {
48341
+ await this.client.query(statement);
48342
+ }
48343
+ })();
48344
+ await this.schemaReady;
48345
+ }
48346
+ async transaction(fn) {
48347
+ await this.ensureSchema();
48348
+ if (typeof this.client.transaction !== "function") {
48349
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE", "PostgreSQL project registration requires an authoritative transaction");
48350
+ }
48351
+ return this.client.transaction((transaction) => fn(new PostgresTodosProjectRegistrationTransaction(transaction, this.service, this.tableName, this.cursorTableName)));
48352
+ }
48353
+ async direct() {
48354
+ await this.ensureSchema();
48355
+ return new PostgresTodosProjectRegistrationTransaction(this.client, this.service, this.tableName, this.cursorTableName);
48356
+ }
48357
+ async getReceiptForLookup(identity) {
48358
+ return (await this.direct()).getReceiptForLookup(identity);
48359
+ }
48360
+ async getReceiptById(receiptId) {
48361
+ return (await this.direct()).getReceiptById(receiptId);
48362
+ }
48363
+ async getBinding(scope, resourceKind, targetSelector) {
48364
+ return (await this.direct()).getBinding(scope, resourceKind, targetSelector);
48365
+ }
48366
+ async getProject(id) {
48367
+ return (await this.direct()).getProject(id);
48368
+ }
48369
+ async getTaskList(id) {
48370
+ return (await this.direct()).getTaskList(id);
48371
+ }
48372
+ }
48373
+ var init_postgres2 = __esm(() => {
48374
+ init_postgres_adapter();
48375
+ init_postgres_sync();
48376
+ init_types4();
48377
+ });
48378
+
48379
+ // src/project-registration/sqlite.ts
48380
+ var sqliteTransactionTails, PROJECT_REFERENCE_COLUMNS, TASK_LIST_REFERENCE_COLUMNS;
48381
+ var init_sqlite = __esm(() => {
48382
+ init_local_sqlite();
48383
+ sqliteTransactionTails = new WeakMap;
48384
+ PROJECT_REFERENCE_COLUMNS = new Set([
48385
+ "project_id",
48386
+ "active_project_id",
48387
+ "assigned_from_project",
48388
+ "external_project_id"
48389
+ ]);
48390
+ TASK_LIST_REFERENCE_COLUMNS = new Set(["task_list_id"]);
48391
+ });
48392
+
48393
+ // src/project-registration/authority.ts
48394
+ import { createHash as createHash14 } from "crypto";
48395
+ function canonicalProjectRegistrationJson(value) {
48396
+ return JSON.stringify(canonicalize2(value));
48397
+ }
48398
+ function canonicalize2(value) {
48399
+ if (Array.isArray(value))
48400
+ return value.map(canonicalize2);
48401
+ if (!value || typeof value !== "object")
48402
+ return value;
48403
+ const out = {};
48404
+ for (const key of Object.keys(value).sort()) {
48405
+ const entry2 = value[key];
48406
+ if (entry2 !== undefined)
48407
+ out[key] = canonicalize2(entry2);
48408
+ }
48409
+ return out;
48410
+ }
48411
+ function digestProjectRegistrationValue(value) {
48412
+ return createHash14("sha256").update(canonicalProjectRegistrationJson(value)).digest("hex");
48413
+ }
48414
+ function deriveTodosProjectRegistrationIdempotencyKey(input) {
48415
+ return `prk_${digestProjectRegistrationValue({
48416
+ route: TODOS_PROJECT_REGISTRATION_CALLER_ROUTE,
48417
+ ...input
48418
+ }).slice(0, 48)}`;
48419
+ }
48420
+ function responseBytes(value) {
48421
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
48422
+ }
48423
+ function assertBounds(bounds) {
48424
+ if (!Number.isSafeInteger(bounds.response_byte_limit) || bounds.response_byte_limit <= 0) {
48425
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "response_byte_limit must be a positive integer");
48426
+ }
48427
+ if (!Number.isSafeInteger(bounds.time_budget_ms) || bounds.time_budget_ms <= 0) {
48428
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "time_budget_ms must be a positive integer");
48429
+ }
48430
+ }
48431
+ function assertResourceKind(value) {
48432
+ if (value !== "project" && value !== "task_list") {
48433
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", "resource_kind must be project or task_list");
48434
+ }
48435
+ }
48436
+ function assertDirection(value) {
48437
+ if (value !== "forward" && value !== "inverse") {
48438
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", "direction must be forward or inverse");
48439
+ }
48440
+ }
48441
+ function assertWithinBounds(value, bounds, startedAt) {
48442
+ const bytes = responseBytes(value);
48443
+ if (bytes > bounds.response_byte_limit) {
48444
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_RESPONSE_TOO_LARGE", `registration response requires ${bytes} bytes but the bound is ${bounds.response_byte_limit}`, { response_bytes: bytes, response_byte_limit: bounds.response_byte_limit });
48445
+ }
48446
+ const elapsed = Date.now() - startedAt;
48447
+ if (elapsed > bounds.time_budget_ms) {
48448
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_TIME_BUDGET_EXCEEDED", `registration call took ${elapsed}ms but the bound is ${bounds.time_budget_ms}ms`, { elapsed_ms: elapsed, time_budget_ms: bounds.time_budget_ms });
48449
+ }
48450
+ return { response_bytes: bytes, elapsed_ms: elapsed };
48451
+ }
48452
+ function withResponseControl(payload, bounds, startedAt) {
48453
+ const envelope = {
48454
+ ...payload,
48455
+ response_control: {
48456
+ response_byte_limit: bounds.response_byte_limit,
48457
+ time_budget_ms: bounds.time_budget_ms,
48458
+ response_bytes: 0,
48459
+ elapsed_ms: 0,
48460
+ complete: true,
48461
+ truncated: false
48462
+ }
48463
+ };
48464
+ for (let attempt = 0;attempt < 8; attempt += 1) {
48465
+ const measured = assertWithinBounds(envelope, bounds, startedAt);
48466
+ const stable3 = envelope.response_control.response_bytes === measured.response_bytes && envelope.response_control.elapsed_ms === measured.elapsed_ms;
48467
+ envelope.response_control = {
48468
+ response_byte_limit: bounds.response_byte_limit,
48469
+ time_budget_ms: bounds.time_budget_ms,
48470
+ response_bytes: measured.response_bytes,
48471
+ elapsed_ms: measured.elapsed_ms,
48472
+ complete: true,
48473
+ truncated: false
48474
+ };
48475
+ if (stable3)
48476
+ break;
48477
+ }
48478
+ const finalMeasurement = assertWithinBounds(envelope, bounds, startedAt);
48479
+ envelope.response_control.response_bytes = finalMeasurement.response_bytes;
48480
+ envelope.response_control.elapsed_ms = finalMeasurement.elapsed_ms;
48481
+ return envelope;
48482
+ }
48483
+ function requireString(value, field, options = {}) {
48484
+ const min = options.min ?? 1;
48485
+ const max = options.max ?? 512;
48486
+ if (typeof value !== "string" || value.length < min || value.length > max || /[\u0000-\u001f]/.test(value) || options.pattern && !options.pattern.test(value)) {
48487
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", `${field} is not a valid bounded registration identifier`);
48488
+ }
48489
+ return value;
48490
+ }
48491
+ function exactKeys(value, expected, field) {
48492
+ const actual = Object.keys(value).sort();
48493
+ const wanted = [...expected].sort();
48494
+ if (actual.length !== wanted.length || actual.some((key, index) => key !== wanted[index])) {
48495
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", `${field} must contain exactly: ${wanted.join(", ")}`);
48496
+ }
48497
+ }
48498
+ function publicReceipt(row) {
48499
+ const {
48500
+ target_selector: _targetSelector,
48501
+ normalized_call_digest: _normalizedCallDigest,
48502
+ ...receipt
48503
+ } = row;
48504
+ return receipt;
48505
+ }
48506
+ function projectRegistrationPath(projectId) {
48507
+ return `hasna-project://${encodeURIComponent(projectId)}`;
48508
+ }
48509
+ function taskListSlug(projectSlug) {
48510
+ const slug = normalizeSlug(projectSlug);
48511
+ if (!slug || slug !== projectSlug) {
48512
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", "project_slug must be canonical kebab-case");
48513
+ }
48514
+ return `todos-${slug}`;
48515
+ }
48516
+ function deterministicTaskPrefix(projectSlug) {
48517
+ const letters = projectSlug.replace(/[^a-z0-9]/gi, "").toUpperCase();
48518
+ return (letters.slice(0, 3) || "PRJ").padEnd(3, "X");
48519
+ }
48520
+ function projectRecord(project) {
48521
+ return {
48522
+ target_id: project.id,
48523
+ revision: project.updated_at,
48524
+ digest: digestProjectRegistrationValue({
48525
+ id: project.id,
48526
+ name: project.name,
48527
+ path: project.path,
48528
+ description: project.description,
48529
+ task_list_id: project.task_list_id,
48530
+ task_prefix: project.task_prefix,
48531
+ task_counter: project.task_counter,
48532
+ created_at: project.created_at,
48533
+ updated_at: project.updated_at
48534
+ })
48535
+ };
48536
+ }
48537
+ function taskListRecord(taskList) {
48538
+ return {
48539
+ target_id: taskList.id,
48540
+ revision: taskList.updated_at,
48541
+ digest: digestProjectRegistrationValue({
48542
+ id: taskList.id,
48543
+ project_id: taskList.project_id,
48544
+ slug: taskList.slug,
48545
+ name: taskList.name,
48546
+ description: taskList.description,
48547
+ metadata: taskList.metadata,
48548
+ created_at: taskList.created_at,
48549
+ updated_at: taskList.updated_at
48550
+ })
48551
+ };
48552
+ }
48553
+ function receiptId(input) {
48554
+ return `tpr_${digestProjectRegistrationValue(input).slice(0, 40)}`;
48555
+ }
48556
+ function capabilityMatches(request, capability) {
48557
+ return request.authority_route === capability.route && request.package_version === capability.package_version && request.authority_id === capability.authority_id && request.tenant_id === capability.tenant_id && request.corpus_id === capability.corpus_id;
48558
+ }
48559
+ function authorityScope(capability) {
48560
+ return {
48561
+ authority_id: capability.authority_id,
48562
+ tenant_id: capability.tenant_id,
48563
+ corpus_id: capability.corpus_id
48564
+ };
48565
+ }
48566
+ function assertCapabilityRequest(request, capability) {
48567
+ if (!capabilityMatches(request, capability)) {
48568
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH", "registration request does not match this authority capability identity");
48569
+ }
48570
+ }
48571
+ function normalizedCallDigest(request) {
48572
+ return digestProjectRegistrationValue({
48573
+ authority_route: request.authority_route,
48574
+ package_version: request.package_version,
48575
+ authority_id: request.authority_id,
48576
+ tenant_id: request.tenant_id,
48577
+ corpus_id: request.corpus_id,
48578
+ operation_id: request.operation_id,
48579
+ step_id: request.step_id,
48580
+ resource_kind: request.resource_kind,
48581
+ direction: request.direction,
48582
+ target_selector: request.target_selector,
48583
+ idempotency_key: request.idempotency_key,
48584
+ request_digest: request.request_digest,
48585
+ precondition_digest: request.precondition_digest,
48586
+ project_id: request.project_id,
48587
+ project_slug: request.project_slug,
48588
+ project_name: request.project_name,
48589
+ desired: request.desired,
48590
+ accepted_receipt_id: request.accepted_receipt?.receipt_id ?? null
48591
+ });
48592
+ }
48593
+ function assertCommonRequest(request, capability) {
48594
+ assertBounds(request);
48595
+ assertResourceKind(request.resource_kind);
48596
+ assertDirection(request.direction);
48597
+ assertCapabilityRequest(request, capability);
48598
+ requireString(request.operation_id, "operation_id", {
48599
+ min: 8,
48600
+ max: 128,
48601
+ pattern: OPERATION_PATTERN
48602
+ });
48603
+ requireString(request.step_id, "step_id", {
48604
+ min: 3,
48605
+ max: 128,
48606
+ pattern: STEP_PATTERN
48607
+ });
48608
+ requireString(request.target_selector, "target_selector", { max: 512 });
48609
+ requireString(request.project_id, "project_id", {
48610
+ min: 16,
48611
+ max: 128,
48612
+ pattern: WORKSPACE_ID_PATTERN
48613
+ });
48614
+ requireString(request.project_name, "project_name", { max: 256 });
48615
+ requireString(request.project_slug, "project_slug", { max: 128 });
48616
+ requireString(request.request_digest, "request_digest", {
48617
+ min: 64,
48618
+ max: 64,
48619
+ pattern: SHA256_PATTERN
48620
+ });
48621
+ requireString(request.precondition_digest, "precondition_digest", {
48622
+ min: 64,
48623
+ max: 64,
48624
+ pattern: SHA256_PATTERN
48625
+ });
48626
+ requireString(request.idempotency_key, "idempotency_key", {
48627
+ min: 52,
48628
+ max: 52,
48629
+ pattern: IDEMPOTENCY_PATTERN
48630
+ });
48631
+ if (!request.desired || typeof request.desired !== "object" || Array.isArray(request.desired)) {
48632
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", "desired must be a JSON object");
48633
+ }
48634
+ const expectedKey = deriveTodosProjectRegistrationIdempotencyKey({
48635
+ operation_id: request.operation_id,
48636
+ step_id: request.step_id,
48637
+ direction: request.direction,
48638
+ target_selector: request.target_selector,
48639
+ request_digest: request.request_digest,
48640
+ precondition_digest: request.precondition_digest
48641
+ });
48642
+ if (request.idempotency_key !== expectedKey) {
48643
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH", "idempotency_key does not match the deterministic operation/step/direction payload", { expected: expectedKey });
48644
+ }
48645
+ taskListSlug(request.project_slug);
48646
+ }
48647
+ function assertForwardRequest(request, capability) {
48648
+ assertCommonRequest(request, capability);
48649
+ if (request.direction !== "forward") {
48650
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", "create requires direction=forward");
48651
+ }
48652
+ const expectedRequestDigest = digestProjectRegistrationValue(request.desired);
48653
+ const expectedPreconditionDigest = digestProjectRegistrationValue({
48654
+ target_selector: request.target_selector,
48655
+ expected: "absent"
48656
+ });
48657
+ if (request.request_digest !== expectedRequestDigest || request.precondition_digest !== expectedPreconditionDigest) {
48658
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "request_digest or precondition_digest does not match normalized forward semantics", {
48659
+ expected_request_digest: expectedRequestDigest,
48660
+ expected_precondition_digest: expectedPreconditionDigest
48661
+ });
48662
+ }
48663
+ if (request.resource_kind === "project") {
48664
+ exactKeys(request.desired, ["source_project_id", "source_project_slug", "name"], "project desired");
48665
+ if (request.desired["source_project_id"] !== request.project_id || request.desired["source_project_slug"] !== request.project_slug || request.desired["name"] !== request.project_name || request.target_selector !== request.project_id) {
48666
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", "project desired state and target selector must match the complete Projects identity");
48667
+ }
48668
+ return;
48669
+ }
48670
+ if (request.resource_kind === "task_list") {
48671
+ exactKeys(request.desired, ["todos_project_id", "source_project_id", "name"], "task-list desired");
48672
+ const todosProjectId = request.desired["todos_project_id"];
48673
+ if (typeof todosProjectId !== "string" || !UUID_PATTERN.test(todosProjectId)) {
48674
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_EXACT_ID_REQUIRED", "task-list create requires the exact full Todos project UUID");
48675
+ }
48676
+ if (request.target_selector !== `${todosProjectId}:default` || request.desired["source_project_id"] !== request.project_id || request.desired["name"] !== request.project_name) {
48677
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", "task-list desired state must bind the exact Todos project id and Projects identity");
48678
+ }
48679
+ return;
48680
+ }
48681
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", "unsupported registration resource kind");
48682
+ }
48683
+ function assertInverseRequest(request, capability) {
48684
+ assertCommonRequest(request, capability);
48685
+ if (request.direction !== "inverse") {
48686
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", "compensate requires direction=inverse");
48687
+ }
48688
+ const accepted = request.accepted_receipt;
48689
+ if (!accepted || accepted.authority !== "todos" || accepted.route !== capability.route || accepted.package_version !== capability.package_version || accepted.authority_id !== capability.authority_id || accepted.tenant_id !== capability.tenant_id || accepted.corpus_id !== capability.corpus_id || accepted.operation_id !== request.operation_id || accepted.step_id !== request.step_id || accepted.resource_kind !== request.resource_kind || accepted.direction !== "forward" || accepted.outcome !== "accepted" || !accepted.created_by_operation || !accepted.target_id || !accepted.result_revision || !accepted.result_digest) {
48690
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_INPUT", "inverse requires the complete accepted forward receipt created by this operation");
48691
+ }
48692
+ exactKeys(request.desired, ["accepted_receipt_id", "target_id"], "inverse desired");
48693
+ const expectedDesired = {
48694
+ accepted_receipt_id: accepted.receipt_id,
48695
+ target_id: accepted.target_id
48696
+ };
48697
+ const expectedPrecondition = {
48698
+ expected_revision: accepted.result_revision,
48699
+ expected_digest: accepted.result_digest
48700
+ };
48701
+ const expectedRequestDigest = digestProjectRegistrationValue(expectedDesired);
48702
+ const expectedPreconditionDigest = digestProjectRegistrationValue(expectedPrecondition);
48703
+ if (canonicalProjectRegistrationJson(request.desired) !== canonicalProjectRegistrationJson(expectedDesired) || request.request_digest !== expectedRequestDigest || request.precondition_digest !== expectedPreconditionDigest || request.target_selector !== accepted.target_id) {
48704
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "inverse request does not match the accepted receipt and exact readback precondition");
48705
+ }
48706
+ return accepted;
48707
+ }
48708
+ function makeReceipt(input, createdAt2) {
48709
+ return {
48710
+ ...input,
48711
+ receipt_id: receiptId(input),
48712
+ created_at: createdAt2
48713
+ };
48714
+ }
48715
+ function receiptBase(request, callDigest, capability) {
48716
+ return {
48717
+ authority: "todos",
48718
+ route: capability.route,
48719
+ package_version: capability.package_version,
48720
+ authority_id: capability.authority_id,
48721
+ tenant_id: capability.tenant_id,
48722
+ corpus_id: capability.corpus_id,
48723
+ operation_id: request.operation_id,
48724
+ step_id: request.step_id,
48725
+ resource_kind: request.resource_kind,
48726
+ direction: request.direction,
48727
+ target_selector: request.target_selector,
48728
+ idempotency_key: request.idempotency_key,
48729
+ request_digest: request.request_digest,
48730
+ precondition_digest: request.precondition_digest,
48731
+ normalized_call_digest: callDigest
48732
+ };
48733
+ }
48734
+ function makeAcceptedReceipt(request, callDigest, capability, record, createdAt2) {
48735
+ return makeReceipt({
48736
+ ...receiptBase(request, callDigest, capability),
48737
+ outcome: "accepted",
48738
+ reason: null,
48739
+ target_id: record.target_id,
48740
+ result_revision: record.revision,
48741
+ result_digest: record.digest,
48742
+ duplicate_of_receipt_id: null,
48743
+ accepted_receipt_id: request.direction === "inverse" ? request.accepted_receipt.receipt_id : null,
48744
+ created_by_operation: true
48745
+ }, createdAt2);
48746
+ }
48747
+ function makeDuplicateReceipt(request, callDigest, capability, accepted, createdAt2) {
48748
+ return makeReceipt({
48749
+ ...receiptBase(request, callDigest, capability),
48750
+ outcome: "duplicate_of_accepted",
48751
+ reason: null,
48752
+ target_id: accepted.target_id,
48753
+ result_revision: accepted.result_revision,
48754
+ result_digest: accepted.result_digest,
48755
+ duplicate_of_receipt_id: accepted.receipt_id,
48756
+ accepted_receipt_id: null,
48757
+ created_by_operation: false
48758
+ }, createdAt2);
48759
+ }
48760
+ function makeTerminalReceipt(request, callDigest, capability, reason, createdAt2, options = {}) {
48761
+ return makeReceipt({
48762
+ ...receiptBase(request, callDigest, capability),
48763
+ outcome: "terminal_nonacceptance",
48764
+ reason,
48765
+ target_id: options.targetId ?? null,
48766
+ result_revision: null,
48767
+ result_digest: null,
48768
+ duplicate_of_receipt_id: null,
48769
+ accepted_receipt_id: options.acceptedReceiptId ?? null,
48770
+ created_by_operation: false
48771
+ }, createdAt2);
48772
+ }
48773
+ async function insertDeterministicReceipt(transaction, receipt) {
48774
+ if (await transaction.insertReceipt(receipt))
48775
+ return receipt;
48776
+ const existing = await transaction.getReceiptById(receipt.receipt_id);
48777
+ const { created_at: _existingCreatedAt, ...existingContent } = existing ?? {};
48778
+ const { created_at: _receiptCreatedAt, ...receiptContent } = receipt;
48779
+ if (!existing || canonicalProjectRegistrationJson(existingContent) !== canonicalProjectRegistrationJson(receiptContent)) {
48780
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_CONFLICT", "deterministic receipt id is occupied by different immutable content", { receipt_id: receipt.receipt_id });
48781
+ }
48782
+ return existing;
48783
+ }
48784
+ function bindingFor(request, callDigest, timestamp4, capability) {
48785
+ return {
48786
+ ...authorityScope(capability),
48787
+ resource_kind: request.resource_kind,
48788
+ target_selector: request.target_selector,
48789
+ operation_id: request.operation_id,
48790
+ step_id: request.step_id,
48791
+ direction: "forward",
48792
+ idempotency_key: request.idempotency_key,
48793
+ request_digest: request.request_digest,
48794
+ precondition_digest: request.precondition_digest,
48795
+ normalized_call_digest: callDigest,
48796
+ state: "pending",
48797
+ target_id: null,
48798
+ accepted_receipt_id: null,
48799
+ result_revision: null,
48800
+ result_digest: null,
48801
+ removed_receipt_id: null,
48802
+ created_at: timestamp4,
48803
+ updated_at: timestamp4
48804
+ };
48805
+ }
48806
+
48807
+ class PackageOwnedTodosProjectRegistrationAuthority {
48808
+ backend;
48809
+ authority = "todos";
48810
+ capabilityValue;
48811
+ now;
48812
+ faultInjector;
48813
+ constructor(backend, options = {}) {
48814
+ this.backend = backend;
48815
+ this.capabilityValue = {
48816
+ authority: "todos",
48817
+ route: TODOS_PROJECT_REGISTRATION_ROUTE,
48818
+ package_version: options.packageVersion ?? getPackageVersion(import.meta.url),
48819
+ authority_id: options.authorityId ?? "todos",
48820
+ tenant_id: options.tenantId ?? backend.kind,
48821
+ corpus_id: options.corpusId ?? `todos:${backend.kind}`,
48822
+ supported_resources: ["project", "task_list"],
48823
+ conditional_create: true,
48824
+ immutable_receipts: true,
48825
+ exact_terminal_lookup: true,
48826
+ exact_readback: true,
48827
+ conditional_inverse: true,
48828
+ ambiguous_outcome_reconciliation: true
48829
+ };
48830
+ this.now = options.now ?? (() => new Date().toISOString());
48831
+ this.faultInjector = options.faultInjector;
48832
+ }
48833
+ async capability() {
48834
+ return {
48835
+ ...this.capabilityValue,
48836
+ supported_resources: [...this.capabilityValue.supported_resources]
48837
+ };
48838
+ }
48839
+ async fault(point, request) {
48840
+ if (!this.faultInjector)
48841
+ return;
48842
+ try {
48843
+ await this.faultInjector(point, {
48844
+ operation_id: request.operation_id,
48845
+ step_id: request.step_id,
48846
+ resource_kind: request.resource_kind,
48847
+ direction: request.direction
48848
+ });
48849
+ } catch (cause) {
48850
+ throw new WriteBoundaryError(point, cause);
48851
+ }
48852
+ }
48853
+ async afterCommit(request) {
48854
+ await this.faultInjector?.("after_commit", {
48855
+ operation_id: request.operation_id,
48856
+ step_id: request.step_id,
48857
+ resource_kind: request.resource_kind,
48858
+ direction: request.direction
48859
+ });
48860
+ }
48861
+ async duplicateFor(transaction, request, callDigest, accepted) {
48862
+ const duplicate = makeDuplicateReceipt(request, callDigest, this.capabilityValue, accepted, this.now());
48863
+ return insertDeterministicReceipt(transaction, duplicate);
48864
+ }
48865
+ async terminalFor(transaction, request, callDigest, reason, options = {}) {
48866
+ return insertDeterministicReceipt(transaction, makeTerminalReceipt(request, callDigest, this.capabilityValue, reason, this.now(), options));
48867
+ }
48868
+ async existingForwardResolution(transaction, request, callDigest) {
48869
+ const exact = await transaction.getReceiptForLookup({
48870
+ ...authorityScope(this.capabilityValue),
48871
+ operation_id: request.operation_id,
48872
+ step_id: request.step_id,
48873
+ resource_kind: request.resource_kind,
48874
+ direction: request.direction,
48875
+ idempotency_key: request.idempotency_key,
48876
+ target_selector: request.target_selector
48877
+ });
48878
+ if (exact) {
48879
+ if (exact.outcome === "terminal_nonacceptance")
48880
+ return exact;
48881
+ const accepted2 = exact.outcome === "accepted" ? exact : await transaction.getReceiptById(exact.duplicate_of_receipt_id);
48882
+ if (!accepted2) {
48883
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_CONFLICT", "duplicate receipt points to a missing accepted receipt");
48884
+ }
48885
+ if (accepted2.normalized_call_digest !== callDigest) {
48886
+ return this.terminalFor(transaction, request, callDigest, "operation_step_semantics_changed", { targetId: accepted2.target_id });
48887
+ }
48888
+ return this.duplicateFor(transaction, request, callDigest, accepted2);
48889
+ }
48890
+ const accepted = await transaction.getAcceptedReceiptForStep({
48891
+ ...authorityScope(this.capabilityValue),
48892
+ operation_id: request.operation_id,
48893
+ step_id: request.step_id,
48894
+ resource_kind: request.resource_kind,
48895
+ direction: "forward"
48896
+ });
48897
+ if (!accepted)
48898
+ return null;
48899
+ if (accepted.normalized_call_digest === callDigest) {
48900
+ return this.duplicateFor(transaction, request, callDigest, accepted);
48901
+ }
48902
+ return this.terminalFor(transaction, request, callDigest, "operation_step_semantics_changed", { targetId: accepted.target_id });
48903
+ }
48904
+ async createObject(transaction, request) {
48905
+ if (request.resource_kind === "project") {
48906
+ const path = projectRegistrationPath(request.project_id);
48907
+ const slug2 = taskListSlug(request.project_slug);
48908
+ const conflict2 = await transaction.findProjectConflict(path, slug2);
48909
+ if (conflict2) {
48910
+ return this.terminalFor(transaction, request, normalizedCallDigest(request), "target_already_exists", { targetId: conflict2.id });
48911
+ }
48912
+ await this.fault("before_object_write", request);
48913
+ const project = await transaction.createProject({
48914
+ name: request.project_name,
48915
+ path,
48916
+ description: `Registered from Projects workspace ${request.project_id}`,
48917
+ task_list_id: slug2,
48918
+ task_prefix: deterministicTaskPrefix(request.project_slug)
48919
+ });
48920
+ await this.fault("after_object_write", request);
48921
+ return projectRecord(project);
48922
+ }
48923
+ const todosProjectId = String(request.desired["todos_project_id"]);
48924
+ const sourceBinding = await transaction.getBinding(authorityScope(this.capabilityValue), "project", request.project_id);
48925
+ if (!sourceBinding || sourceBinding.state !== "accepted" || sourceBinding.target_id !== todosProjectId) {
48926
+ return this.terminalFor(transaction, request, normalizedCallDigest(request), "exact_parent_registration_missing", { targetId: todosProjectId });
48927
+ }
48928
+ const parent = await transaction.getProject(todosProjectId);
48929
+ if (!parent) {
48930
+ return this.terminalFor(transaction, request, normalizedCallDigest(request), "exact_parent_project_missing", { targetId: todosProjectId });
48931
+ }
48932
+ const slug = taskListSlug(request.project_slug);
48933
+ const conflict = await transaction.findTaskListConflict(todosProjectId, slug);
48934
+ if (conflict) {
48935
+ return this.terminalFor(transaction, request, normalizedCallDigest(request), "target_already_exists", { targetId: conflict.id });
48936
+ }
48937
+ await this.fault("before_object_write", request);
48938
+ const taskList = await transaction.createTaskList({
48939
+ name: request.project_name,
48940
+ slug,
48941
+ project_id: todosProjectId,
48942
+ metadata: {
48943
+ source_project_id: request.project_id,
48944
+ registration_authority: "todos"
48945
+ }
48946
+ });
48947
+ await this.fault("after_object_write", request);
48948
+ if (taskList.project_id !== todosProjectId) {
48949
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_CONFLICT", "task-list create did not preserve the exact full Todos project id");
48950
+ }
48951
+ return taskListRecord(taskList);
48952
+ }
48953
+ async create(request) {
48954
+ const startedAt = Date.now();
48955
+ assertForwardRequest(request, this.capabilityValue);
48956
+ const callDigest = normalizedCallDigest(request);
48957
+ try {
48958
+ const row = await this.backend.transaction(async (transaction) => {
48959
+ await transaction.lockStep({
48960
+ ...authorityScope(this.capabilityValue),
48961
+ operation_id: request.operation_id,
48962
+ step_id: request.step_id,
48963
+ resource_kind: request.resource_kind,
48964
+ direction: request.direction
48965
+ });
48966
+ const resolved = await this.existingForwardResolution(transaction, request, callDigest);
48967
+ if (resolved)
48968
+ return resolved;
48969
+ const timestamp4 = this.now();
48970
+ const claimed = await transaction.claimBinding(bindingFor(request, callDigest, timestamp4, this.capabilityValue));
48971
+ if (!claimed) {
48972
+ const binding = await transaction.getBinding(authorityScope(this.capabilityValue), request.resource_kind, request.target_selector);
48973
+ if (binding?.state === "accepted" && binding.normalized_call_digest === callDigest && binding.accepted_receipt_id) {
48974
+ const accepted2 = await transaction.getReceiptById(binding.accepted_receipt_id);
48975
+ if (accepted2) {
48976
+ return this.duplicateFor(transaction, request, callDigest, accepted2);
48977
+ }
48978
+ }
48979
+ return this.terminalFor(transaction, request, callDigest, binding?.state === "removed" ? "target_registration_was_removed" : "target_already_registered", { targetId: binding?.target_id ?? null });
48980
+ }
48981
+ const recordOrTerminal = await this.createObject(transaction, request);
48982
+ if ("outcome" in recordOrTerminal) {
48983
+ await transaction.setBindingTerminal(authorityScope(this.capabilityValue), request.resource_kind, request.target_selector, this.now());
48984
+ return recordOrTerminal;
48985
+ }
48986
+ const accepted = makeAcceptedReceipt(request, callDigest, this.capabilityValue, recordOrTerminal, this.now());
48987
+ await this.fault("before_receipt_write", request);
48988
+ const stored = await insertDeterministicReceipt(transaction, accepted);
48989
+ await this.fault("after_receipt_write", request);
48990
+ await transaction.setBindingAccepted(authorityScope(this.capabilityValue), request.resource_kind, request.target_selector, {
48991
+ target_id: recordOrTerminal.target_id,
48992
+ accepted_receipt_id: stored.receipt_id,
48993
+ result_revision: recordOrTerminal.revision,
48994
+ result_digest: recordOrTerminal.digest,
48995
+ updated_at: this.now()
48996
+ });
48997
+ return stored;
48998
+ });
48999
+ await this.afterCommit(request);
49000
+ const receipt = publicReceipt(row);
49001
+ assertWithinBounds(receipt, request, startedAt);
49002
+ return receipt;
49003
+ } catch (error) {
49004
+ if (!(error instanceof WriteBoundaryError))
49005
+ throw error;
49006
+ const terminal = await this.recordWriteFailure(request, callDigest, error.point);
49007
+ const receipt = publicReceipt(terminal);
49008
+ assertWithinBounds(receipt, request, startedAt);
49009
+ return receipt;
49010
+ }
49011
+ }
49012
+ async recordWriteFailure(request, callDigest, point) {
49013
+ return this.backend.transaction(async (transaction) => {
49014
+ await transaction.lockStep({
49015
+ ...authorityScope(this.capabilityValue),
49016
+ operation_id: request.operation_id,
49017
+ step_id: request.step_id,
49018
+ resource_kind: request.resource_kind,
49019
+ direction: request.direction
49020
+ });
49021
+ const exact = await transaction.getReceiptForLookup({
49022
+ ...authorityScope(this.capabilityValue),
49023
+ operation_id: request.operation_id,
49024
+ step_id: request.step_id,
49025
+ resource_kind: request.resource_kind,
49026
+ direction: request.direction,
49027
+ idempotency_key: request.idempotency_key,
49028
+ target_selector: request.target_selector
49029
+ });
49030
+ if (exact)
49031
+ return exact;
49032
+ const accepted = await transaction.getAcceptedReceiptForStep({
49033
+ ...authorityScope(this.capabilityValue),
49034
+ operation_id: request.operation_id,
49035
+ step_id: request.step_id,
49036
+ resource_kind: request.resource_kind,
49037
+ direction: request.direction
49038
+ });
49039
+ if (accepted) {
49040
+ return accepted.normalized_call_digest === callDigest ? this.duplicateFor(transaction, request, callDigest, accepted) : this.terminalFor(transaction, request, callDigest, "operation_step_semantics_changed", { targetId: accepted.target_id });
49041
+ }
49042
+ const timestamp4 = this.now();
49043
+ const claimed = await transaction.claimBinding(bindingFor(request, callDigest, timestamp4, this.capabilityValue));
49044
+ const terminal = await this.terminalFor(transaction, request, callDigest, `write_failed:${point}`);
49045
+ if (claimed) {
49046
+ await transaction.setBindingTerminal(authorityScope(this.capabilityValue), request.resource_kind, request.target_selector, this.now());
49047
+ }
49048
+ return terminal;
49049
+ });
49050
+ }
49051
+ async readExact(request) {
49052
+ const startedAt = Date.now();
49053
+ assertBounds(request);
49054
+ assertResourceKind(request.resource_kind);
49055
+ if (!UUID_PATTERN.test(request.target_id)) {
49056
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_EXACT_ID_REQUIRED", "exact readback requires a complete Todos object UUID");
49057
+ }
49058
+ const record = request.resource_kind === "project" ? await this.backend.getProject(request.target_id).then((value) => value ? projectRecord(value) : null) : await this.backend.getTaskList(request.target_id).then((value) => value ? taskListRecord(value) : null);
49059
+ if (!record) {
49060
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_RECORD_NOT_FOUND", `registered ${request.resource_kind} was not found by exact id`, { target_id: request.target_id });
49061
+ }
49062
+ assertWithinBounds(record, request, startedAt);
49063
+ return record;
49064
+ }
49065
+ async lookupReceipt(request) {
49066
+ const startedAt = Date.now();
49067
+ assertBounds(request);
49068
+ assertResourceKind(request.resource_kind);
49069
+ assertDirection(request.direction);
49070
+ if (request.max_items !== 1) {
49071
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "max_items must be exactly 1 for terminal receipt lookup");
49072
+ }
49073
+ if (request.authority !== "todos" || request.authority_route !== this.capabilityValue.route || request.package_version !== this.capabilityValue.package_version || request.authority_id !== this.capabilityValue.authority_id || request.tenant_id !== this.capabilityValue.tenant_id || request.corpus_id !== this.capabilityValue.corpus_id) {
49074
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH", "receipt lookup does not match this authority capability identity");
49075
+ }
49076
+ requireString(request.operation_id, "operation_id", {
49077
+ min: 8,
49078
+ max: 128,
49079
+ pattern: OPERATION_PATTERN
49080
+ });
49081
+ requireString(request.step_id, "step_id", {
49082
+ min: 3,
49083
+ max: 128,
49084
+ pattern: STEP_PATTERN
49085
+ });
49086
+ requireString(request.target_selector, "target_selector", { max: 512 });
49087
+ requireString(request.idempotency_key, "idempotency_key", {
49088
+ min: 52,
49089
+ max: 52,
49090
+ pattern: IDEMPOTENCY_PATTERN
49091
+ });
49092
+ if (request.target_id !== undefined && !UUID_PATTERN.test(request.target_id)) {
49093
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_EXACT_ID_REQUIRED", "receipt lookup target_id must be a complete Todos object UUID");
49094
+ }
49095
+ const receipt = await this.backend.getReceiptForLookup({
49096
+ ...authorityScope(this.capabilityValue),
49097
+ operation_id: request.operation_id,
49098
+ step_id: request.step_id,
49099
+ resource_kind: request.resource_kind,
49100
+ direction: request.direction,
49101
+ idempotency_key: request.idempotency_key,
49102
+ target_selector: request.target_selector
49103
+ });
49104
+ if (!receipt || request.target_id && receipt.target_id !== request.target_id) {
49105
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND", "no exact terminal receipt matched the bounded lookup");
49106
+ }
49107
+ return withResponseControl({ receipt: publicReceipt(receipt) }, request, startedAt);
49108
+ }
49109
+ async storedAcceptedReceipt(request, supplied) {
49110
+ const stored = await this.backend.getReceiptById(supplied.receipt_id);
49111
+ if (!stored || stored.outcome !== "accepted" || !stored.created_by_operation || canonicalProjectRegistrationJson(publicReceipt(stored)) !== canonicalProjectRegistrationJson(supplied)) {
49112
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND", "accepted receipt is not an exact immutable receipt owned by this authority", { receipt_id: supplied.receipt_id });
49113
+ }
49114
+ if (stored.operation_id !== request.operation_id || stored.step_id !== request.step_id || stored.resource_kind !== request.resource_kind || stored.target_id !== supplied.target_id) {
49115
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND", "accepted receipt does not own this exact operation step and target");
49116
+ }
49117
+ return stored;
49118
+ }
49119
+ async compensate(request) {
49120
+ const startedAt = Date.now();
49121
+ const suppliedAccepted = assertInverseRequest(request, this.capabilityValue);
49122
+ const accepted = await this.storedAcceptedReceipt(request, suppliedAccepted);
49123
+ const callDigest = normalizedCallDigest(request);
49124
+ try {
49125
+ const row = await this.backend.transaction(async (transaction) => {
49126
+ await transaction.lockStep({
49127
+ ...authorityScope(this.capabilityValue),
49128
+ operation_id: request.operation_id,
49129
+ step_id: request.step_id,
49130
+ resource_kind: request.resource_kind,
49131
+ direction: request.direction
49132
+ });
49133
+ const exact = await transaction.getReceiptForLookup({
49134
+ ...authorityScope(this.capabilityValue),
49135
+ operation_id: request.operation_id,
49136
+ step_id: request.step_id,
49137
+ resource_kind: request.resource_kind,
49138
+ direction: "inverse",
49139
+ idempotency_key: request.idempotency_key,
49140
+ target_selector: request.target_selector
49141
+ });
49142
+ if (exact)
49143
+ return exact;
49144
+ const storedAccepted = await transaction.getReceiptById(accepted.receipt_id);
49145
+ if (!storedAccepted || storedAccepted.outcome !== "accepted" || !storedAccepted.created_by_operation) {
49146
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND", "accepted receipt disappeared before conditional inverse");
49147
+ }
49148
+ const binding = await transaction.getBinding(authorityScope(this.capabilityValue), accepted.resource_kind, accepted.target_selector);
49149
+ if (!binding || binding.state !== "accepted" || binding.accepted_receipt_id !== accepted.receipt_id || binding.target_id !== accepted.target_id) {
49150
+ return this.terminalFor(transaction, request, callDigest, "target_not_owned_by_receipt", {
49151
+ targetId: accepted.target_id,
49152
+ acceptedReceiptId: accepted.receipt_id
49153
+ });
49154
+ }
49155
+ await transaction.lockCompensationWrites();
49156
+ const object = request.resource_kind === "project" ? await transaction.getProject(accepted.target_id) : await transaction.getTaskList(accepted.target_id);
49157
+ if (!object) {
49158
+ return this.terminalFor(transaction, request, callDigest, "target_missing_before_inverse", {
49159
+ targetId: accepted.target_id,
49160
+ acceptedReceiptId: accepted.receipt_id
49161
+ });
49162
+ }
49163
+ const current = request.resource_kind === "project" ? projectRecord(object) : taskListRecord(object);
49164
+ if (current.revision !== accepted.result_revision || current.digest !== accepted.result_digest) {
49165
+ return this.terminalFor(transaction, request, callDigest, "target_drifted", {
49166
+ targetId: accepted.target_id,
49167
+ acceptedReceiptId: accepted.receipt_id
49168
+ });
49169
+ }
49170
+ if (await transaction.hasDependents(request.resource_kind, accepted.target_id)) {
49171
+ return this.terminalFor(transaction, request, callDigest, "target_has_dependents", {
49172
+ targetId: accepted.target_id,
49173
+ acceptedReceiptId: accepted.receipt_id
49174
+ });
49175
+ }
49176
+ await this.fault("before_object_write", request);
49177
+ const deleted = request.resource_kind === "project" ? await transaction.deleteProject(accepted.target_id) : await transaction.deleteTaskList(accepted.target_id);
49178
+ if (!deleted) {
49179
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_CONFLICT", "conditional inverse could not delete the exact accepted target");
49180
+ }
49181
+ await this.fault("after_object_write", request);
49182
+ const inverseRecord = {
49183
+ target_id: accepted.target_id,
49184
+ revision: "absent",
49185
+ digest: digestProjectRegistrationValue({
49186
+ target_id: accepted.target_id,
49187
+ accepted_receipt_id: accepted.receipt_id,
49188
+ absent: true
49189
+ })
49190
+ };
49191
+ const inverse = makeAcceptedReceipt(request, callDigest, this.capabilityValue, inverseRecord, this.now());
49192
+ await this.fault("before_receipt_write", request);
49193
+ const stored = await insertDeterministicReceipt(transaction, inverse);
49194
+ await this.fault("after_receipt_write", request);
49195
+ await transaction.setBindingRemoved(authorityScope(this.capabilityValue), accepted.resource_kind, accepted.target_selector, stored.receipt_id, this.now());
49196
+ return stored;
49197
+ });
49198
+ await this.afterCommit(request);
49199
+ const receipt = publicReceipt(row);
49200
+ assertWithinBounds(receipt, request, startedAt);
49201
+ return receipt;
49202
+ } catch (error) {
49203
+ if (!(error instanceof WriteBoundaryError))
49204
+ throw error;
49205
+ const terminal = await this.backend.transaction(async (transaction) => {
49206
+ await transaction.lockStep({
49207
+ ...authorityScope(this.capabilityValue),
49208
+ operation_id: request.operation_id,
49209
+ step_id: request.step_id,
49210
+ resource_kind: request.resource_kind,
49211
+ direction: request.direction
49212
+ });
49213
+ return this.terminalFor(transaction, request, callDigest, `write_failed:${error.point}`, {
49214
+ targetId: accepted.target_id,
49215
+ acceptedReceiptId: accepted.receipt_id
49216
+ });
49217
+ });
49218
+ const receipt = publicReceipt(terminal);
49219
+ assertWithinBounds(receipt, request, startedAt);
49220
+ return receipt;
49221
+ }
49222
+ }
49223
+ async verifyInverse(request) {
49224
+ const startedAt = Date.now();
49225
+ const accepted = assertInverseRequest(request, this.capabilityValue);
49226
+ await this.storedAcceptedReceipt(request, accepted);
49227
+ const receipt = await this.backend.getReceiptForLookup({
49228
+ ...authorityScope(this.capabilityValue),
49229
+ operation_id: request.operation_id,
49230
+ step_id: request.step_id,
49231
+ resource_kind: request.resource_kind,
49232
+ direction: "inverse",
49233
+ idempotency_key: request.idempotency_key,
49234
+ target_selector: request.target_selector
49235
+ });
49236
+ if (!receipt || receipt.outcome !== "accepted" || receipt.accepted_receipt_id !== accepted.receipt_id || receipt.result_revision !== "absent" || !receipt.result_digest) {
49237
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND", "accepted conditional inverse receipt was not found");
49238
+ }
49239
+ const object = request.resource_kind === "project" ? await this.backend.getProject(accepted.target_id) : await this.backend.getTaskList(accepted.target_id);
49240
+ if (object) {
49241
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_CONFLICT", "inverse verification found the accepted target still present");
49242
+ }
49243
+ const verification = {
49244
+ target_id: accepted.target_id,
49245
+ accepted_receipt_id: accepted.receipt_id,
49246
+ absent: true,
49247
+ digest: digestProjectRegistrationValue({
49248
+ target_id: accepted.target_id,
49249
+ accepted_receipt_id: accepted.receipt_id,
49250
+ absent: true
49251
+ })
49252
+ };
49253
+ assertWithinBounds(verification, request, startedAt);
49254
+ if (verification.digest !== receipt.result_digest) {
49255
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_CONFLICT", "inverse verification digest does not match the immutable receipt");
49256
+ }
49257
+ return verification;
49258
+ }
49259
+ }
49260
+ function createPostgresTodosProjectRegistrationAuthority(client, options = {}) {
49261
+ const {
49262
+ service,
49263
+ tableName,
49264
+ cursorTableName,
49265
+ ...authorityOptions
49266
+ } = options;
49267
+ return new PackageOwnedTodosProjectRegistrationAuthority(new PostgresTodosProjectRegistrationBackend(client, {
49268
+ service,
49269
+ tableName,
49270
+ cursorTableName
49271
+ }), authorityOptions);
49272
+ }
49273
+ var UUID_PATTERN, WORKSPACE_ID_PATTERN, OPERATION_PATTERN, STEP_PATTERN, SHA256_PATTERN, IDEMPOTENCY_PATTERN, WriteBoundaryError;
49274
+ var init_authority = __esm(() => {
49275
+ init_package_version();
49276
+ init_postgres2();
49277
+ init_sqlite();
49278
+ init_types4();
49279
+ UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
49280
+ WORKSPACE_ID_PATTERN = /^wks_[A-Za-z0-9][A-Za-z0-9_-]{11,}$/;
49281
+ OPERATION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
49282
+ STEP_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{2,127}$/;
49283
+ SHA256_PATTERN = /^[0-9a-f]{64}$/;
49284
+ IDEMPOTENCY_PATTERN = /^prk_[0-9a-f]{48}$/;
49285
+ WriteBoundaryError = class WriteBoundaryError extends Error {
49286
+ point;
49287
+ cause;
49288
+ constructor(point, cause) {
49289
+ super(`Todos project registration failed at ${point}`);
49290
+ this.point = point;
49291
+ this.cause = cause;
49292
+ }
49293
+ };
49294
+ });
49295
+
49296
+ // src/project-registration/http.ts
49297
+ function json(body, status = 200) {
49298
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
49299
+ }
49300
+ function errorStatus(error) {
49301
+ switch (error.code) {
49302
+ case "TODOS_PROJECT_REGISTRATION_INVALID_INPUT":
49303
+ case "TODOS_PROJECT_REGISTRATION_INVALID_BOUNDS":
49304
+ case "TODOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH":
49305
+ case "TODOS_PROJECT_REGISTRATION_DIGEST_MISMATCH":
49306
+ case "TODOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH":
49307
+ case "TODOS_PROJECT_REGISTRATION_EXACT_ID_REQUIRED":
49308
+ return 400;
49309
+ case "TODOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND":
49310
+ case "TODOS_PROJECT_REGISTRATION_RECORD_NOT_FOUND":
49311
+ case "TODOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND":
49312
+ return 404;
49313
+ case "TODOS_PROJECT_REGISTRATION_RESPONSE_TOO_LARGE":
49314
+ return 413;
49315
+ case "TODOS_PROJECT_REGISTRATION_TIME_BUDGET_EXCEEDED":
49316
+ return 408;
49317
+ case "TODOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE":
49318
+ return 503;
49319
+ default:
49320
+ return 409;
49321
+ }
49322
+ }
49323
+ async function readJson(req) {
49324
+ try {
49325
+ const value = await req.json();
49326
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
49327
+ } catch {
49328
+ return null;
49329
+ }
49330
+ }
49331
+ async function handleTodosProjectRegistrationHttpRequest(req, url, authority, basePath = "/v1/project-registration") {
49332
+ const path = url.pathname;
49333
+ if (path !== basePath && !path.startsWith(`${basePath}/`))
49334
+ return null;
49335
+ const action = path.slice(basePath.length).split("/").filter(Boolean).join("/");
49336
+ const method = req.method.toUpperCase();
49337
+ try {
49338
+ if ((action === "" || action === "capability") && method === "GET") {
49339
+ return json({ capability: await authority.capability() });
49340
+ }
49341
+ if (method !== "POST")
49342
+ return json({ error: "method not allowed" }, 405);
49343
+ const body = await readJson(req);
49344
+ if (!body) {
49345
+ return json({
49346
+ error: "invalid JSON body",
49347
+ code: "TODOS_PROJECT_REGISTRATION_INVALID_INPUT"
49348
+ }, 400);
49349
+ }
49350
+ if (action === "create") {
49351
+ return json({
49352
+ receipt: await authority.create(body)
49353
+ }, 201);
49354
+ }
49355
+ if (action === "receipts/lookup") {
49356
+ return json(await authority.lookupReceipt(body));
49357
+ }
49358
+ if (action === "read-exact") {
49359
+ return json({
49360
+ record: await authority.readExact(body)
49361
+ });
49362
+ }
49363
+ if (action === "compensate") {
49364
+ return json({
49365
+ receipt: await authority.compensate(body)
49366
+ }, 201);
49367
+ }
49368
+ if (action === "verify-inverse") {
49369
+ return json({
49370
+ verification: await authority.verifyInverse(body)
49371
+ });
49372
+ }
49373
+ return json({
49374
+ error: "unknown Todos project-registration route",
49375
+ code: "TODOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND"
49376
+ }, 404);
49377
+ } catch (cause) {
49378
+ if (cause instanceof TodosProjectRegistrationError) {
49379
+ return json({
49380
+ error: cause.message,
49381
+ code: cause.code,
49382
+ details: cause.details,
49383
+ authoritative: true
49384
+ }, errorStatus(cause));
49385
+ }
49386
+ return json({
49387
+ error: cause instanceof Error ? cause.message : "internal registration error",
49388
+ code: "TODOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE"
49389
+ }, 500);
49390
+ }
49391
+ }
49392
+ var JSON_HEADERS;
49393
+ var init_http2 = __esm(() => {
49394
+ init_types4();
49395
+ JSON_HEADERS = { "Content-Type": "application/json" };
49396
+ });
49397
+
49398
+ // src/project-registration/index.ts
49399
+ var init_project_registration = __esm(() => {
49400
+ init_authority();
49401
+ init_http2();
49402
+ init_postgres2();
49403
+ init_sqlite();
49404
+ init_types4();
49405
+ });
49406
+
47726
49407
  // src/storage/comment-redaction-backfill.ts
47727
49408
  function assertSafeIdentifier2(value) {
47728
49409
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
@@ -47831,6 +49512,7 @@ __export(exports_cloud, {
47831
49512
  isCloudModeEnabled: () => isCloudModeEnabled,
47832
49513
  getCloudVerifier: () => getCloudVerifier,
47833
49514
  getCloudStorageAdapter: () => getCloudStorageAdapter,
49515
+ getCloudProjectRegistrationAuthority: () => getCloudProjectRegistrationAuthority,
47834
49516
  getCloudPrGroupLedger: () => getCloudPrGroupLedger,
47835
49517
  getApiKeyStore: () => getApiKeyStore,
47836
49518
  ensureCloudTaskShortIdIndex: () => ensureCloudTaskShortIdIndex,
@@ -47879,6 +49561,17 @@ function getCloudPrGroupLedger() {
47879
49561
  cachedPrGroupLedger = new PrGroupLedger(new PostgresPrGroupLedgerPersistence(getClient()));
47880
49562
  return cachedPrGroupLedger;
47881
49563
  }
49564
+ function getCloudProjectRegistrationAuthority() {
49565
+ if (cachedProjectRegistrationAuthority)
49566
+ return cachedProjectRegistrationAuthority;
49567
+ cachedProjectRegistrationAuthority = createPostgresTodosProjectRegistrationAuthority(getClient(), {
49568
+ service: TODOS_APP_SLUG,
49569
+ authorityId: TODOS_APP_SLUG,
49570
+ tenantId: process.env.HASNA_TODOS_TENANT_ID ?? "default",
49571
+ corpusId: process.env.HASNA_TODOS_CORPUS_ID ?? `${TODOS_APP_SLUG}:postgresql`
49572
+ });
49573
+ return cachedProjectRegistrationAuthority;
49574
+ }
47882
49575
  function authClient() {
47883
49576
  const client = getClient();
47884
49577
  return {
@@ -47927,6 +49620,9 @@ async function ensureCloudSchema() {
47927
49620
  for (const sql of postgresPrGroupSchemaSql()) {
47928
49621
  await client.query(sql);
47929
49622
  }
49623
+ for (const sql of postgresTodosProjectRegistrationSchemaSql()) {
49624
+ await client.query(sql);
49625
+ }
47930
49626
  await getApiKeyStore().ensureSchema();
47931
49627
  })();
47932
49628
  return schemaEnsured;
@@ -47967,14 +49663,16 @@ async function closeCloud() {
47967
49663
  cachedStore = null;
47968
49664
  cachedVerifier = null;
47969
49665
  cachedPrGroupLedger = null;
49666
+ cachedProjectRegistrationAuthority = null;
47970
49667
  schemaEnsured = null;
47971
49668
  }
47972
- var TODOS_APP_SLUG = "todos", cachedClient = null, cachedAdapter = null, cachedStore = null, cachedVerifier = null, cachedPrGroupLedger = null, schemaEnsured = null;
49669
+ var TODOS_APP_SLUG = "todos", cachedClient = null, cachedAdapter = null, cachedStore = null, cachedVerifier = null, cachedPrGroupLedger = null, cachedProjectRegistrationAuthority = null, schemaEnsured = null;
47973
49670
  var init_cloud = __esm(() => {
47974
49671
  init_cloud_client();
47975
49672
  init_postgres_adapter();
47976
49673
  init_ledger();
47977
49674
  init_postgres();
49675
+ init_project_registration();
47978
49676
  init_postgres_sync();
47979
49677
  init_comment_redaction_backfill();
47980
49678
  });
@@ -48333,9 +50031,9 @@ function parseBoundedLimit(value, fallback, max) {
48333
50031
  return fallback;
48334
50032
  return Math.min(parsed, max);
48335
50033
  }
48336
- function mapTaskError(e, json2) {
50034
+ function mapTaskError(e, json3) {
48337
50035
  if (e instanceof VersionConflictError) {
48338
- return json2({
50036
+ return json3({
48339
50037
  error: e.message,
48340
50038
  code: VersionConflictError.code,
48341
50039
  expected_version: e.expectedVersion,
@@ -48343,23 +50041,23 @@ function mapTaskError(e, json2) {
48343
50041
  }, 409);
48344
50042
  }
48345
50043
  if (e instanceof TaskNotFoundError) {
48346
- return json2({ error: e.message, code: TaskNotFoundError.code }, 404);
50044
+ return json3({ error: e.message, code: TaskNotFoundError.code }, 404);
48347
50045
  }
48348
50046
  if (e instanceof LockError) {
48349
- return json2({ error: e.message, code: LockError.code }, 409);
50047
+ return json3({ error: e.message, code: LockError.code }, 409);
48350
50048
  }
48351
50049
  if (e instanceof CompletionGuardError) {
48352
- return json2({
50050
+ return json3({
48353
50051
  error: e.message,
48354
50052
  code: CompletionGuardError.code,
48355
50053
  retry_after: e.retryAfterSeconds ?? null
48356
50054
  }, 409);
48357
50055
  }
48358
50056
  if (e instanceof TaskNotStartableError) {
48359
- return json2({ error: e.message, code: TaskNotStartableError.code }, 409);
50057
+ return json3({ error: e.message, code: TaskNotStartableError.code }, 409);
48360
50058
  }
48361
50059
  if (e instanceof Error && / is blocked by /.test(e.message)) {
48362
- return json2({ error: e.message, code: "TASK_NOT_STARTABLE" }, 409);
50060
+ return json3({ error: e.message, code: "TASK_NOT_STARTABLE" }, 409);
48363
50061
  }
48364
50062
  return null;
48365
50063
  }
@@ -48444,11 +50142,11 @@ data: ${JSON.stringify({ type: "connected", agent_id: agentId, timestamp: new Da
48444
50142
  }
48445
50143
  });
48446
50144
  }
48447
- function handleHealth(_ctx, json2) {
50145
+ function handleHealth(_ctx, json3) {
48448
50146
  const stats2 = getTaskStats();
48449
50147
  const staleCount = getStaleTasks(30).length;
48450
50148
  const overdueRecurring = getOverdueTasks().filter((t) => t.recurrence_rule && t.status === "pending").length;
48451
- return json2({
50149
+ return json3({
48452
50150
  status: staleCount === 0 && overdueRecurring === 0 ? "ok" : "warn",
48453
50151
  tasks: stats2.total,
48454
50152
  stale: staleCount,
@@ -48456,18 +50154,18 @@ function handleHealth(_ctx, json2) {
48456
50154
  timestamp: new Date().toISOString()
48457
50155
  });
48458
50156
  }
48459
- function handleHeadlessBoundary(_ctx, json2) {
50157
+ function handleHeadlessBoundary(_ctx, json3) {
48460
50158
  const { getHeadlessBoundaryManifest: getHeadlessBoundaryManifest2 } = (init_headless_boundaries(), __toCommonJS(exports_headless_boundaries));
48461
- return json2(getHeadlessBoundaryManifest2());
50159
+ return json3(getHeadlessBoundaryManifest2());
48462
50160
  }
48463
- function handleStats(_ctx, json2) {
50161
+ function handleStats(_ctx, json3) {
48464
50162
  const stats2 = getTaskStats();
48465
50163
  const byStatus = stats2.by_status;
48466
50164
  const projects = listProjects();
48467
50165
  const agents = listAgents();
48468
50166
  const staleCount = getStaleTasks(30).length;
48469
50167
  const overdueRecurring = getOverdueTasks().filter((t) => t.recurrence_rule && t.status === "pending").length;
48470
- return json2({
50168
+ return json3({
48471
50169
  total_tasks: stats2.total,
48472
50170
  pending: byStatus["pending"] ?? 0,
48473
50171
  in_progress: byStatus["in_progress"] ?? 0,
@@ -48490,10 +50188,10 @@ function taskStatusQueryParam(url) {
48490
50188
  return { ok: false, message: result.message };
48491
50189
  return { ok: true, value: collapseEnumValues(result.values) };
48492
50190
  }
48493
- async function handleListTasks(_req, url, _ctx, json2, taskToSummary2) {
50191
+ async function handleListTasks(_req, url, _ctx, json3, taskToSummary2) {
48494
50192
  const statusParam = taskStatusQueryParam(url);
48495
50193
  if (!statusParam.ok)
48496
- return json2({ error: statusParam.message }, 400);
50194
+ return json3({ error: statusParam.message }, 400);
48497
50195
  const projectId = url.searchParams.get("project_id") || undefined;
48498
50196
  const sessionId = url.searchParams.get("session_id") || undefined;
48499
50197
  const agentId = url.searchParams.get("agent_id") || undefined;
@@ -48508,13 +50206,13 @@ async function handleListTasks(_req, url, _ctx, json2, taskToSummary2) {
48508
50206
  limit: limitParam ? parseInt(limitParam, 10) : undefined,
48509
50207
  offset: offsetParam ? parseInt(offsetParam, 10) : undefined
48510
50208
  });
48511
- return json2(tasks.map((t) => taskToSummary2(t, fields)));
50209
+ return json3(tasks.map((t) => taskToSummary2(t, fields)));
48512
50210
  }
48513
- async function handleCreateTask(req, ctx, json2, taskToSummary2) {
50211
+ async function handleCreateTask(req, ctx, json3, taskToSummary2) {
48514
50212
  try {
48515
50213
  const body = await req.json();
48516
50214
  if (!body.title)
48517
- return json2({ error: "Missing 'title'" }, 400);
50215
+ return json3({ error: "Missing 'title'" }, 400);
48518
50216
  const createdBy = body.created_by ?? body.agent_id ?? "dashboard";
48519
50217
  const task2 = createTask({
48520
50218
  title: body.title,
@@ -48526,19 +50224,19 @@ async function handleCreateTask(req, ctx, json2, taskToSummary2) {
48526
50224
  ...body.assigned_to ? { assigned_to: body.assigned_to } : {}
48527
50225
  });
48528
50226
  ctx.broadcastEvent({ type: "task", task_id: task2.id, action: "created", agent_id: task2.agent_id, project_id: task2.project_id });
48529
- return json2(taskToSummary2(task2), 201);
50227
+ return json3(taskToSummary2(task2), 201);
48530
50228
  } catch (e) {
48531
- return json2({ error: e instanceof Error ? e.message : "Failed to create task" }, 500);
50229
+ return json3({ error: e instanceof Error ? e.message : "Failed to create task" }, 500);
48532
50230
  }
48533
50231
  }
48534
- async function handleUpsertTask(req, ctx, json2, taskToSummary2) {
50232
+ async function handleUpsertTask(req, ctx, json3, taskToSummary2) {
48535
50233
  try {
48536
50234
  const body = await req.json();
48537
50235
  if (typeof body["fingerprint"] !== "string" || body["fingerprint"].trim() === "") {
48538
- return json2({ error: "Missing 'fingerprint'" }, 400);
50236
+ return json3({ error: "Missing 'fingerprint'" }, 400);
48539
50237
  }
48540
50238
  if (typeof body["title"] !== "string" || body["title"].trim() === "") {
48541
- return json2({ error: "Missing 'title'" }, 400);
50239
+ return json3({ error: "Missing 'title'" }, 400);
48542
50240
  }
48543
50241
  const metadata = body["metadata"] && typeof body["metadata"] === "object" && !Array.isArray(body["metadata"]) ? { ...body["metadata"] } : {};
48544
50242
  for (const key of ["expectation_id", "expectation_fingerprint", "evidence_paths", "origin_loop_id", "origin_run_id", "expected", "observed", "acceptance"]) {
@@ -48559,9 +50257,9 @@ async function handleUpsertTask(req, ctx, json2, taskToSummary2) {
48559
50257
  metadata
48560
50258
  });
48561
50259
  ctx.broadcastEvent({ type: "task", task_id: result.task.id, action: result.created ? "created" : "updated", agent_id: result.task.agent_id, project_id: result.task.project_id });
48562
- return json2({ created: result.created, task: taskToSummary2(result.task) }, result.created ? 201 : 200);
50260
+ return json3({ created: result.created, task: taskToSummary2(result.task) }, result.created ? 201 : 200);
48563
50261
  } catch (e) {
48564
- return json2({ error: e instanceof Error ? e.message : "Failed to upsert task" }, 500);
50262
+ return json3({ error: e instanceof Error ? e.message : "Failed to upsert task" }, 500);
48565
50263
  }
48566
50264
  }
48567
50265
  function handleTasksExport(_req, url, _ctx, _json, taskToSummary2) {
@@ -48613,11 +50311,11 @@ function handleTasksExport(_req, url, _ctx, _json, taskToSummary2) {
48613
50311
  }
48614
50312
  });
48615
50313
  }
48616
- async function handleTasksBulk(req, _ctx, json2) {
50314
+ async function handleTasksBulk(req, _ctx, json3) {
48617
50315
  try {
48618
50316
  const body = await req.json();
48619
50317
  if (!body.ids?.length || !body.action)
48620
- return json2({ error: "Missing ids or action" }, 400);
50318
+ return json3({ error: "Missing ids or action" }, 400);
48621
50319
  const results = [];
48622
50320
  for (const id of body.ids) {
48623
50321
  try {
@@ -48635,66 +50333,66 @@ async function handleTasksBulk(req, _ctx, json2) {
48635
50333
  results.push({ id, success: false, error: e instanceof Error ? e.message : "Failed" });
48636
50334
  }
48637
50335
  }
48638
- return json2({ results, succeeded: results.filter((r) => r.success).length, failed: results.filter((r) => !r.success).length });
50336
+ return json3({ results, succeeded: results.filter((r) => r.success).length, failed: results.filter((r) => !r.success).length });
48639
50337
  } catch (e) {
48640
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50338
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
48641
50339
  }
48642
50340
  }
48643
- function handleTasksStatus(_req, url, _ctx, json2) {
50341
+ function handleTasksStatus(_req, url, _ctx, json3) {
48644
50342
  try {
48645
50343
  const projectId = url.searchParams.get("project_id") || undefined;
48646
50344
  const agentId = url.searchParams.get("agent_id") || undefined;
48647
50345
  const status = getStatus(projectId ? { project_id: projectId } : undefined, agentId);
48648
- return json2(status);
50346
+ return json3(status);
48649
50347
  } catch (e) {
48650
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50348
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
48651
50349
  }
48652
50350
  }
48653
- function handleTasksNext(_req, url, _ctx, json2, taskToSummary2) {
50351
+ function handleTasksNext(_req, url, _ctx, json3, taskToSummary2) {
48654
50352
  try {
48655
50353
  const projectId = url.searchParams.get("project_id") || undefined;
48656
50354
  const agentId = url.searchParams.get("agent_id") || undefined;
48657
50355
  const fields = parseFieldsParam(url);
48658
50356
  const task2 = getNextTask(agentId, projectId ? { project_id: projectId } : undefined);
48659
- return json2({ task: task2 ? taskToSummary2(task2, fields) : null });
50357
+ return json3({ task: task2 ? taskToSummary2(task2, fields) : null });
48660
50358
  } catch (e) {
48661
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50359
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
48662
50360
  }
48663
50361
  }
48664
- function handleTasksActive(_req, url, _ctx, json2) {
50362
+ function handleTasksActive(_req, url, _ctx, json3) {
48665
50363
  try {
48666
50364
  const projectId = url.searchParams.get("project_id") || undefined;
48667
50365
  const work = getActiveWork(projectId ? { project_id: projectId } : undefined);
48668
- return json2({ active: work, count: work.length });
50366
+ return json3({ active: work, count: work.length });
48669
50367
  } catch (e) {
48670
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50368
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
48671
50369
  }
48672
50370
  }
48673
- function handleTasksStale(_req, url, _ctx, json2, taskToSummary2) {
50371
+ function handleTasksStale(_req, url, _ctx, json3, taskToSummary2) {
48674
50372
  try {
48675
50373
  const projectId = url.searchParams.get("project_id") || undefined;
48676
50374
  const minutes2 = parseInt(url.searchParams.get("minutes") || "30", 10);
48677
50375
  const fields = parseFieldsParam(url);
48678
50376
  const tasks = getStaleTasks(minutes2, projectId ? { project_id: projectId } : undefined);
48679
- return json2({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length });
50377
+ return json3({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length });
48680
50378
  } catch (e) {
48681
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50379
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
48682
50380
  }
48683
50381
  }
48684
- function handleTasksChanged(_req, url, _ctx, json2, taskToSummary2) {
50382
+ function handleTasksChanged(_req, url, _ctx, json3, taskToSummary2) {
48685
50383
  try {
48686
50384
  const since = url.searchParams.get("since");
48687
50385
  if (!since)
48688
- return json2({ error: "since parameter required (ISO date string)" }, 400);
50386
+ return json3({ error: "since parameter required (ISO date string)" }, 400);
48689
50387
  const projectId = url.searchParams.get("project_id") || undefined;
48690
50388
  const fields = parseFieldsParam(url);
48691
50389
  const tasks = getTasksChangedSince(since, projectId ? { project_id: projectId } : undefined);
48692
- return json2({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length, since });
50390
+ return json3({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length, since });
48693
50391
  } catch (e) {
48694
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50392
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
48695
50393
  }
48696
50394
  }
48697
- function handleTasksContext(_req, url, _ctx, json2, taskToSummary2) {
50395
+ function handleTasksContext(_req, url, _ctx, json3, taskToSummary2) {
48698
50396
  const agentId = url.searchParams.get("agent_id") || undefined;
48699
50397
  const projectId = url.searchParams.get("project_id") || undefined;
48700
50398
  const format = url.searchParams.get("format") || "text";
@@ -48703,7 +50401,7 @@ function handleTasksContext(_req, url, _ctx, json2, taskToSummary2) {
48703
50401
  const status = getStatus(filters, agentId);
48704
50402
  const next = getNextTask(agentId, filters);
48705
50403
  if (format === "json") {
48706
- return json2({ status, next_task: next ? taskToSummary2(next, fields) : null });
50404
+ return json3({ status, next_task: next ? taskToSummary2(next, fields) : null });
48707
50405
  }
48708
50406
  const lines = [];
48709
50407
  lines.push(`Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done`);
@@ -48720,18 +50418,18 @@ function handleTasksContext(_req, url, _ctx, json2, taskToSummary2) {
48720
50418
  `);
48721
50419
  return new Response(text2, { headers: { "Content-Type": "text/plain" } });
48722
50420
  }
48723
- function handleTaskAttachments(id, _ctx, json2) {
50421
+ function handleTaskAttachments(id, _ctx, json3) {
48724
50422
  const task2 = getTask(id);
48725
50423
  if (!task2)
48726
- return json2({ error: "Task not found" }, 404);
50424
+ return json3({ error: "Task not found" }, 404);
48727
50425
  const evidence = task2.metadata?._evidence || {};
48728
50426
  const attachmentIds = evidence.attachments || [];
48729
- return json2({ task_id: id, short_id: task2.short_id, attachment_ids: attachmentIds, count: attachmentIds.length, files_changed: evidence.files_changed, commit_hash: evidence.commit_hash, notes: evidence.notes });
50427
+ return json3({ task_id: id, short_id: task2.short_id, attachment_ids: attachmentIds, count: attachmentIds.length, files_changed: evidence.files_changed, commit_hash: evidence.commit_hash, notes: evidence.notes });
48730
50428
  }
48731
- async function handleTaskProgress(id, req, method, _ctx, json2, url) {
50429
+ async function handleTaskProgress(id, req, method, _ctx, json3, url) {
48732
50430
  const task2 = getTask(id);
48733
50431
  if (!task2)
48734
- return json2({ error: "Task not found" }, 404);
50432
+ return json3({ error: "Task not found" }, 404);
48735
50433
  if (method === "GET") {
48736
50434
  const all = listComments(id);
48737
50435
  const progress = all.filter((c) => c.type === "progress");
@@ -48739,7 +50437,7 @@ async function handleTaskProgress(id, req, method, _ctx, json2, url) {
48739
50437
  const format = url?.searchParams.get("format") || "compact";
48740
50438
  const limit = parseBoundedLimit(url?.searchParams.get("limit") || null, 20, 200);
48741
50439
  const progressEntries = format === "full" ? progress : progress.slice(-limit);
48742
- return json2({
50440
+ return json3({
48743
50441
  task_id: id,
48744
50442
  progress_entries: progressEntries,
48745
50443
  latest,
@@ -48756,27 +50454,27 @@ async function handleTaskProgress(id, req, method, _ctx, json2, url) {
48756
50454
  try {
48757
50455
  const body = await req.json();
48758
50456
  if (!body.message)
48759
- return json2({ error: "message required" }, 400);
50457
+ return json3({ error: "message required" }, 400);
48760
50458
  const comment = logProgress(id, body.message, body.pct_complete, body.agent_id);
48761
- return json2(comment, 201);
50459
+ return json3(comment, 201);
48762
50460
  } catch (e) {
48763
- return json2({ error: e instanceof Error ? e.message : "Failed to log progress" }, 500);
50461
+ return json3({ error: e instanceof Error ? e.message : "Failed to log progress" }, 500);
48764
50462
  }
48765
50463
  }
48766
50464
  return null;
48767
50465
  }
48768
- function handleGetTask(id, _ctx, json2, taskToSummary2, url) {
50466
+ function handleGetTask(id, _ctx, json3, taskToSummary2, url) {
48769
50467
  const task2 = getTask(id);
48770
50468
  if (!task2)
48771
- return json2({ error: "Task not found" }, 404);
48772
- return json2(taskToSummary2(task2, url ? parseFieldsParam(url) : undefined));
50469
+ return json3({ error: "Task not found" }, 404);
50470
+ return json3(taskToSummary2(task2, url ? parseFieldsParam(url) : undefined));
48773
50471
  }
48774
- async function handlePatchTask(id, req, _ctx, json2, taskToSummary2) {
50472
+ async function handlePatchTask(id, req, _ctx, json3, taskToSummary2) {
48775
50473
  try {
48776
50474
  const body = await req.json();
48777
50475
  const task2 = getTask(id);
48778
50476
  if (!task2)
48779
- return json2({ error: "Task not found" }, 404);
50477
+ return json3({ error: "Task not found" }, 404);
48780
50478
  const ALLOWED = new Set(["title", "description", "status", "priority", "assigned_to", "plan_id", "task_list_id", "tags", "metadata", "due_at", "estimated_minutes", "actual_minutes", "confidence", "retry_count", "max_retries", "retry_after", "task_type"]);
48781
50479
  const safeBody = {};
48782
50480
  for (const [key, value] of Object.entries(body)) {
@@ -48788,85 +50486,85 @@ async function handlePatchTask(id, req, _ctx, json2, taskToSummary2) {
48788
50486
  ...safeBody,
48789
50487
  version: clientVersion
48790
50488
  });
48791
- return json2(taskToSummary2(updated));
50489
+ return json3(taskToSummary2(updated));
48792
50490
  } catch (e) {
48793
- const mapped = mapTaskError(e, json2);
50491
+ const mapped = mapTaskError(e, json3);
48794
50492
  if (mapped)
48795
50493
  return mapped;
48796
- return json2({ error: e instanceof Error ? e.message : "Failed to update task" }, 500);
50494
+ return json3({ error: e instanceof Error ? e.message : "Failed to update task" }, 500);
48797
50495
  }
48798
50496
  }
48799
- function handleDeleteTask(id, _ctx, json2) {
50497
+ function handleDeleteTask(id, _ctx, json3) {
48800
50498
  const deleted = deleteTask(id);
48801
50499
  if (!deleted)
48802
- return json2({ error: "Task not found" }, 404);
48803
- return json2({ success: true });
50500
+ return json3({ error: "Task not found" }, 404);
50501
+ return json3({ success: true });
48804
50502
  }
48805
- function handleStartTask(id, ctx, json2, taskToSummary2) {
50503
+ function handleStartTask(id, ctx, json3, taskToSummary2) {
48806
50504
  try {
48807
50505
  const task2 = startTask(id, "dashboard");
48808
50506
  ctx.broadcastEvent({ type: "task", task_id: task2.id, action: "started", agent_id: "dashboard", project_id: task2.project_id });
48809
- return json2(taskToSummary2(task2));
50507
+ return json3(taskToSummary2(task2));
48810
50508
  } catch (e) {
48811
- const mapped = mapTaskError(e, json2);
50509
+ const mapped = mapTaskError(e, json3);
48812
50510
  if (mapped)
48813
50511
  return mapped;
48814
- return json2({ error: e instanceof Error ? e.message : "Failed to start task" }, 500);
50512
+ return json3({ error: e instanceof Error ? e.message : "Failed to start task" }, 500);
48815
50513
  }
48816
50514
  }
48817
- async function handleFailTask(id, req, ctx, json2, taskToSummary2) {
50515
+ async function handleFailTask(id, req, ctx, json3, taskToSummary2) {
48818
50516
  try {
48819
50517
  const body = await req.json().catch(() => ({}));
48820
50518
  const result = failTask(id, body.agent_id, body.reason, { retry: body.retry, error_code: body.error_code });
48821
50519
  ctx.broadcastEvent({ type: "task", task_id: id, action: "failed", agent_id: body.agent_id || null, project_id: result.task.project_id });
48822
- return json2({ task: taskToSummary2(result.task), retry_task: result.retryTask ? taskToSummary2(result.retryTask) : null });
50520
+ return json3({ task: taskToSummary2(result.task), retry_task: result.retryTask ? taskToSummary2(result.retryTask) : null });
48823
50521
  } catch (e) {
48824
- return json2({ error: e instanceof Error ? e.message : "Failed to fail task" }, 500);
50522
+ return json3({ error: e instanceof Error ? e.message : "Failed to fail task" }, 500);
48825
50523
  }
48826
50524
  }
48827
- function handleCompleteTask(id, ctx, json2, taskToSummary2) {
50525
+ function handleCompleteTask(id, ctx, json3, taskToSummary2) {
48828
50526
  try {
48829
50527
  const task2 = completeTask(id, "dashboard");
48830
50528
  ctx.broadcastEvent({ type: "task", task_id: task2.id, action: "completed", agent_id: "dashboard", project_id: task2.project_id });
48831
- return json2(taskToSummary2(task2));
50529
+ return json3(taskToSummary2(task2));
48832
50530
  } catch (e) {
48833
- const mapped = mapTaskError(e, json2);
50531
+ const mapped = mapTaskError(e, json3);
48834
50532
  if (mapped)
48835
50533
  return mapped;
48836
- return json2({ error: e instanceof Error ? e.message : "Failed to complete task" }, 500);
50534
+ return json3({ error: e instanceof Error ? e.message : "Failed to complete task" }, 500);
48837
50535
  }
48838
50536
  }
48839
- function handleListProjects(url, _ctx, json2) {
50537
+ function handleListProjects(url, _ctx, json3) {
48840
50538
  const pFieldsParam = url.searchParams.get("fields");
48841
50539
  const pFields = pFieldsParam ? pFieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
48842
50540
  const projects = listProjects();
48843
- return json2(pFields ? projects.map((p) => Object.fromEntries(pFields.map((f) => [f, p[f] ?? null]))) : projects);
50541
+ return json3(pFields ? projects.map((p) => Object.fromEntries(pFields.map((f) => [f, p[f] ?? null]))) : projects);
48844
50542
  }
48845
- async function handleCreateProject(req, _ctx, json2) {
50543
+ async function handleCreateProject(req, _ctx, json3) {
48846
50544
  try {
48847
50545
  const body = await req.json();
48848
50546
  if (!body.name || !body.path)
48849
- return json2({ error: "Missing name or path" }, 400);
50547
+ return json3({ error: "Missing name or path" }, 400);
48850
50548
  const project = createProject({ name: body.name, path: body.path, description: body.description });
48851
- return json2(project, 201);
50549
+ return json3(project, 201);
48852
50550
  } catch (e) {
48853
- return json2({ error: e instanceof Error ? e.message : "Failed to create project" }, 500);
50551
+ return json3({ error: e instanceof Error ? e.message : "Failed to create project" }, 500);
48854
50552
  }
48855
50553
  }
48856
- function handleDeleteProject(id, _ctx, json2) {
50554
+ function handleDeleteProject(id, _ctx, json3) {
48857
50555
  const deleted = deleteProject(id);
48858
50556
  if (!deleted)
48859
- return json2({ error: "Project not found" }, 404);
48860
- return json2({ success: true });
50557
+ return json3({ error: "Project not found" }, 404);
50558
+ return json3({ success: true });
48861
50559
  }
48862
- async function handleAgentMe(_req, url, _ctx, json2, taskToSummary2) {
50560
+ async function handleAgentMe(_req, url, _ctx, json3, taskToSummary2) {
48863
50561
  try {
48864
50562
  const name = url.searchParams.get("name");
48865
50563
  if (!name)
48866
- return json2({ error: "Missing name param" }, 400);
50564
+ return json3({ error: "Missing name param" }, 400);
48867
50565
  const agentResult = registerAgent({ name });
48868
50566
  if (isAgentConflict(agentResult))
48869
- return json2({ error: agentResult.message, conflict: true }, 409);
50567
+ return json3({ error: agentResult.message, conflict: true }, 409);
48870
50568
  const agent = agentResult;
48871
50569
  const tasks = listTasks({ assigned_to: agent.name });
48872
50570
  const agentIdTasks = listTasks({ agent_id: agent.id });
@@ -48874,7 +50572,7 @@ async function handleAgentMe(_req, url, _ctx, json2, taskToSummary2) {
48874
50572
  const pending = allTasks.filter((t) => t.status === "pending");
48875
50573
  const inProgress = allTasks.filter((t) => t.status === "in_progress");
48876
50574
  const completed = allTasks.filter((t) => t.status === "completed");
48877
- return json2({
50575
+ return json3({
48878
50576
  agent,
48879
50577
  pending_tasks: pending.map((t) => taskToSummary2(t)),
48880
50578
  in_progress_tasks: inProgress.map((t) => taskToSummary2(t)),
@@ -48888,132 +50586,132 @@ async function handleAgentMe(_req, url, _ctx, json2, taskToSummary2) {
48888
50586
  });
48889
50587
  } catch (e) {
48890
50588
  if (e instanceof InvalidAgentNameError)
48891
- return json2({ error: e.message, suggestions: e.suggestions }, 400);
48892
- return json2({ error: e instanceof Error ? e.message : "Failed to get agent profile" }, 500);
50589
+ return json3({ error: e.message, suggestions: e.suggestions }, 400);
50590
+ return json3({ error: e instanceof Error ? e.message : "Failed to get agent profile" }, 500);
48893
50591
  }
48894
50592
  }
48895
- function handleAgentQueue(agentId, _ctx, json2, taskToSummary2) {
50593
+ function handleAgentQueue(agentId, _ctx, json3, taskToSummary2) {
48896
50594
  const aliasSet = assignedToAliasSet(getDatabase(), agentId);
48897
50595
  const pending = listTasks({ status: "pending" });
48898
50596
  const queue = pending.filter((t) => aliasSet.has((t.assigned_to ?? "").toLowerCase()) || t.agent_id === agentId || !t.assigned_to && !t.locked_by);
48899
50597
  const order = { critical: 0, high: 1, medium: 2, low: 3 };
48900
50598
  queue.sort((a, b) => (order[a.priority] ?? 4) - (order[b.priority] ?? 4) || new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
48901
- return json2(queue.map((t) => taskToSummary2(t)));
50599
+ return json3(queue.map((t) => taskToSummary2(t)));
48902
50600
  }
48903
- async function handleClaimTask(req, _ctx, json2, taskToSummary2) {
50601
+ async function handleClaimTask(req, _ctx, json3, taskToSummary2) {
48904
50602
  try {
48905
50603
  const body = await req.json();
48906
50604
  const agentId = body.agent_id || "anonymous";
48907
50605
  const task2 = claimNextTask(agentId, body.project_id ? { project_id: body.project_id } : undefined);
48908
- return json2({ task: task2 ? taskToSummary2(task2) : null });
50606
+ return json3({ task: task2 ? taskToSummary2(task2) : null });
48909
50607
  } catch (e) {
48910
- return json2({ error: e instanceof Error ? e.message : "Failed to claim" }, 500);
50608
+ return json3({ error: e instanceof Error ? e.message : "Failed to claim" }, 500);
48911
50609
  }
48912
50610
  }
48913
- function handleListOrgs(_ctx, json2) {
48914
- return json2(listOrgs());
50611
+ function handleListOrgs(_ctx, json3) {
50612
+ return json3(listOrgs());
48915
50613
  }
48916
- async function handleCreateOrg(req, _ctx, json2) {
50614
+ async function handleCreateOrg(req, _ctx, json3) {
48917
50615
  try {
48918
50616
  const body = await req.json();
48919
50617
  if (!body.name)
48920
- return json2({ error: "Missing name" }, 400);
48921
- return json2(createOrg(body), 201);
50618
+ return json3({ error: "Missing name" }, 400);
50619
+ return json3(createOrg(body), 201);
48922
50620
  } catch (e) {
48923
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50621
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
48924
50622
  }
48925
50623
  }
48926
- async function handleUpdateOrg(id, req, _ctx, json2) {
50624
+ async function handleUpdateOrg(id, req, _ctx, json3) {
48927
50625
  try {
48928
50626
  const body = await req.json();
48929
- return json2(updateOrg(id, body));
50627
+ return json3(updateOrg(id, body));
48930
50628
  } catch (e) {
48931
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50629
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
48932
50630
  }
48933
50631
  }
48934
- function handleDeleteOrg(id, _ctx, json2) {
50632
+ function handleDeleteOrg(id, _ctx, json3) {
48935
50633
  const deleted = deleteOrg(id);
48936
- return json2(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
50634
+ return json3(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
48937
50635
  }
48938
- function handleOrgChart(_ctx, json2) {
48939
- return json2(getOrgChart());
50636
+ function handleOrgChart(_ctx, json3) {
50637
+ return json3(getOrgChart());
48940
50638
  }
48941
- function handleAgentTeam(agentId, _ctx, json2) {
48942
- return json2(getDirectReports(decodeURIComponent(agentId)));
50639
+ function handleAgentTeam(agentId, _ctx, json3) {
50640
+ return json3(getDirectReports(decodeURIComponent(agentId)));
48943
50641
  }
48944
- function handleListAgents(url, _ctx, json2) {
50642
+ function handleListAgents(url, _ctx, json3) {
48945
50643
  const aFieldsParam = url.searchParams.get("fields");
48946
50644
  const aFields = aFieldsParam ? aFieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
48947
50645
  const agents = listAgents();
48948
- return json2(aFields ? agents.map((a) => Object.fromEntries(aFields.map((f) => [f, a[f] ?? null]))) : agents);
50646
+ return json3(aFields ? agents.map((a) => Object.fromEntries(aFields.map((f) => [f, a[f] ?? null]))) : agents);
48949
50647
  }
48950
- async function handleRegisterAgent(req, _ctx, json2) {
50648
+ async function handleRegisterAgent(req, _ctx, json3) {
48951
50649
  try {
48952
50650
  const body = await req.json();
48953
50651
  if (!body.name)
48954
- return json2({ error: "Missing name" }, 400);
50652
+ return json3({ error: "Missing name" }, 400);
48955
50653
  const result = registerAgent({ name: body.name, description: body.description, session_id: body.session_id, working_dir: body.working_dir });
48956
50654
  if (isAgentConflict(result))
48957
- return json2({ error: result.message, conflict: true }, 409);
48958
- return json2(result, 201);
50655
+ return json3({ error: result.message, conflict: true }, 409);
50656
+ return json3(result, 201);
48959
50657
  } catch (e) {
48960
50658
  if (e instanceof InvalidAgentNameError)
48961
- return json2({ error: e.message, suggestions: e.suggestions }, 400);
48962
- return json2({ error: e instanceof Error ? e.message : "Failed to register agent" }, 500);
50659
+ return json3({ error: e.message, suggestions: e.suggestions }, 400);
50660
+ return json3({ error: e instanceof Error ? e.message : "Failed to register agent" }, 500);
48963
50661
  }
48964
50662
  }
48965
- async function handleUpdateAgent(id, req, _ctx, json2) {
50663
+ async function handleUpdateAgent(id, req, _ctx, json3) {
48966
50664
  try {
48967
50665
  const body = await req.json();
48968
50666
  const agent = updateAgent(id, body);
48969
- return json2(agent);
50667
+ return json3(agent);
48970
50668
  } catch (e) {
48971
50669
  if (e instanceof InvalidAgentNameError)
48972
- return json2({ error: e.message, suggestions: e.suggestions }, 400);
48973
- return json2({ error: e instanceof Error ? e.message : "Failed to update agent" }, 500);
50670
+ return json3({ error: e.message, suggestions: e.suggestions }, 400);
50671
+ return json3({ error: e instanceof Error ? e.message : "Failed to update agent" }, 500);
48974
50672
  }
48975
50673
  }
48976
- function handleDeleteAgent(id, _ctx, json2) {
50674
+ function handleDeleteAgent(id, _ctx, json3) {
48977
50675
  const deleted = deleteAgent(id);
48978
50676
  if (!deleted)
48979
- return json2({ error: "Agent not found" }, 404);
48980
- return json2({ success: true });
50677
+ return json3({ error: "Agent not found" }, 404);
50678
+ return json3({ success: true });
48981
50679
  }
48982
- async function handleBulkDeleteAgents(req, _ctx, json2) {
50680
+ async function handleBulkDeleteAgents(req, _ctx, json3) {
48983
50681
  try {
48984
50682
  const body = await req.json();
48985
50683
  if (!body.ids?.length || body.action !== "delete")
48986
- return json2({ error: "Missing ids or invalid action" }, 400);
50684
+ return json3({ error: "Missing ids or invalid action" }, 400);
48987
50685
  let succeeded = 0;
48988
50686
  for (const id of body.ids) {
48989
50687
  if (deleteAgent(id))
48990
50688
  succeeded++;
48991
50689
  }
48992
- return json2({ succeeded, failed: body.ids.length - succeeded });
50690
+ return json3({ succeeded, failed: body.ids.length - succeeded });
48993
50691
  } catch (e) {
48994
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50692
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
48995
50693
  }
48996
50694
  }
48997
- async function handleBulkDeleteProjects(req, _ctx, json2) {
50695
+ async function handleBulkDeleteProjects(req, _ctx, json3) {
48998
50696
  try {
48999
50697
  const body = await req.json();
49000
50698
  if (!body.ids?.length || body.action !== "delete")
49001
- return json2({ error: "Missing ids or invalid action" }, 400);
50699
+ return json3({ error: "Missing ids or invalid action" }, 400);
49002
50700
  let succeeded = 0;
49003
50701
  for (const id of body.ids) {
49004
50702
  if (deleteProject(id))
49005
50703
  succeeded++;
49006
50704
  }
49007
- return json2({ succeeded, failed: body.ids.length - succeeded });
50705
+ return json3({ succeeded, failed: body.ids.length - succeeded });
49008
50706
  } catch (e) {
49009
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50707
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
49010
50708
  }
49011
50709
  }
49012
- function handleDoctor(_ctx, json2) {
50710
+ function handleDoctor(_ctx, json3) {
49013
50711
  const { runTodosDoctor: runTodosDoctor2 } = (init_doctor(), __toCommonJS(exports_doctor));
49014
- return json2(runTodosDoctor2({ apply: false }));
50712
+ return json3(runTodosDoctor2({ apply: false }));
49015
50713
  }
49016
- function handleReport(_req, url, _ctx, json2) {
50714
+ function handleReport(_req, url, _ctx, json3) {
49017
50715
  const days = parseInt(url.searchParams.get("days") || "7", 10);
49018
50716
  const projectId = url.searchParams.get("project_id") || undefined;
49019
50717
  const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
@@ -49029,62 +50727,62 @@ function handleReport(_req, url, _ctx, json2) {
49029
50727
  byDay[day] = (byDay[day] || 0) + 1;
49030
50728
  }
49031
50729
  const completionRate = changed.length > 0 ? Math.round(completed.length / changed.length * 100) : 0;
49032
- return json2({ days, period_since: since, total: all.length, stats: stats2, changed: changed.length, completed: completed.length, failed: failed.length, completion_rate: completionRate, by_day: byDay });
50730
+ return json3({ days, period_since: since, total: all.length, stats: stats2, changed: changed.length, completed: completed.length, failed: failed.length, completion_rate: completionRate, by_day: byDay });
49033
50731
  }
49034
- function handleActivity(_req, url, _ctx, json2) {
50732
+ function handleActivity(_req, url, _ctx, json3) {
49035
50733
  const limit = parseInt(url.searchParams.get("limit") || "50", 10);
49036
- return json2(getRecentActivity(limit));
50734
+ return json3(getRecentActivity(limit));
49037
50735
  }
49038
- function handleTaskHistory(id, _ctx, json2, url) {
50736
+ function handleTaskHistory(id, _ctx, json3, url) {
49039
50737
  const history = getTaskHistory(id);
49040
50738
  const format = url?.searchParams.get("format") || "compact";
49041
50739
  const limit = parseBoundedLimit(url?.searchParams.get("limit") || null, 20, 500);
49042
- return json2(format === "full" ? history : history.slice(0, limit));
50740
+ return json3(format === "full" ? history : history.slice(0, limit));
49043
50741
  }
49044
- function handleListWebhooks(_ctx, json2) {
49045
- return json2(listWebhooks());
50742
+ function handleListWebhooks(_ctx, json3) {
50743
+ return json3(listWebhooks());
49046
50744
  }
49047
- async function handleCreateWebhook(req, _ctx, json2) {
50745
+ async function handleCreateWebhook(req, _ctx, json3) {
49048
50746
  try {
49049
50747
  const body = await req.json();
49050
50748
  if (!body.url)
49051
- return json2({ error: "Missing url" }, 400);
49052
- return json2(createWebhook(body), 201);
50749
+ return json3({ error: "Missing url" }, 400);
50750
+ return json3(createWebhook(body), 201);
49053
50751
  } catch (e) {
49054
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50752
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
49055
50753
  }
49056
50754
  }
49057
- function handleDeleteWebhook(id, _ctx, json2) {
50755
+ function handleDeleteWebhook(id, _ctx, json3) {
49058
50756
  const deleted = deleteWebhook(id);
49059
- return json2(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
50757
+ return json3(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
49060
50758
  }
49061
- function handleListTemplates(_ctx, json2) {
49062
- return json2(listTemplates());
50759
+ function handleListTemplates(_ctx, json3) {
50760
+ return json3(listTemplates());
49063
50761
  }
49064
- async function handleCreateTemplate(req, _ctx, json2) {
50762
+ async function handleCreateTemplate(req, _ctx, json3) {
49065
50763
  try {
49066
50764
  const body = await req.json();
49067
50765
  if (!body.name || !body.title_pattern)
49068
- return json2({ error: "Missing name or title_pattern" }, 400);
49069
- return json2(createTemplate(body), 201);
50766
+ return json3({ error: "Missing name or title_pattern" }, 400);
50767
+ return json3(createTemplate(body), 201);
49070
50768
  } catch (e) {
49071
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50769
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
49072
50770
  }
49073
50771
  }
49074
- function handleDeleteTemplate(id, _ctx, json2) {
50772
+ function handleDeleteTemplate(id, _ctx, json3) {
49075
50773
  const deleted = deleteTemplate(id);
49076
- return json2(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
50774
+ return json3(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
49077
50775
  }
49078
- function handleListPlans(url, _ctx, json2) {
50776
+ function handleListPlans(url, _ctx, json3) {
49079
50777
  const projectId = url.searchParams.get("project_id") || undefined;
49080
50778
  const plans = listPlans(projectId);
49081
- return json2(plans);
50779
+ return json3(plans);
49082
50780
  }
49083
- async function handleCreatePlan(req, _ctx, json2) {
50781
+ async function handleCreatePlan(req, _ctx, json3) {
49084
50782
  try {
49085
50783
  const body = await req.json();
49086
50784
  if (!body.name)
49087
- return json2({ error: "Missing 'name'" }, 400);
50785
+ return json3({ error: "Missing 'name'" }, 400);
49088
50786
  const plan = createPlan({
49089
50787
  name: body.name,
49090
50788
  slug: body.slug,
@@ -49094,49 +50792,49 @@ async function handleCreatePlan(req, _ctx, json2) {
49094
50792
  agent_id: body.agent_id,
49095
50793
  status: body.status
49096
50794
  });
49097
- return json2(plan, 201);
50795
+ return json3(plan, 201);
49098
50796
  } catch (e) {
49099
- return json2({ error: e instanceof Error ? e.message : "Failed to create plan" }, 500);
50797
+ return json3({ error: e instanceof Error ? e.message : "Failed to create plan" }, 500);
49100
50798
  }
49101
50799
  }
49102
- async function handleBulkDeletePlans(req, _ctx, json2) {
50800
+ async function handleBulkDeletePlans(req, _ctx, json3) {
49103
50801
  try {
49104
50802
  const body = await req.json();
49105
50803
  if (!body.ids?.length || body.action !== "delete")
49106
- return json2({ error: "Missing ids or invalid action" }, 400);
50804
+ return json3({ error: "Missing ids or invalid action" }, 400);
49107
50805
  let succeeded = 0;
49108
50806
  for (const id of body.ids) {
49109
50807
  if (deletePlan(id))
49110
50808
  succeeded++;
49111
50809
  }
49112
- return json2({ succeeded, failed: body.ids.length - succeeded });
50810
+ return json3({ succeeded, failed: body.ids.length - succeeded });
49113
50811
  } catch (e) {
49114
- return json2({ error: e instanceof Error ? e.message : "Failed" }, 500);
50812
+ return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
49115
50813
  }
49116
50814
  }
49117
- function handleGetPlan(id, _ctx, json2, taskToSummary2) {
50815
+ function handleGetPlan(id, _ctx, json3, taskToSummary2) {
49118
50816
  const plan = getPlan(id);
49119
50817
  if (!plan)
49120
- return json2({ error: "Plan not found" }, 404);
50818
+ return json3({ error: "Plan not found" }, 404);
49121
50819
  const tasks = listTasks({ plan_id: id });
49122
- return json2({ ...plan, tasks: tasks.map((t) => taskToSummary2(t)) });
50820
+ return json3({ ...plan, tasks: tasks.map((t) => taskToSummary2(t)) });
49123
50821
  }
49124
- async function handleUpdatePlan(id, req, _ctx, json2) {
50822
+ async function handleUpdatePlan(id, req, _ctx, json3) {
49125
50823
  try {
49126
50824
  const body = await req.json();
49127
50825
  const plan = updatePlan(id, body);
49128
- return json2(plan);
50826
+ return json3(plan);
49129
50827
  } catch (e) {
49130
- return json2({ error: e instanceof Error ? e.message : "Failed to update plan" }, 500);
50828
+ return json3({ error: e instanceof Error ? e.message : "Failed to update plan" }, 500);
49131
50829
  }
49132
50830
  }
49133
- function handleDeletePlan(id, _ctx, json2) {
50831
+ function handleDeletePlan(id, _ctx, json3) {
49134
50832
  const deleted = deletePlan(id);
49135
50833
  if (!deleted)
49136
- return json2({ error: "Plan not found" }, 404);
49137
- return json2({ success: true });
50834
+ return json3({ error: "Plan not found" }, 404);
50835
+ return json3({ success: true });
49138
50836
  }
49139
- function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
50837
+ function handleStaticFiles(path, method, ctx, json3, serveStaticFile2) {
49140
50838
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
49141
50839
  return null;
49142
50840
  if (path !== "/") {
@@ -49144,7 +50842,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
49144
50842
  const resolvedFile = resolve16(filePath);
49145
50843
  const resolvedBase = resolve16(ctx.dashboardDir);
49146
50844
  if (!resolvedFile.startsWith(resolvedBase + sep3) && resolvedFile !== resolvedBase) {
49147
- return json2({ error: "Forbidden" }, 403);
50845
+ return json3({ error: "Forbidden" }, 403);
49148
50846
  }
49149
50847
  const res2 = serveStaticFile2(filePath);
49150
50848
  if (res2)
@@ -50805,10 +52503,10 @@ var exports_pr_groups = {};
50805
52503
  __export(exports_pr_groups, {
50806
52504
  handlePrGroupHttpRequest: () => handlePrGroupHttpRequest
50807
52505
  });
50808
- function json2(body, status = 200) {
50809
- return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
52506
+ function json3(body, status = 200) {
52507
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS2 });
50810
52508
  }
50811
- function errorStatus(error) {
52509
+ function errorStatus2(error) {
50812
52510
  switch (error.code) {
50813
52511
  case "PR_GROUP_INVALID_INPUT":
50814
52512
  case "PR_GROUP_EXACT_HEAD_REQUIRED":
@@ -50822,7 +52520,7 @@ function errorStatus(error) {
50822
52520
  return 409;
50823
52521
  }
50824
52522
  }
50825
- async function readJson(req) {
52523
+ async function readJson2(req) {
50826
52524
  try {
50827
52525
  const value = await req.json();
50828
52526
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -50840,26 +52538,26 @@ async function handlePrGroupHttpRequest(req, url, ledger, basePath, principal) {
50840
52538
  const method = req.method.toUpperCase();
50841
52539
  try {
50842
52540
  if (!groupId && method === "POST" && action === undefined) {
50843
- return json2({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
52541
+ return json3({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
50844
52542
  }
50845
52543
  if (groupId === "admit" && !action) {
50846
52544
  if (method !== "POST")
50847
- return json2({ error: "method not allowed" }, 405);
50848
- const body = await readJson(req);
52545
+ return json3({ error: "method not allowed" }, 405);
52546
+ const body = await readJson2(req);
50849
52547
  if (!body)
50850
- return json2({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
50851
- return json2(await ledger.admit(body), 201);
52548
+ return json3({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
52549
+ return json3(await ledger.admit(body), 201);
50852
52550
  }
50853
52551
  if (!groupId)
50854
- return json2({ error: "PR group id is required", code: "PR_GROUP_INVALID_INPUT" }, 400);
52552
+ return json3({ error: "PR group id is required", code: "PR_GROUP_INVALID_INPUT" }, 400);
50855
52553
  if (!action && method === "GET") {
50856
- return json2({ view: await ledger.get(groupId) });
52554
+ return json3({ view: await ledger.get(groupId) });
50857
52555
  }
50858
52556
  if (action === "events") {
50859
52557
  if (method === "GET") {
50860
52558
  const limit = url.searchParams.has("limit") ? Number(url.searchParams.get("limit")) : undefined;
50861
52559
  const afterSequence = url.searchParams.has("after_sequence") ? Number(url.searchParams.get("after_sequence")) : undefined;
50862
- return json2({
52560
+ return json3({
50863
52561
  history: await ledger.events(groupId, {
50864
52562
  ...limit !== undefined ? { limit } : {},
50865
52563
  ...afterSequence !== undefined ? { after_sequence: afterSequence } : {}
@@ -50867,51 +52565,74 @@ async function handlePrGroupHttpRequest(req, url, ledger, basePath, principal) {
50867
52565
  });
50868
52566
  }
50869
52567
  if (method === "POST") {
50870
- const body = await readJson(req);
52568
+ const body = await readJson2(req);
50871
52569
  if (!body)
50872
- return json2({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
50873
- return json2(await ledger.append({
52570
+ return json3({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
52571
+ return json3(await ledger.append({
50874
52572
  ...body,
50875
52573
  group_id: groupId,
50876
52574
  authenticated_actor_id: principal?.actor_id ?? undefined,
50877
52575
  authenticated_actor_run_id: principal?.actor_run_id ?? undefined
50878
52576
  }), 201);
50879
52577
  }
50880
- return json2({ error: "method not allowed" }, 405);
52578
+ return json3({ error: "method not allowed" }, 405);
50881
52579
  }
50882
52580
  if (action === "recover") {
50883
52581
  if (method !== "POST")
50884
- return json2({ error: "method not allowed" }, 405);
50885
- const body = await readJson(req);
52582
+ return json3({ error: "method not allowed" }, 405);
52583
+ const body = await readJson2(req);
50886
52584
  if (!body)
50887
- return json2({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
50888
- return json2(await ledger.recover({
52585
+ return json3({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
52586
+ return json3(await ledger.recover({
50889
52587
  ...body,
50890
52588
  group_id: groupId
50891
52589
  }), 201);
50892
52590
  }
50893
- return json2({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
52591
+ return json3({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
50894
52592
  } catch (cause) {
50895
52593
  if (cause instanceof PrGroupLedgerError) {
50896
- return json2({
52594
+ return json3({
50897
52595
  error: cause.message,
50898
52596
  code: cause.code,
50899
52597
  details: cause.details,
50900
52598
  authoritative: true
50901
- }, errorStatus(cause));
52599
+ }, errorStatus2(cause));
50902
52600
  }
50903
- return json2({
52601
+ return json3({
50904
52602
  error: cause instanceof Error ? cause.message : "internal PR-group error",
50905
52603
  code: "PR_GROUP_ATOMICITY_UNAVAILABLE"
50906
52604
  }, 500);
50907
52605
  }
50908
52606
  }
50909
- var JSON_HEADERS;
52607
+ var JSON_HEADERS2;
50910
52608
  var init_pr_groups = __esm(() => {
50911
52609
  init_types3();
50912
- JSON_HEADERS = { "Content-Type": "application/json" };
52610
+ JSON_HEADERS2 = { "Content-Type": "application/json" };
50913
52611
  });
50914
52612
 
52613
+ // src/lib/comment-cursor.ts
52614
+ function encodeCommentCursor(comment) {
52615
+ return Buffer.from(JSON.stringify({ created_at: comment.created_at, id: comment.id }), "utf8").toString("base64url");
52616
+ }
52617
+ function decodeCommentCursor(value) {
52618
+ if (value.length > MAX_COMMENT_CURSOR_LENGTH)
52619
+ throw new Error("invalid comment cursor");
52620
+ let parsed;
52621
+ try {
52622
+ parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
52623
+ } catch {
52624
+ throw new Error("invalid comment cursor");
52625
+ }
52626
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
52627
+ throw new Error("invalid comment cursor");
52628
+ const cursor = parsed;
52629
+ if (typeof cursor["created_at"] !== "string" || cursor["created_at"].length > 64 || !Number.isFinite(Date.parse(cursor["created_at"])) || typeof cursor["id"] !== "string" || !cursor["id"] || cursor["id"].length > 256) {
52630
+ throw new Error("invalid comment cursor");
52631
+ }
52632
+ return { created_at: cursor["created_at"], id: cursor["id"] };
52633
+ }
52634
+ var MAX_COMMENT_CURSOR_LENGTH = 1024;
52635
+
50915
52636
  // src/server/v1.ts
50916
52637
  var exports_v1 = {};
50917
52638
  __export(exports_v1, {
@@ -50919,11 +52640,11 @@ __export(exports_v1, {
50919
52640
  handleV1Request: () => handleV1Request,
50920
52641
  countSnapshotRecords: () => countSnapshotRecords
50921
52642
  });
50922
- function json3(body, status = 200) {
50923
- return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS2 });
52643
+ function json4(body, status = 200) {
52644
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS3 });
50924
52645
  }
50925
52646
  function error(status, message, extra) {
50926
- return json3({ error: message, ...extra ?? {} }, status);
52647
+ return json4({ error: message, ...extra ?? {} }, status);
50927
52648
  }
50928
52649
  function enumQueryParam(url, name, vocabulary) {
50929
52650
  const raw = url.searchParams.get(name);
@@ -51174,7 +52895,7 @@ function validateTemplatePatch(value) {
51174
52895
  ...body.plan_id === null ? { plan_id: null } : {}
51175
52896
  } };
51176
52897
  }
51177
- async function readJson2(req) {
52898
+ async function readJson3(req) {
51178
52899
  try {
51179
52900
  const text2 = await req.text();
51180
52901
  if (!text2)
@@ -51201,26 +52922,6 @@ function contextFromPrincipal(principal, body) {
51201
52922
  function redactComment3(comment) {
51202
52923
  return { ...comment, content: redactEvidenceText(comment.content) };
51203
52924
  }
51204
- function encodeCommentCursor(comment) {
51205
- return Buffer.from(JSON.stringify({ created_at: comment.created_at, id: comment.id }), "utf8").toString("base64url");
51206
- }
51207
- function decodeCommentCursor(value) {
51208
- if (value.length > 1024)
51209
- throw new Error("invalid comment cursor");
51210
- let parsed;
51211
- try {
51212
- parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
51213
- } catch {
51214
- throw new Error("invalid comment cursor");
51215
- }
51216
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
51217
- throw new Error("invalid comment cursor");
51218
- const cursor = parsed;
51219
- if (typeof cursor["created_at"] !== "string" || cursor["created_at"].length > 64 || !Number.isFinite(Date.parse(cursor["created_at"])) || typeof cursor["id"] !== "string" || !cursor["id"] || cursor["id"].length > 256) {
51220
- throw new Error("invalid comment cursor");
51221
- }
51222
- return { created_at: cursor["created_at"], id: cursor["id"] };
51223
- }
51224
52925
  function normalizeImportSnapshot(raw) {
51225
52926
  const body = raw && typeof raw === "object" ? raw : {};
51226
52927
  const arr = (v) => Array.isArray(v) ? v : [];
@@ -51264,6 +52965,9 @@ async function handleV1Request(req, url, dependencies = {}) {
51264
52965
  if (path === "/v1/pr-groups" || path.startsWith("/v1/pr-groups/")) {
51265
52966
  return handlePrGroupHttpRequest(req, url, (dependencies.getPrGroupLedger ?? getCloudPrGroupLedger)(), "/v1/pr-groups", { actor_id: principal.agent, actor_run_id: principal.kid });
51266
52967
  }
52968
+ if (path === "/v1/project-registration" || path.startsWith("/v1/project-registration/")) {
52969
+ return handleTodosProjectRegistrationHttpRequest(req, url, (dependencies.getProjectRegistrationAuthority ?? getCloudProjectRegistrationAuthority)());
52970
+ }
51267
52971
  const store = (dependencies.getStorageAdapter ?? getCloudStorageAdapter)();
51268
52972
  const segments = path.split("/").filter(Boolean);
51269
52973
  const resource = segments[1];
@@ -51275,7 +52979,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51275
52979
  if (id === "exists" && !action) {
51276
52980
  if (method !== "POST")
51277
52981
  return error(405, `method ${method} not allowed on /v1/tasks/exists`);
51278
- const body = await readJson2(req);
52982
+ const body = await readJson3(req);
51279
52983
  const ids2 = Array.isArray(body?.ids) ? Array.from(new Set(body.ids.filter((v) => typeof v === "string" && v.length > 0))) : [];
51280
52984
  if (ids2.length === 0)
51281
52985
  return error(400, "provide a non-empty string array `ids`");
@@ -51285,7 +52989,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51285
52989
  const presentSet = new Set(found.map((t) => t.id));
51286
52990
  const present = ids2.filter((i) => presentSet.has(i));
51287
52991
  const missing = ids2.filter((i) => !presentSet.has(i));
51288
- return json3({
52992
+ return json4({
51289
52993
  requested: ids2.length,
51290
52994
  present_count: present.length,
51291
52995
  missing_count: missing.length,
@@ -51298,7 +53002,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51298
53002
  if (typeof store.tasks.getByFingerprint !== "function") {
51299
53003
  return error(501, "fingerprint upsert is not supported by this storage backend");
51300
53004
  }
51301
- const body = await readJson2(req) ?? {};
53005
+ const body = await readJson3(req) ?? {};
51302
53006
  const fingerprint3 = typeof body.fingerprint === "string" ? body.fingerprint.trim() : "";
51303
53007
  if (!fingerprint3)
51304
53008
  return error(400, "fingerprint is required");
@@ -51335,11 +53039,11 @@ async function handleV1Request(req, url, dependencies = {}) {
51335
53039
  }
51336
53040
  if (!existing) {
51337
53041
  const task2 = await store.tasks.create({ ...fields, title: body.title }, contextFromPrincipal(principal, body));
51338
- return json3({ task: task2, created: true }, 201);
53042
+ return json4({ task: task2, created: true }, 201);
51339
53043
  }
51340
53044
  try {
51341
53045
  const task2 = await store.tasks.update(existing.id, { ...fields, version: existing.version }, contextFromPrincipal(principal, body));
51342
- return json3({ task: task2, created: false });
53046
+ return json4({ task: task2, created: false });
51343
53047
  } catch (e) {
51344
53048
  const msg = e.message || "";
51345
53049
  if (msg.includes("version conflict"))
@@ -51381,15 +53085,15 @@ async function handleV1Request(req, url, dependencies = {}) {
51381
53085
  const tasks = await store.tasks.list(filter);
51382
53086
  const { limit: _l, offset: _o, ...countFilter } = filter;
51383
53087
  const total = await store.tasks.count(countFilter);
51384
- return json3({ tasks, count: tasks.length, total });
53088
+ return json4({ tasks, count: tasks.length, total });
51385
53089
  }
51386
53090
  if (method === "POST") {
51387
- const body = await readJson2(req);
53091
+ const body = await readJson3(req);
51388
53092
  if (!body || typeof body.title !== "string" || !body.title.trim()) {
51389
53093
  return error(400, "title is required");
51390
53094
  }
51391
53095
  const task2 = await store.tasks.create(body, contextFromPrincipal(principal, body));
51392
- return json3({ task: task2 }, 201);
53096
+ return json4({ task: task2 }, 201);
51393
53097
  }
51394
53098
  return error(405, `method ${method} not allowed on /v1/tasks`);
51395
53099
  }
@@ -51406,7 +53110,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51406
53110
  if (legacyPage.length > LEGACY_COMMENT_RESPONSE_LIMIT) {
51407
53111
  return error(426, "task has too many comments for this client; upgrade @hasna/todos to use cursor pagination");
51408
53112
  }
51409
- return json3({
53113
+ return json4({
51410
53114
  comments: legacyPage,
51411
53115
  count: legacyPage.length,
51412
53116
  has_more: false,
@@ -51431,7 +53135,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51431
53135
  const page = (await store.audit.getCommentsPage(id, { limit: limit + 1, ...before ? { before } : {} }, contextFromPrincipal(principal))).map(redactComment3);
51432
53136
  const hasMore = page.length > limit;
51433
53137
  const comments = hasMore ? page.slice(1) : page;
51434
- return json3({
53138
+ return json4({
51435
53139
  comments,
51436
53140
  count: comments.length,
51437
53141
  has_more: hasMore,
@@ -51439,7 +53143,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51439
53143
  });
51440
53144
  }
51441
53145
  if (method === "POST") {
51442
- const body2 = await readJson2(req) ?? {};
53146
+ const body2 = await readJson3(req) ?? {};
51443
53147
  if (typeof body2.content !== "string" || !body2.content.trim()) {
51444
53148
  return error(400, "content is required");
51445
53149
  }
@@ -51454,7 +53158,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51454
53158
  type: body2.type,
51455
53159
  progress_pct: body2.progress_pct
51456
53160
  }, contextFromPrincipal(principal, body2));
51457
- return json3({ comment: redactComment3(comment) }, 201);
53161
+ return json4({ comment: redactComment3(comment) }, 201);
51458
53162
  }
51459
53163
  return error(405, `method ${method} not allowed on /v1/tasks/:id/comments`);
51460
53164
  }
@@ -51464,19 +53168,19 @@ async function handleV1Request(req, url, dependencies = {}) {
51464
53168
  if (!await store.tasks.get(id))
51465
53169
  return error(404, "task not found");
51466
53170
  const history = await store.audit.getTaskHistory(id);
51467
- return json3({ history, count: history.length });
53171
+ return json4({ history, count: history.length });
51468
53172
  }
51469
53173
  if (action === "lock" || action === "unlock") {
51470
53174
  if (method !== "POST")
51471
53175
  return error(405, `method ${method} not allowed on /v1/tasks/:id/${action}`);
51472
- const body2 = await readJson2(req) ?? {};
53176
+ const body2 = await readJson3(req) ?? {};
51473
53177
  if (!await store.tasks.get(id))
51474
53178
  return error(404, "task not found");
51475
53179
  if (action === "lock") {
51476
53180
  if (typeof store.tasks.lock !== "function")
51477
53181
  return error(501, "task locking is not supported by this storage backend");
51478
53182
  const agentId3 = body2.agent_id || principal.agent || "todos-serve";
51479
- return json3({ result: await store.tasks.lock(id, agentId3) });
53183
+ return json4({ result: await store.tasks.lock(id, agentId3) });
51480
53184
  }
51481
53185
  if (typeof store.tasks.unlock !== "function")
51482
53186
  return error(501, "task unlocking is not supported by this storage backend");
@@ -51484,7 +53188,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51484
53188
  if (!principal.scopes.includes("todos:*"))
51485
53189
  return error(403, "force unlock requires todos:* scope");
51486
53190
  const released2 = await store.tasks.unlock(id);
51487
- return json3({ success: released2 });
53191
+ return json4({ success: released2 });
51488
53192
  }
51489
53193
  if (body2.agent_id && body2.agent_id !== principal.agent && !principal.scopes.includes("todos:*")) {
51490
53194
  return error(403, "unlock agent_id must match the authenticated agent");
@@ -51493,7 +53197,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51493
53197
  if (!agentId2)
51494
53198
  return error(403, "unlock requires an agent-bound key or force=true");
51495
53199
  const released = await store.tasks.unlock(id, agentId2);
51496
- return json3({ success: released });
53200
+ return json4({ success: released });
51497
53201
  }
51498
53202
  if (action === "dependencies") {
51499
53203
  if (!store.dependencies)
@@ -51502,16 +53206,16 @@ async function handleV1Request(req, url, dependencies = {}) {
51502
53206
  if (!await store.tasks.get(id))
51503
53207
  return error(404, "task not found");
51504
53208
  const edges = await store.dependencies.list(id);
51505
- return json3(edges);
53209
+ return json4(edges);
51506
53210
  }
51507
53211
  if (method === "POST") {
51508
- const body2 = await readJson2(req) ?? {};
53212
+ const body2 = await readJson3(req) ?? {};
51509
53213
  if (typeof body2.depends_on !== "string" || !body2.depends_on.trim()) {
51510
53214
  return error(400, "depends_on is required");
51511
53215
  }
51512
53216
  try {
51513
53217
  const dependency = await store.dependencies.add(id, body2.depends_on, contextFromPrincipal(principal));
51514
- return json3({ dependency }, 201);
53218
+ return json4({ dependency }, 201);
51515
53219
  } catch (e) {
51516
53220
  const msg = e.message || "";
51517
53221
  if (msg.includes("not found"))
@@ -51525,7 +53229,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51525
53229
  if (!subId)
51526
53230
  return error(400, "dependency target id is required (/v1/tasks/:id/dependencies/:dep)");
51527
53231
  const removed = await store.dependencies.remove(id, subId);
51528
- return json3({ removed });
53232
+ return json4({ removed });
51529
53233
  }
51530
53234
  return error(405, `method ${method} not allowed on /v1/tasks/:id/dependencies`);
51531
53235
  }
@@ -51536,10 +53240,10 @@ async function handleV1Request(req, url, dependencies = {}) {
51536
53240
  if (!await store.tasks.get(id))
51537
53241
  return error(404, "task not found");
51538
53242
  const verifications = await store.verifications.list(id);
51539
- return json3({ verifications, count: verifications.length });
53243
+ return json4({ verifications, count: verifications.length });
51540
53244
  }
51541
53245
  if (method === "POST") {
51542
- const body2 = await readJson2(req) ?? {};
53246
+ const body2 = await readJson3(req) ?? {};
51543
53247
  if (typeof body2.command !== "string" || !body2.command.trim()) {
51544
53248
  return error(400, "command is required");
51545
53249
  }
@@ -51552,7 +53256,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51552
53256
  artifact_path: body2.artifact_path,
51553
53257
  agent_id: body2.agent_id
51554
53258
  }, contextFromPrincipal(principal, body2));
51555
- return json3({ verification }, 201);
53259
+ return json4({ verification }, 201);
51556
53260
  } catch (e) {
51557
53261
  const msg = e.message || "";
51558
53262
  if (msg.includes("not found"))
@@ -51569,10 +53273,10 @@ async function handleV1Request(req, url, dependencies = {}) {
51569
53273
  if (!await store.tasks.get(id))
51570
53274
  return error(404, "task not found");
51571
53275
  const commits = await store.commits.list(id);
51572
- return json3({ commits, count: commits.length });
53276
+ return json4({ commits, count: commits.length });
51573
53277
  }
51574
53278
  if (method === "POST") {
51575
- const body2 = await readJson2(req) ?? {};
53279
+ const body2 = await readJson3(req) ?? {};
51576
53280
  if (typeof body2.sha !== "string" || !body2.sha.trim())
51577
53281
  return error(400, "sha is required");
51578
53282
  try {
@@ -51583,7 +53287,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51583
53287
  author: body2.author,
51584
53288
  files_changed: Array.isArray(body2.files_changed) ? body2.files_changed : undefined
51585
53289
  }, contextFromPrincipal(principal));
51586
- return json3({ commit }, 201);
53290
+ return json4({ commit }, 201);
51587
53291
  } catch (e) {
51588
53292
  const msg = e.message || "";
51589
53293
  if (msg.includes("not found"))
@@ -51600,10 +53304,10 @@ async function handleV1Request(req, url, dependencies = {}) {
51600
53304
  if (!await store.tasks.get(id))
51601
53305
  return error(404, "task not found");
51602
53306
  const refs = await store.gitRefs.list(id);
51603
- return json3({ refs, count: refs.length });
53307
+ return json4({ refs, count: refs.length });
51604
53308
  }
51605
53309
  if (method === "POST") {
51606
- const body2 = await readJson2(req) ?? {};
53310
+ const body2 = await readJson3(req) ?? {};
51607
53311
  const refType = body2.ref_type === "pull_request" || body2.ref_type === "branch" ? body2.ref_type : "branch";
51608
53312
  if (typeof body2.name !== "string" || !body2.name.trim())
51609
53313
  return error(400, "name is required");
@@ -51616,7 +53320,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51616
53320
  provider: body2.provider,
51617
53321
  metadata: body2.metadata
51618
53322
  }, contextFromPrincipal(principal));
51619
- return json3({ ref }, 201);
53323
+ return json4({ ref }, 201);
51620
53324
  } catch (e) {
51621
53325
  const msg = e.message || "";
51622
53326
  if (msg.includes("not found"))
@@ -51632,21 +53336,21 @@ async function handleV1Request(req, url, dependencies = {}) {
51632
53336
  const body = actionJson.value && typeof actionJson.value === "object" && !Array.isArray(actionJson.value) ? actionJson.value : {};
51633
53337
  const agentId = typeof body.agent_id === "string" ? body.agent_id : principal.agent || "todos-serve";
51634
53338
  if (action === "start" && method === "POST") {
51635
- return json3({ task: await store.tasks.start(id, agentId) });
53339
+ return json4({ task: await store.tasks.start(id, agentId) });
51636
53340
  }
51637
53341
  if (action === "complete" && method === "POST") {
51638
53342
  const parsed = validateTaskCompletion(actionJson.value);
51639
53343
  if (!parsed.ok)
51640
53344
  return error(400, parsed.message);
51641
- return json3({
53345
+ return json4({
51642
53346
  task: await store.tasks.complete(id, parsed.agentId || principal.agent || "todos-serve", parsed.options, contextFromPrincipal(principal, body))
51643
53347
  });
51644
53348
  }
51645
53349
  if (action === "fail" && method === "POST") {
51646
- return json3({ result: await store.tasks.fail(id, agentId, typeof body.reason === "string" ? body.reason : "failed", {}) });
53350
+ return json4({ result: await store.tasks.fail(id, agentId, typeof body.reason === "string" ? body.reason : "failed", {}) });
51647
53351
  }
51648
53352
  if (action === "claim" && method === "POST") {
51649
- return json3({ task: await store.tasks.claimNext(agentId, {}) });
53353
+ return json4({ task: await store.tasks.claimNext(agentId, {}) });
51650
53354
  }
51651
53355
  return error(404, `unknown task action: ${action}`);
51652
53356
  }
@@ -51669,10 +53373,10 @@ async function handleV1Request(req, url, dependencies = {}) {
51669
53373
  throw e;
51670
53374
  }
51671
53375
  }
51672
- return task2 ? json3({ task: task2 }) : error(404, "task not found");
53376
+ return task2 ? json4({ task: task2 }) : error(404, "task not found");
51673
53377
  }
51674
53378
  if (method === "PATCH" || method === "PUT") {
51675
- const body = await readJson2(req);
53379
+ const body = await readJson3(req);
51676
53380
  if (!body)
51677
53381
  return error(400, "invalid JSON body");
51678
53382
  const current = await store.tasks.get(id);
@@ -51684,7 +53388,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51684
53388
  };
51685
53389
  try {
51686
53390
  const task2 = await store.tasks.update(id, patch);
51687
- return task2 ? json3({ task: task2 }) : error(404, "task not found");
53391
+ return task2 ? json4({ task: task2 }) : error(404, "task not found");
51688
53392
  } catch (e) {
51689
53393
  const msg = e.message || "";
51690
53394
  if (msg.includes("version conflict"))
@@ -51694,7 +53398,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51694
53398
  }
51695
53399
  if (method === "DELETE") {
51696
53400
  await store.tasks.delete(id, contextFromPrincipal(principal));
51697
- return json3({ deleted: true, id });
53401
+ return json4({ deleted: true, id });
51698
53402
  }
51699
53403
  return error(405, `method ${method} not allowed on /v1/tasks/:id`);
51700
53404
  }
@@ -51702,24 +53406,24 @@ async function handleV1Request(req, url, dependencies = {}) {
51702
53406
  if (!id) {
51703
53407
  if (method === "GET") {
51704
53408
  const projects = await store.projects.list();
51705
- return json3({ projects, count: projects.length });
53409
+ return json4({ projects, count: projects.length });
51706
53410
  }
51707
53411
  if (method === "POST") {
51708
- const body = await readJson2(req);
53412
+ const body = await readJson3(req);
51709
53413
  if (!body)
51710
53414
  return error(400, "invalid JSON body");
51711
53415
  const validated = validateProjectCreate(body);
51712
53416
  if (!validated.ok)
51713
53417
  return error(400, validated.message);
51714
53418
  const project = await store.projects.create(validated.input, contextFromPrincipal(principal));
51715
- return json3({ project }, 201);
53419
+ return json4({ project }, 201);
51716
53420
  }
51717
53421
  return error(405, `method ${method} not allowed on /v1/projects`);
51718
53422
  }
51719
53423
  if (action === "rename") {
51720
53424
  if (method !== "POST")
51721
53425
  return error(405, `method ${method} not allowed on /v1/projects/:id/rename`);
51722
- const body = await readJson2(req);
53426
+ const body = await readJson3(req);
51723
53427
  if (!body || typeof body.new_slug !== "string" || !body.new_slug.trim() || !normalizeSlug(body.new_slug)) {
51724
53428
  return error(400, "new_slug must be a non-empty string");
51725
53429
  }
@@ -51729,14 +53433,14 @@ async function handleV1Request(req, url, dependencies = {}) {
51729
53433
  const unknownField = Object.keys(body).find((key) => !["new_slug", "name"].includes(key));
51730
53434
  if (unknownField)
51731
53435
  return error(400, `unknown project rename field: ${unknownField}`);
51732
- return json3(await store.projects.rename(id, body, contextFromPrincipal(principal)));
53436
+ return json4(await store.projects.rename(id, body, contextFromPrincipal(principal)));
51733
53437
  }
51734
53438
  if (method === "GET") {
51735
53439
  const project = await store.projects.get(id);
51736
- return project ? json3({ project }) : error(404, "project not found");
53440
+ return project ? json4({ project }) : error(404, "project not found");
51737
53441
  }
51738
53442
  if (method === "PATCH" || method === "PUT") {
51739
- const body = await readJson2(req);
53443
+ const body = await readJson3(req);
51740
53444
  if (!body)
51741
53445
  return error(400, "invalid JSON body");
51742
53446
  const validated = validateProjectPatch(body);
@@ -51745,21 +53449,21 @@ async function handleV1Request(req, url, dependencies = {}) {
51745
53449
  if (!await store.projects.get(id))
51746
53450
  return error(404, "project not found");
51747
53451
  const project = await store.projects.update(id, validated.patch);
51748
- return json3({ project });
53452
+ return json4({ project });
51749
53453
  }
51750
53454
  if (method === "DELETE") {
51751
53455
  await store.projects.delete(id, contextFromPrincipal(principal));
51752
- return json3({ deleted: true, id });
53456
+ return json4({ deleted: true, id });
51753
53457
  }
51754
53458
  return error(405, `method ${method} not allowed on /v1/projects/:id`);
51755
53459
  }
51756
53460
  if (resource === "plans") {
51757
53461
  if (!id && method === "GET") {
51758
53462
  const plans = await store.plans.list(url.searchParams.get("project_id") ?? undefined);
51759
- return json3({ plans, count: plans.length });
53463
+ return json4({ plans, count: plans.length });
51760
53464
  }
51761
53465
  if (!id && method === "POST") {
51762
- const body = await readJson2(req);
53466
+ const body = await readJson3(req);
51763
53467
  const validated = validatePlanCreate(body);
51764
53468
  if (!validated.ok)
51765
53469
  return error(400, validated.message);
@@ -51774,14 +53478,14 @@ async function handleV1Request(req, url, dependencies = {}) {
51774
53478
  }
51775
53479
  }
51776
53480
  const plan = await store.plans.create(validated.input, contextFromPrincipal(principal, validated.input));
51777
- return json3({ plan }, 201);
53481
+ return json4({ plan }, 201);
51778
53482
  }
51779
53483
  if (id && method === "GET") {
51780
53484
  const plan = await store.plans.get(id);
51781
- return plan ? json3({ plan }) : error(404, "plan not found");
53485
+ return plan ? json4({ plan }) : error(404, "plan not found");
51782
53486
  }
51783
53487
  if (id && (method === "PATCH" || method === "PUT")) {
51784
- const body = await readJson2(req);
53488
+ const body = await readJson3(req);
51785
53489
  if (!body || Object.keys(body).length === 0)
51786
53490
  return error(400, "plan patch is required");
51787
53491
  const allowed = new Set(["name", "slug", "description", "status", "task_list_id", "agent_id"]);
@@ -51818,12 +53522,12 @@ async function handleV1Request(req, url, dependencies = {}) {
51818
53522
  }
51819
53523
  }
51820
53524
  const plan = await store.plans.update(id, body);
51821
- return json3({ plan });
53525
+ return json4({ plan });
51822
53526
  }
51823
53527
  if (id && method === "DELETE") {
51824
53528
  if (!await store.plans.delete(id, contextFromPrincipal(principal)))
51825
53529
  return error(404, "plan not found");
51826
- return json3({ deleted: true, id });
53530
+ return json4({ deleted: true, id });
51827
53531
  }
51828
53532
  if (id)
51829
53533
  return error(405, `method ${method} not allowed on /v1/plans/:id`);
@@ -51832,50 +53536,50 @@ async function handleV1Request(req, url, dependencies = {}) {
51832
53536
  if (!id && method === "GET") {
51833
53537
  const projectId = url.searchParams.get("project_id");
51834
53538
  const templates = (await store.templates.list()).filter((template) => projectId === null || template.project_id === projectId);
51835
- return json3({ templates, count: templates.length });
53539
+ return json4({ templates, count: templates.length });
51836
53540
  }
51837
53541
  if (!id && method === "POST") {
51838
- const body = await readJson2(req);
53542
+ const body = await readJson3(req);
51839
53543
  const validated = validateTemplateCreate(body);
51840
53544
  if (!validated.ok)
51841
53545
  return error(400, validated.message);
51842
53546
  const template = await store.templates.create(validated.input, contextFromPrincipal(principal));
51843
- return json3({ template: await store.templates.getWithTasks(template.id) }, 201);
53547
+ return json4({ template: await store.templates.getWithTasks(template.id) }, 201);
51844
53548
  }
51845
53549
  if (!id)
51846
53550
  return error(405, `method ${method} not allowed on /v1/templates`);
51847
53551
  if (method === "GET") {
51848
53552
  const template = await store.templates.getWithTasks(id);
51849
- return template ? json3({ template }) : error(404, "template not found");
53553
+ return template ? json4({ template }) : error(404, "template not found");
51850
53554
  }
51851
53555
  if (method === "PATCH" || method === "PUT") {
51852
- const body = await readJson2(req);
53556
+ const body = await readJson3(req);
51853
53557
  const validated = validateTemplatePatch(body);
51854
53558
  if (!validated.ok)
51855
53559
  return error(400, validated.message);
51856
53560
  const template = await store.templates.update(id, validated.patch, contextFromPrincipal(principal));
51857
- return template ? json3({ template: await store.templates.getWithTasks(id) }) : error(404, "template not found");
53561
+ return template ? json4({ template: await store.templates.getWithTasks(id) }) : error(404, "template not found");
51858
53562
  }
51859
53563
  if (method === "DELETE") {
51860
53564
  const deleted = await store.templates.delete(id, contextFromPrincipal(principal));
51861
- return deleted ? json3({ deleted: true, id }) : error(404, "template not found");
53565
+ return deleted ? json4({ deleted: true, id }) : error(404, "template not found");
51862
53566
  }
51863
53567
  return error(405, `method ${method} not allowed on /v1/templates/:id`);
51864
53568
  }
51865
53569
  if (resource === "agents") {
51866
53570
  if (!id && method === "GET") {
51867
53571
  const agents = await store.agents.list();
51868
- return json3({ agents, count: agents.length });
53572
+ return json4({ agents, count: agents.length });
51869
53573
  }
51870
53574
  if (!id && method === "POST") {
51871
- const body = await readJson2(req);
53575
+ const body = await readJson3(req);
51872
53576
  if (!body || typeof body.name !== "string" || !body.name.trim())
51873
53577
  return error(400, "name is required");
51874
53578
  const result = await store.agents.register(body, contextFromPrincipal(principal));
51875
53579
  if (result && typeof result === "object" && "conflict" in result) {
51876
53580
  return error(409, result.message ?? "agent name conflict", { conflict: true });
51877
53581
  }
51878
- return json3({ agent: result }, 201);
53582
+ return json4({ agent: result }, 201);
51879
53583
  }
51880
53584
  if (id && action === "heartbeat") {
51881
53585
  if (method !== "POST")
@@ -51884,7 +53588,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51884
53588
  return error(501, "agent heartbeat is not supported by this storage backend");
51885
53589
  }
51886
53590
  const agent = await store.agents.heartbeat(id, contextFromPrincipal(principal));
51887
- return agent ? json3({ agent }) : error(404, "agent not found");
53591
+ return agent ? json4({ agent }) : error(404, "agent not found");
51888
53592
  }
51889
53593
  if (id && action === "release") {
51890
53594
  if (method !== "POST")
@@ -51892,18 +53596,18 @@ async function handleV1Request(req, url, dependencies = {}) {
51892
53596
  if (typeof store.agents.release !== "function") {
51893
53597
  return error(501, "agent release is not supported by this storage backend");
51894
53598
  }
51895
- const body = await readJson2(req) ?? {};
53599
+ const body = await readJson3(req) ?? {};
51896
53600
  const result = await store.agents.release(id, body.session_id, contextFromPrincipal(principal));
51897
53601
  if (!result)
51898
53602
  return error(404, "agent not found");
51899
53603
  if (!result.released) {
51900
53604
  return error(409, "release denied: session_id does not match agent's current session", { released: false });
51901
53605
  }
51902
- return json3({ agent: result.agent, released: true });
53606
+ return json4({ agent: result.agent, released: true });
51903
53607
  }
51904
53608
  if (id && method === "GET") {
51905
53609
  const agent = await store.agents.get(id);
51906
- return agent ? json3({ agent }) : error(404, "agent not found");
53610
+ return agent ? json4({ agent }) : error(404, "agent not found");
51907
53611
  }
51908
53612
  }
51909
53613
  if (resource === "activity" && !id) {
@@ -51912,16 +53616,16 @@ async function handleV1Request(req, url, dependencies = {}) {
51912
53616
  const limitParam = url.searchParams.get("limit");
51913
53617
  const limit = limitParam ? Math.max(1, Math.min(1e4, Number(limitParam) || 50)) : 50;
51914
53618
  const activity = await store.audit.getRecentActivity(limit);
51915
- return json3({ activity, count: activity.length });
53619
+ return json4({ activity, count: activity.length });
51916
53620
  }
51917
53621
  if (resource === "task-lists") {
51918
53622
  if (!id && method === "GET") {
51919
53623
  const projectId = url.searchParams.get("project_id") ?? undefined;
51920
53624
  const taskLists = await store.taskLists.list(projectId);
51921
- return json3({ task_lists: taskLists, count: taskLists.length });
53625
+ return json4({ task_lists: taskLists, count: taskLists.length });
51922
53626
  }
51923
53627
  if (!id && method === "POST") {
51924
- const body = await readJson2(req);
53628
+ const body = await readJson3(req);
51925
53629
  if (!body || typeof body.name !== "string" || !body.name.trim())
51926
53630
  return error(400, "name is required");
51927
53631
  const unknownField = Object.keys(body).find((key) => !["name", "slug", "project_id", "description", "metadata"].includes(key));
@@ -51940,14 +53644,14 @@ async function handleV1Request(req, url, dependencies = {}) {
51940
53644
  return error(400, "task-list slug must be non-empty kebab-case");
51941
53645
  }
51942
53646
  const taskList = await store.taskLists.create(body, contextFromPrincipal(principal));
51943
- return json3({ task_list: taskList }, 201);
53647
+ return json4({ task_list: taskList }, 201);
51944
53648
  }
51945
53649
  if (id && method === "GET") {
51946
53650
  const taskList = await store.taskLists.get(id);
51947
- return taskList ? json3({ task_list: taskList }) : error(404, "task list not found");
53651
+ return taskList ? json4({ task_list: taskList }) : error(404, "task list not found");
51948
53652
  }
51949
53653
  if (id && (method === "PATCH" || method === "PUT")) {
51950
- const body = await readJson2(req);
53654
+ const body = await readJson3(req);
51951
53655
  if (!body)
51952
53656
  return error(400, "invalid JSON body");
51953
53657
  const unknownField = Object.keys(body).find((key) => !["slug", "name", "description", "metadata"].includes(key));
@@ -51967,11 +53671,11 @@ async function handleV1Request(req, url, dependencies = {}) {
51967
53671
  if (!await store.taskLists.get(id))
51968
53672
  return error(404, "task list not found");
51969
53673
  const taskList = await store.taskLists.update(id, body);
51970
- return json3({ task_list: taskList });
53674
+ return json4({ task_list: taskList });
51971
53675
  }
51972
53676
  if (id && method === "DELETE") {
51973
53677
  const deleted = await store.taskLists.delete(id, contextFromPrincipal(principal));
51974
- return deleted ? json3({ deleted: true, id }) : error(404, "task list not found");
53678
+ return deleted ? json4({ deleted: true, id }) : error(404, "task list not found");
51975
53679
  }
51976
53680
  return error(405, `method ${method} not allowed on /v1/task-lists${id ? "/:id" : ""}`);
51977
53681
  }
@@ -51982,7 +53686,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51982
53686
  return error(501, "dependency edge listing is not supported by this storage backend");
51983
53687
  }
51984
53688
  const dependencies2 = await store.dependencies.listAll();
51985
- return json3({ dependencies: dependencies2, count: dependencies2.length });
53689
+ return json4({ dependencies: dependencies2, count: dependencies2.length });
51986
53690
  }
51987
53691
  if (resource === "commits" && id) {
51988
53692
  if (method !== "GET")
@@ -51990,7 +53694,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51990
53694
  if (!store.commits)
51991
53695
  return error(501, "commit links are not supported by this storage backend");
51992
53696
  const commit = await store.commits.find(id);
51993
- return json3({ commit: commit ?? null });
53697
+ return json4({ commit: commit ?? null });
51994
53698
  }
51995
53699
  if (resource === "refs" && id) {
51996
53700
  if (method !== "GET")
@@ -52004,7 +53708,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52004
53708
  return error(400, "ref path segment has invalid percent encoding");
52005
53709
  }
52006
53710
  const refs = await store.gitRefs.find(decodedRef);
52007
- return json3({ refs, count: refs.length });
53711
+ return json4({ refs, count: refs.length });
52008
53712
  }
52009
53713
  if (resource === "next" && !id) {
52010
53714
  if (method !== "GET")
@@ -52016,7 +53720,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52016
53720
  ...url.searchParams.get("plan_id") ? { plan_id: url.searchParams.get("plan_id") } : {}
52017
53721
  };
52018
53722
  const task2 = await store.tasks.getNext(agent, filters);
52019
- return json3({ task: task2 ?? null });
53723
+ return json4({ task: task2 ?? null });
52020
53724
  }
52021
53725
  if (resource === "stats" && method === "GET") {
52022
53726
  const [tasks, tasksAll, projects] = await Promise.all([
@@ -52024,7 +53728,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52024
53728
  store.tasks.count({ include_subtasks: true }),
52025
53729
  store.projects.list()
52026
53730
  ]);
52027
- return json3({ tasks, tasks_all: tasksAll, subtasks: tasksAll - tasks, projects: projects.length });
53731
+ return json4({ tasks, tasks_all: tasksAll, subtasks: tasksAll - tasks, projects: projects.length });
52028
53732
  }
52029
53733
  if (resource === "integrity" && !id) {
52030
53734
  if (method !== "GET")
@@ -52033,7 +53737,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52033
53737
  return error(501, "referential-integrity reporting is not supported by this storage backend");
52034
53738
  }
52035
53739
  const integrity = await store.integrity.report();
52036
- return json3({ integrity });
53740
+ return json4({ integrity });
52037
53741
  }
52038
53742
  if (resource === "import") {
52039
53743
  if (method !== "POST")
@@ -52041,7 +53745,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52041
53745
  if (typeof store.sync.importSnapshot !== "function") {
52042
53746
  return error(501, "snapshot import is not supported by this storage backend");
52043
53747
  }
52044
- const raw = await readJson2(req);
53748
+ const raw = await readJson3(req);
52045
53749
  if (raw === null)
52046
53750
  return error(400, "invalid JSON body");
52047
53751
  const snapshot = normalizeImportSnapshot(raw);
@@ -52050,7 +53754,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52050
53754
  return error(400, "empty snapshot: provide at least one record array (tasks/projects/plans/...)");
52051
53755
  }
52052
53756
  const result = await store.sync.importSnapshot(snapshot, contextFromPrincipal(principal));
52053
- return json3({ result, received });
53757
+ return json4({ result, received });
52054
53758
  }
52055
53759
  return error(404, `unknown /v1 resource: ${resource ?? "(root)"}`);
52056
53760
  } catch (e) {
@@ -52073,13 +53777,14 @@ async function handleV1Request(req, url, dependencies = {}) {
52073
53777
  return error(500, e.message || "internal error");
52074
53778
  }
52075
53779
  }
52076
- var JSON_HEADERS2, DEFAULT_COMMENT_PAGE_SIZE = 100, MAX_COMMENT_PAGE_SIZE = 500, LEGACY_COMMENT_RESPONSE_LIMIT = 500;
53780
+ var JSON_HEADERS3, DEFAULT_COMMENT_PAGE_SIZE = 100, MAX_COMMENT_PAGE_SIZE = 500, LEGACY_COMMENT_RESPONSE_LIMIT = 500;
52077
53781
  var init_v1 = __esm(() => {
52078
53782
  init_types();
52079
53783
  init_cloud();
52080
53784
  init_pr_groups();
53785
+ init_project_registration();
52081
53786
  init_redaction();
52082
- JSON_HEADERS2 = { "Content-Type": "application/json" };
53787
+ JSON_HEADERS3 = { "Content-Type": "application/json" };
52083
53788
  });
52084
53789
 
52085
53790
  // src/pr-groups/sqlite.ts
@@ -52238,12 +53943,12 @@ class SqlitePrGroupLedgerPersistence {
52238
53943
  this.tx = new SqlitePrGroupTransaction(db);
52239
53944
  }
52240
53945
  async transaction(fn) {
52241
- const previous = sqliteTransactionTails.get(this.db) ?? Promise.resolve();
53946
+ const previous = sqliteTransactionTails2.get(this.db) ?? Promise.resolve();
52242
53947
  let release;
52243
53948
  const current = new Promise((resolve17) => {
52244
53949
  release = resolve17;
52245
53950
  });
52246
- sqliteTransactionTails.set(this.db, current);
53951
+ sqliteTransactionTails2.set(this.db, current);
52247
53952
  await previous;
52248
53953
  try {
52249
53954
  this.db.exec("BEGIN IMMEDIATE");
@@ -52257,8 +53962,8 @@ class SqlitePrGroupLedgerPersistence {
52257
53962
  throw error2;
52258
53963
  } finally {
52259
53964
  release();
52260
- if (sqliteTransactionTails.get(this.db) === current)
52261
- sqliteTransactionTails.delete(this.db);
53965
+ if (sqliteTransactionTails2.get(this.db) === current)
53966
+ sqliteTransactionTails2.delete(this.db);
52262
53967
  }
52263
53968
  }
52264
53969
  getGroup(id) {
@@ -52288,9 +53993,9 @@ class SqlitePrGroupLedgerPersistence {
52288
53993
  return row ? eventFromRow2(row) : null;
52289
53994
  }
52290
53995
  }
52291
- var sqliteTransactionTails;
52292
- var init_sqlite = __esm(() => {
52293
- sqliteTransactionTails = new WeakMap;
53996
+ var sqliteTransactionTails2;
53997
+ var init_sqlite2 = __esm(() => {
53998
+ sqliteTransactionTails2 = new WeakMap;
52294
53999
  });
52295
54000
 
52296
54001
  // src/pr-groups/index.ts
@@ -52320,10 +54025,10 @@ function createLocalPrGroupLedger(db = getDatabase()) {
52320
54025
  var init_pr_groups2 = __esm(() => {
52321
54026
  init_database();
52322
54027
  init_ledger();
52323
- init_sqlite();
54028
+ init_sqlite2();
52324
54029
  init_types3();
52325
54030
  init_ledger();
52326
- init_sqlite();
54031
+ init_sqlite2();
52327
54032
  init_http_client();
52328
54033
  init_postgres();
52329
54034
  });
@@ -52334,7 +54039,7 @@ __export(exports_serve, {
52334
54039
  taskToSummary: () => taskToSummary,
52335
54040
  startServer: () => startServer,
52336
54041
  serveStaticFile: () => serveStaticFile,
52337
- json: () => json,
54042
+ json: () => json2,
52338
54043
  checkAuth: () => checkAuth,
52339
54044
  SECURITY_HEADERS: () => SECURITY_HEADERS,
52340
54045
  MIME_TYPES: () => MIME_TYPES
@@ -52420,7 +54125,7 @@ function checkRateLimit(ip) {
52420
54125
  }
52421
54126
  return { allowed: true };
52422
54127
  }
52423
- function json(data, status = 200, headers) {
54128
+ function json2(data, status = 200, headers) {
52424
54129
  return new Response(JSON.stringify(data), {
52425
54130
  status,
52426
54131
  headers: {
@@ -52565,7 +54270,7 @@ Dashboard not found at: ${dashboardDir}`);
52565
54270
  "Access-Control-Allow-Headers": "Content-Type, X-API-Key, Authorization",
52566
54271
  Vary: "Origin"
52567
54272
  } : undefined;
52568
- const jsonWithCors = (data, status = 200) => json(data, status, corsHeaders);
54273
+ const jsonWithCors = (data, status = 200) => json2(data, status, corsHeaders);
52569
54274
  if (method === "OPTIONS") {
52570
54275
  return new Response(null, {
52571
54276
  headers: corsHeaders || {
@@ -52646,13 +54351,13 @@ Dashboard not found at: ${dashboardDir}`);
52646
54351
  return res;
52647
54352
  }
52648
54353
  if (path === "/api/health" && method === "GET") {
52649
- return handleHealth(ctx, json);
54354
+ return handleHealth(ctx, json2);
52650
54355
  }
52651
54356
  if (path === "/api/headless" && method === "GET") {
52652
- return handleHeadlessBoundary(ctx, json);
54357
+ return handleHeadlessBoundary(ctx, json2);
52653
54358
  }
52654
54359
  if (path === "/api/stats" && method === "GET") {
52655
- return handleStats(ctx, json);
54360
+ return handleStats(ctx, json2);
52656
54361
  }
52657
54362
  if (path === "/api/tasks" && method === "GET") {
52658
54363
  return handleListTasks(req, url, ctx, jsonWithCors, taskToSummary);
@@ -52667,16 +54372,16 @@ Dashboard not found at: ${dashboardDir}`);
52667
54372
  return handleTasksExport(req, url, ctx, jsonWithCors, taskToSummary);
52668
54373
  }
52669
54374
  if (path === "/api/tasks/bulk" && method === "POST") {
52670
- return handleTasksBulk(req, ctx, json);
54375
+ return handleTasksBulk(req, ctx, json2);
52671
54376
  }
52672
54377
  if (path === "/api/tasks/status" && method === "GET") {
52673
- return handleTasksStatus(req, url, ctx, json);
54378
+ return handleTasksStatus(req, url, ctx, json2);
52674
54379
  }
52675
54380
  if (path === "/api/tasks/next" && method === "GET") {
52676
54381
  return handleTasksNext(req, url, ctx, jsonWithCors, taskToSummary);
52677
54382
  }
52678
54383
  if (path === "/api/tasks/active" && method === "GET") {
52679
- return handleTasksActive(req, url, ctx, json);
54384
+ return handleTasksActive(req, url, ctx, json2);
52680
54385
  }
52681
54386
  if (path === "/api/tasks/stale" && method === "GET") {
52682
54387
  return handleTasksStale(req, url, ctx, jsonWithCors, taskToSummary);
@@ -52689,11 +54394,11 @@ Dashboard not found at: ${dashboardDir}`);
52689
54394
  }
52690
54395
  const attachmentsMatch = path.match(/^\/api\/tasks\/([^/]+)\/attachments$/);
52691
54396
  if (attachmentsMatch && method === "GET") {
52692
- return handleTaskAttachments(attachmentsMatch[1], ctx, json);
54397
+ return handleTaskAttachments(attachmentsMatch[1], ctx, json2);
52693
54398
  }
52694
54399
  const progressMatch = path.match(/^\/api\/tasks\/([^/]+)\/progress$/);
52695
54400
  if (progressMatch) {
52696
- const res = await handleTaskProgress(progressMatch[1], req, method, ctx, json, url);
54401
+ const res = await handleTaskProgress(progressMatch[1], req, method, ctx, json2, url);
52697
54402
  if (res !== null)
52698
54403
  return res;
52699
54404
  }
@@ -52707,7 +54412,7 @@ Dashboard not found at: ${dashboardDir}`);
52707
54412
  return handlePatchTask(id, req, ctx, jsonWithCors, taskToSummary);
52708
54413
  }
52709
54414
  if (method === "DELETE") {
52710
- return handleDeleteTask(id, ctx, json);
54415
+ return handleDeleteTask(id, ctx, json2);
52711
54416
  }
52712
54417
  }
52713
54418
  const startMatch = path.match(/^\/api\/tasks\/([^/]+)\/start$/);
@@ -52723,7 +54428,7 @@ Dashboard not found at: ${dashboardDir}`);
52723
54428
  return handleCompleteTask(completeMatch[1], ctx, jsonWithCors, taskToSummary);
52724
54429
  }
52725
54430
  if (path === "/api/projects" && method === "GET") {
52726
- return handleListProjects(url, ctx, json);
54431
+ return handleListProjects(url, ctx, json2);
52727
54432
  }
52728
54433
  if (path === "/api/agents/me" && method === "GET") {
52729
54434
  return handleAgentMe(req, url, ctx, jsonWithCors, taskToSummary);
@@ -52736,92 +54441,92 @@ Dashboard not found at: ${dashboardDir}`);
52736
54441
  return handleClaimTask(req, ctx, jsonWithCors, taskToSummary);
52737
54442
  }
52738
54443
  if (path === "/api/orgs" && method === "GET") {
52739
- return handleListOrgs(ctx, json);
54444
+ return handleListOrgs(ctx, json2);
52740
54445
  }
52741
54446
  if (path === "/api/orgs" && method === "POST") {
52742
- return handleCreateOrg(req, ctx, json);
54447
+ return handleCreateOrg(req, ctx, json2);
52743
54448
  }
52744
54449
  const orgMatch = path.match(/^\/api\/orgs\/([^/]+)$/);
52745
54450
  if (orgMatch && method === "PATCH") {
52746
- return handleUpdateOrg(orgMatch[1], req, ctx, json);
54451
+ return handleUpdateOrg(orgMatch[1], req, ctx, json2);
52747
54452
  }
52748
54453
  if (orgMatch && method === "DELETE") {
52749
- return handleDeleteOrg(orgMatch[1], ctx, json);
54454
+ return handleDeleteOrg(orgMatch[1], ctx, json2);
52750
54455
  }
52751
54456
  if (path === "/api/org" && method === "GET") {
52752
- return handleOrgChart(ctx, json);
54457
+ return handleOrgChart(ctx, json2);
52753
54458
  }
52754
54459
  const teamMatch = path.match(/^\/api\/agents\/([^/]+)\/team$/);
52755
54460
  if (teamMatch && method === "GET") {
52756
- return handleAgentTeam(teamMatch[1], ctx, json);
54461
+ return handleAgentTeam(teamMatch[1], ctx, json2);
52757
54462
  }
52758
54463
  if (path === "/api/agents" && method === "GET") {
52759
- return handleListAgents(url, ctx, json);
54464
+ return handleListAgents(url, ctx, json2);
52760
54465
  }
52761
54466
  if (path === "/api/projects" && method === "POST") {
52762
- return handleCreateProject(req, ctx, json);
54467
+ return handleCreateProject(req, ctx, json2);
52763
54468
  }
52764
54469
  const projectDeleteMatch = path.match(/^\/api\/projects\/([^/]+)$/);
52765
54470
  if (projectDeleteMatch && method === "DELETE") {
52766
- return handleDeleteProject(projectDeleteMatch[1], ctx, json);
54471
+ return handleDeleteProject(projectDeleteMatch[1], ctx, json2);
52767
54472
  }
52768
54473
  if (path === "/api/agents" && method === "POST") {
52769
- return handleRegisterAgent(req, ctx, json);
54474
+ return handleRegisterAgent(req, ctx, json2);
52770
54475
  }
52771
54476
  const agentMatch = path.match(/^\/api\/agents\/([^/]+)$/);
52772
54477
  if (agentMatch && method === "PATCH") {
52773
- return handleUpdateAgent(agentMatch[1], req, ctx, json);
54478
+ return handleUpdateAgent(agentMatch[1], req, ctx, json2);
52774
54479
  }
52775
54480
  if (agentMatch && method === "DELETE") {
52776
- return handleDeleteAgent(agentMatch[1], ctx, json);
54481
+ return handleDeleteAgent(agentMatch[1], ctx, json2);
52777
54482
  }
52778
54483
  if (path === "/api/agents/bulk" && method === "POST") {
52779
- return handleBulkDeleteAgents(req, ctx, json);
54484
+ return handleBulkDeleteAgents(req, ctx, json2);
52780
54485
  }
52781
54486
  if (path === "/api/projects/bulk" && method === "POST") {
52782
- return handleBulkDeleteProjects(req, ctx, json);
54487
+ return handleBulkDeleteProjects(req, ctx, json2);
52783
54488
  }
52784
54489
  if (path === "/api/doctor" && method === "GET") {
52785
- return handleDoctor(ctx, json);
54490
+ return handleDoctor(ctx, json2);
52786
54491
  }
52787
54492
  if (path === "/api/report" && method === "GET") {
52788
- return handleReport(req, url, ctx, json);
54493
+ return handleReport(req, url, ctx, json2);
52789
54494
  }
52790
54495
  if (path === "/api/activity" && method === "GET") {
52791
- return handleActivity(req, url, ctx, json);
54496
+ return handleActivity(req, url, ctx, json2);
52792
54497
  }
52793
54498
  const historyMatch = path.match(/^\/api\/tasks\/([^/]+)\/history$/);
52794
54499
  if (historyMatch && method === "GET") {
52795
- return handleTaskHistory(historyMatch[1], ctx, json, url);
54500
+ return handleTaskHistory(historyMatch[1], ctx, json2, url);
52796
54501
  }
52797
54502
  if (path === "/api/webhooks" && method === "GET") {
52798
- return handleListWebhooks(ctx, json);
54503
+ return handleListWebhooks(ctx, json2);
52799
54504
  }
52800
54505
  if (path === "/api/webhooks" && method === "POST") {
52801
- return handleCreateWebhook(req, ctx, json);
54506
+ return handleCreateWebhook(req, ctx, json2);
52802
54507
  }
52803
54508
  const webhookMatch = path.match(/^\/api\/webhooks\/([^/]+)$/);
52804
54509
  if (webhookMatch && method === "DELETE") {
52805
- return handleDeleteWebhook(webhookMatch[1], ctx, json);
54510
+ return handleDeleteWebhook(webhookMatch[1], ctx, json2);
52806
54511
  }
52807
54512
  if (path === "/api/templates" && method === "GET") {
52808
- return handleListTemplates(ctx, json);
54513
+ return handleListTemplates(ctx, json2);
52809
54514
  }
52810
54515
  if (path === "/api/templates" && method === "POST") {
52811
- return handleCreateTemplate(req, ctx, json);
54516
+ return handleCreateTemplate(req, ctx, json2);
52812
54517
  }
52813
54518
  const templateMatch = path.match(/^\/api\/templates\/([^/]+)$/);
52814
54519
  if (templateMatch && method === "DELETE") {
52815
- return handleDeleteTemplate(templateMatch[1], ctx, json);
54520
+ return handleDeleteTemplate(templateMatch[1], ctx, json2);
52816
54521
  }
52817
54522
  if (path === "/api/plans" && method === "GET") {
52818
- return handleListPlans(url, ctx, json);
54523
+ return handleListPlans(url, ctx, json2);
52819
54524
  }
52820
54525
  if (path === "/api/plans" && method === "POST") {
52821
- return handleCreatePlan(req, ctx, json);
54526
+ return handleCreatePlan(req, ctx, json2);
52822
54527
  }
52823
54528
  if (path === "/api/plans/bulk" && method === "POST") {
52824
- return handleBulkDeletePlans(req, ctx, json);
54529
+ return handleBulkDeletePlans(req, ctx, json2);
52825
54530
  }
52826
54531
  const planMatch = path.match(/^\/api\/plans\/([^/]+)$/);
52827
54532
  if (planMatch) {
@@ -52830,16 +54535,16 @@ Dashboard not found at: ${dashboardDir}`);
52830
54535
  return handleGetPlan(id, ctx, jsonWithCors, taskToSummary);
52831
54536
  }
52832
54537
  if (method === "PATCH") {
52833
- return handleUpdatePlan(id, req, ctx, json);
54538
+ return handleUpdatePlan(id, req, ctx, json2);
52834
54539
  }
52835
54540
  if (method === "DELETE") {
52836
- return handleDeletePlan(id, ctx, json);
54541
+ return handleDeletePlan(id, ctx, json2);
52837
54542
  }
52838
54543
  }
52839
54544
  const staticRes = handleStaticFiles(path, method, ctx, jsonWithCors, serveStaticFile);
52840
54545
  if (staticRes)
52841
54546
  return staticRes;
52842
- return json({ error: "Not found" }, 404);
54547
+ return json2({ error: "Not found" }, 404);
52843
54548
  }
52844
54549
  });
52845
54550
  const shutdown = () => {