@mstar-harness/dsh 3.8.0 → 3.8.2
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 +142 -194
- package/README.zh.md +29 -17
- package/bundle/README.md +83 -171
- package/dist/client/index.d.ts +17 -8
- package/dist/client/panel/MstarPanelTitle.d.ts +13 -0
- package/dist/client/panel/PanelView.d.ts +56 -52
- package/dist/client/panel/TabNav.d.ts +17 -14
- package/dist/client/panel/definition.d.ts +23 -0
- package/dist/client/panel/engine-status-client.d.ts +84 -6
- package/dist/client/panel/graph/project-graph.d.ts +35 -64
- package/dist/client/panel/graph/schema.d.ts +1 -2
- package/dist/client/panel/guards.d.ts +41 -1
- package/dist/client/panel/locale.d.ts +1 -1
- package/dist/client/panel/mstar-glyph.d.ts +22 -0
- package/dist/client/panel/pages/AgentListPage.d.ts +71 -0
- package/dist/client/panel/pages/EventLogPage.d.ts +7 -4
- package/dist/client/panel/pages/IterationInfoSection.d.ts +16 -13
- package/dist/client/panel/pages/IterationTaskPage.d.ts +13 -14
- package/dist/client/panel/panel-store.d.ts +28 -0
- package/dist/client/panel/sidebar.d.ts +13 -7
- package/dist/client/panel/state-section.d.ts +25 -3
- package/dist/client/panel/use-mstar-engine-status.d.ts +39 -14
- package/dist/client/panel/zones/Legend.d.ts +5 -3
- package/dist/client/panel/zones/TaskBoard.d.ts +13 -9
- package/dist/client.js +1038 -1242
- package/dist/engine-status-endpoint.d.ts +85 -8
- package/dist/engine-status-store.d.ts +91 -1
- package/dist/engine-status-wire.d.ts +9 -0
- package/dist/gates/_shared.d.ts +61 -9
- package/dist/gates/adapter.d.ts +32 -2
- package/dist/gates/agent-flow.d.ts +312 -60
- package/dist/gates/catalog.d.ts +59 -38
- package/dist/gates/dispatch.d.ts +11 -2
- package/dist/gates/goal-bridge.d.ts +10 -130
- package/dist/gates/plan-mode-bridge.d.ts +20 -11
- package/dist/gates/role-persona.d.ts +16 -0
- package/dist/gates/steering.d.ts +41 -0
- package/dist/gates/workflow-ledger.d.ts +31 -4
- package/dist/gates/workflow-selection.d.ts +41 -20
- package/dist/index.js +1208 -394
- package/dist/types.d.ts +50 -18
- package/harness-commands/amazing-pr-review.md +2 -0
- package/harness-commands/codebase-audit.md +2 -0
- package/harness-skills/mstar-host/SKILL.md +3 -1
- package/harness-skills/mstar-host/references/dsh-workflow-scripts.md +424 -0
- package/harness-skills/mstar-host/references/dsh.md +259 -266
- package/harness-skills/mstar-roles/references/project-manager.md +2 -0
- package/harness-skills/mstar-sdd/SKILL.md +2 -0
- package/package.json +66 -64
- package/dist/client/panel/pages/AgentCanvasPage.d.ts +0 -345
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Context } from '@deepseek-ai/cordis';
|
|
2
2
|
import type { AgentFlowView } from '../types.ts';
|
|
3
3
|
import type { Config } from './_shared.ts';
|
|
4
|
+
import type { ActiveWorkflowSelection, SessionHint } from './workflow-selection.ts';
|
|
4
5
|
/** The agent-flow ledger file name under `{HARNESS_DIR}`. */
|
|
5
6
|
export declare const AGENT_FLOW_FILE = "agent-flow.jsonl";
|
|
6
7
|
/** Truncation bound: the ledger keeps only the most recent events. */
|
|
@@ -26,8 +27,11 @@ export declare const AGENT_FLOW_SIZE_GATE_BYTES: number;
|
|
|
26
27
|
* unbounded. ID-sized fields (`runId`, `childId`) SKIP the row when
|
|
27
28
|
* oversized — truncating them could forge collisions; display fields
|
|
28
29
|
* (`name`, `label`, `phase`) are truncated deterministically with a suffix
|
|
29
|
-
* marker.
|
|
30
|
-
*
|
|
30
|
+
* marker. The settle identity fields are OPTIONAL: an invalid or oversized
|
|
31
|
+
* `childId` / `taskRef` omits only the FIELD — the real completion still
|
|
32
|
+
* records (see {@link optionalLedgerId}). `2^31` bounds every sequence number
|
|
33
|
+
* (envelope + member) — the cursor-math safe range (see the consumer's
|
|
34
|
+
* durable watermark).
|
|
31
35
|
*/
|
|
32
36
|
export declare const WORKFLOW_LEDGER_MAX_ID_LENGTH = 512;
|
|
33
37
|
/** Cap for label-sized display fields (`label`, `phase`). */
|
|
@@ -54,6 +58,20 @@ export declare const WORKFLOW_LEDGER_MAX_SEQ: number;
|
|
|
54
58
|
* most one code point of slack. Pure — NEVER throws.
|
|
55
59
|
*/
|
|
56
60
|
export declare function truncateLedgerField(value: string, cap: number): string;
|
|
61
|
+
/**
|
|
62
|
+
* Canonicalize one Assignment `Execute as` value for the ledger's `role`
|
|
63
|
+
* field: `trim()`, then strip ONE leading `@`. The `role` column is what
|
|
64
|
+
* every consumer groups by, so `@explore` and `explore` must be the SAME
|
|
65
|
+
* role — normalization happens ONCE, at the write boundary (the dispatch row
|
|
66
|
+
* and its pairing ref, plus the direct `recordSettle` /
|
|
67
|
+
* `recordSubagentLink` entry points, whose callers may hand in a raw
|
|
68
|
+
* Assignment value). NO case folding: role ids are host-defined strings, and
|
|
69
|
+
* folding could merge ids a host intends to keep distinct. No interior
|
|
70
|
+
* rewrite either — only the edges are trimmed. A missing role stays `''`,
|
|
71
|
+
* and rows already on disk are never rewritten or renormalized. Pure —
|
|
72
|
+
* NEVER throws.
|
|
73
|
+
*/
|
|
74
|
+
export declare function normalizeRoleId(value: string): string;
|
|
57
75
|
/** Logger label for the agent-flow ledger (dsh logger naming: `<scope>/<subject>`). */
|
|
58
76
|
export declare const AGENT_FLOW_LOGGER = "mstar/agent-flow";
|
|
59
77
|
/**
|
|
@@ -71,12 +89,12 @@ export declare const SETTLE_SEAM = "tools/post-execute";
|
|
|
71
89
|
* surface, so the constant was renamed to the accurate `PAIRING` name. The
|
|
72
90
|
* message states the VERIFIED pairing facts: the seam is emitted by the
|
|
73
91
|
* registry; foreground dispatch calls settle via it, background subagents
|
|
74
|
-
* settle via `ctx.jobs.onJobDone` pairing; only
|
|
75
|
-
* dispatch-only (never fabricated settlement). Logged
|
|
76
|
-
* (≈ once per apply — the same module-level flag)
|
|
77
|
-
* listener is registered.
|
|
92
|
+
* settle via the `ctx.inject(['jobs'])` → `jobs.onJobDone` pairing; only
|
|
93
|
+
* unpaired payloads stay dispatch-only (never fabricated settlement). Logged
|
|
94
|
+
* ONCE per logger binding (≈ once per apply — the same module-level flag)
|
|
95
|
+
* when the pairing listener is registered.
|
|
78
96
|
*/
|
|
79
|
-
export declare const SETTLE_SEAM_PAIRING_NOTE = "settle seam \"tools/post-execute\" IS part of the verified dsh-tools registry surface (runPostExecute dispatches it for every tool call) \u2014 foreground dispatch calls settle here, background subagents settle via ctx.jobs.onJobDone pairing; only UNPAIRED payloads (non-dispatch tools, calls outside the apply-scoped pairing window) stay dispatch-only \u2014 never a fabricated settle";
|
|
97
|
+
export declare const SETTLE_SEAM_PAIRING_NOTE = "settle seam \"tools/post-execute\" IS part of the verified dsh-tools registry surface (runPostExecute dispatches it for every tool call) \u2014 foreground dispatch calls settle here, background subagents settle via ctx.inject(['jobs']) \u2192 jobs.onJobDone pairing; only UNPAIRED payloads (non-dispatch tools, calls outside the apply-scoped pairing window) stay dispatch-only \u2014 never a fabricated settle";
|
|
80
98
|
/** Dispatch verdict vocabulary (spec §2.1.3). */
|
|
81
99
|
export type DispatchVerdict = 'ok' | 'advisory' | 'denied';
|
|
82
100
|
/** Settle outcome vocabulary (spec §2.1.3). */
|
|
@@ -177,13 +195,55 @@ export type AgentFlowEvent = {
|
|
|
177
195
|
* `role` is the Assignment `Execute as` ('' when missing), `planId` /
|
|
178
196
|
* `taskId` the plan + `Task N` tags. Written for every paired settle;
|
|
179
197
|
* ABSENT on unpaired (legacy) settles — the client pairs on identity
|
|
180
|
-
* presence
|
|
181
|
-
* written here (`taskRef` is reserved as the distinct field name if a
|
|
182
|
-
* future audit needs it — it never collides with `taskId`).
|
|
198
|
+
* presence (`paired` in the view never depends on `childId`).
|
|
183
199
|
*/
|
|
184
200
|
role?: string;
|
|
185
201
|
planId?: string;
|
|
186
202
|
taskId?: string;
|
|
203
|
+
/**
|
|
204
|
+
* The settled child session's stable id, when a seam actually supplied
|
|
205
|
+
* one: the returned foreground `runId`, or a background job's child id
|
|
206
|
+
* once the catalog join copied it onto the dispatch ref. OMITTED
|
|
207
|
+
* otherwise — never fabricated, never a registry job id.
|
|
208
|
+
*/
|
|
209
|
+
childId?: string;
|
|
210
|
+
/**
|
|
211
|
+
* The registry background-job id a background settle pairs on
|
|
212
|
+
* (`jobs.onJobDone` → `recordJobSettle`). A jobs-registry key
|
|
213
|
+
* (`<kind>-N`), NEVER a child session id and never the Assignment
|
|
214
|
+
* `Task N` tag (`taskId`).
|
|
215
|
+
*/
|
|
216
|
+
taskRef?: string;
|
|
217
|
+
} | {
|
|
218
|
+
v: 1;
|
|
219
|
+
ts: number;
|
|
220
|
+
kind: 'subagent-link';
|
|
221
|
+
/** The dispatching session's stable id (the paired dispatch ref's agent; absent when it carried none). */
|
|
222
|
+
agent?: string;
|
|
223
|
+
/**
|
|
224
|
+
* The catalog `childId` — the child session the dispatch actually
|
|
225
|
+
* started. REQUIRED: a row whose identity is missing, empty, or
|
|
226
|
+
* oversized is skipped (never truncated, never fabricated).
|
|
227
|
+
*/
|
|
228
|
+
childId: string;
|
|
229
|
+
/**
|
|
230
|
+
* The delegation `description` the join correlated on, DISPLAY-
|
|
231
|
+
* normalized and capped at this write boundary (the matcher always
|
|
232
|
+
* compares the RAW label, never this value).
|
|
233
|
+
*/
|
|
234
|
+
label: string;
|
|
235
|
+
/** The paired dispatch's Assignment `Execute as` — written even when `''`. */
|
|
236
|
+
role: string;
|
|
237
|
+
/** The paired dispatch's `planIdOf(header)`. */
|
|
238
|
+
planId?: string;
|
|
239
|
+
/** The paired dispatch's Assignment `Task N` tag. */
|
|
240
|
+
taskId?: string;
|
|
241
|
+
/**
|
|
242
|
+
* The registry background-job id, on a link whose dispatch started a
|
|
243
|
+
* background job (`<kind>-N`). A continuable link omits it — that path
|
|
244
|
+
* starts no registry job. Never a child session id.
|
|
245
|
+
*/
|
|
246
|
+
taskRef?: string;
|
|
187
247
|
} | {
|
|
188
248
|
v: 1;
|
|
189
249
|
ts: number;
|
|
@@ -220,12 +280,71 @@ export interface AgentFlowDispatchRef {
|
|
|
220
280
|
workflowDir: string;
|
|
221
281
|
/** The dispatching session's stable id ('' when the exec carried none). */
|
|
222
282
|
agent?: string;
|
|
223
|
-
/** Assignment `Execute as` ('' when missing — the dispatch event's grammar). */
|
|
283
|
+
/** Assignment `Execute as`, canonicalized by `normalizeRoleId` ('' when missing — the dispatch event's grammar). */
|
|
224
284
|
role: string;
|
|
225
285
|
/** `planIdOf(header)` — the dispatch event's grammar. */
|
|
226
286
|
planId?: string;
|
|
227
287
|
/** `taskIdOf(prompt)` — the Assignment `Task N` tag, NOT a registry task id. */
|
|
228
288
|
taskId?: string;
|
|
289
|
+
/**
|
|
290
|
+
* The child session's stable id, once an upstream seam actually supplied
|
|
291
|
+
* one: the catalog join copies the observed catalog `childId` onto this
|
|
292
|
+
* ref (the same object survives into `dispatchByJobId`, so a background
|
|
293
|
+
* settle then carries it). Omitted until known — never a synthetic id and
|
|
294
|
+
* never a registry job id.
|
|
295
|
+
*/
|
|
296
|
+
childId?: string;
|
|
297
|
+
/**
|
|
298
|
+
* The registry job id of the background call this dispatch started
|
|
299
|
+
* (observed at `tools/post-execute`, paired by `recordJobSettle`). A
|
|
300
|
+
* jobs-registry key (`<kind>-N`) — NEVER a child session id and never the
|
|
301
|
+
* `Task N` tag.
|
|
302
|
+
*/
|
|
303
|
+
taskRef?: string;
|
|
304
|
+
/**
|
|
305
|
+
* The admitted call-window catalog CANDIDATE of this dispatch: the live
|
|
306
|
+
* parent Session it runs on plus the RAW delegation label that names its
|
|
307
|
+
* slot. Present only while a reservation is live — `recordDispatch`
|
|
308
|
+
* installs it, and the post-execute branch either advances it to eligible
|
|
309
|
+
* or retires it (both release this field). Its presence is what lets the
|
|
310
|
+
* post-execute path find the join without re-deriving the session.
|
|
311
|
+
*/
|
|
312
|
+
catalog?: {
|
|
313
|
+
session: AgentFlowSessionView;
|
|
314
|
+
label: string;
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* The structural view of one live dsh `Session` the call-window join needs:
|
|
319
|
+
* the VERIFIED upstream surface (`id` / `seq` / `eventAt`). A real Session
|
|
320
|
+
* exposes no `events` member — that read is why the join uses `eventAt(seq)`
|
|
321
|
+
* over a bounded window instead of copying a log. Structural by design (no
|
|
322
|
+
* session package dependency); `seq` is read LIVE at every use, so the
|
|
323
|
+
* captured view is also the catch-up bound source while `fromSeq` (stored on
|
|
324
|
+
* the join) keeps the capture moment.
|
|
325
|
+
*/
|
|
326
|
+
export interface AgentFlowSessionView {
|
|
327
|
+
readonly id: string;
|
|
328
|
+
readonly seq: number;
|
|
329
|
+
eventAt(seq: number): unknown;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* One pending call-window catalog candidate: the recorded dispatch ref and
|
|
333
|
+
* the `fromSeq` window start. `result` marks ELIGIBILITY — an inert
|
|
334
|
+
* candidate (no `result` yet, awaiting its post-execute branch) can never be
|
|
335
|
+
* matched by a catalog, and the slot's `null` tombstone is a
|
|
336
|
+
* consumed/rejected candidate, never a second one.
|
|
337
|
+
*/
|
|
338
|
+
export interface AgentFlowCatalogJoin {
|
|
339
|
+
ref: AgentFlowDispatchRef;
|
|
340
|
+
fromSeq: number;
|
|
341
|
+
result?: {
|
|
342
|
+
kind: 'background';
|
|
343
|
+
jobId: string;
|
|
344
|
+
} | {
|
|
345
|
+
kind: 'continuable';
|
|
346
|
+
subagentId: string;
|
|
347
|
+
};
|
|
229
348
|
}
|
|
230
349
|
/**
|
|
231
350
|
* The apply-scoped pairing store (
|
|
@@ -233,8 +352,8 @@ export interface AgentFlowDispatchRef {
|
|
|
233
352
|
* cache; an HMR restart resets it, and completions outside the window stay
|
|
234
353
|
* unpaired → no settle, the documented honest degrade). Maps are keyed by
|
|
235
354
|
* the TWO verified pairing keys: the tool-call `callId` (pre → post-execute)
|
|
236
|
-
* and the registry background-
|
|
237
|
-
* `onJobDone` terminal).
|
|
355
|
+
* and the registry background-job id (post-execute background shape →
|
|
356
|
+
* `jobs.onJobDone` terminal).
|
|
238
357
|
*/
|
|
239
358
|
export interface AgentFlowPairing {
|
|
240
359
|
/**
|
|
@@ -248,8 +367,19 @@ export interface AgentFlowPairing {
|
|
|
248
367
|
* by the post-execute branch — the map holds only in-flight calls.
|
|
249
368
|
*/
|
|
250
369
|
dispatchByCallId: Map<string, AgentFlowDispatchRef>;
|
|
251
|
-
/** Registry background-job id (`JobSnapshot.id`) → the dispatch that started it (populated by the post-execute background branch; consumed by `
|
|
252
|
-
|
|
370
|
+
/** Registry background-job id (`JobSnapshot.id`) → the dispatch that started it (populated by the post-execute background branch; consumed by `recordJobSettle`). */
|
|
371
|
+
dispatchByJobId: Map<string, AgentFlowDispatchRef>;
|
|
372
|
+
/**
|
|
373
|
+
* The call-window catalog join's slots: the EXACT parent Session object →
|
|
374
|
+
* (RAW delegation label → candidate). A `null` slot is a consumed or
|
|
375
|
+
* rejected tombstone, never a second candidate — reuse is what would let a
|
|
376
|
+
* delayed duplicate label or a pre-existing catalog acquire a new owner.
|
|
377
|
+
* Keying on the session OBJECT (not its id) is what makes the namespace
|
|
378
|
+
* session-incarnation safe and delimiter-free; per Session the map holds at
|
|
379
|
+
* most `AGENT_FLOW_MAX_EVENTS` slots (new keys are refused at capacity —
|
|
380
|
+
* existing pending entries and tombstones are retained).
|
|
381
|
+
*/
|
|
382
|
+
catalogBySession: WeakMap<object, Map<string, AgentFlowCatalogJoin | null>>;
|
|
253
383
|
}
|
|
254
384
|
/** Module-scoped log sink (bound to `mstar/agent-flow` by the entry at apply). */
|
|
255
385
|
type AgentFlowLogSink = (level: 'info' | 'warn' | 'error', message: string) => void;
|
|
@@ -274,8 +404,8 @@ type AgentFlowInvalidator = (harnessDir: string) => void;
|
|
|
274
404
|
export declare function setAgentFlowInvalidator(invalidate: AgentFlowInvalidator | undefined): AgentFlowInvalidator | undefined;
|
|
275
405
|
/**
|
|
276
406
|
* Bind the module's log sink (called once at apply; tests may rebind to
|
|
277
|
-
* capture ledger logs). Rebinding RESETS the once-per-apply
|
|
278
|
-
*
|
|
407
|
+
* capture ledger logs). Rebinding RESETS the once-per-apply trace latches
|
|
408
|
+
* — each binding is a fresh "apply" (production binds once; tests bind
|
|
279
409
|
* per case for deterministic capture).
|
|
280
410
|
* @param sink - the sink (entry binds `ctx.logger('mstar/agent-flow')`);
|
|
281
411
|
* `undefined` clears the binding (restores the pre-bind no-op state).
|
|
@@ -295,24 +425,50 @@ export declare function setAgentFlowLogger(sink: AgentFlowLogSink | undefined):
|
|
|
295
425
|
*/
|
|
296
426
|
export declare function taskIdOf(prompt: string): string | undefined;
|
|
297
427
|
/**
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
|
|
304
|
-
|
|
428
|
+
* The write-path selection for one session: the resolver verdict plus the
|
|
429
|
+
* absolute dir that verdict resolves to. The ledger needs BOTH (it
|
|
430
|
+
* classifies the unbound skip to keep the no-backfill floor honest), and
|
|
431
|
+
* both must come from ONE registry read — {@link resolveAgentFlowWriteDir}
|
|
432
|
+
* is the dir-only facade over this.
|
|
433
|
+
*/
|
|
434
|
+
export interface AgentFlowWriteTarget {
|
|
435
|
+
/** The absolute workflow dir (`<harnessDir>/workflows/<id>`), or `null` when this session may not write. */
|
|
436
|
+
dir: string | null;
|
|
437
|
+
/** The verdict `dir` was derived from. */
|
|
438
|
+
selection: ActiveWorkflowSelection;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Resolve the agent-flow WRITE target for one harness dir: the ACTIVE
|
|
442
|
+
* workflow the CARRYING SESSION is bound to (root v2 `workflows[]` — the
|
|
443
|
+
* shared active-set resolver; the terminal-mtime fallback is
|
|
444
|
+
* catalog-read-only and MUST NOT enter the writer's module graph). The active
|
|
445
|
+
* set is defined by root `workflows[]` MEMBERSHIP — non-terminal lifecycles
|
|
446
|
+
* (`running` AND `paused`; terminal lifecycles are removed at terminal) — so
|
|
447
|
+
* a paused lifecycle stays a valid append target (explicit decision, plan
|
|
305
448
|
* the workflow viz plan).
|
|
306
449
|
*
|
|
307
|
-
*
|
|
450
|
+
* D4 binding: `hint` carries the session's lease/cwd/pick evidence, so two
|
|
451
|
+
* sessions writing in ONE harness reach two different workflow dirs. An
|
|
452
|
+
* omitted hint is the exec-less case — the automatic rungs miss and only a
|
|
453
|
+
* unique active lifecycle resolves.
|
|
454
|
+
*
|
|
455
|
+
* No bound entry → `dir: null`: the record is SKIPPED with a one-time warn
|
|
308
456
|
* (per sink binding) — never a silent write into the root v1 file, never a
|
|
309
|
-
* write into a terminal snapshot dir
|
|
310
|
-
* rule — the writer appends only to an
|
|
457
|
+
* write into a terminal snapshot dir, never another session's lifecycle
|
|
458
|
+
* (compass v3.0.0 § Catalog selection rule — the writer appends only to an
|
|
459
|
+
* active lifecycle).
|
|
311
460
|
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
312
|
-
* @
|
|
313
|
-
* `null` when no active lifecycle resolves.
|
|
461
|
+
* @param hint - the carrying session's structural identity + durable pick.
|
|
314
462
|
*/
|
|
315
|
-
export declare function
|
|
463
|
+
export declare function resolveAgentFlowWriteTarget(harnessDir: string, hint?: SessionHint): AgentFlowWriteTarget;
|
|
464
|
+
/**
|
|
465
|
+
* The dir-only facade over {@link resolveAgentFlowWriteTarget} — the pinned
|
|
466
|
+
* writer contract every record path uses.
|
|
467
|
+
* @param harnessDir - the resolved `{HARNESS_DIR}`.
|
|
468
|
+
* @param hint - the carrying session's structural identity + durable pick.
|
|
469
|
+
* @returns the absolute workflow dir, or `null` when nothing may be written.
|
|
470
|
+
*/
|
|
471
|
+
export declare function resolveAgentFlowWriteDir(harnessDir: string, hint?: SessionHint): string | null;
|
|
316
472
|
/**
|
|
317
473
|
* Lock-directory name for the per-workflow write lock :
|
|
318
474
|
* guards the ledger append + size-gated truncating read-modify-write AND
|
|
@@ -378,11 +534,16 @@ export declare function withWorkflowDirLock<T>(workflowDir: string, fn: () => T,
|
|
|
378
534
|
* only after the ledger append SUCCEEDED — a failed record never pairs to a
|
|
379
535
|
* phantom dispatch. An exec-less record (host-hook path) has no callId → no
|
|
380
536
|
* pairing. The pairing sub-path has its OWN catch scope: a `Map.set` throw must not log "record
|
|
381
|
-
* failed" after the dispatch was already appended.
|
|
537
|
+
* failed" after the dispatch was already appended. On that same successful
|
|
538
|
+
* pairing the dispatch's call-window catalog candidate is RESERVED (the
|
|
539
|
+
* first RAW-label slot under the live parent Session, when the exec carries
|
|
540
|
+
* one) — the child-identity join's step 1; admission is all-or-nothing and
|
|
541
|
+
* never affects the dispatch, the pairing, or a later settle.
|
|
382
542
|
* @param input - the harness dir; the dispatching exec's agent id; the
|
|
383
543
|
* Assignment text; the gate's violations; the hard-enforcement resolution;
|
|
384
544
|
* the apply-scoped pairing store (the adapter passes its own; direct callers
|
|
385
|
-
* may omit it)
|
|
545
|
+
* may omit it); the carrying session's `hint` (the adapter derives it — this
|
|
546
|
+
* module never reads the picker store).
|
|
386
547
|
*/
|
|
387
548
|
export declare function recordDispatch(input: {
|
|
388
549
|
harnessDir: string;
|
|
@@ -391,6 +552,8 @@ export declare function recordDispatch(input: {
|
|
|
391
552
|
violations: readonly unknown[];
|
|
392
553
|
hard: boolean;
|
|
393
554
|
pairing?: AgentFlowPairing;
|
|
555
|
+
/** The carrying session's selection hint — decides WHICH active lifecycle the row lands in. */
|
|
556
|
+
hint?: SessionHint;
|
|
394
557
|
}): void;
|
|
395
558
|
/**
|
|
396
559
|
* Record one settle event (spec §2.1.3). Fully try/catch-contained; a failing
|
|
@@ -405,7 +568,15 @@ export declare function recordDispatch(input: {
|
|
|
405
568
|
* outcome + optional duration + the PAIRED dispatch's identity
|
|
406
569
|
* (`role`/`planId`/`taskId` — same field names + semantics as the dispatch
|
|
407
570
|
* event; written for every paired settle, so the client can exactly pair
|
|
408
|
-
* the settle back to its dispatch
|
|
571
|
+
* the settle back to its dispatch — `role` is canonicalized by
|
|
572
|
+
* `normalizeRoleId`, so a direct caller's raw `@role` lands in the same
|
|
573
|
+
* grammar as the dispatch row) + the OPTIONAL child identity
|
|
574
|
+
* (`childId` — the settled child session; `taskRef` — a background settle's
|
|
575
|
+
* registry job id). A missing, empty, or oversized optional id is OMITTED
|
|
576
|
+
* from the row (never truncated, never re-keyed) and the completion still
|
|
577
|
+
* records. The carrying session's `hint` is the no-dir fallback (a PAIRED
|
|
578
|
+
* settle keeps its pinned `workflowDir` — a later pick must never split a
|
|
579
|
+
* dispatch from its settle).
|
|
409
580
|
*/
|
|
410
581
|
export declare function recordSettle(input: {
|
|
411
582
|
harnessDir: string;
|
|
@@ -416,6 +587,10 @@ export declare function recordSettle(input: {
|
|
|
416
587
|
role?: string;
|
|
417
588
|
planId?: string;
|
|
418
589
|
taskId?: string;
|
|
590
|
+
/** The carrying session's selection hint — consulted ONLY when no `workflowDir` is pinned. */
|
|
591
|
+
hint?: SessionHint;
|
|
592
|
+
childId?: string;
|
|
593
|
+
taskRef?: string;
|
|
419
594
|
}): void;
|
|
420
595
|
/**
|
|
421
596
|
* Record one workflow ledger event (the JSONL schema below
|
|
@@ -436,10 +611,11 @@ export declare function recordSettle(input: {
|
|
|
436
611
|
* `harnessDir`. No active lifecycle → `false` (skipped with a one-time warn
|
|
437
612
|
* — never a root v1 write, never a terminal snapshot write).
|
|
438
613
|
* @param input - harness dir + optional pre-resolved active workflow dir +
|
|
439
|
-
* the fully-shaped v1 workflow event
|
|
614
|
+
* the fully-shaped v1 workflow event + the carrying session's `hint` for the
|
|
615
|
+
* no-dir fallback (a pinned dir always wins).
|
|
440
616
|
* @returns `true` when the row was appended (the caller may durably advance
|
|
441
617
|
* its watermark); `false` on a contained append failure OR when no active
|
|
442
|
-
* lifecycle
|
|
618
|
+
* lifecycle is bound — the caller must leave the cursor behind so the row
|
|
443
619
|
* is re-attempted at the next scan (advance-then-record made a
|
|
444
620
|
* failed append permanent loss).
|
|
445
621
|
*/
|
|
@@ -447,6 +623,8 @@ export declare function recordWorkflowEvent(input: {
|
|
|
447
623
|
harnessDir: string;
|
|
448
624
|
workflowDir?: string;
|
|
449
625
|
event: AgentFlowWorkflowEvent;
|
|
626
|
+
/** The carrying session's selection hint — consulted ONLY when no `workflowDir` is pinned. */
|
|
627
|
+
hint?: SessionHint;
|
|
450
628
|
}): boolean;
|
|
451
629
|
/**
|
|
452
630
|
* Input for {@link recordWorkflowVerdict} — one gated workflow/ralph call's
|
|
@@ -470,6 +648,13 @@ export interface WorkflowVerdictInput {
|
|
|
470
648
|
verdict: WorkflowVerdict;
|
|
471
649
|
/** The policy violation code (advisory/ask/denied rows only). */
|
|
472
650
|
code?: string;
|
|
651
|
+
/**
|
|
652
|
+
* The carrying session's selection hint (the adapter derives it from the
|
|
653
|
+
* same `exec` it passes here) — decides WHICH active lifecycle the verdict
|
|
654
|
+
* row lands in. Absent (exec-less / unreadable binding store) → the row is
|
|
655
|
+
* skipped rather than attributed to an arbitrary lifecycle.
|
|
656
|
+
*/
|
|
657
|
+
hint?: SessionHint;
|
|
473
658
|
}
|
|
474
659
|
/**
|
|
475
660
|
* Record one workflow/ralph gate verdict row . Fully
|
|
@@ -532,18 +717,37 @@ export declare function readAgentFlow(workflowDir: string, limit?: number): Agen
|
|
|
532
717
|
* - `result.isError === true` OR an `error` payload present → settle `error`
|
|
533
718
|
* immediately (fabrication guard — the dispatch
|
|
534
719
|
* call failed; a result carrying `error` without `isError` never settles ok);
|
|
535
|
-
* - successful `result.value` shape `{ kind: 'background',
|
|
536
|
-
* valid
|
|
537
|
-
* `
|
|
538
|
-
*
|
|
720
|
+
* - successful `result.value` shape `{ kind: 'background', jobId }` with a
|
|
721
|
+
* valid jobId (the registry id, `<kind>-N` — never a child session id) →
|
|
722
|
+
* store `jobId → dispatchRef` and the bounded job id as the ref's `taskRef`
|
|
723
|
+
* (so the eventual `jobs.onJobDone` settle carries it); `{ kind: 'background' }`
|
|
724
|
+
* WITHOUT a valid jobId → nothing mappable (no settle);
|
|
539
725
|
* - `{ kind: 'continuable', subagentId }` → no terminal signal this round →
|
|
540
|
-
* no settle (documented limit — the child owns its turns)
|
|
726
|
+
* no settle (documented limit — the child owns its turns), and NOTHING is
|
|
727
|
+
* copied from its value onto any row;
|
|
541
728
|
* - any other successful value (foreground `{ kind: 'foreground', … }`
|
|
542
729
|
* included) → settle `ok` (the call completed synchronously).
|
|
730
|
+
* Child identity: a returned foreground `runId` becomes the settle's
|
|
731
|
+
* `childId`, extracted from the value INDEPENDENTLY of the outcome branch —
|
|
732
|
+
* a failed call whose value still carries a foreground `runId` keeps that
|
|
733
|
+
* identity on its `error` settle (identity never turns an error into `ok`).
|
|
543
734
|
* The consumed `dispatchByCallId` entry is DELETED after the branch resolves
|
|
544
735
|
* the call (map pruning — each callId
|
|
545
736
|
* pairs exactly once; the map holds only in-flight calls).
|
|
546
737
|
*
|
|
738
|
+
* Catalog candidate (steps 2–3 of the child-identity join): the SAME branch
|
|
739
|
+
* decides the dispatch's reserved call-window catalog candidate — a valid
|
|
740
|
+
* background result makes it eligible expecting the job's `one-shot` catalog
|
|
741
|
+
* (after the job pairing + `taskRef` are in place), a valid continuable
|
|
742
|
+
* result makes it eligible for exactly the returned `subagentId`, and every
|
|
743
|
+
* other outcome (other success / foreground, tool error, missing result
|
|
744
|
+
* payload, malformed result, invalid background job id) retires it to its
|
|
745
|
+
* tombstone. Eligibility then runs the catch-up walk over
|
|
746
|
+
* `[fromSeq, session.seq)` through the same matcher the live observer uses,
|
|
747
|
+
* so a catalog appended before this seam ran is still recovered. Both
|
|
748
|
+
* candidate helpers are self-contained: a throwing join degrades to a log
|
|
749
|
+
* line and NEVER costs the settle/awarded identity its row.
|
|
750
|
+
*
|
|
547
751
|
* The waterfall MUST be delegated via `next()` on every path — returning
|
|
548
752
|
* without calling `next` bails the chain and breaks every tool call. A
|
|
549
753
|
* throwing record never propagates.
|
|
@@ -554,21 +758,24 @@ export declare function readAgentFlow(workflowDir: string, limit?: number): Agen
|
|
|
554
758
|
* @param ctx - registrant context (fiber disposal unwinds the listener).
|
|
555
759
|
* @param config - the plugin Config (dispatch-tool matching).
|
|
556
760
|
* @param pairing - the apply-scoped pairing store (dispatchByCallId read,
|
|
557
|
-
*
|
|
761
|
+
* dispatchByJobId written by the background branch, catalogBySession
|
|
762
|
+
* advanced/retired here).
|
|
558
763
|
*/
|
|
559
764
|
export declare function registerSettleListener(ctx: Context, config: Config, pairing: AgentFlowPairing): void;
|
|
560
765
|
/**
|
|
561
766
|
* The structural read of the dsh-jobs terminal snapshot the pairing consumes
|
|
562
|
-
* (the settle-pairing upgrade). The `ctx.jobs
|
|
563
|
-
* contract was verified against the upstream
|
|
564
|
-
* `types.ts`: `JobDoneListener = (snapshot, owner) =>
|
|
565
|
-
* `snapshot.status` ∈ `completed | killed | failed`,
|
|
566
|
-
* are epoch ms (`finishedAt` absent while running).
|
|
767
|
+
* (the settle-pairing upgrade). The `ctx.inject(['jobs'])` →
|
|
768
|
+
* `jobs.onJobDone` contract was verified against the upstream
|
|
769
|
+
* `@deepseek-ai/dsh-jobs` `types.ts`: `JobDoneListener = (snapshot, owner) =>
|
|
770
|
+
* …`, terminal `snapshot.status` ∈ `completed | killed | failed`,
|
|
771
|
+
* `startedAt`/`finishedAt` are epoch ms (`finishedAt` absent while running).
|
|
772
|
+
* `id` stays the registry-issued JOB id (`<kind>-N`, e.g. `subagent-1`) — it
|
|
773
|
+
* is a jobs-registry key, NOT a child session id. Structural (no runtime or
|
|
567
774
|
* type import of the optional dsh-jobs seam — the plugin treats it as an
|
|
568
775
|
* optional service, wired via `ctx.inject(['jobs'])`).
|
|
569
776
|
*/
|
|
570
|
-
export interface
|
|
571
|
-
/** The registry-issued
|
|
777
|
+
export interface JobDoneSnapshot {
|
|
778
|
+
/** The registry-issued job id (`<kind>-N`, e.g. `subagent-1`) — never a child session id. */
|
|
572
779
|
id: string;
|
|
573
780
|
/** Terminal lifecycle status: `completed | killed | failed`. */
|
|
574
781
|
status: string;
|
|
@@ -578,20 +785,65 @@ export interface TaskDoneSnapshot {
|
|
|
578
785
|
finishedAt?: number;
|
|
579
786
|
}
|
|
580
787
|
/**
|
|
581
|
-
* Record the settle for one background-
|
|
582
|
-
* the `ctx.jobs.onJobDone` path):
|
|
583
|
-
* the snapshot's
|
|
584
|
-
* (populated by the post-execute background branch) — a
|
|
585
|
-
* (honest degrade, never fabricated). Outcome mapping:
|
|
586
|
-
* `killed → denied` / `failed → error`;
|
|
587
|
-
* when both are present. After a
|
|
588
|
-
* `
|
|
589
|
-
* in-flight
|
|
788
|
+
* Record the settle for one background-job terminal (plan
|
|
789
|
+
* the `ctx.inject(['jobs'])` → `jobs.onJobDone` path):
|
|
790
|
+
* the snapshot's registry job id must hit the pairing store's
|
|
791
|
+
* `dispatchByJobId` (populated by the post-execute background branch) — a
|
|
792
|
+
* miss records NOTHING (honest degrade, never fabricated). Outcome mapping:
|
|
793
|
+
* `completed → ok` / `killed → denied` / `failed → error`;
|
|
794
|
+
* `durationMs = finishedAt − startedAt` when both are present. After a
|
|
795
|
+
* SUCCESSFUL settle the consumed `dispatchByJobId` entry is deleted (map
|
|
796
|
+
* pruning — the map holds only in-flight jobs; a contract-violating
|
|
590
797
|
* non-terminal snapshot records nothing and KEEPS the entry so a later real
|
|
591
|
-
* terminal can still settle). Fully contained — never throws into the
|
|
798
|
+
* terminal can still settle). Fully contained — never throws into the job
|
|
592
799
|
* registry's listener notification.
|
|
593
|
-
* @param snapshot - the terminal
|
|
800
|
+
* @param snapshot - the terminal job snapshot (structural read).
|
|
594
801
|
* @param pairing - the apply-scoped pairing store.
|
|
595
802
|
*/
|
|
596
|
-
export declare function
|
|
803
|
+
export declare function recordJobSettle(snapshot: JobDoneSnapshot, pairing: AgentFlowPairing): void;
|
|
804
|
+
/**
|
|
805
|
+
* Record one NONTERMINAL `subagent-link` row — the call-window join's output:
|
|
806
|
+
* the child session a dispatch actually started, correlated back to the
|
|
807
|
+
* dispatch identity mstar recorded. This is an IDENTITY record, not a
|
|
808
|
+
* completion: the row carries NO `outcome`, NO `verdict` and NO `paired`
|
|
809
|
+
* marker (the child is still running this round), and `ts` is the link
|
|
810
|
+
* OBSERVATION time — never a fabricated child-creation time
|
|
811
|
+
* (`childCreatedAt` is validated upstream, it is not another ledger clock).
|
|
812
|
+
* The row is written ONLY from a candidate that actually matched, and only
|
|
813
|
+
* from the ref of a dispatch that was really recorded: a link is never a
|
|
814
|
+
* guess or a synthesis.
|
|
815
|
+
*
|
|
816
|
+
* Attribution follows the dispatch, not the clock: the row lands in
|
|
817
|
+
* `ref.workflowDir` (the dir the dispatch was appended to), never in today's
|
|
818
|
+
* active workflow dir, and the ref's `harnessDir` is the invalidation key.
|
|
819
|
+
* Fully try/catch-contained — a failing link write logs only and never
|
|
820
|
+
* escapes into the session append (or the job notification) that triggered
|
|
821
|
+
* it.
|
|
822
|
+
* @param input - the retained dispatch ref, the observed catalog `childId`,
|
|
823
|
+
* and the RAW delegation label the join matched on.
|
|
824
|
+
*/
|
|
825
|
+
export declare function recordSubagentLink(input: {
|
|
826
|
+
ref: AgentFlowDispatchRef;
|
|
827
|
+
childId: string;
|
|
828
|
+
label: string;
|
|
829
|
+
}): void;
|
|
830
|
+
/**
|
|
831
|
+
* The LIVE half of the call-window join (step 4): ONE root-context
|
|
832
|
+
* `session/event` observer, registered by the entry at apply — before
|
|
833
|
+
* execution can start — that reads only an ELIGIBLE slot of the EXACT
|
|
834
|
+
* carrying Session. The root (untagged) registration is admitted globally by
|
|
835
|
+
* the session store's scope carrier, so a catalog appended by any session
|
|
836
|
+
* reaches it; slots for every other session are simply absent from the
|
|
837
|
+
* WeakMap, and an inert (pending) or tombstoned slot is skipped. Its window
|
|
838
|
+
* bound is the live `session.seq`, not the catch-up endpoint, so a catalog
|
|
839
|
+
* appended after eligibility is still joined.
|
|
840
|
+
*
|
|
841
|
+
* No cold scan and no `session/created` backfill: a catalog written before
|
|
842
|
+
* apply has no surviving dispatch ref, so it can never label a future
|
|
843
|
+
* same-label dispatch. Contained — a throwing observation never escapes the
|
|
844
|
+
* session append.
|
|
845
|
+
* @param ctx - registrant context (fiber disposal unwinds the observer).
|
|
846
|
+
* @param pairing - the apply-scoped pairing store (the slots).
|
|
847
|
+
*/
|
|
848
|
+
export declare function registerSubagentCatalogListener(ctx: Context, pairing: AgentFlowPairing): void;
|
|
597
849
|
export {};
|