@loomcycle/client 1.112.0 → 1.113.0

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/types.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * `client.ts` for the input shapes (RunOptions, CreateSnapshotOptions,
8
8
  * etc.) — those are translated to snake_case in the request body.
9
9
  */
10
- export type EventType = "started" | "text" | "tool_call" | "tool_result" | "usage" | "done" | "error" | "retry" | "host_widened" | "session" | "agent" | "awaiting_input" | "awaiting_review" | "subagent_hold" | "awaiting_children" | "children_note" | "steer" | "context_compaction" | "capability_inert" | "hook_decision" | "limit" | "override" | "context_recap" | "context_state" | "context_distill_declined" | "context_exhausted" | "thinking" | "team_decision" | "turn_cancelled" | "provider_fallback" | "fallback_suppressed" | "model_downgraded" | "cache_invalidated" | "reasoning_invalidated" | "channel_publish" | "channel_delivery" | "interruption_pending" | "spawn_child_started" | "spawn_child_result" | "spawn_child_read" | "prompt_snapshot" | "_meta";
10
+ export type EventType = "started" | "text" | "tool_call" | "tool_result" | "usage" | "done" | "error" | "retry" | "host_widened" | "session" | "agent" | "awaiting_input" | "awaiting_review" | "subagent_hold" | "awaiting_children" | "children_note" | "steer" | "context_compaction" | "capability_inert" | "hook_decision" | "limit" | "override" | "context_recap" | "context_state" | "context_distill_declined" | "context_exhausted" | "thinking" | "team_decision" | "turn_cancelled" | "provider_fallback" | "fallback_suppressed" | "model_downgraded" | "cache_invalidated" | "reasoning_invalidated" | "channel_publish" | "channel_delivery" | "interruption_pending" | "spawn_child_started" | "spawn_child_result" | "spawn_child_read" | "prompt_snapshot" | "queued" | "admitted" | "call_queued" | "call_admitted" | "_meta";
11
11
  export interface ToolUse {
12
12
  id: string;
13
13
  name: string;
@@ -223,6 +223,53 @@ export interface InertContextSetting {
223
223
  * leaves the operator hunting for the live knob. */
224
224
  fix?: string;
225
225
  }
226
+ /** Why a per-run override is refused, in {@link OverridableField.reason}:
227
+ * - `token_locked` — the caller's token carries an overrides policy that
228
+ * leaves the field out
229
+ * - `agent_locked` — the agent's definition says `allow_overrides: false`
230
+ *
231
+ * When both apply the token is named, because a run setting the field is
232
+ * refused for that reason first. */
233
+ export type OverrideLockReason = "token_locked" | "agent_locked";
234
+ /** One per-run override in {@link AgentOverridableResponse.fields}. */
235
+ export interface OverridableField {
236
+ /** Whether a run of the agent, started by this caller, may set it. */
237
+ overridable: boolean;
238
+ /** Present when `overridable` is false. */
239
+ reason?: OverrideLockReason;
240
+ /** The run may only make it smaller than the definition allows
241
+ * (`max_concurrent_children`, `interruption`). */
242
+ narrow_only?: boolean;
243
+ /** The largest value accepted (`max_concurrent_children`: the fan-out
244
+ * ceiling, the definition's or the runtime default). */
245
+ max?: number;
246
+ /** The values accepted, when the run path checks against a closed set this
247
+ * agent has: `effort`, the configured `tier`s, and the `provider` and
248
+ * `model` sets the definition declares. Absent means no closed set here —
249
+ * any value the deployment accepts (see `listModels()` / `getRouting()`),
250
+ * not anything at all. Each describes the field set on its own. */
251
+ values?: string[];
252
+ }
253
+ /** The reply from `GET /v1/_agentdef/{name}/overridable` — for every per-run
254
+ * override, whether a run of this agent started by THIS caller may set it.
255
+ *
256
+ * Folds the definition's `allow_overrides` with the caller's token overrides
257
+ * policy, through the same checks the run path refuses with. The answer is
258
+ * the caller's: do not reuse it for another token. Keys of `fields` are wire
259
+ * names (`max_tokens`, not `maxTokens`), one per run option a token's
260
+ * overrides policy can name. */
261
+ export interface AgentOverridableResponse {
262
+ agent: string;
263
+ /** The AgentDef version answered for; absent for a yaml or registered agent,
264
+ * which have no versions. Always the active version: a run cannot pin one. */
265
+ def_id?: string;
266
+ /** The definition's `allow_overrides`; unset reads as true. */
267
+ allow_overrides: boolean;
268
+ /** True when the caller's token carries an overrides policy, so `fields`
269
+ * reflects this caller and not only the agent. */
270
+ restricted_by_token: boolean;
271
+ fields: Record<string, OverridableField>;
272
+ }
226
273
  /** The reply from `retuneRun` — the run's MERGED configuration, not an echo of
227
274
  * the request.
228
275
  *
@@ -375,6 +422,28 @@ export interface CapabilityInertInfo {
375
422
  /** A line naming the tool, the gate, and what to set. */
376
423
  message?: string;
377
424
  }
425
+ /** AdmissionInfo accompanies `event: queued` and `event: admitted` frames.
426
+ * Mirrors providers.AdmissionInfo. */
427
+ export interface AdmissionInfo {
428
+ /** On `queued`: why the run waits — `"user_cap"` (its user already has its
429
+ * cap of runs active) or `"global_cap"` (every run slot is taken). On
430
+ * `call_queued`: why the call waits — `"lane"` (every slot of its call lane
431
+ * is taken) or `"models_in_flight"` (the provider has its cap of models in
432
+ * flight and this call's model is not among them). */
433
+ reason?: "user_cap" | "global_cap" | "lane" | "models_in_flight";
434
+ /** On `queued`: the run's place when it was queued, 1 being the next to
435
+ * start. For `user_cap` it counts only the same user's waiting runs. It
436
+ * moves as other runs start and end; `getRun` reports the current place
437
+ * (`Agent.queue_position`). */
438
+ position?: number;
439
+ /** On `admitted`: how long the run waited for its slot, in milliseconds. On
440
+ * `call_admitted`: how long the call waited. */
441
+ wait_ms?: number;
442
+ /** On `call_queued` / `call_admitted`: the provider id as configured. */
443
+ provider?: string;
444
+ /** On `call_queued` / `call_admitted`: the call lane's model kind (`"chat"`). */
445
+ kind?: string;
446
+ }
378
447
  export interface LimitInfo {
379
448
  /** Which axis tripped: "operator" | "tenant" | "user". */
380
449
  scope: string;
@@ -615,6 +684,11 @@ export interface AgentEvent {
615
684
  /** Payload on `event: limit` (RFC AW) — a per-scope token-budget crossing.
616
685
  * Nil on all other event types. */
617
686
  limit?: LimitInfo;
687
+ /** Payload on `event: queued` (why the run waits, and its place) and
688
+ * `event: admitted` (how long it waited), and on `event: call_queued` /
689
+ * `event: call_admitted` (the same for one model call, with its provider
690
+ * and lane kind). Nil on all other event types. */
691
+ admission?: AdmissionInfo;
618
692
  /** Set on `capability_inert` events: a tool the agent holds and cannot use. */
619
693
  capability_inert?: CapabilityInertInfo;
620
694
  /** Payload on `event: hook_decision`. */
@@ -859,6 +933,11 @@ export interface RunOptions extends RunOverrideOptions {
859
933
  * is the time bound for a `mode: "detach"` batch child, where `timeoutMs`
860
934
  * is refused. Any agent, model-driven or code-js. */
861
935
  maxWallSeconds?: number;
936
+ /** What the run does when it cannot start at once: `"wait"` (the default)
937
+ * accepts it with status `queued` — the stream yields a `queued` event,
938
+ * then `admitted` when it gets a slot — and `"reject"` refuses it at once
939
+ * (`per_user_quota_exhausted` or `backpressure`). A fresh run only. */
940
+ admission?: "wait" | "reject";
862
941
  /** `"none"` makes this a no-trace run: what it says is delivered on this
863
942
  * stream and stored nowhere — the server keeps a marker for each event
864
943
  * (its type and size) and a class for an error, never the text. Omit (or
@@ -1217,7 +1296,8 @@ export interface ClientOptions {
1217
1296
  }
1218
1297
  /** `configured` is a run created but not yet started (RFC DI). */
1219
1298
  /** `rejected` is a run a reviewer turned down without feedback (RFC DJ). */
1220
- export type AgentStatus = "configured" | "running" | "completed" | "failed" | "cancelled" | "rejected";
1299
+ /** `queued` is a run accepted but waiting for a run slot. */
1300
+ export type AgentStatus = "configured" | "queued" | "running" | "completed" | "failed" | "cancelled" | "rejected";
1221
1301
  /** What a running run is blocked on: an open Channel subscribe, an open
1222
1302
  * Interruption ask, a hold for an operator's review verdict, an
1223
1303
  * interactive run parked for the operator's next turn (`input`), or a run
@@ -1267,6 +1347,14 @@ export interface Agent {
1267
1347
  * (`awaited_state: "review"`) or the child run ids, comma-separated and
1268
1348
  * bounded (`awaited_state: "children"`) the run waits on. */
1269
1349
  awaited_on?: string;
1350
+ /** Where a `queued` run stands now: 1 is the next to start. For
1351
+ * `queue_reason: "user_cap"` it counts only the same user's waiting runs.
1352
+ * {@link LoomcycleClient.getAgent} / getRun only, from the replica that
1353
+ * holds the run's wait; absent on every other run. */
1354
+ queue_position?: number;
1355
+ /** Why a `queued` run waits: its user already has its cap of runs active
1356
+ * (`user_cap`), or every run slot is taken (`global_cap`). */
1357
+ queue_reason?: "user_cap" | "global_cap";
1270
1358
  /** The replica owning the run's live cancel handle. Absent in a
1271
1359
  * single-replica deployment. */
1272
1360
  replica_id?: string;
@@ -1328,6 +1416,10 @@ export interface RunSpec {
1328
1416
  * `maxWallSeconds`: past it the run ends `cancelled` with
1329
1417
  * `stop_reason: "wall_limit"`. Absent when the run has no such limit. */
1330
1418
  max_wall_seconds?: number;
1419
+ /** The run started with its tree's ephemeral volumes read-only: its own
1420
+ * definition, or an ancestor's, says `ephemeral_volumes: "ro"`. A resume
1421
+ * restores it whatever the definition says by then. */
1422
+ ephemeral_read_only?: boolean;
1331
1423
  review_ttl_seconds?: number;
1332
1424
  /** Which model serves the run: model / provider / tier / effort. */
1333
1425
  routing?: Record<string, unknown>;
@@ -1517,7 +1609,33 @@ export interface RunResult {
1517
1609
  }
1518
1610
  export interface ListAgentsResponse {
1519
1611
  agents: Agent[];
1612
+ /** True when an `awaited_state` listing stopped before reading every
1613
+ * running run (it reads at most 1,000), so a matching run may be missing.
1614
+ * Absent otherwise. */
1615
+ truncated?: boolean;
1616
+ }
1617
+ /** {@link LoomcycleClient.listUserAgents}'s filters. */
1618
+ export interface ListUserAgentsOptions {
1619
+ /** One status or several (a run in any of them is listed). Omitted → only
1620
+ * running runs; `"all"` → every status. An unknown status is a 400. */
1621
+ status?: AgentStatus | "all" | AgentStatus[];
1622
+ /** List only running runs blocked on one of these waits. Implies running:
1623
+ * a `status` other than `"running"` with it is a 400. */
1624
+ awaitedState?: AwaitedState | AwaitedState[];
1625
+ /** Narrows the result CLIENT-SIDE to runs whose `parent_agent_id`
1626
+ * matches. */
1627
+ parentAgentId?: string;
1628
+ /** Super-admin tenant-focus (?tenant=, RFC L v0.17.0). Ignored
1629
+ * server-side for a tenant principal — its own tenant is forced. */
1630
+ tenant?: string;
1631
+ signal?: AbortSignal;
1520
1632
  }
1633
+ /** {@link LoomcycleClient.listUserAgents}'s answer: the runs, still an array,
1634
+ * carrying `truncated: true` when an `awaitedState` listing stopped before
1635
+ * reading every running run. */
1636
+ export type AgentList = Agent[] & {
1637
+ truncated?: boolean;
1638
+ };
1521
1639
  /** One page of a team walk's runs ({@link LoomcycleClient.listWalkRuns}),
1522
1640
  * ordered by started_at then run id. `next_cursor` is "" on the last page. */
1523
1641
  export interface WalkRunsPage {
@@ -2308,6 +2426,9 @@ export interface PreHookCall {
2308
2426
  phase: "pre";
2309
2427
  owner: string;
2310
2428
  hook_name: string;
2429
+ /** Present on a signed call only: this delivery's id, inside the signed body.
2430
+ * Dedupe on it, never on the unsigned X-Loomcycle-Delivery header copy. */
2431
+ delivery_id?: string;
2311
2432
  agent: string;
2312
2433
  user_id?: string;
2313
2434
  agent_id?: string;
@@ -2334,6 +2455,9 @@ export interface PostHookCall {
2334
2455
  phase: "post";
2335
2456
  owner: string;
2336
2457
  hook_name: string;
2458
+ /** Present on a signed call only: this delivery's id, inside the signed body.
2459
+ * Dedupe on it, never on the unsigned X-Loomcycle-Delivery header copy. */
2460
+ delivery_id?: string;
2337
2461
  agent: string;
2338
2462
  user_id?: string;
2339
2463
  agent_id?: string;
@@ -2347,6 +2471,9 @@ export interface ChannelHookCall {
2347
2471
  /** `"channel:<name>"`. */
2348
2472
  owner: string;
2349
2473
  hook_name: string;
2474
+ /** Present on a signed call only: this delivery's id, inside the signed body.
2475
+ * Dedupe on it, never on the unsigned X-Loomcycle-Delivery header copy. */
2476
+ delivery_id?: string;
2350
2477
  channel: string;
2351
2478
  scope: string;
2352
2479
  scope_id?: string;
@@ -2378,7 +2505,7 @@ export interface ChannelHookResult {
2378
2505
  * the adapter doesn't re-validate. Use the optional `extra` index
2379
2506
  * signature for forward-compat fields. */
2380
2507
  export type SubstrateToolInput = {
2381
- op: "create" | "fork" | "get" | "list" | "promote" | "retire" | "rediscover" | "verify" | "delete" | "purge";
2508
+ op: "create" | "fork" | "get" | "list" | "promote" | "retire" | "rediscover" | "verify" | "delete" | "purge" | "set_mode";
2382
2509
  name?: string;
2383
2510
  def_id?: string;
2384
2511
  parent_def_id?: string;
@@ -2387,6 +2514,27 @@ export type SubstrateToolInput = {
2387
2514
  promote?: boolean;
2388
2515
  retired?: boolean;
2389
2516
  mode?: VolumeMode;
2517
+ source?: {
2518
+ git: {
2519
+ /** https URL of the repository, on a host the operator allows. */
2520
+ url: string;
2521
+ /** The full 40-digit sha-1 of the commit. */
2522
+ commit: string;
2523
+ /** Where to find the commit when the server serves none by sha. */
2524
+ ref?: string;
2525
+ /** `$cred:<name>` or `$ghapp:<name>`; must be restricted to this URL. */
2526
+ credential?: string;
2527
+ /** May only lower the operator's limit. */
2528
+ max_bytes?: number;
2529
+ /** Also write a diff from this commit: "merge-base:default" or a sha. */
2530
+ diff_base?: string;
2531
+ /** Names for the diff's files, each a plain name under `_meta/`. */
2532
+ outputs?: {
2533
+ diff?: string;
2534
+ numstat?: string;
2535
+ };
2536
+ };
2537
+ };
2390
2538
  [extra: string]: unknown;
2391
2539
  };
2392
2540
  /** Response shape for {@link LoomcycleClient.agentDef} and
@@ -2648,12 +2796,20 @@ export interface TeamRunResult {
2648
2796
  capped_state?: string;
2649
2797
  max_iterations?: number;
2650
2798
  iteration_count?: number;
2651
- /** The per-state trace. Absent when `deduplicated` is set. */
2652
- steps: TeamRunStep[];
2799
+ /** The per-state trace. Absent when `deduplicated` is set: the trace is
2800
+ * built by the request that walks and is not stored on the run, so a retry
2801
+ * answered from the key has none to return. */
2802
+ steps?: TeamRunStep[];
2653
2803
  /** Set (true) when the request's `idempotencyKey` was already held: nothing
2654
2804
  * was started, and this is the walk that holds it, as it is now — `status`
2655
2805
  * may be "running", and there are no `steps`. */
2656
2806
  deduplicated?: boolean;
2807
+ /** On a `deduplicated` answer for a walk that has ended: the stop reason
2808
+ * recorded on its run. Absent while it runs, and when none was recorded. */
2809
+ stop_reason?: string;
2810
+ /** On a `deduplicated` answer for a walk that has ended: why it failed, as
2811
+ * recorded on its run. Absent when it did not fail. */
2812
+ error?: string;
2657
2813
  [extra: string]: unknown;
2658
2814
  }
2659
2815
  /** What team {@link LoomcycleClient.runTeam} walks, and how. */
@@ -2720,6 +2876,11 @@ export interface TeamRunTarget {
2720
2876
  * it has no `steps`: read the walk's member runs for those. Refused with
2721
2877
  * `boardChunkId`, which already resumes a stored position. */
2722
2878
  idempotencyKey?: string;
2879
+ /** When a state hits its iteration cap, ask a human (an interruption)
2880
+ * whether to continue, `reroute:<state>` or abort, instead of ending with
2881
+ * the `iteration_cap` outcome. An ask nobody answers, that times out or is
2882
+ * declined aborts the walk. Default false. */
2883
+ interruptOnCap?: boolean;
2723
2884
  }
2724
2885
  /** What {@link LoomcycleClient.runTeam} returns for `mode: "detach"` — the
2725
2886
  * handle, immediately, with the walk still running behind it.
@@ -2973,6 +3134,114 @@ export type HistoryToolInput = {
2973
3134
  * returns the chat metadata + transcript (or Markdown), `recap` a summary,
2974
3135
  * `resume` a continuation handle. Callers narrow as needed. */
2975
3136
  export type HistoryToolResponse = unknown;
3137
+ /** Who recorded an evaluation, relative to the run it scores. The server
3138
+ * derives it from the caller and the target run; it is never sent. A call
3139
+ * outside a run (this client) records `external` or `unrelated`. */
3140
+ export type EvaluationEmitterRole = "self" | "sibling" | "parent" | "external" | "unrelated";
3141
+ /** `evaluation({op:"submit"})`: record a score against a run. Scores are
3142
+ * additive — submitting again records another evaluation. */
3143
+ export interface EvaluationSubmitInput {
3144
+ op: "submit";
3145
+ run_id: string;
3146
+ /** Required. Convention [-1,1] or [0,1]; the operator chooses. */
3147
+ score: number;
3148
+ /** Named axes to numeric values, e.g. `{correctness: 0.8}`. */
3149
+ dimensions?: Record<string, number>;
3150
+ /** Free-form structured payload. */
3151
+ judgement?: unknown;
3152
+ /** Natural-language reasoning. */
3153
+ rationale?: string;
3154
+ }
3155
+ /** `evaluation({op:"get"})`: one evaluation by id. */
3156
+ export interface EvaluationGetInput {
3157
+ op: "get";
3158
+ eval_id: string;
3159
+ }
3160
+ /** `evaluation({op:"list_for_run"})`: every evaluation of one run. */
3161
+ export interface EvaluationListForRunInput {
3162
+ op: "list_for_run";
3163
+ run_id: string;
3164
+ /** Max rows (default 100). */
3165
+ limit?: number;
3166
+ }
3167
+ /** `evaluation({op:"list_for_def"})`: every evaluation of runs of one
3168
+ * definition version, in the caller's tenant. */
3169
+ export interface EvaluationListForDefInput {
3170
+ op: "list_for_def";
3171
+ def_id: string;
3172
+ /** Max rows (default 100). */
3173
+ limit?: number;
3174
+ }
3175
+ /** `evaluation({op:"aggregate"})`: the scores of a definition rolled up. */
3176
+ export interface EvaluationAggregateInput {
3177
+ op: "aggregate";
3178
+ def_id: string;
3179
+ /** Walk parent_def_id and include the ancestors' evaluations. */
3180
+ include_lineage?: boolean;
3181
+ }
3182
+ /** Input for {@link LoomcycleClient.evaluation}, discriminated by `op`. */
3183
+ export type EvaluationInput = EvaluationSubmitInput | EvaluationGetInput | EvaluationListForRunInput | EvaluationListForDefInput | EvaluationAggregateInput;
3184
+ /** What `submit` returns: the stored row's identity and the derived role. */
3185
+ export interface EvaluationSubmitResult {
3186
+ eval_id: string;
3187
+ run_id: string;
3188
+ /** The definition version the run was running, denormalised at submit
3189
+ * time; `""` for a run that resolved through static config only. */
3190
+ def_id: string;
3191
+ emitter_role: EvaluationEmitterRole;
3192
+ score: number;
3193
+ /** RFC 3339 UTC. */
3194
+ created_at: string;
3195
+ }
3196
+ /** One stored evaluation, as `get` and the `list_for_*` ops return it. */
3197
+ export interface EvaluationRecord {
3198
+ eval_id: string;
3199
+ run_id: string;
3200
+ def_id: string;
3201
+ score: number;
3202
+ /** `null` when the submit carried none. */
3203
+ dimensions: Record<string, number> | null;
3204
+ /** `null` when the submit carried none. */
3205
+ judgement: unknown;
3206
+ rationale: string;
3207
+ emitter_role: EvaluationEmitterRole;
3208
+ emitter_agent_id: string;
3209
+ emitter_run_id: string;
3210
+ /** RFC 3339 UTC. */
3211
+ created_at: string;
3212
+ }
3213
+ /** What `list_for_run` returns. An unknown run, or one the caller may not
3214
+ * read, lists nothing rather than refusing. */
3215
+ export interface EvaluationListForRunResult {
3216
+ run_id: string;
3217
+ evaluations: EvaluationRecord[];
3218
+ }
3219
+ /** What `list_for_def` returns. An unknown definition, or one the caller may
3220
+ * not read, lists nothing rather than refusing. */
3221
+ export interface EvaluationListForDefResult {
3222
+ def_id: string;
3223
+ evaluations: EvaluationRecord[];
3224
+ }
3225
+ /** Summary statistics over a set of scores. */
3226
+ export interface EvaluationScoreStats {
3227
+ mean: number;
3228
+ median: number;
3229
+ min: number;
3230
+ max: number;
3231
+ latest: number;
3232
+ count: number;
3233
+ }
3234
+ /** What `aggregate` returns. `count: 0` for an unknown definition. */
3235
+ export interface EvaluationAggregate {
3236
+ def_id: string;
3237
+ count: number;
3238
+ score: EvaluationScoreStats;
3239
+ /** Keyed by dimension name; only dimensions some row supplied. */
3240
+ dimensions?: Record<string, EvaluationScoreStats>;
3241
+ /** Keyed by emitter role. */
3242
+ by_emitter_role?: Record<string, EvaluationScoreStats>;
3243
+ lineage_included: boolean;
3244
+ }
2976
3245
  /** Volume access mode — read-only or read-write (RFC AH). */
2977
3246
  export type VolumeMode = "ro" | "rw";
2978
3247
  /** One row of {@link LoomcycleClient.listVolumes} (`GET /v1/_volumes`). */
@@ -3467,7 +3736,7 @@ export interface StreamUserRunStatesOptions {
3467
3736
  * `parent_context.walk_id`).
3468
3737
  *
3469
3738
  * A team walk's own run_id IS its walk id, so a caller that started a team
3470
- * with `detach: true` passes back exactly the handle it already holds —
3739
+ * with `mode: "detach"` passes back exactly the handle it already holds —
3471
3740
  * there is no second identifier and nothing to map. That is what makes a
3472
3741
  * live view of one running workflow's agents a filter rather than a
3473
3742
  * feature.
@@ -3635,17 +3904,6 @@ export interface EnsureMcpServerResult {
3635
3904
  /** Populated when `rediscover` ran: the number of tools discovered. */
3636
3905
  discoveredToolCount?: number;
3637
3906
  }
3638
- /** Typed `overlay` for an {@link LoomcycleClient.agentDef} create/fork —
3639
- * the mutable subset of an agent definition. All fields optional (a fork
3640
- * overlays only what changes). The `[extra]` tail keeps it forward-compatible
3641
- * with fields the in-process tool may accept that the adapter doesn't model
3642
- * yet — the tool owns the authoritative schema; the adapter doesn't re-validate.
3643
- *
3644
- * `code_body` is the inline code-js orchestrator source (RFC J): set it (with
3645
- * `provider: "code-js"`) to ingest a code agent through the substrate with NO
3646
- * host filesystem bind — the symmetry that makes code agents work in
3647
- * containers / pure-cloud. Requires `LOOMCYCLE_CODE_AGENTS_ENABLED=1` on the
3648
- * sidecar; create/fork refuses a non-empty `code_body` otherwise. */
3649
3907
  /** Per-agent Channel tool ACL (mirrors the sidecar `channels:` agent yaml).
3650
3908
  * The Channel tool default-denies until publish/subscribe patterns are granted. */
3651
3909
  export interface AgentChannelACL {
@@ -3706,6 +3964,82 @@ export interface AgentDecision {
3706
3964
  /** The models a Decision call may name. Empty = every model the server lists. */
3707
3965
  models?: string[];
3708
3966
  }
3967
+ /** The `sampling` block of an {@link AgentDefOverlay}: {@link SamplingOptions}
3968
+ * in its wire spelling. The overlay is sent verbatim, so its nested blocks are
3969
+ * snake_case — the camelCase run-option types are converted by the client and
3970
+ * would be ignored here. A fork merges it per field; an explicit
3971
+ * `temperature: 0` is deterministic, not unset. */
3972
+ export interface AgentSampling {
3973
+ temperature?: number;
3974
+ top_p?: number;
3975
+ top_k?: number;
3976
+ frequency_penalty?: number;
3977
+ presence_penalty?: number;
3978
+ seed?: number;
3979
+ stop?: string[];
3980
+ }
3981
+ /** The `compaction` block of an {@link AgentDefOverlay}: {@link CompactionOptions}
3982
+ * in its wire spelling, plus `memory_flush`, which only an agent definition
3983
+ * may set. A fork merges it per field. */
3984
+ export interface AgentCompaction {
3985
+ enabled?: boolean;
3986
+ target_percentage?: number;
3987
+ keep_last_n?: number;
3988
+ keep_first?: boolean;
3989
+ autocompact_at_pct?: number;
3990
+ model?: string;
3991
+ /** Bank the span a compaction discards for the memory consolidator.
3992
+ * Per-agent only: a run cannot set it, because it makes the agent persist
3993
+ * memory its definition must authorize. */
3994
+ memory_flush?: boolean;
3995
+ }
3996
+ /** The `context` block of an {@link AgentDefOverlay}: {@link ContextOptions}
3997
+ * in its wire spelling — see there for what each field does. A fork merges it
3998
+ * per field, and the spawn tree inherits it. */
3999
+ export interface AgentContext {
4000
+ mode?: ContextOptions["mode"];
4001
+ keep_last_n?: number;
4002
+ reasoning?: ContextOptions["reasoning"];
4003
+ recap_max_chars?: number;
4004
+ model?: string;
4005
+ autorecap_at_pct?: number;
4006
+ state_schema?: Record<string, unknown>;
4007
+ on_invalid_patch?: ContextOptions["onInvalidPatch"];
4008
+ max_patch_retries?: number;
4009
+ recall?: boolean;
4010
+ harvest_to_memory?: boolean;
4011
+ }
4012
+ /** One entry of a per-tier candidate list (`models` on an
4013
+ * {@link AgentDefOverlay}). */
4014
+ export interface TierCandidate {
4015
+ provider: string;
4016
+ model: string;
4017
+ }
4018
+ /** A core memory block an agent carries (`core_blocks`). Its value is the
4019
+ * Memory entry `core/<label>` in the block's scope, rendered into the system
4020
+ * prompt. */
4021
+ export interface CoreBlock {
4022
+ /** One path segment: no `/`, no whitespace. */
4023
+ label: string;
4024
+ /** Where the value lives. An `agent` block never crosses a spawn boundary. */
4025
+ scope?: "agent" | "user" | "tenant";
4026
+ /** Caps an agent write to the block, in bytes of the JSON value. 0 = only the
4027
+ * scope's `memory_quota_bytes`. */
4028
+ limit_bytes?: number;
4029
+ /** Operator-authored: the agent's writes to it are refused. */
4030
+ read_only?: boolean;
4031
+ }
4032
+ /** Typed `overlay` for an {@link LoomcycleClient.agentDef} create/fork —
4033
+ * the mutable subset of an agent definition. All fields optional (a fork
4034
+ * overlays only what changes). The `[extra]` tail keeps it forward-compatible
4035
+ * with fields the in-process tool may accept that the adapter doesn't model
4036
+ * yet — the tool owns the authoritative schema; the adapter doesn't re-validate.
4037
+ *
4038
+ * `code_body` is the inline code-js orchestrator source (RFC J): set it (with
4039
+ * `provider: "code-js"`) to ingest a code agent through the substrate with NO
4040
+ * host filesystem bind — the symmetry that makes code agents work in
4041
+ * containers / pure-cloud. Requires `LOOMCYCLE_CODE_AGENTS_ENABLED=1` on the
4042
+ * sidecar; create/fork refuses a non-empty `code_body` otherwise. */
3709
4043
  export interface AgentDefOverlay {
3710
4044
  provider?: string;
3711
4045
  model?: string;
@@ -3774,6 +4108,10 @@ export interface AgentDefOverlay {
3774
4108
  * refused with `override_not_allowed`. Unset or `true` leaves them to the
3775
4109
  * caller. A fork that does not send it keeps its parent's. */
3776
4110
  allow_overrides?: boolean;
4111
+ /** `"ro"` gives the agent every ephemeral volume of its run read-only,
4112
+ * whatever mode the volume was created with, and the same to every agent
4113
+ * it spawns. The only value; a fork cannot clear it. */
4114
+ ephemeral_volumes?: "ro";
3777
4115
  /** `"none"` makes every run of the agent a no-trace run: its content is
3778
4116
  * delivered to the caller and stored nowhere. A fork that does not send it
3779
4117
  * keeps its parent's, and `"default"` on a no-trace parent is refused. */
@@ -3788,6 +4126,88 @@ export interface AgentDefOverlay {
3788
4126
  channels?: AgentChannelACL;
3789
4127
  /** Interruption tool gate — `enabled: true` REQUIRED for the tool to work. */
3790
4128
  interruption?: AgentInterruptionACL;
4129
+ /** The agent's own description — what an agent file's frontmatter
4130
+ * carries — and part of its content hash. Not the request's top-level
4131
+ * `description`, which is the rationale for this create/fork. */
4132
+ description?: string;
4133
+ /** Tier-routing override: the providers this agent's tier resolution tries,
4134
+ * in order, replacing the server's priority list. No effect on a
4135
+ * provider+model pin. */
4136
+ providers?: string[];
4137
+ /** Per-tier candidate lists, keyed by tier name. A tier set here replaces
4138
+ * the server's definition of that tier for this agent. */
4139
+ models?: Record<string, TierCandidate[]>;
4140
+ /** The web-search providers the WebSearch tool tries, in order. Empty uses
4141
+ * the server's default order. */
4142
+ search_providers?: string[];
4143
+ /** The context window this agent uses, in tokens — distinct from
4144
+ * `max_tokens`, the output cap. Sizes the window on a local model; on a
4145
+ * hosted one it can only lower the effective window. */
4146
+ max_context_tokens?: number;
4147
+ /** Lift the `max_iterations` cap for an LLM agent (cancel is the stop). A
4148
+ * fork can set it but cannot clear it. */
4149
+ unbounded_iterations?: boolean;
4150
+ /** Wall-clock budget, in seconds, for a code-js agent; 0 = the server's
4151
+ * default. Ignored by LLM agents. */
4152
+ run_timeout_seconds?: number;
4153
+ /** The system prompt before skill bodies are baked in. Defaults to
4154
+ * `system_prompt`; set it only to rebuild a prompt from a different base. */
4155
+ system_prompt_base?: string;
4156
+ /** Per-agent LLM sampling, merged per field over the parent's. */
4157
+ sampling?: AgentSampling;
4158
+ /** Context compaction in `append` mode, merged per field. */
4159
+ compaction?: AgentCompaction;
4160
+ /** How history is distilled, merged per field. */
4161
+ context?: AgentContext;
4162
+ /** Volumes the file and exec tools may resolve paths against. None declared
4163
+ * means `default`; declaring any confines the agent to exactly those. */
4164
+ volumes?: string[];
4165
+ /** SQL Memory scopes: `agent` | `user` | `run`. Unset means `["user"]`;
4166
+ * `["-*"]` grants none. */
4167
+ sql_scopes?: string[];
4168
+ /** Per-scope SQL database byte cap; 0 = the server's default. */
4169
+ sql_quota_bytes?: number;
4170
+ /** Whose past chats the History tool may read: `self` | `user` | `tenant` |
4171
+ * `global` (honored only for an admin). Unset means `["user"]`; `["-*"]`
4172
+ * grants none. */
4173
+ history_scope?: string[];
4174
+ /** Core memory blocks rendered into the system prompt. */
4175
+ core_blocks?: CoreBlock[];
4176
+ /** A sub-agent also receives its parent run's user- and tenant-scope core
4177
+ * blocks. */
4178
+ inherit_core_blocks?: boolean;
4179
+ /** Cap on all memory-injected prompt content, in tokens (estimated); 0 = the
4180
+ * server's default. */
4181
+ memory_inject_max_tokens?: number;
4182
+ /** Opt into the memory-usage protocol note. */
4183
+ memory_protocol?: boolean;
4184
+ /** Soft size cap, in bytes, the memory protocol asks the agent to keep its
4185
+ * index document under; 0 = the server's default. */
4186
+ memory_index_max_bytes?: number;
4187
+ /** Grant the Memory tool's consolidation control ops. Default-deny. */
4188
+ memory_consolidation?: boolean;
4189
+ /** Provisioning of the user-root memory document: `lazy` (default),
4190
+ * `force` or `suppress`. */
4191
+ memory_roots?: "lazy" | "force" | "suppress";
4192
+ /** Append the runtime's capability and tool-guide text to the system prompt
4193
+ * — for small models that under-read tool schemas. */
4194
+ inject_tool_guide?: boolean;
4195
+ /** Mark the agent as maintenance plumbing: its chats are excluded from
4196
+ * memory consolidation and hidden from History by default. A fork can set
4197
+ * it and cannot clear it. */
4198
+ internal?: boolean;
4199
+ /** Which AgentDef names this agent may fork / promote / retire: `self`,
4200
+ * `descendants`, `named:<name>` or `any`. Default-deny. */
4201
+ agent_def_scopes?: string[];
4202
+ /** Same closed set as `agent_def_scopes`, for ScheduleDef. Default-deny. */
4203
+ schedule_def_scopes?: string[];
4204
+ /** Same closed set as `agent_def_scopes`, for A2AServerCardDef. Default-deny. */
4205
+ a2a_server_card_def_scopes?: string[];
4206
+ /** Same closed set as `agent_def_scopes`, for A2AAgentDef. Default-deny. */
4207
+ a2a_agent_def_scopes?: string[];
4208
+ /** Which volumes this agent may create / delete / purge: `named:<volume>`
4209
+ * or `any`. Default-deny. */
4210
+ volume_def_scopes?: string[];
3791
4211
  [extra: string]: unknown;
3792
4212
  }
3793
4213
  /** Options for {@link LoomcycleClient.ensureCodeAgent}. */
@@ -3832,6 +4252,440 @@ export interface MCPServerDefVerifyResult {
3832
4252
  /** True if an active row exists for this name. */
3833
4253
  deployed: boolean;
3834
4254
  }
4255
+ /** A HookDef's body: a code-js script the server runs in-process, or a webhook
4256
+ * it calls. The server refuses a code-js body carrying a url or headers, and
4257
+ * an http body carrying code. */
4258
+ export type HookDefBody = {
4259
+ kind: "code-js";
4260
+ /** A script defining `function hook(ev)` that returns the decision. */
4261
+ code: string;
4262
+ } | {
4263
+ kind: "http";
4264
+ /** http:// or https://. */
4265
+ url: string;
4266
+ /** Sent with each call. A value may name a credential as
4267
+ * `$cred:<name>`, resolved for the run when the hook is called — keep
4268
+ * secrets there, not in the URL or a literal value. */
4269
+ headers?: Record<string, string>;
4270
+ /** Signs every delivery: the credential, as `"$cred:<name>"` and nothing
4271
+ * else, whose value keys the `X-Loomcycle-Signature` HMAC. It must be
4272
+ * usable for this `url` (unrestricted, or its `allowed_urls` covering
4273
+ * it). A secret that cannot be resolved fails the call; it is never
4274
+ * sent unsigned. Verify with `verifyLoomcycleDelivery`. */
4275
+ signing_secret?: string;
4276
+ };
4277
+ /** Narrows a tool-event hook (`pre` / `post` / `post_failure`) within what it
4278
+ * is attached to. The server refuses it on any other event. */
4279
+ export type HookDefMatch = {
4280
+ /** Exact tool names or trailing-`*` globs; empty = every tool. */
4281
+ tools?: string[];
4282
+ };
4283
+ /** A HookDef's definition — the `overlay` of a `hookDef` create.
4284
+ *
4285
+ * CLOSED on purpose (no index signature): the server decodes the overlay
4286
+ * strictly and refuses any key it does not know, so an unknown key here is a
4287
+ * compile error rather than a runtime refusal. A type alias rather than an
4288
+ * interface so it stays assignable to the loose `SubstrateToolInput.overlay`. */
4289
+ export type HookDefOverlay = {
4290
+ /** Shown to whoever the hook denies, holds or rewrites — including a tenant
4291
+ * that did not write it. The body is never shown. At most 4 KiB. */
4292
+ description?: string;
4293
+ /** What the hook answers. Wherever it is attached, it is attached under this
4294
+ * event; `channel_publish` is a channel's own. */
4295
+ event: HookEvent;
4296
+ match?: HookDefMatch;
4297
+ body: HookDefBody;
4298
+ /** `"open"` (default): a failing hook lets the call through. `"closed"`: it
4299
+ * stops it. */
4300
+ fail_mode?: HookFailMode;
4301
+ /** Webhook: the whole call (default 5 s, max 60 s). code-js: each run of the
4302
+ * script (default 50 ms, max 1 s). */
4303
+ timeout_ms?: number;
4304
+ };
4305
+ /** The `overlay` of a `hookDef` fork: each top-level field given replaces the
4306
+ * parent's, and `null` clears one (it decodes to the field's zero value). */
4307
+ export type HookDefForkOverlay = {
4308
+ [K in keyof HookDefOverlay]?: HookDefOverlay[K] | null;
4309
+ };
4310
+ /** Typed input for {@link LoomcycleClient.hookDef}, one shape per op. */
4311
+ export type HookDefInput = {
4312
+ op: "create";
4313
+ name: string;
4314
+ overlay: HookDefOverlay;
4315
+ /** Free-text rationale (distinct from `overlay.description`). */
4316
+ description?: string;
4317
+ /** Default true. */
4318
+ promote?: boolean;
4319
+ } | {
4320
+ op: "fork";
4321
+ name: string;
4322
+ /** Default: the active version of `name` in your tenant. */
4323
+ parent_def_id?: string;
4324
+ overlay?: HookDefForkOverlay;
4325
+ description?: string;
4326
+ /** Default false. */
4327
+ promote?: boolean;
4328
+ }
4329
+ /** By `def_id`, or the active version of `name`. */
4330
+ | {
4331
+ op: "get";
4332
+ name?: string;
4333
+ def_id?: string;
4334
+ } | {
4335
+ op: "list";
4336
+ name: string;
4337
+ } | {
4338
+ op: "promote";
4339
+ def_id: string;
4340
+ } | {
4341
+ op: "retire";
4342
+ def_id: string;
4343
+ retired: boolean;
4344
+ } | {
4345
+ op: "verify";
4346
+ name: string;
4347
+ content_sha256?: string;
4348
+ } | {
4349
+ op: "delete";
4350
+ name: string;
4351
+ };
4352
+ /** One HookDef version, as create / fork / get return it (`list` returns
4353
+ * `{name, versions: HookDefRowResponse[]}`). */
4354
+ export interface HookDefRowResponse {
4355
+ def_id: string;
4356
+ name: string;
4357
+ version: number;
4358
+ parent_def_id?: string;
4359
+ description?: string;
4360
+ created_at: string;
4361
+ created_by_agent_id?: string;
4362
+ retired: boolean;
4363
+ /** "sha256:" + 64 hex chars, over the name and the normalized definition. */
4364
+ content_sha256: string;
4365
+ tenant_id?: string;
4366
+ /** The stored definition, normalized: `fail_mode` filled in, empty fields
4367
+ * left out. */
4368
+ definition: HookDefOverlay;
4369
+ /** On create / fork: whether the new version was made active. */
4370
+ promoted?: boolean;
4371
+ }
4372
+ /** Response shape for `HookDef verify`; same semantics as
4373
+ * {@link MCPServerDefVerifyResult}. Asking without `content_sha256` is how to
4374
+ * learn whether `name` is deployed at all: `deployed: false`, never an error. */
4375
+ export interface HookDefVerifyResult {
4376
+ matches: boolean;
4377
+ current_sha256: string;
4378
+ current_def_id: string;
4379
+ version: number;
4380
+ name: string;
4381
+ deployed: boolean;
4382
+ }
4383
+ /** Options for the strictly-typed create methods
4384
+ * ({@link LoomcycleClient.createHookDef},
4385
+ * {@link LoomcycleClient.createScheduleDef},
4386
+ * {@link LoomcycleClient.createWebhookDef}). */
4387
+ export interface CreateDefOptions {
4388
+ /** Free-text rationale stored on the version (not part of the definition). */
4389
+ description?: string;
4390
+ /** Make the new version active. Server default: true. */
4391
+ promote?: boolean;
4392
+ signal?: AbortSignal;
4393
+ }
4394
+ /** Options for the strictly-typed fork methods. */
4395
+ export interface ForkDefOptions extends CreateDefOptions {
4396
+ /** The version to fork. Default: the active version of the name in your
4397
+ * tenant (ScheduleDef / WebhookDef fall back to the yaml template of that
4398
+ * name). Server default for `promote` on a fork: false for HookDef, true
4399
+ * for ScheduleDef and WebhookDef. */
4400
+ parentDefId?: string;
4401
+ }
4402
+ /** Options for {@link LoomcycleClient.ensureHookDef}. */
4403
+ export interface EnsureHookDefOptions {
4404
+ /** Free-text rationale stored on a NEW version. Not part of the definition,
4405
+ * so changing only this does not mint one. */
4406
+ description?: string;
4407
+ signal?: AbortSignal;
4408
+ }
4409
+ /** Result of {@link LoomcycleClient.ensureHookDef}. */
4410
+ export interface EnsureHookDefResult {
4411
+ name: string;
4412
+ defId: string;
4413
+ version: number;
4414
+ /** True when this call created a version; false when the active version
4415
+ * already carried the same definition. */
4416
+ changed: boolean;
4417
+ }
4418
+ /** One `on_complete` entry of a ScheduleDef or WebhookDef: what the server
4419
+ * does after the triggered run finishes. Mirrors config.ScheduledRunHook. */
4420
+ export type ScheduledRunHook = {
4421
+ kind: "channel.publish";
4422
+ channel: string;
4423
+ payload?: Record<string, unknown>;
4424
+ } | {
4425
+ kind: "mcp.call";
4426
+ server: string;
4427
+ tool: string;
4428
+ args?: Record<string, unknown>;
4429
+ } | {
4430
+ kind: "memory.set";
4431
+ scope: string;
4432
+ key: string;
4433
+ /** The value written. */
4434
+ payload?: Record<string, unknown>;
4435
+ };
4436
+ /** One block of a scheduled run's prompt. Only text survives into the run, so
4437
+ * there is no image form here. */
4438
+ export interface ScheduledRunPromptContent {
4439
+ /** Default `"trusted-text"`. `"untrusted-block"` wraps the text as external
4440
+ * input. */
4441
+ type?: "trusted-text" | "untrusted-block";
4442
+ text: string;
4443
+ }
4444
+ /** One segment of a scheduled run's prompt. */
4445
+ export interface ScheduledRunPromptSegment {
4446
+ role: "system" | "user";
4447
+ content: ScheduledRunPromptContent[];
4448
+ }
4449
+ /** The `overlay` of a `scheduleDef` create / fork. On fork, a field the
4450
+ * overlay leaves out keeps the parent's; the credential maps merge key by key.
4451
+ *
4452
+ * The `[extra]` tail stays because the server decodes the overlay loosely and
4453
+ * ignores keys it does not know — including the fields it stamps itself
4454
+ * (`operator_key_restricted`, `isolated`, `capture_disabled`,
4455
+ * `operator_layer`), which an overlay can neither set nor clear. */
4456
+ export interface ScheduleDefOverlay {
4457
+ /** `"run"` (default) invokes `agent`; `"channel"` publishes a tick to
4458
+ * `channel` and starts no run; `"team"` starts a detached walk of `team`. */
4459
+ delivery?: "run" | "channel" | "team";
4460
+ channel?: string;
4461
+ agent?: string;
4462
+ prompt?: ScheduledRunPromptSegment[];
4463
+ /** A 5-field cron expression. Setting it clears `user_tier_schedules`. */
4464
+ schedule?: string;
4465
+ /** Cron per user tier, for a template a fork picks from with `user_tier`.
4466
+ * Setting it clears `schedule`. */
4467
+ user_tier_schedules?: Record<string, string>;
4468
+ required_credentials?: string[];
4469
+ /** IANA zone the cron is read in, e.g. `"Europe/Berlin"`. Default UTC. */
4470
+ timezone?: string;
4471
+ enabled?: boolean;
4472
+ /** 0–1000: run the newest N slots missed during an outage, in slot order.
4473
+ * 0 (default) collapses them into one fire. */
4474
+ catch_up_max?: number;
4475
+ /** N > 0 retires the def after its Nth fire; 0 = unbounded (a fork sends 0
4476
+ * to lift a parent's cap). */
4477
+ max_fires?: number;
4478
+ user_id?: string;
4479
+ /** What a slot does while the previous run is still going. Default
4480
+ * `"forbid"`; refused on `delivery: "channel"`. */
4481
+ concurrency_policy?: "forbid" | "allow" | "replace";
4482
+ user_tier?: string;
4483
+ /** Credential key → value, handed to the fired run. */
4484
+ user_credentials?: Record<string, string>;
4485
+ /** Credential key → env-var NAME the scheduler reads it from (the variable
4486
+ * must be on LOOMCYCLE_SCHEDULER_ENV_ALLOWLIST). */
4487
+ user_credentials_from_env?: Record<string, string>;
4488
+ on_complete?: ScheduledRunHook[];
4489
+ /** Non-secret metadata the agent receives as trusted. */
4490
+ metadata?: Record<string, unknown>;
4491
+ /** The tenant fired runs execute in. Your own unless you are an admin;
4492
+ * omit it to use yours. */
4493
+ tenant_id?: string;
4494
+ /** `delivery: "team"` only: the team, its variables as literals, and the
4495
+ * walk's optional input. */
4496
+ team?: string;
4497
+ vars?: Record<string, string>;
4498
+ input?: string;
4499
+ [extra: string]: unknown;
4500
+ }
4501
+ /** Typed input for {@link LoomcycleClient.scheduleDef}, one shape per op. */
4502
+ export type ScheduleDefInput = {
4503
+ op: "create" | "fork";
4504
+ name: string;
4505
+ /** fork only. Default: the active version of `name`, or its yaml
4506
+ * template. */
4507
+ parent_def_id?: string;
4508
+ overlay?: ScheduleDefOverlay;
4509
+ description?: string;
4510
+ /** Default true on both create and fork. */
4511
+ promote?: boolean;
4512
+ } | {
4513
+ op: "get";
4514
+ def_id: string;
4515
+ } | {
4516
+ op: "list";
4517
+ name: string;
4518
+ } | {
4519
+ op: "retire";
4520
+ def_id: string;
4521
+ retired: boolean;
4522
+ }
4523
+ /** Fork `def_id` with `hook` appended to its `on_complete`. */
4524
+ | {
4525
+ op: "add_hook";
4526
+ def_id: string;
4527
+ hook: ScheduledRunHook;
4528
+ }
4529
+ /** Fork `def_id` without its `on_complete[hook_index]` (0-based). */
4530
+ | {
4531
+ op: "remove_hook";
4532
+ def_id: string;
4533
+ hook_index: number;
4534
+ };
4535
+ /** One ScheduleDef version, as create / fork return it. */
4536
+ export interface ScheduleDefRowResponse {
4537
+ def_id: string;
4538
+ name: string;
4539
+ version: number;
4540
+ parent_def_id?: string;
4541
+ description?: string;
4542
+ created_at: string;
4543
+ created_by_agent_id?: string;
4544
+ retired: boolean;
4545
+ bootstrapped_from_static: boolean;
4546
+ /** The stored definition, including the fields the server stamps. */
4547
+ definition: ScheduleDefOverlay;
4548
+ /** Whether the new version was made active. */
4549
+ promoted?: boolean;
4550
+ /** Credential keys a snapshot stripped that this write did not re-supply;
4551
+ * the def stays disabled until they are. */
4552
+ disabled_until_credentials_supplied?: string[];
4553
+ /** Why a key you did name did not count, one line per key. */
4554
+ unusable_credentials?: string[];
4555
+ }
4556
+ /** How a WebhookDef authenticates an inbound request. */
4557
+ export interface WebhookDefAuth {
4558
+ /** `"hmac"` (default) verifies a body signature; `"bearer"` a token;
4559
+ * `"none"` neither. */
4560
+ kind?: "hmac" | "bearer" | "none";
4561
+ /** hmac only; the only one implemented. */
4562
+ algorithm?: "sha256";
4563
+ /** The header carrying the signature. */
4564
+ header?: string;
4565
+ /** Env-var NAME holding the hmac secret — never the secret itself. */
4566
+ signing_secret_env?: string;
4567
+ /** The header naming each delivery, for dedup; without it a body hash is
4568
+ * the delivery's identity. */
4569
+ delivery_id_header?: string;
4570
+ /** Env-var NAME holding the bearer token — never the token itself. */
4571
+ bearer_token_env?: string;
4572
+ /** The verification secret as a stored credential, `"$cred:<name>"` — a
4573
+ * tenant credential of the definition's own tenant, resolved on each
4574
+ * delivery so a rotation needs no restart. Takes the place of
4575
+ * `signing_secret_env` / `bearer_token_env`: set one form, not both. A
4576
+ * literal is refused. */
4577
+ secret?: string;
4578
+ /** The previous secret during a rotation, also `"$cred:<name>"`: a delivery
4579
+ * verified by either is accepted. */
4580
+ previous_secret?: string;
4581
+ }
4582
+ /** Inbound request rate bound for one webhook. */
4583
+ export interface WebhookDefRateLimit {
4584
+ requests_per_minute?: number;
4585
+ burst?: number;
4586
+ }
4587
+ /** Hold the inbound request open until the run finishes (or the timeout). */
4588
+ export interface WebhookDefSyncResponse {
4589
+ enabled?: boolean;
4590
+ timeout_ms?: number;
4591
+ }
4592
+ /** The `overlay` of a `webhookDef` create / fork. On fork, a field the overlay
4593
+ * leaves out keeps the parent's; naming exactly one of `agent` / `channel` /
4594
+ * `team` clears the other two.
4595
+ *
4596
+ * The `[extra]` tail stays for now: the server decodes the overlay loosely
4597
+ * and ignores keys it does not know — including the fields it stamps itself
4598
+ * (`operator_key_restricted`, `isolated`, `capture_disabled`). */
4599
+ export interface WebhookDefOverlay {
4600
+ /** An overlay can only turn a webhook on; to turn one off, restate it. */
4601
+ enabled?: boolean;
4602
+ /** `"spawn"` (default) starts a run of `agent`; `"channel"` publishes the
4603
+ * body to `channel`; `"team"` starts a detached walk of `team` with the body
4604
+ * as its input. */
4605
+ delivery?: "spawn" | "channel" | "team";
4606
+ agent?: string;
4607
+ channel?: string;
4608
+ auth?: WebhookDefAuth;
4609
+ rate_limit?: WebhookDefRateLimit;
4610
+ body_size_limit_bytes?: number;
4611
+ /** Credential key → env-var NAME the receiver reads it from. */
4612
+ user_credentials_from_env?: Record<string, string>;
4613
+ /** Credential key → value, handed to the run. */
4614
+ user_credentials?: Record<string, string>;
4615
+ /** Pins the started run's user tier; a delivery never chooses it. Must be
4616
+ * one of the server's user tiers. */
4617
+ user_tier?: string;
4618
+ /** A forge preset — `"github"`, `"gitea"` or `"gitlab"` — supplying
4619
+ * defaults for the auth kind and headers, the event header, the ping and
4620
+ * the shapes of the events it knows. A field set here overrides it. */
4621
+ provider?: string;
4622
+ /** The request header naming a delivery's event. */
4623
+ event_header?: string;
4624
+ /** When set, the only events that start anything: another is answered 202
4625
+ * `ignored`, the provider's ping 200, and a listed event counts only when
4626
+ * the signed body has its shape. Unset: every delivery is handled. */
4627
+ events?: string[];
4628
+ /** The JSONPaths the body of a listed event must hold, for an event the
4629
+ * provider has no shape for, or in place of its. A fork merges key by key;
4630
+ * an empty list removes that event's shape. */
4631
+ event_shapes?: Record<string, string[]>;
4632
+ /** Non-secret metadata the agent receives as trusted. */
4633
+ metadata?: Record<string, unknown>;
4634
+ /** The tenant spawned runs execute in — taken from the def, never from the
4635
+ * payload. Your own unless you are an admin; omit it to use yours. */
4636
+ tenant_id?: string;
4637
+ /** Target → JSONPath into the request body (`$`, `.key` and `[N]` only).
4638
+ * Targets: `goal`, `user_id`, `user_credentials.<KEY>`,
4639
+ * `run_metadata.<key>`. */
4640
+ payload_mapping?: Record<string, string>;
4641
+ sync_response?: WebhookDefSyncResponse;
4642
+ on_complete?: ScheduledRunHook[];
4643
+ /** `delivery: "team"` only: the team, and each variable as a JSONPath into
4644
+ * the body (`$`, `.key` and `[N]` only). */
4645
+ team?: string;
4646
+ vars?: Record<string, string>;
4647
+ [extra: string]: unknown;
4648
+ }
4649
+ /** One WebhookDef version, as create / fork return it. */
4650
+ export interface WebhookDefRowResponse {
4651
+ def_id: string;
4652
+ name: string;
4653
+ version: number;
4654
+ parent_def_id?: string;
4655
+ description?: string;
4656
+ created_at: string;
4657
+ created_by_agent_id?: string;
4658
+ retired: boolean;
4659
+ bootstrapped_from_static: boolean;
4660
+ /** The stored definition, including the fields the server stamps. */
4661
+ definition: WebhookDefOverlay;
4662
+ promoted?: boolean;
4663
+ /** Same meaning as on {@link ScheduleDefRowResponse}. */
4664
+ disabled_until_credentials_supplied?: string[];
4665
+ unusable_credentials?: string[];
4666
+ }
4667
+ /** Typed input for {@link LoomcycleClient.webhookDef}, one shape per op. */
4668
+ export type WebhookDefInput = {
4669
+ op: "create" | "fork";
4670
+ name: string;
4671
+ /** fork only. Default: the active version of `name`, or its yaml
4672
+ * template. */
4673
+ parent_def_id?: string;
4674
+ overlay?: WebhookDefOverlay;
4675
+ description?: string;
4676
+ /** Default true on both create and fork. */
4677
+ promote?: boolean;
4678
+ } | {
4679
+ op: "get";
4680
+ def_id: string;
4681
+ } | {
4682
+ op: "list";
4683
+ name: string;
4684
+ } | {
4685
+ op: "retire";
4686
+ def_id: string;
4687
+ retired: boolean;
4688
+ };
3835
4689
  /** Static-side agent definition body. Snake_case keys mirror
3836
4690
  * internal/lookup.SubstrateAgentDef so substrate-side and static-side
3837
4691
  * definitions share one renderer. Forward-compatible: unknown fields
@@ -4268,6 +5122,12 @@ export interface CredentialMeta {
4268
5122
  updated_at?: string;
4269
5123
  expires_at?: string;
4270
5124
  status?: string;
5125
+ /** The https URLs the value may be sent to, in canonical form. Absent for
5126
+ * an unrestricted credential; `[]` for one that is sent nowhere. */
5127
+ allowed_urls?: string[];
5128
+ /** The user name sent with the value where a destination takes one (HTTP
5129
+ * basic auth for a git source). Absent when none is stored. */
5130
+ username?: string;
4271
5131
  }
4272
5132
  /** CredentialListResponse is the {op:list} result: metadata for the given scope. */
4273
5133
  export interface CredentialListResponse {
@@ -4392,3 +5252,343 @@ export interface DecisionModelsResponse {
4392
5252
  default: string;
4393
5253
  models: DecisionModel[];
4394
5254
  }
5255
+ /** One name's version roll-up from a `list*DefNames` method. Shared by every
5256
+ * def family except OperatorTokenDef ({@link OperatorTokenDefNameSummary}). */
5257
+ export interface DefNameSummary {
5258
+ name: string;
5259
+ /** The owning tenant; absent for the shared "" tenant. */
5260
+ tenant_id?: string;
5261
+ version_count: number;
5262
+ active_def_id?: string;
5263
+ latest_version: number;
5264
+ /** When the newest version was created (RFC 3339). */
5265
+ last_updated: string;
5266
+ /** Versions not retired. Reported by the AgentDef, SkillDef, HookDef and
5267
+ * MCPServerDef families only. */
5268
+ live_version_count?: number;
5269
+ /** The active version is retired. Same families as `live_version_count`. */
5270
+ active_retired?: boolean;
5271
+ }
5272
+ /** Response of the `list*DefNames` methods. `names` is `[]`, never `null`,
5273
+ * when nothing is declared. */
5274
+ export interface DefNamesResponse {
5275
+ names: DefNameSummary[];
5276
+ }
5277
+ /** One token name's summary from {@link LoomcycleClient.listOperatorTokenDefNames}.
5278
+ * Carries no secret material. */
5279
+ export interface OperatorTokenDefNameSummary {
5280
+ name: string;
5281
+ tenant_id: string;
5282
+ subject: string;
5283
+ /** Every token minted under the name, rotated and retired ones included. */
5284
+ token_count: number;
5285
+ /** A non-retired token exists. */
5286
+ has_current: boolean;
5287
+ last_updated: string;
5288
+ }
5289
+ /** Response of {@link LoomcycleClient.listOperatorTokenDefNames}. */
5290
+ export interface OperatorTokenDefNamesResponse {
5291
+ names: OperatorTokenDefNameSummary[];
5292
+ }
5293
+ /** One schedule in {@link LoomcycleClient.listAllSchedules}: a static yaml
5294
+ * `scheduled_runs` entry, a ScheduleDef, or both under one name. */
5295
+ export interface ScheduleListEntry {
5296
+ name: string;
5297
+ source: "static-only" | "dynamic-only" | "both";
5298
+ in_static: boolean;
5299
+ in_substrate: boolean;
5300
+ version_count?: number;
5301
+ /** The def id the per-schedule methods take. Absent for a static-only
5302
+ * schedule, which has no ScheduleDef to address. */
5303
+ active_def_id?: string;
5304
+ latest_version?: number;
5305
+ /** When the newest ScheduleDef version was created. Absent for a
5306
+ * static-only schedule. */
5307
+ last_updated?: string;
5308
+ /** The yaml definition, for a static entry. */
5309
+ static_definition?: unknown;
5310
+ }
5311
+ /** Response of {@link LoomcycleClient.listAllSchedules}. */
5312
+ export interface ScheduleListResponse {
5313
+ entries: ScheduleListEntry[];
5314
+ }
5315
+ /** A run a schedule started and has not finished. */
5316
+ export interface ScheduleActiveRun {
5317
+ def_id: string;
5318
+ run_id: string;
5319
+ /** The slot the run was fired for. */
5320
+ slot_at: string;
5321
+ /** Fired for a missed slot rather than a live one. */
5322
+ catch_up?: boolean;
5323
+ started_at: string;
5324
+ /** The replica that claimed the slot; absent outside cluster mode. */
5325
+ claimed_by?: string;
5326
+ }
5327
+ /** A schedule's runtime state from {@link LoomcycleClient.getScheduleState}.
5328
+ * Credentials never appear here. An optional instant is absent when unset —
5329
+ * test for presence. (A server before the fix that dropped them sent
5330
+ * `last_run_at` and `paused_until` as "0001-01-01T00:00:00Z" instead.) */
5331
+ export interface ScheduleStateView {
5332
+ def_id: string;
5333
+ /** Absent until the schedule first fires. */
5334
+ last_run_at?: string;
5335
+ last_run_id?: string;
5336
+ /** completed | failed | cancelled | rejected | running | skipped_overlap | skipped… */
5337
+ last_status?: string;
5338
+ last_error?: string;
5339
+ next_run_at: string;
5340
+ /** Present while paused. {@link LoomcycleClient.pauseSchedule} sets it about
5341
+ * 100 years ahead: an indefinite pause. */
5342
+ paused_until?: string;
5343
+ /** Fires so far, the count `max_fires` is measured against. */
5344
+ fire_count: number;
5345
+ /** The last claim: which slot, which replica, when. */
5346
+ slot_at?: string;
5347
+ claimed_by?: string;
5348
+ claimed_at?: string;
5349
+ /** When the last tracked run finished. */
5350
+ finished_at?: string;
5351
+ /** Present while a `catch_up_max` backlog drains: its newest slot. */
5352
+ catch_up_until?: string;
5353
+ /** Slots the last outage dropped (older than `catch_up_max`). */
5354
+ missed_slots?: number;
5355
+ /** Every run the schedule started and has not finished — `[]`, never absent. */
5356
+ active_runs: ScheduleActiveRun[];
5357
+ }
5358
+ /** Result of {@link LoomcycleClient.runScheduleNow}. The run starts on the
5359
+ * scheduler's next tick, not during the call. */
5360
+ export interface ScheduleRunNowResult {
5361
+ def_id: string;
5362
+ scheduled: "next-tick";
5363
+ }
5364
+ /** Result of {@link LoomcycleClient.pauseSchedule} / {@link LoomcycleClient.resumeSchedule}. */
5365
+ export interface SchedulePauseResult {
5366
+ def_id: string;
5367
+ paused: boolean;
5368
+ }
5369
+ /** One configured model alias (the top-level `models:` map). */
5370
+ export interface ModelAlias {
5371
+ provider: string;
5372
+ model: string;
5373
+ /** chat | decision | embedder. Absent for an untagged alias, which is a chat
5374
+ * model. */
5375
+ kind?: string;
5376
+ }
5377
+ /** Response of {@link LoomcycleClient.listModels}: alias name → target. */
5378
+ export interface ModelAliasesResponse {
5379
+ aliases: Record<string, ModelAlias>;
5380
+ }
5381
+ /** A candidate's throughput estimate. `multiplier` is what a scaled timeout
5382
+ * would be multiplied by. */
5383
+ export interface RoutingThroughput {
5384
+ slowdown?: number;
5385
+ multiplier: number;
5386
+ samples: number;
5387
+ source: string;
5388
+ decode_tps?: number;
5389
+ prefill_tps?: number;
5390
+ ttft_ms?: number;
5391
+ cold_load_ms?: number;
5392
+ /** Admin only. */
5393
+ queue_ms_p50?: number;
5394
+ max_multiplier?: number;
5395
+ }
5396
+ /** One candidate in a routing cascade. */
5397
+ export interface RoutingCandidate {
5398
+ provider: string;
5399
+ model: string;
5400
+ /** First in the displayed cascade. */
5401
+ primary: boolean;
5402
+ available?: boolean;
5403
+ /** The first available candidate: what a run resolves to now. */
5404
+ selected?: boolean;
5405
+ stalled?: boolean;
5406
+ rate_limited?: boolean;
5407
+ reachable?: boolean;
5408
+ /** Absent when throughput estimation is off. */
5409
+ throughput?: RoutingThroughput;
5410
+ }
5411
+ /** One tier's ordered cascade, top first. */
5412
+ export interface RoutingTier {
5413
+ /** low | middle | high */
5414
+ tier: string;
5415
+ cascade: RoutingCandidate[];
5416
+ }
5417
+ /** One user tier's tiers. */
5418
+ export interface RoutingUserTier {
5419
+ /** "" when no user tiers are configured. */
5420
+ name: string;
5421
+ tiers: RoutingTier[];
5422
+ }
5423
+ /** One provider in the active-providers header. */
5424
+ export interface RoutingProvider {
5425
+ provider: string;
5426
+ reachable: boolean;
5427
+ excluded: boolean;
5428
+ /** The raw probe error — admin only. */
5429
+ last_error?: string;
5430
+ }
5431
+ /** One web-search provider in the search cascade. */
5432
+ export interface SearchRoutingProvider {
5433
+ provider: string;
5434
+ primary: boolean;
5435
+ /** This caller has a usable key. */
5436
+ keyable?: boolean;
5437
+ /** Keyable and not in a failure cooldown. */
5438
+ available?: boolean;
5439
+ selected?: boolean;
5440
+ reachable?: boolean;
5441
+ /** Admin only. */
5442
+ last_error?: string;
5443
+ }
5444
+ /** Response of {@link LoomcycleClient.getRouting}. */
5445
+ export interface RoutingResponse {
5446
+ generated_at: string;
5447
+ /** The caller gets the admin view: raw `last_error`, nothing filtered. */
5448
+ admin: boolean;
5449
+ providers?: RoutingProvider[];
5450
+ user_tiers: RoutingUserTier[];
5451
+ /** The operator-key gate is on and this tenant's view is filtered to the
5452
+ * providers it can key itself. */
5453
+ operator_key_restricted?: boolean;
5454
+ /** `timeout_scaling.mode` when throughput estimation is on. */
5455
+ throughput_mode?: string;
5456
+ /** Absent when no search providers are configured. */
5457
+ search?: SearchRoutingProvider[];
5458
+ }
5459
+ /** Response of {@link LoomcycleClient.listProviderModels}: a live listing from
5460
+ * the provider, not the cached probe matrix. */
5461
+ export interface ProviderModelsResponse {
5462
+ provider: string;
5463
+ fetched_at: string;
5464
+ duration_ms: number;
5465
+ /** Provider-native model ids. `[]` when the provider answered with none. */
5466
+ models: string[];
5467
+ }
5468
+ /** Options of {@link LoomcycleClient.createTeam}. */
5469
+ export interface CreateTeamOptions {
5470
+ signal?: AbortSignal;
5471
+ /** Make the new version the team's active one. The server default is true
5472
+ * for a create; pass `false` to author it without putting it in force
5473
+ * (then {@link LoomcycleClient.promoteTeam}). */
5474
+ promote?: boolean;
5475
+ /** Free-text rationale stored on the version. */
5476
+ description?: string;
5477
+ }
5478
+ /** Options of {@link LoomcycleClient.forkTeam}. Note `promote` defaults the
5479
+ * other way from a create: false. */
5480
+ export interface ForkTeamOptions extends CreateTeamOptions {
5481
+ /** Make the new version the team's active one. The server default is false:
5482
+ * a fork is authored first and put in force with
5483
+ * {@link LoomcycleClient.promoteTeam}. */
5484
+ promote?: boolean;
5485
+ /** Fork this version instead of the name's active one. */
5486
+ parentDefId?: string;
5487
+ }
5488
+ /** The kind of change a {@link MemoryChange} names. `memory.*` arrive on
5489
+ * {@link LoomcycleClient.streamMemoryChanges}, `document.chunk.*` on
5490
+ * {@link LoomcycleClient.streamDocumentChanges}. Mirrors the runtime's
5491
+ * `store.MemoryChangeType`. */
5492
+ export type MemoryChangeType = "memory.set" | "memory.delete" | "memory.scope_deleted" | "document.chunk.updated" | "document.chunk.deleted";
5493
+ /** The memory scope a change landed in. `global` is listed because the
5494
+ * runtime's scope enum has it and the feed passes the written scope through
5495
+ * unchanged, not because Memory writes it today. */
5496
+ export type MemoryChangeScope = "agent" | "user" | "tenant" | "global";
5497
+ /** One change coordinate, exactly as the runtime's `store.MemoryChange`
5498
+ * serialises.
5499
+ *
5500
+ * VALUE-FREE by design: it names WHAT changed, never the value — pull the
5501
+ * current value through the data API (`getMemoryEntry`, `document`). `key` is
5502
+ * set for `memory.set` / `memory.delete`; `chunk_id` for `document.chunk.*`;
5503
+ * neither for `memory.scope_deleted`, which covers the whole scope. */
5504
+ export interface MemoryChange {
5505
+ /** Monotonic cursor. Persist the last one you processed and pass it back as
5506
+ * `since` to resume after a disconnect. */
5507
+ seq: number;
5508
+ /** The caller's own tenant — the feed is never cross-tenant. Omitted for
5509
+ * the shared "" tenant. */
5510
+ tenant?: string;
5511
+ type: MemoryChangeType;
5512
+ scope: MemoryChangeScope;
5513
+ scope_id: string;
5514
+ key?: string;
5515
+ chunk_id?: string;
5516
+ /** RFC3339 time of the write. */
5517
+ at: string;
5518
+ }
5519
+ /** What the runtime's embedder has actually been doing, since boot. Mirrors
5520
+ * `providers.EmbedderHealth`.
5521
+ *
5522
+ * Carried on the change feed because an embedding failure on a content write
5523
+ * is NOT fatal — the body is stored and the embedding skipped — so an
5524
+ * embedder outage shows as a perfectly busy feed over rows that are no longer
5525
+ * searchable. `untried` is "configured, nothing embedded yet", not a warning;
5526
+ * `absent` is "no embedder configured". */
5527
+ export interface EmbedderHealth {
5528
+ state: "absent" | "untried" | "ok" | "failing";
5529
+ provider?: string;
5530
+ model?: string;
5531
+ calls: number;
5532
+ /** Failed embeddings since boot — roughly how many rows need
5533
+ * `backfillEmbeddings` to become searchable. */
5534
+ failures: number;
5535
+ /** A classified failure kind, never the raw error text (which can carry an
5536
+ * address). */
5537
+ last_failure_kind?: string;
5538
+ /** Unix seconds; absent when never. */
5539
+ last_ok_unix?: number;
5540
+ last_failure_unix?: number;
5541
+ }
5542
+ /** The opening frame of a change feed (`event: feed`). */
5543
+ export interface ChangeFeedStatus {
5544
+ /** Whether writes are being captured at all. `false` means the server runs
5545
+ * without `LOOMCYCLE_MEMORY_CHANGES_ENABLED`: the stream stays open and
5546
+ * idles, and NO change will ever arrive — check this before waiting. */
5547
+ enabled: boolean;
5548
+ /** The cursor the server accepted. `0` when `since` was absent OR
5549
+ * unparseable — the server does not reject a bad cursor. */
5550
+ since: number;
5551
+ /** Reported beside `enabled`, never folded into it: capturing and embedding
5552
+ * fail independently. Absent on a runtime older than the field. */
5553
+ embedder?: EmbedderHealth;
5554
+ }
5555
+ /** A change-feed `event: error` frame: the server could not read its change
5556
+ * log. NON-FATAL — the stream stays open, the server keeps retrying with
5557
+ * backoff, and its cursor does not advance, so change frames resume after
5558
+ * recovery without a gap. Sent once per failure streak, not per retry. */
5559
+ export interface ChangeFeedError {
5560
+ /** Machine-readable; `store_unavailable` today. Branch on this. */
5561
+ code: string;
5562
+ /** Human-readable description. */
5563
+ error: string;
5564
+ }
5565
+ /** Yielded by {@link LoomcycleClient.streamMemoryChanges} and
5566
+ * {@link LoomcycleClient.streamDocumentChanges}.
5567
+ *
5568
+ * Same `{ kind, payload }` shape as {@link RunStateStreamItem}: the first
5569
+ * item is `{ kind: "feed" }`, then one `{ kind: "change" }` per change, with
5570
+ * an occasional `{ kind: "error" }` (see {@link ChangeFeedError}) when the
5571
+ * server cannot read its change log — iteration continues past it. `kind`
5572
+ * rather than `type` so the wrapper's discriminator cannot be confused with
5573
+ * the change's own `payload.type`. */
5574
+ export type ChangeFeedFrame = {
5575
+ kind: "feed";
5576
+ payload: ChangeFeedStatus;
5577
+ } | {
5578
+ kind: "change";
5579
+ payload: MemoryChange;
5580
+ } | {
5581
+ kind: "error";
5582
+ payload: ChangeFeedError;
5583
+ };
5584
+ /** Options for {@link LoomcycleClient.streamMemoryChanges} /
5585
+ * {@link LoomcycleClient.streamDocumentChanges}. */
5586
+ export interface StreamChangesOptions {
5587
+ /** Resume cursor: deliver only changes with `seq` greater than this. Omit
5588
+ * (or `0`) to start from the oldest change the server still retains. Must
5589
+ * be a non-negative integer — the client throws rather than send a value
5590
+ * the server would silently read as `0` and replay everything. */
5591
+ since?: number;
5592
+ /** Abort to close the stream; the iterator then throws an `AbortError`. */
5593
+ signal?: AbortSignal;
5594
+ }