@astrosheep/keiyaku 4.0.6 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/build/src/akuma/archetype.js +8 -1
  2. package/build/src/akuma/provider-recipe.d.ts +2 -0
  3. package/build/src/akuma/provider-recipe.js +10 -0
  4. package/build/src/akuma/providers/acp/config.d.ts +2 -0
  5. package/build/src/akuma/providers/acp/config.js +9 -1
  6. package/build/src/akuma/providers/acp/core.d.ts +5 -2
  7. package/build/src/akuma/providers/acp/core.js +29 -16
  8. package/build/src/akuma/providers/acp/index.js +8 -2
  9. package/build/src/akuma/providers/claude/index.d.ts +1 -1
  10. package/build/src/akuma/providers/claude/index.js +9 -5
  11. package/build/src/akuma/providers/codex-app-server/index.js +4 -1
  12. package/build/src/akuma/providers/grok-build/index.js +15 -1
  13. package/build/src/akuma/providers/index.js +1 -1
  14. package/build/src/akuma/providers/opencode-sdk/index.js +3 -0
  15. package/build/src/akuma/providers/pi/index.js +3 -1
  16. package/build/src/body/amend.js +0 -9
  17. package/build/src/body/region.js +2 -2
  18. package/build/src/cli/accepted.js +2 -0
  19. package/build/src/cli/commands/contract.d.ts +5 -3
  20. package/build/src/cli/commands/contract.js +23 -13
  21. package/build/src/cli/commands/task-invoke.d.ts +4 -1
  22. package/build/src/cli/commands/task-invoke.js +6 -3
  23. package/build/src/cli/commands/task.js +10 -2
  24. package/build/src/cli/parse.js +2 -2
  25. package/build/src/cli/render/akuma.js +42 -11
  26. package/build/src/cli/render/catalog.js +5 -1
  27. package/build/src/cli/render/contract.js +25 -3
  28. package/build/src/cli/render/kanshi.js +142 -94
  29. package/build/src/cli/render/receipt.d.ts +1 -0
  30. package/build/src/cli/render/receipt.js +18 -14
  31. package/build/src/cli/render/refusal.js +6 -0
  32. package/build/src/cli/render/task.js +63 -11
  33. package/build/src/cli/result.d.ts +8 -1
  34. package/build/src/contract-worktree.d.ts +0 -2
  35. package/build/src/contract-worktree.js +13 -24
  36. package/build/src/git/nuke.js +1 -6
  37. package/build/src/git/reconcile.d.ts +0 -2
  38. package/build/src/git/reconcile.js +2 -33
  39. package/build/src/git/tender.d.ts +13 -2
  40. package/build/src/git/tender.js +43 -4
  41. package/build/src/kanshi/read.js +32 -12
  42. package/build/src/kanshi/report.d.ts +2 -1
  43. package/build/src/library/continuation.d.ts +26 -0
  44. package/build/src/library/continuation.js +78 -0
  45. package/build/src/library/contract.d.ts +9 -5
  46. package/build/src/library/contract.js +27 -2
  47. package/build/src/library/delivery.d.ts +18 -13
  48. package/build/src/library/keiyaku.d.ts +1 -1
  49. package/build/src/library/mutation.js +11 -4
  50. package/build/src/protocol/deliver.d.ts +12 -2
  51. package/build/src/protocol/deliver.js +52 -19
  52. package/build/src/protocol/operations.d.ts +7 -2
  53. package/build/src/settlement/fence.d.ts +0 -2
  54. package/build/src/settlement/fence.js +0 -24
  55. package/build/src/task/board.d.ts +21 -2
  56. package/build/src/task/board.js +25 -12
  57. package/build/src/task/identity.js +14 -4
  58. package/build/src/task/index.d.ts +1 -0
  59. package/build/src/task/index.js +7 -1
  60. package/build/src/task/operations.d.ts +2 -1
  61. package/build/src/task/operations.js +15 -4
  62. package/build/src/task/query.js +2 -2
  63. package/build/src/task/store.d.ts +2 -0
  64. package/build/src/task/store.js +12 -83
  65. package/package.json +1 -1
@@ -5,6 +5,7 @@ import { dirname, relative, resolve, sep } from "node:path";
5
5
  import { acquireSqliteTransactionLock, SqliteTransactionLockError } from "../coordination/sqlite-transaction-lock.js";
6
6
  import { parseTaskDocument } from "./document.js";
7
7
  import { formatTaskId, parseTaskId, taskAuthorityPath } from "./identity.js";
8
+ export const DEFAULT_TASK_LOCK_TIMEOUT_MS = 3_000;
8
9
  function syncTaskDirectory(path) {
9
10
  if (process.platform === "win32")
10
11
  return;
@@ -17,7 +18,6 @@ function syncTaskDirectory(path) {
17
18
  }
18
19
  }
19
20
  function tasksDirectory(world) { return resolve(world, ".keiyaku", "tasks"); }
20
- function taskLocksDirectory(world) { return resolve(world, ".keiyaku", "locks", "task"); }
21
21
  function allocationLockPath(world) { return resolve(world, ".keiyaku", "locks", "task-allocation.sqlite"); }
22
22
  async function authorityFiles(directory) {
23
23
  const files = [];
@@ -105,90 +105,14 @@ function lockPath(world, id) {
105
105
  const coordinate = parseTaskId(id);
106
106
  return resolve(world, ".keiyaku", "locks", "task", ...coordinate.namespace, `${coordinate.localId}.sqlite`);
107
107
  }
108
- async function regularFile(path, label) {
109
- try {
110
- const entry = await lstat(path);
111
- if (!entry.isFile() || entry.isSymbolicLink())
112
- throw new Error(`${label} is not a regular file: ${path}`);
113
- return true;
114
- }
115
- catch (error) {
116
- if (error.code === "ENOENT")
117
- return false;
118
- throw error;
119
- }
120
- }
121
- async function taskLockFiles(world, directory = taskLocksDirectory(world)) {
122
- let entries;
123
- try {
124
- entries = await readdir(directory, { withFileTypes: true });
125
- }
126
- catch (error) {
127
- if (error.code === "ENOENT")
128
- return [];
129
- throw error;
130
- }
131
- const files = [];
132
- for (const entry of entries) {
133
- const path = resolve(directory, entry.name);
134
- if (entry.isDirectory()) {
135
- files.push(...await taskLockFiles(world, path));
136
- continue;
137
- }
138
- if (!entry.isFile() || entry.isSymbolicLink() || !entry.name.endsWith(".sqlite")) {
139
- throw new Error(`Task lock has foreign or corrupt custody: ${path}`);
140
- }
141
- const local = relative(taskLocksDirectory(world), path);
142
- const id = formatTaskId(parseTaskId(`task/${local.slice(0, -7).split(sep).join("/")}`));
143
- if (lockPath(world, id) !== path)
144
- throw new Error(`Task lock has invalid coordinate: ${path}`);
145
- files.push({ id, path });
146
- }
147
- return files.sort((left, right) => Buffer.compare(Buffer.from(left.path), Buffer.from(right.path)));
148
- }
149
- async function taskNukeCustody(world) {
150
- const snapshot = await readBoard(world);
151
- const locks = await taskLockFiles(world);
152
- const allocation = allocationLockPath(world);
153
- const lockPaths = [
154
- ...(await regularFile(allocation, "Task allocation lock") ? [allocation] : []),
155
- ...locks.map((lock) => lock.path),
156
- ];
157
- return {
158
- authorityIds: [...snapshot.board.tasks.keys()].sort((left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right))),
159
- lockIds: locks.map((lock) => lock.id),
160
- lockPaths,
161
- };
162
- }
163
- function sameIds(left, right) {
164
- return left.length === right.length && left.every((id, index) => id === right[index]);
165
- }
166
- async function removeRegularFile(path, label) {
167
- if (!(await regularFile(path, label)))
168
- return;
169
- await unlink(path);
170
- }
171
108
  export async function nukeTaskAuthority(world) {
172
- const initial = await taskNukeCustody(world);
173
- if (initial.authorityIds.length === 0 && initial.lockPaths.length === 0)
109
+ const ids = [...(await readBoard(world)).board.tasks.keys()]
110
+ .sort((left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right)));
111
+ if (ids.length === 0)
174
112
  return;
175
- const ids = [...new Set([...initial.authorityIds, ...initial.lockIds])];
176
113
  const result = await withTaskLocks({ world, allocation: true, ids }, async () => {
177
- const fresh = await taskNukeCustody(world);
178
- if (!sameIds(initial.authorityIds, fresh.authorityIds)) {
179
- throw new Error("Task authority changed while acquiring reset locks");
180
- }
181
- const expectedLocks = new Set([
182
- ...initial.lockPaths,
183
- allocationLockPath(world),
184
- ...initial.authorityIds.map((id) => lockPath(world, id)),
185
- ]);
186
- if (fresh.lockPaths.some((path) => !expectedLocks.has(path))) {
187
- throw new Error("Task lock custody changed while acquiring reset locks");
188
- }
189
- for (const id of fresh.authorityIds) {
190
- await removeRegularFile(authorityPath(world, id), "Task authority");
191
- }
114
+ for (const id of ids)
115
+ await unlink(authorityPath(world, id));
192
116
  });
193
117
  if (result === "busy")
194
118
  return "busy";
@@ -201,7 +125,12 @@ export async function withTaskLocks(input, action) {
201
125
  const held = [];
202
126
  try {
203
127
  for (const path of paths)
204
- held.push(await acquireSqliteTransactionLock({ path, mode: "immediate", timeoutMs: 3_000, ...(input.signal === undefined ? {} : { signal: input.signal }) }));
128
+ held.push(await acquireSqliteTransactionLock({
129
+ path,
130
+ mode: "immediate",
131
+ timeoutMs: input.timeoutMs ?? DEFAULT_TASK_LOCK_TIMEOUT_MS,
132
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
133
+ }));
205
134
  return await action();
206
135
  }
207
136
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/keiyaku",
3
- "version": "4.0.6",
3
+ "version": "4.1.0",
4
4
  "files": [
5
5
  "build"
6
6
  ],