- ConfigPicker harness dropdown rows now render the Claude Code mark
as a leading icon (PickerRow gained an optional `icon` prop).
- Removed the Anthropic credentials status pill from the composer
chrome per request. Deleted the AnthropicStatusBadge component and
its now-dead plumbing: the anthropicState/anthropicStatus props
threaded ConfigPicker ← SessionView ← AppShell, and AppShell's
useAnthropicStatus consumption. The hook + anthropic-status lib stay
(still used by the harness settings page).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- ConfigPicker: the harness pill above the composer now shows the
Claude Code mark (brand orange #D97757) instead of the generic
lucide Bot. Every HARNESS_OPTIONS entry is a Claude Code variant
today, so the mark always fits.
- Harnesses settings page: ClaudeCodeIcon switched from currentColor
to the #D97757 color variant; the row's icon badge dropped the
indigo gradient for a neutral surface so the orange reads cleanly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both Claude Code harness rows now render the official Claude Code
mark (from @lobehub/icons-static-svg, mono variant inlined as a
component) instead of the placeholder lucide Sparkles/Terminal icons.
fill=currentColor keeps it white inside the existing gradient badge.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Committing the working-tree state at the user's request ("commit
everything"). This is a mixed checkpoint, not a single feature:
- core/back process-pty/: new interactive PTY harness engine (Claude
Code driven through a pseudo-terminal instead of -p), plus its
jsonl-tailer, stream-scraper, supervisor, and home-provisioner.
- core/back/scripts/: PTY scraper/tailer test fixtures + inspection
helpers.
- core/ui: ConfigPicker / Sidebar / SessionView / store / harnesses
updates surfacing the PTY harness option in the composer.
- platform/front: move to a persistent (app) route group with
AppShellPersistent so the WS + session buffers survive
/ ↔ /sessions/<id> navigation.
- protocol/sdk: session + ws shape updates feeding the above.
All 7 workspace packages typecheck clean at this revision.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- _core-shell.tsx: sidebar aside was m-3, platform's Sidebar uses m-1.
Match it so Core front and the workspace chrome line up.
- settings/harnesses/page.tsx: split into "Available harnesses" (data-
driven list of Claude Code + Claude Code PTY, with version tag and
availability badge, mirroring HARNESS_OPTIONS) and "Provider
accounts" (the existing Anthropic credential card).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When the OAuth-self-login flow was debugged in Phase 4 someone
short-circuited the chat handler's `credentials` dep — registerChatWsHandlers
was called WITHOUT a CredentialsStore, so every chat.send / chat.answer
resolved credential=undefined regardless of what the OAuth exchange
stored. Claude Code then asked for /login because the harness fallback
to ~/.claude/.credentials.json had nothing to fall back to in a clean
instance container.
The fix is the one-line revert: `credentials: deps.credentials`. The
store itself is already constructed in core/back/src/index.ts and
flows in via createSessionsModule. When no Cial-managed credential is
stored, resolveSpawnCredential still returns undefined and the
load-bearing fallback documented in anthropic-credentials.md kicks
in unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Whenever the Forgejo fork advanced after boot-fork.sh cloned it
(e.g. Pull Upstream from the dashboard, or a parallel cial-push from
another harness instance), /cial/data/repo lags the remote. cial-push
would then commit on top of the stale HEAD and the push was rejected
with `! [rejected] main -> main (fetch first)`.
The semantic insight: /cial/data/repo is a *derived view*. The actual
source of truth is /cial/core + /cial/platform — the running tree the
agent edits. Resetting the view to origin/main and rsyncing the
running tree on top produces a clean fast-forward commit that
captures exactly the diff between current fork HEAD and what's in the
running tree.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the cial-tenant side of Phase 1. Two boot modes:
CIAL_REPO_URL unset → bare supervisor against the baked image
(legacy / dev / fallback).
CIAL_REPO_URL set → boot-fork.sh clones the per-instance Forgejo
fork into /cial/data/repo, rsyncs into
/cial/core + /cial/platform, rebuilds, then
exec's supervisor.
cial-push.sh ships as a sibling helper invoked by the cial:self-edit
skill: rsyncs the running tree back into /cial/data/repo and pushes
to the fork via the deploy key boot-fork.sh provisioned. No-op when
CIAL_REPO_URL is unset, so safe to always run.
Both scripts are staged to /usr/local/bin so they survive boot-fork's
rsync over /cial/core. Adds git/rsync/openssh-client to the runtime
image. Skill files (.claude/skills.src/cial:self-edit/{restricted,unrestricted}.md)
need a matching workflow step 6 to invoke cial-push after build/restart;
left for a follow-up since they're gated as sensitive.
GitHub Action mirror (.github/workflows/forgejo-mirror.yml) is staged
locally but NOT committed — the OAuth token used here lacks the
`workflow` scope. Add it via the GitHub web UI or a PAT with workflow
scope.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PR #1 removed /cial/app (now lives in techforces-ai/cial-app), but
core/docker/Dockerfile still tried to COPY app ./app and rm -rf
/cial/app afterwards. The COPY would fail on a clean build of current
main with 'no such file or directory: ./app'.
This is the production Dockerfile cial-app spawns instance containers
from (cial-tenant:dev tag). Without this fix nobody can rebuild a
fresh tenant image post-split.
Standalone HTML diagram of the cial-app topology — three Fly apps
(cial-app, cial-router, cial-instances) plus Render Postgres — with
five interactive flows (sign up, create, open, suspend, delete) and
the API-keys reference table.
Served via an iframe-wrapper page at /architecture in platform-front.
Stragglers PR #1 didn't catch:
- core/scripts/smoke.mjs: drop @cial/app-api from the boot matrix and
appApi from the port table — the orchestrator lives in the sibling
cial-app repo now and is not part of this smoke.
- docs/architecture/core-vs-platform.md, docs/self-edit/unrestricted-mode.md:
rephrase 'app/' references to point at the sibling cial-app repo by
name instead of the deleted in-tree directory.
The multi-tenant SaaS control plane (api, orchestrator, router,
scheduler, docker/, docker-compose.yml) has moved to a sibling repo:
https://github.com/techforces-ai/cial-app
This repo is now the tenant runtime only — what runs INSIDE a Cial
container. The contract between the two repos is documented at
docs/architecture/sso-handoff.md and is the only thing both sides need
to agree on (HS256 JWT shape + shared CIAL_SSO_SECRET + the
/.cial/api/sso?token= endpoint).
Changes:
- Delete app/ (api, orchestrator, router, scheduler, docker/, README).
- Delete docker-compose.yml (App-side compose; tenant image is built
directly via core/docker/Dockerfile.dev).
- pnpm-workspace.yaml: drop the app/* glob.
- docs/file-structure.md: drop the app/ block, update the editable-mode
table, fix the legacy-layout row.
- docs/architecture/overview.md: drop app/ from the tree, add a
paragraph pointing at the sibling repo + the SSO contract.
History remains in this repo for reference: app/'s commits are still
reachable via `git log --all`. The cial-app repo carries the same
history rooted at its own top level (filter-repo'd from this one).
- Replace /admin pages with /, /signin, /signup, /dashboard.
- Add Google + GitHub OAuth via Better-Auth; remove first-user-wins gate.
- Quota-gate POST /api/admin/tenants (MAX_INSTANCES_PER_USER=3 default).
- Build chrome (AppHeader, ThemeToggle, UserMenu), auth (AuthCard,
SocialButtons, EmailPasswordForm), landing (CialBubble), dashboard
(InstanceList, InstanceCard, InstanceStateBadge, NewInstanceModal,
EmptyInstances) + types.
- Wire styles: vendor cial design tokens to app/api/src/styles/cial/
(314 lines, copied from core/ui/src/styles) + vendor CialBubble to
app/api/src/components/cial-bubble/. Drops every cross-package
workspace reference from app/ -> core/.
- Drop @cial/core-ui + @cial/protocol from app/api, orchestrator,
scheduler package.json. Revert ClassicCialBubble re-export from
core/ui/src/index.ts.
- Add scripts/check-cut.sh inventory script verifying the cut surface
between app/ and core/.
- Document the SSO handoff contract at
docs/architecture/sso-handoff.md so any external orchestrator can
bridge a user into a tenant.
- Fix stale comment in core/back self/index.ts.
Sets the stage for app/ to extract cleanly into a sibling cial-app
repo while leaving the per-tenant runtime self-contained.
Sidebar brand row now shows the running build's DD/MM HH:MM next to
the Cial logo (sourced from the system.version event already plumbed
through useDeployStatus) and a PanelLeftClose button to collapse the
panel. When collapsed, AppShell renders a floating hamburger button
at the top-left that re-opens it — mirrors the Old Cial pattern so
the workspace can claim the full width when desired.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
`session.messages[i]` is typed as `Message | undefined` under
noUncheckedIndexedAccess. Optional-chain the role check so the
agent's lastUserIdx scan type-checks again — the runtime semantics
are identical (loop only runs while `i` is in range).
Adds a transient `turnStartedAt` to the Session schema (not persisted —
mixed in by the back from ProcessManager). chat.send now broadcasts
`session.updated` with `status='busy'` + `turnStartedAt` to every tab,
and process-bridge's finalize broadcasts the idle flip with
`turnStartedAt: null`. session.list and session.rename payloads are
decorated so the field is always coherent.
Front maps it onto `streamingStartedAt`, so the sidebar timer in
`SessionRow` now ticks for any in-flight session — across tabs, after
reload, and even when the user is viewing a different session.
Persists a streaming-state snapshot to chat_session.streaming_state
every ~500 ms during a turn (text + thinking + collected tools), and
returns it from session.history (first page only) along with the
session status and turnStartedAt.
Front hydrates streamingText/Thinking/pendingTools/streamingStartedAt
from that payload when the session is busy, so a page reload mid-turn
no longer drops the active notch and pending tool calls — matches the
Old Cial reload behavior.
Splits POST /self/deploy (build only, blocking) from POST /self/restart
(response-first when bouncing core-back) so the agent gets a clean
"build done" signal and can decide when to apply.
Adds system.deploying / system.deploy_done / system.restarting /
system.version WS events broadcast to all sockets, plus
GET /api/v1/system/version for post-restart confirmation.
UI: DeployBadge component renders building/restarting/reconnecting/
reloading/failed stages above the composer; Sidebar Cial logo gradient
mirrors the same lifecycle. AppShell shares one SessionsClient so the
deploy state machine and the session store consume the same socket.
BuildRunner snapshots .next/static/chunks to chunks-prev/ before next
build; edge falls back to chunks-prev (rewrite or direct disk read) so
already-open tabs survive the build window and hard-reload only after
a confirmed version mismatch on reconnect.
Skills (cial:build, cial:restart) updated for the new contract.
`next build` always emits a production bundle, so serving it with
NODE_ENV=development at runtime mixes dev React with prod-built output
and surfaces as `useContext: null` during prerender (most visibly on
the synthetic `/global-error` route). Old-Cial parity is the dev tenant
running fully in production mode — `next dev`-style hot reload was
already gone after the watcher-removal commit, so the env-var was the
only remaining mismatch.
- supervisor.dev.ts: all four service children now spawn with
NODE_ENV=production; rationale moved to the file's docblock so it
isn't repeated four times.
- dev-entrypoint.sh: `pnpm turbo run build` runs with NODE_ENV=production
too. The Dockerfile's image-level NODE_ENV=development is left intact
so `pnpm install` keeps installing devDependencies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After a normal `cial:build` redeploy, `restart('all')` was bouncing the
edge supervisor too — `process.exit(0)` on the supervisor causes Docker
to recreate the entire container, killing the agent's in-flight session
mid-deploy.
`expandRestartTargets('all')` now returns only the four service children
that load rebuilt application code (platform-front, platform-back,
core-front, core-back). Edge bounces only on an explicit `scope: 'edge'`
request. Updated `expectedRestartCount`, the broadcast targets list, and
the `edgeRestart` flag accordingly, plus all referencing docs and the
unrestricted restart skill.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Old-Cial-style dev tenant. Source edits flow through cial:build →
BuildRunner → supervisor restart, the same path prod uses, instead of
relying on tsx watch / next dev --turbopack. Fixes the .next/ clobber
where a concurrent next build (scope=all) crashed the running next dev
core-front and tore the whole tenant down.
- dev-entrypoint.sh: pre-build the full workspace with `pnpm turbo run
build` (was just protocol+sdk+edge); export CIAL_MONOREPO_ROOT for
the next build --turbopack steps.
- supervisor.dev.ts: every child now spawns its built artefact
(node dist/index.js, next start). Updated header doc accordingly.
- next.config.ts (both): refreshed comments — turbopack.root is only
consulted at build time now, not by a running dev server.
- docs: updated dev-tenant, supervisor, and recipes to reflect the
built-artefact model and remove HMR claims.
Previous attempt added \`import path from 'node:path'\` + \`import.meta.url\`
to both next.config.ts files. Next 16's TS-config compiler emitted
CJS-style output (\`exports.default = ...\`), but the package.json
\`"type": "module"\` made Node load it as ESM, crashing on startup with:
Failed to load next.config.ts
ReferenceError: exports is not defined
at <unknown> (next.config.compiled.js:2:23)
Switch to a single env var (\`CIAL_MONOREPO_ROOT\`) read at runtime —
no imports beyond the type-only NextConfig. Same Turbopack-root pin
as before, but the config file stays a literal object that Next's
TS compiler can emit as a no-op ESM module either way.
\`CIAL_MONOREPO_ROOT\` is now propagated to core-front and platform-front
in supervisor.dev.ts (core-back already had it). Outside the dev
container the var is undefined and Turbopack falls back to lockfile
auto-detection, which works fine without bind-mount interference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Next 16's auto-detection of the workspace root (walking up looking for
pnpm-lock.yaml) misfires inside the dev container — when the anonymous
volume masking core/front/.next gets re-created on restart, Turbopack
mis-infers the project root as src/app/ and crashes with:
Error: Next.js inferred your workspace root, but it may not be correct.
We couldn't find the Next.js package (next/package.json) from the
project directory: /cial/core/front/src/app
Set turbopack.root explicitly to the monorepo root (two levels up from
each next.config.ts) so detection can't go sideways. Recommended fix
per the error message itself.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update API docs, recipes, design doc, deploy-pipeline architecture,
and deploy-logs ops doc to match the new synchronous behaviour
(commit 8505981). The endpoint now returns 200/500 with status,
durationMs, exitCode, errorSummary, and an inline logTail (last
~8KB) — no polling, no companion GET endpoint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Old behaviour returned 202 with a deployId and expected the agent to
poll GET /deploy/:id — but that route lives on the Better-Auth-gated
deploy module, so localhost curl from inside the container got 401 on
every poll. The agent had no terminal signal and no log visibility,
matching the symptoms Eliot observed (stale-looking session, repeated
401s in core-back logs, no build output).
Mirror old-Cial's ergonomics: one synchronous request, blocks until
the build (and post-build restart) reach a terminal state, returns
{ ok, status, durationMs, exitCode, errorSummary, logTail } with HTTP
200 on success or 500 on failure. logTail is the last 8KB of the
deploy log file so the agent has the failure context inline without
needing a second round-trip.
- DeployService.waitForDone(deployId, timeoutMs): EventEmitter-based
promise that resolves on the next 'done'/'cancelled' event for the
given deployId, or immediately if the row is already terminal.
- DeployService.getLogPath(deployId) + DeployRepository.getLogPath:
surface the persisted log_path for tail reading.
- self/router.ts: await waitForDone, read log tail, respond once.
- cial:build skills (restricted + unrestricted): drop the polling
loop, document the synchronous response shape.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
By default tsc writes the incremental build info to
<package>/tsconfig.tsbuildinfo (next to the tsconfig). In restricted
dev mode /cial/core is RO, so this fails with EROFS — and only the
dist/ subdir is shadow-mounted as writable.
Setting tsBuildInfoFile to "dist/.tsbuildinfo" puts it inside the
writable shadow, matching the convention edge/tsconfig.json already
follows. No behavior change in unrestricted mode or prod (rw paths).
Restricted mode mounts /cial/core as RO, but core-front (Next.js
Turbopack) needs to write a lockfile to .next/, and the entrypoint's
pre-build writes tsc output to core/{protocol,sdk,edge}/dist. Without
shadows, the first restricted run after --reset crashes core-front
with EROFS on the lockfile.
Add anonymous volumes for the four writable paths under core/ when
--unrestricted is not set. Writes land in the ephemeral container
layer; the RO trust boundary on source files is preserved.
This was latent until the recent --reset flow exposed it (cached
.next/ + dist/ from prior unrestricted runs were masking the issue).
Skills now templated under .claude/skills.src/<name>/{restricted,
unrestricted}.md and rendered to .claude/skills/<name>/SKILL.md by
core/scripts/render-skills.mjs based on the active mode. The agent
only sees the variant matching its actual permissions, so it no
longer has to branch on CIAL_UNRESTRICTED on every invocation.
- Dev: dev-entrypoint.sh re-renders on container start; dev-tenant.mjs
masks /cial/.claude/skills with an anonymous volume so writes don't
leak into the host repo bind mount.
- Prod: Dockerfile builder stage renders once (always restricted; prod
doesn't expose --unrestricted).
- Static .claude/skills/ deleted from the repo and gitignored.
- docs/file-structure.md updated with the new layout.
Audit pass over docs/ + adjacent code following the cial-* → core/platform/app
layout consolidation.
Bug fix:
- core/back BuildRunner ALL_FILTERS referenced @cial/core-back and
@cial/core-front, which no longer exist (the packages are @cial/back +
@cial/front). Self-edit deploys with scope=all would have silently
skipped those packages. Filters corrected.
Docs aligned with reality:
- docs/README.md — promotes file-structure.md to the start-here entry.
- architecture/dev-tenant.md — full rewrite: paths now /cial/* throughout,
documents the read-only :ro overlay of /cial/core, the new
--config.confirm-modules-purge=false install flag, the symlink dance for
project skills, and the agent's cwd=/cial + HOME=/cial/data/home setup.
- architecture/deploy-pipeline.md — package-name fix for ALL_FILTERS.
- architecture/core-vs-platform.md — package-name fix for the build list.
- ops/supervisor.md — drops stale "added in Phase 7" annotation.
- ops/deploy-logs.md — example log line uses @cial/back.
- self-edit/recipes.md — protocol path and dependency chain naming.
- design/self-edit-unrestricted.md — banner clarifying it's the original
design record (pre-rename) so an agent doesn't follow stale paths from it.
Tiny code touch:
- core/edge/src/supervisor.dev.ts — comment on CIAL_MONOREPO_ROOT no longer
contradicts itself ("not /cial" → "the bind-mounted repo at /cial").
Build verified: turbo run build for @cial/back still passes (cache miss
re-executed cleanly with the updated runner.ts).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After the cial-* → core/platform/app rename, the named modules volume
from any previous container run no longer matches the lockfile layout.
pnpm 9 detects this and prompts "remove and reinstall? (Y/n)" — which
stalls forever because the entrypoint runs without a TTY, leaving
node_modules empty and the next pre-build step crashing with
"Cannot find module 'zod'" (and friends).
Pass --config.confirm-modules-purge=false so pnpm just wipes and
reinstalls without asking. No behavior change on a fresh volume.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reorganize the dev/prod tenant container so the agent runs in the monorepo
root with a clear, semantic directory tree:
/cial/core/ — runtime (back, front, edge, ui, sdk, protocol, scripts,
docker). Locked down to the cial linux user (mode 0700
in prod; :ro bind mount in restricted dev).
/cial/platform/ — agent-editable surface (back, front).
/cial/app/ — App control plane sources, present in workspace but
never built or run inside the tenant container.
/cial/docs/ — architecture + ops reference.
/cial/.claude/ — project skills/agents/commands (symlinked into the
harness HOME by the dev entrypoint).
/cial/data/ — persistent state (sqlite, deploy-logs, agent home).
Concrete changes:
- git mv cial-core → core, cial-platform → platform, cial-app → app,
scripts → core/scripts.
- pnpm-workspace.yaml: packages now core/*, platform/*, app/*.
- Bulk path rewrites across 250+ source / docker / docs files.
- core/scripts/dev-tenant.mjs: ROOT path fix, rw mount of repo + ro
overlay of /cial/core when --unrestricted is not set (FS-level
trust boundary, defense in depth).
- core/edge/src/supervisor.{ts,dev.ts}: cwd + CLAUDE_HOME relocated to
/cial/data/home; agent runs from /cial root so skill discovery picks
up /cial/.claude/skills automatically.
- core/back providers/claude.ts: HOME defaults to /cial/data/home, cwd
defaults to /cial.
- core/docker/{Dockerfile,Dockerfile.dev,dev-entrypoint.sh}: COPY +
WORKDIR + ENTRYPOINT updated; .claude → harness symlink.
- app/docker/{Dockerfile,Dockerfile.router}: COPY core, COPY app
(instead of cial-core / cial-app).
- New docs/file-structure.md — single canonical map of the runtime
layout. cial:self-edit SKILL.md mandates reading it first.
- cial:build SKILL.md: scope notes updated to platform/* and core/*.
- root package.json: smoke / dev:tenant scripts now under core/scripts/.
- core/scripts/smoke.mjs: cial-core.db → cial.db.
Externals preserved as-is by intent:
- JWT issuer string 'cial-app' in core/back/src/modules/sso/index.ts +
app/api/src/lib/sso.ts is an external contract — NOT renamed.
- @cial/back / @cial/edge / @cial/protocol / @cial/sdk / @cial/front
package names kept stable to minimize blast radius.
Verified:
- pnpm install --prod=false → ok
- turbo run build for protocol, sdk, back, edge, front, platform-back,
platform-front → all 7 successful (Next builds + tsc clean).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Claude harness spawned by sessions/process/providers/claude.ts runs with
HOME=$CLAUDE_HOME (= /var/lib/cial/claude-home) and cwd=$HOME by default.
Slash-command discovery walks up from cwd looking for .claude/skills/, but
nothing under /var/lib/cial/... sees /workspace/.claude/. Result: skills
shipped in the repo (cial:self-edit, cial:build, cial:restart) appeared as
"Unknown command".
Entrypoint now symlinks the bind-mounted project .claude/* into the harness
HOME so they're discovered at the user level. Symlinks (not copies) so live
edits via the bind mount take effect on next session spawn without rebuild.
Adds the two utility skills the agent invokes from the master cial:self-edit
flow: cial:build POSTs /api/v1/self/deploy and polls until terminal,
cial:restart POSTs /api/v1/self/restart and handles the edge-bounce case.
Both target core-back directly on :4000 because edge only proxies /.cial/api/*,
not /api/v1/*.
Widens DeployStartEvent.targets, DeployRestartStartEvent.service, and
DeployRestartDoneEvent.service in @cial/protocol to include core-front,
core-back, and edge so the broadcast events emitted during scope=all
deploys typecheck. Trust enforcement still lives in the supervisor, not
the schema.
Adds POST /api/v1/self/deploy and /api/v1/self/restart on cial-core for
agent-initiated builds and restarts. Introduces CIAL_UNRESTRICTED=1
(opt-in via `pnpm dev:tenant --unrestricted`) which widens the trust
boundary so the agent can rebuild and restart core+sdk+protocol+edge
in addition to platform.
Trust boundary enforced at three layers:
- BuildRunner pnpm filter (platform vs all)
- Supervisor IPC RESTARTABLE_SERVICES set
- localhost-only middleware on /api/v1/self/*
Edge restart uses 50ms-deferred process.exit so Docker restart-policy
bounces the container. Dev supervisor gained the IPC server it was
silently missing.
Ships docs/ tree (architecture, self-edit, ui, ops) and the
cial:self-edit Claude skill, both copied into the dev+prod images so
the in-container agent can read them before editing.
Sidebar:
- SessionRow → rounded-xl + py-1.5 + mb-0.5; 3px indigo active bar with glow
- Port SidebarTimer (live MM:SS in indigo busy pill) reading streamingStartedAt
- Replace 7×7 + icon with full-width "New session" CTA; drop Sessions caption
Chat:
- MessageList: drop hardcoded max-w-5xl so messages span full pane
- StatsFooter: surface costUsd via new fmtCost (Coins icon)
- MessageRow: hover-revealed Copy button next to AssistantHeader (Copy → Check)
- InputBar: drop "Cial can make mistakes…" disclaimer (not in old)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a Unix-socket protocol (default /run/cial-supervisor.sock, mode 0660
cial:cial) so Core Back can ask the supervisor to bounce platform-front /
platform-back without taking the container down. Only platform-* children
are restartable; core-back / core-front / edge are rejected. Exit handler
distinguishes requested restarts (respawn) from crashes (existing
crash-the-container behaviour preserved).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the always-visible ToolList stack with a single
"Process (N steps)" pill below each finished assistant message.
Click to expand a panel listing reasoning + every tool call (with
input/output) + response, mirroring the legacy Cial UX.
Multi-turn answers show "(X turns, X steps)" and group by turn.
SDK re-exports SubTurn so the UI can type the metadata.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- getStatusLabel + ActiveNotch now filter pendingTools by output==null
before picking the "active" call, so the notch correctly flips to
Writing once tools settle (was sticking on the last tool's name).
- Streaming timeline moved inside StreamingBubble / ThinkingBubble,
rendered between AssistantHeader and the body — matches legacy Cial
layout where tools sit under "Cial" and above the answer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- StreamingBubble: paced reveal now varies 0.6×–1.4× per frame so the
typewriter feels organic instead of metronomic.
- StreamingTimeline: subtle vertical dots+line of pending/done tool
calls (label · check · duration), shown above the streaming
response. Adds a "Writing response" step once tools settle. Last 3
visible by default; older steps collapse behind "+ N earlier
steps". Replaces the heavy ToolList during streaming — full
ToolPills stay for completed messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Decouples visible text from upstream chunk size. A rAF loop reveals
characters at ~220 chars/sec regardless of how big each backend
delivery is, with a burst-catch-up when the backlog grows past 600
chars so long answers don't fall behind. Resets on new turn.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drops TAIL_POLL_MS from 500ms to 50ms (env-overridable) so text
flushes ~20 Hz instead of 2 Hz. fs.statSync + readSync on the
per-turn log is negligible. StreamingBubble now memoises
renderMarkdown(text) so the tighter tick rate doesn't reparse the
buffer on every parent re-render.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Picker now lives outside the composer shell as its own subtle pill row,
solid background (not transparent) so it reads as a real control.
Chat content widens to max-w-5xl while the input stays max-w-3xl
centered, matching the legacy Cial layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Per platform.claude.com (models overview) and code.claude.com
(--effort flag in CLI reference):
- effort enum: drop 'normal' (not a CLI value); add 'low', 'medium',
'max' so the wire matches the official 5-level set
('low' | 'medium' | 'high' | 'xhigh' | 'max')
- models.ts: surface all 5 effort options in the picker, strongest
first; default stays 'xhigh'
- models.ts: add legacy models (Opus 4.6, Sonnet 4.5, Opus 4.5,
Opus 4.1) as picker options tagged legacy
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>