@yeaft/webchat-agent 1.0.366 → 1.0.367

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.
@@ -23,9 +23,12 @@ import { readWorkCenterSettings, writeWorkCenterSettings } from './settings.js';
23
23
  import {
24
24
  defaultWorkCenterStageInstructions,
25
25
  listWorkItemTypeTemplates,
26
- resolvePlanningWorkflowSnapshot,
27
- resolveWorkflowSnapshot,
28
26
  } from './workflow.js';
27
+ import {
28
+ DYNAMIC_COORDINATION_MODE,
29
+ DYNAMIC_EXECUTION_SCHEMA_VERSION,
30
+ resolveDynamicActionPolicySnapshot,
31
+ } from './dynamic-coordination.js';
29
32
 
30
33
  function requiredString(value, name) {
31
34
  if (typeof value !== 'string' || !value.trim()) throw new Error(`${name} is required`);
@@ -122,6 +125,7 @@ export class WorkCenterService {
122
125
  ? Number(options.pollIntervalMs)
123
126
  : 2_000;
124
127
  this.recoveryTimer = null;
128
+ this.dynamicCoordinatorTasks = new Map();
125
129
  this.shuttingDown = false;
126
130
  this.watcher = new WorkItemWatcher({
127
131
  store: this.store,
@@ -227,14 +231,8 @@ export class WorkCenterService {
227
231
  }
228
232
  case 'create': {
229
233
  const settings = this.settingsReader(this.yeaftDir);
230
- const explicitWorkflow = requestContext.trustedProducer === true
231
- && typeof payload.workflowTemplate === 'string' && payload.workflowTemplate.trim()
232
- ? payload.workflowTemplate.trim()
233
- : null;
234
- const workflowTemplate = explicitWorkflow || 'ai-planned';
235
- const workflowSnapshot = explicitWorkflow
236
- ? resolveWorkflowSnapshot(settings, explicitWorkflow, payload.stageOverrides)
237
- : resolvePlanningWorkflowSnapshot(settings, payload.workItemType);
234
+ const workflowTemplate = 'coordinator-driven';
235
+ const workflowSnapshot = resolveDynamicActionPolicySnapshot(settings, payload.workItemType);
238
236
  const runtime = !Object.hasOwn(payload, 'workDir') && !settings.defaultWorkDir
239
237
  ? await this.runtimeInfo()
240
238
  : null;
@@ -248,6 +246,7 @@ export class WorkCenterService {
248
246
  root: this.attachmentRoot,
249
247
  workItemId,
250
248
  });
249
+ const shouldStart = payload.start === undefined ? settings.startImmediately : payload.start !== false;
251
250
  this.controller.create({
252
251
  id: workItemId,
253
252
  title: requiredString(payload.title, 'title'),
@@ -257,6 +256,8 @@ export class WorkCenterService {
257
256
  : [],
258
257
  workflowTemplate,
259
258
  workflowSnapshot,
259
+ coordinationMode: DYNAMIC_COORDINATION_MODE,
260
+ executionSchemaVersion: DYNAMIC_EXECUTION_SCHEMA_VERSION,
260
261
  workDir,
261
262
  reuseMemory: payload.reuseMemory !== false,
262
263
  origin: payload.origin && typeof payload.origin === 'object'
@@ -274,9 +275,13 @@ export class WorkCenterService {
274
275
  ? normalizeSessionContextSnapshot(payload.sessionContext)
275
276
  : [],
276
277
  attachments,
277
- start: payload.start === undefined ? settings.startImmediately : payload.start !== false,
278
+ start: false,
278
279
  });
279
- const detail = this.#requiredItem(workItemId);
280
+ let detail = this.#requiredItem(workItemId);
281
+ if (shouldStart) {
282
+ detail = this.controller.start(workItemId);
283
+ this.#queueDynamicCoordinatorWake(workItemId);
284
+ }
280
285
  this.#emit({ type: 'work_item.created', workItem: detail });
281
286
  return detail;
282
287
  } catch (error) {
@@ -288,11 +293,17 @@ export class WorkCenterService {
288
293
  const id = requiredString(payload.id, 'id');
289
294
  const detail = this.controller.update(id, payload.patch || {});
290
295
  this.watcher.abortInvalidWorkItemRuns(id);
296
+ if (detail.coordinationMode === DYNAMIC_COORDINATION_MODE) {
297
+ this.#queueDynamicCoordinatorWake(id);
298
+ }
291
299
  this.#emit({ type: 'work_item.updated', workItem: detail });
292
300
  return detail;
293
301
  }
294
302
  case 'start': {
295
303
  const detail = this.controller.start(requiredString(payload.id, 'id'));
304
+ if (detail.coordinationMode === DYNAMIC_COORDINATION_MODE) {
305
+ this.#queueDynamicCoordinatorWake(detail.id);
306
+ }
296
307
  this.#emit({ type: 'work_item.started', workItem: detail });
297
308
  return detail;
298
309
  }
@@ -307,6 +318,9 @@ export class WorkCenterService {
307
318
  const id = requiredString(payload.id, 'id');
308
319
  const detail = this.controller.resume(id, { revision: payload.revision });
309
320
  this.watcher.abortInvalidWorkItemRuns(id);
321
+ if (detail.coordinationMode === DYNAMIC_COORDINATION_MODE) {
322
+ this.#queueDynamicCoordinatorWake(id);
323
+ }
310
324
  this.#emit({ type: 'work_item.resumed', workItem: detail });
311
325
  return detail;
312
326
  }
@@ -571,6 +585,10 @@ export class WorkCenterService {
571
585
 
572
586
  #emit(event) {
573
587
  try { this.onEvent(event); } catch {}
588
+ if (event?.workItem?.coordinationMode === DYNAMIC_COORDINATION_MODE) {
589
+ this.#queueDynamicCoordinatorWake(event.workItem.id);
590
+ return;
591
+ }
574
592
  if (['run.finished', 'coordinator.turn_completed'].includes(event?.type)) {
575
593
  for (const action of event.workItem?.actions || []) {
576
594
  if (action.status !== 'failed') continue;
@@ -583,6 +601,56 @@ export class WorkCenterService {
583
601
  }
584
602
  }
585
603
 
604
+ #queueDynamicCoordinatorWake(workItemId) {
605
+ if (this.shuttingDown || !this.coordinator || !workItemId
606
+ || this.dynamicCoordinatorTasks.has(workItemId)) return;
607
+ this.dynamicCoordinatorTasks.set(workItemId, null);
608
+ queueMicrotask(() => {
609
+ if (this.dynamicCoordinatorTasks.get(workItemId) === null) {
610
+ this.dynamicCoordinatorTasks.delete(workItemId);
611
+ this.#drainDynamicCoordinatorWakes(workItemId);
612
+ }
613
+ });
614
+ }
615
+
616
+ #scanDynamicCoordinatorWakes() {
617
+ if (this.shuttingDown || !this.coordinator) return;
618
+ for (const entry of this.store.listPendingDynamicCoordinatorWakes()) {
619
+ this.#queueDynamicCoordinatorWake(entry.workItemId);
620
+ }
621
+ }
622
+
623
+ #drainDynamicCoordinatorWakes(workItemId) {
624
+ if (this.shuttingDown || !this.coordinator || this.dynamicCoordinatorTasks.has(workItemId)) return null;
625
+ const entries = this.store.listPendingDynamicCoordinatorWakes()
626
+ .filter(candidate => candidate.workItemId === workItemId);
627
+ const entry = entries.find(candidate => candidate.payload?.turnId) || entries[0];
628
+ if (!entry) return null;
629
+ const detail = this.store.getWorkItemDetail(workItemId);
630
+ if (detail?.actions?.some(action => action.status === 'running')) return null;
631
+ let turn;
632
+ try {
633
+ turn = this.coordinator.advance(entry.id, {
634
+ workItemId,
635
+ onUpdate: (type, workItem) => this.#emit({ type, workItem }),
636
+ });
637
+ } catch (error) {
638
+ this.#emit({
639
+ type: 'coordinator.advance_schedule_failed',
640
+ workItem: this.store.getWorkItemDetail(workItemId),
641
+ error: error?.message || String(error),
642
+ });
643
+ return null;
644
+ }
645
+ if (!turn) return null;
646
+ const task = turn.task.finally(() => {
647
+ this.dynamicCoordinatorTasks.delete(workItemId);
648
+ });
649
+ this.dynamicCoordinatorTasks.set(workItemId, task);
650
+ task.catch(() => {});
651
+ return task;
652
+ }
653
+
586
654
  #recoveryKey(entry) {
587
655
  return `${entry.workItemId}:${entry.actionId}:${entry.actionGeneration}`;
588
656
  }
@@ -621,6 +689,7 @@ export class WorkCenterService {
621
689
 
622
690
  #scanRecoveries() {
623
691
  this.#scanCoordinatorProviderRecoveries();
692
+ this.#scanDynamicCoordinatorWakes();
624
693
  this.#scanFailureRecoveries();
625
694
  }
626
695
 
@@ -701,8 +770,12 @@ export class WorkCenterService {
701
770
  if (this.recoveryTimer) clearInterval(this.recoveryTimer);
702
771
  this.recoveryTimer = null;
703
772
  await this.coordinator?.shutdown?.();
704
- await Promise.allSettled([...this.recoveryTasks.values()]);
773
+ await Promise.allSettled([
774
+ ...this.recoveryTasks.values(),
775
+ ...[...this.dynamicCoordinatorTasks.values()].filter(Boolean),
776
+ ]);
705
777
  this.recoveryTasks.clear();
778
+ this.dynamicCoordinatorTasks.clear();
706
779
  this.recoveryQueue.clear();
707
780
  await this.watcher.stop();
708
781
  try { await this.watcher.runner?.shutdown?.(); } catch {}