@cleocode/contracts 2026.8.2 → 2026.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/dist/collection-keys.d.ts +44 -0
- package/dist/collection-keys.d.ts.map +1 -0
- package/dist/collection-keys.js +42 -0
- package/dist/collection-keys.js.map +1 -0
- package/dist/dispatch/operations-registry.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.js +21 -0
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/system-resolver.d.ts +10 -0
- package/dist/llm/system-resolver.d.ts.map +1 -1
- package/dist/operations/llm.d.ts +32 -1
- package/dist/operations/llm.d.ts.map +1 -1
- package/dist/spawn-types.d.ts +19 -0
- package/dist/spawn-types.d.ts.map +1 -1
- package/dist/task.d.ts +12 -1
- package/dist/task.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
- package/src/collection-keys.ts +45 -0
- package/src/dispatch/operations-registry.ts +22 -0
- package/src/index.ts +2 -0
- package/src/llm/system-resolver.ts +10 -0
- package/src/operations/llm.ts +32 -1
- package/src/spawn-types.ts +20 -0
- package/src/task.ts +17 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The keys under which CLEO payloads carry a list of records.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is a shared constant (T12077)
|
|
5
|
+
*
|
|
6
|
+
* Different operations name their collection differently — `tasks.list` emits
|
|
7
|
+
* `tasks`, the SDK's generic list emits `items`, `tasks.find` emits `results`,
|
|
8
|
+
* and `tasks.next` emits `suggestions`. Each consumer that wants "the rows"
|
|
9
|
+
* has historically inlined its own guess, and each wrong guess fails SILENTLY,
|
|
10
|
+
* because an absent key is indistinguishable from an empty result set.
|
|
11
|
+
*
|
|
12
|
+
* Three separate outages traced to exactly this:
|
|
13
|
+
*
|
|
14
|
+
* 1. **T12067** — `output-mode.ts` knew only `tasks`/`items`, in three
|
|
15
|
+
* separately-inlined lists. `cleo find … --output count` reported 1626
|
|
16
|
+
* while `--output id` reported `No ids.` against the same payload.
|
|
17
|
+
* 2. **T12077 (this)** — `cleo next --output id` / `--output count` reported
|
|
18
|
+
* nothing and zero while the envelope held 836 candidates, because
|
|
19
|
+
* `suggestions` was in nobody's list.
|
|
20
|
+
* 3. **T12077 (the severe one)** — `defaultPickTask` in the sentient loop read
|
|
21
|
+
* `response.data.tasks` from `cleo.tasks.find()`, which returns
|
|
22
|
+
* `{results, total}` *unwrapped*. The key was wrong AND the `data` envelope
|
|
23
|
+
* it was reaching through does not exist on the SDK surface, so
|
|
24
|
+
* `allCandidates` was **always `[]`**. The autonomous loop therefore
|
|
25
|
+
* returned `no-task` on every tick it had ever run — 24 ticks, 0 tasks
|
|
26
|
+
* picked, across three months — and looked exactly like "there is no work
|
|
27
|
+
* to do" rather than like a bug.
|
|
28
|
+
*
|
|
29
|
+
* The lesson those three share is that this list must exist in ONE place that
|
|
30
|
+
* both the render layer and the SDK consumers import. Adding an operation with
|
|
31
|
+
* a new collection key now means adding it here, once.
|
|
32
|
+
*
|
|
33
|
+
* @task T12077
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Collection keys in resolution order.
|
|
38
|
+
*
|
|
39
|
+
* Order matters only when a payload carries more than one (it should not);
|
|
40
|
+
* earlier keys win, which keeps the canonical `tasks` shape authoritative.
|
|
41
|
+
*/
|
|
42
|
+
export const COLLECTION_KEYS = ['tasks', 'items', 'results', 'suggestions'] as const;
|
|
43
|
+
|
|
44
|
+
/** A key under which a payload may carry its rows. */
|
|
45
|
+
export type CollectionKey = (typeof COLLECTION_KEYS)[number];
|
|
@@ -8392,6 +8392,28 @@ export const OPERATIONS: OperationDef[] = [
|
|
|
8392
8392
|
required: false,
|
|
8393
8393
|
description: 'Commit plan file before dispatching',
|
|
8394
8394
|
},
|
|
8395
|
+
// T12089: the plan SCOPE. The workflow regenerates the plan when no
|
|
8396
|
+
// committed plan is supplied, and `cleo release plan` requires
|
|
8397
|
+
// `--saga | --epic | --tasks` — so without one of these the dispatched
|
|
8398
|
+
// run dies at "Prepare bump-PR" after a fully green preflight.
|
|
8399
|
+
//
|
|
8400
|
+
// A param absent from this registry is STRIPPED before the domain handler
|
|
8401
|
+
// sees it, silently. `--tasks` was accepted by the CLI, forwarded by the
|
|
8402
|
+
// handler and by `releaseOpen`, and still arrived empty at the workflow
|
|
8403
|
+
// because this declaration was the fourth hop in the chain.
|
|
8404
|
+
{
|
|
8405
|
+
name: 'epic',
|
|
8406
|
+
type: 'string',
|
|
8407
|
+
required: false,
|
|
8408
|
+
description: 'Epic task ID forwarded as the plan scope (e.g. T9999)',
|
|
8409
|
+
},
|
|
8410
|
+
{
|
|
8411
|
+
name: 'tasks',
|
|
8412
|
+
type: 'string',
|
|
8413
|
+
required: false,
|
|
8414
|
+
description:
|
|
8415
|
+
'Comma-separated task IDs forwarded as the plan scope for a task-scoped release',
|
|
8416
|
+
},
|
|
8395
8417
|
] satisfies ParamDef[],
|
|
8396
8418
|
},
|
|
8397
8419
|
|
package/src/index.ts
CHANGED
|
@@ -124,6 +124,16 @@ export interface ResolveLLMForSystemOptions {
|
|
|
124
124
|
* without changing the system identity.
|
|
125
125
|
*/
|
|
126
126
|
roleOverride?: RoleName;
|
|
127
|
+
/**
|
|
128
|
+
* Providers to skip for this resolution (T12084).
|
|
129
|
+
*
|
|
130
|
+
* Mirrors `ResolveLLMForRoleOptions.excludeProviders` and exists for the same
|
|
131
|
+
* reason: a caller that has just watched a provider fail needs its retry to
|
|
132
|
+
* land elsewhere. Without it, every consumer of this resolver — fix-gen among
|
|
133
|
+
* them — is permanently pinned to the first provider the selector picks, no
|
|
134
|
+
* matter how reliably it fails.
|
|
135
|
+
*/
|
|
136
|
+
excludeProviders?: readonly string[];
|
|
127
137
|
|
|
128
138
|
/**
|
|
129
139
|
* When `true`, the resolver skips the provider-registry defaultModel lookup
|
package/src/operations/llm.ts
CHANGED
|
@@ -164,7 +164,9 @@ export type CredentialSourceWire =
|
|
|
164
164
|
| 'cred-file'
|
|
165
165
|
| 'claude-creds'
|
|
166
166
|
| 'global-config'
|
|
167
|
-
| 'project-config'
|
|
167
|
+
| 'project-config'
|
|
168
|
+
/** Keyless local inference daemon detected at runtime (T12082). */
|
|
169
|
+
| 'local-daemon';
|
|
168
170
|
|
|
169
171
|
/**
|
|
170
172
|
* Authentication scheme used when sending the credential to the provider.
|
|
@@ -223,6 +225,22 @@ export interface CredentialMetadataWire {
|
|
|
223
225
|
source: CredentialSourceWire | undefined;
|
|
224
226
|
/** Scheme used to present the credential to the provider. */
|
|
225
227
|
authType: AuthTypeWire;
|
|
228
|
+
/**
|
|
229
|
+
* Custom API endpoint this credential targets, when it has one.
|
|
230
|
+
*
|
|
231
|
+
* NOT a secret — an endpoint URL — so it belongs on the metadata wire rather
|
|
232
|
+
* than behind the sealed handle. It is load-bearing: without it a consumer
|
|
233
|
+
* building a transport cannot learn that a credential points at a local or
|
|
234
|
+
* proxied endpoint, and silently falls back to the vendor's public API.
|
|
235
|
+
*
|
|
236
|
+
* T12081: `role-executor` set `baseUrl` from `deriveApiWire(provider)` — the
|
|
237
|
+
* provider DEFAULT — because this field did not exist. An Ollama credential
|
|
238
|
+
* carrying `http://localhost:11434/v1` was rewritten to `api.openai.com` and
|
|
239
|
+
* rejected 401; the same held for LM Studio, vLLM, OpenRouter and Azure.
|
|
240
|
+
* Every custom-endpoint credential resolved correctly, then called the wrong
|
|
241
|
+
* host.
|
|
242
|
+
*/
|
|
243
|
+
baseUrl?: string | null;
|
|
226
244
|
}
|
|
227
245
|
|
|
228
246
|
// ============================================================================
|
|
@@ -365,6 +383,19 @@ export interface ResolveLLMForRoleOptions {
|
|
|
365
383
|
* @task T11759
|
|
366
384
|
*/
|
|
367
385
|
profileOverride?: string;
|
|
386
|
+
/**
|
|
387
|
+
* Providers to skip during this resolution (T12082).
|
|
388
|
+
*
|
|
389
|
+
* Set by the caller after a provider has already failed for this call, so
|
|
390
|
+
* the retry lands somewhere else. Applies to EVERY tier including config
|
|
391
|
+
* pins: a pinned provider that just returned a terminal error is not a
|
|
392
|
+
* better answer for having been pinned.
|
|
393
|
+
*
|
|
394
|
+
* Empty/absent leaves resolution completely unchanged.
|
|
395
|
+
*
|
|
396
|
+
* @task T12082
|
|
397
|
+
*/
|
|
398
|
+
excludeProviders?: readonly string[];
|
|
368
399
|
}
|
|
369
400
|
|
|
370
401
|
// ============================================================================
|
package/src/spawn-types.ts
CHANGED
|
@@ -18,6 +18,15 @@ export type Provider = string;
|
|
|
18
18
|
* CAAMP-compatible spawn options (inlined for zero-dep contracts).
|
|
19
19
|
*/
|
|
20
20
|
export interface CAAMPSpawnOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Capacity-aware routing hint — the id of the least-loaded registered agent
|
|
23
|
+
* (Wave 6-C/D). Declared explicitly because `spawn-ops.ts` both writes it
|
|
24
|
+
* and reads it back for the `SubagentStart`/`SubagentStop` payloads; under
|
|
25
|
+
* the bare index signature it resolved to `unknown`, so
|
|
26
|
+
* `options?.preferredAgent ?? \`worker-${taskId}\`` widened to `{}` and the
|
|
27
|
+
* agentId silently lost its type. @task T12071
|
|
28
|
+
*/
|
|
29
|
+
preferredAgent?: string;
|
|
21
30
|
[key: string]: unknown;
|
|
22
31
|
}
|
|
23
32
|
|
|
@@ -79,6 +88,17 @@ export interface CLEOSpawnResult extends CAAMPSpawnResult {
|
|
|
79
88
|
durationMs?: number;
|
|
80
89
|
};
|
|
81
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Terminal (or in-flight) state of the spawned agent.
|
|
93
|
+
*
|
|
94
|
+
* Declared explicitly because `spawn-ops.ts` reads `result.status` to build
|
|
95
|
+
* the `SubagentStop` hook payload. Inherited from `CAAMPSpawnResult`'s bare
|
|
96
|
+
* `[key: string]: unknown` it resolved to `unknown`, so the value flowed
|
|
97
|
+
* into a typed payload field unchecked — and it does not even share that
|
|
98
|
+
* field's vocabulary ('completed' here vs 'complete' there). @task T12071
|
|
99
|
+
*/
|
|
100
|
+
status?: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
101
|
+
|
|
82
102
|
/** Reference to manifest entry if output was captured */
|
|
83
103
|
manifestEntryId?: string;
|
|
84
104
|
}
|
package/src/task.ts
CHANGED
|
@@ -192,7 +192,23 @@ export type EvidenceAtom =
|
|
|
192
192
|
failCount: number;
|
|
193
193
|
skipCount: number;
|
|
194
194
|
}
|
|
195
|
-
| {
|
|
195
|
+
| {
|
|
196
|
+
kind: 'tool';
|
|
197
|
+
tool: string;
|
|
198
|
+
exitCode: number;
|
|
199
|
+
stdoutTail?: string;
|
|
200
|
+
/**
|
|
201
|
+
* The project has no such toolchain, so the gate is satisfied by absence
|
|
202
|
+
* rather than by a run (T12083).
|
|
203
|
+
*
|
|
204
|
+
* Recorded explicitly so the audit trail never implies a tool ran that
|
|
205
|
+
* did not. Only ever set for a GUESSED language default — a command
|
|
206
|
+
* declared in `project-context.json` is always executed.
|
|
207
|
+
*/
|
|
208
|
+
notApplicable?: boolean;
|
|
209
|
+
/** Why the tool was judged inapplicable. Present iff `notApplicable`. */
|
|
210
|
+
reason?: string;
|
|
211
|
+
}
|
|
196
212
|
| { kind: 'url'; url: string }
|
|
197
213
|
| { kind: 'note'; note: string }
|
|
198
214
|
| { kind: 'override'; reason: string }
|