@north-light/cli 0.3.576 → 0.3.578

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.
Files changed (54) hide show
  1. package/dist/api/contract.d.ts +1 -1
  2. package/dist/api/contract.js +1 -1
  3. package/dist/api/dto/run-events.d.ts +10 -4
  4. package/dist/builtin-memory/crouter-sdk/release-notes.md +12 -0
  5. package/dist/builtin-memory/crouter-sdk/streaming.md +2 -0
  6. package/dist/builtin-memory/northlight-apps/concepts/runs.md +2 -0
  7. package/dist/builtin-memory/northlight-apps/guides/stream-a-run.md +1 -1
  8. package/dist/builtin-memory/northlight-apps/guides/test-your-app.md +1 -1
  9. package/dist/clients/attach/viewer.js +207 -207
  10. package/dist/core/io.js +4 -4
  11. package/dist/core/llm/refusal.d.ts +2 -1
  12. package/dist/core/llm/refusal.js +1 -1
  13. package/dist/core/llm/run.js +1 -1
  14. package/dist/core/model-routes.d.ts +11 -0
  15. package/dist/core/model-routes.js +1 -1
  16. package/dist/core/peer/envelope.d.ts +2 -0
  17. package/dist/core/peer/envelope.js +1 -1
  18. package/dist/core/providers/call.js +1 -1
  19. package/dist/core/runs/operations.d.ts +1 -1
  20. package/dist/core/runs/operations.js +5 -5
  21. package/dist/core/runtime/broker/daemon-ops.d.ts +4 -0
  22. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  23. package/dist/core/runtime/broker/held-deferred-inbox.d.ts +4 -0
  24. package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
  25. package/dist/core/runtime/broker/rebind.d.ts +3 -0
  26. package/dist/core/runtime/broker/rebind.js +1 -1
  27. package/dist/core/runtime/broker/run-steer.d.ts +15 -0
  28. package/dist/core/runtime/broker/run-steer.js +1 -0
  29. package/dist/core/runtime/broker/run-turn-queue.d.ts +2 -0
  30. package/dist/core/runtime/broker/run-turn-queue.js +1 -1
  31. package/dist/core/runtime/broker.js +1 -1
  32. package/dist/core/runtime/revive.d.ts +6 -0
  33. package/dist/core/runtime/revive.js +2 -2
  34. package/dist/core/runtime/run-command.js +1 -1
  35. package/dist/core/runtime/runner/blaxel/client.d.ts +2 -2
  36. package/dist/core/runtime/runner/blaxel/client.js +1 -1
  37. package/dist/core/runtime/runner/blaxel/runner.js +2 -2
  38. package/dist/core/runtime/spawn.js +2 -2
  39. package/dist/daemon/api/handlers/messages.js +2 -2
  40. package/dist/daemon/api/handlers/nodes.js +1 -1
  41. package/dist/daemon/api/handlers/runs.js +1 -1
  42. package/dist/daemon/peer/forward.js +1 -1
  43. package/dist/daemon/runs/append-section.js +2 -2
  44. package/dist/pi-extensions/canvas-bash-valve.js +6 -6
  45. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
  46. package/dist/pi-extensions/canvas-stophook.js +1 -1
  47. package/docs/apps/concepts/runs.md +2 -0
  48. package/docs/apps/guides/stream-a-run.md +1 -1
  49. package/docs/apps/guides/test-your-app.md +1 -1
  50. package/docs/sdk/release-notes.md +12 -0
  51. package/docs/sdk/streaming.md +2 -0
  52. package/package.json +2 -2
  53. package/packages/crouter-identity/package.json +1 -1
  54. package/runtime.lock.json +162 -665
@@ -1,5 +1,5 @@
1
1
  /** The contract level this build serves and requires. Written by `scripts/contract.mjs release`; never edit by hand. */
2
- export declare const CONTRACT_LEVEL = "1.38";
2
+ export declare const CONTRACT_LEVEL = "1.40";
3
3
  /** Request header naming the one contract level the caller needs, e.g. `1.3`. Optional: absent means no check. */
4
4
  export declare const REQUIRED_CONTRACT_HEADER = "Crouter-Required-Contract";
5
5
  /** Response header listing the contract levels the server serves, comma-separated, one per major, e.g. `1.3`. */
@@ -1 +1 @@
1
- var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.38",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
1
+ var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.40",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
@@ -32,10 +32,13 @@ export type RunActivityDTO = {
32
32
  };
33
33
  };
34
34
  /** Why a turn ended in error: `content_refused` when the vendor refused the
35
- * model call on safety grounds, `model_error` for any other failed call. */
35
+ * model call on safety grounds, `rate_limited` when every model route is
36
+ * rate-limited for longer than a turn waits, `model_error` for any other failed call. */
36
37
  export interface TurnErrorDTO {
37
- code: 'content_refused' | 'model_error';
38
+ code: 'content_refused' | 'model_error' | 'rate_limited';
38
39
  message: string;
40
+ /** ISO time the rate limit is expected to lift; present only when `code` is `rate_limited`. */
41
+ retry_at?: string;
39
42
  }
40
43
  /** The broker's stop reason for one assistant message, unchanged. */
41
44
  export type AssistantStopReasonDTO = 'stop' | 'toolUse' | 'length' | 'error' | 'aborted';
@@ -111,7 +114,10 @@ export interface NodeMessageAcceptedEvent extends RunEventBaseDTO {
111
114
  }
112
115
  export interface NodeTurnStartedEvent extends RunEventBaseDTO {
113
116
  type: 'node.turn.started';
114
- /** `message_ids`: the accepted `run.message` messages this turn delivers (empty for the prompt, an answer, or a child's report). */
117
+ /**
118
+ * `message_ids`: the accepted `run.message` messages this turn delivers (empty for the prompt, an answer, or a child's report).
119
+ * A turn can emit more than one `node.turn.started`: each time a steered `run.message` joins the open turn at the agent's next step, another one arrives with that message's `message_ids` and its own `entry_id`. A single `node.turn.completed` ends the turn and carries every joined id.
120
+ */
115
121
  input: {
116
122
  text: string;
117
123
  message_ids: string[];
@@ -156,7 +162,7 @@ export interface NodeToolCallCompletedEvent extends RunEventBaseDTO, Redactable
156
162
  }
157
163
  export interface NodeTurnCompletedEvent extends RunEventBaseDTO {
158
164
  type: 'node.turn.completed';
159
- /** The same as the turn's `node.turn.started` `input.message_ids`. */
165
+ /** Every `input.message_ids` of the turn's `node.turn.started` events, in order (more than one when messages joined the turn). */
160
166
  message_ids: string[];
161
167
  /** The run's `run.get` status when the turn ended. */
162
168
  run_status: Exclude<RunStatusDTO, 'settled'>;
@@ -9,10 +9,22 @@ when-and-why-to-read: When you need Release notes, this knowledge should be read
9
9
 
10
10
  Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
11
11
 
12
+ ## 0.3.578
13
+
14
+ `runs.message` now steers a running root turn by default: the agent reads it at its next step. Pass `{ steer: false }` to wait for the current turn to end and start a separate turn. `start_turn: false` remains deferred and cannot use `steer: true`. When a later message joins a turn, `runs.reply` stops yielding text for the earlier message at that boundary, but still waits for the turn's completion. An app on an older SDK gets steering as soon as the runtime upgrades, because it cannot send `steer: false`; its `runs.reply` then streams the answer to a joined later message as part of the earlier reply. Upgrade the SDK to get the `RunReply` boundary and the opt-out.
15
+
16
+ ## 0.3.577
17
+
18
+ `POST /v1/nodes` with a pinned `cwd` that is not an existing directory on the runtime's host now answers `cwd_missing` instead of creating a node whose broker then fails to launch. A sandbox, isolated or worktree node is not checked. `POST /v1/nodes/{id}/messages` with `reopen` to a dormant node whose recorded cwd is gone now answers `cwd_missing` before the reopen is committed or any mail is queued, instead of accepting the message and reporting `wake_failed`.
19
+
12
20
  ## 0.3.576
13
21
 
14
22
  On a cloud role, `provider.tools` now reports a granted provider as `available` once its bundle is installed in the deployment's shared plugin directory. It no longer answers `500` when a person has no materialized bundle; a granted provider still being installed reports `installing` or `install_failed`. Providers without that person's grant remain hidden. The Mac's plugin settings and provider tools behavior are unchanged.
15
23
 
24
+ ## Next release
25
+
26
+ A turn that ends because every model route is rate-limited for longer than a turn waits now reports `error.code: 'rate_limited'` with `error.retry_at` (ISO time the limit is expected to lift), on `turn.completed` and `runs.get().last_turn_error`. It was `model_error` with the time only inside `message`. The run stays `idle` and usable, as before. An app that matches `error.code` exhaustively should handle the new value; one that treats any other code as a model error needs no change.
27
+
16
28
  ## 0.3.572
17
29
 
18
30
  `POST /v1/runs` works on a cloud role (a runtime started with `--role api` or `--role reconcile`). It answered `500 server_failure` because the role has no root launcher and the run start tried to prepare an app OS user and folder. Behavior on a person's Mac and on a hosted guest with a launcher is unchanged.
@@ -70,6 +70,8 @@ Every event except `error` carries `node_id` and `sequence_number` in addition t
70
70
  | `node.settled` | `{ node_id, sequence_number, outcome }` — terminal; the daemon ends the response after it |
71
71
  | `error` | `{ error: { code: 'stream_gap' \| 'stream_dropped' \| 'stream_error', message, details?: { earliest_sequence?: number } } }` — `stream_gap` is recoverable; the other codes terminate the response |
72
72
 
73
+ A turn can emit more than one `node.turn.started`. A `run.message` sent while the turn is running steers by default, so the agent reads it at its next step and the stream emits another `node.turn.started` whose `input.message_ids` holds the joined message; the turn ends with one `node.turn.completed` whose `message_ids` lists every message that joined it. A reply to one message therefore ends at the turn's completion, not at the next `node.turn.started`; the SDK's `RunReply` stops yielding text once a later message joins. Send `{ steer: false }` to start a separate turn after the current one ends.
74
+
73
75
  `summary` on a tool-call event states whether arguments are null, an array and its item count, an object and its field count, or a primitive type. It never includes argument values or tool output.
74
76
 
75
77
  **Deliberately not carried:** thinking deltas, the model's tool-call construction deltas, raw tool output, and the system prompt. Those belong to the owner's viewer, not to an application's run stream.
@@ -33,6 +33,8 @@ const run = await runtime.runs.wait(started.run_id); // status is 'idle' once th
33
33
 
34
34
  `runs.wait` returns on `waiting_on_user`, `idle` and `settled`, so check the status before you read a result.
35
35
 
36
+ A turn that fails on a model error leaves the run `idle`, not `settled`, with `last_turn_error` set (`{code, message}`); the same error is on that turn's `turn.completed` event. The run stays usable, and a retry is a `runs.message`. When `code` is `rate_limited`, every model route is rate-limited and `retry_at` (an ISO time) says when the limit is expected to lift: send the retry after `retry_at`, or `runs.cancel` the run, rather than wait for its deadline. A message sent before `retry_at` fails the same way.
37
+
36
38
  ## Outcome and result
37
39
 
38
40
  A `settled` run has an `outcome`:
@@ -44,7 +44,7 @@ A chat app sends a message and shows the answer to it. `runs.reply(runId, sent,
44
44
  const sent = await runtime.runs.message(runId, 'What did the report say?');
45
45
  for await (const event of runtime.runs.reply(runId, sent)) {
46
46
  if (event.type === 'text.delta') append(event.delta);
47
- if (event.type === 'turn.completed' && event.error) showError(event.error); // {code: 'content_refused' | 'model_error', message}
47
+ if (event.type === 'turn.completed' && event.error) showError(event.error); // {code: 'content_refused' | 'model_error' | 'rate_limited', message, retry_at?}
48
48
  }
49
49
  // or collect it: text as it streams, then the whole text and how the turn ended
50
50
  const {text, ended, error, outcome} = await runtime.runs.reply(runId, sent).collect({onDelta: (delta) => append(delta)});
@@ -62,7 +62,7 @@ A responder gets `{run, kind: 'start' | 'message', messages}`. It is called afte
62
62
  | Method | Effect |
63
63
  |---|---|
64
64
  | `reply(text, {chunks = 2, messageIds?})` | One turn: `node.turn.started`, text deltas, `node.output_text.done`, `node.turn.completed`. Delivers `messageIds` (default: every pending message). The run goes `idle` (`waiting_on_user` with an open question). |
65
- | `failTurn({code: 'model_error' \| 'content_refused', message})` | The same turn ending `stop_reason: 'error'`; `runs.get` shows `last_turn_error`; `runs.reply(...).collect()` ends `error`. |
65
+ | `failTurn({code: 'model_error' \| 'content_refused' \| 'rate_limited', message, retry_at?})` | The same turn ending `stop_reason: 'error'`; `runs.get` shows `last_turn_error`; `runs.reply(...).collect()` ends `error`. `retry_at` (ISO) is required with `rate_limited` and refused with any other code. |
66
66
  | `setActivity(activity, {at?})` | Fake-only: status `running` with this `activity` until the next event. The only way to reach `writing` or a streaming tool call. |
67
67
  | `settle(ending)` | `{result}`, `{declined: {reason, code, retryable}}`, `{failure: 'canceled' \| 'deadline_exceeded'}`, or `{runtimeError: reason}`. Emits `node.settled` with the outcome the runtime writes. |
68
68
  | `idle()` | Goes idle without a new turn (stopped without submitting). |