machine-bridge-mcp 0.12.2 → 0.13.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.
- package/CHANGELOG.md +19 -0
- package/README.md +6 -4
- package/SECURITY.md +4 -0
- package/browser-extension/manifest.json +2 -2
- package/docs/AGENT_CONTEXT.md +16 -6
- package/docs/ARCHITECTURE.md +6 -4
- package/docs/AUDIT.md +13 -1
- package/docs/CLIENTS.md +1 -1
- package/docs/ENGINEERING.md +1 -0
- package/docs/LOCAL_AUTOMATION.md +1 -1
- package/docs/LOGGING.md +3 -1
- package/docs/OPERATIONS.md +5 -0
- package/docs/TESTING.md +4 -3
- package/package.json +3 -1
- package/src/local/agent-context.mjs +75 -18
- package/src/local/app-automation.mjs +15 -1
- package/src/local/capability-observer.mjs +56 -0
- package/src/local/default-instructions.mjs +21 -91
- package/src/local/network-proxy.mjs +34 -0
- package/src/local/process-sessions.mjs +7 -0
- package/src/local/project-package.mjs +234 -0
- package/src/local/relay-connection.mjs +26 -0
- package/src/local/runtime.mjs +21 -20
- package/src/shared/tool-catalog.json +4 -4
- package/src/worker/index.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.13.0 - 2026-07-13
|
|
4
|
+
|
|
5
|
+
### Automatic capability routing and observability
|
|
6
|
+
|
|
7
|
+
- Register bounded `package.*` commands from safe root `package.json` script names, while preserving explicit manifest override/deletion and never injecting script bodies. Windows uses a fixed `cmd.exe` wrapper for package-manager shims; Unix keeps direct executable argv. Extend default skill discovery to project `.codex/skills` and unrestricted `CODEX_HOME/skills` compatibility roots.
|
|
8
|
+
- Match installed applications by their actual names for every canonical-full task instead of requiring generic “app/window” words, with a bounded discovery cache to avoid repeated filesystem scans.
|
|
9
|
+
- Normalize a bounded set of common English inflections and Chinese workflow intents before skill/command ranking, and weight capability-name matches above incidental description overlap. This fixes Chinese selection of `skill-creator`, `smart-search-cli`, and `skill-installer` and prevents generic “create” wording from preferring unrelated design skills.
|
|
10
|
+
- Record privacy-preserving bootstrap and task-resolution telemetry in `server_info` and `project_overview`: counts, timestamps, source/load flags, selected capability metadata, and a runtime-keyed task fingerprint rather than raw task text. Suppress weak skill-overlap recommendations and clarify that the MCP host still controls whether the resolver and recommended tools are invoked.
|
|
11
|
+
|
|
12
|
+
### Process and network lifecycle
|
|
13
|
+
|
|
14
|
+
- Consolidate graceful process-tree termination plus forced escalation. Timeout, cancellation, and replacement now retain the escalation timer after the direct child exits, preventing a SIGTERM-resistant descendant with detached stdio from surviving as an orphan.
|
|
15
|
+
- Add relay support for standard `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` routing through a reviewed HTTP(S) proxy agent. Invalid proxy URLs or unsupported proxy protocols fail fast with corrective guidance; status exposes only direct/proxy/invalid route state and never proxy URLs or credentials.
|
|
16
|
+
|
|
17
|
+
### Architecture, tests, and documentation
|
|
18
|
+
|
|
19
|
+
- Extract package metadata/command discovery and capability observation into focused modules, add domain-to-adapter import-boundary checks, remove duplicate object fields, and centralize reused metadata parsing.
|
|
20
|
+
- Add behavior-level regression coverage for automatic package commands, direct `.codex/skills` compatibility, capability telemetry privacy, application-cache refresh, proxy selection/failure, and the direct-child-exits-first orphan-process boundary. Synchronize architecture, operations, logging, security, testing, and agent-context documentation.
|
|
21
|
+
|
|
3
22
|
## 0.12.2 - 2026-07-12
|
|
4
23
|
|
|
5
24
|
### Cross-platform persistence and browser reliability
|
package/README.md
CHANGED
|
@@ -128,7 +128,7 @@ Two lower-precedence virtual sources are generated in memory:
|
|
|
128
128
|
- `machine-bridge://defaults/working-agreements` supplies conservative cross-project rules for inspection, scoped changes, preservation of unrelated work, validation, security, Git discipline, and explicit authorization for deployment/publication/destructive operations.
|
|
129
129
|
- `machine-bridge://project-context/current` derives bounded facts from the active repository: target path, common project/build files, package-manager declarations and lockfiles, package script names, runtime constraints, documentation files, and CI entrypoints. It never injects package-script bodies or source contents and does not claim commands were validated.
|
|
130
130
|
|
|
131
|
-
No files are created or modified by this bootstrap. Explicit user and repository instructions load later and therefore override the defaults. `session_bootstrap` supplies the chain during MCP initialization; `agent_context` exposes every source and hash; `resolve_task_capabilities` refreshes project facts, instructions, skills, and
|
|
131
|
+
No files are created or modified by this bootstrap. Explicit user and repository instructions load later and therefore override the defaults. `session_bootstrap` supplies the chain during MCP initialization; `agent_context` exposes every source and hash; `resolve_task_capabilities` refreshes project facts, instructions, skills, commands, and installed-application matches for the current task. `server_info` and `project_overview` expose whether bootstrap and task resolution actually reached the local runtime without retaining raw task text.
|
|
132
132
|
|
|
133
133
|
Optional user-global preferences still live in `~/.config/machine-bridge-mcp/agent.json`:
|
|
134
134
|
|
|
@@ -153,7 +153,7 @@ To disable either automatic layer globally:
|
|
|
153
153
|
|
|
154
154
|
Repositories cannot disable these user-level controls. Editing instruction files or project metadata does not require a daemon restart; start a new MCP conversation or reconnect when initialization-time injection must be guaranteed from the beginning.
|
|
155
155
|
|
|
156
|
-
Skill discovery follows Codex-style progressive disclosure. Default roots
|
|
156
|
+
Skill discovery follows Codex-style progressive disclosure. Default project roots include target-to-project `.agents/skills` and `.codex/skills`; unrestricted policy also enables `~/.agents/skills`, `CODEX_HOME/skills` (normally `~/.codex/skills`), and the Unix admin root. Newly added or edited skills are found on the next resolver/list call without restarting the daemon. Safe root `package.json` script names are also exposed as `package.*` command aliases with bounded deterministic English/Chinese workflow-intent terms; explicit `.machine-bridge/agent.json` commands can override or delete them. Package-script bodies are never injected, but invoking one still runs repository-controlled code with the active local policy. See [Session instructions, defaults, skills, commands, and capability discovery](docs/AGENT_CONTEXT.md).
|
|
157
157
|
|
|
158
158
|
Under canonical `full`, Machine Bridge also exposes structured local automation:
|
|
159
159
|
|
|
@@ -170,7 +170,7 @@ machine-mcp browser status
|
|
|
170
170
|
|
|
171
171
|
Load the printed unpacked-extension directory once in Chrome, Edge, Brave, Vivaldi, or another compatible Chromium browser. The extension badge reports connection state, and clicking it opens the saved local pairing page. The local pairing token remains in owner-only state and the loopback pairing page; it is not returned through MCP. For a mass-market release, distribute the same extension as a signed browser-store build rather than asking end users to enable Developer mode. See [Local application and browser automation](docs/LOCAL_AUTOMATION.md).
|
|
172
172
|
|
|
173
|
-
Machine Bridge can discover, refresh, rank, and load capabilities automatically. The ChatGPT/MCP host still owns tool selection and approval, so the server cannot force a host to expose or invoke a recommended skill, command, app, or browser operation.
|
|
173
|
+
Machine Bridge can discover, refresh, rank, and load capabilities automatically. The ChatGPT/MCP host still owns tool selection and approval, so the server cannot force a host to expose or invoke a recommended skill, command, app, or browser operation. Check `server_info.observability.capability_routing` or `project_overview.capabilityRouting` to distinguish “the host never called the resolver” from “the resolver ran but found no relevant capability.”
|
|
174
174
|
|
|
175
175
|
## Policy controls
|
|
176
176
|
|
|
@@ -357,7 +357,7 @@ Managed jobs are non-interactive and persist independently of the MCP connection
|
|
|
357
357
|
|
|
358
358
|
### Processes
|
|
359
359
|
|
|
360
|
-
- `run_local_command` —
|
|
360
|
+
- `run_local_command` — fixed argv execution of an explicit or automatic package-script command
|
|
361
361
|
- `run_process` — one-shot argv execution without a shell
|
|
362
362
|
- `start_process`
|
|
363
363
|
- `read_process`
|
|
@@ -424,6 +424,8 @@ Default state roots:
|
|
|
424
424
|
|
|
425
425
|
State/config writes use owner-only temporary files, `fsync`, and atomic replacement. Exclusive locks are fully written before a same-directory hard-link claim becomes visible; lock ownership includes a token and process start time so PID reuse and lock replacement cannot silently transfer ownership. Only successfully read but invalid JSON is retained as a bounded corrupt backup; permission, symbolic-link, size, encoding, and I/O failures remain explicit. A custom state root must not equal, contain, or be contained by the selected workspace. Resource source paths are redacted from `status` output. Active managed-job plans are owner-only and are deleted after a terminal result; bounded redacted results are retained temporarily. Uninstall first acquires a state-root maintenance lock that blocks new profile/state operations and state-backed operations from already constructed managed-job/browser managers, stops the platform service and all verified workspace daemons before removing definitions, rechecks jobs and locks, then validates markers, canonical paths, workspace/source exclusions, and known contents before recursive deletion.
|
|
426
426
|
|
|
427
|
+
The relay honors standard `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment routing. Invalid or unsupported proxy configuration fails fast; runtime status reports only `direct`, `proxy`, or `invalid-proxy-configuration`, never proxy URLs or credentials.
|
|
428
|
+
|
|
427
429
|
Default foreground logs report authenticated relay readiness, readable persistent-degradation summaries, and recovery rather than raw WebSocket callbacks or JSON field dumps. Brief self-healing disconnects and close codes/reasons are debug-only. Stalled connection attempts have a deadline, and sustained-outage reminders use autonomous exponential backoff. Every per-tool event—including success, failure, cancellation, and slow-call timing—also appears only at `--log-level debug` or `--verbose`. Background services use `warn`, so ordinary tool outcomes and brief network changes do not fill daemon logs. Log messages and structured fields are bounded, secret-like keys and known token formats are redacted, and tool arguments/results are not written. See [docs/LOGGING.md](docs/LOGGING.md) and [docs/OPERATIONS.md](docs/OPERATIONS.md).
|
|
428
430
|
|
|
429
431
|
## Development and verification
|
package/SECURITY.md
CHANGED
|
@@ -57,6 +57,10 @@ Machine Bridge prepends a package-controlled built-in working-agreement block an
|
|
|
57
57
|
|
|
58
58
|
Repository and user instruction files remain untrusted content from the model's perspective. `agent_context` returns their text in deterministic precedence order but does not certify correctness or safety. A malicious `AGENTS.md`, custom instruction file, filename, script name, or `SKILL.md` can attempt prompt injection or recommend destructive operations. Use only trusted repositories and skill roots, keep host approvals/policy enabled, or isolate the bridge externally.
|
|
59
59
|
|
|
60
|
+
Automatic `package.*` commands expose only validated script names and fixed package-manager argv; they do not expose script bodies. Executing one still runs repository-controlled package-script code with local-user authority and is not a sandbox or trust upgrade.
|
|
61
|
+
|
|
62
|
+
Relay proxy selection honors `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY`. Only HTTP(S) proxy URLs are accepted. Proxy endpoints, credentials, and authorization headers are not returned through MCP or written to operational logs; status exposes only coarse route state.
|
|
63
|
+
|
|
60
64
|
Skill loading is non-executing. `load_local_skill` returns an entrypoint and bounded file inventory; scripts remain inert until a separate process or command tool is called. Symlinked skill directories are followed only after canonical path-policy validation, while symbolic-link `SKILL.md` entrypoints are rejected. Traversal, cycles, content, summaries, and inventory are bounded.
|
|
61
65
|
|
|
62
66
|
Registered commands are available only under direct-execution-capable policy. They use argv spawning rather than shell parsing, reject undeclared caller arguments, and enforce the manifest timeout as a ceiling. They are not an approval or sandbox boundary: a repository-controlled package script, interpreter, compiler, or executable can still run arbitrary code with local-user authority. Deeper manifests can override or remove inherited commands, so callers must obtain context for the actual target path rather than assuming the repository-root registry applies everywhere.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Machine Bridge Browser",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.13.0",
|
|
5
5
|
"description": "Connects the current Chromium browser profile to the local Machine Bridge runtime for user-authorized automation.",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"tabs",
|
|
@@ -29,5 +29,5 @@
|
|
|
29
29
|
"action": {
|
|
30
30
|
"default_title": "Machine Bridge Browser"
|
|
31
31
|
},
|
|
32
|
-
"version_name": "0.
|
|
32
|
+
"version_name": "0.13.0"
|
|
33
33
|
}
|
package/docs/AGENT_CONTEXT.md
CHANGED
|
@@ -14,7 +14,7 @@ This approximates a local coding agent without pretending that the MCP server ow
|
|
|
14
14
|
- `list_local_commands` returns effective registered commands.
|
|
15
15
|
- `run_local_command` executes a registered direct-argv command when policy permits.
|
|
16
16
|
|
|
17
|
-
Both stdio and remote Worker connection initialization attempt `session_bootstrap`. Its instruction text is appended to the MCP `initialize` result. Because a host may reuse one MCP connection across conversations, the explicit tool and per-task `resolve_task_capabilities` call remain necessary to refresh and reapply instructions reliably.
|
|
17
|
+
Both stdio and remote Worker connection initialization attempt `session_bootstrap`. Its instruction text is appended to the MCP `initialize` result. Because a host may reuse one MCP connection across conversations, the explicit tool and per-task `resolve_task_capabilities` call remain necessary to refresh and reapply instructions reliably. `server_info.observability.capability_routing` and `project_overview.capabilityRouting` report whether those calls reached the local runtime, their counts and timestamps, loaded-source flags, selected capability metadata, and a runtime-keyed HMAC task fingerprint. Raw task text is not retained.
|
|
18
18
|
|
|
19
19
|
## Useful defaults without configuration
|
|
20
20
|
|
|
@@ -171,9 +171,13 @@ Use `AGENTS.md` for durable facts a new contributor or agent must know: validate
|
|
|
171
171
|
Without explicit `skill_roots`, discovery scans:
|
|
172
172
|
|
|
173
173
|
- target-to-root `.agents/skills` directories;
|
|
174
|
+
- target-to-root `.codex/skills` compatibility directories;
|
|
174
175
|
- under unrestricted policy, `~/.agents/skills`;
|
|
176
|
+
- under unrestricted policy, `CODEX_HOME/skills` (normally `~/.codex/skills`);
|
|
175
177
|
- under unrestricted policy on Unix-like systems, `/etc/codex/skills`.
|
|
176
178
|
|
|
179
|
+
Canonical paths are deduplicated, so a compatibility symlink from `.agents/skills` to `.codex/skills` does not duplicate a skill.
|
|
180
|
+
|
|
177
181
|
A skill directory contains `SKILL.md` or `skill.md` with simple front matter:
|
|
178
182
|
|
|
179
183
|
```markdown
|
|
@@ -189,15 +193,21 @@ No persistent skill or project-context index is trusted as authoritative. `sessi
|
|
|
189
193
|
|
|
190
194
|
## Progressive disclosure and task selection
|
|
191
195
|
|
|
192
|
-
`agent_context` returns bounded skill metadata. `load_local_skill` returns full instructions only for one selected bundle. `resolve_task_capabilities` tokenizes the current task, ranks skill names/descriptions and command names/descriptions/argv, returns matches with scores, and loads the leading skill only when its relevance threshold is met.
|
|
196
|
+
`agent_context` returns bounded skill metadata. `load_local_skill` returns full instructions only for one selected bundle. `resolve_task_capabilities` tokenizes the current task, ranks skill names/descriptions and command names/descriptions/argv, returns matches with scores, and loads the leading skill only when its relevance threshold is met. Under canonical `full`, it also compares the task with installed application names on every call; application discovery is cached briefly and refreshed after a bounded interval.
|
|
197
|
+
|
|
198
|
+
Matching remains deterministic and local. Hyphens, underscores, dots, and whitespace are normalized; common English inflections are reduced to a small canonical form; and a bounded Chinese/English workflow vocabulary covers creation, improvement, installation, search, current/official documentation, verification, testing, frontend/design, browser/web, email, performance, and security intents. Capability-name token matches receive more weight than incidental words in a long description. This lets Chinese tasks select English-metadata skills such as `skill-creator`, `smart-search-cli`, and `skill-installer`, while avoiding the prior tie where generic “create” wording could select `frontend-design`. An explicitly named skill or registered command still receives the strongest deterministic boost.
|
|
199
|
+
|
|
200
|
+
This ranking is deterministic local assistance, not semantic certainty. Weak positive matches remain visible for diagnosis, but only a skill meeting the selection threshold is recommended for loading. The model must still evaluate whether the selected skill applies. Machine Bridge does not execute skill scripts implicitly and does not fabricate a dynamically named MCP tool per skill.
|
|
201
|
+
|
|
202
|
+
## Registered and automatic package commands
|
|
193
203
|
|
|
194
|
-
|
|
204
|
+
`run_local_command` spawns the registered argv directly rather than parsing caller text through a Machine Bridge shell. Manifest commands control working directory, timeout ceiling, and whether caller arguments are accepted. A caller may reduce but not increase the timeout.
|
|
195
205
|
|
|
196
|
-
|
|
206
|
+
When the project root has a valid, non-symbolic-link `package.json`, safe script names are exposed automatically as `package.<normalized-name>` commands. The manager is selected from the validated `packageManager` field or one unambiguous lockfile; npm is used only when no manager signal exists. Conflicting lockfiles suppress automatic commands until the ambiguity is resolved. Only script names and bounded built-in workflow-intent terms are exposed. Script bodies and dependency values are not injected. Automatic commands accept no caller-supplied extra arguments, use the project root as cwd, and have a bounded timeout. Explicit manifests are applied afterward, so they can override or delete an automatic command by name.
|
|
197
207
|
|
|
198
|
-
|
|
208
|
+
On Unix-like systems the package-manager executable is spawned directly. On Windows, npm/pnpm/yarn/bun are command shims rather than native executables, so the bridge uses a fixed `cmd.exe /d /s /c` command assembled only from an allowlisted manager and a validated script name; callers still cannot append arguments or shell syntax. This wrapper is transport compatibility, not a trust boundary.
|
|
199
209
|
|
|
200
|
-
|
|
210
|
+
Directly invoking `npm run`, `pnpm run`, `yarn run`, or `bun run` does not make the repository script itself shell-free: package managers execute repository-controlled script bodies according to their own semantics. All registered commands are workflow aliases, not an approval boundary or sandbox. Package scripts, interpreters, compilers, and executables retain local-user authority. Use `review` or `edit`, or external VM/container isolation, for untrusted content.
|
|
201
211
|
|
|
202
212
|
## Recommended host workflow
|
|
203
213
|
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -44,9 +44,9 @@ A canonical workspace receives an independent profile, Worker name, secret set,
|
|
|
44
44
|
|
|
45
45
|
### Agent context and capability resolver
|
|
46
46
|
|
|
47
|
-
`AgentContextManager` discovers the nearest Git/workspace scope, applies the user configuration and hierarchical `.machine-bridge/agent.json` files, selects built-in/user/root-to-target instructions, discovers bounded filesystem skills, and resolves registered commands. `default-instructions.mjs` supplies a versioned in-package working-agreement block and derives a small virtual project-context block from root filenames and bounded metadata. It reads package script names but not bodies, does not inspect dependency values or source contents, executes nothing, and writes no user/repository files. A global `model_instructions_file` is a separate user-designated session source and cannot be overridden by a project.
|
|
47
|
+
`AgentContextManager` discovers the nearest Git/workspace scope, applies the user configuration and hierarchical `.machine-bridge/agent.json` files, selects built-in/user/root-to-target instructions, discovers bounded filesystem skills, and resolves registered commands. `project-package.mjs` owns no-follow package metadata parsing, package-manager selection (including fail-closed conflicting-lockfile handling), script-name normalization, bounded workflow-intent aliases, and automatic `package.*` command construction so instruction rendering and command execution do not duplicate package parsing. `default-instructions.mjs` supplies a versioned in-package working-agreement block and derives a small virtual project-context block from root filenames and bounded metadata. It reads package script names but not bodies, does not inspect dependency values or source contents, executes nothing, and writes no user/repository files. A global `model_instructions_file` is a separate user-designated session source and cannot be overridden by a project.
|
|
48
48
|
|
|
49
|
-
`session_bootstrap` is requested during both stdio and remote MCP initialization. The Worker delegates this read to the connected daemon with a short bounded timeout; failure falls back to static server instructions rather than blocking initialization indefinitely. `resolve_task_capabilities` performs a fresh deterministic scan, rebuilds automatic project facts, and ranks skill/command metadata for the current task. Application and browser capability metadata is added by `LocalRuntime
|
|
49
|
+
`session_bootstrap` is requested during both stdio and remote MCP initialization. The Worker delegates this read to the connected daemon with a short bounded timeout; failure falls back to static server instructions rather than blocking initialization indefinitely. `resolve_task_capabilities` performs a fresh deterministic scan, rebuilds automatic project facts, and ranks skill/command metadata for the current task. Application and browser capability metadata is added by `LocalRuntime`; installed application inventory uses a short bounded cache. `CapabilityObserver` records only counts, timestamps, source flags, selected metadata, match counts, recommended tool names, and a runtime-keyed task fingerprint so operators can verify routing without creating a task-content log.
|
|
50
50
|
|
|
51
51
|
The MCP catalog remains static: local skills and commands do not become dynamically named tools. This avoids stale host catalog caches and keeps Worker/stdio schema parity. Progressive disclosure separates discovery, instruction loading, and execution authority. A refresh fingerprint is descriptive rather than a cache-validity guarantee.
|
|
52
52
|
|
|
@@ -208,7 +208,9 @@ Managed jobs use the same argv/environment primitives but a different lifecycle.
|
|
|
208
208
|
|
|
209
209
|
## Daemon reconnect and replacement
|
|
210
210
|
|
|
211
|
-
The local `RelayConnection` treats transport construction, transport open, authenticated readiness, and outage recovery as separate states.
|
|
211
|
+
The local `RelayConnection` treats proxy selection, transport construction, transport open, authenticated readiness, and outage recovery as separate states. `network-proxy.mjs` maps WebSocket targets to standard HTTP(S) environment-proxy resolution, honors `NO_PROXY`, rejects non-HTTP(S) proxy schemes, and creates the proxy agent without exposing its URL or credentials. Invalid proxy configuration is a fatal configuration error rather than a retryable outage.
|
|
212
|
+
|
|
213
|
+
The local `RelayConnection` otherwise treats transport construction, transport open, authenticated readiness, and outage recovery as separate states. A connection-attempt deadline terminates sockets stuck in `CONNECTING`; after WebSocket open it sends `hello` and reports readiness only after `hello_ack`. A missing acknowledgement reaches a distinct handshake deadline and the candidate is terminated. Once ready, application heartbeats require inbound activity; a silent half-open socket is terminated and reconnected. Outage reminders run on their own exponential-backoff timer rather than depending on another transport callback.
|
|
212
214
|
|
|
213
215
|
Reconnect uses bounded exponential backoff with jitter. Brief self-healing interruptions are debug-only. An unresolved outage is promoted to a rate-limited warning after a grace period, and recovery produces one summary. Raw close codes and reason strings remain debug-only.
|
|
214
216
|
|
|
@@ -230,7 +232,7 @@ OAuth metadata is pruned on access. Expired codes/tokens, old throttling records
|
|
|
230
232
|
|
|
231
233
|
## Observability
|
|
232
234
|
|
|
233
|
-
Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status, policy origin/revision, managed-job counts, resource alias names without paths or values,
|
|
235
|
+
Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status, policy origin/revision, managed-job counts, resource alias names without paths or values, daemon/relay-advertised tool counts, relay route state without endpoint details, and privacy-preserving capability-routing evidence. It explicitly reports that the host-exposed subset is unknown to the server. `diagnose_runtime` runs fixed local probes and explicitly reports that its own request reached the daemon.
|
|
234
236
|
|
|
235
237
|
Foreground logging defaults to `info`; autostart uses `warn`. Authenticated readiness, persistent degradation, and recovery are user-visible state transitions. Brief relay interruptions, raw transport close details, retry timing, and all per-tool starts/successes/failures/cancellations/durations are debug-only. Unexpected local and Worker infrastructure errors are reduced to classes. Messages, strings, arrays, object depth/key counts, and serialized fields are bounded.
|
|
236
238
|
|
package/docs/AUDIT.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
# Engineering and security audit
|
|
2
2
|
|
|
3
|
-
This document records the cross-cutting audit initiated for version 0.12.0
|
|
3
|
+
This document records the cross-cutting audit initiated for version 0.12.0, the 0.12.2 cross-platform CI follow-up, and the 0.13.0 architecture/automatic-routing follow-up. It complements, but does not replace, the continuously enforced contracts in `SECURITY.md`, `docs/ENGINEERING.md`, and the test suite.
|
|
4
|
+
|
|
5
|
+
## 2026-07-13 architecture and automatic-routing follow-up
|
|
6
|
+
|
|
7
|
+
A new behavior-level review found that the existing global/project instruction path was functioning, but three gaps explained weak user-visible automation: direct `.codex/skills` compatibility depended on user-created symlinks, root package scripts were described but not executable through the registered-command surface, and the runtime provided no proof that the host had called bootstrap or task resolution. The review added direct compatibility roots, bounded automatic package commands, and privacy-preserving routing telemetry while retaining the host/model invocation boundary.
|
|
8
|
+
|
|
9
|
+
The review also reproduced a lifecycle defect not covered by the previous descendant test: when a direct child exited after group `SIGTERM` while a detached-stdio descendant ignored the signal, `runProcess` cleared the pending `SIGKILL` escalation and left the descendant alive. Timeout/cancellation/replacement now use one escalation primitive whose forced phase remains referenced independently of direct-child tracking. A dedicated real-process regression covers that exact ordering.
|
|
10
|
+
|
|
11
|
+
Relay routing now honors standard HTTP(S) proxy environment variables and `NO_PROXY`, rejects invalid/unsupported proxy configuration before reconnect, and exposes only coarse route state. Package metadata parsing and capability observation were extracted from orchestration code, and architecture tests now reject domain-to-adapter imports.
|
|
12
|
+
|
|
13
|
+
A real macOS stdio smoke test used the user's actual global instructions, repository instructions, installed skills, package scripts, and installed applications with an isolated temporary state root. Initialization advertised 49 tools and contained all four instruction layers. A Chinese skill-creation task selected `skill-creator` at score 16 and loaded 22,047 bytes of instructions; 42 automatic package commands were discovered and `package.version-check` executed with exit code 0; 100 applications were discovered, `Chess` matched at score 15, opened in the background, was observed running, then quit cleanly. Capability telemetry recorded bootstrap plus three task resolutions without retaining raw task text.
|
|
14
|
+
|
|
15
|
+
The same review reproduced two daily-use ranking failures before the fix: Chinese tasks did not match English skill metadata, and an English skill-creation request tied several generic “create” descriptions and selected `frontend-design` alphabetically. Cross-platform CI then exposed a separate Windows execution defect: automatic commands spawned `npm` as if it were a native executable, but Windows provides `npm.cmd`, producing `spawn npm ENOENT`. Windows package commands now use one fixed command-shell wrapper built from allowlisted manager and validated script names. Bounded bilingual intent normalization plus capability-name weighting corrected both cases. Residual limits remain external: the MCP host can filter initialization instructions or tools, a package manager can execute arbitrary repository script bodies, HTTP(S) proxy support is not SOCKS support, and OS process-tree guarantees remain platform dependent. In the observed remote ChatGPT path, the 0.12.2 relay advertised 49 tools while the host made only 11 available to this conversation, demonstrating that server-side discovery cannot by itself guarantee host invocation.
|
|
4
16
|
|
|
5
17
|
## 0.12.2 follow-up findings
|
|
6
18
|
|
package/docs/CLIENTS.md
CHANGED
|
@@ -54,7 +54,7 @@ The MCP specification defines stdio and Streamable HTTP as standard transports.
|
|
|
54
54
|
|
|
55
55
|
## Automatic capability selection
|
|
56
56
|
|
|
57
|
-
MCP initialization and `resolve_task_capabilities` give the host a current view of conservative built-in working agreements, bounded project facts, explicit global/project instructions, skills,
|
|
57
|
+
MCP initialization and `resolve_task_capabilities` give the host a current view of conservative built-in working agreements, bounded project facts, explicit global/project instructions, skills, explicit/automatic package commands, applications, and browser capability. The resolver rescans rather than relying on a stale dynamic tool list and can return the best matching skill instructions in one call. No instruction file is required for the default layers, and no repository file is written automatically.
|
|
58
58
|
|
|
59
59
|
The host still owns the agent loop. ChatGPT web may use the recommendation automatically, ask for confirmation, expose only part of the catalog, or ignore server instructions. No MCP implementation can guarantee automatic invocation from the server side. Machine Bridge models that limitation explicitly instead of treating a recommendation as execution.
|
|
60
60
|
|
package/docs/ENGINEERING.md
CHANGED
|
@@ -46,6 +46,7 @@ CLI, service, release, and deployment orchestration
|
|
|
46
46
|
Rules:
|
|
47
47
|
|
|
48
48
|
- Transport lifecycle, domain execution, persistence, and presentation are separate modules.
|
|
49
|
+
- Domain modules must not import CLI, service, stdio, or relay adapters. Architecture tests enforce this dependency direction for agent context, package metadata, default instructions, capability observation, application automation, process sessions, and proxy selection.
|
|
49
50
|
- Pure classification and normalization functions are exported and tested directly when practical.
|
|
50
51
|
- Adapters may translate data but should not duplicate policy or schemas.
|
|
51
52
|
- Every protocol control message emitted by one side must be explicitly accepted, rejected, or version-gated by the other side, with an end-to-end contract test covering the message name and semantics.
|
package/docs/LOCAL_AUTOMATION.md
CHANGED
|
@@ -89,7 +89,7 @@ Menu-bar and menu subtrees are not recursively expanded by default. This keeps m
|
|
|
89
89
|
|
|
90
90
|
## Capability discovery and automatic selection
|
|
91
91
|
|
|
92
|
-
`resolve_task_capabilities` rescans instruction files, skills,
|
|
92
|
+
`resolve_task_capabilities` rescans instruction files, skills, explicit/automatic package commands, and relevant local automation metadata on every call. It ranks matching skills and commands, optionally loads the best skill, and compares every canonical-full task with cached installed-application names, so a task that directly names an app does not need generic “app/window” wording. Application inventory is refreshed after a bounded cache interval.
|
|
93
93
|
|
|
94
94
|
This is the strongest reliable server-side automation boundary available through MCP: discovery, refresh, ranking, and progressive skill loading are automatic. The MCP host still owns the model loop and decides whether a recommended tool is exposed, approved, or invoked. Machine Bridge cannot force ChatGPT web or another host to make a call that the host declines.
|
|
95
95
|
|
package/docs/LOGGING.md
CHANGED
|
@@ -39,6 +39,8 @@ A TCP/WebSocket `open` event is only transport availability. The daemon is repor
|
|
|
39
39
|
|
|
40
40
|
Brief network interruptions are expected on laptop network changes, Worker deployment, proxy rotation, and ordinary internet transport. They are handled as follows:
|
|
41
41
|
|
|
42
|
+
- proxy selection records only `direct` or `proxy` at debug level; proxy URLs, usernames, passwords, and headers are never log fields;
|
|
43
|
+
- invalid proxy URLs or unsupported schemes fail fast with a sanitized corrective error rather than repeated reconnect warnings;
|
|
42
44
|
- the raw close code, close reason, error class, connected duration, and retry delay are debug-only;
|
|
43
45
|
- a brief interruption that reconnects within the grace period produces no `info` or `warn` line;
|
|
44
46
|
- an outage that remains unresolved for 10 seconds produces a user-readable warning stating the duration, reconnect attempt count, classified cause, and automatic recovery behavior;
|
|
@@ -118,7 +120,7 @@ Each managed job has owner-only runner diagnostic logs. Child-step output is ret
|
|
|
118
120
|
|
|
119
121
|
Machine Bridge does not classify filenames as sensitive and does not block credential-looking names under canonical `full`. An MCP host, connector, model provider, desktop application, operating system, or endpoint-security layer may independently reject a request before it reaches Machine Bridge.
|
|
120
122
|
|
|
121
|
-
Use `server_info`, `machine-mcp status`, `machine-mcp doctor`, and `diagnose_runtime` to distinguish local policy from host-side enforcement. Changing the Machine Bridge profile cannot override another layer.
|
|
123
|
+
Use `server_info`, `project_overview`, `machine-mcp status`, `machine-mcp doctor`, and `diagnose_runtime` to distinguish local policy from host-side enforcement. Capability-routing status is returned on demand rather than written as task logs; it stores a runtime-keyed task fingerprint, not raw task text. Changing the Machine Bridge profile cannot override another layer.
|
|
122
124
|
|
|
123
125
|
## Adding or changing logs
|
|
124
126
|
|
package/docs/OPERATIONS.md
CHANGED
|
@@ -15,6 +15,9 @@ machine-mcp service status
|
|
|
15
15
|
| Result | Interpretation |
|
|
16
16
|
|---|---|
|
|
17
17
|
| `server_info` reports full and all relay tools, but the current session UI exposes fewer tools | Host/connector post-relay filtering; Machine Bridge cannot enumerate or override that subset |
|
|
18
|
+
| `capability_routing.bootstrap_observed` is false | The current local runtime has not received `session_bootstrap`; reconnect or inspect host initialization handling |
|
|
19
|
+
| `task_resolution_observed` is false after a substantive task | The host/model did not call `resolve_task_capabilities`; server-side discovery cannot force that host decision |
|
|
20
|
+
| Task resolution ran but all match counts are zero | The resolver ran successfully but found no sufficiently relevant local skill, command, or application |
|
|
18
21
|
| No structured result because the host rejects the call | Host/connector approval or safety layer, or transport before daemon delivery |
|
|
19
22
|
| `mcp-host-to-daemon` passes but `local-filesystem` fails | Local state/runtime permissions, disk policy, sandbox, or endpoint security |
|
|
20
23
|
| Filesystem passes but `local-process-spawn` fails | Local executable policy, endpoint security, OS permissions, or damaged Node runtime |
|
|
@@ -30,6 +33,8 @@ A brief relay interruption is retried automatically and is visible only with `--
|
|
|
30
33
|
|
|
31
34
|
Use `--verbose` only when close codes, close reasons, heartbeat timeouts, and retry delays are needed for diagnosis. A close code of 1006 means the transport ended without a normal close handshake; it does not by itself identify the cause.
|
|
32
35
|
|
|
36
|
+
The daemon honors `HTTPS_PROXY`/`HTTP_PROXY` and `NO_PROXY` through standard environment-proxy resolution. `wss:` targets use HTTPS proxy selection and `ws:` targets use HTTP proxy selection. Only HTTP and HTTPS proxy URLs are accepted. Invalid URLs or unsupported protocols fail startup with corrective guidance instead of entering the reconnect loop. `server_info.runtime.relay.network_route` reports only `direct`, `proxy`, or `invalid-proxy-configuration`; proxy endpoints and credentials are never returned or logged.
|
|
37
|
+
|
|
33
38
|
## Browser extension setup and diagnosis
|
|
34
39
|
|
|
35
40
|
The full-profile daemon starts the loopback browser broker automatically. Install/pair the packaged extension once:
|
package/docs/TESTING.md
CHANGED
|
@@ -17,11 +17,12 @@ The suite includes:
|
|
|
17
17
|
- generated Cloudflare Worker types and strict TypeScript checking, including unused-local and unused-parameter rejection;
|
|
18
18
|
- recursive syntax validation for every JavaScript file under the shipped/runtime/test roots plus the shell wrapper;
|
|
19
19
|
- shared tool-catalog schema, annotation, and profile-inventory checks;
|
|
20
|
-
- default working-agreement injection without user files, bounded automatic project metadata, script-body non-disclosure, user-global opt-out, repository opt-out rejection, global `model_instructions_file` injection in stdio/remote initialization, hierarchical precedence, live project/skill rescanning and fingerprints, automatic task ranking/loading, command override/removal, direct argv handling, timeout ceilings, and execution-profile denial;
|
|
20
|
+
- default working-agreement injection without user files, bounded automatic project metadata, script-body non-disclosure, user-global opt-out, repository opt-out rejection, global `model_instructions_file` injection in stdio/remote initialization, hierarchical precedence, `.agents/skills` and `.codex/skills` compatibility discovery, live project/skill rescanning and fingerprints, automatic task ranking/loading with English inflection normalization, bounded Chinese/English intent aliases, capability-name weighting, and selected-skill instruction loading; automatic `package.*` commands with English/Chinese workflow-intent matching and Windows command-shim execution, explicit command override/removal, direct argv handling, timeout ceilings, runtime-keyed routing-telemetry privacy, and execution-profile denial;
|
|
21
21
|
- concurrent complete-before-visible lock claims, atomic replacement under active readers, malformed-lock grace, snapshot/token-safe reclamation, absolute-age expiry, PID-reuse detection, and bounded startup-lock waiting;
|
|
22
22
|
- foreground takeover of active and orphaned background daemons, legacy lock identification from canonicalized process arguments, foreground-process protection, actual-PID exit waiting, POSIX non-escalating timeout behavior, Windows verified-daemon stop semantics, daemon lock mode/version/process-start metadata, launchd service-target semantics, and silent idempotent duplicate service starts;
|
|
23
23
|
- fail-closed service lifecycle ordering for provider-stop, all-workspace daemon-stop, and definition removal, including platform/daemon/removal failure injection and normalized macOS/systemd/Windows results;
|
|
24
|
-
- machine-level browser-broker ownership/client proxying, authenticated extension origin/subprotocol, non-cacheable local pairing, pairing-token non-disclosure, resource-backed upload routing, broker result redaction, pre-registered runtime-handshake listeners,
|
|
24
|
+
- machine-level browser-broker ownership/client proxying, authenticated extension origin/subprotocol, non-cacheable local pairing, pairing-token non-disclosure, resource-backed upload routing, broker result redaction, pre-registered runtime-handshake listeners, bounded socket/HTTP waits, installed-application discovery caching, and name-based task matching;
|
|
25
|
+
- relay environment-proxy direct/bypass/agent selection, unsupported proxy rejection, fail-fast invalid configuration, and route observability without endpoint or credential disclosure;
|
|
25
26
|
- canonical path and symbolic-link escape tests;
|
|
26
27
|
- relative-path privacy and error-path redaction tests;
|
|
27
28
|
- atomic create/update, optimistic hash, exact edit, and patch transaction tests;
|
|
@@ -31,7 +32,7 @@ The suite includes:
|
|
|
31
32
|
- nested Git repository detection and helper suppression;
|
|
32
33
|
- author-email privacy in `git_log`;
|
|
33
34
|
- isolated command HOME/temp/cache behavior;
|
|
34
|
-
- one-shot timeout, descendant process-group/tree termination including descendants that ignore graceful shutdown, cancellation, and process-session interaction;
|
|
35
|
+
- one-shot timeout, descendant process-group/tree termination including descendants that ignore graceful shutdown after the direct child has already exited, cancellation, and process-session interaction;
|
|
35
36
|
- layered fixed runtime diagnostics for filesystem, direct process, shell, managed-job storage, and resource availability;
|
|
36
37
|
- local resource CLI registration, permission checks, dynamic reload, state-path redaction, and content non-disclosure;
|
|
37
38
|
- real Ed25519 and RSA generation, idempotent reuse, public/private correspondence, mode enforcement, incomplete/mismatched/symlink rejection, and private-content non-disclosure;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "machine-bridge-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Cross-client MCP bridge for local agent context, structured browser and application automation, files, Git, processes, resources, and durable jobs over stdio or OAuth relay.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -75,6 +75,8 @@
|
|
|
75
75
|
"app-automation:live-test": "node tests/app-automation-test.mjs --live-macos"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
|
+
"https-proxy-agent": "9.1.0",
|
|
79
|
+
"proxy-from-env": "2.1.0",
|
|
78
80
|
"wrangler": "4.110.0",
|
|
79
81
|
"ws": "8.21.0"
|
|
80
82
|
},
|
|
@@ -3,6 +3,7 @@ import { constants as fsConstants } from "node:fs";
|
|
|
3
3
|
import { lstat, open, opendir, realpath, stat } from "node:fs/promises";
|
|
4
4
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { createBuiltinInstruction, discoverAutomaticProjectInstruction } from "./default-instructions.mjs";
|
|
6
|
+
import { automaticPackageCommands, readProjectPackageMetadata } from "./project-package.mjs";
|
|
6
7
|
|
|
7
8
|
const CONFIG_RELATIVE_PATH = join(".machine-bridge", "agent.json");
|
|
8
9
|
const GLOBAL_CONFIG_RELATIVE_PATH = join(".config", "machine-bridge-mcp", "agent.json");
|
|
@@ -24,6 +25,35 @@ const MAX_COMMAND_ARGV = 128;
|
|
|
24
25
|
const MAX_COMMAND_ARGUMENT_BYTES = 256 * 1024;
|
|
25
26
|
const COMMAND_NAME_PATTERN = /^[a-z][a-z0-9._-]{0,63}$/;
|
|
26
27
|
const TOKEN_STOP_WORDS = new Set(["the", "and", "for", "with", "from", "this", "that", "use", "using", "into", "of", "to", "a", "an", "in", "on", "is", "are", "or", "及", "和", "的", "了", "在", "用", "使用", "进行", "根据"]);
|
|
28
|
+
const ENGLISH_TOKEN_CANONICAL = new Map([
|
|
29
|
+
["created", "create"], ["creating", "create"], ["creation", "create"], ["creator", "create"],
|
|
30
|
+
["improved", "improve"], ["improving", "improve"], ["improvement", "improve"],
|
|
31
|
+
["installed", "install"], ["installing", "install"], ["installation", "install"], ["installer", "install"],
|
|
32
|
+
["searched", "search"], ["searching", "search"], ["finder", "find"],
|
|
33
|
+
["documentation", "docs"], ["document", "docs"], ["documents", "docs"],
|
|
34
|
+
["validated", "verify"], ["validate", "verify"], ["validation", "verify"], ["verification", "verify"],
|
|
35
|
+
["factcheck", "verify"], ["fact-check", "verify"], ["testing", "test"], ["tests", "test"],
|
|
36
|
+
["building", "build"], ["built", "build"], ["deployment", "deploy"], ["deployed", "deploy"],
|
|
37
|
+
]);
|
|
38
|
+
const HAN_TOKEN_ALIASES = Object.freeze([
|
|
39
|
+
[/创建|新建|编写/u, ["create", "creator"]],
|
|
40
|
+
[/改进|优化|更新|维护/u, ["improve", "update"]],
|
|
41
|
+
[/技能/u, ["skill"]],
|
|
42
|
+
[/安装/u, ["install", "installer"]],
|
|
43
|
+
[/部署/u, ["deploy", "deployment"]],
|
|
44
|
+
[/查找|搜索|检索/u, ["search", "find"]],
|
|
45
|
+
[/最新|当前/u, ["latest", "current"]],
|
|
46
|
+
[/官方/u, ["official"]],
|
|
47
|
+
[/文档|资料/u, ["docs", "documentation"]],
|
|
48
|
+
[/事实核查|核查|验证|校验/u, ["verify", "factcheck"]],
|
|
49
|
+
[/测试/u, ["test"]],
|
|
50
|
+
[/前端/u, ["frontend"]],
|
|
51
|
+
[/设计/u, ["design"]],
|
|
52
|
+
[/邮件|邮箱/u, ["email"]],
|
|
53
|
+
[/浏览器|网页|网站/u, ["browser", "web"]],
|
|
54
|
+
[/性能/u, ["performance"]],
|
|
55
|
+
[/安全|漏洞/u, ["security", "audit"]],
|
|
56
|
+
]);
|
|
27
57
|
const CONFIG_KEYS = new Set(["version", "builtin_instructions", "automatic_project_context", "model_instructions_file", "instruction_files", "instruction_max_bytes", "skill_roots", "commands"]);
|
|
28
58
|
const COMMAND_KEYS = new Set(["description", "argv", "cwd", "timeout_seconds", "allow_extra_args"]);
|
|
29
59
|
|
|
@@ -117,7 +147,7 @@ export class AgentContextManager {
|
|
|
117
147
|
.sort((a, b) => b.score - a.score || a.skill.name.localeCompare(b.skill.name))
|
|
118
148
|
.slice(0, maxSkills);
|
|
119
149
|
const commandMatches = [...state.commands.values()]
|
|
120
|
-
.map((command) => ({ command, score: relevanceScore(task,
|
|
150
|
+
.map((command) => ({ command, score: relevanceScore(task, commandMatchText(command), command.name) }))
|
|
121
151
|
.filter((item) => item.score > 0)
|
|
122
152
|
.sort((a, b) => b.score - a.score || a.command.name.localeCompare(b.command.name))
|
|
123
153
|
.slice(0, 20);
|
|
@@ -127,7 +157,11 @@ export class AgentContextManager {
|
|
|
127
157
|
const content = await readRegularUtf8(selected.entrypoint, MAX_SKILL_ENTRY_BYTES, "skill entrypoint");
|
|
128
158
|
selectedSkill = { ...publicSkill(selected, this.displayPath), instructions: content.text };
|
|
129
159
|
}
|
|
130
|
-
const recommendedTools = recommendTools(task,
|
|
160
|
+
const recommendedTools = recommendTools(task, {
|
|
161
|
+
commandsAvailable: state.commands.size > 0,
|
|
162
|
+
commandRelevant: (commandMatches[0]?.score || 0) >= 3,
|
|
163
|
+
skillRelevant: Boolean(selected),
|
|
164
|
+
});
|
|
131
165
|
const refresh = capabilityFingerprint(state, discovered.skills);
|
|
132
166
|
return {
|
|
133
167
|
task,
|
|
@@ -239,7 +273,7 @@ export class AgentContextManager {
|
|
|
239
273
|
scopeRoot,
|
|
240
274
|
instructionFiles: [...DEFAULT_INSTRUCTION_FILES],
|
|
241
275
|
instructionMaxBytes: DEFAULT_INSTRUCTION_MAX_BYTES,
|
|
242
|
-
skillRoots: defaultSkillRoots(directories, this.home, this.policy.unrestrictedPaths === true),
|
|
276
|
+
skillRoots: defaultSkillRoots(directories, this.home, this.codexHome, this.policy.unrestrictedPaths === true),
|
|
243
277
|
commands: new Map(),
|
|
244
278
|
builtinInstructionsEnabled: true,
|
|
245
279
|
automaticProjectContextEnabled: true,
|
|
@@ -253,6 +287,9 @@ export class AgentContextManager {
|
|
|
253
287
|
instructionsTruncated: false,
|
|
254
288
|
};
|
|
255
289
|
|
|
290
|
+
const packageMetadata = await readProjectPackageMetadata(scopeRoot, () => this.throwIfCancelled(context));
|
|
291
|
+
state.commands = automaticPackageCommands(packageMetadata, scopeRoot);
|
|
292
|
+
|
|
256
293
|
if (this.home) {
|
|
257
294
|
const globalConfig = join(this.home, GLOBAL_CONFIG_RELATIVE_PATH);
|
|
258
295
|
const config = await readOptionalConfig(globalConfig, this.home, false);
|
|
@@ -488,9 +525,14 @@ function directoriesBetween(root, leaf) {
|
|
|
488
525
|
return result.reverse();
|
|
489
526
|
}
|
|
490
527
|
|
|
491
|
-
function defaultSkillRoots(directories, home, unrestricted) {
|
|
492
|
-
const roots = [
|
|
528
|
+
function defaultSkillRoots(directories, home, codexHome, unrestricted) {
|
|
529
|
+
const roots = [];
|
|
530
|
+
for (const directory of [...directories].reverse()) {
|
|
531
|
+
roots.push(resolve(directory, ".agents", "skills"));
|
|
532
|
+
roots.push(resolve(directory, ".codex", "skills"));
|
|
533
|
+
}
|
|
493
534
|
if (unrestricted && home) roots.push(resolve(home, ".agents", "skills"));
|
|
535
|
+
if (unrestricted && codexHome) roots.push(resolve(codexHome, "skills"));
|
|
494
536
|
if (unrestricted && process.platform !== "win32") roots.push(resolve("/etc/codex/skills"));
|
|
495
537
|
return [...new Set(roots)];
|
|
496
538
|
}
|
|
@@ -718,13 +760,19 @@ function capabilityFingerprint(state, skills) {
|
|
|
718
760
|
}));
|
|
719
761
|
}
|
|
720
762
|
|
|
763
|
+
function commandMatchText(command) {
|
|
764
|
+
return `${command.name} ${command.description} ${command.argv.join(" ")} ${command.searchTerms || ""}`;
|
|
765
|
+
}
|
|
766
|
+
|
|
721
767
|
function relevanceScore(task, candidate, identity = "") {
|
|
722
768
|
const taskTokens = tokenize(task);
|
|
723
769
|
const candidateTokens = tokenize(candidate);
|
|
724
770
|
if (!taskTokens.size || !candidateTokens.size) return 0;
|
|
771
|
+
const identityTokens = tokenize(identity);
|
|
725
772
|
let score = 0;
|
|
726
773
|
for (const token of taskTokens) {
|
|
727
774
|
if (candidateTokens.has(token)) score += token.length >= 6 ? 2 : 1;
|
|
775
|
+
if (identityTokens.has(token)) score += token.length >= 6 ? 4 : 3;
|
|
728
776
|
}
|
|
729
777
|
const taskComparable = comparableText(task);
|
|
730
778
|
const candidateComparable = comparableText(candidate);
|
|
@@ -747,33 +795,40 @@ function tokenize(value) {
|
|
|
747
795
|
const tokens = new Set();
|
|
748
796
|
for (const raw of text.match(/[a-z0-9_][a-z0-9_.-]{1,}/g) || []) {
|
|
749
797
|
const token = raw.replace(/^[.-]+|[.-]+$/g, "");
|
|
750
|
-
|
|
751
|
-
for (const part of token.split(/[._-]+/))
|
|
752
|
-
if (part.length >= 2 && !TOKEN_STOP_WORDS.has(part)) tokens.add(part);
|
|
753
|
-
}
|
|
798
|
+
addToken(tokens, token);
|
|
799
|
+
for (const part of token.split(/[._-]+/)) addToken(tokens, part);
|
|
754
800
|
}
|
|
755
801
|
for (const sequence of text.match(/[\p{Script=Han}]{1,}/gu) || []) {
|
|
756
|
-
|
|
802
|
+
addToken(tokens, sequence);
|
|
757
803
|
const minimumSize = sequence.length === 1 ? 1 : 2;
|
|
758
804
|
for (let size = minimumSize; size <= Math.min(3, sequence.length); size += 1) {
|
|
759
|
-
for (let index = 0; index + size <= sequence.length; index += 1)
|
|
760
|
-
const token = sequence.slice(index, index + size);
|
|
761
|
-
if (!TOKEN_STOP_WORDS.has(token)) tokens.add(token);
|
|
762
|
-
}
|
|
805
|
+
for (let index = 0; index + size <= sequence.length; index += 1) addToken(tokens, sequence.slice(index, index + size));
|
|
763
806
|
}
|
|
764
807
|
}
|
|
808
|
+
for (const [pattern, aliases] of HAN_TOKEN_ALIASES) {
|
|
809
|
+
if (!pattern.test(text)) continue;
|
|
810
|
+
for (const alias of aliases) addToken(tokens, alias);
|
|
811
|
+
}
|
|
765
812
|
return tokens;
|
|
766
813
|
}
|
|
767
814
|
|
|
768
|
-
function
|
|
815
|
+
function addToken(tokens, raw) {
|
|
816
|
+
const token = String(raw || "").replace(/^[.-]+|[.-]+$/g, "");
|
|
817
|
+
if (token.length < 2 || TOKEN_STOP_WORDS.has(token)) return;
|
|
818
|
+
tokens.add(token);
|
|
819
|
+
const canonical = ENGLISH_TOKEN_CANONICAL.get(token);
|
|
820
|
+
if (canonical) tokens.add(canonical);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function recommendTools(task, { commandsAvailable, commandRelevant, skillRelevant }) {
|
|
769
824
|
const lower = task.toLowerCase();
|
|
770
825
|
const tools = ["agent_context"];
|
|
771
|
-
if (
|
|
772
|
-
if (
|
|
826
|
+
if (skillRelevant) tools.push("load_local_skill");
|
|
827
|
+
if (commandRelevant) tools.push("run_local_command");
|
|
773
828
|
if (/browser|chrome|edge|brave|网页|浏览器|表单|网站/.test(lower)) tools.push("browser_status", "browser_list_tabs", "browser_inspect_page", "browser_action", "browser_fill_form");
|
|
774
829
|
if (/app|application|gui|window|应用|软件|窗口|界面/.test(lower)) tools.push("list_local_applications", "inspect_local_application", "operate_local_application");
|
|
775
830
|
if (/git|commit|branch|diff|仓库|提交|分支/.test(lower)) tools.push("git_status", "git_diff");
|
|
776
|
-
if (/test|build|lint|command|terminal|测试|构建|命令|终端/.test(lower)) tools.push(
|
|
831
|
+
if (/test|build|lint|command|terminal|测试|构建|命令|终端/.test(lower)) tools.push(commandsAvailable ? "run_local_command" : "run_process");
|
|
777
832
|
if (/file|code|source|edit|write|文件|代码|源码|修改|写入/.test(lower)) tools.push("read_file", "search_text", "edit_file", "apply_patch");
|
|
778
833
|
return [...new Set(tools)];
|
|
779
834
|
}
|
|
@@ -828,6 +883,8 @@ function publicCommands(commands, displayPath) {
|
|
|
828
883
|
timeout_seconds: command.timeoutSeconds,
|
|
829
884
|
allow_extra_args: command.allowExtraArgs,
|
|
830
885
|
source: displayPath(command.source),
|
|
886
|
+
source_type: command.sourceType || "agent-config",
|
|
887
|
+
...(command.script ? { package_script: command.script } : {}),
|
|
831
888
|
}));
|
|
832
889
|
}
|
|
833
890
|
|
|
@@ -9,9 +9,10 @@ const MAX_UI_ELEMENTS = 500;
|
|
|
9
9
|
const MAX_UI_DEPTH = 12;
|
|
10
10
|
const MAX_TEXT_CHARS = 4000;
|
|
11
11
|
const APP_NAME_PATTERN = /^[^\0\r\n]{1,300}$/;
|
|
12
|
+
const DEFAULT_APPLICATION_CACHE_MS = 30_000;
|
|
12
13
|
|
|
13
14
|
export class AppAutomationManager {
|
|
14
|
-
constructor({ policy, displayPath, runProcess, readResourceText, throwIfCancelled = () => {}, platform = process.platform, home = homedir(), applicationRoots = null }) {
|
|
15
|
+
constructor({ policy, displayPath, runProcess, readResourceText, throwIfCancelled = () => {}, platform = process.platform, home = homedir(), applicationRoots = null, applicationCacheMs = DEFAULT_APPLICATION_CACHE_MS, now = () => Date.now() }) {
|
|
15
16
|
this.policy = policy || {};
|
|
16
17
|
this.displayPath = displayPath;
|
|
17
18
|
this.runProcess = runProcess;
|
|
@@ -20,6 +21,9 @@ export class AppAutomationManager {
|
|
|
20
21
|
this.platform = platform;
|
|
21
22
|
this.home = home;
|
|
22
23
|
this.applicationRoots = applicationRoots;
|
|
24
|
+
this.applicationCacheMs = Math.max(0, Number(applicationCacheMs) || 0);
|
|
25
|
+
this.now = now;
|
|
26
|
+
this.applicationCache = null;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
capabilities() {
|
|
@@ -57,6 +61,16 @@ export class AppAutomationManager {
|
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
async discoverApplications(context = {}) {
|
|
64
|
+
const now = this.now();
|
|
65
|
+
if (this.applicationCache && now - this.applicationCache.createdAt < this.applicationCacheMs) {
|
|
66
|
+
return this.applicationCache.result;
|
|
67
|
+
}
|
|
68
|
+
const result = await this.scanApplications(context);
|
|
69
|
+
this.applicationCache = { createdAt: this.now(), result };
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async scanApplications(context = {}) {
|
|
60
74
|
if (this.platform === "darwin") {
|
|
61
75
|
return listMacApplications(this.applicationRoots || ["/Applications", "/System/Applications", join(this.home, "Applications")], context, this.throwIfCancelled);
|
|
62
76
|
}
|