@miller-tech/uap 2.45.0 → 2.48.0

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.
@@ -0,0 +1,252 @@
1
+ # Droid client performance options — the local qwen3.8 UAP fleet
2
+
3
+ **Status:** Proposed (monitoring findings + a ranked option menu; nothing starts
4
+ without an explicit go, per the [proof-options house rule](model-performance-proof-options.md))
5
+ **Date:** 2026-11-14
6
+ **Monitored:** the live Droid qwen3.8 UAP clients (3 hooked repos, 4 droid
7
+ processes; the pay2u security-audit run live since 16:01) against
8
+ `~/.uap/telemetry/tool-calls.db` — 20,002 tool calls, 2026-10-11 → now.
9
+
10
+ ## What the monitoring shows
11
+
12
+ **The failure mass is one class.** 20.6% of all tool calls fail (4,118), and
13
+ **83% of the failures are the write/edit family**:
14
+
15
+ | Tool | Calls | Failed | Fail rate |
16
+ |---|--:|--:|--:|
17
+ | edit_file | 3,677 | 1,557 | 42.3% |
18
+ | edit_range | 588 | 233 | 39.6% |
19
+ | write_file | 4,690 | 1,597 | 34.1% |
20
+ | run_bash | 2,251 | 600 | 26.7% |
21
+ | list_dir | 1,475 | 81 | 5.5% |
22
+ | read_file | 7,321 | 50 | **0.7%** |
23
+
24
+ Reads are essentially perfect. The model writes blind. Decomposing the
25
+ write/edit failures:
26
+
27
+ - **stub-refusal (1,097)** — UAP's gutting guard catches the model shrinking a
28
+ real file into a stub (or writing N declared functions with empty bodies).
29
+ Each refusal is a saved file but a wasted round-trip; the error text already
30
+ says "use edit_file", and models re-send anyway.
31
+ - **no-op (758)** — writes/edits that produced no change (idempotent retries).
32
+ - **edit-miss (642)** — `old_string not found`: editing from memory instead of
33
+ reading the file first.
34
+ - **path-not-found (502)** — plausible-but-wrong guessed paths
35
+ (`apps/api/src/repositories/installments_repository.ts`-shaped).
36
+ - edit-ambiguous (180), truncated-write (22).
37
+ - run_bash's 600 = 447 deliver-sandbox refusals + 153 exit-1.
38
+
39
+ **Era trend (the arm is the biggest single factor):**
40
+
41
+ | Date | Era | Fail rate |
42
+ |---|---|---:|
43
+ | 11-02 | worst benchmark block | 46.5% |
44
+ | 11-11 | **v31 UD-IQ4_XS + Q8_0 engram live** | **8.7%** |
45
+ | 11-13 | v31/Q8_0 measured + swap round | 10.4% |
46
+ | 11-14 (today) | **abliterated-iq2_xs serving** (user-switched) | 17.3% |
47
+
48
+ The best measured live day is the v31 arm; today the client fleet is served by
49
+ **iq2_xs — the fleet's floor tier (2.86 bpw)**. That alone roughly doubles the
50
+ tool-call failure rate vs the v31 era.
51
+
52
+ **The frontier routes are dark.** The client's 25 frontier-named entries
53
+ (CC Opus 4.5/4.6, GPT-5.1/5.2, Gemini 3, AG variants) all point at
54
+ `localhost:8317` — **no listener is bound**. The only frontier-tagged outcomes
55
+ in the delivery corpus (opus-4.8) died at 0% of gates in ≤5 turns (early route
56
+ death, not model quality). So "Opus 5.5 / Fable 5.2 levels" currently has **no
57
+ valid measured baseline on this box** — the honest reading of the local gap is
58
+ the failure table above: frontier-class clients read before editing (~0
59
+ edit-miss), don't gut files (~0 stub-refusal), and don't guess paths (~0
60
+ path-not-found). Closing the write/edit class is closing the gap.
61
+
62
+ ## The options, ranked by cost
63
+
64
+ ### O-A — Serve the strongest arm for client sessions (free, today)
65
+
66
+ **Cost:** one panel switch · **Expected effect:** the 8.7%-era failure rate
67
+ instead of today's 17.3% (the arm is the largest single measured factor).
68
+
69
+ Droid client work runs on v31 (UD-IQ4_XS + the Q8_0 engram — the measured-best
70
+ long-prompt decode) until the **UD-Q3_K_XL arm** (staged 2026-11-14, registry
71
+ `strata-unsloth-ud-q3_k_xl`) is measured and A/B'd; iq2_xs stays for batch and
72
+ benchmark traffic where its throughput per GiB matters. The Q3_K_XL A/B is
73
+ already required before that arm can place (fail-closed `unmeasured`).
74
+
75
+ ### O-B — Restore (or drop) the frontier route, then measure the real gap (free)
76
+
77
+ **Cost:** operator time · **Effect:** turns "frontier levels" from a vibe into
78
+ a number.
79
+
80
+ Either bring the `:8317` CC proxy back up or delete the 25 dead entries so the
81
+ client menu stops lying. Then ONE paired run (the
82
+ [paired discipline](../benchmarks/PAIRED_FINDINGS.md): same tasks, same
83
+ seeds, UAP layer identical, only the model route toggled) — local fleet vs one
84
+ frontier route — gives the actual per-task gap and the failure-table delta.
85
+ Without this, every "get to Opus/Fable levels" claim is unmeasurable.
86
+
87
+ ### O-C — Kill the three big failure classes at the tool layer (days; the 83%)
88
+
89
+ **Cost:** small, contained proxy/tool-router changes · **Effect:** directly
90
+ targets 2,241 of the 2,687 tool failures (stub-refusal + no-op + edit-miss +
91
+ path-not-found + edit-ambiguous).
92
+
93
+ 1. **edit-miss → auto-context**: on `old_string not found`, the error returns
94
+ the target's actual content around the nearest match (or line numbers +
95
+ first lines). The model currently guesses again; give it the truth. This is
96
+ the single highest-yield fix (642 failures, and each one saved is also a
97
+ saved retry turn).
98
+ 2. **path-not-found → fuzzy-match hint**: return the nearest existing path(s)
99
+ in the error (the 502 guessed paths are near-misses of real files).
100
+ 3. **stub-refusal → forced-read gate**: after a gutting refusal, the next
101
+ write_file to that path requires a fresh read of the target in the same
102
+ turn context — make the correct path (edit_file) cheaper than re-sending.
103
+ The refusal text alone demonstrably does not stop the 1,097 re-sends.
104
+ 4. **no-op surfacing**: flag "this write produced zero change" at the tool
105
+ layer (758 idempotent retries today pass silently).
106
+
107
+ These are LOCKSTEP-class fixes (the v2.33 known-fix-table family): each one
108
+ needs a paired before/after on the same benchmark suite, reported with the
109
+ house discrimination status.
110
+
111
+ ### O-D — Proxy-side terse-prompt shim for the strata arms (days)
112
+
113
+ **Cost:** small proxy change · **Effect:** the Sharp-template gains
114
+ (terseness, 2.7× faster median upstream) approximated for the arms that
115
+ cannot take a serve-time template.
116
+
117
+ The strata engine has no serve-time template override (GGUF-embedded), so the
118
+ Sharp template round skipped all 4 strata arms. What the proxy CAN do is
119
+ inject the Sharp **system-prompt** text (the terseness directive) into the
120
+ wire when the client's system prompt is absent/overridable — the template's
121
+ mechanical wins (fewer filler tokens, earlier tool calls) without the
122
+ GGUF re-embed. Weaker than the real template; cheap to A/B.
123
+
124
+ ### O-E — Aggregate the delivery-gate corpus (free; proof-plan O1)
125
+
126
+ **Cost:** hours · **Effect:** the model × technique × tier gate table that
127
+ directs every option above.
128
+
129
+ The `[deliver]` lessons already record best-%-of-gates per model per task;
130
+ aggregate them into the descriptive table the
131
+ [proof plan](model-performance-proof-options.md) O1 specified. This is also
132
+ what tells us whether the qwen35-a3b tier (67-83% best-gates) is client-viable
133
+ or benchmark-only.
134
+
135
+ ## What NOT to do (the measured history says so)
136
+
137
+ - **More gates on the Droid path.** The paired data: against a self-verifying
138
+ harness the gate loop is a wash (Δ 0.0pp [−5.5, +6.9]) at token cost. The
139
+ frontier gap is not a gating problem; it is the failure table.
140
+ - **Chasing frontier models for the interactive client before O-B.** No valid
141
+ baseline exists; the two opus-tagged runs died at 0% in ≤5 turns.
142
+ - **Full-pin any strata arm** (the 10-28 OOM doctrine) — throughput gains die
143
+ with the box.
144
+
145
+ ## Sequencing
146
+
147
+ O-A today (panel switch, zero risk) → O-C.1 (edit-miss auto-context — the
148
+ single best fix) in the next proxy round → O-B when the frontier route is
149
+ restored → O-C.2/3/4 + O-E as the following PR → O-D last (weakest
150
+ expected value per hour).
151
+
152
+ ---
153
+
154
+ ## O-B MEASURED — the frontier baseline via Droid + GLM-5.3 (2026-11-14)
155
+
156
+ The frontier route landed differently than planned: not Claude Code through
157
+ the `:8317` CLIProxyAPI OAuth (its refresh token is expired; re-login is
158
+ user-interactive), but **Droid itself on `glm-5.3` (Max plan)** — the user
159
+ switched the session default to glm-5.3 and directed the baseline there. This
160
+ is the same client shape the monitored local baseline used (a Droid session
161
+ doing real tool work), which makes it the honest pair.
162
+
163
+ **Method (paired discipline): same tasks, same seeds, same driver, same
164
+ gates — only the model route toggled.**
165
+
166
+ - **Driver:** `droid exec --auto high -o json`, identical flags and 8-minute
167
+ per-task ceiling for both arms.
168
+ - **Frontier arm:** `-m glm-5.3` (Droid's own provider auth — no local key
169
+ material involved).
170
+ - **Local arm:** `-m qwen3.8-flash-next-unsloth-ud-iq4_xs-v31` through the
171
+ UAP proxy (`:4000`) → the fleet. During the run the fleet was serving
172
+ **abliterated-iq2_xs** (a peer's live deliver run owned the box, so no arm
173
+ switch was made) — the local arm is therefore labeled iq2_xs, the fleet's
174
+ floor tier, NOT v31.
175
+ - **Suite:** 6 fresh-dir tasks exercising the monitored failure classes —
176
+ create+verify (t1), precise-edit of a seeded bug (t2), path near-miss
177
+ `.yaml` vs `.yml` (t3), add-to-existing without gutting (t4), multi-file
178
+ rename (t5), ambiguous-match edit-one-of-two (t6). 16 deterministic gates
179
+ evaluated identically for both arms.
180
+ - **Anatomy:** from each run's droid session transcript (per-tool-call
181
+ results, same failure classes as the monitoring round, adapted to
182
+ Droid-native result text).
183
+
184
+ ### Results (2026-11-14, 19:34–20:22 AEDT)
185
+
186
+ **Gate outcomes — TIED 13/13.** On small, well-specified, gate-verifiable
187
+ tasks the floor-tier arm produces correct results too. The paired gap is
188
+ **not outcome quality on this suite — it is cost**:
189
+
190
+ | Arm | Gates | Exec completes | Tool calls | Turns (mean) | Wall time (total) |
191
+ |---|---:|---:|---:|---:|---:|
192
+ | **glm-5.3** (frontier, Max) | 13/13 | 6/6 | **59** | 7.3 | **4.7 min** |
193
+ | **abliterated-iq2_xs** (local fleet, floor tier, via shimmed UAP proxy) | 13/13 | 1/6 (5 hit the 480s ceiling after finishing the file work) | **1,394** | 45 on the one completed task | **47 min** |
194
+
195
+ The local arm needed **~24x the tool-call volume** and **~10x the wall
196
+ time** for identical gate outcomes, and it burned its ceiling on 5 of 6
197
+ tasks (the substantive file work was done when the ceiling killed the
198
+ session — the gates scored the artifacts, which is why the tie is real).
199
+ One completed local task (t6: a 3-line edit) took **45 turns**; the
200
+ frontier took 4 for the same task. The local arm also spawned 20 subagent
201
+ Task calls + 5 Skill calls where the frontier used none.
202
+
203
+ **Failure anatomy (per Droid-native tool results, both arms):**
204
+
205
+ | Tool | glm-5.3 (calls / fail / rate) | iq2_xs (calls / fail / rate) |
206
+ |---|---|---|
207
+ | Edit | 7 / **0** / 0.0% | 66 / **0** / 0.0% |
208
+ | Create (write) | 7 / **0** / 0.0% | 21 / **0** / 0.0% |
209
+ | Read | 19 / 2 / 10.5% | 270 / 5 / 1.9% |
210
+ | Execute (bash) | 14 / 2 / 14.3% | 866 / 16 / 1.8% |
211
+ | TaskOutput (bg polls) | — | 65 / 55 / 84.6% |
212
+ | Overall | 59 / 4 / **6.8%** | 1,394 / 76 / 5.5% (1.6% excl. killed-bg-polls) |
213
+
214
+ Classes: frontier = 2 bash-failures (both its own runs of the
215
+ resolver-broken t5 fixture — reproduced identically on the pristine
216
+ template, corrected in the gate), 1 path-not-found (the deliberate t3
217
+ `.yaml`→`.yml` near-miss — recovered within the task), 1 other. Local = 16
218
+ bash, 5 path-not-found, 55 killed-background-poll errors from the
219
+ ceiling kills.
220
+
221
+ ### What the baseline actually establishes
222
+
223
+ 1. **The frontier "levels" number on this stack, for this suite, is: same
224
+ gates, 1/24th the tool activity, 1/10th the wall time, zero edit/write
225
+ misses.** On well-scoped tasks the floor arm eventually converges too —
226
+ frontier value here is throughput and turn economy, not correctness.
227
+ 2. **The blind-write failure class did not fire on this suite** — neither
228
+ arm missed an edit or gutted a file (all 73 edit/create calls across
229
+ both arms succeeded). These are small tasks whose files fit in one
230
+ read; the monitored corpus's 42%-edit-miss anatomy lives in long,
231
+ context-heavy sessions where the model edits from memory. The suite
232
+ bounds the claim: frontier ≠ better edit *accuracy* here; it is faster
233
+ at the same accuracy. The 20.6% live-corpus failure anatomy remains
234
+ the measure of the long-session problem — O-C's fixes target that
235
+ instrument, not this one.
236
+ 3. **The instrument caveat is load-bearing**: these anatomy numbers are
237
+ Droid-native tool results; the monitoring corpus's 20.6% is the UAP
238
+ tool layer. Same failure classes, different instruments — rates are not
239
+ comparable across them, only within this paired table.
240
+
241
+ Raw artifacts: `benchmark-results/ob-paired-droid-exec-2026-11-14/`
242
+ (report.json in the corpus schema + the full run suite), regenerated by
243
+ the archived scripts in `artifacts/`. The O-E corpus aggregator picks the
244
+ report up as a paired row on its next run.
245
+
246
+ **Caveats (labeled, not hidden):** n=6 tasks per arm is directional, not
247
+ powered — treat per-task deltas as single observations and the aggregate as
248
+ descriptive. The local arm ran under concurrent fleet load (the live peer
249
+ deliver run), which inflates its latency but not its gate outcomes. The
250
+ frontier anatomy counts Droid-native tool results, not the UAP tool layer the
251
+ monitoring round measured — the classes are aligned but the instruments
252
+ differ; cross-comparison of rates between the two instruments is qualitative.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "2.45.0",
3
+ "version": "2.48.0",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -6752,6 +6752,38 @@ def _extract_images(content) -> list[dict]:
6752
6752
  return images
6753
6753
 
6754
6754
 
6755
+ # O-D — the terse-prompt shim for arms that cannot take a serve-time template
6756
+ # (harness plan docs/plans/droid-client-performance-options.md). The strata
6757
+ # engine has no --chat-template override: its template is GGUF-embedded, so the
6758
+ # Sharp-template round (inference-panel PR #4) skipped every strata arm. What
6759
+ # the proxy CAN do is what the template's own terseness block does at render
6760
+ # time: prepend the terse directive to the system prompt. Upstream measured the
6761
+ # full template at 2.7x faster medians at the same solve rate; this shim is
6762
+ # the approximation, weaker by construction (no thinking-retention/tool-format
6763
+ # changes), and cheap to A/B by env.
6764
+ # UAP_PROXY_TERSE_SYSTEM=0 — disable (the A/B off-arm)
6765
+ # The directive text is the Sharp template's terse block verbatim (its lead +
6766
+ # core), so the shim and the template say the same thing.
6767
+ _TERSE_SYSTEM_DIRECTIVE = (
6768
+ "Answer directly and concisely. Give the answer with only what it needs to be "
6769
+ "correct and usable.\n"
6770
+ "Never: open with preamble or pleasantries; restate the question; add filler "
6771
+ "transitions; hedge with niceties; or repeat a point you've already made.\n"
6772
+ "Always: keep essential steps, caveats, uncertainties, and specifics — never "
6773
+ "drop correctness or a needed warning for brevity. Keep the final answer lean. "
6774
+ "Use the least structure that conveys it (plain prose when short; lists or code "
6775
+ "only when they earn their place). If genuinely uncertain, say so and explain "
6776
+ "why — never omit uncertainty for the sake of brevity.\n"
6777
+ "If a user request is genuinely ambiguous, ask a sharp question, don't guess."
6778
+ )
6779
+
6780
+
6781
+ def _terse_system_enabled() -> bool:
6782
+ """Default ON, opt-out by env — mirrors the Sharp template's own
6783
+ default-on `terse` kwarg (opt out per request there, per machine here)."""
6784
+ return os.environ.get("UAP_PROXY_TERSE_SYSTEM", "1") != "0"
6785
+
6786
+
6755
6787
  def anthropic_to_openai_messages(anthropic_body: dict) -> list[dict]:
6756
6788
  """Convert Anthropic message format to OpenAI message format.
6757
6789
 
@@ -6763,17 +6795,32 @@ def anthropic_to_openai_messages(anthropic_body: dict) -> list[dict]:
6763
6795
  """
6764
6796
  messages = []
6765
6797
 
6766
- # Anthropic has system as a top-level param
6798
+ # Anthropic has system as a top-level param (string or content blocks).
6799
+ # O-D: when the terse shim is on, the directive is PREPENDED to the
6800
+ # caller's system text — matching the Sharp template's own behaviour of
6801
+ # merging the terse block into the caller's system prompt. A caller's
6802
+ # system prompt is never replaced, only led. When the client sent NO
6803
+ # system prompt, NOTHING is injected: the proxy's standing doctrine
6804
+ # (test_no_tools_does_not_inject_agentic_system_message) forbids
6805
+ # inventing a system message the caller did not send, terse or otherwise.
6767
6806
  system = anthropic_body.get("system")
6768
- if system:
6769
- if isinstance(system, str):
6770
- messages.append({"role": "system", "content": system})
6771
- elif isinstance(system, list):
6772
- text = "\n".join(
6773
- b.get("text", "") for b in system if b.get("type") == "text"
6807
+ system_text = ""
6808
+ if isinstance(system, str):
6809
+ system_text = system
6810
+ elif isinstance(system, list):
6811
+ system_text = "\n".join(
6812
+ b.get("text", "") for b in system if b.get("type") == "text"
6813
+ )
6814
+ if system_text:
6815
+ if _terse_system_enabled():
6816
+ messages.append(
6817
+ {
6818
+ "role": "system",
6819
+ "content": _TERSE_SYSTEM_DIRECTIVE + "\n\n" + system_text,
6820
+ }
6774
6821
  )
6775
- if text:
6776
- messages.append({"role": "system", "content": text})
6822
+ else:
6823
+ messages.append({"role": "system", "content": system_text})
6777
6824
 
6778
6825
  for msg in anthropic_body.get("messages", []):
6779
6826
  role = msg["role"]