ftown 0.25.2 → 0.25.3

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 (71) hide show
  1. package/dist/command-rpc.d.ts +3 -0
  2. package/dist/command-rpc.js +41 -0
  3. package/dist/command-rpc.js.map +1 -1
  4. package/dist/ftown-cli.js +5 -2
  5. package/dist/ftown-cli.js.map +1 -1
  6. package/dist/index.js +3 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/quota/agy.d.ts +16 -0
  9. package/dist/quota/agy.js +287 -0
  10. package/dist/quota/agy.js.map +1 -0
  11. package/dist/quota/annotate.d.ts +5 -0
  12. package/dist/quota/annotate.js +56 -0
  13. package/dist/quota/annotate.js.map +1 -0
  14. package/dist/quota/claude.d.ts +27 -0
  15. package/dist/quota/claude.js +265 -0
  16. package/dist/quota/claude.js.map +1 -0
  17. package/dist/quota/codex.d.ts +25 -0
  18. package/dist/quota/codex.js +378 -0
  19. package/dist/quota/codex.js.map +1 -0
  20. package/dist/quota/format.d.ts +6 -0
  21. package/dist/quota/format.js +113 -0
  22. package/dist/quota/format.js.map +1 -0
  23. package/dist/quota/grok.d.ts +97 -0
  24. package/dist/quota/grok.js +563 -0
  25. package/dist/quota/grok.js.map +1 -0
  26. package/dist/quota/models-agy.d.ts +10 -0
  27. package/dist/quota/models-agy.js +144 -0
  28. package/dist/quota/models-agy.js.map +1 -0
  29. package/dist/quota/models-claude.d.ts +10 -0
  30. package/dist/quota/models-claude.js +227 -0
  31. package/dist/quota/models-claude.js.map +1 -0
  32. package/dist/quota/models-codex.d.ts +18 -0
  33. package/dist/quota/models-codex.js +305 -0
  34. package/dist/quota/models-codex.js.map +1 -0
  35. package/dist/quota/models-grok.d.ts +19 -0
  36. package/dist/quota/models-grok.js +272 -0
  37. package/dist/quota/models-grok.js.map +1 -0
  38. package/dist/quota/models-muse.d.ts +13 -0
  39. package/dist/quota/models-muse.js +200 -0
  40. package/dist/quota/models-muse.js.map +1 -0
  41. package/dist/quota/models-opencode.d.ts +51 -0
  42. package/dist/quota/models-opencode.js +400 -0
  43. package/dist/quota/models-opencode.js.map +1 -0
  44. package/dist/quota/muse.d.ts +38 -0
  45. package/dist/quota/muse.js +225 -0
  46. package/dist/quota/muse.js.map +1 -0
  47. package/dist/quota/opencode.d.ts +7 -0
  48. package/dist/quota/opencode.js +47 -0
  49. package/dist/quota/opencode.js.map +1 -0
  50. package/dist/quota/routing-config.d.ts +18 -0
  51. package/dist/quota/routing-config.js +438 -0
  52. package/dist/quota/routing-config.js.map +1 -0
  53. package/dist/quota/routing-service.d.ts +24 -0
  54. package/dist/quota/routing-service.js +118 -0
  55. package/dist/quota/routing-service.js.map +1 -0
  56. package/dist/quota/routing.d.ts +10 -0
  57. package/dist/quota/routing.js +180 -0
  58. package/dist/quota/routing.js.map +1 -0
  59. package/dist/quota/types.d.ts +304 -0
  60. package/dist/quota/types.js +45 -0
  61. package/dist/quota/types.js.map +1 -0
  62. package/dist/quota-cli.d.ts +80 -0
  63. package/dist/quota-cli.js +462 -0
  64. package/dist/quota-cli.js.map +1 -0
  65. package/dist/security/bridge/permissions.js +4 -2
  66. package/dist/security/bridge/permissions.js.map +1 -1
  67. package/dist/types.d.ts +2 -2
  68. package/package.json +3 -2
  69. package/skills/ftown/SKILL.md +2 -1
  70. package/skills/ftown/references/orchestrator.md +5 -0
  71. package/skills/ftown/references/quota.md +177 -0
@@ -0,0 +1,177 @@
1
+ # Quota-aware routing
2
+
3
+ Read `../SKILL.md` for identity and reporting. This reference covers choosing the
4
+ CLI, model and effort for delegated work. The owner sets up tiers; you choose.
5
+ `ftown quota` reports data and never chooses for you.
6
+
7
+ ## When to use
8
+
9
+ - Before `ftown sessions create` for a child whose harness you are free to pick.
10
+ - When a worker stopped on a usage limit, before re-routing its task.
11
+ - Not needed when the user named the harness or model. Keep their choice.
12
+
13
+ ## What to run
14
+
15
+ ```bash
16
+ ftown quota --json # full report
17
+ ftown quota claude codex --json # only the named harnesses
18
+ ftown quota --json --no-models # skip model lists (picks become unchecked)
19
+ ftown quota --json --timeout 30 # per-harness limit, 1-600 s, default 15
20
+ ```
21
+
22
+ Harnesses: `claude`, `codex`, `grok`, `muse`, `agy`, `opencode`. If `ftown` is
23
+ not on PATH, use `~/.ftown/ftown quota`. `--models` only affects table mode.
24
+
25
+ A run takes several seconds. It launches hidden local CLIs to read their usage
26
+ and model lists. It makes no model turns. The numbers describe the owner's
27
+ subscription plans (5-hour and weekly windows), not API spend.
28
+
29
+ ## Decision procedure
30
+
31
+ The owner configures tiers in the dashboard ("Model routing..." on a bridge).
32
+ Each tier has a `name`, a `description` of the work it is for, an ordered list of
33
+ `picks` (CLI + model + effort), and free-text `guidance`. The report also has a
34
+ global free-text `steering`.
35
+
36
+ 1. Read `routingConfig.status`. If it is not `ok`, see "When things are missing".
37
+ 2. Match the task to a tier by the tier `description`s. Pick the closest one.
38
+ 3. Walk that tier's `picks` in order. Skip any pick whose `problem` is not null:
39
+ the model is gone or the effort is unsupported.
40
+ 4. Weigh each remaining pick: `availability`, `leftPercent` (null when `unknown`),
41
+ `resetsAt` (null when `unknown`), and `scopedLimits`. A scoped limit (for example
42
+ one model family) can be tighter than the plan-wide figure. Scopes usually name a
43
+ model family that appears in the model id or name, so matching a scope to a pick
44
+ is your judgment; when several match use the one with most used.
45
+ 5. `checked: false` means the harness's model list could not be read, so the pick
46
+ was not verified. It is usable with care: expect a possible launch failure.
47
+ 6. Apply the tier's `guidance` and the global `steering`. They are the owner's
48
+ instructions and outrank the pick order when they conflict. Example: if
49
+ steering says "don't fully exhaust Claude", skip a Claude pick that is `low`
50
+ even when it is first.
51
+ 7. Guidance is free text and may name a class, such as "otherwise any free
52
+ opencode model". Resolve it against `models`: filter that provider's
53
+ `models[].models[]` by `pricing` and choose one that fits the task. If that
54
+ provider's list is empty you cannot resolve the class, so fall back to `routing[]`
55
+ and say so. `pricing` is `free` (zero-priced), `included` (covered by a plan the
56
+ owner holds) or `paid` (billed per use). It is omitted when unknown.
57
+ 8. Launch the choice (next section). Report to the user which tier and pick you
58
+ used, and why if you departed from the pick order.
59
+
60
+ ## Availability values
61
+
62
+ Each pick carries its provider's `availability`. The same values appear in `routing[]`.
63
+
64
+ - `available`: enough left for normal work. Use it.
65
+ - `low`: `leftPercent` below 15 in the binding (tightest) window, or `runway`
66
+ below 0.5 when `runway` is not null. Use only for small tasks, when guidance
67
+ allows it, or when nothing `available` fits.
68
+ - `exhausted`: `leftPercent` 1 or less. Never use it before `resetsAt`.
69
+ - `unknown`: no usable numbers. It is not proof of empty or full quota. It ranks
70
+ below `low` and above `exhausted`; use it as a fallback after `available` and
71
+ `low`, and say so.
72
+
73
+ If a window resets within 10 minutes it is reported as `available` with `leftPercent`
74
+ 100 and `runway` null, even if `usedPercent` was high. `routing[]` is ordered best
75
+ first as available, then low, then unknown, then exhausted.
76
+
77
+ `leftPercent` is percent left in the binding (tightest) window. `windows[].usedPercent`
78
+ in `providers` is percent used. Do not mix them up.
79
+
80
+ ## Turning a pick into a launch
81
+
82
+ Find the provider's entry in `models`. It gives `modelFlag` (for example
83
+ `--model <id>`) and `effortFlag` (for example `--effort <level>`, or null when
84
+ the harness has no effort flag). Use the pick's `model` as the id, verbatim, and
85
+ its `effort` as the level. `effort: null` means omit the effort flag.
86
+
87
+ opencode takes effort as a `#variant` suffix on the model, for example
88
+ `provider/model#high`. Check its `effortFlag` and the model's `efforts` in the report.
89
+
90
+ ```bash
91
+ ftown sessions create --shell <provider> --model <id> --parent \
92
+ --workdir "$PWD" --name review-auth --prompt "..."
93
+ ```
94
+
95
+ Where `ftown sessions create` has no flag for the effort, apply `effortFlag` as
96
+ the harness documents it. Omit `--model` to take the harness default.
97
+
98
+ ## When things are missing
99
+
100
+ - `routingConfig.status` is `missing`: the owner has set no tiers. `tiers` is
101
+ empty. Walk `routing[]` in order (already best first: available, low, unknown,
102
+ exhausted): take the first `available`, else `low` only for small tasks, else
103
+ `unknown` with disclosure, and never use `exhausted` before its `resetsAt`.
104
+ Pick a model from `models` and tell the user no tiers are configured.
105
+ - `routingConfig.status` is `invalid`: `routingConfig.errors` lists the problems.
106
+ Treat it like `missing`, and tell the user the file at `routingConfig.path`
107
+ needs fixing. Still honor `steering` if it is non-empty.
108
+ - An empty tier (no `picks`): use its `guidance` if it has any; otherwise fall
109
+ back to `routing[]`.
110
+ - Every pick unusable (all have a `problem`, or are `exhausted`): follow the
111
+ guidance if it names an alternative. Otherwise try the next closest tier, then
112
+ `routing[]`. If nothing is usable, stop and ask the user rather than launching
113
+ on an exhausted plan.
114
+ - A provider with `status` other than `ok` (`no_data`, `auth`, `unavailable`,
115
+ `error`): read its `message`. Its routing is `unknown`. For `auth`, tell the
116
+ user what to fix. A `models` entry with status other than `ok` has an empty
117
+ `models` list, so its picks are `checked: false`.
118
+ - opencode may expose no plan usage at all, so when it reports no windows its
119
+ availability will be `unknown`. That is expected when it happens. Judge it by
120
+ `pricing` (`free` models are safe to use) and by the owner's guidance, not by
121
+ quota.
122
+
123
+ ## JSON field reference
124
+
125
+ | Field | Meaning |
126
+ |---|---|
127
+ | `generatedAt` | ISO time of the report |
128
+ | `providers[]` | raw readings: `provider`, `status`, `plan`, `windows[]`, `source`, `observedAt`, `message`, optional `scopedLimits[]` |
129
+ | `providers[].windows[]` | `kind` (`five_hour`/`weekly`), `usedPercent` (used, may be null), `resetsAt`, `windowMinutes`, `note` |
130
+ | `scopedLimits[]` | partial limit: `scope` (e.g. a model family), `kind`, `usedPercent`, `resetsAt` |
131
+ | `routing[]` | per provider, best first: `provider`, `availability`, `leftPercent`, `bindingWindow`, `resetsAt`, `resetsInMinutes`, `runway` (1 = on pace, >1 spare, <1 will run out early, null when length or reset unknown), `reason` |
132
+ | `models[]` | per provider: `provider`, `status` (`ok`/`unavailable`/`error`; empty `models` unless `ok`), `models[]`, `source` (`live`/`cached`), `observedAt`, `message`, `modelFlag`, `effortFlag` |
133
+ | `models[].models[]` | `id`, `name`, `isDefault`, `description`, `contextTokens`, `efforts[]`, `defaultEffort`, optional `pricing` |
134
+ | `routingConfig` | `status` (`ok`/`missing`/`invalid`), `path`, `errors[]` |
135
+ | `tiers[]` | `name`, `description`, `picks[]`, `guidance`. Empty unless status is `ok` |
136
+ | `tiers[].picks[]` | `provider`, `model`, `effort`, `modelName`, `availability`, `leftPercent`, `resetsAt`, `scopedLimits[]`, `problem`, `checked` |
137
+ | `steering` | global owner instructions, verbatim, `""` when none |
138
+
139
+ Trimmed example (invented values):
140
+
141
+ ```json
142
+ {
143
+ "routingConfig": { "status": "ok", "path": "/home/u/.ftown/model-routing.json", "errors": [] },
144
+ "steering": "Don't fully exhaust Claude.",
145
+ "tiers": [
146
+ {
147
+ "name": "complex",
148
+ "description": "Hard development and debugging",
149
+ "guidance": "Otherwise any free opencode model.",
150
+ "picks": [
151
+ { "provider": "claude", "model": "model-a", "effort": "high", "modelName": "Model A",
152
+ "availability": "low", "leftPercent": 9, "resetsAt": "2026-01-05T00:00:00Z",
153
+ "scopedLimits": [], "problem": null, "checked": true },
154
+ { "provider": "codex", "model": "model-b", "effort": "medium", "modelName": null,
155
+ "availability": "available", "leftPercent": 70, "resetsAt": "2026-01-06T00:00:00Z",
156
+ "scopedLimits": [], "problem": null, "checked": true }
157
+ ]
158
+ }
159
+ ],
160
+ "models": [
161
+ { "provider": "codex", "status": "ok", "modelFlag": "--model <id>", "effortFlag": "--effort <level>",
162
+ "models": [ { "id": "model-b", "efforts": ["low", "medium", "high"], "pricing": "included" } ] }
163
+ ]
164
+ }
165
+ ```
166
+
167
+ Here the first pick is `low` and steering says not to exhaust Claude, so choose
168
+ the second pick: `codex`, `model-b`, effort `medium`.
169
+
170
+ ## Limits
171
+
172
+ - Grok has only a weekly window. `FTOWN_QUOTA_GROK_NO_REFRESH=1` skips its hidden
173
+ refresh and uses the last logged value.
174
+ - opencode lists only its subscription providers by default. Override with
175
+ `FTOWN_QUOTA_OPENCODE_PROVIDERS=a,b`.
176
+ - The tool does not rank models by capability. The owner's tiers and your
177
+ judgment decide.