@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.
- package/LICENSE +674 -0
- package/README.md +165 -2
- package/dist/activity-BbC4sgKL.d.cts +258 -0
- package/dist/activity-Czlan5xW.d.ts +258 -0
- package/dist/activity-module-DaO1Rry0.d.cts +22 -0
- package/dist/activity-module-T22wliZu.d.ts +22 -0
- package/dist/activity-ports-Dqxa86zT.d.cts +12 -0
- package/dist/activity-ports-cxfrelaL.d.ts +12 -0
- package/dist/activity.cjs +16 -0
- package/dist/activity.cjs.map +1 -0
- package/dist/activity.d.cts +12 -0
- package/dist/activity.d.ts +12 -0
- package/dist/activity.js +16 -0
- package/dist/activity.js.map +1 -0
- package/dist/chunk-3JYLJPE5.js +95 -0
- package/dist/chunk-3JYLJPE5.js.map +1 -0
- package/dist/chunk-3LEIHB7Y.js +221 -0
- package/dist/chunk-3LEIHB7Y.js.map +1 -0
- package/dist/chunk-4XF6LQSU.cjs +9 -0
- package/dist/chunk-4XF6LQSU.cjs.map +1 -0
- package/dist/chunk-5Q7WB63A.cjs +282 -0
- package/dist/chunk-5Q7WB63A.cjs.map +1 -0
- package/dist/chunk-7KDOTBRX.cjs +58 -0
- package/dist/chunk-7KDOTBRX.cjs.map +1 -0
- package/dist/chunk-AHHEGTUA.js +148 -0
- package/dist/chunk-AHHEGTUA.js.map +1 -0
- package/dist/chunk-AOHQCX7K.cjs +95 -0
- package/dist/chunk-AOHQCX7K.cjs.map +1 -0
- package/dist/chunk-DINLR6E6.cjs +443 -0
- package/dist/chunk-DINLR6E6.cjs.map +1 -0
- package/dist/chunk-EYTVC2H5.cjs +1612 -0
- package/dist/chunk-EYTVC2H5.cjs.map +1 -0
- package/dist/chunk-FEE2YBZ7.js +1972 -0
- package/dist/chunk-FEE2YBZ7.js.map +1 -0
- package/dist/chunk-FNO5K3A7.js +282 -0
- package/dist/chunk-FNO5K3A7.js.map +1 -0
- package/dist/chunk-IHHDXZPZ.js +382 -0
- package/dist/chunk-IHHDXZPZ.js.map +1 -0
- package/dist/chunk-J3EAIVA3.js +280 -0
- package/dist/chunk-J3EAIVA3.js.map +1 -0
- package/dist/chunk-KH3WF3K4.js +443 -0
- package/dist/chunk-KH3WF3K4.js.map +1 -0
- package/dist/chunk-M35X5PHO.js +196 -0
- package/dist/chunk-M35X5PHO.js.map +1 -0
- package/dist/chunk-QRGZD2JU.cjs +1972 -0
- package/dist/chunk-QRGZD2JU.cjs.map +1 -0
- package/dist/chunk-RBOER6EW.cjs +221 -0
- package/dist/chunk-RBOER6EW.cjs.map +1 -0
- package/dist/chunk-RQP77ZTY.js +9 -0
- package/dist/chunk-RQP77ZTY.js.map +1 -0
- package/dist/chunk-RSH6QSEC.cjs +196 -0
- package/dist/chunk-RSH6QSEC.cjs.map +1 -0
- package/dist/chunk-VMRDXOLX.cjs +148 -0
- package/dist/chunk-VMRDXOLX.cjs.map +1 -0
- package/dist/chunk-X4UHZX6F.js +58 -0
- package/dist/chunk-X4UHZX6F.js.map +1 -0
- package/dist/chunk-XBSGUVCY.cjs +382 -0
- package/dist/chunk-XBSGUVCY.cjs.map +1 -0
- package/dist/chunk-XKDZHAUE.js +1612 -0
- package/dist/chunk-XKDZHAUE.js.map +1 -0
- package/dist/chunk-ZUZGAO6G.cjs +280 -0
- package/dist/chunk-ZUZGAO6G.cjs.map +1 -0
- package/dist/content-C5aJZFlw.d.cts +35 -0
- package/dist/content-hLH5BNud.d.ts +35 -0
- package/dist/contracts/index.cjs +31 -0
- package/dist/contracts/index.cjs.map +1 -0
- package/dist/contracts/index.d.cts +5 -0
- package/dist/contracts/index.d.ts +5 -0
- package/dist/contracts/index.js +31 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/discovery-9E-LfwIw.d.cts +61 -0
- package/dist/discovery-D60yrjej.d.ts +61 -0
- package/dist/discovery.cjs +377 -0
- package/dist/discovery.cjs.map +1 -0
- package/dist/discovery.d.cts +22 -0
- package/dist/discovery.d.ts +22 -0
- package/dist/discovery.js +377 -0
- package/dist/discovery.js.map +1 -0
- package/dist/execution-bridge-fpCnH6z5.d.cts +52 -0
- package/dist/execution-bridge-rk0uVy_n.d.ts +52 -0
- package/dist/execution.cjs +18 -0
- package/dist/execution.cjs.map +1 -0
- package/dist/execution.d.cts +42 -0
- package/dist/execution.d.ts +42 -0
- package/dist/execution.js +18 -0
- package/dist/execution.js.map +1 -0
- package/dist/index.cjs +74 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +54 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/module-CYfE7YhM.d.ts +11 -0
- package/dist/module-Ca9L3qCn.d.cts +11 -0
- package/dist/planning-CHrOqQ6b.d.cts +879 -0
- package/dist/planning-CHrOqQ6b.d.ts +879 -0
- package/dist/ports-B0McVg7B.d.ts +143 -0
- package/dist/ports-BeGO1VRI.d.cts +49 -0
- package/dist/ports-GQy0x-RI.d.cts +143 -0
- package/dist/ports-ykGiNUSx.d.ts +49 -0
- package/dist/runtime/index.cjs +205 -0
- package/dist/runtime/index.cjs.map +1 -0
- package/dist/runtime/index.d.cts +75 -0
- package/dist/runtime/index.d.ts +75 -0
- package/dist/runtime/index.js +205 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/integration.cjs +423 -0
- package/dist/runtime/integration.cjs.map +1 -0
- package/dist/runtime/integration.d.cts +658 -0
- package/dist/runtime/integration.d.ts +658 -0
- package/dist/runtime/integration.js +423 -0
- package/dist/runtime/integration.js.map +1 -0
- package/dist/storage.cjs +21 -0
- package/dist/storage.cjs.map +1 -0
- package/dist/storage.d.cts +71 -0
- package/dist/storage.d.ts +71 -0
- package/dist/storage.js +21 -0
- package/dist/storage.js.map +1 -0
- package/dist/store-DPwHzGiz.d.cts +28 -0
- package/dist/store-DgdrPCV9.d.ts +28 -0
- package/dist/task-projection-CSorlMws.d.ts +11 -0
- package/dist/task-projection-DKKB_ueb.d.cts +11 -0
- package/examples/standalone.ts +22 -0
- package/package.json +143 -4
|
@@ -0,0 +1,377 @@
|
|
|
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; } 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; } var _class;
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
var _chunkRBOER6EWcjs = require('./chunk-RBOER6EW.cjs');
|
|
8
|
+
require('./chunk-7KDOTBRX.cjs');
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
var _chunkRSH6QSECcjs = require('./chunk-RSH6QSEC.cjs');
|
|
14
|
+
require('./chunk-AOHQCX7K.cjs');
|
|
15
|
+
|
|
16
|
+
// src/scan-context.ts
|
|
17
|
+
function scanContext(board, state, sessions, options, runId) {
|
|
18
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, state.board.revision === board.revision, "SCAN_BOARD_CHANGED");
|
|
19
|
+
const archived = _nullishCoalesce(state.archiveIndex, () => ( []));
|
|
20
|
+
const input = {
|
|
21
|
+
runId,
|
|
22
|
+
executorId: options.executor.identity,
|
|
23
|
+
board: { ...board, items: [] },
|
|
24
|
+
sessions,
|
|
25
|
+
connection: structuredClone(options.policy.connection),
|
|
26
|
+
projectContext: { totalRecords: _nullishCoalesce(_optionalChain([state, 'access', _ => _.records, 'optionalAccess', _2 => _2.filter, 'call', _3 => _3((record) => record.status === "confirmed"), 'access', _4 => _4.length]), () => ( 0)), omittedRecords: _nullishCoalesce(_optionalChain([state, 'access', _5 => _5.records, 'optionalAccess', _6 => _6.filter, 'call', _7 => _7((record) => record.status === "confirmed"), 'access', _8 => _8.length]), () => ( 0)), excludedRecords: _nullishCoalesce(_optionalChain([state, 'access', _9 => _9.records, 'optionalAccess', _10 => _10.filter, 'call', _11 => _11((record) => record.status !== "confirmed"), 'access', _12 => _12.length]), () => ( 0)), records: [] },
|
|
27
|
+
context: {
|
|
28
|
+
totalItems: board.items.length,
|
|
29
|
+
omittedItems: board.items.length,
|
|
30
|
+
archivedTotal: archived.length,
|
|
31
|
+
archivedItems: []
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const bytes = () => new TextEncoder().encode(JSON.stringify(input)).length;
|
|
35
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, bytes() <= options.policy.maxInputBytes, "SCAN_INPUT_LIMIT");
|
|
36
|
+
const sessionIds = new Set(sessions.map((s) => s.session.id));
|
|
37
|
+
addRecords(input, state, sessionIds, bytes, options.policy.maxInputBytes);
|
|
38
|
+
const content = sessions.flatMap((s) => s.messages.map((m) => m.text.toLowerCase())).join("\n");
|
|
39
|
+
const ordered = [...board.items].sort((a, b) => score(a, sessionIds, content) - score(b, sessionIds, content) || (_nullishCoalesce(_optionalChain([a, 'access', _13 => _13.planning, 'optionalAccess', _14 => _14.priority]), () => ( 2))) - (_nullishCoalesce(_optionalChain([b, 'access', _15 => _15.planning, 'optionalAccess', _16 => _16.priority]), () => ( 2))) || a.id.localeCompare(b.id));
|
|
40
|
+
for (const item of ordered) {
|
|
41
|
+
if (input.board.items.length >= 50) break;
|
|
42
|
+
input.board.items.push(item);
|
|
43
|
+
input.context.omittedItems--;
|
|
44
|
+
if (bytes() > options.policy.maxInputBytes) {
|
|
45
|
+
input.board.items.pop();
|
|
46
|
+
input.context.omittedItems++;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const history = [...archived].sort((a, b) => Number(b.sessionIds.some((id) => sessionIds.has(id))) - Number(a.sessionIds.some((id) => sessionIds.has(id))) || (_nullishCoalesce(b.closedAt, () => ( b.updatedAt))) - (_nullishCoalesce(a.closedAt, () => ( a.updatedAt))));
|
|
50
|
+
for (const item of history.slice(0, 20)) {
|
|
51
|
+
input.context.archivedItems.push(item);
|
|
52
|
+
if (bytes() > options.policy.maxInputBytes) input.context.archivedItems.pop();
|
|
53
|
+
}
|
|
54
|
+
return structuredClone(input);
|
|
55
|
+
}
|
|
56
|
+
function addRecords(input, state, sessionIds, bytes, maxBytes) {
|
|
57
|
+
const records = (_nullishCoalesce(state.records, () => ( []))).filter((record) => record.status === "confirmed").sort((a, b) => Number(b.kind === "decision") - Number(a.kind === "decision") || Number(b.sources.some((source) => sessionIds.has(source.sessionId))) - Number(a.sources.some((source) => sessionIds.has(source.sessionId))) || b.createdAt - a.createdAt);
|
|
58
|
+
for (const record of records.slice(0, 10)) {
|
|
59
|
+
input.projectContext.records.push(record);
|
|
60
|
+
input.projectContext.omittedRecords--;
|
|
61
|
+
if (bytes() > maxBytes) {
|
|
62
|
+
input.projectContext.records.pop();
|
|
63
|
+
input.projectContext.omittedRecords++;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function score(item, sessions, content) {
|
|
68
|
+
if (_optionalChain([item, 'access', _17 => _17.sources, 'optionalAccess', _18 => _18.some, 'call', _19 => _19((source) => sessions.has(source.sessionId))]) || content.includes(item.title.toLowerCase())) return 0;
|
|
69
|
+
if (item.status === "claimed" || item.status === "review") return 1;
|
|
70
|
+
if (item.status === "open") return 2;
|
|
71
|
+
return item.status === "completed" || item.status === "cancelled" ? 4 : 3;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/discovery.ts
|
|
75
|
+
function createKanbanDiscovery(options) {
|
|
76
|
+
return new Discovery(options);
|
|
77
|
+
}
|
|
78
|
+
var Discovery = (_class = class {
|
|
79
|
+
constructor(options) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);
|
|
80
|
+
this.options = options;
|
|
81
|
+
this.now = _nullishCoalesce(options.now, () => ( Date.now));
|
|
82
|
+
this.id = _nullishCoalesce(options.id, () => ( _chunkRSH6QSECcjs.randomUUID));
|
|
83
|
+
this.token = this.id();
|
|
84
|
+
for (const value of [options.policy.idleMs, options.policy.maxSessions, options.policy.maxInputBytes, options.policy.leaseMs]) _chunkRBOER6EWcjs.positive.call(void 0, value);
|
|
85
|
+
_chunkRBOER6EWcjs.identity.call(void 0, options.policy.connection.connectionId);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
__init() {this.stopping = false}
|
|
92
|
+
__init2() {this.resets = /* @__PURE__ */ new Set()}
|
|
93
|
+
__init3() {this.stage = "prepare"}
|
|
94
|
+
__init4() {this.scan = () => {
|
|
95
|
+
if (this.stopping) return Promise.reject(new (0, _chunkRSH6QSECcjs.KanbanError)("SERVICE_STOPPING"));
|
|
96
|
+
if (this.active) return this.active;
|
|
97
|
+
const operationId = this.id(), started = this.now();
|
|
98
|
+
this.active = this.run().catch(async (error) => {
|
|
99
|
+
if (error instanceof _chunkRSH6QSECcjs.KanbanError && ["SOURCE_CHANGED", "SCAN_BOARD_CHANGED", "REVISION_CONFLICT"].includes(error.code)) {
|
|
100
|
+
await this.settleInvalidatedRun();
|
|
101
|
+
await this.update("invalidate", (state) => {
|
|
102
|
+
if (_optionalChain([state, 'access', _22 => _22.pending, 'optionalAccess', _23 => _23.token]) === this.token) {
|
|
103
|
+
state.lastRun = { runId: state.pending.runId, outcome: "invalidated", at: this.now(), code: error.code };
|
|
104
|
+
delete state.pending;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
throw error;
|
|
109
|
+
}).then(() => this.log(operationId, started, "success"), (error) => {
|
|
110
|
+
this.log(operationId, started, "failure", errorCode(error));
|
|
111
|
+
throw error;
|
|
112
|
+
}).finally(() => {
|
|
113
|
+
this.active = void 0;
|
|
114
|
+
});
|
|
115
|
+
return this.active;
|
|
116
|
+
}}
|
|
117
|
+
__init5() {this.tick = () => this.scan()}
|
|
118
|
+
async quiesce() {
|
|
119
|
+
this.stopping = true;
|
|
120
|
+
await Promise.allSettled([this.active, ...this.resets]);
|
|
121
|
+
}
|
|
122
|
+
__init6() {this.dispose = () => this.quiesce()}
|
|
123
|
+
__init7() {this.reset = (runId) => {
|
|
124
|
+
if (this.stopping) return Promise.reject(new (0, _chunkRSH6QSECcjs.KanbanError)("SERVICE_STOPPING"));
|
|
125
|
+
const work = this.resetRun(runId).finally(() => {
|
|
126
|
+
this.resets.delete(work);
|
|
127
|
+
});
|
|
128
|
+
this.resets.add(work);
|
|
129
|
+
return work;
|
|
130
|
+
}}
|
|
131
|
+
async resetRun(runId) {
|
|
132
|
+
const pending = await _asyncOptionalChain([_chunkRBOER6EWcjs.discoveryState.call(void 0, await this.options.store.read(this.options.boardId)), 'optionalAccess', async _24 => _24.pending]);
|
|
133
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, _optionalChain([pending, 'optionalAccess', _25 => _25.runId]) === runId, "SCAN_CHANGED");
|
|
134
|
+
await this.options.authorize("commit", pending.input);
|
|
135
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, pending.token === this.token || pending.leaseUntil <= this.now(), "SCAN_BUSY");
|
|
136
|
+
if (pending.executionStarted && !pending.executionSettled) _chunkRBOER6EWcjs.requireValue.call(void 0, await _optionalChain([this, 'access', _26 => _26.options, 'access', _27 => _27.executor, 'access', _28 => _28.settled, 'optionalCall', _29 => _29(pending.input)]), "SCAN_EXECUTION_UNSETTLED");
|
|
137
|
+
await this.update("reset", (state) => {
|
|
138
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, _optionalChain([state, 'access', _30 => _30.pending, 'optionalAccess', _31 => _31.runId]) === runId && state.pending.token === pending.token, "SCAN_CHANGED");
|
|
139
|
+
state.lastRun = { runId, outcome: "invalidated", at: this.now(), code: "SCAN_MANUAL_RESET" };
|
|
140
|
+
delete state.pending;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async run() {
|
|
144
|
+
await this.options.authorize("read");
|
|
145
|
+
const board = await this.options.service.read(this.options.boardId);
|
|
146
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, matchesScope(board.scope, this.options.source), "SOURCE_PROJECT_MISMATCH");
|
|
147
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, this.options.source.kind === this.options.policy.connection.kind || this.options.policy.connection.allowContentTransfer, "CONTENT_TRANSFER_NOT_ALLOWED");
|
|
148
|
+
const stored = await this.options.store.read(board.id);
|
|
149
|
+
let pending = _optionalChain([_chunkRBOER6EWcjs.discoveryState.call(void 0, stored), 'optionalAccess', _32 => _32.pending]);
|
|
150
|
+
if (pending && pending.token !== this.token && pending.leaseUntil > this.now()) return;
|
|
151
|
+
if (!pending) {
|
|
152
|
+
const sessions = await this.collect();
|
|
153
|
+
if (!sessions.length) return;
|
|
154
|
+
const current = await this.options.store.read(board.id);
|
|
155
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, current, "BOARD_NOT_FOUND");
|
|
156
|
+
const input = scanContext(board, current, sessions, this.options, this.id());
|
|
157
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, new TextEncoder().encode(JSON.stringify(input)).length <= this.options.policy.maxInputBytes, "SCAN_INPUT_LIMIT");
|
|
158
|
+
pending = { runId: input.runId, autoCreate: this.options.policy.autoCreate && (_nullishCoalesce(_optionalChain([input, 'access', _33 => _33.projectContext, 'optionalAccess', _34 => _34.omittedRecords]), () => ( 0))) === 0 && input.context.omittedItems === 0 && input.context.archivedItems.length === input.context.archivedTotal, token: this.token, leaseUntil: this.now() + this.options.policy.leaseMs, input };
|
|
159
|
+
}
|
|
160
|
+
const selected = pending;
|
|
161
|
+
await this.update("claim", (state) => {
|
|
162
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, !state.pending || state.pending.token === this.token || state.pending.leaseUntil <= this.now(), "SCAN_BUSY");
|
|
163
|
+
if (state.pending) _chunkRBOER6EWcjs.requireValue.call(void 0, state.pending.runId === selected.runId, "SCAN_CHANGED");
|
|
164
|
+
const restoring = selected.token !== this.token;
|
|
165
|
+
state.pending = { ...selected, ...restoring ? { request: void 0 } : {}, token: this.token, leaseUntil: this.now() + this.options.policy.leaseMs };
|
|
166
|
+
});
|
|
167
|
+
await this.process();
|
|
168
|
+
}
|
|
169
|
+
async collect() {
|
|
170
|
+
this.stage = "source.list";
|
|
171
|
+
const sessions = await this.options.source.list(this.options.policy.includeArchived);
|
|
172
|
+
for (const session of sessions) {
|
|
173
|
+
_chunkRBOER6EWcjs.identity.call(void 0, session.id);
|
|
174
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, typeof session.revision === "string" && session.revision.length > 0 && session.revision.length <= 16e3, "INVALID_SOURCE_REVISION");
|
|
175
|
+
}
|
|
176
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, new Set(sessions.map((s) => s.id)).size === sessions.length, "DUPLICATE_SOURCE_SESSION");
|
|
177
|
+
await this.observe(sessions);
|
|
178
|
+
const state = _chunkRBOER6EWcjs.discoveryState.call(void 0, await this.options.store.read(this.options.boardId));
|
|
179
|
+
const result = [];
|
|
180
|
+
let bytes = 0;
|
|
181
|
+
for (const session of sessions.sort((a, b) => (_nullishCoalesce(a.updatedAt, () => ( 0))) - (_nullishCoalesce(b.updatedAt, () => ( 0))) || a.id.localeCompare(b.id))) {
|
|
182
|
+
if (!this.eligible(session, state)) continue;
|
|
183
|
+
const current = await this.options.source.inspect(session.id);
|
|
184
|
+
if (!this.eligible(current, state) || current.revision !== session.revision) continue;
|
|
185
|
+
this.stage = "source.read";
|
|
186
|
+
const text2 = await this.readCandidate(current, Math.floor(this.options.policy.maxInputBytes * 0.7) - bytes);
|
|
187
|
+
if (!text2) continue;
|
|
188
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, text2.session.id === current.id && text2.session.revision === current.revision, "SOURCE_CHANGED");
|
|
189
|
+
if (!text2.complete) continue;
|
|
190
|
+
const size = new TextEncoder().encode(JSON.stringify(text2)).length;
|
|
191
|
+
if (bytes + size > this.options.policy.maxInputBytes * 0.7) break;
|
|
192
|
+
bytes += size;
|
|
193
|
+
result.push(text2);
|
|
194
|
+
if (result.length >= this.options.policy.maxSessions) break;
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
async readCandidate(session, maxBytes) {
|
|
199
|
+
const started = this.now();
|
|
200
|
+
try {
|
|
201
|
+
const result = await this.options.source.read(session, maxBytes);
|
|
202
|
+
if (!result.complete) this.log(this.id(), started, "failure", "SOURCE_PROVENANCE_UNKNOWN", session.id);
|
|
203
|
+
return result;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
const code = errorCode(error);
|
|
206
|
+
if (!["SCAN_INPUT_LIMIT", "SOURCE_CHANGED", "HARNESS_DIGEST_CHANGED"].includes(code)) throw error;
|
|
207
|
+
this.log(this.id(), started, "failure", code, session.id);
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
eligible(session, state) {
|
|
212
|
+
const quiet = _optionalChain([state, 'access', _35 => _35.observations, 'optionalAccess', _36 => _36[session.id]]);
|
|
213
|
+
return !session.scannerOwned && (this.options.policy.includeArchived || !session.archived) && session.activity === "idle" && session.updatedAt !== null && session.updatedAt <= this.now() && _optionalChain([quiet, 'optionalAccess', _37 => _37.revision]) === session.revision && this.now() - quiet.idleSince >= this.options.policy.idleMs && state.checkpoints[session.id] !== session.revision;
|
|
214
|
+
}
|
|
215
|
+
async observe(sessions) {
|
|
216
|
+
await this.update("observe", (state) => {
|
|
217
|
+
state.observations ??= {};
|
|
218
|
+
for (const session of sessions) {
|
|
219
|
+
if (session.activity !== "idle" || session.scannerOwned) {
|
|
220
|
+
delete state.observations[session.id];
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
const old = state.observations[session.id];
|
|
224
|
+
if (_optionalChain([old, 'optionalAccess', _38 => _38.revision]) !== session.revision) state.observations[session.id] = { revision: session.revision, idleSince: this.now() };
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
async process() {
|
|
229
|
+
let pending = _chunkRBOER6EWcjs.discoveryState.call(void 0, await this.options.store.read(this.options.boardId)).pending;
|
|
230
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, pending.input.executorId === this.options.executor.identity, "SCANNER_EXECUTOR_CHANGED");
|
|
231
|
+
await this.checkSources(pending.input);
|
|
232
|
+
if (!pending.changes) {
|
|
233
|
+
await this.options.authorize("execute", pending.input);
|
|
234
|
+
this.stage = "analyzer.execute";
|
|
235
|
+
await this.updateOwned("execute", (state) => {
|
|
236
|
+
state.pending.executionStarted = true;
|
|
237
|
+
});
|
|
238
|
+
const output = await this.execute(pending.input), result = Array.isArray(output) ? { changes: output } : output;
|
|
239
|
+
await this.updateOwned("settled", (state) => {
|
|
240
|
+
state.pending.executionSettled = true;
|
|
241
|
+
});
|
|
242
|
+
validateChanges(result.changes, pending.input);
|
|
243
|
+
validateRecords(_nullishCoalesce(result.records, () => ( [])), pending.input, result.changes);
|
|
244
|
+
await this.updateOwned("result", (state) => {
|
|
245
|
+
state.pending.changes = result.changes;
|
|
246
|
+
state.pending.records = result.records;
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
pending = _chunkRBOER6EWcjs.discoveryState.call(void 0, await this.options.store.read(this.options.boardId)).pending;
|
|
250
|
+
await this.options.authorize("commit", pending.input);
|
|
251
|
+
await this.checkSources(pending.input);
|
|
252
|
+
if (!pending.request) {
|
|
253
|
+
const board = await this.options.service.read(this.options.boardId);
|
|
254
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, board.revision === pending.input.board.revision, "SCAN_BOARD_CHANGED");
|
|
255
|
+
const request = {
|
|
256
|
+
operationId: `discovery:${pending.runId}`,
|
|
257
|
+
expectedRevision: board.revision,
|
|
258
|
+
command: { type: "apply-discovery", batch: {
|
|
259
|
+
runId: pending.runId,
|
|
260
|
+
leaseToken: this.token,
|
|
261
|
+
changes: pending.changes,
|
|
262
|
+
...pending.records ? { records: pending.records } : {},
|
|
263
|
+
autoCreate: pending.autoCreate,
|
|
264
|
+
checkpoints: pending.input.sessions.map((s) => ({ sessionId: s.session.id, revision: s.session.revision }))
|
|
265
|
+
} }
|
|
266
|
+
};
|
|
267
|
+
await this.updateOwned("prepare", (state) => {
|
|
268
|
+
state.pending.request = request;
|
|
269
|
+
});
|
|
270
|
+
pending.request = request;
|
|
271
|
+
}
|
|
272
|
+
this.stage = "board.commit";
|
|
273
|
+
await this.options.service.execute(this.options.boardId, pending.request);
|
|
274
|
+
}
|
|
275
|
+
async execute(input) {
|
|
276
|
+
try {
|
|
277
|
+
return await this.options.executor.run(structuredClone(input));
|
|
278
|
+
} catch (error) {
|
|
279
|
+
if (error instanceof _chunkRSH6QSECcjs.KanbanScanNotStartedError) await this.updateOwned("not-started", (state) => {
|
|
280
|
+
state.pending.executionSettled = true;
|
|
281
|
+
});
|
|
282
|
+
throw error;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
async settleInvalidatedRun() {
|
|
286
|
+
const pending = await _asyncOptionalChain([_chunkRBOER6EWcjs.discoveryState.call(void 0, await this.options.store.read(this.options.boardId)), 'optionalAccess', async _39 => _39.pending]);
|
|
287
|
+
if (!pending || pending.token !== this.token || !pending.executionStarted || pending.executionSettled) return;
|
|
288
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, await _optionalChain([this, 'access', _40 => _40.options, 'access', _41 => _41.executor, 'access', _42 => _42.settled, 'optionalCall', _43 => _43(pending.input)]), "SCAN_EXECUTION_UNSETTLED");
|
|
289
|
+
}
|
|
290
|
+
async checkSources(input) {
|
|
291
|
+
for (const source of input.sessions) {
|
|
292
|
+
const current = await this.options.source.inspect(source.session.id);
|
|
293
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, current.revision === source.session.revision && current.activity === "idle" && !current.scannerOwned, "SOURCE_CHANGED");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async updateOwned(stage, mutate) {
|
|
297
|
+
await this.update(stage, (state) => {
|
|
298
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, _optionalChain([state, 'access', _44 => _44.pending, 'optionalAccess', _45 => _45.token]) === this.token && state.pending.leaseUntil > this.now(), "SCAN_LEASE_EXPIRED");
|
|
299
|
+
mutate(state);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
async update(stage, mutate) {
|
|
303
|
+
const operation = this.id();
|
|
304
|
+
await this.options.store.update(this.options.boardId, operation, `discovery:${stage}:${operation}`, (state) => {
|
|
305
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, state, "BOARD_NOT_FOUND");
|
|
306
|
+
state.discovery ??= { checkpoints: {} };
|
|
307
|
+
mutate(state.discovery);
|
|
308
|
+
return state;
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
log(operationId, started, result, code, sessionId) {
|
|
312
|
+
try {
|
|
313
|
+
this.options.log({ operationId, boardId: this.options.boardId, sessionId, stage: `discovery.${this.stage}`, result, code, durationMs: this.now() - started });
|
|
314
|
+
} catch (e) {
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}, _class);
|
|
318
|
+
function validateChanges(changes, input) {
|
|
319
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, Array.isArray(changes) && changes.length <= 50, "SCAN_CHANGE_LIMIT");
|
|
320
|
+
const ids = /* @__PURE__ */ new Set();
|
|
321
|
+
for (const change of changes) {
|
|
322
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, change && ["create", "update", "link"].includes(change.action) && !ids.has(change.itemId), "INVALID_CHANGE");
|
|
323
|
+
ids.add(change.itemId);
|
|
324
|
+
_chunkRBOER6EWcjs.identity.call(void 0, change.itemId);
|
|
325
|
+
if (change.action !== "create") _chunkRBOER6EWcjs.requireValue.call(void 0, input.board.items.some((i) => i.id === change.itemId), "UNKNOWN_CONTEXT_ITEM");
|
|
326
|
+
_chunkRBOER6EWcjs.text.call(void 0, change.title);
|
|
327
|
+
_chunkRBOER6EWcjs.text.call(void 0, change.responsibility);
|
|
328
|
+
_chunkRBOER6EWcjs.text.call(void 0, change.acceptance);
|
|
329
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, change.eligibleAgentIds === void 0 || Array.isArray(change.eligibleAgentIds), "INVALID_ELIGIBILITY");
|
|
330
|
+
_optionalChain([change, 'access', _46 => _46.eligibleAgentIds, 'optionalAccess', _47 => _47.forEach, 'call', _48 => _48(_chunkRBOER6EWcjs.identity)]);
|
|
331
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, change.eligiblePrincipals === void 0 || Array.isArray(change.eligiblePrincipals), "INVALID_ELIGIBILITY");
|
|
332
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, Array.isArray(change.sources) && change.sources.length > 0 && change.sources.length <= 100 && change.sources.some((s) => s.role === "user"), "USER_EVIDENCE_REQUIRED");
|
|
333
|
+
for (const ref of change.sources) {
|
|
334
|
+
const source = input.sessions.find((s) => s.session.id === ref.sessionId && s.session.revision === ref.revision);
|
|
335
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, _optionalChain([source, 'optionalAccess', _49 => _49.messages, 'access', _50 => _50.some, 'call', _51 => _51((m) => m.id === ref.messageId && m.role === ref.role)]), "INVALID_SOURCE_REFERENCE");
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function validateRecords(records, input, changes) {
|
|
340
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, Array.isArray(records) && records.length <= 20, "SCAN_RECORD_LIMIT");
|
|
341
|
+
const ids = /* @__PURE__ */ new Set();
|
|
342
|
+
for (const record of records) {
|
|
343
|
+
_chunkRBOER6EWcjs.identity.call(void 0, record.id);
|
|
344
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, !ids.has(record.id), "INVALID_RECORD_ID");
|
|
345
|
+
ids.add(record.id);
|
|
346
|
+
_chunkRBOER6EWcjs.text.call(void 0, record.title);
|
|
347
|
+
_chunkRBOER6EWcjs.text.call(void 0, record.content);
|
|
348
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, ["log", "decision"].includes(record.kind), "INVALID_RECORD_KIND");
|
|
349
|
+
if (record.kind === "decision") _chunkRBOER6EWcjs.text.call(void 0, _nullishCoalesce(record.rationale, () => ( "")));
|
|
350
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, Array.isArray(record.itemIds) && record.itemIds.every((id) => input.board.items.some((item) => item.id === id) || changes.some((change) => change.action === "create" && change.itemId === id)), "UNKNOWN_CONTEXT_ITEM");
|
|
351
|
+
if (record.supersedes) _chunkRBOER6EWcjs.requireValue.call(void 0, _optionalChain([input, 'access', _52 => _52.projectContext, 'optionalAccess', _53 => _53.records, 'access', _54 => _54.some, 'call', _55 => _55((old) => old.id === record.supersedes && old.kind === "decision")]), "UNKNOWN_CONTEXT_RECORD");
|
|
352
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, Array.isArray(record.sources) && record.sources.length > 0 && record.sources.length <= 100 && record.sources.some((source) => source.role === "user"), "USER_EVIDENCE_REQUIRED");
|
|
353
|
+
for (const ref of record.sources) {
|
|
354
|
+
const source = input.sessions.find((s) => s.session.id === ref.sessionId && s.session.revision === ref.revision);
|
|
355
|
+
_chunkRBOER6EWcjs.requireValue.call(void 0, _optionalChain([source, 'optionalAccess', _56 => _56.messages, 'access', _57 => _57.some, 'call', _58 => _58((message) => message.id === ref.messageId && message.role === ref.role)]), "INVALID_SOURCE_REFERENCE");
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function errorCode(error) {
|
|
360
|
+
const value = error;
|
|
361
|
+
const code = _nullishCoalesce(_optionalChain([value, 'optionalAccess', _59 => _59.remoteCode]), () => ( _optionalChain([value, 'optionalAccess', _60 => _60.code])));
|
|
362
|
+
return typeof code === "string" && /^[A-Z][A-Z0-9_]{0,79}$/.test(code) ? code : "SCAN_PORT_FAILURE";
|
|
363
|
+
}
|
|
364
|
+
function matchesScope(scope, source) {
|
|
365
|
+
const expected = _nullishCoalesce(source.scope, () => ( (source.projectId ? { kind: "project", projectId: source.projectId } : void 0)));
|
|
366
|
+
if (!expected || scope.kind !== expected.kind) return false;
|
|
367
|
+
if (scope.kind === "system") return true;
|
|
368
|
+
if (scope.kind === "project" && expected.kind === "project") return scope.projectId === expected.projectId;
|
|
369
|
+
return scope.kind === "collection" && expected.kind === "collection" && scope.namespace === expected.namespace && scope.id === expected.id;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
exports.KanbanScanNotStartedError = _chunkRSH6QSECcjs.KanbanScanNotStartedError; exports.buildDiscoveryContext = scanContext; exports.createKanbanDiscovery = createKanbanDiscovery; exports.discoveryState = _chunkRBOER6EWcjs.discoveryState;
|
|
377
|
+
//# sourceMappingURL=discovery.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/li/share/prj/x1/libraries/kanban/dist/discovery.cjs","../src/scan-context.ts","../src/discovery.ts"],"names":["text"],"mappings":"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","file":"/home/li/share/prj/x1/libraries/kanban/dist/discovery.cjs","sourcesContent":[null,"import type {KanbanBoard, KanbanItem, KanbanScanInput, KanbanScanText} from './contracts';\nimport type {KanbanDiscoveryOptions} from './scan-ports';\nimport type {KanbanState} from './ports';\nimport {requireValue} from './rules';\n\n/** A bounded working set, with explicit omitted counts instead of pretending full coverage. */\nexport function scanContext(board: KanbanBoard, state: KanbanState, sessions: KanbanScanText[], options: KanbanDiscoveryOptions, runId: string): KanbanScanInput {\n requireValue(state.board.revision === board.revision, 'SCAN_BOARD_CHANGED');\n const archived = state.archiveIndex ?? [];\n const input: KanbanScanInput = {runId, executorId: options.executor.identity, board: {...board, items: []}, sessions,\n connection: structuredClone(options.policy.connection), projectContext: {totalRecords: state.records?.filter(record => record.status === 'confirmed').length ?? 0, omittedRecords: state.records?.filter(record => record.status === 'confirmed').length ?? 0, excludedRecords: state.records?.filter(record => record.status !== 'confirmed').length ?? 0, records: []}, context: {totalItems: board.items.length, omittedItems: board.items.length,\n archivedTotal: archived.length, archivedItems: []}};\n const bytes = () => new TextEncoder().encode(JSON.stringify(input)).length;\n requireValue(bytes() <= options.policy.maxInputBytes, 'SCAN_INPUT_LIMIT');\n const sessionIds = new Set(sessions.map(s => s.session.id));\n addRecords(input, state, sessionIds, bytes, options.policy.maxInputBytes);\n const content = sessions.flatMap(s => s.messages.map(m => m.text.toLowerCase())).join('\\n');\n const ordered = [...board.items].sort((a, b) => score(a, sessionIds, content) - score(b, sessionIds, content)\n || (a.planning?.priority ?? 2) - (b.planning?.priority ?? 2) || a.id.localeCompare(b.id));\n for (const item of ordered) {\n if (input.board.items.length >= 50) break;\n input.board.items.push(item); input.context!.omittedItems--;\n if (bytes() > options.policy.maxInputBytes) {input.board.items.pop(); input.context!.omittedItems++;}\n }\n const history = [...archived].sort((a, b) => Number(b.sessionIds.some(id => sessionIds.has(id))) - Number(a.sessionIds.some(id => sessionIds.has(id)))\n || (b.closedAt ?? b.updatedAt) - (a.closedAt ?? a.updatedAt));\n for (const item of history.slice(0, 20)) {\n input.context!.archivedItems.push(item);\n if (bytes() > options.policy.maxInputBytes) input.context!.archivedItems.pop();\n }\n return structuredClone(input);\n}\nfunction addRecords(input: KanbanScanInput, state: KanbanState, sessionIds: Set<string>, bytes: () => number, maxBytes: number): void {\n const records = (state.records ?? []).filter(record => record.status === 'confirmed').sort((a, b) => Number(b.kind === 'decision') - Number(a.kind === 'decision')\n || Number(b.sources.some(source => sessionIds.has(source.sessionId))) - Number(a.sources.some(source => sessionIds.has(source.sessionId))) || b.createdAt - a.createdAt);\n for (const record of records.slice(0, 10)) {\n input.projectContext!.records.push(record); input.projectContext!.omittedRecords--;\n if (bytes() > maxBytes) {input.projectContext!.records.pop(); input.projectContext!.omittedRecords++;}\n }\n}\n\nfunction score(item: KanbanItem, sessions: Set<string>, content: string): number {\n if (item.sources?.some(source => sessions.has(source.sessionId)) || content.includes(item.title.toLowerCase())) return 0;\n if (item.status === 'claimed' || item.status === 'review') return 1;\n if (item.status === 'open') return 2;\n return item.status === 'completed' || item.status === 'cancelled' ? 4 : 3;\n}\n","import {discoveryState} from './discovery-state';\nimport {randomUUID} from './utils';\nimport type {KanbanDiscoveryChange, KanbanRequest, KanbanScanInput, KanbanScanSession, KanbanScanText} from './contracts';\nimport {KanbanError, KanbanScanNotStartedError} from './ports';\nimport type {KanbanDiscoveryOptions, KanbanDiscoveryState} from './scan-ports';\nimport {scanContext} from './scan-context';\nimport {identity, positive, requireValue, text as validateText} from './rules';\n\nexport interface KanbanDiscoveryHandle {scan(): Promise<void>; tick(): Promise<void>; reset(runId: string): Promise<void>; quiesce(): Promise<void>; dispose(): Promise<void>}\nexport function createKanbanDiscovery(options: KanbanDiscoveryOptions): KanbanDiscoveryHandle {return new Discovery(options);}\nclass Discovery {\n private readonly now;\n private readonly id;\n private readonly token;\n private active?: Promise<void>;\n private stopping = false;\n private readonly resets = new Set<Promise<void>>();\n private stage = 'prepare';\n constructor(private readonly options: KanbanDiscoveryOptions) {\n this.now = options.now ?? Date.now; this.id = options.id ?? randomUUID; this.token = this.id();\n for (const value of [options.policy.idleMs, options.policy.maxSessions, options.policy.maxInputBytes, options.policy.leaseMs]) positive(value);\n identity(options.policy.connection.connectionId);\n }\n scan = (): Promise<void> => {\n if (this.stopping) return Promise.reject(new KanbanError('SERVICE_STOPPING'));\n if (this.active) return this.active;\n const operationId = this.id(), started = this.now();\n this.active = this.run().catch(async error => {\n if (error instanceof KanbanError && ['SOURCE_CHANGED', 'SCAN_BOARD_CHANGED', 'REVISION_CONFLICT'].includes(error.code)) {\n await this.settleInvalidatedRun();\n await this.update('invalidate', state => {if (state.pending?.token === this.token) {\n state.lastRun = {runId: state.pending.runId, outcome: 'invalidated', at: this.now(), code: error.code}; delete state.pending;\n }});\n }\n throw error;\n }).then(() => this.log(operationId, started, 'success'), error => {\n this.log(operationId, started, 'failure', errorCode(error)); throw error;\n }).finally(() => {this.active = undefined;});\n return this.active;\n };\n tick = (): Promise<void> => this.scan();\n async quiesce(): Promise<void> {\n this.stopping = true;\n await Promise.allSettled([this.active, ...this.resets]);\n }\n dispose = (): Promise<void> => this.quiesce();\n reset = (runId: string): Promise<void> => {\n if (this.stopping) return Promise.reject(new KanbanError('SERVICE_STOPPING'));\n const work = this.resetRun(runId).finally(() => {this.resets.delete(work);});\n this.resets.add(work); return work;\n };\n private async resetRun(runId: string): Promise<void> {\n const pending = discoveryState(await this.options.store.read(this.options.boardId))?.pending;\n requireValue(pending?.runId === runId, 'SCAN_CHANGED');\n await this.options.authorize('commit', pending.input);\n requireValue(pending.token === this.token || pending.leaseUntil <= this.now(), 'SCAN_BUSY');\n if (pending.executionStarted && !pending.executionSettled) requireValue(await this.options.executor.settled?.(pending.input), 'SCAN_EXECUTION_UNSETTLED');\n await this.update('reset', state => {\n requireValue(state.pending?.runId === runId && state.pending.token === pending.token, 'SCAN_CHANGED');\n state.lastRun = {runId, outcome: 'invalidated', at: this.now(), code: 'SCAN_MANUAL_RESET'}; delete state.pending;\n });\n }\n private async run(): Promise<void> {\n await this.options.authorize('read');\n const board = await this.options.service.read(this.options.boardId);\n requireValue(matchesScope(board.scope, this.options.source), 'SOURCE_PROJECT_MISMATCH');\n requireValue(this.options.source.kind === this.options.policy.connection.kind || this.options.policy.connection.allowContentTransfer, 'CONTENT_TRANSFER_NOT_ALLOWED');\n const stored = await this.options.store.read(board.id);\n let pending = discoveryState(stored)?.pending;\n if (pending && pending.token !== this.token && pending.leaseUntil > this.now()) return;\n if (!pending) {\n const sessions = await this.collect(); if (!sessions.length) return;\n const current = await this.options.store.read(board.id); requireValue(current, 'BOARD_NOT_FOUND');\n const input = scanContext(board, current, sessions, this.options, this.id());\n requireValue(new TextEncoder().encode(JSON.stringify(input)).length <= this.options.policy.maxInputBytes, 'SCAN_INPUT_LIMIT');\n pending = {runId: input.runId, autoCreate: this.options.policy.autoCreate && (input.projectContext?.omittedRecords ?? 0) === 0 && input.context!.omittedItems === 0 && input.context!.archivedItems.length === input.context!.archivedTotal, token: this.token, leaseUntil: this.now() + this.options.policy.leaseMs, input};\n }\n const selected = pending;\n await this.update('claim', state => {\n requireValue(!state.pending || state.pending.token === this.token || state.pending.leaseUntil <= this.now(), 'SCAN_BUSY');\n if (state.pending) requireValue(state.pending.runId === selected.runId, 'SCAN_CHANGED');\n const restoring = selected.token !== this.token;\n state.pending = {...selected, ...(restoring ? {request: undefined} : {}), token: this.token, leaseUntil: this.now() + this.options.policy.leaseMs};\n });\n await this.process();\n }\n private async collect(): Promise<KanbanScanText[]> {\n this.stage = 'source.list';\n const sessions = await this.options.source.list(this.options.policy.includeArchived);\n for (const session of sessions) {\n identity(session.id); requireValue(typeof session.revision === 'string' && session.revision.length > 0 && session.revision.length <= 16000, 'INVALID_SOURCE_REVISION');\n }\n requireValue(new Set(sessions.map(s => s.id)).size === sessions.length, 'DUPLICATE_SOURCE_SESSION');\n await this.observe(sessions);\n const state = discoveryState(await this.options.store.read(this.options.boardId))!;\n const result: KanbanScanText[] = []; let bytes = 0;\n for (const session of sessions.sort((a, b) => (a.updatedAt ?? 0) - (b.updatedAt ?? 0) || a.id.localeCompare(b.id))) {\n if (!this.eligible(session, state)) continue;\n const current = await this.options.source.inspect(session.id);\n if (!this.eligible(current, state) || current.revision !== session.revision) continue;\n this.stage = 'source.read';\n const text = await this.readCandidate(current, Math.floor(this.options.policy.maxInputBytes * 0.7) - bytes);\n if (!text) continue;\n requireValue(text.session.id === current.id && text.session.revision === current.revision, 'SOURCE_CHANGED');\n if (!text.complete) continue;\n const size = new TextEncoder().encode(JSON.stringify(text)).length;\n if (bytes + size > this.options.policy.maxInputBytes * 0.7) break;\n bytes += size; result.push(text); if (result.length >= this.options.policy.maxSessions) break;\n }\n return result;\n }\n private async readCandidate(session: KanbanScanSession, maxBytes: number): Promise<KanbanScanText | null> {\n const started = this.now();\n try {\n const result = await this.options.source.read(session, maxBytes);\n if (!result.complete) this.log(this.id(), started, 'failure', 'SOURCE_PROVENANCE_UNKNOWN', session.id);\n return result;\n } catch (error) {\n const code = errorCode(error);\n if (!['SCAN_INPUT_LIMIT', 'SOURCE_CHANGED', 'HARNESS_DIGEST_CHANGED'].includes(code)) throw error;\n this.log(this.id(), started, 'failure', code, session.id); return null;\n }\n }\n private eligible(session: KanbanScanSession, state: KanbanDiscoveryState): boolean {\n const quiet = state.observations?.[session.id];\n return !session.scannerOwned && (this.options.policy.includeArchived || !session.archived)\n && session.activity === 'idle' && session.updatedAt !== null && session.updatedAt <= this.now()\n && quiet?.revision === session.revision && this.now() - quiet.idleSince >= this.options.policy.idleMs\n && state.checkpoints[session.id] !== session.revision;\n }\n private async observe(sessions: KanbanScanSession[]): Promise<void> {\n await this.update('observe', state => {\n state.observations ??= {};\n for (const session of sessions) {\n if (session.activity !== 'idle' || session.scannerOwned) {delete state.observations[session.id]; continue;}\n const old = state.observations[session.id];\n if (old?.revision !== session.revision) state.observations[session.id] = {revision: session.revision, idleSince: this.now()};\n }\n });\n }\n private async process(): Promise<void> {\n let pending = discoveryState(await this.options.store.read(this.options.boardId))!.pending!;\n requireValue(pending.input.executorId === this.options.executor.identity, 'SCANNER_EXECUTOR_CHANGED');\n await this.checkSources(pending.input);\n if (!pending.changes) {\n await this.options.authorize('execute', pending.input);\n this.stage = 'analyzer.execute';\n await this.updateOwned('execute', state => {state.pending!.executionStarted = true;});\n const output = await this.execute(pending.input), result = Array.isArray(output) ? {changes: output} : output;\n await this.updateOwned('settled', state => {state.pending!.executionSettled = true;});\n validateChanges(result.changes, pending.input); validateRecords(result.records ?? [], pending.input, result.changes);\n await this.updateOwned('result', state => {state.pending!.changes = result.changes; state.pending!.records = result.records;});\n }\n pending = discoveryState(await this.options.store.read(this.options.boardId))!.pending!;\n await this.options.authorize('commit', pending.input); await this.checkSources(pending.input);\n if (!pending.request) {\n const board = await this.options.service.read(this.options.boardId);\n requireValue(board.revision === pending.input.board.revision, 'SCAN_BOARD_CHANGED');\n const request: KanbanRequest = {operationId: `discovery:${pending.runId}`, expectedRevision: board.revision,\n command: {type: 'apply-discovery', batch: {runId: pending.runId, leaseToken: this.token, changes: pending.changes!, ...(pending.records ? {records: pending.records} : {}), autoCreate: pending.autoCreate,\n checkpoints: pending.input.sessions.map(s => ({sessionId: s.session.id, revision: s.session.revision}))}}};\n await this.updateOwned('prepare', state => {state.pending!.request = request;});\n pending.request = request;\n }\n this.stage = 'board.commit';\n await this.options.service.execute(this.options.boardId, pending.request);\n }\n private async execute(input: KanbanScanInput): Promise<KanbanDiscoveryChange[] | import('./contracts').KanbanDiscoveryResult> {\n try {return await this.options.executor.run(structuredClone(input));}\n catch (error) {\n if (error instanceof KanbanScanNotStartedError) await this.updateOwned('not-started', state => {state.pending!.executionSettled = true;});\n throw error;\n }\n }\n private async settleInvalidatedRun(): Promise<void> {\n const pending = discoveryState(await this.options.store.read(this.options.boardId))?.pending;\n if (!pending || pending.token !== this.token || !pending.executionStarted || pending.executionSettled) return;\n requireValue(await this.options.executor.settled?.(pending.input), 'SCAN_EXECUTION_UNSETTLED');\n }\n private async checkSources(input: KanbanScanInput): Promise<void> {\n for (const source of input.sessions) {\n const current = await this.options.source.inspect(source.session.id);\n requireValue(current.revision === source.session.revision && current.activity === 'idle' && !current.scannerOwned, 'SOURCE_CHANGED');\n }\n }\n private async updateOwned(stage: string, mutate: (state: KanbanDiscoveryState) => void): Promise<void> {\n await this.update(stage, state => {\n requireValue(state.pending?.token === this.token && state.pending.leaseUntil > this.now(), 'SCAN_LEASE_EXPIRED'); mutate(state);\n });\n }\n private async update(stage: string, mutate: (state: KanbanDiscoveryState) => void): Promise<void> {\n const operation = this.id();\n await this.options.store.update(this.options.boardId, operation, `discovery:${stage}:${operation}`, state => {\n requireValue(state, 'BOARD_NOT_FOUND'); state.discovery ??= {checkpoints: {}}; mutate(state.discovery as KanbanDiscoveryState); return state;\n });\n }\n private log(operationId: string, started: number, result: 'success' | 'failure', code?: string, sessionId?: string): void {\n try {this.options.log({operationId, boardId: this.options.boardId, sessionId, stage: `discovery.${this.stage}`, result, code, durationMs: this.now() - started});}\n catch { /* Diagnostics cannot affect durable execution. */ }\n }\n}\nfunction validateChanges(changes: KanbanDiscoveryChange[], input: KanbanScanInput): void {\n requireValue(Array.isArray(changes) && changes.length <= 50, 'SCAN_CHANGE_LIMIT');\n const ids = new Set<string>();\n for (const change of changes) {\n requireValue(change && ['create', 'update', 'link'].includes(change.action) && !ids.has(change.itemId), 'INVALID_CHANGE'); ids.add(change.itemId); identity(change.itemId);\n if (change.action !== 'create') requireValue(input.board.items.some(i => i.id === change.itemId), 'UNKNOWN_CONTEXT_ITEM');\n validateText(change.title); validateText(change.responsibility); validateText(change.acceptance);\n requireValue(change.eligibleAgentIds === undefined || Array.isArray(change.eligibleAgentIds), 'INVALID_ELIGIBILITY');\n change.eligibleAgentIds?.forEach(identity);\n requireValue(change.eligiblePrincipals === undefined || Array.isArray(change.eligiblePrincipals), 'INVALID_ELIGIBILITY');\n requireValue(Array.isArray(change.sources) && change.sources.length > 0 && change.sources.length <= 100 && change.sources.some(s => s.role === 'user'), 'USER_EVIDENCE_REQUIRED');\n for (const ref of change.sources) {\n const source = input.sessions.find(s => s.session.id === ref.sessionId && s.session.revision === ref.revision);\n requireValue(source?.messages.some(m => m.id === ref.messageId && m.role === ref.role), 'INVALID_SOURCE_REFERENCE');\n }\n }\n}\n\nfunction validateRecords(records: import('./contracts').KanbanRecordInput[], input: KanbanScanInput, changes: KanbanDiscoveryChange[]): void {\n requireValue(Array.isArray(records) && records.length <= 20, 'SCAN_RECORD_LIMIT');\n const ids = new Set<string>();\n for (const record of records) {\n identity(record.id); requireValue(!ids.has(record.id), 'INVALID_RECORD_ID'); ids.add(record.id);\n validateText(record.title); validateText(record.content);\n requireValue(['log', 'decision'].includes(record.kind), 'INVALID_RECORD_KIND');\n if (record.kind === 'decision') validateText(record.rationale ?? '');\n requireValue(Array.isArray(record.itemIds) && record.itemIds.every(id => input.board.items.some(item => item.id === id) || changes.some(change => change.action === 'create' && change.itemId === id)), 'UNKNOWN_CONTEXT_ITEM');\n if (record.supersedes) requireValue(input.projectContext?.records.some(old => old.id === record.supersedes && old.kind === 'decision'), 'UNKNOWN_CONTEXT_RECORD');\n requireValue(Array.isArray(record.sources) && record.sources.length > 0 && record.sources.length <= 100\n && record.sources.some(source => source.role === 'user'), 'USER_EVIDENCE_REQUIRED');\n for (const ref of record.sources) {\n const source = input.sessions.find(s => s.session.id === ref.sessionId && s.session.revision === ref.revision);\n requireValue(source?.messages.some(message => message.id === ref.messageId && message.role === ref.role), 'INVALID_SOURCE_REFERENCE');\n }\n }\n}\n\nfunction errorCode(error: unknown): string {\n const value = error as {code?: unknown; remoteCode?: unknown};\n const code = value?.remoteCode ?? value?.code;\n return typeof code === 'string' && /^[A-Z][A-Z0-9_]{0,79}$/.test(code) ? code : 'SCAN_PORT_FAILURE';\n}\n\nfunction matchesScope(scope: import('./contracts').KanbanBoard['scope'], source: import('./scan-ports').KanbanScanSource): boolean {\n const expected = source.scope ?? (source.projectId ? {kind: 'project' as const, projectId: source.projectId} : undefined);\n if (!expected || scope.kind !== expected.kind) return false;\n if (scope.kind === 'system') return true;\n if (scope.kind === 'project' && expected.kind === 'project') return scope.projectId === expected.projectId;\n return scope.kind === 'collection' && expected.kind === 'collection' && scope.namespace === expected.namespace && scope.id === expected.id;\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { f as KanbanDiscoveryOptions, g as KanbanDiscoveryState, d as KanbanState } from './ports-GQy0x-RI.cjs';
|
|
2
|
+
export { h as KanbanScanExecutor, c as KanbanScanNotStartedError, i as KanbanScanSource } from './ports-GQy0x-RI.cjs';
|
|
3
|
+
import { j as KanbanBoard } from './planning-CHrOqQ6b.cjs';
|
|
4
|
+
import { e as KanbanScanText, b as KanbanScanInput } from './discovery-9E-LfwIw.cjs';
|
|
5
|
+
|
|
6
|
+
interface KanbanDiscoveryHandle {
|
|
7
|
+
scan(): Promise<void>;
|
|
8
|
+
tick(): Promise<void>;
|
|
9
|
+
reset(runId: string): Promise<void>;
|
|
10
|
+
quiesce(): Promise<void>;
|
|
11
|
+
dispose(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
declare function createKanbanDiscovery(options: KanbanDiscoveryOptions): KanbanDiscoveryHandle;
|
|
14
|
+
|
|
15
|
+
declare function discoveryState(state: {
|
|
16
|
+
discovery?: unknown;
|
|
17
|
+
} | null | undefined): KanbanDiscoveryState | undefined;
|
|
18
|
+
|
|
19
|
+
/** A bounded working set, with explicit omitted counts instead of pretending full coverage. */
|
|
20
|
+
declare function scanContext(board: KanbanBoard, state: KanbanState, sessions: KanbanScanText[], options: KanbanDiscoveryOptions, runId: string): KanbanScanInput;
|
|
21
|
+
|
|
22
|
+
export { type KanbanDiscoveryHandle, KanbanDiscoveryOptions, KanbanDiscoveryState, scanContext as buildDiscoveryContext, createKanbanDiscovery, discoveryState };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { f as KanbanDiscoveryOptions, g as KanbanDiscoveryState, d as KanbanState } from './ports-B0McVg7B.js';
|
|
2
|
+
export { h as KanbanScanExecutor, c as KanbanScanNotStartedError, i as KanbanScanSource } from './ports-B0McVg7B.js';
|
|
3
|
+
import { j as KanbanBoard } from './planning-CHrOqQ6b.js';
|
|
4
|
+
import { e as KanbanScanText, b as KanbanScanInput } from './discovery-D60yrjej.js';
|
|
5
|
+
|
|
6
|
+
interface KanbanDiscoveryHandle {
|
|
7
|
+
scan(): Promise<void>;
|
|
8
|
+
tick(): Promise<void>;
|
|
9
|
+
reset(runId: string): Promise<void>;
|
|
10
|
+
quiesce(): Promise<void>;
|
|
11
|
+
dispose(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
declare function createKanbanDiscovery(options: KanbanDiscoveryOptions): KanbanDiscoveryHandle;
|
|
14
|
+
|
|
15
|
+
declare function discoveryState(state: {
|
|
16
|
+
discovery?: unknown;
|
|
17
|
+
} | null | undefined): KanbanDiscoveryState | undefined;
|
|
18
|
+
|
|
19
|
+
/** A bounded working set, with explicit omitted counts instead of pretending full coverage. */
|
|
20
|
+
declare function scanContext(board: KanbanBoard, state: KanbanState, sessions: KanbanScanText[], options: KanbanDiscoveryOptions, runId: string): KanbanScanInput;
|
|
21
|
+
|
|
22
|
+
export { type KanbanDiscoveryHandle, KanbanDiscoveryOptions, KanbanDiscoveryState, scanContext as buildDiscoveryContext, createKanbanDiscovery, discoveryState };
|