@synadia-ai/agent-fabric-claude-code 0.1.0 → 0.3.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,190 @@
1
+ ---
2
+ name: configure
3
+ description: Configure NATS channel - select context, set owner/session, sender identity, inbound trust, and permissions. Use when user asks to set up NATS, connect to NATS, change context, identity, trust, or permissions.
4
+ user-invocable: true
5
+ allowed-tools:
6
+ - Read
7
+ - Write
8
+ - Bash(ls *)
9
+ - Bash(mkdir *)
10
+ - Bash(nats context ls *)
11
+ - Bash(cat *)
12
+ - Bash(*list-contexts*)
13
+ effort: low
14
+ ---
15
+
16
+ # /nats-channel:configure - NATS Channel Configuration
17
+
18
+ Configures the NATS channel plugin: connection context, session name, sender
19
+ identity, inbound sender trust, and permission handling. State lives in
20
+ `~/.claude/channels/nats/config.json`.
21
+
22
+ Arguments passed: `$ARGUMENTS`
23
+
24
+ ---
25
+
26
+ ## Dispatch on arguments
27
+
28
+ ### No args - status, list, and offer to change
29
+
30
+ Read state and give the user a complete picture, then ask:
31
+
32
+ 1. **Current config** - read `~/.claude/channels/nats/config.json`. Show:
33
+ - Selected context name (or "none - using demo.nats.io")
34
+ - Owner override (if set)
35
+ - Session name override (if set)
36
+ - Sender identity mode (`off` or `signed`; default `off`)
37
+ - Minimum sender trust (`any` or `signed`; default `any`)
38
+ - Connection URL and description from the context file
39
+ - Permission mode (`terminal` or `query`) and whether permission prompts
40
+ will be relayed as NATS query chunks or handled in the local terminal
41
+
42
+ 2. **Available contexts** - run the `list-contexts.sh` script bundled
43
+ alongside this skill file to list all NATS CLI contexts with their
44
+ URL and description.
45
+
46
+ 3. **Ask the user** - show the numbered list of available contexts and
47
+ ask if they'd like to switch. Example:
48
+ - *"Currently using `<name>`. Available contexts:"*
49
+ - *(numbered list)*
50
+ - *"Would you like to switch? Enter a context name or number, or
51
+ 'no' to keep the current selection."*
52
+ If no config is set, skip straight to asking them to pick one.
53
+
54
+ ### `list` - list available NATS contexts
55
+
56
+ Two separate steps - do NOT combine into one Bash call:
57
+
58
+ 1. Bash: run the `list-contexts.sh` script bundled alongside this skill
59
+ file. It produces no stdout - it writes to a file.
60
+ 2. Read: open `~/.claude/channels/nats/.contexts` and present the
61
+ markdown table to the user.
62
+
63
+ ### `<context-name>` - select a context
64
+
65
+ 1. Treat `$ARGUMENTS` as the context name (trim whitespace).
66
+ 2. Verify `~/.config/nats/context/<name>.json` exists. If not, show
67
+ available contexts and error.
68
+ 3. Read the context file. Show the URL and description so the user can
69
+ confirm it's correct.
70
+ 4. `mkdir -p ~/.claude/channels/nats`
71
+ 5. Read existing `config.json` if present; update/add the `context` field,
72
+ preserve other fields (like `sessionName`, `permissions`). Write back.
73
+ 6. Confirm, then show the status view so the user sees where they stand.
74
+
75
+ ### `session <name>` - set session name override
76
+
77
+ 1. The session name is the 5th token in the v0.3 verb-first subject
78
+ `agents.prompt.cc.<owner>.<name>`. It defaults to the working directory
79
+ basename. This command overrides it.
80
+ 2. Read existing `config.json` (or start fresh). Set `sessionName` field.
81
+ Write back.
82
+ 3. Confirm the override.
83
+
84
+ ### `session clear` - remove session name override
85
+
86
+ Remove the `sessionName` field from `config.json` so the default
87
+ (CWD basename) is used.
88
+
89
+ ### `owner <name>` - set owner override
90
+
91
+ 1. The owner is the 4th token in the v0.3 verb-first subject
92
+ `agents.prompt.cc.<owner>.<name>`. It defaults to the sanitized
93
+ `$USER`. This command overrides it — useful for service accounts or
94
+ deployment-scoped owners.
95
+ 2. Read existing `config.json` (or start fresh). Set the `owner` field.
96
+ Write back.
97
+ 3. Confirm the override. Note: the `SYNADIA_CLAUDE_CODE_OWNER` and
98
+ `SYNADIA_OWNER` env vars take precedence over this config field.
99
+
100
+ ### `owner clear` - remove owner override
101
+
102
+ Remove the `owner` field from `config.json` so the default (sanitized
103
+ `$USER`) is used.
104
+
105
+ ### `identity off` - disable host identity
106
+
107
+ Set `senderIdentity` to `"off"`. The channel performs no host identity lookup
108
+ and registers no identity metadata. This is the default and works with
109
+ token/password authentication and servers where user-info lookup is not
110
+ permitted. It does not change inbound sender policy.
111
+
112
+ ### `identity signed` - use the connection identity
113
+
114
+ Set `senderIdentity` to `"signed"`. The selected NATS context must contain a
115
+ user seed (`creds`, `nkey`, or `user_jwt` plus `user_seed`). The same immutable
116
+ credential snapshot supplies both connection authentication and signing; never
117
+ ask for or add a second identity credential. Signed startup fails rather than
118
+ downgrading when the connection cannot be bound to that signer.
119
+
120
+ `NATS_SENDER_IDENTITY` overrides this config field. A plugin reload or Claude
121
+ Code restart is required after changing it.
122
+
123
+ ### `trust any` - accept all sender trust classes
124
+
125
+ Set `minSenderTrust` to `"any"`. Headerless, claimed, and verified prompts are
126
+ admitted. This is the backward-compatible default and is independent of the
127
+ channel's own identity.
128
+
129
+ ### `trust signed` - require verified senders
130
+
131
+ Set `minSenderTrust` to `"signed"`. Headerless, claimed, malformed, stale, and
132
+ replayed requests are rejected before an acknowledgement and before Claude sees
133
+ the prompt. `NATS_MIN_SENDER_TRUST` overrides this config field.
134
+
135
+ ### `permissions terminal` - use terminal for permission prompts
136
+
137
+ Set `permissions.mode` to `terminal`. Permission prompts will appear in the
138
+ Claude Code terminal session. This is the simplest option when the operator
139
+ has direct terminal access, and the default when no permissions block is set.
140
+
141
+ 1. Read existing `config.json` (or start fresh).
142
+ 2. Set `permissions` to `{ "mode": "terminal" }`.
143
+ 3. Write back and confirm.
144
+
145
+ ### `permissions query` - relay permissions as query chunks
146
+
147
+ Set `permissions.mode` to `query`. When Claude needs permission to use a
148
+ tool during an active NATS prompt, the plugin emits a protocol `query`
149
+ chunk (§7 of the Synadia Agent Protocol for NATS) on the active stream's reply
150
+ subject. The caller replies with `yes`/`no` on the query's dynamic reply
151
+ inbox, and the plugin forwards the decision back to the Claude Code
152
+ harness. The legacy value `"nats"` is accepted as an alias for
153
+ backward compatibility.
154
+
155
+ If Claude asks for permission while no NATS request is active, the request
156
+ is denied (there is no open stream to carry a query chunk on). Operators
157
+ who need terminal-style prompting in that case should use `permissions
158
+ terminal` instead.
159
+
160
+ 1. Read existing `config.json` (or start fresh).
161
+ 2. Set `permissions` to `{ "mode": "query" }`.
162
+ 3. Write back and confirm.
163
+
164
+ ### `permissions clear` - reset to default
165
+
166
+ Remove the `permissions` field from `config.json`, restoring the default
167
+ behavior (terminal-mode prompts).
168
+
169
+ ### `clear` - remove config
170
+
171
+ Delete `~/.claude/channels/nats/config.json`.
172
+
173
+ ---
174
+
175
+ ## Implementation notes
176
+
177
+ - The channels dir might not exist if the server hasn't run yet. Missing
178
+ file = not configured, not an error.
179
+ - The server reads `config.json` once at boot. Config changes need a
180
+ session restart or `/reload-plugins`. Say so after saving.
181
+ - NATS CLI contexts live in `~/.config/nats/context/<name>.json`. Each
182
+ file contains `url`, `description`, `creds`, `nkey`, `user`, `password`,
183
+ `token`, `cert`, `key`, `ca`, and other fields.
184
+ - Do not modify NATS CLI context files - only read them.
185
+ - `senderIdentity` and `minSenderTrust` are independent. Do not enable strict
186
+ inbound policy merely because signed host identity was enabled.
187
+ - Legacy configs with `"mode": "nats"` are still accepted and treated as
188
+ the new `"query"` mode; they do not need to be rewritten.
189
+ - Legacy configs with `permissions.subject` are silently ignored - query
190
+ chunks use a dynamic reply inbox per request, not a fixed subject.
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+ # List NATS CLI contexts as a markdown table.
3
+ # Writes to ~/.claude/channels/nats/.contexts for the skill to Read.
4
+
5
+ CONTEXT_DIR="${HOME}/.config/nats/context"
6
+ STATE_DIR="${HOME}/.claude/channels/nats"
7
+ OUT="${STATE_DIR}/.contexts"
8
+
9
+ mkdir -p "$STATE_DIR"
10
+
11
+ if [ ! -d "$CONTEXT_DIR" ]; then
12
+ echo "No NATS contexts found (${CONTEXT_DIR} does not exist)" > "$OUT"
13
+ exit 0
14
+ fi
15
+
16
+ {
17
+ echo "| Name | URL | Description |"
18
+ echo "|------|-----|-------------|"
19
+ jq -r 'input_filename as $f |
20
+ [($f | split("/") | last | rtrimstr(".json")), (.url // "-"), (.description // "-")] |
21
+ "| \(.[0]) | \(.[1]) | \(.[2]) |"' "$CONTEXT_DIR"/*.json 2>/dev/null
22
+ } > "$OUT"
@@ -26,7 +26,7 @@ shell command can then allow `scripts/sp` by its path, and does not ask.
26
26
  the NATS server, this agent's credentials, its workspace, its volume on
27
27
  `init`, the trace of the prompt you are serving, your address as the
28
28
  creator of what you publish, and the agent that prompted you as the grantee
29
- of a grant. Never pass `--url`, `--creds`, `--dir`, `--volume`,
29
+ of a grant. Never pass `--url`, `--creds`, `--dir`, `--volume`, `--bootstrap-file`,
30
30
  `--thread-id`, `--root-id`, `--creator`, `--grantee-account` or
31
31
  `--grantee-user` yourself; `scripts/sp` refuses them.
32
32
 
@@ -39,8 +39,8 @@ The workspace is private to this agent and persists across restarts.
39
39
  Each successful command prints JSON.
40
40
  Read the returned receipt before reporting that publication succeeded.
41
41
  An upload alone does not publish a checkpoint.
42
- Retention preserves the root until explicit release.
43
- Its deadline permits later client cleanup.
42
+ Retention preserves the root for its recorded promise.
43
+ A finite deadline permits later cleanup by the client or the private lifecycle worker.
44
44
  A grant separately permits one authenticated principal to read that root and path.
45
45
  Do not print credential contents.
46
46
 
@@ -68,13 +68,27 @@ An expired reference or refused grant does not authorize another identity or pat
68
68
 
69
69
  ```sh
70
70
  scripts/sp info
71
- scripts/sp init --operation-id "$CREATE_ID"
71
+ scripts/sp init
72
72
  scripts/sp describe
73
73
  ```
74
74
 
75
75
  `info` returns this agent's authenticated principal, owned scope allowances, and request limits.
76
76
  It needs no initialized workspace and changes no recovery state.
77
77
  The scope byte and object figures are configured allowances; `describe` shows current usage.
78
+
79
+ ### Import the configured bootstrap
80
+
81
+ When `SCRATCHPAD_BOOTSTRAP_FILE` is configured, the wrapper supplies it on `init`.
82
+ The public file selects one volume, admission binding, and complete saved creation Action.
83
+ It contains no credentials and supplies no authentication authority.
84
+ Run `scripts/sp init` without inventing a creation operation identifier.
85
+ Keep both the file and the private workspace across restarts.
86
+ An exact matching import preserves existing commands, writer state, and unknown outcomes.
87
+ A different creation request refuses; an `exists` result never permits adoption.
88
+ Saved creation recovery uses the owner's connection and does not need admin ensure.
89
+ An expiry or closed refusal alone does not settle an earlier possibly sent creation.
90
+ An explicit later admission needs a separate workspace; never erase the earlier one to continue.
91
+ A configured custom volume must match the file. Unsupported command lines refuse the import.
78
92
  An empty scope list needs an operator admission check before this agent can publish.
79
93
  Volumes readable through another owner's grant are not listed; their granted references can still be read.
80
94
  If `info` reports an unknown command, an older binary was selected; continue with the existing commands and ask the operator for the allowances.
@@ -83,6 +97,10 @@ If `info` reports an unknown command, an older binary was selected; continue wit
83
97
  Rerun the saved creation action after an unknown outcome.
84
98
  An established writer whose journal disappeared requires an explicit reset.
85
99
  Initialization cannot silently reset that writer.
100
+ For a reusable lifetime, `info` also returns its admission binding.
101
+ `init` saves that binding before its first creation request and reuses it after a restart.
102
+ A saved creation cannot adopt a later admission with the same volume name.
103
+ Preserve the workspace after a refusal; do not erase it to bypass lifetime fencing.
86
104
 
87
105
  ## Publish a file and share it with the agent that prompted you
88
106
 
@@ -194,9 +212,12 @@ scripts/sp maintain --apply
194
212
  ```
195
213
 
196
214
  The first command reports the proposed maintenance.
197
- The second releases unprotected expired pins, closes the volume, traces retained dependencies, collects unreachable objects, and reopens it.
215
+ The second releases unprotected expired pins and closes a collection interval.
216
+ On a reusable deployment, private maintenance traces retained dependencies,
217
+ collects unreachable objects and reopens that interval. The client observes its completion.
218
+ On an existing deployment, the client performs that collection.
198
219
  Maintenance does not scrub retained file payloads.
199
- If interrupted, recover the saved request or repeat maintenance against the durable closed state.
220
+ If interrupted, recover the saved request. A newer collection interval fences that request.
200
221
  Capacity refusal requires maintenance or an explicit operator decision; it never permits eviction of retained content.
201
222
 
202
223
  ## Capacity
@@ -207,3 +228,63 @@ Objects include chunks, file metadata, and directories.
207
228
  Retained results and unfinished uploads consume the same allowance.
208
229
  Replacing unique content needs room for both versions until old retention is released and collection completes.
209
230
  Local spools also need enough private disk space for the prepared artifact.
231
+
232
+ ### Choose what to remove at capacity
233
+
234
+ On a reusable volume, `describe` includes `lifetime` with the lease, grace period,
235
+ recovery-read state and reserved metadata allowance.
236
+ Choose a path from the current head to remove. The service never chooses a live
237
+ file to evict because the volume is full.
238
+
239
+ ```sh
240
+ scripts/sp remove --path "$ARTIFACT_PATH" --operation-id "$REMOVE_ID"
241
+ scripts/sp remove --path "$ARTIFACT_PATH" --operation-id "$REMOVE_ID" --apply
242
+ ```
243
+
244
+ The first command reports a plan without changing the head.
245
+ Applying saves the exact command and uses reserved metadata space within the reported bounds.
246
+ The receipt confirms the new head. Retention or grants can still protect the removed content.
247
+ Private maintenance returns space after that content is no longer protected.
248
+ After an unknown result, use `recover`; do not change the saved path or operation identifier.
249
+ Older binaries or deployments can refuse this command; that refusal does not permit eviction.
250
+
251
+ ### Keep or end a reusable lifetime
252
+
253
+ A volume with `lifetime.expires_at` equal to zero is durable.
254
+ For a managed lease, request one absolute Unix deadline in seconds:
255
+
256
+ ```sh
257
+ scripts/sp renew --until "$UNTIL"
258
+ ```
259
+
260
+ Save and retry the same deadline. Renewal must win before retirement begins.
261
+ An already achieved renewal can still recover after that deadline passes.
262
+ If recovery reports definite `expired` for an unachieved saved deadline, only the
263
+ renewal intent is cleared. Request a new absolute deadline if the same lifetime is
264
+ still renewable, then recover the unchanged ordinary pending operation.
265
+ An unknown result keeps the saved renewal pending; do not replace its deadline.
266
+ During recorded grace, recovery reads can remain available while ordinary writes are closed.
267
+ Do not infer disposal authority from an agent disconnect, an idle volume, or full quota.
268
+
269
+ Whole-volume retirement ends all head, retention, grant and access promises in its plan.
270
+ It can permanently delete every artifact. Use it only when the user has authorized
271
+ ending those promises and any required export has been restored and checked.
272
+
273
+ ```sh
274
+ scripts/sp retire --operation-id "$RETIRE_ID"
275
+ scripts/sp retire --operation-id "$RETIRE_ID" --end-promises --apply
276
+ ```
277
+
278
+ Review the first result before applying. The exact terminal operation persists for recovery.
279
+ If recovery proves a strictly newer nonterminal plan for the same incarnation,
280
+ slot, generation, target, and saved request, it clears only the old release intent
281
+ and reports `fenced`. The lifetime remains nonterminal. Inspect the reported
282
+ lifetime and renew before ordinary work if needed. Review a new plan before
283
+ applying again; this refusal does not mean that artifacts were disposed or capacity returned.
284
+ Unknown or mismatched evidence and an already accepted terminal fence stay pending.
285
+ `disposed` reports physical disposal; only `capacity_returned` confirms admission capacity is free.
286
+ If a successor generation proves the old lifetime ended but this workspace never
287
+ observed its own release fence, recovery also reports `acceptance_unknown: true`.
288
+ That result confirms capacity return while leaving the original operation's acceptance unknown.
289
+ Preserve the old workspace through completion. A later admission is a different lifetime,
290
+ and old references and receipts do not select it.
@@ -50,7 +50,7 @@ refuse() {
50
50
  }
51
51
 
52
52
  # The command: the first argument that is not a flag or a flag's value, as
53
- # sp itself finds it. Every sp flag but --apply and --help takes a value.
53
+ # sp itself finds it. --apply, --end-promises and --help take no value.
54
54
  # On the way, the operation id and the expiry, for the caller's grant after
55
55
  # a publication, and the reference, for the record of a grant.
56
56
  command=
@@ -70,7 +70,7 @@ for arg in "$@"; do
70
70
  fi
71
71
  case $arg in
72
72
  --) break ;;
73
- -apply | --apply | -help | --help | -h) ;;
73
+ -apply | --apply | -end-promises | --end-promises | -help | --help | -h) ;;
74
74
  -*=*)
75
75
  name=${arg%%=*}
76
76
  case ${name#-} in
@@ -94,7 +94,7 @@ for arg in "$@"; do
94
94
  case $arg in
95
95
  -*)
96
96
  case $name in
97
- url | creds | dir | volume | thread-id | root-id | tool-call-id | creator | grantee-account | grantee-user)
97
+ url | creds | dir | volume | bootstrap-file | thread-id | root-id | tool-call-id | creator | grantee-account | grantee-user)
98
98
  refuse "--$name is set by the agent, not passed; run the command without it" ;;
99
99
  esac
100
100
  ;;
@@ -102,12 +102,12 @@ for arg in "$@"; do
102
102
  done
103
103
 
104
104
  if [ -n "${SP_ENV_FILE:-}" ] && [ -f "$SP_ENV_FILE" ]; then
105
- unset SP_THREAD_ID SP_ROOT_ID SP_CALLER_ACCOUNT SP_CALLER_USER
105
+ unset SP_THREAD_ID SP_ROOT_ID SP_CALLER_ACCOUNT SP_CALLER_USER SCRATCHPAD_VOLUME SCRATCHPAD_BOOTSTRAP_FILE
106
106
  while IFS= read -r line || [ -n "$line" ]; do
107
107
  case $line in
108
108
  *=*)
109
109
  case ${line%%=*} in
110
- SP_NATS_URL | SP_NATS_CREDS | SCRATCHPAD_DIR | SCRATCHPAD_VOLUME | SP_CREATOR | \
110
+ SP_NATS_URL | SP_NATS_CREDS | SCRATCHPAD_DIR | SCRATCHPAD_VOLUME | SCRATCHPAD_BOOTSTRAP_FILE | SP_CREATOR | \
111
111
  SP_THREAD_ID | SP_ROOT_ID | SP_CALLER_ACCOUNT | SP_CALLER_USER | SP_GRANTS_FILE)
112
112
  # The name is one of these; the value is assigned as it is.
113
113
  eval "${line%%=*}=\${line#*=}"
@@ -150,8 +150,14 @@ fi
150
150
 
151
151
  case $command in
152
152
  init)
153
- [ -n "${SCRATCHPAD_VOLUME:-}" ] || refuse "ScratchPad has no volume for this agent: SCRATCHPAD_VOLUME is unset"
154
- set -- --volume "$SCRATCHPAD_VOLUME" "$@"
153
+ if [ -n "${SCRATCHPAD_BOOTSTRAP_FILE:-}" ]; then
154
+ set -- --bootstrap-file "$SCRATCHPAD_BOOTSTRAP_FILE" "$@"
155
+ else
156
+ [ -n "${SCRATCHPAD_VOLUME:-}" ] || refuse "ScratchPad has no volume or bootstrap file for this agent"
157
+ fi
158
+ if [ -n "${SCRATCHPAD_VOLUME:-}" ]; then
159
+ set -- --volume "$SCRATCHPAD_VOLUME" "$@"
160
+ fi
155
161
  ;;
156
162
  publish-file)
157
163
  if [ -n "${SP_CREATOR:-}" ]; then
package/CHANGELOG.md DELETED
@@ -1,18 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to `@synadia-ai/agent-fabric-claude-code` are recorded here. The
4
- format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
5
- package follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
-
7
- ## [0.1.0] — unreleased
8
-
9
- ### Added
10
-
11
- - The extension module for the Claude Code channel plugin, named in `SYNADIA_CLAUDE_CODE_EXTENSIONS`: `fabricTracing()` on the plugin's service and agent-tools client.
12
- - The signed `served` pair binding each served prompt to the Claude Code session, its `end` at the turn's `Stop`.
13
- - The `409` loop guard in front of the channel's handler.
14
- - The settings: the edge subject (`options.edgeSubject`, `SYNADIA_FABRIC_EDGE_SUBJECT`, or `TRACE.edges`) and `options.stopWaitMs`.
15
- - The Claude Code plugin `agent-fabric`: the ScratchPad skill with the sp command line for darwin and linux, and the `SessionStart` hook that names the file `scripts/sp` reads.
16
- - ScratchPad: references in and out, and grant requests.
17
-
18
- [0.1.0]: https://github.com/synadia-ai/agent-fabric-sdk/tree/main/plugins/claude-code