@abelxiaoxing/cadence 1.8.4 → 2.0.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 +324 -117
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/agents/design-explorer.md +14 -18
- package/agents/diagnosis-worker.md +14 -14
- package/agents/implementation-worker.md +16 -17
- package/config/workflow-task.example.json +7 -0
- package/docs/remote-access.md +60 -0
- package/docs/reuse-and-distribution.md +44 -0
- package/docs/service-management.md +108 -0
- package/licenses/bundled-web.txt +6245 -0
- package/licenses/pi-web-ui-MIT.txt +21 -0
- package/licenses/pi-web-ui-reuse.md +22 -0
- package/package.json +92 -20
- package/src/index.ts +21 -1802
- package/src/native-build.ts +6 -0
- package/src/native-client.ts +103 -0
- package/src/native-export.ts +58 -0
- package/src/native-history.ts +359 -0
- package/src/operator-cli.mjs +453 -1937
- package/src/plan-proposal.ts +87 -0
- package/src/sdk-worker.mjs +549 -0
- package/src/service/contracts/active.ts +82 -0
- package/src/service/contracts/budget.ts +148 -0
- package/src/service/contracts/build.ts +67 -0
- package/src/service/contracts/capture.ts +48 -0
- package/src/service/contracts/core.ts +33 -0
- package/src/service/contracts/current.ts +261 -0
- package/src/service/contracts/domain.ts +68 -0
- package/src/service/contracts/history.ts +2245 -0
- package/src/service/contracts/models.ts +139 -0
- package/src/service/coordination.ts +862 -0
- package/src/service/private-files.ts +204 -0
- package/src/service-cli.mjs +5592 -0
- package/src/service-client.ts +747 -0
- package/src/service-contract.ts +245 -0
- package/src/service-host.mjs +22382 -0
- package/src/service-http-client.ts +425 -0
- package/src/service-wire.ts +372 -0
- package/src/task-shell.ts +1045 -0
- package/src/task-store.ts +479 -0
- package/src/task-types.ts +184 -0
- package/src/verification-diagnostics.ts +29 -72
- package/src/worker-capture.mjs +1197 -0
- package/src/worker-public.mjs +1753 -0
- package/src/worker-redaction.mjs +608 -0
- package/src/worker-tools.mjs +202 -0
- package/web/dist/assets/KaTeX_AMS-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_AMS-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold.woff +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Bold.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-Bold.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Bold.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic.woff +0 -0
- package/web/dist/assets/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Italic.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-Italic.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Italic.woff2 +0 -0
- package/web/dist/assets/KaTeX_Main-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Main-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Main-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic.woff +0 -0
- package/web/dist/assets/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/web/dist/assets/KaTeX_Math-Italic.ttf +0 -0
- package/web/dist/assets/KaTeX_Math-Italic.woff +0 -0
- package/web/dist/assets/KaTeX_Math-Italic.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Script-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Script-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Script-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Size1-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Size2-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Size3-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Size4-Regular.woff2 +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular.ttf +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular.woff +0 -0
- package/web/dist/assets/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/web/dist/assets/cadence.css +2 -0
- package/web/dist/assets/cadence.js +7 -0
- package/web/dist/assets/markdown.js +296 -0
- package/web/dist/assets/react.js +9 -0
- package/web/dist/assets/rolldown-runtime.js +1 -0
- package/web/dist/assets/timeline-client.js +41 -0
- package/web/dist/assets/vis-timeline.css +1274 -0
- package/web/dist/assets/vis-timeline.js +32 -0
- package/web/dist/index.html +18 -0
- package/web/dist/manifest.json +505 -0
- package/config/plan-draft.example.json +0 -125
- package/config/plan-draft.multiple-tasks.example.json +0 -375
- package/config/plan-draft.quick.example.json +0 -78
- package/prompts/abel-design.md +0 -382
- package/prompts/abel-diagnose.md +0 -71
- package/prompts/abel-implement.md +0 -284
- package/prompts/abel-init.md +0 -52
- package/src/activation.ts +0 -44
- package/src/activity-contracts.ts +0 -82
- package/src/agent-registry.ts +0 -53
- package/src/apply-transaction.ts +0 -1437
- package/src/artifact-store.ts +0 -213
- package/src/build-windows-job.mjs +0 -95
- package/src/candidate-artifact.ts +0 -143
- package/src/candidate-context.ts +0 -289
- package/src/candidate-patch.ts +0 -271
- package/src/candidate-workspace.ts +0 -80
- package/src/canonical.ts +0 -16
- package/src/change-contract-schema.ts +0 -191
- package/src/change-contract.ts +0 -324
- package/src/change-verification.ts +0 -1617
- package/src/contracts.ts +0 -2466
- package/src/control-contracts.ts +0 -382
- package/src/delivery-compiler.ts +0 -1805
- package/src/delivery-revision.ts +0 -42
- package/src/design-control.ts +0 -1299
- package/src/design-diagnostics.ts +0 -181
- package/src/design-journal.ts +0 -1773
- package/src/directory-sync.ts +0 -36
- package/src/durable-contracts.ts +0 -227
- package/src/durable-workflow.ts +0 -822
- package/src/evidence-draft.ts +0 -122
- package/src/execution-profile.ts +0 -64
- package/src/execution-retention.ts +0 -77
- package/src/file-snapshot.ts +0 -163
- package/src/implement-continuation.ts +0 -559
- package/src/implement-graph.ts +0 -690
- package/src/implement-plan.ts +0 -72
- package/src/install-config.d.mts +0 -6
- package/src/isolation-backend.ts +0 -353
- package/src/model-source.ts +0 -22
- package/src/openspec-cli.ts +0 -494
- package/src/operator-cli-entry.mjs +0 -24
- package/src/operator-tools.ts +0 -309
- package/src/package-candidate-process.ts +0 -206
- package/src/package-candidate.ts +0 -24
- package/src/package-delivery.ts +0 -486
- package/src/package-state.ts +0 -320
- package/src/package-verification.ts +0 -635
- package/src/package-workflow.ts +0 -305
- package/src/packet-runtime.ts +0 -459
- package/src/phase-execution.ts +0 -2163
- package/src/pi-adapter.ts +0 -19
- package/src/plan-draft-summary.ts +0 -527
- package/src/plan-draft.ts +0 -502
- package/src/remove-path.ts +0 -32
- package/src/run-state.ts +0 -420
- package/src/run-store.ts +0 -1179
- package/src/safe-path.ts +0 -77
- package/src/scoped-grep-worker.mjs +0 -23
- package/src/scoped-tools.ts +0 -775
- package/src/single-task-draft.ts +0 -142
- package/src/sqlite-schema.ts +0 -171
- package/src/state-root.ts +0 -215
- package/src/storage-schema.ts +0 -360
- package/src/subagent-activity.ts +0 -1417
- package/src/subagent-process.ts +0 -476
- package/src/task-ledger.ts +0 -1338
- package/src/transport-budget.ts +0 -50
- package/src/verification-capability.ts +0 -1191
- package/src/verification-environment-io.ts +0 -389
- package/src/verification-environment.ts +0 -162
- package/src/verification-identity.ts +0 -77
- package/src/verification-scan.ts +0 -64
- package/src/windows-job-backend.ts +0 -419
- package/src/windows-job.c +0 -149
- package/src/workflow-engine.ts +0 -28
- package/src/workflow-policy.ts +0 -1670
- package/src/workflow-recovery-policy.ts +0 -97
- package/src/workflow-scheduling.ts +0 -105
- package/src/workflow-state-machine.ts +0 -4341
- package/src/workflow-status.ts +0 -576
- package/src/workspace-io-entry.mjs +0 -76
- package/src/workspace-io-worker.mjs +0 -1312
- package/src/workspace-io.ts +0 -89
- package/src/workspace-store.ts +0 -634
package/src/index.ts
CHANGED
|
@@ -1,1810 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
projectDesignDiagnostic,
|
|
5
|
-
type SafeDesignDiagnostic,
|
|
6
|
-
} from "./design-diagnostics.ts";
|
|
7
|
-
import { PackageStateError } from "./package-state.ts";
|
|
8
|
-
import { openPackageWorkflowService } from "./package-workflow.ts";
|
|
9
|
-
import { packageContext } from "./pi-adapter.ts";
|
|
10
|
-
|
|
11
|
-
export {
|
|
12
|
-
type PackageDeliverySourceOptions,
|
|
13
|
-
type PackageWorkflowDeliverySource,
|
|
14
|
-
packageDeliverySource,
|
|
15
|
-
} from "./package-delivery.ts";
|
|
16
|
-
export { executePackageVerification } from "./package-verification.ts";
|
|
17
|
-
|
|
18
|
-
// Private workflow orchestration extension. Registers the abel_dispatch tool
|
|
19
|
-
// on load, then keeps it inactive by default by removing only that name from
|
|
20
|
-
// the active set at session start. Eligible-stage activation is wired by the
|
|
21
|
-
// workflow routing (abel-design/implement/diagnose provenance) in the prompts
|
|
22
|
-
// integration; abel-init and ordinary prompts never activate dispatch.
|
|
23
|
-
|
|
24
|
-
import { createHash } from "node:crypto";
|
|
25
|
-
import { homedir } from "node:os";
|
|
26
|
-
import { dirname, join } from "node:path";
|
|
27
|
-
import { fileURLToPath } from "node:url";
|
|
28
|
-
import type {
|
|
29
|
-
AgentToolResult,
|
|
30
|
-
AgentToolUpdateCallback,
|
|
31
|
-
ExtensionAPI,
|
|
32
|
-
ExtensionContext,
|
|
33
|
-
Theme,
|
|
34
|
-
ToolRenderResultOptions,
|
|
35
|
-
} from "@earendil-works/pi-coding-agent";
|
|
36
|
-
import { type Activation, activateTool, deactivateTool } from "./activation.ts";
|
|
37
|
-
|
|
38
|
-
import type { DesignEvidenceResult } from "./contracts.ts";
|
|
39
|
-
import {
|
|
40
|
-
CONTROL_COMMAND_PARAMETERS,
|
|
41
|
-
canonicalizeControlCommandToolInput,
|
|
42
|
-
validateControlCommand,
|
|
43
|
-
} from "./control-contracts.ts";
|
|
44
|
-
import {
|
|
45
|
-
DeliveryValidationError,
|
|
46
|
-
DesignPlanValidationError,
|
|
47
|
-
} from "./delivery-compiler.ts";
|
|
48
|
-
import {
|
|
49
|
-
DesignControlValidationError,
|
|
50
|
-
DesignFinalizationError,
|
|
51
|
-
validateDesignControlRequest,
|
|
52
|
-
} from "./design-control.ts";
|
|
53
|
-
import {
|
|
54
|
-
ImplementContinuationDriver,
|
|
55
|
-
implementChangeFromInvocation,
|
|
56
|
-
implementPromptBindsChange,
|
|
57
|
-
} from "./implement-continuation.ts";
|
|
58
|
-
import { canonicalJson } from "./implement-graph.ts";
|
|
59
|
-
import { PACKET_ACTIONS, PacketRuntime } from "./packet-runtime.ts";
|
|
60
|
-
|
|
61
|
-
import { RunStoreFormatError, RunStoreMigrationError } from "./run-store.ts";
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
62
3
|
import { startupCardLines } from "./startup-card.ts";
|
|
63
|
-
import {
|
|
64
|
-
import {
|
|
65
|
-
ACTIVITY_DETAILS_KEY,
|
|
66
|
-
ActivityController,
|
|
67
|
-
renderActivityCall,
|
|
68
|
-
renderActivityResult,
|
|
69
|
-
type WorkflowActivityUpdate,
|
|
70
|
-
} from "./subagent-activity.ts";
|
|
71
|
-
|
|
72
|
-
export {
|
|
73
|
-
inspectOpenSpecDelivery,
|
|
74
|
-
type OpenSpecDeliveryInspection,
|
|
75
|
-
} from "./openspec-cli.ts";
|
|
76
|
-
|
|
77
|
-
export const DISPATCH_TOOL = "abel_dispatch";
|
|
78
|
-
const DESIGN_PARENT_READ_TOOLS = new Set(["read", "grep", "find", "ls"]);
|
|
79
|
-
|
|
80
|
-
const PACKAGE_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
81
|
-
const ELIGIBLE_PROMPTS = [
|
|
82
|
-
"abel-design",
|
|
83
|
-
"abel-implement",
|
|
84
|
-
"abel-diagnose",
|
|
85
|
-
] as const;
|
|
86
|
-
type EligiblePrompt = (typeof ELIGIBLE_PROMPTS)[number];
|
|
87
|
-
|
|
88
|
-
const GENERIC_REQUEST_SCHEMA = {
|
|
89
|
-
type: "object",
|
|
90
|
-
description:
|
|
91
|
-
"Stage-specific request envelope for action=run. The verified Abel prompt refreshes this schema before the stage's first model turn.",
|
|
92
|
-
} as const;
|
|
93
|
-
|
|
94
|
-
const DESIGN_REQUEST_SCHEMA = {
|
|
95
|
-
type: "object",
|
|
96
|
-
description:
|
|
97
|
-
"One bounded read-only design-explorer packet. Send one sibling abel_dispatch tool call per packet; do not wrap multiple packets in requests.",
|
|
98
|
-
properties: {
|
|
99
|
-
stage: {
|
|
100
|
-
type: "string",
|
|
101
|
-
enum: ["abel-design"],
|
|
102
|
-
description: "Exact verified workflow stage.",
|
|
103
|
-
},
|
|
104
|
-
role: {
|
|
105
|
-
type: "string",
|
|
106
|
-
enum: ["design-explorer"],
|
|
107
|
-
description: "Exact package-owned read-only Agent role.",
|
|
108
|
-
},
|
|
109
|
-
runId: {
|
|
110
|
-
type: "string",
|
|
111
|
-
description: "Durable Design run identity returned by Design start.",
|
|
112
|
-
},
|
|
113
|
-
id: {
|
|
114
|
-
type: "string",
|
|
115
|
-
description:
|
|
116
|
-
"Unique packet and request identity, at most 128 characters.",
|
|
117
|
-
},
|
|
118
|
-
phase: {
|
|
119
|
-
type: "string",
|
|
120
|
-
enum: ["evidence"],
|
|
121
|
-
description: "Design exploration phase.",
|
|
122
|
-
},
|
|
123
|
-
objective: {
|
|
124
|
-
type: "string",
|
|
125
|
-
description: "Bounded evidence objective for this packet only.",
|
|
126
|
-
},
|
|
127
|
-
roots: {
|
|
128
|
-
type: "array",
|
|
129
|
-
items: { type: "string" },
|
|
130
|
-
description: 'Approved relative workspace roots, normally ["."].',
|
|
131
|
-
},
|
|
132
|
-
context: {
|
|
133
|
-
type: "object",
|
|
134
|
-
properties: {
|
|
135
|
-
agents: {
|
|
136
|
-
type: "string",
|
|
137
|
-
description: "Applicable AGENTS.md instructions for this packet.",
|
|
138
|
-
},
|
|
139
|
-
contract: {
|
|
140
|
-
type: "string",
|
|
141
|
-
description:
|
|
142
|
-
"Packet scope, retrieval, read-only, and structured-output contract.",
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
required: ["agents", "contract"],
|
|
146
|
-
additionalProperties: false,
|
|
147
|
-
},
|
|
148
|
-
declared: {
|
|
149
|
-
type: "object",
|
|
150
|
-
properties: {
|
|
151
|
-
read: {
|
|
152
|
-
type: "array",
|
|
153
|
-
items: { type: "string" },
|
|
154
|
-
description:
|
|
155
|
-
"Exact relative files or directories this packet may read.",
|
|
156
|
-
},
|
|
157
|
-
write: {
|
|
158
|
-
type: "array",
|
|
159
|
-
items: { type: "string" },
|
|
160
|
-
description: "Must be empty for design-explorer.",
|
|
161
|
-
},
|
|
162
|
-
conflicts: {
|
|
163
|
-
type: "array",
|
|
164
|
-
items: { type: "string" },
|
|
165
|
-
description:
|
|
166
|
-
"Declared conflict edges; normally empty for read-only packets.",
|
|
167
|
-
},
|
|
168
|
-
resources: {
|
|
169
|
-
type: "array",
|
|
170
|
-
items: { type: "string" },
|
|
171
|
-
description: "Declared exclusive resources; normally empty.",
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
required: ["read", "write", "conflicts", "resources"],
|
|
175
|
-
additionalProperties: false,
|
|
176
|
-
},
|
|
177
|
-
output: {
|
|
178
|
-
type: "string",
|
|
179
|
-
enum: ["evidence"],
|
|
180
|
-
description: "Exact structured child-result kind.",
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
required: [
|
|
184
|
-
"stage",
|
|
185
|
-
"role",
|
|
186
|
-
"runId",
|
|
187
|
-
"id",
|
|
188
|
-
"phase",
|
|
189
|
-
"objective",
|
|
190
|
-
"roots",
|
|
191
|
-
"context",
|
|
192
|
-
"declared",
|
|
193
|
-
"output",
|
|
194
|
-
],
|
|
195
|
-
additionalProperties: false,
|
|
196
|
-
} as const;
|
|
197
|
-
|
|
198
|
-
const DESIGN_CONTROL_REQUEST_SCHEMA = {
|
|
199
|
-
description:
|
|
200
|
-
"One closed durable Design control operation. The operation body is strict and idempotent by operationId.",
|
|
201
|
-
oneOf: [
|
|
202
|
-
...(["requirement", "change"] as const).map((field) => ({
|
|
203
|
-
type: "object" as const,
|
|
204
|
-
properties: {
|
|
205
|
-
operation: { type: "string" as const, enum: ["start"] },
|
|
206
|
-
operationId: { type: "string" as const },
|
|
207
|
-
[field]: {
|
|
208
|
-
type: "string" as const,
|
|
209
|
-
description:
|
|
210
|
-
field === "requirement"
|
|
211
|
-
? "Raw Design requirement; code derives and stores only its stable hash."
|
|
212
|
-
: "Existing canonical change name to resume or revise.",
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
required: ["operation", "operationId", field],
|
|
216
|
-
additionalProperties: false,
|
|
217
|
-
})),
|
|
218
|
-
...(["status", "validate-plan-draft"] as const).map((operation) => ({
|
|
219
|
-
type: "object" as const,
|
|
220
|
-
properties: {
|
|
221
|
-
operation: { type: "string" as const, enum: [operation] },
|
|
222
|
-
runId: { type: "string" as const },
|
|
223
|
-
},
|
|
224
|
-
required: ["operation", "runId"],
|
|
225
|
-
additionalProperties: false,
|
|
226
|
-
})),
|
|
227
|
-
{
|
|
228
|
-
type: "object",
|
|
229
|
-
properties: {
|
|
230
|
-
operation: { type: "string", enum: ["bind-change"] },
|
|
231
|
-
runId: { type: "string" },
|
|
232
|
-
operationId: { type: "string" },
|
|
233
|
-
change: { type: "string" },
|
|
234
|
-
},
|
|
235
|
-
required: ["operation", "runId", "operationId", "change"],
|
|
236
|
-
additionalProperties: false,
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
type: "object",
|
|
240
|
-
properties: {
|
|
241
|
-
operation: { type: "string", enum: ["record-decision"] },
|
|
242
|
-
runId: { type: "string" },
|
|
243
|
-
operationId: { type: "string" },
|
|
244
|
-
decisionId: { type: "string" },
|
|
245
|
-
category: { type: "string", enum: ["behavior", "technical"] },
|
|
246
|
-
contract: {
|
|
247
|
-
type: "string",
|
|
248
|
-
description:
|
|
249
|
-
"Normalized decision contract; code hashes it and never persists the text.",
|
|
250
|
-
},
|
|
251
|
-
refs: { type: "array", items: { type: "string" } },
|
|
252
|
-
},
|
|
253
|
-
required: [
|
|
254
|
-
"operation",
|
|
255
|
-
"runId",
|
|
256
|
-
"operationId",
|
|
257
|
-
"decisionId",
|
|
258
|
-
"category",
|
|
259
|
-
"contract",
|
|
260
|
-
"refs",
|
|
261
|
-
],
|
|
262
|
-
additionalProperties: false,
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
type: "object",
|
|
266
|
-
properties: {
|
|
267
|
-
operation: { type: "string", enum: ["approve-gate"] },
|
|
268
|
-
runId: { type: "string" },
|
|
269
|
-
operationId: { type: "string" },
|
|
270
|
-
gate: { type: "string", enum: ["gate-a"] },
|
|
271
|
-
contract: {
|
|
272
|
-
anyOf: [{ type: "string" }, CHANGE_CONTRACT_SCHEMA],
|
|
273
|
-
description:
|
|
274
|
-
"ChangeContract: {goal, acceptance:[{id,statement,verification}], constraints:[{id,statement}], policy:{writeRoots,dependencies,verificationModes}}. Pass the example's changeContract object, not its whole plan. Legacy prose remains readable; code hashes authority.",
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
required: ["operation", "runId", "operationId", "gate", "contract"],
|
|
278
|
-
additionalProperties: false,
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
type: "object",
|
|
282
|
-
properties: {
|
|
283
|
-
operation: { type: "string", enum: ["approve-gate"] },
|
|
284
|
-
runId: { type: "string" },
|
|
285
|
-
operationId: { type: "string" },
|
|
286
|
-
gate: { type: "string", enum: ["gate-b"] },
|
|
287
|
-
},
|
|
288
|
-
required: ["operation", "runId", "operationId", "gate"],
|
|
289
|
-
additionalProperties: false,
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
type: "object",
|
|
293
|
-
properties: {
|
|
294
|
-
operation: { type: "string", enum: ["write-artifact"] },
|
|
295
|
-
runId: { type: "string" },
|
|
296
|
-
operationId: { type: "string" },
|
|
297
|
-
path: {
|
|
298
|
-
type: "string",
|
|
299
|
-
description: "Allowed path relative to this Design change root.",
|
|
300
|
-
},
|
|
301
|
-
content: {
|
|
302
|
-
type: "string",
|
|
303
|
-
maxLength: 16 * 1024 * 1024,
|
|
304
|
-
description: `Exact bounded UTF-8 artifact content. For plan-draft.json, omit tracking, phase verificationInputs and relatedTests disposition to derive them. Read the complete example at ${join(PACKAGE_ROOT, "config/plan-draft.example.json")}.`,
|
|
305
|
-
},
|
|
306
|
-
},
|
|
307
|
-
required: ["operation", "runId", "operationId", "path", "content"],
|
|
308
|
-
additionalProperties: false,
|
|
309
|
-
},
|
|
310
|
-
{
|
|
311
|
-
type: "object",
|
|
312
|
-
properties: {
|
|
313
|
-
operation: { type: "string", enum: ["delete-artifact"] },
|
|
314
|
-
runId: { type: "string" },
|
|
315
|
-
operationId: { type: "string" },
|
|
316
|
-
path: {
|
|
317
|
-
type: "string",
|
|
318
|
-
description: "Allowed path relative to this Design change root.",
|
|
319
|
-
},
|
|
320
|
-
},
|
|
321
|
-
required: ["operation", "runId", "operationId", "path"],
|
|
322
|
-
additionalProperties: false,
|
|
323
|
-
},
|
|
324
|
-
...(["compile-plan", "finalize-delivery"] as const).map((operation) => ({
|
|
325
|
-
type: "object" as const,
|
|
326
|
-
properties: {
|
|
327
|
-
operation: { type: "string" as const, enum: [operation] },
|
|
328
|
-
runId: { type: "string" as const },
|
|
329
|
-
operationId: { type: "string" as const },
|
|
330
|
-
},
|
|
331
|
-
required: ["operation", "runId", "operationId"],
|
|
332
|
-
additionalProperties: false,
|
|
333
|
-
})),
|
|
334
|
-
],
|
|
335
|
-
} as const;
|
|
336
|
-
|
|
337
|
-
function invokedPrompt(text: string): EligiblePrompt | undefined {
|
|
338
|
-
const name = text.match(/^\/([^\s]+)(?:\s|$)/)?.[1];
|
|
339
|
-
return ELIGIBLE_PROMPTS.find((candidate) => candidate === name);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
type WorkflowPrompt = EligiblePrompt | "abel-init";
|
|
343
|
-
|
|
344
|
-
function promptMarker(name: WorkflowPrompt): string {
|
|
345
|
-
return `<!-- ABEL:PROMPT:${name} -->`;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function hasPackageProvenance(pi: ExtensionAPI, name: WorkflowPrompt): boolean {
|
|
349
|
-
const commands = pi
|
|
350
|
-
.getCommands()
|
|
351
|
-
.filter((candidate) => candidate.name === name);
|
|
352
|
-
return (
|
|
353
|
-
commands.length === 1 &&
|
|
354
|
-
commands[0]?.source === "prompt" &&
|
|
355
|
-
commands[0].sourceInfo.origin === "package" &&
|
|
356
|
-
commands[0].sourceInfo.baseDir === PACKAGE_ROOT &&
|
|
357
|
-
commands[0].sourceInfo.path === join(PACKAGE_ROOT, "prompts", `${name}.md`)
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
function hasExpandedPromptMarker(
|
|
362
|
-
prompt: string,
|
|
363
|
-
name: WorkflowPrompt,
|
|
364
|
-
): boolean {
|
|
365
|
-
const requestEnd = prompt.lastIndexOf("</abel-request>");
|
|
366
|
-
if (requestEnd < 0) return false;
|
|
367
|
-
const body = prompt.slice(requestEnd + "</abel-request>".length);
|
|
368
|
-
const marker = promptMarker(name);
|
|
369
|
-
return (
|
|
370
|
-
body.includes(marker) && body.indexOf(marker) === body.lastIndexOf(marker)
|
|
371
|
-
);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// A marker is never authority. Recognize only the expanded entrypoint shape
|
|
375
|
-
// here so quoted documentation and ordinary command discussions stay ordinary.
|
|
376
|
-
function expandedStagePrompt(text: string): EligiblePrompt | undefined {
|
|
377
|
-
return ELIGIBLE_PROMPTS.find(
|
|
378
|
-
(name) =>
|
|
379
|
-
text.startsWith(
|
|
380
|
-
`This procedure applies only when the user explicitly invokes \`/${name}\`.`,
|
|
381
|
-
) && hasExpandedPromptMarker(text, name),
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
type StageStartupError =
|
|
386
|
-
| "abel-stage-requires-idle"
|
|
387
|
-
| "abel-stage-provenance-invalid"
|
|
388
|
-
| "abel-stage-unverified-input"
|
|
389
|
-
| "abel-stage-tools-unavailable";
|
|
390
|
-
|
|
391
|
-
function startupErrorText(code: StageStartupError): string {
|
|
392
|
-
return `Cadence configuration error: ${code}. Do not execute this workflow or substitute shell/subagent tools for abel_dispatch. Ensure the Cadence extension and its package prompts are enabled, inspect host extension-load errors and tool filters, then submit the original slash command through session.prompt when idle. Do not pre-expand the template or queue it through steer/followUp. No new stage authority was granted by this rejected request.`;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function isVerifiedStageInvocation(
|
|
396
|
-
pi: ExtensionAPI,
|
|
397
|
-
activation: Activation,
|
|
398
|
-
name: EligiblePrompt,
|
|
399
|
-
prompt: string,
|
|
400
|
-
): boolean {
|
|
401
|
-
return (
|
|
402
|
-
hasPackageProvenance(pi, name) &&
|
|
403
|
-
hasExpandedPromptMarker(prompt, name) &&
|
|
404
|
-
(activation.isActive() || activation.state === "inactive")
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
function activateDispatcher(
|
|
409
|
-
pi: ExtensionAPI,
|
|
410
|
-
activation: Activation,
|
|
411
|
-
name: EligiblePrompt,
|
|
412
|
-
prompt: string,
|
|
413
|
-
): void {
|
|
414
|
-
if (!isVerifiedStageInvocation(pi, activation, name, prompt)) return;
|
|
415
|
-
if (!activation.isActive()) {
|
|
416
|
-
activation.request();
|
|
417
|
-
activation.activate();
|
|
418
|
-
}
|
|
419
|
-
const active = pi.getActiveTools();
|
|
420
|
-
if (!active.includes(DISPATCH_TOOL)) {
|
|
421
|
-
pi.setActiveTools(activateTool(active, DISPATCH_TOOL));
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
function splitUsage(result: unknown): { payload: unknown; usage?: unknown } {
|
|
426
|
-
if (typeof result !== "object" || result === null || !("usage" in result)) {
|
|
427
|
-
return { payload: result };
|
|
428
|
-
}
|
|
429
|
-
const { usage, ...payload } = result as Record<string, unknown>;
|
|
430
|
-
return { payload, usage };
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const SAFE_DESIGN_ERROR_CODE = /^[a-z0-9](?:[a-z0-9._:-]{0,126}[a-z0-9])?$/iu;
|
|
434
|
-
const MAX_DESIGN_ERROR_DIAGNOSTICS = 64;
|
|
435
|
-
|
|
436
|
-
type SafeDesignFailure = {
|
|
437
|
-
kind: "design-control-failure";
|
|
438
|
-
operation: string;
|
|
439
|
-
code: string;
|
|
440
|
-
diagnostics: readonly SafeDesignDiagnostic[];
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
type DesignFailureBoundary =
|
|
444
|
-
| "dispatch"
|
|
445
|
-
| "initialization"
|
|
446
|
-
| "execution"
|
|
447
|
-
| "serialization";
|
|
448
|
-
|
|
449
|
-
function sqliteLocked(error: unknown): boolean {
|
|
450
|
-
return (
|
|
451
|
-
typeof error === "object" &&
|
|
452
|
-
error !== null &&
|
|
453
|
-
"errcode" in error &&
|
|
454
|
-
((error as { errcode?: unknown }).errcode === 5 ||
|
|
455
|
-
(error as { errcode?: unknown }).errcode === 6)
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
function sqliteUnavailable(error: unknown): boolean {
|
|
460
|
-
if (typeof error !== "object" || error === null) return false;
|
|
461
|
-
const errcode = Number((error as { errcode?: unknown }).errcode);
|
|
462
|
-
const code = (error as { code?: unknown }).code;
|
|
463
|
-
return (
|
|
464
|
-
[8, 10, 13, 14, 23, 24].includes(errcode) ||
|
|
465
|
-
(typeof code === "string" &&
|
|
466
|
-
[
|
|
467
|
-
"EACCES",
|
|
468
|
-
"EBUSY",
|
|
469
|
-
"EMFILE",
|
|
470
|
-
"ENFILE",
|
|
471
|
-
"ENOSPC",
|
|
472
|
-
"EPERM",
|
|
473
|
-
"EROFS",
|
|
474
|
-
].includes(code))
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
function classifyDesignFailure(
|
|
479
|
-
error: unknown,
|
|
480
|
-
operation: string,
|
|
481
|
-
boundary: DesignFailureBoundary,
|
|
482
|
-
): {
|
|
483
|
-
code: string;
|
|
484
|
-
category: string;
|
|
485
|
-
retryable: boolean;
|
|
486
|
-
field?: string;
|
|
487
|
-
} {
|
|
488
|
-
const message = error instanceof Error ? error.message : "";
|
|
489
|
-
if (error instanceof PackageStateError) {
|
|
490
|
-
return {
|
|
491
|
-
code: error.code,
|
|
492
|
-
category: "storage",
|
|
493
|
-
retryable: false,
|
|
494
|
-
field: "packageVersion",
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
if (error instanceof DesignControlValidationError) {
|
|
498
|
-
return {
|
|
499
|
-
code: "invalid-design-control-request",
|
|
500
|
-
category: "validation",
|
|
501
|
-
retryable: false,
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
if (error instanceof DesignPlanValidationError) {
|
|
505
|
-
return {
|
|
506
|
-
code: "design-plan-validation-invalid",
|
|
507
|
-
category: "validation",
|
|
508
|
-
retryable: false,
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
if (
|
|
512
|
-
error instanceof RunStoreMigrationError ||
|
|
513
|
-
error instanceof RunStoreFormatError
|
|
514
|
-
) {
|
|
515
|
-
return {
|
|
516
|
-
code: "design-store-migration-failed",
|
|
517
|
-
category: "storage",
|
|
518
|
-
retryable: false,
|
|
519
|
-
field: "schemaVersion",
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
if (sqliteLocked(error)) {
|
|
523
|
-
return {
|
|
524
|
-
code: "design-store-locked",
|
|
525
|
-
category: "storage",
|
|
526
|
-
retryable: true,
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
if (sqliteUnavailable(error)) {
|
|
530
|
-
return {
|
|
531
|
-
code: "design-store-unavailable",
|
|
532
|
-
category: "storage",
|
|
533
|
-
retryable: false,
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
if (error instanceof StateRootError) {
|
|
537
|
-
return {
|
|
538
|
-
code: "design-store-unavailable",
|
|
539
|
-
category: "storage",
|
|
540
|
-
retryable: false,
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
if (
|
|
544
|
-
message === "design-operation-conflict" ||
|
|
545
|
-
message === "operation-id-conflict"
|
|
546
|
-
) {
|
|
547
|
-
return {
|
|
548
|
-
code: "design-operation-conflict",
|
|
549
|
-
category: "control",
|
|
550
|
-
retryable: false,
|
|
551
|
-
field: "operationId",
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
if (message === "stage-control-mismatch") {
|
|
555
|
-
return {
|
|
556
|
-
code: "design-stage-inactive",
|
|
557
|
-
category: "stage",
|
|
558
|
-
retryable: false,
|
|
559
|
-
};
|
|
560
|
-
}
|
|
561
|
-
if (message === "invalid-design-control-request") {
|
|
562
|
-
return {
|
|
563
|
-
code: "invalid-design-control-request",
|
|
564
|
-
category: "validation",
|
|
565
|
-
retryable: false,
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
|
-
if (boundary === "serialization") {
|
|
569
|
-
return {
|
|
570
|
-
code: "design-receipt-serialization-failed",
|
|
571
|
-
category: "control",
|
|
572
|
-
retryable: false,
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
if (boundary === "initialization") {
|
|
576
|
-
return {
|
|
577
|
-
code: "design-store-unavailable",
|
|
578
|
-
category: "storage",
|
|
579
|
-
retryable: false,
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
if (
|
|
583
|
-
error instanceof DesignFinalizationError ||
|
|
584
|
-
error instanceof DeliveryValidationError ||
|
|
585
|
-
(message.startsWith("design-") && SAFE_DESIGN_ERROR_CODE.test(message))
|
|
586
|
-
) {
|
|
587
|
-
return { code: message, category: "control", retryable: false };
|
|
588
|
-
}
|
|
589
|
-
if (operation === "start") {
|
|
590
|
-
return {
|
|
591
|
-
code: "design-run-create-failed",
|
|
592
|
-
category: "storage",
|
|
593
|
-
retryable: false,
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
return {
|
|
597
|
-
code: "design-control-internal",
|
|
598
|
-
category: "control",
|
|
599
|
-
retryable: false,
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
function safeDesignFailure(
|
|
604
|
-
error: unknown,
|
|
605
|
-
operation: string,
|
|
606
|
-
boundary: DesignFailureBoundary = "dispatch",
|
|
607
|
-
): SafeDesignFailure {
|
|
608
|
-
const classified = classifyDesignFailure(error, operation, boundary);
|
|
609
|
-
const rawDiagnostics: readonly unknown[] =
|
|
610
|
-
error instanceof DesignFinalizationError ||
|
|
611
|
-
error instanceof DesignPlanValidationError ||
|
|
612
|
-
error instanceof DeliveryValidationError
|
|
613
|
-
? error instanceof DesignFinalizationError
|
|
614
|
-
? [
|
|
615
|
-
...error.diagnostics.filter(
|
|
616
|
-
(code) =>
|
|
617
|
-
!error.openSpecDiagnostic ||
|
|
618
|
-
code !== "design-openspec-unavailable",
|
|
619
|
-
),
|
|
620
|
-
...(error.openSpecDiagnostic ? [error.openSpecDiagnostic] : []),
|
|
621
|
-
]
|
|
622
|
-
: error.diagnostics
|
|
623
|
-
: [];
|
|
624
|
-
const diagnostics = rawDiagnostics
|
|
625
|
-
.flatMap((candidate) => {
|
|
626
|
-
const diagnostic = projectDesignDiagnostic(candidate);
|
|
627
|
-
return diagnostic ? [diagnostic] : [];
|
|
628
|
-
})
|
|
629
|
-
.sort((left, right) =>
|
|
630
|
-
compareCanonicalStrings(canonicalJson(left), canonicalJson(right)),
|
|
631
|
-
)
|
|
632
|
-
.filter(
|
|
633
|
-
(candidate, index, values) =>
|
|
634
|
-
index === 0 ||
|
|
635
|
-
canonicalJson(candidate) !== canonicalJson(values[index - 1]),
|
|
636
|
-
)
|
|
637
|
-
.slice(0, MAX_DESIGN_ERROR_DIAGNOSTICS);
|
|
638
|
-
if (diagnostics.length === 0) {
|
|
639
|
-
diagnostics.push({
|
|
640
|
-
code: classified.code,
|
|
641
|
-
category: classified.category,
|
|
642
|
-
retryable: classified.retryable,
|
|
643
|
-
...(classified.field ? { field: classified.field } : {}),
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
return {
|
|
647
|
-
kind: "design-control-failure",
|
|
648
|
-
operation: SAFE_DESIGN_ERROR_CODE.test(operation) ? operation : "unknown",
|
|
649
|
-
code: classified.code,
|
|
650
|
-
diagnostics,
|
|
651
|
-
};
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
export interface WorkflowControlEngine {
|
|
655
|
-
execute(
|
|
656
|
-
command: unknown,
|
|
657
|
-
context?: ExtensionContext,
|
|
658
|
-
signal?: AbortSignal,
|
|
659
|
-
onActivity?: (event: WorkflowActivityUpdate) => void,
|
|
660
|
-
): Promise<Record<string, unknown>>;
|
|
661
|
-
executeDesign?(request: unknown): Promise<Record<string, unknown>>;
|
|
662
|
-
executeAmendment?(
|
|
663
|
-
change: string,
|
|
664
|
-
batchId: string,
|
|
665
|
-
request: unknown,
|
|
666
|
-
): Promise<Record<string, unknown>>;
|
|
667
|
-
assertDesignRun?(runId: string): void;
|
|
668
|
-
recordDesignEvidence?(input: {
|
|
669
|
-
runId: string;
|
|
670
|
-
evidence: DesignEvidenceResult;
|
|
671
|
-
}): unknown;
|
|
672
|
-
close(): void | Promise<void>;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
export type WorkflowControlEngineFactory = (
|
|
676
|
-
ctx: ExtensionContext,
|
|
677
|
-
) => WorkflowControlEngine | Promise<WorkflowControlEngine>;
|
|
678
|
-
|
|
679
|
-
/** Pi adapter: project host capabilities before entering the package services. */
|
|
680
|
-
export function openPackageWorkflowControlEngine(
|
|
681
|
-
initialContext: ExtensionContext,
|
|
682
|
-
): WorkflowControlEngine {
|
|
683
|
-
const service = openPackageWorkflowService(packageContext(initialContext));
|
|
684
|
-
return {
|
|
685
|
-
...service,
|
|
686
|
-
execute(command, context = initialContext, signal, onActivity) {
|
|
687
|
-
return service.execute(
|
|
688
|
-
command,
|
|
689
|
-
packageContext(context),
|
|
690
|
-
signal,
|
|
691
|
-
onActivity,
|
|
692
|
-
);
|
|
693
|
-
},
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
function runStoreUnavailableStatus(
|
|
698
|
-
error: RunStoreFormatError,
|
|
699
|
-
command: {
|
|
700
|
-
stage: "abel-implement";
|
|
701
|
-
change: string;
|
|
702
|
-
},
|
|
703
|
-
): Record<string, unknown> {
|
|
704
|
-
return {
|
|
705
|
-
stage: command.stage,
|
|
706
|
-
change: command.change,
|
|
707
|
-
state: "paused",
|
|
708
|
-
durable: false,
|
|
709
|
-
completed: false,
|
|
710
|
-
pause: { code: error.code },
|
|
711
|
-
legalCommands: ["status", "start"],
|
|
712
|
-
tasks: [],
|
|
713
|
-
queue: [],
|
|
714
|
-
controlStore: {
|
|
715
|
-
code: error.code,
|
|
716
|
-
databasePath: error.databasePath,
|
|
717
|
-
recovery: structuredClone(error.recovery),
|
|
718
|
-
},
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
function runStoreResetError(error: RunStoreFormatError): Error {
|
|
723
|
-
const reset = new Error(
|
|
724
|
-
`${error.code}: back up and remove the private run store at ${error.databasePath}, then retry the operation`,
|
|
725
|
-
);
|
|
726
|
-
reset.name = "RunStoreResetError";
|
|
727
|
-
return reset;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
export function registerWorkflowControl(
|
|
731
|
-
pi: ExtensionAPI,
|
|
732
|
-
engineFactory: WorkflowControlEngineFactory = openPackageWorkflowControlEngine,
|
|
733
|
-
): void {
|
|
734
|
-
const activity = new ActivityController();
|
|
735
|
-
const engines = new Map<string, Promise<WorkflowControlEngine>>();
|
|
736
|
-
const designOperations = new Set<Promise<void>>();
|
|
737
|
-
const designFailures = new Map<string, SafeDesignFailure>();
|
|
738
|
-
const implementContinuation = new ImplementContinuationDriver();
|
|
739
|
-
let pendingPrompt: EligiblePrompt | undefined;
|
|
740
|
-
let pendingImplementChange: string | undefined;
|
|
741
|
-
let pendingInit = false;
|
|
742
|
-
let exitingStage = false;
|
|
743
|
-
let activePrompt: EligiblePrompt | undefined;
|
|
744
|
-
let designToolSnapshot: string[] | undefined;
|
|
745
|
-
let startupFailure: StageStartupError | undefined;
|
|
746
|
-
let admittedPrompt: string | undefined;
|
|
747
|
-
let latestContextUser: string | undefined;
|
|
748
|
-
const admittedMessages = new Set<string>();
|
|
749
|
-
const reportStartupFailure = (code: StageStartupError) => {
|
|
750
|
-
pi.sendMessage(
|
|
751
|
-
{
|
|
752
|
-
customType: "abel-stage-startup-error",
|
|
753
|
-
content: startupErrorText(code),
|
|
754
|
-
display: true,
|
|
755
|
-
details: { code },
|
|
756
|
-
},
|
|
757
|
-
{ triggerTurn: false },
|
|
758
|
-
);
|
|
759
|
-
};
|
|
760
|
-
const activation = new (class implements Activation {
|
|
761
|
-
state: Activation["state"] = "inactive";
|
|
762
|
-
|
|
763
|
-
isActive(): boolean {
|
|
764
|
-
return this.state === "active";
|
|
765
|
-
}
|
|
4
|
+
import { registerTaskShell } from "./task-shell.ts";
|
|
766
5
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
return true;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
drain(): boolean {
|
|
780
|
-
if (this.state === "inactive" || this.state === "pending") return false;
|
|
781
|
-
this.state = "inactive";
|
|
782
|
-
return true;
|
|
783
|
-
}
|
|
784
|
-
})();
|
|
785
|
-
const packetRuntime = new PacketRuntime({ activation });
|
|
786
|
-
|
|
787
|
-
const engineFor = (ctx: ExtensionContext) => {
|
|
788
|
-
const key = ctx.cwd;
|
|
789
|
-
const existing = engines.get(key);
|
|
790
|
-
if (existing) return existing;
|
|
791
|
-
const opened = Promise.resolve(engineFactory(ctx));
|
|
792
|
-
engines.set(key, opened);
|
|
793
|
-
void opened.catch(() => {
|
|
794
|
-
if (engines.get(key) === opened) engines.delete(key);
|
|
795
|
-
});
|
|
796
|
-
return opened;
|
|
797
|
-
};
|
|
798
|
-
const closeEngines = async () => {
|
|
799
|
-
// Design close() is synchronous and does not own an operation queue.
|
|
800
|
-
// Keep its journal open through finalization's commit/lease-cleanup path
|
|
801
|
-
// and the dispatch result handling before closing any engine storage.
|
|
802
|
-
await Promise.all([...designOperations]);
|
|
803
|
-
const closed = await Promise.allSettled(
|
|
804
|
-
[...engines].map(async ([key, opened]) => {
|
|
805
|
-
const engine = await opened;
|
|
806
|
-
await engine.close();
|
|
807
|
-
if (engines.get(key) === opened) engines.delete(key);
|
|
808
|
-
}),
|
|
809
|
-
);
|
|
810
|
-
const failed = closed.find((result) => result.status === "rejected");
|
|
811
|
-
if (failed?.status === "rejected") throw failed.reason;
|
|
812
|
-
};
|
|
813
|
-
const restoreDesignTools = () => {
|
|
814
|
-
if (!designToolSnapshot) return;
|
|
815
|
-
const snapshot = designToolSnapshot;
|
|
816
|
-
designToolSnapshot = undefined;
|
|
817
|
-
pi.setActiveTools([...snapshot]);
|
|
818
|
-
};
|
|
819
|
-
const enforceDesignTools = () => {
|
|
820
|
-
if (!designToolSnapshot) {
|
|
821
|
-
designToolSnapshot = pi
|
|
822
|
-
.getActiveTools()
|
|
823
|
-
.filter((name) => name !== DISPATCH_TOOL);
|
|
824
|
-
}
|
|
825
|
-
pi.setActiveTools(
|
|
826
|
-
activateTool(
|
|
827
|
-
designToolSnapshot.filter((name) => DESIGN_PARENT_READ_TOOLS.has(name)),
|
|
828
|
-
DISPATCH_TOOL,
|
|
829
|
-
),
|
|
830
|
-
);
|
|
831
|
-
};
|
|
832
|
-
const stageToolsAvailable = () => {
|
|
833
|
-
if (!activation.isActive()) return true;
|
|
834
|
-
const tools = pi.getActiveTools();
|
|
835
|
-
return (
|
|
836
|
-
tools.includes(DISPATCH_TOOL) &&
|
|
837
|
-
(activePrompt !== "abel-design" ||
|
|
838
|
-
tools.every(
|
|
839
|
-
(name) =>
|
|
840
|
-
name === DISPATCH_TOOL || DESIGN_PARENT_READ_TOOLS.has(name),
|
|
841
|
-
))
|
|
842
|
-
);
|
|
843
|
-
};
|
|
844
|
-
const deactivate = () => {
|
|
845
|
-
activation.drain();
|
|
846
|
-
if (designToolSnapshot) {
|
|
847
|
-
restoreDesignTools();
|
|
848
|
-
return;
|
|
849
|
-
}
|
|
850
|
-
const active = pi.getActiveTools();
|
|
851
|
-
if (active.includes(DISPATCH_TOOL)) {
|
|
852
|
-
pi.setActiveTools(deactivateTool(active, DISPATCH_TOOL));
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
const deactivateStage = async () => {
|
|
856
|
-
activePrompt = undefined;
|
|
857
|
-
implementContinuation.deactivate();
|
|
858
|
-
await packetRuntime.drain();
|
|
859
|
-
deactivate();
|
|
860
|
-
};
|
|
861
|
-
const exitStage = async () => {
|
|
862
|
-
if (exitingStage) throw new Error("stage-control-mismatch");
|
|
863
|
-
exitingStage = true;
|
|
864
|
-
try {
|
|
865
|
-
await closeEngines();
|
|
866
|
-
await deactivateStage();
|
|
867
|
-
} finally {
|
|
868
|
-
exitingStage = false;
|
|
869
|
-
}
|
|
870
|
-
};
|
|
871
|
-
|
|
872
|
-
// Pi validates tool arguments before execute(), so expose only the schema
|
|
873
|
-
// legal for the active stage instead of one ambiguous command/packet union.
|
|
874
|
-
const PACKET_PARAMETERS = {
|
|
875
|
-
type: "object",
|
|
876
|
-
properties: {
|
|
877
|
-
action: {
|
|
878
|
-
type: "string",
|
|
879
|
-
enum: [...PACKET_ACTIONS, "design"],
|
|
880
|
-
description:
|
|
881
|
-
"Top-level dispatch action. design and run require request; finish is explicit stage exit and MUST omit request.",
|
|
882
|
-
},
|
|
883
|
-
request: {
|
|
884
|
-
anyOf: [
|
|
885
|
-
DESIGN_REQUEST_SCHEMA,
|
|
886
|
-
DESIGN_CONTROL_REQUEST_SCHEMA,
|
|
887
|
-
GENERIC_REQUEST_SCHEMA,
|
|
888
|
-
],
|
|
889
|
-
},
|
|
890
|
-
},
|
|
891
|
-
required: ["action"],
|
|
892
|
-
additionalProperties: false,
|
|
893
|
-
} as const;
|
|
894
|
-
let registeredParameterKind: "command" | "packet" | undefined;
|
|
895
|
-
|
|
896
|
-
// Stage exit is session control, not a durable Implement command. Keep the
|
|
897
|
-
// six engine commands unchanged and admit only this exact extra envelope.
|
|
898
|
-
const FINISH_PARAMETERS = {
|
|
899
|
-
type: "object",
|
|
900
|
-
properties: { action: { type: "string", enum: ["finish"] } },
|
|
901
|
-
required: ["action"],
|
|
902
|
-
additionalProperties: false,
|
|
903
|
-
} as const;
|
|
904
|
-
const AMEND_PARAMETERS = {
|
|
905
|
-
type: "object",
|
|
906
|
-
properties: {
|
|
907
|
-
action: { type: "string", enum: ["amend"] },
|
|
908
|
-
change: { type: "string" },
|
|
909
|
-
batchId: { type: "string" },
|
|
910
|
-
request: DESIGN_CONTROL_REQUEST_SCHEMA,
|
|
911
|
-
},
|
|
912
|
-
required: ["action", "change", "batchId", "request"],
|
|
913
|
-
additionalProperties: false,
|
|
914
|
-
} as const;
|
|
915
|
-
const IMPLEMENT_PARAMETERS = {
|
|
916
|
-
...CONTROL_COMMAND_PARAMETERS,
|
|
917
|
-
properties: {
|
|
918
|
-
...CONTROL_COMMAND_PARAMETERS.properties,
|
|
919
|
-
...AMEND_PARAMETERS.properties,
|
|
920
|
-
action: { type: "string", enum: ["finish", "amend"] },
|
|
921
|
-
},
|
|
922
|
-
required: [],
|
|
923
|
-
anyOf: [
|
|
924
|
-
...CONTROL_COMMAND_PARAMETERS.anyOf,
|
|
925
|
-
FINISH_PARAMETERS,
|
|
926
|
-
AMEND_PARAMETERS,
|
|
927
|
-
],
|
|
928
|
-
} as const;
|
|
929
|
-
const prepareImplementArguments = (args: unknown): unknown => {
|
|
930
|
-
if (!args || typeof args !== "object" || Array.isArray(args)) return args;
|
|
931
|
-
const record = { ...(args as Record<string, unknown>) };
|
|
932
|
-
// Strict providers can pad fields belonging to the other union branch.
|
|
933
|
-
if (record.action !== "amend") {
|
|
934
|
-
for (const key of ["batchId", "request"]) {
|
|
935
|
-
if (record[key] === null || record[key] === undefined)
|
|
936
|
-
delete record[key];
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
if (record.action === "finish" || record.action === "amend") {
|
|
940
|
-
for (const key of Object.keys(CONTROL_COMMAND_PARAMETERS.properties)) {
|
|
941
|
-
if (record[key] === null || record[key] === undefined)
|
|
942
|
-
delete record[key];
|
|
943
|
-
}
|
|
944
|
-
return record;
|
|
945
|
-
}
|
|
946
|
-
if (record.action === null || record.action === undefined)
|
|
947
|
-
delete record.action;
|
|
948
|
-
return canonicalizeControlCommandToolInput(record);
|
|
949
|
-
};
|
|
950
|
-
|
|
951
|
-
const registerDispatchTool = (kind: "command" | "packet") => {
|
|
952
|
-
if (registeredParameterKind === kind) return;
|
|
953
|
-
pi.registerTool({
|
|
954
|
-
name: DISPATCH_TOOL,
|
|
955
|
-
label: "Abel Control",
|
|
956
|
-
promptSnippet:
|
|
957
|
-
"Control the explicitly activated Abel stage; retain paused work across same-task follow-ups.",
|
|
958
|
-
description:
|
|
959
|
-
kind === "command"
|
|
960
|
-
? 'Private stage-bound Abel workflow control. Accepts durable Implement commands, batch-bound {"action":"amend"} artifact revisions, or {"action":"finish"} to leave and preserve resumable work.'
|
|
961
|
-
: "Private stage-bound Abel packet control. Accepts bounded Design and Diagnose packet operations.",
|
|
962
|
-
executionMode: "parallel",
|
|
963
|
-
...(kind === "command"
|
|
964
|
-
? {
|
|
965
|
-
prepareArguments: (args: unknown) =>
|
|
966
|
-
prepareImplementArguments(args) as never,
|
|
967
|
-
}
|
|
968
|
-
: {}),
|
|
969
|
-
parameters: kind === "command" ? IMPLEMENT_PARAMETERS : PACKET_PARAMETERS,
|
|
970
|
-
async execute(
|
|
971
|
-
toolCallId: string,
|
|
972
|
-
params: unknown,
|
|
973
|
-
signal: AbortSignal | undefined,
|
|
974
|
-
onUpdate: AgentToolUpdateCallback<unknown> | undefined,
|
|
975
|
-
ctx: ExtensionContext,
|
|
976
|
-
) {
|
|
977
|
-
if (
|
|
978
|
-
startupFailure ||
|
|
979
|
-
exitingStage ||
|
|
980
|
-
!activation.isActive() ||
|
|
981
|
-
activePrompt === undefined
|
|
982
|
-
) {
|
|
983
|
-
throw new Error("stage-control-mismatch");
|
|
984
|
-
}
|
|
985
|
-
if (kind === "command") params = prepareImplementArguments(params);
|
|
986
|
-
const record =
|
|
987
|
-
params && typeof params === "object" && !Array.isArray(params)
|
|
988
|
-
? (params as Record<string, unknown>)
|
|
989
|
-
: undefined;
|
|
990
|
-
if (record?.action === "amend") {
|
|
991
|
-
if (
|
|
992
|
-
activePrompt !== "abel-implement" ||
|
|
993
|
-
Object.keys(record).some(
|
|
994
|
-
(key) =>
|
|
995
|
-
!["action", "change", "batchId", "request"].includes(key),
|
|
996
|
-
) ||
|
|
997
|
-
typeof record.change !== "string" ||
|
|
998
|
-
typeof record.batchId !== "string"
|
|
999
|
-
) {
|
|
1000
|
-
throw new Error("stage-control-mismatch");
|
|
1001
|
-
}
|
|
1002
|
-
const parsed = validateDesignControlRequest(record.request);
|
|
1003
|
-
if (!parsed.ok) {
|
|
1004
|
-
const error = new DesignControlValidationError(parsed.diagnostics);
|
|
1005
|
-
designFailures.set(
|
|
1006
|
-
toolCallId,
|
|
1007
|
-
safeDesignFailure(error, "validation"),
|
|
1008
|
-
);
|
|
1009
|
-
throw error;
|
|
1010
|
-
}
|
|
1011
|
-
const request = parsed.value;
|
|
1012
|
-
if (
|
|
1013
|
-
request.operation === "bind-change" ||
|
|
1014
|
-
(request.operation === "start" &&
|
|
1015
|
-
(!("change" in request) || request.change !== record.change))
|
|
1016
|
-
) {
|
|
1017
|
-
throw new Error("amendment-change-mismatch");
|
|
1018
|
-
}
|
|
1019
|
-
let settle!: () => void;
|
|
1020
|
-
const pending = new Promise<void>((resolve) => {
|
|
1021
|
-
settle = resolve;
|
|
1022
|
-
});
|
|
1023
|
-
designOperations.add(pending);
|
|
1024
|
-
try {
|
|
1025
|
-
const engine = await engineFor(ctx);
|
|
1026
|
-
if (exitingStage || !activation.isActive() || !engine.executeDesign)
|
|
1027
|
-
throw new Error("stage-control-mismatch");
|
|
1028
|
-
const status = await engine.execute(
|
|
1029
|
-
{
|
|
1030
|
-
command: "status",
|
|
1031
|
-
stage: "abel-implement",
|
|
1032
|
-
change: record.change,
|
|
1033
|
-
},
|
|
1034
|
-
ctx,
|
|
1035
|
-
signal,
|
|
1036
|
-
);
|
|
1037
|
-
const batch = status.decisionBatch as { id?: string } | undefined;
|
|
1038
|
-
if (
|
|
1039
|
-
!["approval-needed", "paused"].includes(String(status.state)) ||
|
|
1040
|
-
batch?.id !== record.batchId
|
|
1041
|
-
)
|
|
1042
|
-
throw new Error("amendment-batch-stale");
|
|
1043
|
-
if (request.operation !== "start") {
|
|
1044
|
-
const designStatus = await engine.executeDesign({
|
|
1045
|
-
operation: "status",
|
|
1046
|
-
runId: request.runId,
|
|
1047
|
-
});
|
|
1048
|
-
if (designStatus.change !== record.change)
|
|
1049
|
-
throw new Error("amendment-change-mismatch");
|
|
1050
|
-
}
|
|
1051
|
-
if (!engine.executeAmendment)
|
|
1052
|
-
throw new Error("amendment-control-unavailable");
|
|
1053
|
-
const revision = await engine.executeAmendment(
|
|
1054
|
-
record.change,
|
|
1055
|
-
record.batchId,
|
|
1056
|
-
request,
|
|
1057
|
-
);
|
|
1058
|
-
const payload =
|
|
1059
|
-
request.operation === "finalize-delivery" &&
|
|
1060
|
-
revision.state === "completed"
|
|
1061
|
-
? {
|
|
1062
|
-
...revision,
|
|
1063
|
-
scope: "amendment",
|
|
1064
|
-
state: "ready",
|
|
1065
|
-
completed: false,
|
|
1066
|
-
runId: status.runId,
|
|
1067
|
-
amendmentRunId: revision.runId,
|
|
1068
|
-
stage: "abel-implement",
|
|
1069
|
-
}
|
|
1070
|
-
: { ...revision, scope: "amendment" };
|
|
1071
|
-
// A compiled amendment stays in Implement; its private Design
|
|
1072
|
-
// records do not activate the Design stage or broaden parent tools.
|
|
1073
|
-
return {
|
|
1074
|
-
content: [
|
|
1075
|
-
{ type: "text" as const, text: JSON.stringify(payload) },
|
|
1076
|
-
],
|
|
1077
|
-
details: payload,
|
|
1078
|
-
};
|
|
1079
|
-
} catch (error) {
|
|
1080
|
-
if (
|
|
1081
|
-
error instanceof DesignPlanValidationError ||
|
|
1082
|
-
error instanceof DesignFinalizationError ||
|
|
1083
|
-
error instanceof DeliveryValidationError
|
|
1084
|
-
) {
|
|
1085
|
-
const failure = safeDesignFailure(
|
|
1086
|
-
error,
|
|
1087
|
-
request.operation,
|
|
1088
|
-
"execution",
|
|
1089
|
-
);
|
|
1090
|
-
designFailures.set(toolCallId, failure);
|
|
1091
|
-
throw new Error(failure.code);
|
|
1092
|
-
}
|
|
1093
|
-
throw error;
|
|
1094
|
-
} finally {
|
|
1095
|
-
settle();
|
|
1096
|
-
designOperations.delete(pending);
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
if (record && typeof record.action === "string") {
|
|
1100
|
-
if (
|
|
1101
|
-
Object.keys(record).some(
|
|
1102
|
-
(key) => key !== "action" && key !== "request",
|
|
1103
|
-
)
|
|
1104
|
-
) {
|
|
1105
|
-
throw new Error("control-envelope-ambiguous");
|
|
1106
|
-
}
|
|
1107
|
-
if (record.action === "finish" && Object.hasOwn(record, "request")) {
|
|
1108
|
-
throw new Error("control-envelope-ambiguous");
|
|
1109
|
-
}
|
|
1110
|
-
if (record.action === "finish") {
|
|
1111
|
-
// close() aborts and settles active operations before ordinary tools
|
|
1112
|
-
// return; durable work is retained, never discarded or completed.
|
|
1113
|
-
await exitStage();
|
|
1114
|
-
const payload = { ok: true, state: "inactive" };
|
|
1115
|
-
return {
|
|
1116
|
-
content: [
|
|
1117
|
-
{ type: "text" as const, text: JSON.stringify(payload) },
|
|
1118
|
-
],
|
|
1119
|
-
details: payload,
|
|
1120
|
-
};
|
|
1121
|
-
}
|
|
1122
|
-
if (
|
|
1123
|
-
activePrompt !== "abel-design" &&
|
|
1124
|
-
activePrompt !== "abel-diagnose"
|
|
1125
|
-
) {
|
|
1126
|
-
throw new Error("stage-control-mismatch");
|
|
1127
|
-
}
|
|
1128
|
-
if (record.action === "design") {
|
|
1129
|
-
if (activePrompt !== "abel-design") {
|
|
1130
|
-
throw new Error("stage-control-mismatch");
|
|
1131
|
-
}
|
|
1132
|
-
const designRequest = validateDesignControlRequest(record.request);
|
|
1133
|
-
if (!designRequest.ok) {
|
|
1134
|
-
const error = new DesignControlValidationError(
|
|
1135
|
-
designRequest.diagnostics,
|
|
1136
|
-
);
|
|
1137
|
-
designFailures.set(
|
|
1138
|
-
toolCallId,
|
|
1139
|
-
safeDesignFailure(error, "validation"),
|
|
1140
|
-
);
|
|
1141
|
-
throw error;
|
|
1142
|
-
}
|
|
1143
|
-
let settleDesign!: () => void;
|
|
1144
|
-
const settled = new Promise<void>((resolve) => {
|
|
1145
|
-
settleDesign = resolve;
|
|
1146
|
-
});
|
|
1147
|
-
// Register before the first await, including asynchronous engine
|
|
1148
|
-
// opening. Teardown blocks admission before taking its snapshot.
|
|
1149
|
-
designOperations.add(settled);
|
|
1150
|
-
let boundary: DesignFailureBoundary = "initialization";
|
|
1151
|
-
try {
|
|
1152
|
-
const engine = await engineFor(ctx);
|
|
1153
|
-
if (exitingStage || !activation.isActive())
|
|
1154
|
-
throw new Error("stage-control-mismatch");
|
|
1155
|
-
if (!engine.executeDesign)
|
|
1156
|
-
throw new Error("design-control-unavailable");
|
|
1157
|
-
boundary = "execution";
|
|
1158
|
-
const payload = await engine.executeDesign(designRequest.value);
|
|
1159
|
-
boundary = "serialization";
|
|
1160
|
-
const serialized = JSON.stringify(payload);
|
|
1161
|
-
if (serialized === undefined) {
|
|
1162
|
-
throw new Error("design-receipt-serialization-failed");
|
|
1163
|
-
}
|
|
1164
|
-
boundary = "execution";
|
|
1165
|
-
if (
|
|
1166
|
-
designRequest.value.operation === "finalize-delivery" &&
|
|
1167
|
-
payload.state === "completed" &&
|
|
1168
|
-
!exitingStage &&
|
|
1169
|
-
activePrompt === "abel-design"
|
|
1170
|
-
) {
|
|
1171
|
-
await deactivateStage();
|
|
1172
|
-
}
|
|
1173
|
-
return {
|
|
1174
|
-
content: [{ type: "text" as const, text: serialized }],
|
|
1175
|
-
details: payload,
|
|
1176
|
-
};
|
|
1177
|
-
} catch (error) {
|
|
1178
|
-
const failure = safeDesignFailure(
|
|
1179
|
-
error,
|
|
1180
|
-
designRequest.value.operation,
|
|
1181
|
-
boundary,
|
|
1182
|
-
);
|
|
1183
|
-
designFailures.set(toolCallId, failure);
|
|
1184
|
-
const sanitized = new Error(failure.code);
|
|
1185
|
-
sanitized.name = "DesignControlError";
|
|
1186
|
-
throw sanitized;
|
|
1187
|
-
} finally {
|
|
1188
|
-
designOperations.delete(settled);
|
|
1189
|
-
settleDesign();
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
let designEngine: WorkflowControlEngine | undefined;
|
|
1193
|
-
if (record.action === "run") {
|
|
1194
|
-
const packet = packetRuntime.validateRequest(record.request);
|
|
1195
|
-
if (!packet.ok || packet.value.stage !== activePrompt) {
|
|
1196
|
-
throw new Error(
|
|
1197
|
-
packet.ok ? "stage-control-mismatch" : packet.reason,
|
|
1198
|
-
);
|
|
1199
|
-
}
|
|
1200
|
-
if (packet.value.stage === "abel-design") {
|
|
1201
|
-
try {
|
|
1202
|
-
designEngine = await engineFor(ctx);
|
|
1203
|
-
if (exitingStage || !activation.isActive())
|
|
1204
|
-
throw new Error("stage-control-mismatch");
|
|
1205
|
-
} catch (error) {
|
|
1206
|
-
if (error instanceof RunStoreFormatError)
|
|
1207
|
-
throw runStoreResetError(error);
|
|
1208
|
-
throw error;
|
|
1209
|
-
}
|
|
1210
|
-
if (!designEngine.assertDesignRun || !packet.value.runId) {
|
|
1211
|
-
throw new Error("design-control-unavailable");
|
|
1212
|
-
}
|
|
1213
|
-
designEngine.assertDesignRun(packet.value.runId);
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
const operation = {
|
|
1217
|
-
request: record.request,
|
|
1218
|
-
};
|
|
1219
|
-
const tuiRun = ctx.mode === "tui" && record.action === "run";
|
|
1220
|
-
const packetPayload = tuiRun
|
|
1221
|
-
? await packetRuntime.execute(
|
|
1222
|
-
record.action,
|
|
1223
|
-
operation,
|
|
1224
|
-
packageContext(ctx),
|
|
1225
|
-
signal,
|
|
1226
|
-
activity.observe(
|
|
1227
|
-
toolCallId,
|
|
1228
|
-
onUpdate as ((result: unknown) => void) | undefined,
|
|
1229
|
-
),
|
|
1230
|
-
)
|
|
1231
|
-
: await packetRuntime.execute(
|
|
1232
|
-
record.action,
|
|
1233
|
-
operation,
|
|
1234
|
-
packageContext(ctx),
|
|
1235
|
-
signal,
|
|
1236
|
-
);
|
|
1237
|
-
let payload:
|
|
1238
|
-
| typeof packetPayload
|
|
1239
|
-
| (typeof packetPayload & {
|
|
1240
|
-
recordedEvidence: unknown;
|
|
1241
|
-
}) = packetPayload;
|
|
1242
|
-
if (
|
|
1243
|
-
packetPayload.ok &&
|
|
1244
|
-
record.action === "run" &&
|
|
1245
|
-
activePrompt === "abel-design"
|
|
1246
|
-
) {
|
|
1247
|
-
const packet = packetRuntime.validateRequest(record.request);
|
|
1248
|
-
if (
|
|
1249
|
-
!packet.ok ||
|
|
1250
|
-
!packet.value.runId ||
|
|
1251
|
-
!designEngine?.recordDesignEvidence
|
|
1252
|
-
) {
|
|
1253
|
-
throw new Error("design-control-unavailable");
|
|
1254
|
-
}
|
|
1255
|
-
const recordedEvidence = designEngine.recordDesignEvidence({
|
|
1256
|
-
runId: packet.value.runId,
|
|
1257
|
-
evidence: packetPayload.result as DesignEvidenceResult,
|
|
1258
|
-
});
|
|
1259
|
-
payload = { ...packetPayload, recordedEvidence };
|
|
1260
|
-
}
|
|
1261
|
-
const display = tuiRun
|
|
1262
|
-
? activity.finalize(toolCallId, payload)
|
|
1263
|
-
: undefined;
|
|
1264
|
-
const { payload: publicPayload, usage } = splitUsage(payload);
|
|
1265
|
-
const details = display
|
|
1266
|
-
? {
|
|
1267
|
-
...(publicPayload as Record<string, unknown>),
|
|
1268
|
-
[ACTIVITY_DETAILS_KEY]: display,
|
|
1269
|
-
}
|
|
1270
|
-
: publicPayload;
|
|
1271
|
-
return {
|
|
1272
|
-
content: [
|
|
1273
|
-
{ type: "text" as const, text: JSON.stringify(publicPayload) },
|
|
1274
|
-
],
|
|
1275
|
-
details,
|
|
1276
|
-
...(usage === undefined ? {} : { usage }),
|
|
1277
|
-
};
|
|
1278
|
-
}
|
|
1279
|
-
const validation = validateControlCommand(
|
|
1280
|
-
canonicalizeControlCommandToolInput(params),
|
|
1281
|
-
);
|
|
1282
|
-
if (!validation.ok) {
|
|
1283
|
-
const error = new Error(validation.code);
|
|
1284
|
-
error.name = "ControlCommandError";
|
|
1285
|
-
throw error;
|
|
1286
|
-
}
|
|
1287
|
-
if (
|
|
1288
|
-
activePrompt !== "abel-implement" ||
|
|
1289
|
-
validation.value.stage !== activePrompt
|
|
1290
|
-
) {
|
|
1291
|
-
throw new Error("stage-control-mismatch");
|
|
1292
|
-
}
|
|
1293
|
-
let engine: WorkflowControlEngine;
|
|
1294
|
-
try {
|
|
1295
|
-
engine = await engineFor(ctx);
|
|
1296
|
-
if (exitingStage || !activation.isActive())
|
|
1297
|
-
throw new Error("stage-control-mismatch");
|
|
1298
|
-
} catch (error) {
|
|
1299
|
-
if (error instanceof PackageStateError) {
|
|
1300
|
-
const payload = {
|
|
1301
|
-
stage: "abel-implement",
|
|
1302
|
-
change: validation.value.change,
|
|
1303
|
-
state: "paused",
|
|
1304
|
-
durable: false,
|
|
1305
|
-
completed: false,
|
|
1306
|
-
pause: { code: error.code },
|
|
1307
|
-
legalCommands: ["status", "start"],
|
|
1308
|
-
tasks: [],
|
|
1309
|
-
queue: [],
|
|
1310
|
-
};
|
|
1311
|
-
return {
|
|
1312
|
-
content: [
|
|
1313
|
-
{ type: "text" as const, text: JSON.stringify(payload) },
|
|
1314
|
-
],
|
|
1315
|
-
details: payload,
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
|
-
if (!(error instanceof RunStoreFormatError)) throw error;
|
|
1319
|
-
const payload = runStoreUnavailableStatus(error, validation.value);
|
|
1320
|
-
return {
|
|
1321
|
-
content: [{ type: "text" as const, text: JSON.stringify(payload) }],
|
|
1322
|
-
details: payload,
|
|
1323
|
-
};
|
|
1324
|
-
}
|
|
1325
|
-
const tuiControl = ctx.mode === "tui";
|
|
1326
|
-
if (tuiControl) {
|
|
1327
|
-
try {
|
|
1328
|
-
activity.beginWorkflow(
|
|
1329
|
-
toolCallId,
|
|
1330
|
-
validation.value,
|
|
1331
|
-
onUpdate as ((result: unknown) => void) | undefined,
|
|
1332
|
-
);
|
|
1333
|
-
} catch {
|
|
1334
|
-
// Presentation state must never alter workflow execution.
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
let payload: Record<string, unknown>;
|
|
1338
|
-
try {
|
|
1339
|
-
payload = await engine.execute(
|
|
1340
|
-
validation.value,
|
|
1341
|
-
ctx,
|
|
1342
|
-
signal,
|
|
1343
|
-
tuiControl
|
|
1344
|
-
? (event) => {
|
|
1345
|
-
try {
|
|
1346
|
-
activity.updateWorkflow(
|
|
1347
|
-
toolCallId,
|
|
1348
|
-
validation.value,
|
|
1349
|
-
event,
|
|
1350
|
-
);
|
|
1351
|
-
} catch {
|
|
1352
|
-
// Presentation state must never alter workflow execution.
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
: undefined,
|
|
1356
|
-
);
|
|
1357
|
-
} catch (error) {
|
|
1358
|
-
if (tuiControl) activity.failWorkflow(toolCallId);
|
|
1359
|
-
throw error;
|
|
1360
|
-
}
|
|
1361
|
-
if (
|
|
1362
|
-
["completed", "discarded", "rejected"].includes(String(payload.state))
|
|
1363
|
-
) {
|
|
1364
|
-
await deactivateStage();
|
|
1365
|
-
}
|
|
1366
|
-
let display: ReturnType<typeof activity.finalizeWorkflow>;
|
|
1367
|
-
if (tuiControl) {
|
|
1368
|
-
try {
|
|
1369
|
-
display = activity.finalizeWorkflow(
|
|
1370
|
-
toolCallId,
|
|
1371
|
-
validation.value,
|
|
1372
|
-
payload,
|
|
1373
|
-
);
|
|
1374
|
-
} catch {
|
|
1375
|
-
activity.failWorkflow(toolCallId);
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
return {
|
|
1379
|
-
content: [{ type: "text" as const, text: JSON.stringify(payload) }],
|
|
1380
|
-
details: display
|
|
1381
|
-
? { ...payload, [ACTIVITY_DETAILS_KEY]: display }
|
|
1382
|
-
: payload,
|
|
1383
|
-
};
|
|
1384
|
-
},
|
|
1385
|
-
renderCall(args: unknown, theme: Theme, context: unknown) {
|
|
1386
|
-
return renderActivityCall(
|
|
1387
|
-
args,
|
|
1388
|
-
theme,
|
|
1389
|
-
context as Parameters<typeof renderActivityCall>[2],
|
|
1390
|
-
);
|
|
1391
|
-
},
|
|
1392
|
-
renderResult(
|
|
1393
|
-
result: AgentToolResult<unknown>,
|
|
1394
|
-
options: ToolRenderResultOptions,
|
|
1395
|
-
theme: Theme,
|
|
1396
|
-
context: unknown,
|
|
1397
|
-
) {
|
|
1398
|
-
return renderActivityResult(
|
|
1399
|
-
result,
|
|
1400
|
-
options,
|
|
1401
|
-
theme,
|
|
1402
|
-
context as Parameters<typeof renderActivityResult>[3],
|
|
1403
|
-
);
|
|
1404
|
-
},
|
|
1405
|
-
} as never);
|
|
1406
|
-
registeredParameterKind = kind;
|
|
1407
|
-
};
|
|
1408
|
-
registerDispatchTool("packet");
|
|
1409
|
-
|
|
1410
|
-
pi.on("tool_result", (event) => {
|
|
1411
|
-
implementContinuation.noteToolResult({
|
|
1412
|
-
toolName: event.toolName,
|
|
1413
|
-
input: event.input,
|
|
1414
|
-
content: event.content,
|
|
1415
|
-
details: event.details,
|
|
1416
|
-
isError: event.isError,
|
|
1417
|
-
});
|
|
1418
|
-
if (
|
|
1419
|
-
event.toolName !== DISPATCH_TOOL ||
|
|
1420
|
-
!event.isError ||
|
|
1421
|
-
(event.input.action !== "design" &&
|
|
1422
|
-
!(
|
|
1423
|
-
event.input.action === "amend" && designFailures.has(event.toolCallId)
|
|
1424
|
-
))
|
|
1425
|
-
) {
|
|
1426
|
-
return;
|
|
1427
|
-
}
|
|
1428
|
-
const errorText = event.content
|
|
1429
|
-
.flatMap((item) =>
|
|
1430
|
-
item.type === "text" && typeof item.text === "string"
|
|
1431
|
-
? [item.text]
|
|
1432
|
-
: [],
|
|
1433
|
-
)
|
|
1434
|
-
.join("\n");
|
|
1435
|
-
const failure = safeDesignFailure(
|
|
1436
|
-
new Error(errorText.trim()),
|
|
1437
|
-
typeof (event.input.request as Record<string, unknown> | undefined)
|
|
1438
|
-
?.operation === "string"
|
|
1439
|
-
? String((event.input.request as Record<string, unknown>).operation)
|
|
1440
|
-
: "unknown",
|
|
1441
|
-
);
|
|
1442
|
-
const details =
|
|
1443
|
-
event.details && typeof event.details === "object"
|
|
1444
|
-
? (event.details as Record<string, unknown>)
|
|
1445
|
-
: {};
|
|
1446
|
-
const visible = designFailures.get(event.toolCallId) ?? failure;
|
|
1447
|
-
designFailures.delete(event.toolCallId);
|
|
1448
|
-
return {
|
|
1449
|
-
content: [{ type: "text" as const, text: JSON.stringify(visible) }],
|
|
1450
|
-
details: { ...details, designFailure: visible },
|
|
1451
|
-
};
|
|
1452
|
-
});
|
|
1453
|
-
pi.on("input", (event) => {
|
|
1454
|
-
implementContinuation.noteInput();
|
|
1455
|
-
pendingPrompt = undefined;
|
|
1456
|
-
pendingImplementChange = undefined;
|
|
1457
|
-
pendingInit = false;
|
|
1458
|
-
const prompt = invokedPrompt(event.text);
|
|
1459
|
-
const init = /^\/abel-init(?:\s|$)/u.test(event.text);
|
|
1460
|
-
if (event.source !== "interactive" && event.source !== "rpc") {
|
|
1461
|
-
// Stop before Pi expands the template: hiding dispatch alone would still
|
|
1462
|
-
// expose the complete workflow instructions to the parent model.
|
|
1463
|
-
return { action: prompt || init ? "handled" : "continue" };
|
|
1464
|
-
}
|
|
1465
|
-
if (
|
|
1466
|
-
prompt &&
|
|
1467
|
-
(event.streamingBehavior || !hasPackageProvenance(pi, prompt))
|
|
1468
|
-
) {
|
|
1469
|
-
reportStartupFailure(
|
|
1470
|
-
event.streamingBehavior
|
|
1471
|
-
? "abel-stage-requires-idle"
|
|
1472
|
-
: "abel-stage-provenance-invalid",
|
|
1473
|
-
);
|
|
1474
|
-
return { action: "handled" };
|
|
1475
|
-
}
|
|
1476
|
-
pendingPrompt = prompt;
|
|
1477
|
-
pendingImplementChange =
|
|
1478
|
-
prompt === "abel-implement"
|
|
1479
|
-
? implementChangeFromInvocation(event.text)
|
|
1480
|
-
: undefined;
|
|
1481
|
-
pendingInit = init && hasPackageProvenance(pi, "abel-init");
|
|
1482
|
-
if (
|
|
1483
|
-
activePrompt &&
|
|
1484
|
-
(pendingInit ||
|
|
1485
|
-
(prompt && prompt !== activePrompt && hasPackageProvenance(pi, prompt)))
|
|
1486
|
-
) {
|
|
1487
|
-
return (async () => {
|
|
1488
|
-
await exitStage();
|
|
1489
|
-
return { action: "continue" as const };
|
|
1490
|
-
})();
|
|
1491
|
-
}
|
|
1492
|
-
return { action: "continue" };
|
|
1493
|
-
});
|
|
1494
|
-
pi.on("before_agent_start", (event, ctx) => {
|
|
1495
|
-
startupFailure = undefined;
|
|
1496
|
-
admittedPrompt = undefined;
|
|
1497
|
-
latestContextUser = undefined;
|
|
1498
|
-
const prompt = pendingPrompt;
|
|
1499
|
-
const implementChange = pendingImplementChange;
|
|
1500
|
-
const init =
|
|
1501
|
-
pendingInit && hasExpandedPromptMarker(event.prompt, "abel-init");
|
|
1502
|
-
pendingPrompt = undefined;
|
|
1503
|
-
pendingImplementChange = undefined;
|
|
1504
|
-
pendingInit = false;
|
|
1505
|
-
const verified =
|
|
1506
|
-
prompt && isVerifiedStageInvocation(pi, activation, prompt, event.prompt);
|
|
1507
|
-
if (prompt && !verified) startupFailure = "abel-stage-unverified-input";
|
|
1508
|
-
if (!prompt && expandedStagePrompt(event.prompt))
|
|
1509
|
-
startupFailure = "abel-stage-unverified-input";
|
|
1510
|
-
const toolsBefore = pi.getActiveTools();
|
|
1511
|
-
const wasActive = activation.isActive();
|
|
1512
|
-
try {
|
|
1513
|
-
if (verified) {
|
|
1514
|
-
if (activePrompt === "abel-design" && prompt !== "abel-design") {
|
|
1515
|
-
restoreDesignTools();
|
|
1516
|
-
}
|
|
1517
|
-
registerDispatchTool(
|
|
1518
|
-
prompt === "abel-implement" ? "command" : "packet",
|
|
1519
|
-
);
|
|
1520
|
-
activePrompt = prompt;
|
|
1521
|
-
if (prompt === "abel-implement") {
|
|
1522
|
-
if (
|
|
1523
|
-
implementChange &&
|
|
1524
|
-
implementPromptBindsChange(event.prompt, implementChange) &&
|
|
1525
|
-
typeof ctx.cwd === "string"
|
|
1526
|
-
)
|
|
1527
|
-
implementContinuation.activate({
|
|
1528
|
-
cwd: ctx.cwd,
|
|
1529
|
-
change: implementChange,
|
|
1530
|
-
});
|
|
1531
|
-
else implementContinuation.deactivate();
|
|
1532
|
-
} else {
|
|
1533
|
-
implementContinuation.deactivate();
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
if (prompt) activateDispatcher(pi, activation, prompt, event.prompt);
|
|
1537
|
-
if (activation.isActive() && activePrompt === "abel-design")
|
|
1538
|
-
enforceDesignTools();
|
|
1539
|
-
if (!stageToolsAvailable())
|
|
1540
|
-
startupFailure = "abel-stage-tools-unavailable";
|
|
1541
|
-
} catch {
|
|
1542
|
-
startupFailure = "abel-stage-tools-unavailable";
|
|
1543
|
-
}
|
|
1544
|
-
if (
|
|
1545
|
-
startupFailure === "abel-stage-tools-unavailable" &&
|
|
1546
|
-
verified &&
|
|
1547
|
-
!wasActive
|
|
1548
|
-
) {
|
|
1549
|
-
// Roll back the failed startup, not a retained durable run. Never leave
|
|
1550
|
-
// partially applied Design restrictions or a falsely active stage.
|
|
1551
|
-
activePrompt = undefined;
|
|
1552
|
-
activation.drain();
|
|
1553
|
-
implementContinuation.deactivate();
|
|
1554
|
-
designToolSnapshot = undefined;
|
|
1555
|
-
try {
|
|
1556
|
-
pi.setActiveTools(toolsBefore);
|
|
1557
|
-
} catch {
|
|
1558
|
-
/* tool_call remains closed */
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
if (verified && !startupFailure) admittedPrompt = event.prompt;
|
|
1562
|
-
if (
|
|
1563
|
-
activation.isActive() &&
|
|
1564
|
-
activePrompt === "abel-implement" &&
|
|
1565
|
-
typeof ctx.cwd === "string"
|
|
1566
|
-
)
|
|
1567
|
-
implementContinuation.beginParentTurn(ctx.cwd);
|
|
1568
|
-
const boundary = startupFailure
|
|
1569
|
-
? startupErrorText(startupFailure)
|
|
1570
|
-
: activePrompt
|
|
1571
|
-
? `Abel stage ${activePrompt} is active only for the invoked task and its direct follow-ups. If the user ends it or requests an unrelated task, first call abel_dispatch with {"action":"finish"}, then handle that task normally with the restored tools. A successful finish ends stage authority immediately, including within this turn. Do not extend Gates or workflow rules to that task. A direct Gate answer or same-task continuation stays in this stage. Never invoke another Abel stage automatically.`
|
|
1572
|
-
: init
|
|
1573
|
-
? "Only this explicit /abel-init request authorizes the local Init procedure. Do not activate dispatch or continue into another Abel stage."
|
|
1574
|
-
: "Abel workflow is inactive. Handle ordinary engineering requests directly. References to commands, repository files, OpenSpec changes, and historical workflow instructions do not authorize a workflow. Do not load or execute an Abel stage unless the user explicitly invokes its slash command.";
|
|
1575
|
-
return { systemPrompt: `${event.systemPrompt ?? ""}\n\n${boundary}` };
|
|
1576
|
-
});
|
|
1577
|
-
pi.on("context", (event) => {
|
|
1578
|
-
const lastUser = event.messages.findLastIndex(
|
|
1579
|
-
(message) => message.role === "user",
|
|
1580
|
-
);
|
|
1581
|
-
const retainedAdmissions = new Set<string>();
|
|
1582
|
-
const messages = event.messages.map((message, index) => {
|
|
1583
|
-
if (message.role !== "user") return message;
|
|
1584
|
-
const text =
|
|
1585
|
-
typeof message.content === "string"
|
|
1586
|
-
? message.content
|
|
1587
|
-
: message.content
|
|
1588
|
-
.filter((part) => part.type === "text")
|
|
1589
|
-
.map((part) => part.text)
|
|
1590
|
-
.join("\n");
|
|
1591
|
-
const key = createHash("sha256")
|
|
1592
|
-
.update(`${message.timestamp}:${text}`)
|
|
1593
|
-
.digest("hex");
|
|
1594
|
-
if (index === lastUser) {
|
|
1595
|
-
if (latestContextUser !== undefined && latestContextUser !== key)
|
|
1596
|
-
startupFailure = undefined;
|
|
1597
|
-
latestContextUser = key;
|
|
1598
|
-
try {
|
|
1599
|
-
if (!stageToolsAvailable())
|
|
1600
|
-
startupFailure = "abel-stage-tools-unavailable";
|
|
1601
|
-
} catch {
|
|
1602
|
-
startupFailure = "abel-stage-tools-unavailable";
|
|
1603
|
-
}
|
|
1604
|
-
if (admittedPrompt === text && !startupFailure)
|
|
1605
|
-
admittedMessages.add(key);
|
|
1606
|
-
admittedPrompt = undefined;
|
|
1607
|
-
}
|
|
1608
|
-
if (admittedMessages.has(key)) retainedAdmissions.add(key);
|
|
1609
|
-
const orphan = expandedStagePrompt(text) && !admittedMessages.has(key);
|
|
1610
|
-
if (index === lastUser && orphan && !startupFailure)
|
|
1611
|
-
startupFailure = "abel-stage-unverified-input";
|
|
1612
|
-
if (!orphan && !(index === lastUser && startupFailure)) return message;
|
|
1613
|
-
return {
|
|
1614
|
-
...message,
|
|
1615
|
-
content: [
|
|
1616
|
-
{
|
|
1617
|
-
type: "text" as const,
|
|
1618
|
-
text: startupErrorText(
|
|
1619
|
-
index === lastUser && startupFailure
|
|
1620
|
-
? startupFailure
|
|
1621
|
-
: "abel-stage-unverified-input",
|
|
1622
|
-
),
|
|
1623
|
-
},
|
|
1624
|
-
],
|
|
1625
|
-
};
|
|
1626
|
-
});
|
|
1627
|
-
// Keep only identities still present after compaction; never retain raw requirements.
|
|
1628
|
-
admittedMessages.clear();
|
|
1629
|
-
for (const key of retainedAdmissions) admittedMessages.add(key);
|
|
1630
|
-
return { messages };
|
|
1631
|
-
});
|
|
1632
|
-
pi.on("tool_call", (event) => {
|
|
1633
|
-
if (!stageToolsAvailable()) startupFailure = "abel-stage-tools-unavailable";
|
|
1634
|
-
if (startupFailure)
|
|
1635
|
-
return { block: true, reason: startupErrorText(startupFailure) };
|
|
1636
|
-
// Defense in depth against a host/extension restoring a stale tool list.
|
|
1637
|
-
if (
|
|
1638
|
-
activePrompt === "abel-design" &&
|
|
1639
|
-
event.toolName !== DISPATCH_TOOL &&
|
|
1640
|
-
!DESIGN_PARENT_READ_TOOLS.has(event.toolName)
|
|
1641
|
-
)
|
|
1642
|
-
return { block: true, reason: "abel-design-tool-boundary" };
|
|
1643
|
-
});
|
|
1644
|
-
pi.on("before_provider_request", (event, ctx) => {
|
|
1645
|
-
if (
|
|
1646
|
-
activePrompt !== "abel-design" ||
|
|
1647
|
-
ctx.model?.api !== "openai-responses" ||
|
|
1648
|
-
event.payload === null ||
|
|
1649
|
-
typeof event.payload !== "object" ||
|
|
1650
|
-
Array.isArray(event.payload)
|
|
1651
|
-
) {
|
|
1652
|
-
return;
|
|
1653
|
-
}
|
|
1654
|
-
return { ...event.payload, parallel_tool_calls: true };
|
|
1655
|
-
});
|
|
1656
|
-
pi.on("agent_end", async (event, ctx) => {
|
|
1657
|
-
// The host may replace its current signal after abort/settlement. Retain
|
|
1658
|
-
// this run's signal so a late status result cannot revive an aborted turn.
|
|
1659
|
-
const runSignal = ctx.signal;
|
|
1660
|
-
const aborted = () => runSignal?.aborted || ctx.signal?.aborted;
|
|
1661
|
-
if (
|
|
1662
|
-
aborted() ||
|
|
1663
|
-
startupFailure ||
|
|
1664
|
-
exitingStage ||
|
|
1665
|
-
!activation.isActive() ||
|
|
1666
|
-
activePrompt !== "abel-implement"
|
|
1667
|
-
) {
|
|
1668
|
-
return;
|
|
1669
|
-
}
|
|
1670
|
-
if (
|
|
1671
|
-
typeof ctx.hasPendingMessages === "function" &&
|
|
1672
|
-
ctx.hasPendingMessages()
|
|
1673
|
-
) {
|
|
1674
|
-
return;
|
|
1675
|
-
}
|
|
1676
|
-
const probe = implementContinuation.prepareSettlement({
|
|
1677
|
-
cwd: ctx.cwd,
|
|
1678
|
-
messages: event.messages,
|
|
1679
|
-
});
|
|
1680
|
-
if (!probe) return;
|
|
1681
|
-
|
|
1682
|
-
let status: Record<string, unknown>;
|
|
6
|
+
/** Sole production composition. Discovery registers inert capabilities only;
|
|
7
|
+
* actual host binding belongs to the lightweight shell’s session_start. */
|
|
8
|
+
export default function register(pi: ExtensionAPI): void {
|
|
9
|
+
if (process.env.CADENCE_SUBAGENT_CHILD === "1") return;
|
|
10
|
+
registerTaskShell(pi);
|
|
11
|
+
pi.on("session_start", (_event, ctx) => {
|
|
12
|
+
if (!ctx.hasUI || (ctx.mode !== "tui" && ctx.mode !== "rpc")) return;
|
|
13
|
+
// Preserve independent, nonactivating configuration presentation. This
|
|
14
|
+
// creates nothing and cannot discharge resources or override shell refusal.
|
|
1683
15
|
try {
|
|
1684
|
-
const
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
}
|
|
1693
|
-
status = await engine.execute(
|
|
1694
|
-
{
|
|
1695
|
-
command: "status",
|
|
1696
|
-
stage: "abel-implement",
|
|
1697
|
-
change: probe.change,
|
|
1698
|
-
},
|
|
1699
|
-
ctx,
|
|
1700
|
-
runSignal,
|
|
16
|
+
const lines = startupCardLines({
|
|
17
|
+
cwd: ctx.cwd,
|
|
18
|
+
home: os.homedir(),
|
|
19
|
+
projectTrusted: ctx.isProjectTrusted(),
|
|
20
|
+
});
|
|
21
|
+
ctx.ui.notify(
|
|
22
|
+
(lines.length > 1 ? lines.slice(1) : lines).join("\n"),
|
|
23
|
+
"info",
|
|
1701
24
|
);
|
|
1702
|
-
if (
|
|
1703
|
-
aborted() ||
|
|
1704
|
-
(typeof ctx.hasPendingMessages === "function" &&
|
|
1705
|
-
ctx.hasPendingMessages())
|
|
1706
|
-
) {
|
|
1707
|
-
return;
|
|
1708
|
-
}
|
|
1709
25
|
} catch {
|
|
1710
|
-
//
|
|
1711
|
-
return;
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
|
-
const decision = implementContinuation.finishSettlement(probe, status);
|
|
1715
|
-
if (decision?.kind === "terminal") {
|
|
1716
|
-
await deactivateStage();
|
|
1717
|
-
return;
|
|
26
|
+
// Presentation only; shell lifecycle still runs.
|
|
1718
27
|
}
|
|
1719
|
-
if (!decision) return;
|
|
1720
|
-
if (
|
|
1721
|
-
aborted() ||
|
|
1722
|
-
(typeof ctx.hasPendingMessages === "function" && ctx.hasPendingMessages())
|
|
1723
|
-
) {
|
|
1724
|
-
return;
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
if (decision.kind === "stalled") {
|
|
1728
|
-
pi.sendMessage(
|
|
1729
|
-
{
|
|
1730
|
-
customType: "abel-implement-continuation-stalled",
|
|
1731
|
-
content: [{ type: "text", text: decision.message }],
|
|
1732
|
-
display: true,
|
|
1733
|
-
details: {
|
|
1734
|
-
code: decision.code,
|
|
1735
|
-
change: decision.change,
|
|
1736
|
-
state: decision.state,
|
|
1737
|
-
statusFingerprint: decision.statusFingerprint,
|
|
1738
|
-
},
|
|
1739
|
-
},
|
|
1740
|
-
{ triggerTurn: false },
|
|
1741
|
-
);
|
|
1742
|
-
return;
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
|
-
pi.sendMessage(
|
|
1746
|
-
{
|
|
1747
|
-
customType: "abel-implement-continuation",
|
|
1748
|
-
content: [{ type: "text", text: decision.message }],
|
|
1749
|
-
display: false,
|
|
1750
|
-
details: {
|
|
1751
|
-
change: decision.change,
|
|
1752
|
-
statusFingerprint: decision.statusFingerprint,
|
|
1753
|
-
},
|
|
1754
|
-
},
|
|
1755
|
-
{ triggerTurn: true, deliverAs: "followUp" },
|
|
1756
|
-
);
|
|
1757
|
-
});
|
|
1758
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
1759
|
-
startupFailure = undefined;
|
|
1760
|
-
admittedPrompt = undefined;
|
|
1761
|
-
latestContextUser = undefined;
|
|
1762
|
-
admittedMessages.clear();
|
|
1763
|
-
pendingPrompt = undefined;
|
|
1764
|
-
pendingImplementChange = undefined;
|
|
1765
|
-
pendingInit = false;
|
|
1766
|
-
activePrompt = undefined;
|
|
1767
|
-
implementContinuation.deactivate();
|
|
1768
|
-
designFailures.clear();
|
|
1769
|
-
activity.detach();
|
|
1770
|
-
await packetRuntime.drain();
|
|
1771
|
-
await closeEngines();
|
|
1772
|
-
deactivate();
|
|
1773
|
-
if (ctx.mode === "tui") activity.attach(ctx.ui);
|
|
1774
|
-
});
|
|
1775
|
-
pi.on("session_shutdown", async () => {
|
|
1776
|
-
startupFailure = undefined;
|
|
1777
|
-
admittedPrompt = undefined;
|
|
1778
|
-
latestContextUser = undefined;
|
|
1779
|
-
admittedMessages.clear();
|
|
1780
|
-
pendingPrompt = undefined;
|
|
1781
|
-
pendingImplementChange = undefined;
|
|
1782
|
-
pendingInit = false;
|
|
1783
|
-
activePrompt = undefined;
|
|
1784
|
-
implementContinuation.deactivate();
|
|
1785
|
-
designFailures.clear();
|
|
1786
|
-
activity.detach();
|
|
1787
|
-
await packetRuntime.drain();
|
|
1788
|
-
await closeEngines();
|
|
1789
|
-
activity.clear();
|
|
1790
|
-
deactivate();
|
|
1791
|
-
});
|
|
1792
|
-
}
|
|
1793
|
-
|
|
1794
|
-
export default function register(pi: ExtensionAPI): void {
|
|
1795
|
-
// Process children are deliberately started without extensions as a second
|
|
1796
|
-
// defense. Keep this guard for hosts that inject the extension explicitly.
|
|
1797
|
-
if (process.env.CADENCE_SUBAGENT_CHILD === "1") return;
|
|
1798
|
-
registerWorkflowControl(pi);
|
|
1799
|
-
// Presentation only: use the host's subdued one-shot notification rather
|
|
1800
|
-
// than a persistent widget; never persist a dismissal or inject a model message.
|
|
1801
|
-
pi.on("session_start", (_event, ctx) => {
|
|
1802
|
-
if (!ctx.hasUI) return;
|
|
1803
|
-
const lines = startupCardLines({
|
|
1804
|
-
cwd: ctx.cwd,
|
|
1805
|
-
home: homedir(),
|
|
1806
|
-
projectTrusted: ctx.isProjectTrusted(),
|
|
1807
|
-
});
|
|
1808
|
-
ctx.ui.notify(lines.at(-1) ?? "Cadence研究配置已加载。", "info");
|
|
1809
28
|
});
|
|
1810
29
|
}
|