Skip to content

Combine the Agent Companion into this repository as companion/ - #102

Merged
nniiovoo merged 28 commits into
mainfrom
feat/companion-in-repo
Nov 2, 2026
Merged

nniiovoo merged 28 commits into
mainfrom
feat/companion-in-repo

Conversation

@nniiovoo

@nniiovoo nniiovoo commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Combines the Agent Companion's private repository into this one as companion/, with its history, as the owner approved on 2026-11-01. The code is now public under this repository's MIT license. The decision and its trade-offs are in ADR-0010.

Important

Merge this with "Create a merge commit". A squash or rebase would drop or rewrite the imported history, including the commits this app used to pin.

Why

No decision ever compared one repository with two. What protects the Vault is the helper's XPC sandbox, not where its source is kept. Meanwhile, the split meant a pin bump and re-verification for every companion change, companion CI blocked by a billing limit, a public app whose helper couldn't be built without private access, and a readiness bug that sat between the two test suites.

What changed

Area Change
companion/ The old repository's main at 1c983a7, merged with its original commit SHAs. Also added: its own .npmrc and an MIT license field. Removed: its workflow, which GitHub wouldn't run from a subfolder.
scripts/package-companion.sh Bundles companion/ as committed, and refuses if that folder has uncommitted changes. Still verifies policy.json against policySha256 and the Node.js SHA-256 values.
companion.lock.json The repository and commit fields and --companion-source are gone.
scripts/check-network-source.mjs Leaves companion/ out on purpose and says so in its header. Now fails if any file in src/, src-tauri/src/ or crates/ imports from companion/.
scripts/check.sh, CI Run the companion's tests on Mac, one file at a time, because they share port 8789.
Docs ADR-0010 amends "One pinned source" in ADR-0009. AGENTS.md, README.md, docs/agent-companion.md and docs/agent-key-validation.md no longer describe a separate private repository, except as history.

To review, go commit by commit. The first commit on this branch is the import alone, and its companion/ tree is identical to 1c983a7. The Commits tab lists 28: 23 from the companion and 5 here.

What didn't change

  • The helper is still a separately sandboxed XPC service.
  • The entitlement files, src-tauri/native/, src/, src-tauri/src/ and crates/ have no changes.
  • package.json, package-lock.json, Cargo.toml, Cargo.lock and deny.toml have no changes, so there's no new crate or Vault dependency.
  • In companion/, src/, the .mjs files in isolation/, policy.json and the dependency versions match 1c983a7.

Checks

Run on a fresh clone at 357e240, with fictional data only:

Check Result
scripts/verify-companion.sh, on a helper packaged from companion/ 42 of 42
scripts/check.sh Passes, including 63 companion tests with none skipped and 643 Rust tests
Clone of this branch from GitHub with no credentials, at 8323705 The helper packages and signs with no private access, its runtime files match the verified helper's exactly, and the 63 companion tests pass
Scan of the full history before publishing (gitleaks, plus a second pass over every file version, commit message, path and identity) No real secrets, only FAKE_ test markers

Not covered

  • scripts/package-companion.sh --app wasn't re-run. The part of the script that builds the app is unchanged.
  • The companion's tests don't run on Windows, where the helper is unavailable.
  • Everything already pending stays pending: the real Agent Key, the agent account and the live call.

After this merges

  • fix/recovery-kit-typing shares only README.md with this branch, on different lines, and its changes still apply cleanly.
  • The old private repository stays as it is, no longer updated. Archiving or deleting it is the owner's decision.
  • In imported commit messages, #N means a pull request in the old repository, though GitHub links it to this one.
  • To read a file's older history, use its old path, for example git log 1c983a7 -- src/generate.mjs. git log --follow stops at the import.

🤖 Generated with Claude Code

nniiovoo and others added 28 commits October 21, 2026 17:26
Add bounded OpenAI text generation core
Expose restricted text generation through MCP
Prepare separate owner and agent execution on Mac
Reviewed plans move to version 2 with grantExpiresAt, a canonical UTC
instant. Version-1 plans are refused with a request for a new reviewed
plan; nothing is migrated.

The grant is checked before the credential read, before the broker opens
and before every admission. Admission ends at the earlier of the absolute
deadline and one hour of monotonic time since start, so a backwards clock
change cannot add time and a restart cannot renew an expired plan. An
expired grant returns the fixed grant_expired error without validation,
counter or provider work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
isolation/integrated-broker.mjs runs the same fixed generate_text broker
under encrypted-note's Agent Companion helper:

- fixed data paths inside the helper's own sandbox container (HOME);
- one new grant of at most one hour per owner Start;
- a fresh delegated agent token per Start, written owner-only with the
  single agent read ACL and removed on every stop;
- a bounded owner-control protocol on stdin/stdout (one stop command in;
  ready/stopped/error/check events with fixed codes out), separate from
  the agent MCP channel. Control-pipe EOF, malformed control, signals and
  the grant deadline all stop the broker.

Code/runtime ancestors may be admin-group-writable (for /Applications)
only after the agent account is confirmed not to be an administrator.
Readiness checks report booleans and never read the credential contents.

Tests cover start/stop/EOF/malformed control, expiry for direct loopback
calls, every refusal code, busy ports, readiness and that fictional key,
token and provider-error markers never reach protocol output or tool
results. 49 tests pass on Node 26.8.2 and the pinned Node 24.22.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README and isolation/PLAN.md now cover version-2 plans, the owner grant
and the integrated broker entry point used by encrypted-note's Agent
Companion helper. docs/chatgpt-connection.md prepares the optional
Secure MCP Tunnel route through the same agent bridge and lists the
observations required before a connection counts as verified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the integrated helper entry point and an absolute one-hour grant
body-parser 2.0.0-2.3.0 can be crashed by trailing bytes after a
compressed request body (high, CVSS 7.5). It reaches us through
@modelcontextprotocol/sdk 1.32.1 -> express 5.3.0, and no SDK release
forces a fixed copy (1.32.2 still accepts express ^5.2.1, which accepts
body-parser ^2.3.0), so pin the patched version with an exact override.

Only body-parser changes in the lockfile; its raised dependency floors
are already met by the installed qs, iconv-lite and content-type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An agent that hit a failed call got no next step, so it could go looking
for keys or tokens. Each fixed error text now says what to do: ask the
owner to choose Start for up to one hour, tell the owner, or try once
more. None says why the Agent Companion is off, so nothing about the
Vault being locked reaches the agent.

The tool text now says the tool has no route to the owner's Vault, and
that it stops working when the owner's grant ends or the owner stops it.

The bridge entry point also exited with nothing when started through a
linked folder, because Node resolves links for the module's own URL but
not for argv. It now compares the resolved path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The control pipe kept the process alive after its final event, and the
helper learns that a run has ended only when the process exits. Close
the pipe. pause() isn't enough: called from the data handler, the stream
starts reading again once the handler returns.

Add tests that run the real broker and bridge entry points: exiting with
the pipe still open, exiting and removing the token when a grant expires,
and the bridge serving the tool across helper stops and starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new tests start real processes and share port 8789, and their timing
fails under parallel load. npm test and npm run check now run serially.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix: clearer agent errors, and let the broker exit after its final event
verifyCodeTree started one /bin/ls per entry. The packaged helper has about
4,200 entries, so with a real agent account the walk took about 16 s, and
the helper kills its readiness check at 10 s. Every row but the runtime then
showed as not ready, and Start could never turn on, with any real account.
A process test with an account that exists hadn't been run.

List ACLs in batches of 128. The same entries get the same checks, and any
failure of the listing refuses. Only indented lines count as ACL entries, so
a file name can cause a refusal but can't hide a real grant.

On the packaged tree: about 16,200 ms before, about 300 ms now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix: check the code tree in batches, so readiness finishes in time
Merges nniiovoo/encrypted-note-agent-companion main at 1c983a7 unchanged, so
companion/ is byte-for-byte that commit. Nothing else changes here; the
packaging, checks and docs follow in their own commits (ADR-0010).

The owner approved this on 2026-11-01: publish the companion under this
repository's MIT license and keep its history. A scan of the full history
(gitleaks, plus a second pass over every file version, commit message, path
and identity) found no real secrets, only FAKE_ test markers.

The original commit SHAs are kept, so the commits this app used to pin stay
findable here. Land this with a merge commit: a squash would drop the history.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
package-companion.sh now takes companion/ as committed at the current commit
(git archive) instead of cloning a pinned commit, and refuses if that folder
has uncommitted changes, so the helper still ships only reviewable code. It
keeps verifying policy.json against policySha256 and the Node.js archives
against their SHA-256 values. The lock file's repository and commit fields and
the --companion-source option are gone.

The helper's EnoteCompanionCommit value now names the commit it was packaged
from. Nothing reads it. The helper bundle also gains the MIT license that now
covers the companion.

companion/ gets its own .npmrc, because npm doesn't read the root one from
there, and declares its MIT license. No dependency changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
check-network-source.mjs leaves companion/ out on purpose, and says so in its
header: calling OpenAI is the broker's job, and it runs only in the helper's
sandbox. In its place the script now fails if any file in src/, src-tauri/src/
or crates/ imports from companion/. Paths resolve against the importing file,
so the pins in src-tauri/companion/ aren't confused with it.

check.sh and CI run the companion's tests on Mac, the only place the helper
exists, one file at a time because they share port 8789. GitHub doesn't run
workflows in a subfolder, so companion/.github goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR-0010 amends "One pinned source" in ADR-0009 and leaves the rest of it
alone. AGENTS.md, the README, the setup guide and the validation record no
longer describe a separate private repository, except as history.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nniiovoo
nniiovoo merged commit bf96967 into main Nov 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant