@hasna/todos 0.15.18 → 0.15.20
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 +20 -1
- package/dist/cli/cloud-router.d.ts.map +1 -1
- package/dist/cli/commands/project-commands.d.ts.map +1 -1
- package/dist/cli/commands/query-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/index.js +1306 -122
- package/dist/cli/stage-a.d.ts +21 -10
- package/dist/cli/stage-a.d.ts.map +1 -1
- package/dist/contracts.js +283 -16
- package/dist/db/audit.d.ts +8 -0
- package/dist/db/audit.d.ts.map +1 -1
- package/dist/db/plans.d.ts +4 -0
- package/dist/db/plans.d.ts.map +1 -1
- package/dist/db/task-lifecycle.d.ts +7 -1
- package/dist/db/task-lifecycle.d.ts.map +1 -1
- package/dist/db/tasks.d.ts +1 -1
- package/dist/db/tasks.d.ts.map +1 -1
- package/dist/index.js +599 -26
- package/dist/lib/cli-help.d.ts +3 -2
- package/dist/lib/cli-help.d.ts.map +1 -1
- package/dist/lib/stale-lock-handoff.d.ts +25 -0
- package/dist/lib/stale-lock-handoff.d.ts.map +1 -0
- package/dist/mcp/index.js +954 -46
- package/dist/mcp.js +3 -1
- package/dist/project-registration.js +4250 -3684
- package/dist/registry.js +283 -16
- package/dist/release-provenance.json +5 -5
- package/dist/sdk/index.js +7 -0
- package/dist/sdk/v1.generated.d.ts +40 -1
- package/dist/sdk/v1.generated.d.ts.map +1 -1
- package/dist/server/index.js +1287 -379
- package/dist/server/openapi.d.ts +232 -0
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage/audit-history-import.d.ts +14 -0
- package/dist/storage/audit-history-import.d.ts.map +1 -0
- package/dist/storage/interfaces.d.ts +22 -1
- 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/shadow.d.ts.map +1 -1
- package/dist/storage/sqlite-snapshot.d.ts.map +1 -1
- package/dist/storage.js +596 -25
- package/dist/task-manifest.js +24 -1
- package/dist/types/index.d.ts +50 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -1
package/dist/cli/stage-a.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ type Env = Record<string, string | undefined>;
|
|
|
3
3
|
export type TodosCliAuthorityInitialization = {
|
|
4
4
|
route: "local";
|
|
5
5
|
v1_base_url: null;
|
|
6
|
+
selected_by?: "local-only-command";
|
|
6
7
|
} | {
|
|
7
8
|
route: "remote-diagnostic";
|
|
8
9
|
v1_base_url: string | null;
|
|
@@ -11,6 +12,14 @@ export type TodosCliAuthorityInitialization = {
|
|
|
11
12
|
v1_base_url: string;
|
|
12
13
|
};
|
|
13
14
|
export type TodosCliCommandOwner = "diagnostic" | "remote-http" | "local-only";
|
|
15
|
+
/**
|
|
16
|
+
* Stage A is intentionally pure: it decides the authority route without
|
|
17
|
+
* mutating the caller's environment. The executable applies that decision
|
|
18
|
+
* before importing command modules so any later `getTodosCloudClient()` call
|
|
19
|
+
* observes the same local-only route instead of reconstructing hosted routing
|
|
20
|
+
* from the ambient selector.
|
|
21
|
+
*/
|
|
22
|
+
export declare function applyTodosCliAuthorityEnvironment(authority: TodosCliAuthorityInitialization, env?: Env): void;
|
|
14
23
|
export declare const TODOS_CLI_COMMAND_ALIASES: {
|
|
15
24
|
readonly onboarding: readonly ["demo-fixtures"];
|
|
16
25
|
readonly retrospectives: readonly ["retro"];
|
|
@@ -40,24 +49,26 @@ export declare const TODOS_CLI_COMMAND_ALIASES: {
|
|
|
40
49
|
export declare function getTodosCliCommandCapabilityMatrix(): ReadonlyMap<string, TodosCliCommandOwner>;
|
|
41
50
|
/**
|
|
42
51
|
* Whether a top-level command should be advertised (help/manual/completions) for
|
|
43
|
-
* a resolved authority route.
|
|
44
|
-
* `local-only`
|
|
45
|
-
*
|
|
46
|
-
*
|
|
52
|
+
* a resolved authority route. Remote help describes the authority-served
|
|
53
|
+
* surface, while an explicitly named `local-only` command can still select the
|
|
54
|
+
* local route before command modules load. Diagnostic and remote-http owners
|
|
55
|
+
* stay visible. Commands with no capability owner (e.g. optional
|
|
47
56
|
* dynamically-registered families) self-gate at runtime and remain visible.
|
|
48
57
|
*/
|
|
49
58
|
export declare function isTodosCliCommandVisibleForRoute(command: string, route: TodosCliAuthorityInitialization["route"]): boolean;
|
|
50
59
|
/**
|
|
51
|
-
* Filter
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
60
|
+
* Filter commander help to the authority-served catalog. Explicitly named
|
|
61
|
+
* local-only commands select their own local route, but are omitted from remote
|
|
62
|
+
* metadata so help/manual/completions continue to describe the shared /v1
|
|
63
|
+
* surface rather than mixing two authorities into one catalog.
|
|
55
64
|
*/
|
|
56
65
|
export declare function applyTodosCliHelpVisibility(program: Command, route: TodosCliAuthorityInitialization["route"]): void;
|
|
57
66
|
/**
|
|
58
67
|
* Stage A runs before importing any command module that can reach SQLite or
|
|
59
|
-
* native Postgres adapters. It validates the complete mode state,
|
|
60
|
-
*
|
|
68
|
+
* native Postgres adapters. It validates the complete mode state, routes an
|
|
69
|
+
* explicitly named local-only command back to the local transport, gates the
|
|
70
|
+
* remote command surface, then constructs only the authenticated HTTP client
|
|
71
|
+
* for remote-supported commands.
|
|
61
72
|
*/
|
|
62
73
|
export declare function initializeTodosCliAuthority(args?: string[], env?: Env): TodosCliAuthorityInitialization;
|
|
63
74
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage-a.d.ts","sourceRoot":"","sources":["../../src/cli/stage-a.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAQ,MAAM,WAAW,CAAC;AAO1C,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9C,MAAM,MAAM,+BAA+B,GACvC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,IAAI,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"stage-a.d.ts","sourceRoot":"","sources":["../../src/cli/stage-a.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAQ,MAAM,WAAW,CAAC;AAO1C,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9C,MAAM,MAAM,+BAA+B,GACvC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,oBAAoB,CAAA;CAAE,GACzE;IAAE,KAAK,EAAE,mBAAmB,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC1D;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,aAAa,GAAG,YAAY,CAAC;AAE/E;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAC/C,SAAS,EAAE,+BAA+B,EAC1C,GAAG,GAAE,GAAwB,GAC5B,IAAI,CAIN;AAyBD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;CA8BgB,CAAC;AAyEvD,wBAAgB,kCAAkC,IAAI,WAAW,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAE9F;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,+BAA+B,CAAC,OAAO,CAAC,GAC9C,OAAO,CAKT;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,+BAA+B,CAAC,OAAO,CAAC,GAAG,IAAI,CASnH;AA8WD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,GAAE,MAAM,EAA0B,EACtC,GAAG,GAAE,GAAwB,GAC5B,+BAA+B,CAqBjC"}
|
package/dist/contracts.js
CHANGED
|
@@ -3662,7 +3662,7 @@ function isBlockingDependencyStatus(status) {
|
|
|
3662
3662
|
function isTerminalStatus(status) {
|
|
3663
3663
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
3664
3664
|
}
|
|
3665
|
-
var TASK_STATUSES, TASK_PRIORITIES, PLAN_STATUSES, VersionConflictError, TaskNotFoundError, TaskNotStartableError, TaskReferenceAmbiguousError, ProjectNotFoundError, ResourceConflictError, PlanNotFoundError, LockError, AgentNotFoundError, IdentityAliasAmbiguousError, IdentityIdImmutableError, TaskListNotFoundError, DependencyCycleError, CompletionGuardError, DISPATCH_STATUSES, DispatchNotFoundError;
|
|
3665
|
+
var TASK_STATUSES, TASK_PRIORITIES, PLAN_STATUSES, VersionConflictError, TaskNotFoundError, TaskNotStartableError, TaskReferenceAmbiguousError, ProjectNotFoundError, ResourceConflictError, PlanRevisionConflictError, PlanNotFoundError, LockError, StaleLockHandoffError, AgentNotFoundError, IdentityAliasAmbiguousError, IdentityIdImmutableError, TaskListNotFoundError, DependencyCycleError, CompletionGuardError, DISPATCH_STATUSES, DispatchNotFoundError;
|
|
3666
3666
|
var init_types = __esm(() => {
|
|
3667
3667
|
TASK_STATUSES = [
|
|
3668
3668
|
"pending",
|
|
@@ -3750,6 +3750,19 @@ var init_types = __esm(() => {
|
|
|
3750
3750
|
this.name = "ResourceConflictError";
|
|
3751
3751
|
}
|
|
3752
3752
|
};
|
|
3753
|
+
PlanRevisionConflictError = class PlanRevisionConflictError extends Error {
|
|
3754
|
+
planId;
|
|
3755
|
+
expectedUpdatedAt;
|
|
3756
|
+
currentUpdatedAt;
|
|
3757
|
+
static code = "PLAN_REVISION_CONFLICT";
|
|
3758
|
+
constructor(planId, expectedUpdatedAt, currentUpdatedAt) {
|
|
3759
|
+
super(`Plan revision conflict for ${planId}: expected ${expectedUpdatedAt}, current ${currentUpdatedAt}`);
|
|
3760
|
+
this.planId = planId;
|
|
3761
|
+
this.expectedUpdatedAt = expectedUpdatedAt;
|
|
3762
|
+
this.currentUpdatedAt = currentUpdatedAt;
|
|
3763
|
+
this.name = "PlanRevisionConflictError";
|
|
3764
|
+
}
|
|
3765
|
+
};
|
|
3753
3766
|
PlanNotFoundError = class PlanNotFoundError extends Error {
|
|
3754
3767
|
planId;
|
|
3755
3768
|
static code = "PLAN_NOT_FOUND";
|
|
@@ -3772,6 +3785,16 @@ var init_types = __esm(() => {
|
|
|
3772
3785
|
this.name = "LockError";
|
|
3773
3786
|
}
|
|
3774
3787
|
};
|
|
3788
|
+
StaleLockHandoffError = class StaleLockHandoffError extends Error {
|
|
3789
|
+
code;
|
|
3790
|
+
details;
|
|
3791
|
+
constructor(code, message, details = {}) {
|
|
3792
|
+
super(message);
|
|
3793
|
+
this.code = code;
|
|
3794
|
+
this.details = details;
|
|
3795
|
+
this.name = "StaleLockHandoffError";
|
|
3796
|
+
}
|
|
3797
|
+
};
|
|
3775
3798
|
AgentNotFoundError = class AgentNotFoundError extends Error {
|
|
3776
3799
|
agentId;
|
|
3777
3800
|
static code = "AGENT_NOT_FOUND";
|
|
@@ -7490,28 +7513,55 @@ var init_activity_audit = __esm(() => {
|
|
|
7490
7513
|
function sanitizeHistoryValue(value, context) {
|
|
7491
7514
|
return value === undefined || value === null ? null : sanitizePreWriteText(String(value), context);
|
|
7492
7515
|
}
|
|
7493
|
-
function
|
|
7516
|
+
function insertTaskHistory(entry, db) {
|
|
7494
7517
|
const d = db || getDatabase();
|
|
7495
|
-
const
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7518
|
+
const safeEntry = {
|
|
7519
|
+
...entry,
|
|
7520
|
+
field: entry.field || null,
|
|
7521
|
+
old_value: sanitizeHistoryValue(entry.old_value, "task_history.old_value"),
|
|
7522
|
+
new_value: sanitizeHistoryValue(entry.new_value, "task_history.new_value"),
|
|
7523
|
+
agent_id: entry.agent_id || null,
|
|
7524
|
+
machine_id: entry.machine_id ?? currentStorageMachineId(d)
|
|
7525
|
+
};
|
|
7500
7526
|
d.run(`INSERT INTO task_history (id, task_id, action, field, old_value, new_value, agent_id, created_at, machine_id)
|
|
7501
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
7527
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
7528
|
+
safeEntry.id,
|
|
7529
|
+
safeEntry.task_id,
|
|
7530
|
+
safeEntry.action,
|
|
7531
|
+
safeEntry.field,
|
|
7532
|
+
safeEntry.old_value,
|
|
7533
|
+
safeEntry.new_value,
|
|
7534
|
+
safeEntry.agent_id,
|
|
7535
|
+
safeEntry.created_at,
|
|
7536
|
+
safeEntry.machine_id ?? null
|
|
7537
|
+
]);
|
|
7502
7538
|
try {
|
|
7503
7539
|
const { logActivity: logActivity2 } = (init_activity_audit(), __toCommonJS(exports_activity_audit));
|
|
7504
7540
|
logActivity2({
|
|
7505
7541
|
entity_type: "task",
|
|
7506
|
-
entity_id:
|
|
7507
|
-
action,
|
|
7508
|
-
field:
|
|
7509
|
-
old_value:
|
|
7510
|
-
new_value:
|
|
7511
|
-
actor_id:
|
|
7542
|
+
entity_id: safeEntry.task_id,
|
|
7543
|
+
action: safeEntry.action,
|
|
7544
|
+
field: safeEntry.field ?? undefined,
|
|
7545
|
+
old_value: safeEntry.old_value,
|
|
7546
|
+
new_value: safeEntry.new_value,
|
|
7547
|
+
actor_id: safeEntry.agent_id ?? undefined
|
|
7512
7548
|
}, d);
|
|
7513
7549
|
} catch {}
|
|
7514
|
-
return
|
|
7550
|
+
return safeEntry;
|
|
7551
|
+
}
|
|
7552
|
+
function logTaskChange(taskId, action, field2, oldValue, newValue, agentId, db) {
|
|
7553
|
+
const d = db || getDatabase();
|
|
7554
|
+
return insertTaskHistory({
|
|
7555
|
+
id: uuid(),
|
|
7556
|
+
task_id: taskId,
|
|
7557
|
+
action,
|
|
7558
|
+
field: field2 || null,
|
|
7559
|
+
old_value: oldValue ?? null,
|
|
7560
|
+
new_value: newValue ?? null,
|
|
7561
|
+
agent_id: agentId || null,
|
|
7562
|
+
created_at: now(),
|
|
7563
|
+
machine_id: currentStorageMachineId(d)
|
|
7564
|
+
}, d);
|
|
7515
7565
|
}
|
|
7516
7566
|
function getTaskHistory(taskId, db) {
|
|
7517
7567
|
const d = db || getDatabase();
|
|
@@ -8534,6 +8584,145 @@ var init_task_graph = __esm(() => {
|
|
|
8534
8584
|
init_task_crud();
|
|
8535
8585
|
});
|
|
8536
8586
|
|
|
8587
|
+
// src/lib/stale-lock-handoff.ts
|
|
8588
|
+
function normalizeExactTaskId(value) {
|
|
8589
|
+
if (typeof value !== "string" || !EXACT_TASK_UUID_RE.test(value.trim())) {
|
|
8590
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_INVALID_TASK_ID", "stale-lock handoff requires one exact full task UUID", { task_id: typeof value === "string" ? value : null });
|
|
8591
|
+
}
|
|
8592
|
+
return value.trim().toLowerCase();
|
|
8593
|
+
}
|
|
8594
|
+
function requireNonEmptyString(value, field2) {
|
|
8595
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
8596
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_INVALID_INPUT", `${field2} must be a non-empty string`, { field: field2 });
|
|
8597
|
+
}
|
|
8598
|
+
const trimmed = value.trim();
|
|
8599
|
+
if (field2 === "reason" && trimmed.length > MAX_REASON_LENGTH) {
|
|
8600
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_INVALID_INPUT", `reason must be at most ${MAX_REASON_LENGTH} characters`, { field: field2, max_length: MAX_REASON_LENGTH });
|
|
8601
|
+
}
|
|
8602
|
+
return trimmed;
|
|
8603
|
+
}
|
|
8604
|
+
function requireCanonicalLockVersion(value) {
|
|
8605
|
+
if (typeof value !== "string" || !CANONICAL_LOCK_VERSION_RE.test(value)) {
|
|
8606
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_INVALID_INPUT", "expected_lock_version must be the exact canonical locked_at timestamp (YYYY-MM-DDTHH:mm:ss.sssZ)", { field: "expected_lock_version" });
|
|
8607
|
+
}
|
|
8608
|
+
const parsed = Date.parse(value);
|
|
8609
|
+
if (Number.isNaN(parsed) || new Date(parsed).toISOString() !== value) {
|
|
8610
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_INVALID_INPUT", "expected_lock_version must name a real canonical UTC instant", { field: "expected_lock_version" });
|
|
8611
|
+
}
|
|
8612
|
+
return value;
|
|
8613
|
+
}
|
|
8614
|
+
function requireStaleThreshold(value) {
|
|
8615
|
+
if (!Number.isSafeInteger(value) || Number(value) <= 0) {
|
|
8616
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_INVALID_INPUT", "stale_after_seconds must be a positive safe integer", { field: "stale_after_seconds" });
|
|
8617
|
+
}
|
|
8618
|
+
return Number(value);
|
|
8619
|
+
}
|
|
8620
|
+
function prepareStaleLockHandoff(input, options = {}) {
|
|
8621
|
+
const taskId = normalizeExactTaskId(input.task_id);
|
|
8622
|
+
const actor = requireNonEmptyString(input.actor, "actor");
|
|
8623
|
+
const expectedHolder = requireNonEmptyString(input.expected_holder, "expected_holder");
|
|
8624
|
+
const newHolder = requireNonEmptyString(input.new_holder, "new_holder");
|
|
8625
|
+
const expectedLockVersion = requireCanonicalLockVersion(input.expected_lock_version);
|
|
8626
|
+
const staleAfterSeconds = requireStaleThreshold(input.stale_after_seconds);
|
|
8627
|
+
const reason = sanitizePreWriteText(requireNonEmptyString(input.reason, "reason"), "stale_lock_handoff.reason").trim();
|
|
8628
|
+
if (!reason) {
|
|
8629
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_INVALID_INPUT", "reason must remain non-empty after safety filtering", { field: "reason" });
|
|
8630
|
+
}
|
|
8631
|
+
if (canonicalAgentRef(actor) !== canonicalAgentRef(newHolder)) {
|
|
8632
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_ACTOR_MISMATCH", "new_holder must match the authenticated actor", { actor, new_holder: newHolder });
|
|
8633
|
+
}
|
|
8634
|
+
if (canonicalAgentRef(expectedHolder) === canonicalAgentRef(newHolder)) {
|
|
8635
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_INVALID_INPUT", "new_holder must differ from expected_holder", { field: "new_holder" });
|
|
8636
|
+
}
|
|
8637
|
+
const operationTimestamp = options.now ?? new Date().toISOString();
|
|
8638
|
+
if (!CANONICAL_LOCK_VERSION_RE.test(operationTimestamp) || new Date(Date.parse(operationTimestamp)).toISOString() !== operationTimestamp) {
|
|
8639
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_INVALID_INPUT", "operation timestamp must be a canonical UTC instant");
|
|
8640
|
+
}
|
|
8641
|
+
const staleCutoff = new Date(Date.parse(operationTimestamp) - staleAfterSeconds * 1000).toISOString();
|
|
8642
|
+
return {
|
|
8643
|
+
task_id: taskId,
|
|
8644
|
+
actor,
|
|
8645
|
+
expected_holder: expectedHolder,
|
|
8646
|
+
expected_lock_version: expectedLockVersion,
|
|
8647
|
+
stale_after_seconds: staleAfterSeconds,
|
|
8648
|
+
new_holder: newHolder,
|
|
8649
|
+
reason,
|
|
8650
|
+
operation_timestamp: operationTimestamp,
|
|
8651
|
+
stale_cutoff: staleCutoff,
|
|
8652
|
+
receipt_id: options.receiptId ?? crypto.randomUUID()
|
|
8653
|
+
};
|
|
8654
|
+
}
|
|
8655
|
+
function buildStaleLockHandoffReceipt(input) {
|
|
8656
|
+
return {
|
|
8657
|
+
schema_version: STALE_LOCK_HANDOFF_SCHEMA_VERSION,
|
|
8658
|
+
receipt_id: input.receipt_id,
|
|
8659
|
+
task_id: input.task_id,
|
|
8660
|
+
actor: input.actor,
|
|
8661
|
+
previous_holder: input.expected_holder,
|
|
8662
|
+
previous_lock_version: input.expected_lock_version,
|
|
8663
|
+
new_holder: input.new_holder,
|
|
8664
|
+
new_lock_version: input.operation_timestamp,
|
|
8665
|
+
stale_after_seconds: input.stale_after_seconds,
|
|
8666
|
+
stale_cutoff: input.stale_cutoff,
|
|
8667
|
+
reason: input.reason,
|
|
8668
|
+
created_at: input.operation_timestamp
|
|
8669
|
+
};
|
|
8670
|
+
}
|
|
8671
|
+
function staleLockHandoffHistory(receipt, machineId) {
|
|
8672
|
+
return {
|
|
8673
|
+
id: receipt.receipt_id,
|
|
8674
|
+
task_id: receipt.task_id,
|
|
8675
|
+
action: STALE_LOCK_HANDOFF_ACTION,
|
|
8676
|
+
field: STALE_LOCK_HANDOFF_FIELD,
|
|
8677
|
+
old_value: JSON.stringify({
|
|
8678
|
+
holder: receipt.previous_holder,
|
|
8679
|
+
lock_version: receipt.previous_lock_version
|
|
8680
|
+
}),
|
|
8681
|
+
new_value: JSON.stringify(receipt),
|
|
8682
|
+
agent_id: receipt.actor,
|
|
8683
|
+
created_at: receipt.created_at,
|
|
8684
|
+
machine_id: machineId
|
|
8685
|
+
};
|
|
8686
|
+
}
|
|
8687
|
+
function throwStaleLockHandoffConflict(task, input) {
|
|
8688
|
+
if (!task.locked_by || !task.locked_at) {
|
|
8689
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_NOT_LOCKED", `Task ${input.task_id} does not have a complete lock to hand off`, { task_id: input.task_id });
|
|
8690
|
+
}
|
|
8691
|
+
if (task.locked_at !== input.expected_lock_version) {
|
|
8692
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_VERSION_MISMATCH", `Task ${input.task_id} lock version changed`, {
|
|
8693
|
+
task_id: input.task_id,
|
|
8694
|
+
expected_lock_version: input.expected_lock_version,
|
|
8695
|
+
current_lock_version: task.locked_at
|
|
8696
|
+
});
|
|
8697
|
+
}
|
|
8698
|
+
if (task.locked_by !== input.expected_holder) {
|
|
8699
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_HOLDER_MISMATCH", `Task ${input.task_id} lock holder changed`, {
|
|
8700
|
+
task_id: input.task_id,
|
|
8701
|
+
expected_holder: input.expected_holder,
|
|
8702
|
+
current_holder: task.locked_by
|
|
8703
|
+
});
|
|
8704
|
+
}
|
|
8705
|
+
if (isTerminalStatus(task.status)) {
|
|
8706
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_TERMINAL", `Task ${input.task_id} is ${task.status} and cannot transfer a lock`, { task_id: input.task_id, status: task.status });
|
|
8707
|
+
}
|
|
8708
|
+
if (task.locked_at >= input.stale_cutoff) {
|
|
8709
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_NOT_STALE", `Task ${input.task_id} lock is not older than the supplied stale threshold`, {
|
|
8710
|
+
task_id: input.task_id,
|
|
8711
|
+
current_lock_version: task.locked_at,
|
|
8712
|
+
stale_cutoff: input.stale_cutoff
|
|
8713
|
+
});
|
|
8714
|
+
}
|
|
8715
|
+
throw new StaleLockHandoffError("STALE_LOCK_HANDOFF_CONFLICT", `Task ${input.task_id} changed during stale-lock handoff`, { task_id: input.task_id });
|
|
8716
|
+
}
|
|
8717
|
+
var STALE_LOCK_HANDOFF_SCHEMA_VERSION = "todos.stale-lock-handoff.v1", STALE_LOCK_HANDOFF_ACTION = "stale_lock_handoff", STALE_LOCK_HANDOFF_FIELD = "lock", EXACT_TASK_UUID_RE, CANONICAL_LOCK_VERSION_RE, MAX_REASON_LENGTH = 4096;
|
|
8718
|
+
var init_stale_lock_handoff = __esm(() => {
|
|
8719
|
+
init_types();
|
|
8720
|
+
init_creator_identity();
|
|
8721
|
+
init_prewrite_secrets();
|
|
8722
|
+
EXACT_TASK_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8723
|
+
CANONICAL_LOCK_VERSION_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
|
8724
|
+
});
|
|
8725
|
+
|
|
8537
8726
|
// src/db/task-lifecycle.ts
|
|
8538
8727
|
var exports_task_lifecycle = {};
|
|
8539
8728
|
__export(exports_task_lifecycle, {
|
|
@@ -8542,6 +8731,7 @@ __export(exports_task_lifecycle, {
|
|
|
8542
8731
|
startTask: () => startTask,
|
|
8543
8732
|
spawnNextRecurrence: () => spawnNextRecurrence,
|
|
8544
8733
|
lockTask: () => lockTask,
|
|
8734
|
+
handoffStaleTaskLock: () => handoffStaleTaskLock,
|
|
8545
8735
|
getTasksChangedSince: () => getTasksChangedSince,
|
|
8546
8736
|
getTaskLockStatus: () => getTaskLockStatus,
|
|
8547
8737
|
getStaleTasks: () => getStaleTasks,
|
|
@@ -8810,6 +9000,38 @@ function unlockTask(id, agentId, db) {
|
|
|
8810
9000
|
WHERE id = ?`, [timestamp2, id]);
|
|
8811
9001
|
return true;
|
|
8812
9002
|
}
|
|
9003
|
+
function handoffStaleTaskLock(input, db) {
|
|
9004
|
+
const d = db || getDatabase();
|
|
9005
|
+
const prepared = prepareStaleLockHandoff(input);
|
|
9006
|
+
const receipt = buildStaleLockHandoffReceipt(prepared);
|
|
9007
|
+
const history = staleLockHandoffHistory(receipt, null);
|
|
9008
|
+
const transfer = d.transaction(() => {
|
|
9009
|
+
const result = d.run(`UPDATE tasks
|
|
9010
|
+
SET locked_by = ?, locked_at = ?, updated_at = ?, version = version + 1
|
|
9011
|
+
WHERE id = ?
|
|
9012
|
+
AND locked_by = ?
|
|
9013
|
+
AND locked_at = ?
|
|
9014
|
+
AND julianday(locked_at) < julianday(?)
|
|
9015
|
+
AND status NOT IN ('completed', 'failed', 'cancelled')`, [
|
|
9016
|
+
prepared.new_holder,
|
|
9017
|
+
prepared.operation_timestamp,
|
|
9018
|
+
prepared.operation_timestamp,
|
|
9019
|
+
prepared.task_id,
|
|
9020
|
+
prepared.expected_holder,
|
|
9021
|
+
prepared.expected_lock_version,
|
|
9022
|
+
prepared.stale_cutoff
|
|
9023
|
+
]);
|
|
9024
|
+
if (result.changes === 0) {
|
|
9025
|
+
const current = getTask(prepared.task_id, d);
|
|
9026
|
+
if (!current)
|
|
9027
|
+
throw new TaskNotFoundError(prepared.task_id);
|
|
9028
|
+
throwStaleLockHandoffConflict(current, prepared);
|
|
9029
|
+
}
|
|
9030
|
+
insertTaskHistory(history, d);
|
|
9031
|
+
});
|
|
9032
|
+
transfer();
|
|
9033
|
+
return receipt;
|
|
9034
|
+
}
|
|
8813
9035
|
function getTaskLockStatus(id, db) {
|
|
8814
9036
|
const d = db || getDatabase();
|
|
8815
9037
|
const task = getTask(id, d);
|
|
@@ -9110,6 +9332,7 @@ var init_task_lifecycle = __esm(() => {
|
|
|
9110
9332
|
init_task_crud();
|
|
9111
9333
|
init_task_graph();
|
|
9112
9334
|
init_prewrite_secrets();
|
|
9335
|
+
init_stale_lock_handoff();
|
|
9113
9336
|
});
|
|
9114
9337
|
|
|
9115
9338
|
// src/db/task-crud.ts
|
|
@@ -10521,6 +10744,48 @@ function updatePlan(id, input, db) {
|
|
|
10521
10744
|
return updatePlanStored(id, input, d);
|
|
10522
10745
|
})();
|
|
10523
10746
|
}
|
|
10747
|
+
function nextPlanCompletionTimestamp(expectedUpdatedAt) {
|
|
10748
|
+
const expected = Date.parse(expectedUpdatedAt);
|
|
10749
|
+
const minimum = Number.isNaN(expected) ? Date.now() : expected + 2;
|
|
10750
|
+
return new Date(Math.max(Date.now(), minimum)).toISOString();
|
|
10751
|
+
}
|
|
10752
|
+
function completePlanAtRevision(id, expectedUpdatedAt, db) {
|
|
10753
|
+
const d = db || getDatabase();
|
|
10754
|
+
return d.transaction(() => {
|
|
10755
|
+
guardPlanRowsSqlite([id], d);
|
|
10756
|
+
const plan = getPlan(id, d);
|
|
10757
|
+
if (!plan)
|
|
10758
|
+
throw new PlanNotFoundError(id);
|
|
10759
|
+
if (plan.updated_at !== expectedUpdatedAt) {
|
|
10760
|
+
throw new PlanRevisionConflictError(id, expectedUpdatedAt, plan.updated_at);
|
|
10761
|
+
}
|
|
10762
|
+
if (plan.status === "completed")
|
|
10763
|
+
return { plan, applied: false };
|
|
10764
|
+
const updatedAt = nextPlanCompletionTimestamp(expectedUpdatedAt);
|
|
10765
|
+
const result = d.run(`UPDATE plans
|
|
10766
|
+
SET status = 'completed', updated_at = ?
|
|
10767
|
+
WHERE id = ? AND updated_at = ? AND status <> 'completed'`, [updatedAt, id, expectedUpdatedAt]);
|
|
10768
|
+
if (result.changes !== 1) {
|
|
10769
|
+
const current = getPlan(id, d);
|
|
10770
|
+
if (!current)
|
|
10771
|
+
throw new PlanNotFoundError(id);
|
|
10772
|
+
throw new PlanRevisionConflictError(id, expectedUpdatedAt, current.updated_at);
|
|
10773
|
+
}
|
|
10774
|
+
const completed = getPlan(id, d);
|
|
10775
|
+
emitLocalEventHooksQuiet({
|
|
10776
|
+
type: "plan.updated",
|
|
10777
|
+
payload: {
|
|
10778
|
+
id,
|
|
10779
|
+
old_status: plan.status,
|
|
10780
|
+
new_status: completed.status,
|
|
10781
|
+
name: completed.name,
|
|
10782
|
+
project_id: completed.project_id
|
|
10783
|
+
},
|
|
10784
|
+
databasePath: databasePathFromDatabase(d)
|
|
10785
|
+
});
|
|
10786
|
+
return { plan: completed, applied: true };
|
|
10787
|
+
})();
|
|
10788
|
+
}
|
|
10524
10789
|
function deletePlan(id, db) {
|
|
10525
10790
|
const d = db || getDatabase();
|
|
10526
10791
|
const plan = getPlan(id, d);
|
|
@@ -12380,7 +12645,7 @@ var init_tasks = __esm(() => {
|
|
|
12380
12645
|
// package.json
|
|
12381
12646
|
var package_default = {
|
|
12382
12647
|
name: "@hasna/todos",
|
|
12383
|
-
version: "0.15.
|
|
12648
|
+
version: "0.15.20",
|
|
12384
12649
|
description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
12385
12650
|
type: "module",
|
|
12386
12651
|
main: "dist/index.js",
|
|
@@ -12451,6 +12716,8 @@ var package_default = {
|
|
|
12451
12716
|
"dev:mcp": "bun run src/mcp/index.ts",
|
|
12452
12717
|
"dev:serve": "bun run src/server/index.ts",
|
|
12453
12718
|
"verify:release": "bun run scripts/verify-public-release.ts --mode=review",
|
|
12719
|
+
"verify:release-review": "bun run scripts/verify-npm-release-agent-review.ts",
|
|
12720
|
+
"issue:release-review": "bun run scripts/issue-npm-release-agent-review.ts",
|
|
12454
12721
|
prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
|
|
12455
12722
|
postinstall: "mkdir -p $HOME/.hasna/todos $HOME/.hasna/todos/training 2>/dev/null || true"
|
|
12456
12723
|
},
|
package/dist/db/audit.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { Database } from "bun:sqlite";
|
|
2
2
|
import type { TaskHistory } from "../types/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Insert one caller-constructed history entry through the owning audit path.
|
|
5
|
+
*
|
|
6
|
+
* Operations that must mutate a task and persist their immutable receipt in the
|
|
7
|
+
* SAME transaction use this form. Ordinary callers should keep using
|
|
8
|
+
* logTaskChange(), which creates the id and timestamp for them.
|
|
9
|
+
*/
|
|
10
|
+
export declare function insertTaskHistory(entry: TaskHistory, db?: Database): TaskHistory;
|
|
3
11
|
export declare function logTaskChange(taskId: string, action: string, field?: string, oldValue?: string | null, newValue?: string | null, agentId?: string | null, db?: Database): TaskHistory;
|
|
4
12
|
export declare function getTaskHistory(taskId: string, db?: Database): TaskHistory[];
|
|
5
13
|
export declare function getRecentActivity(limit?: number, db?: Database): TaskHistory[];
|
package/dist/db/audit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/db/audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AASrD,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,EACvB,EAAE,CAAC,EAAE,QAAQ,GACZ,WAAW,
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/db/audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AASrD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,WAAW,CA0ChF;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,EACvB,EAAE,CAAC,EAAE,QAAQ,GACZ,WAAW,CAab;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,WAAW,EAAE,CAG3E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,SAAK,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,WAAW,EAAE,CAG1E;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;IAC9J,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/G,WAAW,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;IAC7H,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;IAC9F,KAAK,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAChH,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtG;AAED,wBAAgB,QAAQ,CAAC,KAAK,SAAI,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,YAAY,CAyDnF"}
|
package/dist/db/plans.d.ts
CHANGED
|
@@ -12,5 +12,9 @@ export declare function createPlan(input: CreatePlanInput, db?: Database): Plan;
|
|
|
12
12
|
export declare function getPlan(id: string, db?: Database): Plan | null;
|
|
13
13
|
export declare function listPlans(projectId?: string, db?: Database): Plan[];
|
|
14
14
|
export declare function updatePlan(id: string, input: UpdatePlanInput, db?: Database): Plan;
|
|
15
|
+
export declare function completePlanAtRevision(id: string, expectedUpdatedAt: string, db?: Database): {
|
|
16
|
+
plan: Plan;
|
|
17
|
+
applied: boolean;
|
|
18
|
+
};
|
|
15
19
|
export declare function deletePlan(id: string, db?: Database): boolean;
|
|
16
20
|
//# sourceMappingURL=plans.d.ts.map
|
package/dist/db/plans.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plans.d.ts","sourceRoot":"","sources":["../../src/db/plans.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAShF,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC;IAClD,OAAO,EAAE,IAAI,EAAE,CAAC;CACjB;AAMD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD;AAsCD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,oBAAoB,CAalH;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAEnG;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CA2BtE;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAI9D;AAED,wBAAgB,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,EAAE,CAUnE;AAuDD,wBAAgB,UAAU,CACxB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,eAAe,EACtB,EAAE,CAAC,EAAE,QAAQ,GACZ,IAAI,CAMN;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAW7D"}
|
|
1
|
+
{"version":3,"file":"plans.d.ts","sourceRoot":"","sources":["../../src/db/plans.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAShF,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC;IAClD,OAAO,EAAE,IAAI,EAAE,CAAC;CACjB;AAMD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD;AAsCD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,oBAAoB,CAalH;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAEnG;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CA2BtE;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAI9D;AAED,wBAAgB,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,EAAE,CAUnE;AAuDD,wBAAgB,UAAU,CACxB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,eAAe,EACtB,EAAE,CAAC,EAAE,QAAQ,GACZ,IAAI,CAMN;AAQD,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,MAAM,EACV,iBAAiB,EAAE,MAAM,EACzB,EAAE,CAAC,EAAE,QAAQ,GACZ;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAqClC;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAW7D"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Database } from "bun:sqlite";
|
|
2
|
-
import type { LockResult, Task } from "../types/index.js";
|
|
2
|
+
import type { LockResult, StaleLockHandoffInput, StaleLockHandoffReceipt, Task } from "../types/index.js";
|
|
3
3
|
export declare function getBlockingDeps(id: string, db?: Database): Task[];
|
|
4
4
|
export declare function startTask(id: string, agentId: string, db?: Database): Task;
|
|
5
5
|
export declare function completeTask(id: string, agentId?: string, db?: Database, options?: {
|
|
@@ -14,6 +14,12 @@ export declare function completeTask(id: string, agentId?: string, db?: Database
|
|
|
14
14
|
}): Task;
|
|
15
15
|
export declare function lockTask(id: string, agentId: string, db?: Database): LockResult;
|
|
16
16
|
export declare function unlockTask(id: string, agentId?: string, db?: Database): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Atomically transfer exactly one stale lock from its observed holder/version
|
|
19
|
+
* to the authenticated new holder, and persist the immutable receipt in the
|
|
20
|
+
* owning task_history table inside the same SQLite transaction.
|
|
21
|
+
*/
|
|
22
|
+
export declare function handoffStaleTaskLock(input: StaleLockHandoffInput, db?: Database): StaleLockHandoffReceipt;
|
|
17
23
|
export interface TaskLockStatus {
|
|
18
24
|
task_id: string;
|
|
19
25
|
locked: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-lifecycle.d.ts","sourceRoot":"","sources":["../../src/db/task-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EACV,UAAU,EACV,IAAI,EAEL,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"task-lifecycle.d.ts","sourceRoot":"","sources":["../../src/db/task-lifecycle.ts"],"names":[],"mappings":"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"}
|
package/dist/db/tasks.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createTask, getTask, getTaskWithRelations, listTasks, countTasks, updateTask, upsertTaskByFingerprint, getTaskByFingerprint, deleteTask, rowToTask, insertTaskTags, replaceTaskTags, } from "./task-crud.js";
|
|
2
|
-
export { startTask, completeTask, failTask, lockTask, unlockTask, getTaskLockStatus, claimNextTask, getNextTask, getActiveWork, getTasksChangedSince, stealTask, claimOrSteal, getBlockingDeps, getStaleTasks, } from "./task-lifecycle.js";
|
|
2
|
+
export { startTask, completeTask, failTask, lockTask, unlockTask, handoffStaleTaskLock, getTaskLockStatus, claimNextTask, getNextTask, getActiveWork, getTasksChangedSince, stealTask, claimOrSteal, getBlockingDeps, getStaleTasks, } from "./task-lifecycle.js";
|
|
3
3
|
export type { ActiveWorkItem, StaleTaskQuery, TaskLockStatus } from "./task-lifecycle.js";
|
|
4
4
|
export { addDependency, removeDependency, getTaskDependencies, getTaskDependents, cloneTask, getTaskGraph, moveTask, } from "./task-graph.js";
|
|
5
5
|
export type { TaskGraphNode, TaskGraph } from "./task-graph.js";
|
package/dist/db/tasks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/db/tasks.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,UAAU,EACV,OAAO,EACP,oBAAoB,EACpB,SAAS,EACT,UAAU,EACV,UAAU,EACV,uBAAuB,EACvB,oBAAoB,EACpB,UAAU,EACV,SAAS,EACT,cAAc,EACd,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,YAAY,EACZ,eAAe,EACf,aAAa,GACd,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1F,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,QAAQ,GACT,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEhE,OAAO,EACL,SAAS,EACT,cAAc,EACd,aAAa,EACb,eAAe,EACf,sBAAsB,EACtB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EACL,eAAe,EACf,eAAe,EACf,eAAe,EACf,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EACf,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,0BAA0B,EAC1B,SAAS,EACT,WAAW,EACX,eAAe,EACf,cAAc,EACd,OAAO,GACR,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EACV,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,YAAY,EACZ,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,aAAa,EACb,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,aAAa,EACb,uBAAuB,EACvB,gBAAgB,EAChB,eAAe,GAChB,MAAM,eAAe,CAAC;AAGvB,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,4BAA4B,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/db/tasks.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,UAAU,EACV,OAAO,EACP,oBAAoB,EACpB,SAAS,EACT,UAAU,EACV,UAAU,EACV,uBAAuB,EACvB,oBAAoB,EACpB,UAAU,EACV,SAAS,EACT,cAAc,EACd,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,YAAY,EACZ,eAAe,EACf,aAAa,GACd,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1F,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,QAAQ,GACT,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEhE,OAAO,EACL,SAAS,EACT,cAAc,EACd,aAAa,EACb,eAAe,EACf,sBAAsB,EACtB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EACL,eAAe,EACf,eAAe,EACf,eAAe,EACf,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,eAAe,EACf,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,0BAA0B,EAC1B,SAAS,EACT,WAAW,EACX,eAAe,EACf,cAAc,EACd,OAAO,GACR,MAAM,qBAAqB,CAAC;AAE7B,YAAY,EACV,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,YAAY,EACZ,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,aAAa,EACb,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,aAAa,EACb,uBAAuB,EACvB,gBAAgB,EAChB,eAAe,GAChB,MAAM,eAAe,CAAC;AAGvB,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,4BAA4B,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC"}
|