@mgiles/perk 3.2.0 → 3.3.0
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 +5 -0
- package/extension/authoring/gist/draft.ts +198 -0
- package/extension/authoring/gist/prose.ts +46 -0
- package/extension/authoring/gist/review.ts +133 -0
- package/extension/authoring/gist/save.ts +118 -0
- package/extension/authoring/objective/draft.ts +345 -0
- package/extension/{factories/objectiveDreamReport.ts → authoring/objective/dreamReportGate.ts} +74 -131
- package/extension/authoring/objective/planning.ts +124 -0
- package/extension/authoring/objective/prose.ts +103 -0
- package/extension/authoring/objective/review.ts +128 -0
- package/extension/authoring/objective/save.ts +224 -0
- package/extension/authoring/plan/draft.ts +84 -0
- package/extension/authoring/plan/prose.ts +41 -0
- package/extension/authoring/plan/review.ts +269 -0
- package/extension/authoring/plan/save.ts +256 -0
- package/extension/authoring/plan/source.ts +82 -0
- package/extension/authoring/refinement/context.ts +468 -0
- package/extension/authoring/refinement/draft.ts +261 -0
- package/extension/authoring/refinement/prose.ts +79 -0
- package/extension/authoring/refinement/review.ts +111 -0
- package/extension/authoring/refinement/save.ts +119 -0
- package/extension/authoring/review/approvalGate.ts +34 -0
- package/extension/authoring/review/draftContext.ts +68 -0
- package/extension/codeReview/automated.ts +352 -0
- package/extension/codeReview/submission.ts +229 -0
- package/extension/delivery/address.ts +295 -0
- package/extension/delivery/ci.ts +355 -0
- package/extension/delivery/commitCompact.ts +93 -0
- package/extension/delivery/conflictResolution.ts +247 -0
- package/extension/delivery/ready.ts +193 -0
- package/extension/delivery/stackConflict.ts +361 -0
- package/extension/delivery/stackObjective.ts +16 -0
- package/extension/delivery/stackReconcile.ts +165 -0
- package/extension/delivery/submit.ts +171 -0
- package/extension/index.ts +365 -380
- package/extension/learning/analystWave.ts +324 -0
- package/extension/learning/audit.ts +667 -0
- package/extension/learning/capture.ts +92 -0
- package/extension/learning/containment.ts +104 -0
- package/extension/{waves/dreamWave.ts → learning/dream.ts} +112 -94
- package/extension/learning/dreamAnalysis.ts +435 -0
- package/extension/{waves/dreamReducerWave.ts → learning/dreamReducer.ts} +46 -41
- package/extension/{waves → learning}/dreamReport.ts +35 -31
- package/extension/learning/harvest.ts +491 -0
- package/extension/learning/prose.ts +66 -0
- package/extension/learning/routing.ts +79 -0
- package/extension/pi/v1/bashScanTimeout.ts +64 -0
- package/extension/{doors/prReview.ts → pi/v1/codeReview/automated.ts} +215 -311
- package/extension/{doors/prReviewBrowser.ts → pi/v1/codeReview/browser.ts} +53 -33
- package/extension/{doors/hunkHandoff.ts → pi/v1/codeReview/checkout.ts} +12 -8
- package/extension/{doors/reviewWaveTools.ts → pi/v1/codeReview/reviewWave.ts} +146 -114
- package/extension/{doors/stackReviewBrowser.ts → pi/v1/codeReview/stack.ts} +62 -29
- package/extension/pi/v1/codeReview/submit.ts +354 -0
- package/extension/{doors/prReviewTerminal.ts → pi/v1/codeReview/terminal.ts} +32 -27
- package/extension/pi/v1/contextEvidence.ts +80 -0
- package/extension/pi/v1/contextInjection.ts +207 -0
- package/extension/{doors → pi/v1/delivery}/address.ts +154 -267
- package/extension/pi/v1/delivery/ci.ts +570 -0
- package/extension/pi/v1/delivery/commitCompact.ts +201 -0
- package/extension/pi/v1/delivery/conflictResolverEngine.ts +425 -0
- package/extension/{doors → pi/v1/delivery}/land.ts +123 -61
- package/extension/pi/v1/delivery/ready.ts +322 -0
- package/extension/pi/v1/delivery/stackConflictResolver.ts +172 -0
- package/extension/pi/v1/delivery/stackDrive.ts +120 -0
- package/extension/pi/v1/delivery/stackLand.ts +223 -0
- package/extension/pi/v1/delivery/stackRecover.ts +265 -0
- package/extension/pi/v1/delivery/stackStatus.ts +237 -0
- package/extension/pi/v1/delivery/stackSync.ts +658 -0
- package/extension/pi/v1/delivery/submit.ts +389 -0
- package/extension/pi/v1/delivery/submitConflict.ts +186 -0
- package/extension/pi/v1/draftReview.ts +431 -0
- package/extension/{doors → pi/v1}/draftReviewWaveTools.ts +141 -151
- package/extension/pi/v1/gist.ts +794 -0
- package/extension/pi/v1/learning/audit.ts +186 -0
- package/extension/pi/v1/learning/dream.ts +207 -0
- package/extension/{doors/learnFactory.ts → pi/v1/learning/factory.ts} +18 -65
- package/extension/{doors/harvestWaveTools.ts → pi/v1/learning/harvest.ts} +46 -100
- package/extension/pi/v1/learning/learn.ts +585 -0
- package/extension/pi/v1/lifecycleGates.ts +127 -0
- package/extension/{factories → pi/v1}/objective.ts +53 -33
- package/extension/pi/v1/objectiveAuthoring.ts +672 -0
- package/extension/pi/v1/objectiveDreamGate.ts +160 -0
- package/extension/{factories/objectivePlan.ts → pi/v1/objectivePlanning.ts} +328 -533
- package/extension/pi/v1/objectiveRefinement.ts +1320 -0
- package/extension/pi/v1/objectiveReview.ts +451 -0
- package/extension/{doors → pi/v1}/objectiveReviewBrowser.ts +259 -172
- package/extension/pi/v1/plan.ts +812 -0
- package/extension/pi/v1/planReview.ts +820 -0
- package/extension/{doors → pi/v1}/planReviewBrowser.ts +228 -152
- package/extension/{doors/annotationPush.ts → pi/v1/providers/annotations.ts} +158 -89
- package/extension/pi/v1/providers/plannotator.ts +487 -0
- package/extension/{doors → pi/v1/providers}/plannotatorHandoff.ts +73 -27
- package/extension/pi/v1/providers/selection.ts +43 -0
- package/extension/{adapters/planAdapterTombell.ts → pi/v1/providers/tombell.ts} +43 -72
- package/extension/pi/v1/review.ts +538 -0
- package/extension/pi/v1/reviewOutcome.ts +9 -0
- package/extension/pi/v1/scoutWave.ts +318 -0
- package/extension/{doors → pi/v1}/selfcheck.ts +4 -4
- package/extension/session/branchWorkflowSession.ts +60 -0
- package/extension/session/lifecycle.ts +644 -0
- package/extension/session/lifecycleGates.ts +64 -0
- package/extension/session/saveDestination.ts +87 -0
- package/extension/session/workflowSession.ts +971 -0
- package/extension/substrate/agentScratch.ts +27 -54
- package/extension/substrate/bashScanTimeout.ts +181 -0
- package/extension/substrate/bindingDelivery.ts +38 -30
- package/extension/substrate/bindings.ts +4 -5
- package/extension/substrate/cache.ts +64 -12
- package/extension/substrate/childRestrictions.ts +39 -0
- package/extension/substrate/coldDoor.ts +17 -1
- package/extension/substrate/config.ts +157 -21
- package/extension/substrate/git.ts +88 -6
- package/extension/substrate/modelVisible.ts +53 -0
- package/extension/substrate/prompts.ts +22 -0
- package/extension/substrate/registry.ts +2 -0
- package/extension/substrate/resolverLease.ts +5 -4
- package/extension/substrate/sessionData.ts +85 -152
- package/extension/substrate/toolGating.ts +263 -84
- package/extension/substrate/unifiedDiff.ts +1 -1
- package/extension/substrate/workflowState.ts +178 -163
- package/extension/substrate/worktreeResolverLock.ts +261 -0
- package/extension/surfaces/surfaces.ts +79 -27
- package/extension/waves/adversarialReviewWave.ts +87 -46
- package/extension/waves/blockedReports.ts +59 -0
- package/extension/waves/draftReviewWave.ts +42 -42
- package/extension/waves/laneIdentity.ts +77 -0
- package/extension/waves/objectiveExplorerWave.ts +24 -24
- package/extension/waves/prReviewWave.ts +89 -77
- package/extension/waves/reportWave.ts +438 -578
- package/extension/waves/reviewClassifierWave.ts +22 -22
- package/extension/waves/rpcAdapter.ts +100 -15
- package/extension/waves/scoutWave.ts +192 -0
- package/extension/waves/transport.ts +480 -0
- package/extension/worker/sdkAdapter.ts +494 -0
- package/extension/worker/stageExecution.ts +679 -0
- package/extension/workerMain.ts +18 -19
- package/package.json +6 -4
- package/prompts/_fixtures/live.yaml +43 -18
- package/prompts/contexts/adapters/plannotator-gist.md +6 -0
- package/prompts/contexts/adapters/plannotator-objective.md +6 -0
- package/prompts/contexts/adapters/plannotator-plan.md +8 -1
- package/prompts/contexts/adapters/plannotator-refinement.md +22 -0
- package/prompts/contexts/objective-refinement.md +17 -0
- package/prompts/contexts/read-only.md +1 -1
- package/prompts/stages/conflict-resolution-continuation.md +9 -6
- package/prompts/stages/conflict-resolution.md +4 -4
- package/prompts/stages/objective-plan/guidance.md +2 -2
- package/prompts/stages/objective-plan/seed.md +9 -1
- package/prompts/stages/objective-reconcile-ready.md +1 -1
- package/prompts/stages/objective-reconcile.md +1 -1
- package/prompts/stages/objective-refine/seed.md +18 -0
- package/prompts/stages/objective-review-browser.md +4 -4
- package/prompts/stages/objective-sync.md +1 -1
- package/prompts/stages/plan-review-browser.md +4 -4
- package/prompts/stages/pr-review-browser/active.md +3 -4
- package/prompts/stages/pr-review-browser/foreign.md +3 -4
- package/prompts/stages/pr-review-terminal/active.md +3 -3
- package/prompts/stages/pr-review-terminal/foreign.md +3 -3
- package/prompts/stages/pr-review.md +3 -3
- package/prompts/stages/stack-review-browser/stack.md +5 -6
- package/shared/README.md +8 -0
- package/shared/bindings.yaml +3 -3
- package/shared/contracts.md +2601 -506
- package/shared/fixtures/issues-table.json +130 -0
- package/shared/registry.yaml +13 -0
- package/shared/schemas/outputs/objective-node-engagement.schema.json +318 -0
- package/shared/schemas/outputs/objective-stack-status.schema.json +6 -1
- package/shared/schemas/outputs/pr-review-context.schema.json +54 -9
- package/shared/schemas/outputs/pr-review-stack-context.schema.json +196 -0
- package/extension/adapters/planAdapterPlannotator.ts +0 -362
- package/extension/doors/auditWaveTools.ts +0 -352
- package/extension/doors/ciExecutor.ts +0 -756
- package/extension/doors/commitCompact.ts +0 -251
- package/extension/doors/dreamWaveTools.ts +0 -489
- package/extension/doors/learn.ts +0 -668
- package/extension/doors/lifecycleGates.ts +0 -207
- package/extension/doors/objectiveStack.ts +0 -1543
- package/extension/doors/prReviewDynamic.ts +0 -276
- package/extension/doors/ready.ts +0 -279
- package/extension/doors/submit.ts +0 -373
- package/extension/doors/submitPrReview.ts +0 -505
- package/extension/factories/gistAuthor.ts +0 -94
- package/extension/factories/gistDraft.ts +0 -265
- package/extension/factories/gistSave.ts +0 -251
- package/extension/factories/implementHere.ts +0 -116
- package/extension/factories/objectiveAuthor.ts +0 -98
- package/extension/factories/objectiveDraft.ts +0 -466
- package/extension/factories/objectiveSave.ts +0 -366
- package/extension/factories/planDraft.ts +0 -140
- package/extension/factories/planMode.ts +0 -205
- package/extension/factories/planReview.ts +0 -1237
- package/extension/factories/planSave.ts +0 -604
- package/extension/factories/planTitle.ts +0 -141
- package/extension/substrate/structuredOutput.ts +0 -202
- package/extension/waves/auditWave.ts +0 -312
- package/extension/waves/harvestWave.ts +0 -399
- package/extension/waves/learnWave.ts +0 -155
- package/extension/waves/memoryAdapter.ts +0 -139
- package/extension/waves/prReviewDynamicWave.ts +0 -777
- package/extension/worker/readOnlySession.ts +0 -294
- package/extension/worker/worker.ts +0 -899
- package/prompts/stages/pr-review-dynamic.md +0 -7
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// The shared "a schema-valid report is not necessarily a completed assessment" normalization
|
|
2
|
+
// over a settled `ReportWaveResult`. Assessment completion is domain policy, not engine success:
|
|
3
|
+
// the engine accepts any schema-valid report, so a reviewer that could not finish (context fetch
|
|
4
|
+
// failed, a context file unreadable, the hunt stopped early) would otherwise count as COVERED
|
|
5
|
+
// with empty findings — the exact failure mode this helper exists to prevent. Each flow supplies
|
|
6
|
+
// its own typed predicate (`verdict: "blocked"` for pr-review, `blocked: true` for the
|
|
7
|
+
// adversarial doors); the reclassification itself is flow-neutral and byte-identical across
|
|
8
|
+
// callers. Diagnostic prose (`fyi`) is untrusted DATA preserved verbatim for the parent's
|
|
9
|
+
// in-session diagnosis — never an instruction and never parsed for a decision.
|
|
10
|
+
|
|
11
|
+
import type { AssignmentReport, ReportWaveFailure, ReportWaveResult } from "./reportWave.ts";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Move every report the predicate marks blocked from `reports` into `failures` as an
|
|
15
|
+
* assignment-keyed `lane-failed` (the enclosing assignment key — never the report's own angle
|
|
16
|
+
* or prose — identifies the failure). Only non-null, non-array object reports are tested; the
|
|
17
|
+
* detail is exactly `"reviewer blocked:\n"` + the nonblank-trimmed `fyi` strings joined by
|
|
18
|
+
* `"\n"` (retained bytes, duplicates and order preserved), or the fixed fallback sentence.
|
|
19
|
+
* Existing failures precede newly blocked ones (report order); surviving report order and the
|
|
20
|
+
* receipt are preserved; `complete` requires incoming completeness AND no removed block.
|
|
21
|
+
*/
|
|
22
|
+
export function reclassifyBlockedReports(
|
|
23
|
+
result: ReportWaveResult,
|
|
24
|
+
isBlocked: (report: Record<string, unknown>) => boolean,
|
|
25
|
+
): ReportWaveResult {
|
|
26
|
+
const reports: AssignmentReport[] = [];
|
|
27
|
+
const blocked: ReportWaveFailure[] = [];
|
|
28
|
+
for (const assignment of result.reports) {
|
|
29
|
+
const report = assignment.report;
|
|
30
|
+
if (
|
|
31
|
+
typeof report !== "object" ||
|
|
32
|
+
report === null ||
|
|
33
|
+
Array.isArray(report) ||
|
|
34
|
+
!isBlocked(report as Record<string, unknown>)
|
|
35
|
+
) {
|
|
36
|
+
reports.push(assignment);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const notes =
|
|
40
|
+
"fyi" in report && Array.isArray(report.fyi)
|
|
41
|
+
? report.fyi.filter(
|
|
42
|
+
(entry): entry is string => typeof entry === "string" && entry.trim().length > 0,
|
|
43
|
+
)
|
|
44
|
+
: [];
|
|
45
|
+
blocked.push({
|
|
46
|
+
key: assignment.key,
|
|
47
|
+
reason: "lane-failed",
|
|
48
|
+
detail:
|
|
49
|
+
"reviewer blocked:\n" +
|
|
50
|
+
(notes.length > 0 ? notes.join("\n") : "required review assessment could not complete"),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
complete: result.complete && blocked.length === 0,
|
|
55
|
+
reports,
|
|
56
|
+
failures: [...result.failures, ...blocked],
|
|
57
|
+
receipt: result.receipt,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
// The draft-review `
|
|
2
|
-
// draft-review doors' (/plan-review-browser, /objective-review-browser) vocabulary as tested
|
|
1
|
+
// The draft-review `ReportWaveRequest`-building entrypoint over the shared report-wave module —
|
|
2
|
+
// the draft-review doors' (/plan-review-browser, /objective-review-browser) vocabulary as tested
|
|
3
3
|
// code (sibling of `adversarialReviewWave.ts`): the four settled angles plus the custom lane,
|
|
4
4
|
// the per-lane completion-report schema, and the lane/task composition are module-owned here,
|
|
5
|
-
// launched NON-BLOCKING via `
|
|
6
|
-
//
|
|
5
|
+
// launched NON-BLOCKING via `wave.start` so the parent ends its turn after launch and relays
|
|
6
|
+
// batches on native supervisor wakes before collecting on matching workflow completion.
|
|
7
7
|
//
|
|
8
8
|
// CONSUMED by the `/plan-review-browser` and `/objective-review-browser` doors via the
|
|
9
9
|
// `start_draft_review_wave` / `collect_draft_review_wave` tool pair
|
|
10
|
-
// (`extension/
|
|
10
|
+
// (`extension/pi/v1/draftReviewWaveTools.ts`, over the door-primed context in
|
|
11
|
+
// `extension/authoring/review/draftContext.ts`) — `draftType` parameterizes the lane tasks.
|
|
11
12
|
//
|
|
12
13
|
// ZERO retries — deliberate: the doors' contract is honest incompleteness surfaced to the human
|
|
13
14
|
// during triage (an `ok: false` lane is reported, never papered over), so the pr-review
|
|
14
15
|
// bounded-retry policy does not carry over.
|
|
15
16
|
//
|
|
16
|
-
// The surface handle (URL/port) is STRUCTURALLY UNREPRESENTABLE: `
|
|
17
|
-
// URL parameter at all, so the children can never learn the review surface — enforced by
|
|
17
|
+
// The surface handle (URL/port) is STRUCTURALLY UNREPRESENTABLE: `buildDraftReviewAssignments`
|
|
18
|
+
// has no URL parameter at all, so the children can never learn the review surface — enforced by
|
|
18
19
|
// construction, pinned by the suite. There is likewise NO `directive` parameter: the PR doors'
|
|
19
20
|
// focus-note semantics deliberately do not carry over — the custom lane IS the draft doors'
|
|
20
21
|
// user-input channel.
|
|
21
22
|
//
|
|
22
|
-
// The finding rows are deliberately identical to `
|
|
23
|
+
// The finding rows are deliberately identical to `pi/v1/providers/annotations.ts`'s `PlanFinding` shape
|
|
23
24
|
// (`{phrase, severity, confidence, body}`), so draft reports feed `push_annotations` plan-mode
|
|
24
25
|
// without reshaping; the `agents/draft-reviewer.md` def completes via the `structured_output`
|
|
25
26
|
// tool this wave's `outputSchema` injects per lane.
|
|
26
27
|
|
|
27
28
|
import { PONYTAIL_CORE_SKILL } from "./ponytail.ts";
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
type WaveSpec,
|
|
29
|
+
import type {
|
|
30
|
+
ReportAssignment,
|
|
31
|
+
ReportWave,
|
|
32
|
+
ReportWaveRequest,
|
|
33
|
+
StartWaveResult,
|
|
34
34
|
} from "./reportWave.ts";
|
|
35
35
|
|
|
36
36
|
/** The four-slug settled draft-review angle allowlist (the custom lane rides separately). */
|
|
@@ -56,11 +56,11 @@ export function isDraftReviewAngle(value: string): value is DraftReviewAngle {
|
|
|
56
56
|
/**
|
|
57
57
|
* The per-lane completion-report schema the wave enforces as its `outputSchema` — the engine
|
|
58
58
|
* injects a `structured_output` tool into each lane and fails any lane whose report is missing
|
|
59
|
-
* or schema-invalid. Closed shapes, `{angle, summary, findings, fyi}` all required, and
|
|
59
|
+
* or schema-invalid. Closed shapes, `{angle, summary, findings, fyi, streamed}` all required, and
|
|
60
60
|
* DELIBERATELY NO VERDICT FIELD — the human adjudicates every finding in the browser, so there
|
|
61
61
|
* is no clean/actionable derivation to make consistent (the `ADVERSARIAL_REVIEW_REPORT_SCHEMA`
|
|
62
62
|
* rationale). The `angle` enum includes `custom` (the custom lane echoes it). Finding rows are
|
|
63
|
-
* the forward-bound plan-mode `PlanFinding` shape (`
|
|
63
|
+
* the forward-bound plan-mode `PlanFinding` shape (`pi/v1/providers/annotations.ts`'s `PLAN_FINDING_KEYS`):
|
|
64
64
|
* `phrase` is required-nullable (the byte-exact draft span, or `null` for a global finding),
|
|
65
65
|
* and the severity/confidence enums match the agent def's triage tags. The `phrase` string arm
|
|
66
66
|
* requires a non-whitespace character (`pattern` applies only to string instances, so `null`
|
|
@@ -71,12 +71,13 @@ export function isDraftReviewAngle(value: string): value is DraftReviewAngle {
|
|
|
71
71
|
export const DRAFT_REVIEW_REPORT_SCHEMA = {
|
|
72
72
|
type: "object",
|
|
73
73
|
additionalProperties: false,
|
|
74
|
-
required: ["angle", "summary", "findings", "fyi"],
|
|
74
|
+
required: ["angle", "summary", "findings", "fyi", "streamed"],
|
|
75
75
|
properties: {
|
|
76
76
|
angle: {
|
|
77
77
|
type: "string",
|
|
78
78
|
enum: ["grounding", "scope", "decision-completeness", "risk", "custom", "ponytail"],
|
|
79
79
|
},
|
|
80
|
+
streamed: { type: "boolean" },
|
|
80
81
|
summary: { type: "string" },
|
|
81
82
|
findings: {
|
|
82
83
|
type: "array",
|
|
@@ -108,22 +109,23 @@ function laneTaskTail(draftType: "plan" | "objective", draft: string): string {
|
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
/**
|
|
111
|
-
* Build the reviewer
|
|
112
|
-
* task carrying the angle opener, the draft type, and the `<untrusted_draft>`-wrapped
|
|
113
|
-
* draft — AND NOTHING ELSE: no URL parameter exists (the surface handle is
|
|
114
|
-
* construction) and no `directive` parameter exists (the custom lane is the
|
|
115
|
-
* When `custom` is supplied, one additional
|
|
116
|
-
* human-supplied angle definition as flagged DATA. The builder stays
|
|
117
|
-
* count — angle-selection policy (2–3 picked + optional custom) is
|
|
112
|
+
* Build the reviewer assignments for a selection: key = label = slug, the fixed agent/phase,
|
|
113
|
+
* and a task carrying the angle opener, the draft type, and the `<untrusted_draft>`-wrapped
|
|
114
|
+
* rendered draft — AND NOTHING ELSE: no URL parameter exists (the surface handle is
|
|
115
|
+
* unrepresentable by construction) and no `directive` parameter exists (the custom lane is the
|
|
116
|
+
* user-input channel). When `custom` is supplied, one additional assignment (key = label =
|
|
117
|
+
* `"custom"`) carries the human-supplied angle definition as flagged DATA. The builder stays
|
|
118
|
+
* permissive about assignment count — angle-selection policy (2–3 picked + optional custom) is
|
|
119
|
+
* the door/tool's concern.
|
|
118
120
|
*/
|
|
119
|
-
export function
|
|
121
|
+
export function buildDraftReviewAssignments(opts: {
|
|
120
122
|
angles: DraftReviewAngle[];
|
|
121
123
|
custom?: string;
|
|
122
124
|
draftType: "plan" | "objective";
|
|
123
125
|
draft: string;
|
|
124
|
-
}):
|
|
126
|
+
}): ReportAssignment[] {
|
|
125
127
|
const tail = laneTaskTail(opts.draftType, opts.draft);
|
|
126
|
-
const
|
|
128
|
+
const assignments: ReportAssignment[] = opts.angles.map((angle) => ({
|
|
127
129
|
key: angle,
|
|
128
130
|
label: angle,
|
|
129
131
|
agent: "perk.draft-reviewer",
|
|
@@ -131,7 +133,7 @@ export function buildDraftReviewLanes(opts: {
|
|
|
131
133
|
task: `${DRAFT_REVIEW_ANGLES[angle]}\n${tail}`,
|
|
132
134
|
}));
|
|
133
135
|
if (opts.custom !== undefined) {
|
|
134
|
-
|
|
136
|
+
assignments.push({
|
|
135
137
|
key: CUSTOM_LANE_KEY,
|
|
136
138
|
label: CUSTOM_LANE_KEY,
|
|
137
139
|
agent: "perk.draft-reviewer",
|
|
@@ -141,7 +143,7 @@ export function buildDraftReviewLanes(opts: {
|
|
|
141
143
|
`for this lane): ${opts.custom}\n${tail}`,
|
|
142
144
|
});
|
|
143
145
|
}
|
|
144
|
-
|
|
146
|
+
assignments.push({
|
|
145
147
|
key: "ponytail",
|
|
146
148
|
label: "ponytail",
|
|
147
149
|
agent: "perk.draft-reviewer",
|
|
@@ -150,7 +152,7 @@ export function buildDraftReviewLanes(opts: {
|
|
|
150
152
|
skill: "ponytail",
|
|
151
153
|
requiredSkill: PONYTAIL_CORE_SKILL,
|
|
152
154
|
});
|
|
153
|
-
return
|
|
155
|
+
return assignments;
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
export interface DraftReviewWaveOptions {
|
|
@@ -168,25 +170,23 @@ export interface DraftReviewWaveOptions {
|
|
|
168
170
|
/** Accepted for parity/tests only — the flow tool deliberately never threads its own signal. */
|
|
169
171
|
signal?: AbortSignal;
|
|
170
172
|
/** Test seam; production validates the exact source-bound Ponytail skill. */
|
|
171
|
-
requiredSkillPreflight?:
|
|
173
|
+
requiredSkillPreflight?: ReportWaveRequest["requiredSkillPreflight"];
|
|
172
174
|
}
|
|
173
175
|
|
|
174
176
|
/**
|
|
175
|
-
* Start the draft-review wave NON-BLOCKING (the streaming
|
|
176
|
-
* angle vocabulary and launch under the strict completeness policy — zero retries, so an
|
|
177
|
-
* uncovered angle stays an honest, human-visible incompleteness. Returns the `
|
|
178
|
-
* outcome: the
|
|
179
|
-
* failure.
|
|
177
|
+
* Start the draft-review wave NON-BLOCKING (the streaming split): build the assignments from
|
|
178
|
+
* the angle vocabulary and launch under the strict completeness policy — zero retries, so an
|
|
179
|
+
* uncovered angle stays an honest, human-visible incompleteness. Returns the `wave.start`
|
|
180
|
+
* outcome: the opaque ref + identity telemetry on success, or the normalized launch failure.
|
|
180
181
|
*/
|
|
181
182
|
export async function startDraftReviewWave(
|
|
182
|
-
|
|
183
|
+
wave: ReportWave,
|
|
183
184
|
opts: DraftReviewWaveOptions,
|
|
184
|
-
): Promise<
|
|
185
|
-
return await
|
|
186
|
-
adapter,
|
|
185
|
+
): Promise<StartWaveResult> {
|
|
186
|
+
return await wave.start(
|
|
187
187
|
{
|
|
188
188
|
flow: "draft-review",
|
|
189
|
-
|
|
189
|
+
assignments: buildDraftReviewAssignments({
|
|
190
190
|
angles: opts.angles,
|
|
191
191
|
draftType: opts.draftType,
|
|
192
192
|
draft: opts.draft,
|
|
@@ -200,6 +200,6 @@ export async function startDraftReviewWave(
|
|
|
200
200
|
? { requiredSkillPreflight: opts.requiredSkillPreflight }
|
|
201
201
|
: {}),
|
|
202
202
|
},
|
|
203
|
-
opts.signal,
|
|
203
|
+
{ signal: opts.signal },
|
|
204
204
|
);
|
|
205
205
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// The routing-token fence for report-wave lane identity. A ROUTING TOKEN is any producer-owned
|
|
2
|
+
// identity rendered into a report child's task prose so the child can select its lane byte-exact
|
|
3
|
+
// against the manifest, or echo it verbatim into its typed report — untrusted DATA, never an
|
|
4
|
+
// instruction. Usually that is the SEMANTIC LANE ID (a harvest `<category>-<n>`, a dream cluster
|
|
5
|
+
// id, an audit `expectation_id`); the audit wave additionally renders a second, pair-level token
|
|
6
|
+
// — the `session_basename` the child echoes verbatim — which is a routing token but not a lane
|
|
7
|
+
// id. Because a token must survive byte-exact matching against the manifest (and byte-exact echo
|
|
8
|
+
// back), the fence is a REFUSAL rule, never an escaping rule: any escaping breaks selection by
|
|
9
|
+
// construction, so an accepted token renders byte-identical and an unacceptable one is refused
|
|
10
|
+
// (strict decoders) or degraded (the lenient audit planner) upstream, before any task is composed.
|
|
11
|
+
//
|
|
12
|
+
// Refused: the empty token; C0 controls U+0000–U+001F (a newline or carriage return would let a
|
|
13
|
+
// producer-owned id read as further task instructions); DEL U+007F; C1 controls U+0080–U+009F
|
|
14
|
+
// (NEL U+0085 included); the Unicode line/paragraph separators U+2028/U+2029; and the double
|
|
15
|
+
// quote `"` (every task quotes the id in double quotes). Nothing else — spaces, `@`, `/`, `:`,
|
|
16
|
+
// backticks, `${}`, astral characters and any length are admitted. There is deliberately NO
|
|
17
|
+
// length bound: an oversized id fails as availability (a transport or model-context limit
|
|
18
|
+
// surfacing as a wave-level failure), never as a silent success or an injection — the ceiling is
|
|
19
|
+
// the transport's, not perk's. (contracts.md §8.35)
|
|
20
|
+
//
|
|
21
|
+
// The module has exactly two concerns: the routing-token FENCE above (`isRoutingToken` +
|
|
22
|
+
// `renderRoutingToken`) and the fixed ORCHESTRATION-KEY format (`orchestrationKey`). The
|
|
23
|
+
// vocabulary split (contracts.md §8.35): the SEMANTIC LANE ID is the producer-owned identity a
|
|
24
|
+
// typed outcome reports; a ROUTING TOKEN is that identity (or a pair-level token) rendered into
|
|
25
|
+
// task prose for byte-exact selection/echo; an ORCHESTRATION KEY is the opaque code-owned
|
|
26
|
+
// `runs.all` item key a producer-lane wave gives one lane — never derived from producer bytes,
|
|
27
|
+
// never surfaced as a lane identity.
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The refused character classes as one reviewable single-line statement. No `g`/`u` flags: every
|
|
31
|
+
* member is a single BMP code unit, so UTF-16 iteration is exact and astral characters (two
|
|
32
|
+
* surrogate code units, neither inside any listed range) can never match.
|
|
33
|
+
*/
|
|
34
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: refusing control characters is the point
|
|
35
|
+
const UNSAFE = /[\u0000-\u001f\u007f-\u009f\u2028\u2029"]/;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The fence predicate: `true` iff `token` is non-empty and carries none of the refused classes.
|
|
39
|
+
* Decoders and planners call this to refuse or degrade an unsafe id BEFORE a lane is planned.
|
|
40
|
+
*/
|
|
41
|
+
export function isRoutingToken(token: string): boolean {
|
|
42
|
+
return token !== "" && !UNSAFE.test(token);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The asserting identity render helper for a `laneTask` interpolation site: returns `token`
|
|
47
|
+
* UNCHANGED when it passes the fence (the fenced form of an accepted token IS the raw token —
|
|
48
|
+
* every existing task-prose pin stays valid), otherwise throws. A throw here is a programmer
|
|
49
|
+
* error — a caller composed a task from a token it never fenced upstream — in the
|
|
50
|
+
* `validateAssignments` posture of `reportWave.ts`: never an operational failure arm, and
|
|
51
|
+
* unreachable on the production path (the strict decoders refuse, the audit planner degrades).
|
|
52
|
+
*/
|
|
53
|
+
export function renderRoutingToken(token: string): string {
|
|
54
|
+
if (!isRoutingToken(token)) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`renderRoutingToken: routing token ${JSON.stringify(token)} failed the fence — refuse or degrade it upstream`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return token;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The ONE fixed orchestration-key format the producer-lane learn waves — harvest, audit, and
|
|
64
|
+
* the dream analyst tier, whose lanes are drawn from a producer-owned manifest — use for their
|
|
65
|
+
* `runs.all` item keys: `lane.<ordinal>` — opaque, code-owned, never derived from producer
|
|
66
|
+
* bytes. (Waves over a closed slug enum — the learn analyst angles, the dream reducer angles —
|
|
67
|
+
* key by the slug itself and never need it.) There is deliberately NO sanitizer: uniqueness
|
|
68
|
+
* lives in the caller's ordinal, and the semantic lane id rides `label` and the task text.
|
|
69
|
+
* `ordinal` is the caller's global 1-based counter in lane-plan order. The format is trivially inside `RUN_KEY_PATTERN`, so `validateAssignments`' run-key
|
|
70
|
+
* throw is unreachable for any planned lane. The key is never surfaced as a lane identity — it
|
|
71
|
+
* appears only in attempt receipts (`requestedKeys`, `children[*].key`) and failure `detail`s;
|
|
72
|
+
* typed outcomes join rows back to the SEMANTIC id through each flow's module-private lane plan,
|
|
73
|
+
* never by parsing keys.
|
|
74
|
+
*/
|
|
75
|
+
export function orchestrationKey(ordinal: number): string {
|
|
76
|
+
return `lane.${ordinal}`;
|
|
77
|
+
}
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
// The objective-plan factory's OPTIONAL explore step as a per-flow wave entrypoint over the
|
|
2
|
-
// shared report-wave
|
|
3
|
-
// schema was previously a shared prompt include the parent model had to hand-transcribe
|
|
4
|
-
// borrowed `subagent` call (the same prompt-drift risk the /address classify step
|
|
5
|
-
// module makes the schema and the
|
|
6
|
-
//
|
|
2
|
+
// shared report-wave module: the ONE `perk.objective-explorer` assignment as CODE. The explorer
|
|
3
|
+
// report schema was previously a shared prompt include the parent model had to hand-transcribe
|
|
4
|
+
// onto a borrowed `subagent` call (the same prompt-drift risk the /address classify step
|
|
5
|
+
// carried); this module makes the schema and the assignment/task composition module constants,
|
|
6
|
+
// delegating
|
|
7
|
+
// spawn/timeout/aggregate mechanics to `wave.run` under the `strict` completeness policy.
|
|
7
8
|
// No retry — the flow's posture on failure is "explore directly instead" (guidance-owned).
|
|
8
9
|
// `node`/`description`/`focus` are model-relayed and embedded in the code-owned task as
|
|
9
10
|
// untrusted DATA; the report content is likewise untrusted DATA, never instructions.
|
|
10
11
|
|
|
11
|
-
import {
|
|
12
|
+
import type { ReportWave, ReportWaveResult } from "./reportWave.ts";
|
|
12
13
|
|
|
13
|
-
/** The flow name — feeds `
|
|
14
|
+
/** The flow name — feeds `ReportWaveRequest.flow` AND the door's `toAttemptReceipt` call. */
|
|
14
15
|
export const OBJECTIVE_EXPLORER_FLOW = "objective-explorer";
|
|
15
16
|
|
|
16
|
-
/** The single
|
|
17
|
-
export const
|
|
17
|
+
/** The single assignment's stable key. */
|
|
18
|
+
export const EXPLORE_ASSIGNMENT_KEY = "explore";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* The explorer report schema (the workflow-level `outputSchema` — the engine injects a
|
|
21
|
-
* `structured_output` tool and fails the
|
|
22
|
+
* `structured_output` tool and fails the assignment on a missing/invalid report): closed shapes, all
|
|
22
23
|
* six root keys required. Same vocabulary as the `perk.objective-explorer` agent def's report
|
|
23
24
|
* contract (the def↔schema lockstep test).
|
|
24
25
|
*/
|
|
@@ -73,9 +74,9 @@ export interface ObjectiveExplorerWaveOptions {
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
/**
|
|
76
|
-
* Compose the single
|
|
77
|
-
* the node id + the fenced untrusted node text, plus the optional focus. The agent
|
|
78
|
-
* the node id and a description of the work to reach the child.
|
|
77
|
+
* Compose the single assignment's task text IN CODE (prompt-drift-proof: task text composed in
|
|
78
|
+
* code, never a template): the node id + the fenced untrusted node text, plus the optional focus. The agent
|
|
79
|
+
* def requires the node id and a description of the work to reach the child.
|
|
79
80
|
*/
|
|
80
81
|
export function explorerLaneTask(node: string, description: string, focus?: string): string {
|
|
81
82
|
return [
|
|
@@ -89,22 +90,21 @@ export function explorerLaneTask(node: string, description: string, focus?: stri
|
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
/**
|
|
92
|
-
* Run the objective-explorer wave: ONE fresh-context `perk.objective-explorer`
|
|
93
|
-
* code-owned task, `strict` completeness, no retry, module-default timeout. Returns the
|
|
94
|
-
* `
|
|
93
|
+
* Run the objective-explorer wave: ONE fresh-context `perk.objective-explorer` assignment over
|
|
94
|
+
* the code-owned task, `strict` completeness, no retry, module-default timeout. Returns the
|
|
95
|
+
* wave's `ReportWaveResult` unchanged — the only projection lives in the tool.
|
|
95
96
|
*/
|
|
96
97
|
export async function runObjectiveExplorerWave(
|
|
97
|
-
|
|
98
|
+
wave: ReportWave,
|
|
98
99
|
opts: ObjectiveExplorerWaveOptions,
|
|
99
|
-
): Promise<
|
|
100
|
-
return await
|
|
101
|
-
adapter,
|
|
100
|
+
): Promise<ReportWaveResult> {
|
|
101
|
+
return await wave.run(
|
|
102
102
|
{
|
|
103
103
|
flow: OBJECTIVE_EXPLORER_FLOW,
|
|
104
|
-
|
|
104
|
+
assignments: [
|
|
105
105
|
{
|
|
106
|
-
key:
|
|
107
|
-
label:
|
|
106
|
+
key: EXPLORE_ASSIGNMENT_KEY,
|
|
107
|
+
label: EXPLORE_ASSIGNMENT_KEY,
|
|
108
108
|
agent: "perk.objective-explorer",
|
|
109
109
|
phase: "objective-plan",
|
|
110
110
|
task: explorerLaneTask(opts.node, opts.description, opts.focus),
|
|
@@ -115,6 +115,6 @@ export async function runObjectiveExplorerWave(
|
|
|
115
115
|
...(opts.model !== undefined ? { model: opts.model } : {}),
|
|
116
116
|
...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),
|
|
117
117
|
},
|
|
118
|
-
opts.signal,
|
|
118
|
+
{ signal: opts.signal },
|
|
119
119
|
);
|
|
120
120
|
}
|