@mgiles/perk 3.1.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 +369 -359
- 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 -310
- package/extension/{doors/prReviewBrowser.ts → pi/v1/codeReview/browser.ts} +125 -57
- package/extension/{doors/hunkHandoff.ts → pi/v1/codeReview/checkout.ts} +12 -8
- package/extension/{doors/reviewWaveTools.ts → pi/v1/codeReview/reviewWave.ts} +169 -116
- package/extension/pi/v1/codeReview/stack.ts +606 -0
- 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 +163 -265
- 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 +128 -60
- 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/{doors → pi/v1}/lifecycleGates.ts +14 -59
- 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/pi/v1/objectivePlanning.ts +776 -0
- 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 +152 -34
- 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 -19
- package/extension/substrate/git.ts +88 -6
- package/extension/substrate/modelVisible.ts +53 -0
- package/extension/substrate/paths.ts +2 -7
- package/extension/substrate/prompts.ts +22 -0
- package/extension/substrate/registry.ts +2 -0
- package/extension/substrate/resolverLease.ts +364 -0
- package/extension/substrate/sessionData.ts +85 -152
- package/extension/substrate/toolGating.ts +279 -84
- package/extension/substrate/unifiedDiff.ts +1 -1
- package/extension/substrate/workflowState.ts +191 -166
- package/extension/substrate/worktreeResolverLock.ts +261 -0
- package/extension/surfaces/surfaces.ts +79 -27
- package/extension/waves/adversarialReviewWave.ts +103 -48
- 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 +98 -10
- 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/adapters/tombell-plan.md +4 -0
- package/prompts/contexts/objective-refinement.md +17 -0
- package/prompts/contexts/plan-authoring.md +6 -5
- package/prompts/contexts/read-only.md +1 -1
- package/prompts/stages/conflict-resolution-continuation.md +9 -0
- package/prompts/stages/conflict-resolution.md +4 -4
- package/prompts/stages/objective-author/adopt.md +1 -1
- package/prompts/stages/objective-author/file.md +1 -1
- package/prompts/stages/objective-author/seed.md +1 -1
- 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 +7 -0
- 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/cold.md +1 -0
- package/prompts/stages/stack-review-browser/stack.md +22 -0
- package/shared/README.md +8 -3
- package/shared/bindings.yaml +6 -3
- package/shared/contracts.md +4499 -2147
- package/shared/fixtures/issues-table.json +130 -0
- package/shared/registry.yaml +29 -1
- package/shared/schemas/outputs/objective-node-engagement.schema.json +318 -0
- package/shared/schemas/outputs/objective-stack-status.schema.json +178 -2
- package/shared/schemas/outputs/pr-ready.schema.json +110 -2
- 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 -475
- package/extension/doors/learn.ts +0 -655
- package/extension/doors/objectiveStack.ts +0 -1143
- package/extension/doors/prReviewDynamic.ts +0 -276
- package/extension/doors/ready.ts +0 -87
- package/extension/doors/submit.ts +0 -347
- package/extension/doors/submitPrReview.ts +0 -408
- 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/objectivePlan.ts +0 -975
- package/extension/factories/objectiveSave.ts +0 -363
- 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
- package/shared/contracts-history.md +0 -605
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
// Session-lifecycle gates (the interior safety primitive)
|
|
2
|
-
// session_before_switch / session_before_fork that returns
|
|
3
|
-
// workflow* has uncommitted changes — so a stage
|
|
4
|
-
// guard-only `/implement` command that
|
|
5
|
-
//
|
|
1
|
+
// Session-lifecycle gates (the interior safety primitive), the Pi registration half. One
|
|
2
|
+
// reusable dirty-repo guard on session_before_switch / session_before_fork that returns
|
|
3
|
+
// { cancel: true } when an *active perk workflow* has uncommitted changes — so a stage
|
|
4
|
+
// transition never silently orphans work. Plus the guard-only `/implement` command that
|
|
5
|
+
// *enforces* the implement stage's `warm: false` legality (the plan→implement jump requires
|
|
6
|
+
// fresh context; that is the cold door `perk implement`). The Pi-free policy these hooks apply
|
|
7
|
+
// (`implementHandoffPrompt`, `planningStageRefusal`) lives in `session/lifecycleGates.ts`.
|
|
6
8
|
//
|
|
7
9
|
// pi.on("session_before_fork"/"...switch") handlers are fired by extensionRunner.emit({type,...})
|
|
8
10
|
// and their { cancel } result round-trips; the handler's
|
|
@@ -13,25 +15,17 @@ import type {
|
|
|
13
15
|
ExtensionCommandContext,
|
|
14
16
|
ExtensionContext,
|
|
15
17
|
} from "@earendil-works/pi-coding-agent";
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { branchOf, rebuildWorkflowState } from "
|
|
20
|
-
import { report } from "
|
|
18
|
+
import { implementHandoffPrompt } from "../../session/lifecycleGates.ts";
|
|
19
|
+
import type { PlanRef } from "../../substrate/cache.ts";
|
|
20
|
+
import { registerPerkCommand } from "../../substrate/command.ts";
|
|
21
|
+
import { branchOf, rebuildWorkflowState } from "../../substrate/workflowState.ts";
|
|
22
|
+
import { report } from "../../surfaces/report.ts";
|
|
21
23
|
|
|
22
24
|
const DIRTY_MESSAGE = "uncommitted changes — commit or stash before switching/forking this stage.";
|
|
23
25
|
const HANDOFF_DIRTY_MESSAGE =
|
|
24
26
|
"uncommitted changes — commit before a fresh-context /implement handoff (the plan is the " +
|
|
25
27
|
"only artifact that crosses the boundary).";
|
|
26
28
|
|
|
27
|
-
/**
|
|
28
|
-
* Pure gate policy: cancel a transition only inside an active perk workflow whose tree is
|
|
29
|
-
* dirty. Kept separate from the `pi.exec` effect so the matrix is unit-testable offline.
|
|
30
|
-
*/
|
|
31
|
-
export function gateDecision(inputs: { active: boolean; dirty: boolean }): { cancel: boolean } {
|
|
32
|
-
return { cancel: inputs.active && inputs.dirty };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
29
|
/** True when this session is linked to a plan (a perk workflow is in progress). */
|
|
36
30
|
function workflowActive(ctx: ExtensionContext): boolean {
|
|
37
31
|
return rebuildWorkflowState(branchOf(ctx)).active_plan_ref != null;
|
|
@@ -46,53 +40,14 @@ async function guardTransition(
|
|
|
46
40
|
pi: ExtensionAPI,
|
|
47
41
|
ctx: ExtensionContext,
|
|
48
42
|
): Promise<{ cancel: true } | undefined> {
|
|
49
|
-
|
|
50
|
-
if (!active) return undefined; // perk does not interfere with non-perk transitions
|
|
43
|
+
if (!workflowActive(ctx)) return undefined; // perk does not interfere with non-perk transitions
|
|
51
44
|
const res = await pi.exec("git", ["status", "--porcelain"], { cwd: ctx.cwd });
|
|
52
45
|
const dirty = res.code === 0 && res.stdout.trim().length > 0;
|
|
53
|
-
if (!
|
|
46
|
+
if (!dirty) return undefined;
|
|
54
47
|
report(ctx, "lifecycle", "warning", DIRTY_MESSAGE); // fail-safe-headless: loud, still cancels
|
|
55
48
|
return { cancel: true };
|
|
56
49
|
}
|
|
57
50
|
|
|
58
|
-
/**
|
|
59
|
-
* The per-backend plan-read instruction — the prompt SSOT for "how do I read the saved
|
|
60
|
-
* plan". Byte-identical to `perk/run/launch.py::_plan_read_instruction` (the Python twin); drift in
|
|
61
|
-
* either plane fails the paired parity suites. `github` reads via `gh`; `linear` points at the
|
|
62
|
-
* pi-mono-linear tools with an `open <url>` fallback; any other provider falls back to opening
|
|
63
|
-
* the url.
|
|
64
|
-
*
|
|
65
|
-
* The wording now lives in the canonical templates `prompts/common/plan-read/*.md`, rendered
|
|
66
|
-
* identically by both planes via the shared render seam (contracts.md §8.31); branching stays in
|
|
67
|
-
* code — only the arm chosen and the vars passed differ. Golden-fixture parity (the three
|
|
68
|
-
* `plan-read-*` cases) plus a thin per-arm selection test replace the dedicated substring parity.
|
|
69
|
-
*/
|
|
70
|
-
export function planReadInstruction(provider: string, prId: string, url: string): string {
|
|
71
|
-
if (provider === "github") return render("common/plan-read/github.md", { pr_id: prId, url });
|
|
72
|
-
if (provider === "linear") return render("common/plan-read/linear.md", { pr_id: prId, url });
|
|
73
|
-
return render("common/plan-read/other.md", { pr_id: prId, url });
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* The plan-read priming seed for a fresh implement session. The in-session twin of
|
|
78
|
-
* `perk/run/launch.py`'s `_implement_prompt`: carry the plan FORWARD (read it from its canonical
|
|
79
|
-
* source), never summarize it — the plan is the only artifact that crosses the boundary.
|
|
80
|
-
*
|
|
81
|
-
* The wording lives in the canonical template `prompts/stages/implement.md`, rendered by the shared
|
|
82
|
-
* seam (contracts.md §8.31); branching stays in code — only the `read_cmd` var differs. This warm
|
|
83
|
-
* handoff is now byte-identical to the cold/worker primer, so it carries the same "Progress
|
|
84
|
-
* tracking:" tail (the prior shorter near-copy omission is removed).
|
|
85
|
-
*/
|
|
86
|
-
export function implementHandoffPrompt(ref: PlanRef): string {
|
|
87
|
-
const readCmd = planReadInstruction(ref.provider, String(ref.pr_id), ref.url);
|
|
88
|
-
return render("stages/implement.md", {
|
|
89
|
-
provider: ref.provider,
|
|
90
|
-
pr_id: String(ref.pr_id),
|
|
91
|
-
url: ref.url,
|
|
92
|
-
read_cmd: readCmd,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
51
|
/**
|
|
97
52
|
* The warm `/implement`. It still NEVER performs the cross-worktree plan→implement
|
|
98
53
|
* transition (that is structurally the Python cold door's job — no extension session API can change
|
|
@@ -22,9 +22,11 @@
|
|
|
22
22
|
// best-effort and never throw (logged-not-thrown).
|
|
23
23
|
|
|
24
24
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
25
|
+
import { openBranchWorkflowSession } from "../../session/branchWorkflowSession.ts";
|
|
26
|
+
import { type ColdJson, objectField, runColdDoor, stringField } from "../../substrate/coldDoor.ts";
|
|
27
|
+
import { registerPerkCommand } from "../../substrate/command.ts";
|
|
28
|
+
import { loadPerkConfig } from "../../substrate/config.ts";
|
|
29
|
+
import { type BranchEntry, branchOf, WORKFLOW_STATE_TYPE } from "../../substrate/workflowState.ts";
|
|
28
30
|
import {
|
|
29
31
|
formatBudgetLine,
|
|
30
32
|
MARK_OBJECTIVE,
|
|
@@ -32,7 +34,7 @@ import {
|
|
|
32
34
|
type PerkStatusHandle,
|
|
33
35
|
registerTranscriptRenderer,
|
|
34
36
|
report,
|
|
35
|
-
} from "
|
|
37
|
+
} from "../../surfaces/surfaces.ts";
|
|
36
38
|
|
|
37
39
|
/** The dedicated budget/activation session entry type (kept off `perk:workflow-state`). */
|
|
38
40
|
export const OBJECTIVE_BUDGET_TYPE = "perk:objective-budget";
|
|
@@ -41,10 +43,7 @@ export const OBJECTIVE_BUDGET_TYPE = "perk:objective-budget";
|
|
|
41
43
|
export const DEFAULT_COMPACT_THRESHOLD = 0.8;
|
|
42
44
|
|
|
43
45
|
/** A branch entry as seen by the budget scan: a custom marker OR an assistant message. */
|
|
44
|
-
interface ScanEntry {
|
|
45
|
-
type: string;
|
|
46
|
-
customType?: string;
|
|
47
|
-
data?: { objective_id?: string; activated_at?: string };
|
|
46
|
+
interface ScanEntry extends BranchEntry {
|
|
48
47
|
message?: { role?: string; usage?: { input?: number; output?: number } };
|
|
49
48
|
}
|
|
50
49
|
|
|
@@ -108,7 +107,7 @@ export function rebuildBudget(branch: readonly ScanEntry[], now: number): Budget
|
|
|
108
107
|
|
|
109
108
|
/** True when context usage has crossed the compaction threshold (pure; tolerant of unknowns). */
|
|
110
109
|
export function shouldCompact(
|
|
111
|
-
usage: { percent: number | null
|
|
110
|
+
usage: { percent: number | null } | undefined,
|
|
112
111
|
threshold: number,
|
|
113
112
|
): boolean {
|
|
114
113
|
if (!usage || usage.percent === null) return false;
|
|
@@ -118,16 +117,14 @@ export function shouldCompact(
|
|
|
118
117
|
// --- the controller -----------------------------------------------------------------------------
|
|
119
118
|
|
|
120
119
|
function scanBranchOf(ctx: ExtensionContext): ScanEntry[] {
|
|
121
|
-
|
|
120
|
+
// `branchOf` owns the typed accessor; the scan only ADDS the optional assistant-message
|
|
121
|
+
// shape, so no round-trip through `unknown` is needed (field presence is checked locally).
|
|
122
|
+
return branchOf(ctx) as ScanEntry[];
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return state.active_objective ?? null;
|
|
128
|
-
} catch {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
125
|
+
/** The rebuilt `active_objective`, read through the session seam (fail-open null). */
|
|
126
|
+
function activeObjective(pi: ExtensionAPI, ctx: ExtensionContext): string | null {
|
|
127
|
+
return openBranchWorkflowSession(pi, ctx).activeObjective();
|
|
131
128
|
}
|
|
132
129
|
|
|
133
130
|
/**
|
|
@@ -135,10 +132,10 @@ function activeObjective(ctx: ExtensionContext): string | null {
|
|
|
135
132
|
* widget is retired; the value carries id + tokens + elapsed). Headless-safe:
|
|
136
133
|
* the handle no-ops without UI.
|
|
137
134
|
*/
|
|
138
|
-
function renderStatus(ctx: ExtensionContext, status: PerkStatusHandle): void {
|
|
135
|
+
function renderStatus(pi: ExtensionAPI, ctx: ExtensionContext, status: PerkStatusHandle): void {
|
|
139
136
|
if (!ctx.hasUI) return;
|
|
140
137
|
try {
|
|
141
|
-
const active = activeObjective(ctx);
|
|
138
|
+
const active = activeObjective(pi, ctx);
|
|
142
139
|
if (active === null) {
|
|
143
140
|
status.set(ctx, undefined);
|
|
144
141
|
return;
|
|
@@ -164,7 +161,7 @@ function objectiveCommand(
|
|
|
164
161
|
const arg = args.trim();
|
|
165
162
|
try {
|
|
166
163
|
if (arg === "") {
|
|
167
|
-
const active = activeObjective(ctx);
|
|
164
|
+
const active = activeObjective(pi, ctx);
|
|
168
165
|
const budget = rebuildBudget(scanBranchOf(ctx), Date.now());
|
|
169
166
|
const message =
|
|
170
167
|
active === null
|
|
@@ -176,18 +173,20 @@ function objectiveCommand(
|
|
|
176
173
|
|
|
177
174
|
if (arg === "clear") {
|
|
178
175
|
pi.appendEntry(WORKFLOW_STATE_TYPE, { active_objective: null });
|
|
179
|
-
renderStatus(ctx, status);
|
|
176
|
+
renderStatus(pi, ctx, status);
|
|
180
177
|
report(ctx, "objective", "info", "cleared the active objective.");
|
|
181
178
|
return;
|
|
182
179
|
}
|
|
183
180
|
|
|
184
|
-
// Activate: set the LWW field + seed a fresh budget activation marker.
|
|
181
|
+
// Activate: set the LWW field + seed a fresh budget activation marker. This command path's
|
|
182
|
+
// `active_objective` write is a raw LWW append WITHOUT the session seam's strict read-back
|
|
183
|
+
// (the seam path in `saveObjective` verifies; this one stays best-effort — stated honestly).
|
|
185
184
|
pi.appendEntry(WORKFLOW_STATE_TYPE, { active_objective: arg });
|
|
186
185
|
pi.appendEntry(OBJECTIVE_BUDGET_TYPE, {
|
|
187
186
|
objective_id: arg,
|
|
188
187
|
activated_at: new Date().toISOString(),
|
|
189
188
|
});
|
|
190
|
-
renderStatus(ctx, status);
|
|
189
|
+
renderStatus(pi, ctx, status);
|
|
191
190
|
report(ctx, "objective", "info", `activated objective ${arg} (budget tracking started).`);
|
|
192
191
|
} catch (error) {
|
|
193
192
|
reportError(ctx, `command failed: ${error}`);
|
|
@@ -195,37 +194,58 @@ function objectiveCommand(
|
|
|
195
194
|
}
|
|
196
195
|
|
|
197
196
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
197
|
+
* Fetch the objective's URL via `perk objective show <id> --json` (reading `objective.url`).
|
|
198
|
+
* Lenient: returns "" on any failure / missing url — `runColdDoor` is a soft-result seam and
|
|
199
|
+
* never throws (the seed prompt's step-1 `perk objective show <id>` step surfaces the URL
|
|
200
|
+
* anyway). Only called for the linear backend (github needs no clause → no fetch). Exported for
|
|
201
|
+
* the warm drives that compose the same backend-aware read clause (the ready-time reconcile
|
|
202
|
+
* drive in `pi/v1/delivery/ready.ts`).
|
|
203
|
+
*/
|
|
204
|
+
export async function fetchObjectiveUrl(
|
|
205
|
+
pi: ExtensionAPI,
|
|
206
|
+
ctx: ExtensionContext,
|
|
207
|
+
objectiveId: string,
|
|
208
|
+
): Promise<string> {
|
|
209
|
+
const r = await runColdDoor<string>(pi, ctx, ["objective", "show", objectiveId, "--json"], {
|
|
210
|
+
label: "perk objective show",
|
|
211
|
+
decode: (payload: ColdJson) =>
|
|
212
|
+
stringField(objectField(payload, "objective") ?? {}, "url") ?? "",
|
|
213
|
+
});
|
|
214
|
+
return r.ok ? r.data : "";
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Install the objective substrate bindings: `/objective` command, budget accounting
|
|
219
|
+
* (session_start / session_tree / agent_settled), and threshold compaction (turn_end). All
|
|
220
|
+
* inert when no objective is active; never throws.
|
|
201
221
|
*/
|
|
202
|
-
export function
|
|
222
|
+
export function installObjectiveBindings(pi: ExtensionAPI, status: PerkStatusHandle): void {
|
|
203
223
|
// Transcript marker for `perk:objective-budget` activations (audit §2.3): renderer body in
|
|
204
224
|
// surfaces.ts, registration = wiring, feature-detect inside the seam (pre-0.80.4 hosts stay
|
|
205
225
|
// inert). Also covers objectiveSave.ts's appends — registration is per entry TYPE.
|
|
206
226
|
registerTranscriptRenderer(pi, OBJECTIVE_BUDGET_TYPE, objectiveBudgetEntryRenderer);
|
|
207
227
|
|
|
208
228
|
pi.on("session_start", async (_event, ctx) => {
|
|
209
|
-
renderStatus(ctx, status);
|
|
229
|
+
renderStatus(pi, ctx, status);
|
|
210
230
|
});
|
|
211
231
|
|
|
212
232
|
pi.on("session_tree", async (_event, ctx) => {
|
|
213
|
-
renderStatus(ctx, status);
|
|
233
|
+
renderStatus(pi, ctx, status);
|
|
214
234
|
});
|
|
215
235
|
|
|
216
236
|
pi.on("agent_settled", async (_event, ctx) => {
|
|
217
237
|
// Recompute the budget after each settled run (stateless rebuild from the branch).
|
|
218
|
-
renderStatus(ctx, status);
|
|
238
|
+
renderStatus(pi, ctx, status);
|
|
219
239
|
});
|
|
220
240
|
|
|
221
241
|
pi.on("turn_end", async (_event, ctx) => {
|
|
222
242
|
try {
|
|
223
|
-
if (activeObjective(ctx) === null) return; // inert unless an objective is active
|
|
243
|
+
if (activeObjective(pi, ctx) === null) return; // inert unless an objective is active
|
|
224
244
|
const threshold =
|
|
225
245
|
loadPerkConfig(ctx.cwd).objectiveCompactThreshold ?? DEFAULT_COMPACT_THRESHOLD;
|
|
226
246
|
const usage = ctx.getContextUsage();
|
|
227
247
|
if (!shouldCompact(usage, threshold)) return;
|
|
228
|
-
const active = activeObjective(ctx);
|
|
248
|
+
const active = activeObjective(pi, ctx);
|
|
229
249
|
ctx.compact({
|
|
230
250
|
customInstructions:
|
|
231
251
|
`Preserve the active perk objective (${active}) and its budget context. ` +
|
|
@@ -234,7 +254,7 @@ export function registerObjective(pi: ExtensionAPI, status: PerkStatusHandle): v
|
|
|
234
254
|
console.error(`perk: objective compaction failed — ${error}`);
|
|
235
255
|
},
|
|
236
256
|
onComplete: () => {
|
|
237
|
-
renderStatus(ctx, status);
|
|
257
|
+
renderStatus(pi, ctx, status);
|
|
238
258
|
},
|
|
239
259
|
});
|
|
240
260
|
} catch (error) {
|