@akanjs/cli 3.0.0-beta.1 → 3.0.0-beta.11

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 (60) hide show
  1. package/.build-stamp +1 -1
  2. package/TunnelShare-sbsaggmb.js +9 -0
  3. package/{abstractCompactor-mjh5ppsq.js → abstractCompactor-bqh31fk5.js} +2 -1
  4. package/{agent.command-d2gfxfck.js → agent.command-7357vxd8.js} +5 -5
  5. package/{application.command-pj2m7584.js → application.command-pc8mdn3w.js} +10 -10
  6. package/{applicationBuildRunner-kxwbssgn.js → applicationBuildRunner-4f1wrba0.js} +2 -2
  7. package/{applicationReleasePackager-4yasxwv9.js → applicationReleasePackager-jkjmy5ee.js} +1 -1
  8. package/buildBatch.proc.js +2 -2
  9. package/{capacitorApp-36rbr858.js → capacitorApp-hqvsrec4.js} +2 -2
  10. package/{cloud.command-jshwdq22.js → cloud.command-cawrd5nq.js} +12 -11
  11. package/cloud.script-1bmptvw0.js +20 -0
  12. package/commandManifest.json +1 -1
  13. package/{context.command-ez8a0zyz.js → context.command-982b6kdp.js} +14 -13
  14. package/{devTui-40ggmd99.js → devTui-hmteztv3.js} +4 -24
  15. package/{devTuiView-c08ccbat.js → devTuiView-40jvjnyz.js} +1 -1
  16. package/{guideline.command-xmqwx8zm.js → guideline.command-jn0vwszx.js} +4 -3
  17. package/guidelines/agentRule/agentRule.instruction.md +109 -4
  18. package/guidelines/conventions/conventions.instruction.md +19 -0
  19. package/guidelines/fieldRule/fieldRule.instruction.md +1 -0
  20. package/guidelines/transportRule/transportRule.instruction.md +49 -0
  21. package/incrementalBuilder.proc.js +1 -1
  22. package/{index-ktb5njgy.js → index-15r9a10e.js} +2 -2
  23. package/{index-259dsnz3.js → index-1nwxbr2y.js} +2 -2
  24. package/{index-600zhnve.js → index-3pvkjza8.js} +10 -10
  25. package/{index-2tv3fdx4.js → index-5nyznra1.js} +1 -0
  26. package/{index-wt7qrtd4.js → index-7f4dsf9v.js} +1 -1
  27. package/{index-7737gxcp.js → index-8pkvrcv1.js} +3 -3
  28. package/{index-2hq52jq3.js → index-8tfmh0v6.js} +25 -19
  29. package/{index-zry5nz81.js → index-ay8wj00x.js} +23 -14
  30. package/{index-mak3cd28.js → index-b3ae16mg.js} +25 -25
  31. package/index-bw2marpf.js +237 -0
  32. package/{index-rwg2aa4f.js → index-ceph7zqj.js} +3 -3
  33. package/{index-fz5q3gd7.js → index-em4swemt.js} +3 -3
  34. package/{index-8pm30m73.js → index-fr2x3qvv.js} +7 -7
  35. package/index-jcekapz4.js +49 -0
  36. package/{index-gyd220p2.js → index-jwjaf9fy.js} +1 -1
  37. package/{index-4edzf1gr.js → index-kbsach1y.js} +4 -4
  38. package/index-p6pbz9n5.js +25 -0
  39. package/{index-v0qh106m.js → index-r8bd61d9.js} +46 -11
  40. package/{index-ne2qy0nk.js → index-s3fvrydw.js} +3 -3
  41. package/{index-pnqbkf5p.js → index-sh88vtye.js} +1 -1
  42. package/{index-ws5e0j51.js → index-t8gvwcrx.js} +2 -2
  43. package/{index-a64esmtb.js → index-vrwdx0zx.js} +1 -1
  44. package/{index-kn1h7p1x.js → index-yc1keaer.js} +4 -226
  45. package/{index-j9yhg404.js → index-ys7dvdb3.js} +1 -1
  46. package/index.js +20 -19
  47. package/{library.command-nbsky672.js → library.command-1f9h2ex6.js} +3 -3
  48. package/{localRegistry.command-yeq57wjr.js → localRegistry.command-rx758ajg.js} +9 -8
  49. package/{module.command-29vqzrh8.js → module.command-c2eve036.js} +7 -6
  50. package/{package.command-axv0abmn.js → package.command-y2rh4np7.js} +3 -3
  51. package/package.json +2 -2
  52. package/{page.command-vs9s3zrh.js → page.command-e5h29fyt.js} +3 -3
  53. package/{primitive.command-cbxmqbtc.js → primitive.command-c44f7z1s.js} +8 -7
  54. package/{quality.command-gmbs7nws.js → quality.command-5evdd263.js} +2 -2
  55. package/{repair.command-bqsjptsj.js → repair.command-c2amvt74.js} +5 -5
  56. package/{scalar.command-6k1vhe9s.js → scalar.command-xyzerygj.js} +6 -5
  57. package/{subspace.command-fwjfcts7.js → subspace.command-ebfwzypw.js} +92 -12
  58. package/tunnel.command-v6a7at16.js +130 -0
  59. package/{workflow.command-nwpcdc8p.js → workflow.command-t174m6sy.js} +11 -10
  60. package/{workspace.command-8s050m0h.js → workspace.command-bg3eveth.js} +23 -22
package/.build-stamp CHANGED
@@ -1 +1 @@
1
- d72ecb79076daa348d8166c58417584a4ceda3a467c9d1a7ca95d7d85863822a
1
+ 14dcf08e297228b84d390560fb0b666562b61989362641c9027868a93d4c503a
@@ -0,0 +1,9 @@
1
+ // @bun
2
+ import {
3
+ TunnelShare2
4
+ } from "./index-jcekapz4.js";
5
+ import"./index-bw2marpf.js";
6
+ import"./index-q0y2jq9j.js";
7
+ export {
8
+ TunnelShare2 as TunnelShare
9
+ };
@@ -1,8 +1,9 @@
1
1
  // @bun
2
2
  import"./index-q0y2jq9j.js";
3
+ import"./index-bw2marpf.js";
3
4
  import {
4
5
  AiSession
5
- } from "./index-kn1h7p1x.js";
6
+ } from "./index-yc1keaer.js";
6
7
  import {
7
8
  AbstractDoc
8
9
  } from "./index-meh558jy.js";
@@ -2,17 +2,17 @@
2
2
  import {
3
3
  Workspace,
4
4
  command
5
- } from "./index-mak3cd28.js";
5
+ } from "./index-b3ae16mg.js";
6
6
  import"./index-q0y2jq9j.js";
7
- import"./index-2tv3fdx4.js";
7
+ import"./index-5nyznra1.js";
8
8
  import"./index-6wwzs1bc.js";
9
9
  import"./index-by5gpp8x.js";
10
- import"./index-gyd220p2.js";
11
- import"./index-ktb5njgy.js";
10
+ import"./index-jwjaf9fy.js";
11
+ import"./index-15r9a10e.js";
12
12
  import"./index-h7yw2z9n.js";
13
13
  import {
14
14
  AgentScript
15
- } from "./index-ne2qy0nk.js";
15
+ } from "./index-s3fvrydw.js";
16
16
 
17
17
  // pkgs/@akanjs/cli/agent/agent.command.ts
18
18
  class AgentCommand extends command("agent", [AgentScript], ({ public: target }) => ({
@@ -6,22 +6,22 @@ import {
6
6
  Exec,
7
7
  Workspace,
8
8
  command
9
- } from "./index-mak3cd28.js";
9
+ } from "./index-b3ae16mg.js";
10
10
  import"./index-q0y2jq9j.js";
11
- import"./index-2tv3fdx4.js";
11
+ import"./index-5nyznra1.js";
12
12
  import"./index-6wwzs1bc.js";
13
13
  import {
14
14
  ApplicationScript
15
- } from "./index-v0qh106m.js";
16
- import"./index-259dsnz3.js";
15
+ } from "./index-r8bd61d9.js";
16
+ import"./index-1nwxbr2y.js";
17
17
  import"./index-hwkygn9s.js";
18
- import {
19
- AbstractDoc
20
- } from "./index-meh558jy.js";
21
- import"./index-j9yhg404.js";
18
+ import"./index-ys7dvdb3.js";
22
19
  import {
23
20
  getMobileTargetChoices
24
21
  } from "./index-47pj3aee.js";
22
+ import {
23
+ AbstractDoc
24
+ } from "./index-meh558jy.js";
25
25
 
26
26
  // pkgs/@akanjs/cli/application/application.command.ts
27
27
  import { select } from "@inquirer/prompts";
@@ -106,8 +106,8 @@ class ApplicationCommand extends command("application", [ApplicationScript], ({
106
106
  start: target({ short: true, desc: "Start development server(s) (frontend SSR + backend)" }).with(Apps).option("plain", Boolean, { desc: "print prefixed lines instead of the full-screen view", default: false }).option("kill", Boolean, { flag: "k", desc: "free the dev ports first, whoever is holding them", default: false }).option("concurrency", Number, {
107
107
  desc: "apps to boot at a time (default: what this machine's memory and cores allow)",
108
108
  nullable: true
109
- }).option("dbup", Boolean, { desc: "start the local database first", default: true }).option("open", Boolean, { desc: "open web browser?", default: false }).option("write", Boolean, { desc: "write code generation", default: true }).exec(async function(apps, plain, kill, concurrency, dbup, open, write) {
110
- await this.applicationScript.start(apps, { plain, kill, concurrency, dbup, open, write });
109
+ }).option("dbup", Boolean, { desc: "start the local database first", default: true }).option("open", Boolean, { desc: "open web browser?", default: false }).option("share", Boolean, { desc: "also share each app on a public URL through an akan tunnel", default: false }).option("write", Boolean, { desc: "write code generation", default: true }).exec(async function(apps, plain, kill, concurrency, dbup, open, share, write) {
110
+ await this.applicationScript.start(apps, { plain, kill, concurrency, dbup, open, share, write });
111
111
  }),
112
112
  startIos: target({ short: true, desc: "Start iOS app in simulator or device" }).with(App).option("target", String, mobileTargetOption).option("env", String, {
113
113
  enum: ["local", "debug", "develop", "main"],
@@ -1,14 +1,14 @@
1
1
  // @bun
2
2
  import {
3
3
  Spinner
4
- } from "./index-2tv3fdx4.js";
4
+ } from "./index-5nyznra1.js";
5
5
  import {
6
6
  AllRoutesBuilder,
7
7
  CsrArtifactBuilder,
8
8
  FontPruner,
9
9
  precompressArtifacts,
10
10
  SsrBaseArtifactBuilder
11
- } from "./index-a64esmtb.js";
11
+ } from "./index-vrwdx0zx.js";
12
12
  import"./index-w8kmx1b6.js";
13
13
  import"./index-q0y2jq9j.js";
14
14
  import"./index-hwkygn9s.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./index-q0y2jq9j.js";
5
5
  import {
6
6
  Spinner
7
- } from "./index-2tv3fdx4.js";
7
+ } from "./index-5nyznra1.js";
8
8
 
9
9
  // pkgs/@akanjs/devkit/applicationReleasePackager.ts
10
10
  import { cp, mkdir, rm } from "fs/promises";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  WorkspaceExecutor,
4
4
  AppExecutor
5
- } from "./index-2tv3fdx4.js";
5
+ } from "./index-5nyznra1.js";
6
6
  import"./index-q0y2jq9j.js";
7
7
  import"./index-6wwzs1bc.js";
8
8
  import {
@@ -11,7 +11,7 @@ import {
11
11
  CsrArtifactBuilder,
12
12
  FontOptimizer,
13
13
  SsrBaseArtifactBuilder
14
- } from "./index-a64esmtb.js";
14
+ } from "./index-vrwdx0zx.js";
15
15
  import"./index-w8kmx1b6.js";
16
16
  import"./index-hwkygn9s.js";
17
17
  import"./index-n68jx79w.js";
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  isPlaceholderAppId2,
4
4
  CapacitorApp2
5
- } from "./index-wt7qrtd4.js";
6
- import"./index-2tv3fdx4.js";
5
+ } from "./index-7f4dsf9v.js";
6
+ import"./index-5nyznra1.js";
7
7
  import"./index-6wwzs1bc.js";
8
8
  import"./index-q0y2jq9j.js";
9
9
  export {
@@ -2,29 +2,30 @@
2
2
  import"./index-q0y2jq9j.js";
3
3
  import {
4
4
  GlobalConfig
5
- } from "./index-kn1h7p1x.js";
5
+ } from "./index-bw2marpf.js";
6
6
  import {
7
7
  Workspace,
8
8
  command
9
- } from "./index-mak3cd28.js";
10
- import"./index-2tv3fdx4.js";
9
+ } from "./index-b3ae16mg.js";
10
+ import"./index-5nyznra1.js";
11
11
  import"./index-6wwzs1bc.js";
12
- import"./index-v0qh106m.js";
13
- import"./index-259dsnz3.js";
12
+ import"./index-yc1keaer.js";
13
+ import"./index-r8bd61d9.js";
14
+ import"./index-1nwxbr2y.js";
14
15
  import"./index-hwkygn9s.js";
15
16
  import"./index-kcxmv48c.js";
16
- import"./index-ws5e0j51.js";
17
- import"./index-2hq52jq3.js";
17
+ import"./index-t8gvwcrx.js";
18
+ import"./index-8tfmh0v6.js";
18
19
  import {
19
- CloudScript
20
- } from "./index-zry5nz81.js";
21
- import"./index-j9yhg404.js";
20
+ CloudScript2
21
+ } from "./index-ay8wj00x.js";
22
+ import"./index-ys7dvdb3.js";
22
23
 
23
24
  // pkgs/@akanjs/cli/cloud/cloud.command.ts
24
25
  var localRegistryUrl = () => process.env.AKAN_NPM_REGISTRY ?? "http://127.0.0.1:4873";
25
26
  var resolveRegistryUrl = (registry) => registry === "local" ? localRegistryUrl() : undefined;
26
27
 
27
- class CloudCommand extends command("cloud", [CloudScript], ({ public: target }) => ({
28
+ class CloudCommand extends command("cloud", [CloudScript2], ({ public: target }) => ({
28
29
  login: target({ desc: "Login to Akan Cloud services" }).option("host", String, { desc: "host of the cloud", default: GlobalConfig.akanCloudHost }).with(Workspace).exec(async function(host, workspace) {
29
30
  await this.cloudScript.login(workspace, host);
30
31
  }),
@@ -0,0 +1,20 @@
1
+ // @bun
2
+ import {
3
+ CloudScript2
4
+ } from "./index-ay8wj00x.js";
5
+ import"./index-8tfmh0v6.js";
6
+ import"./index-r8bd61d9.js";
7
+ import"./index-ys7dvdb3.js";
8
+ import"./index-1nwxbr2y.js";
9
+ import"./index-t8gvwcrx.js";
10
+ import"./index-yc1keaer.js";
11
+ import"./index-bw2marpf.js";
12
+ import"./index-b3ae16mg.js";
13
+ import"./index-hwkygn9s.js";
14
+ import"./index-5nyznra1.js";
15
+ import"./index-6wwzs1bc.js";
16
+ import"./index-kcxmv48c.js";
17
+ import"./index-q0y2jq9j.js";
18
+ export {
19
+ CloudScript2 as CloudScript
20
+ };
@@ -1 +1 @@
1
- {"byCommand":{"create-workspace":"workspace","generate-agent-rules":"workspace","lint":"workspace","lint-all":"workspace","sync-all":"workspace","init":"workspace","agent":"agent","create-application":"application","remove-application":"application","plan-slice":"application","sync":"application","compact":"application","script":"application","console":"application","logs":"application","build":"application","b":"application","typecheck":"application","t":"application","test":"application","build-ios":"application","bi":"application","build-android":"application","ba":"application","start":"application","s":"application","start-ios":"application","si":"application","start-android":"application","sa":"application","release-ios":"application","release-android":"application","release-source":"application","codepush":"application","dbup":"application","dbdown":"application","configure-app":"application","create-library":"library","remove-library":"library","sync-library":"library","library-status":"library","install-library":"library","start-registry":"localRegistry","reset-registry":"localRegistry","smoke-registry":"localRegistry","version":"package","create-package":"package","remove-package":"package","sync-package":"package","build-package":"package","verify-dist-package":"package","verify-akan-publish-packages":"package","create-module":"module","remove-module":"module","create-service":"module","create-view":"module","create-unit":"module","create-template":"module","create-crud-page":"page","context":"context","doctor":"context","mcp-install":"context","mcp":"context","mcp-call":"context","login":"cloud","logout":"cloud","set-llm":"cloud","reset-llm":"cloud","ask":"cloud","deploy-akan":"cloud","update":"cloud","download-env":"cloud","upload-env":"cloud","subspace":"subspace","guideline":"guideline","generate-instruction":"guideline","update-instruction":"guideline","generate-document":"guideline","reapply-instruction":"guideline","create-scalar":"scalar","remove-scalar":"scalar","create-ui":"primitive","add-field":"primitive","add-enum-field":"primitive","quality":"quality","repair":"repair","workflow":"workflow"}}
1
+ {"byCommand":{"create-workspace":"workspace","generate-agent-rules":"workspace","lint":"workspace","lint-all":"workspace","sync-all":"workspace","init":"workspace","agent":"agent","create-application":"application","remove-application":"application","plan-slice":"application","sync":"application","compact":"application","script":"application","console":"application","logs":"application","build":"application","b":"application","typecheck":"application","t":"application","test":"application","build-ios":"application","bi":"application","build-android":"application","ba":"application","start":"application","s":"application","start-ios":"application","si":"application","start-android":"application","sa":"application","release-ios":"application","release-android":"application","release-source":"application","codepush":"application","dbup":"application","dbdown":"application","configure-app":"application","create-library":"library","remove-library":"library","sync-library":"library","library-status":"library","install-library":"library","start-registry":"localRegistry","reset-registry":"localRegistry","smoke-registry":"localRegistry","version":"package","create-package":"package","remove-package":"package","sync-package":"package","build-package":"package","verify-dist-package":"package","verify-akan-publish-packages":"package","create-module":"module","remove-module":"module","create-service":"module","create-view":"module","create-unit":"module","create-template":"module","create-crud-page":"page","context":"context","doctor":"context","mcp-install":"context","mcp":"context","mcp-call":"context","login":"cloud","logout":"cloud","set-llm":"cloud","reset-llm":"cloud","ask":"cloud","deploy-akan":"cloud","update":"cloud","download-env":"cloud","upload-env":"cloud","subspace":"subspace","guideline":"guideline","generate-instruction":"guideline","update-instruction":"guideline","generate-document":"guideline","reapply-instruction":"guideline","create-scalar":"scalar","remove-scalar":"scalar","create-ui":"primitive","add-field":"primitive","add-enum-field":"primitive","quality":"quality","repair":"repair","workflow":"workflow","tunnel":"tunnel"}}
@@ -2,25 +2,26 @@
2
2
  import {
3
3
  Workspace,
4
4
  command
5
- } from "./index-mak3cd28.js";
5
+ } from "./index-b3ae16mg.js";
6
6
  import"./index-q0y2jq9j.js";
7
- import"./index-2tv3fdx4.js";
7
+ import"./index-5nyznra1.js";
8
8
  import"./index-6wwzs1bc.js";
9
9
  import"./index-by5gpp8x.js";
10
- import"./index-gyd220p2.js";
11
- import"./index-ktb5njgy.js";
10
+ import"./index-jwjaf9fy.js";
11
+ import"./index-15r9a10e.js";
12
12
  import {
13
13
  ContextScript
14
- } from "./index-600zhnve.js";
15
- import"./index-wt7qrtd4.js";
14
+ } from "./index-3pvkjza8.js";
15
+ import"./index-7f4dsf9v.js";
16
16
  import"./index-h7yw2z9n.js";
17
- import"./index-fz5q3gd7.js";
18
- import"./index-7737gxcp.js";
19
- import"./index-kn1h7p1x.js";
20
- import"./index-8pm30m73.js";
21
- import"./index-pnqbkf5p.js";
22
- import"./index-4edzf1gr.js";
23
- import"./index-rwg2aa4f.js";
17
+ import"./index-em4swemt.js";
18
+ import"./index-8pkvrcv1.js";
19
+ import"./index-bw2marpf.js";
20
+ import"./index-yc1keaer.js";
21
+ import"./index-fr2x3qvv.js";
22
+ import"./index-sh88vtye.js";
23
+ import"./index-kbsach1y.js";
24
+ import"./index-ceph7zqj.js";
24
25
 
25
26
  // pkgs/@akanjs/cli/context/context.command.ts
26
27
  class ContextCommand extends command("context", [ContextScript], ({ public: target }) => ({
@@ -4,34 +4,14 @@ import {
4
4
  plainTextOf,
5
5
  HOST_SOURCE,
6
6
  DevLogBuffer
7
- } from "./index-v0qh106m.js";
7
+ } from "./index-r8bd61d9.js";
8
+ import {
9
+ writeClipboard
10
+ } from "./index-p6pbz9n5.js";
8
11
 
9
12
  // pkgs/@akanjs/cli/application/devTui.tsx
10
13
  import { render } from "ink";
11
14
 
12
- // pkgs/@akanjs/cli/clipboard.ts
13
- var clipboardCommands = () => {
14
- if (process.platform === "darwin")
15
- return [["pbcopy"]];
16
- if (process.platform === "win32")
17
- return [["clip"]];
18
- return [["wl-copy"], ["xclip", "-selection", "clipboard"], ["xsel", "--clipboard", "--input"]];
19
- };
20
- var writeClipboard = async (text) => {
21
- for (const command of clipboardCommands()) {
22
- try {
23
- const proc = Bun.spawn(command, {
24
- stdin: new TextEncoder().encode(text),
25
- stdout: "ignore",
26
- stderr: "ignore"
27
- });
28
- if (await proc.exited === 0)
29
- return true;
30
- } catch {}
31
- }
32
- return false;
33
- };
34
-
35
15
  // pkgs/@akanjs/cli/ui/DevTuiApp.tsx
36
16
  import { Box, Text, useInput } from "ink";
37
17
  import { useEffect, useState } from "react";
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  // pkgs/@akanjs/cli/application/devTuiView.ts
3
3
  var createDevTuiView = async (supervisor) => {
4
- const { DevTui } = await import("./devTui-40ggmd99.js");
4
+ const { DevTui } = await import("./devTui-hmteztv3.js");
5
5
  return new DevTui(supervisor);
6
6
  };
7
7
  export {
@@ -4,20 +4,21 @@ import {
4
4
  runner,
5
5
  script,
6
6
  command
7
- } from "./index-mak3cd28.js";
7
+ } from "./index-b3ae16mg.js";
8
8
  import {
9
9
  FileSys
10
10
  } from "./index-q0y2jq9j.js";
11
- import"./index-2tv3fdx4.js";
11
+ import"./index-5nyznra1.js";
12
12
  import {
13
13
  getDirname2
14
14
  } from "./index-6wwzs1bc.js";
15
15
  import {
16
16
  Prompter
17
17
  } from "./index-h7yw2z9n.js";
18
+ import"./index-bw2marpf.js";
18
19
  import {
19
20
  AiSession
20
- } from "./index-kn1h7p1x.js";
21
+ } from "./index-yc1keaer.js";
21
22
 
22
23
  // pkgs/@akanjs/cli/guideline/guideline.script.ts
23
24
  import { Logger } from "akanjs/common";
@@ -45,10 +45,16 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
45
45
  clear and close. `chrome={false}` drops the header bar whole, for an `inline` chat inside a panel the app
46
46
  already titles, and `defaultDraft` opens the composer with text in it without sending it. A panel driven by a
47
47
  controlled `open` with **no** `onOpenChange` draws no close button at all rather than an inert one. Then the
48
- slots: `AgentLauncher`, `AgentBubble`, `AgentComposer`, `AgentApproval`,
48
+ slots: `AgentLauncher`, `AgentSteps`, `AgentBubble`, `AgentComposer`, `AgentApproval`,
49
49
  `AgentQuestion`, `AgentQueued`, `AgentMenu`, `AgentMarkdown` and `AgentCode` each replace one part in `_overrides.tsx`, and
50
50
  `akanjs/ui` exports every default beside them (`DefaultBubble`, `DefaultComposer`, …) so a skin composes the
51
- one it is replacing. `AgentCode` is where a highlighter binds — the fence's language reaches it — and a
51
+ one it is replacing. **`AgentSteps` is one agent turn** — the messages between the user message that opened it
52
+ and the next one, with `isRunning` saying whether more are still coming — which is the grain a chat needs to
53
+ fold a turn's steps into a `details` and stand its answer outside them. It is the one boundary a per-message
54
+ slot cannot see, since neither message on either side of it knows it is at an edge. The default draws the turn's
55
+ messages flat into a Fragment, so a chat that binds nothing renders exactly what it did before the slot existed. The attachment row is exported on its own as `AgentAttachments`, because a replaced
56
+ `AgentBubble` that redraws it by hand forks the rule for which carrier is shown how — and then keeps whatever
57
+ that rule was on the day it was copied. `AgentCode` is where a highlighter binds — the fence's language reaches it — and a
52
58
  replacement for `AgentBubble` carries its own `memo`, since the transcript re-renders on every delta. Only then
53
59
  `AgentChat`, which replaces the panel whole: it also replaces the slash commands, the compaction notice and the
54
60
  approval gate, so reach for it when the *layout* differs, not when the look does.
@@ -110,8 +116,9 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
110
116
  arrives as an image. `accepts.document` is one boolean over every non-image type, so an adaptor whose API has a
111
117
  block for only some of them — Anthropic's `document` is PDF and nothing else — names the rest in the text rather
112
118
  than dropping them, which is the wire's own rule applied at the one layer that knows which blocks exist. Persisting keeps each
113
- attachment's name and drops its content: web storage is a few megabytes, one screenshot fills a chunk of it, and
114
- a save that fails is silent — so keeping the bytes would quietly stop keeping the transcript.
119
+ attachment's name, type, address and `ref` and drops only its content: web storage is a few megabytes, one
120
+ screenshot fills a chunk of it, and a save that fails is silent — so keeping the bytes would quietly stop keeping
121
+ the transcript, while an id is the handle a restored conversation has left to find the file with.
115
122
  **The ceilings are the message's, not the file's**: 4 MB per file, 8 MB and five files per message, and the same
116
123
  file twice is refused by name. The bytes ride inside one turn's JSON, so what a provider refuses is the sum —
117
124
  and a request that cannot be sent is one the user has to empty the composer to escape, which is why the refusal
@@ -121,6 +128,26 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
121
128
  reader produced**, not on the file that was picked: a reader that uploads and answers a `url` has already paid
122
129
  what the ceiling exists for, so refusing its 6 MB photo would refuse it for a cost it does not incur. Only the
123
130
  built-in path checks the source size, and there the file itself is the carrier.
131
+ - **An app may also build attachments itself and hand them to `session.send([{ role: "user", text, attachments }])`**,
132
+ which is why `MessageAttachment` is exported from `akanjs/ui` — the composer's path is typed by the `AttachReader`
133
+ callback, that one is not, and an object assembled with no nameable type is the one that gets a field wrong.
134
+ Nothing downstream trusts the result: an attachment whose `mimeType` is not a string is noted like any other
135
+ unreadable one, and its chip renders its name with no preview, because the host wrote the object and the declared
136
+ type is a claim rather than a check. A field that arrives wrong costs that attachment, never the turn.
137
+ - **A `url` carrier is an address the *provider* fetches, and bytes beside it win.** A reader that uploads answers
138
+ `{ url }` when its storage is publicly reachable, and `{ data }` — or both — when it is not: an attachment
139
+ carrying both is read from its bytes and displayed from its address, so the chip draws a thumbnail and the model
140
+ still gets the picture. That precedence exists because the other order fails the one way nothing reports: the
141
+ default storage backend serves a path only the app can resolve, the adaptor would hand it over as written, and
142
+ the model answers about a picture it never saw. Send a provider-reachable URL **alone** — paired with bytes it
143
+ costs the provider hop whatever the bytes weigh, for a fetch the provider would have done itself.
144
+ - **An image type the provider has no block for is refused at the adaptor, not upstream.** `accepts.image` is one
145
+ boolean, so `AgentService.readable` passes every `image/*` through, and the built-in composer reader base64s
146
+ every `image/*` as well — an `AttachReader` answering `null` means "not mine" and falls through to it, so an app
147
+ cannot gate one either. Both shipped vision adaptors therefore match an exact set (`jpeg`, `png`, `gif`, `webp`)
148
+ and name the rest in the text. It matters because an unsupported type is not one unread attachment: it is a
149
+ block the API refuses, so the **whole turn** dies on a vendor 400 — and `image/heic`, the iPhone camera default,
150
+ is the likeliest one an app meets.
124
151
  - **`MessageAttachment.ref` is the host's own handle on the file — carried, never read.** A file id, a storage key,
125
152
  whatever turns the attachment back into something a tool can be handed. The framework only moves it: the model is
126
153
  never shown one, and a tool the app publishes (`useFileFieldTool` below) is what spends it. Without it a host that
@@ -141,6 +168,43 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
141
168
  WebView, **which has neither on Android or iOS**, so a `speech.plugin.ts` declares the permission and
142
169
  the packages the native build needs. An engine answering `available()` false renders no microphone at all,
143
170
  the same rule as publishing no tool for a control the screen does not draw.
171
+ - **Data the user points at rides the message too, as `references` — and its value is a snapshot.** A file is
172
+ something they handed over; a reference is something they pointed at while asking, so it belongs in the same
173
+ place and for the same reason. Screen context does not do this job: `ContextBlock`s are rebuilt from the screen
174
+ every turn, so what was named three turns ago is simply not in the conversation any more. A `MessageReference`
175
+ carries the host's own `refName`, the id, a label, an optional dotted `path` into the document, and the `value`
176
+ those resolved to. **The value is never re-read on a later turn**, which is the point: re-reading rewrites what
177
+ the person was looking at when they spoke, and the usual next thing the agent does is edit the very field it was
178
+ pointed at — leaving a live reference showing the result with no record of what it was changed from.
179
+ `refName`/`refId`/`path` travel so a tool can read the current value when the answer needs it, and
180
+ `AgentService.referenced` folds the whole lot into the message text in one place, so every provider carries a
181
+ reference without either adaptor knowing the word. **The value is masked on the way in and nowhere else**: masking
182
+ needs the model class, which no wire carries, so the model a host names when it stages the reference is the whole
183
+ of the decision about what leaves the browser — name the class that actually holds the field, because a `Light`
184
+ one usually does not. **The ceiling is 20,000 characters per reference**, applied as it is staged and again on
185
+ the server; a reference is bulkier than a tool result and outlives one, riding every later turn and being the last
186
+ thing compaction folds. Persisting keeps the pointer and drops the value with a note saying so, which is a better
187
+ restored state than an attachment can offer — the pointer is still live, so the answer is to read it again.
188
+ Staging lives on the **session** (`stage` / `staged` / `unstage(key)` / `clearStaged()`), not on the composer where
189
+ staged files live, because a reference comes from whichever component drew the data rather than from the paperclip;
190
+ `unstage` is keyed on `refName/refId#path` because what orders a message's references is its text, not that list.
191
+ - **Two ways to point at data, and which one depends on who knows the value.** `<Agent.Chat reference={[…]} />`
192
+ takes one `ReferenceSource` per kind of document — `refName`, a `type`, the app's own `search`, and a `resolve` —
193
+ and the composer's `@` menu offers whole documents from them; the token is written the moment a row is picked and
194
+ the value is staged when `resolve` lands, so a slow fetch never freezes the menu and one that fails leaves a
195
+ pointer that reads as *go and read it*. For a field *inside* a document, the component drawing it calls
196
+ `useAgentReference()` — it already holds the value, so there is no round trip, and it is the only thing that
197
+ knows a rich-text field stored as `field(Any)` reads as a paragraph rather than as the editor document it is. The
198
+ hook no-ops with a warning outside a session, the same call `AgentValue.publishable` makes: a card carrying a
199
+ reference button must not cost a route its render.
200
+ - **The token in the draft is what carries a reference, and the chip only draws it.** Deleting
201
+ `@[label](mention:refName/id#path)` by hand drops the reference exactly as removing the chip does — removing the
202
+ chip removes the token — so the two can never disagree, and nothing re-derives a value from text somebody edited.
203
+ A token pasted out of an earlier message travels as a pointer with a note, which is the same shape a restored
204
+ conversation produces. Staged references are the **session's**, not the composer's, because `useAgentReference`
205
+ is called from components the composer cannot see; writing the token is the composer's half, and `refer` warns
206
+ rather than staging silently when no chat is mounted on that session — the case where a card sits outside the
207
+ `<Agent.Zone>` its chat is inside.
144
208
  - **`attach` and `voice` both carry functions, so a server layout cannot pass either.** A closure does not cross
145
209
  the RSC boundary — `non-scalar-props-restricted` says so on `page/**` — so an app that wants either mounts the
146
210
  chat from a small client component in the app's own `ui/` that calls the hook.
@@ -611,3 +675,44 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
611
675
  error result rather than a silent empty answer, and the settled exchange renders as question-and-answer instead
612
676
  of a tool row. **Never re-implement it per screen** — a `st.tool("askAboutX")` that opens a modal is the same
613
677
  thing with a worse transcript — and a hook tool named `askUser` shadows it like any other built-in.
678
+
679
+ ## What The Page Draws
680
+ - **The page shows the agent's work, not just the transcript.** A chat panel is closed as often as it is open, and
681
+ a form that fills itself or a tab that switches on its own is a change the user watches happen with nothing
682
+ anywhere attributing it. So **the control a call was published from is ringed** where it stands, scrolled to
683
+ first when it is off screen, and **a pointer travels to it and presses it** — both on by default, and both
684
+ driven from the tool call itself rather than from a store action, which cannot tell the agent's write from the
685
+ user's.
686
+ - **It costs an app nothing, for the same reason `data-akan-action` does.** `onChange={st.do.setTitleOnTask}` by
687
+ reference is what annotates the control, and the annotation is what makes it findable — so the fields and verbs
688
+ that matter light up with no code. An inline arrow publishes no tool, carries no annotation, and therefore
689
+ draws nothing: one wrapper silently costs three things at once.
690
+ - **For a control the framework did not render, spread `agentAttrs`** from `akanjs/ui`:
691
+ `<button {...agentAttrs(run)} onClick={run}>`, where `run` is what `st.tool(…).exec(…)` returned. It reads the
692
+ name off the callable, so there is nothing to keep in sync and no `ref` to thread — and it earns the same E2E
693
+ selector and accessibility name an `akanjs/ui` control has. Never hand-write `data-akan-*`.
694
+ - **Nothing is ever waited on.** The call starts the moment the effect is handed its event; a ring that lands a
695
+ frame later costs the turn nothing, and an animation that held a call would make the agent slower for a
696
+ decoration. A batch past four calls stops scrolling and keeps ringing — eight scrolls across a page is motion
697
+ sickness rather than attribution.
698
+ - **What it refuses to draw is the point.** A name several rows answer to rings nothing rather than guessing a row,
699
+ a call an approval or a guard turned back is never drawn at all, a tool no control carries draws nothing, and a
700
+ backgrounded tab draws nothing. A ring on the wrong element is worse than no ring: it is the screen telling the
701
+ user something untrue about what just happened. **`navigate` is in that last class** — the router is not an
702
+ element, so it draws nothing. A bar across the top of the page was tried for it and removed: it read as chrome
703
+ the page had grown rather than as the agent doing something, which is the opposite of attribution.
704
+ - **Turn it off with `visual`** on `Agent.Chat` / `Agent.Zone` — `false` for all of it, `{ cursor: false }` or
705
+ `{ reveal: false }` for one effect. The pointer is chrome (`data-agent-ui`, `aria-hidden`,
706
+ `pointer-events: none`), so `readScreen` never reads it back and `highlight` can never aim at it.
707
+ - **`ToolActivity` is the channel underneath**, a `ToolRunner` host option and an `AgentSession.onActivity`, firing
708
+ `start` / `end` around the execution alone. It is deliberately not `progress`: that one fires only for a tool
709
+ that chose to report, and a tool that says nothing about itself is exactly the one whose effect arrives
710
+ unexplained.
711
+ - **The pointer presses on arrival, never on departure** — a press fired as it leaves is a click on whatever it
712
+ was still over. It glides only when the hop is far enough to be worth following (a third of a second to cross
713
+ fifty pixels reads as lag, not motion), teleports otherwise, and fades once the batch is over. The ring answers
714
+ *where* and the pointer answers *who*, which is why they are one default rather than two.
715
+ - **A form patch fans out over the fields it named.** `fill<Model>Form` is published by the form rather than by any
716
+ one control, so it rings one control per field in the arguments — resolved through the same
717
+ `data-akan-state="<model>Form.<field>"` the setter annotates — capped at five. `writeOn<Model>(path, value)`
718
+ draws nothing, for the same reason it carries no annotation: there is no control to put one on.
@@ -464,6 +464,24 @@ Full contract — credential handshake, room revalidation, socket cleanup scopin
464
464
  - **A cleanup registered with `ws.on("disconnect" | "unsubscribe", fn)` is scoped to the call that registered it**
465
465
  — a room for a `pubsub` subscribe, the socket for a `message` handler. Cleanup that must happen either way
466
466
  registers for both.
467
+ - **A call's deadline is the endpoint's `{ timeout: <ms> }`, and there is nowhere else to put one.** It bounds
468
+ both ends: the `Timeout` middleware — registered by default, and standing aside for every endpoint that
469
+ declares none — rejects the call with `base.error.gatewayTimeout`, and the same value is serialized to the
470
+ client as that call's request budget. Declared nowhere, a call takes the client's own default of **30
471
+ seconds**, so an endpoint whose real work runs longer (provisioning, a firmware flow, an external
472
+ orchestration) has to declare one or it dies mid-flight with a transport error that does not say whether the
473
+ server ever received it. A caller overrides it per call with `fetch.x(…, { timeout })` — `false` waits as long
474
+ as the runtime will — and an app moves its own default with `fetch.instance.setTimeout(ms)`. An upload is
475
+ never bounded. **Losing the race does not cancel the work**: the handler runs to completion with nobody
476
+ holding its result, so a deadline is an answer to the caller, not an undo.
477
+ - **A cached answer is the endpoint's `{ cache: <ms> }`, and only a `query` taking no internal argument may have
478
+ one.** The `Cache` middleware is registered by default and stands aside for every endpoint that declares
479
+ nothing. Internal arguments are how a call learns who is asking (`.with(Self)`), so an endpoint that has them
480
+ answers per caller and one shared entry would be one caller's answer handed to the next — that endpoint and
481
+ every `mutation` are named in the log and left uncached. Guards run on every hit; what is cached is the
482
+ handler's result, so `resolveReturn` still masks fields and resolves relations per call; a cache backend that
483
+ is down is warned about and the call runs uncached. The default chain is `Logging → Timeout → Cache → handler`,
484
+ and there is no `Retry` middleware.
467
485
 
468
486
  ### Authorization Defaults
469
487
 
@@ -727,6 +745,7 @@ what every developer must know even when not building one.
727
745
  - Put display and predicate logic on the `Light<Model>` class rather than in a util module — see Module File Playbook.
728
746
  - Defaults are a literal for scalars and a thunk for anything constructed. Arrays are `field([T])`; optional is the postfix `.optional()`.
729
747
  - **`field.visual(T)` is a field the page renders and an agent never sees** — a blur placeholder, a rendered HTML body, a serialized geometry. It stays an ordinary stored `property` (persistence, search, forms and the page response untouched) and is stripped wherever a value is masked for an AI caller: every in-page-agent read and every MCP result, along with the MCP readable schema. Unlike `hidden`/`secret` it is cost, not secrecy — nothing is refused over one. Reach for it whenever a field is bulky and useless to a model; that is cheaper than every tool learning to avoid it.
748
+ - **A `field.hidden` / `field.secret` value reads `null` on the client, and the type still declares it.** `SignalContext.resolveReturn` skips both field types while it builds an endpoint's response, and hydration writes `null` over the key rather than leaving it absent — the first branch of the loop in `constant/getDefault.ts`, ahead of the field's own default. So the value is a deliberate `null` behind a type promising a `string`, every use of it typechecks, and the failure lands wherever it is finally dereferenced instead of where it was read. **Guard with `??` or `== null`**: `=== undefined`, a destructuring default and an optional parameter default all catch only a missing key and sail straight past this one. A projection (`pickById(id, { secret: true })`) widens the *server's* read, never the response. If a screen needs the value, the field is neither hidden nor secret; if it only needs to be cheap for a model rather than unseen, that is `field.visual`.
730
749
 
731
750
  ### Scalar & Field Type Reference
732
751
 
@@ -9,6 +9,7 @@ Use this whenever generating constants or scalar constants. It defines the curre
9
9
  - `field(Type).optional()` marks a nullable or optional business value when absence is valid.
10
10
  - `field.hidden(Type).optional()` is for internal state that should not be treated as a normal public field.
11
11
  - `field.secret(Type).optional()` is for sensitive values that should not be selected by default.
12
+ - Neither survives an endpoint response: both are stripped on the way out and hydration writes `null` over the key, while the generated type still declares the field — so a client holds a deliberate `null` behind a type that promises a value, and only `??` / `== null` catch it. A field a browser has to read is neither.
12
13
  - `field([Type])` defines arrays; provide empty array defaults when the app expects list operations.
13
14
 
14
15
  ## Current Akan Patterns
@@ -28,6 +28,55 @@ mutation is mounted under. `conventions` carries the invariants — this is the
28
28
  neither again, and cleanup that must happen either way registers for both. From a `message` handler it belongs to
29
29
  the socket and runs at close. A handler that throws is logged and never blocks the rest of the teardown.
30
30
 
31
+ ## Request Deadlines
32
+
33
+ - **`{ timeout: <ms> }` in an endpoint's signal option is the whole configuration surface**, and it reaches both
34
+ ends of the call. Server side, the `Timeout` middleware races the handler against it and answers
35
+ `base.error.gatewayTimeout` (504) once it is spent. Client side, `FetchSerializer` ships the value on the
36
+ serialized endpoint and `fetch.<endpoint>()` uses it as that request's `AbortSignal.timeout` budget, so the
37
+ browser does not abandon a call the server is still allowed to be working on.
38
+ - **The middleware is registered by default and has no default of its own.** An endpoint that declares no
39
+ `timeout` is passed straight through — a default here would put a deadline on every endpoint in the app that
40
+ nobody asked for, which is why the middleware used to be opt-in and, being opt-in, made the declared value
41
+ dead. It clears its timer in a `finally`: the race's loser is a pending timer per call otherwise, and with a
42
+ five-minute budget that is a five-minute hold on the event loop for a call that answered in a millisecond.
43
+ - **Without a declaration the client's own default applies — 30 seconds** (`DEFAULT_TIMEOUT_MS`,
44
+ `fetch/client/httpClient.ts`), and the server imposes nothing. Nothing else bounds a request: `Bun.serve` runs
45
+ with `idleTimeout: 0`, so a long call is only ever ended by one of these two.
46
+ - **Precedence is caller, then endpoint, then client default.** `fetch.x(…, { timeout: 5_000 })` overrides what
47
+ the endpoint declared; `{ timeout: false }` waits as long as the runtime will. An app moves its own default
48
+ with `fetch.instance.setTimeout(ms)` — `lib/useClient.ts` is generated, so the `timeout` option on
49
+ `FetchClient.build` belongs to the template that writes it, not to app code.
50
+ - **A file upload is never bounded**, on any of those paths: a large body on a slow uplink is a request that is
51
+ working, and the request body is the only thing the client can tell that from.
52
+ - **The deadline answers the caller; it does not undo the call.** `Promise.race` cannot cancel `next()`, so a
53
+ handler that timed out keeps running and keeps writing. Reach for a `timeout` to bound what a *caller* waits
54
+ for, not to bound an effect — an operation that must not half-happen needs its own idempotency or compensation.
55
+ - The failure is the same `base.error.gatewayTimeout` key whichever side gave up, so the user reads one sentence
56
+ in their own language either way. What differs is the status code — 504 from the server's middleware, 408 from
57
+ the client's own abort — and nothing downstream branches on that.
58
+
59
+ ## Cached Answers
60
+
61
+ - **`{ cache: <ms> }` in an endpoint's signal option is the whole cache surface.** The `Cache` middleware is
62
+ registered by default and stands aside unless an endpoint declares one — it used to be opt-in and to ignore the
63
+ declared value for a hard-coded 60 seconds, which is the same shape the `timeout` bug had.
64
+ - **Only a `query` that takes no internal argument may carry one.** The handler's inputs are its declared
65
+ arguments plus its internal ones, so an endpoint with none of the latter answers the same thing to everyone who
66
+ may read it — which is the only answer a shared entry can hold. An endpoint that takes `.with(Self)` answers
67
+ per caller, and one entry would be one caller's answer handed to the next; that endpoint and every `mutation`
68
+ are named once in the log and left uncached rather than silently ignored.
69
+ - **The guards run on every hit.** A cache hit skips `next()`, and `next()` is what runs them, so the middleware
70
+ calls `context.checkGuards()` itself before handing the entry over. Shared is not public.
71
+ - **The entry is the handler's result, not the response.** The middleware wraps execution, so `resolveReturn`
72
+ still runs per call on the cached value — field masking, `hidden`/`secret` stripping and relation resolution are
73
+ never cached, and a relation still costs its own load. The cache saves the handler, not the serialization.
74
+ - **A cache backend that is down does not take the endpoint down.** A failed read is warned and the call runs
75
+ uncached; a failed write is warned and dropped.
76
+ - The default middleware chain is `Logging → Timeout → Cache → handler`: a hung cache backend is bounded by the
77
+ same deadline as the handler it stands in for. **`Retry` was removed** — an automatic re-run of an endpoint
78
+ whose failure it cannot classify replays whatever the first attempt already did.
79
+
31
80
  ## Binary Pubsub
32
81
 
33
82
  - **`pubsub(Binary)` sends its payload in a websocket binary frame**, skipping the JSON `{ type: "pub" }`
@@ -3,7 +3,7 @@ import {
3
3
  CodegenLock,
4
4
  WorkspaceExecutor,
5
5
  AppExecutor
6
- } from "./index-2tv3fdx4.js";
6
+ } from "./index-5nyznra1.js";
7
7
  import"./index-q0y2jq9j.js";
8
8
  import"./index-6wwzs1bc.js";
9
9
  import {