@yeaft/webchat-agent 1.0.366 → 1.0.368
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/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +51 -51
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +1 -1
- package/package.json +1 -1
- package/yeaft/tools/create-work-item.js +8 -8
- package/yeaft/work-center/controller.js +12 -2
- package/yeaft/work-center/coordinator.js +156 -26
- package/yeaft/work-center/durable-model.js +51 -1
- package/yeaft/work-center/dynamic-coordination.js +243 -0
- package/yeaft/work-center/execution-mode.js +16 -0
- package/yeaft/work-center/mainline-projection.js +25 -13
- package/yeaft/work-center/projection.js +32 -9
- package/yeaft/work-center/runner.js +18 -13
- package/yeaft/work-center/service.js +86 -13
- package/yeaft/work-center/store.js +535 -60
|
@@ -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
|
|
231
|
-
|
|
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:
|
|
278
|
+
start: false,
|
|
278
279
|
});
|
|
279
|
-
|
|
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([
|
|
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 {}
|