opencode-muse-auth 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,132 @@
1
+ # Muse wire adapter contract (v3)
2
+
3
+ Every Muse Code plugin in this family sends subscription inference through the same local
4
+ adapter. This file pins the wire rules; `ADAPTER_VERSION` in the code is the matching constant.
5
+ Change both together in all four repositories:
6
+
7
+ - `opencode-muse-auth`
8
+ - `hermes-muse-code`
9
+ - `openclaw-muse-code`
10
+ - `dsh-muse-code`
11
+
12
+ ## Why the adapter exists
13
+
14
+ Since 2026-10-29/30, `https://api.meta.ai/v1` refuses a Muse Code subscription credential when
15
+ the request declares a flat function tool whose name is exactly one of Muse Code's builtin tool
16
+ names. The refusal is HTTP 403 with `code: client_attestation_denied`:
17
+
18
+ > This API key is billed to a Muse Code subscription. Muse Code subscriptions cover requests from
19
+ > the Muse Code client only. Use it in Muse Code, or use a pay-as-you-go API key for other clients.
20
+
21
+ The gate inspects the declared tool names, not the client identity: headers, user agent, system
22
+ prompt and the rest of the payload do not change the outcome. The adapter aliases only the
23
+ colliding names on the way out and restores them on the way back, so the host keeps its own tool
24
+ names and no credential or signature is forged.
25
+
26
+ ## Measured trigger (live subscription key, 2026-10-30)
27
+
28
+ | Request tool declarations | Result |
29
+ |---|---|
30
+ | no `tools` | 200 |
31
+ | flat `get_weather` | 200 |
32
+ | flat `client_read_file` | 200 |
33
+ | flat `read_file` | 403 |
34
+ | flat `bash` | 403 |
35
+ | flat `web_search` | 403 |
36
+ | flat names outside the first six, e.g. `web_fetch`, `workflow`, `powershell`, `add_memory` | 403 (each, separate probe) |
37
+ | flat `read_file` + `get_weather` | 403 |
38
+ | namespace `client` containing `read_file` | 200 |
39
+ | namespace `muse` with the genuine builtin declaration | 200 |
40
+ | namespace `muse` with a re-schematized `read_file` | 403 |
41
+
42
+ Two consequences drive the rules below:
43
+
44
+ 1. A single colliding flat name is enough to fail the whole request.
45
+ 2. Wrapping client tools in a namespace also passes today, but Meta validates the contents of a
46
+ namespace named `muse`, and a wrapper changes the tool surface the model sees. The adapter
47
+ therefore keeps the flat shape and only renames the colliding declarations.
48
+
49
+ ## Builtin collision set (v3)
50
+
51
+ `workflow`, `read_file`, `write_file`, `edit_file`, `bash`, `bash_input`, `search`,
52
+ `web_search`, `web_fetch`, `powershell`, `powershell_input`, `read_memory`,
53
+ `add_memory`, `edit_memory`, `read_skill`, `write_todos`, `monitor`, `work_status`,
54
+ `work_list`, `work_stop`, `snooze_reminder`, `apply_patch`, `shell`, `create_goal`,
55
+ `get_goal`, `update_goal`, `report_progress`, `request_user_input`, `cron_create`,
56
+ `cron_delete`, `cron_list`, `send_session_message`, `list_peer_sessions`,
57
+ `subagent_send_message`, `subagent_cancel`, `subagent_spawn`, `subagent_status`,
58
+ `subagent_wait`, `subagent_read_result`, `tool_search`.
59
+
60
+ The set comes from the 1.4.14 binary's tool catalog plus `muse exec` captures;
61
+ every name was probed individually against api.meta.ai on 2026-10-30: all but
62
+ `tool_search` returned 403, and `tool_search` stays in the set defensively. The
63
+ catalog includes the goal, cron, session and patch tools as well as the file,
64
+ shell and web families.
65
+
66
+ The set is pinned in code (`COLLIDING_TOOL_NAMES`). Add a name only with evidence that Meta
67
+ rejects it, and bump `ADAPTER_VERSION`.
68
+
69
+ ## Request rules
70
+
71
+ 1. **Aliasing.** Every flat tool of `type: "function"` whose `name` is in the collision set is
72
+ renamed to `client_<name>`. If that alias is already taken by another declared tool, append
73
+ `_2`, `_3`, and so on. The same mapping is applied to `input[].function_call.name` for
74
+ `type: "function_call"` history items. Non-function tools, namespace containers and
75
+ non-colliding names are left untouched.
76
+ 2. **`tool_choice`.** Meta accepts only `"auto"`. Any other value (named function, `"none"`,
77
+ `"required"`) is replaced with `"auto"`.
78
+ 3. **Reasoning effort.** A top-level `reasoning_effort` string moves to `reasoning.effort`; an
79
+ explicit `reasoning.effort` already present wins.
80
+ 4. **Output floor.** `max_output_tokens` below 16 is raised to 16.
81
+ 5. **Reasoning replay.** `reasoning.encrypted_content` is appended to `include` when missing, so a
82
+ host that replays provider items can keep the encrypted reasoning item.
83
+ 6. **Version header.** `x-api-version: 1.0.0` is added when the host did not set it.
84
+ 7. Unknown fields are preserved untouched, and the caller's body object is never mutated.
85
+
86
+ ## Automatic fallback
87
+
88
+ If Meta refuses a request with `client_attestation_denied` even after these
89
+ rules, the adapter retries the same request once with every flat function tool
90
+ aliased (`client_<name>`), and restores the names in the answer. That keeps a
91
+ Muse builtin name this release does not know from taking the lane down; the
92
+ retry is logged as `gate refused; retried with all N tools aliased`.
93
+
94
+ ## Response rules
95
+
96
+ 1. Aliased `function_call` names are restored to the original name in:
97
+ `response.output_item.added` / `.done` items, the `output` array of every `response.*` event,
98
+ the `tools` echo of the response object, the top-level `name` of
99
+ `response.function_call_arguments.done`, and the `output` array of a non-streaming JSON body.
100
+ 2. If an authoritative `output_item.done` or completed item carries an empty `arguments`, the
101
+ value accumulated from `response.function_call_arguments.delta` events is written back.
102
+ 3. SSE is relayed line by line and flushed per line; responses are never buffered.
103
+
104
+ ## Transport rules
105
+
106
+ - The adapter listens on `127.0.0.1` only, on the first free port of `43219`, `43220`, `43221`,
107
+ `43222`, then an ephemeral port.
108
+ - The provider base URL becomes `http://127.0.0.1:<port>/v1`.
109
+ - `Authorization` is forwarded unchanged; the adapter never reads, stores or logs credentials,
110
+ request bodies or response bodies.
111
+ - Every path other than the Responses and Chat completion paths is relayed untouched, so
112
+ `GET /v1/models` discovery keeps working through the same base URL.
113
+ - `MUSE_CODE_ADAPTER=off` turns the wire rules off while keeping the proxy in place, so a
114
+ persisted base URL stays valid. Diagnostic logging is one line per adapted request, through the
115
+ host's debug logger.
116
+ - If the adapter cannot bind, the plugin logs the failure and leaves the provider on the direct
117
+ endpoint, which is the pre-0.3 behavior.
118
+
119
+ ## Failure modes
120
+
121
+ - Meta changes the gate to a real per-client signature: aliasing stops helping and requests fail
122
+ again with `client_attestation_denied`. Nothing in this design can fake a signature.
123
+ - Meta renames or removes a builtin: the collision set needs an update.
124
+ - A host does not preserve provider reasoning items across turns: tool arguments may degrade to
125
+ `{}`. The adapter repairs the empty-arguments field, but not a host that never replays the
126
+ reasoning item.
127
+
128
+ ## Verification
129
+
130
+ - Offline: the adapter unit and proxy tests in each repository, against a fake upstream.
131
+ - Live: start the adapter against `https://api.meta.ai`, send a request with a colliding tool
132
+ name, and require HTTP 200 with the original name restored in the response.
package/AUTH_CONTRACT.md CHANGED
@@ -1,11 +1,12 @@
1
1
  # Muse auth contract (v1)
2
2
 
3
- The three Muse Code plugins implement the same Meta device-code flow. Change
4
- this file and the pinned constants/tests together in all three repositories:
3
+ The four Muse Code plugins implement the same Meta device-code flow. Change
4
+ this file and the pinned constants/tests together in all four repositories:
5
5
 
6
6
  - `opencode-muse-auth`
7
7
  - `hermes-muse-code`
8
8
  - `openclaw-muse-code`
9
+ - `dsh-muse-code`
9
10
 
10
11
  ## Wire contract
11
12
 
@@ -18,6 +19,7 @@ this file and the pinned constants/tests together in all three repositories:
18
19
  | API version header | `x-api-version: 1.0.0` |
19
20
  | Cache fields | `apiKey`, `accountId`, `email` (never the OAuth access token) |
20
21
  | Inference base URL | `https://api.meta.ai/v1` |
22
+ | Inference wire adapter | see `ADAPTER_CONTRACT.md` (v3) |
21
23
 
22
24
  ## Behaviour
23
25
 
@@ -28,5 +30,11 @@ this file and the pinned constants/tests together in all three repositories:
28
30
  key; reject an inactive subscription or a pending payment.
29
31
  - Persistence: only the inference key and account identity are stored; the
30
32
  OAuth access token stays in memory.
33
+ - Inference traffic on the V2 plugin API goes through the loopback adapter
34
+ described in `ADAPTER_CONTRACT.md` (tool-name aliasing, parameter
35
+ normalization, response name restoration). The adapter is part of the wire
36
+ contract: a change to those rules bumps `ADAPTER_VERSION` and the pinned
37
+ tests in every repository that vendors the adapter. The V1 code path keeps
38
+ sending directly to `https://api.meta.ai/v1`.
31
39
  - Any change to this contract bumps `AUTH_CONTRACT_VERSION` in the code and
32
- requires updating the pinned tests in all three repositories.
40
+ requires updating the pinned tests in all four repositories.
package/DISCLAIMER.md CHANGED
@@ -25,6 +25,19 @@ Meta may change, restrict or enforce its terms at any time, and may suspend,
25
25
  restrict or terminate accounts, subscriptions or credentials involved. The
26
26
  authors cannot prevent, predict or reverse that outcome.
27
27
 
28
+ ## Request shaping
29
+
30
+ Since 2026-10-29 Meta refuses subscription requests from other clients when the
31
+ declared client tool names collide with Muse Code's builtin tool names. On the
32
+ V2 plugin API this plugin therefore runs a local adapter that renames exactly
33
+ those declarations on the way to `api.meta.ai` and restores them in the
34
+ response, so the request matches Muse Code's wire format. This is a deliberate
35
+ workaround of Meta's client restriction: it does not forge a credential, token
36
+ or signature, and the subscription key remains your own, but Meta may treat the
37
+ reshaping itself as a violation of its terms. The suspension, revocation and
38
+ no-refund risks described above apply with full force, and revocation can
39
+ happen immediately.
40
+
28
41
  ## No warranty, no guarantee
29
42
 
30
43
  - The integration is provided "as is"; there is no guarantee that it is
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 TheStreamCode
3
+ Copyright (c) 2026 Michael Gasperini (Mikesoft)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,16 +1,26 @@
1
1
  # opencode-muse-auth
2
2
 
3
- Use Meta **Muse Spark** in [opencode](https://opencode.ai) billed to your
4
- **Muse Code monthly subscription** — no pay-as-you-go API key, no third-party CLI.
3
+ Muse Code subscription sign-in for opencode
5
4
 
6
- > **Unofficial and experimental.** This plugin uses a Muse Code subscription
7
- > credential from opencode. Meta's terms limit that credential to Muse Code, so
8
- > this use may not be supported and Meta may suspend or revoke accounts or
9
- > credentials. Use it at your own risk; the authors cannot recover an account.
10
- > Read [DISCLAIMER.md](DISCLAIMER.md) before installing.
5
+ [![CI](https://github.com/TheStreamCode/opencode-muse-auth/actions/workflows/ci.yml/badge.svg)](https://github.com/TheStreamCode/opencode-muse-auth/actions/workflows/ci.yml)
6
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ [![npm](https://img.shields.io/npm/v/opencode-muse-auth)](https://www.npmjs.com/package/opencode-muse-auth)
8
+
9
+ opencode plugin that registers the **Muse Code subscription** login method for the built-in `meta`
10
+ provider. No API key to copy and no third-party CLI to install. On the V2 plugin API the plugin also
11
+ routes subscription inference through a local wire adapter that keeps requests compatible with Muse
12
+ Code's tool declarations; see [ADAPTER_CONTRACT.md](ADAPTER_CONTRACT.md). A successful login still
13
+ does not guarantee model access: Meta may reject or restrict this use.
14
+
15
+ > **Unofficial and experimental.** This plugin uses a Muse Code subscription credential from
16
+ > opencode. Meta's terms limit that credential to Muse Code, so this use may not be supported and
17
+ > Meta may suspend or revoke accounts or credentials. Use it at your own risk; the authors cannot
18
+ > recover an account. Read [DISCLAIMER.md](DISCLAIMER.md) before installing.
11
19
 
12
20
  ## Install
13
21
 
22
+ Add the plugin to `opencode.json`; opencode installs it from npm:
23
+
14
24
  ```jsonc
15
25
  // opencode.json
16
26
  {
@@ -19,64 +29,152 @@ Use Meta **Muse Spark** in [opencode](https://opencode.ai) billed to your
19
29
  }
20
30
  ```
21
31
 
22
- Then run `/connect` in opencode, pick the `meta` provider and the
23
- *Muse Code subscription* method: it shows a Meta device page URL and a code.
24
- Enter the code in your browser (your own Meta account) and the login
25
- completes by itself.
26
-
27
- Verified end-to-end against the published package: isolated project with
28
- only `"plugins": ["opencode-muse-auth"]`, live chat on `meta/muse-spark-1.3`
29
- billed to subscription.
32
+ ## Sign in
33
+
34
+ 1. Run `/connect` in opencode.
35
+ 2. Pick the `meta` provider and the **Muse Code subscription** method.
36
+ 3. Open the printed Meta device page, enter the printed code, and approve the login in your own
37
+ browser. The login completes by itself.
38
+ 4. Select a `muse-*` model exposed by the `meta` provider, for example `meta/muse-spark-1.3`.
39
+ On opencode 2.x the plugin routes inference through the local adapter, so client tool names that
40
+ collide with Muse Code's builtins no longer trip Meta's client gate. The V1 plugin API still
41
+ sends requests straight to `api.meta.ai` and keeps receiving the 403.
42
+
43
+ The plugin mints a stable, account-bound inference key and hands it to opencode. The OAuth access
44
+ token from the login flow is kept in memory only and is never written to disk.
45
+
46
+ ## Features
47
+
48
+ - Registers an `auth` hook for the built-in `meta` provider; there is no separate provider id to
49
+ configure.
50
+ - Runs the Meta device-code exchange (RFC 8628) inside `/connect`, then mints the subscription
51
+ inference key through the Model API.
52
+ - Routes through the Responses API; the `meta.txt` system prompt applies automatically to `muse-*`
53
+ model ids.
54
+ - Runs a loopback wire adapter for the V2 plugin API: it aliases the client tool names that collide
55
+ with Muse Code's builtins on the way to `api.meta.ai`, restores them in the response, and
56
+ normalizes parameters Meta rejects (`tool_choice`, reasoning effort, output-token floor,
57
+ encrypted-reasoning include). The adapter's `/v1` origin becomes the per-request base URL; on
58
+ routes that ignore that override the plugin rewrites the assembled request onto the same origin. If Meta still refuses the request, the
59
+ adapter retries once with every tool aliased. See [ADAPTER_CONTRACT.md](ADAPTER_CONTRACT.md).
60
+ - V1 storage: the OpenCode auth store is the primary source, and the loader falls back to the
61
+ legacy cache only when the store has no key.
62
+ - V2 storage: the credential lives in the OpenCode integration store, and no local cache is
63
+ written on this path.
64
+ - Never re-mints on its own; re-run `/connect` only after a 401, because the mint endpoint is
65
+ aggressively rate-limited.
66
+ - Flow parameters are compatible with the published behavior of oh-my-pi (MIT); see [NOTICE](NOTICE)
67
+ and [AUTH_CONTRACT.md](AUTH_CONTRACT.md).
30
68
 
31
69
  ## Requirements
32
70
 
33
- - opencode with plugin support
34
- - A Meta account with an active Muse Code subscription
35
-
36
- ## How it works
37
-
38
- The plugin registers an `auth` hook for the built-in `meta` provider
39
- (Routes through the Responses API, `meta.txt` system prompt applies
40
- automatically to `muse-*` model ids):
41
-
42
- - **Login** — Meta device-code exchange (RFC 8628) inside `/connect`, then
43
- mints the stable account-bound inference key via the Model API. The key is
44
- never printed and the OAuth access token from the login flow is kept in
45
- memory only.
46
- - **V1 storage** — OpenCode's auth store is the primary source; the auth
47
- `loader` falls back to the legacy cache
48
- (`~/.config/opencode/muse-code-sub.json`) only when the store has no key.
49
- The cache is still written so upgrades keep working.
50
- - **V2 storage** — the credential lives in OpenCode's integration store; the
51
- plugin does not write the local cache on this path.
52
- - **Runtime** — the mint endpoint is aggressively rate-limited, so the plugin
53
- never re-mints on its own; re-run `/connect` only if access is revoked (401).
54
-
55
- Flow parameters are compatible with the published behavior of oh-my-pi (MIT);
56
- see [NOTICE](NOTICE) and [AUTH_CONTRACT.md](AUTH_CONTRACT.md).
57
-
58
- ## Credentials and removal
59
-
60
- The inference key is held by OpenCode (V1 auth store or V2 integration store);
61
- the legacy cache, when present, stores only `apiKey`, `accountId`, and `email`.
62
- The OAuth access token is never written to disk. To remove access:
63
-
64
- - disconnect the provider or log out in OpenCode (this removes the key OpenCode
65
- uses; the store is `~/.local/share/opencode/auth.json`);
66
- - delete `~/.config/opencode/muse-code-sub.json` if it exists from an older
67
- version;
71
+ - opencode with plugin support, including the v2 plugin module shape accepted by opencode 1.18.29+
72
+ and 2.x.
73
+ - The loopback adapter requires the V2 plugin API (opencode 2.x). V1 installs keep authenticating
74
+ but still receive Meta's `client_attestation_denied` 403.
75
+ - Node.js 22.6 or newer for development; CI runs Node.js 22 and 24.
76
+ - An active Muse Code subscription on the Meta account used at login.
77
+
78
+ ## Configuration
79
+
80
+ | Setting | Purpose | Default |
81
+ | --- | --- | --- |
82
+ | `plugins` entry in `opencode.json` | Loads the plugin. | not set |
83
+ | Auth store | Primary credential storage used by the V1 loader. | `~/.local/share/opencode/auth.json` |
84
+ | Legacy cache | Read-only fallback for credentials written by earlier versions. | `~/.config/opencode/muse-code-sub.json` |
85
+ | `/connect` method | Selects Muse Code subscription login for the `meta` provider. | `meta` provider |
86
+ | `MUSE_CODE_ADAPTER` | `off` keeps the local proxy but disables the tool-name reshaping and parameter normalization. | unset |
87
+
88
+ ## Privacy and security
89
+
90
+ The inference key is held by opencode: in the V1 auth store or the V2 integration store. The
91
+ legacy cache, when present, stores only `apiKey`, `accountId`, and `email`; the OAuth access token
92
+ is never written to disk. The plugin talks only to `auth.meta.com` for the device login and
93
+ `api.meta.ai` for key minting and inference. The V2 wire adapter listens on `127.0.0.1` only,
94
+ forwards the `Authorization` header unchanged, and never reads, stores or logs credentials,
95
+ request bodies or response bodies. See [SECURITY.md](SECURITY.md) for vulnerability reporting and
96
+ [ADAPTER_CONTRACT.md](ADAPTER_CONTRACT.md) for the inference wire rules.
97
+
98
+ To remove access:
99
+
100
+ - disconnect the provider or log out in opencode (the store is
101
+ `~/.local/share/opencode/auth.json`);
102
+ - delete `~/.config/opencode/muse-code-sub.json` if an older version created it;
68
103
  - run `muse logout` if you also want to clear the Muse Code CLI session;
69
104
  - remove `opencode-muse-auth` from `opencode.json`.
70
105
 
106
+ ## Troubleshooting
107
+
108
+ ### `/connect` shows no Muse Code subscription method
109
+
110
+ The plugin is not loaded. Check the JSON syntax of `opencode.json`, the plugin name spelling, and
111
+ the package installation, then restart opencode.
112
+
113
+ ### Requests fail with 401 or access was revoked
114
+
115
+ A 401 may indicate a rotated or revoked key. Re-run `/connect` once; the plugin never re-mints on
116
+ its own.
117
+
118
+ ### Meta rejects subscription requests from this client
119
+
120
+ Meta may return a 403 mentioning `client_attestation_denied` or that Muse Code subscriptions cover
121
+ the Muse Code client only. With the V2 adapter running this should not happen for tool names that
122
+ collide with Muse Code's builtins. If it appears anyway, one of these is true:
123
+
124
+ - the adapter could not bind its loopback port; opencode's log records
125
+ `the local request adapter could not start`, and the provider stays on the direct endpoint;
126
+ - `MUSE_CODE_ADAPTER=off` is set, which keeps the proxy but disables the reshaping;
127
+ - you are on the V1 plugin API, which has no adapter;
128
+ - Meta changed the gate, for example to a real per-client signature. Nothing in this plugin can
129
+ forge one.
130
+
131
+ This is a client restriction, not evidence of an expired key. Signing in again does not remove it.
132
+ Use Muse Code for the subscription, or a separate pay-as-you-go Meta Model API key for other
133
+ clients, subject to Meta's terms.
134
+
135
+ ### The login times out or is denied
136
+
137
+ Approve the printed code in the browser before the flow expires, then retry. A login rejected by
138
+ Meta usually means the account has no active Muse Code subscription.
139
+
140
+ ### A legacy cache file is still present
141
+
142
+ It is harmless: the V1 loader prefers the OpenCode auth store and reads the cache only when the
143
+ store has no key. Delete the file if you are migrating to V2 storage.
144
+
71
145
  ## Development
72
146
 
73
- ```sh
147
+ Requirements: Node.js 22.6 or newer and npm.
148
+
149
+ ```bash
150
+ git clone https://github.com/TheStreamCode/opencode-muse-auth.git
151
+ cd opencode-muse-auth
74
152
  npm install
75
153
  npm run build
76
154
  npm test
77
155
  ```
78
156
 
157
+ `npm test` builds with `tsc` and runs the TypeScript test suite with the Node test runner. CI runs
158
+ the same gate on Node.js 22 and 24; Node 22 needs 22.6 or newer for test-runner type stripping.
159
+ No Meta account is needed for the test suite.
160
+
161
+ ## Support
162
+
163
+ Usage questions and workflows belong in GitHub Discussions; reproducible defects belong in GitHub
164
+ Issues using the bug report form. See [SUPPORT.md](SUPPORT.md). Problems in opencode or Meta
165
+ themselves belong to their respective projects.
166
+
167
+ ## Contributing
168
+
169
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Pull requests must pass the required CI checks.
170
+
79
171
  ## License
80
172
 
81
- MIT — see [LICENSE](LICENSE). The software is provided "as is"; see
173
+ MIT — see [`LICENSE`](LICENSE). The software is provided "as is"; see
82
174
  [DISCLAIMER.md](DISCLAIMER.md) for the risk notice and liability terms.
175
+
176
+ ---
177
+
178
+ Maintained by [Michael Gasperini (Mikesoft)](https://mikesoft.it).
179
+ If this project is useful to you, consider
180
+ [sponsoring its maintenance](https://github.com/sponsors/TheStreamCode).
@@ -0,0 +1,97 @@
1
+ /** Version of the wire rules below; bump with ADAPTER_CONTRACT.md. */
2
+ export declare const ADAPTER_VERSION = 3;
3
+ /** Default upstream; tests override it with a local fake. */
4
+ export declare const DEFAULT_UPSTREAM = "https://api.meta.ai";
5
+ /**
6
+ * Muse Code builtin tool names.
7
+ *
8
+ * Reconstructed from the 1.4.14 binary's tool catalog and from `muse exec`
9
+ * captures, then probed one by one against api.meta.ai on 2026-10-30: a flat
10
+ * client tool with one of these exact names is refused with 403
11
+ * `client_attestation_denied`. The catalog includes goal, cron, session and
12
+ * patch tools as well as the file, shell and web families. `tool_search` is
13
+ * included defensively: one probe returned 200, but field reports see a 403.
14
+ */
15
+ export declare const COLLIDING_TOOL_NAMES: ReadonlySet<string>;
16
+ type JsonRecord = Record<string, unknown>;
17
+ /** One alias plan: original name to wire name, plus the reverse map. */
18
+ export interface AliasPlan {
19
+ readonly aliases: ReadonlyMap<string, string>;
20
+ readonly reverse: ReadonlyMap<string, string>;
21
+ }
22
+ /**
23
+ * Decide which declared tool names must be aliased.
24
+ *
25
+ * Only flat `function` tools are considered: those are the declarations Meta
26
+ * compares against its builtin set. A namespace container is left untouched.
27
+ * With `all`, every flat function tool is aliased; the proxy uses that as a
28
+ * one-shot retry when the gate refuses names this release does not know.
29
+ */
30
+ export declare function planToolAliases(tools: unknown, options?: {
31
+ all?: boolean;
32
+ }): AliasPlan;
33
+ /** Result of adapting a request body: the body to send, its plan and a log line. */
34
+ export interface AdaptedRequest {
35
+ readonly body: JsonRecord;
36
+ readonly plan: AliasPlan;
37
+ readonly notes: string[];
38
+ }
39
+ /**
40
+ * Apply the wire rules to one Responses/Chat request body.
41
+ *
42
+ * The input object is not mutated: a shallow copy is returned. Unknown fields
43
+ * are preserved untouched. Pass a plan from {@link planToolAliases} to reuse a
44
+ * mapping, for example the full-aliasing retry.
45
+ */
46
+ export declare function transformRequestBody(body: JsonRecord, plan?: AliasPlan): AdaptedRequest;
47
+ /**
48
+ * Streaming name restorer.
49
+ *
50
+ * Feeds complete SSE lines and returns the line to relay. It restores aliased
51
+ * function-call names and repairs empty `arguments` from the streamed deltas:
52
+ * Meta's authoritative `response.output_item.done` sometimes ships "" even
53
+ * though the deltas carried the JSON.
54
+ */
55
+ export declare class SseRestorer {
56
+ private readonly reverse;
57
+ private readonly arguments;
58
+ constructor(reverse: ReadonlyMap<string, string>);
59
+ /** Adapt one complete SSE line (without the trailing newline). */
60
+ feed(line: string): string;
61
+ private adapt;
62
+ /** Restore names in an emitted response object (tools echo and output items). */
63
+ private adaptResponse;
64
+ private collectArguments;
65
+ private repairArguments;
66
+ }
67
+ /** Restore aliased names and empty arguments in a complete JSON response. */
68
+ export declare function transformResponseBody(body: JsonRecord, reverse: ReadonlyMap<string, string>): JsonRecord;
69
+ /** Options for {@link startMuseCodeAdapter}. */
70
+ export interface AdapterOptions {
71
+ /** Upstream origin; override for tests only. */
72
+ readonly upstream?: string;
73
+ /** Candidate loopback ports; the first free one wins, then an ephemeral port. */
74
+ readonly ports?: readonly number[];
75
+ /** Diagnostic sink; one short line per adapted request. */
76
+ readonly log?: (message: string) => void;
77
+ /** Disable the wire rules but keep proxying (MUSE_CODE_ADAPTER=off). */
78
+ readonly passthrough?: boolean;
79
+ }
80
+ /** A running adapter. */
81
+ export interface RunningAdapter {
82
+ /** Loopback origin to use as the provider baseURL, e.g. http://127.0.0.1:43219/v1. */
83
+ readonly baseUrl: string;
84
+ /** Actual loopback port. */
85
+ readonly port: number;
86
+ /** Stop accepting and close the server. */
87
+ close(): Promise<void>;
88
+ }
89
+ /**
90
+ * Start the loopback adapter.
91
+ *
92
+ * The server binds 127.0.0.1 only. Requests to the Responses and Chat
93
+ * completion paths are adapted; every other path is relayed untouched, so
94
+ * the provider's model discovery keeps working through the same baseURL.
95
+ */
96
+ export declare function startMuseCodeAdapter(options?: AdapterOptions): Promise<RunningAdapter>;
97
+ export {};