Skip to content
Closed
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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]

### Changed

#### KB compile box: distinct image name + pod prefix (operations)

Renamed the KB compile box so operators can tell it apart from chat agentboxes at a glance during production troubleshooting (previously every pod was `agentbox-<id>` and the compile box could only be isolated by label/image filter).

- **Image rename**: `kbc-compile-box` → `siclaw-kbc-box` (Makefile build/push targets, helm `siclaw.compileBoxImage` derivation, Dockerfile/README/docs). `agentbox.compileBoxImage` explicit-override semantics are unchanged.
- **Pod-name prefix**: compile boxes now spawn as `kbc-box-<id>` instead of `agentbox-<id>`, declared on the BoxProfile (`podNamePrefix`) for the `kb-compile` / `kb-compile-codex` profiles. Derived resources (cert Secret, hostname) follow the prefix. Chat agentboxes and the read-only `kb-test` box keep the `agentbox-` prefix.
- **Upgrade behavior**: on the first compile spawn after upgrade, the spawner reaps any pod left under the old `agentbox-<id>` name for that agent (guarded to compile boxes only — a chat box under the same name is never touched), so the old and new pods do not coexist.
- **Transition double-push**: `make push-kbc` republishes the same image digest under the legacy name `kbc-compile-box` for one release cycle so runtimes still pinned to the old name keep resolving. Remove the legacy tag/push next release.
- **DevOps action required**: the registry replication rule (ap-southeast → cn-shanghai) must gain an entry for `siclaw-kbc-box` (the old `kbc-compile-box` was not in the rule and had to be pushed manually). Until the rule is added, push `siclaw-kbc-box` to cn-shanghai manually for production.

### Added

#### Prometheus Observability Layer
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ mTLS is **K8s mode only**. Do not add mTLS dependencies to local mode code paths
| `src/core/prompt.ts` | **⚠️ REQUIRES HUMAN APPROVAL** | — | Describe intent and wait for OK before editing |
| `src/memory/` | invariants.md §7, decisions.md ADR-005 | `npm test` | Requires embedding config; pi-agent only |
| `Dockerfile.agentbox` | security.md §3-5 | `docker build` | Dual-user model; capability set; setgid kubectl |
| `kbc/platform/pod/*` (KB compile box, Python) | kbc/platform/pod/README.md | `cd kbc/platform/pod && python test_compile_box.py` (needs `pip install claude-agent-sdk aiohttp`; CI: kbc-ci.yml, path-filtered) | Box↔runtime HTTP+SSE contract is shared with `src/gateway/capability/session-driver.ts` + `server.ts` (`/session` body, event vocabulary) and `agentbox/box-profile.ts` (allowedTools names, ANTHROPIC env forward) — change both sides together. Behavior changes need a `kbc-compile-box` image rebuild + redeploy (runtime env `SICLAW_COMPILE_BOX_IMAGE`); a rolled-out runtime does NOT pick up a new box image for existing live sessions |
| `kbc/platform/pod/*` (KB compile box, Python) | kbc/platform/pod/README.md | `cd kbc/platform/pod && python test_compile_box.py` (needs `pip install claude-agent-sdk aiohttp`; CI: kbc-ci.yml, path-filtered) | Box↔runtime HTTP+SSE contract is shared with `src/gateway/capability/session-driver.ts` + `server.ts` (`/session` body, event vocabulary) and `agentbox/box-profile.ts` (allowedTools names, ANTHROPIC env forward) — change both sides together. Behavior changes need a `siclaw-kbc-box` image rebuild + redeploy (runtime env `SICLAW_COMPILE_BOX_IMAGE`); a rolled-out runtime does NOT pick up a new box image for existing live sessions |
| `k8s/` or `helm/` | security.md §5, invariants.md §11 | `helm template` | mTLS K8s-only; container hardening |
| `src/agentbox/resource-handlers.ts` | invariants.md §1,6 | `npm test` | `materialize()` safe in K8s, destructive in local mode |
| `src/core/job-registry.ts` | tools.md §9 | `npm test` (`job-registry.test.ts`) | `claimNotification` is the single-fire dedup; a completion notice fires exactly once across the process-exit vs `job_stop` race |
Expand Down
17 changes: 14 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ RUNTIME_IMAGE = $(REGISTRY)/siclaw-runtime:$(TAG)
AGENTBOX_IMAGE = $(REGISTRY)/siclaw-agentbox:$(TAG)
PORTAL_IMAGE = $(REGISTRY)/siclaw-portal:$(TAG)
OCR_IMAGE = $(REGISTRY)/siclaw-ocr:$(TAG)
KBC_IMAGE = $(REGISTRY)/kbc-compile-box:$(TAG)
KBC_IMAGE = $(REGISTRY)/siclaw-kbc-box:$(TAG)
# Transition alias (remove next release): the KB compile box's former name. The
# push-kbc target double-pushes this same digest so runtimes still pinned to the
# old name keep resolving during the rename window.
KBC_LEGACY_IMAGE = $(REGISTRY)/kbc-compile-box:$(TAG)

# ── OCI labels injected into every image ──
DOCKER_LABELS = \
Expand Down Expand Up @@ -87,7 +91,7 @@ docker-portal: ## Build portal image
docker-ocr: ## Build OCR backend image
docker build -f Dockerfile.ocr $(DOCKER_LABELS) -t $(OCR_IMAGE) .

docker-kbc: ## Build KB compile-box image (spawned per compile run; helm agentbox.compileBoxEnabled derives this tag)
docker-kbc: ## Build KB compile-box image siclaw-kbc-box (spawned per compile run; helm agentbox.compileBoxEnabled derives this tag)
cd kbc && docker build -f platform/pod/Dockerfile $(DOCKER_LABELS) -t $(KBC_IMAGE) .

push: push-runtime push-agentbox push-portal push-ocr push-kbc ## Push all images to registry
Expand All @@ -104,8 +108,14 @@ push-portal: ## Push portal image
push-ocr: ## Push OCR backend image
docker push $(OCR_IMAGE)

push-kbc: ## Push KB compile-box image
push-kbc: ## Push KB compile-box image siclaw-kbc-box (+ transition legacy alias)
docker push $(KBC_IMAGE)
# Transition double-push (remove next release): republish the SAME digest under
# the legacy name kbc-compile-box so a runtime pinned to it during the rename
# window still resolves. Drop these two lines once all runtimes ship on the
# siclaw-kbc-box name.
docker tag $(KBC_IMAGE) $(KBC_LEGACY_IMAGE)
docker push $(KBC_LEGACY_IMAGE)

# ==================== Test ====================
##@ Test
Expand All @@ -132,6 +142,7 @@ info: ## Print build variables
@echo "PORTAL: $(PORTAL_IMAGE)"
@echo "OCR: $(OCR_IMAGE)"
@echo "KBC: $(KBC_IMAGE)"
@echo "KBC(legacy): $(KBC_LEGACY_IMAGE)"

logs: ## View recent logs (all components)
@echo "=== Runtime ===" && \
Expand Down
2 changes: 1 addition & 1 deletion docs/design/2026-07-kb-authoring-message-idempotency.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ turning the run checkpoint into an unbounded transcript.
The field is additive and optional, so Sicore, the runtime, and the compile-box
image may roll independently. Full protection is active after all three pieces
are deployed; changing `compile_box.py` requires rebuilding the
`kbc-compile-box` image.
`siclaw-kbc-box` image.
6 changes: 3 additions & 3 deletions helm/siclaw/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Build agentbox image string — same registry/tag as gateway, different componen
{{/*
KB compile-box image (spawned per compile run by the runtime; NOT a helm-managed
pod). Release-coupled by default: agentbox.compileBoxEnabled=true derives
{registry}/kbc-compile-box:{image.tag} — the image ships with every release
{registry}/siclaw-kbc-box:{image.tag} — the image ships with every release
(`make docker` builds it). agentbox.compileBoxImage overrides the full string
(hot-fix the compile brain independently of a release) and implies enabled.
Empty result ⇒ KB stays dark (fail-closed).
Expand All @@ -93,9 +93,9 @@ Empty result ⇒ KB stays dark (fail-closed).
{{- $ab.compileBoxImage -}}
{{- else if $ab.compileBoxEnabled -}}
{{- if .Values.image.registry -}}
{{- printf "%s/kbc-compile-box:%s" .Values.image.registry .Values.image.tag -}}
{{- printf "%s/siclaw-kbc-box:%s" .Values.image.registry .Values.image.tag -}}
{{- else -}}
{{- printf "kbc-compile-box:%s" .Values.image.tag -}}
{{- printf "siclaw-kbc-box:%s" .Values.image.tag -}}
{{- end -}}
{{- end -}}
{{- end }}
Expand Down
4 changes: 2 additions & 2 deletions helm/siclaw/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,13 @@ agentbox:
debugImage: ""
# KB compile capability (kb-compile / kb-test boxes; built from this repo's
# kbc/, ships with every release via `make docker`). The RELEASE-COUPLED
# switch: true ⇒ the runtime spawns {image.registry}/kbc-compile-box:{image.tag}
# switch: true ⇒ the runtime spawns {image.registry}/siclaw-kbc-box:{image.tag}
# — same registry/tag convention as every other component. false + no
# override ⇒ KB stays dark (fail-closed; compile attempts error, chat
# unaffected).
compileBoxEnabled: false
# Full-image override (implies enabled): hot-fix the compile brain
# independently of a release, e.g. "registry/.../kbc-compile-box:main-<sha>".
# independently of a release, e.g. "registry/.../siclaw-kbc-box:main-<sha>".
compileBoxImage: ""
# Node selector applied to every spawned AgentBox pod. Constrains pods to
# nodes carrying all of these labels (e.g. { disktype: ssd, pool: agents }).
Expand Down
4 changes: 2 additions & 2 deletions kbc/platform/pod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ platform/pod/.venv/bin/python platform/pod/test_compile_box.py
## Run (container, production form)

```bash
docker build -f platform/pod/Dockerfile -t kbc-compile-box .
docker build -f platform/pod/Dockerfile -t siclaw-kbc-box .
docker run --rm -p 3000:3000 \
-e ANTHROPIC_BASE_URL=https://<massapi>/ \ # model goes through the company massapi (key injected on the proxy side)
-v /tmp/wd:/work \
kbc-compile-box
siclaw-kbc-box
# then:
# POST :3000/sources {"run_id":"r1","bundle_base64":"...","bundle_sha256":"..."}
# or for Source Snapshot v2:
Expand Down
2 changes: 1 addition & 1 deletion kbc/platform/pod/destream.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
codex/OpenAI route is untouched, and KBC_DESTREAM=0/off is the operator
escape hatch (e.g. once the gateway ships cross-chunk incremental decoding).
Blast radius is this container image only: the shim binds 127.0.0.1 inside
the kbc-compile-box pod; runtime/agentbox/sicore LLM callers never see it.
the siclaw-kbc-box pod; runtime/agentbox/sicore LLM callers never see it.
"""

from __future__ import annotations
Expand Down
15 changes: 13 additions & 2 deletions src/gateway/agentbox/box-profile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ describe("getBoxProfile", () => {
});

it("kb-compile → dedicated image + LLM env + writable /work + box-owned restricted tools", () => {
process.env.SICLAW_COMPILE_BOX_IMAGE = "kbc-compile-box:test-tag";
process.env.SICLAW_COMPILE_BOX_IMAGE = "siclaw-kbc-box:test-tag";
const p = getBoxProfile("kb-compile");
expect(p.image).toBe("kbc-compile-box:test-tag");
expect(p.image).toBe("siclaw-kbc-box:test-tag");
expect(p.home).toBe("/work");
expect(p.nestedSandbox).toBeUndefined();
expect(p.volumes).toEqual([{ name: "work", mountPath: "/work", sizeLimit: "4Gi" }]);
Expand Down Expand Up @@ -60,6 +60,17 @@ describe("getBoxProfile", () => {
expect(test.allowedTools).not.toContain("Bash");
});

it("compile profiles declare the kbc-box pod prefix; agent/test keep the default", () => {
// Operational distinguishability: a compile box's pod name must read as a KB
// box, not a chat agentbox, when an operator scans `kubectl get pods`.
expect(getBoxProfile("kb-compile").podNamePrefix).toBe("kbc-box");
expect(getBoxProfile("kb-compile-codex").podNamePrefix).toBe("kbc-box");
// The default agent and the read-only kb-test box keep the agentbox prefix
// (kb-test is short-lived and out of scope for this rename).
expect(getBoxProfile("agent").podNamePrefix).toBeUndefined();
expect(getBoxProfile("kb-test").podNamePrefix).toBeUndefined();
});

it("fail-closed: an unknown profile throws (no silent downgrade to the all-tools agent)", () => {
expect(() => getBoxProfile("kb-tset")).toThrow(/unknown BoxProfile/);
});
Expand Down
15 changes: 12 additions & 3 deletions src/gateway/agentbox/box-profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ export interface BoxProfileVolume {
export interface BoxProfile {
/** Profile name; also the box reuse-key discriminator (A.5) and a pod label. */
name: string;
/**
* Pod-name prefix for boxes of this profile (default "agentbox"). The compile
* profiles override it to "kbc-box" so an operator scanning `kubectl get pods`
* during a production incident can tell a KB compile box from a chat agentbox
* by name alone, not just by label/image filtering. All resources derived from
* the pod name (cert Secret, hostname) follow this prefix automatically.
*/
podNamePrefix?: string;
/**
* Container image. undefined → the spawner's default agentbox image
* (this.config.image / $SICLAW_AGENTBOX_IMAGE).
Expand Down Expand Up @@ -71,7 +79,7 @@ export const AGENT_PROFILE: BoxProfile = {
* explicit compile-box image (helm `agentbox.compileBoxEnabled` ⇒ the
* SICLAW_COMPILE_BOX_IMAGE env). This is the single source of truth the Runtime
* advertises to its consumer on connect so the consumer can route compile runs
* here WITHOUT any consumer-side config. The bare `kbc-compile-box:latest`
* here WITHOUT any consumer-side config. The bare `siclaw-kbc-box:latest`
* fallback in the profiles above is deliberately NOT treated as capable: an
* unset env means KB stays dark (fail-closed), so we must not claim capability.
*/
Expand All @@ -89,7 +97,8 @@ export function isCompileCapable(): boolean {
function kbCompileProfile(): BoxProfile {
return {
name: "kb-compile",
image: process.env.SICLAW_COMPILE_BOX_IMAGE || "kbc-compile-box:latest",
podNamePrefix: "kbc-box",
image: process.env.SICLAW_COMPILE_BOX_IMAGE || "siclaw-kbc-box:latest",
// LLM credentials arrive in /session after fail-closed input materialization:
// consumer block first, Runtime Helm fallback only when that block is absent.
// Never duplicate a Runtime secret into the pod spec; only the non-secret
Expand Down Expand Up @@ -133,7 +142,7 @@ function kbCompileCodexProfile(): BoxProfile {
function kbTestProfile(): BoxProfile {
return {
name: "kb-test",
image: process.env.SICLAW_COMPILE_BOX_IMAGE || "kbc-compile-box:latest",
image: process.env.SICLAW_COMPILE_BOX_IMAGE || "siclaw-kbc-box:latest",
envForward: ["ANTHROPIC_BASE_URL", "KBC_*"],
home: "/work",
// Same cap as kb-compile — the profiles' documented invariant is "identical
Expand Down
Loading
Loading