@linxin666/dsh-client-ui-task-board 0.4.4 → 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 +59 -20
- package/README.zh.md +59 -20
- package/lib/client.js +6208 -624
- package/lib/client.js.map +1 -1
- package/lib/index.js +4774 -493
- package/lib/types/client/PluginSettingsCard.d.ts +7 -0
- package/lib/types/client/TaskBoardSettingsCard.d.ts +31 -3
- package/lib/types/client/board/ClampedMarkdown.d.ts +3 -0
- package/lib/types/client/board/ConfirmDialog.d.ts +4 -1
- package/lib/types/client/board/EditTaskModal.d.ts +5 -2
- package/lib/types/client/board/LinkSubtaskModal.d.ts +5 -2
- package/lib/types/client/board/NewTaskModal.d.ts +4 -1
- package/lib/types/client/board/TagManagerModal.d.ts +26 -0
- package/lib/types/client/board/TaskBoard.d.ts +10 -1
- package/lib/types/client/board/TaskCard.d.ts +14 -1
- package/lib/types/client/board/TaskDetail.d.ts +3 -1
- package/lib/types/client/board/TaskForm.d.ts +4 -1
- package/lib/types/client/board/VerificationReport.d.ts +23 -0
- package/lib/types/client/board/card-view.d.ts +113 -0
- package/lib/types/client/board/icons.d.ts +32 -0
- package/lib/types/client/board/overlay.d.ts +69 -0
- package/lib/types/client/board/status-key.d.ts +15 -0
- package/lib/types/client/board/task-markdown.d.ts +19 -0
- package/lib/types/client/index.d.ts +23 -5
- package/lib/types/client/locales.d.ts +168 -1
- package/lib/types/client/native-panel.d.ts +6 -20
- package/lib/types/client/plugin-card-seat.d.ts +5 -0
- package/lib/types/client/schedule-zone.d.ts +50 -0
- package/lib/types/client/seats.d.ts +18 -0
- package/lib/types/client/service.d.ts +32 -0
- package/lib/types/client/settings-form.d.ts +16 -0
- package/lib/types/core/controller.d.ts +46 -11
- package/lib/types/core/extension.d.ts +283 -0
- package/lib/types/core/handover.d.ts +13 -1
- package/lib/types/core/issue-form.d.ts +16 -0
- package/lib/types/core/poll-cadence.d.ts +31 -0
- package/lib/types/core/tasks.d.ts +228 -17
- package/lib/types/core/use-cases/task-schedule.d.ts +68 -10
- package/lib/types/core/use-cases/task-tag.d.ts +48 -0
- package/lib/types/core/use-cases/task-update.d.ts +5 -3
- package/lib/types/core/verification-budget.d.ts +52 -0
- package/lib/types/core/verification.d.ts +753 -0
- package/lib/types/core/workspace-target.d.ts +41 -0
- package/lib/types/host/agent-tools.d.ts +1 -1
- package/lib/types/host/extension-registry.d.ts +111 -0
- package/lib/types/host/llm-dispatch.d.ts +63 -0
- package/lib/types/host/verification-gate.d.ts +95 -0
- package/lib/types/host/verification-runner.d.ts +139 -0
- package/lib/types/host/workspace-evidence.d.ts +53 -0
- package/lib/types/host-ledger.d.ts +133 -14
- package/lib/types/host-runner.d.ts +55 -10
- package/lib/types/host-service.d.ts +205 -11
- package/lib/types/index.d.ts +134 -26
- package/lib/types/protocol.d.ts +92 -10
- package/lib/types/tool-surface.d.ts +71 -0
- package/package.json +28 -31
- package/src/client/PluginSettingsCard.tsx +9 -0
- package/src/client/TaskBoardSettingsCard.tsx +378 -66
- package/src/client/board/ClampedMarkdown.tsx +69 -0
- package/src/client/board/ConfirmDialog.tsx +7 -3
- package/src/client/board/EditTaskModal.tsx +5 -2
- package/src/client/board/LinkSubtaskModal.tsx +12 -7
- package/src/client/board/NewTaskModal.tsx +168 -21
- package/src/client/board/TagManagerModal.tsx +235 -0
- package/src/client/board/TaskBoard.tsx +259 -103
- package/src/client/board/TaskCard.tsx +214 -81
- package/src/client/board/TaskDetail.tsx +289 -43
- package/src/client/board/TaskForm.tsx +13 -3
- package/src/client/board/VerificationReport.tsx +322 -0
- package/src/client/board/card-view.ts +220 -0
- package/src/client/board/icons.tsx +89 -0
- package/src/client/board/overlay.tsx +189 -0
- package/src/client/board/status-key.ts +30 -0
- package/src/client/board/task-markdown.module.css +137 -0
- package/src/client/board/task-markdown.tsx +106 -0
- package/src/client/board-settings.module.css +66 -0
- package/src/client/board.module.css +488 -63
- package/src/client/index.ts +82 -51
- package/src/client/locales.ts +343 -6
- package/src/client/native-panel.tsx +18 -2
- package/src/client/plugin-card-seat.ts +7 -0
- package/src/client/schedule-zone.ts +100 -2
- package/src/client/seats.tsx +58 -0
- package/src/client/service.ts +94 -0
- package/src/client/settings-card.module.css +31 -5
- package/src/client/settings-form.ts +27 -1
- package/src/core/controller.ts +105 -10
- package/src/core/extension.ts +376 -0
- package/src/core/freeze-snapshot.ts +2 -2
- package/src/core/handover.ts +16 -1
- package/src/core/issue-form.ts +19 -0
- package/src/core/poll-cadence.ts +41 -0
- package/src/core/store.ts +80 -15
- package/src/core/tasks.ts +293 -45
- package/src/core/use-cases/task-create.ts +30 -9
- package/src/core/use-cases/task-parent.ts +3 -2
- package/src/core/use-cases/task-schedule.ts +190 -25
- package/src/core/use-cases/task-tag.ts +110 -0
- package/src/core/use-cases/task-update.ts +13 -7
- package/src/core/verification-budget.ts +115 -0
- package/src/core/verification.ts +1594 -0
- package/src/core/workspace-target.ts +59 -0
- package/src/host/agent-tools.ts +125 -38
- package/src/host/extension-registry.ts +509 -0
- package/src/host/llm-dispatch.ts +87 -0
- package/src/host/verification-gate.ts +487 -0
- package/src/host/verification-runner.ts +702 -0
- package/src/host/workspace-evidence.ts +176 -0
- package/src/host-ai.ts +15 -9
- package/src/host-ledger.ts +468 -71
- package/src/host-routes.ts +15 -2
- package/src/host-runner.ts +106 -24
- package/src/host-service.ts +546 -40
- package/src/index.ts +305 -21
- package/src/protocol.ts +226 -28
- package/src/tool-surface.ts +95 -0
- package/lib/invariant.js +0 -6
- package/lib/types/invariant.d.ts +0 -3
- package/src/invariant.ts +0 -4
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* unit-testable in isolation.
|
|
6
6
|
*/
|
|
7
7
|
import type { FreezeSnapshot } from './freeze-snapshot.ts';
|
|
8
|
+
import type { ExecutionVerification } from './verification.ts';
|
|
8
9
|
import type { TaskHandover, TaskHandoverInput } from './handover.ts';
|
|
10
|
+
import { type TaskBoardExtensionPayload } from './extension.ts';
|
|
9
11
|
/** Task lifecycle status, one per kanban column. */
|
|
10
12
|
export type TaskStatus = 'backlog' | 'todo' | 'running' | 'done' | 'failed';
|
|
11
13
|
/** Settled outcome of one execution attempt. */
|
|
@@ -32,8 +34,23 @@ export interface ExecutionRecord {
|
|
|
32
34
|
* Session id of the DSH session that issued the run/rerun action (issue #6
|
|
33
35
|
* audit origin). Client-asserted, not a trust boundary; absent when the run
|
|
34
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.
|
|
35
40
|
*/
|
|
36
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;
|
|
37
54
|
/** Freeze instant captured from the card snapshot when the run opened. */
|
|
38
55
|
frozenAt?: number;
|
|
39
56
|
/** Freeze source session captured from the card snapshot when the run opened. */
|
|
@@ -52,6 +69,14 @@ export interface ExecutionRecord {
|
|
|
52
69
|
ownResult?: ExecutionOutcome;
|
|
53
70
|
/** Human failure text that arrived with {@link ownResult}. */
|
|
54
71
|
ownError?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Goal acceptance state of this execution (task-board goal verification).
|
|
74
|
+
* Fixed when the execution starts and never rewritten from later settings:
|
|
75
|
+
* the contract is frozen here, the attempts accrue here, and the settlement
|
|
76
|
+
* rule reads the pass record here. Absent on an execution that started
|
|
77
|
+
* before the feature existed, which keeps it on the historical verdict.
|
|
78
|
+
*/
|
|
79
|
+
verification?: ExecutionVerification;
|
|
55
80
|
}
|
|
56
81
|
/**
|
|
57
82
|
* Maximum number of execution records retained per task. Older settled runs
|
|
@@ -66,17 +91,69 @@ export declare const EXECUTION_HISTORY_LIMIT = 20;
|
|
|
66
91
|
* cannot start a new run while one is still open.
|
|
67
92
|
*/
|
|
68
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[];
|
|
69
126
|
/**
|
|
70
127
|
* A scheduled-run rule attached to a task. The Host scheduler triggers the
|
|
71
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.
|
|
72
136
|
*/
|
|
73
137
|
export interface ScheduleRule {
|
|
74
138
|
/** Whether the schedule is armed. */
|
|
75
139
|
enabled: boolean;
|
|
76
|
-
/** 5-field cron expression: `分 时 日 月 周`. */
|
|
77
|
-
cron: string;
|
|
78
140
|
/**
|
|
79
|
-
*
|
|
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
|
|
80
157
|
* Host process zone, which is what a rule written before zones were
|
|
81
158
|
* persisted keeps using; the Host stamps its own zone at ledger migration so
|
|
82
159
|
* a later `TZ` change cannot silently move an existing rule.
|
|
@@ -86,7 +163,51 @@ export interface ScheduleRule {
|
|
|
86
163
|
nextRunAt: number | undefined;
|
|
87
164
|
/** Instant of the latest scheduled trigger (ms epoch). */
|
|
88
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;
|
|
89
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;
|
|
90
211
|
/**
|
|
91
212
|
* Frozen context snapshot carried by a continuation card (issue #4): the
|
|
92
213
|
* goal/progress/next text (already sanitized by the freeze gates) plus the
|
|
@@ -226,11 +347,27 @@ export interface TaskRecord {
|
|
|
226
347
|
* armed with the composed execution prompt as its objective. The session then
|
|
227
348
|
* keeps working automatic continuation rounds until the agent marks the goal
|
|
228
349
|
* complete, and this board settles the execution on the goal's own end
|
|
229
|
-
* instead of at the first turn end. Absent means
|
|
230
|
-
* by default, so only an explicit `
|
|
231
|
-
*
|
|
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.
|
|
232
355
|
*/
|
|
233
356
|
goalRun?: boolean;
|
|
357
|
+
/**
|
|
358
|
+
* Run this task's goal executions WITHOUT the board's acceptance gate.
|
|
359
|
+
*
|
|
360
|
+
* Absent means INHERIT: the execution's applicability is decided by the
|
|
361
|
+
* board-wide `goalVerification` switch frozen at start, exactly as a card
|
|
362
|
+
* that never touched this option behaves. Only an explicit `true` opts the
|
|
363
|
+
* card out, and the execution records `applicability: 'disabled'` so the
|
|
364
|
+
* report says plainly that nothing certified this run.
|
|
365
|
+
*
|
|
366
|
+
* The opt-out is per execution, not per run: flipping it never rewrites the
|
|
367
|
+
* verdict rule of an execution already open, and a rerun or a scheduled
|
|
368
|
+
* occurrence reads the card's current value when it freezes its contract.
|
|
369
|
+
*/
|
|
370
|
+
skipVerification?: boolean;
|
|
234
371
|
/**
|
|
235
372
|
* Frozen context snapshot for a continuation card; absent on plain tasks.
|
|
236
373
|
* Sanitized before it enters the ledger (redaction, slash-command taint,
|
|
@@ -262,6 +399,20 @@ export interface TaskRecord {
|
|
|
262
399
|
* absent means on-board.
|
|
263
400
|
*/
|
|
264
401
|
archivedAt?: number;
|
|
402
|
+
/**
|
|
403
|
+
* Opaque external-provider metadata, keyed by extension id; optional and
|
|
404
|
+
* additive so existing tasks require no ledger migration. The board stores
|
|
405
|
+
* and forwards it without interpreting the keys — each provider owns the
|
|
406
|
+
* shape and validation of its own entry.
|
|
407
|
+
*/
|
|
408
|
+
integrations?: Record<string, TaskBoardExtensionPayload>;
|
|
409
|
+
/**
|
|
410
|
+
* Whether the card is held off the board: it exists in the ledger (and its
|
|
411
|
+
* execution history is intact) but no column ever renders it. Providers
|
|
412
|
+
* create hidden cards for work they materialize but do not want displayed;
|
|
413
|
+
* absent means on-board.
|
|
414
|
+
*/
|
|
415
|
+
hidden?: boolean;
|
|
265
416
|
}
|
|
266
417
|
/**
|
|
267
418
|
* Statuses a task may be archived from: every status but `running`, whose
|
|
@@ -287,7 +438,7 @@ export interface NewTaskInput {
|
|
|
287
438
|
* inherits the parent's unset execution targets.
|
|
288
439
|
*/
|
|
289
440
|
parentId?: string;
|
|
290
|
-
/** Workspace the execution must run in; empty/absent = the recent workspace. */
|
|
441
|
+
/** Workspace the execution must run in; empty/absent = the creating session's workspace for a root task, else the recent workspace at execution time. */
|
|
291
442
|
workspaceId?: string;
|
|
292
443
|
/** Agent preset the execution session must be composed from; empty/absent = deployment default. */
|
|
293
444
|
mode?: string;
|
|
@@ -300,20 +451,31 @@ export interface NewTaskInput {
|
|
|
300
451
|
/** Run the subtree as an Agent Team (Team Lead session plus one teammate per direct subtask). */
|
|
301
452
|
teamRun?: boolean;
|
|
302
453
|
/**
|
|
303
|
-
* Start the execution with dsh's built-in `/goal`. Absent
|
|
304
|
-
*
|
|
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.
|
|
305
456
|
*/
|
|
306
457
|
goalRun?: boolean;
|
|
307
458
|
/**
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
|
|
311
|
-
|
|
459
|
+
* Run this task's goal executions without the acceptance gate; absent
|
|
460
|
+
* inherits the board-wide `goalVerification` switch.
|
|
461
|
+
*/
|
|
462
|
+
skipVerification?: boolean;
|
|
463
|
+
/**
|
|
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.
|
|
312
469
|
*/
|
|
313
470
|
schedule?: {
|
|
314
471
|
enabled: boolean;
|
|
315
|
-
|
|
472
|
+
mode?: ScheduleMode;
|
|
473
|
+
cron?: string;
|
|
474
|
+
/** Planned instant of a one-shot rule (ms epoch). */
|
|
475
|
+
at?: number;
|
|
316
476
|
timeZone?: string;
|
|
477
|
+
/** Run cap of a recurring rule; absent means unlimited. */
|
|
478
|
+
maxRuns?: number;
|
|
317
479
|
};
|
|
318
480
|
/**
|
|
319
481
|
* Optional frozen context snapshot (goal/progress/next, sanitized by the
|
|
@@ -333,13 +495,31 @@ export interface NewTaskInput {
|
|
|
333
495
|
* ahead of the execution prompt; a bare name changes nothing at run time.
|
|
334
496
|
*/
|
|
335
497
|
tags?: TaskTag[];
|
|
498
|
+
/** Opaque external-provider metadata keyed by extension id. */
|
|
499
|
+
integrations?: Record<string, TaskBoardExtensionPayload>;
|
|
500
|
+
/** Column the created card opens in; absent means the board default (todo). */
|
|
501
|
+
status?: TaskStatus;
|
|
502
|
+
/** Create the card off-board (held in the ledger, never rendered). */
|
|
503
|
+
hidden?: boolean;
|
|
336
504
|
}
|
|
337
505
|
/** The five kanban columns, in display order. */
|
|
338
506
|
export declare const COLUMNS: readonly {
|
|
339
507
|
status: TaskStatus;
|
|
340
508
|
label: string;
|
|
341
509
|
}[];
|
|
342
|
-
/**
|
|
510
|
+
/**
|
|
511
|
+
* Statuses a card may be moved to by hand: every column. The board UI and the
|
|
512
|
+
* agent tool surface share this one list, so no column can be manual on one
|
|
513
|
+
* surface and locked on the other.
|
|
514
|
+
*
|
|
515
|
+
* The column is a board statement, not a claim about a run. A hand-written
|
|
516
|
+
* `done`/`failed` declares that the work finished (or failed) outside a
|
|
517
|
+
* Host-run execution — human hands, an external system — and a hand-written
|
|
518
|
+
* `running` says the work is under way without a tracked session. None of them
|
|
519
|
+
* creates an {@link ExecutionRecord}, which is what keeps a declaration
|
|
520
|
+
* distinguishable from a recorded outcome in the card's execution history; the
|
|
521
|
+
* next settled run overwrites the column with the real one.
|
|
522
|
+
*/
|
|
343
523
|
export declare const MANUAL_STATUSES: readonly TaskStatus[];
|
|
344
524
|
/** Statuses the runner may move a card to from 'running'. */
|
|
345
525
|
export declare const RUNNER_SETTLE_STATUSES: readonly TaskStatus[];
|
|
@@ -347,8 +527,28 @@ export declare const RUNNER_SETTLE_STATUSES: readonly TaskStatus[];
|
|
|
347
527
|
export declare const ALL_STATUSES: readonly TaskStatus[];
|
|
348
528
|
/** Brand an unknown string as a status; undefined when it is not one. */
|
|
349
529
|
export declare function isTaskStatus(value: unknown): value is TaskStatus;
|
|
350
|
-
/**
|
|
530
|
+
/**
|
|
531
|
+
* Whether the manual move `from` -> `to` is a legal *column* change: any
|
|
532
|
+
* column, and never the one the card already shows.
|
|
533
|
+
*
|
|
534
|
+
* Whether the card may be moved at all is a separate question. The lock an
|
|
535
|
+
* executing card carries belongs to its open execution, never to the column
|
|
536
|
+
* text, so it is {@link canMoveTask} — not this predicate — that expresses it.
|
|
537
|
+
*/
|
|
351
538
|
export declare function canMoveManually(from: TaskStatus, to: TaskStatus): boolean;
|
|
539
|
+
/**
|
|
540
|
+
* Whether the board may move `task` to `to` by hand: the card is on-board,
|
|
541
|
+
* holds no open execution, and the target is a legal column change. The ledger,
|
|
542
|
+
* the detail view's status buttons and the board's drop handler all decide
|
|
543
|
+
* through this one predicate, so no two surfaces can disagree about a card.
|
|
544
|
+
*/
|
|
545
|
+
export declare function canMoveTask(task: TaskRecord, to: TaskStatus): boolean;
|
|
546
|
+
/**
|
|
547
|
+
* Whether the task still has an execution the runner has not settled. This —
|
|
548
|
+
* not the `running` column — is what "the runner owns this card" means: a card
|
|
549
|
+
* parked in `running` by hand has no session and stays fully movable.
|
|
550
|
+
*/
|
|
551
|
+
export declare function hasOpenExecution(task: TaskRecord): boolean;
|
|
352
552
|
/** Normalize one optional execution-target string: trim; blank collapses to undefined. */
|
|
353
553
|
export declare function normalizeTargetId(value: string | undefined): string | undefined;
|
|
354
554
|
/**
|
|
@@ -367,7 +567,8 @@ export declare function withStatus(task: TaskRecord, status: TaskStatus, now: nu
|
|
|
367
567
|
* Merge a schedule patch into a task's schedule rule (creating it when
|
|
368
568
|
* absent), with a fresh updatedAt. Keys present in the patch overwrite the
|
|
369
569
|
* current value — including explicit `undefined`, which clears a field (used
|
|
370
|
-
* to disarm `nextRunAt`); absent keys keep
|
|
570
|
+
* to disarm `nextRunAt` or to drop an ended/skip record); absent keys keep
|
|
571
|
+
* their current value.
|
|
371
572
|
*/
|
|
372
573
|
export declare function withSchedule(task: TaskRecord, patch: Partial<ScheduleRule>, now: number): TaskRecord;
|
|
373
574
|
/**
|
|
@@ -380,6 +581,16 @@ export declare function startExecution(task: TaskRecord, now: number, executionI
|
|
|
380
581
|
task: TaskRecord;
|
|
381
582
|
execution: ExecutionRecord;
|
|
382
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;
|
|
383
594
|
/**
|
|
384
595
|
* Settle a running execution: record the outcome and move the task into the
|
|
385
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[];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Label-management use cases (issue: labels could not be cleaned up).
|
|
3
|
+
*
|
|
4
|
+
* A label lives on every task that carries it, so renaming or removing one is a
|
|
5
|
+
* ledger-wide edit, not a task edit: the board's label row is the union of the
|
|
6
|
+
* labels in use, and without these two transitions a label created by an
|
|
7
|
+
* afternoon experiment stayed on the board forever.
|
|
8
|
+
*
|
|
9
|
+
* Both transitions are pure ledger rewrites (no persistence, no notify — the
|
|
10
|
+
* controller and the Host ledger orchestrate those), and both are idempotent:
|
|
11
|
+
* a label that no task carries changes nothing and reports no error for the
|
|
12
|
+
* delete path (the desired end state already holds), while a rename from an
|
|
13
|
+
* unknown label is refused so a stale tab cannot silently do nothing.
|
|
14
|
+
*
|
|
15
|
+
* Renaming onto a name that is already in use MERGES the two: a task carrying
|
|
16
|
+
* both keeps the row it already had (with its execution hint) and loses the
|
|
17
|
+
* duplicate, which is the only reading that cannot lose a hint the user typed.
|
|
18
|
+
*/
|
|
19
|
+
import { type TaskRecord } from '../tasks.ts';
|
|
20
|
+
/** Outcome of one ledger-wide label edit. */
|
|
21
|
+
export interface TagEditResult {
|
|
22
|
+
/** The ledger after the edit, or the input unchanged when nothing applied. */
|
|
23
|
+
tasks: readonly TaskRecord[];
|
|
24
|
+
/** Whether any task actually changed (drives whether the caller persists). */
|
|
25
|
+
changed: boolean;
|
|
26
|
+
/** Refusal reason; the ledger is unchanged when set. */
|
|
27
|
+
error?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Rename one label everywhere, merging into an existing label of the target
|
|
31
|
+
* name. Every touched task gets a fresh updatedAt; untouched tasks keep theirs.
|
|
32
|
+
* @param tasks - current ledger.
|
|
33
|
+
* @param from - the label to rename.
|
|
34
|
+
* @param to - the new name (an existing name merges the two labels).
|
|
35
|
+
* @param now - clock instant (ms epoch).
|
|
36
|
+
* @returns the rewritten ledger, or a refusal.
|
|
37
|
+
*/
|
|
38
|
+
export declare function applyRenameTag(tasks: readonly TaskRecord[], from: string, to: string, now: number): TagEditResult;
|
|
39
|
+
/**
|
|
40
|
+
* Remove one label from every task that carries it. Archived cards are included:
|
|
41
|
+
* the board's label row draws from the whole ledger, so a label left on an
|
|
42
|
+
* archived card would come straight back into the filter.
|
|
43
|
+
* @param tasks - current ledger.
|
|
44
|
+
* @param name - the label to remove.
|
|
45
|
+
* @param now - clock instant (ms epoch).
|
|
46
|
+
* @returns the rewritten ledger.
|
|
47
|
+
*/
|
|
48
|
+
export declare function applyDeleteTag(tasks: readonly TaskRecord[], name: string, now: number): TagEditResult;
|
|
@@ -15,7 +15,7 @@ import type { TaskHandoverInput } from '../handover.ts';
|
|
|
15
15
|
* Editable fields on a task (the update patch surface). `freeze` replaces the
|
|
16
16
|
* continuation-card snapshot (restamping frozenAt); an explicit null clears it.
|
|
17
17
|
*/
|
|
18
|
-
export type TaskUpdatePatch = Partial<Pick<TaskRecord, 'title' | 'description' | 'prompt' | 'workspaceId' | 'mode' | 'permission' | 'model' | 'reuseSession' | 'teamRun' | 'goalRun'>> & {
|
|
18
|
+
export type TaskUpdatePatch = Partial<Pick<TaskRecord, 'title' | 'description' | 'prompt' | 'workspaceId' | 'mode' | 'permission' | 'model' | 'reuseSession' | 'teamRun' | 'goalRun' | 'skipVerification'>> & {
|
|
19
19
|
freeze?: FreezeSnapshot & {
|
|
20
20
|
redacted?: boolean;
|
|
21
21
|
} | null;
|
|
@@ -39,8 +39,10 @@ export declare const TASK_CONTENT_FIELDS: readonly ["title", "description", "pro
|
|
|
39
39
|
export declare function hasContentPatch(patch: TaskUpdatePatch): boolean;
|
|
40
40
|
/**
|
|
41
41
|
* Whether a task's content may still be edited: the task must be on-board
|
|
42
|
-
* (not archived) and must never have started executing. Fail-closed: a
|
|
43
|
-
* running, settled, or cancelled
|
|
42
|
+
* (not archived) and must never have started executing. Fail-closed: a task
|
|
43
|
+
* with any execution record — running, settled, or cancelled before launch —
|
|
44
|
+
* keeps its content fixed. The run, not the column, is the trigger: a card
|
|
45
|
+
* parked in 'running' by hand has nothing recorded yet and stays editable.
|
|
44
46
|
*/
|
|
45
47
|
export declare function canEditTaskContent(task: TaskRecord): boolean;
|
|
46
48
|
/**
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** Judge calls one acceptance issues: every criterion, every round. */
|
|
2
|
+
export declare const ACCEPTANCE_JUDGE_CALLS: number;
|
|
3
|
+
/**
|
|
4
|
+
* Per-judge-call ceiling used when the row names none: two and a half minutes
|
|
5
|
+
* for a reasoning model reading an 80,000 character trace with a 16,384 token
|
|
6
|
+
* output cap. The former 120s constant was tight enough that a slow but healthy
|
|
7
|
+
* route produced a wall of per-call timeouts, and every one of them was charged
|
|
8
|
+
* as an acceptance anomaly against a budget that then failed the card.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_VERIFICATION_CALL_TIMEOUT_SECONDS = 150;
|
|
11
|
+
/** Shortest accepted per-call ceiling: below this no useful judge answers. */
|
|
12
|
+
export declare const VERIFICATION_CALL_TIMEOUT_MIN_SECONDS = 30;
|
|
13
|
+
/** Longest accepted per-call ceiling, ten minutes. */
|
|
14
|
+
export declare const VERIFICATION_CALL_TIMEOUT_MAX_SECONDS = 600;
|
|
15
|
+
/**
|
|
16
|
+
* Total ceiling of one acceptance attempt, covering evidence rendering and
|
|
17
|
+
* every judge call including retries. Derived from the per-call default rather
|
|
18
|
+
* than pinned, because a total budget smaller than
|
|
19
|
+
* {@link ACCEPTANCE_JUDGE_CALLS} times the per-call ceiling guarantees that no
|
|
20
|
+
* acceptance can ever finish.
|
|
21
|
+
*/
|
|
22
|
+
export declare const DEFAULT_VERIFICATION_BUDGET_SECONDS: number;
|
|
23
|
+
/** Shortest accepted total budget: long enough for at least one judge call. */
|
|
24
|
+
export declare const VERIFICATION_BUDGET_MIN_SECONDS = 120;
|
|
25
|
+
/** Longest accepted total budget, thirty minutes. */
|
|
26
|
+
export declare const VERIFICATION_BUDGET_MAX_SECONDS = 1800;
|
|
27
|
+
/** The default total budget in milliseconds, the face the abort timer takes. */
|
|
28
|
+
export declare const DEFAULT_VERIFICATION_BUDGET_MS: number;
|
|
29
|
+
/**
|
|
30
|
+
* The effective per-judge-call ceiling.
|
|
31
|
+
* @param seconds - configured seconds, or undefined for the default.
|
|
32
|
+
* @returns the effective ceiling in seconds.
|
|
33
|
+
*/
|
|
34
|
+
export declare function normalizeVerificationCallTimeoutSeconds(seconds: number | undefined): number;
|
|
35
|
+
/**
|
|
36
|
+
* The effective per-judge-call ceiling the timer face takes.
|
|
37
|
+
* @param seconds - configured seconds, or undefined for the default.
|
|
38
|
+
* @returns the effective ceiling in milliseconds.
|
|
39
|
+
*/
|
|
40
|
+
export declare function verificationCallTimeoutMs(seconds: number | undefined): number;
|
|
41
|
+
/**
|
|
42
|
+
* The effective total budget of one acceptance attempt.
|
|
43
|
+
* @param seconds - configured seconds, or undefined for the default.
|
|
44
|
+
* @returns the effective budget in seconds.
|
|
45
|
+
*/
|
|
46
|
+
export declare function normalizeVerificationBudgetSeconds(seconds: number | undefined): number;
|
|
47
|
+
/**
|
|
48
|
+
* The effective total budget the abort face takes.
|
|
49
|
+
* @param seconds - configured seconds, or undefined for the default.
|
|
50
|
+
* @returns the effective budget in milliseconds.
|
|
51
|
+
*/
|
|
52
|
+
export declare function verificationBudgetMs(seconds: number | undefined): number;
|