@akanjs/cli 3.0.0-alpha.40 → 3.0.0-alpha.42

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/.build-stamp CHANGED
@@ -1 +1 @@
1
- 873ee7e4176e7c5645844f46050d48c225f02463271a6932419b73784565ed10
1
+ eaeb44245969a1707e38a765624a1541652e2558214acaf1d91d2be618ba8bbe
@@ -689,6 +689,13 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
689
689
  providers the way middleware is applied: `option.applyAdaptor(LlmAdaptorRole, ClaudeLlm)`, where the
690
690
  implementation is an `adapt()` class in a `srvkit/` implementing `LlmAdaptor.chat(request, onDelta?)` — ignore
691
691
  `onDelta` and the chat still answers whole.
692
+ - **An adaptor answers `null` for "not configured" and *throws* for a refusal it can explain.** The two are
693
+ different things to be told: collapsing both into `null`, the way the adapter convention otherwise reads, left a
694
+ user reading `llmUnavailable` — "no model is configured" — about a conversation that had merely outgrown the
695
+ context window. A thrown `Err` reaches the chat as its own text, so the reason the provider gave is what the
696
+ user sees. It travels as the dictionary key plus the values that key interpolates, on both the JSON and the SSE
697
+ path, and **`fetchRunner` resolves it against the dictionary one step before the transcript** — the endpoint has
698
+ no language to resolve it in and the browser does, which is why the key was reaching the screen raw.
692
699
  - **A file the user attaches rides the message, and nothing is stored.** The composer takes a paperclip, a drop and
693
700
  a paste; an image rides as bytes and a text file as text, which is all a browser reads with no dependency.
694
701
  Everything else is the app's own reader — `<Agent.Chat attach={…} />`, one `File` in, a `MessageAttachment` or
@@ -897,9 +904,9 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
897
904
  - **`prompt()` endpoints double as the chat's slash commands.** There is no listing endpoint — the client reads
898
905
  its own serialized signals — so a prompt's dictionary `.desc()` is what the menu shows, and its guards are
899
906
  enforced by the prompt's own GET at call time.
900
- - **The chat answers five slash commands of its own**, listed in the same `/` menu ahead of the prompts:
901
- `/new` (`/clear`), `/retry`, `/copy`, `/help` and `/tools`. An app writes none of them and cannot add one — the
902
- extension point for a product's own command is a `prompt()` endpoint, which is guarded and server-side.
907
+ - **The chat answers six slash commands of its own**, listed in the same `/` menu ahead of the prompts:
908
+ `/new` (`/clear`), `/retry`, `/compact`, `/copy`, `/help` and `/tools`. An app writes none of them and cannot add
909
+ one — the extension point for a product's own command is a `prompt()` endpoint, which is guarded and server-side.
903
910
  **A built-in wins a name collision with a prompt of the same name**, the mirror image of the tool rule: a
904
911
  component's `st.tool` shadows a built-in it means to replace, but no library's prompt may take `/new` away from
905
912
  the user who typed it — so a shadowed prompt is dropped from the menu rather than listed twice. `/new` and
@@ -916,15 +923,31 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
916
923
  only in that browser, so an export is the one path a wrong answer has to whoever could fix it — which is why it
917
924
  carries the route and the timestamp. `/retry` replays only the trailing user message, leaving anything before it
918
925
  in place, so a prompt's own preamble is not sent twice.
926
+ - **A long conversation summarizes itself, because nothing else is keeping it inside the model's window.** The
927
+ loop runs in the browser and the relay holds no session, so an uncompacted chat grows until the provider
928
+ refuses the whole request — a refusal, never a shorter answer, which is why compaction runs *before* the turn
929
+ that would have overflowed rather than as a recovery after it. Past `compact.at` estimated tokens (four
930
+ characters to a token, over the JSON the turn posts; 24k by default, well under the smallest window a provider
931
+ is likely to have, since the tools and the screen context ride on top of it and neither compacts) the history above the last `keep` messages
932
+ becomes one message standing in for it, flagged `summary` on the wire — `<Agent.Chat compact={{ at, keep }} />`
933
+ tunes it per provider and `{ at: 0 }` turns it off, and `/compact` does the same on demand keeping nothing.
934
+ **The cut only ever lands on a user message**: everything above one is settled, so the kept half can never open
935
+ with a `tool` result whose call was summarized away, a shape every provider dialect rejects. The summarizing
936
+ turn carries no tools and no screen context — it summarizes the conversation, it does not act on it — and it is
937
+ fed a *bounded* digest rather than the transcript itself, since the transcript being summarized is the one that
938
+ no longer fits. A summary that cannot be produced leaves the transcript alone and the turn goes out as it would
939
+ have; one that fails to shrink anything is not retried until another threshold's worth has been added. On the
940
+ wire a summary wears the user's role because the wire has no other, so a provider mapping frames it as a system
941
+ message and `/retry` steps over it — replaying it would send the notes back as a question.
919
942
  - **↑ and ↓ in the composer walk what was sent.** A single-line input has nothing of its own on the vertical
920
943
  arrows, and the half-written draft they were walked away from comes back at the bottom of the walk.
921
- - The framework publishes five built-ins on every store surface: `navigate` (internal paths only, the same
944
+ - The framework publishes six built-ins on every store surface: `navigate` (internal paths only, the same
922
945
  router `Link` rides), `goBack` (this session's history — global, because history is not a control a page owns and
923
946
  a page that draws no back link is not one you may not leave), `readScreen` (the rendered DOM as compact text —
924
947
  headings, links, control values, and `(disabled)` on a control or button that has it; the chat's own UI is
925
- skipped via `data-agent-ui`, and a password value is never read), `readState(key)` (one masked store key), and
926
- `highlight(target)`. Declaring a hook tool under one of those names shadows the built-in, so reuse them only to
927
- mean that.
948
+ skipped via `data-agent-ui`, and a password value is never read), `readState(key)` (one masked store key),
949
+ `waitFor(key)` (park until that key moves), and `highlight(target)`. Declaring a hook tool under one of those
950
+ names shadows the built-in, so reuse them only to mean that.
928
951
  - **A tool that changes the screen waits for the screen before it answers.** `router.push` returns while the RSC
929
952
  payload is still in flight and a store action that fires `void fetch.*` commits a tick later, so `navigate`
930
953
  awaits `ScreenSettle.wait()` — DOM quiescence, bounded, because the client router hands its promise to nobody —
@@ -932,6 +955,26 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
932
955
  describes the moment before the change landed and the `readScreen` that follows reads the page the user left.
933
956
  New tools and state from a fresh route are still only listed from the next turn: the catalogue is snapshotted
934
957
  when the turn starts.
958
+ - **A tool that waits for its own work costs no model turns; one that returns early costs one round trip per
959
+ look.** The session awaits `run`, so a `.exec` that awaits the store action finishing the job simply makes the
960
+ turn take that long — and the change report that follows carries whatever landed, so the model needs no second
961
+ call to read the result. A fire-and-forget tool leaves the agent to poll instead, which burns the whole
962
+ `maxTurns` budget in seconds on a job measured in minutes. Say so in the `desc` ("takes about two minutes; do
963
+ not poll while it runs") and, for a route full of slow work, in an `Agent.Guide`.
964
+ - **`waitFor(key, equals?, timeoutSeconds?)` is for the job the *tool* cannot await** — started in an earlier turn,
965
+ or by a person clicking the button. It parks on a **store state key a mounted component subscribes**, exactly the
966
+ set `readState` reads, and resumes the moment the key moves; `equals` waits for one value, omitted it waits for
967
+ any change. **Not a resource**: `st.expose` and `st.useState` register on the surface, whose values already ride
968
+ inline in the screen context block, and `waitFor` refuses one of those names like any other. Deliberately not a
969
+ bare sleep either — a sleep only makes the polling slower, and a value worth waiting minutes for is server-derived
970
+ state, which lives in the store already. Running out is not a failure, it answers with what the key holds now
971
+ (default 120s, clamped to 600), and a key this screen does not read is refused by name with the ones it does.
972
+ - **Stop reaches a tool that is still running.** The session races every call against its abort signal, so a
973
+ two-minute tool does not hold the loop for two minutes after the user presses Stop. The signal itself arrives
974
+ through `AgentAbort.current` — the same module slot `AgentProgress` is — and honouring it is optional, since the
975
+ race lands whatever the tool does; what it buys is the tool's own cleanup, a timer or a poll loop that would
976
+ otherwise run out with nobody left to answer. A tool that ignores it is left running rather than cancelled: the
977
+ work is usually a job a server is already doing.
935
978
  - **`readScreen` takes a `section`, and `highlight` a `target`.** Both resolve a name the agent has already seen —
936
979
  a `data-akan-action` / `data-akan-state` annotation, an `Agent.Zone` or `useScreenScope` container
937
980
  (`data-agent-scope`, which `Load.Units` / `Load.View` / `Data.ListContainer` put on the container they render),
@@ -950,7 +993,8 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
950
993
  - **A slow tool reports its own progress with `AgentProgress.report(message, { done, total })`** from wherever the
951
994
  work is — a store action, an upload loop, an adapter — reached through a module slot rather than a parameter, and
952
995
  a no-op when nobody is rendering it. The chat shows it on that call's row until the row resolves. It is the
953
- browser twin of `McpProgress.report`.
996
+ browser twin of `McpProgress.report`. Import it and `AgentAbort` from `akanjs/store`: an app may not reach
997
+ `use-agentic` directly (`no-import-external-library`), and those two are the channels a long tool body needs.
954
998
  - **The turn cap is a question, not a dead end.** At `maxTurns` the session asks whether to keep going through the
955
999
  same card `askUser` uses, and the answer rides as the user's own turn — so a steer typed instead of the
956
1000
  keep-going choice reaches the model as guidance. A host that renders no `pendingQuestion` passes no
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/cli",
3
- "version": "3.0.0-alpha.40",
3
+ "version": "3.0.0-alpha.42",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -34,7 +34,7 @@
34
34
  "@langchain/openai": "^1.4.6",
35
35
  "@tailwindcss/node": "^4.3.0",
36
36
  "@trapezedev/project": "^7.1.4",
37
- "akanjs": "3.0.0-alpha.40",
37
+ "akanjs": "3.0.0-alpha.42",
38
38
  "chalk": "^5.6.2",
39
39
  "commander": "^14.0.3",
40
40
  "dayjs": "^1.11.20",