@hasna/todos 0.15.35 → 0.15.41
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.
- package/dist/cli/cloud-router.d.ts +52 -18
- package/dist/cli/cloud-router.d.ts.map +1 -1
- package/dist/cli/commands/agent-commands.d.ts.map +1 -1
- package/dist/cli/commands/plan-template-commands.d.ts.map +1 -1
- package/dist/cli/commands/project-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-subtree-transfer-commands.d.ts +3 -0
- package/dist/cli/commands/task-subtree-transfer-commands.d.ts.map +1 -0
- package/dist/cli/index.js +3299 -796
- package/dist/cli/stage-a.d.ts.map +1 -1
- package/dist/contracts.js +62 -54
- package/dist/db/plan-comments.d.ts +14 -0
- package/dist/db/plan-comments.d.ts.map +1 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/task-crud.d.ts.map +1 -1
- package/dist/db/task-lifecycle.d.ts.map +1 -1
- package/dist/db/task-lists.d.ts.map +1 -1
- package/dist/db/task-relations.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1787 -309
- package/dist/lib/comment-cursor.d.ts +19 -0
- package/dist/lib/comment-cursor.d.ts.map +1 -1
- package/dist/lib/import-export-bridge.d.ts.map +1 -1
- package/dist/mcp/index.js +2273 -471
- package/dist/mcp.js +8 -4
- package/dist/project-registration.js +207 -63
- package/dist/registry.d.ts +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +69 -54
- package/dist/release-provenance.json +5 -5
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +35 -0
- package/dist/sdk/v1.generated.d.ts +141 -0
- package/dist/sdk/v1.generated.d.ts.map +1 -1
- package/dist/server/cloud.d.ts +12 -2
- package/dist/server/cloud.d.ts.map +1 -1
- package/dist/server/index.js +5171 -8535
- package/dist/server/openapi.d.ts +2714 -1937
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/v1.d.ts +2 -1
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage/config.d.ts +12 -30
- package/dist/storage/config.d.ts.map +1 -1
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/interfaces.d.ts +13 -2
- package/dist/storage/interfaces.d.ts.map +1 -1
- package/dist/storage/local-sqlite.d.ts.map +1 -1
- package/dist/storage/postgres-adapter.d.ts.map +1 -1
- package/dist/storage/timestamp-backfill.d.ts +62 -0
- package/dist/storage/timestamp-backfill.d.ts.map +1 -0
- package/dist/storage.d.ts +2 -2
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +199 -63
- package/dist/task-subtree-transfer/authority.d.ts +27 -0
- package/dist/task-subtree-transfer/authority.d.ts.map +1 -0
- package/dist/task-subtree-transfer/backend.d.ts +9 -0
- package/dist/task-subtree-transfer/backend.d.ts.map +1 -0
- package/dist/task-subtree-transfer/canonical.d.ts +14 -0
- package/dist/task-subtree-transfer/canonical.d.ts.map +1 -0
- package/dist/task-subtree-transfer/http.d.ts +16 -0
- package/dist/task-subtree-transfer/http.d.ts.map +1 -0
- package/dist/task-subtree-transfer/index.d.ts +6 -0
- package/dist/task-subtree-transfer/index.d.ts.map +1 -0
- package/dist/task-subtree-transfer/planner.d.ts +24 -0
- package/dist/task-subtree-transfer/planner.d.ts.map +1 -0
- package/dist/task-subtree-transfer/postgres.d.ts +19 -0
- package/dist/task-subtree-transfer/postgres.d.ts.map +1 -0
- package/dist/task-subtree-transfer/schema-sql.d.ts +5 -0
- package/dist/task-subtree-transfer/schema-sql.d.ts.map +1 -0
- package/dist/task-subtree-transfer/sqlite.d.ts +16 -0
- package/dist/task-subtree-transfer/sqlite.d.ts.map +1 -0
- package/dist/task-subtree-transfer/types.d.ts +148 -0
- package/dist/task-subtree-transfer/types.d.ts.map +1 -0
- package/dist/task-subtree-transfer.d.ts +2 -0
- package/dist/task-subtree-transfer.d.ts.map +1 -0
- package/dist/task-subtree-transfer.js +1869 -0
- package/dist/testing.d.ts +41 -7
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +44 -1
- package/dist/types/index.d.ts +24 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +8 -4
package/dist/storage.js
CHANGED
|
@@ -42,20 +42,16 @@ var __require = import.meta.require;
|
|
|
42
42
|
// src/storage/config.ts
|
|
43
43
|
var exports_config = {};
|
|
44
44
|
__export(exports_config, {
|
|
45
|
-
parseStorageMode: () => parseStorageMode,
|
|
46
45
|
parseStorageBackend: () => parseStorageBackend,
|
|
47
46
|
loadTodosStorageConfig: () => loadTodosStorageConfig,
|
|
48
47
|
loadStorageConfig: () => loadStorageConfig,
|
|
49
48
|
isTodosShadowEnabled: () => isTodosShadowEnabled,
|
|
50
|
-
isTodosRemoteStorageEnabled: () => isTodosRemoteStorageEnabled,
|
|
51
49
|
isTodosPostgresBackend: () => isTodosPostgresBackend,
|
|
52
50
|
getTodosStorageShadowEnvName: () => getTodosStorageShadowEnvName,
|
|
53
|
-
getTodosStorageMode: () => getTodosStorageMode,
|
|
54
51
|
getTodosStorageEnvName: () => getTodosStorageEnvName,
|
|
55
52
|
getTodosStorageDatabaseUrl: () => getTodosStorageDatabaseUrl,
|
|
56
53
|
getTodosStorageDatabaseEnv: () => getTodosStorageDatabaseEnv,
|
|
57
54
|
getTodosStorageBackend: () => getTodosStorageBackend,
|
|
58
|
-
getStorageMode: () => getStorageMode,
|
|
59
55
|
getStorageDatabaseUrl: () => getStorageDatabaseUrl,
|
|
60
56
|
getStorageDatabaseEnv: () => getStorageDatabaseEnv,
|
|
61
57
|
getCanonicalTodosRdsConfig: () => getCanonicalTodosRdsConfig,
|
|
@@ -65,7 +61,6 @@ __export(exports_config, {
|
|
|
65
61
|
TODOS_STORAGE_FALLBACK_ENV: () => TODOS_STORAGE_FALLBACK_ENV,
|
|
66
62
|
TODOS_STORAGE_ENV: () => TODOS_STORAGE_ENV,
|
|
67
63
|
STORAGE_TABLES: () => STORAGE_TABLES,
|
|
68
|
-
REMOVED_STORAGE_MODE_ENV_KEYS: () => REMOVED_STORAGE_MODE_ENV_KEYS,
|
|
69
64
|
CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV: () => CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV,
|
|
70
65
|
CANONICAL_TODOS_RDS_DATABASE: () => CANONICAL_TODOS_RDS_DATABASE,
|
|
71
66
|
CANONICAL_TODOS_RDS_CLUSTER_ENV: () => CANONICAL_TODOS_RDS_CLUSTER_ENV
|
|
@@ -120,9 +115,6 @@ function loadStorageConfig(env = process.env) {
|
|
|
120
115
|
function isTodosPostgresBackend(config) {
|
|
121
116
|
return config.mode === "postgres";
|
|
122
117
|
}
|
|
123
|
-
function isTodosRemoteStorageEnabled(config) {
|
|
124
|
-
return isTodosPostgresBackend(config);
|
|
125
|
-
}
|
|
126
118
|
function assertTodosRemoteStorageConfig(config) {
|
|
127
119
|
if (!isTodosPostgresBackend(config))
|
|
128
120
|
return;
|
|
@@ -138,32 +130,15 @@ function parseStorageBackend(value) {
|
|
|
138
130
|
return "sqlite";
|
|
139
131
|
if (normalized === "postgres" || normalized === "postgresql")
|
|
140
132
|
return "postgres";
|
|
141
|
-
if (["local", "remote", "cloud", "hybrid", "self_hosted"].includes(normalized)) {
|
|
142
|
-
throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} selects the backend. Deployment modes no longer exist: ` + `delete the storage-mode variable and set ${TODOS_STORAGE_ENV.databaseUrl} to select the ` + `postgresql backend, or leave it unset for sqlite.`);
|
|
143
|
-
}
|
|
144
133
|
throw new Error(`Storage backend must be sqlite or postgres`);
|
|
145
134
|
}
|
|
146
|
-
function parseStorageMode(value) {
|
|
147
|
-
return parseStorageBackend(value);
|
|
148
|
-
}
|
|
149
135
|
function getTodosStorageBackend(env = process.env) {
|
|
150
|
-
for (const key of REMOVED_STORAGE_MODE_ENV_KEYS) {
|
|
151
|
-
if (Object.hasOwn(env, key) && env[key] !== undefined) {
|
|
152
|
-
throw new Error(`${key} was removed. Deployment modes no longer exist: delete the storage-mode variable. ` + `Set ${TODOS_STORAGE_ENV.databaseUrl} to select the postgresql backend, ` + `or leave it unset for sqlite.`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
136
|
if (!getTodosStorageDatabaseUrl(env))
|
|
156
137
|
return "sqlite";
|
|
157
138
|
if (isTodosShadowEnabled(env))
|
|
158
139
|
return "sqlite";
|
|
159
140
|
return "postgres";
|
|
160
141
|
}
|
|
161
|
-
function getTodosStorageMode(env = process.env) {
|
|
162
|
-
return getTodosStorageBackend(env);
|
|
163
|
-
}
|
|
164
|
-
function getStorageMode(env = process.env) {
|
|
165
|
-
return getTodosStorageBackend(env);
|
|
166
|
-
}
|
|
167
142
|
function isTodosShadowEnabled(env = process.env) {
|
|
168
143
|
return parseBoolean(readStorageEnv(env, "shadow").value, false);
|
|
169
144
|
}
|
|
@@ -230,7 +205,7 @@ function parsePositiveInteger(value, fallback) {
|
|
|
230
205
|
}
|
|
231
206
|
return parsed;
|
|
232
207
|
}
|
|
233
|
-
var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV,
|
|
208
|
+
var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos";
|
|
234
209
|
var init_config = __esm(() => {
|
|
235
210
|
TODOS_STORAGE_TABLES = [
|
|
236
211
|
"todos_sync_records",
|
|
@@ -269,12 +244,6 @@ var init_config = __esm(() => {
|
|
|
269
244
|
syncBatchSize: "TODOS_SYNC_BATCH_SIZE",
|
|
270
245
|
syncDryRun: "TODOS_SYNC_DRY_RUN"
|
|
271
246
|
};
|
|
272
|
-
REMOVED_STORAGE_MODE_ENV_KEYS = [
|
|
273
|
-
"HASNA_TODOS_STORAGE_MODE",
|
|
274
|
-
"HASNA_TODOS_MODE",
|
|
275
|
-
"TODOS_STORAGE_MODE",
|
|
276
|
-
"TODOS_MODE"
|
|
277
|
-
];
|
|
278
247
|
});
|
|
279
248
|
|
|
280
249
|
// src/types/index.ts
|
|
@@ -3127,6 +3096,20 @@ function ensureSchema(db) {
|
|
|
3127
3096
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_event ON webhook_deliveries(event)");
|
|
3128
3097
|
ensureColumn("task_comments", "type", "TEXT DEFAULT 'comment'");
|
|
3129
3098
|
ensureColumn("task_comments", "progress_pct", "INTEGER");
|
|
3099
|
+
ensureTable("plan_comments", `
|
|
3100
|
+
CREATE TABLE plan_comments (
|
|
3101
|
+
id TEXT PRIMARY KEY,
|
|
3102
|
+
plan_id TEXT NOT NULL REFERENCES plans(id) ON DELETE CASCADE,
|
|
3103
|
+
agent_id TEXT,
|
|
3104
|
+
session_id TEXT,
|
|
3105
|
+
content TEXT NOT NULL,
|
|
3106
|
+
type TEXT DEFAULT 'comment' CHECK(type IN ('comment', 'progress', 'note')),
|
|
3107
|
+
progress_pct INTEGER CHECK(progress_pct IS NULL OR (progress_pct >= 0 AND progress_pct <= 100)),
|
|
3108
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
3109
|
+
machine_id TEXT,
|
|
3110
|
+
synced_at TEXT
|
|
3111
|
+
)`);
|
|
3112
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plan_comments_plan ON plan_comments(plan_id)");
|
|
3130
3113
|
ensureColumn("projects", "machine_id", "TEXT");
|
|
3131
3114
|
ensureColumn("projects", "synced_at", "TEXT");
|
|
3132
3115
|
ensureColumn("tasks", "machine_id", "TEXT");
|
|
@@ -4108,6 +4091,7 @@ var init_machines = __esm(() => {
|
|
|
4108
4091
|
"task_lists",
|
|
4109
4092
|
"plans",
|
|
4110
4093
|
"task_comments",
|
|
4094
|
+
"plan_comments",
|
|
4111
4095
|
"sessions",
|
|
4112
4096
|
"task_history",
|
|
4113
4097
|
"webhooks",
|
|
@@ -6857,22 +6841,31 @@ function updateTaskList(id, input, db) {
|
|
|
6857
6841
|
throw new TaskListNotFoundError(id);
|
|
6858
6842
|
const sets = ["updated_at = ?"];
|
|
6859
6843
|
const params = [now()];
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6844
|
+
const projectId = input.project_id === "" || input.project_id === null ? null : input.project_id === undefined ? existing.project_id : input.project_id;
|
|
6845
|
+
const scopeChanged = projectId !== existing.project_id;
|
|
6846
|
+
const effectiveSlug = input.slug !== undefined ? slugify(input.slug) : existing.slug;
|
|
6847
|
+
if (input.slug !== undefined && !effectiveSlug) {
|
|
6848
|
+
throw new Error("Invalid task-list slug \u2014 must be non-empty kebab-case");
|
|
6849
|
+
}
|
|
6850
|
+
if (scopeChanged || input.slug !== undefined && effectiveSlug !== existing.slug) {
|
|
6851
|
+
const duplicate = projectId ? d.query("SELECT id FROM task_lists WHERE project_id = ? AND slug = ? AND id != ?").get(projectId, effectiveSlug, id) : d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ? AND id != ?").get(effectiveSlug, id);
|
|
6865
6852
|
if (duplicate) {
|
|
6866
|
-
throw new ResourceConflictError("TASK_LIST_SLUG_CONFLICT", `Task list with slug "${
|
|
6853
|
+
throw new ResourceConflictError("TASK_LIST_SLUG_CONFLICT", `Task list with slug "${effectiveSlug}" already exists in this scope`);
|
|
6867
6854
|
}
|
|
6868
|
-
if (
|
|
6855
|
+
if (scopeChanged || effectiveSlug !== existing.slug) {
|
|
6869
6856
|
releaseCanonicalSlugClaims("task_list", id, d);
|
|
6870
|
-
if (!claimCanonicalSlug("task_list", taskListSlugScopeKey(
|
|
6871
|
-
throw new ResourceConflictError("TASK_LIST_SLUG_CONFLICT", `Task list with slug "${
|
|
6857
|
+
if (!claimCanonicalSlug("task_list", taskListSlugScopeKey(projectId), effectiveSlug, id, d)) {
|
|
6858
|
+
throw new ResourceConflictError("TASK_LIST_SLUG_CONFLICT", `Task list with slug "${effectiveSlug}" already exists in this scope`);
|
|
6872
6859
|
}
|
|
6873
6860
|
}
|
|
6861
|
+
}
|
|
6862
|
+
if (input.slug !== undefined) {
|
|
6874
6863
|
sets.push("slug = ?");
|
|
6875
|
-
params.push(
|
|
6864
|
+
params.push(effectiveSlug);
|
|
6865
|
+
}
|
|
6866
|
+
if (projectId !== existing.project_id) {
|
|
6867
|
+
sets.push("project_id = ?");
|
|
6868
|
+
params.push(projectId);
|
|
6876
6869
|
}
|
|
6877
6870
|
if (input.name !== undefined) {
|
|
6878
6871
|
sets.push("name = ?");
|
|
@@ -9321,8 +9314,8 @@ function failTask(id, agentId, reason, options, db) {
|
|
|
9321
9314
|
const safeMeta = sanitizePreWriteValue(meta, "task.failure.metadata");
|
|
9322
9315
|
const timestamp2 = now();
|
|
9323
9316
|
const failTx = d.transaction(() => {
|
|
9324
|
-
const res = d.run(`UPDATE tasks SET status = 'failed', reason = ?, locked_by = NULL, locked_at = NULL, metadata = ?, version = version + 1, updated_at = ?
|
|
9325
|
-
WHERE id = ? AND version = ?`, [safeReason, JSON.stringify(safeMeta), timestamp2, id, task.version]);
|
|
9317
|
+
const res = d.run(`UPDATE tasks SET status = 'failed', reason = ?, locked_by = NULL, locked_at = NULL, completed_at = COALESCE(completed_at, ?), metadata = ?, version = version + 1, updated_at = ?
|
|
9318
|
+
WHERE id = ? AND version = ?`, [safeReason, timestamp2, JSON.stringify(safeMeta), timestamp2, id, task.version]);
|
|
9326
9319
|
if (res.changes === 0) {
|
|
9327
9320
|
const current = getTask(id, d);
|
|
9328
9321
|
throw new VersionConflictError(id, task.version, current?.version ?? -1);
|
|
@@ -9334,6 +9327,7 @@ function failTask(id, agentId, reason, options, db) {
|
|
|
9334
9327
|
status: "failed",
|
|
9335
9328
|
locked_by: null,
|
|
9336
9329
|
locked_at: null,
|
|
9330
|
+
completed_at: task.completed_at ?? timestamp2,
|
|
9337
9331
|
reason: safeReason,
|
|
9338
9332
|
metadata: safeMeta,
|
|
9339
9333
|
version: task.version + 1,
|
|
@@ -9982,13 +9976,20 @@ function updateTaskStored(id, input, db) {
|
|
|
9982
9976
|
if (isTerminalStatus(input.status)) {
|
|
9983
9977
|
sets.push("locked_by = NULL");
|
|
9984
9978
|
sets.push("locked_at = NULL");
|
|
9985
|
-
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
|
|
9979
|
+
if (input.status === "completed") {
|
|
9980
|
+
sets.push("completed_at = ?");
|
|
9981
|
+
params.push(completionTimestamp);
|
|
9982
|
+
} else {
|
|
9983
|
+
sets.push("completed_at = COALESCE(completed_at, ?)");
|
|
9984
|
+
params.push(completionTimestamp);
|
|
9985
|
+
}
|
|
9989
9986
|
} else if (task.status === "completed" && input.completed_at === undefined) {
|
|
9990
9987
|
sets.push("completed_at = NULL");
|
|
9991
9988
|
}
|
|
9989
|
+
if (input.status === "in_progress") {
|
|
9990
|
+
sets.push("started_at = COALESCE(started_at, ?)");
|
|
9991
|
+
params.push(timestamp2);
|
|
9992
|
+
}
|
|
9992
9993
|
}
|
|
9993
9994
|
if (input.priority !== undefined) {
|
|
9994
9995
|
sets.push("priority = ?");
|
|
@@ -10126,7 +10127,6 @@ function updateTaskStored(id, input, db) {
|
|
|
10126
10127
|
if (input.approved_by !== undefined)
|
|
10127
10128
|
logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
|
|
10128
10129
|
const reopened = input.status !== undefined && input.status !== "completed" && task.status === "completed" && input.completed_at === undefined;
|
|
10129
|
-
const completedNow = input.status === "completed";
|
|
10130
10130
|
const terminalNow = input.status !== undefined && isTerminalStatus(input.status);
|
|
10131
10131
|
const updatedTask = {
|
|
10132
10132
|
...task,
|
|
@@ -10137,7 +10137,8 @@ function updateTaskStored(id, input, db) {
|
|
|
10137
10137
|
updated_at: timestamp2,
|
|
10138
10138
|
locked_by: terminalNow ? null : task.locked_by,
|
|
10139
10139
|
locked_at: terminalNow ? null : task.locked_at,
|
|
10140
|
-
completed_at:
|
|
10140
|
+
completed_at: input.status === "completed" ? completionTimestamp : input.completed_at !== undefined ? input.completed_at : terminalNow ? task.completed_at ?? completionTimestamp : reopened ? null : task.completed_at,
|
|
10141
|
+
started_at: input.status === "in_progress" ? task.started_at ?? timestamp2 : task.started_at,
|
|
10141
10142
|
sla_minutes: input.sla_minutes !== undefined ? input.sla_minutes : task.sla_minutes,
|
|
10142
10143
|
actual_minutes: input.actual_minutes ?? task.actual_minutes,
|
|
10143
10144
|
confidence: input.confidence !== undefined ? input.confidence : task.confidence,
|
|
@@ -10198,6 +10199,9 @@ function deleteTask(id, db) {
|
|
|
10198
10199
|
version: row.version
|
|
10199
10200
|
}, d);
|
|
10200
10201
|
const result = d.run("DELETE FROM tasks WHERE id = ?", [id]);
|
|
10202
|
+
if (result.changes > 0) {
|
|
10203
|
+
d.run("DELETE FROM task_dependencies WHERE task_id = ? OR depends_on = ?", [id, id]);
|
|
10204
|
+
}
|
|
10201
10205
|
return result.changes > 0;
|
|
10202
10206
|
}
|
|
10203
10207
|
var init_task_crud = __esm(() => {
|
|
@@ -12967,6 +12971,40 @@ init_tasks();
|
|
|
12967
12971
|
init_projects();
|
|
12968
12972
|
init_plans();
|
|
12969
12973
|
|
|
12974
|
+
// src/db/plan-comments.ts
|
|
12975
|
+
init_types();
|
|
12976
|
+
init_database();
|
|
12977
|
+
init_plans();
|
|
12978
|
+
init_prewrite_secrets();
|
|
12979
|
+
function addPlanComment(input, db) {
|
|
12980
|
+
const d = db || getDatabase();
|
|
12981
|
+
if (!getPlan(input.plan_id, d)) {
|
|
12982
|
+
throw new PlanNotFoundError(input.plan_id);
|
|
12983
|
+
}
|
|
12984
|
+
const id = uuid();
|
|
12985
|
+
const timestamp2 = now();
|
|
12986
|
+
d.run(`INSERT INTO plan_comments (id, plan_id, agent_id, session_id, content, type, progress_pct, created_at)
|
|
12987
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
12988
|
+
id,
|
|
12989
|
+
input.plan_id,
|
|
12990
|
+
input.agent_id || null,
|
|
12991
|
+
input.session_id || null,
|
|
12992
|
+
sanitizePreWriteText(input.content, "comment.content"),
|
|
12993
|
+
input.type || "comment",
|
|
12994
|
+
input.progress_pct ?? null,
|
|
12995
|
+
timestamp2
|
|
12996
|
+
]);
|
|
12997
|
+
return getPlanComment(id, d);
|
|
12998
|
+
}
|
|
12999
|
+
function getPlanComment(id, db) {
|
|
13000
|
+
const d = db || getDatabase();
|
|
13001
|
+
return d.query("SELECT * FROM plan_comments WHERE id = ?").get(id);
|
|
13002
|
+
}
|
|
13003
|
+
function listPlanComments(planId, db) {
|
|
13004
|
+
const d = db || getDatabase();
|
|
13005
|
+
return d.query("SELECT * FROM plan_comments WHERE plan_id = ? ORDER BY created_at, rowid").all(planId);
|
|
13006
|
+
}
|
|
13007
|
+
|
|
12970
13008
|
// src/db/agents.ts
|
|
12971
13009
|
init_types();
|
|
12972
13010
|
init_database();
|
|
@@ -13624,6 +13662,20 @@ init_task_lists();
|
|
|
13624
13662
|
init_templates();
|
|
13625
13663
|
init_audit();
|
|
13626
13664
|
init_comments();
|
|
13665
|
+
|
|
13666
|
+
// src/lib/comment-cursor.ts
|
|
13667
|
+
function isStrictlyOlder(comment, before) {
|
|
13668
|
+
return comment.created_at < before.created_at || comment.created_at === before.created_at && comment.id < before.id;
|
|
13669
|
+
}
|
|
13670
|
+
function compareCommentKeyset(left, right) {
|
|
13671
|
+
if (isStrictlyOlder(left, right))
|
|
13672
|
+
return -1;
|
|
13673
|
+
if (isStrictlyOlder(right, left))
|
|
13674
|
+
return 1;
|
|
13675
|
+
return 0;
|
|
13676
|
+
}
|
|
13677
|
+
|
|
13678
|
+
// src/storage/local-sqlite.ts
|
|
13627
13679
|
init_database();
|
|
13628
13680
|
|
|
13629
13681
|
// src/db/integrity.ts
|
|
@@ -14720,7 +14772,22 @@ function createLocalSqliteTodosStorageAdapter(options = {}) {
|
|
|
14720
14772
|
list: (projectId) => listPlans(projectId, database()),
|
|
14721
14773
|
update: (id, input) => updatePlan(id, input, database()),
|
|
14722
14774
|
completeAtRevision: (id, expectedUpdatedAt) => completePlanAtRevision(id, expectedUpdatedAt, database()),
|
|
14723
|
-
delete: (id) => deletePlan(id, database())
|
|
14775
|
+
delete: (id) => deletePlan(id, database()),
|
|
14776
|
+
addComment: (input) => addPlanComment(input, database()),
|
|
14777
|
+
getComments: (planId) => listPlanComments(planId, database()),
|
|
14778
|
+
getCommentsPage: (planId, options2) => {
|
|
14779
|
+
if (options2?.limit !== undefined && (!Number.isSafeInteger(options2.limit) || options2.limit < 1 || options2.limit > 1001)) {
|
|
14780
|
+
throw new Error("Plan comment limit must be an integer between 1 and 1001");
|
|
14781
|
+
}
|
|
14782
|
+
let comments = listPlanComments(planId, database());
|
|
14783
|
+
comments = comments.sort(compareCommentKeyset);
|
|
14784
|
+
if (options2?.before) {
|
|
14785
|
+
const before = options2.before;
|
|
14786
|
+
comments = comments.filter((comment) => isStrictlyOlder(comment, before));
|
|
14787
|
+
}
|
|
14788
|
+
const limit = options2?.limit ?? 100;
|
|
14789
|
+
return comments.slice(-limit);
|
|
14790
|
+
}
|
|
14724
14791
|
},
|
|
14725
14792
|
planProjectLinks: {
|
|
14726
14793
|
apply: (input) => applyPlanProjectLinkSqlite(input, database()),
|
|
@@ -14761,10 +14828,10 @@ function createLocalSqliteTodosStorageAdapter(options = {}) {
|
|
|
14761
14828
|
throw new Error("Comment limit must be an integer between 1 and 1001");
|
|
14762
14829
|
}
|
|
14763
14830
|
let comments = listComments(taskId, database());
|
|
14764
|
-
comments = comments.sort(
|
|
14831
|
+
comments = comments.sort(compareCommentKeyset);
|
|
14765
14832
|
if (options2?.before) {
|
|
14766
14833
|
const before = options2.before;
|
|
14767
|
-
comments = comments.filter((comment) => comment
|
|
14834
|
+
comments = comments.filter((comment) => isStrictlyOlder(comment, before));
|
|
14768
14835
|
}
|
|
14769
14836
|
if (options2?.limit !== undefined)
|
|
14770
14837
|
comments = comments.slice(-options2.limit);
|
|
@@ -15294,7 +15361,26 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
15294
15361
|
list: async (projectId) => (await store.list("plans")).filter((plan) => projectId === undefined || plan.project_id === projectId).sort((a, b) => a.name.localeCompare(b.name)),
|
|
15295
15362
|
update: (id, input) => updatePlan2(id, input, store),
|
|
15296
15363
|
completeAtRevision: (id, expectedUpdatedAt, context) => store.completePlanAtRevision(id, expectedUpdatedAt, context),
|
|
15297
|
-
delete: (id, context) => store.deletePlan(id, context)
|
|
15364
|
+
delete: (id, context) => store.deletePlan(id, context),
|
|
15365
|
+
addComment: (input, context) => addPlanComment2(input, store, context),
|
|
15366
|
+
getComments: async (planId) => {
|
|
15367
|
+
const pages = [];
|
|
15368
|
+
let before;
|
|
15369
|
+
while (true) {
|
|
15370
|
+
const page = await store.listPlanComments(planId, { limit: 1000, ...before ? { before } : {} });
|
|
15371
|
+
if (page.length === 0)
|
|
15372
|
+
break;
|
|
15373
|
+
pages.unshift(page);
|
|
15374
|
+
if (page.length < 1000)
|
|
15375
|
+
break;
|
|
15376
|
+
const oldest = page[0];
|
|
15377
|
+
before = { created_at: oldest.created_at, id: oldest.id };
|
|
15378
|
+
}
|
|
15379
|
+
return pages.flat().map(redactPlanComment).sort((a, b) => a.created_at.localeCompare(b.created_at) || a.id.localeCompare(b.id));
|
|
15380
|
+
},
|
|
15381
|
+
getCommentsPage: async (planId, options2) => {
|
|
15382
|
+
return (await store.listPlanComments(planId, options2)).map(redactPlanComment).sort((a, b) => a.created_at.localeCompare(b.created_at) || a.id.localeCompare(b.id));
|
|
15383
|
+
}
|
|
15298
15384
|
},
|
|
15299
15385
|
planProjectLinks: {
|
|
15300
15386
|
apply: (input, context) => store.applyPlanProjectLink(input, context),
|
|
@@ -15512,6 +15598,27 @@ class PostgresJsonRecordStore {
|
|
|
15512
15598
|
LIMIT $${params.length}`, params);
|
|
15513
15599
|
return result.rows.map((row) => payloadRecord2(row.payload)).reverse();
|
|
15514
15600
|
}
|
|
15601
|
+
async listPlanComments(planId, options = {}) {
|
|
15602
|
+
await this.ensureSchema();
|
|
15603
|
+
const limit = options.limit ?? 100;
|
|
15604
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1001) {
|
|
15605
|
+
throw new Error("Postgres plan comment limit must be an integer between 1 and 1001");
|
|
15606
|
+
}
|
|
15607
|
+
const params = [this.service, planId];
|
|
15608
|
+
let cursorPredicate = "";
|
|
15609
|
+
if (options.before) {
|
|
15610
|
+
params.push(options.before.created_at, options.before.id);
|
|
15611
|
+
cursorPredicate = `AND (payload->>'created_at', object_id) < ($3, $4)`;
|
|
15612
|
+
}
|
|
15613
|
+
params.push(limit);
|
|
15614
|
+
const result = await this.options.client.query(`/* todos:list-plan-comments */ SELECT payload FROM ${this.tableName}
|
|
15615
|
+
WHERE service = $1 AND object_type = 'plan_comments' AND deleted_at IS NULL
|
|
15616
|
+
AND payload->>'plan_id' = $2
|
|
15617
|
+
${cursorPredicate}
|
|
15618
|
+
ORDER BY payload->>'created_at' DESC, object_id DESC
|
|
15619
|
+
LIMIT $${params.length}`, params);
|
|
15620
|
+
return result.rows.map((row) => payloadRecord2(row.payload)).reverse();
|
|
15621
|
+
}
|
|
15515
15622
|
async listRecords(type) {
|
|
15516
15623
|
await this.ensureSchema();
|
|
15517
15624
|
const result = await this.options.client.query(`SELECT object_type, object_id, payload, updated_at
|
|
@@ -16673,6 +16780,15 @@ class PostgresJsonRecordStore {
|
|
|
16673
16780
|
WHERE r.service = $1 AND r.object_type = 'tasks' AND r.deleted_at IS NULL
|
|
16674
16781
|
AND r.payload->>'plan_id' = $2 AND EXISTS (SELECT 1 FROM target)
|
|
16675
16782
|
RETURNING 1
|
|
16783
|
+
), deleted_plan_comments AS (
|
|
16784
|
+
UPDATE ${this.tableName} r SET
|
|
16785
|
+
deleted_at = $3::timestamptz,
|
|
16786
|
+
updated_at = $3::timestamptz,
|
|
16787
|
+
source_machine_id = COALESCE($4, r.source_machine_id),
|
|
16788
|
+
version = COALESCE(r.version, 0) + 1
|
|
16789
|
+
WHERE r.service = $1 AND r.object_type = 'plan_comments' AND r.deleted_at IS NULL
|
|
16790
|
+
AND r.payload->>'plan_id' = $2 AND EXISTS (SELECT 1 FROM target)
|
|
16791
|
+
RETURNING 1
|
|
16676
16792
|
), deleted_plan AS (
|
|
16677
16793
|
UPDATE ${this.tableName} r SET
|
|
16678
16794
|
deleted_at = $3::timestamptz,
|
|
@@ -16848,7 +16964,8 @@ async function updateTask2(id, input, store, context) {
|
|
|
16848
16964
|
task_list_id: input.task_list_id !== undefined ? input.task_list_id : existing.task_list_id,
|
|
16849
16965
|
parent_id: input.parent_id !== undefined ? input.parent_id : existing.parent_id,
|
|
16850
16966
|
created_by: existing.created_by,
|
|
16851
|
-
completed_at:
|
|
16967
|
+
completed_at: input.status === "completed" ? input.completed_at ?? new Date().toISOString() : input.completed_at !== undefined ? input.completed_at : input.status !== undefined && isTerminalStatus(input.status) ? existing.completed_at ?? new Date().toISOString() : reopened ? null : existing.completed_at,
|
|
16968
|
+
started_at: input.status === "in_progress" ? existing.started_at ?? new Date().toISOString() : existing.started_at
|
|
16852
16969
|
};
|
|
16853
16970
|
const storedTask = await store.upsertTaskWithPlanMembershipGuard(task, [existing.plan_id, effectivePlanId].filter((planId) => Boolean(planId)), input.project_id !== undefined, context, {
|
|
16854
16971
|
operation: "update",
|
|
@@ -16893,7 +17010,8 @@ async function failTask2(id, agentId, reason, options, store) {
|
|
|
16893
17010
|
status: "failed",
|
|
16894
17011
|
assigned_to: task.assigned_to ?? agentId ?? null,
|
|
16895
17012
|
reason: reason ?? task.reason,
|
|
16896
|
-
retry_after: options?.retry_after ?? task.retry_after
|
|
17013
|
+
retry_after: options?.retry_after ?? task.retry_after,
|
|
17014
|
+
completed_at: task.completed_at ?? new Date().toISOString()
|
|
16897
17015
|
}, store);
|
|
16898
17016
|
if (!options?.retry)
|
|
16899
17017
|
return { task: failed };
|
|
@@ -17341,16 +17459,22 @@ async function createTaskList2(input, store, context) {
|
|
|
17341
17459
|
async function updateTaskList2(id, input, store) {
|
|
17342
17460
|
const list = await requireRecord("task_lists", id, store);
|
|
17343
17461
|
const patch = definedPatch(input);
|
|
17462
|
+
const projectId = input.project_id === "" || input.project_id === null ? null : input.project_id === undefined ? list.project_id : input.project_id;
|
|
17344
17463
|
if (input.slug !== undefined) {
|
|
17345
17464
|
const slug = slugifyRaw(input.slug);
|
|
17346
17465
|
if (!slug)
|
|
17347
17466
|
throw new Error("Invalid task-list slug \u2014 must be non-empty kebab-case");
|
|
17348
|
-
|
|
17467
|
+
patch.slug = slug;
|
|
17468
|
+
}
|
|
17469
|
+
const effectiveSlug = patch.slug ?? list.slug;
|
|
17470
|
+
if (projectId !== list.project_id || effectiveSlug !== list.slug) {
|
|
17471
|
+
const duplicate = (await store.list("task_lists")).find((candidate) => candidate.id !== id && candidate.project_id === projectId && candidate.slug === effectiveSlug);
|
|
17349
17472
|
if (duplicate) {
|
|
17350
|
-
throw new ResourceConflictError("TASK_LIST_SLUG_CONFLICT", `Task list with slug "${
|
|
17473
|
+
throw new ResourceConflictError("TASK_LIST_SLUG_CONFLICT", `Task list with slug "${effectiveSlug}" already exists in this scope`);
|
|
17351
17474
|
}
|
|
17352
|
-
patch.slug = slug;
|
|
17353
17475
|
}
|
|
17476
|
+
if (projectId !== list.project_id)
|
|
17477
|
+
patch.project_id = projectId;
|
|
17354
17478
|
return store.upsert("task_lists", {
|
|
17355
17479
|
...list,
|
|
17356
17480
|
...patch,
|
|
@@ -17443,6 +17567,22 @@ async function addComment2(input, store, context) {
|
|
|
17443
17567
|
function redactComment(comment) {
|
|
17444
17568
|
return { ...comment, content: redactEvidenceText(comment.content) };
|
|
17445
17569
|
}
|
|
17570
|
+
async function addPlanComment2(input, store, context) {
|
|
17571
|
+
const comment = {
|
|
17572
|
+
id: randomUUID3(),
|
|
17573
|
+
plan_id: input.plan_id,
|
|
17574
|
+
agent_id: input.agent_id ?? context?.agentId ?? null,
|
|
17575
|
+
session_id: input.session_id ?? context?.sessionId ?? null,
|
|
17576
|
+
content: redactEvidenceText(input.content),
|
|
17577
|
+
type: input.type ?? "comment",
|
|
17578
|
+
progress_pct: input.progress_pct ?? null,
|
|
17579
|
+
created_at: new Date().toISOString()
|
|
17580
|
+
};
|
|
17581
|
+
return store.upsert("plan_comments", comment, context);
|
|
17582
|
+
}
|
|
17583
|
+
function redactPlanComment(comment) {
|
|
17584
|
+
return { ...comment, content: redactEvidenceText(comment.content) };
|
|
17585
|
+
}
|
|
17446
17586
|
async function exportSnapshot(store) {
|
|
17447
17587
|
return {
|
|
17448
17588
|
exportedAt: new Date().toISOString(),
|
|
@@ -19051,24 +19191,20 @@ export {
|
|
|
19051
19191
|
postgresTodosScopedSlugIndexStatusSql,
|
|
19052
19192
|
postgresTodosCommentCursorIndexSql,
|
|
19053
19193
|
planRunArtifactsS3Sync,
|
|
19054
|
-
parseStorageMode,
|
|
19055
19194
|
parseStorageBackend,
|
|
19056
19195
|
maybeInstallShadowCapture2 as maybeInstallShadowCapture,
|
|
19057
19196
|
loadTodosStorageConfig,
|
|
19058
19197
|
loadStorageConfig,
|
|
19059
19198
|
isTodosShadowEnabled,
|
|
19060
|
-
isTodosRemoteStorageEnabled,
|
|
19061
19199
|
isTodosPostgresBackend,
|
|
19062
19200
|
isCommentRedactionBackfillComplete,
|
|
19063
19201
|
installShadowOutboxSchema,
|
|
19064
19202
|
importSqliteTodosStorageSnapshot,
|
|
19065
19203
|
getTodosStorageShadowEnvName,
|
|
19066
|
-
getTodosStorageMode,
|
|
19067
19204
|
getTodosStorageEnvName,
|
|
19068
19205
|
getTodosStorageDatabaseUrl,
|
|
19069
19206
|
getTodosStorageDatabaseEnv,
|
|
19070
19207
|
getTodosStorageBackend,
|
|
19071
|
-
getStorageMode,
|
|
19072
19208
|
getStorageDatabaseUrl,
|
|
19073
19209
|
getStorageDatabaseEnv,
|
|
19074
19210
|
getRuntimeShadowOutbox,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type PostgresTodosTaskSubtreeTransferAuthorityOptions, type SqliteTodosTaskSubtreeTransferAuthorityOptions, type TodosTaskSubtreeTransferApplyRequest, type TodosTaskSubtreeTransferAuthority, type TodosTaskSubtreeTransferAuthorityOptions, type TodosTaskSubtreeTransferCapability, type TodosTaskSubtreeTransferInspectRequest, type TodosTaskSubtreeTransferRollbackRequest } from "./types.js";
|
|
2
|
+
import type { TodosTaskSubtreeTransferBackend } from "./backend.js";
|
|
3
|
+
export declare function parseTodosTaskSubtreeTransferInspect(input: unknown): TodosTaskSubtreeTransferInspectRequest;
|
|
4
|
+
export declare function parseTodosTaskSubtreeTransferApply(input: unknown): TodosTaskSubtreeTransferApplyRequest;
|
|
5
|
+
export declare function parseTodosTaskSubtreeTransferRollback(input: unknown): TodosTaskSubtreeTransferRollbackRequest;
|
|
6
|
+
export { deriveTodosTaskSubtreeTransferIdempotencyKey, taskSubtreeTransferRequestDigest, taskSubtreeTransferRollbackRequestDigest, } from "./canonical.js";
|
|
7
|
+
export declare function deriveTodosTaskSubtreeTransferApplyPreconditionDigest(input: Pick<TodosTaskSubtreeTransferApplyRequest, "source_project_id" | "destination_project_id" | "destination_task_list_id" | "root_task_id" | "expected_root_parent_id" | "destination_parent_id" | "source_population_digest" | "expected_tasks" | "shared_plan_splits">): string;
|
|
8
|
+
export declare function deriveTodosTaskSubtreeTransferRollbackPreconditionDigest(input: {
|
|
9
|
+
receipt_id: string;
|
|
10
|
+
operation_id: string;
|
|
11
|
+
step_id: string;
|
|
12
|
+
apply_result_digest: string;
|
|
13
|
+
}): string;
|
|
14
|
+
export declare class PackageOwnedTodosTaskSubtreeTransferAuthority implements TodosTaskSubtreeTransferAuthority {
|
|
15
|
+
private readonly backend;
|
|
16
|
+
private readonly tenantId;
|
|
17
|
+
private readonly backendOptions;
|
|
18
|
+
constructor(backend: TodosTaskSubtreeTransferBackend, options?: TodosTaskSubtreeTransferAuthorityOptions);
|
|
19
|
+
capability(): Promise<TodosTaskSubtreeTransferCapability>;
|
|
20
|
+
inspect(input: unknown): Promise<import("./types.js").TodosTaskSubtreeTransferInspection>;
|
|
21
|
+
apply(input: unknown): Promise<import("./types.js").TodosTaskSubtreeTransferResult>;
|
|
22
|
+
readExact(receiptIdValue: string): Promise<import("./types.js").TodosTaskSubtreeTransferResult>;
|
|
23
|
+
rollback(input: unknown): Promise<import("./types.js").TodosTaskSubtreeTransferResult>;
|
|
24
|
+
}
|
|
25
|
+
export declare function createSqliteTodosTaskSubtreeTransferAuthority(options: SqliteTodosTaskSubtreeTransferAuthorityOptions): TodosTaskSubtreeTransferAuthority;
|
|
26
|
+
export declare function createPostgresTodosTaskSubtreeTransferAuthority(client: import("./types.js").TodosTaskSubtreeTransferPostgresClient, options?: PostgresTodosTaskSubtreeTransferAuthorityOptions): TodosTaskSubtreeTransferAuthority;
|
|
27
|
+
//# sourceMappingURL=authority.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authority.d.ts","sourceRoot":"","sources":["../../src/task-subtree-transfer/authority.ts"],"names":[],"mappings":"AAQA,OAAO,EAIL,KAAK,gDAAgD,EACrD,KAAK,8CAA8C,EACnD,KAAK,oCAAoC,EACzC,KAAK,iCAAiC,EACtC,KAAK,wCAAwC,EAC7C,KAAK,kCAAkC,EACvC,KAAK,sCAAsC,EAC3C,KAAK,uCAAuC,EAC7C,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAsEpE,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,OAAO,GACb,sCAAsC,CAgBxC;AAeD,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,OAAO,GACb,oCAAoC,CAkEtC;AAED,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,OAAO,GACb,uCAAuC,CAezC;AAED,OAAO,EACL,4CAA4C,EAC5C,gCAAgC,EAChC,wCAAwC,GACzC,MAAM,gBAAgB,CAAC;AAExB,wBAAgB,qDAAqD,CACnE,KAAK,EAAE,IAAI,CACT,oCAAoC,EACpC,mBAAmB,GACjB,wBAAwB,GACxB,0BAA0B,GAC1B,cAAc,GACd,yBAAyB,GACzB,uBAAuB,GACvB,0BAA0B,GAC1B,gBAAgB,GAChB,oBAAoB,CACvB,GACA,MAAM,CAyBR;AAED,wBAAgB,wDAAwD,CACtE,KAAK,EAAE;IACL,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC;CAC7B,GACA,MAAM,CASR;AAaD,qBAAa,6CAA8C,YAAW,iCAAiC;IAInG,OAAO,CAAC,QAAQ,CAAC,OAAO;IAH1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2C;gBAEvD,OAAO,EAAE,+BAA+B,EACzD,OAAO,GAAE,wCAA6C;IAMlD,UAAU,IAAI,OAAO,CAAC,kCAAkC,CAAC;IAoBzD,OAAO,CAAC,KAAK,EAAE,OAAO;IAItB,KAAK,CAAC,KAAK,EAAE,OAAO;IA6BpB,SAAS,CAAC,cAAc,EAAE,MAAM;IAIhC,QAAQ,CAAC,KAAK,EAAE,OAAO;CAoB9B;AAED,wBAAgB,6CAA6C,CAC3D,OAAO,EAAE,8CAA8C,GACtD,iCAAiC,CAKnC;AAED,wBAAgB,+CAA+C,CAC7D,MAAM,EAAE,OAAO,YAAY,EAAE,sCAAsC,EACnE,OAAO,GAAE,gDAAqD,GAC7D,iCAAiC,CAKnC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TodosTaskSubtreeTransferApplyRequest, TodosTaskSubtreeTransferAuthorityOptions, TodosTaskSubtreeTransferInspectRequest, TodosTaskSubtreeTransferInspection, TodosTaskSubtreeTransferResult, TodosTaskSubtreeTransferRollbackRequest } from "./types.js";
|
|
2
|
+
export interface TodosTaskSubtreeTransferBackend {
|
|
3
|
+
readonly kind: "sqlite" | "postgresql";
|
|
4
|
+
inspect(input: TodosTaskSubtreeTransferInspectRequest): Promise<TodosTaskSubtreeTransferInspection>;
|
|
5
|
+
apply(input: TodosTaskSubtreeTransferApplyRequest, options: TodosTaskSubtreeTransferAuthorityOptions): Promise<TodosTaskSubtreeTransferResult>;
|
|
6
|
+
readExact(receiptId: string): Promise<TodosTaskSubtreeTransferResult>;
|
|
7
|
+
rollback(input: TodosTaskSubtreeTransferRollbackRequest, options: TodosTaskSubtreeTransferAuthorityOptions): Promise<TodosTaskSubtreeTransferResult>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/task-subtree-transfer/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oCAAoC,EACpC,wCAAwC,EACxC,sCAAsC,EACtC,kCAAkC,EAClC,8BAA8B,EAC9B,uCAAuC,EACxC,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,YAAY,CAAC;IACvC,OAAO,CAAC,KAAK,EAAE,sCAAsC,GAAG,OAAO,CAAC,kCAAkC,CAAC,CAAC;IACpG,KAAK,CAAC,KAAK,EAAE,oCAAoC,EAAE,OAAO,EAAE,wCAAwC,GAAG,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC/I,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,8BAA8B,CAAC,CAAC;IACtE,QAAQ,CAAC,KAAK,EAAE,uCAAuC,EAAE,OAAO,EAAE,wCAAwC,GAAG,OAAO,CAAC,8BAA8B,CAAC,CAAC;CACtJ"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { canonicalDigest, canonicalJson, deterministicUuid } from "../task-manifest/canonical.js";
|
|
2
|
+
import { type TodosTaskSubtreeTransferApplyRequest, type TodosTaskSubtreeTransferDirection, type TodosTaskSubtreeTransferRollbackRequest } from "./types.js";
|
|
3
|
+
export { canonicalDigest, canonicalJson, deterministicUuid };
|
|
4
|
+
export declare function taskSubtreeTransferRequestDigest(input: Omit<TodosTaskSubtreeTransferApplyRequest, "idempotency_key">): string;
|
|
5
|
+
export declare function taskSubtreeTransferRollbackRequestDigest(input: Omit<TodosTaskSubtreeTransferRollbackRequest, "idempotency_key">): string;
|
|
6
|
+
export declare function deriveTodosTaskSubtreeTransferIdempotencyKey(input: {
|
|
7
|
+
operation_id: string;
|
|
8
|
+
step_id: string;
|
|
9
|
+
direction: TodosTaskSubtreeTransferDirection;
|
|
10
|
+
target_selector: string;
|
|
11
|
+
request_digest: string;
|
|
12
|
+
precondition_digest: string;
|
|
13
|
+
}): string;
|
|
14
|
+
//# sourceMappingURL=canonical.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical.d.ts","sourceRoot":"","sources":["../../src/task-subtree-transfer/canonical.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,aAAa,EACb,iBAAiB,EAClB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,KAAK,oCAAoC,EACzC,KAAK,iCAAiC,EACtC,KAAK,uCAAuC,EAC7C,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,CAAC;AAE7D,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,IAAI,CAAC,oCAAoC,EAAE,iBAAiB,CAAC,GACnE,MAAM,CAGR;AAED,wBAAgB,wCAAwC,CACtD,KAAK,EAAE,IAAI,CAAC,uCAAuC,EAAE,iBAAiB,CAAC,GACtE,MAAM,CAGR;AAED,wBAAgB,4CAA4C,CAAC,KAAK,EAAE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,iCAAiC,CAAC;IAC7C,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B,GAAG,MAAM,CAKT"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type TodosTaskSubtreeTransferAuthority, type TodosTaskSubtreeTransferHttpClientOptions } from "./types.js";
|
|
2
|
+
export declare function handleTodosTaskSubtreeTransferHttpRequest(request: Request, url: URL, authority: TodosTaskSubtreeTransferAuthority, basePath?: string): Promise<Response | null>;
|
|
3
|
+
export declare class TodosTaskSubtreeTransferHttpClient implements TodosTaskSubtreeTransferAuthority {
|
|
4
|
+
private readonly baseUrl;
|
|
5
|
+
private readonly fetchImpl;
|
|
6
|
+
private readonly headers;
|
|
7
|
+
constructor(options: TodosTaskSubtreeTransferHttpClientOptions);
|
|
8
|
+
private request;
|
|
9
|
+
capability(): Promise<import("./types.js").TodosTaskSubtreeTransferCapability>;
|
|
10
|
+
inspect(input: unknown): Promise<import("./types.js").TodosTaskSubtreeTransferInspection>;
|
|
11
|
+
apply(input: unknown): Promise<import("./types.js").TodosTaskSubtreeTransferResult>;
|
|
12
|
+
readExact(receiptId: string): Promise<import("./types.js").TodosTaskSubtreeTransferResult>;
|
|
13
|
+
rollback(input: unknown): Promise<import("./types.js").TodosTaskSubtreeTransferResult>;
|
|
14
|
+
}
|
|
15
|
+
export declare function createTodosTaskSubtreeTransferHttpClient(options: TodosTaskSubtreeTransferHttpClientOptions): TodosTaskSubtreeTransferAuthority;
|
|
16
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/task-subtree-transfer/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,iCAAiC,EACtC,KAAK,yCAAyC,EAC/C,MAAM,YAAY,CAAC;AA0EpB,wBAAsB,yCAAyC,CAC7D,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,iCAAiC,EAC5C,QAAQ,SAA8B,GACrC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAiC1B;AAED,qBAAa,kCAAmC,YAAW,iCAAiC;IAC1F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;gBAErC,OAAO,EAAE,yCAAyC;YAUhD,OAAO;IA+Bf,UAAU;IAIV,OAAO,CAAC,KAAK,EAAE,OAAO;IAOtB,KAAK,CAAC,KAAK,EAAE,OAAO;IAOpB,SAAS,CAAC,SAAS,EAAE,MAAM;IAO3B,QAAQ,CAAC,KAAK,EAAE,OAAO;CAM9B;AAED,wBAAgB,wCAAwC,CACtD,OAAO,EAAE,yCAAyC,GACjD,iCAAiC,CAEnC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { PackageOwnedTodosTaskSubtreeTransferAuthority, createPostgresTodosTaskSubtreeTransferAuthority, createSqliteTodosTaskSubtreeTransferAuthority, deriveTodosTaskSubtreeTransferApplyPreconditionDigest, deriveTodosTaskSubtreeTransferIdempotencyKey, deriveTodosTaskSubtreeTransferRollbackPreconditionDigest, parseTodosTaskSubtreeTransferApply, parseTodosTaskSubtreeTransferInspect, parseTodosTaskSubtreeTransferRollback, taskSubtreeTransferRequestDigest, taskSubtreeTransferRollbackRequestDigest, } from "./authority.js";
|
|
2
|
+
export { TodosTaskSubtreeTransferHttpClient, createTodosTaskSubtreeTransferHttpClient, handleTodosTaskSubtreeTransferHttpRequest, } from "./http.js";
|
|
3
|
+
export { TODOS_TASK_SUBTREE_TRANSFER_ROUTE, TODOS_TASK_SUBTREE_TRANSFER_SCHEMA_VERSION, TodosTaskSubtreeTransferError, } from "./types.js";
|
|
4
|
+
export type * from "./types.js";
|
|
5
|
+
export { postgresTodosTaskSubtreeTransferSchemaSql, sqliteTodosTaskSubtreeTransferSchemaSql, } from "./schema-sql.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/task-subtree-transfer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6CAA6C,EAC7C,+CAA+C,EAC/C,6CAA6C,EAC7C,qDAAqD,EACrD,4CAA4C,EAC5C,wDAAwD,EACxD,kCAAkC,EAClC,oCAAoC,EACpC,qCAAqC,EACrC,gCAAgC,EAChC,wCAAwC,GACzC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kCAAkC,EAClC,wCAAwC,EACxC,yCAAyC,GAC1C,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iCAAiC,EACjC,0CAA0C,EAC1C,6BAA6B,GAC9B,MAAM,YAAY,CAAC;AACpB,mBAAmB,YAAY,CAAC;AAChC,OAAO,EACL,yCAAyC,EACzC,uCAAuC,GACxC,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type TodosTaskSubtreeTransferApplyRequest, type TodosTaskSubtreeTransferInspection, type TodosTaskSubtreeTransferPlanImage, type TodosTaskSubtreeTransferTaskImage } from "./types.js";
|
|
2
|
+
export interface TransferTaskRecord extends TodosTaskSubtreeTransferTaskImage {
|
|
3
|
+
archived_at?: string | null;
|
|
4
|
+
}
|
|
5
|
+
export interface TransferPlanRecord extends TodosTaskSubtreeTransferPlanImage {
|
|
6
|
+
}
|
|
7
|
+
export interface TransferSnapshot {
|
|
8
|
+
source_tasks: TransferTaskRecord[];
|
|
9
|
+
plan_tasks: TransferTaskRecord[];
|
|
10
|
+
plans: TransferPlanRecord[];
|
|
11
|
+
destination_project_found: boolean;
|
|
12
|
+
destination_task_list_found: boolean;
|
|
13
|
+
destination_parent_found: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface PreparedTransfer {
|
|
16
|
+
inspection: TodosTaskSubtreeTransferInspection;
|
|
17
|
+
prior_tasks: TodosTaskSubtreeTransferTaskImage[];
|
|
18
|
+
prior_plans: TodosTaskSubtreeTransferPlanImage[];
|
|
19
|
+
task_plan_targets: Map<string, string | null>;
|
|
20
|
+
}
|
|
21
|
+
export declare function sourcePopulationDigest(tasks: TransferTaskRecord[]): string;
|
|
22
|
+
export declare function prepareTransfer(snapshot: TransferSnapshot, input: TodosTaskSubtreeTransferApplyRequest | (Omit<TodosTaskSubtreeTransferApplyRequest, "version" | "operation_id" | "step_id" | "idempotency_key" | "precondition_digest" | "expected_root_parent_id" | "source_population_digest" | "expected_tasks" | "shared_plan_splits">)): PreparedTransfer;
|
|
23
|
+
export declare function validateApplySnapshot(prepared: PreparedTransfer, request: TodosTaskSubtreeTransferApplyRequest, plans: TransferPlanRecord[]): void;
|
|
24
|
+
//# sourceMappingURL=planner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../../src/task-subtree-transfer/planner.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,oCAAoC,EACzC,KAAK,kCAAkC,EACvC,KAAK,iCAAiC,EACtC,KAAK,iCAAiC,EACvC,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,kBAAmB,SAAQ,iCAAiC;IAC3E,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAmB,SAAQ,iCAAiC;CAAG;AAEhF,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,yBAAyB,EAAE,OAAO,CAAC;IACnC,2BAA2B,EAAE,OAAO,CAAC;IACrC,wBAAwB,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,kCAAkC,CAAC;IAC/C,WAAW,EAAE,iCAAiC,EAAE,CAAC;IACjD,WAAW,EAAE,iCAAiC,EAAE,CAAC;IACjD,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;CAC/C;AAcD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,MAAM,CAO1E;AAkDD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,oCAAoC,GAAG,CAAC,IAAI,CAAC,oCAAoC,EACtF,SAAS,GAAG,cAAc,GAAG,SAAS,GAAG,iBAAiB,GAAG,qBAAqB,GAChF,yBAAyB,GAAG,0BAA0B,GAAG,gBAAgB,GAAG,oBAAoB,CAAC,CAAC,GACrG,gBAAgB,CAuElB;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,oCAAoC,EAC7C,KAAK,EAAE,kBAAkB,EAAE,GAC1B,IAAI,CAsEN"}
|