@nestos/kanban 0.5.1 → 0.5.2

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 (147) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +73 -29
  3. package/dist/{activity-C6xDM-cg.d.cts → activity-BbC4sgKL.d.cts} +28 -2
  4. package/dist/{activity-Dw8DOw0L.d.ts → activity-Czlan5xW.d.ts} +28 -2
  5. package/dist/activity-module-DaO1Rry0.d.cts +22 -0
  6. package/dist/activity-module-T22wliZu.d.ts +22 -0
  7. package/dist/activity-ports-Dqxa86zT.d.cts +12 -0
  8. package/dist/activity-ports-cxfrelaL.d.ts +12 -0
  9. package/dist/activity.cjs +10 -282
  10. package/dist/activity.cjs.map +1 -1
  11. package/dist/activity.d.cts +8 -30
  12. package/dist/activity.d.ts +8 -30
  13. package/dist/activity.js +9 -281
  14. package/dist/activity.js.map +1 -1
  15. package/dist/{chunk-O4OGAAV4.js → chunk-3JYLJPE5.js} +29 -5
  16. package/dist/chunk-3JYLJPE5.js.map +1 -0
  17. package/dist/{chunk-ATUFFNLV.js → chunk-3LEIHB7Y.js} +5 -5
  18. package/dist/chunk-4XF6LQSU.cjs.map +1 -1
  19. package/dist/chunk-5Q7WB63A.cjs +282 -0
  20. package/dist/chunk-5Q7WB63A.cjs.map +1 -0
  21. package/dist/{chunk-PSKQAFDF.cjs → chunk-7KDOTBRX.cjs} +1 -1
  22. package/dist/chunk-7KDOTBRX.cjs.map +1 -0
  23. package/dist/{chunk-5DLZWW5Y.js → chunk-AHHEGTUA.js} +3 -3
  24. package/dist/chunk-AOHQCX7K.cjs +95 -0
  25. package/dist/chunk-AOHQCX7K.cjs.map +1 -0
  26. package/dist/{chunk-IUI5CI2S.cjs → chunk-DINLR6E6.cjs} +96 -92
  27. package/dist/chunk-DINLR6E6.cjs.map +1 -0
  28. package/dist/{chunk-L44TM4EE.cjs → chunk-EYTVC2H5.cjs} +568 -454
  29. package/dist/chunk-EYTVC2H5.cjs.map +1 -0
  30. package/dist/chunk-FEE2YBZ7.js +1972 -0
  31. package/dist/chunk-FEE2YBZ7.js.map +1 -0
  32. package/dist/chunk-FNO5K3A7.js +282 -0
  33. package/dist/chunk-FNO5K3A7.js.map +1 -0
  34. package/dist/chunk-IHHDXZPZ.js +382 -0
  35. package/dist/chunk-IHHDXZPZ.js.map +1 -0
  36. package/dist/chunk-J3EAIVA3.js +280 -0
  37. package/dist/chunk-J3EAIVA3.js.map +1 -0
  38. package/dist/{chunk-DFTOTBGF.js → chunk-KH3WF3K4.js} +20 -16
  39. package/dist/chunk-KH3WF3K4.js.map +1 -0
  40. package/dist/{chunk-TB2RZGDV.js → chunk-M35X5PHO.js} +14 -1
  41. package/dist/chunk-M35X5PHO.js.map +1 -0
  42. package/dist/chunk-QRGZD2JU.cjs +1972 -0
  43. package/dist/chunk-QRGZD2JU.cjs.map +1 -0
  44. package/dist/{chunk-4KF7KUAI.cjs → chunk-RBOER6EW.cjs} +6 -6
  45. package/dist/chunk-RBOER6EW.cjs.map +1 -0
  46. package/dist/{chunk-6MN3H3YF.cjs → chunk-RSH6QSEC.cjs} +16 -3
  47. package/dist/chunk-RSH6QSEC.cjs.map +1 -0
  48. package/dist/{chunk-T7P5EIC4.cjs → chunk-VMRDXOLX.cjs} +33 -33
  49. package/dist/chunk-VMRDXOLX.cjs.map +1 -0
  50. package/dist/{chunk-5XKOCKY2.js → chunk-X4UHZX6F.js} +1 -1
  51. package/dist/chunk-XBSGUVCY.cjs +382 -0
  52. package/dist/chunk-XBSGUVCY.cjs.map +1 -0
  53. package/dist/{chunk-OHQTKGQN.js → chunk-XKDZHAUE.js} +242 -128
  54. package/dist/chunk-XKDZHAUE.js.map +1 -0
  55. package/dist/chunk-ZUZGAO6G.cjs +280 -0
  56. package/dist/chunk-ZUZGAO6G.cjs.map +1 -0
  57. package/dist/{content-CAIp1njn.d.cts → content-C5aJZFlw.d.cts} +1 -1
  58. package/dist/{content-CGMJdyH_.d.ts → content-hLH5BNud.d.ts} +1 -1
  59. package/dist/contracts/index.cjs +13 -3
  60. package/dist/contracts/index.cjs.map +1 -1
  61. package/dist/contracts/index.d.cts +5 -4
  62. package/dist/contracts/index.d.ts +5 -4
  63. package/dist/contracts/index.js +14 -4
  64. package/dist/{discovery-DxpirOz3.d.cts → discovery-9E-LfwIw.d.cts} +1 -1
  65. package/dist/{discovery-gSqWpuRm.d.ts → discovery-D60yrjej.d.ts} +1 -1
  66. package/dist/discovery.cjs +63 -65
  67. package/dist/discovery.cjs.map +1 -1
  68. package/dist/discovery.d.cts +4 -4
  69. package/dist/discovery.d.ts +4 -4
  70. package/dist/discovery.js +5 -7
  71. package/dist/discovery.js.map +1 -1
  72. package/dist/execution-bridge-fpCnH6z5.d.cts +52 -0
  73. package/dist/execution-bridge-rk0uVy_n.d.ts +52 -0
  74. package/dist/execution.cjs +6 -356
  75. package/dist/execution.cjs.map +1 -1
  76. package/dist/execution.d.cts +4 -51
  77. package/dist/execution.d.ts +4 -51
  78. package/dist/execution.js +9 -359
  79. package/dist/execution.js.map +1 -1
  80. package/dist/index.cjs +10 -12
  81. package/dist/index.cjs.map +1 -1
  82. package/dist/index.d.cts +9 -16
  83. package/dist/index.d.ts +9 -16
  84. package/dist/index.js +10 -12
  85. package/dist/index.js.map +1 -1
  86. package/dist/{module-CWNK2BMj.d.ts → module-CYfE7YhM.d.ts} +2 -2
  87. package/dist/{module-BbvwdCDF.d.cts → module-Ca9L3qCn.d.cts} +2 -2
  88. package/dist/{planning-CcFZYdIL.d.cts → planning-CHrOqQ6b.d.cts} +45 -10
  89. package/dist/{planning-CcFZYdIL.d.ts → planning-CHrOqQ6b.d.ts} +45 -10
  90. package/dist/{ports-Bn6ro2FB.d.ts → ports-B0McVg7B.d.ts} +4 -3
  91. package/dist/ports-BeGO1VRI.d.cts +49 -0
  92. package/dist/{ports-CaL6geqR.d.cts → ports-GQy0x-RI.d.cts} +4 -3
  93. package/dist/ports-ykGiNUSx.d.ts +49 -0
  94. package/dist/runtime/index.cjs +205 -0
  95. package/dist/runtime/index.cjs.map +1 -0
  96. package/dist/runtime/index.d.cts +75 -0
  97. package/dist/runtime/index.d.ts +75 -0
  98. package/dist/runtime/index.js +205 -0
  99. package/dist/runtime/index.js.map +1 -0
  100. package/dist/runtime/integration.cjs +423 -0
  101. package/dist/runtime/integration.cjs.map +1 -0
  102. package/dist/runtime/integration.d.cts +658 -0
  103. package/dist/runtime/integration.d.ts +658 -0
  104. package/dist/runtime/integration.js +423 -0
  105. package/dist/runtime/integration.js.map +1 -0
  106. package/dist/storage.cjs +9 -277
  107. package/dist/storage.cjs.map +1 -1
  108. package/dist/storage.d.cts +6 -6
  109. package/dist/storage.d.ts +6 -6
  110. package/dist/storage.js +11 -279
  111. package/dist/storage.js.map +1 -1
  112. package/dist/{store-q63eSMoT.d.cts → store-DPwHzGiz.d.cts} +2 -2
  113. package/dist/{store-Ct0gOAAg.d.ts → store-DgdrPCV9.d.ts} +2 -2
  114. package/dist/task-projection-CSorlMws.d.ts +11 -0
  115. package/dist/task-projection-DKKB_ueb.d.cts +11 -0
  116. package/examples/standalone.ts +22 -0
  117. package/package.json +117 -87
  118. package/dist/chunk-2YFPV5GF.cjs +0 -16
  119. package/dist/chunk-2YFPV5GF.cjs.map +0 -1
  120. package/dist/chunk-4KF7KUAI.cjs.map +0 -1
  121. package/dist/chunk-6MN3H3YF.cjs.map +0 -1
  122. package/dist/chunk-DFTOTBGF.js.map +0 -1
  123. package/dist/chunk-G6FCDLDF.js +0 -16
  124. package/dist/chunk-G6FCDLDF.js.map +0 -1
  125. package/dist/chunk-IUI5CI2S.cjs.map +0 -1
  126. package/dist/chunk-KYNYPKKH.cjs +0 -71
  127. package/dist/chunk-KYNYPKKH.cjs.map +0 -1
  128. package/dist/chunk-L44TM4EE.cjs.map +0 -1
  129. package/dist/chunk-LSE6JG5I.cjs +0 -20
  130. package/dist/chunk-LSE6JG5I.cjs.map +0 -1
  131. package/dist/chunk-O4OGAAV4.js.map +0 -1
  132. package/dist/chunk-OHQTKGQN.js.map +0 -1
  133. package/dist/chunk-PSKQAFDF.cjs.map +0 -1
  134. package/dist/chunk-QTMTBEBA.js +0 -20
  135. package/dist/chunk-QTMTBEBA.js.map +0 -1
  136. package/dist/chunk-T7P5EIC4.cjs.map +0 -1
  137. package/dist/chunk-TB2RZGDV.js.map +0 -1
  138. package/dist/ui/index.cjs +0 -1961
  139. package/dist/ui/index.cjs.map +0 -1
  140. package/dist/ui/index.d.cts +0 -209
  141. package/dist/ui/index.d.ts +0 -209
  142. package/dist/ui/index.js +0 -1961
  143. package/dist/ui/index.js.map +0 -1
  144. package/dist/ui/kanban.css +0 -194
  145. /package/dist/{chunk-ATUFFNLV.js.map → chunk-3LEIHB7Y.js.map} +0 -0
  146. /package/dist/{chunk-5DLZWW5Y.js.map → chunk-AHHEGTUA.js.map} +0 -0
  147. /package/dist/{chunk-5XKOCKY2.js.map → chunk-X4UHZX6F.js.map} +0 -0
package/dist/execution.js CHANGED
@@ -1,363 +1,13 @@
1
1
  import {
2
- executionObservationChanged
3
- } from "./chunk-RQP77ZTY.js";
4
- import {
5
- KanbanError
6
- } from "./chunk-G6FCDLDF.js";
7
- import {
8
- taskDue
9
- } from "./chunk-O4OGAAV4.js";
10
- import {
11
- randomUUID,
12
- sha256HexSync
13
- } from "./chunk-TB2RZGDV.js";
14
- import {
15
- boardAllowsStart
16
- } from "./chunk-QTMTBEBA.js";
17
-
18
- // src/execution-bridge.ts
19
- async function createKanbanExecutionBridge(options) {
20
- const path = `${options.directory}/executions.seq`;
21
- try {
22
- await options.fs.driver.createFile({ parentPath: options.directory, name: "executions.seq", type: "seqfile", recursive: true });
23
- } catch (error) {
24
- if (error?.code !== "EEXIST") throw error;
25
- }
26
- if (!options.fs.meta.seq?.transaction) throw new KanbanError("TRANSACTION_REQUIRED");
27
- let stopped = false, again = false, pending;
28
- const writes = /* @__PURE__ */ new Set();
29
- const failures = /* @__PURE__ */ new Map();
30
- const track = (operation) => {
31
- const tracked = operation.finally(() => {
32
- writes.delete(tracked);
33
- });
34
- writes.add(tracked);
35
- return tracked;
36
- };
37
- const active = () => !stopped;
38
- const scan = async () => {
39
- if (stopped) return;
40
- if (pending) {
41
- again = true;
42
- return pending;
43
- }
44
- const started = Date.now();
45
- pending = runBridge(options, path, active, track, failures).catch((error) => {
46
- try {
47
- options.log({ operationId: randomUUID(), stage: "bridge.scan", result: "failure", code: safeCode(error), durationMs: Date.now() - started });
48
- } catch {
49
- }
50
- throw error;
51
- }).finally(() => {
52
- pending = void 0;
53
- if (again && !stopped) {
54
- again = false;
55
- void scan().catch(() => {
56
- });
57
- }
58
- });
59
- return pending;
60
- };
61
- const unsubscribe = options.subscribe?.(() => {
62
- void scan().catch(() => {
63
- });
64
- });
65
- return { scan, tick: scan, async quiesce() {
66
- stopped = true;
67
- unsubscribe?.();
68
- if (!pending) return;
69
- let timer;
70
- try {
71
- await Promise.race([pending, new Promise((resolve) => {
72
- timer = setTimeout(resolve, options.shutdownTimeoutMs ?? 5e3);
73
- })]);
74
- } finally {
75
- if (timer) clearTimeout(timer);
76
- await Promise.allSettled([...writes]);
77
- }
78
- } };
79
- }
80
- async function runBridge(options, path, active, track, failures) {
81
- const workflow = await options.service.workflow(options.boardId);
82
- const live = new Set(Object.values(workflow.dispatches).filter((dispatch) => ["prepared", "running", "unknown"].includes(dispatch.outcome)).map((dispatch) => dispatch.id));
83
- for (const id of failures.keys()) if (!live.has(id)) failures.delete(id);
84
- for (const dispatch of Object.values(workflow.dispatches)) {
85
- if (!active()) return;
86
- if (live.has(dispatch.id) && (failures.get(dispatch.id)?.retryAt ?? 0) <= Date.now()) await runDispatch(options, path, dispatch, active, track, failures);
87
- }
88
- const automatic = typeof options.autoDispatch === "function" ? await options.autoDispatch() : options.autoDispatch;
89
- if (!automatic || !active()) return;
90
- for (const id of (await options.service.workflow(options.boardId)).ready) {
91
- if (!active()) return;
92
- const { item } = await options.service.item(options.boardId, id), owner = item.eligiblePrincipals?.[0];
93
- if (!owner || !taskDue(item, (options.now ?? Date.now)())) continue;
94
- const worker = await options.worker(owner), board = await worker.read(options.boardId);
95
- if (!active()) return;
96
- if (!boardAllowsStart(board, item)) continue;
97
- try {
98
- const dispatchId = randomUUID();
99
- const estimate = await options.estimate?.(item, owner);
100
- if (!active()) return;
101
- await worker.execute(board.id, { operationId: "dispatch:" + dispatchId, expectedRevision: board.revision, command: { type: "prepare-dispatch", itemId: id, dispatchId, estimate, trigger: "automatic" } });
102
- const dispatch = (await options.service.workflow(board.id)).dispatches[dispatchId];
103
- if (active()) await runDispatch(options, path, dispatch, active, track, failures);
104
- } catch (error) {
105
- if (!["WIP_LIMIT", "ASSIGNEE_WIP_LIMIT", "READY_LIMIT", "REVISION_CONFLICT"].includes(safeCode(error))) throw error;
106
- }
107
- }
108
- }
109
- async function runDispatch(options, path, dispatch, active, track, failures) {
110
- const start = Date.now();
111
- let stage = "inspect", result;
112
- const previousFailure = failures.has(dispatch.id);
113
- try {
114
- let record = await loadRecord(options, path, dispatch);
115
- if (!active()) return;
116
- if ((!record || record.phase === "prepared") && (await options.service.read(options.boardId)).executionControl?.paused) return;
117
- if (!record) {
118
- stage = "prepare";
119
- await options.authorize(stage, dispatch);
120
- if (!active()) return;
121
- const item = (await options.service.item(options.boardId, dispatch.itemId)).item;
122
- if (!active()) return;
123
- const request = await prepareRequest(options, path, dispatch, item);
124
- if (!active()) return;
125
- record = await track(savePrepared(options, path, dispatch, request));
126
- }
127
- if (!active()) return;
128
- const submit = record.phase === "prepared" && await track(claimSubmission(options, path, dispatch));
129
- if (!active()) return;
130
- stage = submit ? "start" : "inspect";
131
- await options.authorize(stage, dispatch);
132
- if (!active()) return;
133
- result = await (submit ? options.executor.start(record.request) : options.executor.inspect(record.request));
134
- failures.delete(dispatch.id);
135
- } catch (error) {
136
- const notStarted = error?.executionOutcome === "not-started" && error.retryable === true;
137
- if (notStarted && stage === "start") await track(releaseUnstartedSubmission(options, path, dispatch));
138
- result = { outcome: stage === "prepare" && !notStarted ? "failed" : "unknown", code: safeCode(error) };
139
- if (stage === "inspect" || notStarted) {
140
- const previous = failures.get(dispatch.id), count = previous && previous.code === result.code ? previous.count + 1 : 1;
141
- failures.set(dispatch.id, { code: result.code, count, retryAt: Date.now() + Math.min(6e4, 1e3 * 2 ** Math.min(count - 1, 6)) });
142
- }
143
- }
144
- if (stage !== "inspect" || executionObservationChanged(dispatch, result) || previousFailure && !failures.has(dispatch.id))
145
- try {
146
- options.log({ operationId: dispatch.id, stage: `bridge.${stage}`, result: ["unknown", "failed", "cancelled"].includes(result.outcome) ? "failure" : "success", code: result.code, ...failures.has(dispatch.id) ? { retryAfterMs: Math.max(0, failures.get(dispatch.id).retryAt - Date.now()) } : {}, durationMs: Date.now() - start });
147
- } catch {
148
- }
149
- if (active()) try {
150
- await linkSession(options, dispatch, result.executionSessionId);
151
- if (active()) await settle(options, dispatch, result);
152
- } catch (error) {
153
- if (safeCode(error) !== "REVISION_CONFLICT") throw error;
154
- }
155
- }
156
- async function releaseUnstartedSubmission(options, path, dispatch) {
157
- await options.fs.meta.seq.transaction(async (tx) => {
158
- const raw = await tx.getEntry(path, dispatch.id);
159
- if (!raw) throw new KanbanError("EXECUTION_JOURNAL_MISSING");
160
- const record = decodeRecord(raw, dispatch);
161
- if (record.phase === "submitting") await tx.setEntry(path, dispatch.id, JSON.stringify({ ...record, phase: "prepared" }));
162
- });
163
- }
164
- async function prepareRequest(options, path, dispatch, item) {
165
- if (!dispatch.continuation) return options.executor.prepare(dispatch, item);
166
- if (!options.executor.prepareContinuation) throw new KanbanError("CONTINUATION_UNSUPPORTED");
167
- const previous = (await options.service.workflow(options.boardId)).dispatches[dispatch.continuation.previousDispatchId];
168
- if (!previous || previous.itemId !== dispatch.itemId) throw new KanbanError("CONTINUATION_SCOPE_MISMATCH");
169
- const record = await loadRecord(options, path, previous);
170
- if (!record) throw new KanbanError("EXECUTION_JOURNAL_MISSING");
171
- return options.executor.prepareContinuation(dispatch, item, record.request);
172
- }
173
- async function loadRecord(options, path, dispatch) {
174
- const raw = await options.fs.meta.seq.getEntry(path, dispatch.id);
175
- if (!raw) return;
176
- return decodeRecord(raw, dispatch);
177
- }
178
- async function savePrepared(options, path, dispatch, request) {
179
- const rawRequest = JSON.stringify(request);
180
- if (typeof rawRequest !== "string" || new TextEncoder().encode(rawRequest).byteLength > 1024 * 1024) throw new KanbanError("INVALID_EXECUTION_REQUEST");
181
- return options.fs.meta.seq.transaction(async (tx) => {
182
- const raw = await tx.getEntry(path, dispatch.id);
183
- if (raw) return decodeRecord(raw, dispatch);
184
- const record = { schema: 1, fence: dispatch.fence, request: JSON.parse(rawRequest), digest: sha256HexSync(rawRequest), phase: "prepared" };
185
- await tx.setEntry(path, dispatch.id, JSON.stringify(record));
186
- return record;
187
- });
188
- }
189
- async function claimSubmission(options, path, dispatch) {
190
- return options.fs.meta.seq.transaction(async (tx) => {
191
- const raw = await tx.getEntry(path, dispatch.id);
192
- if (!raw) throw new KanbanError("EXECUTION_JOURNAL_MISSING");
193
- const record = decodeRecord(raw, dispatch);
194
- if (record.phase !== "prepared") return false;
195
- await tx.setEntry(path, dispatch.id, JSON.stringify({ ...record, phase: "submitting" }));
196
- return true;
197
- });
198
- }
199
- function decodeRecord(raw, dispatch) {
200
- const record = JSON.parse(raw);
201
- if (record.schema !== 1 || record.fence !== dispatch.fence || !["prepared", "submitting"].includes(record.phase) || record.digest !== sha256HexSync(JSON.stringify(record.request))) throw new KanbanError("INVALID_EXECUTION_JOURNAL");
202
- return record;
203
- }
204
- async function settle(options, dispatch, result) {
205
- const current = (await options.service.workflow(options.boardId)).dispatches[dispatch.id];
206
- if (!current || !["prepared", "running", "unknown"].includes(current.outcome)) return;
207
- const board = await options.service.read(options.boardId), item = board.items.find((item2) => item2.id === dispatch.itemId);
208
- const renew = ["running", "unknown"].includes(result.outcome) && (item?.owner?.leaseUntil ?? 0) <= Date.now() + board.policy.leaseMs / 2;
209
- if (!renew && !executionObservationChanged(current, result)) return;
210
- const { outcome, outputs, code, executionSessionId, controlCapabilities } = result, observation = { outcome, ...controlCapabilities ? { controlCapabilities } : {}, ...executionSessionId ? { executionSessionId } : {}, ...outputs ? { outputs } : {}, ...code ? { code } : {} };
211
- const digest = sha256HexSync(JSON.stringify(observation));
212
- await options.service.execute(board.id, {
213
- operationId: `settle:${dispatch.id}:${digest}:${board.revision}`,
214
- expectedRevision: board.revision,
215
- command: { type: "observe-execution", dispatchId: dispatch.id, fence: dispatch.fence, ...observation }
216
- });
217
- }
218
- async function linkSession(options, dispatch, sessionId) {
219
- if (!sessionId || !options.executionReference) return;
220
- const reference = options.executionReference(sessionId, dispatch.id);
221
- const board = await options.service.read(options.boardId), item = board.items.find((item2) => item2.id === dispatch.itemId);
222
- if (item?.owner?.token !== dispatch.fence) throw new KanbanError("STALE_EXECUTION_RESULT");
223
- const current = item.externalRefs?.filter((ref) => ref.system === reference.system).at(-1);
224
- if (current?.id === reference.id && current.revision === reference.revision) return;
225
- await options.service.execute(board.id, {
226
- operationId: `execution-session:${dispatch.id}:${sha256HexSync(sessionId)}:${board.revision}`,
227
- expectedRevision: board.revision,
228
- command: { type: "link-reference", itemId: dispatch.itemId, reference }
229
- });
230
- }
231
- function safeCode(error) {
232
- const code = error?.reasonCode ?? error?.code;
233
- return typeof code === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(code) ? code : "EXECUTION_OBSERVATION_FAILED";
234
- }
235
-
236
- // src/monitor.ts
237
- function startKanbanMonitor(options) {
238
- let stopped = false, pending;
239
- const tick = () => {
240
- if (stopped || pending) return;
241
- pending = Promise.resolve().then(() => stopped ? void 0 : options.service.scan()).then(() => stopped ? void 0 : options.onSuccess?.()).catch(async (error) => {
242
- if (stopped) return;
243
- try {
244
- await options.onError(error);
245
- } catch {
246
- }
247
- }).finally(() => {
248
- pending = void 0;
249
- });
250
- };
251
- const unsubscribe = options.subscribeTick(tick), unwatch = options.subscribeChanges?.(tick);
252
- if (options.initialScan !== false) tick();
253
- return { async dispose() {
254
- if (stopped) {
255
- await pending;
256
- return;
257
- }
258
- stopped = true;
259
- try {
260
- unsubscribe();
261
- unwatch?.();
262
- } finally {
263
- await pending;
264
- }
265
- } };
266
- }
267
-
268
- // src/work-messages.ts
269
- import { FSError } from "@nestos/vfs-core";
270
- async function consumeWorkMessages(fs, directory, scope, journal, deliver, decode, publishReply) {
271
- const publications = await readPublications(fs, directory, scope, decode), indexKey = "workspace-message-index:" + scope.attemptId;
272
- const known = await journal.get(indexKey) ?? [];
273
- if (known.some((id) => !publications.some((p) => p.message.id === id))) throw protocolError("WORK_MESSAGE_MISSING", "Published workspace message is missing", "ECONFLICT");
274
- unresolvedWorkIssues(publications.map((p) => p.message));
275
- publications.sort((a, b) => Number(a.message.type === "resolved") - Number(b.message.type === "resolved"));
276
- for (const { message, bytes } of publications) {
277
- const digest = sha256HexSync(bytes);
278
- const delivered = await deliverPublication(journal, indexKey, message, digest, deliver);
279
- if (delivered) await publishReply(directory + "/outbox", "ack-" + message.id + ".yaml", JSON.stringify({ schema: "work-reply/1", ...scope, id: "ack-" + message.id, type: "accepted", replyTo: message.id, content: "Accepted", digest }));
280
- }
281
- return publications.map((p) => p.message);
282
- }
283
- async function readPublications(fs, directory, scope, decode) {
284
- const files = await fs.driver.getChildren(directory + "/inbox", { fields: "entry" }), publications = [];
285
- if (files.length > 200) throw protocolError("WORK_MESSAGE_COUNT_LIMIT", "Workspace inbox exceeds 200 entries");
286
- for (const file of files.sort((a, b) => a.name.localeCompare(b.name))) {
287
- if (file.type !== "file" || !/^[a-zA-Z0-9_-]{1,128}\.(yaml|yml|json)$/.test(file.name)) continue;
288
- const bytes = await readMessageBytes(fs, directory + "/inbox/" + file.name);
289
- const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes), envelope = decode(text);
290
- if (envelope?.schema === "work-message/2" && Object.hasOwn(envelope, "scopeId")) continue;
291
- const message = parseWorkMessage(text, scope, decode);
292
- if (file.name.slice(0, file.name.lastIndexOf(".")) !== message.id) throw protocolError("WORK_MESSAGE_FILENAME_MISMATCH", "Workspace message filename does not match its identity");
293
- publications.push({ message, bytes });
294
- }
295
- return publications;
296
- }
297
- async function deliverPublication(journal, indexKey, message, digest, deliver) {
298
- const key = "workspace-message:" + message.attemptId + ":" + message.id;
299
- const receipt = await journal.update(key, (old) => {
300
- if (old && old.digest !== digest) throw protocolError("WORK_MESSAGE_CHANGED", "Published workspace message changed", "ECONFLICT");
301
- return old ?? { digest, message, delivered: false };
302
- });
303
- await journal.update(indexKey, (old) => old?.includes(message.id) ? old : [...old ?? [], message.id]);
304
- if (receipt.delivered) return true;
305
- const outcome = await deliver(receipt.message);
306
- if (outcome?.pending) return false;
307
- await journal.update(key, (old) => ({ ...old, delivered: true }));
308
- return true;
309
- }
310
- function parseWorkMessage(text, scope, decode) {
311
- let value;
312
- try {
313
- value = decode(text);
314
- } catch {
315
- throw protocolError("WORK_MESSAGE_YAML_INVALID", "Workspace message is not valid bounded YAML");
316
- }
317
- if (!value || typeof value !== "object" || Array.isArray(value)) throw protocolError("WORK_MESSAGE_SCHEMA_INVALID", "Workspace message schema is invalid");
318
- if ("body" in value) {
319
- if (Object.hasOwn(value, "content")) throw protocolError("WORK_MESSAGE_FIELDS_INVALID", "Workspace message must not contain both body and content");
320
- value = { ...value, content: value.body };
321
- }
322
- if (Object.keys(value).some((key) => !["schema", "runId", "itemId", "attemptId", "id", "type", "content", "body", "replyTo"].includes(key)))
323
- throw protocolError("WORK_MESSAGE_FIELDS_INVALID", "Workspace message contains undeclared fields");
324
- validateEnvelope(value, scope);
325
- return value;
326
- }
327
- function validateEnvelope(value, scope) {
328
- if (!["work-message/1", "work-message/2"].includes(value.schema)) throw protocolError("WORK_MESSAGE_SCHEMA_INVALID", "Workspace message schema is unsupported");
329
- if (!["progress", "clarification", "error", "result", ...value.schema === "work-message/2" ? ["resolved"] : []].includes(value.type))
330
- throw protocolError("WORK_MESSAGE_TYPE_INVALID", "Workspace message schema does not permit this type");
331
- if (!validId(value.id)) throw protocolError("WORK_MESSAGE_ID_INVALID", "Workspace message identity is invalid");
332
- if (["runId", "itemId", "attemptId"].some((key) => value[key] !== scope[key]))
333
- throw protocolError("WORK_MESSAGE_SCOPE_MISMATCH", "Workspace message execution scope is invalid");
334
- if (typeof value.content !== "string" || !value.content.trim() || value.content.length > 8e3) throw protocolError("WORK_MESSAGE_CONTENT_INVALID", "Workspace message content is invalid");
335
- if (value.replyTo !== void 0 && !validId(value.replyTo) || value.type === "resolved" && !value.replyTo)
336
- throw protocolError("WORK_MESSAGE_REPLY_INVALID", "Workspace message reply target is invalid");
337
- }
338
- function validId(value) {
339
- return typeof value === "string" && /^[a-zA-Z0-9_-]{1,128}$/.test(value);
340
- }
341
- function protocolError(reasonCode, message, code = "EINVAL") {
342
- return Object.assign(new FSError(code, message), { reasonCode });
343
- }
344
- async function readMessageBytes(fs, path) {
345
- const before = await fs.driver.getNode(path);
346
- if (before?.type !== "file" || !Number.isSafeInteger(before.size) || before.size < 1) throw protocolError("WORK_MESSAGE_FILE_INVALID", "Workspace message is not a nonempty regular file");
347
- if (before.size > 65536) throw protocolError("WORK_MESSAGE_SIZE_LIMIT", "Workspace message exceeds 64 KiB");
348
- const bytes = await fs.driver.readContent(path, { encoding: "binary", length: before.size });
349
- const after = await fs.driver.getNode(path);
350
- if (bytes.byteLength !== before.size || after?.type !== "file" || after.size !== before.size || after.revision !== before.revision || after.modifiedAt !== before.modifiedAt)
351
- throw protocolError("WORK_MESSAGE_READ_CONFLICT", "Workspace message changed while reading", "ECONFLICT");
352
- return bytes;
353
- }
354
- function unresolvedWorkIssues(messages) {
355
- const issues = messages.filter((m) => m.type === "clarification" || m.type === "error");
356
- for (const resolution of messages.filter((m) => m.type === "resolved")) {
357
- if (!issues.some((issue) => issue.id === resolution.replyTo)) throw Object.assign(new Error("Resolution target is not an issue in this attempt"), { code: "WORKSPACE_RESOLUTION_INVALID" });
358
- }
359
- return issues.filter((issue) => !messages.some((m) => m.type === "resolved" && m.replyTo === issue.id));
360
- }
2
+ consumeWorkMessages,
3
+ createKanbanExecutionBridge,
4
+ parseWorkMessage,
5
+ startKanbanMonitor,
6
+ unresolvedWorkIssues
7
+ } from "./chunk-IHHDXZPZ.js";
8
+ import "./chunk-RQP77ZTY.js";
9
+ import "./chunk-M35X5PHO.js";
10
+ import "./chunk-3JYLJPE5.js";
361
11
  export {
362
12
  consumeWorkMessages,
363
13
  createKanbanExecutionBridge,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/execution-bridge.ts","../src/monitor.ts","../src/work-messages.ts"],"sourcesContent":["import {boardAllowsStart} from './contracts/board-control';\nimport {taskDue} from './contracts/task-projection';\nimport {sha256HexSync, randomUUID} from './utils';\nimport type {IFileSystem} from '@nestos/vfs-core';\nimport type {KanbanDispatch, KanbanService, KanbanItem, KanbanArtifactRef, KanbanPrincipal, KanbanEstimate} from './contracts';\nimport {KanbanError} from './ports';\nimport {executionObservationChanged} from './execution-observation';\n\nexport interface KanbanExecutionObservation {\n controlCapabilities?: {pause:'boundary'|'none';resume:boolean};\n executionSessionId?: string;\n outcome: 'running' | 'unknown' | 'succeeded' | 'failed' | 'cancelled';\n outputs?: KanbanArtifactRef[]; code?: string;\n}\nexport interface KanbanExecutionPort {\n prepareContinuation?(dispatch: KanbanDispatch, item: KanbanItem, previousRequest: unknown): Promise<unknown>;\n /** Persistable request; preparing cannot submit agent work. */\n prepare(dispatch: KanbanDispatch, item: KanbanItem): Promise<unknown>;\n start(request: unknown): Promise<KanbanExecutionObservation>;\n /** Observe the original admission only. Missing receipts mean unknown. */\n inspect(request: unknown): Promise<KanbanExecutionObservation>;\n}\nexport interface KanbanExecutionBridgeOptions {\n fs: IFileSystem; directory: string; boardId: string; service: KanbanService;\n worker(principal: KanbanPrincipal): Promise<KanbanService>;\n executor: KanbanExecutionPort;\n authorize(stage: 'prepare' | 'start' | 'inspect', dispatch: KanbanDispatch): Promise<void>;\n log(input: {operationId: string; stage: string; result: 'success' | 'failure'; code?: string; retryAfterMs?: number; durationMs: number}): void;\n executionReference?(sessionId: string, dispatchId: string): import('./contracts').KanbanExternalReference;\n subscribe?(wake: () => void): () => void;\n now?:()=>number;\n autoDispatch?: boolean | (() => Promise<boolean>); shutdownTimeoutMs?: number;\n estimate?(item: KanbanItem, principal: KanbanPrincipal): Promise<KanbanEstimate | undefined>;\n}\ninterface ExecutionRecord {schema: 1; fence: string; request: unknown; digest: string; phase: 'prepared' | 'submitting'}\ninterface ReadFailure {code: string; count: number; retryAt: number}\ntype TrackWrite = <T>(operation: Promise<T>) => Promise<T>;\n\n/** The host owns execution. Prepared/submitting identities survive response loss and host shutdown. */\nexport async function createKanbanExecutionBridge(options: KanbanExecutionBridgeOptions) {\n const path = `${options.directory}/executions.seq`;\n try {await options.fs.driver.createFile({parentPath:options.directory,name:'executions.seq',type:'seqfile',recursive:true});}\n catch(error) {if((error as {code?:string})?.code!=='EEXIST') throw error;}\n if(!options.fs.meta.seq?.transaction) throw new KanbanError('TRANSACTION_REQUIRED');\n let stopped=false, again=false, pending: Promise<void>|undefined;\n const writes=new Set<Promise<unknown>>();\n const failures = new Map<string, ReadFailure>();\n const track:TrackWrite=operation=>{const tracked=operation.finally(()=>{writes.delete(tracked);});writes.add(tracked);return tracked;};\n const active=()=>!stopped;\n const scan=async()=>{if(stopped) return; if(pending) {again=true; return pending;} const started=Date.now();\n pending=runBridge(options,path,active,track,failures).catch(error=>{try {options.log({operationId:randomUUID(),stage:'bridge.scan',result:'failure',code:safeCode(error),durationMs:Date.now()-started});} catch {/* Diagnostics cannot replay work. */} throw error;}).finally(()=>{pending=undefined;if(again&&!stopped) {again=false;void scan().catch(()=>{});}});\n return pending;\n };\n const unsubscribe=options.subscribe?.(()=>{void scan().catch(()=>{});});\n return {scan, tick: scan, async quiesce() {\n stopped=true;unsubscribe?.(); if(!pending) return;\n let timer: ReturnType<typeof setTimeout>|undefined;\n try {await Promise.race([pending,new Promise<void>(resolve=>{timer=setTimeout(resolve,options.shutdownTimeoutMs ?? 5000);})]);}\n finally {if(timer) clearTimeout(timer);await Promise.allSettled([...writes]);}\n }};\n}\nasync function runBridge(options: KanbanExecutionBridgeOptions, path: string, active: () => boolean, track: TrackWrite, failures: Map<string, ReadFailure>): Promise<void> {\n const workflow=await options.service.workflow(options.boardId);\n const live = new Set(Object.values(workflow.dispatches).filter(dispatch => ['prepared', 'running', 'unknown'].includes(dispatch.outcome)).map(dispatch => dispatch.id));\n for (const id of failures.keys()) if (!live.has(id)) failures.delete(id);\n for(const dispatch of Object.values(workflow.dispatches)) {\n if(!active()) return;\n if(live.has(dispatch.id) && (failures.get(dispatch.id)?.retryAt ?? 0) <= Date.now()) await runDispatch(options,path,dispatch,active,track,failures);\n }\n const automatic = typeof options.autoDispatch === 'function' ? await options.autoDispatch() : options.autoDispatch;\n if(!automatic||!active()) return;\n for(const id of (await options.service.workflow(options.boardId)).ready) {\n if(!active()) return;\n const {item}=await options.service.item(options.boardId,id), owner=item.eligiblePrincipals?.[0]; if(!owner || !taskDue(item, (options.now??Date.now)())) continue;\n const worker=await options.worker(owner), board=await worker.read(options.boardId);if(!active()) return;\n if (!boardAllowsStart(board, item)) continue;\n try {\n const dispatchId=randomUUID();\n const estimate=await options.estimate?.(item,owner);if(!active()) return;\n await worker.execute(board.id,{operationId:'dispatch:'+dispatchId,expectedRevision:board.revision,command:{type:'prepare-dispatch',itemId:id,dispatchId,estimate,trigger:'automatic'}});\n const dispatch=(await options.service.workflow(board.id)).dispatches[dispatchId];\n if(active()) await runDispatch(options,path,dispatch,active,track,failures);\n }\n catch(error) {if(!['WIP_LIMIT','ASSIGNEE_WIP_LIMIT','READY_LIMIT','REVISION_CONFLICT'].includes(safeCode(error))) throw error;}\n }\n}\nasync function runDispatch(options: KanbanExecutionBridgeOptions, path: string, dispatch: KanbanDispatch, active: () => boolean, track: TrackWrite, failures: Map<string, ReadFailure>): Promise<void> {\n const start=Date.now(); let stage: 'inspect' | 'prepare' | 'start'='inspect', result: KanbanExecutionObservation;\n const previousFailure = failures.has(dispatch.id);\n try {\n let record=await loadRecord(options,path,dispatch);if(!active()) return;\n if ((!record || record.phase === 'prepared') && (await options.service.read(options.boardId)).executionControl?.paused) return;\n if(!record) {\n stage='prepare';await options.authorize(stage,dispatch);if(!active()) return;\n const item=(await options.service.item(options.boardId,dispatch.itemId)).item;if(!active()) return;\n const request=await prepareRequest(options, path, dispatch, item);if(!active()) return;\n record=await track(savePrepared(options,path,dispatch,request));\n }\n if(!active()) return;\n const submit=record.phase==='prepared'&&await track(claimSubmission(options,path,dispatch));\n if(!active()) return; stage=submit?'start':'inspect';await options.authorize(stage,dispatch);if(!active()) return;\n result=await (submit?options.executor.start(record.request):options.executor.inspect(record.request));\n failures.delete(dispatch.id);\n } catch(error) {\n const notStarted = (error as {executionOutcome?: string; retryable?: boolean})?.executionOutcome === 'not-started'\n && (error as {retryable?: boolean}).retryable === true;\n if (notStarted && stage === 'start') await track(releaseUnstartedSubmission(options, path, dispatch));\n result={outcome:stage==='prepare'&&!notStarted?'failed':'unknown',code:safeCode(error)};\n if (stage === 'inspect' || notStarted) {\n const previous = failures.get(dispatch.id), count = previous && previous.code === result.code ? previous.count + 1 : 1;\n failures.set(dispatch.id, {code: result.code!, count, retryAt: Date.now() + Math.min(60000, 1000 * 2 ** Math.min(count - 1, 6))});\n }\n }\n if (stage !== 'inspect' || executionObservationChanged(dispatch, result) || previousFailure && !failures.has(dispatch.id))\n try {options.log({operationId:dispatch.id,stage:`bridge.${stage}`,result:['unknown','failed','cancelled'].includes(result.outcome)?'failure':'success',code:result.code,...(failures.has(dispatch.id)?{retryAfterMs:Math.max(0,failures.get(dispatch.id)!.retryAt-Date.now())}:{}),durationMs:Date.now()-start});} catch {/* Metadata-only diagnostics. */}\n if(active()) try {await linkSession(options,dispatch,result.executionSessionId);if(active()) await settle(options,dispatch,result);} catch(error) {if(safeCode(error)!=='REVISION_CONFLICT') throw error;}\n}\nasync function releaseUnstartedSubmission(options: KanbanExecutionBridgeOptions, path: string, dispatch: KanbanDispatch): Promise<void> {\n await options.fs.meta.seq!.transaction!(async tx => {\n const raw = await tx.getEntry(path, dispatch.id);\n if (!raw) throw new KanbanError('EXECUTION_JOURNAL_MISSING');\n const record = decodeRecord(raw, dispatch);\n if (record.phase === 'submitting') await tx.setEntry(path, dispatch.id, JSON.stringify({...record, phase: 'prepared'}));\n });\n}\nasync function prepareRequest(options: KanbanExecutionBridgeOptions, path: string, dispatch: KanbanDispatch, item: KanbanItem): Promise<unknown> {\n if (!dispatch.continuation) return options.executor.prepare(dispatch, item);\n if (!options.executor.prepareContinuation) throw new KanbanError('CONTINUATION_UNSUPPORTED');\n const previous = (await options.service.workflow(options.boardId)).dispatches[dispatch.continuation.previousDispatchId];\n if (!previous || previous.itemId !== dispatch.itemId) throw new KanbanError('CONTINUATION_SCOPE_MISMATCH');\n const record = await loadRecord(options, path, previous);\n if (!record) throw new KanbanError('EXECUTION_JOURNAL_MISSING');\n return options.executor.prepareContinuation(dispatch, item, record.request);\n}\nasync function loadRecord(options: KanbanExecutionBridgeOptions, path: string, dispatch: KanbanDispatch): Promise<ExecutionRecord|undefined> {\n const raw=await options.fs.meta.seq!.getEntry(path,dispatch.id); if(!raw) return;\n return decodeRecord(raw,dispatch);\n}\nasync function savePrepared(options: KanbanExecutionBridgeOptions, path: string, dispatch: KanbanDispatch, request: unknown): Promise<ExecutionRecord> {\n const rawRequest=JSON.stringify(request);\n if(typeof rawRequest!=='string'||new TextEncoder().encode(rawRequest).byteLength>1024*1024) throw new KanbanError('INVALID_EXECUTION_REQUEST');\n return options.fs.meta.seq!.transaction!(async tx=>{\n const raw=await tx.getEntry(path,dispatch.id);if(raw) return decodeRecord(raw,dispatch);\n const record: ExecutionRecord={schema:1,fence:dispatch.fence,request:JSON.parse(rawRequest),digest:sha256HexSync(rawRequest),phase:'prepared'};\n await tx.setEntry(path,dispatch.id,JSON.stringify(record));return record;\n });\n}\nasync function claimSubmission(options: KanbanExecutionBridgeOptions, path: string, dispatch: KanbanDispatch): Promise<boolean> {\n return options.fs.meta.seq!.transaction!(async tx=>{\n const raw=await tx.getEntry(path,dispatch.id);if(!raw) throw new KanbanError('EXECUTION_JOURNAL_MISSING');\n const record=decodeRecord(raw,dispatch);\n if(record.phase!=='prepared') return false;\n await tx.setEntry(path,dispatch.id,JSON.stringify({...record,phase:'submitting'}));return true;\n });\n}\nfunction decodeRecord(raw: string, dispatch: KanbanDispatch): ExecutionRecord {\n const record=JSON.parse(raw) as ExecutionRecord;\n if(record.schema!==1||record.fence!==dispatch.fence||!['prepared','submitting'].includes(record.phase)||record.digest!==sha256HexSync(JSON.stringify(record.request))) throw new KanbanError('INVALID_EXECUTION_JOURNAL');return record;\n}\nasync function settle(options: KanbanExecutionBridgeOptions, dispatch: KanbanDispatch, result: KanbanExecutionObservation): Promise<void> {\n const current=(await options.service.workflow(options.boardId)).dispatches[dispatch.id];\n if(!current||!['prepared','running','unknown'].includes(current.outcome)) return;\n const board=await options.service.read(options.boardId), item=board.items.find(item=>item.id===dispatch.itemId);\n const renew=['running','unknown'].includes(result.outcome) && (item?.owner?.leaseUntil ?? 0) <= Date.now() + board.policy.leaseMs / 2;\n if(!renew&&!executionObservationChanged(current,result)) return;\n const {outcome,outputs,code,executionSessionId,controlCapabilities}=result, observation={outcome,...(controlCapabilities?{controlCapabilities}:{}),...(executionSessionId?{executionSessionId}:{}),...(outputs?{outputs}:{}),...(code?{code}:{})};\n const digest=sha256HexSync(JSON.stringify(observation));\n await options.service.execute(board.id,{operationId:`settle:${dispatch.id}:${digest}:${board.revision}`,expectedRevision:board.revision,\n command:{type:'observe-execution',dispatchId:dispatch.id,fence:dispatch.fence,...observation}});\n}\nasync function linkSession(options: KanbanExecutionBridgeOptions, dispatch: KanbanDispatch, sessionId?: string): Promise<void> {\n if(!sessionId || !options.executionReference) return;\n const reference = options.executionReference(sessionId, dispatch.id);\n const board=await options.service.read(options.boardId),item=board.items.find(item=>item.id===dispatch.itemId);\n if(item?.owner?.token!==dispatch.fence) throw new KanbanError('STALE_EXECUTION_RESULT');\n const current=item.externalRefs?.filter(ref=>ref.system===reference.system).at(-1);\n if(current?.id===reference.id&&current.revision===reference.revision) return;\n await options.service.execute(board.id,{operationId:`execution-session:${dispatch.id}:${sha256HexSync(sessionId)}:${board.revision}`,expectedRevision:board.revision,\n command:{type:'link-reference',itemId:dispatch.itemId,reference}});\n}\nfunction safeCode(error: unknown): string {\n const code=(error as {reasonCode?:unknown})?.reasonCode??(error as {code?:unknown})?.code;return typeof code==='string'&&/^[A-Z][A-Z0-9_]{0,79}$/.test(code)?code:'EXECUTION_OBSERVATION_FAILED';\n}\n","import type {KanbanClient} from './contracts';\n\n/** Host supplies its scheduler; disposal prevents new scans and awaits in-flight work. */\nexport function startKanbanMonitor(options: {\n service: Pick<KanbanClient, 'scan'>;\n subscribeTick: (tick: () => void) => () => void;\n onError: (error: unknown) => void | Promise<void>;\n onSuccess?: () => void | Promise<void>;\n subscribeChanges?: (tick: () => void) => () => void;\n initialScan?: boolean;\n}): { dispose(): Promise<void> } {\n let stopped = false, pending: Promise<void> | undefined;\n const tick = () => {\n if (stopped || pending) return;\n pending = Promise.resolve().then(() => stopped ? undefined : options.service.scan()).then(()=>stopped ? undefined : options.onSuccess?.()).catch(async error => {\n if (stopped) return;\n try { await options.onError(error); } catch { /* Reporting must not leave an unhandled rejection. */ }\n }).finally(() => { pending = undefined; });\n };\n const unsubscribe = options.subscribeTick(tick), unwatch = options.subscribeChanges?.(tick);\n if (options.initialScan !== false) tick();\n return { async dispose() {\n if (stopped) { await pending; return; }\n stopped = true; try { unsubscribe(); unwatch?.(); } finally { await pending; }\n } };\n}\n","import {FSError, type IFileSystem} from '@nestos/vfs-core';\nimport {sha256HexSync} from './utils';\nexport interface WorkMessageScope {runId:string;itemId:string;attemptId:string}\nexport interface WorkMessage extends WorkMessageScope {schema:'work-message/1'|'work-message/2';id:string;type:'progress'|'clarification'|'error'|'result'|'resolved';content:string;replyTo?:string}\ninterface Journal {get<T>(key:string):Promise<T|undefined>;update<T>(key:string,change:(old:T|undefined)=>T):Promise<T>}\ninterface Receipt {digest:string;message:WorkMessage;delivered:boolean}\n/** Directory events are wakeups; immutable files plus durable receipts are authoritative. */\nexport async function consumeWorkMessages(fs:IFileSystem,directory:string,scope:WorkMessageScope,journal:Journal,\n deliver:(message:WorkMessage)=>Promise<void|{pending:true}>,decode:(text:string)=>unknown,publishReply:(directory:string,name:string,content:string)=>Promise<void>):Promise<WorkMessage[]> {\n const publications=await readPublications(fs,directory,scope,decode),indexKey='workspace-message-index:'+scope.attemptId;\n const known=await journal.get<string[]>(indexKey)??[];\n if(known.some(id=>!publications.some(p=>p.message.id===id)))throw protocolError('WORK_MESSAGE_MISSING','Published workspace message is missing','ECONFLICT');\n unresolvedWorkIssues(publications.map(p=>p.message));\n publications.sort((a,b)=>Number(a.message.type==='resolved')-Number(b.message.type==='resolved'));\n for(const {message,bytes} of publications) {\n const digest=sha256HexSync(bytes);\n const delivered=await deliverPublication(journal,indexKey,message,digest,deliver);\n if(delivered)await publishReply(directory+'/outbox','ack-'+message.id+'.yaml',JSON.stringify({schema:'work-reply/1',...scope,id:'ack-'+message.id,type:'accepted',replyTo:message.id,content:'Accepted',digest}));\n }\n return publications.map(p=>p.message);\n}\nasync function readPublications(fs:IFileSystem,directory:string,scope:WorkMessageScope,decode:(text:string)=>unknown) {\n const files=await fs.driver.getChildren(directory+'/inbox',{fields:'entry'}),publications:Array<{message:WorkMessage;bytes:ArrayBuffer}>=[];\n if(files.length>200)throw protocolError('WORK_MESSAGE_COUNT_LIMIT','Workspace inbox exceeds 200 entries');\n for(const file of files.sort((a,b)=>a.name.localeCompare(b.name))) {\n if(file.type!=='file'||!/^[a-zA-Z0-9_-]{1,128}\\.(yaml|yml|json)$/.test(file.name))continue;\n const bytes=await readMessageBytes(fs,directory+'/inbox/'+file.name);\n const text = new TextDecoder('utf-8',{fatal:true}).decode(bytes), envelope = decode(text) as {schema?: string; scopeId?: string};\n if (envelope?.schema === 'work-message/2' && Object.hasOwn(envelope, 'scopeId')) continue;\n const message=parseWorkMessage(text,scope,decode);\n if(file.name.slice(0,file.name.lastIndexOf('.'))!==message.id)throw protocolError('WORK_MESSAGE_FILENAME_MISMATCH','Workspace message filename does not match its identity');\n publications.push({message,bytes});\n }\n return publications;\n}\nasync function deliverPublication(journal:Journal,indexKey:string,message:WorkMessage,digest:string,deliver:(message:WorkMessage)=>Promise<void|{pending:true}>):Promise<boolean> {\n const key='workspace-message:'+message.attemptId+':'+message.id;\n const receipt=await journal.update<Receipt>(key,old=>{\n if(old&&old.digest!==digest)throw protocolError('WORK_MESSAGE_CHANGED','Published workspace message changed','ECONFLICT');\n return old??{digest,message,delivered:false};\n });\n await journal.update<string[]>(indexKey,old=>old?.includes(message.id)?old:[...old??[],message.id]);\n if(receipt.delivered)return true;\n const outcome=await deliver(receipt.message);if(outcome?.pending)return false;\n await journal.update<Receipt>(key,old=>({...old!,delivered:true}));return true;\n}\n\nexport function parseWorkMessage(text:string,scope:WorkMessageScope,decode:(text:string)=>unknown):WorkMessage {\n let value:WorkMessage;\n try {value=decode(text) as WorkMessage;} catch {throw protocolError('WORK_MESSAGE_YAML_INVALID','Workspace message is not valid bounded YAML');}\n if(!value||typeof value!=='object'||Array.isArray(value))throw protocolError('WORK_MESSAGE_SCHEMA_INVALID','Workspace message schema is invalid');\n if('body' in value) {\n if(Object.hasOwn(value,'content'))throw protocolError('WORK_MESSAGE_FIELDS_INVALID','Workspace message must not contain both body and content');\n value={...value,content:(value as WorkMessage&{body:string}).body};\n }\n if(Object.keys(value).some(key=>!['schema','runId','itemId','attemptId','id','type','content','body','replyTo'].includes(key)))\n throw protocolError('WORK_MESSAGE_FIELDS_INVALID','Workspace message contains undeclared fields');\n validateEnvelope(value,scope);return value;\n}\nfunction validateEnvelope(value:WorkMessage,scope:WorkMessageScope):void {\n if(!['work-message/1','work-message/2'].includes(value.schema))throw protocolError('WORK_MESSAGE_SCHEMA_INVALID','Workspace message schema is unsupported');\n if(!['progress','clarification','error','result',...(value.schema==='work-message/2'?['resolved']:[])].includes(value.type))\n throw protocolError('WORK_MESSAGE_TYPE_INVALID','Workspace message schema does not permit this type');\n if(!validId(value.id))throw protocolError('WORK_MESSAGE_ID_INVALID','Workspace message identity is invalid');\n if(['runId','itemId','attemptId'].some(key=>value[key as keyof WorkMessageScope]!==scope[key as keyof WorkMessageScope]))\n throw protocolError('WORK_MESSAGE_SCOPE_MISMATCH','Workspace message execution scope is invalid');\n if(typeof value.content!=='string'||!value.content.trim()||value.content.length>8000)throw protocolError('WORK_MESSAGE_CONTENT_INVALID','Workspace message content is invalid');\n if(value.replyTo!==undefined&&!validId(value.replyTo)||value.type==='resolved'&&!value.replyTo)\n throw protocolError('WORK_MESSAGE_REPLY_INVALID','Workspace message reply target is invalid');\n}\nfunction validId(value:unknown):value is string {return typeof value==='string'&&/^[a-zA-Z0-9_-]{1,128}$/.test(value);}\nfunction protocolError(reasonCode:string,message:string,code:'EINVAL'|'ECONFLICT'='EINVAL'):Error {\n return Object.assign(new FSError(code,message),{reasonCode});\n}\n\n/** Range reads must fit the advertised size; remote backends reject short ranges. */\nasync function readMessageBytes(fs: IFileSystem, path: string): Promise<ArrayBuffer> {\n const before = await fs.driver.getNode(path);\n if (before?.type !== 'file' || !Number.isSafeInteger(before.size) || before.size < 1) throw protocolError('WORK_MESSAGE_FILE_INVALID','Workspace message is not a nonempty regular file');\n if (before.size > 65536) throw protocolError('WORK_MESSAGE_SIZE_LIMIT','Workspace message exceeds 64 KiB');\n const bytes = await fs.driver.readContent(path, {encoding: 'binary', length: before.size});\n const after = await fs.driver.getNode(path);\n if (bytes.byteLength !== before.size || after?.type !== 'file' || after.size !== before.size\n || after.revision !== before.revision || after.modifiedAt !== before.modifiedAt)\n throw protocolError('WORK_MESSAGE_READ_CONFLICT','Workspace message changed while reading','ECONFLICT');\n return bytes;\n}\n\n/** Only explicit linked resolutions close issues; unrelated progress cannot erase them. */\nexport function unresolvedWorkIssues(messages: WorkMessage[]): WorkMessage[] {\n const issues = messages.filter(m => m.type === 'clarification' || m.type === 'error');\n for (const resolution of messages.filter(m => m.type === 'resolved')) {\n if (!issues.some(issue => issue.id === resolution.replyTo)) throw Object.assign(new Error('Resolution target is not an issue in this attempt'), {code:'WORKSPACE_RESOLUTION_INVALID'});\n }\n return issues.filter(issue => !messages.some(m => m.type === 'resolved' && m.replyTo === issue.id));\n}\n"],"mappings":"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","names":["item"]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.cjs CHANGED
@@ -1,36 +1,34 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunkIUI5CI2Scjs = require('./chunk-IUI5CI2S.cjs');
3
+ var _chunkDINLR6E6cjs = require('./chunk-DINLR6E6.cjs');
4
4
 
5
5
 
6
- var _chunkT7P5EIC4cjs = require('./chunk-T7P5EIC4.cjs');
6
+ var _chunkVMRDXOLXcjs = require('./chunk-VMRDXOLX.cjs');
7
7
 
8
8
 
9
9
 
10
- var _chunkL44TM4EEcjs = require('./chunk-L44TM4EE.cjs');
10
+ var _chunkEYTVC2H5cjs = require('./chunk-EYTVC2H5.cjs');
11
11
  require('./chunk-4XF6LQSU.cjs');
12
- require('./chunk-4KF7KUAI.cjs');
12
+ require('./chunk-RBOER6EW.cjs');
13
13
 
14
14
 
15
15
 
16
- var _chunk2YFPV5GFcjs = require('./chunk-2YFPV5GF.cjs');
16
+ var _chunk7KDOTBRXcjs = require('./chunk-7KDOTBRX.cjs');
17
17
 
18
18
 
19
19
 
20
+ var _chunkRSH6QSECcjs = require('./chunk-RSH6QSEC.cjs');
20
21
 
21
22
 
22
23
 
23
- var _chunkKYNYPKKHcjs = require('./chunk-KYNYPKKH.cjs');
24
24
 
25
25
 
26
26
 
27
- var _chunkPSKQAFDFcjs = require('./chunk-PSKQAFDF.cjs');
28
- require('./chunk-6MN3H3YF.cjs');
29
- require('./chunk-LSE6JG5I.cjs');
27
+ var _chunkAOHQCX7Kcjs = require('./chunk-AOHQCX7K.cjs');
30
28
 
31
29
  // src/api.ts
32
30
  function createKanban(options) {
33
- const service = _chunkIUI5CI2Scjs.createKanbanModule.call(void 0, options);
31
+ const service = _chunkDINLR6E6cjs.createKanbanModule.call(void 0, options);
34
32
  return {
35
33
  boards: { create: service.create, get: service.read, list: service.catalog, overview: service.overview },
36
34
  items: { get: service.item, query: service.query },
@@ -52,7 +50,7 @@ function createKanban(options) {
52
50
  // src/event-policy.ts
53
51
  function isKanbanEventRelevant(event, board, now) {
54
52
  const item = board.items.find((i) => i.id === event.itemId);
55
- if (event.kind === "escalated") return !!item && !_chunkL44TM4EEcjs.terminal.call(void 0, item) && (event.data.reason === "transfer-timeout" ? !item.transfer && item.status === "claimed" : _chunkL44TM4EEcjs.escalationReasons.call(void 0, board, item, now).includes(_nullishCoalesce(event.data.reason, () => ( ""))));
53
+ if (event.kind === "escalated") return !!item && !_chunkEYTVC2H5cjs.terminal.call(void 0, item) && (event.data.reason === "transfer-timeout" ? !item.transfer && item.status === "claimed" : _chunkEYTVC2H5cjs.escalationReasons.call(void 0, board, item, now).includes(_nullishCoalesce(event.data.reason, () => ( ""))));
56
54
  if (event.kind === "submit") return _optionalChain([item, 'optionalAccess', _ => _.status]) === "review";
57
55
  if (event.kind === "request-transfer") return !!_optionalChain([item, 'optionalAccess', _2 => _2.transfer]) && item.transfer.expiresAt > now;
58
56
  if (event.kind === "publish-proposal") return _optionalChain([item, 'optionalAccess', _3 => _3.status]) === "open";
@@ -72,5 +70,5 @@ function isKanbanEventRelevant(event, board, now) {
72
70
 
73
71
 
74
72
 
75
- exports.DEFAULT_KANBAN_WORK_TYPES = _chunkPSKQAFDFcjs.DEFAULT_KANBAN_WORK_TYPES; exports.KanbanError = _chunk2YFPV5GFcjs.KanbanError; exports.KanbanScanNotStartedError = _chunk2YFPV5GFcjs.KanbanScanNotStartedError; exports.SeqKanbanStore = _chunkT7P5EIC4cjs.SeqKanbanStore; exports.TASK_START_DELAY_MS = _chunkKYNYPKKHcjs.TASK_START_DELAY_MS; exports.createKanban = createKanban; exports.createKanbanModule = _chunkIUI5CI2Scjs.createKanbanModule; exports.isKanbanEventRelevant = isKanbanEventRelevant; exports.kanbanReference = _chunkPSKQAFDFcjs.kanbanReference; exports.projectKanbanTask = _chunkKYNYPKKHcjs.projectKanbanTask; exports.taskDue = _chunkKYNYPKKHcjs.taskDue; exports.taskEditing = _chunkKYNYPKKHcjs.taskEditing; exports.taskStartAt = _chunkKYNYPKKHcjs.taskStartAt;
73
+ exports.DEFAULT_KANBAN_WORK_TYPES = _chunk7KDOTBRXcjs.DEFAULT_KANBAN_WORK_TYPES; exports.KanbanError = _chunkRSH6QSECcjs.KanbanError; exports.KanbanScanNotStartedError = _chunkRSH6QSECcjs.KanbanScanNotStartedError; exports.SeqKanbanStore = _chunkVMRDXOLXcjs.SeqKanbanStore; exports.TASK_START_DELAY_MS = _chunkAOHQCX7Kcjs.TASK_START_DELAY_MS; exports.createKanban = createKanban; exports.createKanbanModule = _chunkDINLR6E6cjs.createKanbanModule; exports.isKanbanEventRelevant = isKanbanEventRelevant; exports.kanbanReference = _chunk7KDOTBRXcjs.kanbanReference; exports.projectKanbanTask = _chunkAOHQCX7Kcjs.projectKanbanTask; exports.taskDue = _chunkAOHQCX7Kcjs.taskDue; exports.taskEditing = _chunkAOHQCX7Kcjs.taskEditing; exports.taskStartAt = _chunkAOHQCX7Kcjs.taskStartAt;
76
74
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/li/share/prj/x1/release/local-libraries/work/1ca933a0-6fe7-4e9c-b13b-76d7f318e0fb/source/libraries/kanban/dist/index.cjs","../src/api.ts","../src/event-policy.ts"],"names":[],"mappings":"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","file":"/home/li/share/prj/x1/release/local-libraries/work/1ca933a0-6fe7-4e9c-b13b-76d7f318e0fb/source/libraries/kanban/dist/index.cjs","sourcesContent":[null,"import {createKanbanModule} from './module';\nimport type {KanbanModuleOptions} from './ports';\nimport type {KanbanRequest} from './contracts';\n\nexport interface KanbanCommit {boardId: string; operationId: string; revision: number}\nexport type Kanban = ReturnType<typeof createKanban>;\n\n/** Grouped public API; all writes still enter the same authorized transaction path. */\nexport function createKanban(options: KanbanModuleOptions) {\n const service = createKanbanModule(options);\n return {\n boards: {create: service.create, get: service.read, list: service.catalog, overview: service.overview},\n items: {get: service.item, query: service.query},\n records: {get: service.record, query: service.records},\n planning: {get: service.workflow},\n references: {resolve: service.resolve, suggest: service.suggest},\n currentPrincipal: service.currentPrincipal,\n async execute(boardId: string, request: KanbanRequest): Promise<KanbanCommit> {\n const board = await service.execute(boardId, request);\n return {boardId, operationId: request.operationId, revision: board.revision};\n },\n events: service.events,\n tick: service.scan,\n quiesce: () => service.quiesce(),\n dispose: () => service.dispose(),\n };\n}\n","import type {KanbanEvent, KanbanBoard} from './contracts';\nimport {terminal} from './planning';\nimport {escalationReasons} from './reducer';\n\nexport function isKanbanEventRelevant(event: KanbanEvent, board: KanbanBoard, now: number): boolean {\n const item = board.items.find(i => i.id === event.itemId);\n if (event.kind === 'escalated') return !!item && !terminal(item) && (event.data.reason === 'transfer-timeout'\n ? !item.transfer && item.status === 'claimed' : escalationReasons(board, item, now).includes(event.data.reason ?? ''));\n if (event.kind === 'submit') return item?.status === 'review';\n if (event.kind === 'request-transfer') return !!item?.transfer && item.transfer.expiresAt > now;\n if (event.kind === 'publish-proposal') return item?.status === 'open';\n return true;\n}\n"]}
1
+ {"version":3,"sources":["/home/li/share/prj/x1/libraries/kanban/dist/index.cjs","../src/api.ts","../src/event-policy.ts"],"names":[],"mappings":"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","file":"/home/li/share/prj/x1/libraries/kanban/dist/index.cjs","sourcesContent":[null,"import {createKanbanModule} from './module';\nimport type {KanbanModuleOptions} from './ports';\nimport type {KanbanRequest} from './contracts';\n\nexport interface KanbanCommit {boardId: string; operationId: string; revision: number}\nexport type Kanban = ReturnType<typeof createKanban>;\n\n/** Grouped public API; all writes still enter the same authorized transaction path. */\nexport function createKanban(options: KanbanModuleOptions) {\n const service = createKanbanModule(options);\n return {\n boards: {create: service.create, get: service.read, list: service.catalog, overview: service.overview},\n items: {get: service.item, query: service.query},\n records: {get: service.record, query: service.records},\n planning: {get: service.workflow},\n references: {resolve: service.resolve, suggest: service.suggest},\n currentPrincipal: service.currentPrincipal,\n async execute(boardId: string, request: KanbanRequest): Promise<KanbanCommit> {\n const board = await service.execute(boardId, request);\n return {boardId, operationId: request.operationId, revision: board.revision};\n },\n events: service.events,\n tick: service.scan,\n quiesce: () => service.quiesce(),\n dispose: () => service.dispose(),\n };\n}\n","import type {KanbanEvent, KanbanBoard} from './contracts';\nimport {terminal} from './planning';\nimport {escalationReasons} from './reducer';\n\nexport function isKanbanEventRelevant(event: KanbanEvent, board: KanbanBoard, now: number): boolean {\n const item = board.items.find(i => i.id === event.itemId);\n if (event.kind === 'escalated') return !!item && !terminal(item) && (event.data.reason === 'transfer-timeout'\n ? !item.transfer && item.status === 'claimed' : escalationReasons(board, item, now).includes(event.data.reason ?? ''));\n if (event.kind === 'submit') return item?.status === 'review';\n if (event.kind === 'request-transfer') return !!item?.transfer && item.transfer.expiresAt > now;\n if (event.kind === 'publish-proposal') return item?.status === 'open';\n return true;\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,11 +1,12 @@
1
- export { K as KanbanModuleHandle, c as createKanbanModule } from './module-BbvwdCDF.cjs';
2
- export { K as KanbanBodyStore, S as SeqKanbanStore } from './store-q63eSMoT.cjs';
3
- import { K as KanbanModuleOptions } from './ports-CaL6geqR.cjs';
4
- export { i as KanbanError, b as KanbanReadView, g as KanbanScanNotStartedError, c as KanbanState, a as KanbanStore } from './ports-CaL6geqR.cjs';
5
- import { l as KanbanBoardInput, j as KanbanBoard, m as KanbanBoardSummary, E as KanbanOverview, w as KanbanItem, a1 as KanbanTaskProjection, c as KanbanArchivedItem, y as KanbanItemQuery, x as KanbanItemPage, J as KanbanProjectRecord, P as KanbanRecordQuery, O as KanbanRecordPage, a7 as KanbanWorkflowState, R as KanbanReferenceTarget, Y as KanbanSuggestionQuery, X as KanbanSuggestionPage, I as KanbanPrincipal, S as KanbanRequest, t as KanbanEvent, z as KanbanItemSummary } from './planning-CcFZYdIL.cjs';
6
- export { D as DEFAULT_KANBAN_WORK_TYPES, K as KanbanActor, a as KanbanAmendment, d as KanbanArtifactRef, e as KanbanAssignment, n as KanbanColumn, o as KanbanCommand, p as KanbanDiscoveryBatch, q as KanbanDiscoveryChange, r as KanbanDispatch, s as KanbanEstimate, u as KanbanExternalReference, B as KanbanMember, C as KanbanNotificationPolicy, F as KanbanParticipant, G as KanbanPlanning, H as KanbanPolicy, L as KanbanPublishedPlan, N as KanbanRecordInput, Q as KanbanRecordSummary, T as KanbanService, U as KanbanSourceRef, V as KanbanSuggestion, W as KanbanSuggestionKind, a2 as KanbanText, a3 as KanbanWorkDefinition, a4 as KanbanWorkPlan, a5 as KanbanWorkType, a6 as KanbanWorkflowCommand, ab as kanbanReference } from './planning-CcFZYdIL.cjs';
1
+ export { K as KanbanModuleHandle, c as createKanbanModule } from './module-Ca9L3qCn.cjs';
2
+ export { K as KanbanBodyStore, S as SeqKanbanStore } from './store-DPwHzGiz.cjs';
3
+ import { K as KanbanModuleOptions } from './ports-GQy0x-RI.cjs';
4
+ export { a as KanbanError, b as KanbanReadView, c as KanbanScanNotStartedError, d as KanbanState, e as KanbanStore } from './ports-GQy0x-RI.cjs';
5
+ import { l as KanbanBoardInput, j as KanbanBoard, m as KanbanBoardSummary, E as KanbanOverview, w as KanbanItem, a1 as KanbanTaskProjection, c as KanbanArchivedItem, y as KanbanItemQuery, x as KanbanItemPage, J as KanbanProjectRecord, P as KanbanRecordQuery, O as KanbanRecordPage, a7 as KanbanWorkflowState, R as KanbanReferenceTarget, Y as KanbanSuggestionQuery, X as KanbanSuggestionPage, I as KanbanPrincipal, S as KanbanRequest, t as KanbanEvent } from './planning-CHrOqQ6b.cjs';
6
+ export { D as DEFAULT_KANBAN_WORK_TYPES, K as KanbanActor, a as KanbanAmendment, d as KanbanArtifactRef, e as KanbanAssignment, n as KanbanColumn, o as KanbanCommand, p as KanbanDiscoveryBatch, q as KanbanDiscoveryChange, r as KanbanDispatch, s as KanbanEstimate, u as KanbanExternalReference, z as KanbanItemSummary, B as KanbanMember, C as KanbanNotificationPolicy, F as KanbanParticipant, G as KanbanPlanning, H as KanbanPolicy, L as KanbanPublishedPlan, N as KanbanRecordInput, Q as KanbanRecordSummary, T as KanbanService, U as KanbanSourceRef, V as KanbanSuggestion, W as KanbanSuggestionKind, a2 as KanbanText, a3 as KanbanWorkDefinition, a4 as KanbanWorkPlan, a5 as KanbanWorkType, a6 as KanbanWorkflowCommand, ab as kanbanReference } from './planning-CHrOqQ6b.cjs';
7
+ export { T as TASK_START_DELAY_MS, p as projectKanbanTask, t as taskDue, a as taskEditing, b as taskStartAt } from './task-projection-DKKB_ueb.cjs';
7
8
  import '@nestos/vfs-core';
8
- import './discovery-DxpirOz3.cjs';
9
+ import './discovery-9E-LfwIw.cjs';
9
10
 
10
11
  interface KanbanCommit {
11
12
  boardId: string;
@@ -50,12 +51,4 @@ declare function createKanban(options: KanbanModuleOptions): {
50
51
 
51
52
  declare function isKanbanEventRelevant(event: KanbanEvent, board: KanbanBoard, now: number): boolean;
52
53
 
53
- type Item = Pick<KanbanItem, 'id' | 'status' | 'createdAt' | 'launch' | 'analysis' | 'control' | 'issues' | 'blocked' | 'externalRefs' | 'planning'>;
54
- declare const TASK_START_DELAY_MS = 300000;
55
- declare function taskStartAt(item: Pick<Item, 'createdAt' | 'launch'>): number;
56
- declare function taskEditing(item: Pick<Item, 'launch'>, now: number): boolean;
57
- declare function taskDue(item: Item, now: number): boolean;
58
- /** Ordered evidence projection shared by storage queries, CLI and UI. Session idle never proves acceptance. */
59
- declare function projectKanbanTask(item: Item, workflow?: KanbanWorkflowState, now?: number, all?: Array<Pick<KanbanItemSummary, 'id' | 'status'>>): KanbanTaskProjection;
60
-
61
- export { type Kanban, KanbanArchivedItem, KanbanBoard, KanbanBoardInput, KanbanBoardSummary, type KanbanCommit, KanbanEvent, KanbanItem, KanbanItemPage, KanbanItemQuery, KanbanItemSummary, KanbanModuleOptions, KanbanOverview, KanbanPrincipal, KanbanProjectRecord, KanbanRecordPage, KanbanRecordQuery, KanbanReferenceTarget, KanbanRequest, KanbanSuggestionPage, KanbanSuggestionQuery, KanbanWorkflowState, TASK_START_DELAY_MS, createKanban, isKanbanEventRelevant, projectKanbanTask, taskDue, taskEditing, taskStartAt };
54
+ export { type Kanban, KanbanArchivedItem, KanbanBoard, KanbanBoardInput, KanbanBoardSummary, type KanbanCommit, KanbanEvent, KanbanItem, KanbanItemPage, KanbanItemQuery, KanbanModuleOptions, KanbanOverview, KanbanPrincipal, KanbanProjectRecord, KanbanRecordPage, KanbanRecordQuery, KanbanReferenceTarget, KanbanRequest, KanbanSuggestionPage, KanbanSuggestionQuery, KanbanWorkflowState, createKanban, isKanbanEventRelevant };