@abelxiaoxing/cadence 1.0.2 → 1.0.4
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/agents/contract-reviewer.md +2 -2
- package/agents/diagnosis-worker.md +3 -2
- package/agents/implementation-worker.md +3 -2
- package/config/.env.example +16 -1
- package/package.json +1 -1
- package/prompts/abel-design.md +13 -3
- package/prompts/abel-implement.md +50 -21
- package/skills/_shared/load-config.d.mts +7 -0
- package/skills/abel-workflow/SKILL.md +57 -25
- package/src/candidate-preflight.ts +361 -56
- package/src/child-session.ts +92 -27
- package/src/contracts.ts +994 -38
- package/src/index.ts +50 -24
- package/src/parent-provider.ts +206 -23
- package/src/patch.ts +269 -41
- package/src/result-store.ts +254 -16
- package/src/runtime.ts +1336 -560
- package/src/scheduler.ts +59 -16
- package/src/subagent-activity.ts +56 -25
- package/src/subagent-endpoint.ts +158 -0
- package/src/submit-tool.ts +12 -2
- package/src/worker.ts +121 -195
package/src/worker.ts
CHANGED
|
@@ -1,163 +1,94 @@
|
|
|
1
|
-
// Process-local
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
conflicts: string[];
|
|
44
|
-
resources: string[];
|
|
45
|
-
verificationLock?: string;
|
|
46
|
-
snapshot?: unknown;
|
|
47
|
-
verification?: VerificationContract;
|
|
48
|
-
correctionIndex: 0 | 1;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface LogicalWorker {
|
|
52
|
-
identity: string;
|
|
53
|
-
taskContract: WorkerTaskContract;
|
|
54
|
-
currentPhase: WorkerPhaseContract;
|
|
55
|
-
state: WorkerState;
|
|
1
|
+
// Process-local task registry for the private Implement runtime.
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
CandidateFailure,
|
|
5
|
+
ImplementationPhase,
|
|
6
|
+
PhaseAttempt,
|
|
7
|
+
TaskBoundary,
|
|
8
|
+
TaskFailure,
|
|
9
|
+
} from "./contracts.ts";
|
|
10
|
+
import type { ConflictDeclaration } from "./scheduler.ts";
|
|
11
|
+
import type { SubagentEndpoint } from "./subagent-endpoint.ts";
|
|
12
|
+
|
|
13
|
+
export type TaskState =
|
|
14
|
+
| {
|
|
15
|
+
kind: "ready";
|
|
16
|
+
phase: ImplementationPhase;
|
|
17
|
+
launchIndex: 0 | 1;
|
|
18
|
+
correction?: CandidateFailure;
|
|
19
|
+
}
|
|
20
|
+
| {
|
|
21
|
+
kind: "candidate-pending";
|
|
22
|
+
phase: ImplementationPhase;
|
|
23
|
+
launchIndex: 0 | 1;
|
|
24
|
+
originRequestId: string;
|
|
25
|
+
resultId: string;
|
|
26
|
+
}
|
|
27
|
+
| {
|
|
28
|
+
kind: "agents-checkpoint-pending";
|
|
29
|
+
finalPhase: "green" | "refactor";
|
|
30
|
+
attemptIndex: 0 | 1;
|
|
31
|
+
}
|
|
32
|
+
| { kind: "blocked"; phase: ImplementationPhase; failure: TaskFailure }
|
|
33
|
+
| { kind: "completed"; finalPhase: "green" | "refactor" };
|
|
34
|
+
|
|
35
|
+
export interface TaskRecord {
|
|
36
|
+
key: string;
|
|
37
|
+
workspaceRoot: string;
|
|
38
|
+
workerIdentity: string;
|
|
39
|
+
subagentEndpoint: Readonly<SubagentEndpoint> | null;
|
|
40
|
+
boundary: TaskBoundary;
|
|
41
|
+
conflict: ConflictDeclaration;
|
|
42
|
+
state: TaskState;
|
|
56
43
|
}
|
|
57
44
|
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
| { kind: "blocked"; reason: "artifact" | "mechanical" };
|
|
65
|
-
|
|
66
|
-
export function contractOf(envelope: RequestEnvelope): WorkerContract {
|
|
67
|
-
return {
|
|
68
|
-
stage: envelope.stage,
|
|
69
|
-
role: envelope.role,
|
|
70
|
-
taskId: envelope.taskId ?? envelope.id,
|
|
71
|
-
objective: envelope.objective,
|
|
72
|
-
roots: [...envelope.roots],
|
|
73
|
-
agents: envelope.context.agents,
|
|
74
|
-
contractText: envelope.context.contract,
|
|
75
|
-
read: [...envelope.declared.read],
|
|
76
|
-
conflicts: [...envelope.declared.conflicts],
|
|
77
|
-
resources: [...envelope.declared.resources],
|
|
78
|
-
output: envelope.output,
|
|
79
|
-
currentPhase: phaseOf(envelope, 0),
|
|
80
|
-
};
|
|
45
|
+
export function taskRecordKey(
|
|
46
|
+
workspaceRoot: string,
|
|
47
|
+
changeId: string,
|
|
48
|
+
taskId: string,
|
|
49
|
+
): string {
|
|
50
|
+
return `${workspaceRoot}\0${changeId}\0${taskId}`;
|
|
81
51
|
}
|
|
82
52
|
|
|
83
|
-
export function
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
a.contractText !== b.contractText ||
|
|
94
|
-
a.output !== b.output
|
|
95
|
-
)
|
|
96
|
-
return false;
|
|
97
|
-
return (
|
|
98
|
-
arrayEqual(a.roots, b.roots) &&
|
|
99
|
-
arrayEqual(a.read, b.read) &&
|
|
100
|
-
arrayEqual(a.conflicts, b.conflicts) &&
|
|
101
|
-
arrayEqual(a.resources, b.resources)
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function samePhaseContract(
|
|
106
|
-
a: WorkerPhaseContract,
|
|
107
|
-
b: WorkerPhaseContract,
|
|
108
|
-
): boolean {
|
|
109
|
-
return (
|
|
110
|
-
a.phase === b.phase &&
|
|
111
|
-
arrayEqual(a.readSet, b.readSet) &&
|
|
112
|
-
arrayEqual(a.writeSet, b.writeSet) &&
|
|
113
|
-
arrayEqual(a.conflicts, b.conflicts) &&
|
|
114
|
-
arrayEqual(a.resources, b.resources) &&
|
|
115
|
-
a.verificationLock === b.verificationLock &&
|
|
116
|
-
JSON.stringify(a.verification) === JSON.stringify(b.verification)
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function phaseOf(
|
|
121
|
-
envelope: RequestEnvelope,
|
|
122
|
-
correctionIndex: 0 | 1,
|
|
123
|
-
): WorkerPhaseContract {
|
|
53
|
+
export function taskConflictOf(boundary: TaskBoundary): ConflictDeclaration {
|
|
54
|
+
const phases = [
|
|
55
|
+
boundary.phases.red,
|
|
56
|
+
boundary.phases.green,
|
|
57
|
+
...(boundary.phases.refactor ? [boundary.phases.refactor] : []),
|
|
58
|
+
];
|
|
59
|
+
const write = new Set(phases.flatMap((phase) => phase.write));
|
|
60
|
+
if (boundary.agents.impact !== "none" && boundary.agents.target) {
|
|
61
|
+
write.add(boundary.agents.target);
|
|
62
|
+
}
|
|
124
63
|
return {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
envelope.verification === undefined
|
|
138
|
-
? undefined
|
|
139
|
-
: {
|
|
140
|
-
...envelope.verification,
|
|
141
|
-
argv: [...envelope.verification.argv],
|
|
142
|
-
},
|
|
143
|
-
correctionIndex,
|
|
64
|
+
taskId: boundary.taskId,
|
|
65
|
+
read: [...new Set(phases.flatMap((phase) => phase.read))],
|
|
66
|
+
write: [...write],
|
|
67
|
+
conflicts: [...boundary.scheduling.conflicts],
|
|
68
|
+
resources: [...boundary.scheduling.resources],
|
|
69
|
+
verificationLocks: [
|
|
70
|
+
...new Set(
|
|
71
|
+
phases.flatMap((phase) =>
|
|
72
|
+
phase.verificationLock ? [phase.verificationLock] : [],
|
|
73
|
+
),
|
|
74
|
+
),
|
|
75
|
+
],
|
|
144
76
|
};
|
|
145
77
|
}
|
|
146
78
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Stable provider/model/API/thinking identity fingerprint for a phase. Each
|
|
152
|
-
// child attempt re-derives it while the phase-local auth boundary is called
|
|
153
|
-
// anew, so the identity stays pinned across fresh phases.
|
|
79
|
+
// Stable provider/model/API identity fingerprint. Fresh auth may be acquired
|
|
80
|
+
// for each phase, while the selected execution identity remains pinned.
|
|
154
81
|
export function workerIdentity(model: {
|
|
155
82
|
provider?: unknown;
|
|
83
|
+
api?: unknown;
|
|
156
84
|
baseUrl?: unknown;
|
|
157
85
|
id?: unknown;
|
|
158
86
|
name?: unknown;
|
|
159
87
|
}): string {
|
|
160
|
-
const parts: string[] = [
|
|
88
|
+
const parts: string[] = [
|
|
89
|
+
`p:${String(model.provider ?? "")}`,
|
|
90
|
+
`a:${String(model.api ?? "")}`,
|
|
91
|
+
];
|
|
161
92
|
if (model.baseUrl !== undefined) parts.push(`u:${String(model.baseUrl)}`);
|
|
162
93
|
if (typeof model.id === "string") parts.push(`id:${model.id}`);
|
|
163
94
|
if (typeof model.name === "string") parts.push(`n:${model.name}`);
|
|
@@ -165,65 +96,60 @@ export function workerIdentity(model: {
|
|
|
165
96
|
}
|
|
166
97
|
|
|
167
98
|
export class WorkerRegistry {
|
|
168
|
-
private readonly
|
|
99
|
+
private readonly tasks = new Map<string, TaskRecord>();
|
|
169
100
|
|
|
170
|
-
has(
|
|
171
|
-
return this.
|
|
101
|
+
has(key: string): boolean {
|
|
102
|
+
return this.tasks.has(key);
|
|
172
103
|
}
|
|
173
104
|
|
|
174
|
-
get(
|
|
175
|
-
return this.
|
|
105
|
+
get(key: string): TaskRecord | undefined {
|
|
106
|
+
return this.tasks.get(key);
|
|
176
107
|
}
|
|
177
108
|
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
};
|
|
186
|
-
this.workers.set(contract.taskId, worker);
|
|
187
|
-
return worker;
|
|
109
|
+
find(workspaceRoot: string, taskId: string): TaskRecord | undefined {
|
|
110
|
+
const matches = [...this.tasks.values()].filter(
|
|
111
|
+
(record) =>
|
|
112
|
+
record.workspaceRoot === workspaceRoot &&
|
|
113
|
+
record.boundary.taskId === taskId,
|
|
114
|
+
);
|
|
115
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
188
116
|
}
|
|
189
117
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
118
|
+
open(
|
|
119
|
+
boundary: TaskBoundary,
|
|
120
|
+
identity: string,
|
|
121
|
+
workspaceRoot: string,
|
|
122
|
+
attempt: PhaseAttempt,
|
|
123
|
+
subagentEndpoint: Readonly<SubagentEndpoint> | null = null,
|
|
124
|
+
): TaskRecord {
|
|
125
|
+
const key = taskRecordKey(
|
|
126
|
+
workspaceRoot,
|
|
127
|
+
boundary.changeId,
|
|
128
|
+
boundary.taskId,
|
|
129
|
+
);
|
|
130
|
+
if (this.tasks.has(key)) throw new Error("duplicate task open");
|
|
131
|
+
const storedBoundary = structuredClone(boundary);
|
|
132
|
+
const record: TaskRecord = {
|
|
133
|
+
key,
|
|
134
|
+
workspaceRoot,
|
|
135
|
+
workerIdentity: identity,
|
|
136
|
+
subagentEndpoint:
|
|
137
|
+
subagentEndpoint === null
|
|
138
|
+
? null
|
|
139
|
+
: Object.freeze({ ...subagentEndpoint }),
|
|
140
|
+
boundary: storedBoundary,
|
|
141
|
+
conflict: taskConflictOf(storedBoundary),
|
|
142
|
+
state: { kind: "ready", phase: attempt.phase, launchIndex: 0 },
|
|
200
143
|
};
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return worker;
|
|
144
|
+
this.tasks.set(key, record);
|
|
145
|
+
return record;
|
|
204
146
|
}
|
|
205
147
|
|
|
206
|
-
|
|
207
|
-
this.
|
|
148
|
+
values(): TaskRecord[] {
|
|
149
|
+
return [...this.tasks.values()];
|
|
208
150
|
}
|
|
209
|
-
}
|
|
210
151
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
role: contract.role,
|
|
215
|
-
taskId: contract.taskId,
|
|
216
|
-
objective: contract.objective,
|
|
217
|
-
roots: [...contract.roots],
|
|
218
|
-
agents: contract.agents,
|
|
219
|
-
contractText: contract.contractText,
|
|
220
|
-
read: [...contract.read],
|
|
221
|
-
conflicts: [...contract.conflicts],
|
|
222
|
-
resources: [...contract.resources],
|
|
223
|
-
output: contract.output,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function clonePhase(phase: WorkerPhaseContract): WorkerPhaseContract {
|
|
228
|
-
return structuredClone(phase);
|
|
152
|
+
clear(): void {
|
|
153
|
+
this.tasks.clear();
|
|
154
|
+
}
|
|
229
155
|
}
|