Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"name": "lua-agent-builder",
"source": "./plugins/lua-agent-builder",
"description": "Build, test, and deploy Lua AI agents from inside Claude Code",
"version": "1.2.1",
"version": "1.2.2",
"homepage": "https://github.com/lua-ai-global/claude-code-lua-plugin#readme",
"repository": "https://github.com/lua-ai-global/claude-code-lua-plugin.git",
"license": "MIT",
Expand Down
4 changes: 2 additions & 2 deletions docs/USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ mkdir -p ~/agents/weather-news && cd ~/agents/weather-news && claude

**5. Test** — `/lua-test` picks the type (`skill`, `webhook`, `job`, `preprocessor`, `postprocessor`, `workflow`) and name from `dist-v2/manifest.json`. A failure is handed to the debug subagent automatically.

**6. Chat** — `/lua-chat` → sandbox (which pushes your local skills to the sandbox first), your message, a fresh thread (`lua chat --ci -e sandbox -m … -t`).
**6. Chat** — `/lua-chat` → sandbox (which pushes your local skills to the sandbox first), your message, a fresh thread (`lua chat --ci -e sandbox -m … -t`). ⚠ lua-cli 3.33.0 uploads the **whole environment of the shell Claude Code runs in** with those sandbox skill versions, and the runtime never reads it — start Claude Code from a clean shell (`env -i …`) when your shell holds secrets; see the plugin's SECURITY.md.

**7. QA** — `/lua-qa` runs 8–15 conversations on an isolated thread each, offline scenarios for every workflow, scans logs for `subType === 'error'`, and writes a triage report with a fix path per finding.

Expand Down Expand Up @@ -268,6 +268,6 @@ Precedence is deny → ask → allow. **The production gate is the `confirm-depl

**Where does my code go?** From lua-cli to `api.heylua.ai` (and `webhook.heylua.ai`, `cdn.heylua.ai`). The MCP server talks to `api.heylua.ai` and, for a session login, to Google's token endpoint to refresh the session. Claude Code sends the conversation to Anthropic per its own policy.

**How do I update the plugin?** `/plugin marketplace update claude-code-lua-plugin` then reinstall; 1.2.1 targets lua-cli 3.33.0.
**How do I update the plugin?** `/plugin marketplace update claude-code-lua-plugin` then reinstall; 1.2.2 targets lua-cli 3.33.0.

**Where do I report bugs?** Plugin: https://github.com/lua-ai-global/claude-code-lua-plugin/issues · Security: security@heylua.ai · lua-cli: https://github.com/lua-ai-global/lua-cli/issues · Docs: https://docs.heylua.ai (and `mcp__plugin_lua-agent-builder_lua-docs__submit_feedback` for a wrong page).
2 changes: 1 addition & 1 deletion plugins/lua-agent-builder/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lua-agent-builder",
"version": "1.2.1",
"version": "1.2.2",
"description": "Build, test, and deploy Lua AI agents from inside Claude Code",
"author": {
"name": "Lua AI",
Expand Down
37 changes: 37 additions & 0 deletions plugins/lua-agent-builder/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,43 @@

All notable changes to the `lua-agent-builder` plugin. Versions follow the tag `release-prod.yml` cuts from `package.json` (`v<version>`). lua-cli is a TypeScript SDK/CLI; it is unrelated to the Lua programming language.

## 1.2.2 — 2026-09-13

Knowledge-only release: corrections validated against production by the system audit of 2026-09-13 (live probes on the E2E agents, re-verified in lua-core-services and lua-iac source), applied to the knowledge base, subagent prompts, slash commands, SECURITY notes and user guide. No hook, permission or MCP behaviour changed. Every line names its source.

### Jobs (`primitives.md` §6, §14; `agents/lua-skill-builder.md`)

- Dropped the "two retry algorithms" hedge. Production and staging pin `LUA_JOBS_INTAKE_MODE = "enqueue"` (lua-iac `services/lua-core/{prod,staging}/k8s/config-map.tf`), so code jobs retry on the queued path only: a fixed `backoffSeconds` wait (default 60, no jitter), `min(maxAttempts, 10)` attempts, zero retries without a finite `maxAttempts`, and `job.execution` on every deployed run (lua-core `job.service.ts` `run` → `enqueueJob`, `getHeavyRetryEligibility`, `scheduleHeavyRetry`; `lua-sandbox-runner` `executor-entry.ts`). The exponential path is reached only by the platform's own `agent`-kind jobs (`enqueueJob` routes them to `processJob`; `DEFAULT_AGENT_JOB_RETRY`).

### `user.send()` reach (`primitives.md` §12, §16; `decision-trees.md`)

- Email removed from the last-interaction reach list: the only channel-window writer is lua-whatsapp `upsertChannelWindow` (`utils/channel-window.util.ts`, called from its WhatsApp, Facebook, Instagram, MessageBird, SMS and Teams services); lua-email never writes one, so the `type === 'email'` branch of `channel.service.ts` `sendToLastInteraction` is unreachable. Use `Channels.email.send`.

### Workflows runtime (`primitives.md` §5, §12, §14; `workflows.md` §1, §6, §9; `commands/lua-workflow.md`; `decision-trees.md`)

- `Workflows.resume` / `signal` / `signalByKey` / `startBatch` / `setGoal` / `goals.*` **work in production** (live probe 2026-09-13 reached `RUN_NOT_FOUND` / `CORRELATION_KEY_NOT_FOUND` / `WORKFLOW_NOT_FOUND` / `[]`); the `resume_unavailable` / `signal_unavailable` / `not_implemented` codes in lua-core `workflow-sandbox-bridge.ts` guard an unbound optional provider. Only `raiseBudget` is 501 in both runtimes (`raiseBudget: notImplemented(...)`; lua-cli `workflow.api.service.ts` `unavailable('raiseBudget', 'R45')`).
- Deployed `Workflows.list` applies only `status` and an untyped `workflowId`; the typed `workflow` filter is ignored in production (`workflow-sandbox-bridge.ts` `list`).
- `concurrencyPolicy: 'forbid'` is not enforced on `lua workflows start`, the REST start route, `Workflows.start()` or a trigger's `{ startWorkflow }`: `WorkflowRunService.createRun` has no overlap check (lua-core `workflow-run.service.ts` header; two starts 2 s apart both ran). Only the schedule dispatcher (`workflow-schedule-dispatch.service.ts`), the compose-tool start and batch starts check it. Guidance no longer relies on `RUNS_IN_FLIGHT`; the trigger `skipped_overlap` row is described as never produced today.

### Deployed runtime vs typings (`primitives.md` §4, §12, §14; `decision-trees.md`; `integrations.md`; `agents/lua-architect.md`, `agents/lua-skill-builder.md`)

- `Data.create` / `Data.update` deployed never forward `index` in any form (`sandbox-runtime` `custom.data.api.service.ts` bodies are `{ data, searchText }`); an index can only be declared from a `lua test` run (lua-cli `custom.data.api.service.ts`). Added the seed-tool recipe.
- `Voice.createSession` re-validated as absent in every runtime, `lua test` included (`sandbox-runtime` `context.ts`; lua-cli `utils/sandbox.ts`).
- `LuaWebhook` Zod schemas are never applied: the bundler rewrites the constructor to an object literal (`compiler/bundler.ts`) and both `lua test` (`utils/sandbox.ts`) and lua-core (`execute.webhook.service.ts`) call `primitive.execute(event)` directly — a schema-violating body reached `execute` with HTTP 200 live. Guidance: `safeParse` in `execute`.
- `LuaWebhook.secret` must be a string literal / compile-time constant (`compiler/plugins/webhook.plugin.ts`: `env('X')` fails `lua compile`) and verifies Lua's own `x-lua-signature` (lua-core `webhook.service.ts`), so it blocks vendors that sign with their own scheme — leave it unset for them and verify their HMAC in a `defineTrigger` `verify` over `rawBody`.
- Deployed webhooks have no wall timeout: the direct path runs in-process with only the VM's synchronous-prefix `timeout` (`webhook.service.ts` → `execute.webhook.service.ts`; `sandbox-runtime` `runner.ts`); a 200 s handler completed after the ~90 s ingress 504, while tools on the remote runner are cut at 180 s (`execute-function.service.ts` `TOOL_TIMEOUT_MS`; lua-iac `LUA_SANDBOX_ROUTING_DEFAULT = "remote"`). Guidance: short, idempotent handlers; never rely on a server-side cut.
- Commerce: SDK `OrderStatus.FULFILLED` sends `fulfilled` (lua-cli `interfaces/orders.ts`; `order.api.service.ts` puts it in the path unmapped) while the platform stores, filters and charts `fullfilled` (`shared-schemas` `ecommerce-order.schema.ts`, lua-api `orders/base.controller.ts`, `chart.service.ts`) and never validates the status param; order routes answer 200 `success:false`, which the SDK rethrows as a generic error. Guidance added.

### Sandbox chat uploads the shell environment (`primitives.md` §12 `env(key)`, §14; `cli-reference.md` §4; `commands/lua-chat.md`, `commands/lua-test.md`, `commands/lua-env.md`; `agents/lua-qa.md`; `SECURITY.md`; `docs/USER_GUIDE.md`)

- 1.2.1 already said `lua chat -e sandbox` uploads `process.env` merged with `.env`; 1.2.2 adds what the live probe showed — the runtime never reads it (a sandbox turn's `env()` resolves from `subAgent.env`, lua-core `skill-eligibility.resolver.ts`), lua-api/lua-agents accept it unfiltered (`ValidationPipe whitelist:false`) and lua-agents caches it ~24 h with the sandbox version. Corrected the 1.2.1 claim that sandbox turns read the uploaded map. New guidance wherever sandbox chat is offered: never run it from a shell holding secrets you would not hand to the platform; prefer `env -i` / a clean shell; `lua test` uploads nothing. A platform ticket is open.

### `--ci` exit codes (`cli-reference.md` §4; `commands/lua-test.md`, `commands/lua-push.md`, `commands/lua-deploy.md`; `agents/lua-skill-builder.md`, `agents/lua-debug.md`, `agents/lua-deploy-pilot.md`; `primitives.md` §14)

- Raw prompts exiting 0 under `--ci` were already covered in 1.2.1 (`prompt-handler.ts` `safePrompt`; `test.ts` `promptToolSelection`) — unchanged.
- Added: `lua test skill|webhook|job|…` prints `✅ … execution successful!` and exits 0 when `execute` throws — `utils/sandbox.ts` returns `{ status: 'error', error }` and `test.ts` never checks it. The test slash, skill-builder and debug agents now treat `status: 'error'` as a failure (the debug agent previously said "exit 1 with a stack").
- Added: `lua push all` and `lua deploy all` exit 0 after per-item failures (`push.ts` `failedItems` → summary only; `deploy.ts` `No versions … skipping`, no exit code). The push slash and the deploy pilot now parse the output.

## 1.2.1 — 2026-09-12

Knowledge-only release: reconciles the plugin's knowledge base, subagent prompts and slash commands with facts the docs rewrite verified against lua-cli 3.33.0 source and the platform packages (lua-api, lua-agents, lua-core, sandbox-runtime, lua-sandbox-runner, shared-types, lua-whatsapp, lua-web). No hook, permission or MCP behaviour changed; one lint guard was added. Every line below names the source that supports it.
Expand Down
4 changes: 4 additions & 0 deletions plugins/lua-agent-builder/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ Out of scope:
| Read-only MCP: no tool mutates platform state | `mcp/lua-platform/src/tools/*` (GET routes and `lua agents --json` only) |

If you find a way to bypass any of these without an explicit user prompt, please report it.

## Known platform-side exposure the plugin cannot close

**`lua chat -e sandbox` uploads the invoking shell's entire environment.** lua-cli 3.33.0 attaches `loadEnvironmentVariables()` — all of `process.env` merged with `.env` — as the `env` of every sandbox skill version it pushes (`src/utils/sandbox.ts` ~149-172, `src/services/sandbox.service.ts` ~232-238). lua-api and lua-agents accept the field unfiltered (`ValidationPipe whitelist:false`) and lua-agents caches it with the sandbox version in Redis for about 24 h; the runtime never reads it (a sandbox turn's `env()` resolves from the agent's server-side env — lua-core `skill-eligibility.resolver.ts`). Validated live on 2026-09-13 with a scrubbed shell. Consequence for this plugin: `/lua-chat` (sandbox) and the `lua-qa` subagent run `lua chat -e sandbox` from Claude Code's own process environment, so whatever that shell holds (cloud keys, `GITHUB_TOKEN`, npm tokens, …) leaves the machine on every sandbox chat. The plugin does not scrub the child environment itself — wrapping the command would break the single-permission contract and the `-t` lint — so the mitigation is operational: **start Claude Code from a shell that holds nothing you would not hand to the platform** (e.g. `env -i HOME="$HOME" PATH="$PATH" TERM="$TERM" zsh -f`, then `claude`, or a dedicated terminal profile), or use `-e production` / `lua test`, which upload nothing. The chat slash and the QA agent say this once before a sandbox chat. A platform ticket to upload `.env` only (or nothing) is open.
4 changes: 2 additions & 2 deletions plugins/lua-agent-builder/agents/lua-architect.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Apply these decisions in order (details in decision-trees.md):
1. **Integration before tools.** For a known SaaS, the auto-provisioned MCP already exposes CRUD — do not propose `list_events` / `create_record` tools. Custom tools are for derived logic only; raw endpoints go through `Integrations.passthrough`. Plan the discovery step: `lua integrations mcp list`, then inspect the MCP's real tool names before any custom tool is written.
2. **Event reactions**: a known-SaaS event → `lua integrations webhooks create` subscription; an arbitrary HTTP event → `defineTrigger` (agent turn / direct tool / `{ startWorkflow }`) or `LuaWebhook` (you need code and response control). `lua triggers …` manages platform triggers; it is not the integration-subscription command.
3. **Job vs workflow**: one unit under ~10 min with no human → `LuaJob`; several dependent steps, approvals, signals, fan-out, retries with a budget, or Job-tier code → `createWorkflow` (which can carry its own `schedule`).
4. **Per-user vs agent-wide data**: `User` vs `Data` (declare `index` on filtered fields). Commerce → `Products/Baskets/Orders` unless Shopify/WooCommerce owns the cart.
4. **Per-user vs agent-wide data**: `User` vs `Data` (declare `index` on filtered fields — from a `lua test` seed run; deployed code cannot declare one). Commerce → `Products/Baskets/Orders` unless Shopify/WooCommerce owns the cart (mind the `OrderStatus.FULFILLED` / `fullfilled` spelling split, primitives.md §12).
5. **Processors** only for agent-wide uniform transforms.
6. **Model**: omit for the platform default (`alibaba/qwen3.8-flash`); recommend a code from `lua models list --json --ci`, never an invented one; a per-request resolver when channels differ.
7. **Channel constraints**: WhatsApp → template strategy outside the 24 h window; voice → a `LuaVoice` and fast tools; email → plain text/HTML; Teams group chats → `conversationId` sends.
Expand Down Expand Up @@ -125,7 +125,7 @@ Be opinionated: recommend the **minimum viable set** plus one or two "v2" ideas.

Over-engineering to avoid: a Skill for 2-3 unrelated tools (still fine — skills are just groups; don't invent shared context that isn't there); a PreProcessor "for safety" when the persona refuses fine; a Job for what is really an event; `Agents.invoke` for what is `AI.generate`; a workflow for a single tool call; custom CRUD tools over an integration MCP; a hand-written `LuaMCPServer` for a SaaS Unified.to covers; e-commerce primitives when Shopify owns the cart.

Under-engineering to flag: per-user state with no `User` usage; a webhook receiving sensitive data with no signature verification (`secret` or vendor HMAC on `rawBody`); WhatsApp chosen with no template plan; external calls with no error handling; a multi-step process with human approval modelled as chat back-and-forth instead of a workflow `approval()`; a `Jobs.create` design that closes over variables (it is serialised); a long process on the worker tier (> 600 s) without `tier:'job'`.
Under-engineering to flag: per-user state with no `User` usage; a webhook receiving sensitive data with no signature verification (`secret` for callers you control — it is Lua's own scheme and a compile-time literal — or the vendor's HMAC verified in a `defineTrigger` `verify` over `rawBody`); a webhook that does long work inline (deployed webhooks have no wall timeout and the caller times out at ~90 s — hand off to a workflow) or trusts its Zod schemas (never applied); a non-reentrant workflow that relies on `concurrencyPolicy: 'forbid'` alone (not enforced on manual/API/SDK starts today); a job with no finite `retry.maxAttempts` (zero retries deployed); a `user.send()` path meant to reach email (unreachable — `Channels.email.send`); WhatsApp chosen with no template plan; external calls with no error handling; a multi-step process with human approval modelled as chat back-and-forth instead of a workflow `approval()`; a `Jobs.create` design that closes over variables (it is serialised); a long process on the worker tier (> 600 s) without `tier:'job'`.

## Constraints (§3.7 single-permission)

Expand Down
Loading
Loading