@robota-sdk/agent-transport-acp 3.0.0-beta.96 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,29 @@
1
1
  import { Readable, Writable } from "node:stream";
2
- import { ISessionEvents, ISessionPromptResolution, ISessionStatusRead, ISessionTurnControl, ISessionTurnSubmission, ISubmitOptions, TDriverId } from "@robota-sdk/agent-interface-session";
2
+ import { ISessionCommands, ISessionConversationRead, ISessionEvents, ISessionPromptResolution, ISessionStatusRead, ISessionTurnControl, ISessionTurnSubmission, ISubmitOptions, TDriverId } from "@robota-sdk/agent-interface-session";
3
3
  import { ITransportAdapter } from "@robota-sdk/agent-interface-transport";
4
4
  //#region src/acp-types.d.ts
5
5
  /**
6
6
  * The session roles ACP uses: submit and abort a turn, stream its events, answer its permission
7
- * prompts, and name the session. A full interactive session is assignable; nothing else is required.
7
+ * prompts, and name the session. Optional catalog reads let the editor offer commands; optional
8
+ * history reads enable a host load port to replay a stored session. A full interactive session is assignable.
8
9
  */
9
- interface IAcpTransportSession extends ISessionTurnSubmission, ISessionTurnControl, ISessionEvents, ISessionPromptResolution, ISessionStatusRead {
10
+ interface IAcpTransportSession extends ISessionTurnSubmission, ISessionTurnControl, ISessionEvents, ISessionPromptResolution, ISessionStatusRead, Partial<Pick<ISessionCommands, 'listCommands' | 'listSkills'>>, Partial<Pick<ISessionConversationRead, 'getMessagesDisplay'>> {
10
11
  /**
11
12
  * Resolves once the session can name itself, and rejects if it failed to start. A session that
12
13
  * builds itself in the background (`InteractiveSession`) has no id before then, so `session/new`
13
- * waits on this. A session that is ready at construction can omit it.
14
+ * and `session/load` wait on this. A session that is ready at construction can omit it.
14
15
  */
15
16
  whenInitialized?(): Promise<void>;
16
17
  }
18
+ /**
19
+ * The editor's pathname file system, as the editor offered it (`clientCapabilities.fs`): a method
20
+ * it did not offer is absent. Paths are absolute. ACP v1 does not bind the path to stable rooted
21
+ * authority, so contained host file tools must not treat this as `IEditorFileAccess.pathAuthority`.
22
+ */
23
+ interface IAcpEditorFileAccess {
24
+ readTextFile?(path: string, signal?: AbortSignal): Promise<string>;
25
+ writeTextFile?(path: string, content: string, signal?: AbortSignal): Promise<void>;
26
+ }
17
27
  /** The host's answer to whether the workspace may serve an editor; a refusal carries what to tell the user. */
18
28
  type TAcpWorkspaceAdmission = {
19
29
  readonly admitted: true;
@@ -21,6 +31,13 @@ type TAcpWorkspaceAdmission = {
21
31
  readonly admitted: false;
22
32
  readonly message: string;
23
33
  };
34
+ /** The host's answer to a `session/load`; a refusal carries what to tell the editor. */
35
+ type TAcpSessionLoad = {
36
+ readonly loaded: true;
37
+ } | {
38
+ readonly loaded: false;
39
+ readonly message: string;
40
+ };
24
41
  interface IAcpTransportOptions {
25
42
  /** Process-owned byte streams carrying newline-delimited JSON-RPC: the editor writes `input`, reads `output`. */
26
43
  readonly input: Readable;
@@ -30,9 +47,9 @@ interface IAcpTransportOptions {
30
47
  readonly name: string;
31
48
  readonly version: string;
32
49
  };
33
- /** The directory the attached session is rooted at; `session/new` must name this directory. */
50
+ /** The directory the attached session is rooted at; `session/new` and `session/load` must name it. */
34
51
  readonly cwd: string;
35
- /** The host's trust check, asked when the editor creates the session. */
52
+ /** The host's trust check, asked when the editor creates or loads the session. */
36
53
  readonly admitWorkspace: () => TAcpWorkspaceAdmission;
37
54
  /**
38
55
  * Runs a `/command` prompt and returns the text to show. Supplied by the host because command
@@ -41,6 +58,21 @@ interface IAcpTransportOptions {
41
58
  * cancels the prompt or the connection closes; pass it on so what the command starts stops with it.
42
59
  */
43
60
  readonly executeCommand?: (text: string, signal: AbortSignal) => Promise<string>;
61
+ /**
62
+ * Makes the stored session `sessionId` current, so that the object this transport was attached to
63
+ * answers for it from then on (the host attaches a forwarder, such as a session slot, whose current
64
+ * session the port replaces). It must admit only a session rooted in the served directory, and may
65
+ * refuse with a message for the editor. Supplied by the host because building a session from
66
+ * storage is not this package's; without it or the session's history read, `session/load` is not offered.
67
+ */
68
+ readonly loadSession?: (sessionId: string) => Promise<TAcpSessionLoad>;
69
+ /**
70
+ * Receives the editor's file system after a new or loaded session binds, when the editor offers reads or
71
+ * writes, and `undefined` when the transport stops. The host decides which of its tools use it;
72
+ * they must apply their own path and permission checks first, because the editor checks nothing
73
+ * for the agent. Never called for an editor that offers no file system.
74
+ */
75
+ readonly onEditorFileAccess?: (access: IAcpEditorFileAccess | undefined) => void;
44
76
  /** Attribution recorded on turns and prompt answers submitted through this transport. Default `'owner'`. */
45
77
  readonly driverId?: TDriverId;
46
78
  /** Usage surface recorded on turns. Default `'unknown'` (no editor value exists). */
@@ -59,5 +91,5 @@ interface IAcpTransport extends ITransportAdapter<IAcpTransportSession> {
59
91
  //#region src/acp-transport.d.ts
60
92
  declare function createAcpTransport(options: IAcpTransportOptions): IAcpTransport;
61
93
  //#endregion
62
- export { type IAcpTransport, type IAcpTransportOptions, type IAcpTransportSession, type TAcpWorkspaceAdmission, createAcpTransport };
94
+ export { type IAcpEditorFileAccess, type IAcpTransport, type IAcpTransportOptions, type IAcpTransportSession, type TAcpSessionLoad, type TAcpWorkspaceAdmission, createAcpTransport };
63
95
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/acp-types.ts","../../src/acp-transport.ts"],"mappings":";;;;;;;;UAkBiB,6BAEb,wBACA,qBACA,gBACA,0BACA;;;;;;EAMF,oBAAoB;;;KAIV;WACC;;WAA8B;WAA0B;;UAEpD;;WAEN,OAAO;WACP,QAAQ;;WAER;aAAsB;aAAuB;;;WAE7C;;WAEA,sBAAsB;;;;;;;WAOtB,kBAAkB,cAAc,QAAQ,gBAAgB;;WAExD,WAAW;;WAEX,UAAU;;;;;WAKV,mBAAmB,OAAO,OAAO;;UAG3B,sBAAsB,kBAAkB;;EAEvD,gBAAgB;;;;iBChCF,mBAAmB,SAAS,uBAAuB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/acp-types.ts","../../src/acp-transport.ts"],"mappings":";;;;;;;;;UAqBiB,6BAEb,wBACA,qBACA,gBACA,0BACA,oBACA,QAAQ,KAAK,mDACb,QAAQ,KAAK;;;;;;EAMf,oBAAoB;;;;;;;UAQL;EACf,cAAc,cAAc,SAAS,cAAc;EACnD,eAAe,cAAc,iBAAiB,SAAS,cAAc;;;KAI3D;WACC;;WAA8B;WAA0B;;;KAGzD;WACC;;WAA4B;WAAwB;;UAEhD;;WAEN,OAAO;WACP,QAAQ;;WAER;aAAsB;aAAuB;;;WAE7C;;WAEA,sBAAsB;;;;;;;WAOtB,kBAAkB,cAAc,QAAQ,gBAAgB;;;;;;;;WAQxD,eAAe,sBAAsB,QAAQ;;;;;;;WAO7C,sBAAsB,QAAQ;;WAE9B,WAAW;;WAEX,UAAU;;;;;WAKV,mBAAmB,OAAO,OAAO;;UAG3B,sBAAsB,kBAAkB;;EAEvD,gBAAgB;;;;iBClEF,mBAAmB,SAAS,uBAAuB"}
@@ -1,7 +1,7 @@
1
- import{Readable as e}from"node:stream";import{PROTOCOL_VERSION as t,RequestError as n,agent as r,ndJsonStream as i}from"@agentclientprotocol/sdk";import{attachStdioCarrier as a}from"@robota-sdk/agent-transport/node";import{realpathSync as o}from"node:fs";import{isAbsolute as s,resolve as c}from"node:path";import{OWNER_DRIVER_ID as l,isTurnNotRunError as u}from"@robota-sdk/agent-interface-session";import{permissionSummaryArgument as d}from"@robota-sdk/agent-transport";function f(e){let t=Math.max(0,...[...e.matchAll(/`+/g)].map(e=>e[0].length));return"`".repeat(Math.max(3,t+1))}function p(e){let t=``,n=[],r=[];for(let i of e)if(i.type===`text`)t+=i.text;else if(i.type===`resource`&&`text`in i.resource){let e=f(i.resource.text);n.push(`Context from ${i.resource.uri}:\n${e}\n${i.resource.text}\n${e}`)}else i.type===`resource_link`&&r.push(`- ${i.uri}`);return r.length>0&&n.push(`Referenced resources:\n${r.join(`
1
+ import{Readable as e}from"node:stream";import{PROTOCOL_VERSION as t,RequestError as n,agent as r,ndJsonStream as i}from"@agentclientprotocol/sdk";import{attachStdioCarrier as a}from"@robota-sdk/agent-transport/node";import{realpathSync as o}from"node:fs";import{isAbsolute as s,resolve as c}from"node:path";import{OWNER_DRIVER_ID as l,isTurnNotRunError as u}from"@robota-sdk/agent-interface-session";import{permissionSummaryArgument as d}from"@robota-sdk/agent-transport";function f(e){return e===void 0?void 0:{cancellationSignal:e}}function p(e){if(!(e instanceof n))return e instanceof Error?e:Error(String(e));let t=e.data,r=typeof t==`object`&&t&&`details`in t&&typeof t.details==`string`?t.details:void 0;return Error(r===void 0?e.message:`${e.message}: ${r}`)}function m(e){if(!s(e))throw Error(`the editor is given absolute paths only: ${e}`)}function h(e,t,n){let r=e?.readTextFile===!0,i=e?.writeTextFile===!0;if(!r&&!i)return;let a=()=>{let e=n();if(e===void 0)throw Error(`the editor has no session to read or write for`);return e};return{...r?{readTextFile:async(e,n)=>{m(e);try{return(await t.request(`fs/read_text_file`,{sessionId:a(),path:e},f(n))).content}catch(e){throw p(e)}}}:{},...i?{writeTextFile:async(e,n,r)=>{m(e);try{await t.request(`fs/write_text_file`,{sessionId:a(),path:e,content:n},f(r))}catch(e){throw p(e)}}}:{}}}function g(e){let t=Math.max(0,...[...e.matchAll(/`+/g)].map(e=>e[0].length));return"`".repeat(Math.max(3,t+1))}function _(e){let t=``,n=[],r=[];for(let i of e)if(i.type===`text`)t+=i.text;else if(i.type===`resource`&&`text`in i.resource){let e=g(i.resource.text);n.push(`Context from ${i.resource.uri}:\n${e}\n${i.resource.text}\n${e}`)}else i.type===`resource_link`&&r.push(`- ${i.uri}`);return r.length>0&&n.push(`Referenced resources:\n${r.join(`
2
2
  `)}`),{typed:t,full:n.length===0?t:`${t}\n\n${n.join(`
3
3
 
4
- `)}`}}const m={Read:`read`,Glob:`search`,Grep:`search`,Edit:`edit`,Write:`edit`,Shell:`execute`,Bash:`execute`,WebFetch:`fetch`,WebSearch:`fetch`};function h(e){return m[e]??`other`}function g(e,t){let n=t?.replace(/\s+/g,` `).trim();return n?`${e}: ${n.length>120?`${n.slice(0,119)}…`:n}`:e}function _(e,t){let n=t?.trim();return n?`${e}: ${n}`:e}function v(e){return g(e.toolName,e.displayPath??e.firstArg)}function y(e){return e.result===`success`?`completed`:`failed`}const b={allowOnce:`allow-once`,allowSession:`allow-session`,allowProject:`allow-project`,reject:`reject`};function x(e){return[{optionId:b.allowOnce,name:`Allow once`,kind:`allow_once`},{optionId:b.allowSession,name:`Allow for this session`,kind:`allow_always`},...e?[{optionId:b.allowProject,name:`Allow for this project`,kind:`allow_always`}]:[],{optionId:b.reject,name:`Reject`,kind:`reject_once`}]}function S(e){let t=_(e.toolName,e.diffFile??d(e.toolArgs));return e.requester?`${e.requester.label}: ${t}`:t}function C(e){return e.cwd===void 0||`cwd`in e.toolArgs?e.toolArgs:{...e.toolArgs,cwd:e.cwd}}const w=/^@@ /;function T(e){let t=[],n=[],r=`context`;for(let i of e){if(i.type===`hunk`){if(w.test(i.text))continue;r!==`add`&&t.push(i.text),r!==`remove`&&n.push(i.text);continue}r=i.type,i.type!==`add`&&t.push(i.text),i.type!==`remove`&&n.push(i.text)}return{...t.length>0?{oldText:t.join(`
4
+ `)}`}}const v={Read:`read`,Glob:`search`,Grep:`search`,Edit:`edit`,Write:`edit`,Shell:`execute`,Bash:`execute`,WebFetch:`fetch`,WebSearch:`fetch`};function y(e){return v[e]??`other`}function b(e,t){let n=t?.replace(/\s+/g,` `).trim();return n?`${e}: ${n.length>120?`${n.slice(0,119)}…`:n}`:e}function x(e,t){let n=t?.trim();return n?`${e}: ${n}`:e}function S(e){return b(e.toolName,e.displayPath??e.firstArg)}function C(e){return e.result===`success`?`completed`:`failed`}function w(e){let t=[],n=new Set,r=(e,r,i)=>{n.has(e)||(n.add(e),t.push({name:e,description:r,...i?{input:{hint:i}}:{}}))};for(let t of e.listCommands?.()??[])t.runner===`runtime`&&r(t.name,t.description,t.argumentHint);for(let t of e.listSkills?.()??[])t.userInvocable&&r(t.name,t.description,t.argumentHint);return t}function T(e,t,n){for(let r of e){if(r.type===`text`){if(r.content===``)continue;t.send({sessionUpdate:r.role===`user`?`user_message_chunk`:`agent_message_chunk`,content:{type:`text`,text:r.content}});continue}let{tool:e}=r;if(e.internal)continue;let i=n(e.executionId);t.send({sessionUpdate:`tool_call`,toolCallId:i,title:S(e),kind:y(e.toolName),status:C(e)})}}const E={allowOnce:`allow-once`,allowSession:`allow-session`,allowProject:`allow-project`,reject:`reject`};function D(e){return[{optionId:E.allowOnce,name:`Allow once`,kind:`allow_once`},{optionId:E.allowSession,name:`Allow for this session`,kind:`allow_always`},...e?[{optionId:E.allowProject,name:`Allow for this project`,kind:`allow_always`}]:[],{optionId:E.reject,name:`Reject`,kind:`reject_once`}]}function O(e){let t=x(e.toolName,e.diffFile??d(e.toolArgs));return e.requester?`${e.requester.label}: ${t}`:t}function k(e){return e.cwd===void 0||`cwd`in e.toolArgs?e.toolArgs:{...e.toolArgs,cwd:e.cwd}}const A=/^@@ /;function j(e){let t=[],n=[],r=`context`;for(let i of e){if(i.type===`hunk`){if(A.test(i.text))continue;r!==`add`&&t.push(i.text),r!==`remove`&&n.push(i.text);continue}r=i.type,i.type!==`add`&&t.push(i.text),i.type!==`remove`&&n.push(i.text)}return{...t.length>0?{oldText:t.join(`
5
5
  `)}:{},newText:n.join(`
6
- `)}}function E(e,t,n){let r=e.diffFile===void 0?void 0:c(n,e.diffFile);return{toolCallId:t,title:S(e),kind:h(e.toolName),status:`pending`,rawInput:C(e),...r!==void 0&&e.diffLines!==void 0?{content:[{type:`diff`,path:r,...T(e.diffLines)}]}:{},...r===void 0?{}:{locations:[{path:r}]}}}function D(e,t){if(e.outcome!==`selected`||!t.some(t=>t.optionId===e.optionId))return!1;switch(e.optionId){case b.allowOnce:return!0;case b.allowSession:return`allow-session`;case b.allowProject:return`allow-project`;default:return!1}}function O(e,t){let n=Object.keys(t);for(let r of n)e.on(r,t[r]);return()=>{for(let r of n)e.off(r,t[r])}}function k(e){let{updates:t}=e,n=[],r=new Map,i=new Map,a=!1,o=e=>{if(e===void 0)return;let t=r.get(e);return r.delete(e),t},s=(e,n)=>t.send({sessionUpdate:`tool_call_update`,toolCallId:e,status:n?`completed`:`failed`});return{start(r){if(r.internal)return;let i=o(r.executionId),a=r.executionId??e.nextToolCallId();n.push({id:a,toolName:r.toolName,executionId:r.executionId});let s={toolCallId:a,title:v(r),kind:h(r.toolName),status:`in_progress`};i===void 0?t.send({sessionUpdate:`tool_call`,...s}):t.send({sessionUpdate:`tool_call_update`,...s,rawInput:i.rawInput})},end(r){if(r.internal)return;let i=y(r),a=n.findIndex(e=>r.executionId===void 0?e.executionId===void 0&&e.toolName===r.toolName:e.executionId===r.executionId);if(a>=0){let[e]=n.splice(a,1);t.send({sessionUpdate:`tool_call_update`,toolCallId:e.id,status:i});return}if(o(r.executionId)!==void 0){t.send({sessionUpdate:`tool_call_update`,toolCallId:r.executionId,status:i});return}t.send({sessionUpdate:`tool_call`,toolCallId:r.executionId??e.nextToolCallId(),title:v(r),kind:h(r.toolName),status:i})},requested(t){let{executionId:a}=t;if(a===void 0){let n=E(t,`permission-${t.id}`,e.cwd);return i.set(t.id,n.toolCallId),n}let o=E(t,a,e.cwd);return n.some(e=>e.executionId===a)||r.set(a,{rawInput:o.rawInput,background:t.requester!==void 0}),o},answered(e,t){if(e.executionId!==void 0){let n=r.get(e.executionId);n!==void 0&&(n.allowed=t);return}let n=i.get(e.id);n===void 0||a||(i.delete(e.id),s(n,t))},finish(){a=!0;for(let[e,t]of r)s(e,t.background&&t.allowed===!0);for(let e of i.values())s(e,!1);r.clear(),i.clear()}}}const A={stopReason:`cancelled`},j={stopReason:`end_turn`};function M(e){return e instanceof Error?e:Error(String(e))}async function N(e,t){let{session:n,sessionId:r,client:i,updates:a}=e,o=k({updates:a,nextToolCallId:e.nextToolCallId,cwd:e.cwd}),s=!1,c=O(n,{text_delta:e=>{e!==``&&(s=!0,a.text(e))},thinking_delta:e=>{e!==``&&a.thought(e)},tool_start:o.start,tool_end:o.end,permission_request:t=>{let a=x(t.canPersistProjectPermission===!0),s=o.requested(t);(async()=>{let c=!1;try{c=D((await i.request(`session/request_permission`,{sessionId:r,toolCall:s,options:a})).outcome,a)}catch(t){e.report(M(t),`session/request_permission`)}o.answered(t,c!==!1);try{n.resolvePermission(t.id,c,e.driverId)}catch(t){e.report(M(t),`resolvePermission`)}})()}});try{let n=await t.run();return e.wasCancelled()||e.signal.aborted||n.interrupted?A:(!s&&n.reply!==``&&a.text(n.reply),j)}catch(n){return e.wasCancelled()||e.signal.aborted||t.isCancellation?.(n)===!0?A:t.fail(n)}finally{c(),o.finish(),await a.drained()}}function P(e,t){return N(e,{async run(){let n=await(await e.session.submit(t,void 0,void 0,{signal:e.signal,driverId:e.driverId,...e.surface===void 0?{}:{surface:e.surface}})).completed;return{interrupted:n.interrupted===!0,reply:n.response}},isCancellation:e=>u(e)&&e.reason===`cancelled`,fail(t){throw e.report(M(t),`turn`),new n(-32603,u(t)?`Turn was not run`:`Turn failed`)}})}function F(e,t,r){return N(e,{async run(){return{interrupted:!1,reply:await r(t,e.signal)}},fail(t){throw e.report(M(t),`command`),new n(-32603,`Command failed`)}})}function I(e,t,n){let r=Promise.resolve(),i=!1,a=a=>{r=r.then(async()=>{if(!i)try{await e.notify(`session/update`,{sessionId:t,update:a})}catch(e){i=!0,n(e instanceof Error?e:Error(String(e)),`session/update`)}})};return{send:a,text:e=>a({sessionUpdate:`agent_message_chunk`,content:{type:`text`,text:e}}),thought:e=>a({sessionUpdate:`agent_thought_chunk`,content:{type:`text`,text:e}}),drained:()=>r}}const L=()=>Promise.resolve(`Slash commands are not available in this host.`);function R(e,t){if(c(e)===c(t))return!0;try{return o(e)===o(t)}catch{return!1}}function z(e,i){let a=i.driverId??l,o=(e,t)=>i.onDeliveryError?.(e,t),c,u=!1,d,f=0;return{app:r({name:i.agentInfo.name}).onRequest(`initialize`,()=>({protocolVersion:t,agentCapabilities:{loadSession:!1,promptCapabilities:{image:!1,audio:!1,embeddedContext:!0}},agentInfo:{name:i.agentInfo.name,version:i.agentInfo.version}})).onRequest(`session/new`,async({params:t})=>{if(c!==void 0||u)throw n.invalidParams(void 0,`one session per connection`);if(!s(t.cwd)||!R(t.cwd,i.cwd))throw n.invalidParams(void 0,`cwd must be the directory this agent was started in: ${i.cwd}`);if(t.mcpServers.length>0)throw n.invalidParams(void 0,`mcpServers are not accepted by this agent`);if((t.additionalDirectories??[]).length>0)throw n.invalidParams(void 0,`additionalDirectories are not accepted by this agent`);let r=i.admitWorkspace();if(!r.admitted)throw new n(-32001,r.message,{reason:`workspace-not-trusted`});u=!0;try{await e.whenInitialized?.(),c=e.getStatusSnapshot().sessionId}catch(e){throw o(e instanceof Error?e:Error(String(e)),`session/new`),new n(-32603,`The session could not be started`)}finally{u=!1}return{sessionId:c}}).onRequest(`session/prompt`,async({params:t,signal:r,client:s})=>{if(t.sessionId!==c)throw n.invalidParams(void 0,`unknown session`);if(d!==void 0)throw n.invalidParams(void 0,`a prompt is already running`);let l=p(t.prompt);if(l.full.trim()===``)throw n.invalidParams(void 0,`the prompt has no text`);let u=new AbortController,m=!1;d={cancel(){m=!0,u.abort(),e.abort()}};let h={session:e,sessionId:t.sessionId,client:s,updates:I(s,t.sessionId,o),signal:AbortSignal.any([r,u.signal]),driverId:a,surface:i.surface??`unknown`,cwd:i.cwd,nextToolCallId:()=>`tool-${f+=1}`,wasCancelled:()=>m,report:o};try{return l.typed.trimStart().startsWith(`/`)?await F(h,l.typed,i.executeCommand??L):await P(h,l.full)}finally{d=void 0}}).onNotification(`session/cancel`,({params:e})=>{e.sessionId===c&&d?.cancel()}),cancelActive:()=>d?.cancel()}}function B(e){return new WritableStream({write:t=>new Promise((n,r)=>{e.write(t,e=>e?r(e):n())})})}function V(t){let n=null,r=!1,o=null,s=null,c=null,l=null,u=null,d=null,f=Promise.resolve(),p=()=>{f=new Promise((e,t)=>{u=e,d=t}),f.catch(()=>void 0)},m=()=>{try{l?.(),c?.close()}finally{s?.release(),s=null,c=null,l=null}},h=e=>Object.assign(Error(`ACP transport ${e}.`),{name:`TransportLifecycleError`,code:e,transportName:`acp`});return{name:`acp`,lifecycle:Object.freeze({kind:`service`}),attach(e){if(r||o)throw h(`already-started`);n=e,p()},async start(){if(!n)throw h(`not-attached`);if(r||o)throw h(`already-started`);r=!0;try{let r=z(n,t);l=r.cancelActive;let o=a(t.input,t.output,{onInputClose:()=>u?.(),onOutputClose:()=>u?.(),onOutputError:e=>d?.(e)});s=o;let f=r.app.connect(i(B(t.output),e.toWeb(o.bridge)));c=f,f.closed.then(()=>u?.())}catch(e){throw m(),r=!1,e}},stop(){return o||(o=(async()=>{try{m()}finally{n=null,r=!1,u?.()}})().finally(()=>{o=null}),o)},waitForClose(){return f}}}export{V as createAcpTransport};
6
+ `)}}function M(e,t,n){let r=e.diffFile===void 0?void 0:c(n,e.diffFile);return{toolCallId:t,title:O(e),kind:y(e.toolName),status:`pending`,rawInput:k(e),...r!==void 0&&e.diffLines!==void 0?{content:[{type:`diff`,path:r,...j(e.diffLines)}]}:{},...r===void 0?{}:{locations:[{path:r}]}}}function N(e,t){if(e.outcome!==`selected`||!t.some(t=>t.optionId===e.optionId))return!1;switch(e.optionId){case E.allowOnce:return!0;case E.allowSession:return`allow-session`;case E.allowProject:return`allow-project`;default:return!1}}function P(e,t){let n=Object.keys(t);for(let r of n)e.on(r,t[r]);return()=>{for(let r of n)e.off(r,t[r])}}function F(e){let{updates:t}=e,n=[],r=new Map,i=new Map,a=!1,o=e=>{if(e===void 0)return;let t=r.get(e);return r.delete(e),t},s=(e,n)=>t.send({sessionUpdate:`tool_call_update`,toolCallId:e,status:n?`completed`:`failed`});return{start(r){if(r.internal)return;let i=o(r.executionId),a=i?.id??e.nextToolCallId(r.executionId);n.push({id:a,toolName:r.toolName,executionId:r.executionId});let s={toolCallId:a,title:S(r),kind:y(r.toolName),status:`in_progress`};i===void 0?t.send({sessionUpdate:`tool_call`,...s}):t.send({sessionUpdate:`tool_call_update`,...s,rawInput:i.rawInput})},end(r){if(r.internal)return;let i=C(r),a=n.findIndex(e=>r.executionId===void 0?e.executionId===void 0&&e.toolName===r.toolName:e.executionId===r.executionId);if(a>=0){let[e]=n.splice(a,1);t.send({sessionUpdate:`tool_call_update`,toolCallId:e.id,status:i});return}let s=o(r.executionId);if(s!==void 0){t.send({sessionUpdate:`tool_call_update`,toolCallId:s.id,status:i});return}t.send({sessionUpdate:`tool_call`,toolCallId:e.nextToolCallId(r.executionId),title:S(r),kind:y(r.toolName),status:i})},requested(t){let{executionId:a}=t;if(a===void 0){let n=M(t,e.nextToolCallId(`permission-${t.id}`),e.cwd);return i.set(t.id,n.toolCallId),n}let o=n.find(e=>e.executionId===a),s=r.get(a),c=o?.id??s?.id??e.nextToolCallId(a),l=M(t,c,e.cwd);return o===void 0&&s===void 0&&r.set(a,{id:c,rawInput:l.rawInput,background:t.requester!==void 0}),l},answered(e,t){if(e.executionId!==void 0){let n=r.get(e.executionId);n!==void 0&&(n.allowed=t);return}let n=i.get(e.id);n===void 0||a||(i.delete(e.id),s(n,t))},finish(){a=!0;for(let e of r.values())s(e.id,e.background&&e.allowed===!0);for(let e of i.values())s(e,!1);r.clear(),i.clear()}}}const I={stopReason:`cancelled`},L={stopReason:`end_turn`};function R(e){return e instanceof Error?e:Error(String(e))}async function z(e,t){let{session:n,sessionId:r,client:i,updates:a}=e,o=F({updates:a,nextToolCallId:e.nextToolCallId,cwd:e.cwd}),s=!1,c=P(n,{text_delta:e=>{e!==``&&(s=!0,a.text(e))},thinking_delta:e=>{e!==``&&a.thought(e)},tool_start:o.start,tool_end:o.end,permission_request:t=>{let a=D(t.canPersistProjectPermission===!0),s=o.requested(t);(async()=>{let c=!1;try{c=N((await i.request(`session/request_permission`,{sessionId:r,toolCall:s,options:a})).outcome,a)}catch(t){e.report(R(t),`session/request_permission`)}o.answered(t,c!==!1);try{n.resolvePermission(t.id,c,e.driverId)}catch(t){e.report(R(t),`resolvePermission`)}})()}});try{let n=await t.run();return e.wasCancelled()||e.signal.aborted||n.interrupted?I:(!s&&n.reply!==``&&a.text(n.reply),L)}catch(n){return e.wasCancelled()||e.signal.aborted||t.isCancellation?.(n)===!0?I:t.fail(n)}finally{c(),o.finish(),await a.drained()}}function B(e,t){return z(e,{async run(){let n=await(await e.session.submit(t,void 0,void 0,{signal:e.signal,driverId:e.driverId,...e.surface===void 0?{}:{surface:e.surface}})).completed;return{interrupted:n.interrupted===!0,reply:n.response}},isCancellation:e=>u(e)&&e.reason===`cancelled`,fail(t){throw e.report(R(t),`turn`),new n(-32603,u(t)?`Turn was not run`:`Turn failed`)}})}function V(e,t,r){return z(e,{async run(){return{interrupted:!1,reply:await r(t,e.signal)}},fail(t){throw e.report(R(t),`command`),new n(-32603,`Command failed`)}})}function H(e,t,n){let r=Promise.resolve(),i=!1,a=a=>{r=r.then(async()=>{if(!i)try{await e.notify(`session/update`,{sessionId:t,update:a})}catch(e){i=!0,n(e instanceof Error?e:Error(String(e)),`session/update`)}})};return{send:a,text:e=>a({sessionUpdate:`agent_message_chunk`,content:{type:`text`,text:e}}),thought:e=>a({sessionUpdate:`agent_thought_chunk`,content:{type:`text`,text:e}}),drained:()=>r}}const U=-32603,W=()=>Promise.resolve(`Slash commands are not available in this host.`);function G(e,t){if(c(e)===c(t))return!0;try{return o(e)===o(t)}catch{return!1}}function K(e){return e instanceof Error?e:Error(String(e))}function q(e,i){let a=i.driverId??l,o=(e,t)=>i.onDeliveryError?.(e,t),c,u=!1,d,f=0,p=new Set,m=e=>{let t=e;for(;t===void 0||p.has(t);)t=`tool-${f+=1}`;return p.add(t),t},g,v=e=>{if(!s(e.cwd)||!G(e.cwd,i.cwd))throw n.invalidParams(void 0,`cwd must be the directory this agent was started in: ${i.cwd}`);if(e.mcpServers.length>0)throw n.invalidParams(void 0,`mcpServers are not accepted by this agent`);if((e.additionalDirectories??[]).length>0)throw n.invalidParams(void 0,`additionalDirectories are not accepted by this agent`);let t=i.admitWorkspace();if(!t.admitted)throw new n(-32001,t.message,{reason:`workspace-not-trusted`})},y=async t=>{try{return await e.whenInitialized?.(),e.getStatusSnapshot().sessionId}catch(e){throw o(K(e),t),new n(U,`The session could not be started`)}},b=(t,n)=>{if(e.listCommands===void 0&&e.listSkills===void 0)return;let r;try{r=w(e)}catch(e){o(K(e),`available_commands_update`);return}let i=JSON.stringify(r);i!==g&&(g=i,t.notify(`session/update`,{sessionId:n,update:{sessionUpdate:`available_commands_update`,availableCommands:r}}).catch(e=>o(K(e),`available_commands_update`)))},x=(e,t)=>{setImmediate(()=>b(e,t))},S,C=!1,E=e=>{let t=h(S,e,()=>c);t!==void 0&&(C=!0,i.onEditorFileAccess?.(t))};return{app:r({name:i.agentInfo.name}).onRequest(`initialize`,({params:n})=>(S=n.clientCapabilities?.fs,{protocolVersion:t,agentCapabilities:{loadSession:i.loadSession!==void 0&&e.getMessagesDisplay!==void 0,promptCapabilities:{image:!1,audio:!1,embeddedContext:!0}},agentInfo:{name:i.agentInfo.name,version:i.agentInfo.version}})).onRequest(`session/new`,async({params:e,client:t})=>{if(c!==void 0||u)throw n.invalidParams(void 0,`one session per connection`);v(e),u=!0;try{c=await y(`session/new`)}finally{u=!1}return E(t),x(t,c),{sessionId:c}}).onRequest(`session/load`,async({params:t,client:r})=>{let a=i.loadSession;if(a===void 0||e.getMessagesDisplay===void 0)throw n.methodNotFound(`session/load`);if(u)throw n.invalidParams(void 0,`a session is already being opened`);if(d!==void 0)throw n.invalidParams(void 0,`a prompt is running`);v(t),u=!0;try{let i;try{i=await a(t.sessionId)}catch(e){throw o(K(e),`session/load`),new n(U,`The session could not be loaded`)}if(!i.loaded)throw n.invalidParams(void 0,i.message);c=await y(`session/load`),E(r);let s=H(r,c,o);try{T(e.getMessagesDisplay(),s,m)}catch(e){throw o(K(e),`session/load`),new n(U,`The session history could not be shown`)}await s.drained()}finally{u=!1}return g=void 0,x(r,c),{}}).onRequest(`session/prompt`,async({params:t,signal:r,client:s})=>{if(t.sessionId!==c||u)throw n.invalidParams(void 0,`unknown session`);if(d!==void 0)throw n.invalidParams(void 0,`a prompt is already running`);let l=_(t.prompt);if(l.full.trim()===``)throw n.invalidParams(void 0,`the prompt has no text`);let f=new AbortController,p=!1;d={cancel(){p=!0,f.abort(),e.abort()}};let h={session:e,sessionId:t.sessionId,client:s,updates:H(s,t.sessionId,o),signal:AbortSignal.any([r,f.signal]),driverId:a,surface:i.surface??`unknown`,cwd:i.cwd,nextToolCallId:m,wasCancelled:()=>p,report:o};try{return l.typed.trimStart().startsWith(`/`)?await V(h,l.typed,i.executeCommand??W):await B(h,l.full)}finally{d=void 0,b(s,t.sessionId)}}).onNotification(`session/cancel`,({params:e})=>{e.sessionId===c&&d?.cancel()}),cancelActive:()=>d?.cancel(),withdrawEditorFiles:()=>{C&&(C=!1,i.onEditorFileAccess?.(void 0))}}}function J(e){return new WritableStream({write:t=>new Promise((n,r)=>{e.write(t,e=>e?r(e):n())})})}function Y(t){let n=null,r=!1,o=null,s=null,c=null,l=null,u=null,d=null,f=null,p=Promise.resolve(),m=()=>{p=new Promise((e,t)=>{d=e,f=t}),p.catch(()=>void 0)},h=()=>{try{u?.(),l?.(),c?.close()}finally{s?.release(),s=null,c=null,l=null,u=null}},g=e=>Object.assign(Error(`ACP transport ${e}.`),{name:`TransportLifecycleError`,code:e,transportName:`acp`});return{name:`acp`,lifecycle:Object.freeze({kind:`service`}),attach(e){if(r||o)throw g(`already-started`);n=e,m()},async start(){if(!n)throw g(`not-attached`);if(r||o)throw g(`already-started`);r=!0;try{let r=q(n,t);l=r.cancelActive,u=r.withdrawEditorFiles;let o=a(t.input,t.output,{onInputClose:()=>d?.(),onOutputClose:()=>d?.(),onOutputError:e=>f?.(e)});s=o;let p=r.app.connect(i(J(t.output),e.toWeb(o.bridge)));c=p,p.closed.then(()=>d?.())}catch(e){throw h(),r=!1,e}},stop(){return o||(o=(async()=>{try{h()}finally{n=null,r=!1,d?.()}})().finally(()=>{o=null}),o)},waitForClose(){return p}}}export{Y as createAcpTransport};
7
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/acp-prompt-text.ts","../../src/acp-tool-mapping.ts","../../src/acp-permission.ts","../../src/acp-session-events.ts","../../src/acp-tool-calls.ts","../../src/acp-turn.ts","../../src/acp-updates.ts","../../src/acp-agent.ts","../../src/acp-transport.ts"],"sourcesContent":["/** A prompt's content blocks as the single text the session receives. */\n\nimport type { ContentBlock } from '@agentclientprotocol/sdk';\n\nexport interface IAcpPromptText {\n /** What the person typed: the text blocks alone. A `/command` is recognised on this. */\n readonly typed: string;\n /** What the session receives: the typed text followed by the context the editor attached. */\n readonly full: string;\n}\n\nconst MIN_FENCE = 3;\n\n/** A backtick fence longer than any run inside the text, so attached content cannot close it early. */\nfunction fence(text: string): string {\n const longest = Math.max(0, ...[...text.matchAll(/`+/g)].map((run) => run[0].length));\n return '`'.repeat(Math.max(MIN_FENCE, longest + 1));\n}\n\n/**\n * Text blocks are the typed message. An embedded text resource is appended as fenced context; a\n * resource link is listed by URI so the model can read it under the session's own permissions. Image\n * and audio blocks are not advertised as supported, so they are ignored.\n */\nexport function promptText(blocks: readonly ContentBlock[]): IAcpPromptText {\n let typed = '';\n const context: string[] = [];\n const links: string[] = [];\n for (const block of blocks) {\n if (block.type === 'text') typed += block.text;\n else if (block.type === 'resource' && 'text' in block.resource) {\n const marks = fence(block.resource.text);\n context.push(\n `Context from ${block.resource.uri}:\\n${marks}\\n${block.resource.text}\\n${marks}`,\n );\n } else if (block.type === 'resource_link') links.push(`- ${block.uri}`);\n }\n if (links.length > 0) context.push(`Referenced resources:\\n${links.join('\\n')}`);\n return { typed, full: context.length === 0 ? typed : `${typed}\\n\\n${context.join('\\n\\n')}` };\n}\n","/** How a session tool call is described to an editor. */\n\nimport type { ToolCallStatus, ToolKind } from '@agentclientprotocol/sdk';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\nconst TOOL_KINDS: Readonly<Record<string, ToolKind>> = {\n Read: 'read',\n Glob: 'search',\n Grep: 'search',\n Edit: 'edit',\n Write: 'edit',\n Shell: 'execute',\n Bash: 'execute',\n WebFetch: 'fetch',\n WebSearch: 'fetch',\n};\n\nconst MAX_TITLE_DETAIL = 120;\n\nexport function acpToolKind(toolName: string): ToolKind {\n return TOOL_KINDS[toolName] ?? 'other';\n}\n\n/** The tool's name plus the one argument the session summarises it by, shortened for a title line. */\nexport function acpToolTitle(toolName: string, detail: string | undefined): string {\n const text = detail?.replace(/\\s+/g, ' ').trim();\n if (!text) return toolName;\n const shown = text.length > MAX_TITLE_DETAIL ? `${text.slice(0, MAX_TITLE_DETAIL - 1)}…` : text;\n return `${toolName}: ${shown}`;\n}\n\n/**\n * The tool's name plus its argument as given. A person deciding whether a call may run sees all of\n * what it would do, so nothing is shortened and line breaks stay.\n */\nexport function acpFullToolTitle(toolName: string, detail: string | undefined): string {\n const text = detail?.trim();\n return text ? `${toolName}: ${text}` : toolName;\n}\n\nexport function acpToolStartTitle(tool: IToolState): string {\n return acpToolTitle(tool.toolName, tool.displayPath ?? tool.firstArg);\n}\n\n/** A call that ended in error or was denied is `failed`; only a success is `completed`. */\nexport function acpToolEndStatus(tool: IToolState): ToolCallStatus {\n return tool.result === 'success' ? 'completed' : 'failed';\n}\n","/** How a session permission prompt becomes an ACP permission request and its answer becomes a decision. */\n\nimport { resolve } from 'node:path';\n\nimport { permissionSummaryArgument } from '@robota-sdk/agent-transport';\n\nimport { acpFullToolTitle, acpToolKind } from './acp-tool-mapping.js';\n\nimport type {\n PermissionOption,\n RequestPermissionOutcome,\n ToolCallUpdate,\n} from '@agentclientprotocol/sdk';\nimport type {\n IDiffLine,\n IPermissionRequestEvent,\n TPermissionResultValue,\n} from '@robota-sdk/agent-interface-session';\n\nconst OPTION = {\n allowOnce: 'allow-once',\n allowSession: 'allow-session',\n allowProject: 'allow-project',\n reject: 'reject',\n} as const;\n\nexport function permissionOptions(canPersistProject: boolean): PermissionOption[] {\n return [\n { optionId: OPTION.allowOnce, name: 'Allow once', kind: 'allow_once' },\n { optionId: OPTION.allowSession, name: 'Allow for this session', kind: 'allow_always' },\n ...(canPersistProject\n ? [\n {\n optionId: OPTION.allowProject,\n name: 'Allow for this project',\n kind: 'allow_always' as const,\n },\n ]\n : []),\n { optionId: OPTION.reject, name: 'Reject', kind: 'reject_once' },\n ];\n}\n\nexport function permissionTitle(event: IPermissionRequestEvent): string {\n const title = acpFullToolTitle(\n event.toolName,\n event.diffFile ?? permissionSummaryArgument(event.toolArgs),\n );\n return event.requester ? `${event.requester.label}: ${title}` : title;\n}\n\n/** What the call was asked with, including a shell call's working directory when the session resolved one. */\nfunction permissionRawInput(event: IPermissionRequestEvent): unknown {\n if (event.cwd === undefined || 'cwd' in event.toolArgs) return event.toolArgs;\n return { ...event.toolArgs, cwd: event.cwd };\n}\n\nconst HUNK_HEADER = /^@@ /;\n\n/**\n * The file as it is before and after, rebuilt from the session's diff lines: context lines belong\n * to both sides. A diff with nothing on the old side is a new file, which ACP spells as no old text.\n * The session caps each side and says so in a hunk line placed after the lines it kept; that line\n * stays with its side so a cut-off file is not presented as whole. A hunk header carries no text.\n */\nfunction diffTexts(lines: readonly IDiffLine[]): { oldText?: string; newText: string } {\n const before: string[] = [];\n const after: string[] = [];\n let previous: IDiffLine['type'] = 'context';\n for (const line of lines) {\n if (line.type === 'hunk') {\n if (HUNK_HEADER.test(line.text)) continue;\n if (previous !== 'add') before.push(line.text);\n if (previous !== 'remove') after.push(line.text);\n continue;\n }\n previous = line.type;\n if (line.type !== 'add') before.push(line.text);\n if (line.type !== 'remove') after.push(line.text);\n }\n return {\n ...(before.length > 0 ? { oldText: before.join('\\n') } : {}),\n newText: after.join('\\n'),\n };\n}\n\n/**\n * The tool call a permission request describes. The editor gets what the terminal shows: the\n * arguments whole, and for an edit the diff and the file it concerns. `cwd` roots a path the\n * session reported relative to the workspace, because ACP paths are absolute.\n */\nexport function permissionToolCall(\n event: IPermissionRequestEvent,\n toolCallId: string,\n cwd: string,\n): ToolCallUpdate {\n const path = event.diffFile === undefined ? undefined : resolve(cwd, event.diffFile);\n return {\n toolCallId,\n title: permissionTitle(event),\n kind: acpToolKind(event.toolName),\n status: 'pending',\n rawInput: permissionRawInput(event),\n ...(path !== undefined && event.diffLines !== undefined\n ? { content: [{ type: 'diff' as const, path, ...diffTexts(event.diffLines) }] }\n : {}),\n ...(path !== undefined ? { locations: [{ path }] } : {}),\n };\n}\n\n/**\n * The editor's answer as a session decision. Anything the editor was not offered, or that is not\n * recognised, is a denial: an answer widens permission only by naming an option this request listed.\n */\nexport function permissionDecision(\n outcome: RequestPermissionOutcome,\n offered: readonly PermissionOption[],\n): TPermissionResultValue {\n if (outcome.outcome !== 'selected') return false;\n if (!offered.some((option) => option.optionId === outcome.optionId)) return false;\n switch (outcome.optionId) {\n case OPTION.allowOnce:\n return true;\n case OPTION.allowSession:\n return 'allow-session';\n case OPTION.allowProject:\n return 'allow-project';\n default:\n return false;\n }\n}\n","/**\n * Which session events this transport acts on.\n *\n * This is the ACP owner's own exhaustive policy over the shared event vocabulary, not the WebSocket\n * fan-out's: a new session event fails compilation here until someone decides what an editor sees.\n * The subscription is derived from it, so promoting an event to `stream` or `permission` without a\n * handler fails compilation too.\n */\n\nimport type {\n IInteractiveSessionEvents,\n ISessionEvents,\n TInteractiveEventName,\n} from '@robota-sdk/agent-interface-session';\n\n/**\n * - `stream`: forwarded to the editor as a session update while a prompt runs.\n * - `permission`: becomes a permission request to the editor.\n * - `settle`: ends the turn; the outcome is read from the submission's turn handle, which names its\n * own turn, rather than from the session-wide event, which cannot say which turn ended.\n * - `ignore`: nothing an ACP editor shows. `ask_request` is deliberately here: a session with no\n * listener answers an ask as cancelled, which fails closed because ACP v1 has no question prompt.\n */\nexport type TAcpEventPolicy = 'stream' | 'permission' | 'settle' | 'ignore';\n\nexport const ACP_SESSION_EVENT_POLICY = {\n text_delta: 'stream',\n tool_start: 'stream',\n tool_end: 'stream',\n thinking: 'ignore', // a busy flag with no text; the reasoning text is `thinking_delta`\n thinking_delta: 'stream',\n complete: 'settle',\n error: 'settle',\n interrupted: 'settle',\n permission_request: 'permission',\n ask_request: 'ignore',\n context_update: 'ignore',\n compact: 'ignore',\n skill_activation: 'ignore',\n background_task_event: 'ignore',\n background_job_group_event: 'ignore',\n execution_workspace_event: 'ignore',\n user_message: 'ignore',\n turn_source: 'ignore',\n context_file_refreshed: 'ignore',\n memory_event: 'ignore',\n goal_event: 'ignore',\n plan_event: 'ignore',\n branch_event: 'ignore',\n prompt_resolved: 'ignore',\n ui_intent: 'ignore',\n session_renamed: 'ignore',\n history_cleared: 'ignore',\n session_switched: 'ignore',\n status_changed: 'ignore',\n} as const satisfies Record<TInteractiveEventName, TAcpEventPolicy>;\n\ntype TPolicyMap = typeof ACP_SESSION_EVENT_POLICY;\ntype TEventsWith<P extends TAcpEventPolicy> = {\n [K in keyof TPolicyMap]: TPolicyMap[K] extends P ? K : never;\n}[keyof TPolicyMap];\n\nexport type TAcpSubscribedEvent = TEventsWith<'stream' | 'permission'>;\n\n/** Exactly one handler for every event the policy subscribes, and none for any other. */\nexport type TAcpEventHandlers = {\n readonly [K in TAcpSubscribedEvent]: IInteractiveSessionEvents[K];\n};\n\n/** Subscribe the handlers; returns the unsubscribe. */\nexport function subscribeAcpEvents(\n session: ISessionEvents,\n handlers: TAcpEventHandlers,\n): () => void {\n const names = Object.keys(handlers) as TAcpSubscribedEvent[];\n for (const name of names) session.on(name, handlers[name]);\n return () => {\n for (const name of names) session.off(name, handlers[name]);\n };\n}\n","/** The tool-call rows one turn shows an editor: which session call each row stands for, and when it is done. */\n\nimport { permissionToolCall } from './acp-permission.js';\nimport { acpToolEndStatus, acpToolKind, acpToolStartTitle } from './acp-tool-mapping.js';\n\nimport type { IAcpUpdateStream } from './acp-updates.js';\nimport type { ToolCallUpdate } from '@agentclientprotocol/sdk';\nimport type { IPermissionRequestEvent, IToolState } from '@robota-sdk/agent-interface-session';\n\nexport interface IAcpToolCallTracker {\n start(tool: IToolState): void;\n end(tool: IToolState): void;\n /** The row a permission request asks about; when the request names its call, the row is that call's. */\n requested(event: IPermissionRequestEvent): ToolCallUpdate;\n /** The editor answered; the session decides what happens next, and its tool events say so. */\n answered(event: IPermissionRequestEvent, allowed: boolean): void;\n /** The turn is over: close every row still waiting for a call that never came. */\n finish(): void;\n}\n\nexport interface IAcpToolCallDeps {\n readonly updates: IAcpUpdateStream;\n /** Ids for tool calls the session gives no id; unique for the life of the session. */\n readonly nextToolCallId: () => string;\n /** The served directory, for paths the session reports relative to it. */\n readonly cwd: string;\n}\n\ninterface IOpenCall {\n readonly toolName: string;\n readonly executionId: string | undefined;\n readonly id: string;\n}\n\n/** A permission request's row, held until the call it names starts or is refused. */\ninterface IAwaitedCall {\n readonly rawInput: unknown;\n /** A background agent's call can run after this prompt, so its row closes with the answer. */\n readonly background: boolean;\n allowed?: boolean;\n}\n\nexport function createToolCallTracker(deps: IAcpToolCallDeps): IAcpToolCallTracker {\n const { updates } = deps;\n const open: IOpenCall[] = [];\n /**\n * The session asks permission before it reports a call, and a refused call is reported ending\n * without ever having started. A request names its call by the execution id the call's own\n * events carry, so the request's row is keyed by it and those events address the same row.\n */\n const awaited = new Map<string, IAwaitedCall>();\n /** Rows of requests that name no call, by request id: nothing else will close them. */\n const unnamed = new Map<string, string>();\n let finished = false;\n\n const take = (executionId: string | undefined): IAwaitedCall | undefined => {\n if (executionId === undefined) return undefined;\n const call = awaited.get(executionId);\n awaited.delete(executionId);\n return call;\n };\n const close = (toolCallId: string, allowed: boolean): void =>\n updates.send({\n sessionUpdate: 'tool_call_update',\n toolCallId,\n status: allowed ? 'completed' : 'failed',\n });\n\n return {\n start(tool) {\n // An internal signal tool is not a call the person made or should see.\n if (tool.internal) return;\n const asked = take(tool.executionId);\n const id = tool.executionId ?? deps.nextToolCallId();\n open.push({ id, toolName: tool.toolName, executionId: tool.executionId });\n const call = {\n toolCallId: id,\n title: acpToolStartTitle(tool),\n kind: acpToolKind(tool.toolName),\n status: 'in_progress',\n } as const;\n if (asked === undefined) updates.send({ sessionUpdate: 'tool_call', ...call });\n else {\n updates.send({ sessionUpdate: 'tool_call_update', ...call, rawInput: asked.rawInput });\n }\n },\n\n end(tool) {\n if (tool.internal) return;\n const status = acpToolEndStatus(tool);\n const index = open.findIndex((call) =>\n tool.executionId !== undefined\n ? call.executionId === tool.executionId\n : call.executionId === undefined && call.toolName === tool.toolName,\n );\n if (index >= 0) {\n const [call] = open.splice(index, 1);\n updates.send({ sessionUpdate: 'tool_call_update', toolCallId: call!.id, status });\n return;\n }\n // A refused call ends with no start; when its permission was asked, that row is the call.\n if (take(tool.executionId) !== undefined) {\n updates.send({ sessionUpdate: 'tool_call_update', toolCallId: tool.executionId!, status });\n return;\n }\n // Otherwise give the editor the call, finished, rather than an update for nothing.\n updates.send({\n sessionUpdate: 'tool_call',\n toolCallId: tool.executionId ?? deps.nextToolCallId(),\n title: acpToolStartTitle(tool),\n kind: acpToolKind(tool.toolName),\n status,\n });\n },\n\n requested(event) {\n const { executionId } = event;\n if (executionId === undefined) {\n const call = permissionToolCall(event, `permission-${event.id}`, deps.cwd);\n unnamed.set(event.id, call.toolCallId);\n return call;\n }\n const call = permissionToolCall(event, executionId, deps.cwd);\n // A call that is already running (asked again before its effect) keeps the row it has.\n if (!open.some((running) => running.executionId === executionId)) {\n awaited.set(executionId, {\n rawInput: call.rawInput,\n background: event.requester !== undefined,\n });\n }\n return call;\n },\n\n answered(event, allowed) {\n if (event.executionId !== undefined) {\n // The call's own events say what the answer led to; the answer is kept for a call that\n // may run after this turn.\n const asked = awaited.get(event.executionId);\n if (asked !== undefined) asked.allowed = allowed;\n return;\n }\n const row = unnamed.get(event.id);\n if (row === undefined || finished) return;\n unnamed.delete(event.id);\n close(row, allowed);\n },\n\n finish() {\n finished = true;\n for (const [id, call] of awaited) close(id, call.background && call.allowed === true);\n for (const row of unnamed.values()) close(row, false);\n awaited.clear();\n unnamed.clear();\n },\n };\n}\n","/**\n * One `session/prompt` that runs a turn: stream the session's events to the editor, ask it about\n * permissions, and settle with a stop reason. A typed message and a `/command` that runs a model\n * turn (a skill) both go through here, so the editor sees the same thing from either.\n */\n\nimport { RequestError } from '@agentclientprotocol/sdk';\nimport { isTurnNotRunError } from '@robota-sdk/agent-interface-session';\n\nimport { permissionDecision, permissionOptions } from './acp-permission.js';\nimport { subscribeAcpEvents } from './acp-session-events.js';\nimport { createToolCallTracker } from './acp-tool-calls.js';\n\nimport type { IAcpTransportSession } from './acp-types.js';\nimport type { IAcpUpdateStream } from './acp-updates.js';\nimport type { AgentContext, PromptResponse } from '@agentclientprotocol/sdk';\nimport type {\n IPermissionRequestEvent,\n ISubmitOptions,\n TDriverId,\n} from '@robota-sdk/agent-interface-session';\n\nexport interface IAcpTurnContext {\n readonly session: IAcpTransportSession;\n readonly sessionId: string;\n readonly client: AgentContext;\n readonly updates: IAcpUpdateStream;\n /** Aborts when the editor cancels the request or the connection closes. */\n readonly signal: AbortSignal;\n readonly driverId: TDriverId;\n readonly surface: ISubmitOptions['surface'];\n /** The served directory; paths the session reports relative to it are made absolute for the editor. */\n readonly cwd: string;\n /** Ids for tool calls the session gives no id; unique for the life of the session. */\n readonly nextToolCallId: () => string;\n /** Whether the editor sent `session/cancel` for this prompt. */\n readonly wasCancelled: () => boolean;\n readonly report: (error: Error, context: string) => void;\n}\n\nconst CANCELLED: PromptResponse = { stopReason: 'cancelled' };\nconst END_TURN: PromptResponse = { stopReason: 'end_turn' };\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\n/** What a turn's work left behind: whether it was cut short, and the text to show if nothing streamed. */\ninterface IWorkOutcome {\n readonly interrupted: boolean;\n readonly reply: string;\n}\n\ninterface IStreamedWork {\n run(): Promise<IWorkOutcome>;\n /** A failure that was not a cancellation, as the editor's answer: it reports the cause and throws a fixed error. */\n fail(error: unknown): never;\n /** Whether `error` is the work saying it was cancelled. */\n isCancellation?(error: unknown): boolean;\n}\n\nasync function runStreamedTurn(\n turn: IAcpTurnContext,\n work: IStreamedWork,\n): Promise<PromptResponse> {\n const { session, sessionId, client, updates } = turn;\n const tools = createToolCallTracker({\n updates,\n nextToolCallId: turn.nextToolCallId,\n cwd: turn.cwd,\n });\n let streamed = false;\n\n const onPermissionRequest = (event: IPermissionRequestEvent): void => {\n const options = permissionOptions(event.canPersistProjectPermission === true);\n const toolCall = tools.requested(event);\n void (async () => {\n let decision: ReturnType<typeof permissionDecision> = false;\n try {\n const response = await client.request('session/request_permission', {\n sessionId,\n toolCall,\n options,\n });\n decision = permissionDecision(response.outcome, options);\n } catch (error) {\n // A closed connection or a refused request means nobody approved: deny.\n turn.report(asError(error), 'session/request_permission');\n }\n tools.answered(event, decision !== false);\n try {\n session.resolvePermission(event.id, decision, turn.driverId);\n } catch (error) {\n turn.report(asError(error), 'resolvePermission');\n }\n })();\n };\n\n const unsubscribe = subscribeAcpEvents(session, {\n text_delta: (delta) => {\n if (delta === '') return;\n streamed = true;\n updates.text(delta);\n },\n // Reasoning is not the reply: it never marks the turn streamed, so a turn that showed only\n // thoughts still gets its result text.\n thinking_delta: (delta) => {\n if (delta === '') return;\n updates.thought(delta);\n },\n tool_start: tools.start,\n tool_end: tools.end,\n permission_request: onPermissionRequest,\n });\n\n try {\n const outcome = await work.run();\n if (turn.wasCancelled() || turn.signal.aborted || outcome.interrupted) return CANCELLED;\n // Nothing streamed (a provider without deltas, or a command that is not a turn): the text arrives whole.\n if (!streamed && outcome.reply !== '') updates.text(outcome.reply);\n return END_TURN;\n } catch (error) {\n if (turn.wasCancelled() || turn.signal.aborted || work.isCancellation?.(error) === true) {\n return CANCELLED;\n }\n return work.fail(error);\n } finally {\n unsubscribe();\n tools.finish();\n await updates.drained();\n }\n}\n\n/** A typed message: submit it to the session and take the turn's result. */\nexport function runModelTurn(turn: IAcpTurnContext, text: string): Promise<PromptResponse> {\n return runStreamedTurn(turn, {\n async run() {\n const handle = await turn.session.submit(text, undefined, undefined, {\n signal: turn.signal,\n driverId: turn.driverId,\n ...(turn.surface !== undefined ? { surface: turn.surface } : {}),\n });\n const result = await handle.completed;\n return { interrupted: result.interrupted === true, reply: result.response };\n },\n isCancellation: (error) => isTurnNotRunError(error) && error.reason === 'cancelled',\n fail(error) {\n turn.report(asError(error), 'turn');\n // The provider's own text can carry request details; the editor gets a fixed message.\n throw new RequestError(-32603, isTurnNotRunError(error) ? 'Turn was not run' : 'Turn failed');\n },\n });\n}\n\n/**\n * A `/command` prompt: the host runs it and its output is the reply. A command that runs a model\n * turn (a skill) streams that turn and asks about permissions while it runs; its output then\n * repeats the streamed reply, so it is shown only when nothing streamed (`/help`, a message).\n */\nexport function runCommandTurn(\n turn: IAcpTurnContext,\n text: string,\n executeCommand: (text: string, signal: AbortSignal) => Promise<string>,\n): Promise<PromptResponse> {\n return runStreamedTurn(turn, {\n async run() {\n return { interrupted: false, reply: await executeCommand(text, turn.signal) };\n },\n fail(error) {\n turn.report(asError(error), 'command');\n throw new RequestError(-32603, 'Command failed');\n },\n });\n}\n","/** Ordered `session/update` delivery for one prompt. */\n\nimport type { AgentContext, SessionUpdate } from '@agentclientprotocol/sdk';\n\nexport interface IAcpUpdateStream {\n send(update: SessionUpdate): void;\n /** Queue message text for the editor. */\n text(text: string): void;\n /** Queue reasoning text for the editor, shown apart from the reply. */\n thought(text: string): void;\n /** Settles once everything queued has been written or abandoned. Never rejects. */\n drained(): Promise<void>;\n}\n\n/**\n * Updates are written in the order they were queued, and a prompt's reply waits on `drained()`, so\n * the editor has every update before it sees `stopReason`. A failed write is reported once and the\n * rest are dropped: the editor is gone, and the session turn is not the carrier's to fail.\n */\nexport function createUpdateStream(\n client: AgentContext,\n sessionId: string,\n report: (error: Error, context: string) => void,\n): IAcpUpdateStream {\n let tail: Promise<void> = Promise.resolve();\n let failed = false;\n const send = (update: SessionUpdate): void => {\n tail = tail.then(async () => {\n if (failed) return;\n try {\n await client.notify('session/update', { sessionId, update });\n } catch (error) {\n failed = true;\n report(error instanceof Error ? error : new Error(String(error)), 'session/update');\n }\n });\n };\n return {\n send,\n text: (text) => send({ sessionUpdate: 'agent_message_chunk', content: { type: 'text', text } }),\n thought: (text) =>\n send({ sessionUpdate: 'agent_thought_chunk', content: { type: 'text', text } }),\n drained: () => tail,\n };\n}\n","/** The ACP agent handlers for one connection: one attached session, one prompt at a time. */\n\nimport { realpathSync } from 'node:fs';\nimport { isAbsolute, resolve } from 'node:path';\n\nimport { agent, PROTOCOL_VERSION, RequestError } from '@agentclientprotocol/sdk';\nimport { OWNER_DRIVER_ID } from '@robota-sdk/agent-interface-session';\n\nimport { promptText } from './acp-prompt-text.js';\nimport { runCommandTurn, runModelTurn } from './acp-turn.js';\nimport { createUpdateStream } from './acp-updates.js';\n\nimport type { IAcpTurnContext } from './acp-turn.js';\nimport type { IAcpTransportOptions, IAcpTransportSession } from './acp-types.js';\nimport type { AgentApp, PromptResponse } from '@agentclientprotocol/sdk';\n\n/**\n * JSON-RPC server-error code for a workspace the host has not admitted. Not -32000: ACP assigns\n * that to \"authentication required\", which an editor answers with a sign-in state this agent\n * does not have.\n */\nconst WORKSPACE_NOT_ADMITTED = -32001;\nconst INTERNAL_ERROR = -32603;\nconst COMMANDS_UNAVAILABLE = 'Slash commands are not available in this host.';\n\nconst commandsUnavailable = (): Promise<string> => Promise.resolve(COMMANDS_UNAVAILABLE);\n\nexport interface IAcpAgent {\n readonly app: AgentApp;\n /** Cancel the running prompt, if there is one. */\n cancelActive(): void;\n}\n\nfunction sameDirectory(requested: string, served: string): boolean {\n if (resolve(requested) === resolve(served)) return true;\n try {\n return realpathSync(requested) === realpathSync(served);\n } catch {\n // allow-fallback: a path that cannot be resolved is not the served directory, and the caller refuses it.\n return false;\n }\n}\n\nexport function createAcpAgent(\n session: IAcpTransportSession,\n options: IAcpTransportOptions,\n): IAcpAgent {\n const driverId = options.driverId ?? OWNER_DRIVER_ID;\n const report = (error: Error, context: string): void => options.onDeliveryError?.(error, context);\n let boundSessionId: string | undefined;\n /** A `session/new` is waiting for the session to start; a second one must not also claim it. */\n let binding = false;\n let active: { cancel(): void } | undefined;\n let toolCalls = 0;\n\n const app = agent({ name: options.agentInfo.name })\n .onRequest('initialize', () => ({\n // Always the version this agent speaks: the client disconnects if it cannot use it.\n protocolVersion: PROTOCOL_VERSION,\n agentCapabilities: {\n loadSession: false,\n promptCapabilities: { image: false, audio: false, embeddedContext: true },\n },\n agentInfo: { name: options.agentInfo.name, version: options.agentInfo.version },\n }))\n .onRequest('session/new', async ({ params }) => {\n if (boundSessionId !== undefined || binding) {\n throw RequestError.invalidParams(undefined, 'one session per connection');\n }\n if (!isAbsolute(params.cwd) || !sameDirectory(params.cwd, options.cwd)) {\n throw RequestError.invalidParams(\n undefined,\n `cwd must be the directory this agent was started in: ${options.cwd}`,\n );\n }\n // Servers or roots named by the editor would widen what the session can reach.\n if (params.mcpServers.length > 0) {\n throw RequestError.invalidParams(undefined, 'mcpServers are not accepted by this agent');\n }\n if ((params.additionalDirectories ?? []).length > 0) {\n throw RequestError.invalidParams(\n undefined,\n 'additionalDirectories are not accepted by this agent',\n );\n }\n const admission = options.admitWorkspace();\n if (!admission.admitted) {\n throw new RequestError(WORKSPACE_NOT_ADMITTED, admission.message, {\n reason: 'workspace-not-trusted',\n });\n }\n binding = true;\n try {\n // A session that builds itself in the background cannot name itself until it is ready.\n await session.whenInitialized?.();\n boundSessionId = session.getStatusSnapshot().sessionId;\n } catch (error) {\n report(error instanceof Error ? error : new Error(String(error)), 'session/new');\n // The cause can name host paths or settings; the editor only needs to know it failed.\n throw new RequestError(INTERNAL_ERROR, 'The session could not be started');\n } finally {\n binding = false;\n }\n return { sessionId: boundSessionId };\n })\n .onRequest('session/prompt', async ({ params, signal, client }): Promise<PromptResponse> => {\n if (params.sessionId !== boundSessionId) {\n throw RequestError.invalidParams(undefined, 'unknown session');\n }\n if (active !== undefined) {\n throw RequestError.invalidParams(undefined, 'a prompt is already running');\n }\n const text = promptText(params.prompt);\n if (text.full.trim() === '') {\n throw RequestError.invalidParams(undefined, 'the prompt has no text');\n }\n const controller = new AbortController();\n let cancelled = false;\n active = {\n cancel() {\n cancelled = true;\n controller.abort();\n session.abort();\n },\n };\n const turn: IAcpTurnContext = {\n session,\n sessionId: params.sessionId,\n client,\n updates: createUpdateStream(client, params.sessionId, report),\n signal: AbortSignal.any([signal, controller.signal]),\n driverId,\n surface: options.surface ?? 'unknown',\n cwd: options.cwd,\n nextToolCallId: () => `tool-${(toolCalls += 1)}`,\n wasCancelled: () => cancelled,\n report,\n };\n try {\n return text.typed.trimStart().startsWith('/')\n ? await runCommandTurn(turn, text.typed, options.executeCommand ?? commandsUnavailable)\n : await runModelTurn(turn, text.full);\n } finally {\n active = undefined;\n }\n })\n .onNotification('session/cancel', ({ params }) => {\n if (params.sessionId === boundSessionId) active?.cancel();\n });\n\n return { app, cancelActive: () => active?.cancel() };\n}\n","/**\n * ITransportAdapter that runs the attached session as an Agent Client Protocol (v1) agent, so an\n * editor that launches the process can create a session, prompt it, see streamed output and tool\n * calls, approve or reject permissions, and cancel a turn.\n *\n * The adapter owns the carrier's readiness and teardown; the product shell owns process policy\n * (stdout reservation, workspace trust, signals).\n */\n\nimport { Readable } from 'node:stream';\n\nimport { ndJsonStream } from '@agentclientprotocol/sdk';\nimport { attachStdioCarrier } from '@robota-sdk/agent-transport/node';\n\nimport { createAcpAgent } from './acp-agent.js';\n\nimport type { IAcpTransport, IAcpTransportOptions, IAcpTransportSession } from './acp-types.js';\nimport type { AgentConnection } from '@agentclientprotocol/sdk';\nimport type { Writable } from 'node:stream';\nimport type { ITransportLifecycleError } from '@robota-sdk/agent-interface-transport';\nimport type { IStdioCarrier } from '@robota-sdk/agent-transport/node';\n\n/**\n * A Web sink over the process output. `Writable.toWeb` would leave its own listeners on a stream\n * the transport does not own, and destroy it if the SDK ever aborts the sink; this adds none.\n */\nfunction outputSink(output: Writable): WritableStream<Uint8Array> {\n return new WritableStream<Uint8Array>({\n write: (chunk) =>\n new Promise<void>((resolve, reject) => {\n output.write(chunk, (error) => (error ? reject(error) : resolve()));\n }),\n });\n}\n\nexport function createAcpTransport(options: IAcpTransportOptions): IAcpTransport {\n let session: IAcpTransportSession | null = null;\n let running = false;\n let stopping: Promise<void> | null = null;\n let carrier: IStdioCarrier | null = null;\n let connection: AgentConnection | null = null;\n let cancelActive: (() => void) | null = null;\n let resolveClose: (() => void) | null = null;\n let rejectClose: ((error: Error) => void) | null = null;\n let closePromise: Promise<void> = Promise.resolve();\n const resetClose = (): void => {\n closePromise = new Promise<void>((resolve, reject) => {\n resolveClose = resolve;\n rejectClose = reject;\n });\n // allow-fallback: waitForClose retains the rejection for the process owner; this observer only prevents an early unhandled-rejection warning.\n void closePromise.catch(() => undefined);\n };\n const release = (): void => {\n try {\n cancelActive?.();\n connection?.close();\n } finally {\n carrier?.release();\n carrier = null;\n connection = null;\n cancelActive = null;\n }\n };\n const lifecycleError = (code: ITransportLifecycleError['code']): ITransportLifecycleError =>\n Object.assign(new Error(`ACP transport ${code}.`), {\n name: 'TransportLifecycleError' as const,\n code,\n transportName: 'acp',\n });\n\n return {\n name: 'acp',\n lifecycle: Object.freeze({ kind: 'service' }),\n attach(s: IAcpTransportSession) {\n if (running || stopping) throw lifecycleError('already-started');\n session = s;\n resetClose();\n },\n async start() {\n if (!session) throw lifecycleError('not-attached');\n if (running || stopping) throw lifecycleError('already-started');\n running = true;\n try {\n const acpAgent = createAcpAgent(session, options);\n cancelActive = acpAgent.cancelActive;\n // The SDK destroys what it reads when its connection closes; it reads the bridge, never the\n // process input, and a closed or failed end ends the transport instead of leaving it idle.\n const attached = attachStdioCarrier(options.input, options.output, {\n onInputClose: () => resolveClose?.(),\n onOutputClose: () => resolveClose?.(),\n onOutputError: (error) => rejectClose?.(error),\n });\n carrier = attached;\n const opened = acpAgent.app.connect(\n ndJsonStream(\n outputSink(options.output),\n Readable.toWeb(attached.bridge) as ReadableStream<Uint8Array>,\n ),\n );\n connection = opened;\n void opened.closed.then(() => resolveClose?.());\n } catch (error) {\n // A start that failed serves nothing, so a retry must not be told it is already running.\n release();\n running = false;\n throw error;\n }\n },\n stop() {\n if (stopping) return stopping;\n stopping = (async () => {\n try {\n release();\n } finally {\n session = null;\n running = false;\n resolveClose?.();\n }\n })().finally(() => {\n stopping = null;\n });\n return stopping;\n },\n waitForClose() {\n return closePromise;\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["asError"],"sources":["../../src/acp-editor-files.ts","../../src/acp-prompt-text.ts","../../src/acp-tool-mapping.ts","../../src/acp-session-catalog.ts","../../src/acp-permission.ts","../../src/acp-session-events.ts","../../src/acp-tool-calls.ts","../../src/acp-turn.ts","../../src/acp-updates.ts","../../src/acp-agent.ts","../../src/acp-transport.ts"],"sourcesContent":["/**\n * The editor's file system, as the editor offered it in `initialize`: reads and writes the host's\n * file tools may inspect once the session is bound. ACP v1 carries only a pathname, not a stable\n * rooted file capability, so this adapter deliberately cannot claim the authority required to\n * carry contained Read, Write or Edit operations.\n */\n\nimport { isAbsolute } from 'node:path';\n\nimport { RequestError } from '@agentclientprotocol/sdk';\n\nimport type { IAcpEditorFileAccess } from './acp-types.js';\nimport type { AgentContext, ClientCapabilities, SendRequestOptions } from '@agentclientprotocol/sdk';\n\nfunction cancellable(signal: AbortSignal | undefined): SendRequestOptions | undefined {\n return signal === undefined ? undefined : { cancellationSignal: signal };\n}\n\n/** A JSON-RPC error names its kind in `message` and, from an SDK client, the cause in `data.details`. */\nfunction editorError(error: unknown): Error {\n if (!(error instanceof RequestError)) return error instanceof Error ? error : new Error(String(error));\n const data: unknown = error.data;\n const details =\n typeof data === 'object' && data !== null && 'details' in data && typeof data.details === 'string'\n ? data.details\n : undefined;\n return new Error(details === undefined ? error.message : `${error.message}: ${details}`);\n}\n\nfunction absolute(path: string): void {\n // ACP names files by absolute path; a relative one would be resolved by the editor, not the tool.\n if (!isAbsolute(path)) throw new Error(`the editor is given absolute paths only: ${path}`);\n}\n\n/**\n * The access the editor offered, for the bound session; `undefined` when it offered neither read nor\n * write. `sessionId` is read at each request, so the access follows the session the connection serves.\n */\nexport function editorFileAccess(\n fs: ClientCapabilities['fs'],\n client: AgentContext,\n sessionId: () => string | undefined,\n): IAcpEditorFileAccess | undefined {\n const reads = fs?.readTextFile === true;\n const writes = fs?.writeTextFile === true;\n if (!reads && !writes) return undefined;\n const bound = (): string => {\n const id = sessionId();\n if (id === undefined) throw new Error('the editor has no session to read or write for');\n return id;\n };\n return {\n ...(reads\n ? {\n readTextFile: async (path: string, signal?: AbortSignal): Promise<string> => {\n absolute(path);\n try {\n const reply = await client.request(\n 'fs/read_text_file',\n { sessionId: bound(), path },\n cancellable(signal),\n );\n return reply.content;\n } catch (error) {\n throw editorError(error);\n }\n },\n }\n : {}),\n ...(writes\n ? {\n writeTextFile: async (path: string, content: string, signal?: AbortSignal): Promise<void> => {\n absolute(path);\n try {\n await client.request(\n 'fs/write_text_file',\n { sessionId: bound(), path, content },\n cancellable(signal),\n );\n } catch (error) {\n throw editorError(error);\n }\n },\n }\n : {}),\n };\n}\n","/** A prompt's content blocks as the single text the session receives. */\n\nimport type { ContentBlock } from '@agentclientprotocol/sdk';\n\nexport interface IAcpPromptText {\n /** What the person typed: the text blocks alone. A `/command` is recognised on this. */\n readonly typed: string;\n /** What the session receives: the typed text followed by the context the editor attached. */\n readonly full: string;\n}\n\nconst MIN_FENCE = 3;\n\n/** A backtick fence longer than any run inside the text, so attached content cannot close it early. */\nfunction fence(text: string): string {\n const longest = Math.max(0, ...[...text.matchAll(/`+/g)].map((run) => run[0].length));\n return '`'.repeat(Math.max(MIN_FENCE, longest + 1));\n}\n\n/**\n * Text blocks are the typed message. An embedded text resource is appended as fenced context; a\n * resource link is listed by URI so the model can read it under the session's own permissions. Image\n * and audio blocks are not advertised as supported, so they are ignored.\n */\nexport function promptText(blocks: readonly ContentBlock[]): IAcpPromptText {\n let typed = '';\n const context: string[] = [];\n const links: string[] = [];\n for (const block of blocks) {\n if (block.type === 'text') typed += block.text;\n else if (block.type === 'resource' && 'text' in block.resource) {\n const marks = fence(block.resource.text);\n context.push(\n `Context from ${block.resource.uri}:\\n${marks}\\n${block.resource.text}\\n${marks}`,\n );\n } else if (block.type === 'resource_link') links.push(`- ${block.uri}`);\n }\n if (links.length > 0) context.push(`Referenced resources:\\n${links.join('\\n')}`);\n return { typed, full: context.length === 0 ? typed : `${typed}\\n\\n${context.join('\\n\\n')}` };\n}\n","/** How a session tool call is described to an editor. */\n\nimport type { ToolCallStatus, ToolKind } from '@agentclientprotocol/sdk';\nimport type { IToolState } from '@robota-sdk/agent-interface-session';\n\nconst TOOL_KINDS: Readonly<Record<string, ToolKind>> = {\n Read: 'read',\n Glob: 'search',\n Grep: 'search',\n Edit: 'edit',\n Write: 'edit',\n Shell: 'execute',\n Bash: 'execute',\n WebFetch: 'fetch',\n WebSearch: 'fetch',\n};\n\nconst MAX_TITLE_DETAIL = 120;\n\nexport function acpToolKind(toolName: string): ToolKind {\n return TOOL_KINDS[toolName] ?? 'other';\n}\n\n/** The tool's name plus the one argument the session summarises it by, shortened for a title line. */\nexport function acpToolTitle(toolName: string, detail: string | undefined): string {\n const text = detail?.replace(/\\s+/g, ' ').trim();\n if (!text) return toolName;\n const shown = text.length > MAX_TITLE_DETAIL ? `${text.slice(0, MAX_TITLE_DETAIL - 1)}…` : text;\n return `${toolName}: ${shown}`;\n}\n\n/**\n * The tool's name plus its argument as given. A person deciding whether a call may run sees all of\n * what it would do, so nothing is shortened and line breaks stay.\n */\nexport function acpFullToolTitle(toolName: string, detail: string | undefined): string {\n const text = detail?.trim();\n return text ? `${toolName}: ${text}` : toolName;\n}\n\nexport function acpToolStartTitle(tool: IToolState): string {\n return acpToolTitle(tool.toolName, tool.displayPath ?? tool.firstArg);\n}\n\n/** A call that ended in error or was denied is `failed`; only a success is `completed`. */\nexport function acpToolEndStatus(tool: IToolState): ToolCallStatus {\n return tool.result === 'success' ? 'completed' : 'failed';\n}\n","/** What an editor learns about the session besides a turn: the commands it can offer, and the history of a loaded session. */\n\nimport { acpToolEndStatus, acpToolKind, acpToolStartTitle } from './acp-tool-mapping.js';\n\nimport type { IAcpUpdateStream } from './acp-updates.js';\nimport type { AvailableCommand } from '@agentclientprotocol/sdk';\nimport type { IHistoryDisplaySegment, ISessionCommands } from '@robota-sdk/agent-interface-session';\n\n/**\n * The slash commands an editor may offer: the runtime's commands, then the user-invocable skills\n * whose names no command already uses. A command a client implements itself (its runner is the\n * client) is not offered, because an editor cannot run it and the runtime would not.\n */\nexport function availableCommands(\n session: Partial<Pick<ISessionCommands, 'listCommands' | 'listSkills'>>,\n): AvailableCommand[] {\n const offered: AvailableCommand[] = [];\n const names = new Set<string>();\n const offer = (name: string, description: string, hint: string | undefined): void => {\n if (names.has(name)) return;\n names.add(name);\n offered.push({ name, description, ...(hint ? { input: { hint } } : {}) });\n };\n for (const command of session.listCommands?.() ?? []) {\n if (command.runner !== 'runtime') continue;\n offer(command.name, command.description, command.argumentHint);\n }\n for (const skill of session.listSkills?.() ?? []) {\n if (skill.userInvocable) offer(skill.name, skill.description, skill.argumentHint);\n }\n return offered;\n}\n\n/**\n * A loaded session's history as the editor would have seen it live: what the person typed, what the\n * agent answered, and each finished tool call with how it ended. A call keeps its own id unless that\n * id already named a row of this replay (a provider can reuse ids across turns), because the editor\n * would merge the two rows into one. The connection's allocator also keeps later live calls distinct.\n */\nexport function replayHistory(\n segments: readonly IHistoryDisplaySegment[],\n updates: IAcpUpdateStream,\n nextToolCallId: (preferredId?: string) => string,\n): void {\n for (const segment of segments) {\n if (segment.type === 'text') {\n if (segment.content === '') continue;\n updates.send({\n sessionUpdate: segment.role === 'user' ? 'user_message_chunk' : 'agent_message_chunk',\n content: { type: 'text', text: segment.content },\n });\n continue;\n }\n const { tool } = segment;\n if (tool.internal) continue;\n const toolCallId = nextToolCallId(tool.executionId);\n updates.send({\n sessionUpdate: 'tool_call',\n toolCallId,\n title: acpToolStartTitle(tool),\n kind: acpToolKind(tool.toolName),\n status: acpToolEndStatus(tool),\n });\n }\n}\n","/** How a session permission prompt becomes an ACP permission request and its answer becomes a decision. */\n\nimport { resolve } from 'node:path';\n\nimport { permissionSummaryArgument } from '@robota-sdk/agent-transport';\n\nimport { acpFullToolTitle, acpToolKind } from './acp-tool-mapping.js';\n\nimport type {\n PermissionOption,\n RequestPermissionOutcome,\n ToolCallUpdate,\n} from '@agentclientprotocol/sdk';\nimport type {\n IDiffLine,\n IPermissionRequestEvent,\n TPermissionResultValue,\n} from '@robota-sdk/agent-interface-session';\n\nconst OPTION = {\n allowOnce: 'allow-once',\n allowSession: 'allow-session',\n allowProject: 'allow-project',\n reject: 'reject',\n} as const;\n\nexport function permissionOptions(canPersistProject: boolean): PermissionOption[] {\n return [\n { optionId: OPTION.allowOnce, name: 'Allow once', kind: 'allow_once' },\n { optionId: OPTION.allowSession, name: 'Allow for this session', kind: 'allow_always' },\n ...(canPersistProject\n ? [\n {\n optionId: OPTION.allowProject,\n name: 'Allow for this project',\n kind: 'allow_always' as const,\n },\n ]\n : []),\n { optionId: OPTION.reject, name: 'Reject', kind: 'reject_once' },\n ];\n}\n\nexport function permissionTitle(event: IPermissionRequestEvent): string {\n const title = acpFullToolTitle(\n event.toolName,\n event.diffFile ?? permissionSummaryArgument(event.toolArgs),\n );\n return event.requester ? `${event.requester.label}: ${title}` : title;\n}\n\n/** What the call was asked with, including a shell call's working directory when the session resolved one. */\nfunction permissionRawInput(event: IPermissionRequestEvent): unknown {\n if (event.cwd === undefined || 'cwd' in event.toolArgs) return event.toolArgs;\n return { ...event.toolArgs, cwd: event.cwd };\n}\n\nconst HUNK_HEADER = /^@@ /;\n\n/**\n * The file as it is before and after, rebuilt from the session's diff lines: context lines belong\n * to both sides. A diff with nothing on the old side is a new file, which ACP spells as no old text.\n * The session caps each side and says so in a hunk line placed after the lines it kept; that line\n * stays with its side so a cut-off file is not presented as whole. A hunk header carries no text.\n */\nfunction diffTexts(lines: readonly IDiffLine[]): { oldText?: string; newText: string } {\n const before: string[] = [];\n const after: string[] = [];\n let previous: IDiffLine['type'] = 'context';\n for (const line of lines) {\n if (line.type === 'hunk') {\n if (HUNK_HEADER.test(line.text)) continue;\n if (previous !== 'add') before.push(line.text);\n if (previous !== 'remove') after.push(line.text);\n continue;\n }\n previous = line.type;\n if (line.type !== 'add') before.push(line.text);\n if (line.type !== 'remove') after.push(line.text);\n }\n return {\n ...(before.length > 0 ? { oldText: before.join('\\n') } : {}),\n newText: after.join('\\n'),\n };\n}\n\n/**\n * The tool call a permission request describes. The editor gets what the terminal shows: the\n * arguments whole, and for an edit the diff and the file it concerns. `cwd` roots a path the\n * session reported relative to the workspace, because ACP paths are absolute.\n */\nexport function permissionToolCall(\n event: IPermissionRequestEvent,\n toolCallId: string,\n cwd: string,\n): ToolCallUpdate {\n const path = event.diffFile === undefined ? undefined : resolve(cwd, event.diffFile);\n return {\n toolCallId,\n title: permissionTitle(event),\n kind: acpToolKind(event.toolName),\n status: 'pending',\n rawInput: permissionRawInput(event),\n ...(path !== undefined && event.diffLines !== undefined\n ? { content: [{ type: 'diff' as const, path, ...diffTexts(event.diffLines) }] }\n : {}),\n ...(path !== undefined ? { locations: [{ path }] } : {}),\n };\n}\n\n/**\n * The editor's answer as a session decision. Anything the editor was not offered, or that is not\n * recognised, is a denial: an answer widens permission only by naming an option this request listed.\n */\nexport function permissionDecision(\n outcome: RequestPermissionOutcome,\n offered: readonly PermissionOption[],\n): TPermissionResultValue {\n if (outcome.outcome !== 'selected') return false;\n if (!offered.some((option) => option.optionId === outcome.optionId)) return false;\n switch (outcome.optionId) {\n case OPTION.allowOnce:\n return true;\n case OPTION.allowSession:\n return 'allow-session';\n case OPTION.allowProject:\n return 'allow-project';\n default:\n return false;\n }\n}\n","/**\n * Which session events this transport acts on.\n *\n * This is the ACP owner's own exhaustive policy over the shared event vocabulary, not the WebSocket\n * fan-out's: a new session event fails compilation here until someone decides what an editor sees.\n * The subscription is derived from it, so promoting an event to `stream` or `permission` without a\n * handler fails compilation too.\n */\n\nimport type {\n IInteractiveSessionEvents,\n ISessionEvents,\n TInteractiveEventName,\n} from '@robota-sdk/agent-interface-session';\n\n/**\n * - `stream`: forwarded to the editor as a session update while a prompt runs.\n * - `permission`: becomes a permission request to the editor.\n * - `settle`: ends the turn; the outcome is read from the submission's turn handle, which names its\n * own turn, rather than from the session-wide event, which cannot say which turn ended.\n * - `ignore`: nothing an ACP editor shows. `ask_request` is deliberately here: a session with no\n * listener answers an ask as cancelled, which fails closed because ACP v1 has no question prompt.\n */\nexport type TAcpEventPolicy = 'stream' | 'permission' | 'settle' | 'ignore';\n\nexport const ACP_SESSION_EVENT_POLICY = {\n text_delta: 'stream',\n tool_start: 'stream',\n tool_end: 'stream',\n thinking: 'ignore', // a busy flag with no text; the reasoning text is `thinking_delta`\n thinking_delta: 'stream',\n complete: 'settle',\n error: 'settle',\n interrupted: 'settle',\n permission_request: 'permission',\n ask_request: 'ignore',\n context_update: 'ignore',\n compact: 'ignore',\n skill_activation: 'ignore',\n background_task_event: 'ignore',\n background_job_group_event: 'ignore',\n execution_workspace_event: 'ignore',\n user_message: 'ignore',\n turn_source: 'ignore',\n context_file_refreshed: 'ignore',\n memory_event: 'ignore',\n goal_event: 'ignore',\n plan_event: 'ignore',\n branch_event: 'ignore',\n prompt_resolved: 'ignore',\n ui_intent: 'ignore',\n session_renamed: 'ignore',\n history_cleared: 'ignore',\n session_switched: 'ignore',\n status_changed: 'ignore',\n} as const satisfies Record<TInteractiveEventName, TAcpEventPolicy>;\n\ntype TPolicyMap = typeof ACP_SESSION_EVENT_POLICY;\ntype TEventsWith<P extends TAcpEventPolicy> = {\n [K in keyof TPolicyMap]: TPolicyMap[K] extends P ? K : never;\n}[keyof TPolicyMap];\n\nexport type TAcpSubscribedEvent = TEventsWith<'stream' | 'permission'>;\n\n/** Exactly one handler for every event the policy subscribes, and none for any other. */\nexport type TAcpEventHandlers = {\n readonly [K in TAcpSubscribedEvent]: IInteractiveSessionEvents[K];\n};\n\n/** Subscribe the handlers; returns the unsubscribe. */\nexport function subscribeAcpEvents(\n session: ISessionEvents,\n handlers: TAcpEventHandlers,\n): () => void {\n const names = Object.keys(handlers) as TAcpSubscribedEvent[];\n for (const name of names) session.on(name, handlers[name]);\n return () => {\n for (const name of names) session.off(name, handlers[name]);\n };\n}\n","/** The tool-call rows one turn shows an editor: which session call each row stands for, and when it is done. */\n\nimport { permissionToolCall } from './acp-permission.js';\nimport { acpToolEndStatus, acpToolKind, acpToolStartTitle } from './acp-tool-mapping.js';\n\nimport type { IAcpUpdateStream } from './acp-updates.js';\nimport type { ToolCallUpdate } from '@agentclientprotocol/sdk';\nimport type { IPermissionRequestEvent, IToolState } from '@robota-sdk/agent-interface-session';\n\nexport interface IAcpToolCallTracker {\n start(tool: IToolState): void;\n end(tool: IToolState): void;\n /** The row a permission request asks about; when the request names its call, the row is that call's. */\n requested(event: IPermissionRequestEvent): ToolCallUpdate;\n /** The editor answered; the session decides what happens next, and its tool events say so. */\n answered(event: IPermissionRequestEvent, allowed: boolean): void;\n /** The turn is over: close every row still waiting for a call that never came. */\n finish(): void;\n}\n\nexport interface IAcpToolCallDeps {\n readonly updates: IAcpUpdateStream;\n /** Allocate a new row id, preserving a preferred execution id only when no earlier row used it. */\n readonly nextToolCallId: (preferredId?: string) => string;\n /** The served directory, for paths the session reports relative to it. */\n readonly cwd: string;\n}\n\ninterface IOpenCall {\n readonly toolName: string;\n readonly executionId: string | undefined;\n readonly id: string;\n}\n\n/** A permission request's row, held until the call it names starts or is refused. */\ninterface IAwaitedCall {\n readonly id: string;\n readonly rawInput: unknown;\n /** A background agent's call can run after this prompt, so its row closes with the answer. */\n readonly background: boolean;\n allowed?: boolean;\n}\n\nexport function createToolCallTracker(deps: IAcpToolCallDeps): IAcpToolCallTracker {\n const { updates } = deps;\n const open: IOpenCall[] = [];\n /**\n * The session asks permission before it reports a call, and a refused call is reported ending\n * without ever having started. A request names its call by the execution id the call's own\n * events carry, so the request's row is keyed by it and those events address the same row.\n */\n const awaited = new Map<string, IAwaitedCall>();\n /** Rows of requests that name no call, by request id: nothing else will close them. */\n const unnamed = new Map<string, string>();\n let finished = false;\n\n const take = (executionId: string | undefined): IAwaitedCall | undefined => {\n if (executionId === undefined) return undefined;\n const call = awaited.get(executionId);\n awaited.delete(executionId);\n return call;\n };\n const close = (toolCallId: string, allowed: boolean): void =>\n updates.send({\n sessionUpdate: 'tool_call_update',\n toolCallId,\n status: allowed ? 'completed' : 'failed',\n });\n\n return {\n start(tool) {\n // An internal signal tool is not a call the person made or should see.\n if (tool.internal) return;\n const asked = take(tool.executionId);\n const id = asked?.id ?? deps.nextToolCallId(tool.executionId);\n open.push({ id, toolName: tool.toolName, executionId: tool.executionId });\n const call = {\n toolCallId: id,\n title: acpToolStartTitle(tool),\n kind: acpToolKind(tool.toolName),\n status: 'in_progress',\n } as const;\n if (asked === undefined) updates.send({ sessionUpdate: 'tool_call', ...call });\n else {\n updates.send({ sessionUpdate: 'tool_call_update', ...call, rawInput: asked.rawInput });\n }\n },\n\n end(tool) {\n if (tool.internal) return;\n const status = acpToolEndStatus(tool);\n const index = open.findIndex((call) =>\n tool.executionId !== undefined\n ? call.executionId === tool.executionId\n : call.executionId === undefined && call.toolName === tool.toolName,\n );\n if (index >= 0) {\n const [call] = open.splice(index, 1);\n updates.send({ sessionUpdate: 'tool_call_update', toolCallId: call!.id, status });\n return;\n }\n // A refused call ends with no start; when its permission was asked, that row is the call.\n const asked = take(tool.executionId);\n if (asked !== undefined) {\n updates.send({ sessionUpdate: 'tool_call_update', toolCallId: asked.id, status });\n return;\n }\n // Otherwise give the editor the call, finished, rather than an update for nothing.\n updates.send({\n sessionUpdate: 'tool_call',\n toolCallId: deps.nextToolCallId(tool.executionId),\n title: acpToolStartTitle(tool),\n kind: acpToolKind(tool.toolName),\n status,\n });\n },\n\n requested(event) {\n const { executionId } = event;\n if (executionId === undefined) {\n const call = permissionToolCall(\n event,\n deps.nextToolCallId(`permission-${event.id}`),\n deps.cwd,\n );\n unnamed.set(event.id, call.toolCallId);\n return call;\n }\n // A repeated request and the call's own start/end keep the row allocated for this call.\n const running = open.find((call) => call.executionId === executionId);\n const waiting = awaited.get(executionId);\n const id = running?.id ?? waiting?.id ?? deps.nextToolCallId(executionId);\n const call = permissionToolCall(event, id, deps.cwd);\n if (running === undefined && waiting === undefined) {\n awaited.set(executionId, {\n id,\n rawInput: call.rawInput,\n background: event.requester !== undefined,\n });\n }\n return call;\n },\n\n answered(event, allowed) {\n if (event.executionId !== undefined) {\n // The call's own events say what the answer led to; the answer is kept for a call that\n // may run after this turn.\n const asked = awaited.get(event.executionId);\n if (asked !== undefined) asked.allowed = allowed;\n return;\n }\n const row = unnamed.get(event.id);\n if (row === undefined || finished) return;\n unnamed.delete(event.id);\n close(row, allowed);\n },\n\n finish() {\n finished = true;\n for (const call of awaited.values()) close(call.id, call.background && call.allowed === true);\n for (const row of unnamed.values()) close(row, false);\n awaited.clear();\n unnamed.clear();\n },\n };\n}\n","/**\n * One `session/prompt` that runs a turn: stream the session's events to the editor, ask it about\n * permissions, and settle with a stop reason. A typed message and a `/command` that runs a model\n * turn (a skill) both go through here, so the editor sees the same thing from either.\n */\n\nimport { RequestError } from '@agentclientprotocol/sdk';\nimport { isTurnNotRunError } from '@robota-sdk/agent-interface-session';\n\nimport { permissionDecision, permissionOptions } from './acp-permission.js';\nimport { subscribeAcpEvents } from './acp-session-events.js';\nimport { createToolCallTracker } from './acp-tool-calls.js';\n\nimport type { IAcpTransportSession } from './acp-types.js';\nimport type { IAcpUpdateStream } from './acp-updates.js';\nimport type { AgentContext, PromptResponse } from '@agentclientprotocol/sdk';\nimport type {\n IPermissionRequestEvent,\n ISubmitOptions,\n TDriverId,\n} from '@robota-sdk/agent-interface-session';\n\nexport interface IAcpTurnContext {\n readonly session: IAcpTransportSession;\n readonly sessionId: string;\n readonly client: AgentContext;\n readonly updates: IAcpUpdateStream;\n /** Aborts when the editor cancels the request or the connection closes. */\n readonly signal: AbortSignal;\n readonly driverId: TDriverId;\n readonly surface: ISubmitOptions['surface'];\n /** The served directory; paths the session reports relative to it are made absolute for the editor. */\n readonly cwd: string;\n /** Allocate a new row id, preserving a preferred execution id only when no earlier row used it. */\n readonly nextToolCallId: (preferredId?: string) => string;\n /** Whether the editor sent `session/cancel` for this prompt. */\n readonly wasCancelled: () => boolean;\n readonly report: (error: Error, context: string) => void;\n}\n\nconst CANCELLED: PromptResponse = { stopReason: 'cancelled' };\nconst END_TURN: PromptResponse = { stopReason: 'end_turn' };\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\n/** What a turn's work left behind: whether it was cut short, and the text to show if nothing streamed. */\ninterface IWorkOutcome {\n readonly interrupted: boolean;\n readonly reply: string;\n}\n\ninterface IStreamedWork {\n run(): Promise<IWorkOutcome>;\n /** A failure that was not a cancellation, as the editor's answer: it reports the cause and throws a fixed error. */\n fail(error: unknown): never;\n /** Whether `error` is the work saying it was cancelled. */\n isCancellation?(error: unknown): boolean;\n}\n\nasync function runStreamedTurn(\n turn: IAcpTurnContext,\n work: IStreamedWork,\n): Promise<PromptResponse> {\n const { session, sessionId, client, updates } = turn;\n const tools = createToolCallTracker({\n updates,\n nextToolCallId: turn.nextToolCallId,\n cwd: turn.cwd,\n });\n let streamed = false;\n\n const onPermissionRequest = (event: IPermissionRequestEvent): void => {\n const options = permissionOptions(event.canPersistProjectPermission === true);\n const toolCall = tools.requested(event);\n void (async () => {\n let decision: ReturnType<typeof permissionDecision> = false;\n try {\n const response = await client.request('session/request_permission', {\n sessionId,\n toolCall,\n options,\n });\n decision = permissionDecision(response.outcome, options);\n } catch (error) {\n // A closed connection or a refused request means nobody approved: deny.\n turn.report(asError(error), 'session/request_permission');\n }\n tools.answered(event, decision !== false);\n try {\n session.resolvePermission(event.id, decision, turn.driverId);\n } catch (error) {\n turn.report(asError(error), 'resolvePermission');\n }\n })();\n };\n\n const unsubscribe = subscribeAcpEvents(session, {\n text_delta: (delta) => {\n if (delta === '') return;\n streamed = true;\n updates.text(delta);\n },\n // Reasoning is not the reply: it never marks the turn streamed, so a turn that showed only\n // thoughts still gets its result text.\n thinking_delta: (delta) => {\n if (delta === '') return;\n updates.thought(delta);\n },\n tool_start: tools.start,\n tool_end: tools.end,\n permission_request: onPermissionRequest,\n });\n\n try {\n const outcome = await work.run();\n if (turn.wasCancelled() || turn.signal.aborted || outcome.interrupted) return CANCELLED;\n // Nothing streamed (a provider without deltas, or a command that is not a turn): the text arrives whole.\n if (!streamed && outcome.reply !== '') updates.text(outcome.reply);\n return END_TURN;\n } catch (error) {\n if (turn.wasCancelled() || turn.signal.aborted || work.isCancellation?.(error) === true) {\n return CANCELLED;\n }\n return work.fail(error);\n } finally {\n unsubscribe();\n tools.finish();\n await updates.drained();\n }\n}\n\n/** A typed message: submit it to the session and take the turn's result. */\nexport function runModelTurn(turn: IAcpTurnContext, text: string): Promise<PromptResponse> {\n return runStreamedTurn(turn, {\n async run() {\n const handle = await turn.session.submit(text, undefined, undefined, {\n signal: turn.signal,\n driverId: turn.driverId,\n ...(turn.surface !== undefined ? { surface: turn.surface } : {}),\n });\n const result = await handle.completed;\n return { interrupted: result.interrupted === true, reply: result.response };\n },\n isCancellation: (error) => isTurnNotRunError(error) && error.reason === 'cancelled',\n fail(error) {\n turn.report(asError(error), 'turn');\n // The provider's own text can carry request details; the editor gets a fixed message.\n throw new RequestError(-32603, isTurnNotRunError(error) ? 'Turn was not run' : 'Turn failed');\n },\n });\n}\n\n/**\n * A `/command` prompt: the host runs it and its output is the reply. A command that runs a model\n * turn (a skill) streams that turn and asks about permissions while it runs; its output then\n * repeats the streamed reply, so it is shown only when nothing streamed (`/help`, a message).\n */\nexport function runCommandTurn(\n turn: IAcpTurnContext,\n text: string,\n executeCommand: (text: string, signal: AbortSignal) => Promise<string>,\n): Promise<PromptResponse> {\n return runStreamedTurn(turn, {\n async run() {\n return { interrupted: false, reply: await executeCommand(text, turn.signal) };\n },\n fail(error) {\n turn.report(asError(error), 'command');\n throw new RequestError(-32603, 'Command failed');\n },\n });\n}\n","/** Ordered `session/update` delivery for one prompt. */\n\nimport type { AgentContext, SessionUpdate } from '@agentclientprotocol/sdk';\n\nexport interface IAcpUpdateStream {\n send(update: SessionUpdate): void;\n /** Queue message text for the editor. */\n text(text: string): void;\n /** Queue reasoning text for the editor, shown apart from the reply. */\n thought(text: string): void;\n /** Settles once everything queued has been written or abandoned. Never rejects. */\n drained(): Promise<void>;\n}\n\n/**\n * Updates are written in the order they were queued, and a prompt's reply waits on `drained()`, so\n * the editor has every update before it sees `stopReason`. A failed write is reported once and the\n * rest are dropped: the editor is gone, and the session turn is not the carrier's to fail.\n */\nexport function createUpdateStream(\n client: AgentContext,\n sessionId: string,\n report: (error: Error, context: string) => void,\n): IAcpUpdateStream {\n let tail: Promise<void> = Promise.resolve();\n let failed = false;\n const send = (update: SessionUpdate): void => {\n tail = tail.then(async () => {\n if (failed) return;\n try {\n await client.notify('session/update', { sessionId, update });\n } catch (error) {\n failed = true;\n report(error instanceof Error ? error : new Error(String(error)), 'session/update');\n }\n });\n };\n return {\n send,\n text: (text) => send({ sessionUpdate: 'agent_message_chunk', content: { type: 'text', text } }),\n thought: (text) =>\n send({ sessionUpdate: 'agent_thought_chunk', content: { type: 'text', text } }),\n drained: () => tail,\n };\n}\n","/** The ACP agent handlers for one connection: one attached session, one prompt at a time. */\n\nimport { realpathSync } from 'node:fs';\nimport { isAbsolute, resolve } from 'node:path';\n\nimport { agent, PROTOCOL_VERSION, RequestError } from '@agentclientprotocol/sdk';\nimport { OWNER_DRIVER_ID } from '@robota-sdk/agent-interface-session';\n\nimport { editorFileAccess } from './acp-editor-files.js';\nimport { promptText } from './acp-prompt-text.js';\nimport { availableCommands, replayHistory } from './acp-session-catalog.js';\nimport { runCommandTurn, runModelTurn } from './acp-turn.js';\nimport { createUpdateStream } from './acp-updates.js';\n\nimport type { IAcpTurnContext } from './acp-turn.js';\nimport type { IAcpTransportOptions, IAcpTransportSession } from './acp-types.js';\nimport type {\n AgentApp,\n AgentContext,\n ClientCapabilities,\n McpServer,\n PromptResponse,\n} from '@agentclientprotocol/sdk';\n\n/**\n * JSON-RPC server-error code for a workspace the host has not admitted. Not -32000: ACP assigns\n * that to \"authentication required\", which an editor answers with a sign-in state this agent\n * does not have.\n */\nconst WORKSPACE_NOT_ADMITTED = -32001;\nconst INTERNAL_ERROR = -32603;\nconst COMMANDS_UNAVAILABLE = 'Slash commands are not available in this host.';\n\nconst commandsUnavailable = (): Promise<string> => Promise.resolve(COMMANDS_UNAVAILABLE);\n\nexport interface IAcpAgent {\n readonly app: AgentApp;\n /** Cancel the running prompt, if there is one. */\n cancelActive(): void;\n /** Tell the host the editor's file system is gone, if it was handed one. */\n withdrawEditorFiles(): void;\n}\n\n/** What `session/new` and `session/load` both carry that decides whether the editor may have the session. */\ninterface ISessionRequest {\n readonly cwd: string;\n readonly mcpServers: readonly McpServer[];\n readonly additionalDirectories?: readonly string[] | null;\n}\n\nfunction sameDirectory(requested: string, served: string): boolean {\n if (resolve(requested) === resolve(served)) return true;\n try {\n return realpathSync(requested) === realpathSync(served);\n } catch {\n // allow-fallback: a path that cannot be resolved is not the served directory, and the caller refuses it.\n return false;\n }\n}\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nexport function createAcpAgent(\n session: IAcpTransportSession,\n options: IAcpTransportOptions,\n): IAcpAgent {\n const driverId = options.driverId ?? OWNER_DRIVER_ID;\n const report = (error: Error, context: string): void => options.onDeliveryError?.(error, context);\n let boundSessionId: string | undefined;\n /** A `session/new` or `session/load` is still settling which session this connection serves. */\n let binding = false;\n let active: { cancel(): void } | undefined;\n let toolCalls = 0;\n const usedToolCallIds = new Set<string>();\n const nextToolCallId = (preferredId?: string): string => {\n let id = preferredId;\n while (id === undefined || usedToolCallIds.has(id)) id = `tool-${(toolCalls += 1)}`;\n usedToolCallIds.add(id);\n return id;\n };\n /** The command list the editor last received, so an unchanged list is not sent again. */\n let advertised: string | undefined;\n\n /** Refuse what the editor may not ask for; the same rules hold for a new and a loaded session. */\n const admit = (params: ISessionRequest): void => {\n if (!isAbsolute(params.cwd) || !sameDirectory(params.cwd, options.cwd)) {\n throw RequestError.invalidParams(\n undefined,\n `cwd must be the directory this agent was started in: ${options.cwd}`,\n );\n }\n // Servers or roots named by the editor would widen what the session can reach.\n if (params.mcpServers.length > 0) {\n throw RequestError.invalidParams(undefined, 'mcpServers are not accepted by this agent');\n }\n if ((params.additionalDirectories ?? []).length > 0) {\n throw RequestError.invalidParams(\n undefined,\n 'additionalDirectories are not accepted by this agent',\n );\n }\n const admission = options.admitWorkspace();\n if (!admission.admitted) {\n throw new RequestError(WORKSPACE_NOT_ADMITTED, admission.message, {\n reason: 'workspace-not-trusted',\n });\n }\n };\n\n /** Wait for the attached session to name itself; a session that failed to start is a fixed error. */\n const sessionId = async (context: string): Promise<string> => {\n try {\n // A session that builds itself in the background cannot name itself until it is ready.\n await session.whenInitialized?.();\n return session.getStatusSnapshot().sessionId;\n } catch (error) {\n report(asError(error), context);\n // The cause can name host paths or settings; the editor only needs to know it failed.\n throw new RequestError(INTERNAL_ERROR, 'The session could not be started');\n }\n };\n\n /**\n * Tell the editor which commands it may offer, when they differ from what it last received. The\n * update names the session, so it goes out only once the editor holds that id.\n */\n const advertiseCommands = (client: AgentContext, id: string): void => {\n if (session.listCommands === undefined && session.listSkills === undefined) return;\n let commands: ReturnType<typeof availableCommands>;\n try {\n commands = availableCommands(session);\n } catch (error) {\n report(asError(error), 'available_commands_update');\n return;\n }\n const serialized = JSON.stringify(commands);\n if (serialized === advertised) return;\n advertised = serialized;\n client\n .notify('session/update', {\n sessionId: id,\n update: { sessionUpdate: 'available_commands_update', availableCommands: commands },\n })\n .catch((error: unknown) => report(asError(error), 'available_commands_update'));\n };\n /** After the response that gives the editor the session id has been written. */\n const advertiseAfterReply = (client: AgentContext, id: string): void => {\n setImmediate(() => advertiseCommands(client, id));\n };\n\n /** The file system the editor offered in `initialize`. */\n let editorFs: ClientCapabilities['fs'];\n let editorFilesOffered = false;\n /** Hand the host the editor's file system for the bound session, if the editor offered one. */\n const offerEditorFiles = (client: AgentContext): void => {\n const access = editorFileAccess(editorFs, client, () => boundSessionId);\n if (access === undefined) return;\n editorFilesOffered = true;\n options.onEditorFileAccess?.(access);\n };\n\n const app = agent({ name: options.agentInfo.name })\n .onRequest('initialize', ({ params }) => {\n editorFs = params.clientCapabilities?.fs;\n return {\n // Always the version this agent speaks: the client disconnects if it cannot use it.\n protocolVersion: PROTOCOL_VERSION,\n agentCapabilities: {\n loadSession:\n options.loadSession !== undefined && session.getMessagesDisplay !== undefined,\n promptCapabilities: { image: false, audio: false, embeddedContext: true },\n },\n agentInfo: { name: options.agentInfo.name, version: options.agentInfo.version },\n };\n })\n .onRequest('session/new', async ({ params, client }) => {\n if (boundSessionId !== undefined || binding) {\n throw RequestError.invalidParams(undefined, 'one session per connection');\n }\n admit(params);\n binding = true;\n try {\n boundSessionId = await sessionId('session/new');\n } finally {\n binding = false;\n }\n offerEditorFiles(client);\n advertiseAfterReply(client, boundSessionId);\n return { sessionId: boundSessionId };\n })\n .onRequest('session/load', async ({ params, client }) => {\n const load = options.loadSession;\n if (load === undefined || session.getMessagesDisplay === undefined) {\n throw RequestError.methodNotFound('session/load');\n }\n if (binding) throw RequestError.invalidParams(undefined, 'a session is already being opened');\n // A load replaces this connection's session, so it may not pull the session out from under a turn.\n if (active !== undefined) {\n throw RequestError.invalidParams(undefined, 'a prompt is running');\n }\n admit(params);\n binding = true;\n try {\n let outcome: Awaited<ReturnType<typeof load>>;\n try {\n outcome = await load(params.sessionId);\n } catch (error) {\n report(asError(error), 'session/load');\n throw new RequestError(INTERNAL_ERROR, 'The session could not be loaded');\n }\n if (!outcome.loaded) throw RequestError.invalidParams(undefined, outcome.message);\n boundSessionId = await sessionId('session/load');\n offerEditorFiles(client);\n // The editor sees the conversation it reopened before the load answers.\n const updates = createUpdateStream(client, boundSessionId, report);\n try {\n replayHistory(session.getMessagesDisplay(), updates, nextToolCallId);\n } catch (error) {\n report(asError(error), 'session/load');\n // The session is loaded; only its history could not be shown, and the cause stays local.\n throw new RequestError(INTERNAL_ERROR, 'The session history could not be shown');\n }\n await updates.drained();\n } finally {\n binding = false;\n }\n advertised = undefined;\n advertiseAfterReply(client, boundSessionId);\n return {};\n })\n .onRequest('session/prompt', async ({ params, signal, client }): Promise<PromptResponse> => {\n if (params.sessionId !== boundSessionId || binding) {\n throw RequestError.invalidParams(undefined, 'unknown session');\n }\n if (active !== undefined) {\n throw RequestError.invalidParams(undefined, 'a prompt is already running');\n }\n const text = promptText(params.prompt);\n if (text.full.trim() === '') {\n throw RequestError.invalidParams(undefined, 'the prompt has no text');\n }\n const controller = new AbortController();\n let cancelled = false;\n active = {\n cancel() {\n cancelled = true;\n controller.abort();\n session.abort();\n },\n };\n const turn: IAcpTurnContext = {\n session,\n sessionId: params.sessionId,\n client,\n updates: createUpdateStream(client, params.sessionId, report),\n signal: AbortSignal.any([signal, controller.signal]),\n driverId,\n surface: options.surface ?? 'unknown',\n cwd: options.cwd,\n nextToolCallId,\n wasCancelled: () => cancelled,\n report,\n };\n try {\n return text.typed.trimStart().startsWith('/')\n ? await runCommandTurn(turn, text.typed, options.executeCommand ?? commandsUnavailable)\n : await runModelTurn(turn, text.full);\n } finally {\n active = undefined;\n // A command or a model turn can change the catalog (a plugin reload, a preset): the editor\n // gets the new list, and an unchanged one is not sent again.\n advertiseCommands(client, params.sessionId);\n }\n })\n .onNotification('session/cancel', ({ params }) => {\n if (params.sessionId === boundSessionId) active?.cancel();\n });\n\n return {\n app,\n cancelActive: () => active?.cancel(),\n withdrawEditorFiles: () => {\n if (!editorFilesOffered) return;\n editorFilesOffered = false;\n options.onEditorFileAccess?.(undefined);\n },\n };\n}\n","/**\n * ITransportAdapter that runs the attached session as an Agent Client Protocol (v1) agent, so an\n * editor that launches the process can create a session, prompt it, see streamed output and tool\n * calls, approve or reject permissions, and cancel a turn.\n *\n * The adapter owns the carrier's readiness and teardown; the product shell owns process policy\n * (stdout reservation, workspace trust, signals).\n */\n\nimport { Readable } from 'node:stream';\n\nimport { ndJsonStream } from '@agentclientprotocol/sdk';\nimport { attachStdioCarrier } from '@robota-sdk/agent-transport/node';\n\nimport { createAcpAgent } from './acp-agent.js';\n\nimport type { IAcpTransport, IAcpTransportOptions, IAcpTransportSession } from './acp-types.js';\nimport type { AgentConnection } from '@agentclientprotocol/sdk';\nimport type { Writable } from 'node:stream';\nimport type { ITransportLifecycleError } from '@robota-sdk/agent-interface-transport';\nimport type { IStdioCarrier } from '@robota-sdk/agent-transport/node';\n\n/**\n * A Web sink over the process output. `Writable.toWeb` would leave its own listeners on a stream\n * the transport does not own, and destroy it if the SDK ever aborts the sink; this adds none.\n */\nfunction outputSink(output: Writable): WritableStream<Uint8Array> {\n return new WritableStream<Uint8Array>({\n write: (chunk) =>\n new Promise<void>((resolve, reject) => {\n output.write(chunk, (error) => (error ? reject(error) : resolve()));\n }),\n });\n}\n\nexport function createAcpTransport(options: IAcpTransportOptions): IAcpTransport {\n let session: IAcpTransportSession | null = null;\n let running = false;\n let stopping: Promise<void> | null = null;\n let carrier: IStdioCarrier | null = null;\n let connection: AgentConnection | null = null;\n let cancelActive: (() => void) | null = null;\n let withdrawEditorFiles: (() => void) | null = null;\n let resolveClose: (() => void) | null = null;\n let rejectClose: ((error: Error) => void) | null = null;\n let closePromise: Promise<void> = Promise.resolve();\n const resetClose = (): void => {\n closePromise = new Promise<void>((resolve, reject) => {\n resolveClose = resolve;\n rejectClose = reject;\n });\n // allow-fallback: waitForClose retains the rejection for the process owner; this observer only prevents an early unhandled-rejection warning.\n void closePromise.catch(() => undefined);\n };\n const release = (): void => {\n try {\n // The host's tools go back to the disk before the connection that carried their access closes.\n withdrawEditorFiles?.();\n cancelActive?.();\n connection?.close();\n } finally {\n carrier?.release();\n carrier = null;\n connection = null;\n cancelActive = null;\n withdrawEditorFiles = null;\n }\n };\n const lifecycleError = (code: ITransportLifecycleError['code']): ITransportLifecycleError =>\n Object.assign(new Error(`ACP transport ${code}.`), {\n name: 'TransportLifecycleError' as const,\n code,\n transportName: 'acp',\n });\n\n return {\n name: 'acp',\n lifecycle: Object.freeze({ kind: 'service' }),\n attach(s: IAcpTransportSession) {\n if (running || stopping) throw lifecycleError('already-started');\n session = s;\n resetClose();\n },\n async start() {\n if (!session) throw lifecycleError('not-attached');\n if (running || stopping) throw lifecycleError('already-started');\n running = true;\n try {\n const acpAgent = createAcpAgent(session, options);\n cancelActive = acpAgent.cancelActive;\n withdrawEditorFiles = acpAgent.withdrawEditorFiles;\n // The SDK destroys what it reads when its connection closes; it reads the bridge, never the\n // process input, and a closed or failed end ends the transport instead of leaving it idle.\n const attached = attachStdioCarrier(options.input, options.output, {\n onInputClose: () => resolveClose?.(),\n onOutputClose: () => resolveClose?.(),\n onOutputError: (error) => rejectClose?.(error),\n });\n carrier = attached;\n const opened = acpAgent.app.connect(\n ndJsonStream(\n outputSink(options.output),\n Readable.toWeb(attached.bridge) as ReadableStream<Uint8Array>,\n ),\n );\n connection = opened;\n void opened.closed.then(() => resolveClose?.());\n } catch (error) {\n // A start that failed serves nothing, so a retry must not be told it is already running.\n release();\n running = false;\n throw error;\n }\n },\n stop() {\n if (stopping) return stopping;\n stopping = (async () => {\n try {\n release();\n } finally {\n session = null;\n running = false;\n resolveClose?.();\n }\n })().finally(() => {\n stopping = null;\n });\n return stopping;\n },\n waitForClose() {\n return closePromise;\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robota-sdk/agent-transport-acp",
3
- "version": "3.0.0-beta.96",
3
+ "version": "3.0.1",
4
4
  "description": "Agent Client Protocol (ACP) agent transport for the Robota SDK",
5
5
  "type": "module",
6
6
  "main": "dist/node/index.js",
@@ -41,11 +41,11 @@
41
41
  ],
42
42
  "dependencies": {
43
43
  "@agentclientprotocol/sdk": "1.9.0",
44
- "zod": "^3.25.76",
45
- "@robota-sdk/agent-core": "3.0.0-beta.96",
46
- "@robota-sdk/agent-interface-transport": "3.0.0-beta.96",
47
- "@robota-sdk/agent-interface-session": "3.0.0-beta.96",
48
- "@robota-sdk/agent-transport": "3.0.0-beta.96"
44
+ "zod": "^4.6.7",
45
+ "@robota-sdk/agent-interface-session": "3.0.1",
46
+ "@robota-sdk/agent-transport": "3.0.1",
47
+ "@robota-sdk/agent-core": "3.0.1",
48
+ "@robota-sdk/agent-interface-transport": "3.0.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "^20.19.43",