@openchambery/web 1.19.3-beta.1 → 1.19.3-beta.3
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/dist/assets/{AgentAvatar-HJ-RYWko.js → AgentAvatar-CVTgW_rx.js} +1 -1
- package/dist/assets/{AssistantView-BYydnWD8.js → AssistantView-1swUVJya.js} +1 -1
- package/dist/assets/{ChatContainer-BY_TJzdJ.js → ChatContainer-ibiZostM.js} +4 -4
- package/dist/assets/{ContextPanel-B74zPN__.js → ContextPanel-DPKkdDGe.js} +3 -3
- package/dist/assets/{DiagramView-DRVlnenZ.js → DiagramView-CxoLhGvN.js} +1 -1
- package/dist/assets/{MarkdownRendererImpl-DxyIKpKt.js → MarkdownRendererImpl-l1QCa5IY.js} +1 -1
- package/dist/assets/{MobileDetailNavigation-CFD0y3to.js → MobileDetailNavigation-ua_R5hOw.js} +1 -1
- package/dist/assets/{MobileShareBridge-DSIXMEOA.js → MobileShareBridge-BOgPRlDg.js} +1 -1
- package/dist/assets/{MobileSurface-BPkqBzWy.js → MobileSurface-EK0jM2iR.js} +1 -1
- package/dist/assets/{MultiRunWindow-bGCiFiju.js → MultiRunWindow-BGAxl4yU.js} +1 -1
- package/dist/assets/{OnboardingScreen-yNfhXL7h.js → OnboardingScreen-CTNi2zuA.js} +1 -1
- package/dist/assets/{SettingsView-k41W8W9w.js → SettingsView-UHD02SWB.js} +1 -1
- package/dist/assets/{SettingsWindow-V9CVGFFs.js → SettingsWindow-CZipVPDU.js} +1 -1
- package/dist/assets/{TerminalView-CwPOcvvC.js → TerminalView-C1UZuv2-.js} +1 -1
- package/dist/assets/{ToolOutputDialog-Dm_5ZIji.js → ToolOutputDialog-DHx_Q0Vr.js} +1 -1
- package/dist/assets/{appThemeRegistry-DDdrS3FM.js → appThemeRegistry-D6FA_ZA0.js} +2 -2
- package/dist/assets/{clipboard-2rQvL28z.js → clipboard-BXC1vZwE.js} +1 -1
- package/dist/assets/{dialog-DnzdtCcc.js → dialog-wraJHZQ2.js} +1 -1
- package/dist/assets/{es-BOiTSQ5G.js → es-DQqFAaWT.js} +1 -1
- package/dist/assets/{fr-C5pZX4Yb.js → fr-CNfGRpc1.js} +1 -1
- package/dist/assets/{gitApi-BKNMDT1C.js → gitApi-BxqwY1Ib.js} +1 -1
- package/dist/assets/{insertionBoundaries-CpK1dYQb.js → insertionBoundaries-DexnTazC.js} +1 -1
- package/dist/assets/{ja-80U-a23j.js → ja-CQqehnVn.js} +1 -1
- package/dist/assets/{ko-tcJcPnGS.js → ko-kmwi8UJD.js} +1 -1
- package/dist/assets/{main-BnG6Equj.js → main-BgH88NFT.js} +3 -3
- package/dist/assets/{main-DnRyoiLh.js → main-Dsi8GlJE.js} +2 -2
- package/dist/assets/{miniChat-B4adskso.js → miniChat-BoUvYKIn.js} +2 -2
- package/dist/assets/{mobile-C7W32Ulc.js → mobile-C9pZYY0E.js} +2 -2
- package/dist/assets/{multirun-DFuwoLYJ.js → multirun-BZqzMl-w.js} +1 -1
- package/dist/assets/{pl-CIRIRP8a.js → pl-Blv5o2QA.js} +1 -1
- package/dist/assets/{projectMeta-CZ2Mroqq.js → projectMeta-Csrlj84N.js} +1 -1
- package/dist/assets/{pt-BR-yXSOtWjW.js → pt-BR-BK74t-Bd.js} +1 -1
- package/dist/assets/{radio-B00bPKTq.js → radio-ZySQUxed.js} +1 -1
- package/dist/assets/{renderElectronMiniChatApp-gv_G3VwH.js → renderElectronMiniChatApp-Cf6iV1ge.js} +2 -2
- package/dist/assets/{renderMobileApp-182C48yO.js → renderMobileApp-BhrVDiRk.js} +4 -4
- package/dist/assets/{runtimeConfig-UeQ6crFG.js → runtimeConfig-DnLjqksJ.js} +1 -1
- package/dist/assets/{runtimeEndpointReset-BWsr-TZy.js → runtimeEndpointReset-C_w3Pob3.js} +2 -2
- package/dist/assets/{shikiHighlight-U6tsqA4k.js → shikiHighlight-3KCstW2R.js} +1 -1
- package/dist/assets/{sortable-tabs-strip-CmUWXOHX.js → sortable-tabs-strip-BdbKDDAP.js} +1 -1
- package/dist/assets/src-eGuNWxHc.css +2 -0
- package/dist/assets/{store-NWfuTiFD.js → store-UweBeN2y.js} +2 -2
- package/dist/assets/{uk-Do9LnhN3.js → uk-6I-KXy3R.js} +1 -1
- package/dist/assets/{useAppFontEffects-BCqJlAQn.js → useAppFontEffects-D7icrngj.js} +2 -2
- package/dist/assets/{useDesktopWindowControlsLayout-UxYwZF05.js → useDesktopWindowControlsLayout-F8s373xi.js} +1 -1
- package/dist/assets/{useEffectiveDirectory-BDSaXwNC.js → useEffectiveDirectory-60L8iJVV.js} +1 -1
- package/dist/assets/{useMobileNavigationStore-D0Pwjlff.js → useMobileNavigationStore-B1J6-RtX.js} +4 -4
- package/dist/assets/{useSessionAutoCleanup-DDPv1Yp8.js → useSessionAutoCleanup-Cn1MblZ3.js} +1 -1
- package/dist/assets/{useWorkerHighlightedLines-C09Uo5ZH.js → useWorkerHighlightedLines-DyhaC2bi.js} +1 -1
- package/dist/assets/{zh-CN-BAZNUvn0.js → zh-CN-DwlzS4yK.js} +1 -1
- package/dist/assets/{zh-TW-BQsPCZWH.js → zh-TW-p0VriyfT.js} +1 -1
- package/dist/index.html +3 -3
- package/dist/mini-chat.html +3 -3
- package/dist/mobile.html +3 -3
- package/package.json +1 -1
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +16 -14
- package/server/lib/scheduled-tasks/runtime.js +59 -22
- package/server/lib/scheduled-tasks/runtime.test.js +87 -0
- package/dist/assets/src-BM9HiQZr.css +0 -2
package/dist/index.html
CHANGED
|
@@ -552,7 +552,7 @@
|
|
|
552
552
|
opacity: 0.72;
|
|
553
553
|
}
|
|
554
554
|
</style>
|
|
555
|
-
<script type="module" crossorigin src="/assets/main-
|
|
555
|
+
<script type="module" crossorigin src="/assets/main-Dsi8GlJE.js"></script>
|
|
556
556
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
|
|
557
557
|
<link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
|
|
558
558
|
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
|
|
@@ -569,8 +569,8 @@
|
|
|
569
569
|
<link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
|
|
570
570
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
571
571
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
|
|
572
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
573
|
-
<link rel="stylesheet" crossorigin href="/assets/src-
|
|
572
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-DnLjqksJ.js">
|
|
573
|
+
<link rel="stylesheet" crossorigin href="/assets/src-eGuNWxHc.css">
|
|
574
574
|
</head>
|
|
575
575
|
<body class="h-full bg-background text-foreground">
|
|
576
576
|
<!-- Keep the startup layer outside #root. React replaces every child of
|
package/dist/mini-chat.html
CHANGED
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
}
|
|
75
75
|
</style>
|
|
76
76
|
|
|
77
|
-
<script type="module" crossorigin src="/assets/miniChat-
|
|
77
|
+
<script type="module" crossorigin src="/assets/miniChat-BoUvYKIn.js"></script>
|
|
78
78
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
|
|
79
79
|
<link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
|
|
80
80
|
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
|
|
@@ -91,8 +91,8 @@
|
|
|
91
91
|
<link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
|
|
92
92
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
93
93
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
|
|
94
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
95
|
-
<link rel="stylesheet" crossorigin href="/assets/src-
|
|
94
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-DnLjqksJ.js">
|
|
95
|
+
<link rel="stylesheet" crossorigin href="/assets/src-eGuNWxHc.css">
|
|
96
96
|
</head>
|
|
97
97
|
<body class="h-full bg-background text-foreground">
|
|
98
98
|
<div id="root" class="h-full">
|
package/dist/mobile.html
CHANGED
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
})();
|
|
64
64
|
</script>
|
|
65
65
|
|
|
66
|
-
<script type="module" crossorigin src="/assets/mobile-
|
|
66
|
+
<script type="module" crossorigin src="/assets/mobile-C9pZYY0E.js"></script>
|
|
67
67
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
|
|
68
68
|
<link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
|
|
69
69
|
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
<link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
|
|
81
81
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
82
82
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
|
|
83
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
84
|
-
<link rel="stylesheet" crossorigin href="/assets/src-
|
|
83
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-DnLjqksJ.js">
|
|
84
|
+
<link rel="stylesheet" crossorigin href="/assets/src-eGuNWxHc.css">
|
|
85
85
|
</head>
|
|
86
86
|
<body class="h-full bg-background text-foreground">
|
|
87
87
|
<div id="root" class="h-full"></div>
|
package/package.json
CHANGED
|
@@ -223,22 +223,24 @@ SSE events keep the existing `openchamber:scheduled-task-ran` shape.
|
|
|
223
223
|
|
|
224
224
|
## Post-run continuation
|
|
225
225
|
|
|
226
|
-
After a run has finalized
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
-
|
|
233
|
-
|
|
226
|
+
After a run has finalized with a session, the user may continue chatting in
|
|
227
|
+
that same history session. `observeSessionEvent` corrects **only task state**
|
|
228
|
+
(history rows are not rewritten, `finishRun` is not called again):
|
|
229
|
+
|
|
230
|
+
- `session.status` busy/retry while `lastStatus` is `error` or `success` →
|
|
231
|
+
`lastStatus` `running`, clear `lastError`, keep/set `lastSessionId`
|
|
232
|
+
- later `session.idle` (or idle `session.status`) with a success snapshot →
|
|
233
|
+
`lastStatus` `success`
|
|
234
234
|
- do not change `lastRunAt` / `lastDurationMs` / `nextRunAt`
|
|
235
|
-
- do not call `finishRun` again (the history row stays the original run snapshot)
|
|
236
235
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
is
|
|
240
|
-
|
|
241
|
-
|
|
236
|
+
A live run (`runningTaskKeys`) owns settlement; the observer is a no-op while
|
|
237
|
+
the task is in `runningTaskKeys`. Idle correction is a no-op if `lastStatus`
|
|
238
|
+
is already `success`, or if the snapshot is still error/busy/unknown. Observer
|
|
239
|
+
failures are logged and must not throw out of the event bus.
|
|
240
|
+
|
|
241
|
+
Run start also emits `scheduled-task-ran` with `status: running` as soon as
|
|
242
|
+
task state is persisted, so the outer task list can show in-progress before
|
|
243
|
+
the session is attached.
|
|
242
244
|
|
|
243
245
|
## Watchdog cancel / upstream abort
|
|
244
246
|
|
|
@@ -390,21 +390,26 @@ const snapshotSessionOutcome = async ({
|
|
|
390
390
|
return { outcome: 'busy' };
|
|
391
391
|
};
|
|
392
392
|
|
|
393
|
-
const
|
|
393
|
+
const parseSessionEventPhase = (event) => {
|
|
394
394
|
const payload = event?.payload?.payload ?? event?.payload;
|
|
395
395
|
const properties = payload?.properties;
|
|
396
396
|
const sessionID = typeof properties?.sessionID === 'string' ? properties.sessionID : '';
|
|
397
397
|
if (!sessionID) {
|
|
398
|
-
return {
|
|
398
|
+
return { phase: '', sessionID: '' };
|
|
399
399
|
}
|
|
400
400
|
if (payload?.type === 'session.idle') {
|
|
401
|
-
return {
|
|
401
|
+
return { phase: 'idle', sessionID };
|
|
402
402
|
}
|
|
403
403
|
if (payload?.type === 'session.status') {
|
|
404
404
|
const type = properties?.status?.type ?? properties?.info?.type;
|
|
405
|
-
|
|
405
|
+
if (type === 'idle') {
|
|
406
|
+
return { phase: 'idle', sessionID };
|
|
407
|
+
}
|
|
408
|
+
if (type === 'busy' || type === 'retry') {
|
|
409
|
+
return { phase: 'busy', sessionID };
|
|
410
|
+
}
|
|
406
411
|
}
|
|
407
|
-
return {
|
|
412
|
+
return { phase: '', sessionID };
|
|
408
413
|
};
|
|
409
414
|
|
|
410
415
|
export const createScheduledTasksRuntime = (deps) => {
|
|
@@ -1291,6 +1296,15 @@ export const createScheduledTasksRuntime = (deps) => {
|
|
|
1291
1296
|
if (runningState.task) {
|
|
1292
1297
|
updateInMemoryTask(projectID, runningState.task);
|
|
1293
1298
|
}
|
|
1299
|
+
try {
|
|
1300
|
+
emitTaskRunEvent?.({
|
|
1301
|
+
projectID,
|
|
1302
|
+
taskID,
|
|
1303
|
+
ranAt: runStartedAt,
|
|
1304
|
+
status: 'running',
|
|
1305
|
+
});
|
|
1306
|
+
} catch {
|
|
1307
|
+
}
|
|
1294
1308
|
} catch (error) {
|
|
1295
1309
|
errorMessage = safeErrorMessage(error);
|
|
1296
1310
|
logger.warn?.('[ScheduledTasks] failed to persist running state', {
|
|
@@ -1586,9 +1600,22 @@ export const createScheduledTasksRuntime = (deps) => {
|
|
|
1586
1600
|
};
|
|
1587
1601
|
};
|
|
1588
1602
|
|
|
1589
|
-
const
|
|
1590
|
-
|
|
1591
|
-
|
|
1603
|
+
const emitContinuationStatus = (projectID, taskID, sessionID, status) => {
|
|
1604
|
+
try {
|
|
1605
|
+
emitTaskRunEvent?.({
|
|
1606
|
+
projectID,
|
|
1607
|
+
taskID,
|
|
1608
|
+
ranAt: Date.now(),
|
|
1609
|
+
status,
|
|
1610
|
+
sessionID,
|
|
1611
|
+
});
|
|
1612
|
+
} catch {
|
|
1613
|
+
}
|
|
1614
|
+
};
|
|
1615
|
+
|
|
1616
|
+
const handleSessionContinuation = async (event) => {
|
|
1617
|
+
const { phase, sessionID } = parseSessionEventPhase(event);
|
|
1618
|
+
if (!phase || !sessionID) {
|
|
1592
1619
|
return;
|
|
1593
1620
|
}
|
|
1594
1621
|
|
|
@@ -1605,10 +1632,28 @@ export const createScheduledTasksRuntime = (deps) => {
|
|
|
1605
1632
|
|
|
1606
1633
|
const task = tasksByProject.get(projectID)?.get(taskID);
|
|
1607
1634
|
const lastStatus = task?.state?.lastStatus;
|
|
1608
|
-
|
|
1635
|
+
|
|
1636
|
+
if (phase === 'busy') {
|
|
1637
|
+
if (lastStatus !== 'error' && lastStatus !== 'success') {
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
const stateResult = await projectConfigRuntime.updateScheduledTaskState(projectID, taskID, {
|
|
1641
|
+
lastStatus: 'running',
|
|
1642
|
+
lastError: undefined,
|
|
1643
|
+
lastSessionId: sessionID,
|
|
1644
|
+
updatedAt: Date.now(),
|
|
1645
|
+
});
|
|
1646
|
+
if (stateResult.task) {
|
|
1647
|
+
updateInMemoryTask(projectID, stateResult.task);
|
|
1648
|
+
}
|
|
1649
|
+
emitContinuationStatus(projectID, taskID, sessionID, 'running');
|
|
1609
1650
|
return;
|
|
1610
1651
|
}
|
|
1611
|
-
|
|
1652
|
+
|
|
1653
|
+
if (lastStatus === 'success') {
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
if (lastStatus !== 'error' && lastStatus !== 'running') {
|
|
1612
1657
|
return;
|
|
1613
1658
|
}
|
|
1614
1659
|
|
|
@@ -1637,7 +1682,8 @@ export const createScheduledTasksRuntime = (deps) => {
|
|
|
1637
1682
|
return;
|
|
1638
1683
|
}
|
|
1639
1684
|
const latest = tasksByProject.get(projectID)?.get(taskID);
|
|
1640
|
-
|
|
1685
|
+
const latestStatus = latest?.state?.lastStatus;
|
|
1686
|
+
if (latestStatus !== 'error' && latestStatus !== 'running') {
|
|
1641
1687
|
return;
|
|
1642
1688
|
}
|
|
1643
1689
|
|
|
@@ -1651,20 +1697,11 @@ export const createScheduledTasksRuntime = (deps) => {
|
|
|
1651
1697
|
updateInMemoryTask(projectID, stateResult.task);
|
|
1652
1698
|
}
|
|
1653
1699
|
|
|
1654
|
-
|
|
1655
|
-
emitTaskRunEvent?.({
|
|
1656
|
-
projectID,
|
|
1657
|
-
taskID,
|
|
1658
|
-
ranAt: Date.now(),
|
|
1659
|
-
status: 'success',
|
|
1660
|
-
sessionID,
|
|
1661
|
-
});
|
|
1662
|
-
} catch {
|
|
1663
|
-
}
|
|
1700
|
+
emitContinuationStatus(projectID, taskID, sessionID, 'success');
|
|
1664
1701
|
};
|
|
1665
1702
|
|
|
1666
1703
|
const observeSessionEvent = (event) => {
|
|
1667
|
-
return
|
|
1704
|
+
return handleSessionContinuation(event).catch((error) => {
|
|
1668
1705
|
logger.warn?.('[ScheduledTasks] observeSessionEvent failed', {
|
|
1669
1706
|
error: safeErrorMessage(error),
|
|
1670
1707
|
});
|
|
@@ -1039,6 +1039,79 @@ describe('scheduled-tasks run history and session lifecycle', () => {
|
|
|
1039
1039
|
expect(Object.hasOwn(finalPatch, 'lastSessionId')).toBe(false);
|
|
1040
1040
|
});
|
|
1041
1041
|
|
|
1042
|
+
it('marks lastStatus running when a history session continues, then success on idle', async () => {
|
|
1043
|
+
const history = createHistoryStore();
|
|
1044
|
+
const client = createSuccessfulClient({ settlement: 'assistant-error' });
|
|
1045
|
+
const fetchMock = vi.fn(async (url) => {
|
|
1046
|
+
if (String(url).includes('prompt_async')) {
|
|
1047
|
+
return { ok: true, text: async () => '' };
|
|
1048
|
+
}
|
|
1049
|
+
throw new Error(`unexpected fetch ${url}`);
|
|
1050
|
+
});
|
|
1051
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
1052
|
+
|
|
1053
|
+
const emitTaskRunEvent = vi.fn();
|
|
1054
|
+
const updateScheduledTaskState = vi.fn(async (_projectID, _taskID, state) => ({
|
|
1055
|
+
task: { ...scheduledTask, state: { ...scheduledTask.state, ...state } },
|
|
1056
|
+
}));
|
|
1057
|
+
const runtime = createRuntime(updateScheduledTaskState, {
|
|
1058
|
+
runHistoryStore: history,
|
|
1059
|
+
waitForOpenCodeReady: vi.fn(async () => {}),
|
|
1060
|
+
emitTaskRunEvent,
|
|
1061
|
+
});
|
|
1062
|
+
await runtime.syncProject('project-1');
|
|
1063
|
+
const result = await runtime.runNow('project-1', 'task-1');
|
|
1064
|
+
expect(result.status).toBe('error');
|
|
1065
|
+
expect(history.finishRun).toHaveBeenCalledTimes(1);
|
|
1066
|
+
|
|
1067
|
+
await runtime.observeSessionEvent({
|
|
1068
|
+
payload: {
|
|
1069
|
+
type: 'session.status',
|
|
1070
|
+
properties: { sessionID: 'ses_1', status: { type: 'busy' } },
|
|
1071
|
+
},
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
expect(history.finishRun).toHaveBeenCalledTimes(1);
|
|
1075
|
+
const runningPatch = updateScheduledTaskState.mock.calls.at(-1)[2];
|
|
1076
|
+
expect(runningPatch).toEqual(expect.objectContaining({
|
|
1077
|
+
lastStatus: 'running',
|
|
1078
|
+
lastError: undefined,
|
|
1079
|
+
lastSessionId: 'ses_1',
|
|
1080
|
+
}));
|
|
1081
|
+
expect(Object.hasOwn(runningPatch, 'lastRunAt')).toBe(false);
|
|
1082
|
+
expect(emitTaskRunEvent).toHaveBeenCalledWith(expect.objectContaining({
|
|
1083
|
+
projectID: 'project-1',
|
|
1084
|
+
taskID: 'task-1',
|
|
1085
|
+
status: 'running',
|
|
1086
|
+
sessionID: 'ses_1',
|
|
1087
|
+
}));
|
|
1088
|
+
|
|
1089
|
+
client.messages.mockImplementation(async () => ({
|
|
1090
|
+
data: [{
|
|
1091
|
+
info: {
|
|
1092
|
+
id: 'msg_ok',
|
|
1093
|
+
role: 'assistant',
|
|
1094
|
+
time: { completed: Date.now() },
|
|
1095
|
+
},
|
|
1096
|
+
}],
|
|
1097
|
+
}));
|
|
1098
|
+
|
|
1099
|
+
await runtime.observeSessionEvent({
|
|
1100
|
+
payload: {
|
|
1101
|
+
type: 'session.idle',
|
|
1102
|
+
properties: { sessionID: 'ses_1' },
|
|
1103
|
+
},
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
expect(updateScheduledTaskState.mock.calls.at(-1)[2]).toEqual(expect.objectContaining({
|
|
1107
|
+
lastStatus: 'success',
|
|
1108
|
+
lastSessionId: 'ses_1',
|
|
1109
|
+
}));
|
|
1110
|
+
expect(history.finishRun).toHaveBeenCalledTimes(1);
|
|
1111
|
+
|
|
1112
|
+
vi.unstubAllGlobals();
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1042
1115
|
it('corrects lastStatus error → success on idle after a continuation, without rewriting history', async () => {
|
|
1043
1116
|
const history = createHistoryStore();
|
|
1044
1117
|
const client = createSuccessfulClient({ settlement: 'assistant-error' });
|
|
@@ -1143,6 +1216,15 @@ describe('scheduled-tasks run history and session lifecycle', () => {
|
|
|
1143
1216
|
expect(history.finishRun).not.toHaveBeenCalled();
|
|
1144
1217
|
|
|
1145
1218
|
const successEmitsBefore = emitTaskRunEvent.mock.calls.filter((call) => call[0].status === 'success').length;
|
|
1219
|
+
const continuationRunningBefore = updateScheduledTaskState.mock.calls.filter((call) => (
|
|
1220
|
+
call[2].lastStatus === 'running' && !Object.hasOwn(call[2], 'lastRunAt')
|
|
1221
|
+
)).length;
|
|
1222
|
+
await runtime.observeSessionEvent({
|
|
1223
|
+
payload: {
|
|
1224
|
+
type: 'session.status',
|
|
1225
|
+
properties: { sessionID: 'ses_1', status: { type: 'busy' } },
|
|
1226
|
+
},
|
|
1227
|
+
});
|
|
1146
1228
|
await runtime.observeSessionEvent({
|
|
1147
1229
|
payload: {
|
|
1148
1230
|
type: 'session.idle',
|
|
@@ -1152,6 +1234,9 @@ describe('scheduled-tasks run history and session lifecycle', () => {
|
|
|
1152
1234
|
expect(history.finishRun).not.toHaveBeenCalled();
|
|
1153
1235
|
expect(updateScheduledTaskState.mock.calls.map((call) => call[2].lastStatus)).not.toContain('success');
|
|
1154
1236
|
expect(emitTaskRunEvent.mock.calls.filter((call) => call[0].status === 'success')).toHaveLength(successEmitsBefore);
|
|
1237
|
+
expect(updateScheduledTaskState.mock.calls.filter((call) => (
|
|
1238
|
+
call[2].lastStatus === 'running' && !Object.hasOwn(call[2], 'lastRunAt')
|
|
1239
|
+
))).toHaveLength(continuationRunningBefore);
|
|
1155
1240
|
|
|
1156
1241
|
await vi.advanceTimersByTimeAsync(1_000);
|
|
1157
1242
|
const result = await runPromise;
|
|
@@ -1183,7 +1268,9 @@ describe('scheduled-tasks run history and session lifecycle', () => {
|
|
|
1183
1268
|
});
|
|
1184
1269
|
await runtime.syncProject('project-1');
|
|
1185
1270
|
const result = await runtime.runNow('project-1', 'task-1');
|
|
1271
|
+
|
|
1186
1272
|
expect(result.ok).toBe(true);
|
|
1273
|
+
expect(emitTaskRunEvent.mock.calls.map((call) => call[0].status)[0]).toBe('running');
|
|
1187
1274
|
expect(history.finishRun).toHaveBeenCalledTimes(1);
|
|
1188
1275
|
const stateWritesAfterRun = updateScheduledTaskState.mock.calls.length;
|
|
1189
1276
|
const successEmitsAfterRun = emitTaskRunEvent.mock.calls.filter((call) => call[0].status === 'success').length;
|