@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
|
-
|
|
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
|
|
901
|
-
`/new` (`/clear`), `/retry`, `/copy`, `/help` and `/tools`. An app writes none of them and cannot add
|
|
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
|
|
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),
|
|
926
|
-
`highlight(target)`. Declaring a hook tool under one of those
|
|
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.
|
|
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.
|
|
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",
|