@akanjs/cli 3.0.0-alpha.31 → 3.0.0-alpha.33

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
- 6666c78ff8b3c6d6f59ad62c78bd4c8771fbd5e4f66acfa5bb2402f05b6ed982
1
+ 92c020a25f9a2117ffc1aac1e1581b005e93ce7da63656e22aa9756cad18ea57
@@ -63,6 +63,13 @@ that looks wrong; do not "fix" it back.
63
63
  exempt because neither is an instance: `cnst.<Enum>["value"]`, whose indexed access resolves to a string union, and
64
64
  a `ClientInit` / `ClientView` / `ClientEdit` type argument, which the framework maps to `GetStateObject<…>` plain
65
65
  data. Any *other* indexed access is still flagged — `cnst.Banner["image"]` is a `File`.
66
+ **Only prop positions are read** — a `*Props` interface or type alias, and the inline object type on the
67
+ component's own parameter. A `cnst` type that never leaves the file is not a boundary crossing and stays legal: a
68
+ local annotation, a callback parameter the framework itself types with the model
69
+ (`renderItem={(ticket: cnst.LightTicket) => …}`), a module-scope helper, a non-`Props` local shape, and the props
70
+ of a component nested inside another one. A function-typed prop (`onPick?: (t: cnst.LightTicket) => void`) is
71
+ exempt for the same reason — a closure cannot cross the RSC boundary at all, so whoever passes it is a client
72
+ component already holding the value.
66
73
  - **No deep imports past a barrel** (`no-deep-internal-import.grit`). Cross-module constant references such as
67
74
  `../map/map.constant` are the sanctioned exception.
68
75
  - **Never import across the client/server boundary.** Client files (`ui/`, `webkit/`, `page/`, `*.store.ts`, every
@@ -390,6 +397,15 @@ workflow changes.
390
397
  - Numbers must use `Int` or `Float` — `Number` is rejected (`pkgs/akanjs/signal/endpointInfo.ts`).
391
398
  - `Upload` is valid only inside a mutation flagged for file upload: `mutation([cnst.File], { fileUpload: true }).body("files", [Upload])` (see `libs/shared/lib/file/file.signal.ts`). It is not a model field type.
392
399
 
400
+ ### Mutation HTTP Verb
401
+
402
+ - A `mutation` is `POST`. `{ method: "PATCH" | "PUT" | "DELETE" }` moves it, and one path may carry several verbs
403
+ — a `query` GET and a `mutation` POST on the same custom `path` are mounted side by side. Two endpoints claiming
404
+ the same path **and** verb fail the boot rather than silently shadowing one another.
405
+ - Reach for it only when a foreign wire protocol forces the verb (a client you cannot change that sends
406
+ `PATCH /rest/v1/<table>`). Akan's own `fetch.*` client, the OpenAPI document, and the API explorer all follow
407
+ whatever is declared, so nothing needs restating per caller.
408
+
393
409
  ### Reserved Endpoint Names
394
410
 
395
411
  - Auto-generated CRUD endpoints (e.g. `create<Model>`, `update<Model>`, `remove<Model>`) already exist for every model. Do not declare an `Endpoint`/`Slice` with a name that collides with them.
@@ -648,9 +664,10 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
648
664
  `AKAN_AGENT=false` takes it off — and negotiates streaming via `accept`, so assistant text arrives as it is
649
665
  generated with zero app code. The endpoint is a stateless relay and **never executes tools**: every tool runs in
650
666
  the caller's own browser session, gated by guards and the approval card. Its guard is `AgentRelayAccess`, which
651
- **refuses every call until a policy is registered** — the same answer `None` gives, with no boot warning. A
652
- product with accounts locks it in its `option.ts`, `option.setAgentAccess((ctx) => !!ctx.get("account"))`;
653
- without a policy the chat cannot spend the LLM key.
667
+ **refuses every call until the app names a guard of its own** — the same answer `None` gives, with no boot
668
+ warning. A product with accounts names it in its `option.ts`, `option.setAgentAccess(SignedIn)`, taking the same
669
+ guard classes every endpoint takes (an array is ANDed, `null` clears what a library set); without one the chat
670
+ cannot spend the LLM key.
654
671
  `persist` keeps the transcript across reloads (sessionStorage; `{ storage: "local" }` to outlive the tab),
655
672
  default off. Re-skin through the `AgentChat` slot in `_overrides.tsx`.
656
673
  - **The LLM is configured in `option.ts`, never through the environment.** `option.setLlm({ apiKey, model, host })`
@@ -693,7 +710,11 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
693
710
  globally and a component cannot build one per render: pass the list it has — a slice's sort keys, the options a
694
711
  prop carried — and it is published and enforced the same way. Neither reaches a set that fills in *after* the
695
712
  first render, since a declaration is mount-static; put that in the tool's `guard`, which is re-read per call and
696
- can name the current values in its refusal.
713
+ can name the current values in its refusal. **An argument type nothing can describe — a model class, `Any`, a
714
+ `Map` — withdraws the whole tool and says so on the console**, naming the tool, the argument and the type; the
715
+ callable still drives the click a person makes. It does not throw: a tool schema is built during render, and an
716
+ agent-tooling mistake that aborted the render would cost the route its server rendering. `st.useState`'s `set`
717
+ degrades the same way, to read-only.
697
718
  - **A component that renders once per row publishes nothing.** A tool registered under one name by fifty rows is
698
719
  forty-nine collisions and one survivor. The container publishes one tool taking the id instead —
699
720
  `removeTask(taskId)`, never fifty `removeTask` — and the agent reads the ids from the `<slice>.items` resource
@@ -732,6 +753,27 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
732
753
  - **`prompt()` endpoints double as the chat's slash commands.** There is no listing endpoint — the client reads
733
754
  its own serialized signals — so a prompt's dictionary `.desc()` is what the menu shows, and its guards are
734
755
  enforced by the prompt's own GET at call time.
756
+ - **The chat answers five slash commands of its own**, listed in the same `/` menu ahead of the prompts:
757
+ `/new` (`/clear`), `/retry`, `/copy`, `/help` and `/tools`. An app writes none of them and cannot add one — the
758
+ extension point for a product's own command is a `prompt()` endpoint, which is guarded and server-side.
759
+ **A built-in wins a name collision with a prompt of the same name**, the mirror image of the tool rule: a
760
+ component's `st.tool` shadows a built-in it means to replace, but no library's prompt may take `/new` away from
761
+ the user who typed it — so a shadowed prompt is dropped from the menu rather than listed twice. `/new` and
762
+ `/copy` are also dispatched *before* the is-a-turn-running check, because mid-turn is exactly when they are
763
+ reached for; `/new` therefore aborts the turn it is clearing and waits for it to wind down, since the loop
764
+ clears its own running flag a microtask later and a transcript emptied before that lands is one the dying turn
765
+ appends onto.
766
+ - **A command's output is a `local` message: rendered in the transcript, withheld from the wire.** The transcript
767
+ *is* the model's history, so `/help` text appended plainly would come back next turn as something the assistant
768
+ believes it said. `session.note(text)` is the only way to write one, `session.report(error)` stays what a
769
+ host-side *failure* lands in, and `local` messages are left out of a `/copy` export too — they are the chat
770
+ talking to itself. Their text is user-facing, so it goes through `l("base.*")` like every other chat string.
771
+ - **`/copy` exists because nothing else keeps the transcript.** The relay is stateless and the conversation lives
772
+ only in that browser, so an export is the one path a wrong answer has to whoever could fix it — which is why it
773
+ carries the route and the timestamp. `/retry` replays only the trailing user message, leaving anything before it
774
+ in place, so a prompt's own preamble is not sent twice.
775
+ - **↑ and ↓ in the composer walk what was sent.** A single-line input has nothing of its own on the vertical
776
+ arrows, and the half-written draft they were walked away from comes back at the bottom of the walk.
735
777
  - The framework publishes five built-ins on every store surface: `navigate` (internal paths only, the same
736
778
  router `Link` rides), `goBack` (this session's history — global, because history is not a control a page owns and
737
779
  a page that draws no back link is not one you may not leave), `readScreen` (the rendered DOM as compact text —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/cli",
3
- "version": "3.0.0-alpha.31",
3
+ "version": "3.0.0-alpha.33",
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.31",
37
+ "akanjs": "3.0.0-alpha.33",
38
38
  "chalk": "^5.6.2",
39
39
  "commander": "^14.0.3",
40
40
  "dayjs": "^1.11.20",