@yeaft/webchat-agent 1.0.173 → 1.0.175
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/package.json +1 -1
- package/yeaft/work-center/bridge.js +8 -2
- package/yeaft/work-center/controller.js +48 -4
- package/yeaft/work-center/debug-projection.js +541 -0
- package/yeaft/work-center/projection.js +441 -63
- package/yeaft/work-center/runner.js +8 -2
- package/yeaft/work-center/service.js +95 -9
- package/yeaft/work-center/store.js +31 -5
|
@@ -12,7 +12,13 @@ import {
|
|
|
12
12
|
removeWorkItemAttachments,
|
|
13
13
|
} from './attachments.js';
|
|
14
14
|
import { normalizeSessionContextSnapshot } from './session-context.js';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
projectActionMessagePage,
|
|
17
|
+
projectActionRequestDetail,
|
|
18
|
+
projectActionRequestIndex,
|
|
19
|
+
projectWorkItemDetail,
|
|
20
|
+
projectWorkItemSummary,
|
|
21
|
+
} from './projection.js';
|
|
16
22
|
import { readWorkCenterSettings, writeWorkCenterSettings } from './settings.js';
|
|
17
23
|
import {
|
|
18
24
|
defaultWorkCenterStageInstructions,
|
|
@@ -81,7 +87,38 @@ export class WorkCenterService {
|
|
|
81
87
|
watcher: this.watcher.status(),
|
|
82
88
|
};
|
|
83
89
|
case 'get':
|
|
84
|
-
return
|
|
90
|
+
return this.#requiredItem(payload.id);
|
|
91
|
+
case 'get_action_messages': {
|
|
92
|
+
const detail = this.#requiredItem(payload.id);
|
|
93
|
+
const action = this.#requiredAction(detail, payload.actionId);
|
|
94
|
+
return projectActionMessagePage(action, detail.runs, detail.events, {
|
|
95
|
+
cursor: payload.cursor,
|
|
96
|
+
limit: payload.limit,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
case 'get_action_requests': {
|
|
100
|
+
const detail = this.#requiredItem(payload.id);
|
|
101
|
+
const action = this.#requiredAction(detail, payload.actionId);
|
|
102
|
+
const entries = [];
|
|
103
|
+
for (const run of detail.runs.filter(item => item.actionId === action.id)) {
|
|
104
|
+
const history = await this.#debugHistory(run, { indexOnly: true });
|
|
105
|
+
for (const turn of Array.isArray(history?.turns) ? history.turns : []) {
|
|
106
|
+
entries.push({ run, turn });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return projectActionRequestIndex(action, entries);
|
|
110
|
+
}
|
|
111
|
+
case 'get_action_request': {
|
|
112
|
+
const detail = this.#requiredItem(payload.id);
|
|
113
|
+
const action = this.#requiredAction(detail, payload.actionId);
|
|
114
|
+
const requestId = requiredString(payload.requestId, 'requestId');
|
|
115
|
+
const run = detail.runs.find(item => item.actionId === action.id && item.id === payload.runId);
|
|
116
|
+
if (!run) throw new Error('Action request not found');
|
|
117
|
+
const history = await this.#debugHistory(run, { detailTurnId: requestId });
|
|
118
|
+
const projected = projectActionRequestDetail(action, run, history);
|
|
119
|
+
if (!projected) throw new Error('Action request detail is no longer available');
|
|
120
|
+
return projected;
|
|
121
|
+
}
|
|
85
122
|
case 'get_settings': {
|
|
86
123
|
const settings = this.settingsReader(this.yeaftDir);
|
|
87
124
|
return { settings, runtime: await this.runtimeInfo() };
|
|
@@ -168,6 +205,38 @@ export class WorkCenterService {
|
|
|
168
205
|
this.#emit({ type: 'work_item.cancelled', workItem: detail });
|
|
169
206
|
return detail;
|
|
170
207
|
}
|
|
208
|
+
case 'action_input': {
|
|
209
|
+
const id = requiredString(payload.id, 'id');
|
|
210
|
+
const workItem = this.#requiredItem(id);
|
|
211
|
+
let addedAttachments = [];
|
|
212
|
+
let detail;
|
|
213
|
+
try {
|
|
214
|
+
addedAttachments = appendWorkItemAttachments(workItem.attachments, payload.files, {
|
|
215
|
+
root: this.attachmentRoot,
|
|
216
|
+
workItemId: id,
|
|
217
|
+
});
|
|
218
|
+
detail = this.controller.input(id, {
|
|
219
|
+
text: typeof payload.text === 'string' ? payload.text : '',
|
|
220
|
+
actionId: typeof payload.actionId === 'string' ? payload.actionId : '',
|
|
221
|
+
revision: payload.revision,
|
|
222
|
+
addedAttachmentCount: addedAttachments.length,
|
|
223
|
+
addedAttachments,
|
|
224
|
+
attachments: [...(workItem.attachments || []), ...addedAttachments],
|
|
225
|
+
});
|
|
226
|
+
} catch (error) {
|
|
227
|
+
try {
|
|
228
|
+
if ((workItem.attachments || []).length === 0 && addedAttachments.length > 0) {
|
|
229
|
+
removeWorkItemAttachments(this.attachmentRoot, id);
|
|
230
|
+
} else {
|
|
231
|
+
removeWorkItemAttachmentFiles(this.attachmentRoot, id, addedAttachments);
|
|
232
|
+
}
|
|
233
|
+
} catch {}
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
this.watcher.abortInvalidWorkItemRuns(id);
|
|
237
|
+
this.#emit({ type: 'action.input_added', workItem: detail });
|
|
238
|
+
return detail;
|
|
239
|
+
}
|
|
171
240
|
case 'guide': {
|
|
172
241
|
const id = requiredString(payload.id, 'id');
|
|
173
242
|
const workItem = this.#requiredItem(id);
|
|
@@ -183,6 +252,7 @@ export class WorkCenterService {
|
|
|
183
252
|
actionId: typeof payload.actionId === 'string' ? payload.actionId : '',
|
|
184
253
|
revision: payload.revision,
|
|
185
254
|
addedAttachmentCount: addedAttachments.length,
|
|
255
|
+
addedAttachments,
|
|
186
256
|
attachments: [...(workItem.attachments || []), ...addedAttachments],
|
|
187
257
|
});
|
|
188
258
|
} catch (error) {
|
|
@@ -221,13 +291,6 @@ export class WorkCenterService {
|
|
|
221
291
|
},
|
|
222
292
|
};
|
|
223
293
|
}
|
|
224
|
-
case 'retry': {
|
|
225
|
-
const detail = this.controller.retry(requiredString(payload.id, 'id'), {
|
|
226
|
-
answer: typeof payload.answer === 'string' ? payload.answer : '',
|
|
227
|
-
});
|
|
228
|
-
this.#emit({ type: 'work_item.retried', workItem: detail });
|
|
229
|
-
return detail;
|
|
230
|
-
}
|
|
231
294
|
case 'set_watcher':
|
|
232
295
|
if (payload.enabled === false) await this.watcher.stop();
|
|
233
296
|
else this.watcher.start();
|
|
@@ -243,6 +306,28 @@ export class WorkCenterService {
|
|
|
243
306
|
return item;
|
|
244
307
|
}
|
|
245
308
|
|
|
309
|
+
#requiredAction(detail, actionId) {
|
|
310
|
+
const id = requiredString(actionId, 'actionId');
|
|
311
|
+
const action = detail.actions.find(item => item.id === id);
|
|
312
|
+
if (!action) throw new Error(`Action not found: ${id}`);
|
|
313
|
+
return action;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async #debugHistory(run, options = {}) {
|
|
317
|
+
const trace = this.watcher.runner?.trace;
|
|
318
|
+
if (!trace || typeof trace.fetchRecentDebugHistory !== 'function') {
|
|
319
|
+
return { turns: [], loops: [] };
|
|
320
|
+
}
|
|
321
|
+
return trace.fetchRecentDebugHistory({
|
|
322
|
+
limit: 10,
|
|
323
|
+
dreamLimit: 0,
|
|
324
|
+
sessionId: `work-item-${run.workItemId}`,
|
|
325
|
+
threadId: run.id,
|
|
326
|
+
indexOnly: options.indexOnly === true,
|
|
327
|
+
detailTurnId: options.detailTurnId || null,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
246
331
|
#emit(event) {
|
|
247
332
|
try { this.onEvent(event); } catch {}
|
|
248
333
|
}
|
|
@@ -253,6 +338,7 @@ export class WorkCenterService {
|
|
|
253
338
|
|
|
254
339
|
async shutdown() {
|
|
255
340
|
await this.watcher.stop();
|
|
341
|
+
try { await this.watcher.runner?.trace?.close?.(); } catch {}
|
|
256
342
|
this.store.close();
|
|
257
343
|
}
|
|
258
344
|
}
|
|
@@ -755,7 +755,7 @@ export class WorkItemStore {
|
|
|
755
755
|
});
|
|
756
756
|
}
|
|
757
757
|
|
|
758
|
-
addActionGuidance(id, guidance, expected, makeAction, attachments = null) {
|
|
758
|
+
addActionGuidance(id, guidance, expected, makeAction, attachments = null, addedAttachments = []) {
|
|
759
759
|
return withTransaction(this.db, () => {
|
|
760
760
|
const workItem = this.getWorkItem(id);
|
|
761
761
|
if (!workItem) return null;
|
|
@@ -788,7 +788,16 @@ export class WorkItemStore {
|
|
|
788
788
|
now,
|
|
789
789
|
id,
|
|
790
790
|
);
|
|
791
|
-
this.appendEvent(id, 'action.guidance_added', {
|
|
791
|
+
this.appendEvent(id, 'action.guidance_added', {
|
|
792
|
+
guidance,
|
|
793
|
+
attachments: (Array.isArray(addedAttachments) ? addedAttachments : []).map(attachment => ({
|
|
794
|
+
id: attachment.id,
|
|
795
|
+
name: attachment.name,
|
|
796
|
+
mimeType: attachment.mimeType,
|
|
797
|
+
size: Math.max(0, Number(attachment.size) || 0),
|
|
798
|
+
isImage: attachment.isImage === true,
|
|
799
|
+
})),
|
|
800
|
+
}, { actionId: action.id });
|
|
792
801
|
return this.getWorkItemDetail(id);
|
|
793
802
|
});
|
|
794
803
|
}
|
|
@@ -906,13 +915,18 @@ export class WorkItemStore {
|
|
|
906
915
|
});
|
|
907
916
|
}
|
|
908
917
|
|
|
909
|
-
retryWorkItemAtomic(id, makeAction) {
|
|
918
|
+
retryWorkItemAtomic(id, makeAction, options = {}) {
|
|
910
919
|
return withTransaction(this.db, () => {
|
|
911
920
|
const workItem = this.getWorkItem(id);
|
|
912
921
|
if (!workItem) return null;
|
|
913
922
|
if (!['waiting', 'needs_attention'].includes(workItem.status)) {
|
|
914
923
|
throw new Error(`WorkItem in ${workItem.status} does not need retry`);
|
|
915
924
|
}
|
|
925
|
+
if (options.expected) {
|
|
926
|
+
if (workItem.currentActionId !== options.expected.actionId || workItem.revision !== options.expected.revision) {
|
|
927
|
+
throw new Error('Action changed before input was applied; refresh and try again');
|
|
928
|
+
}
|
|
929
|
+
}
|
|
916
930
|
const previous = workItem.currentActionId ? this.getAction(workItem.currentActionId) : null;
|
|
917
931
|
const previousRun = previous
|
|
918
932
|
? mapRun(this.db.prepare(`SELECT * FROM runs WHERE work_item_id = ? AND action_id = ?
|
|
@@ -939,8 +953,20 @@ export class WorkItemStore {
|
|
|
939
953
|
contractRevision: workItem.revision,
|
|
940
954
|
}, this.#nextSequence(id), now);
|
|
941
955
|
this.db.prepare(`UPDATE work_items SET status = 'ready', current_action_id = ?,
|
|
942
|
-
current_run_id = NULL, updated_at = ? WHERE id = ?`).run(
|
|
943
|
-
|
|
956
|
+
current_run_id = NULL, attachments = ?, updated_at = ? WHERE id = ?`).run(
|
|
957
|
+
action.id,
|
|
958
|
+
stringify(Array.isArray(options.attachments) ? options.attachments : workItem.attachments),
|
|
959
|
+
now,
|
|
960
|
+
id,
|
|
961
|
+
);
|
|
962
|
+
const inputEvent = options.inputEvent && typeof options.inputEvent === 'object'
|
|
963
|
+
? options.inputEvent
|
|
964
|
+
: null;
|
|
965
|
+
if (inputEvent) {
|
|
966
|
+
this.appendEvent(id, 'action.input_added', inputEvent, { actionId: action.id });
|
|
967
|
+
} else {
|
|
968
|
+
this.appendEvent(id, 'work_item.retried', {}, { actionId: action.id });
|
|
969
|
+
}
|
|
944
970
|
return this.getWorkItemDetail(id);
|
|
945
971
|
});
|
|
946
972
|
}
|