@modootoday/pmcp 0.10.0 → 0.12.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,393 @@
1
+ # Native terminal control
2
+
3
+ Use `pmcp harness` to manage a main runtime and its workers through a local CLI.
4
+ Each runtime keeps its own conversation interface, OAuth login, folder trust and
5
+ tool permissions. PMCP manages owned terminal processes, observations and
6
+ explicit input delivery. These controls are available from version 0.12.0;
7
+ check your installed release's `pmcp harness --help` before using them.
8
+
9
+ The host currently needs Linux, tmux, util-linux flock and a working user systemd
10
+ manager. Native executables must be on PATH. The terminal transport was tested
11
+ with tmux 3.2a and controlled executable fixtures. That does not qualify every
12
+ vendor's prompt state, automatic wake, authentication refresh or native children.
13
+
14
+ ## Check the host
15
+
16
+ ```sh
17
+ pmcp harness capabilities inspect
18
+ pmcp harness doctor inspect
19
+ ```
20
+
21
+ These commands report executable paths and required tools without starting a
22
+ provider or reading its credentials. An installed executable is not a qualified
23
+ provider feature. Use `pmcp doctor` separately for project configuration checks.
24
+
25
+ Group creation checks required tools and contacts the user manager before it
26
+ writes group state. Missing tools return native_host_tool_unavailable followed
27
+ by the tool name; an unreachable manager returns native_user_manager_unavailable.
28
+ The doctor report lists prerequisites and does not certify cgroup delegation.
29
+ An ordinary isolated Docker container usually lacks this user manager and a
30
+ writable delegated cgroup subtree. Container resource limits alone do not
31
+ qualify the native session controls; the current profile requires its Linux host.
32
+
33
+ ## Choose native or Docker execution
34
+
35
+ The default native profile starts executables on the Linux host. The optional
36
+ Docker profile keeps the same host-side tmux and user-systemd controller, while
37
+ each native executable runs in its own bounded container. Docker does not
38
+ replace the host prerequisites above. Only a local Docker Engine is supported.
39
+
40
+ Prepare an image locally with your runtime installed. PMCP requires its exact
41
+ image ID, refuses a missing image and never builds or pulls one. The image must
42
+ support the host UID/GID and the absolute command paths in your policy.
43
+ Create `docker-policy.json` with this structure, replacing the image ID:
44
+
45
+ ```json
46
+ {
47
+ "schemaVersion": 1,
48
+ "image": "sha256:YOUR_64_CHARACTER_IMAGE_ID",
49
+ "network": "none",
50
+ "workspaceAccess": "read-only",
51
+ "commands": {
52
+ "codex-cli": ["/usr/local/bin/codex"]
53
+ }
54
+ }
55
+ ```
56
+
57
+ Set `profile` to `docker` and `dockerPolicyFile` to the policy's absolute path in
58
+ your group specification. The policy is validated and frozen into group state;
59
+ editing its file does not expand a running group's authority. Group creation and
60
+ session start use the same commands as native execution.
61
+
62
+ Each container gets a read-only root, no network, all capabilities dropped,
63
+ no-new-privileges, 64 PIDs and a memory/swap ceiling. Its only bind mounts are
64
+ the project at `/workspace` and a private writable profile at `/state`.
65
+ The workspace is read-only unless the policy explicitly selects read-write.
66
+ Native arguments belong in the session specification; PMCP adds no native
67
+ permission bypass. Docker access itself is a privileged host capability.
68
+
69
+ The session `memoryMb` reserves both runtime memory and 64 MiB for its host
70
+ connection. Its Docker memory limit is therefore 64 MiB smaller. Leave additional
71
+ headroom in the group budget for its controller and tmux server.
72
+ Session receipts include the owned container identity only for Docker execution.
73
+ Unknown or changed container identity blocks cleanup and admission.
74
+
75
+ ### Allow provider connections explicitly
76
+
77
+ Network-none containers are offline by default. To allow provider HTTPS, add an
78
+ `egress` entry to the policy:
79
+
80
+ ```json
81
+ {
82
+ "hosts": ["chatgpt.com", "auth.openai.com"],
83
+ "bridgeExecutable": "/usr/local/bin/node"
84
+ }
85
+ ```
86
+
87
+ These are exact hostnames on port 443, not domain suffixes or wildcards. The
88
+ host proxy rejects private addresses and undeclared destinations. The declared
89
+ Node executable must exist inside the image. PMCP stages a digest-checked opaque
90
+ bridge and provides proxy environment variables to the native process. It does
91
+ not intercept TLS or interpret OAuth. A runtime that ignores proxy variables
92
+ cannot reach the provider through this profile. Actual Codex 0.161.0 login and
93
+ main/worker responses have been qualified; other vendors and token refresh need
94
+ their own validation. These two hosts are an observed test scope, not a promise
95
+ that all future Codex features use only them.
96
+
97
+ ### Keep authenticated roles independent
98
+
99
+ Omitting `privateProfiles` creates a new empty profile for each launch. For
100
+ explicit reuse, prepare distinct local directories owned by your UID with mode
101
+ 0700, outside the project and credential home. Add named grants to the policy:
102
+
103
+ ```json
104
+ {
105
+ "main": {
106
+ "runtime": "codex-cli",
107
+ "directory": "/absolute/private/main-profile",
108
+ "lockExecutable": "/usr/bin/flock"
109
+ },
110
+ "worker": {
111
+ "runtime": "codex-cli",
112
+ "directory": "/absolute/private/worker-profile",
113
+ "lockExecutable": "/usr/bin/flock"
114
+ }
115
+ }
116
+ ```
117
+
118
+ Place this object under `privateProfiles`. Select `privateProfile: "main"` or
119
+ `privateProfile: "worker"` in the corresponding session specification. PMCP
120
+ checks and freezes directory/lock identity; the selected runtime must match its
121
+ grant. The image must provide the declared flock executable.
122
+
123
+ Authenticate each role with its vendor's native login in that role's isolated
124
+ profile. PMCP supplies no credential import or account-switching command.
125
+ Do not copy your host credentials or clone one role's profile into another.
126
+ Never place profiles or mailbox actor files below the mounted project. An
127
+ exclusive kernel lock stays held by the container while its runtime lives,
128
+ even if the host terminal connection exits. Stop the exact owned session before
129
+ reusing its profile. This lock is cooperative same-UID fencing, not protection
130
+ against a hostile host administrator.
131
+
132
+ Keep private profiles when you stop a group if you intend to reuse their native
133
+ login. Session termination does not log out the account or delete credentials.
134
+ Do not publish these directories or add them to source control.
135
+
136
+ ## Create a group and its sessions
137
+
138
+ Save this as `group-spec.json`, replacing the project path:
139
+
140
+ ```json
141
+ {
142
+ "projectRoot": "/absolute/project/path",
143
+ "allowedRuntimes": ["codex-cli", "claude-code"],
144
+ "memoryMb": 2048,
145
+ "maxActive": 2
146
+ }
147
+ ```
148
+
149
+ ```sh
150
+ pmcp harness group create --input group-spec.json
151
+ pmcp harness group list
152
+ ```
153
+
154
+ The JSON receipt provides the private groupFile and generation. Set GROUP_FILE
155
+ and GENERATION to those values for the examples below. Group state lives below
156
+ XDG_STATE_HOME/pmcp/harness, or ~/.local/state/pmcp/harness. The tmux socket path
157
+ must fit within 100 bytes; an unusually long state root is refused.
158
+
159
+ Save `main-spec.json`:
160
+
161
+ ```json
162
+ {
163
+ "runtime": "codex-cli",
164
+ "role": "main",
165
+ "memoryMb": 768
166
+ }
167
+ ```
168
+
169
+ ```sh
170
+ pmcp harness session start --group-file "$GROUP_FILE" --generation "$GENERATION" --input main-spec.json
171
+ pmcp harness session list --group-file "$GROUP_FILE"
172
+ ```
173
+
174
+ Start a worker with another specification whose role is worker. Supported
175
+ runtime IDs are codex-cli, claude-code, gemini-cli, grok-cli and antigravity;
176
+ their executable names are codex, claude, gemini, grok and agy. Optional args
177
+ contains native CLI arguments. PMCP adds no permission bypass flags.
178
+ Start resolves the executable against the native working directory before
179
+ creating a launch record. A missing or non-executable program is refused as
180
+ runtime_executable_unavailable; directories are not accepted as executables.
181
+
182
+ A group has one main. Workers require an available main, a runtime grant and
183
+ room in both the session-count and memory budgets. cwd defaults to projectRoot
184
+ and must resolve inside it. This working-directory check and cooperative writer
185
+ leases do not isolate filesystem access from another process of the same UID.
186
+
187
+ Native execution waits behind a start gate until its owned terminal record and
188
+ group registration are saved. The gate checks the group generation, runtime
189
+ grant and role before starting the executable. A failed or interrupted start
190
+ blocks further admission until you explicitly resolve it.
191
+
192
+ ## Observe or take control
193
+
194
+ Set SESSION to a session ID from the receipt:
195
+
196
+ ```sh
197
+ pmcp harness session inspect --group-file "$GROUP_FILE" --session "$SESSION"
198
+ pmcp harness session read --group-file "$GROUP_FILE" --session "$SESSION"
199
+ pmcp harness attach open --group-file "$GROUP_FILE" --session "$SESSION"
200
+ ```
201
+
202
+ Attachment is read-only by default. Detach with tmux's Ctrl-b then d, or use
203
+ attach detach with the target session and current generation. Detach preserves
204
+ the runtime. Reading returns terminal text and does not infer model readiness.
205
+
206
+ For longer history, save read-options.json:
207
+
208
+ ```json
209
+ { "lines": 2000, "maxBytes": 16384 }
210
+ ```
211
+
212
+ ```sh
213
+ pmcp harness session read --group-file "$GROUP_FILE" --session "$SESSION" --input read-options.json
214
+ ```
215
+
216
+ lines accepts 1–2000 and maxBytes accepts 1–65536. Defaults remain 100 lines and
217
+ 16384 bytes. A bounded result reports truncated when it omits earlier content.
218
+ During an alternate-screen application, alternate: true reads the saved normal
219
+ screen; omitted or false reads the current screen. This returns the owned native
220
+ pane's content, separate from a surrounding terminal's older shell output.
221
+
222
+ On the tested tmux 3.2a, read-only native clients ignore typing, wheel reports
223
+ and copy-mode keys; use session read for history without acquiring control.
224
+ Inside another tmux with the same Ctrl-b prefix, Ctrl-b then Ctrl-b forwards
225
+ one prefix to the inner layer. Ctrl-b then [ opens the outer copy mode, while
226
+ Ctrl-b, Ctrl-b, d detaches the inner connection. Outer history is the rendered
227
+ wrapper screen; session read retrieves the owned runtime's history.
228
+ PMCP does not change the outer tmux configuration. Other terminal mouse and
229
+ trackpad settings require their own qualification.
230
+
231
+ For bounded polling, session watch also takes an input file containing count
232
+ (1–10) and intervalMs (100–2000). It returns snapshots rather than an endless
233
+ stream.
234
+
235
+ Acquire native control before opening a writable attachment:
236
+
237
+ ```sh
238
+ pmcp harness control acquire --group-file "$GROUP_FILE" --generation "$GENERATION" --session "$SESSION" --controller operator --mode native
239
+ pmcp harness attach open --group-file "$GROUP_FILE" --generation "$GENERATION" --session "$SESSION" --mode write --lease-file "$LEASE_FILE"
240
+ ```
241
+
242
+ Set LEASE_FILE to the acquire receipt's private credential path. A writable
243
+ attachment renews its 60-second lease and releases it after detach. Failed
244
+ renewal disconnects the client. It does not stop the runtime. Another writable
245
+ tmux client blocks CLI delivery even if it bypassed PMCP's lease.
246
+
247
+ ## Deliver input explicitly
248
+
249
+ Use mode cli when acquiring a lease for explicit command-line input.
250
+ Save literal single-line UTF-8 text in prompt.txt:
251
+
252
+ ```sh
253
+ pmcp harness input write --group-file "$GROUP_FILE" --generation "$GENERATION" --lease-file "$LEASE_FILE" --request-id task1-attempt1-draft --text-file prompt.txt
254
+ pmcp harness input submit --group-file "$GROUP_FILE" --generation "$GENERATION" --lease-file "$LEASE_FILE" --request-id task1-attempt1-submit
255
+ pmcp harness control release --group-file "$GROUP_FILE" --generation "$GENERATION" --lease-file "$LEASE_FILE"
256
+ ```
257
+
258
+ Write and Enter submission are separate operations. Newlines, tabs, terminal
259
+ control characters and input above 16 KiB are refused. A tmux paste flag alone
260
+ cannot guarantee safe multiline delivery in a native approval menu.
261
+
262
+ Inspect the native screen before writing or submitting. A control lease identifies
263
+ the writer; it does not identify the active native widget. PMCP does not infer
264
+ that a composer is ready or that a native approval has been granted.
265
+
266
+ Reuse an input request ID only for an exact retry. Matching retries return the
267
+ previous receipt; they do not send again. Conflicting content is refused.
268
+ input_written means terminal delivery, not model processing or task completion.
269
+ Intent or uncertain delivery requires recovery and is never automatically
270
+ replayed. delivery list and delivery inspect expose this journal.
271
+
272
+ An uncertain receipt has ok: false and CLI exit code 1, including an exact
273
+ retry that returns the stored receipt. Inspect its state instead of assuming
274
+ that a nonzero exit means no record exists. An exited worker remains observable;
275
+ stop it by ID and explicitly start a replacement. Old request IDs are not
276
+ transferred to the replacement, and sibling sessions retain their input.
277
+
278
+ Input interrupt sends an explicit terminal Ctrl-c under the same lease.
279
+ Cancellation, process termination and provider-side completion are distinct.
280
+
281
+ ## Coordinate through the existing mailbox
282
+
283
+ Use the existing `pmcp mailbox` commands for send, inbox, read and ack.
284
+ Keep task and attempt IDs in your message body; send a result with replyToId
285
+ pointing to its request. Main review reads the reply. Acknowledgment records
286
+ receipt handling and does not declare task completion or user acceptance.
287
+
288
+ PMCP does not create another message engine or automatically type a wake prompt
289
+ into a native menu. Follow the [mailbox guide](mailbox.md) for actor credentials
290
+ and launcher-bound runtime attachments.
291
+ There is no automatic delivery loop. Native readiness and approval stay with
292
+ the operator and runtime; an input receipt does not establish prompt readiness.
293
+
294
+ ### A manual request and review workflow
295
+
296
+ Mailbox actor names and harness session roles are separate. Creating a main or
297
+ worker terminal does not register a mailbox actor or give it credentials. Use
298
+ the mailbox guide to initialize the project and register main, worker and
299
+ operator actors. Keep their credential files private. The following workflow
300
+ uses CLI actor files for manual coordination; it does not attach a vendor child
301
+ or automatically configure a runtime's MCP server.
302
+
303
+ Save request.json with your actor name and a unique task/attempt pair:
304
+
305
+ ```json
306
+ {
307
+ "recipients": [{ "actorId": "worker" }],
308
+ "body": "{\"taskId\":\"review-1\",\"attemptId\":\"attempt-1\",\"kind\":\"request\",\"text\":\"Review the proposed change\"}",
309
+ "idempotencyKey": "review-1-attempt-1"
310
+ }
311
+ ```
312
+
313
+ ```sh
314
+ pmcp mailbox send --config ./pmcp.toml --actor-file "$MAIN_ACTOR_FILE" --input request.json
315
+ pmcp mailbox inbox --config ./pmcp.toml --actor-file "$WORKER_ACTOR_FILE"
316
+ ```
317
+
318
+ The send receipt returns messageId and threadId. Save the returned messageId
319
+ in message.json as {"messageId":"the-returned-UUID"}, then read and acknowledge:
320
+
321
+ ```sh
322
+ pmcp mailbox read --config ./pmcp.toml --actor-file "$WORKER_ACTOR_FILE" --input message.json
323
+ pmcp mailbox ack --config ./pmcp.toml --actor-file "$WORKER_ACTOR_FILE" --input message.json
324
+ ```
325
+
326
+ Receiving and acknowledging a request sends no terminal input. When its native
327
+ prompt is ready, deliver a single-line prompt through the worker's CLI lease
328
+ and submit separately. Include the task/attempt and request message ID so the
329
+ worker can associate its response. Preserve uncertain delivery for inspection.
330
+
331
+ To report a result, use mailbox send with the same taskId and attemptId in body,
332
+ a new idempotencyKey, recipients containing main, and replyToId containing the
333
+ original request messageId. The worker must first read that request. The result
334
+ keeps its threadId; the main can filter inbox with an input file containing
335
+ {"threadId":"the-returned-thread-UUID"} and read the selected reply.
336
+
337
+ Read the body and verify its task, attempt and sender before accepting the
338
+ result. These body fields are a coordination convention, not a mailbox-enforced
339
+ task state machine. A new attempt uses a new request and idempotency key; keep
340
+ the previous result in its original thread. Exact send retries reuse their key
341
+ and return the existing message; changed payloads are refused.
342
+
343
+ The main can send its synthesis to operator as a reply to the reviewed result.
344
+ After reading it, the operator may send a separate acceptance reply. ACK records
345
+ receipt handling only. Neither result text, ACK nor acceptance automatically
346
+ submits a terminal draft, transfers control or executes a command.
347
+
348
+ ## Pause, stop and recover
349
+
350
+ group pause prevents new sessions without interrupting running providers.
351
+ group resume permits admission again. group configure takes a JSON input to
352
+ adjust budgets or narrow runtime grants; it refuses reductions below active
353
+ reservations. Expanding grants requires a new group.
354
+
355
+ session stop names one owned session. group stop explicitly stops its entire
356
+ owned group and removes runtime budget properties. Both require the current
357
+ generation. They do not target an existing external tmux session.
358
+
359
+ recovery inspect reports observations, leases, uncertain deliveries and
360
+ unregistered owned sessions. recovery reconcile removes a writer lock only
361
+ after its controller has exited and never resends input. Use recovery
362
+ stop-orphan for a named unregistered session, or recovery replace-main with a
363
+ new main specification after the previous main has stopped. Replacement changes
364
+ the generation and invalidates old control credentials. Unknown server or pane
365
+ identity fails closed; no session is silently adopted.
366
+
367
+ Include pending starts explicitly when inspecting a failed launch:
368
+
369
+ ```sh
370
+ pmcp harness recovery inspect --group-file "$GROUP_FILE" --include-starts
371
+ pmcp harness recovery abort-start --group-file "$GROUP_FILE" --generation "$GENERATION" --session "$SESSION"
372
+ ```
373
+
374
+ abort-start stops a named incomplete launch and marks any saved group reference
375
+ as stopped. It neither adopts the session nor restarts it. A committed session
376
+ uses session stop instead. If the start gate died before recording ownership,
377
+ automatic cleanup is refused and admission remains blocked for administrative
378
+ repair. Keep the private state directory as evidence; do not guess a pane target
379
+ or resend an uncertain request.
380
+
381
+ ## Layout planning
382
+
383
+ ```sh
384
+ pmcp harness layout --columns 120 --rows 40
385
+ ```
386
+
387
+ This pure command needs no tmux, provider, project or network. It returns layout
388
+ A at 120 columns and 32 rows or larger: a main pane, worker dock and bottom
389
+ coordination pane. Smaller terminals retain a full-width main. The minimum is
390
+ 40 columns by 12 rows; the maximum is 1000 in either dimension.
391
+
392
+ There is no product TUI yet. Its terminal layout will consume the qualified CLI
393
+ contract after the remaining native and recovery acceptance gates.
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@modootoday/pmcp",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "license": "Elastic-2.0",
5
5
  "author": "modootoday",
6
- "description": "An MCP server that finds the skills your installed packages already ship. Reads SKILL.md out of node_modules by shape rather than by path, so it sees the skills, .agent, .claude, .gemini and .codex conventions alike, and keeps session startup flat as the number of installed skills grows.",
6
+ "description": "Use the skills your packages already ship, and keep your AI tools' project configuration in one place.",
7
7
  "keywords": [
8
8
  "mcp",
9
9
  "model-context-protocol",
@@ -58,6 +58,7 @@
58
58
  "!dist/**/*.map",
59
59
  "!dist/.tsbuildinfo",
60
60
  "skills",
61
+ "docs/*.md",
61
62
  "README.md",
62
63
  "SECURITY.md",
63
64
  "LICENSE",