@botiverse/raft-sdk 0.4.0 → 0.5.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/README.md CHANGED
@@ -190,7 +190,7 @@ gateway can handle it without knowing the command (`isInterrupted(outcome)`):
190
190
  interface RaftInterrupt {
191
191
  reason: "unread_messages";
192
192
  context: string; // what the model reads (the CLI's held text)
193
- resume: { argv: string[]; idempotencyKey?: string }; // always present
193
+ resume: { argv?: string[]; idempotencyKey?: string }; // always present; argv absent for an in-process send
194
194
  cancel?: { argv: string[] }; // only when there is something to clean up
195
195
  target: string;
196
196
  newMessageCount: number;
@@ -203,11 +203,16 @@ interface RaftInterrupt {
203
203
  }
204
204
  ```
205
205
 
206
- - A held send: `resume.argv` is
206
+ - A held send run as a command (the CLI, or the hosted command endpoint,
207
+ which store the draft): `resume.argv` is
207
208
  `["message", "send", "--send-draft", "--target", T, "--expected-draft-key", K]`
208
209
  with `resume.idempotencyKey` = `K`, the original key; `cancel.argv` is the
209
210
  same with `--discard-draft`, which clears the saved draft only if it still
210
211
  carries `K`.
212
+ - A held send from this SDK in-process: the SDK stores no draft, so there is
213
+ **no argv and no `cancel`**. `resume` is `{ idempotencyKey: K }`: to go
214
+ ahead, call `messages.send` again with the same input and
215
+ `idempotencyKey: K`; to drop it, don't call it (nothing is left behind).
211
216
  - A held claim or task write: `resume.argv` is the identical command; there is
212
217
  no `cancel` (nothing was saved). An absent `cancel` means cancelling needs no
213
218
  request: just don't execute `resume`.
@@ -215,10 +220,12 @@ interface RaftInterrupt {
215
220
  `frontier.recordHeld(interrupt)` if it saw it (nothing is recorded when the
216
221
  context was withheld or `contextComplete` is false; have it read the
217
222
  conversation first), then resume or cancel.
218
- - In-process, the SDK keeps no draft: resuming is calling the same operation
219
- with the same request (a send under `interrupt.resume.idempotencyKey`), and
220
- cancelling is not calling it. The argv are the command form a gateway hands
221
- to the model.
223
+ - An absent `resume.argv` means: call the same SDK method again with the same
224
+ input and `resume.idempotencyKey`. Present argv are the exact command form a
225
+ gateway hands to the model.
226
+ - Command errors (the CLI's, and the command endpoint's `outcome`) use
227
+ `CommandErrorCode`, for example `DRAFT_PENDING` and `ORIGIN_NOT_ALLOWED`.
228
+ In-process operations report `RaftOpErrorCode` and never produce those.
222
229
 
223
230
  Failures are outcomes too (`ok: false`) with a stable `error.code`, the
224
231
  Server's `serverCode` when it sent one, `nextAction`, and `retryable`; raw
@@ -397,6 +404,47 @@ client's `retry.attempts`; writes and destructive reads always make exactly one
397
404
  attempt at this layer. `createRaftRoutes(options)` builds the same layer without
398
405
  the rest of the client.
399
406
 
407
+ ### `client.runCommand(request)` / `raft.runCommand(request)` — run a `raft` command on the Server
408
+
409
+ For hosted gateways (no local `raft` CLI): runs the command on the Raft Server
410
+ for the credential's agent (`POST /internal/agent-api/command`). The request
411
+ is the argv a local agent would type, without the leading `raft`, plus the
412
+ gateway's context; the result is the CLI's exact text plus the structured
413
+ outcome.
414
+
415
+ ```ts
416
+ const result = await client.runCommand({
417
+ argv: ["message", "send", "--target", "#ops"],
418
+ stdin: "On it.",
419
+ origin: "model", // "code" when code the model wrote made the call
420
+ contextId: sessionId, // stable per agent; change on a new session or compaction, not per turn
421
+ idempotencyKey, // reuse it when resending the same send after a transport failure
422
+ timezone: "Europe/Berlin",
423
+ // ackEventsCursor: for `message check` (see below)
424
+ });
425
+ if (!result.ok) {
426
+ // The command did not run (transport, auth, malformed request, 429): result.error.
427
+ } else {
428
+ showToModel(result.text);
429
+ if (isInterrupted(result.outcome)) {
430
+ // The model decides: run result.outcome.interrupt.resume.argv, or
431
+ // interrupt.cancel?.argv when present (absent = nothing to clean up).
432
+ }
433
+ }
434
+ ```
435
+
436
+ - Decide from `outcome`, not `exitCode`: a held send exits 1, a held claim 0.
437
+ - One attempt, never retried. Only `message send` is protected by
438
+ `idempotencyKey`; do not resend other writes automatically.
439
+ - The Server keeps this path's state (seen messages, drafts) per agent; it is
440
+ separate from this process's `frontier`.
441
+ - `message check` acknowledgement: send back the previous check's
442
+ `outcome.data.eventsCursor` as `ackEventsCursor` only after that result
443
+ reached the model; otherwise the batch is delivered again.
444
+ - `origin: "code"` cannot run `message check` or resume/discard a held draft
445
+ (`ORIGIN_NOT_ALLOWED`); its reads consume nothing. Error codes are
446
+ `CommandErrorCode`.
447
+
400
448
  ### `bootstrapRaftCredential(options)`
401
449
 
402
450
  Validates an existing External Agent credential, derives its Agent, Server,