diff --git a/AGENTS.md b/AGENTS.md index 6c4c73c7..c7b5fa68 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -272,18 +272,23 @@ When sending messages to other SBs via `send_to_inbox`, use `threadKey` to maint | `task:` | PCP task coordination | `task:abc123` | | `thread:` | Multi-step conversation with no natural key | `thread:perf-audit` | -### Cross-Project threadKeys +### Cross-Project threadKeys (MANDATORY outside Inkwell) When working across multiple repos/projects, prefix the threadKey with the project name to avoid collisions. The format is `::`. -| Context | threadKey | -| ----------------------------- | ------------------------- | -| PR in Inkwell (this repo) | `pr:389` | -| PR in a different project | `inktrade:pr:42` | -| Issue in another project | `openclaw:issue:15` | -| Cross-project spec discussion | `inktrade:spec:valuation` | +| Context | threadKey | +| ----------------------------- | ------------------------------- | +| PR in Inkwell (this repo) | `pr:389` | +| PR in a different project | `inktrade:pr:42` | +| Issue in another project | `openclaw:issue:15` | +| Cross-project spec discussion | `inktrade:spec:valuation` | +| Branch in another project | `inktrade:branch:supabase-auth` | -Within the Inkwell repo, the project prefix is optional — `pr:389` is unambiguous. For any thread that references work in a different repo, always include the project prefix so trigger routing and session matching work correctly across boundaries. +Within the Inkwell repo, the project prefix is optional — `pr:389` is unambiguous. For any thread that references work in a **different repo, the project prefix is REQUIRED** — `pr:12` in two repos is a routing collision, and studio route patterns (`inktrade:pr:*`) can only target project-scoped keys. + +The project goes in the **prefix slot, never the identifier**: `inktrade:pr:42`, not `pr:inktrade-42` or `pr:inktrade-supabase-auth`. Baking the project into the identifier defeats pattern matching and prefix-based routing. + +Each repo's AGENTS.md should carry this threadKey section so agents working there natively derive project-prefixed keys. ### Sender Rules @@ -292,6 +297,7 @@ Within the Inkwell repo, the project prefix is optional — `pr:389` is unambigu 3. **DERIVE** the key from the most specific reference. If a PR review involves spec changes, use `pr:` (the actionable unit), not `spec:`. 4. **Keep identifiers stable** — use PR numbers, not PR titles. Use spec URI slugs, not descriptions. 5. If no natural key exists for a multi-step conversation, use `thread:` with a descriptive slug. +6. **Prefix with the project** for any thread about work outside this repo: `::` (see Cross-Project threadKeys above). ### Recipient Rules diff --git a/packages/api/src/services/sessions/codex-runner.ts b/packages/api/src/services/sessions/codex-runner.ts index cdf86fbc..3ea38a49 100644 --- a/packages/api/src/services/sessions/codex-runner.ts +++ b/packages/api/src/services/sessions/codex-runner.ts @@ -20,6 +20,7 @@ import type { ToolCall, } from './types.js'; import { formatInjectedContext } from './context-builder.js'; +import { env } from '../../config/env.js'; import { logger } from '../../utils/logger.js'; import { resolveBinaryPath, buildSpawnPath } from './resolve-binary.js'; import { @@ -137,9 +138,19 @@ export class CodexRunner implements IRunner { args.push('--json'); args.push('-c', `model_instructions_file=${promptPath}`); - // Ink session headers — Codex resolves env var names to values at runtime. - // The server key must match what's in .codex/config.toml (mcp_servers.inkwell). + // Ink MCP server — fully defined via -c overrides so the spawn never + // depends on the user's ~/.codex/config.toml having (or keeping) the + // entry. A partial entry (headers without url) makes Codex fail with + // "Error loading config.toml: invalid transport". + // Inside a Docker sandbox, loopback resolves to the container, so use + // host.docker.internal (matches the orchestrator's .mcp.json rewrite). const codexServerKey = 'inkwell'; + const mcpHost = config.container ? 'host.docker.internal' : 'localhost'; + args.push( + '-c', + `mcp_servers.${codexServerKey}.url="http://${mcpHost}:${env.MCP_HTTP_PORT}/mcp"` + ); + // Session headers — Codex resolves env var names to values at runtime. args.push('-c', `mcp_servers.${codexServerKey}.env_http_headers.x-ink-context="INK_CONTEXT"`); args.push('-c', `mcp_servers.${codexServerKey}.env_http_headers.x-ink-agent-id="AGENT_ID"`); args.push( diff --git a/packages/api/src/skills/builtin/pcp/SKILL.md b/packages/api/src/skills/builtin/pcp/SKILL.md index ee244d5c..c4a71b5e 100644 --- a/packages/api/src/skills/builtin/pcp/SKILL.md +++ b/packages/api/src/skills/builtin/pcp/SKILL.md @@ -55,7 +55,7 @@ Inkwell tools are available via MCP. If the Inkwell MCP server is not already co ```json { "mcpServers": { - "pcp": { + "inkwell": { "command": "node", "args": ["/path/to/pcp/packages/api/dist/index.js"], "env": { "MCP_TRANSPORT": "stdio" } @@ -69,7 +69,7 @@ Inkwell tools are available via MCP. If the Inkwell MCP server is not already co ```json { "mcpServers": { - "pcp": { + "inkwell": { "url": "http://localhost:3001/mcp", "headers": { "Authorization": "Bearer ${INK_ACCESS_TOKEN}" diff --git a/packages/desktop/README.md b/packages/desktop/README.md index fb0faa1a..37e2e73d 100644 --- a/packages/desktop/README.md +++ b/packages/desktop/README.md @@ -4,7 +4,7 @@ Inkwell desktop app — a **thin Tauri v2 native shell** over the locally runnin ## Architecture -The app does **not** bundle the web app. It opens a native window pointed at the local Next.js server (default `http://localhost:3000`, i.e. `INK_PORT_BASE - 1`; the API stays on 3001). Everything you see is the same dashboard you'd see in a browser — the desktop app just adds native chrome: +The app does **not** bundle the web app. It opens a native window pointed at the local Next.js server (default `http://localhost:3002`, i.e. `INK_PORT_BASE + 1`; the API stays on 3001). Everything you see is the same dashboard you'd see in a browser — the desktop app just adds native chrome: - **Window** titled "Inkwell" with the standard macOS menu (Edit/Copy/Paste bindings work in the webview). - **Server-not-running fallback**: on launch the window shows a bundled static page (`ui/index.html`) — never a blank white window. A background thread in the Rust shell polls the server's TCP port every 1.5s and navigates the webview to the dashboard the moment it's reachable. @@ -19,11 +19,11 @@ Optional config file at `~/.ink/desktop.json`: ```json { "host": "localhost", - "port": 3000 + "port": 3002 } ``` -Both fields are optional — defaults are `localhost:3000`. If you run the dashboard on a different port base (e.g. `INK_PORT_BASE=4001` → web on 4002), set `"port": 4002`. Invalid JSON falls back to defaults (with a warning on stderr). +Both fields are optional — defaults are `localhost:3002`. If you run the dashboard on a different port base (e.g. `INK_PORT_BASE=4001` → web on 4002), set `"port": 4002`. Invalid JSON falls back to defaults (with a warning on stderr). ## Prerequisites diff --git a/packages/desktop/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs index 4a10f603..a5627969 100644 --- a/packages/desktop/src-tauri/src/lib.rs +++ b/packages/desktop/src-tauri/src/lib.rs @@ -1,7 +1,7 @@ //! Inkwell desktop shell. //! //! A thin native wrapper around the locally running Inkwell web dashboard -//! (Next.js, default `http://localhost:3000`). The app does NOT bundle the web +//! (Next.js, default `http://localhost:3002`). The app does NOT bundle the web //! app — it points the webview at the local dev/prod server. //! //! On launch the window shows a bundled "waiting for server" page while a @@ -19,15 +19,15 @@ use tauri::{AppHandle, Manager, Url, WebviewUrl, WebviewWindowBuilder, WindowEve const MAIN_WINDOW: &str = "main"; const DEFAULT_HOST: &str = "localhost"; -/// Web dashboard default port (`INK_PORT_BASE - 1`, i.e. 3001 - 1). -const DEFAULT_PORT: u16 = 3000; +/// Web dashboard default port (`INK_PORT_BASE + 1`, i.e. 3001 + 1). +const DEFAULT_PORT: u16 = 3002; const POLL_INTERVAL: Duration = Duration::from_millis(1500); const CONNECT_TIMEOUT: Duration = Duration::from_millis(800); /// Optional user config at `~/.ink/desktop.json`: /// /// ```json -/// { "host": "localhost", "port": 3000 } +/// { "host": "localhost", "port": 3002 } /// ``` #[derive(Debug, Clone, Default, Deserialize)] #[serde(default)] diff --git a/packages/desktop/ui/index.html b/packages/desktop/ui/index.html index 035b30bb..9a3536ba 100644 --- a/packages/desktop/ui/index.html +++ b/packages/desktop/ui/index.html @@ -115,7 +115,7 @@

Waiting for the Inkwell server

This app is a shell over your locally running dashboard at - http://localhost:3000 + http://localhost:3002

Start it with yarn dev in the Inkwell repo — this window will connect diff --git a/packages/openclaw-plugin/skills/SKILL.md b/packages/openclaw-plugin/skills/SKILL.md index 870ba833..35c1a8f8 100644 --- a/packages/openclaw-plugin/skills/SKILL.md +++ b/packages/openclaw-plugin/skills/SKILL.md @@ -55,7 +55,7 @@ PCP tools are available via MCP. If the PCP MCP server is not already configured ```json { "mcpServers": { - "pcp": { + "inkwell": { "command": "node", "args": ["/path/to/pcp/packages/api/dist/index.js"], "env": { "MCP_TRANSPORT": "stdio" } @@ -69,7 +69,7 @@ PCP tools are available via MCP. If the PCP MCP server is not already configured ```json { "mcpServers": { - "pcp": { + "inkwell": { "url": "http://localhost:3001/mcp", "headers": { "Authorization": "Bearer ${INK_ACCESS_TOKEN}"