@hasna/todos 0.15.13 → 0.15.15

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 (42) hide show
  1. package/dist/cli/cloud-router.d.ts.map +1 -1
  2. package/dist/cli/index.js +12117 -10745
  3. package/dist/contracts.js +1 -1
  4. package/dist/index.js +287 -53
  5. package/dist/mcp/index.js +2181 -810
  6. package/dist/mcp.js +1 -1
  7. package/dist/project-registration.js +12 -13
  8. package/dist/registry.js +1 -1
  9. package/dist/release-provenance.json +5 -5
  10. package/dist/sdk/index.d.ts +1 -1
  11. package/dist/sdk/index.d.ts.map +1 -1
  12. package/dist/sdk/index.js +7 -0
  13. package/dist/sdk/v1.generated.d.ts +23 -0
  14. package/dist/sdk/v1.generated.d.ts.map +1 -1
  15. package/dist/server/cloud.d.ts +3 -0
  16. package/dist/server/cloud.d.ts.map +1 -1
  17. package/dist/server/index.js +19661 -18290
  18. package/dist/server/openapi.d.ts +134 -0
  19. package/dist/server/openapi.d.ts.map +1 -1
  20. package/dist/server/v1.d.ts +2 -1
  21. package/dist/server/v1.d.ts.map +1 -1
  22. package/dist/storage.js +11 -12
  23. package/dist/task-manifest/authority.d.ts +3 -1
  24. package/dist/task-manifest/authority.d.ts.map +1 -1
  25. package/dist/task-manifest/backend.d.ts +18 -1
  26. package/dist/task-manifest/backend.d.ts.map +1 -1
  27. package/dist/task-manifest/http.d.ts +2 -1
  28. package/dist/task-manifest/http.d.ts.map +1 -1
  29. package/dist/task-manifest/index.d.ts +1 -1
  30. package/dist/task-manifest/index.d.ts.map +1 -1
  31. package/dist/task-manifest/postgres.d.ts +3 -1
  32. package/dist/task-manifest/postgres.d.ts.map +1 -1
  33. package/dist/task-manifest/schema-sql.d.ts +3 -1
  34. package/dist/task-manifest/schema-sql.d.ts.map +1 -1
  35. package/dist/task-manifest/schema.d.ts +2 -1
  36. package/dist/task-manifest/schema.d.ts.map +1 -1
  37. package/dist/task-manifest/sqlite.d.ts +4 -2
  38. package/dist/task-manifest/sqlite.d.ts.map +1 -1
  39. package/dist/task-manifest/types.d.ts +22 -1
  40. package/dist/task-manifest/types.d.ts.map +1 -1
  41. package/dist/task-manifest.js +275 -40
  42. package/package.json +1 -1
package/dist/mcp/index.js CHANGED
@@ -35415,7 +35415,7 @@ var package_default;
35415
35415
  var init_package = __esm(() => {
35416
35416
  package_default = {
35417
35417
  name: "@hasna/todos",
35418
- version: "0.15.13",
35418
+ version: "0.15.15",
35419
35419
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
35420
35420
  type: "module",
35421
35421
  main: "dist/index.js",
@@ -47421,21 +47421,21 @@ class PostgresJsonRecordStore {
47421
47421
  ), existing AS (
47422
47422
  SELECT payload FROM ${this.tableName}
47423
47423
  WHERE service = $1 AND object_type = 'plan_project_link_rollback_receipts'
47424
- AND object_id = $5 AND deleted_at IS NULL
47424
+ AND object_id = $4 AND deleted_at IS NULL
47425
47425
  FOR UPDATE
47426
47426
  ), checks AS (
47427
47427
  SELECT
47428
47428
  EXISTS (SELECT 1 FROM target_plan) AS plan_found,
47429
47429
  COALESCE((SELECT payload->>'updated_at' = $3 FROM target_plan), false) AS plan_revision_ok,
47430
- COALESCE((SELECT jsonb_object_agg(object_id, COALESCE(payload->'project_id', 'null'::jsonb) ORDER BY object_id) FROM member_tasks), '{}'::jsonb) = $7::jsonb
47431
- AND COALESCE((SELECT jsonb_agg(object_id ORDER BY object_id) FROM member_tasks), '[]'::jsonb) = $8::jsonb AS membership_ok,
47430
+ COALESCE((SELECT jsonb_object_agg(object_id, COALESCE(payload->'project_id', 'null'::jsonb) ORDER BY object_id) FROM member_tasks), '{}'::jsonb) = $6::jsonb
47431
+ AND COALESCE((SELECT jsonb_agg(object_id ORDER BY object_id) FROM member_tasks), '[]'::jsonb) = $7::jsonb AS membership_ok,
47432
47432
  EXISTS (SELECT 1 FROM existing) AS has_existing
47433
47433
  ), updated_plan AS (
47434
47434
  UPDATE ${this.tableName} r SET
47435
- payload = r.payload || jsonb_build_object('project_id', $9::jsonb, 'updated_at', $10::text),
47436
- updated_at = $10::timestamptz,
47435
+ payload = r.payload || jsonb_build_object('project_id', $8::jsonb, 'updated_at', $9::text),
47436
+ updated_at = $9::timestamptz,
47437
47437
  version = COALESCE(r.version, 0) + 1,
47438
- source_machine_id = COALESCE($11, r.source_machine_id)
47438
+ source_machine_id = COALESCE($10, r.source_machine_id)
47439
47439
  FROM checks
47440
47440
  WHERE r.service = $1 AND r.object_type = 'plans' AND r.object_id = $2 AND r.deleted_at IS NULL
47441
47441
  AND checks.plan_found AND checks.plan_revision_ok AND checks.membership_ok AND NOT checks.has_existing
@@ -47443,20 +47443,20 @@ class PostgresJsonRecordStore {
47443
47443
  ), updated_tasks AS (
47444
47444
  UPDATE ${this.tableName} r SET
47445
47445
  payload = r.payload || jsonb_build_object(
47446
- 'project_id', COALESCE($12::jsonb -> r.object_id, 'null'::jsonb),
47447
- 'updated_at', $10::text,
47446
+ 'project_id', COALESCE($11::jsonb -> r.object_id, 'null'::jsonb),
47447
+ 'updated_at', $9::text,
47448
47448
  'version', COALESCE((r.payload->>'version')::int, 0) + 1
47449
47449
  ),
47450
- updated_at = $10::timestamptz,
47450
+ updated_at = $9::timestamptz,
47451
47451
  version = COALESCE(r.version, 0) + 1,
47452
- source_machine_id = COALESCE($11, r.source_machine_id)
47452
+ source_machine_id = COALESCE($10, r.source_machine_id)
47453
47453
  WHERE r.service = $1 AND r.object_type = 'tasks' AND r.deleted_at IS NULL
47454
47454
  AND r.payload->>'plan_id' = $2 AND EXISTS (SELECT 1 FROM updated_plan)
47455
47455
  RETURNING 1
47456
47456
  ), task_gate AS (SELECT count(*) AS count FROM updated_tasks), inserted AS (
47457
47457
  INSERT INTO ${this.tableName}
47458
47458
  (service, object_type, object_id, payload, updated_at, deleted_at, source_machine_id, version)
47459
- SELECT $1, 'plan_project_link_rollback_receipts', $5, $6::jsonb, $10::timestamptz, NULL, $11, 1
47459
+ SELECT $1, 'plan_project_link_rollback_receipts', $4, $5::jsonb, $9::timestamptz, NULL, $10, 1
47460
47460
  FROM checks, task_gate
47461
47461
  WHERE NOT checks.has_existing AND EXISTS (SELECT 1 FROM updated_plan)
47462
47462
  RETURNING payload
@@ -47470,7 +47470,6 @@ class PostgresJsonRecordStore {
47470
47470
  this.service,
47471
47471
  input.plan_id,
47472
47472
  input.expected_plan_revision,
47473
- input.receipt_id,
47474
47473
  input.rollback_receipt_id,
47475
47474
  jsonbParam(rollback),
47476
47475
  jsonbParam(currentTaskProjects),
@@ -50912,6 +50911,1282 @@ var init_project_registration = __esm(() => {
50912
50911
  init_types4();
50913
50912
  });
50914
50913
 
50914
+ // src/task-manifest/canonical.ts
50915
+ import { createHash as createHash16 } from "crypto";
50916
+ function canonicalize3(value) {
50917
+ if (Array.isArray(value))
50918
+ return value.map(canonicalize3);
50919
+ if (value !== null && typeof value === "object") {
50920
+ return Object.fromEntries(Object.entries(value).filter(([, entry2]) => entry2 !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry2]) => [key, canonicalize3(entry2)]));
50921
+ }
50922
+ return value;
50923
+ }
50924
+ function canonicalJson(value) {
50925
+ return JSON.stringify(canonicalize3(value));
50926
+ }
50927
+ function canonicalDigest(value) {
50928
+ return createHash16("sha256").update(canonicalJson(value)).digest("hex");
50929
+ }
50930
+ function deterministicUuid(namespace, ...parts) {
50931
+ const bytes = createHash16("sha256").update([namespace, ...parts].join("\x1F")).digest().subarray(0, 16);
50932
+ bytes[6] = bytes[6] & 15 | 80;
50933
+ bytes[8] = bytes[8] & 63 | 128;
50934
+ const hex = bytes.toString("hex");
50935
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
50936
+ }
50937
+ var init_canonical = () => {};
50938
+
50939
+ // src/task-manifest/types.ts
50940
+ var TODOS_TASK_MANIFEST_ROUTE = "todos.task-manifest.v1", TODOS_TASK_MANIFEST_SCHEMA_VERSION = 1, TodosTaskManifestError;
50941
+ var init_types5 = __esm(() => {
50942
+ TodosTaskManifestError = class TodosTaskManifestError extends Error {
50943
+ code;
50944
+ details;
50945
+ constructor(code, message, details = {}) {
50946
+ super(message);
50947
+ this.code = code;
50948
+ this.details = details;
50949
+ this.name = "TodosTaskManifestError";
50950
+ }
50951
+ };
50952
+ });
50953
+
50954
+ // src/task-manifest/schema.ts
50955
+ function parseTodosTaskManifest(input) {
50956
+ const parsed = schema.safeParse(input);
50957
+ if (!parsed.success) {
50958
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `Invalid task manifest: ${parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")}`, { issues: parsed.error.issues });
50959
+ }
50960
+ const taskKeys = new Set;
50961
+ let comments = 0;
50962
+ let verifications = 0;
50963
+ for (const entry2 of parsed.data.tasks) {
50964
+ if (taskKeys.has(entry2.key)) {
50965
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `Duplicate task key: ${entry2.key}`);
50966
+ }
50967
+ taskKeys.add(entry2.key);
50968
+ comments += entry2.comments?.length ?? 0;
50969
+ verifications += entry2.verifications?.length ?? 0;
50970
+ }
50971
+ if (comments > TODOS_TASK_MANIFEST_BOUNDS.comments || verifications > TODOS_TASK_MANIFEST_BOUNDS.verifications) {
50972
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_BOUNDS_EXCEEDED", "Task manifest nested resource bounds exceeded", {
50973
+ comments,
50974
+ verifications
50975
+ });
50976
+ }
50977
+ const seenDependencies = new Set;
50978
+ const dependencyGraph = new Map;
50979
+ for (const edge of parsed.data.dependencies ?? []) {
50980
+ if (!taskKeys.has(edge.task) || !taskKeys.has(edge.depends_on)) {
50981
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_FOREIGN_REFERENCE", `Dependency contains foreign task key: ${edge.task} -> ${edge.depends_on}`);
50982
+ }
50983
+ if (edge.task === edge.depends_on) {
50984
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `Task ${edge.task} cannot depend on itself`);
50985
+ }
50986
+ const identity = `${edge.task}\x1F${edge.depends_on}`;
50987
+ if (seenDependencies.has(identity)) {
50988
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `Duplicate dependency: ${edge.task} -> ${edge.depends_on}`);
50989
+ }
50990
+ seenDependencies.add(identity);
50991
+ const prerequisites = dependencyGraph.get(edge.task) ?? [];
50992
+ prerequisites.push(edge.depends_on);
50993
+ dependencyGraph.set(edge.task, prerequisites);
50994
+ }
50995
+ const visiting = new Set;
50996
+ const visited = new Set;
50997
+ const visit = (taskKey) => {
50998
+ if (visiting.has(taskKey)) {
50999
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `Dependency cycle contains task ${taskKey}`);
51000
+ }
51001
+ if (visited.has(taskKey))
51002
+ return;
51003
+ visiting.add(taskKey);
51004
+ for (const prerequisite of dependencyGraph.get(taskKey) ?? [])
51005
+ visit(prerequisite);
51006
+ visiting.delete(taskKey);
51007
+ visited.add(taskKey);
51008
+ };
51009
+ for (const taskKey of taskKeys)
51010
+ visit(taskKey);
51011
+ return parsed.data;
51012
+ }
51013
+ function parseTodosTaskManifestCompensation(input) {
51014
+ const parsed = compensationSchema.safeParse(input);
51015
+ if (!parsed.success) {
51016
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `Invalid task-manifest compensation: ${parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")}`, { issues: parsed.error.issues });
51017
+ }
51018
+ return parsed.data;
51019
+ }
51020
+ function parseTodosTaskManifestBindingLookup(input) {
51021
+ const parsed = bindingLookupSchema.safeParse(input);
51022
+ if (!parsed.success) {
51023
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `Invalid task-manifest binding lookup: ${parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")}`, { issues: parsed.error.issues });
51024
+ }
51025
+ return parsed.data;
51026
+ }
51027
+ var TODOS_TASK_MANIFEST_BOUNDS, key, identifier, uuid2, scalar, boundedScalarRecord = (limit, field) => exports_external.record(exports_external.string().max(200), scalar).superRefine((value, context) => {
51028
+ if (Object.keys(value).length > limit) {
51029
+ context.addIssue({ code: exports_external.ZodIssueCode.custom, message: `${field} exceeds ${limit} fields` });
51030
+ }
51031
+ }), comment, verification, task2, dependency, effect, schema, compensationSchema, bindingLookupSchema;
51032
+ var init_schema2 = __esm(() => {
51033
+ init_zod();
51034
+ init_types();
51035
+ init_types5();
51036
+ TODOS_TASK_MANIFEST_BOUNDS = {
51037
+ tasks: 128,
51038
+ dependencies: 512,
51039
+ comments: 512,
51040
+ verifications: 512,
51041
+ effects: 64,
51042
+ metadata_fields: 128,
51043
+ effect_payload_fields: 128,
51044
+ request_bytes: 16777216,
51045
+ response_bytes: 1048576
51046
+ };
51047
+ key = exports_external.string().min(1).max(96).regex(/^[a-z][a-z0-9_-]*$/);
51048
+ identifier = exports_external.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);
51049
+ uuid2 = exports_external.string().uuid();
51050
+ scalar = exports_external.union([exports_external.string().max(4096), exports_external.number().finite(), exports_external.boolean(), exports_external.null()]);
51051
+ comment = exports_external.object({
51052
+ content: exports_external.string().min(1).max(16384),
51053
+ type: exports_external.enum(["comment", "progress", "status_change", "system"]).optional(),
51054
+ progress_pct: exports_external.number().int().min(0).max(100).optional(),
51055
+ agent_id: identifier.optional(),
51056
+ session_id: identifier.optional()
51057
+ }).strict();
51058
+ verification = exports_external.object({
51059
+ command: exports_external.string().min(1).max(8192),
51060
+ status: exports_external.enum(["passed", "failed", "unknown"]).optional(),
51061
+ output_summary: exports_external.string().max(16384).optional(),
51062
+ artifact_path: exports_external.string().max(4096).optional(),
51063
+ agent_id: identifier.optional()
51064
+ }).strict();
51065
+ task2 = exports_external.object({
51066
+ key,
51067
+ title: exports_external.string().min(1).max(500),
51068
+ description: exports_external.string().max(64000).optional(),
51069
+ status: exports_external.enum(TASK_STATUSES).optional(),
51070
+ priority: exports_external.enum(TASK_PRIORITIES).optional(),
51071
+ assigned_to: identifier.optional(),
51072
+ created_by: identifier.optional(),
51073
+ tags: exports_external.array(exports_external.string().min(1).max(100)).max(64).optional(),
51074
+ metadata: boundedScalarRecord(TODOS_TASK_MANIFEST_BOUNDS.metadata_fields, "metadata").optional(),
51075
+ comments: exports_external.array(comment).max(64).optional(),
51076
+ verifications: exports_external.array(verification).max(64).optional()
51077
+ }).strict();
51078
+ dependency = exports_external.object({ task: key, depends_on: key }).strict();
51079
+ effect = exports_external.object({
51080
+ topic: exports_external.string().min(1).max(200),
51081
+ payload: boundedScalarRecord(TODOS_TASK_MANIFEST_BOUNDS.effect_payload_fields, "effect payload")
51082
+ }).strict();
51083
+ schema = exports_external.object({
51084
+ version: exports_external.literal(1),
51085
+ operation_id: identifier,
51086
+ idempotency_key: identifier,
51087
+ project_id: uuid2,
51088
+ task_list_id: uuid2.optional(),
51089
+ if_binding_version: exports_external.number().int().min(0).optional(),
51090
+ plan: exports_external.object({
51091
+ key,
51092
+ name: exports_external.string().min(1).max(500),
51093
+ description: exports_external.string().max(64000).optional(),
51094
+ status: exports_external.enum(["active", "completed", "archived"]).optional()
51095
+ }).strict(),
51096
+ tasks: exports_external.array(task2).min(1).max(TODOS_TASK_MANIFEST_BOUNDS.tasks),
51097
+ dependencies: exports_external.array(dependency).max(TODOS_TASK_MANIFEST_BOUNDS.dependencies).optional(),
51098
+ effects: exports_external.array(effect).max(TODOS_TASK_MANIFEST_BOUNDS.effects).optional()
51099
+ }).strict();
51100
+ compensationSchema = exports_external.object({
51101
+ receipt_id: uuid2,
51102
+ idempotency_key: identifier,
51103
+ if_binding_version: exports_external.number().int().min(1).max(Number.MAX_SAFE_INTEGER)
51104
+ }).strict();
51105
+ bindingLookupSchema = exports_external.object({
51106
+ authority: exports_external.string().min(1).max(64),
51107
+ route: exports_external.string().min(1).max(128),
51108
+ schema_version: exports_external.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
51109
+ tenant_id: identifier,
51110
+ plan_id: uuid2,
51111
+ max_items: exports_external.number().int().min(1).max(Number.MAX_SAFE_INTEGER)
51112
+ }).strict();
51113
+ });
51114
+
51115
+ // src/task-manifest/backend.ts
51116
+ function validateTaskManifestBindingLookupRows(rows, tenantId, planId) {
51117
+ if (rows.length === 0) {
51118
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_BINDING_NOT_FOUND", `Managed task-manifest binding not found for plan: ${planId}`, { plan_id: planId });
51119
+ }
51120
+ if (rows.length !== 1) {
51121
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_LOOKUP_CONFLICT", "Task-manifest plan lookup matched more than one binding", { plan_id: planId, matched_items: rows.length, max_items: 1 });
51122
+ }
51123
+ const row = rows[0];
51124
+ const bindingVersion = Number(row.binding_version);
51125
+ const state = row.state;
51126
+ if (row.binding_tenant_id !== tenantId || row.receipt_tenant_id !== tenantId || row.binding_plan_id !== planId || row.receipt_plan_id !== planId || row.receipt_authority !== "todos" || row.receipt_route !== "todos.task-manifest.v1" || Number(row.receipt_schema_version) !== 1 || row.receipt_kind !== "apply" || row.binding_operation_id !== row.receipt_operation_id || typeof row.apply_receipt_id !== "string" || !UUID_PATTERN2.test(row.apply_receipt_id) || !Number.isSafeInteger(bindingVersion) || bindingVersion < 1 || state !== "applied" && state !== "compensated") {
51127
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_LOOKUP_CONFLICT", "Task-manifest binding and immutable apply receipt disagree", { plan_id: planId });
51128
+ }
51129
+ return {
51130
+ plan_id: planId,
51131
+ apply_receipt_id: row.apply_receipt_id,
51132
+ binding_version: bindingVersion,
51133
+ state
51134
+ };
51135
+ }
51136
+ var UUID_PATTERN2;
51137
+ var init_backend = __esm(() => {
51138
+ init_types5();
51139
+ UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
51140
+ });
51141
+
51142
+ // src/task-manifest/reference-guard.ts
51143
+ function postgresTaskManifestForeignReferenceSql(tableName) {
51144
+ return `SELECT object_type, object_id
51145
+ FROM ${tableName}
51146
+ WHERE service = $1 AND deleted_at IS NULL
51147
+ AND NOT (object_type = 'plans' AND object_id = $2)
51148
+ AND NOT (object_type = 'tasks' AND object_id IN (
51149
+ SELECT value FROM jsonb_array_elements_text($3::jsonb)
51150
+ ))
51151
+ AND NOT (object_type = 'dependencies' AND object_id IN (
51152
+ SELECT value FROM jsonb_array_elements_text($4::jsonb)
51153
+ ))
51154
+ AND NOT (object_type = 'comments' AND object_id IN (
51155
+ SELECT value FROM jsonb_array_elements_text($5::jsonb)
51156
+ ))
51157
+ AND NOT (object_type = 'verifications' AND object_id IN (
51158
+ SELECT value FROM jsonb_array_elements_text($6::jsonb)
51159
+ ))
51160
+ AND jsonb_path_exists(
51161
+ payload,
51162
+ '$.** ? (@ == $refs[*])',
51163
+ jsonb_build_object('refs', $7::jsonb)
51164
+ )
51165
+ LIMIT 1`;
51166
+ }
51167
+
51168
+ // src/task-manifest/schema-sql.ts
51169
+ function sqlString(value) {
51170
+ return `'${value.replaceAll("'", "''")}'`;
51171
+ }
51172
+ function postgresTodosTaskManifestSchemaSql(tenantId = "default") {
51173
+ const tenantDefault = sqlString(tenantId);
51174
+ return [
51175
+ `CREATE TABLE IF NOT EXISTS todos_task_manifest_receipts (
51176
+ receipt_id text PRIMARY KEY,
51177
+ tenant_id text NOT NULL,
51178
+ authority text NOT NULL CHECK(authority = 'todos'),
51179
+ route text NOT NULL,
51180
+ schema_version integer NOT NULL CHECK(schema_version = 1),
51181
+ kind text NOT NULL CHECK(kind IN ('apply', 'compensate')),
51182
+ operation_id text NOT NULL,
51183
+ idempotency_key text NOT NULL,
51184
+ request_digest text NOT NULL,
51185
+ result_digest text NOT NULL,
51186
+ binding_version integer NOT NULL,
51187
+ apply_receipt_id text,
51188
+ manifest_json jsonb,
51189
+ result_json jsonb NOT NULL,
51190
+ created_at timestamptz NOT NULL,
51191
+ UNIQUE(kind, idempotency_key)
51192
+ )`,
51193
+ `ALTER TABLE todos_task_manifest_receipts
51194
+ ADD COLUMN IF NOT EXISTS tenant_id text NOT NULL DEFAULT ${tenantDefault}`,
51195
+ `ALTER TABLE todos_task_manifest_receipts
51196
+ ALTER COLUMN tenant_id DROP DEFAULT`,
51197
+ `CREATE TABLE IF NOT EXISTS todos_task_manifest_bindings (
51198
+ operation_id text PRIMARY KEY,
51199
+ tenant_id text NOT NULL,
51200
+ idempotency_key text NOT NULL UNIQUE,
51201
+ request_digest text NOT NULL,
51202
+ result_digest text NOT NULL,
51203
+ apply_receipt_id text NOT NULL UNIQUE REFERENCES todos_task_manifest_receipts(receipt_id),
51204
+ manifest_json jsonb NOT NULL,
51205
+ result_json jsonb NOT NULL,
51206
+ state text NOT NULL CHECK(state IN ('applied', 'compensated')),
51207
+ version integer NOT NULL,
51208
+ compensation_receipt_id text,
51209
+ created_at timestamptz NOT NULL,
51210
+ updated_at timestamptz NOT NULL
51211
+ )`,
51212
+ `ALTER TABLE todos_task_manifest_bindings
51213
+ ADD COLUMN IF NOT EXISTS tenant_id text NOT NULL DEFAULT ${tenantDefault}`,
51214
+ `ALTER TABLE todos_task_manifest_bindings
51215
+ ALTER COLUMN tenant_id DROP DEFAULT`,
51216
+ `CREATE TABLE IF NOT EXISTS todos_task_manifest_outbox (
51217
+ id text PRIMARY KEY,
51218
+ apply_receipt_id text NOT NULL REFERENCES todos_task_manifest_receipts(receipt_id),
51219
+ topic text NOT NULL,
51220
+ payload jsonb NOT NULL,
51221
+ payload_digest text NOT NULL,
51222
+ status text NOT NULL CHECK(status IN ('pending', 'delivered', 'cancelled')),
51223
+ attempts integer NOT NULL DEFAULT 0,
51224
+ created_at timestamptz NOT NULL,
51225
+ delivered_at timestamptz
51226
+ )`,
51227
+ `CREATE INDEX IF NOT EXISTS todos_task_manifest_outbox_receipt_idx
51228
+ ON todos_task_manifest_outbox(apply_receipt_id, status)`,
51229
+ `CREATE INDEX IF NOT EXISTS todos_task_manifest_receipts_tenant_idx
51230
+ ON todos_task_manifest_receipts(tenant_id, receipt_id, kind)`,
51231
+ `CREATE INDEX IF NOT EXISTS todos_task_manifest_bindings_tenant_plan_idx
51232
+ ON todos_task_manifest_bindings(
51233
+ tenant_id,
51234
+ ((result_json #>> '{graph,plan_id}'))
51235
+ )`,
51236
+ `CREATE OR REPLACE FUNCTION todos_task_manifest_receipts_immutable()
51237
+ RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN
51238
+ RAISE EXCEPTION 'todos task manifest receipts are immutable';
51239
+ END; $$`,
51240
+ `DROP TRIGGER IF EXISTS todos_task_manifest_receipts_immutable ON todos_task_manifest_receipts`,
51241
+ `CREATE TRIGGER todos_task_manifest_receipts_immutable
51242
+ BEFORE UPDATE OR DELETE ON todos_task_manifest_receipts
51243
+ FOR EACH ROW EXECUTE FUNCTION todos_task_manifest_receipts_immutable()`
51244
+ ];
51245
+ }
51246
+
51247
+ // src/task-manifest/sqlite.ts
51248
+ var sqliteTails;
51249
+ var init_sqlite2 = __esm(() => {
51250
+ init_canonical();
51251
+ init_backend();
51252
+ init_types5();
51253
+ sqliteTails = new WeakMap;
51254
+ });
51255
+
51256
+ // src/task-manifest/postgres.ts
51257
+ function safeIdentifier2(value, field) {
51258
+ if (!/^[a-z_][a-z0-9_]*$/.test(value)) {
51259
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `${field} must be a safe PostgreSQL identifier`);
51260
+ }
51261
+ return value;
51262
+ }
51263
+ function parseJson2(value) {
51264
+ return typeof value === "string" ? JSON.parse(value) : value;
51265
+ }
51266
+ function timestamp4(value) {
51267
+ return value instanceof Date ? value.toISOString() : new Date(String(value)).toISOString();
51268
+ }
51269
+ function fault(faults, point) {
51270
+ if (faults.points.has(point))
51271
+ throw new Error(`Injected task-manifest fault at ${point}`);
51272
+ }
51273
+ function receiptFromRow3(row) {
51274
+ return {
51275
+ receipt_id: String(row["receipt_id"]),
51276
+ authority: "todos",
51277
+ route: "todos.task-manifest.v1",
51278
+ schema_version: 1,
51279
+ kind: row["kind"],
51280
+ operation_id: String(row["operation_id"]),
51281
+ idempotency_key: String(row["idempotency_key"]),
51282
+ request_digest: String(row["request_digest"]),
51283
+ result_digest: String(row["result_digest"]),
51284
+ binding_version: Number(row["binding_version"]),
51285
+ apply_receipt_id: row["apply_receipt_id"] == null ? null : String(row["apply_receipt_id"]),
51286
+ created_at: timestamp4(row["created_at"])
51287
+ };
51288
+ }
51289
+ function taskPayload2(manifest, task3, taskId, planId, now4) {
51290
+ return {
51291
+ id: taskId,
51292
+ short_id: null,
51293
+ project_id: manifest.project_id,
51294
+ parent_id: null,
51295
+ plan_id: planId,
51296
+ task_list_id: manifest.task_list_id ?? null,
51297
+ title: task3.title,
51298
+ description: task3.description ?? null,
51299
+ status: task3.status ?? "pending",
51300
+ priority: task3.priority ?? "medium",
51301
+ agent_id: null,
51302
+ assigned_to: task3.assigned_to ?? null,
51303
+ session_id: null,
51304
+ working_dir: null,
51305
+ tags: task3.tags ?? [],
51306
+ metadata: task3.metadata ?? {},
51307
+ version: 1,
51308
+ locked_by: null,
51309
+ locked_at: null,
51310
+ created_at: now4,
51311
+ updated_at: now4,
51312
+ started_at: null,
51313
+ completed_at: null,
51314
+ due_at: null,
51315
+ estimated_minutes: null,
51316
+ actual_minutes: null,
51317
+ requires_approval: false,
51318
+ approved_by: null,
51319
+ approved_at: null,
51320
+ recurrence_rule: null,
51321
+ recurrence_parent_id: null,
51322
+ spawns_template_id: null,
51323
+ confidence: null,
51324
+ reason: null,
51325
+ spawned_from_session: null,
51326
+ assigned_by: null,
51327
+ created_by: task3.created_by ?? null,
51328
+ assigned_from_project: null,
51329
+ task_type: null,
51330
+ cost_tokens: 0,
51331
+ cost_usd: 0,
51332
+ delegated_from: null,
51333
+ delegation_depth: 0,
51334
+ retry_count: 0,
51335
+ max_retries: 0,
51336
+ retry_after: null,
51337
+ sla_minutes: null,
51338
+ runner_id: null,
51339
+ runner_started_at: null,
51340
+ runner_completed_at: null,
51341
+ current_step: null,
51342
+ total_steps: null,
51343
+ machine_id: null,
51344
+ synced_at: null,
51345
+ archived_at: null
51346
+ };
51347
+ }
51348
+ function planPayload(input) {
51349
+ return {
51350
+ id: input.graph.plan_id,
51351
+ slug: null,
51352
+ project_id: input.manifest.project_id,
51353
+ task_list_id: input.manifest.task_list_id ?? null,
51354
+ agent_id: null,
51355
+ name: input.manifest.plan.name,
51356
+ description: input.manifest.plan.description ?? null,
51357
+ status: input.manifest.plan.status ?? "active",
51358
+ created_at: input.now,
51359
+ updated_at: input.now,
51360
+ machine_id: null,
51361
+ synced_at: null
51362
+ };
51363
+ }
51364
+ function placeholders2(start, count2) {
51365
+ return Array.from({ length: count2 }, (_, index) => `$${start + index}`).join(",");
51366
+ }
51367
+
51368
+ class PostgresTodosTaskManifestBackend {
51369
+ client;
51370
+ kind = "postgresql";
51371
+ service;
51372
+ tableName;
51373
+ tenantId;
51374
+ schemaReady = null;
51375
+ constructor(client, options = {}) {
51376
+ this.client = client;
51377
+ this.service = options.service ?? "todos";
51378
+ this.tableName = safeIdentifier2(options.tableName ?? DEFAULT_TODOS_POSTGRES_SYNC_TABLE, "tableName");
51379
+ this.tenantId = options.tenantId ?? "default";
51380
+ }
51381
+ async ensureSchema() {
51382
+ this.schemaReady ??= (async () => {
51383
+ for (const sql of postgresTodosSyncSchemaSql(this.tableName))
51384
+ await this.client.query(sql);
51385
+ for (const sql of postgresTodosTaskManifestSchemaSql(this.tenantId))
51386
+ await this.client.query(sql);
51387
+ })();
51388
+ await this.schemaReady;
51389
+ }
51390
+ async insertSync(tx, objectType2, objectId, payload, now4) {
51391
+ await tx.query(`INSERT INTO ${this.tableName} (
51392
+ service, object_type, object_id, payload, updated_at, deleted_at, source_machine_id, version
51393
+ ) VALUES ($1, $2, $3, $4::jsonb, $5, NULL, NULL, 1)`, [
51394
+ this.service,
51395
+ objectType2,
51396
+ objectId,
51397
+ canonicalJson(payload),
51398
+ now4
51399
+ ]);
51400
+ }
51401
+ async apply(input, faults) {
51402
+ await this.ensureSchema();
51403
+ return this.client.transaction(async (tx) => {
51404
+ const { manifest } = input;
51405
+ await tx.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${this.service}\x1F${manifest.operation_id}`]);
51406
+ await tx.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${this.service}\x1Fidempotency\x1F${manifest.idempotency_key}`]);
51407
+ const existing = await tx.query("SELECT * FROM todos_task_manifest_bindings WHERE tenant_id = $1 AND operation_id = $2 LIMIT 1 FOR UPDATE", [this.tenantId, manifest.operation_id]);
51408
+ if (existing.rows[0]) {
51409
+ const binding = existing.rows[0];
51410
+ if (binding["idempotency_key"] !== manifest.idempotency_key || binding["request_digest"] !== input.request_digest) {
51411
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_IDEMPOTENCY_CONFLICT", "Operation is already bound to a different request");
51412
+ }
51413
+ if (binding["state"] !== "applied") {
51414
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_GRAPH_CONFLICT", "Operation was already compensated");
51415
+ }
51416
+ return { ...parseJson2(binding["result_json"]), duplicate: true };
51417
+ }
51418
+ const reused = await tx.query("SELECT operation_id FROM todos_task_manifest_bindings WHERE tenant_id = $1 AND idempotency_key = $2 LIMIT 1", [this.tenantId, manifest.idempotency_key]);
51419
+ if (reused.rows[0])
51420
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_IDEMPOTENCY_CONFLICT", "Idempotency key is already used");
51421
+ if (manifest.if_binding_version !== undefined && manifest.if_binding_version !== 0) {
51422
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_CAS_CONFLICT", "New manifest binding version must be 0");
51423
+ }
51424
+ const project = await tx.query(`SELECT 1 AS found FROM ${this.tableName}
51425
+ WHERE service = $1 AND object_type = 'projects' AND object_id = $2 AND deleted_at IS NULL LIMIT 1`, [this.service, manifest.project_id]);
51426
+ if (!project.rows[0])
51427
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_FOREIGN_REFERENCE", "Project does not exist");
51428
+ if (manifest.task_list_id) {
51429
+ const taskList = await tx.query(`SELECT payload FROM ${this.tableName}
51430
+ WHERE service = $1 AND object_type = 'task_lists' AND object_id = $2 AND deleted_at IS NULL LIMIT 1`, [this.service, manifest.task_list_id]);
51431
+ const payload = taskList.rows[0] ? parseJson2(taskList.rows[0]["payload"]) : null;
51432
+ if (!payload || payload["project_id"] !== manifest.project_id) {
51433
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_FOREIGN_REFERENCE", "Task list does not belong to the project");
51434
+ }
51435
+ }
51436
+ const objectIds = [input.graph.plan_id, ...Object.values(input.graph.task_ids), ...input.graph.comment_ids, ...input.graph.verification_ids, ...input.graph.dependency_ids];
51437
+ const conflict = await tx.query(`SELECT object_id FROM ${this.tableName}
51438
+ WHERE service = $1 AND object_id IN (${placeholders2(2, objectIds.length)}) LIMIT 1`, [this.service, ...objectIds]);
51439
+ if (conflict.rows[0])
51440
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_GRAPH_CONFLICT", "A deterministic graph id already exists");
51441
+ await this.insertSync(tx, "plans", input.graph.plan_id, planPayload(input), input.now);
51442
+ fault(faults, "after_plan_write");
51443
+ for (const task3 of manifest.tasks) {
51444
+ await this.insertSync(tx, "tasks", input.graph.task_ids[task3.key], taskPayload2(manifest, task3, input.graph.task_ids[task3.key], input.graph.plan_id, input.now), input.now);
51445
+ }
51446
+ fault(faults, "after_task_write");
51447
+ for (const [index, edge] of (manifest.dependencies ?? []).entries()) {
51448
+ await this.insertSync(tx, "dependencies", input.graph.dependency_ids[index], {
51449
+ id: input.graph.dependency_ids[index],
51450
+ task_id: input.graph.task_ids[edge.task],
51451
+ depends_on: input.graph.task_ids[edge.depends_on],
51452
+ created_at: input.now,
51453
+ updated_at: input.now
51454
+ }, input.now);
51455
+ }
51456
+ fault(faults, "after_dependency_write");
51457
+ let commentIndex = 0;
51458
+ for (const task3 of manifest.tasks)
51459
+ for (const comment2 of task3.comments ?? []) {
51460
+ const id = input.graph.comment_ids[commentIndex++];
51461
+ await this.insertSync(tx, "comments", id, {
51462
+ id,
51463
+ task_id: input.graph.task_ids[task3.key],
51464
+ agent_id: comment2.agent_id ?? null,
51465
+ session_id: comment2.session_id ?? null,
51466
+ content: comment2.content,
51467
+ type: comment2.type ?? "comment",
51468
+ progress_pct: comment2.progress_pct ?? null,
51469
+ created_at: input.now
51470
+ }, input.now);
51471
+ }
51472
+ fault(faults, "after_comment_write");
51473
+ let verificationIndex = 0;
51474
+ for (const task3 of manifest.tasks)
51475
+ for (const verification2 of task3.verifications ?? []) {
51476
+ const id = input.graph.verification_ids[verificationIndex++];
51477
+ await this.insertSync(tx, "verifications", id, {
51478
+ id,
51479
+ task_id: input.graph.task_ids[task3.key],
51480
+ command: verification2.command,
51481
+ status: verification2.status ?? "unknown",
51482
+ output_summary: verification2.output_summary ?? null,
51483
+ artifact_path: verification2.artifact_path ?? null,
51484
+ agent_id: verification2.agent_id ?? null,
51485
+ run_at: input.now,
51486
+ created_at: input.now,
51487
+ updated_at: input.now
51488
+ }, input.now);
51489
+ }
51490
+ fault(faults, "after_verification_write");
51491
+ const readback = await this.readback(tx, input.graph);
51492
+ const expected = {
51493
+ plans: 1,
51494
+ tasks: manifest.tasks.length,
51495
+ dependencies: manifest.dependencies?.length ?? 0,
51496
+ comments: input.graph.comment_ids.length,
51497
+ verifications: input.graph.verification_ids.length,
51498
+ complete: true
51499
+ };
51500
+ if (canonicalJson(readback) !== canonicalJson(expected)) {
51501
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_READBACK_MISMATCH", "Exact PostgreSQL graph readback did not match", { expected, readback });
51502
+ }
51503
+ const receipt = {
51504
+ receipt_id: input.receipt_id,
51505
+ authority: "todos",
51506
+ route: "todos.task-manifest.v1",
51507
+ schema_version: 1,
51508
+ kind: "apply",
51509
+ operation_id: manifest.operation_id,
51510
+ idempotency_key: manifest.idempotency_key,
51511
+ request_digest: input.request_digest,
51512
+ result_digest: input.result_digest,
51513
+ binding_version: 1,
51514
+ apply_receipt_id: null,
51515
+ created_at: input.now
51516
+ };
51517
+ const result = {
51518
+ duplicate: false,
51519
+ receipt,
51520
+ graph: input.graph,
51521
+ readback,
51522
+ outbox_ids: input.outbox.map((entry2) => entry2.id),
51523
+ result_digest: input.result_digest
51524
+ };
51525
+ const manifestJson = canonicalJson(manifest);
51526
+ const resultJson = canonicalJson(result);
51527
+ await tx.query(`INSERT INTO todos_task_manifest_receipts (
51528
+ receipt_id, tenant_id, authority, route, schema_version, kind, operation_id, idempotency_key,
51529
+ request_digest, result_digest, binding_version, apply_receipt_id, manifest_json, result_json, created_at
51530
+ ) VALUES ($1, $2, 'todos', 'todos.task-manifest.v1', 1, 'apply', $3, $4, $5, $6, 1, NULL, $7::jsonb, $8::jsonb, $9)`, [
51531
+ input.receipt_id,
51532
+ this.tenantId,
51533
+ manifest.operation_id,
51534
+ manifest.idempotency_key,
51535
+ input.request_digest,
51536
+ input.result_digest,
51537
+ manifestJson,
51538
+ resultJson,
51539
+ input.now
51540
+ ]);
51541
+ for (const entry2 of input.outbox) {
51542
+ await tx.query(`INSERT INTO todos_task_manifest_outbox (
51543
+ id, apply_receipt_id, topic, payload, payload_digest, status, created_at
51544
+ ) VALUES ($1, $2, $3, $4::jsonb, $5, 'pending', $6)`, [
51545
+ entry2.id,
51546
+ input.receipt_id,
51547
+ entry2.topic,
51548
+ canonicalJson(entry2.payload),
51549
+ entry2.digest,
51550
+ input.now
51551
+ ]);
51552
+ }
51553
+ fault(faults, "after_outbox_write");
51554
+ await tx.query(`INSERT INTO todos_task_manifest_bindings (
51555
+ operation_id, tenant_id, idempotency_key, request_digest, result_digest, apply_receipt_id,
51556
+ manifest_json, result_json, state, version, created_at, updated_at
51557
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8::jsonb, 'applied', 1, $9, $9)`, [
51558
+ manifest.operation_id,
51559
+ this.tenantId,
51560
+ manifest.idempotency_key,
51561
+ input.request_digest,
51562
+ input.result_digest,
51563
+ input.receipt_id,
51564
+ manifestJson,
51565
+ resultJson,
51566
+ input.now
51567
+ ]);
51568
+ fault(faults, "after_receipt_write");
51569
+ return result;
51570
+ });
51571
+ }
51572
+ async readExact(receiptId2) {
51573
+ await this.ensureSchema();
51574
+ const result = await this.client.query("SELECT result_json FROM todos_task_manifest_receipts WHERE tenant_id = $1 AND receipt_id = $2 AND kind = 'apply' LIMIT 1", [this.tenantId, receiptId2]);
51575
+ if (!result.rows[0])
51576
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_RECEIPT_NOT_FOUND", `Apply receipt not found: ${receiptId2}`);
51577
+ return { ...parseJson2(result.rows[0]["result_json"]), duplicate: false };
51578
+ }
51579
+ async lookupBindingByPlanId(planId) {
51580
+ await this.ensureSchema();
51581
+ const result = await this.client.query(`
51582
+ SELECT
51583
+ b.apply_receipt_id AS apply_receipt_id,
51584
+ b.state AS state,
51585
+ b.version AS binding_version,
51586
+ b.tenant_id AS binding_tenant_id,
51587
+ b.operation_id AS binding_operation_id,
51588
+ b.result_json #>> '{graph,plan_id}' AS binding_plan_id,
51589
+ r.tenant_id AS receipt_tenant_id,
51590
+ r.authority AS receipt_authority,
51591
+ r.route AS receipt_route,
51592
+ r.schema_version AS receipt_schema_version,
51593
+ r.kind AS receipt_kind,
51594
+ r.operation_id AS receipt_operation_id,
51595
+ r.result_json #>> '{graph,plan_id}' AS receipt_plan_id
51596
+ FROM todos_task_manifest_bindings b
51597
+ LEFT JOIN todos_task_manifest_receipts r
51598
+ ON r.receipt_id = b.apply_receipt_id
51599
+ AND r.tenant_id = b.tenant_id
51600
+ WHERE b.tenant_id = $1
51601
+ AND b.result_json #>> '{graph,plan_id}' = $2
51602
+ LIMIT 2
51603
+ `, [this.tenantId, planId]);
51604
+ return validateTaskManifestBindingLookupRows(result.rows, this.tenantId, planId);
51605
+ }
51606
+ async markOutboxDelivered(outboxId, deliveredAt) {
51607
+ await this.ensureSchema();
51608
+ await this.client.transaction(async (tx) => {
51609
+ const result = await tx.query(`UPDATE todos_task_manifest_outbox
51610
+ SET status = 'delivered', delivered_at = $1, attempts = attempts + 1
51611
+ WHERE id = $2 AND status = 'pending'
51612
+ AND EXISTS (
51613
+ SELECT 1 FROM todos_task_manifest_receipts r
51614
+ WHERE r.receipt_id = todos_task_manifest_outbox.apply_receipt_id
51615
+ AND r.tenant_id = $3
51616
+ )
51617
+ RETURNING id`, [deliveredAt, outboxId, this.tenantId]);
51618
+ if (!result.rows[0])
51619
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_GRAPH_CONFLICT", `Pending outbox row not found: ${outboxId}`);
51620
+ });
51621
+ }
51622
+ async compensate(input, receipt, compensationReceiptId, requestDigest, now4) {
51623
+ await this.ensureSchema();
51624
+ return this.client.transaction(async (tx) => {
51625
+ await tx.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${this.service}\x1F${receipt.operation_id}`]);
51626
+ await tx.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${this.service}\x1Fcompensation-idempotency\x1F${input.idempotency_key}`]);
51627
+ const previous = await tx.query(`SELECT apply_receipt_id, request_digest, result_json
51628
+ FROM todos_task_manifest_receipts
51629
+ WHERE tenant_id = $1 AND kind = 'compensate' AND idempotency_key = $2
51630
+ LIMIT 1`, [this.tenantId, input.idempotency_key]);
51631
+ if (previous.rows[0]) {
51632
+ const row = previous.rows[0];
51633
+ if (row["apply_receipt_id"] !== input.receipt_id || row["request_digest"] !== requestDigest) {
51634
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_IDEMPOTENCY_CONFLICT", "Compensation idempotency key is already used");
51635
+ }
51636
+ return { ...parseJson2(row["result_json"]), duplicate: true };
51637
+ }
51638
+ const applyRows = await tx.query("SELECT * FROM todos_task_manifest_receipts WHERE tenant_id = $1 AND receipt_id = $2 AND kind = 'apply' LIMIT 1", [this.tenantId, input.receipt_id]);
51639
+ const applyRow = applyRows.rows[0];
51640
+ if (!applyRow)
51641
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_RECEIPT_NOT_FOUND", "Apply receipt not found");
51642
+ const bindingRows = await tx.query("SELECT * FROM todos_task_manifest_bindings WHERE tenant_id = $1 AND operation_id = $2 LIMIT 1 FOR UPDATE", [this.tenantId, receipt.operation_id]);
51643
+ const binding = bindingRows.rows[0];
51644
+ if (!binding || Number(binding["version"]) !== input.if_binding_version) {
51645
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_CAS_CONFLICT", "Binding version changed before compensation");
51646
+ }
51647
+ if (binding["state"] !== "applied")
51648
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Graph is not applied");
51649
+ const delivered = await tx.query(`SELECT o.id FROM todos_task_manifest_outbox o
51650
+ JOIN todos_task_manifest_receipts r ON r.receipt_id = o.apply_receipt_id
51651
+ WHERE r.tenant_id = $1 AND o.apply_receipt_id = $2 AND o.status = 'delivered'
51652
+ LIMIT 1`, [this.tenantId, input.receipt_id]);
51653
+ if (delivered.rows[0])
51654
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: delivered outbox row exists");
51655
+ const applyResult = parseJson2(applyRow["result_json"]);
51656
+ const manifest = parseJson2(applyRow["manifest_json"]);
51657
+ const expectedEffects = [
51658
+ {
51659
+ topic: "todos.task-manifest.applied",
51660
+ payload: { operation_id: manifest.operation_id, project_id: manifest.project_id }
51661
+ },
51662
+ ...(manifest.effects ?? []).map((effect2) => ({ topic: effect2.topic, payload: effect2.payload }))
51663
+ ];
51664
+ const outboxRows = await tx.query(`SELECT id, topic, payload, payload_digest, status, attempts, delivered_at
51665
+ FROM todos_task_manifest_outbox
51666
+ WHERE apply_receipt_id = $1
51667
+ AND EXISTS (
51668
+ SELECT 1 FROM todos_task_manifest_receipts r
51669
+ WHERE r.receipt_id = todos_task_manifest_outbox.apply_receipt_id
51670
+ AND r.tenant_id = $2
51671
+ )
51672
+ ORDER BY id`, [input.receipt_id, this.tenantId]);
51673
+ if (outboxRows.rows.length !== expectedEffects.length) {
51674
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: outbox changed since apply");
51675
+ }
51676
+ const outboxById = new Map(outboxRows.rows.map((entry2) => [String(entry2["id"]), entry2]));
51677
+ for (const [index, expectedEffect] of expectedEffects.entries()) {
51678
+ const stored2 = outboxById.get(applyResult.outbox_ids[index]);
51679
+ if (!stored2 || stored2["topic"] !== expectedEffect.topic || canonicalJson(parseJson2(stored2["payload"])) !== canonicalJson(expectedEffect.payload) || stored2["payload_digest"] !== canonicalDigest(expectedEffect) || stored2["status"] !== "pending" || Number(stored2["attempts"]) !== 0 || stored2["delivered_at"] != null) {
51680
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: outbox changed since apply");
51681
+ }
51682
+ }
51683
+ const taskIds = Object.values(applyResult.graph.task_ids);
51684
+ const foreignReference = await tx.query(postgresTaskManifestForeignReferenceSql(this.tableName), [
51685
+ this.service,
51686
+ applyResult.graph.plan_id,
51687
+ taskIds,
51688
+ applyResult.graph.dependency_ids,
51689
+ applyResult.graph.comment_ids,
51690
+ applyResult.graph.verification_ids,
51691
+ [applyResult.graph.plan_id, ...taskIds]
51692
+ ]);
51693
+ if (foreignReference.rows[0]) {
51694
+ const row = foreignReference.rows[0];
51695
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", `Compensation refused: foreign reference in ${String(row["object_type"])}:${String(row["object_id"])} would be changed`, row);
51696
+ }
51697
+ const actualReadback = await this.readback(tx, applyResult.graph);
51698
+ if (canonicalJson(actualReadback) !== canonicalJson(applyResult.readback)) {
51699
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: graph changed since apply");
51700
+ }
51701
+ const appliedAt = receiptFromRow3(applyRow).created_at;
51702
+ const expectedPayloads = new Map;
51703
+ expectedPayloads.set(applyResult.graph.plan_id, {
51704
+ type: "plans",
51705
+ payload: canonicalJson(planPayload({ manifest, graph: applyResult.graph, now: appliedAt }))
51706
+ });
51707
+ for (const task3 of manifest.tasks)
51708
+ expectedPayloads.set(applyResult.graph.task_ids[task3.key], {
51709
+ type: "tasks",
51710
+ payload: canonicalJson(taskPayload2(manifest, task3, applyResult.graph.task_ids[task3.key], applyResult.graph.plan_id, appliedAt))
51711
+ });
51712
+ for (const [index, edge] of (manifest.dependencies ?? []).entries())
51713
+ expectedPayloads.set(applyResult.graph.dependency_ids[index], {
51714
+ type: "dependencies",
51715
+ payload: canonicalJson({
51716
+ id: applyResult.graph.dependency_ids[index],
51717
+ task_id: applyResult.graph.task_ids[edge.task],
51718
+ depends_on: applyResult.graph.task_ids[edge.depends_on],
51719
+ created_at: appliedAt,
51720
+ updated_at: appliedAt
51721
+ })
51722
+ });
51723
+ let expectedCommentIndex = 0;
51724
+ for (const task3 of manifest.tasks)
51725
+ for (const comment2 of task3.comments ?? []) {
51726
+ const id = applyResult.graph.comment_ids[expectedCommentIndex++];
51727
+ expectedPayloads.set(id, {
51728
+ type: "comments",
51729
+ payload: canonicalJson({
51730
+ id,
51731
+ task_id: applyResult.graph.task_ids[task3.key],
51732
+ agent_id: comment2.agent_id ?? null,
51733
+ session_id: comment2.session_id ?? null,
51734
+ content: comment2.content,
51735
+ type: comment2.type ?? "comment",
51736
+ progress_pct: comment2.progress_pct ?? null,
51737
+ created_at: appliedAt
51738
+ })
51739
+ });
51740
+ }
51741
+ let expectedVerificationIndex = 0;
51742
+ for (const task3 of manifest.tasks)
51743
+ for (const verification2 of task3.verifications ?? []) {
51744
+ const id = applyResult.graph.verification_ids[expectedVerificationIndex++];
51745
+ expectedPayloads.set(id, {
51746
+ type: "verifications",
51747
+ payload: canonicalJson({
51748
+ id,
51749
+ task_id: applyResult.graph.task_ids[task3.key],
51750
+ command: verification2.command,
51751
+ status: verification2.status ?? "unknown",
51752
+ output_summary: verification2.output_summary ?? null,
51753
+ artifact_path: verification2.artifact_path ?? null,
51754
+ agent_id: verification2.agent_id ?? null,
51755
+ run_at: appliedAt,
51756
+ created_at: appliedAt,
51757
+ updated_at: appliedAt
51758
+ })
51759
+ });
51760
+ }
51761
+ const managedIds = [...expectedPayloads.keys()];
51762
+ const stored = await tx.query(`SELECT object_type, object_id, payload FROM ${this.tableName}
51763
+ WHERE service = $1 AND object_id IN (${placeholders2(2, managedIds.length)})`, [this.service, ...managedIds]);
51764
+ for (const row of stored.rows) {
51765
+ const expected = expectedPayloads.get(String(row["object_id"]));
51766
+ if (!expected || expected.type !== row["object_type"] || expected.payload !== canonicalJson(parseJson2(row["payload"]))) {
51767
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: managed graph values changed");
51768
+ }
51769
+ }
51770
+ if (stored.rows.length !== managedIds.length)
51771
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: managed graph is incomplete");
51772
+ await tx.query(`UPDATE todos_task_manifest_outbox
51773
+ SET status = 'cancelled'
51774
+ WHERE apply_receipt_id = $1 AND status = 'pending'
51775
+ AND EXISTS (
51776
+ SELECT 1 FROM todos_task_manifest_receipts r
51777
+ WHERE r.receipt_id = todos_task_manifest_outbox.apply_receipt_id
51778
+ AND r.tenant_id = $2
51779
+ )`, [input.receipt_id, this.tenantId]);
51780
+ const typedIds = [
51781
+ ["dependencies", applyResult.graph.dependency_ids],
51782
+ ["comments", applyResult.graph.comment_ids],
51783
+ ["verifications", applyResult.graph.verification_ids],
51784
+ ["tasks", taskIds],
51785
+ ["plans", [applyResult.graph.plan_id]]
51786
+ ];
51787
+ for (const [objectType2, ids2] of typedIds) {
51788
+ if (!ids2.length)
51789
+ continue;
51790
+ await tx.query(`DELETE FROM ${this.tableName} WHERE service = $1 AND object_type = $2
51791
+ AND object_id IN (${placeholders2(3, ids2.length)})`, [this.service, objectType2, ...ids2]);
51792
+ }
51793
+ const readback = await this.readback(tx, applyResult.graph);
51794
+ const result = { duplicate: false, receipt, absent: true, readback };
51795
+ await tx.query(`INSERT INTO todos_task_manifest_receipts (
51796
+ receipt_id, tenant_id, authority, route, schema_version, kind, operation_id, idempotency_key,
51797
+ request_digest, result_digest, binding_version, apply_receipt_id, manifest_json, result_json, created_at
51798
+ ) VALUES ($1, $2, 'todos', 'todos.task-manifest.v1', 1, 'compensate', $3, $4, $5, $6, $7, $8, NULL, $9::jsonb, $10)`, [
51799
+ compensationReceiptId,
51800
+ this.tenantId,
51801
+ receipt.operation_id,
51802
+ input.idempotency_key,
51803
+ requestDigest,
51804
+ receipt.result_digest,
51805
+ receipt.binding_version,
51806
+ input.receipt_id,
51807
+ canonicalJson(result),
51808
+ now4
51809
+ ]);
51810
+ const updated = await tx.query(`UPDATE todos_task_manifest_bindings
51811
+ SET state = 'compensated', version = $1, compensation_receipt_id = $2, updated_at = $3
51812
+ WHERE tenant_id = $4 AND operation_id = $5 AND state = 'applied' AND version = $6 RETURNING operation_id`, [
51813
+ receipt.binding_version,
51814
+ compensationReceiptId,
51815
+ now4,
51816
+ this.tenantId,
51817
+ receipt.operation_id,
51818
+ input.if_binding_version
51819
+ ]);
51820
+ if (!updated.rows[0])
51821
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_CAS_CONFLICT", "Binding changed during compensation");
51822
+ return result;
51823
+ });
51824
+ }
51825
+ async readback(tx, graph) {
51826
+ const count2 = async (objectType2, ids2) => {
51827
+ if (!ids2.length)
51828
+ return 0;
51829
+ const result = await tx.query(`SELECT count(*) AS count FROM ${this.tableName}
51830
+ WHERE service = $1 AND object_type = $2 AND deleted_at IS NULL
51831
+ AND object_id IN (${placeholders2(3, ids2.length)})`, [this.service, objectType2, ...ids2]);
51832
+ return Number(result.rows[0]?.count ?? 0);
51833
+ };
51834
+ return {
51835
+ plans: await count2("plans", [graph.plan_id]),
51836
+ tasks: await count2("tasks", Object.values(graph.task_ids)),
51837
+ dependencies: await count2("dependencies", graph.dependency_ids),
51838
+ comments: await count2("comments", graph.comment_ids),
51839
+ verifications: await count2("verifications", graph.verification_ids),
51840
+ complete: true
51841
+ };
51842
+ }
51843
+ }
51844
+ var init_postgres3 = __esm(() => {
51845
+ init_canonical();
51846
+ init_backend();
51847
+ init_postgres_sync();
51848
+ init_types5();
51849
+ });
51850
+
51851
+ // src/task-manifest/authority.ts
51852
+ function resolveTenantId(value) {
51853
+ const tenantId = value ?? "default";
51854
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/.test(tenantId)) {
51855
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "tenantId must be a bounded exact authority identifier");
51856
+ }
51857
+ return tenantId;
51858
+ }
51859
+ function normalize(input, now4) {
51860
+ const parsed = parseTodosTaskManifest(input);
51861
+ const requestBytes = Buffer.byteLength(canonicalJson(parsed), "utf8");
51862
+ if (requestBytes > TODOS_TASK_MANIFEST_BOUNDS.request_bytes) {
51863
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_BOUNDS_EXCEEDED", `Task manifest requires ${requestBytes} bytes but the bound is ${TODOS_TASK_MANIFEST_BOUNDS.request_bytes}`, { request_bytes: requestBytes, request_byte_limit: TODOS_TASK_MANIFEST_BOUNDS.request_bytes });
51864
+ }
51865
+ const manifest = sanitizeManifest(parsed);
51866
+ const task_ids = Object.fromEntries(manifest.tasks.map((task3) => [
51867
+ task3.key,
51868
+ deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, manifest.operation_id, "task", task3.key)
51869
+ ]));
51870
+ const graph = {
51871
+ plan_id: deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, manifest.operation_id, "plan", manifest.plan.key),
51872
+ task_ids,
51873
+ comment_ids: manifest.tasks.flatMap((task3) => (task3.comments ?? []).map((_, index) => deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, manifest.operation_id, "comment", task3.key, String(index)))),
51874
+ verification_ids: manifest.tasks.flatMap((task3) => (task3.verifications ?? []).map((_, index) => deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, manifest.operation_id, "verification", task3.key, String(index)))),
51875
+ dependency_ids: (manifest.dependencies ?? []).map((edge) => `${task_ids[edge.task]}::${task_ids[edge.depends_on]}`)
51876
+ };
51877
+ const request_digest = canonicalDigest(parsed);
51878
+ const effectInputs = [
51879
+ {
51880
+ topic: "todos.task-manifest.applied",
51881
+ payload: { operation_id: manifest.operation_id, project_id: manifest.project_id }
51882
+ },
51883
+ ...manifest.effects ?? []
51884
+ ];
51885
+ const outbox = effectInputs.map((effect2, index) => {
51886
+ const payload = { ...effect2.payload };
51887
+ return {
51888
+ id: deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, manifest.operation_id, "outbox", String(index)),
51889
+ topic: effect2.topic,
51890
+ payload,
51891
+ digest: canonicalDigest({ topic: effect2.topic, payload })
51892
+ };
51893
+ });
51894
+ const result_digest = canonicalDigest({ manifest, graph, outbox });
51895
+ return {
51896
+ manifest,
51897
+ request_digest,
51898
+ result_digest,
51899
+ receipt_id: deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, "apply", manifest.operation_id, manifest.idempotency_key, request_digest),
51900
+ graph,
51901
+ outbox,
51902
+ now: now4
51903
+ };
51904
+ }
51905
+ function sanitizeManifest(manifest) {
51906
+ return {
51907
+ ...manifest,
51908
+ plan: {
51909
+ ...manifest.plan,
51910
+ name: sanitizePreWriteText(manifest.plan.name, "task_manifest.plan.name"),
51911
+ ...manifest.plan.description !== undefined ? { description: sanitizePreWriteText(manifest.plan.description, "task_manifest.plan.description") } : {}
51912
+ },
51913
+ tasks: manifest.tasks.map((task3) => ({
51914
+ ...task3,
51915
+ title: sanitizePreWriteText(task3.title, `task_manifest.tasks.${task3.key}.title`),
51916
+ ...task3.description !== undefined ? { description: sanitizePreWriteText(task3.description, `task_manifest.tasks.${task3.key}.description`) } : {},
51917
+ ...task3.tags !== undefined ? { tags: sanitizePreWriteValue(task3.tags, `task_manifest.tasks.${task3.key}.tags`) } : {},
51918
+ ...task3.metadata !== undefined ? { metadata: sanitizePreWriteValue(task3.metadata, `task_manifest.tasks.${task3.key}.metadata`) } : {},
51919
+ ...task3.comments !== undefined ? {
51920
+ comments: task3.comments.map((comment2, index) => ({
51921
+ ...comment2,
51922
+ content: sanitizePreWriteText(comment2.content, `task_manifest.tasks.${task3.key}.comments.${index}.content`)
51923
+ }))
51924
+ } : {},
51925
+ ...task3.verifications !== undefined ? {
51926
+ verifications: task3.verifications.map((verification2, index) => ({
51927
+ ...verification2,
51928
+ command: sanitizePreWriteText(verification2.command, `task_manifest.tasks.${task3.key}.verifications.${index}.command`),
51929
+ ...verification2.output_summary !== undefined ? { output_summary: sanitizePreWriteText(verification2.output_summary, `task_manifest.tasks.${task3.key}.verifications.${index}.output_summary`) } : {},
51930
+ ...verification2.artifact_path !== undefined ? { artifact_path: sanitizePreWriteText(verification2.artifact_path, `task_manifest.tasks.${task3.key}.verifications.${index}.artifact_path`) } : {}
51931
+ }))
51932
+ } : {}
51933
+ })),
51934
+ ...manifest.effects !== undefined ? {
51935
+ effects: manifest.effects.map((effect2, index) => ({
51936
+ topic: sanitizePreWriteText(effect2.topic, `task_manifest.effects.${index}.topic`),
51937
+ payload: sanitizePreWriteValue(effect2.payload, `task_manifest.effects.${index}.payload`)
51938
+ }))
51939
+ } : {}
51940
+ };
51941
+ }
51942
+
51943
+ class PackageOwnedTodosTaskManifestAuthority {
51944
+ backend;
51945
+ options;
51946
+ tenantId;
51947
+ constructor(backend, options = {}) {
51948
+ this.backend = backend;
51949
+ this.options = options;
51950
+ this.tenantId = resolveTenantId(options.tenantId);
51951
+ }
51952
+ async capability() {
51953
+ return {
51954
+ authority: "todos",
51955
+ route: TODOS_TASK_MANIFEST_ROUTE,
51956
+ schema_version: TODOS_TASK_MANIFEST_SCHEMA_VERSION,
51957
+ tenant_id: this.tenantId,
51958
+ backend: this.backend.kind,
51959
+ deterministic_ids: true,
51960
+ immutable_receipts: true,
51961
+ transactional_outbox: true,
51962
+ exact_bounded_readback: true,
51963
+ conditional_compensation: true,
51964
+ transcript_safe: false,
51965
+ bounds: { ...TODOS_TASK_MANIFEST_BOUNDS }
51966
+ };
51967
+ }
51968
+ now() {
51969
+ const value = this.options.now?.() ?? new Date().toISOString();
51970
+ if (!Number.isFinite(Date.parse(value))) {
51971
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "now() returned an invalid timestamp");
51972
+ }
51973
+ return value;
51974
+ }
51975
+ async prepareFaults() {
51976
+ const points = new Set;
51977
+ if (this.options.faultInjector) {
51978
+ for (const point of FAULT_POINTS) {
51979
+ if (await this.options.faultInjector(point) === true)
51980
+ points.add(point);
51981
+ }
51982
+ }
51983
+ return { points };
51984
+ }
51985
+ async apply(input) {
51986
+ const normalized = normalize(input, this.now());
51987
+ const faults = await this.prepareFaults();
51988
+ return this.bounded(await this.backend.apply(normalized, faults));
51989
+ }
51990
+ readExact(receiptId2) {
51991
+ if (!receiptId2 || receiptId2.length > 200) {
51992
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "receiptId must be a bounded exact identifier");
51993
+ }
51994
+ return this.backend.readExact(receiptId2).then((result) => this.bounded(result));
51995
+ }
51996
+ async lookupBinding(input) {
51997
+ const request = parseTodosTaskManifestBindingLookup(input);
51998
+ if (request.max_items !== 1) {
51999
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_BOUNDS_EXCEEDED", "max_items must be exactly 1 for task-manifest binding lookup", { max_items: request.max_items, max_items_limit: 1 });
52000
+ }
52001
+ if (request.authority !== "todos" || request.route !== TODOS_TASK_MANIFEST_ROUTE || request.schema_version !== TODOS_TASK_MANIFEST_SCHEMA_VERSION || request.tenant_id !== this.tenantId) {
52002
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_CAPABILITY_MISMATCH", "Task-manifest binding lookup does not match this authority identity");
52003
+ }
52004
+ return this.bounded({
52005
+ authority: "todos",
52006
+ route: TODOS_TASK_MANIFEST_ROUTE,
52007
+ schema_version: TODOS_TASK_MANIFEST_SCHEMA_VERSION,
52008
+ tenant_id: this.tenantId,
52009
+ ...await this.backend.lookupBindingByPlanId(request.plan_id)
52010
+ });
52011
+ }
52012
+ markOutboxDelivered(outboxId) {
52013
+ if (!outboxId || outboxId.length > 200) {
52014
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "outboxId must be a bounded exact identifier");
52015
+ }
52016
+ return this.backend.markOutboxDelivered(outboxId, this.now());
52017
+ }
52018
+ async compensate(input) {
52019
+ const request = parseTodosTaskManifestCompensation(input);
52020
+ const applied = await this.backend.readExact(request.receipt_id);
52021
+ const requestDigest = canonicalDigest(request);
52022
+ const compensationReceiptId = deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, "compensate", applied.receipt.operation_id, request.idempotency_key, requestDigest);
52023
+ const receipt = {
52024
+ receipt_id: compensationReceiptId,
52025
+ authority: "todos",
52026
+ route: TODOS_TASK_MANIFEST_ROUTE,
52027
+ schema_version: 1,
52028
+ kind: "compensate",
52029
+ operation_id: applied.receipt.operation_id,
52030
+ idempotency_key: request.idempotency_key,
52031
+ request_digest: requestDigest,
52032
+ result_digest: canonicalDigest({ absent: true, apply_receipt_id: applied.receipt.receipt_id }),
52033
+ binding_version: request.if_binding_version + 1,
52034
+ apply_receipt_id: applied.receipt.receipt_id,
52035
+ created_at: this.now()
52036
+ };
52037
+ return this.bounded(await this.backend.compensate(request, receipt, compensationReceiptId, requestDigest, receipt.created_at));
52038
+ }
52039
+ bounded(result) {
52040
+ const responseBytes2 = Buffer.byteLength(canonicalJson(result), "utf8");
52041
+ if (responseBytes2 > TODOS_TASK_MANIFEST_BOUNDS.response_bytes) {
52042
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_BOUNDS_EXCEEDED", `Task-manifest response requires ${responseBytes2} bytes but the bound is ${TODOS_TASK_MANIFEST_BOUNDS.response_bytes}`, { response_bytes: responseBytes2, response_byte_limit: TODOS_TASK_MANIFEST_BOUNDS.response_bytes });
52043
+ }
52044
+ return result;
52045
+ }
52046
+ }
52047
+ function createPostgresTodosTaskManifestAuthority(client, options = {}) {
52048
+ if (!client || typeof client.transaction !== "function") {
52049
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_ATOMICITY_UNAVAILABLE", "An authoritative PostgreSQL transaction(callback) client is required");
52050
+ }
52051
+ const tenantId = resolveTenantId(options.tenantId);
52052
+ return new PackageOwnedTodosTaskManifestAuthority(new PostgresTodosTaskManifestBackend(client, { ...options, tenantId }), { ...options, tenantId });
52053
+ }
52054
+ var FAULT_POINTS;
52055
+ var init_authority2 = __esm(() => {
52056
+ init_canonical();
52057
+ init_schema2();
52058
+ init_sqlite2();
52059
+ init_postgres3();
52060
+ init_prewrite_secrets();
52061
+ init_types5();
52062
+ init_schema2();
52063
+ FAULT_POINTS = [
52064
+ "after_plan_write",
52065
+ "after_task_write",
52066
+ "after_dependency_write",
52067
+ "after_comment_write",
52068
+ "after_verification_write",
52069
+ "after_outbox_write",
52070
+ "after_receipt_write"
52071
+ ];
52072
+ });
52073
+
52074
+ // src/task-manifest/http.ts
52075
+ function json2(body, status = 200) {
52076
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS2 });
52077
+ }
52078
+ function status(error) {
52079
+ switch (error.code) {
52080
+ case "TODOS_TASK_MANIFEST_INVALID_INPUT":
52081
+ case "TODOS_TASK_MANIFEST_BOUNDS_EXCEEDED":
52082
+ case "TODOS_TASK_MANIFEST_FOREIGN_REFERENCE":
52083
+ return 400;
52084
+ case "TODOS_TASK_MANIFEST_RECEIPT_NOT_FOUND":
52085
+ case "TODOS_TASK_MANIFEST_BINDING_NOT_FOUND":
52086
+ return 404;
52087
+ case "TODOS_TASK_MANIFEST_ATOMICITY_UNAVAILABLE":
52088
+ return 503;
52089
+ default:
52090
+ return 409;
52091
+ }
52092
+ }
52093
+ async function body(request) {
52094
+ const declaredLength = Number(request.headers.get("content-length") ?? 0);
52095
+ if (Number.isFinite(declaredLength) && declaredLength > TODOS_TASK_MANIFEST_BOUNDS.request_bytes) {
52096
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_BOUNDS_EXCEEDED", "Task-manifest HTTP body exceeds the request bound");
52097
+ }
52098
+ const text2 = await boundedText(request, TODOS_TASK_MANIFEST_BOUNDS.request_bytes, "request");
52099
+ try {
52100
+ return JSON.parse(text2);
52101
+ } catch {
52102
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "Invalid JSON body");
52103
+ }
52104
+ }
52105
+ async function boundedText(message, limit, label) {
52106
+ if (!message.body)
52107
+ return "";
52108
+ const reader = message.body.getReader();
52109
+ const decoder = new TextDecoder;
52110
+ let bytes = 0;
52111
+ let text2 = "";
52112
+ try {
52113
+ while (true) {
52114
+ const { done, value } = await reader.read();
52115
+ if (done)
52116
+ break;
52117
+ bytes += value.byteLength;
52118
+ if (bytes > limit) {
52119
+ await reader.cancel();
52120
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_BOUNDS_EXCEEDED", `Task-manifest HTTP ${label} exceeds the byte bound`);
52121
+ }
52122
+ text2 += decoder.decode(value, { stream: true });
52123
+ }
52124
+ return text2 + decoder.decode();
52125
+ } finally {
52126
+ reader.releaseLock();
52127
+ }
52128
+ }
52129
+ async function handleTodosTaskManifestHttpRequest(request, url, authority, basePath = "/v1/task-manifest") {
52130
+ if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`))
52131
+ return null;
52132
+ const action = url.pathname.slice(basePath.length).split("/").filter(Boolean).join("/");
52133
+ try {
52134
+ if ((action === "" || action === "capability") && request.method === "GET") {
52135
+ return json2({ capability: await authority.capability() });
52136
+ }
52137
+ if (request.method !== "POST")
52138
+ return json2({ error: "method not allowed" }, 405);
52139
+ if (action === "apply")
52140
+ return json2({ result: await authority.apply(await body(request)) }, 201);
52141
+ if (action === "read-exact") {
52142
+ const input = await body(request);
52143
+ if (!input || typeof input.receipt_id !== "string") {
52144
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "receipt_id is required");
52145
+ }
52146
+ return json2({ result: await authority.readExact(input.receipt_id) });
52147
+ }
52148
+ if (action === "bindings/lookup") {
52149
+ return json2({
52150
+ result: await authority.lookupBinding(await body(request))
52151
+ });
52152
+ }
52153
+ if (action === "compensate") {
52154
+ return json2({ result: await authority.compensate(await body(request)) }, 201);
52155
+ }
52156
+ if (action === "outbox/delivered") {
52157
+ const input = await body(request);
52158
+ if (!input || typeof input.outbox_id !== "string") {
52159
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "outbox_id is required");
52160
+ }
52161
+ await authority.markOutboxDelivered(input.outbox_id);
52162
+ return json2({ delivered: true });
52163
+ }
52164
+ return json2({ error: "unknown task-manifest route" }, 404);
52165
+ } catch (cause) {
52166
+ if (cause instanceof TodosTaskManifestError) {
52167
+ return json2({ error: cause.message, code: cause.code, details: cause.details, authoritative: true }, status(cause));
52168
+ }
52169
+ return json2({ error: cause instanceof Error ? cause.message : "task-manifest error" }, 500);
52170
+ }
52171
+ }
52172
+ var JSON_HEADERS2;
52173
+ var init_http3 = __esm(() => {
52174
+ init_types5();
52175
+ init_schema2();
52176
+ JSON_HEADERS2 = { "Content-Type": "application/json" };
52177
+ });
52178
+
52179
+ // src/task-manifest/index.ts
52180
+ var init_task_manifest = __esm(() => {
52181
+ init_authority2();
52182
+ init_postgres3();
52183
+ init_sqlite2();
52184
+ init_http3();
52185
+ init_canonical();
52186
+ init_schema2();
52187
+ init_types5();
52188
+ });
52189
+
50915
52190
  // src/storage/comment-redaction-backfill.ts
50916
52191
  function assertSafeIdentifier2(value) {
50917
52192
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
@@ -50995,12 +52270,12 @@ async function backfillPostgresCommentRedaction(client, options = {}) {
50995
52270
  result.remaining_candidates = result.candidates;
50996
52271
  return result;
50997
52272
  }
50998
- const verification = await backfillPostgresCommentRedaction(client, {
52273
+ const verification2 = await backfillPostgresCommentRedaction(client, {
50999
52274
  ...options,
51000
52275
  apply: false,
51001
52276
  confirmation: undefined
51002
52277
  });
51003
- result.remaining_candidates = verification.candidates;
52278
+ result.remaining_candidates = verification2.candidates;
51004
52279
  return result;
51005
52280
  }
51006
52281
  var COMMENT_REDACTION_BACKFILL_CONFIRMATION = "REDACT_STORED_TODOS_COMMENTS";
@@ -51019,6 +52294,7 @@ __export(exports_cloud, {
51019
52294
  isPostgresBackendConfigured: () => isPostgresBackendConfigured,
51020
52295
  isCloudModeEnabled: () => isCloudModeEnabled,
51021
52296
  getCloudVerifier: () => getCloudVerifier,
52297
+ getCloudTaskManifestAuthority: () => getCloudTaskManifestAuthority,
51022
52298
  getCloudStorageAdapter: () => getCloudStorageAdapter,
51023
52299
  getCloudProjectRegistrationAuthority: () => getCloudProjectRegistrationAuthority,
51024
52300
  getCloudPrGroupLedger: () => getCloudPrGroupLedger,
@@ -51046,6 +52322,9 @@ function isPostgresBackendConfigured(env = process.env) {
51046
52322
  function isCloudModeEnabled(env = process.env) {
51047
52323
  return isPostgresBackendConfigured(env);
51048
52324
  }
52325
+ function getCloudTenantId() {
52326
+ return process.env.HASNA_TODOS_TENANT_ID ?? "default";
52327
+ }
51049
52328
  function getClient() {
51050
52329
  if (cachedClient)
51051
52330
  return cachedClient;
@@ -51075,11 +52354,20 @@ function getCloudProjectRegistrationAuthority() {
51075
52354
  cachedProjectRegistrationAuthority = createPostgresTodosProjectRegistrationAuthority(getClient(), {
51076
52355
  service: TODOS_APP_SLUG,
51077
52356
  authorityId: TODOS_APP_SLUG,
51078
- tenantId: process.env.HASNA_TODOS_TENANT_ID ?? "default",
52357
+ tenantId: getCloudTenantId(),
51079
52358
  corpusId: process.env.HASNA_TODOS_CORPUS_ID ?? `${TODOS_APP_SLUG}:postgresql`
51080
52359
  });
51081
52360
  return cachedProjectRegistrationAuthority;
51082
52361
  }
52362
+ function getCloudTaskManifestAuthority() {
52363
+ if (cachedTaskManifestAuthority)
52364
+ return cachedTaskManifestAuthority;
52365
+ cachedTaskManifestAuthority = createPostgresTodosTaskManifestAuthority(getClient(), {
52366
+ service: TODOS_APP_SLUG,
52367
+ tenantId: getCloudTenantId()
52368
+ });
52369
+ return cachedTaskManifestAuthority;
52370
+ }
51083
52371
  function authClient() {
51084
52372
  const client = getClient();
51085
52373
  return {
@@ -51131,6 +52419,9 @@ async function ensureCloudSchema() {
51131
52419
  for (const sql of postgresTodosProjectRegistrationSchemaSql()) {
51132
52420
  await client.query(sql);
51133
52421
  }
52422
+ for (const sql of postgresTodosTaskManifestSchemaSql(getCloudTenantId())) {
52423
+ await client.query(sql);
52424
+ }
51134
52425
  await getApiKeyStore().ensureSchema();
51135
52426
  })();
51136
52427
  return schemaEnsured;
@@ -51172,15 +52463,17 @@ async function closeCloud() {
51172
52463
  cachedVerifier = null;
51173
52464
  cachedPrGroupLedger = null;
51174
52465
  cachedProjectRegistrationAuthority = null;
52466
+ cachedTaskManifestAuthority = null;
51175
52467
  schemaEnsured = null;
51176
52468
  }
51177
- var TODOS_APP_SLUG = "todos", cachedClient = null, cachedAdapter = null, cachedStore = null, cachedVerifier = null, cachedPrGroupLedger = null, cachedProjectRegistrationAuthority = null, schemaEnsured = null;
52469
+ var TODOS_APP_SLUG = "todos", cachedClient = null, cachedAdapter = null, cachedStore = null, cachedVerifier = null, cachedPrGroupLedger = null, cachedProjectRegistrationAuthority = null, cachedTaskManifestAuthority = null, schemaEnsured = null;
51178
52470
  var init_cloud = __esm(() => {
51179
52471
  init_cloud_client();
51180
52472
  init_postgres_adapter();
51181
52473
  init_ledger();
51182
52474
  init_postgres();
51183
52475
  init_project_registration();
52476
+ init_task_manifest();
51184
52477
  init_postgres_sync();
51185
52478
  init_comment_redaction_backfill();
51186
52479
  });
@@ -51394,8 +52687,8 @@ function rowToOrg(row) {
51394
52687
  function createOrg(input, db) {
51395
52688
  const d = db || getDatabase();
51396
52689
  const id = uuid();
51397
- const timestamp4 = now();
51398
- d.run(`INSERT INTO orgs (id, name, description, metadata, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`, [id, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp4, timestamp4]);
52690
+ const timestamp5 = now();
52691
+ d.run(`INSERT INTO orgs (id, name, description, metadata, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`, [id, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp5, timestamp5]);
51399
52692
  return getOrg(id, d);
51400
52693
  }
51401
52694
  function getOrg(id, db) {
@@ -51539,9 +52832,9 @@ function parseBoundedLimit(value, fallback, max) {
51539
52832
  return fallback;
51540
52833
  return Math.min(parsed, max);
51541
52834
  }
51542
- function mapTaskError(e, json3) {
52835
+ function mapTaskError(e, json4) {
51543
52836
  if (e instanceof VersionConflictError) {
51544
- return json3({
52837
+ return json4({
51545
52838
  error: e.message,
51546
52839
  code: VersionConflictError.code,
51547
52840
  expected_version: e.expectedVersion,
@@ -51549,23 +52842,23 @@ function mapTaskError(e, json3) {
51549
52842
  }, 409);
51550
52843
  }
51551
52844
  if (e instanceof TaskNotFoundError) {
51552
- return json3({ error: e.message, code: TaskNotFoundError.code }, 404);
52845
+ return json4({ error: e.message, code: TaskNotFoundError.code }, 404);
51553
52846
  }
51554
52847
  if (e instanceof LockError) {
51555
- return json3({ error: e.message, code: LockError.code }, 409);
52848
+ return json4({ error: e.message, code: LockError.code }, 409);
51556
52849
  }
51557
52850
  if (e instanceof CompletionGuardError) {
51558
- return json3({
52851
+ return json4({
51559
52852
  error: e.message,
51560
52853
  code: CompletionGuardError.code,
51561
52854
  retry_after: e.retryAfterSeconds ?? null
51562
52855
  }, 409);
51563
52856
  }
51564
52857
  if (e instanceof TaskNotStartableError) {
51565
- return json3({ error: e.message, code: TaskNotStartableError.code }, 409);
52858
+ return json4({ error: e.message, code: TaskNotStartableError.code }, 409);
51566
52859
  }
51567
52860
  if (e instanceof Error && / is blocked by /.test(e.message)) {
51568
- return json3({ error: e.message, code: "TASK_NOT_STARTABLE" }, 409);
52861
+ return json4({ error: e.message, code: "TASK_NOT_STARTABLE" }, 409);
51569
52862
  }
51570
52863
  return null;
51571
52864
  }
@@ -51650,11 +52943,11 @@ data: ${JSON.stringify({ type: "connected", agent_id: agentId, timestamp: new Da
51650
52943
  }
51651
52944
  });
51652
52945
  }
51653
- function handleHealth(_ctx, json3) {
52946
+ function handleHealth(_ctx, json4) {
51654
52947
  const stats2 = getTaskStats();
51655
52948
  const staleCount = getStaleTasks(30).length;
51656
52949
  const overdueRecurring = getOverdueTasks().filter((t) => t.recurrence_rule && t.status === "pending").length;
51657
- return json3({
52950
+ return json4({
51658
52951
  status: staleCount === 0 && overdueRecurring === 0 ? "ok" : "warn",
51659
52952
  tasks: stats2.total,
51660
52953
  stale: staleCount,
@@ -51662,18 +52955,18 @@ function handleHealth(_ctx, json3) {
51662
52955
  timestamp: new Date().toISOString()
51663
52956
  });
51664
52957
  }
51665
- function handleHeadlessBoundary(_ctx, json3) {
52958
+ function handleHeadlessBoundary(_ctx, json4) {
51666
52959
  const { getHeadlessBoundaryManifest: getHeadlessBoundaryManifest2 } = (init_headless_boundaries(), __toCommonJS(exports_headless_boundaries));
51667
- return json3(getHeadlessBoundaryManifest2());
52960
+ return json4(getHeadlessBoundaryManifest2());
51668
52961
  }
51669
- function handleStats(_ctx, json3) {
52962
+ function handleStats(_ctx, json4) {
51670
52963
  const stats2 = getTaskStats();
51671
52964
  const byStatus = stats2.by_status;
51672
52965
  const projects = listProjects();
51673
52966
  const agents = listAgents();
51674
52967
  const staleCount = getStaleTasks(30).length;
51675
52968
  const overdueRecurring = getOverdueTasks().filter((t) => t.recurrence_rule && t.status === "pending").length;
51676
- return json3({
52969
+ return json4({
51677
52970
  total_tasks: stats2.total,
51678
52971
  pending: byStatus["pending"] ?? 0,
51679
52972
  in_progress: byStatus["in_progress"] ?? 0,
@@ -51696,10 +52989,10 @@ function taskStatusQueryParam(url) {
51696
52989
  return { ok: false, message: result.message };
51697
52990
  return { ok: true, value: collapseEnumValues(result.values) };
51698
52991
  }
51699
- async function handleListTasks(_req, url, _ctx, json3, taskToSummary2) {
52992
+ async function handleListTasks(_req, url, _ctx, json4, taskToSummary2) {
51700
52993
  const statusParam = taskStatusQueryParam(url);
51701
52994
  if (!statusParam.ok)
51702
- return json3({ error: statusParam.message }, 400);
52995
+ return json4({ error: statusParam.message }, 400);
51703
52996
  const projectId = url.searchParams.get("project_id") || undefined;
51704
52997
  const sessionId = url.searchParams.get("session_id") || undefined;
51705
52998
  const agentId = url.searchParams.get("agent_id") || undefined;
@@ -51714,60 +53007,60 @@ async function handleListTasks(_req, url, _ctx, json3, taskToSummary2) {
51714
53007
  limit: limitParam ? parseInt(limitParam, 10) : undefined,
51715
53008
  offset: offsetParam ? parseInt(offsetParam, 10) : undefined
51716
53009
  });
51717
- return json3(tasks.map((t) => taskToSummary2(t, fields)));
53010
+ return json4(tasks.map((t) => taskToSummary2(t, fields)));
51718
53011
  }
51719
- async function handleCreateTask(req, ctx, json3, taskToSummary2) {
53012
+ async function handleCreateTask(req, ctx, json4, taskToSummary2) {
51720
53013
  try {
51721
- const body = await req.json();
51722
- if (!body.title)
51723
- return json3({ error: "Missing 'title'" }, 400);
51724
- const createdBy = body.created_by ?? body.agent_id ?? "dashboard";
51725
- const task2 = createTask({
51726
- title: body.title,
51727
- description: body.description,
51728
- priority: body.priority,
51729
- project_id: body.project_id,
51730
- agent_id: body.agent_id ?? createdBy,
53014
+ const body2 = await req.json();
53015
+ if (!body2.title)
53016
+ return json4({ error: "Missing 'title'" }, 400);
53017
+ const createdBy = body2.created_by ?? body2.agent_id ?? "dashboard";
53018
+ const task3 = createTask({
53019
+ title: body2.title,
53020
+ description: body2.description,
53021
+ priority: body2.priority,
53022
+ project_id: body2.project_id,
53023
+ agent_id: body2.agent_id ?? createdBy,
51731
53024
  created_by: createdBy,
51732
- ...body.assigned_to ? { assigned_to: body.assigned_to } : {}
53025
+ ...body2.assigned_to ? { assigned_to: body2.assigned_to } : {}
51733
53026
  });
51734
- ctx.broadcastEvent({ type: "task", task_id: task2.id, action: "created", agent_id: task2.agent_id, project_id: task2.project_id });
51735
- return json3(taskToSummary2(task2), 201);
53027
+ ctx.broadcastEvent({ type: "task", task_id: task3.id, action: "created", agent_id: task3.agent_id, project_id: task3.project_id });
53028
+ return json4(taskToSummary2(task3), 201);
51736
53029
  } catch (e) {
51737
- return json3({ error: e instanceof Error ? e.message : "Failed to create task" }, 500);
53030
+ return json4({ error: e instanceof Error ? e.message : "Failed to create task" }, 500);
51738
53031
  }
51739
53032
  }
51740
- async function handleUpsertTask(req, ctx, json3, taskToSummary2) {
53033
+ async function handleUpsertTask(req, ctx, json4, taskToSummary2) {
51741
53034
  try {
51742
- const body = await req.json();
51743
- if (typeof body["fingerprint"] !== "string" || body["fingerprint"].trim() === "") {
51744
- return json3({ error: "Missing 'fingerprint'" }, 400);
53035
+ const body2 = await req.json();
53036
+ if (typeof body2["fingerprint"] !== "string" || body2["fingerprint"].trim() === "") {
53037
+ return json4({ error: "Missing 'fingerprint'" }, 400);
51745
53038
  }
51746
- if (typeof body["title"] !== "string" || body["title"].trim() === "") {
51747
- return json3({ error: "Missing 'title'" }, 400);
53039
+ if (typeof body2["title"] !== "string" || body2["title"].trim() === "") {
53040
+ return json4({ error: "Missing 'title'" }, 400);
51748
53041
  }
51749
- const metadata = body["metadata"] && typeof body["metadata"] === "object" && !Array.isArray(body["metadata"]) ? { ...body["metadata"] } : {};
51750
- for (const key of ["expectation_id", "expectation_fingerprint", "evidence_paths", "origin_loop_id", "origin_run_id", "expected", "observed", "acceptance"]) {
51751
- if (body[key] !== undefined)
51752
- metadata[key] = body[key];
53042
+ const metadata = body2["metadata"] && typeof body2["metadata"] === "object" && !Array.isArray(body2["metadata"]) ? { ...body2["metadata"] } : {};
53043
+ for (const key2 of ["expectation_id", "expectation_fingerprint", "evidence_paths", "origin_loop_id", "origin_run_id", "expected", "observed", "acceptance"]) {
53044
+ if (body2[key2] !== undefined)
53045
+ metadata[key2] = body2[key2];
51753
53046
  }
51754
53047
  const result = upsertTaskByFingerprint({
51755
- fingerprint: body["fingerprint"],
51756
- title: body["title"],
51757
- description: typeof body["description"] === "string" ? body["description"] : undefined,
51758
- status: body["status"],
51759
- priority: body["priority"],
51760
- project_id: typeof body["project_id"] === "string" ? body["project_id"] : undefined,
51761
- task_list_id: typeof body["task_list_id"] === "string" ? body["task_list_id"] : undefined,
51762
- assigned_to: typeof body["assigned_to"] === "string" ? body["assigned_to"] : undefined,
51763
- working_dir: typeof body["working_dir"] === "string" ? body["working_dir"] : undefined,
51764
- tags: Array.isArray(body["tags"]) ? body["tags"].filter((tag) => typeof tag === "string") : undefined,
53048
+ fingerprint: body2["fingerprint"],
53049
+ title: body2["title"],
53050
+ description: typeof body2["description"] === "string" ? body2["description"] : undefined,
53051
+ status: body2["status"],
53052
+ priority: body2["priority"],
53053
+ project_id: typeof body2["project_id"] === "string" ? body2["project_id"] : undefined,
53054
+ task_list_id: typeof body2["task_list_id"] === "string" ? body2["task_list_id"] : undefined,
53055
+ assigned_to: typeof body2["assigned_to"] === "string" ? body2["assigned_to"] : undefined,
53056
+ working_dir: typeof body2["working_dir"] === "string" ? body2["working_dir"] : undefined,
53057
+ tags: Array.isArray(body2["tags"]) ? body2["tags"].filter((tag) => typeof tag === "string") : undefined,
51765
53058
  metadata
51766
53059
  });
51767
53060
  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 });
51768
- return json3({ created: result.created, task: taskToSummary2(result.task) }, result.created ? 201 : 200);
53061
+ return json4({ created: result.created, task: taskToSummary2(result.task) }, result.created ? 201 : 200);
51769
53062
  } catch (e) {
51770
- return json3({ error: e instanceof Error ? e.message : "Failed to upsert task" }, 500);
53063
+ return json4({ error: e instanceof Error ? e.message : "Failed to upsert task" }, 500);
51771
53064
  }
51772
53065
  }
51773
53066
  function handleTasksExport(_req, url, _ctx, _json, taskToSummary2) {
@@ -51819,21 +53112,21 @@ function handleTasksExport(_req, url, _ctx, _json, taskToSummary2) {
51819
53112
  }
51820
53113
  });
51821
53114
  }
51822
- async function handleTasksBulk(req, _ctx, json3) {
53115
+ async function handleTasksBulk(req, _ctx, json4) {
51823
53116
  try {
51824
- const body = await req.json();
51825
- if (!body.ids?.length || !body.action)
51826
- return json3({ error: "Missing ids or action" }, 400);
53117
+ const body2 = await req.json();
53118
+ if (!body2.ids?.length || !body2.action)
53119
+ return json4({ error: "Missing ids or action" }, 400);
51827
53120
  const results = [];
51828
- for (const id of body.ids) {
53121
+ for (const id of body2.ids) {
51829
53122
  try {
51830
- if (body.action === "delete") {
53123
+ if (body2.action === "delete") {
51831
53124
  deleteTask(id);
51832
53125
  results.push({ id, success: true });
51833
- } else if (body.action === "start") {
53126
+ } else if (body2.action === "start") {
51834
53127
  startTask(id, "dashboard");
51835
53128
  results.push({ id, success: true });
51836
- } else if (body.action === "complete") {
53129
+ } else if (body2.action === "complete") {
51837
53130
  completeTask(id, "dashboard");
51838
53131
  results.push({ id, success: true });
51839
53132
  }
@@ -51841,84 +53134,84 @@ async function handleTasksBulk(req, _ctx, json3) {
51841
53134
  results.push({ id, success: false, error: e instanceof Error ? e.message : "Failed" });
51842
53135
  }
51843
53136
  }
51844
- return json3({ results, succeeded: results.filter((r) => r.success).length, failed: results.filter((r) => !r.success).length });
53137
+ return json4({ results, succeeded: results.filter((r) => r.success).length, failed: results.filter((r) => !r.success).length });
51845
53138
  } catch (e) {
51846
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53139
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51847
53140
  }
51848
53141
  }
51849
- function handleTasksStatus(_req, url, _ctx, json3) {
53142
+ function handleTasksStatus(_req, url, _ctx, json4) {
51850
53143
  try {
51851
53144
  const projectId = url.searchParams.get("project_id") || undefined;
51852
53145
  const agentId = url.searchParams.get("agent_id") || undefined;
51853
- const status = getStatus(projectId ? { project_id: projectId } : undefined, agentId);
51854
- return json3(status);
53146
+ const status2 = getStatus(projectId ? { project_id: projectId } : undefined, agentId);
53147
+ return json4(status2);
51855
53148
  } catch (e) {
51856
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53149
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51857
53150
  }
51858
53151
  }
51859
- function handleTasksNext(_req, url, _ctx, json3, taskToSummary2) {
53152
+ function handleTasksNext(_req, url, _ctx, json4, taskToSummary2) {
51860
53153
  try {
51861
53154
  const projectId = url.searchParams.get("project_id") || undefined;
51862
53155
  const agentId = url.searchParams.get("agent_id") || undefined;
51863
53156
  const fields = parseFieldsParam(url);
51864
- const task2 = getNextTask(agentId, projectId ? { project_id: projectId } : undefined);
51865
- return json3({ task: task2 ? taskToSummary2(task2, fields) : null });
53157
+ const task3 = getNextTask(agentId, projectId ? { project_id: projectId } : undefined);
53158
+ return json4({ task: task3 ? taskToSummary2(task3, fields) : null });
51866
53159
  } catch (e) {
51867
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53160
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51868
53161
  }
51869
53162
  }
51870
- function handleTasksActive(_req, url, _ctx, json3) {
53163
+ function handleTasksActive(_req, url, _ctx, json4) {
51871
53164
  try {
51872
53165
  const projectId = url.searchParams.get("project_id") || undefined;
51873
53166
  const work = getActiveWork(projectId ? { project_id: projectId } : undefined);
51874
- return json3({ active: work, count: work.length });
53167
+ return json4({ active: work, count: work.length });
51875
53168
  } catch (e) {
51876
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53169
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51877
53170
  }
51878
53171
  }
51879
- function handleTasksStale(_req, url, _ctx, json3, taskToSummary2) {
53172
+ function handleTasksStale(_req, url, _ctx, json4, taskToSummary2) {
51880
53173
  try {
51881
53174
  const projectId = url.searchParams.get("project_id") || undefined;
51882
53175
  const minutes2 = parseInt(url.searchParams.get("minutes") || "30", 10);
51883
53176
  const fields = parseFieldsParam(url);
51884
53177
  const tasks = getStaleTasks(minutes2, projectId ? { project_id: projectId } : undefined);
51885
- return json3({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length });
53178
+ return json4({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length });
51886
53179
  } catch (e) {
51887
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53180
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51888
53181
  }
51889
53182
  }
51890
- function handleTasksChanged(_req, url, _ctx, json3, taskToSummary2) {
53183
+ function handleTasksChanged(_req, url, _ctx, json4, taskToSummary2) {
51891
53184
  try {
51892
53185
  const since = url.searchParams.get("since");
51893
53186
  if (!since)
51894
- return json3({ error: "since parameter required (ISO date string)" }, 400);
53187
+ return json4({ error: "since parameter required (ISO date string)" }, 400);
51895
53188
  const projectId = url.searchParams.get("project_id") || undefined;
51896
53189
  const fields = parseFieldsParam(url);
51897
53190
  const tasks = getTasksChangedSince(since, projectId ? { project_id: projectId } : undefined);
51898
- return json3({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length, since });
53191
+ return json4({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length, since });
51899
53192
  } catch (e) {
51900
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53193
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51901
53194
  }
51902
53195
  }
51903
- function handleTasksContext(_req, url, _ctx, json3, taskToSummary2) {
53196
+ function handleTasksContext(_req, url, _ctx, json4, taskToSummary2) {
51904
53197
  const agentId = url.searchParams.get("agent_id") || undefined;
51905
53198
  const projectId = url.searchParams.get("project_id") || undefined;
51906
53199
  const format = url.searchParams.get("format") || "text";
51907
53200
  const fields = parseFieldsParam(url);
51908
53201
  const filters = projectId ? { project_id: projectId } : undefined;
51909
- const status = getStatus(filters, agentId);
53202
+ const status2 = getStatus(filters, agentId);
51910
53203
  const next = getNextTask(agentId, filters);
51911
53204
  if (format === "json") {
51912
- return json3({ status, next_task: next ? taskToSummary2(next, fields) : null });
53205
+ return json4({ status: status2, next_task: next ? taskToSummary2(next, fields) : null });
51913
53206
  }
51914
53207
  const lines = [];
51915
- lines.push(`Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done`);
51916
- if (status.stale_count > 0)
51917
- lines.push(`${status.stale_count} stale tasks stuck in-progress`);
51918
- if (status.overdue_recurring > 0)
51919
- lines.push(`${status.overdue_recurring} overdue recurring tasks`);
51920
- if (status.active_work.length > 0) {
51921
- lines.push(`Active: ${status.active_work.slice(0, 3).map((w) => `${w.short_id || w.id.slice(0, 8)} (${w.assigned_to || "?"})`).join(", ")}`);
53208
+ lines.push(`Tasks: ${status2.pending} pending | ${status2.in_progress} active | ${status2.completed} done`);
53209
+ if (status2.stale_count > 0)
53210
+ lines.push(`${status2.stale_count} stale tasks stuck in-progress`);
53211
+ if (status2.overdue_recurring > 0)
53212
+ lines.push(`${status2.overdue_recurring} overdue recurring tasks`);
53213
+ if (status2.active_work.length > 0) {
53214
+ lines.push(`Active: ${status2.active_work.slice(0, 3).map((w) => `${w.short_id || w.id.slice(0, 8)} (${w.assigned_to || "?"})`).join(", ")}`);
51922
53215
  }
51923
53216
  if (next)
51924
53217
  lines.push(`Next up: ${next.short_id || next.id.slice(0, 8)} [${next.priority}] ${next.title}`);
@@ -51926,18 +53219,18 @@ function handleTasksContext(_req, url, _ctx, json3, taskToSummary2) {
51926
53219
  `);
51927
53220
  return new Response(text2, { headers: { "Content-Type": "text/plain" } });
51928
53221
  }
51929
- function handleTaskAttachments(id, _ctx, json3) {
51930
- const task2 = getTask(id);
51931
- if (!task2)
51932
- return json3({ error: "Task not found" }, 404);
51933
- const evidence = task2.metadata?._evidence || {};
53222
+ function handleTaskAttachments(id, _ctx, json4) {
53223
+ const task3 = getTask(id);
53224
+ if (!task3)
53225
+ return json4({ error: "Task not found" }, 404);
53226
+ const evidence = task3.metadata?._evidence || {};
51934
53227
  const attachmentIds = evidence.attachments || [];
51935
- 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 });
53228
+ return json4({ task_id: id, short_id: task3.short_id, attachment_ids: attachmentIds, count: attachmentIds.length, files_changed: evidence.files_changed, commit_hash: evidence.commit_hash, notes: evidence.notes });
51936
53229
  }
51937
- async function handleTaskProgress(id, req, method, _ctx, json3, url) {
51938
- const task2 = getTask(id);
51939
- if (!task2)
51940
- return json3({ error: "Task not found" }, 404);
53230
+ async function handleTaskProgress(id, req, method, _ctx, json4, url) {
53231
+ const task3 = getTask(id);
53232
+ if (!task3)
53233
+ return json4({ error: "Task not found" }, 404);
51941
53234
  if (method === "GET") {
51942
53235
  const all = listComments(id);
51943
53236
  const progress = all.filter((c) => c.type === "progress");
@@ -51945,7 +53238,7 @@ async function handleTaskProgress(id, req, method, _ctx, json3, url) {
51945
53238
  const format = url?.searchParams.get("format") || "compact";
51946
53239
  const limit = parseBoundedLimit(url?.searchParams.get("limit") || null, 20, 200);
51947
53240
  const progressEntries = format === "full" ? progress : progress.slice(-limit);
51948
- return json3({
53241
+ return json4({
51949
53242
  task_id: id,
51950
53243
  progress_entries: progressEntries,
51951
53244
  latest,
@@ -51960,119 +53253,119 @@ async function handleTaskProgress(id, req, method, _ctx, json3, url) {
51960
53253
  }
51961
53254
  if (method === "POST") {
51962
53255
  try {
51963
- const body = await req.json();
51964
- if (!body.message)
51965
- return json3({ error: "message required" }, 400);
51966
- const comment = logProgress(id, body.message, body.pct_complete, body.agent_id);
51967
- return json3(comment, 201);
53256
+ const body2 = await req.json();
53257
+ if (!body2.message)
53258
+ return json4({ error: "message required" }, 400);
53259
+ const comment2 = logProgress(id, body2.message, body2.pct_complete, body2.agent_id);
53260
+ return json4(comment2, 201);
51968
53261
  } catch (e) {
51969
- return json3({ error: e instanceof Error ? e.message : "Failed to log progress" }, 500);
53262
+ return json4({ error: e instanceof Error ? e.message : "Failed to log progress" }, 500);
51970
53263
  }
51971
53264
  }
51972
53265
  return null;
51973
53266
  }
51974
- function handleGetTask(id, _ctx, json3, taskToSummary2, url) {
51975
- const task2 = getTask(id);
51976
- if (!task2)
51977
- return json3({ error: "Task not found" }, 404);
51978
- return json3(taskToSummary2(task2, url ? parseFieldsParam(url) : undefined));
53267
+ function handleGetTask(id, _ctx, json4, taskToSummary2, url) {
53268
+ const task3 = getTask(id);
53269
+ if (!task3)
53270
+ return json4({ error: "Task not found" }, 404);
53271
+ return json4(taskToSummary2(task3, url ? parseFieldsParam(url) : undefined));
51979
53272
  }
51980
- async function handlePatchTask(id, req, _ctx, json3, taskToSummary2) {
53273
+ async function handlePatchTask(id, req, _ctx, json4, taskToSummary2) {
51981
53274
  try {
51982
- const body = await req.json();
51983
- const task2 = getTask(id);
51984
- if (!task2)
51985
- return json3({ error: "Task not found" }, 404);
53275
+ const body2 = await req.json();
53276
+ const task3 = getTask(id);
53277
+ if (!task3)
53278
+ return json4({ error: "Task not found" }, 404);
51986
53279
  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"]);
51987
53280
  const safeBody = {};
51988
- for (const [key, value] of Object.entries(body)) {
51989
- if (ALLOWED.has(key))
51990
- safeBody[key] = value;
53281
+ for (const [key2, value] of Object.entries(body2)) {
53282
+ if (ALLOWED.has(key2))
53283
+ safeBody[key2] = value;
51991
53284
  }
51992
- const clientVersion = typeof body["version"] === "number" ? body["version"] : task2.version;
53285
+ const clientVersion = typeof body2["version"] === "number" ? body2["version"] : task3.version;
51993
53286
  const updated = updateTask(id, {
51994
53287
  ...safeBody,
51995
53288
  version: clientVersion
51996
53289
  });
51997
- return json3(taskToSummary2(updated));
53290
+ return json4(taskToSummary2(updated));
51998
53291
  } catch (e) {
51999
- const mapped = mapTaskError(e, json3);
53292
+ const mapped = mapTaskError(e, json4);
52000
53293
  if (mapped)
52001
53294
  return mapped;
52002
- return json3({ error: e instanceof Error ? e.message : "Failed to update task" }, 500);
53295
+ return json4({ error: e instanceof Error ? e.message : "Failed to update task" }, 500);
52003
53296
  }
52004
53297
  }
52005
- function handleDeleteTask(id, _ctx, json3) {
53298
+ function handleDeleteTask(id, _ctx, json4) {
52006
53299
  const deleted = deleteTask(id);
52007
53300
  if (!deleted)
52008
- return json3({ error: "Task not found" }, 404);
52009
- return json3({ success: true });
53301
+ return json4({ error: "Task not found" }, 404);
53302
+ return json4({ success: true });
52010
53303
  }
52011
- function handleStartTask(id, ctx, json3, taskToSummary2) {
53304
+ function handleStartTask(id, ctx, json4, taskToSummary2) {
52012
53305
  try {
52013
- const task2 = startTask(id, "dashboard");
52014
- ctx.broadcastEvent({ type: "task", task_id: task2.id, action: "started", agent_id: "dashboard", project_id: task2.project_id });
52015
- return json3(taskToSummary2(task2));
53306
+ const task3 = startTask(id, "dashboard");
53307
+ ctx.broadcastEvent({ type: "task", task_id: task3.id, action: "started", agent_id: "dashboard", project_id: task3.project_id });
53308
+ return json4(taskToSummary2(task3));
52016
53309
  } catch (e) {
52017
- const mapped = mapTaskError(e, json3);
53310
+ const mapped = mapTaskError(e, json4);
52018
53311
  if (mapped)
52019
53312
  return mapped;
52020
- return json3({ error: e instanceof Error ? e.message : "Failed to start task" }, 500);
53313
+ return json4({ error: e instanceof Error ? e.message : "Failed to start task" }, 500);
52021
53314
  }
52022
53315
  }
52023
- async function handleFailTask(id, req, ctx, json3, taskToSummary2) {
53316
+ async function handleFailTask(id, req, ctx, json4, taskToSummary2) {
52024
53317
  try {
52025
- const body = await req.json().catch(() => ({}));
52026
- const result = failTask(id, body.agent_id, body.reason, { retry: body.retry, error_code: body.error_code });
52027
- ctx.broadcastEvent({ type: "task", task_id: id, action: "failed", agent_id: body.agent_id || null, project_id: result.task.project_id });
52028
- return json3({ task: taskToSummary2(result.task), retry_task: result.retryTask ? taskToSummary2(result.retryTask) : null });
53318
+ const body2 = await req.json().catch(() => ({}));
53319
+ const result = failTask(id, body2.agent_id, body2.reason, { retry: body2.retry, error_code: body2.error_code });
53320
+ ctx.broadcastEvent({ type: "task", task_id: id, action: "failed", agent_id: body2.agent_id || null, project_id: result.task.project_id });
53321
+ return json4({ task: taskToSummary2(result.task), retry_task: result.retryTask ? taskToSummary2(result.retryTask) : null });
52029
53322
  } catch (e) {
52030
- return json3({ error: e instanceof Error ? e.message : "Failed to fail task" }, 500);
53323
+ return json4({ error: e instanceof Error ? e.message : "Failed to fail task" }, 500);
52031
53324
  }
52032
53325
  }
52033
- function handleCompleteTask(id, ctx, json3, taskToSummary2) {
53326
+ function handleCompleteTask(id, ctx, json4, taskToSummary2) {
52034
53327
  try {
52035
- const task2 = completeTask(id, "dashboard");
52036
- ctx.broadcastEvent({ type: "task", task_id: task2.id, action: "completed", agent_id: "dashboard", project_id: task2.project_id });
52037
- return json3(taskToSummary2(task2));
53328
+ const task3 = completeTask(id, "dashboard");
53329
+ ctx.broadcastEvent({ type: "task", task_id: task3.id, action: "completed", agent_id: "dashboard", project_id: task3.project_id });
53330
+ return json4(taskToSummary2(task3));
52038
53331
  } catch (e) {
52039
- const mapped = mapTaskError(e, json3);
53332
+ const mapped = mapTaskError(e, json4);
52040
53333
  if (mapped)
52041
53334
  return mapped;
52042
- return json3({ error: e instanceof Error ? e.message : "Failed to complete task" }, 500);
53335
+ return json4({ error: e instanceof Error ? e.message : "Failed to complete task" }, 500);
52043
53336
  }
52044
53337
  }
52045
- function handleListProjects(url, _ctx, json3) {
53338
+ function handleListProjects(url, _ctx, json4) {
52046
53339
  const pFieldsParam = url.searchParams.get("fields");
52047
53340
  const pFields = pFieldsParam ? pFieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
52048
53341
  const projects = listProjects();
52049
- return json3(pFields ? projects.map((p) => Object.fromEntries(pFields.map((f) => [f, p[f] ?? null]))) : projects);
53342
+ return json4(pFields ? projects.map((p) => Object.fromEntries(pFields.map((f) => [f, p[f] ?? null]))) : projects);
52050
53343
  }
52051
- async function handleCreateProject(req, _ctx, json3) {
53344
+ async function handleCreateProject(req, _ctx, json4) {
52052
53345
  try {
52053
- const body = await req.json();
52054
- if (!body.name || !body.path)
52055
- return json3({ error: "Missing name or path" }, 400);
52056
- const project = createProject({ name: body.name, path: body.path, description: body.description });
52057
- return json3(project, 201);
53346
+ const body2 = await req.json();
53347
+ if (!body2.name || !body2.path)
53348
+ return json4({ error: "Missing name or path" }, 400);
53349
+ const project = createProject({ name: body2.name, path: body2.path, description: body2.description });
53350
+ return json4(project, 201);
52058
53351
  } catch (e) {
52059
- return json3({ error: e instanceof Error ? e.message : "Failed to create project" }, 500);
53352
+ return json4({ error: e instanceof Error ? e.message : "Failed to create project" }, 500);
52060
53353
  }
52061
53354
  }
52062
- function handleDeleteProject(id, _ctx, json3) {
53355
+ function handleDeleteProject(id, _ctx, json4) {
52063
53356
  const deleted = deleteProject(id);
52064
53357
  if (!deleted)
52065
- return json3({ error: "Project not found" }, 404);
52066
- return json3({ success: true });
53358
+ return json4({ error: "Project not found" }, 404);
53359
+ return json4({ success: true });
52067
53360
  }
52068
- async function handleAgentMe(_req, url, _ctx, json3, taskToSummary2) {
53361
+ async function handleAgentMe(_req, url, _ctx, json4, taskToSummary2) {
52069
53362
  try {
52070
53363
  const name = url.searchParams.get("name");
52071
53364
  if (!name)
52072
- return json3({ error: "Missing name param" }, 400);
53365
+ return json4({ error: "Missing name param" }, 400);
52073
53366
  const agentResult = registerAgent({ name });
52074
53367
  if (isAgentConflict(agentResult))
52075
- return json3({ error: agentResult.message, conflict: true }, 409);
53368
+ return json4({ error: agentResult.message, conflict: true }, 409);
52076
53369
  const agent = agentResult;
52077
53370
  const tasks = listTasks({ assigned_to: agent.name });
52078
53371
  const agentIdTasks = listTasks({ agent_id: agent.id });
@@ -52080,7 +53373,7 @@ async function handleAgentMe(_req, url, _ctx, json3, taskToSummary2) {
52080
53373
  const pending = allTasks.filter((t) => t.status === "pending");
52081
53374
  const inProgress = allTasks.filter((t) => t.status === "in_progress");
52082
53375
  const completed = allTasks.filter((t) => t.status === "completed");
52083
- return json3({
53376
+ return json4({
52084
53377
  agent,
52085
53378
  pending_tasks: pending.map((t) => taskToSummary2(t)),
52086
53379
  in_progress_tasks: inProgress.map((t) => taskToSummary2(t)),
@@ -52094,132 +53387,132 @@ async function handleAgentMe(_req, url, _ctx, json3, taskToSummary2) {
52094
53387
  });
52095
53388
  } catch (e) {
52096
53389
  if (e instanceof InvalidAgentNameError)
52097
- return json3({ error: e.message, suggestions: e.suggestions }, 400);
52098
- return json3({ error: e instanceof Error ? e.message : "Failed to get agent profile" }, 500);
53390
+ return json4({ error: e.message, suggestions: e.suggestions }, 400);
53391
+ return json4({ error: e instanceof Error ? e.message : "Failed to get agent profile" }, 500);
52099
53392
  }
52100
53393
  }
52101
- function handleAgentQueue(agentId, _ctx, json3, taskToSummary2) {
53394
+ function handleAgentQueue(agentId, _ctx, json4, taskToSummary2) {
52102
53395
  const aliasSet = assignedToAliasSet(getDatabase(), agentId);
52103
53396
  const pending = listTasks({ status: "pending" });
52104
53397
  const queue = pending.filter((t) => aliasSet.has((t.assigned_to ?? "").toLowerCase()) || t.agent_id === agentId || !t.assigned_to && !t.locked_by);
52105
53398
  const order = { critical: 0, high: 1, medium: 2, low: 3 };
52106
53399
  queue.sort((a, b) => (order[a.priority] ?? 4) - (order[b.priority] ?? 4) || new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
52107
- return json3(queue.map((t) => taskToSummary2(t)));
53400
+ return json4(queue.map((t) => taskToSummary2(t)));
52108
53401
  }
52109
- async function handleClaimTask(req, _ctx, json3, taskToSummary2) {
53402
+ async function handleClaimTask(req, _ctx, json4, taskToSummary2) {
52110
53403
  try {
52111
- const body = await req.json();
52112
- const agentId = body.agent_id || "anonymous";
52113
- const task2 = claimNextTask(agentId, body.project_id ? { project_id: body.project_id } : undefined);
52114
- return json3({ task: task2 ? taskToSummary2(task2) : null });
53404
+ const body2 = await req.json();
53405
+ const agentId = body2.agent_id || "anonymous";
53406
+ const task3 = claimNextTask(agentId, body2.project_id ? { project_id: body2.project_id } : undefined);
53407
+ return json4({ task: task3 ? taskToSummary2(task3) : null });
52115
53408
  } catch (e) {
52116
- return json3({ error: e instanceof Error ? e.message : "Failed to claim" }, 500);
53409
+ return json4({ error: e instanceof Error ? e.message : "Failed to claim" }, 500);
52117
53410
  }
52118
53411
  }
52119
- function handleListOrgs(_ctx, json3) {
52120
- return json3(listOrgs());
53412
+ function handleListOrgs(_ctx, json4) {
53413
+ return json4(listOrgs());
52121
53414
  }
52122
- async function handleCreateOrg(req, _ctx, json3) {
53415
+ async function handleCreateOrg(req, _ctx, json4) {
52123
53416
  try {
52124
- const body = await req.json();
52125
- if (!body.name)
52126
- return json3({ error: "Missing name" }, 400);
52127
- return json3(createOrg(body), 201);
53417
+ const body2 = await req.json();
53418
+ if (!body2.name)
53419
+ return json4({ error: "Missing name" }, 400);
53420
+ return json4(createOrg(body2), 201);
52128
53421
  } catch (e) {
52129
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53422
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52130
53423
  }
52131
53424
  }
52132
- async function handleUpdateOrg(id, req, _ctx, json3) {
53425
+ async function handleUpdateOrg(id, req, _ctx, json4) {
52133
53426
  try {
52134
- const body = await req.json();
52135
- return json3(updateOrg(id, body));
53427
+ const body2 = await req.json();
53428
+ return json4(updateOrg(id, body2));
52136
53429
  } catch (e) {
52137
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53430
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52138
53431
  }
52139
53432
  }
52140
- function handleDeleteOrg(id, _ctx, json3) {
53433
+ function handleDeleteOrg(id, _ctx, json4) {
52141
53434
  const deleted = deleteOrg(id);
52142
- return json3(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
53435
+ return json4(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
52143
53436
  }
52144
- function handleOrgChart(_ctx, json3) {
52145
- return json3(getOrgChart());
53437
+ function handleOrgChart(_ctx, json4) {
53438
+ return json4(getOrgChart());
52146
53439
  }
52147
- function handleAgentTeam(agentId, _ctx, json3) {
52148
- return json3(getDirectReports(decodeURIComponent(agentId)));
53440
+ function handleAgentTeam(agentId, _ctx, json4) {
53441
+ return json4(getDirectReports(decodeURIComponent(agentId)));
52149
53442
  }
52150
- function handleListAgents(url, _ctx, json3) {
53443
+ function handleListAgents(url, _ctx, json4) {
52151
53444
  const aFieldsParam = url.searchParams.get("fields");
52152
53445
  const aFields = aFieldsParam ? aFieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
52153
53446
  const agents = listAgents();
52154
- return json3(aFields ? agents.map((a) => Object.fromEntries(aFields.map((f) => [f, a[f] ?? null]))) : agents);
53447
+ return json4(aFields ? agents.map((a) => Object.fromEntries(aFields.map((f) => [f, a[f] ?? null]))) : agents);
52155
53448
  }
52156
- async function handleRegisterAgent(req, _ctx, json3) {
53449
+ async function handleRegisterAgent(req, _ctx, json4) {
52157
53450
  try {
52158
- const body = await req.json();
52159
- if (!body.name)
52160
- return json3({ error: "Missing name" }, 400);
52161
- const result = registerAgent({ name: body.name, description: body.description, session_id: body.session_id, working_dir: body.working_dir });
53451
+ const body2 = await req.json();
53452
+ if (!body2.name)
53453
+ return json4({ error: "Missing name" }, 400);
53454
+ const result = registerAgent({ name: body2.name, description: body2.description, session_id: body2.session_id, working_dir: body2.working_dir });
52162
53455
  if (isAgentConflict(result))
52163
- return json3({ error: result.message, conflict: true }, 409);
52164
- return json3(result, 201);
53456
+ return json4({ error: result.message, conflict: true }, 409);
53457
+ return json4(result, 201);
52165
53458
  } catch (e) {
52166
53459
  if (e instanceof InvalidAgentNameError)
52167
- return json3({ error: e.message, suggestions: e.suggestions }, 400);
52168
- return json3({ error: e instanceof Error ? e.message : "Failed to register agent" }, 500);
53460
+ return json4({ error: e.message, suggestions: e.suggestions }, 400);
53461
+ return json4({ error: e instanceof Error ? e.message : "Failed to register agent" }, 500);
52169
53462
  }
52170
53463
  }
52171
- async function handleUpdateAgent(id, req, _ctx, json3) {
53464
+ async function handleUpdateAgent(id, req, _ctx, json4) {
52172
53465
  try {
52173
- const body = await req.json();
52174
- const agent = updateAgent(id, body);
52175
- return json3(agent);
53466
+ const body2 = await req.json();
53467
+ const agent = updateAgent(id, body2);
53468
+ return json4(agent);
52176
53469
  } catch (e) {
52177
53470
  if (e instanceof InvalidAgentNameError)
52178
- return json3({ error: e.message, suggestions: e.suggestions }, 400);
52179
- return json3({ error: e instanceof Error ? e.message : "Failed to update agent" }, 500);
53471
+ return json4({ error: e.message, suggestions: e.suggestions }, 400);
53472
+ return json4({ error: e instanceof Error ? e.message : "Failed to update agent" }, 500);
52180
53473
  }
52181
53474
  }
52182
- function handleDeleteAgent(id, _ctx, json3) {
53475
+ function handleDeleteAgent(id, _ctx, json4) {
52183
53476
  const deleted = deleteAgent(id);
52184
53477
  if (!deleted)
52185
- return json3({ error: "Agent not found" }, 404);
52186
- return json3({ success: true });
53478
+ return json4({ error: "Agent not found" }, 404);
53479
+ return json4({ success: true });
52187
53480
  }
52188
- async function handleBulkDeleteAgents(req, _ctx, json3) {
53481
+ async function handleBulkDeleteAgents(req, _ctx, json4) {
52189
53482
  try {
52190
- const body = await req.json();
52191
- if (!body.ids?.length || body.action !== "delete")
52192
- return json3({ error: "Missing ids or invalid action" }, 400);
53483
+ const body2 = await req.json();
53484
+ if (!body2.ids?.length || body2.action !== "delete")
53485
+ return json4({ error: "Missing ids or invalid action" }, 400);
52193
53486
  let succeeded = 0;
52194
- for (const id of body.ids) {
53487
+ for (const id of body2.ids) {
52195
53488
  if (deleteAgent(id))
52196
53489
  succeeded++;
52197
53490
  }
52198
- return json3({ succeeded, failed: body.ids.length - succeeded });
53491
+ return json4({ succeeded, failed: body2.ids.length - succeeded });
52199
53492
  } catch (e) {
52200
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53493
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52201
53494
  }
52202
53495
  }
52203
- async function handleBulkDeleteProjects(req, _ctx, json3) {
53496
+ async function handleBulkDeleteProjects(req, _ctx, json4) {
52204
53497
  try {
52205
- const body = await req.json();
52206
- if (!body.ids?.length || body.action !== "delete")
52207
- return json3({ error: "Missing ids or invalid action" }, 400);
53498
+ const body2 = await req.json();
53499
+ if (!body2.ids?.length || body2.action !== "delete")
53500
+ return json4({ error: "Missing ids or invalid action" }, 400);
52208
53501
  let succeeded = 0;
52209
- for (const id of body.ids) {
53502
+ for (const id of body2.ids) {
52210
53503
  if (deleteProject(id))
52211
53504
  succeeded++;
52212
53505
  }
52213
- return json3({ succeeded, failed: body.ids.length - succeeded });
53506
+ return json4({ succeeded, failed: body2.ids.length - succeeded });
52214
53507
  } catch (e) {
52215
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53508
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52216
53509
  }
52217
53510
  }
52218
- function handleDoctor(_ctx, json3) {
53511
+ function handleDoctor(_ctx, json4) {
52219
53512
  const { runTodosDoctor: runTodosDoctor2 } = (init_doctor(), __toCommonJS(exports_doctor));
52220
- return json3(runTodosDoctor2({ apply: false }));
53513
+ return json4(runTodosDoctor2({ apply: false }));
52221
53514
  }
52222
- function handleReport(_req, url, _ctx, json3) {
53515
+ function handleReport(_req, url, _ctx, json4) {
52223
53516
  const days = parseInt(url.searchParams.get("days") || "7", 10);
52224
53517
  const projectId = url.searchParams.get("project_id") || undefined;
52225
53518
  const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
@@ -52235,114 +53528,114 @@ function handleReport(_req, url, _ctx, json3) {
52235
53528
  byDay[day] = (byDay[day] || 0) + 1;
52236
53529
  }
52237
53530
  const completionRate = changed.length > 0 ? Math.round(completed.length / changed.length * 100) : 0;
52238
- 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 });
53531
+ return json4({ days, period_since: since, total: all.length, stats: stats2, changed: changed.length, completed: completed.length, failed: failed.length, completion_rate: completionRate, by_day: byDay });
52239
53532
  }
52240
- function handleActivity(_req, url, _ctx, json3) {
53533
+ function handleActivity(_req, url, _ctx, json4) {
52241
53534
  const limit = parseInt(url.searchParams.get("limit") || "50", 10);
52242
- return json3(getRecentActivity(limit));
53535
+ return json4(getRecentActivity(limit));
52243
53536
  }
52244
- function handleTaskHistory(id, _ctx, json3, url) {
53537
+ function handleTaskHistory(id, _ctx, json4, url) {
52245
53538
  const history = getTaskHistory(id);
52246
53539
  const format = url?.searchParams.get("format") || "compact";
52247
53540
  const limit = parseBoundedLimit(url?.searchParams.get("limit") || null, 20, 500);
52248
- return json3(format === "full" ? history : history.slice(0, limit));
53541
+ return json4(format === "full" ? history : history.slice(0, limit));
52249
53542
  }
52250
- function handleListWebhooks(_ctx, json3) {
52251
- return json3(listWebhooks());
53543
+ function handleListWebhooks(_ctx, json4) {
53544
+ return json4(listWebhooks());
52252
53545
  }
52253
- async function handleCreateWebhook(req, _ctx, json3) {
53546
+ async function handleCreateWebhook(req, _ctx, json4) {
52254
53547
  try {
52255
- const body = await req.json();
52256
- if (!body.url)
52257
- return json3({ error: "Missing url" }, 400);
52258
- return json3(createWebhook(body), 201);
53548
+ const body2 = await req.json();
53549
+ if (!body2.url)
53550
+ return json4({ error: "Missing url" }, 400);
53551
+ return json4(createWebhook(body2), 201);
52259
53552
  } catch (e) {
52260
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53553
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52261
53554
  }
52262
53555
  }
52263
- function handleDeleteWebhook(id, _ctx, json3) {
53556
+ function handleDeleteWebhook(id, _ctx, json4) {
52264
53557
  const deleted = deleteWebhook(id);
52265
- return json3(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
53558
+ return json4(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
52266
53559
  }
52267
- function handleListTemplates(_ctx, json3) {
52268
- return json3(listTemplates());
53560
+ function handleListTemplates(_ctx, json4) {
53561
+ return json4(listTemplates());
52269
53562
  }
52270
- async function handleCreateTemplate(req, _ctx, json3) {
53563
+ async function handleCreateTemplate(req, _ctx, json4) {
52271
53564
  try {
52272
- const body = await req.json();
52273
- if (!body.name || !body.title_pattern)
52274
- return json3({ error: "Missing name or title_pattern" }, 400);
52275
- return json3(createTemplate(body), 201);
53565
+ const body2 = await req.json();
53566
+ if (!body2.name || !body2.title_pattern)
53567
+ return json4({ error: "Missing name or title_pattern" }, 400);
53568
+ return json4(createTemplate(body2), 201);
52276
53569
  } catch (e) {
52277
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53570
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52278
53571
  }
52279
53572
  }
52280
- function handleDeleteTemplate(id, _ctx, json3) {
53573
+ function handleDeleteTemplate(id, _ctx, json4) {
52281
53574
  const deleted = deleteTemplate(id);
52282
- return json3(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
53575
+ return json4(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
52283
53576
  }
52284
- function handleListPlans(url, _ctx, json3) {
53577
+ function handleListPlans(url, _ctx, json4) {
52285
53578
  const projectId = url.searchParams.get("project_id") || undefined;
52286
53579
  const plans = listPlans(projectId);
52287
- return json3(plans);
53580
+ return json4(plans);
52288
53581
  }
52289
- async function handleCreatePlan(req, _ctx, json3) {
53582
+ async function handleCreatePlan(req, _ctx, json4) {
52290
53583
  try {
52291
- const body = await req.json();
52292
- if (!body.name)
52293
- return json3({ error: "Missing 'name'" }, 400);
53584
+ const body2 = await req.json();
53585
+ if (!body2.name)
53586
+ return json4({ error: "Missing 'name'" }, 400);
52294
53587
  const plan = createPlan({
52295
- name: body.name,
52296
- slug: body.slug,
52297
- description: body.description,
52298
- project_id: body.project_id,
52299
- task_list_id: body.task_list_id,
52300
- agent_id: body.agent_id,
52301
- status: body.status
52302
- });
52303
- return json3(plan, 201);
53588
+ name: body2.name,
53589
+ slug: body2.slug,
53590
+ description: body2.description,
53591
+ project_id: body2.project_id,
53592
+ task_list_id: body2.task_list_id,
53593
+ agent_id: body2.agent_id,
53594
+ status: body2.status
53595
+ });
53596
+ return json4(plan, 201);
52304
53597
  } catch (e) {
52305
- return json3({ error: e instanceof Error ? e.message : "Failed to create plan" }, 500);
53598
+ return json4({ error: e instanceof Error ? e.message : "Failed to create plan" }, 500);
52306
53599
  }
52307
53600
  }
52308
- async function handleBulkDeletePlans(req, _ctx, json3) {
53601
+ async function handleBulkDeletePlans(req, _ctx, json4) {
52309
53602
  try {
52310
- const body = await req.json();
52311
- if (!body.ids?.length || body.action !== "delete")
52312
- return json3({ error: "Missing ids or invalid action" }, 400);
53603
+ const body2 = await req.json();
53604
+ if (!body2.ids?.length || body2.action !== "delete")
53605
+ return json4({ error: "Missing ids or invalid action" }, 400);
52313
53606
  let succeeded = 0;
52314
- for (const id of body.ids) {
53607
+ for (const id of body2.ids) {
52315
53608
  if (deletePlan(id))
52316
53609
  succeeded++;
52317
53610
  }
52318
- return json3({ succeeded, failed: body.ids.length - succeeded });
53611
+ return json4({ succeeded, failed: body2.ids.length - succeeded });
52319
53612
  } catch (e) {
52320
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53613
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52321
53614
  }
52322
53615
  }
52323
- function handleGetPlan(id, _ctx, json3, taskToSummary2) {
53616
+ function handleGetPlan(id, _ctx, json4, taskToSummary2) {
52324
53617
  const plan = getPlan(id);
52325
53618
  if (!plan)
52326
- return json3({ error: "Plan not found" }, 404);
53619
+ return json4({ error: "Plan not found" }, 404);
52327
53620
  const tasks = listTasks({ plan_id: id });
52328
- return json3({ ...plan, tasks: tasks.map((t) => taskToSummary2(t)) });
53621
+ return json4({ ...plan, tasks: tasks.map((t) => taskToSummary2(t)) });
52329
53622
  }
52330
- async function handleUpdatePlan(id, req, _ctx, json3) {
53623
+ async function handleUpdatePlan(id, req, _ctx, json4) {
52331
53624
  try {
52332
- const body = await req.json();
52333
- const plan = updatePlan(id, body);
52334
- return json3(plan);
53625
+ const body2 = await req.json();
53626
+ const plan = updatePlan(id, body2);
53627
+ return json4(plan);
52335
53628
  } catch (e) {
52336
- return json3({ error: e instanceof Error ? e.message : "Failed to update plan" }, 500);
53629
+ return json4({ error: e instanceof Error ? e.message : "Failed to update plan" }, 500);
52337
53630
  }
52338
53631
  }
52339
- function handleDeletePlan(id, _ctx, json3) {
53632
+ function handleDeletePlan(id, _ctx, json4) {
52340
53633
  const deleted = deletePlan(id);
52341
53634
  if (!deleted)
52342
- return json3({ error: "Plan not found" }, 404);
52343
- return json3({ success: true });
53635
+ return json4({ error: "Plan not found" }, 404);
53636
+ return json4({ success: true });
52344
53637
  }
52345
- function handleStaticFiles(path, method, ctx, json3, serveStaticFile2) {
53638
+ function handleStaticFiles(path, method, ctx, json4, serveStaticFile2) {
52346
53639
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
52347
53640
  return null;
52348
53641
  if (path !== "/") {
@@ -52350,7 +53643,7 @@ function handleStaticFiles(path, method, ctx, json3, serveStaticFile2) {
52350
53643
  const resolvedFile = resolve16(filePath);
52351
53644
  const resolvedBase = resolve16(ctx.dashboardDir);
52352
53645
  if (!resolvedFile.startsWith(resolvedBase + sep3) && resolvedFile !== resolvedBase) {
52353
- return json3({ error: "Forbidden" }, 403);
53646
+ return json4({ error: "Forbidden" }, 403);
52354
53647
  }
52355
53648
  const res2 = serveStaticFile2(filePath);
52356
53649
  if (res2)
@@ -52399,6 +53692,9 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
52399
53692
  schemas: {
52400
53693
  Task: taskSchema,
52401
53694
  Project: projectSchema,
53695
+ TaskManifestBindingLookupRequest: taskManifestBindingLookupRequestSchema,
53696
+ TaskManifestBindingLookupResult: taskManifestBindingLookupResultSchema,
53697
+ TaskManifestBindingLookupResponse: taskManifestBindingLookupResponseSchema,
52402
53698
  TaskList: taskListSchema,
52403
53699
  ProjectTaskListEnsureReceipt: projectTaskListEnsureReceiptSchema,
52404
53700
  ProjectTaskListEnsureResult: projectTaskListEnsureResultSchema,
@@ -53374,6 +54670,32 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
53374
54670
  },
53375
54671
  security: [{ apiKey: [] }],
53376
54672
  paths: {
54673
+ "/v1/task-manifest/bindings/lookup": {
54674
+ post: {
54675
+ operationId: "lookupTaskManifestBinding",
54676
+ summary: "Recover one exact task-manifest apply receipt from its managed plan id",
54677
+ requestBody: {
54678
+ required: true,
54679
+ content: {
54680
+ "application/json": {
54681
+ schema: { $ref: "#/components/schemas/TaskManifestBindingLookupRequest" }
54682
+ }
54683
+ }
54684
+ },
54685
+ responses: {
54686
+ "200": {
54687
+ content: {
54688
+ "application/json": {
54689
+ schema: { $ref: "#/components/schemas/TaskManifestBindingLookupResponse" }
54690
+ }
54691
+ }
54692
+ },
54693
+ "400": { content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } } },
54694
+ "404": { content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } } },
54695
+ "409": { content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } } }
54696
+ }
54697
+ }
54698
+ },
53377
54699
  "/v1/tasks": {
53378
54700
  get: {
53379
54701
  operationId: "listTasks",
@@ -54104,7 +55426,7 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
54104
55426
  }
54105
55427
  };
54106
55428
  }
54107
- var taskSchema, projectSchema, taskListSchema, projectTaskListEnsureReceiptSchema, projectTaskListEnsureResultSchema, projectTaskListRollbackResultSchema, taskCommentSchema, taskGitRefSchema, planSchema, planProjectLinkReceiptSchema, planProjectLinkResultSchema, planProjectLinkRollbackResultSchema, templateTaskSchema, templateSchema, templateVariableSchema, createTemplateTaskInputSchema;
55429
+ var taskSchema, projectSchema, taskManifestBindingLookupRequestSchema, taskManifestBindingLookupResultSchema, taskManifestBindingLookupResponseSchema, taskListSchema, projectTaskListEnsureReceiptSchema, projectTaskListEnsureResultSchema, projectTaskListRollbackResultSchema, taskCommentSchema, taskGitRefSchema, planSchema, planProjectLinkReceiptSchema, planProjectLinkResultSchema, planProjectLinkRollbackResultSchema, templateTaskSchema, templateSchema, templateVariableSchema, createTemplateTaskInputSchema;
54108
55430
  var init_openapi = __esm(() => {
54109
55431
  init_package_version();
54110
55432
  init_types();
@@ -54140,6 +55462,51 @@ var init_openapi = __esm(() => {
54140
55462
  updated_at: { type: "string" }
54141
55463
  }
54142
55464
  };
55465
+ taskManifestBindingLookupRequestSchema = {
55466
+ type: "object",
55467
+ additionalProperties: false,
55468
+ required: ["authority", "route", "schema_version", "tenant_id", "plan_id", "max_items"],
55469
+ properties: {
55470
+ authority: { type: "string", enum: ["todos"] },
55471
+ route: { type: "string", enum: ["todos.task-manifest.v1"] },
55472
+ schema_version: { type: "integer", enum: [1] },
55473
+ tenant_id: { type: "string", minLength: 1, maxLength: 200 },
55474
+ plan_id: { type: "string", format: "uuid" },
55475
+ max_items: { type: "integer", enum: [1] }
55476
+ }
55477
+ };
55478
+ taskManifestBindingLookupResultSchema = {
55479
+ type: "object",
55480
+ additionalProperties: false,
55481
+ required: [
55482
+ "authority",
55483
+ "route",
55484
+ "schema_version",
55485
+ "tenant_id",
55486
+ "plan_id",
55487
+ "apply_receipt_id",
55488
+ "binding_version",
55489
+ "state"
55490
+ ],
55491
+ properties: {
55492
+ authority: { type: "string", enum: ["todos"] },
55493
+ route: { type: "string", enum: ["todos.task-manifest.v1"] },
55494
+ schema_version: { type: "integer", enum: [1] },
55495
+ tenant_id: { type: "string" },
55496
+ plan_id: { type: "string", format: "uuid" },
55497
+ apply_receipt_id: { type: "string", format: "uuid" },
55498
+ binding_version: { type: "integer", minimum: 1 },
55499
+ state: { type: "string", enum: ["applied", "compensated"] }
55500
+ }
55501
+ };
55502
+ taskManifestBindingLookupResponseSchema = {
55503
+ type: "object",
55504
+ additionalProperties: false,
55505
+ required: ["result"],
55506
+ properties: {
55507
+ result: { $ref: "#/components/schemas/TaskManifestBindingLookupResult" }
55508
+ }
55509
+ };
54143
55510
  taskListSchema = {
54144
55511
  type: "object",
54145
55512
  properties: {
@@ -54425,8 +55792,8 @@ var exports_pr_groups = {};
54425
55792
  __export(exports_pr_groups, {
54426
55793
  handlePrGroupHttpRequest: () => handlePrGroupHttpRequest
54427
55794
  });
54428
- function json3(body, status = 200) {
54429
- return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS2 });
55795
+ function json4(body2, status2 = 200) {
55796
+ return new Response(JSON.stringify(body2), { status: status2, headers: JSON_HEADERS3 });
54430
55797
  }
54431
55798
  function errorStatus2(error) {
54432
55799
  switch (error.code) {
@@ -54460,26 +55827,26 @@ async function handlePrGroupHttpRequest(req, url, ledger, basePath, principal) {
54460
55827
  const method = req.method.toUpperCase();
54461
55828
  try {
54462
55829
  if (!groupId && method === "POST" && action === undefined) {
54463
- return json3({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
55830
+ return json4({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
54464
55831
  }
54465
55832
  if (groupId === "admit" && !action) {
54466
55833
  if (method !== "POST")
54467
- return json3({ error: "method not allowed" }, 405);
54468
- const body = await readJson2(req);
54469
- if (!body)
54470
- return json3({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
54471
- return json3(await ledger.admit(body), 201);
55834
+ return json4({ error: "method not allowed" }, 405);
55835
+ const body2 = await readJson2(req);
55836
+ if (!body2)
55837
+ return json4({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
55838
+ return json4(await ledger.admit(body2), 201);
54472
55839
  }
54473
55840
  if (!groupId)
54474
- return json3({ error: "PR group id is required", code: "PR_GROUP_INVALID_INPUT" }, 400);
55841
+ return json4({ error: "PR group id is required", code: "PR_GROUP_INVALID_INPUT" }, 400);
54475
55842
  if (!action && method === "GET") {
54476
- return json3({ view: await ledger.get(groupId) });
55843
+ return json4({ view: await ledger.get(groupId) });
54477
55844
  }
54478
55845
  if (action === "events") {
54479
55846
  if (method === "GET") {
54480
55847
  const limit = url.searchParams.has("limit") ? Number(url.searchParams.get("limit")) : undefined;
54481
55848
  const afterSequence = url.searchParams.has("after_sequence") ? Number(url.searchParams.get("after_sequence")) : undefined;
54482
- return json3({
55849
+ return json4({
54483
55850
  history: await ledger.events(groupId, {
54484
55851
  ...limit !== undefined ? { limit } : {},
54485
55852
  ...afterSequence !== undefined ? { after_sequence: afterSequence } : {}
@@ -54487,54 +55854,54 @@ async function handlePrGroupHttpRequest(req, url, ledger, basePath, principal) {
54487
55854
  });
54488
55855
  }
54489
55856
  if (method === "POST") {
54490
- const body = await readJson2(req);
54491
- if (!body)
54492
- return json3({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
54493
- return json3(await ledger.append({
54494
- ...body,
55857
+ const body2 = await readJson2(req);
55858
+ if (!body2)
55859
+ return json4({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
55860
+ return json4(await ledger.append({
55861
+ ...body2,
54495
55862
  group_id: groupId,
54496
55863
  authenticated_actor_id: principal?.actor_id ?? undefined,
54497
55864
  authenticated_actor_run_id: principal?.actor_run_id ?? undefined
54498
55865
  }), 201);
54499
55866
  }
54500
- return json3({ error: "method not allowed" }, 405);
55867
+ return json4({ error: "method not allowed" }, 405);
54501
55868
  }
54502
55869
  if (action === "recover") {
54503
55870
  if (method !== "POST")
54504
- return json3({ error: "method not allowed" }, 405);
54505
- const body = await readJson2(req);
54506
- if (!body)
54507
- return json3({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
54508
- return json3(await ledger.recover({
54509
- ...body,
55871
+ return json4({ error: "method not allowed" }, 405);
55872
+ const body2 = await readJson2(req);
55873
+ if (!body2)
55874
+ return json4({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
55875
+ return json4(await ledger.recover({
55876
+ ...body2,
54510
55877
  group_id: groupId
54511
55878
  }), 201);
54512
55879
  }
54513
- return json3({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
55880
+ return json4({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
54514
55881
  } catch (cause) {
54515
55882
  if (cause instanceof PrGroupLedgerError) {
54516
- return json3({
55883
+ return json4({
54517
55884
  error: cause.message,
54518
55885
  code: cause.code,
54519
55886
  details: cause.details,
54520
55887
  authoritative: true
54521
55888
  }, errorStatus2(cause));
54522
55889
  }
54523
- return json3({
55890
+ return json4({
54524
55891
  error: cause instanceof Error ? cause.message : "internal PR-group error",
54525
55892
  code: "PR_GROUP_ATOMICITY_UNAVAILABLE"
54526
55893
  }, 500);
54527
55894
  }
54528
55895
  }
54529
- var JSON_HEADERS2;
55896
+ var JSON_HEADERS3;
54530
55897
  var init_pr_groups = __esm(() => {
54531
55898
  init_types3();
54532
- JSON_HEADERS2 = { "Content-Type": "application/json" };
55899
+ JSON_HEADERS3 = { "Content-Type": "application/json" };
54533
55900
  });
54534
55901
 
54535
55902
  // src/lib/comment-cursor.ts
54536
- function encodeCommentCursor(comment) {
54537
- return Buffer.from(JSON.stringify({ created_at: comment.created_at, id: comment.id }), "utf8").toString("base64url");
55903
+ function encodeCommentCursor(comment2) {
55904
+ return Buffer.from(JSON.stringify({ created_at: comment2.created_at, id: comment2.id }), "utf8").toString("base64url");
54538
55905
  }
54539
55906
  function decodeCommentCursor(value) {
54540
55907
  if (value.length > MAX_COMMENT_CURSOR_LENGTH)
@@ -54556,26 +55923,26 @@ function decodeCommentCursor(value) {
54556
55923
  var MAX_COMMENT_CURSOR_LENGTH = 1024;
54557
55924
 
54558
55925
  // src/lib/project-task-list-ensure.ts
54559
- import { createHash as createHash16 } from "crypto";
54560
- function canonicalJson(value) {
55926
+ import { createHash as createHash17 } from "crypto";
55927
+ function canonicalJson2(value) {
54561
55928
  if (value === null || typeof value !== "object")
54562
55929
  return JSON.stringify(value);
54563
55930
  if (Array.isArray(value))
54564
- return `[${value.map(canonicalJson).join(",")}]`;
54565
- return `{${Object.entries(value).filter(([, item]) => item !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
55931
+ return `[${value.map(canonicalJson2).join(",")}]`;
55932
+ return `{${Object.entries(value).filter(([, item]) => item !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key2, item]) => `${JSON.stringify(key2)}:${canonicalJson2(item)}`).join(",")}}`;
54566
55933
  }
54567
55934
  function digest(value) {
54568
- return createHash16("sha256").update(canonicalJson(value)).digest("hex");
55935
+ return createHash17("sha256").update(canonicalJson2(value)).digest("hex");
54569
55936
  }
54570
55937
  function deriveIdempotencyKey(projectId, slug) {
54571
55938
  return `ptlk_${digest({ project_id: projectId, slug }).slice(0, 48)}`;
54572
55939
  }
54573
55940
  function normalizeIdempotencyKey(value, projectId, slug) {
54574
- const key = value?.trim() || deriveIdempotencyKey(projectId, slug);
54575
- if (key.length < 8 || key.length > 128 || !/^[A-Za-z0-9._:-]+$/.test(key)) {
55941
+ const key2 = value?.trim() || deriveIdempotencyKey(projectId, slug);
55942
+ if (key2.length < 8 || key2.length > 128 || !/^[A-Za-z0-9._:-]+$/.test(key2)) {
54576
55943
  throw new ProjectTaskListEnsureError("PROJECT_TASK_LIST_IDEMPOTENCY_KEY_INVALID", "idempotency_key must be 8-128 ASCII letters, digits, dots, underscores, colons, or hyphens");
54577
55944
  }
54578
- return key;
55945
+ return key2;
54579
55946
  }
54580
55947
  function receiptId2(projectId, slug, idempotencyKey) {
54581
55948
  return `ptlr_${digest({ project_id: projectId, slug, idempotency_key: idempotencyKey }).slice(0, 48)}`;
@@ -54838,7 +56205,7 @@ async function exactPlanProjectLinkState(store, planId, projectId) {
54838
56205
  }
54839
56206
  async function planPlanProjectLink(store, planId, projectId) {
54840
56207
  const state = await exactPlanProjectLinkState(store, planId, projectId);
54841
- const alreadyLinked = state.plan.project_id === state.project.id && state.tasks.every((task2) => task2.project_id === state.project.id);
56208
+ const alreadyLinked = state.plan.project_id === state.project.id && state.tasks.every((task3) => task3.project_id === state.project.id);
54842
56209
  return {
54843
56210
  mode: "plan",
54844
56211
  action: alreadyLinked ? "already_linked" : "would_link",
@@ -54850,14 +56217,14 @@ async function applyPlanProjectLink(store, planId, projectId, options) {
54850
56217
  if (!store.planProjectLinks) {
54851
56218
  throw new PlanProjectLinkError("PLAN_PROJECT_LINK_UNSUPPORTED", "This storage backend cannot atomically link an existing plan and its tasks", { storage_kind: store.kind });
54852
56219
  }
54853
- const key = normalizePlanProjectLinkIdempotencyKey(options.idempotency_key);
56220
+ const key2 = normalizePlanProjectLinkIdempotencyKey(options.idempotency_key);
54854
56221
  return store.planProjectLinks.apply({
54855
56222
  plan_id: planId,
54856
56223
  project_id: projectId,
54857
56224
  expected_plan_revision: options.expected_plan_revision,
54858
56225
  expected_project_revision: options.expected_project_revision,
54859
- idempotency_key: key,
54860
- receipt_id: planProjectLinkReceiptId(key),
56226
+ idempotency_key: key2,
56227
+ receipt_id: planProjectLinkReceiptId(key2),
54861
56228
  created_at: new Date().toISOString()
54862
56229
  });
54863
56230
  }
@@ -54886,11 +56253,11 @@ __export(exports_v1, {
54886
56253
  handleV1Request: () => handleV1Request,
54887
56254
  countSnapshotRecords: () => countSnapshotRecords
54888
56255
  });
54889
- function json4(body, status = 200) {
54890
- return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS3 });
56256
+ function json5(body2, status2 = 200) {
56257
+ return new Response(JSON.stringify(body2), { status: status2, headers: JSON_HEADERS4 });
54891
56258
  }
54892
- function error(status, message, extra) {
54893
- return json4({ error: message, ...extra ?? {} }, status);
56259
+ function error(status2, message, extra) {
56260
+ return json5({ error: message, ...extra ?? {} }, status2);
54894
56261
  }
54895
56262
  function enumQueryParam(url, name, vocabulary) {
54896
56263
  const raw = url.searchParams.get(name);
@@ -54919,112 +56286,112 @@ function parseSinceCursor(raw) {
54919
56286
  function validateTaskCompletion(value) {
54920
56287
  if (!value || typeof value !== "object" || Array.isArray(value))
54921
56288
  return { ok: false, message: "completion body must be an object" };
54922
- const body = value;
56289
+ const body2 = value;
54923
56290
  const allowed = new Set(["agent_id", "attachment_ids", "files_changed", "test_results", "commit_hash", "notes", "confidence"]);
54924
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56291
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
54925
56292
  if (unknown)
54926
56293
  return { ok: false, message: `unknown completion field: ${unknown}` };
54927
- if (body.agent_id !== undefined && (typeof body.agent_id !== "string" || !body.agent_id.trim())) {
56294
+ if (body2.agent_id !== undefined && (typeof body2.agent_id !== "string" || !body2.agent_id.trim())) {
54928
56295
  return { ok: false, message: "agent_id must be a non-empty string" };
54929
56296
  }
54930
56297
  for (const field of ["attachment_ids", "files_changed"]) {
54931
- const value2 = body[field];
56298
+ const value2 = body2[field];
54932
56299
  if (value2 !== undefined && (!Array.isArray(value2) || value2.some((item) => typeof item !== "string" || !item.trim()))) {
54933
56300
  return { ok: false, message: `${field} must be an array of non-empty strings` };
54934
56301
  }
54935
56302
  }
54936
56303
  for (const field of ["test_results", "commit_hash", "notes"]) {
54937
- if (body[field] !== undefined && typeof body[field] !== "string") {
56304
+ if (body2[field] !== undefined && typeof body2[field] !== "string") {
54938
56305
  return { ok: false, message: `${field} must be a string` };
54939
56306
  }
54940
56307
  }
54941
- if (body.confidence !== undefined && (typeof body.confidence !== "number" || !Number.isFinite(body.confidence) || body.confidence < 0 || body.confidence > 1)) {
56308
+ if (body2.confidence !== undefined && (typeof body2.confidence !== "number" || !Number.isFinite(body2.confidence) || body2.confidence < 0 || body2.confidence > 1)) {
54942
56309
  return { ok: false, message: "confidence must be a number between 0 and 1" };
54943
56310
  }
54944
56311
  return {
54945
56312
  ok: true,
54946
- ...typeof body.agent_id === "string" ? { agentId: body.agent_id } : {},
56313
+ ...typeof body2.agent_id === "string" ? { agentId: body2.agent_id } : {},
54947
56314
  options: {
54948
- ...Array.isArray(body.attachment_ids) ? { attachment_ids: body.attachment_ids } : {},
54949
- ...Array.isArray(body.files_changed) ? { files_changed: body.files_changed } : {},
54950
- ...typeof body.test_results === "string" ? { test_results: body.test_results } : {},
54951
- ...typeof body.commit_hash === "string" ? { commit_hash: body.commit_hash } : {},
54952
- ...typeof body.notes === "string" ? { notes: body.notes } : {},
54953
- ...typeof body.confidence === "number" ? { confidence: body.confidence } : {}
56315
+ ...Array.isArray(body2.attachment_ids) ? { attachment_ids: body2.attachment_ids } : {},
56316
+ ...Array.isArray(body2.files_changed) ? { files_changed: body2.files_changed } : {},
56317
+ ...typeof body2.test_results === "string" ? { test_results: body2.test_results } : {},
56318
+ ...typeof body2.commit_hash === "string" ? { commit_hash: body2.commit_hash } : {},
56319
+ ...typeof body2.notes === "string" ? { notes: body2.notes } : {},
56320
+ ...typeof body2.confidence === "number" ? { confidence: body2.confidence } : {}
54954
56321
  }
54955
56322
  };
54956
56323
  }
54957
56324
  function validateProjectPatch(value) {
54958
56325
  if (!value || typeof value !== "object" || Array.isArray(value))
54959
56326
  return { ok: false, message: "project patch must be an object" };
54960
- const body = value;
56327
+ const body2 = value;
54961
56328
  const allowed = new Set(["name", "path", "description"]);
54962
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56329
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
54963
56330
  if (unknown)
54964
56331
  return { ok: false, message: `unknown project field: ${unknown}` };
54965
- if (Object.keys(body).length === 0)
56332
+ if (Object.keys(body2).length === 0)
54966
56333
  return { ok: false, message: "project patch must not be empty" };
54967
- if (body["name"] !== undefined && (typeof body["name"] !== "string" || !body["name"].trim()))
56334
+ if (body2["name"] !== undefined && (typeof body2["name"] !== "string" || !body2["name"].trim()))
54968
56335
  return { ok: false, message: "name must be a non-empty string" };
54969
- if (body["path"] !== undefined && (typeof body["path"] !== "string" || !body["path"].trim()))
56336
+ if (body2["path"] !== undefined && (typeof body2["path"] !== "string" || !body2["path"].trim()))
54970
56337
  return { ok: false, message: "path must be a non-empty string" };
54971
- if (body["description"] !== undefined && body["description"] !== null && typeof body["description"] !== "string")
56338
+ if (body2["description"] !== undefined && body2["description"] !== null && typeof body2["description"] !== "string")
54972
56339
  return { ok: false, message: "description must be a string or null" };
54973
- return { ok: true, patch: body };
56340
+ return { ok: true, patch: body2 };
54974
56341
  }
54975
56342
  function validateProjectCreate(value) {
54976
56343
  if (!value || typeof value !== "object" || Array.isArray(value))
54977
56344
  return { ok: false, message: "project body must be an object" };
54978
- const body = value;
56345
+ const body2 = value;
54979
56346
  const allowed = new Set(["name", "path", "description", "task_list_id", "task_prefix"]);
54980
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56347
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
54981
56348
  if (unknown)
54982
56349
  return { ok: false, message: `unknown project field: ${unknown}` };
54983
- if (typeof body["name"] !== "string" || !body["name"].trim())
56350
+ if (typeof body2["name"] !== "string" || !body2["name"].trim())
54984
56351
  return { ok: false, message: "name must be a non-empty string" };
54985
- if (!normalizeSlug(body["name"]))
56352
+ if (!normalizeSlug(body2["name"]))
54986
56353
  return { ok: false, message: "name must produce a non-empty canonical slug" };
54987
- if (typeof body["path"] !== "string" || !body["path"].trim())
56354
+ if (typeof body2["path"] !== "string" || !body2["path"].trim())
54988
56355
  return { ok: false, message: "path must be a non-empty string" };
54989
- if (body["description"] !== undefined && typeof body["description"] !== "string")
56356
+ if (body2["description"] !== undefined && typeof body2["description"] !== "string")
54990
56357
  return { ok: false, message: "description must be a string" };
54991
- if (body["task_list_id"] !== undefined && !isCanonicalSlug(body["task_list_id"])) {
56358
+ if (body2["task_list_id"] !== undefined && !isCanonicalSlug(body2["task_list_id"])) {
54992
56359
  return { ok: false, message: "task_list_id must be non-empty canonical kebab-case" };
54993
56360
  }
54994
- if (body["task_prefix"] !== undefined && (typeof body["task_prefix"] !== "string" || !body["task_prefix"].trim())) {
56361
+ if (body2["task_prefix"] !== undefined && (typeof body2["task_prefix"] !== "string" || !body2["task_prefix"].trim())) {
54995
56362
  return { ok: false, message: "task_prefix must be a non-empty string" };
54996
56363
  }
54997
- return { ok: true, input: body };
56364
+ return { ok: true, input: body2 };
54998
56365
  }
54999
56366
  function validatePlanCreate(value) {
55000
56367
  if (!value || typeof value !== "object" || Array.isArray(value))
55001
56368
  return { ok: false, message: "plan body must be an object" };
55002
- const body = value;
56369
+ const body2 = value;
55003
56370
  const allowed = new Set(["title", "name", "slug", "description", "project_id", "task_list_id", "agent_id", "status"]);
55004
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56371
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
55005
56372
  if (unknown)
55006
56373
  return { ok: false, message: `unknown plan field: ${unknown}` };
55007
- if (body.name !== undefined && (typeof body.name !== "string" || !body.name.trim()))
56374
+ if (body2.name !== undefined && (typeof body2.name !== "string" || !body2.name.trim()))
55008
56375
  return { ok: false, message: "name must be a non-empty string" };
55009
- if (body.title !== undefined && (typeof body.title !== "string" || !body.title.trim()))
56376
+ if (body2.title !== undefined && (typeof body2.title !== "string" || !body2.title.trim()))
55010
56377
  return { ok: false, message: "title must be a non-empty string" };
55011
- if (typeof body.name === "string" && typeof body.title === "string" && body.name !== body.title) {
56378
+ if (typeof body2.name === "string" && typeof body2.title === "string" && body2.name !== body2.title) {
55012
56379
  return { ok: false, message: "name and title must match when both are provided" };
55013
56380
  }
55014
- const name = body.name ?? body.title;
56381
+ const name = body2.name ?? body2.title;
55015
56382
  if (!name)
55016
56383
  return { ok: false, message: "name is required" };
55017
56384
  for (const field of ["slug", "project_id", "task_list_id", "agent_id"]) {
55018
- if (body[field] !== undefined && (typeof body[field] !== "string" || !body[field].trim())) {
56385
+ if (body2[field] !== undefined && (typeof body2[field] !== "string" || !body2[field].trim())) {
55019
56386
  return { ok: false, message: `${field} must be a non-empty string` };
55020
56387
  }
55021
56388
  }
55022
- const slug = typeof body.slug === "string" ? normalizeSlug(body.slug) : undefined;
55023
- if (body.slug !== undefined && !slug)
56389
+ const slug = typeof body2.slug === "string" ? normalizeSlug(body2.slug) : undefined;
56390
+ if (body2.slug !== undefined && !slug)
55024
56391
  return { ok: false, message: "slug must produce a non-empty canonical slug" };
55025
- if (body.description !== undefined && typeof body.description !== "string")
56392
+ if (body2.description !== undefined && typeof body2.description !== "string")
55026
56393
  return { ok: false, message: "description must be a string" };
55027
- if (body.status !== undefined && (typeof body.status !== "string" || !["active", "completed", "archived"].includes(body.status))) {
56394
+ if (body2.status !== undefined && (typeof body2.status !== "string" || !["active", "completed", "archived"].includes(body2.status))) {
55028
56395
  return { ok: false, message: "status must be active, completed, or archived" };
55029
56396
  }
55030
56397
  return {
@@ -55032,102 +56399,102 @@ function validatePlanCreate(value) {
55032
56399
  input: {
55033
56400
  name,
55034
56401
  ...slug ? { slug } : {},
55035
- ...typeof body.description === "string" ? { description: body.description } : {},
55036
- ...typeof body.project_id === "string" ? { project_id: body.project_id } : {},
55037
- ...typeof body.task_list_id === "string" ? { task_list_id: body.task_list_id } : {},
55038
- ...typeof body.agent_id === "string" ? { agent_id: body.agent_id } : {},
55039
- ...typeof body.status === "string" ? { status: body.status } : {}
56402
+ ...typeof body2.description === "string" ? { description: body2.description } : {},
56403
+ ...typeof body2.project_id === "string" ? { project_id: body2.project_id } : {},
56404
+ ...typeof body2.task_list_id === "string" ? { task_list_id: body2.task_list_id } : {},
56405
+ ...typeof body2.agent_id === "string" ? { agent_id: body2.agent_id } : {},
56406
+ ...typeof body2.status === "string" ? { status: body2.status } : {}
55040
56407
  }
55041
56408
  };
55042
56409
  }
55043
56410
  function validateTemplateTask(value) {
55044
56411
  if (!value || typeof value !== "object" || Array.isArray(value))
55045
56412
  return null;
55046
- const body = value;
56413
+ const body2 = value;
55047
56414
  const allowed = new Set(["position", "title_pattern", "description", "priority", "tags", "task_type", "condition", "include_template_id", "depends_on", "depends_on_positions", "metadata"]);
55048
- if (Object.keys(body).some((key) => !allowed.has(key)))
56415
+ if (Object.keys(body2).some((key2) => !allowed.has(key2)))
55049
56416
  return null;
55050
- if (typeof body.title_pattern !== "string" || !body.title_pattern.trim())
56417
+ if (typeof body2.title_pattern !== "string" || !body2.title_pattern.trim())
55051
56418
  return null;
55052
- if (body.position !== undefined && (typeof body.position !== "number" || !Number.isSafeInteger(body.position) || body.position < 0))
56419
+ if (body2.position !== undefined && (typeof body2.position !== "number" || !Number.isSafeInteger(body2.position) || body2.position < 0))
55053
56420
  return null;
55054
- if (body.description !== undefined && body.description !== null && typeof body.description !== "string")
56421
+ if (body2.description !== undefined && body2.description !== null && typeof body2.description !== "string")
55055
56422
  return null;
55056
- if (body.priority !== undefined && (typeof body.priority !== "string" || !["low", "medium", "high", "critical"].includes(body.priority)))
56423
+ if (body2.priority !== undefined && (typeof body2.priority !== "string" || !["low", "medium", "high", "critical"].includes(body2.priority)))
55057
56424
  return null;
55058
- if (body.tags !== undefined && (!Array.isArray(body.tags) || body.tags.some((tag) => typeof tag !== "string" || !tag.trim())))
56425
+ if (body2.tags !== undefined && (!Array.isArray(body2.tags) || body2.tags.some((tag) => typeof tag !== "string" || !tag.trim())))
55059
56426
  return null;
55060
56427
  for (const field of ["task_type", "condition", "include_template_id"]) {
55061
- if (body[field] !== undefined && body[field] !== null && (typeof body[field] !== "string" || !body[field].trim()))
56428
+ if (body2[field] !== undefined && body2[field] !== null && (typeof body2[field] !== "string" || !body2[field].trim()))
55062
56429
  return null;
55063
56430
  }
55064
- if (body.depends_on !== undefined && body.depends_on_positions !== undefined)
56431
+ if (body2.depends_on !== undefined && body2.depends_on_positions !== undefined)
55065
56432
  return null;
55066
- const dependencies = body.depends_on ?? body.depends_on_positions;
56433
+ const dependencies = body2.depends_on ?? body2.depends_on_positions;
55067
56434
  if (dependencies !== undefined && (!Array.isArray(dependencies) || dependencies.some((position) => !Number.isSafeInteger(position) || position < 0)))
55068
56435
  return null;
55069
- if (body.metadata !== undefined && (!body.metadata || typeof body.metadata !== "object" || Array.isArray(body.metadata)))
56436
+ if (body2.metadata !== undefined && (!body2.metadata || typeof body2.metadata !== "object" || Array.isArray(body2.metadata)))
55070
56437
  return null;
55071
56438
  return {
55072
- title_pattern: body.title_pattern,
55073
- ...typeof body.description === "string" ? { description: body.description } : {},
55074
- ...typeof body.priority === "string" ? { priority: body.priority } : {},
55075
- ...Array.isArray(body.tags) ? { tags: body.tags } : {},
55076
- ...typeof body.task_type === "string" ? { task_type: body.task_type } : {},
55077
- ...typeof body.condition === "string" ? { condition: body.condition } : {},
55078
- ...typeof body.include_template_id === "string" ? { include_template_id: body.include_template_id } : {},
56439
+ title_pattern: body2.title_pattern,
56440
+ ...typeof body2.description === "string" ? { description: body2.description } : {},
56441
+ ...typeof body2.priority === "string" ? { priority: body2.priority } : {},
56442
+ ...Array.isArray(body2.tags) ? { tags: body2.tags } : {},
56443
+ ...typeof body2.task_type === "string" ? { task_type: body2.task_type } : {},
56444
+ ...typeof body2.condition === "string" ? { condition: body2.condition } : {},
56445
+ ...typeof body2.include_template_id === "string" ? { include_template_id: body2.include_template_id } : {},
55079
56446
  ...Array.isArray(dependencies) ? { depends_on: dependencies } : {},
55080
- ...body.metadata && typeof body.metadata === "object" && !Array.isArray(body.metadata) ? { metadata: body.metadata } : {}
56447
+ ...body2.metadata && typeof body2.metadata === "object" && !Array.isArray(body2.metadata) ? { metadata: body2.metadata } : {}
55081
56448
  };
55082
56449
  }
55083
56450
  function validateTemplateCreate(value) {
55084
56451
  if (!value || typeof value !== "object" || Array.isArray(value))
55085
56452
  return { ok: false, message: "template body must be an object" };
55086
- const body = value;
56453
+ const body2 = value;
55087
56454
  const allowed = new Set(["name", "title_pattern", "description", "priority", "tags", "variables", "project_id", "plan_id", "metadata", "tasks"]);
55088
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56455
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
55089
56456
  if (unknown)
55090
56457
  return { ok: false, message: `unknown template field: ${unknown}` };
55091
- if (typeof body.name !== "string" || !body.name.trim())
56458
+ if (typeof body2.name !== "string" || !body2.name.trim())
55092
56459
  return { ok: false, message: "name must be a non-empty string" };
55093
- if (typeof body.title_pattern !== "string" || !body.title_pattern.trim())
56460
+ if (typeof body2.title_pattern !== "string" || !body2.title_pattern.trim())
55094
56461
  return { ok: false, message: "title_pattern must be a non-empty string" };
55095
- if (body.description !== undefined && body.description !== null && typeof body.description !== "string")
56462
+ if (body2.description !== undefined && body2.description !== null && typeof body2.description !== "string")
55096
56463
  return { ok: false, message: "description must be a string or null" };
55097
- if (body.priority !== undefined && (typeof body.priority !== "string" || !["low", "medium", "high", "critical"].includes(body.priority)))
56464
+ if (body2.priority !== undefined && (typeof body2.priority !== "string" || !["low", "medium", "high", "critical"].includes(body2.priority)))
55098
56465
  return { ok: false, message: "priority must be low, medium, high, or critical" };
55099
- if (body.tags !== undefined && (!Array.isArray(body.tags) || body.tags.some((tag) => typeof tag !== "string" || !tag.trim())))
56466
+ if (body2.tags !== undefined && (!Array.isArray(body2.tags) || body2.tags.some((tag) => typeof tag !== "string" || !tag.trim())))
55100
56467
  return { ok: false, message: "tags must be an array of non-empty strings" };
55101
- if (body.variables !== undefined && (!Array.isArray(body.variables) || body.variables.some((variable) => !variable || typeof variable !== "object" || Array.isArray(variable) || typeof variable.name !== "string" || !variable.name || typeof variable.required !== "boolean" || variable.default !== undefined && typeof variable.default !== "string" || variable.description !== undefined && typeof variable.description !== "string"))) {
56468
+ if (body2.variables !== undefined && (!Array.isArray(body2.variables) || body2.variables.some((variable) => !variable || typeof variable !== "object" || Array.isArray(variable) || typeof variable.name !== "string" || !variable.name || typeof variable.required !== "boolean" || variable.default !== undefined && typeof variable.default !== "string" || variable.description !== undefined && typeof variable.description !== "string"))) {
55102
56469
  return { ok: false, message: "variables must be valid template variable objects" };
55103
56470
  }
55104
56471
  for (const field of ["project_id", "plan_id"]) {
55105
- if (body[field] !== undefined && body[field] !== null && (typeof body[field] !== "string" || !body[field].trim()))
56472
+ if (body2[field] !== undefined && body2[field] !== null && (typeof body2[field] !== "string" || !body2[field].trim()))
55106
56473
  return { ok: false, message: `${field} must be a non-empty string or null` };
55107
56474
  }
55108
- if (body.metadata !== undefined && (!body.metadata || typeof body.metadata !== "object" || Array.isArray(body.metadata)))
56475
+ if (body2.metadata !== undefined && (!body2.metadata || typeof body2.metadata !== "object" || Array.isArray(body2.metadata)))
55109
56476
  return { ok: false, message: "metadata must be an object" };
55110
- const tasks = body.tasks === undefined ? [] : Array.isArray(body.tasks) ? body.tasks.map(validateTemplateTask) : null;
55111
- if (tasks === null || tasks.some((task2) => task2 === null))
56477
+ const tasks = body2.tasks === undefined ? [] : Array.isArray(body2.tasks) ? body2.tasks.map(validateTemplateTask) : null;
56478
+ if (tasks === null || tasks.some((task3) => task3 === null))
55112
56479
  return { ok: false, message: "tasks must be valid template task objects" };
55113
56480
  const taskInputs = tasks;
55114
- for (const [position, task2] of taskInputs.entries()) {
55115
- if ((task2.depends_on ?? []).some((dependency) => dependency >= position)) {
56481
+ for (const [position, task3] of taskInputs.entries()) {
56482
+ if ((task3.depends_on ?? []).some((dependency2) => dependency2 >= position)) {
55116
56483
  return { ok: false, message: "template task dependencies must reference earlier task positions" };
55117
56484
  }
55118
56485
  }
55119
56486
  return {
55120
56487
  ok: true,
55121
56488
  input: {
55122
- name: body.name,
55123
- title_pattern: body.title_pattern,
55124
- ...typeof body.description === "string" ? { description: body.description } : {},
55125
- ...typeof body.priority === "string" ? { priority: body.priority } : {},
55126
- ...Array.isArray(body.tags) ? { tags: body.tags } : {},
55127
- ...Array.isArray(body.variables) ? { variables: body.variables } : {},
55128
- ...typeof body.project_id === "string" ? { project_id: body.project_id } : {},
55129
- ...typeof body.plan_id === "string" ? { plan_id: body.plan_id } : {},
55130
- ...body.metadata && typeof body.metadata === "object" && !Array.isArray(body.metadata) ? { metadata: body.metadata } : {},
56489
+ name: body2.name,
56490
+ title_pattern: body2.title_pattern,
56491
+ ...typeof body2.description === "string" ? { description: body2.description } : {},
56492
+ ...typeof body2.priority === "string" ? { priority: body2.priority } : {},
56493
+ ...Array.isArray(body2.tags) ? { tags: body2.tags } : {},
56494
+ ...Array.isArray(body2.variables) ? { variables: body2.variables } : {},
56495
+ ...typeof body2.project_id === "string" ? { project_id: body2.project_id } : {},
56496
+ ...typeof body2.plan_id === "string" ? { plan_id: body2.plan_id } : {},
56497
+ ...body2.metadata && typeof body2.metadata === "object" && !Array.isArray(body2.metadata) ? { metadata: body2.metadata } : {},
55131
56498
  tasks: taskInputs
55132
56499
  }
55133
56500
  };
@@ -55135,25 +56502,25 @@ function validateTemplateCreate(value) {
55135
56502
  function validateTemplatePatch(value) {
55136
56503
  if (!value || typeof value !== "object" || Array.isArray(value))
55137
56504
  return { ok: false, message: "template patch must be an object" };
55138
- const body = value;
56505
+ const body2 = value;
55139
56506
  const allowed = new Set(["name", "title_pattern", "description", "priority", "tags", "variables", "project_id", "plan_id", "metadata"]);
55140
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56507
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
55141
56508
  if (unknown)
55142
56509
  return { ok: false, message: `unknown template field: ${unknown}` };
55143
- if (Object.keys(body).length === 0)
56510
+ if (Object.keys(body2).length === 0)
55144
56511
  return { ok: false, message: "template patch must not be empty" };
55145
- const templateLike = { name: body.name ?? "template", title_pattern: body.title_pattern ?? "template", ...body };
56512
+ const templateLike = { name: body2.name ?? "template", title_pattern: body2.title_pattern ?? "template", ...body2 };
55146
56513
  const validated = validateTemplateCreate(templateLike);
55147
56514
  if (!validated.ok)
55148
56515
  return validated;
55149
56516
  const { name: _name, title_pattern: _title, tasks: _tasks, ...patch } = validated.input;
55150
56517
  return { ok: true, patch: {
55151
- ...body.name !== undefined ? { name: validated.input.name } : {},
55152
- ...body.title_pattern !== undefined ? { title_pattern: validated.input.title_pattern } : {},
56518
+ ...body2.name !== undefined ? { name: validated.input.name } : {},
56519
+ ...body2.title_pattern !== undefined ? { title_pattern: validated.input.title_pattern } : {},
55153
56520
  ...patch,
55154
- ...body.description === null ? { description: null } : {},
55155
- ...body.project_id === null ? { project_id: null } : {},
55156
- ...body.plan_id === null ? { plan_id: null } : {}
56521
+ ...body2.description === null ? { description: null } : {},
56522
+ ...body2.project_id === null ? { project_id: null } : {},
56523
+ ...body2.plan_id === null ? { plan_id: null } : {}
55157
56524
  } };
55158
56525
  }
55159
56526
  async function readJson3(req) {
@@ -55176,29 +56543,29 @@ async function readOptionalJson(req) {
55176
56543
  return { ok: false };
55177
56544
  }
55178
56545
  }
55179
- function contextFromPrincipal(principal, body) {
55180
- const agentId = body?.agent_id || principal.agent || undefined;
56546
+ function contextFromPrincipal(principal, body2) {
56547
+ const agentId = body2?.agent_id || principal.agent || undefined;
55181
56548
  return agentId ? { agentId } : {};
55182
56549
  }
55183
- function redactComment3(comment) {
55184
- return { ...comment, content: redactEvidenceText(comment.content) };
56550
+ function redactComment3(comment2) {
56551
+ return { ...comment2, content: redactEvidenceText(comment2.content) };
55185
56552
  }
55186
56553
  function normalizeImportSnapshot(raw) {
55187
- const body = raw && typeof raw === "object" ? raw : {};
56554
+ const body2 = raw && typeof raw === "object" ? raw : {};
55188
56555
  const arr = (v) => Array.isArray(v) ? v : [];
55189
56556
  return {
55190
- exportedAt: typeof body["exportedAt"] === "string" ? body["exportedAt"] : new Date().toISOString(),
55191
- source: typeof body["source"] === "string" ? body["source"] : "sqlite",
55192
- tasks: arr(body["tasks"]),
55193
- projects: arr(body["projects"]),
55194
- projectMachinePaths: arr(body["projectMachinePaths"]),
55195
- plans: arr(body["plans"]),
55196
- agents: arr(body["agents"]),
55197
- taskLists: arr(body["taskLists"]),
55198
- templates: arr(body["templates"]),
55199
- templateTasks: arr(body["templateTasks"]),
55200
- auditHistory: arr(body["auditHistory"]),
55201
- tombstones: arr(body["tombstones"])
56557
+ exportedAt: typeof body2["exportedAt"] === "string" ? body2["exportedAt"] : new Date().toISOString(),
56558
+ source: typeof body2["source"] === "string" ? body2["source"] : "sqlite",
56559
+ tasks: arr(body2["tasks"]),
56560
+ projects: arr(body2["projects"]),
56561
+ projectMachinePaths: arr(body2["projectMachinePaths"]),
56562
+ plans: arr(body2["plans"]),
56563
+ agents: arr(body2["agents"]),
56564
+ taskLists: arr(body2["taskLists"]),
56565
+ templates: arr(body2["templates"]),
56566
+ templateTasks: arr(body2["templateTasks"]),
56567
+ auditHistory: arr(body2["auditHistory"]),
56568
+ tombstones: arr(body2["tombstones"])
55202
56569
  };
55203
56570
  }
55204
56571
  function countSnapshotRecords(s) {
@@ -55229,6 +56596,9 @@ async function handleV1Request(req, url, dependencies = {}) {
55229
56596
  if (path === "/v1/project-registration" || path.startsWith("/v1/project-registration/")) {
55230
56597
  return handleTodosProjectRegistrationHttpRequest(req, url, (dependencies.getProjectRegistrationAuthority ?? getCloudProjectRegistrationAuthority)());
55231
56598
  }
56599
+ if (path === "/v1/task-manifest" || path.startsWith("/v1/task-manifest/")) {
56600
+ return handleTodosTaskManifestHttpRequest(req, url, (dependencies.getTaskManifestAuthority ?? getCloudTaskManifestAuthority)());
56601
+ }
55232
56602
  const store = (dependencies.getStorageAdapter ?? getCloudStorageAdapter)();
55233
56603
  const segments = path.split("/").filter(Boolean);
55234
56604
  const resource = segments[1];
@@ -55240,8 +56610,8 @@ async function handleV1Request(req, url, dependencies = {}) {
55240
56610
  if (id === "exists" && !action) {
55241
56611
  if (method !== "POST")
55242
56612
  return error(405, `method ${method} not allowed on /v1/tasks/exists`);
55243
- const body = await readJson3(req);
55244
- const ids2 = Array.isArray(body?.ids) ? Array.from(new Set(body.ids.filter((v) => typeof v === "string" && v.length > 0))) : [];
56613
+ const body2 = await readJson3(req);
56614
+ const ids2 = Array.isArray(body2?.ids) ? Array.from(new Set(body2.ids.filter((v) => typeof v === "string" && v.length > 0))) : [];
55245
56615
  if (ids2.length === 0)
55246
56616
  return error(400, "provide a non-empty string array `ids`");
55247
56617
  if (ids2.length > 5000)
@@ -55250,7 +56620,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55250
56620
  const presentSet = new Set(found.map((t) => t.id));
55251
56621
  const present = ids2.filter((i) => presentSet.has(i));
55252
56622
  const missing = ids2.filter((i) => !presentSet.has(i));
55253
- return json4({
56623
+ return json5({
55254
56624
  requested: ids2.length,
55255
56625
  present_count: present.length,
55256
56626
  missing_count: missing.length,
@@ -55263,20 +56633,20 @@ async function handleV1Request(req, url, dependencies = {}) {
55263
56633
  if (typeof store.tasks.getByFingerprint !== "function") {
55264
56634
  return error(501, "fingerprint upsert is not supported by this storage backend");
55265
56635
  }
55266
- const body = await readJson3(req) ?? {};
55267
- const fingerprint3 = typeof body.fingerprint === "string" ? body.fingerprint.trim() : "";
56636
+ const body2 = await readJson3(req) ?? {};
56637
+ const fingerprint3 = typeof body2.fingerprint === "string" ? body2.fingerprint.trim() : "";
55268
56638
  if (!fingerprint3)
55269
56639
  return error(400, "fingerprint is required");
55270
- if (typeof body.title !== "string" || !body.title.trim())
56640
+ if (typeof body2.title !== "string" || !body2.title.trim())
55271
56641
  return error(400, "title is required");
55272
56642
  const existing = await store.tasks.getByFingerprint(fingerprint3);
55273
56643
  const metadata = {
55274
56644
  ...existing?.metadata ?? {},
55275
- ...body.metadata ?? {},
56645
+ ...body2.metadata ?? {},
55276
56646
  fingerprint: fingerprint3
55277
56647
  };
55278
56648
  const fields = { metadata };
55279
- for (const key of [
56649
+ for (const key2 of [
55280
56650
  "title",
55281
56651
  "description",
55282
56652
  "priority",
@@ -55294,17 +56664,17 @@ async function handleV1Request(req, url, dependencies = {}) {
55294
56664
  "recurrence_rule",
55295
56665
  "task_type"
55296
56666
  ]) {
55297
- const bag = body;
55298
- if (bag[key] !== undefined)
55299
- fields[key] = bag[key];
56667
+ const bag = body2;
56668
+ if (bag[key2] !== undefined)
56669
+ fields[key2] = bag[key2];
55300
56670
  }
55301
56671
  if (!existing) {
55302
- const task2 = await store.tasks.create({ ...fields, title: body.title }, contextFromPrincipal(principal, body));
55303
- return json4({ task: task2, created: true }, 201);
56672
+ const task3 = await store.tasks.create({ ...fields, title: body2.title }, contextFromPrincipal(principal, body2));
56673
+ return json5({ task: task3, created: true }, 201);
55304
56674
  }
55305
56675
  try {
55306
- const task2 = await store.tasks.update(existing.id, { ...fields, version: existing.version }, contextFromPrincipal(principal, body));
55307
- return json4({ task: task2, created: false });
56676
+ const task3 = await store.tasks.update(existing.id, { ...fields, version: existing.version }, contextFromPrincipal(principal, body2));
56677
+ return json5({ task: task3, created: false });
55308
56678
  } catch (e) {
55309
56679
  const msg = e.message || "";
55310
56680
  if (msg.includes("version conflict"))
@@ -55352,32 +56722,32 @@ async function handleV1Request(req, url, dependencies = {}) {
55352
56722
  const tasks = await store.tasks.list(filter);
55353
56723
  const { limit: _l, offset: _o, ...countFilter } = filter;
55354
56724
  const total = await store.tasks.count(countFilter);
55355
- return json4({ tasks, count: tasks.length, total });
56725
+ return json5({ tasks, count: tasks.length, total });
55356
56726
  }
55357
56727
  if (method === "POST") {
55358
- const body = await readJson3(req);
55359
- if (!body || typeof body.title !== "string" || !body.title.trim()) {
56728
+ const body2 = await readJson3(req);
56729
+ if (!body2 || typeof body2.title !== "string" || !body2.title.trim()) {
55360
56730
  return error(400, "title is required");
55361
56731
  }
55362
- const storageContext = contextFromPrincipal(principal, body);
55363
- if (body.parent_id !== undefined) {
55364
- if (typeof body.parent_id !== "string" || !body.parent_id.trim()) {
56732
+ const storageContext = contextFromPrincipal(principal, body2);
56733
+ if (body2.parent_id !== undefined) {
56734
+ if (typeof body2.parent_id !== "string" || !body2.parent_id.trim()) {
55365
56735
  return error(400, "parent_id must be a non-empty task id", {
55366
56736
  code: "PARENT_TASK_ID_INVALID"
55367
56737
  });
55368
56738
  }
55369
- if (!await store.tasks.get(body.parent_id, storageContext)) {
55370
- return error(404, `parent task not found: ${body.parent_id}`, {
56739
+ if (!await store.tasks.get(body2.parent_id, storageContext)) {
56740
+ return error(404, `parent task not found: ${body2.parent_id}`, {
55371
56741
  code: "PARENT_TASK_NOT_FOUND"
55372
56742
  });
55373
56743
  }
55374
56744
  }
55375
- const created = await store.tasks.create(body, storageContext);
56745
+ const created = await store.tasks.create(body2, storageContext);
55376
56746
  const persisted = created?.id ? await store.tasks.get(created.id, storageContext) : null;
55377
- if (!persisted || persisted.id !== created.id || (persisted.parent_id ?? null) !== (body.parent_id ?? null)) {
56747
+ if (!persisted || persisted.id !== created.id || (persisted.parent_id ?? null) !== (body2.parent_id ?? null)) {
55378
56748
  return error(500, "TASK_CREATE_PERSISTENCE_UNVERIFIED: task create was acknowledged but authoritative readback did not return the same stored task id and parent_id", { code: "TASK_CREATE_PERSISTENCE_UNVERIFIED" });
55379
56749
  }
55380
- return json4({ task: persisted }, 201);
56750
+ return json5({ task: persisted }, 201);
55381
56751
  }
55382
56752
  return error(405, `method ${method} not allowed on /v1/tasks`);
55383
56753
  }
@@ -55394,7 +56764,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55394
56764
  if (legacyPage.length > LEGACY_COMMENT_RESPONSE_LIMIT) {
55395
56765
  return error(426, "task has too many comments for this client; upgrade @hasna/todos to use cursor pagination");
55396
56766
  }
55397
- return json4({
56767
+ return json5({
55398
56768
  comments: legacyPage,
55399
56769
  count: legacyPage.length,
55400
56770
  has_more: false,
@@ -55419,7 +56789,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55419
56789
  const page = (await store.audit.getCommentsPage(id, { limit: limit + 1, ...before ? { before } : {} }, contextFromPrincipal(principal))).map(redactComment3);
55420
56790
  const hasMore = page.length > limit;
55421
56791
  const comments = hasMore ? page.slice(1) : page;
55422
- return json4({
56792
+ return json5({
55423
56793
  comments,
55424
56794
  count: comments.length,
55425
56795
  has_more: hasMore,
@@ -55427,22 +56797,22 @@ async function handleV1Request(req, url, dependencies = {}) {
55427
56797
  });
55428
56798
  }
55429
56799
  if (method === "POST") {
55430
- const body2 = await readJson3(req) ?? {};
55431
- if (typeof body2.content !== "string" || !body2.content.trim()) {
56800
+ const body3 = await readJson3(req) ?? {};
56801
+ if (typeof body3.content !== "string" || !body3.content.trim()) {
55432
56802
  return error(400, "content is required");
55433
56803
  }
55434
56804
  const target = await store.tasks.get(id);
55435
56805
  if (!target)
55436
56806
  return error(404, "task not found");
55437
- const comment = await store.audit.addComment({
56807
+ const comment2 = await store.audit.addComment({
55438
56808
  task_id: id,
55439
- content: body2.content,
55440
- agent_id: body2.agent_id ?? principal.agent ?? undefined,
55441
- session_id: body2.session_id,
55442
- type: body2.type,
55443
- progress_pct: body2.progress_pct
55444
- }, contextFromPrincipal(principal, body2));
55445
- return json4({ comment: redactComment3(comment) }, 201);
56809
+ content: body3.content,
56810
+ agent_id: body3.agent_id ?? principal.agent ?? undefined,
56811
+ session_id: body3.session_id,
56812
+ type: body3.type,
56813
+ progress_pct: body3.progress_pct
56814
+ }, contextFromPrincipal(principal, body3));
56815
+ return json5({ comment: redactComment3(comment2) }, 201);
55446
56816
  }
55447
56817
  return error(405, `method ${method} not allowed on /v1/tasks/:id/comments`);
55448
56818
  }
@@ -55452,36 +56822,36 @@ async function handleV1Request(req, url, dependencies = {}) {
55452
56822
  if (!await store.tasks.get(id))
55453
56823
  return error(404, "task not found");
55454
56824
  const history = await store.audit.getTaskHistory(id);
55455
- return json4({ history, count: history.length });
56825
+ return json5({ history, count: history.length });
55456
56826
  }
55457
56827
  if (action === "lock" || action === "unlock") {
55458
56828
  if (method !== "POST")
55459
56829
  return error(405, `method ${method} not allowed on /v1/tasks/:id/${action}`);
55460
- const body2 = await readJson3(req) ?? {};
56830
+ const body3 = await readJson3(req) ?? {};
55461
56831
  if (!await store.tasks.get(id))
55462
56832
  return error(404, "task not found");
55463
56833
  if (action === "lock") {
55464
56834
  if (typeof store.tasks.lock !== "function")
55465
56835
  return error(501, "task locking is not supported by this storage backend");
55466
- const agentId3 = body2.agent_id || principal.agent || "todos-serve";
55467
- return json4({ result: await store.tasks.lock(id, agentId3) });
56836
+ const agentId3 = body3.agent_id || principal.agent || "todos-serve";
56837
+ return json5({ result: await store.tasks.lock(id, agentId3) });
55468
56838
  }
55469
56839
  if (typeof store.tasks.unlock !== "function")
55470
56840
  return error(501, "task unlocking is not supported by this storage backend");
55471
- if (body2.force === true) {
56841
+ if (body3.force === true) {
55472
56842
  if (!principal.scopes.includes("todos:*"))
55473
56843
  return error(403, "force unlock requires todos:* scope");
55474
56844
  const released2 = await store.tasks.unlock(id);
55475
- return json4({ success: released2 });
56845
+ return json5({ success: released2 });
55476
56846
  }
55477
- if (body2.agent_id && body2.agent_id !== principal.agent && !principal.scopes.includes("todos:*")) {
56847
+ if (body3.agent_id && body3.agent_id !== principal.agent && !principal.scopes.includes("todos:*")) {
55478
56848
  return error(403, "unlock agent_id must match the authenticated agent");
55479
56849
  }
55480
- const agentId2 = body2.agent_id || principal.agent;
56850
+ const agentId2 = body3.agent_id || principal.agent;
55481
56851
  if (!agentId2)
55482
56852
  return error(403, "unlock requires an agent-bound key or force=true");
55483
56853
  const released = await store.tasks.unlock(id, agentId2);
55484
- return json4({ success: released });
56854
+ return json5({ success: released });
55485
56855
  }
55486
56856
  if (action === "dependencies") {
55487
56857
  if (!store.dependencies)
@@ -55490,16 +56860,16 @@ async function handleV1Request(req, url, dependencies = {}) {
55490
56860
  if (!await store.tasks.get(id))
55491
56861
  return error(404, "task not found");
55492
56862
  const edges = await store.dependencies.list(id);
55493
- return json4(edges);
56863
+ return json5(edges);
55494
56864
  }
55495
56865
  if (method === "POST") {
55496
- const body2 = await readJson3(req) ?? {};
55497
- if (typeof body2.depends_on !== "string" || !body2.depends_on.trim()) {
56866
+ const body3 = await readJson3(req) ?? {};
56867
+ if (typeof body3.depends_on !== "string" || !body3.depends_on.trim()) {
55498
56868
  return error(400, "depends_on is required");
55499
56869
  }
55500
56870
  try {
55501
- const dependency = await store.dependencies.add(id, body2.depends_on, contextFromPrincipal(principal));
55502
- return json4({ dependency }, 201);
56871
+ const dependency2 = await store.dependencies.add(id, body3.depends_on, contextFromPrincipal(principal));
56872
+ return json5({ dependency: dependency2 }, 201);
55503
56873
  } catch (e) {
55504
56874
  const msg = e.message || "";
55505
56875
  if (msg.includes("not found"))
@@ -55513,7 +56883,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55513
56883
  if (!subId)
55514
56884
  return error(400, "dependency target id is required (/v1/tasks/:id/dependencies/:dep)");
55515
56885
  const removed = await store.dependencies.remove(id, subId);
55516
- return json4({ removed });
56886
+ return json5({ removed });
55517
56887
  }
55518
56888
  return error(405, `method ${method} not allowed on /v1/tasks/:id/dependencies`);
55519
56889
  }
@@ -55524,23 +56894,23 @@ async function handleV1Request(req, url, dependencies = {}) {
55524
56894
  if (!await store.tasks.get(id))
55525
56895
  return error(404, "task not found");
55526
56896
  const verifications = await store.verifications.list(id);
55527
- return json4({ verifications, count: verifications.length });
56897
+ return json5({ verifications, count: verifications.length });
55528
56898
  }
55529
56899
  if (method === "POST") {
55530
- const body2 = await readJson3(req) ?? {};
55531
- if (typeof body2.command !== "string" || !body2.command.trim()) {
56900
+ const body3 = await readJson3(req) ?? {};
56901
+ if (typeof body3.command !== "string" || !body3.command.trim()) {
55532
56902
  return error(400, "command is required");
55533
56903
  }
55534
56904
  try {
55535
- const verification = await store.verifications.add({
56905
+ const verification2 = await store.verifications.add({
55536
56906
  task_id: id,
55537
- command: body2.command,
55538
- status: body2.status,
55539
- output_summary: body2.output_summary,
55540
- artifact_path: body2.artifact_path,
55541
- agent_id: body2.agent_id
55542
- }, contextFromPrincipal(principal, body2));
55543
- return json4({ verification }, 201);
56907
+ command: body3.command,
56908
+ status: body3.status,
56909
+ output_summary: body3.output_summary,
56910
+ artifact_path: body3.artifact_path,
56911
+ agent_id: body3.agent_id
56912
+ }, contextFromPrincipal(principal, body3));
56913
+ return json5({ verification: verification2 }, 201);
55544
56914
  } catch (e) {
55545
56915
  const msg = e.message || "";
55546
56916
  if (msg.includes("not found"))
@@ -55557,21 +56927,21 @@ async function handleV1Request(req, url, dependencies = {}) {
55557
56927
  if (!await store.tasks.get(id))
55558
56928
  return error(404, "task not found");
55559
56929
  const commits = await store.commits.list(id);
55560
- return json4({ commits, count: commits.length });
56930
+ return json5({ commits, count: commits.length });
55561
56931
  }
55562
56932
  if (method === "POST") {
55563
- const body2 = await readJson3(req) ?? {};
55564
- if (typeof body2.sha !== "string" || !body2.sha.trim())
56933
+ const body3 = await readJson3(req) ?? {};
56934
+ if (typeof body3.sha !== "string" || !body3.sha.trim())
55565
56935
  return error(400, "sha is required");
55566
56936
  try {
55567
56937
  const commit = await store.commits.add({
55568
56938
  task_id: id,
55569
- sha: body2.sha,
55570
- message: body2.message,
55571
- author: body2.author,
55572
- files_changed: Array.isArray(body2.files_changed) ? body2.files_changed : undefined
56939
+ sha: body3.sha,
56940
+ message: body3.message,
56941
+ author: body3.author,
56942
+ files_changed: Array.isArray(body3.files_changed) ? body3.files_changed : undefined
55573
56943
  }, contextFromPrincipal(principal));
55574
- return json4({ commit }, 201);
56944
+ return json5({ commit }, 201);
55575
56945
  } catch (e) {
55576
56946
  const msg = e.message || "";
55577
56947
  if (msg.includes("not found"))
@@ -55588,23 +56958,23 @@ async function handleV1Request(req, url, dependencies = {}) {
55588
56958
  if (!await store.tasks.get(id))
55589
56959
  return error(404, "task not found");
55590
56960
  const refs = await store.gitRefs.list(id);
55591
- return json4({ refs, count: refs.length });
56961
+ return json5({ refs, count: refs.length });
55592
56962
  }
55593
56963
  if (method === "POST") {
55594
- const body2 = await readJson3(req) ?? {};
55595
- const refType = body2.ref_type === "pull_request" || body2.ref_type === "branch" ? body2.ref_type : "branch";
55596
- if (typeof body2.name !== "string" || !body2.name.trim())
56964
+ const body3 = await readJson3(req) ?? {};
56965
+ const refType = body3.ref_type === "pull_request" || body3.ref_type === "branch" ? body3.ref_type : "branch";
56966
+ if (typeof body3.name !== "string" || !body3.name.trim())
55597
56967
  return error(400, "name is required");
55598
56968
  try {
55599
56969
  const ref = await store.gitRefs.add({
55600
56970
  task_id: id,
55601
56971
  ref_type: refType,
55602
- name: body2.name,
55603
- url: body2.url,
55604
- provider: body2.provider,
55605
- metadata: body2.metadata
56972
+ name: body3.name,
56973
+ url: body3.url,
56974
+ provider: body3.provider,
56975
+ metadata: body3.metadata
55606
56976
  }, contextFromPrincipal(principal));
55607
- return json4({ ref }, 201);
56977
+ return json5({ ref }, 201);
55608
56978
  } catch (e) {
55609
56979
  const msg = e.message || "";
55610
56980
  if (msg.includes("not found"))
@@ -55617,32 +56987,32 @@ async function handleV1Request(req, url, dependencies = {}) {
55617
56987
  const actionJson = await readOptionalJson(req);
55618
56988
  if (!actionJson.ok)
55619
56989
  return error(400, "invalid JSON body");
55620
- const body = actionJson.value && typeof actionJson.value === "object" && !Array.isArray(actionJson.value) ? actionJson.value : {};
55621
- const agentId = typeof body.agent_id === "string" ? body.agent_id : principal.agent || "todos-serve";
56990
+ const body2 = actionJson.value && typeof actionJson.value === "object" && !Array.isArray(actionJson.value) ? actionJson.value : {};
56991
+ const agentId = typeof body2.agent_id === "string" ? body2.agent_id : principal.agent || "todos-serve";
55622
56992
  if (action === "start" && method === "POST") {
55623
- return json4({ task: await store.tasks.start(id, agentId) });
56993
+ return json5({ task: await store.tasks.start(id, agentId) });
55624
56994
  }
55625
56995
  if (action === "complete" && method === "POST") {
55626
56996
  const parsed = validateTaskCompletion(actionJson.value);
55627
56997
  if (!parsed.ok)
55628
56998
  return error(400, parsed.message);
55629
- return json4({
55630
- task: await store.tasks.complete(id, parsed.agentId || principal.agent || "todos-serve", parsed.options, contextFromPrincipal(principal, body))
56999
+ return json5({
57000
+ task: await store.tasks.complete(id, parsed.agentId || principal.agent || "todos-serve", parsed.options, contextFromPrincipal(principal, body2))
55631
57001
  });
55632
57002
  }
55633
57003
  if (action === "fail" && method === "POST") {
55634
- return json4({ result: await store.tasks.fail(id, agentId, typeof body.reason === "string" ? body.reason : "failed", {}) });
57004
+ return json5({ result: await store.tasks.fail(id, agentId, typeof body2.reason === "string" ? body2.reason : "failed", {}) });
55635
57005
  }
55636
57006
  if (action === "claim" && method === "POST") {
55637
- return json4({ task: await store.tasks.claimNext(agentId, {}) });
57007
+ return json5({ task: await store.tasks.claimNext(agentId, {}) });
55638
57008
  }
55639
57009
  return error(404, `unknown task action: ${action}`);
55640
57010
  }
55641
57011
  if (method === "GET") {
55642
- let task2 = await store.tasks.get(id);
55643
- if (!task2 && typeof store.tasks.resolveRef === "function") {
57012
+ let task3 = await store.tasks.get(id);
57013
+ if (!task3 && typeof store.tasks.resolveRef === "function") {
55644
57014
  try {
55645
- task2 = await store.tasks.resolveRef(id);
57015
+ task3 = await store.tasks.resolveRef(id);
55646
57016
  } catch (e) {
55647
57017
  if (e instanceof TaskReferenceAmbiguousError) {
55648
57018
  return error(409, e.message, {
@@ -55657,22 +57027,22 @@ async function handleV1Request(req, url, dependencies = {}) {
55657
57027
  throw e;
55658
57028
  }
55659
57029
  }
55660
- return task2 ? json4({ task: task2 }) : error(404, "task not found");
57030
+ return task3 ? json5({ task: task3 }) : error(404, "task not found");
55661
57031
  }
55662
57032
  if (method === "PATCH" || method === "PUT") {
55663
- const body = await readJson3(req);
55664
- if (!body)
57033
+ const body2 = await readJson3(req);
57034
+ if (!body2)
55665
57035
  return error(400, "invalid JSON body");
55666
57036
  const current = await store.tasks.get(id);
55667
57037
  if (!current)
55668
57038
  return error(404, "task not found");
55669
57039
  const patch = {
55670
- ...body,
55671
- version: typeof body.version === "number" ? body.version : current.version
57040
+ ...body2,
57041
+ version: typeof body2.version === "number" ? body2.version : current.version
55672
57042
  };
55673
57043
  try {
55674
- const task2 = await store.tasks.update(id, patch);
55675
- return task2 ? json4({ task: task2 }) : error(404, "task not found");
57044
+ const task3 = await store.tasks.update(id, patch);
57045
+ return task3 ? json5({ task: task3 }) : error(404, "task not found");
55676
57046
  } catch (e) {
55677
57047
  const msg = e.message || "";
55678
57048
  if (msg.includes("version conflict"))
@@ -55682,7 +57052,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55682
57052
  }
55683
57053
  if (method === "DELETE") {
55684
57054
  await store.tasks.delete(id, contextFromPrincipal(principal));
55685
- return json4({ deleted: true, id });
57055
+ return json5({ deleted: true, id });
55686
57056
  }
55687
57057
  return error(405, `method ${method} not allowed on /v1/tasks/:id`);
55688
57058
  }
@@ -55690,111 +57060,111 @@ async function handleV1Request(req, url, dependencies = {}) {
55690
57060
  if (!id) {
55691
57061
  if (method === "GET") {
55692
57062
  const projects = await store.projects.list();
55693
- return json4({ projects, count: projects.length });
57063
+ return json5({ projects, count: projects.length });
55694
57064
  }
55695
57065
  if (method === "POST") {
55696
- const body = await readJson3(req);
55697
- if (!body)
57066
+ const body2 = await readJson3(req);
57067
+ if (!body2)
55698
57068
  return error(400, "invalid JSON body");
55699
- const validated = validateProjectCreate(body);
57069
+ const validated = validateProjectCreate(body2);
55700
57070
  if (!validated.ok)
55701
57071
  return error(400, validated.message);
55702
57072
  const project = await store.projects.create(validated.input, contextFromPrincipal(principal));
55703
- return json4({ project }, 201);
57073
+ return json5({ project }, 201);
55704
57074
  }
55705
57075
  return error(405, `method ${method} not allowed on /v1/projects`);
55706
57076
  }
55707
57077
  if (action === "task-list" && subId === "ensure") {
55708
57078
  if (method === "GET") {
55709
- return json4(await planProjectTaskListEnsure(store, id));
57079
+ return json5(await planProjectTaskListEnsure(store, id));
55710
57080
  }
55711
57081
  if (method !== "POST") {
55712
57082
  return error(405, `method ${method} not allowed on /v1/projects/:id/task-list/ensure`);
55713
57083
  }
55714
- const body = await readJson3(req);
55715
- if (!body)
57084
+ const body2 = await readJson3(req);
57085
+ if (!body2)
55716
57086
  return error(400, "invalid JSON body");
55717
- const unknown = Object.keys(body).find((key) => !["expected_project_revision", "idempotency_key"].includes(key));
57087
+ const unknown = Object.keys(body2).find((key2) => !["expected_project_revision", "idempotency_key"].includes(key2));
55718
57088
  if (unknown)
55719
57089
  return error(400, `unknown task-list ensure field: ${unknown}`);
55720
- if (typeof body.expected_project_revision !== "string" || !body.expected_project_revision.trim()) {
57090
+ if (typeof body2.expected_project_revision !== "string" || !body2.expected_project_revision.trim()) {
55721
57091
  return error(400, "expected_project_revision must be a non-empty string from a fresh ensure plan");
55722
57092
  }
55723
- if (body.idempotency_key !== undefined && typeof body.idempotency_key !== "string") {
57093
+ if (body2.idempotency_key !== undefined && typeof body2.idempotency_key !== "string") {
55724
57094
  return error(400, "idempotency_key must be a string");
55725
57095
  }
55726
57096
  const result = await applyProjectTaskListEnsure(store, id, {
55727
- expected_project_revision: body.expected_project_revision,
55728
- ...typeof body.idempotency_key === "string" ? { idempotency_key: body.idempotency_key } : {}
57097
+ expected_project_revision: body2.expected_project_revision,
57098
+ ...typeof body2.idempotency_key === "string" ? { idempotency_key: body2.idempotency_key } : {}
55729
57099
  });
55730
- return json4(result, result.action === "created" ? 201 : 200);
57100
+ return json5(result, result.action === "created" ? 201 : 200);
55731
57101
  }
55732
57102
  if (action === "task-list" && subId === "rollback") {
55733
57103
  if (method !== "POST") {
55734
57104
  return error(405, `method ${method} not allowed on /v1/projects/:id/task-list/rollback`);
55735
57105
  }
55736
- const body = await readJson3(req);
55737
- if (!body)
57106
+ const body2 = await readJson3(req);
57107
+ if (!body2)
55738
57108
  return error(400, "invalid JSON body");
55739
- const unknown = Object.keys(body).find((key) => !["receipt_id", "expected_task_list_revision"].includes(key));
57109
+ const unknown = Object.keys(body2).find((key2) => !["receipt_id", "expected_task_list_revision"].includes(key2));
55740
57110
  if (unknown)
55741
57111
  return error(400, `unknown task-list rollback field: ${unknown}`);
55742
- if (typeof body.receipt_id !== "string" || !body.receipt_id.trim()) {
57112
+ if (typeof body2.receipt_id !== "string" || !body2.receipt_id.trim()) {
55743
57113
  return error(400, "receipt_id must be a non-empty string");
55744
57114
  }
55745
- if (typeof body.expected_task_list_revision !== "string" || !body.expected_task_list_revision.trim()) {
57115
+ if (typeof body2.expected_task_list_revision !== "string" || !body2.expected_task_list_revision.trim()) {
55746
57116
  return error(400, "expected_task_list_revision must be a non-empty string from the accepted receipt");
55747
57117
  }
55748
- return json4(await rollbackProjectTaskListEnsure(store, id, {
55749
- receipt_id: body.receipt_id,
55750
- expected_task_list_revision: body.expected_task_list_revision
57118
+ return json5(await rollbackProjectTaskListEnsure(store, id, {
57119
+ receipt_id: body2.receipt_id,
57120
+ expected_task_list_revision: body2.expected_task_list_revision
55751
57121
  }));
55752
57122
  }
55753
57123
  if (action === "rename") {
55754
57124
  if (method !== "POST")
55755
57125
  return error(405, `method ${method} not allowed on /v1/projects/:id/rename`);
55756
- const body = await readJson3(req);
55757
- if (!body || typeof body.new_slug !== "string" || !body.new_slug.trim() || !normalizeSlug(body.new_slug)) {
57126
+ const body2 = await readJson3(req);
57127
+ if (!body2 || typeof body2.new_slug !== "string" || !body2.new_slug.trim() || !normalizeSlug(body2.new_slug)) {
55758
57128
  return error(400, "new_slug must be a non-empty string");
55759
57129
  }
55760
- if (body.name !== undefined && (typeof body.name !== "string" || !body.name.trim())) {
57130
+ if (body2.name !== undefined && (typeof body2.name !== "string" || !body2.name.trim())) {
55761
57131
  return error(400, "name must be a non-empty string");
55762
57132
  }
55763
- const unknownField = Object.keys(body).find((key) => !["new_slug", "name"].includes(key));
57133
+ const unknownField = Object.keys(body2).find((key2) => !["new_slug", "name"].includes(key2));
55764
57134
  if (unknownField)
55765
57135
  return error(400, `unknown project rename field: ${unknownField}`);
55766
- return json4(await store.projects.rename(id, body, contextFromPrincipal(principal)));
57136
+ return json5(await store.projects.rename(id, body2, contextFromPrincipal(principal)));
55767
57137
  }
55768
57138
  if (method === "GET") {
55769
57139
  const project = await store.projects.get(id);
55770
- return project ? json4({ project }) : error(404, "project not found");
57140
+ return project ? json5({ project }) : error(404, "project not found");
55771
57141
  }
55772
57142
  if (method === "PATCH" || method === "PUT") {
55773
- const body = await readJson3(req);
55774
- if (!body)
57143
+ const body2 = await readJson3(req);
57144
+ if (!body2)
55775
57145
  return error(400, "invalid JSON body");
55776
- const validated = validateProjectPatch(body);
57146
+ const validated = validateProjectPatch(body2);
55777
57147
  if (!validated.ok)
55778
57148
  return error(400, validated.message);
55779
57149
  if (!await store.projects.get(id))
55780
57150
  return error(404, "project not found");
55781
57151
  const project = await store.projects.update(id, validated.patch);
55782
- return json4({ project });
57152
+ return json5({ project });
55783
57153
  }
55784
57154
  if (method === "DELETE") {
55785
57155
  await store.projects.delete(id, contextFromPrincipal(principal));
55786
- return json4({ deleted: true, id });
57156
+ return json5({ deleted: true, id });
55787
57157
  }
55788
57158
  return error(405, `method ${method} not allowed on /v1/projects/:id`);
55789
57159
  }
55790
57160
  if (resource === "plans") {
55791
57161
  if (!id && method === "GET") {
55792
57162
  const plans = await store.plans.list(url.searchParams.get("project_id") ?? undefined);
55793
- return json4({ plans, count: plans.length });
57163
+ return json5({ plans, count: plans.length });
55794
57164
  }
55795
57165
  if (!id && method === "POST") {
55796
- const body = await readJson3(req);
55797
- const validated = validatePlanCreate(body);
57166
+ const body2 = await readJson3(req);
57167
+ const validated = validatePlanCreate(body2);
55798
57168
  if (!validated.ok)
55799
57169
  return error(400, validated.message);
55800
57170
  if (validated.input.slug) {
@@ -55808,104 +57178,104 @@ async function handleV1Request(req, url, dependencies = {}) {
55808
57178
  }
55809
57179
  }
55810
57180
  const plan = await store.plans.create(validated.input, contextFromPrincipal(principal, validated.input));
55811
- return json4({ plan }, 201);
57181
+ return json5({ plan }, 201);
55812
57182
  }
55813
57183
  if (id && action === "project-link" && !subId) {
55814
57184
  if (method === "GET") {
55815
57185
  const projectId = url.searchParams.get("project_id");
55816
57186
  if (!projectId?.trim())
55817
57187
  return error(400, "project_id query parameter is required");
55818
- return json4(await planPlanProjectLink(store, id, projectId));
57188
+ return json5(await planPlanProjectLink(store, id, projectId));
55819
57189
  }
55820
57190
  if (method !== "POST")
55821
57191
  return error(405, `method ${method} not allowed on /v1/plans/:id/project-link`);
55822
- const body = await readJson3(req);
55823
- if (!body)
57192
+ const body2 = await readJson3(req);
57193
+ if (!body2)
55824
57194
  return error(400, "invalid JSON body");
55825
57195
  const allowed = new Set(["project_id", "expected_plan_revision", "expected_project_revision", "idempotency_key"]);
55826
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
57196
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
55827
57197
  if (unknown)
55828
57198
  return error(400, `unknown plan-project-link field: ${unknown}`);
55829
57199
  for (const field of ["project_id", "expected_plan_revision", "expected_project_revision", "idempotency_key"]) {
55830
- if (typeof body[field] !== "string" || !body[field].trim()) {
57200
+ if (typeof body2[field] !== "string" || !body2[field].trim()) {
55831
57201
  return error(400, `${field} must be a non-empty string`);
55832
57202
  }
55833
57203
  }
55834
- const result = await applyPlanProjectLink(store, id, body.project_id, {
55835
- expected_plan_revision: body.expected_plan_revision,
55836
- expected_project_revision: body.expected_project_revision,
55837
- idempotency_key: body.idempotency_key
57204
+ const result = await applyPlanProjectLink(store, id, body2.project_id, {
57205
+ expected_plan_revision: body2.expected_plan_revision,
57206
+ expected_project_revision: body2.expected_project_revision,
57207
+ idempotency_key: body2.idempotency_key
55838
57208
  });
55839
- return json4(result, result.action === "linked" ? 201 : 200);
57209
+ return json5(result, result.action === "linked" ? 201 : 200);
55840
57210
  }
55841
57211
  if (id && action === "project-link" && subId === "rollback") {
55842
57212
  if (method !== "POST")
55843
57213
  return error(405, `method ${method} not allowed on /v1/plans/:id/project-link/rollback`);
55844
- const body = await readJson3(req);
55845
- if (!body)
57214
+ const body2 = await readJson3(req);
57215
+ if (!body2)
55846
57216
  return error(400, "invalid JSON body");
55847
57217
  const allowed = new Set(["project_id", "receipt_id", "expected_plan_revision"]);
55848
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
57218
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
55849
57219
  if (unknown)
55850
57220
  return error(400, `unknown plan-project-link rollback field: ${unknown}`);
55851
57221
  for (const field of ["project_id", "receipt_id", "expected_plan_revision"]) {
55852
- if (typeof body[field] !== "string" || !body[field].trim()) {
57222
+ if (typeof body2[field] !== "string" || !body2[field].trim()) {
55853
57223
  return error(400, `${field} must be a non-empty string`);
55854
57224
  }
55855
57225
  }
55856
- return json4(await rollbackPlanProjectLink(store, id, body.project_id, {
55857
- receipt_id: body.receipt_id,
55858
- expected_plan_revision: body.expected_plan_revision
57226
+ return json5(await rollbackPlanProjectLink(store, id, body2.project_id, {
57227
+ receipt_id: body2.receipt_id,
57228
+ expected_plan_revision: body2.expected_plan_revision
55859
57229
  }));
55860
57230
  }
55861
57231
  if (id && method === "GET") {
55862
57232
  const plan = await store.plans.get(id);
55863
- return plan ? json4({ plan }) : error(404, "plan not found");
57233
+ return plan ? json5({ plan }) : error(404, "plan not found");
55864
57234
  }
55865
57235
  if (id && (method === "PATCH" || method === "PUT")) {
55866
- const body = await readJson3(req);
55867
- if (!body || Object.keys(body).length === 0)
57236
+ const body2 = await readJson3(req);
57237
+ if (!body2 || Object.keys(body2).length === 0)
55868
57238
  return error(400, "plan patch is required");
55869
57239
  const allowed = new Set(["name", "slug", "description", "status", "task_list_id", "agent_id"]);
55870
- const unknownField = Object.keys(body).find((key) => !allowed.has(key));
57240
+ const unknownField = Object.keys(body2).find((key2) => !allowed.has(key2));
55871
57241
  if (unknownField)
55872
57242
  return error(400, `unknown plan field: ${unknownField}`);
55873
57243
  for (const field of ["name", "slug", "task_list_id", "agent_id"]) {
55874
- if (body[field] !== undefined && (typeof body[field] !== "string" || !body[field].trim())) {
57244
+ if (body2[field] !== undefined && (typeof body2[field] !== "string" || !body2[field].trim())) {
55875
57245
  return error(400, `${field} must be a non-empty string`);
55876
57246
  }
55877
57247
  }
55878
- if (typeof body.slug === "string") {
55879
- const slug = normalizeSlug(body.slug);
57248
+ if (typeof body2.slug === "string") {
57249
+ const slug = normalizeSlug(body2.slug);
55880
57250
  if (!slug)
55881
57251
  return error(400, "slug must produce a non-empty canonical slug");
55882
- body.slug = slug;
57252
+ body2.slug = slug;
55883
57253
  }
55884
- if (body.description !== undefined && typeof body.description !== "string") {
57254
+ if (body2.description !== undefined && typeof body2.description !== "string") {
55885
57255
  return error(400, "description must be a string");
55886
57256
  }
55887
- if (body.status !== undefined && (typeof body.status !== "string" || !["active", "completed", "archived"].includes(body.status))) {
57257
+ if (body2.status !== undefined && (typeof body2.status !== "string" || !["active", "completed", "archived"].includes(body2.status))) {
55888
57258
  return error(400, "status must be active, completed, or archived");
55889
57259
  }
55890
57260
  const existing = await store.plans.get(id);
55891
57261
  if (!existing)
55892
57262
  return error(404, "plan not found");
55893
- if (typeof body.slug === "string") {
55894
- const duplicate = (await store.plans.list(existing.project_id ?? undefined)).find((plan2) => plan2.id !== id && plan2.project_id === existing.project_id && plan2.slug === body.slug);
57263
+ if (typeof body2.slug === "string") {
57264
+ const duplicate = (await store.plans.list(existing.project_id ?? undefined)).find((plan2) => plan2.id !== id && plan2.project_id === existing.project_id && plan2.slug === body2.slug);
55895
57265
  if (duplicate) {
55896
- return error(409, `Plan slug already exists in this scope: ${body.slug}`, {
57266
+ return error(409, `Plan slug already exists in this scope: ${body2.slug}`, {
55897
57267
  code: "PLAN_SLUG_CONFLICT",
55898
57268
  conflict: true
55899
57269
  });
55900
57270
  }
55901
57271
  }
55902
- const plan = await store.plans.update(id, body);
55903
- return json4({ plan });
57272
+ const plan = await store.plans.update(id, body2);
57273
+ return json5({ plan });
55904
57274
  }
55905
57275
  if (id && method === "DELETE") {
55906
57276
  if (!await store.plans.delete(id, contextFromPrincipal(principal)))
55907
57277
  return error(404, "plan not found");
55908
- return json4({ deleted: true, id });
57278
+ return json5({ deleted: true, id });
55909
57279
  }
55910
57280
  if (id)
55911
57281
  return error(405, `method ${method} not allowed on /v1/plans/:id`);
@@ -55914,50 +57284,50 @@ async function handleV1Request(req, url, dependencies = {}) {
55914
57284
  if (!id && method === "GET") {
55915
57285
  const projectId = url.searchParams.get("project_id");
55916
57286
  const templates = (await store.templates.list()).filter((template) => projectId === null || template.project_id === projectId);
55917
- return json4({ templates, count: templates.length });
57287
+ return json5({ templates, count: templates.length });
55918
57288
  }
55919
57289
  if (!id && method === "POST") {
55920
- const body = await readJson3(req);
55921
- const validated = validateTemplateCreate(body);
57290
+ const body2 = await readJson3(req);
57291
+ const validated = validateTemplateCreate(body2);
55922
57292
  if (!validated.ok)
55923
57293
  return error(400, validated.message);
55924
57294
  const template = await store.templates.create(validated.input, contextFromPrincipal(principal));
55925
- return json4({ template: await store.templates.getWithTasks(template.id) }, 201);
57295
+ return json5({ template: await store.templates.getWithTasks(template.id) }, 201);
55926
57296
  }
55927
57297
  if (!id)
55928
57298
  return error(405, `method ${method} not allowed on /v1/templates`);
55929
57299
  if (method === "GET") {
55930
57300
  const template = await store.templates.getWithTasks(id);
55931
- return template ? json4({ template }) : error(404, "template not found");
57301
+ return template ? json5({ template }) : error(404, "template not found");
55932
57302
  }
55933
57303
  if (method === "PATCH" || method === "PUT") {
55934
- const body = await readJson3(req);
55935
- const validated = validateTemplatePatch(body);
57304
+ const body2 = await readJson3(req);
57305
+ const validated = validateTemplatePatch(body2);
55936
57306
  if (!validated.ok)
55937
57307
  return error(400, validated.message);
55938
57308
  const template = await store.templates.update(id, validated.patch, contextFromPrincipal(principal));
55939
- return template ? json4({ template: await store.templates.getWithTasks(id) }) : error(404, "template not found");
57309
+ return template ? json5({ template: await store.templates.getWithTasks(id) }) : error(404, "template not found");
55940
57310
  }
55941
57311
  if (method === "DELETE") {
55942
57312
  const deleted = await store.templates.delete(id, contextFromPrincipal(principal));
55943
- return deleted ? json4({ deleted: true, id }) : error(404, "template not found");
57313
+ return deleted ? json5({ deleted: true, id }) : error(404, "template not found");
55944
57314
  }
55945
57315
  return error(405, `method ${method} not allowed on /v1/templates/:id`);
55946
57316
  }
55947
57317
  if (resource === "agents") {
55948
57318
  if (!id && method === "GET") {
55949
57319
  const agents = await store.agents.list();
55950
- return json4({ agents, count: agents.length });
57320
+ return json5({ agents, count: agents.length });
55951
57321
  }
55952
57322
  if (!id && method === "POST") {
55953
- const body = await readJson3(req);
55954
- if (!body || typeof body.name !== "string" || !body.name.trim())
57323
+ const body2 = await readJson3(req);
57324
+ if (!body2 || typeof body2.name !== "string" || !body2.name.trim())
55955
57325
  return error(400, "name is required");
55956
- const result = await store.agents.register(body, contextFromPrincipal(principal));
57326
+ const result = await store.agents.register(body2, contextFromPrincipal(principal));
55957
57327
  if (result && typeof result === "object" && "conflict" in result) {
55958
57328
  return error(409, result.message ?? "agent name conflict", { conflict: true });
55959
57329
  }
55960
- return json4({ agent: result }, 201);
57330
+ return json5({ agent: result }, 201);
55961
57331
  }
55962
57332
  if (id && action === "heartbeat") {
55963
57333
  if (method !== "POST")
@@ -55966,7 +57336,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55966
57336
  return error(501, "agent heartbeat is not supported by this storage backend");
55967
57337
  }
55968
57338
  const agent = await store.agents.heartbeat(id, contextFromPrincipal(principal));
55969
- return agent ? json4({ agent }) : error(404, "agent not found");
57339
+ return agent ? json5({ agent }) : error(404, "agent not found");
55970
57340
  }
55971
57341
  if (id && action === "release") {
55972
57342
  if (method !== "POST")
@@ -55974,18 +57344,18 @@ async function handleV1Request(req, url, dependencies = {}) {
55974
57344
  if (typeof store.agents.release !== "function") {
55975
57345
  return error(501, "agent release is not supported by this storage backend");
55976
57346
  }
55977
- const body = await readJson3(req) ?? {};
55978
- const result = await store.agents.release(id, body.session_id, contextFromPrincipal(principal));
57347
+ const body2 = await readJson3(req) ?? {};
57348
+ const result = await store.agents.release(id, body2.session_id, contextFromPrincipal(principal));
55979
57349
  if (!result)
55980
57350
  return error(404, "agent not found");
55981
57351
  if (!result.released) {
55982
57352
  return error(409, "release denied: session_id does not match agent's current session", { released: false });
55983
57353
  }
55984
- return json4({ agent: result.agent, released: true });
57354
+ return json5({ agent: result.agent, released: true });
55985
57355
  }
55986
57356
  if (id && method === "GET") {
55987
57357
  const agent = await store.agents.get(id);
55988
- return agent ? json4({ agent }) : error(404, "agent not found");
57358
+ return agent ? json5({ agent }) : error(404, "agent not found");
55989
57359
  }
55990
57360
  }
55991
57361
  if (resource === "activity" && !id) {
@@ -55994,66 +57364,66 @@ async function handleV1Request(req, url, dependencies = {}) {
55994
57364
  const limitParam = url.searchParams.get("limit");
55995
57365
  const limit = limitParam ? Math.max(1, Math.min(1e4, Number(limitParam) || 50)) : 50;
55996
57366
  const activity = await store.audit.getRecentActivity(limit);
55997
- return json4({ activity, count: activity.length });
57367
+ return json5({ activity, count: activity.length });
55998
57368
  }
55999
57369
  if (resource === "task-lists") {
56000
57370
  if (!id && method === "GET") {
56001
57371
  const projectId = url.searchParams.get("project_id") ?? undefined;
56002
57372
  const taskLists = await store.taskLists.list(projectId);
56003
- return json4({ task_lists: taskLists, count: taskLists.length });
57373
+ return json5({ task_lists: taskLists, count: taskLists.length });
56004
57374
  }
56005
57375
  if (!id && method === "POST") {
56006
- const body = await readJson3(req);
56007
- if (!body || typeof body.name !== "string" || !body.name.trim())
57376
+ const body2 = await readJson3(req);
57377
+ if (!body2 || typeof body2.name !== "string" || !body2.name.trim())
56008
57378
  return error(400, "name is required");
56009
- const unknownField = Object.keys(body).find((key) => !["name", "slug", "project_id", "description", "metadata"].includes(key));
57379
+ const unknownField = Object.keys(body2).find((key2) => !["name", "slug", "project_id", "description", "metadata"].includes(key2));
56010
57380
  if (unknownField)
56011
57381
  return error(400, `unsupported task-list create field: ${unknownField}`);
56012
- if (body.slug !== undefined && typeof body.slug !== "string")
57382
+ if (body2.slug !== undefined && typeof body2.slug !== "string")
56013
57383
  return error(400, "slug must be a string");
56014
- if (body.project_id !== undefined && (typeof body.project_id !== "string" || !body.project_id.trim()))
57384
+ if (body2.project_id !== undefined && (typeof body2.project_id !== "string" || !body2.project_id.trim()))
56015
57385
  return error(400, "project_id must be a non-empty string");
56016
- if (body.description !== undefined && typeof body.description !== "string")
57386
+ if (body2.description !== undefined && typeof body2.description !== "string")
56017
57387
  return error(400, "description must be a string");
56018
- if (body.metadata !== undefined && (!body.metadata || typeof body.metadata !== "object" || Array.isArray(body.metadata))) {
57388
+ if (body2.metadata !== undefined && (!body2.metadata || typeof body2.metadata !== "object" || Array.isArray(body2.metadata))) {
56019
57389
  return error(400, "metadata must be an object");
56020
57390
  }
56021
- if (!normalizeSlug(body.slug === undefined ? body.name : body.slug)) {
57391
+ if (!normalizeSlug(body2.slug === undefined ? body2.name : body2.slug)) {
56022
57392
  return error(400, "task-list slug must be non-empty kebab-case");
56023
57393
  }
56024
- const taskList = await store.taskLists.create(body, contextFromPrincipal(principal));
56025
- return json4({ task_list: taskList }, 201);
57394
+ const taskList = await store.taskLists.create(body2, contextFromPrincipal(principal));
57395
+ return json5({ task_list: taskList }, 201);
56026
57396
  }
56027
57397
  if (id && method === "GET") {
56028
57398
  const taskList = await store.taskLists.get(id);
56029
- return taskList ? json4({ task_list: taskList }) : error(404, "task list not found");
57399
+ return taskList ? json5({ task_list: taskList }) : error(404, "task list not found");
56030
57400
  }
56031
57401
  if (id && (method === "PATCH" || method === "PUT")) {
56032
- const body = await readJson3(req);
56033
- if (!body)
57402
+ const body2 = await readJson3(req);
57403
+ if (!body2)
56034
57404
  return error(400, "invalid JSON body");
56035
- const unknownField = Object.keys(body).find((key) => !["slug", "name", "description", "metadata"].includes(key));
57405
+ const unknownField = Object.keys(body2).find((key2) => !["slug", "name", "description", "metadata"].includes(key2));
56036
57406
  if (unknownField)
56037
57407
  return error(400, `unsupported task-list update field: ${unknownField}`);
56038
- if (Object.keys(body).length === 0)
57408
+ if (Object.keys(body2).length === 0)
56039
57409
  return error(400, "task-list update must not be empty");
56040
- if (body.slug !== undefined && (typeof body.slug !== "string" || !normalizeSlug(body.slug)))
57410
+ if (body2.slug !== undefined && (typeof body2.slug !== "string" || !normalizeSlug(body2.slug)))
56041
57411
  return error(400, "slug must be a non-empty string");
56042
- if (body.name !== undefined && (typeof body.name !== "string" || !body.name.trim()))
57412
+ if (body2.name !== undefined && (typeof body2.name !== "string" || !body2.name.trim()))
56043
57413
  return error(400, "name must be a non-empty string");
56044
- if (body.description !== undefined && typeof body.description !== "string")
57414
+ if (body2.description !== undefined && typeof body2.description !== "string")
56045
57415
  return error(400, "description must be a string");
56046
- if (body.metadata !== undefined && (!body.metadata || typeof body.metadata !== "object" || Array.isArray(body.metadata))) {
57416
+ if (body2.metadata !== undefined && (!body2.metadata || typeof body2.metadata !== "object" || Array.isArray(body2.metadata))) {
56047
57417
  return error(400, "metadata must be an object");
56048
57418
  }
56049
57419
  if (!await store.taskLists.get(id))
56050
57420
  return error(404, "task list not found");
56051
- const taskList = await store.taskLists.update(id, body);
56052
- return json4({ task_list: taskList });
57421
+ const taskList = await store.taskLists.update(id, body2);
57422
+ return json5({ task_list: taskList });
56053
57423
  }
56054
57424
  if (id && method === "DELETE") {
56055
57425
  const deleted = await store.taskLists.delete(id, contextFromPrincipal(principal));
56056
- return deleted ? json4({ deleted: true, id }) : error(404, "task list not found");
57426
+ return deleted ? json5({ deleted: true, id }) : error(404, "task list not found");
56057
57427
  }
56058
57428
  return error(405, `method ${method} not allowed on /v1/task-lists${id ? "/:id" : ""}`);
56059
57429
  }
@@ -56064,7 +57434,7 @@ async function handleV1Request(req, url, dependencies = {}) {
56064
57434
  return error(501, "dependency edge listing is not supported by this storage backend");
56065
57435
  }
56066
57436
  const dependencies2 = await store.dependencies.listAll();
56067
- return json4({ dependencies: dependencies2, count: dependencies2.length });
57437
+ return json5({ dependencies: dependencies2, count: dependencies2.length });
56068
57438
  }
56069
57439
  if (resource === "commits" && id) {
56070
57440
  if (method !== "GET")
@@ -56072,7 +57442,7 @@ async function handleV1Request(req, url, dependencies = {}) {
56072
57442
  if (!store.commits)
56073
57443
  return error(501, "commit links are not supported by this storage backend");
56074
57444
  const commit = await store.commits.find(id);
56075
- return json4({ commit: commit ?? null });
57445
+ return json5({ commit: commit ?? null });
56076
57446
  }
56077
57447
  if (resource === "refs" && id) {
56078
57448
  if (method !== "GET")
@@ -56086,7 +57456,7 @@ async function handleV1Request(req, url, dependencies = {}) {
56086
57456
  return error(400, "ref path segment has invalid percent encoding");
56087
57457
  }
56088
57458
  const refs = await store.gitRefs.find(decodedRef);
56089
- return json4({ refs, count: refs.length });
57459
+ return json5({ refs, count: refs.length });
56090
57460
  }
56091
57461
  if (resource === "next" && !id) {
56092
57462
  if (method !== "GET")
@@ -56097,8 +57467,8 @@ async function handleV1Request(req, url, dependencies = {}) {
56097
57467
  ...url.searchParams.get("task_list_id") ? { task_list_id: url.searchParams.get("task_list_id") } : {},
56098
57468
  ...url.searchParams.get("plan_id") ? { plan_id: url.searchParams.get("plan_id") } : {}
56099
57469
  };
56100
- const task2 = await store.tasks.getNext(agent, filters);
56101
- return json4({ task: task2 ?? null });
57470
+ const task3 = await store.tasks.getNext(agent, filters);
57471
+ return json5({ task: task3 ?? null });
56102
57472
  }
56103
57473
  if (resource === "stats" && method === "GET") {
56104
57474
  const [tasks, tasksAll, projects] = await Promise.all([
@@ -56106,7 +57476,7 @@ async function handleV1Request(req, url, dependencies = {}) {
56106
57476
  store.tasks.count({ include_subtasks: true }),
56107
57477
  store.projects.list()
56108
57478
  ]);
56109
- return json4({ tasks, tasks_all: tasksAll, subtasks: tasksAll - tasks, projects: projects.length });
57479
+ return json5({ tasks, tasks_all: tasksAll, subtasks: tasksAll - tasks, projects: projects.length });
56110
57480
  }
56111
57481
  if (resource === "integrity" && !id) {
56112
57482
  if (method !== "GET")
@@ -56115,7 +57485,7 @@ async function handleV1Request(req, url, dependencies = {}) {
56115
57485
  return error(501, "referential-integrity reporting is not supported by this storage backend");
56116
57486
  }
56117
57487
  const integrity = await store.integrity.report();
56118
- return json4({ integrity });
57488
+ return json5({ integrity });
56119
57489
  }
56120
57490
  if (resource === "import") {
56121
57491
  if (method !== "POST")
@@ -56132,17 +57502,17 @@ async function handleV1Request(req, url, dependencies = {}) {
56132
57502
  return error(400, "empty snapshot: provide at least one record array (tasks/projects/plans/...)");
56133
57503
  }
56134
57504
  const result = await store.sync.importSnapshot(snapshot, contextFromPrincipal(principal));
56135
- return json4({ result, received });
57505
+ return json5({ result, received });
56136
57506
  }
56137
57507
  return error(404, `unknown /v1 resource: ${resource ?? "(root)"}`);
56138
57508
  } catch (e) {
56139
57509
  if (e instanceof PlanProjectLinkError) {
56140
- const status = e.code === "PLAN_PROJECT_LINK_PLAN_NOT_FOUND" || e.code === "PLAN_PROJECT_LINK_PROJECT_NOT_FOUND" || e.code === "PLAN_PROJECT_LINK_RECEIPT_NOT_FOUND" ? 404 : e.code === "PLAN_PROJECT_LINK_IDEMPOTENCY_KEY_INVALID" ? 400 : e.code === "PLAN_PROJECT_LINK_UNSUPPORTED" ? 501 : 409;
56141
- return error(status, e.message, { code: e.code, conflict: status === 409, ...e.details });
57510
+ const status2 = e.code === "PLAN_PROJECT_LINK_PLAN_NOT_FOUND" || e.code === "PLAN_PROJECT_LINK_PROJECT_NOT_FOUND" || e.code === "PLAN_PROJECT_LINK_RECEIPT_NOT_FOUND" ? 404 : e.code === "PLAN_PROJECT_LINK_IDEMPOTENCY_KEY_INVALID" ? 400 : e.code === "PLAN_PROJECT_LINK_UNSUPPORTED" ? 501 : 409;
57511
+ return error(status2, e.message, { code: e.code, conflict: status2 === 409, ...e.details });
56142
57512
  }
56143
57513
  if (e instanceof ProjectTaskListEnsureError) {
56144
- const status = e.code === "PROJECT_NOT_FOUND" || e.code === "PROJECT_TASK_LIST_RECEIPT_NOT_FOUND" ? 404 : e.code === "PROJECT_TASK_LIST_IDEMPOTENCY_KEY_INVALID" ? 400 : 409;
56145
- return error(status, e.message, { code: e.code, conflict: status === 409, ...e.details });
57514
+ const status2 = e.code === "PROJECT_NOT_FOUND" || e.code === "PROJECT_TASK_LIST_RECEIPT_NOT_FOUND" ? 404 : e.code === "PROJECT_TASK_LIST_IDEMPOTENCY_KEY_INVALID" ? 400 : 409;
57515
+ return error(status2, e.message, { code: e.code, conflict: status2 === 409, ...e.details });
56146
57516
  }
56147
57517
  if (e instanceof TaskReferenceAmbiguousError) {
56148
57518
  return error(409, e.message, {
@@ -56166,21 +57536,22 @@ async function handleV1Request(req, url, dependencies = {}) {
56166
57536
  return error(500, e.message || "internal error");
56167
57537
  }
56168
57538
  }
56169
- var JSON_HEADERS3, DEFAULT_COMMENT_PAGE_SIZE = 100, MAX_COMMENT_PAGE_SIZE = 500, LEGACY_COMMENT_RESPONSE_LIMIT = 500, RFC3339_DATE_TIME;
57539
+ var JSON_HEADERS4, DEFAULT_COMMENT_PAGE_SIZE = 100, MAX_COMMENT_PAGE_SIZE = 500, LEGACY_COMMENT_RESPONSE_LIMIT = 500, RFC3339_DATE_TIME;
56170
57540
  var init_v1 = __esm(() => {
56171
57541
  init_types();
56172
57542
  init_cloud();
56173
57543
  init_pr_groups();
56174
57544
  init_project_registration();
57545
+ init_task_manifest();
56175
57546
  init_redaction();
56176
57547
  init_project_task_list_ensure();
56177
57548
  init_plan_project_link();
56178
- JSON_HEADERS3 = { "Content-Type": "application/json" };
57549
+ JSON_HEADERS4 = { "Content-Type": "application/json" };
56179
57550
  RFC3339_DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})[Tt]\d{2}:\d{2}:\d{2}(\.\d+)?([Zz]|[+-]\d{2}:\d{2})$/;
56180
57551
  });
56181
57552
 
56182
57553
  // src/pr-groups/sqlite.ts
56183
- function parseJson2(value) {
57554
+ function parseJson3(value) {
56184
57555
  return JSON.parse(value);
56185
57556
  }
56186
57557
  function groupFromRow2(row) {
@@ -56207,9 +57578,9 @@ function eventFromRow2(row) {
56207
57578
  sequence: Number(row["sequence"]),
56208
57579
  pr_number: row["pr_number"] === null ? null : Number(row["pr_number"]),
56209
57580
  repair_cycle: row["repair_cycle"] === null ? null : Number(row["repair_cycle"]),
56210
- ci_proof: row["ci_proof"] ? parseJson2(String(row["ci_proof"])) : null,
56211
- cleanup_proof: row["cleanup_proof"] ? parseJson2(String(row["cleanup_proof"])) : null,
56212
- metadata: parseJson2(String(row["metadata"] ?? "{}"))
57581
+ ci_proof: row["ci_proof"] ? parseJson3(String(row["ci_proof"])) : null,
57582
+ cleanup_proof: row["cleanup_proof"] ? parseJson3(String(row["cleanup_proof"])) : null,
57583
+ metadata: parseJson3(String(row["metadata"] ?? "{}"))
56213
57584
  };
56214
57585
  }
56215
57586
 
@@ -56273,8 +57644,8 @@ class SqlitePrGroupTransaction {
56273
57644
  WHERE id = ?
56274
57645
  `).run(attempt.status, attempt.started_at, attempt.last_heartbeat_at, attempt.handed_off_at, attempt.fenced_at, attempt.terminal_at, attempt.updated_at, attempt.id);
56275
57646
  }
56276
- async getEventByIdempotency(groupId, key) {
56277
- const row = this.db.query("SELECT * FROM pr_group_events WHERE group_id = ? AND idempotency_key = ? LIMIT 1").get(groupId, key);
57647
+ async getEventByIdempotency(groupId, key2) {
57648
+ const row = this.db.query("SELECT * FROM pr_group_events WHERE group_id = ? AND idempotency_key = ? LIMIT 1").get(groupId, key2);
56278
57649
  return row ? eventFromRow2(row) : null;
56279
57650
  }
56280
57651
  async findEventByReceiptKey(receiptKey) {
@@ -56284,14 +57655,14 @@ class SqlitePrGroupTransaction {
56284
57655
  async findEvent(groupId, filters) {
56285
57656
  const clauses = ["group_id = ?", "event_type = ?"];
56286
57657
  const values = [groupId, filters.event_type];
56287
- for (const key of ["attempt_id", "head_sha", "outcome", "receipt_key"]) {
56288
- if (!(key in filters))
57658
+ for (const key2 of ["attempt_id", "head_sha", "outcome", "receipt_key"]) {
57659
+ if (!(key2 in filters))
56289
57660
  continue;
56290
- if (filters[key] === null)
56291
- clauses.push(`${key} IS NULL`);
57661
+ if (filters[key2] === null)
57662
+ clauses.push(`${key2} IS NULL`);
56292
57663
  else {
56293
- clauses.push(`${key} = ?`);
56294
- values.push(String(filters[key]));
57664
+ clauses.push(`${key2} = ?`);
57665
+ values.push(String(filters[key2]));
56295
57666
  }
56296
57667
  }
56297
57668
  const row = this.db.query(`SELECT * FROM pr_group_events WHERE ${clauses.join(" AND ")} ORDER BY sequence DESC LIMIT 1`).get(...values);
@@ -56386,7 +57757,7 @@ class SqlitePrGroupLedgerPersistence {
56386
57757
  }
56387
57758
  }
56388
57759
  var sqliteTransactionTails2;
56389
- var init_sqlite2 = __esm(() => {
57760
+ var init_sqlite3 = __esm(() => {
56390
57761
  sqliteTransactionTails2 = new WeakMap;
56391
57762
  });
56392
57763
 
@@ -56417,10 +57788,10 @@ function createLocalPrGroupLedger(db = getDatabase()) {
56417
57788
  var init_pr_groups2 = __esm(() => {
56418
57789
  init_database();
56419
57790
  init_ledger();
56420
- init_sqlite2();
57791
+ init_sqlite3();
56421
57792
  init_types3();
56422
57793
  init_ledger();
56423
- init_sqlite2();
57794
+ init_sqlite3();
56424
57795
  init_http_client();
56425
57796
  init_postgres();
56426
57797
  });
@@ -56431,7 +57802,7 @@ __export(exports_serve, {
56431
57802
  taskToSummary: () => taskToSummary,
56432
57803
  startServer: () => startServer,
56433
57804
  serveStaticFile: () => serveStaticFile,
56434
- json: () => json2,
57805
+ json: () => json3,
56435
57806
  checkAuth: () => checkAuth,
56436
57807
  SECURITY_HEADERS: () => SECURITY_HEADERS,
56437
57808
  MIME_TYPES: () => MIME_TYPES
@@ -56517,9 +57888,9 @@ function checkRateLimit(ip) {
56517
57888
  }
56518
57889
  return { allowed: true };
56519
57890
  }
56520
- function json2(data, status = 200, headers) {
57891
+ function json3(data, status2 = 200, headers) {
56521
57892
  return new Response(JSON.stringify(data), {
56522
- status,
57893
+ status: status2,
56523
57894
  headers: {
56524
57895
  "Content-Type": "application/json",
56525
57896
  ...SECURITY_HEADERS,
@@ -56539,29 +57910,29 @@ function serveStaticFile(filePath) {
56539
57910
  }
56540
57911
  });
56541
57912
  }
56542
- function taskToSummary(task2, fields) {
57913
+ function taskToSummary(task3, fields) {
56543
57914
  const full = {
56544
- id: task2.id,
56545
- short_id: task2.short_id,
56546
- title: task2.title,
56547
- description: task2.description,
56548
- status: task2.status,
56549
- priority: task2.priority,
56550
- project_id: task2.project_id,
56551
- plan_id: task2.plan_id,
56552
- task_list_id: task2.task_list_id,
56553
- agent_id: task2.agent_id,
56554
- assigned_to: task2.assigned_to,
56555
- working_dir: task2.working_dir,
56556
- locked_by: task2.locked_by,
56557
- tags: task2.tags,
56558
- metadata: task2.metadata,
56559
- version: task2.version,
56560
- created_at: task2.created_at,
56561
- updated_at: task2.updated_at,
56562
- completed_at: task2.completed_at,
56563
- due_at: task2.due_at,
56564
- recurrence_rule: task2.recurrence_rule
57915
+ id: task3.id,
57916
+ short_id: task3.short_id,
57917
+ title: task3.title,
57918
+ description: task3.description,
57919
+ status: task3.status,
57920
+ priority: task3.priority,
57921
+ project_id: task3.project_id,
57922
+ plan_id: task3.plan_id,
57923
+ task_list_id: task3.task_list_id,
57924
+ agent_id: task3.agent_id,
57925
+ assigned_to: task3.assigned_to,
57926
+ working_dir: task3.working_dir,
57927
+ locked_by: task3.locked_by,
57928
+ tags: task3.tags,
57929
+ metadata: task3.metadata,
57930
+ version: task3.version,
57931
+ created_at: task3.created_at,
57932
+ updated_at: task3.updated_at,
57933
+ completed_at: task3.completed_at,
57934
+ due_at: task3.due_at,
57935
+ recurrence_rule: task3.recurrence_rule
56565
57936
  };
56566
57937
  if (!fields || fields.length === 0)
56567
57938
  return full;
@@ -56662,7 +58033,7 @@ Dashboard not found at: ${dashboardDir}`);
56662
58033
  "Access-Control-Allow-Headers": "Content-Type, X-API-Key, Authorization",
56663
58034
  Vary: "Origin"
56664
58035
  } : undefined;
56665
- const jsonWithCors = (data, status = 200) => json2(data, status, corsHeaders);
58036
+ const jsonWithCors = (data, status2 = 200) => json3(data, status2, corsHeaders);
56666
58037
  if (method === "OPTIONS") {
56667
58038
  return new Response(null, {
56668
58039
  headers: corsHeaders || {
@@ -56743,13 +58114,13 @@ Dashboard not found at: ${dashboardDir}`);
56743
58114
  return res;
56744
58115
  }
56745
58116
  if (path === "/api/health" && method === "GET") {
56746
- return handleHealth(ctx, json2);
58117
+ return handleHealth(ctx, json3);
56747
58118
  }
56748
58119
  if (path === "/api/headless" && method === "GET") {
56749
- return handleHeadlessBoundary(ctx, json2);
58120
+ return handleHeadlessBoundary(ctx, json3);
56750
58121
  }
56751
58122
  if (path === "/api/stats" && method === "GET") {
56752
- return handleStats(ctx, json2);
58123
+ return handleStats(ctx, json3);
56753
58124
  }
56754
58125
  if (path === "/api/tasks" && method === "GET") {
56755
58126
  return handleListTasks(req, url, ctx, jsonWithCors, taskToSummary);
@@ -56764,16 +58135,16 @@ Dashboard not found at: ${dashboardDir}`);
56764
58135
  return handleTasksExport(req, url, ctx, jsonWithCors, taskToSummary);
56765
58136
  }
56766
58137
  if (path === "/api/tasks/bulk" && method === "POST") {
56767
- return handleTasksBulk(req, ctx, json2);
58138
+ return handleTasksBulk(req, ctx, json3);
56768
58139
  }
56769
58140
  if (path === "/api/tasks/status" && method === "GET") {
56770
- return handleTasksStatus(req, url, ctx, json2);
58141
+ return handleTasksStatus(req, url, ctx, json3);
56771
58142
  }
56772
58143
  if (path === "/api/tasks/next" && method === "GET") {
56773
58144
  return handleTasksNext(req, url, ctx, jsonWithCors, taskToSummary);
56774
58145
  }
56775
58146
  if (path === "/api/tasks/active" && method === "GET") {
56776
- return handleTasksActive(req, url, ctx, json2);
58147
+ return handleTasksActive(req, url, ctx, json3);
56777
58148
  }
56778
58149
  if (path === "/api/tasks/stale" && method === "GET") {
56779
58150
  return handleTasksStale(req, url, ctx, jsonWithCors, taskToSummary);
@@ -56786,11 +58157,11 @@ Dashboard not found at: ${dashboardDir}`);
56786
58157
  }
56787
58158
  const attachmentsMatch = path.match(/^\/api\/tasks\/([^/]+)\/attachments$/);
56788
58159
  if (attachmentsMatch && method === "GET") {
56789
- return handleTaskAttachments(attachmentsMatch[1], ctx, json2);
58160
+ return handleTaskAttachments(attachmentsMatch[1], ctx, json3);
56790
58161
  }
56791
58162
  const progressMatch = path.match(/^\/api\/tasks\/([^/]+)\/progress$/);
56792
58163
  if (progressMatch) {
56793
- const res = await handleTaskProgress(progressMatch[1], req, method, ctx, json2, url);
58164
+ const res = await handleTaskProgress(progressMatch[1], req, method, ctx, json3, url);
56794
58165
  if (res !== null)
56795
58166
  return res;
56796
58167
  }
@@ -56804,7 +58175,7 @@ Dashboard not found at: ${dashboardDir}`);
56804
58175
  return handlePatchTask(id, req, ctx, jsonWithCors, taskToSummary);
56805
58176
  }
56806
58177
  if (method === "DELETE") {
56807
- return handleDeleteTask(id, ctx, json2);
58178
+ return handleDeleteTask(id, ctx, json3);
56808
58179
  }
56809
58180
  }
56810
58181
  const startMatch = path.match(/^\/api\/tasks\/([^/]+)\/start$/);
@@ -56820,7 +58191,7 @@ Dashboard not found at: ${dashboardDir}`);
56820
58191
  return handleCompleteTask(completeMatch[1], ctx, jsonWithCors, taskToSummary);
56821
58192
  }
56822
58193
  if (path === "/api/projects" && method === "GET") {
56823
- return handleListProjects(url, ctx, json2);
58194
+ return handleListProjects(url, ctx, json3);
56824
58195
  }
56825
58196
  if (path === "/api/agents/me" && method === "GET") {
56826
58197
  return handleAgentMe(req, url, ctx, jsonWithCors, taskToSummary);
@@ -56833,92 +58204,92 @@ Dashboard not found at: ${dashboardDir}`);
56833
58204
  return handleClaimTask(req, ctx, jsonWithCors, taskToSummary);
56834
58205
  }
56835
58206
  if (path === "/api/orgs" && method === "GET") {
56836
- return handleListOrgs(ctx, json2);
58207
+ return handleListOrgs(ctx, json3);
56837
58208
  }
56838
58209
  if (path === "/api/orgs" && method === "POST") {
56839
- return handleCreateOrg(req, ctx, json2);
58210
+ return handleCreateOrg(req, ctx, json3);
56840
58211
  }
56841
58212
  const orgMatch = path.match(/^\/api\/orgs\/([^/]+)$/);
56842
58213
  if (orgMatch && method === "PATCH") {
56843
- return handleUpdateOrg(orgMatch[1], req, ctx, json2);
58214
+ return handleUpdateOrg(orgMatch[1], req, ctx, json3);
56844
58215
  }
56845
58216
  if (orgMatch && method === "DELETE") {
56846
- return handleDeleteOrg(orgMatch[1], ctx, json2);
58217
+ return handleDeleteOrg(orgMatch[1], ctx, json3);
56847
58218
  }
56848
58219
  if (path === "/api/org" && method === "GET") {
56849
- return handleOrgChart(ctx, json2);
58220
+ return handleOrgChart(ctx, json3);
56850
58221
  }
56851
58222
  const teamMatch = path.match(/^\/api\/agents\/([^/]+)\/team$/);
56852
58223
  if (teamMatch && method === "GET") {
56853
- return handleAgentTeam(teamMatch[1], ctx, json2);
58224
+ return handleAgentTeam(teamMatch[1], ctx, json3);
56854
58225
  }
56855
58226
  if (path === "/api/agents" && method === "GET") {
56856
- return handleListAgents(url, ctx, json2);
58227
+ return handleListAgents(url, ctx, json3);
56857
58228
  }
56858
58229
  if (path === "/api/projects" && method === "POST") {
56859
- return handleCreateProject(req, ctx, json2);
58230
+ return handleCreateProject(req, ctx, json3);
56860
58231
  }
56861
58232
  const projectDeleteMatch = path.match(/^\/api\/projects\/([^/]+)$/);
56862
58233
  if (projectDeleteMatch && method === "DELETE") {
56863
- return handleDeleteProject(projectDeleteMatch[1], ctx, json2);
58234
+ return handleDeleteProject(projectDeleteMatch[1], ctx, json3);
56864
58235
  }
56865
58236
  if (path === "/api/agents" && method === "POST") {
56866
- return handleRegisterAgent(req, ctx, json2);
58237
+ return handleRegisterAgent(req, ctx, json3);
56867
58238
  }
56868
58239
  const agentMatch = path.match(/^\/api\/agents\/([^/]+)$/);
56869
58240
  if (agentMatch && method === "PATCH") {
56870
- return handleUpdateAgent(agentMatch[1], req, ctx, json2);
58241
+ return handleUpdateAgent(agentMatch[1], req, ctx, json3);
56871
58242
  }
56872
58243
  if (agentMatch && method === "DELETE") {
56873
- return handleDeleteAgent(agentMatch[1], ctx, json2);
58244
+ return handleDeleteAgent(agentMatch[1], ctx, json3);
56874
58245
  }
56875
58246
  if (path === "/api/agents/bulk" && method === "POST") {
56876
- return handleBulkDeleteAgents(req, ctx, json2);
58247
+ return handleBulkDeleteAgents(req, ctx, json3);
56877
58248
  }
56878
58249
  if (path === "/api/projects/bulk" && method === "POST") {
56879
- return handleBulkDeleteProjects(req, ctx, json2);
58250
+ return handleBulkDeleteProjects(req, ctx, json3);
56880
58251
  }
56881
58252
  if (path === "/api/doctor" && method === "GET") {
56882
- return handleDoctor(ctx, json2);
58253
+ return handleDoctor(ctx, json3);
56883
58254
  }
56884
58255
  if (path === "/api/report" && method === "GET") {
56885
- return handleReport(req, url, ctx, json2);
58256
+ return handleReport(req, url, ctx, json3);
56886
58257
  }
56887
58258
  if (path === "/api/activity" && method === "GET") {
56888
- return handleActivity(req, url, ctx, json2);
58259
+ return handleActivity(req, url, ctx, json3);
56889
58260
  }
56890
58261
  const historyMatch = path.match(/^\/api\/tasks\/([^/]+)\/history$/);
56891
58262
  if (historyMatch && method === "GET") {
56892
- return handleTaskHistory(historyMatch[1], ctx, json2, url);
58263
+ return handleTaskHistory(historyMatch[1], ctx, json3, url);
56893
58264
  }
56894
58265
  if (path === "/api/webhooks" && method === "GET") {
56895
- return handleListWebhooks(ctx, json2);
58266
+ return handleListWebhooks(ctx, json3);
56896
58267
  }
56897
58268
  if (path === "/api/webhooks" && method === "POST") {
56898
- return handleCreateWebhook(req, ctx, json2);
58269
+ return handleCreateWebhook(req, ctx, json3);
56899
58270
  }
56900
58271
  const webhookMatch = path.match(/^\/api\/webhooks\/([^/]+)$/);
56901
58272
  if (webhookMatch && method === "DELETE") {
56902
- return handleDeleteWebhook(webhookMatch[1], ctx, json2);
58273
+ return handleDeleteWebhook(webhookMatch[1], ctx, json3);
56903
58274
  }
56904
58275
  if (path === "/api/templates" && method === "GET") {
56905
- return handleListTemplates(ctx, json2);
58276
+ return handleListTemplates(ctx, json3);
56906
58277
  }
56907
58278
  if (path === "/api/templates" && method === "POST") {
56908
- return handleCreateTemplate(req, ctx, json2);
58279
+ return handleCreateTemplate(req, ctx, json3);
56909
58280
  }
56910
58281
  const templateMatch = path.match(/^\/api\/templates\/([^/]+)$/);
56911
58282
  if (templateMatch && method === "DELETE") {
56912
- return handleDeleteTemplate(templateMatch[1], ctx, json2);
58283
+ return handleDeleteTemplate(templateMatch[1], ctx, json3);
56913
58284
  }
56914
58285
  if (path === "/api/plans" && method === "GET") {
56915
- return handleListPlans(url, ctx, json2);
58286
+ return handleListPlans(url, ctx, json3);
56916
58287
  }
56917
58288
  if (path === "/api/plans" && method === "POST") {
56918
- return handleCreatePlan(req, ctx, json2);
58289
+ return handleCreatePlan(req, ctx, json3);
56919
58290
  }
56920
58291
  if (path === "/api/plans/bulk" && method === "POST") {
56921
- return handleBulkDeletePlans(req, ctx, json2);
58292
+ return handleBulkDeletePlans(req, ctx, json3);
56922
58293
  }
56923
58294
  const planMatch = path.match(/^\/api\/plans\/([^/]+)$/);
56924
58295
  if (planMatch) {
@@ -56927,16 +58298,16 @@ Dashboard not found at: ${dashboardDir}`);
56927
58298
  return handleGetPlan(id, ctx, jsonWithCors, taskToSummary);
56928
58299
  }
56929
58300
  if (method === "PATCH") {
56930
- return handleUpdatePlan(id, req, ctx, json2);
58301
+ return handleUpdatePlan(id, req, ctx, json3);
56931
58302
  }
56932
58303
  if (method === "DELETE") {
56933
- return handleDeletePlan(id, ctx, json2);
58304
+ return handleDeletePlan(id, ctx, json3);
56934
58305
  }
56935
58306
  }
56936
58307
  const staticRes = handleStaticFiles(path, method, ctx, jsonWithCors, serveStaticFile);
56937
58308
  if (staticRes)
56938
58309
  return staticRes;
56939
- return json2({ error: "Not found" }, 404);
58310
+ return json3({ error: "Not found" }, 404);
56940
58311
  }
56941
58312
  });
56942
58313
  const shutdown = () => {
@@ -57136,52 +58507,52 @@ function resolveId(partialId, table = "tasks") {
57136
58507
  }
57137
58508
  return id;
57138
58509
  }
57139
- function formatTask(task2) {
57140
- const id = task2.short_id || task2.id.slice(0, 8);
57141
- const assigned = task2.assigned_to ? ` -> ${task2.assigned_to}` : "";
57142
- const lockState = lockDisplayState(task2.locked_by, task2.locked_at);
58510
+ function formatTask(task3) {
58511
+ const id = task3.short_id || task3.id.slice(0, 8);
58512
+ const assigned = task3.assigned_to ? ` -> ${task3.assigned_to}` : "";
58513
+ const lockState = lockDisplayState(task3.locked_by, task3.locked_at);
57143
58514
  const lock = lockState.held ? ` [locked:${lockState.holder}]` : "";
57144
- const recur = task2.recurrence_rule ? ` [\u21BB]` : "";
57145
- return `${id} ${task2.status.padEnd(11)} ${task2.priority.padEnd(8)} ${task2.title}${assigned}${lock}${recur}`;
58515
+ const recur = task3.recurrence_rule ? ` [\u21BB]` : "";
58516
+ return `${id} ${task3.status.padEnd(11)} ${task3.priority.padEnd(8)} ${task3.title}${assigned}${lock}${recur}`;
57146
58517
  }
57147
- function formatTaskDetail(task2, maxDescriptionChars) {
58518
+ function formatTaskDetail(task3, maxDescriptionChars) {
57148
58519
  const parts = [
57149
- `ID: ${task2.id}`,
57150
- `Title: ${task2.title}`,
57151
- `Status: ${task2.status}`,
57152
- `Priority: ${task2.priority}`
58520
+ `ID: ${task3.id}`,
58521
+ `Title: ${task3.title}`,
58522
+ `Status: ${task3.status}`,
58523
+ `Priority: ${task3.priority}`
57153
58524
  ];
57154
- if (task2.description) {
57155
- const desc = maxDescriptionChars && task2.description.length > maxDescriptionChars ? task2.description.slice(0, maxDescriptionChars) + "\u2026" : task2.description;
58525
+ if (task3.description) {
58526
+ const desc = maxDescriptionChars && task3.description.length > maxDescriptionChars ? task3.description.slice(0, maxDescriptionChars) + "\u2026" : task3.description;
57156
58527
  parts.push(`Description: ${desc}`);
57157
58528
  }
57158
- if (task2.assigned_to)
57159
- parts.push(`Assigned to: ${task2.assigned_to}`);
57160
- if (task2.agent_id)
57161
- parts.push(`Agent: ${task2.agent_id}`);
57162
- const detailLock = lockDisplayState(task2.locked_by, task2.locked_at);
58529
+ if (task3.assigned_to)
58530
+ parts.push(`Assigned to: ${task3.assigned_to}`);
58531
+ if (task3.agent_id)
58532
+ parts.push(`Agent: ${task3.agent_id}`);
58533
+ const detailLock = lockDisplayState(task3.locked_by, task3.locked_at);
57163
58534
  if (detailLock.held)
57164
58535
  parts.push(`Locked by: ${detailLock.holder}`);
57165
58536
  else if (detailLock.expired)
57166
58537
  parts.push(`Lock: ${formatExpiredLock(detailLock)}`);
57167
- if (task2.parent_id)
57168
- parts.push(`Parent: ${task2.parent_id}`);
57169
- if (task2.project_id)
57170
- parts.push(`Project: ${task2.project_id}`);
57171
- if (task2.plan_id)
57172
- parts.push(`Plan: ${task2.plan_id}`);
57173
- if (task2.due_at)
57174
- parts.push(`Due: ${task2.due_at.slice(0, 10)}`);
57175
- if (task2.tags.length > 0)
57176
- parts.push(`Tags: ${task2.tags.join(", ")}`);
57177
- if (task2.recurrence_rule)
57178
- parts.push(`Recurrence: ${task2.recurrence_rule}`);
57179
- if (task2.recurrence_parent_id)
57180
- parts.push(`Recurrence parent: ${task2.recurrence_parent_id}`);
57181
- parts.push(`Version: ${task2.version}`);
57182
- parts.push(`Created: ${task2.created_at}`);
57183
- if (task2.completed_at)
57184
- parts.push(`Completed: ${task2.completed_at}`);
58538
+ if (task3.parent_id)
58539
+ parts.push(`Parent: ${task3.parent_id}`);
58540
+ if (task3.project_id)
58541
+ parts.push(`Project: ${task3.project_id}`);
58542
+ if (task3.plan_id)
58543
+ parts.push(`Plan: ${task3.plan_id}`);
58544
+ if (task3.due_at)
58545
+ parts.push(`Due: ${task3.due_at.slice(0, 10)}`);
58546
+ if (task3.tags.length > 0)
58547
+ parts.push(`Tags: ${task3.tags.join(", ")}`);
58548
+ if (task3.recurrence_rule)
58549
+ parts.push(`Recurrence: ${task3.recurrence_rule}`);
58550
+ if (task3.recurrence_parent_id)
58551
+ parts.push(`Recurrence parent: ${task3.recurrence_parent_id}`);
58552
+ parts.push(`Version: ${task3.version}`);
58553
+ parts.push(`Created: ${task3.created_at}`);
58554
+ if (task3.completed_at)
58555
+ parts.push(`Completed: ${task3.completed_at}`);
57185
58556
  return parts.join(`
57186
58557
  `);
57187
58558
  }