@yeaft/webchat-agent 1.0.345 → 1.0.347

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.
@@ -12,6 +12,7 @@ import { runPreflow } from '../memory/preflow.js';
12
12
  import { formatPickedForInjection } from '../sessions/pre-flow.js';
13
13
  import { existsSync, lstatSync, realpathSync } from 'node:fs';
14
14
  import path from 'node:path';
15
+ import { sessionMessageQuotePrompt } from '../session-message-quote.js';
15
16
  import { buildWorkItemAttachmentContext } from './attachments.js';
16
17
  import { withUsageAccounting } from '../llm/usage-accounting.js';
17
18
  import {
@@ -62,6 +63,21 @@ const WORK_ITEM_TOOL_NAMES = Object.freeze([
62
63
  ]);
63
64
  const WORK_ITEM_TOOL_ALLOWLIST = new Set(WORK_ITEM_TOOL_NAMES);
64
65
  const DEFAULT_PROGRESS_INTERVAL_MS = 200;
66
+ const ACTION_INPUT_QUOTE_MAX_BYTES = 8 * 1024;
67
+
68
+ export function renderPendingActionInput(item, attachmentFileById = new Map()) {
69
+ const attachments = Array.isArray(item?.attachments) ? item.attachments : [];
70
+ const attachmentLines = attachments.map(attachment => {
71
+ const file = attachmentFileById.get(attachment.id);
72
+ return file ? `- ${attachment.name}: ${file.ref}` : `- ${attachment.name}`;
73
+ });
74
+ const quoteContext = sessionMessageQuotePrompt(item?.quote, {
75
+ maxBytes: ACTION_INPUT_QUOTE_MAX_BYTES,
76
+ });
77
+ return [item?.text || '', quoteContext, attachmentLines.length > 0
78
+ ? `Additional WorkItem attachments:\n${attachmentLines.join('\n')}` : '']
79
+ .filter(Boolean).join('\n\n');
80
+ }
65
81
 
66
82
  function structuredOutcome(value) {
67
83
  return value && typeof value === 'object'
@@ -1197,13 +1213,7 @@ export class WorkItemRunner {
1197
1213
  ) || [];
1198
1214
  const accepted = [];
1199
1215
  for (const item of pending) {
1200
- const attachmentLines = item.attachments.map(attachment => {
1201
- const file = attachmentFileById.get(attachment.id);
1202
- return file ? `- ${attachment.name}: ${file.ref}` : `- ${attachment.name}`;
1203
- });
1204
- const content = [item.text, attachmentLines.length > 0
1205
- ? `Additional WorkItem attachments:\n${attachmentLines.join('\n')}` : '']
1206
- .filter(Boolean).join('\n\n');
1216
+ const content = renderPendingActionInput(item, attachmentFileById);
1207
1217
  if (!content) continue;
1208
1218
  pendingEntriesById.set(String(item.id), item);
1209
1219
  accepted.push({
@@ -388,6 +388,7 @@ export class WorkCenterService {
388
388
  ledgerRevision: payload.ledgerRevision,
389
389
  coordinatorRevision: payload.coordinatorRevision,
390
390
  clientMessageId: typeof payload.clientMessageId === 'string' ? payload.clientMessageId : null,
391
+ quote: payload.quote,
391
392
  addedAttachments,
392
393
  attachments: [...(workItem.attachments || []), ...addedAttachments],
393
394
  }, {
@@ -450,6 +451,7 @@ export class WorkCenterService {
450
451
  revision: payload.revision,
451
452
  generation,
452
453
  clientMessageId: typeof payload.clientMessageId === 'string' ? payload.clientMessageId : null,
454
+ quote: payload.quote,
453
455
  addedAttachmentCount: addedAttachments.length,
454
456
  addedAttachments,
455
457
  attachments: [...(workItem.attachments || []), ...addedAttachments],
@@ -610,6 +612,7 @@ export class WorkCenterService {
610
612
  recovery: Boolean(recoverable.recovery),
611
613
  addedAttachments: Array.isArray(recoverable.addedAttachments)
612
614
  ? recoverable.addedAttachments : [],
615
+ quote: recoverable.quote || null,
613
616
  onUpdate: (type, detail) => this.#emit({ type, workItem: detail }),
614
617
  });
615
618
  turn?.task?.catch?.(() => {});
@@ -385,6 +385,8 @@ function carryCurrentActionInputContext(db, action, extraInputs = [], explicitOn
385
385
  return [{
386
386
  ...entry,
387
387
  inputId,
388
+ summary: event?.data?.text || entry.summary || '',
389
+ quote: event?.data?.quote || entry.quote || null,
388
390
  attachments: Array.isArray(entry.attachments) && entry.attachments.length > 0
389
391
  ? entry.attachments
390
392
  : inputAttachments(event),
@@ -400,6 +402,7 @@ function carryCurrentActionInputContext(db, action, extraInputs = [], explicitOn
400
402
  role: 'user',
401
403
  inputId,
402
404
  summary: event.data?.text || '',
405
+ quote: event.data?.quote || null,
403
406
  attachments: inputAttachments(event),
404
407
  evidence: [],
405
408
  });
@@ -1755,7 +1758,7 @@ export class WorkItemStore {
1755
1758
  return true;
1756
1759
  }
1757
1760
 
1758
- addActionInput(id, input, expected, attachments = null, addedAttachments = [], clientMessageId = null) {
1761
+ addActionInput(id, input, expected, attachments = null, addedAttachments = [], clientMessageId = null, quote = null) {
1759
1762
  return withTransaction(this.db, () => {
1760
1763
  const sourceKey = typeof clientMessageId === 'string' && clientMessageId
1761
1764
  ? `client:message:${id}:${clientMessageId}` : null;
@@ -1818,6 +1821,7 @@ export class WorkItemStore {
1818
1821
  role: 'user',
1819
1822
  inputId,
1820
1823
  summary: input,
1824
+ quote,
1821
1825
  attachments: projectedAttachments,
1822
1826
  evidence: [],
1823
1827
  });
@@ -1871,6 +1875,7 @@ export class WorkItemStore {
1871
1875
  inputId,
1872
1876
  clientMessageId,
1873
1877
  text: input,
1878
+ quote,
1874
1879
  attachments: projectedAttachments,
1875
1880
  }, { actionId: action.id, runId: eventRunId, actionGeneration: eventGeneration });
1876
1881
  if (action.status === 'running') {
@@ -1896,7 +1901,7 @@ export class WorkItemStore {
1896
1901
  status: 'pending',
1897
1902
  text: input,
1898
1903
  attachments: projectedAttachments,
1899
- payload: { eventId, inputId, actionGeneration: eventGeneration, actionSpecHash: eventSpecHash },
1904
+ payload: { eventId, inputId, quote, actionGeneration: eventGeneration, actionSpecHash: eventSpecHash },
1900
1905
  createdAt: now,
1901
1906
  }, sourceKey || `pending_action_inputs:event:${eventId}`);
1902
1907
  } else if (sourceKey) {
@@ -1909,7 +1914,7 @@ export class WorkItemStore {
1909
1914
  status: 'consumed',
1910
1915
  text: input,
1911
1916
  attachments: projectedAttachments,
1912
- payload: { eventId, inputId, actionGeneration: eventGeneration, actionSpecHash: eventSpecHash },
1917
+ payload: { eventId, inputId, quote, actionGeneration: eventGeneration, actionSpecHash: eventSpecHash },
1913
1918
  createdAt: now,
1914
1919
  }, sourceKey);
1915
1920
  }
@@ -1920,8 +1925,8 @@ export class WorkItemStore {
1920
1925
  listPendingActionInputs(actionId, runId, ownerBootId, leaseEpoch) {
1921
1926
  const active = this.#activeRunRow(runId, ownerBootId, leaseEpoch, true);
1922
1927
  if (!active || active.action_id !== actionId) return [];
1923
- return this.db.prepare(`SELECT p.*, ae.id AS action_entry_id, ae.sequence AS action_entry_sequence
1924
- FROM pending_action_inputs p
1928
+ return this.db.prepare(`SELECT p.*, ae.id AS action_entry_id, ae.sequence AS action_entry_sequence,
1929
+ ae.payload AS action_entry_payload FROM pending_action_inputs p
1925
1930
  LEFT JOIN action_entries ae ON CAST(json_extract(ae.payload, '$.eventId') AS INTEGER) = p.event_id
1926
1931
  AND ae.action_id = p.action_id
1927
1932
  WHERE p.action_id = ? AND p.run_id = ? AND p.action_generation = ? AND p.action_spec_hash = ?
@@ -1932,6 +1937,7 @@ export class WorkItemStore {
1932
1937
  actionEntryId: row.action_entry_id || null,
1933
1938
  sequence: row.action_entry_sequence == null ? null : Number(row.action_entry_sequence),
1934
1939
  text: row.text || '',
1940
+ quote: parseJson(row.action_entry_payload, null)?.quote || null,
1935
1941
  attachments: parseJson(row.attachments, []),
1936
1942
  }));
1937
1943
  }
@@ -3155,9 +3161,11 @@ export class WorkItemStore {
3155
3161
  throw new Error('WorkItem Coordinator message or attachments are required');
3156
3162
  }
3157
3163
  const turnId = randomUUID();
3164
+ const quote = options.quote && typeof options.quote === 'object' ? options.quote : null;
3158
3165
  const userMessage = automaticRecovery ? null : {
3159
3166
  id: randomUUID(), turnId, role: 'user', text, attachments: projectedAttachments,
3160
3167
  status: 'completed', createdAt: now,
3168
+ ...(quote ? { quote } : {}),
3161
3169
  };
3162
3170
  const assistantMessage = {
3163
3171
  id: randomUUID(), turnId, role: 'assistant', text: '', status: 'thinking',
@@ -3177,6 +3185,7 @@ export class WorkItemStore {
3177
3185
  text,
3178
3186
  recovery,
3179
3187
  clientMessageId,
3188
+ ...(quote ? { quote } : {}),
3180
3189
  addedAttachments: projectedAttachments,
3181
3190
  }, clientMessageId ? `client:message:${id}:${clientMessageId}` : `coordinator:turn:${turnId}`);
3182
3191
  const messages = [...(workItem.messages || []), ...(userMessage ? [userMessage] : []), assistantMessage]
@@ -1,3 +1,4 @@
1
+ import { sessionMessageQuotePrompt } from '../session-message-quote.js';
1
2
  import { renderSessionContextSnapshot } from './session-context.js';
2
3
 
3
4
  export const BUILT_IN_ACTION_TYPES = Object.freeze([
@@ -22,6 +23,7 @@ const MODEL_MODES = new Set(['inherit', 'primary', 'fast', 'specific']);
22
23
  const MODEL_EFFORTS = new Set(['minimal', 'low', 'medium', 'high', 'xhigh', 'max']);
23
24
  const WORKSPACE_MODES = new Set(['shared', 'read', 'isolated-write', 'integrate']);
24
25
  const HIGH_EFFORT_ACTION_TYPES = new Set(['triage', 'research', 'design', 'diagnose', 'review']);
26
+ const ACTION_CONTEXT_QUOTE_MAX_BYTES = 8 * 1024;
25
27
 
26
28
  const DEFAULT_STAGE_INSTRUCTIONS = Object.freeze({
27
29
  triage: 'Turn the request into an executable contract. Inspect relevant repository facts before deciding the flow. Classify the WorkItem, identify constraints, risks, dependencies, and missing acceptance criteria, then plan only the Actions needed for this task. Do not implement. If the goal or acceptance criteria must change, submit a contractPatch and explain why.',
@@ -716,8 +718,11 @@ function renderContext(context = []) {
716
718
  const decision = entry.reviewDecision ? `\nReview decision: ${entry.reviewDecision}` : '';
717
719
  const waitingReason = entry.waitingReason ? `\nWaiting reason: ${entry.waitingReason}` : '';
718
720
  const answer = entry.answer ? `\nUser answer: ${entry.answer}` : '';
721
+ const quote = entry.quote
722
+ ? sessionMessageQuotePrompt(entry.quote, { maxBytes: ACTION_CONTEXT_QUOTE_MAX_BYTES })
723
+ : '';
719
724
  const source = entry.sourceTitle ? ` from ${entry.sourceTitle}` : '';
720
- return `### ${entry.type}${source} (${entry.vpId || entry.role || 'unknown VP'})\n${entry.summary || '(no summary)'}${decision}${waitingReason}${answer}${evidence}`;
725
+ return `### ${entry.type}${source} (${entry.vpId || entry.role || 'unknown VP'})\n${entry.summary || '(no summary)'}${decision}${waitingReason}${answer}${quote}${evidence}`;
721
726
  });
722
727
  return `\n\nReusable Work Center context and prior Action results:\n${blocks.join('\n\n')}`;
723
728
  }