crewly 1.20.306 → 1.20.308
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/config/hooks/agent-status/report.sh +39 -1
- package/dist/backend/backend/src/constants.d.ts +120 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +120 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.d.ts +2 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js +18 -1
- package/dist/backend/backend/src/controllers/agent-hooks/agent-hooks.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/board/board.controller.d.ts +23 -0
- package/dist/backend/backend/src/controllers/board/board.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/board/board.controller.js +48 -0
- package/dist/backend/backend/src/controllers/board/board.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/board/board.routes.d.ts +15 -0
- package/dist/backend/backend/src/controllers/board/board.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/board/board.routes.js +21 -0
- package/dist/backend/backend/src/controllers/board/board.routes.js.map +1 -0
- package/dist/backend/backend/src/controllers/browser/browser.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/browser/browser.controller.js +2 -0
- package/dist/backend/backend/src/controllers/browser/browser.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat/chat.controller.js +5 -0
- package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.js +3 -0
- package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts +18 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +172 -2
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +3 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/board/board-activity.normalizer.d.ts +102 -0
- package/dist/backend/backend/src/services/board/board-activity.normalizer.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-activity.normalizer.js +244 -0
- package/dist/backend/backend/src/services/board/board-activity.normalizer.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-activity.tracker.d.ts +159 -0
- package/dist/backend/backend/src/services/board/board-activity.tracker.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-activity.tracker.js +346 -0
- package/dist/backend/backend/src/services/board/board-activity.tracker.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-outcome-phrases.d.ts +43 -0
- package/dist/backend/backend/src/services/board/board-outcome-phrases.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-outcome-phrases.js +76 -0
- package/dist/backend/backend/src/services/board/board-outcome-phrases.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-push.service.d.ts +132 -0
- package/dist/backend/backend/src/services/board/board-push.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-push.service.js +374 -0
- package/dist/backend/backend/src/services/board/board-push.service.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-redact.d.ts +90 -0
- package/dist/backend/backend/src/services/board/board-redact.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-redact.js +359 -0
- package/dist/backend/backend/src/services/board/board-redact.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-snapshot.builder.d.ts +183 -0
- package/dist/backend/backend/src/services/board/board-snapshot.builder.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-snapshot.builder.js +344 -0
- package/dist/backend/backend/src/services/board/board-snapshot.builder.js.map +1 -0
- package/dist/backend/backend/src/services/board/board-usage.d.ts +20 -0
- package/dist/backend/backend/src/services/board/board-usage.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board-usage.js +82 -0
- package/dist/backend/backend/src/services/board/board-usage.js.map +1 -0
- package/dist/backend/backend/src/services/board/board.service.d.ts +60 -0
- package/dist/backend/backend/src/services/board/board.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board.service.js +82 -0
- package/dist/backend/backend/src/services/board/board.service.js.map +1 -0
- package/dist/backend/backend/src/services/board/board.sources.d.ts +25 -0
- package/dist/backend/backend/src/services/board/board.sources.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board.sources.js +300 -0
- package/dist/backend/backend/src/services/board/board.sources.js.map +1 -0
- package/dist/backend/backend/src/services/board/board.types.d.ts +122 -0
- package/dist/backend/backend/src/services/board/board.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/board.types.js +8 -0
- package/dist/backend/backend/src/services/board/board.types.js.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-inbound.service.d.ts +122 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-inbound.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-inbound.service.js +339 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-inbound.service.js.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-redact.d.ts +44 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-redact.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-redact.js +161 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-redact.js.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-step.normalizer.d.ts +57 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-step.normalizer.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-step.normalizer.js +164 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-step.normalizer.js.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-step.stream.d.ts +140 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-step.stream.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-step.stream.js +300 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-step.stream.js.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-terminal.service.d.ts +99 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-terminal.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-terminal.service.js +223 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-terminal.service.js.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-transcript.tail.d.ts +126 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-transcript.tail.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-transcript.tail.js +385 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-transcript.tail.js.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-wiring.d.ts +103 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-wiring.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-wiring.js +124 -0
- package/dist/backend/backend/src/services/board/canvas/canvas-wiring.js.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas.types.d.ts +88 -0
- package/dist/backend/backend/src/services/board/canvas/canvas.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/board/canvas/canvas.types.js +8 -0
- package/dist/backend/backend/src/services/board/canvas/canvas.types.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
- package/dist/backend/backend/src/services/google/google-reauth-notifier.service.d.ts +13 -0
- package/dist/backend/backend/src/services/google/google-reauth-notifier.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/google/google-reauth-notifier.service.js +15 -0
- package/dist/backend/backend/src/services/google/google-reauth-notifier.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js +2 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +120 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +120 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.js +1 -0
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live activity per agent (crewly#1247): the current activity, the last
|
|
3
|
+
* meaningful action and a rolling timeline, fed by hook events, browser
|
|
4
|
+
* actions, chat replies and owner waits. In memory only: a restart starts the
|
|
5
|
+
* timeline over (the agents restart with it).
|
|
6
|
+
*
|
|
7
|
+
* Event-driven: `record*` do a few map operations and tell the listeners; the
|
|
8
|
+
* push scheduler decides when to send. Nothing here reads files or the
|
|
9
|
+
* network.
|
|
10
|
+
*
|
|
11
|
+
* @module services/board/board-activity.tracker
|
|
12
|
+
*/
|
|
13
|
+
import { BOARD_CONSTANTS } from '../../constants.js';
|
|
14
|
+
import { normalizeBrowserAction, normalizeMessage, normalizeOwnerWait, normalizeToolUse, THINKING, } from './board-activity.normalizer.js';
|
|
15
|
+
import { StepStream } from './canvas/canvas-step.stream.js';
|
|
16
|
+
import { stepOfBrowserAction, stepOfMessage, stepOfOwnerWait, stepOfTool } from './canvas/canvas-step.normalizer.js';
|
|
17
|
+
const C = BOARD_CONSTANTS;
|
|
18
|
+
/**
|
|
19
|
+
* Per-session board activity.
|
|
20
|
+
*/
|
|
21
|
+
export class BoardActivityTracker {
|
|
22
|
+
static instance = null;
|
|
23
|
+
sessions = new Map();
|
|
24
|
+
listeners = new Set();
|
|
25
|
+
/** Project roots by session, for relative paths */
|
|
26
|
+
rootsOf = () => [];
|
|
27
|
+
/** The canvas step stream: the same events, one line per tool call with its duration */
|
|
28
|
+
steps = new StepStream();
|
|
29
|
+
/** @returns The process-wide tracker */
|
|
30
|
+
static getInstance() {
|
|
31
|
+
if (!BoardActivityTracker.instance)
|
|
32
|
+
BoardActivityTracker.instance = new BoardActivityTracker();
|
|
33
|
+
return BoardActivityTracker.instance;
|
|
34
|
+
}
|
|
35
|
+
/** Drop the process-wide tracker (tests). */
|
|
36
|
+
static resetInstance() {
|
|
37
|
+
BoardActivityTracker.instance = null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Where an agent's project roots come from.
|
|
41
|
+
*
|
|
42
|
+
* @param fn - Session -> project roots
|
|
43
|
+
*/
|
|
44
|
+
setRootsResolver(fn) {
|
|
45
|
+
this.rootsOf = fn;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Subscribe to changes.
|
|
49
|
+
*
|
|
50
|
+
* @param listener - Called with the session whose row may have changed
|
|
51
|
+
* @returns Unsubscribe
|
|
52
|
+
*/
|
|
53
|
+
onChange(listener) {
|
|
54
|
+
this.listeners.add(listener);
|
|
55
|
+
return () => this.listeners.delete(listener);
|
|
56
|
+
}
|
|
57
|
+
emit(session) {
|
|
58
|
+
for (const l of this.listeners) {
|
|
59
|
+
try {
|
|
60
|
+
l(session);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// a listener must never break a hook
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
get(session) {
|
|
68
|
+
let s = this.sessions.get(session);
|
|
69
|
+
if (!s) {
|
|
70
|
+
s = { open: new Map(), current: null, last: null, ownerWait: null, timeline: [], unsent: [], seq: 0, waitStep: null };
|
|
71
|
+
this.sessions.set(session, s);
|
|
72
|
+
}
|
|
73
|
+
return s;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A hook event arrived. PreToolUse starts an activity; PostToolUse ends
|
|
77
|
+
* it; UserPromptSubmit / Stop / SessionStart reset the turn; a permission
|
|
78
|
+
* prompt or question is a wait on the owner.
|
|
79
|
+
*
|
|
80
|
+
* @param session - Agent session
|
|
81
|
+
* @param event - Hook event name
|
|
82
|
+
* @param info - Tool details (PreToolUse / PostToolUse) and the notification type
|
|
83
|
+
* @param now - Clock (tests)
|
|
84
|
+
*/
|
|
85
|
+
recordHook(session, event, info = {}, now = Date.now()) {
|
|
86
|
+
const s = this.get(session);
|
|
87
|
+
switch (event) {
|
|
88
|
+
case 'PreToolUse': {
|
|
89
|
+
if (!info.toolName)
|
|
90
|
+
return;
|
|
91
|
+
const roots = this.safeRoots(session);
|
|
92
|
+
const activity = normalizeToolUse(info.toolName, info.toolInput ?? {}, { roots });
|
|
93
|
+
s.open.set(info.toolUseId ?? `anon-${(s.seq += 1)}`, now);
|
|
94
|
+
this.endWaitStep(session, s, now);
|
|
95
|
+
s.ownerWait = null;
|
|
96
|
+
this.steps.start(session, stepOfTool(info.toolName, info.toolInput ?? {}, { roots }), { ...(info.toolUseId ? { id: info.toolUseId } : {}) }, now);
|
|
97
|
+
this.show(session, s, activity, now);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
case 'PostToolUse': {
|
|
101
|
+
if (info.toolUseId)
|
|
102
|
+
s.open.delete(info.toolUseId);
|
|
103
|
+
else if (s.open.size > 0)
|
|
104
|
+
s.open.delete(s.open.keys().next().value);
|
|
105
|
+
if (s.current)
|
|
106
|
+
s.current.lastEventAt = now;
|
|
107
|
+
this.endWaitStep(session, s, now);
|
|
108
|
+
s.ownerWait = null;
|
|
109
|
+
this.steps.finish(session, info.toolUseId, 'done', now);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
case 'UserPromptSubmit':
|
|
113
|
+
case 'SessionStart':
|
|
114
|
+
s.open.clear();
|
|
115
|
+
this.endWaitStep(session, s, now);
|
|
116
|
+
s.ownerWait = null;
|
|
117
|
+
this.steps.closeAll(session, now);
|
|
118
|
+
s.current = { activity: THINKING, since: now, lastEventAt: now };
|
|
119
|
+
this.emit(session);
|
|
120
|
+
return;
|
|
121
|
+
case 'Stop':
|
|
122
|
+
s.open.clear();
|
|
123
|
+
this.endWaitStep(session, s, now);
|
|
124
|
+
s.ownerWait = null;
|
|
125
|
+
this.steps.closeAll(session, now);
|
|
126
|
+
s.current = null;
|
|
127
|
+
this.emit(session);
|
|
128
|
+
return;
|
|
129
|
+
case 'Notification':
|
|
130
|
+
case 'PermissionRequest':
|
|
131
|
+
if (event === 'PermissionRequest' || info.notificationType === 'permission_prompt') {
|
|
132
|
+
this.setOwnerWait(session, 'permission to continue', now);
|
|
133
|
+
}
|
|
134
|
+
else if (info.notificationType === 'elicitation_dialog') {
|
|
135
|
+
this.setOwnerWait(session, 'an answer to its question', now);
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
default:
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* An agent action in Chrome.
|
|
144
|
+
*
|
|
145
|
+
* @param session - Agent session
|
|
146
|
+
* @param tool - Extension tool name
|
|
147
|
+
* @param url - Page URL, if known
|
|
148
|
+
* @param now - Clock (tests)
|
|
149
|
+
*/
|
|
150
|
+
recordBrowser(session, tool, url, now = Date.now()) {
|
|
151
|
+
const s = this.get(session);
|
|
152
|
+
this.steps.instant(session, stepOfBrowserAction(tool, url), now);
|
|
153
|
+
this.show(session, s, normalizeBrowserAction(tool, url), now);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* An agent posted to a channel or the owner chat.
|
|
157
|
+
*
|
|
158
|
+
* @param session - Agent session
|
|
159
|
+
* @param channel - Channel or chat name
|
|
160
|
+
* @param now - Clock (tests)
|
|
161
|
+
*/
|
|
162
|
+
recordMessage(session, channel, now = Date.now()) {
|
|
163
|
+
const s = this.get(session);
|
|
164
|
+
this.steps.instant(session, stepOfMessage(channel), now);
|
|
165
|
+
this.show(session, s, normalizeMessage(channel), now);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* An agent waits on the owner.
|
|
169
|
+
*
|
|
170
|
+
* @param session - Agent session
|
|
171
|
+
* @param what - Short plain-words description
|
|
172
|
+
* @param now - Clock (tests)
|
|
173
|
+
*/
|
|
174
|
+
setOwnerWait(session, what, now = Date.now()) {
|
|
175
|
+
const s = this.get(session);
|
|
176
|
+
if (s.ownerWait)
|
|
177
|
+
return;
|
|
178
|
+
const activity = normalizeOwnerWait(what);
|
|
179
|
+
s.ownerWait = { activity, since: now };
|
|
180
|
+
s.waitStep = this.steps.start(session, stepOfOwnerWait(what), {}, now);
|
|
181
|
+
this.pushTimeline(s, session, activity, now);
|
|
182
|
+
this.emit(session);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The wait is over.
|
|
186
|
+
*
|
|
187
|
+
* @param session - Agent session
|
|
188
|
+
*/
|
|
189
|
+
clearOwnerWait(session) {
|
|
190
|
+
const s = this.sessions.get(session);
|
|
191
|
+
if (!s?.ownerWait)
|
|
192
|
+
return;
|
|
193
|
+
this.endWaitStep(session, s, Date.now());
|
|
194
|
+
s.ownerWait = null;
|
|
195
|
+
this.emit(session);
|
|
196
|
+
}
|
|
197
|
+
/** The wait on the owner is over: its canvas step ends. */
|
|
198
|
+
endWaitStep(session, s, now) {
|
|
199
|
+
if (!s.waitStep)
|
|
200
|
+
return;
|
|
201
|
+
this.steps.finish(session, s.waitStep, 'done', now);
|
|
202
|
+
s.waitStep = null;
|
|
203
|
+
}
|
|
204
|
+
/** Make `activity` current and the last action; notify when the row changed. */
|
|
205
|
+
show(session, s, activity, now) {
|
|
206
|
+
const prev = s.current;
|
|
207
|
+
const same = prev !== null && prev.activity.type === activity.type && prev.activity.detail === activity.detail;
|
|
208
|
+
s.current = { activity, since: same && prev ? prev.since : now, lastEventAt: now };
|
|
209
|
+
if (activity.type !== 'thinking') {
|
|
210
|
+
const changedText = s.last?.text !== activity.text;
|
|
211
|
+
s.last = { text: activity.text, at: now };
|
|
212
|
+
this.pushTimeline(s, session, activity, now);
|
|
213
|
+
if (!same || changedText)
|
|
214
|
+
this.emit(session);
|
|
215
|
+
}
|
|
216
|
+
else if (!same) {
|
|
217
|
+
this.emit(session);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
pushTimeline(s, session, activity, now) {
|
|
221
|
+
const tail = s.timeline[s.timeline.length - 1];
|
|
222
|
+
// The same line twice within 10 s is one event (a tool called in a loop)
|
|
223
|
+
if (tail && tail.text === activity.text && now - Date.parse(tail.at) < 10_000)
|
|
224
|
+
return;
|
|
225
|
+
const event = { session, at: new Date(now).toISOString(), type: activity.type, text: activity.text };
|
|
226
|
+
s.timeline.push(event);
|
|
227
|
+
s.unsent.push(event);
|
|
228
|
+
if (s.timeline.length > C.TIMELINE_MAX_EVENTS)
|
|
229
|
+
s.timeline.splice(0, s.timeline.length - C.TIMELINE_MAX_EVENTS);
|
|
230
|
+
if (s.unsent.length > C.TIMELINE_MAX_EVENTS)
|
|
231
|
+
s.unsent.splice(0, s.unsent.length - C.TIMELINE_MAX_EVENTS);
|
|
232
|
+
}
|
|
233
|
+
safeRoots(session) {
|
|
234
|
+
try {
|
|
235
|
+
return this.rootsOf(session);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return [];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* What the session shows now. While a tool runs it is that tool; for
|
|
243
|
+
* {@link BOARD_CONSTANTS.ACTIVITY_LINGER_MS} after it, the same; then
|
|
244
|
+
* "thinking" (when `working`), else nothing.
|
|
245
|
+
*
|
|
246
|
+
* @param session - Agent session
|
|
247
|
+
* @param working - The runtime says the agent is in a turn
|
|
248
|
+
* @param now - Clock (tests)
|
|
249
|
+
* @returns Activity and last action
|
|
250
|
+
*/
|
|
251
|
+
live(session, working, now = Date.now()) {
|
|
252
|
+
const s = this.sessions.get(session);
|
|
253
|
+
if (!s)
|
|
254
|
+
return { activity: working ? { type: 'thinking', detail: THINKING.detail, since: new Date(now).toISOString() } : null, lastAction: null };
|
|
255
|
+
const lastAction = s.last ? { text: s.last.text, at: new Date(s.last.at).toISOString() } : null;
|
|
256
|
+
if (s.ownerWait) {
|
|
257
|
+
return { activity: { type: s.ownerWait.activity.type, detail: s.ownerWait.activity.detail, since: new Date(s.ownerWait.since).toISOString() }, lastAction };
|
|
258
|
+
}
|
|
259
|
+
// A lost PostToolUse must not pin a tool forever
|
|
260
|
+
for (const [id, at] of s.open)
|
|
261
|
+
if (now - at > C.OPEN_TOOL_MAX_MS)
|
|
262
|
+
s.open.delete(id);
|
|
263
|
+
const cur = s.current;
|
|
264
|
+
if (!working && s.open.size === 0)
|
|
265
|
+
return { activity: null, lastAction };
|
|
266
|
+
if (cur && (s.open.size > 0 || now - cur.lastEventAt <= C.ACTIVITY_LINGER_MS || cur.activity.type === 'thinking')) {
|
|
267
|
+
return { activity: { type: cur.activity.type, detail: cur.activity.detail, since: new Date(cur.since).toISOString() }, lastAction };
|
|
268
|
+
}
|
|
269
|
+
if (working) {
|
|
270
|
+
const since = cur ? cur.lastEventAt + C.ACTIVITY_LINGER_MS : now;
|
|
271
|
+
return { activity: { type: 'thinking', detail: THINKING.detail, since: new Date(Math.min(since, now)).toISOString() }, lastAction };
|
|
272
|
+
}
|
|
273
|
+
return { activity: null, lastAction };
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Timeline events not sent yet. Call {@link ackUnsent} once Cloud took them;
|
|
277
|
+
* until then they stay queued (capped), so a failed push loses nothing.
|
|
278
|
+
*
|
|
279
|
+
* @param sessions - Only these sessions (all when omitted)
|
|
280
|
+
* @returns Events, oldest first, at most {@link BOARD_CONSTANTS.DELTA_TIMELINE_MAX} per session
|
|
281
|
+
*/
|
|
282
|
+
peekUnsent(sessions) {
|
|
283
|
+
const out = [];
|
|
284
|
+
const keys = sessions ? [...sessions] : [...this.sessions.keys()];
|
|
285
|
+
for (const key of keys) {
|
|
286
|
+
const s = this.sessions.get(key);
|
|
287
|
+
if (!s || s.unsent.length === 0)
|
|
288
|
+
continue;
|
|
289
|
+
out.push(...s.unsent.slice(-C.DELTA_TIMELINE_MAX));
|
|
290
|
+
}
|
|
291
|
+
return out.sort((a, b) => Date.parse(a.at) - Date.parse(b.at));
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Mark events as sent (everything unsent up to the newest of them is
|
|
295
|
+
* dropped, including older ones cut by the per-delta limit).
|
|
296
|
+
*
|
|
297
|
+
* @param events - Events returned by {@link peekUnsent} that Cloud took
|
|
298
|
+
*/
|
|
299
|
+
ackUnsent(events) {
|
|
300
|
+
const newest = new Map();
|
|
301
|
+
for (const e of events)
|
|
302
|
+
newest.set(e.session, Math.max(newest.get(e.session) ?? 0, Date.parse(e.at)));
|
|
303
|
+
for (const [session, at] of newest) {
|
|
304
|
+
const s = this.sessions.get(session);
|
|
305
|
+
if (s)
|
|
306
|
+
s.unsent = s.unsent.filter((e) => Date.parse(e.at) > at);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/** Sessions with timeline events not yet sent. */
|
|
310
|
+
sessionsWithUnsent() {
|
|
311
|
+
return [...this.sessions.entries()].filter(([, s]) => s.unsent.length > 0).map(([k]) => k);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* A session's recent timeline (24 h, newest last).
|
|
315
|
+
*
|
|
316
|
+
* @param session - Agent session
|
|
317
|
+
* @param now - Clock (tests)
|
|
318
|
+
* @returns Events
|
|
319
|
+
*/
|
|
320
|
+
timeline(session, now = Date.now()) {
|
|
321
|
+
const s = this.sessions.get(session);
|
|
322
|
+
if (!s)
|
|
323
|
+
return [];
|
|
324
|
+
const cutoff = now - C.TIMELINE_MAX_AGE_MS;
|
|
325
|
+
s.timeline = s.timeline.filter((e) => Date.parse(e.at) >= cutoff);
|
|
326
|
+
return [...s.timeline];
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Forget a session (it was deleted).
|
|
330
|
+
*
|
|
331
|
+
* @param session - Agent session
|
|
332
|
+
*/
|
|
333
|
+
forget(session) {
|
|
334
|
+
this.sessions.delete(session);
|
|
335
|
+
this.steps.forget(session);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* The process-wide tracker.
|
|
340
|
+
*
|
|
341
|
+
* @returns The tracker
|
|
342
|
+
*/
|
|
343
|
+
export function getBoardActivity() {
|
|
344
|
+
return BoardActivityTracker.getInstance();
|
|
345
|
+
}
|
|
346
|
+
//# sourceMappingURL=board-activity.tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-activity.tracker.js","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-activity.tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result phrases for the "Done today" strip (crewly#1247): a finished
|
|
3
|
+
* ticket or WorkItem is described by what it produced ("post published"),
|
|
4
|
+
* and equal results are counted ("2 posts published"). Deterministic keyword
|
|
5
|
+
* rules over the title and kind; no model call.
|
|
6
|
+
*
|
|
7
|
+
* Pure (no I/O).
|
|
8
|
+
*
|
|
9
|
+
* @module services/board/board-outcome-phrases
|
|
10
|
+
*/
|
|
11
|
+
/** One kind of result. */
|
|
12
|
+
interface ResultRule {
|
|
13
|
+
/** Matches the title (and kind label) */
|
|
14
|
+
pattern: RegExp;
|
|
15
|
+
one: string;
|
|
16
|
+
many: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The rule describing a finished item.
|
|
20
|
+
*
|
|
21
|
+
* @param title - Item title
|
|
22
|
+
* @param kind - Ticket kind (`issue`, `feature`, ...), when known
|
|
23
|
+
* @returns A rule (a generic one when no keyword matches)
|
|
24
|
+
*/
|
|
25
|
+
export declare function ruleFor(title: string, kind?: string): ResultRule;
|
|
26
|
+
/**
|
|
27
|
+
* The result of one finished item, in singular ("post published").
|
|
28
|
+
*
|
|
29
|
+
* @param title - Item title
|
|
30
|
+
* @param kind - Ticket kind, when known
|
|
31
|
+
* @returns Result phrase
|
|
32
|
+
*/
|
|
33
|
+
export declare function resultOf(title: string, kind?: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Count equal results: ["post published","post published","bug fixed"] ->
|
|
36
|
+
* ["2 posts published", "1 bug fixed"], most frequent first.
|
|
37
|
+
*
|
|
38
|
+
* @param results - Singular result phrases
|
|
39
|
+
* @returns Summary phrases (at most {@link BOARD_CONSTANTS.MAX_SUMMARY_PHRASES})
|
|
40
|
+
*/
|
|
41
|
+
export declare function summarizeResults(results: readonly string[]): string[];
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=board-outcome-phrases.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-outcome-phrases.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-outcome-phrases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,0BAA0B;AAC1B,UAAU,UAAU;IACnB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACb;AAyBD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,UAAU,CAIhE;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CASrE"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result phrases for the "Done today" strip (crewly#1247): a finished
|
|
3
|
+
* ticket or WorkItem is described by what it produced ("post published"),
|
|
4
|
+
* and equal results are counted ("2 posts published"). Deterministic keyword
|
|
5
|
+
* rules over the title and kind; no model call.
|
|
6
|
+
*
|
|
7
|
+
* Pure (no I/O).
|
|
8
|
+
*
|
|
9
|
+
* @module services/board/board-outcome-phrases
|
|
10
|
+
*/
|
|
11
|
+
import { BOARD_CONSTANTS } from '../../constants.js';
|
|
12
|
+
const RULES = [
|
|
13
|
+
{ pattern: /\b(?:posts?|tweets?|threads?|linkedin|newsletter|publish(?:ed|ing)?|announce(?:ment)?s?|social)\b/i, one: 'post published', many: 'posts published' },
|
|
14
|
+
{ pattern: /\b(?:customers?|clients?|support|inquir(?:y|ies)|repl(?:y|ies|ied)|dms?)\b/i, one: 'customer answered', many: 'customers answered' },
|
|
15
|
+
{ pattern: /\b(?:bugs?|fix(?:es|ed)?|hotfix|regression|crash|broken)\b/i, one: 'bug fixed', many: 'bugs fixed' },
|
|
16
|
+
{ pattern: /\b(?:releases?|deploy(?:ed|ment)?|ship(?:ped)?|rollout|launch)\b/i, one: 'release shipped', many: 'releases shipped' },
|
|
17
|
+
{ pattern: /\b(?:pull request|pr #?\d+|merge[ds]?)\b/i, one: 'change merged', many: 'changes merged' },
|
|
18
|
+
{ pattern: /\b(?:reports?|summary|digest|analysis|research|audit|review(?:ed)?|survey)\b/i, one: 'report delivered', many: 'reports delivered' },
|
|
19
|
+
{ pattern: /\b(?:videos?|designs?|images?|banners?|graphics?|canva|thumbnails?|logo)\b/i, one: 'design created', many: 'designs created' },
|
|
20
|
+
{ pattern: /\b(?:docs?|documents?|articles?|blog|write-?ups?|specs?|guides?|sop|copy|draft)\b/i, one: 'document written', many: 'documents written' },
|
|
21
|
+
{ pattern: /\b(?:tests?|ci|lint|refactor|cleanup|chore|migrat(?:e|ion))\b/i, one: 'chore finished', many: 'chores finished' },
|
|
22
|
+
{ pattern: /\b(?:features?|implement(?:ed)?|add(?:ed)?|build|built|page|app)\b/i, one: 'feature built', many: 'features built' },
|
|
23
|
+
];
|
|
24
|
+
/** Kind of a finished item, from its ticket kind when it has one. */
|
|
25
|
+
const KIND_FALLBACK = {
|
|
26
|
+
issue: { pattern: /./, one: 'issue resolved', many: 'issues resolved' },
|
|
27
|
+
feature: { pattern: /./, one: 'feature built', many: 'features built' },
|
|
28
|
+
idea: { pattern: /./, one: 'idea explored', many: 'ideas explored' },
|
|
29
|
+
question: { pattern: /./, one: 'question answered', many: 'questions answered' },
|
|
30
|
+
};
|
|
31
|
+
const GENERIC = { pattern: /./, one: 'task done', many: 'tasks done' };
|
|
32
|
+
/**
|
|
33
|
+
* The rule describing a finished item.
|
|
34
|
+
*
|
|
35
|
+
* @param title - Item title
|
|
36
|
+
* @param kind - Ticket kind (`issue`, `feature`, ...), when known
|
|
37
|
+
* @returns A rule (a generic one when no keyword matches)
|
|
38
|
+
*/
|
|
39
|
+
export function ruleFor(title, kind) {
|
|
40
|
+
for (const rule of RULES)
|
|
41
|
+
if (rule.pattern.test(title))
|
|
42
|
+
return rule;
|
|
43
|
+
if (kind && KIND_FALLBACK[kind])
|
|
44
|
+
return KIND_FALLBACK[kind];
|
|
45
|
+
return GENERIC;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The result of one finished item, in singular ("post published").
|
|
49
|
+
*
|
|
50
|
+
* @param title - Item title
|
|
51
|
+
* @param kind - Ticket kind, when known
|
|
52
|
+
* @returns Result phrase
|
|
53
|
+
*/
|
|
54
|
+
export function resultOf(title, kind) {
|
|
55
|
+
return ruleFor(title, kind).one;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Count equal results: ["post published","post published","bug fixed"] ->
|
|
59
|
+
* ["2 posts published", "1 bug fixed"], most frequent first.
|
|
60
|
+
*
|
|
61
|
+
* @param results - Singular result phrases
|
|
62
|
+
* @returns Summary phrases (at most {@link BOARD_CONSTANTS.MAX_SUMMARY_PHRASES})
|
|
63
|
+
*/
|
|
64
|
+
export function summarizeResults(results) {
|
|
65
|
+
const counts = new Map();
|
|
66
|
+
for (const r of results)
|
|
67
|
+
counts.set(r, (counts.get(r) ?? 0) + 1);
|
|
68
|
+
const byOne = new Map();
|
|
69
|
+
for (const rule of [...RULES, ...Object.values(KIND_FALLBACK), GENERIC])
|
|
70
|
+
byOne.set(rule.one, rule);
|
|
71
|
+
return [...counts.entries()]
|
|
72
|
+
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
|
|
73
|
+
.slice(0, BOARD_CONSTANTS.MAX_SUMMARY_PHRASES)
|
|
74
|
+
.map(([one, n]) => `${n} ${n === 1 ? one : (byOne.get(one)?.many ?? one)}`);
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=board-outcome-phrases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-outcome-phrases.js","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-outcome-phrases.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pushes the owner board to Crewly Cloud (crewly#1247).
|
|
3
|
+
*
|
|
4
|
+
* - a full snapshot every 60 s (Cloud may ask for another cadence);
|
|
5
|
+
* - a delta (only the agent rows that changed, plus new timeline events and
|
|
6
|
+
* canvas steps) within {@link BOARD_CONSTANTS.DELTA_DELAY_MS} of an activity
|
|
7
|
+
* change, or within {@link CANVAS_CONSTANTS.LIVE_DELTA_DELAY_MS} while Cloud
|
|
8
|
+
* says someone is watching the canvas (`live: true` / `viewers > 0` in its
|
|
9
|
+
* answer, believed for {@link CANVAS_CONSTANTS.LIVE_TTL_MS});
|
|
10
|
+
* - gzip bodies, exponential backoff on failure, an hour's pause when Cloud
|
|
11
|
+
* does not take board pushes (not deployed / refusing);
|
|
12
|
+
* - nothing is sent when the machine is not signed in to Cloud, or when
|
|
13
|
+
* `CREWLY_BOARD_PUSH=0`.
|
|
14
|
+
*
|
|
15
|
+
* Event-driven: a change only marks a session dirty and arms one timer; the
|
|
16
|
+
* delta is built from the builder's cache and the live tracker, never from a
|
|
17
|
+
* disk scan.
|
|
18
|
+
*
|
|
19
|
+
* @module services/board/board-push.service
|
|
20
|
+
*/
|
|
21
|
+
import { type ComponentLogger } from '../core/logger.service.js';
|
|
22
|
+
import type { BoardActivityTracker } from './board-activity.tracker.js';
|
|
23
|
+
import type { BoardSnapshotBuilder } from './board-snapshot.builder.js';
|
|
24
|
+
import type { BoardSnapshot } from './board.types.js';
|
|
25
|
+
/** The Cloud session (CloudClientService). */
|
|
26
|
+
export interface BoardCloudSession {
|
|
27
|
+
getToken(): string | null;
|
|
28
|
+
getCloudUrl(): string | null;
|
|
29
|
+
tryRefreshToken?(): Promise<boolean>;
|
|
30
|
+
}
|
|
31
|
+
/** Constructor dependencies. */
|
|
32
|
+
export interface BoardPushDeps {
|
|
33
|
+
builder: BoardSnapshotBuilder;
|
|
34
|
+
tracker: BoardActivityTracker;
|
|
35
|
+
cloud: BoardCloudSession;
|
|
36
|
+
env?: NodeJS.ProcessEnv;
|
|
37
|
+
fetchImpl?: (input: string, init: RequestInit) => Promise<Response>;
|
|
38
|
+
now?: () => number;
|
|
39
|
+
setTimeout?: (fn: () => void, ms: number) => ReturnType<typeof setTimeout>;
|
|
40
|
+
clearTimeout?: (handle: ReturnType<typeof setTimeout>) => void;
|
|
41
|
+
logger?: ComponentLogger;
|
|
42
|
+
}
|
|
43
|
+
/** What one push did. */
|
|
44
|
+
export type BoardPushOutcome = 'sent' | 'nothing' | 'skipped' | 'paused' | 'failed';
|
|
45
|
+
/**
|
|
46
|
+
* Whether the board push is switched off.
|
|
47
|
+
*
|
|
48
|
+
* @param env - Environment
|
|
49
|
+
* @returns True for `CREWLY_BOARD_PUSH=0` (also `false` / `off`)
|
|
50
|
+
*/
|
|
51
|
+
export declare function isBoardPushDisabled(env?: NodeJS.ProcessEnv): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Shrink a snapshot until its JSON fits the body limit: the longest list
|
|
54
|
+
* loses its tail first.
|
|
55
|
+
*
|
|
56
|
+
* @param snapshot - Snapshot (mutated)
|
|
57
|
+
* @returns The same snapshot
|
|
58
|
+
*/
|
|
59
|
+
export declare function fitSnapshot(snapshot: BoardSnapshot): BoardSnapshot;
|
|
60
|
+
/** Pushes snapshots and deltas. */
|
|
61
|
+
export declare class BoardPushService {
|
|
62
|
+
private readonly deps;
|
|
63
|
+
private readonly logger;
|
|
64
|
+
private readonly fetchImpl;
|
|
65
|
+
private readonly now;
|
|
66
|
+
private readonly setTimer;
|
|
67
|
+
private readonly clearTimer;
|
|
68
|
+
private started;
|
|
69
|
+
private fullTimer;
|
|
70
|
+
private deltaTimer;
|
|
71
|
+
private unsubscribe;
|
|
72
|
+
private readonly dirty;
|
|
73
|
+
/** Rows Cloud has, as JSON, by session */
|
|
74
|
+
private readonly lastSent;
|
|
75
|
+
private lastToken;
|
|
76
|
+
private fullOk;
|
|
77
|
+
private fullAfterMs;
|
|
78
|
+
private failures;
|
|
79
|
+
private pausedUntil;
|
|
80
|
+
private pauseLogged;
|
|
81
|
+
/** Cloud said viewers are watching until this time (epoch ms) */
|
|
82
|
+
private liveUntil;
|
|
83
|
+
private inFlight;
|
|
84
|
+
constructor(deps: BoardPushDeps);
|
|
85
|
+
/**
|
|
86
|
+
* Whether Cloud recently said someone is watching the canvas.
|
|
87
|
+
*
|
|
88
|
+
* @returns True while the live answer is believed
|
|
89
|
+
*/
|
|
90
|
+
isLive(): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Treat the canvas as watched for a while (an owner action just arrived, so
|
|
93
|
+
* someone is there).
|
|
94
|
+
*/
|
|
95
|
+
noteViewer(): void;
|
|
96
|
+
/** Whether the push is switched off in the environment. */
|
|
97
|
+
get disabled(): boolean;
|
|
98
|
+
/** Start pushing. Idempotent; a no-op when switched off. Never throws. */
|
|
99
|
+
start(): void;
|
|
100
|
+
/** Stop timers and the subscription. */
|
|
101
|
+
stop(): void;
|
|
102
|
+
/**
|
|
103
|
+
* A session's row may have changed: send a delta soon.
|
|
104
|
+
*
|
|
105
|
+
* @param session - Agent session
|
|
106
|
+
*/
|
|
107
|
+
markDirty(session: string): void;
|
|
108
|
+
private scheduleFull;
|
|
109
|
+
private nextFullDelay;
|
|
110
|
+
/** The Cloud endpoint and token, or null when the machine is not signed in or a pause is on. */
|
|
111
|
+
private target;
|
|
112
|
+
/**
|
|
113
|
+
* Build and send the full snapshot, then any timeline events that are
|
|
114
|
+
* waiting. Serialised with deltas. Never throws.
|
|
115
|
+
*
|
|
116
|
+
* @returns What happened
|
|
117
|
+
*/
|
|
118
|
+
pushFull(): Promise<BoardPushOutcome>;
|
|
119
|
+
private doFull;
|
|
120
|
+
/**
|
|
121
|
+
* Send a delta for the sessions marked dirty. Serialised with full pushes.
|
|
122
|
+
* Never throws.
|
|
123
|
+
*
|
|
124
|
+
* @returns What happened
|
|
125
|
+
*/
|
|
126
|
+
flushDelta(): Promise<BoardPushOutcome>;
|
|
127
|
+
private doDelta;
|
|
128
|
+
/** Cloud's answer says whether anyone is watching (`live: true` or `viewers > 0`, at the top or under `next`). */
|
|
129
|
+
private noteLiveAnswer;
|
|
130
|
+
private post;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=board-push.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"board-push.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/board/board-push.service.ts"],"names":[],"mappings":"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"}
|