@linxin666/dsh-client-ui-task-board 0.4.5 → 0.4.6
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/README.i18n.yaml +2 -2
- package/README.md +20 -12
- package/README.zh.md +20 -12
- package/lib/client.js +1327 -229
- package/lib/client.js.map +1 -1
- package/lib/index.js +1909 -348
- package/lib/types/client/TaskBoardSettingsCard.d.ts +12 -0
- package/lib/types/client/index.d.ts +4 -5
- package/lib/types/client/locales.d.ts +65 -0
- package/lib/types/client/plugin-card-seat.d.ts +5 -0
- package/lib/types/client/schedule-zone.d.ts +50 -0
- package/lib/types/core/controller.d.ts +10 -11
- package/lib/types/core/poll-cadence.d.ts +31 -0
- package/lib/types/core/tasks.d.ts +144 -14
- package/lib/types/core/use-cases/task-schedule.d.ts +68 -10
- package/lib/types/core/verification-budget.d.ts +52 -0
- package/lib/types/core/verification.d.ts +376 -9
- package/lib/types/host/verification-gate.d.ts +24 -0
- package/lib/types/host/verification-runner.d.ts +38 -7
- package/lib/types/host-ledger.d.ts +82 -7
- package/lib/types/host-runner.d.ts +32 -9
- package/lib/types/host-service.d.ts +117 -9
- package/lib/types/index.d.ts +54 -0
- package/lib/types/protocol.d.ts +50 -12
- package/package.json +2 -6
- package/src/client/TaskBoardSettingsCard.tsx +74 -1
- package/src/client/board/NewTaskModal.tsx +151 -20
- package/src/client/board/TaskCard.tsx +3 -3
- package/src/client/board/TaskDetail.tsx +219 -18
- package/src/client/board/VerificationReport.tsx +132 -13
- package/src/client/board/status-key.ts +3 -0
- package/src/client/index.ts +43 -50
- package/src/client/locales.ts +137 -4
- package/src/client/plugin-card-seat.ts +7 -0
- package/src/client/schedule-zone.ts +100 -2
- package/src/core/controller.ts +12 -8
- package/src/core/poll-cadence.ts +41 -0
- package/src/core/store.ts +64 -15
- package/src/core/tasks.ts +193 -39
- package/src/core/use-cases/task-create.ts +30 -9
- package/src/core/use-cases/task-schedule.ts +190 -25
- package/src/core/use-cases/task-update.ts +3 -3
- package/src/core/verification-budget.ts +115 -0
- package/src/core/verification.ts +785 -17
- package/src/host/agent-tools.ts +106 -21
- package/src/host/verification-gate.ts +172 -16
- package/src/host/verification-runner.ts +207 -40
- package/src/host-ledger.ts +328 -42
- package/src/host-runner.ts +52 -19
- package/src/host-service.ts +299 -36
- package/src/index.ts +101 -5
- package/src/protocol.ts +132 -28
- package/lib/invariant.js +0 -6
- package/lib/types/client/board-card-seat.d.ts +0 -34
- package/lib/types/invariant.d.ts +0 -3
- package/src/client/board-card-seat.ts +0 -102
- package/src/invariant.ts +0 -4
|
@@ -18,6 +18,14 @@ export interface TaskBoardSettings {
|
|
|
18
18
|
preventIdleSleep?: boolean;
|
|
19
19
|
/** Subtask depth limit (1..3); 1 means a single level of subtasks. */
|
|
20
20
|
maxSubtaskDepth?: number;
|
|
21
|
+
/** Roster-poll cadence in seconds while the board has work to reconcile. */
|
|
22
|
+
sessionPollSeconds?: number;
|
|
23
|
+
/**
|
|
24
|
+
* GLOBAL native /goal switch (default off): the master control over whether
|
|
25
|
+
* this board starts a run with dsh's built-in /goal at all. Frozen per
|
|
26
|
+
* execution.
|
|
27
|
+
*/
|
|
28
|
+
goalRunEnabled?: boolean;
|
|
21
29
|
/** Goal acceptance for this board's executions (default on). */
|
|
22
30
|
goalVerification?: boolean;
|
|
23
31
|
/** Judge model route for goal acceptance; blank inherits the host default. */
|
|
@@ -35,6 +43,10 @@ export interface TaskBoardSettingsCardState extends CardShell {
|
|
|
35
43
|
preventIdleSleep: CardFieldState;
|
|
36
44
|
/** Subtask depth limit field. */
|
|
37
45
|
maxSubtaskDepth: CardFieldState;
|
|
46
|
+
/** Roster-poll cadence field. */
|
|
47
|
+
sessionPollSeconds: CardFieldState;
|
|
48
|
+
/** GLOBAL native /goal switch. */
|
|
49
|
+
goalRunEnabled: CardFieldState;
|
|
38
50
|
/** Goal-acceptance switch. */
|
|
39
51
|
goalVerification: CardFieldState;
|
|
40
52
|
/** Judge model route field. */
|
|
@@ -88,11 +88,10 @@ declare module '@deepseek-ai/cordis' {
|
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
90
|
* Required services (fiber inject waiting — the runtime must be up first).
|
|
91
|
-
* The generated remote
|
|
92
|
-
* `remote.agentPresets`
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* connection RPC face.
|
|
91
|
+
* The generated remote namespaces are not all hard injects: `remote.session`
|
|
92
|
+
* is one, while `remote.agentPresets` is reached through a scoped inject at
|
|
93
|
+
* use time (see mountUi), because the namespace mounts with the official
|
|
94
|
+
* api-remotes assembly applied beside this entry rather than before it.
|
|
96
95
|
*/
|
|
97
96
|
export declare const inject: string[];
|
|
98
97
|
/**
|
|
@@ -150,6 +150,7 @@ export declare const zh: {
|
|
|
150
150
|
'detail.noSession': string;
|
|
151
151
|
'detail.executionStarted': string;
|
|
152
152
|
'detail.execution.initiator': string;
|
|
153
|
+
'detail.execution.external': string;
|
|
153
154
|
'detail.executionEnded': string;
|
|
154
155
|
'detail.result.succeeded': string;
|
|
155
156
|
'detail.result.failed': string;
|
|
@@ -192,7 +193,34 @@ export declare const zh: {
|
|
|
192
193
|
'detail.schedule.duration.hours': string;
|
|
193
194
|
'detail.schedule.duration.minutes': string;
|
|
194
195
|
'detail.schedule.duration.seconds': string;
|
|
196
|
+
'detail.schedule.mode': string;
|
|
197
|
+
'detail.schedule.mode.cron': string;
|
|
198
|
+
'detail.schedule.mode.once': string;
|
|
199
|
+
'detail.schedule.at': string;
|
|
200
|
+
'detail.schedule.atHint': string;
|
|
201
|
+
'detail.schedule.atInvalid': string;
|
|
202
|
+
'detail.schedule.plannedAt': string;
|
|
203
|
+
'detail.schedule.zoneOnceHint': string;
|
|
204
|
+
'detail.schedule.maxRuns': string;
|
|
205
|
+
'detail.schedule.maxRunsUnlimited': string;
|
|
206
|
+
'detail.schedule.maxRunsValue': string;
|
|
207
|
+
'detail.schedule.maxRunsCustom': string;
|
|
208
|
+
'detail.schedule.maxRunsInvalid': string;
|
|
209
|
+
'detail.schedule.runs': string;
|
|
210
|
+
'detail.schedule.runsUnlimited': string;
|
|
211
|
+
'detail.schedule.remaining': string;
|
|
212
|
+
'detail.schedule.cancel': string;
|
|
213
|
+
'detail.schedule.ended': string;
|
|
214
|
+
'detail.schedule.skipped': string;
|
|
215
|
+
'detail.schedule.stop.fired': string;
|
|
216
|
+
'detail.schedule.stop.limit': string;
|
|
217
|
+
'detail.schedule.stop.no-target': string;
|
|
218
|
+
'detail.schedule.stop.missed': string;
|
|
219
|
+
'detail.schedule.stop.busy': string;
|
|
220
|
+
'detail.schedule.stop.permission': string;
|
|
221
|
+
'detail.schedule.stop.launch-failed': string;
|
|
195
222
|
'card.scheduled': string;
|
|
223
|
+
'card.scheduledOnce': string;
|
|
196
224
|
'new.workspace': string;
|
|
197
225
|
'new.agentPreset': string;
|
|
198
226
|
'new.permission': string;
|
|
@@ -255,9 +283,16 @@ export declare const zh: {
|
|
|
255
283
|
'settings.unsaved': string;
|
|
256
284
|
'settings.saveFailed': string;
|
|
257
285
|
'settings.invalidNumber': string;
|
|
286
|
+
'settings.sessionPollSeconds': string;
|
|
287
|
+
'settings.sessionPollSecondsHint': string;
|
|
258
288
|
'settings.maxSubtaskDepth': string;
|
|
259
289
|
'settings.maxSubtaskDepthHint': string;
|
|
260
290
|
'settings.maxSubtaskDepthOption': string;
|
|
291
|
+
'settings.runBehaviorTitle': string;
|
|
292
|
+
'settings.runBehaviorCardHint': string;
|
|
293
|
+
'settings.goalRunGlobal': string;
|
|
294
|
+
'settings.goalRunGlobalHint': string;
|
|
295
|
+
'settings.goalRunGlobalDisabledTaskOption': string;
|
|
261
296
|
'settings.goalVerificationTitle': string;
|
|
262
297
|
'settings.goalVerification': string;
|
|
263
298
|
'settings.goalVerificationCardHint': string;
|
|
@@ -287,6 +322,7 @@ export declare const zh: {
|
|
|
287
322
|
'running.repairing': string;
|
|
288
323
|
'running.verificationPassed': string;
|
|
289
324
|
'running.verificationFailed': string;
|
|
325
|
+
'running.verificationInvalid': string;
|
|
290
326
|
'verify.title': string;
|
|
291
327
|
'verify.status.passed': string;
|
|
292
328
|
'verify.status.failed': string;
|
|
@@ -294,6 +330,34 @@ export declare const zh: {
|
|
|
294
330
|
'verify.status.verifying': string;
|
|
295
331
|
'verify.status.pending': string;
|
|
296
332
|
'verify.status.off': string;
|
|
333
|
+
'verify.status.invalid': string;
|
|
334
|
+
'verify.status.invalidHeld': string;
|
|
335
|
+
'verify.attemptInvalid': string;
|
|
336
|
+
'verify.invalidReason.missing-finding': string;
|
|
337
|
+
'verify.invalidReason.unlocatable-quote': string;
|
|
338
|
+
'verify.invalidReason.baseline-finding': string;
|
|
339
|
+
'verify.invalidReason.vacuous-finding': string;
|
|
340
|
+
'verify.invalidReason.insufficient-evidence': string;
|
|
341
|
+
'verify.invalidReason.unknown': string;
|
|
342
|
+
'verify.invalidCounts': string;
|
|
343
|
+
'verify.invalidDetail': string;
|
|
344
|
+
'verify.criterionFindings': string;
|
|
345
|
+
'verify.criterionFinding': string;
|
|
346
|
+
'verify.criterionFindingFull': string;
|
|
347
|
+
'verify.location.task': string;
|
|
348
|
+
'verify.location.trajectory': string;
|
|
349
|
+
'verify.location.workspace': string;
|
|
350
|
+
'verify.location.baseline': string;
|
|
351
|
+
'verify.location.unknown': string;
|
|
352
|
+
'verify.detailRemoved': string;
|
|
353
|
+
'verify.cleanup.cleaned': string;
|
|
354
|
+
'verify.cleanup.pending': string;
|
|
355
|
+
'verify.cleanup.failed': string;
|
|
356
|
+
'verify.cleanup.audit': string;
|
|
357
|
+
'verify.cleanup.stage.quality': string;
|
|
358
|
+
'verify.cleanup.stage.invalid': string;
|
|
359
|
+
'verify.cleanup.stage.exception': string;
|
|
360
|
+
'verify.cleanup.stage.budget': string;
|
|
297
361
|
'verify.summary': string;
|
|
298
362
|
'verify.criteria': string;
|
|
299
363
|
'verify.criterion': string;
|
|
@@ -315,6 +379,7 @@ export declare const zh: {
|
|
|
315
379
|
'verify.applicability.goalUnavailable': string;
|
|
316
380
|
'verify.applicability.skipped': string;
|
|
317
381
|
'verify.applicability.teamMember': string;
|
|
382
|
+
'verify.applicability.goalDisabled': string;
|
|
318
383
|
'verify.effortFallback': string;
|
|
319
384
|
'verify.thresholdValue': string;
|
|
320
385
|
'detail.parent': string;
|
|
@@ -78,6 +78,11 @@ export interface PluginCardSeat {
|
|
|
78
78
|
label?: () => string;
|
|
79
79
|
/** Locale namespace the card renders with. */
|
|
80
80
|
locale: string;
|
|
81
|
+
/**
|
|
82
|
+
* Child slots this card declares and renders. Forwarded to whichever seat
|
|
83
|
+
* the host renders; absent for a card that declares none.
|
|
84
|
+
*/
|
|
85
|
+
children?: Record<string, unknown>;
|
|
81
86
|
/** Business-face factory of the registration. */
|
|
82
87
|
inject?: () => object;
|
|
83
88
|
/**
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type ScheduleRule, type ScheduleStopReason } from '../core/tasks.ts';
|
|
2
|
+
import { type TaskBoardKey } from './locales.ts';
|
|
1
3
|
/** One selectable zone, in the order the picker shows it. */
|
|
2
4
|
export interface ZoneChoice {
|
|
3
5
|
/** Canonical IANA id, or `''` for the Host-zone entry. */
|
|
@@ -32,6 +34,54 @@ export declare function zoneChoices(hostTimeZone: string | undefined, storedTime
|
|
|
32
34
|
* @returns localized relative wording.
|
|
33
35
|
*/
|
|
34
36
|
export declare function relativeTimeLabel(targetMs: number, now: number): string;
|
|
37
|
+
/**
|
|
38
|
+
* The `datetime-local` input value for an instant, read in an explicit zone:
|
|
39
|
+
* `YYYY-MM-DDTHH:mm`. Uses the shared engine's zone reader, so the editor
|
|
40
|
+
* shows the same wall clock the Host schedules by.
|
|
41
|
+
* @param epochMs - the instant to render.
|
|
42
|
+
* @param timeZone - zone to read it in; absent uses the Host zone.
|
|
43
|
+
* @returns the input value.
|
|
44
|
+
*/
|
|
45
|
+
export declare function zonedInputValue(epochMs: number, timeZone?: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Parse a `datetime-local` input value back to an instant, read in an explicit
|
|
48
|
+
* zone. Returns undefined when the text is not a whole calendar wall clock or
|
|
49
|
+
* names one the zone skips (a spring-forward gap), so the editor can refuse an
|
|
50
|
+
* unusable instant instead of silently moving it.
|
|
51
|
+
* @param value - the input text.
|
|
52
|
+
* @param timeZone - zone to interpret it in; absent uses the Host zone.
|
|
53
|
+
* @returns the instant, or undefined when it is not a usable wall clock.
|
|
54
|
+
*/
|
|
55
|
+
export declare function parseZonedInput(value: string, timeZone?: string): number | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* The default instant a new one-shot editor opens on: the next whole hour,
|
|
58
|
+
* rendered in the zone the rule will be read in.
|
|
59
|
+
* @param now - reference instant.
|
|
60
|
+
* @param timeZone - zone the rule is read in.
|
|
61
|
+
* @returns the input value for a near-future default.
|
|
62
|
+
*/
|
|
63
|
+
export declare function defaultZonedInputValue(now: number, timeZone?: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* The editor's run-cap option for a stored cap: the two named presets, the
|
|
66
|
+
* unlimited entry, or a custom value the editor shows in its own field.
|
|
67
|
+
* @param maxRuns - the stored cap, when any.
|
|
68
|
+
* @returns the select option value.
|
|
69
|
+
*/
|
|
70
|
+
export declare function runCapOption(maxRuns: number | undefined): string;
|
|
71
|
+
/** The locale key describing why a rule stopped or skipped an occurrence. */
|
|
72
|
+
export declare function stopReasonKey(reason: ScheduleStopReason): TaskBoardKey;
|
|
73
|
+
/**
|
|
74
|
+
* The run-budget line: how many scheduled executions the Host opened and how
|
|
75
|
+
* many remain. Derived from the counter and the cap rather than stored, so the
|
|
76
|
+
* display cannot drift from the ledger.
|
|
77
|
+
* @param rule - the schedule rule.
|
|
78
|
+
* @returns localized wording.
|
|
79
|
+
*/
|
|
80
|
+
export declare function budgetLabel(rule: ScheduleRule): string;
|
|
81
|
+
/** The line describing a rule that stopped itself, or undefined while it may still fire. */
|
|
82
|
+
export declare function endedLabel(rule: ScheduleRule): string | undefined;
|
|
83
|
+
/** The line describing the last skipped occurrence, or undefined when none. */
|
|
84
|
+
export declare function skippedLabel(rule: ScheduleRule): string | undefined;
|
|
35
85
|
/**
|
|
36
86
|
* The full next-run label: the absolute wall clock in the schedule's own zone,
|
|
37
87
|
* plus the relative distance in parentheses. Showing both matters because a
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { TaskStore } from './store.ts';
|
|
15
15
|
import { type NewTaskInput, type TaskRecord, type TaskStatus } from './tasks.ts';
|
|
16
|
+
import { type SetSchedulePatch } from './use-cases/task-schedule.ts';
|
|
16
17
|
import { type TaskUpdatePatch } from './use-cases/task-update.ts';
|
|
17
18
|
import type { TaskBoardAction, TaskBoardEventPayload, TaskBoardParseDraft, TaskBoardParseRequest, TaskBoardSnapshot } from '../protocol.ts';
|
|
18
19
|
import type { TaskBoardExtensionActionRequest, TaskBoardVisibilityPredicate } from './extension.ts';
|
|
@@ -111,7 +112,7 @@ export interface ExecutionOptionsSnapshot {
|
|
|
111
112
|
* (revision/scheduler/power); a full snapshot also carries the deployment
|
|
112
113
|
* constants the UI reads (session default permission, subtask depth).
|
|
113
114
|
*/
|
|
114
|
-
export type HostMirror = Pick<TaskBoardSnapshot, 'revision' | 'scheduler' | 'power' | 'sessionDefaultPermission' | 'maxSubtaskDepth' | 'teamRunAvailable' | 'extensions'>;
|
|
115
|
+
export type HostMirror = Pick<TaskBoardSnapshot, 'revision' | 'scheduler' | 'power' | 'sessionDefaultPermission' | 'maxSubtaskDepth' | 'teamRunAvailable' | 'goalRunEnabled' | 'extensions'>;
|
|
115
116
|
/** Immutable controller snapshot for UI subscriptions. */
|
|
116
117
|
export interface ControllerSnapshot {
|
|
117
118
|
tasks: readonly TaskRecord[];
|
|
@@ -287,19 +288,17 @@ export declare class BoardController {
|
|
|
287
288
|
*/
|
|
288
289
|
deleteTag(name: string): Promise<boolean>;
|
|
289
290
|
/**
|
|
290
|
-
* Update a task's schedule rule.
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
291
|
+
* Update a task's schedule rule. An unusable plan (blank or invalid cron, a
|
|
292
|
+
* one-shot with no future instant, a non-positive run cap) is rejected
|
|
293
|
+
* (returns false, state untouched). When the rule ends up enabled the next
|
|
294
|
+
* run instant is computed immediately — a one-shot's next instant IS its
|
|
295
|
+
* planned instant — and a disabled rule carries none. Delegates the domain
|
|
296
|
+
* transition to the schedule use case.
|
|
294
297
|
* @param id - the task to schedule.
|
|
295
298
|
* @param patch - fields to change (absent fields keep their current value).
|
|
296
|
-
* @returns true when applied, false when rejected (
|
|
299
|
+
* @returns true when applied, false when rejected (unknown task / unusable plan).
|
|
297
300
|
*/
|
|
298
|
-
setSchedule(id: string, patch:
|
|
299
|
-
enabled?: boolean;
|
|
300
|
-
cron?: string;
|
|
301
|
-
timeZone?: string | null;
|
|
302
|
-
}): boolean;
|
|
301
|
+
setSchedule(id: string, patch: SetSchedulePatch): boolean;
|
|
303
302
|
/**
|
|
304
303
|
* Legacy pure-controller seam retained for migration-focused tests. The
|
|
305
304
|
* production browser never rolls schedules; the Host ledger owns them.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Roster-poll cadence: the one recurring Host timer the board holds.
|
|
3
|
+
*
|
|
4
|
+
* The cadence is a user setting (seconds), so the bounds and the seconds to
|
|
5
|
+
* milliseconds conversion live here rather than in either half: the settings
|
|
6
|
+
* card validates a draft against the same range the Host clamps a hand-edited
|
|
7
|
+
* configuration to, and a programmatic mount that passes no cadence gets the
|
|
8
|
+
* default.
|
|
9
|
+
*
|
|
10
|
+
* @module dsh-task-board/core/poll-cadence
|
|
11
|
+
*/
|
|
12
|
+
/** Cadence used when neither the settings document nor the mount names one. */
|
|
13
|
+
export declare const DEFAULT_SESSION_POLL_SECONDS = 5;
|
|
14
|
+
/** Shortest accepted cadence: a faster poll only burns CPU on the Host. */
|
|
15
|
+
export declare const SESSION_POLL_MIN_SECONDS = 1;
|
|
16
|
+
/** Longest accepted cadence, five minutes. */
|
|
17
|
+
export declare const SESSION_POLL_MAX_SECONDS = 300;
|
|
18
|
+
/**
|
|
19
|
+
* Clamp a configured cadence into the supported range. A value the schema
|
|
20
|
+
* would refuse (non-finite, below the floor, above the ceiling) still leaves
|
|
21
|
+
* the board polling: an out-of-range setting never disables the timer.
|
|
22
|
+
* @param seconds - configured cadence in seconds, or undefined for the default.
|
|
23
|
+
* @returns the effective cadence in seconds.
|
|
24
|
+
*/
|
|
25
|
+
export declare function normalizeSessionPollSeconds(seconds: number | undefined): number;
|
|
26
|
+
/**
|
|
27
|
+
* The cadence in the milliseconds the Host timer face takes.
|
|
28
|
+
* @param seconds - configured cadence in seconds, or undefined for the default.
|
|
29
|
+
* @returns the effective cadence in milliseconds.
|
|
30
|
+
*/
|
|
31
|
+
export declare function sessionPollMs(seconds: number | undefined): number;
|
|
@@ -34,8 +34,23 @@ export interface ExecutionRecord {
|
|
|
34
34
|
* Session id of the DSH session that issued the run/rerun action (issue #6
|
|
35
35
|
* audit origin). Client-asserted, not a trust boundary; absent when the run
|
|
36
36
|
* was triggered by cron (source unknown).
|
|
37
|
+
*
|
|
38
|
+
* On an EXTERNAL record (issue #1826) this names the agent that completed the
|
|
39
|
+
* work outside the Host instead, which is what makes the history readable.
|
|
37
40
|
*/
|
|
38
41
|
initiatedBy?: string;
|
|
42
|
+
/**
|
|
43
|
+
* True when this outcome was recorded from outside the Host by
|
|
44
|
+
* `record-external-outcome` rather than observed from a DSH session
|
|
45
|
+
* (issue #1826).
|
|
46
|
+
*
|
|
47
|
+
* The board never manufactures one of these itself: every Host-side path
|
|
48
|
+
* (`run`, `settle`, cascade folding, restart recovery) writes an execution
|
|
49
|
+
* without it, so `external` is the single field separating work this Host ran
|
|
50
|
+
* from work an outside agent reported. Such a record never carries a
|
|
51
|
+
* `sessionId` and never claims a verdict the Host observed.
|
|
52
|
+
*/
|
|
53
|
+
external?: boolean;
|
|
39
54
|
/** Freeze instant captured from the card snapshot when the run opened. */
|
|
40
55
|
frozenAt?: number;
|
|
41
56
|
/** Freeze source session captured from the card snapshot when the run opened. */
|
|
@@ -76,17 +91,69 @@ export declare const EXECUTION_HISTORY_LIMIT = 20;
|
|
|
76
91
|
* cannot start a new run while one is still open.
|
|
77
92
|
*/
|
|
78
93
|
export declare function retainRecentExecutions(executions: readonly ExecutionRecord[]): ExecutionRecord[];
|
|
94
|
+
/**
|
|
95
|
+
* How a rule produces its triggers: a recurring cron rule, or one single
|
|
96
|
+
* planned instant. The distinction is stored rather than inferred so a
|
|
97
|
+
* one-shot is never represented as a date-restricted cron expression.
|
|
98
|
+
*/
|
|
99
|
+
export type ScheduleMode = 'cron' | 'once';
|
|
100
|
+
/**
|
|
101
|
+
* Why a due occurrence produced no run, or why a rule stopped on its own.
|
|
102
|
+
*
|
|
103
|
+
* These are stable codes, not display text: the board renders them through its
|
|
104
|
+
* locale dictionary, so the ledger never carries a translated string.
|
|
105
|
+
*/
|
|
106
|
+
export type ScheduleStopReason =
|
|
107
|
+
/** The single planned instant produced its run. */
|
|
108
|
+
'fired'
|
|
109
|
+
/** The recurring rule reached its run budget. */
|
|
110
|
+
| 'limit'
|
|
111
|
+
/** The plan has no reachable instant any more (an impossible cron). */
|
|
112
|
+
| 'no-target'
|
|
113
|
+
/** The instant passed while the board was not running through it. */
|
|
114
|
+
| 'missed'
|
|
115
|
+
/** The task already had an open execution, so this occurrence was skipped. */
|
|
116
|
+
| 'busy'
|
|
117
|
+
/** An above-default permission was still unconfirmed, so this occurrence was skipped. */
|
|
118
|
+
| 'permission'
|
|
119
|
+
/**
|
|
120
|
+
* A scheduled launch failed before any session existed, so the occurrence
|
|
121
|
+
* created no execution and its run was refunded rather than spent.
|
|
122
|
+
*/
|
|
123
|
+
| 'launch-failed';
|
|
124
|
+
/** Every stop reason this build understands; the repair and wire gates share it. */
|
|
125
|
+
export declare const SCHEDULE_STOP_REASONS: readonly ScheduleStopReason[];
|
|
79
126
|
/**
|
|
80
127
|
* A scheduled-run rule attached to a task. The Host scheduler triggers the
|
|
81
128
|
* task when `nextRunAt` is due and persists the rule in the Host ledger.
|
|
129
|
+
*
|
|
130
|
+
* A rule is either `cron` (the historical shape: a 5-field expression with an
|
|
131
|
+
* explicit IANA zone, optionally capped by `maxRuns`) or `once` (one planned
|
|
132
|
+
* instant, `at`, with an implicit budget of one run). Both modes count the
|
|
133
|
+
* executions the scheduler actually started in `runCount`; a rule that reaches
|
|
134
|
+
* its budget stops itself in the ledger (`enabled: false` + `endedReason`)
|
|
135
|
+
* instead of relying on the execution agent to disable it.
|
|
82
136
|
*/
|
|
83
137
|
export interface ScheduleRule {
|
|
84
138
|
/** Whether the schedule is armed. */
|
|
85
139
|
enabled: boolean;
|
|
86
|
-
/** 5-field cron expression: `分 时 日 月 周`. */
|
|
87
|
-
cron: string;
|
|
88
140
|
/**
|
|
89
|
-
*
|
|
141
|
+
* Which kind of plan this rule carries. A rule loaded from a ledger written
|
|
142
|
+
* before the field existed normalizes to `cron` (its `cron` is the plan).
|
|
143
|
+
*/
|
|
144
|
+
mode: ScheduleMode;
|
|
145
|
+
/**
|
|
146
|
+
* 5-field cron expression: `分 时 日 月 周`. Present on a `cron` rule and
|
|
147
|
+
* absent on a `once` rule.
|
|
148
|
+
*/
|
|
149
|
+
cron?: string;
|
|
150
|
+
/**
|
|
151
|
+
* The instant a `once` rule executes (ms epoch). Kept after the run so the
|
|
152
|
+
* detail view can still show the planned time once the rule has ended.
|
|
153
|
+
*/
|
|
154
|
+
at?: number;
|
|
155
|
+
/**
|
|
156
|
+
* IANA zone the wall clock is read in (issue #1722). Absent means the
|
|
90
157
|
* Host process zone, which is what a rule written before zones were
|
|
91
158
|
* persisted keeps using; the Host stamps its own zone at ledger migration so
|
|
92
159
|
* a later `TZ` change cannot silently move an existing rule.
|
|
@@ -96,7 +163,51 @@ export interface ScheduleRule {
|
|
|
96
163
|
nextRunAt: number | undefined;
|
|
97
164
|
/** Instant of the latest scheduled trigger (ms epoch). */
|
|
98
165
|
lastTriggeredAt: number | undefined;
|
|
166
|
+
/**
|
|
167
|
+
* Scheduled executions the Host has created under this rule. This is the
|
|
168
|
+
* run budget's counter: it counts executions that were successfully opened
|
|
169
|
+
* (whatever their later outcome), never completions, and a manual run never
|
|
170
|
+
* touches it. A skipped occurrence (busy / permission / missed / no target)
|
|
171
|
+
* creates no execution and therefore consumes nothing.
|
|
172
|
+
*/
|
|
173
|
+
runCount: number;
|
|
174
|
+
/**
|
|
175
|
+
* Maximum scheduled executions for a `cron` rule; absent means unlimited
|
|
176
|
+
* (the historical behavior). A `once` rule has an implicit budget of one and
|
|
177
|
+
* stores no `maxRuns`.
|
|
178
|
+
*/
|
|
179
|
+
maxRuns?: number;
|
|
180
|
+
/** Instant the rule stopped itself (ms epoch); absent while it may still fire. */
|
|
181
|
+
endedAt?: number;
|
|
182
|
+
/** Why the rule stopped itself; present with `endedAt`. */
|
|
183
|
+
endedReason?: ScheduleStopReason;
|
|
184
|
+
/** Instant of the latest occurrence that produced no run. */
|
|
185
|
+
skippedAt?: number;
|
|
186
|
+
/** Why that occurrence produced no run; present with `skippedAt`. */
|
|
187
|
+
skippedReason?: ScheduleStopReason;
|
|
99
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* The rule's total run budget: one for a one-shot, the stored cap for a
|
|
191
|
+
* recurring rule, and undefined when the rule is unlimited.
|
|
192
|
+
* @param rule - the schedule rule.
|
|
193
|
+
* @returns the budget, or undefined for an uncapped rule.
|
|
194
|
+
*/
|
|
195
|
+
export declare function scheduleRunBudget(rule: ScheduleRule): number | undefined;
|
|
196
|
+
/**
|
|
197
|
+
* Whether a rule has no runs left. The scheduler refuses to open another
|
|
198
|
+
* execution for an exhausted rule, which is what makes the budget terminal
|
|
199
|
+
* regardless of restarts or repeated timer fires.
|
|
200
|
+
* @param rule - the schedule rule.
|
|
201
|
+
* @returns true when the run budget is spent.
|
|
202
|
+
*/
|
|
203
|
+
export declare function scheduleExhausted(rule: ScheduleRule): boolean;
|
|
204
|
+
/**
|
|
205
|
+
* The instant a rule plans to fire next, for display, whatever its mode: the
|
|
206
|
+
* planned `at` of a one-shot, or the stored `nextRunAt` of a recurring rule.
|
|
207
|
+
* @param rule - the schedule rule.
|
|
208
|
+
* @returns the planned instant, or undefined when the rule carries none.
|
|
209
|
+
*/
|
|
210
|
+
export declare function schedulePlannedAt(rule: ScheduleRule): number | undefined;
|
|
100
211
|
/**
|
|
101
212
|
* Frozen context snapshot carried by a continuation card (issue #4): the
|
|
102
213
|
* goal/progress/next text (already sanitized by the freeze gates) plus the
|
|
@@ -236,9 +347,11 @@ export interface TaskRecord {
|
|
|
236
347
|
* armed with the composed execution prompt as its objective. The session then
|
|
237
348
|
* keeps working automatic continuation rounds until the agent marks the goal
|
|
238
349
|
* complete, and this board settles the execution on the goal's own end
|
|
239
|
-
* instead of at the first turn end. Absent means
|
|
240
|
-
* by default, so only an explicit `
|
|
241
|
-
*
|
|
350
|
+
* instead of at the first turn end. Absent means OFF — the option is
|
|
351
|
+
* unchecked by default, so only an explicit `true` joins the goal run and
|
|
352
|
+
* every card that never touched it (including every card written before the
|
|
353
|
+
* field existed) runs one plain turn. A session whose `/goal` command is
|
|
354
|
+
* refused still runs the prompt.
|
|
242
355
|
*/
|
|
243
356
|
goalRun?: boolean;
|
|
244
357
|
/**
|
|
@@ -338,8 +451,8 @@ export interface NewTaskInput {
|
|
|
338
451
|
/** Run the subtree as an Agent Team (Team Lead session plus one teammate per direct subtask). */
|
|
339
452
|
teamRun?: boolean;
|
|
340
453
|
/**
|
|
341
|
-
* Start the execution with dsh's built-in `/goal`. Absent
|
|
342
|
-
*
|
|
454
|
+
* Start the execution with dsh's built-in `/goal`. Absent means the default
|
|
455
|
+
* (one plain turn); only an explicit true joins the goal run.
|
|
343
456
|
*/
|
|
344
457
|
goalRun?: boolean;
|
|
345
458
|
/**
|
|
@@ -348,15 +461,21 @@ export interface NewTaskInput {
|
|
|
348
461
|
*/
|
|
349
462
|
skipVerification?: boolean;
|
|
350
463
|
/**
|
|
351
|
-
* Optional
|
|
352
|
-
* dialog): an enable flag, a 5-field cron
|
|
353
|
-
*
|
|
354
|
-
*
|
|
464
|
+
* Optional scheduling rule requested at creation time (the new-task
|
|
465
|
+
* dialog): an enable flag, the kind of plan (a recurring 5-field cron
|
|
466
|
+
* expression, or one planned instant for a one-shot), an optional run cap,
|
|
467
|
+
* and the IANA zone its wall clock is read in (absent means the Host zone).
|
|
468
|
+
* The create use case arms it only when enabled and the plan is valid.
|
|
355
469
|
*/
|
|
356
470
|
schedule?: {
|
|
357
471
|
enabled: boolean;
|
|
358
|
-
|
|
472
|
+
mode?: ScheduleMode;
|
|
473
|
+
cron?: string;
|
|
474
|
+
/** Planned instant of a one-shot rule (ms epoch). */
|
|
475
|
+
at?: number;
|
|
359
476
|
timeZone?: string;
|
|
477
|
+
/** Run cap of a recurring rule; absent means unlimited. */
|
|
478
|
+
maxRuns?: number;
|
|
360
479
|
};
|
|
361
480
|
/**
|
|
362
481
|
* Optional frozen context snapshot (goal/progress/next, sanitized by the
|
|
@@ -448,7 +567,8 @@ export declare function withStatus(task: TaskRecord, status: TaskStatus, now: nu
|
|
|
448
567
|
* Merge a schedule patch into a task's schedule rule (creating it when
|
|
449
568
|
* absent), with a fresh updatedAt. Keys present in the patch overwrite the
|
|
450
569
|
* current value — including explicit `undefined`, which clears a field (used
|
|
451
|
-
* to disarm `nextRunAt`); absent keys keep
|
|
570
|
+
* to disarm `nextRunAt` or to drop an ended/skip record); absent keys keep
|
|
571
|
+
* their current value.
|
|
452
572
|
*/
|
|
453
573
|
export declare function withSchedule(task: TaskRecord, patch: Partial<ScheduleRule>, now: number): TaskRecord;
|
|
454
574
|
/**
|
|
@@ -461,6 +581,16 @@ export declare function startExecution(task: TaskRecord, now: number, executionI
|
|
|
461
581
|
task: TaskRecord;
|
|
462
582
|
execution: ExecutionRecord;
|
|
463
583
|
};
|
|
584
|
+
/**
|
|
585
|
+
* The column a settled outcome puts a card in. The ONE derivation shared by
|
|
586
|
+
* every settlement path (issue #1826): a Host-run execution and an outcome an
|
|
587
|
+
* outside agent reported both land in the same column from the same rule, so
|
|
588
|
+
* the column never becomes a second, independent piece of state.
|
|
589
|
+
@param task - the card being settled.
|
|
590
|
+
@param outcome - the settled outcome.
|
|
591
|
+
@returns the status the card shows afterwards.
|
|
592
|
+
*/
|
|
593
|
+
export declare function settledStatus(task: TaskRecord, outcome: ExecutionOutcome): TaskStatus;
|
|
464
594
|
/**
|
|
465
595
|
* Settle a running execution: record the outcome and move the task into the
|
|
466
596
|
* matching column. No-op (returns the input task) when the execution is not
|
|
@@ -1,23 +1,37 @@
|
|
|
1
|
-
import { type TaskRecord } from '../tasks.ts';
|
|
1
|
+
import { type ScheduleMode, type ScheduleStopReason, type TaskRecord } from '../tasks.ts';
|
|
2
2
|
/** Fields the schedule use case may change on a rule. */
|
|
3
3
|
export interface SetSchedulePatch {
|
|
4
4
|
enabled?: boolean;
|
|
5
|
+
/** Which kind of plan to write; an absent key keeps the current mode. */
|
|
6
|
+
mode?: ScheduleMode;
|
|
7
|
+
/** 5-field cron expression of a recurring rule. */
|
|
5
8
|
cron?: string;
|
|
6
|
-
/**
|
|
9
|
+
/** Planned instant of a one-shot rule (ms epoch). */
|
|
10
|
+
at?: number;
|
|
11
|
+
/** IANA zone for the wall clock; `null` clears it back to the Host zone. */
|
|
7
12
|
timeZone?: string | null;
|
|
13
|
+
/** Run cap of a recurring rule; `null` clears it back to unlimited. */
|
|
14
|
+
maxRuns?: number | null;
|
|
8
15
|
}
|
|
9
16
|
/** Result of arming/disarming a rule. */
|
|
10
17
|
export interface SetScheduleResult {
|
|
11
18
|
/** The next ledger; unchanged reference when rejected. */
|
|
12
19
|
tasks: readonly TaskRecord[];
|
|
13
|
-
/** Whether the rule was applied (false on unknown task / invalid
|
|
20
|
+
/** Whether the rule was applied (false on unknown task / invalid plan). */
|
|
14
21
|
applied: boolean;
|
|
15
22
|
}
|
|
16
23
|
/**
|
|
17
|
-
* Set an on-board task's
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
24
|
+
* Set an on-board task's scheduling rule. An unknown or archived task, an
|
|
25
|
+
* unusable zone, a blank or invalid cron, a one-shot with no usable instant,
|
|
26
|
+
* a one-shot armed in the past, and a run cap that is not a positive integer
|
|
27
|
+
* are all rejected (state untouched). An enabled rule computes its next run
|
|
28
|
+
* instant immediately — a one-shot's next instant IS its planned instant — and
|
|
29
|
+
* a disabled rule carries none.
|
|
30
|
+
*
|
|
31
|
+
* Arming a rule that was not enabled, or switching its kind, starts a fresh
|
|
32
|
+
* budget: the run counter and any previous stop/skip record are cleared. Edits
|
|
33
|
+
* to a live rule (expression, zone, cap) keep the consumed count, so an
|
|
34
|
+
* already-spent budget can never silently reset and overrun.
|
|
21
35
|
* @param tasks - current ledger.
|
|
22
36
|
* @param id - the task to schedule.
|
|
23
37
|
* @param patch - rule fields to change (absent fields keep their current value).
|
|
@@ -26,9 +40,8 @@ export interface SetScheduleResult {
|
|
|
26
40
|
*/
|
|
27
41
|
export declare function applySetSchedule(tasks: readonly TaskRecord[], id: string, patch: SetSchedulePatch, now: number, hostTimeZone: string): SetScheduleResult;
|
|
28
42
|
/**
|
|
29
|
-
* Roll a task's schedule rule forward (
|
|
30
|
-
* due instant and the trigger instant. No-op for tasks without a rule
|
|
31
|
-
* mid-tick, for example).
|
|
43
|
+
* Roll a task's schedule rule forward (legacy pure-controller seam): persist
|
|
44
|
+
* the next due instant and the trigger instant. No-op for tasks without a rule.
|
|
32
45
|
* @param tasks - current ledger.
|
|
33
46
|
* @param id - the task to roll forward.
|
|
34
47
|
* @param nextRunAt - next due instant (may be undefined to clear).
|
|
@@ -36,3 +49,48 @@ export declare function applySetSchedule(tasks: readonly TaskRecord[], id: strin
|
|
|
36
49
|
* @param now - clock instant (ms epoch).
|
|
37
50
|
*/
|
|
38
51
|
export declare function applyScheduleNextRun(tasks: readonly TaskRecord[], id: string, nextRunAt: number | undefined, lastTriggeredAt: number | undefined, now: number): readonly TaskRecord[];
|
|
52
|
+
/**
|
|
53
|
+
* The effect one due occurrence has on its rule, applied by the Host ledger.
|
|
54
|
+
*/
|
|
55
|
+
export interface ScheduleProgress {
|
|
56
|
+
/** Executions this occurrence actually opened (0 or 1), added to the counter. */
|
|
57
|
+
consumed: number;
|
|
58
|
+
/** Next due instant; absent stops the rule. */
|
|
59
|
+
nextRunAt?: number;
|
|
60
|
+
/** Trigger instant recorded as `lastTriggeredAt` (a consumed run only). */
|
|
61
|
+
lastTriggeredAt?: number;
|
|
62
|
+
/** Terminal reason when the rule stops itself (disarms it). */
|
|
63
|
+
endedReason?: ScheduleStopReason;
|
|
64
|
+
/** Why this occurrence produced no run, when it did not. */
|
|
65
|
+
skippedReason?: ScheduleStopReason;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Refund the one scheduled run an occurrence consumed before its launch failed
|
|
69
|
+
* without ever creating a session. Nothing ran, so the run budget is restored
|
|
70
|
+
* and the rule is left able to serve its next occurrence — a launch failure
|
|
71
|
+
* must never silently spend the user's limited budget.
|
|
72
|
+
*
|
|
73
|
+
* A recurring rule keeps its already-rolled-forward target; a fire that ended
|
|
74
|
+
* the rule at its cap (no target left) is restored and re-armed at the next
|
|
75
|
+
* occurrence, so a failed launch consumes nothing. A one-shot has no later
|
|
76
|
+
* occurrence, so it is left stopped with `launch-failed` and its run returned.
|
|
77
|
+
* A rule the user disabled in the meantime is not resurrected.
|
|
78
|
+
* @param tasks - current ledger.
|
|
79
|
+
* @param id - the task whose occurrence is being refunded.
|
|
80
|
+
* @param now - clock instant (ms epoch).
|
|
81
|
+
* @param hostTimeZone - zone a rule with no stored zone follows.
|
|
82
|
+
* @returns the next ledger.
|
|
83
|
+
*/
|
|
84
|
+
export declare function applyScheduleRefund(tasks: readonly TaskRecord[], id: string, now: number, hostTimeZone: string): readonly TaskRecord[];
|
|
85
|
+
/**
|
|
86
|
+
* Record what a due occurrence did to a task's rule: advance the run counter
|
|
87
|
+
* by the executions it opened, set the next target (or stop the rule), and
|
|
88
|
+
* note a terminal reason or a skipped occurrence. Nothing is written for a
|
|
89
|
+
* task that has no rule or has been archived mid-tick.
|
|
90
|
+
* @param tasks - current ledger.
|
|
91
|
+
* @param id - the task whose rule fired.
|
|
92
|
+
* @param progress - the occurrence's effect.
|
|
93
|
+
* @param now - clock instant (ms epoch).
|
|
94
|
+
* @returns the next ledger.
|
|
95
|
+
*/
|
|
96
|
+
export declare function applyScheduleProgress(tasks: readonly TaskRecord[], id: string, progress: ScheduleProgress, now: number): readonly TaskRecord[];
|