@linxin666/dsh-client-ui-task-board 0.4.4 → 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.
- package/README.i18n.yaml +2 -2
- package/README.md +42 -11
- package/README.zh.md +42 -11
- package/lib/client.js +4958 -472
- package/lib/client.js.map +1 -1
- package/lib/index.js +2841 -121
- package/lib/types/client/PluginSettingsCard.d.ts +7 -0
- package/lib/types/client/TaskBoardSettingsCard.d.ts +19 -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/board-card-seat.d.ts +34 -0
- package/lib/types/client/index.d.ts +19 -0
- package/lib/types/client/locales.d.ts +103 -1
- package/lib/types/client/native-panel.d.ts +6 -20
- 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 +37 -1
- 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/tasks.d.ts +84 -3
- 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.d.ts +386 -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 +71 -0
- package/lib/types/host/verification-runner.d.ts +108 -0
- package/lib/types/host/workspace-evidence.d.ts +53 -0
- package/lib/types/host-ledger.d.ts +52 -8
- package/lib/types/host-runner.d.ts +23 -1
- package/lib/types/host-service.d.ts +88 -2
- package/lib/types/index.d.ts +80 -26
- package/lib/types/protocol.d.ts +49 -5
- package/lib/types/tool-surface.d.ts +71 -0
- package/package.json +27 -26
- package/src/client/PluginSettingsCard.tsx +9 -0
- package/src/client/TaskBoardSettingsCard.tsx +304 -65
- 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 +17 -1
- 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 +71 -26
- package/src/client/board/TaskForm.tsx +13 -3
- package/src/client/board/VerificationReport.tsx +203 -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 +27 -0
- package/src/client/board/task-markdown.module.css +137 -0
- package/src/client/board/task-markdown.tsx +106 -0
- package/src/client/board-card-seat.ts +102 -0
- package/src/client/board-settings.module.css +66 -0
- package/src/client/board.module.css +488 -63
- package/src/client/index.ts +43 -5
- package/src/client/locales.ts +208 -4
- package/src/client/native-panel.tsx +18 -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 +94 -3
- 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/store.ts +16 -0
- package/src/core/tasks.ts +100 -6
- package/src/core/use-cases/task-parent.ts +3 -2
- package/src/core/use-cases/task-tag.ts +110 -0
- package/src/core/use-cases/task-update.ts +10 -4
- package/src/core/verification.ts +826 -0
- package/src/core/workspace-target.ts +59 -0
- package/src/host/agent-tools.ts +20 -18
- package/src/host/extension-registry.ts +509 -0
- package/src/host/llm-dispatch.ts +87 -0
- package/src/host/verification-gate.ts +331 -0
- package/src/host/verification-runner.ts +535 -0
- package/src/host/workspace-evidence.ts +176 -0
- package/src/host-ai.ts +15 -9
- package/src/host-ledger.ts +143 -32
- package/src/host-routes.ts +15 -2
- package/src/host-runner.ts +60 -11
- package/src/host-service.ts +253 -10
- package/src/index.ts +204 -16
- package/src/protocol.ts +102 -8
- package/src/tool-surface.ts +95 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace targeting for cards that pin none.
|
|
3
|
+
*
|
|
4
|
+
* The Host answers two questions here instead of leaving them to
|
|
5
|
+
* `session.create`, whose own fallback is the Host process working directory
|
|
6
|
+
* (the desktop app's profile directory): which workspace a root card inherits
|
|
7
|
+
* from the session that created it, and which workspace an unpinned run lands
|
|
8
|
+
* in when nothing could be inherited.
|
|
9
|
+
*
|
|
10
|
+
* Structural types only — the Host's `Workspace` entity satisfies them — so the
|
|
11
|
+
* browser program compiles this module without importing an SDK value.
|
|
12
|
+
*
|
|
13
|
+
* @module dsh-task-board/core/workspace-target
|
|
14
|
+
*/
|
|
15
|
+
/** The workspace facts these rules read (a `Workspace` entity subset). */
|
|
16
|
+
export interface WorkspaceCandidate {
|
|
17
|
+
/** Stable workspace record id. */
|
|
18
|
+
readonly id: string;
|
|
19
|
+
/** ISO-8601 instant of the workspace's last durable mutation. */
|
|
20
|
+
readonly updatedAt: string;
|
|
21
|
+
/** Header-validated session ids the workspace owns, most recent first. */
|
|
22
|
+
readonly sessionIds: readonly string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The workspace that owns one session.
|
|
26
|
+
* @param items - the deployment's workspaces.
|
|
27
|
+
* @param sessionId - the session whose workspace is requested.
|
|
28
|
+
* @returns the owning workspace id, or undefined when none claims the session.
|
|
29
|
+
*/
|
|
30
|
+
export declare function workspaceOwningSession(items: readonly WorkspaceCandidate[], sessionId: string): string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* The workspace the board treats as most recently used: the one whose record
|
|
33
|
+
* changed last. Attaching a session stamps that instant, so the newest stamp
|
|
34
|
+
* tracks where the user most recently opened or resumed a session. The
|
|
35
|
+
* deployment's own list order breaks ties, and a stamp that does not parse
|
|
36
|
+
* never outranks one that does; a registry whose stamps are all unreadable
|
|
37
|
+
* falls back to list order rather than to "no workspace at all".
|
|
38
|
+
* @param items - the deployment's workspaces.
|
|
39
|
+
* @returns the most recently used workspace id, or undefined when there is none.
|
|
40
|
+
*/
|
|
41
|
+
export declare function mostRecentWorkspaceId(items: readonly WorkspaceCandidate[]): string | undefined;
|
|
@@ -8,7 +8,7 @@ export interface TaskBoardToolHost {
|
|
|
8
8
|
/** Current board snapshot (tasks + scheduler + power + deployment constants). */
|
|
9
9
|
snapshot(): TaskBoardSnapshot;
|
|
10
10
|
/** Submit one confirmed Host action; returns the resulting snapshot. */
|
|
11
|
-
apply(requestId: string, action: TaskBoardAction, initiator?: string): TaskBoardSnapshot
|
|
11
|
+
apply(requestId: string, action: TaskBoardAction, initiator?: string): TaskBoardSnapshot | Promise<TaskBoardSnapshot>;
|
|
12
12
|
}
|
|
13
13
|
/** Registered tool names, in registration order. */
|
|
14
14
|
export declare const TASK_BOARD_TOOL_NAMES: readonly ["task_board_list", "task_board_get", "task_board_create", "task_board_update", "task_board_set_parent", "task_board_run", "task_board_manage", "task_board_schedule"];
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-side extension registry: the one place that admits external providers,
|
|
3
|
+
* negotiates the contract version, gates them on the board's own enable state,
|
|
4
|
+
* fans board events out to them, and routes dispatched provider actions back.
|
|
5
|
+
*
|
|
6
|
+
* Failure policy (mirrors the board's plugin philosophy): a provider that
|
|
7
|
+
* throws, mis-declares its version, or disables itself degrades only its own
|
|
8
|
+
* surface. The board keeps serving; the registry records what it refused.
|
|
9
|
+
*
|
|
10
|
+
* @module dsh-task-board/host/extension-registry
|
|
11
|
+
*/
|
|
12
|
+
import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
|
|
13
|
+
import type { HostTaskLedger } from '../host-ledger.ts';
|
|
14
|
+
import type { TaskBoardAction, TaskBoardSnapshot } from '../protocol.ts';
|
|
15
|
+
import { type TaskBoardExecutionSettledEvent, type TaskBoardExtension, type TaskBoardExtensionActionRequest, type TaskBoardExtensionPayload, type TaskBoardStatusChangedEvent, type TaskBoardTaskDeletedEvent } from '../core/extension.ts';
|
|
16
|
+
/** Diagnosable refusal raised by the registry and the capability face. */
|
|
17
|
+
export declare class TaskBoardExtensionError extends Error {
|
|
18
|
+
readonly code: string;
|
|
19
|
+
constructor(code: string, message: string);
|
|
20
|
+
}
|
|
21
|
+
/** Logging seat the registry uses; a deployment may supply its own. */
|
|
22
|
+
export interface TaskBoardExtensionLogger {
|
|
23
|
+
warn(message: string, ...rest: unknown[]): void;
|
|
24
|
+
error(message: string, ...rest: unknown[]): void;
|
|
25
|
+
}
|
|
26
|
+
/** The deployment's model-visible tool registry, resolved lazily. */
|
|
27
|
+
export interface TaskBoardExtensionToolRegistry {
|
|
28
|
+
register(definition: ToolDefinition): () => void;
|
|
29
|
+
}
|
|
30
|
+
/** Applies one board action with every board gate intact (never an extension action). */
|
|
31
|
+
export type TaskBoardActionApplier = (action: Exclude<TaskBoardAction, {
|
|
32
|
+
kind: 'extension-action';
|
|
33
|
+
}>, initiator?: string) => TaskBoardSnapshot;
|
|
34
|
+
/** Registry dependencies. */
|
|
35
|
+
export interface TaskBoardExtensionRegistryOptions {
|
|
36
|
+
ledger: HostTaskLedger;
|
|
37
|
+
/** Apply an action through the board service (gates, run dispatch, schedule). */
|
|
38
|
+
apply?: TaskBoardActionApplier;
|
|
39
|
+
/** Resolve the deployment's tool registry; absent loses the tool surface only. */
|
|
40
|
+
toolRegistry?: () => TaskBoardExtensionToolRegistry | undefined;
|
|
41
|
+
now?: () => number;
|
|
42
|
+
uuid?: () => string;
|
|
43
|
+
logger?: TaskBoardExtensionLogger;
|
|
44
|
+
}
|
|
45
|
+
export declare class TaskBoardExtensionRegistry {
|
|
46
|
+
private readonly entries;
|
|
47
|
+
private readonly ledger;
|
|
48
|
+
private readonly applyAction;
|
|
49
|
+
private readonly now;
|
|
50
|
+
private readonly uuid;
|
|
51
|
+
private readonly logger;
|
|
52
|
+
private toolResolver;
|
|
53
|
+
/** Board master switch (enabled config); providers start only while it is on. */
|
|
54
|
+
private boardEnabled;
|
|
55
|
+
private disposed;
|
|
56
|
+
constructor(options: TaskBoardExtensionRegistryOptions);
|
|
57
|
+
/**
|
|
58
|
+
* Pin (or replace) the deployment's tool registry. A registry that appears
|
|
59
|
+
* after a provider started is picked up here, which is what keeps the tool
|
|
60
|
+
* surface alive across the board's own late service resolution.
|
|
61
|
+
*/
|
|
62
|
+
setToolRegistry(resolver: (() => TaskBoardExtensionToolRegistry | undefined) | undefined): void;
|
|
63
|
+
/** Apply the board's master switch; providers start and stop with it. */
|
|
64
|
+
setEnabled(enabled: boolean): void;
|
|
65
|
+
/** Whether the board currently runs a given extension (board x extension). */
|
|
66
|
+
isActive(extensionId: string): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Admit one provider. Idempotent by id: a second registration of the same id
|
|
69
|
+
* is a no-op that returns the same release handle. A contract-version
|
|
70
|
+
* mismatch is refused visibly (logged and thrown) without disturbing the
|
|
71
|
+
* board or any other provider.
|
|
72
|
+
* @param extension - the provider to admit.
|
|
73
|
+
* @returns a disposer releasing the registration (idempotent).
|
|
74
|
+
*/
|
|
75
|
+
registerExtension(extension: TaskBoardExtension): () => void;
|
|
76
|
+
/** Remove one provider; its provider-side work stops and its payload stays stored. */
|
|
77
|
+
unregister(extensionId: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* Route one dispatched provider action. Refuses visibly when the extension is
|
|
80
|
+
* unknown, currently gated off, or does not implement the action.
|
|
81
|
+
*/
|
|
82
|
+
handleAction(request: TaskBoardExtensionActionRequest): Promise<unknown>;
|
|
83
|
+
/** Published summaries of currently running providers, keyed by extension id. */
|
|
84
|
+
published(): Record<string, TaskBoardExtensionPayload>;
|
|
85
|
+
/** Fan a status change out to every running provider, isolated per callback. */
|
|
86
|
+
emitStatusChanged(event: TaskBoardStatusChangedEvent): void;
|
|
87
|
+
/** Fan an execution settlement out to every running provider. */
|
|
88
|
+
emitExecutionSettled(event: TaskBoardExecutionSettledEvent): void;
|
|
89
|
+
/** Fan a task deletion out to every running provider. */
|
|
90
|
+
emitTaskDeleted(event: TaskBoardTaskDeletedEvent): void;
|
|
91
|
+
/** Stop every provider and release the registry (idempotent). */
|
|
92
|
+
dispose(): void;
|
|
93
|
+
private emit;
|
|
94
|
+
/** Start or stop one entry against the current board + extension gate. */
|
|
95
|
+
private reconcile;
|
|
96
|
+
private startEntry;
|
|
97
|
+
private stopEntry;
|
|
98
|
+
/** (Re)register every buffered tool against the current tool registry. */
|
|
99
|
+
private syncTools;
|
|
100
|
+
private disposeTools;
|
|
101
|
+
private assertActive;
|
|
102
|
+
/** Build the per-extension capability face. */
|
|
103
|
+
private capabilities;
|
|
104
|
+
private createTask;
|
|
105
|
+
private patchContent;
|
|
106
|
+
private setStatus;
|
|
107
|
+
private linkedTasks;
|
|
108
|
+
private writeIntegration;
|
|
109
|
+
private publish;
|
|
110
|
+
private registerTool;
|
|
111
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-shot model dispatch for the board's Host half.
|
|
3
|
+
*
|
|
4
|
+
* The board makes two one-shot model calls: the goal-acceptance judge
|
|
5
|
+
* (`verification-runner.ts`) and the "parse pasted text" action
|
|
6
|
+
* (`host-ai.ts`). Both call the official `llm` service, and both must call it
|
|
7
|
+
* the way the official agent loop does.
|
|
8
|
+
*
|
|
9
|
+
* The runtime exposes two entry points with the same name shape. The public
|
|
10
|
+
* `stream(options)` is a single-argument convenience method, and it is an
|
|
11
|
+
* ordinary instance property — any plugin may overwrite it. The registration
|
|
12
|
+
* bound path `prepareCall(config)` returns a one-shot handle whose
|
|
13
|
+
* `stream(request)` reaches the adapter through the runtime's internal
|
|
14
|
+
* dispatch instead, and `ctx.on('llm/stream')` remains the supported seam every
|
|
15
|
+
* extension is expected to register on.
|
|
16
|
+
*
|
|
17
|
+
* A third-party provider plugin that patched `llm.stream` with the waterfall
|
|
18
|
+
* listener signature `(options, next)` broke every caller that used the public
|
|
19
|
+
* method: `llm.stream(options)` invoked the listener with `next === undefined`,
|
|
20
|
+
* so the guard's `for await (const chunk of next())` raised
|
|
21
|
+
* `TypeError: next(...) is not a function or its return value is not async
|
|
22
|
+
* iterable`. The agent loop was unaffected because it already dispatches
|
|
23
|
+
* through `prepareCall(...).stream(...)`; the board's two one-shot calls were
|
|
24
|
+
* not, and each completed acceptance was recorded as an anomaly
|
|
25
|
+
* ("验收异常(裁判请求失败)") after two judge attempts.
|
|
26
|
+
*
|
|
27
|
+
* Dispatching through the prepared call fixes that at the owner: the board no
|
|
28
|
+
* longer depends on a mutable instance property that a plugin may legitimately
|
|
29
|
+
* replace, and a plugin that registers on `llm/stream` is still invoked. The
|
|
30
|
+
* public method remains the fallback for a runtime that exposes no
|
|
31
|
+
* `prepareCall` at all, so an older or proxied service keeps working.
|
|
32
|
+
*/
|
|
33
|
+
import { type GenerateOptions, type LlmRuntime, type StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
34
|
+
/** The request fields the board adds around a call config. */
|
|
35
|
+
export type OneShotRequest = Pick<GenerateOptions, 'messages' | 'signal'> & Pick<GenerateOptions, 'system'>;
|
|
36
|
+
/**
|
|
37
|
+
* The call config as this board's own domain types carry it.
|
|
38
|
+
*
|
|
39
|
+
* `VerificationRoute.reasoningEffort` and the parse route are plain strings (the
|
|
40
|
+
* catalog publishes effort ids as strings), while the SDK brands them
|
|
41
|
+
* nominally; the single crossing is branded here so no caller repeats the
|
|
42
|
+
* assertion.
|
|
43
|
+
*/
|
|
44
|
+
export interface OneShotCallConfig {
|
|
45
|
+
provider: string;
|
|
46
|
+
model: string;
|
|
47
|
+
reasoningEffort?: string;
|
|
48
|
+
temperature?: number;
|
|
49
|
+
maxTokens?: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Open one one-shot model stream on the registration-bound dispatch path.
|
|
53
|
+
*
|
|
54
|
+
* The call-config fields must travel inside `config` (not the request): the
|
|
55
|
+
* prepared handle refuses a request whose config fields differ from the ones it
|
|
56
|
+
* resolved, so the request carries only messages, signal, and the optional
|
|
57
|
+
* one-shot system prompt.
|
|
58
|
+
* @param llm - the Host's llm service.
|
|
59
|
+
* @param config - provider/model route plus the caller's sampling controls.
|
|
60
|
+
* @param request - the assembled messages and cancellation for this call.
|
|
61
|
+
* @returns the chunk stream, including any registered `llm/stream` listener.
|
|
62
|
+
*/
|
|
63
|
+
export declare function openOneShotStream(llm: LlmRuntime, config: OneShotCallConfig, request: OneShotRequest): Promise<AsyncIterable<StreamChunk>>;
|
|
@@ -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;
|
|
@@ -89,8 +90,14 @@ export declare class HostTaskLedger {
|
|
|
89
90
|
readonly lockFile: string;
|
|
90
91
|
/** Small sidecar for the 30 s scheduler heartbeat (lastTickAt only). */
|
|
91
92
|
readonly schedulerFile: string;
|
|
92
|
-
/**
|
|
93
|
-
readonly
|
|
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;
|
|
94
101
|
/**
|
|
95
102
|
* Deployment subtask depth limit (1..3): the lineage gate every write obeys.
|
|
96
103
|
* The settings card edits it live, so {@link setMaxSubtaskDepth} mutates it
|
|
@@ -98,7 +105,7 @@ export declare class HostTaskLedger {
|
|
|
98
105
|
*/
|
|
99
106
|
private depthLimit;
|
|
100
107
|
constructor(dir?: string, now?: () => number, options?: {
|
|
101
|
-
sessionDefaultPermission?: TaskPermission;
|
|
108
|
+
sessionDefaultPermission?: TaskPermission | (() => TaskPermission);
|
|
102
109
|
maxSubtaskDepth?: number;
|
|
103
110
|
});
|
|
104
111
|
/** Current subtask depth limit (1..3). */
|
|
@@ -158,16 +165,21 @@ export declare class HostTaskLedger {
|
|
|
158
165
|
*/
|
|
159
166
|
settle(taskId: string, executionId: string, outcome: ExecutionOutcome, error?: string): void;
|
|
160
167
|
attachSession(taskId: string, executionId: string, sessionId: string): void;
|
|
168
|
+
allTasks(): readonly TaskRecord[];
|
|
169
|
+
getTask(id: string): TaskRecord | undefined;
|
|
170
|
+
saveTaskRecord(task: TaskRecord): void;
|
|
161
171
|
private apply;
|
|
162
172
|
/**
|
|
163
173
|
* The binding that makes a run illegal, if any.
|
|
164
174
|
*
|
|
165
175
|
* A plain cascade launches one session per participant, so every participant
|
|
166
176
|
* carries its own resolved binding and each one gates the run. A team run
|
|
167
|
-
* launches only the Lead session: the Lead's binding gates it,
|
|
168
|
-
*
|
|
169
|
-
* refused
|
|
170
|
-
*
|
|
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).
|
|
171
183
|
* @param root - the task being run.
|
|
172
184
|
* @returns the first refusal, or undefined when the run may start.
|
|
173
185
|
*/
|
|
@@ -239,7 +251,7 @@ export declare class HostTaskLedger {
|
|
|
239
251
|
private repairSchedules;
|
|
240
252
|
private reconcileInterruptedStarts;
|
|
241
253
|
/**
|
|
242
|
-
* Field-preserving migration of
|
|
254
|
+
* Field-preserving migration of an older document. It first proves every task
|
|
243
255
|
* row is structurally valid, so a document that would silently drop or coerce
|
|
244
256
|
* rows fails loudly instead (no quarantined-empty restart), and then reuses
|
|
245
257
|
* the current normalization.
|
|
@@ -250,8 +262,40 @@ export declare class HostTaskLedger {
|
|
|
250
262
|
* (the stored `nextRunAt` already encodes the old zone), but the rule stops
|
|
251
263
|
* following a later `TZ` change, which is what made an existing schedule
|
|
252
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.
|
|
253
270
|
*/
|
|
254
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;
|
|
255
299
|
private load;
|
|
256
300
|
private normalizeDocument;
|
|
257
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
|
-
/**
|
|
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
|
}
|
|
@@ -118,6 +121,14 @@ export declare class HostExecutionRunner {
|
|
|
118
121
|
});
|
|
119
122
|
private invoke;
|
|
120
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;
|
|
121
132
|
/**
|
|
122
133
|
* Launch one execution. Without `options.reuseSessionId` a fresh session is
|
|
123
134
|
* created, renamed, pinned, and prompted (the historical contract). With it,
|
|
@@ -131,6 +142,8 @@ export declare class HostExecutionRunner {
|
|
|
131
142
|
launch(task: TaskRecord, options?: {
|
|
132
143
|
reuseSessionId?: string;
|
|
133
144
|
promptContext?: PromptContext;
|
|
145
|
+
onSession?: (sessionId: string) => void;
|
|
146
|
+
onGoalArmed?: (armed: boolean) => void;
|
|
134
147
|
}): Promise<string>;
|
|
135
148
|
/**
|
|
136
149
|
* Fail closed when a reused session records a different Agent preset than
|
|
@@ -153,6 +166,15 @@ export declare class HostExecutionRunner {
|
|
|
153
166
|
* the same permission/model pins before the prompt.
|
|
154
167
|
*/
|
|
155
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>;
|
|
156
178
|
/**
|
|
157
179
|
* Arm dsh's built-in goal on the session. The task prompt is queued FIRST, so
|
|
158
180
|
* the session's first turn carries the instruction verbatim; `/goal
|