@linxin666/dsh-client-ui-task-board 0.4.5 → 0.4.7
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 +34 -18
- package/README.zh.md +34 -18
- package/lib/client.js +2804 -1024
- package/lib/client.js.map +1 -1
- package/lib/index.js +2576 -416
- package/lib/types/client/CardDefaultPresetField.d.ts +35 -0
- package/lib/types/client/TaskBoardSettingsCard.d.ts +61 -0
- package/lib/types/client/board/ReasoningEffortField.d.ts +25 -0
- package/lib/types/client/board/model-options.d.ts +67 -0
- package/lib/types/client/board/preset-label.d.ts +21 -0
- package/lib/types/client/board/preset-roster.d.ts +45 -0
- package/lib/types/client/board/team-run-promotion.d.ts +26 -0
- package/lib/types/client/index.d.ts +4 -5
- package/lib/types/client/locales.d.ts +81 -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/card-defaults.d.ts +78 -0
- package/lib/types/core/controller.d.ts +41 -17
- package/lib/types/core/launch-width.d.ts +31 -0
- package/lib/types/core/poll-cadence.d.ts +31 -0
- package/lib/types/core/reasoning-effort.d.ts +57 -0
- package/lib/types/core/subtask.d.ts +30 -8
- package/lib/types/core/tasks.d.ts +163 -14
- package/lib/types/core/use-cases/task-create.d.ts +10 -5
- package/lib/types/core/use-cases/task-schedule.d.ts +68 -10
- package/lib/types/core/use-cases/task-update.d.ts +1 -1
- package/lib/types/core/verification-budget.d.ts +52 -0
- package/lib/types/core/verification.d.ts +376 -9
- package/lib/types/host/launch-admission.d.ts +72 -0
- 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 +91 -11
- package/lib/types/host-runner.d.ts +111 -13
- package/lib/types/host-service.d.ts +199 -8
- package/lib/types/index.d.ts +141 -0
- package/lib/types/protocol.d.ts +63 -12
- package/package.json +29 -33
- package/src/client/CardDefaultPresetField.tsx +101 -0
- package/src/client/TaskBoardSettingsCard.tsx +288 -17
- package/src/client/board/LinkSubtaskModal.tsx +8 -0
- package/src/client/board/NewTaskModal.tsx +237 -60
- package/src/client/board/ReasoningEffortField.tsx +50 -0
- package/src/client/board/TaskCard.tsx +3 -3
- package/src/client/board/TaskDetail.tsx +261 -24
- package/src/client/board/VerificationReport.tsx +132 -13
- package/src/client/board/model-options.ts +178 -0
- package/src/client/board/preset-label.ts +32 -0
- package/src/client/board/preset-roster.ts +58 -0
- package/src/client/board/status-key.ts +3 -0
- package/src/client/board/team-run-promotion.ts +39 -0
- package/src/client/index.ts +105 -108
- package/src/client/locales.ts +171 -6
- package/src/client/plugin-card-seat.ts +7 -0
- package/src/client/schedule-zone.ts +100 -2
- package/src/client/settings-entry-form.ts +0 -1
- package/src/core/card-defaults.ts +132 -0
- package/src/core/controller.ts +55 -16
- package/src/core/launch-width.ts +38 -0
- package/src/core/poll-cadence.ts +41 -0
- package/src/core/reasoning-effort.ts +92 -0
- package/src/core/store.ts +69 -15
- package/src/core/subtask.ts +48 -13
- package/src/core/tasks.ts +213 -39
- package/src/core/use-cases/task-create.ts +57 -25
- package/src/core/use-cases/task-schedule.ts +190 -25
- package/src/core/use-cases/task-update.ts +9 -4
- package/src/core/verification-budget.ts +115 -0
- package/src/core/verification.ts +785 -17
- package/src/host/agent-tools.ts +127 -29
- package/src/host/launch-admission.ts +140 -0
- package/src/host/verification-gate.ts +172 -16
- package/src/host/verification-runner.ts +207 -40
- package/src/host-ledger.ts +346 -48
- package/src/host-runner.ts +234 -35
- package/src/host-service.ts +539 -52
- package/src/index.ts +250 -7
- package/src/protocol.ts +160 -32
- 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
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admission control for the session-creating half of a run.
|
|
3
|
+
*
|
|
4
|
+
* One board action can open a whole subtree, and a team run hands every member
|
|
5
|
+
* of it to the Lead at once. Both paths used to fire every launch in the same
|
|
6
|
+
* tick, so the Host asked DSH for N sessions at once — the burst the repository's
|
|
7
|
+
* measured guidance caps at three heavy implementation subagents
|
|
8
|
+
* (docs/multi-agent-resources.md), with no mechanism enforcing it.
|
|
9
|
+
*
|
|
10
|
+
* The queue is SERVICE-wide, not per run group: the pressure it bounds is the
|
|
11
|
+
* machine's (the Host process's memory and the swap it drives), and two run
|
|
12
|
+
* groups opened from two tabs share one Host. Admission is round-robin across
|
|
13
|
+
* run groups — FIFO inside a group, so a group's first run (the Lead, which
|
|
14
|
+
* carries the run shape and the teammate namespace) always starts before its
|
|
15
|
+
* members — so a large subtree cannot starve a run opened after it, and no
|
|
16
|
+
* entry can be starved.
|
|
17
|
+
*
|
|
18
|
+
* The width is read through a live callback, so a settings edit takes effect on
|
|
19
|
+
* the next admission without remounting anything.
|
|
20
|
+
*
|
|
21
|
+
* @module dsh-task-board/host/launch-admission
|
|
22
|
+
*/
|
|
23
|
+
/** One launch the queue has not started yet. */
|
|
24
|
+
export interface QueuedLaunch {
|
|
25
|
+
/** Run group this launch belongs to; the unit of round-robin fairness. */
|
|
26
|
+
readonly groupId: string;
|
|
27
|
+
/**
|
|
28
|
+
* Perform the launch. The slot is held until this promise settles, which
|
|
29
|
+
* makes "in flight" mean "still creating and prompting its session".
|
|
30
|
+
* A rejection is swallowed here so one broken launch can never wedge the
|
|
31
|
+
* slot; the caller owns reporting.
|
|
32
|
+
*/
|
|
33
|
+
start(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Called by {@link LaunchAdmissionQueue.drain} for a launch that never
|
|
36
|
+
* started, so the caller can take its execution out of the running column.
|
|
37
|
+
*/
|
|
38
|
+
abandon(): void;
|
|
39
|
+
}
|
|
40
|
+
export declare class LaunchAdmissionQueue {
|
|
41
|
+
private readonly width;
|
|
42
|
+
/** Waiting launches per run group, in arrival order. */
|
|
43
|
+
private readonly waiting;
|
|
44
|
+
/** Run groups in round-robin order. */
|
|
45
|
+
private rotation;
|
|
46
|
+
/** Slot holders of the launches in flight, one per admitted launch. */
|
|
47
|
+
private readonly inFlight;
|
|
48
|
+
/** Highest number of launches in flight at once; observability and tests. */
|
|
49
|
+
peakInFlight: number;
|
|
50
|
+
/**
|
|
51
|
+
* @param width - live admission width (already normalized by the caller).
|
|
52
|
+
*/
|
|
53
|
+
constructor(width: () => number);
|
|
54
|
+
/** Launches the queue has not admitted yet. */
|
|
55
|
+
get pendingCount(): number;
|
|
56
|
+
/** Launches currently holding a slot. */
|
|
57
|
+
get inFlightCount(): number;
|
|
58
|
+
/** Admit one launch, immediately if a slot is free. */
|
|
59
|
+
enqueue(launch: QueuedLaunch): void;
|
|
60
|
+
/**
|
|
61
|
+
* Abandon every waiting launch: the queue is being torn down, so an entry
|
|
62
|
+
* that never started can never start. Launches already in flight are left
|
|
63
|
+
* alone — their session may already exist, and the callers that own them
|
|
64
|
+
* settle them through the normal path.
|
|
65
|
+
* @returns the abandoned launches, in abandonment order.
|
|
66
|
+
*/
|
|
67
|
+
drain(): QueuedLaunch[];
|
|
68
|
+
private pump;
|
|
69
|
+
/** The next launch by round-robin across groups and FIFO inside one. */
|
|
70
|
+
private takeNext;
|
|
71
|
+
private admit;
|
|
72
|
+
}
|
|
@@ -18,7 +18,21 @@
|
|
|
18
18
|
* - an anomaly (timeout, authentication, unparseable answer, an unusable judge
|
|
19
19
|
* route) is recorded separately from a quality verdict and is never treated
|
|
20
20
|
* as one;
|
|
21
|
+
* - exhausting the anomaly budget does NOT judge the card: an environment that
|
|
22
|
+
* cannot answer is not the work failing, so the cycle is held open with no
|
|
23
|
+
* judge call until a user clears the recorded anomalies (issue #1828);
|
|
24
|
+
* - an attempt the time budget ends before any verdict is recorded as its own
|
|
25
|
+
* stage and charged to no budget at all (issue #1828);
|
|
26
|
+
* - an INVALID acceptance (a veto whose required evidence is missing,
|
|
27
|
+
* unlocatable, or aimed at the empty-work baseline) is recorded in its own
|
|
28
|
+
* stage, spends its OWN bounded budget, and never consumes a quality attempt
|
|
29
|
+
* or fails the card; when that budget is spent the cycle is held for a human
|
|
30
|
+
* exactly like a spent anomaly budget;
|
|
21
31
|
* - concurrent completion calls for one execution share one acceptance.
|
|
32
|
+
|
|
33
|
+
* The validity check itself is program-level (`assessAcceptanceValidity` in
|
|
34
|
+
* `core/verification.ts`), applied after the judge answers and BEFORE any
|
|
35
|
+
* quality verdict is written: a stronger prompt alone could not enforce it.
|
|
22
36
|
*/
|
|
23
37
|
import type { PreToolDecision } from '@deepseek-ai/dsh-tools';
|
|
24
38
|
import type { LlmRuntime } from '@deepseek-ai/dsh-llm';
|
|
@@ -51,6 +65,16 @@ export interface GoalVerificationGateDeps {
|
|
|
51
65
|
* that serves none) simply means the judge sees the trajectory alone.
|
|
52
66
|
*/
|
|
53
67
|
workspaceChanges?: () => WorkspaceChangeSource | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Ceiling of one judge request, read per acceptance (a row setting). Absent
|
|
70
|
+
* keeps the default, so a programmatic mount still judges.
|
|
71
|
+
*/
|
|
72
|
+
callTimeoutMs?: () => number;
|
|
73
|
+
/**
|
|
74
|
+
* Total ceiling of one acceptance attempt, read per acceptance (a row
|
|
75
|
+
* setting). Absent keeps the default.
|
|
76
|
+
*/
|
|
77
|
+
budgetMs?: () => number;
|
|
54
78
|
logger: {
|
|
55
79
|
warn(message: string, ...rest: unknown[]): void;
|
|
56
80
|
};
|
|
@@ -15,18 +15,45 @@ export declare const VERIFICATION_MAX_TRACE_CHARS = 80000;
|
|
|
15
15
|
export declare const VERIFICATION_MAX_ENTRY_CHARS = 4000;
|
|
16
16
|
/** Largest per-finding text kept from one judge answer. */
|
|
17
17
|
export declare const VERIFICATION_MAX_FINDING_CHARS = 400;
|
|
18
|
-
/**
|
|
18
|
+
/** Largest verbatim citation kept from one finding. */
|
|
19
|
+
export declare const VERIFICATION_MAX_QUOTE_CHARS = 600;
|
|
20
|
+
/** Largest requirement/observation/gap statement kept from one finding. */
|
|
21
|
+
export declare const VERIFICATION_MAX_PROBLEM_CHARS = 400;
|
|
22
|
+
/** Structured findings kept per judge answer and per acceptance. */
|
|
19
23
|
export declare const VERIFICATION_MAX_FINDINGS_PER_CALL = 3;
|
|
20
|
-
export declare const VERIFICATION_MAX_FINDINGS =
|
|
21
|
-
/**
|
|
22
|
-
|
|
24
|
+
export declare const VERIFICATION_MAX_FINDINGS = 12;
|
|
25
|
+
/**
|
|
26
|
+
* Default ceiling of ONE judge request. The effective ceiling is a row setting
|
|
27
|
+
* (`goalVerificationCallTimeoutSeconds`), read live by the gate, because 120s was
|
|
28
|
+
* tight for a reasoning model reading an 80,000 character trace with a 16,384
|
|
29
|
+
* token output cap: every timeout it produced was charged as an acceptance
|
|
30
|
+
* anomaly against a budget that then failed the card.
|
|
31
|
+
*/
|
|
32
|
+
export declare const VERIFICATION_CALL_TIMEOUT_MS = 150000;
|
|
23
33
|
export declare const VERIFICATION_MAX_TOKENS = 16384;
|
|
24
34
|
export declare const VERIFICATION_TEMPERATURE = 0.2;
|
|
25
35
|
export declare const VERIFICATION_REQUEST_ATTEMPTS = 3;
|
|
26
36
|
/** Redaction patterns, mirrored from the verifier's defaults. */
|
|
27
37
|
export declare const VERIFICATION_REDACT_PATTERNS: readonly string[];
|
|
28
38
|
/** Why one acceptance could not produce a verdict. */
|
|
29
|
-
export type VerificationErrorKind = 'route-unavailable' | 'timeout' | 'auth' | 'parse' | 'request' | 'aborted';
|
|
39
|
+
export type VerificationErrorKind = 'route-unavailable' | 'timeout' | 'auth' | 'parse' | 'request' | 'aborted' | 'budget';
|
|
40
|
+
/**
|
|
41
|
+
* The time one acceptance attempt may still spend.
|
|
42
|
+
*
|
|
43
|
+
* `deadline` is the instant the whole attempt runs out; `callTimeoutMs` is the
|
|
44
|
+
* ceiling of a single judge call. The pair is what ties the retry budget to the
|
|
45
|
+
* acceptance budget: a call is not started unless its own ceiling still fits
|
|
46
|
+
* inside what is left, because starting one the outer budget will abort anyway
|
|
47
|
+
* only burns wall time and turns a slow machine into an anomaly.
|
|
48
|
+
*/
|
|
49
|
+
export interface AcceptanceBudget {
|
|
50
|
+
/** Absolute instant (ms epoch) the acceptance attempt runs out at. */
|
|
51
|
+
deadline: number;
|
|
52
|
+
/** Ceiling of one judge call in milliseconds. */
|
|
53
|
+
callTimeoutMs: number;
|
|
54
|
+
/** Clock the caller runs on, injected so tests drive it deterministically. */
|
|
55
|
+
now: () => number;
|
|
56
|
+
}
|
|
30
57
|
/** An acceptance anomaly: never a quality verdict, always recorded as one. */
|
|
31
58
|
export declare class VerificationError extends Error {
|
|
32
59
|
readonly kind: VerificationErrorKind;
|
|
@@ -89,8 +116,8 @@ export interface AcceptanceResult {
|
|
|
89
116
|
* Run one acceptance: every criterion is judged twice, the odd round with the
|
|
90
117
|
* A/B slots swapped, and the per-round scores are mapped back and averaged per
|
|
91
118
|
* criterion.
|
|
92
|
-
* @param input - the judge route, evidence, threshold and cancellation.
|
|
93
|
-
* @returns the recorded attempt (quality verdict or
|
|
119
|
+
* @param input - the judge route, evidence, threshold, time budget and cancellation.
|
|
120
|
+
* @returns the recorded attempt (quality verdict, anomaly, or budget stop).
|
|
94
121
|
*/
|
|
95
122
|
export declare function runAcceptance(input: {
|
|
96
123
|
llm: LlmRuntime;
|
|
@@ -99,6 +126,10 @@ export declare function runAcceptance(input: {
|
|
|
99
126
|
threshold: number;
|
|
100
127
|
index: number;
|
|
101
128
|
signal: AbortSignal;
|
|
129
|
+
/** Instant (ms epoch) the attempt runs out at; defaults to now plus the default budget. */
|
|
130
|
+
deadline?: number;
|
|
131
|
+
/** Ceiling of one judge call; defaults to {@link VERIFICATION_CALL_TIMEOUT_MS}. */
|
|
132
|
+
callTimeoutMs?: number;
|
|
102
133
|
/** Host-observed workspace changes, rendered as the prompt's reference context. */
|
|
103
134
|
context?: string;
|
|
104
135
|
/** How many changed files that context shows. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { type ExecutionOutcome, type ExecutionRecord, type TaskRecord } from './core/tasks.ts';
|
|
1
|
+
import { type ExecutionOutcome, type ExecutionRecord, type ScheduleMode, type TaskRecord } from './core/tasks.ts';
|
|
2
|
+
import type { CardDefaults } from './core/card-defaults.ts';
|
|
2
3
|
import { type TaskBoardAction, type TaskBoardSchedulerSnapshot } from './protocol.ts';
|
|
3
|
-
import type
|
|
4
|
+
import { type ExecutionVerification } from './core/verification.ts';
|
|
4
5
|
import { type TaskPermission } from './core/handover.ts';
|
|
5
6
|
export interface LedgerState {
|
|
6
7
|
revision: number;
|
|
@@ -35,7 +36,10 @@ export interface OpenExecutionReference {
|
|
|
35
36
|
/** Minimal value copy used by the Host scheduler. */
|
|
36
37
|
export interface DueScheduleReference {
|
|
37
38
|
readonly taskId: string;
|
|
38
|
-
|
|
39
|
+
/** Whether this due rule is a recurring cron rule or a single planned instant. */
|
|
40
|
+
readonly mode: ScheduleMode;
|
|
41
|
+
/** Cron expression of a recurring rule; absent on a one-shot. */
|
|
42
|
+
readonly cron?: string;
|
|
39
43
|
/** Zone this rule's wall clock is read in. */
|
|
40
44
|
readonly timeZone: string;
|
|
41
45
|
readonly nextRunAt: number;
|
|
@@ -44,6 +48,24 @@ export interface DueScheduleReference {
|
|
|
44
48
|
export interface LedgerRuntimeView {
|
|
45
49
|
readonly armedSchedules: number;
|
|
46
50
|
readonly openExecutions: readonly OpenExecutionReference[];
|
|
51
|
+
/**
|
|
52
|
+
* Session ids of every open execution, including the ones
|
|
53
|
+
* {@link openExecutions} skips because their own outcome is already
|
|
54
|
+
* recorded. A caller that asks whether the board still observes any session
|
|
55
|
+
* must use this list: a deferred cascade parent is settled by its members,
|
|
56
|
+
* but it is still an open execution.
|
|
57
|
+
*/
|
|
58
|
+
readonly openSessionIds: readonly string[];
|
|
59
|
+
/**
|
|
60
|
+
* Whether this document holds anything the session roster can still decide:
|
|
61
|
+
* an open execution that needs inspecting, or a card in the running column
|
|
62
|
+
* whose verdict may arrive from a settle this process never saw.
|
|
63
|
+
*
|
|
64
|
+
* A board with no running card, no open execution and no armed schedule has
|
|
65
|
+
* no use for the roster at all, which is what lets the Host poll stand down
|
|
66
|
+
* instead of re-reading every persisted session forever.
|
|
67
|
+
*/
|
|
68
|
+
readonly needsSessionState: boolean;
|
|
47
69
|
}
|
|
48
70
|
/**
|
|
49
71
|
* Best-effort single-letter process state ('R','S','D','Z',...) or undefined
|
|
@@ -104,9 +126,12 @@ export declare class HostTaskLedger {
|
|
|
104
126
|
* instead of remounting the row.
|
|
105
127
|
*/
|
|
106
128
|
private depthLimit;
|
|
129
|
+
/** Live reader of the deployment's new-card defaults (a volatile config read). */
|
|
130
|
+
private readonly cardDefaults;
|
|
107
131
|
constructor(dir?: string, now?: () => number, options?: {
|
|
108
132
|
sessionDefaultPermission?: TaskPermission | (() => TaskPermission);
|
|
109
133
|
maxSubtaskDepth?: number;
|
|
134
|
+
cardDefaults?: () => CardDefaults;
|
|
110
135
|
});
|
|
111
136
|
/** Current subtask depth limit (1..3). */
|
|
112
137
|
get maxSubtaskDepth(): number;
|
|
@@ -125,7 +150,7 @@ export declare class HostTaskLedger {
|
|
|
125
150
|
};
|
|
126
151
|
state(): LedgerState;
|
|
127
152
|
/**
|
|
128
|
-
* Runtime-only projection for the
|
|
153
|
+
* Runtime-only projection for the Host poll. It copies just primitive
|
|
129
154
|
* identifiers and timestamps, never the complete task/execution history or
|
|
130
155
|
* an authoritative mutable object from the ledger.
|
|
131
156
|
*/
|
|
@@ -152,9 +177,45 @@ export declare class HostTaskLedger {
|
|
|
152
177
|
/**
|
|
153
178
|
* Open the cascade one due schedule triggers: an empty array means nothing
|
|
154
179
|
* ran (already running, or a participant whose elevated permission is still
|
|
155
|
-
* unconfirmed
|
|
180
|
+
* unconfirmed, a one-shot whose instant was missed, or an exhausted budget),
|
|
181
|
+
* and the rule's own state says which.
|
|
182
|
+
*
|
|
183
|
+
* The run budget is enforced HERE, inside the single ledger writer: a rule
|
|
184
|
+
* whose counter already reached its cap is stopped instead of opened, so no
|
|
185
|
+
* restart, repeated timer fire or re-arm can overrun it.
|
|
186
|
+
* @param taskId - the task whose rule came due.
|
|
187
|
+
* @param triggeredAt - the instant the occurrence came due.
|
|
188
|
+
* @returns the executions this occurrence opened.
|
|
189
|
+
*/
|
|
190
|
+
openScheduled(taskId: string, triggeredAt: number): OpenedRun[];
|
|
191
|
+
/**
|
|
192
|
+
* Record a due occurrence that opened no execution: roll a recurring rule to
|
|
193
|
+
* its next occurrence (budget untouched), or stop it when there is none. A
|
|
194
|
+
* one-shot has no second occurrence, so it always stops here with the reason.
|
|
195
|
+
*/
|
|
196
|
+
private skipOccurrence;
|
|
197
|
+
/** Apply a schedule-occurrence transition and persist it atomically. */
|
|
198
|
+
private progressSchedule;
|
|
199
|
+
/**
|
|
200
|
+
* Refund the one scheduled run an occurrence consumed before its launch
|
|
201
|
+
* failed without creating a session (see
|
|
202
|
+
* {@link applyScheduleRefund}). Called by the service on the failed-launch
|
|
203
|
+
* path only; a manual run and a launch that did reach a session are never
|
|
204
|
+
* refunded. A no-op for a task with no rule, so a failed cascade member does
|
|
205
|
+
* not touch anything.
|
|
206
|
+
* @param taskId - the task whose occurrence is being refunded.
|
|
207
|
+
* @param now - clock instant (ms epoch).
|
|
208
|
+
*/
|
|
209
|
+
refundScheduledOccurrence(taskId: string, now: number): void;
|
|
210
|
+
/**
|
|
211
|
+
* Boot / resume recovery: skip every occurrence that came due while the board
|
|
212
|
+
* was not running through it. A recurring rule rolls to its next future
|
|
213
|
+
* target; a one-shot whose instant has passed is stopped as missed, because
|
|
214
|
+
* there is no later occurrence left to run. Rendering the missed occurrence
|
|
215
|
+
* is deliberately not attempted: the ACL of a card that fired hours ago is
|
|
216
|
+
* stale, and the board's own recovery contract is "missed triggers are
|
|
217
|
+
* skipped, never replayed".
|
|
156
218
|
*/
|
|
157
|
-
openScheduled(taskId: string, nextRunAt: number | undefined, triggeredAt: number): OpenedRun[];
|
|
158
219
|
skipMissed(now: number): void;
|
|
159
220
|
setScheduler(patch: Partial<TaskBoardSchedulerSnapshot>): void;
|
|
160
221
|
/**
|
|
@@ -169,6 +230,14 @@ export declare class HostTaskLedger {
|
|
|
169
230
|
getTask(id: string): TaskRecord | undefined;
|
|
170
231
|
saveTaskRecord(task: TaskRecord): void;
|
|
171
232
|
private apply;
|
|
233
|
+
/**
|
|
234
|
+
* Whether a creation-time schedule request is usable. A one-shot must carry
|
|
235
|
+
* a whole-millisecond instant in the future; a recurring rule must carry a
|
|
236
|
+
* valid expression with a reachable occurrence and, when capped, a positive
|
|
237
|
+
* whole run budget. An unusable zone is refused rather than reinterpreted as
|
|
238
|
+
* the Host zone.
|
|
239
|
+
*/
|
|
240
|
+
private validScheduleRequest;
|
|
172
241
|
/**
|
|
173
242
|
* The binding that makes a run illegal, if any.
|
|
174
243
|
*
|
|
@@ -194,15 +263,18 @@ export declare class HostTaskLedger {
|
|
|
194
263
|
/** Whether a task or any of its subtasks still has an open execution. */
|
|
195
264
|
private subtreeHasOpenExecution;
|
|
196
265
|
/**
|
|
197
|
-
* Open one execution per cascade participant
|
|
198
|
-
* on-board descendants, within the depth limit) under
|
|
199
|
-
* commit the ledger once.
|
|
200
|
-
*
|
|
266
|
+
* Open one execution per cascade participant that still has work (the
|
|
267
|
+
* requested task and its on-board descendants, within the depth limit) under
|
|
268
|
+
* a single run group, and commit the ledger once. A descendant in 'done' is
|
|
269
|
+
* not a participant ({@link joinsCascadeRun}). The returned runs are ordered
|
|
270
|
+
* root-first, so the Host launches the parent before its subtasks.
|
|
201
271
|
* @param root - the task the user ran.
|
|
202
272
|
* @param now - clock instant (ms epoch).
|
|
203
273
|
* @param initiator - the DSH session that asked for the run (audit only).
|
|
204
274
|
* @param rerun - true to reset the root to 'todo' before starting it.
|
|
205
|
-
* @param
|
|
275
|
+
* @param progress - when set, this run was a due occurrence and the root's
|
|
276
|
+
* rule is advanced (counter, next target, stop/skip record) in the same
|
|
277
|
+
* atomic commit that opened the executions.
|
|
206
278
|
*/
|
|
207
279
|
private startCascade;
|
|
208
280
|
/**
|
|
@@ -248,6 +320,14 @@ export declare class HostTaskLedger {
|
|
|
248
320
|
* @returns whether the document changed.
|
|
249
321
|
*/
|
|
250
322
|
private closeTeamMembers;
|
|
323
|
+
/**
|
|
324
|
+
* Re-align every armed rule at load/import. A recurring rule is recomputed
|
|
325
|
+
* from the current instant; a one-shot is armed at its planned instant when
|
|
326
|
+
* that is still in the future, and stopped as missed once it has passed
|
|
327
|
+
* (there is no later occurrence to run). A recurring rule recomputed past an
|
|
328
|
+
* occurrence records the skip, so the board can show that a fire was passed
|
|
329
|
+
* over rather than silently rewinding.
|
|
330
|
+
*/
|
|
251
331
|
private repairSchedules;
|
|
252
332
|
private reconcileInterruptedStarts;
|
|
253
333
|
/**
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { InvokeRemoteRequest, TypertGateway } from '@deepseek-ai/dsh-api-gateway';
|
|
2
3
|
import type { SessionSummary } from '@deepseek-ai/dsh-api-session-controller/types';
|
|
3
4
|
import type { CommandResult } from '@deepseek-ai/dsh-commands/types';
|
|
4
5
|
import type { Workspace } from '@deepseek-ai/dsh-workspace/types';
|
|
6
|
+
import type { ModelCatalogView } from './core/verification.ts';
|
|
5
7
|
import type { TaskRecord } from './core/tasks.ts';
|
|
6
8
|
/**
|
|
7
9
|
* Host services needed to validate a task's workspace before creating a
|
|
@@ -10,12 +12,47 @@ import type { TaskRecord } from './core/tasks.ts';
|
|
|
10
12
|
export interface TaskBoardWorkspaceRegistry {
|
|
11
13
|
list(): readonly Workspace[];
|
|
12
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* The peer every gateway request names: derived from the public
|
|
17
|
+
* `InvokeRemoteRequest` type so this package needs no direct dependency on
|
|
18
|
+
* `@deepseek-ai/dsh-typert-protocol` for the shape.
|
|
19
|
+
*/
|
|
20
|
+
export type GatewayPeer = InvokeRemoteRequest['peer'];
|
|
13
21
|
interface GatewayRequest {
|
|
14
22
|
namespace: string;
|
|
15
23
|
method: string;
|
|
16
24
|
args: Record<string, unknown>;
|
|
25
|
+
peer: GatewayPeer;
|
|
17
26
|
signal?: AbortSignal;
|
|
18
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Mint the in-process owner peer this plugin's Host calls speak for.
|
|
30
|
+
*
|
|
31
|
+
* The 0.2.1 gateway made `peer` mandatory on every request, and the
|
|
32
|
+
* authorization layer reads only `peer.principal`: a principal of
|
|
33
|
+
* `{ kind: 'owner' }` is how host-local code states that the call IS the local
|
|
34
|
+
* user driving their own Host, so the gateway answers it directly instead of
|
|
35
|
+
* running a remote-access decision (only non-owner principals are judged).
|
|
36
|
+
* That is exactly the task board's position — it creates, renames, prompts,
|
|
37
|
+
* and reads sessions as the Host itself, which before 0.2.1 was the implicit
|
|
38
|
+
* privilege of every in-process caller. The board claims owner explicitly, and
|
|
39
|
+
* only ever inside its own Host process: no wire field can set a principal,
|
|
40
|
+
* and this plugin never forwards a browser-supplied one.
|
|
41
|
+
*
|
|
42
|
+
* `interactions` stays `'none'`: the board only invokes methods and opens the
|
|
43
|
+
* short `session/follow` stream. The gateway's interactions check gates
|
|
44
|
+
* exactly the forwarded-event endpoints (opening an event stream, answering a
|
|
45
|
+
* waterfall), which this caller never touches; an `'answer'` peer would
|
|
46
|
+
* additionally receive forwarded Host events, a capability the board neither
|
|
47
|
+
* needs nor wants.
|
|
48
|
+
*
|
|
49
|
+
* The peer's signal aborts once `ctx` disposes, so streams opened for it end
|
|
50
|
+
* with the plugin fiber, and `dispose()` is idempotent with racing calls
|
|
51
|
+
* sharing one completion. The id carries no credential — the protocol fixes
|
|
52
|
+
* `principal` as the only identity persisted in a session log — so a fresh
|
|
53
|
+
* unique id per mount is sufficient.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createOwnerPeer(ctx: Context): GatewayPeer;
|
|
19
56
|
interface SessionGateway {
|
|
20
57
|
invoke(request: GatewayRequest): Promise<unknown>;
|
|
21
58
|
stream?(request: GatewayRequest): Promise<AsyncIterable<unknown>>;
|
|
@@ -78,15 +115,16 @@ export interface PromptContext {
|
|
|
78
115
|
/** True when those members run as teammates inside this session (Team Lead). */
|
|
79
116
|
team?: boolean;
|
|
80
117
|
/**
|
|
81
|
-
* Scheduled-run provenance: the instant the
|
|
82
|
-
* IANA zone its wall clock was read in. Present only for a
|
|
83
|
-
* run, so the model can resolve an unqualified date in the
|
|
84
|
-
* instead of guessing from the Host clock (issue #1722).
|
|
118
|
+
* Scheduled-run provenance: the instant the rule actually fired, its kind,
|
|
119
|
+
* and the IANA zone its wall clock was read in. Present only for a
|
|
120
|
+
* timer-triggered run, so the model can resolve an unqualified date in the
|
|
121
|
+
* run's own terms instead of guessing from the Host clock (issue #1722).
|
|
85
122
|
*/
|
|
86
123
|
schedule?: {
|
|
87
124
|
triggeredAt: number;
|
|
88
125
|
timeZone: string;
|
|
89
|
-
|
|
126
|
+
mode: 'cron' | 'once';
|
|
127
|
+
cron?: string;
|
|
90
128
|
};
|
|
91
129
|
}
|
|
92
130
|
/**
|
|
@@ -110,15 +148,25 @@ export declare class HostExecutionRunner {
|
|
|
110
148
|
private readonly gateway;
|
|
111
149
|
private readonly commands?;
|
|
112
150
|
private readonly workspaceRegistry?;
|
|
113
|
-
/**
|
|
151
|
+
/**
|
|
152
|
+
* Newest event sequence per session whose COMPLETE history walk (reaching
|
|
153
|
+
* the execution boundary) found no matching execution end. A later walk may
|
|
154
|
+
* trust everything at or below it and examine only newer records.
|
|
155
|
+
*/
|
|
114
156
|
private readonly scanMemos;
|
|
115
157
|
private readonly unavailableAttempts;
|
|
116
158
|
private readonly unavailableBackoffMs;
|
|
159
|
+
/**
|
|
160
|
+
* The peer every request speaks for. Production passes the peer bound to the
|
|
161
|
+
* plugin fiber; a bare runner (unit tests around fake gateways) gets one
|
|
162
|
+
* bound to a fresh root context instead.
|
|
163
|
+
*/
|
|
164
|
+
private readonly ownerPeer;
|
|
117
165
|
private unsupportedSessionListWarned;
|
|
118
166
|
constructor(gateway: SessionGateway | TypertGateway, commands?: SessionCommandDispatcher | undefined, workspaceRegistry?: TaskBoardWorkspaceRegistry | undefined, unavailableRetry?: {
|
|
119
167
|
attempts?: number;
|
|
120
168
|
backoffMs?: number;
|
|
121
|
-
});
|
|
169
|
+
}, ownerPeer?: GatewayPeer);
|
|
122
170
|
private invoke;
|
|
123
171
|
private stream;
|
|
124
172
|
/**
|
|
@@ -144,6 +192,26 @@ export declare class HostExecutionRunner {
|
|
|
144
192
|
promptContext?: PromptContext;
|
|
145
193
|
onSession?: (sessionId: string) => void;
|
|
146
194
|
onGoalArmed?: (armed: boolean) => void;
|
|
195
|
+
/**
|
|
196
|
+
* GLOBAL native /goal switch, resolved by the caller ONCE per execution and
|
|
197
|
+
* carried here so the decision is frozen for this launch.
|
|
198
|
+
*
|
|
199
|
+
* Frozen deliberately: a settings edit made while the run is in flight must
|
|
200
|
+
* not arm, disarm or otherwise change the goal/acceptance contract of a run
|
|
201
|
+
* already running. Absent means off — the default for every deployment that
|
|
202
|
+
* never touched the switch, including cards written before the option
|
|
203
|
+
* existed. A plain single turn is what an off switch produces; the task-level
|
|
204
|
+
* `goalRun` still applies on top of it (both must ASK for a goal).
|
|
205
|
+
*/
|
|
206
|
+
goalEnabled?: boolean;
|
|
207
|
+
/**
|
|
208
|
+
* The host model catalog, read ONCE per execution by the caller. The card's
|
|
209
|
+
* pinned reasoning effort is sent only when this catalog declares it for the
|
|
210
|
+
* route the execution runs at, so one snapshot decides both the acceptance
|
|
211
|
+
* contract and the effort this run freezes. Absent means "no capability
|
|
212
|
+
* evidence": an effort is then not sent at all.
|
|
213
|
+
*/
|
|
214
|
+
catalog?: ModelCatalogView;
|
|
147
215
|
}): Promise<string>;
|
|
148
216
|
/**
|
|
149
217
|
* Fail closed when a reused session records a different Agent preset than
|
|
@@ -160,10 +228,30 @@ export declare class HostExecutionRunner {
|
|
|
160
228
|
* @param pinned - the preset id the task pins, when it pins one.
|
|
161
229
|
*/
|
|
162
230
|
private assertReusedPreset;
|
|
231
|
+
/**
|
|
232
|
+
* Split a `provider/model` pin. A pin without a provider keeps only the model
|
|
233
|
+
* id: the provider is the one the host catalog declares for it, resolved at
|
|
234
|
+
* launch because the session wire contract requires a provider.
|
|
235
|
+
*/
|
|
236
|
+
private static pinnedRoute;
|
|
237
|
+
/**
|
|
238
|
+
* The route a card that pins no model actually runs at: the session's own
|
|
239
|
+
* recorded selection (the durable fold the GUI reads; `next` is the pending
|
|
240
|
+
* user choice, `lastUsed` what the last request consumed), or — for a session
|
|
241
|
+
* that has selected none yet, which is exactly a session that would start at
|
|
242
|
+
* the deployment default — the host default from the catalog.
|
|
243
|
+
*
|
|
244
|
+
* A route that cannot be READ is undefined and the effort is not sent:
|
|
245
|
+
* falling back to the host default there would silently switch the model of a
|
|
246
|
+
* session this run only meant to continue.
|
|
247
|
+
*/
|
|
248
|
+
private sessionRoute;
|
|
163
249
|
/**
|
|
164
250
|
* Re-assert the pinned execution contract on a session and queue the task
|
|
165
251
|
* prompt. Shared by the fresh-session and reuse paths so both apply exactly
|
|
166
|
-
* the same permission/model pins before the prompt.
|
|
252
|
+
* the same permission/model/effort pins before the prompt. A session that
|
|
253
|
+
* refuses a selection carrying a catalog-confirmed effort throws here, so the
|
|
254
|
+
* prompt is never queued behind a run at the wrong level.
|
|
167
255
|
*/
|
|
168
256
|
private pinAndPrompt;
|
|
169
257
|
/**
|
|
@@ -182,11 +270,21 @@ export declare class HostExecutionRunner {
|
|
|
182
270
|
* dsh's goal-round driver keeps starting continuation rounds until the agent
|
|
183
271
|
* marks it complete (see {@link goalVerdict} for how the run settles).
|
|
184
272
|
*
|
|
185
|
-
*
|
|
273
|
+
* Two switches gate the arming, and BOTH must ask for it:
|
|
274
|
+
* - the GLOBAL native-/goal switch (`goalEnabled`, default OFF), frozen when
|
|
275
|
+
* this execution started; and
|
|
276
|
+
* - the task's own `goalRun` (absent means off, only an explicit true opts
|
|
277
|
+
* the card into a goal run).
|
|
278
|
+
*
|
|
186
279
|
* A refusal (no command dispatcher, no `/goal` command in this cohort, an
|
|
187
|
-
* objective the command rejects) is reported and the run continues as
|
|
188
|
-
*
|
|
189
|
-
*
|
|
280
|
+
* objective the command rejects) is reported and the run continues as a plain
|
|
281
|
+
* turn: the task was asked to run, and a missing goal mode must not lose the
|
|
282
|
+
* work.
|
|
283
|
+
* @param sessionId - the session to arm in.
|
|
284
|
+
* @param task - the task being run.
|
|
285
|
+
* @param context - the run's prompt context.
|
|
286
|
+
* @param goalEnabled - the GLOBAL switch, frozen for this execution.
|
|
287
|
+
* @returns whether `/goal` was really armed.
|
|
190
288
|
*/
|
|
191
289
|
private armGoal;
|
|
192
290
|
/**
|