@yeaft/webchat-agent 1.0.560 → 1.0.564
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 +195 -218
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/yeaft/web-bridge.js +13 -12
- package/yeaft/work-center/controller.js +13 -0
- package/yeaft/work-center/durable-model.js +16 -0
- package/yeaft/work-center/projection.js +3 -0
- package/yeaft/work-center/service.js +28 -1
- package/yeaft/work-center/store.js +60 -5
|
Binary file
|
package/package.json
CHANGED
package/yeaft/web-bridge.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { delimiter, join } from 'node:path';
|
|
19
19
|
import { COLLAB_TOOL_POLICY } from './tools/registry.js';
|
|
20
|
+
import { createFullRegistry } from './tools/index.js';
|
|
20
21
|
import { existsSync, lstatSync } from 'node:fs';
|
|
21
22
|
import { randomUUID } from 'node:crypto';
|
|
22
23
|
import { DEFAULT_YEAFT_DIR } from './init.js';
|
|
@@ -3360,9 +3361,15 @@ function resolvePluginCatalogRuntime(workDir = '') {
|
|
|
3360
3361
|
const active = runtimeBelongsToOwner(runtime, owner)
|
|
3361
3362
|
? runtime
|
|
3362
3363
|
: (baseRuntime && runtimeBelongsToOwner(baseRuntime, owner) ? baseRuntime : null);
|
|
3364
|
+
const yeaftDir = ctx.CONFIG?.yeaftDir || session?.yeaftDir || DEFAULT_YEAFT_DIR;
|
|
3365
|
+
// Inventory belongs to the Agent, not an initialized inference runtime.
|
|
3366
|
+
// Read Skills afresh for the requested scope (empty means Agent-only), so a
|
|
3367
|
+
// cold/unvisited project never inherits another Session's Skills. Do not boot
|
|
3368
|
+
// a Session, load a provider, or connect MCP just to inspect capabilities.
|
|
3363
3369
|
return {
|
|
3364
|
-
toolRegistry:
|
|
3365
|
-
skillManager:
|
|
3370
|
+
toolRegistry: createFullRegistry(),
|
|
3371
|
+
skillManager: createSkillManager(yeaftDir, normalizedWorkDir),
|
|
3372
|
+
mcpConfig: loadPluginCatalogMcpConfig(yeaftDir),
|
|
3366
3373
|
mcpManager: active?.mcpManager || session?.mcpManager || null,
|
|
3367
3374
|
};
|
|
3368
3375
|
}
|
|
@@ -3439,11 +3446,8 @@ function reloadManagedSkillRuntime(scope, workDir = '') {
|
|
|
3439
3446
|
if (scope === 'user') reloadActiveSkills(owner);
|
|
3440
3447
|
}
|
|
3441
3448
|
|
|
3442
|
-
function managedSkillCatalog(
|
|
3443
|
-
|
|
3444
|
-
const runtime = resolvePluginCatalogRuntime(normalizedWorkDir);
|
|
3445
|
-
const manager = createSkillManager(yeaftDir, normalizedWorkDir || process.cwd());
|
|
3446
|
-
return buildPluginCatalog({ ...runtime, skillManager: manager });
|
|
3449
|
+
function managedSkillCatalog(workDir = '') {
|
|
3450
|
+
return buildPluginCatalog(resolvePluginCatalogRuntime(workDir));
|
|
3447
3451
|
}
|
|
3448
3452
|
|
|
3449
3453
|
/**
|
|
@@ -3479,7 +3483,7 @@ export function handleYeaftManagedSkill(msg = {}) {
|
|
|
3479
3483
|
? createManagedSkill(join(yeaftDir, 'skills'), msg.skill || {})
|
|
3480
3484
|
: removeManagedSkill(join(yeaftDir, 'skills'), msg.name));
|
|
3481
3485
|
reloadManagedSkillRuntime(scope, managedSession?.workDir || '');
|
|
3482
|
-
const catalog = managedSkillCatalog(
|
|
3486
|
+
const catalog = managedSkillCatalog(managedSession?.workDir || '');
|
|
3483
3487
|
respond({ scope, sessionId: managedSession?.sessionId || null, result, catalog, error: null });
|
|
3484
3488
|
} catch (err) {
|
|
3485
3489
|
respond({ scope, sessionId: null, catalog: { tools: [], skills: [], skillSources: [], mcpServers: [] }, error: err?.message || String(err) });
|
|
@@ -3495,10 +3499,7 @@ export function handleYeaftPluginCatalog(msg = {}) {
|
|
|
3495
3499
|
const requestId = msg.requestId || null;
|
|
3496
3500
|
const requestedWorkDir = normalizeSessionWorkDir(msg.workDir);
|
|
3497
3501
|
try {
|
|
3498
|
-
const
|
|
3499
|
-
const yeaftDir = ctx.CONFIG?.yeaftDir || session?.yeaftDir || DEFAULT_YEAFT_DIR;
|
|
3500
|
-
runtime.mcpConfig = loadPluginCatalogMcpConfig(yeaftDir);
|
|
3501
|
-
const catalog = buildPluginCatalog(runtime);
|
|
3502
|
+
const catalog = buildPluginCatalog(resolvePluginCatalogRuntime(requestedWorkDir));
|
|
3502
3503
|
sendToServer({
|
|
3503
3504
|
type: 'yeaft_plugin_catalog_result',
|
|
3504
3505
|
requestId,
|
|
@@ -164,6 +164,19 @@ export class WorkflowController {
|
|
|
164
164
|
return detail;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
startScheduled(id, scheduledAt) {
|
|
168
|
+
return this.store.startWorkItemAtomic(id, workItem => {
|
|
169
|
+
const action = initialActionFor(workItem);
|
|
170
|
+
if (workItem.reuseMemory === false) return action;
|
|
171
|
+
const context = this.store.getReusableContext(workItem.workDir, workItem.id);
|
|
172
|
+
return {
|
|
173
|
+
...action,
|
|
174
|
+
context,
|
|
175
|
+
instruction: actionInstruction(action, workItem, context, renderSessionContextSnapshot(workItem.sessionContext)),
|
|
176
|
+
};
|
|
177
|
+
}, { scheduledAt });
|
|
178
|
+
}
|
|
179
|
+
|
|
167
180
|
update(id, patch) {
|
|
168
181
|
const updated = this.store.updateWorkItemAtomic(id, patch, initialActionFor);
|
|
169
182
|
if (!updated) throw new Error(`WorkItem not found: ${id}`);
|
|
@@ -20,6 +20,7 @@ const MIGRATIONS = [
|
|
|
20
20
|
['37-run-acceptance-checks', migrateRunAcceptanceChecks],
|
|
21
21
|
['38-action-closure-and-outputs', migrateActionClosureAndOutputs],
|
|
22
22
|
['39-action-creation-source', migrateActionCreationSource],
|
|
23
|
+
['40-work-item-schedules', migrateWorkItemSchedules],
|
|
23
24
|
];
|
|
24
25
|
|
|
25
26
|
const MIGRATION_ALIASES = new Map([
|
|
@@ -566,6 +567,21 @@ function migrateActionClosureAndOutputs(db) {
|
|
|
566
567
|
`);
|
|
567
568
|
}
|
|
568
569
|
|
|
570
|
+
function migrateWorkItemSchedules(db) {
|
|
571
|
+
for (const [column, definition] of [
|
|
572
|
+
['schedule_status', 'TEXT'],
|
|
573
|
+
['scheduled_for', 'INTEGER'],
|
|
574
|
+
['schedule_triggered_at', 'INTEGER'],
|
|
575
|
+
]) {
|
|
576
|
+
if (!hasColumn(db, 'work_items', column)) {
|
|
577
|
+
db.exec(`ALTER TABLE work_items ADD COLUMN ${column} ${definition}`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_work_items_schedule_due
|
|
581
|
+
ON work_items(schedule_status, scheduled_for)
|
|
582
|
+
WHERE schedule_status = 'scheduled'`);
|
|
583
|
+
}
|
|
584
|
+
|
|
569
585
|
function migrateActionCreationSource(db) {
|
|
570
586
|
if (!hasColumn(db, 'actions', 'creation_source')) {
|
|
571
587
|
db.exec("ALTER TABLE actions ADD COLUMN creation_source TEXT NOT NULL DEFAULT 'legacy'");
|
|
@@ -1094,6 +1094,7 @@ export function projectWorkItemDetail(detail, options = {}) {
|
|
|
1094
1094
|
executionControl: detail.executionControl,
|
|
1095
1095
|
executionStats: combinedExecutionStats(detail),
|
|
1096
1096
|
reuseMemory: detail.reuseMemory !== false,
|
|
1097
|
+
schedule: detail.schedule || null,
|
|
1097
1098
|
deliveryTarget: ['response', 'workspace_files', 'pull_request', 'merge'].includes(detail.deliveryTarget)
|
|
1098
1099
|
? detail.deliveryTarget : null,
|
|
1099
1100
|
waitingReason: sanitizeDiagnosticText(waitingReason(detail), MAX_ACTION_DIAGNOSTIC_CHARS),
|
|
@@ -1204,6 +1205,7 @@ export function projectWorkItemSummary(detail) {
|
|
|
1204
1205
|
executionStats: combinedExecutionStats(detail),
|
|
1205
1206
|
executionControl: detail.executionControl,
|
|
1206
1207
|
origin: detail.origin?.sessionId ? { sessionId: detail.origin.sessionId } : null,
|
|
1208
|
+
schedule: detail.schedule || null,
|
|
1207
1209
|
linkedSessionIds: Array.isArray(detail.linkedSessionIds) ? detail.linkedSessionIds : [],
|
|
1208
1210
|
attachmentCount: Array.isArray(detail.attachments) ? detail.attachments.length : 0,
|
|
1209
1211
|
createdAt: detail.createdAt,
|
|
@@ -1244,6 +1246,7 @@ export function projectWorkItemSummary(detail) {
|
|
|
1244
1246
|
currentAction: projectCurrentActionSummary(action, projectedAction),
|
|
1245
1247
|
actionStats: projectActionStats(detail, null),
|
|
1246
1248
|
origin: detail.origin?.sessionId ? { sessionId: detail.origin.sessionId } : null,
|
|
1249
|
+
schedule: detail.schedule || null,
|
|
1247
1250
|
linkedSessionIds: Array.isArray(detail.linkedSessionIds) ? detail.linkedSessionIds : [],
|
|
1248
1251
|
attachmentCount: Array.isArray(detail.attachments) ? detail.attachments.length : 0,
|
|
1249
1252
|
createdAt: detail.createdAt,
|
|
@@ -97,6 +97,7 @@ export class WorkCenterService {
|
|
|
97
97
|
constructor(options) {
|
|
98
98
|
const yeaftDir = requiredString(options?.yeaftDir, 'yeaftDir');
|
|
99
99
|
this.yeaftDir = yeaftDir;
|
|
100
|
+
this.now = typeof options.now === 'function' ? options.now : () => Date.now();
|
|
100
101
|
this.settingsReader = options.settingsReader || readWorkCenterSettings;
|
|
101
102
|
this.settingsWriter = options.settingsWriter || writeWorkCenterSettings;
|
|
102
103
|
this.runtimeInfoProvider = typeof options.runtimeInfoProvider === 'function'
|
|
@@ -246,7 +247,13 @@ export class WorkCenterService {
|
|
|
246
247
|
root: this.attachmentRoot,
|
|
247
248
|
workItemId,
|
|
248
249
|
});
|
|
249
|
-
const
|
|
250
|
+
const scheduledFor = payload.scheduledFor == null || payload.scheduledFor === ''
|
|
251
|
+
? null : Number(payload.scheduledFor);
|
|
252
|
+
if (scheduledFor != null && (!Number.isSafeInteger(scheduledFor) || scheduledFor <= this.now())) {
|
|
253
|
+
throw new Error('scheduledFor must be in the future');
|
|
254
|
+
}
|
|
255
|
+
const shouldStart = scheduledFor == null
|
|
256
|
+
&& (payload.start === undefined ? settings.startImmediately : payload.start !== false);
|
|
250
257
|
const goal = requiredString(payload.goal, 'goal');
|
|
251
258
|
const explicitTitle = payload.titleSource !== 'coordinator_pending' && typeof payload.title === 'string'
|
|
252
259
|
? payload.title.trim() : '';
|
|
@@ -289,6 +296,10 @@ export class WorkCenterService {
|
|
|
289
296
|
? normalizeSessionContextSnapshot(payload.sessionContext)
|
|
290
297
|
: [],
|
|
291
298
|
attachments,
|
|
299
|
+
schedule: scheduledFor == null ? null : {
|
|
300
|
+
status: payload.scheduleEnabled === false ? 'paused' : 'scheduled',
|
|
301
|
+
scheduledFor,
|
|
302
|
+
},
|
|
292
303
|
start: false,
|
|
293
304
|
});
|
|
294
305
|
let detail = this.#requiredItem(workItemId);
|
|
@@ -313,6 +324,13 @@ export class WorkCenterService {
|
|
|
313
324
|
this.#emit({ type: 'work_item.updated', workItem: detail });
|
|
314
325
|
return detail;
|
|
315
326
|
}
|
|
327
|
+
case 'update_schedule': {
|
|
328
|
+
const id = requiredString(payload.id, 'id');
|
|
329
|
+
const detail = this.store.updateWorkItemSchedule(id, payload.schedule || payload);
|
|
330
|
+
if (!detail) throw new Error(`WorkItem not found: ${id}`);
|
|
331
|
+
this.#emit({ type: 'work_item.schedule_updated', workItem: detail });
|
|
332
|
+
return detail;
|
|
333
|
+
}
|
|
316
334
|
case 'start': {
|
|
317
335
|
const detail = this.controller.start(requiredString(payload.id, 'id'));
|
|
318
336
|
if (detail.coordinationMode === DYNAMIC_COORDINATION_MODE) {
|
|
@@ -715,7 +733,16 @@ export class WorkCenterService {
|
|
|
715
733
|
}
|
|
716
734
|
}
|
|
717
735
|
|
|
736
|
+
#scanSchedules() {
|
|
737
|
+
const now = this.now();
|
|
738
|
+
for (const id of this.store.listDueScheduledWorkItemIds(now)) {
|
|
739
|
+
const detail = this.controller.startScheduled(id, now);
|
|
740
|
+
if (detail) this.#emit({ type: 'work_item.schedule_triggered', workItem: detail });
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
718
744
|
#scanRecoveries() {
|
|
745
|
+
this.#scanSchedules();
|
|
719
746
|
this.#scanCoordinatorProviderRecoveries();
|
|
720
747
|
this.#scanDynamicCoordinatorWakes();
|
|
721
748
|
this.#scanFailureRecoveries();
|
|
@@ -112,6 +112,11 @@ function mapWorkItem(row) {
|
|
|
112
112
|
coordinationMode: row.coordination_mode || 'legacy',
|
|
113
113
|
finalResult: parseJson(row.final_result, null),
|
|
114
114
|
deliveryTarget: row.delivery_target || null,
|
|
115
|
+
schedule: row.schedule_status ? {
|
|
116
|
+
status: row.schedule_status,
|
|
117
|
+
scheduledFor: Number(row.scheduled_for) || null,
|
|
118
|
+
triggeredAt: Number(row.schedule_triggered_at) || null,
|
|
119
|
+
} : null,
|
|
115
120
|
title: row.title,
|
|
116
121
|
titleSource: row.title_source || 'explicit',
|
|
117
122
|
requirement: row.requirement ?? row.goal,
|
|
@@ -858,6 +863,9 @@ export class WorkItemStore {
|
|
|
858
863
|
coordination_mode TEXT NOT NULL DEFAULT 'legacy',
|
|
859
864
|
final_result TEXT,
|
|
860
865
|
delivery_target TEXT,
|
|
866
|
+
schedule_status TEXT,
|
|
867
|
+
scheduled_for INTEGER,
|
|
868
|
+
schedule_triggered_at INTEGER,
|
|
861
869
|
title TEXT NOT NULL,
|
|
862
870
|
title_source TEXT NOT NULL DEFAULT 'explicit',
|
|
863
871
|
requirement TEXT,
|
|
@@ -1022,6 +1030,16 @@ export class WorkItemStore {
|
|
|
1022
1030
|
|
|
1023
1031
|
// The feature shipped first as an unmerged PR, but keep the store tolerant
|
|
1024
1032
|
// of databases created by review builds.
|
|
1033
|
+
if (!hasColumn(this.db, 'work_items', 'schedule_status')) {
|
|
1034
|
+
this.db.exec('ALTER TABLE work_items ADD COLUMN schedule_status TEXT');
|
|
1035
|
+
}
|
|
1036
|
+
if (!hasColumn(this.db, 'work_items', 'scheduled_for')) {
|
|
1037
|
+
this.db.exec('ALTER TABLE work_items ADD COLUMN scheduled_for INTEGER');
|
|
1038
|
+
}
|
|
1039
|
+
if (!hasColumn(this.db, 'work_items', 'schedule_triggered_at')) {
|
|
1040
|
+
this.db.exec('ALTER TABLE work_items ADD COLUMN schedule_triggered_at INTEGER');
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1025
1043
|
if (!hasColumn(this.db, 'work_items', 'workspace_key')) {
|
|
1026
1044
|
withTransaction(this.db, () => {
|
|
1027
1045
|
this.db.exec("ALTER TABLE work_items ADD COLUMN workspace_key TEXT NOT NULL DEFAULT ''");
|
|
@@ -2407,14 +2425,16 @@ export class WorkItemStore {
|
|
|
2407
2425
|
const workspaceKey = canonicalWorkspaceKey(input.workDir);
|
|
2408
2426
|
this.db.prepare(`INSERT INTO work_items
|
|
2409
2427
|
(id, revision, execution_schema_version, ledger_revision, coordination_mode, final_result, delivery_target,
|
|
2410
|
-
title, title_source, requirement, goal, acceptance_criteria, workflow_template, workflow_snapshot, status,
|
|
2428
|
+
schedule_status, scheduled_for, schedule_triggered_at, title, title_source, requirement, goal, acceptance_criteria, workflow_template, workflow_snapshot, status,
|
|
2411
2429
|
current_action_id, current_run_id, work_dir, workspace_key, reuse_memory, origin, linked_session_ids,
|
|
2412
2430
|
session_context, attachments, created_at, updated_at)
|
|
2413
|
-
VALUES (?, 1, ?, 0, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(
|
|
2431
|
+
VALUES (?, 1, ?, 0, ?, NULL, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(
|
|
2414
2432
|
id,
|
|
2415
2433
|
Number.isInteger(input.executionSchemaVersion) ? input.executionSchemaVersion : 2,
|
|
2416
2434
|
input.coordinationMode || 'legacy',
|
|
2417
2435
|
input.deliveryTarget || null,
|
|
2436
|
+
input.schedule?.status || null,
|
|
2437
|
+
input.schedule?.scheduledFor || null,
|
|
2418
2438
|
input.title,
|
|
2419
2439
|
input.titleSource === 'coordinator_pending' ? 'coordinator_pending' : 'explicit',
|
|
2420
2440
|
input.goal,
|
|
@@ -3099,6 +3119,32 @@ export class WorkItemStore {
|
|
|
3099
3119
|
}));
|
|
3100
3120
|
}
|
|
3101
3121
|
|
|
3122
|
+
listDueScheduledWorkItemIds(now = this.now()) {
|
|
3123
|
+
return this.db.prepare(`SELECT id FROM work_items
|
|
3124
|
+
WHERE schedule_status = 'scheduled' AND scheduled_for <= ? AND status = 'draft'
|
|
3125
|
+
ORDER BY scheduled_for, id`).all(now).map(row => row.id);
|
|
3126
|
+
}
|
|
3127
|
+
|
|
3128
|
+
updateWorkItemSchedule(id, input = {}) {
|
|
3129
|
+
return withTransaction(this.db, () => {
|
|
3130
|
+
const row = this.db.prepare('SELECT * FROM work_items WHERE id = ?').get(id);
|
|
3131
|
+
if (!row) return null;
|
|
3132
|
+
if (row.status !== 'draft' || !['scheduled', 'paused'].includes(row.schedule_status)) {
|
|
3133
|
+
throw new Error('Only pending scheduled WorkItems can be changed');
|
|
3134
|
+
}
|
|
3135
|
+
const scheduledFor = input.scheduledFor == null ? Number(row.scheduled_for) : Number(input.scheduledFor);
|
|
3136
|
+
if (!Number.isSafeInteger(scheduledFor) || scheduledFor <= this.now()) {
|
|
3137
|
+
throw new Error('scheduledFor must be in the future');
|
|
3138
|
+
}
|
|
3139
|
+
const status = input.enabled === false ? 'paused' : 'scheduled';
|
|
3140
|
+
const now = this.now();
|
|
3141
|
+
this.db.prepare(`UPDATE work_items SET schedule_status = ?, scheduled_for = ?,
|
|
3142
|
+
revision = revision + 1, updated_at = ? WHERE id = ?`).run(status, scheduledFor, now, id);
|
|
3143
|
+
this.appendEvent(id, 'work_item.schedule_updated', { status, scheduledFor });
|
|
3144
|
+
return this.getWorkItemDetail(id);
|
|
3145
|
+
});
|
|
3146
|
+
}
|
|
3147
|
+
|
|
3102
3148
|
listWorkItems(filters = {}) {
|
|
3103
3149
|
const where = [];
|
|
3104
3150
|
const values = [];
|
|
@@ -4492,10 +4538,14 @@ export class WorkItemStore {
|
|
|
4492
4538
|
});
|
|
4493
4539
|
}
|
|
4494
4540
|
|
|
4495
|
-
startWorkItemAtomic(id, makeInitialAction) {
|
|
4541
|
+
startWorkItemAtomic(id, makeInitialAction, options = {}) {
|
|
4496
4542
|
return withTransaction(this.db, () => {
|
|
4497
4543
|
const workItem = this.getWorkItem(id);
|
|
4498
4544
|
if (!workItem) return null;
|
|
4545
|
+
const scheduledAt = Number(options.scheduledAt);
|
|
4546
|
+
const scheduledStart = Number.isSafeInteger(scheduledAt);
|
|
4547
|
+
if (scheduledStart && (workItem.status !== 'draft' || workItem.schedule?.status !== 'scheduled'
|
|
4548
|
+
|| workItem.schedule.scheduledFor > scheduledAt)) return null;
|
|
4499
4549
|
if (['done', 'cancelled'].includes(workItem.status)) {
|
|
4500
4550
|
throw new Error(`Cannot start WorkItem in ${workItem.status}`);
|
|
4501
4551
|
}
|
|
@@ -4505,13 +4555,18 @@ export class WorkItemStore {
|
|
|
4505
4555
|
throw new Error(`WorkItem in ${workItem.status} must be resumed with retry`);
|
|
4506
4556
|
}
|
|
4507
4557
|
const now = this.now();
|
|
4558
|
+
if (scheduledStart) {
|
|
4559
|
+
this.db.prepare(`UPDATE work_items SET schedule_status = 'triggered',
|
|
4560
|
+
schedule_triggered_at = ?, revision = revision + 1, updated_at = ? WHERE id = ?`)
|
|
4561
|
+
.run(scheduledAt, now, id);
|
|
4562
|
+
}
|
|
4508
4563
|
if (isDynamicWorkItem(workItem)) {
|
|
4509
4564
|
this.db.prepare(`UPDATE work_items SET status = 'running', current_action_id = NULL,
|
|
4510
4565
|
current_run_id = NULL, updated_at = ? WHERE id = ?`).run(now, id);
|
|
4511
4566
|
this.enqueueCoordinatorMailbox(id, 'work_item_started', {
|
|
4512
4567
|
trigger: { workItemId: id },
|
|
4513
4568
|
}, `dynamic:start:${id}:${workItem.revision}`);
|
|
4514
|
-
this.appendEvent(id, 'work_item.started');
|
|
4569
|
+
this.appendEvent(id, 'work_item.started', scheduledStart ? { scheduled: true } : {});
|
|
4515
4570
|
return this.getWorkItemDetail(id);
|
|
4516
4571
|
}
|
|
4517
4572
|
const action = this.#insertAction(id, {
|
|
@@ -4520,7 +4575,7 @@ export class WorkItemStore {
|
|
|
4520
4575
|
}, this.#nextSequence(id), now);
|
|
4521
4576
|
this.db.prepare(`UPDATE work_items SET status = 'ready', current_action_id = ?,
|
|
4522
4577
|
current_run_id = NULL, updated_at = ? WHERE id = ?`).run(action.id, now, id);
|
|
4523
|
-
this.appendEvent(id, 'work_item.started', {}, { actionId: action.id });
|
|
4578
|
+
this.appendEvent(id, 'work_item.started', scheduledStart ? { scheduled: true } : {}, { actionId: action.id });
|
|
4524
4579
|
return this.getWorkItemDetail(id);
|
|
4525
4580
|
});
|
|
4526
4581
|
}
|