@abelxiaoxing/cadence 1.0.4 → 1.0.6

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.
@@ -1,29 +1,33 @@
1
1
  # Copy to <project>/.pi/cadence/.env or ~/.pi/agent/cadence/.env.
2
2
  # The project file wins as a whole file; values are not merged or interpolated.
3
3
 
4
- # Optional Subagent endpoint. Any non-empty key commits the whole layer, which
5
- # then requires MODEL and API_URL. Empty API/bounds use the documented defaults.
4
+ # Optional custom Subagent endpoint. To enable it, set all required values.
5
+ # Any non-empty key commits the whole layer; partial/invalid layers fail closed.
6
6
  SUBAGENT_API_URL=
7
7
  SUBAGENT_API_KEY=
8
8
  SUBAGENT_MODEL=
9
- # Dialect: openai-completions (default), openai-responses, anthropic-messages.
10
- SUBAGENT_API=
11
- # Internal defaults: context window 256000, max output tokens 128000.
12
- SUBAGENT_CONTEXT_WINDOW=
13
- SUBAGENT_MAX_TOKENS=
9
+ # Optional dialect: openai-completions (default), openai-responses,or anthropic-messages.
10
+ # SUBAGENT_API=openai-completions
11
+ # Optional bounds; internal defaults are 256000 and 128000 respectively.
12
+ # SUBAGENT_CONTEXT_WINDOW=256000
13
+ # SUBAGENT_MAX_TOKENS=128000
14
14
 
15
15
  # Per-role layers use the same six suffixes and override the global layer whole:
16
16
  # SUBAGENT_<ROLE>_<SUFFIX>, where ROLE is DESIGN_EXPLORER, CONTRACT_REVIEWER,
17
17
  # IMPLEMENTATION_WORKER, or DIAGNOSIS_WORKER. Partial/invalid layers fail closed.
18
18
 
19
- CONTEXT7_API_URL=https://context7.com/api/v2
20
- CONTEXT7_API_KEY=
19
+ # Context7 works anonymously with its default URL. Uncomment the API key for
20
+ # authenticated access, or the URL only when overriding the default endpoint.
21
+ # CONTEXT7_API_URL=https://context7.com/api/v2
22
+ # CONTEXT7_API_KEY=
21
23
 
24
+ # Required when using the Grok search skill.
22
25
  GROK_API_URL=https://api.x.ai/v1
23
26
  GROK_API_KEY=
24
- GROK_MODEL=grok-4.20-non-reasoning
27
+ # Optional model override.
28
+ # GROK_MODEL=grok-4.20-non-reasoning
25
29
 
26
- # Tavily is enabled by default when TAVILY_API_KEY is set; set to false to opt out.
27
- TAVILY_ENABLED=true
28
- TAVILY_API_URL=https://api.tavily.com
29
- TAVILY_API_KEY=
30
+ # Optional Tavily integration. It is enabled by default when TAVILY_API_KEY is set; set TAVILY_ENABLED to false to opt out.
31
+ # TAVILY_ENABLED=true
32
+ # TAVILY_API_URL=https://api.tavily.com
33
+ # TAVILY_API_KEY=
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abelxiaoxing/cadence",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Prompts, skills, and a private orchestration extension exposing the Abel four-stage workflow (/abel-init, /abel-design, /abel-implement, /abel-diagnose) with package-owned professional Agents.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import type { Model, Usage } from "@earendil-works/pi-ai";
2
+ import type { AssistantMessage, Model, Usage } from "@earendil-works/pi-ai";
3
3
  import {
4
4
  createAgentSession,
5
5
  defineTool,
@@ -137,6 +137,26 @@ function wrapScopedTools(
137
137
 
138
138
  export type ChildFailureKind = "failed" | "cancelled" | "timed-out";
139
139
 
140
+ function finalDeliveryContent(message: AssistantMessage | undefined) {
141
+ return (
142
+ message?.content.filter(
143
+ (content) =>
144
+ content.type !== "thinking" &&
145
+ !(content.type === "text" && content.text.trim().length === 0),
146
+ ) ?? []
147
+ );
148
+ }
149
+
150
+ function isSingleStructuralSubmit(message: AssistantMessage | undefined) {
151
+ const content = finalDeliveryContent(message);
152
+ return (
153
+ message?.role === "assistant" &&
154
+ content.length === 1 &&
155
+ content[0]?.type === "toolCall" &&
156
+ content[0].name === "abel_submit_result"
157
+ );
158
+ }
159
+
140
160
  export type ChildSessionResult =
141
161
  | {
142
162
  ok: true;
@@ -218,17 +238,15 @@ export async function runChildSession(input: {
218
238
  let finalCategory: FinalCategory;
219
239
  if (!last) {
220
240
  finalCategory = "no-final-assistant";
221
- } else if (
222
- last.content.length === 1 &&
223
- last.content[0].type === "toolCall" &&
224
- last.content[0].name === "abel_submit_result"
225
- ) {
241
+ } else if (isSingleStructuralSubmit(last)) {
226
242
  finalCategory =
227
243
  submit.getAttempts() > 1 ? "multiple-submit" : "single-submit-only";
228
- } else if (last.content.length === 1 && last.content[0].type === "text") {
229
- finalCategory = "text-only";
230
244
  } else {
231
- finalCategory = "mixed";
245
+ const content = finalDeliveryContent(last);
246
+ finalCategory =
247
+ content.length === 1 && content[0]?.type === "text"
248
+ ? "text-only"
249
+ : "mixed";
232
250
  }
233
251
  return {
234
252
  finalCategory,
@@ -336,12 +354,7 @@ export async function runChildSession(input: {
336
354
  }
337
355
  const assistants = session.messages.filter((m) => m.role === "assistant");
338
356
  const final = assistants.at(-1);
339
- if (
340
- final?.role !== "assistant" ||
341
- final.content.length !== 1 ||
342
- final.content[0]?.type !== "toolCall" ||
343
- final.content[0]?.name !== "abel_submit_result"
344
- ) {
357
+ if (!isSingleStructuralSubmit(final)) {
345
358
  disposeOnce();
346
359
  return {
347
360
  ok: false,
@@ -82,6 +82,9 @@ function customSubagentModel(endpoint: SubagentEndpoint): Model<string> {
82
82
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
83
83
  contextWindow: endpoint.contextWindow,
84
84
  maxTokens: endpoint.maxTokens,
85
+ ...(endpoint.dialect === "openai-completions"
86
+ ? { compat: { supportsDeveloperRole: false } }
87
+ : {}),
85
88
  } as Model<string>;
86
89
  }
87
90