@linxin666/dsh-client-ui-task-board 0.4.3 → 0.4.5

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.
Files changed (128) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +56 -18
  3. package/README.zh.md +56 -18
  4. package/lib/client.js +6242 -958
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +3475 -234
  7. package/lib/types/client/PluginSettingsCard.d.ts +7 -0
  8. package/lib/types/client/TaskBoardSettingsCard.d.ts +19 -3
  9. package/lib/types/client/board/ClampedMarkdown.d.ts +3 -0
  10. package/lib/types/client/board/ConfirmDialog.d.ts +4 -1
  11. package/lib/types/client/board/EditTaskModal.d.ts +5 -2
  12. package/lib/types/client/board/LinkSubtaskModal.d.ts +5 -2
  13. package/lib/types/client/board/NewTaskModal.d.ts +4 -1
  14. package/lib/types/client/board/TagManagerModal.d.ts +26 -0
  15. package/lib/types/client/board/TaskBoard.d.ts +10 -1
  16. package/lib/types/client/board/TaskCard.d.ts +14 -1
  17. package/lib/types/client/board/TaskDetail.d.ts +3 -1
  18. package/lib/types/client/board/TaskForm.d.ts +23 -2
  19. package/lib/types/client/board/VerificationReport.d.ts +23 -0
  20. package/lib/types/client/board/card-view.d.ts +113 -0
  21. package/lib/types/client/board/icons.d.ts +32 -0
  22. package/lib/types/client/board/overlay.d.ts +69 -0
  23. package/lib/types/client/board/preset-label.d.ts +22 -0
  24. package/lib/types/client/board/status-key.d.ts +15 -0
  25. package/lib/types/client/board/task-markdown.d.ts +19 -0
  26. package/lib/types/client/board-card-seat.d.ts +34 -0
  27. package/lib/types/client/host-api.d.ts +24 -1
  28. package/lib/types/client/index.d.ts +19 -0
  29. package/lib/types/client/locales.d.ts +141 -3
  30. package/lib/types/client/native-panel.d.ts +6 -20
  31. package/lib/types/client/schedule-zone.d.ts +47 -0
  32. package/lib/types/client/seats.d.ts +18 -0
  33. package/lib/types/client/service.d.ts +32 -0
  34. package/lib/types/client/settings-form.d.ts +16 -0
  35. package/lib/types/core/controller.d.ts +38 -1
  36. package/lib/types/core/degraded-reason.d.ts +46 -0
  37. package/lib/types/core/extension.d.ts +283 -0
  38. package/lib/types/core/handover.d.ts +13 -1
  39. package/lib/types/core/issue-form.d.ts +16 -0
  40. package/lib/types/core/package-name.d.ts +9 -0
  41. package/lib/types/core/schedule.d.ts +95 -20
  42. package/lib/types/core/subtask.d.ts +30 -11
  43. package/lib/types/core/tasks.d.ts +110 -5
  44. package/lib/types/core/use-cases/task-create.d.ts +2 -1
  45. package/lib/types/core/use-cases/task-schedule.d.ts +8 -4
  46. package/lib/types/core/use-cases/task-tag.d.ts +48 -0
  47. package/lib/types/core/use-cases/task-update.d.ts +5 -3
  48. package/lib/types/core/verification.d.ts +386 -0
  49. package/lib/types/core/workspace-target.d.ts +41 -0
  50. package/lib/types/host/agent-tools.d.ts +1 -1
  51. package/lib/types/host/extension-registry.d.ts +111 -0
  52. package/lib/types/host/llm-dispatch.d.ts +63 -0
  53. package/lib/types/host/verification-gate.d.ts +71 -0
  54. package/lib/types/host/verification-runner.d.ts +108 -0
  55. package/lib/types/host/workspace-evidence.d.ts +53 -0
  56. package/lib/types/host-ledger.d.ts +107 -11
  57. package/lib/types/host-runner.d.ts +99 -4
  58. package/lib/types/host-service.d.ts +114 -2
  59. package/lib/types/index.d.ts +80 -26
  60. package/lib/types/protocol.d.ts +53 -3
  61. package/lib/types/tool-surface.d.ts +71 -0
  62. package/package.json +27 -26
  63. package/src/client/PluginSettingsCard.tsx +9 -0
  64. package/src/client/TaskBoardSettingsCard.tsx +304 -65
  65. package/src/client/board/ClampedMarkdown.tsx +69 -0
  66. package/src/client/board/ConfirmDialog.tsx +7 -3
  67. package/src/client/board/EditTaskModal.tsx +5 -2
  68. package/src/client/board/LinkSubtaskModal.tsx +30 -15
  69. package/src/client/board/NewTaskModal.tsx +288 -151
  70. package/src/client/board/TagManagerModal.tsx +235 -0
  71. package/src/client/board/TaskBoard.tsx +259 -103
  72. package/src/client/board/TaskCard.tsx +238 -86
  73. package/src/client/board/TaskDetail.tsx +154 -59
  74. package/src/client/board/TaskForm.tsx +61 -6
  75. package/src/client/board/VerificationReport.tsx +203 -0
  76. package/src/client/board/card-view.ts +220 -0
  77. package/src/client/board/icons.tsx +89 -0
  78. package/src/client/board/overlay.tsx +189 -0
  79. package/src/client/board/preset-label.ts +44 -0
  80. package/src/client/board/status-key.ts +27 -0
  81. package/src/client/board/task-markdown.module.css +137 -0
  82. package/src/client/board/task-markdown.tsx +106 -0
  83. package/src/client/board-card-seat.ts +102 -0
  84. package/src/client/board-settings.module.css +66 -0
  85. package/src/client/board.module.css +602 -68
  86. package/src/client/host-api.ts +109 -6
  87. package/src/client/index.ts +48 -6
  88. package/src/client/locales.ts +290 -12
  89. package/src/client/native-panel.tsx +18 -2
  90. package/src/client/schedule-zone.ts +135 -0
  91. package/src/client/seats.tsx +58 -0
  92. package/src/client/service.ts +94 -0
  93. package/src/client/settings-card.module.css +31 -5
  94. package/src/client/settings-form.ts +27 -1
  95. package/src/core/controller.ts +101 -5
  96. package/src/core/degraded-reason.ts +81 -0
  97. package/src/core/extension.ts +376 -0
  98. package/src/core/freeze-snapshot.ts +2 -2
  99. package/src/core/handover.ts +16 -1
  100. package/src/core/issue-form.ts +19 -0
  101. package/src/core/package-name.ts +9 -0
  102. package/src/core/schedule.ts +211 -55
  103. package/src/core/store.ts +26 -1
  104. package/src/core/subtask.ts +77 -22
  105. package/src/core/tasks.ts +138 -10
  106. package/src/core/use-cases/task-create.ts +14 -2
  107. package/src/core/use-cases/task-parent.ts +3 -2
  108. package/src/core/use-cases/task-schedule.ts +17 -6
  109. package/src/core/use-cases/task-tag.ts +110 -0
  110. package/src/core/use-cases/task-update.ts +13 -4
  111. package/src/core/verification.ts +826 -0
  112. package/src/core/workspace-target.ts +59 -0
  113. package/src/host/agent-tools.ts +58 -24
  114. package/src/host/extension-registry.ts +509 -0
  115. package/src/host/llm-dispatch.ts +87 -0
  116. package/src/host/verification-gate.ts +331 -0
  117. package/src/host/verification-runner.ts +535 -0
  118. package/src/host/workspace-evidence.ts +176 -0
  119. package/src/host-ai.ts +15 -9
  120. package/src/host-ledger.ts +342 -51
  121. package/src/host-routes.ts +15 -2
  122. package/src/host-runner.ts +219 -20
  123. package/src/host-service.ts +346 -26
  124. package/src/index.ts +204 -16
  125. package/src/protocol.ts +122 -9
  126. package/src/tool-surface.ts +95 -0
  127. package/lib/types/host/run-guarded.d.ts +0 -39
  128. package/src/host/run-guarded.ts +0 -76
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Goal acceptance gate: the board's evaluator-backed certification of a goal
3
+ * completion claim.
4
+ *
5
+ * The gate sits on the official tool pre-execute lifecycle and intercepts the
6
+ * one call that ends a goal run — `update_goal` with `action: 'complete'`
7
+ * — BEFORE it takes effect. This is why the board does not ask the agent to
8
+ * verify itself: the tool call that finishes the goal is refused until a
9
+ * matching acceptance pass record exists, so no prompt wording and no
10
+ * self-report can route around it.
11
+ *
12
+ * Semantics owned here:
13
+ * - one acceptance cycle per EXECUTION, spent at most twice: the first failure
14
+ * returns its findings to the fixing agent, the second ends the cycle;
15
+ * - a repeated completion call during a repair, a new automatic goal round, a
16
+ * plugin reload and a Host restart all reuse the same cycle rather than
17
+ * resetting it (the budget is persisted on the execution record);
18
+ * - an anomaly (timeout, authentication, unparseable answer, an unusable judge
19
+ * route) is recorded separately from a quality verdict and is never treated
20
+ * as one;
21
+ * - concurrent completion calls for one execution share one acceptance.
22
+ */
23
+ import type { PreToolDecision } from '@deepseek-ai/dsh-tools';
24
+ import type { LlmRuntime } from '@deepseek-ai/dsh-llm';
25
+ import { type WorkspaceChangeSource } from './workspace-evidence.ts';
26
+ import type { HostTaskLedger } from '../host-ledger.ts';
27
+ /** The subset of the official goal service this gate drives. */
28
+ export interface GoalFace {
29
+ get(agent: unknown): {
30
+ id: string;
31
+ revision: number;
32
+ objective: string;
33
+ } | undefined;
34
+ block(agent: unknown, ref: {
35
+ id: string;
36
+ revision: number;
37
+ }, reason: {
38
+ code: string;
39
+ message: string;
40
+ }): unknown;
41
+ }
42
+ /** Everything the gate needs from its owner. */
43
+ export interface GoalVerificationGateDeps {
44
+ ledger: HostTaskLedger;
45
+ /** Resolve the optional model runtime lazily (it may activate later). */
46
+ llm?: () => LlmRuntime | undefined;
47
+ /** Resolve the optional goal service lazily. */
48
+ goals?: () => GoalFace | undefined;
49
+ /**
50
+ * Resolve the optional host change service lazily. Absent (or a deployment
51
+ * that serves none) simply means the judge sees the trajectory alone.
52
+ */
53
+ workspaceChanges?: () => WorkspaceChangeSource | undefined;
54
+ logger: {
55
+ warn(message: string, ...rest: unknown[]): void;
56
+ };
57
+ now?: () => number;
58
+ }
59
+ /** The shape of one tool pre-execution the gate inspects. */
60
+ export interface GateExecution {
61
+ name: string;
62
+ arguments: unknown;
63
+ agent?: unknown;
64
+ signal: AbortSignal;
65
+ }
66
+ /**
67
+ * Create the goal acceptance gate.
68
+ * @param deps - the ledger, the model runtime, the goal service and the settings.
69
+ * @returns the `tools/pre-execute` listener body (without the waterfall next()).
70
+ */
71
+ export declare function createGoalVerificationGate(deps: GoalVerificationGateDeps): (exec: GateExecution) => Promise<PreToolDecision | undefined>;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Goal acceptance runner: evidence assembly and the judge calls of one
3
+ * acceptance attempt.
4
+ *
5
+ * Lives in the host half because it needs the live session event log and the
6
+ * official `llm` service; every rule it applies (criteria, scale, tags,
7
+ * thresholds, budgets, caps) comes from `../core/verification.ts`, which mirrors
8
+ * the installed dsh-llm-verifier 0.8.4 default final acceptance.
9
+ */
10
+ import { type LlmRuntime } from '@deepseek-ai/dsh-llm';
11
+ import { type VerificationAttempt, type VerificationRoute, type VerificationUsage } from '../core/verification.ts';
12
+ /** Largest trace handed to the judge, mirroring the verifier's default cap. */
13
+ export declare const VERIFICATION_MAX_TRACE_CHARS = 80000;
14
+ /** Largest single trace entry; one huge tool result must not eat the budget. */
15
+ export declare const VERIFICATION_MAX_ENTRY_CHARS = 4000;
16
+ /** Largest per-finding text kept from one judge answer. */
17
+ export declare const VERIFICATION_MAX_FINDING_CHARS = 400;
18
+ /** Findings kept per judge answer and per acceptance. */
19
+ export declare const VERIFICATION_MAX_FINDINGS_PER_CALL = 3;
20
+ export declare const VERIFICATION_MAX_FINDINGS = 6;
21
+ /** One judge request's ceiling and its retry budget. */
22
+ export declare const VERIFICATION_CALL_TIMEOUT_MS = 120000;
23
+ export declare const VERIFICATION_MAX_TOKENS = 16384;
24
+ export declare const VERIFICATION_TEMPERATURE = 0.2;
25
+ export declare const VERIFICATION_REQUEST_ATTEMPTS = 3;
26
+ /** Redaction patterns, mirrored from the verifier's defaults. */
27
+ export declare const VERIFICATION_REDACT_PATTERNS: readonly string[];
28
+ /** Why one acceptance could not produce a verdict. */
29
+ export type VerificationErrorKind = 'route-unavailable' | 'timeout' | 'auth' | 'parse' | 'request' | 'aborted';
30
+ /** An acceptance anomaly: never a quality verdict, always recorded as one. */
31
+ export declare class VerificationError extends Error {
32
+ readonly kind: VerificationErrorKind;
33
+ readonly usage: VerificationUsage;
34
+ constructor(kind: VerificationErrorKind, message: string, usage?: VerificationUsage, options?: {
35
+ cause?: unknown;
36
+ });
37
+ }
38
+ /** Fresh zeroed usage counters. */
39
+ export declare function emptyUsage(): VerificationUsage;
40
+ /** Structural view of one session event, as the host log carries it. */
41
+ interface RawEvent {
42
+ type?: unknown;
43
+ seq?: unknown;
44
+ time?: unknown;
45
+ data?: unknown;
46
+ }
47
+ /** Read one session's events across the shapes the SDK cohort exposes. */
48
+ export declare function sessionEvents(session: unknown): readonly RawEvent[];
49
+ /** Evidence assembled for one acceptance attempt. */
50
+ export interface AcceptanceEvidence {
51
+ problem: string;
52
+ trace: string;
53
+ entries: number;
54
+ fromSeq?: number;
55
+ toSeq?: number;
56
+ omittedCharacters: number;
57
+ hash: string;
58
+ }
59
+ /**
60
+ * Assemble the evidence one acceptance judges.
61
+ *
62
+ * The window starts at the execution's own `startedAt`, never at the session's
63
+ * first event: a reused session carries the history of earlier executions, and
64
+ * those runs must not be judged as this one's work. Only observed output is
65
+ * rendered — tool calls with their arguments and their results, assistant
66
+ * prose, goal round markers and team messages — and the result is redacted and
67
+ * capped before it ever reaches a judge.
68
+ * @param session - the live session of the execution.
69
+ * @param problem - the execution's composed prompt, shown as the task statement.
70
+ * @param startedAt - the instant the execution opened (ms epoch).
71
+ * @returns the bounded, redacted evidence.
72
+ */
73
+ /**
74
+ * The session events inside one execution's own window.
75
+ *
76
+ * A reused session carries the history of earlier executions; only the events
77
+ * at or after this execution's start belong to the work being judged.
78
+ * @param session - the live session.
79
+ * @param startedAt - the execution's start instant (ms epoch).
80
+ * @returns the events of this execution, oldest first.
81
+ */
82
+ export declare function evidenceEvents(session: unknown, startedAt: number): readonly RawEvent[];
83
+ export declare function collectEvidence(session: unknown, problem: string, startedAt: number): AcceptanceEvidence;
84
+ /** The verdict of one acceptance attempt. */
85
+ export interface AcceptanceResult {
86
+ attempt: VerificationAttempt;
87
+ }
88
+ /**
89
+ * Run one acceptance: every criterion is judged twice, the odd round with the
90
+ * A/B slots swapped, and the per-round scores are mapped back and averaged per
91
+ * criterion.
92
+ * @param input - the judge route, evidence, threshold and cancellation.
93
+ * @returns the recorded attempt (quality verdict or anomaly).
94
+ */
95
+ export declare function runAcceptance(input: {
96
+ llm: LlmRuntime;
97
+ route: VerificationRoute;
98
+ evidence: AcceptanceEvidence;
99
+ threshold: number;
100
+ index: number;
101
+ signal: AbortSignal;
102
+ /** Host-observed workspace changes, rendered as the prompt's reference context. */
103
+ context?: string;
104
+ /** How many changed files that context shows. */
105
+ workspaceFiles?: number;
106
+ now?: () => number;
107
+ }): Promise<AcceptanceResult>;
108
+ export {};
@@ -0,0 +1,53 @@
1
+ /** The subset of the host change service this module drives. */
2
+ export interface WorkspaceChangeSource {
3
+ summary(sessionId: string, seq: number): unknown;
4
+ diff(sessionId: string, seq: number, index: number, signal: AbortSignal): Promise<unknown>;
5
+ }
6
+ /** One session event, as the host log carries it. */
7
+ interface RawEvent {
8
+ type?: unknown;
9
+ seq?: unknown;
10
+ }
11
+ /** How many changed files one evidence block renders. */
12
+ export declare const MAX_WORKSPACE_FILES = 8;
13
+ /** Character ceiling for the whole rendered block. */
14
+ export declare const WORKSPACE_EVIDENCE_MAX_CHARS = 24000;
15
+ /** Character ceiling for one file's rendered comparison. */
16
+ export declare const WORKSPACE_EVIDENCE_MAX_FILE_CHARS = 6000;
17
+ /**
18
+ * Resolve the optional host change service from a context-like object.
19
+ * @param ctx - anything exposing @@get(name)@@.
20
+ * @returns the service, or undefined when this deployment serves none.
21
+ */
22
+ export declare function probeWorkspaceChanges(ctx: {
23
+ get(name: string): unknown;
24
+ }): WorkspaceChangeSource | undefined;
25
+ /** The sequence of the newest change event inside the reviewed window. */
26
+ export declare function latestChangeEvent(events: readonly RawEvent[]): {
27
+ seq: number;
28
+ turn?: number;
29
+ } | undefined;
30
+ /**
31
+ * Render the host's own record of what the reviewed window changed.
32
+ *
33
+ * Bounded twice — at most {@link MAX_WORKSPACE_FILES} files, and a shared
34
+ * character budget split across them — so one enormous generated file cannot
35
+ * crowd out every other change. Every failure degrades to less evidence (or an
36
+ * empty block) instead of an error, because the acceptance being prepared must
37
+ * still reach a verdict.
38
+ * @param events - session events already bounded to the execution's window.
39
+ * @param sessionId - the session whose changes these are.
40
+ * @param source - the probed host service.
41
+ * @param options - budget, cancellation and the diagnostic sink.
42
+ * @returns the rendered block plus how many files it shows.
43
+ */
44
+ export declare function renderWorkspaceEvidence(events: readonly RawEvent[], sessionId: string, source: WorkspaceChangeSource, options: {
45
+ signal: AbortSignal;
46
+ warn?: (message: string) => void;
47
+ maxChars?: number;
48
+ }): Promise<{
49
+ text: string;
50
+ files: number;
51
+ hash: string;
52
+ }>;
53
+ export {};
@@ -1,5 +1,6 @@
1
1
  import { type ExecutionOutcome, type ExecutionRecord, type TaskRecord } from './core/tasks.ts';
2
2
  import { type TaskBoardAction, type TaskBoardSchedulerSnapshot } from './protocol.ts';
3
+ import type { ExecutionVerification } from './core/verification.ts';
3
4
  import { type TaskPermission } from './core/handover.ts';
4
5
  export interface LedgerState {
5
6
  revision: number;
@@ -23,11 +24,20 @@ export interface OpenExecutionReference {
23
24
  readonly executionId: string;
24
25
  readonly sessionId: string | undefined;
25
26
  readonly startedAt: number;
27
+ /**
28
+ * True for a member of a team-mode run other than its Lead. A teammate is a
29
+ * durable member of the Team: it stays alive after its turn ends, so the
30
+ * roster may keep reporting its session as running, and the turn it completed
31
+ * is the only verdict it exposes.
32
+ */
33
+ readonly teamMember: boolean;
26
34
  }
27
35
  /** Minimal value copy used by the Host scheduler. */
28
36
  export interface DueScheduleReference {
29
37
  readonly taskId: string;
30
38
  readonly cron: string;
39
+ /** Zone this rule's wall clock is read in. */
40
+ readonly timeZone: string;
31
41
  readonly nextRunAt: number;
32
42
  }
33
43
  /** Derived runtime data for one session-poll pass. */
@@ -80,8 +90,14 @@ export declare class HostTaskLedger {
80
90
  readonly lockFile: string;
81
91
  /** Small sidecar for the 30 s scheduler heartbeat (lastTickAt only). */
82
92
  readonly schedulerFile: string;
83
- /** Session-default permission the confirmation gate compares against. */
84
- readonly sessionDefaultPermission: TaskPermission;
93
+ /** Resolves the baseline the confirmation gate compares a binding against. */
94
+ private readonly sessionDefault;
95
+ /**
96
+ * Session-default permission the confirmation gate compares against. Read
97
+ * through the resolver on every use, so a baseline that follows the Host's
98
+ * own default preset tracks a Settings change made while the board runs.
99
+ */
100
+ get sessionDefaultPermission(): TaskPermission;
85
101
  /**
86
102
  * Deployment subtask depth limit (1..3): the lineage gate every write obeys.
87
103
  * The settings card edits it live, so {@link setMaxSubtaskDepth} mutates it
@@ -89,7 +105,7 @@ export declare class HostTaskLedger {
89
105
  */
90
106
  private depthLimit;
91
107
  constructor(dir?: string, now?: () => number, options?: {
92
- sessionDefaultPermission?: TaskPermission;
108
+ sessionDefaultPermission?: TaskPermission | (() => TaskPermission);
93
109
  maxSubtaskDepth?: number;
94
110
  });
95
111
  /** Current subtask depth limit (1..3). */
@@ -149,16 +165,21 @@ export declare class HostTaskLedger {
149
165
  */
150
166
  settle(taskId: string, executionId: string, outcome: ExecutionOutcome, error?: string): void;
151
167
  attachSession(taskId: string, executionId: string, sessionId: string): void;
168
+ allTasks(): readonly TaskRecord[];
169
+ getTask(id: string): TaskRecord | undefined;
170
+ saveTaskRecord(task: TaskRecord): void;
152
171
  private apply;
153
172
  /**
154
173
  * The binding that makes a run illegal, if any.
155
174
  *
156
175
  * A plain cascade launches one session per participant, so every participant
157
176
  * carries its own resolved binding and each one gates the run. A team run
158
- * launches only the Lead session: the Lead's binding gates it, while a
159
- * subtask's OWN above-default pin cannot be applied to a teammate and is
160
- * refused instead of being silently dropped (an inherited binding is the
161
- * Lead's own and stays allowed once the Lead is confirmed).
177
+ * launches only the Lead session: the Lead's binding gates it, and a
178
+ * teammate inherits that session's permission, so a subtask's OWN pin is
179
+ * refused exactly when the Lead session does not already carry it — the
180
+ * teammate could never be given that authority, and dropping the pin
181
+ * silently would misreport the work (an inherited binding is the Lead's own
182
+ * and stays allowed once the Lead is confirmed).
162
183
  * @param root - the task being run.
163
184
  * @returns the first refusal, or undefined when the run may start.
164
185
  */
@@ -191,15 +212,90 @@ export declare class HostTaskLedger {
191
212
  * (failure dominates, then cancellation).
192
213
  */
193
214
  private settleCascade;
215
+ /**
216
+ * Close every run that is already decided, and fold every lineage whose
217
+ * members are all settled. The Host poll calls this on every tick, so a run
218
+ * can no longer be left in the running column by a settle nobody observed:
219
+ *
220
+ * - A team run is governed by its Lead. Once the Lead's own outcome is
221
+ * recorded, its still-open members are settled with that verdict. A
222
+ * teammate is a durable member of the Team and may never report a turn of
223
+ * its own, and without this the whole lineage waits on it forever.
224
+ * - A cascade parent whose members are all settled is finalized here even
225
+ * when the settle that completed the tree happened in another process, or
226
+ * was interrupted between its child write and its parent write.
227
+ *
228
+ * Idempotent: a run that is already folded is left untouched, so a caller may
229
+ * invoke it on every tick.
230
+ * @param persist - false to leave the document for the caller to commit.
231
+ * @returns whether the document changed.
232
+ */
233
+ finalizeReadyRuns(persist?: boolean): boolean;
234
+ /**
235
+ * The verdict a team run's Lead has already recorded: its own turn outcome
236
+ * once that turn ended, or the folded outcome once its card settled.
237
+ */
238
+ private leadVerdict;
239
+ /**
240
+ * Settle every still-open member of a team run with the Lead's verdict.
241
+ * A teammate that already reported its own outcome keeps it: the ordinary
242
+ * fold still lets a failure dominate.
243
+ * @param leadId - the Lead card's task id (never settled here).
244
+ * @param groupId - the team run's group.
245
+ * @param verdict - the Lead's outcome, inherited by members that never reported.
246
+ * @param error - the Lead's failure text, inherited with a failed verdict.
247
+ * @param now - clock instant (ms epoch).
248
+ * @returns whether the document changed.
249
+ */
250
+ private closeTeamMembers;
194
251
  private repairSchedules;
195
252
  private reconcileInterruptedStarts;
196
253
  /**
197
- * Field-preserving v2 to v3 migration. v3 adds no fields yet, so the
198
- * migration reuses the v3 normalization, but it first proves every task
199
- * row is structurally valid: a v2 document that would silently drop or
200
- * coerce rows fails loudly instead (no quarantined-empty restart).
254
+ * Field-preserving migration of an older document. It first proves every task
255
+ * row is structurally valid, so a document that would silently drop or coerce
256
+ * rows fails loudly instead (no quarantined-empty restart), and then reuses
257
+ * the current normalization.
258
+ *
259
+ * v4 adds `ScheduleRule.timeZone`. A rule written before v4 was evaluated in
260
+ * whatever zone the Host process happened to report, so the migration stamps
261
+ * that zone onto every enabled rule: the trigger instant is preserved exactly
262
+ * (the stored `nextRunAt` already encodes the old zone), but the rule stops
263
+ * following a later `TZ` change, which is what made an existing schedule
264
+ * silently move when the Host's zone changed.
265
+ *
266
+ * v5 adds the per-execution acceptance block. The migration deliberately does
267
+ * NOT stamp one: an execution opened before v5 keeps no contract, so it
268
+ * settles on its own historical verdict and no in-flight run is retroactively
269
+ * judged by a gate that was never armed for it.
201
270
  */
202
271
  private migrateLegacyDocument;
272
+ /**
273
+ * Find the still-open execution that ran in one session, with its task.
274
+ *
275
+ * The completion gate keys on the session id its tool call arrives from: the
276
+ * execution record is the only object binding a session to a card, and an
277
+ * already-settled execution is deliberately not returned (a session the board
278
+ * no longer owns must not be gated).
279
+ * @param sessionId - the DSH session the tool call runs in.
280
+ * @returns the open execution and its task, or undefined.
281
+ */
282
+ findOpenExecutionBySession(sessionId: string): {
283
+ task: TaskRecord;
284
+ execution: ExecutionRecord;
285
+ } | undefined;
286
+ /**
287
+ * Replace one execution's acceptance block (creating it when absent).
288
+ *
289
+ * A no-op when the serialized value is unchanged, so a coarse caller that
290
+ * re-writes the same state never bumps the ledger revision. A settled
291
+ * execution keeps its record: the report of a finished cycle must survive,
292
+ * and only the board's own settlement moves the card.
293
+ * @param taskId - the task owning the execution.
294
+ * @param executionId - the execution to update.
295
+ * @param verification - the new block.
296
+ * @returns true when the ledger changed.
297
+ */
298
+ setVerification(taskId: string, executionId: string, verification: ExecutionVerification): boolean;
203
299
  private load;
204
300
  private normalizeDocument;
205
301
  /** Quarantine an unreadable document and start from an empty ledger. */
@@ -3,7 +3,10 @@ import type { SessionSummary } from '@deepseek-ai/dsh-api-session-controller/typ
3
3
  import type { CommandResult } from '@deepseek-ai/dsh-commands/types';
4
4
  import type { Workspace } from '@deepseek-ai/dsh-workspace/types';
5
5
  import type { TaskRecord } from './core/tasks.ts';
6
- /** Host services needed to validate a task's workspace before creating a session. */
6
+ /**
7
+ * Host services needed to validate a task's workspace before creating a
8
+ * session, and to resolve the workspace an unpinned card runs in.
9
+ */
7
10
  export interface TaskBoardWorkspaceRegistry {
8
11
  list(): readonly Workspace[];
9
12
  }
@@ -23,8 +26,18 @@ type ExecutionSessionId = SessionSummary['sessionId'];
23
26
  export interface SessionCommandDispatcher {
24
27
  execute(sessionId: ExecutionSessionId, line: string, signal: AbortSignal): Promise<CommandResult | undefined>;
25
28
  }
26
- export type ExecutionInspection = {
29
+ export type ExecutionInspection =
30
+ /**
31
+ * No verdict yet. `unreadable` separates "the work may still be in progress"
32
+ * from "this session's history could not be read at all": a reader failure is
33
+ * not progress, and the caller decides how long to keep waiting before it
34
+ * reports the execution as undeterminable instead of holding a card open
35
+ * forever.
36
+ */
37
+ {
27
38
  outcome: 'pending';
39
+ unreadable?: true;
40
+ reason?: string;
28
41
  } | {
29
42
  outcome: 'succeeded';
30
43
  } | {
@@ -64,6 +77,17 @@ export interface PromptContext {
64
77
  peers?: readonly PromptPeer[];
65
78
  /** True when those members run as teammates inside this session (Team Lead). */
66
79
  team?: boolean;
80
+ /**
81
+ * Scheduled-run provenance: the instant the cron rule actually fired and the
82
+ * IANA zone its wall clock was read in. Present only for a cron-triggered
83
+ * run, so the model can resolve an unqualified date in the run's own terms
84
+ * instead of guessing from the Host clock (issue #1722).
85
+ */
86
+ schedule?: {
87
+ triggeredAt: number;
88
+ timeZone: string;
89
+ cron: string;
90
+ };
67
91
  }
68
92
  /**
69
93
  * Build the execution prompt for one task.
@@ -72,6 +96,16 @@ export interface PromptContext {
72
96
  * @returns the prompt text.
73
97
  */
74
98
  export declare function promptText(task: TaskRecord, context?: PromptContext): string;
99
+ /**
100
+ * Build the objective the built-in `/goal` command receives. A prompt whose
101
+ * text would be read as a goal operation is prefixed with a neutral label: the
102
+ * instruction stays intact, and the command arms an objective instead of
103
+ * clearing, pausing, or editing an unrelated goal (or refusing for a missing
104
+ * one).
105
+ * @param prompt - the composed execution prompt.
106
+ * @returns the objective text for `/goal <objective>`.
107
+ */
108
+ export declare function goalObjective(prompt: string): string;
75
109
  export declare class HostExecutionRunner {
76
110
  private readonly gateway;
77
111
  private readonly commands?;
@@ -87,6 +121,14 @@ export declare class HostExecutionRunner {
87
121
  });
88
122
  private invoke;
89
123
  private stream;
124
+ /**
125
+ * The workspace an unpinned run lands in: the deployment's most recently used
126
+ * one. Undefined when the deployment serves no workspace registry or has
127
+ * registered none yet; the launch then keeps the "let the Host decide" shape,
128
+ * which is the only remaining path to the Host's own working directory.
129
+ * @returns the workspace id to create the session in, when one is known.
130
+ */
131
+ private recentWorkspaceId;
90
132
  /**
91
133
  * Launch one execution. Without `options.reuseSessionId` a fresh session is
92
134
  * created, renamed, pinned, and prompted (the historical contract). With it,
@@ -100,6 +142,8 @@ export declare class HostExecutionRunner {
100
142
  launch(task: TaskRecord, options?: {
101
143
  reuseSessionId?: string;
102
144
  promptContext?: PromptContext;
145
+ onSession?: (sessionId: string) => void;
146
+ onGoalArmed?: (armed: boolean) => void;
103
147
  }): Promise<string>;
104
148
  /**
105
149
  * Fail closed when a reused session records a different Agent preset than
@@ -122,6 +166,46 @@ export declare class HostExecutionRunner {
122
166
  * the same permission/model pins before the prompt.
123
167
  */
124
168
  private pinAndPrompt;
169
+ /**
170
+ * Read the host's model catalog: the default route an unconfigured session
171
+ * starts at, and each provider's models with the reasoning metadata its
172
+ * adapter exposes. The acceptance settings card and the frozen contract both
173
+ * resolve against it, so "inherit host" means the host's own configuration
174
+ * rather than a session's or a card's pinned model.
175
+ * @returns the raw catalog value, or undefined when this cohort serves none.
176
+ */
177
+ modelCatalog(): Promise<unknown>;
178
+ /**
179
+ * Arm dsh's built-in goal on the session. The task prompt is queued FIRST, so
180
+ * the session's first turn carries the instruction verbatim; `/goal
181
+ * <objective>` then turns the same text into a persistent objective, and
182
+ * dsh's goal-round driver keeps starting continuation rounds until the agent
183
+ * marks it complete (see {@link goalVerdict} for how the run settles).
184
+ *
185
+ * Opt-out: a task whose `goalRun` is an explicit false runs one plain turn.
186
+ * A refusal (no command dispatcher, no `/goal` command in this cohort, an
187
+ * objective the command rejects) is reported and the run continues as that
188
+ * plain turn: the task was asked to run, and a missing goal mode must not
189
+ * lose the work.
190
+ */
191
+ private armGoal;
192
+ /**
193
+ * The verdict an armed dsh goal imposes on an otherwise completed turn.
194
+ *
195
+ * A goal run is one session working many automatic continuation rounds:
196
+ * dsh's goal-round driver queues the next round whenever the agent goes
197
+ * idle, so the first completed `turn/end` is not the execution's result.
198
+ * Settling there would leave the card in `done` while the session keeps
199
+ * working, and a scheduled task would fire a second, concurrent session.
200
+ * The session projection is the authority: an `active` goal keeps the
201
+ * execution pending, a `blocked` goal fails it with the recorded reason, and
202
+ * any other phase (complete, paused, or no goal at all) leaves the turn
203
+ * verdict in place.
204
+ *
205
+ * An unreadable projection falls back to that turn verdict: a cohort whose
206
+ * gateway withdrew `session/projections` must not hang every execution.
207
+ */
208
+ private goalVerdict;
125
209
  listRunning(): Promise<{
126
210
  known: true;
127
211
  count: number;
@@ -129,6 +213,17 @@ export declare class HostExecutionRunner {
129
213
  } | {
130
214
  known: false;
131
215
  }>;
132
- /** Resolve an execution outcome from the session list and bounded history pages. */
133
- inspect(sessionId: string, startedAt?: number, sessions?: readonly SessionSummary[]): Promise<ExecutionInspection>;
216
+ /**
217
+ * Resolve an execution outcome from the session list and bounded history pages.
218
+ * @param sessionId - the session the execution runs in.
219
+ * @param startedAt - instant the execution opened; earlier events are not its own.
220
+ * @param sessions - roster from the caller's own poll, when it already has one.
221
+ * @param options.whileRunning - read the first completed turn even while the
222
+ * roster reports the session as running. A durable Agent Teams teammate
223
+ * stays alive after its turn ends, so `running` never clears for it and the
224
+ * turn it completed is the only verdict it will ever expose.
225
+ */
226
+ inspect(sessionId: string, startedAt?: number, sessions?: readonly SessionSummary[], options?: {
227
+ whileRunning?: boolean;
228
+ }): Promise<ExecutionInspection>;
134
229
  }