@hasna/todos 0.15.52 → 0.17.0

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 (206) hide show
  1. package/CHANGELOG.md +1993 -0
  2. package/README.md +356 -42
  3. package/dist/ai-tools.d.ts.map +1 -1
  4. package/dist/ai.d.ts.map +1 -1
  5. package/dist/cli/cloud-router.d.ts +162 -72
  6. package/dist/cli/cloud-router.d.ts.map +1 -1
  7. package/dist/cli/commands/agent-commands.d.ts.map +1 -1
  8. package/dist/cli/commands/config-serve-commands.d.ts.map +1 -1
  9. package/dist/cli/commands/delegate.d.ts.map +1 -1
  10. package/dist/cli/commands/environment-snapshots.d.ts.map +1 -1
  11. package/dist/cli/commands/machines-api.d.ts +4 -0
  12. package/dist/cli/commands/machines-api.d.ts.map +1 -0
  13. package/dist/cli/commands/machines.d.ts.map +1 -1
  14. package/dist/cli/commands/mcp-hooks-commands.d.ts.map +1 -1
  15. package/dist/cli/commands/plan-commands.d.ts +3 -0
  16. package/dist/cli/commands/plan-commands.d.ts.map +1 -0
  17. package/dist/cli/commands/plan-template-commands.d.ts.map +1 -1
  18. package/dist/cli/commands/pr-group-commands.d.ts.map +1 -1
  19. package/dist/cli/commands/project-commands.d.ts.map +1 -1
  20. package/dist/cli/commands/query-commands.d.ts.map +1 -1
  21. package/dist/cli/commands/storage-commands.d.ts.map +1 -1
  22. package/dist/cli/commands/task-list-commands.d.ts +3 -0
  23. package/dist/cli/commands/task-list-commands.d.ts.map +1 -0
  24. package/dist/cli/commands/template-commands.d.ts +3 -0
  25. package/dist/cli/commands/template-commands.d.ts.map +1 -0
  26. package/dist/cli/helpers.d.ts.map +1 -1
  27. package/dist/cli/index.js +22072 -15949
  28. package/dist/cli/machine-api.d.ts +7 -0
  29. package/dist/cli/machine-api.d.ts.map +1 -0
  30. package/dist/cli/plan-read-api.d.ts +6 -0
  31. package/dist/cli/plan-read-api.d.ts.map +1 -0
  32. package/dist/cli/project-api.d.ts +119 -0
  33. package/dist/cli/project-api.d.ts.map +1 -0
  34. package/dist/cli/stage-a.d.ts +25 -5
  35. package/dist/cli/stage-a.d.ts.map +1 -1
  36. package/dist/cli/task-query-api.d.ts +21 -0
  37. package/dist/cli/task-query-api.d.ts.map +1 -0
  38. package/dist/cli/template-api.d.ts +7 -0
  39. package/dist/cli/template-api.d.ts.map +1 -0
  40. package/dist/cli/template-remote.d.ts +90 -0
  41. package/dist/cli/template-remote.d.ts.map +1 -0
  42. package/dist/cli-mcp-parity.d.ts.map +1 -1
  43. package/dist/contracts.js +908 -291
  44. package/dist/db/agents.d.ts +8 -0
  45. package/dist/db/agents.d.ts.map +1 -1
  46. package/dist/db/builtin-templates.d.ts +2 -33
  47. package/dist/db/builtin-templates.d.ts.map +1 -1
  48. package/dist/db/database.d.ts +6 -0
  49. package/dist/db/database.d.ts.map +1 -1
  50. package/dist/db/handoffs.d.ts.map +1 -1
  51. package/dist/db/identity-mapping.d.ts +25 -0
  52. package/dist/db/identity-mapping.d.ts.map +1 -1
  53. package/dist/db/machines.d.ts.map +1 -1
  54. package/dist/db/migrations.d.ts.map +1 -1
  55. package/dist/db/plans.d.ts.map +1 -1
  56. package/dist/db/retrospectives.d.ts.map +1 -1
  57. package/dist/db/schema.d.ts.map +1 -1
  58. package/dist/db/task-crud.d.ts.map +1 -1
  59. package/dist/db/task-lists.d.ts.map +1 -1
  60. package/dist/db/task-runs.d.ts +2 -0
  61. package/dist/db/task-runs.d.ts.map +1 -1
  62. package/dist/index.d.ts +2 -2
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +4341 -1756
  65. package/dist/lib/assignee-validation.d.ts.map +1 -1
  66. package/dist/lib/builtin-template-library.d.ts +32 -0
  67. package/dist/lib/builtin-template-library.d.ts.map +1 -0
  68. package/dist/lib/cli-reference.d.ts.map +1 -1
  69. package/dist/lib/config.d.ts +10 -13
  70. package/dist/lib/config.d.ts.map +1 -1
  71. package/dist/lib/db-backup.d.ts +1 -1
  72. package/dist/lib/db-backup.d.ts.map +1 -1
  73. package/dist/lib/env.d.ts +18 -0
  74. package/dist/lib/env.d.ts.map +1 -0
  75. package/dist/lib/environment-snapshots.d.ts.map +1 -1
  76. package/dist/lib/feature-manifest.d.ts.map +1 -1
  77. package/dist/lib/gatherer.d.ts.map +1 -1
  78. package/dist/lib/headless-boundaries.d.ts +4 -3
  79. package/dist/lib/headless-boundaries.d.ts.map +1 -1
  80. package/dist/lib/local-encryption.d.ts +12 -0
  81. package/dist/lib/local-encryption.d.ts.map +1 -1
  82. package/dist/lib/local-opt-in.d.ts +104 -0
  83. package/dist/lib/local-opt-in.d.ts.map +1 -0
  84. package/dist/lib/paths.d.ts +20 -1
  85. package/dist/lib/paths.d.ts.map +1 -1
  86. package/dist/lib/plan-artifacts.d.ts +1 -0
  87. package/dist/lib/plan-artifacts.d.ts.map +1 -1
  88. package/dist/lib/plan-client-boundary.d.ts +2 -0
  89. package/dist/lib/plan-client-boundary.d.ts.map +1 -0
  90. package/dist/lib/plan-schedule.d.ts +6 -0
  91. package/dist/lib/plan-schedule.d.ts.map +1 -0
  92. package/dist/lib/project-panel.d.ts +6 -0
  93. package/dist/lib/project-panel.d.ts.map +1 -1
  94. package/dist/lib/project-receipt.d.ts +3 -0
  95. package/dist/lib/project-receipt.d.ts.map +1 -0
  96. package/dist/lib/sandbox-profiles.d.ts.map +1 -1
  97. package/dist/lib/shared-plan-artifacts.d.ts +14 -0
  98. package/dist/lib/shared-plan-artifacts.d.ts.map +1 -0
  99. package/dist/lib/sync.d.ts.map +1 -1
  100. package/dist/lib/task-list-client-boundary.d.ts +3 -0
  101. package/dist/lib/task-list-client-boundary.d.ts.map +1 -0
  102. package/dist/lib/task-routing.d.ts.map +1 -1
  103. package/dist/lib/template-client-boundary.d.ts +2 -0
  104. package/dist/lib/template-client-boundary.d.ts.map +1 -0
  105. package/dist/mcp/index.d.ts +53 -0
  106. package/dist/mcp/index.d.ts.map +1 -1
  107. package/dist/mcp/index.js +50124 -60838
  108. package/dist/mcp/plan-api.d.ts +6 -0
  109. package/dist/mcp/plan-api.d.ts.map +1 -0
  110. package/dist/mcp/remote-authority.d.ts +49 -0
  111. package/dist/mcp/remote-authority.d.ts.map +1 -0
  112. package/dist/mcp/task-coordination-api.d.ts +15 -0
  113. package/dist/mcp/task-coordination-api.d.ts.map +1 -0
  114. package/dist/mcp/task-list-api.d.ts +8 -0
  115. package/dist/mcp/task-list-api.d.ts.map +1 -0
  116. package/dist/mcp/token-utils.d.ts.map +1 -1
  117. package/dist/mcp/tools/agents.d.ts.map +1 -1
  118. package/dist/mcp/tools/environment-snapshots.d.ts.map +1 -1
  119. package/dist/mcp/tools/machines.d.ts.map +1 -1
  120. package/dist/mcp/tools/task-adv-tools.d.ts.map +1 -1
  121. package/dist/mcp/tools/task-auto-tools.d.ts.map +1 -1
  122. package/dist/mcp/tools/task-crud.d.ts.map +1 -1
  123. package/dist/mcp/tools/task-meta-tools.d.ts.map +1 -1
  124. package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
  125. package/dist/mcp/tools/task-resources.d.ts.map +1 -1
  126. package/dist/mcp/tools/templates.d.ts.map +1 -1
  127. package/dist/mcp.d.ts +2 -2
  128. package/dist/mcp.d.ts.map +1 -1
  129. package/dist/mcp.js +45 -19
  130. package/dist/project-registration.js +3349 -645
  131. package/dist/registry.js +909 -292
  132. package/dist/release-provenance.json +6 -6
  133. package/dist/sdk/client.d.ts +53 -4
  134. package/dist/sdk/client.d.ts.map +1 -1
  135. package/dist/sdk/index.d.ts +2 -0
  136. package/dist/sdk/index.d.ts.map +1 -1
  137. package/dist/sdk/index.js +1253 -1088
  138. package/dist/sdk/resolve.d.ts +101 -0
  139. package/dist/sdk/resolve.d.ts.map +1 -0
  140. package/dist/sdk/types.d.ts +17 -2
  141. package/dist/sdk/types.d.ts.map +1 -1
  142. package/dist/sdk/v1.generated.d.ts +334 -3
  143. package/dist/sdk/v1.generated.d.ts.map +1 -1
  144. package/dist/server/auth-posture.d.ts +66 -7
  145. package/dist/server/auth-posture.d.ts.map +1 -1
  146. package/dist/server/client-ip.d.ts +39 -0
  147. package/dist/server/client-ip.d.ts.map +1 -0
  148. package/dist/server/cloud.d.ts +2 -1
  149. package/dist/server/cloud.d.ts.map +1 -1
  150. package/dist/server/index.d.ts +1 -1
  151. package/dist/server/index.js +24758 -16687
  152. package/dist/server/openapi.d.ts +2514 -469
  153. package/dist/server/openapi.d.ts.map +1 -1
  154. package/dist/server/port.d.ts +2 -2
  155. package/dist/server/port.d.ts.map +1 -1
  156. package/dist/server/rate-limit-config.d.ts +6 -0
  157. package/dist/server/rate-limit-config.d.ts.map +1 -0
  158. package/dist/server/routes.d.ts +3 -6
  159. package/dist/server/routes.d.ts.map +1 -1
  160. package/dist/server/serve.d.ts +2 -6
  161. package/dist/server/serve.d.ts.map +1 -1
  162. package/dist/server/v1.d.ts +3 -1
  163. package/dist/server/v1.d.ts.map +1 -1
  164. package/dist/storage/atomic-project-migration.d.ts +53 -0
  165. package/dist/storage/atomic-project-migration.d.ts.map +1 -0
  166. package/dist/storage/index.d.ts +4 -2
  167. package/dist/storage/index.d.ts.map +1 -1
  168. package/dist/storage/interfaces.d.ts +135 -3
  169. package/dist/storage/interfaces.d.ts.map +1 -1
  170. package/dist/storage/local-sqlite.d.ts.map +1 -1
  171. package/dist/storage/machine-registry.d.ts +36 -0
  172. package/dist/storage/machine-registry.d.ts.map +1 -0
  173. package/dist/storage/postgres-adapter.d.ts.map +1 -1
  174. package/dist/storage/postgres-sync.d.ts +2 -2
  175. package/dist/storage/postgres-sync.d.ts.map +1 -1
  176. package/dist/storage/run-artifact-creation-upload.d.ts +50 -0
  177. package/dist/storage/run-artifact-creation-upload.d.ts.map +1 -0
  178. package/dist/storage/s3-artifact-sync.d.ts +18 -0
  179. package/dist/storage/s3-artifact-sync.d.ts.map +1 -1
  180. package/dist/storage/sqlite-snapshot.d.ts +2 -0
  181. package/dist/storage/sqlite-snapshot.d.ts.map +1 -1
  182. package/dist/storage.d.ts +2 -2
  183. package/dist/storage.d.ts.map +1 -1
  184. package/dist/storage.js +3154 -350
  185. package/dist/task-manifest/postgres.d.ts +1 -1
  186. package/dist/task-manifest/sqlite.d.ts +1 -1
  187. package/dist/task-manifest.js +250 -73
  188. package/dist/task-subtree-transfer.js +198 -4
  189. package/dist/testing.d.ts +90 -29
  190. package/dist/testing.d.ts.map +1 -1
  191. package/dist/testing.js +34 -10
  192. package/dist/types/index.d.ts +43 -4
  193. package/dist/types/index.d.ts.map +1 -1
  194. package/docs/PLAN_API.md +93 -0
  195. package/docs/TASK_LIST_API.md +58 -0
  196. package/docs/TASK_QUERY_API.md +14 -0
  197. package/docs/TEMPLATE_API.md +18 -0
  198. package/docs/native-storage.md +405 -0
  199. package/package.json +18 -15
  200. package/postinstall.js +52 -1
  201. package/dashboard/dist/assets/index-BNQ4gJua.js +0 -342
  202. package/dashboard/dist/assets/index-DjzvHUWt.css +0 -1
  203. package/dashboard/dist/index.html +0 -13
  204. package/dashboard/dist/logo.jpg +0 -0
  205. package/dist/mcp/tools/code-tools.d.ts +0 -15
  206. package/dist/mcp/tools/code-tools.d.ts.map +0 -1
package/dist/sdk/index.js CHANGED
@@ -52,1292 +52,1456 @@ class TodosTimeoutError extends Error {
52
52
  }
53
53
  }
54
54
 
55
- // src/lib/config.ts
56
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
57
- import { dirname, join as join3 } from "path";
55
+ // src/sdk/resolve.ts
56
+ import {
57
+ ClientTransportConfigurationError,
58
+ resolveClientTransport,
59
+ resolveCredential
60
+ } from "@hasna/contracts/client";
58
61
 
59
- // src/lib/sync-utils.ts
60
- import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
61
- import { homedir as homedir3 } from "os";
62
-
63
- // src/lib/paths.ts
64
- import { existsSync } from "fs";
65
- import { homedir as homedir2 } from "os";
66
- import { join as join2, resolve } from "path";
67
-
68
- // node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
69
- import { homedir } from "os";
70
- import { join } from "path";
71
- var KIND_ENV = {
72
- config: "HASNA_CONFIG_HOME",
73
- data: "HASNA_DATA_HOME",
74
- state: "HASNA_STATE_HOME",
75
- cache: "HASNA_CACHE_HOME"
76
- };
77
- var APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
78
- function assertApp(app) {
79
- if (typeof app !== "string" || app.length === 0) {
80
- throw new TypeError("paths: app must be a non-empty string");
81
- }
82
- if (!APP_SLUG_RE.test(app)) {
83
- throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
84
- }
85
- }
86
- function envOf(options) {
87
- return options.env ?? process.env;
88
- }
89
- function envValue(options, kind) {
90
- const value = envOf(options)[KIND_ENV[kind]];
91
- return typeof value === "string" && value.length > 0 ? value : undefined;
92
- }
93
- function isMacOS(platform) {
94
- return platform === "darwin";
95
- }
96
- function baseDir(kind, options) {
97
- const override = envValue(options, kind);
98
- if (override)
99
- return override;
100
- const home = options.home ?? homedir();
101
- const platform = options.platform ?? process.platform;
102
- if (isMacOS(platform)) {
103
- switch (kind) {
104
- case "config":
105
- case "data":
106
- return join(home, "Library", "Application Support", "Hasna");
107
- case "cache":
108
- return join(home, "Library", "Caches", "Hasna");
109
- case "state":
110
- return join(home, "Library", "Logs", "Hasna");
111
- }
112
- }
113
- switch (kind) {
114
- case "config":
115
- return join(home, ".config", "hasna");
116
- case "data":
117
- return join(home, ".local", "share", "hasna");
118
- case "state":
119
- return join(home, ".local", "state", "hasna");
120
- case "cache":
121
- return join(home, ".cache", "hasna");
62
+ // src/project-registration/types.ts
63
+ class TodosProjectRegistrationError extends Error {
64
+ code;
65
+ details;
66
+ constructor(code, message, details = {}) {
67
+ super(message);
68
+ this.code = code;
69
+ this.details = details;
70
+ this.name = "TodosProjectRegistrationError";
122
71
  }
123
72
  }
124
- function resolvePath(kind, options) {
125
- assertApp(options.app);
126
- const appSegment = options.internal === true ? join("internal", options.app) : options.app;
127
- return join(baseDir(kind, options), appSegment);
128
- }
129
- function dataDir(options) {
130
- return resolvePath("data", options);
131
- }
132
73
 
133
- // src/lib/paths.ts
134
- function effectiveHome(env = process.env) {
135
- return env.HOME || env.USERPROFILE || homedir2();
136
- }
137
- function legacyHomeDir(env = process.env) {
138
- return join2(effectiveHome(env), ".hasna", "todos");
139
- }
140
- function resolverHome(env = process.env) {
141
- return dataDir({ app: "todos", home: effectiveHome(env), env });
142
- }
143
- function adoptResolverHome(resolved, env = process.env) {
144
- const dataOverride = env.HASNA_DATA_HOME;
145
- if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
146
- return true;
147
- return existsSync(join2(resolved, "todos.db")) || existsSync(join2(resolved, "config.json"));
74
+ // src/project-registration/adoption-validation.ts
75
+ var VALIDATION_KEYS = [
76
+ "valid",
77
+ "resource_kind",
78
+ "target_id",
79
+ "source_receipt_id",
80
+ "accepted_receipt_id",
81
+ "source_outcome",
82
+ "created_at",
83
+ "current_revision",
84
+ "accepted_result_digest"
85
+ ];
86
+ function isRecord(value) {
87
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
148
88
  }
149
- function getTodosDir(env = process.env) {
150
- const resolved = resolverHome(env);
151
- return resolve(adoptResolverHome(resolved, env) ? resolved : legacyHomeDir(env));
89
+ function isNonEmptyString(value) {
90
+ return typeof value === "string" && value.length > 0;
152
91
  }
153
-
154
- // src/lib/sync-utils.ts
155
- function getHomeDir() {
156
- return process.env["HOME"] || process.env["USERPROFILE"] || homedir3();
92
+ function hasExactKeys(value, expected) {
93
+ const actual = Object.keys(value).sort();
94
+ const wanted = [...expected].sort();
95
+ return actual.length === wanted.length && actual.every((key, index) => key === wanted[index]);
157
96
  }
158
- var HOME = getHomeDir();
159
- function getTodosGlobalDir() {
160
- return getTodosDir();
97
+ function adoptionRejected(message) {
98
+ throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_ADOPTION_REJECTED", `TODOS_PROJECT_REGISTRATION_ADOPTION_REJECTED: ${message}`);
161
99
  }
162
- function readJsonFile(path) {
163
- try {
164
- return JSON.parse(readFileSync(path, "utf-8"));
165
- } catch {
166
- return null;
100
+ function assertTodosPriorRegistrationAdoptionValidationEnvelope(value, input) {
101
+ if (!isRecord(input) || !hasExactKeys(input, [
102
+ "source_request",
103
+ "source_receipt",
104
+ "current_record"
105
+ ])) {
106
+ adoptionRejected("prior-adoption validation input is incomplete");
167
107
  }
108
+ const request = input["source_request"];
109
+ const receipt = input["source_receipt"];
110
+ const current = input["current_record"];
111
+ if (!isRecord(request) || !isRecord(receipt) || !isRecord(current)) {
112
+ adoptionRejected("prior-adoption validation input records are incomplete");
113
+ }
114
+ const resourceKind = request["resource_kind"];
115
+ const sourceOutcome = receipt["outcome"];
116
+ const acceptedReceiptId = sourceOutcome === "accepted" ? receipt["receipt_id"] : sourceOutcome === "duplicate_of_accepted" ? receipt["duplicate_of_receipt_id"] : null;
117
+ if (resourceKind !== "project" && resourceKind !== "task_list" || request["direction"] !== "forward" || sourceOutcome !== "accepted" && sourceOutcome !== "duplicate_of_accepted" || !isNonEmptyString(acceptedReceiptId) || !isNonEmptyString(receipt["receipt_id"]) || !isNonEmptyString(receipt["target_id"]) || !isNonEmptyString(receipt["result_revision"]) || !isNonEmptyString(receipt["result_digest"]) || !isNonEmptyString(current["id"]) || !isNonEmptyString(current["created_at"]) || !isNonEmptyString(current["updated_at"]) || receipt["authority"] !== "todos" || receipt["route"] !== request["authority_route"] || receipt["package_version"] !== request["package_version"] || receipt["authority_id"] !== request["authority_id"] || receipt["tenant_id"] !== request["tenant_id"] || receipt["corpus_id"] !== request["corpus_id"] || receipt["operation_id"] !== request["operation_id"] || receipt["step_id"] !== request["step_id"] || receipt["resource_kind"] !== resourceKind || receipt["direction"] !== "forward" || receipt["idempotency_key"] !== request["idempotency_key"] || receipt["request_digest"] !== request["request_digest"] || receipt["precondition_digest"] !== request["precondition_digest"] || receipt["accepted_receipt_id"] !== null || receipt["target_id"] !== current["id"] || receipt["result_revision"] !== current["created_at"]) {
118
+ adoptionRejected("prior-adoption validation input does not carry one complete accepted receipt and current target incarnation");
119
+ }
120
+ if (sourceOutcome === "accepted" && receipt["duplicate_of_receipt_id"] !== null || sourceOutcome === "duplicate_of_accepted" && receipt["duplicate_of_receipt_id"] !== acceptedReceiptId) {
121
+ adoptionRejected("prior-adoption validation source receipt lineage is incomplete");
122
+ }
123
+ if (!isRecord(value) || !hasExactKeys(value, ["validation"]) || !isRecord(value["validation"]) || !hasExactKeys(value["validation"], VALIDATION_KEYS)) {
124
+ adoptionRejected("prior-adoption validation response envelope is incomplete");
125
+ }
126
+ const validation = value["validation"];
127
+ if (validation["valid"] !== true || validation["resource_kind"] !== resourceKind || validation["target_id"] !== current["id"] || validation["source_receipt_id"] !== receipt["receipt_id"] || validation["accepted_receipt_id"] !== acceptedReceiptId || validation["source_outcome"] !== sourceOutcome || validation["created_at"] !== current["created_at"] || validation["current_revision"] !== current["updated_at"] || validation["accepted_result_digest"] !== receipt["result_digest"]) {
128
+ adoptionRejected("prior-adoption validation response does not prove the exact accepted receipt and current target");
129
+ }
130
+ return validation;
168
131
  }
169
132
 
170
- // src/lib/config.ts
171
- function getConfigPath() {
172
- return join3(getTodosGlobalDir(), "config.json");
173
- }
174
- var cached = null;
175
- function loadConfig() {
176
- if (cached)
177
- return cached;
178
- if (!existsSync3(getConfigPath())) {
179
- cached = {};
180
- return cached;
181
- }
182
- const config = readJsonFile(getConfigPath()) || {};
183
- if (typeof config.sync_agents === "string") {
184
- config.sync_agents = config.sync_agents.split(",").map((a) => a.trim()).filter(Boolean);
185
- }
186
- cached = config;
187
- return cached;
188
- }
189
- function normalizeApiUrl(value) {
190
- const trimmed = value?.trim();
191
- if (!trimmed)
192
- return null;
193
- return trimmed.replace(/\/+$/, "");
194
- }
195
- function getLocalApiConfig(env = process.env) {
196
- const config = loadConfig();
197
- const envApiUrl = normalizeApiUrl(env["TODOS_URL"]);
198
- const configApiUrl = normalizeApiUrl(config.apiUrl);
199
- const apiUrl = envApiUrl ?? configApiUrl;
200
- const apiKey = env["TODOS_API_KEY"] || config.apiKey || null;
201
- return {
202
- apiUrl,
203
- apiKey,
204
- source: {
205
- apiUrl: envApiUrl ? "TODOS_URL" : configApiUrl ? "config" : "none",
206
- apiKey: env["TODOS_API_KEY"] ? "TODOS_API_KEY" : config.apiKey ? "config" : "none"
207
- }
208
- };
209
- }
210
-
211
- // src/sdk/client.ts
212
- function buildQuery(params) {
213
- const search = new URLSearchParams;
214
- for (const [k, v] of Object.entries(params)) {
215
- if (v !== undefined)
216
- search.set(k, Array.isArray(v) ? v.join(",") : String(v));
133
+ // src/sdk/v1.generated.ts
134
+ class ApiError extends Error {
135
+ status;
136
+ body;
137
+ constructor(status, message, body) {
138
+ super(message);
139
+ this.status = status;
140
+ this.body = body;
141
+ this.name = "ApiError";
217
142
  }
218
- const s = search.toString();
219
- return s ? `?${s}` : "";
220
143
  }
221
144
 
222
- class TasksResource {
223
- client;
224
- constructor(client) {
225
- this.client = client;
145
+ class TodosV1Client {
146
+ baseUrl;
147
+ apiKey;
148
+ fetchImpl;
149
+ baseHeaders;
150
+ constructor(options) {
151
+ if (!options.baseUrl)
152
+ throw new Error("TodosV1Client requires a baseUrl.");
153
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
154
+ this.apiKey = options.apiKey;
155
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
156
+ this.baseHeaders = options.headers ?? {};
226
157
  }
227
- async list(options) {
228
- return this.client._get("/api/tasks", buildQuery(options || {}));
158
+ async request(method, path, opts) {
159
+ const url = new URL(this.baseUrl + path);
160
+ if (opts.query) {
161
+ for (const [key, value] of Object.entries(opts.query)) {
162
+ if (value === undefined || value === null)
163
+ continue;
164
+ if (Array.isArray(value)) {
165
+ for (const item of value) {
166
+ if (item !== undefined && item !== null)
167
+ url.searchParams.append(key, String(item));
168
+ }
169
+ } else {
170
+ url.searchParams.set(key, String(value));
171
+ }
172
+ }
173
+ }
174
+ const headers = { Accept: "application/json", ...this.baseHeaders, ...opts.init?.headers };
175
+ if (this.apiKey)
176
+ headers["x-api-key"] = this.apiKey;
177
+ let payload;
178
+ if (opts.body !== undefined) {
179
+ headers["Content-Type"] = "application/json";
180
+ payload = JSON.stringify(opts.body);
181
+ }
182
+ const response = await this.fetchImpl(url.toString(), { ...opts.init, method, headers, body: payload });
183
+ const text = await response.text();
184
+ const data = text ? (() => {
185
+ try {
186
+ return JSON.parse(text);
187
+ } catch {
188
+ return text;
189
+ }
190
+ })() : undefined;
191
+ if (!response.ok) {
192
+ throw new ApiError(response.status, `${method} ${path} failed: ${response.status}`, data);
193
+ }
194
+ return data;
229
195
  }
230
- async get(id, options) {
231
- return this.client._get(`/api/tasks/${id}`, buildQuery(options || {}));
196
+ async listRecentActivity(query, init) {
197
+ return this.request("GET", `/v1/activity`, {
198
+ body: undefined,
199
+ query,
200
+ init
201
+ });
232
202
  }
233
- async getWithRelations(id) {
234
- return this.client._get(`/api/tasks/${id}`, buildQuery({ format: "full" }));
203
+ async listAgents(query, init) {
204
+ return this.request("GET", `/v1/agents`, {
205
+ body: undefined,
206
+ query,
207
+ init
208
+ });
235
209
  }
236
- async create(data) {
237
- return this.client._post("/api/tasks", data);
210
+ async listDependencies(query, init) {
211
+ return this.request("GET", `/v1/dependencies`, {
212
+ body: undefined,
213
+ query,
214
+ init
215
+ });
238
216
  }
239
- async update(id, data) {
240
- return this.client._patch(`/api/tasks/${id}`, data);
217
+ async importSnapshot(body, init) {
218
+ if (body.machines !== undefined && !Array.isArray(body.machines))
219
+ throw new Error("Invalid machine snapshot array; no snapshot was posted");
220
+ if (body.machines?.length) {
221
+ let capability;
222
+ try {
223
+ capability = await this.listMachines(init);
224
+ } catch {
225
+ throw new Error("REMOTE_API_INCOMPATIBLE: machine import requires an upgraded Todos server; no snapshot was posted");
226
+ }
227
+ if (capability?.schema_version !== 2 || !Array.isArray(capability.machines) || !capability.authority || typeof capability.authority.tenant_id !== "string" || !capability.authority.tenant_id || typeof capability.authority.kid !== "string" || !capability.authority.kid)
228
+ throw new Error("REMOTE_API_INCOMPATIBLE: machine registry capability is missing; no snapshot was posted");
229
+ if (body.expected_machine_authority && (body.expected_machine_authority.kid !== capability.authority.kid || body.expected_machine_authority.tenant_id !== capability.authority.tenant_id))
230
+ throw new Error("Machine migration authority changed before POST; no snapshot was posted");
231
+ body = { ...body, expected_machine_authority: { ...capability.authority } };
232
+ }
233
+ const result = await this.request("POST", `/v1/import`, {
234
+ body,
235
+ query: undefined,
236
+ init
237
+ });
238
+ if (body.machines?.length && (result.machine_authority?.kid !== body.expected_machine_authority?.kid || result.machine_authority?.tenant_id !== body.expected_machine_authority?.tenant_id))
239
+ throw new Error("Machine receipt authority changed; preserve the source and inspect before retrying");
240
+ return result;
241
241
  }
242
- async delete(id) {
243
- return this.client._delete(`/api/tasks/${id}`);
242
+ async listMachines(init) {
243
+ return this.request("GET", `/v1/machines`, {
244
+ body: undefined,
245
+ query: undefined,
246
+ init
247
+ });
244
248
  }
245
- async start(id, agentId) {
246
- return this.client._post(`/api/tasks/${id}/start`, { agent_id: agentId });
249
+ async mutateMachineRegistry(body, init) {
250
+ return this.request("POST", `/v1/machines`, {
251
+ body,
252
+ query: undefined,
253
+ init
254
+ });
247
255
  }
248
- async complete(id, agentId) {
249
- return this.client._post(`/api/tasks/${id}/complete`, { agent_id: agentId });
256
+ async listPlans(query, init) {
257
+ return this.request("GET", `/v1/plans`, {
258
+ body: undefined,
259
+ query,
260
+ init
261
+ });
250
262
  }
251
- async fail(id, options) {
252
- return this.client._post(`/api/tasks/${id}/fail`, options || {});
263
+ async createPlan(body, init) {
264
+ return this.request("POST", `/v1/plans`, {
265
+ body,
266
+ query: undefined,
267
+ init
268
+ });
253
269
  }
254
- async logProgress(taskId, message, pctComplete, agentId) {
255
- return this.client._post(`/api/tasks/${taskId}/progress`, {
256
- message,
257
- pct_complete: pctComplete,
258
- agent_id: agentId
270
+ async getPlan(id, init) {
271
+ return this.request("GET", `/v1/plans/${encodeURIComponent(String(id))}`, {
272
+ body: undefined,
273
+ query: undefined,
274
+ init
259
275
  });
260
276
  }
261
- async getProgress(id, options) {
262
- return this.client._get(`/api/tasks/${id}/progress`, buildQuery(options || {}));
277
+ async deletePlan(id, init) {
278
+ return this.request("DELETE", `/v1/plans/${encodeURIComponent(String(id))}`, {
279
+ body: undefined,
280
+ query: undefined,
281
+ init
282
+ });
263
283
  }
264
- async getHistory(id, options) {
265
- return this.client._get(`/api/tasks/${id}/history`, buildQuery(options || {}));
284
+ async updatePlan(id, body, init) {
285
+ return this.request("PATCH", `/v1/plans/${encodeURIComponent(String(id))}`, {
286
+ body,
287
+ query: undefined,
288
+ init
289
+ });
266
290
  }
267
- async getAttachments(id) {
268
- return this.client._get(`/api/tasks/${id}/attachments`);
291
+ async listPlanComments(id, query, init) {
292
+ return this.request("GET", `/v1/plans/${encodeURIComponent(String(id))}/comments`, {
293
+ body: undefined,
294
+ query,
295
+ init
296
+ });
269
297
  }
270
- async status(options) {
271
- return this.client._get("/api/tasks/status", buildQuery(options || {}));
298
+ async deletePlanPreserving(id, body, init) {
299
+ return this.request("POST", `/v1/plans/${encodeURIComponent(String(id))}/delete-preserving`, {
300
+ body,
301
+ query: undefined,
302
+ init
303
+ });
272
304
  }
273
- async next(options) {
274
- return this.client._get("/api/tasks/next", buildQuery(options || {}));
305
+ async planPlanProjectLink(id, query, init) {
306
+ return this.request("GET", `/v1/plans/${encodeURIComponent(String(id))}/project-link`, {
307
+ body: undefined,
308
+ query,
309
+ init
310
+ });
275
311
  }
276
- async active(projectId) {
277
- return this.client._get("/api/tasks/active", projectId ? `?project_id=${projectId}` : "");
278
- }
279
- async stale(options) {
280
- return this.client._get("/api/tasks/stale", buildQuery(options || {}));
281
- }
282
- async changedSince(since, projectId, options) {
283
- return this.client._get("/api/tasks/changed", buildQuery({ since, project_id: projectId, fields: options?.fields }));
312
+ async applyPlanProjectLink(id, body, init) {
313
+ return this.request("POST", `/v1/plans/${encodeURIComponent(String(id))}/project-link`, {
314
+ body,
315
+ query: undefined,
316
+ init
317
+ });
284
318
  }
285
- async context(options) {
286
- const q = buildQuery({
287
- agent_id: options?.agentId,
288
- project_id: options?.projectId,
289
- format: options?.format,
290
- fields: options?.fields
319
+ async rollbackPlanProjectLink(id, body, init) {
320
+ return this.request("POST", `/v1/plans/${encodeURIComponent(String(id))}/project-link/rollback`, {
321
+ body,
322
+ query: undefined,
323
+ init
291
324
  });
292
- const url = `${this.client.baseUrl}/api/tasks/context${q}`;
293
- const res = await this.client._fetchRaw(url);
294
- if (!res.ok)
295
- return options?.format === "json" ? {} : "";
296
- if (options?.format === "json")
297
- return res.json();
298
- return res.text();
299
325
  }
300
- async export(options) {
301
- const q = buildQuery(options || {});
302
- const url = `${this.client.baseUrl}/api/tasks/export${q}`;
303
- const res = await this.client._fetchRaw(url);
304
- if (!res.ok)
305
- throw new TodosAPIError("Export failed", res.status, res.statusText, null);
306
- if (options?.format === "csv")
307
- return res.text();
308
- return res.json();
326
+ async admitPrGroup(body, init) {
327
+ return this.request("POST", `/v1/pr-groups/admit`, {
328
+ body,
329
+ query: undefined,
330
+ init
331
+ });
309
332
  }
310
- async bulk(ids, action) {
311
- return this.client._post("/api/tasks/bulk", { ids, action });
333
+ async getPrGroupState(id, init) {
334
+ return this.request("GET", `/v1/pr-groups/${encodeURIComponent(String(id))}`, {
335
+ body: undefined,
336
+ query: undefined,
337
+ init
338
+ });
312
339
  }
313
- async claim(agentId, projectId) {
314
- return this.client._post("/api/tasks/claim", { agent_id: agentId, project_id: projectId });
340
+ async getPrGroupEvents(id, query, init) {
341
+ return this.request("GET", `/v1/pr-groups/${encodeURIComponent(String(id))}/events`, {
342
+ body: undefined,
343
+ query,
344
+ init
345
+ });
315
346
  }
316
- async* subscribe(options = {}) {
317
- const q = buildQuery({
318
- agent_id: options.agentId,
319
- project_id: options.projectId,
320
- events: options.events?.join(",")
347
+ async appendPrGroupEvent(id, body, init) {
348
+ return this.request("POST", `/v1/pr-groups/${encodeURIComponent(String(id))}/events`, {
349
+ body,
350
+ query: undefined,
351
+ init
321
352
  });
322
- const url = `${this.client.baseUrl}/api/tasks/stream${q}`;
323
- const resp = await this.client._fetchRaw(url);
324
- if (!resp.ok || !resp.body)
325
- throw new Error(`SSE connection failed: ${resp.status}`);
326
- const reader = resp.body.getReader();
327
- const decoder = new TextDecoder;
328
- let buffer = "";
329
- try {
330
- while (true) {
331
- const { done, value } = await reader.read();
332
- if (done)
333
- break;
334
- buffer += decoder.decode(value, { stream: true });
335
- const lines = buffer.split(`
336
- `);
337
- buffer = lines.pop() || "";
338
- for (const line of lines) {
339
- if (line.startsWith("data: ")) {
340
- try {
341
- const data = JSON.parse(line.slice(6));
342
- if (data.type !== "connected")
343
- yield data;
344
- } catch {}
345
- }
346
- }
347
- }
348
- } finally {
349
- reader.releaseLock();
350
- }
351
353
  }
352
- }
353
-
354
- class AgentsResource {
355
- client;
356
- constructor(client) {
357
- this.client = client;
354
+ async recoverPrGroup(id, body, init) {
355
+ return this.request("POST", `/v1/pr-groups/${encodeURIComponent(String(id))}/recover`, {
356
+ body,
357
+ query: undefined,
358
+ init
359
+ });
358
360
  }
359
- async list(options) {
360
- return this.client._get("/api/agents", buildQuery(options || {}));
361
+ async getAtomicProjectMigrationCapability(init) {
362
+ return this.request("GET", `/v1/project-migrations`, {
363
+ body: undefined,
364
+ query: undefined,
365
+ init
366
+ });
361
367
  }
362
- async register(data) {
363
- return this.client._post("/api/agents", data);
368
+ async importAtomicProjectSnapshot(body, init) {
369
+ return this.request("POST", `/v1/project-migrations`, {
370
+ body,
371
+ query: undefined,
372
+ init
373
+ });
364
374
  }
365
- async fullRegister(data) {
366
- return this.client._post("/api/agents", data);
375
+ async getProjectRegistrationCapability(init) {
376
+ return this.request("GET", `/v1/project-registration/capability`, {
377
+ body: undefined,
378
+ query: undefined,
379
+ init
380
+ });
367
381
  }
368
- async update(id, data) {
369
- return this.client._patch(`/api/agents/${id}`, data);
382
+ async compensateProjectRegistrationResource(body, init) {
383
+ return this.request("POST", `/v1/project-registration/compensate`, {
384
+ body,
385
+ query: undefined,
386
+ init
387
+ });
370
388
  }
371
- async delete(id) {
372
- return this.client._delete(`/api/agents/${id}`);
389
+ async createProjectRegistrationResource(body, init) {
390
+ return this.request("POST", `/v1/project-registration/create`, {
391
+ body,
392
+ query: undefined,
393
+ init
394
+ });
373
395
  }
374
- async bulkDelete(ids) {
375
- return this.client._post("/api/agents/bulk", { ids, action: "delete" });
396
+ async readExactProjectRegistrationResource(body, init) {
397
+ return this.request("POST", `/v1/project-registration/read-exact`, {
398
+ body,
399
+ query: undefined,
400
+ init
401
+ });
376
402
  }
377
- async me(name) {
378
- return this.client._get("/api/agents/me", `?name=${encodeURIComponent(name)}`);
403
+ async lookupProjectRegistrationReceipt(body, init) {
404
+ return this.request("POST", `/v1/project-registration/receipts/lookup`, {
405
+ body,
406
+ query: undefined,
407
+ init
408
+ });
379
409
  }
380
- async queue(agentId) {
381
- return this.client._get(`/api/agents/${encodeURIComponent(agentId)}/queue`);
410
+ async listProjectRegistrationResources(query, init) {
411
+ return this.request("GET", `/v1/project-registration/resources`, {
412
+ body: undefined,
413
+ query,
414
+ init
415
+ });
382
416
  }
383
- async team(agentId) {
384
- return this.client._get(`/api/agents/${encodeURIComponent(agentId)}/team`);
417
+ async validatePriorRegistrationAdoption(body, init) {
418
+ const response = await this.request("POST", `/v1/project-registration/validate-prior-adoption`, {
419
+ body,
420
+ query: undefined,
421
+ init
422
+ });
423
+ return {
424
+ validation: assertTodosPriorRegistrationAdoptionValidationEnvelope(response, body)
425
+ };
385
426
  }
386
- async orgChart() {
387
- return this.client._get("/api/org");
427
+ async verifyInverseProjectRegistrationResource(body, init) {
428
+ return this.request("POST", `/v1/project-registration/verify-inverse`, {
429
+ body,
430
+ query: undefined,
431
+ init
432
+ });
388
433
  }
389
- }
390
-
391
- class ProjectsResource {
392
- client;
393
- constructor(client) {
394
- this.client = client;
434
+ async listProjects(init) {
435
+ return this.request("GET", `/v1/projects`, {
436
+ body: undefined,
437
+ query: undefined,
438
+ init
439
+ });
395
440
  }
396
- async list(options) {
397
- return this.client._get("/api/projects", buildQuery(options || {}));
441
+ async createProject(body, init) {
442
+ return this.request("POST", `/v1/projects`, {
443
+ body,
444
+ query: undefined,
445
+ init
446
+ });
398
447
  }
399
- async create(data) {
400
- return this.client._post("/api/projects", data);
448
+ async getProject(id, init) {
449
+ return this.request("GET", `/v1/projects/${encodeURIComponent(String(id))}`, {
450
+ body: undefined,
451
+ query: undefined,
452
+ init
453
+ });
401
454
  }
402
- async delete(id) {
403
- return this.client._delete(`/api/projects/${id}`);
455
+ async deleteProject(id, init) {
456
+ return this.request("DELETE", `/v1/projects/${encodeURIComponent(String(id))}`, {
457
+ body: undefined,
458
+ query: undefined,
459
+ init
460
+ });
404
461
  }
405
- async bulkDelete(ids) {
406
- return this.client._post("/api/projects/bulk", { ids, action: "delete" });
462
+ async updateProject(id, body, init) {
463
+ return this.request("PATCH", `/v1/projects/${encodeURIComponent(String(id))}`, {
464
+ body,
465
+ query: undefined,
466
+ init
467
+ });
407
468
  }
408
- }
409
-
410
- class PlansResource {
411
- client;
412
- constructor(client) {
413
- this.client = client;
469
+ async deleteProjectPreserving(id, body, init) {
470
+ return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/delete-preserving`, {
471
+ body,
472
+ query: undefined,
473
+ init
474
+ });
414
475
  }
415
- async list(projectId) {
416
- return this.client._get("/api/plans", projectId ? `?project_id=${projectId}` : "");
476
+ async renameProject(id, body, init) {
477
+ return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/rename`, {
478
+ body,
479
+ query: undefined,
480
+ init
481
+ });
417
482
  }
418
- async create(data) {
419
- return this.client._post("/api/plans", data);
483
+ async planProjectTaskListEnsure(id, init) {
484
+ return this.request("GET", `/v1/projects/${encodeURIComponent(String(id))}/task-list/ensure`, {
485
+ body: undefined,
486
+ query: undefined,
487
+ init
488
+ });
420
489
  }
421
- async get(id) {
422
- return this.client._get(`/api/plans/${id}`);
490
+ async ensureProjectTaskList(id, body, init) {
491
+ return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/task-list/ensure`, {
492
+ body,
493
+ query: undefined,
494
+ init
495
+ });
423
496
  }
424
- async update(id, data) {
425
- return this.client._patch(`/api/plans/${id}`, data);
497
+ async rollbackProjectTaskListEnsure(id, body, init) {
498
+ return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/task-list/rollback`, {
499
+ body,
500
+ query: undefined,
501
+ init
502
+ });
426
503
  }
427
- async delete(id) {
428
- return this.client._delete(`/api/plans/${id}`);
504
+ async findTaskGitRefs(ref, init) {
505
+ return this.request("GET", `/v1/refs/${encodeURIComponent(String(ref))}`, {
506
+ body: undefined,
507
+ query: undefined,
508
+ init
509
+ });
429
510
  }
430
- async bulkDelete(ids) {
431
- return this.client._post("/api/plans/bulk", { ids, action: "delete" });
511
+ async getStats(init) {
512
+ return this.request("GET", `/v1/stats`, {
513
+ body: undefined,
514
+ query: undefined,
515
+ init
516
+ });
432
517
  }
433
- }
434
-
435
- class OrgsResource {
436
- client;
437
- constructor(client) {
438
- this.client = client;
518
+ async listTaskLists(query, init) {
519
+ return this.request("GET", `/v1/task-lists`, {
520
+ body: undefined,
521
+ query,
522
+ init
523
+ });
439
524
  }
440
- async list() {
441
- return this.client._get("/api/orgs");
525
+ async createTaskList(body, init) {
526
+ return this.request("POST", `/v1/task-lists`, {
527
+ body,
528
+ query: undefined,
529
+ init
530
+ });
442
531
  }
443
- async create(data) {
444
- return this.client._post("/api/orgs", data);
532
+ async getTaskList(id, init) {
533
+ return this.request("GET", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
534
+ body: undefined,
535
+ query: undefined,
536
+ init
537
+ });
445
538
  }
446
- async update(id, data) {
447
- return this.client._patch(`/api/orgs/${id}`, data);
539
+ async deleteTaskList(id, init) {
540
+ return this.request("DELETE", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
541
+ body: undefined,
542
+ query: undefined,
543
+ init
544
+ });
448
545
  }
449
- async delete(id) {
450
- return this.client._delete(`/api/orgs/${id}`);
546
+ async updateTaskList(id, body, init) {
547
+ return this.request("PATCH", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
548
+ body,
549
+ query: undefined,
550
+ init
551
+ });
451
552
  }
452
- }
453
-
454
- class WebhooksResource {
455
- client;
456
- constructor(client) {
457
- this.client = client;
553
+ async deleteTaskListPreserving(id, body, init) {
554
+ return this.request("POST", `/v1/task-lists/${encodeURIComponent(String(id))}/delete-preserving`, {
555
+ body,
556
+ query: undefined,
557
+ init
558
+ });
458
559
  }
459
- async list() {
460
- return this.client._get("/api/webhooks");
560
+ async applyTaskManifest(body, init) {
561
+ return this.request("POST", `/v1/task-manifest/apply`, {
562
+ body,
563
+ query: undefined,
564
+ init
565
+ });
461
566
  }
462
- async create(data) {
463
- return this.client._post("/api/webhooks", data);
567
+ async lookupTaskManifestBinding(body, init) {
568
+ return this.request("POST", `/v1/task-manifest/bindings/lookup`, {
569
+ body,
570
+ query: undefined,
571
+ init
572
+ });
464
573
  }
465
- async delete(id) {
466
- return this.client._delete(`/api/webhooks/${id}`);
574
+ async getTaskManifestCapability(init) {
575
+ return this.request("GET", `/v1/task-manifest/capability`, {
576
+ body: undefined,
577
+ query: undefined,
578
+ init
579
+ });
467
580
  }
468
- }
469
-
470
- class TemplatesResource {
471
- client;
472
- constructor(client) {
473
- this.client = client;
581
+ async compensateTaskManifest(body, init) {
582
+ return this.request("POST", `/v1/task-manifest/compensate`, {
583
+ body,
584
+ query: undefined,
585
+ init
586
+ });
474
587
  }
475
- async list() {
476
- return this.client._get("/api/templates");
588
+ async readExactTaskManifest(body, init) {
589
+ return this.request("POST", `/v1/task-manifest/read-exact`, {
590
+ body,
591
+ query: undefined,
592
+ init
593
+ });
477
594
  }
478
- async create(data) {
479
- return this.client._post("/api/templates", data);
595
+ async applyTaskSubtreeTransfer(body, init) {
596
+ return this.request("POST", `/v1/task-subtree-transfer/apply`, {
597
+ body,
598
+ query: undefined,
599
+ init
600
+ });
480
601
  }
481
- async delete(id) {
482
- return this.client._delete(`/api/templates/${id}`);
602
+ async getTaskSubtreeTransferCapability(init) {
603
+ return this.request("GET", `/v1/task-subtree-transfer/capability`, {
604
+ body: undefined,
605
+ query: undefined,
606
+ init
607
+ });
483
608
  }
484
- }
485
-
486
- class PrGroupsResource {
487
- client;
488
- constructor(client) {
489
- this.client = client;
609
+ async inspectTaskSubtreeTransfer(body, init) {
610
+ return this.request("POST", `/v1/task-subtree-transfer/inspect`, {
611
+ body,
612
+ query: undefined,
613
+ init
614
+ });
490
615
  }
491
- async admit(input) {
492
- return this.client._post("/api/pr-groups/admit", input);
616
+ async readExactTaskSubtreeTransfer(body, init) {
617
+ return this.request("POST", `/v1/task-subtree-transfer/read-exact`, {
618
+ body,
619
+ query: undefined,
620
+ init
621
+ });
493
622
  }
494
- async get(groupId) {
495
- const result = await this.client._get(`/api/pr-groups/${encodeURIComponent(groupId)}`);
496
- return result.view;
623
+ async rollbackTaskSubtreeTransfer(body, init) {
624
+ return this.request("POST", `/v1/task-subtree-transfer/rollback`, {
625
+ body,
626
+ query: undefined,
627
+ init
628
+ });
497
629
  }
498
- async events(groupId, options = {}) {
499
- const result = await this.client._get(`/api/pr-groups/${encodeURIComponent(groupId)}/events`, buildQuery({
500
- limit: options.limit,
501
- after_sequence: options.after_sequence
502
- }));
503
- return result.history;
630
+ async listTasks(query, init) {
631
+ return this.request("GET", `/v1/tasks`, {
632
+ body: undefined,
633
+ query,
634
+ init
635
+ });
504
636
  }
505
- async append(input) {
506
- return this.client._post(`/api/pr-groups/${encodeURIComponent(input.group_id)}/events`, input);
637
+ async createTask(body, init) {
638
+ return this.request("POST", `/v1/tasks`, {
639
+ body,
640
+ query: undefined,
641
+ init
642
+ });
507
643
  }
508
- async recover(input) {
509
- return this.client._post(`/api/pr-groups/${encodeURIComponent(input.group_id)}/recover`, input);
644
+ async bulkCreateTasks(body, init) {
645
+ return this.request("POST", `/v1/tasks/bulk-create`, {
646
+ body,
647
+ query: undefined,
648
+ init
649
+ });
510
650
  }
511
- }
512
-
513
- class TodosClient {
514
- baseUrl;
515
- timeout;
516
- apiKey;
517
- maxRetries;
518
- retryDelay;
519
- tasks;
520
- agents;
521
- projects;
522
- plans;
523
- orgs;
524
- webhooks;
525
- templates;
526
- prGroups;
527
- constructor(options = {}) {
528
- const localConfig = getLocalApiConfig();
529
- this.baseUrl = normalizeApiUrl(options.baseUrl) || localConfig.apiUrl || "http://localhost:19427";
530
- this.timeout = options.timeout ?? 1e4;
531
- this.apiKey = options.apiKey || localConfig.apiKey;
532
- this.maxRetries = options.maxRetries ?? 0;
533
- this.retryDelay = options.retryDelay ?? 1000;
534
- this.tasks = new TasksResource(this);
535
- this.agents = new AgentsResource(this);
536
- this.projects = new ProjectsResource(this);
537
- this.plans = new PlansResource(this);
538
- this.orgs = new OrgsResource(this);
539
- this.webhooks = new WebhooksResource(this);
540
- this.templates = new TemplatesResource(this);
541
- this.prGroups = new PrGroupsResource(this);
651
+ async bulkDeleteTasks(body, init) {
652
+ return this.request("POST", `/v1/tasks/bulk-delete`, {
653
+ body,
654
+ query: undefined,
655
+ init
656
+ });
542
657
  }
543
- static fromEnv(apiKey) {
544
- return new TodosClient({ apiKey });
658
+ async getTask(id, init) {
659
+ return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}`, {
660
+ body: undefined,
661
+ query: undefined,
662
+ init
663
+ });
545
664
  }
546
- async _fetchRaw(url, init) {
547
- const controller = new AbortController;
548
- const timer = setTimeout(() => controller.abort(), this.timeout);
549
- try {
550
- const headers = this._buildHeaders(init?.headers);
551
- return await fetch(url, { ...init, headers, signal: controller.signal });
552
- } finally {
553
- clearTimeout(timer);
554
- }
555
- }
556
- _buildHeaders(existing) {
557
- const headers = { "Content-Type": "application/json" };
558
- if (this.apiKey)
559
- headers["x-api-key"] = this.apiKey;
560
- if (existing) {
561
- if (existing instanceof Headers) {
562
- existing.forEach((v, k) => {
563
- headers[k] = v;
564
- });
565
- } else if (Array.isArray(existing)) {
566
- for (const [k, v] of existing)
567
- headers[k] = v;
568
- } else {
569
- Object.assign(headers, existing);
570
- }
571
- }
572
- return headers;
573
- }
574
- async _fetchWithRetry(path, init) {
575
- let lastError = null;
576
- const maxAttempts = this.maxRetries + 1;
577
- for (let attempt = 0;attempt < maxAttempts; attempt++) {
578
- try {
579
- return await this._fetch(path, init);
580
- } catch (e) {
581
- lastError = e;
582
- if (e instanceof TodosAPIError && e.status < 500 && e.status !== 429)
583
- throw e;
584
- if (e instanceof TodosUnauthorizedError || e instanceof TodosNotFoundError || e instanceof TodosConflictError)
585
- throw e;
586
- if (attempt < maxAttempts - 1) {
587
- const delay = this.retryDelay * Math.pow(2, attempt);
588
- if (e instanceof TodosRateLimitError) {
589
- await this._sleep(e.retryAfter * 1000);
590
- } else {
591
- await this._sleep(delay);
592
- }
593
- }
594
- }
595
- }
596
- throw lastError || new Error("Request failed after retries");
597
- }
598
- async _fetch(path, init) {
599
- const controller = new AbortController;
600
- const timer = setTimeout(() => controller.abort(), this.timeout);
601
- try {
602
- const url = `${this.baseUrl}${path}`;
603
- const headers = this._buildHeaders(init?.headers);
604
- const res = await fetch(url, { ...init, headers, signal: controller.signal });
605
- if (!res.ok) {
606
- const body = await res.json().catch(() => ({ error: res.statusText }));
607
- const message = body.error || `HTTP ${res.status}: ${res.statusText}`;
608
- if (res.status === 401)
609
- throw new TodosUnauthorizedError(message, body);
610
- if (res.status === 404)
611
- throw new TodosNotFoundError(message, body);
612
- if (res.status === 409)
613
- throw new TodosConflictError(message, body);
614
- if (res.status === 429) {
615
- const retryAfter = parseInt(res.headers.get("retry-after") || "60", 10);
616
- throw new TodosRateLimitError(message, retryAfter, body);
617
- }
618
- throw new TodosAPIError(message, res.status, res.statusText, body);
619
- }
620
- if (res.status === 204)
621
- return null;
622
- const text = await res.text();
623
- if (text.length === 0)
624
- return null;
625
- return JSON.parse(text);
626
- } catch (e) {
627
- if (e instanceof DOMException && e.name === "AbortError") {
628
- throw new TodosTimeoutError(this.timeout);
629
- }
630
- throw e;
631
- } finally {
632
- clearTimeout(timer);
633
- }
634
- }
635
- async _get(path, query = "") {
636
- return this._fetchWithRetry(`${path}${query}`);
665
+ async deleteTask(id, init) {
666
+ return this.request("DELETE", `/v1/tasks/${encodeURIComponent(String(id))}`, {
667
+ body: undefined,
668
+ query: undefined,
669
+ init
670
+ });
637
671
  }
638
- async _post(path, body) {
639
- return this._fetchWithRetry(path, {
640
- method: "POST",
641
- body: body ? JSON.stringify(body) : undefined
672
+ async updateTask(id, body, init) {
673
+ return this.request("PATCH", `/v1/tasks/${encodeURIComponent(String(id))}`, {
674
+ body,
675
+ query: undefined,
676
+ init
642
677
  });
643
678
  }
644
- async _patch(path, body) {
645
- return this._fetchWithRetry(path, {
646
- method: "PATCH",
647
- body: JSON.stringify(body)
679
+ async listTaskComments(id, query, init) {
680
+ return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}/comments`, {
681
+ body: undefined,
682
+ query,
683
+ init
648
684
  });
649
685
  }
650
- async _delete(path) {
651
- return this._fetchWithRetry(path, { method: "DELETE" });
686
+ async createTaskComment(id, body, init) {
687
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/comments`, {
688
+ body,
689
+ query: undefined,
690
+ init
691
+ });
652
692
  }
653
- _sleep(ms) {
654
- return new Promise((resolve2) => setTimeout(resolve2, ms));
693
+ async completeTask(id, body, init) {
694
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/complete`, {
695
+ body,
696
+ query: undefined,
697
+ init
698
+ });
655
699
  }
656
- async getHealth() {
657
- return this._get("/api/health");
700
+ async failTask(id, body, init) {
701
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/fail`, {
702
+ body,
703
+ query: undefined,
704
+ init
705
+ });
658
706
  }
659
- async isAlive() {
660
- try {
661
- await this._get("/api/stats");
662
- return true;
663
- } catch {
664
- return false;
665
- }
707
+ async listTaskHistory(id, query, init) {
708
+ return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}/history`, {
709
+ body: undefined,
710
+ query,
711
+ init
712
+ });
666
713
  }
667
- async getStats() {
668
- return this._get("/api/stats");
714
+ async listTaskGitRefs(id, init) {
715
+ return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}/refs`, {
716
+ body: undefined,
717
+ query: undefined,
718
+ init
719
+ });
669
720
  }
670
- async getReport(options) {
671
- return this._get("/api/report", buildQuery({ days: options?.days, project_id: options?.projectId }));
721
+ async linkTaskGitRef(id, body, init) {
722
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/refs`, {
723
+ body,
724
+ query: undefined,
725
+ init
726
+ });
672
727
  }
673
- async doctor() {
674
- return this._get("/api/doctor");
728
+ async handoffStaleTaskLock(id, body, init) {
729
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/stale-lock-handoff`, {
730
+ body,
731
+ query: undefined,
732
+ init
733
+ });
675
734
  }
676
- async activity(limit) {
677
- return this._get("/api/activity", limit ? `?limit=${limit}` : "");
735
+ async startTask(id, init) {
736
+ return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/start`, {
737
+ body: undefined,
738
+ query: undefined,
739
+ init
740
+ });
678
741
  }
679
- async listTasks(filter = {}) {
680
- return this.tasks.list(filter);
742
+ async listTemplates(query, init) {
743
+ return this.request("GET", `/v1/templates`, {
744
+ body: undefined,
745
+ query,
746
+ init
747
+ });
681
748
  }
682
- async getTask(id, options) {
683
- return this.tasks.get(id, options);
749
+ async createTemplate(body, init) {
750
+ return this.request("POST", `/v1/templates`, {
751
+ body,
752
+ query: undefined,
753
+ init
754
+ });
684
755
  }
685
- async createTask(data) {
686
- return this.tasks.create(data);
756
+ async initializeTemplates(body, init) {
757
+ return this.request("POST", `/v1/templates/initialize`, {
758
+ body,
759
+ query: undefined,
760
+ init
761
+ });
687
762
  }
688
- async updateTask(id, data) {
689
- return this.tasks.update(id, data);
763
+ async getTemplate(id, init) {
764
+ return this.request("GET", `/v1/templates/${encodeURIComponent(String(id))}`, {
765
+ body: undefined,
766
+ query: undefined,
767
+ init
768
+ });
690
769
  }
691
- async deleteTask(id) {
692
- await this.tasks.delete(id);
770
+ async deleteTemplate(id, init) {
771
+ return this.request("DELETE", `/v1/templates/${encodeURIComponent(String(id))}`, {
772
+ body: undefined,
773
+ query: undefined,
774
+ init
775
+ });
693
776
  }
694
- async startTask(id, agentId) {
695
- return this.tasks.start(id, agentId);
777
+ async updateTemplate(id, body, init) {
778
+ return this.request("PATCH", `/v1/templates/${encodeURIComponent(String(id))}`, {
779
+ body,
780
+ query: undefined,
781
+ init
782
+ });
696
783
  }
697
- async completeTask(id, agentId) {
698
- return this.tasks.complete(id, agentId);
784
+ async getTemplateHistory(id, init) {
785
+ return this.request("GET", `/v1/templates/${encodeURIComponent(String(id))}/history`, {
786
+ body: undefined,
787
+ query: undefined,
788
+ init
789
+ });
699
790
  }
700
- async failTask(id, options = {}) {
701
- return this.tasks.fail(id, options);
791
+ }
792
+
793
+ // src/lib/local-opt-in.ts
794
+ import {
795
+ clientTransportEnvKeys,
796
+ credentialOverrideEnvKey,
797
+ credentialPointerEnvKey,
798
+ CREDENTIAL_PROFILE_ENV_KEY
799
+ } from "@hasna/contracts/client";
800
+ var TODOS_LOCAL_OPT_IN_ENV_KEYS = ["HASNA_TODOS_LOCAL", "TODOS_LOCAL"];
801
+ function isTodosLocalOptIn(env = process.env) {
802
+ return TODOS_LOCAL_OPT_IN_ENV_KEYS.some((key) => (env[key] ?? "").trim() !== "");
803
+ }
804
+ function todosAuthorityEnvKeys() {
805
+ const keys = clientTransportEnvKeys("todos");
806
+ return [
807
+ ...keys.apiUrlKeys,
808
+ ...keys.apiKeyKeys,
809
+ credentialOverrideEnvKey("todos"),
810
+ credentialPointerEnvKey("todos"),
811
+ CREDENTIAL_PROFILE_ENV_KEY
812
+ ];
813
+ }
814
+ function hasTodosEnvAuthorityIntent(env = process.env) {
815
+ return todosAuthorityEnvKeys().some((key) => (env[key] ?? "").trim() !== "");
816
+ }
817
+ function selectsTodosLocalStore(env = process.env) {
818
+ return !hasTodosEnvAuthorityIntent(env) && isTodosLocalOptIn(env);
819
+ }
820
+ function todosResolverEnv(env) {
821
+ const blanks = todosAuthorityEnvKeys().filter((key) => (key in env) && (env[key] ?? "").trim() === "");
822
+ if (blanks.length === 0)
823
+ return env;
824
+ const next = { ...env };
825
+ for (const key of blanks)
826
+ delete next[key];
827
+ return next;
828
+ }
829
+ var CONTRACTS_AMBIENT_ENVIRONMENT = Symbol.for("hasna:contracts:ambientClientEnvironment");
830
+ function isAmbientTodosEnv(env) {
831
+ if (typeof process !== "undefined" && env === process.env)
832
+ return true;
833
+ return env[CONTRACTS_AMBIENT_ENVIRONMENT] === true;
834
+ }
835
+ function todosResolverInputs(env, credentials = {}) {
836
+ const normalised = todosResolverEnv(env);
837
+ if (normalised === env)
838
+ return { env: normalised, credentials };
839
+ const keychain = { ...credentials.keychain };
840
+ if (keychain.enabled === undefined && keychain.run === undefined) {
841
+ keychain.enabled = isAmbientTodosEnv(env);
842
+ }
843
+ return { env: normalised, credentials: { ...credentials, keychain } };
844
+ }
845
+
846
+ // src/sdk/resolve.ts
847
+ var TODOS_LOCAL_SERVE_URL = "http://localhost:19427";
848
+ var TODOS_CREDENTIAL_MISSING_MESSAGE = "TODOS_CREDENTIAL_MISSING: no Hasna Todos credential resolved. Looked at " + "HASNA_TODOS_API_KEY_OVERRIDE / HASNA_PROFILE / HASNA_TODOS_API_KEY_REF, the Keychain item " + "hasna.credentials.todos.api-key, ~/.hasna/todos/config/credentials, then HASNA_TODOS_API_KEY. " + "There is no local fallback: the on-box todos-serve is opt-in only (HASNA_TODOS_LOCAL=1, alias " + "TODOS_LOCAL=1) and is disabled by default \u2014 failing closed.";
849
+ var localNoticePrinted = false;
850
+ function __resetTodosSdkLocalNotice() {
851
+ localNoticePrinted = false;
852
+ }
853
+ function stripV1(baseUrl) {
854
+ return baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "");
855
+ }
856
+ function announceLocal(notice) {
857
+ if (localNoticePrinted)
858
+ return;
859
+ localNoticePrinted = true;
860
+ const line = `todos: LOCAL mode \u2014 HASNA_TODOS_LOCAL is set and nothing configures a Todos authority; reading and ` + `writing the local todos-serve at ${TODOS_LOCAL_SERVE_URL}, not the hosted fleet. Unset it, and ` + `provide a credential via the Keychain item hasna.credentials.todos.api-key, ` + `~/.hasna/todos/config/credentials, or HASNA_TODOS_API_KEY, to work against https://api.hasna.com/todos.`;
861
+ if (notice)
862
+ notice(line);
863
+ else if (typeof process !== "undefined")
864
+ process.stderr.write(`${line}
865
+ `);
866
+ }
867
+ function resolveTodosSdkTransport(options = {}) {
868
+ const rawEnv = options.env ?? (typeof process !== "undefined" ? process.env : {});
869
+ const requestedCredentials = {
870
+ ...options.credentials,
871
+ ...options.apiKey !== undefined ? { apiKey: options.apiKey } : {}
872
+ };
873
+ const { env, credentials } = todosResolverInputs(rawEnv, requestedCredentials);
874
+ if (options.baseUrl) {
875
+ return {
876
+ mode: "http",
877
+ baseUrl: stripV1(options.baseUrl),
878
+ apiKey: options.apiKey ?? null,
879
+ apiKeySource: options.apiKey ? "explicit apiKey argument" : null,
880
+ apiUrlSource: "explicit baseUrl argument"
881
+ };
702
882
  }
703
- async logProgress(taskId, message, pctComplete, agentId) {
704
- return this.tasks.logProgress(taskId, message, pctComplete, agentId);
883
+ if (selectsTodosLocalStore(env)) {
884
+ announceLocal(options.notice);
885
+ return {
886
+ mode: "local-serve",
887
+ baseUrl: TODOS_LOCAL_SERVE_URL,
888
+ apiKey: options.apiKey ?? null,
889
+ apiKeySource: options.apiKey ? "explicit apiKey argument" : null,
890
+ apiUrlSource: "local-serve"
891
+ };
705
892
  }
706
- async getStatus(projectId, agentId) {
707
- return this.tasks.status({ project_id: projectId, agent_id: agentId });
893
+ const credential = resolveCredential("todos", env, credentials);
894
+ const chainOptions = {
895
+ credentials: credential ? { ...credentials, apiKey: credential.apiKey } : credentials
896
+ };
897
+ let resolution;
898
+ try {
899
+ resolution = resolveClientTransport("todos", env, chainOptions);
900
+ } catch (error) {
901
+ if (error instanceof ClientTransportConfigurationError && /is not set and no API key could be resolved/.test(error.message)) {
902
+ throw new Error(`${TODOS_CREDENTIAL_MISSING_MESSAGE} ${error.message}`, { cause: error });
903
+ }
904
+ throw error;
708
905
  }
709
- async getActiveWork(projectId) {
710
- const res = await this.tasks.active(projectId);
711
- return res.active;
712
- }
713
- async getTasksChangedSince(since, projectId) {
714
- return this.tasks.changedSince(since, projectId);
906
+ return {
907
+ mode: "http",
908
+ baseUrl: stripV1(resolution.baseUrl),
909
+ apiKey: credential ? credential.apiKey : null,
910
+ apiKeySource: credential ? credential.source : resolution.apiKeySource,
911
+ apiUrlSource: resolution.apiUrlSource ?? "default"
912
+ };
913
+ }
914
+ function createTodosV1Client(options = {}) {
915
+ const resolved = resolveTodosSdkTransport(options);
916
+ if (resolved.mode !== "http" || !resolved.apiKey) {
917
+ throw new Error(`${TODOS_CREDENTIAL_MISSING_MESSAGE} The /v1 client is hosted-only: the local opt-in does not apply to it.`);
918
+ }
919
+ const baseFetch = options.fetch ?? ((input, init) => fetch(input, init));
920
+ const refreshOptions = { ...options, notice: () => {} };
921
+ const fetchWithFreshCredential = (input, init) => {
922
+ const headers = {};
923
+ new Headers(init?.headers ?? {}).forEach((value, key) => {
924
+ headers[key] = value;
925
+ });
926
+ try {
927
+ const fresh = resolveTodosSdkTransport(refreshOptions).apiKey;
928
+ if (fresh)
929
+ headers["x-api-key"] = fresh;
930
+ } catch {}
931
+ return baseFetch(input, { ...init, headers });
932
+ };
933
+ return new TodosV1Client({
934
+ baseUrl: resolved.baseUrl,
935
+ apiKey: resolved.apiKey,
936
+ fetch: fetchWithFreshCredential,
937
+ ...options.headers ? { headers: options.headers } : {}
938
+ });
939
+ }
940
+
941
+ // src/sdk/client.ts
942
+ function buildQuery(params) {
943
+ const search = new URLSearchParams;
944
+ for (const [k, v] of Object.entries(params)) {
945
+ if (v !== undefined)
946
+ search.set(k, Array.isArray(v) ? v.join(",") : String(v));
715
947
  }
716
- async getStaleTasks(minutes, projectId) {
717
- return this.tasks.stale({ minutes, project_id: projectId });
948
+ const s = search.toString();
949
+ return s ? `?${s}` : "";
950
+ }
951
+
952
+ class TasksResource {
953
+ client;
954
+ constructor(client) {
955
+ this.client = client;
718
956
  }
719
- async getContext(options = {}) {
720
- return this.tasks.context(options);
957
+ async list(options) {
958
+ return this.client._get("/api/tasks", buildQuery(options || {}));
721
959
  }
722
- async exportTasks(filter = {}) {
723
- return this.tasks.export(filter);
960
+ async get(id, options) {
961
+ return this.client._get(`/api/tasks/${id}`, buildQuery(options || {}));
724
962
  }
725
- async claimNextTask(agentId, projectId) {
726
- return this.tasks.claim(agentId, projectId);
963
+ async getWithRelations(id) {
964
+ return this.client._get(`/api/tasks/${id}`, buildQuery({ format: "full" }));
727
965
  }
728
- async getTaskHistory(id, options) {
729
- return this.tasks.getHistory(id, options);
966
+ async create(data) {
967
+ return this.client._post("/api/tasks", data);
730
968
  }
731
- async getTaskAttachments(id) {
732
- return this.tasks.getAttachments(id);
969
+ async update(id, data) {
970
+ return this.client._patch(`/api/tasks/${id}`, data);
733
971
  }
734
- async getTaskProgress(id, options) {
735
- return this.tasks.getProgress(id, options);
972
+ async delete(id) {
973
+ return this.client._delete(`/api/tasks/${id}`);
736
974
  }
737
- async* subscribeToStream(options = {}) {
738
- yield* this.tasks.subscribe(options);
975
+ async start(id, agentId) {
976
+ return this.client._post(`/api/tasks/${id}/start`, { agent_id: agentId });
739
977
  }
740
- async getProjects() {
741
- return this.projects.list();
978
+ async complete(id, agentId) {
979
+ return this.client._post(`/api/tasks/${id}/complete`, { agent_id: agentId });
742
980
  }
743
- }
744
- function createClient(options) {
745
- return new TodosClient(options);
746
- }
747
- // src/project-registration/types.ts
748
- class TodosProjectRegistrationError extends Error {
749
- code;
750
- details;
751
- constructor(code, message, details = {}) {
752
- super(message);
753
- this.code = code;
754
- this.details = details;
755
- this.name = "TodosProjectRegistrationError";
981
+ async fail(id, options) {
982
+ return this.client._post(`/api/tasks/${id}/fail`, options || {});
756
983
  }
757
- }
758
-
759
- // src/project-registration/adoption-validation.ts
760
- var VALIDATION_KEYS = [
761
- "valid",
762
- "resource_kind",
763
- "target_id",
764
- "source_receipt_id",
765
- "accepted_receipt_id",
766
- "source_outcome",
767
- "created_at",
768
- "current_revision",
769
- "accepted_result_digest"
770
- ];
771
- function isRecord(value) {
772
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
773
- }
774
- function isNonEmptyString(value) {
775
- return typeof value === "string" && value.length > 0;
776
- }
777
- function hasExactKeys(value, expected) {
778
- const actual = Object.keys(value).sort();
779
- const wanted = [...expected].sort();
780
- return actual.length === wanted.length && actual.every((key, index) => key === wanted[index]);
781
- }
782
- function adoptionRejected(message) {
783
- throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_ADOPTION_REJECTED", `TODOS_PROJECT_REGISTRATION_ADOPTION_REJECTED: ${message}`);
784
- }
785
- function assertTodosPriorRegistrationAdoptionValidationEnvelope(value, input) {
786
- if (!isRecord(input) || !hasExactKeys(input, [
787
- "source_request",
788
- "source_receipt",
789
- "current_record"
790
- ])) {
791
- adoptionRejected("prior-adoption validation input is incomplete");
984
+ async logProgress(taskId, message, pctComplete, agentId) {
985
+ return this.client._post(`/api/tasks/${taskId}/progress`, {
986
+ message,
987
+ pct_complete: pctComplete,
988
+ agent_id: agentId
989
+ });
792
990
  }
793
- const request = input["source_request"];
794
- const receipt = input["source_receipt"];
795
- const current = input["current_record"];
796
- if (!isRecord(request) || !isRecord(receipt) || !isRecord(current)) {
797
- adoptionRejected("prior-adoption validation input records are incomplete");
991
+ async getProgress(id, options) {
992
+ return this.client._get(`/api/tasks/${id}/progress`, buildQuery(options || {}));
798
993
  }
799
- const resourceKind = request["resource_kind"];
800
- const sourceOutcome = receipt["outcome"];
801
- const acceptedReceiptId = sourceOutcome === "accepted" ? receipt["receipt_id"] : sourceOutcome === "duplicate_of_accepted" ? receipt["duplicate_of_receipt_id"] : null;
802
- if (resourceKind !== "project" && resourceKind !== "task_list" || request["direction"] !== "forward" || sourceOutcome !== "accepted" && sourceOutcome !== "duplicate_of_accepted" || !isNonEmptyString(acceptedReceiptId) || !isNonEmptyString(receipt["receipt_id"]) || !isNonEmptyString(receipt["target_id"]) || !isNonEmptyString(receipt["result_revision"]) || !isNonEmptyString(receipt["result_digest"]) || !isNonEmptyString(current["id"]) || !isNonEmptyString(current["created_at"]) || !isNonEmptyString(current["updated_at"]) || receipt["authority"] !== "todos" || receipt["route"] !== request["authority_route"] || receipt["package_version"] !== request["package_version"] || receipt["authority_id"] !== request["authority_id"] || receipt["tenant_id"] !== request["tenant_id"] || receipt["corpus_id"] !== request["corpus_id"] || receipt["operation_id"] !== request["operation_id"] || receipt["step_id"] !== request["step_id"] || receipt["resource_kind"] !== resourceKind || receipt["direction"] !== "forward" || receipt["idempotency_key"] !== request["idempotency_key"] || receipt["request_digest"] !== request["request_digest"] || receipt["precondition_digest"] !== request["precondition_digest"] || receipt["accepted_receipt_id"] !== null || receipt["target_id"] !== current["id"] || receipt["result_revision"] !== current["created_at"]) {
803
- adoptionRejected("prior-adoption validation input does not carry one complete accepted receipt and current target incarnation");
994
+ async getHistory(id, options) {
995
+ return this.client._get(`/api/tasks/${id}/history`, buildQuery(options || {}));
804
996
  }
805
- if (sourceOutcome === "accepted" && receipt["duplicate_of_receipt_id"] !== null || sourceOutcome === "duplicate_of_accepted" && receipt["duplicate_of_receipt_id"] !== acceptedReceiptId) {
806
- adoptionRejected("prior-adoption validation source receipt lineage is incomplete");
997
+ async getAttachments(id) {
998
+ return this.client._get(`/api/tasks/${id}/attachments`);
807
999
  }
808
- if (!isRecord(value) || !hasExactKeys(value, ["validation"]) || !isRecord(value["validation"]) || !hasExactKeys(value["validation"], VALIDATION_KEYS)) {
809
- adoptionRejected("prior-adoption validation response envelope is incomplete");
1000
+ async status(options) {
1001
+ return this.client._get("/api/tasks/status", buildQuery(options || {}));
810
1002
  }
811
- const validation = value["validation"];
812
- if (validation["valid"] !== true || validation["resource_kind"] !== resourceKind || validation["target_id"] !== current["id"] || validation["source_receipt_id"] !== receipt["receipt_id"] || validation["accepted_receipt_id"] !== acceptedReceiptId || validation["source_outcome"] !== sourceOutcome || validation["created_at"] !== current["created_at"] || validation["current_revision"] !== current["updated_at"] || validation["accepted_result_digest"] !== receipt["result_digest"]) {
813
- adoptionRejected("prior-adoption validation response does not prove the exact accepted receipt and current target");
1003
+ async next(options) {
1004
+ return this.client._get("/api/tasks/next", buildQuery(options || {}));
814
1005
  }
815
- return validation;
816
- }
817
-
818
- // src/sdk/v1.generated.ts
819
- class ApiError extends Error {
820
- status;
821
- body;
822
- constructor(status, message, body) {
823
- super(message);
824
- this.status = status;
825
- this.body = body;
826
- this.name = "ApiError";
1006
+ async active(projectId) {
1007
+ return this.client._get("/api/tasks/active", projectId ? `?project_id=${projectId}` : "");
827
1008
  }
828
- }
829
-
830
- class TodosV1Client {
831
- baseUrl;
832
- apiKey;
833
- fetchImpl;
834
- baseHeaders;
835
- constructor(options) {
836
- if (!options.baseUrl)
837
- throw new Error("TodosV1Client requires a baseUrl.");
838
- this.baseUrl = options.baseUrl.replace(/\/$/, "");
839
- this.apiKey = options.apiKey;
840
- this.fetchImpl = options.fetch ?? globalThis.fetch;
841
- this.baseHeaders = options.headers ?? {};
1009
+ async stale(options) {
1010
+ return this.client._get("/api/tasks/stale", buildQuery(options || {}));
842
1011
  }
843
- async request(method, path, opts) {
844
- const url = new URL(this.baseUrl + path);
845
- if (opts.query) {
846
- for (const [key, value] of Object.entries(opts.query)) {
847
- if (value !== undefined && value !== null)
848
- url.searchParams.set(key, String(value));
849
- }
850
- }
851
- const headers = { Accept: "application/json", ...this.baseHeaders, ...opts.init?.headers };
852
- if (this.apiKey)
853
- headers["x-api-key"] = this.apiKey;
854
- let payload;
855
- if (opts.body !== undefined) {
856
- headers["Content-Type"] = "application/json";
857
- payload = JSON.stringify(opts.body);
858
- }
859
- const response = await this.fetchImpl(url.toString(), { ...opts.init, method, headers, body: payload });
860
- const text = await response.text();
861
- const data = text ? (() => {
862
- try {
863
- return JSON.parse(text);
864
- } catch {
865
- return text;
866
- }
867
- })() : undefined;
868
- if (!response.ok) {
869
- throw new ApiError(response.status, `${method} ${path} failed: ${response.status}`, data);
870
- }
871
- return data;
1012
+ async changedSince(since, projectId, options) {
1013
+ return this.client._get("/api/tasks/changed", buildQuery({ since, project_id: projectId, fields: options?.fields }));
872
1014
  }
873
- async importSnapshot(body, init) {
874
- return this.request("POST", `/v1/import`, {
875
- body,
876
- query: undefined,
877
- init
1015
+ async context(options) {
1016
+ const q = buildQuery({
1017
+ agent_id: options?.agentId,
1018
+ project_id: options?.projectId,
1019
+ format: options?.format,
1020
+ fields: options?.fields
878
1021
  });
1022
+ const url = `${this.client.baseUrl}/api/tasks/context${q}`;
1023
+ const res = await this.client._fetchRaw(url);
1024
+ if (!res.ok)
1025
+ return options?.format === "json" ? {} : "";
1026
+ if (options?.format === "json")
1027
+ return res.json();
1028
+ return res.text();
879
1029
  }
880
- async listPlans(query, init) {
881
- return this.request("GET", `/v1/plans`, {
882
- body: undefined,
883
- query,
884
- init
885
- });
1030
+ async export(options) {
1031
+ const q = buildQuery(options || {});
1032
+ const url = `${this.client.baseUrl}/api/tasks/export${q}`;
1033
+ const res = await this.client._fetchRaw(url);
1034
+ if (!res.ok)
1035
+ throw new TodosAPIError("Export failed", res.status, res.statusText, null);
1036
+ if (options?.format === "csv")
1037
+ return res.text();
1038
+ return res.json();
886
1039
  }
887
- async createPlan(body, init) {
888
- return this.request("POST", `/v1/plans`, {
889
- body,
890
- query: undefined,
891
- init
892
- });
1040
+ async bulk(ids, action) {
1041
+ return this.client._post("/api/tasks/bulk", { ids, action });
893
1042
  }
894
- async getPlan(id, init) {
895
- return this.request("GET", `/v1/plans/${encodeURIComponent(String(id))}`, {
896
- body: undefined,
897
- query: undefined,
898
- init
899
- });
1043
+ async claim(agentId, projectId) {
1044
+ return this.client._post("/api/tasks/claim", { agent_id: agentId, project_id: projectId });
900
1045
  }
901
- async deletePlan(id, init) {
902
- return this.request("DELETE", `/v1/plans/${encodeURIComponent(String(id))}`, {
903
- body: undefined,
904
- query: undefined,
905
- init
906
- });
907
- }
908
- async updatePlan(id, body, init) {
909
- return this.request("PATCH", `/v1/plans/${encodeURIComponent(String(id))}`, {
910
- body,
911
- query: undefined,
912
- init
1046
+ async* subscribe(options = {}) {
1047
+ const q = buildQuery({
1048
+ agent_id: options.agentId,
1049
+ project_id: options.projectId,
1050
+ events: options.events?.join(",")
913
1051
  });
1052
+ const url = `${this.client.baseUrl}/api/tasks/stream${q}`;
1053
+ const resp = await this.client._fetchRaw(url);
1054
+ if (!resp.ok || !resp.body)
1055
+ throw new Error(`SSE connection failed: ${resp.status}`);
1056
+ const reader = resp.body.getReader();
1057
+ const decoder = new TextDecoder;
1058
+ let buffer = "";
1059
+ try {
1060
+ while (true) {
1061
+ const { done, value } = await reader.read();
1062
+ if (done)
1063
+ break;
1064
+ buffer += decoder.decode(value, { stream: true });
1065
+ const lines = buffer.split(`
1066
+ `);
1067
+ buffer = lines.pop() || "";
1068
+ for (const line of lines) {
1069
+ if (line.startsWith("data: ")) {
1070
+ try {
1071
+ const data = JSON.parse(line.slice(6));
1072
+ if (data.type !== "connected")
1073
+ yield data;
1074
+ } catch {}
1075
+ }
1076
+ }
1077
+ }
1078
+ } finally {
1079
+ reader.releaseLock();
1080
+ }
914
1081
  }
915
- async planPlanProjectLink(id, query, init) {
916
- return this.request("GET", `/v1/plans/${encodeURIComponent(String(id))}/project-link`, {
917
- body: undefined,
918
- query,
919
- init
920
- });
1082
+ }
1083
+
1084
+ class AgentsResource {
1085
+ client;
1086
+ constructor(client) {
1087
+ this.client = client;
921
1088
  }
922
- async applyPlanProjectLink(id, body, init) {
923
- return this.request("POST", `/v1/plans/${encodeURIComponent(String(id))}/project-link`, {
924
- body,
925
- query: undefined,
926
- init
927
- });
1089
+ async list(options) {
1090
+ return this.client._get("/api/agents", buildQuery(options || {}));
928
1091
  }
929
- async rollbackPlanProjectLink(id, body, init) {
930
- return this.request("POST", `/v1/plans/${encodeURIComponent(String(id))}/project-link/rollback`, {
931
- body,
932
- query: undefined,
933
- init
934
- });
1092
+ async register(data) {
1093
+ return this.client._post("/api/agents", data);
935
1094
  }
936
- async admitPrGroup(body, init) {
937
- return this.request("POST", `/v1/pr-groups/admit`, {
938
- body,
939
- query: undefined,
940
- init
941
- });
1095
+ async fullRegister(data) {
1096
+ return this.client._post("/api/agents", data);
942
1097
  }
943
- async getPrGroupState(id, init) {
944
- return this.request("GET", `/v1/pr-groups/${encodeURIComponent(String(id))}`, {
945
- body: undefined,
946
- query: undefined,
947
- init
948
- });
1098
+ async update(id, data) {
1099
+ return this.client._patch(`/api/agents/${id}`, data);
949
1100
  }
950
- async getPrGroupEvents(id, query, init) {
951
- return this.request("GET", `/v1/pr-groups/${encodeURIComponent(String(id))}/events`, {
952
- body: undefined,
953
- query,
954
- init
955
- });
1101
+ async delete(id) {
1102
+ return this.client._delete(`/api/agents/${id}`);
956
1103
  }
957
- async appendPrGroupEvent(id, body, init) {
958
- return this.request("POST", `/v1/pr-groups/${encodeURIComponent(String(id))}/events`, {
959
- body,
960
- query: undefined,
961
- init
962
- });
1104
+ async bulkDelete(ids) {
1105
+ return this.client._post("/api/agents/bulk", { ids, action: "delete" });
963
1106
  }
964
- async recoverPrGroup(id, body, init) {
965
- return this.request("POST", `/v1/pr-groups/${encodeURIComponent(String(id))}/recover`, {
966
- body,
967
- query: undefined,
968
- init
969
- });
1107
+ async me(name) {
1108
+ return this.client._get("/api/agents/me", `?name=${encodeURIComponent(name)}`);
970
1109
  }
971
- async getProjectRegistrationCapability(init) {
972
- return this.request("GET", `/v1/project-registration/capability`, {
973
- body: undefined,
974
- query: undefined,
975
- init
976
- });
1110
+ async queue(agentId) {
1111
+ return this.client._get(`/api/agents/${encodeURIComponent(agentId)}/queue`);
977
1112
  }
978
- async compensateProjectRegistrationResource(body, init) {
979
- return this.request("POST", `/v1/project-registration/compensate`, {
980
- body,
981
- query: undefined,
982
- init
983
- });
1113
+ async team(agentId) {
1114
+ return this.client._get(`/api/agents/${encodeURIComponent(agentId)}/team`);
984
1115
  }
985
- async createProjectRegistrationResource(body, init) {
986
- return this.request("POST", `/v1/project-registration/create`, {
987
- body,
988
- query: undefined,
989
- init
990
- });
1116
+ async orgChart() {
1117
+ return this.client._get("/api/org");
991
1118
  }
992
- async readExactProjectRegistrationResource(body, init) {
993
- return this.request("POST", `/v1/project-registration/read-exact`, {
994
- body,
995
- query: undefined,
996
- init
997
- });
1119
+ }
1120
+
1121
+ class ProjectsResource {
1122
+ client;
1123
+ constructor(client) {
1124
+ this.client = client;
998
1125
  }
999
- async lookupProjectRegistrationReceipt(body, init) {
1000
- return this.request("POST", `/v1/project-registration/receipts/lookup`, {
1001
- body,
1002
- query: undefined,
1003
- init
1004
- });
1126
+ async list(options) {
1127
+ return this.client._get("/api/projects", buildQuery(options || {}));
1005
1128
  }
1006
- async listProjectRegistrationResources(query, init) {
1007
- return this.request("GET", `/v1/project-registration/resources`, {
1008
- body: undefined,
1009
- query,
1010
- init
1011
- });
1129
+ async create(data) {
1130
+ return this.client._post("/api/projects", data);
1012
1131
  }
1013
- async validatePriorRegistrationAdoption(body, init) {
1014
- const response = await this.request("POST", `/v1/project-registration/validate-prior-adoption`, {
1015
- body,
1016
- query: undefined,
1017
- init
1018
- });
1019
- return {
1020
- validation: assertTodosPriorRegistrationAdoptionValidationEnvelope(response, body)
1021
- };
1132
+ async delete(id) {
1133
+ return this.client._delete(`/api/projects/${id}`);
1022
1134
  }
1023
- async verifyInverseProjectRegistrationResource(body, init) {
1024
- return this.request("POST", `/v1/project-registration/verify-inverse`, {
1025
- body,
1026
- query: undefined,
1027
- init
1028
- });
1135
+ async bulkDelete(ids) {
1136
+ return this.client._post("/api/projects/bulk", { ids, action: "delete" });
1029
1137
  }
1030
- async listProjects(init) {
1031
- return this.request("GET", `/v1/projects`, {
1032
- body: undefined,
1033
- query: undefined,
1034
- init
1035
- });
1138
+ }
1139
+
1140
+ class PlansResource {
1141
+ client;
1142
+ constructor(client) {
1143
+ this.client = client;
1036
1144
  }
1037
- async createProject(body, init) {
1038
- return this.request("POST", `/v1/projects`, {
1039
- body,
1040
- query: undefined,
1041
- init
1042
- });
1145
+ async list(projectId) {
1146
+ return this.client._get("/api/plans", projectId ? `?project_id=${projectId}` : "");
1043
1147
  }
1044
- async getProject(id, init) {
1045
- return this.request("GET", `/v1/projects/${encodeURIComponent(String(id))}`, {
1046
- body: undefined,
1047
- query: undefined,
1048
- init
1049
- });
1148
+ async create(data) {
1149
+ return this.client._post("/api/plans", data);
1050
1150
  }
1051
- async deleteProject(id, init) {
1052
- return this.request("DELETE", `/v1/projects/${encodeURIComponent(String(id))}`, {
1053
- body: undefined,
1054
- query: undefined,
1055
- init
1056
- });
1151
+ async get(id) {
1152
+ return this.client._get(`/api/plans/${id}`);
1057
1153
  }
1058
- async updateProject(id, body, init) {
1059
- return this.request("PATCH", `/v1/projects/${encodeURIComponent(String(id))}`, {
1060
- body,
1061
- query: undefined,
1062
- init
1063
- });
1154
+ async update(id, data) {
1155
+ return this.client._patch(`/api/plans/${id}`, data);
1064
1156
  }
1065
- async renameProject(id, body, init) {
1066
- return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/rename`, {
1067
- body,
1068
- query: undefined,
1069
- init
1070
- });
1157
+ async delete(id) {
1158
+ return this.client._delete(`/api/plans/${id}`);
1071
1159
  }
1072
- async planProjectTaskListEnsure(id, init) {
1073
- return this.request("GET", `/v1/projects/${encodeURIComponent(String(id))}/task-list/ensure`, {
1074
- body: undefined,
1075
- query: undefined,
1076
- init
1077
- });
1160
+ async bulkDelete(ids) {
1161
+ return this.client._post("/api/plans/bulk", { ids, action: "delete" });
1078
1162
  }
1079
- async ensureProjectTaskList(id, body, init) {
1080
- return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/task-list/ensure`, {
1081
- body,
1082
- query: undefined,
1083
- init
1084
- });
1163
+ }
1164
+
1165
+ class OrgsResource {
1166
+ client;
1167
+ constructor(client) {
1168
+ this.client = client;
1085
1169
  }
1086
- async rollbackProjectTaskListEnsure(id, body, init) {
1087
- return this.request("POST", `/v1/projects/${encodeURIComponent(String(id))}/task-list/rollback`, {
1088
- body,
1089
- query: undefined,
1090
- init
1091
- });
1170
+ async list() {
1171
+ return this.client._get("/api/orgs");
1092
1172
  }
1093
- async findTaskGitRefs(ref, init) {
1094
- return this.request("GET", `/v1/refs/${encodeURIComponent(String(ref))}`, {
1095
- body: undefined,
1096
- query: undefined,
1097
- init
1098
- });
1173
+ async create(data) {
1174
+ return this.client._post("/api/orgs", data);
1099
1175
  }
1100
- async getStats(init) {
1101
- return this.request("GET", `/v1/stats`, {
1102
- body: undefined,
1103
- query: undefined,
1104
- init
1105
- });
1176
+ async update(id, data) {
1177
+ return this.client._patch(`/api/orgs/${id}`, data);
1106
1178
  }
1107
- async listTaskLists(query, init) {
1108
- return this.request("GET", `/v1/task-lists`, {
1109
- body: undefined,
1110
- query,
1111
- init
1112
- });
1179
+ async delete(id) {
1180
+ return this.client._delete(`/api/orgs/${id}`);
1113
1181
  }
1114
- async createTaskList(body, init) {
1115
- return this.request("POST", `/v1/task-lists`, {
1116
- body,
1117
- query: undefined,
1118
- init
1119
- });
1182
+ }
1183
+
1184
+ class WebhooksResource {
1185
+ client;
1186
+ constructor(client) {
1187
+ this.client = client;
1120
1188
  }
1121
- async getTaskList(id, init) {
1122
- return this.request("GET", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
1123
- body: undefined,
1124
- query: undefined,
1125
- init
1189
+ async list() {
1190
+ return this.client._get("/api/webhooks");
1191
+ }
1192
+ async create(data) {
1193
+ return this.client._post("/api/webhooks", data);
1194
+ }
1195
+ async delete(id) {
1196
+ return this.client._delete(`/api/webhooks/${id}`);
1197
+ }
1198
+ }
1199
+
1200
+ class TemplatesResource {
1201
+ client;
1202
+ constructor(client) {
1203
+ this.client = client;
1204
+ }
1205
+ async list() {
1206
+ return this.client._get("/api/templates");
1207
+ }
1208
+ async create(data) {
1209
+ return this.client._post("/api/templates", data);
1210
+ }
1211
+ async delete(id) {
1212
+ return this.client._delete(`/api/templates/${id}`);
1213
+ }
1214
+ }
1215
+
1216
+ class PrGroupsResource {
1217
+ client;
1218
+ constructor(client) {
1219
+ this.client = client;
1220
+ }
1221
+ async admit(input) {
1222
+ return this.client._post("/api/pr-groups/admit", input);
1223
+ }
1224
+ async get(groupId) {
1225
+ const result = await this.client._get(`/api/pr-groups/${encodeURIComponent(groupId)}`);
1226
+ return result.view;
1227
+ }
1228
+ async events(groupId, options = {}) {
1229
+ const result = await this.client._get(`/api/pr-groups/${encodeURIComponent(groupId)}/events`, buildQuery({
1230
+ limit: options.limit,
1231
+ after_sequence: options.after_sequence
1232
+ }));
1233
+ return result.history;
1234
+ }
1235
+ async append(input) {
1236
+ return this.client._post(`/api/pr-groups/${encodeURIComponent(input.group_id)}/events`, input);
1237
+ }
1238
+ async recover(input) {
1239
+ return this.client._post(`/api/pr-groups/${encodeURIComponent(input.group_id)}/recover`, input);
1240
+ }
1241
+ }
1242
+
1243
+ class TodosClient {
1244
+ baseUrl;
1245
+ timeout;
1246
+ maxRetries;
1247
+ retryDelay;
1248
+ explicitApiKey;
1249
+ constructedApiKey;
1250
+ pinnedAuthority;
1251
+ tasks;
1252
+ agents;
1253
+ projects;
1254
+ plans;
1255
+ orgs;
1256
+ webhooks;
1257
+ templates;
1258
+ prGroups;
1259
+ constructor(options = {}) {
1260
+ const resolved = resolveTodosSdkTransport({
1261
+ ...options.baseUrl !== undefined ? { baseUrl: options.baseUrl } : {},
1262
+ ...options.apiKey !== undefined ? { apiKey: options.apiKey } : {}
1126
1263
  });
1264
+ this.baseUrl = resolved.baseUrl;
1265
+ this.timeout = options.timeout ?? 1e4;
1266
+ this.explicitApiKey = options.apiKey;
1267
+ this.constructedApiKey = resolved.apiKey;
1268
+ this.pinnedAuthority = Boolean(options.baseUrl);
1269
+ this.maxRetries = options.maxRetries ?? 0;
1270
+ this.retryDelay = options.retryDelay ?? 1000;
1271
+ this.tasks = new TasksResource(this);
1272
+ this.agents = new AgentsResource(this);
1273
+ this.projects = new ProjectsResource(this);
1274
+ this.plans = new PlansResource(this);
1275
+ this.orgs = new OrgsResource(this);
1276
+ this.webhooks = new WebhooksResource(this);
1277
+ this.templates = new TemplatesResource(this);
1278
+ this.prGroups = new PrGroupsResource(this);
1279
+ }
1280
+ static fromEnv(apiKey) {
1281
+ return new TodosClient(apiKey === undefined ? {} : { apiKey });
1282
+ }
1283
+ async _fetchRaw(url, init) {
1284
+ const controller = new AbortController;
1285
+ const timer = setTimeout(() => controller.abort(), this.timeout);
1286
+ try {
1287
+ const headers = this._buildHeaders(init?.headers);
1288
+ return await fetch(url, { ...init, headers, signal: controller.signal });
1289
+ } finally {
1290
+ clearTimeout(timer);
1291
+ }
1292
+ }
1293
+ currentApiKey() {
1294
+ if (this.explicitApiKey !== undefined)
1295
+ return this.explicitApiKey;
1296
+ if (this.pinnedAuthority)
1297
+ return this.constructedApiKey;
1298
+ try {
1299
+ const fresh = resolveTodosSdkTransport({ notice: () => {} }).apiKey;
1300
+ if (fresh)
1301
+ return fresh;
1302
+ } catch {}
1303
+ return this.constructedApiKey;
1304
+ }
1305
+ get apiKey() {
1306
+ return this.currentApiKey();
1307
+ }
1308
+ _buildHeaders(existing) {
1309
+ const headers = { "Content-Type": "application/json" };
1310
+ const apiKey = this.currentApiKey();
1311
+ if (apiKey)
1312
+ headers["x-api-key"] = apiKey;
1313
+ if (existing) {
1314
+ if (existing instanceof Headers) {
1315
+ existing.forEach((v, k) => {
1316
+ headers[k] = v;
1317
+ });
1318
+ } else if (Array.isArray(existing)) {
1319
+ for (const [k, v] of existing)
1320
+ headers[k] = v;
1321
+ } else {
1322
+ Object.assign(headers, existing);
1323
+ }
1324
+ }
1325
+ return headers;
1326
+ }
1327
+ async _fetchWithRetry(path, init) {
1328
+ let lastError = null;
1329
+ const maxAttempts = this.maxRetries + 1;
1330
+ for (let attempt = 0;attempt < maxAttempts; attempt++) {
1331
+ try {
1332
+ return await this._fetch(path, init);
1333
+ } catch (e) {
1334
+ lastError = e;
1335
+ if (e instanceof TodosAPIError && e.status < 500 && e.status !== 429)
1336
+ throw e;
1337
+ if (e instanceof TodosUnauthorizedError || e instanceof TodosNotFoundError || e instanceof TodosConflictError)
1338
+ throw e;
1339
+ if (attempt < maxAttempts - 1) {
1340
+ const delay = this.retryDelay * Math.pow(2, attempt);
1341
+ if (e instanceof TodosRateLimitError) {
1342
+ await this._sleep(e.retryAfter * 1000);
1343
+ } else {
1344
+ await this._sleep(delay);
1345
+ }
1346
+ }
1347
+ }
1348
+ }
1349
+ throw lastError || new Error("Request failed after retries");
1350
+ }
1351
+ async _fetch(path, init) {
1352
+ const controller = new AbortController;
1353
+ const timer = setTimeout(() => controller.abort(), this.timeout);
1354
+ try {
1355
+ const url = `${this.baseUrl}${path}`;
1356
+ const headers = this._buildHeaders(init?.headers);
1357
+ const res = await fetch(url, { ...init, headers, signal: controller.signal });
1358
+ if (!res.ok) {
1359
+ const body = await res.json().catch(() => ({ error: res.statusText }));
1360
+ const message = body.error || `HTTP ${res.status}: ${res.statusText}`;
1361
+ if (res.status === 401)
1362
+ throw new TodosUnauthorizedError(message, body);
1363
+ if (res.status === 404)
1364
+ throw new TodosNotFoundError(message, body);
1365
+ if (res.status === 409)
1366
+ throw new TodosConflictError(message, body);
1367
+ if (res.status === 429) {
1368
+ const retryAfter = parseInt(res.headers.get("retry-after") || "60", 10);
1369
+ throw new TodosRateLimitError(message, retryAfter, body);
1370
+ }
1371
+ throw new TodosAPIError(message, res.status, res.statusText, body);
1372
+ }
1373
+ if (res.status === 204)
1374
+ return null;
1375
+ const text = await res.text();
1376
+ if (text.length === 0)
1377
+ return null;
1378
+ return JSON.parse(text);
1379
+ } catch (e) {
1380
+ if (e instanceof DOMException && e.name === "AbortError") {
1381
+ throw new TodosTimeoutError(this.timeout);
1382
+ }
1383
+ throw e;
1384
+ } finally {
1385
+ clearTimeout(timer);
1386
+ }
1127
1387
  }
1128
- async deleteTaskList(id, init) {
1129
- return this.request("DELETE", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
1130
- body: undefined,
1131
- query: undefined,
1132
- init
1133
- });
1388
+ async _get(path, query = "") {
1389
+ return this._fetchWithRetry(`${path}${query}`);
1134
1390
  }
1135
- async updateTaskList(id, body, init) {
1136
- return this.request("PATCH", `/v1/task-lists/${encodeURIComponent(String(id))}`, {
1137
- body,
1138
- query: undefined,
1139
- init
1391
+ async _post(path, body) {
1392
+ return this._fetchWithRetry(path, {
1393
+ method: "POST",
1394
+ body: body ? JSON.stringify(body) : undefined
1140
1395
  });
1141
1396
  }
1142
- async applyTaskManifest(body, init) {
1143
- return this.request("POST", `/v1/task-manifest/apply`, {
1144
- body,
1145
- query: undefined,
1146
- init
1397
+ async _patch(path, body) {
1398
+ return this._fetchWithRetry(path, {
1399
+ method: "PATCH",
1400
+ body: JSON.stringify(body)
1147
1401
  });
1148
1402
  }
1149
- async lookupTaskManifestBinding(body, init) {
1150
- return this.request("POST", `/v1/task-manifest/bindings/lookup`, {
1151
- body,
1152
- query: undefined,
1153
- init
1154
- });
1403
+ async _delete(path) {
1404
+ return this._fetchWithRetry(path, { method: "DELETE" });
1155
1405
  }
1156
- async getTaskManifestCapability(init) {
1157
- return this.request("GET", `/v1/task-manifest/capability`, {
1158
- body: undefined,
1159
- query: undefined,
1160
- init
1161
- });
1406
+ _sleep(ms) {
1407
+ return new Promise((resolve) => setTimeout(resolve, ms));
1162
1408
  }
1163
- async compensateTaskManifest(body, init) {
1164
- return this.request("POST", `/v1/task-manifest/compensate`, {
1165
- body,
1166
- query: undefined,
1167
- init
1168
- });
1409
+ async getHealth() {
1410
+ return this._get("/api/health");
1169
1411
  }
1170
- async readExactTaskManifest(body, init) {
1171
- return this.request("POST", `/v1/task-manifest/read-exact`, {
1172
- body,
1173
- query: undefined,
1174
- init
1175
- });
1412
+ async isAlive() {
1413
+ try {
1414
+ await this._get("/api/stats");
1415
+ return true;
1416
+ } catch {
1417
+ return false;
1418
+ }
1176
1419
  }
1177
- async applyTaskSubtreeTransfer(body, init) {
1178
- return this.request("POST", `/v1/task-subtree-transfer/apply`, {
1179
- body,
1180
- query: undefined,
1181
- init
1182
- });
1420
+ async getStats() {
1421
+ return this._get("/api/stats");
1183
1422
  }
1184
- async getTaskSubtreeTransferCapability(init) {
1185
- return this.request("GET", `/v1/task-subtree-transfer/capability`, {
1186
- body: undefined,
1187
- query: undefined,
1188
- init
1189
- });
1423
+ async getReport(options) {
1424
+ return this._get("/api/report", buildQuery({ days: options?.days, project_id: options?.projectId }));
1190
1425
  }
1191
- async inspectTaskSubtreeTransfer(body, init) {
1192
- return this.request("POST", `/v1/task-subtree-transfer/inspect`, {
1193
- body,
1194
- query: undefined,
1195
- init
1196
- });
1426
+ async doctor() {
1427
+ return this._get("/api/doctor");
1197
1428
  }
1198
- async readExactTaskSubtreeTransfer(body, init) {
1199
- return this.request("POST", `/v1/task-subtree-transfer/read-exact`, {
1200
- body,
1201
- query: undefined,
1202
- init
1203
- });
1429
+ async activity(limit) {
1430
+ return this._get("/api/activity", limit ? `?limit=${limit}` : "");
1204
1431
  }
1205
- async rollbackTaskSubtreeTransfer(body, init) {
1206
- return this.request("POST", `/v1/task-subtree-transfer/rollback`, {
1207
- body,
1208
- query: undefined,
1209
- init
1210
- });
1432
+ async listTasks(filter = {}) {
1433
+ return this.tasks.list(filter);
1211
1434
  }
1212
- async listTasks(query, init) {
1213
- return this.request("GET", `/v1/tasks`, {
1214
- body: undefined,
1215
- query,
1216
- init
1217
- });
1435
+ async getTask(id, options) {
1436
+ return this.tasks.get(id, options);
1218
1437
  }
1219
- async createTask(body, init) {
1220
- return this.request("POST", `/v1/tasks`, {
1221
- body,
1222
- query: undefined,
1223
- init
1224
- });
1438
+ async createTask(data) {
1439
+ return this.tasks.create(data);
1225
1440
  }
1226
- async getTask(id, init) {
1227
- return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}`, {
1228
- body: undefined,
1229
- query: undefined,
1230
- init
1231
- });
1441
+ async updateTask(id, data) {
1442
+ return this.tasks.update(id, data);
1232
1443
  }
1233
- async deleteTask(id, init) {
1234
- return this.request("DELETE", `/v1/tasks/${encodeURIComponent(String(id))}`, {
1235
- body: undefined,
1236
- query: undefined,
1237
- init
1238
- });
1444
+ async deleteTask(id) {
1445
+ await this.tasks.delete(id);
1239
1446
  }
1240
- async updateTask(id, body, init) {
1241
- return this.request("PATCH", `/v1/tasks/${encodeURIComponent(String(id))}`, {
1242
- body,
1243
- query: undefined,
1244
- init
1245
- });
1447
+ async startTask(id, agentId) {
1448
+ return this.tasks.start(id, agentId);
1246
1449
  }
1247
- async listTaskComments(id, query, init) {
1248
- return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}/comments`, {
1249
- body: undefined,
1250
- query,
1251
- init
1252
- });
1450
+ async completeTask(id, agentId) {
1451
+ return this.tasks.complete(id, agentId);
1253
1452
  }
1254
- async createTaskComment(id, body, init) {
1255
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/comments`, {
1256
- body,
1257
- query: undefined,
1258
- init
1259
- });
1453
+ async failTask(id, options = {}) {
1454
+ return this.tasks.fail(id, options);
1260
1455
  }
1261
- async completeTask(id, body, init) {
1262
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/complete`, {
1263
- body,
1264
- query: undefined,
1265
- init
1266
- });
1456
+ async logProgress(taskId, message, pctComplete, agentId) {
1457
+ return this.tasks.logProgress(taskId, message, pctComplete, agentId);
1267
1458
  }
1268
- async failTask(id, body, init) {
1269
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/fail`, {
1270
- body,
1271
- query: undefined,
1272
- init
1273
- });
1459
+ async getStatus(projectId, agentId) {
1460
+ return this.tasks.status({ project_id: projectId, agent_id: agentId });
1274
1461
  }
1275
- async listTaskGitRefs(id, init) {
1276
- return this.request("GET", `/v1/tasks/${encodeURIComponent(String(id))}/refs`, {
1277
- body: undefined,
1278
- query: undefined,
1279
- init
1280
- });
1462
+ async getActiveWork(projectId) {
1463
+ const res = await this.tasks.active(projectId);
1464
+ return res.active;
1281
1465
  }
1282
- async linkTaskGitRef(id, body, init) {
1283
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/refs`, {
1284
- body,
1285
- query: undefined,
1286
- init
1287
- });
1466
+ async getTasksChangedSince(since, projectId) {
1467
+ return this.tasks.changedSince(since, projectId);
1288
1468
  }
1289
- async handoffStaleTaskLock(id, body, init) {
1290
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/stale-lock-handoff`, {
1291
- body,
1292
- query: undefined,
1293
- init
1294
- });
1469
+ async getStaleTasks(minutes, projectId) {
1470
+ return this.tasks.stale({ minutes, project_id: projectId });
1295
1471
  }
1296
- async startTask(id, init) {
1297
- return this.request("POST", `/v1/tasks/${encodeURIComponent(String(id))}/start`, {
1298
- body: undefined,
1299
- query: undefined,
1300
- init
1301
- });
1472
+ async getContext(options = {}) {
1473
+ return this.tasks.context(options);
1302
1474
  }
1303
- async listTemplates(query, init) {
1304
- return this.request("GET", `/v1/templates`, {
1305
- body: undefined,
1306
- query,
1307
- init
1308
- });
1475
+ async exportTasks(filter = {}) {
1476
+ return this.tasks.export(filter);
1309
1477
  }
1310
- async createTemplate(body, init) {
1311
- return this.request("POST", `/v1/templates`, {
1312
- body,
1313
- query: undefined,
1314
- init
1315
- });
1478
+ async claimNextTask(agentId, projectId) {
1479
+ return this.tasks.claim(agentId, projectId);
1316
1480
  }
1317
- async getTemplate(id, init) {
1318
- return this.request("GET", `/v1/templates/${encodeURIComponent(String(id))}`, {
1319
- body: undefined,
1320
- query: undefined,
1321
- init
1322
- });
1481
+ async getTaskHistory(id, options) {
1482
+ return this.tasks.getHistory(id, options);
1323
1483
  }
1324
- async deleteTemplate(id, init) {
1325
- return this.request("DELETE", `/v1/templates/${encodeURIComponent(String(id))}`, {
1326
- body: undefined,
1327
- query: undefined,
1328
- init
1329
- });
1484
+ async getTaskAttachments(id) {
1485
+ return this.tasks.getAttachments(id);
1330
1486
  }
1331
- async updateTemplate(id, body, init) {
1332
- return this.request("PATCH", `/v1/templates/${encodeURIComponent(String(id))}`, {
1333
- body,
1334
- query: undefined,
1335
- init
1336
- });
1487
+ async getTaskProgress(id, options) {
1488
+ return this.tasks.getProgress(id, options);
1489
+ }
1490
+ async* subscribeToStream(options = {}) {
1491
+ yield* this.tasks.subscribe(options);
1492
+ }
1493
+ async getProjects() {
1494
+ return this.projects.list();
1337
1495
  }
1338
1496
  }
1497
+ function createClient(options) {
1498
+ return new TodosClient(options);
1499
+ }
1339
1500
  export {
1501
+ resolveTodosSdkTransport,
1502
+ createTodosV1Client,
1340
1503
  createClient,
1504
+ __resetTodosSdkLocalNotice,
1341
1505
  TodosV1Client,
1342
1506
  ApiError as TodosV1ApiError,
1343
1507
  TodosUnauthorizedError,
@@ -1346,5 +1510,6 @@ export {
1346
1510
  TodosNotFoundError,
1347
1511
  TodosConflictError,
1348
1512
  TodosClient,
1349
- TodosAPIError
1513
+ TodosAPIError,
1514
+ TODOS_LOCAL_SERVE_URL
1350
1515
  };