@miller-tech/uap 1.43.2 → 1.44.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,155 @@
1
+ /**
2
+ * Self-authored acceptance gate
3
+ *
4
+ * When a project exposes no detectable gates (no package.json build/test
5
+ * scripts — the common case for polyglot CLI tasks), the convergence loop has
6
+ * nothing to iterate against and degrades to a vacuous single-shot "success".
7
+ *
8
+ * This module asks the executor model to author a task-specific verification
9
+ * script (`.uap-deliver/verify.sh`) that exits 0 iff the task is correctly
10
+ * completed, using only repo-observable evidence (files, program output) — no
11
+ * network, no hidden test harness. The script is then registered as a required
12
+ * gate so deliver converges against it.
13
+ *
14
+ * The floor (anti-vacuous) rule: a usable acceptance gate MUST fail on the
15
+ * current, unsolved repository. If it already passes before the model has done
16
+ * any work, it is trivial and is regenerated with that feedback. This is what
17
+ * stops deliver from "converging to 1 immediately": turn 1 cannot pass a gate
18
+ * that is required to fail at the start.
19
+ */
20
+ import { spawnSync } from 'child_process';
21
+ import { existsSync, mkdirSync, writeFileSync, chmodSync } from 'fs';
22
+ import { join } from 'path';
23
+ const GATE_DIR = '.uap-deliver';
24
+ const GATE_FILE = 'verify.sh';
25
+ const DEFAULT_ATTEMPTS = 3;
26
+ const DEFAULT_TIMEOUT_MS = 120_000;
27
+ /** Pull a shell script out of a model response (fenced block preferred). */
28
+ export function extractScript(modelOutput) {
29
+ const fence = modelOutput.match(/```(?:bash|sh|shell)?\s*\n([\s\S]*?)```/);
30
+ const body = (fence ? fence[1] : modelOutput).trim();
31
+ // Guarantee a shebang so `bash <file>` and direct exec both behave.
32
+ if (/^#!/.test(body))
33
+ return body;
34
+ return `#!/usr/bin/env bash\n${body}`;
35
+ }
36
+ function buildAuthorPrompt(instruction, projectRoot, priorFeedback) {
37
+ const retry = priorFeedback
38
+ ? `\n\nYour previous script was rejected because: ${priorFeedback}\nWrite a stricter script that genuinely verifies the required outcome.`
39
+ : '';
40
+ return [
41
+ 'You are writing an ACCEPTANCE TEST for a coding task, not solving it.',
42
+ '',
43
+ `TASK:\n${instruction}`,
44
+ '',
45
+ `PROJECT ROOT: ${projectRoot}`,
46
+ '',
47
+ 'Write a self-contained POSIX bash script that:',
48
+ ' - exits 0 ONLY if the task is fully and correctly completed',
49
+ ' - exits non-zero otherwise (print a short reason to stderr)',
50
+ ' - checks concrete, observable evidence: expected files exist, a program',
51
+ ' builds/runs, output matches what the task requires',
52
+ ' - uses ONLY the repository and standard tools (no network, no access to',
53
+ ' any hidden test harness)',
54
+ ' - is runnable from the project root',
55
+ '',
56
+ 'CRITICAL: the script must FAIL right now, on the current unsolved repo,',
57
+ 'and only PASS once the task has actually been done. Do not write a check',
58
+ 'that trivially passes (e.g. `exit 0`, or only `test -d .`).',
59
+ '',
60
+ 'Output ONLY the script inside a single ```bash code block.',
61
+ retry,
62
+ ].join('\n');
63
+ }
64
+ /** Run the candidate gate against the current repo state. */
65
+ function runGate(scriptPath, projectRoot, timeoutMs) {
66
+ const r = spawnSync('bash', [scriptPath], {
67
+ cwd: projectRoot,
68
+ timeout: timeoutMs,
69
+ encoding: 'utf-8',
70
+ env: { ...process.env, CI: 'true' },
71
+ });
72
+ if (r.error) {
73
+ return { exitCode: null, spawnError: true, outputTail: String(r.error.message).slice(-500) };
74
+ }
75
+ const out = `${r.stdout ?? ''}${r.stderr ?? ''}`.slice(-500);
76
+ return { exitCode: r.status, spawnError: false, outputTail: out };
77
+ }
78
+ /**
79
+ * Author and validate a task-specific acceptance gate. Retries until the
80
+ * generated script fails on the current (unsolved) repo — the non-vacuity
81
+ * floor — or attempts are exhausted.
82
+ */
83
+ export async function authorAcceptanceGate(opts) {
84
+ const { instruction, projectRoot, executor } = opts;
85
+ const attempts = opts.maxAuthorAttempts ?? DEFAULT_ATTEMPTS;
86
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
87
+ const notes = [];
88
+ const gateDir = join(projectRoot, GATE_DIR);
89
+ const scriptPath = join(gateDir, GATE_FILE);
90
+ if (!existsSync(gateDir))
91
+ mkdirSync(gateDir, { recursive: true });
92
+ let priorFeedback = null;
93
+ let producedAny = false;
94
+ for (let attempt = 1; attempt <= attempts; attempt++) {
95
+ let response;
96
+ try {
97
+ response = await executor(buildAuthorPrompt(instruction, projectRoot, priorFeedback));
98
+ }
99
+ catch (err) {
100
+ notes.push(`attempt ${attempt}: model error authoring gate`);
101
+ priorFeedback = `the authoring call errored (${String(err).slice(0, 80)})`;
102
+ continue;
103
+ }
104
+ const script = extractScript(response);
105
+ writeFileSync(scriptPath, script, 'utf-8');
106
+ try {
107
+ chmodSync(scriptPath, 0o755);
108
+ }
109
+ catch {
110
+ /* non-fatal */
111
+ }
112
+ producedAny = true;
113
+ const run = runGate(scriptPath, projectRoot, timeoutMs);
114
+ if (run.spawnError) {
115
+ notes.push(`attempt ${attempt}: gate failed to run (${run.outputTail.slice(0, 80)})`);
116
+ priorFeedback = 'the script could not execute (syntax/interpreter error)';
117
+ continue;
118
+ }
119
+ if (run.exitCode === 0) {
120
+ // Vacuous: passes on the unsolved repo. Reject and retry stricter.
121
+ notes.push(`attempt ${attempt}: gate passed on the UNSOLVED repo — too weak, regenerating`);
122
+ priorFeedback = 'it passed on the unsolved repository (it must fail until the work is done)';
123
+ continue;
124
+ }
125
+ // Good: a discriminating gate that fails now and must be made to pass.
126
+ notes.push(`attempt ${attempt}: gate fails on unsolved repo (exit ${run.exitCode}) — accepted`);
127
+ return {
128
+ rung: buildRung(scriptPath, timeoutMs),
129
+ scriptPath,
130
+ vacuous: false,
131
+ attempts: attempt,
132
+ notes,
133
+ };
134
+ }
135
+ // Exhausted attempts. Return the last gate (if any) flagged as vacuous/weak so
136
+ // the caller can warn; never silently treat absence as success.
137
+ return {
138
+ rung: producedAny ? buildRung(scriptPath, timeoutMs) : null,
139
+ scriptPath: producedAny ? scriptPath : null,
140
+ vacuous: true,
141
+ attempts,
142
+ notes,
143
+ };
144
+ }
145
+ function buildRung(scriptPath, timeoutMs) {
146
+ return {
147
+ id: 'acceptance',
148
+ name: 'Acceptance check (.uap-deliver/verify.sh)',
149
+ command: 'bash',
150
+ args: [scriptPath],
151
+ required: true,
152
+ timeoutMs,
153
+ };
154
+ }
155
+ //# sourceMappingURL=self-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-gate.js","sourceRoot":"","sources":["../../src/delivery/self-gate.ts"],"names":[],"mappings":"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"}
package/docs/INDEX.md CHANGED
@@ -18,6 +18,7 @@ New here? Start with the [project README](../README.md), then [Getting Started](
18
18
 
19
19
  | Doc | What it covers |
20
20
  |---|---|
21
+ | [**What UAP Does Automatically**](guides/AUTOMATIC.md) | Every feature in benefit / when-it-kicks-in terms — install once, it all self-applies ⭐ |
21
22
  | [**`uap deliver`**](guides/DELIVER.md) | The delivery harness — convergence loop to verified completion ⭐ |
22
23
  | [Memory](guides/MEMORY.md) | The 4-tier memory system, write-gates, semantic recall |
23
24
  | [MCP Router](guides/MCP_ROUTER.md) | Token-optimizing tool proxy + FTS5 output compression |
@@ -28,6 +29,7 @@ New here? Start with the [project README](../README.md), then [Getting Started](
28
29
  | [Deploy Batching](guides/DEPLOY_BATCHING.md) | Conflict-free batched git/deploy actions |
29
30
  | [Coordination](guides/COORDINATION.md) | Multi-agent overlap detection |
30
31
  | [Local Models](guides/LOCAL_MODELS.md) | Running agents against local llama.cpp / Qwen models |
32
+ | [Qwen3.6 on llama.cpp by VRAM](guides/QWEN36_LLAMACPP.md) | Tiered 8/12/16/24/32 GB setup; how `uap deliver` uplifts small local models |
31
33
 
32
34
  ## Architecture
33
35
 
@@ -0,0 +1,92 @@
1
+ # What UAP Does For You — Automatically
2
+
3
+ > The whole point of UAP: **you install it once, and every feature applies itself
4
+ > as you code.** You don't call commands or remember protocols. UAP watches the
5
+ > coding agent's lifecycle (session start, every prompt, every tool call, every
6
+ > stop) and injects the right help or enforces the right guardrail *at the moment
7
+ > it's needed*.
8
+
9
+ ```bash
10
+ npx @miller-tech/uap init # one-time, per project
11
+ # …that's it. Open your coding agent and everything below is live.
12
+ ```
13
+
14
+ `init`/`setup` wire UAP into whichever agent you use — Claude Code, Cursor,
15
+ OpenCode, Factory, VSCode, Codex — by installing lifecycle hooks and the MCP
16
+ router. After that the features are **on by default and apply themselves as
17
+ appropriate**. Nothing here needs to be invoked by hand.
18
+
19
+ ---
20
+
21
+ ## How to read this
22
+
23
+ Two kinds of automatic behaviour, and they're deliberately different:
24
+
25
+ - **Assist** (dynamic, *helps* you): surfaces the right context — experts,
26
+ skills, patterns, memories — by *injecting* it where the model will see it.
27
+ It's confidence-gated, so quiet on conversational turns and rich on real
28
+ coding tasks. It never blocks; worst case it stays silent.
29
+ - **Enforce** (deterministic, *protects* you): hard guardrails that *block* a
30
+ tool call when it would violate a rule (edit outside a worktree, skip
31
+ delivery, run a dangerous command). Each has an escape hatch for the rare
32
+ sanctioned exception.
33
+
34
+ For every feature below: **what it does for you**, and **when it kicks in**.
35
+
36
+ ---
37
+
38
+ ## Assist — the right help shows up on its own
39
+
40
+ | Feature | What it does for you | When it kicks in |
41
+ |---|---|---|
42
+ | **Reactor** (dynamic routing) | On every prompt, surfaces the expert droids, skills, and enforcement patterns relevant to *this* task, so the agent works like it already knows the domain. | Every substantive prompt (`UserPromptSubmit` / per-message). Confidence-gated — silent on "thanks"/"merge it", rich on "fix the auth race condition". |
43
+ | **Memory recall** | Pulls back the lessons, decisions, and gotchas you (or another agent) learned before, so mistakes aren't repeated and context survives across sessions. | Session start (recent + high-importance memories) and per-prompt semantic recall on the task text. |
44
+ | **Pattern RAG** | Injects battle-tested execution patterns (Output-Existence, Decoder-First, Round-Trip verify, …) mined from Terminal-Bench, so the agent uses the approach that actually passes. | Per-prompt, matched to the task; full set retrievable on demand via Qdrant. |
45
+ | **Expert droids** | Routes domain work (security, performance, data, testing, …) to a specialist persona instead of a generalist guess. | When the capability router matches the task's type/files — recommended automatically, with optional auto-spawn above a confidence threshold. |
46
+ | **Skills** | Surfaces the right *procedure* (git-forensics, compression, SQLite-WAL recovery, polyglot, …) for the task at hand. | Per-prompt match against the task; top-N surfaced. |
47
+ | **Model routing** | Picks the right model tier per step (plan with the strong model, execute with the fast one) instead of one model for everything. | On task classification, by complexity and role. |
48
+
49
+ You don't ask for any of this. It appears in the agent's context the moment the
50
+ task warrants it, and stays out of the way when it doesn't.
51
+
52
+ ---
53
+
54
+ ## Enforce — the guardrails that keep work safe and verified
55
+
56
+ | Feature | What it does for you | When it kicks in |
57
+ |---|---|---|
58
+ | **Delivery enforcement** (`uap deliver`, **block by default**) | Routes substantive coding through the **convergence loop** — which iterates a model against your real gates (build, type-check, tests) until the change is *verified*, not just plausible. This is what **uplifts small local models well above their weight**: a 3B-active model that would flail on one shot succeeds when driven to green against the gates. | The moment the agent tries to edit a **source** file directly. Docs/configs/scripts/tests are exempt — only real implementation work is gated. Escape: `UAP_DELIVER_BYPASS=1`, or relax with `UAP_ENFORCE_DELIVERY=advisory`. |
59
+ | **Worktree isolation** | Forces code changes into an isolated `.worktrees/NNN-slug/` branch so you never clobber your working tree and every change is a clean, reviewable branch with an auto-PR. | Any source edit outside a worktree is blocked (`PreToolUse`). |
60
+ | **Policy / compliance gates** | Block non-compliant tool calls before they run — dangerous shell (force-push, `terraform apply`), edits that skip a schema diff, plan-before-read violations, etc. | `PreToolUse` on every Edit/Write/Bash/Task call. |
61
+ | **Schema-diff gate** | Flags breaking API/contract changes so you diff-and-verify consumers before shipping them. | After editing a schema/contract file (`*.schema.ts`, `types.ts`, `.proto`, `.graphql`, …). |
62
+ | **Completion gates** | Won't let the agent declare "done" until build/type-check/tests actually pass and a version bump happened. | On `Stop` (end of turn). |
63
+ | **Coordination** | Detects when multiple agents would touch the same files and prevents them stepping on each other. | Session start (register) + work announcement before claiming a task. |
64
+ | **rtk token-optimization** | Rewrites heavy CLI output (git/docker/npm/…) into compact form so the agent burns far fewer tokens reading command output. | Every wrapped CLI command. |
65
+ | **Deploy batching** | Queues changes into conflict-free batched commits/deploys instead of racy one-off pushes. | On `uap deliver --deploy` success. |
66
+
67
+ Each enforce-gate has a sanctioned escape hatch (an env var) for the rare case
68
+ you genuinely need to bypass it — so the guardrail is firm, not a cage.
69
+
70
+ ---
71
+
72
+ ## Behind it all
73
+
74
+ | Feature | What it does for you | When it kicks in |
75
+ |---|---|---|
76
+ | **MCP router** | Exposes a tiny meta-tool surface (`discover_tools`/`execute_tool`/`deliver`/`react`) instead of 150+ tools, cutting tool-schema tokens by ~98%. | Wired at install; used whenever the agent discovers/runs a tool. |
77
+ | **HALO trace analysis** | Mines your execution traces for systemic failure modes (loops, stalls) so the harness gets better over time. | Session end / on demand (`uap harness analyze`). |
78
+ | **4-tier memory** | Short-term (recent), long-term (semantic Qdrant), coordination, and patterns — the substrate the recall/pattern features draw from. | Continuously; written on significant decisions, read on recall. |
79
+
80
+ ---
81
+
82
+ ## The one-liner
83
+
84
+ **Install UAP, then just code.** The assist layer makes your agent act like a
85
+ domain expert with perfect recall; the enforce layer makes sure whatever it
86
+ produces is isolated, verified, and safe — and it drives even small local models
87
+ to *verified* results they couldn't reach in one shot. You never invoke any of
88
+ it; it applies itself, in the right place, at the right time.
89
+
90
+ See also: [`uap deliver`](DELIVER.md) · [Local Models](LOCAL_MODELS.md) ·
91
+ [Droids & Skills](DROIDS_AND_SKILLS.md) · [Policies](POLICIES.md) ·
92
+ the [Reactor design](../design/UAP_REACTOR.md).
@@ -7,6 +7,11 @@ UAP can drive its coding/convergence loop against **local models** served by
7
7
  This keeps inference on your own hardware (zero per-token cost) and works with
8
8
  quantized open-weight models such as Qwen 3.x.
9
9
 
10
+ > **Just want the recommended local setup?** See
11
+ > [Qwen3.6 35B-A3B on llama.cpp, by VRAM tier](QWEN36_LLAMACPP.md) for
12
+ > copy-paste launch commands for 8 / 12 / 16 / 24 / 32 GB GPUs, and how
13
+ > `uap deliver` uplifts a small local model to *verified* results.
14
+
10
15
  There are two endpoint shapes involved, and it matters which client speaks
11
16
  which protocol:
12
17
 
@@ -0,0 +1,110 @@
1
+ # Qwen3.6 35B-A3B on llama.cpp, by VRAM tier — with UAP
2
+
3
+ This is the recommended local stack for UAP: **Qwen3.6 35B-A3B** (a Mixture-of-
4
+ Experts model with only **~3B active parameters** per token) served by
5
+ **llama.cpp**, driven by **UAP's automatic features** — above all `uap deliver`,
6
+ which iterates the model against your real build/test gates until the change is
7
+ *verified*. That convergence loop is what lets a small, cheap, local model
8
+ **punch well above its weight**: one-shot it would flail; driven to green it
9
+ delivers.
10
+
11
+ Because the active footprint is ~3B, this model runs usefully even on modest
12
+ GPUs by **offloading the (sparse, mostly-idle) expert tensors to system RAM**
13
+ while keeping attention on the GPU. The knob for that is `--n-cpu-moe`.
14
+
15
+ ## Get the model
16
+
17
+ A 4-bit quant is the sweet spot for coding (quality vs. size). The full weights
18
+ are ~18–19 GB at IQ4_XS:
19
+
20
+ ```
21
+ Qwen3.6-35B-A3B-UD-IQ4_XS.gguf # ~18–19 GB on disk
22
+ # (a *-MTP.gguf build adds multi-token prediction for faster decode — use it if you have it)
23
+ ```
24
+
25
+ ## The base llama-server command
26
+
27
+ UAP speaks the OpenAI-compatible endpoint, so serve on `:8080/v1`. The flags
28
+ below are the ones that matter; the per-tier table just changes `--n-cpu-moe`,
29
+ `--ctx-size`, and the KV-cache type.
30
+
31
+ ```bash
32
+ llama-server \
33
+ --model Qwen3.6-35B-A3B-UD-IQ4_XS.gguf \
34
+ --host 0.0.0.0 --port 8080 \
35
+ --gpu-layers 99 \ # put all layers on GPU; experts get pulled back by --n-cpu-moe
36
+ --n-cpu-moe <PER TIER> \ # how many layers keep their MoE experts in CPU RAM
37
+ --ctx-size <PER TIER> \
38
+ --cache-type-k <q4_0|q8_0> --cache-type-v <q4_0|q8_0> \ # quantize KV cache to save VRAM
39
+ --flash-attn on \ # faster + less VRAM
40
+ --jinja \ # use the model's chat template (REQUIRED for tool calls)
41
+ --parallel 1
42
+ ```
43
+
44
+ > **`--jinja` / the chat template is non-negotiable for agent use.** Qwen3.6
45
+ > emits *native* OpenAI tool calls, but only when its chat template is active.
46
+ > If tools silently never fire, that's the cause — run `uap tool-calls setup`
47
+ > to install/repair the template, and `uap tool-calls status` to check.
48
+
49
+ ## VRAM tiers
50
+
51
+ Values are **starting points** — exact `--n-cpu-moe` depends on your build and
52
+ layer count. Rule of thumb: **raise `--n-cpu-moe` if you OOM, lower it for more
53
+ speed.** "System RAM" is what the offloaded experts need *in addition* to the
54
+ GPU.
55
+
56
+ | VRAM | `--n-cpu-moe` | `--ctx-size` | KV cache | System RAM | What to expect |
57
+ |---|---|---|---|---|---|
58
+ | **8 GB** | `99` (all experts → CPU) | `8192` | `q4_0` | ≥ 32 GB | Attention on GPU, all experts on CPU. Decode is CPU-bandwidth-bound (a few tok/s) — slow but *real*. `uap deliver` makes it productive by driving to verified completion instead of needing a strong one-shot. |
59
+ | **12 GB** | `~36` | `16384` | `q4_0` | ≥ 32 GB | Keep ~the top experts on GPU, rest on CPU. Noticeably faster than 8 GB. |
60
+ | **16 GB** | `~24` | `24576` | `q4_0` | ≥ 24 GB | Roughly half the experts on GPU. Comfortable for most coding tasks. |
61
+ | **24 GB** | *omit* (full model on GPU) | `32768`–`65536` | `q8_0` | 16 GB | **Sweet spot** (RTX 3090/4090). Weights + KV fit on-GPU; use `q8_0` KV for quality, `q4_0` if you want more context. Add `--flash-attn on`. |
62
+ | **32 GB** | *omit* | `131072` | `q8_0`/`f16` | 16 GB | Full model + large context. Add `--parallel 2–4` for concurrent sessions, bump `--batch-size`/`--ubatch-size`. |
63
+
64
+ Speed extras (any tier): `--flash-attn on` (always), and if your build supports
65
+ it, **self-speculation / MTP** (the `*-MTP.gguf` model, or `--draft-*` flags with
66
+ a tiny draft model) for materially faster decode.
67
+
68
+ ## Point UAP at it
69
+
70
+ ```bash
71
+ # 1) Tell UAP where the model lives (OpenAI-compatible endpoint)
72
+ export UAP_INFERENCE_ENDPOINT="http://localhost:8080/v1"
73
+ # (or set the endpoint on the model preset in .uap.json / src/models/types.ts)
74
+
75
+ # 2) Make sure tool calls work
76
+ uap tool-calls setup # install the chat template + helpers
77
+ uap tool-calls status # verify
78
+
79
+ # 3) That's it — code as normal. Everything is automatic from here.
80
+ ```
81
+
82
+ Once installed, **you don't run `uap deliver` by hand** — delivery enforcement
83
+ is on by default, so when your agent goes to implement something it's routed
84
+ through the convergence loop automatically (see
85
+ [What UAP Does For You, Automatically](AUTOMATIC.md)). If you *want* to drive a
86
+ task explicitly:
87
+
88
+ ```bash
89
+ uap deliver "implement a token-bucket rate limiter with tests"
90
+ ```
91
+
92
+ ## Why this punches above its weight
93
+
94
+ A 3B-active model rarely nails a non-trivial change in one shot. UAP changes the
95
+ game without changing the model:
96
+
97
+ - **`uap deliver`** loops execute → run-the-gates → fix → re-run until build,
98
+ type-check, and tests all pass — turning "plausible" into "verified".
99
+ - **Pattern RAG + expert routing** (automatic) put the right approach and the
100
+ right specialist persona in front of the model before it starts.
101
+ - **Memory** stops it re-making the same mistakes across turns and sessions.
102
+ - **Worktree + completion gates** keep every attempt isolated and only let
103
+ "done" mean *actually done*.
104
+
105
+ Net effect: a local, zero-per-token, 4-bit MoE model produces verified results
106
+ that a naive one-shot of a much larger model often won't — and it runs on a GPU
107
+ you already own.
108
+
109
+ See also: [Local Models](LOCAL_MODELS.md) · [`uap deliver`](DELIVER.md) ·
110
+ [Automatic features](AUTOMATIC.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "1.43.2",
3
+ "version": "1.44.0",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -118,12 +118,17 @@ else
118
118
  # -------------------------------------------------------------------
119
119
  # NPM REGISTRY MODE: Fallback when no local mount is present
120
120
  # -------------------------------------------------------------------
121
+ # NOTE: the published package is @miller-tech/uap (the old
122
+ # `universal-agent-protocol` name 404s). Prefer the local /uap-local mount
123
+ # (handled above + re-installed by the agent after upload); this registry
124
+ # path is only a last-resort fallback.
121
125
  CURRENT_UAP=$(uap --version 2>/dev/null || echo "none")
122
- if [ "$CURRENT_UAP" = "$UAP_VERSION" ]; then
123
- echo " UAP v${UAP_VERSION} already installed - skipping"
126
+ if [ "$CURRENT_UAP" != "none" ]; then
127
+ echo " UAP already installed (v${CURRENT_UAP}) - skipping registry fetch"
124
128
  else
125
- npm i -g universal-agent-protocol@${UAP_VERSION} 2>/dev/null || true
126
- echo " UAP v${UAP_VERSION} installed from npm registry"
129
+ npm i -g @miller-tech/uap@latest 2>/dev/null \
130
+ && echo " UAP installed from npm registry (@miller-tech/uap@latest)" \
131
+ || echo " WARNING: UAP npm registry install failed (will rely on /uap-local)"
127
132
  fi
128
133
  fi
129
134
 
@@ -1179,6 +1179,10 @@ class OpenCodeUAP(BaseInstalledAgent):
1179
1179
  "tools/agents",
1180
1180
  "tools/uap_harbor",
1181
1181
  "harbor-configs",
1182
+ # Needed so `uap install opencode` can wire the policy gate +
1183
+ # delivery-enforcement enforcer inside the container.
1184
+ "src/policies/enforcers",
1185
+ "src/policies/schemas/policies",
1182
1186
  ]
1183
1187
  local_upload_files = [
1184
1188
  "package.json",
@@ -1227,6 +1231,13 @@ class OpenCodeUAP(BaseInstalledAgent):
1227
1231
 
1228
1232
  logger.info("[Local UAP] Local project uploaded to /uap-local/")
1229
1233
 
1234
+ # NOTE: we deliberately do NOT `npm i -g file:/uap-local` here. The
1235
+ # install script's npm path already globally installs @miller-tech/uap
1236
+ # (with its runtime deps) and runs `uap install opencode`. Re-linking
1237
+ # the global bin to /uap-local — which has no node_modules — would break
1238
+ # the CLI at runtime (ERR_MODULE_NOT_FOUND: commander). The uploaded
1239
+ # /uap-local copy is kept only for Python-side imports / inspection.
1240
+
1230
1241
  def populate_context_post_run(self, context: AgentContext) -> None:
1231
1242
  _parse_token_counts(self.logs_dir, context)
1232
1243
 
@@ -1398,7 +1409,14 @@ class OpenCodeUAP(BaseInstalledAgent):
1398
1409
  def create_run_agent_commands(self, instruction: str) -> list[ExecInput]:
1399
1410
  model = self.model_name or "llama.cpp/qwen35-a3b-iq4xs"
1400
1411
 
1401
- env = {"OPENCODE_FAKE_VCS": "git"}
1412
+ env = {
1413
+ "OPENCODE_FAKE_VCS": "git",
1414
+ # Transparent delivery: the uap-enforce plugin reads these to run
1415
+ # the `uap deliver` convergence loop on the first source edit.
1416
+ "UAP_DELIVER_ENDPOINT": self._api_endpoint,
1417
+ "UAP_DELIVER_MODEL": os.environ.get("UAP_DELIVER_MODEL", "qwen35-a3b"),
1418
+ "UAP_ENFORCE_DELIVERY": os.environ.get("UAP_ENFORCE_DELIVERY", "block"),
1419
+ }
1402
1420
 
1403
1421
  # --- Step 0: Build classified CLAUDE.md and enhanced instruction ---
1404
1422
  classified_claude_md = build_classified_claude_md(instruction)
@@ -1524,6 +1542,17 @@ class OpenCodeUAP(BaseInstalledAgent):
1524
1542
  # --- Step 5: Environment bootstrapping ---
1525
1543
  commands.append(ExecInput(command=ENV_BOOTSTRAP_CMD))
1526
1544
 
1545
+ # --- Step 5b: Stage raw task instruction for transparent delivery ---
1546
+ # The uap-enforce plugin reads /app/.uap-deliver/task.txt to seed the
1547
+ # `uap deliver` convergence loop on the first source edit.
1548
+ task_b64 = base64.b64encode(instruction.encode()).decode()
1549
+ deliver_task_cmd = (
1550
+ "mkdir -p /app/.uap-deliver && "
1551
+ f"echo '{task_b64}' | base64 -d > /app/.uap-deliver/task.txt && "
1552
+ "echo '[Deliver] task instruction staged for transparent delivery'"
1553
+ )
1554
+ commands.append(ExecInput(command=deliver_task_cmd))
1555
+
1527
1556
  # --- Step 6: Run opencode with enhanced instruction ---
1528
1557
  # opencode.json baseURL points to proxy at http://127.0.0.1:11435/v1
1529
1558
  # which injects tool_choice="required" and forwards to the real LLM
@@ -41,6 +41,11 @@ export const UapEnforce: Plugin = async ({ $ }) => {
41
41
  let totalToolCalls = 0;
42
42
  const SOFT_BUDGET = 30;
43
43
 
44
+ // Transparent delivery: the first substantive source edit triggers the
45
+ // `uap deliver` convergence loop ONCE per session. Guarded so it never
46
+ // re-enters or fires per-edit.
47
+ let deliverRan = false;
48
+
44
49
  const TELEMETRY_PATH = '/tmp/uap-telemetry.jsonl';
45
50
 
46
51
  const simpleHash = (s: string): string => {
@@ -60,6 +65,47 @@ export const UapEnforce: Plugin = async ({ $ }) => {
60
65
  'tool.execute.before': async (input, output) => {
61
66
  totalToolCalls++;
62
67
 
68
+ // --- Transparent delivery (Layer 5): converge via `uap deliver` ---
69
+ // When delivery enforcement is active (the UAP default), the FIRST
70
+ // substantive write/edit hands the task to the `uap deliver` convergence
71
+ // loop, which iterates the model against the project's real gates
72
+ // (build/typecheck/test) before the agent's own edit lands. This is how
73
+ // deliver "uplifts" a small local model: plausible -> verified. It runs
74
+ // exactly once per session; the original edit then proceeds normally.
75
+ // Escape hatches: UAP_ENFORCE_DELIVERY=advisory or UAP_DELIVER_BYPASS=1.
76
+ if (
77
+ !deliverRan &&
78
+ (input.tool === 'write' || input.tool === 'edit') &&
79
+ process.env.UAP_ENFORCE_DELIVERY !== 'advisory' &&
80
+ !process.env.UAP_DELIVER_BYPASS
81
+ ) {
82
+ deliverRan = true; // set first — prevents re-entry / per-edit loops
83
+ try {
84
+ const endpoint = process.env.UAP_DELIVER_ENDPOINT || '';
85
+ const model = process.env.UAP_DELIVER_MODEL || 'qwen35-a3b';
86
+ const endpointArg = endpoint ? `--endpoint ${endpoint}` : '';
87
+ const deliverScript =
88
+ 'source $HOME/.nvm/nvm.sh 2>/dev/null || true; ' +
89
+ 'TASK="$(cat /app/.uap-deliver/task.txt 2>/dev/null)"; ' +
90
+ 'if [ -n "$TASK" ] && command -v uap >/dev/null 2>&1; then ' +
91
+ `echo "[Deliver] converging via uap deliver (model ${model})..."; ` +
92
+ `UAP_DELIVER_ACTIVE=1 uap deliver "$TASK" ${endpointArg} --model ${model} ` +
93
+ '--project-root /app --max-turns 5 --no-until-delivered ' +
94
+ '> /logs/agent/uap-deliver.log 2>&1 || true; ' +
95
+ 'echo "[Deliver] convergence loop finished"; ' +
96
+ 'else echo "[Deliver] skipped (no task file or uap CLI absent)"; fi';
97
+ await $`bash -lc ${deliverScript}`.nothrow();
98
+ await $`echo ${JSON.stringify(
99
+ JSON.stringify({ event: 'transparent_deliver', tool: input.tool, ts: new Date().toISOString() })
100
+ )} >> ${TELEMETRY_PATH}`
101
+ .quiet()
102
+ .nothrow();
103
+ } catch {
104
+ /* fail open — let the original edit proceed */
105
+ }
106
+ // fall through: the agent's own edit still executes
107
+ }
108
+
63
109
  // --- Worktree enforcement (Layer 4) ---
64
110
  // Warn when file writes happen outside a worktree
65
111
  if ((input.tool === 'write' || input.tool === 'edit') && output.args?.filePath) {