@hasna/todos 0.15.14 → 0.15.16

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 (45) hide show
  1. package/dist/cli/cloud-router.d.ts.map +1 -1
  2. package/dist/cli/commands/mcp-hooks-commands.d.ts.map +1 -1
  3. package/dist/cli/commands/task-commands.d.ts.map +1 -1
  4. package/dist/cli/index.js +12470 -10747
  5. package/dist/contracts.js +1 -1
  6. package/dist/index.js +358 -47
  7. package/dist/lib/agent-adapter-docs.d.ts +10 -0
  8. package/dist/lib/agent-adapter-docs.d.ts.map +1 -1
  9. package/dist/mcp/index.js +2290 -803
  10. package/dist/mcp.js +1 -1
  11. package/dist/project-registration.js +1 -1
  12. package/dist/registry.js +1 -1
  13. package/dist/release-provenance.json +5 -5
  14. package/dist/sdk/index.d.ts +1 -1
  15. package/dist/sdk/index.d.ts.map +1 -1
  16. package/dist/sdk/index.js +7 -0
  17. package/dist/sdk/v1.generated.d.ts +23 -0
  18. package/dist/sdk/v1.generated.d.ts.map +1 -1
  19. package/dist/server/cloud.d.ts +3 -0
  20. package/dist/server/cloud.d.ts.map +1 -1
  21. package/dist/server/index.js +19806 -18319
  22. package/dist/server/openapi.d.ts +134 -0
  23. package/dist/server/openapi.d.ts.map +1 -1
  24. package/dist/server/v1.d.ts +2 -1
  25. package/dist/server/v1.d.ts.map +1 -1
  26. package/dist/task-manifest/authority.d.ts +3 -1
  27. package/dist/task-manifest/authority.d.ts.map +1 -1
  28. package/dist/task-manifest/backend.d.ts +18 -1
  29. package/dist/task-manifest/backend.d.ts.map +1 -1
  30. package/dist/task-manifest/http.d.ts +2 -1
  31. package/dist/task-manifest/http.d.ts.map +1 -1
  32. package/dist/task-manifest/index.d.ts +2 -2
  33. package/dist/task-manifest/index.d.ts.map +1 -1
  34. package/dist/task-manifest/postgres.d.ts +3 -1
  35. package/dist/task-manifest/postgres.d.ts.map +1 -1
  36. package/dist/task-manifest/schema-sql.d.ts +3 -1
  37. package/dist/task-manifest/schema-sql.d.ts.map +1 -1
  38. package/dist/task-manifest/schema.d.ts +2 -1
  39. package/dist/task-manifest/schema.d.ts.map +1 -1
  40. package/dist/task-manifest/sqlite.d.ts +4 -2
  41. package/dist/task-manifest/sqlite.d.ts.map +1 -1
  42. package/dist/task-manifest/types.d.ts +24 -1
  43. package/dist/task-manifest/types.d.ts.map +1 -1
  44. package/dist/task-manifest.js +357 -46
  45. package/package.json +1 -1
package/dist/mcp/index.js CHANGED
@@ -21041,10 +21041,72 @@ async function requireTagsFilterCapability(client) {
21041
21041
  throw new Error(`REMOTE_TAGS_FILTER_UNSUPPORTED: configured Todos authority ${authority} does not advertise the tags ` + "query param on GET /v1/tasks; deploy the current @hasna/todos /v1 server to filter by tag; " + "no unfiltered task read was issued");
21042
21042
  }
21043
21043
  }
21044
- async function requestCloudTaskPage(client, filter) {
21044
+ function parseCloudTaskTotal(raw) {
21045
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
21046
+ return;
21047
+ const total = raw.total;
21048
+ return typeof total === "number" && Number.isSafeInteger(total) && total >= 0 ? total : undefined;
21049
+ }
21050
+ async function requestRawCloudTaskPage(client, filter) {
21045
21051
  const res = await requiredRemoteRoute(client, "/v1/tasks", () => client.list("tasks", { query: toListQuery(filter) }));
21046
21052
  const envelope = res.raw;
21047
- return Array.isArray(envelope?.tasks) ? envelope.tasks : res.items;
21053
+ return {
21054
+ tasks: Array.isArray(envelope?.tasks) ? envelope.tasks : res.items,
21055
+ total: parseCloudTaskTotal(res.raw)
21056
+ };
21057
+ }
21058
+ function cloudTaskListFilterError(code, taskListId, detail) {
21059
+ return new Error(`${code}: hosted authority returned rows outside requested task_list_id ${taskListId}; ${detail}; refusing an incomplete exact-list result`);
21060
+ }
21061
+ async function requestCloudTaskPage(client, filter) {
21062
+ const firstPage = await requestRawCloudTaskPage(client, filter);
21063
+ const tasks = firstPage.tasks;
21064
+ if (filter.task_list_id === undefined)
21065
+ return tasks;
21066
+ const taskListId = filter.task_list_id;
21067
+ if (tasks.every((task) => task.task_list_id === taskListId))
21068
+ return tasks;
21069
+ const scanLimit = filter.limit;
21070
+ const startOffset = filter.offset ?? 0;
21071
+ if (startOffset !== 0 || scanLimit === undefined || !Number.isSafeInteger(scanLimit) || scanLimit <= 0 || firstPage.total === undefined) {
21072
+ throw cloudTaskListFilterError("REMOTE_TASK_LIST_FILTER_UNSUPPORTED", taskListId, "the response does not provide complete bounded total/offset pagination evidence");
21073
+ }
21074
+ const total = firstPage.total;
21075
+ if (total > scanLimit) {
21076
+ throw cloudTaskListFilterError("REMOTE_TASK_LIST_FILTER_INCOMPLETE", taskListId, `reported total ${total} exceeds bounded scan limit ${scanLimit}`);
21077
+ }
21078
+ if (tasks.length > total) {
21079
+ throw cloudTaskListFilterError("REMOTE_TASK_LIST_FILTER_UNSUPPORTED", taskListId, `the first page contains ${tasks.length} rows but reports total ${total}`);
21080
+ }
21081
+ const seenTaskIds = new Set;
21082
+ for (const task of tasks) {
21083
+ if (seenTaskIds.has(task.id)) {
21084
+ throw cloudTaskListFilterError("REMOTE_TASK_LIST_FILTER_UNSUPPORTED", taskListId, `the first page repeats task id ${task.id}`);
21085
+ }
21086
+ seenTaskIds.add(task.id);
21087
+ }
21088
+ while (tasks.length < total) {
21089
+ const remaining = total - tasks.length;
21090
+ const page = await requestRawCloudTaskPage(client, {
21091
+ ...filter,
21092
+ offset: tasks.length,
21093
+ limit: Math.min(scanLimit - tasks.length, remaining)
21094
+ });
21095
+ if (page.total !== total) {
21096
+ throw cloudTaskListFilterError("REMOTE_TASK_LIST_FILTER_UNSUPPORTED", taskListId, `pagination total changed from ${total} to ${String(page.total)}`);
21097
+ }
21098
+ if (page.tasks.length === 0 || tasks.length + page.tasks.length > total) {
21099
+ throw cloudTaskListFilterError("REMOTE_TASK_LIST_FILTER_UNSUPPORTED", taskListId, "pagination did not make bounded progress toward the reported total");
21100
+ }
21101
+ for (const task of page.tasks) {
21102
+ if (seenTaskIds.has(task.id)) {
21103
+ throw cloudTaskListFilterError("REMOTE_TASK_LIST_FILTER_UNSUPPORTED", taskListId, `pagination repeats task id ${task.id}`);
21104
+ }
21105
+ seenTaskIds.add(task.id);
21106
+ }
21107
+ tasks.push(...page.tasks);
21108
+ }
21109
+ return tasks.filter((task) => task.task_list_id === taskListId);
21048
21110
  }
21049
21111
  async function cloudListTasks(client, filter = {}) {
21050
21112
  if (filter.tags?.length)
@@ -21083,12 +21145,10 @@ async function cloudGetTask(client, id) {
21083
21145
  }
21084
21146
  async function cloudCreateTask(client, input) {
21085
21147
  const expectedParentId = typeof input["parent_id"] === "string" ? input["parent_id"] : null;
21086
- const created = unwrapTask(await requiredRemoteRoute(client, "/v1/tasks", () => expectedParentId === null ? client.create("tasks", input) : client.create("tasks", input, { retry: false }), ["PARENT_TASK_NOT_FOUND"]));
21148
+ const created = unwrapTask(await requiredRemoteRoute(client, "/v1/tasks", () => client.create("tasks", input, { retry: false }), ["PARENT_TASK_NOT_FOUND"]));
21087
21149
  if (!created || typeof created.id !== "string" || !created.id.trim()) {
21088
21150
  throw new Error(`REMOTE_API_INCOMPATIBLE: configured Todos authority ${remoteAuthorityBase(client)} returned a task create ` + "response without a stored task id; no success row or local SQLite fallback is permitted");
21089
21151
  }
21090
- if (expectedParentId === null)
21091
- return created;
21092
21152
  const persisted = await cloudGetTask(client, created.id);
21093
21153
  if (!persisted || persisted.id !== created.id || (persisted.parent_id ?? null) !== expectedParentId) {
21094
21154
  throw new Error(`TASK_CREATE_PERSISTENCE_UNVERIFIED: configured Todos authority ${remoteAuthorityBase(client)} accepted ` + `POST /v1/tasks but authoritative GET /v1/tasks/${encodeURIComponent(created.id)} did not return the same ` + "stored task id and parent_id; no success row or local SQLite fallback is permitted");
@@ -35415,7 +35475,7 @@ var package_default;
35415
35475
  var init_package = __esm(() => {
35416
35476
  package_default = {
35417
35477
  name: "@hasna/todos",
35418
- version: "0.15.14",
35478
+ version: "0.15.16",
35419
35479
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
35420
35480
  type: "module",
35421
35481
  main: "dist/index.js",
@@ -50911,6 +50971,1337 @@ var init_project_registration = __esm(() => {
50911
50971
  init_types4();
50912
50972
  });
50913
50973
 
50974
+ // src/task-manifest/canonical.ts
50975
+ import { createHash as createHash16 } from "crypto";
50976
+ function canonicalize3(value) {
50977
+ if (Array.isArray(value))
50978
+ return value.map(canonicalize3);
50979
+ if (value !== null && typeof value === "object") {
50980
+ return Object.fromEntries(Object.entries(value).filter(([, entry2]) => entry2 !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry2]) => [key, canonicalize3(entry2)]));
50981
+ }
50982
+ return value;
50983
+ }
50984
+ function canonicalJson(value) {
50985
+ return JSON.stringify(canonicalize3(value));
50986
+ }
50987
+ function canonicalDigest(value) {
50988
+ return createHash16("sha256").update(canonicalJson(value)).digest("hex");
50989
+ }
50990
+ function deterministicUuid(namespace, ...parts) {
50991
+ const bytes = createHash16("sha256").update([namespace, ...parts].join("\x1F")).digest().subarray(0, 16);
50992
+ bytes[6] = bytes[6] & 15 | 80;
50993
+ bytes[8] = bytes[8] & 63 | 128;
50994
+ const hex = bytes.toString("hex");
50995
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
50996
+ }
50997
+ var init_canonical = () => {};
50998
+
50999
+ // src/task-manifest/types.ts
51000
+ var TODOS_TASK_MANIFEST_ROUTE = "todos.task-manifest.v1", TODOS_TASK_MANIFEST_SCHEMA_VERSION = 1, TodosTaskManifestError;
51001
+ var init_types5 = __esm(() => {
51002
+ TodosTaskManifestError = class TodosTaskManifestError extends Error {
51003
+ code;
51004
+ details;
51005
+ constructor(code, message, details = {}) {
51006
+ super(message);
51007
+ this.code = code;
51008
+ this.details = details;
51009
+ this.name = "TodosTaskManifestError";
51010
+ }
51011
+ };
51012
+ });
51013
+
51014
+ // src/task-manifest/schema.ts
51015
+ function parseTodosTaskManifest(input) {
51016
+ const parsed = schema.safeParse(input);
51017
+ if (!parsed.success) {
51018
+ 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 });
51019
+ }
51020
+ const taskKeys = new Set;
51021
+ let comments = 0;
51022
+ let verifications = 0;
51023
+ for (const entry2 of parsed.data.tasks) {
51024
+ if (taskKeys.has(entry2.key)) {
51025
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `Duplicate task key: ${entry2.key}`);
51026
+ }
51027
+ taskKeys.add(entry2.key);
51028
+ comments += entry2.comments?.length ?? 0;
51029
+ verifications += entry2.verifications?.length ?? 0;
51030
+ }
51031
+ if (comments > TODOS_TASK_MANIFEST_BOUNDS.comments || verifications > TODOS_TASK_MANIFEST_BOUNDS.verifications) {
51032
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_BOUNDS_EXCEEDED", "Task manifest nested resource bounds exceeded", {
51033
+ comments,
51034
+ verifications
51035
+ });
51036
+ }
51037
+ const seenDependencies = new Set;
51038
+ const dependencyGraph = new Map;
51039
+ for (const edge of parsed.data.dependencies ?? []) {
51040
+ if (!taskKeys.has(edge.task) || !taskKeys.has(edge.depends_on)) {
51041
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_FOREIGN_REFERENCE", `Dependency contains foreign task key: ${edge.task} -> ${edge.depends_on}`);
51042
+ }
51043
+ if (edge.task === edge.depends_on) {
51044
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `Task ${edge.task} cannot depend on itself`);
51045
+ }
51046
+ const identity = `${edge.task}\x1F${edge.depends_on}`;
51047
+ if (seenDependencies.has(identity)) {
51048
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `Duplicate dependency: ${edge.task} -> ${edge.depends_on}`);
51049
+ }
51050
+ seenDependencies.add(identity);
51051
+ const prerequisites = dependencyGraph.get(edge.task) ?? [];
51052
+ prerequisites.push(edge.depends_on);
51053
+ dependencyGraph.set(edge.task, prerequisites);
51054
+ }
51055
+ const visiting = new Set;
51056
+ const visited = new Set;
51057
+ const visit = (taskKey) => {
51058
+ if (visiting.has(taskKey)) {
51059
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `Dependency cycle contains task ${taskKey}`);
51060
+ }
51061
+ if (visited.has(taskKey))
51062
+ return;
51063
+ visiting.add(taskKey);
51064
+ for (const prerequisite of dependencyGraph.get(taskKey) ?? [])
51065
+ visit(prerequisite);
51066
+ visiting.delete(taskKey);
51067
+ visited.add(taskKey);
51068
+ };
51069
+ for (const taskKey of taskKeys)
51070
+ visit(taskKey);
51071
+ return parsed.data;
51072
+ }
51073
+ function parseTodosTaskManifestCompensation(input) {
51074
+ const parsed = compensationSchema.safeParse(input);
51075
+ if (!parsed.success) {
51076
+ 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 });
51077
+ }
51078
+ return parsed.data;
51079
+ }
51080
+ function parseTodosTaskManifestBindingLookup(input) {
51081
+ const parsed = bindingLookupSchema.safeParse(input);
51082
+ if (!parsed.success) {
51083
+ 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 });
51084
+ }
51085
+ return parsed.data;
51086
+ }
51087
+ var TODOS_TASK_MANIFEST_BOUNDS, key, identifier, uuid2, scalar, boundedScalarRecord = (limit, field) => exports_external.record(exports_external.string().max(200), scalar).superRefine((value, context) => {
51088
+ if (Object.keys(value).length > limit) {
51089
+ context.addIssue({ code: exports_external.ZodIssueCode.custom, message: `${field} exceeds ${limit} fields` });
51090
+ }
51091
+ }), comment, verification, task2, dependency, effect, schema, compensationSchema, bindingLookupSchema;
51092
+ var init_schema2 = __esm(() => {
51093
+ init_zod();
51094
+ init_types();
51095
+ init_types5();
51096
+ TODOS_TASK_MANIFEST_BOUNDS = {
51097
+ tasks: 128,
51098
+ dependencies: 512,
51099
+ comments: 512,
51100
+ verifications: 512,
51101
+ effects: 64,
51102
+ metadata_fields: 128,
51103
+ effect_payload_fields: 128,
51104
+ request_bytes: 16777216,
51105
+ response_bytes: 1048576
51106
+ };
51107
+ key = exports_external.string().min(1).max(96).regex(/^[a-z][a-z0-9_-]*$/);
51108
+ identifier = exports_external.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);
51109
+ uuid2 = exports_external.string().uuid();
51110
+ scalar = exports_external.union([exports_external.string().max(4096), exports_external.number().finite(), exports_external.boolean(), exports_external.null()]);
51111
+ comment = exports_external.object({
51112
+ content: exports_external.string().min(1).max(16384),
51113
+ type: exports_external.enum(["comment", "progress", "status_change", "system"]).optional(),
51114
+ progress_pct: exports_external.number().int().min(0).max(100).optional(),
51115
+ agent_id: identifier.optional(),
51116
+ session_id: identifier.optional()
51117
+ }).strict();
51118
+ verification = exports_external.object({
51119
+ command: exports_external.string().min(1).max(8192),
51120
+ status: exports_external.enum(["passed", "failed", "unknown"]).optional(),
51121
+ output_summary: exports_external.string().max(16384).optional(),
51122
+ artifact_path: exports_external.string().max(4096).optional(),
51123
+ agent_id: identifier.optional()
51124
+ }).strict();
51125
+ task2 = exports_external.object({
51126
+ key,
51127
+ title: exports_external.string().min(1).max(500),
51128
+ description: exports_external.string().max(64000).optional(),
51129
+ status: exports_external.enum(TASK_STATUSES).optional(),
51130
+ priority: exports_external.enum(TASK_PRIORITIES).optional(),
51131
+ assigned_to: identifier.optional(),
51132
+ created_by: identifier.optional(),
51133
+ tags: exports_external.array(exports_external.string().min(1).max(100)).max(64).optional(),
51134
+ metadata: boundedScalarRecord(TODOS_TASK_MANIFEST_BOUNDS.metadata_fields, "metadata").optional(),
51135
+ comments: exports_external.array(comment).max(64).optional(),
51136
+ verifications: exports_external.array(verification).max(64).optional()
51137
+ }).strict();
51138
+ dependency = exports_external.object({ task: key, depends_on: key }).strict();
51139
+ effect = exports_external.object({
51140
+ topic: exports_external.string().min(1).max(200),
51141
+ payload: boundedScalarRecord(TODOS_TASK_MANIFEST_BOUNDS.effect_payload_fields, "effect payload")
51142
+ }).strict();
51143
+ schema = exports_external.object({
51144
+ version: exports_external.literal(1),
51145
+ operation_id: identifier,
51146
+ idempotency_key: identifier,
51147
+ project_id: uuid2,
51148
+ task_list_id: uuid2.optional(),
51149
+ if_binding_version: exports_external.number().int().min(0).optional(),
51150
+ plan: exports_external.object({
51151
+ key,
51152
+ name: exports_external.string().min(1).max(500),
51153
+ description: exports_external.string().max(64000).optional(),
51154
+ status: exports_external.enum(["active", "completed", "archived"]).optional()
51155
+ }).strict(),
51156
+ tasks: exports_external.array(task2).min(1).max(TODOS_TASK_MANIFEST_BOUNDS.tasks),
51157
+ dependencies: exports_external.array(dependency).max(TODOS_TASK_MANIFEST_BOUNDS.dependencies).optional(),
51158
+ effects: exports_external.array(effect).max(TODOS_TASK_MANIFEST_BOUNDS.effects).optional()
51159
+ }).strict();
51160
+ compensationSchema = exports_external.object({
51161
+ receipt_id: uuid2,
51162
+ idempotency_key: identifier,
51163
+ if_binding_version: exports_external.number().int().min(1).max(Number.MAX_SAFE_INTEGER)
51164
+ }).strict();
51165
+ bindingLookupSchema = exports_external.object({
51166
+ authority: exports_external.string().min(1).max(64),
51167
+ route: exports_external.string().min(1).max(128),
51168
+ schema_version: exports_external.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
51169
+ tenant_id: identifier,
51170
+ plan_id: uuid2,
51171
+ max_items: exports_external.number().int().min(1).max(Number.MAX_SAFE_INTEGER)
51172
+ }).strict();
51173
+ });
51174
+
51175
+ // src/task-manifest/backend.ts
51176
+ function validateTaskManifestBindingLookupRows(rows, tenantId, planId) {
51177
+ if (rows.length === 0) {
51178
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_BINDING_NOT_FOUND", `Managed task-manifest binding not found for plan: ${planId}`, { plan_id: planId });
51179
+ }
51180
+ if (rows.length !== 1) {
51181
+ 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 });
51182
+ }
51183
+ const row = rows[0];
51184
+ const bindingVersion = Number(row.binding_version);
51185
+ const state = row.state;
51186
+ 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") {
51187
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_LOOKUP_CONFLICT", "Task-manifest binding and immutable apply receipt disagree", { plan_id: planId });
51188
+ }
51189
+ return {
51190
+ plan_id: planId,
51191
+ apply_receipt_id: row.apply_receipt_id,
51192
+ binding_version: bindingVersion,
51193
+ state
51194
+ };
51195
+ }
51196
+ var UUID_PATTERN2;
51197
+ var init_backend = __esm(() => {
51198
+ init_types5();
51199
+ 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;
51200
+ });
51201
+
51202
+ // src/task-manifest/reference-guard.ts
51203
+ function postgresTaskManifestForeignReferenceSql(tableName) {
51204
+ return `SELECT object_type, object_id
51205
+ FROM ${tableName}
51206
+ WHERE service = $1 AND deleted_at IS NULL
51207
+ AND NOT (object_type = 'plans' AND object_id = $2)
51208
+ AND NOT (object_type = 'tasks' AND object_id IN (
51209
+ SELECT value FROM jsonb_array_elements_text($3::jsonb)
51210
+ ))
51211
+ AND NOT (object_type = 'dependencies' AND object_id IN (
51212
+ SELECT value FROM jsonb_array_elements_text($4::jsonb)
51213
+ ))
51214
+ AND NOT (object_type = 'comments' AND object_id IN (
51215
+ SELECT value FROM jsonb_array_elements_text($5::jsonb)
51216
+ ))
51217
+ AND NOT (object_type = 'verifications' AND object_id IN (
51218
+ SELECT value FROM jsonb_array_elements_text($6::jsonb)
51219
+ ))
51220
+ AND jsonb_path_exists(
51221
+ payload,
51222
+ '$.** ? (@ == $refs[*])',
51223
+ jsonb_build_object('refs', $7::jsonb)
51224
+ )
51225
+ LIMIT 1`;
51226
+ }
51227
+
51228
+ // src/task-manifest/schema-sql.ts
51229
+ function sqlString(value) {
51230
+ return `'${value.replaceAll("'", "''")}'`;
51231
+ }
51232
+ function postgresTodosTaskManifestSchemaSql(tenantId = "default") {
51233
+ const tenantDefault = sqlString(tenantId);
51234
+ return [
51235
+ `CREATE TABLE IF NOT EXISTS todos_task_manifest_receipts (
51236
+ receipt_id text PRIMARY KEY,
51237
+ tenant_id text NOT NULL,
51238
+ authority text NOT NULL CHECK(authority = 'todos'),
51239
+ route text NOT NULL,
51240
+ schema_version integer NOT NULL CHECK(schema_version = 1),
51241
+ kind text NOT NULL CHECK(kind IN ('apply', 'compensate')),
51242
+ operation_id text NOT NULL,
51243
+ idempotency_key text NOT NULL,
51244
+ request_digest text NOT NULL,
51245
+ result_digest text NOT NULL,
51246
+ binding_version integer NOT NULL,
51247
+ apply_receipt_id text,
51248
+ manifest_json jsonb,
51249
+ result_json jsonb NOT NULL,
51250
+ created_at timestamptz NOT NULL,
51251
+ UNIQUE(kind, idempotency_key)
51252
+ )`,
51253
+ `ALTER TABLE todos_task_manifest_receipts
51254
+ ADD COLUMN IF NOT EXISTS tenant_id text NOT NULL DEFAULT ${tenantDefault}`,
51255
+ `ALTER TABLE todos_task_manifest_receipts
51256
+ ALTER COLUMN tenant_id DROP DEFAULT`,
51257
+ `CREATE TABLE IF NOT EXISTS todos_task_manifest_bindings (
51258
+ operation_id text PRIMARY KEY,
51259
+ tenant_id text NOT NULL,
51260
+ idempotency_key text NOT NULL UNIQUE,
51261
+ request_digest text NOT NULL,
51262
+ result_digest text NOT NULL,
51263
+ apply_receipt_id text NOT NULL UNIQUE REFERENCES todos_task_manifest_receipts(receipt_id),
51264
+ manifest_json jsonb NOT NULL,
51265
+ result_json jsonb NOT NULL,
51266
+ state text NOT NULL CHECK(state IN ('applied', 'compensated')),
51267
+ version integer NOT NULL,
51268
+ compensation_receipt_id text,
51269
+ created_at timestamptz NOT NULL,
51270
+ updated_at timestamptz NOT NULL
51271
+ )`,
51272
+ `ALTER TABLE todos_task_manifest_bindings
51273
+ ADD COLUMN IF NOT EXISTS tenant_id text NOT NULL DEFAULT ${tenantDefault}`,
51274
+ `ALTER TABLE todos_task_manifest_bindings
51275
+ ALTER COLUMN tenant_id DROP DEFAULT`,
51276
+ `CREATE TABLE IF NOT EXISTS todos_task_manifest_outbox (
51277
+ id text PRIMARY KEY,
51278
+ apply_receipt_id text NOT NULL REFERENCES todos_task_manifest_receipts(receipt_id),
51279
+ topic text NOT NULL,
51280
+ payload jsonb NOT NULL,
51281
+ payload_digest text NOT NULL,
51282
+ status text NOT NULL CHECK(status IN ('pending', 'delivered', 'cancelled')),
51283
+ attempts integer NOT NULL DEFAULT 0,
51284
+ created_at timestamptz NOT NULL,
51285
+ delivered_at timestamptz
51286
+ )`,
51287
+ `CREATE INDEX IF NOT EXISTS todos_task_manifest_outbox_receipt_idx
51288
+ ON todos_task_manifest_outbox(apply_receipt_id, status)`,
51289
+ `CREATE INDEX IF NOT EXISTS todos_task_manifest_receipts_tenant_idx
51290
+ ON todos_task_manifest_receipts(tenant_id, receipt_id, kind)`,
51291
+ `CREATE INDEX IF NOT EXISTS todos_task_manifest_bindings_tenant_plan_idx
51292
+ ON todos_task_manifest_bindings(
51293
+ tenant_id,
51294
+ ((result_json #>> '{graph,plan_id}'))
51295
+ )`,
51296
+ `CREATE OR REPLACE FUNCTION todos_task_manifest_receipts_immutable()
51297
+ RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN
51298
+ RAISE EXCEPTION 'todos task manifest receipts are immutable';
51299
+ END; $$`,
51300
+ `DROP TRIGGER IF EXISTS todos_task_manifest_receipts_immutable ON todos_task_manifest_receipts`,
51301
+ `CREATE TRIGGER todos_task_manifest_receipts_immutable
51302
+ BEFORE UPDATE OR DELETE ON todos_task_manifest_receipts
51303
+ FOR EACH ROW EXECUTE FUNCTION todos_task_manifest_receipts_immutable()`
51304
+ ];
51305
+ }
51306
+
51307
+ // src/task-manifest/sqlite.ts
51308
+ var sqliteTails;
51309
+ var init_sqlite2 = __esm(() => {
51310
+ init_canonical();
51311
+ init_backend();
51312
+ init_types5();
51313
+ sqliteTails = new WeakMap;
51314
+ });
51315
+
51316
+ // src/task-manifest/postgres.ts
51317
+ function safeIdentifier2(value, field) {
51318
+ if (!/^[a-z_][a-z0-9_]*$/.test(value)) {
51319
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", `${field} must be a safe PostgreSQL identifier`);
51320
+ }
51321
+ return value;
51322
+ }
51323
+ function parseJson2(value) {
51324
+ return typeof value === "string" ? JSON.parse(value) : value;
51325
+ }
51326
+ function timestamp4(value) {
51327
+ return value instanceof Date ? value.toISOString() : new Date(String(value)).toISOString();
51328
+ }
51329
+ function fault(faults, point) {
51330
+ if (faults.points.has(point))
51331
+ throw new Error(`Injected task-manifest fault at ${point}`);
51332
+ }
51333
+ function receiptFromRow3(row) {
51334
+ return {
51335
+ receipt_id: String(row["receipt_id"]),
51336
+ authority: "todos",
51337
+ route: "todos.task-manifest.v1",
51338
+ schema_version: 1,
51339
+ kind: row["kind"],
51340
+ operation_id: String(row["operation_id"]),
51341
+ idempotency_key: String(row["idempotency_key"]),
51342
+ request_digest: String(row["request_digest"]),
51343
+ result_digest: String(row["result_digest"]),
51344
+ binding_version: Number(row["binding_version"]),
51345
+ apply_receipt_id: row["apply_receipt_id"] == null ? null : String(row["apply_receipt_id"]),
51346
+ created_at: timestamp4(row["created_at"])
51347
+ };
51348
+ }
51349
+ function taskPayload2(manifest, task3, taskId, planId, now4) {
51350
+ return {
51351
+ id: taskId,
51352
+ short_id: null,
51353
+ project_id: manifest.project_id,
51354
+ parent_id: null,
51355
+ plan_id: planId,
51356
+ task_list_id: manifest.task_list_id ?? null,
51357
+ title: task3.title,
51358
+ description: task3.description ?? null,
51359
+ status: task3.status ?? "pending",
51360
+ priority: task3.priority ?? "medium",
51361
+ agent_id: null,
51362
+ assigned_to: task3.assigned_to ?? null,
51363
+ session_id: null,
51364
+ working_dir: null,
51365
+ tags: task3.tags ?? [],
51366
+ metadata: task3.metadata ?? {},
51367
+ version: 1,
51368
+ locked_by: null,
51369
+ locked_at: null,
51370
+ created_at: now4,
51371
+ updated_at: now4,
51372
+ started_at: null,
51373
+ completed_at: null,
51374
+ due_at: null,
51375
+ estimated_minutes: null,
51376
+ actual_minutes: null,
51377
+ requires_approval: false,
51378
+ approved_by: null,
51379
+ approved_at: null,
51380
+ recurrence_rule: null,
51381
+ recurrence_parent_id: null,
51382
+ spawns_template_id: null,
51383
+ confidence: null,
51384
+ reason: null,
51385
+ spawned_from_session: null,
51386
+ assigned_by: null,
51387
+ created_by: task3.created_by ?? null,
51388
+ assigned_from_project: null,
51389
+ task_type: null,
51390
+ cost_tokens: 0,
51391
+ cost_usd: 0,
51392
+ delegated_from: null,
51393
+ delegation_depth: 0,
51394
+ retry_count: 0,
51395
+ max_retries: 0,
51396
+ retry_after: null,
51397
+ sla_minutes: null,
51398
+ runner_id: null,
51399
+ runner_started_at: null,
51400
+ runner_completed_at: null,
51401
+ current_step: null,
51402
+ total_steps: null,
51403
+ machine_id: null,
51404
+ synced_at: null,
51405
+ archived_at: null
51406
+ };
51407
+ }
51408
+ function planPayload(input) {
51409
+ return {
51410
+ id: input.graph.plan_id,
51411
+ slug: null,
51412
+ project_id: input.manifest.project_id,
51413
+ task_list_id: input.manifest.task_list_id ?? null,
51414
+ agent_id: null,
51415
+ name: input.manifest.plan.name,
51416
+ description: input.manifest.plan.description ?? null,
51417
+ status: input.manifest.plan.status ?? "active",
51418
+ created_at: input.now,
51419
+ updated_at: input.now,
51420
+ machine_id: null,
51421
+ synced_at: null
51422
+ };
51423
+ }
51424
+ function placeholders2(start, count2) {
51425
+ return Array.from({ length: count2 }, (_, index) => `$${start + index}`).join(",");
51426
+ }
51427
+
51428
+ class PostgresTodosTaskManifestBackend {
51429
+ client;
51430
+ kind = "postgresql";
51431
+ service;
51432
+ tableName;
51433
+ tenantId;
51434
+ schemaReady = null;
51435
+ constructor(client, options = {}) {
51436
+ this.client = client;
51437
+ this.service = options.service ?? "todos";
51438
+ this.tableName = safeIdentifier2(options.tableName ?? DEFAULT_TODOS_POSTGRES_SYNC_TABLE, "tableName");
51439
+ this.tenantId = options.tenantId ?? "default";
51440
+ }
51441
+ async ensureSchema() {
51442
+ this.schemaReady ??= (async () => {
51443
+ for (const sql of postgresTodosSyncSchemaSql(this.tableName))
51444
+ await this.client.query(sql);
51445
+ for (const sql of postgresTodosTaskManifestSchemaSql(this.tenantId))
51446
+ await this.client.query(sql);
51447
+ })();
51448
+ await this.schemaReady;
51449
+ }
51450
+ async insertSync(tx, objectType2, objectId, payload, now4) {
51451
+ await tx.query(`INSERT INTO ${this.tableName} (
51452
+ service, object_type, object_id, payload, updated_at, deleted_at, source_machine_id, version
51453
+ ) VALUES ($1, $2, $3, $4::jsonb, $5, NULL, NULL, 1)`, [
51454
+ this.service,
51455
+ objectType2,
51456
+ objectId,
51457
+ canonicalJson(payload),
51458
+ now4
51459
+ ]);
51460
+ }
51461
+ async apply(input, faults) {
51462
+ await this.ensureSchema();
51463
+ return this.client.transaction(async (tx) => {
51464
+ const { manifest } = input;
51465
+ await tx.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${this.service}\x1F${manifest.operation_id}`]);
51466
+ await tx.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${this.service}\x1Fidempotency\x1F${manifest.idempotency_key}`]);
51467
+ 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]);
51468
+ if (existing.rows[0]) {
51469
+ const binding = existing.rows[0];
51470
+ if (binding["idempotency_key"] !== manifest.idempotency_key || binding["request_digest"] !== input.request_digest) {
51471
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_IDEMPOTENCY_CONFLICT", "Operation is already bound to a different request");
51472
+ }
51473
+ if (binding["state"] !== "applied") {
51474
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_GRAPH_CONFLICT", "Operation was already compensated");
51475
+ }
51476
+ return { ...parseJson2(binding["result_json"]), duplicate: true };
51477
+ }
51478
+ 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]);
51479
+ if (reused.rows[0])
51480
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_IDEMPOTENCY_CONFLICT", "Idempotency key is already used");
51481
+ if (manifest.if_binding_version !== undefined && manifest.if_binding_version !== 0) {
51482
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_CAS_CONFLICT", "New manifest binding version must be 0");
51483
+ }
51484
+ const project = await tx.query(`SELECT 1 AS found FROM ${this.tableName}
51485
+ WHERE service = $1 AND object_type = 'projects' AND object_id = $2 AND deleted_at IS NULL LIMIT 1`, [this.service, manifest.project_id]);
51486
+ if (!project.rows[0])
51487
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_FOREIGN_REFERENCE", "Project does not exist");
51488
+ if (manifest.task_list_id) {
51489
+ const taskList = await tx.query(`SELECT payload FROM ${this.tableName}
51490
+ 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]);
51491
+ const payload = taskList.rows[0] ? parseJson2(taskList.rows[0]["payload"]) : null;
51492
+ if (!payload || payload["project_id"] !== manifest.project_id) {
51493
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_FOREIGN_REFERENCE", "Task list does not belong to the project");
51494
+ }
51495
+ }
51496
+ const objectIds = [input.graph.plan_id, ...Object.values(input.graph.task_ids), ...input.graph.comment_ids, ...input.graph.verification_ids, ...input.graph.dependency_ids];
51497
+ const conflict = await tx.query(`SELECT object_id FROM ${this.tableName}
51498
+ WHERE service = $1 AND object_id IN (${placeholders2(2, objectIds.length)}) LIMIT 1`, [this.service, ...objectIds]);
51499
+ if (conflict.rows[0])
51500
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_GRAPH_CONFLICT", "A deterministic graph id already exists");
51501
+ await this.insertSync(tx, "plans", input.graph.plan_id, planPayload(input), input.now);
51502
+ fault(faults, "after_plan_write");
51503
+ for (const task3 of manifest.tasks) {
51504
+ 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);
51505
+ }
51506
+ fault(faults, "after_task_write");
51507
+ for (const [index, edge] of (manifest.dependencies ?? []).entries()) {
51508
+ await this.insertSync(tx, "dependencies", input.graph.dependency_ids[index], {
51509
+ id: input.graph.dependency_ids[index],
51510
+ task_id: input.graph.task_ids[edge.task],
51511
+ depends_on: input.graph.task_ids[edge.depends_on],
51512
+ created_at: input.now,
51513
+ updated_at: input.now
51514
+ }, input.now);
51515
+ }
51516
+ fault(faults, "after_dependency_write");
51517
+ let commentIndex = 0;
51518
+ for (const task3 of manifest.tasks)
51519
+ for (const comment2 of task3.comments ?? []) {
51520
+ const id = input.graph.comment_ids[commentIndex++];
51521
+ await this.insertSync(tx, "comments", id, {
51522
+ id,
51523
+ task_id: input.graph.task_ids[task3.key],
51524
+ agent_id: comment2.agent_id ?? null,
51525
+ session_id: comment2.session_id ?? null,
51526
+ content: comment2.content,
51527
+ type: comment2.type ?? "comment",
51528
+ progress_pct: comment2.progress_pct ?? null,
51529
+ created_at: input.now
51530
+ }, input.now);
51531
+ }
51532
+ fault(faults, "after_comment_write");
51533
+ let verificationIndex = 0;
51534
+ for (const task3 of manifest.tasks)
51535
+ for (const verification2 of task3.verifications ?? []) {
51536
+ const id = input.graph.verification_ids[verificationIndex++];
51537
+ await this.insertSync(tx, "verifications", id, {
51538
+ id,
51539
+ task_id: input.graph.task_ids[task3.key],
51540
+ command: verification2.command,
51541
+ status: verification2.status ?? "unknown",
51542
+ output_summary: verification2.output_summary ?? null,
51543
+ artifact_path: verification2.artifact_path ?? null,
51544
+ agent_id: verification2.agent_id ?? null,
51545
+ run_at: input.now,
51546
+ created_at: input.now,
51547
+ updated_at: input.now
51548
+ }, input.now);
51549
+ }
51550
+ fault(faults, "after_verification_write");
51551
+ const readback = await this.readback(tx, input.graph);
51552
+ const expected = {
51553
+ plans: 1,
51554
+ tasks: manifest.tasks.length,
51555
+ dependencies: manifest.dependencies?.length ?? 0,
51556
+ comments: input.graph.comment_ids.length,
51557
+ verifications: input.graph.verification_ids.length,
51558
+ complete: true
51559
+ };
51560
+ if (canonicalJson(readback) !== canonicalJson(expected)) {
51561
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_READBACK_MISMATCH", "Exact PostgreSQL graph readback did not match", { expected, readback });
51562
+ }
51563
+ const receipt = {
51564
+ receipt_id: input.receipt_id,
51565
+ authority: "todos",
51566
+ route: "todos.task-manifest.v1",
51567
+ schema_version: 1,
51568
+ kind: "apply",
51569
+ operation_id: manifest.operation_id,
51570
+ idempotency_key: manifest.idempotency_key,
51571
+ request_digest: input.request_digest,
51572
+ result_digest: input.result_digest,
51573
+ binding_version: 1,
51574
+ apply_receipt_id: null,
51575
+ created_at: input.now
51576
+ };
51577
+ const result = {
51578
+ duplicate: false,
51579
+ receipt,
51580
+ graph: input.graph,
51581
+ readback,
51582
+ outbox_ids: input.outbox.map((entry2) => entry2.id),
51583
+ result_digest: input.result_digest
51584
+ };
51585
+ const manifestJson = canonicalJson(manifest);
51586
+ const resultJson = canonicalJson(result);
51587
+ await tx.query(`INSERT INTO todos_task_manifest_receipts (
51588
+ receipt_id, tenant_id, authority, route, schema_version, kind, operation_id, idempotency_key,
51589
+ request_digest, result_digest, binding_version, apply_receipt_id, manifest_json, result_json, created_at
51590
+ ) VALUES ($1, $2, 'todos', 'todos.task-manifest.v1', 1, 'apply', $3, $4, $5, $6, 1, NULL, $7::jsonb, $8::jsonb, $9)`, [
51591
+ input.receipt_id,
51592
+ this.tenantId,
51593
+ manifest.operation_id,
51594
+ manifest.idempotency_key,
51595
+ input.request_digest,
51596
+ input.result_digest,
51597
+ manifestJson,
51598
+ resultJson,
51599
+ input.now
51600
+ ]);
51601
+ for (const entry2 of input.outbox) {
51602
+ await tx.query(`INSERT INTO todos_task_manifest_outbox (
51603
+ id, apply_receipt_id, topic, payload, payload_digest, status, created_at
51604
+ ) VALUES ($1, $2, $3, $4::jsonb, $5, 'pending', $6)`, [
51605
+ entry2.id,
51606
+ input.receipt_id,
51607
+ entry2.topic,
51608
+ canonicalJson(entry2.payload),
51609
+ entry2.digest,
51610
+ input.now
51611
+ ]);
51612
+ }
51613
+ fault(faults, "after_outbox_write");
51614
+ await tx.query(`INSERT INTO todos_task_manifest_bindings (
51615
+ operation_id, tenant_id, idempotency_key, request_digest, result_digest, apply_receipt_id,
51616
+ manifest_json, result_json, state, version, created_at, updated_at
51617
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8::jsonb, 'applied', 1, $9, $9)`, [
51618
+ manifest.operation_id,
51619
+ this.tenantId,
51620
+ manifest.idempotency_key,
51621
+ input.request_digest,
51622
+ input.result_digest,
51623
+ input.receipt_id,
51624
+ manifestJson,
51625
+ resultJson,
51626
+ input.now
51627
+ ]);
51628
+ fault(faults, "after_receipt_write");
51629
+ return result;
51630
+ });
51631
+ }
51632
+ async readExact(receiptId2) {
51633
+ await this.ensureSchema();
51634
+ 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]);
51635
+ if (!result.rows[0])
51636
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_RECEIPT_NOT_FOUND", `Apply receipt not found: ${receiptId2}`);
51637
+ return { ...parseJson2(result.rows[0]["result_json"]), duplicate: false };
51638
+ }
51639
+ async lookupBindingByPlanId(planId) {
51640
+ await this.ensureSchema();
51641
+ const result = await this.client.query(`
51642
+ SELECT
51643
+ b.apply_receipt_id AS apply_receipt_id,
51644
+ b.state AS state,
51645
+ b.version AS binding_version,
51646
+ b.tenant_id AS binding_tenant_id,
51647
+ b.operation_id AS binding_operation_id,
51648
+ b.result_json #>> '{graph,plan_id}' AS binding_plan_id,
51649
+ r.tenant_id AS receipt_tenant_id,
51650
+ r.authority AS receipt_authority,
51651
+ r.route AS receipt_route,
51652
+ r.schema_version AS receipt_schema_version,
51653
+ r.kind AS receipt_kind,
51654
+ r.operation_id AS receipt_operation_id,
51655
+ r.result_json #>> '{graph,plan_id}' AS receipt_plan_id
51656
+ FROM todos_task_manifest_bindings b
51657
+ LEFT JOIN todos_task_manifest_receipts r
51658
+ ON r.receipt_id = b.apply_receipt_id
51659
+ AND r.tenant_id = b.tenant_id
51660
+ WHERE b.tenant_id = $1
51661
+ AND b.result_json #>> '{graph,plan_id}' = $2
51662
+ LIMIT 2
51663
+ `, [this.tenantId, planId]);
51664
+ return validateTaskManifestBindingLookupRows(result.rows, this.tenantId, planId);
51665
+ }
51666
+ async markOutboxDelivered(outboxId, deliveredAt) {
51667
+ await this.ensureSchema();
51668
+ await this.client.transaction(async (tx) => {
51669
+ const owned = await tx.query(`SELECT r.operation_id
51670
+ FROM todos_task_manifest_outbox o
51671
+ JOIN todos_task_manifest_receipts r
51672
+ ON r.receipt_id = o.apply_receipt_id
51673
+ WHERE r.tenant_id = $1
51674
+ AND r.authority = 'todos'
51675
+ AND r.route = 'todos.task-manifest.v1'
51676
+ AND r.schema_version = 1
51677
+ AND r.kind = 'apply'
51678
+ AND o.id = $2
51679
+ LIMIT 1`, [this.tenantId, outboxId]);
51680
+ const operationId = owned.rows[0]?.operation_id;
51681
+ if (operationId == null) {
51682
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_GRAPH_CONFLICT", `Pending outbox row not found: ${outboxId}`);
51683
+ }
51684
+ await tx.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${this.service}\x1F${String(operationId)}`]);
51685
+ const result = await tx.query(`UPDATE todos_task_manifest_outbox
51686
+ SET status = 'delivered', delivered_at = $1, attempts = attempts + 1
51687
+ WHERE id = $2 AND status = 'pending'
51688
+ AND EXISTS (
51689
+ SELECT 1 FROM todos_task_manifest_receipts r
51690
+ WHERE r.receipt_id = todos_task_manifest_outbox.apply_receipt_id
51691
+ AND r.tenant_id = $3
51692
+ AND r.authority = 'todos'
51693
+ AND r.route = 'todos.task-manifest.v1'
51694
+ AND r.schema_version = 1
51695
+ AND r.kind = 'apply'
51696
+ )
51697
+ RETURNING id`, [deliveredAt, outboxId, this.tenantId]);
51698
+ if (result.rows[0])
51699
+ return;
51700
+ const existing = await tx.query(`SELECT o.status
51701
+ FROM todos_task_manifest_outbox o
51702
+ JOIN todos_task_manifest_receipts r
51703
+ ON r.receipt_id = o.apply_receipt_id
51704
+ WHERE r.tenant_id = $1
51705
+ AND r.authority = 'todos'
51706
+ AND r.route = 'todos.task-manifest.v1'
51707
+ AND r.schema_version = 1
51708
+ AND r.kind = 'apply'
51709
+ AND o.id = $2
51710
+ LIMIT 1`, [this.tenantId, outboxId]);
51711
+ if (existing.rows[0]?.status === "delivered")
51712
+ return;
51713
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_GRAPH_CONFLICT", `Pending outbox row not found: ${outboxId}`);
51714
+ });
51715
+ }
51716
+ async compensate(input, receipt, compensationReceiptId, requestDigest, now4) {
51717
+ await this.ensureSchema();
51718
+ return this.client.transaction(async (tx) => {
51719
+ await tx.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${this.service}\x1F${receipt.operation_id}`]);
51720
+ await tx.query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))", [`${this.service}\x1Fcompensation-idempotency\x1F${input.idempotency_key}`]);
51721
+ const previous = await tx.query(`SELECT apply_receipt_id, request_digest, result_json
51722
+ FROM todos_task_manifest_receipts
51723
+ WHERE tenant_id = $1 AND kind = 'compensate' AND idempotency_key = $2
51724
+ LIMIT 1`, [this.tenantId, input.idempotency_key]);
51725
+ if (previous.rows[0]) {
51726
+ const row = previous.rows[0];
51727
+ if (row["apply_receipt_id"] !== input.receipt_id || row["request_digest"] !== requestDigest) {
51728
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_IDEMPOTENCY_CONFLICT", "Compensation idempotency key is already used");
51729
+ }
51730
+ return { ...parseJson2(row["result_json"]), duplicate: true };
51731
+ }
51732
+ 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]);
51733
+ const applyRow = applyRows.rows[0];
51734
+ if (!applyRow)
51735
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_RECEIPT_NOT_FOUND", "Apply receipt not found");
51736
+ 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]);
51737
+ const binding = bindingRows.rows[0];
51738
+ if (!binding || Number(binding["version"]) !== input.if_binding_version) {
51739
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_CAS_CONFLICT", "Binding version changed before compensation");
51740
+ }
51741
+ if (binding["state"] !== "applied")
51742
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Graph is not applied");
51743
+ const delivered = await tx.query(`SELECT o.id FROM todos_task_manifest_outbox o
51744
+ JOIN todos_task_manifest_receipts r ON r.receipt_id = o.apply_receipt_id
51745
+ WHERE r.tenant_id = $1
51746
+ AND r.authority = 'todos'
51747
+ AND r.route = 'todos.task-manifest.v1'
51748
+ AND r.schema_version = 1
51749
+ AND r.kind = 'apply'
51750
+ AND o.apply_receipt_id = $2
51751
+ AND o.status = 'delivered'
51752
+ LIMIT 1`, [this.tenantId, input.receipt_id]);
51753
+ if (delivered.rows[0])
51754
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: delivered outbox row exists");
51755
+ const applyResult = parseJson2(applyRow["result_json"]);
51756
+ const manifest = parseJson2(applyRow["manifest_json"]);
51757
+ const expectedEffects = [
51758
+ {
51759
+ topic: "todos.task-manifest.applied",
51760
+ payload: { operation_id: manifest.operation_id, project_id: manifest.project_id }
51761
+ },
51762
+ ...(manifest.effects ?? []).map((effect2) => ({ topic: effect2.topic, payload: effect2.payload }))
51763
+ ];
51764
+ const outboxRows = await tx.query(`SELECT o.id, o.topic, o.payload, o.payload_digest, o.status, o.attempts, o.delivered_at
51765
+ FROM todos_task_manifest_outbox o
51766
+ WHERE o.apply_receipt_id = $1
51767
+ AND EXISTS (
51768
+ SELECT 1 FROM todos_task_manifest_receipts r
51769
+ WHERE r.receipt_id = o.apply_receipt_id
51770
+ AND r.tenant_id = $2
51771
+ AND r.authority = 'todos'
51772
+ AND r.route = 'todos.task-manifest.v1'
51773
+ AND r.schema_version = 1
51774
+ AND r.kind = 'apply'
51775
+ )
51776
+ ORDER BY o.id
51777
+ FOR UPDATE OF o`, [input.receipt_id, this.tenantId]);
51778
+ if (outboxRows.rows.length !== expectedEffects.length) {
51779
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: outbox changed since apply");
51780
+ }
51781
+ const outboxById = new Map(outboxRows.rows.map((entry2) => [String(entry2["id"]), entry2]));
51782
+ for (const [index, expectedEffect] of expectedEffects.entries()) {
51783
+ const stored2 = outboxById.get(applyResult.outbox_ids[index]);
51784
+ 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) {
51785
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: outbox changed since apply");
51786
+ }
51787
+ }
51788
+ const taskIds = Object.values(applyResult.graph.task_ids);
51789
+ const foreignReference = await tx.query(postgresTaskManifestForeignReferenceSql(this.tableName), [
51790
+ this.service,
51791
+ applyResult.graph.plan_id,
51792
+ taskIds,
51793
+ applyResult.graph.dependency_ids,
51794
+ applyResult.graph.comment_ids,
51795
+ applyResult.graph.verification_ids,
51796
+ [applyResult.graph.plan_id, ...taskIds]
51797
+ ]);
51798
+ if (foreignReference.rows[0]) {
51799
+ const row = foreignReference.rows[0];
51800
+ 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);
51801
+ }
51802
+ const actualReadback = await this.readback(tx, applyResult.graph);
51803
+ if (canonicalJson(actualReadback) !== canonicalJson(applyResult.readback)) {
51804
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: graph changed since apply");
51805
+ }
51806
+ const appliedAt = receiptFromRow3(applyRow).created_at;
51807
+ const expectedPayloads = new Map;
51808
+ expectedPayloads.set(applyResult.graph.plan_id, {
51809
+ type: "plans",
51810
+ payload: canonicalJson(planPayload({ manifest, graph: applyResult.graph, now: appliedAt }))
51811
+ });
51812
+ for (const task3 of manifest.tasks)
51813
+ expectedPayloads.set(applyResult.graph.task_ids[task3.key], {
51814
+ type: "tasks",
51815
+ payload: canonicalJson(taskPayload2(manifest, task3, applyResult.graph.task_ids[task3.key], applyResult.graph.plan_id, appliedAt))
51816
+ });
51817
+ for (const [index, edge] of (manifest.dependencies ?? []).entries())
51818
+ expectedPayloads.set(applyResult.graph.dependency_ids[index], {
51819
+ type: "dependencies",
51820
+ payload: canonicalJson({
51821
+ id: applyResult.graph.dependency_ids[index],
51822
+ task_id: applyResult.graph.task_ids[edge.task],
51823
+ depends_on: applyResult.graph.task_ids[edge.depends_on],
51824
+ created_at: appliedAt,
51825
+ updated_at: appliedAt
51826
+ })
51827
+ });
51828
+ let expectedCommentIndex = 0;
51829
+ for (const task3 of manifest.tasks)
51830
+ for (const comment2 of task3.comments ?? []) {
51831
+ const id = applyResult.graph.comment_ids[expectedCommentIndex++];
51832
+ expectedPayloads.set(id, {
51833
+ type: "comments",
51834
+ payload: canonicalJson({
51835
+ id,
51836
+ task_id: applyResult.graph.task_ids[task3.key],
51837
+ agent_id: comment2.agent_id ?? null,
51838
+ session_id: comment2.session_id ?? null,
51839
+ content: comment2.content,
51840
+ type: comment2.type ?? "comment",
51841
+ progress_pct: comment2.progress_pct ?? null,
51842
+ created_at: appliedAt
51843
+ })
51844
+ });
51845
+ }
51846
+ let expectedVerificationIndex = 0;
51847
+ for (const task3 of manifest.tasks)
51848
+ for (const verification2 of task3.verifications ?? []) {
51849
+ const id = applyResult.graph.verification_ids[expectedVerificationIndex++];
51850
+ expectedPayloads.set(id, {
51851
+ type: "verifications",
51852
+ payload: canonicalJson({
51853
+ id,
51854
+ task_id: applyResult.graph.task_ids[task3.key],
51855
+ command: verification2.command,
51856
+ status: verification2.status ?? "unknown",
51857
+ output_summary: verification2.output_summary ?? null,
51858
+ artifact_path: verification2.artifact_path ?? null,
51859
+ agent_id: verification2.agent_id ?? null,
51860
+ run_at: appliedAt,
51861
+ created_at: appliedAt,
51862
+ updated_at: appliedAt
51863
+ })
51864
+ });
51865
+ }
51866
+ const managedIds = [...expectedPayloads.keys()];
51867
+ const stored = await tx.query(`SELECT object_type, object_id, payload FROM ${this.tableName}
51868
+ WHERE service = $1 AND object_id IN (${placeholders2(2, managedIds.length)})`, [this.service, ...managedIds]);
51869
+ for (const row of stored.rows) {
51870
+ const expected = expectedPayloads.get(String(row["object_id"]));
51871
+ if (!expected || expected.type !== row["object_type"] || expected.payload !== canonicalJson(parseJson2(row["payload"]))) {
51872
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: managed graph values changed");
51873
+ }
51874
+ }
51875
+ if (stored.rows.length !== managedIds.length)
51876
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: managed graph is incomplete");
51877
+ const cancelled = await tx.query(`UPDATE todos_task_manifest_outbox
51878
+ SET status = 'cancelled'
51879
+ WHERE apply_receipt_id = $1 AND status = 'pending'
51880
+ AND EXISTS (
51881
+ SELECT 1 FROM todos_task_manifest_receipts r
51882
+ WHERE r.receipt_id = todos_task_manifest_outbox.apply_receipt_id
51883
+ AND r.tenant_id = $2
51884
+ AND r.authority = 'todos'
51885
+ AND r.route = 'todos.task-manifest.v1'
51886
+ AND r.schema_version = 1
51887
+ AND r.kind = 'apply'
51888
+ )
51889
+ RETURNING id`, [input.receipt_id, this.tenantId]);
51890
+ const cancelledIds = new Set(cancelled.rows.map((row) => String(row.id)));
51891
+ if (cancelledIds.size !== applyResult.outbox_ids.length || applyResult.outbox_ids.some((id) => !cancelledIds.has(id))) {
51892
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_COMPENSATION_REFUSED", "Compensation refused: failed to cancel every expected outbox row");
51893
+ }
51894
+ const typedIds = [
51895
+ ["dependencies", applyResult.graph.dependency_ids],
51896
+ ["comments", applyResult.graph.comment_ids],
51897
+ ["verifications", applyResult.graph.verification_ids],
51898
+ ["tasks", taskIds],
51899
+ ["plans", [applyResult.graph.plan_id]]
51900
+ ];
51901
+ for (const [objectType2, ids2] of typedIds) {
51902
+ if (!ids2.length)
51903
+ continue;
51904
+ await tx.query(`DELETE FROM ${this.tableName} WHERE service = $1 AND object_type = $2
51905
+ AND object_id IN (${placeholders2(3, ids2.length)})`, [this.service, objectType2, ...ids2]);
51906
+ }
51907
+ const readback = await this.readback(tx, applyResult.graph);
51908
+ const result = { duplicate: false, receipt, absent: true, readback };
51909
+ await tx.query(`INSERT INTO todos_task_manifest_receipts (
51910
+ receipt_id, tenant_id, authority, route, schema_version, kind, operation_id, idempotency_key,
51911
+ request_digest, result_digest, binding_version, apply_receipt_id, manifest_json, result_json, created_at
51912
+ ) VALUES ($1, $2, 'todos', 'todos.task-manifest.v1', 1, 'compensate', $3, $4, $5, $6, $7, $8, NULL, $9::jsonb, $10)`, [
51913
+ compensationReceiptId,
51914
+ this.tenantId,
51915
+ receipt.operation_id,
51916
+ input.idempotency_key,
51917
+ requestDigest,
51918
+ receipt.result_digest,
51919
+ receipt.binding_version,
51920
+ input.receipt_id,
51921
+ canonicalJson(result),
51922
+ now4
51923
+ ]);
51924
+ const updated = await tx.query(`UPDATE todos_task_manifest_bindings
51925
+ SET state = 'compensated', version = $1, compensation_receipt_id = $2, updated_at = $3
51926
+ WHERE tenant_id = $4 AND operation_id = $5 AND state = 'applied' AND version = $6 RETURNING operation_id`, [
51927
+ receipt.binding_version,
51928
+ compensationReceiptId,
51929
+ now4,
51930
+ this.tenantId,
51931
+ receipt.operation_id,
51932
+ input.if_binding_version
51933
+ ]);
51934
+ if (!updated.rows[0])
51935
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_CAS_CONFLICT", "Binding changed during compensation");
51936
+ return result;
51937
+ });
51938
+ }
51939
+ async readback(tx, graph) {
51940
+ const count2 = async (objectType2, ids2) => {
51941
+ if (!ids2.length)
51942
+ return 0;
51943
+ const result = await tx.query(`SELECT count(*) AS count FROM ${this.tableName}
51944
+ WHERE service = $1 AND object_type = $2 AND deleted_at IS NULL
51945
+ AND object_id IN (${placeholders2(3, ids2.length)})`, [this.service, objectType2, ...ids2]);
51946
+ return Number(result.rows[0]?.count ?? 0);
51947
+ };
51948
+ return {
51949
+ plans: await count2("plans", [graph.plan_id]),
51950
+ tasks: await count2("tasks", Object.values(graph.task_ids)),
51951
+ dependencies: await count2("dependencies", graph.dependency_ids),
51952
+ comments: await count2("comments", graph.comment_ids),
51953
+ verifications: await count2("verifications", graph.verification_ids),
51954
+ complete: true
51955
+ };
51956
+ }
51957
+ }
51958
+ var init_postgres3 = __esm(() => {
51959
+ init_canonical();
51960
+ init_backend();
51961
+ init_postgres_sync();
51962
+ init_types5();
51963
+ });
51964
+
51965
+ // src/task-manifest/authority.ts
51966
+ function resolveTenantId(value) {
51967
+ const tenantId = value ?? "default";
51968
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/.test(tenantId)) {
51969
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "tenantId must be a bounded exact authority identifier");
51970
+ }
51971
+ return tenantId;
51972
+ }
51973
+ function normalize(input, now4) {
51974
+ const parsed = parseTodosTaskManifest(input);
51975
+ const requestBytes = Buffer.byteLength(canonicalJson(parsed), "utf8");
51976
+ if (requestBytes > TODOS_TASK_MANIFEST_BOUNDS.request_bytes) {
51977
+ 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 });
51978
+ }
51979
+ const manifest = sanitizeManifest(parsed);
51980
+ const task_ids = Object.fromEntries(manifest.tasks.map((task3) => [
51981
+ task3.key,
51982
+ deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, manifest.operation_id, "task", task3.key)
51983
+ ]));
51984
+ const graph = {
51985
+ plan_id: deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, manifest.operation_id, "plan", manifest.plan.key),
51986
+ task_ids,
51987
+ comment_ids: manifest.tasks.flatMap((task3) => (task3.comments ?? []).map((_, index) => deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, manifest.operation_id, "comment", task3.key, String(index)))),
51988
+ verification_ids: manifest.tasks.flatMap((task3) => (task3.verifications ?? []).map((_, index) => deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, manifest.operation_id, "verification", task3.key, String(index)))),
51989
+ dependency_ids: (manifest.dependencies ?? []).map((edge) => `${task_ids[edge.task]}::${task_ids[edge.depends_on]}`)
51990
+ };
51991
+ const request_digest = canonicalDigest(parsed);
51992
+ const effectInputs = [
51993
+ {
51994
+ topic: "todos.task-manifest.applied",
51995
+ payload: { operation_id: manifest.operation_id, project_id: manifest.project_id }
51996
+ },
51997
+ ...manifest.effects ?? []
51998
+ ];
51999
+ const outbox = effectInputs.map((effect2, index) => {
52000
+ const payload = { ...effect2.payload };
52001
+ return {
52002
+ id: deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, manifest.operation_id, "outbox", String(index)),
52003
+ topic: effect2.topic,
52004
+ payload,
52005
+ digest: canonicalDigest({ topic: effect2.topic, payload })
52006
+ };
52007
+ });
52008
+ const result_digest = canonicalDigest({ manifest, graph, outbox });
52009
+ return {
52010
+ manifest,
52011
+ request_digest,
52012
+ result_digest,
52013
+ receipt_id: deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, "apply", manifest.operation_id, manifest.idempotency_key, request_digest),
52014
+ graph,
52015
+ outbox,
52016
+ now: now4
52017
+ };
52018
+ }
52019
+ function sanitizeManifest(manifest) {
52020
+ return {
52021
+ ...manifest,
52022
+ plan: {
52023
+ ...manifest.plan,
52024
+ name: sanitizePreWriteText(manifest.plan.name, "task_manifest.plan.name"),
52025
+ ...manifest.plan.description !== undefined ? { description: sanitizePreWriteText(manifest.plan.description, "task_manifest.plan.description") } : {}
52026
+ },
52027
+ tasks: manifest.tasks.map((task3) => ({
52028
+ ...task3,
52029
+ title: sanitizePreWriteText(task3.title, `task_manifest.tasks.${task3.key}.title`),
52030
+ ...task3.description !== undefined ? { description: sanitizePreWriteText(task3.description, `task_manifest.tasks.${task3.key}.description`) } : {},
52031
+ ...task3.tags !== undefined ? { tags: sanitizePreWriteValue(task3.tags, `task_manifest.tasks.${task3.key}.tags`) } : {},
52032
+ ...task3.metadata !== undefined ? { metadata: sanitizePreWriteValue(task3.metadata, `task_manifest.tasks.${task3.key}.metadata`) } : {},
52033
+ ...task3.comments !== undefined ? {
52034
+ comments: task3.comments.map((comment2, index) => ({
52035
+ ...comment2,
52036
+ content: sanitizePreWriteText(comment2.content, `task_manifest.tasks.${task3.key}.comments.${index}.content`)
52037
+ }))
52038
+ } : {},
52039
+ ...task3.verifications !== undefined ? {
52040
+ verifications: task3.verifications.map((verification2, index) => ({
52041
+ ...verification2,
52042
+ command: sanitizePreWriteText(verification2.command, `task_manifest.tasks.${task3.key}.verifications.${index}.command`),
52043
+ ...verification2.output_summary !== undefined ? { output_summary: sanitizePreWriteText(verification2.output_summary, `task_manifest.tasks.${task3.key}.verifications.${index}.output_summary`) } : {},
52044
+ ...verification2.artifact_path !== undefined ? { artifact_path: sanitizePreWriteText(verification2.artifact_path, `task_manifest.tasks.${task3.key}.verifications.${index}.artifact_path`) } : {}
52045
+ }))
52046
+ } : {}
52047
+ })),
52048
+ ...manifest.effects !== undefined ? {
52049
+ effects: manifest.effects.map((effect2, index) => ({
52050
+ topic: sanitizePreWriteText(effect2.topic, `task_manifest.effects.${index}.topic`),
52051
+ payload: sanitizePreWriteValue(effect2.payload, `task_manifest.effects.${index}.payload`)
52052
+ }))
52053
+ } : {}
52054
+ };
52055
+ }
52056
+
52057
+ class PackageOwnedTodosTaskManifestAuthority {
52058
+ backend;
52059
+ options;
52060
+ tenantId;
52061
+ constructor(backend, options = {}) {
52062
+ this.backend = backend;
52063
+ this.options = options;
52064
+ this.tenantId = resolveTenantId(options.tenantId);
52065
+ }
52066
+ async capability() {
52067
+ return {
52068
+ authority: "todos",
52069
+ route: TODOS_TASK_MANIFEST_ROUTE,
52070
+ schema_version: TODOS_TASK_MANIFEST_SCHEMA_VERSION,
52071
+ tenant_id: this.tenantId,
52072
+ backend: this.backend.kind,
52073
+ deterministic_ids: true,
52074
+ immutable_receipts: true,
52075
+ transactional_outbox: true,
52076
+ idempotent_outbox_delivery: true,
52077
+ exact_bounded_readback: true,
52078
+ conditional_compensation: true,
52079
+ transcript_safe: false,
52080
+ bounds: { ...TODOS_TASK_MANIFEST_BOUNDS }
52081
+ };
52082
+ }
52083
+ now() {
52084
+ const value = this.options.now?.() ?? new Date().toISOString();
52085
+ if (!Number.isFinite(Date.parse(value))) {
52086
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "now() returned an invalid timestamp");
52087
+ }
52088
+ return value;
52089
+ }
52090
+ async prepareFaults() {
52091
+ const points = new Set;
52092
+ if (this.options.faultInjector) {
52093
+ for (const point of FAULT_POINTS) {
52094
+ if (await this.options.faultInjector(point) === true)
52095
+ points.add(point);
52096
+ }
52097
+ }
52098
+ return { points };
52099
+ }
52100
+ async apply(input) {
52101
+ const normalized = normalize(input, this.now());
52102
+ const faults = await this.prepareFaults();
52103
+ return this.bounded(await this.backend.apply(normalized, faults));
52104
+ }
52105
+ readExact(receiptId2) {
52106
+ if (!receiptId2 || receiptId2.length > 200) {
52107
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "receiptId must be a bounded exact identifier");
52108
+ }
52109
+ return this.backend.readExact(receiptId2).then((result) => this.bounded(result));
52110
+ }
52111
+ async lookupBinding(input) {
52112
+ const request = parseTodosTaskManifestBindingLookup(input);
52113
+ if (request.max_items !== 1) {
52114
+ 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 });
52115
+ }
52116
+ if (request.authority !== "todos" || request.route !== TODOS_TASK_MANIFEST_ROUTE || request.schema_version !== TODOS_TASK_MANIFEST_SCHEMA_VERSION || request.tenant_id !== this.tenantId) {
52117
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_CAPABILITY_MISMATCH", "Task-manifest binding lookup does not match this authority identity");
52118
+ }
52119
+ return this.bounded({
52120
+ authority: "todos",
52121
+ route: TODOS_TASK_MANIFEST_ROUTE,
52122
+ schema_version: TODOS_TASK_MANIFEST_SCHEMA_VERSION,
52123
+ tenant_id: this.tenantId,
52124
+ ...await this.backend.lookupBindingByPlanId(request.plan_id)
52125
+ });
52126
+ }
52127
+ markOutboxDelivered(outboxId) {
52128
+ if (!outboxId || outboxId.length > 200) {
52129
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "outboxId must be a bounded exact identifier");
52130
+ }
52131
+ return this.backend.markOutboxDelivered(outboxId, this.now());
52132
+ }
52133
+ async compensate(input) {
52134
+ const request = parseTodosTaskManifestCompensation(input);
52135
+ const applied = await this.backend.readExact(request.receipt_id);
52136
+ const requestDigest = canonicalDigest(request);
52137
+ const compensationReceiptId = deterministicUuid(TODOS_TASK_MANIFEST_ROUTE, "compensate", applied.receipt.operation_id, request.idempotency_key, requestDigest);
52138
+ const receipt = {
52139
+ receipt_id: compensationReceiptId,
52140
+ authority: "todos",
52141
+ route: TODOS_TASK_MANIFEST_ROUTE,
52142
+ schema_version: 1,
52143
+ kind: "compensate",
52144
+ operation_id: applied.receipt.operation_id,
52145
+ idempotency_key: request.idempotency_key,
52146
+ request_digest: requestDigest,
52147
+ result_digest: canonicalDigest({ absent: true, apply_receipt_id: applied.receipt.receipt_id }),
52148
+ binding_version: request.if_binding_version + 1,
52149
+ apply_receipt_id: applied.receipt.receipt_id,
52150
+ created_at: this.now()
52151
+ };
52152
+ return this.bounded(await this.backend.compensate(request, receipt, compensationReceiptId, requestDigest, receipt.created_at));
52153
+ }
52154
+ bounded(result) {
52155
+ const responseBytes2 = Buffer.byteLength(canonicalJson(result), "utf8");
52156
+ if (responseBytes2 > TODOS_TASK_MANIFEST_BOUNDS.response_bytes) {
52157
+ 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 });
52158
+ }
52159
+ return result;
52160
+ }
52161
+ }
52162
+ function createPostgresTodosTaskManifestAuthority(client, options = {}) {
52163
+ if (!client || typeof client.transaction !== "function") {
52164
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_ATOMICITY_UNAVAILABLE", "An authoritative PostgreSQL transaction(callback) client is required");
52165
+ }
52166
+ const tenantId = resolveTenantId(options.tenantId);
52167
+ return new PackageOwnedTodosTaskManifestAuthority(new PostgresTodosTaskManifestBackend(client, { ...options, tenantId }), { ...options, tenantId });
52168
+ }
52169
+ var FAULT_POINTS;
52170
+ var init_authority2 = __esm(() => {
52171
+ init_canonical();
52172
+ init_schema2();
52173
+ init_sqlite2();
52174
+ init_postgres3();
52175
+ init_prewrite_secrets();
52176
+ init_types5();
52177
+ init_schema2();
52178
+ FAULT_POINTS = [
52179
+ "after_plan_write",
52180
+ "after_task_write",
52181
+ "after_dependency_write",
52182
+ "after_comment_write",
52183
+ "after_verification_write",
52184
+ "after_outbox_write",
52185
+ "after_receipt_write"
52186
+ ];
52187
+ });
52188
+
52189
+ // src/task-manifest/http.ts
52190
+ function json2(body, status = 200) {
52191
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS2 });
52192
+ }
52193
+ function status(error) {
52194
+ switch (error.code) {
52195
+ case "TODOS_TASK_MANIFEST_INVALID_INPUT":
52196
+ case "TODOS_TASK_MANIFEST_BOUNDS_EXCEEDED":
52197
+ case "TODOS_TASK_MANIFEST_FOREIGN_REFERENCE":
52198
+ return 400;
52199
+ case "TODOS_TASK_MANIFEST_RECEIPT_NOT_FOUND":
52200
+ case "TODOS_TASK_MANIFEST_BINDING_NOT_FOUND":
52201
+ return 404;
52202
+ case "TODOS_TASK_MANIFEST_ATOMICITY_UNAVAILABLE":
52203
+ return 503;
52204
+ default:
52205
+ return 409;
52206
+ }
52207
+ }
52208
+ async function body(request) {
52209
+ const declaredLength = Number(request.headers.get("content-length") ?? 0);
52210
+ if (Number.isFinite(declaredLength) && declaredLength > TODOS_TASK_MANIFEST_BOUNDS.request_bytes) {
52211
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_BOUNDS_EXCEEDED", "Task-manifest HTTP body exceeds the request bound");
52212
+ }
52213
+ const text2 = await boundedText(request, TODOS_TASK_MANIFEST_BOUNDS.request_bytes, "request");
52214
+ try {
52215
+ return JSON.parse(text2);
52216
+ } catch {
52217
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "Invalid JSON body");
52218
+ }
52219
+ }
52220
+ async function boundedText(message, limit, label) {
52221
+ if (!message.body)
52222
+ return "";
52223
+ const reader = message.body.getReader();
52224
+ const decoder = new TextDecoder;
52225
+ let bytes = 0;
52226
+ let text2 = "";
52227
+ try {
52228
+ while (true) {
52229
+ const { done, value } = await reader.read();
52230
+ if (done)
52231
+ break;
52232
+ bytes += value.byteLength;
52233
+ if (bytes > limit) {
52234
+ await reader.cancel();
52235
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_BOUNDS_EXCEEDED", `Task-manifest HTTP ${label} exceeds the byte bound`);
52236
+ }
52237
+ text2 += decoder.decode(value, { stream: true });
52238
+ }
52239
+ return text2 + decoder.decode();
52240
+ } finally {
52241
+ reader.releaseLock();
52242
+ }
52243
+ }
52244
+ async function handleTodosTaskManifestHttpRequest(request, url, authority, basePath = "/v1/task-manifest") {
52245
+ if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`))
52246
+ return null;
52247
+ const action = url.pathname.slice(basePath.length).split("/").filter(Boolean).join("/");
52248
+ try {
52249
+ if ((action === "" || action === "capability") && request.method === "GET") {
52250
+ return json2({ capability: await authority.capability() });
52251
+ }
52252
+ if (request.method !== "POST")
52253
+ return json2({ error: "method not allowed" }, 405);
52254
+ if (action === "apply")
52255
+ return json2({ result: await authority.apply(await body(request)) }, 201);
52256
+ if (action === "read-exact") {
52257
+ const input = await body(request);
52258
+ if (!input || typeof input.receipt_id !== "string") {
52259
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "receipt_id is required");
52260
+ }
52261
+ return json2({ result: await authority.readExact(input.receipt_id) });
52262
+ }
52263
+ if (action === "bindings/lookup") {
52264
+ return json2({
52265
+ result: await authority.lookupBinding(await body(request))
52266
+ });
52267
+ }
52268
+ if (action === "compensate") {
52269
+ return json2({ result: await authority.compensate(await body(request)) }, 201);
52270
+ }
52271
+ if (action === "outbox/delivered") {
52272
+ const input = await body(request);
52273
+ if (!input || typeof input.outbox_id !== "string") {
52274
+ throw new TodosTaskManifestError("TODOS_TASK_MANIFEST_INVALID_INPUT", "outbox_id is required");
52275
+ }
52276
+ await authority.markOutboxDelivered(input.outbox_id);
52277
+ return json2({ delivered: true });
52278
+ }
52279
+ return json2({ error: "unknown task-manifest route" }, 404);
52280
+ } catch (cause) {
52281
+ if (cause instanceof TodosTaskManifestError) {
52282
+ return json2({ error: cause.message, code: cause.code, details: cause.details, authoritative: true }, status(cause));
52283
+ }
52284
+ return json2({ error: cause instanceof Error ? cause.message : "task-manifest error" }, 500);
52285
+ }
52286
+ }
52287
+ var JSON_HEADERS2;
52288
+ var init_http3 = __esm(() => {
52289
+ init_types5();
52290
+ init_schema2();
52291
+ JSON_HEADERS2 = { "Content-Type": "application/json" };
52292
+ });
52293
+
52294
+ // src/task-manifest/index.ts
52295
+ var init_task_manifest = __esm(() => {
52296
+ init_authority2();
52297
+ init_postgres3();
52298
+ init_sqlite2();
52299
+ init_http3();
52300
+ init_canonical();
52301
+ init_schema2();
52302
+ init_types5();
52303
+ });
52304
+
50914
52305
  // src/storage/comment-redaction-backfill.ts
50915
52306
  function assertSafeIdentifier2(value) {
50916
52307
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
@@ -50994,12 +52385,12 @@ async function backfillPostgresCommentRedaction(client, options = {}) {
50994
52385
  result.remaining_candidates = result.candidates;
50995
52386
  return result;
50996
52387
  }
50997
- const verification = await backfillPostgresCommentRedaction(client, {
52388
+ const verification2 = await backfillPostgresCommentRedaction(client, {
50998
52389
  ...options,
50999
52390
  apply: false,
51000
52391
  confirmation: undefined
51001
52392
  });
51002
- result.remaining_candidates = verification.candidates;
52393
+ result.remaining_candidates = verification2.candidates;
51003
52394
  return result;
51004
52395
  }
51005
52396
  var COMMENT_REDACTION_BACKFILL_CONFIRMATION = "REDACT_STORED_TODOS_COMMENTS";
@@ -51018,6 +52409,7 @@ __export(exports_cloud, {
51018
52409
  isPostgresBackendConfigured: () => isPostgresBackendConfigured,
51019
52410
  isCloudModeEnabled: () => isCloudModeEnabled,
51020
52411
  getCloudVerifier: () => getCloudVerifier,
52412
+ getCloudTaskManifestAuthority: () => getCloudTaskManifestAuthority,
51021
52413
  getCloudStorageAdapter: () => getCloudStorageAdapter,
51022
52414
  getCloudProjectRegistrationAuthority: () => getCloudProjectRegistrationAuthority,
51023
52415
  getCloudPrGroupLedger: () => getCloudPrGroupLedger,
@@ -51045,6 +52437,9 @@ function isPostgresBackendConfigured(env = process.env) {
51045
52437
  function isCloudModeEnabled(env = process.env) {
51046
52438
  return isPostgresBackendConfigured(env);
51047
52439
  }
52440
+ function getCloudTenantId() {
52441
+ return process.env.HASNA_TODOS_TENANT_ID ?? "default";
52442
+ }
51048
52443
  function getClient() {
51049
52444
  if (cachedClient)
51050
52445
  return cachedClient;
@@ -51074,11 +52469,20 @@ function getCloudProjectRegistrationAuthority() {
51074
52469
  cachedProjectRegistrationAuthority = createPostgresTodosProjectRegistrationAuthority(getClient(), {
51075
52470
  service: TODOS_APP_SLUG,
51076
52471
  authorityId: TODOS_APP_SLUG,
51077
- tenantId: process.env.HASNA_TODOS_TENANT_ID ?? "default",
52472
+ tenantId: getCloudTenantId(),
51078
52473
  corpusId: process.env.HASNA_TODOS_CORPUS_ID ?? `${TODOS_APP_SLUG}:postgresql`
51079
52474
  });
51080
52475
  return cachedProjectRegistrationAuthority;
51081
52476
  }
52477
+ function getCloudTaskManifestAuthority() {
52478
+ if (cachedTaskManifestAuthority)
52479
+ return cachedTaskManifestAuthority;
52480
+ cachedTaskManifestAuthority = createPostgresTodosTaskManifestAuthority(getClient(), {
52481
+ service: TODOS_APP_SLUG,
52482
+ tenantId: getCloudTenantId()
52483
+ });
52484
+ return cachedTaskManifestAuthority;
52485
+ }
51082
52486
  function authClient() {
51083
52487
  const client = getClient();
51084
52488
  return {
@@ -51130,6 +52534,9 @@ async function ensureCloudSchema() {
51130
52534
  for (const sql of postgresTodosProjectRegistrationSchemaSql()) {
51131
52535
  await client.query(sql);
51132
52536
  }
52537
+ for (const sql of postgresTodosTaskManifestSchemaSql(getCloudTenantId())) {
52538
+ await client.query(sql);
52539
+ }
51133
52540
  await getApiKeyStore().ensureSchema();
51134
52541
  })();
51135
52542
  return schemaEnsured;
@@ -51171,15 +52578,17 @@ async function closeCloud() {
51171
52578
  cachedVerifier = null;
51172
52579
  cachedPrGroupLedger = null;
51173
52580
  cachedProjectRegistrationAuthority = null;
52581
+ cachedTaskManifestAuthority = null;
51174
52582
  schemaEnsured = null;
51175
52583
  }
51176
- var TODOS_APP_SLUG = "todos", cachedClient = null, cachedAdapter = null, cachedStore = null, cachedVerifier = null, cachedPrGroupLedger = null, cachedProjectRegistrationAuthority = null, schemaEnsured = null;
52584
+ var TODOS_APP_SLUG = "todos", cachedClient = null, cachedAdapter = null, cachedStore = null, cachedVerifier = null, cachedPrGroupLedger = null, cachedProjectRegistrationAuthority = null, cachedTaskManifestAuthority = null, schemaEnsured = null;
51177
52585
  var init_cloud = __esm(() => {
51178
52586
  init_cloud_client();
51179
52587
  init_postgres_adapter();
51180
52588
  init_ledger();
51181
52589
  init_postgres();
51182
52590
  init_project_registration();
52591
+ init_task_manifest();
51183
52592
  init_postgres_sync();
51184
52593
  init_comment_redaction_backfill();
51185
52594
  });
@@ -51393,8 +52802,8 @@ function rowToOrg(row) {
51393
52802
  function createOrg(input, db) {
51394
52803
  const d = db || getDatabase();
51395
52804
  const id = uuid();
51396
- const timestamp4 = now();
51397
- 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]);
52805
+ const timestamp5 = now();
52806
+ 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]);
51398
52807
  return getOrg(id, d);
51399
52808
  }
51400
52809
  function getOrg(id, db) {
@@ -51538,9 +52947,9 @@ function parseBoundedLimit(value, fallback, max) {
51538
52947
  return fallback;
51539
52948
  return Math.min(parsed, max);
51540
52949
  }
51541
- function mapTaskError(e, json3) {
52950
+ function mapTaskError(e, json4) {
51542
52951
  if (e instanceof VersionConflictError) {
51543
- return json3({
52952
+ return json4({
51544
52953
  error: e.message,
51545
52954
  code: VersionConflictError.code,
51546
52955
  expected_version: e.expectedVersion,
@@ -51548,23 +52957,23 @@ function mapTaskError(e, json3) {
51548
52957
  }, 409);
51549
52958
  }
51550
52959
  if (e instanceof TaskNotFoundError) {
51551
- return json3({ error: e.message, code: TaskNotFoundError.code }, 404);
52960
+ return json4({ error: e.message, code: TaskNotFoundError.code }, 404);
51552
52961
  }
51553
52962
  if (e instanceof LockError) {
51554
- return json3({ error: e.message, code: LockError.code }, 409);
52963
+ return json4({ error: e.message, code: LockError.code }, 409);
51555
52964
  }
51556
52965
  if (e instanceof CompletionGuardError) {
51557
- return json3({
52966
+ return json4({
51558
52967
  error: e.message,
51559
52968
  code: CompletionGuardError.code,
51560
52969
  retry_after: e.retryAfterSeconds ?? null
51561
52970
  }, 409);
51562
52971
  }
51563
52972
  if (e instanceof TaskNotStartableError) {
51564
- return json3({ error: e.message, code: TaskNotStartableError.code }, 409);
52973
+ return json4({ error: e.message, code: TaskNotStartableError.code }, 409);
51565
52974
  }
51566
52975
  if (e instanceof Error && / is blocked by /.test(e.message)) {
51567
- return json3({ error: e.message, code: "TASK_NOT_STARTABLE" }, 409);
52976
+ return json4({ error: e.message, code: "TASK_NOT_STARTABLE" }, 409);
51568
52977
  }
51569
52978
  return null;
51570
52979
  }
@@ -51649,11 +53058,11 @@ data: ${JSON.stringify({ type: "connected", agent_id: agentId, timestamp: new Da
51649
53058
  }
51650
53059
  });
51651
53060
  }
51652
- function handleHealth(_ctx, json3) {
53061
+ function handleHealth(_ctx, json4) {
51653
53062
  const stats2 = getTaskStats();
51654
53063
  const staleCount = getStaleTasks(30).length;
51655
53064
  const overdueRecurring = getOverdueTasks().filter((t) => t.recurrence_rule && t.status === "pending").length;
51656
- return json3({
53065
+ return json4({
51657
53066
  status: staleCount === 0 && overdueRecurring === 0 ? "ok" : "warn",
51658
53067
  tasks: stats2.total,
51659
53068
  stale: staleCount,
@@ -51661,18 +53070,18 @@ function handleHealth(_ctx, json3) {
51661
53070
  timestamp: new Date().toISOString()
51662
53071
  });
51663
53072
  }
51664
- function handleHeadlessBoundary(_ctx, json3) {
53073
+ function handleHeadlessBoundary(_ctx, json4) {
51665
53074
  const { getHeadlessBoundaryManifest: getHeadlessBoundaryManifest2 } = (init_headless_boundaries(), __toCommonJS(exports_headless_boundaries));
51666
- return json3(getHeadlessBoundaryManifest2());
53075
+ return json4(getHeadlessBoundaryManifest2());
51667
53076
  }
51668
- function handleStats(_ctx, json3) {
53077
+ function handleStats(_ctx, json4) {
51669
53078
  const stats2 = getTaskStats();
51670
53079
  const byStatus = stats2.by_status;
51671
53080
  const projects = listProjects();
51672
53081
  const agents = listAgents();
51673
53082
  const staleCount = getStaleTasks(30).length;
51674
53083
  const overdueRecurring = getOverdueTasks().filter((t) => t.recurrence_rule && t.status === "pending").length;
51675
- return json3({
53084
+ return json4({
51676
53085
  total_tasks: stats2.total,
51677
53086
  pending: byStatus["pending"] ?? 0,
51678
53087
  in_progress: byStatus["in_progress"] ?? 0,
@@ -51695,10 +53104,10 @@ function taskStatusQueryParam(url) {
51695
53104
  return { ok: false, message: result.message };
51696
53105
  return { ok: true, value: collapseEnumValues(result.values) };
51697
53106
  }
51698
- async function handleListTasks(_req, url, _ctx, json3, taskToSummary2) {
53107
+ async function handleListTasks(_req, url, _ctx, json4, taskToSummary2) {
51699
53108
  const statusParam = taskStatusQueryParam(url);
51700
53109
  if (!statusParam.ok)
51701
- return json3({ error: statusParam.message }, 400);
53110
+ return json4({ error: statusParam.message }, 400);
51702
53111
  const projectId = url.searchParams.get("project_id") || undefined;
51703
53112
  const sessionId = url.searchParams.get("session_id") || undefined;
51704
53113
  const agentId = url.searchParams.get("agent_id") || undefined;
@@ -51713,60 +53122,60 @@ async function handleListTasks(_req, url, _ctx, json3, taskToSummary2) {
51713
53122
  limit: limitParam ? parseInt(limitParam, 10) : undefined,
51714
53123
  offset: offsetParam ? parseInt(offsetParam, 10) : undefined
51715
53124
  });
51716
- return json3(tasks.map((t) => taskToSummary2(t, fields)));
53125
+ return json4(tasks.map((t) => taskToSummary2(t, fields)));
51717
53126
  }
51718
- async function handleCreateTask(req, ctx, json3, taskToSummary2) {
53127
+ async function handleCreateTask(req, ctx, json4, taskToSummary2) {
51719
53128
  try {
51720
- const body = await req.json();
51721
- if (!body.title)
51722
- return json3({ error: "Missing 'title'" }, 400);
51723
- const createdBy = body.created_by ?? body.agent_id ?? "dashboard";
51724
- const task2 = createTask({
51725
- title: body.title,
51726
- description: body.description,
51727
- priority: body.priority,
51728
- project_id: body.project_id,
51729
- agent_id: body.agent_id ?? createdBy,
53129
+ const body2 = await req.json();
53130
+ if (!body2.title)
53131
+ return json4({ error: "Missing 'title'" }, 400);
53132
+ const createdBy = body2.created_by ?? body2.agent_id ?? "dashboard";
53133
+ const task3 = createTask({
53134
+ title: body2.title,
53135
+ description: body2.description,
53136
+ priority: body2.priority,
53137
+ project_id: body2.project_id,
53138
+ agent_id: body2.agent_id ?? createdBy,
51730
53139
  created_by: createdBy,
51731
- ...body.assigned_to ? { assigned_to: body.assigned_to } : {}
53140
+ ...body2.assigned_to ? { assigned_to: body2.assigned_to } : {}
51732
53141
  });
51733
- ctx.broadcastEvent({ type: "task", task_id: task2.id, action: "created", agent_id: task2.agent_id, project_id: task2.project_id });
51734
- return json3(taskToSummary2(task2), 201);
53142
+ ctx.broadcastEvent({ type: "task", task_id: task3.id, action: "created", agent_id: task3.agent_id, project_id: task3.project_id });
53143
+ return json4(taskToSummary2(task3), 201);
51735
53144
  } catch (e) {
51736
- return json3({ error: e instanceof Error ? e.message : "Failed to create task" }, 500);
53145
+ return json4({ error: e instanceof Error ? e.message : "Failed to create task" }, 500);
51737
53146
  }
51738
53147
  }
51739
- async function handleUpsertTask(req, ctx, json3, taskToSummary2) {
53148
+ async function handleUpsertTask(req, ctx, json4, taskToSummary2) {
51740
53149
  try {
51741
- const body = await req.json();
51742
- if (typeof body["fingerprint"] !== "string" || body["fingerprint"].trim() === "") {
51743
- return json3({ error: "Missing 'fingerprint'" }, 400);
53150
+ const body2 = await req.json();
53151
+ if (typeof body2["fingerprint"] !== "string" || body2["fingerprint"].trim() === "") {
53152
+ return json4({ error: "Missing 'fingerprint'" }, 400);
51744
53153
  }
51745
- if (typeof body["title"] !== "string" || body["title"].trim() === "") {
51746
- return json3({ error: "Missing 'title'" }, 400);
53154
+ if (typeof body2["title"] !== "string" || body2["title"].trim() === "") {
53155
+ return json4({ error: "Missing 'title'" }, 400);
51747
53156
  }
51748
- const metadata = body["metadata"] && typeof body["metadata"] === "object" && !Array.isArray(body["metadata"]) ? { ...body["metadata"] } : {};
51749
- for (const key of ["expectation_id", "expectation_fingerprint", "evidence_paths", "origin_loop_id", "origin_run_id", "expected", "observed", "acceptance"]) {
51750
- if (body[key] !== undefined)
51751
- metadata[key] = body[key];
53157
+ const metadata = body2["metadata"] && typeof body2["metadata"] === "object" && !Array.isArray(body2["metadata"]) ? { ...body2["metadata"] } : {};
53158
+ for (const key2 of ["expectation_id", "expectation_fingerprint", "evidence_paths", "origin_loop_id", "origin_run_id", "expected", "observed", "acceptance"]) {
53159
+ if (body2[key2] !== undefined)
53160
+ metadata[key2] = body2[key2];
51752
53161
  }
51753
53162
  const result = upsertTaskByFingerprint({
51754
- fingerprint: body["fingerprint"],
51755
- title: body["title"],
51756
- description: typeof body["description"] === "string" ? body["description"] : undefined,
51757
- status: body["status"],
51758
- priority: body["priority"],
51759
- project_id: typeof body["project_id"] === "string" ? body["project_id"] : undefined,
51760
- task_list_id: typeof body["task_list_id"] === "string" ? body["task_list_id"] : undefined,
51761
- assigned_to: typeof body["assigned_to"] === "string" ? body["assigned_to"] : undefined,
51762
- working_dir: typeof body["working_dir"] === "string" ? body["working_dir"] : undefined,
51763
- tags: Array.isArray(body["tags"]) ? body["tags"].filter((tag) => typeof tag === "string") : undefined,
53163
+ fingerprint: body2["fingerprint"],
53164
+ title: body2["title"],
53165
+ description: typeof body2["description"] === "string" ? body2["description"] : undefined,
53166
+ status: body2["status"],
53167
+ priority: body2["priority"],
53168
+ project_id: typeof body2["project_id"] === "string" ? body2["project_id"] : undefined,
53169
+ task_list_id: typeof body2["task_list_id"] === "string" ? body2["task_list_id"] : undefined,
53170
+ assigned_to: typeof body2["assigned_to"] === "string" ? body2["assigned_to"] : undefined,
53171
+ working_dir: typeof body2["working_dir"] === "string" ? body2["working_dir"] : undefined,
53172
+ tags: Array.isArray(body2["tags"]) ? body2["tags"].filter((tag) => typeof tag === "string") : undefined,
51764
53173
  metadata
51765
53174
  });
51766
53175
  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 });
51767
- return json3({ created: result.created, task: taskToSummary2(result.task) }, result.created ? 201 : 200);
53176
+ return json4({ created: result.created, task: taskToSummary2(result.task) }, result.created ? 201 : 200);
51768
53177
  } catch (e) {
51769
- return json3({ error: e instanceof Error ? e.message : "Failed to upsert task" }, 500);
53178
+ return json4({ error: e instanceof Error ? e.message : "Failed to upsert task" }, 500);
51770
53179
  }
51771
53180
  }
51772
53181
  function handleTasksExport(_req, url, _ctx, _json, taskToSummary2) {
@@ -51818,21 +53227,21 @@ function handleTasksExport(_req, url, _ctx, _json, taskToSummary2) {
51818
53227
  }
51819
53228
  });
51820
53229
  }
51821
- async function handleTasksBulk(req, _ctx, json3) {
53230
+ async function handleTasksBulk(req, _ctx, json4) {
51822
53231
  try {
51823
- const body = await req.json();
51824
- if (!body.ids?.length || !body.action)
51825
- return json3({ error: "Missing ids or action" }, 400);
53232
+ const body2 = await req.json();
53233
+ if (!body2.ids?.length || !body2.action)
53234
+ return json4({ error: "Missing ids or action" }, 400);
51826
53235
  const results = [];
51827
- for (const id of body.ids) {
53236
+ for (const id of body2.ids) {
51828
53237
  try {
51829
- if (body.action === "delete") {
53238
+ if (body2.action === "delete") {
51830
53239
  deleteTask(id);
51831
53240
  results.push({ id, success: true });
51832
- } else if (body.action === "start") {
53241
+ } else if (body2.action === "start") {
51833
53242
  startTask(id, "dashboard");
51834
53243
  results.push({ id, success: true });
51835
- } else if (body.action === "complete") {
53244
+ } else if (body2.action === "complete") {
51836
53245
  completeTask(id, "dashboard");
51837
53246
  results.push({ id, success: true });
51838
53247
  }
@@ -51840,84 +53249,84 @@ async function handleTasksBulk(req, _ctx, json3) {
51840
53249
  results.push({ id, success: false, error: e instanceof Error ? e.message : "Failed" });
51841
53250
  }
51842
53251
  }
51843
- return json3({ results, succeeded: results.filter((r) => r.success).length, failed: results.filter((r) => !r.success).length });
53252
+ return json4({ results, succeeded: results.filter((r) => r.success).length, failed: results.filter((r) => !r.success).length });
51844
53253
  } catch (e) {
51845
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53254
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51846
53255
  }
51847
53256
  }
51848
- function handleTasksStatus(_req, url, _ctx, json3) {
53257
+ function handleTasksStatus(_req, url, _ctx, json4) {
51849
53258
  try {
51850
53259
  const projectId = url.searchParams.get("project_id") || undefined;
51851
53260
  const agentId = url.searchParams.get("agent_id") || undefined;
51852
- const status = getStatus(projectId ? { project_id: projectId } : undefined, agentId);
51853
- return json3(status);
53261
+ const status2 = getStatus(projectId ? { project_id: projectId } : undefined, agentId);
53262
+ return json4(status2);
51854
53263
  } catch (e) {
51855
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53264
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51856
53265
  }
51857
53266
  }
51858
- function handleTasksNext(_req, url, _ctx, json3, taskToSummary2) {
53267
+ function handleTasksNext(_req, url, _ctx, json4, taskToSummary2) {
51859
53268
  try {
51860
53269
  const projectId = url.searchParams.get("project_id") || undefined;
51861
53270
  const agentId = url.searchParams.get("agent_id") || undefined;
51862
53271
  const fields = parseFieldsParam(url);
51863
- const task2 = getNextTask(agentId, projectId ? { project_id: projectId } : undefined);
51864
- return json3({ task: task2 ? taskToSummary2(task2, fields) : null });
53272
+ const task3 = getNextTask(agentId, projectId ? { project_id: projectId } : undefined);
53273
+ return json4({ task: task3 ? taskToSummary2(task3, fields) : null });
51865
53274
  } catch (e) {
51866
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53275
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51867
53276
  }
51868
53277
  }
51869
- function handleTasksActive(_req, url, _ctx, json3) {
53278
+ function handleTasksActive(_req, url, _ctx, json4) {
51870
53279
  try {
51871
53280
  const projectId = url.searchParams.get("project_id") || undefined;
51872
53281
  const work = getActiveWork(projectId ? { project_id: projectId } : undefined);
51873
- return json3({ active: work, count: work.length });
53282
+ return json4({ active: work, count: work.length });
51874
53283
  } catch (e) {
51875
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53284
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51876
53285
  }
51877
53286
  }
51878
- function handleTasksStale(_req, url, _ctx, json3, taskToSummary2) {
53287
+ function handleTasksStale(_req, url, _ctx, json4, taskToSummary2) {
51879
53288
  try {
51880
53289
  const projectId = url.searchParams.get("project_id") || undefined;
51881
53290
  const minutes2 = parseInt(url.searchParams.get("minutes") || "30", 10);
51882
53291
  const fields = parseFieldsParam(url);
51883
53292
  const tasks = getStaleTasks(minutes2, projectId ? { project_id: projectId } : undefined);
51884
- return json3({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length });
53293
+ return json4({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length });
51885
53294
  } catch (e) {
51886
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53295
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51887
53296
  }
51888
53297
  }
51889
- function handleTasksChanged(_req, url, _ctx, json3, taskToSummary2) {
53298
+ function handleTasksChanged(_req, url, _ctx, json4, taskToSummary2) {
51890
53299
  try {
51891
53300
  const since = url.searchParams.get("since");
51892
53301
  if (!since)
51893
- return json3({ error: "since parameter required (ISO date string)" }, 400);
53302
+ return json4({ error: "since parameter required (ISO date string)" }, 400);
51894
53303
  const projectId = url.searchParams.get("project_id") || undefined;
51895
53304
  const fields = parseFieldsParam(url);
51896
53305
  const tasks = getTasksChangedSince(since, projectId ? { project_id: projectId } : undefined);
51897
- return json3({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length, since });
53306
+ return json4({ tasks: tasks.map((t) => taskToSummary2(t, fields)), count: tasks.length, since });
51898
53307
  } catch (e) {
51899
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53308
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
51900
53309
  }
51901
53310
  }
51902
- function handleTasksContext(_req, url, _ctx, json3, taskToSummary2) {
53311
+ function handleTasksContext(_req, url, _ctx, json4, taskToSummary2) {
51903
53312
  const agentId = url.searchParams.get("agent_id") || undefined;
51904
53313
  const projectId = url.searchParams.get("project_id") || undefined;
51905
53314
  const format = url.searchParams.get("format") || "text";
51906
53315
  const fields = parseFieldsParam(url);
51907
53316
  const filters = projectId ? { project_id: projectId } : undefined;
51908
- const status = getStatus(filters, agentId);
53317
+ const status2 = getStatus(filters, agentId);
51909
53318
  const next = getNextTask(agentId, filters);
51910
53319
  if (format === "json") {
51911
- return json3({ status, next_task: next ? taskToSummary2(next, fields) : null });
53320
+ return json4({ status: status2, next_task: next ? taskToSummary2(next, fields) : null });
51912
53321
  }
51913
53322
  const lines = [];
51914
- lines.push(`Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done`);
51915
- if (status.stale_count > 0)
51916
- lines.push(`${status.stale_count} stale tasks stuck in-progress`);
51917
- if (status.overdue_recurring > 0)
51918
- lines.push(`${status.overdue_recurring} overdue recurring tasks`);
51919
- if (status.active_work.length > 0) {
51920
- lines.push(`Active: ${status.active_work.slice(0, 3).map((w) => `${w.short_id || w.id.slice(0, 8)} (${w.assigned_to || "?"})`).join(", ")}`);
53323
+ lines.push(`Tasks: ${status2.pending} pending | ${status2.in_progress} active | ${status2.completed} done`);
53324
+ if (status2.stale_count > 0)
53325
+ lines.push(`${status2.stale_count} stale tasks stuck in-progress`);
53326
+ if (status2.overdue_recurring > 0)
53327
+ lines.push(`${status2.overdue_recurring} overdue recurring tasks`);
53328
+ if (status2.active_work.length > 0) {
53329
+ lines.push(`Active: ${status2.active_work.slice(0, 3).map((w) => `${w.short_id || w.id.slice(0, 8)} (${w.assigned_to || "?"})`).join(", ")}`);
51921
53330
  }
51922
53331
  if (next)
51923
53332
  lines.push(`Next up: ${next.short_id || next.id.slice(0, 8)} [${next.priority}] ${next.title}`);
@@ -51925,18 +53334,18 @@ function handleTasksContext(_req, url, _ctx, json3, taskToSummary2) {
51925
53334
  `);
51926
53335
  return new Response(text2, { headers: { "Content-Type": "text/plain" } });
51927
53336
  }
51928
- function handleTaskAttachments(id, _ctx, json3) {
51929
- const task2 = getTask(id);
51930
- if (!task2)
51931
- return json3({ error: "Task not found" }, 404);
51932
- const evidence = task2.metadata?._evidence || {};
53337
+ function handleTaskAttachments(id, _ctx, json4) {
53338
+ const task3 = getTask(id);
53339
+ if (!task3)
53340
+ return json4({ error: "Task not found" }, 404);
53341
+ const evidence = task3.metadata?._evidence || {};
51933
53342
  const attachmentIds = evidence.attachments || [];
51934
- 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 });
53343
+ 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 });
51935
53344
  }
51936
- async function handleTaskProgress(id, req, method, _ctx, json3, url) {
51937
- const task2 = getTask(id);
51938
- if (!task2)
51939
- return json3({ error: "Task not found" }, 404);
53345
+ async function handleTaskProgress(id, req, method, _ctx, json4, url) {
53346
+ const task3 = getTask(id);
53347
+ if (!task3)
53348
+ return json4({ error: "Task not found" }, 404);
51940
53349
  if (method === "GET") {
51941
53350
  const all = listComments(id);
51942
53351
  const progress = all.filter((c) => c.type === "progress");
@@ -51944,7 +53353,7 @@ async function handleTaskProgress(id, req, method, _ctx, json3, url) {
51944
53353
  const format = url?.searchParams.get("format") || "compact";
51945
53354
  const limit = parseBoundedLimit(url?.searchParams.get("limit") || null, 20, 200);
51946
53355
  const progressEntries = format === "full" ? progress : progress.slice(-limit);
51947
- return json3({
53356
+ return json4({
51948
53357
  task_id: id,
51949
53358
  progress_entries: progressEntries,
51950
53359
  latest,
@@ -51959,119 +53368,119 @@ async function handleTaskProgress(id, req, method, _ctx, json3, url) {
51959
53368
  }
51960
53369
  if (method === "POST") {
51961
53370
  try {
51962
- const body = await req.json();
51963
- if (!body.message)
51964
- return json3({ error: "message required" }, 400);
51965
- const comment = logProgress(id, body.message, body.pct_complete, body.agent_id);
51966
- return json3(comment, 201);
53371
+ const body2 = await req.json();
53372
+ if (!body2.message)
53373
+ return json4({ error: "message required" }, 400);
53374
+ const comment2 = logProgress(id, body2.message, body2.pct_complete, body2.agent_id);
53375
+ return json4(comment2, 201);
51967
53376
  } catch (e) {
51968
- return json3({ error: e instanceof Error ? e.message : "Failed to log progress" }, 500);
53377
+ return json4({ error: e instanceof Error ? e.message : "Failed to log progress" }, 500);
51969
53378
  }
51970
53379
  }
51971
53380
  return null;
51972
53381
  }
51973
- function handleGetTask(id, _ctx, json3, taskToSummary2, url) {
51974
- const task2 = getTask(id);
51975
- if (!task2)
51976
- return json3({ error: "Task not found" }, 404);
51977
- return json3(taskToSummary2(task2, url ? parseFieldsParam(url) : undefined));
53382
+ function handleGetTask(id, _ctx, json4, taskToSummary2, url) {
53383
+ const task3 = getTask(id);
53384
+ if (!task3)
53385
+ return json4({ error: "Task not found" }, 404);
53386
+ return json4(taskToSummary2(task3, url ? parseFieldsParam(url) : undefined));
51978
53387
  }
51979
- async function handlePatchTask(id, req, _ctx, json3, taskToSummary2) {
53388
+ async function handlePatchTask(id, req, _ctx, json4, taskToSummary2) {
51980
53389
  try {
51981
- const body = await req.json();
51982
- const task2 = getTask(id);
51983
- if (!task2)
51984
- return json3({ error: "Task not found" }, 404);
53390
+ const body2 = await req.json();
53391
+ const task3 = getTask(id);
53392
+ if (!task3)
53393
+ return json4({ error: "Task not found" }, 404);
51985
53394
  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"]);
51986
53395
  const safeBody = {};
51987
- for (const [key, value] of Object.entries(body)) {
51988
- if (ALLOWED.has(key))
51989
- safeBody[key] = value;
53396
+ for (const [key2, value] of Object.entries(body2)) {
53397
+ if (ALLOWED.has(key2))
53398
+ safeBody[key2] = value;
51990
53399
  }
51991
- const clientVersion = typeof body["version"] === "number" ? body["version"] : task2.version;
53400
+ const clientVersion = typeof body2["version"] === "number" ? body2["version"] : task3.version;
51992
53401
  const updated = updateTask(id, {
51993
53402
  ...safeBody,
51994
53403
  version: clientVersion
51995
53404
  });
51996
- return json3(taskToSummary2(updated));
53405
+ return json4(taskToSummary2(updated));
51997
53406
  } catch (e) {
51998
- const mapped = mapTaskError(e, json3);
53407
+ const mapped = mapTaskError(e, json4);
51999
53408
  if (mapped)
52000
53409
  return mapped;
52001
- return json3({ error: e instanceof Error ? e.message : "Failed to update task" }, 500);
53410
+ return json4({ error: e instanceof Error ? e.message : "Failed to update task" }, 500);
52002
53411
  }
52003
53412
  }
52004
- function handleDeleteTask(id, _ctx, json3) {
53413
+ function handleDeleteTask(id, _ctx, json4) {
52005
53414
  const deleted = deleteTask(id);
52006
53415
  if (!deleted)
52007
- return json3({ error: "Task not found" }, 404);
52008
- return json3({ success: true });
53416
+ return json4({ error: "Task not found" }, 404);
53417
+ return json4({ success: true });
52009
53418
  }
52010
- function handleStartTask(id, ctx, json3, taskToSummary2) {
53419
+ function handleStartTask(id, ctx, json4, taskToSummary2) {
52011
53420
  try {
52012
- const task2 = startTask(id, "dashboard");
52013
- ctx.broadcastEvent({ type: "task", task_id: task2.id, action: "started", agent_id: "dashboard", project_id: task2.project_id });
52014
- return json3(taskToSummary2(task2));
53421
+ const task3 = startTask(id, "dashboard");
53422
+ ctx.broadcastEvent({ type: "task", task_id: task3.id, action: "started", agent_id: "dashboard", project_id: task3.project_id });
53423
+ return json4(taskToSummary2(task3));
52015
53424
  } catch (e) {
52016
- const mapped = mapTaskError(e, json3);
53425
+ const mapped = mapTaskError(e, json4);
52017
53426
  if (mapped)
52018
53427
  return mapped;
52019
- return json3({ error: e instanceof Error ? e.message : "Failed to start task" }, 500);
53428
+ return json4({ error: e instanceof Error ? e.message : "Failed to start task" }, 500);
52020
53429
  }
52021
53430
  }
52022
- async function handleFailTask(id, req, ctx, json3, taskToSummary2) {
53431
+ async function handleFailTask(id, req, ctx, json4, taskToSummary2) {
52023
53432
  try {
52024
- const body = await req.json().catch(() => ({}));
52025
- const result = failTask(id, body.agent_id, body.reason, { retry: body.retry, error_code: body.error_code });
52026
- ctx.broadcastEvent({ type: "task", task_id: id, action: "failed", agent_id: body.agent_id || null, project_id: result.task.project_id });
52027
- return json3({ task: taskToSummary2(result.task), retry_task: result.retryTask ? taskToSummary2(result.retryTask) : null });
53433
+ const body2 = await req.json().catch(() => ({}));
53434
+ const result = failTask(id, body2.agent_id, body2.reason, { retry: body2.retry, error_code: body2.error_code });
53435
+ ctx.broadcastEvent({ type: "task", task_id: id, action: "failed", agent_id: body2.agent_id || null, project_id: result.task.project_id });
53436
+ return json4({ task: taskToSummary2(result.task), retry_task: result.retryTask ? taskToSummary2(result.retryTask) : null });
52028
53437
  } catch (e) {
52029
- return json3({ error: e instanceof Error ? e.message : "Failed to fail task" }, 500);
53438
+ return json4({ error: e instanceof Error ? e.message : "Failed to fail task" }, 500);
52030
53439
  }
52031
53440
  }
52032
- function handleCompleteTask(id, ctx, json3, taskToSummary2) {
53441
+ function handleCompleteTask(id, ctx, json4, taskToSummary2) {
52033
53442
  try {
52034
- const task2 = completeTask(id, "dashboard");
52035
- ctx.broadcastEvent({ type: "task", task_id: task2.id, action: "completed", agent_id: "dashboard", project_id: task2.project_id });
52036
- return json3(taskToSummary2(task2));
53443
+ const task3 = completeTask(id, "dashboard");
53444
+ ctx.broadcastEvent({ type: "task", task_id: task3.id, action: "completed", agent_id: "dashboard", project_id: task3.project_id });
53445
+ return json4(taskToSummary2(task3));
52037
53446
  } catch (e) {
52038
- const mapped = mapTaskError(e, json3);
53447
+ const mapped = mapTaskError(e, json4);
52039
53448
  if (mapped)
52040
53449
  return mapped;
52041
- return json3({ error: e instanceof Error ? e.message : "Failed to complete task" }, 500);
53450
+ return json4({ error: e instanceof Error ? e.message : "Failed to complete task" }, 500);
52042
53451
  }
52043
53452
  }
52044
- function handleListProjects(url, _ctx, json3) {
53453
+ function handleListProjects(url, _ctx, json4) {
52045
53454
  const pFieldsParam = url.searchParams.get("fields");
52046
53455
  const pFields = pFieldsParam ? pFieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
52047
53456
  const projects = listProjects();
52048
- return json3(pFields ? projects.map((p) => Object.fromEntries(pFields.map((f) => [f, p[f] ?? null]))) : projects);
53457
+ return json4(pFields ? projects.map((p) => Object.fromEntries(pFields.map((f) => [f, p[f] ?? null]))) : projects);
52049
53458
  }
52050
- async function handleCreateProject(req, _ctx, json3) {
53459
+ async function handleCreateProject(req, _ctx, json4) {
52051
53460
  try {
52052
- const body = await req.json();
52053
- if (!body.name || !body.path)
52054
- return json3({ error: "Missing name or path" }, 400);
52055
- const project = createProject({ name: body.name, path: body.path, description: body.description });
52056
- return json3(project, 201);
53461
+ const body2 = await req.json();
53462
+ if (!body2.name || !body2.path)
53463
+ return json4({ error: "Missing name or path" }, 400);
53464
+ const project = createProject({ name: body2.name, path: body2.path, description: body2.description });
53465
+ return json4(project, 201);
52057
53466
  } catch (e) {
52058
- return json3({ error: e instanceof Error ? e.message : "Failed to create project" }, 500);
53467
+ return json4({ error: e instanceof Error ? e.message : "Failed to create project" }, 500);
52059
53468
  }
52060
53469
  }
52061
- function handleDeleteProject(id, _ctx, json3) {
53470
+ function handleDeleteProject(id, _ctx, json4) {
52062
53471
  const deleted = deleteProject(id);
52063
53472
  if (!deleted)
52064
- return json3({ error: "Project not found" }, 404);
52065
- return json3({ success: true });
53473
+ return json4({ error: "Project not found" }, 404);
53474
+ return json4({ success: true });
52066
53475
  }
52067
- async function handleAgentMe(_req, url, _ctx, json3, taskToSummary2) {
53476
+ async function handleAgentMe(_req, url, _ctx, json4, taskToSummary2) {
52068
53477
  try {
52069
53478
  const name = url.searchParams.get("name");
52070
53479
  if (!name)
52071
- return json3({ error: "Missing name param" }, 400);
53480
+ return json4({ error: "Missing name param" }, 400);
52072
53481
  const agentResult = registerAgent({ name });
52073
53482
  if (isAgentConflict(agentResult))
52074
- return json3({ error: agentResult.message, conflict: true }, 409);
53483
+ return json4({ error: agentResult.message, conflict: true }, 409);
52075
53484
  const agent = agentResult;
52076
53485
  const tasks = listTasks({ assigned_to: agent.name });
52077
53486
  const agentIdTasks = listTasks({ agent_id: agent.id });
@@ -52079,7 +53488,7 @@ async function handleAgentMe(_req, url, _ctx, json3, taskToSummary2) {
52079
53488
  const pending = allTasks.filter((t) => t.status === "pending");
52080
53489
  const inProgress = allTasks.filter((t) => t.status === "in_progress");
52081
53490
  const completed = allTasks.filter((t) => t.status === "completed");
52082
- return json3({
53491
+ return json4({
52083
53492
  agent,
52084
53493
  pending_tasks: pending.map((t) => taskToSummary2(t)),
52085
53494
  in_progress_tasks: inProgress.map((t) => taskToSummary2(t)),
@@ -52093,132 +53502,132 @@ async function handleAgentMe(_req, url, _ctx, json3, taskToSummary2) {
52093
53502
  });
52094
53503
  } catch (e) {
52095
53504
  if (e instanceof InvalidAgentNameError)
52096
- return json3({ error: e.message, suggestions: e.suggestions }, 400);
52097
- return json3({ error: e instanceof Error ? e.message : "Failed to get agent profile" }, 500);
53505
+ return json4({ error: e.message, suggestions: e.suggestions }, 400);
53506
+ return json4({ error: e instanceof Error ? e.message : "Failed to get agent profile" }, 500);
52098
53507
  }
52099
53508
  }
52100
- function handleAgentQueue(agentId, _ctx, json3, taskToSummary2) {
53509
+ function handleAgentQueue(agentId, _ctx, json4, taskToSummary2) {
52101
53510
  const aliasSet = assignedToAliasSet(getDatabase(), agentId);
52102
53511
  const pending = listTasks({ status: "pending" });
52103
53512
  const queue = pending.filter((t) => aliasSet.has((t.assigned_to ?? "").toLowerCase()) || t.agent_id === agentId || !t.assigned_to && !t.locked_by);
52104
53513
  const order = { critical: 0, high: 1, medium: 2, low: 3 };
52105
53514
  queue.sort((a, b) => (order[a.priority] ?? 4) - (order[b.priority] ?? 4) || new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
52106
- return json3(queue.map((t) => taskToSummary2(t)));
53515
+ return json4(queue.map((t) => taskToSummary2(t)));
52107
53516
  }
52108
- async function handleClaimTask(req, _ctx, json3, taskToSummary2) {
53517
+ async function handleClaimTask(req, _ctx, json4, taskToSummary2) {
52109
53518
  try {
52110
- const body = await req.json();
52111
- const agentId = body.agent_id || "anonymous";
52112
- const task2 = claimNextTask(agentId, body.project_id ? { project_id: body.project_id } : undefined);
52113
- return json3({ task: task2 ? taskToSummary2(task2) : null });
53519
+ const body2 = await req.json();
53520
+ const agentId = body2.agent_id || "anonymous";
53521
+ const task3 = claimNextTask(agentId, body2.project_id ? { project_id: body2.project_id } : undefined);
53522
+ return json4({ task: task3 ? taskToSummary2(task3) : null });
52114
53523
  } catch (e) {
52115
- return json3({ error: e instanceof Error ? e.message : "Failed to claim" }, 500);
53524
+ return json4({ error: e instanceof Error ? e.message : "Failed to claim" }, 500);
52116
53525
  }
52117
53526
  }
52118
- function handleListOrgs(_ctx, json3) {
52119
- return json3(listOrgs());
53527
+ function handleListOrgs(_ctx, json4) {
53528
+ return json4(listOrgs());
52120
53529
  }
52121
- async function handleCreateOrg(req, _ctx, json3) {
53530
+ async function handleCreateOrg(req, _ctx, json4) {
52122
53531
  try {
52123
- const body = await req.json();
52124
- if (!body.name)
52125
- return json3({ error: "Missing name" }, 400);
52126
- return json3(createOrg(body), 201);
53532
+ const body2 = await req.json();
53533
+ if (!body2.name)
53534
+ return json4({ error: "Missing name" }, 400);
53535
+ return json4(createOrg(body2), 201);
52127
53536
  } catch (e) {
52128
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53537
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52129
53538
  }
52130
53539
  }
52131
- async function handleUpdateOrg(id, req, _ctx, json3) {
53540
+ async function handleUpdateOrg(id, req, _ctx, json4) {
52132
53541
  try {
52133
- const body = await req.json();
52134
- return json3(updateOrg(id, body));
53542
+ const body2 = await req.json();
53543
+ return json4(updateOrg(id, body2));
52135
53544
  } catch (e) {
52136
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53545
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52137
53546
  }
52138
53547
  }
52139
- function handleDeleteOrg(id, _ctx, json3) {
53548
+ function handleDeleteOrg(id, _ctx, json4) {
52140
53549
  const deleted = deleteOrg(id);
52141
- return json3(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
53550
+ return json4(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
52142
53551
  }
52143
- function handleOrgChart(_ctx, json3) {
52144
- return json3(getOrgChart());
53552
+ function handleOrgChart(_ctx, json4) {
53553
+ return json4(getOrgChart());
52145
53554
  }
52146
- function handleAgentTeam(agentId, _ctx, json3) {
52147
- return json3(getDirectReports(decodeURIComponent(agentId)));
53555
+ function handleAgentTeam(agentId, _ctx, json4) {
53556
+ return json4(getDirectReports(decodeURIComponent(agentId)));
52148
53557
  }
52149
- function handleListAgents(url, _ctx, json3) {
53558
+ function handleListAgents(url, _ctx, json4) {
52150
53559
  const aFieldsParam = url.searchParams.get("fields");
52151
53560
  const aFields = aFieldsParam ? aFieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
52152
53561
  const agents = listAgents();
52153
- return json3(aFields ? agents.map((a) => Object.fromEntries(aFields.map((f) => [f, a[f] ?? null]))) : agents);
53562
+ return json4(aFields ? agents.map((a) => Object.fromEntries(aFields.map((f) => [f, a[f] ?? null]))) : agents);
52154
53563
  }
52155
- async function handleRegisterAgent(req, _ctx, json3) {
53564
+ async function handleRegisterAgent(req, _ctx, json4) {
52156
53565
  try {
52157
- const body = await req.json();
52158
- if (!body.name)
52159
- return json3({ error: "Missing name" }, 400);
52160
- const result = registerAgent({ name: body.name, description: body.description, session_id: body.session_id, working_dir: body.working_dir });
53566
+ const body2 = await req.json();
53567
+ if (!body2.name)
53568
+ return json4({ error: "Missing name" }, 400);
53569
+ const result = registerAgent({ name: body2.name, description: body2.description, session_id: body2.session_id, working_dir: body2.working_dir });
52161
53570
  if (isAgentConflict(result))
52162
- return json3({ error: result.message, conflict: true }, 409);
52163
- return json3(result, 201);
53571
+ return json4({ error: result.message, conflict: true }, 409);
53572
+ return json4(result, 201);
52164
53573
  } catch (e) {
52165
53574
  if (e instanceof InvalidAgentNameError)
52166
- return json3({ error: e.message, suggestions: e.suggestions }, 400);
52167
- return json3({ error: e instanceof Error ? e.message : "Failed to register agent" }, 500);
53575
+ return json4({ error: e.message, suggestions: e.suggestions }, 400);
53576
+ return json4({ error: e instanceof Error ? e.message : "Failed to register agent" }, 500);
52168
53577
  }
52169
53578
  }
52170
- async function handleUpdateAgent(id, req, _ctx, json3) {
53579
+ async function handleUpdateAgent(id, req, _ctx, json4) {
52171
53580
  try {
52172
- const body = await req.json();
52173
- const agent = updateAgent(id, body);
52174
- return json3(agent);
53581
+ const body2 = await req.json();
53582
+ const agent = updateAgent(id, body2);
53583
+ return json4(agent);
52175
53584
  } catch (e) {
52176
53585
  if (e instanceof InvalidAgentNameError)
52177
- return json3({ error: e.message, suggestions: e.suggestions }, 400);
52178
- return json3({ error: e instanceof Error ? e.message : "Failed to update agent" }, 500);
53586
+ return json4({ error: e.message, suggestions: e.suggestions }, 400);
53587
+ return json4({ error: e instanceof Error ? e.message : "Failed to update agent" }, 500);
52179
53588
  }
52180
53589
  }
52181
- function handleDeleteAgent(id, _ctx, json3) {
53590
+ function handleDeleteAgent(id, _ctx, json4) {
52182
53591
  const deleted = deleteAgent(id);
52183
53592
  if (!deleted)
52184
- return json3({ error: "Agent not found" }, 404);
52185
- return json3({ success: true });
53593
+ return json4({ error: "Agent not found" }, 404);
53594
+ return json4({ success: true });
52186
53595
  }
52187
- async function handleBulkDeleteAgents(req, _ctx, json3) {
53596
+ async function handleBulkDeleteAgents(req, _ctx, json4) {
52188
53597
  try {
52189
- const body = await req.json();
52190
- if (!body.ids?.length || body.action !== "delete")
52191
- return json3({ error: "Missing ids or invalid action" }, 400);
53598
+ const body2 = await req.json();
53599
+ if (!body2.ids?.length || body2.action !== "delete")
53600
+ return json4({ error: "Missing ids or invalid action" }, 400);
52192
53601
  let succeeded = 0;
52193
- for (const id of body.ids) {
53602
+ for (const id of body2.ids) {
52194
53603
  if (deleteAgent(id))
52195
53604
  succeeded++;
52196
53605
  }
52197
- return json3({ succeeded, failed: body.ids.length - succeeded });
53606
+ return json4({ succeeded, failed: body2.ids.length - succeeded });
52198
53607
  } catch (e) {
52199
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53608
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52200
53609
  }
52201
53610
  }
52202
- async function handleBulkDeleteProjects(req, _ctx, json3) {
53611
+ async function handleBulkDeleteProjects(req, _ctx, json4) {
52203
53612
  try {
52204
- const body = await req.json();
52205
- if (!body.ids?.length || body.action !== "delete")
52206
- return json3({ error: "Missing ids or invalid action" }, 400);
53613
+ const body2 = await req.json();
53614
+ if (!body2.ids?.length || body2.action !== "delete")
53615
+ return json4({ error: "Missing ids or invalid action" }, 400);
52207
53616
  let succeeded = 0;
52208
- for (const id of body.ids) {
53617
+ for (const id of body2.ids) {
52209
53618
  if (deleteProject(id))
52210
53619
  succeeded++;
52211
53620
  }
52212
- return json3({ succeeded, failed: body.ids.length - succeeded });
53621
+ return json4({ succeeded, failed: body2.ids.length - succeeded });
52213
53622
  } catch (e) {
52214
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53623
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52215
53624
  }
52216
53625
  }
52217
- function handleDoctor(_ctx, json3) {
53626
+ function handleDoctor(_ctx, json4) {
52218
53627
  const { runTodosDoctor: runTodosDoctor2 } = (init_doctor(), __toCommonJS(exports_doctor));
52219
- return json3(runTodosDoctor2({ apply: false }));
53628
+ return json4(runTodosDoctor2({ apply: false }));
52220
53629
  }
52221
- function handleReport(_req, url, _ctx, json3) {
53630
+ function handleReport(_req, url, _ctx, json4) {
52222
53631
  const days = parseInt(url.searchParams.get("days") || "7", 10);
52223
53632
  const projectId = url.searchParams.get("project_id") || undefined;
52224
53633
  const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
@@ -52234,114 +53643,114 @@ function handleReport(_req, url, _ctx, json3) {
52234
53643
  byDay[day] = (byDay[day] || 0) + 1;
52235
53644
  }
52236
53645
  const completionRate = changed.length > 0 ? Math.round(completed.length / changed.length * 100) : 0;
52237
- 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 });
53646
+ 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 });
52238
53647
  }
52239
- function handleActivity(_req, url, _ctx, json3) {
53648
+ function handleActivity(_req, url, _ctx, json4) {
52240
53649
  const limit = parseInt(url.searchParams.get("limit") || "50", 10);
52241
- return json3(getRecentActivity(limit));
53650
+ return json4(getRecentActivity(limit));
52242
53651
  }
52243
- function handleTaskHistory(id, _ctx, json3, url) {
53652
+ function handleTaskHistory(id, _ctx, json4, url) {
52244
53653
  const history = getTaskHistory(id);
52245
53654
  const format = url?.searchParams.get("format") || "compact";
52246
53655
  const limit = parseBoundedLimit(url?.searchParams.get("limit") || null, 20, 500);
52247
- return json3(format === "full" ? history : history.slice(0, limit));
53656
+ return json4(format === "full" ? history : history.slice(0, limit));
52248
53657
  }
52249
- function handleListWebhooks(_ctx, json3) {
52250
- return json3(listWebhooks());
53658
+ function handleListWebhooks(_ctx, json4) {
53659
+ return json4(listWebhooks());
52251
53660
  }
52252
- async function handleCreateWebhook(req, _ctx, json3) {
53661
+ async function handleCreateWebhook(req, _ctx, json4) {
52253
53662
  try {
52254
- const body = await req.json();
52255
- if (!body.url)
52256
- return json3({ error: "Missing url" }, 400);
52257
- return json3(createWebhook(body), 201);
53663
+ const body2 = await req.json();
53664
+ if (!body2.url)
53665
+ return json4({ error: "Missing url" }, 400);
53666
+ return json4(createWebhook(body2), 201);
52258
53667
  } catch (e) {
52259
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53668
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52260
53669
  }
52261
53670
  }
52262
- function handleDeleteWebhook(id, _ctx, json3) {
53671
+ function handleDeleteWebhook(id, _ctx, json4) {
52263
53672
  const deleted = deleteWebhook(id);
52264
- return json3(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
53673
+ return json4(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
52265
53674
  }
52266
- function handleListTemplates(_ctx, json3) {
52267
- return json3(listTemplates());
53675
+ function handleListTemplates(_ctx, json4) {
53676
+ return json4(listTemplates());
52268
53677
  }
52269
- async function handleCreateTemplate(req, _ctx, json3) {
53678
+ async function handleCreateTemplate(req, _ctx, json4) {
52270
53679
  try {
52271
- const body = await req.json();
52272
- if (!body.name || !body.title_pattern)
52273
- return json3({ error: "Missing name or title_pattern" }, 400);
52274
- return json3(createTemplate(body), 201);
53680
+ const body2 = await req.json();
53681
+ if (!body2.name || !body2.title_pattern)
53682
+ return json4({ error: "Missing name or title_pattern" }, 400);
53683
+ return json4(createTemplate(body2), 201);
52275
53684
  } catch (e) {
52276
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53685
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52277
53686
  }
52278
53687
  }
52279
- function handleDeleteTemplate(id, _ctx, json3) {
53688
+ function handleDeleteTemplate(id, _ctx, json4) {
52280
53689
  const deleted = deleteTemplate(id);
52281
- return json3(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
53690
+ return json4(deleted ? { success: true } : { error: "Not found" }, deleted ? 200 : 404);
52282
53691
  }
52283
- function handleListPlans(url, _ctx, json3) {
53692
+ function handleListPlans(url, _ctx, json4) {
52284
53693
  const projectId = url.searchParams.get("project_id") || undefined;
52285
53694
  const plans = listPlans(projectId);
52286
- return json3(plans);
53695
+ return json4(plans);
52287
53696
  }
52288
- async function handleCreatePlan(req, _ctx, json3) {
53697
+ async function handleCreatePlan(req, _ctx, json4) {
52289
53698
  try {
52290
- const body = await req.json();
52291
- if (!body.name)
52292
- return json3({ error: "Missing 'name'" }, 400);
53699
+ const body2 = await req.json();
53700
+ if (!body2.name)
53701
+ return json4({ error: "Missing 'name'" }, 400);
52293
53702
  const plan = createPlan({
52294
- name: body.name,
52295
- slug: body.slug,
52296
- description: body.description,
52297
- project_id: body.project_id,
52298
- task_list_id: body.task_list_id,
52299
- agent_id: body.agent_id,
52300
- status: body.status
52301
- });
52302
- return json3(plan, 201);
53703
+ name: body2.name,
53704
+ slug: body2.slug,
53705
+ description: body2.description,
53706
+ project_id: body2.project_id,
53707
+ task_list_id: body2.task_list_id,
53708
+ agent_id: body2.agent_id,
53709
+ status: body2.status
53710
+ });
53711
+ return json4(plan, 201);
52303
53712
  } catch (e) {
52304
- return json3({ error: e instanceof Error ? e.message : "Failed to create plan" }, 500);
53713
+ return json4({ error: e instanceof Error ? e.message : "Failed to create plan" }, 500);
52305
53714
  }
52306
53715
  }
52307
- async function handleBulkDeletePlans(req, _ctx, json3) {
53716
+ async function handleBulkDeletePlans(req, _ctx, json4) {
52308
53717
  try {
52309
- const body = await req.json();
52310
- if (!body.ids?.length || body.action !== "delete")
52311
- return json3({ error: "Missing ids or invalid action" }, 400);
53718
+ const body2 = await req.json();
53719
+ if (!body2.ids?.length || body2.action !== "delete")
53720
+ return json4({ error: "Missing ids or invalid action" }, 400);
52312
53721
  let succeeded = 0;
52313
- for (const id of body.ids) {
53722
+ for (const id of body2.ids) {
52314
53723
  if (deletePlan(id))
52315
53724
  succeeded++;
52316
53725
  }
52317
- return json3({ succeeded, failed: body.ids.length - succeeded });
53726
+ return json4({ succeeded, failed: body2.ids.length - succeeded });
52318
53727
  } catch (e) {
52319
- return json3({ error: e instanceof Error ? e.message : "Failed" }, 500);
53728
+ return json4({ error: e instanceof Error ? e.message : "Failed" }, 500);
52320
53729
  }
52321
53730
  }
52322
- function handleGetPlan(id, _ctx, json3, taskToSummary2) {
53731
+ function handleGetPlan(id, _ctx, json4, taskToSummary2) {
52323
53732
  const plan = getPlan(id);
52324
53733
  if (!plan)
52325
- return json3({ error: "Plan not found" }, 404);
53734
+ return json4({ error: "Plan not found" }, 404);
52326
53735
  const tasks = listTasks({ plan_id: id });
52327
- return json3({ ...plan, tasks: tasks.map((t) => taskToSummary2(t)) });
53736
+ return json4({ ...plan, tasks: tasks.map((t) => taskToSummary2(t)) });
52328
53737
  }
52329
- async function handleUpdatePlan(id, req, _ctx, json3) {
53738
+ async function handleUpdatePlan(id, req, _ctx, json4) {
52330
53739
  try {
52331
- const body = await req.json();
52332
- const plan = updatePlan(id, body);
52333
- return json3(plan);
53740
+ const body2 = await req.json();
53741
+ const plan = updatePlan(id, body2);
53742
+ return json4(plan);
52334
53743
  } catch (e) {
52335
- return json3({ error: e instanceof Error ? e.message : "Failed to update plan" }, 500);
53744
+ return json4({ error: e instanceof Error ? e.message : "Failed to update plan" }, 500);
52336
53745
  }
52337
53746
  }
52338
- function handleDeletePlan(id, _ctx, json3) {
53747
+ function handleDeletePlan(id, _ctx, json4) {
52339
53748
  const deleted = deletePlan(id);
52340
53749
  if (!deleted)
52341
- return json3({ error: "Plan not found" }, 404);
52342
- return json3({ success: true });
53750
+ return json4({ error: "Plan not found" }, 404);
53751
+ return json4({ success: true });
52343
53752
  }
52344
- function handleStaticFiles(path, method, ctx, json3, serveStaticFile2) {
53753
+ function handleStaticFiles(path, method, ctx, json4, serveStaticFile2) {
52345
53754
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
52346
53755
  return null;
52347
53756
  if (path !== "/") {
@@ -52349,7 +53758,7 @@ function handleStaticFiles(path, method, ctx, json3, serveStaticFile2) {
52349
53758
  const resolvedFile = resolve16(filePath);
52350
53759
  const resolvedBase = resolve16(ctx.dashboardDir);
52351
53760
  if (!resolvedFile.startsWith(resolvedBase + sep3) && resolvedFile !== resolvedBase) {
52352
- return json3({ error: "Forbidden" }, 403);
53761
+ return json4({ error: "Forbidden" }, 403);
52353
53762
  }
52354
53763
  const res2 = serveStaticFile2(filePath);
52355
53764
  if (res2)
@@ -52398,6 +53807,9 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
52398
53807
  schemas: {
52399
53808
  Task: taskSchema,
52400
53809
  Project: projectSchema,
53810
+ TaskManifestBindingLookupRequest: taskManifestBindingLookupRequestSchema,
53811
+ TaskManifestBindingLookupResult: taskManifestBindingLookupResultSchema,
53812
+ TaskManifestBindingLookupResponse: taskManifestBindingLookupResponseSchema,
52401
53813
  TaskList: taskListSchema,
52402
53814
  ProjectTaskListEnsureReceipt: projectTaskListEnsureReceiptSchema,
52403
53815
  ProjectTaskListEnsureResult: projectTaskListEnsureResultSchema,
@@ -53373,6 +54785,32 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
53373
54785
  },
53374
54786
  security: [{ apiKey: [] }],
53375
54787
  paths: {
54788
+ "/v1/task-manifest/bindings/lookup": {
54789
+ post: {
54790
+ operationId: "lookupTaskManifestBinding",
54791
+ summary: "Recover one exact task-manifest apply receipt from its managed plan id",
54792
+ requestBody: {
54793
+ required: true,
54794
+ content: {
54795
+ "application/json": {
54796
+ schema: { $ref: "#/components/schemas/TaskManifestBindingLookupRequest" }
54797
+ }
54798
+ }
54799
+ },
54800
+ responses: {
54801
+ "200": {
54802
+ content: {
54803
+ "application/json": {
54804
+ schema: { $ref: "#/components/schemas/TaskManifestBindingLookupResponse" }
54805
+ }
54806
+ }
54807
+ },
54808
+ "400": { content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } } },
54809
+ "404": { content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } } },
54810
+ "409": { content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } } }
54811
+ }
54812
+ }
54813
+ },
53376
54814
  "/v1/tasks": {
53377
54815
  get: {
53378
54816
  operationId: "listTasks",
@@ -54103,7 +55541,7 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
54103
55541
  }
54104
55542
  };
54105
55543
  }
54106
- var taskSchema, projectSchema, taskListSchema, projectTaskListEnsureReceiptSchema, projectTaskListEnsureResultSchema, projectTaskListRollbackResultSchema, taskCommentSchema, taskGitRefSchema, planSchema, planProjectLinkReceiptSchema, planProjectLinkResultSchema, planProjectLinkRollbackResultSchema, templateTaskSchema, templateSchema, templateVariableSchema, createTemplateTaskInputSchema;
55544
+ var taskSchema, projectSchema, taskManifestBindingLookupRequestSchema, taskManifestBindingLookupResultSchema, taskManifestBindingLookupResponseSchema, taskListSchema, projectTaskListEnsureReceiptSchema, projectTaskListEnsureResultSchema, projectTaskListRollbackResultSchema, taskCommentSchema, taskGitRefSchema, planSchema, planProjectLinkReceiptSchema, planProjectLinkResultSchema, planProjectLinkRollbackResultSchema, templateTaskSchema, templateSchema, templateVariableSchema, createTemplateTaskInputSchema;
54107
55545
  var init_openapi = __esm(() => {
54108
55546
  init_package_version();
54109
55547
  init_types();
@@ -54139,6 +55577,51 @@ var init_openapi = __esm(() => {
54139
55577
  updated_at: { type: "string" }
54140
55578
  }
54141
55579
  };
55580
+ taskManifestBindingLookupRequestSchema = {
55581
+ type: "object",
55582
+ additionalProperties: false,
55583
+ required: ["authority", "route", "schema_version", "tenant_id", "plan_id", "max_items"],
55584
+ properties: {
55585
+ authority: { type: "string", enum: ["todos"] },
55586
+ route: { type: "string", enum: ["todos.task-manifest.v1"] },
55587
+ schema_version: { type: "integer", enum: [1] },
55588
+ tenant_id: { type: "string", minLength: 1, maxLength: 200 },
55589
+ plan_id: { type: "string", format: "uuid" },
55590
+ max_items: { type: "integer", enum: [1] }
55591
+ }
55592
+ };
55593
+ taskManifestBindingLookupResultSchema = {
55594
+ type: "object",
55595
+ additionalProperties: false,
55596
+ required: [
55597
+ "authority",
55598
+ "route",
55599
+ "schema_version",
55600
+ "tenant_id",
55601
+ "plan_id",
55602
+ "apply_receipt_id",
55603
+ "binding_version",
55604
+ "state"
55605
+ ],
55606
+ properties: {
55607
+ authority: { type: "string", enum: ["todos"] },
55608
+ route: { type: "string", enum: ["todos.task-manifest.v1"] },
55609
+ schema_version: { type: "integer", enum: [1] },
55610
+ tenant_id: { type: "string" },
55611
+ plan_id: { type: "string", format: "uuid" },
55612
+ apply_receipt_id: { type: "string", format: "uuid" },
55613
+ binding_version: { type: "integer", minimum: 1 },
55614
+ state: { type: "string", enum: ["applied", "compensated"] }
55615
+ }
55616
+ };
55617
+ taskManifestBindingLookupResponseSchema = {
55618
+ type: "object",
55619
+ additionalProperties: false,
55620
+ required: ["result"],
55621
+ properties: {
55622
+ result: { $ref: "#/components/schemas/TaskManifestBindingLookupResult" }
55623
+ }
55624
+ };
54142
55625
  taskListSchema = {
54143
55626
  type: "object",
54144
55627
  properties: {
@@ -54424,8 +55907,8 @@ var exports_pr_groups = {};
54424
55907
  __export(exports_pr_groups, {
54425
55908
  handlePrGroupHttpRequest: () => handlePrGroupHttpRequest
54426
55909
  });
54427
- function json3(body, status = 200) {
54428
- return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS2 });
55910
+ function json4(body2, status2 = 200) {
55911
+ return new Response(JSON.stringify(body2), { status: status2, headers: JSON_HEADERS3 });
54429
55912
  }
54430
55913
  function errorStatus2(error) {
54431
55914
  switch (error.code) {
@@ -54459,26 +55942,26 @@ async function handlePrGroupHttpRequest(req, url, ledger, basePath, principal) {
54459
55942
  const method = req.method.toUpperCase();
54460
55943
  try {
54461
55944
  if (!groupId && method === "POST" && action === undefined) {
54462
- return json3({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
55945
+ return json4({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
54463
55946
  }
54464
55947
  if (groupId === "admit" && !action) {
54465
55948
  if (method !== "POST")
54466
- return json3({ error: "method not allowed" }, 405);
54467
- const body = await readJson2(req);
54468
- if (!body)
54469
- return json3({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
54470
- return json3(await ledger.admit(body), 201);
55949
+ return json4({ error: "method not allowed" }, 405);
55950
+ const body2 = await readJson2(req);
55951
+ if (!body2)
55952
+ return json4({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
55953
+ return json4(await ledger.admit(body2), 201);
54471
55954
  }
54472
55955
  if (!groupId)
54473
- return json3({ error: "PR group id is required", code: "PR_GROUP_INVALID_INPUT" }, 400);
55956
+ return json4({ error: "PR group id is required", code: "PR_GROUP_INVALID_INPUT" }, 400);
54474
55957
  if (!action && method === "GET") {
54475
- return json3({ view: await ledger.get(groupId) });
55958
+ return json4({ view: await ledger.get(groupId) });
54476
55959
  }
54477
55960
  if (action === "events") {
54478
55961
  if (method === "GET") {
54479
55962
  const limit = url.searchParams.has("limit") ? Number(url.searchParams.get("limit")) : undefined;
54480
55963
  const afterSequence = url.searchParams.has("after_sequence") ? Number(url.searchParams.get("after_sequence")) : undefined;
54481
- return json3({
55964
+ return json4({
54482
55965
  history: await ledger.events(groupId, {
54483
55966
  ...limit !== undefined ? { limit } : {},
54484
55967
  ...afterSequence !== undefined ? { after_sequence: afterSequence } : {}
@@ -54486,54 +55969,54 @@ async function handlePrGroupHttpRequest(req, url, ledger, basePath, principal) {
54486
55969
  });
54487
55970
  }
54488
55971
  if (method === "POST") {
54489
- const body = await readJson2(req);
54490
- if (!body)
54491
- return json3({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
54492
- return json3(await ledger.append({
54493
- ...body,
55972
+ const body2 = await readJson2(req);
55973
+ if (!body2)
55974
+ return json4({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
55975
+ return json4(await ledger.append({
55976
+ ...body2,
54494
55977
  group_id: groupId,
54495
55978
  authenticated_actor_id: principal?.actor_id ?? undefined,
54496
55979
  authenticated_actor_run_id: principal?.actor_run_id ?? undefined
54497
55980
  }), 201);
54498
55981
  }
54499
- return json3({ error: "method not allowed" }, 405);
55982
+ return json4({ error: "method not allowed" }, 405);
54500
55983
  }
54501
55984
  if (action === "recover") {
54502
55985
  if (method !== "POST")
54503
- return json3({ error: "method not allowed" }, 405);
54504
- const body = await readJson2(req);
54505
- if (!body)
54506
- return json3({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
54507
- return json3(await ledger.recover({
54508
- ...body,
55986
+ return json4({ error: "method not allowed" }, 405);
55987
+ const body2 = await readJson2(req);
55988
+ if (!body2)
55989
+ return json4({ error: "invalid JSON body", code: "PR_GROUP_INVALID_INPUT" }, 400);
55990
+ return json4(await ledger.recover({
55991
+ ...body2,
54509
55992
  group_id: groupId
54510
55993
  }), 201);
54511
55994
  }
54512
- return json3({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
55995
+ return json4({ error: "unknown PR-group route", code: "PR_GROUP_NOT_FOUND" }, 404);
54513
55996
  } catch (cause) {
54514
55997
  if (cause instanceof PrGroupLedgerError) {
54515
- return json3({
55998
+ return json4({
54516
55999
  error: cause.message,
54517
56000
  code: cause.code,
54518
56001
  details: cause.details,
54519
56002
  authoritative: true
54520
56003
  }, errorStatus2(cause));
54521
56004
  }
54522
- return json3({
56005
+ return json4({
54523
56006
  error: cause instanceof Error ? cause.message : "internal PR-group error",
54524
56007
  code: "PR_GROUP_ATOMICITY_UNAVAILABLE"
54525
56008
  }, 500);
54526
56009
  }
54527
56010
  }
54528
- var JSON_HEADERS2;
56011
+ var JSON_HEADERS3;
54529
56012
  var init_pr_groups = __esm(() => {
54530
56013
  init_types3();
54531
- JSON_HEADERS2 = { "Content-Type": "application/json" };
56014
+ JSON_HEADERS3 = { "Content-Type": "application/json" };
54532
56015
  });
54533
56016
 
54534
56017
  // src/lib/comment-cursor.ts
54535
- function encodeCommentCursor(comment) {
54536
- return Buffer.from(JSON.stringify({ created_at: comment.created_at, id: comment.id }), "utf8").toString("base64url");
56018
+ function encodeCommentCursor(comment2) {
56019
+ return Buffer.from(JSON.stringify({ created_at: comment2.created_at, id: comment2.id }), "utf8").toString("base64url");
54537
56020
  }
54538
56021
  function decodeCommentCursor(value) {
54539
56022
  if (value.length > MAX_COMMENT_CURSOR_LENGTH)
@@ -54555,26 +56038,26 @@ function decodeCommentCursor(value) {
54555
56038
  var MAX_COMMENT_CURSOR_LENGTH = 1024;
54556
56039
 
54557
56040
  // src/lib/project-task-list-ensure.ts
54558
- import { createHash as createHash16 } from "crypto";
54559
- function canonicalJson(value) {
56041
+ import { createHash as createHash17 } from "crypto";
56042
+ function canonicalJson2(value) {
54560
56043
  if (value === null || typeof value !== "object")
54561
56044
  return JSON.stringify(value);
54562
56045
  if (Array.isArray(value))
54563
- return `[${value.map(canonicalJson).join(",")}]`;
54564
- return `{${Object.entries(value).filter(([, item]) => item !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
56046
+ return `[${value.map(canonicalJson2).join(",")}]`;
56047
+ return `{${Object.entries(value).filter(([, item]) => item !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key2, item]) => `${JSON.stringify(key2)}:${canonicalJson2(item)}`).join(",")}}`;
54565
56048
  }
54566
56049
  function digest(value) {
54567
- return createHash16("sha256").update(canonicalJson(value)).digest("hex");
56050
+ return createHash17("sha256").update(canonicalJson2(value)).digest("hex");
54568
56051
  }
54569
56052
  function deriveIdempotencyKey(projectId, slug) {
54570
56053
  return `ptlk_${digest({ project_id: projectId, slug }).slice(0, 48)}`;
54571
56054
  }
54572
56055
  function normalizeIdempotencyKey(value, projectId, slug) {
54573
- const key = value?.trim() || deriveIdempotencyKey(projectId, slug);
54574
- if (key.length < 8 || key.length > 128 || !/^[A-Za-z0-9._:-]+$/.test(key)) {
56056
+ const key2 = value?.trim() || deriveIdempotencyKey(projectId, slug);
56057
+ if (key2.length < 8 || key2.length > 128 || !/^[A-Za-z0-9._:-]+$/.test(key2)) {
54575
56058
  throw new ProjectTaskListEnsureError("PROJECT_TASK_LIST_IDEMPOTENCY_KEY_INVALID", "idempotency_key must be 8-128 ASCII letters, digits, dots, underscores, colons, or hyphens");
54576
56059
  }
54577
- return key;
56060
+ return key2;
54578
56061
  }
54579
56062
  function receiptId2(projectId, slug, idempotencyKey) {
54580
56063
  return `ptlr_${digest({ project_id: projectId, slug, idempotency_key: idempotencyKey }).slice(0, 48)}`;
@@ -54837,7 +56320,7 @@ async function exactPlanProjectLinkState(store, planId, projectId) {
54837
56320
  }
54838
56321
  async function planPlanProjectLink(store, planId, projectId) {
54839
56322
  const state = await exactPlanProjectLinkState(store, planId, projectId);
54840
- const alreadyLinked = state.plan.project_id === state.project.id && state.tasks.every((task2) => task2.project_id === state.project.id);
56323
+ const alreadyLinked = state.plan.project_id === state.project.id && state.tasks.every((task3) => task3.project_id === state.project.id);
54841
56324
  return {
54842
56325
  mode: "plan",
54843
56326
  action: alreadyLinked ? "already_linked" : "would_link",
@@ -54849,14 +56332,14 @@ async function applyPlanProjectLink(store, planId, projectId, options) {
54849
56332
  if (!store.planProjectLinks) {
54850
56333
  throw new PlanProjectLinkError("PLAN_PROJECT_LINK_UNSUPPORTED", "This storage backend cannot atomically link an existing plan and its tasks", { storage_kind: store.kind });
54851
56334
  }
54852
- const key = normalizePlanProjectLinkIdempotencyKey(options.idempotency_key);
56335
+ const key2 = normalizePlanProjectLinkIdempotencyKey(options.idempotency_key);
54853
56336
  return store.planProjectLinks.apply({
54854
56337
  plan_id: planId,
54855
56338
  project_id: projectId,
54856
56339
  expected_plan_revision: options.expected_plan_revision,
54857
56340
  expected_project_revision: options.expected_project_revision,
54858
- idempotency_key: key,
54859
- receipt_id: planProjectLinkReceiptId(key),
56341
+ idempotency_key: key2,
56342
+ receipt_id: planProjectLinkReceiptId(key2),
54860
56343
  created_at: new Date().toISOString()
54861
56344
  });
54862
56345
  }
@@ -54885,11 +56368,11 @@ __export(exports_v1, {
54885
56368
  handleV1Request: () => handleV1Request,
54886
56369
  countSnapshotRecords: () => countSnapshotRecords
54887
56370
  });
54888
- function json4(body, status = 200) {
54889
- return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS3 });
56371
+ function json5(body2, status2 = 200) {
56372
+ return new Response(JSON.stringify(body2), { status: status2, headers: JSON_HEADERS4 });
54890
56373
  }
54891
- function error(status, message, extra) {
54892
- return json4({ error: message, ...extra ?? {} }, status);
56374
+ function error(status2, message, extra) {
56375
+ return json5({ error: message, ...extra ?? {} }, status2);
54893
56376
  }
54894
56377
  function enumQueryParam(url, name, vocabulary) {
54895
56378
  const raw = url.searchParams.get(name);
@@ -54918,112 +56401,112 @@ function parseSinceCursor(raw) {
54918
56401
  function validateTaskCompletion(value) {
54919
56402
  if (!value || typeof value !== "object" || Array.isArray(value))
54920
56403
  return { ok: false, message: "completion body must be an object" };
54921
- const body = value;
56404
+ const body2 = value;
54922
56405
  const allowed = new Set(["agent_id", "attachment_ids", "files_changed", "test_results", "commit_hash", "notes", "confidence"]);
54923
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56406
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
54924
56407
  if (unknown)
54925
56408
  return { ok: false, message: `unknown completion field: ${unknown}` };
54926
- if (body.agent_id !== undefined && (typeof body.agent_id !== "string" || !body.agent_id.trim())) {
56409
+ if (body2.agent_id !== undefined && (typeof body2.agent_id !== "string" || !body2.agent_id.trim())) {
54927
56410
  return { ok: false, message: "agent_id must be a non-empty string" };
54928
56411
  }
54929
56412
  for (const field of ["attachment_ids", "files_changed"]) {
54930
- const value2 = body[field];
56413
+ const value2 = body2[field];
54931
56414
  if (value2 !== undefined && (!Array.isArray(value2) || value2.some((item) => typeof item !== "string" || !item.trim()))) {
54932
56415
  return { ok: false, message: `${field} must be an array of non-empty strings` };
54933
56416
  }
54934
56417
  }
54935
56418
  for (const field of ["test_results", "commit_hash", "notes"]) {
54936
- if (body[field] !== undefined && typeof body[field] !== "string") {
56419
+ if (body2[field] !== undefined && typeof body2[field] !== "string") {
54937
56420
  return { ok: false, message: `${field} must be a string` };
54938
56421
  }
54939
56422
  }
54940
- if (body.confidence !== undefined && (typeof body.confidence !== "number" || !Number.isFinite(body.confidence) || body.confidence < 0 || body.confidence > 1)) {
56423
+ if (body2.confidence !== undefined && (typeof body2.confidence !== "number" || !Number.isFinite(body2.confidence) || body2.confidence < 0 || body2.confidence > 1)) {
54941
56424
  return { ok: false, message: "confidence must be a number between 0 and 1" };
54942
56425
  }
54943
56426
  return {
54944
56427
  ok: true,
54945
- ...typeof body.agent_id === "string" ? { agentId: body.agent_id } : {},
56428
+ ...typeof body2.agent_id === "string" ? { agentId: body2.agent_id } : {},
54946
56429
  options: {
54947
- ...Array.isArray(body.attachment_ids) ? { attachment_ids: body.attachment_ids } : {},
54948
- ...Array.isArray(body.files_changed) ? { files_changed: body.files_changed } : {},
54949
- ...typeof body.test_results === "string" ? { test_results: body.test_results } : {},
54950
- ...typeof body.commit_hash === "string" ? { commit_hash: body.commit_hash } : {},
54951
- ...typeof body.notes === "string" ? { notes: body.notes } : {},
54952
- ...typeof body.confidence === "number" ? { confidence: body.confidence } : {}
56430
+ ...Array.isArray(body2.attachment_ids) ? { attachment_ids: body2.attachment_ids } : {},
56431
+ ...Array.isArray(body2.files_changed) ? { files_changed: body2.files_changed } : {},
56432
+ ...typeof body2.test_results === "string" ? { test_results: body2.test_results } : {},
56433
+ ...typeof body2.commit_hash === "string" ? { commit_hash: body2.commit_hash } : {},
56434
+ ...typeof body2.notes === "string" ? { notes: body2.notes } : {},
56435
+ ...typeof body2.confidence === "number" ? { confidence: body2.confidence } : {}
54953
56436
  }
54954
56437
  };
54955
56438
  }
54956
56439
  function validateProjectPatch(value) {
54957
56440
  if (!value || typeof value !== "object" || Array.isArray(value))
54958
56441
  return { ok: false, message: "project patch must be an object" };
54959
- const body = value;
56442
+ const body2 = value;
54960
56443
  const allowed = new Set(["name", "path", "description"]);
54961
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56444
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
54962
56445
  if (unknown)
54963
56446
  return { ok: false, message: `unknown project field: ${unknown}` };
54964
- if (Object.keys(body).length === 0)
56447
+ if (Object.keys(body2).length === 0)
54965
56448
  return { ok: false, message: "project patch must not be empty" };
54966
- if (body["name"] !== undefined && (typeof body["name"] !== "string" || !body["name"].trim()))
56449
+ if (body2["name"] !== undefined && (typeof body2["name"] !== "string" || !body2["name"].trim()))
54967
56450
  return { ok: false, message: "name must be a non-empty string" };
54968
- if (body["path"] !== undefined && (typeof body["path"] !== "string" || !body["path"].trim()))
56451
+ if (body2["path"] !== undefined && (typeof body2["path"] !== "string" || !body2["path"].trim()))
54969
56452
  return { ok: false, message: "path must be a non-empty string" };
54970
- if (body["description"] !== undefined && body["description"] !== null && typeof body["description"] !== "string")
56453
+ if (body2["description"] !== undefined && body2["description"] !== null && typeof body2["description"] !== "string")
54971
56454
  return { ok: false, message: "description must be a string or null" };
54972
- return { ok: true, patch: body };
56455
+ return { ok: true, patch: body2 };
54973
56456
  }
54974
56457
  function validateProjectCreate(value) {
54975
56458
  if (!value || typeof value !== "object" || Array.isArray(value))
54976
56459
  return { ok: false, message: "project body must be an object" };
54977
- const body = value;
56460
+ const body2 = value;
54978
56461
  const allowed = new Set(["name", "path", "description", "task_list_id", "task_prefix"]);
54979
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56462
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
54980
56463
  if (unknown)
54981
56464
  return { ok: false, message: `unknown project field: ${unknown}` };
54982
- if (typeof body["name"] !== "string" || !body["name"].trim())
56465
+ if (typeof body2["name"] !== "string" || !body2["name"].trim())
54983
56466
  return { ok: false, message: "name must be a non-empty string" };
54984
- if (!normalizeSlug(body["name"]))
56467
+ if (!normalizeSlug(body2["name"]))
54985
56468
  return { ok: false, message: "name must produce a non-empty canonical slug" };
54986
- if (typeof body["path"] !== "string" || !body["path"].trim())
56469
+ if (typeof body2["path"] !== "string" || !body2["path"].trim())
54987
56470
  return { ok: false, message: "path must be a non-empty string" };
54988
- if (body["description"] !== undefined && typeof body["description"] !== "string")
56471
+ if (body2["description"] !== undefined && typeof body2["description"] !== "string")
54989
56472
  return { ok: false, message: "description must be a string" };
54990
- if (body["task_list_id"] !== undefined && !isCanonicalSlug(body["task_list_id"])) {
56473
+ if (body2["task_list_id"] !== undefined && !isCanonicalSlug(body2["task_list_id"])) {
54991
56474
  return { ok: false, message: "task_list_id must be non-empty canonical kebab-case" };
54992
56475
  }
54993
- if (body["task_prefix"] !== undefined && (typeof body["task_prefix"] !== "string" || !body["task_prefix"].trim())) {
56476
+ if (body2["task_prefix"] !== undefined && (typeof body2["task_prefix"] !== "string" || !body2["task_prefix"].trim())) {
54994
56477
  return { ok: false, message: "task_prefix must be a non-empty string" };
54995
56478
  }
54996
- return { ok: true, input: body };
56479
+ return { ok: true, input: body2 };
54997
56480
  }
54998
56481
  function validatePlanCreate(value) {
54999
56482
  if (!value || typeof value !== "object" || Array.isArray(value))
55000
56483
  return { ok: false, message: "plan body must be an object" };
55001
- const body = value;
56484
+ const body2 = value;
55002
56485
  const allowed = new Set(["title", "name", "slug", "description", "project_id", "task_list_id", "agent_id", "status"]);
55003
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56486
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
55004
56487
  if (unknown)
55005
56488
  return { ok: false, message: `unknown plan field: ${unknown}` };
55006
- if (body.name !== undefined && (typeof body.name !== "string" || !body.name.trim()))
56489
+ if (body2.name !== undefined && (typeof body2.name !== "string" || !body2.name.trim()))
55007
56490
  return { ok: false, message: "name must be a non-empty string" };
55008
- if (body.title !== undefined && (typeof body.title !== "string" || !body.title.trim()))
56491
+ if (body2.title !== undefined && (typeof body2.title !== "string" || !body2.title.trim()))
55009
56492
  return { ok: false, message: "title must be a non-empty string" };
55010
- if (typeof body.name === "string" && typeof body.title === "string" && body.name !== body.title) {
56493
+ if (typeof body2.name === "string" && typeof body2.title === "string" && body2.name !== body2.title) {
55011
56494
  return { ok: false, message: "name and title must match when both are provided" };
55012
56495
  }
55013
- const name = body.name ?? body.title;
56496
+ const name = body2.name ?? body2.title;
55014
56497
  if (!name)
55015
56498
  return { ok: false, message: "name is required" };
55016
56499
  for (const field of ["slug", "project_id", "task_list_id", "agent_id"]) {
55017
- if (body[field] !== undefined && (typeof body[field] !== "string" || !body[field].trim())) {
56500
+ if (body2[field] !== undefined && (typeof body2[field] !== "string" || !body2[field].trim())) {
55018
56501
  return { ok: false, message: `${field} must be a non-empty string` };
55019
56502
  }
55020
56503
  }
55021
- const slug = typeof body.slug === "string" ? normalizeSlug(body.slug) : undefined;
55022
- if (body.slug !== undefined && !slug)
56504
+ const slug = typeof body2.slug === "string" ? normalizeSlug(body2.slug) : undefined;
56505
+ if (body2.slug !== undefined && !slug)
55023
56506
  return { ok: false, message: "slug must produce a non-empty canonical slug" };
55024
- if (body.description !== undefined && typeof body.description !== "string")
56507
+ if (body2.description !== undefined && typeof body2.description !== "string")
55025
56508
  return { ok: false, message: "description must be a string" };
55026
- if (body.status !== undefined && (typeof body.status !== "string" || !["active", "completed", "archived"].includes(body.status))) {
56509
+ if (body2.status !== undefined && (typeof body2.status !== "string" || !["active", "completed", "archived"].includes(body2.status))) {
55027
56510
  return { ok: false, message: "status must be active, completed, or archived" };
55028
56511
  }
55029
56512
  return {
@@ -55031,102 +56514,102 @@ function validatePlanCreate(value) {
55031
56514
  input: {
55032
56515
  name,
55033
56516
  ...slug ? { slug } : {},
55034
- ...typeof body.description === "string" ? { description: body.description } : {},
55035
- ...typeof body.project_id === "string" ? { project_id: body.project_id } : {},
55036
- ...typeof body.task_list_id === "string" ? { task_list_id: body.task_list_id } : {},
55037
- ...typeof body.agent_id === "string" ? { agent_id: body.agent_id } : {},
55038
- ...typeof body.status === "string" ? { status: body.status } : {}
56517
+ ...typeof body2.description === "string" ? { description: body2.description } : {},
56518
+ ...typeof body2.project_id === "string" ? { project_id: body2.project_id } : {},
56519
+ ...typeof body2.task_list_id === "string" ? { task_list_id: body2.task_list_id } : {},
56520
+ ...typeof body2.agent_id === "string" ? { agent_id: body2.agent_id } : {},
56521
+ ...typeof body2.status === "string" ? { status: body2.status } : {}
55039
56522
  }
55040
56523
  };
55041
56524
  }
55042
56525
  function validateTemplateTask(value) {
55043
56526
  if (!value || typeof value !== "object" || Array.isArray(value))
55044
56527
  return null;
55045
- const body = value;
56528
+ const body2 = value;
55046
56529
  const allowed = new Set(["position", "title_pattern", "description", "priority", "tags", "task_type", "condition", "include_template_id", "depends_on", "depends_on_positions", "metadata"]);
55047
- if (Object.keys(body).some((key) => !allowed.has(key)))
56530
+ if (Object.keys(body2).some((key2) => !allowed.has(key2)))
55048
56531
  return null;
55049
- if (typeof body.title_pattern !== "string" || !body.title_pattern.trim())
56532
+ if (typeof body2.title_pattern !== "string" || !body2.title_pattern.trim())
55050
56533
  return null;
55051
- if (body.position !== undefined && (typeof body.position !== "number" || !Number.isSafeInteger(body.position) || body.position < 0))
56534
+ if (body2.position !== undefined && (typeof body2.position !== "number" || !Number.isSafeInteger(body2.position) || body2.position < 0))
55052
56535
  return null;
55053
- if (body.description !== undefined && body.description !== null && typeof body.description !== "string")
56536
+ if (body2.description !== undefined && body2.description !== null && typeof body2.description !== "string")
55054
56537
  return null;
55055
- if (body.priority !== undefined && (typeof body.priority !== "string" || !["low", "medium", "high", "critical"].includes(body.priority)))
56538
+ if (body2.priority !== undefined && (typeof body2.priority !== "string" || !["low", "medium", "high", "critical"].includes(body2.priority)))
55056
56539
  return null;
55057
- if (body.tags !== undefined && (!Array.isArray(body.tags) || body.tags.some((tag) => typeof tag !== "string" || !tag.trim())))
56540
+ if (body2.tags !== undefined && (!Array.isArray(body2.tags) || body2.tags.some((tag) => typeof tag !== "string" || !tag.trim())))
55058
56541
  return null;
55059
56542
  for (const field of ["task_type", "condition", "include_template_id"]) {
55060
- if (body[field] !== undefined && body[field] !== null && (typeof body[field] !== "string" || !body[field].trim()))
56543
+ if (body2[field] !== undefined && body2[field] !== null && (typeof body2[field] !== "string" || !body2[field].trim()))
55061
56544
  return null;
55062
56545
  }
55063
- if (body.depends_on !== undefined && body.depends_on_positions !== undefined)
56546
+ if (body2.depends_on !== undefined && body2.depends_on_positions !== undefined)
55064
56547
  return null;
55065
- const dependencies = body.depends_on ?? body.depends_on_positions;
56548
+ const dependencies = body2.depends_on ?? body2.depends_on_positions;
55066
56549
  if (dependencies !== undefined && (!Array.isArray(dependencies) || dependencies.some((position) => !Number.isSafeInteger(position) || position < 0)))
55067
56550
  return null;
55068
- if (body.metadata !== undefined && (!body.metadata || typeof body.metadata !== "object" || Array.isArray(body.metadata)))
56551
+ if (body2.metadata !== undefined && (!body2.metadata || typeof body2.metadata !== "object" || Array.isArray(body2.metadata)))
55069
56552
  return null;
55070
56553
  return {
55071
- title_pattern: body.title_pattern,
55072
- ...typeof body.description === "string" ? { description: body.description } : {},
55073
- ...typeof body.priority === "string" ? { priority: body.priority } : {},
55074
- ...Array.isArray(body.tags) ? { tags: body.tags } : {},
55075
- ...typeof body.task_type === "string" ? { task_type: body.task_type } : {},
55076
- ...typeof body.condition === "string" ? { condition: body.condition } : {},
55077
- ...typeof body.include_template_id === "string" ? { include_template_id: body.include_template_id } : {},
56554
+ title_pattern: body2.title_pattern,
56555
+ ...typeof body2.description === "string" ? { description: body2.description } : {},
56556
+ ...typeof body2.priority === "string" ? { priority: body2.priority } : {},
56557
+ ...Array.isArray(body2.tags) ? { tags: body2.tags } : {},
56558
+ ...typeof body2.task_type === "string" ? { task_type: body2.task_type } : {},
56559
+ ...typeof body2.condition === "string" ? { condition: body2.condition } : {},
56560
+ ...typeof body2.include_template_id === "string" ? { include_template_id: body2.include_template_id } : {},
55078
56561
  ...Array.isArray(dependencies) ? { depends_on: dependencies } : {},
55079
- ...body.metadata && typeof body.metadata === "object" && !Array.isArray(body.metadata) ? { metadata: body.metadata } : {}
56562
+ ...body2.metadata && typeof body2.metadata === "object" && !Array.isArray(body2.metadata) ? { metadata: body2.metadata } : {}
55080
56563
  };
55081
56564
  }
55082
56565
  function validateTemplateCreate(value) {
55083
56566
  if (!value || typeof value !== "object" || Array.isArray(value))
55084
56567
  return { ok: false, message: "template body must be an object" };
55085
- const body = value;
56568
+ const body2 = value;
55086
56569
  const allowed = new Set(["name", "title_pattern", "description", "priority", "tags", "variables", "project_id", "plan_id", "metadata", "tasks"]);
55087
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56570
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
55088
56571
  if (unknown)
55089
56572
  return { ok: false, message: `unknown template field: ${unknown}` };
55090
- if (typeof body.name !== "string" || !body.name.trim())
56573
+ if (typeof body2.name !== "string" || !body2.name.trim())
55091
56574
  return { ok: false, message: "name must be a non-empty string" };
55092
- if (typeof body.title_pattern !== "string" || !body.title_pattern.trim())
56575
+ if (typeof body2.title_pattern !== "string" || !body2.title_pattern.trim())
55093
56576
  return { ok: false, message: "title_pattern must be a non-empty string" };
55094
- if (body.description !== undefined && body.description !== null && typeof body.description !== "string")
56577
+ if (body2.description !== undefined && body2.description !== null && typeof body2.description !== "string")
55095
56578
  return { ok: false, message: "description must be a string or null" };
55096
- if (body.priority !== undefined && (typeof body.priority !== "string" || !["low", "medium", "high", "critical"].includes(body.priority)))
56579
+ if (body2.priority !== undefined && (typeof body2.priority !== "string" || !["low", "medium", "high", "critical"].includes(body2.priority)))
55097
56580
  return { ok: false, message: "priority must be low, medium, high, or critical" };
55098
- if (body.tags !== undefined && (!Array.isArray(body.tags) || body.tags.some((tag) => typeof tag !== "string" || !tag.trim())))
56581
+ if (body2.tags !== undefined && (!Array.isArray(body2.tags) || body2.tags.some((tag) => typeof tag !== "string" || !tag.trim())))
55099
56582
  return { ok: false, message: "tags must be an array of non-empty strings" };
55100
- 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"))) {
56583
+ 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"))) {
55101
56584
  return { ok: false, message: "variables must be valid template variable objects" };
55102
56585
  }
55103
56586
  for (const field of ["project_id", "plan_id"]) {
55104
- if (body[field] !== undefined && body[field] !== null && (typeof body[field] !== "string" || !body[field].trim()))
56587
+ if (body2[field] !== undefined && body2[field] !== null && (typeof body2[field] !== "string" || !body2[field].trim()))
55105
56588
  return { ok: false, message: `${field} must be a non-empty string or null` };
55106
56589
  }
55107
- if (body.metadata !== undefined && (!body.metadata || typeof body.metadata !== "object" || Array.isArray(body.metadata)))
56590
+ if (body2.metadata !== undefined && (!body2.metadata || typeof body2.metadata !== "object" || Array.isArray(body2.metadata)))
55108
56591
  return { ok: false, message: "metadata must be an object" };
55109
- const tasks = body.tasks === undefined ? [] : Array.isArray(body.tasks) ? body.tasks.map(validateTemplateTask) : null;
55110
- if (tasks === null || tasks.some((task2) => task2 === null))
56592
+ const tasks = body2.tasks === undefined ? [] : Array.isArray(body2.tasks) ? body2.tasks.map(validateTemplateTask) : null;
56593
+ if (tasks === null || tasks.some((task3) => task3 === null))
55111
56594
  return { ok: false, message: "tasks must be valid template task objects" };
55112
56595
  const taskInputs = tasks;
55113
- for (const [position, task2] of taskInputs.entries()) {
55114
- if ((task2.depends_on ?? []).some((dependency) => dependency >= position)) {
56596
+ for (const [position, task3] of taskInputs.entries()) {
56597
+ if ((task3.depends_on ?? []).some((dependency2) => dependency2 >= position)) {
55115
56598
  return { ok: false, message: "template task dependencies must reference earlier task positions" };
55116
56599
  }
55117
56600
  }
55118
56601
  return {
55119
56602
  ok: true,
55120
56603
  input: {
55121
- name: body.name,
55122
- title_pattern: body.title_pattern,
55123
- ...typeof body.description === "string" ? { description: body.description } : {},
55124
- ...typeof body.priority === "string" ? { priority: body.priority } : {},
55125
- ...Array.isArray(body.tags) ? { tags: body.tags } : {},
55126
- ...Array.isArray(body.variables) ? { variables: body.variables } : {},
55127
- ...typeof body.project_id === "string" ? { project_id: body.project_id } : {},
55128
- ...typeof body.plan_id === "string" ? { plan_id: body.plan_id } : {},
55129
- ...body.metadata && typeof body.metadata === "object" && !Array.isArray(body.metadata) ? { metadata: body.metadata } : {},
56604
+ name: body2.name,
56605
+ title_pattern: body2.title_pattern,
56606
+ ...typeof body2.description === "string" ? { description: body2.description } : {},
56607
+ ...typeof body2.priority === "string" ? { priority: body2.priority } : {},
56608
+ ...Array.isArray(body2.tags) ? { tags: body2.tags } : {},
56609
+ ...Array.isArray(body2.variables) ? { variables: body2.variables } : {},
56610
+ ...typeof body2.project_id === "string" ? { project_id: body2.project_id } : {},
56611
+ ...typeof body2.plan_id === "string" ? { plan_id: body2.plan_id } : {},
56612
+ ...body2.metadata && typeof body2.metadata === "object" && !Array.isArray(body2.metadata) ? { metadata: body2.metadata } : {},
55130
56613
  tasks: taskInputs
55131
56614
  }
55132
56615
  };
@@ -55134,25 +56617,25 @@ function validateTemplateCreate(value) {
55134
56617
  function validateTemplatePatch(value) {
55135
56618
  if (!value || typeof value !== "object" || Array.isArray(value))
55136
56619
  return { ok: false, message: "template patch must be an object" };
55137
- const body = value;
56620
+ const body2 = value;
55138
56621
  const allowed = new Set(["name", "title_pattern", "description", "priority", "tags", "variables", "project_id", "plan_id", "metadata"]);
55139
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
56622
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
55140
56623
  if (unknown)
55141
56624
  return { ok: false, message: `unknown template field: ${unknown}` };
55142
- if (Object.keys(body).length === 0)
56625
+ if (Object.keys(body2).length === 0)
55143
56626
  return { ok: false, message: "template patch must not be empty" };
55144
- const templateLike = { name: body.name ?? "template", title_pattern: body.title_pattern ?? "template", ...body };
56627
+ const templateLike = { name: body2.name ?? "template", title_pattern: body2.title_pattern ?? "template", ...body2 };
55145
56628
  const validated = validateTemplateCreate(templateLike);
55146
56629
  if (!validated.ok)
55147
56630
  return validated;
55148
56631
  const { name: _name, title_pattern: _title, tasks: _tasks, ...patch } = validated.input;
55149
56632
  return { ok: true, patch: {
55150
- ...body.name !== undefined ? { name: validated.input.name } : {},
55151
- ...body.title_pattern !== undefined ? { title_pattern: validated.input.title_pattern } : {},
56633
+ ...body2.name !== undefined ? { name: validated.input.name } : {},
56634
+ ...body2.title_pattern !== undefined ? { title_pattern: validated.input.title_pattern } : {},
55152
56635
  ...patch,
55153
- ...body.description === null ? { description: null } : {},
55154
- ...body.project_id === null ? { project_id: null } : {},
55155
- ...body.plan_id === null ? { plan_id: null } : {}
56636
+ ...body2.description === null ? { description: null } : {},
56637
+ ...body2.project_id === null ? { project_id: null } : {},
56638
+ ...body2.plan_id === null ? { plan_id: null } : {}
55156
56639
  } };
55157
56640
  }
55158
56641
  async function readJson3(req) {
@@ -55175,29 +56658,29 @@ async function readOptionalJson(req) {
55175
56658
  return { ok: false };
55176
56659
  }
55177
56660
  }
55178
- function contextFromPrincipal(principal, body) {
55179
- const agentId = body?.agent_id || principal.agent || undefined;
56661
+ function contextFromPrincipal(principal, body2) {
56662
+ const agentId = body2?.agent_id || principal.agent || undefined;
55180
56663
  return agentId ? { agentId } : {};
55181
56664
  }
55182
- function redactComment3(comment) {
55183
- return { ...comment, content: redactEvidenceText(comment.content) };
56665
+ function redactComment3(comment2) {
56666
+ return { ...comment2, content: redactEvidenceText(comment2.content) };
55184
56667
  }
55185
56668
  function normalizeImportSnapshot(raw) {
55186
- const body = raw && typeof raw === "object" ? raw : {};
56669
+ const body2 = raw && typeof raw === "object" ? raw : {};
55187
56670
  const arr = (v) => Array.isArray(v) ? v : [];
55188
56671
  return {
55189
- exportedAt: typeof body["exportedAt"] === "string" ? body["exportedAt"] : new Date().toISOString(),
55190
- source: typeof body["source"] === "string" ? body["source"] : "sqlite",
55191
- tasks: arr(body["tasks"]),
55192
- projects: arr(body["projects"]),
55193
- projectMachinePaths: arr(body["projectMachinePaths"]),
55194
- plans: arr(body["plans"]),
55195
- agents: arr(body["agents"]),
55196
- taskLists: arr(body["taskLists"]),
55197
- templates: arr(body["templates"]),
55198
- templateTasks: arr(body["templateTasks"]),
55199
- auditHistory: arr(body["auditHistory"]),
55200
- tombstones: arr(body["tombstones"])
56672
+ exportedAt: typeof body2["exportedAt"] === "string" ? body2["exportedAt"] : new Date().toISOString(),
56673
+ source: typeof body2["source"] === "string" ? body2["source"] : "sqlite",
56674
+ tasks: arr(body2["tasks"]),
56675
+ projects: arr(body2["projects"]),
56676
+ projectMachinePaths: arr(body2["projectMachinePaths"]),
56677
+ plans: arr(body2["plans"]),
56678
+ agents: arr(body2["agents"]),
56679
+ taskLists: arr(body2["taskLists"]),
56680
+ templates: arr(body2["templates"]),
56681
+ templateTasks: arr(body2["templateTasks"]),
56682
+ auditHistory: arr(body2["auditHistory"]),
56683
+ tombstones: arr(body2["tombstones"])
55201
56684
  };
55202
56685
  }
55203
56686
  function countSnapshotRecords(s) {
@@ -55228,6 +56711,9 @@ async function handleV1Request(req, url, dependencies = {}) {
55228
56711
  if (path === "/v1/project-registration" || path.startsWith("/v1/project-registration/")) {
55229
56712
  return handleTodosProjectRegistrationHttpRequest(req, url, (dependencies.getProjectRegistrationAuthority ?? getCloudProjectRegistrationAuthority)());
55230
56713
  }
56714
+ if (path === "/v1/task-manifest" || path.startsWith("/v1/task-manifest/")) {
56715
+ return handleTodosTaskManifestHttpRequest(req, url, (dependencies.getTaskManifestAuthority ?? getCloudTaskManifestAuthority)());
56716
+ }
55231
56717
  const store = (dependencies.getStorageAdapter ?? getCloudStorageAdapter)();
55232
56718
  const segments = path.split("/").filter(Boolean);
55233
56719
  const resource = segments[1];
@@ -55239,8 +56725,8 @@ async function handleV1Request(req, url, dependencies = {}) {
55239
56725
  if (id === "exists" && !action) {
55240
56726
  if (method !== "POST")
55241
56727
  return error(405, `method ${method} not allowed on /v1/tasks/exists`);
55242
- const body = await readJson3(req);
55243
- const ids2 = Array.isArray(body?.ids) ? Array.from(new Set(body.ids.filter((v) => typeof v === "string" && v.length > 0))) : [];
56728
+ const body2 = await readJson3(req);
56729
+ const ids2 = Array.isArray(body2?.ids) ? Array.from(new Set(body2.ids.filter((v) => typeof v === "string" && v.length > 0))) : [];
55244
56730
  if (ids2.length === 0)
55245
56731
  return error(400, "provide a non-empty string array `ids`");
55246
56732
  if (ids2.length > 5000)
@@ -55249,7 +56735,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55249
56735
  const presentSet = new Set(found.map((t) => t.id));
55250
56736
  const present = ids2.filter((i) => presentSet.has(i));
55251
56737
  const missing = ids2.filter((i) => !presentSet.has(i));
55252
- return json4({
56738
+ return json5({
55253
56739
  requested: ids2.length,
55254
56740
  present_count: present.length,
55255
56741
  missing_count: missing.length,
@@ -55262,20 +56748,20 @@ async function handleV1Request(req, url, dependencies = {}) {
55262
56748
  if (typeof store.tasks.getByFingerprint !== "function") {
55263
56749
  return error(501, "fingerprint upsert is not supported by this storage backend");
55264
56750
  }
55265
- const body = await readJson3(req) ?? {};
55266
- const fingerprint3 = typeof body.fingerprint === "string" ? body.fingerprint.trim() : "";
56751
+ const body2 = await readJson3(req) ?? {};
56752
+ const fingerprint3 = typeof body2.fingerprint === "string" ? body2.fingerprint.trim() : "";
55267
56753
  if (!fingerprint3)
55268
56754
  return error(400, "fingerprint is required");
55269
- if (typeof body.title !== "string" || !body.title.trim())
56755
+ if (typeof body2.title !== "string" || !body2.title.trim())
55270
56756
  return error(400, "title is required");
55271
56757
  const existing = await store.tasks.getByFingerprint(fingerprint3);
55272
56758
  const metadata = {
55273
56759
  ...existing?.metadata ?? {},
55274
- ...body.metadata ?? {},
56760
+ ...body2.metadata ?? {},
55275
56761
  fingerprint: fingerprint3
55276
56762
  };
55277
56763
  const fields = { metadata };
55278
- for (const key of [
56764
+ for (const key2 of [
55279
56765
  "title",
55280
56766
  "description",
55281
56767
  "priority",
@@ -55293,17 +56779,17 @@ async function handleV1Request(req, url, dependencies = {}) {
55293
56779
  "recurrence_rule",
55294
56780
  "task_type"
55295
56781
  ]) {
55296
- const bag = body;
55297
- if (bag[key] !== undefined)
55298
- fields[key] = bag[key];
56782
+ const bag = body2;
56783
+ if (bag[key2] !== undefined)
56784
+ fields[key2] = bag[key2];
55299
56785
  }
55300
56786
  if (!existing) {
55301
- const task2 = await store.tasks.create({ ...fields, title: body.title }, contextFromPrincipal(principal, body));
55302
- return json4({ task: task2, created: true }, 201);
56787
+ const task3 = await store.tasks.create({ ...fields, title: body2.title }, contextFromPrincipal(principal, body2));
56788
+ return json5({ task: task3, created: true }, 201);
55303
56789
  }
55304
56790
  try {
55305
- const task2 = await store.tasks.update(existing.id, { ...fields, version: existing.version }, contextFromPrincipal(principal, body));
55306
- return json4({ task: task2, created: false });
56791
+ const task3 = await store.tasks.update(existing.id, { ...fields, version: existing.version }, contextFromPrincipal(principal, body2));
56792
+ return json5({ task: task3, created: false });
55307
56793
  } catch (e) {
55308
56794
  const msg = e.message || "";
55309
56795
  if (msg.includes("version conflict"))
@@ -55351,32 +56837,32 @@ async function handleV1Request(req, url, dependencies = {}) {
55351
56837
  const tasks = await store.tasks.list(filter);
55352
56838
  const { limit: _l, offset: _o, ...countFilter } = filter;
55353
56839
  const total = await store.tasks.count(countFilter);
55354
- return json4({ tasks, count: tasks.length, total });
56840
+ return json5({ tasks, count: tasks.length, total });
55355
56841
  }
55356
56842
  if (method === "POST") {
55357
- const body = await readJson3(req);
55358
- if (!body || typeof body.title !== "string" || !body.title.trim()) {
56843
+ const body2 = await readJson3(req);
56844
+ if (!body2 || typeof body2.title !== "string" || !body2.title.trim()) {
55359
56845
  return error(400, "title is required");
55360
56846
  }
55361
- const storageContext = contextFromPrincipal(principal, body);
55362
- if (body.parent_id !== undefined) {
55363
- if (typeof body.parent_id !== "string" || !body.parent_id.trim()) {
56847
+ const storageContext = contextFromPrincipal(principal, body2);
56848
+ if (body2.parent_id !== undefined) {
56849
+ if (typeof body2.parent_id !== "string" || !body2.parent_id.trim()) {
55364
56850
  return error(400, "parent_id must be a non-empty task id", {
55365
56851
  code: "PARENT_TASK_ID_INVALID"
55366
56852
  });
55367
56853
  }
55368
- if (!await store.tasks.get(body.parent_id, storageContext)) {
55369
- return error(404, `parent task not found: ${body.parent_id}`, {
56854
+ if (!await store.tasks.get(body2.parent_id, storageContext)) {
56855
+ return error(404, `parent task not found: ${body2.parent_id}`, {
55370
56856
  code: "PARENT_TASK_NOT_FOUND"
55371
56857
  });
55372
56858
  }
55373
56859
  }
55374
- const created = await store.tasks.create(body, storageContext);
56860
+ const created = await store.tasks.create(body2, storageContext);
55375
56861
  const persisted = created?.id ? await store.tasks.get(created.id, storageContext) : null;
55376
- if (!persisted || persisted.id !== created.id || (persisted.parent_id ?? null) !== (body.parent_id ?? null)) {
56862
+ if (!persisted || persisted.id !== created.id || (persisted.parent_id ?? null) !== (body2.parent_id ?? null)) {
55377
56863
  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" });
55378
56864
  }
55379
- return json4({ task: persisted }, 201);
56865
+ return json5({ task: persisted }, 201);
55380
56866
  }
55381
56867
  return error(405, `method ${method} not allowed on /v1/tasks`);
55382
56868
  }
@@ -55393,7 +56879,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55393
56879
  if (legacyPage.length > LEGACY_COMMENT_RESPONSE_LIMIT) {
55394
56880
  return error(426, "task has too many comments for this client; upgrade @hasna/todos to use cursor pagination");
55395
56881
  }
55396
- return json4({
56882
+ return json5({
55397
56883
  comments: legacyPage,
55398
56884
  count: legacyPage.length,
55399
56885
  has_more: false,
@@ -55418,7 +56904,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55418
56904
  const page = (await store.audit.getCommentsPage(id, { limit: limit + 1, ...before ? { before } : {} }, contextFromPrincipal(principal))).map(redactComment3);
55419
56905
  const hasMore = page.length > limit;
55420
56906
  const comments = hasMore ? page.slice(1) : page;
55421
- return json4({
56907
+ return json5({
55422
56908
  comments,
55423
56909
  count: comments.length,
55424
56910
  has_more: hasMore,
@@ -55426,22 +56912,22 @@ async function handleV1Request(req, url, dependencies = {}) {
55426
56912
  });
55427
56913
  }
55428
56914
  if (method === "POST") {
55429
- const body2 = await readJson3(req) ?? {};
55430
- if (typeof body2.content !== "string" || !body2.content.trim()) {
56915
+ const body3 = await readJson3(req) ?? {};
56916
+ if (typeof body3.content !== "string" || !body3.content.trim()) {
55431
56917
  return error(400, "content is required");
55432
56918
  }
55433
56919
  const target = await store.tasks.get(id);
55434
56920
  if (!target)
55435
56921
  return error(404, "task not found");
55436
- const comment = await store.audit.addComment({
56922
+ const comment2 = await store.audit.addComment({
55437
56923
  task_id: id,
55438
- content: body2.content,
55439
- agent_id: body2.agent_id ?? principal.agent ?? undefined,
55440
- session_id: body2.session_id,
55441
- type: body2.type,
55442
- progress_pct: body2.progress_pct
55443
- }, contextFromPrincipal(principal, body2));
55444
- return json4({ comment: redactComment3(comment) }, 201);
56924
+ content: body3.content,
56925
+ agent_id: body3.agent_id ?? principal.agent ?? undefined,
56926
+ session_id: body3.session_id,
56927
+ type: body3.type,
56928
+ progress_pct: body3.progress_pct
56929
+ }, contextFromPrincipal(principal, body3));
56930
+ return json5({ comment: redactComment3(comment2) }, 201);
55445
56931
  }
55446
56932
  return error(405, `method ${method} not allowed on /v1/tasks/:id/comments`);
55447
56933
  }
@@ -55451,36 +56937,36 @@ async function handleV1Request(req, url, dependencies = {}) {
55451
56937
  if (!await store.tasks.get(id))
55452
56938
  return error(404, "task not found");
55453
56939
  const history = await store.audit.getTaskHistory(id);
55454
- return json4({ history, count: history.length });
56940
+ return json5({ history, count: history.length });
55455
56941
  }
55456
56942
  if (action === "lock" || action === "unlock") {
55457
56943
  if (method !== "POST")
55458
56944
  return error(405, `method ${method} not allowed on /v1/tasks/:id/${action}`);
55459
- const body2 = await readJson3(req) ?? {};
56945
+ const body3 = await readJson3(req) ?? {};
55460
56946
  if (!await store.tasks.get(id))
55461
56947
  return error(404, "task not found");
55462
56948
  if (action === "lock") {
55463
56949
  if (typeof store.tasks.lock !== "function")
55464
56950
  return error(501, "task locking is not supported by this storage backend");
55465
- const agentId3 = body2.agent_id || principal.agent || "todos-serve";
55466
- return json4({ result: await store.tasks.lock(id, agentId3) });
56951
+ const agentId3 = body3.agent_id || principal.agent || "todos-serve";
56952
+ return json5({ result: await store.tasks.lock(id, agentId3) });
55467
56953
  }
55468
56954
  if (typeof store.tasks.unlock !== "function")
55469
56955
  return error(501, "task unlocking is not supported by this storage backend");
55470
- if (body2.force === true) {
56956
+ if (body3.force === true) {
55471
56957
  if (!principal.scopes.includes("todos:*"))
55472
56958
  return error(403, "force unlock requires todos:* scope");
55473
56959
  const released2 = await store.tasks.unlock(id);
55474
- return json4({ success: released2 });
56960
+ return json5({ success: released2 });
55475
56961
  }
55476
- if (body2.agent_id && body2.agent_id !== principal.agent && !principal.scopes.includes("todos:*")) {
56962
+ if (body3.agent_id && body3.agent_id !== principal.agent && !principal.scopes.includes("todos:*")) {
55477
56963
  return error(403, "unlock agent_id must match the authenticated agent");
55478
56964
  }
55479
- const agentId2 = body2.agent_id || principal.agent;
56965
+ const agentId2 = body3.agent_id || principal.agent;
55480
56966
  if (!agentId2)
55481
56967
  return error(403, "unlock requires an agent-bound key or force=true");
55482
56968
  const released = await store.tasks.unlock(id, agentId2);
55483
- return json4({ success: released });
56969
+ return json5({ success: released });
55484
56970
  }
55485
56971
  if (action === "dependencies") {
55486
56972
  if (!store.dependencies)
@@ -55489,16 +56975,16 @@ async function handleV1Request(req, url, dependencies = {}) {
55489
56975
  if (!await store.tasks.get(id))
55490
56976
  return error(404, "task not found");
55491
56977
  const edges = await store.dependencies.list(id);
55492
- return json4(edges);
56978
+ return json5(edges);
55493
56979
  }
55494
56980
  if (method === "POST") {
55495
- const body2 = await readJson3(req) ?? {};
55496
- if (typeof body2.depends_on !== "string" || !body2.depends_on.trim()) {
56981
+ const body3 = await readJson3(req) ?? {};
56982
+ if (typeof body3.depends_on !== "string" || !body3.depends_on.trim()) {
55497
56983
  return error(400, "depends_on is required");
55498
56984
  }
55499
56985
  try {
55500
- const dependency = await store.dependencies.add(id, body2.depends_on, contextFromPrincipal(principal));
55501
- return json4({ dependency }, 201);
56986
+ const dependency2 = await store.dependencies.add(id, body3.depends_on, contextFromPrincipal(principal));
56987
+ return json5({ dependency: dependency2 }, 201);
55502
56988
  } catch (e) {
55503
56989
  const msg = e.message || "";
55504
56990
  if (msg.includes("not found"))
@@ -55512,7 +56998,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55512
56998
  if (!subId)
55513
56999
  return error(400, "dependency target id is required (/v1/tasks/:id/dependencies/:dep)");
55514
57000
  const removed = await store.dependencies.remove(id, subId);
55515
- return json4({ removed });
57001
+ return json5({ removed });
55516
57002
  }
55517
57003
  return error(405, `method ${method} not allowed on /v1/tasks/:id/dependencies`);
55518
57004
  }
@@ -55523,23 +57009,23 @@ async function handleV1Request(req, url, dependencies = {}) {
55523
57009
  if (!await store.tasks.get(id))
55524
57010
  return error(404, "task not found");
55525
57011
  const verifications = await store.verifications.list(id);
55526
- return json4({ verifications, count: verifications.length });
57012
+ return json5({ verifications, count: verifications.length });
55527
57013
  }
55528
57014
  if (method === "POST") {
55529
- const body2 = await readJson3(req) ?? {};
55530
- if (typeof body2.command !== "string" || !body2.command.trim()) {
57015
+ const body3 = await readJson3(req) ?? {};
57016
+ if (typeof body3.command !== "string" || !body3.command.trim()) {
55531
57017
  return error(400, "command is required");
55532
57018
  }
55533
57019
  try {
55534
- const verification = await store.verifications.add({
57020
+ const verification2 = await store.verifications.add({
55535
57021
  task_id: id,
55536
- command: body2.command,
55537
- status: body2.status,
55538
- output_summary: body2.output_summary,
55539
- artifact_path: body2.artifact_path,
55540
- agent_id: body2.agent_id
55541
- }, contextFromPrincipal(principal, body2));
55542
- return json4({ verification }, 201);
57022
+ command: body3.command,
57023
+ status: body3.status,
57024
+ output_summary: body3.output_summary,
57025
+ artifact_path: body3.artifact_path,
57026
+ agent_id: body3.agent_id
57027
+ }, contextFromPrincipal(principal, body3));
57028
+ return json5({ verification: verification2 }, 201);
55543
57029
  } catch (e) {
55544
57030
  const msg = e.message || "";
55545
57031
  if (msg.includes("not found"))
@@ -55556,21 +57042,21 @@ async function handleV1Request(req, url, dependencies = {}) {
55556
57042
  if (!await store.tasks.get(id))
55557
57043
  return error(404, "task not found");
55558
57044
  const commits = await store.commits.list(id);
55559
- return json4({ commits, count: commits.length });
57045
+ return json5({ commits, count: commits.length });
55560
57046
  }
55561
57047
  if (method === "POST") {
55562
- const body2 = await readJson3(req) ?? {};
55563
- if (typeof body2.sha !== "string" || !body2.sha.trim())
57048
+ const body3 = await readJson3(req) ?? {};
57049
+ if (typeof body3.sha !== "string" || !body3.sha.trim())
55564
57050
  return error(400, "sha is required");
55565
57051
  try {
55566
57052
  const commit = await store.commits.add({
55567
57053
  task_id: id,
55568
- sha: body2.sha,
55569
- message: body2.message,
55570
- author: body2.author,
55571
- files_changed: Array.isArray(body2.files_changed) ? body2.files_changed : undefined
57054
+ sha: body3.sha,
57055
+ message: body3.message,
57056
+ author: body3.author,
57057
+ files_changed: Array.isArray(body3.files_changed) ? body3.files_changed : undefined
55572
57058
  }, contextFromPrincipal(principal));
55573
- return json4({ commit }, 201);
57059
+ return json5({ commit }, 201);
55574
57060
  } catch (e) {
55575
57061
  const msg = e.message || "";
55576
57062
  if (msg.includes("not found"))
@@ -55587,23 +57073,23 @@ async function handleV1Request(req, url, dependencies = {}) {
55587
57073
  if (!await store.tasks.get(id))
55588
57074
  return error(404, "task not found");
55589
57075
  const refs = await store.gitRefs.list(id);
55590
- return json4({ refs, count: refs.length });
57076
+ return json5({ refs, count: refs.length });
55591
57077
  }
55592
57078
  if (method === "POST") {
55593
- const body2 = await readJson3(req) ?? {};
55594
- const refType = body2.ref_type === "pull_request" || body2.ref_type === "branch" ? body2.ref_type : "branch";
55595
- if (typeof body2.name !== "string" || !body2.name.trim())
57079
+ const body3 = await readJson3(req) ?? {};
57080
+ const refType = body3.ref_type === "pull_request" || body3.ref_type === "branch" ? body3.ref_type : "branch";
57081
+ if (typeof body3.name !== "string" || !body3.name.trim())
55596
57082
  return error(400, "name is required");
55597
57083
  try {
55598
57084
  const ref = await store.gitRefs.add({
55599
57085
  task_id: id,
55600
57086
  ref_type: refType,
55601
- name: body2.name,
55602
- url: body2.url,
55603
- provider: body2.provider,
55604
- metadata: body2.metadata
57087
+ name: body3.name,
57088
+ url: body3.url,
57089
+ provider: body3.provider,
57090
+ metadata: body3.metadata
55605
57091
  }, contextFromPrincipal(principal));
55606
- return json4({ ref }, 201);
57092
+ return json5({ ref }, 201);
55607
57093
  } catch (e) {
55608
57094
  const msg = e.message || "";
55609
57095
  if (msg.includes("not found"))
@@ -55616,32 +57102,32 @@ async function handleV1Request(req, url, dependencies = {}) {
55616
57102
  const actionJson = await readOptionalJson(req);
55617
57103
  if (!actionJson.ok)
55618
57104
  return error(400, "invalid JSON body");
55619
- const body = actionJson.value && typeof actionJson.value === "object" && !Array.isArray(actionJson.value) ? actionJson.value : {};
55620
- const agentId = typeof body.agent_id === "string" ? body.agent_id : principal.agent || "todos-serve";
57105
+ const body2 = actionJson.value && typeof actionJson.value === "object" && !Array.isArray(actionJson.value) ? actionJson.value : {};
57106
+ const agentId = typeof body2.agent_id === "string" ? body2.agent_id : principal.agent || "todos-serve";
55621
57107
  if (action === "start" && method === "POST") {
55622
- return json4({ task: await store.tasks.start(id, agentId) });
57108
+ return json5({ task: await store.tasks.start(id, agentId) });
55623
57109
  }
55624
57110
  if (action === "complete" && method === "POST") {
55625
57111
  const parsed = validateTaskCompletion(actionJson.value);
55626
57112
  if (!parsed.ok)
55627
57113
  return error(400, parsed.message);
55628
- return json4({
55629
- task: await store.tasks.complete(id, parsed.agentId || principal.agent || "todos-serve", parsed.options, contextFromPrincipal(principal, body))
57114
+ return json5({
57115
+ task: await store.tasks.complete(id, parsed.agentId || principal.agent || "todos-serve", parsed.options, contextFromPrincipal(principal, body2))
55630
57116
  });
55631
57117
  }
55632
57118
  if (action === "fail" && method === "POST") {
55633
- return json4({ result: await store.tasks.fail(id, agentId, typeof body.reason === "string" ? body.reason : "failed", {}) });
57119
+ return json5({ result: await store.tasks.fail(id, agentId, typeof body2.reason === "string" ? body2.reason : "failed", {}) });
55634
57120
  }
55635
57121
  if (action === "claim" && method === "POST") {
55636
- return json4({ task: await store.tasks.claimNext(agentId, {}) });
57122
+ return json5({ task: await store.tasks.claimNext(agentId, {}) });
55637
57123
  }
55638
57124
  return error(404, `unknown task action: ${action}`);
55639
57125
  }
55640
57126
  if (method === "GET") {
55641
- let task2 = await store.tasks.get(id);
55642
- if (!task2 && typeof store.tasks.resolveRef === "function") {
57127
+ let task3 = await store.tasks.get(id);
57128
+ if (!task3 && typeof store.tasks.resolveRef === "function") {
55643
57129
  try {
55644
- task2 = await store.tasks.resolveRef(id);
57130
+ task3 = await store.tasks.resolveRef(id);
55645
57131
  } catch (e) {
55646
57132
  if (e instanceof TaskReferenceAmbiguousError) {
55647
57133
  return error(409, e.message, {
@@ -55656,22 +57142,22 @@ async function handleV1Request(req, url, dependencies = {}) {
55656
57142
  throw e;
55657
57143
  }
55658
57144
  }
55659
- return task2 ? json4({ task: task2 }) : error(404, "task not found");
57145
+ return task3 ? json5({ task: task3 }) : error(404, "task not found");
55660
57146
  }
55661
57147
  if (method === "PATCH" || method === "PUT") {
55662
- const body = await readJson3(req);
55663
- if (!body)
57148
+ const body2 = await readJson3(req);
57149
+ if (!body2)
55664
57150
  return error(400, "invalid JSON body");
55665
57151
  const current = await store.tasks.get(id);
55666
57152
  if (!current)
55667
57153
  return error(404, "task not found");
55668
57154
  const patch = {
55669
- ...body,
55670
- version: typeof body.version === "number" ? body.version : current.version
57155
+ ...body2,
57156
+ version: typeof body2.version === "number" ? body2.version : current.version
55671
57157
  };
55672
57158
  try {
55673
- const task2 = await store.tasks.update(id, patch);
55674
- return task2 ? json4({ task: task2 }) : error(404, "task not found");
57159
+ const task3 = await store.tasks.update(id, patch);
57160
+ return task3 ? json5({ task: task3 }) : error(404, "task not found");
55675
57161
  } catch (e) {
55676
57162
  const msg = e.message || "";
55677
57163
  if (msg.includes("version conflict"))
@@ -55681,7 +57167,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55681
57167
  }
55682
57168
  if (method === "DELETE") {
55683
57169
  await store.tasks.delete(id, contextFromPrincipal(principal));
55684
- return json4({ deleted: true, id });
57170
+ return json5({ deleted: true, id });
55685
57171
  }
55686
57172
  return error(405, `method ${method} not allowed on /v1/tasks/:id`);
55687
57173
  }
@@ -55689,111 +57175,111 @@ async function handleV1Request(req, url, dependencies = {}) {
55689
57175
  if (!id) {
55690
57176
  if (method === "GET") {
55691
57177
  const projects = await store.projects.list();
55692
- return json4({ projects, count: projects.length });
57178
+ return json5({ projects, count: projects.length });
55693
57179
  }
55694
57180
  if (method === "POST") {
55695
- const body = await readJson3(req);
55696
- if (!body)
57181
+ const body2 = await readJson3(req);
57182
+ if (!body2)
55697
57183
  return error(400, "invalid JSON body");
55698
- const validated = validateProjectCreate(body);
57184
+ const validated = validateProjectCreate(body2);
55699
57185
  if (!validated.ok)
55700
57186
  return error(400, validated.message);
55701
57187
  const project = await store.projects.create(validated.input, contextFromPrincipal(principal));
55702
- return json4({ project }, 201);
57188
+ return json5({ project }, 201);
55703
57189
  }
55704
57190
  return error(405, `method ${method} not allowed on /v1/projects`);
55705
57191
  }
55706
57192
  if (action === "task-list" && subId === "ensure") {
55707
57193
  if (method === "GET") {
55708
- return json4(await planProjectTaskListEnsure(store, id));
57194
+ return json5(await planProjectTaskListEnsure(store, id));
55709
57195
  }
55710
57196
  if (method !== "POST") {
55711
57197
  return error(405, `method ${method} not allowed on /v1/projects/:id/task-list/ensure`);
55712
57198
  }
55713
- const body = await readJson3(req);
55714
- if (!body)
57199
+ const body2 = await readJson3(req);
57200
+ if (!body2)
55715
57201
  return error(400, "invalid JSON body");
55716
- const unknown = Object.keys(body).find((key) => !["expected_project_revision", "idempotency_key"].includes(key));
57202
+ const unknown = Object.keys(body2).find((key2) => !["expected_project_revision", "idempotency_key"].includes(key2));
55717
57203
  if (unknown)
55718
57204
  return error(400, `unknown task-list ensure field: ${unknown}`);
55719
- if (typeof body.expected_project_revision !== "string" || !body.expected_project_revision.trim()) {
57205
+ if (typeof body2.expected_project_revision !== "string" || !body2.expected_project_revision.trim()) {
55720
57206
  return error(400, "expected_project_revision must be a non-empty string from a fresh ensure plan");
55721
57207
  }
55722
- if (body.idempotency_key !== undefined && typeof body.idempotency_key !== "string") {
57208
+ if (body2.idempotency_key !== undefined && typeof body2.idempotency_key !== "string") {
55723
57209
  return error(400, "idempotency_key must be a string");
55724
57210
  }
55725
57211
  const result = await applyProjectTaskListEnsure(store, id, {
55726
- expected_project_revision: body.expected_project_revision,
55727
- ...typeof body.idempotency_key === "string" ? { idempotency_key: body.idempotency_key } : {}
57212
+ expected_project_revision: body2.expected_project_revision,
57213
+ ...typeof body2.idempotency_key === "string" ? { idempotency_key: body2.idempotency_key } : {}
55728
57214
  });
55729
- return json4(result, result.action === "created" ? 201 : 200);
57215
+ return json5(result, result.action === "created" ? 201 : 200);
55730
57216
  }
55731
57217
  if (action === "task-list" && subId === "rollback") {
55732
57218
  if (method !== "POST") {
55733
57219
  return error(405, `method ${method} not allowed on /v1/projects/:id/task-list/rollback`);
55734
57220
  }
55735
- const body = await readJson3(req);
55736
- if (!body)
57221
+ const body2 = await readJson3(req);
57222
+ if (!body2)
55737
57223
  return error(400, "invalid JSON body");
55738
- const unknown = Object.keys(body).find((key) => !["receipt_id", "expected_task_list_revision"].includes(key));
57224
+ const unknown = Object.keys(body2).find((key2) => !["receipt_id", "expected_task_list_revision"].includes(key2));
55739
57225
  if (unknown)
55740
57226
  return error(400, `unknown task-list rollback field: ${unknown}`);
55741
- if (typeof body.receipt_id !== "string" || !body.receipt_id.trim()) {
57227
+ if (typeof body2.receipt_id !== "string" || !body2.receipt_id.trim()) {
55742
57228
  return error(400, "receipt_id must be a non-empty string");
55743
57229
  }
55744
- if (typeof body.expected_task_list_revision !== "string" || !body.expected_task_list_revision.trim()) {
57230
+ if (typeof body2.expected_task_list_revision !== "string" || !body2.expected_task_list_revision.trim()) {
55745
57231
  return error(400, "expected_task_list_revision must be a non-empty string from the accepted receipt");
55746
57232
  }
55747
- return json4(await rollbackProjectTaskListEnsure(store, id, {
55748
- receipt_id: body.receipt_id,
55749
- expected_task_list_revision: body.expected_task_list_revision
57233
+ return json5(await rollbackProjectTaskListEnsure(store, id, {
57234
+ receipt_id: body2.receipt_id,
57235
+ expected_task_list_revision: body2.expected_task_list_revision
55750
57236
  }));
55751
57237
  }
55752
57238
  if (action === "rename") {
55753
57239
  if (method !== "POST")
55754
57240
  return error(405, `method ${method} not allowed on /v1/projects/:id/rename`);
55755
- const body = await readJson3(req);
55756
- if (!body || typeof body.new_slug !== "string" || !body.new_slug.trim() || !normalizeSlug(body.new_slug)) {
57241
+ const body2 = await readJson3(req);
57242
+ if (!body2 || typeof body2.new_slug !== "string" || !body2.new_slug.trim() || !normalizeSlug(body2.new_slug)) {
55757
57243
  return error(400, "new_slug must be a non-empty string");
55758
57244
  }
55759
- if (body.name !== undefined && (typeof body.name !== "string" || !body.name.trim())) {
57245
+ if (body2.name !== undefined && (typeof body2.name !== "string" || !body2.name.trim())) {
55760
57246
  return error(400, "name must be a non-empty string");
55761
57247
  }
55762
- const unknownField = Object.keys(body).find((key) => !["new_slug", "name"].includes(key));
57248
+ const unknownField = Object.keys(body2).find((key2) => !["new_slug", "name"].includes(key2));
55763
57249
  if (unknownField)
55764
57250
  return error(400, `unknown project rename field: ${unknownField}`);
55765
- return json4(await store.projects.rename(id, body, contextFromPrincipal(principal)));
57251
+ return json5(await store.projects.rename(id, body2, contextFromPrincipal(principal)));
55766
57252
  }
55767
57253
  if (method === "GET") {
55768
57254
  const project = await store.projects.get(id);
55769
- return project ? json4({ project }) : error(404, "project not found");
57255
+ return project ? json5({ project }) : error(404, "project not found");
55770
57256
  }
55771
57257
  if (method === "PATCH" || method === "PUT") {
55772
- const body = await readJson3(req);
55773
- if (!body)
57258
+ const body2 = await readJson3(req);
57259
+ if (!body2)
55774
57260
  return error(400, "invalid JSON body");
55775
- const validated = validateProjectPatch(body);
57261
+ const validated = validateProjectPatch(body2);
55776
57262
  if (!validated.ok)
55777
57263
  return error(400, validated.message);
55778
57264
  if (!await store.projects.get(id))
55779
57265
  return error(404, "project not found");
55780
57266
  const project = await store.projects.update(id, validated.patch);
55781
- return json4({ project });
57267
+ return json5({ project });
55782
57268
  }
55783
57269
  if (method === "DELETE") {
55784
57270
  await store.projects.delete(id, contextFromPrincipal(principal));
55785
- return json4({ deleted: true, id });
57271
+ return json5({ deleted: true, id });
55786
57272
  }
55787
57273
  return error(405, `method ${method} not allowed on /v1/projects/:id`);
55788
57274
  }
55789
57275
  if (resource === "plans") {
55790
57276
  if (!id && method === "GET") {
55791
57277
  const plans = await store.plans.list(url.searchParams.get("project_id") ?? undefined);
55792
- return json4({ plans, count: plans.length });
57278
+ return json5({ plans, count: plans.length });
55793
57279
  }
55794
57280
  if (!id && method === "POST") {
55795
- const body = await readJson3(req);
55796
- const validated = validatePlanCreate(body);
57281
+ const body2 = await readJson3(req);
57282
+ const validated = validatePlanCreate(body2);
55797
57283
  if (!validated.ok)
55798
57284
  return error(400, validated.message);
55799
57285
  if (validated.input.slug) {
@@ -55807,104 +57293,104 @@ async function handleV1Request(req, url, dependencies = {}) {
55807
57293
  }
55808
57294
  }
55809
57295
  const plan = await store.plans.create(validated.input, contextFromPrincipal(principal, validated.input));
55810
- return json4({ plan }, 201);
57296
+ return json5({ plan }, 201);
55811
57297
  }
55812
57298
  if (id && action === "project-link" && !subId) {
55813
57299
  if (method === "GET") {
55814
57300
  const projectId = url.searchParams.get("project_id");
55815
57301
  if (!projectId?.trim())
55816
57302
  return error(400, "project_id query parameter is required");
55817
- return json4(await planPlanProjectLink(store, id, projectId));
57303
+ return json5(await planPlanProjectLink(store, id, projectId));
55818
57304
  }
55819
57305
  if (method !== "POST")
55820
57306
  return error(405, `method ${method} not allowed on /v1/plans/:id/project-link`);
55821
- const body = await readJson3(req);
55822
- if (!body)
57307
+ const body2 = await readJson3(req);
57308
+ if (!body2)
55823
57309
  return error(400, "invalid JSON body");
55824
57310
  const allowed = new Set(["project_id", "expected_plan_revision", "expected_project_revision", "idempotency_key"]);
55825
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
57311
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
55826
57312
  if (unknown)
55827
57313
  return error(400, `unknown plan-project-link field: ${unknown}`);
55828
57314
  for (const field of ["project_id", "expected_plan_revision", "expected_project_revision", "idempotency_key"]) {
55829
- if (typeof body[field] !== "string" || !body[field].trim()) {
57315
+ if (typeof body2[field] !== "string" || !body2[field].trim()) {
55830
57316
  return error(400, `${field} must be a non-empty string`);
55831
57317
  }
55832
57318
  }
55833
- const result = await applyPlanProjectLink(store, id, body.project_id, {
55834
- expected_plan_revision: body.expected_plan_revision,
55835
- expected_project_revision: body.expected_project_revision,
55836
- idempotency_key: body.idempotency_key
57319
+ const result = await applyPlanProjectLink(store, id, body2.project_id, {
57320
+ expected_plan_revision: body2.expected_plan_revision,
57321
+ expected_project_revision: body2.expected_project_revision,
57322
+ idempotency_key: body2.idempotency_key
55837
57323
  });
55838
- return json4(result, result.action === "linked" ? 201 : 200);
57324
+ return json5(result, result.action === "linked" ? 201 : 200);
55839
57325
  }
55840
57326
  if (id && action === "project-link" && subId === "rollback") {
55841
57327
  if (method !== "POST")
55842
57328
  return error(405, `method ${method} not allowed on /v1/plans/:id/project-link/rollback`);
55843
- const body = await readJson3(req);
55844
- if (!body)
57329
+ const body2 = await readJson3(req);
57330
+ if (!body2)
55845
57331
  return error(400, "invalid JSON body");
55846
57332
  const allowed = new Set(["project_id", "receipt_id", "expected_plan_revision"]);
55847
- const unknown = Object.keys(body).find((key) => !allowed.has(key));
57333
+ const unknown = Object.keys(body2).find((key2) => !allowed.has(key2));
55848
57334
  if (unknown)
55849
57335
  return error(400, `unknown plan-project-link rollback field: ${unknown}`);
55850
57336
  for (const field of ["project_id", "receipt_id", "expected_plan_revision"]) {
55851
- if (typeof body[field] !== "string" || !body[field].trim()) {
57337
+ if (typeof body2[field] !== "string" || !body2[field].trim()) {
55852
57338
  return error(400, `${field} must be a non-empty string`);
55853
57339
  }
55854
57340
  }
55855
- return json4(await rollbackPlanProjectLink(store, id, body.project_id, {
55856
- receipt_id: body.receipt_id,
55857
- expected_plan_revision: body.expected_plan_revision
57341
+ return json5(await rollbackPlanProjectLink(store, id, body2.project_id, {
57342
+ receipt_id: body2.receipt_id,
57343
+ expected_plan_revision: body2.expected_plan_revision
55858
57344
  }));
55859
57345
  }
55860
57346
  if (id && method === "GET") {
55861
57347
  const plan = await store.plans.get(id);
55862
- return plan ? json4({ plan }) : error(404, "plan not found");
57348
+ return plan ? json5({ plan }) : error(404, "plan not found");
55863
57349
  }
55864
57350
  if (id && (method === "PATCH" || method === "PUT")) {
55865
- const body = await readJson3(req);
55866
- if (!body || Object.keys(body).length === 0)
57351
+ const body2 = await readJson3(req);
57352
+ if (!body2 || Object.keys(body2).length === 0)
55867
57353
  return error(400, "plan patch is required");
55868
57354
  const allowed = new Set(["name", "slug", "description", "status", "task_list_id", "agent_id"]);
55869
- const unknownField = Object.keys(body).find((key) => !allowed.has(key));
57355
+ const unknownField = Object.keys(body2).find((key2) => !allowed.has(key2));
55870
57356
  if (unknownField)
55871
57357
  return error(400, `unknown plan field: ${unknownField}`);
55872
57358
  for (const field of ["name", "slug", "task_list_id", "agent_id"]) {
55873
- if (body[field] !== undefined && (typeof body[field] !== "string" || !body[field].trim())) {
57359
+ if (body2[field] !== undefined && (typeof body2[field] !== "string" || !body2[field].trim())) {
55874
57360
  return error(400, `${field} must be a non-empty string`);
55875
57361
  }
55876
57362
  }
55877
- if (typeof body.slug === "string") {
55878
- const slug = normalizeSlug(body.slug);
57363
+ if (typeof body2.slug === "string") {
57364
+ const slug = normalizeSlug(body2.slug);
55879
57365
  if (!slug)
55880
57366
  return error(400, "slug must produce a non-empty canonical slug");
55881
- body.slug = slug;
57367
+ body2.slug = slug;
55882
57368
  }
55883
- if (body.description !== undefined && typeof body.description !== "string") {
57369
+ if (body2.description !== undefined && typeof body2.description !== "string") {
55884
57370
  return error(400, "description must be a string");
55885
57371
  }
55886
- if (body.status !== undefined && (typeof body.status !== "string" || !["active", "completed", "archived"].includes(body.status))) {
57372
+ if (body2.status !== undefined && (typeof body2.status !== "string" || !["active", "completed", "archived"].includes(body2.status))) {
55887
57373
  return error(400, "status must be active, completed, or archived");
55888
57374
  }
55889
57375
  const existing = await store.plans.get(id);
55890
57376
  if (!existing)
55891
57377
  return error(404, "plan not found");
55892
- if (typeof body.slug === "string") {
55893
- 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);
57378
+ if (typeof body2.slug === "string") {
57379
+ 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);
55894
57380
  if (duplicate) {
55895
- return error(409, `Plan slug already exists in this scope: ${body.slug}`, {
57381
+ return error(409, `Plan slug already exists in this scope: ${body2.slug}`, {
55896
57382
  code: "PLAN_SLUG_CONFLICT",
55897
57383
  conflict: true
55898
57384
  });
55899
57385
  }
55900
57386
  }
55901
- const plan = await store.plans.update(id, body);
55902
- return json4({ plan });
57387
+ const plan = await store.plans.update(id, body2);
57388
+ return json5({ plan });
55903
57389
  }
55904
57390
  if (id && method === "DELETE") {
55905
57391
  if (!await store.plans.delete(id, contextFromPrincipal(principal)))
55906
57392
  return error(404, "plan not found");
55907
- return json4({ deleted: true, id });
57393
+ return json5({ deleted: true, id });
55908
57394
  }
55909
57395
  if (id)
55910
57396
  return error(405, `method ${method} not allowed on /v1/plans/:id`);
@@ -55913,50 +57399,50 @@ async function handleV1Request(req, url, dependencies = {}) {
55913
57399
  if (!id && method === "GET") {
55914
57400
  const projectId = url.searchParams.get("project_id");
55915
57401
  const templates = (await store.templates.list()).filter((template) => projectId === null || template.project_id === projectId);
55916
- return json4({ templates, count: templates.length });
57402
+ return json5({ templates, count: templates.length });
55917
57403
  }
55918
57404
  if (!id && method === "POST") {
55919
- const body = await readJson3(req);
55920
- const validated = validateTemplateCreate(body);
57405
+ const body2 = await readJson3(req);
57406
+ const validated = validateTemplateCreate(body2);
55921
57407
  if (!validated.ok)
55922
57408
  return error(400, validated.message);
55923
57409
  const template = await store.templates.create(validated.input, contextFromPrincipal(principal));
55924
- return json4({ template: await store.templates.getWithTasks(template.id) }, 201);
57410
+ return json5({ template: await store.templates.getWithTasks(template.id) }, 201);
55925
57411
  }
55926
57412
  if (!id)
55927
57413
  return error(405, `method ${method} not allowed on /v1/templates`);
55928
57414
  if (method === "GET") {
55929
57415
  const template = await store.templates.getWithTasks(id);
55930
- return template ? json4({ template }) : error(404, "template not found");
57416
+ return template ? json5({ template }) : error(404, "template not found");
55931
57417
  }
55932
57418
  if (method === "PATCH" || method === "PUT") {
55933
- const body = await readJson3(req);
55934
- const validated = validateTemplatePatch(body);
57419
+ const body2 = await readJson3(req);
57420
+ const validated = validateTemplatePatch(body2);
55935
57421
  if (!validated.ok)
55936
57422
  return error(400, validated.message);
55937
57423
  const template = await store.templates.update(id, validated.patch, contextFromPrincipal(principal));
55938
- return template ? json4({ template: await store.templates.getWithTasks(id) }) : error(404, "template not found");
57424
+ return template ? json5({ template: await store.templates.getWithTasks(id) }) : error(404, "template not found");
55939
57425
  }
55940
57426
  if (method === "DELETE") {
55941
57427
  const deleted = await store.templates.delete(id, contextFromPrincipal(principal));
55942
- return deleted ? json4({ deleted: true, id }) : error(404, "template not found");
57428
+ return deleted ? json5({ deleted: true, id }) : error(404, "template not found");
55943
57429
  }
55944
57430
  return error(405, `method ${method} not allowed on /v1/templates/:id`);
55945
57431
  }
55946
57432
  if (resource === "agents") {
55947
57433
  if (!id && method === "GET") {
55948
57434
  const agents = await store.agents.list();
55949
- return json4({ agents, count: agents.length });
57435
+ return json5({ agents, count: agents.length });
55950
57436
  }
55951
57437
  if (!id && method === "POST") {
55952
- const body = await readJson3(req);
55953
- if (!body || typeof body.name !== "string" || !body.name.trim())
57438
+ const body2 = await readJson3(req);
57439
+ if (!body2 || typeof body2.name !== "string" || !body2.name.trim())
55954
57440
  return error(400, "name is required");
55955
- const result = await store.agents.register(body, contextFromPrincipal(principal));
57441
+ const result = await store.agents.register(body2, contextFromPrincipal(principal));
55956
57442
  if (result && typeof result === "object" && "conflict" in result) {
55957
57443
  return error(409, result.message ?? "agent name conflict", { conflict: true });
55958
57444
  }
55959
- return json4({ agent: result }, 201);
57445
+ return json5({ agent: result }, 201);
55960
57446
  }
55961
57447
  if (id && action === "heartbeat") {
55962
57448
  if (method !== "POST")
@@ -55965,7 +57451,7 @@ async function handleV1Request(req, url, dependencies = {}) {
55965
57451
  return error(501, "agent heartbeat is not supported by this storage backend");
55966
57452
  }
55967
57453
  const agent = await store.agents.heartbeat(id, contextFromPrincipal(principal));
55968
- return agent ? json4({ agent }) : error(404, "agent not found");
57454
+ return agent ? json5({ agent }) : error(404, "agent not found");
55969
57455
  }
55970
57456
  if (id && action === "release") {
55971
57457
  if (method !== "POST")
@@ -55973,18 +57459,18 @@ async function handleV1Request(req, url, dependencies = {}) {
55973
57459
  if (typeof store.agents.release !== "function") {
55974
57460
  return error(501, "agent release is not supported by this storage backend");
55975
57461
  }
55976
- const body = await readJson3(req) ?? {};
55977
- const result = await store.agents.release(id, body.session_id, contextFromPrincipal(principal));
57462
+ const body2 = await readJson3(req) ?? {};
57463
+ const result = await store.agents.release(id, body2.session_id, contextFromPrincipal(principal));
55978
57464
  if (!result)
55979
57465
  return error(404, "agent not found");
55980
57466
  if (!result.released) {
55981
57467
  return error(409, "release denied: session_id does not match agent's current session", { released: false });
55982
57468
  }
55983
- return json4({ agent: result.agent, released: true });
57469
+ return json5({ agent: result.agent, released: true });
55984
57470
  }
55985
57471
  if (id && method === "GET") {
55986
57472
  const agent = await store.agents.get(id);
55987
- return agent ? json4({ agent }) : error(404, "agent not found");
57473
+ return agent ? json5({ agent }) : error(404, "agent not found");
55988
57474
  }
55989
57475
  }
55990
57476
  if (resource === "activity" && !id) {
@@ -55993,66 +57479,66 @@ async function handleV1Request(req, url, dependencies = {}) {
55993
57479
  const limitParam = url.searchParams.get("limit");
55994
57480
  const limit = limitParam ? Math.max(1, Math.min(1e4, Number(limitParam) || 50)) : 50;
55995
57481
  const activity = await store.audit.getRecentActivity(limit);
55996
- return json4({ activity, count: activity.length });
57482
+ return json5({ activity, count: activity.length });
55997
57483
  }
55998
57484
  if (resource === "task-lists") {
55999
57485
  if (!id && method === "GET") {
56000
57486
  const projectId = url.searchParams.get("project_id") ?? undefined;
56001
57487
  const taskLists = await store.taskLists.list(projectId);
56002
- return json4({ task_lists: taskLists, count: taskLists.length });
57488
+ return json5({ task_lists: taskLists, count: taskLists.length });
56003
57489
  }
56004
57490
  if (!id && method === "POST") {
56005
- const body = await readJson3(req);
56006
- if (!body || typeof body.name !== "string" || !body.name.trim())
57491
+ const body2 = await readJson3(req);
57492
+ if (!body2 || typeof body2.name !== "string" || !body2.name.trim())
56007
57493
  return error(400, "name is required");
56008
- const unknownField = Object.keys(body).find((key) => !["name", "slug", "project_id", "description", "metadata"].includes(key));
57494
+ const unknownField = Object.keys(body2).find((key2) => !["name", "slug", "project_id", "description", "metadata"].includes(key2));
56009
57495
  if (unknownField)
56010
57496
  return error(400, `unsupported task-list create field: ${unknownField}`);
56011
- if (body.slug !== undefined && typeof body.slug !== "string")
57497
+ if (body2.slug !== undefined && typeof body2.slug !== "string")
56012
57498
  return error(400, "slug must be a string");
56013
- if (body.project_id !== undefined && (typeof body.project_id !== "string" || !body.project_id.trim()))
57499
+ if (body2.project_id !== undefined && (typeof body2.project_id !== "string" || !body2.project_id.trim()))
56014
57500
  return error(400, "project_id must be a non-empty string");
56015
- if (body.description !== undefined && typeof body.description !== "string")
57501
+ if (body2.description !== undefined && typeof body2.description !== "string")
56016
57502
  return error(400, "description must be a string");
56017
- if (body.metadata !== undefined && (!body.metadata || typeof body.metadata !== "object" || Array.isArray(body.metadata))) {
57503
+ if (body2.metadata !== undefined && (!body2.metadata || typeof body2.metadata !== "object" || Array.isArray(body2.metadata))) {
56018
57504
  return error(400, "metadata must be an object");
56019
57505
  }
56020
- if (!normalizeSlug(body.slug === undefined ? body.name : body.slug)) {
57506
+ if (!normalizeSlug(body2.slug === undefined ? body2.name : body2.slug)) {
56021
57507
  return error(400, "task-list slug must be non-empty kebab-case");
56022
57508
  }
56023
- const taskList = await store.taskLists.create(body, contextFromPrincipal(principal));
56024
- return json4({ task_list: taskList }, 201);
57509
+ const taskList = await store.taskLists.create(body2, contextFromPrincipal(principal));
57510
+ return json5({ task_list: taskList }, 201);
56025
57511
  }
56026
57512
  if (id && method === "GET") {
56027
57513
  const taskList = await store.taskLists.get(id);
56028
- return taskList ? json4({ task_list: taskList }) : error(404, "task list not found");
57514
+ return taskList ? json5({ task_list: taskList }) : error(404, "task list not found");
56029
57515
  }
56030
57516
  if (id && (method === "PATCH" || method === "PUT")) {
56031
- const body = await readJson3(req);
56032
- if (!body)
57517
+ const body2 = await readJson3(req);
57518
+ if (!body2)
56033
57519
  return error(400, "invalid JSON body");
56034
- const unknownField = Object.keys(body).find((key) => !["slug", "name", "description", "metadata"].includes(key));
57520
+ const unknownField = Object.keys(body2).find((key2) => !["slug", "name", "description", "metadata"].includes(key2));
56035
57521
  if (unknownField)
56036
57522
  return error(400, `unsupported task-list update field: ${unknownField}`);
56037
- if (Object.keys(body).length === 0)
57523
+ if (Object.keys(body2).length === 0)
56038
57524
  return error(400, "task-list update must not be empty");
56039
- if (body.slug !== undefined && (typeof body.slug !== "string" || !normalizeSlug(body.slug)))
57525
+ if (body2.slug !== undefined && (typeof body2.slug !== "string" || !normalizeSlug(body2.slug)))
56040
57526
  return error(400, "slug must be a non-empty string");
56041
- if (body.name !== undefined && (typeof body.name !== "string" || !body.name.trim()))
57527
+ if (body2.name !== undefined && (typeof body2.name !== "string" || !body2.name.trim()))
56042
57528
  return error(400, "name must be a non-empty string");
56043
- if (body.description !== undefined && typeof body.description !== "string")
57529
+ if (body2.description !== undefined && typeof body2.description !== "string")
56044
57530
  return error(400, "description must be a string");
56045
- if (body.metadata !== undefined && (!body.metadata || typeof body.metadata !== "object" || Array.isArray(body.metadata))) {
57531
+ if (body2.metadata !== undefined && (!body2.metadata || typeof body2.metadata !== "object" || Array.isArray(body2.metadata))) {
56046
57532
  return error(400, "metadata must be an object");
56047
57533
  }
56048
57534
  if (!await store.taskLists.get(id))
56049
57535
  return error(404, "task list not found");
56050
- const taskList = await store.taskLists.update(id, body);
56051
- return json4({ task_list: taskList });
57536
+ const taskList = await store.taskLists.update(id, body2);
57537
+ return json5({ task_list: taskList });
56052
57538
  }
56053
57539
  if (id && method === "DELETE") {
56054
57540
  const deleted = await store.taskLists.delete(id, contextFromPrincipal(principal));
56055
- return deleted ? json4({ deleted: true, id }) : error(404, "task list not found");
57541
+ return deleted ? json5({ deleted: true, id }) : error(404, "task list not found");
56056
57542
  }
56057
57543
  return error(405, `method ${method} not allowed on /v1/task-lists${id ? "/:id" : ""}`);
56058
57544
  }
@@ -56063,7 +57549,7 @@ async function handleV1Request(req, url, dependencies = {}) {
56063
57549
  return error(501, "dependency edge listing is not supported by this storage backend");
56064
57550
  }
56065
57551
  const dependencies2 = await store.dependencies.listAll();
56066
- return json4({ dependencies: dependencies2, count: dependencies2.length });
57552
+ return json5({ dependencies: dependencies2, count: dependencies2.length });
56067
57553
  }
56068
57554
  if (resource === "commits" && id) {
56069
57555
  if (method !== "GET")
@@ -56071,7 +57557,7 @@ async function handleV1Request(req, url, dependencies = {}) {
56071
57557
  if (!store.commits)
56072
57558
  return error(501, "commit links are not supported by this storage backend");
56073
57559
  const commit = await store.commits.find(id);
56074
- return json4({ commit: commit ?? null });
57560
+ return json5({ commit: commit ?? null });
56075
57561
  }
56076
57562
  if (resource === "refs" && id) {
56077
57563
  if (method !== "GET")
@@ -56085,7 +57571,7 @@ async function handleV1Request(req, url, dependencies = {}) {
56085
57571
  return error(400, "ref path segment has invalid percent encoding");
56086
57572
  }
56087
57573
  const refs = await store.gitRefs.find(decodedRef);
56088
- return json4({ refs, count: refs.length });
57574
+ return json5({ refs, count: refs.length });
56089
57575
  }
56090
57576
  if (resource === "next" && !id) {
56091
57577
  if (method !== "GET")
@@ -56096,8 +57582,8 @@ async function handleV1Request(req, url, dependencies = {}) {
56096
57582
  ...url.searchParams.get("task_list_id") ? { task_list_id: url.searchParams.get("task_list_id") } : {},
56097
57583
  ...url.searchParams.get("plan_id") ? { plan_id: url.searchParams.get("plan_id") } : {}
56098
57584
  };
56099
- const task2 = await store.tasks.getNext(agent, filters);
56100
- return json4({ task: task2 ?? null });
57585
+ const task3 = await store.tasks.getNext(agent, filters);
57586
+ return json5({ task: task3 ?? null });
56101
57587
  }
56102
57588
  if (resource === "stats" && method === "GET") {
56103
57589
  const [tasks, tasksAll, projects] = await Promise.all([
@@ -56105,7 +57591,7 @@ async function handleV1Request(req, url, dependencies = {}) {
56105
57591
  store.tasks.count({ include_subtasks: true }),
56106
57592
  store.projects.list()
56107
57593
  ]);
56108
- return json4({ tasks, tasks_all: tasksAll, subtasks: tasksAll - tasks, projects: projects.length });
57594
+ return json5({ tasks, tasks_all: tasksAll, subtasks: tasksAll - tasks, projects: projects.length });
56109
57595
  }
56110
57596
  if (resource === "integrity" && !id) {
56111
57597
  if (method !== "GET")
@@ -56114,7 +57600,7 @@ async function handleV1Request(req, url, dependencies = {}) {
56114
57600
  return error(501, "referential-integrity reporting is not supported by this storage backend");
56115
57601
  }
56116
57602
  const integrity = await store.integrity.report();
56117
- return json4({ integrity });
57603
+ return json5({ integrity });
56118
57604
  }
56119
57605
  if (resource === "import") {
56120
57606
  if (method !== "POST")
@@ -56131,17 +57617,17 @@ async function handleV1Request(req, url, dependencies = {}) {
56131
57617
  return error(400, "empty snapshot: provide at least one record array (tasks/projects/plans/...)");
56132
57618
  }
56133
57619
  const result = await store.sync.importSnapshot(snapshot, contextFromPrincipal(principal));
56134
- return json4({ result, received });
57620
+ return json5({ result, received });
56135
57621
  }
56136
57622
  return error(404, `unknown /v1 resource: ${resource ?? "(root)"}`);
56137
57623
  } catch (e) {
56138
57624
  if (e instanceof PlanProjectLinkError) {
56139
- 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;
56140
- return error(status, e.message, { code: e.code, conflict: status === 409, ...e.details });
57625
+ 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;
57626
+ return error(status2, e.message, { code: e.code, conflict: status2 === 409, ...e.details });
56141
57627
  }
56142
57628
  if (e instanceof ProjectTaskListEnsureError) {
56143
- 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;
56144
- return error(status, e.message, { code: e.code, conflict: status === 409, ...e.details });
57629
+ 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;
57630
+ return error(status2, e.message, { code: e.code, conflict: status2 === 409, ...e.details });
56145
57631
  }
56146
57632
  if (e instanceof TaskReferenceAmbiguousError) {
56147
57633
  return error(409, e.message, {
@@ -56165,21 +57651,22 @@ async function handleV1Request(req, url, dependencies = {}) {
56165
57651
  return error(500, e.message || "internal error");
56166
57652
  }
56167
57653
  }
56168
- var JSON_HEADERS3, DEFAULT_COMMENT_PAGE_SIZE = 100, MAX_COMMENT_PAGE_SIZE = 500, LEGACY_COMMENT_RESPONSE_LIMIT = 500, RFC3339_DATE_TIME;
57654
+ var JSON_HEADERS4, DEFAULT_COMMENT_PAGE_SIZE = 100, MAX_COMMENT_PAGE_SIZE = 500, LEGACY_COMMENT_RESPONSE_LIMIT = 500, RFC3339_DATE_TIME;
56169
57655
  var init_v1 = __esm(() => {
56170
57656
  init_types();
56171
57657
  init_cloud();
56172
57658
  init_pr_groups();
56173
57659
  init_project_registration();
57660
+ init_task_manifest();
56174
57661
  init_redaction();
56175
57662
  init_project_task_list_ensure();
56176
57663
  init_plan_project_link();
56177
- JSON_HEADERS3 = { "Content-Type": "application/json" };
57664
+ JSON_HEADERS4 = { "Content-Type": "application/json" };
56178
57665
  RFC3339_DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})[Tt]\d{2}:\d{2}:\d{2}(\.\d+)?([Zz]|[+-]\d{2}:\d{2})$/;
56179
57666
  });
56180
57667
 
56181
57668
  // src/pr-groups/sqlite.ts
56182
- function parseJson2(value) {
57669
+ function parseJson3(value) {
56183
57670
  return JSON.parse(value);
56184
57671
  }
56185
57672
  function groupFromRow2(row) {
@@ -56206,9 +57693,9 @@ function eventFromRow2(row) {
56206
57693
  sequence: Number(row["sequence"]),
56207
57694
  pr_number: row["pr_number"] === null ? null : Number(row["pr_number"]),
56208
57695
  repair_cycle: row["repair_cycle"] === null ? null : Number(row["repair_cycle"]),
56209
- ci_proof: row["ci_proof"] ? parseJson2(String(row["ci_proof"])) : null,
56210
- cleanup_proof: row["cleanup_proof"] ? parseJson2(String(row["cleanup_proof"])) : null,
56211
- metadata: parseJson2(String(row["metadata"] ?? "{}"))
57696
+ ci_proof: row["ci_proof"] ? parseJson3(String(row["ci_proof"])) : null,
57697
+ cleanup_proof: row["cleanup_proof"] ? parseJson3(String(row["cleanup_proof"])) : null,
57698
+ metadata: parseJson3(String(row["metadata"] ?? "{}"))
56212
57699
  };
56213
57700
  }
56214
57701
 
@@ -56272,8 +57759,8 @@ class SqlitePrGroupTransaction {
56272
57759
  WHERE id = ?
56273
57760
  `).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);
56274
57761
  }
56275
- async getEventByIdempotency(groupId, key) {
56276
- const row = this.db.query("SELECT * FROM pr_group_events WHERE group_id = ? AND idempotency_key = ? LIMIT 1").get(groupId, key);
57762
+ async getEventByIdempotency(groupId, key2) {
57763
+ const row = this.db.query("SELECT * FROM pr_group_events WHERE group_id = ? AND idempotency_key = ? LIMIT 1").get(groupId, key2);
56277
57764
  return row ? eventFromRow2(row) : null;
56278
57765
  }
56279
57766
  async findEventByReceiptKey(receiptKey) {
@@ -56283,14 +57770,14 @@ class SqlitePrGroupTransaction {
56283
57770
  async findEvent(groupId, filters) {
56284
57771
  const clauses = ["group_id = ?", "event_type = ?"];
56285
57772
  const values = [groupId, filters.event_type];
56286
- for (const key of ["attempt_id", "head_sha", "outcome", "receipt_key"]) {
56287
- if (!(key in filters))
57773
+ for (const key2 of ["attempt_id", "head_sha", "outcome", "receipt_key"]) {
57774
+ if (!(key2 in filters))
56288
57775
  continue;
56289
- if (filters[key] === null)
56290
- clauses.push(`${key} IS NULL`);
57776
+ if (filters[key2] === null)
57777
+ clauses.push(`${key2} IS NULL`);
56291
57778
  else {
56292
- clauses.push(`${key} = ?`);
56293
- values.push(String(filters[key]));
57779
+ clauses.push(`${key2} = ?`);
57780
+ values.push(String(filters[key2]));
56294
57781
  }
56295
57782
  }
56296
57783
  const row = this.db.query(`SELECT * FROM pr_group_events WHERE ${clauses.join(" AND ")} ORDER BY sequence DESC LIMIT 1`).get(...values);
@@ -56385,7 +57872,7 @@ class SqlitePrGroupLedgerPersistence {
56385
57872
  }
56386
57873
  }
56387
57874
  var sqliteTransactionTails2;
56388
- var init_sqlite2 = __esm(() => {
57875
+ var init_sqlite3 = __esm(() => {
56389
57876
  sqliteTransactionTails2 = new WeakMap;
56390
57877
  });
56391
57878
 
@@ -56416,10 +57903,10 @@ function createLocalPrGroupLedger(db = getDatabase()) {
56416
57903
  var init_pr_groups2 = __esm(() => {
56417
57904
  init_database();
56418
57905
  init_ledger();
56419
- init_sqlite2();
57906
+ init_sqlite3();
56420
57907
  init_types3();
56421
57908
  init_ledger();
56422
- init_sqlite2();
57909
+ init_sqlite3();
56423
57910
  init_http_client();
56424
57911
  init_postgres();
56425
57912
  });
@@ -56430,7 +57917,7 @@ __export(exports_serve, {
56430
57917
  taskToSummary: () => taskToSummary,
56431
57918
  startServer: () => startServer,
56432
57919
  serveStaticFile: () => serveStaticFile,
56433
- json: () => json2,
57920
+ json: () => json3,
56434
57921
  checkAuth: () => checkAuth,
56435
57922
  SECURITY_HEADERS: () => SECURITY_HEADERS,
56436
57923
  MIME_TYPES: () => MIME_TYPES
@@ -56516,9 +58003,9 @@ function checkRateLimit(ip) {
56516
58003
  }
56517
58004
  return { allowed: true };
56518
58005
  }
56519
- function json2(data, status = 200, headers) {
58006
+ function json3(data, status2 = 200, headers) {
56520
58007
  return new Response(JSON.stringify(data), {
56521
- status,
58008
+ status: status2,
56522
58009
  headers: {
56523
58010
  "Content-Type": "application/json",
56524
58011
  ...SECURITY_HEADERS,
@@ -56538,29 +58025,29 @@ function serveStaticFile(filePath) {
56538
58025
  }
56539
58026
  });
56540
58027
  }
56541
- function taskToSummary(task2, fields) {
58028
+ function taskToSummary(task3, fields) {
56542
58029
  const full = {
56543
- id: task2.id,
56544
- short_id: task2.short_id,
56545
- title: task2.title,
56546
- description: task2.description,
56547
- status: task2.status,
56548
- priority: task2.priority,
56549
- project_id: task2.project_id,
56550
- plan_id: task2.plan_id,
56551
- task_list_id: task2.task_list_id,
56552
- agent_id: task2.agent_id,
56553
- assigned_to: task2.assigned_to,
56554
- working_dir: task2.working_dir,
56555
- locked_by: task2.locked_by,
56556
- tags: task2.tags,
56557
- metadata: task2.metadata,
56558
- version: task2.version,
56559
- created_at: task2.created_at,
56560
- updated_at: task2.updated_at,
56561
- completed_at: task2.completed_at,
56562
- due_at: task2.due_at,
56563
- recurrence_rule: task2.recurrence_rule
58030
+ id: task3.id,
58031
+ short_id: task3.short_id,
58032
+ title: task3.title,
58033
+ description: task3.description,
58034
+ status: task3.status,
58035
+ priority: task3.priority,
58036
+ project_id: task3.project_id,
58037
+ plan_id: task3.plan_id,
58038
+ task_list_id: task3.task_list_id,
58039
+ agent_id: task3.agent_id,
58040
+ assigned_to: task3.assigned_to,
58041
+ working_dir: task3.working_dir,
58042
+ locked_by: task3.locked_by,
58043
+ tags: task3.tags,
58044
+ metadata: task3.metadata,
58045
+ version: task3.version,
58046
+ created_at: task3.created_at,
58047
+ updated_at: task3.updated_at,
58048
+ completed_at: task3.completed_at,
58049
+ due_at: task3.due_at,
58050
+ recurrence_rule: task3.recurrence_rule
56564
58051
  };
56565
58052
  if (!fields || fields.length === 0)
56566
58053
  return full;
@@ -56661,7 +58148,7 @@ Dashboard not found at: ${dashboardDir}`);
56661
58148
  "Access-Control-Allow-Headers": "Content-Type, X-API-Key, Authorization",
56662
58149
  Vary: "Origin"
56663
58150
  } : undefined;
56664
- const jsonWithCors = (data, status = 200) => json2(data, status, corsHeaders);
58151
+ const jsonWithCors = (data, status2 = 200) => json3(data, status2, corsHeaders);
56665
58152
  if (method === "OPTIONS") {
56666
58153
  return new Response(null, {
56667
58154
  headers: corsHeaders || {
@@ -56742,13 +58229,13 @@ Dashboard not found at: ${dashboardDir}`);
56742
58229
  return res;
56743
58230
  }
56744
58231
  if (path === "/api/health" && method === "GET") {
56745
- return handleHealth(ctx, json2);
58232
+ return handleHealth(ctx, json3);
56746
58233
  }
56747
58234
  if (path === "/api/headless" && method === "GET") {
56748
- return handleHeadlessBoundary(ctx, json2);
58235
+ return handleHeadlessBoundary(ctx, json3);
56749
58236
  }
56750
58237
  if (path === "/api/stats" && method === "GET") {
56751
- return handleStats(ctx, json2);
58238
+ return handleStats(ctx, json3);
56752
58239
  }
56753
58240
  if (path === "/api/tasks" && method === "GET") {
56754
58241
  return handleListTasks(req, url, ctx, jsonWithCors, taskToSummary);
@@ -56763,16 +58250,16 @@ Dashboard not found at: ${dashboardDir}`);
56763
58250
  return handleTasksExport(req, url, ctx, jsonWithCors, taskToSummary);
56764
58251
  }
56765
58252
  if (path === "/api/tasks/bulk" && method === "POST") {
56766
- return handleTasksBulk(req, ctx, json2);
58253
+ return handleTasksBulk(req, ctx, json3);
56767
58254
  }
56768
58255
  if (path === "/api/tasks/status" && method === "GET") {
56769
- return handleTasksStatus(req, url, ctx, json2);
58256
+ return handleTasksStatus(req, url, ctx, json3);
56770
58257
  }
56771
58258
  if (path === "/api/tasks/next" && method === "GET") {
56772
58259
  return handleTasksNext(req, url, ctx, jsonWithCors, taskToSummary);
56773
58260
  }
56774
58261
  if (path === "/api/tasks/active" && method === "GET") {
56775
- return handleTasksActive(req, url, ctx, json2);
58262
+ return handleTasksActive(req, url, ctx, json3);
56776
58263
  }
56777
58264
  if (path === "/api/tasks/stale" && method === "GET") {
56778
58265
  return handleTasksStale(req, url, ctx, jsonWithCors, taskToSummary);
@@ -56785,11 +58272,11 @@ Dashboard not found at: ${dashboardDir}`);
56785
58272
  }
56786
58273
  const attachmentsMatch = path.match(/^\/api\/tasks\/([^/]+)\/attachments$/);
56787
58274
  if (attachmentsMatch && method === "GET") {
56788
- return handleTaskAttachments(attachmentsMatch[1], ctx, json2);
58275
+ return handleTaskAttachments(attachmentsMatch[1], ctx, json3);
56789
58276
  }
56790
58277
  const progressMatch = path.match(/^\/api\/tasks\/([^/]+)\/progress$/);
56791
58278
  if (progressMatch) {
56792
- const res = await handleTaskProgress(progressMatch[1], req, method, ctx, json2, url);
58279
+ const res = await handleTaskProgress(progressMatch[1], req, method, ctx, json3, url);
56793
58280
  if (res !== null)
56794
58281
  return res;
56795
58282
  }
@@ -56803,7 +58290,7 @@ Dashboard not found at: ${dashboardDir}`);
56803
58290
  return handlePatchTask(id, req, ctx, jsonWithCors, taskToSummary);
56804
58291
  }
56805
58292
  if (method === "DELETE") {
56806
- return handleDeleteTask(id, ctx, json2);
58293
+ return handleDeleteTask(id, ctx, json3);
56807
58294
  }
56808
58295
  }
56809
58296
  const startMatch = path.match(/^\/api\/tasks\/([^/]+)\/start$/);
@@ -56819,7 +58306,7 @@ Dashboard not found at: ${dashboardDir}`);
56819
58306
  return handleCompleteTask(completeMatch[1], ctx, jsonWithCors, taskToSummary);
56820
58307
  }
56821
58308
  if (path === "/api/projects" && method === "GET") {
56822
- return handleListProjects(url, ctx, json2);
58309
+ return handleListProjects(url, ctx, json3);
56823
58310
  }
56824
58311
  if (path === "/api/agents/me" && method === "GET") {
56825
58312
  return handleAgentMe(req, url, ctx, jsonWithCors, taskToSummary);
@@ -56832,92 +58319,92 @@ Dashboard not found at: ${dashboardDir}`);
56832
58319
  return handleClaimTask(req, ctx, jsonWithCors, taskToSummary);
56833
58320
  }
56834
58321
  if (path === "/api/orgs" && method === "GET") {
56835
- return handleListOrgs(ctx, json2);
58322
+ return handleListOrgs(ctx, json3);
56836
58323
  }
56837
58324
  if (path === "/api/orgs" && method === "POST") {
56838
- return handleCreateOrg(req, ctx, json2);
58325
+ return handleCreateOrg(req, ctx, json3);
56839
58326
  }
56840
58327
  const orgMatch = path.match(/^\/api\/orgs\/([^/]+)$/);
56841
58328
  if (orgMatch && method === "PATCH") {
56842
- return handleUpdateOrg(orgMatch[1], req, ctx, json2);
58329
+ return handleUpdateOrg(orgMatch[1], req, ctx, json3);
56843
58330
  }
56844
58331
  if (orgMatch && method === "DELETE") {
56845
- return handleDeleteOrg(orgMatch[1], ctx, json2);
58332
+ return handleDeleteOrg(orgMatch[1], ctx, json3);
56846
58333
  }
56847
58334
  if (path === "/api/org" && method === "GET") {
56848
- return handleOrgChart(ctx, json2);
58335
+ return handleOrgChart(ctx, json3);
56849
58336
  }
56850
58337
  const teamMatch = path.match(/^\/api\/agents\/([^/]+)\/team$/);
56851
58338
  if (teamMatch && method === "GET") {
56852
- return handleAgentTeam(teamMatch[1], ctx, json2);
58339
+ return handleAgentTeam(teamMatch[1], ctx, json3);
56853
58340
  }
56854
58341
  if (path === "/api/agents" && method === "GET") {
56855
- return handleListAgents(url, ctx, json2);
58342
+ return handleListAgents(url, ctx, json3);
56856
58343
  }
56857
58344
  if (path === "/api/projects" && method === "POST") {
56858
- return handleCreateProject(req, ctx, json2);
58345
+ return handleCreateProject(req, ctx, json3);
56859
58346
  }
56860
58347
  const projectDeleteMatch = path.match(/^\/api\/projects\/([^/]+)$/);
56861
58348
  if (projectDeleteMatch && method === "DELETE") {
56862
- return handleDeleteProject(projectDeleteMatch[1], ctx, json2);
58349
+ return handleDeleteProject(projectDeleteMatch[1], ctx, json3);
56863
58350
  }
56864
58351
  if (path === "/api/agents" && method === "POST") {
56865
- return handleRegisterAgent(req, ctx, json2);
58352
+ return handleRegisterAgent(req, ctx, json3);
56866
58353
  }
56867
58354
  const agentMatch = path.match(/^\/api\/agents\/([^/]+)$/);
56868
58355
  if (agentMatch && method === "PATCH") {
56869
- return handleUpdateAgent(agentMatch[1], req, ctx, json2);
58356
+ return handleUpdateAgent(agentMatch[1], req, ctx, json3);
56870
58357
  }
56871
58358
  if (agentMatch && method === "DELETE") {
56872
- return handleDeleteAgent(agentMatch[1], ctx, json2);
58359
+ return handleDeleteAgent(agentMatch[1], ctx, json3);
56873
58360
  }
56874
58361
  if (path === "/api/agents/bulk" && method === "POST") {
56875
- return handleBulkDeleteAgents(req, ctx, json2);
58362
+ return handleBulkDeleteAgents(req, ctx, json3);
56876
58363
  }
56877
58364
  if (path === "/api/projects/bulk" && method === "POST") {
56878
- return handleBulkDeleteProjects(req, ctx, json2);
58365
+ return handleBulkDeleteProjects(req, ctx, json3);
56879
58366
  }
56880
58367
  if (path === "/api/doctor" && method === "GET") {
56881
- return handleDoctor(ctx, json2);
58368
+ return handleDoctor(ctx, json3);
56882
58369
  }
56883
58370
  if (path === "/api/report" && method === "GET") {
56884
- return handleReport(req, url, ctx, json2);
58371
+ return handleReport(req, url, ctx, json3);
56885
58372
  }
56886
58373
  if (path === "/api/activity" && method === "GET") {
56887
- return handleActivity(req, url, ctx, json2);
58374
+ return handleActivity(req, url, ctx, json3);
56888
58375
  }
56889
58376
  const historyMatch = path.match(/^\/api\/tasks\/([^/]+)\/history$/);
56890
58377
  if (historyMatch && method === "GET") {
56891
- return handleTaskHistory(historyMatch[1], ctx, json2, url);
58378
+ return handleTaskHistory(historyMatch[1], ctx, json3, url);
56892
58379
  }
56893
58380
  if (path === "/api/webhooks" && method === "GET") {
56894
- return handleListWebhooks(ctx, json2);
58381
+ return handleListWebhooks(ctx, json3);
56895
58382
  }
56896
58383
  if (path === "/api/webhooks" && method === "POST") {
56897
- return handleCreateWebhook(req, ctx, json2);
58384
+ return handleCreateWebhook(req, ctx, json3);
56898
58385
  }
56899
58386
  const webhookMatch = path.match(/^\/api\/webhooks\/([^/]+)$/);
56900
58387
  if (webhookMatch && method === "DELETE") {
56901
- return handleDeleteWebhook(webhookMatch[1], ctx, json2);
58388
+ return handleDeleteWebhook(webhookMatch[1], ctx, json3);
56902
58389
  }
56903
58390
  if (path === "/api/templates" && method === "GET") {
56904
- return handleListTemplates(ctx, json2);
58391
+ return handleListTemplates(ctx, json3);
56905
58392
  }
56906
58393
  if (path === "/api/templates" && method === "POST") {
56907
- return handleCreateTemplate(req, ctx, json2);
58394
+ return handleCreateTemplate(req, ctx, json3);
56908
58395
  }
56909
58396
  const templateMatch = path.match(/^\/api\/templates\/([^/]+)$/);
56910
58397
  if (templateMatch && method === "DELETE") {
56911
- return handleDeleteTemplate(templateMatch[1], ctx, json2);
58398
+ return handleDeleteTemplate(templateMatch[1], ctx, json3);
56912
58399
  }
56913
58400
  if (path === "/api/plans" && method === "GET") {
56914
- return handleListPlans(url, ctx, json2);
58401
+ return handleListPlans(url, ctx, json3);
56915
58402
  }
56916
58403
  if (path === "/api/plans" && method === "POST") {
56917
- return handleCreatePlan(req, ctx, json2);
58404
+ return handleCreatePlan(req, ctx, json3);
56918
58405
  }
56919
58406
  if (path === "/api/plans/bulk" && method === "POST") {
56920
- return handleBulkDeletePlans(req, ctx, json2);
58407
+ return handleBulkDeletePlans(req, ctx, json3);
56921
58408
  }
56922
58409
  const planMatch = path.match(/^\/api\/plans\/([^/]+)$/);
56923
58410
  if (planMatch) {
@@ -56926,16 +58413,16 @@ Dashboard not found at: ${dashboardDir}`);
56926
58413
  return handleGetPlan(id, ctx, jsonWithCors, taskToSummary);
56927
58414
  }
56928
58415
  if (method === "PATCH") {
56929
- return handleUpdatePlan(id, req, ctx, json2);
58416
+ return handleUpdatePlan(id, req, ctx, json3);
56930
58417
  }
56931
58418
  if (method === "DELETE") {
56932
- return handleDeletePlan(id, ctx, json2);
58419
+ return handleDeletePlan(id, ctx, json3);
56933
58420
  }
56934
58421
  }
56935
58422
  const staticRes = handleStaticFiles(path, method, ctx, jsonWithCors, serveStaticFile);
56936
58423
  if (staticRes)
56937
58424
  return staticRes;
56938
- return json2({ error: "Not found" }, 404);
58425
+ return json3({ error: "Not found" }, 404);
56939
58426
  }
56940
58427
  });
56941
58428
  const shutdown = () => {
@@ -57135,52 +58622,52 @@ function resolveId(partialId, table = "tasks") {
57135
58622
  }
57136
58623
  return id;
57137
58624
  }
57138
- function formatTask(task2) {
57139
- const id = task2.short_id || task2.id.slice(0, 8);
57140
- const assigned = task2.assigned_to ? ` -> ${task2.assigned_to}` : "";
57141
- const lockState = lockDisplayState(task2.locked_by, task2.locked_at);
58625
+ function formatTask(task3) {
58626
+ const id = task3.short_id || task3.id.slice(0, 8);
58627
+ const assigned = task3.assigned_to ? ` -> ${task3.assigned_to}` : "";
58628
+ const lockState = lockDisplayState(task3.locked_by, task3.locked_at);
57142
58629
  const lock = lockState.held ? ` [locked:${lockState.holder}]` : "";
57143
- const recur = task2.recurrence_rule ? ` [\u21BB]` : "";
57144
- return `${id} ${task2.status.padEnd(11)} ${task2.priority.padEnd(8)} ${task2.title}${assigned}${lock}${recur}`;
58630
+ const recur = task3.recurrence_rule ? ` [\u21BB]` : "";
58631
+ return `${id} ${task3.status.padEnd(11)} ${task3.priority.padEnd(8)} ${task3.title}${assigned}${lock}${recur}`;
57145
58632
  }
57146
- function formatTaskDetail(task2, maxDescriptionChars) {
58633
+ function formatTaskDetail(task3, maxDescriptionChars) {
57147
58634
  const parts = [
57148
- `ID: ${task2.id}`,
57149
- `Title: ${task2.title}`,
57150
- `Status: ${task2.status}`,
57151
- `Priority: ${task2.priority}`
58635
+ `ID: ${task3.id}`,
58636
+ `Title: ${task3.title}`,
58637
+ `Status: ${task3.status}`,
58638
+ `Priority: ${task3.priority}`
57152
58639
  ];
57153
- if (task2.description) {
57154
- const desc = maxDescriptionChars && task2.description.length > maxDescriptionChars ? task2.description.slice(0, maxDescriptionChars) + "\u2026" : task2.description;
58640
+ if (task3.description) {
58641
+ const desc = maxDescriptionChars && task3.description.length > maxDescriptionChars ? task3.description.slice(0, maxDescriptionChars) + "\u2026" : task3.description;
57155
58642
  parts.push(`Description: ${desc}`);
57156
58643
  }
57157
- if (task2.assigned_to)
57158
- parts.push(`Assigned to: ${task2.assigned_to}`);
57159
- if (task2.agent_id)
57160
- parts.push(`Agent: ${task2.agent_id}`);
57161
- const detailLock = lockDisplayState(task2.locked_by, task2.locked_at);
58644
+ if (task3.assigned_to)
58645
+ parts.push(`Assigned to: ${task3.assigned_to}`);
58646
+ if (task3.agent_id)
58647
+ parts.push(`Agent: ${task3.agent_id}`);
58648
+ const detailLock = lockDisplayState(task3.locked_by, task3.locked_at);
57162
58649
  if (detailLock.held)
57163
58650
  parts.push(`Locked by: ${detailLock.holder}`);
57164
58651
  else if (detailLock.expired)
57165
58652
  parts.push(`Lock: ${formatExpiredLock(detailLock)}`);
57166
- if (task2.parent_id)
57167
- parts.push(`Parent: ${task2.parent_id}`);
57168
- if (task2.project_id)
57169
- parts.push(`Project: ${task2.project_id}`);
57170
- if (task2.plan_id)
57171
- parts.push(`Plan: ${task2.plan_id}`);
57172
- if (task2.due_at)
57173
- parts.push(`Due: ${task2.due_at.slice(0, 10)}`);
57174
- if (task2.tags.length > 0)
57175
- parts.push(`Tags: ${task2.tags.join(", ")}`);
57176
- if (task2.recurrence_rule)
57177
- parts.push(`Recurrence: ${task2.recurrence_rule}`);
57178
- if (task2.recurrence_parent_id)
57179
- parts.push(`Recurrence parent: ${task2.recurrence_parent_id}`);
57180
- parts.push(`Version: ${task2.version}`);
57181
- parts.push(`Created: ${task2.created_at}`);
57182
- if (task2.completed_at)
57183
- parts.push(`Completed: ${task2.completed_at}`);
58653
+ if (task3.parent_id)
58654
+ parts.push(`Parent: ${task3.parent_id}`);
58655
+ if (task3.project_id)
58656
+ parts.push(`Project: ${task3.project_id}`);
58657
+ if (task3.plan_id)
58658
+ parts.push(`Plan: ${task3.plan_id}`);
58659
+ if (task3.due_at)
58660
+ parts.push(`Due: ${task3.due_at.slice(0, 10)}`);
58661
+ if (task3.tags.length > 0)
58662
+ parts.push(`Tags: ${task3.tags.join(", ")}`);
58663
+ if (task3.recurrence_rule)
58664
+ parts.push(`Recurrence: ${task3.recurrence_rule}`);
58665
+ if (task3.recurrence_parent_id)
58666
+ parts.push(`Recurrence parent: ${task3.recurrence_parent_id}`);
58667
+ parts.push(`Version: ${task3.version}`);
58668
+ parts.push(`Created: ${task3.created_at}`);
58669
+ if (task3.completed_at)
58670
+ parts.push(`Completed: ${task3.completed_at}`);
57184
58671
  return parts.join(`
57185
58672
  `);
57186
58673
  }