@nanmicoder/dsh-agent-teams 0.1.22 → 0.1.24-rc.1
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.md +35 -13
- package/README_ZH.md +35 -13
- package/compatibility.json +6 -2
- package/lib/artifacts.js +342 -0
- package/lib/capabilities.js +6 -4
- package/lib/client/ActivityPanel.js +5 -2
- package/lib/client/agent-teams-card-definition.js +100 -6
- package/lib/client.js +90 -7
- package/lib/client.js.map +1 -1
- package/lib/command.js +3 -2
- package/lib/git-artifact-stamp.json +2 -2
- package/lib/harness-compat.js +139 -24
- package/lib/index.js +26 -3
- package/lib/lineage.js +198 -0
- package/lib/mailbox.js +59 -34
- package/lib/members.js +355 -82
- package/lib/profiles.js +3 -2
- package/lib/quality-gates.js +850 -65
- package/lib/scheduler.js +22 -3
- package/lib/snapshot.js +22 -1
- package/lib/state.js +282 -13
- package/lib/tool-policies.js +187 -0
- package/lib/tools.js +382 -109
- package/lib/types/artifacts.d.ts +66 -0
- package/lib/types/client/activity-monitor.d.ts +6 -0
- package/lib/types/client/agent-teams-card-definition.d.ts +56 -0
- package/lib/types/harness-compat.d.ts +78 -15
- package/lib/types/index.d.ts +26 -1
- package/lib/types/lineage.d.ts +92 -0
- package/lib/types/members.d.ts +113 -2
- package/lib/types/profiles.d.ts +6 -0
- package/lib/types/quality-gates.d.ts +216 -10
- package/lib/types/scheduler.d.ts +2 -1
- package/lib/types/snapshot.d.ts +20 -0
- package/lib/types/state.d.ts +22 -5
- package/lib/types/tool-policies.d.ts +119 -0
- package/lib/types/tools.d.ts +12 -1
- package/lib/types/types.d.ts +100 -0
- package/lib/types/workspace-audit.d.ts +27 -0
- package/lib/workspace-audit.js +52 -0
- package/package.json +61 -54
- package/pnpm-workspace.yaml +304 -273
- package/release-notes/v0.1.23.md +34 -0
- package/release-notes/v0.1.24-rc.1.md +13 -0
- package/scripts/legacy-session-source-repair.mjs +344 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* coverage, and resume. Tools and persistence call these; they do not I/O.
|
|
4
4
|
* @module dsh-agent-teams/quality-gates
|
|
5
5
|
*/
|
|
6
|
-
import { type AcceptanceResult, type CommandResult, type FindingSeverity, type ReviewFinding, type ReviewPolicy, type ReviewVerdict, type TaskKind, type TaskEvidence, type TaskRevision, type TaskStatus, type TeamState, type TeamTask } from './types.ts';
|
|
6
|
+
import { type AcceptanceResult, type ArtifactDigest, type ArtifactScopeGap, type ArtifactSnapshot, type CommandResult, type FindingSeverity, type ReviewFinding, type ReviewPolicy, type ReviewVerdict, type TaskKind, type TaskEvidence, type TaskRevision, type TaskStatus, type TeamState, type TeamTask } from './types.ts';
|
|
7
7
|
declare const QUALITY_KINDS: readonly TaskKind[];
|
|
8
8
|
declare const WRITE_KINDS: readonly TaskKind[];
|
|
9
9
|
declare const DEFAULT_REVIEW_POLICY: Required<Pick<ReviewPolicy, 'requirementsMinRounds' | 'requirementsMaxRounds' | 'codeMaxRounds' | 'maxRepairAttempts'>>;
|
|
@@ -54,6 +54,7 @@ export interface PlannedFollowUpTask {
|
|
|
54
54
|
id?: string;
|
|
55
55
|
kind: TaskKind;
|
|
56
56
|
subject?: string;
|
|
57
|
+
description?: string;
|
|
57
58
|
assignee?: string;
|
|
58
59
|
dependencies?: string[];
|
|
59
60
|
round?: number;
|
|
@@ -64,13 +65,23 @@ export interface PlannedFollowUpTask {
|
|
|
64
65
|
verify?: string[];
|
|
65
66
|
sourceTaskId?: string;
|
|
66
67
|
sourceFindingIds?: string[];
|
|
68
|
+
findingSourceTaskIds?: string[];
|
|
67
69
|
reviewedTaskId?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Paths whose current bytes the generated contract was derived from. The
|
|
72
|
+
* caller snapshots them so the work order can later be marked stale.
|
|
73
|
+
*/
|
|
74
|
+
contractPaths?: string[];
|
|
68
75
|
}
|
|
69
76
|
export interface PlanQualityFollowUpResult {
|
|
70
77
|
created: PlannedFollowUpTask[];
|
|
71
78
|
tasks: PlannedFollowUpTask[];
|
|
72
79
|
escalated?: boolean;
|
|
73
80
|
status?: 'escalated';
|
|
81
|
+
/** Why the automatic loop stopped; surfaced to the captain. */
|
|
82
|
+
reason?: string;
|
|
83
|
+
/** Findings that were refused as contract material (default-excluded paths). */
|
|
84
|
+
excludedFindings?: string[];
|
|
74
85
|
}
|
|
75
86
|
export interface CoverageRow {
|
|
76
87
|
goal_item: string;
|
|
@@ -81,6 +92,38 @@ export interface CoverageRow {
|
|
|
81
92
|
export interface DeliveryResult {
|
|
82
93
|
ok: boolean;
|
|
83
94
|
blockers: string[];
|
|
95
|
+
/**
|
|
96
|
+
* Non-blocking observations: gates that legitimately drifted because a later
|
|
97
|
+
* revision superseded them, and generated work orders whose cited files
|
|
98
|
+
* moved on. Delivery is still allowed, but never silently.
|
|
99
|
+
*/
|
|
100
|
+
warnings: string[];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Current workspace bytes handed to the delivery check. The rules stay pure:
|
|
104
|
+
* the caller (which owns the workspace path) reads the digests and passes them
|
|
105
|
+
* in. Omit it when no workspace is available — the attempt-level staleness
|
|
106
|
+
* checks still run, and content checks simply cannot be made.
|
|
107
|
+
*/
|
|
108
|
+
export interface ScopeEntry {
|
|
109
|
+
path: string;
|
|
110
|
+
present: boolean;
|
|
111
|
+
}
|
|
112
|
+
export interface DeliveryAudit {
|
|
113
|
+
/** Current bytes of the paths recorded by verdicts and work orders. */
|
|
114
|
+
artifacts?: readonly ArtifactDigest[];
|
|
115
|
+
/**
|
|
116
|
+
* Current contents of the scope patterns recorded verdicts bound. A path that
|
|
117
|
+
* appears here but not in the verdict's recorded artifacts was never
|
|
118
|
+
* reviewed, so a broad scope cannot silently absorb it.
|
|
119
|
+
*/
|
|
120
|
+
expandedScope?: readonly ScopeEntry[];
|
|
121
|
+
/**
|
|
122
|
+
* Parts of the *current* read that could not be completed (expansion cap,
|
|
123
|
+
* unreadable directory or file, illegal pattern). A non-empty list means the
|
|
124
|
+
* audit itself is incomplete, so no verdict may be confirmed from it.
|
|
125
|
+
*/
|
|
126
|
+
gaps?: readonly ArtifactScopeGap[];
|
|
84
127
|
}
|
|
85
128
|
export interface ResumeTeamResult {
|
|
86
129
|
ok?: boolean;
|
|
@@ -95,6 +138,8 @@ export interface QualityLoopSnapshot {
|
|
|
95
138
|
escalated: boolean;
|
|
96
139
|
deliverable: boolean;
|
|
97
140
|
summary: string;
|
|
141
|
+
/** Non-blocking gate observations (stale verdicts and work orders). */
|
|
142
|
+
warnings: string[];
|
|
98
143
|
}
|
|
99
144
|
export interface QualityGraphDraft {
|
|
100
145
|
subject: string;
|
|
@@ -116,6 +161,13 @@ export declare function isReviewPolicy(value: unknown): value is ReviewPolicy;
|
|
|
116
161
|
/** Normalize a workspace-relative POSIX path. `undefined` means illegal. */
|
|
117
162
|
export declare function normalizeWorkspacePath(path: string): string | undefined;
|
|
118
163
|
export declare function pathMatchesScope(path: string, pattern: string): boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Paths no quality contract may claim: version control, harness/plugin state,
|
|
166
|
+
* secrets and private keys. Exported because the completion gate and contract
|
|
167
|
+
* generation (including the artifact reader that must not walk into
|
|
168
|
+
* `.agent-teams/`, `secrets/` or `.env*`) have to share exactly one predicate.
|
|
169
|
+
*/
|
|
170
|
+
export declare function isDefaultExcludedPath(path: string): boolean;
|
|
119
171
|
export declare function classifyChangedPath(path: string, inScope?: readonly string[], outOfScope?: readonly string[]): PathClassification;
|
|
120
172
|
export declare function collectChangedPaths(gitStatusText: string): string[];
|
|
121
173
|
export declare function inScopeOverlap(left: readonly string[] | undefined, right: readonly string[] | undefined): string[];
|
|
@@ -132,15 +184,36 @@ export declare function evaluateQualityCompletion(task: TeamTask, update: Qualit
|
|
|
132
184
|
* forbids, so no honest completion exists and the repair dead-locks.
|
|
133
185
|
*
|
|
134
186
|
* Absolute and otherwise illegal paths are dropped (they can never match
|
|
135
|
-
* workspace-relative scope patterns anyway)
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
187
|
+
* workspace-relative scope patterns anyway), and so are paths the completion
|
|
188
|
+
* gate refuses by default (`secrets/` segments, `.env*`, `.git`, `.dsh`,
|
|
189
|
+
* `id_rsa*` — issue #183). `classifyChangedPath` checks that default-exclusion
|
|
190
|
+
* set BEFORE inScope, so collecting such a path here would generate a contract
|
|
191
|
+
* that demands an edit the gate calls `out_of_scope`: no honest completion
|
|
192
|
+
* exists and the repair burns its rounds. Contract generation and the
|
|
193
|
+
* completion gate must agree; the exemption channel stays the user-configured
|
|
194
|
+
* outOfScope list, never a reviewer's finding.
|
|
139
195
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
196
|
+
* When nothing legal remains, the source task's own inScope is kept as the
|
|
197
|
+
* fallback. Over-inclusion is accepted otherwise: inScope is an audit upper
|
|
198
|
+
* bound, and the requiredFix text still tells the implementer what to touch.
|
|
199
|
+
* Keep every remaining derived path until the generator resolves inherited
|
|
200
|
+
* exclusions; filtering those first would silently discard a required fix
|
|
201
|
+
* target.
|
|
142
202
|
*/
|
|
143
203
|
export declare function repairScopeFromFindings(findings: readonly ReviewFinding[], fallback: string[] | undefined): string[] | undefined;
|
|
204
|
+
/**
|
|
205
|
+
* Every workspace-relative path one finding names, before any exclusion rule:
|
|
206
|
+
* the observed file plus the fix targets referenced by `requiredFix`.
|
|
207
|
+
*/
|
|
208
|
+
export declare function findingDerivedPaths(finding: ReviewFinding): string[];
|
|
209
|
+
/**
|
|
210
|
+
* Findings whose every cited path is default-excluded. They cannot become an
|
|
211
|
+
* honest repair contract (see {@link repairScopeFromFindings}), so they are
|
|
212
|
+
* surfaced to the captain instead of silently disappearing or dead-locking a
|
|
213
|
+
* member. A finding that names no path at all is NOT excluded: it simply falls
|
|
214
|
+
* back to the judged task's own scope, exactly as before.
|
|
215
|
+
*/
|
|
216
|
+
export declare function excludedFindingsOf(findings: readonly ReviewFinding[], pathExists?: (path: string) => boolean): ReviewFinding[];
|
|
144
217
|
/** Captain-only amendment payload: replacement values for contract fields. */
|
|
145
218
|
export interface ContractAmendmentInput {
|
|
146
219
|
objective?: string;
|
|
@@ -170,14 +243,136 @@ export interface AmendTaskContractResult {
|
|
|
170
243
|
* amended contract.
|
|
171
244
|
*/
|
|
172
245
|
export declare function amendTaskContract(team: TeamState, task: TeamTask, input: ContractAmendmentInput, by: string, reason: string): AmendTaskContractResult;
|
|
173
|
-
|
|
246
|
+
/** Remove drifting line anchors from generated contract text. */
|
|
247
|
+
export declare function stripLineAnchors(text: string): string;
|
|
248
|
+
export interface PlanQualityFollowUpOptions {
|
|
249
|
+
/**
|
|
250
|
+
* Whether a derived path exists in the workspace. Used only to classify a
|
|
251
|
+
* finding as excluded-only; without it every derived path counts.
|
|
252
|
+
*/
|
|
253
|
+
pathExists?: (path: string) => boolean;
|
|
254
|
+
}
|
|
255
|
+
export declare function planQualityFollowUp(team: TeamState, closed: TeamTask, options?: PlanQualityFollowUpOptions): PlanQualityFollowUpResult;
|
|
174
256
|
export declare function buildCoverageMatrix(goalItems: readonly string[], tasks: readonly TeamTask[]): CoverageRow[];
|
|
175
|
-
|
|
257
|
+
/** Kinds whose completion is a verdict about a revision. */
|
|
258
|
+
export declare function isVerdictKind(kind: TaskKind | undefined): boolean;
|
|
259
|
+
/**
|
|
260
|
+
* Compare recorded bytes against the bytes on disk now. Every returned entry
|
|
261
|
+
* is a reason a verdict or a work order no longer describes the workspace.
|
|
262
|
+
* "Cannot be read" is reported as its own reason: a permission error is not a
|
|
263
|
+
* match, and a partial read is not proof of an unchanged file.
|
|
264
|
+
*/
|
|
265
|
+
/** One reason an artifact set no longer describes the workspace. */
|
|
266
|
+
export interface ArtifactDriftDetail {
|
|
267
|
+
/** The implicated path, when the reason is about one concrete file. */
|
|
268
|
+
path?: string;
|
|
269
|
+
message: string;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Compare recorded bytes against the bytes on disk now, keeping the path each
|
|
273
|
+
* reason belongs to. "Cannot be read" is reported as its own reason: a
|
|
274
|
+
* permission error is not a match, and a partial read is not proof of an
|
|
275
|
+
* unchanged file.
|
|
276
|
+
*/
|
|
277
|
+
export declare function artifactDriftDetails(recorded: readonly ArtifactDigest[] | undefined, current: readonly ArtifactDigest[] | undefined): ArtifactDriftDetail[];
|
|
278
|
+
/** Message-only view of {@link artifactDriftDetails}. */
|
|
279
|
+
export declare function artifactDrift(recorded: readonly ArtifactDigest[] | undefined, current: readonly ArtifactDigest[] | undefined): string[];
|
|
280
|
+
/** A partially enumerated scope is incomplete evidence, never a silent pass. */
|
|
281
|
+
export declare function gapDrift(gaps: readonly ArtifactScopeGap[] | undefined): string[];
|
|
282
|
+
/**
|
|
283
|
+
* The task a verdict is about: an explicit `reviewedTaskId`/`sourceTaskId`
|
|
284
|
+
* first, otherwise the implementation/repair this gate depends on.
|
|
285
|
+
*/
|
|
286
|
+
export declare function reviewedTargetOf(team: TeamState, task: TeamTask): TeamTask | undefined;
|
|
287
|
+
/**
|
|
288
|
+
* Bind a closing verdict to the revision it judged: the target's execution
|
|
289
|
+
* generation plus the bytes of the paths it changed. Without this a `pass` is
|
|
290
|
+
* only a task record, and delivery cannot tell that the reviewed bytes were
|
|
291
|
+
* edited or deleted afterwards. `gaps` carries the parts of the reviewed scope
|
|
292
|
+
* that could not be enumerated, `scope` the patterns it bound, and
|
|
293
|
+
* `duringDrift` any change observed between claim and close — partial or
|
|
294
|
+
* moving coverage is never read as complete.
|
|
295
|
+
*/
|
|
296
|
+
export interface ReviewBinding {
|
|
297
|
+
artifacts: readonly ArtifactDigest[];
|
|
298
|
+
gaps?: readonly ArtifactScopeGap[];
|
|
299
|
+
scope?: readonly string[];
|
|
300
|
+
duringDrift?: readonly string[];
|
|
301
|
+
}
|
|
302
|
+
export declare function bindReviewToRevision(team: TeamState, task: TeamTask, binding: ReviewBinding): TeamTask;
|
|
303
|
+
/**
|
|
304
|
+
* Scope a verdict must bind: the paths the judged task changed, otherwise the
|
|
305
|
+
* scope patterns of its contract (expanded by the caller).
|
|
306
|
+
*/
|
|
307
|
+
export declare function verdictScopePatterns(team: TeamState, task: TeamTask): string[];
|
|
308
|
+
/** Scope patterns recorded verdicts bound, for new-file detection. */
|
|
309
|
+
export declare function deliveryScopePatterns(team: TeamState): string[];
|
|
310
|
+
/** Paths whose current bytes decide whether recorded verdicts and orders hold. */
|
|
311
|
+
export declare function deliveryArtifactPaths(team: TeamState): string[];
|
|
312
|
+
/**
|
|
313
|
+
* Every completed verdict that claims to cover the current revision but no
|
|
314
|
+
* longer does.
|
|
315
|
+
*
|
|
316
|
+
* A drifted review/requirements pass is the delivery evidence itself, so it
|
|
317
|
+
* blocks — unless a later sound verdict covers the same lineage, which is the
|
|
318
|
+
* normal shape of `pass → repair → new pass` and must converge instead of
|
|
319
|
+
* blocking forever behind an old snapshot. A drifted `verification` is only a
|
|
320
|
+
* warning: a later repair legitimately supersedes it, but never silently.
|
|
321
|
+
* A verdict that predates content binding is reported as an unauditable gap
|
|
322
|
+
* rather than being read as a pass over the current bytes.
|
|
323
|
+
*/
|
|
324
|
+
/** One audited verdict observation, attributed to the task that carries it. */
|
|
325
|
+
export interface VerdictAuditNote {
|
|
326
|
+
taskId: string;
|
|
327
|
+
/**
|
|
328
|
+
* `blocker` when this pass is the delivery evidence itself, `warning` when a
|
|
329
|
+
* later sound verdict supersedes it or when it is an intermediate gate.
|
|
330
|
+
*/
|
|
331
|
+
level: 'blocker' | 'warning';
|
|
332
|
+
message: string;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Audit every completed verdict against the current revision (issue #161).
|
|
336
|
+
*
|
|
337
|
+
* A drifted review/requirements pass is the delivery evidence itself, so it
|
|
338
|
+
* blocks — unless a later sound verdict covers the same lineage, which is the
|
|
339
|
+
* normal shape of `pass → repair → new pass` and must converge instead of
|
|
340
|
+
* blocking forever behind an old snapshot. A drifted `verification` or a
|
|
341
|
+
* superseded pass is reported as a warning: a later repair legitimately
|
|
342
|
+
* supersedes it, but never silently. A verdict that predates content binding
|
|
343
|
+
* is reported as an unauditable gap rather than being read as a pass over the
|
|
344
|
+
* current bytes.
|
|
345
|
+
*/
|
|
346
|
+
export declare function verdictAuditNotes(team: TeamState, audit?: DeliveryAudit): VerdictAuditNote[];
|
|
347
|
+
/** Staleness of one auto-generated work order against the current workspace. */
|
|
348
|
+
export declare function contractStaleness(task: TeamTask, current: readonly ArtifactDigest[] | undefined): string[];
|
|
349
|
+
export interface WorkOrderWarning {
|
|
350
|
+
taskId: string;
|
|
351
|
+
message: string;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Open auto-generated work orders whose cited bytes moved on (issue #174).
|
|
355
|
+
* A repair round's acceptance/inScope is a snapshot of the workspace at
|
|
356
|
+
* finding-generation time; every later round invalidates it, and nothing used
|
|
357
|
+
* to say so — a member following it literally edits the wrong line or redoes
|
|
358
|
+
* work that is already done.
|
|
359
|
+
*/
|
|
360
|
+
export declare function workOrderWarnings(team: TeamState, current: readonly ArtifactDigest[] | undefined): WorkOrderWarning[];
|
|
361
|
+
export declare function canDeclareDelivery(team: TeamState, audit?: DeliveryAudit): DeliveryResult;
|
|
176
362
|
export declare function resumeTeamState(team: TeamState, reason: string): ResumeTeamResult;
|
|
363
|
+
/**
|
|
364
|
+
* The durable `findings[].line` contract, shared by the write-side parser
|
|
365
|
+
* (`parseFindings`) and the durable validator (`isReviewFinding`) so the two
|
|
366
|
+
* cannot drift apart again (issue #188, #105 class).
|
|
367
|
+
*/
|
|
368
|
+
export declare function isValidFindingLine(value: unknown): value is number;
|
|
177
369
|
export declare function isReviewFinding(value: unknown): value is ReviewFinding;
|
|
178
370
|
export declare function isAcceptanceResult(value: unknown): value is AcceptanceResult;
|
|
179
371
|
export declare function isCommandResult(value: unknown): value is CommandResult;
|
|
180
372
|
export declare function isTaskRevision(value: unknown): value is TaskRevision;
|
|
373
|
+
export declare function isArtifactDigest(value: unknown): value is ArtifactDigest;
|
|
374
|
+
export declare function isArtifactScopeGap(value: unknown): value is ArtifactScopeGap;
|
|
375
|
+
export declare function isArtifactSnapshot(value: unknown): value is ArtifactSnapshot;
|
|
181
376
|
/**
|
|
182
377
|
* Normalize blank optional task fields to omitted ("blank means absent").
|
|
183
378
|
* Blank string scalars are deleted; string lists have blank entries filtered
|
|
@@ -186,6 +381,17 @@ export declare function isTaskRevision(value: unknown): value is TaskRevision;
|
|
|
186
381
|
* validation stays strict.
|
|
187
382
|
*/
|
|
188
383
|
export declare function normalizeBlankOptionalTaskFields<T extends object>(task: T): T;
|
|
384
|
+
/**
|
|
385
|
+
* Treat an out-of-contract **numeric** sentinel as absent, including
|
|
386
|
+
* `findings[].line` (contract: `isValidFindingLine`).
|
|
387
|
+
*
|
|
388
|
+
* Only numbers are healed. A value of any other type (`round: "oops"`,
|
|
389
|
+
* `attempt: {}`, `line: "12"`) is left exactly as written and stays a hard,
|
|
390
|
+
* named validation failure, so genuine corruption is never silently swallowed
|
|
391
|
+
* while `-1`-style sentinels — the #188 brick — self-heal. Field order, unknown
|
|
392
|
+
* fields and every in-range value are preserved.
|
|
393
|
+
*/
|
|
394
|
+
export declare function normalizeSentinelNumericTaskFields<T extends object>(task: T): T;
|
|
189
395
|
export declare function hasValidQualityTaskFields(value: Record<string, unknown>): boolean;
|
|
190
396
|
export declare function isTaskKind(value: unknown): value is TaskKind;
|
|
191
397
|
export declare function isReviewVerdict(value: unknown): value is ReviewVerdict;
|
|
@@ -202,7 +408,7 @@ export declare function defaultQualityDeliveryGraph(input: {
|
|
|
202
408
|
integrator?: string;
|
|
203
409
|
}): QualityGraphDraft[];
|
|
204
410
|
export declare function qualityPlanningPrompt(): string;
|
|
205
|
-
export declare function describeQualityLoop(team: TeamState): QualityLoopSnapshot;
|
|
411
|
+
export declare function describeQualityLoop(team: TeamState, audit?: DeliveryAudit): QualityLoopSnapshot;
|
|
206
412
|
export { QUALITY_KINDS, WRITE_KINDS };
|
|
207
413
|
/** Persisted evidence must remain loadable after process restart. */
|
|
208
414
|
export declare function isTaskEvidence(value: unknown): value is TaskEvidence;
|
package/lib/types/scheduler.d.ts
CHANGED
|
@@ -65,6 +65,7 @@ export interface DispatchTicket {
|
|
|
65
65
|
readonly acceptance?: readonly string[];
|
|
66
66
|
readonly verify?: readonly string[];
|
|
67
67
|
readonly reviewedTaskId?: string;
|
|
68
|
+
readonly findingSourceTaskIds?: readonly string[];
|
|
68
69
|
}
|
|
69
70
|
/**
|
|
70
71
|
* Recursively collect `status=completed` ancestors of `taskId` in topological
|
|
@@ -73,6 +74,6 @@ export interface DispatchTicket {
|
|
|
73
74
|
export declare function collectCompletedDependencyOutputs(tasks: readonly TeamTask[], taskId: string, warn?: (message: string) => void): DependencyOutput[];
|
|
74
75
|
/** Format completed-dependency outputs with per-item and total truncation. */
|
|
75
76
|
export declare function formatDependencyOutputs(items: readonly DependencyOutput[]): string;
|
|
76
|
-
export declare function assignmentPrompt(ticket: DispatchTicket, stateDir: string, teamId: string): string;
|
|
77
|
+
export declare function assignmentPrompt(ticket: DispatchTicket, stateDir: string, teamId: string, workOrderWarnings?: readonly string[]): string;
|
|
77
78
|
/** Install one scheduler and its member activity observer. */
|
|
78
79
|
export declare function installTeamScheduler(ctx: Context, config: SchedulerConfig): TeamScheduler;
|
package/lib/types/snapshot.d.ts
CHANGED
|
@@ -42,6 +42,11 @@ export interface TeamActivityTask {
|
|
|
42
42
|
readonly kind?: string;
|
|
43
43
|
readonly round?: number;
|
|
44
44
|
readonly verdict?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Set when this task's verdict or auto-generated work order no longer
|
|
47
|
+
* describes the current bytes; the panel must not render a bare pass badge.
|
|
48
|
+
*/
|
|
49
|
+
readonly verdictStale?: string;
|
|
45
50
|
/** Durable last-write stamp; drives the finished-member ordering (issue #192). */
|
|
46
51
|
readonly updatedAt: number;
|
|
47
52
|
}
|
|
@@ -64,6 +69,14 @@ export interface TeamActivitySnapshot {
|
|
|
64
69
|
readonly tasks: readonly TeamActivityTask[];
|
|
65
70
|
readonly messageCount: number;
|
|
66
71
|
readonly captainInbox: readonly TeamActivityMessage[];
|
|
72
|
+
/** Present when the snapshot could audit the workspace bytes. */
|
|
73
|
+
readonly loopState?: string;
|
|
74
|
+
readonly deliverable?: boolean;
|
|
75
|
+
readonly delivery?: {
|
|
76
|
+
readonly ok: boolean;
|
|
77
|
+
readonly blockers: readonly string[];
|
|
78
|
+
readonly warnings: readonly string[];
|
|
79
|
+
};
|
|
67
80
|
}
|
|
68
81
|
/** Snapshot projection switches for live and archived teams. */
|
|
69
82
|
export interface TeamSnapshotOptions {
|
|
@@ -71,6 +84,12 @@ export interface TeamSnapshotOptions {
|
|
|
71
84
|
readonly includeRemoved?: boolean;
|
|
72
85
|
/** Archived teams have no meaningful live activity after their sessions stop. */
|
|
73
86
|
readonly historic?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Absolute workspace path of the owning workspace. When present, the snapshot
|
|
89
|
+
* audits the current bytes behind every recorded verdict, so the panel and
|
|
90
|
+
* the model-facing status tool cannot report different delivery verdicts.
|
|
91
|
+
*/
|
|
92
|
+
readonly workspacePath?: string;
|
|
74
93
|
}
|
|
75
94
|
/** Compact `provider/model` route for the activity panel, or just the model. */
|
|
76
95
|
export declare function memberModelRoute(member: {
|
|
@@ -95,6 +114,7 @@ export declare function assembleTeamSnapshot(ctx: Context, stateRoot: string, wo
|
|
|
95
114
|
export declare function collectTeamsActivity(ctx: Context, roots: readonly {
|
|
96
115
|
workspace: string;
|
|
97
116
|
stateRoot: string;
|
|
117
|
+
workspacePath?: string;
|
|
98
118
|
}[]): Promise<TeamActivitySnapshot[]>;
|
|
99
119
|
/**
|
|
100
120
|
* Collect every archived team under the given workspace state roots (the
|
package/lib/types/state.d.ts
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
* @module dsh-agent-teams/state
|
|
14
14
|
*/
|
|
15
15
|
import { type TaskStatus, type TeamMessage, type TeamState, type TeamTask } from './types.ts';
|
|
16
|
-
|
|
16
|
+
import { type LeadIdentity } from './lineage.ts';
|
|
17
|
+
export { amendTaskContract, buildCoverageMatrix, canDeclareDelivery, classifyChangedPath, collectChangedPaths, defaultQualityDeliveryGraph, describeQualityLoop, evaluateQualityCompletion, hasValidQualityTaskFields, isQualityKind, isTaskRevision, isValidFindingLine, normalizeBlankOptionalTaskFields, normalizeSentinelNumericTaskFields, pathMatchesScope, planQualityFollowUp, qualityPlanningPrompt, resumeTeamState, sanitizeReviewAcceptance, sanitizeReviewObjective, taskKindOf, validateCreateTask, } from './quality-gates.ts';
|
|
17
18
|
export type { ContractAmendmentInput } from './quality-gates.ts';
|
|
18
19
|
/** Mailbox key of the captain. */
|
|
19
20
|
export declare const CAPTAIN_KEY = "captain";
|
|
@@ -116,18 +117,22 @@ export declare function recordRetiredMemberIds(stateRoot: string, memberIds: rea
|
|
|
116
117
|
* Find the team owned by one captain session (at most one per captain).
|
|
117
118
|
* @param stateRoot - resolved absolute state root directory.
|
|
118
119
|
* @param captainSessionId - the owning session id.
|
|
120
|
+
* @param lead - durable lead identity of the calling session, when it may lead
|
|
121
|
+
* through a fork chain; omitted keeps the exact-id contract.
|
|
119
122
|
* @returns the team record, or undefined when the captain leads no team.
|
|
120
123
|
*/
|
|
121
|
-
export declare function findTeamByCaptain(stateRoot: string, captainSessionId: string): Promise<TeamState | undefined>;
|
|
124
|
+
export declare function findTeamByCaptain(stateRoot: string, captainSessionId: string, lead?: LeadIdentity): Promise<TeamState | undefined>;
|
|
122
125
|
/**
|
|
123
126
|
* Find the team in which one session is an active participant.
|
|
124
|
-
* Captains match `captainSessionId
|
|
125
|
-
*
|
|
127
|
+
* Captains match `captainSessionId` — including a lead session that inherited
|
|
128
|
+
* it through a fork — and members match their durable child session id.
|
|
129
|
+
* Removed members no longer have access to team-scoped tools.
|
|
126
130
|
* @param stateRoot - resolved absolute state root directory.
|
|
127
131
|
* @param agentSessionId - calling captain/member session id.
|
|
132
|
+
* @param lead - durable lead identity of the calling session, when available.
|
|
128
133
|
* @returns the team record, or undefined when the caller belongs to no team.
|
|
129
134
|
*/
|
|
130
|
-
export declare function findTeamByParticipant(stateRoot: string, agentSessionId: string): Promise<TeamState | undefined>;
|
|
135
|
+
export declare function findTeamByParticipant(stateRoot: string, agentSessionId: string, lead?: LeadIdentity): Promise<TeamState | undefined>;
|
|
131
136
|
/** Build a fresh message record. */
|
|
132
137
|
export declare function createMessage(from: string, to: string, content: string): TeamMessage;
|
|
133
138
|
/**
|
|
@@ -193,6 +198,18 @@ export interface AtomicReplaceOptions {
|
|
|
193
198
|
* @returns nothing once the file has been replaced by one of the two paths.
|
|
194
199
|
*/
|
|
195
200
|
export declare function replaceFileAtomicOrDirect(temporary: string, file: string, content: string, primitives: AtomicReplacePrimitives, options?: AtomicReplaceOptions): Promise<void>;
|
|
201
|
+
/**
|
|
202
|
+
* Name the first durable defect in a record `coerceTeamState` rejected. Before
|
|
203
|
+
* this, a rejected `team.json` surfaced only as
|
|
204
|
+
* `invalid AgentTeams state in team "x"` — no task, no field, no value — while
|
|
205
|
+
* every AgentTeams tool (read *and* write) failed at once, leaving an operator
|
|
206
|
+
* to bisect the file by hand (issue #188). The detail is computed only on the
|
|
207
|
+
* failure path, so the happy path keeps its existing cost.
|
|
208
|
+
* @param raw - the parsed record as read from disk, before coercion.
|
|
209
|
+
* @param expectedId - the team id the record was read for.
|
|
210
|
+
* @returns a `task "t2": findings[0].line -1 must be ...` style detail.
|
|
211
|
+
*/
|
|
212
|
+
export declare function describeTeamStateDefect(raw: unknown, expectedId: string): string;
|
|
196
213
|
export declare function isTeamTask(value: unknown): value is TeamTask;
|
|
197
214
|
/**
|
|
198
215
|
* Remove a team's whole directory (members should be interrupted first).
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner-declared, reusable per-member tool policies (#59).
|
|
3
|
+
*
|
|
4
|
+
* A deployment owner declares reusable policies under `toolPolicies`; a member
|
|
5
|
+
* *selects* one by id (`tool_policy` on `agent_teams_add_member`, an inline
|
|
6
|
+
* plan member, or a profile member row). The Captain model can therefore only
|
|
7
|
+
* choose an existing policy — it can never submit raw allow/deny lists or
|
|
8
|
+
* invent one at runtime.
|
|
9
|
+
*
|
|
10
|
+
* One pure resolver turns the selection into the immutable effective filter
|
|
11
|
+
* that is persisted on the member (`toolPolicy` + `toolFilter`) and handed to
|
|
12
|
+
* the existing `startContinuable({ toolFilter })` seam, which hides the named
|
|
13
|
+
* tools from the child's schemas and rejects their execution. This is tool
|
|
14
|
+
* visibility / invocation policy for global tools, not an OS sandbox.
|
|
15
|
+
*
|
|
16
|
+
* Invariants (issue #59):
|
|
17
|
+
* 1. the captain-only deny list always applies and cannot be undone by a
|
|
18
|
+
* policy — the host intersects `allow` and `deny`, so a denied name never
|
|
19
|
+
* becomes visible again;
|
|
20
|
+
* 2. the four member coordination tools stay available, and a policy may not
|
|
21
|
+
* deny them;
|
|
22
|
+
* 3. an allow-list policy explicitly includes those coordination tools;
|
|
23
|
+
* 4. the effective deny list is the union of the policy deny list and the
|
|
24
|
+
* captain-only deny list.
|
|
25
|
+
* @module dsh-agent-teams/tool-policies
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Deny names the lenient spawn retry may drop when the host reports them
|
|
29
|
+
* unknown: exactly the built-in captain-only and depth entries (#163/#164).
|
|
30
|
+
* Names an owner policy declares are never relaxed — a policy that the host
|
|
31
|
+
* cannot apply must fail loud instead of silently applying a different one.
|
|
32
|
+
*/
|
|
33
|
+
export declare const BUILTIN_DENY_TOOLS: readonly string[];
|
|
34
|
+
/** One owner-declared reusable member tool policy. */
|
|
35
|
+
export interface MemberToolPolicy {
|
|
36
|
+
/** Global tool names that stay visible; coordination tools are added automatically. */
|
|
37
|
+
allow?: string[];
|
|
38
|
+
/** Global tool names removed from visibility; captain-only tools are added automatically. */
|
|
39
|
+
deny?: string[];
|
|
40
|
+
}
|
|
41
|
+
/** The immutable effective filter of one member. */
|
|
42
|
+
export interface MemberToolFilter {
|
|
43
|
+
/** Visible global tools; absent means "no allow-list" (everything not denied stays). */
|
|
44
|
+
allow?: string[];
|
|
45
|
+
/** Removed global tools; never empty (the captain-only deny list is always included). */
|
|
46
|
+
deny: string[];
|
|
47
|
+
}
|
|
48
|
+
/** How the running host resolves the depth entries; both fields are optional. */
|
|
49
|
+
export interface ToolPolicyContext {
|
|
50
|
+
/** Member delegation depth cap; `0` denies the host delegation tools when they exist. */
|
|
51
|
+
maxDepth?: number;
|
|
52
|
+
/** Names the host's `tools.restrict()` would admit, or undefined without a registry view. */
|
|
53
|
+
knownTools?: ReadonlySet<string>;
|
|
54
|
+
}
|
|
55
|
+
/** Trim, drop blanks, and de-duplicate names, preserving first-seen order. */
|
|
56
|
+
export declare function dedupeToolNames(names: readonly string[]): string[];
|
|
57
|
+
/**
|
|
58
|
+
* Validate and normalize one declared policy. Empty policies and empty lists
|
|
59
|
+
* are configuration mistakes: an `allow: []` policy would hide even the
|
|
60
|
+
* coordination tools, and a policy with neither list restricts nothing.
|
|
61
|
+
* Denying a required coordination tool is rejected for the same reason.
|
|
62
|
+
* @param id - the policy id, used in error messages.
|
|
63
|
+
* @param policy - the declared policy.
|
|
64
|
+
* @returns the normalized policy (trimmed, de-duplicated, blanks dropped).
|
|
65
|
+
*/
|
|
66
|
+
export declare function normalizeToolPolicy(id: string, policy: MemberToolPolicy): MemberToolPolicy;
|
|
67
|
+
/**
|
|
68
|
+
* Validate every configured policy once, at plugin mount, so a malformed
|
|
69
|
+
* deployment config fails loud instead of at the first member creation.
|
|
70
|
+
* @param raw - the configured `toolPolicies` map.
|
|
71
|
+
* @returns normalized policies keyed by trimmed id.
|
|
72
|
+
*/
|
|
73
|
+
export declare function parseToolPolicies(raw: Record<string, MemberToolPolicy> | undefined | null): Record<string, MemberToolPolicy>;
|
|
74
|
+
/**
|
|
75
|
+
* The policy behind one selected id, or `undefined` when this deployment no
|
|
76
|
+
* longer declares it (a config change after the member was created). Used on
|
|
77
|
+
* paths that must not fail a durable record merely because the owner edited
|
|
78
|
+
* configuration; selection itself uses {@link requireToolPolicy}.
|
|
79
|
+
* @param policies - normalized configured policies.
|
|
80
|
+
* @param id - the selected policy id.
|
|
81
|
+
* @returns the declared policy, or undefined.
|
|
82
|
+
*/
|
|
83
|
+
export declare function optionalToolPolicy(policies: Record<string, MemberToolPolicy>, id: string): MemberToolPolicy | undefined;
|
|
84
|
+
/**
|
|
85
|
+
* The deny names one spawn may relax for a member that selected a policy.
|
|
86
|
+
*
|
|
87
|
+
* The built-in captain/depth entries keep the #163/#164 recovery path, but a
|
|
88
|
+
* name the owner explicitly declared in the policy's own `deny` list is never
|
|
89
|
+
* relaxable — even when it is also a built-in entry (`send_message` and
|
|
90
|
+
* `subagent` are depth entries, and every captain-only name is a built-in
|
|
91
|
+
* deny). Dropping such a name would spawn the member under a different policy
|
|
92
|
+
* than the owner declared, which is contract drift rather than recovery.
|
|
93
|
+
* @param policy - the member's selected policy, or undefined when the member
|
|
94
|
+
* selected none (or the id is no longer configured).
|
|
95
|
+
* @param policySelected - whether the member selected a policy at all. A
|
|
96
|
+
* selected id whose policy is no longer configured relaxes nothing, so the
|
|
97
|
+
* member fails loud instead of spawning under an unknown filter.
|
|
98
|
+
* @returns the deny names the lenient retry may drop.
|
|
99
|
+
*/
|
|
100
|
+
export declare function memberSpawnRelaxableDeny(policy: MemberToolPolicy | undefined, policySelected?: boolean): readonly string[];
|
|
101
|
+
/**
|
|
102
|
+
* The policy behind one selected id. A model-facing selection may only name a
|
|
103
|
+
* policy the deployment owner declared, so an unknown id is an error raised
|
|
104
|
+
* before any member is persisted.
|
|
105
|
+
* @param policies - normalized configured policies.
|
|
106
|
+
* @param id - the selected policy id.
|
|
107
|
+
* @returns the declared policy.
|
|
108
|
+
*/
|
|
109
|
+
export declare function requireToolPolicy(policies: Record<string, MemberToolPolicy>, id: string): MemberToolPolicy;
|
|
110
|
+
/**
|
|
111
|
+
* Turn a selected policy (or no policy) into the immutable effective filter.
|
|
112
|
+
*
|
|
113
|
+
* Without `knownTools` the host's delegation names are forwarded verbatim, as
|
|
114
|
+
* before #163: the spawn retry drops whatever the host reports unknown.
|
|
115
|
+
* @param policy - the normalized policy, or undefined for the legacy behavior.
|
|
116
|
+
* @param context - depth cap and the host's restrictable-name view.
|
|
117
|
+
* @returns the effective filter handed to `startContinuable`.
|
|
118
|
+
*/
|
|
119
|
+
export declare function resolveMemberToolFilter(policy: MemberToolPolicy | undefined, context?: ToolPolicyContext): MemberToolFilter;
|
package/lib/types/tools.d.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { Context } from '@deepseek-ai/cordis';
|
|
12
12
|
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
13
|
+
import { type MemberToolPolicy } from './tool-policies.ts';
|
|
14
|
+
import type { ArtifactSnapshot } from './types.ts';
|
|
13
15
|
import { type TeamState, type TeamTask } from './types.ts';
|
|
14
16
|
export { steerCaptainReport } from './members.ts';
|
|
15
17
|
/** Resolved plugin config consumed by the tools. */
|
|
@@ -20,6 +22,8 @@ export interface ToolsConfig {
|
|
|
20
22
|
memberProvider: string;
|
|
21
23
|
/** Optional member model override. */
|
|
22
24
|
memberModel?: string;
|
|
25
|
+
/** Optional member LLM provider, paired with `memberModel` (issue #189). */
|
|
26
|
+
memberLlmProvider?: string;
|
|
23
27
|
/** Prompt injected into member personas and assignments. */
|
|
24
28
|
executionPrompt?: string;
|
|
25
29
|
/** Plugin fallback route. */
|
|
@@ -30,6 +34,8 @@ export interface ToolsConfig {
|
|
|
30
34
|
maxMembers: number;
|
|
31
35
|
/** Named team profiles from the active DSH profile. */
|
|
32
36
|
profiles: Record<string, import('./profiles.ts').TeamProfileConfig>;
|
|
37
|
+
/** Owner-declared reusable member tool policies, keyed by policy id (#59). */
|
|
38
|
+
toolPolicies: Record<string, MemberToolPolicy>;
|
|
33
39
|
}
|
|
34
40
|
/** Browser/UI mutations allowed while a plan is waiting for approval. */
|
|
35
41
|
export type StagedPlanMutation = {
|
|
@@ -40,6 +46,8 @@ export type StagedPlanMutation = {
|
|
|
40
46
|
model: string;
|
|
41
47
|
reasoningEffort?: string | null;
|
|
42
48
|
executionPrompt?: string | null;
|
|
49
|
+
/** Selected tool-policy id; an empty string clears it back to no policy. */
|
|
50
|
+
toolPolicy?: string;
|
|
43
51
|
} | {
|
|
44
52
|
action: 'update_task';
|
|
45
53
|
taskId: string;
|
|
@@ -86,6 +94,7 @@ export declare function haltTeamWork(input: {
|
|
|
86
94
|
signal?: AbortSignal;
|
|
87
95
|
}): Promise<{
|
|
88
96
|
teamName: string;
|
|
97
|
+
captainSessionId: string;
|
|
89
98
|
cancelledTasks: number;
|
|
90
99
|
alreadyHalted: boolean;
|
|
91
100
|
}>;
|
|
@@ -101,7 +110,9 @@ export declare function stagedPlanFeedbackContext(teamName: string): string;
|
|
|
101
110
|
* @param config - resolved tool config.
|
|
102
111
|
*/
|
|
103
112
|
export declare function registerAgentTeamsTools(ctx: Context, config: ToolsConfig): AgentTeamsRuntime;
|
|
104
|
-
export declare function applyQualityFollowUp(team: TeamState, closed: TeamTask): {
|
|
113
|
+
export declare function applyQualityFollowUp(team: TeamState, closed: TeamTask, snapshotPaths?: (paths: readonly string[]) => ArtifactSnapshot, pathExists?: (path: string) => boolean): {
|
|
105
114
|
created: TeamTask[];
|
|
106
115
|
escalated: boolean;
|
|
116
|
+
reason?: string;
|
|
117
|
+
excludedFindings?: string[];
|
|
107
118
|
};
|