@hasna/todos 0.11.66 → 0.11.67
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/README.md +2 -2
- package/dashboard/dist/assets/{index-aJefI7kh.js → index-DVotjwab.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dist/cli/commands/storage-commands.d.ts +5 -0
- package/dist/cli/commands/storage-commands.d.ts.map +1 -1
- package/dist/cli/index.js +667 -169
- package/dist/contracts.js +301 -71
- package/dist/db/agents.d.ts.map +1 -1
- package/dist/db/audit.d.ts.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/plans.d.ts.map +1 -1
- package/dist/db/projects.d.ts.map +1 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/storage-tombstones.d.ts +26 -0
- package/dist/db/storage-tombstones.d.ts.map +1 -0
- 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-runs.d.ts.map +1 -1
- package/dist/db/templates.d.ts.map +1 -1
- package/dist/index.js +628 -160
- package/dist/lib/approval-gates.d.ts.map +1 -1
- package/dist/lib/event-emission-safety.d.ts +9 -0
- package/dist/lib/event-emission-safety.d.ts.map +1 -0
- package/dist/lib/event-hooks.d.ts +1 -0
- package/dist/lib/event-hooks.d.ts.map +1 -1
- package/dist/lib/feature-manifest.d.ts.map +1 -1
- package/dist/lib/review-queues.d.ts.map +1 -1
- package/dist/lib/shared-events.d.ts +1 -0
- package/dist/lib/shared-events.d.ts.map +1 -1
- package/dist/mcp/index.js +343 -104
- package/dist/registry.js +301 -71
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +366 -127
- package/dist/storage/interfaces.d.ts +20 -0
- package/dist/storage/interfaces.d.ts.map +1 -1
- package/dist/storage/postgres-adapter.d.ts.map +1 -1
- package/dist/storage/postgres-sync.d.ts +6 -1
- package/dist/storage/postgres-sync.d.ts.map +1 -1
- package/dist/storage/sqlite-snapshot.d.ts.map +1 -1
- package/dist/storage.js +569 -94
- package/dist/types/index.d.ts +22 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/contracts.js
CHANGED
|
@@ -1182,6 +1182,22 @@ var init_migrations = __esm(() => {
|
|
|
1182
1182
|
CREATE INDEX IF NOT EXISTS idx_task_findings_source ON task_findings(source);
|
|
1183
1183
|
CREATE INDEX IF NOT EXISTS idx_task_findings_fingerprint ON task_findings(fingerprint);
|
|
1184
1184
|
INSERT OR IGNORE INTO _migrations (id) VALUES (62);
|
|
1185
|
+
`,
|
|
1186
|
+
`
|
|
1187
|
+
CREATE TABLE IF NOT EXISTS storage_tombstones (
|
|
1188
|
+
id TEXT PRIMARY KEY,
|
|
1189
|
+
object_type TEXT NOT NULL,
|
|
1190
|
+
object_id TEXT NOT NULL,
|
|
1191
|
+
deleted_at TEXT NOT NULL,
|
|
1192
|
+
updated_at TEXT NOT NULL,
|
|
1193
|
+
source_machine_id TEXT,
|
|
1194
|
+
payload TEXT,
|
|
1195
|
+
version INTEGER,
|
|
1196
|
+
UNIQUE(object_type, object_id)
|
|
1197
|
+
);
|
|
1198
|
+
CREATE INDEX IF NOT EXISTS idx_storage_tombstones_object ON storage_tombstones(object_type, object_id);
|
|
1199
|
+
CREATE INDEX IF NOT EXISTS idx_storage_tombstones_updated ON storage_tombstones(updated_at);
|
|
1200
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (63);
|
|
1185
1201
|
`
|
|
1186
1202
|
];
|
|
1187
1203
|
});
|
|
@@ -1703,6 +1719,20 @@ function ensureSchema(db) {
|
|
|
1703
1719
|
)`);
|
|
1704
1720
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_machine_paths_project ON project_machine_paths(project_id)");
|
|
1705
1721
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_machine_paths_machine ON project_machine_paths(machine_id)");
|
|
1722
|
+
ensureTable("storage_tombstones", `
|
|
1723
|
+
CREATE TABLE storage_tombstones (
|
|
1724
|
+
id TEXT PRIMARY KEY,
|
|
1725
|
+
object_type TEXT NOT NULL,
|
|
1726
|
+
object_id TEXT NOT NULL,
|
|
1727
|
+
deleted_at TEXT NOT NULL,
|
|
1728
|
+
updated_at TEXT NOT NULL,
|
|
1729
|
+
source_machine_id TEXT,
|
|
1730
|
+
payload TEXT,
|
|
1731
|
+
version INTEGER,
|
|
1732
|
+
UNIQUE(object_type, object_id)
|
|
1733
|
+
)`);
|
|
1734
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_storage_tombstones_object ON storage_tombstones(object_type, object_id)");
|
|
1735
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_storage_tombstones_updated ON storage_tombstones(updated_at)");
|
|
1706
1736
|
ensureTable("machines", `
|
|
1707
1737
|
CREATE TABLE machines (
|
|
1708
1738
|
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, hostname TEXT, platform TEXT,
|
|
@@ -6068,6 +6098,80 @@ init_config();
|
|
|
6068
6098
|
init_types();
|
|
6069
6099
|
init_database();
|
|
6070
6100
|
init_machines();
|
|
6101
|
+
|
|
6102
|
+
// src/db/storage-tombstones.ts
|
|
6103
|
+
init_database();
|
|
6104
|
+
init_machines();
|
|
6105
|
+
function recordStorageTombstone(input, db) {
|
|
6106
|
+
const d = db ?? getDatabase();
|
|
6107
|
+
const deletedAt = input.deleted_at ?? now();
|
|
6108
|
+
const machineId = input.source_machine_id ?? currentStorageMachineId(d);
|
|
6109
|
+
d.run(`INSERT INTO storage_tombstones (
|
|
6110
|
+
id, object_type, object_id, deleted_at, updated_at, source_machine_id, payload, version
|
|
6111
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
6112
|
+
ON CONFLICT(object_type, object_id) DO UPDATE SET
|
|
6113
|
+
deleted_at = excluded.deleted_at,
|
|
6114
|
+
updated_at = excluded.updated_at,
|
|
6115
|
+
source_machine_id = excluded.source_machine_id,
|
|
6116
|
+
payload = excluded.payload,
|
|
6117
|
+
version = excluded.version
|
|
6118
|
+
WHERE storage_tombstones.updated_at IS NULL OR storage_tombstones.updated_at <= excluded.updated_at`, [
|
|
6119
|
+
uuid(),
|
|
6120
|
+
input.object_type,
|
|
6121
|
+
input.object_id,
|
|
6122
|
+
deletedAt,
|
|
6123
|
+
deletedAt,
|
|
6124
|
+
machineId,
|
|
6125
|
+
input.payload ? JSON.stringify(input.payload) : null,
|
|
6126
|
+
input.version ?? null
|
|
6127
|
+
]);
|
|
6128
|
+
return getStorageTombstone(input.object_type, input.object_id, d);
|
|
6129
|
+
}
|
|
6130
|
+
function getStorageTombstone(objectType, objectId, db) {
|
|
6131
|
+
const d = db ?? getDatabase();
|
|
6132
|
+
const row = d.query("SELECT * FROM storage_tombstones WHERE object_type = ? AND object_id = ?").get(objectType, objectId);
|
|
6133
|
+
return row ? rowToStorageTombstone(row) : null;
|
|
6134
|
+
}
|
|
6135
|
+
function listStorageTombstones(db) {
|
|
6136
|
+
const d = db ?? getDatabase();
|
|
6137
|
+
return d.query("SELECT * FROM storage_tombstones ORDER BY updated_at ASC, object_type ASC, object_id ASC").all().map(rowToStorageTombstone);
|
|
6138
|
+
}
|
|
6139
|
+
function shouldApplyStorageTombstone(tombstone, existingUpdatedAt) {
|
|
6140
|
+
const tombstoneClock = Date.parse(tombstone.updated_at || tombstone.deleted_at);
|
|
6141
|
+
if (!existingUpdatedAt)
|
|
6142
|
+
return true;
|
|
6143
|
+
const existingClock = Date.parse(existingUpdatedAt);
|
|
6144
|
+
if (Number.isNaN(tombstoneClock))
|
|
6145
|
+
return true;
|
|
6146
|
+
if (Number.isNaN(existingClock))
|
|
6147
|
+
return true;
|
|
6148
|
+
return tombstoneClock >= existingClock;
|
|
6149
|
+
}
|
|
6150
|
+
function rowToStorageTombstone(row) {
|
|
6151
|
+
return {
|
|
6152
|
+
...row,
|
|
6153
|
+
payload: parsePayload(row.payload)
|
|
6154
|
+
};
|
|
6155
|
+
}
|
|
6156
|
+
function parsePayload(value) {
|
|
6157
|
+
if (!value)
|
|
6158
|
+
return null;
|
|
6159
|
+
try {
|
|
6160
|
+
const parsed = JSON.parse(value);
|
|
6161
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
6162
|
+
} catch {
|
|
6163
|
+
return null;
|
|
6164
|
+
}
|
|
6165
|
+
}
|
|
6166
|
+
function currentStorageMachineId(db) {
|
|
6167
|
+
try {
|
|
6168
|
+
return getMachineId(db);
|
|
6169
|
+
} catch {
|
|
6170
|
+
return null;
|
|
6171
|
+
}
|
|
6172
|
+
}
|
|
6173
|
+
|
|
6174
|
+
// src/db/projects.ts
|
|
6071
6175
|
function slugify(name) {
|
|
6072
6176
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
6073
6177
|
}
|
|
@@ -6097,8 +6201,9 @@ function createProject(input, db) {
|
|
|
6097
6201
|
const timestamp = now();
|
|
6098
6202
|
const taskListId = input.task_list_id ?? `todos-${slugify(input.name)}`;
|
|
6099
6203
|
const taskPrefix = input.task_prefix || generatePrefix(input.name, d);
|
|
6100
|
-
|
|
6101
|
-
|
|
6204
|
+
const machineId = currentStorageMachineId(d);
|
|
6205
|
+
d.run(`INSERT INTO projects (id, name, path, description, task_list_id, task_prefix, task_counter, created_at, updated_at, machine_id)
|
|
6206
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, taskPrefix, timestamp, timestamp, machineId]);
|
|
6102
6207
|
return getProject(id, d);
|
|
6103
6208
|
}
|
|
6104
6209
|
function getProject(id, db) {
|
|
@@ -6177,6 +6282,14 @@ function renameProject(id, input, db) {
|
|
|
6177
6282
|
}
|
|
6178
6283
|
function deleteProject(id, db) {
|
|
6179
6284
|
const d = db || getDatabase();
|
|
6285
|
+
const project = getProject(id, d);
|
|
6286
|
+
if (!project)
|
|
6287
|
+
return false;
|
|
6288
|
+
recordStorageTombstone({
|
|
6289
|
+
object_type: "projects",
|
|
6290
|
+
object_id: id,
|
|
6291
|
+
payload: project
|
|
6292
|
+
}, d);
|
|
6180
6293
|
const result = d.run("DELETE FROM projects WHERE id = ?", [id]);
|
|
6181
6294
|
return result.changes > 0;
|
|
6182
6295
|
}
|
|
@@ -6281,6 +6394,14 @@ function listMachineLocalPaths(projectId, db) {
|
|
|
6281
6394
|
function removeMachineLocalPath(projectId, machineId, db) {
|
|
6282
6395
|
const d = db || getDatabase();
|
|
6283
6396
|
const mid = machineId ?? getMachineId(d);
|
|
6397
|
+
const existing = d.query("SELECT * FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(projectId, mid);
|
|
6398
|
+
if (!existing)
|
|
6399
|
+
return false;
|
|
6400
|
+
recordStorageTombstone({
|
|
6401
|
+
object_type: "project_machine_paths",
|
|
6402
|
+
object_id: existing.id,
|
|
6403
|
+
payload: existing
|
|
6404
|
+
}, d);
|
|
6284
6405
|
const result = d.run("DELETE FROM project_machine_paths WHERE project_id = ? AND machine_id = ?", [projectId, mid]);
|
|
6285
6406
|
return result.changes > 0;
|
|
6286
6407
|
}
|
|
@@ -6329,20 +6450,69 @@ function checkCompletionGuard(task, agentId, db, configOverride) {
|
|
|
6329
6450
|
}
|
|
6330
6451
|
}
|
|
6331
6452
|
|
|
6453
|
+
// src/lib/event-emission-safety.ts
|
|
6454
|
+
init_database();
|
|
6455
|
+
init_sync_utils();
|
|
6456
|
+
import { tmpdir } from "os";
|
|
6457
|
+
import { resolve as resolve3, sep } from "path";
|
|
6458
|
+
function envFlag(name) {
|
|
6459
|
+
const value = process.env[name];
|
|
6460
|
+
return value === "1" || value === "true" || value === "yes";
|
|
6461
|
+
}
|
|
6462
|
+
function isUnder(parent, child) {
|
|
6463
|
+
const normalizedParent = resolve3(parent);
|
|
6464
|
+
const normalizedChild = resolve3(child);
|
|
6465
|
+
return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}${sep}`);
|
|
6466
|
+
}
|
|
6467
|
+
function databasePathFromDatabase(db) {
|
|
6468
|
+
const filename = db?.filename;
|
|
6469
|
+
return typeof filename === "string" && filename.trim() ? filename : undefined;
|
|
6470
|
+
}
|
|
6471
|
+
function isEphemeralTodosDatabase(dbPath) {
|
|
6472
|
+
const resolvedPath = dbPath ?? getDatabasePath();
|
|
6473
|
+
if (resolvedPath === ":memory:" || resolvedPath.startsWith("file::memory:"))
|
|
6474
|
+
return true;
|
|
6475
|
+
return isUnder(tmpdir(), resolvedPath);
|
|
6476
|
+
}
|
|
6477
|
+
function hasExplicitSharedEventsStore() {
|
|
6478
|
+
return Boolean(process.env["HASNA_EVENTS_DIR"] || process.env["HASNA_EVENTS_HOME"]);
|
|
6479
|
+
}
|
|
6480
|
+
function usesIsolatedTodosHome() {
|
|
6481
|
+
return isUnder(tmpdir(), getTodosGlobalDir());
|
|
6482
|
+
}
|
|
6483
|
+
function shouldEmitSharedTaskEvents(dbPath) {
|
|
6484
|
+
if (envFlag("TODOS_DISABLE_SHARED_EVENTS"))
|
|
6485
|
+
return false;
|
|
6486
|
+
if (envFlag("TODOS_ALLOW_EPHEMERAL_SHARED_EVENTS"))
|
|
6487
|
+
return true;
|
|
6488
|
+
if (!isEphemeralTodosDatabase(dbPath))
|
|
6489
|
+
return true;
|
|
6490
|
+
return hasExplicitSharedEventsStore();
|
|
6491
|
+
}
|
|
6492
|
+
function shouldDeliverLocalLifecycleHooks(dbPath) {
|
|
6493
|
+
if (envFlag("TODOS_DISABLE_LOCAL_EVENT_HOOKS"))
|
|
6494
|
+
return false;
|
|
6495
|
+
if (envFlag("TODOS_ALLOW_EPHEMERAL_LOCAL_EVENT_HOOKS"))
|
|
6496
|
+
return true;
|
|
6497
|
+
if (!isEphemeralTodosDatabase(dbPath))
|
|
6498
|
+
return true;
|
|
6499
|
+
return usesIsolatedTodosHome();
|
|
6500
|
+
}
|
|
6501
|
+
|
|
6332
6502
|
// src/lib/event-hooks.ts
|
|
6333
6503
|
init_redaction();
|
|
6334
6504
|
import { createHash, randomUUID } from "crypto";
|
|
6335
6505
|
import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
6336
|
-
import { dirname as dirname4, resolve as
|
|
6506
|
+
import { dirname as dirname4, resolve as resolve6 } from "path";
|
|
6337
6507
|
import { createConnection } from "net";
|
|
6338
6508
|
|
|
6339
6509
|
// src/lib/runner-sandbox.ts
|
|
6340
6510
|
init_config();
|
|
6341
|
-
import { relative as relative2, resolve as
|
|
6511
|
+
import { relative as relative2, resolve as resolve5 } from "path";
|
|
6342
6512
|
|
|
6343
6513
|
// src/lib/workspace-trust.ts
|
|
6344
6514
|
init_config();
|
|
6345
|
-
import { relative, resolve as
|
|
6515
|
+
import { relative, resolve as resolve4 } from "path";
|
|
6346
6516
|
var DEFAULT_DENYLIST = ["rm -rf", "mkfs", "dd if=", "curl | sh", "wget | sh"];
|
|
6347
6517
|
var DEFAULT_ENV_REDACTIONS = ["API_KEY", "TOKEN", "SECRET", "PASSWORD", "AUTH"];
|
|
6348
6518
|
var PRESET_DEFAULTS = {
|
|
@@ -6388,7 +6558,7 @@ var PRESET_DEFAULTS = {
|
|
|
6388
6558
|
}
|
|
6389
6559
|
};
|
|
6390
6560
|
function normalizePath(path) {
|
|
6391
|
-
return
|
|
6561
|
+
return resolve4(path);
|
|
6392
6562
|
}
|
|
6393
6563
|
function unique2(values) {
|
|
6394
6564
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -6525,7 +6695,7 @@ function checkWorkspacePermission(input = {}) {
|
|
|
6525
6695
|
var DEFAULT_COMMAND_DENYLIST = ["rm -rf", "mkfs", "dd if=", "curl | sh", "wget | sh"];
|
|
6526
6696
|
var DEFAULT_ENV_REDACTIONS2 = ["API_KEY", "TOKEN", "SECRET", "PASSWORD", "AUTH"];
|
|
6527
6697
|
function normalizePath2(path) {
|
|
6528
|
-
return
|
|
6698
|
+
return resolve5(path);
|
|
6529
6699
|
}
|
|
6530
6700
|
function unique3(values) {
|
|
6531
6701
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -6871,7 +7041,7 @@ async function deliverHook(hook, envelope) {
|
|
|
6871
7041
|
if (hook.target === "stdout") {
|
|
6872
7042
|
output = line.trim();
|
|
6873
7043
|
} else if (hook.target === "file") {
|
|
6874
|
-
const filePath =
|
|
7044
|
+
const filePath = resolve6(hook.file_path);
|
|
6875
7045
|
mkdirSync3(dirname4(filePath), { recursive: true });
|
|
6876
7046
|
appendFileSync(filePath, line);
|
|
6877
7047
|
} else if (hook.target === "socket") {
|
|
@@ -6946,6 +7116,8 @@ async function emitLocalEventHooks(input) {
|
|
|
6946
7116
|
return Promise.all(hooks.map((hook) => deliverHook(hook, envelope)));
|
|
6947
7117
|
}
|
|
6948
7118
|
function emitLocalEventHooksQuiet(input) {
|
|
7119
|
+
if (!shouldDeliverLocalLifecycleHooks(input.databasePath))
|
|
7120
|
+
return;
|
|
6949
7121
|
emitLocalEventHooks(input).catch(() => {});
|
|
6950
7122
|
}
|
|
6951
7123
|
async function testLocalEventHook(name, input) {
|
|
@@ -7262,7 +7434,7 @@ async function dispatchCommand(event, channel) {
|
|
|
7262
7434
|
HASNA_EVENT_SCHEMA_VERSION: event.schemaVersion,
|
|
7263
7435
|
HASNA_EVENT_JSON: eventJson
|
|
7264
7436
|
};
|
|
7265
|
-
return new Promise((
|
|
7437
|
+
return new Promise((resolve7) => {
|
|
7266
7438
|
const child = spawn(channel.command.command, channel.command.args ?? [], {
|
|
7267
7439
|
cwd: channel.command.cwd,
|
|
7268
7440
|
env,
|
|
@@ -7280,7 +7452,7 @@ async function dispatchCommand(event, channel) {
|
|
|
7280
7452
|
});
|
|
7281
7453
|
child.on("error", (error) => {
|
|
7282
7454
|
clearTimeout(timeout);
|
|
7283
|
-
|
|
7455
|
+
resolve7({
|
|
7284
7456
|
attempt: 1,
|
|
7285
7457
|
status: "failed",
|
|
7286
7458
|
startedAt,
|
|
@@ -7293,7 +7465,7 @@ async function dispatchCommand(event, channel) {
|
|
|
7293
7465
|
child.on("close", (code, signal) => {
|
|
7294
7466
|
clearTimeout(timeout);
|
|
7295
7467
|
const success = code === 0;
|
|
7296
|
-
|
|
7468
|
+
resolve7({
|
|
7297
7469
|
attempt: 1,
|
|
7298
7470
|
status: success ? "success" : "failed",
|
|
7299
7471
|
startedAt,
|
|
@@ -7561,14 +7733,15 @@ function createTaskList(input, db) {
|
|
|
7561
7733
|
const id = uuid();
|
|
7562
7734
|
const timestamp = now();
|
|
7563
7735
|
const slug = input.slug || slugify(input.name);
|
|
7736
|
+
const machineId = currentStorageMachineId(d);
|
|
7564
7737
|
if (!input.project_id) {
|
|
7565
7738
|
const existing = d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ?").get(slug);
|
|
7566
7739
|
if (existing) {
|
|
7567
7740
|
throw new Error(`Standalone task list with slug "${slug}" already exists`);
|
|
7568
7741
|
}
|
|
7569
7742
|
}
|
|
7570
|
-
d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at)
|
|
7571
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
|
|
7743
|
+
d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at, machine_id)
|
|
7744
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp, machineId]);
|
|
7572
7745
|
return getTaskList(id, d);
|
|
7573
7746
|
}
|
|
7574
7747
|
function getTaskList(id, db) {
|
|
@@ -7618,6 +7791,14 @@ function updateTaskList(id, input, db) {
|
|
|
7618
7791
|
}
|
|
7619
7792
|
function deleteTaskList(id, db) {
|
|
7620
7793
|
const d = db || getDatabase();
|
|
7794
|
+
const list = getTaskList(id, d);
|
|
7795
|
+
if (!list)
|
|
7796
|
+
return false;
|
|
7797
|
+
recordStorageTombstone({
|
|
7798
|
+
object_type: "task_lists",
|
|
7799
|
+
object_id: id,
|
|
7800
|
+
payload: list
|
|
7801
|
+
}, d);
|
|
7621
7802
|
return d.run("DELETE FROM task_lists WHERE id = ?", [id]).changes > 0;
|
|
7622
7803
|
}
|
|
7623
7804
|
function ensureTaskList(name, slug, projectId, db) {
|
|
@@ -7834,6 +8015,8 @@ function readMachineLocalPath(project) {
|
|
|
7834
8015
|
}
|
|
7835
8016
|
}
|
|
7836
8017
|
async function emitSharedTaskEvent(input) {
|
|
8018
|
+
if (!shouldEmitSharedTaskEvents(input.databasePath))
|
|
8019
|
+
return;
|
|
7837
8020
|
const data = taskEventData(input.task, input.data);
|
|
7838
8021
|
await new EventsClient().emit({
|
|
7839
8022
|
source: SOURCE,
|
|
@@ -7858,8 +8041,9 @@ function logTaskChange(taskId, action, field2, oldValue, newValue, agentId, db)
|
|
|
7858
8041
|
const d = db || getDatabase();
|
|
7859
8042
|
const id = uuid();
|
|
7860
8043
|
const timestamp = now();
|
|
7861
|
-
|
|
7862
|
-
|
|
8044
|
+
const machineId = currentStorageMachineId(d);
|
|
8045
|
+
d.run(`INSERT INTO task_history (id, task_id, action, field, old_value, new_value, agent_id, created_at, machine_id)
|
|
8046
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, taskId, action, field2 || null, oldValue ?? null, newValue ?? null, agentId || null, timestamp, machineId]);
|
|
7863
8047
|
try {
|
|
7864
8048
|
const { logActivity: logActivity2 } = (init_activity_audit(), __toCommonJS(exports_activity_audit));
|
|
7865
8049
|
logActivity2({
|
|
@@ -7872,7 +8056,7 @@ function logTaskChange(taskId, action, field2, oldValue, newValue, agentId, db)
|
|
|
7872
8056
|
actor_id: agentId ?? undefined
|
|
7873
8057
|
}, d);
|
|
7874
8058
|
} catch {}
|
|
7875
|
-
return { id, task_id: taskId, action, field: field2 || null, old_value: oldValue ?? null, new_value: newValue ?? null, agent_id: agentId || null, created_at: timestamp };
|
|
8059
|
+
return { id, task_id: taskId, action, field: field2 || null, old_value: oldValue ?? null, new_value: newValue ?? null, agent_id: agentId || null, created_at: timestamp, machine_id: machineId };
|
|
7876
8060
|
}
|
|
7877
8061
|
function getTaskHistory(taskId, db) {
|
|
7878
8062
|
const d = db || getDatabase();
|
|
@@ -8221,13 +8405,14 @@ function createTask(input, db) {
|
|
|
8221
8405
|
const d = db || getDatabase();
|
|
8222
8406
|
const timestamp = now();
|
|
8223
8407
|
const tags = input.tags || [];
|
|
8408
|
+
const machineId = currentStorageMachineId(d);
|
|
8224
8409
|
const assignedBy = input.assigned_by || input.agent_id;
|
|
8225
8410
|
const assignedFromProject = input.assigned_from_project || null;
|
|
8226
8411
|
let id = uuid();
|
|
8227
8412
|
for (let attempt = 0;attempt < 3; attempt++) {
|
|
8228
8413
|
try {
|
|
8229
|
-
d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, sla_minutes, confidence, retry_count, max_retries, retry_after, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, assigned_from_project, task_type)
|
|
8230
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
8414
|
+
d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, sla_minutes, confidence, retry_count, max_retries, retry_after, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, assigned_from_project, task_type, machine_id)
|
|
8415
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
8231
8416
|
id,
|
|
8232
8417
|
null,
|
|
8233
8418
|
input.project_id || null,
|
|
@@ -8264,7 +8449,8 @@ function createTask(input, db) {
|
|
|
8264
8449
|
input.spawned_from_session || null,
|
|
8265
8450
|
assignedBy || null,
|
|
8266
8451
|
assignedFromProject || null,
|
|
8267
|
-
input.task_type || null
|
|
8452
|
+
input.task_type || null,
|
|
8453
|
+
machineId
|
|
8268
8454
|
]);
|
|
8269
8455
|
break;
|
|
8270
8456
|
} catch (e) {
|
|
@@ -8280,9 +8466,10 @@ function createTask(input, db) {
|
|
|
8280
8466
|
}
|
|
8281
8467
|
const task = getTask(id, d);
|
|
8282
8468
|
const payload = taskEventData(task);
|
|
8469
|
+
const databasePath = databasePathFromDatabase(d);
|
|
8283
8470
|
dispatchWebhook2("task.created", payload, d).catch(() => {});
|
|
8284
|
-
emitLocalEventHooksQuiet({ type: "task.created", payload });
|
|
8285
|
-
emitSharedTaskEventQuiet({ type: "task.created", task });
|
|
8471
|
+
emitLocalEventHooksQuiet({ type: "task.created", payload, databasePath });
|
|
8472
|
+
emitSharedTaskEventQuiet({ type: "task.created", task, databasePath });
|
|
8286
8473
|
return task;
|
|
8287
8474
|
}
|
|
8288
8475
|
function getTask(id, db) {
|
|
@@ -8700,29 +8887,39 @@ function updateTask(id, input, db) {
|
|
|
8700
8887
|
approved_by: input.approved_by ?? task.approved_by,
|
|
8701
8888
|
approved_at: input.approved_by ? timestamp : task.approved_at
|
|
8702
8889
|
};
|
|
8890
|
+
const databasePath = databasePathFromDatabase(d);
|
|
8703
8891
|
if (input.assigned_to !== undefined && input.assigned_to !== task.assigned_to) {
|
|
8704
8892
|
const payload = taskEventData(updatedTask, { assigned_to: input.assigned_to, old_assigned_to: task.assigned_to });
|
|
8705
8893
|
dispatchWebhook2("task.assigned", payload, d).catch(() => {});
|
|
8706
|
-
emitLocalEventHooksQuiet({ type: "task.assigned", payload });
|
|
8707
|
-
emitSharedTaskEventQuiet({ type: "task.assigned", task: updatedTask, data: { old_assigned_to: task.assigned_to } });
|
|
8894
|
+
emitLocalEventHooksQuiet({ type: "task.assigned", payload, databasePath });
|
|
8895
|
+
emitSharedTaskEventQuiet({ type: "task.assigned", task: updatedTask, data: { old_assigned_to: task.assigned_to }, databasePath });
|
|
8708
8896
|
}
|
|
8709
8897
|
if (input.status !== undefined && input.status !== task.status) {
|
|
8710
8898
|
const payload = taskEventData(updatedTask, { old_status: task.status, new_status: input.status });
|
|
8711
8899
|
dispatchWebhook2("task.status_changed", payload, d).catch(() => {});
|
|
8712
|
-
emitLocalEventHooksQuiet({ type: "task.status_changed", payload });
|
|
8713
|
-
emitSharedTaskEventQuiet({ type: "task.status_changed", task: updatedTask, data: { old_status: task.status, new_status: input.status } });
|
|
8900
|
+
emitLocalEventHooksQuiet({ type: "task.status_changed", payload, databasePath });
|
|
8901
|
+
emitSharedTaskEventQuiet({ type: "task.status_changed", task: updatedTask, data: { old_status: task.status, new_status: input.status }, databasePath });
|
|
8714
8902
|
}
|
|
8715
8903
|
if (input.approved_by !== undefined) {
|
|
8716
|
-
emitLocalEventHooksQuiet({ type: "approval.decided", payload: { id, approved_by: input.approved_by, title: task.title } });
|
|
8904
|
+
emitLocalEventHooksQuiet({ type: "approval.decided", payload: { id, approved_by: input.approved_by, title: task.title }, databasePath });
|
|
8717
8905
|
}
|
|
8718
8906
|
const updatePayload = taskEventData(updatedTask);
|
|
8719
8907
|
dispatchWebhook2("task.updated", updatePayload, d).catch(() => {});
|
|
8720
|
-
emitLocalEventHooksQuiet({ type: "task.updated", payload: updatePayload });
|
|
8721
|
-
emitSharedTaskEventQuiet({ type: "task.updated", task: updatedTask });
|
|
8908
|
+
emitLocalEventHooksQuiet({ type: "task.updated", payload: updatePayload, databasePath });
|
|
8909
|
+
emitSharedTaskEventQuiet({ type: "task.updated", task: updatedTask, databasePath });
|
|
8722
8910
|
return updatedTask;
|
|
8723
8911
|
}
|
|
8724
8912
|
function deleteTask(id, db) {
|
|
8725
8913
|
const d = db || getDatabase();
|
|
8914
|
+
const row = d.query("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
8915
|
+
if (!row)
|
|
8916
|
+
return false;
|
|
8917
|
+
recordStorageTombstone({
|
|
8918
|
+
object_type: "tasks",
|
|
8919
|
+
object_id: id,
|
|
8920
|
+
payload: rowToTask(row),
|
|
8921
|
+
version: row.version
|
|
8922
|
+
}, d);
|
|
8726
8923
|
const result = d.run("DELETE FROM tasks WHERE id = ?", [id]);
|
|
8727
8924
|
return result.changes > 0;
|
|
8728
8925
|
}
|
|
@@ -8854,8 +9051,9 @@ function resolveTemplateId(id, d) {
|
|
|
8854
9051
|
function createTemplate(input, db) {
|
|
8855
9052
|
const d = db || getDatabase();
|
|
8856
9053
|
const id = uuid();
|
|
8857
|
-
|
|
8858
|
-
|
|
9054
|
+
const machineId = currentStorageMachineId(d);
|
|
9055
|
+
d.run(`INSERT INTO task_templates (id, name, title_pattern, description, priority, tags, variables, project_id, plan_id, metadata, created_at, machine_id)
|
|
9056
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
8859
9057
|
id,
|
|
8860
9058
|
input.name,
|
|
8861
9059
|
input.title_pattern,
|
|
@@ -8866,7 +9064,8 @@ function createTemplate(input, db) {
|
|
|
8866
9064
|
input.project_id || null,
|
|
8867
9065
|
input.plan_id || null,
|
|
8868
9066
|
JSON.stringify(input.metadata || {}),
|
|
8869
|
-
now()
|
|
9067
|
+
now(),
|
|
9068
|
+
machineId
|
|
8870
9069
|
]);
|
|
8871
9070
|
if (input.tasks && input.tasks.length > 0) {
|
|
8872
9071
|
addTemplateTasks(id, input.tasks, d);
|
|
@@ -8890,6 +9089,15 @@ function deleteTemplate(id, db) {
|
|
|
8890
9089
|
const resolved = resolveTemplateId(id, d);
|
|
8891
9090
|
if (!resolved)
|
|
8892
9091
|
return false;
|
|
9092
|
+
const template = getTemplate(resolved, d);
|
|
9093
|
+
if (!template)
|
|
9094
|
+
return false;
|
|
9095
|
+
recordStorageTombstone({
|
|
9096
|
+
object_type: "templates",
|
|
9097
|
+
object_id: resolved,
|
|
9098
|
+
payload: template,
|
|
9099
|
+
version: template.version
|
|
9100
|
+
}, d);
|
|
8893
9101
|
return d.run("DELETE FROM task_templates WHERE id = ?", [resolved]).changes > 0;
|
|
8894
9102
|
}
|
|
8895
9103
|
function updateTemplate(id, updates, db) {
|
|
@@ -9419,6 +9627,7 @@ function getBlockingDeps(id, db) {
|
|
|
9419
9627
|
}
|
|
9420
9628
|
function startTask(id, agentId, db) {
|
|
9421
9629
|
const d = db || getDatabase();
|
|
9630
|
+
const databasePath = databasePathFromDatabase(d);
|
|
9422
9631
|
const task = getTask(id, d);
|
|
9423
9632
|
if (!task)
|
|
9424
9633
|
throw new TaskNotFoundError(id);
|
|
@@ -9433,7 +9642,8 @@ function startTask(id, agentId, db) {
|
|
|
9433
9642
|
agent_id: agentId,
|
|
9434
9643
|
title: task.title,
|
|
9435
9644
|
blockers: blocking.map((b) => ({ id: b.id, short_id: b.short_id, title: b.title, status: b.status }))
|
|
9436
|
-
}
|
|
9645
|
+
},
|
|
9646
|
+
databasePath
|
|
9437
9647
|
});
|
|
9438
9648
|
throw new Error(`Task is blocked by ${blocking.length} unfinished dependency(ies): ${blockerIds}`);
|
|
9439
9649
|
}
|
|
@@ -9455,12 +9665,13 @@ function startTask(id, agentId, db) {
|
|
|
9455
9665
|
const startedTask = { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, started_at: task.started_at || timestamp, version: task.version + 1, updated_at: timestamp };
|
|
9456
9666
|
const payload = taskEventData(startedTask, { agent_id: agentId });
|
|
9457
9667
|
dispatchWebhook2("task.started", payload, d).catch(() => {});
|
|
9458
|
-
emitLocalEventHooksQuiet({ type: "task.started", payload });
|
|
9459
|
-
emitSharedTaskEventQuiet({ type: "task.started", task: startedTask, data: { agent_id: agentId } });
|
|
9668
|
+
emitLocalEventHooksQuiet({ type: "task.started", payload, databasePath });
|
|
9669
|
+
emitSharedTaskEventQuiet({ type: "task.started", task: startedTask, data: { agent_id: agentId }, databasePath });
|
|
9460
9670
|
return startedTask;
|
|
9461
9671
|
}
|
|
9462
9672
|
function completeTask(id, agentId, db, options) {
|
|
9463
9673
|
const d = db || getDatabase();
|
|
9674
|
+
const databasePath = databasePathFromDatabase(d);
|
|
9464
9675
|
const task = getTask(id, d);
|
|
9465
9676
|
if (!task)
|
|
9466
9677
|
throw new TaskNotFoundError(id);
|
|
@@ -9506,8 +9717,8 @@ function completeTask(id, agentId, db, options) {
|
|
|
9506
9717
|
};
|
|
9507
9718
|
const completionPayload = taskEventData(completedTaskForEvent, { agent_id: agentId, completed_at: timestamp });
|
|
9508
9719
|
dispatchWebhook2("task.completed", completionPayload, d).catch(() => {});
|
|
9509
|
-
emitLocalEventHooksQuiet({ type: "task.completed", payload: completionPayload });
|
|
9510
|
-
emitSharedTaskEventQuiet({ type: "task.completed", task: completedTaskForEvent, data: { agent_id: agentId, completed_at: timestamp } });
|
|
9720
|
+
emitLocalEventHooksQuiet({ type: "task.completed", payload: completionPayload, databasePath });
|
|
9721
|
+
emitSharedTaskEventQuiet({ type: "task.completed", task: completedTaskForEvent, data: { agent_id: agentId, completed_at: timestamp }, databasePath });
|
|
9511
9722
|
let spawnedTask = null;
|
|
9512
9723
|
if (task.recurrence_rule && !options?.skip_recurrence) {
|
|
9513
9724
|
spawnedTask = spawnNextRecurrence(task, d, timestamp);
|
|
@@ -9551,9 +9762,9 @@ function completeTask(id, agentId, db, options) {
|
|
|
9551
9762
|
const depTask = getTask(dep.id, d);
|
|
9552
9763
|
const payload = depTask ? taskEventData(depTask, { unblocked_by: id }) : { id: dep.id, unblocked_by: id, title: dep.title };
|
|
9553
9764
|
dispatchWebhook2("task.unblocked", payload, d).catch(() => {});
|
|
9554
|
-
emitLocalEventHooksQuiet({ type: "task.unblocked", payload });
|
|
9765
|
+
emitLocalEventHooksQuiet({ type: "task.unblocked", payload, databasePath });
|
|
9555
9766
|
if (depTask)
|
|
9556
|
-
emitSharedTaskEventQuiet({ type: "task.unblocked", task: depTask, data: { unblocked_by: id } });
|
|
9767
|
+
emitSharedTaskEventQuiet({ type: "task.unblocked", task: depTask, data: { unblocked_by: id }, databasePath });
|
|
9557
9768
|
}
|
|
9558
9769
|
}
|
|
9559
9770
|
return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, confidence, version: task.version + 1, updated_at: timestamp, metadata: meta };
|
|
@@ -9723,6 +9934,7 @@ function getTasksChangedSince(since, filters, db) {
|
|
|
9723
9934
|
}
|
|
9724
9935
|
function failTask(id, agentId, reason, options, db) {
|
|
9725
9936
|
const d = db || getDatabase();
|
|
9937
|
+
const databasePath = databasePathFromDatabase(d);
|
|
9726
9938
|
const task = getTask(id, d);
|
|
9727
9939
|
if (!task)
|
|
9728
9940
|
throw new TaskNotFoundError(id);
|
|
@@ -9751,8 +9963,8 @@ function failTask(id, agentId, reason, options, db) {
|
|
|
9751
9963
|
logTaskChange(id, "fail", "status", task.status, "failed", agentId || null, d);
|
|
9752
9964
|
const failurePayload = taskEventData(failedTask, { reason, error_code: options?.error_code, agent_id: agentId });
|
|
9753
9965
|
dispatchWebhook2("task.failed", failurePayload, d).catch(() => {});
|
|
9754
|
-
emitLocalEventHooksQuiet({ type: "task.failed", payload: failurePayload });
|
|
9755
|
-
emitSharedTaskEventQuiet({ type: "task.failed", task: failedTask, data: { reason, error_code: options?.error_code, agent_id: agentId }, severity: "warning" });
|
|
9966
|
+
emitLocalEventHooksQuiet({ type: "task.failed", payload: failurePayload, databasePath });
|
|
9967
|
+
emitSharedTaskEventQuiet({ type: "task.failed", task: failedTask, data: { reason, error_code: options?.error_code, agent_id: agentId }, severity: "warning", databasePath });
|
|
9756
9968
|
let retryTask;
|
|
9757
9969
|
if (options?.retry) {
|
|
9758
9970
|
const retryCount = (task.retry_count || 0) + 1;
|
|
@@ -9812,6 +10024,7 @@ function getStaleTasks(staleQuery = 30, filters, db) {
|
|
|
9812
10024
|
}
|
|
9813
10025
|
function stealTask(agentId, opts, db) {
|
|
9814
10026
|
const d = db || getDatabase();
|
|
10027
|
+
const databasePath = databasePathFromDatabase(d);
|
|
9815
10028
|
const staleMinutes = opts?.stale_minutes ?? 30;
|
|
9816
10029
|
const staleTasks = getStaleTasks(staleMinutes, { project_id: opts?.project_id, task_list_id: opts?.task_list_id }, d);
|
|
9817
10030
|
if (staleTasks.length === 0)
|
|
@@ -9830,8 +10043,8 @@ function stealTask(agentId, opts, db) {
|
|
|
9830
10043
|
const stolenTask = { ...target, assigned_to: agentId, locked_by: agentId, locked_at: timestamp, updated_at: timestamp, version: target.version + 1 };
|
|
9831
10044
|
const payload = taskEventData(stolenTask, { agent_id: agentId, stolen_from: target.assigned_to });
|
|
9832
10045
|
dispatchWebhook2("task.assigned", payload, d).catch(() => {});
|
|
9833
|
-
emitLocalEventHooksQuiet({ type: "task.assigned", payload });
|
|
9834
|
-
emitSharedTaskEventQuiet({ type: "task.assigned", task: stolenTask, data: { agent_id: agentId, stolen_from: target.assigned_to } });
|
|
10046
|
+
emitLocalEventHooksQuiet({ type: "task.assigned", payload, databasePath });
|
|
10047
|
+
emitSharedTaskEventQuiet({ type: "task.assigned", task: stolenTask, data: { agent_id: agentId, stolen_from: target.assigned_to }, databasePath });
|
|
9835
10048
|
return stolenTask;
|
|
9836
10049
|
}
|
|
9837
10050
|
function claimOrSteal(agentId, filters, db) {
|
|
@@ -10422,8 +10635,9 @@ function createPlan(input, db) {
|
|
|
10422
10635
|
const d = db || getDatabase();
|
|
10423
10636
|
const id = uuid();
|
|
10424
10637
|
const timestamp = now();
|
|
10425
|
-
|
|
10426
|
-
|
|
10638
|
+
const machineId = currentStorageMachineId(d);
|
|
10639
|
+
d.run(`INSERT INTO plans (id, project_id, task_list_id, agent_id, name, description, status, created_at, updated_at, machine_id)
|
|
10640
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
10427
10641
|
id,
|
|
10428
10642
|
input.project_id || null,
|
|
10429
10643
|
input.task_list_id || null,
|
|
@@ -10432,7 +10646,8 @@ function createPlan(input, db) {
|
|
|
10432
10646
|
input.description || null,
|
|
10433
10647
|
input.status || "active",
|
|
10434
10648
|
timestamp,
|
|
10435
|
-
timestamp
|
|
10649
|
+
timestamp,
|
|
10650
|
+
machineId
|
|
10436
10651
|
]);
|
|
10437
10652
|
return getPlan(id, d);
|
|
10438
10653
|
}
|
|
@@ -10480,12 +10695,21 @@ function updatePlan(id, input, db) {
|
|
|
10480
10695
|
const updated = getPlan(id, d);
|
|
10481
10696
|
emitLocalEventHooksQuiet({
|
|
10482
10697
|
type: "plan.updated",
|
|
10483
|
-
payload: { id, old_status: plan.status, new_status: updated.status, name: updated.name, project_id: updated.project_id }
|
|
10698
|
+
payload: { id, old_status: plan.status, new_status: updated.status, name: updated.name, project_id: updated.project_id },
|
|
10699
|
+
databasePath: databasePathFromDatabase(d)
|
|
10484
10700
|
});
|
|
10485
10701
|
return updated;
|
|
10486
10702
|
}
|
|
10487
10703
|
function deletePlan(id, db) {
|
|
10488
10704
|
const d = db || getDatabase();
|
|
10705
|
+
const plan = getPlan(id, d);
|
|
10706
|
+
if (!plan)
|
|
10707
|
+
return false;
|
|
10708
|
+
recordStorageTombstone({
|
|
10709
|
+
object_type: "plans",
|
|
10710
|
+
object_id: id,
|
|
10711
|
+
payload: plan
|
|
10712
|
+
}, d);
|
|
10489
10713
|
const result = d.run("DELETE FROM plans WHERE id = ?", [id]);
|
|
10490
10714
|
return result.changes > 0;
|
|
10491
10715
|
}
|
|
@@ -10892,20 +11116,20 @@ init_database();
|
|
|
10892
11116
|
init_redaction();
|
|
10893
11117
|
import { createHash as createHash2 } from "crypto";
|
|
10894
11118
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
10895
|
-
import { basename, dirname as dirname5, join as join6, resolve as
|
|
10896
|
-
import { tmpdir } from "os";
|
|
11119
|
+
import { basename, dirname as dirname5, join as join6, resolve as resolve7 } from "path";
|
|
11120
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
10897
11121
|
function isInMemoryDb2(path) {
|
|
10898
11122
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
10899
11123
|
}
|
|
10900
11124
|
function artifactStoreRoot() {
|
|
10901
11125
|
if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
|
|
10902
|
-
return
|
|
11126
|
+
return resolve7(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
|
|
10903
11127
|
if (process.env["TODOS_ARTIFACTS_DIR"])
|
|
10904
|
-
return
|
|
11128
|
+
return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
|
|
10905
11129
|
const dbPath = getDatabasePath();
|
|
10906
11130
|
if (isInMemoryDb2(dbPath))
|
|
10907
|
-
return join6(
|
|
10908
|
-
return join6(dirname5(
|
|
11131
|
+
return join6(tmpdir2(), "hasna-todos-artifacts");
|
|
11132
|
+
return join6(dirname5(resolve7(dbPath)), "artifacts");
|
|
10909
11133
|
}
|
|
10910
11134
|
function artifactStorePath(relativePath) {
|
|
10911
11135
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
@@ -10952,7 +11176,7 @@ function mediaTypeFor(path, textLike) {
|
|
|
10952
11176
|
return "application/octet-stream";
|
|
10953
11177
|
}
|
|
10954
11178
|
function storeArtifactContent(input) {
|
|
10955
|
-
const sourcePath =
|
|
11179
|
+
const sourcePath = resolve7(input.path);
|
|
10956
11180
|
if (!existsSync8(sourcePath))
|
|
10957
11181
|
return null;
|
|
10958
11182
|
const sourceStat = statSync2(sourcePath);
|
|
@@ -11108,19 +11332,19 @@ function importStoredArtifactContent(content) {
|
|
|
11108
11332
|
var DEFAULT_DELETED_RETENTION_DAYS = 30;
|
|
11109
11333
|
function getArtifactStoreRoot(dbPath) {
|
|
11110
11334
|
if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
|
|
11111
|
-
return
|
|
11335
|
+
return resolve7(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
|
|
11112
11336
|
if (process.env["TODOS_ARTIFACTS_DIR"])
|
|
11113
|
-
return
|
|
11337
|
+
return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
|
|
11114
11338
|
const path = dbPath ?? getDatabasePath();
|
|
11115
11339
|
if (isInMemoryDb2(path))
|
|
11116
|
-
return join6(
|
|
11117
|
-
return join6(dirname5(
|
|
11340
|
+
return join6(tmpdir2(), "hasna-todos-artifacts");
|
|
11341
|
+
return join6(dirname5(resolve7(path)), "artifacts");
|
|
11118
11342
|
}
|
|
11119
11343
|
function computeContentHash(path) {
|
|
11120
|
-
return sha256(readFileSync4(
|
|
11344
|
+
return sha256(readFileSync4(resolve7(path)));
|
|
11121
11345
|
}
|
|
11122
11346
|
function storeArtifactFile(input) {
|
|
11123
|
-
const sourcePath =
|
|
11347
|
+
const sourcePath = resolve7(input.sourcePath);
|
|
11124
11348
|
if (!existsSync8(sourcePath)) {
|
|
11125
11349
|
throw new Error(`Source file not found: ${input.sourcePath}`);
|
|
11126
11350
|
}
|
|
@@ -11169,7 +11393,7 @@ function buildArtifactExportManifest(artifacts, dbPath) {
|
|
|
11169
11393
|
};
|
|
11170
11394
|
}
|
|
11171
11395
|
function writeArtifactExportManifest(manifest, outputPath) {
|
|
11172
|
-
const destination =
|
|
11396
|
+
const destination = resolve7(outputPath);
|
|
11173
11397
|
mkdirSync4(dirname5(destination), { recursive: true });
|
|
11174
11398
|
writeFileSync2(destination, `${JSON.stringify(manifest, null, 2)}
|
|
11175
11399
|
`);
|
|
@@ -11556,7 +11780,11 @@ function startTaskRun(input, db) {
|
|
|
11556
11780
|
}, d);
|
|
11557
11781
|
}
|
|
11558
11782
|
const run = getTaskRun(id, d);
|
|
11559
|
-
emitLocalEventHooksQuiet({
|
|
11783
|
+
emitLocalEventHooksQuiet({
|
|
11784
|
+
type: "run.started",
|
|
11785
|
+
payload: { id: run.id, task_id: run.task_id, agent_id: run.agent_id, title: run.title },
|
|
11786
|
+
databasePath: databasePathFromDatabase(d)
|
|
11787
|
+
});
|
|
11560
11788
|
return run;
|
|
11561
11789
|
}
|
|
11562
11790
|
function beginTaskRunTransaction(input, db) {
|
|
@@ -11825,7 +12053,8 @@ function finishTaskRun(input, db) {
|
|
|
11825
12053
|
const updated = getTaskRun(run.id, d);
|
|
11826
12054
|
emitLocalEventHooksQuiet({
|
|
11827
12055
|
type: `run.${input.status}`,
|
|
11828
|
-
payload: { id: updated.id, task_id: updated.task_id, agent_id: updated.agent_id, status: updated.status, summary: updated.summary, completed_at: timestamp }
|
|
12056
|
+
payload: { id: updated.id, task_id: updated.task_id, agent_id: updated.agent_id, status: updated.status, summary: updated.summary, completed_at: timestamp },
|
|
12057
|
+
databasePath: databasePathFromDatabase(d)
|
|
11829
12058
|
});
|
|
11830
12059
|
return updated;
|
|
11831
12060
|
}
|
|
@@ -13159,7 +13388,7 @@ function importOnboardingFixture(options = {}) {
|
|
|
13159
13388
|
init_database();
|
|
13160
13389
|
import { createHash as createHash3 } from "crypto";
|
|
13161
13390
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
13162
|
-
import { dirname as dirname6, resolve as
|
|
13391
|
+
import { dirname as dirname6, resolve as resolve8 } from "path";
|
|
13163
13392
|
import { mkdirSync as mkdirSync6 } from "fs";
|
|
13164
13393
|
var TODOS_LOCAL_BACKUP_KIND = "hasna.todos.local-backup";
|
|
13165
13394
|
var TODOS_LOCAL_BACKUP_SCHEMA_VERSION = 1;
|
|
@@ -13266,14 +13495,14 @@ function createLocalBackup(options = {}, db) {
|
|
|
13266
13495
|
return backup;
|
|
13267
13496
|
}
|
|
13268
13497
|
function writeLocalBackupFile(backup, outputPath) {
|
|
13269
|
-
const path =
|
|
13498
|
+
const path = resolve8(outputPath);
|
|
13270
13499
|
mkdirSync6(dirname6(path), { recursive: true });
|
|
13271
13500
|
writeFileSync4(path, `${JSON.stringify(backup, null, 2)}
|
|
13272
13501
|
`);
|
|
13273
13502
|
return path;
|
|
13274
13503
|
}
|
|
13275
13504
|
function readLocalBackupFile(path) {
|
|
13276
|
-
return JSON.parse(readFileSync5(
|
|
13505
|
+
return JSON.parse(readFileSync5(resolve8(path), "utf-8"));
|
|
13277
13506
|
}
|
|
13278
13507
|
function verifyLocalBackup(value, options = {}, db) {
|
|
13279
13508
|
const verifiedAt = options.verified_at ?? now();
|
|
@@ -16241,7 +16470,8 @@ function writeQueue(task2, queue, actor, action, db) {
|
|
|
16241
16470
|
logTaskChange(task2.id, `review_queue.${action}`, "review_queue", null, JSON.stringify(queue), actor, d);
|
|
16242
16471
|
emitLocalEventHooksQuiet({
|
|
16243
16472
|
type: `review.${action}`,
|
|
16244
|
-
payload: { task_id: task2.id, queue: queue.queue, state: queue.state, reviewer: queue.reviewer, claimed_by: queue.claimed_by }
|
|
16473
|
+
payload: { task_id: task2.id, queue: queue.queue, state: queue.state, reviewer: queue.reviewer, claimed_by: queue.claimed_by },
|
|
16474
|
+
databasePath: databasePathFromDatabase(d)
|
|
16245
16475
|
});
|
|
16246
16476
|
return itemFromTask(taskOrThrow2(task2.id, d), queue, d);
|
|
16247
16477
|
}
|
|
@@ -16922,7 +17152,7 @@ function canonicalize(value) {
|
|
|
16922
17152
|
function hash(value) {
|
|
16923
17153
|
return createHash5("sha256").update(value).digest("hex");
|
|
16924
17154
|
}
|
|
16925
|
-
function
|
|
17155
|
+
function parsePayload2(value) {
|
|
16926
17156
|
if (!value)
|
|
16927
17157
|
return {};
|
|
16928
17158
|
try {
|
|
@@ -17021,7 +17251,7 @@ function taskScopedRows(db, scope) {
|
|
|
17021
17251
|
FROM handoffs h
|
|
17022
17252
|
WHERE h.project_id IS NULL OR h.project_id = COALESCE(?, h.project_id)
|
|
17023
17253
|
`).all(scope.project_id ?? null).filter((row) => {
|
|
17024
|
-
const payload =
|
|
17254
|
+
const payload = parsePayload2(row.payload_json);
|
|
17025
17255
|
const taskRefs = parseStringArray(payload["task_ids"]);
|
|
17026
17256
|
const runRefs = parseStringArray(payload["run_ids"]);
|
|
17027
17257
|
if (scope.project_id && row.project_id !== scope.project_id)
|
|
@@ -17052,7 +17282,7 @@ function toLedgerEntries(rows) {
|
|
|
17052
17282
|
});
|
|
17053
17283
|
let previous = LOCAL_AUDIT_LEDGER_INITIAL_HASH;
|
|
17054
17284
|
return ordered.map((row, index) => {
|
|
17055
|
-
const payload =
|
|
17285
|
+
const payload = parsePayload2(row.payload_json);
|
|
17056
17286
|
const payloadHash = hash(canonicalize({ source: row.source, source_id: row.source_id, created_at: row.created_at, payload }));
|
|
17057
17287
|
const chainHash = hash(`${previous}
|
|
17058
17288
|
${payloadHash}`);
|
|
@@ -17187,7 +17417,7 @@ function renderLocalAuditLedgerMarkdown(ledger) {
|
|
|
17187
17417
|
init_migrations();
|
|
17188
17418
|
init_schema();
|
|
17189
17419
|
import { readFileSync as readFileSync6 } from "fs";
|
|
17190
|
-
import { join as join9, resolve as
|
|
17420
|
+
import { join as join9, resolve as resolve9 } from "path";
|
|
17191
17421
|
import { Database as Database2 } from "bun:sqlite";
|
|
17192
17422
|
var LOCAL_RELEASE_COMPATIBILITY_SCHEMA_VERSION = 1;
|
|
17193
17423
|
var EXPECTED_PACKAGE_NAME = "@hasna/todos";
|
|
@@ -17329,7 +17559,7 @@ function checkChangelog() {
|
|
|
17329
17559
|
];
|
|
17330
17560
|
}
|
|
17331
17561
|
function createReleaseCompatibilityReport(options = {}) {
|
|
17332
|
-
const root =
|
|
17562
|
+
const root = resolve9(options.root ?? process.cwd());
|
|
17333
17563
|
const packageJson = readPackageJson(root);
|
|
17334
17564
|
const simulatedLevels = options.simulated_levels ?? defaultSimulationLevels();
|
|
17335
17565
|
const checks = [
|