@synadia-ai/agent-fabric-claude-code 0.2.0 → 0.3.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nats-channel",
3
- "version": "0.6.0+fabric.554414d.bbd3128",
3
+ "version": "0.6.0+fabric.a204518.95f67d6",
4
4
  "description": "NATS channel plugin for Claude Code — bridges NATS messaging to Claude Code sessions, with the Synadia Agent Fabric add-on and ScratchPad",
5
5
  "author": {
6
6
  "name": "Synadia"
package/BUILD CHANGED
@@ -1,4 +1,4 @@
1
1
  plugin nats-channel 0.6.0
2
- synadia-agents 554414d
3
- agent-fabric-sdk bbd3128
4
- built 2026-10-11T08:01:50.287Z
2
+ synadia-agents a204518
3
+ agent-fabric-sdk 95f67d6
4
+ built 2026-10-27T07:45:52.971Z
package/README.md CHANGED
@@ -5,7 +5,8 @@ plugin for Claude Code (`nats-channel`), the fabric's tracing and
5
5
  ScratchPad's skill. The Claude Code session becomes an agent on the fabric:
6
6
  other agents find it and prompt it, its model calls join the prompt's
7
7
  thread, and its own prompts to other agents hang under the tool call that
8
- sent them.
8
+ sent them. A turn typed at the terminal is traced as a task of its own,
9
+ with the agents it prompts beneath it.
9
10
 
10
11
  ## Install
11
12
 
@@ -17,7 +18,7 @@ plugin; a marketplace names it with an npm source:
17
18
  "name": "agent-fabric",
18
19
  "owner": { "name": "Synadia" },
19
20
  "plugins": [
20
- { "name": "nats-channel", "source": { "source": "npm", "package": "@synadia-ai/agent-fabric-claude-code", "version": "^0.2.0" } }
21
+ { "name": "nats-channel", "source": { "source": "npm", "package": "@synadia-ai/agent-fabric-claude-code", "version": "^0.3.0" } }
21
22
  ]
22
23
  }
23
24
  ```
@@ -52,6 +53,7 @@ environment:
52
53
  | `SCRATCHPAD_VOLUME`, `SCRATCHPAD_DIR` | optional: the agent's ScratchPad volume and its private workspace |
53
54
 
54
55
  `NATS_CONTEXT`, a NATS CLI context, wins over `NATS_URL` and `NATS_CREDS`.
56
+ A context in the plugin's own config file counts only when neither is set.
55
57
 
56
58
  Start it with the channel, which is not on Claude Code's list of channels,
57
59
  so it loads as a development channel, with the warning Claude Code shows
package/hooks/hooks.json CHANGED
@@ -31,6 +31,28 @@
31
31
  ]
32
32
  }
33
33
  ],
34
+ "UserPromptSubmit": [
35
+ {
36
+ "hooks": [
37
+ {
38
+ "type": "command",
39
+ "command": "bun \"${CLAUDE_PLUGIN_ROOT}/hooks/session-event.js\"",
40
+ "timeout": 10
41
+ }
42
+ ]
43
+ }
44
+ ],
45
+ "StopFailure": [
46
+ {
47
+ "hooks": [
48
+ {
49
+ "type": "command",
50
+ "command": "bun \"${CLAUDE_PLUGIN_ROOT}/hooks/session-event.js\"",
51
+ "timeout": 10
52
+ }
53
+ ]
54
+ }
55
+ ],
34
56
  "PreToolUse": [
35
57
  {
36
58
  "hooks": [
@@ -1,11 +1,14 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import{writeSync as K}from"fs";import{homedir as V}from"os";import{join as Y}from"path";import{existsSync as rn,mkdirSync as g,readdirSync as sn,readFileSync as w,renameSync as J,rmSync as j,writeFileSync as D}from"fs";import{join as f}from"path";var U=/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/,q=/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/,F=["discover_agents","prompt_agent","wait_agent","answer_agent","cancel_agent","list_agent_calls"];function A(n){return typeof n==="string"&&U.test(n)}function L(n){return typeof n==="string"&&q.test(n)}function c(n){return f(n,"sessions")}function C(n,e){return f(c(n),String(e))}function b(n,e){return f(c(n),`${e}.stop`)}function h(n,e){return f(c(n),`${e}.nudge`)}function x(n,e){return f(c(n),`${e}.turn`)}function z(n,e){return f(c(n),`${e}.open`)}function Z(n,e){return f(c(n),`${e}.tools`)}function l(n,e){let t=`${n}.${process.pid}.tmp`;D(t,e),J(t,n)}var B=new RegExp(`^mcp__.+__(${F.join("|")})$`);function G(n){return B.test(n)}function v(n){if(!("background_tasks"in n)&&!("session_crons"in n))return;return k(n.background_tasks)||k(n.session_crons)}function k(n){if(n===void 0||n===null||n===!1||n===0||n==="")return!1;if(Array.isArray(n))return n.length>0;if(typeof n==="object")return Object.keys(n).length>0;return!0}function R(n,e,t,r=Date.now()){let s=t.hook_event_name,o=A(t.session_id)?t.session_id:void 0;if(s==="SessionStart"){if(o===void 0)return;let i=typeof t.source==="string"?t.source:"unknown";return g(c(n),{recursive:!0}),l(C(n,e),`${JSON.stringify({session_id:o,source:i,at_ms:r})}
4
- `),"session"}if(s==="Stop"){let i=v(t),u=O(n,e),d=u===void 0||u.length>0?W(n,e)??N(t):void 0;return g(c(n),{recursive:!0}),l(b(n,e),`${JSON.stringify({...o?{session_id:o}:{},...i!==void 0?{background:i}:{},...d!==void 0?{final_text:d}:{},at_ms:r})}
5
- `),Q(h(n,e)),"stop"}if(s==="PreToolUse"){let{tool_use_id:i,tool_name:u}=t;if(typeof u!=="string"||u.length===0)return;if(g(c(n),{recursive:!0}),H(n,e,t.prompt_id,r),!G(u)||!L(i))return"turn";let d=Z(n,e);return g(d,{recursive:!0}),l(f(d,i),`${JSON.stringify({tool_use_id:i,tool_name:u,tool_input:t.tool_input??null,at_ms:r})}
6
- `),"tool"}return}function H(n,e,t,r){let s=A(t)?t:void 0,o=m(x(n,e)),i=p(o?.at_ms),u=p(o?.first_ms),d=p(m(b(n,e))?.at_ms),a=o!==void 0&&o.prompt_id===s&&i!==void 0&&u!==void 0&&(d===void 0||i>d);l(x(n,e),`${JSON.stringify({...s!==void 0?{prompt_id:s}:{},first_ms:a?u:r,at_ms:r})}
7
- `)}function E(n,e,t){if(t.hook_event_name!=="Stop"||t.stop_hook_active===!0)return;if(v(t)!==!1)return;let r=O(n,e);if(r===void 0||r.length===0)return;let s=r.map((o)=>`"${o}"`).join(", ");return`The NATS channel ${r.length===1?"request":"requests"} ${s} ${r.length===1?"is":"are"} still open: the sender has not received an answer, and cannot see this session's own output. Send your answer with the reply tool (load it with ToolSearch if it is deferred), with the request_id and done: true.`}function I(n,e,t,r=Date.now()){let s=N(t);g(c(n),{recursive:!0}),l(h(n,e),`${JSON.stringify({...s!==void 0?{final_text:s}:{},at_ms:r})}
8
- `)}function W(n,e){let t=m(h(n,e)),r=p(t?.at_ms);if(t===void 0||r===void 0)return;let s=p(m(b(n,e))?.at_ms);if(s!==void 0&&r<=s)return;let o=t.final_text;return typeof o==="string"&&o.length>0?o:void 0}function O(n,e){let t=m(z(n,e));if(!t)return;let{server_pid:r,request_ids:s}=t;if(typeof r!=="number"||!Number.isSafeInteger(r)||r<=0)return;if(!Array.isArray(s)||!s.every((o)=>typeof o==="string"&&/^[A-Za-z0-9_-]{1,64}$/.test(o)))return;if(!X(r))return;return s}function N(n){if("last_assistant_message"in n){let i=n.last_assistant_message;return typeof i==="string"&&i.trim().length>0?i.trim():void 0}let e=n.transcript_path;if(typeof e!=="string"||e.length===0)return;let t;try{t=w(e,"utf8")}catch{return}let r,s=[];for(let i of t.split(`
9
- `)){if(i.trim().length===0)continue;let u;try{u=JSON.parse(i)}catch{continue}if(typeof u!=="object"||u===null)continue;let{type:d,message:a,uuid:T}=u;if(d!=="assistant"||typeof a!=="object"||a===null)continue;let{id:M,content:_}=a,y=M??T;if(y===void 0||y!==r)r=y,s=[];if(typeof _==="string")s.push(_);else if(Array.isArray(_))for(let P of _){let S=P;if(S?.type==="text"&&typeof S.text==="string")s.push(S.text)}}let o=s.join(`
10
- `).trim();return o.length>0?o:void 0}function m(n){let e;try{e=w(n,"utf8")}catch{return}try{let t=JSON.parse(e);return typeof t==="object"&&t!==null&&!Array.isArray(t)?t:void 0}catch{return}}function p(n){return typeof n==="number"&&Number.isSafeInteger(n)&&n>=0?n:void 0}function Q(n){try{j(n,{recursive:!0,force:!0})}catch{}}function X(n){try{return process.kill(n,0),!0}catch(e){return e.code==="EPERM"}}var nn=5000;setTimeout(()=>process.exit(0),nn).unref();async function en(){let n=[];for await(let e of process.stdin)n.push(Buffer.from(e));return Buffer.concat(n).toString("utf8")}try{let n=process.env.CLAUDE_PID;if(n!==void 0&&/^\d+$/.test(n)){let e=JSON.parse(await en());if(typeof e==="object"&&e!==null&&!Array.isArray(e)){let t=process.env.NATS_STATE_DIR??Y(V(),".claude","channels","nats"),r=e,s=E(t,n,r);if(s!==void 0){try{I(t,n,r)}catch{}K(1,`${JSON.stringify({decision:"block",reason:s})}
11
- `)}else R(t,n,r)}}}catch{}process.exit(0);
3
+ import{writeSync as mn}from"fs";import{homedir as _n}from"os";import{join as yn}from"path";import{appendFileSync as D,existsSync as xn,mkdirSync as _,readdirSync as hn,readFileSync as E,renameSync as G,rmSync as X,writeFileSync as W}from"fs";import{join as a}from"path";var Q=/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/,K=/^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/,V=["discover_agents","prompt_agent","wait_agent","answer_agent","cancel_agent","list_agent_calls"];function p(n){return typeof n==="string"&&Q.test(n)}function Y(n){return typeof n==="string"&&K.test(n)}function f(n){return a(n,"sessions")}function M(n,e){return a(f(n),String(e))}function h(n,e){return a(f(n),`${e}.stop`)}function k(n,e){return a(f(n),`${e}.nudge`)}function A(n,e){return a(f(n),`${e}.turn`)}function nn(n,e){return a(f(n),`${e}.open`)}function q(n,e){return a(f(n),`${e}.tools`)}function v(n,e){return a(f(n),`${e}.events`)}function b(n,e){let t=`${n}.${process.pid}.tmp`;W(t,e),G(t,n)}var en=new RegExp(`^mcp__.+__(${V.join("|")})$`);function tn(n){return en.test(n)}function P(n){if(!("background_tasks"in n)&&!("session_crons"in n))return;return N(n.background_tasks)||N(n.session_crons)}function N(n){if(n===void 0||n===null||n===!1||n===0||n==="")return!1;if(Array.isArray(n))return n.length>0;if(typeof n==="object")return Object.keys(n).length>0;return!0}var J=/^plugin:[^"]+:nats$/,O=/^[A-Za-z0-9_-]{1,64}$/,rn=1024,U=64,j=200;function sn(n){if(typeof n!=="string"||!n.startsWith("<channel "))return;let e=n.indexOf(">");if(e<0||e>rn)return;let t=n.slice(0,e),r=/\ssource="([^"]*)"/.exec(t)?.[1],s=/\srequest_id="([^"]*)"/.exec(t)?.[1];if(r===void 0||s===void 0)return;if(!J.test(r)||!O.test(s))return;return{requestId:s,source:r}}function x(n,e){if(typeof n!=="string")return;let t=Array.from(n.replace(/[\u0000-\u001f\u007f]+/g," ").trim()).slice(0,e).join("");return t.length>0?t:void 0}function w(n,e,t){_(f(n),{recursive:!0}),D(v(n,e),`${JSON.stringify(t)}
4
+ `)}function F(n,e,t,r=Date.now()){let s=t.hook_event_name,o=p(t.session_id)?t.session_id:void 0,d=p(t.prompt_id)?t.prompt_id:void 0;if(s==="UserPromptSubmit"){let i=sn(t.prompt);return w(n,e,{event:"submit",...o?{session_id:o}:{},...d?{prompt_id:d}:{},...i?{request_id:i.requestId,source:i.source}:{},at_ms:r}),"submit"}if(s==="StopFailure"){let i=x(t.error,U),u=x(t.error_details,j);return w(n,e,{event:"failure",...o?{session_id:o}:{},...d?{prompt_id:d}:{},...i!==void 0?{error:i}:{},...u!==void 0?{error_details:u}:{},at_ms:r}),"failure"}if(s==="SessionStart"){if(t.source==="startup")gn(n,e);if(o===void 0)return;let i=typeof t.source==="string"?t.source:"unknown";return _(f(n),{recursive:!0}),b(M(n,e),`${JSON.stringify({session_id:o,source:i,at_ms:r})}
5
+ `),"session"}if(s==="Stop"){let i=P(t),u=H(n,e,t),c=u===void 0||u.length>0?dn(n,e)??z(t):void 0,m={...o?{session_id:o}:{},...i!==void 0?{background:i}:{},...c!==void 0?{final_text:c}:{},at_ms:r};return _(f(n),{recursive:!0}),b(h(n,e),`${JSON.stringify(m)}
6
+ `),w(n,e,{event:"stop",...d?{prompt_id:d}:{},...m}),Z(k(n,e)),"stop"}if(s==="PreToolUse"){let{tool_use_id:i,tool_name:u}=t;if(typeof u!=="string"||u.length===0)return;if(_(f(n),{recursive:!0}),on(n,e,t.prompt_id,r),!tn(u)||!Y(i))return"turn";let c=q(n,e);return _(c,{recursive:!0}),b(a(c,i),`${JSON.stringify({tool_use_id:i,tool_name:u,tool_input:t.tool_input??null,...d?{prompt_id:d}:{},at_ms:r})}
7
+ `),"tool"}return}function on(n,e,t,r){let s=p(t)?t:void 0,o=S(A(n,e)),d=l(o?.at_ms),i=l(o?.first_ms),u=l(S(h(n,e))?.at_ms),c=o!==void 0&&o.prompt_id===s&&d!==void 0&&i!==void 0&&(u===void 0||d>u);b(A(n,e),`${JSON.stringify({...s!==void 0?{prompt_id:s}:{},first_ms:c?i:r,at_ms:r})}
8
+ `)}function L(n,e,t){if(t.hook_event_name!=="Stop"||t.stop_hook_active===!0)return;if(P(t)!==!1)return;let r=H(n,e,t);if(r===void 0||r.length===0)return;let s=r.map((o)=>`"${o}"`).join(", ");return`The NATS channel ${r.length===1?"request":"requests"} ${s} ${r.length===1?"is":"are"} still open: the sender has not received an answer, and cannot see this session's own output. Send your answer with the reply tool (load it with ToolSearch if it is deferred), with the request_id and done: true.`}function C(n,e,t,r=Date.now()){let s=z(t);_(f(n),{recursive:!0}),b(k(n,e),`${JSON.stringify({...s!==void 0?{final_text:s}:{},at_ms:r})}
9
+ `)}function dn(n,e){let t=S(k(n,e)),r=l(t?.at_ms);if(t===void 0||r===void 0)return;let s=l(S(h(n,e))?.at_ms);if(s!==void 0&&r<=s)return;let o=t.final_text;return typeof o==="string"&&o.length>0?o:void 0}function un(n,e){let t=S(nn(n,e));if(!t)return;let r=t.server_pid,s=T(t.request_ids),o=t.open_ids===void 0?s:T(t.open_ids);if(typeof r!=="number"||!Number.isSafeInteger(r)||r<=0)return;if(s===void 0||o===void 0)return;if(!ln(r))return;return{requestIds:s,openIds:o}}function T(n){return Array.isArray(n)&&n.every((e)=>typeof e==="string"&&O.test(e))?n:void 0}function H(n,e,t){let r=un(n,e);if(r===void 0)return;let s=p(t.prompt_id)?t.prompt_id:void 0;if(s===void 0)return r.requestIds;let o=pn(n,e);if(!o.some((u)=>u.kind==="submit"&&u.submit.promptId!==void 0))return r.requestIds;let d=new Map,i;for(let u of o){if(u.kind!=="submit"||u.submit.requestId===void 0){i=void 0;continue}let{requestId:c,promptId:m,atMs:I}=u.submit,g=i!==void 0&&I-i.atMs<=cn?i.promptIds:new Set;g.add(m),d.set(c,g),i={atMs:I,promptIds:g}}return r.openIds.filter((u)=>d.get(u)?.has(s)===!0)}var cn=30000;function z(n){if("last_assistant_message"in n){let d=n.last_assistant_message;return typeof d==="string"&&d.trim().length>0?d.trim():void 0}let e=n.transcript_path;if(typeof e!=="string"||e.length===0)return;let t;try{t=E(e,"utf8")}catch{return}let r,s=[];for(let d of t.split(`
10
+ `)){if(d.trim().length===0)continue;let i;try{i=JSON.parse(d)}catch{continue}if(typeof i!=="object"||i===null)continue;let{type:u,message:c,uuid:m}=i;if(u!=="assistant"||typeof c!=="object"||c===null)continue;let{id:I,content:y}=c,g=I??m;if(g===void 0||g!==r)r=g,s=[];if(typeof y==="string")s.push(y);else if(Array.isArray(y))for(let B of y){let R=B;if(R?.type==="text"&&typeof R.text==="string")s.push(R.text)}}let o=s.join(`
11
+ `).trim();return o.length>0?o:void 0}function S(n){let e;try{e=E(n,"utf8")}catch{return}try{let t=JSON.parse(e);return typeof t==="object"&&t!==null&&!Array.isArray(t)?t:void 0}catch{return}}function l(n){return typeof n==="number"&&Number.isSafeInteger(n)&&n>=0?n:void 0}function fn(n){if(!n)return;let e=l(n.at_ms);if(e===void 0)return;return{...p(n.session_id)?{sessionId:n.session_id}:{},...p(n.prompt_id)?{promptId:n.prompt_id}:{},...typeof n.background==="boolean"?{background:n.background}:{},...typeof n.final_text==="string"&&n.final_text.length>0?{finalText:n.final_text}:{},atMs:e}}function an(n){let e;try{e=JSON.parse(n)}catch{return}if(typeof e!=="object"||e===null||Array.isArray(e))return;let t=e,r=l(t.at_ms);if(r===void 0)return;let s={...p(t.session_id)?{sessionId:t.session_id}:{},...p(t.prompt_id)?{promptId:t.prompt_id}:{}};switch(t.event){case"submit":{let o=typeof t.request_id==="string"&&O.test(t.request_id)&&typeof t.source==="string"&&J.test(t.source)?{requestId:t.request_id,source:t.source}:{};return{kind:"submit",submit:{...s,...o,atMs:r}}}case"stop":{let o=fn(t);return o?{kind:"stop",stop:o}:void 0}case"failure":{let o=x(t.error,U),d=x(t.error_details,j);return{kind:"failure",failure:{...s,...o!==void 0?{error:o}:{},...d!==void 0?{errorDetails:d}:{},atMs:r}}}default:return}}function pn(n,e){let t;try{t=E(v(n,e),"utf8")}catch{return[]}let r=[],s=t.lastIndexOf(`
12
+ `);for(let o of t.slice(0,s+1).split(`
13
+ `)){if(o.length===0)continue;let d=an(o);if(d)r.push(d)}return r}function Z(n){try{X(n,{recursive:!0,force:!0})}catch{}}function gn(n,e){for(let t of[M(n,e),h(n,e),A(n,e),v(n,e),k(n,e),q(n,e)])Z(t)}function ln(n){try{return process.kill(n,0),!0}catch(e){return e.code==="EPERM"}}var bn=5000;setTimeout(()=>process.exit(0),bn).unref();async function Sn(){let n=[];for await(let e of process.stdin)n.push(Buffer.from(e));return Buffer.concat(n).toString("utf8")}try{let n=process.env.CLAUDE_PID;if(n!==void 0&&/^\d+$/.test(n)){let e=JSON.parse(await Sn());if(typeof e==="object"&&e!==null&&!Array.isArray(e)){let t=process.env.NATS_STATE_DIR??yn(_n(),".claude","channels","nats"),r=e,s=L(t,n,r);if(s!==void 0){try{C(t,n,r)}catch{}mn(1,`${JSON.stringify({decision:"block",reason:s})}
14
+ `)}else F(t,n,r)}}}catch{}process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synadia-ai/agent-fabric-claude-code",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Claude Code on the Synadia Agent Fabric: the NATS channel plugin for Claude Code with the fabric's tracing and ScratchPad, in one plugin",
5
5
  "keywords": [
6
6
  "claude-code",