Artur Mukhamadiev 6743c358f5 diagnostics: explain why a run failed instead of just "execution_failed"
Every internal failure was mapped to a fixed sanitized code before anything
recorded the cause, so an intermittent execution_failed was undebuggable: an
exhausted model-call budget, a model turn with no text, and a genuine crash all
looked identical.

The bridge now writes one bounded line to container stderr on failure with the
code, the internal reason, the state and both call counters, and the failure
sites pass a reason (budget exhausted, empty final answer with its content block
types, token counts against the limits). The wire error is unchanged.

The backend logs the agent's terminal code together with its own call counters,
and the sanitized tail of container stderr rather than only its byte count.
deploy/logging.json gives every logger a timestamp (uvicorn's default config
leaves non-uvicorn loggers on logging's fallback handler); override with
CONFLUENCE_WEB_LOG_CONFIG.
2026-09-16 19:48:50 +03:00
..

Confluence pi runtime

This package wraps the unmodified pi SDK. It registers native Confluence tools and the SDK's shell/read/write/edit tools, and supplies a custom model stream function over revision-1 NDJSON. There are no provider credentials, remote Confluence clients, CLI stdout scraping, or modifications to pi dependencies.

From the repository root:

npm --prefix agent ci --ignore-scripts
npm --prefix agent run build
npm --prefix agent test
npm --prefix agent run proof

The host proof uses an explicit development supervisor, fresh temporary workspace and home, and a deterministic model/backend substitute. It runs real local commands. The supervisor signals only its own descendants and adopts orphaned children with Linux PR_SET_CHILD_SUBREAPER. Ordinary host npm start refuses to launch: production supervision requires PID 1 in a private container PID namespace. Do not add Docker --init or share the host PID namespace.

Build and exercise the image against a rootless Docker daemon:

docker build -t confluence-pi-agent:rev1 agent
node agent/dist/dev/fake-backend.js --image confluence-pi-agent:rev1
node agent/dist/dev/image-checks.js
node agent/dist/dev/boundary-checks.js

The image-check suite includes a real, unchanged 180-second missing-start test. It prints progress every 30 seconds. Boundary checks generate large payloads on the host and send them to a runtime constrained to 1 GiB and one CPU. These test commands need access to Docker and local supervisor sockets; restrictive execution sandboxes can prevent them from running.

dev/fake-backend.ts is an independent host peer that uses the public wire contract. Add --mode=... for no-artifacts, empty-file, skip, delayed, background, spaces, confluence-error, upstream-failure, bad-reply, duplicate, eof-model, eof-collection, eof-begin, eof-end, context-limit, output-limit, zero-text, failure-during-collection, stalled-model, or stalled-collection. isolation and stop-bridge require --image. --transcript records dummy wire traffic.

The default entrypoint is /usr/bin/python3 /opt/agent/supervisor; user/group 10001:10001, cwd /work, HOME /home/agent. Mount fresh owned tmpfs filesystems at /work (256 MiB), /tmp (64 MiB), and /home/agent (32 MiB). Supply no bind mounts or secret environment variables. dev/fake-backend.ts contains the complete launch flags: read-only root, no network, no privileges/capabilities, default seccomp, private namespaces, 1 GiB memory/no extra swap, 128 PIDs, one CPU, no TTY, and no Docker logging. The backend owns launch, attachment, kill/removal, host artifact storage, provider configuration, and upstream HTTP.

Shell tools use /bin/bash --noprofile --norc, a 30-second maximum per command, and 1 MiB captured output per invocation. Excess output terminates the command and becomes a model-visible tool failure; pi retains its normal 50 KiB/2000-line presentation truncation and temporary output file. Native read/edit operations require regular files up to 16 MiB; use shell ranges for larger files. The shared model boundary supports text and tools, so read never creates image attachments. Tool results and input arguments also obey shared serialized protocol limits.

Artifacts are collected only after the supervisor has killed and reaped local descendants. Directory traversal uses verified directory descriptors through Linux /proc/self/fd, with no-follow opens at each component. File descriptors remain open through transfer; link/type/inode/size/time checks precede and follow bounded reads. Names and Unicode-normalized duplicates are validated, file/count/ aggregate limits produce bounded warnings, and a traversal budget prevents unbounded directory enumeration. The backend still receives untrusted bytes and must independently validate paths, sizes, storage and attachment delivery.

Completion is committed only after the complete frame finishes writing. The bridge timer is then cleared and no further terminal error is emitted; the supervisor's independent lifetime remains armed. Failure exit allows up to one second for an active NDJSON frame and the terminal error to drain. Not-yet-started frames are cancelled, preserving complete lines rather than interleaving error bytes. If the stream breaks or remains blocked, nonzero exit/EOF conveys failure. Repeated warnings include occurrence counts; overflow is summarized within 100 entries.