@yeaft/webchat-agent 1.0.547 → 1.0.548

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "1.0.547",
3
+ "version": "1.0.548",
4
4
  "description": "Remote worker agent for Yeaft Web Code Agent — connects the native Yeaft engine, CLI providers, and workbench tools",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,4 +1,5 @@
1
1
  const MAX_TOOL_EVENTS = 16;
2
+ const MAX_TOOL_ID_LENGTH = 256;
2
3
  const MAX_TOOL_NAME_LENGTH = 80;
3
4
  const MAX_RESOURCE_LENGTH = 240;
4
5
  const MAX_RESPONSE_LENGTH = 4_000;
@@ -9,27 +10,33 @@ function boundedString(value, maxLength) {
9
10
  }
10
11
 
11
12
  function safeResource(value) {
12
- const resource = boundedString(value, MAX_RESOURCE_LENGTH);
13
- if (!resource) return '';
13
+ const raw = typeof value === 'string' ? value.trim() : '';
14
+ if (!raw) return '';
15
+ const looksLikeHttpUrl = /^https?:/i.test(raw);
14
16
  try {
15
- const url = new URL(resource);
17
+ const url = new URL(raw);
16
18
  if (['http:', 'https:'].includes(url.protocol)) {
17
19
  return `${url.protocol}//${url.host}${url.pathname}`.slice(0, MAX_RESOURCE_LENGTH);
18
20
  }
19
- } catch {}
20
- return resource;
21
+ if (looksLikeHttpUrl) return '';
22
+ } catch {
23
+ if (looksLikeHttpUrl) return '';
24
+ }
25
+ return raw.slice(0, MAX_RESOURCE_LENGTH);
21
26
  }
22
27
 
23
28
  function normalizeToolEvent(value) {
24
29
  if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
25
30
  const name = boundedString(value.name, MAX_TOOL_NAME_LENGTH);
26
31
  if (!name) return null;
27
- const event = {
28
- name,
29
- status: value.status === 'error' ? 'error' : 'completed',
30
- };
32
+ const status = ['running', 'error'].includes(value.status) ? value.status : 'completed';
33
+ const event = { name, status };
34
+ const id = boundedString(value.id, MAX_TOOL_ID_LENGTH);
35
+ if (id) event.id = id;
31
36
  const resource = safeResource(value.resource);
32
37
  if (resource) event.resource = resource;
38
+ const startedAt = Number(value.startedAt);
39
+ if (Number.isFinite(startedAt) && startedAt > 0) event.startedAt = startedAt;
33
40
  return event;
34
41
  }
35
42
 
@@ -48,9 +55,25 @@ export function normalizeActionCheckpoint(value) {
48
55
 
49
56
  export function appendCheckpointToolEvent(checkpoint, event) {
50
57
  const current = normalizeActionCheckpoint(checkpoint) || { version: 1, toolEvents: [] };
58
+ const normalized = normalizeToolEvent(event);
59
+ if (!normalized) return current;
60
+ const toolEvents = normalized.id
61
+ ? current.toolEvents.filter(item => item.id !== normalized.id)
62
+ : current.toolEvents;
63
+ return normalizeActionCheckpoint({
64
+ ...current,
65
+ toolEvents: [...toolEvents, normalized],
66
+ });
67
+ }
68
+
69
+ export function settleCheckpointToolEvents(checkpoint, status = 'error') {
70
+ const current = normalizeActionCheckpoint(checkpoint);
71
+ if (!current?.toolEvents.some(event => event.status === 'running')) return current;
51
72
  return normalizeActionCheckpoint({
52
73
  ...current,
53
- toolEvents: [...current.toolEvents, event],
74
+ toolEvents: current.toolEvents.map(event => (
75
+ event.status === 'running' ? { ...event, status } : event
76
+ )),
54
77
  });
55
78
  }
56
79
 
@@ -10,6 +10,7 @@ import { runMatchesActionIdentity } from './action-identity.js';
10
10
  import { normalizeOutputs } from './evidence.js';
11
11
  import { taskSpecificActionBrief } from './workflow.js';
12
12
  import { buildMainlineProjection } from './mainline-projection.js';
13
+ import { normalizeActionCheckpoint } from './action-checkpoint.js';
13
14
 
14
15
  const MAX_ACTION_MESSAGE_CHARS = 16_000;
15
16
  const MAX_ACTION_DIAGNOSTIC_CHARS = 8_000;
@@ -293,8 +294,13 @@ function normalizeProjectedMessage(message) {
293
294
  const text = typeof message.text === 'string'
294
295
  ? message.text.trim().slice(0, MAX_ACTION_MESSAGE_CHARS)
295
296
  : '';
297
+ const checkpoint = normalizeActionCheckpoint({ toolEvents: message.toolEvents });
298
+ const toolEvents = (checkpoint?.toolEvents || []).map((event, index) => ({
299
+ ...event,
300
+ id: event.id || `${index}`,
301
+ }));
296
302
  const attachments = projectAttachments(message.attachments);
297
- if (!text && attachments.length === 0) return null;
303
+ if (!text && attachments.length === 0 && toolEvents.length === 0) return null;
298
304
  const speaker = message.role === 'user' ? null : projectVpSpeaker(message.speaker);
299
305
  return {
300
306
  id: String(message.id || ''),
@@ -302,6 +308,7 @@ function normalizeProjectedMessage(message) {
302
308
  kind: message.kind === 'input' ? 'input' : 'response',
303
309
  status: message.status || null,
304
310
  text,
311
+ toolEvents,
305
312
  attachments,
306
313
  ...(projectedMessageQuote(message.quote) ? { quote: projectedMessageQuote(message.quote) } : {}),
307
314
  createdAt: count(message.createdAt),
@@ -341,6 +348,7 @@ function runResponseMessage(run) {
341
348
  kind: 'response',
342
349
  status: run.status || 'running',
343
350
  text,
351
+ toolEvents: run.checkpoint?.toolEvents,
344
352
  createdAt: count(run.endedAt || run.startedAt),
345
353
  updatedAt: count(run.endedAt || run.startedAt),
346
354
  progressRevision: count(run.progressRevision),
@@ -30,6 +30,7 @@ import {
30
30
  import {
31
31
  appendCheckpointToolEvent,
32
32
  renderActionResumeBlock,
33
+ settleCheckpointToolEvents,
33
34
  } from './action-checkpoint.js';
34
35
  import { createSkillManager } from '../skills.js';
35
36
  import { loadMCPConfig } from '../config.js';
@@ -1303,6 +1304,7 @@ export class WorkItemRunner {
1303
1304
  let checkpoint = null;
1304
1305
  let terminalEngineError = null;
1305
1306
  const toolInputs = new Map();
1307
+ const toolStartedAt = new Map();
1306
1308
  const usageStats = {
1307
1309
  llmRequestCount: 0,
1308
1310
  inputTokens: 0,
@@ -1318,6 +1320,14 @@ export class WorkItemRunner {
1318
1320
  ...executionStats(),
1319
1321
  checkpoint,
1320
1322
  });
1323
+ const settleOpenTools = (status = 'error') => {
1324
+ const settled = settleCheckpointToolEvents(checkpoint, status);
1325
+ const changed = settled !== checkpoint;
1326
+ checkpoint = settled;
1327
+ toolInputs.clear();
1328
+ toolStartedAt.clear();
1329
+ return changed;
1330
+ };
1321
1331
  const reportProgress = (force = false) => {
1322
1332
  if (typeof onProgress !== 'function') return;
1323
1333
  const now = Date.now();
@@ -1472,16 +1482,33 @@ export class WorkItemRunner {
1472
1482
  response: publicWorkItemResponse(event.response),
1473
1483
  });
1474
1484
  }
1475
- else if (event?.type === 'tool_start') toolInputs.set(event.id, event.input);
1485
+ else if (event?.type === 'tool_start') {
1486
+ const startedAt = Date.now();
1487
+ toolInputs.set(event.id, event.input);
1488
+ toolStartedAt.set(event.id, startedAt);
1489
+ checkpoint = appendCheckpointToolEvent(checkpoint, {
1490
+ id: event.id,
1491
+ name: event.name,
1492
+ status: 'running',
1493
+ resource: checkpointResource(event.name, event.input, workDir),
1494
+ startedAt,
1495
+ });
1496
+ reportProgress(true);
1497
+ }
1476
1498
  else if (event?.type === 'tool_end') {
1477
1499
  toolCount += 1;
1478
1500
  const input = toolInputs.get(event.id);
1479
1501
  toolInputs.delete(event.id);
1502
+ const startedAt = toolStartedAt.get(event.id);
1503
+ toolStartedAt.delete(event.id);
1480
1504
  checkpoint = appendCheckpointToolEvent(checkpoint, {
1505
+ id: event.id,
1481
1506
  name: event.name,
1482
1507
  status: event.isError ? 'error' : 'completed',
1483
1508
  resource: checkpointResource(event.name, input, workDir),
1509
+ startedAt,
1484
1510
  });
1511
+ reportProgress(true);
1485
1512
  }
1486
1513
  else if (event?.type === 'error' && !terminalEngineError) {
1487
1514
  terminalEngineError = event.error instanceof Error
@@ -1523,11 +1550,13 @@ export class WorkItemRunner {
1523
1550
  throw stopped;
1524
1551
  }
1525
1552
  } catch (error) {
1553
+ if (settleOpenTools('error')) reportProgress(true);
1526
1554
  error.workItemExecutionStats = currentProgress();
1527
1555
  throw error;
1528
1556
  } finally {
1529
1557
  try { engine.abort?.('work_item_run_finished'); } catch {}
1530
1558
  }
1559
+ settleOpenTools('error');
1531
1560
  const response = publicWorkItemResponse(text);
1532
1561
  reportProgress(true);
1533
1562
  const submittedPlan = !replanToolEnabled ? planCollector.value : null;