@astrosheep/keiyaku 4.5.11 → 4.5.13
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/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +135 -377
- package/build/integrations/pi/keiyaku.ts +11 -116
- package/build/src/akuma/akuma-errors.d.ts +1 -0
- package/build/src/akuma/akuma-errors.js +1 -0
- package/build/src/akuma/akuma-handle.d.ts +1 -0
- package/build/src/akuma/akuma-handle.js +6 -7
- package/build/src/akuma/akuma-product.d.ts +6 -8
- package/build/src/akuma/akuma-product.js +141 -57
- package/build/src/akuma/akuma.d.ts +5 -7
- package/build/src/akuma/akuma.js +1 -22
- package/build/src/akuma/archetype.d.ts +6 -5
- package/build/src/akuma/archetype.js +137 -38
- package/build/src/akuma/body.d.ts +5 -5
- package/build/src/akuma/body.js +223 -63
- package/build/src/akuma/call-input.d.ts +4 -0
- package/build/src/akuma/call-input.js +21 -0
- package/build/src/akuma/call-request.d.ts +5 -8
- package/build/src/akuma/call-request.js +28 -73
- package/build/src/akuma/fleet-execution.d.ts +26 -0
- package/build/src/akuma/fleet-execution.js +109 -0
- package/build/src/{library/fleet-result.d.ts → akuma/fleet-observation.d.ts} +13 -57
- package/build/src/{library/fleet-result.js → akuma/fleet-observation.js} +4 -25
- package/build/src/akuma/fleet-request.d.ts +122 -0
- package/build/src/akuma/fleet-request.js +168 -0
- package/build/src/akuma/heart/facts.d.ts +23 -0
- package/build/src/akuma/heart/facts.js +7 -0
- package/build/src/akuma/heart/index.d.ts +43 -4
- package/build/src/akuma/heart/index.js +107 -7
- package/build/src/akuma/heart/request-authority.d.ts +2 -0
- package/build/src/akuma/heart/request-authority.js +28 -3
- package/build/src/akuma/heart/request-rows.d.ts +2 -0
- package/build/src/akuma/heart/request-rows.js +24 -5
- package/build/src/akuma/heart/rows.d.ts +4 -1
- package/build/src/akuma/heart/rows.js +46 -19
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +28 -6
- package/build/src/akuma/heart/storage.d.ts +1 -1
- package/build/src/akuma/heart/tells.d.ts +29 -0
- package/build/src/akuma/heart/tells.js +136 -5
- package/build/src/akuma/heart/timeline.js +9 -3
- package/build/src/akuma/projection-read.js +36 -3
- package/build/src/akuma/projection.js +3 -4
- package/build/src/akuma/provider.d.ts +4 -0
- package/build/src/akuma/provider.js +56 -3
- package/build/src/akuma/providers/claude/index.js +3 -0
- package/build/src/akuma/providers/codex-app-server/index.js +14 -12
- package/build/src/akuma/providers/pi/index.js +7 -1
- package/build/src/akuma/publication.js +65 -49
- package/build/src/akuma/request-lifecycle.d.ts +5 -4
- package/build/src/akuma/request-lifecycle.js +29 -16
- package/build/src/akuma/request-rendezvous.d.ts +15 -12
- package/build/src/akuma/request-rendezvous.js +80 -51
- package/build/src/akuma/request-serve.d.ts +1 -2
- package/build/src/akuma/request-serve.js +94 -39
- package/build/src/akuma/request-wire.d.ts +57 -26
- package/build/src/akuma/request-wire.js +64 -25
- package/build/src/akuma/turn-drive.d.ts +5 -4
- package/build/src/akuma/turn-drive.js +27 -11
- package/build/src/akuma-body.d.ts +4 -6
- package/build/src/akuma-body.js +19 -17
- package/build/src/bounded-list.d.ts +8 -0
- package/build/src/bounded-list.js +14 -0
- package/build/src/cli/accepted.d.ts +1 -1
- package/build/src/cli/accepted.js +1 -0
- package/build/src/cli/commands/akuma-invoke.js +1 -25
- package/build/src/cli/commands/contract-help.d.ts +5 -2
- package/build/src/cli/commands/contract-help.js +62 -12
- package/build/src/cli/commands/contract-invoke.d.ts +5 -0
- package/build/src/cli/commands/contract-invoke.js +26 -1
- package/build/src/cli/commands/contract.js +50 -18
- package/build/src/cli/commands/task-invoke.js +10 -2
- package/build/src/cli/commands/task-query.js +3 -4
- package/build/src/cli/coordinates.d.ts +13 -0
- package/build/src/cli/coordinates.js +38 -24
- package/build/src/cli/draft.d.ts +1 -1
- package/build/src/cli/invoke.js +14 -1
- package/build/src/cli/render/akuma-activity.d.ts +0 -1
- package/build/src/cli/render/akuma-activity.js +5 -38
- package/build/src/cli/render/akuma.js +4 -1
- package/build/src/cli/render/audit.js +4 -1
- package/build/src/cli/render/catalog.js +13 -39
- package/build/src/cli/render/contract.js +4 -14
- package/build/src/cli/render/kanshi-akuma.js +3 -8
- package/build/src/cli/render/kanshi.js +8 -34
- package/build/src/cli/render/nuke.js +7 -3
- package/build/src/cli/render/receipt.d.ts +1 -0
- package/build/src/cli/render/receipt.js +8 -0
- package/build/src/cli/render/refusal.js +4 -3
- package/build/src/cli/render/task.js +4 -37
- package/build/src/cli/render/terminal.d.ts +2 -6
- package/build/src/cli/render/terminal.js +4 -9
- package/build/src/cli/result.d.ts +2 -1
- package/build/src/contract-guidance.js +13 -3
- package/build/src/contract-worktree.d.ts +1 -0
- package/build/src/contract-worktree.js +26 -2
- package/build/src/core/facts/codec.d.ts +2 -1
- package/build/src/core/facts/codec.js +3 -0
- package/build/src/core/facts/gate.d.ts +2 -1
- package/build/src/core/facts/gate.js +63 -0
- package/build/src/core/verbs/abandon.d.ts +2 -1
- package/build/src/core/verbs/abandon.js +16 -0
- package/build/src/core/verbs/amend.d.ts +2 -1
- package/build/src/core/verbs/amend.js +20 -0
- package/build/src/core/verbs/arc.d.ts +2 -1
- package/build/src/core/verbs/arc.js +16 -0
- package/build/src/core/verbs/attestation.d.ts +2 -1
- package/build/src/core/verbs/attestation.js +16 -0
- package/build/src/core/verbs/bind.d.ts +2 -1
- package/build/src/core/verbs/bind.js +16 -0
- package/build/src/core/verbs/deliver.d.ts +2 -1
- package/build/src/core/verbs/deliver.js +16 -0
- package/build/src/core/verbs/placement.d.ts +2 -1
- package/build/src/core/verbs/placement.js +58 -1
- package/build/src/dispatch/association.d.ts +13 -0
- package/build/src/dispatch/association.js +20 -0
- package/build/src/dispatch/index.d.ts +6 -0
- package/build/src/dispatch/index.js +27 -3
- package/build/src/git/hooks.d.ts +9 -1
- package/build/src/git/hooks.js +79 -1
- package/build/src/git/nuke.d.ts +2 -1
- package/build/src/git/nuke.js +6 -11
- package/build/src/git/private-state-seat.d.ts +26 -1
- package/build/src/git/private-state-seat.js +78 -13
- package/build/src/git/process.d.ts +2 -0
- package/build/src/git/reconcile.d.ts +1 -2
- package/build/src/git/reconcile.js +0 -6
- package/build/src/git/repository.d.ts +0 -2
- package/build/src/git/repository.js +27 -25
- package/build/src/git/result-codec.d.ts +29 -0
- package/build/src/git/result-codec.js +306 -0
- package/build/src/git/target-placement.js +2 -8
- package/build/src/git/workspace.d.ts +2 -0
- package/build/src/git/workspace.js +4 -0
- package/build/src/index.d.ts +1 -0
- package/build/src/kanshi/fleet.js +1 -1
- package/build/src/kanshi/read.js +55 -28
- package/build/src/kanshi/report.d.ts +4 -3
- package/build/src/kanshi/select.js +14 -1
- package/build/src/library/address.d.ts +1 -0
- package/build/src/library/address.js +4 -7
- package/build/src/library/akuma-creation.d.ts +5 -3
- package/build/src/library/akuma-creation.js +90 -33
- package/build/src/library/audit.js +2 -1
- package/build/src/library/catalog.d.ts +10 -2
- package/build/src/library/catalog.js +65 -25
- package/build/src/library/composition.d.ts +1 -0
- package/build/src/library/composition.js +1 -0
- package/build/src/library/configuration.d.ts +4 -21
- package/build/src/library/configuration.js +2 -158
- package/build/src/library/continuation.d.ts +20 -1
- package/build/src/library/continuation.js +36 -0
- package/build/src/library/contract-bind.d.ts +1 -0
- package/build/src/library/contract-bind.js +16 -5
- package/build/src/library/contract-forwarding.d.ts +2 -2
- package/build/src/library/contract-forwarding.js +18 -4
- package/build/src/library/contract-handle.js +4 -3
- package/build/src/library/contract-operations.d.ts +5 -9
- package/build/src/library/contract-operations.js +34 -47
- package/build/src/library/contract.d.ts +11 -2
- package/build/src/library/contract.js +21 -1
- package/build/src/library/delivery.d.ts +4 -1
- package/build/src/library/delivery.js +47 -0
- package/build/src/library/fleet.d.ts +4 -136
- package/build/src/library/fleet.js +31 -315
- package/build/src/library/keiyaku.d.ts +7 -3
- package/build/src/library/keiyaku.js +1 -0
- package/build/src/library/mutation.d.ts +431 -3
- package/build/src/library/mutation.js +192 -19
- package/build/src/library/nuke.d.ts +7 -1
- package/build/src/library/nuke.js +18 -4
- package/build/src/library/reconcile.d.ts +14 -0
- package/build/src/library/reconcile.js +22 -8
- package/build/src/library/refusal.d.ts +9 -1
- package/build/src/library/refusal.js +76 -0
- package/build/src/library/region.d.ts +0 -8
- package/build/src/library/region.js +1 -29
- package/build/src/library/result-codec.d.ts +2 -0
- package/build/src/library/result-codec.js +12 -0
- package/build/src/markdown/lex.js +9 -2
- package/build/src/markdown/types.d.ts +2 -1
- package/build/src/markdown/types.js +3 -1
- package/build/src/plugin/akuma-signals.d.ts +12 -0
- package/build/src/plugin/akuma-signals.js +14 -0
- package/build/src/plugin/public.d.ts +51 -0
- package/build/src/plugin/public.js +1 -0
- package/build/src/plugin/runtime.d.ts +14 -0
- package/build/src/plugin/runtime.js +357 -0
- package/build/src/protocol/amend.js +3 -3
- package/build/src/protocol/attempt.d.ts +2 -1
- package/build/src/protocol/attempt.js +1 -1
- package/build/src/protocol/audit.d.ts +1 -1
- package/build/src/protocol/audit.js +1 -1
- package/build/src/protocol/bind.d.ts +3 -1
- package/build/src/protocol/bind.js +26 -0
- package/build/src/protocol/completion.js +0 -1
- package/build/src/protocol/deliver.d.ts +1 -1
- package/build/src/protocol/deliver.js +136 -57
- package/build/src/protocol/intent.d.ts +0 -1
- package/build/src/protocol/intent.js +0 -1
- package/build/src/protocol/operations.d.ts +9 -2
- package/build/src/protocol/operations.js +16 -1
- package/build/src/protocol/outcome.d.ts +3 -0
- package/build/src/protocol/outcome.js +17 -1
- package/build/src/protocol/placement.js +10 -3
- package/build/src/protocol/read/status.d.ts +8 -0
- package/build/src/protocol/read/status.js +49 -0
- package/build/src/protocol/reintegrate.js +10 -3
- package/build/src/protocol/result-codec.d.ts +22 -0
- package/build/src/protocol/result-codec.js +442 -0
- package/build/src/protocol/review.d.ts +1 -1
- package/build/src/protocol/review.js +81 -46
- package/build/src/protocol/run.d.ts +22 -2
- package/build/src/protocol/run.js +119 -29
- package/build/src/runtime/proc/lifecycle.js +1 -1
- package/build/src/runtime/proc/line-rpc.d.ts +7 -1
- package/build/src/runtime/proc/line-rpc.js +49 -9
- package/build/src/runtime/proc/run.d.ts +0 -1
- package/build/src/runtime/proc/run.js +1 -11
- package/build/src/runtime/proc/termination.d.ts +1 -0
- package/build/src/runtime/proc/termination.js +63 -18
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settings.d.ts +10 -0
- package/build/src/settings.js +88 -2
- package/build/src/settlement/result-codec.d.ts +2 -0
- package/build/src/settlement/result-codec.js +56 -0
- package/build/src/settlement/settle.d.ts +5 -12
- package/build/src/settlement/settle.js +60 -55
- package/build/src/task/board.d.ts +3 -0
- package/build/src/task/board.js +22 -4
- package/build/src/task/catalog.d.ts +13 -2
- package/build/src/task/catalog.js +59 -5
- package/build/src/task/compose.d.ts +4 -2
- package/build/src/task/compose.js +56 -30
- package/build/src/task/created-observation.d.ts +39 -0
- package/build/src/task/created-observation.js +26 -0
- package/build/src/task/identity.d.ts +6 -0
- package/build/src/task/identity.js +48 -7
- package/build/src/task/index.d.ts +4 -3
- package/build/src/task/index.js +19 -16
- package/build/src/task/input.d.ts +3 -1
- package/build/src/task/input.js +7 -4
- package/build/src/task/lifecycle-operations.d.ts +6 -0
- package/build/src/task/lifecycle-operations.js +116 -0
- package/build/src/task/mutation-result.d.ts +762 -20
- package/build/src/task/mutation-result.js +34 -9
- package/build/src/task/mutation.d.ts +4 -9
- package/build/src/task/mutation.js +17 -52
- package/build/src/task/operation-types.d.ts +114 -0
- package/build/src/task/operation-types.js +1 -0
- package/build/src/task/operations.d.ts +6 -113
- package/build/src/task/operations.js +75 -117
- package/build/src/task/query.d.ts +8 -11
- package/build/src/task/query.js +7 -14
- package/build/src/task/store.d.ts +11 -1
- package/build/src/task/store.js +169 -55
- package/build/src/task/view.d.ts +4 -0
- package/build/src/task/view.js +7 -0
- package/build/src/verification/declaration.d.ts +1 -0
- package/build/src/verification/declaration.js +18 -1
- package/build/src/verification/execution.d.ts +0 -1
- package/build/src/verification/execution.js +5 -5
- package/build/src/world.d.ts +0 -2
- package/build/src/world.js +3 -7
- package/package.json +17 -5
- package/build/src/cli/square-edge.d.ts +0 -7
- package/build/src/cli/square-edge.js +0 -159
- package/build/src/git/ref-migration.d.ts +0 -22
- package/build/src/git/ref-migration.js +0 -55
- package/build/src/library/contract-forwarding-reconciliation-result.d.ts +0 -102
- package/build/src/library/contract-forwarding-reconciliation-result.js +0 -92
- package/build/src/library/contract-forwarding-result.d.ts +0 -4270
- package/build/src/library/contract-forwarding-result.js +0 -521
package/build/src/task/store.js
CHANGED
|
@@ -3,8 +3,9 @@ import { closeSync, fsyncSync, openSync, renameSync, writeFileSync } from "node:
|
|
|
3
3
|
import { lstat, mkdir, readFile, readdir, rmdir, unlink } from "node:fs/promises";
|
|
4
4
|
import { dirname, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { acquireSqliteTransactionLock, SqliteTransactionLockError, } from "../coordination/sqlite-transaction-lock.js";
|
|
6
|
+
import { boundedListLimit, projectBoundedList } from "../bounded-list.js";
|
|
6
7
|
import { parseTaskDocument } from "./document.js";
|
|
7
|
-
import { formatTaskId, parseTaskId, taskAuthorityPath } from "./identity.js";
|
|
8
|
+
import { formatTaskId, parseTaskId, physicalTaskSegment, TASK_LOCAL_ID_BYTES, sameNamespace, taskAuthorityPath, } from "./identity.js";
|
|
8
9
|
export const DEFAULT_TASK_LOCK_TIMEOUT_MS = 3_000;
|
|
9
10
|
function syncTaskDirectory(path) {
|
|
10
11
|
if (process.platform === "win32")
|
|
@@ -23,22 +24,73 @@ function tasksDirectory(world) {
|
|
|
23
24
|
function allocationLockPath(world) {
|
|
24
25
|
return resolve(world, ".keiyaku", "locks", "task-allocation.sqlite");
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
function custodyError(path) {
|
|
28
|
+
return new Error(`Task authority custody violation: symlink or non-authority component: ${path}`);
|
|
29
|
+
}
|
|
30
|
+
async function requireDirectory(path, mode) {
|
|
29
31
|
try {
|
|
30
|
-
|
|
32
|
+
const stat = await lstat(path);
|
|
33
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
34
|
+
throw custodyError(path);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error.code !== "ENOENT")
|
|
39
|
+
throw error;
|
|
40
|
+
if (mode === "read")
|
|
41
|
+
return false;
|
|
42
|
+
try {
|
|
43
|
+
await mkdir(path);
|
|
44
|
+
syncTaskDirectory(dirname(path));
|
|
45
|
+
}
|
|
46
|
+
catch (mkdirError) {
|
|
47
|
+
if (mkdirError.code !== "EEXIST")
|
|
48
|
+
throw mkdirError;
|
|
49
|
+
}
|
|
50
|
+
const stat = await lstat(path);
|
|
51
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
52
|
+
throw custodyError(path);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function resolveAuthority(world, id, mode) {
|
|
57
|
+
const coordinate = parseTaskId(id);
|
|
58
|
+
const root = tasksDirectory(world);
|
|
59
|
+
const safePath = taskAuthorityPath(root, coordinate);
|
|
60
|
+
if (!(await requireDirectory(root, mode)))
|
|
61
|
+
return { path: safePath, exists: false };
|
|
62
|
+
let parent = root;
|
|
63
|
+
for (const segment of coordinate.namespace) {
|
|
64
|
+
parent = resolve(parent, physicalTaskSegment(segment));
|
|
65
|
+
if (!(await requireDirectory(parent, mode)))
|
|
66
|
+
return { path: safePath, exists: false };
|
|
67
|
+
}
|
|
68
|
+
const path = safePath;
|
|
69
|
+
try {
|
|
70
|
+
const stat = await lstat(path);
|
|
71
|
+
if (stat.isSymbolicLink() || !stat.isFile())
|
|
72
|
+
throw custodyError(path);
|
|
73
|
+
return { path, exists: true };
|
|
31
74
|
}
|
|
32
75
|
catch (error) {
|
|
33
76
|
if (error.code === "ENOENT")
|
|
34
|
-
return
|
|
77
|
+
return { path, exists: false };
|
|
35
78
|
throw error;
|
|
36
79
|
}
|
|
80
|
+
}
|
|
81
|
+
async function authorityFiles(directory) {
|
|
82
|
+
if (!(await requireDirectory(directory, "read")))
|
|
83
|
+
return [];
|
|
84
|
+
const files = [];
|
|
85
|
+
const entries = await readdir(directory);
|
|
37
86
|
for (const entry of entries) {
|
|
38
|
-
const path = resolve(directory, entry
|
|
39
|
-
|
|
87
|
+
const path = resolve(directory, entry);
|
|
88
|
+
const stat = await lstat(path);
|
|
89
|
+
if (stat.isSymbolicLink())
|
|
90
|
+
throw custodyError(path);
|
|
91
|
+
if (stat.isDirectory())
|
|
40
92
|
files.push(...(await authorityFiles(path)));
|
|
41
|
-
else if (
|
|
93
|
+
else if (stat.isFile() && entry.endsWith(".md"))
|
|
42
94
|
files.push(path);
|
|
43
95
|
}
|
|
44
96
|
return files.sort((a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b)));
|
|
@@ -47,38 +99,33 @@ function coordinateFromPath(tasksDirectory, path) {
|
|
|
47
99
|
const local = relative(tasksDirectory, path);
|
|
48
100
|
if (local.startsWith("..") || resolve(tasksDirectory, local) !== path || !local.endsWith(".md"))
|
|
49
101
|
throw new Error(`invalid Task authority path: ${path}`);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
return formatTaskId(coordinateFromPath(tasksDirectory, path));
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
102
|
+
const segments = local.slice(0, -3).split(sep);
|
|
103
|
+
segments.forEach((segment) => physicalTaskSegment(segment));
|
|
104
|
+
return parseTaskId(`task/${segments.join("/")}`);
|
|
59
105
|
}
|
|
60
106
|
async function ownedAuthorityCandidates(directory) {
|
|
61
107
|
const candidates = [];
|
|
62
108
|
for (const path of await authorityFiles(directory)) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
109
|
+
try {
|
|
110
|
+
candidates.push({ id: formatTaskId(coordinateFromPath(directory, path)), path });
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// Files outside the Task identity grammar are not owned candidates.
|
|
114
|
+
}
|
|
66
115
|
}
|
|
67
116
|
return candidates;
|
|
68
117
|
}
|
|
69
118
|
async function removeEmptyTaskDirectories(directory, removeSelf) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
if (error.code === "ENOENT")
|
|
76
|
-
return;
|
|
77
|
-
throw error;
|
|
78
|
-
}
|
|
119
|
+
if (!(await requireDirectory(directory, "read")))
|
|
120
|
+
return;
|
|
121
|
+
const entries = await readdir(directory);
|
|
79
122
|
for (const entry of entries) {
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
const path = resolve(directory, entry);
|
|
124
|
+
const stat = await lstat(path);
|
|
125
|
+
if (stat.isSymbolicLink())
|
|
126
|
+
throw custodyError(path);
|
|
127
|
+
if (stat.isDirectory())
|
|
128
|
+
await removeEmptyTaskDirectories(path, true);
|
|
82
129
|
}
|
|
83
130
|
if (!removeSelf)
|
|
84
131
|
return;
|
|
@@ -108,22 +155,42 @@ export async function readBoard(world) {
|
|
|
108
155
|
}
|
|
109
156
|
return { board: { tasks }, bytes };
|
|
110
157
|
}
|
|
158
|
+
function compareRecentTasks(left, right) {
|
|
159
|
+
const updated = right.updatedAt.localeCompare(left.updatedAt);
|
|
160
|
+
return updated === 0 ? Buffer.compare(Buffer.from(left.id), Buffer.from(right.id)) : updated;
|
|
161
|
+
}
|
|
162
|
+
export async function readRecentTaskDocuments(world, input) {
|
|
163
|
+
const limit = boundedListLimit(input.limit);
|
|
164
|
+
const directory = tasksDirectory(world);
|
|
165
|
+
const tasks = [];
|
|
166
|
+
for (const path of await authorityFiles(directory)) {
|
|
167
|
+
const metadata = await lstat(path);
|
|
168
|
+
if (!metadata.isFile() || metadata.isSymbolicLink())
|
|
169
|
+
throw new Error(`Task authority is not a regular file: ${path}`);
|
|
170
|
+
const coordinate = coordinateFromPath(directory, path);
|
|
171
|
+
if (input.namespace !== undefined && !sameNamespace(coordinate.namespace, input.namespace))
|
|
172
|
+
continue;
|
|
173
|
+
const task = parseTaskDocument(await readFile(path), coordinate);
|
|
174
|
+
if (input.selection === "active" && (task.state === "done" || task.state === "drop"))
|
|
175
|
+
continue;
|
|
176
|
+
tasks.push(task);
|
|
177
|
+
}
|
|
178
|
+
return projectBoundedList(tasks.sort(compareRecentTasks), limit);
|
|
179
|
+
}
|
|
180
|
+
export async function readTaskDocument(world, id) {
|
|
181
|
+
const resolved = await resolveAuthority(world, id, "read");
|
|
182
|
+
return resolved.exists ? parseTaskDocument(await readFile(resolved.path), parseTaskId(id)) : undefined;
|
|
183
|
+
}
|
|
111
184
|
function equal(left, right) {
|
|
112
185
|
return left === null || right === null ? left === right : Buffer.from(left).equals(Buffer.from(right));
|
|
113
186
|
}
|
|
114
|
-
async function currentBytes(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
if (error.code === "ENOENT")
|
|
120
|
-
return null;
|
|
121
|
-
throw error;
|
|
122
|
-
}
|
|
187
|
+
async function currentBytes(world, id) {
|
|
188
|
+
const resolved = await resolveAuthority(world, id, "read");
|
|
189
|
+
return resolved.exists ? readFile(resolved.path) : null;
|
|
123
190
|
}
|
|
124
191
|
export async function replaceAuthority(input) {
|
|
125
|
-
const
|
|
126
|
-
|
|
192
|
+
const resolved = await resolveAuthority(input.world, input.id, "write");
|
|
193
|
+
const parent = dirname(resolved.path);
|
|
127
194
|
const temporary = resolve(parent, `.tmp-${randomBytes(8).toString("hex")}`);
|
|
128
195
|
let descriptor;
|
|
129
196
|
try {
|
|
@@ -132,9 +199,9 @@ export async function replaceAuthority(input) {
|
|
|
132
199
|
fsyncSync(descriptor);
|
|
133
200
|
closeSync(descriptor);
|
|
134
201
|
descriptor = undefined;
|
|
135
|
-
if (!equal(await currentBytes(input.
|
|
202
|
+
if (!equal(await currentBytes(input.world, input.id), input.expected))
|
|
136
203
|
return "concurrent-modification";
|
|
137
|
-
renameSync(temporary,
|
|
204
|
+
renameSync(temporary, resolved.path);
|
|
138
205
|
syncTaskDirectory(parent);
|
|
139
206
|
return "replaced";
|
|
140
207
|
}
|
|
@@ -149,9 +216,31 @@ export async function replaceAuthority(input) {
|
|
|
149
216
|
}
|
|
150
217
|
}
|
|
151
218
|
}
|
|
219
|
+
export async function authorityBytesMatch(world, id, expected) {
|
|
220
|
+
return equal(await currentBytes(world, id), expected);
|
|
221
|
+
}
|
|
152
222
|
function lockPath(world, id) {
|
|
153
223
|
const coordinate = parseTaskId(id);
|
|
154
|
-
return resolve(world, ".keiyaku", "locks", "task", ...coordinate.namespace, `${coordinate.localId}.sqlite`);
|
|
224
|
+
return resolve(world, ".keiyaku", "locks", "task", ...coordinate.namespace.map((segment) => physicalTaskSegment(segment)), `${physicalTaskSegment(coordinate.localId, TASK_LOCAL_ID_BYTES)}.sqlite`);
|
|
225
|
+
}
|
|
226
|
+
function errorDetail(error) {
|
|
227
|
+
return error instanceof Error ? error.message : String(error);
|
|
228
|
+
}
|
|
229
|
+
function preserveCommittedResult(result, errors) {
|
|
230
|
+
if (typeof result === "object" && result !== null && "kind" in result) {
|
|
231
|
+
const kind = result.kind;
|
|
232
|
+
if (kind === "accepted" || kind === "changed" || kind === "incomplete") {
|
|
233
|
+
const previous = result.cleanup;
|
|
234
|
+
return {
|
|
235
|
+
...result,
|
|
236
|
+
cleanup: {
|
|
237
|
+
kind: "lock-release-failed",
|
|
238
|
+
diagnostics: [...(previous?.diagnostics ?? []), ...errors.map(errorDetail)],
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
throw new AggregateError(errors, "Task lock cleanup failed");
|
|
155
244
|
}
|
|
156
245
|
export async function nukeTaskAuthority(world, options) {
|
|
157
246
|
const directory = tasksDirectory(world);
|
|
@@ -162,9 +251,9 @@ export async function nukeTaskAuthority(world, options) {
|
|
|
162
251
|
return await withTaskLocks({ world, allocation: false, ids, ...lockOptions }, async () => {
|
|
163
252
|
for (const candidate of candidates) {
|
|
164
253
|
try {
|
|
165
|
-
const
|
|
166
|
-
if (
|
|
167
|
-
await unlink(
|
|
254
|
+
const resolved = await resolveAuthority(world, candidate.id, "read");
|
|
255
|
+
if (resolved.exists)
|
|
256
|
+
await unlink(resolved.path);
|
|
168
257
|
}
|
|
169
258
|
catch (error) {
|
|
170
259
|
if (error.code !== "ENOENT")
|
|
@@ -185,6 +274,8 @@ export async function withTaskLocks(input, action) {
|
|
|
185
274
|
.map((id) => lockPath(input.world, id)),
|
|
186
275
|
];
|
|
187
276
|
const held = [];
|
|
277
|
+
let result;
|
|
278
|
+
let failure;
|
|
188
279
|
try {
|
|
189
280
|
for (const path of paths)
|
|
190
281
|
held.push(await acquireSqliteTransactionLock({
|
|
@@ -193,17 +284,40 @@ export async function withTaskLocks(input, action) {
|
|
|
193
284
|
timeoutMs: input.timeoutMs ?? DEFAULT_TASK_LOCK_TIMEOUT_MS,
|
|
194
285
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
195
286
|
}));
|
|
196
|
-
|
|
287
|
+
result = await action();
|
|
197
288
|
}
|
|
198
289
|
catch (error) {
|
|
199
|
-
|
|
200
|
-
return "busy";
|
|
201
|
-
throw error;
|
|
290
|
+
failure = error;
|
|
202
291
|
}
|
|
203
292
|
finally {
|
|
204
|
-
|
|
205
|
-
|
|
293
|
+
const cleanupErrors = [];
|
|
294
|
+
for (const lock of held.reverse()) {
|
|
295
|
+
try {
|
|
296
|
+
lock.close();
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
cleanupErrors.push(error);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (cleanupErrors.length > 0) {
|
|
303
|
+
if (failure === undefined && result !== undefined)
|
|
304
|
+
result = preserveCommittedResult(result, cleanupErrors);
|
|
305
|
+
else if (failure === undefined)
|
|
306
|
+
failure = new AggregateError(cleanupErrors, "Task lock cleanup failed");
|
|
307
|
+
else {
|
|
308
|
+
const originalFailure = failure;
|
|
309
|
+
failure = new AggregateError([originalFailure, ...cleanupErrors], errorDetail(originalFailure), {
|
|
310
|
+
cause: originalFailure,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (failure !== undefined) {
|
|
316
|
+
if (failure instanceof SqliteTransactionLockError && failure.reason === "timeout")
|
|
317
|
+
return "busy";
|
|
318
|
+
throw failure;
|
|
206
319
|
}
|
|
320
|
+
return result;
|
|
207
321
|
}
|
|
208
322
|
export function authorityPath(world, id) {
|
|
209
323
|
return taskAuthorityPath(tasksDirectory(world), parseTaskId(id));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { parseTaskId } from "./identity.js";
|
|
2
|
+
export function taskView(document) {
|
|
3
|
+
return { ...document, namespace: parseTaskId(document.id).namespace };
|
|
4
|
+
}
|
|
5
|
+
export function advanceTaskTimestamp(previous, candidate) {
|
|
6
|
+
return candidate > previous ? candidate : new Date(Date.parse(previous) + 1).toISOString();
|
|
7
|
+
}
|
|
@@ -14,6 +14,7 @@ export type VerificationDeclarationRefusal = Readonly<{
|
|
|
14
14
|
kind: "verification-declaration-invalid";
|
|
15
15
|
contractId?: ContractId;
|
|
16
16
|
}>;
|
|
17
|
+
export declare function decodeVerificationDeclarationRefusal(value: unknown): VerificationDeclarationRefusal;
|
|
17
18
|
export type VerificationDeclarationPreparation = Preparation<VerificationDefinition | null, VerificationDeclarationRefusal>;
|
|
18
19
|
export declare const VERIFIED: Gate;
|
|
19
20
|
export declare function prepareVerificationDeclaration(input: Readonly<{
|
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
import { gate } from "../core/facts/types.js";
|
|
1
|
+
import { contractId, gate } from "../core/facts/types.js";
|
|
2
|
+
export function decodeVerificationDeclarationRefusal(value) {
|
|
3
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
4
|
+
throw new Error("malformed verification declaration refusal");
|
|
5
|
+
const object = value;
|
|
6
|
+
if (object.kind !== "verification-declaration-invalid")
|
|
7
|
+
throw new Error("malformed verification declaration refusal");
|
|
8
|
+
if (Object.keys(object).some((key) => key !== "kind" && key !== "contractId"))
|
|
9
|
+
throw new Error("malformed verification declaration refusal");
|
|
10
|
+
if (object.contractId === undefined)
|
|
11
|
+
return { kind: "verification-declaration-invalid" };
|
|
12
|
+
try {
|
|
13
|
+
return { kind: "verification-declaration-invalid", contractId: contractId(String(object.contractId)) };
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
throw new Error("malformed verification declaration refusal");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
2
19
|
export const VERIFIED = gate("verified");
|
|
3
20
|
export function prepareVerificationDeclaration(input) {
|
|
4
21
|
if (input.definition !== null || !input.gates.includes(VERIFIED)) {
|
|
@@ -41,7 +41,6 @@ export type ExecuteVerificationInput = Readonly<{
|
|
|
41
41
|
repository: GitRepository;
|
|
42
42
|
candidate: SnapshotId;
|
|
43
43
|
declarations: readonly VerificationDeclaration[];
|
|
44
|
-
environment: NodeJS.ProcessEnv;
|
|
45
44
|
materializeScratchCandidate: (repository: GitRepository, candidate: SnapshotId) => Promise<MaterializedScratchCandidate>;
|
|
46
45
|
projectSettings: (root: string) => Promise<Settings>;
|
|
47
46
|
signal?: AbortSignal;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { runHookCommands } from "../git/hooks.js";
|
|
2
|
-
import { worktreeHooksFrom } from "../library/configuration.js";
|
|
1
|
+
import { runHookCommands, worktreeHooksFrom } from "../git/hooks.js";
|
|
3
2
|
import { runProcess } from "../runtime/proc/run.js";
|
|
4
3
|
const SUMMARY_BYTES = 32 * 1024;
|
|
5
4
|
function argvFor(declaration) {
|
|
@@ -58,6 +57,7 @@ async function executeDeclarations(input) {
|
|
|
58
57
|
}
|
|
59
58
|
/** Execute one disposable Verification attempt over the exact integration snapshot. */
|
|
60
59
|
export async function executeVerification(input) {
|
|
60
|
+
const environment = process.env;
|
|
61
61
|
let scratch;
|
|
62
62
|
try {
|
|
63
63
|
scratch = await input.materializeScratchCandidate(input.repository, input.candidate);
|
|
@@ -75,7 +75,7 @@ export async function executeVerification(input) {
|
|
|
75
75
|
try {
|
|
76
76
|
const hooks = worktreeHooksFrom({ settings: await input.projectSettings(scratch.cwd) });
|
|
77
77
|
destroy = hooks.destroy;
|
|
78
|
-
const readiness = await runHookCommands(scratch.cwd, hooks.create, input.signal);
|
|
78
|
+
const readiness = await runHookCommands(scratch.cwd, hooks.create, input.signal, environment);
|
|
79
79
|
outcome =
|
|
80
80
|
readiness.kind === "cancelled"
|
|
81
81
|
? { kind: "cancelled" }
|
|
@@ -84,7 +84,7 @@ export async function executeVerification(input) {
|
|
|
84
84
|
: await executeDeclarations({
|
|
85
85
|
declarations: input.declarations,
|
|
86
86
|
cwd: scratch.cwd,
|
|
87
|
-
environment
|
|
87
|
+
environment,
|
|
88
88
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
89
89
|
});
|
|
90
90
|
}
|
|
@@ -94,7 +94,7 @@ export async function executeVerification(input) {
|
|
|
94
94
|
}
|
|
95
95
|
finally {
|
|
96
96
|
if (destroy !== undefined) {
|
|
97
|
-
const result = await runHookCommands(scratch.cwd, destroy);
|
|
97
|
+
const result = await runHookCommands(scratch.cwd, destroy, undefined, environment);
|
|
98
98
|
if (result.kind === "cancelled")
|
|
99
99
|
throw new Error("scratch destroy cancelled without a signal");
|
|
100
100
|
if (result.kind === "failed")
|
package/build/src/world.d.ts
CHANGED
|
@@ -11,8 +11,6 @@ export type WorldResolution = Readonly<{
|
|
|
11
11
|
candidate: WorldRoot | null;
|
|
12
12
|
establish: () => Promise<WorldRoot>;
|
|
13
13
|
}>;
|
|
14
|
-
export declare const KEIYAKU_SQUARE_FILE = "KEIYAKU.square";
|
|
15
|
-
export declare function keiyakuSquarePath(worldRoot: string): string;
|
|
16
14
|
export declare class WorldError extends Error {
|
|
17
15
|
readonly kind: "invalid-world" | "home-world" | "root-world";
|
|
18
16
|
constructor(kind: "invalid-world" | "home-world" | "root-world", message: string);
|
package/build/src/world.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { mkdir, realpath, stat } from "node:fs/promises";
|
|
1
|
+
import { lstat, mkdir, realpath, stat } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join, parse, resolve } from "node:path";
|
|
4
4
|
const WORLD_BRAND = Symbol("keiyaku.world");
|
|
5
|
-
export const KEIYAKU_SQUARE_FILE = "KEIYAKU.square";
|
|
6
|
-
export function keiyakuSquarePath(worldRoot) {
|
|
7
|
-
return join(worldRoot, ".square", KEIYAKU_SQUARE_FILE);
|
|
8
|
-
}
|
|
9
5
|
export class WorldError extends Error {
|
|
10
6
|
kind;
|
|
11
7
|
constructor(kind, message) {
|
|
@@ -60,7 +56,7 @@ async function rejectReservedRoot(root) {
|
|
|
60
56
|
async function ensureMarker(root) {
|
|
61
57
|
const path = marker(root);
|
|
62
58
|
try {
|
|
63
|
-
if (!(await
|
|
59
|
+
if (!(await lstat(path)).isDirectory())
|
|
64
60
|
throw new Error("not a directory");
|
|
65
61
|
return;
|
|
66
62
|
}
|
|
@@ -88,7 +84,7 @@ async function locateMarker(input) {
|
|
|
88
84
|
const filesystemRoot = parse(candidate).root;
|
|
89
85
|
if (candidate !== home && candidate !== filesystemRoot) {
|
|
90
86
|
try {
|
|
91
|
-
if (!(await
|
|
87
|
+
if (!(await lstat(marker(candidate))).isDirectory()) {
|
|
92
88
|
throw new WorldError("invalid-world", `world marker is not a directory: ${marker(candidate)}`);
|
|
93
89
|
}
|
|
94
90
|
return brand(candidate);
|
package/package.json
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrosheep/keiyaku",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.13",
|
|
4
|
+
"workspaces": [
|
|
5
|
+
"plugins/*"
|
|
6
|
+
],
|
|
4
7
|
"files": [
|
|
5
8
|
"build"
|
|
6
9
|
],
|
|
@@ -22,6 +25,10 @@
|
|
|
22
25
|
"./akuma": {
|
|
23
26
|
"types": "./build/src/akuma/index.d.ts",
|
|
24
27
|
"import": "./build/src/akuma/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./plugin": {
|
|
30
|
+
"types": "./build/src/plugin/public.d.ts",
|
|
31
|
+
"import": "./build/src/plugin/public.js"
|
|
25
32
|
}
|
|
26
33
|
},
|
|
27
34
|
"bin": {
|
|
@@ -32,15 +39,18 @@
|
|
|
32
39
|
"node": ">=22.19.0"
|
|
33
40
|
},
|
|
34
41
|
"scripts": {
|
|
35
|
-
"build": "node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('build', { recursive: true, force: true })\" && tsc -p tsconfig.json && node scripts/copy-integrations.js && node scripts/build-windows-launcher.js && node --input-type=module -e \"import { chmodSync } from 'node:fs'; chmodSync('build/src/cli/index.js', 0o755)\"",
|
|
42
|
+
"build": "node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('build', { recursive: true, force: true })\" && tsc -p tsconfig.json && npm run build --workspace @astrosheep/keiyaku-plugin-square && node scripts/copy-integrations.js && node scripts/build-windows-launcher.js && node --input-type=module -e \"import { chmodSync } from 'node:fs'; chmodSync('build/src/cli/index.js', 0o755)\"",
|
|
36
43
|
"prepack": "node scripts/verify-package.js",
|
|
37
44
|
"postpack": "node scripts/verify-package.js",
|
|
38
45
|
"harness:version": "node scripts/update-harness-version.js",
|
|
39
46
|
"review:model-impact": "node --import tsx scripts/model-change-impact.ts",
|
|
40
47
|
"format": "prettier --write --print-width 120 \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
41
48
|
"format:check": "prettier --check --print-width 120 \"src/**/*.ts\" \"scripts/**/*.ts\"",
|
|
49
|
+
"lint": "eslint src scripts tests",
|
|
42
50
|
"test": "node scripts/test-entry.mjs",
|
|
43
|
-
"test:all": "
|
|
51
|
+
"test:all": "node scripts/test-entry.mjs --release",
|
|
52
|
+
"test:release": "node scripts/test-entry.mjs --release",
|
|
53
|
+
"test:dev": "node scripts/test-entry.mjs --dev",
|
|
44
54
|
"test:architecture": "node --import tsx scripts/check-architecture.ts",
|
|
45
55
|
"test:e2e": "npm run build && node scripts/run-tests.mjs tests/packaged-cli.e2e.ts",
|
|
46
56
|
"test:local": "node scripts/run-tests.mjs --suite local",
|
|
@@ -50,7 +60,9 @@
|
|
|
50
60
|
"test:focused": "node scripts/run-tests.mjs",
|
|
51
61
|
"test:focused:serial": "node scripts/run-tests.mjs --test-concurrency=1",
|
|
52
62
|
"test:reachability": "knip --include files,dependencies,unlisted,unresolved,duplicates",
|
|
53
|
-
"test:typecheck": "tsc -p tsconfig.json --noEmit"
|
|
63
|
+
"test:typecheck": "tsc -p tsconfig.json --noEmit && npm run test:typecheck:scoped-tests && npm run test:typecheck:scripts",
|
|
64
|
+
"test:typecheck:scoped-tests": "tsc -p tsconfig.tests.json",
|
|
65
|
+
"test:typecheck:scripts": "tsc -p tsconfig.scripts.json"
|
|
54
66
|
},
|
|
55
67
|
"devDependencies": {
|
|
56
68
|
"@types/cross-spawn": "^6.0.6",
|
|
@@ -65,7 +77,7 @@
|
|
|
65
77
|
"dependencies": {
|
|
66
78
|
"@agentclientprotocol/sdk": "^1.3.0",
|
|
67
79
|
"@anthropic-ai/claude-agent-sdk": "^0.3.226",
|
|
68
|
-
"@astrosheep/square": "0.
|
|
80
|
+
"@astrosheep/keiyaku-plugin-square": "0.1.1",
|
|
69
81
|
"@earendil-works/pi-coding-agent": "0.80.10",
|
|
70
82
|
"@opencode-ai/sdk": "1.18.3",
|
|
71
83
|
"cross-spawn": "^7.0.6",
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { AllocatedAkuma } from "../akuma/identity.js";
|
|
2
|
-
import { type WorldRoot } from "../world.js";
|
|
3
|
-
export declare function recognizeAndListen(worldRoot: WorldRoot, environment: NodeJS.ProcessEnv, allocated: AllocatedAkuma): Promise<{
|
|
4
|
-
committed: boolean;
|
|
5
|
-
participantName: string;
|
|
6
|
-
rollback(): Promise<void>;
|
|
7
|
-
} | void>;
|