@yeaft/webchat-agent 1.0.558 → 1.0.560

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.558",
3
+ "version": "1.0.560",
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",
@@ -254,6 +254,7 @@ export async function handleWorkCenterRequest(msg) {
254
254
  op,
255
255
  ok: false,
256
256
  error: err?.message || String(err),
257
+ ...(err?.code === 'WORK_CENTER_INPUT_STALE' ? { errorCode: err.code } : {}),
257
258
  _requestUserId: msg._requestUserId || null,
258
259
  });
259
260
  }
@@ -251,11 +251,14 @@ export class WorkflowController {
251
251
  throw new Error('actionId, revision, and generation are required for Action input');
252
252
  }
253
253
  const graphMode = workItem.workflowSnapshot?.executionMode === 'graph';
254
+ const concurrentMode = graphMode || workItem.coordinationMode === 'dynamic';
254
255
  const targetMatches = targetAction?.workItemId === id
255
256
  && targetAction.generation === expectedGeneration
256
- && (graphMode || workItem.currentActionId === input.actionId);
257
+ && (concurrentMode || workItem.currentActionId === input.actionId);
257
258
  if (!targetMatches || workItem.revision !== input.revision) {
258
- throw new Error('Action changed before input was applied; refresh and try again');
259
+ const error = new Error('Action changed before input was applied; refresh and try again');
260
+ error.code = 'WORK_CENTER_INPUT_STALE';
261
+ throw error;
259
262
  }
260
263
  if (['ready', 'running'].includes(targetAction.status)) {
261
264
  if (targetAction.status === 'running' && addedAttachmentCount > 0) {
@@ -269,7 +272,9 @@ export class WorkflowController {
269
272
  }, input.attachments, input.addedAttachments, input.clientMessageId, quote);
270
273
  }
271
274
  if (!['waiting', 'failed'].includes(targetAction.status)) {
272
- throw new Error(`Action in ${targetAction.status} cannot accept input`);
275
+ const error = new Error(`Action in ${targetAction.status} cannot accept input`);
276
+ error.code = 'WORK_CENTER_INPUT_STALE';
277
+ throw error;
273
278
  }
274
279
  return this.retry(id, {
275
280
  answer: text,
@@ -1944,7 +1944,9 @@ export class WorkItemStore {
1944
1944
  ? ['ready', 'running', 'waiting', 'needs_attention']
1945
1945
  : ['ready', 'running'];
1946
1946
  if (!inputStatuses.includes(workItem.status)) {
1947
- throw new Error(`WorkItem in ${workItem.status} cannot accept Action input`);
1947
+ const error = new Error(`WorkItem in ${workItem.status} cannot accept Action input`);
1948
+ error.code = 'WORK_CENTER_INPUT_STALE';
1949
+ throw error;
1948
1950
  }
1949
1951
  const action = this.getAction(expected.actionId);
1950
1952
  const actionMatches = action?.workItemId === id
@@ -1956,7 +1958,9 @@ export class WorkItemStore {
1956
1958
  || (activeRun?.status === 'running' && activeRun.acceptingInput !== false
1957
1959
  && runMatchesActionIdentity(activeRun, action));
1958
1960
  if (!actionMatches || !runMatches || workItem.revision !== expected.revision) {
1959
- throw new Error('Action changed before input was applied; refresh and try again');
1961
+ const error = new Error('Action changed before input was applied; refresh and try again');
1962
+ error.code = 'WORK_CENTER_INPUT_STALE';
1963
+ throw error;
1960
1964
  }
1961
1965
  const projectedAttachments = (Array.isArray(addedAttachments) ? addedAttachments : []).map(attachment => ({
1962
1966
  id: attachment.id,
@@ -1998,7 +2002,7 @@ export class WorkItemStore {
1998
2002
  const changedAction = this.db.prepare(`UPDATE actions SET context = ?, instruction = ?, attempt = 0,
1999
2003
  generation = generation + 1, spec_hash = ?, identity_history = ?, result_run_id = NULL,
2000
2004
  workspace = NULL, updated_at = ? WHERE id = ? AND status = 'ready' AND current_run_id IS NULL
2001
- AND generation = ? AND spec_hash = ?`).run(
2005
+ AND generation = ? AND spec_hash = ? AND contract_revision = ?`).run(
2002
2006
  stringify(context),
2003
2007
  nextAction.instruction,
2004
2008
  nextAction.specHash,
@@ -2007,9 +2011,12 @@ export class WorkItemStore {
2007
2011
  action.id,
2008
2012
  action.generation,
2009
2013
  action.specHash,
2014
+ action.contractRevision,
2010
2015
  );
2011
2016
  if (Number(changedAction.changes) !== 1) {
2012
- throw new Error('Action changed before input was applied; refresh and try again');
2017
+ const error = new Error('Action changed before input was applied; refresh and try again');
2018
+ error.code = 'WORK_CENTER_INPUT_STALE';
2019
+ throw error;
2013
2020
  }
2014
2021
  this.#supersedePendingActionInputs(
2015
2022
  [action],
@@ -4536,12 +4543,15 @@ export class WorkItemStore {
4536
4543
  }
4537
4544
  const workItem = this.getWorkItem(id);
4538
4545
  if (!workItem) return null;
4546
+ const dynamicMode = isDynamicWorkItem(workItem);
4539
4547
  const graphMode = usesLegacyGraph(workItem);
4540
- const retryableWorkItemStatuses = graphMode
4548
+ const retryableWorkItemStatuses = graphMode || dynamicMode
4541
4549
  ? ['ready', 'running', 'waiting', 'needs_attention']
4542
4550
  : ['waiting', 'needs_attention'];
4543
4551
  if (!retryableWorkItemStatuses.includes(workItem.status)) {
4544
- throw new Error(`WorkItem in ${workItem.status} does not need retry`);
4552
+ const error = new Error(`WorkItem in ${workItem.status} does not need retry`);
4553
+ error.code = 'WORK_CENTER_INPUT_STALE';
4554
+ throw error;
4545
4555
  }
4546
4556
  let previous = workItem.currentActionId ? this.getAction(workItem.currentActionId) : null;
4547
4557
  if (options.expected) {
@@ -4553,10 +4563,12 @@ export class WorkItemStore {
4553
4563
  const hasExpectedGeneration = Number.isInteger(expectedGeneration) && expectedGeneration > 0;
4554
4564
  const expectedMatches = expectedAction?.workItemId === id
4555
4565
  && allowedExpectedStatuses.includes(expectedAction.status)
4556
- && (graphMode || workItem.currentActionId === options.expected.actionId)
4557
- && (hasExpectedGeneration ? expectedAction.generation === expectedGeneration : !graphMode);
4566
+ && (graphMode || dynamicMode || workItem.currentActionId === options.expected.actionId)
4567
+ && (hasExpectedGeneration ? expectedAction.generation === expectedGeneration : !(graphMode || dynamicMode));
4558
4568
  if (!expectedMatches || workItem.revision !== options.expected.revision) {
4559
- throw new Error('Action changed before input was applied; refresh and try again');
4569
+ const error = new Error('Action changed before input was applied; refresh and try again');
4570
+ error.code = 'WORK_CENTER_INPUT_STALE';
4571
+ throw error;
4560
4572
  }
4561
4573
  previous = expectedAction;
4562
4574
  }
@@ -4565,7 +4577,7 @@ export class WorkItemStore {
4565
4577
  AND status != 'running' ORDER BY ended_at DESC, started_at DESC LIMIT 1`).get(id, previous.id))
4566
4578
  : null;
4567
4579
  const now = this.now();
4568
- const revision = options.expected ? workItem.revision + 1 : workItem.revision;
4580
+ const revision = options.expected && !dynamicMode ? workItem.revision + 1 : workItem.revision;
4569
4581
  const replacement = {
4570
4582
  ...makeAction(workItem, previous, previousRun),
4571
4583
  contractRevision: previous?.contractRevision ?? workItem.revision,
@@ -4573,6 +4585,55 @@ export class WorkItemStore {
4573
4585
  const inputEvent = options.inputEvent && typeof options.inputEvent === 'object'
4574
4586
  ? options.inputEvent
4575
4587
  : null;
4588
+ if (dynamicMode) {
4589
+ if (!previous) throw new Error('Dynamic WorkItem retry target is missing');
4590
+ const candidate = {
4591
+ ...previous,
4592
+ ...replacement,
4593
+ id: previous.id,
4594
+ generation: previous.generation + 1,
4595
+ attempt: 0,
4596
+ currentRunId: null,
4597
+ resultRunId: null,
4598
+ workspace: null,
4599
+ };
4600
+ candidate.instruction = canonicalActionInstruction(workItem, candidate, candidate.context);
4601
+ candidate.specHash = actionSpecHash(candidate);
4602
+ const changed = this.db.prepare(`UPDATE actions SET status = 'ready', attempt = 0,
4603
+ current_run_id = NULL, context = ?, instruction = ?, generation = generation + 1,
4604
+ spec_hash = ?, identity_history = ?, result_run_id = NULL, workspace = NULL, updated_at = ?
4605
+ WHERE id = ? AND work_item_id = ? AND status IN ('waiting', 'failed')
4606
+ AND current_run_id IS NULL AND generation = ? AND spec_hash = ? AND contract_revision = ?`).run(
4607
+ stringify(candidate.context), candidate.instruction, candidate.specHash,
4608
+ stringify(actionIdentityHistory(previous, candidate.generation, candidate.specHash)),
4609
+ now, previous.id, id, previous.generation, previous.specHash, previous.contractRevision,
4610
+ );
4611
+ if (Number(changed.changes) !== 1) {
4612
+ const error = new Error('Action changed before input was applied; refresh and try again');
4613
+ error.code = 'WORK_CENTER_INPUT_STALE';
4614
+ throw error;
4615
+ }
4616
+ const changedWorkItem = this.db.prepare(`UPDATE work_items SET attachments = ?, updated_at = ?
4617
+ WHERE id = ? AND revision = ? AND status NOT IN ('done', 'cancelled')`).run(
4618
+ stringify(Array.isArray(options.attachments) ? options.attachments : workItem.attachments),
4619
+ now, id, workItem.revision,
4620
+ );
4621
+ if (Number(changedWorkItem.changes) !== 1) {
4622
+ const error = new Error('Action changed before input was applied; refresh and try again');
4623
+ error.code = 'WORK_CENTER_INPUT_STALE';
4624
+ throw error;
4625
+ }
4626
+ const action = this.getAction(previous.id);
4627
+ if (inputEvent) {
4628
+ this.appendEvent(id, 'action.input_added', inputEvent, {
4629
+ actionId: action.id,
4630
+ actionGeneration: action.generation,
4631
+ });
4632
+ } else {
4633
+ this.appendEvent(id, 'work_item.retried', {}, { actionId: action.id });
4634
+ }
4635
+ return this.getWorkItemDetail(id);
4636
+ }
4576
4637
  if (graphMode) {
4577
4638
  if (!previous) throw new Error('WorkItem graph retry target is missing');
4578
4639
  const action = this.#resetGraphFromStage(