@hasna/todos 0.15.6 → 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 (40) hide show
  1. package/dist/cli/cloud-router.d.ts.map +1 -1
  2. package/dist/cli/index.js +3011 -1325
  3. package/dist/contracts.js +213 -3
  4. package/dist/db/migrations.d.ts.map +1 -1
  5. package/dist/db/schema.d.ts.map +1 -1
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1950 -26
  9. package/dist/mcp/index.js +2073 -371
  10. package/dist/mcp.js +7 -3
  11. package/dist/project-registration/authority.d.ts +45 -0
  12. package/dist/project-registration/authority.d.ts.map +1 -0
  13. package/dist/project-registration/backend.d.ts +83 -0
  14. package/dist/project-registration/backend.d.ts.map +1 -0
  15. package/dist/project-registration/http.d.ts +24 -0
  16. package/dist/project-registration/http.d.ts.map +1 -0
  17. package/dist/project-registration/index.d.ts +8 -0
  18. package/dist/project-registration/index.d.ts.map +1 -0
  19. package/dist/project-registration/postgres.d.ts +30 -0
  20. package/dist/project-registration/postgres.d.ts.map +1 -0
  21. package/dist/project-registration/schema.d.ts +3 -0
  22. package/dist/project-registration/schema.d.ts.map +1 -0
  23. package/dist/project-registration/sqlite.d.ts +17 -0
  24. package/dist/project-registration/sqlite.d.ts.map +1 -0
  25. package/dist/project-registration/types.d.ts +155 -0
  26. package/dist/project-registration/types.d.ts.map +1 -0
  27. package/dist/project-registration.d.ts +2 -0
  28. package/dist/project-registration.d.ts.map +1 -0
  29. package/dist/project-registration.js +17689 -0
  30. package/dist/registry.d.ts +1 -1
  31. package/dist/registry.d.ts.map +1 -1
  32. package/dist/registry.js +220 -3
  33. package/dist/release-provenance.json +5 -5
  34. package/dist/server/cloud.d.ts +3 -0
  35. package/dist/server/cloud.d.ts.map +1 -1
  36. package/dist/server/index.js +4374 -2672
  37. package/dist/server/v1.d.ts +2 -1
  38. package/dist/server/v1.d.ts.map +1 -1
  39. package/dist/storage.js +206 -0
  40. 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.6",
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,49 +52565,49 @@ 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
 
50915
52613
  // src/lib/comment-cursor.ts
@@ -50942,11 +52640,11 @@ __export(exports_v1, {
50942
52640
  handleV1Request: () => handleV1Request,
50943
52641
  countSnapshotRecords: () => countSnapshotRecords
50944
52642
  });
50945
- function json3(body, status = 200) {
50946
- 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 });
50947
52645
  }
50948
52646
  function error(status, message, extra) {
50949
- return json3({ error: message, ...extra ?? {} }, status);
52647
+ return json4({ error: message, ...extra ?? {} }, status);
50950
52648
  }
50951
52649
  function enumQueryParam(url, name, vocabulary) {
50952
52650
  const raw = url.searchParams.get(name);
@@ -51197,7 +52895,7 @@ function validateTemplatePatch(value) {
51197
52895
  ...body.plan_id === null ? { plan_id: null } : {}
51198
52896
  } };
51199
52897
  }
51200
- async function readJson2(req) {
52898
+ async function readJson3(req) {
51201
52899
  try {
51202
52900
  const text2 = await req.text();
51203
52901
  if (!text2)
@@ -51267,6 +52965,9 @@ async function handleV1Request(req, url, dependencies = {}) {
51267
52965
  if (path === "/v1/pr-groups" || path.startsWith("/v1/pr-groups/")) {
51268
52966
  return handlePrGroupHttpRequest(req, url, (dependencies.getPrGroupLedger ?? getCloudPrGroupLedger)(), "/v1/pr-groups", { actor_id: principal.agent, actor_run_id: principal.kid });
51269
52967
  }
52968
+ if (path === "/v1/project-registration" || path.startsWith("/v1/project-registration/")) {
52969
+ return handleTodosProjectRegistrationHttpRequest(req, url, (dependencies.getProjectRegistrationAuthority ?? getCloudProjectRegistrationAuthority)());
52970
+ }
51270
52971
  const store = (dependencies.getStorageAdapter ?? getCloudStorageAdapter)();
51271
52972
  const segments = path.split("/").filter(Boolean);
51272
52973
  const resource = segments[1];
@@ -51278,7 +52979,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51278
52979
  if (id === "exists" && !action) {
51279
52980
  if (method !== "POST")
51280
52981
  return error(405, `method ${method} not allowed on /v1/tasks/exists`);
51281
- const body = await readJson2(req);
52982
+ const body = await readJson3(req);
51282
52983
  const ids2 = Array.isArray(body?.ids) ? Array.from(new Set(body.ids.filter((v) => typeof v === "string" && v.length > 0))) : [];
51283
52984
  if (ids2.length === 0)
51284
52985
  return error(400, "provide a non-empty string array `ids`");
@@ -51288,7 +52989,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51288
52989
  const presentSet = new Set(found.map((t) => t.id));
51289
52990
  const present = ids2.filter((i) => presentSet.has(i));
51290
52991
  const missing = ids2.filter((i) => !presentSet.has(i));
51291
- return json3({
52992
+ return json4({
51292
52993
  requested: ids2.length,
51293
52994
  present_count: present.length,
51294
52995
  missing_count: missing.length,
@@ -51301,7 +53002,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51301
53002
  if (typeof store.tasks.getByFingerprint !== "function") {
51302
53003
  return error(501, "fingerprint upsert is not supported by this storage backend");
51303
53004
  }
51304
- const body = await readJson2(req) ?? {};
53005
+ const body = await readJson3(req) ?? {};
51305
53006
  const fingerprint3 = typeof body.fingerprint === "string" ? body.fingerprint.trim() : "";
51306
53007
  if (!fingerprint3)
51307
53008
  return error(400, "fingerprint is required");
@@ -51338,11 +53039,11 @@ async function handleV1Request(req, url, dependencies = {}) {
51338
53039
  }
51339
53040
  if (!existing) {
51340
53041
  const task2 = await store.tasks.create({ ...fields, title: body.title }, contextFromPrincipal(principal, body));
51341
- return json3({ task: task2, created: true }, 201);
53042
+ return json4({ task: task2, created: true }, 201);
51342
53043
  }
51343
53044
  try {
51344
53045
  const task2 = await store.tasks.update(existing.id, { ...fields, version: existing.version }, contextFromPrincipal(principal, body));
51345
- return json3({ task: task2, created: false });
53046
+ return json4({ task: task2, created: false });
51346
53047
  } catch (e) {
51347
53048
  const msg = e.message || "";
51348
53049
  if (msg.includes("version conflict"))
@@ -51384,15 +53085,15 @@ async function handleV1Request(req, url, dependencies = {}) {
51384
53085
  const tasks = await store.tasks.list(filter);
51385
53086
  const { limit: _l, offset: _o, ...countFilter } = filter;
51386
53087
  const total = await store.tasks.count(countFilter);
51387
- return json3({ tasks, count: tasks.length, total });
53088
+ return json4({ tasks, count: tasks.length, total });
51388
53089
  }
51389
53090
  if (method === "POST") {
51390
- const body = await readJson2(req);
53091
+ const body = await readJson3(req);
51391
53092
  if (!body || typeof body.title !== "string" || !body.title.trim()) {
51392
53093
  return error(400, "title is required");
51393
53094
  }
51394
53095
  const task2 = await store.tasks.create(body, contextFromPrincipal(principal, body));
51395
- return json3({ task: task2 }, 201);
53096
+ return json4({ task: task2 }, 201);
51396
53097
  }
51397
53098
  return error(405, `method ${method} not allowed on /v1/tasks`);
51398
53099
  }
@@ -51409,7 +53110,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51409
53110
  if (legacyPage.length > LEGACY_COMMENT_RESPONSE_LIMIT) {
51410
53111
  return error(426, "task has too many comments for this client; upgrade @hasna/todos to use cursor pagination");
51411
53112
  }
51412
- return json3({
53113
+ return json4({
51413
53114
  comments: legacyPage,
51414
53115
  count: legacyPage.length,
51415
53116
  has_more: false,
@@ -51434,7 +53135,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51434
53135
  const page = (await store.audit.getCommentsPage(id, { limit: limit + 1, ...before ? { before } : {} }, contextFromPrincipal(principal))).map(redactComment3);
51435
53136
  const hasMore = page.length > limit;
51436
53137
  const comments = hasMore ? page.slice(1) : page;
51437
- return json3({
53138
+ return json4({
51438
53139
  comments,
51439
53140
  count: comments.length,
51440
53141
  has_more: hasMore,
@@ -51442,7 +53143,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51442
53143
  });
51443
53144
  }
51444
53145
  if (method === "POST") {
51445
- const body2 = await readJson2(req) ?? {};
53146
+ const body2 = await readJson3(req) ?? {};
51446
53147
  if (typeof body2.content !== "string" || !body2.content.trim()) {
51447
53148
  return error(400, "content is required");
51448
53149
  }
@@ -51457,7 +53158,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51457
53158
  type: body2.type,
51458
53159
  progress_pct: body2.progress_pct
51459
53160
  }, contextFromPrincipal(principal, body2));
51460
- return json3({ comment: redactComment3(comment) }, 201);
53161
+ return json4({ comment: redactComment3(comment) }, 201);
51461
53162
  }
51462
53163
  return error(405, `method ${method} not allowed on /v1/tasks/:id/comments`);
51463
53164
  }
@@ -51467,19 +53168,19 @@ async function handleV1Request(req, url, dependencies = {}) {
51467
53168
  if (!await store.tasks.get(id))
51468
53169
  return error(404, "task not found");
51469
53170
  const history = await store.audit.getTaskHistory(id);
51470
- return json3({ history, count: history.length });
53171
+ return json4({ history, count: history.length });
51471
53172
  }
51472
53173
  if (action === "lock" || action === "unlock") {
51473
53174
  if (method !== "POST")
51474
53175
  return error(405, `method ${method} not allowed on /v1/tasks/:id/${action}`);
51475
- const body2 = await readJson2(req) ?? {};
53176
+ const body2 = await readJson3(req) ?? {};
51476
53177
  if (!await store.tasks.get(id))
51477
53178
  return error(404, "task not found");
51478
53179
  if (action === "lock") {
51479
53180
  if (typeof store.tasks.lock !== "function")
51480
53181
  return error(501, "task locking is not supported by this storage backend");
51481
53182
  const agentId3 = body2.agent_id || principal.agent || "todos-serve";
51482
- return json3({ result: await store.tasks.lock(id, agentId3) });
53183
+ return json4({ result: await store.tasks.lock(id, agentId3) });
51483
53184
  }
51484
53185
  if (typeof store.tasks.unlock !== "function")
51485
53186
  return error(501, "task unlocking is not supported by this storage backend");
@@ -51487,7 +53188,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51487
53188
  if (!principal.scopes.includes("todos:*"))
51488
53189
  return error(403, "force unlock requires todos:* scope");
51489
53190
  const released2 = await store.tasks.unlock(id);
51490
- return json3({ success: released2 });
53191
+ return json4({ success: released2 });
51491
53192
  }
51492
53193
  if (body2.agent_id && body2.agent_id !== principal.agent && !principal.scopes.includes("todos:*")) {
51493
53194
  return error(403, "unlock agent_id must match the authenticated agent");
@@ -51496,7 +53197,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51496
53197
  if (!agentId2)
51497
53198
  return error(403, "unlock requires an agent-bound key or force=true");
51498
53199
  const released = await store.tasks.unlock(id, agentId2);
51499
- return json3({ success: released });
53200
+ return json4({ success: released });
51500
53201
  }
51501
53202
  if (action === "dependencies") {
51502
53203
  if (!store.dependencies)
@@ -51505,16 +53206,16 @@ async function handleV1Request(req, url, dependencies = {}) {
51505
53206
  if (!await store.tasks.get(id))
51506
53207
  return error(404, "task not found");
51507
53208
  const edges = await store.dependencies.list(id);
51508
- return json3(edges);
53209
+ return json4(edges);
51509
53210
  }
51510
53211
  if (method === "POST") {
51511
- const body2 = await readJson2(req) ?? {};
53212
+ const body2 = await readJson3(req) ?? {};
51512
53213
  if (typeof body2.depends_on !== "string" || !body2.depends_on.trim()) {
51513
53214
  return error(400, "depends_on is required");
51514
53215
  }
51515
53216
  try {
51516
53217
  const dependency = await store.dependencies.add(id, body2.depends_on, contextFromPrincipal(principal));
51517
- return json3({ dependency }, 201);
53218
+ return json4({ dependency }, 201);
51518
53219
  } catch (e) {
51519
53220
  const msg = e.message || "";
51520
53221
  if (msg.includes("not found"))
@@ -51528,7 +53229,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51528
53229
  if (!subId)
51529
53230
  return error(400, "dependency target id is required (/v1/tasks/:id/dependencies/:dep)");
51530
53231
  const removed = await store.dependencies.remove(id, subId);
51531
- return json3({ removed });
53232
+ return json4({ removed });
51532
53233
  }
51533
53234
  return error(405, `method ${method} not allowed on /v1/tasks/:id/dependencies`);
51534
53235
  }
@@ -51539,10 +53240,10 @@ async function handleV1Request(req, url, dependencies = {}) {
51539
53240
  if (!await store.tasks.get(id))
51540
53241
  return error(404, "task not found");
51541
53242
  const verifications = await store.verifications.list(id);
51542
- return json3({ verifications, count: verifications.length });
53243
+ return json4({ verifications, count: verifications.length });
51543
53244
  }
51544
53245
  if (method === "POST") {
51545
- const body2 = await readJson2(req) ?? {};
53246
+ const body2 = await readJson3(req) ?? {};
51546
53247
  if (typeof body2.command !== "string" || !body2.command.trim()) {
51547
53248
  return error(400, "command is required");
51548
53249
  }
@@ -51555,7 +53256,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51555
53256
  artifact_path: body2.artifact_path,
51556
53257
  agent_id: body2.agent_id
51557
53258
  }, contextFromPrincipal(principal, body2));
51558
- return json3({ verification }, 201);
53259
+ return json4({ verification }, 201);
51559
53260
  } catch (e) {
51560
53261
  const msg = e.message || "";
51561
53262
  if (msg.includes("not found"))
@@ -51572,10 +53273,10 @@ async function handleV1Request(req, url, dependencies = {}) {
51572
53273
  if (!await store.tasks.get(id))
51573
53274
  return error(404, "task not found");
51574
53275
  const commits = await store.commits.list(id);
51575
- return json3({ commits, count: commits.length });
53276
+ return json4({ commits, count: commits.length });
51576
53277
  }
51577
53278
  if (method === "POST") {
51578
- const body2 = await readJson2(req) ?? {};
53279
+ const body2 = await readJson3(req) ?? {};
51579
53280
  if (typeof body2.sha !== "string" || !body2.sha.trim())
51580
53281
  return error(400, "sha is required");
51581
53282
  try {
@@ -51586,7 +53287,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51586
53287
  author: body2.author,
51587
53288
  files_changed: Array.isArray(body2.files_changed) ? body2.files_changed : undefined
51588
53289
  }, contextFromPrincipal(principal));
51589
- return json3({ commit }, 201);
53290
+ return json4({ commit }, 201);
51590
53291
  } catch (e) {
51591
53292
  const msg = e.message || "";
51592
53293
  if (msg.includes("not found"))
@@ -51603,10 +53304,10 @@ async function handleV1Request(req, url, dependencies = {}) {
51603
53304
  if (!await store.tasks.get(id))
51604
53305
  return error(404, "task not found");
51605
53306
  const refs = await store.gitRefs.list(id);
51606
- return json3({ refs, count: refs.length });
53307
+ return json4({ refs, count: refs.length });
51607
53308
  }
51608
53309
  if (method === "POST") {
51609
- const body2 = await readJson2(req) ?? {};
53310
+ const body2 = await readJson3(req) ?? {};
51610
53311
  const refType = body2.ref_type === "pull_request" || body2.ref_type === "branch" ? body2.ref_type : "branch";
51611
53312
  if (typeof body2.name !== "string" || !body2.name.trim())
51612
53313
  return error(400, "name is required");
@@ -51619,7 +53320,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51619
53320
  provider: body2.provider,
51620
53321
  metadata: body2.metadata
51621
53322
  }, contextFromPrincipal(principal));
51622
- return json3({ ref }, 201);
53323
+ return json4({ ref }, 201);
51623
53324
  } catch (e) {
51624
53325
  const msg = e.message || "";
51625
53326
  if (msg.includes("not found"))
@@ -51635,21 +53336,21 @@ async function handleV1Request(req, url, dependencies = {}) {
51635
53336
  const body = actionJson.value && typeof actionJson.value === "object" && !Array.isArray(actionJson.value) ? actionJson.value : {};
51636
53337
  const agentId = typeof body.agent_id === "string" ? body.agent_id : principal.agent || "todos-serve";
51637
53338
  if (action === "start" && method === "POST") {
51638
- return json3({ task: await store.tasks.start(id, agentId) });
53339
+ return json4({ task: await store.tasks.start(id, agentId) });
51639
53340
  }
51640
53341
  if (action === "complete" && method === "POST") {
51641
53342
  const parsed = validateTaskCompletion(actionJson.value);
51642
53343
  if (!parsed.ok)
51643
53344
  return error(400, parsed.message);
51644
- return json3({
53345
+ return json4({
51645
53346
  task: await store.tasks.complete(id, parsed.agentId || principal.agent || "todos-serve", parsed.options, contextFromPrincipal(principal, body))
51646
53347
  });
51647
53348
  }
51648
53349
  if (action === "fail" && method === "POST") {
51649
- 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", {}) });
51650
53351
  }
51651
53352
  if (action === "claim" && method === "POST") {
51652
- return json3({ task: await store.tasks.claimNext(agentId, {}) });
53353
+ return json4({ task: await store.tasks.claimNext(agentId, {}) });
51653
53354
  }
51654
53355
  return error(404, `unknown task action: ${action}`);
51655
53356
  }
@@ -51672,10 +53373,10 @@ async function handleV1Request(req, url, dependencies = {}) {
51672
53373
  throw e;
51673
53374
  }
51674
53375
  }
51675
- return task2 ? json3({ task: task2 }) : error(404, "task not found");
53376
+ return task2 ? json4({ task: task2 }) : error(404, "task not found");
51676
53377
  }
51677
53378
  if (method === "PATCH" || method === "PUT") {
51678
- const body = await readJson2(req);
53379
+ const body = await readJson3(req);
51679
53380
  if (!body)
51680
53381
  return error(400, "invalid JSON body");
51681
53382
  const current = await store.tasks.get(id);
@@ -51687,7 +53388,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51687
53388
  };
51688
53389
  try {
51689
53390
  const task2 = await store.tasks.update(id, patch);
51690
- return task2 ? json3({ task: task2 }) : error(404, "task not found");
53391
+ return task2 ? json4({ task: task2 }) : error(404, "task not found");
51691
53392
  } catch (e) {
51692
53393
  const msg = e.message || "";
51693
53394
  if (msg.includes("version conflict"))
@@ -51697,7 +53398,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51697
53398
  }
51698
53399
  if (method === "DELETE") {
51699
53400
  await store.tasks.delete(id, contextFromPrincipal(principal));
51700
- return json3({ deleted: true, id });
53401
+ return json4({ deleted: true, id });
51701
53402
  }
51702
53403
  return error(405, `method ${method} not allowed on /v1/tasks/:id`);
51703
53404
  }
@@ -51705,24 +53406,24 @@ async function handleV1Request(req, url, dependencies = {}) {
51705
53406
  if (!id) {
51706
53407
  if (method === "GET") {
51707
53408
  const projects = await store.projects.list();
51708
- return json3({ projects, count: projects.length });
53409
+ return json4({ projects, count: projects.length });
51709
53410
  }
51710
53411
  if (method === "POST") {
51711
- const body = await readJson2(req);
53412
+ const body = await readJson3(req);
51712
53413
  if (!body)
51713
53414
  return error(400, "invalid JSON body");
51714
53415
  const validated = validateProjectCreate(body);
51715
53416
  if (!validated.ok)
51716
53417
  return error(400, validated.message);
51717
53418
  const project = await store.projects.create(validated.input, contextFromPrincipal(principal));
51718
- return json3({ project }, 201);
53419
+ return json4({ project }, 201);
51719
53420
  }
51720
53421
  return error(405, `method ${method} not allowed on /v1/projects`);
51721
53422
  }
51722
53423
  if (action === "rename") {
51723
53424
  if (method !== "POST")
51724
53425
  return error(405, `method ${method} not allowed on /v1/projects/:id/rename`);
51725
- const body = await readJson2(req);
53426
+ const body = await readJson3(req);
51726
53427
  if (!body || typeof body.new_slug !== "string" || !body.new_slug.trim() || !normalizeSlug(body.new_slug)) {
51727
53428
  return error(400, "new_slug must be a non-empty string");
51728
53429
  }
@@ -51732,14 +53433,14 @@ async function handleV1Request(req, url, dependencies = {}) {
51732
53433
  const unknownField = Object.keys(body).find((key) => !["new_slug", "name"].includes(key));
51733
53434
  if (unknownField)
51734
53435
  return error(400, `unknown project rename field: ${unknownField}`);
51735
- return json3(await store.projects.rename(id, body, contextFromPrincipal(principal)));
53436
+ return json4(await store.projects.rename(id, body, contextFromPrincipal(principal)));
51736
53437
  }
51737
53438
  if (method === "GET") {
51738
53439
  const project = await store.projects.get(id);
51739
- return project ? json3({ project }) : error(404, "project not found");
53440
+ return project ? json4({ project }) : error(404, "project not found");
51740
53441
  }
51741
53442
  if (method === "PATCH" || method === "PUT") {
51742
- const body = await readJson2(req);
53443
+ const body = await readJson3(req);
51743
53444
  if (!body)
51744
53445
  return error(400, "invalid JSON body");
51745
53446
  const validated = validateProjectPatch(body);
@@ -51748,21 +53449,21 @@ async function handleV1Request(req, url, dependencies = {}) {
51748
53449
  if (!await store.projects.get(id))
51749
53450
  return error(404, "project not found");
51750
53451
  const project = await store.projects.update(id, validated.patch);
51751
- return json3({ project });
53452
+ return json4({ project });
51752
53453
  }
51753
53454
  if (method === "DELETE") {
51754
53455
  await store.projects.delete(id, contextFromPrincipal(principal));
51755
- return json3({ deleted: true, id });
53456
+ return json4({ deleted: true, id });
51756
53457
  }
51757
53458
  return error(405, `method ${method} not allowed on /v1/projects/:id`);
51758
53459
  }
51759
53460
  if (resource === "plans") {
51760
53461
  if (!id && method === "GET") {
51761
53462
  const plans = await store.plans.list(url.searchParams.get("project_id") ?? undefined);
51762
- return json3({ plans, count: plans.length });
53463
+ return json4({ plans, count: plans.length });
51763
53464
  }
51764
53465
  if (!id && method === "POST") {
51765
- const body = await readJson2(req);
53466
+ const body = await readJson3(req);
51766
53467
  const validated = validatePlanCreate(body);
51767
53468
  if (!validated.ok)
51768
53469
  return error(400, validated.message);
@@ -51777,14 +53478,14 @@ async function handleV1Request(req, url, dependencies = {}) {
51777
53478
  }
51778
53479
  }
51779
53480
  const plan = await store.plans.create(validated.input, contextFromPrincipal(principal, validated.input));
51780
- return json3({ plan }, 201);
53481
+ return json4({ plan }, 201);
51781
53482
  }
51782
53483
  if (id && method === "GET") {
51783
53484
  const plan = await store.plans.get(id);
51784
- return plan ? json3({ plan }) : error(404, "plan not found");
53485
+ return plan ? json4({ plan }) : error(404, "plan not found");
51785
53486
  }
51786
53487
  if (id && (method === "PATCH" || method === "PUT")) {
51787
- const body = await readJson2(req);
53488
+ const body = await readJson3(req);
51788
53489
  if (!body || Object.keys(body).length === 0)
51789
53490
  return error(400, "plan patch is required");
51790
53491
  const allowed = new Set(["name", "slug", "description", "status", "task_list_id", "agent_id"]);
@@ -51821,12 +53522,12 @@ async function handleV1Request(req, url, dependencies = {}) {
51821
53522
  }
51822
53523
  }
51823
53524
  const plan = await store.plans.update(id, body);
51824
- return json3({ plan });
53525
+ return json4({ plan });
51825
53526
  }
51826
53527
  if (id && method === "DELETE") {
51827
53528
  if (!await store.plans.delete(id, contextFromPrincipal(principal)))
51828
53529
  return error(404, "plan not found");
51829
- return json3({ deleted: true, id });
53530
+ return json4({ deleted: true, id });
51830
53531
  }
51831
53532
  if (id)
51832
53533
  return error(405, `method ${method} not allowed on /v1/plans/:id`);
@@ -51835,50 +53536,50 @@ async function handleV1Request(req, url, dependencies = {}) {
51835
53536
  if (!id && method === "GET") {
51836
53537
  const projectId = url.searchParams.get("project_id");
51837
53538
  const templates = (await store.templates.list()).filter((template) => projectId === null || template.project_id === projectId);
51838
- return json3({ templates, count: templates.length });
53539
+ return json4({ templates, count: templates.length });
51839
53540
  }
51840
53541
  if (!id && method === "POST") {
51841
- const body = await readJson2(req);
53542
+ const body = await readJson3(req);
51842
53543
  const validated = validateTemplateCreate(body);
51843
53544
  if (!validated.ok)
51844
53545
  return error(400, validated.message);
51845
53546
  const template = await store.templates.create(validated.input, contextFromPrincipal(principal));
51846
- return json3({ template: await store.templates.getWithTasks(template.id) }, 201);
53547
+ return json4({ template: await store.templates.getWithTasks(template.id) }, 201);
51847
53548
  }
51848
53549
  if (!id)
51849
53550
  return error(405, `method ${method} not allowed on /v1/templates`);
51850
53551
  if (method === "GET") {
51851
53552
  const template = await store.templates.getWithTasks(id);
51852
- return template ? json3({ template }) : error(404, "template not found");
53553
+ return template ? json4({ template }) : error(404, "template not found");
51853
53554
  }
51854
53555
  if (method === "PATCH" || method === "PUT") {
51855
- const body = await readJson2(req);
53556
+ const body = await readJson3(req);
51856
53557
  const validated = validateTemplatePatch(body);
51857
53558
  if (!validated.ok)
51858
53559
  return error(400, validated.message);
51859
53560
  const template = await store.templates.update(id, validated.patch, contextFromPrincipal(principal));
51860
- 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");
51861
53562
  }
51862
53563
  if (method === "DELETE") {
51863
53564
  const deleted = await store.templates.delete(id, contextFromPrincipal(principal));
51864
- return deleted ? json3({ deleted: true, id }) : error(404, "template not found");
53565
+ return deleted ? json4({ deleted: true, id }) : error(404, "template not found");
51865
53566
  }
51866
53567
  return error(405, `method ${method} not allowed on /v1/templates/:id`);
51867
53568
  }
51868
53569
  if (resource === "agents") {
51869
53570
  if (!id && method === "GET") {
51870
53571
  const agents = await store.agents.list();
51871
- return json3({ agents, count: agents.length });
53572
+ return json4({ agents, count: agents.length });
51872
53573
  }
51873
53574
  if (!id && method === "POST") {
51874
- const body = await readJson2(req);
53575
+ const body = await readJson3(req);
51875
53576
  if (!body || typeof body.name !== "string" || !body.name.trim())
51876
53577
  return error(400, "name is required");
51877
53578
  const result = await store.agents.register(body, contextFromPrincipal(principal));
51878
53579
  if (result && typeof result === "object" && "conflict" in result) {
51879
53580
  return error(409, result.message ?? "agent name conflict", { conflict: true });
51880
53581
  }
51881
- return json3({ agent: result }, 201);
53582
+ return json4({ agent: result }, 201);
51882
53583
  }
51883
53584
  if (id && action === "heartbeat") {
51884
53585
  if (method !== "POST")
@@ -51887,7 +53588,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51887
53588
  return error(501, "agent heartbeat is not supported by this storage backend");
51888
53589
  }
51889
53590
  const agent = await store.agents.heartbeat(id, contextFromPrincipal(principal));
51890
- return agent ? json3({ agent }) : error(404, "agent not found");
53591
+ return agent ? json4({ agent }) : error(404, "agent not found");
51891
53592
  }
51892
53593
  if (id && action === "release") {
51893
53594
  if (method !== "POST")
@@ -51895,18 +53596,18 @@ async function handleV1Request(req, url, dependencies = {}) {
51895
53596
  if (typeof store.agents.release !== "function") {
51896
53597
  return error(501, "agent release is not supported by this storage backend");
51897
53598
  }
51898
- const body = await readJson2(req) ?? {};
53599
+ const body = await readJson3(req) ?? {};
51899
53600
  const result = await store.agents.release(id, body.session_id, contextFromPrincipal(principal));
51900
53601
  if (!result)
51901
53602
  return error(404, "agent not found");
51902
53603
  if (!result.released) {
51903
53604
  return error(409, "release denied: session_id does not match agent's current session", { released: false });
51904
53605
  }
51905
- return json3({ agent: result.agent, released: true });
53606
+ return json4({ agent: result.agent, released: true });
51906
53607
  }
51907
53608
  if (id && method === "GET") {
51908
53609
  const agent = await store.agents.get(id);
51909
- return agent ? json3({ agent }) : error(404, "agent not found");
53610
+ return agent ? json4({ agent }) : error(404, "agent not found");
51910
53611
  }
51911
53612
  }
51912
53613
  if (resource === "activity" && !id) {
@@ -51915,16 +53616,16 @@ async function handleV1Request(req, url, dependencies = {}) {
51915
53616
  const limitParam = url.searchParams.get("limit");
51916
53617
  const limit = limitParam ? Math.max(1, Math.min(1e4, Number(limitParam) || 50)) : 50;
51917
53618
  const activity = await store.audit.getRecentActivity(limit);
51918
- return json3({ activity, count: activity.length });
53619
+ return json4({ activity, count: activity.length });
51919
53620
  }
51920
53621
  if (resource === "task-lists") {
51921
53622
  if (!id && method === "GET") {
51922
53623
  const projectId = url.searchParams.get("project_id") ?? undefined;
51923
53624
  const taskLists = await store.taskLists.list(projectId);
51924
- return json3({ task_lists: taskLists, count: taskLists.length });
53625
+ return json4({ task_lists: taskLists, count: taskLists.length });
51925
53626
  }
51926
53627
  if (!id && method === "POST") {
51927
- const body = await readJson2(req);
53628
+ const body = await readJson3(req);
51928
53629
  if (!body || typeof body.name !== "string" || !body.name.trim())
51929
53630
  return error(400, "name is required");
51930
53631
  const unknownField = Object.keys(body).find((key) => !["name", "slug", "project_id", "description", "metadata"].includes(key));
@@ -51943,14 +53644,14 @@ async function handleV1Request(req, url, dependencies = {}) {
51943
53644
  return error(400, "task-list slug must be non-empty kebab-case");
51944
53645
  }
51945
53646
  const taskList = await store.taskLists.create(body, contextFromPrincipal(principal));
51946
- return json3({ task_list: taskList }, 201);
53647
+ return json4({ task_list: taskList }, 201);
51947
53648
  }
51948
53649
  if (id && method === "GET") {
51949
53650
  const taskList = await store.taskLists.get(id);
51950
- 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");
51951
53652
  }
51952
53653
  if (id && (method === "PATCH" || method === "PUT")) {
51953
- const body = await readJson2(req);
53654
+ const body = await readJson3(req);
51954
53655
  if (!body)
51955
53656
  return error(400, "invalid JSON body");
51956
53657
  const unknownField = Object.keys(body).find((key) => !["slug", "name", "description", "metadata"].includes(key));
@@ -51970,11 +53671,11 @@ async function handleV1Request(req, url, dependencies = {}) {
51970
53671
  if (!await store.taskLists.get(id))
51971
53672
  return error(404, "task list not found");
51972
53673
  const taskList = await store.taskLists.update(id, body);
51973
- return json3({ task_list: taskList });
53674
+ return json4({ task_list: taskList });
51974
53675
  }
51975
53676
  if (id && method === "DELETE") {
51976
53677
  const deleted = await store.taskLists.delete(id, contextFromPrincipal(principal));
51977
- 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");
51978
53679
  }
51979
53680
  return error(405, `method ${method} not allowed on /v1/task-lists${id ? "/:id" : ""}`);
51980
53681
  }
@@ -51985,7 +53686,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51985
53686
  return error(501, "dependency edge listing is not supported by this storage backend");
51986
53687
  }
51987
53688
  const dependencies2 = await store.dependencies.listAll();
51988
- return json3({ dependencies: dependencies2, count: dependencies2.length });
53689
+ return json4({ dependencies: dependencies2, count: dependencies2.length });
51989
53690
  }
51990
53691
  if (resource === "commits" && id) {
51991
53692
  if (method !== "GET")
@@ -51993,7 +53694,7 @@ async function handleV1Request(req, url, dependencies = {}) {
51993
53694
  if (!store.commits)
51994
53695
  return error(501, "commit links are not supported by this storage backend");
51995
53696
  const commit = await store.commits.find(id);
51996
- return json3({ commit: commit ?? null });
53697
+ return json4({ commit: commit ?? null });
51997
53698
  }
51998
53699
  if (resource === "refs" && id) {
51999
53700
  if (method !== "GET")
@@ -52007,7 +53708,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52007
53708
  return error(400, "ref path segment has invalid percent encoding");
52008
53709
  }
52009
53710
  const refs = await store.gitRefs.find(decodedRef);
52010
- return json3({ refs, count: refs.length });
53711
+ return json4({ refs, count: refs.length });
52011
53712
  }
52012
53713
  if (resource === "next" && !id) {
52013
53714
  if (method !== "GET")
@@ -52019,7 +53720,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52019
53720
  ...url.searchParams.get("plan_id") ? { plan_id: url.searchParams.get("plan_id") } : {}
52020
53721
  };
52021
53722
  const task2 = await store.tasks.getNext(agent, filters);
52022
- return json3({ task: task2 ?? null });
53723
+ return json4({ task: task2 ?? null });
52023
53724
  }
52024
53725
  if (resource === "stats" && method === "GET") {
52025
53726
  const [tasks, tasksAll, projects] = await Promise.all([
@@ -52027,7 +53728,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52027
53728
  store.tasks.count({ include_subtasks: true }),
52028
53729
  store.projects.list()
52029
53730
  ]);
52030
- 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 });
52031
53732
  }
52032
53733
  if (resource === "integrity" && !id) {
52033
53734
  if (method !== "GET")
@@ -52036,7 +53737,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52036
53737
  return error(501, "referential-integrity reporting is not supported by this storage backend");
52037
53738
  }
52038
53739
  const integrity = await store.integrity.report();
52039
- return json3({ integrity });
53740
+ return json4({ integrity });
52040
53741
  }
52041
53742
  if (resource === "import") {
52042
53743
  if (method !== "POST")
@@ -52044,7 +53745,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52044
53745
  if (typeof store.sync.importSnapshot !== "function") {
52045
53746
  return error(501, "snapshot import is not supported by this storage backend");
52046
53747
  }
52047
- const raw = await readJson2(req);
53748
+ const raw = await readJson3(req);
52048
53749
  if (raw === null)
52049
53750
  return error(400, "invalid JSON body");
52050
53751
  const snapshot = normalizeImportSnapshot(raw);
@@ -52053,7 +53754,7 @@ async function handleV1Request(req, url, dependencies = {}) {
52053
53754
  return error(400, "empty snapshot: provide at least one record array (tasks/projects/plans/...)");
52054
53755
  }
52055
53756
  const result = await store.sync.importSnapshot(snapshot, contextFromPrincipal(principal));
52056
- return json3({ result, received });
53757
+ return json4({ result, received });
52057
53758
  }
52058
53759
  return error(404, `unknown /v1 resource: ${resource ?? "(root)"}`);
52059
53760
  } catch (e) {
@@ -52076,13 +53777,14 @@ async function handleV1Request(req, url, dependencies = {}) {
52076
53777
  return error(500, e.message || "internal error");
52077
53778
  }
52078
53779
  }
52079
- 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;
52080
53781
  var init_v1 = __esm(() => {
52081
53782
  init_types();
52082
53783
  init_cloud();
52083
53784
  init_pr_groups();
53785
+ init_project_registration();
52084
53786
  init_redaction();
52085
- JSON_HEADERS2 = { "Content-Type": "application/json" };
53787
+ JSON_HEADERS3 = { "Content-Type": "application/json" };
52086
53788
  });
52087
53789
 
52088
53790
  // src/pr-groups/sqlite.ts
@@ -52241,12 +53943,12 @@ class SqlitePrGroupLedgerPersistence {
52241
53943
  this.tx = new SqlitePrGroupTransaction(db);
52242
53944
  }
52243
53945
  async transaction(fn) {
52244
- const previous = sqliteTransactionTails.get(this.db) ?? Promise.resolve();
53946
+ const previous = sqliteTransactionTails2.get(this.db) ?? Promise.resolve();
52245
53947
  let release;
52246
53948
  const current = new Promise((resolve17) => {
52247
53949
  release = resolve17;
52248
53950
  });
52249
- sqliteTransactionTails.set(this.db, current);
53951
+ sqliteTransactionTails2.set(this.db, current);
52250
53952
  await previous;
52251
53953
  try {
52252
53954
  this.db.exec("BEGIN IMMEDIATE");
@@ -52260,8 +53962,8 @@ class SqlitePrGroupLedgerPersistence {
52260
53962
  throw error2;
52261
53963
  } finally {
52262
53964
  release();
52263
- if (sqliteTransactionTails.get(this.db) === current)
52264
- sqliteTransactionTails.delete(this.db);
53965
+ if (sqliteTransactionTails2.get(this.db) === current)
53966
+ sqliteTransactionTails2.delete(this.db);
52265
53967
  }
52266
53968
  }
52267
53969
  getGroup(id) {
@@ -52291,9 +53993,9 @@ class SqlitePrGroupLedgerPersistence {
52291
53993
  return row ? eventFromRow2(row) : null;
52292
53994
  }
52293
53995
  }
52294
- var sqliteTransactionTails;
52295
- var init_sqlite = __esm(() => {
52296
- sqliteTransactionTails = new WeakMap;
53996
+ var sqliteTransactionTails2;
53997
+ var init_sqlite2 = __esm(() => {
53998
+ sqliteTransactionTails2 = new WeakMap;
52297
53999
  });
52298
54000
 
52299
54001
  // src/pr-groups/index.ts
@@ -52323,10 +54025,10 @@ function createLocalPrGroupLedger(db = getDatabase()) {
52323
54025
  var init_pr_groups2 = __esm(() => {
52324
54026
  init_database();
52325
54027
  init_ledger();
52326
- init_sqlite();
54028
+ init_sqlite2();
52327
54029
  init_types3();
52328
54030
  init_ledger();
52329
- init_sqlite();
54031
+ init_sqlite2();
52330
54032
  init_http_client();
52331
54033
  init_postgres();
52332
54034
  });
@@ -52337,7 +54039,7 @@ __export(exports_serve, {
52337
54039
  taskToSummary: () => taskToSummary,
52338
54040
  startServer: () => startServer,
52339
54041
  serveStaticFile: () => serveStaticFile,
52340
- json: () => json,
54042
+ json: () => json2,
52341
54043
  checkAuth: () => checkAuth,
52342
54044
  SECURITY_HEADERS: () => SECURITY_HEADERS,
52343
54045
  MIME_TYPES: () => MIME_TYPES
@@ -52423,7 +54125,7 @@ function checkRateLimit(ip) {
52423
54125
  }
52424
54126
  return { allowed: true };
52425
54127
  }
52426
- function json(data, status = 200, headers) {
54128
+ function json2(data, status = 200, headers) {
52427
54129
  return new Response(JSON.stringify(data), {
52428
54130
  status,
52429
54131
  headers: {
@@ -52568,7 +54270,7 @@ Dashboard not found at: ${dashboardDir}`);
52568
54270
  "Access-Control-Allow-Headers": "Content-Type, X-API-Key, Authorization",
52569
54271
  Vary: "Origin"
52570
54272
  } : undefined;
52571
- const jsonWithCors = (data, status = 200) => json(data, status, corsHeaders);
54273
+ const jsonWithCors = (data, status = 200) => json2(data, status, corsHeaders);
52572
54274
  if (method === "OPTIONS") {
52573
54275
  return new Response(null, {
52574
54276
  headers: corsHeaders || {
@@ -52649,13 +54351,13 @@ Dashboard not found at: ${dashboardDir}`);
52649
54351
  return res;
52650
54352
  }
52651
54353
  if (path === "/api/health" && method === "GET") {
52652
- return handleHealth(ctx, json);
54354
+ return handleHealth(ctx, json2);
52653
54355
  }
52654
54356
  if (path === "/api/headless" && method === "GET") {
52655
- return handleHeadlessBoundary(ctx, json);
54357
+ return handleHeadlessBoundary(ctx, json2);
52656
54358
  }
52657
54359
  if (path === "/api/stats" && method === "GET") {
52658
- return handleStats(ctx, json);
54360
+ return handleStats(ctx, json2);
52659
54361
  }
52660
54362
  if (path === "/api/tasks" && method === "GET") {
52661
54363
  return handleListTasks(req, url, ctx, jsonWithCors, taskToSummary);
@@ -52670,16 +54372,16 @@ Dashboard not found at: ${dashboardDir}`);
52670
54372
  return handleTasksExport(req, url, ctx, jsonWithCors, taskToSummary);
52671
54373
  }
52672
54374
  if (path === "/api/tasks/bulk" && method === "POST") {
52673
- return handleTasksBulk(req, ctx, json);
54375
+ return handleTasksBulk(req, ctx, json2);
52674
54376
  }
52675
54377
  if (path === "/api/tasks/status" && method === "GET") {
52676
- return handleTasksStatus(req, url, ctx, json);
54378
+ return handleTasksStatus(req, url, ctx, json2);
52677
54379
  }
52678
54380
  if (path === "/api/tasks/next" && method === "GET") {
52679
54381
  return handleTasksNext(req, url, ctx, jsonWithCors, taskToSummary);
52680
54382
  }
52681
54383
  if (path === "/api/tasks/active" && method === "GET") {
52682
- return handleTasksActive(req, url, ctx, json);
54384
+ return handleTasksActive(req, url, ctx, json2);
52683
54385
  }
52684
54386
  if (path === "/api/tasks/stale" && method === "GET") {
52685
54387
  return handleTasksStale(req, url, ctx, jsonWithCors, taskToSummary);
@@ -52692,11 +54394,11 @@ Dashboard not found at: ${dashboardDir}`);
52692
54394
  }
52693
54395
  const attachmentsMatch = path.match(/^\/api\/tasks\/([^/]+)\/attachments$/);
52694
54396
  if (attachmentsMatch && method === "GET") {
52695
- return handleTaskAttachments(attachmentsMatch[1], ctx, json);
54397
+ return handleTaskAttachments(attachmentsMatch[1], ctx, json2);
52696
54398
  }
52697
54399
  const progressMatch = path.match(/^\/api\/tasks\/([^/]+)\/progress$/);
52698
54400
  if (progressMatch) {
52699
- const res = await handleTaskProgress(progressMatch[1], req, method, ctx, json, url);
54401
+ const res = await handleTaskProgress(progressMatch[1], req, method, ctx, json2, url);
52700
54402
  if (res !== null)
52701
54403
  return res;
52702
54404
  }
@@ -52710,7 +54412,7 @@ Dashboard not found at: ${dashboardDir}`);
52710
54412
  return handlePatchTask(id, req, ctx, jsonWithCors, taskToSummary);
52711
54413
  }
52712
54414
  if (method === "DELETE") {
52713
- return handleDeleteTask(id, ctx, json);
54415
+ return handleDeleteTask(id, ctx, json2);
52714
54416
  }
52715
54417
  }
52716
54418
  const startMatch = path.match(/^\/api\/tasks\/([^/]+)\/start$/);
@@ -52726,7 +54428,7 @@ Dashboard not found at: ${dashboardDir}`);
52726
54428
  return handleCompleteTask(completeMatch[1], ctx, jsonWithCors, taskToSummary);
52727
54429
  }
52728
54430
  if (path === "/api/projects" && method === "GET") {
52729
- return handleListProjects(url, ctx, json);
54431
+ return handleListProjects(url, ctx, json2);
52730
54432
  }
52731
54433
  if (path === "/api/agents/me" && method === "GET") {
52732
54434
  return handleAgentMe(req, url, ctx, jsonWithCors, taskToSummary);
@@ -52739,92 +54441,92 @@ Dashboard not found at: ${dashboardDir}`);
52739
54441
  return handleClaimTask(req, ctx, jsonWithCors, taskToSummary);
52740
54442
  }
52741
54443
  if (path === "/api/orgs" && method === "GET") {
52742
- return handleListOrgs(ctx, json);
54444
+ return handleListOrgs(ctx, json2);
52743
54445
  }
52744
54446
  if (path === "/api/orgs" && method === "POST") {
52745
- return handleCreateOrg(req, ctx, json);
54447
+ return handleCreateOrg(req, ctx, json2);
52746
54448
  }
52747
54449
  const orgMatch = path.match(/^\/api\/orgs\/([^/]+)$/);
52748
54450
  if (orgMatch && method === "PATCH") {
52749
- return handleUpdateOrg(orgMatch[1], req, ctx, json);
54451
+ return handleUpdateOrg(orgMatch[1], req, ctx, json2);
52750
54452
  }
52751
54453
  if (orgMatch && method === "DELETE") {
52752
- return handleDeleteOrg(orgMatch[1], ctx, json);
54454
+ return handleDeleteOrg(orgMatch[1], ctx, json2);
52753
54455
  }
52754
54456
  if (path === "/api/org" && method === "GET") {
52755
- return handleOrgChart(ctx, json);
54457
+ return handleOrgChart(ctx, json2);
52756
54458
  }
52757
54459
  const teamMatch = path.match(/^\/api\/agents\/([^/]+)\/team$/);
52758
54460
  if (teamMatch && method === "GET") {
52759
- return handleAgentTeam(teamMatch[1], ctx, json);
54461
+ return handleAgentTeam(teamMatch[1], ctx, json2);
52760
54462
  }
52761
54463
  if (path === "/api/agents" && method === "GET") {
52762
- return handleListAgents(url, ctx, json);
54464
+ return handleListAgents(url, ctx, json2);
52763
54465
  }
52764
54466
  if (path === "/api/projects" && method === "POST") {
52765
- return handleCreateProject(req, ctx, json);
54467
+ return handleCreateProject(req, ctx, json2);
52766
54468
  }
52767
54469
  const projectDeleteMatch = path.match(/^\/api\/projects\/([^/]+)$/);
52768
54470
  if (projectDeleteMatch && method === "DELETE") {
52769
- return handleDeleteProject(projectDeleteMatch[1], ctx, json);
54471
+ return handleDeleteProject(projectDeleteMatch[1], ctx, json2);
52770
54472
  }
52771
54473
  if (path === "/api/agents" && method === "POST") {
52772
- return handleRegisterAgent(req, ctx, json);
54474
+ return handleRegisterAgent(req, ctx, json2);
52773
54475
  }
52774
54476
  const agentMatch = path.match(/^\/api\/agents\/([^/]+)$/);
52775
54477
  if (agentMatch && method === "PATCH") {
52776
- return handleUpdateAgent(agentMatch[1], req, ctx, json);
54478
+ return handleUpdateAgent(agentMatch[1], req, ctx, json2);
52777
54479
  }
52778
54480
  if (agentMatch && method === "DELETE") {
52779
- return handleDeleteAgent(agentMatch[1], ctx, json);
54481
+ return handleDeleteAgent(agentMatch[1], ctx, json2);
52780
54482
  }
52781
54483
  if (path === "/api/agents/bulk" && method === "POST") {
52782
- return handleBulkDeleteAgents(req, ctx, json);
54484
+ return handleBulkDeleteAgents(req, ctx, json2);
52783
54485
  }
52784
54486
  if (path === "/api/projects/bulk" && method === "POST") {
52785
- return handleBulkDeleteProjects(req, ctx, json);
54487
+ return handleBulkDeleteProjects(req, ctx, json2);
52786
54488
  }
52787
54489
  if (path === "/api/doctor" && method === "GET") {
52788
- return handleDoctor(ctx, json);
54490
+ return handleDoctor(ctx, json2);
52789
54491
  }
52790
54492
  if (path === "/api/report" && method === "GET") {
52791
- return handleReport(req, url, ctx, json);
54493
+ return handleReport(req, url, ctx, json2);
52792
54494
  }
52793
54495
  if (path === "/api/activity" && method === "GET") {
52794
- return handleActivity(req, url, ctx, json);
54496
+ return handleActivity(req, url, ctx, json2);
52795
54497
  }
52796
54498
  const historyMatch = path.match(/^\/api\/tasks\/([^/]+)\/history$/);
52797
54499
  if (historyMatch && method === "GET") {
52798
- return handleTaskHistory(historyMatch[1], ctx, json, url);
54500
+ return handleTaskHistory(historyMatch[1], ctx, json2, url);
52799
54501
  }
52800
54502
  if (path === "/api/webhooks" && method === "GET") {
52801
- return handleListWebhooks(ctx, json);
54503
+ return handleListWebhooks(ctx, json2);
52802
54504
  }
52803
54505
  if (path === "/api/webhooks" && method === "POST") {
52804
- return handleCreateWebhook(req, ctx, json);
54506
+ return handleCreateWebhook(req, ctx, json2);
52805
54507
  }
52806
54508
  const webhookMatch = path.match(/^\/api\/webhooks\/([^/]+)$/);
52807
54509
  if (webhookMatch && method === "DELETE") {
52808
- return handleDeleteWebhook(webhookMatch[1], ctx, json);
54510
+ return handleDeleteWebhook(webhookMatch[1], ctx, json2);
52809
54511
  }
52810
54512
  if (path === "/api/templates" && method === "GET") {
52811
- return handleListTemplates(ctx, json);
54513
+ return handleListTemplates(ctx, json2);
52812
54514
  }
52813
54515
  if (path === "/api/templates" && method === "POST") {
52814
- return handleCreateTemplate(req, ctx, json);
54516
+ return handleCreateTemplate(req, ctx, json2);
52815
54517
  }
52816
54518
  const templateMatch = path.match(/^\/api\/templates\/([^/]+)$/);
52817
54519
  if (templateMatch && method === "DELETE") {
52818
- return handleDeleteTemplate(templateMatch[1], ctx, json);
54520
+ return handleDeleteTemplate(templateMatch[1], ctx, json2);
52819
54521
  }
52820
54522
  if (path === "/api/plans" && method === "GET") {
52821
- return handleListPlans(url, ctx, json);
54523
+ return handleListPlans(url, ctx, json2);
52822
54524
  }
52823
54525
  if (path === "/api/plans" && method === "POST") {
52824
- return handleCreatePlan(req, ctx, json);
54526
+ return handleCreatePlan(req, ctx, json2);
52825
54527
  }
52826
54528
  if (path === "/api/plans/bulk" && method === "POST") {
52827
- return handleBulkDeletePlans(req, ctx, json);
54529
+ return handleBulkDeletePlans(req, ctx, json2);
52828
54530
  }
52829
54531
  const planMatch = path.match(/^\/api\/plans\/([^/]+)$/);
52830
54532
  if (planMatch) {
@@ -52833,16 +54535,16 @@ Dashboard not found at: ${dashboardDir}`);
52833
54535
  return handleGetPlan(id, ctx, jsonWithCors, taskToSummary);
52834
54536
  }
52835
54537
  if (method === "PATCH") {
52836
- return handleUpdatePlan(id, req, ctx, json);
54538
+ return handleUpdatePlan(id, req, ctx, json2);
52837
54539
  }
52838
54540
  if (method === "DELETE") {
52839
- return handleDeletePlan(id, ctx, json);
54541
+ return handleDeletePlan(id, ctx, json2);
52840
54542
  }
52841
54543
  }
52842
54544
  const staticRes = handleStaticFiles(path, method, ctx, jsonWithCors, serveStaticFile);
52843
54545
  if (staticRes)
52844
54546
  return staticRes;
52845
- return json({ error: "Not found" }, 404);
54547
+ return json2({ error: "Not found" }, 404);
52846
54548
  }
52847
54549
  });
52848
54550
  const shutdown = () => {