@abelxiaoxing/cadence 1.8.2 → 1.8.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/README.md +71 -531
- package/agents/design-explorer.md +15 -23
- package/agents/diagnosis-worker.md +12 -23
- package/agents/implementation-worker.md +16 -34
- package/config/.env.example +2 -5
- package/package.json +1 -1
- package/prompts/abel-diagnose.md +5 -5
- package/src/candidate-context.ts +289 -0
- package/src/contracts.ts +12 -22
- package/src/durable-contracts.ts +1 -5
- package/src/durable-workflow.ts +3 -159
- package/src/evidence-draft.ts +1 -1
- package/src/index.ts +12 -16
- package/src/package-candidate-process.ts +206 -0
- package/src/package-candidate.ts +11 -214
- package/src/package-workflow.ts +5 -42
- package/src/packet-runtime.ts +144 -255
- package/src/phase-execution.ts +134 -230
- package/src/startup-card.ts +17 -23
- package/src/subagent-process.ts +476 -0
- package/src/transport-budget.ts +1 -3
- package/src/workflow-policy.ts +9 -18
- package/src/workflow-state-machine.ts +1 -15
- package/config/routes.example.json +0 -39
- package/src/child-budget.ts +0 -58
- package/src/child-model.ts +0 -142
- package/src/child-session.ts +0 -640
- package/src/parent-provider.ts +0 -375
- package/src/route-policy.ts +0 -545
- package/src/submit-tool.ts +0 -950
- package/src/worker-broker.ts +0 -806
package/src/parent-provider.ts
DELETED
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createModels,
|
|
3
|
-
createProvider,
|
|
4
|
-
InMemoryCredentialStore,
|
|
5
|
-
lazyApi,
|
|
6
|
-
type Model,
|
|
7
|
-
type Models,
|
|
8
|
-
type Provider,
|
|
9
|
-
type StreamOptions,
|
|
10
|
-
} from "@earendil-works/pi-ai";
|
|
11
|
-
import type { ChildFailure } from "./contracts.ts";
|
|
12
|
-
import type { ParentModelSource } from "./model-source.ts";
|
|
13
|
-
import {
|
|
14
|
-
type CustomRoutePolicy,
|
|
15
|
-
type InheritedRoutePolicy,
|
|
16
|
-
type ResolvedCustomRoute,
|
|
17
|
-
resolveCustomRoute,
|
|
18
|
-
} from "./route-policy.ts";
|
|
19
|
-
|
|
20
|
-
const SUBAGENT_PROVIDER_ID = "abel-subagent";
|
|
21
|
-
|
|
22
|
-
export interface PhaseTransportObserver {
|
|
23
|
-
onResponse?(): void;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function observedStreamOptions<T extends StreamOptions>(
|
|
27
|
-
options?: T,
|
|
28
|
-
observer?: PhaseTransportObserver,
|
|
29
|
-
): T | undefined {
|
|
30
|
-
if (!observer?.onResponse) return options;
|
|
31
|
-
const childOnResponse = options?.onResponse;
|
|
32
|
-
return {
|
|
33
|
-
...options,
|
|
34
|
-
onResponse: async (response, requestModel) => {
|
|
35
|
-
observer.onResponse?.();
|
|
36
|
-
await childOnResponse?.(response, requestModel);
|
|
37
|
-
},
|
|
38
|
-
} as T;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Public API modules, selected explicitly by the closed route dialect. No
|
|
42
|
-
// global compat registry or provider-specific payload rewriting is involved.
|
|
43
|
-
const routeApis = {
|
|
44
|
-
"openai-completions": lazyApi(
|
|
45
|
-
() => import("@earendil-works/pi-ai/api/openai-completions"),
|
|
46
|
-
),
|
|
47
|
-
"openai-responses": lazyApi(
|
|
48
|
-
() => import("@earendil-works/pi-ai/api/openai-responses"),
|
|
49
|
-
),
|
|
50
|
-
"anthropic-messages": lazyApi(
|
|
51
|
-
() => import("@earendil-works/pi-ai/api/anthropic-messages"),
|
|
52
|
-
),
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
function subagentStreams(
|
|
56
|
-
dialect: ResolvedCustomRoute["dialect"],
|
|
57
|
-
observer?: PhaseTransportObserver,
|
|
58
|
-
) {
|
|
59
|
-
const api = routeApis[dialect];
|
|
60
|
-
return {
|
|
61
|
-
stream: ((model, context, options) =>
|
|
62
|
-
api.stream(
|
|
63
|
-
model,
|
|
64
|
-
context,
|
|
65
|
-
observedStreamOptions({ ...options, maxRetries: 0 }, observer),
|
|
66
|
-
)) as Provider["stream"],
|
|
67
|
-
streamSimple: ((model, context, options) =>
|
|
68
|
-
api.streamSimple(
|
|
69
|
-
model,
|
|
70
|
-
context,
|
|
71
|
-
observedStreamOptions({ ...options, maxRetries: 0 }, observer),
|
|
72
|
-
)) as Provider["streamSimple"],
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function modelsForProvider(provider: Provider, signal?: AbortSignal): Models {
|
|
77
|
-
signal?.throwIfAborted();
|
|
78
|
-
const models = createModels({ credentials: new InMemoryCredentialStore() });
|
|
79
|
-
models.setProvider(provider);
|
|
80
|
-
return models;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function customSubagentModel(endpoint: ResolvedCustomRoute): Model<string> {
|
|
84
|
-
return {
|
|
85
|
-
id: endpoint.model,
|
|
86
|
-
name: endpoint.model,
|
|
87
|
-
api: endpoint.dialect,
|
|
88
|
-
provider: SUBAGENT_PROVIDER_ID,
|
|
89
|
-
baseUrl: endpoint.url,
|
|
90
|
-
reasoning: true,
|
|
91
|
-
input: ["text"],
|
|
92
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
93
|
-
contextWindow: endpoint.contextWindow,
|
|
94
|
-
maxTokens: endpoint.maxTokens,
|
|
95
|
-
...(endpoint.dialect === "openai-completions"
|
|
96
|
-
? { compat: { supportsDeveloperRole: false } }
|
|
97
|
-
: {}),
|
|
98
|
-
} as Model<string>;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function customSubagentProvider(
|
|
102
|
-
endpoint: ResolvedCustomRoute,
|
|
103
|
-
model: Model<string>,
|
|
104
|
-
observer?: PhaseTransportObserver,
|
|
105
|
-
): Provider {
|
|
106
|
-
return createProvider({
|
|
107
|
-
id: SUBAGENT_PROVIDER_ID,
|
|
108
|
-
name: "Abel Subagent Endpoint",
|
|
109
|
-
baseUrl: endpoint.url,
|
|
110
|
-
auth: {
|
|
111
|
-
apiKey: {
|
|
112
|
-
name: "Abel subagent endpoint auth",
|
|
113
|
-
resolve: async () =>
|
|
114
|
-
endpoint.apiKey
|
|
115
|
-
? { auth: { apiKey: endpoint.apiKey }, source: "subagent-endpoint" }
|
|
116
|
-
: {
|
|
117
|
-
auth: {
|
|
118
|
-
apiKey: "unused",
|
|
119
|
-
headers: { authorization: null, "x-api-key": null },
|
|
120
|
-
},
|
|
121
|
-
source: "subagent-endpoint-keyless",
|
|
122
|
-
},
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
models: [model],
|
|
126
|
-
api: subagentStreams(endpoint.dialect, observer),
|
|
127
|
-
}) as Provider;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export async function customPhaseRuntime(
|
|
131
|
-
route: CustomRoutePolicy,
|
|
132
|
-
signal?: AbortSignal,
|
|
133
|
-
environment: Readonly<Record<string, string | undefined>> = process.env,
|
|
134
|
-
observer?: PhaseTransportObserver,
|
|
135
|
-
): Promise<PhaseRuntimeResult> {
|
|
136
|
-
if (signal?.aborted) return cancelledPhaseRuntime();
|
|
137
|
-
const endpoint = resolveCustomRoute(route, environment);
|
|
138
|
-
const model = customSubagentModel(endpoint);
|
|
139
|
-
const provider = customSubagentProvider(endpoint, model, observer);
|
|
140
|
-
let modelRuntime: Models;
|
|
141
|
-
try {
|
|
142
|
-
modelRuntime = modelsForProvider(provider, signal);
|
|
143
|
-
} catch (error) {
|
|
144
|
-
if (isCancellation(error, signal)) return cancelledPhaseRuntime();
|
|
145
|
-
throw error;
|
|
146
|
-
}
|
|
147
|
-
if (signal?.aborted) return cancelledPhaseRuntime();
|
|
148
|
-
return { ok: true, modelRuntime, model };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
type PhaseRuntimeFailure = Extract<
|
|
152
|
-
ChildFailure,
|
|
153
|
-
{ kind: "cancelled" | "environment" | "transport" }
|
|
154
|
-
>;
|
|
155
|
-
|
|
156
|
-
export type PhaseRuntimeResult =
|
|
157
|
-
| {
|
|
158
|
-
ok: true;
|
|
159
|
-
modelRuntime: Models;
|
|
160
|
-
model: Model<string>;
|
|
161
|
-
}
|
|
162
|
-
| { ok: false; error: string; failure: PhaseRuntimeFailure };
|
|
163
|
-
|
|
164
|
-
function phaseRuntimeFailure(
|
|
165
|
-
failure: PhaseRuntimeFailure,
|
|
166
|
-
error: string,
|
|
167
|
-
): Extract<PhaseRuntimeResult, { ok: false }> {
|
|
168
|
-
return { ok: false, error, failure };
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function cancelledPhaseRuntime(): Extract<PhaseRuntimeResult, { ok: false }> {
|
|
172
|
-
return phaseRuntimeFailure(
|
|
173
|
-
{ kind: "cancelled", code: "cancelled" },
|
|
174
|
-
"child phase cancelled",
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function isCancellation(error: unknown, signal?: AbortSignal): boolean {
|
|
179
|
-
return signal?.aborted === true && error === signal.reason;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function phaseProvider(
|
|
183
|
-
parent: Provider,
|
|
184
|
-
auth: {
|
|
185
|
-
apiKey?: string;
|
|
186
|
-
headers?: Record<string, string | null>;
|
|
187
|
-
baseUrl?: string;
|
|
188
|
-
env?: Record<string, string>;
|
|
189
|
-
},
|
|
190
|
-
observer?: PhaseTransportObserver,
|
|
191
|
-
): Provider {
|
|
192
|
-
auth = structuredClone(auth);
|
|
193
|
-
return {
|
|
194
|
-
id: parent.id,
|
|
195
|
-
name: parent.name,
|
|
196
|
-
getModels: () => parent.getModels(),
|
|
197
|
-
baseUrl: auth.baseUrl ?? parent.baseUrl,
|
|
198
|
-
headers: { ...parent.headers, ...auth.headers },
|
|
199
|
-
auth: {
|
|
200
|
-
apiKey: {
|
|
201
|
-
name: "Abel phase-local parent auth",
|
|
202
|
-
resolve: async () => ({
|
|
203
|
-
auth: {
|
|
204
|
-
apiKey: auth.apiKey,
|
|
205
|
-
headers: auth.headers,
|
|
206
|
-
baseUrl: auth.baseUrl,
|
|
207
|
-
},
|
|
208
|
-
env: auth.env,
|
|
209
|
-
}),
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
stream(model, context, options) {
|
|
213
|
-
return parent.stream(model, context, {
|
|
214
|
-
...observedStreamOptions(options, observer),
|
|
215
|
-
apiKey: auth.apiKey,
|
|
216
|
-
headers: { ...options?.headers, ...auth.headers },
|
|
217
|
-
env: { ...options?.env, ...auth.env },
|
|
218
|
-
maxRetries: 0,
|
|
219
|
-
} as never);
|
|
220
|
-
},
|
|
221
|
-
streamSimple(model, context, options) {
|
|
222
|
-
return parent.streamSimple(model, context, {
|
|
223
|
-
...observedStreamOptions(options, observer),
|
|
224
|
-
apiKey: auth.apiKey,
|
|
225
|
-
headers: { ...options?.headers, ...auth.headers },
|
|
226
|
-
env: { ...options?.env, ...auth.env },
|
|
227
|
-
maxRetries: 0,
|
|
228
|
-
});
|
|
229
|
-
},
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function modelKeyFor(model: Model<string>) {
|
|
234
|
-
return {
|
|
235
|
-
provider: model.provider,
|
|
236
|
-
id: model.id,
|
|
237
|
-
api: model.api,
|
|
238
|
-
baseUrl: model.baseUrl,
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function sameSelectedModel(
|
|
243
|
-
left: Model<string>,
|
|
244
|
-
right: ReturnType<typeof modelKeyFor>,
|
|
245
|
-
): boolean {
|
|
246
|
-
return (
|
|
247
|
-
left.provider === right.provider &&
|
|
248
|
-
left.id === right.id &&
|
|
249
|
-
left.api === right.api &&
|
|
250
|
-
left.baseUrl === right.baseUrl
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export async function runtimeFromContext(
|
|
255
|
-
ctx: ParentModelSource,
|
|
256
|
-
signal?: AbortSignal,
|
|
257
|
-
observer?: PhaseTransportObserver,
|
|
258
|
-
): Promise<PhaseRuntimeResult> {
|
|
259
|
-
if (signal?.aborted) return cancelledPhaseRuntime();
|
|
260
|
-
if (!ctx.model) {
|
|
261
|
-
return phaseRuntimeFailure(
|
|
262
|
-
{ kind: "environment", code: "sandbox-runtime-unavailable" },
|
|
263
|
-
"parent model is unavailable",
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
const selectedModel = structuredClone(ctx.model);
|
|
267
|
-
const selectedModelKey = modelKeyFor(selectedModel);
|
|
268
|
-
const admittedProvider = ctx.modelRegistry.getProvider(
|
|
269
|
-
selectedModel.provider,
|
|
270
|
-
);
|
|
271
|
-
let resolved: Awaited<
|
|
272
|
-
ReturnType<typeof ctx.modelRegistry.getApiKeyAndHeaders>
|
|
273
|
-
>;
|
|
274
|
-
try {
|
|
275
|
-
resolved = await abortable(
|
|
276
|
-
ctx.modelRegistry.getApiKeyAndHeaders(selectedModel),
|
|
277
|
-
signal,
|
|
278
|
-
);
|
|
279
|
-
} catch (error) {
|
|
280
|
-
if (isCancellation(error, signal)) return cancelledPhaseRuntime();
|
|
281
|
-
throw error;
|
|
282
|
-
}
|
|
283
|
-
if (!resolved.ok) {
|
|
284
|
-
return phaseRuntimeFailure(
|
|
285
|
-
{ kind: "environment", code: "sandbox-runtime-unavailable" },
|
|
286
|
-
"phase authentication is unavailable",
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
if (signal?.aborted) return cancelledPhaseRuntime();
|
|
290
|
-
const model = {
|
|
291
|
-
...selectedModel,
|
|
292
|
-
baseUrl: resolved.baseUrl ?? selectedModel.baseUrl,
|
|
293
|
-
} as Model<string>;
|
|
294
|
-
const delegate = ctx.modelRegistry.getProvider(model.provider);
|
|
295
|
-
if (delegate !== admittedProvider)
|
|
296
|
-
return phaseRuntimeFailure(
|
|
297
|
-
{
|
|
298
|
-
kind: "environment",
|
|
299
|
-
code: "sandbox-runtime-unavailable",
|
|
300
|
-
stage: "phase-runtime",
|
|
301
|
-
},
|
|
302
|
-
"parent Provider changed during authentication",
|
|
303
|
-
);
|
|
304
|
-
if (!delegate)
|
|
305
|
-
return phaseRuntimeFailure(
|
|
306
|
-
{
|
|
307
|
-
kind: "environment",
|
|
308
|
-
code: "sandbox-runtime-unavailable",
|
|
309
|
-
stage: "phase-runtime",
|
|
310
|
-
},
|
|
311
|
-
"parent Provider is unavailable",
|
|
312
|
-
);
|
|
313
|
-
if (!ctx.model || !sameSelectedModel(ctx.model, selectedModelKey))
|
|
314
|
-
return phaseRuntimeFailure(
|
|
315
|
-
{
|
|
316
|
-
kind: "environment",
|
|
317
|
-
code: "sandbox-runtime-unavailable",
|
|
318
|
-
stage: "phase-runtime",
|
|
319
|
-
},
|
|
320
|
-
"parent model changed during authentication",
|
|
321
|
-
);
|
|
322
|
-
// Snapshot effective Provider behavior and auth, never mutate its registry or
|
|
323
|
-
// capture host-session callbacks. Explicit provider implementations still run.
|
|
324
|
-
const provider = phaseProvider(delegate, resolved, observer);
|
|
325
|
-
let modelRuntime: Models;
|
|
326
|
-
try {
|
|
327
|
-
modelRuntime = modelsForProvider(provider, signal);
|
|
328
|
-
} catch (error) {
|
|
329
|
-
if (isCancellation(error, signal)) return cancelledPhaseRuntime();
|
|
330
|
-
throw error;
|
|
331
|
-
}
|
|
332
|
-
if (signal?.aborted) return cancelledPhaseRuntime();
|
|
333
|
-
if (
|
|
334
|
-
!ctx.model ||
|
|
335
|
-
!sameSelectedModel(ctx.model, selectedModelKey) ||
|
|
336
|
-
ctx.modelRegistry.getProvider(model.provider) !== delegate
|
|
337
|
-
) {
|
|
338
|
-
return phaseRuntimeFailure(
|
|
339
|
-
{
|
|
340
|
-
kind: "environment",
|
|
341
|
-
code: "sandbox-runtime-unavailable",
|
|
342
|
-
stage: "phase-runtime",
|
|
343
|
-
},
|
|
344
|
-
"parent model or Provider changed during admission",
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
return { ok: true, modelRuntime, model };
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
export async function runtimeForWorkerRoute(
|
|
351
|
-
route: CustomRoutePolicy | InheritedRoutePolicy,
|
|
352
|
-
ctx: ParentModelSource,
|
|
353
|
-
signal?: AbortSignal,
|
|
354
|
-
environment: Readonly<Record<string, string | undefined>> = process.env,
|
|
355
|
-
observer?: PhaseTransportObserver,
|
|
356
|
-
): Promise<PhaseRuntimeResult> {
|
|
357
|
-
return route.kind === "custom"
|
|
358
|
-
? customPhaseRuntime(route, signal, environment, observer)
|
|
359
|
-
: runtimeFromContext(ctx, signal, observer);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function abortable<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {
|
|
363
|
-
if (!signal) return promise;
|
|
364
|
-
signal.throwIfAborted();
|
|
365
|
-
return new Promise<T>((resolve, reject) => {
|
|
366
|
-
const onAbort = () => {
|
|
367
|
-
signal.removeEventListener("abort", onAbort);
|
|
368
|
-
reject(signal.reason);
|
|
369
|
-
};
|
|
370
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
371
|
-
promise
|
|
372
|
-
.then(resolve, reject)
|
|
373
|
-
.finally(() => signal.removeEventListener("abort", onAbort));
|
|
374
|
-
});
|
|
375
|
-
}
|