shennian 0.3.12 → 0.3.13

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.
@@ -296,8 +296,8 @@
296
296
  },
297
297
  {
298
298
  "file": "src/integrations/room-session-hook.js",
299
- "beforeBytes": 11963,
300
- "afterBytes": 6166
299
+ "beforeBytes": 12200,
300
+ "afterBytes": 6164
301
301
  },
302
302
  {
303
303
  "file": "src/integrations/workbuddy-plugin-bundle.js",
@@ -311,8 +311,8 @@
311
311
  },
312
312
  {
313
313
  "file": "src/integrations/workbuddy-session-hook.js",
314
- "beforeBytes": 2624,
315
- "afterBytes": 1254
314
+ "beforeBytes": 2622,
315
+ "afterBytes": 1252
316
316
  },
317
317
  {
318
318
  "file": "src/log-reporter.js",
@@ -8,7 +8,7 @@ declare const LIFECYCLE_BY_EVENT: {
8
8
  readonly PostCompact: "active";
9
9
  readonly PreToolUse: "active";
10
10
  readonly Stop: "idle";
11
- readonly SessionEnd: "closed";
11
+ readonly SessionEnd: "idle";
12
12
  };
13
13
  type HookEventName = keyof typeof LIFECYCLE_BY_EVENT;
14
14
  type InvokeDaemon = typeof callDaemonIpc;
@@ -1,2 +1,2 @@
1
- import I from"node:crypto";import{callDaemonIpc as b}from"../daemon-ipc/client.js";import{DaemonIpcError as f}from"../daemon-ipc/protocol.js";import{normalizeRoomActionParameters as E}from"../room/room-action-schema.js";import{SHENNIAN_ROOM_TOOL_NAME as T}from"../room/tool-schema.js";import{roomMcpHostToolName as z}from"./room-plugin-shared.js";const N=256*1024,_={SessionStart:"active",UserPromptSubmit:"active",PreCompact:"active",PostCompact:"active",PreToolUse:"active",Stop:"idle",SessionEnd:"closed"};async function Y(t,o=process.stdin,e=process.stdout,r=b){let s="";for await(const a of o)if(s+=Buffer.isBuffer(a)?a.toString("utf8"):String(a),Buffer.byteLength(s,"utf8")>N)return"ignored";const n=O(t,s);if(!n)return"ignored";let l=!1;try{await r({method:"room-host-session.observe",params:{runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey,hostContextHash:n.hostContextHash,workspaceHash:n.workspaceHash,workspacePath:n.workspacePath,lifecycleState:n.lifecycleState,eventName:n.eventName},timeoutMs:2e3})}catch(a){if(a instanceof f&&a.code==="machine_not_authorized")l=!0;else return"unavailable"}if(n.eventName!=="PreToolUse")return"observed";if(n.toolName!==z(n.runtimeAdapter)||!n.toolInput)return"ignored";let i=null,c="";try{if(i=H(n.toolInput),c=v(n,i),i.action==="room.create"||i.action==="room.join")try{const u=await r({method:"authorized-action.status",params:{idempotencyKey:c},timeoutMs:2e3}),k=A(u);return S(e,n.toolInput,k),"authorized"}catch(u){if(!(u instanceof f)||u.code!=="pending_action_not_found")throw u}if(l)return await w(n,i,c,e,r);const a=await r({method:"room-session.resolve",params:{runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey},timeoutMs:2e3}),d=K(a),g=await r({method:"room-invocation.issue",params:{registryKey:d,action:i.action,targetTool:T,actionParameters:i.actionParameters},timeoutMs:2e3}),P=$(g);return y(e,{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"Authorized by the local Shennian daemon for this Agent conversation.",updatedInput:{...n.toolInput,_shennianRegistryKey:d,_shennianInvocationToken:P}}}),"authorized"}catch(a){if(a instanceof f&&a.code==="machine_not_authorized"&&i&&(i.action==="room.create"||i.action==="room.join"))try{return await w(n,i,c,e,r)}catch{}return y(e,{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"This Agent conversation is not connected to Shennian yet. Open it from Shennian or use the repair connection action."}}),"denied"}}async function w(t,o,e,r,s){if(o.action!=="room.create"&&o.action!=="room.join")throw new Error("authorization_requires_onboarding_action");const n=await s({method:"authorized-action.create",params:{actionType:o.action,actionSummary:R(o.action,o.actionParameters),actionPayload:x(t,o),idempotencyKey:e,returnTo:"/spaces"},timeoutMs:12e3});return S(r,t.toolInput,A(n)),"authorized"}function O(t,o){if(t!=="codex"&&t!=="claude_code"&&t!=="workbuddy")return null;let e;try{e=JSON.parse(o)}catch{return null}if(!e||typeof e!="object"||Array.isArray(e))return null;const r=e,s=m(r.session_id,1024),n=U(r.hook_event_name);if(!s||!n)return null;const l=m(r.transcript_path,4096)??"",i=m(r.cwd,4096)??"",c=n==="PreToolUse"?m(r.tool_name,256):null,a=n==="PreToolUse"?C(r.tool_input):null,d=n==="PreToolUse"?m(r.tool_use_id??r.tool_call_id,512):null;return{runtimeAdapter:t,sourceSessionKey:s,hostContextHash:h(`${s}\0${l}`),workspaceHash:i?h(i):null,workspacePath:i||null,lifecycleState:_[n],eventName:n,toolName:c,toolInput:a,toolRequestId:d}}function H(t){const o=j(t.action);if(Object.keys(t).some(r=>r.startsWith("_shennian")))throw new Error("hidden_input");const e=Object.fromEntries(Object.entries(t).filter(([r])=>r!=="action"));return{action:o,actionParameters:E(o,e)}}function j(t){const o={create:"room.create",join:"room.join",read:"room.read",send:"room.send",mode:"room.mode",status:"room.status"};if(typeof t!="string"||!(t in o))throw new Error("invalid_action");return o[t]}function K(t){const o=p(t,"registryKey");if(!/^[a-f0-9]{64}$/.test(o))throw new Error("invalid_registry");return o}function $(t){const o=p(t,"invocationToken");if(!/^sni_v1_[A-Za-z0-9_-]{40,80}$/.test(o))throw new Error("invalid_token");return o}function A(t){const o=p(t,"id");if(!/^[A-Za-z0-9_-]{8,128}$/.test(o))throw new Error("invalid_pending_action");return o}function v(t,o){const e=t.toolRequestId??h(JSON.stringify(o.actionParameters));return`${o.action.replace(".","-")}-auth:${h(`${t.runtimeAdapter}\0${t.sourceSessionKey}\0${e}`)}`}function x(t,o){return{schemaVersion:1,runtimeAdapter:t.runtimeAdapter,sourceSessionKey:t.sourceSessionKey,hostContextHash:t.hostContextHash,workspaceHash:t.workspaceHash,workspacePath:t.workspacePath,lifecycleState:t.lifecycleState,action:o.action,actionParameters:o.actionParameters}}function R(t,o){if(!o||Array.isArray(o)||typeof o!="object")throw new Error("invalid_authorized_action");const e=typeof o.agentAlias=="string"?o.agentAlias:"";return t==="room.create"?`\u521B\u5EFA\u7FA4\u804A\u300C${typeof o.name=="string"?o.name:""}\u300D\uFF0C\u5E76\u4EE5 Agent\u300C${e}\u300D\u52A0\u5165`:`\u4F7F\u7528\u5F53\u524D\u9080\u8BF7\u52A0\u5165\u7FA4\u804A\uFF0C\u5E76\u4EE5 Agent\u300C${e}\u300D\u53C2\u4E0E`}function S(t,o,e){y(t,{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"Shennian will open a secure browser authorization and continue this action automatically.",updatedInput:{...o,_shennianAuthorizedActionId:e}}})}function p(t,o){if(!t||Array.isArray(t)||typeof t!="object")throw new Error("invalid_response");const e=t[o];if(typeof e!="string")throw new Error("invalid_response");return e}function U(t){return typeof t=="string"&&t in _?t:null}function m(t,o){if(typeof t!="string")return null;const e=t.trim();return!e||e.length>o||/[\0\r\n]/.test(e)?null:e}function C(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function y(t,o){t.write(`${JSON.stringify(o)}
1
+ import I from"node:crypto";import{callDaemonIpc as b}from"../daemon-ipc/client.js";import{DaemonIpcError as f}from"../daemon-ipc/protocol.js";import{normalizeRoomActionParameters as E}from"../room/room-action-schema.js";import{SHENNIAN_ROOM_TOOL_NAME as T}from"../room/tool-schema.js";import{roomMcpHostToolName as z}from"./room-plugin-shared.js";const N=256*1024,_={SessionStart:"active",UserPromptSubmit:"active",PreCompact:"active",PostCompact:"active",PreToolUse:"active",Stop:"idle",SessionEnd:"idle"};async function Y(t,o=process.stdin,e=process.stdout,r=b){let s="";for await(const a of o)if(s+=Buffer.isBuffer(a)?a.toString("utf8"):String(a),Buffer.byteLength(s,"utf8")>N)return"ignored";const n=O(t,s);if(!n)return"ignored";let l=!1;try{await r({method:"room-host-session.observe",params:{runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey,hostContextHash:n.hostContextHash,workspaceHash:n.workspaceHash,workspacePath:n.workspacePath,lifecycleState:n.lifecycleState,eventName:n.eventName},timeoutMs:2e3})}catch(a){if(a instanceof f&&a.code==="machine_not_authorized")l=!0;else return"unavailable"}if(n.eventName!=="PreToolUse")return"observed";if(n.toolName!==z(n.runtimeAdapter)||!n.toolInput)return"ignored";let i=null,c="";try{if(i=H(n.toolInput),c=v(n,i),i.action==="room.create"||i.action==="room.join")try{const u=await r({method:"authorized-action.status",params:{idempotencyKey:c},timeoutMs:2e3}),k=A(u);return S(e,n.toolInput,k),"authorized"}catch(u){if(!(u instanceof f)||u.code!=="pending_action_not_found")throw u}if(l)return await w(n,i,c,e,r);const a=await r({method:"room-session.resolve",params:{runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey},timeoutMs:2e3}),d=K(a),g=await r({method:"room-invocation.issue",params:{registryKey:d,action:i.action,targetTool:T,actionParameters:i.actionParameters},timeoutMs:2e3}),P=$(g);return y(e,{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"Authorized by the local Shennian daemon for this Agent conversation.",updatedInput:{...n.toolInput,_shennianRegistryKey:d,_shennianInvocationToken:P}}}),"authorized"}catch(a){if(a instanceof f&&a.code==="machine_not_authorized"&&i&&(i.action==="room.create"||i.action==="room.join"))try{return await w(n,i,c,e,r)}catch{}return y(e,{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"This Agent conversation is not connected to Shennian yet. Open it from Shennian or use the repair connection action."}}),"denied"}}async function w(t,o,e,r,s){if(o.action!=="room.create"&&o.action!=="room.join")throw new Error("authorization_requires_onboarding_action");const n=await s({method:"authorized-action.create",params:{actionType:o.action,actionSummary:R(o.action,o.actionParameters),actionPayload:x(t,o),idempotencyKey:e,returnTo:"/spaces"},timeoutMs:12e3});return S(r,t.toolInput,A(n)),"authorized"}function O(t,o){if(t!=="codex"&&t!=="claude_code"&&t!=="workbuddy")return null;let e;try{e=JSON.parse(o)}catch{return null}if(!e||typeof e!="object"||Array.isArray(e))return null;const r=e,s=m(r.session_id,1024),n=U(r.hook_event_name);if(!s||!n)return null;const l=m(r.transcript_path,4096)??"",i=m(r.cwd,4096)??"",c=n==="PreToolUse"?m(r.tool_name,256):null,a=n==="PreToolUse"?C(r.tool_input):null,d=n==="PreToolUse"?m(r.tool_use_id??r.tool_call_id,512):null;return{runtimeAdapter:t,sourceSessionKey:s,hostContextHash:h(`${s}\0${l}`),workspaceHash:i?h(i):null,workspacePath:i||null,lifecycleState:_[n],eventName:n,toolName:c,toolInput:a,toolRequestId:d}}function H(t){const o=j(t.action);if(Object.keys(t).some(r=>r.startsWith("_shennian")))throw new Error("hidden_input");const e=Object.fromEntries(Object.entries(t).filter(([r])=>r!=="action"));return{action:o,actionParameters:E(o,e)}}function j(t){const o={create:"room.create",join:"room.join",read:"room.read",send:"room.send",mode:"room.mode",status:"room.status"};if(typeof t!="string"||!(t in o))throw new Error("invalid_action");return o[t]}function K(t){const o=p(t,"registryKey");if(!/^[a-f0-9]{64}$/.test(o))throw new Error("invalid_registry");return o}function $(t){const o=p(t,"invocationToken");if(!/^sni_v1_[A-Za-z0-9_-]{40,80}$/.test(o))throw new Error("invalid_token");return o}function A(t){const o=p(t,"id");if(!/^[A-Za-z0-9_-]{8,128}$/.test(o))throw new Error("invalid_pending_action");return o}function v(t,o){const e=t.toolRequestId??h(JSON.stringify(o.actionParameters));return`${o.action.replace(".","-")}-auth:${h(`${t.runtimeAdapter}\0${t.sourceSessionKey}\0${e}`)}`}function x(t,o){return{schemaVersion:1,runtimeAdapter:t.runtimeAdapter,sourceSessionKey:t.sourceSessionKey,hostContextHash:t.hostContextHash,workspaceHash:t.workspaceHash,workspacePath:t.workspacePath,lifecycleState:t.lifecycleState,action:o.action,actionParameters:o.actionParameters}}function R(t,o){if(!o||Array.isArray(o)||typeof o!="object")throw new Error("invalid_authorized_action");const e=typeof o.agentAlias=="string"?o.agentAlias:"";return t==="room.create"?`\u521B\u5EFA\u7FA4\u804A\u300C${typeof o.name=="string"?o.name:""}\u300D\uFF0C\u5E76\u4EE5 Agent\u300C${e}\u300D\u52A0\u5165`:`\u4F7F\u7528\u5F53\u524D\u9080\u8BF7\u52A0\u5165\u7FA4\u804A\uFF0C\u5E76\u4EE5 Agent\u300C${e}\u300D\u53C2\u4E0E`}function S(t,o,e){y(t,{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow",permissionDecisionReason:"Shennian will open a secure browser authorization and continue this action automatically.",updatedInput:{...o,_shennianAuthorizedActionId:e}}})}function p(t,o){if(!t||Array.isArray(t)||typeof t!="object")throw new Error("invalid_response");const e=t[o];if(typeof e!="string")throw new Error("invalid_response");return e}function U(t){return typeof t=="string"&&t in _?t:null}function m(t,o){if(typeof t!="string")return null;const e=t.trim();return!e||e.length>o||/[\0\r\n]/.test(e)?null:e}function C(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function y(t,o){t.write(`${JSON.stringify(o)}
2
2
  `)}function h(t){return I.createHash("sha256").update(t,"utf8").digest("hex")}export{O as parseRoomSessionHookInput,Y as runRoomSessionHook};
@@ -6,7 +6,7 @@ declare const LIFECYCLE_BY_EVENT: {
6
6
  readonly PreCompact: "active";
7
7
  readonly PostCompact: "active";
8
8
  readonly Stop: "idle";
9
- readonly SessionEnd: "closed";
9
+ readonly SessionEnd: "idle";
10
10
  };
11
11
  type SupportedHookEvent = keyof typeof LIFECYCLE_BY_EVENT;
12
12
  export type WorkBuddyHookObservation = {
@@ -1 +1 @@
1
- import f from"node:crypto";import{callDaemonIpc as d}from"../daemon-ipc/client.js";const p=256*1024,a={SessionStart:"active",UserPromptSubmit:"active",PreCompact:"active",PostCompact:"active",Stop:"idle",SessionEnd:"closed"};async function S(e=process.stdin,r=d){let t="";for await(const n of e)if(t+=Buffer.isBuffer(n)?n.toString("utf8"):String(n),Buffer.byteLength(t,"utf8")>p)return"ignored";const o=l(t);if(!o)return"ignored";try{return await r({method:"room-host-session.observe",params:o,timeoutMs:2e3}),"observed"}catch{return"unavailable"}}function l(e){let r;try{r=JSON.parse(e)}catch{return null}if(!r||typeof r!="object"||Array.isArray(r))return null;const t=r,o=s(t.session_id,1024),n=m(t.hook_event_name);if(!o||!n)return null;const u=s(t.transcript_path,4096)??"",i=s(t.cwd,4096)??"";return{runtimeAdapter:"workbuddy",sourceSessionKey:o,hostContextHash:c(`${o}\0${u}`),workspaceHash:i?c(i):null,lifecycleState:a[n],eventName:n}}function m(e){return typeof e=="string"&&e in a?e:null}function s(e,r){if(typeof e!="string")return null;const t=e.trim();return!t||t.length>r||/[\0\r\n]/.test(t)?null:t}function c(e){return f.createHash("sha256").update(e,"utf8").digest("hex")}export{l as parseWorkBuddyHookInput,S as runWorkBuddySessionHook};
1
+ import f from"node:crypto";import{callDaemonIpc as d}from"../daemon-ipc/client.js";const p=256*1024,a={SessionStart:"active",UserPromptSubmit:"active",PreCompact:"active",PostCompact:"active",Stop:"idle",SessionEnd:"idle"};async function S(e=process.stdin,r=d){let t="";for await(const n of e)if(t+=Buffer.isBuffer(n)?n.toString("utf8"):String(n),Buffer.byteLength(t,"utf8")>p)return"ignored";const o=l(t);if(!o)return"ignored";try{return await r({method:"room-host-session.observe",params:o,timeoutMs:2e3}),"observed"}catch{return"unavailable"}}function l(e){let r;try{r=JSON.parse(e)}catch{return null}if(!r||typeof r!="object"||Array.isArray(r))return null;const t=r,o=s(t.session_id,1024),n=m(t.hook_event_name);if(!o||!n)return null;const u=s(t.transcript_path,4096)??"",i=s(t.cwd,4096)??"";return{runtimeAdapter:"workbuddy",sourceSessionKey:o,hostContextHash:c(`${o}\0${u}`),workspaceHash:i?c(i):null,lifecycleState:a[n],eventName:n}}function m(e){return typeof e=="string"&&e in a?e:null}function s(e,r){if(typeof e!="string")return null;const t=e.trim();return!t||t.length>r||/[\0\r\n]/.test(t)?null:t}function c(e){return f.createHash("sha256").update(e,"utf8").digest("hex")}export{l as parseWorkBuddyHookInput,S as runWorkBuddySessionHook};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.3.12",
3
+ "version": "0.3.13",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {