@abelxiaoxing/cadence 1.0.2 → 1.0.3
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/package.json +1 -1
- package/prompts/abel-design.md +13 -3
- package/prompts/abel-implement.md +50 -21
- 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 +987 -38
- package/src/index.ts +50 -24
- package/src/parent-provider.ts +89 -23
- package/src/patch.ts +269 -41
- package/src/result-store.ts +254 -16
- package/src/runtime.ts +1281 -560
- package/src/scheduler.ts +59 -16
- package/src/subagent-activity.ts +56 -25
- package/src/submit-tool.ts +12 -2
- package/src/worker.ts +114 -195
package/src/worker.ts
CHANGED
|
@@ -1,163 +1,92 @@
|
|
|
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
|
-
readSet: string[];
|
|
42
|
-
writeSet: string[];
|
|
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
|
+
|
|
12
|
+
export type TaskState =
|
|
13
|
+
| {
|
|
14
|
+
kind: "ready";
|
|
15
|
+
phase: ImplementationPhase;
|
|
16
|
+
launchIndex: 0 | 1;
|
|
17
|
+
correction?: CandidateFailure;
|
|
18
|
+
}
|
|
19
|
+
| {
|
|
20
|
+
kind: "candidate-pending";
|
|
21
|
+
phase: ImplementationPhase;
|
|
22
|
+
launchIndex: 0 | 1;
|
|
23
|
+
originRequestId: string;
|
|
24
|
+
resultId: string;
|
|
25
|
+
}
|
|
26
|
+
| {
|
|
27
|
+
kind: "agents-checkpoint-pending";
|
|
28
|
+
finalPhase: "green" | "refactor";
|
|
29
|
+
attemptIndex: 0 | 1;
|
|
30
|
+
}
|
|
31
|
+
| { kind: "blocked"; phase: ImplementationPhase; failure: TaskFailure }
|
|
32
|
+
| { kind: "completed"; finalPhase: "green" | "refactor" };
|
|
33
|
+
|
|
34
|
+
export interface TaskRecord {
|
|
35
|
+
key: string;
|
|
36
|
+
workspaceRoot: string;
|
|
37
|
+
workerIdentity: string;
|
|
38
|
+
boundary: TaskBoundary;
|
|
39
|
+
conflict: ConflictDeclaration;
|
|
40
|
+
state: TaskState;
|
|
56
41
|
}
|
|
57
42
|
|
|
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
|
-
};
|
|
43
|
+
export function taskRecordKey(
|
|
44
|
+
workspaceRoot: string,
|
|
45
|
+
changeId: string,
|
|
46
|
+
taskId: string,
|
|
47
|
+
): string {
|
|
48
|
+
return `${workspaceRoot}\0${changeId}\0${taskId}`;
|
|
81
49
|
}
|
|
82
50
|
|
|
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 {
|
|
51
|
+
export function taskConflictOf(boundary: TaskBoundary): ConflictDeclaration {
|
|
52
|
+
const phases = [
|
|
53
|
+
boundary.phases.red,
|
|
54
|
+
boundary.phases.green,
|
|
55
|
+
...(boundary.phases.refactor ? [boundary.phases.refactor] : []),
|
|
56
|
+
];
|
|
57
|
+
const write = new Set(phases.flatMap((phase) => phase.write));
|
|
58
|
+
if (boundary.agents.impact !== "none" && boundary.agents.target) {
|
|
59
|
+
write.add(boundary.agents.target);
|
|
60
|
+
}
|
|
124
61
|
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,
|
|
62
|
+
taskId: boundary.taskId,
|
|
63
|
+
read: [...new Set(phases.flatMap((phase) => phase.read))],
|
|
64
|
+
write: [...write],
|
|
65
|
+
conflicts: [...boundary.scheduling.conflicts],
|
|
66
|
+
resources: [...boundary.scheduling.resources],
|
|
67
|
+
verificationLocks: [
|
|
68
|
+
...new Set(
|
|
69
|
+
phases.flatMap((phase) =>
|
|
70
|
+
phase.verificationLock ? [phase.verificationLock] : [],
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
],
|
|
144
74
|
};
|
|
145
75
|
}
|
|
146
76
|
|
|
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.
|
|
77
|
+
// Stable provider/model/API identity fingerprint. Fresh auth may be acquired
|
|
78
|
+
// for each phase, while the selected execution identity remains pinned.
|
|
154
79
|
export function workerIdentity(model: {
|
|
155
80
|
provider?: unknown;
|
|
81
|
+
api?: unknown;
|
|
156
82
|
baseUrl?: unknown;
|
|
157
83
|
id?: unknown;
|
|
158
84
|
name?: unknown;
|
|
159
85
|
}): string {
|
|
160
|
-
const parts: string[] = [
|
|
86
|
+
const parts: string[] = [
|
|
87
|
+
`p:${String(model.provider ?? "")}`,
|
|
88
|
+
`a:${String(model.api ?? "")}`,
|
|
89
|
+
];
|
|
161
90
|
if (model.baseUrl !== undefined) parts.push(`u:${String(model.baseUrl)}`);
|
|
162
91
|
if (typeof model.id === "string") parts.push(`id:${model.id}`);
|
|
163
92
|
if (typeof model.name === "string") parts.push(`n:${model.name}`);
|
|
@@ -165,65 +94,55 @@ export function workerIdentity(model: {
|
|
|
165
94
|
}
|
|
166
95
|
|
|
167
96
|
export class WorkerRegistry {
|
|
168
|
-
private readonly
|
|
97
|
+
private readonly tasks = new Map<string, TaskRecord>();
|
|
169
98
|
|
|
170
|
-
has(
|
|
171
|
-
return this.
|
|
99
|
+
has(key: string): boolean {
|
|
100
|
+
return this.tasks.has(key);
|
|
172
101
|
}
|
|
173
102
|
|
|
174
|
-
get(
|
|
175
|
-
return this.
|
|
103
|
+
get(key: string): TaskRecord | undefined {
|
|
104
|
+
return this.tasks.get(key);
|
|
176
105
|
}
|
|
177
106
|
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
};
|
|
186
|
-
this.workers.set(contract.taskId, worker);
|
|
187
|
-
return worker;
|
|
107
|
+
find(workspaceRoot: string, taskId: string): TaskRecord | undefined {
|
|
108
|
+
const matches = [...this.tasks.values()].filter(
|
|
109
|
+
(record) =>
|
|
110
|
+
record.workspaceRoot === workspaceRoot &&
|
|
111
|
+
record.boundary.taskId === taskId,
|
|
112
|
+
);
|
|
113
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
188
114
|
}
|
|
189
115
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
116
|
+
open(
|
|
117
|
+
boundary: TaskBoundary,
|
|
118
|
+
identity: string,
|
|
119
|
+
workspaceRoot: string,
|
|
120
|
+
attempt: PhaseAttempt,
|
|
121
|
+
): TaskRecord {
|
|
122
|
+
const key = taskRecordKey(
|
|
123
|
+
workspaceRoot,
|
|
124
|
+
boundary.changeId,
|
|
125
|
+
boundary.taskId,
|
|
126
|
+
);
|
|
127
|
+
if (this.tasks.has(key)) throw new Error("duplicate task open");
|
|
128
|
+
const storedBoundary = structuredClone(boundary);
|
|
129
|
+
const record: TaskRecord = {
|
|
130
|
+
key,
|
|
131
|
+
workspaceRoot,
|
|
132
|
+
workerIdentity: identity,
|
|
133
|
+
boundary: storedBoundary,
|
|
134
|
+
conflict: taskConflictOf(storedBoundary),
|
|
135
|
+
state: { kind: "ready", phase: attempt.phase, launchIndex: 0 },
|
|
200
136
|
};
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return worker;
|
|
137
|
+
this.tasks.set(key, record);
|
|
138
|
+
return record;
|
|
204
139
|
}
|
|
205
140
|
|
|
206
|
-
|
|
207
|
-
this.
|
|
141
|
+
values(): TaskRecord[] {
|
|
142
|
+
return [...this.tasks.values()];
|
|
208
143
|
}
|
|
209
|
-
}
|
|
210
144
|
|
|
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);
|
|
145
|
+
clear(): void {
|
|
146
|
+
this.tasks.clear();
|
|
147
|
+
}
|
|
229
148
|
}
|