@astrosheep/keiyaku 4.5.7 → 4.5.8

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 (234) hide show
  1. package/README.md +10 -0
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/opencode.js +3 -2
  5. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +10 -9
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +22 -1
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-babysit/SKILL.md +35 -96
  9. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +28 -49
  10. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +21 -15
  11. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +148 -89
  12. package/build/src/akuma/abort.d.ts +2 -1
  13. package/build/src/akuma/abort.js +6 -23
  14. package/build/src/akuma/akuma-handle.js +4 -2
  15. package/build/src/akuma/akuma-observe.d.ts +2 -0
  16. package/build/src/akuma/akuma-observe.js +1 -0
  17. package/build/src/akuma/akuma-product.js +9 -7
  18. package/build/src/akuma/akuma.d.ts +372 -9
  19. package/build/src/akuma/akuma.js +31 -1
  20. package/build/src/akuma/allowed.d.ts +1 -1
  21. package/build/src/akuma/allowed.js +1 -0
  22. package/build/src/akuma/body.d.ts +6 -4
  23. package/build/src/akuma/body.js +5 -3
  24. package/build/src/akuma/call-request.d.ts +80 -0
  25. package/build/src/akuma/call-request.js +211 -0
  26. package/build/src/akuma/heart/facts.d.ts +6 -78
  27. package/build/src/akuma/heart/index.d.ts +1 -1
  28. package/build/src/akuma/heart/request-authority.d.ts +5 -4
  29. package/build/src/akuma/heart/request-authority.js +20 -34
  30. package/build/src/akuma/heart/request-rows.js +12 -23
  31. package/build/src/akuma/heart/schema.d.ts +1 -1
  32. package/build/src/akuma/heart/schema.js +5 -13
  33. package/build/src/akuma/index.d.ts +1 -1
  34. package/build/src/akuma/index.js +1 -1
  35. package/build/src/akuma/projection-read.d.ts +1 -0
  36. package/build/src/akuma/projection-read.js +8 -1
  37. package/build/src/akuma/projection.d.ts +856 -137
  38. package/build/src/akuma/projection.js +190 -0
  39. package/build/src/akuma/provider.d.ts +30 -3
  40. package/build/src/akuma/provider.js +104 -0
  41. package/build/src/akuma/providers/acp/core.d.ts +2 -2
  42. package/build/src/akuma/providers/acp/core.js +13 -6
  43. package/build/src/akuma/providers/acp/index.js +5 -4
  44. package/build/src/akuma/providers/claude/index.d.ts +3 -3
  45. package/build/src/akuma/providers/claude/index.js +16 -5
  46. package/build/src/akuma/providers/codex-app-server/events.js +1 -1
  47. package/build/src/akuma/providers/codex-app-server/index.js +18 -6
  48. package/build/src/akuma/providers/grok-build/index.js +5 -4
  49. package/build/src/akuma/providers/opencode-sdk/index.js +66 -25
  50. package/build/src/akuma/providers/opencode-sdk/session.d.ts +3 -2
  51. package/build/src/akuma/providers/opencode-sdk/session.js +28 -11
  52. package/build/src/akuma/providers/pi/index.js +42 -14
  53. package/build/src/akuma/request-lifecycle.d.ts +3 -9
  54. package/build/src/akuma/request-lifecycle.js +4 -4
  55. package/build/src/akuma/request-rendezvous.d.ts +26 -0
  56. package/build/src/akuma/request-rendezvous.js +100 -0
  57. package/build/src/akuma/request-serve.d.ts +5 -65
  58. package/build/src/akuma/request-serve.js +81 -208
  59. package/build/src/akuma/request-wire.d.ts +109 -115
  60. package/build/src/akuma/request-wire.js +63 -318
  61. package/build/src/akuma/requests.d.ts +19 -47
  62. package/build/src/akuma/requests.js +51 -107
  63. package/build/src/akuma/turn-drive.d.ts +5 -3
  64. package/build/src/akuma/turn-drive.js +32 -10
  65. package/build/src/akuma-body.d.ts +7 -2
  66. package/build/src/akuma-body.js +68 -46
  67. package/build/src/cli/accepted.d.ts +1 -1
  68. package/build/src/cli/accepted.js +2 -2
  69. package/build/src/cli/commands/akuma-invoke.d.ts +2 -0
  70. package/build/src/cli/commands/akuma-invoke.js +17 -9
  71. package/build/src/cli/commands/akuma.js +7 -7
  72. package/build/src/cli/commands/contract-invoke.d.ts +3 -2
  73. package/build/src/cli/commands/contract-invoke.js +26 -19
  74. package/build/src/cli/commands/contract.d.ts +13 -13
  75. package/build/src/cli/commands/contract.js +32 -17
  76. package/build/src/cli/commands/install.d.ts +1 -1
  77. package/build/src/cli/commands/install.js +1 -1
  78. package/build/src/cli/commands/task-invoke.d.ts +3 -0
  79. package/build/src/cli/commands/task-invoke.js +23 -140
  80. package/build/src/cli/commands/task.js +54 -20
  81. package/build/src/cli/invoke.js +39 -25
  82. package/build/src/cli/main.js +2 -1
  83. package/build/src/cli/parse.d.ts +2 -2
  84. package/build/src/cli/parse.js +37 -22
  85. package/build/src/cli/render/akuma-activity.d.ts +4 -1
  86. package/build/src/cli/render/akuma-activity.js +45 -30
  87. package/build/src/cli/render/akuma-tool.d.ts +5 -1
  88. package/build/src/cli/render/akuma.js +5 -9
  89. package/build/src/cli/render/audit.js +1 -1
  90. package/build/src/cli/render/catalog.js +80 -21
  91. package/build/src/cli/render/contract-observation.d.ts +2 -0
  92. package/build/src/cli/render/contract-observation.js +11 -0
  93. package/build/src/cli/render/contract.js +25 -65
  94. package/build/src/cli/render/kanshi-akuma.d.ts +8 -0
  95. package/build/src/cli/render/kanshi-akuma.js +111 -0
  96. package/build/src/cli/render/kanshi.js +108 -213
  97. package/build/src/cli/render/receipt.d.ts +1 -2
  98. package/build/src/cli/render/receipt.js +76 -26
  99. package/build/src/cli/render/refusal.js +2 -10
  100. package/build/src/cli/render/region.js +5 -2
  101. package/build/src/cli/render/settings.d.ts +1 -1
  102. package/build/src/cli/render/settings.js +27 -8
  103. package/build/src/cli/render/task.js +22 -5
  104. package/build/src/cli/render/terminal.d.ts +28 -0
  105. package/build/src/cli/render/terminal.js +65 -0
  106. package/build/src/cli/result.d.ts +3 -3
  107. package/build/src/cli/runtime.js +3 -1
  108. package/build/src/cli/selectors.d.ts +2 -1
  109. package/build/src/cli/selectors.js +4 -3
  110. package/build/src/cli/square-edge.js +120 -36
  111. package/build/src/cli/usage.js +2 -1
  112. package/build/src/contract-guidance.d.ts +5 -0
  113. package/build/src/contract-guidance.js +121 -0
  114. package/build/src/contract-worktree.d.ts +0 -1
  115. package/build/src/contract-worktree.js +32 -83
  116. package/build/src/coordination/sqlite-transaction-lock.d.ts +1 -0
  117. package/build/src/coordination/sqlite-transaction-lock.js +5 -0
  118. package/build/src/core/facts/codec.d.ts +2 -0
  119. package/build/src/core/facts/codec.js +4 -0
  120. package/build/src/core/verbs/placement.d.ts +6 -1
  121. package/build/src/core/verbs/placement.js +5 -3
  122. package/build/src/dispatch/index.d.ts +0 -2
  123. package/build/src/dispatch/index.js +54 -55
  124. package/build/src/git/admission.js +2 -2
  125. package/build/src/git/hooks.d.ts +9 -15
  126. package/build/src/git/hooks.js +23 -317
  127. package/build/src/git/nuke.d.ts +2 -1
  128. package/build/src/git/nuke.js +36 -18
  129. package/build/src/git/private-state-seat.d.ts +12 -0
  130. package/build/src/git/private-state-seat.js +39 -0
  131. package/build/src/git/process.d.ts +6 -0
  132. package/build/src/git/process.js +19 -7
  133. package/build/src/git/read-observation.js +29 -20
  134. package/build/src/git/reconcile.d.ts +11 -0
  135. package/build/src/git/reconcile.js +64 -11
  136. package/build/src/git/repository.d.ts +6 -2
  137. package/build/src/git/repository.js +8 -0
  138. package/build/src/git/target-placement.d.ts +1 -0
  139. package/build/src/git/target-placement.js +5 -0
  140. package/build/src/git/tender.d.ts +1 -0
  141. package/build/src/git/terminal-reconcile.d.ts +1 -1
  142. package/build/src/git/terminal-reconcile.js +22 -20
  143. package/build/src/git/workspace.d.ts +14 -0
  144. package/build/src/git/workspace.js +44 -0
  145. package/build/src/kanshi/index.d.ts +1 -1
  146. package/build/src/kanshi/read.js +16 -16
  147. package/build/src/kanshi/report.d.ts +2 -2
  148. package/build/src/library/address.d.ts +9 -3
  149. package/build/src/library/address.js +3 -2
  150. package/build/src/library/akuma-creation.d.ts +4 -3
  151. package/build/src/library/akuma-creation.js +19 -13
  152. package/build/src/library/audit.d.ts +1 -1
  153. package/build/src/library/audit.js +5 -3
  154. package/build/src/library/catalog.d.ts +3 -1
  155. package/build/src/library/catalog.js +20 -6
  156. package/build/src/library/composition.d.ts +371 -0
  157. package/build/src/library/composition.js +28 -0
  158. package/build/src/library/configuration.js +8 -2
  159. package/build/src/library/continuation.js +66 -30
  160. package/build/src/library/contract-bind.js +10 -2
  161. package/build/src/library/contract-forwarding-reconciliation-result.d.ts +96 -0
  162. package/build/src/library/contract-forwarding-reconciliation-result.js +83 -0
  163. package/build/src/library/contract-forwarding-result.d.ts +6723 -0
  164. package/build/src/library/contract-forwarding-result.js +521 -0
  165. package/build/src/library/contract-forwarding.d.ts +72 -0
  166. package/build/src/library/contract-forwarding.js +125 -0
  167. package/build/src/library/contract-handle.d.ts +36 -0
  168. package/build/src/library/contract-handle.js +313 -0
  169. package/build/src/library/contract-operations.d.ts +1044 -67
  170. package/build/src/library/contract-operations.js +190 -118
  171. package/build/src/library/contract.d.ts +11 -32
  172. package/build/src/library/contract.js +16 -289
  173. package/build/src/library/fleet-result.d.ts +1454 -0
  174. package/build/src/library/fleet-result.js +96 -0
  175. package/build/src/library/fleet.d.ts +112 -54
  176. package/build/src/library/fleet.js +211 -71
  177. package/build/src/library/input.d.ts +2 -0
  178. package/build/src/library/input.js +4 -0
  179. package/build/src/library/keiyaku.d.ts +371 -20
  180. package/build/src/library/keiyaku.js +8 -21
  181. package/build/src/library/mutation.d.ts +3 -3
  182. package/build/src/library/mutation.js +5 -9
  183. package/build/src/library/nuke.d.ts +1 -1
  184. package/build/src/library/nuke.js +4 -3
  185. package/build/src/library/reconcile.d.ts +4 -0
  186. package/build/src/library/reconcile.js +2 -0
  187. package/build/src/library/refusal.d.ts +0 -9
  188. package/build/src/library/refusal.js +0 -7
  189. package/build/src/protocol/amend.js +56 -50
  190. package/build/src/protocol/attempt.d.ts +2 -0
  191. package/build/src/protocol/attempt.js +8 -1
  192. package/build/src/protocol/completion.js +7 -0
  193. package/build/src/protocol/deliver.js +6 -2
  194. package/build/src/protocol/operations.d.ts +4 -1
  195. package/build/src/protocol/operations.js +5 -2
  196. package/build/src/protocol/placement.d.ts +1 -0
  197. package/build/src/protocol/placement.js +52 -38
  198. package/build/src/protocol/read/observation.d.ts +1 -4
  199. package/build/src/protocol/read/observation.js +2 -15
  200. package/build/src/protocol/reintegrate.js +62 -58
  201. package/build/src/protocol/review.js +5 -0
  202. package/build/src/protocol/run.js +17 -13
  203. package/build/src/runtime/proc/run.d.ts +8 -0
  204. package/build/src/runtime/proc/run.js +27 -0
  205. package/build/src/runtime/proc/termination.js +26 -22
  206. package/build/src/runtime/proc/windows-launch.exe +0 -0
  207. package/build/src/settlement/holder.d.ts +2 -1
  208. package/build/src/settlement/holder.js +15 -4
  209. package/build/src/settlement/settle.js +99 -33
  210. package/build/src/task/board.d.ts +68 -27
  211. package/build/src/task/board.js +31 -1
  212. package/build/src/task/catalog.d.ts +4 -0
  213. package/build/src/task/catalog.js +23 -0
  214. package/build/src/task/compose-language.js +33 -7
  215. package/build/src/task/compose-parser.d.ts +1 -1
  216. package/build/src/task/compose-parser.js +1 -1
  217. package/build/src/task/compose.js +2 -0
  218. package/build/src/task/index.d.ts +12 -4
  219. package/build/src/task/index.js +97 -16
  220. package/build/src/task/mutation-result.d.ts +923 -0
  221. package/build/src/task/mutation-result.js +140 -0
  222. package/build/src/task/mutation.d.ts +552 -28
  223. package/build/src/task/mutation.js +304 -97
  224. package/build/src/task/operations.d.ts +1 -4
  225. package/build/src/task/operations.js +0 -6
  226. package/build/src/task/public.d.ts +2 -0
  227. package/build/src/task/public.js +1 -0
  228. package/build/src/workspace-place.d.ts +7 -2
  229. package/build/src/workspace-place.js +34 -24
  230. package/build/src/world.d.ts +1 -0
  231. package/build/src/world.js +11 -0
  232. package/package.json +8 -6
  233. package/build/src/akuma/request-execution.d.ts +0 -8
  234. package/build/src/akuma/request-execution.js +0 -98
@@ -1,4 +1,12 @@
1
- import { addInput, closed, namespace, record, taskId, taskIds, text, updateInput } from "./input.js";
1
+ /* eslint-disable max-lines -- Task owns one coherent forwarded mutation boundary. */
2
+ import { World } from "../world.js";
3
+ import { requestBodyCommand } from "../akuma/request-rendezvous.js";
4
+ import { eraseRequestCommand } from "../akuma/request-wire.js";
5
+ import { addTask, addTaskDocument, batchTasks, lifecycleTask, updateTask, } from "./operations.js";
6
+ import { composeTasks } from "./compose.js";
7
+ import { isTaskSegment } from "./identity.js";
8
+ import { taskBatchResultSchema, taskCompositionResultSchema, taskMutationIdSchema, taskMutationResultSchema, taskUpdateResultSchema, isTaskMutationExecutionResult, } from "./mutation-result.js";
9
+ import { z } from "zod";
2
10
  export const TASK_MUTATION_ACTIONS = Object.freeze([
3
11
  "task.add",
4
12
  "task.addDocument",
@@ -15,121 +23,320 @@ const TASK_MUTATION_ACTION_SET = new Set(TASK_MUTATION_ACTIONS);
15
23
  export function isTaskMutationAction(value) {
16
24
  return typeof value === "string" && TASK_MUTATION_ACTION_SET.has(value);
17
25
  }
18
- function mutationObject(value, keys, label) {
19
- const decoded = record(value, label);
20
- closed(decoded, keys, label);
21
- return decoded;
26
+ const nonblankTextSchema = z.string().refine((value) => value.trim() !== "");
27
+ const namespaceSchema = z.array(z.string().refine(isTaskSegment)).readonly();
28
+ const taskIdsBaseSchema = z.array(taskMutationIdSchema).superRefine((ids, context) => {
29
+ if (new Set(ids).size !== ids.length)
30
+ context.addIssue({ code: "custom", message: "TaskIds must be unique" });
31
+ });
32
+ const taskIdsSchema = taskIdsBaseSchema.readonly();
33
+ const nonemptyTaskIdsSchema = taskIdsBaseSchema.min(1).readonly();
34
+ const taskStateSchema = z.enum(["open", "in_progress", "on_hold", "done", "drop"]);
35
+ const taskPrioritySchema = z.union([z.literal(0), z.literal(1), z.literal(2), z.literal(3)]);
36
+ const addInputSchema = z
37
+ .object({
38
+ title: nonblankTextSchema,
39
+ namespace: namespaceSchema,
40
+ body: z.string().optional(),
41
+ note: z.string().optional(),
42
+ state: taskStateSchema.optional(),
43
+ priority: taskPrioritySchema.optional(),
44
+ needs: taskIdsSchema.optional(),
45
+ parent: taskMutationIdSchema.nullable().optional(),
46
+ supersedes: taskIdsSchema.optional(),
47
+ relates: taskIdsSchema.optional(),
48
+ })
49
+ .strict();
50
+ const updateInputSchema = z
51
+ .object({
52
+ title: nonblankTextSchema.optional(),
53
+ body: z.string().optional(),
54
+ appendBody: z.string().optional(),
55
+ note: z.string().optional(),
56
+ priority: taskPrioritySchema.optional(),
57
+ needs: taskIdsSchema.optional(),
58
+ addNeeds: taskIdsSchema.optional(),
59
+ dropNeeds: taskIdsSchema.optional(),
60
+ parent: taskMutationIdSchema.nullable().optional(),
61
+ supersedes: taskIdsSchema.optional(),
62
+ addSupersedes: taskIdsSchema.optional(),
63
+ dropSupersedes: taskIdsSchema.optional(),
64
+ relates: taskIdsSchema.optional(),
65
+ addRelates: taskIdsSchema.optional(),
66
+ dropRelates: taskIdsSchema.optional(),
67
+ })
68
+ .strict()
69
+ .superRefine((input, context) => {
70
+ if (input.body !== undefined && input.appendBody !== undefined)
71
+ context.addIssue({ code: "custom", message: "body and appendBody are mutually exclusive" });
72
+ if (Object.values(input).every((value) => value === undefined))
73
+ context.addIssue({ code: "custom", message: "update requires at least one field change" });
74
+ });
75
+ const addRequestSchema = z
76
+ .object({ input: addInputSchema })
77
+ .strict()
78
+ .transform(({ input }) => ({ action: "task.add", input }));
79
+ const addDocumentRequestSchema = z
80
+ .object({ input: z.object({ markdown: z.string(), namespace: namespaceSchema }).strict() })
81
+ .strict()
82
+ .transform(({ input }) => ({ action: "task.addDocument", input }));
83
+ const composeRequestSchema = z
84
+ .object({ markdown: z.string(), namespace: namespaceSchema })
85
+ .strict()
86
+ .transform((request) => ({ action: "task.compose", ...request }));
87
+ const updateRequestSchema = z
88
+ .object({ id: taskMutationIdSchema, input: updateInputSchema })
89
+ .strict()
90
+ .transform((request) => ({ action: "task.update", ...request }));
91
+ const singleOrBatchRequestSchema = (action) => z.union([
92
+ z
93
+ .object({ id: taskMutationIdSchema })
94
+ .strict()
95
+ .transform(({ id }) => ({ action, id })),
96
+ z
97
+ .object({ ids: nonemptyTaskIdsSchema })
98
+ .strict()
99
+ .transform(({ ids }) => ({ action, ids })),
100
+ ]);
101
+ const singleRequestSchema = (action) => z
102
+ .object({ id: taskMutationIdSchema })
103
+ .strict()
104
+ .transform((request) => ({ action, ...request }));
105
+ const terminalRequestSchema = (action) => z.union([
106
+ z
107
+ .object({ id: taskMutationIdSchema, note: z.string().optional() })
108
+ .strict()
109
+ .transform(({ id, note }) => ({ action, id, ...(note === undefined ? {} : { note }) })),
110
+ z
111
+ .object({ ids: nonemptyTaskIdsSchema, note: z.string().optional() })
112
+ .strict()
113
+ .transform(({ ids, note }) => ({ action, ids, ...(note === undefined ? {} : { note }) })),
114
+ ]);
115
+ const taskRequestSchemas = {
116
+ "task.add": addRequestSchema,
117
+ "task.addDocument": addDocumentRequestSchema,
118
+ "task.compose": composeRequestSchema,
119
+ "task.done": terminalRequestSchema("task.done"),
120
+ "task.drop": terminalRequestSchema("task.drop"),
121
+ "task.hold": singleOrBatchRequestSchema("task.hold"),
122
+ "task.resume": singleRequestSchema("task.resume"),
123
+ "task.start": singleOrBatchRequestSchema("task.start"),
124
+ "task.stop": singleRequestSchema("task.stop"),
125
+ "task.update": updateRequestSchema,
126
+ };
127
+ const worldPathSchema = z.string().min(1);
128
+ const taskBodyRequestSchemas = {
129
+ "task.add": z.object({ world: worldPathSchema, request: taskRequestSchemas["task.add"] }).strict(),
130
+ "task.addDocument": z.object({ world: worldPathSchema, request: taskRequestSchemas["task.addDocument"] }).strict(),
131
+ "task.compose": z.object({ world: worldPathSchema, request: taskRequestSchemas["task.compose"] }).strict(),
132
+ "task.done": z.object({ world: worldPathSchema, request: taskRequestSchemas["task.done"] }).strict(),
133
+ "task.drop": z.object({ world: worldPathSchema, request: taskRequestSchemas["task.drop"] }).strict(),
134
+ "task.hold": z.object({ world: worldPathSchema, request: taskRequestSchemas["task.hold"] }).strict(),
135
+ "task.resume": z.object({ world: worldPathSchema, request: taskRequestSchemas["task.resume"] }).strict(),
136
+ "task.start": z.object({ world: worldPathSchema, request: taskRequestSchemas["task.start"] }).strict(),
137
+ "task.stop": z.object({ world: worldPathSchema, request: taskRequestSchemas["task.stop"] }).strict(),
138
+ "task.update": z.object({ world: worldPathSchema, request: taskRequestSchemas["task.update"] }).strict(),
139
+ };
140
+ export const taskMutationServiceSchema = z.object({ action: z.enum(TASK_MUTATION_ACTIONS) }).strict();
141
+ export const forwardedTaskReferenceSchema = z
142
+ .object({ kind: z.literal("served-reference"), action: z.enum(TASK_MUTATION_ACTIONS) })
143
+ .strict();
144
+ function decodeTaskMutationExecutionContext(value) {
145
+ const context = resultRecord(value);
146
+ if (context === null ||
147
+ typeof context.requester !== "string" ||
148
+ context.requester.trim() === "" ||
149
+ !isAbortSignal(context.signal) ||
150
+ !isTaskMutationRequestPort(context.upstream))
151
+ throw new Error("invalid Task execution context");
152
+ return { requester: context.requester, signal: context.signal, upstream: context.upstream };
22
153
  }
23
- export function decodeTaskMutationRequest(action, value) {
24
- switch (action) {
25
- case "task.add": {
26
- const input = mutationObject(value, ["input"], "task.add request");
27
- const raw = mutationObject(input.input, ["title", "namespace", "body", "note", "state", "priority", "needs", "parent", "supersedes", "relates"], "task.add input");
28
- const decoded = addInput(raw);
29
- if (decoded.namespace === undefined)
30
- throw new TypeError("task.add request namespace is required");
31
- return { action, input: { ...decoded, namespace: decoded.namespace } };
32
- }
33
- case "task.addDocument": {
34
- const input = mutationObject(value, ["input"], "task.addDocument request");
35
- const raw = mutationObject(input.input, ["markdown", "namespace"], "task.addDocument input");
36
- const markdown = text(raw.markdown, "markdown");
37
- if (markdown === undefined)
38
- throw new TypeError("markdown is required");
39
- const selected = namespace(raw.namespace);
40
- if (selected === undefined)
41
- throw new TypeError("task.addDocument request namespace is required");
42
- return { action, input: { markdown, namespace: selected } };
43
- }
44
- case "task.compose": {
45
- const input = mutationObject(value, ["markdown", "namespace"], "task.compose request");
46
- const markdown = text(input.markdown, "markdown");
47
- if (markdown === undefined)
48
- throw new TypeError("markdown is required");
49
- const selected = namespace(input.namespace);
50
- if (selected === undefined)
51
- throw new TypeError("task.compose request namespace is required");
52
- return { action, markdown, namespace: selected };
53
- }
54
- case "task.update": {
55
- const request = mutationObject(value, ["id", "input"], "task.update request");
56
- const raw = mutationObject(request.input, [
57
- "title",
58
- "body",
59
- "appendBody",
60
- "note",
61
- "priority",
62
- "needs",
63
- "addNeeds",
64
- "dropNeeds",
65
- "parent",
66
- "supersedes",
67
- "addSupersedes",
68
- "dropSupersedes",
69
- "relates",
70
- "addRelates",
71
- "dropRelates",
72
- ], "task.update input");
73
- return { action, id: taskId(request.id), input: updateInput(raw) };
74
- }
75
- case "task.start":
76
- case "task.stop":
77
- case "task.resume": {
78
- const request = mutationObject(value, ["id"], `${action} request`);
79
- return { action, id: taskId(request.id) };
80
- }
81
- case "task.hold": {
82
- const request = mutationObject(value, ["ids"], "task.hold request");
83
- return { action, ids: taskIds(request.ids, "ids") ?? [] };
84
- }
85
- case "task.done":
86
- case "task.drop": {
87
- const request = mutationObject(value, ["ids", "note"], `${action} request`);
88
- const note = text(request.note, "note");
89
- return { action, ids: taskIds(request.ids, "ids") ?? [], ...(note === undefined ? {} : { note }) };
90
- }
154
+ function isAbortSignal(value) {
155
+ return (value !== null &&
156
+ typeof value === "object" &&
157
+ typeof value.throwIfAborted === "function");
158
+ }
159
+ function isTaskMutationRequestPort(value) {
160
+ const port = resultRecord(value);
161
+ return port !== null && typeof port.task === "function";
162
+ }
163
+ function resultRecord(value) {
164
+ return value !== null && typeof value === "object" && !Array.isArray(value)
165
+ ? value
166
+ : null;
167
+ }
168
+ function decodeTaskBodyRequest(action, value) {
169
+ const parsed = taskBodyRequestSchemas[action].safeParse(value);
170
+ return parsed.success ? parsed.data : null;
171
+ }
172
+ function decodeTaskService(action, value) {
173
+ const parsed = taskMutationServiceSchema.safeParse(value);
174
+ if (!parsed.success || parsed.data.action !== action)
175
+ throw new Error(`malformed stored Task service evidence for ${action}`);
176
+ return parsed.data;
177
+ }
178
+ function decodeTaskReference(action, value) {
179
+ const parsed = forwardedTaskReferenceSchema.safeParse(value);
180
+ if (!parsed.success || parsed.data.action !== action)
181
+ throw new Error(`malformed Task service reference for ${action}`);
182
+ return parsed.data;
183
+ }
184
+ /** Task owns forwarded mutation payload, live result, and durable service evidence. */
185
+ export function taskMutationRequestCommand(action) {
186
+ return {
187
+ action,
188
+ encodeRequest: (input) => {
189
+ const { action: _action, ...request } = input.request;
190
+ return { world: input.world, request };
191
+ },
192
+ decodeRequest: (value) => decodeTaskBodyRequest(action, value),
193
+ encodeResult: (result) => result,
194
+ decodeResult: (result) => {
195
+ if (!isTaskMutationExecutionResult(result)) {
196
+ throw new Error(`transport integrity: Task ${action} returned an invalid live result`);
197
+ }
198
+ return result;
199
+ },
200
+ encodeService: (service) => decodeTaskService(action, service),
201
+ decodeService: (service) => decodeTaskService(action, service),
202
+ projectService: (service) => decodeTaskReference(action, { kind: "served-reference", action: service.action }),
203
+ decodeReference: (reference) => decodeTaskReference(action, reference),
204
+ isPermitted: (allowed) => allowed.includes(action),
205
+ decodeExecutionContext: decodeTaskMutationExecutionContext,
206
+ execute: async (request, context) => {
207
+ const world = await World.prove(request.world);
208
+ return {
209
+ result: await context.upstream.task({
210
+ world,
211
+ request: request.request,
212
+ requester: context.requester,
213
+ signal: context.signal,
214
+ }),
215
+ service: { action: request.request.action },
216
+ };
217
+ },
218
+ };
219
+ }
220
+ export function taskMutationRequestCommands() {
221
+ return {
222
+ "task.add": eraseRequestCommand(taskMutationRequestCommand("task.add")),
223
+ "task.addDocument": eraseRequestCommand(taskMutationRequestCommand("task.addDocument")),
224
+ "task.compose": eraseRequestCommand(taskMutationRequestCommand("task.compose")),
225
+ "task.done": eraseRequestCommand(taskMutationRequestCommand("task.done")),
226
+ "task.drop": eraseRequestCommand(taskMutationRequestCommand("task.drop")),
227
+ "task.hold": eraseRequestCommand(taskMutationRequestCommand("task.hold")),
228
+ "task.resume": eraseRequestCommand(taskMutationRequestCommand("task.resume")),
229
+ "task.start": eraseRequestCommand(taskMutationRequestCommand("task.start")),
230
+ "task.stop": eraseRequestCommand(taskMutationRequestCommand("task.stop")),
231
+ "task.update": eraseRequestCommand(taskMutationRequestCommand("task.update")),
232
+ };
233
+ }
234
+ function taskResultForRequest(request, value) {
235
+ const schema = request.action === "task.compose"
236
+ ? taskCompositionResultSchema
237
+ : request.action === "task.update"
238
+ ? taskUpdateResultSchema
239
+ : "ids" in request
240
+ ? taskBatchResultSchema
241
+ : taskMutationResultSchema;
242
+ return schema.safeParse(value).success;
243
+ }
244
+ export async function requestForwardedTask(input) {
245
+ const response = await requestBodyCommand({
246
+ directory: input.directory,
247
+ ...(input.id === undefined ? {} : { id: input.id }),
248
+ command: taskMutationRequestCommand(input.request.action),
249
+ value: { world: input.world, request: input.request },
250
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
251
+ });
252
+ if (response.kind === "reference")
253
+ return response.reference;
254
+ if (!taskResultForRequest(input.request, response.result)) {
255
+ throw new Error(`transport integrity: request Task ${input.request.action} returned an invalid live result`);
91
256
  }
257
+ return response.result;
258
+ }
259
+ export function decodeTaskMutationRequest(action, value) {
260
+ const parsed = taskRequestSchemas[action].safeParse(value);
261
+ if (!parsed.success)
262
+ throw new TypeError(`invalid ${action} request`);
263
+ return parsed.data;
264
+ }
265
+ function addTaskOptions(input, actor, signal) {
266
+ const { body, note, state, priority, needs, parent, supersedes, relates, ...required } = input;
267
+ return {
268
+ ...required,
269
+ actor,
270
+ ...(body === undefined ? {} : { body }),
271
+ ...(note === undefined ? {} : { note }),
272
+ ...(state === undefined ? {} : { state }),
273
+ ...(priority === undefined ? {} : { priority }),
274
+ ...(needs === undefined ? {} : { needs }),
275
+ ...(parent === undefined ? {} : { parent }),
276
+ ...(supersedes === undefined ? {} : { supersedes }),
277
+ ...(relates === undefined ? {} : { relates }),
278
+ ...(signal === undefined ? {} : { signal }),
279
+ };
280
+ }
281
+ function updateTaskOptions(input, signal) {
282
+ const { title, body, appendBody, note, priority, needs, addNeeds, dropNeeds, parent, supersedes, addSupersedes, dropSupersedes, relates, addRelates, dropRelates, } = input;
283
+ return {
284
+ ...(title === undefined ? {} : { title }),
285
+ ...(body === undefined ? {} : { body }),
286
+ ...(appendBody === undefined ? {} : { appendBody }),
287
+ ...(note === undefined ? {} : { note }),
288
+ ...(priority === undefined ? {} : { priority }),
289
+ ...(needs === undefined ? {} : { needs }),
290
+ ...(addNeeds === undefined ? {} : { addNeeds }),
291
+ ...(dropNeeds === undefined ? {} : { dropNeeds }),
292
+ ...(parent === undefined ? {} : { parent }),
293
+ ...(supersedes === undefined ? {} : { supersedes }),
294
+ ...(addSupersedes === undefined ? {} : { addSupersedes }),
295
+ ...(dropSupersedes === undefined ? {} : { dropSupersedes }),
296
+ ...(relates === undefined ? {} : { relates }),
297
+ ...(addRelates === undefined ? {} : { addRelates }),
298
+ ...(dropRelates === undefined ? {} : { dropRelates }),
299
+ ...(signal === undefined ? {} : { signal }),
300
+ };
92
301
  }
93
302
  export async function executeTaskMutation(input) {
94
- const { Tasks } = await import("./index.js");
95
- const tasks = Tasks.of(input.world);
96
- const { request, signal } = input;
303
+ const { world, request, signal } = input;
97
304
  const withSignal = signal === undefined ? {} : { signal };
98
305
  switch (request.action) {
99
306
  case "task.add":
100
- return await tasks.add({ ...request.input, actor: input.requester, ...withSignal });
307
+ return await addTask(world, addTaskOptions(request.input, input.requester, signal));
101
308
  case "task.addDocument":
102
- return await tasks.addDocument({ ...request.input, actor: input.requester, ...withSignal });
309
+ return await addTaskDocument(world, { ...request.input, actor: input.requester, ...withSignal });
103
310
  case "task.compose":
104
- return await tasks.compose({
311
+ return await composeTasks({
312
+ world,
105
313
  markdown: request.markdown,
106
- namespace: request.namespace,
314
+ defaultNamespace: request.namespace,
107
315
  actor: input.requester,
316
+ planOnly: false,
108
317
  ...withSignal,
109
318
  });
110
319
  case "task.update":
111
- return await tasks.task({ id: request.id }).update({ ...request.input, ...withSignal });
320
+ return await updateTask(world, request.id, updateTaskOptions(request.input, signal));
112
321
  case "task.start":
113
- return await tasks.task({ id: request.id }).start(withSignal);
322
+ if ("ids" in request)
323
+ return await batchTasks(world, "start", request.ids, signal);
324
+ return await lifecycleTask(world, request.id, "start", signal);
114
325
  case "task.stop":
115
- return await tasks.task({ id: request.id }).stop(withSignal);
326
+ return await lifecycleTask(world, request.id, "stop", signal);
116
327
  case "task.resume":
117
- return await tasks.task({ id: request.id }).resume(withSignal);
328
+ return await lifecycleTask(world, request.id, "resume", signal);
118
329
  case "task.hold":
119
- return await tasks.batch({ verb: "hold", ids: request.ids, ...withSignal });
330
+ if ("ids" in request)
331
+ return await batchTasks(world, "hold", request.ids, signal);
332
+ return await lifecycleTask(world, request.id, "hold", signal);
120
333
  case "task.done":
121
- return await tasks.batch({
122
- verb: "done",
123
- ids: request.ids,
124
- ...withSignal,
125
- ...(request.note === undefined ? {} : { note: request.note }),
126
- });
334
+ if ("ids" in request)
335
+ return await batchTasks(world, "done", request.ids, signal, request.note);
336
+ return await lifecycleTask(world, request.id, "done", signal, request.note);
127
337
  case "task.drop":
128
- return await tasks.batch({
129
- verb: "drop",
130
- ids: request.ids,
131
- ...withSignal,
132
- ...(request.note === undefined ? {} : { note: request.note }),
133
- });
338
+ if ("ids" in request)
339
+ return await batchTasks(world, "drop", request.ids, signal, request.note);
340
+ return await lifecycleTask(world, request.id, "drop", signal, request.note);
134
341
  }
135
342
  }
@@ -119,7 +119,7 @@ export declare function addTask(world: WorldRoot, input: AddTaskInput): Promise<
119
119
  export declare function addTaskDocument(world: WorldRoot, input: AddTaskDocumentInput): Promise<TaskMutationResult>;
120
120
  export declare function updateTask(world: WorldRoot, id: TaskId, input: UpdateTaskInput): Promise<TaskUpdateResult>;
121
121
  export declare function lifecycleTask(world: WorldRoot, id: TaskId, verb: TaskLifecycleVerb, signal?: AbortSignal, note?: string): Promise<TaskMutationResult>;
122
- export declare function batchTasks(world: WorldRoot, verb: "done" | "drop" | "hold", ids: readonly TaskId[], signal?: AbortSignal, note?: string): Promise<TaskBatchResult>;
122
+ export declare function batchTasks(world: WorldRoot, verb: "start" | "done" | "drop" | "hold", ids: readonly TaskId[], signal?: AbortSignal, note?: string): Promise<TaskBatchResult>;
123
123
  export declare function settleTask(world: WorldRoot, id: TaskId): Promise<SettledTaskResult>;
124
124
  export declare function listTasks(world: WorldRoot, namespace: readonly string[] | undefined, selection: "active" | "closed" | "all", scope?: "namespace" | "world", limit?: number): Promise<TaskOutcome<TaskPage<TaskRow>>>;
125
125
  export declare function readTaskDetails(world: WorldRoot, ids: readonly TaskId[]): Promise<TaskOutcome<readonly TaskDetailFacts[]>>;
@@ -136,7 +136,4 @@ export declare function queryTasks(input: Readonly<{
136
136
  sort?: TaskQuerySort;
137
137
  limit?: number;
138
138
  }>): Promise<TaskOutcome<TaskPage<TaskQueryRow>>>;
139
- /** Internal composite observation from one complete Task board read. */
140
139
  export declare function observeTaskBoard(world: WorldRoot): Promise<TaskBoardObservation>;
141
- /** Internal identity catalog from one complete Task board read. */
142
- export declare function observeTaskCatalogRows(world: WorldRoot): Promise<readonly TaskRow[]>;
@@ -304,12 +304,6 @@ export async function queryTasks(input) {
304
304
  }),
305
305
  };
306
306
  }
307
- /** Internal composite observation from one complete Task board read. */
308
307
  export async function observeTaskBoard(world) {
309
308
  return projectTaskBoardObservation((await readBoard(world)).board);
310
309
  }
311
- /** Internal identity catalog from one complete Task board read. */
312
- export async function observeTaskCatalogRows(world) {
313
- const board = (await readBoard(world)).board;
314
- return projectRows(board, createTaskRelations(board), null, "all");
315
- }
@@ -0,0 +1,2 @@
1
+ export { TASK_RELATION_PREDICATE_FIELDS, TaskAuthorityCorruptionError, Tasks } from "./index.js";
2
+ export type * from "./index.js";
@@ -0,0 +1 @@
1
+ export { TASK_RELATION_PREDICATE_FIELDS, TaskAuthorityCorruptionError, Tasks } from "./index.js";
@@ -1,6 +1,7 @@
1
1
  import { type ContractId } from "./core/facts/types.js";
2
2
  import type { GitRepository } from "./git/process.js";
3
3
  export declare const CONTRACT_PLACES: readonly string[];
4
+ declare const placeAuthorityFence: unique symbol;
4
5
  export type Place = string & {
5
6
  readonly [placeBrand]: "Place";
6
7
  };
@@ -24,6 +25,9 @@ export type ManagedWorktreeAppointment = Readonly<{
24
25
  kind: "failed";
25
26
  diagnostic: string;
26
27
  }>;
28
+ export type PlaceAuthorityFence = Readonly<{
29
+ readonly [placeAuthorityFence]: true;
30
+ }>;
27
31
  export declare function placeRegisterPath(repository: GitRepository): string;
28
32
  export declare function place(value: string): Place;
29
33
  export declare function nextPlace(current?: Place): Place;
@@ -32,9 +36,10 @@ export declare function decodePlaceRegister(path: string, bytes: string): PlaceR
32
36
  export declare function emptyPlaceRegister(): PlaceRegister;
33
37
  export declare function readPlaceRegister(repository: GitRepository): Promise<PlaceRegister>;
34
38
  export declare function appointmentFor(register: PlaceRegister, contract: ContractId): PlaceAppointment | undefined;
39
+ export declare function withPlaceAuthorityFence<T>(repository: GitRepository, action: (fence: PlaceAuthorityFence) => T | Promise<T>): Promise<T>;
35
40
  export declare function appointManagedWorktrees(repository: GitRepository, contracts: readonly ContractId[]): Promise<PlaceRegister>;
36
- export declare function releaseManagedWorktrees(repository: GitRepository, contracts: readonly ContractId[]): Promise<void>;
41
+ export declare function releaseManagedWorktrees(repository: GitRepository, contracts: readonly ContractId[], fence?: PlaceAuthorityFence): Promise<void>;
37
42
  /** Remove only an already-empty Place authority. */
38
- export declare function nukeEmptyPlaceAuthority(repository: GitRepository): Promise<void>;
43
+ export declare function nukeEmptyPlaceAuthority(repository: GitRepository, fence?: PlaceAuthorityFence): Promise<void>;
39
44
  export declare function readManagedWorktreeAppointment(repository: GitRepository, contract: ContractId, register?: PlaceRegister): Promise<ManagedWorktreeAppointment>;
40
45
  export {};
@@ -184,6 +184,7 @@ export const CONTRACT_PLACES = Object.freeze([
184
184
  const PLACE_INDEX = new Map(CONTRACT_PLACES.map((base, index) => [base, index]));
185
185
  const PLACE_VERSION = 1;
186
186
  const GENERATION_SUFFIX = /^(?:[2-9]|[1-9][0-9]+)$/u;
187
+ const placeAuthorityFence = Symbol("place-authority-fence");
187
188
  function placePaths(repository) {
188
189
  return {
189
190
  authority: join(repository.commonDirectory, "keiyaku", "places.json"),
@@ -305,29 +306,38 @@ export async function readPlaceRegister(repository) {
305
306
  export function appointmentFor(register, contract) {
306
307
  return register.byContract.get(contract);
307
308
  }
308
- async function mutatePlaceRegister(repository, mutate) {
309
+ export async function withPlaceAuthorityFence(repository, action) {
309
310
  const location = placePaths(repository);
310
311
  const held = await acquireSqliteTransactionLock({ path: location.lock, mode: "immediate" });
311
312
  try {
312
- const current = await readPlaceRegister(repository);
313
- const next = mutate(current);
314
- const bytes = canonicalPlaceRegister(next);
315
- try {
316
- if ((await readFile(location.authority, "utf8")) === bytes)
317
- return next;
318
- }
319
- catch (error) {
320
- if (error.code !== "ENOENT")
321
- throw error;
322
- }
323
- await mkdir(dirname(location.authority), { recursive: true });
324
- await replaceFileDurably(location.authority, bytes);
325
- return next;
313
+ return await action({ [placeAuthorityFence]: true });
326
314
  }
327
315
  finally {
328
316
  held.close();
329
317
  }
330
318
  }
319
+ async function mutatePlaceRegisterInFence(repository, mutate) {
320
+ const location = placePaths(repository);
321
+ const current = await readPlaceRegister(repository);
322
+ const next = mutate(current);
323
+ const bytes = canonicalPlaceRegister(next);
324
+ try {
325
+ if ((await readFile(location.authority, "utf8")) === bytes)
326
+ return next;
327
+ }
328
+ catch (error) {
329
+ if (error.code !== "ENOENT")
330
+ throw error;
331
+ }
332
+ await mkdir(dirname(location.authority), { recursive: true });
333
+ await replaceFileDurably(location.authority, bytes);
334
+ return next;
335
+ }
336
+ async function mutatePlaceRegister(repository, mutate, fence) {
337
+ if (fence !== undefined)
338
+ return await mutatePlaceRegisterInFence(repository, mutate);
339
+ return await withPlaceAuthorityFence(repository, async () => await mutatePlaceRegisterInFence(repository, mutate));
340
+ }
331
341
  function withAppointments(register, contracts) {
332
342
  const additions = [];
333
343
  const occupied = new Set(register.byPlace.keys());
@@ -360,14 +370,14 @@ function firstUnoccupiedPlace(contract, occupied) {
360
370
  export async function appointManagedWorktrees(repository, contracts) {
361
371
  return await mutatePlaceRegister(repository, (current) => withAppointments(current, contracts));
362
372
  }
363
- export async function releaseManagedWorktrees(repository, contracts) {
373
+ export async function releaseManagedWorktrees(repository, contracts, fence) {
364
374
  if (contracts.length === 0)
365
375
  return;
366
376
  const drop = new Set(contracts);
367
377
  await mutatePlaceRegister(repository, (current) => {
368
378
  const kept = current.appointments.filter((appointment) => !drop.has(appointment.contract));
369
379
  return kept.length === current.appointments.length ? current : indexedRegister(kept);
370
- });
380
+ }, fence);
371
381
  }
372
382
  async function regularFile(path) {
373
383
  const value = await lstat(path).catch((error) => error.code === "ENOENT" ? undefined : Promise.reject(error));
@@ -378,22 +388,22 @@ async function regularFile(path) {
378
388
  return true;
379
389
  }
380
390
  /** Remove only an already-empty Place authority. */
381
- export async function nukeEmptyPlaceAuthority(repository) {
391
+ export async function nukeEmptyPlaceAuthority(repository, fence) {
382
392
  const location = placePaths(repository);
383
393
  const [authorityPresent, lockPresent] = [await regularFile(location.authority), await regularFile(location.lock)];
384
394
  if (!authorityPresent && !lockPresent)
385
395
  return;
386
- const held = await acquireSqliteTransactionLock({ path: location.lock, mode: "immediate" });
387
- try {
396
+ const nuke = async () => {
388
397
  const current = await readPlaceRegister(repository);
389
398
  if (current.appointments.length !== 0)
390
399
  throw new Error("Place authority still has managed worktree appointments");
391
400
  if (await regularFile(location.authority))
392
401
  await unlink(location.authority);
393
- }
394
- finally {
395
- held.close();
396
- }
402
+ };
403
+ if (fence !== undefined)
404
+ await nuke();
405
+ else
406
+ await withPlaceAuthorityFence(repository, nuke);
397
407
  }
398
408
  export async function readManagedWorktreeAppointment(repository, contract, register) {
399
409
  try {
@@ -22,5 +22,6 @@ export declare const World: Readonly<{
22
22
  resolve(input: WorldInput): Promise<WorldResolution>;
23
23
  locate(input: WorldInput): Promise<WorldRoot | null>;
24
24
  at(input: string): Promise<WorldRoot>;
25
+ prove(input: string): Promise<WorldRoot>;
25
26
  }>;
26
27
  export {};
@@ -113,6 +113,14 @@ async function exact(input) {
113
113
  await ensureMarker(root);
114
114
  return brand(root);
115
115
  }
116
+ async function proved(input) {
117
+ const resolution = await resolved({ cwd: input, repositoryRoot: input });
118
+ const root = resolution.root;
119
+ if (root === null || input !== root) {
120
+ throw new WorldError("invalid-world", "world path must be its canonical physical directory coordinate");
121
+ }
122
+ return root;
123
+ }
116
124
  async function resolved(input) {
117
125
  const values = await inputValues(input, "world location");
118
126
  const root = values.repositoryRoot === undefined ? await locateMarker(values.cwd) : brand(values.repositoryRoot);
@@ -147,4 +155,7 @@ export const World = Object.freeze({
147
155
  at(input) {
148
156
  return exact(input);
149
157
  },
158
+ prove(input) {
159
+ return proved(input);
160
+ },
150
161
  });