Response to incident WA-2026-10-27 (the std::vector max_size() crash). Deep research archived under research/safe-cpp-practices (8 Tier 1, 37 Tier 2, 2 conclusions) on production-safe C++ that keeps development speed: OpenSSF compiler hardening, the C++ Core Guidelines bounds profile, integer-overflow practice, libFuzzer/sanitizer CI, the sanitizer overhead ladder, clang-tidy ratchets. The research prescribed an ASan+UBSan+_GLIBCXX_ASSERTIONS build of the parsers. Standing it up immediately caught a second, independent undefined-behavior bug that two manual reviews had missed: a protobuf varint loop that shifted by up to 70 bits (shift >= 64 is UB) on malformed input. Fixed and guarded by a deterministic test and the sanitizer net. - src/whatsapp/wa_proto.cpp: guard the wire-0 varint shift (the UB). - tests/whatsapp_test.cpp: add an overlong-varint hostile case. - tools/san-whatsapp.sh: the standing sanitizer net for detz::wa parsers. - WaClient frame capture: a frame that fails to decode/dispatch has its exact bytes recorded to <home>/whatsapp/last-error.log (bounded), so the still unreproduced live <iq> failure becomes a one-paste deterministic fix. Wired from the wizard and the whatsapp_send tool. - docs/cpp-safety.md: the project-wide playbook and design decisions (four layers: overflow-safe checked-reader parsing, cheap release hardening, the sanitizer test net, capture). docs/whatsapp.md section 13.6 records the follow-up and the honest open item. AGENTS.md makes it a standing rule; cap.md and mcp.md inherit it for their wire parsers. Verified: build, whatsapp_test (incl. hostile+fuzz), make lint, and tools/san-whatsapp.sh all green; research audit PASS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
6.2 KiB
Markdown
104 lines
6.2 KiB
Markdown
# cap (taicpp)
|
|
|
|
Port of the CAP section of `../tai.py` (`CAP_VERSION` ... `TAI_CAP_MANIFEST`),
|
|
normative text in `../cap.md`. Namespace `detz::cap`, sources in `src/cap/`,
|
|
one class per `.h`/`.cpp` pair. The C++ names are the Python names in
|
|
camelCase; behavior is verified by running both programs (see Verification).
|
|
|
|
**Wire parsing is untrusted input**: every byte here comes off a socket, so the parsers follow [cpp-safety.md](cpp-safety.md) (overflow-safe size checks, unsigned range-checked lengths, guarded shifts, no exception escaping the read loop, hostile-input tests under sanitizers). We do not accept regressions in that.
|
|
|
|
## Spec delta (this milestone)
|
|
|
|
- Real `TAI_CAP_MANIFEST` replaces the one-line placeholder
|
|
(`CapAgentManifest::TAI_CAP_MANIFEST`, `--cap-manifest` prints it plus
|
|
the `print()` newline; byte-identical to `tai.py --cap-manifest`).
|
|
- All Cap classes ported: events, errors, bindings, manifest, registry,
|
|
session config, stream-json, speak session, the four drivers, answerer,
|
|
session, fleet, CLI. The old `Orchestrator` grant/check placeholder is gone.
|
|
- Layer `wire_*` (namespace `detz::wire`) supplies what Python's stdlib gave
|
|
tai.py: `Json` (json), `WireToml` (tomllib), `WireRegex` (re), `WireProc` /
|
|
`WirePty` (subprocess, PtyProc), `WireHttp` (urllib, http.client, http.server),
|
|
`WireArgParse` (argparse), `WireShlex`, `WireDifflib`, `WireHash`, `WireText`,
|
|
`WireOs`.
|
|
|
|
## Parity table (Python -> C++)
|
|
|
|
| Python | C++ |
|
|
|---|---|
|
|
| `CAP_VERSION` `CAP_MANIFEST_SCHEMA` `CAP_PROFILE_CODING` `CAP_MANIFEST_REVISION` `CAP_PROBE_TIMEOUT` `CAP_ERROR_CODES` `CAP_ERROR_STRINGS` `CAP_SESSION_CONFIG_ENV` `CAP_SPEAK_MAX_TURNS` `CAP_SPEAK_DIFF_MAX` `CAP_PTY_SETTLE_MS` `CAP_PTY_IDLE_MS` `CAP_PTY_SUBMIT_GAP_MS` `CAP_PTY_RESUBMITS_MAX` `CAP_PTY_SCROLLBACK` `CAP_PTY_INIT_TIMEOUT` `CAP_STREAM_LINE_MAX` `CAP_STREAM_STDERR_KEEP` `CAP_SESSION_POLL` `CAP_TURN_TIMEOUT` `CAP_WORKTREE_DIR` `CAP_ROUTE_QUEUE_MAX` `CAP_FLEET_DIFF_MAX` `CAP_MENTION_RE` `CAP_ACP_VERSION` `CAP_ACP_TERMINALS_MAX` `CAP_ACP_TERM_BUF` `CAP_ACP_TERM_WAIT_MAX` `CAP_ACP_FS_MAX` `CAP_A2A_TIMEOUT` `CAP_ANSWER_TIMEOUT` `CAP_ANSWER_PARALLEL` `CAP_ANSWER_MARKER` `CAP_ANSWER_RISKS` | `cap_constants.h` (same names) |
|
|
| `CAP_ERROR_SYMBOLS` | `capErrorNumeric()` in `cap_error.h` |
|
|
| `TAI_CAP_MANIFEST` | `CapAgentManifest::TAI_CAP_MANIFEST()` |
|
|
| `CapError` | `cap_error.h` |
|
|
| `CapEvent` | `CapEvent` (`KINDS`, `DIRECTIONS`, `REQUIRED`, `ENUMS`, `make`, `validate`, `direction`) |
|
|
| `CapBinding` | `CapBinding` (`ORDER`, `UNSUPPORTED`, `declared`, `select`) |
|
|
| `CapManifest` | `CapManifest` |
|
|
| `CapRegistry` | `CapRegistry` |
|
|
| `CapSessionConfig` | `CapSessionConfig` |
|
|
| `CapStreamJson` | `CapStreamJson` |
|
|
| `CapSpeakSession`, `cap_interrupt`, `cap_speak_main` | `CapSpeakSession`, `CapInterrupt`, `CapSpeakMain` |
|
|
| `CapDriver` | `CapDriver` (`CapDriver::create` in `cap_driver_factory.cpp`) |
|
|
| `CapPtyDriver` | `CapPtyDriver` over `WirePty` and `CapVtScreen` (port of `TuiVtScreen`, `CapScreen` seam) |
|
|
| `CapPipeReader`, `cap_proc_kill`, `cap_proc_reap`, `cap_stderr_drain` | `CapPipeReader`, `CapProcUtil`, `CapStderrDrain` |
|
|
| `CapStreamDriver` `CapAcpDriver` `CapA2aDriver` | same names |
|
|
| `CapAnswerer` | `CapAnswerer` |
|
|
| `CapTurnResult` `CapSession` `CapFleet` | same names |
|
|
| `cap_cli_main` `cap_cli_help` `cap_resolve_main` `cap_chat_main` `cap_run_main` `cap_fleet_plan` `cap_print_result` `cap_build_answerer` `cap_answer_flags` | `CapCli` static methods |
|
|
|
|
## Event kinds
|
|
|
|
`cap.session.ready`, `cap.text_chunk`, `cap.tool_call.start`,
|
|
`cap.tool_call.delta`, `cap.tool_call.end`, `cap.plan`, `cap.thought`,
|
|
`cap.ask_user`, `cap.permission.request`, `cap.usage`, `cap.error`,
|
|
`cap.artifact`, `cap.fs.change`, `cap.pty.raw_bytes`,
|
|
`cap.user_input.inject`, `cap.cancel`, `cap.session.config`,
|
|
`cap.ask_user.answer`, `cap.permission.response`.
|
|
|
|
## Errors
|
|
|
|
Symbols: `cap_manifest_missing`, `cap_session_config_missing`,
|
|
`cap_unsupported_capability`, `cap_budget_exceeded`, `cap_invalid_answer`,
|
|
`cap_unknown_permission`, `cap_queued_input_unsupported`,
|
|
`cap_cancel_unsupported`, `cap_binding_unavailable`,
|
|
`cap_profile_unsupported`, `cap_scope_violation`, `cap_path_not_found`,
|
|
`cap_io_error`, `cap_terminal_unavailable`, `cap_terminal_killed`.
|
|
String codes: `rate_limit`, `auth_error`, `tool_timeout`, `context_overflow`,
|
|
`model_unavailable`, `pty_died`, `parse_failed`, `git_dirty`, `test_failed`,
|
|
`merge_conflict`, `language_server_down`.
|
|
|
|
## Seams (host-provided)
|
|
|
|
`CapAgentPort` / `CapSpeakHost` (the agent for `--cap-speak`), `WireLog`
|
|
(debug log sink), `WireApp::setVersion` (VERSION on the wire),
|
|
`CapPtyDriver::setScreenFactory` (swap the VT screen). Details and the exact
|
|
hooks: `docs/wiring/capmcp.md`.
|
|
|
|
## Differences from Python (all declared)
|
|
|
|
- `CapAnswerer` runs `[binary, --auto, prompt]` directly; Python ran
|
|
`[sys.executable, tai.py, ...]`. Default binary is `/proc/self/exe`.
|
|
- `CapVtScreen` parses SGR but does not store colors/attributes (text-only
|
|
consumer) and keeps no scrollback history.
|
|
- Regex: ECMAScript engine with Python syntax translation. Python-only syntax
|
|
beyond named groups, `\A`, `\Z`, `$`, bare `{`/`}`, leading `(?i)` is not
|
|
translated; compile error texts differ from `re.error`.
|
|
- argparse help is plain (no 3.14 color).
|
|
- Dates in TOML come back as strings.
|
|
- Terminal `terminal/output` keeps the `read(65536)` blocking-chunk behavior.
|
|
- Signals: SIGINT/SIGTERM set a flag polled by waits (`CapInterrupt`).
|
|
|
|
## Agents, models and `@agent` mentions
|
|
|
|
taicpp lists the installed CAP agents, discovers their models (manifest `[models]` list, optional
|
|
`--cap-models` probe), and lets a message start with `@agent`, `@agent:model` or `@model`, matched fuzzily and
|
|
run in a supervised panel on the multiplexer screen. Everything about it is in
|
|
[cap-agents.md](cap-agents.md); the matching in [fuzzy.md](fuzzy.md); the panels, supervision and result
|
|
collection in [orchestration.md](orchestration.md). CAP itself (events, bindings, manifest, fleet) is
|
|
unchanged by this and stays specified on this page.
|
|
|
|
## Verification
|
|
|
|
`tests/capmcp_diff.py` (JSON, TOML, str(), float repr, regex vs CPython),
|
|
`tests/capmcp_cli_diff.py` (`--cap-manifest`, `--cap resolve|chat|run` incl.
|
|
stream-json, PTY, ACP and A2A fake agents, argparse errors at four widths vs
|
|
`tai.py`), `tests/capmcp_unit.cpp` (tables and drift check).
|