package cabal
Install
dune-project
Dependency
Authors
Maintainers
Sources
sha256=f9dc2180e4ceba25c2d2342d0e4ad84e3e3998ec247d71b1cea9e0e069b41cae
sha512=3a23ad08d81e85cd35b84e558a12c139645ffb1befa8de685880de16a69c0723df2dd2dbf48a1f6b2d54c18861963fae6d7a326f15b1767252ae698e7c013bdf
doc/CHANGELOG.html
Changelog
All notable changes to Cabal are documented in this file.
The format is based on Keep a Changelog; this project does not yet emit semantic version tags — entries here are grouped by the date a change merged to main.
Unreleased
Added
Per-backend
modelsenumeration (non-breaking, additive). Every adapter implementingAgentic_backend.Snow exposes amodels : string listmember listing the model ids it accepts via its CLI's--model(or equivalent) flag. The data is surfaced two ways:Agentic_backend.models : t -> string listfor callers that already hold a backend handle, andRegistry.list_models : string -> string list optionfor callers that only know the backend id.Yaml_adapterconfig gains amodels : string listfield populated from a top-levelmodels:sequence in the YAML file; missing or malformed sequences fall back to[](the documented "let the adapter pick" sentinel). Existing call sites that pass~model:NonetoBackend_types.make_task_speccontinue to work unchanged.The same surface now has a dynamic-probe layer on top:
Agentic_backend.Sgained an optionalmodels_probe : (sw:_ -> env:_ -> (string list, string) result) option. WhenAdapter_loader.register_allis called with~swand~env, each registered backend's probe is invoked once under exception protection; a non-emptyOkresult replaces the static list for that backend, while anyError _, exception, orOk []cleanly falls back to the static declaration. The resolved view is exposed viaRegistry.resolved_models : string -> (string list * Registry.models_source) option, withmodels_source = Probe | Static | Hybriddocumenting the origin (Hybridis reserved and not yet emitted).Registry.list_modelskeeps its existing return type and is nowOption.map fst @@ resolved_models. Among the built-in adapters onlyopencodeships a probe today (parsingopencode models);claude-code,codex,gemini-cli, andcopilot-clisetmodels_probe = Noneuntil their upstream CLIs expose a non-interactive listing command.Backend_types.task_result.agent_text(non-breaking, additive). Adapters now populate a newagent_text : stringfield with the agent's final reply, extracted from each CLI's native output format (claude-code JSON envelope, codex JSONL, gemini stream-json, opencode JSON events, copilot plain text).stdoutcontinues to carry the raw bytes produced by the CLI so callers that need backend-specific post-processing retain access. Host applications consuming the response text should switch fromYojson.Safe.from_string result.stdout(or equivalent CLI-specific parsing) toresult.agent_textso they stop having to know which CLI ran.Backend_types.make_task_resultgains a?agent_text:stringoptional argument that defaults to"", so existing call sites continue to compile unchanged.Backend_completer.makenow prefersagent_textoverstdoutfor the completion text (with astdoutfallback for backends that have not yet populatedagent_text).
Changed
- Adapter override directory renamed from
.epure/to.cabal/(breaking).Adapter_loader.register_allnow reads user-global overrides from~/.cabal/adapters/*.yamland project-local overrides from<project_dir>/.cabal/adapters/*.yaml. Host applications that previously dropped YAML overrides under~/.epure/adapters/or<project>/.epure/adapters/must move them to the new locations. Built-in adapters compiled into the library are unaffected. The rename makes the override path host-neutral now that cabal is consumed beyond Épure. - Default
managed_namespaceis now host-neutral (breaking).Backend_types.default_managed_namespaceis now{ id = "cabal"; display_name = "Cabal"; config_dir = ".cabal/backend-config" }instead of{ id = "epure"; display_name = "Epure"; config_dir = ".epure/backend-config" }. Effects on freshly written artifacts: attribution lines change toGenerated by Cabal — do not edit manually, managed comment markers becomecabal-managed/cabal-hash, host-owned config files land under.cabal/backend-config/, sidecar files use the.cabal-meta.jsonsuffix, and--forcebackups use the.cabal-backupsuffix. Existing on-disk files with legacyepure-*markers and attribution remain readable:Backend_config_writerrecognises the legacy namespace as a fallback and migrates legacy JSON metadata keys (_epure_attribution,_epure-managed,_epure-hash) to the current namespace's headers on next write. Hosts that depended on the old default must construct an explicitBackend_types.managed_namespaceand thread it throughmake_task_spec/setup_project_config_with_options/generate_all_with_options. - Cabal-internal environment variables renamed to drop the
EPURE_prefix.CABAL_BACKEND_FAST/CABAL_BACKEND_SMARTare the newBackend_tier.initlookups;CABAL_VALIDATOR_PARALLEL_MAX_MEMconfiguresResource_guardian's memory threshold; andCABAL_MOCK_AGENT_FIXTURESis the newMock_agentfixtures path. The oldEPURE_*names continue to work as deprecated aliases when theirCABAL_*counterpart is unset, so existing host-side wrappers are not broken — but new code and CI should set theCABAL_*names. - Standalone-repo
README.mdno longer documentsEPURE_NO_COMMIT_CHECK=1 dune ...invocations, which are an Épure-monorepo-specific escape hatch and belong in the monorepo's own docs. Architecture diagram and ownership boundaries reworded so the host isBountyNexus, Épure, tests, or another OCaml app, not "Épure" by default. .mlidocstrings generalised:Backend_config_gen.mli,Backend_config_writer.mli,Backend_types.mli,Mock_agent.mli, andSession_event_logmodule-level docs now describe behaviour as "the host application does X" rather than "Épure does X" when the behaviour is host-neutral. TheEpure_ownedownership constructor is retained under its current name for source compatibility, but its docstring clarifies that it denotes a host-owned artifact under the active managed namespace'sconfig_dir. User-facing strings written into generated Copilot CLI instructions and OpenCode comments now read "host application" instead of "Epure".
Security
- Session NDJSON files now created with mode
0o600(previously0o640, group-readable). Post-redaction backend events could leak to any user in the file owner's group on shared/CI machines. - Redaction policy hardened. The
sensitive_fieldsset now coversenvironment,env,env_vars,oauth_token/oauth/jwt/bearer,cookie/set_cookie/session,connection_string/dsn,signature,client_secret, and AWS / GCP credential field names. Pattern-based fallbacks now redact URLs containing embeddeduser:password@credentials and JWT-shaped tokens in any field. urlanderrorremoved fromsafe_string_fields. Both legitimately carry credentials (postgres://user:pw@host) or echo them in error messages; the field-name allowlist was masking those leaks.
Fixed
- Build break: the
mliatresource_guardian.mli:127and three sites inbackend_process.mlused the eio-1.x parameterised resource types (_ Eio.Time.clock,_ Eio.Flow.source,_ Eio.Flow.sink). The library targets eio 0.11 where these are zero-arity. Drop the_and pin eio to>= 0.11 & < 1.0indune-project. - File-descriptor leak in
Backend_config_writer.read_file_opt: the channel was only closed on the success path. Now usesFun.protect. - Race condition in
Resource_guardianPID set:register_pid/unregister_pidmutated amutable int listwithout synchronisation. Now stored inint list Atomic.twith a CAS-retry helper. - Adapter id drift: bundled YAML adapters at
src/adapters/{gemini,copilot}.yamlregistered asgemini/copilot, while the static descriptors, the hand-written*_cli.mlmodules, andbackend_config_genall use the canonicalgemini-cli/copilot-cli.Registry.get "gemini-cli"returnedNone. Both YAMLs renamed.
Added
- Version / availability probes are now timed.
Backend_process.capture_version_outputandBackend_process.check_availabletake?timeout_seconds(default 5.0) and return promptly on a hung binary instead of freezing registry initialisation indefinitely. Resource_guardian.registered_pidsread-only accessor for diagnostics.Backend_types.validated_namespace— opt-inprivate managed_namespacewrapper.validate_namespace : managed_namespace -> (validated_namespace, string) resultis the only constructor; hosts can plumb the type through their own artifact paths to make namespace validation a compile-time obligation.Backend_json_helpersmodule sharing thejson_string_mapcodec previously duplicated in three*_cli.mladapters.Backend_config_cleanupmodule: text-cleanup helpers (remove_dangling_commas_before_closers,strip_managed_mcp_block) extracted fromBackend_config_writer.- CI matrix now covers
ubuntu-latest+macos-lateston OCaml 5.3.0, plus aubuntu-latestleg on 5.2.0. Addsdune build @docto every leg and a separate coverage job uploading abisect_ppxHTML report. - Opam metadata:
dev-repo,doc,tagsfields populated viadune-project.cabal.opamregenerated.
Diagnostics
Session_event_log.write_raw_eventandread_eventsnow emitDiagnostics.warnon unparseable lines instead of silently dropping them.Adapter_loader.env_mappings_fieldnow emits oneDiagnostics.warnper non-string env value, tagged with the YAML source.
Tests added
test/test_session_event_log.ml— file mode, dir mode, diagnostics surfacing.test/test_backend_event_redaction.ml— 18 cases covering sensitive fields, value patterns, and nested-container redaction.test/test_backend_config_writer.ml— managed-header round-trip, idempotent writes, hash-mismatch refusal,forcebackup, invalid-namespace refusal.test/test_validated_namespace.ml— smart-constructor acceptance and rejection cases.test/test_resource_guardian.ml—Domain-based concurrency tests for register/unregister.test/test_backend_process.mlextended with version-probe timeout assertions.test/test_adapter_loader.mlextended with top-level / env-mapping negatives and a diagnostic-capture test.test/test_backend_registry.mlextended with two AC5 static-vs-runtime consistency property tests.
Deferred
- ocamlformat enforcement — adopting stock ocamlformat would reformat every existing
.ml/.mli. The current style is consistent but hand-formatted; a sweeping reformat is out of scope for a bug-fix PR and is better tracked as its own opt-in. - Full split of
Backend_config_writer— only the cleanup helpers were extracted; further decomposition of sidecar IO / write policy would change the public surface and is left for a follow-up. maintenance_intentopam field — requires(lang dune 3.18); the project pins 3.13.