shennian 0.5.13 → 0.5.15
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.
- package/dist/publish-build-manifest.json +121 -51
- package/dist/src/agents/acp/adapter.js +2 -2
- package/dist/src/agents/acp/discovery.js +1 -1
- package/dist/src/agents/adapter.js +2 -1
- package/dist/src/agents/claude-permission-bridge.js +3 -0
- package/dist/src/agents/claude-permission-protocol.js +1 -0
- package/dist/src/agents/claude-permission-relay-server.js +3 -0
- package/dist/src/agents/claude.js +7 -7
- package/dist/src/agents/codex-managed-room-tools.js +1 -1
- package/dist/src/agents/codex-runtime.js +7 -7
- package/dist/src/agents/codex.js +3 -3
- package/dist/src/agents/project-client.js +1 -0
- package/dist/src/agents/public-delivery-client.js +1 -1
- package/dist/src/agents/workbuddy.js +3 -2
- package/dist/src/commands/daemon.js +9 -9
- package/dist/src/commands/pair-browser.js +1 -1
- package/dist/src/commands/pair.js +11 -11
- package/dist/src/commands/project.js +4 -0
- package/dist/src/commands/setup.js +4 -0
- package/dist/src/commands/steward.js +3 -0
- package/dist/src/daemon-ipc/agent-readiness.js +1 -1
- package/dist/src/daemon-ipc/control.js +1 -1
- package/dist/src/index.js +3 -3
- package/dist/src/session/conversation-export.js +1 -0
- package/dist/src/session/handlers/chat-adapter-config.js +1 -1
- package/dist/src/session/handlers/chat-events.js +2 -2
- package/dist/src/session/handlers/chat-session-lifecycle.js +1 -1
- package/dist/src/session/handlers/chat.js +1 -1
- package/dist/src/session/handlers/steward.js +1 -0
- package/dist/src/session/manager.js +1 -1
- package/dist/src/session/queue.js +1 -1
- package/dist/src/session/store.js +2 -2
- package/dist/src/skill/hosts.js +1 -0
- package/dist/src/skill/updater.js +1 -1
- package/dist/src/steward/session.js +2 -0
- package/dist/src/steward/templates.js +66 -0
- package/dist/src/steward/workspace.js +1 -0
- package/dist/src/upgrade/engine.js +2 -2
- package/dist/src/upgrade/npm-command.js +1 -0
- package/dist/src/upgrade/updater-launcher.js +3 -3
- package/node_modules/@shennian/wire/dist/message-payload.js +5 -5
- package/node_modules/@shennian/wire/dist/session.js +1 -1
- package/node_modules/ws/lib/websocket.js +17 -3
- package/node_modules/ws/package.json +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import r from"chalk";import{getDaemonInstallationId as
|
|
2
|
-
`)}function f(
|
|
3
|
-
\u2717 Pairing code expired. Please re-run: shennian pair`)),process.exit(1))),!a.ok)continue;const t=await a.json();if(t.status==="claimed"&&t.machineToken&&t.machineId&&t.accountNamespaceId)return
|
|
1
|
+
import r from"chalk";import{getDaemonInstallationId as B}from"../personal-sync/identity.js";import k from"node:os";import R from"node:readline";import{loadConfig as u,saveConfig as v}from"../config/index.js";import{detectAgents as E}from"../agents/detect.js";import{clearRemoteAccessDisabled as A,getDaemonStatus as L,startDaemonProcess as b,installService as U,saveEnvSnapshot as x}from"./daemon.js";import{waitForDaemonReadyStatus as M}from"./daemon-start-wait.js";import{detectAndChooseServer as N}from"../region.js";import{buildPairQrPayload as F,PAIR_QR_RENDER_OPTIONS as J}from"./pair-qr.js";import{BrowserPairError as q,parsePairModeChoice as Q,resolvePairMode as O,runBrowserPairFlow as z}from"./pair-browser.js";import{markAllSessionIndexesForRepublish as j}from"../session/store.js";const K=3e3,C=600*1e3;function l(n){process.stdout.write(`${JSON.stringify(n)}
|
|
2
|
+
`)}function f(n,e){l({type:"error",code:n,message:e}),process.exit(1)}function W(n){const e="\u2500".repeat(n.length+4);console.log(r.cyan(`\u250C${e}\u2510`)),console.log(r.cyan("\u2502 ")+r.bold.yellow(n)+r.cyan(" \u2502")),console.log(r.cyan(`\u2514${e}\u2518`))}async function Y(n){const e=(await import("qrcode-terminal")).default;return new Promise(o=>{e.generate(n,J,i=>{console.log(i),o()})})}async function H(n,e,o={}){const i=Date.now()+C;for(o.json&&l({type:"pair.waiting"});Date.now()<i;){await new Promise(d=>setTimeout(d,K));let a;try{a=await fetch(`${n}/api/machines/pair/status?token=${encodeURIComponent(e)}`)}catch{continue}if(a.status===410&&(o.json?f("PAIR_EXPIRED","Pairing code expired"):(console.error(r.red(`
|
|
3
|
+
\u2717 Pairing code expired. Please re-run: shennian pair`)),process.exit(1))),!a.ok)continue;const t=await a.json();if(t.status==="claimed"&&t.machineToken&&t.machineId&&t.accountNamespaceId)return o.json&&l({type:"pair.claimed"}),{machineToken:t.machineToken,machineId:t.machineId,machineName:t.machineName??"",accountNamespaceId:t.accountNamespaceId}}return null}function G(n){const e=R.createInterface({input:process.stdin,output:process.stdout});return new Promise(o=>{e.question(n,i=>{e.close(),o(i.trim().toLowerCase()==="y")})})}function $(){const n=R.createInterface({input:process.stdin,output:process.stdout});return console.log(r.white("Choose how to pair this machine:")),console.log(r.cyan(" 1. Browser authorization (recommended)")),console.log(r.gray(" 2. QR code / pairing token (servers and remote terminals)")),new Promise(e=>{n.question("Selection (1/2) [1]: ",o=>{n.close(),e(Q(o))})})}async function _(n){const{serverUrl:e,machineName:o,force:i,json:a}=n,t=u();if(t.machineToken&&t.machineId&&!i){a?l({type:"done",status:"already-paired",machineId:t.machineId}):(console.log(r.green(`\u2713 Already paired (machine ID: ${t.machineId})`)),console.log(r.gray(' Run "shennian pair" to re-pair to another account')));return}const c=(await E()).map(h=>h.type),m=k.platform(),g=m==="win32"?"windows":m==="darwin"?"macos":"linux";a||(console.log(r.bold(`
|
|
4
4
|
Shennian CLI \u2014 Pairing
|
|
5
|
-
`)),console.log(r.gray(` Machine: ${
|
|
6
|
-
If the browser did not open, copy this URL to any signed-in device.`)),console.log(r.gray("Waiting for authorization... (Ctrl+C to cancel)")))}});v({...t,machineToken:s.machineToken,machineId:s.machineId,accountNamespaceId:s.accountNamespaceId,serverUrl:e}),a?l({type:"pair.saved",machineId:s.machineId}):console.log(r.green(`
|
|
7
|
-
\u2713 Paired successfully! Machine "${s.machineName}" is now linked.`));return}catch(s){const T=s instanceof
|
|
8
|
-
\u2717 ${P}`)),process.exit(1)}finally{process.removeListener("SIGINT",I)}}let y;try{y=await fetch(`${e}/api/machines/pair/create`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:
|
|
9
|
-
\u2717 Timed out. Pairing code expired, please try again.`)),process.exit(1)),v({...t,machineToken:p.machineToken,machineId:p.machineId,accountNamespaceId:p.accountNamespaceId,serverUrl:e}),
|
|
10
|
-
\u2713 Paired successfully! Machine "${p.machineName}" is now linked.`))}async function V(
|
|
11
|
-
Starting background service...`)),U()||b()}function he(
|
|
12
|
-
Starting background service...`)),U({quiet:!!e.json})||b({quiet:!!e.json}),e.json&&(
|
|
5
|
+
`)),console.log(r.gray(` Machine: ${o} | OS: ${g} | Agents: ${c.join(", ")||"none"}`)),console.log());let S=n.mode??O({json:a,stdinIsTty:!!process.stdin.isTTY,stdoutIsTty:!!process.stdout.isTTY});if(S==="prompt"&&(S=await $()),S==="browser"){const h=new AbortController,I=()=>h.abort();process.once("SIGINT",I);try{const s=await z({serverUrl:e,machineName:o,machineOs:g,agentList:c,signal:h.signal,openUrl:n.openUrl,emit:T=>{a?l(T):T.type==="pair.browser.created"&&(console.log(r.white("Approve this machine in your browser:")),console.log(r.cyan.underline(T.url)),console.log(r.gray(`
|
|
6
|
+
If the browser did not open, copy this URL to any signed-in device.`)),console.log(r.gray("Waiting for authorization... (Ctrl+C to cancel)")))}});v({...t,machineToken:s.machineToken,machineId:s.machineId,accountNamespaceId:s.accountNamespaceId,serverUrl:e}),n.force&&j(),a?l({type:"pair.saved",machineId:s.machineId}):console.log(r.green(`
|
|
7
|
+
\u2713 Paired successfully! Machine "${s.machineName}" is now linked.`));return}catch(s){const T=s instanceof q||typeof s=="object"&&s&&"code"in s&&typeof s.code=="string"?s.code:"PAIR_BROWSER_FAILED",P=s instanceof Error?s.message:"Browser pairing failed";a&&f(T,P),console.error(r.red(`
|
|
8
|
+
\u2717 ${P}`)),process.exit(1)}finally{process.removeListener("SIGINT",I)}}let y;try{y=await fetch(`${e}/api/machines/pair/create`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:o,os:g,agentList:c,daemonInstallationId:B()})})}catch{const h=`Network error: cannot connect to ${e}`;a&&f("NETWORK_ERROR",h),console.error(r.red(`\u2717 ${h}`)),process.exit(1)}if(!y.ok){const I=`Failed to create machine: ${(await y.json().catch(()=>({}))).error??y.statusText}`;a&&f("PAIR_CREATE_FAILED",I),console.error(r.red(`\u2717 ${I}`)),process.exit(1)}const w=await y.json(),D=F(w.pairToken);a?l({type:"pair.created",code:w.pairToken,machineId:w.machineId,expiresAt:new Date(Date.now()+C).toISOString()}):(console.log(r.white("Scan QR code or enter the pairing code in the Shennian app:")),console.log(),await Y(D),W(w.pairToken),console.log(),console.log(r.gray("Waiting for pairing... (Ctrl+C to cancel)")));const p=await H(e,w.pairToken,{json:a});p||(a&&f("PAIR_TIMEOUT","Timed out waiting for pairing"),console.error(r.red(`
|
|
9
|
+
\u2717 Timed out. Pairing code expired, please try again.`)),process.exit(1)),v({...t,machineToken:p.machineToken,machineId:p.machineId,accountNamespaceId:p.accountNamespaceId,serverUrl:e}),n.force&&j(),a&&l({type:"pair.saved",machineId:p.machineId}),a||console.log(r.green(`
|
|
10
|
+
\u2713 Paired successfully! Machine "${p.machineName}" is now linked.`))}async function V(n){const e=u(),i=(await E({serverUrl:n.serverUrl,authToken:n.authToken})).map(m=>m.type),a=k.platform(),t=a==="win32"?"windows":a==="darwin"?"macos":"linux",d=await fetch(`${n.serverUrl}/api/machines/desktop/register`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${n.authToken}`},body:JSON.stringify({machineId:e.machineId,name:n.machineName,os:t,agentList:i})});if(!d.ok){const g=(await d.json().catch(()=>({}))).error??d.statusText;throw new Error(`Failed to register desktop machine: ${g}`)}const c=await d.json();if(!c.machine?.id||!c.machineToken||!c.accountNamespaceId)throw new Error("Failed to register desktop machine: missing machine token");return e.machineId=c.machine.id,e.machineToken=c.machineToken,e.accountNamespaceId=c.accountNamespaceId,e.serverUrl=n.serverUrl,v(e),{machineId:c.machine.id,machineName:c.machine.name}}async function X(n){const e=n?.trim();if(e)return e;const o=process.env.SHENNIAN_DESKTOP_SERVER_URL?.trim();if(o)return o;const i=u().serverUrl?.trim();return i||N()}async function me(n,e){const o=u();if(!o.machineToken||!o.machineId){const a=n??await N();await _({serverUrl:a,machineName:e,force:!1})}else console.log(r.green(`\u2713 Already paired (machine ID: ${o.machineId})`));x(),A(),console.log(r.gray(`
|
|
11
|
+
Starting background service...`)),U()||b()}function he(n){n.command("desktop-register",{hidden:!0}).description("Register this machine to the authenticated account").option("--api <url>","Server URL").option("--name <name>","Machine name",k.hostname()).action(async e=>{try{const o=process.env.SHENNIAN_DESKTOP_AUTH_TOKEN;if(!o)throw new Error("Missing desktop auth token");const i=await X(e.api),a=await V({serverUrl:i,authToken:o,machineName:e.name});console.log(JSON.stringify({ok:!0,...a}))}catch(o){console.error(o instanceof Error?o.message:String(o)),process.exit(1)}}),n.command("pair").description("Pair this machine to a Shennian account (re-pair if already paired)").option("--api <url>","Server URL override").option("--server <url>","Server URL override").option("--name <name>","Machine name",k.hostname()).option("--json","Emit machine-readable JSONL events").option("--browser","Pair by approving a browser authorization request").option("--token","Pair with the existing QR code / token flow").option("--force","Force re-pair even when a machine token exists").action(async e=>{const o=u();let i;try{i=O({browser:e.browser,token:e.token,json:e.json,stdinIsTty:!!process.stdin.isTTY,stdoutIsTty:!!process.stdout.isTTY})}catch(c){const m=c instanceof Error?c.message:"Invalid pairing mode";e.json&&f("PAIR_MODE_CONFLICT",m),console.error(r.red(`\u2717 ${m}`)),process.exit(1)}if(i==="prompt"&&(i=await $()),o.machineToken&&o.machineId&&!e.force){if(e.json){l({type:"done",status:"already-paired",machineId:o.machineId});return}if(console.log(r.yellow(`Currently paired (machine ID: ${o.machineId})`)),!await G("Re-pairing will unlink the current account. Continue? (y/N) ")){console.log(r.gray("Cancelled"));return}}const a=e.server??e.api??await N(),t=!!(e.force||o.machineToken&&o.machineId);await _({serverUrl:a,machineName:e.name,force:t,json:!!e.json,mode:i}),x(),A(),e.json?l({type:"daemon.starting"}):console.log(r.gray(`
|
|
12
|
+
Starting background service...`)),U({quiet:!!e.json})||b({quiet:!!e.json}),e.json&&(M(()=>L({cleanupStale:!0})),l({type:"done",status:"paired",machineId:u().machineId}))})}export{V as registerDesktopMachine,he as registerPairCommand,_ as runPairFlow,me as runSmartStart};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import h from"node:crypto";import k from"node:fs";import{PublicAgentClientError as a}from"../agents/public-agent-client.js";import{ProjectClient as x}from"../agents/project-client.js";const j={write:i=>process.stdout.write(i),fail:()=>{process.exitCode=1}},_=/^[A-Za-z0-9_.:@-]{1,191}$/,w=/^[A-Za-z0-9_.:@-]{8,128}$/,m=2e4;function I(i,n={}){const t=n.client??new x,e=n.io??j,p=i.command("project").description("List Shennian projects and run tasks in them");p.command("list").description("List the paired user's projects with machine status and recent tasks").action(()=>c("project.list",e,()=>t.listProjects()));const d=p.command("task").description("List, create, read, message or stop tasks in an exact project");d.command("list <project-id>").description("List recent tasks (conversations) of one exact project").option("--limit <count>","Number of tasks (1-15)","3").action((r,o)=>c("project.task.list",e,()=>t.listTasks(s(r,"project_id_invalid"),u(o.limit,15)))),d.command("create <project-id>").description("Start a new task in one exact project; long or file-changing work belongs here").option("--instruction <text>","Task instruction").option("--instruction-file <path>","Read the instruction from a file").option("--agent <type>","Exact AI type of the project machine; defaults to the project's usual one").option("--model <id>","Exact model id; defaults to the project's usual one").option("--request-id <id>","Stable id; reuse it when retrying so no duplicate task is created").action((r,o)=>c("project.task.create",e,()=>t.createTask(s(r,"project_id_invalid"),{instruction:f(o.instruction,o.instructionFile,"instruction"),requestId:l(o.requestId),...o.agent?{agentType:s(o.agent,"agent_invalid")}:{},...o.model?{modelId:s(o.model,"agent_invalid")}:{}}))),d.command("read <task-id>").description("Read the latest visible messages of one exact task").option("--limit <count>","Number of messages (1-8)","4").action((r,o)=>c("project.task.read",e,()=>t.readTask(s(r,"task_id_invalid"),u(o.limit,8)))),d.command("send <task-id>").description("Send another message to one exact existing task").option("--text <text>","Message text").option("--text-file <path>","Read the message from a file").option("--request-id <id>","Stable id; reuse it when retrying so the message is sent once").action((r,o)=>c("project.task.send",e,()=>t.sendTask(s(r,"task_id_invalid"),{text:f(o.text,o.textFile,"text"),requestId:l(o.requestId)}))),d.command("stop <task-id>").description("Ask one exact task to stop its current run (not a durable pause)").action(r=>c("project.task.stop",e,()=>t.stopTask(s(r,"task_id_invalid"))))}async function c(i,n,t){try{n.write(`${JSON.stringify({ok:!0,command:i,result:await t()})}
|
|
2
|
+
`)}catch(e){e instanceof a?n.write(`${JSON.stringify({ok:!1,error:e.code,message:e.message,nextAction:e.nextAction})}
|
|
3
|
+
`):n.write(`${JSON.stringify({ok:!1,error:"project_command_failed",message:"Shennian project command failed.",nextAction:null})}
|
|
4
|
+
`),n.fail()}}function s(i,n){if(!_.test(i))throw new a(n,"Use the exact id returned by Shennian.");return i}function u(i,n){const t=Number(i);return Number.isFinite(t)?Math.min(Math.max(Math.floor(t),1),n):n}function l(i){if(i===void 0)return`steward-${h.randomUUID()}`;if(!w.test(i))throw new a("request_id_invalid","Use 8-128 letters, digits or _.:@- for --request-id.");return i}function f(i,n,t){if(i!==void 0&&n!==void 0)throw new a(`${t}_invalid`,`Use either inline text or a file for ${t}, not both.`);let e=i;if(n!==void 0)try{e=k.readFileSync(n,"utf8")}catch{throw new a(`${t}_invalid`,`The ${t} file could not be read.`)}if(e=e?.trim(),!e||e.length>m)throw new a(`${t}_invalid`,`Provide non-empty ${t} of at most ${m} characters.`);return e}export{I as registerProjectCommand};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import u from"node:fs";import h from"node:os";import v from"node:path";import c from"chalk";import{loadConfig as S,resolveShennianPath as E,saveConfig as $}from"../config/index.js";import{regionToUrl as x}from"../region.js";import{detectAgents as C}from"../agents/detect.js";import{getUnavailableAcpAgents as D}from"../agents/acp/discovery.js";import{findOfficialAdapter as T}from"../adapter-kit/official-adapters.js";import{installSkillForHosts as R}from"../skill/hosts.js";import{getCurrentVersion as _}from"../upgrade/engine.js";import{BrowserPairError as U,createBrowserPairIntent as j,openExternalUrl as L,waitForBrowserPairIntent as O}from"./pair-browser.js";import{ensureDaemonStarted as b}from"./daemon.js";const k="https://shennian.net/#download",M="pending-pair.json",F=6e4,B=600,V=25,H={clientVersion:_,detectAgents:()=>C(),unavailableAgents:D,installSkills:()=>R({clientVersion:_()}),createIntent:j,waitForIntent:O,openUrl:L,ensureDaemon:b,now:Date.now};async function W(e){const n={...H,...e.deps},t=e.emit,r=o=>{const l={type:"done",...o};return t(l),l};t({type:"step",step:"client",status:"ok",message:`\u795E\u5FF5 Client ${n.clientVersion()} \u5DF2\u5B89\u88C5`,version:n.clientVersion()});const i=await n.installSkills().catch(()=>[]);t(Z(i));const s=await n.detectAgents().catch(()=>[]),a=K(s),d=n.unavailableAgents();t(Q(a,d));const f=S();let y=!1;if(f.machineToken&&f.machineId)t({type:"step",step:"pair",status:"ok",message:"\u8FD9\u53F0\u7535\u8111\u5DF2\u8FDE\u63A5\u5230\u4F60\u7684\u795E\u5FF5\u8D26\u53F7"});else{const o=(e.server??f.serverUrl??x("cn")).replace(/\/+$/,"");let l;try{l=await G({serverUrl:o,machineName:e.name??h.hostname(),agentList:s.map(m=>m.type),waitSeconds:e.waitSeconds??(e.interactive?B:V),emit:t,deps:n})}catch(m){const p=m instanceof U?m.code:"PAIR_FAILED";return t({type:"step",step:"pair",status:"failed",message:P(p),error:p}),r({status:"failed",message:P(p),nextAction:p==="PAIR_DENIED"||p==="PAIR_TIMEOUT"||p==="PAIR_EXPIRED"?"\u518D\u6B21\u8FD0\u884C shennian setup \u4F1A\u751F\u6210\u65B0\u7684\u786E\u8BA4\u94FE\u63A5\u3002":"\u68C0\u67E5\u7F51\u7EDC\u540E\u518D\u6B21\u8FD0\u884C shennian setup\uFF1B\u4ECD\u5931\u8D25\u8BF7\u8FD0\u884C shennian support ask \u8054\u7CFB\u795E\u5FF5\u5BA2\u670D\u3002"})}if(!l.result)return r({status:"waiting_for_approval",pairUrl:l.url,message:`\u8BF7\u6253\u5F00\u8FD9\u4E2A\u94FE\u63A5\uFF0C\u7528\u624B\u673A\u53F7\u548C\u9A8C\u8BC1\u7801\u786E\u8BA4\u8FD9\u53F0\u7535\u8111\uFF1A${l.url}`,nextAction:"\u628A\u94FE\u63A5\u53D1\u7ED9\u7528\u6237\uFF0C\u7B49\u7528\u6237\u8BF4\u5DF2\u786E\u8BA4\u540E\u518D\u6B21\u8FD0\u884C shennian setup\uFF1B\u94FE\u63A5\u6709\u6548\u671F\u5185\u4F1A\u7EE7\u7EED\u540C\u4E00\u4E2A\u8BF7\u6C42\u3002"});$({...S(),machineToken:l.result.machineToken,machineId:l.result.machineId,accountNamespaceId:l.result.accountNamespaceId,serverUrl:o}),y=!0,t({type:"step",step:"pair",status:"ok",message:"\u5DF2\u8FDE\u63A5\u5230\u4F60\u7684\u795E\u5FF5\u8D26\u53F7"})}let g;try{g=n.ensureDaemon({resumePaused:y}).status}catch(o){const l=o instanceof Error?o.message:String(o);return t({type:"step",step:"service",status:"failed",message:"\u540E\u53F0\u670D\u52A1\u6CA1\u6709\u542F\u52A8",error:l}),r({status:"failed",message:"\u540E\u53F0\u670D\u52A1\u6CA1\u6709\u542F\u52A8\uFF0C\u624B\u673A\u6682\u65F6\u8FDE\u4E0D\u4E0A\u8FD9\u53F0\u7535\u8111\u3002",nextAction:"\u8FD0\u884C shennian logs -n 100 \u67E5\u770B\u539F\u56E0\uFF1B\u4ECD\u65E0\u6CD5\u89E3\u51B3\u8BF7\u8FD0\u884C shennian support ask \u8054\u7CFB\u795E\u5FF5\u5BA2\u670D\u3002"})}t(g==="paused"?{type:"step",step:"service",status:"attention",message:"\u540E\u53F0\u670D\u52A1\u5DF2\u88AB\u6682\u505C\uFF08\u4F60\u4E4B\u524D\u4E3B\u52A8\u6682\u505C\u8FC7\uFF09"}:{type:"step",step:"service",status:"ok",message:"\u540E\u53F0\u670D\u52A1\u6B63\u5728\u8FD0\u884C\uFF0C\u5F00\u673A\u540E\u4F1A\u81EA\u52A8\u542F\u52A8"});const I=a.filter(o=>o.state==="ready"),w=d.length?` \u53E6\u5916\uFF0C${d.map(o=>o.name).join("\u3001")} \u5DF2\u5B89\u88C5\u4F46\u8FD8\u6CA1\u63A5\u4E0A\uFF1A\u8BE2\u95EE\u7528\u6237\u662F\u5426\u8981\u63A5\u5165\uFF0C\u540C\u610F\u540E\u6309 Shennian Skill \u7684\u201C\u63A5\u5165 AI\u201D\u6D41\u7A0B\u5904\u7406\u3002`:"";return g==="paused"?r({status:"needs_attention",message:"\u5B89\u88C5\u5DF2\u5B8C\u6210\uFF0C\u4F46\u540E\u53F0\u670D\u52A1\u5904\u4E8E\u6682\u505C\u72B6\u6001\u3002",nextAction:"\u8BE2\u95EE\u7528\u6237\u662F\u5426\u6062\u590D\uFF1B\u7528\u6237\u540C\u610F\u540E\u8FD0\u884C shennian start\u3002"}):I.length===0?r({status:"needs_attention",message:a.length===0?"\u795E\u5FF5\u5DF2\u88C5\u597D\uFF0C\u4F46\u8FD9\u53F0\u7535\u8111\u4E0A\u8FD8\u6CA1\u6709\u627E\u5230\u53EF\u7528\u7684 AI \u5DE5\u5177\u3002":"\u795E\u5FF5\u5DF2\u88C5\u597D\uFF0C\u627E\u5230\u7684 AI \u5DE5\u5177\u8FD8\u9700\u8981\u5148\u767B\u5F55\u3002",nextAction:(a.length===0?"\u8BE2\u95EE\u7528\u6237\u60F3\u7528\u54EA\u4E2A AI\uFF08\u4F8B\u5982 Codex\u3001Claude Code\u3001Qwen Code\u3001Kimi CLI\u3001WorkBuddy\uFF09\uFF0C\u6309\u5B83\u7684\u5B98\u65B9\u65B9\u5F0F\u5B89\u88C5\u5E76\u767B\u5F55\u540E\u518D\u6B21\u8FD0\u884C shennian setup\uFF1B\u82E5\u7528\u6237\u5DF2\u88C5\u4E86\u672A\u5217\u51FA\u7684 AI\uFF0C\u6309 Shennian Skill \u7684\u201C\u63A5\u5165 AI\u201D\u6D41\u7A0B\u9002\u914D\u3002":`\u8BF7\u7528\u6237\u6309\u5404\u81EA\u5B98\u65B9\u65B9\u5F0F\u767B\u5F55\uFF1A${a.map(o=>o.name).join("\u3001")}\uFF0C\u7136\u540E\u518D\u6B21\u8FD0\u884C shennian setup\u3002`)+w}):r({status:"ready",message:`\u795E\u5FF5\u5DF2\u5C31\u7EEA\uFF0C\u53EF\u4EE5\u4F7F\u7528\uFF1A${I.map(o=>o.name).join("\u3001")}\u3002`,nextAction:`\u544A\u8BC9\u7528\u6237\uFF1A\u5728\u624B\u673A\u4E0A\u4E0B\u8F7D\u795E\u5FF5 App\uFF08${k}\uFF09\u7528\u540C\u4E00\u4E2A\u624B\u673A\u53F7\u767B\u5F55\uFF0C\u5C31\u80FD\u770B\u5230\u8FD9\u53F0\u7535\u8111\u5E76\u548C\u8FD9\u4E9B AI \u5BF9\u8BDD\uFF1B\u4E5F\u53EF\u4EE5\u76F4\u63A5\u5728\u7535\u8111\u4E0A\u7684 AI \u91CC\u8BF4\u201C\u7528\u795E\u5FF5\u2026\u2026\u201D\u3002\u5982\u679C\u7528\u6237\u8FD8\u6709\u5176\u4ED6\u88C5\u4E86\u4F46\u6CA1\u5217\u51FA\u6765\u7684 AI\uFF0C\u8BE2\u95EE\u662F\u5426\u8981\u63A5\u5165\u3002${w}`})}async function G(e){const{deps:n}=e;let t=J(e.serverUrl,n.now());const r=t!==null;t||(t=await n.createIntent({serverUrl:e.serverUrl,machineName:e.machineName,machineOs:Y(),agentList:e.agentList}),q(t)),e.emit({type:"step",step:"pair",status:"waiting",message:`\u8BF7\u7528\u624B\u673A\u6216\u7535\u8111\u6D4F\u89C8\u5668\u6253\u5F00\u94FE\u63A5\uFF0C\u8F93\u5165\u624B\u673A\u53F7\u548C\u9A8C\u8BC1\u7801\u786E\u8BA4\u8FD9\u53F0\u7535\u8111\uFF1A${t.url}`,url:t.url,expiresAt:t.expiresAt,resumed:r}),r||await n.openUrl(t.url).catch(()=>!1);try{const i=await n.waitForIntent(t,{deadline:n.now()+Math.max(0,e.waitSeconds)*1e3});return i&&N(),{url:t.url,result:i}}catch(i){throw N(),i}}function A(){return E(M)}function J(e,n){try{const t=JSON.parse(u.readFileSync(A(),"utf8"));return t.serverUrl!==e||typeof t.intentId!="string"||typeof t.url!="string"||typeof t.expiresAt!="string"||typeof t.deviceSecret!="string"||typeof t.actionPayloadHash!="string"||typeof t.machineName!="string"||new Date(t.expiresAt).getTime()-n<F?null:t}catch{return null}}function q(e){const n=A();u.mkdirSync(v.dirname(n),{recursive:!0,mode:448});const t=`${n}.${process.pid}.tmp`;u.writeFileSync(t,JSON.stringify(e),{mode:384}),u.renameSync(t,n)}function N(){try{u.unlinkSync(A())}catch{}}function Y(){const e=h.platform();return e==="win32"?"windows":e==="darwin"?"macos":"linux"}function K(e){return e.map(n=>({type:n.type,name:X(n),state:n.authState==="required"?"login_required":"ready"}))}function X(e){return e.display?.name?e.display.name:e.type.startsWith("custom:")?e.type.slice(7):e.type==="gemini"?"Gemini CLI":T(e.type)?.name??e.type}const z={timeout:"\u542F\u52A8\u8D85\u65F6",launch:"\u65E0\u6CD5\u542F\u52A8",version:"\u534F\u8BAE\u7248\u672C\u4E0D\u517C\u5BB9",invalid_protocol:"\u6CA1\u6709\u6309 ACP \u534F\u8BAE\u54CD\u5E94",output_limit:"\u8F93\u51FA\u5F02\u5E38"};function Q(e,n){const t=e.filter(a=>a.state==="ready").map(a=>a.name),r=e.filter(a=>a.state==="login_required").map(a=>a.name),i=n.map(a=>`${a.name}\uFF08${z[a.reason]??"\u8FDE\u63A5\u5931\u8D25"}\uFF09`),s=[t.length?`\u53EF\u4EE5\u4F7F\u7528\uFF1A${t.join("\u3001")}`:"",r.length?`\u9700\u8981\u5148\u767B\u5F55\uFF1A${r.join("\u3001")}`:"",i.length?`\u5DF2\u5B89\u88C5\u4F46\u8FD8\u6CA1\u63A5\u4E0A\uFF1A${i.join("\u3001")}`:""].filter(Boolean);return{type:"step",step:"agents",status:t.length&&!i.length?"ok":"attention",message:s.length?`\u627E\u5230\u672C\u673A AI\u3002${s.join("\uFF1B")}`:"\u6CA1\u6709\u627E\u5230\u53EF\u7528\u7684 AI \u5DE5\u5177",agents:e,unavailable:n.map(a=>({type:a.type,name:a.name,command:[a.command,...a.args].join(" "),reason:a.reason}))}}function Z(e){const n=e.filter(s=>s.ok&&["installed","updated","current","registered"].includes(s.result.status)),t=e.filter(s=>!n.includes(s)),r=e.map(s=>({id:s.id,names:s.names,path:s.path,status:s.ok?s.result.status:"failed",...s.ok?{}:{error:s.error}}));if(e.length===0)return{type:"step",step:"skill",status:"attention",hosts:r,message:"\u6CA1\u6709\u627E\u5230\u9700\u8981\u5B89\u88C5\u795E\u5FF5 Skill \u7684 AI \u5DE5\u5177\uFF1B\u88C5\u597D AI \u540E\u518D\u6B21\u8FD0\u884C shennian setup"};const i=n.flatMap(s=>s.names);return{type:"step",step:"skill",status:t.length?"attention":"ok",hosts:r,message:[i.length?`\u5DF2\u4E3A ${i.join("\u3001")} \u88C5\u597D\u795E\u5FF5 Skill\uFF08\u6B63\u5728\u8FD0\u884C\u7684 AI \u9700\u8981\u65B0\u5F00\u4E00\u4E2A\u5BF9\u8BDD\u624D\u4F1A\u751F\u6548\uFF09`:"",t.length?`${t.flatMap(s=>s.names).join("\u3001")} \u7684 Skill \u672A\u66F4\u65B0\uFF08\u672C\u5730\u6587\u4EF6\u88AB\u6539\u8FC7\u6216\u4E0B\u8F7D\u5931\u8D25\uFF09\uFF0C\u4E0D\u5F71\u54CD\u4F7F\u7528`:""].filter(Boolean).join("\uFF1B")}}function P(e){switch(e){case"PAIR_DENIED":return"\u786E\u8BA4\u8BF7\u6C42\u88AB\u62D2\u7EDD\u4E86\u3002";case"PAIR_TIMEOUT":case"PAIR_EXPIRED":return"\u786E\u8BA4\u94FE\u63A5\u5DF2\u8FC7\u671F\u3002";case"PAIR_ALREADY_CLAIMED":return"\u8FD9\u4E2A\u786E\u8BA4\u8BF7\u6C42\u5DF2\u7ECF\u88AB\u4F7F\u7528\u8FC7\u3002";default:return"\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5\u795E\u5FF5\u670D\u52A1\u5668\u3002"}}const ee={ok:c.green("\u2713"),waiting:c.cyan("\u2026"),attention:c.yellow("!"),failed:c.red("\u2717")};function te(e){if(e.type==="step"){console.log(`${ee[e.status]} ${e.message}`),e.step==="pair"&&e.status==="waiting"&&console.log(c.gray(" \u7B49\u5F85\u786E\u8BA4\u2026\uFF08Ctrl+C \u53EF\u968F\u65F6\u9000\u51FA\uFF0C\u7A0D\u540E\u518D\u8FD0\u884C shennian setup \u4F1A\u7EE7\u7EED\uFF09"));return}console.log();const n=e.status==="ready"?c.green:e.status==="failed"?c.red:c.yellow;console.log(n(e.message)),e.status==="ready"?(console.log(` \u5728\u624B\u673A\u4E0A\u4E0B\u8F7D\u795E\u5FF5 App\uFF1A${k}\uFF0C\u7528\u540C\u4E00\u4E2A\u624B\u673A\u53F7\u767B\u5F55\u5373\u53EF\u3002`),console.log(" \u4E5F\u53EF\u4EE5\u76F4\u63A5\u5728\u7535\u8111\u4E0A\u7684 AI \u91CC\u8BF4\uFF1A\u201C\u7528\u795E\u5FF5\u2026\u2026\u201D")):e.status==="waiting_for_approval"?console.log(" \u786E\u8BA4\u540E\u518D\u6B21\u8FD0\u884C\uFF1Ashennian setup"):console.log(` ${e.nextAction}`)}function ge(e){e.command("setup").description("Install and connect Shennian in one step: Skill, AI discovery, pairing link and background service. Safe to run again.").option("--json",'Emit JSONL events; the last line is {"type":"done",...}').option("--wait <seconds>","How long to wait for pairing approval before returning waiting_for_approval").option("--server <url>","Server URL override").option("--name <name>","Machine name",h.hostname()).action(async n=>{const t=!!n.json,r=n.wait===void 0?void 0:Number(n.wait);if(r!==void 0&&(!Number.isFinite(r)||r<0)){console.error("--wait must be a non-negative number of seconds"),process.exitCode=1;return}t||console.log(c.bold(`
|
|
2
|
+
\u795E\u5FF5\u5B89\u88C5
|
|
3
|
+
`));const i=await W({server:n.server,name:n.name,waitSeconds:r,interactive:!t&&!!(process.stdin.isTTY&&process.stdout.isTTY),emit:t?s=>process.stdout.write(`${JSON.stringify(s)}
|
|
4
|
+
`):te});process.exitCode=i.status==="failed"?1:i.status==="waiting_for_approval"?2:0})}export{k as APP_DOWNLOAD_URL,J as readPendingPair,ge as registerSetupCommand,W as runSetup,K as summarizeAgents};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import c from"node:fs";import{callDaemonIpc as u}from"../daemon-ipc/client.js";import{DaemonIpcError as r}from"../daemon-ipc/protocol.js";import{STEWARD_SAY_MAX_CHARS as m,STEWARD_SESSION_ID_ENV as h}from"../steward/session.js";const y={write:a=>process.stdout.write(a),fail:()=>{process.exitCode=1}};function x(a,e={}){const o=e.io??y,d=e.callDaemon??u,t=e.env??process.env,l=e.readStdin??(()=>c.readFileSync(0,"utf8"));a.command("steward").description("Talk to the user as the Shennian steward").command("say [text]").description("Send a message the user will see on their phone; in a steward session everything else you output stays hidden from them").option("--file <path>","Read the message from a file").option("--stdin","Read the message from standard input").action(async(f,w)=>{try{const n=t[h]?.trim(),s=p(f,w,l),i=await d({method:"steward.say",params:n?{sessionId:n,text:s}:{text:s}});o.write(`${JSON.stringify({ok:!0,command:"steward.say",result:i})}
|
|
2
|
+
`)}catch(n){const s=n instanceof r,i=!s&&n?.code==="ENOENT";o.write(`${JSON.stringify({ok:!1,error:s?n.code:i?"daemon_unavailable":"steward_command_failed",message:s?n.message:i?"Shennian is not running on this computer.":"Shennian steward command failed.",nextAction:null})}
|
|
3
|
+
`),o.fail()}})}function p(a,e,o){if([a!==void 0,e.file!==void 0,e.stdin===!0].filter(Boolean).length!==1)throw new r("steward_say_invalid","Provide exactly one of: text, --file, --stdin.");let t;if(e.file!==void 0)try{t=c.readFileSync(e.file,"utf8")}catch{throw new r("steward_say_invalid","The message file could not be read.")}else if(e.stdin)try{t=o()}catch{throw new r("steward_say_invalid","Standard input could not be read.")}else t=a??"";if(t=t.trim(),!t||t.length>m)throw new r("steward_say_invalid",`Provide a non-empty message of at most ${m} characters.`);return t}export{x as registerStewardCommand};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isAvailableAgentType as
|
|
1
|
+
import{isAvailableAgentType as f}from"@shennian/wire";import{DaemonIpcError as s}from"./protocol.js";function w(n){let l=!1;const d=()=>{const e=n.getStatus();if(e.remoteAccessDisabled)throw new s("daemon_paused","Shennian is paused; resume it only when requested.");if(!e.running)throw new s("daemon_unavailable","Shennian is not running.")};return{"agent.discover":async e=>{d();const o=e&&typeof e=="object"&&!Array.isArray(e)&&typeof e.agentType=="string"?e.agentType:null,u=!!(e&&typeof e=="object"&&!Array.isArray(e)&&e.compact===!0),p=!!(e&&typeof e=="object"&&!Array.isArray(e)&&e.refresh===!0),a=(p?await n.discover(o??void 0):n.getSnapshot()).filter(t=>!o||t.type===o),c=(n.getUnavailable?.()??[]).filter(t=>!o||t.type===o);return{modelTested:!1,compact:u,refreshed:p,unavailable:c,agents:a.map(t=>{const i=t.models.find(r=>r.isDefault),y=(i?[i,...t.models.filter(r=>r.id!==i.id)]:t.models).map(r=>({id:r.id,name:r.name,isDefault:r.isDefault===!0}));return{type:t.type,name:t.display?.name??t.type,authState:t.authState??"unknown",...t.runtime?.cliVersion?{version:t.runtime.cliVersion}:{},...t.runtime?.compatibility?{compatibility:t.runtime.compatibility}:{},...t.runtimePlatform?{runtimePlatform:t.runtimePlatform}:{},...t.runtimeIsWsl!==void 0?{runtimeIsWsl:t.runtimeIsWsl}:{},supportsStandardSession:t.supportsStandardSession===!0,...t.supportsAgent!==void 0?{supportsAgent:t.supportsAgent}:{},...t.supportedAgentExecutionModes?{supportedAgentExecutionModes:t.supportedAgentExecutionModes}:{},supportsManagedAgent:t.supportsManagedAgent===!0,supportsManagedNativeFull:t.supportsManagedNativeFull===!0,supportsRoomTools:t.supportsRoomTools===!0,supportsWorker:t.supportsWorker===!0,supportsManagerMode:t.supportsManagerMode===!0,supportsExternalWorkerTarget:t.supportsExternalWorkerTarget===!0,modelCount:t.models.length,defaultModelId:i?.id??null,...u?{}:{models:y,modelsTruncated:!1}}})}},"agent.check":async e=>{if(d(),!e||typeof e!="object"||Array.isArray(e)||typeof e.agentType!="string"||!f(e.agentType)||typeof e.workDir!="string"||!e.workDir.trim()||e.modelId!=null&&typeof e.modelId!="string")throw new s("agent_validation_invalid_configuration","Select an AI and its absolute working directory.");if(l)throw new s("agent_check_in_progress","An AI reply check is already running.");l=!0;try{const o=await n.validate({runtimeAdapter:e.agentType,workDir:e.workDir,modelId:e.modelId??null,sessionMode:"standard"});if(!o.ok)throw new s(o.errorCode,o.errorCode);return{ready:!0,agentType:e.agentType,modelId:e.modelId??null,scope:"hello_reply"}}finally{l=!1}}}}export{w as createAgentReadinessHandlers};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import s from"node:fs";import{getAcpLocalMetrics as
|
|
1
|
+
import s from"node:fs";import{getAcpLocalMetrics as u}from"../agents/acp/metrics.js";const g=["protocol.info","daemon.status","daemon.shutdown","agent.list","agent.discover","agent.check","room.agent.bind-managed","steward.say","agent.auth.completed","log.read","upgrade.start","upgrade.prepare-external","upgrade.cancel-external"],y=32*1024,m=64*1024;function _(r){return{"protocol.info":()=>{const e=r.getStatus();return{daemonVersion:r.daemonVersion,protocolVersion:r.protocolVersion,launcher:e.launcher,capabilities:[...g]}},"daemon.status":()=>r.getStatus(),"daemon.shutdown":(e,t)=>{const a=e&&typeof e=="object"&&!Array.isArray(e)?e.expectedPid:void 0;if(!Number.isSafeInteger(a)||a!==process.pid)throw new Error("daemon_instance_mismatch");if(!r.requestShutdown||!t.afterResponse)throw new Error("daemon_shutdown_unavailable");return t.afterResponse(r.requestShutdown),{accepted:!0,pid:process.pid}},"agent.list":()=>({agents:r.getAgents().map(e=>({type:e.type,...e.version?{version:e.version}:{},...e.runtimePlatform?{runtimePlatform:e.runtimePlatform}:{},...e.runtimeIsWsl!==void 0?{runtimeIsWsl:e.runtimeIsWsl}:{}})),acpMetrics:u()}),"agent.auth.completed":async e=>{const t=e&&typeof e=="object"&&!Array.isArray(e)&&typeof e.agentType=="string"?e.agentType:"";if(!/^acp:[a-z0-9][a-z0-9._-]{0,63}$/.test(t))throw new Error("invalid_acp_agent");return{resumed:await r.completeAgentAuthentication?.(t)??!1}},"room.agent.bind-managed":async e=>{if(!e||typeof e!="object"||Array.isArray(e))throw new Error("invalid_managed_room_binding");if(!r.bindManagedRoom)throw new Error("managed_room_binding_unavailable");return r.bindManagedRoom(e)},"steward.say":e=>{if(!e||typeof e!="object"||Array.isArray(e))throw new Error("invalid_steward_say");if(!r.stewardSay)throw new Error("steward_unavailable");return r.stewardSay(e)},"log.read":e=>w(r.getStatus().logFile,e),"upgrade.start":async e=>{if(!r.startUpgrade)throw new Error("upgrade_unavailable");return r.startUpgrade(e)},"upgrade.prepare-external":async e=>{if(!r.prepareExternalUpgrade)throw new Error("external_upgrade_unavailable");return r.prepareExternalUpgrade(e)},"upgrade.cancel-external":async()=>{if(!r.cancelExternalUpgrade)throw new Error("external_upgrade_unavailable");return r.cancelExternalUpgrade()}}}function w(r,e){const t=e&&typeof e=="object"&&!Array.isArray(e)?e:{},a=typeof t.limitBytes=="number"&&Number.isFinite(t.limitBytes)?t.limitBytes:y,l=Math.max(1,Math.min(Math.floor(a),m));let i=0;try{i=s.statSync(r).size}catch{return{text:"",start:0,end:0,nextBefore:null,eof:!0}}const f=typeof t.before=="number"&&Number.isFinite(t.before)?Math.floor(t.before):i,o=Math.max(0,Math.min(f,i)),n=Math.max(0,o-l);if(o===n)return{text:"",start:n,end:o,nextBefore:n>0?n:null,eof:n===0};const d=Buffer.allocUnsafe(o-n),c=s.openSync(r,"r");try{s.readSync(c,d,0,d.length,n)}finally{s.closeSync(c)}return{text:d.toString("utf8"),start:n,end:o,nextBefore:n>0?n:null,eof:n===0}}export{g as DAEMON_CONTROL_CAPABILITIES,_ as createDaemonControlHandlers,w as readDaemonLogPage};
|
package/dist/src/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{registerScheduleCommand as
|
|
2
|
-
`),process.exitCode=1}function rn(a){const c=[`trigger=${a.trigger??"unknown"}`,`phase=${a.phase}`,`code=${a.code??"unknown"}`,`attempt=${a.reconnectAttempt}`];return a.reason&&c.push(`reason=${a.reason}`),a.error&&c.push(`error=${a.error}`),c.join(" ")}const i=new Pe;i.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(D),i.enablePositionalOptions(),to(i),ro(i),ao(i),ke(i),i.option("--api <url>","Server URL override").option("--name <name>","Machine name",Ee.hostname()).action(async a=>{const c=p(),g=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??c.serverUrl??void 0;await Le(g,a.name)}),i.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async a=>{const c=J("env.json");try{const e=JSON.parse(z.readFileSync(c,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);ue()}catch{}xe(ae(process.env,p()));const g=bo();ce()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(g),process.exit(0));const d=je(),C=So({lockPath:on,instanceId:d});C.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(C.ownerPid?` (PID ${C.ownerPid})`:"")+", skipping duplicate start"),clearInterval(g),process.exit(0));try{const e=nn();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),C.release(),process.exit(0)}catch{}}catch{}const Y=We(process.env,ye)?[]:He(process.pid);Y.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${Y[0]}), skipping duplicate start`),C.release(),process.exit(0)),le(process.pid,d,{version:D}),me(process.pid,void 0,d),process.on("exit",()=>{ie(process.pid,d),se(d)}),process.on("exit",C.release);let j=null;try{j=fo()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const M=await po();if(M){j||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{ho({fromVersion:D,targetVersion:M.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:M.jobId},{readiness:j}),console.error(`[upgrade] New version crashed ${M.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{Yo()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const m=p(),u=a.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??m.serverUrl??K.cn.url,h=pe(),l=await ge({serverUrl:u,authToken:m.machineToken??m.accessToken}),S=l.map(e=>e.type);let H=V(l);const f=new Mo,_=new xo,k=new qo(f),y=new _o(u),Ce=new Wo({actionClient:y}),Re=new Go({actionClient:y});let w=null,r;const Z=zo({authority:k,actionClient:y,attachmentCache:Ce,attachmentUploader:Re,observations:_,getMachineCredentials:()=>({machineToken:p().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),onSendOutcome:e=>r.noteManagedRoomSendOutcome(e),getActiveRunId:(e,n)=>r.getManagedRoomActiveRunId(e,n),scheduleExecutor:(e,n,o)=>re({serverUrl:u,machineToken:p().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{const t=f.getByRegistryKey(e)?.binding;t&&f.setBinding({registryKey:e,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:"paused",activationMode:"paused"}),k.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let B=null,F=null,W=null,G=null,X=!1;const b=await fe({paths:Ao(),handlers:{...Eo({daemonVersion:h,protocolVersion:Uo,getStatus:()=>de(),getAgents:()=>l,requestShutdown:()=>{G?G():X=!0},completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),bindManagedRoom:e=>r.bindManagedRoomFromLocal(e),startUpgrade:async e=>{if(!B)throw new Error("upgrade_unavailable");return B(e)},prepareExternalUpgrade:e=>{if(!F)throw new Error("external_upgrade_unavailable");return F(e)},cancelExternalUpgrade:()=>{if(!W)throw new Error("external_upgrade_unavailable");return W()}}),...To({getStatus:()=>de(),getSnapshot:()=>V(l).map(e=>{const n=l.find(o=>o.type===e.type);return{...e,runtimePlatform:n?.runtimePlatform,runtimeIsWsl:n?.runtimeIsWsl}}),discover:async e=>{if(!r)throw new Error("daemon_starting");const n=await he(!0,e);if(e){const t=l.filter(I=>I.type!==e);l.splice(0,l.length,...t,...n.detectedAgents)}else l.splice(0,l.length,...n.detectedAgents);const o=V(l);return s.sendEvent({type:"event",event:"machine.agents",payload:{agentList:l.map(t=>t.type),agents:o}}),n.agents.map(t=>{const I=n.detectedAgents.find(q=>q.type===t.type);return{...t,runtimePlatform:I?.runtimePlatform,runtimeIsWsl:I?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return Do(e,{ignoreNativeSession:(n,o)=>P?.ignoreNativeSource(n,o)})}})}});let A;try{A=await fe({paths:Po(),handlers:{"managed-room.execute":e=>Z(e),"schedule.personal":async e=>{const n=e,o=be(n?.context);if(f.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await re({serverUrl:u,machineToken:p().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await b.close(),e}let ee=!1;const N=()=>{ee||(ee=!0,Promise.allSettled([b.close(),A.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",N),process.once("SIGTERM",N),!m.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([b.close(),A.close()]),process.exitCode=1;return}process.removeListener("SIGINT",N),process.removeListener("SIGTERM",N);const oe=`${tn(u)}/relay/machine`,x=m.machineToken;if(!x)return;try{await Qo({serverUrl:u,machineToken:x,machineId:m.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([b.close(),A.close()]),process.exit(1)}m.machineId&&wo(u,m.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${oe}... (v${h}) agents: ${S.join(",")}`),U({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:S}});let P=null,E=null,R=0,v=go();const ne=_e(m.autoUpgrade),O=m.machineId?new Io({serverUrl:u,machineId:m.machineId,getMachineToken:()=>p().machineToken,getDetectedAgents:()=>ge({serverUrl:u,authToken:p().machineToken}),onRuntimeChanged:async()=>{const e=R,n=await he(!0);if(l.splice(0,l.length,...n.detectedAgents),S.splice(0,S.length,...n.agentList),H=n.agents,!(await s.sendReq({type:"req",id:Te(),method:"machine.agents.report",params:{agents:n.agents}},2e4)).ok)throw new Error("machine_capability_report_rejected");e===R&&s.getState()==="connected"&&O?.start()},onError:()=>console.warn("[public-agent] delivery attempt failed")}):null,s=new Ne({serverUrl:oe,machineToken:x,cliVersion:h,agentList:S,autoUpgradePolicy:ne,sessionSyncProtocolVersion:ve,sessionSyncCapabilities:Ie,daemonInstallationId:Jo(),dataEpoch:Se,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),U({level:"info",wsEvent:"daemon.connected"}),s.sendEvent({type:"event",event:"machine.agents",payload:{agentList:S,agents:H}}),yo(l,{serverUrl:m.serverUrl??u,authToken:m.machineToken??m.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(H)&&s.sendEvent({type:"event",event:"machine.agents",payload:{agentList:S,agents:o}})}).catch(()=>{}),v&&s.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:v.from,to:v.to,...v.jobId?{jobId:v.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),$.start(),E?.start();const e=++R,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),v){const o=v;v=null,o.jobId&&vo(o.jobId,o.to),mo(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),Co({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),s.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&s.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===R&&s.getState()==="connected"&&(P?.handleConnected(),O?.start())}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===R&&s.getState()==="connected"){if(o instanceof Xo)return;const t=o instanceof Zo?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{R+=1,r?.revokeProjectFsHandles(),O?.stop(),E?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${rn(e)}`),U({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onProjectFsCapabilityChanged:e=>{e||r?.revokeProjectFsHandles()},onReq:e=>{if(console.log(`[${new Date().toISOString()}] [req] ${e.method}`),U({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),e.method==="room.message.deliver"){$o(s,E,e);return}r.handleReq(e)}});P=Ro()?new ko(s):null,E=new Oo({registry:f,observations:_,actionClient:y,getMachineCredentials:()=>({machineToken:p().machineToken}),isSessionDeleted:e=>new Lo().hasSession(e),onMessages:async(e,n,o,t)=>{await w?.processMessages(e,n,o,t)},onBindingPaused:e=>{e.lifecycleState==="closed"?(k.revokeSession(e.registryKey),r?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")})):r?.discardPendingManagedRoomMessages(e.nianSessionId)}});const $=new uo({currentVersion:h,machineId:m.machineId??"unknown-machine",serverUrl:m.serverUrl??u,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:ve,dataEpoch:Se,advertisedCapabilities:Ie,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(s,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:t=>r.prepareForUpgrade(t),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>r.getUpgradeIdleState().idle})}});$.setPolicy(ne),r=new io(s,P,h,$,new No({registry:f,observations:_,bindingClient:y,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${d}`}),{prepare:()=>Be(),isDisabled:()=>ce(),shutdownAfterResponse:()=>{setTimeout(()=>{Fe(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>k.revokeToken(e),Z,null,so(x),()=>V(l)),B=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,t=n.confirmedMajor===!0;return await Ye(s,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:t,prepareForUpgrade:q=>r.prepareForUpgrade(q),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},F=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},t=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:t,...r.prepareForUpgrade(t)}},W=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),w=new Ho({registry:f,observations:_,enqueueConversationBatch:e=>r.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=p().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const t=await y.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!jo(f,e)||!Vo(e,t))throw new Error("managed_room_binding_unavailable");return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},effectiveModelsBySessionId:t.effectiveModelId!==void 0?{[e.nianSessionId]:t.effectiveModelId}:void 0,agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o,t)=>{const I=p().machineToken;if(!I)throw new Error("machine_not_authorized");return k.issue({registryKey:e,machineCredential:I,activationCauseMessageId:n,allowedActions:o,propagateWorkerRoomOrigin:t})},audit:new Fo,resolveManagedAgentPolicy:e=>Bo({...e,runtime:l.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>Ae({serverUrl:u,machineToken:p().machineToken,req:e,client:s,queue:r.getScheduleQueue(),registry:f}),r.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(f.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await y.status({machineToken:p().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},r.setManagedRoomTurnPreparer(e=>w.prepareConversationTurn(e)),r.setManagedAgentDirectTurnPreparer((e,n)=>w.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?w.prepareDirectConversationTurn(e.sessionId,Ko):e.source==="manager_event"?w.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:w.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),z.mkdirSync(ye,{recursive:!0}),le(process.pid,d,{version:h}),me(process.pid,void 0,d),s.connect(),process.stdin.resume();let te=!1;const L=async()=>{if(!te){te=!0,console.log(T.gray(`
|
|
3
|
-
Disconnecting...`)),
|
|
1
|
+
import{registerScheduleCommand as be}from"./commands/schedule.js";import{resolvePersonalScheduleContext as ke}from"./schedules/personal-context.js";import{executeServerSchedule as re,deliverServerSchedule as Ae}from"./schedules/server-client.js";import{Command as Pe}from"commander";import T from"chalk";import Ee from"node:os";import{randomUUID as Te}from"node:crypto";import z from"node:fs";import{loadConfig as p,saveConfig as Ue,getConfigPath as De,getShennianDir as Me,resolveShennianPath as J,normalizeAutoUpgradePolicy as _e}from"./config/index.js";import{CliRelayClient as Ne}from"./relay/client.js";import{collectKnownLogSecrets as ae,installCliLogRedaction as xe,sanitizeCliLogValue as Oe}from"./log-redaction.js";import{registerPairCommand as $e,runSmartStart as Le}from"./commands/pair.js";import{registerLogoutCommand as Ve}from"./commands/logout.js";import{clearDaemonPidIfOwner as ie,createDaemonInstanceId as je,clearDaemonLauncher as se,findRunningDaemonProcessIds as He,isRemoteAccessDisabled as ce,prepareRemoteAccessPause as Fe,stopPreparedRemoteAccessService as Be,isIsolatedDaemonE2eInstance as We,getDaemonStatus as de,registerDaemonCommand as Ge,writeDaemonPid as le,writeDaemonLauncher as me}from"./commands/daemon.js";import{registerRuntimeCommand as qe}from"./commands/runtime.js";import{registerAdapterCommand as ze}from"./commands/adapter.js";import{registerAgentCommand as Je}from"./commands/agent.js";import{registerManagerCommand as Ke}from"./commands/manager.js";import{registerWorkerCommand as Qe}from"./commands/worker.js";import{registerProjectCommand as Ye}from"./commands/project.js";import{registerStewardCommand as Ze}from"./commands/steward.js";import{handleUpgradeStart as Xe,registerUpgradeCommand as eo}from"./commands/upgrade.js";import{registerToolsCommand as oo}from"./commands/tools.js";import{registerQueueCommand as no}from"./commands/queue.js";import{registerSkillCommand as to}from"./commands/skill.js";import{registerSetupCommand as ro}from"./commands/setup.js";import{getUnavailableAcpAgents as ao}from"./agents/acp/discovery.js";import{registerCommandReference as io}from"./commands/command-reference.js";import{registerIntegrationCommand as so}from"./commands/integration.js";import{registerRoomCommand as co}from"./commands/room.js";import{registerSupportCommand as lo}from"./commands/support.js";import{SessionManager as mo}from"./session/manager.js";import{createTrustedProjectFsRequestResolver as po}from"./session/handlers/project-fs.js";import{SERVERS as K,regionToUrl as go,urlToRegion as uo}from"./region.js";import{clearUpgradeAttempt as fo,getCurrentVersion as pe,handleStartupCrashCheck as ho,readUpgradeAttempt as vo}from"./upgrade/engine.js";import{AutoUpgradeScheduler as Io}from"./upgrade/scheduler.js";import{ensureUpdaterInstalled as So}from"./upgrade/updater-bootstrap.js";import{launchUpdater as yo,markUpdaterHealthy as wo}from"./upgrade/updater-launcher.js";import{detectAgents as ge}from"./agents/detect.js";import{PublicAgentDeliveryWorker as Co}from"./agents/public-delivery-worker.js";import{augmentProcessPath as ue}from"./env-path.js";import{acquireDaemonInstanceLock as Ro}from"./commands/daemon-instance-lock.js";ue();const U=pe();import{getCachedAgentInfos as V,resolveAgentInfos as bo}from"./agents/model-registry.js";import{initCliLogReporter as ko,reportLog as D,reportUpgradeAggregateMetric as Ao}from"./log-reporter.js";import{isNativeFusionEnabled as Po}from"./native-fusion/config.js";import{NativeSessionFusionService as Eo}from"./native-fusion/service.js";import{startDaemonLogRetention as To}from"./daemon-log.js";import{startDaemonIpcServer as fe}from"./daemon-ipc/server.js";import{resolveDaemonIpcPaths as Uo,resolveManagedRoomIpcPaths as Do}from"./daemon-ipc/paths.js";import{createDaemonControlHandlers as Mo}from"./daemon-ipc/control.js";import{createAgentReadinessHandlers as _o}from"./daemon-ipc/agent-readiness.js";import{scanMachineAgents as he}from"./session/handlers/agents.js";import{validateAgentWorkspaceConfiguration as No}from"./session/handlers/agent-workspace-validation.js";import{DAEMON_IPC_VERSION as xo}from"./daemon-ipc/protocol.js";import{RoomSessionRegistry as Oo}from"./room/session-registry.js";import{HttpRoomBindingClient as $o}from"./room/room-binding-client.js";import{TrustedManagedRoomBinder as Lo}from"./room/managed-room-binding.js";import{RoomHostSessionObservationStore as Vo}from"./room/host-session-observations.js";import{RoomMirrorSyncService as jo}from"./room/mirror-sync-service.js";import{handleLiveRoomDelivery as Ho}from"./room/live-delivery.js";import{NativeSourceDeletionStore as Fo}from"./native-fusion/native-source-deletions.js";import{matchesRoomOwner as Bo,isCurrentRegistration as Wo}from"./room/registration-authority.js";import{RoomActivationService as Go,resolveManagedAgentPolicy as qo}from"./room/activation-service.js";import{RoomActivationAudit as zo}from"./room/activation-audit.js";import{RoomAttachmentCache as Jo}from"./room/attachment-cache.js";import{RoomAttachmentUploader as Ko}from"./room/attachment-upload.js";import{ManagedRoomContextAuthority as Qo}from"./room/managed-context.js";import{createManagedRoomExecutor as Yo}from"./room/managed-room-ipc.js";import{SESSION_SYNC_PROTOCOL_VERSION as ve,SESSION_SYNC_ADVERTISED_CAPABILITIES as Ie}from"@shennian/wire";import{getDaemonInstallationId as Zo}from"./session/store.js";import{OWNER_DIRECT_TURN_OPTIONS as Xo}from"./session/queue-helpers.js";import{synchronizeMachinePersonalAuthority as en}from"./personal-sync/machine-authority.js";import{CLI_SESSION_SYNC_DATA_EPOCH as Se,ensurePersonalDataEpoch as on}from"./personal-sync/epoch.js";import{DeletionSyncRetryWaitError as nn,DeletionSyncTerminalError as tn}from"./session/deletion-sync-state.js";const ye=Me(),rn=J("daemon.pid"),an=J("daemon.instance.lock");function sn(){try{const i=z.readFileSync(rn,"utf-8").trim();if(i.startsWith("{")){const g=JSON.parse(i),d=Number(g.pid);return Number.isInteger(d)&&d>0?d:null}const c=parseInt(i,10);return Number.isInteger(c)&&c>0?c:null}catch{return null}}function cn(i){return i.replace(/^https:\/\//,"wss://").replace(/^http:\/\//,"ws://")}function Q(){process.stdout.write(`${JSON.stringify({ok:!1,error:"integration_command_retired",message:"Room MCP and Hooks were retired. Use the Shennian Skill and CLI.",nextAction:{type:"open_install_guide",url:"https://shennian.net/install.md"}})}
|
|
2
|
+
`),process.exitCode=1}function dn(i){const c=[`trigger=${i.trigger??"unknown"}`,`phase=${i.phase}`,`code=${i.code??"unknown"}`,`attempt=${i.reconnectAttempt}`];return i.reason&&c.push(`reason=${i.reason}`),i.error&&c.push(`error=${i.error}`),c.join(" ")}const a=new Pe;a.name("shennian").description("Shennian \u2014 AI Agent Control Plane").version(U),a.enablePositionalOptions(),so(a),co(a),lo(a),be(a),a.option("--api <url>","Server URL override").option("--name <name>","Machine name",Ee.hostname()).action(async i=>{const c=p(),g=i.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??c.serverUrl??void 0;await Le(g,i.name)}),a.command("run-service",{hidden:!0}).description("(internal) Connect to relay server, called by the background service").option("--api <url>","Server URL override").action(async i=>{const c=J("env.json");try{const e=JSON.parse(z.readFileSync(c,"utf-8"));for(const[n,o]of Object.entries(e))process.env[n]||(process.env[n]=o);ue()}catch{}xe(ae(process.env,p()));const g=To();ce()&&(console.log(`[${new Date().toISOString()}] remote access disabled, service start skipped`),clearInterval(g),process.exit(0));const d=je(),C=Ro({lockPath:an,instanceId:d});C.acquired||(console.log(`[${new Date().toISOString()}] daemon already running`+(C.ownerPid?` (PID ${C.ownerPid})`:"")+", skipping duplicate start"),clearInterval(g),process.exit(0));try{const e=sn();if(e&&e!==process.pid)try{process.kill(e,0),console.log(`[${new Date().toISOString()}] daemon already running (PID ${e}), skipping duplicate start`),C.release(),process.exit(0)}catch{}}catch{}const Y=We(process.env,ye)?[]:He(process.pid);Y.length>0&&(console.log(`[${new Date().toISOString()}] daemon already running (PID ${Y[0]}), skipping duplicate start`),C.release(),process.exit(0)),le(process.pid,d,{version:U}),me(process.pid,void 0,d),process.on("exit",()=>{ie(process.pid,d),se(d)}),process.on("exit",C.release);let j=null;try{j=So()}catch(e){console.error(`[upgrade] updater bootstrap unavailable: ${e instanceof Error?e.message:String(e)}`)}const M=await ho();if(M){j||(console.error("[upgrade] Crash-loop rollback blocked: updater bootstrap unavailable"),process.exit(1));try{yo({fromVersion:U,targetVersion:M.from,oldDaemonPid:process.pid,operation:"rollback",rollbackJobId:M.jobId},{readiness:j}),console.error(`[upgrade] New version crashed ${M.attemptCount} times; external rollback scheduled.`),process.stdin.resume();return}catch(e){console.error(`[upgrade] Crash-loop rollback could not start: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}try{on()}catch(e){console.error(`[personal-sync] data epoch initialization failed: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1;return}const m=p(),u=i.api??process.env.SHENNIAN_DESKTOP_SERVER_URL??m.serverUrl??K.cn.url,h=pe(),l=await ge({serverUrl:u,authToken:m.machineToken??m.accessToken}),S=l.map(e=>e.type);let H=V(l);const f=new Oo,_=new Vo,b=new Qo(f),y=new $o(u),Ce=new Jo({actionClient:y}),Re=new Ko({actionClient:y});let w=null,r;const Z=Yo({authority:b,actionClient:y,attachmentCache:Ce,attachmentUploader:Re,observations:_,getMachineCredentials:()=>({machineToken:p().machineToken}),managerExecutor:e=>r.executeManagedManagerAction(e),onSendOutcome:e=>r.noteManagedRoomSendOutcome(e),getActiveRunId:(e,n)=>r.getManagedRoomActiveRunId(e,n),scheduleExecutor:(e,n,o)=>re({serverUrl:u,machineToken:p().machineToken,sessionId:e,action:n,args:o}),onPaused:({registryKey:e,sessionId:n})=>{const t=f.getByRegistryKey(e)?.binding;t&&f.setBinding({registryKey:e,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:"paused",activationMode:"paused"}),b.revokeSession(e),r?.discardPendingManagedRoomMessages(n)}});let F=null,B=null,W=null,G=null,X=!1;const k=await fe({paths:Uo(),handlers:{...Mo({daemonVersion:h,protocolVersion:xo,getStatus:()=>de(),getAgents:()=>l,requestShutdown:()=>{G?G():X=!0},completeAgentAuthentication:e=>r.resumePendingAcpAuthentication(e),bindManagedRoom:e=>r.bindManagedRoomFromLocal(e),stewardSay:e=>r.stewardSayFromLocal(e),startUpgrade:async e=>{if(!F)throw new Error("upgrade_unavailable");return F(e)},prepareExternalUpgrade:e=>{if(!B)throw new Error("external_upgrade_unavailable");return B(e)},cancelExternalUpgrade:()=>{if(!W)throw new Error("external_upgrade_unavailable");return W()}}),..._o({getStatus:()=>de(),getSnapshot:()=>V(l).map(e=>{const n=l.find(o=>o.type===e.type);return{...e,runtimePlatform:n?.runtimePlatform,runtimeIsWsl:n?.runtimeIsWsl}}),getUnavailable:ao,discover:async e=>{if(!r)throw new Error("daemon_starting");const n=await he(!0,e);if(e){const t=l.filter(I=>I.type!==e);l.splice(0,l.length,...t,...n.detectedAgents)}else l.splice(0,l.length,...n.detectedAgents);const o=V(l);return s.sendEvent({type:"event",event:"machine.agents",payload:{agentList:l.map(t=>t.type),agents:o}}),n.agents.map(t=>{const I=n.detectedAgents.find(q=>q.type===t.type);return{...t,runtimePlatform:I?.runtimePlatform,runtimeIsWsl:I?.runtimeIsWsl}})},validate:async e=>{if(!r)throw new Error("daemon_starting");return No(e,{ignoreNativeSession:(n,o)=>P?.ignoreNativeSource(n,o)})}})}});let A;try{A=await fe({paths:Do(),handlers:{"managed-room.execute":e=>Z(e),"schedule.personal":async e=>{const n=e,o=ke(n?.context);if(f.getByNianSessionId(o)?.binding)throw new Error("schedule_personal_context_required");return await re({serverUrl:u,machineToken:p().machineToken,sessionId:o,action:n.operation,args:n})}}})}catch(e){throw await k.close(),e}let ee=!1;const N=()=>{ee||(ee=!0,Promise.allSettled([k.close(),A.close()]).finally(()=>process.exit(0)))};if(process.once("SIGINT",N),process.once("SIGTERM",N),!m.machineToken){console.error('Shennian CLI is not paired. Run "shennian pair --browser" or "shennian pair --token" first.'),await Promise.allSettled([k.close(),A.close()]),process.exitCode=1;return}process.removeListener("SIGINT",N),process.removeListener("SIGTERM",N);const oe=`${cn(u)}/relay/machine`,x=m.machineToken;if(!x)return;try{await en({serverUrl:u,machineToken:x,machineId:m.machineId})}catch(e){console.error(`[personal-sync] machine authority verification failed: ${e instanceof Error?e.message:String(e)}`),await Promise.allSettled([k.close(),A.close()]),process.exit(1)}m.machineId&&ko(u,m.machineId),console.log(`[${new Date().toISOString()}] Connecting to ${oe}... (v${h}) agents: ${S.join(",")}`),D({level:"info",wsEvent:"daemon.start",metadata:{version:h,agents:S}});let P=null,E=null,R=0,v=vo();const ne=_e(m.autoUpgrade),O=m.machineId?new Co({serverUrl:u,machineId:m.machineId,getMachineToken:()=>p().machineToken,getDetectedAgents:()=>ge({serverUrl:u,authToken:p().machineToken}),onRuntimeChanged:async()=>{const e=R,n=await he(!0);if(l.splice(0,l.length,...n.detectedAgents),S.splice(0,S.length,...n.agentList),H=n.agents,!(await s.sendReq({type:"req",id:Te(),method:"machine.agents.report",params:{agents:n.agents}},2e4)).ok)throw new Error("machine_capability_report_rejected");e===R&&s.getState()==="connected"&&O?.start()},onError:()=>console.warn("[public-agent] delivery attempt failed")}):null,s=new Ne({serverUrl:oe,machineToken:x,cliVersion:h,agentList:S,autoUpgradePolicy:ne,sessionSyncProtocolVersion:ve,sessionSyncCapabilities:Ie,daemonInstallationId:Zo(),dataEpoch:Se,updaterProtocolVersion:1,onConnected:()=>{console.log(`[${new Date().toISOString()}] \u2713 Connected`),D({level:"info",wsEvent:"daemon.connected"}),s.sendEvent({type:"event",event:"machine.agents",payload:{agentList:S,agents:H}}),bo(l,{serverUrl:m.serverUrl??u,authToken:m.machineToken??m.accessToken}).then(o=>{JSON.stringify(o)!==JSON.stringify(H)&&s.sendEvent({type:"event",event:"machine.agents",payload:{agentList:S,agents:o}})}).catch(()=>{}),v&&s.sendEvent({type:"event",event:"upgrade",payload:{state:"recovering",machineId:"self",from:v.from,to:v.to,...v.jobId?{jobId:v.jobId}:{},activeRunCount:r.getPendingRecoveryCount()}}),$.start(),E?.start();const e=++R,n=async()=>{try{if(await r.synchronizeSessionDeletions(),await r.recoverInterruptedRuns(),v){const o=v;v=null,o.jobId&&wo(o.jobId,o.to),fo(),console.log(`[${new Date().toISOString()}] [upgrade] ${o.operation==="rollback"?"Rolled back":"Healthy"}: ${o.from} \u2192 ${o.to}`),Ao({kind:"upgrade",result:o.operation==="rollback"?"rolled_back":"healthy",stage:"terminal"}),s.sendEvent({type:"event",event:"upgrade",payload:{state:o.operation==="rollback"?"rolled_back":"healthy",machineId:"self",from:o.from,to:o.to,...o.jobId?{jobId:o.jobId}:{}}}),o.operation!=="rollback"&&s.sendEvent({type:"event",event:"upgrade",payload:{state:"success",machineId:"self",from:o.from,to:o.to}})}e===R&&s.getState()==="connected"&&(P?.handleConnected(),O?.start())}catch(o){if(console.error("[session.deletions] startup catch-up failed",o),e===R&&s.getState()==="connected"){if(o instanceof tn)return;const t=o instanceof nn?Math.max(1e3,o.retryAfterMs):5e3;setTimeout(()=>{n()},t).unref?.()}}};n()},onDisconnected:e=>{R+=1,r?.revokeProjectFsHandles(),O?.stop(),E?.stop(),console.log(`[${new Date().toISOString()}] \u26A0 Disconnected, reconnecting... ${dn(e)}`),D({level:"warn",wsEvent:"daemon.disconnected",metadata:{code:e.code,reason:e.reason,error:e.error,phase:e.phase,trigger:e.trigger,reconnectAttempt:e.reconnectAttempt}})},onProjectFsCapabilityChanged:e=>{e||r?.revokeProjectFsHandles()},onReq:e=>{if(console.log(`[${new Date().toISOString()}] [req] ${e.method}`),D({level:"info",type:"ws",wsEvent:e.method,wsDirection:"in",traceId:e.traceId}),e.method==="room.message.deliver"){Ho(s,E,e);return}r.handleReq(e)}});P=Po()?new Eo(s):null,E=new jo({registry:f,observations:_,actionClient:y,getMachineCredentials:()=>({machineToken:p().machineToken}),isSessionDeleted:e=>new Fo().hasSession(e),onMessages:async(e,n,o,t)=>{await w?.processMessages(e,n,o,t)},onBindingPaused:e=>{e.lifecycleState==="closed"?(b.revokeSession(e.registryKey),r?.retireManagedRoomSession(e.nianSessionId).catch(()=>{console.warn("[room.sync] retired session cleanup failed")})):r?.discardPendingManagedRoomMessages(e.nianSessionId)}});const $=new Io({currentVersion:h,machineId:m.machineId??"unknown-machine",serverUrl:m.serverUrl??u,getIdleState:()=>r.getUpgradeIdleState(),sessionSyncProtocolVersion:ve,dataEpoch:Se,advertisedCapabilities:Ie,updaterProtocolVersion:1,startUpgrade:async(e,n)=>{const{handleUpgradeStart:o}=await import("./commands/upgrade.js");await o(s,"auto-upgrade",e,{currentVersion:h,prepareForUpgrade:t=>r.prepareForUpgrade(t),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0,waitForCompletion:!0,canHandoff:n?void 0:()=>r.getUpgradeIdleState().idle})}});$.setPolicy(ne),r=new mo(s,P,h,$,new Lo({registry:f,observations:_,bindingClient:y,getMachineCredentials:()=>{const e=p();return{machineId:e.machineId,machineToken:e.machineToken}},hostProcessIdentity:()=>`daemon:${d}`}),{prepare:()=>Fe(),isDisabled:()=>ce(),shutdownAfterResponse:()=>{setTimeout(()=>{Be(),process.kill(process.pid,"SIGTERM")},100).unref?.()}},e=>b.revokeToken(e),Z,null,po(x),()=>V(l)),F=async e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=typeof n.targetVersion=="string"?n.targetVersion:void 0,t=n.confirmedMajor===!0;return await Xe(s,`local-upgrade-${Date.now()}`,o,{currentVersion:h,confirmedMajor:t,prepareForUpgrade:q=>r.prepareForUpgrade(q),cancelUpgradePreparation:()=>r.cancelUpgradePreparation(),shutdownAfterHandoff:()=>{setTimeout(()=>process.kill(process.pid,"SIGTERM"),100).unref?.()},suppressResponse:!0})},B=e=>{const n=e&&typeof e=="object"&&!Array.isArray(e)?e:{},t=`desktop-${(typeof n.targetVersion=="string"?n.targetVersion:"unknown").replace(/[^A-Za-z0-9._-]/g,"_")}-${Date.now()}`;return{jobId:t,...r.prepareForUpgrade(t)}},W=()=>(r.cancelUpgradePreparation(),{cancelled:!0}),w=new Go({registry:f,observations:_,enqueueConversationBatch:e=>r.enqueueManagedRoomBatch(e),loadContext:async e=>{const n=p().machineToken?.trim(),o=e.binding;if(!n||!o)throw new Error("managed_room_context_unavailable");const t=await y.status({machineToken:n,roomId:o.roomId,bindingId:o.bindingId});if(!Wo(f,e)||!Bo(e,t))throw new Error("managed_room_binding_unavailable");return{roomName:t.room.name,memberCount:t.room.memberCount,members:t.room.members,agentsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent}:{},effectiveModelsBySessionId:t.effectiveModelId!==void 0?{[e.nianSessionId]:t.effectiveModelId}:void 0,agentSystemPromptsByIdentityId:t.agent?{[t.agentIdentityId]:t.agent.systemPrompt??""}:{}}},issueManagedRoomContext:(e,n,o,t)=>{const I=p().machineToken;if(!I)throw new Error("machine_not_authorized");return b.issue({registryKey:e,machineCredential:I,activationCauseMessageId:n,allowedActions:o,propagateWorkerRoomOrigin:t})},audit:new zo,resolveManagedAgentPolicy:e=>qo({...e,runtime:l.find(n=>n.type===e.registration.runtimeAdapter)})}),r.scheduleHandler=e=>Ae({serverUrl:u,machineToken:p().machineToken,req:e,client:s,queue:r.getScheduleQueue(),registry:f}),r.getScheduleQueue().validateScheduledTarget=async e=>{if(!e.bindingId||!e.roomId)return"ready";if(f.getByNianSessionId(e.sessionId)?.binding?.bindingId!==e.bindingId)return"ended";const o=await y.status({machineToken:p().machineToken,roomId:e.roomId,bindingId:e.bindingId});return o.status==="active"&&o.room.status==="active"?"ready":"ended"},r.setManagedRoomTurnPreparer(e=>w.prepareConversationTurn(e)),r.setManagedAgentDirectTurnPreparer((e,n)=>w.prepareDirectConversationTurn(e,n)),r.setManagedRecoveryPreparer(async e=>e.source==="managed_direct"?w.prepareDirectConversationTurn(e.sessionId,Xo):e.source==="manager_event"?w.prepareDirectConversationTurn(e.sessionId,{allowedRoomActions:[],managerEvent:!0}):e.source!=="managed_room"||!e.roomContextRegistryKey||!e.activationCauseMessageId||!e.rootCauseMessageId||!e.activationSequence||e.activationHopCount===null?null:w.prepareConversationTurn([{registryKey:e.roomContextRegistryKey,causeMessageId:e.activationCauseMessageId,rootCauseMessageId:e.rootCauseMessageId,sequence:e.activationSequence,hopCount:e.activationHopCount}])),z.mkdirSync(ye,{recursive:!0}),le(process.pid,d,{version:h}),me(process.pid,void 0,d),s.connect(),process.stdin.resume();let te=!1;const L=async()=>{if(!te){te=!0,console.log(T.gray(`
|
|
3
|
+
Disconnecting...`)),D({level:"info",wsEvent:"daemon.stop"}),clearInterval(g),O?.stop();try{await r.cleanup(),$.stop(),P?.stop(),E?.stop(),s.disconnect(),await Promise.allSettled([k.close(),A.close()]),ie(process.pid,d),se(d)}finally{process.exit(0)}}};G=L,X&&L(),process.on("SIGINT",()=>{L()}),process.on("SIGTERM",()=>{L()})}),a.command("workbuddy-session-hook",{hidden:!0}).description("(retired) WorkBuddy Room Hook").allowUnknownOption(!0).action(()=>Q()),a.command("room-session-hook",{hidden:!0}).description("(internal) Authorize the official Room tool for one trusted Agent Session").argument("<runtime>","Trusted runtime adapter").allowUnknownOption(!0).action(()=>Q()),a.command("room-tool-server",{hidden:!0}).description("(internal) Run the official Shennian Room MCP tool").allowUnknownOption(!0).action(()=>Q());const we=a.command("config").description("View or modify configuration");we.command("show",{isDefault:!0}).description("Show current configuration").action(()=>{console.log(`Config file: ${De()}`);const i=p(),c=Oe(i,ae(process.env,i));if(i.serverUrl){const g=uo(i.serverUrl);console.log(`Region: ${g} (${K[g].label})`)}console.log(JSON.stringify(c,null,2))}),we.command("set").description("Update a config value").argument("<key>",'Config key (e.g. "server")').argument("<value>",'Config value (e.g. "cn" or "global")').action((i,c)=>{const g=p();if(i==="server"){c!=="cn"&&c!=="global"&&(console.error(T.red('\u2717 Value must be "cn" or "global"')),process.exit(1));const d=c;g.serverUrl=go(d),Ue(g),console.log(T.green(`\u2713 Server set to ${K[d].label}`)),console.log(T.yellow(" Restart the background service for this to take effect: shennian stop && shennian start"));return}console.error(T.red(`\u2717 Unknown config key: ${i}. Supported: server`)),process.exit(1)}),ro(a),$e(a),Ve(a),Ge(a),qe(a),ze(a,{clientVersion:U}),Je(a),Ke(a),Qe(a),Ye(a),Ze(a),eo(a),oo(a),no(a),to(a),io(a,U),a.parse();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash as r}from"node:crypto";const d=i=>r("sha256").update(i).digest("hex"),R="0".repeat(64),g=i=>{if(!Number.isSafeInteger(i)||i<1||i>50)throw new Error("export page limit must be between 1 and 50");return i};function m(i,e){if(!e)throw new Error("sessionId is required");const a=i.getManifest(e);if(!a)throw new Error("canonical session is unavailable");if(a.daemonInstallationId!==i.daemonInstallationId)throw new Error("canonical daemon identity changed");return{daemonInstallationId:i.daemonInstallationId,sessionId:e,snapshotRevision:a.bodyRevision,afterRevision:0,phase:"snapshot",auditHash:R}}function c(i,e,a){if(g(a),!e.sessionId||e.daemonInstallationId!==i.daemonInstallationId||!Number.isSafeInteger(e.snapshotRevision)||e.snapshotRevision<0||!Number.isSafeInteger(e.afterRevision)||e.afterRevision<0||!/^[a-f0-9]{64}$/.test(e.auditHash)||e.phase==="snapshot"&&e.afterRevision>e.snapshotRevision||e.phase!=="snapshot"&&e.phase!=="catchup")throw new Error("invalid export cursor");const o=i.getManifest(e.sessionId);if(!o||o.daemonInstallationId!==e.daemonInstallationId||o.bodyRevision<e.snapshotRevision||o.bodyRevision<e.afterRevision)throw new Error("canonical source changed or became unavailable");const t=e.phase==="snapshot"?e.snapshotRevision:o.bodyRevision,s=e.afterRevision<t?i.readDeltas(e.sessionId,e.afterRevision,a).filter(n=>n.revision<=t):[];if(e.afterRevision<t&&s.length===0)throw new Error("canonical export gap");let I=e.afterRevision+1,h=e.auditHash;const l=s.map(n=>{if(n.sessionId!==e.sessionId||n.daemonInstallationId!==e.daemonInstallationId||n.revision!==I++||!Number.isSafeInteger(n.sequence)||n.sequence<1||!n.mutationId||!n.contentHash)throw new Error("canonical export identity or revision mismatch");const v=d(JSON.stringify({mutation:n.mutation,message:n.message??null,deliveryState:n.deliveryState??null}));if(n.contentHash!==v)throw new Error("canonical export content hash mismatch");return h=d(`${h}\0${n.mutationId}\0${n.contentHash}`),{revision:n.revision,sequence:n.sequence,mutationId:n.mutationId,contentHash:n.contentHash,exportId:d(JSON.stringify([e.daemonInstallationId,e.sessionId,n.revision])),mutationHash:d(JSON.stringify(n)),mutation:n}}),f=l.at(-1)?.revision??e.afterRevision,p={...e,afterRevision:f,auditHash:h,phase:e.phase==="snapshot"&&f===e.snapshotRevision?"catchup":e.phase};return{cursor:e,targetRevision:t,items:l,nextCursor:p,pageHash:d(JSON.stringify(l.map(({exportId:n,mutationHash:v})=>[n,v]))),batch:{sessionId:e.sessionId,fromRevision:e.afterRevision,mutations:s}}}function E(i,e,a,o){if(!o||e.phase!=="catchup"||a!==e.afterRevision)throw new Error("cutover requires a fenced writer and acknowledged catchup");const t=i.getManifest(e.sessionId);if(!t||t.daemonInstallationId!==e.daemonInstallationId||t.bodyRevision!==e.afterRevision)throw new Error("cutover revision changed; resume catchup");const s=e.afterRevision>0?i.readDeltas(e.sessionId,e.afterRevision-1,1)[0]:null;if(e.afterRevision>0&&(!s||s.revision!==e.afterRevision))throw new Error("cutover tail is unavailable");return{daemonInstallationId:e.daemonInstallationId,sessionId:e.sessionId,revision:e.afterRevision,lastMutationHash:s?d(JSON.stringify(s)):null}}export{R as CONVERSATION_EXPORT_GENESIS_HASH,E as assertConversationCutoverFence,m as beginConversationExport,c as readConversationExportPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{buildManagedAgentEnv as
|
|
1
|
+
import{buildManagedAgentEnv as c}from"../../agents/config-status.js";import{lookupClaudeTranscriptCwd as i}from"../../native-fusion/parsers.js";import{getManagedRoomIpcRoot as d,MANAGED_ROOM_IPC_ROOT_ENV as p}from"../../daemon-ipc/paths.js";import{stewardSessionEnv as f}from"../../steward/session.js";function x(n){return c(n)}function C(n,e,o){return{}}function v(n,e,o,t,a,r,u,m){const l=o==="codex";n.configure?.({sessionId:e,externalChannel:t??null,env:{...x(o),...C(e,t,a),...f(e),...r&&!l?{SHENNIAN_MANAGED_ROOM_CONTEXT:r,[p]:d()}:{}},...u?{managedAgentPolicy:u}:{},...l?{managedRoomContextToken:r??null,managedRoomExecutor:r?m??null:null}:{}})}function s(n,e,o){const t=e.managedRoomContextToken;t&&t!==o&&n.revokeManagedRoomContextToken?.(t),e.managedRoomContextToken=o,e.roomSendOutcome={successes:0,failureCodes:[]}}function E(n,e){const o=e?.managedRoomContextToken;!e||!o||(e.managedRoomContextToken=null,n.revokeManagedRoomContextToken?.(o))}function M(n,e,o){if(n!=="claude"||!o)return e;const t=e.trim();return!!t&&t.startsWith("-")&&!t.includes("/")?i(o)??e:e}export{v as configureAdapterForSession,C as externalChannelEnv,x as managedProviderEnv,M as maybeResolveClaudeImportedWorkDir,s as replaceManagedRoomContextToken,E as revokeManagedRoomContextToken};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{buildApprovalPendingPayload as
|
|
2
|
-
`),o=r>0?Math.min(r,80):Math.min(a.length,80);return a.slice(0,o)}function A(a,r,o){return`${a}:${
|
|
1
|
+
import{buildApprovalPendingPayload as T}from"@shennian/wire";import{createHash as R}from"node:crypto";import{AgentEventTerminalGate as M}from"../../agents/event-terminal-gate.js";import{recordAcpAgentModels as D}from"../../agents/model-registry.js";import{reportLog as k}from"../../log-reporter.js";import{registerInternalNativeSource as _}from"../../native-fusion/source-visibility.js";import{requestSessionIndexPublish as c}from"../index-publisher.js";import{noteRunActivity as P}from"../run-stall.js";import{appendMessage as s,recordSession as h,upsertMessage as j}from"../store.js";import{writeToolDetail as O}from"../tool-detail-store.js";import{refreshMachineAgents as C}from"./agents.js";import{roomReplyFailureNotice as N}from"../room-reply-notice.js";import{commitStewardFallback as I}from"./steward.js";function B(a){const r=a.indexOf(`
|
|
2
|
+
`),o=r>0?Math.min(r,80):Math.min(a.length,80);return a.slice(0,o)}function A(a,r,o){return`${a}:${R("sha256").update(`${r}\0${o??""}`).digest("hex")}`}function F(a,r,o={}){if(a.state==="tool-call"||a.state==="tool-result"){const i={runId:a.runId,sourceSeq:a.seq};return{state:a.state,runId:a.runId,seq:a.seq,sessionId:r,detailRef:i,status:a.state==="tool-call"?"running":"completed",detailAvailable:!0,...a.durationMs!==void 0?{durationMs:a.durationMs}:{},...a.name?{name:a.name}:{},...a.source?{source:a.source}:{},...a.agentSessionId?{agentSessionId:a.agentSessionId}:{},...o}}return{...a,sessionId:r,...o}}const H=3e4;function J(a){return a.state==="heartbeat"?a.runPhase??null:a.state==="tool-call"||a.state==="tool-result"?"tool_running":a.state==="approval-pending"?"waiting_approval":a.state==="delta"?a.thinking?"thinking":"streaming_text":a.state==="init"||a.state==="start"?"thinking":null}function w(a,r){return a!=="manager"?a:r==="claude"?"claude":"codex"}function at(a,r,o,i){let S=null;const E=new M;function x(t,n={}){a.client.sendAgentEvent({type:"event",event:"agent",payload:F(t,r,n),seq:t.seq,id:`agent-evt-${t.runId}-${t.seq}`})}o.startsWith("acp:")&&i.on("sessionOptions",()=>{try{const t=i.getSessionOptions?.().models??[];if(!D(o,t))return;const n=a.getAgentSnapshot?.();if(!n)return;a.client.sendEvent({type:"event",event:"machine.agents",payload:{agentList:n.map(e=>e.type),agents:n}})}catch{}});function y(t){t?.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null)}function $(t){if(!t.currentRunId||!t.currentRunPhase)return;const n=t.heartbeatSeq++;a.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:r,runId:t.currentRunId,seq:n,runPhase:t.currentRunPhase},seq:n,id:`agent-heartbeat-${t.currentRunId}-${n}-${Date.now()}`})}function b(t){!t||t.heartbeatTimer||(t.heartbeatTimer=setInterval(()=>{$(t)},H),t.heartbeatTimer.unref?.())}function g(t){const n=t?.pendingTextEvent;!t||!n||!n.text||(s(r,{id:`agent-${n.runId}-${n.seq}`,sessionId:r,role:"agent",ts:Date.now(),payload:n.text}),c(a.client),x({state:"delta",runId:n.runId,seq:n.seq,text:n.text,thinking:n.thinking||void 0}),t.sessionScope==="steward"&&(t.stewardLastText=n.text),t.pendingTextEvent=null)}i.on("agentEvent",t=>{if(!E.accept(t))return;let n=!1;t.errorCode==="acp_auth_required"&&C(a).catch(()=>{});const e=a.sessions.get(r),p=J(t),u=t.state==="final"||t.state==="error"||t.state==="aborted";if(e&&P(r,e,u),e&&(e.nextEventSeq=t.seq+1,!u&&e.currentRunId!==t.runId&&e.sessionScope==="steward"&&(e.stewardSay={count:0},e.stewardLastText=""),u||(e.currentRunId=t.runId),!u&&p&&(e.currentRunPhase=p,b(e)),t.agentSessionId&&(e.agentSessionId=t.agentSessionId),e.journalRunId)){const l={agentSessionId:t.agentSessionId??e.agentSessionId,providerRunId:t.runId||null,lastObservedEventSeq:t.seq,...u?{}:{phase:p==="waiting_approval"?"waiting_approval":"running"}};a.runJournal?.update(r,e.journalRunId,l)}if(a.managerRuntime?.noteAgentEvent(r,t),t.state==="delta"&&t.thinking)return;t.state!=="delta"&&k({level:"info",sessionId:r,wsEvent:`agent.${t.state}`,wsDirection:"out",metadata:{runId:t.runId,seq:t.seq,agentType:o}});let f=!1;if(t.state==="tool-call"||t.state==="tool-result"){const l=t.toolCallId?`agent-tool-${R("sha256").update(`${t.runId}\0${t.toolCallId}`).digest("hex")}`:`agent-${t.runId}-${t.seq}`;O({sessionId:r,toolCallId:l,detailRevision:t.seq,ts:Date.now(),name:t.name??"tool",...t.args!==void 0?{args:t.args}:{},...t.result!==void 0?{result:t.result}:{},status:t.toolStatus??(t.state==="tool-call"?"running":"completed"),...t.durationMs!==void 0?{durationMs:t.durationMs}:{},detailRef:{runId:t.runId,sourceSeq:t.seq}}),(t.toolCallId?j:s)(r,{id:l,sessionId:r,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:t.state==="tool-call"?"tool_use":"tool_result",name:t.name,toolCallId:l,status:t.toolStatus??(t.state==="tool-call"?"running":"completed"),...t.durationMs!==void 0?{durationMs:t.durationMs}:{},detailRef:{runId:t.runId,sourceSeq:t.seq},detailRevision:t.seq,detailAvailable:!0,resultOmitted:t.state==="tool-result"})}),f=!0}else t.state==="approval-pending"?(s(r,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:r,role:"agent",ts:Date.now(),payload:T({...t.approval,permission:t.permission})}),f=!0,e&&h({sessionId:r,agentType:o,workDir:e.workDir,agentSessionId:e.agentSessionId,attentionId:A("approval",t.runId,t.seq)})):(t.state==="error"||t.state==="aborted")&&t.message&&(s(r,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:r,role:"agent",ts:Date.now(),payload:t.message}),f=!0);f&&c(a.client);const d=`${r}:${t.runId}`;if(t.state==="delta"&&!t.thinking&&t.text&&a.runTextAcc.set(d,(a.runTextAcc.get(d)??"")+t.text),t.agentSessionId&&t.agentSessionId!==S&&(e?.nativeSessionVisibility==="internal"&&_(r,w(o,t.source)??o,t.agentSessionId),S=t.agentSessionId,a.nativeFusion?.noteManagedSourceSession(r,w(o,t.source),t.agentSessionId),e&&h({sessionId:r,agentType:o,workDir:e.workDir,agentSessionId:t.agentSessionId}),a.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:r,agentType:o,agentSessionId:t.agentSessionId}}})),t.state==="delta"){const l=t.text??"";if(!l)return;e?.pendingTextEvent&&e.pendingTextEvent.runId!==t.runId&&g(e),e&&!e.pendingTextEvent&&(e.pendingTextEvent={runId:t.runId,seq:t.seq,text:"",thinking:!1}),e?.pendingTextEvent&&(e.pendingTextEvent.text+=l,e.pendingTextEvent.seq=t.seq);return}let q={};if(t.state==="final"){g(e),e&&I(a,r,e,t);const l=e?N(e):null;l&&(n=!0,s(r,{id:`room-reply-notice-${t.runId}`,sessionId:r,role:"agent",ts:Date.now(),payload:l}),c(a.client));const m=a.runTextAcc.get(d)??"";m&&(q={messageSummary:B(m)}),a.runTextAcc.delete(d)}else t.state==="error"||t.state==="aborted"?(g(e),e&&I(a,r,e,t),a.runTextAcc.delete(d)):(t.state==="tool-call"||t.state==="tool-result"||t.state==="approval-pending")&&g(e);u&&e?.currentRunId===t.runId&&(e.currentRunId=null,e.currentRunPhase=null,e.nextEventSeq=0,y(e),a.chatQueue?.noteTerminal(r),h({sessionId:r,agentType:o,workDir:e.workDir,agentSessionId:e.agentSessionId,status:t.state==="final"&&!n?"completed":"failed",attentionId:A("terminal",t.runId),lastActivityAt:new Date().toISOString()}),c(a.client),e.journalRunId&&(a.runJournal?.remove(r,e.journalRunId),a.recoveryBarrier?.end(r),e.journalRunId=null)),x(t,q)}),i.on("error",t=>{console.error(`[chat.send] adapter error sessionId=${r} agentType=${o}: ${t.message}`);const n=a.sessions.get(r);n&&I(a,r,n,{state:"error",runId:n.currentRunId??"",message:t.message}),n?.journalRunId&&(a.runJournal?.update(r,n.journalRunId,{phase:"terminal"}),a.runJournal?.remove(r,n.journalRunId),a.recoveryBarrier?.end(r),n.journalRunId=null),a.sessions.delete(r),a.chatQueue?.noteTerminal(r),a.chatQueue?.isDispatching?.(r)||(s(r,{id:`agent-error-${r}-${Date.now()}`,sessionId:r,role:"agent",ts:Date.now(),payload:t.message}),c(a.client)),a.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:t.message,runId:"",seq:0}})})}export{at as bindAdapterEvents,w as getNativeSourceAgentType};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createAgent as A}from"../../agents/adapter.js";import{ManagerModeAdapter as h}from"../../agents/manager.js";import{registerInternalNativeSource as q}from"../../native-fusion/source-visibility.js";import{recordSession as w}from"../store.js";import{configureAdapterForSession as x,externalChannelEnv as
|
|
1
|
+
import{createAgent as A}from"../../agents/adapter.js";import{ManagerModeAdapter as h}from"../../agents/manager.js";import{registerInternalNativeSource as q}from"../../native-fusion/source-visibility.js";import{recordSession as w}from"../store.js";import{configureAdapterForSession as x,externalChannelEnv as S,managedProviderEnv as E,revokeManagedRoomContextToken as b}from"./chat-adapter-config.js";import{bindAdapterEvents as C}from"./chat-events.js";import{isStewardSessionId as k}from"../../steward/session.js";function U(n,r){if(n.processedReqIds.add(r),n.processedReqIds.size>1e3){const e=n.processedReqIds.values().next().value;n.processedReqIds.delete(e)}}async function $(n,r){b(n,r),r.heartbeatTimer&&(clearInterval(r.heartbeatTimer),r.heartbeatTimer=null),r.adapter.removeAllListeners(),await r.adapter.stop().catch(()=>{})}function B(n,r,e,t,a,u,o){return o?n.send(r,e,t,a,u,o):u?.trim()?n.send(r,e,t,a,u):a?.length?n.send(r,e,t,a):t?n.send(r,e,t):n.send(r,e)}function D(n,r,e,t,a,u,o){return n.prepareSend?n.prepareSend(r,e,t,a,u,o):Promise.resolve()}async function G(n,r,e,t,a,u,o,d,R,c="user"){n.evictIdleSessions();const p=A(e);if(!p)throw new Error(`Unsupported agent: ${e}`);const l=t==="manager"&&e!=="manager"?new h(p,e):p;x(l,r,e,o,d,null,R,n.managedRoomExecutor),w({sessionId:r,agentType:e,sessionMode:t,workDir:a,agentSessionId:u,nativeSessionVisibility:c}),l.on("nativeSession",(v,m)=>{c==="internal"&&q(r,v,m),w({sessionId:r,agentType:e,workDir:a,agentSessionId:m,nativeSessionVisibility:c})}),c==="internal"&&(e==="codex"||e==="manager")?await l.start(r,a,u,{nativeSessionVisibility:c}):await l.start(r,a,u);const f={adapter:l,workDir:a,agentType:e,sessionMode:t,nativeSessionVisibility:c,...k(r)?{sessionScope:"steward"}:{},agentSessionId:u??null,lastActiveAt:Date.now(),currentRunId:null,currentRunPhase:null,nextEventSeq:0,heartbeatSeq:0,heartbeatTimer:null,pendingTextEvent:null,externalChannel:o??null,externalReplyTarget:d??null,externalChannelEnv:{...E(e),...S(r,o,d)},managedRoomContextToken:null,journalRunId:null};return n.sessions.set(r,f),C(n,r,e,l),f}export{G as createActiveSession,$ as disposeSession,D as prepareAdapterTurn,U as rememberProcessedReqId,B as sendAdapterTurn};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{buildUserMessagePayload as ce}from"@shennian/wire";import{createHash as Pe,randomUUID as Ee}from"node:crypto";import ue from"node:os";import{reportLog as b}from"../../log-reporter.js";import{getPersonalSyncIdentity as De}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as Te}from"../../room/managed-prompt-composer.js";import{parsePersonalAttachmentReferences as be}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as xe,markAttachmentReferences as $e}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as _}from"../index-publisher.js";import{isTrustedManagerEvent as Ce}from"../manager-event-authority.js";import{mergeProjectedSessions as We}from"../projection.js";import{appendMessage as ge,getDaemonInstallationId as pe,getMessageDeliveryStatus as qe,upsertMessage as S}from"../store.js";import{configureAdapterForSession as me,maybeResolveClaudeImportedWorkDir as je,replaceManagedRoomContextToken as fe,revokeManagedRoomContextToken as ye}from"./chat-adapter-config.js";import{resolveNativeSessionVisibility as Fe,sendSessionUpdateEvent as Oe,validateManagedRoomExecutionContext as Be}from"./chat-context.js";import{getNativeSourceAgentType as Je}from"./chat-events.js";import{noteRunActivity as ze}from"../run-stall.js";import{createActiveSession as Ue,disposeSession as C,prepareAdapterTurn as He,rememberProcessedReqId as Ne,sendAdapterTurn as ve}from"./chat-session-lifecycle.js";import{sendSessionUpdateEvent as In,validateManagedRoomExecutionContext as Rn}from"./chat-context.js";function he(e,n){return`Agent send failed: ${n instanceof Error?n.message:String(n)}`}function Ve(e,n){const o=e.sessions.get(n),s=o?.currentRunId;if(!o||!s)return;const t=o.nextEventSeq;e.runTextAcc.delete(`${n}:${s}`),ye(e,o),o.pendingTextEvent=null,ze(n,o,!0),o.currentRunId=null,o.currentRunPhase=null,o.nextEventSeq=0,o.heartbeatTimer&&(clearInterval(o.heartbeatTimer),o.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:n,runId:s,seq:t},seq:t,id:`agent-evt-${s}-${t}`}),o.journalRunId&&(e.runJournal?.remove(n,o.journalRunId),e.recoveryBarrier?.end(n),o.journalRunId=null),e.chatQueue?.noteTerminal(n)}async function fn(e,n,o){const s=o?n.params.managedRoomContextToken??null:null;let t=!1;try{await Ke(e,n,o,()=>{t=!0})}finally{s&&!t&&e.revokeManagedRoomContextToken?.(s)}}async function Ke(e,n,o,s){if(e.processedReqIds.has(n.id)){e.client.sendRes({type:"res",id:n.id,ok:!0});return}const{sessionId:t,text:p,agentType:R,sessionMode:W,managerConfig:X,workDir:Ie,agentSessionId:m,modelId:y,modelSelectionSource:q,systemPrompt:Y,managerDefaultWorkerAgentType:j,managerDefaultWorkerModelId:F,reasoningEffort:O,clientMessageId:Re,sessionListProjection:_e,waitForDispatch:ee,responseId:Se,managedRoomContextToken:w,suppressExternalOwnerEcho:we,queuedBatchItems:B,managedWorkerParentSessionId:g,managerWorkerRoomOrigin:J,managerWorkerNativeFull:z,managedRecoverySource:E}=n.params,c=Se||n.id,ne=n.params;if((ne.managerEventId||ne.managerEventRoomOrigin||g||J||z||E)&&!Ce(n))throw new Error("manager_event_source_untrusted");We(_e);const x=null,U="";if(o?.source==="room_activation"&&(Be(o,Y,w),R==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(o?.source==="manager_event"){if(!o.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if(!w||!/^mrc_[A-Za-z0-9_-]{43}$/.test(w))throw new Error("managed_room_context_unavailable")}if(Ne(e,n.id),!t||!p){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:"sessionId and text are required"});return}if(e.recoveryBarrier?.isDispatchFrozen()){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:"upgrade_in_progress"});return}e.recoveryBarrier?.isRecovering(t)&&await e.recoveryBarrier.wait(t);const i=R,k=i==="manager"||W==="manager"?"manager":"standard";k==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(t,j??null,F??null);const D=(i==="claude"||i==="codex"||i==="workbuddy"||i==="pi-coding-agent"||i.startsWith("acp:"))&&typeof O=="string"&&O.trim()?O.trim():void 0,H=o?.source==="room_activation"?{source:"room_activation",...i==="pi-coding-agent"?{modelSelectionSource:q??(y?"request_model":"pi_default_model")}:{}}:o?.source==="manager_event"?{source:"managed_private",...i==="pi-coding-agent"?{modelSelectionSource:q??(y?"request_model":"pi_default_model")}:{}}:i==="pi-coding-agent"?{source:"personal",modelSelectionSource:q??(y?"request_model":"pi_default_model")}:void 0,h=e.resolvePath(je(i,Ie||ue.homedir(),m)||ue.homedir()),v=o?.managedAgentPolicy??(g?e.managerRuntime?.getManagedAgentPolicyForWorker(g,h)??null:null);if(g&&!z&&!v)throw new Error("managed_worker_permission_context_unavailable");const N=Fe(t,i,k,!!(v||g||o)),V=g&&v?Te({requestedPolicy:v.requestedPolicy,enforcement:v.enforcement,resolvedWorkDir:v.adapterPolicy.workDir}):Y??null,ke=h,$=be(n.params.attachments);$?.length&&xe(h,$);const f={text:p,attachments:$,localized:!1};let r=e.sessions.get(t);if(r){if(r.lastActiveAt=Date.now(),r.agentType!==i||(r.sessionMode??(r.agentType==="manager"?"manager":"standard"))!==k||r.workDir!==h||(r.nativeSessionVisibility??"user")!==N||JSON.stringify(r.externalChannel??null)!==JSON.stringify(x??null)){e.sessions.delete(t);try{await C(e,r)}catch{e.processedReqIds.delete(n.id)}r=void 0}else if(m&&r.agentSessionId!==m)try{await r.adapter.resume(m),r.agentSessionId=m}catch{e.sessions.delete(t);try{await C(e,r)}catch{e.processedReqIds.delete(n.id)}r=void 0}}if(!r)try{r=await Ue(e,t,i,k,h,m,x,U,v,N)}catch(a){const l=a instanceof Error&&a.message.startsWith("Unsupported agent:")?a.message:`Failed to start ${R}: ${a instanceof Error?a.message:String(a)}`;console.error(`[chat.send] start failed reqId=${n.id} sessionId=${t} agentType=${R} workDir=${h} agentSessionId=${m??""}: ${l}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:t,message:l,runId:"",seq:0}}),e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:l});return}const u={id:Re??`user-${n.id}`,sessionId:t,role:"user",ts:Date.now(),payload:ce(f.text,f.attachments)},ae=Array.isArray(B)&&B.length>0?B.map(a=>({id:a.clientMessageId??a.id,sessionId:t,role:E==="manager_event"||n.params.scheduledDelivery===!0?"agent":"user",ts:E==="manager_event"||o?.source==="room_activation"?Date.parse(a.createdAt):u.ts,payload:ce(a.text,a.attachments)})):null,A=ae??[u],K=qe(t,u.id),te=K.state==="queued";if(K.state!=="not_found"&&!te){e.client.sendRes({type:"res",id:c,ok:!0,payload:K});return}if(ee){(o?.source==="room_activation"||o?.source==="manager_event")&&me(r.adapter,t,i,x,U,w,o.managedAgentPolicy,e.managedRoomExecutor);try{await He(r.adapter,f.text,y,D,f.attachments,V,H)}catch(a){e.processedReqIds.delete(n.id),e.sessions.delete(t),await C(e,r),e.client.sendRes({type:"res",id:c,ok:!1,error:a instanceof Error?a.message:String(a)});return}}b({level:"info",sessionId:t,wsEvent:"chat.send.start",metadata:{reqId:n.id,agentType:i,sessionMode:k,managerConfig:X??null,modelId:y,managerDefaultWorkerAgentType:j??null,managerDefaultWorkerModelId:F??null,reasoningEffort:D,...o?.source==="room_activation"?{managedRoomPromptSnapshot:o.managedRoomPromptSnapshot}:{}}});const M=(a,l)=>({...De(pe()),sessionId:t,clientMessageId:u.id,messageId:u.id,deliveryState:a,bodyRevision:l,committedAt:new Date(u.ts).toISOString()}),L=a=>{e.nativeFusion?.registerManagedSend({sessionId:t,agentType:i,sourceAgentType:Je(i,y),canonicalMessageId:u.id,sourceSessionKey:r.agentSessionId??m??null,text:f.text,managedEchoPolicy:a?.deliveryMode==="external_owner_queue"&&!we?"import_following":"suppress_following",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??u.id}`:null})},oe=()=>{r.currentRunId||(r.currentRunId=`dispatch:${n.id}`,r.currentRunPhase="thinking",r.nextEventSeq=0,r.lastProviderActivityAt=Date.now())},re=()=>{if(!e.runJournal||r.journalRunId)return;const a=`run:${Ee()}`;e.runJournal.begin({daemonInstallationId:pe(),sessionId:t,runId:a,clientMessageId:u.id,agentType:i,sessionMode:k,workDir:h,agentSessionId:r.agentSessionId??m??null,modelId:y??null,reasoningEffort:D??null,nativeSessionVisibility:N,providerRunId:null,source:E==="manager_event"?"manager_event":o?.source==="room_activation"?E==="managed_direct"?"managed_direct":"managed_room":g?"manager_worker":"personal",parentSessionId:g??null,managerWorkerNativeFull:z===!0,roomContextRegistryKey:typeof n.params.roomContextRegistryKey=="string"?n.params.roomContextRegistryKey:null,activationCauseMessageId:typeof n.params.activationCauseMessageId=="string"?n.params.activationCauseMessageId:null,rootCauseMessageId:typeof n.params.rootCauseMessageId=="string"?n.params.rootCauseMessageId:null,activationSequence:typeof n.params.activationSequence=="string"?n.params.activationSequence:null,activationHopCount:typeof n.params.activationHopCount=="number"?n.params.activationHopCount:null,managedPolicyVersion:o?.source==="room_activation"?o.managedRoomPromptSnapshot.managedRoomPolicyVersion:o?.source==="manager_event"?o.managedRoomPolicyVersion:null,managedRuntimePolicyHash:(o?.source==="room_activation"?o.managedRoomPromptSnapshot.runtimePermissionPolicyHash:o?.source==="manager_event"?o.managedRuntimePolicyHash:null)??(v?Pe("sha256").update(JSON.stringify({requestedPolicy:v.requestedPolicy,enforcement:v.enforcement})).digest("hex"):null)}),r.journalRunId=a},se=()=>{if(L(),te){let a=0;for(const[l,d]of A.entries()){const I=S(t,d,"queued");l===0&&(a=I)}return M("queued",a)}try{Oe(e,{sessionId:t,agentType:i,sessionMode:k,managerConfig:X??null,workDir:ke,agentSessionId:r.agentSessionId??m??null,modelId:y,managerDefaultWorkerAgentType:j??null,managerDefaultWorkerModelId:F??null});let a=0;for(const[l,d]of A.entries()){const I=ae?S(t,d,"committed"):ge(t,d,"committed");l===0&&(a=I)}return $e(h,$??[]),_(e.client),M("committed",a)}catch(a){throw e.nativeFusion?.cancelManagedSend?.(u.id),a}},ie=a=>{r.journalRunId&&e.runJournal?.update(t,r.journalRunId,{deliveryMode:a?.deliveryMode==="external_owner_queue"?"external_owner_queue":"direct",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??u.id}`:null})},de=a=>{r.currentRunId||(r.nextEventSeq=0),ie(a),L(a);const l=a?.deliveryMode==="external_owner_queue"?"queued":"executing";let d=0;for(const[I,T]of A.entries()){const P=S(t,T,l);I===0&&(d=P)}return _(e.client),M(l,d)},Q=async(a,l)=>{e.nativeFusion?.cancelManagedSend?.(u.id);const d=he(i,a);if(console.error(`[chat.send] send failed reqId=${n.id} sessionId=${t} agentType=${R} workDir=${h} agentSessionId=${r.agentSessionId??m??""}: ${d}`),r.adapter.hasPendingAuthenticationRetry?.()===!0){l&&e.client.sendRes({type:"res",id:c,ok:!0,payload:M("queued",0)});return}e.sessions.delete(t),r.journalRunId&&(e.runJournal?.update(t,r.journalRunId,{phase:"terminal"}),e.runJournal?.remove(t,r.journalRunId),e.recoveryBarrier?.end(t),r.journalRunId=null);try{await C(e,r)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:t,message:d,runId:"",seq:0}}),!l){const T={id:`agent-error-${n.id}-${Date.now()}`,sessionId:t,role:"agent",ts:Date.now(),payload:d};ge(t,T),_(e.client)}l&&(e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:d}))};if(ee){try{se()}catch(d){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:`Durable message commit failed: ${d instanceof Error?d.message:String(d)}`});return}re(),oe();let a;try{(o?.source==="room_activation"||o?.source==="manager_event")&&(fe(e,r,w),s());const d=f.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(t,J),a=await ve(r.adapter,f.text,y,D,d,V,H),b({level:"info",sessionId:t,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}catch(d){if(g&&e.managerRuntime?.prepareWorkerTurnOrigin(t),r.adapter.hasPendingAuthenticationRetry?.()){let P=0;for(const[Z,G]of A.entries()){const Me=S(t,G,"queued");Z===0&&(P=Me)}_(e.client),await Q(d,!1),e.client.sendRes({type:"res",id:c,ok:!0,payload:M("queued",P)});return}let I=0;for(const[P,Z]of A.entries()){const G=S(t,Z,"failed");P===0&&(I=G)}_(e.client);const T=he(i,d);await Q(d,!1),e.client.sendRes({type:"res",id:c,ok:!0,payload:{...M("failed",I),failureMessage:T}});return}const l=de(a??void 0);e.client.sendRes({type:"res",id:c,ok:!0,payload:{...l,...f.localized?{localizedAttachments:!0}:{},...a?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:a.deliveryMode,queuedSubmissionId:a.queuedSubmissionId}:{}}}),b({level:"info",sessionId:t,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});return}let le;try{le=se()}catch(a){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:`Durable message commit failed: ${a instanceof Error?a.message:String(a)}`});return}re(),oe(),(o?.source==="room_activation"||o?.source==="manager_event")&&(me(r.adapter,t,i,x,U,w,o.managedAgentPolicy,e.managedRoomExecutor),fe(e,r,w),s()),de(),e.client.sendRes({type:"res",id:c,ok:!0,payload:{...le,...f.localized?{localizedAttachments:!0}:{}}}),b({level:"info",sessionId:t,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});const Ae=f.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(t,J),ve(r.adapter,f.text,y,D,Ae,V,H).then(a=>{if(a?.deliveryMode==="external_owner_queue"){ie(a),L(a);for(const l of A)S(t,l,"queued");_(e.client)}b({level:"info",sessionId:t,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}).catch(a=>{g&&e.managerRuntime?.prepareWorkerTurnOrigin(t),S(t,u,r.adapter.hasPendingAuthenticationRetry?.()?"queued":"failed"),_(e.client),Q(a,!1)})}const Le=2e4;async function Qe(e,n){let o;const s=new Promise(t=>{o=setTimeout(()=>t("timed_out"),n)});try{return await Promise.race([e.adapter.stop().then(()=>"stopped"),s])}finally{clearTimeout(o)}}async function yn(e,n,o=Le){const{sessionId:s,discardPending:t}=n.params,p=e.sessions.get(s);if(p){p.journalRunId&&e.runJournal?.markAborted(s,p.journalRunId),ye(e,p);let R;try{R=await Qe(p,o)}catch{e.client.sendRes({type:"res",id:n.id,ok:!1,error:"agent_stop_failed"});return}const W=p.currentRunId;Ve(e,s),R==="timed_out"&&(e.sessions.delete(s),p.adapter.removeAllListeners(),p.adapter.stop().catch(()=>{}),console.error(`[chat.abort] agent did not stop within ${o}ms; ended the run by force sessionId=${s}`),S(s,{id:`stop-forced-${W??n.id}`,sessionId:s,role:"agent",ts:Date.now(),payload:"AI \u6CA1\u6709\u54CD\u5E94\u201C\u505C\u6B62\u201D\u8BF7\u6C42\uFF0C\u795E\u5FF5\u5DF2\u5F3A\u5236\u7ED3\u675F\u8FD9\u4E00\u8F6E\u3002\u53EF\u4EE5\u76F4\u63A5\u53D1\u9001\u65B0\u6D88\u606F\u7EE7\u7EED\uFF1B\u5982\u679C\u53CD\u590D\u51FA\u73B0\uFF0C\u8BF7\u68C0\u67E5\u8FD9\u53F0\u7535\u8111\u4E0A\u8BE5 AI \u7684\u8FD0\u884C\u72B6\u6001\u3002"}),_(e.client)),e.activityPublisher?.publish(s,null)}t===!0&&e.chatQueue?.purgeSession(s),e.client.sendRes({type:"res",id:n.id,ok:!0})}export{Le as STOP_TIMEOUT_MS,yn as handleChatAbort,fn as handleChatSend,In as sendSessionUpdateEvent,Rn as validateManagedRoomExecutionContext};
|
|
1
|
+
import{buildUserMessagePayload as pe}from"@shennian/wire";import{createHash as be,randomUUID as xe}from"node:crypto";import me from"node:os";import{reportLog as b}from"../../log-reporter.js";import{getPersonalSyncIdentity as $e}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as Ce}from"../../room/managed-prompt-composer.js";import{parsePersonalAttachmentReferences as We}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as qe,markAttachmentReferences as je}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as S}from"../index-publisher.js";import{isTrustedManagerEvent as Oe}from"../manager-event-authority.js";import{mergeProjectedSessions as Fe}from"../projection.js";import{appendMessage as fe,getDaemonInstallationId as ye,getMessageDeliveryStatus as Be,listSessionRecords as Je,recordSession as ze,upsertMessage as w}from"../store.js";import{STEWARD_SYSTEM_PROMPT as Ue,isStewardSessionId as X,pinStewardWorkDir as ve}from"../../steward/session.js";import{configureAdapterForSession as he,maybeResolveClaudeImportedWorkDir as He,replaceManagedRoomContextToken as Ie,revokeManagedRoomContextToken as Re}from"./chat-adapter-config.js";import{resolveNativeSessionVisibility as Ne,sendSessionUpdateEvent as Ve,validateManagedRoomExecutionContext as Ke}from"./chat-context.js";import{getNativeSourceAgentType as Le}from"./chat-events.js";import{noteRunActivity as Qe}from"../run-stall.js";import{createActiveSession as Ye,disposeSession as C,prepareAdapterTurn as Ze,rememberProcessedReqId as Ge,sendAdapterTurn as _e}from"./chat-session-lifecycle.js";import{sendSessionUpdateEvent as Pn,validateManagedRoomExecutionContext as En}from"./chat-context.js";function Se(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function Xe(e,t){const o=e.sessions.get(t),i=o?.currentRunId;if(!o||!i)return;const n=o.nextEventSeq;e.runTextAcc.delete(`${t}:${i}`),Re(e,o),o.pendingTextEvent=null,Qe(t,o,!0),o.currentRunId=null,o.currentRunPhase=null,o.nextEventSeq=0,o.heartbeatTimer&&(clearInterval(o.heartbeatTimer),o.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:i,seq:n},seq:n,id:`agent-evt-${i}-${n}`}),o.journalRunId&&(e.runJournal?.remove(t,o.journalRunId),e.recoveryBarrier?.end(t),o.journalRunId=null),e.chatQueue?.noteTerminal(t)}async function wn(e,t,o){const i=o?t.params.managedRoomContextToken??null:null;let n=!1;try{await en(e,t,o,()=>{n=!0})}finally{i&&!n&&e.revokeManagedRoomContextToken?.(i)}}async function en(e,t,o,i){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}const{sessionId:n,text:p,agentType:R,sessionMode:W,managerConfig:ee,workDir:we,agentSessionId:m,modelId:y,modelSelectionSource:q,systemPrompt:ne,managerDefaultWorkerAgentType:j,managerDefaultWorkerModelId:O,reasoningEffort:F,clientMessageId:ke,sessionListProjection:Ae,waitForDispatch:ae,responseId:Me,managedRoomContextToken:k,suppressExternalOwnerEcho:Pe,queuedBatchItems:B,managedWorkerParentSessionId:g,managerWorkerRoomOrigin:J,managerWorkerNativeFull:z,managedRecoverySource:E}=t.params,c=Me||t.id,te=t.params;if((te.managerEventId||te.managerEventRoomOrigin||g||J||z||E)&&!Oe(t))throw new Error("manager_event_source_untrusted");Fe(Ae);const x=null,U="";if(o?.source==="room_activation"&&(Ke(o,ne,k),R==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(o?.source==="manager_event"){if(!o.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if(!k||!/^mrc_[A-Za-z0-9_-]{43}$/.test(k))throw new Error("managed_room_context_unavailable")}if(Ge(e,t.id),!n||!p){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:c,ok:!1,error:"sessionId and text are required"});return}if(e.recoveryBarrier?.isDispatchFrozen()){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:c,ok:!1,error:"upgrade_in_progress"});return}e.recoveryBarrier?.isRecovering(n)&&await e.recoveryBarrier.wait(n);const s=R,_=s==="manager"||W==="manager"?"manager":"standard";_==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(n,j??null,O??null);const D=(s==="claude"||s==="codex"||s==="workbuddy"||s==="pi-coding-agent"||s.startsWith("acp:"))&&typeof F=="string"&&F.trim()?F.trim():void 0,H=o?.source==="room_activation"?{source:"room_activation",...s==="pi-coding-agent"?{modelSelectionSource:q??(y?"request_model":"pi_default_model")}:{}}:o?.source==="manager_event"?{source:"managed_private",...s==="pi-coding-agent"?{modelSelectionSource:q??(y?"request_model":"pi_default_model")}:{}}:s==="pi-coding-agent"?{source:"personal",modelSelectionSource:q??(y?"request_model":"pi_default_model")}:void 0,oe=X(n)||t.params.sessionScope==="steward",h=oe?ve():e.resolvePath(He(s,we||me.homedir(),m)||me.homedir()),v=o?.managedAgentPolicy??(g?e.managerRuntime?.getManagedAgentPolicyForWorker(g,h)??null:null);if(g&&!z&&!v)throw new Error("managed_worker_permission_context_unavailable");const N=oe;if(N){if(_!=="standard")throw new Error("steward_session_mode_invalid");if(!X(n)&&Je().some(a=>a.sessionId===n))throw new Error("steward_scope_conflict");X(n)||ze({sessionId:n,agentType:s,workDir:ve(),sessionScope:"steward"})}const V=Ne(n,s,_,!!(v||g||o)),K=N?Ue:g&&v?Ce({requestedPolicy:v.requestedPolicy,enforcement:v.enforcement,resolvedWorkDir:v.adapterPolicy.workDir}):ne??null,Ee=h,$=We(t.params.attachments);$?.length&&qe(h,$);const f={text:p,attachments:$,localized:!1};let r=e.sessions.get(n);if(r){if(r.lastActiveAt=Date.now(),r.agentType!==s||(r.sessionMode??(r.agentType==="manager"?"manager":"standard"))!==_||r.workDir!==h||r.sessionScope==="steward"!==N||(r.nativeSessionVisibility??"user")!==V||JSON.stringify(r.externalChannel??null)!==JSON.stringify(x??null)){e.sessions.delete(n);try{await C(e,r)}catch{e.processedReqIds.delete(t.id)}r=void 0}else if(m&&r.agentSessionId!==m)try{await r.adapter.resume(m),r.agentSessionId=m}catch{e.sessions.delete(n);try{await C(e,r)}catch{e.processedReqIds.delete(t.id)}r=void 0}}if(!r)try{r=await Ye(e,n,s,_,h,m,x,U,v,V)}catch(a){const l=a instanceof Error&&a.message.startsWith("Unsupported agent:")?a.message:`Failed to start ${R}: ${a instanceof Error?a.message:String(a)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${n} agentType=${R} workDir=${h} agentSessionId=${m??""}: ${l}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:n,message:l,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:c,ok:!1,error:l});return}const u={id:ke??`user-${t.id}`,sessionId:n,role:"user",ts:Date.now(),payload:pe(f.text,f.attachments)},re=Array.isArray(B)&&B.length>0?B.map(a=>({id:a.clientMessageId??a.id,sessionId:n,role:E==="manager_event"||t.params.scheduledDelivery===!0?"agent":"user",ts:E==="manager_event"||o?.source==="room_activation"?Date.parse(a.createdAt):u.ts,payload:pe(a.text,a.attachments)})):null,A=re??[u],L=Be(n,u.id),se=L.state==="queued";if(L.state!=="not_found"&&!se){e.client.sendRes({type:"res",id:c,ok:!0,payload:L});return}if(ae){(o?.source==="room_activation"||o?.source==="manager_event")&&he(r.adapter,n,s,x,U,k,o.managedAgentPolicy,e.managedRoomExecutor);try{await Ze(r.adapter,f.text,y,D,f.attachments,K,H)}catch(a){e.processedReqIds.delete(t.id),e.sessions.delete(n),await C(e,r),e.client.sendRes({type:"res",id:c,ok:!1,error:a instanceof Error?a.message:String(a)});return}}b({level:"info",sessionId:n,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:s,sessionMode:_,managerConfig:ee??null,modelId:y,managerDefaultWorkerAgentType:j??null,managerDefaultWorkerModelId:O??null,reasoningEffort:D,...o?.source==="room_activation"?{managedRoomPromptSnapshot:o.managedRoomPromptSnapshot}:{}}});const M=(a,l)=>({...$e(ye()),sessionId:n,clientMessageId:u.id,messageId:u.id,deliveryState:a,bodyRevision:l,committedAt:new Date(u.ts).toISOString()}),Q=a=>{e.nativeFusion?.registerManagedSend({sessionId:n,agentType:s,sourceAgentType:Le(s,y),canonicalMessageId:u.id,sourceSessionKey:r.agentSessionId??m??null,text:f.text,managedEchoPolicy:a?.deliveryMode==="external_owner_queue"&&!Pe?"import_following":"suppress_following",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??u.id}`:null})},ie=()=>{r.currentRunId||(r.currentRunId=`dispatch:${t.id}`,r.currentRunPhase="thinking",r.nextEventSeq=0,r.lastProviderActivityAt=Date.now())},de=()=>{if(!e.runJournal||r.journalRunId)return;const a=`run:${xe()}`;e.runJournal.begin({daemonInstallationId:ye(),sessionId:n,runId:a,clientMessageId:u.id,agentType:s,sessionMode:_,workDir:h,agentSessionId:r.agentSessionId??m??null,modelId:y??null,reasoningEffort:D??null,nativeSessionVisibility:V,providerRunId:null,source:E==="manager_event"?"manager_event":o?.source==="room_activation"?E==="managed_direct"?"managed_direct":"managed_room":g?"manager_worker":"personal",parentSessionId:g??null,managerWorkerNativeFull:z===!0,roomContextRegistryKey:typeof t.params.roomContextRegistryKey=="string"?t.params.roomContextRegistryKey:null,activationCauseMessageId:typeof t.params.activationCauseMessageId=="string"?t.params.activationCauseMessageId:null,rootCauseMessageId:typeof t.params.rootCauseMessageId=="string"?t.params.rootCauseMessageId:null,activationSequence:typeof t.params.activationSequence=="string"?t.params.activationSequence:null,activationHopCount:typeof t.params.activationHopCount=="number"?t.params.activationHopCount:null,managedPolicyVersion:o?.source==="room_activation"?o.managedRoomPromptSnapshot.managedRoomPolicyVersion:o?.source==="manager_event"?o.managedRoomPolicyVersion:null,managedRuntimePolicyHash:(o?.source==="room_activation"?o.managedRoomPromptSnapshot.runtimePermissionPolicyHash:o?.source==="manager_event"?o.managedRuntimePolicyHash:null)??(v?be("sha256").update(JSON.stringify({requestedPolicy:v.requestedPolicy,enforcement:v.enforcement})).digest("hex"):null)}),r.journalRunId=a},le=()=>{if(Q(),se){let a=0;for(const[l,d]of A.entries()){const I=w(n,d,"queued");l===0&&(a=I)}return M("queued",a)}try{Ve(e,{sessionId:n,agentType:s,sessionMode:_,managerConfig:ee??null,workDir:Ee,agentSessionId:r.agentSessionId??m??null,modelId:y,managerDefaultWorkerAgentType:j??null,managerDefaultWorkerModelId:O??null});let a=0;for(const[l,d]of A.entries()){const I=re?w(n,d,"committed"):fe(n,d,"committed");l===0&&(a=I)}return je(h,$??[]),S(e.client),M("committed",a)}catch(a){throw e.nativeFusion?.cancelManagedSend?.(u.id),a}},ce=a=>{r.journalRunId&&e.runJournal?.update(n,r.journalRunId,{deliveryMode:a?.deliveryMode==="external_owner_queue"?"external_owner_queue":"direct",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??u.id}`:null})},ue=a=>{r.currentRunId||(r.nextEventSeq=0),ce(a),Q(a);const l=a?.deliveryMode==="external_owner_queue"?"queued":"executing";let d=0;for(const[I,T]of A.entries()){const P=w(n,T,l);I===0&&(d=P)}return S(e.client),M(l,d)},Y=async(a,l)=>{e.nativeFusion?.cancelManagedSend?.(u.id);const d=Se(s,a);if(console.error(`[chat.send] send failed reqId=${t.id} sessionId=${n} agentType=${R} workDir=${h} agentSessionId=${r.agentSessionId??m??""}: ${d}`),r.adapter.hasPendingAuthenticationRetry?.()===!0){l&&e.client.sendRes({type:"res",id:c,ok:!0,payload:M("queued",0)});return}e.sessions.delete(n),r.journalRunId&&(e.runJournal?.update(n,r.journalRunId,{phase:"terminal"}),e.runJournal?.remove(n,r.journalRunId),e.recoveryBarrier?.end(n),r.journalRunId=null);try{await C(e,r)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:n,message:d,runId:"",seq:0}}),!l){const T={id:`agent-error-${t.id}-${Date.now()}`,sessionId:n,role:"agent",ts:Date.now(),payload:d};fe(n,T),S(e.client)}l&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:c,ok:!1,error:d}))};if(ae){try{le()}catch(d){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:c,ok:!1,error:`Durable message commit failed: ${d instanceof Error?d.message:String(d)}`});return}de(),ie();let a;try{(o?.source==="room_activation"||o?.source==="manager_event")&&(Ie(e,r,k),i());const d=f.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(n,J),a=await _e(r.adapter,f.text,y,D,d,K,H),b({level:"info",sessionId:n,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(d){if(g&&e.managerRuntime?.prepareWorkerTurnOrigin(n),r.adapter.hasPendingAuthenticationRetry?.()){let P=0;for(const[Z,G]of A.entries()){const Te=w(n,G,"queued");Z===0&&(P=Te)}S(e.client),await Y(d,!1),e.client.sendRes({type:"res",id:c,ok:!0,payload:M("queued",P)});return}let I=0;for(const[P,Z]of A.entries()){const G=w(n,Z,"failed");P===0&&(I=G)}S(e.client);const T=Se(s,d);await Y(d,!1),e.client.sendRes({type:"res",id:c,ok:!0,payload:{...M("failed",I),failureMessage:T}});return}const l=ue(a??void 0);e.client.sendRes({type:"res",id:c,ok:!0,payload:{...l,...f.localized?{localizedAttachments:!0}:{},...a?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:a.deliveryMode,queuedSubmissionId:a.queuedSubmissionId}:{}}}),b({level:"info",sessionId:n,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}let ge;try{ge=le()}catch(a){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:c,ok:!1,error:`Durable message commit failed: ${a instanceof Error?a.message:String(a)}`});return}de(),ie(),(o?.source==="room_activation"||o?.source==="manager_event")&&(he(r.adapter,n,s,x,U,k,o.managedAgentPolicy,e.managedRoomExecutor),Ie(e,r,k),i()),ue(),e.client.sendRes({type:"res",id:c,ok:!0,payload:{...ge,...f.localized?{localizedAttachments:!0}:{}}}),b({level:"info",sessionId:n,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const De=f.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(n,J),_e(r.adapter,f.text,y,D,De,K,H).then(a=>{if(a?.deliveryMode==="external_owner_queue"){ce(a),Q(a);for(const l of A)w(n,l,"queued");S(e.client)}b({level:"info",sessionId:n,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(a=>{g&&e.managerRuntime?.prepareWorkerTurnOrigin(n),w(n,u,r.adapter.hasPendingAuthenticationRetry?.()?"queued":"failed"),S(e.client),Y(a,!1)})}const nn=2e4;async function an(e,t){let o;const i=new Promise(n=>{o=setTimeout(()=>n("timed_out"),t)});try{return await Promise.race([e.adapter.stop().then(()=>"stopped"),i])}finally{clearTimeout(o)}}async function kn(e,t,o=nn){const{sessionId:i,discardPending:n}=t.params,p=e.sessions.get(i);if(p){p.journalRunId&&e.runJournal?.markAborted(i,p.journalRunId),Re(e,p);let R;try{R=await an(p,o)}catch{e.client.sendRes({type:"res",id:t.id,ok:!1,error:"agent_stop_failed"});return}const W=p.currentRunId;Xe(e,i),R==="timed_out"&&(e.sessions.delete(i),p.adapter.removeAllListeners(),p.adapter.stop().catch(()=>{}),console.error(`[chat.abort] agent did not stop within ${o}ms; ended the run by force sessionId=${i}`),w(i,{id:`stop-forced-${W??t.id}`,sessionId:i,role:"agent",ts:Date.now(),payload:"AI \u6CA1\u6709\u54CD\u5E94\u201C\u505C\u6B62\u201D\u8BF7\u6C42\uFF0C\u795E\u5FF5\u5DF2\u5F3A\u5236\u7ED3\u675F\u8FD9\u4E00\u8F6E\u3002\u53EF\u4EE5\u76F4\u63A5\u53D1\u9001\u65B0\u6D88\u606F\u7EE7\u7EED\uFF1B\u5982\u679C\u53CD\u590D\u51FA\u73B0\uFF0C\u8BF7\u68C0\u67E5\u8FD9\u53F0\u7535\u8111\u4E0A\u8BE5 AI \u7684\u8FD0\u884C\u72B6\u6001\u3002"}),S(e.client)),e.activityPublisher?.publish(i,null)}n===!0&&e.chatQueue?.purgeSession(i),e.client.sendRes({type:"res",id:t.id,ok:!0})}export{nn as STOP_TIMEOUT_MS,kn as handleChatAbort,wn as handleChatSend,Pn as sendSessionUpdateEvent,En as validateManagedRoomExecutionContext};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash as w,randomUUID as S}from"node:crypto";import{buildStewardSayPayload as f}from"@shennian/wire";import{DaemonIpcError as y}from"../../daemon-ipc/protocol.js";import{STEWARD_SAY_MAX_CHARS as c,isStewardSessionId as m}from"../../steward/session.js";import{requestSessionIndexPublish as u}from"../index-publisher.js";import{appendMessage as l,getDaemonInstallationId as h,listSessionRecords as x,recordSession as g}from"../store.js";import{pinStewardWorkDir as _}from"../../steward/session.js";class i extends y{}function p(s,t,e,r="say",o){if(!m(t))throw new i("steward_session_required","This session is not a Shennian steward session.");const a=typeof e=="string"?e.trim():"";if(!a)throw new i("steward_say_empty","There is nothing to say.");if(a.length>c)throw new i("steward_say_too_long",`A message may hold at most ${c} characters.`);const n=o??`steward-say-${S()}`;l(t,{id:n,sessionId:t,role:"agent",ts:Date.now(),payload:f(a,r)});const d=s.sessions.get(t);return d&&r==="say"&&(d.stewardSay={count:(d.stewardSay?.count??0)+1}),u(s.client),{messageId:n}}function N(s,t,e,r){if(!m(t)||(e.stewardSay?.count??0)>0)return;const a=(e.stewardLastText?.trim()??"")||(r.state==="final"?"\u5DF2\u5904\u7406\uFF0C\u4F46\u6CA1\u6709\u53EF\u663E\u793A\u7684\u56DE\u590D\u3002":r.message?.trim()||"\u8FD9\u6B21\u5904\u7406\u6CA1\u6709\u5B8C\u6210\u3002");try{p(s,t,a.slice(0,c),"fallback",`steward-fallback-${r.runId||"run"}`)}catch{}}const T="\u6765\u81EA\u7EC8\u7AEF",I=new Set(["manager","pi"]);function $(s){const t=w("sha256").update(`${s}\0steward-terminal`).digest("hex"),e=(Number.parseInt(t[16],16)&3|8).toString(16);return`sess_${t.slice(0,8)}-${t.slice(8,12)}-4${t.slice(13,16)}-${e}${t.slice(17,20)}-${t.slice(20,32)}`}function W(s,t){const e=$(h()),r=x().find(o=>o.sessionId===e);if(r&&r.sessionScope!=="steward")throw new i("steward_scope_conflict","The terminal conversation id is already in use.");if(!r){const a=(s.getAgentSnapshot?.()??[]).find(n=>n.supportsStandardSession!==!1&&!I.has(n.type))?.type??"codex";g({sessionId:e,agentType:a,workDir:_(),sessionScope:"steward",sessionMode:"standard"}),l(e,{id:`steward-terminal-open-${e}`,sessionId:e,role:"user",ts:Date.now(),payload:T})}return{...p(s,e,t),sessionId:e}}export{T as STEWARD_TERMINAL_TITLE,i as StewardSayError,N as commitStewardFallback,p as commitStewardSay,W as commitTerminalStewardSay,$ as stewardTerminalSessionId};
|