@nestos/kanban 0.0.0-stage → 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 (124) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +165 -2
  3. package/dist/activity-BbC4sgKL.d.cts +258 -0
  4. package/dist/activity-Czlan5xW.d.ts +258 -0
  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 +16 -0
  10. package/dist/activity.cjs.map +1 -0
  11. package/dist/activity.d.cts +12 -0
  12. package/dist/activity.d.ts +12 -0
  13. package/dist/activity.js +16 -0
  14. package/dist/activity.js.map +1 -0
  15. package/dist/chunk-3JYLJPE5.js +95 -0
  16. package/dist/chunk-3JYLJPE5.js.map +1 -0
  17. package/dist/chunk-3LEIHB7Y.js +221 -0
  18. package/dist/chunk-3LEIHB7Y.js.map +1 -0
  19. package/dist/chunk-4XF6LQSU.cjs +9 -0
  20. package/dist/chunk-4XF6LQSU.cjs.map +1 -0
  21. package/dist/chunk-5Q7WB63A.cjs +282 -0
  22. package/dist/chunk-5Q7WB63A.cjs.map +1 -0
  23. package/dist/chunk-7KDOTBRX.cjs +58 -0
  24. package/dist/chunk-7KDOTBRX.cjs.map +1 -0
  25. package/dist/chunk-AHHEGTUA.js +148 -0
  26. package/dist/chunk-AHHEGTUA.js.map +1 -0
  27. package/dist/chunk-AOHQCX7K.cjs +95 -0
  28. package/dist/chunk-AOHQCX7K.cjs.map +1 -0
  29. package/dist/chunk-DINLR6E6.cjs +443 -0
  30. package/dist/chunk-DINLR6E6.cjs.map +1 -0
  31. package/dist/chunk-EYTVC2H5.cjs +1612 -0
  32. package/dist/chunk-EYTVC2H5.cjs.map +1 -0
  33. package/dist/chunk-FEE2YBZ7.js +1972 -0
  34. package/dist/chunk-FEE2YBZ7.js.map +1 -0
  35. package/dist/chunk-FNO5K3A7.js +282 -0
  36. package/dist/chunk-FNO5K3A7.js.map +1 -0
  37. package/dist/chunk-IHHDXZPZ.js +382 -0
  38. package/dist/chunk-IHHDXZPZ.js.map +1 -0
  39. package/dist/chunk-J3EAIVA3.js +280 -0
  40. package/dist/chunk-J3EAIVA3.js.map +1 -0
  41. package/dist/chunk-KH3WF3K4.js +443 -0
  42. package/dist/chunk-KH3WF3K4.js.map +1 -0
  43. package/dist/chunk-M35X5PHO.js +196 -0
  44. package/dist/chunk-M35X5PHO.js.map +1 -0
  45. package/dist/chunk-QRGZD2JU.cjs +1972 -0
  46. package/dist/chunk-QRGZD2JU.cjs.map +1 -0
  47. package/dist/chunk-RBOER6EW.cjs +221 -0
  48. package/dist/chunk-RBOER6EW.cjs.map +1 -0
  49. package/dist/chunk-RQP77ZTY.js +9 -0
  50. package/dist/chunk-RQP77ZTY.js.map +1 -0
  51. package/dist/chunk-RSH6QSEC.cjs +196 -0
  52. package/dist/chunk-RSH6QSEC.cjs.map +1 -0
  53. package/dist/chunk-VMRDXOLX.cjs +148 -0
  54. package/dist/chunk-VMRDXOLX.cjs.map +1 -0
  55. package/dist/chunk-X4UHZX6F.js +58 -0
  56. package/dist/chunk-X4UHZX6F.js.map +1 -0
  57. package/dist/chunk-XBSGUVCY.cjs +382 -0
  58. package/dist/chunk-XBSGUVCY.cjs.map +1 -0
  59. package/dist/chunk-XKDZHAUE.js +1612 -0
  60. package/dist/chunk-XKDZHAUE.js.map +1 -0
  61. package/dist/chunk-ZUZGAO6G.cjs +280 -0
  62. package/dist/chunk-ZUZGAO6G.cjs.map +1 -0
  63. package/dist/content-C5aJZFlw.d.cts +35 -0
  64. package/dist/content-hLH5BNud.d.ts +35 -0
  65. package/dist/contracts/index.cjs +31 -0
  66. package/dist/contracts/index.cjs.map +1 -0
  67. package/dist/contracts/index.d.cts +5 -0
  68. package/dist/contracts/index.d.ts +5 -0
  69. package/dist/contracts/index.js +31 -0
  70. package/dist/contracts/index.js.map +1 -0
  71. package/dist/discovery-9E-LfwIw.d.cts +61 -0
  72. package/dist/discovery-D60yrjej.d.ts +61 -0
  73. package/dist/discovery.cjs +377 -0
  74. package/dist/discovery.cjs.map +1 -0
  75. package/dist/discovery.d.cts +22 -0
  76. package/dist/discovery.d.ts +22 -0
  77. package/dist/discovery.js +377 -0
  78. package/dist/discovery.js.map +1 -0
  79. package/dist/execution-bridge-fpCnH6z5.d.cts +52 -0
  80. package/dist/execution-bridge-rk0uVy_n.d.ts +52 -0
  81. package/dist/execution.cjs +18 -0
  82. package/dist/execution.cjs.map +1 -0
  83. package/dist/execution.d.cts +42 -0
  84. package/dist/execution.d.ts +42 -0
  85. package/dist/execution.js +18 -0
  86. package/dist/execution.js.map +1 -0
  87. package/dist/index.cjs +74 -0
  88. package/dist/index.cjs.map +1 -0
  89. package/dist/index.d.cts +54 -0
  90. package/dist/index.d.ts +54 -0
  91. package/dist/index.js +74 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/module-CYfE7YhM.d.ts +11 -0
  94. package/dist/module-Ca9L3qCn.d.cts +11 -0
  95. package/dist/planning-CHrOqQ6b.d.cts +879 -0
  96. package/dist/planning-CHrOqQ6b.d.ts +879 -0
  97. package/dist/ports-B0McVg7B.d.ts +143 -0
  98. package/dist/ports-BeGO1VRI.d.cts +49 -0
  99. package/dist/ports-GQy0x-RI.d.cts +143 -0
  100. package/dist/ports-ykGiNUSx.d.ts +49 -0
  101. package/dist/runtime/index.cjs +205 -0
  102. package/dist/runtime/index.cjs.map +1 -0
  103. package/dist/runtime/index.d.cts +75 -0
  104. package/dist/runtime/index.d.ts +75 -0
  105. package/dist/runtime/index.js +205 -0
  106. package/dist/runtime/index.js.map +1 -0
  107. package/dist/runtime/integration.cjs +423 -0
  108. package/dist/runtime/integration.cjs.map +1 -0
  109. package/dist/runtime/integration.d.cts +658 -0
  110. package/dist/runtime/integration.d.ts +658 -0
  111. package/dist/runtime/integration.js +423 -0
  112. package/dist/runtime/integration.js.map +1 -0
  113. package/dist/storage.cjs +21 -0
  114. package/dist/storage.cjs.map +1 -0
  115. package/dist/storage.d.cts +71 -0
  116. package/dist/storage.d.ts +71 -0
  117. package/dist/storage.js +21 -0
  118. package/dist/storage.js.map +1 -0
  119. package/dist/store-DPwHzGiz.d.cts +28 -0
  120. package/dist/store-DgdrPCV9.d.ts +28 -0
  121. package/dist/task-projection-CSorlMws.d.ts +11 -0
  122. package/dist/task-projection-DKKB_ueb.d.cts +11 -0
  123. package/examples/standalone.ts +22 -0
  124. package/package.json +143 -4
@@ -0,0 +1,221 @@
1
+ import {
2
+ DEFAULT_KANBAN_WORK_TYPES,
3
+ executableWorkItem
4
+ } from "./chunk-X4UHZX6F.js";
5
+ import {
6
+ KanbanError
7
+ } from "./chunk-M35X5PHO.js";
8
+
9
+ // src/discovery-state.ts
10
+ function discoveryState(state) {
11
+ return state?.discovery;
12
+ }
13
+
14
+ // src/configuration.ts
15
+ function policy(input = {}) {
16
+ return {
17
+ claimTimeoutMs: 864e5,
18
+ leaseMs: 6e5,
19
+ progressTimeoutMs: 36e5,
20
+ transferTimeoutMs: 18e5,
21
+ maxComplexity: 10,
22
+ maxWorkUnits: 100,
23
+ maxDurationMs: 288e5,
24
+ escalationSessionIds: [],
25
+ ...input
26
+ };
27
+ }
28
+ function workTypes(board) {
29
+ return board.workTypes ?? DEFAULT_KANBAN_WORK_TYPES;
30
+ }
31
+ function workType(board, id) {
32
+ const found = workTypes(board).find((type) => type.id === id);
33
+ requireValue(found, "UNKNOWN_WORK_TYPE");
34
+ return found;
35
+ }
36
+ function executable(board, item) {
37
+ workType(board, item.planning?.kind ?? board.defaultWorkType ?? "task");
38
+ return executableWorkItem(board, item);
39
+ }
40
+ function validateConfiguration(board) {
41
+ const types = workTypes(board);
42
+ requireValue(types.length > 0 && types.length <= 20, "WORK_TYPE_LIMIT");
43
+ requireValue(new Set(types.map((t) => t.id)).size === types.length, "DUPLICATE_WORK_TYPE");
44
+ for (const type of types) {
45
+ identity(type.id);
46
+ text(type.label);
47
+ requireValue(Number.isInteger(type.rank) && type.rank >= 0 && type.rank <= 9 && typeof type.executable === "boolean", "INVALID_WORK_TYPE");
48
+ }
49
+ workType(board, board.defaultWorkType ?? "task");
50
+ const members = roster(board);
51
+ requireValue(members.length > 0, "MEMBER_ROSTER_REQUIRED");
52
+ requireValue(new Set(members.map(principalKey)).size === members.length, "DUPLICATE_MEMBER");
53
+ for (const member of members) {
54
+ identity(member.id);
55
+ requireValue(["agent", "human", "service"].includes(member.kind), "INVALID_PRINCIPAL");
56
+ }
57
+ for (const member of board.members ?? []) {
58
+ text(member.responsibility);
59
+ requireValue(!member.roles || member.roles.every((r) => ["worker", "reviewer", "coordinator"].includes(r)), "INVALID_MEMBER_ROLE");
60
+ }
61
+ validateColumns(board);
62
+ for (const [kind, value] of Object.entries(board.notifications?.rules ?? {}))
63
+ requireValue(/^[a-z][a-z0-9.-]{0,79}$/.test(kind) && ["info", "error", "none"].includes(value), "INVALID_NOTIFICATION_RULE");
64
+ }
65
+ function validateColumns(board) {
66
+ if (!board.columns) return;
67
+ const states = board.columns.flatMap((c) => c.states);
68
+ requireValue(states.length === 7 && new Set(states).size === 7 && ["proposed", "backlog", "open", "claimed", "review", "completed", "cancelled"].every((s) => states.includes(s)), "INVALID_COLUMNS");
69
+ requireValue(new Set(board.columns.map((c) => c.id)).size === board.columns.length, "DUPLICATE_COLUMN");
70
+ for (const column of board.columns) {
71
+ identity(column.id);
72
+ text(column.label);
73
+ requireValue(column.states.length > 0, "EMPTY_COLUMN");
74
+ }
75
+ }
76
+
77
+ // src/rules.ts
78
+ function requireValue(condition, code) {
79
+ if (!condition) throw new KanbanError(code);
80
+ }
81
+ function text(value) {
82
+ requireValue(typeof value === "string" && value.trim().length > 0 && value.length <= 8e3, "INVALID_TEXT");
83
+ }
84
+ function identity(value) {
85
+ requireValue(typeof value === "string" && value.length > 0 && value.length <= 256 && !["__proto__", "prototype", "constructor"].includes(value), "INVALID_ID");
86
+ }
87
+ function positive(value) {
88
+ requireValue(Number.isFinite(value) && value > 0, "INVALID_THRESHOLD");
89
+ }
90
+ function validateEstimate(estimate) {
91
+ positive(estimate.complexity);
92
+ positive(estimate.workUnits);
93
+ positive(estimate.durationMs);
94
+ requireValue(["low", "medium", "high"].includes(estimate.confidence), "INVALID_CONFIDENCE");
95
+ }
96
+ function validateBoard(board) {
97
+ identity(board.id);
98
+ text(board.title);
99
+ requireValue(["system", "project", "collection"].includes(board.scope.kind), "INVALID_SCOPE");
100
+ if (board.scope.kind === "project") identity(board.scope.projectId);
101
+ if (board.scope.kind === "collection") {
102
+ identity(board.scope.namespace);
103
+ identity(board.scope.id);
104
+ }
105
+ validateConfiguration(board);
106
+ for (const agent of board.agents ?? []) {
107
+ identity(agent.agentId);
108
+ text(agent.responsibility);
109
+ }
110
+ requireValue(new Set((board.agents ?? []).map((a) => a.agentId)).size === (board.agents ?? []).length, "DUPLICATE_AGENT");
111
+ const p = board.policy;
112
+ for (const value of [p.maxReady, p.maxInProgress, p.maxInProgressPerAgent, p.maxInProgressPerAssignee]) if (value !== void 0) requireValue(Number.isSafeInteger(value) && value > 0, "INVALID_LIMIT");
113
+ requireValue(p.maxInProgressPerAgent === void 0 || p.maxInProgressPerAssignee === void 0 || p.maxInProgressPerAgent === p.maxInProgressPerAssignee, "POLICY_LIMIT_CONFLICT");
114
+ for (const value of [p.archiveAfterMs, p.backlogReviewMs]) if (value !== void 0) positive(value);
115
+ for (const value of [
116
+ p.claimTimeoutMs,
117
+ p.leaseMs,
118
+ p.progressTimeoutMs,
119
+ p.transferTimeoutMs,
120
+ p.maxComplexity,
121
+ p.maxWorkUnits,
122
+ p.maxDurationMs
123
+ ]) positive(value);
124
+ requireValue(Array.isArray(p.escalationSessionIds), "INVALID_ESCALATION_TARGETS");
125
+ requireValue(p.review === void 0 || ["independent", "assignee"].includes(p.review), "INVALID_REVIEW_POLICY");
126
+ requireValue(p.requireEstimate === void 0 || typeof p.requireEstimate === "boolean", "INVALID_ESTIMATE_POLICY");
127
+ p.escalationSessionIds.forEach(identity);
128
+ }
129
+ function participant(list, sessionId, agentId, role, subscribed, now) {
130
+ identity(sessionId);
131
+ requireValue(["creator", "owner", "consultant", "reviewer", "observer", "escalation"].includes(role), "INVALID_ROLE");
132
+ let entry = list.find((value) => value.sessionId === sessionId);
133
+ if (!entry) {
134
+ entry = { sessionId, agentId, roles: [], subscribed, joinedAt: now };
135
+ list.push(entry);
136
+ }
137
+ if (entry.agentId && agentId) requireValue(entry.agentId === agentId, "SESSION_AGENT_CONFLICT");
138
+ entry.agentId ??= agentId;
139
+ if (!entry.roles.includes(role)) entry.roles.push(role);
140
+ entry.subscribed = subscribed;
141
+ }
142
+ function owned(item, actor, token, now) {
143
+ requireValue(item.status === "claimed", "ITEM_NOT_RUNNING");
144
+ requireValue(item.owner && sameOwner(item.owner, actor) && item.owner.token === token, "OWNER_MISMATCH");
145
+ requireValue(item.owner.leaseUntil > now, "LEASE_EXPIRED");
146
+ }
147
+
148
+ // src/identity.ts
149
+ function principal(actor) {
150
+ const kind = actor.kind ?? "agent", id = kind === "agent" ? actor.agentId ?? actor.id : actor.id;
151
+ requireValue(["agent", "human", "service"].includes(kind) && id, "INVALID_ACTOR");
152
+ identity(id);
153
+ if (kind === "agent") {
154
+ requireValue(!actor.agentId || !actor.id || actor.agentId === actor.id, "ACTOR_ID_MISMATCH");
155
+ if (actor.sessionId !== void 0) identity(actor.sessionId);
156
+ if (actor.taskId !== void 0) identity(actor.taskId);
157
+ }
158
+ return { kind, id };
159
+ }
160
+ function principalKey(value) {
161
+ return JSON.stringify([value.kind, value.id]);
162
+ }
163
+ function assigneeKey(value) {
164
+ return value.principalId ?? principalKey({ kind: "agent", id: value.agentId });
165
+ }
166
+ function sameOwner(owner, actor) {
167
+ const value = principal(actor);
168
+ return assigneeKey(owner) === principalKey(value) && (value.kind !== "agent" || owner.sessionId === actor.sessionId && owner.taskId === actor.taskId);
169
+ }
170
+ function roster(board) {
171
+ return [...(board.agents ?? []).map((a) => ({ kind: "agent", id: a.agentId })), ...board.members ?? []];
172
+ }
173
+ function candidates(board, item) {
174
+ if (item.eligibleAgentIds === void 0 && item.eligiblePrincipals === void 0)
175
+ return [
176
+ ...(board.agents ?? []).map((a) => ({ kind: "agent", id: a.agentId })),
177
+ ...(board.members ?? []).filter((m) => !m.roles || m.roles.includes("worker"))
178
+ ];
179
+ return [...(item.eligibleAgentIds ?? []).map((id) => ({ kind: "agent", id })), ...item.eligiblePrincipals ?? []];
180
+ }
181
+ function eligible(board, item, actor) {
182
+ const key = principalKey(principal(actor));
183
+ requireValue(candidates(board, item).some((p) => principalKey(p) === key), actor.kind && actor.kind !== "agent" ? "ASSIGNEE_NOT_ELIGIBLE" : "AGENT_NOT_ELIGIBLE");
184
+ }
185
+ function validateCandidates(board, item, executable2) {
186
+ const allowed = new Set(roster(board).map(principalKey)), values = candidates(board, item);
187
+ requireValue(!executable2 || values.length > 0, "ELIGIBLE_ASSIGNEES_REQUIRED");
188
+ for (const value of values) {
189
+ identity(value.id);
190
+ requireValue(["agent", "human", "service"].includes(value.kind), "INVALID_PRINCIPAL");
191
+ requireValue(allowed.has(principalKey(value)), value.kind === "agent" ? "AGENT_NOT_REGISTERED" : "ASSIGNEE_NOT_REGISTERED");
192
+ }
193
+ }
194
+ function normalizeActor(actor) {
195
+ const value = principal(actor);
196
+ return value.kind === "agent" ? { agentId: value.id, sessionId: actor.sessionId, taskId: actor.taskId } : { kind: value.kind, id: value.id };
197
+ }
198
+
199
+ export {
200
+ principal,
201
+ principalKey,
202
+ assigneeKey,
203
+ sameOwner,
204
+ candidates,
205
+ eligible,
206
+ validateCandidates,
207
+ normalizeActor,
208
+ policy,
209
+ workType,
210
+ executable,
211
+ requireValue,
212
+ text,
213
+ identity,
214
+ positive,
215
+ validateEstimate,
216
+ validateBoard,
217
+ participant,
218
+ owned,
219
+ discoveryState
220
+ };
221
+ //# sourceMappingURL=chunk-3LEIHB7Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/discovery-state.ts","../src/configuration.ts","../src/rules.ts","../src/identity.ts"],"sourcesContent":["import type {KanbanDiscoveryState} from './scan-ports';\n\nexport function discoveryState(state: {discovery?: unknown} | null | undefined): KanbanDiscoveryState | undefined {\n return state?.discovery as KanbanDiscoveryState | undefined;\n}\n","import {DEFAULT_KANBAN_WORK_TYPES, executableWorkItem} from './contracts';\nimport type {KanbanBoard, KanbanPolicy, KanbanWorkType} from './contracts';\nimport {identity, requireValue, text} from './rules';\nimport {principalKey, roster} from './identity';\n\nexport function policy(input: Partial<KanbanPolicy> = {}): KanbanPolicy {\n return {claimTimeoutMs: 86400000, leaseMs: 600000, progressTimeoutMs: 3600000, transferTimeoutMs: 1800000,\n maxComplexity: 10, maxWorkUnits: 100, maxDurationMs: 28800000, escalationSessionIds: [], ...input};\n}\nexport function workTypes(board: KanbanBoard): readonly KanbanWorkType[] {return board.workTypes ?? DEFAULT_KANBAN_WORK_TYPES;}\nexport function workType(board: KanbanBoard, id: string): KanbanWorkType {\n const found = workTypes(board).find(type => type.id === id); requireValue(found, 'UNKNOWN_WORK_TYPE'); return found;\n}\nexport function executable(board: KanbanBoard, item: {planning?: {kind: string; execution?: 'direct'|'children'}}): boolean {\n workType(board, item.planning?.kind ?? board.defaultWorkType ?? 'task');\n return executableWorkItem(board,item);\n}\nexport function validateConfiguration(board: KanbanBoard): void {\n const types = workTypes(board); requireValue(types.length > 0 && types.length <= 20, 'WORK_TYPE_LIMIT');\n requireValue(new Set(types.map(t => t.id)).size === types.length, 'DUPLICATE_WORK_TYPE');\n for (const type of types) {\n identity(type.id); text(type.label); requireValue(Number.isInteger(type.rank) && type.rank >= 0 && type.rank <= 9 && typeof type.executable === 'boolean', 'INVALID_WORK_TYPE');\n }\n workType(board, board.defaultWorkType ?? 'task');\n const members = roster(board); requireValue(members.length > 0, 'MEMBER_ROSTER_REQUIRED');\n requireValue(new Set(members.map(principalKey)).size === members.length, 'DUPLICATE_MEMBER');\n for (const member of members) {identity(member.id); requireValue(['agent', 'human', 'service'].includes(member.kind), 'INVALID_PRINCIPAL');}\n for (const member of board.members ?? []) {\n text(member.responsibility); requireValue(!member.roles || member.roles.every(r => ['worker', 'reviewer', 'coordinator'].includes(r)), 'INVALID_MEMBER_ROLE');\n }\n validateColumns(board);\n for (const [kind, value] of Object.entries(board.notifications?.rules ?? {}))\n requireValue(/^[a-z][a-z0-9.-]{0,79}$/.test(kind) && ['info', 'error', 'none'].includes(value), 'INVALID_NOTIFICATION_RULE');\n}\nfunction validateColumns(board: KanbanBoard): void {\n if (!board.columns) return;\n const states = board.columns.flatMap(c => c.states);\n requireValue(states.length === 7 && new Set(states).size === 7 && ['proposed', 'backlog', 'open', 'claimed', 'review', 'completed', 'cancelled'].every(s => states.includes(s as never)), 'INVALID_COLUMNS');\n requireValue(new Set(board.columns.map(c => c.id)).size === board.columns.length, 'DUPLICATE_COLUMN');\n for (const column of board.columns) {identity(column.id); text(column.label); requireValue(column.states.length > 0, 'EMPTY_COLUMN');}\n}\n","import type { KanbanActor, KanbanBoard, KanbanEstimate, KanbanItem, KanbanParticipant } from './contracts';\nimport {validateConfiguration} from './configuration';\nimport {sameOwner} from './identity';\nimport { KanbanError } from './ports';\n\nexport function requireValue(condition: unknown, code: string): asserts condition {\n if (!condition) throw new KanbanError(code);\n}\nexport function text(value: string): void { requireValue(typeof value === 'string' && value.trim().length > 0 && value.length <= 8000, 'INVALID_TEXT'); }\nexport function identity(value: string): void { requireValue(typeof value === 'string' && value.length > 0 && value.length <= 256 && !['__proto__', 'prototype', 'constructor'].includes(value), 'INVALID_ID'); }\nexport function positive(value: number): void { requireValue(Number.isFinite(value) && value > 0, 'INVALID_THRESHOLD'); }\nexport function validateEstimate(estimate: KanbanEstimate): void {\n positive(estimate.complexity); positive(estimate.workUnits); positive(estimate.durationMs);\n requireValue(['low', 'medium', 'high'].includes(estimate.confidence), 'INVALID_CONFIDENCE');\n}\nexport function validateBoard(board: KanbanBoard): void {\n identity(board.id); text(board.title);\n requireValue(['system', 'project', 'collection'].includes(board.scope.kind), 'INVALID_SCOPE');\n if (board.scope.kind === 'project') identity(board.scope.projectId);\n if (board.scope.kind === 'collection') {identity(board.scope.namespace); identity(board.scope.id);}\n validateConfiguration(board);\n for (const agent of board.agents ?? []) { identity(agent.agentId); text(agent.responsibility); }\n requireValue(new Set((board.agents ?? []).map(a => a.agentId)).size === (board.agents ?? []).length, 'DUPLICATE_AGENT');\n const p = board.policy;\n for (const value of [p.maxReady, p.maxInProgress, p.maxInProgressPerAgent, p.maxInProgressPerAssignee]) if (value !== undefined) requireValue(Number.isSafeInteger(value) && value > 0, 'INVALID_LIMIT');\n requireValue(p.maxInProgressPerAgent === undefined || p.maxInProgressPerAssignee === undefined || p.maxInProgressPerAgent === p.maxInProgressPerAssignee, 'POLICY_LIMIT_CONFLICT');\n for (const value of [p.archiveAfterMs, p.backlogReviewMs]) if (value !== undefined) positive(value);\n for (const value of [p.claimTimeoutMs, p.leaseMs, p.progressTimeoutMs, p.transferTimeoutMs,\n p.maxComplexity, p.maxWorkUnits, p.maxDurationMs]) positive(value);\n requireValue(Array.isArray(p.escalationSessionIds), 'INVALID_ESCALATION_TARGETS');\n requireValue(p.review === undefined || ['independent', 'assignee'].includes(p.review), 'INVALID_REVIEW_POLICY');\n requireValue(p.requireEstimate === undefined || typeof p.requireEstimate === 'boolean', 'INVALID_ESTIMATE_POLICY');\n p.escalationSessionIds.forEach(identity);\n}\nexport function participant(list: KanbanParticipant[], sessionId: string, agentId: string | undefined,\n role: KanbanParticipant['roles'][number], subscribed: boolean, now: number): void {\n identity(sessionId);\n requireValue(['creator', 'owner', 'consultant', 'reviewer', 'observer', 'escalation'].includes(role), 'INVALID_ROLE');\n let entry = list.find(value => value.sessionId === sessionId);\n if (!entry) { entry = { sessionId, agentId, roles: [], subscribed, joinedAt: now }; list.push(entry); }\n if (entry.agentId && agentId) requireValue(entry.agentId === agentId, 'SESSION_AGENT_CONFLICT');\n entry.agentId ??= agentId;\n if (!entry.roles.includes(role)) entry.roles.push(role);\n entry.subscribed = subscribed;\n}\nexport function owned(item: KanbanItem, actor: KanbanActor, token: string, now: number): void {\n requireValue(item.status === 'claimed', 'ITEM_NOT_RUNNING');\n requireValue(item.owner && sameOwner(item.owner, actor) && item.owner.token === token, 'OWNER_MISMATCH');\n requireValue(item.owner.leaseUntil > now, 'LEASE_EXPIRED');\n}\n","import type {KanbanActor, KanbanAssignment, KanbanBoard, KanbanItem, KanbanPrincipal} from './contracts';\nimport {identity, requireValue} from './rules';\n\nexport function principal(actor: KanbanActor): KanbanPrincipal {\n const kind = actor.kind ?? 'agent', id = kind === 'agent' ? actor.agentId ?? actor.id : actor.id;\n requireValue(['agent', 'human', 'service'].includes(kind) && id, 'INVALID_ACTOR'); identity(id);\n if (kind === 'agent') {requireValue(!actor.agentId || !actor.id || actor.agentId === actor.id, 'ACTOR_ID_MISMATCH'); if (actor.sessionId !== undefined) identity(actor.sessionId); if (actor.taskId !== undefined) identity(actor.taskId);}\n return {kind, id};\n}\nexport function principalKey(value: KanbanPrincipal): string {return JSON.stringify([value.kind, value.id]);}\nexport function assigneeKey(value: KanbanAssignment): string {\n return value.principalId ?? principalKey({kind: 'agent', id: value.agentId!});\n}\nexport function sameOwner(owner: KanbanAssignment, actor: KanbanActor): boolean {\n const value = principal(actor);\n return assigneeKey(owner) === principalKey(value) && (value.kind !== 'agent' || owner.sessionId === actor.sessionId && owner.taskId === actor.taskId);\n}\nexport function roster(board: KanbanBoard): KanbanPrincipal[] {\n return [...(board.agents ?? []).map(a => ({kind: 'agent' as const, id: a.agentId})), ...(board.members ?? [])];\n}\nexport function candidates(board: KanbanBoard, item: KanbanItem): KanbanPrincipal[] {\n if (item.eligibleAgentIds === undefined && item.eligiblePrincipals === undefined)\n return [...(board.agents ?? []).map(a => ({kind: 'agent' as const, id: a.agentId})),\n ...(board.members ?? []).filter(m => !m.roles || m.roles.includes('worker'))];\n return [...(item.eligibleAgentIds ?? []).map(id => ({kind: 'agent' as const, id})), ...(item.eligiblePrincipals ?? [])];\n}\nexport function eligible(board: KanbanBoard, item: KanbanItem, actor: KanbanActor): void {\n const key = principalKey(principal(actor));\n requireValue(candidates(board, item).some(p => principalKey(p) === key), actor.kind && actor.kind !== 'agent' ? 'ASSIGNEE_NOT_ELIGIBLE' : 'AGENT_NOT_ELIGIBLE');\n}\nexport function validateCandidates(board: KanbanBoard, item: KanbanItem, executable: boolean): void {\n const allowed = new Set(roster(board).map(principalKey)), values = candidates(board, item);\n requireValue(!executable || values.length > 0, 'ELIGIBLE_ASSIGNEES_REQUIRED');\n for (const value of values) {\n identity(value.id); requireValue(['agent', 'human', 'service'].includes(value.kind), 'INVALID_PRINCIPAL');\n requireValue(allowed.has(principalKey(value)), value.kind === 'agent' ? 'AGENT_NOT_REGISTERED' : 'ASSIGNEE_NOT_REGISTERED');\n }\n}\n\nexport function normalizeActor(actor: KanbanActor): KanbanActor {\n const value = principal(actor);\n return value.kind === 'agent' ? {agentId: value.id, sessionId: actor.sessionId, taskId: actor.taskId}\n : {kind: value.kind, id: value.id};\n}\n"],"mappings":"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","names":["executable"]}
@@ -0,0 +1,9 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/execution-observation.ts
2
+ function executionObservationChanged(current, next) {
3
+ return current.outcome !== next.outcome || current.code !== next.code || JSON.stringify(_nullishCoalesce(current.outputs, () => ( []))) !== JSON.stringify(_nullishCoalesce(next.outputs, () => ( []))) || next.executionSessionId !== void 0 && current.executionSessionId !== next.executionSessionId || next.controlCapabilities !== void 0 && JSON.stringify(current.controlCapabilities) !== JSON.stringify(next.controlCapabilities);
4
+ }
5
+
6
+
7
+
8
+ exports.executionObservationChanged = executionObservationChanged;
9
+ //# sourceMappingURL=chunk-4XF6LQSU.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/li/share/prj/x1/libraries/kanban/dist/chunk-4XF6LQSU.cjs","../src/execution-observation.ts"],"names":[],"mappings":"AAAA;ACIO,SAAS,2BAAA,CAA4B,OAAA,EAAsB,IAAA,EAA4B;AAC1F,EAAA,OAAO,OAAA,CAAQ,QAAA,IAAY,IAAA,CAAK,QAAA,GAAW,OAAA,CAAQ,KAAA,IAAS,IAAA,CAAK,KAAA,GAC1D,IAAA,CAAK,SAAA,kBAAU,OAAA,CAAQ,OAAA,UAAW,CAAC,GAAC,EAAA,IAAM,IAAA,CAAK,SAAA,kBAAU,IAAA,CAAK,OAAA,UAAW,CAAC,GAAC,EAAA,GAC3E,IAAA,CAAK,mBAAA,IAAuB,KAAA,EAAA,GAAa,OAAA,CAAQ,mBAAA,IAAuB,IAAA,CAAK,mBAAA,GAC7E,IAAA,CAAK,oBAAA,IAAwB,KAAA,EAAA,GAAa,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,mBAAmB,EAAA,IAAM,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,mBAAmB,CAAA;AAC5I;ADLA;AACA;AACE;AACF,kEAAC","file":"/home/li/share/prj/x1/libraries/kanban/dist/chunk-4XF6LQSU.cjs","sourcesContent":[null,"import type {KanbanDispatch} from './contracts';\n\ntype Observation = Pick<KanbanDispatch, 'outcome' | 'code' | 'outputs' | 'executionSessionId' | 'controlCapabilities'>;\n/** A lease renewal is operational state, separate from observable execution changes. */\nexport function executionObservationChanged(current: Observation, next: Observation): boolean {\n return current.outcome !== next.outcome || current.code !== next.code\n || JSON.stringify(current.outputs ?? []) !== JSON.stringify(next.outputs ?? [])\n || next.executionSessionId !== undefined && current.executionSessionId !== next.executionSessionId\n || next.controlCapabilities !== undefined && JSON.stringify(current.controlCapabilities) !== JSON.stringify(next.controlCapabilities);\n}\n"]}
@@ -0,0 +1,282 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await 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; } async function _asyncOptionalChain(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 = await fn(value); } else if (op === 'call' || op === 'optionalCall') { value = await fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+ var _chunkDINLR6E6cjs = require('./chunk-DINLR6E6.cjs');
4
+
5
+
6
+
7
+
8
+ var _chunkRBOER6EWcjs = require('./chunk-RBOER6EW.cjs');
9
+
10
+
11
+
12
+
13
+ var _chunkRSH6QSECcjs = require('./chunk-RSH6QSEC.cjs');
14
+
15
+ // src/session-activity-query.ts
16
+ async function querySessionItems(core, boardId, input, project) {
17
+ const { execution = "all", ...query } = input;
18
+ if (!["all", "running", "recent", "active"].includes(execution)) throw new (0, _chunkRSH6QSECcjs.KanbanError)("INVALID_EXECUTION_FILTER");
19
+ if (execution === "all") {
20
+ const page2 = await core.query(boardId, query);
21
+ return { ...page2, items: await project(page2.items) };
22
+ }
23
+ const { cursor, limit = 50, ...filters } = query;
24
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 200) throw new (0, _chunkRSH6QSECcjs.KanbanError)("INVALID_PAGE_SIZE");
25
+ let page = await core.query(boardId, { ...filters, limit: 200 }), rows = [...page.items];
26
+ const revision = page.revision;
27
+ while (page.nextCursor) {
28
+ if (rows.length >= 1e4) throw new (0, _chunkRSH6QSECcjs.KanbanError)("ACTIVITY_QUERY_LIMIT");
29
+ page = await core.query(boardId, { ...filters, limit: 200, cursor: page.nextCursor });
30
+ if (page.revision !== revision) throw new (0, _chunkRSH6QSECcjs.KanbanError)("CURSOR_STALE");
31
+ rows.push(...page.items);
32
+ }
33
+ const matching = (await project(rows)).filter((row) => matchesExecution(row, execution));
34
+ if ((await core.query(boardId, { ...filters, limit: 1 })).revision !== revision) throw new (0, _chunkRSH6QSECcjs.KanbanError)("CURSOR_STALE");
35
+ const scope = _chunkRSH6QSECcjs.sha256HexSync.call(void 0, JSON.stringify({ filters, execution, limit })), signature = _chunkRSH6QSECcjs.sha256HexSync.call(void 0, JSON.stringify(matching.map((row) => row.id)));
36
+ let start = 0;
37
+ if (cursor) {
38
+ let saved;
39
+ try {
40
+ saved = JSON.parse(cursor);
41
+ } catch (e) {
42
+ throw new (0, _chunkRSH6QSECcjs.KanbanError)("INVALID_CURSOR");
43
+ }
44
+ if (saved.scope !== scope) throw new (0, _chunkRSH6QSECcjs.KanbanError)("CURSOR_SCOPE_MISMATCH");
45
+ if (saved.revision !== revision || saved.signature !== signature) throw new (0, _chunkRSH6QSECcjs.KanbanError)("CURSOR_STALE");
46
+ start = matching.findIndex((row) => row.id === saved.after) + 1;
47
+ if (start < 1) throw new (0, _chunkRSH6QSECcjs.KanbanError)("INVALID_CURSOR");
48
+ }
49
+ const items = matching.slice(start, start + limit);
50
+ return { revision, total: matching.length, items, ...start + items.length < matching.length ? { nextCursor: JSON.stringify({ scope, revision, signature, after: items.at(-1).id }) } : {} };
51
+ }
52
+ function matchesExecution(row, filter) {
53
+ const session = row.execution;
54
+ if (!session || session.stale) return false;
55
+ return filter === "running" ? session.state === "running" : filter === "recent" ? session.recent : session.state === "running" || session.recent;
56
+ }
57
+
58
+ // src/session-relations.ts
59
+ function sessionAssociations(item, bindings, workflow) {
60
+ return [...bindings.map((ref) => {
61
+ const dispatch = workflow.dispatches[_nullishCoalesce(ref.revision, () => ( ""))];
62
+ const execution = _optionalChain([dispatch, 'optionalAccess', _ => _.itemId]) === item.id ? {
63
+ id: dispatch.id,
64
+ purpose: "execution",
65
+ dispatchId: dispatch.id,
66
+ attempt: dispatch.attempt,
67
+ outcome: dispatch.outcome,
68
+ createdAt: dispatch.preparedAt,
69
+ code: dispatch.code,
70
+ ...dispatch.continuation ? { previousDispatchId: dispatch.continuation.previousDispatchId } : {}
71
+ } : void 0;
72
+ return { sessionId: ref.id, purpose: "execution", ...execution ? { execution } : {} };
73
+ }), ...item.participants.filter((row) => !bindings.some((ref) => ref.id === row.sessionId)).map((row) => ({ sessionId: row.sessionId, purpose: "discussion" }))];
74
+ }
75
+ async function projectRelatedSessions(associations, currentId, inspect) {
76
+ const grouped = /* @__PURE__ */ new Map();
77
+ for (const row of associations) {
78
+ let group = grouped.get(row.sessionId);
79
+ if (!group) grouped.set(row.sessionId, group = { purposes: /* @__PURE__ */ new Set(), executions: /* @__PURE__ */ new Map() });
80
+ group.purposes.add(row.purpose);
81
+ if (row.execution) group.executions.set(row.execution.id, { ...group.executions.get(row.execution.id), ...row.execution });
82
+ }
83
+ const rows = [], entries = [...grouped];
84
+ let next = 0;
85
+ await Promise.all(Array.from({ length: Math.min(entries.length, 8) }, async () => {
86
+ while (next < entries.length) {
87
+ const [sessionId, group] = entries[next++], state = await inspect(sessionId);
88
+ rows.push({
89
+ ...state,
90
+ current: sessionId === currentId,
91
+ purposes: [...group.purposes].sort(),
92
+ executions: [...group.executions.values()].sort((a, b) => (_nullishCoalesce(a.createdAt, () => ( 0))) - (_nullishCoalesce(b.createdAt, () => ( 0))) || a.id.localeCompare(b.id))
93
+ });
94
+ }
95
+ }));
96
+ return rows.sort((a, b) => Number(b.current) - Number(a.current) || a.sessionId.localeCompare(b.sessionId));
97
+ }
98
+
99
+ // src/session-activity.ts
100
+ function executionBindings(refs, referenceSystem) {
101
+ return (_nullishCoalesce(refs, () => ( []))).filter((ref) => ref.system === referenceSystem);
102
+ }
103
+ function currentBinding(refs, itemId, workflow, referenceSystem) {
104
+ const binding = executionBindings(refs, referenceSystem).at(-1), latest = Object.values(workflow.dispatches).filter((dispatch) => dispatch.itemId === itemId).sort((a, b) => b.attempt - a.attempt)[0];
105
+ if (!binding || !latest || binding.revision === latest.id) return binding;
106
+ return ["prepared", "running", "unknown", "succeeded"].includes(latest.outcome) || workflow.dispatches[_nullishCoalesce(binding.revision, () => ( ""))] ? void 0 : binding;
107
+ }
108
+ function createKanbanSessionActivity(core, options, log, referenceSystem) {
109
+ const inspect = createSessionInspector(options, log);
110
+ const project = async (boardId, rows) => {
111
+ if (!rows.some((row) => executionBindings(row.externalRefs, referenceSystem).length)) return rows;
112
+ const [board, workflow] = await Promise.all([boardMetadata(core, boardId), core.workflow(boardId)]), bindings = new Map(rows.map((row) => [row.id, currentBinding(row.externalRefs, row.id, workflow, referenceSystem)]));
113
+ const ids = [...new Set([...bindings.values()].flatMap((ref) => ref ? [ref.id] : []))], states = /* @__PURE__ */ new Map();
114
+ let next = 0;
115
+ await Promise.all(Array.from({ length: Math.min(ids.length, 8) }, async () => {
116
+ while (next < ids.length) {
117
+ const id = ids[next++];
118
+ states.set(id, await inspect(board, id));
119
+ }
120
+ }));
121
+ return rows.map((row) => {
122
+ const binding = bindings.get(row.id);
123
+ return { ...row, ...binding ? { execution: states.get(binding.id) } : {} };
124
+ });
125
+ };
126
+ return {
127
+ queryActivity: (boardId, query = {}) => querySessionItems(core, boardId, query, (rows) => project(boardId, rows)),
128
+ async executionSession(boardId, itemId, query = {}) {
129
+ const started = Date.now();
130
+ if (!query || typeof query !== "object" || Array.isArray(query) || Object.keys(query).some((key) => key !== "includeRelated") || query.includeRelated !== void 0 && typeof query.includeRelated !== "boolean") throw new (0, _chunkRSH6QSECcjs.KanbanError)("INVALID_QUERY");
131
+ const { item } = await core.item(boardId, itemId), bindings = executionBindings(item.externalRefs, referenceSystem);
132
+ if (!bindings.length && !query.includeRelated) return { history: [] };
133
+ const [board, workflow] = await Promise.all([boardMetadata(core, boardId), core.workflow(boardId)]), last = currentBinding(item.externalRefs, itemId, workflow, referenceSystem);
134
+ let sessionsCode;
135
+ const related = query.includeRelated ? await _optionalChain([options, 'optionalAccess', _2 => _2.source, 'access', _3 => _3.related, 'optionalCall', _4 => _4(board, item, workflow), 'access', _5 => _5.catch, 'call', _6 => _6((error) => {
136
+ const candidate = _optionalChain([error, 'optionalAccess', _7 => _7.code]);
137
+ sessionsCode = candidate && /^[A-Z][A-Z0-9_]{0,79}$/.test(candidate) ? candidate : "SESSION_RELATIONS_FAILED";
138
+ try {
139
+ log({ operationId: _chunkRSH6QSECcjs.randomUUID.call(void 0, ), stage: "session.relations", result: "failure", code: sessionsCode, durationMs: Date.now() - started });
140
+ } catch (e2) {
141
+ }
142
+ return [];
143
+ })]) : [];
144
+ const sessions = query.includeRelated ? await projectRelatedSessions([
145
+ ...sessionAssociations(item, bindings, workflow),
146
+ ..._nullishCoalesce(related, () => ( []))
147
+ ], _optionalChain([last, 'optionalAccess', _8 => _8.id]), (id) => inspect(board, id)) : void 0;
148
+ return {
149
+ current: last ? await _asyncNullishCoalesce(await _asyncOptionalChain([sessions, 'optionalAccess', async _9 => _9.find, 'call', async _10 => _10((row) => row.sessionId === last.id)]), async () => ( await inspect(board, last.id))) : void 0,
150
+ history: bindings.map((ref) => ({ sessionId: ref.id, bindingId: ref.revision, current: ref === last })).reverse(),
151
+ ...sessions ? { sessions } : {},
152
+ ...sessionsCode ? { sessionsCode } : {}
153
+ };
154
+ },
155
+ validate: (board, command, actor) => validateBinding(core, options, board, command, actor, referenceSystem)
156
+ };
157
+ }
158
+ function createSessionInspector(options, log) {
159
+ const now = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _11 => _11.now]), () => ( Date.now)), cache = /* @__PURE__ */ new Map();
160
+ return async (board, sessionId) => {
161
+ const started = now(), key = JSON.stringify([board.id, sessionId]);
162
+ try {
163
+ if (!options) throw new (0, _chunkRSH6QSECcjs.KanbanError)("EXECUTION_SESSION_UNAVAILABLE");
164
+ const row = await options.source.inspect(board, sessionId);
165
+ if (row.sessionId !== sessionId) throw new (0, _chunkRSH6QSECcjs.KanbanError)("SESSION_ACTIVITY_SCOPE_MISMATCH");
166
+ const stale = row.stale || row.observedAt === void 0 || now() - row.observedAt > 9e4;
167
+ const value = {
168
+ sessionId,
169
+ ...typeof row.resourceId === "string" && row.resourceId.length <= 4096 && !row.resourceId.includes("\0") ? { resourceId: row.resourceId } : {},
170
+ title: _optionalChain([row, 'access', _12 => _12.title, 'optionalAccess', _13 => _13.slice, 'call', _14 => _14(0, 256)]),
171
+ state: row.state,
172
+ createdAt: row.createdAt,
173
+ lastActivityAt: row.lastActivityAt,
174
+ observedAt: row.observedAt,
175
+ stale,
176
+ recent: !stale && row.lastActivityAt !== void 0 && row.lastActivityAt <= now() && now() - row.lastActivityAt <= 6e5,
177
+ ...row.code && /^[A-Z][A-Z0-9_]{0,79}$/.test(row.code) ? { code: row.code } : {}
178
+ };
179
+ if (cache.size >= 256) cache.delete(cache.keys().next().value);
180
+ cache.set(key, value);
181
+ return value;
182
+ } catch (error) {
183
+ const candidate = _optionalChain([error, 'optionalAccess', _15 => _15.code]), code = candidate && /^[A-Z][A-Z0-9_]{0,79}$/.test(candidate) ? candidate : "SESSION_ACTIVITY_FAILED";
184
+ try {
185
+ log({ operationId: _chunkRSH6QSECcjs.randomUUID.call(void 0, ), sessionId, stage: "session.activity", result: "failure", code, durationMs: now() - started });
186
+ } catch (e3) {
187
+ }
188
+ const prior = ["EIO", "ETIMEDOUT", "ECONNRESET", "ECONNREFUSED", "SESSION_ACTIVITY_FAILED"].includes(code) ? cache.get(key) : void 0;
189
+ return { ...prior, sessionId, state: _nullishCoalesce(_optionalChain([prior, 'optionalAccess', _16 => _16.state]), () => ( "unknown")), stale: true, recent: false, code };
190
+ }
191
+ };
192
+ }
193
+ async function boardMetadata(core, boardId) {
194
+ const board = await _asyncNullishCoalesce((await core.catalog()).find((board2) => board2.id === boardId), async () => ( await core.read(boardId)));
195
+ return { ...board, items: [] };
196
+ }
197
+ async function validateBinding(core, options, board, command, actor, referenceSystem) {
198
+ if (_optionalChain([command, 'optionalAccess', _17 => _17.type]) !== "link-reference" || command.reference.system !== referenceSystem) return;
199
+ if (!options || !command.reference.revision) throw new (0, _chunkRSH6QSECcjs.KanbanError)("EXECUTION_SESSION_UNAVAILABLE");
200
+ const item = board.items.find((item2) => item2.id === command.itemId);
201
+ if (!item) throw new (0, _chunkRSH6QSECcjs.KanbanError)("ITEM_NOT_FOUND");
202
+ const key = _chunkRBOER6EWcjs.principalKey.call(void 0, _chunkRBOER6EWcjs.principal.call(void 0, actor)), coordinator = _optionalChain([board, 'access', _18 => _18.members, 'optionalAccess', _19 => _19.some, 'call', _20 => _20((member) => _chunkRBOER6EWcjs.principalKey.call(void 0, member) === key && _optionalChain([member, 'access', _21 => _21.roles, 'optionalAccess', _22 => _22.includes, 'call', _23 => _23("coordinator")]))]);
203
+ if (!coordinator && _optionalChain([item, 'access', _24 => _24.owner, 'optionalAccess', _25 => _25.principalId]) !== key && !(item.owner === void 0 && _chunkRBOER6EWcjs.candidates.call(void 0, board, item).some((candidate) => _chunkRBOER6EWcjs.principalKey.call(void 0, candidate) === key))) throw new (0, _chunkRSH6QSECcjs.KanbanError)("EXECUTION_SESSION_ASSIGNMENT_DENIED");
204
+ const active = Object.values((await core.workflow(board.id)).dispatches).filter((dispatch) => dispatch.itemId === item.id).sort((a, b) => b.attempt - a.attempt)[0];
205
+ if (active && command.reference.revision !== active.id) throw new (0, _chunkRSH6QSECcjs.KanbanError)("EXECUTION_SESSION_LOCKED");
206
+ if (active && executionBindings(item.externalRefs, referenceSystem).some((ref) => ref.revision === active.id && ref.id !== command.reference.id)) throw new (0, _chunkRSH6QSECcjs.KanbanError)("EXECUTION_SESSION_LOCKED");
207
+ if (options.source.authorizeBinding) return options.source.authorizeBinding(board, command.reference.id);
208
+ const state = await options.source.inspect(board, command.reference.id);
209
+ if (state.sessionId !== command.reference.id || state.stale) throw new (0, _chunkRSH6QSECcjs.KanbanError)("EXECUTION_SESSION_UNAVAILABLE");
210
+ }
211
+
212
+ // src/activity-module.ts
213
+ function createKanbanActivityModule(options) {
214
+ const { executionSessions, referenceSystem, ...core } = options;
215
+ let activity;
216
+ const service = _chunkDINLR6E6cjs.createKanbanModule.call(void 0, {
217
+ ...core,
218
+ authorize: async (input) => {
219
+ await core.authorize(input);
220
+ await activity.validate(input.board, input.command, input.actor);
221
+ }
222
+ });
223
+ activity = createKanbanSessionActivity(service, executionSessions, core.log, referenceSystem);
224
+ let stopping = false;
225
+ const pending = /* @__PURE__ */ new Set();
226
+ const track = (run) => {
227
+ if (stopping) return Promise.reject(new (0, _chunkRSH6QSECcjs.KanbanError)("SERVICE_STOPPING"));
228
+ const work = Promise.resolve().then(run).finally(() => {
229
+ pending.delete(work);
230
+ });
231
+ pending.add(work);
232
+ return work;
233
+ };
234
+ const quiesce = async () => {
235
+ stopping = true;
236
+ await Promise.allSettled([...pending]);
237
+ await service.quiesce();
238
+ };
239
+ return {
240
+ ...forwardCore(service, track),
241
+ queryActivity: (...args) => track(() => activity.queryActivity(...args)),
242
+ executionSession: (...args) => track(() => activity.executionSession(...args)),
243
+ related: (sessionId) => track(() => relatedBoards(service, referenceSystem, sessionId)),
244
+ quiesce,
245
+ dispose: quiesce
246
+ };
247
+ }
248
+ function forwardCore(service, track) {
249
+ return {
250
+ create: (input) => track(() => service.create(input)),
251
+ read: (id) => track(() => service.read(id)),
252
+ list: () => track(() => service.list()),
253
+ catalog: () => track(() => service.catalog()),
254
+ execute: (id, request) => track(() => service.execute(id, request)),
255
+ events: (id, after, limit) => track(() => service.events(id, after, limit)),
256
+ query: (id, query) => track(() => service.query(id, query)),
257
+ item: (id, item) => track(() => service.item(id, item)),
258
+ overview: (id) => track(() => service.overview(id)),
259
+ scan: () => track(() => service.scan()),
260
+ workflow: (id) => track(() => service.workflow(id)),
261
+ resolve: (reference) => track(() => service.resolve(reference)),
262
+ suggest: (id, query) => track(() => service.suggest(id, query)),
263
+ records: (id, query) => track(() => service.records(id, query)),
264
+ record: (id, record) => track(() => service.record(id, record)),
265
+ currentPrincipal: () => track(() => service.currentPrincipal())
266
+ };
267
+ }
268
+ async function relatedBoards(service, referenceSystem, sessionId) {
269
+ const boards = await service.related(sessionId);
270
+ for (const board of await service.catalog()) {
271
+ if (boards.some((old) => old.id === board.id)) continue;
272
+ const matches = await service.query(board.id, { view: "all", reference: { system: referenceSystem, id: sessionId }, limit: 1 });
273
+ if (matches.total) boards.push(await service.read(board.id));
274
+ }
275
+ return boards;
276
+ }
277
+
278
+
279
+
280
+
281
+ exports.querySessionItems = querySessionItems; exports.createKanbanActivityModule = createKanbanActivityModule;
282
+ //# sourceMappingURL=chunk-5Q7WB63A.cjs.map