Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .trellis/spec/backend/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ error matrices, tests, paths, and security rules belong in the linked owner.
3. Read the focused feature contract that owns the data, filesystem, process,
network, secret, or IPC behavior being changed. Managed Auth is split into
[core/vault/migration](./managed-auth.md),
[provider login sessions](./managed-auth-login.md), and
[consumer projection](./managed-auth-consumers.md); cite only the owner of
the behavior being changed.
[provider login sessions](./managed-auth-login.md),
[consumer projection](./managed-auth-consumers.md), and
[managed account proxy binding](./managed-account-proxy.md); cite only the
owner of the behavior being changed.
4. For platform or delivery work, also read the matching Windows/macOS and
CI/release governance contracts.

Expand Down Expand Up @@ -70,6 +71,7 @@ and reuse of Grok's npm mirrors and ordinary-user execution boundary.
| [Managed Auth Core](./managed-auth.md) | Account metadata, Credential Sessions, SecretRef vault admission, JSON migration, refresh ownership, and Proxy token resolution. |
| [Managed Auth Login](./managed-auth-login.md) | Backend login sessions, OpenAI browser/Device Code, xAI Device Code, cancellation, reopen, and grant admission. |
| [Managed Auth Consumers](./managed-auth-consumers.md) | Codex/Grok/OpenCode connection observation, native projection gates, readback, ownership transfer, and restart evidence. |
| [Managed Account Proxy](./managed-account-proxy.md) | Explicit OpenAI/xAI account binding to local Agent Providers, target handoff, and `fyagent_proxy` overview projection. |
| [Deep-Link Import Security](./deeplink-import-security.md) | Untrusted deep-link parsing, confirmation, import capabilities, and side-effect limits. |
| [Change Plan Typed Executor](./change-plan-executor.md) | Typed plans, idempotency, execution phases, compensation, and partial results. |
| [Codex Provider Configuration](./codex-provider-configuration.md) | Codex provider/auth projection, writer serialization, backup, rollback, and readback. |
Expand Down
40 changes: 40 additions & 0 deletions .trellis/spec/backend/local-proxy-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ Provider switching, live-config backup/restore, and crash recovery are owned by
[Local Proxy Service and Takeover](./proxy-runtime.md). This file owns the HTTP
engine after that service has admitted and configured it.

[Managed Account Proxy](./managed-account-proxy.md) owns which explicit
OpenAI/xAI account, model and Agent target may create the managed Provider and
how its local route appears in the account overview. This pipeline consumes the
admitted Provider/account lineage; it does not select an account or publish
connection summaries.

Provider-specific adapters may add narrower authentication or wire-format
rules, but they must enter and leave through this pipeline rather than creating
a parallel HTTP server, retry loop, health ledger, or usage logger.
Expand Down Expand Up @@ -108,6 +114,34 @@ socket, database handle, or raw response-success override.
can prove failure. Unknown or malformed bodies are not rewritten into a
fabricated success.

### Managed subscription Responses policy

- OpenAI/xAI managed credentials pin vendor origin and Responses protocol before
editable metadata. API-key and official native-client auth paths retain their
own adapters. Resolve the actual bound upstream model before final headers.
- `providers/managed_responses::prepare_request(provider, endpoint, body)` runs
after editable overrides. OpenAI generation requests force `store=false`,
remove `max_output_tokens`, and deduplicate encrypted-reasoning inclusion.
Compact requests have a separate schema and must not receive generation-only
fields. Existing Claude conversion handles a forced upstream SSE response
even when its downstream caller requested non-streaming JSON.
- xAI reuses namespace/sanitize owners, normalizes the route model, hoists
text-only system/developer content into instructions, removes unsupported
reasoning/encrypted replay and retention fields, and maps a legacy response
format only when no explicit text policy exists. Preserve function calls,
call IDs, arguments and empty function-result outputs; reject unsupported
instruction content instead of silently deleting it.
- Request-time vault resolution retains the admitted account lineage. A first
upstream 401 drops its response before a single same-account refresh/replay
inside the existing attempt and permit. Reuse the buffered request, replace
protected bearer/routing headers and bound response-header waits. A second
401 is terminal. Do not create another failover loop or retry a different
account/API-key balance after subscription 401/403 or credential failure.
- Shared OpenAI compatibility headers apply to all consuming Agents. Final
xAI token-auth, model-override and client headers replace caller copies.
Neither upstream credentials nor raw OAuth/upstream error bodies may escape
into renderer results, Agent configuration or diagnostics.

### Response, streaming, and usage

- Rebuilt bodies remove hop-by-hop headers and stale entity headers. Streaming
Expand Down Expand Up @@ -163,6 +197,12 @@ socket, database handle, or raw response-success override.
- Forwarder tests cover retryable/terminal classification, permit settlement,
non-streaming body-read failure, first-stream-chunk priming/replay, semantic
2xx failures, timeout/cancellation, and protected overrides.
- Managed subscription tests cover both providers and all three CLI targets,
official host/path assertions before mock I/O, complete stream/tool terminal
events, tool-result replay, final policy idempotence, preserved native auth,
exact one-401 retry and no default-account/API-key fallback. HTTP fixtures
must honor the actual outbound stream flag; a JSON mock is not valid evidence
for an adapter expecting `response.completed` in SSE.
- Response/SSE tests cover LF/CRLF, split multibyte UTF-8, incomplete/trailing
events, rebuilt headers, disconnect/drop settlement, and bounded diagnostics.
- Usage tests cover protocol parsers, deduplication, model resolution, cache
Expand Down
32 changes: 30 additions & 2 deletions .trellis/spec/backend/macos-system-commit.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ this table or the admitted Agent lifecycle; Claude Code is CLI-only under
- Formal signing requires both nested binaries. `FYAGENT_ALLOW_APP_ONLY_SIGN=1`
is local/diagnostic only.
- `.build/` and `dist/` under `src-tauri/macos-privileged-helper/` are
gitignored build outputs.
gitignored build outputs. Development scratch is `.build-development`.
Copy the current SwiftPM `release/` product first; a leftover
`apple/Products` tree from a previous Xcode driver must not win, or the
embedded helper `CFBundleVersion` will fail the stale-version check.
- WorkBuddy's expected bundle ID is `com.tencent.workbuddy.mac`. Keep
`agent_install/desktop.rs` `DESKTOP_PRODUCTS` / `macos_bundle_id_for`,
`macos_system_commit/policy.rs`, and privileged helper `Policy.swift` equal.
Expand All @@ -177,6 +180,7 @@ this table or the admitted Agent lifecycle; Claude Code is CLI-only under
| Renderer sends path/URL/command/Authorization bytes | Reject at Agent/Codex IPC; helper never sees it |
| Swift package added as a Cargo workspace member | `version:check` / workspace contract fails |
| Formal `sign-app` without both nested binaries | Fail; do not sign an app-only bundle |
| Current SwiftPM `release/` product and leftover `apple/Products` both exist | Copy the current SwiftPM product; the legacy Xcode tree must not win |
| `sudo`, AppleScript admin, `AuthorizationExecuteWithPrivileges`, or setuid | Forbidden; tests and review reject |

## 5. Good / Base / Bad Cases
Expand All @@ -186,9 +190,13 @@ this table or the admitted Agent lifecycle; Claude Code is CLI-only under
`authorization_required` while production is disabled.
- Base: portable Swift tests and Rust `macos_system_commit` tests cover the
product table, ABI layout, and fail-closed port without Blessing a helper.
- Good: a driver switch leaves an old universal Xcode product under
`apple/Products`, but packaging embeds the just-built SwiftPM `release/`
helper and its current `CFBundleVersion` passes verification.
- Bad: enabling `production_enabled()` because the helper compiled; labeling
`~/Applications` success as a system install; adding Claude to the helper
table without a reviewed slot; sending a filesystem path over XPC.
table without a reviewed slot; sending a filesystem path over XPC; or
selecting an older Xcode product merely because it is already universal.

## 6. Tests Required

Expand All @@ -204,6 +212,8 @@ this table or the admitted Agent lifecycle; Claude Code is CLI-only under
- `tests/releaseWorkflow.test.ts`: `build-macos` runs
`build-macos-privileged-helper.sh`, `embed-macos-privileged-helper.sh`, and
`verify-macos-privileged-helper.sh --structure-only` before `sign-app`.
- `tests/miseTaskContract.test.ts`: the helper build copies the current
SwiftPM `release/` product before leftover `apple/Products`.
- Negative: no renderer path/URL/command; no `sudo` / `osascript` elevation;
Cargo workspace members stay `[".", "user-helper"]`.
- Signed/notarized `/Applications` HIL is required before flipping
Expand Down Expand Up @@ -258,6 +268,24 @@ macos_bundle_id: "com.tencent.workbuddy.mac", // lockstep with Policy.swift

#### Wrong

```bash
find_named_artifact() {
try "$SCRATCH_PATH/apple/Products/Release/$name"
try "$SCRATCH_PATH/release/$name"
}
```

#### Correct

```bash
find_named_artifact() {
try "$SCRATCH_PATH/release/$name" # current driver output
try "$SCRATCH_PATH/apple/Products/Release/$name" # compatibility fallback
}
```

#### Wrong

```swift
if try !commit.isDefined() {
try commit.createOrUpdateDefinition(
Expand Down
Loading