crewly 1.20.308 → 1.20.310
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/README.md +2 -2
- package/config/skills/agent/remote-browser/SKILL.md +9 -4
- package/config/skills/agent/remote-browser/execute.sh +11 -1
- package/config/skills/agent/remote-browser/execute.test.sh +14 -0
- package/dist/backend/backend/src/constants.d.ts +20 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +21 -1
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/browser/browser.controller.js +1 -1
- package/dist/backend/backend/src/controllers/browser/browser.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts +7 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +83 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/board/board-interaction-hooks.d.ts +34 -0
- package/dist/backend/backend/src/services/board/board-interaction-hooks.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-interaction-hooks.js +60 -0
- package/dist/backend/backend/src/services/board/board-interaction-hooks.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-interactions.d.ts +54 -0
- package/dist/backend/backend/src/services/board/board-interactions.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-interactions.js +92 -0
- package/dist/backend/backend/src/services/board/board-interactions.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-links.d.ts +51 -0
- package/dist/backend/backend/src/services/board/board-links.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-links.js +168 -0
- package/dist/backend/backend/src/services/board/board-links.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-push.service.d.ts +8 -1
- package/dist/backend/backend/src/services/board/board-push.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/board/board-push.service.js +38 -1
- package/dist/backend/backend/src/services/board/board-push.service.js.map +1 -1
- package/dist/backend/backend/src/services/board/board-snapshot.builder.d.ts +32 -2
- package/dist/backend/backend/src/services/board/board-snapshot.builder.d.ts.map +1 -1
- package/dist/backend/backend/src/services/board/board-snapshot.builder.js +83 -8
- package/dist/backend/backend/src/services/board/board-snapshot.builder.js.map +1 -1
- package/dist/backend/backend/src/services/board/board.sources.d.ts.map +1 -1
- package/dist/backend/backend/src/services/board/board.sources.js +20 -1
- package/dist/backend/backend/src/services/board/board.sources.js.map +1 -1
- package/dist/backend/backend/src/services/board/board.types.d.ts +43 -0
- package/dist/backend/backend/src/services/board/board.types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/board/board.types.js +9 -1
- package/dist/backend/backend/src/services/board/board.types.js.map +1 -1
- package/dist/backend/backend/src/services/browser/browser-bridge.service.d.ts +51 -1
- package/dist/backend/backend/src/services/browser/browser-bridge.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/browser/browser-bridge.service.js +103 -6
- package/dist/backend/backend/src/services/browser/browser-bridge.service.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +20 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +21 -1
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/cli/src/commands/start.d.ts +1 -0
- package/dist/cli/cli/src/commands/start.d.ts.map +1 -1
- package/dist/cli/cli/src/commands/start.js +13 -3
- package/dist/cli/cli/src/commands/start.js.map +1 -1
- package/dist/cli/cli/src/index.js +3 -2
- package/dist/cli/cli/src/index.js.map +1 -1
- package/dist/cli/cli/src/utils/open-dashboard.d.ts +38 -0
- package/dist/cli/cli/src/utils/open-dashboard.d.ts.map +1 -0
- package/dist/cli/cli/src/utils/open-dashboard.js +40 -0
- package/dist/cli/cli/src/utils/open-dashboard.js.map +1 -0
- package/frontend/dist/assets/{index-368419df.js → index-1b4ac286.js} +287 -287
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the board learns that one agent talked to another (canvas
|
|
3
|
+
* `messaging` links): a middleware on the terminal-write API (what the
|
|
4
|
+
* `send-message` skill calls) and a chat-message listener for agent messages
|
|
5
|
+
* that @mention another agent (rooms, DMs, Slack mirrored into chat).
|
|
6
|
+
* Only sender, receiver and time are kept.
|
|
7
|
+
*
|
|
8
|
+
* @module services/board/board-interaction-hooks
|
|
9
|
+
*/
|
|
10
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
11
|
+
import { type AgentInteractionRecorder } from './board-interactions.js';
|
|
12
|
+
/**
|
|
13
|
+
* Middleware (mounted on `/api`): an agent's successful write to another
|
|
14
|
+
* agent's terminal is a message.
|
|
15
|
+
*
|
|
16
|
+
* @param recorder - Where to record (default: process-wide)
|
|
17
|
+
* @returns Express middleware; never blocks or throws
|
|
18
|
+
*/
|
|
19
|
+
export declare function recordTerminalWrites(recorder?: AgentInteractionRecorder): (req: Request, res: Response, next: NextFunction) => void;
|
|
20
|
+
/** The parts of a chat message the board reads. */
|
|
21
|
+
export interface MentionMessage {
|
|
22
|
+
senderType: string;
|
|
23
|
+
senderId: string;
|
|
24
|
+
mentions?: string[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* An agent's chat message that @mentions other agents.
|
|
28
|
+
*
|
|
29
|
+
* @param message - Chat message
|
|
30
|
+
* @param resolve - Member id or session -> session (undefined for a team or the owner)
|
|
31
|
+
* @param recorder - Where to record
|
|
32
|
+
*/
|
|
33
|
+
export declare function recordChatMentions(message: MentionMessage, resolve: (idOrSession: string) => string | undefined, recorder?: AgentInteractionRecorder): void;
|
|
34
|
+
//# sourceMappingURL=board-interaction-hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-interaction-hooks.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-interaction-hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,OAAO,EAAwB,KAAK,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAI9F;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,GAAE,wBAAiD,IACvF,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAAI,CAmB9D;AAED,mDAAmD;AACnD,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,EAAE,QAAQ,GAAE,wBAAiD,GAAG,IAAI,CAOnL"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the board learns that one agent talked to another (canvas
|
|
3
|
+
* `messaging` links): a middleware on the terminal-write API (what the
|
|
4
|
+
* `send-message` skill calls) and a chat-message listener for agent messages
|
|
5
|
+
* that @mention another agent (rooms, DMs, Slack mirrored into chat).
|
|
6
|
+
* Only sender, receiver and time are kept.
|
|
7
|
+
*
|
|
8
|
+
* @module services/board/board-interaction-hooks
|
|
9
|
+
*/
|
|
10
|
+
import { readAgentSessionHeader } from '../../utils/agent-caller.utils.js';
|
|
11
|
+
import { getAgentInteractions } from './board-interactions.js';
|
|
12
|
+
const WRITE_PATH = /^\/terminal\/([^/]+)\/write\/?$/;
|
|
13
|
+
/**
|
|
14
|
+
* Middleware (mounted on `/api`): an agent's successful write to another
|
|
15
|
+
* agent's terminal is a message.
|
|
16
|
+
*
|
|
17
|
+
* @param recorder - Where to record (default: process-wide)
|
|
18
|
+
* @returns Express middleware; never blocks or throws
|
|
19
|
+
*/
|
|
20
|
+
export function recordTerminalWrites(recorder = getAgentInteractions()) {
|
|
21
|
+
return (req, res, next) => {
|
|
22
|
+
try {
|
|
23
|
+
if (req.method === 'POST') {
|
|
24
|
+
const match = WRITE_PATH.exec(req.path);
|
|
25
|
+
if (match) {
|
|
26
|
+
const from = readAgentSessionHeader(req);
|
|
27
|
+
const to = decodeURIComponent(match[1]);
|
|
28
|
+
if (from && to && from !== to) {
|
|
29
|
+
res.once('finish', () => {
|
|
30
|
+
if (res.statusCode >= 200 && res.statusCode < 300)
|
|
31
|
+
recorder.record(from, to);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
/* observing never breaks the request */
|
|
39
|
+
}
|
|
40
|
+
next();
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* An agent's chat message that @mentions other agents.
|
|
45
|
+
*
|
|
46
|
+
* @param message - Chat message
|
|
47
|
+
* @param resolve - Member id or session -> session (undefined for a team or the owner)
|
|
48
|
+
* @param recorder - Where to record
|
|
49
|
+
*/
|
|
50
|
+
export function recordChatMentions(message, resolve, recorder = getAgentInteractions()) {
|
|
51
|
+
if (message.senderType !== 'agent' || !message.mentions || message.mentions.length === 0)
|
|
52
|
+
return;
|
|
53
|
+
const from = resolve(message.senderId) ?? message.senderId;
|
|
54
|
+
for (const m of message.mentions.slice(0, 10)) {
|
|
55
|
+
const to = resolve(m);
|
|
56
|
+
if (to && to !== from)
|
|
57
|
+
recorder.record(from, to);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=board-interaction-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-interaction-hooks.js","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-interaction-hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAiC,MAAM,yBAAyB,CAAC;AAE9F,MAAM,UAAU,GAAG,iCAAiC,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAAqC,oBAAoB,EAAE;IAC/F,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAQ,EAAE;QAChE,IAAI,CAAC;YACJ,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC3B,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACxC,IAAI,KAAK,EAAE,CAAC;oBACX,MAAM,IAAI,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;oBACzC,MAAM,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;oBACxC,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;wBAC/B,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE;4BACvB,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,GAAG,GAAG;gCAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;wBAC9E,CAAC,CAAC,CAAC;oBACJ,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,wCAAwC;QACzC,CAAC;QACD,IAAI,EAAE,CAAC;IACR,CAAC,CAAC;AACH,CAAC;AASD;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAuB,EAAE,OAAoD,EAAE,WAAqC,oBAAoB,EAAE;IAC5K,IAAI,OAAO,CAAC,UAAU,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACjG,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC;IAC3D,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QAC/C,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,EAAE,IAAI,EAAE,KAAK,IAAI;YAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAClD,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who messaged whom (crewly#1247 canvas links): a small in-memory record of
|
|
3
|
+
* agent-to-agent messages, kept for the last two hours. The board turns it into
|
|
4
|
+
* `messaging` links. Contents are never kept, only sender, receiver and time.
|
|
5
|
+
*
|
|
6
|
+
* @module services/board/board-interactions
|
|
7
|
+
*/
|
|
8
|
+
/** One sender -> receiver pair as the board reads it. */
|
|
9
|
+
export interface InteractionPair {
|
|
10
|
+
from: string;
|
|
11
|
+
to: string;
|
|
12
|
+
/** Messages in the window */
|
|
13
|
+
count: number;
|
|
14
|
+
firstAt: number;
|
|
15
|
+
lastAt: number;
|
|
16
|
+
}
|
|
17
|
+
/** Records agent-to-agent messages. */
|
|
18
|
+
export declare class AgentInteractionRecorder {
|
|
19
|
+
private readonly pairs;
|
|
20
|
+
private readonly listeners;
|
|
21
|
+
/**
|
|
22
|
+
* Note a message.
|
|
23
|
+
*
|
|
24
|
+
* @param from - Sender session (`<session>@<instanceId>` when on another machine)
|
|
25
|
+
* @param to - Receiver session
|
|
26
|
+
* @param now - Clock (tests)
|
|
27
|
+
*/
|
|
28
|
+
record(from: string, to: string, now?: number): void;
|
|
29
|
+
/**
|
|
30
|
+
* Be told about each message (the push marks the sender dirty).
|
|
31
|
+
*
|
|
32
|
+
* @param listener - Called with sender and receiver
|
|
33
|
+
* @returns Unsubscribe
|
|
34
|
+
*/
|
|
35
|
+
onRecord(listener: (from: string, to: string) => void): () => void;
|
|
36
|
+
/**
|
|
37
|
+
* Pairs with a message in the window; older entries are forgotten.
|
|
38
|
+
*
|
|
39
|
+
* @param now - Clock
|
|
40
|
+
* @returns Pairs
|
|
41
|
+
*/
|
|
42
|
+
pairsNow(now?: number): InteractionPair[];
|
|
43
|
+
/** Forget everything (tests). */
|
|
44
|
+
clear(): void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The process-wide recorder.
|
|
48
|
+
*
|
|
49
|
+
* @returns The recorder
|
|
50
|
+
*/
|
|
51
|
+
export declare function getAgentInteractions(): AgentInteractionRecorder;
|
|
52
|
+
/** Drop the process-wide recorder (tests). */
|
|
53
|
+
export declare function resetAgentInteractions(): void;
|
|
54
|
+
//# sourceMappingURL=board-interactions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-interactions.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-interactions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,yDAAyD;AACzD,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,uCAAuC;AACvC,qBAAa,wBAAwB;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoE;IAC1F,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiD;IAE3E;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,IAAI;IAmBhE;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAKlE;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,GAAE,MAAmB,GAAG,eAAe,EAAE;IAerD,iCAAiC;IACjC,KAAK,IAAI,IAAI;CAGb;AAID;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,wBAAwB,CAG/D;AAED,8CAA8C;AAC9C,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who messaged whom (crewly#1247 canvas links): a small in-memory record of
|
|
3
|
+
* agent-to-agent messages, kept for the last two hours. The board turns it into
|
|
4
|
+
* `messaging` links. Contents are never kept, only sender, receiver and time.
|
|
5
|
+
*
|
|
6
|
+
* @module services/board/board-interactions
|
|
7
|
+
*/
|
|
8
|
+
import { BOARD_CONSTANTS } from '../../constants.js';
|
|
9
|
+
const C = BOARD_CONSTANTS;
|
|
10
|
+
/** Records agent-to-agent messages. */
|
|
11
|
+
export class AgentInteractionRecorder {
|
|
12
|
+
pairs = new Map();
|
|
13
|
+
listeners = new Set();
|
|
14
|
+
/**
|
|
15
|
+
* Note a message.
|
|
16
|
+
*
|
|
17
|
+
* @param from - Sender session (`<session>@<instanceId>` when on another machine)
|
|
18
|
+
* @param to - Receiver session
|
|
19
|
+
* @param now - Clock (tests)
|
|
20
|
+
*/
|
|
21
|
+
record(from, to, now = Date.now()) {
|
|
22
|
+
if (!from || !to || from === to)
|
|
23
|
+
return;
|
|
24
|
+
const key = `${from}\u0000${to}`;
|
|
25
|
+
let pair = this.pairs.get(key);
|
|
26
|
+
if (!pair) {
|
|
27
|
+
pair = { from, to, times: [] };
|
|
28
|
+
this.pairs.set(key, pair);
|
|
29
|
+
}
|
|
30
|
+
pair.times.push(now);
|
|
31
|
+
if (pair.times.length > C.LINK_MESSAGING_MAX_PER_PAIR)
|
|
32
|
+
pair.times.splice(0, pair.times.length - C.LINK_MESSAGING_MAX_PER_PAIR);
|
|
33
|
+
for (const l of this.listeners) {
|
|
34
|
+
try {
|
|
35
|
+
l(from, to);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
/* a listener never breaks the sender */
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Be told about each message (the push marks the sender dirty).
|
|
44
|
+
*
|
|
45
|
+
* @param listener - Called with sender and receiver
|
|
46
|
+
* @returns Unsubscribe
|
|
47
|
+
*/
|
|
48
|
+
onRecord(listener) {
|
|
49
|
+
this.listeners.add(listener);
|
|
50
|
+
return () => this.listeners.delete(listener);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Pairs with a message in the window; older entries are forgotten.
|
|
54
|
+
*
|
|
55
|
+
* @param now - Clock
|
|
56
|
+
* @returns Pairs
|
|
57
|
+
*/
|
|
58
|
+
pairsNow(now = Date.now()) {
|
|
59
|
+
const out = [];
|
|
60
|
+
const cutoff = now - C.LINK_MESSAGING_WINDOW_MS;
|
|
61
|
+
for (const [key, pair] of this.pairs) {
|
|
62
|
+
const first = pair.times.findIndex((t) => t >= cutoff);
|
|
63
|
+
if (first < 0) {
|
|
64
|
+
this.pairs.delete(key);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (first > 0)
|
|
68
|
+
pair.times.splice(0, first);
|
|
69
|
+
out.push({ from: pair.from, to: pair.to, count: pair.times.length, firstAt: pair.times[0], lastAt: pair.times[pair.times.length - 1] });
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/** Forget everything (tests). */
|
|
74
|
+
clear() {
|
|
75
|
+
this.pairs.clear();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
let instance = null;
|
|
79
|
+
/**
|
|
80
|
+
* The process-wide recorder.
|
|
81
|
+
*
|
|
82
|
+
* @returns The recorder
|
|
83
|
+
*/
|
|
84
|
+
export function getAgentInteractions() {
|
|
85
|
+
instance ??= new AgentInteractionRecorder();
|
|
86
|
+
return instance;
|
|
87
|
+
}
|
|
88
|
+
/** Drop the process-wide recorder (tests). */
|
|
89
|
+
export function resetAgentInteractions() {
|
|
90
|
+
instance = null;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=board-interactions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-interactions.js","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-interactions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,CAAC,GAAG,eAAe,CAAC;AAY1B,uCAAuC;AACvC,MAAM,OAAO,wBAAwB;IACnB,KAAK,GAAG,IAAI,GAAG,EAAyD,CAAC;IACzE,SAAS,GAAG,IAAI,GAAG,EAAsC,CAAC;IAE3E;;;;;;OAMG;IACH,MAAM,CAAC,IAAY,EAAE,EAAU,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;QACxD,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,KAAK,EAAE;YAAE,OAAO;QACxC,MAAM,GAAG,GAAG,GAAG,IAAI,SAAS,EAAE,EAAE,CAAC;QACjC,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,IAAI,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;YAC/B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrB,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,2BAA2B;YAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,2BAA2B,CAAC,CAAC;QAC/H,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC;gBACJ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACb,CAAC;YAAC,MAAM,CAAC;gBACR,wCAAwC;YACzC,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACH,QAAQ,CAAC,QAA4C;QACpD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED;;;;;OAKG;IACH,QAAQ,CAAC,MAAc,IAAI,CAAC,GAAG,EAAE;QAChC,MAAM,GAAG,GAAsB,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,wBAAwB,CAAC;QAChD,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;YACvD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACf,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACvB,SAAS;YACV,CAAC;YACD,IAAI,KAAK,GAAG,CAAC;gBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YAC3C,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QACzI,CAAC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC;IAED,iCAAiC;IACjC,KAAK;QACJ,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;CACD;AAED,IAAI,QAAQ,GAAoC,IAAI,CAAC;AAErD;;;;GAIG;AACH,MAAM,UAAU,oBAAoB;IACnC,QAAQ,KAAK,IAAI,wBAAwB,EAAE,CAAC;IAC5C,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,sBAAsB;IACrC,QAAQ,GAAG,IAAI,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas links (crewly#1247, CANVAS-V2-CONTRACT.md): who reports to whom and
|
|
3
|
+
* who works with whom. Pure functions of what the board already read, so a
|
|
4
|
+
* delta never touches the disk.
|
|
5
|
+
*
|
|
6
|
+
* @module services/board/board-links
|
|
7
|
+
*/
|
|
8
|
+
import type { InteractionPair } from './board-interactions.js';
|
|
9
|
+
import type { BoardTeamInfo, BoardTicket, BoardWorkItem } from './board-snapshot.builder.js';
|
|
10
|
+
import { type BoardLink } from './board.types.js';
|
|
11
|
+
/** What the structural links read. */
|
|
12
|
+
export interface StructuralLinkInput {
|
|
13
|
+
now: number;
|
|
14
|
+
orchestrator: string;
|
|
15
|
+
teams: BoardTeamInfo[];
|
|
16
|
+
workItems: BoardWorkItem[];
|
|
17
|
+
tickets: BoardTicket[];
|
|
18
|
+
/** Each agent's current task (a WorkItem id or a ticket id) */
|
|
19
|
+
tasks: Map<string, {
|
|
20
|
+
id?: string;
|
|
21
|
+
title: string;
|
|
22
|
+
since: string;
|
|
23
|
+
}>;
|
|
24
|
+
/** Sessions that are stopped (their reporting line is not active) */
|
|
25
|
+
stopped: ReadonlySet<string>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Reporting, delegation and shared-ticket links.
|
|
29
|
+
*
|
|
30
|
+
* @param input - Teams, work and tickets
|
|
31
|
+
* @returns Links, one per identity
|
|
32
|
+
*/
|
|
33
|
+
export declare function structuralLinks(input: StructuralLinkInput): BoardLink[];
|
|
34
|
+
/**
|
|
35
|
+
* Messaging links from the recorder.
|
|
36
|
+
*
|
|
37
|
+
* @param pairs - Sender/receiver pairs in the last two hours
|
|
38
|
+
* @param now - Clock
|
|
39
|
+
* @param isKnown - Whether an endpoint is an agent of this machine or qualified
|
|
40
|
+
* @returns Links
|
|
41
|
+
*/
|
|
42
|
+
export declare function messagingLinks(pairs: readonly InteractionPair[], now: number, isKnown: (session: string) => boolean): BoardLink[];
|
|
43
|
+
/**
|
|
44
|
+
* Cap a link list: messaging, then same_task, are dropped first.
|
|
45
|
+
*
|
|
46
|
+
* @param links - Links
|
|
47
|
+
* @param max - Most links to keep
|
|
48
|
+
* @returns At most `max` links
|
|
49
|
+
*/
|
|
50
|
+
export declare function capLinks(links: BoardLink[], max?: number): BoardLink[];
|
|
51
|
+
//# sourceMappingURL=board-links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-links.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-links.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC7F,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAkB3D,sCAAsC;AACtC,MAAM,WAAW,mBAAmB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,+DAA+D;IAC/D,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClE,qEAAqE;IACrE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,mBAAmB,GAAG,SAAS,EAAE,CA6FvE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,EAAE,CAOjI;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,GAAG,GAAE,MAAoB,GAAG,SAAS,EAAE,CAMnF"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas links (crewly#1247, CANVAS-V2-CONTRACT.md): who reports to whom and
|
|
3
|
+
* who works with whom. Pure functions of what the board already read, so a
|
|
4
|
+
* delta never touches the disk.
|
|
5
|
+
*
|
|
6
|
+
* @module services/board/board-links
|
|
7
|
+
*/
|
|
8
|
+
import { BOARD_CONSTANTS } from '../../constants.js';
|
|
9
|
+
import { scrubText } from './board-redact.js';
|
|
10
|
+
import { linkKey } from './board.types.js';
|
|
11
|
+
const C = BOARD_CONSTANTS;
|
|
12
|
+
/** Work states that no longer count as open. */
|
|
13
|
+
const CLOSED = new Set(['done', 'verified', 'cancelled', 'rejected', 'failed']);
|
|
14
|
+
/** Closed states that still show as finished delegations. */
|
|
15
|
+
const FINISHED_OK = new Set(['done', 'verified']);
|
|
16
|
+
const iso = (ms) => new Date(ms).toISOString();
|
|
17
|
+
const msOf = (s) => (s ? Date.parse(s) : NaN);
|
|
18
|
+
/** The label of a link: scrubbed, short. */
|
|
19
|
+
function label(text) {
|
|
20
|
+
const t = text ? scrubText(text, C.LINK_LABEL_MAX) : '';
|
|
21
|
+
return t ? t.slice(0, C.LINK_LABEL_MAX) : undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Reporting, delegation and shared-ticket links.
|
|
25
|
+
*
|
|
26
|
+
* @param input - Teams, work and tickets
|
|
27
|
+
* @returns Links, one per identity
|
|
28
|
+
*/
|
|
29
|
+
export function structuralLinks(input) {
|
|
30
|
+
const { now, orchestrator, teams, workItems, tickets, tasks, stopped } = input;
|
|
31
|
+
const out = new Map();
|
|
32
|
+
const put = (l) => {
|
|
33
|
+
if (l.from === l.to)
|
|
34
|
+
return;
|
|
35
|
+
out.set(linkKey(l), l);
|
|
36
|
+
};
|
|
37
|
+
const known = new Set([orchestrator]);
|
|
38
|
+
const teamOf = new Map();
|
|
39
|
+
for (const t of teams)
|
|
40
|
+
for (const m of t.members) {
|
|
41
|
+
known.add(m.session);
|
|
42
|
+
teamOf.set(m.session, t);
|
|
43
|
+
}
|
|
44
|
+
const isKnown = (s) => known.has(s) || s.includes('@');
|
|
45
|
+
// --- reports_to: members -> their leaders, leaders -> the orchestrator
|
|
46
|
+
for (const t of teams) {
|
|
47
|
+
if (t.teamId === 'orchestrator')
|
|
48
|
+
continue;
|
|
49
|
+
const leaders = (t.leaders ?? []).filter((s) => t.members.some((m) => m.session === s));
|
|
50
|
+
const since = t.createdAt ?? iso(now);
|
|
51
|
+
for (const m of t.members) {
|
|
52
|
+
if (leaders.includes(m.session)) {
|
|
53
|
+
put({ from: m.session, to: orchestrator, kind: 'reports_to', active: !stopped.has(m.session), since });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
for (const lead of leaders)
|
|
57
|
+
put({ from: m.session, to: lead, kind: 'reports_to', active: !stopped.has(m.session), since });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// --- delegated: delegator -> worker, per pair
|
|
62
|
+
const windowStart = now - C.LINK_DELEGATION_WINDOW_MS;
|
|
63
|
+
const delegations = new Map();
|
|
64
|
+
for (const w of workItems) {
|
|
65
|
+
const to = w.target;
|
|
66
|
+
if (!to || !isKnown(to))
|
|
67
|
+
continue;
|
|
68
|
+
let from = w.delegatedBy;
|
|
69
|
+
if (!from) {
|
|
70
|
+
if (w.owner === 'orchestrator')
|
|
71
|
+
from = orchestrator;
|
|
72
|
+
else if (w.owner === 'team_lead')
|
|
73
|
+
from = teamOf.get(to)?.leaders?.find((s) => s !== to);
|
|
74
|
+
}
|
|
75
|
+
if (!from || from === to || !isKnown(from))
|
|
76
|
+
continue;
|
|
77
|
+
const open = !CLOSED.has(w.status);
|
|
78
|
+
const created = msOf(w.createdAt);
|
|
79
|
+
if (!open) {
|
|
80
|
+
const done = msOf(w.completedAt) || msOf(w.statusChangedAt) || created;
|
|
81
|
+
if (!FINISHED_OK.has(w.status) || !(done >= windowStart))
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const key = `${from}\u0000${to}`;
|
|
85
|
+
const entry = delegations.get(key) ?? { from, to, items: [] };
|
|
86
|
+
entry.items.push({ w, open, at: Number.isFinite(created) ? created : now });
|
|
87
|
+
delegations.set(key, entry);
|
|
88
|
+
}
|
|
89
|
+
for (const { from, to, items } of delegations.values()) {
|
|
90
|
+
const open = items.filter((i) => i.open);
|
|
91
|
+
const pool = open.length > 0 ? open : items;
|
|
92
|
+
const newest = pool.reduce((a, b) => (b.at > a.at ? b : a));
|
|
93
|
+
put({ from, to, kind: 'delegated', active: open.length > 0, since: iso(newest.at), ...(label(newest.w.title) ? { label: label(newest.w.title) } : {}), weight: items.length });
|
|
94
|
+
}
|
|
95
|
+
// --- same_task: two agents on one ticket right now
|
|
96
|
+
const ticketTitle = new Map();
|
|
97
|
+
for (const t of tickets) {
|
|
98
|
+
ticketTitle.set(t.id, t.label ? `${t.label} ${t.title}` : t.title);
|
|
99
|
+
if (t.label)
|
|
100
|
+
ticketTitle.set(t.label, `${t.label} ${t.title}`);
|
|
101
|
+
}
|
|
102
|
+
const groups = new Map();
|
|
103
|
+
const join = (key, session, since) => {
|
|
104
|
+
const g = groups.get(key) ?? new Map();
|
|
105
|
+
g.set(session, Math.max(g.get(session) ?? 0, Number.isFinite(since) ? since : now));
|
|
106
|
+
groups.set(key, g);
|
|
107
|
+
};
|
|
108
|
+
for (const w of workItems) {
|
|
109
|
+
if (w.status !== 'running' || !w.target || !isKnown(w.target))
|
|
110
|
+
continue;
|
|
111
|
+
const key = w.projectTaskId ?? w.requestId;
|
|
112
|
+
if (key)
|
|
113
|
+
join(key, w.target, msOf(w.startedAt) || msOf(w.createdAt));
|
|
114
|
+
}
|
|
115
|
+
for (const [session, task] of tasks) {
|
|
116
|
+
if (task.id && ticketTitle.has(task.id) && isKnown(session))
|
|
117
|
+
join(task.id, session, msOf(task.since));
|
|
118
|
+
}
|
|
119
|
+
for (const [key, members] of groups) {
|
|
120
|
+
if (members.size < 2)
|
|
121
|
+
continue;
|
|
122
|
+
const sessions = [...members.keys()].sort().slice(0, C.LINK_SAME_TASK_MAX_GROUP);
|
|
123
|
+
const text = label(ticketTitle.get(key) ?? workItems.find((w) => w.projectTaskId === key || w.requestId === key)?.title);
|
|
124
|
+
for (let i = 0; i < sessions.length; i += 1) {
|
|
125
|
+
for (let j = i + 1; j < sessions.length; j += 1) {
|
|
126
|
+
const since = Math.max(members.get(sessions[i]) ?? now, members.get(sessions[j]) ?? now);
|
|
127
|
+
const link = { from: sessions[i], to: sessions[j], kind: 'same_task', active: true, since: iso(since), ...(text ? { label: text } : {}) };
|
|
128
|
+
// Two tickets shared by one pair: the earlier key keeps the pair
|
|
129
|
+
if (!out.has(linkKey(link)))
|
|
130
|
+
put(link);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return [...out.values()];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Messaging links from the recorder.
|
|
138
|
+
*
|
|
139
|
+
* @param pairs - Sender/receiver pairs in the last two hours
|
|
140
|
+
* @param now - Clock
|
|
141
|
+
* @param isKnown - Whether an endpoint is an agent of this machine or qualified
|
|
142
|
+
* @returns Links
|
|
143
|
+
*/
|
|
144
|
+
export function messagingLinks(pairs, now, isKnown) {
|
|
145
|
+
const out = [];
|
|
146
|
+
for (const p of pairs) {
|
|
147
|
+
if (!isKnown(p.from) || !isKnown(p.to))
|
|
148
|
+
continue;
|
|
149
|
+
out.push({ from: p.from, to: p.to, kind: 'messaging', active: now - p.lastAt <= C.LINK_MESSAGING_ACTIVE_MS, since: iso(p.firstAt), weight: p.count });
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Cap a link list: messaging, then same_task, are dropped first.
|
|
155
|
+
*
|
|
156
|
+
* @param links - Links
|
|
157
|
+
* @param max - Most links to keep
|
|
158
|
+
* @returns At most `max` links
|
|
159
|
+
*/
|
|
160
|
+
export function capLinks(links, max = C.LINKS_MAX) {
|
|
161
|
+
if (links.length <= max)
|
|
162
|
+
return links;
|
|
163
|
+
const rank = { reports_to: 0, delegated: 1, same_task: 2, messaging: 3 };
|
|
164
|
+
return [...links]
|
|
165
|
+
.sort((a, b) => rank[a.kind] - rank[b.kind] || Number(b.active) - Number(a.active) || Date.parse(b.since) - Date.parse(a.since))
|
|
166
|
+
.slice(0, max);
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=board-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-links.js","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-links.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
import { type ComponentLogger } from '../core/logger.service.js';
|
|
22
22
|
import type { BoardActivityTracker } from './board-activity.tracker.js';
|
|
23
23
|
import type { BoardSnapshotBuilder } from './board-snapshot.builder.js';
|
|
24
|
-
import type
|
|
24
|
+
import { type AgentInteractionRecorder } from './board-interactions.js';
|
|
25
|
+
import { type BoardSnapshot } from './board.types.js';
|
|
25
26
|
/** The Cloud session (CloudClientService). */
|
|
26
27
|
export interface BoardCloudSession {
|
|
27
28
|
getToken(): string | null;
|
|
@@ -39,6 +40,8 @@ export interface BoardPushDeps {
|
|
|
39
40
|
setTimeout?: (fn: () => void, ms: number) => ReturnType<typeof setTimeout>;
|
|
40
41
|
clearTimeout?: (handle: ReturnType<typeof setTimeout>) => void;
|
|
41
42
|
logger?: ComponentLogger;
|
|
43
|
+
/** Agent-to-agent messages (default: the process-wide recorder) */
|
|
44
|
+
interactions?: AgentInteractionRecorder;
|
|
42
45
|
}
|
|
43
46
|
/** What one push did. */
|
|
44
47
|
export type BoardPushOutcome = 'sent' | 'nothing' | 'skipped' | 'paused' | 'failed';
|
|
@@ -72,6 +75,10 @@ export declare class BoardPushService {
|
|
|
72
75
|
private readonly dirty;
|
|
73
76
|
/** Rows Cloud has, as JSON, by session */
|
|
74
77
|
private readonly lastSent;
|
|
78
|
+
/** Links Cloud has, as JSON, by identity */
|
|
79
|
+
private readonly lastLinks;
|
|
80
|
+
/** A message was recorded since the last delta: the links may differ */
|
|
81
|
+
private linksDirty;
|
|
75
82
|
private lastToken;
|
|
76
83
|
private fullOk;
|
|
77
84
|
private fullAfterMs;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"board-push.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-push.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"board-push.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-push.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAwB,KAAK,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAC9F,OAAO,EAA4C,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKhG,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IACjC,QAAQ,IAAI,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,IAAI,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACrC;AAED,gCAAgC;AAChC,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,oBAAoB,CAAC;IAC9B,OAAO,EAAE,oBAAoB,CAAC;IAC9B,KAAK,EAAE,iBAAiB,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;IAC3E,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,CAAC;IAC/D,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,mEAAmE;IACnE,YAAY,CAAC,EAAE,wBAAwB,CAAC;CACxC;AAED,yBAAyB;AACzB,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEpF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAGjF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,aAAa,CAmBlE;AAED,mCAAmC;AACnC,qBAAa,gBAAgB;IA4BhB,OAAO,CAAC,QAAQ,CAAC,IAAI;IA3BjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0D;IACpF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgE;IACzF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkD;IAE7E,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,0CAA0C;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,4CAA4C;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IACvD,wEAAwE;IACxE,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,WAAW,CAAS;IAC5B,iEAAiE;IACjE,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,QAAQ,CAAuC;gBAE1B,IAAI,EAAE,aAAa;IAchD;;;;OAIG;IACH,MAAM,IAAI,OAAO;IAIjB;;;OAGG;IACH,UAAU,IAAI,IAAI;IAIlB,2DAA2D;IAC3D,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,0EAA0E;IAC1E,KAAK,IAAI,IAAI;IAkBb,wCAAwC;IACxC,IAAI,IAAI,IAAI;IAWZ;;;;OAIG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAUhC,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,aAAa;IAMrB,gGAAgG;IAChG,OAAO,CAAC,MAAM;IAcd;;;;;OAKG;IACH,QAAQ,IAAI,OAAO,CAAC,gBAAgB,CAAC;YAMvB,MAAM;IAoBpB;;;;;OAKG;IACH,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC;YAQzB,OAAO;IAwDrB,kHAAkH;IAClH,OAAO,CAAC,cAAc;YAaR,IAAI;CAwDlB"}
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
import { gzipSync } from 'zlib';
|
|
22
22
|
import { BOARD_CONSTANTS, CANVAS_CONSTANTS } from '../../constants.js';
|
|
23
23
|
import { LoggerService } from '../core/logger.service.js';
|
|
24
|
+
import { getAgentInteractions } from './board-interactions.js';
|
|
25
|
+
import { linkKey } from './board.types.js';
|
|
24
26
|
const C = BOARD_CONSTANTS;
|
|
25
27
|
const CV = CANVAS_CONSTANTS;
|
|
26
28
|
/**
|
|
@@ -42,6 +44,9 @@ export function isBoardPushDisabled(env = process.env) {
|
|
|
42
44
|
*/
|
|
43
45
|
export function fitSnapshot(snapshot) {
|
|
44
46
|
const size = () => Buffer.byteLength(JSON.stringify(snapshot));
|
|
47
|
+
// Links go first: a map of lines is the least important part of the body
|
|
48
|
+
while (snapshot.links && snapshot.links.length > 0 && size() > C.MAX_BODY_BYTES)
|
|
49
|
+
snapshot.links.length = Math.floor(snapshot.links.length / 2);
|
|
45
50
|
const lists = [snapshot.outcomes.doneToday, snapshot.outcomes.stuck, snapshot.outcomes.waitingOnOwner];
|
|
46
51
|
let guard = 0;
|
|
47
52
|
while (size() > C.MAX_BODY_BYTES && guard < 500) {
|
|
@@ -74,6 +79,10 @@ export class BoardPushService {
|
|
|
74
79
|
dirty = new Set();
|
|
75
80
|
/** Rows Cloud has, as JSON, by session */
|
|
76
81
|
lastSent = new Map();
|
|
82
|
+
/** Links Cloud has, as JSON, by identity */
|
|
83
|
+
lastLinks = new Map();
|
|
84
|
+
/** A message was recorded since the last delta: the links may differ */
|
|
85
|
+
linksDirty = false;
|
|
77
86
|
lastToken = null;
|
|
78
87
|
fullOk = false;
|
|
79
88
|
fullAfterMs = C.FULL_INTERVAL_MS;
|
|
@@ -124,9 +133,14 @@ export class BoardPushService {
|
|
|
124
133
|
// A step changed: the agent's row did not necessarily, but a delta is due
|
|
125
134
|
const offRows = this.deps.tracker.onChange((session) => this.markDirty(session));
|
|
126
135
|
const offSteps = this.deps.tracker.steps.onChange((session) => this.markDirty(session));
|
|
136
|
+
const offMessages = (this.deps.interactions ?? getAgentInteractions()).onRecord((from) => {
|
|
137
|
+
this.linksDirty = true;
|
|
138
|
+
this.markDirty(from);
|
|
139
|
+
});
|
|
127
140
|
this.unsubscribe = () => {
|
|
128
141
|
offRows();
|
|
129
142
|
offSteps();
|
|
143
|
+
offMessages();
|
|
130
144
|
};
|
|
131
145
|
this.scheduleFull(0);
|
|
132
146
|
}
|
|
@@ -218,6 +232,10 @@ export class BoardPushService {
|
|
|
218
232
|
for (const team of snapshot.teams)
|
|
219
233
|
for (const a of team.agents)
|
|
220
234
|
this.lastSent.set(a.session, JSON.stringify(a));
|
|
235
|
+
this.lastLinks.clear();
|
|
236
|
+
for (const l of snapshot.links ?? [])
|
|
237
|
+
this.lastLinks.set(linkKey(l), JSON.stringify(l));
|
|
238
|
+
this.linksDirty = false;
|
|
221
239
|
this.dirty.clear();
|
|
222
240
|
// Timeline events ride in deltas
|
|
223
241
|
const pending = new Set([...this.deps.tracker.sessionsWithUnsent(), ...this.deps.tracker.steps.sessionsWithUnsent()]);
|
|
@@ -249,7 +267,18 @@ export class BoardPushService {
|
|
|
249
267
|
const changed = rows.filter((r) => this.lastSent.get(r.session) !== JSON.stringify(stripTeam(r)));
|
|
250
268
|
const timeline = this.deps.tracker.peekUnsent(sessions);
|
|
251
269
|
let steps = this.deps.tracker.steps.peekUnsent(sessions);
|
|
252
|
-
|
|
270
|
+
// Links are recomputed only after a message (the rest comes with the full build)
|
|
271
|
+
let links = [];
|
|
272
|
+
let removedLinks = [];
|
|
273
|
+
const linksWereDirty = this.linksDirty;
|
|
274
|
+
if (linksWereDirty) {
|
|
275
|
+
this.linksDirty = false;
|
|
276
|
+
const now = this.deps.builder.links();
|
|
277
|
+
links = now.filter((l) => this.lastLinks.get(linkKey(l)) !== JSON.stringify(l));
|
|
278
|
+
const keys = new Set(now.map(linkKey));
|
|
279
|
+
removedLinks = [...this.lastLinks.keys()].filter((k) => !keys.has(k));
|
|
280
|
+
}
|
|
281
|
+
if (changed.length === 0 && timeline.length === 0 && steps.length === 0 && links.length === 0 && removedLinks.length === 0)
|
|
253
282
|
return 'nothing';
|
|
254
283
|
const instanceId = (await this.deps.builder.identity()).instanceId;
|
|
255
284
|
const body = {
|
|
@@ -259,6 +288,8 @@ export class BoardPushService {
|
|
|
259
288
|
agents: changed,
|
|
260
289
|
...(timeline.length > 0 ? { timeline } : {}),
|
|
261
290
|
...(steps.length > 0 ? { steps } : {}),
|
|
291
|
+
...(links.length > 0 ? { links } : {}),
|
|
292
|
+
...(removedLinks.length > 0 ? { removedLinks } : {}),
|
|
262
293
|
};
|
|
263
294
|
// The body limit is on the uncompressed JSON: the newest steps go first when it does not fit
|
|
264
295
|
for (let guard = 0; guard < 20 && body.steps && Buffer.byteLength(JSON.stringify(body)) > C.MAX_BODY_BYTES; guard += 1) {
|
|
@@ -272,6 +303,10 @@ export class BoardPushService {
|
|
|
272
303
|
if (outcome === 'sent') {
|
|
273
304
|
for (const r of changed)
|
|
274
305
|
this.lastSent.set(r.session, JSON.stringify(stripTeam(r)));
|
|
306
|
+
for (const l of links)
|
|
307
|
+
this.lastLinks.set(linkKey(l), JSON.stringify(l));
|
|
308
|
+
for (const k of removedLinks)
|
|
309
|
+
this.lastLinks.delete(k);
|
|
275
310
|
this.deps.tracker.ackUnsent(timeline);
|
|
276
311
|
this.deps.tracker.steps.ackUnsent(steps);
|
|
277
312
|
// Steps the per-delta limit held back go in the next one
|
|
@@ -281,6 +316,8 @@ export class BoardPushService {
|
|
|
281
316
|
}
|
|
282
317
|
else if (outcome === 'failed') {
|
|
283
318
|
// keep them dirty for the next try (the full push also repairs everything)
|
|
319
|
+
if (linksWereDirty)
|
|
320
|
+
this.linksDirty = true;
|
|
284
321
|
for (const s of sessions)
|
|
285
322
|
this.dirty.add(s);
|
|
286
323
|
}
|