@yeaft/webchat-agent 1.0.134 → 1.0.135
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
CHANGED
|
@@ -32,6 +32,8 @@ function normalizeTerminalResult(result, action) {
|
|
|
32
32
|
? result.reviewDecision
|
|
33
33
|
: null,
|
|
34
34
|
contractPatch: normalizeContractPatch(result.contractPatch),
|
|
35
|
+
loopCount: Math.max(0, Number(result.loopCount) || 0),
|
|
36
|
+
toolCount: Math.max(0, Number(result.toolCount) || 0),
|
|
35
37
|
};
|
|
36
38
|
if (normalized.outcome === 'waiting' && !normalized.waitingReason) {
|
|
37
39
|
throw new Error('waiting outcome requires waitingReason');
|
|
@@ -3,77 +3,74 @@ function currentAction(detail) {
|
|
|
3
3
|
return detail.actions.find(action => action.id === detail.currentActionId) || null;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
function
|
|
6
|
+
function count(value) {
|
|
7
|
+
return Math.max(0, Number(value) || 0);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function actionExecutionStats(action, runs) {
|
|
11
|
+
const matchingRuns = Array.isArray(runs)
|
|
12
|
+
? runs.filter(run => run?.actionId === action?.id)
|
|
13
|
+
: [];
|
|
14
|
+
if (matchingRuns.length === 0) {
|
|
15
|
+
return {
|
|
16
|
+
loopCount: count(action?.loopCount),
|
|
17
|
+
toolCount: count(action?.toolCount),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return matchingRuns.reduce((total, run) => ({
|
|
21
|
+
loopCount: total.loopCount + count(run.loopCount),
|
|
22
|
+
toolCount: total.toolCount + count(run.toolCount),
|
|
23
|
+
}), { loopCount: 0, toolCount: 0 });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function projectAssignmentPolicy(policy) {
|
|
27
|
+
if (!policy || typeof policy !== 'object') return null;
|
|
28
|
+
return {
|
|
29
|
+
mode: policy.mode || null,
|
|
30
|
+
fixedVpId: policy.fixedVpId || null,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function projectAction(action, runs) {
|
|
7
35
|
if (!action) return null;
|
|
36
|
+
const stats = actionExecutionStats(action, runs);
|
|
8
37
|
return {
|
|
9
38
|
id: action.id,
|
|
10
|
-
workItemId: action.workItemId,
|
|
11
39
|
sequence: action.sequence,
|
|
12
40
|
type: action.type,
|
|
13
41
|
stageId: action.stageId || action.type,
|
|
14
|
-
assignmentPolicy: action.assignmentPolicy
|
|
15
|
-
modelPolicy: action.modelPolicy || null,
|
|
42
|
+
assignmentPolicy: projectAssignmentPolicy(action.assignmentPolicy),
|
|
16
43
|
requiredRole: action.requiredRole || '',
|
|
17
44
|
status: action.status,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
currentRunId: action.currentRunId || null,
|
|
21
|
-
createdAt: action.createdAt,
|
|
22
|
-
updatedAt: action.updatedAt,
|
|
45
|
+
loopCount: stats.loopCount,
|
|
46
|
+
toolCount: stats.toolCount,
|
|
23
47
|
};
|
|
24
48
|
}
|
|
25
49
|
|
|
26
|
-
function
|
|
27
|
-
if (!
|
|
28
|
-
return {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
evidence: Array.isArray(run.evidence) ? run.evidence : [],
|
|
38
|
-
waitingReason: run.waitingReason || '',
|
|
39
|
-
error: run.error || '',
|
|
40
|
-
reviewDecision: run.reviewDecision || null,
|
|
41
|
-
roleSnapshot: run.roleSnapshot ? {
|
|
42
|
-
id: run.roleSnapshot.id,
|
|
43
|
-
actionType: run.roleSnapshot.actionType,
|
|
44
|
-
selectionReason: run.roleSnapshot.selectionReason,
|
|
45
|
-
} : null,
|
|
46
|
-
vpSnapshot: run.vpSnapshot ? {
|
|
47
|
-
id: run.vpSnapshot.id,
|
|
48
|
-
name: run.vpSnapshot.name || run.vpSnapshot.id,
|
|
49
|
-
nameZh: run.vpSnapshot.nameZh || '',
|
|
50
|
-
role: run.vpSnapshot.role || '',
|
|
51
|
-
roleZh: run.vpSnapshot.roleZh || '',
|
|
52
|
-
} : null,
|
|
53
|
-
modelSnapshot: run.modelSnapshot ? {
|
|
54
|
-
id: run.modelSnapshot.id,
|
|
55
|
-
provider: run.modelSnapshot.provider || null,
|
|
56
|
-
effort: run.modelSnapshot.effort || null,
|
|
57
|
-
source: run.modelSnapshot.source || null,
|
|
58
|
-
} : null,
|
|
59
|
-
};
|
|
50
|
+
function projectActionStats(detail) {
|
|
51
|
+
if (!Array.isArray(detail?.actions)) return [];
|
|
52
|
+
return detail.actions.map(action => {
|
|
53
|
+
const projected = projectAction(action, detail.runs);
|
|
54
|
+
return {
|
|
55
|
+
id: projected.id,
|
|
56
|
+
status: projected.status,
|
|
57
|
+
loopCount: projected.loopCount,
|
|
58
|
+
toolCount: projected.toolCount,
|
|
59
|
+
};
|
|
60
|
+
});
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
function
|
|
63
|
-
if (
|
|
64
|
-
return
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
runId: event.runId || null,
|
|
69
|
-
type: event.type,
|
|
70
|
-
createdAt: event.createdAt,
|
|
71
|
-
};
|
|
63
|
+
function waitingReason(detail) {
|
|
64
|
+
if (typeof detail?.waitingReason === 'string') return detail.waitingReason;
|
|
65
|
+
if (detail?.status !== 'waiting' || !Array.isArray(detail.runs)) return '';
|
|
66
|
+
return detail.runs.find(run => (
|
|
67
|
+
run?.actionId === detail.currentActionId && typeof run.waitingReason === 'string'
|
|
68
|
+
))?.waitingReason || '';
|
|
72
69
|
}
|
|
73
70
|
|
|
74
71
|
/**
|
|
75
|
-
* Authenticated browser detail DTO. Execution
|
|
76
|
-
*
|
|
72
|
+
* Authenticated browser detail DTO. Execution records stay Agent-local; the
|
|
73
|
+
* browser receives only Action status and aggregate Loop/tool counts.
|
|
77
74
|
*/
|
|
78
75
|
export function projectWorkItemDetail(detail) {
|
|
79
76
|
if (!detail) return null;
|
|
@@ -86,21 +83,21 @@ export function projectWorkItemDetail(detail) {
|
|
|
86
83
|
workflowTemplate: detail.workflowTemplate,
|
|
87
84
|
status: detail.status,
|
|
88
85
|
currentActionId: detail.currentActionId || null,
|
|
89
|
-
currentRunId: detail.currentRunId || null,
|
|
90
86
|
reuseMemory: detail.reuseMemory !== false,
|
|
87
|
+
waitingReason: waitingReason(detail),
|
|
91
88
|
origin: detail.origin?.sessionId ? { sessionId: detail.origin.sessionId } : null,
|
|
92
89
|
linkedSessionIds: Array.isArray(detail.linkedSessionIds) ? detail.linkedSessionIds : [],
|
|
93
90
|
createdAt: detail.createdAt,
|
|
94
91
|
updatedAt: detail.updatedAt,
|
|
95
|
-
actions: Array.isArray(detail.actions)
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
actions: Array.isArray(detail.actions)
|
|
93
|
+
? detail.actions.map(action => projectAction(action, detail.runs))
|
|
94
|
+
: [],
|
|
98
95
|
};
|
|
99
96
|
}
|
|
100
97
|
|
|
101
98
|
/**
|
|
102
|
-
* Browser
|
|
103
|
-
*
|
|
99
|
+
* Browser list projection. This deliberately excludes local filesystem paths
|
|
100
|
+
* and all Run-level execution detail.
|
|
104
101
|
*/
|
|
105
102
|
export function projectWorkItemSummary(detail) {
|
|
106
103
|
if (!detail) return null;
|
|
@@ -120,6 +117,7 @@ export function projectWorkItemSummary(detail) {
|
|
|
120
117
|
};
|
|
121
118
|
}
|
|
122
119
|
const action = currentAction(detail);
|
|
120
|
+
const projectedAction = action ? projectAction(action, detail.runs) : null;
|
|
123
121
|
return {
|
|
124
122
|
id: detail.id,
|
|
125
123
|
revision: detail.revision,
|
|
@@ -127,12 +125,12 @@ export function projectWorkItemSummary(detail) {
|
|
|
127
125
|
goal: detail.goal,
|
|
128
126
|
status: detail.status,
|
|
129
127
|
currentActionId: detail.currentActionId || null,
|
|
130
|
-
currentAction:
|
|
131
|
-
id:
|
|
132
|
-
type:
|
|
133
|
-
stageId:
|
|
134
|
-
assignmentMode:
|
|
135
|
-
status:
|
|
128
|
+
currentAction: projectedAction ? {
|
|
129
|
+
id: projectedAction.id,
|
|
130
|
+
type: projectedAction.type,
|
|
131
|
+
stageId: projectedAction.stageId,
|
|
132
|
+
assignmentMode: projectedAction.assignmentPolicy?.mode || (projectedAction.requiredRole ? 'fixed' : null),
|
|
133
|
+
status: projectedAction.status,
|
|
136
134
|
} : null,
|
|
137
135
|
origin: detail.origin?.sessionId ? { sessionId: detail.origin.sessionId } : null,
|
|
138
136
|
linkedSessionIds: Array.isArray(detail.linkedSessionIds) ? detail.linkedSessionIds : [],
|
|
@@ -144,6 +142,9 @@ export function projectWorkItemSummary(detail) {
|
|
|
144
142
|
export function projectWorkCenterEvent(event) {
|
|
145
143
|
return {
|
|
146
144
|
type: event?.type || 'work_item.updated',
|
|
147
|
-
workItem:
|
|
145
|
+
workItem: {
|
|
146
|
+
...projectWorkItemSummary(event?.workItem),
|
|
147
|
+
actionStats: projectActionStats(event?.workItem),
|
|
148
|
+
},
|
|
148
149
|
};
|
|
149
150
|
}
|
|
@@ -298,6 +298,8 @@ export class WorkItemRunner {
|
|
|
298
298
|
});
|
|
299
299
|
|
|
300
300
|
let text = '';
|
|
301
|
+
let loopCount = 0;
|
|
302
|
+
let toolCount = 0;
|
|
301
303
|
try {
|
|
302
304
|
for await (const event of engine.query({
|
|
303
305
|
prompt: `${action.instruction}${completionContract(action)}`,
|
|
@@ -309,13 +311,22 @@ export class WorkItemRunner {
|
|
|
309
311
|
userAlreadyPersisted: true,
|
|
310
312
|
collabToolPolicy: 'single-vp',
|
|
311
313
|
})) {
|
|
314
|
+
if (event?.type === 'loop') loopCount += 1;
|
|
315
|
+
else if (event?.type === 'tool_end') toolCount += 1;
|
|
312
316
|
if (typeof event?.text === 'string') text += event.text;
|
|
313
317
|
else if (typeof event?.delta === 'string') text += event.delta;
|
|
314
318
|
else if (typeof event?.content === 'string' && event.type === 'assistant') text += event.content;
|
|
315
319
|
}
|
|
320
|
+
} catch (error) {
|
|
321
|
+
error.workItemExecutionStats = { loopCount, toolCount };
|
|
322
|
+
throw error;
|
|
316
323
|
} finally {
|
|
317
324
|
try { engine.abort?.('work_item_run_finished'); } catch {}
|
|
318
325
|
}
|
|
319
|
-
return
|
|
326
|
+
return {
|
|
327
|
+
...parseStructuredResult(text, action.type),
|
|
328
|
+
loopCount,
|
|
329
|
+
toolCount,
|
|
330
|
+
};
|
|
320
331
|
}
|
|
321
332
|
}
|
|
@@ -4,7 +4,7 @@ import { dirname, resolve } from 'node:path';
|
|
|
4
4
|
import { randomUUID } from 'node:crypto';
|
|
5
5
|
import { normalizeEvidence } from './evidence.js';
|
|
6
6
|
|
|
7
|
-
const SCHEMA_VERSION =
|
|
7
|
+
const SCHEMA_VERSION = 4;
|
|
8
8
|
const OPEN_ACTION_STATUSES = "'ready','running','waiting'";
|
|
9
9
|
const MAX_REUSABLE_CONTEXT_ITEMS = 12;
|
|
10
10
|
|
|
@@ -91,6 +91,8 @@ function mapRun(row) {
|
|
|
91
91
|
error: row.error || null,
|
|
92
92
|
reviewDecision: row.review_decision || null,
|
|
93
93
|
contractPatch: parseJson(row.contract_patch, null),
|
|
94
|
+
loopCount: Math.max(0, Number(row.loop_count) || 0),
|
|
95
|
+
toolCount: Math.max(0, Number(row.tool_count) || 0),
|
|
94
96
|
};
|
|
95
97
|
}
|
|
96
98
|
|
|
@@ -202,7 +204,9 @@ export class WorkItemStore {
|
|
|
202
204
|
waiting_reason TEXT,
|
|
203
205
|
error TEXT,
|
|
204
206
|
review_decision TEXT,
|
|
205
|
-
contract_patch TEXT
|
|
207
|
+
contract_patch TEXT,
|
|
208
|
+
loop_count INTEGER NOT NULL DEFAULT 0,
|
|
209
|
+
tool_count INTEGER NOT NULL DEFAULT 0
|
|
206
210
|
);
|
|
207
211
|
CREATE TABLE IF NOT EXISTS events (
|
|
208
212
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -249,6 +253,12 @@ export class WorkItemStore {
|
|
|
249
253
|
if (!hasColumn(this.db, 'runs', 'contract_patch')) {
|
|
250
254
|
this.db.exec('ALTER TABLE runs ADD COLUMN contract_patch TEXT');
|
|
251
255
|
}
|
|
256
|
+
if (!hasColumn(this.db, 'runs', 'loop_count')) {
|
|
257
|
+
this.db.exec('ALTER TABLE runs ADD COLUMN loop_count INTEGER NOT NULL DEFAULT 0');
|
|
258
|
+
}
|
|
259
|
+
if (!hasColumn(this.db, 'runs', 'tool_count')) {
|
|
260
|
+
this.db.exec('ALTER TABLE runs ADD COLUMN tool_count INTEGER NOT NULL DEFAULT 0');
|
|
261
|
+
}
|
|
252
262
|
if (!hasColumn(this.db, 'work_items', 'workflow_snapshot')) {
|
|
253
263
|
this.db.exec('ALTER TABLE work_items ADD COLUMN workflow_snapshot TEXT');
|
|
254
264
|
}
|
|
@@ -780,8 +790,8 @@ export class WorkItemStore {
|
|
|
780
790
|
}
|
|
781
791
|
const now = this.now();
|
|
782
792
|
this.db.prepare(`UPDATE runs SET status = ?, ended_at = ?, summary = ?, evidence = ?,
|
|
783
|
-
waiting_reason = ?, error = ?, review_decision = ?, contract_patch =
|
|
784
|
-
WHERE id = ?`).run(
|
|
793
|
+
waiting_reason = ?, error = ?, review_decision = ?, contract_patch = ?,
|
|
794
|
+
loop_count = ?, tool_count = ? WHERE id = ?`).run(
|
|
785
795
|
result.outcome,
|
|
786
796
|
now,
|
|
787
797
|
result.summary || '',
|
|
@@ -790,6 +800,8 @@ export class WorkItemStore {
|
|
|
790
800
|
result.error || null,
|
|
791
801
|
result.reviewDecision || null,
|
|
792
802
|
stringify(result.contractPatch || null),
|
|
803
|
+
Math.max(0, Number(result.loopCount) || 0),
|
|
804
|
+
Math.max(0, Number(result.toolCount) || 0),
|
|
793
805
|
runId,
|
|
794
806
|
);
|
|
795
807
|
this.onTransitionStep?.('after_run_update');
|
|
@@ -105,6 +105,8 @@ export class WorkItemWatcher {
|
|
|
105
105
|
summary: '',
|
|
106
106
|
evidence: [],
|
|
107
107
|
error: err?.message || String(err),
|
|
108
|
+
loopCount: err?.workItemExecutionStats?.loopCount || 0,
|
|
109
|
+
toolCount: err?.workItemExecutionStats?.toolCount || 0,
|
|
108
110
|
};
|
|
109
111
|
}
|
|
110
112
|
if (signal.aborted) return;
|