@hasna/todos 0.15.14 → 0.15.15

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