confluence_web/agent/README.md

4.4 KiB

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.