grix-connector 3.17.0 → 3.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/adapter/acp/acp-adapter.js +10 -10
- package/dist/adapter/acp/session-activity.js +1 -1
- package/dist/adapter/claude/claude-adapter.js +21 -21
- package/dist/adapter/claude/protocol-contract.js +1 -1
- package/dist/adapter/claude/skill-scanner.js +2 -2
- package/dist/adapter/codewhale/codewhale-adapter.js +2 -2
- package/dist/adapter/codex/codex-bridge.js +8 -8
- package/dist/adapter/codex/codex-trust.js +4 -4
- package/dist/adapter/codex/config-overrides.js +1 -0
- package/dist/adapter/codex/raw-capture-hook.js +1 -0
- package/dist/adapter/codex/session-activity.js +3 -3
- package/dist/adapter/opencode/opencode-adapter.js +4 -4
- package/dist/adapter/opencode/opencode-transport.js +4 -4
- package/dist/adapter/openhuman/openhuman-adapter.js +2 -2
- package/dist/adapter/pi/pi-adapter.js +4 -4
- package/dist/adapter/shared/tool-wire-payload.js +1 -0
- package/dist/audit/adapters/acp/kimi-audit-adapter.js +2 -2
- package/dist/audit/adapters/acp/reasonix-audit-adapter.js +1 -1
- package/dist/audit/adapters/claude/claude-audit-adapter.js +1 -1
- package/dist/audit/adapters/codex/codex-audit-adapter.js +1 -1
- package/dist/audit/adapters/pi/pi-audit-adapter.js +1 -1
- package/dist/audit/core/audit-options.js +1 -1
- package/dist/audit/core/audit-session-registry.js +3 -3
- package/dist/audit/core/replay-finalizer.js +1 -1
- package/dist/audit/core/tool-call-audit.js +1 -0
- package/dist/audit/integration/bridge-audit-controller.js +1 -1
- package/dist/audit/producer/ipc-audit-producer.js +1 -1
- package/dist/audit/query/audit-local-actions.js +1 -1
- package/dist/audit/query/audit-replay-query-service.js +1 -1
- package/dist/audit/raw-capture/adapter-hook.js +0 -0
- package/dist/audit/raw-capture/attribution.js +2 -0
- package/dist/audit/raw-capture/body-source.js +1 -0
- package/dist/audit/raw-capture/capture-relay.js +13 -0
- package/dist/audit/raw-capture/redaction.js +1 -0
- package/dist/audit/raw-capture/types.js +1 -0
- package/dist/audit/storage/file-audit-replay-store.js +2 -2
- package/dist/audit/worker/audit-recorder-worker-entry.js +1 -1
- package/dist/bridge/bridge.js +9 -9
- package/dist/bridge/send-controller.js +3 -3
- package/dist/core/aibot/binding-card-meta-cache.js +1 -0
- package/dist/core/aibot/client.js +2 -2
- package/dist/core/aibot/connection-handle.js +1 -1
- package/dist/core/aibot/types.js +1 -1
- package/dist/core/files/file-serve.js +2 -2
- package/dist/core/log/packet-log.js +2 -2
- package/dist/core/mcp/tools.js +1 -1
- package/dist/core/observability/sentry.js +1 -1
- package/dist/core/persistence/session-binding-store.js +1 -1
- package/dist/core/persistence/stop-result-outbox.js +2 -0
- package/dist/core/persistence/terminal-commit-token-store.js +2 -0
- package/dist/core/persistence/terminal-outbox.js +2 -0
- package/dist/core/provider-quota/index.js +1 -1
- package/dist/core/provider-quota/kiro.js +1 -1
- package/dist/core/provider-quota/openai-codex.js +2 -0
- package/dist/core/provider-quota/presentation.js +1 -1
- package/dist/core/provider-quota/providers.js +2 -2
- package/dist/default-skills/grix-agent-dispatch/SKILL.md +17 -14
- package/dist/default-skills/grix-audit-data/SKILL.md +99 -0
- package/dist/default-skills/grix-chat-state/SKILL.md +11 -3
- package/dist/default-skills/grix-query/SKILL.md +4 -2
- package/dist/default-skills/index.js +1 -1
- package/dist/grix.js +7 -6
- package/dist/kimi-plugins/grix-layer/SKILL.md +68 -0
- package/dist/kimi-plugins/grix-layer/SYSTEM.md +16 -0
- package/dist/kimi-plugins/grix-layer/kimi.plugin.json +10 -0
- package/dist/kimi-plugins/grix-layer/references/gateway-architecture.md +153 -0
- package/dist/kimi-plugins/grix-layer/scripts/gateway.mjs +31 -0
- package/dist/kimi-plugins/grix-layer/scripts/lib/architecture-map.mjs +305 -0
- package/dist/kimi-plugins/grix-layer/scripts/lib/downstream-client.mjs +133 -0
- package/dist/kimi-plugins/grix-layer/scripts/lib/navigation-service.mjs +762 -0
- package/dist/kimi-plugins/grix-layer/scripts/lib/normalizers.mjs +202 -0
- package/dist/kimi-plugins/grix-layer/scripts/lib/policy.mjs +78 -0
- package/dist/kimi-plugins/grix-layer/scripts/lib/response-budget.mjs +83 -0
- package/dist/kimi-plugins/grix-layer/scripts/lib/tool-server.mjs +106 -0
- package/dist/kimi-plugins/index.js +3 -0
- package/dist/manager-share-config.js +1 -1
- package/dist/manager.js +2 -2
- package/dist/mcp/stream-http/security.js +1 -1
- package/dist/protocol/event-mapper.js +1 -1
- package/dist/runtime/daemon-lock.js +2 -2
- package/dist/runtime/service-state.js +2 -2
- package/dist/service/log-diagnostics.js +5 -0
- package/dist/service/platform-adapter.js +20 -17
- package/dist/service/process-control.js +1 -1
- package/dist/service/service-manager.js +3 -4
- package/dist/service/service-paths.js +2 -1
- package/openclaw-plugin/index.js +110 -36
- package/openclaw-plugin/skills/grix-query/SKILL.md +6 -2
- package/package.json +8 -3
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import{EventEmitter as S}from"node:events";import{stat as A}from"node:fs/promises";import{existsSync as E,mkdirSync as C,readFileSync as P,writeFileSync as b}from"node:fs";import{join as g,resolve as $,dirname as _}from"node:path";import{homedir as
|
|
2
|
-
`,"utf8"),n.info("opencode-adapter",`MCP config injected into ${s}`);const r=g(t,"opencode","skills"),
|
|
3
|
-
`)){const R=u.match(/opencode server listening on (https?:\/\/[^\s]+)/);if(R){clearTimeout(v),d=!0,this.alive=!0,i(R[1]);return}}}),this.process.stderr?.on("data",a=>{const u=a.toString().trim();u&&n.info("opencode-adapter",`[stderr] ${u}`)}),this.process.on("error",a=>{n.error("opencode-adapter",`Spawn error: ${a.message}`),clearTimeout(v),this.alive=!1,this.transport.disconnect(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`Spawn error: ${a.message}`),this.clearRun()),d?this.stopped||this.emit("exit",1):(d=!0,o(a))}),this.process.on("exit",a=>{n.info("opencode-adapter",`Process exited (code=${a})`),clearTimeout(v),this.alive=!1,this.transport.disconnect(),this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`Process exited (code=${a})`),this.clearRun()),d?this.stopped||this.emit("exit",a??1):(d=!0,o(new Error(`opencode serve exited with code ${a}`)))})})}async ensureOcSession(e){const t=this.sessions.get(e);if(t?.ocSessionId)try{return await this.transport.getSession(t.ocSessionId),t.ocSessionId}catch{n.warn("opencode-adapter",`OC session ${t.ocSessionId} gone, creating new`),t.ocSessionId=""}const s=t?.cwd??this.resolveCwd(),i=this.options.bindingStore?.getAcpSessionId(e);if(i)try{return await this.transport.getSession(i),this.sessions.set(e,{ocSessionId:i,cwd:s}),n.info("opencode-adapter",`Resumed OC session ${i} for aibot=${e}`),i}catch{n.warn("opencode-adapter",`Persisted OC session ${i} gone, creating new`)}const o=await this.transport.createSession({title:`grix-${e.slice(-8)}`});return this.sessions.set(e,{ocSessionId:o.id,cwd:s}),this.options.bindingStore?.setAcpSessionId(e,o.id),n.info("opencode-adapter",`Created OC session ${o.id} for aibot=${e}`),o.id}async sendOcPrompt(e,t){const s=await this.ensureOcSession(e);this.activeRun&&(this.activeRun.ocSessionId=s),await this.transport.sendPromptAsync(s,{parts:[{type:"text",text:t}]})}handleSseEvent(e){if(!this.stopped){if(this.lastSseAt=Date.now(),this.updateToolInFlight(e),this.resetIdleTimer(),this.activeRun){const t=this.auditCaptures.get(this.activeRun.eventId);t&&t.push({sequence:t.length,observedAt:new Date().toISOString(),event:structuredClone(e)})}switch(e.type){case"message.part.updated":{if(!this.activeRun)break;const t=e.part,s=e.delta;if(t.type==="text"){const i=s||t.text;i&&this.acceptAssistantPartText(t.messageID,"text",i)}else t.type==="reasoning"?s&&this.acceptAssistantPartText(t.messageID,"reasoning",s):t.type==="tool"&&this.handleToolPartUpdate(t,s);break}case"message.updated":{if(!this.activeRun)break;const t=e.info;if(this.rememberMessageRole(t),t.role==="assistant"){const s=t;this.callbacks.sendUpdateBindingCard(this.activeRun.sessionId,"composing",this.resolveCwd(),{model_provider:s.providerID,model_id:s.modelID}),s.error&&n.warn("opencode-adapter",`Message error: ${JSON.stringify(s.error)}`)}break}case"session.idle":{if(!this.activeRun)break;e.sessionID===this.activeRun.ocSessionId&&(this.flushTextBuffer(),this.finishRun("responded"));break}case"session.status":{if(!this.activeRun)break;e.sessionID===this.activeRun.ocSessionId&&e.status.type==="idle"&&(this.flushTextBuffer(),this.finishRun("responded"));break}case"session.error":{if(!this.activeRun)break;const t=e.error,s=t?typeof t=="object"&&"message"in t?t.message:JSON.stringify(t):"unknown session error";n.error("opencode-adapter",`Session error: ${s}`),this.flushTextBuffer(),this.finishRun("failed",s);break}case"permission.updated":{this.handlePermission(e.permission);break}case"session.created":case"session.updated":case"session.deleted":case"session.compacted":case"session.diff":case"file.edited":case"server.connected":break;case"server.instance.disposed":{n.warn("opencode-adapter",`Server instance disposed: ${e.directory}`),this.alive=!1,this.transport.disconnect(),this.activeRun&&(this.flushTextBuffer(),this.callbacks.sendEventResult(this.activeRun.eventId,"failed","server instance disposed"),this.clearRun()),this.stopped||this.emit("exit",-1);break}default:break}}}updateToolInFlight(e){if(e.type!=="message.part.updated")return;const t=e.part;if(t?.type!=="tool"||!t.callID)return;const s=t.state?.status;s==="pending"||s==="running"?this.toolCallsInFlight.add(t.callID):(s==="completed"||s==="error")&&this.toolCallsInFlight.delete(t.callID)}rememberMessageRole(e){const t=typeof e.id=="string"?e.id.trim():"";t&&(e.role!=="user"&&e.role!=="assistant"||(this.messageRoles.set(t,e.role),this.flushOrDropPendingParts(t,e.role)))}acceptAssistantPartText(e,t,s){if(!this.activeRun||!s)return;const i=typeof e=="string"?e.trim():"";if(!i)return;const o=this.messageRoles.get(i);if(o==="user")return;if(o==="assistant"){this.emitAssistantPartChunk(t,s);return}const r=this.pendingPartChunks.get(i)??[];r.push({kind:t,value:s}),this.pendingPartChunks.set(i,r)}flushOrDropPendingParts(e,t){const s=this.pendingPartChunks.get(e);if(this.pendingPartChunks.delete(e),!(!s||s.length===0)&&t==="assistant")for(const i of s)this.emitAssistantPartChunk(i.kind,i.value)}emitAssistantPartChunk(e,t){if(!(!this.activeRun||!t)){if(e==="reasoning"){this.callbacks.sendThinking(this.activeRun.eventId,this.activeRun.sessionId,t);return}this.appendText(t)}}clearMessageRoleState(){this.messageRoles.clear(),this.pendingPartChunks.clear()}handleToolPartUpdate(e,t){if(!this.activeRun)return;const s=e.state;switch(s.status){case"pending":case"running":{this.flushTextBuffer();const i=JSON.stringify(s.input??{});this.callbacks.sendToolUse(this.activeRun.eventId,this.activeRun.sessionId,e.tool,i);break}case"completed":{const i=s.output??"";this.callbacks.sendToolResult(this.activeRun.eventId,this.activeRun.sessionId,e.tool,i);break}case"error":{const i=s.error??"unknown tool error";this.callbacks.sendToolResult(this.activeRun.eventId,this.activeRun.sessionId,e.tool,`Error: ${i}`);break}}}handlePermission(e){if(!this.activeRun)return;const{eventId:t,sessionId:s,ocSessionId:i}=this.activeRun;if(this.options.permissionPolicy==="fullAuto"){this.transport.respondPermission(i,e.id,"always").catch(r=>{n.warn("opencode-adapter",`Auto-approve failed: ${r}`)});return}this.stopIdleTimer(),this.pendingPermissions.set(t,{permissionId:e.id,ocSessionId:i});const o=JSON.stringify(e.metadata??{});this.callbacks.sendToolUse(t,s,e.type,o)}handlePermissionAction(e){if(!this.activeRun)return{handled:!1,kind:""};const t=this.pendingPermissions.get(this.activeRun.eventId);if(!t)return{handled:!1,kind:""};const{permissionId:s,ocSessionId:i}=t,r=e.action_type==="exec_approve"||e.action_type==="permission_approve"?"once":"reject";return this.pendingPermissions.delete(this.activeRun.eventId),this.transport.respondPermission(i,s,r).then(()=>{n.info("opencode-adapter",`Permission ${r}: ${s}`),this.resetIdleTimer()}).catch(c=>{n.warn("opencode-adapter",`Permission response failed: ${c}`)}),{handled:!0,kind:"permission"}}startRun(e,t,s=!1){this.activeRun={eventId:e,sessionId:t,ocSessionId:"",chunkSeq:0,clientMsgId:`oc_${++this.clientMsgSeq}_${Date.now()}`,textBuffer:"",flushTimer:null},s&&this.auditCaptures.set(e,[]),this.lastSseAt=Date.now(),this.livenessExtendStartAt=void 0}finishRun(e,t){const s=this.activeRun;if(!s)return;if(this.completedEvents.add(s.eventId),this.completedEvents.size>Y){const c=[...this.completedEvents].slice(-500);this.completedEvents=new Set(c)}this.snapshotAuditBoundary(s),this.activeRun=null,this.toolCallsInFlight.clear(),this.livenessExtendStartAt=void 0,this.clearMessageRoleState(),this.stopComposing(),this.stopIdleTimer();const i=++s.chunkSeq,o=s.clientMsgId;e==="failed"&&t&&this.callbacks.sendRunError(s.eventId,s.sessionId,t);const r=()=>{this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(s.eventId,s.sessionId,o).then(()=>{this.callbacks.sendEventResult(s.eventId,e,t)}).catch(()=>{this.callbacks.sendStreamChunk(s.eventId,s.sessionId,"",i,!0,o),this.callbacks.sendEventResult(s.eventId,e,t)}):(this.callbacks.sendStreamChunk(s.eventId,s.sessionId,"",i,!0,o),this.callbacks.sendEventResult(s.eventId,e,t))};if(s.textBuffer){this.stopTextFlush();for(const c of x(s.textBuffer))s.chunkSeq++,this.callbacks.sendStreamChunk(s.eventId,s.sessionId,c,s.chunkSeq,!1,s.clientMsgId);s.textBuffer=""}r(),this.emit("eventDone",s.eventId)}clearRun(){this.activeRun?.flushTimer&&clearTimeout(this.activeRun.flushTimer);const e=this.activeRun?.eventId;this.activeRun&&this.snapshotAuditBoundary(this.activeRun),this.activeRun=null,this.toolCallsInFlight.clear(),this.livenessExtendStartAt=void 0,this.clearMessageRoleState(),this.stopIdleTimer(),e&&this.emit("eventDone",e)}snapshotAuditBoundary(e){const t=this.auditCaptures.get(e.eventId);t&&(this.completedAuditBoundaries.set(e.eventId,{adapterType:"opencode",providerSessionId:e.ocSessionId||void 0,openCodeEvidence:{events:t}}),this.auditCaptures.delete(e.eventId))}appendText(e){if(this.activeRun){if(this.activeRun.textBuffer+=e,this.activeRun.textBuffer.length>=q){this.flushTextBuffer();return}this.scheduleTextFlush()}}scheduleTextFlush(){!this.activeRun||this.activeRun.flushTimer||(this.activeRun.flushTimer=setTimeout(()=>{this.activeRun&&(this.activeRun.flushTimer=null),this.flushTextBuffer()},X))}flushTextBuffer(){if(this.stopTextFlush(),!(!this.activeRun||!this.activeRun.textBuffer)){for(const e of x(this.activeRun.textBuffer))this.activeRun.chunkSeq++,this.callbacks.sendStreamChunk(this.activeRun.eventId,this.activeRun.sessionId,e,this.activeRun.chunkSeq,!1,this.activeRun.clientMsgId);this.activeRun.textBuffer=""}}stopTextFlush(){this.activeRun?.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null)}startComposing(e){}stopComposing(){}shouldExtendByLiveness(){if(!this.activeRun)return!1;const e=this.process?.pid;if(!this.alive||!e||!w(e))return!1;const t=Date.now(),s=this.lastSseAt>0?t-this.lastSseAt:Number.POSITIVE_INFINITY;if(s<V)return this.livenessExtendStartAt=t,n.info("opencode-adapter",`Liveness check: fresh SSE for ${this.activeRun.eventId} (sseAge=${s}ms, toolsInFlight=${this.toolCallsInFlight.size}), extending`),!0;const o=this.livenessExtendStartAt??t;return this.livenessExtendStartAt===void 0&&(this.livenessExtendStartAt=o),t-o>k?(n.warn("opencode-adapter",`Liveness extension budget exhausted for ${this.activeRun.eventId} (no progress for ${Math.round((t-o)/6e4)}min), allowing idle fail`),!1):(n.info("opencode-adapter",`Liveness check: turn in progress for ${this.activeRun.eventId} (toolsInFlight=${this.toolCallsInFlight.size}, sseAge=${Number.isFinite(s)?`${s}ms`:"n/a"}), extending`),!0)}resetIdleTimer(){this.stopIdleTimer(),!(this.stopped||!this.activeRun)&&(this.idleTimer=setTimeout(()=>{if(this.stopped||!this.activeRun)return;if(this.pendingPermissions.has(this.activeRun.eventId)&&this.shouldExtendByLiveness()){n.info("opencode-adapter",`Idle timeout skipped: pendingPermissions for ${this.activeRun.eventId}, resetting timer`),this.resetIdleTimer();return}if(this.shouldExtendByLiveness()){this.resetIdleTimer();return}const e=this.process?.pid,s=!!(this.alive&&e&&w(e))?`liveness budget exhausted after ${k/6e4}min`:"process not alive";n.error("opencode-adapter",`Idle timeout (${s}, toolsInFlight=${this.toolCallsInFlight.size}) \u2014 emitting exit for respawn`),this.flushTextBuffer(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`idle timeout: ${s}`),this.clearRun()),this.emit("exit",-1)},z))}stopIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}resolveCwd(){const e=(this.config.options??{}).cwd;return typeof e=="string"&&e?e:process.cwd()}buildPromptText(e){let t=e.text;return e.contextMessages&&e.contextMessages.length>0&&(t=e.contextMessages.map(i=>`[context] ${i.senderId}: ${i.content}`).join(`
|
|
1
|
+
import{EventEmitter as S}from"node:events";import{stat as A}from"node:fs/promises";import{existsSync as E,mkdirSync as C,readFileSync as P,writeFileSync as b}from"node:fs";import{join as g,resolve as $,dirname as _}from"node:path";import{homedir as F}from"node:os";import{fileURLToPath as M}from"node:url";import{resolveCommandPath as O,spawnCommand as B,killProcessGroup as w,hasChildProcesses as D}from"../../core/runtime/spawn.js";import{InternalApiServer as N}from"../../core/mcp/internal-api-server.js";import{IdentityInjector as j}from"../shared/identity-injector.js";import{syncDefaultSkillsToDir as L}from"../../default-skills/index.js";import{buildSimpleProbeReport as U}from"../shared/probe-util.js";import{compactToolInputForWire as H}from"../shared/tool-wire-payload.js";import{buildOpencodeConfigContent as J}from"./opencode-config.js";import{OpenCodeTransport as G}from"./opencode-transport.js";import{log as n}from"../../core/log/index.js";import{splitTextForAibotProtocol as T}from"../../core/protocol/index.js";class X extends S{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitError(e){if(this.listenerCount("error")===0){n.warn("opencode-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}const q=200,z=2e3,V=12e4,W=90*1e3,x=1800*1e3,y=3e4,K="127.0.0.1",Y=0,Q=1e3;function k(f){try{return process.kill(f,0),!0}catch(e){return e.code==="EPERM"}}class me extends S{type="opencode";config;callbacks;options;identity;process=null;transport=new G;alive=!1;stopped=!1;internalApi=null;sessions=new Map;activeRun=null;completedEvents=new Set;clientMsgSeq=0;auditCaptures=new Map;completedAuditBoundaries=new Map;idleTimer=null;lastSseAt=0;livenessExtendStartAt;toolCallsInFlight=new Set;toolCardsSent=new Set;messageRoles=new Map;pendingPartChunks=new Map;pendingPermissions=new Map;permissionHandler=null;constructor(e,t,s){super(),this.config=e,this.callbacks=t,this.options=s??{},this.identity=new j("opencode-adapter",t.getAgentProfile)}onAgentProfileChanged(){this.identity.onProfileChanged()}async start(){await this.startInternalApiAndInjectMcp();const e=this.options.hostname??K,t=this.options.port??Y,s=await this.spawnAndWait(e,t),i=this.resolveCwd();await this.transport.connect(s,i),this.transport.on("event",o=>this.handleSseEvent(o)),n.info("opencode-adapter",`Ready (pid=${this.process?.pid}, url=${s})`)}async stop(){if(this.stopped=!0,this.alive=!1,this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.transport.disconnect(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.process){const e=this.process;try{w(e,"SIGTERM")}catch{}const t=setTimeout(()=>{try{w(e,"SIGKILL")}catch{}},5e3);e.on("exit",()=>clearTimeout(t)),this.process=null}}isAlive(){return this.alive}async createSession(e){const t=e.cwd??this.resolveCwd();await this.transport.useDirectory(t);const s=await this.transport.createSession({title:`grix-${Date.now()}`},t);return this.sessions.set(s.id,{ocSessionId:s.id,cwd:t}),n.info("opencode-adapter",`Created OC session ${s.id} for cwd=${t}`),s.id}async resumeSession(e,t){const s=this.sessions.get(e);if(s?.ocSessionId)try{await this.transport.useDirectory(s.cwd),await this.transport.getSession(s.ocSessionId,s.cwd)}catch{n.warn("opencode-adapter",`OC session ${s.ocSessionId} gone, will create new on next prompt`),s.ocSessionId=""}}async destroySession(e){const t=this.sessions.get(e),s=t?t.ocSessionId:e;if(s)try{await this.transport.deleteSession(s,t?.cwd)}catch{}this.sessions.delete(e),this.identity.forgetSession(e)}sendPrompt(e){const t=new X(e.adapterSessionId);return this.sendOcPrompt(e.adapterSessionId,this.buildPromptText(e)).catch(s=>{t.emitError(s instanceof Error?s:new Error(String(s)))}),t}async cancel(e){if(this.activeRun)try{await this.transport.abortSession(this.activeRun.ocSessionId,this.activeRun.cwd)}catch{}}deliverInboundEvent(e){const{event_id:t,session_id:s,content:i}=e;if(this.completedEvents.has(t)){n.info("opencode-adapter",`Dropping duplicate event ${t}`),this.callbacks.sendEventAck(t,s),this.callbacks.sendEventResult(t,"responded");return}if(!this.alive){n.warn("opencode-adapter",`Dropping event ${t}: process not alive`),this.callbacks.sendEventAck(t,s),this.callbacks.sendEventResult(t,"failed","Agent process not running");return}this.activeRun&&this.activeRun.eventId!==t&&(n.info("opencode-adapter",`steer: ${this.activeRun.eventId} -> ${t}`),this.flushTextBuffer(),this.callbacks.sendEventResult(this.activeRun.eventId,"canceled","steered to new event"),this.clearRun()),n.info("opencode-adapter",`prompt: event=${t} session=${s}`),this.callbacks.sendEventAck(t,s),this.startRun(t,s,e.audit?.enabled===!0),this.startComposing(s),this.resetIdleTimer();const o=this.buildPromptTextFromEvent(e);this.sendOcPrompt(s,o).catch(r=>{n.error("opencode-adapter",`prompt_async failed: ${r}`),this.finishRun("failed",String(r))})}deliverStopEvent(e,t){this.activeRun&&this.activeRun.eventId===e&&(n.info("opencode-adapter",`stop: event=${e}`),this.transport.abortSession(this.activeRun.ocSessionId,this.activeRun.cwd).catch(()=>{}),this.flushTextBuffer(),this.finishRun("canceled","stopped by user"))}setPermissionHandler(e){this.permissionHandler=e}async ping(e){return this.transport.healthCheck()}getStatus(){return{alive:this.alive,busy:this.activeRun!==null,sessions:this.sessions.size}}getActiveEventIds(){return this.activeRun?[this.activeRun.eventId]:[]}takeAuditBoundary(e){const t=this.completedAuditBoundaries.get(e);return t&&this.completedAuditBoundaries.delete(e),t}clearActiveEventForShutdown(){this.stopIdleTimer(),this.stopTextFlush(),this.activeRun&&this.snapshotAuditBoundary(this.activeRun),this.activeRun=null}getMcpConfig(){if(!this.internalApi)return null;const e=$(M(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[e,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const e=this.process?.pid;return e?D(e,[e]):!1}async probe(e){const t=this.getStatus();return U(this.config.command||"opencode",{alive:t.alive,busy:t.busy,started:!!this.process},e)}async startInternalApiAndInjectMcp(){try{this.internalApi=new N,this.internalApi.setInvokeHandler(async(d,h,m,p)=>this.callbacks.agentInvoke(d,h,p)),await this.internalApi.start(0),n.info("opencode-adapter",`Internal API started at ${this.internalApi.url}`);const e=this.getMcpConfig(),t=process.env.XDG_CONFIG_HOME||g(F(),".config"),s=g(t,"opencode","opencode.json");C(_(s),{recursive:!0});let i={};try{E(s)&&(i=JSON.parse(P(s,"utf8")))}catch{}const o=i.mcp&&typeof i.mcp=="object"?i.mcp:{};o[e.name]={type:"local",command:[e.command,...e.args??[]],enabled:!0},i.mcp=o,b(s,`${JSON.stringify(i,null,2)}
|
|
2
|
+
`,"utf8"),n.info("opencode-adapter",`MCP config injected into ${s}`);const r=g(t,"opencode","skills"),a=L(r);a.length>0&&n.info("opencode-adapter",`Synced connector skills to ${r}: [${a.join(", ")}]`)}catch(e){n.warn("opencode-adapter",`Failed to inject MCP tools (non-fatal): ${e instanceof Error?e.message:String(e)}`)}}async handleLocalAction(e){const{action_type:t}=e;return t==="exec_approve"||t==="exec_reject"||t==="permission_approve"||t==="permission_reject"?this.handlePermissionAction(e):{handled:!1,kind:""}}bindSession(e,t){if(n.info("opencode-adapter",`bindSession: ${e} \u2192 ${t}`),!this.sessions.has(e))this.sessions.set(e,{ocSessionId:"",cwd:t});else{const s=this.sessions.get(e);s.cwd=t}}async spawnAndWait(e,t){const s=this.resolveCwd();try{if(!(await A(s)).isDirectory())throw new Error(`Bound path is not a directory: ${s}`)}catch(i){throw String(i?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${s}. Please rebind with /grix open <valid-directory>.`):i}return new Promise((i,o)=>{const r=this.config.command||"opencode",d=[...this.config.args??["serve"],`--hostname=${e}`,`--port=${t}`],h={...process.env,...this.config.env},m=J({model:this.options.model,permissionPolicy:this.options.permissionPolicy,provider:this.options.provider});m&&(h.OPENCODE_CONFIG_CONTENT=JSON.stringify(m));const p=O(r,typeof h.PATH=="string"?h.PATH:void 0);n.info("opencode-adapter",`Spawning: ${p} ${d.join(" ")} (cwd=${s})`),this.process=B(p,d,{env:h,cwd:s}).process;let R="",l=!1;const v=setTimeout(()=>{l||(l=!0,o(new Error(`opencode serve did not start after ${y/1e3}s`)))},y);this.process.stdout?.on("data",c=>{if(R+=c.toString(),!l)for(const u of R.split(`
|
|
3
|
+
`)){const I=u.match(/opencode server listening on (https?:\/\/[^\s]+)/);if(I){clearTimeout(v),l=!0,this.alive=!0,i(I[1]);return}}}),this.process.stderr?.on("data",c=>{const u=c.toString().trim();u&&n.info("opencode-adapter",`[stderr] ${u}`)}),this.process.on("error",c=>{n.error("opencode-adapter",`Spawn error: ${c.message}`),clearTimeout(v),this.alive=!1,this.transport.disconnect(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`Spawn error: ${c.message}`),this.clearRun()),l?this.stopped||this.emit("exit",1):(l=!0,o(c))}),this.process.on("exit",c=>{n.info("opencode-adapter",`Process exited (code=${c})`),clearTimeout(v),this.alive=!1,this.transport.disconnect(),this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`Process exited (code=${c})`),this.clearRun()),l?this.stopped||this.emit("exit",c??1):(l=!0,o(new Error(`opencode serve exited with code ${c}`)))})})}async ensureOcSession(e){const t=this.sessions.get(e),s=t?.cwd??this.resolveCwd();if(await this.transport.useDirectory(s),t?.ocSessionId)try{return await this.transport.getSession(t.ocSessionId,s),t.ocSessionId}catch{n.warn("opencode-adapter",`OC session ${t.ocSessionId} gone, creating new`),t.ocSessionId=""}const i=this.options.bindingStore?.getAcpSessionId(e);if(i)try{return await this.transport.getSession(i,s),this.sessions.set(e,{ocSessionId:i,cwd:s}),n.info("opencode-adapter",`Resumed OC session ${i} for aibot=${e}`),i}catch{n.warn("opencode-adapter",`Persisted OC session ${i} gone, creating new`)}const o=await this.transport.createSession({title:`grix-${e.slice(-8)}`},s);return this.sessions.set(e,{ocSessionId:o.id,cwd:s}),this.options.bindingStore?.setAcpSessionId(e,o.id),n.info("opencode-adapter",`Created OC session ${o.id} for aibot=${e}`),o.id}async sendOcPrompt(e,t){const s=this.getSessionCwd(e),i=await this.ensureOcSession(e);this.activeRun&&(this.activeRun.ocSessionId=i,this.activeRun.cwd=s),await this.transport.sendPromptAsync(i,{parts:[{type:"text",text:t}]},s)}handleSseEvent(e){if(!this.stopped){if(this.lastSseAt=Date.now(),this.updateToolInFlight(e),this.resetIdleTimer(),this.activeRun){const t=this.auditCaptures.get(this.activeRun.eventId);t&&t.push({sequence:t.length,observedAt:new Date().toISOString(),event:structuredClone(e)})}switch(e.type){case"message.part.updated":{if(!this.activeRun)break;const t=e.part,s=e.delta;if(t.type==="text"){const i=s||t.text;i&&this.acceptAssistantPartText(t.messageID,"text",i)}else t.type==="reasoning"?s&&this.acceptAssistantPartText(t.messageID,"reasoning",s):t.type==="tool"&&this.handleToolPartUpdate(t,s);break}case"message.updated":{if(!this.activeRun)break;const t=e.info;if(this.rememberMessageRole(t),t.role==="assistant"){const s=t;this.callbacks.sendUpdateBindingCard(this.activeRun.sessionId,"composing",this.activeRun.cwd,{model_provider:s.providerID,model_id:s.modelID}),s.error&&n.warn("opencode-adapter",`Message error: ${JSON.stringify(s.error)}`)}break}case"session.idle":{if(!this.activeRun)break;e.sessionID===this.activeRun.ocSessionId&&(this.flushTextBuffer(),this.finishRun("responded"));break}case"session.status":{if(!this.activeRun)break;e.sessionID===this.activeRun.ocSessionId&&e.status.type==="idle"&&(this.flushTextBuffer(),this.finishRun("responded"));break}case"session.error":{if(!this.activeRun)break;const t=e.error,s=t?typeof t=="object"&&"message"in t?t.message:JSON.stringify(t):"unknown session error";n.error("opencode-adapter",`Session error: ${s}`),this.flushTextBuffer(),this.finishRun("failed",s);break}case"permission.updated":{this.handlePermission(e.permission);break}case"session.created":case"session.updated":case"session.deleted":case"session.compacted":case"session.diff":case"file.edited":case"server.connected":break;case"server.instance.disposed":{n.warn("opencode-adapter",`Server instance disposed: ${e.directory}`),this.alive=!1,this.transport.disconnect(),this.activeRun&&(this.flushTextBuffer(),this.callbacks.sendEventResult(this.activeRun.eventId,"failed","server instance disposed"),this.clearRun()),this.stopped||this.emit("exit",-1);break}default:break}}}updateToolInFlight(e){if(e.type!=="message.part.updated")return;const t=e.part;if(t?.type!=="tool"||!t.callID)return;const s=t.state?.status;s==="pending"||s==="running"?this.toolCallsInFlight.add(t.callID):(s==="completed"||s==="error")&&this.toolCallsInFlight.delete(t.callID)}rememberMessageRole(e){const t=typeof e.id=="string"?e.id.trim():"";t&&(e.role!=="user"&&e.role!=="assistant"||(this.messageRoles.set(t,e.role),this.flushOrDropPendingParts(t,e.role)))}acceptAssistantPartText(e,t,s){if(!this.activeRun||!s)return;const i=typeof e=="string"?e.trim():"";if(!i)return;const o=this.messageRoles.get(i);if(o==="user")return;if(o==="assistant"){this.emitAssistantPartChunk(t,s);return}const r=this.pendingPartChunks.get(i)??[];r.push({kind:t,value:s}),this.pendingPartChunks.set(i,r)}flushOrDropPendingParts(e,t){const s=this.pendingPartChunks.get(e);if(this.pendingPartChunks.delete(e),!(!s||s.length===0)&&t==="assistant")for(const i of s)this.emitAssistantPartChunk(i.kind,i.value)}emitAssistantPartChunk(e,t){if(!(!this.activeRun||!t)){if(e==="reasoning"){this.callbacks.sendThinking(this.activeRun.eventId,this.activeRun.sessionId,t);return}this.appendText(t)}}clearMessageRoleState(){this.messageRoles.clear(),this.pendingPartChunks.clear()}handleToolPartUpdate(e,t){if(!this.activeRun)return;const s=e.state;switch(s.status){case"pending":case"running":{this.sendToolUseOnce(e,s.input);break}case"completed":{this.sendToolUseOnce(e,s.input);break}case"error":{this.sendToolUseOnce(e,s.input),this.callbacks.sendToolResult(this.activeRun.eventId,this.activeRun.sessionId,e.tool,"(failed)");break}}}sendToolUseOnce(e,t){!this.activeRun||this.toolCardsSent.has(e.callID)||(this.flushTextBuffer(),this.toolCardsSent.add(e.callID),this.callbacks.sendToolUse(this.activeRun.eventId,this.activeRun.sessionId,e.tool,H(t??{})))}handlePermission(e){if(!this.activeRun)return;const{eventId:t,sessionId:s,ocSessionId:i}=this.activeRun;if(this.options.permissionPolicy==="fullAuto"){this.transport.respondPermission(i,e.id,"always",this.activeRun.cwd).catch(r=>{n.warn("opencode-adapter",`Auto-approve failed: ${r}`)});return}this.stopIdleTimer(),this.pendingPermissions.set(t,{permissionId:e.id,ocSessionId:i,cwd:this.activeRun.cwd});const o=JSON.stringify(e.metadata??{});this.callbacks.sendToolUse(t,s,e.type,o)}handlePermissionAction(e){if(!this.activeRun)return{handled:!1,kind:""};const t=this.pendingPermissions.get(this.activeRun.eventId);if(!t)return{handled:!1,kind:""};const{permissionId:s,ocSessionId:i,cwd:o}=t,a=e.action_type==="exec_approve"||e.action_type==="permission_approve"?"once":"reject";return this.pendingPermissions.delete(this.activeRun.eventId),this.transport.respondPermission(i,s,a,o).then(()=>{n.info("opencode-adapter",`Permission ${a}: ${s}`),this.resetIdleTimer()}).catch(d=>{n.warn("opencode-adapter",`Permission response failed: ${d}`)}),{handled:!0,kind:"permission"}}startRun(e,t,s=!1){this.activeRun={eventId:e,sessionId:t,ocSessionId:"",cwd:this.getSessionCwd(t),chunkSeq:0,clientMsgId:`oc_${++this.clientMsgSeq}_${Date.now()}`,textBuffer:"",flushTimer:null},this.toolCardsSent.clear(),s&&this.auditCaptures.set(e,[]),this.lastSseAt=Date.now(),this.livenessExtendStartAt=void 0}finishRun(e,t){const s=this.activeRun;if(!s)return;if(this.completedEvents.add(s.eventId),this.completedEvents.size>Q){const a=[...this.completedEvents].slice(-500);this.completedEvents=new Set(a)}this.snapshotAuditBoundary(s),this.activeRun=null,this.toolCallsInFlight.clear(),this.toolCardsSent.clear(),this.livenessExtendStartAt=void 0,this.clearMessageRoleState(),this.stopComposing(),this.stopIdleTimer();const i=++s.chunkSeq,o=s.clientMsgId;e==="failed"&&t&&this.callbacks.sendRunError(s.eventId,s.sessionId,t);const r=()=>{this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(s.eventId,s.sessionId,o).then(()=>{this.callbacks.sendEventResult(s.eventId,e,t)}).catch(a=>{const d=e==="responded"?"failed":e,h=e==="responded"?`final output delivery failed: ${a instanceof Error?a.message:String(a)}`:t;this.callbacks.sendEventResult(s.eventId,d,h)}):(this.callbacks.sendStreamChunk(s.eventId,s.sessionId,"",i,!0,o),this.callbacks.sendEventResult(s.eventId,e,t))};if(s.textBuffer){this.stopTextFlush();for(const a of T(s.textBuffer))s.chunkSeq++,this.callbacks.sendStreamChunk(s.eventId,s.sessionId,a,s.chunkSeq,!1,s.clientMsgId);s.textBuffer=""}r(),this.emit("eventDone",s.eventId)}clearRun(){this.activeRun?.flushTimer&&clearTimeout(this.activeRun.flushTimer);const e=this.activeRun?.eventId;this.activeRun&&this.snapshotAuditBoundary(this.activeRun),this.activeRun=null,this.toolCallsInFlight.clear(),this.toolCardsSent.clear(),this.livenessExtendStartAt=void 0,this.clearMessageRoleState(),this.stopIdleTimer(),e&&this.emit("eventDone",e)}snapshotAuditBoundary(e){const t=this.auditCaptures.get(e.eventId);t&&(this.completedAuditBoundaries.set(e.eventId,{adapterType:"opencode",providerSessionId:e.ocSessionId||void 0,openCodeEvidence:{events:t}}),this.auditCaptures.delete(e.eventId))}appendText(e){if(this.activeRun){if(this.activeRun.textBuffer+=e,this.activeRun.textBuffer.length>=z){this.flushTextBuffer();return}this.scheduleTextFlush()}}scheduleTextFlush(){!this.activeRun||this.activeRun.flushTimer||(this.activeRun.flushTimer=setTimeout(()=>{this.activeRun&&(this.activeRun.flushTimer=null),this.flushTextBuffer()},q))}flushTextBuffer(){if(this.stopTextFlush(),!(!this.activeRun||!this.activeRun.textBuffer)){for(const e of T(this.activeRun.textBuffer))this.activeRun.chunkSeq++,this.callbacks.sendStreamChunk(this.activeRun.eventId,this.activeRun.sessionId,e,this.activeRun.chunkSeq,!1,this.activeRun.clientMsgId);this.activeRun.textBuffer=""}}stopTextFlush(){this.activeRun?.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null)}startComposing(e){}stopComposing(){}shouldExtendByLiveness(){if(!this.activeRun)return!1;const e=this.process?.pid;if(!this.alive||!e||!k(e))return!1;const t=Date.now(),s=this.lastSseAt>0?t-this.lastSseAt:Number.POSITIVE_INFINITY;if(s<W)return this.livenessExtendStartAt=t,n.info("opencode-adapter",`Liveness check: fresh SSE for ${this.activeRun.eventId} (sseAge=${s}ms, toolsInFlight=${this.toolCallsInFlight.size}), extending`),!0;const o=this.livenessExtendStartAt??t;return this.livenessExtendStartAt===void 0&&(this.livenessExtendStartAt=o),t-o>x?(n.warn("opencode-adapter",`Liveness extension budget exhausted for ${this.activeRun.eventId} (no progress for ${Math.round((t-o)/6e4)}min), allowing idle fail`),!1):(n.info("opencode-adapter",`Liveness check: turn in progress for ${this.activeRun.eventId} (toolsInFlight=${this.toolCallsInFlight.size}, sseAge=${Number.isFinite(s)?`${s}ms`:"n/a"}), extending`),!0)}resetIdleTimer(){this.stopIdleTimer(),!(this.stopped||!this.activeRun)&&(this.idleTimer=setTimeout(()=>{if(this.stopped||!this.activeRun)return;if(this.pendingPermissions.has(this.activeRun.eventId)&&this.shouldExtendByLiveness()){n.info("opencode-adapter",`Idle timeout skipped: pendingPermissions for ${this.activeRun.eventId}, resetting timer`),this.resetIdleTimer();return}if(this.shouldExtendByLiveness()){this.resetIdleTimer();return}const e=this.process?.pid,s=!!(this.alive&&e&&k(e))?`liveness budget exhausted after ${x/6e4}min`:"process not alive";n.error("opencode-adapter",`Idle timeout (${s}, toolsInFlight=${this.toolCallsInFlight.size}) \u2014 emitting exit for respawn`),this.flushTextBuffer(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`idle timeout: ${s}`),this.clearRun()),this.emit("exit",-1)},V))}stopIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}resolveCwd(){const e=(this.config.options??{}).cwd;return typeof e=="string"&&e?e:process.cwd()}getSessionCwd(e){return this.sessions.get(e)?.cwd??this.resolveCwd()}buildPromptText(e){let t=e.text;return e.contextMessages&&e.contextMessages.length>0&&(t=e.contextMessages.map(i=>`[context] ${i.senderId}: ${i.content}`).join(`
|
|
4
4
|
`)+`
|
|
5
5
|
|
|
6
6
|
`+t),this.identity.injectOnce(e.adapterSessionId,t)}buildPromptTextFromEvent(e){let t=e.content||"";if(e.context_messages_json)try{const s=JSON.parse(e.context_messages_json);Array.isArray(s)&&s.length>0&&(t=s.map(o=>`[context] ${o.sender_id??"unknown"}: ${o.content}`).join(`
|
|
7
7
|
`)+`
|
|
8
8
|
|
|
9
|
-
`+t)}catch{}return this.identity.injectOnce(e.session_id,t)}}export{
|
|
9
|
+
`+t)}catch{}return this.identity.injectOnce(e.session_id,t)}}export{me as OpenCodeAdapter};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{EventEmitter as u}from"node:events";import{log as l}from"../../core/log/index.js";const
|
|
1
|
+
import{EventEmitter as u}from"node:events";import{log as l}from"../../core/log/index.js";const p=3e4,f=3e3,y=3e4;class w extends u{baseUrl="";directory="";abortController=null;closed=!1;sseConnected=!1;sseGeneration=0;async connect(t,e){this.baseUrl=t,this.directory=e??"",this.closed=!1,await this.subscribeEvents(),l.info("opencode-transport",`connected to ${t}`)}async useDirectory(t){const e=t??"";this.directory===e&&this.sseConnected&&!this.closed||(this.directory=e,!(!this.baseUrl||this.closed)&&(this.abortController&&(this.abortController.abort(),this.abortController=null),this.sseConnected=!1,await this.subscribeEvents(),l.info("opencode-transport",`switched directory to ${e||"<default>"}`)))}async subscribeEvents(){if(this.closed)return;const t=++this.sseGeneration,e=new AbortController;this.abortController=e;const s=new URL("/event",this.baseUrl);this.directory&&s.searchParams.set("directory",this.directory);const r=await fetch(s.toString(),{method:"GET",headers:{Accept:"text/event-stream"},signal:e.signal});if(!r.ok||!r.body)throw new Error(`SSE connect failed: ${r.status}`);this.sseConnected=!0,this.readSseStream(r.body.getReader(),t)}async readSseStream(t,e){const s=new TextDecoder;let r="";try{for(;!this.closed;){const{done:n,value:o}=await t.read();if(n)break;r+=s.decode(o,{stream:!0});const a=r.split(`
|
|
2
2
|
|
|
3
|
-
`);
|
|
4
|
-
`))
|
|
5
|
-
`),
|
|
3
|
+
`);r=a.pop()??"";for(const h of a){const c=this.parseSseFrame(h);if(c)try{this.emit("event",c)}catch(i){const d=i instanceof Error?i.message:String(i);l.warn("opencode-transport",`event handler failed (${c.type}): ${d}`)}}}}catch(n){if(this.closed)return;const o=n instanceof Error?n.message:String(n);if(o.includes("abort"))return;l.warn("opencode-transport",`SSE error: ${o}, reconnecting...`),await this.reconnectSse(e)}finally{e===this.sseGeneration&&(this.sseConnected=!1)}}parseSseFrame(t){let e="",s=[];for(const r of t.split(`
|
|
4
|
+
`))r.startsWith("event:")?e=r.slice(6).trim():r.startsWith("data:")&&s.push(r.slice(5).trimStart());if(s.length===0)return null;try{const r=s.join(`
|
|
5
|
+
`),n=JSON.parse(r);if(e&&n.type!==e&&(n.type=e),n&&typeof n.properties=="object"&&n.properties!==null){const{properties:o,...a}=n;return{...a,...o}}return n}catch{return null}}async reconnectSse(t){if(this.closed||t!==this.sseGeneration)return;const e=f+Math.random()*f;if(await new Promise(s=>setTimeout(s,Math.min(e,y))),!this.closed&&t===this.sseGeneration)try{await this.subscribeEvents()}catch(s){if(this.closed)return;l.warn("opencode-transport",`SSE reconnect failed: ${s instanceof Error?s.message:String(s)}`),this.reconnectSse(this.sseGeneration)}}async request(t,e,s,r){if(this.closed)throw new Error("transport closed");const n=new URL(e,this.baseUrl),o=r?.directory??this.directory;o&&n.searchParams.set("directory",o);const a=new AbortController,h=setTimeout(()=>a.abort(),p);try{const c={method:t,headers:{"Content-Type":"application/json"},signal:a.signal};s!==void 0&&(c.body=JSON.stringify(s));const i=await fetch(n.toString(),c);if(i.status===204)return;const d=await i.json();if(!i.ok)throw new Error(`REST ${t} ${e}: ${i.status} ${JSON.stringify(d)}`);return d}finally{clearTimeout(h)}}async createSession(t,e){return this.request("POST","/session",t??{},{directory:e})}async getSession(t,e){return this.request("GET",`/session/${t}`,void 0,{directory:e})}async deleteSession(t,e){await this.request("DELETE",`/session/${t}`,void 0,{directory:e})}async sendPromptAsync(t,e,s){await this.request("POST",`/session/${t}/prompt_async`,e,{directory:s})}async abortSession(t,e){await this.request("POST",`/session/${t}/abort`,void 0,{directory:e})}async respondPermission(t,e,s,r){await this.request("POST",`/session/${t}/permissions/${e}`,{response:s},{directory:r})}async listProviders(){return(await this.request("GET","/config/providers"))?.providers??[]}async healthCheck(){try{return(await fetch(new URL("/session",this.baseUrl).toString(),{method:"GET",headers:{"Content-Type":"application/json"},signal:AbortSignal.timeout(3e3)})).ok}catch{return!1}}disconnect(){this.closed=!0,this.sseGeneration++,this.abortController&&(this.abortController.abort(),this.abortController=null),this.sseConnected=!1,this.removeAllListeners()}get isConnected(){return this.sseConnected&&!this.closed}}export{w as OpenCodeTransport};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{EventEmitter as f}from"node:events";import{stat as g}from"node:fs/promises";import{mkdirSync as _}from"node:fs";import{homedir as u}from"node:os";import{join as p,resolve as E}from"node:path";import{fileURLToPath as k}from"node:url";import{randomUUID as x}from"node:crypto";import{resolveCommandPath as w,spawnCommand as S,killProcessGroup as v,hasChildProcesses as
|
|
2
|
-
`);n("sqlite3",[s,
|
|
1
|
+
import{EventEmitter as f}from"node:events";import{stat as g}from"node:fs/promises";import{mkdirSync as _}from"node:fs";import{homedir as u}from"node:os";import{join as p,resolve as E}from"node:path";import{fileURLToPath as k}from"node:url";import{randomUUID as x}from"node:crypto";import{resolveCommandPath as w,spawnCommand as S,killProcessGroup as v,hasChildProcesses as y}from"../../core/runtime/spawn.js";import{InternalApiServer as b}from"../../core/mcp/internal-api-server.js";import{syncDefaultSkillsToDir as $}from"../../default-skills/index.js";import{buildSimpleProbeReport as A}from"../shared/probe-util.js";import{OpenHumanTransport as C,readBearerToken as P}from"./openhuman-transport.js";import{IdentityInjector as M}from"../shared/identity-injector.js";import{log as i}from"../../core/log/index.js";import{splitTextForAibotProtocol as L}from"../../core/protocol/index.js";class D extends f{adapterSessionId;constructor(t){super(),this.adapterSessionId=t}emitError(t){if(this.listenerCount("error")===0){i.warn("openhuman-adapter",`Prompt handle error (no listeners): ${t.message}`);return}this.emit("error",t)}async cancel(){}}const F=200,N=2e3,B=12e4,m=1800*1e3,O=5e3,U=500,T=3e4,q=7788,j="127.0.0.1";class rt extends f{type="openhuman";config;callbacks;options;identity;process=null;transport=new C;alive=!1;stopped=!1;internalApi=null;clientId;activeRun=null;completedEvents=new Set;clientMsgSeq=0;idleTimer=null;lastProgressAt=0;constructor(t,e,s){super(),this.config=t,this.callbacks=e,this.options=s??{},this.clientId=`grix-connector-${Date.now()}`,this.identity=new M("openhuman-adapter",e.getAgentProfile)}async start(){await this.startInternalApiAndInjectMcp();const t=this.options.host??j,e=this.options.port??q;await this.spawnProcess(t,e),await this.waitForServerReady(t,e);const s=this.resolveWorkspaceDir(),n=await P(s),r=`http://${t}:${e}`;if(await this.transport.connect(r,n),this.transport.on("event",a=>this.handleSocketEvent(a)),this.options.sessionToken)try{await this.transport.storeSession({token:this.options.sessionToken}),i.info("openhuman-adapter","Session token stored")}catch(a){i.warn("openhuman-adapter",`Failed to store session token: ${a}`)}this.transport.socketId&&(this.clientId=this.transport.socketId),i.info("openhuman-adapter",`Ready (pid=${this.process?.pid}, clientId=${this.clientId})`)}async stop(){if(this.stopped=!0,this.alive=!1,this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.transport.disconnect(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.process){const t=this.process;try{v(t,"SIGTERM")}catch{}const e=setTimeout(()=>{try{v(t,"SIGKILL")}catch{}},5e3);t.on("exit",()=>clearTimeout(e)),this.process=null}}isAlive(){return this.alive}async createSession(t){return this.clientId}async resumeSession(t,e){}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(t){this.identity.forgetSession(t)}sendPrompt(t){const e=new D(t.adapterSessionId),s=this.buildPromptText(t);return this.doWebChat(t.adapterSessionId,s).then(n=>{this.activeRun&&(this.activeRun.requestId=n.request_id)}).catch(n=>{e.emitError(n instanceof Error?n:new Error(String(n)))}),e}async cancel(t){if(this.activeRun)try{await this.transport.webCancel({client_id:this.clientId,thread_id:this.activeRun.threadId})}catch{}}deliverInboundEvent(t){const{event_id:e,session_id:s,content:n}=t,r=this.buildPromptTextFromEvent(t);if(this.completedEvents.has(e)){i.info("openhuman-adapter",`Dropping duplicate event ${e}`),this.callbacks.sendEventAck(e,s),this.callbacks.sendEventResult(e,"responded");return}if(!this.alive){i.warn("openhuman-adapter",`Dropping event ${e}: process not alive`),this.callbacks.sendEventAck(e,s),this.callbacks.sendEventResult(e,"failed","Agent process not running");return}this.activeRun&&this.activeRun.eventId!==e&&(i.info("openhuman-adapter",`steer: ${this.activeRun.eventId} -> ${e}`),this.flushTextBuffer(),this.callbacks.sendEventResult(this.activeRun.eventId,"canceled","steered to new event"),this.clearRun()),i.info("openhuman-adapter",`prompt: event=${e} session=${s}`),this.callbacks.sendEventAck(e,s),this.startRun(e,s,s),this.startComposing(s,e),this.resetIdleTimer(),this.doWebChat(s,r).then(a=>{this.activeRun&&this.activeRun.eventId===e&&(this.activeRun.requestId=a.request_id,a.accepted||(i.warn("openhuman-adapter",`web_chat not accepted: ${a.request_id}`),this.finishRun("failed","Chat request not accepted")))}).catch(a=>{i.error("openhuman-adapter",`web_chat failed: ${a}`),this.finishRun("failed",String(a))})}deliverStopEvent(t,e){this.activeRun&&this.activeRun.eventId===t&&(i.info("openhuman-adapter",`stop: event=${t}`),this.transport.webCancel({client_id:this.clientId,thread_id:this.activeRun.threadId}).catch(()=>{}),this.flushTextBuffer(),this.finishRun("canceled","stopped by user"))}setPermissionHandler(t){}async ping(t){return this.transport.healthCheck()}getStatus(){return{alive:this.alive,busy:this.activeRun!==null,sessions:this.activeRun?1:0}}getActiveEventIds(){return this.activeRun?[this.activeRun.eventId]:[]}clearActiveEventForShutdown(){this.stopIdleTimer(),this.stopTextFlush(),this.activeRun=null}getMcpConfig(){if(!this.internalApi)return null;const t=E(k(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const t=this.process?.pid;return t?y(t,[t]):!1}async probe(t){const e=this.getStatus();return A(this.config.command||"openhuman-core",{alive:e.alive,busy:e.busy,started:!!this.process},t)}async startInternalApiAndInjectMcp(){try{this.internalApi=new b,this.internalApi.setInvokeHandler(async(o,d,H,I)=>this.callbacks.agentInvoke(o,d,I)),await this.internalApi.start(0),i.info("openhuman-adapter",`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=p(u(),".openhuman","users","local","workspace","mcp_clients"),s=p(e,"mcp_clients.db");_(e,{recursive:!0});const{execFileSync:n}=await import("node:child_process"),r=x(),a=JSON.stringify(t.args),h=["CREATE TABLE IF NOT EXISTS mcp_servers (server_id TEXT PRIMARY KEY, qualified_name TEXT NOT NULL, display_name TEXT NOT NULL, description TEXT, icon_url TEXT, command_kind TEXT NOT NULL DEFAULT 'node', command TEXT NOT NULL, args_json TEXT NOT NULL DEFAULT '[]', env_keys_json TEXT NOT NULL DEFAULT '[]', config_json TEXT, installed_at INTEGER NOT NULL, last_connected_at INTEGER);","DELETE FROM mcp_servers WHERE qualified_name = 'grix-connector-tools';",`INSERT INTO mcp_servers (server_id, qualified_name, display_name, description, command_kind, command, args_json, installed_at) VALUES ('${r}', 'grix-connector-tools', 'Grix Connector Tools', 'Grix platform query and management tools', 'node', '${t.command}', '${a.replace(/'/g,"''")}', ${Date.now()});`].join(`
|
|
2
|
+
`);n("sqlite3",[s,h],{timeout:1e4,stdio:"ignore"}),i.info("openhuman-adapter",`MCP server registered in SQLite: ${s}`);const c=p(u(),".openhuman","skills"),l=$(c);l.length>0&&i.info("openhuman-adapter",`Synced connector skills to ${c}: [${l.join(", ")}]`)}catch(t){i.warn("openhuman-adapter",`Failed to inject MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async spawnProcess(t,e){const s=this.config.command||"openhuman-core",n=w(s,typeof process.env.PATH=="string"?process.env.PATH:void 0),r=this.config.args??[],h=r.includes("run")||r.includes("serve")?r:["run","--host",t,"--port",String(e),...r],c={...process.env,...this.config.env},l=this.resolveCwd();i.info("openhuman-adapter",`Spawning: ${n} ${h.join(" ")}`);try{if(!(await g(l)).isDirectory())throw new Error(`Bound path is not a directory: ${l}`)}catch(o){throw String(o?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${l}. Please rebind with /grix open <valid-directory>.`):o}try{this.process=S(n,h,{env:c,cwd:l}).process}catch(o){throw i.error("openhuman-adapter",`Spawn threw: ${o}`),this.alive=!1,o}this.process.on("error",o=>{i.error("openhuman-adapter",`Spawn error: ${o.message}`),this.alive=!1,this.transport.disconnect(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`Spawn error: ${o.message}`),this.clearRun()),this.stopped||this.emit("exit",1)}),this.process.on("exit",o=>{i.info("openhuman-adapter",`Process exited (code=${o})`),this.alive=!1,this.transport.disconnect(),this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`Process exited (code=${o})`),this.clearRun()),this.stopped||this.emit("exit",o??1)}),this.process.stderr?.on("data",o=>{const d=o.toString().trim();d&&i.info("openhuman-adapter",`[stderr] ${d}`)}),this.alive=!0}async waitForServerReady(t,e){const s=`http://${t}:${e}/health`,n=Date.now()+T;for(;Date.now()<n;){try{if((await fetch(s,{signal:AbortSignal.timeout(2e3)})).ok)return}catch{}await new Promise(r=>setTimeout(r,U))}throw new Error(`openhuman-core did not become ready at ${t}:${e} after ${T/1e3}s`)}async doWebChat(t,e){return this.transport.webChat({client_id:this.clientId,thread_id:t,message:e})}handleSocketEvent(t){if(this.stopped||!this.activeRun||t.request_id&&this.activeRun.requestId&&t.request_id!==this.activeRun.requestId)return;this.resetIdleTimer();const e=t.event;switch(e){case"text_delta":{t.delta&&t.delta_kind==="text"&&this.appendText(t.delta);break}case"thinking_delta":{t.delta&&t.delta_kind==="thinking"&&this.activeRun&&this.callbacks.sendThinking(this.activeRun.eventId,this.activeRun.sessionId,t.delta);break}case"tool_call":{if(this.flushTextBuffer(),this.activeRun&&t.tool_name){const s=typeof t.args=="string"?t.args:JSON.stringify(t.args??{});this.callbacks.sendToolUse(this.activeRun.eventId,this.activeRun.sessionId,t.tool_name,s)}break}case"tool_result":{if(this.activeRun&&t.tool_name){const s=t.output??"";this.callbacks.sendToolResult(this.activeRun.eventId,this.activeRun.sessionId,t.tool_name,s)}break}case"chat_done":{i.info("openhuman-adapter",`chat_done request=${t.request_id}`),this.activeRun&&this.activeRun.textBuffer.length===0&&t.full_response&&(this.activeRun.textBuffer=t.full_response),this.flushTextBuffer(),this.finishRun("responded");break}case"chat_error":{if(i.error("openhuman-adapter",`chat_error: ${t.error_type} ${t.message}`),this.flushTextBuffer(),this.activeRun){const s=t.message??t.error_type??"unknown error";this.callbacks.sendRunError(this.activeRun.eventId,this.activeRun.sessionId,s)}this.finishRun("failed",t.message);break}case"chat_segment":case"inference_start":case"iteration_start":case"chat_accepted":break;default:{i.debug("openhuman-adapter",`Unhandled event: ${e}`);break}}}startRun(t,e,s){this.activeRun={eventId:t,sessionId:e,requestId:"",threadId:s,chunkSeq:0,clientMsgId:`oh_${++this.clientMsgSeq}_${Date.now()}`,textBuffer:"",flushTimer:null}}finishRun(t,e){const s=this.activeRun;if(!s)return;if(this.completedEvents.add(s.eventId),this.completedEvents.size>1e3){const a=this.completedEvents.values();for(let c=0;c<500;c++)a.next();const h=[...this.completedEvents].slice(-500);this.completedEvents=new Set(h)}this.activeRun=null,this.emit("eventDone",s.eventId),this.stopComposing(),this.stopIdleTimer();const n=++s.chunkSeq,r=s.clientMsgId;t==="failed"&&e&&this.callbacks.sendRunError(s.eventId,s.sessionId,e),this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(s.eventId,s.sessionId,n,r).then(()=>{this.callbacks.sendEventResult(s.eventId,t,e)}).catch(a=>{const h=t==="responded"?"failed":t,c=t==="responded"?`final output delivery failed: ${a instanceof Error?a.message:String(a)}`:e;this.callbacks.sendEventResult(s.eventId,h,c)}):(this.callbacks.sendStreamChunk(s.eventId,s.sessionId,"",n,!0,r),this.callbacks.sendEventResult(s.eventId,t,e))}clearRun(){this.activeRun?.flushTimer&&clearTimeout(this.activeRun.flushTimer);const t=this.activeRun?.eventId;this.activeRun=null,this.lastProgressAt=0,this.stopIdleTimer(),t&&this.emit("eventDone",t)}appendText(t){if(this.activeRun){if(this.activeRun.textBuffer+=t,this.activeRun.textBuffer.length>=N){this.flushTextBuffer();return}this.scheduleTextFlush()}}scheduleTextFlush(){!this.activeRun||this.activeRun.flushTimer||(this.activeRun.flushTimer=setTimeout(()=>{this.activeRun&&(this.activeRun.flushTimer=null),this.flushTextBuffer()},F))}flushTextBuffer(){if(this.stopTextFlush(),!(!this.activeRun||!this.activeRun.textBuffer)){for(const t of L(this.activeRun.textBuffer))this.activeRun.chunkSeq++,this.callbacks.sendStreamChunk(this.activeRun.eventId,this.activeRun.sessionId,t,this.activeRun.chunkSeq,!1,this.activeRun.clientMsgId);this.activeRun.textBuffer=""}}stopTextFlush(){this.activeRun?.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null)}startComposing(t,e){}stopComposing(){}resetIdleTimer(){this.lastProgressAt=Date.now(),this.armIdleTimer()}armIdleTimer(){if(this.stopIdleTimer(),this.stopped||!this.activeRun)return;const t=this.lastProgressAt+m-Date.now(),e=Math.max(0,Math.min(B,t)),s=this.activeRun.eventId;this.idleTimer=setTimeout(()=>{this.onIdleTimeout(s)},e)}async onIdleTimeout(t){if(this.stopped||this.activeRun?.eventId!==t)return;if(Date.now()-this.lastProgressAt>=m){this.failIdleRun(t,`liveness budget exhausted after ${m/6e4}min`);return}let s=!1;try{s=await this.ping(O)}catch{s=!1}if(!(this.stopped||this.activeRun?.eventId!==t)){if(s){i.info("openhuman-adapter",`Idle timer: transport healthy, extending until liveness cap (${m/6e4}min): ${t}`),this.armIdleTimer();return}this.failIdleRun(t,"transport unavailable")}}failIdleRun(t,e){this.activeRun?.eventId===t&&(i.error("openhuman-adapter",`Idle timeout (${e}) \u2014 emitting exit for respawn`),this.flushTextBuffer(),this.callbacks.sendEventResult(t,"failed",`idle timeout: ${e}`),this.clearRun(),this.emit("exit",-1))}stopIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}resolveWorkspaceDir(){return this.options.workspaceDir?this.options.workspaceDir.replace(/^~/,u()):p(u(),".openhuman")}resolveCwd(){const t=(this.config.options??{}).cwd;return typeof t=="string"&&t?t:process.cwd()}buildPromptText(t){let e=t.text;return t.contextMessages&&t.contextMessages.length>0&&(e=t.contextMessages.map(n=>`[context] ${n.senderId}: ${n.content}`).join(`
|
|
3
3
|
`)+`
|
|
4
4
|
|
|
5
5
|
`+e),this.identity.injectOnce(t.adapterSessionId,e)}buildPromptTextFromEvent(t){let e=t.content||"";if(t.context_messages_json)try{const s=JSON.parse(t.context_messages_json);Array.isArray(s)&&s.length>0&&(e=s.map(r=>`[context] ${r.sender_id??"unknown"}: ${r.content}`).join(`
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import{execFile as _}from"node:child_process";import{promisify as b}from"node:util";import{EventEmitter as v}from"node:events";import{stat as P}from"node:fs/promises";import{mkdirSync as w,writeFileSync as T,unlinkSync as A,statSync as u,readFileSync as k,existsSync as $}from"node:fs";import{join as p,resolve as C}from"node:path";import{fileURLToPath as R}from"node:url";import{tmpdir as M,homedir as m}from"node:os";import{resolveCommandPath as F,spawnCommand as B,killProcessGroup as S,hasChildProcesses as D}from"../../core/runtime/spawn.js";import{InternalApiServer as N}from"../../core/mcp/internal-api-server.js";import{IdentityInjector as G}from"../shared/identity-injector.js";import{syncDefaultSkillsToDir as L}from"../../default-skills/index.js";import{PiTransport as j}from"./pi-transport.js";import{log as a}from"../../core/log/index.js";import{scanSkills as O}from"../claude/skill-scanner.js";import{SessionBindingStore as q}from"../../core/persistence/session-binding-store.js";import{splitTextForAibotProtocol as U}from"../../core/protocol/index.js";import{buildSimpleProbeReport as z}from"../shared/probe-util.js";import{piGrixProviderId as H,removePiGrixProvider as W,writePiGrixProvider as Y}from"./pi-provider-config.js";const I=new Map;function J(){const d=p(m(),".pi","agent","mcp.json");try{if(!$(d))return{};const e=JSON.parse(k(d,"utf8"))?.mcpServers;return!e||typeof e!="object"||Array.isArray(e)?{}:e}catch(t){return a.warn("pi-adapter",`Failed to read Pi user MCP config ${d}: ${t instanceof Error?t.message:String(t)}`),{}}}async function X(d){const t=I.get(d);if(t!==void 0)return t;let e=!1;try{const s=b(_),i=process.platform==="win32",n=i&&/\s/.test(d)&&!d.startsWith('"')?`"${d}"`:d,{stdout:r,stderr:c}=await s(n,["--help"],{timeout:5e3,encoding:"utf-8",...i?{shell:!0}:{}});e=/--mcp-config\b/.test(`${r??""}${c??""}`)}catch{e=!1}return I.set(d,e),a.info("pi-adapter",`Pi --mcp-config \u652F\u6301\u63A2\u6D4B: ${e?"\u652F\u6301":"\u4E0D\u652F\u6301"} (${d})`),e}class V extends v{adapterSessionId;constructor(t){super(),this.adapterSessionId=t}emitDone(t){this.emit("done",t)}emitError(t){if(this.listenerCount("error")===0){a.warn("pi-adapter",`Prompt handle error (no listeners): ${t.message}`);return}this.emit("error",t)}async cancel(){}}const K=12e4,Q=90*1e3,x=1800*1e3;function y(d){try{return process.kill(d,0),!0}catch(t){return t.code==="EPERM"}}const Z=500,tt=2e3,f=3,et=500;function st(d){return new Promise(t=>setTimeout(t,d))}class E extends v{type="pi";config;callbacks;process=null;transport=new j;alive=!1;stopped=!1;internalApi=null;mcpConfigPath=null;identity;piSessionPath=null;activeEventId=null;activeSessionId=null;activeAuditCapture=null;completedAuditBoundaries=new Map;isStreaming=!1;streamSeq=0;clientMsgSeq=0;activeClientMsgId=null;thinkingSeq=0;textBuffer="";emittedTextByIndex=new Map;textFlushTimer=null;idleTimer=null;lastRpcAt=0;livenessExtendStartAt;toolCallsInFlight=new Set;composingSessionId=null;composingInterval=null;doneGuardTimer=null;bindingStore=null;aibotSessionId="";sessionReadyPromise=null;provider;agentName="";constructor(t,e){super(),this.config=t,this.callbacks=e,this.identity=new G("pi-adapter",e.getAgentProfile);const s=t.options??{};this.aibotSessionId=String(s.aibotSessionId??"").trim(),this.agentName=String(s.agentName??"").trim(),this.provider=s.provider,this.bindingStore=s.bindingStore instanceof q?s.bindingStore:null,this.bindingStore&&this.aibotSessionId&&(this.piSessionPath=this.bindingStore.getPiSessionPath(this.aibotSessionId)??null)}async start(){await this.startInternalApi(),await this.spawnPi(),await this.ensureSessionReady(),a.info("pi-adapter",`Ready (pid=${this.process?.pid})`),await this.syncModelBinding()}async syncModelBinding(){if(this.aibotSessionId)for(let t=1;t<=f;t++)try{const[e,s]=await Promise.all([this.transport.send("get_available_models"),this.transport.send("get_state")]),i=e.data?.models;if(!i||i.length===0){if(t<f){await st(et);continue}a.warn("pi-adapter",`syncModelBinding: empty model list after ${f} attempts, giving up`);return}const n=s.data?.model,r={available_models:i.map(c=>({id:c.id,display_name:c.name??c.id,...c.provider?{provider:c.provider}:{}}))};n?.id&&(r.model_id=n.id),n?.provider&&(r.model_provider=n.provider),this.callbacks.sendUpdateBindingCard(this.aibotSessionId,this.isStreaming?"composing":"ready",this.resolveCwd(),r),a.info("pi-adapter",`synced model binding: ${i.length} models, current=${n?.id??"unknown"} (attempt ${t})`);return}catch(e){a.warn("pi-adapter",`syncModelBinding failed (non-fatal): ${e instanceof Error?e.message:String(e)}`);return}}async stop(){if(this.stopped=!0,this.alive=!1,this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.cancelDoneGuard(),this.transport.close(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.mcpConfigPath){try{A(this.mcpConfigPath)}catch{}this.mcpConfigPath=null}if(this.process){const t=this.process;try{S(t,"SIGTERM")}catch{}const e=setTimeout(()=>{try{S(t,"SIGKILL")}catch{}},5e3);t.on("exit",()=>clearTimeout(e)),this.process=null}}isAlive(){return this.alive}async createSession(t){this.sessionReadyPromise=null,await this.createNewSession();const e=this.piSessionPath||`pi-${Date.now()}`;return a.info("pi-adapter",`Session created: ${e} (path=${this.piSessionPath})`),e}async resumeSession(t,e){await this.switchSession()}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(t){this.identity.forgetSession(t),this.piSessionPath=null,this.sessionReadyPromise=null,this.persistPiSessionPath(void 0)}sendPrompt(t){const e=new V(t.adapterSessionId),s=this.buildPromptTextFromRequest(t);return this.ensureSessionReady().then(()=>this.transport.send("prompt",{message:s})).then(i=>{i.success||e.emitDone({status:"failed",error:i.error})}).catch(i=>{e.emitError(i instanceof Error?i:new Error(String(i)))}),e}async cancel(t){try{await this.transport.send("abort")}catch{}}deliverInboundEvent(t){const{event_id:e,session_id:s,content:i}=t,n=this.buildPromptText(t);if(this.isStreaming){if(this.activeEventId&&this.activeEventId!==e){const r=this.activeEventId;a.info("pi-adapter",`steer: cancel ${r} -> ${e}`),this.flushTextBuffer(),this.callbacks.sendEventResult(r,"canceled","steered to new event"),this.activeAuditCapture?.eventId===r&&(this.snapshotAuditBoundary(r),this.emit("eventDone",r))}this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n,streamingBehavior:"steer"}).catch(r=>{a.error("pi-adapter",`steer failed: ${r}`),this.callbacks.sendEventResult(e,"failed",String(r))})}else a.info("pi-adapter",`prompt: event=${e} session=${s}`),this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.isStreaming=!0,this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n}).then(r=>{r.success||(a.error("pi-adapter",`prompt rejected: ${r.error}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",r.error),this.clearActive())}).catch(r=>{a.error("pi-adapter",`prompt error: ${r}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",String(r)),this.clearActive()})}deliverStopEvent(t,e){if(this.activeEventId===t){a.info("pi-adapter",`stop: event=${t}, releasing busy immediately`),this.transport.send("abort").catch(()=>{}),this.flushTextBuffer();const s=this.nextStreamSeq(),i=this.activeClientMsgId??void 0;this.callbacks.sendStreamChunk(t,this.activeSessionId??"","",s,!0,i),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActive()}}async handleLocalAction(t){const e=t.action_id;switch(t.action_type){case"set_model":{const s=t.params??{},i=(s.modelId??s.model_id??"").trim();if(!i)return this.callbacks.sendLocalActionResult(e,"failed",void 0,"invalid_model","model_id is required"),{handled:!0,kind:"set_model_error"};try{let n=s.provider?.trim();return n||(n=(await this.transport.send("get_available_models")).data?.models?.find(l=>l.id===i)?.provider?.trim()),n?(await this.transport.send("set_model",{provider:n,modelId:i}),this.callbacks.sendLocalActionResult(e,"ok",{outcome:"model_set",provider:n,modelId:i}),this.syncModelBinding(),{handled:!0,kind:"set_model"}):(this.callbacks.sendLocalActionResult(e,"failed",void 0,"model_not_found",`No provider for model: ${i}`),{handled:!0,kind:"set_model_error"})}catch(n){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"set_model_error",n instanceof Error?n.message:String(n)),{handled:!0,kind:"set_model_error"}}}case"get_context":try{const s=await this.transport.send("get_state");return this.callbacks.sendLocalActionResult(e,"ok",{state:s}),{handled:!0,kind:"get_context"}}catch(s){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"get_context_error",s instanceof Error?s.message:String(s)),{handled:!1,kind:""}}case"pi_extension_ui_response":return this.transport.sendNoWait({type:"extension_ui_response",...t.params??{}}),this.callbacks.sendLocalActionResult(e,"ok"),{handled:!0,kind:"extension_ui_response"};default:return{handled:!1,kind:""}}}setPermissionHandler(t){}async ping(t){try{return await this.transport.send("get_state"),!0}catch{return!1}}getStatus(){return{alive:this.alive,busy:this.isStreaming,sessions:this.piSessionPath?1:0}}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}takeAuditBoundary(t){const e=this.completedAuditBoundaries.get(t);return e&&this.completedAuditBoundaries.delete(t),e}clearActiveEventForShutdown(){this.stopIdleTimer(),this.activeEventId&&this.snapshotAuditBoundary(this.activeEventId),this.activeEventId=null}getMcpConfig(){if(!this.internalApi)return null;const t=C(R(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const t=this.process?.pid;return t?D(t,[t]):!1}async probe(t){const e=this.getStatus();return{...await z(this.config.command||"pi",{alive:e.alive,busy:e.busy,started:!!this.process},t),session:this.probeSessionRecord()}}probeSessionRecord(){const t=this.piSessionPath;if(!t)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const e=u(t);return{recordPath:t,lastActivityMs:e.mtimeMs,freshMs:Date.now()-e.mtimeMs}}catch{return{recordPath:t,lastActivityMs:null,freshMs:null}}}getSupportedCommands(){return[{name:"model",description:"List or set model",args:"[provider:model_id]"},{name:"interrupt",description:"Interrupt current run"},{name:"status",description:"Show session status"},{name:"skills",description:"List available skills"}]}async execCommand(t,e,s){try{if(!this.alive)return{status:"failed",message:"Pi process not running"};switch(t){case"model":{const i=e.trim();if(i){const c=i.indexOf(":");if(c<1)return{status:"failed",message:"Format: provider:model_id"};const l=i.slice(0,c),h=i.slice(c+1);if(!h)return{status:"failed",message:"Format: provider:model_id"};const o=await this.transport.send("set_model",{provider:l,modelId:h});return o?.success?{status:"ok",message:`Model set to ${l}:${h}`}:{status:"failed",message:`Failed to set model: ${o?.error??"unknown error"}`}}const r=(await this.transport.send("get_available_models"))?.data?.models;return r&&r.length>0?{status:"ok",message:`Available models:
|
|
1
|
+
import{execFile as _}from"node:child_process";import{promisify as b}from"node:util";import{EventEmitter as v}from"node:events";import{stat as P}from"node:fs/promises";import{mkdirSync as w,writeFileSync as T,unlinkSync as A,statSync as u,readFileSync as k,existsSync as $}from"node:fs";import{join as p,resolve as C}from"node:path";import{fileURLToPath as R}from"node:url";import{tmpdir as M,homedir as m}from"node:os";import{resolveCommandPath as F,spawnCommand as B,killProcessGroup as S,hasChildProcesses as D}from"../../core/runtime/spawn.js";import{InternalApiServer as N}from"../../core/mcp/internal-api-server.js";import{IdentityInjector as G}from"../shared/identity-injector.js";import{compactToolInputForWire as L}from"../shared/tool-wire-payload.js";import{syncDefaultSkillsToDir as O}from"../../default-skills/index.js";import{PiTransport as j}from"./pi-transport.js";import{log as a}from"../../core/log/index.js";import{scanSkills as q}from"../claude/skill-scanner.js";import{SessionBindingStore as U}from"../../core/persistence/session-binding-store.js";import{splitTextForAibotProtocol as z}from"../../core/protocol/index.js";import{buildSimpleProbeReport as H}from"../shared/probe-util.js";import{piGrixProviderId as W,removePiGrixProvider as Y,writePiGrixProvider as X}from"./pi-provider-config.js";const I=new Map;function J(){const c=p(m(),".pi","agent","mcp.json");try{if(!$(c))return{};const e=JSON.parse(k(c,"utf8"))?.mcpServers;return!e||typeof e!="object"||Array.isArray(e)?{}:e}catch(t){return a.warn("pi-adapter",`Failed to read Pi user MCP config ${c}: ${t instanceof Error?t.message:String(t)}`),{}}}async function V(c){const t=I.get(c);if(t!==void 0)return t;let e=!1;try{const s=b(_),i=process.platform==="win32",n=i&&/\s/.test(c)&&!c.startsWith('"')?`"${c}"`:c,{stdout:r,stderr:d}=await s(n,["--help"],{timeout:5e3,encoding:"utf-8",...i?{shell:!0}:{}});e=/--mcp-config\b/.test(`${r??""}${d??""}`)}catch{e=!1}return I.set(c,e),a.info("pi-adapter",`Pi --mcp-config \u652F\u6301\u63A2\u6D4B: ${e?"\u652F\u6301":"\u4E0D\u652F\u6301"} (${c})`),e}class K extends v{adapterSessionId;constructor(t){super(),this.adapterSessionId=t}emitDone(t){this.emit("done",t)}emitError(t){if(this.listenerCount("error")===0){a.warn("pi-adapter",`Prompt handle error (no listeners): ${t.message}`);return}this.emit("error",t)}async cancel(){}}const Q=12e4,Z=90*1e3,x=1800*1e3;function y(c){try{return process.kill(c,0),!0}catch(t){return t.code==="EPERM"}}const tt=500,et=2e3,f=3,st=500;function it(c){return new Promise(t=>setTimeout(t,c))}class E extends v{type="pi";config;callbacks;process=null;transport=new j;alive=!1;stopped=!1;internalApi=null;mcpConfigPath=null;identity;piSessionPath=null;activeEventId=null;activeSessionId=null;activeAuditCapture=null;completedAuditBoundaries=new Map;isStreaming=!1;streamSeq=0;clientMsgSeq=0;activeClientMsgId=null;thinkingSeq=0;textBuffer="";emittedTextByIndex=new Map;textFlushTimer=null;idleTimer=null;lastRpcAt=0;livenessExtendStartAt;toolCallsInFlight=new Set;composingSessionId=null;composingInterval=null;doneGuardTimer=null;bindingStore=null;aibotSessionId="";sessionReadyPromise=null;provider;agentName="";constructor(t,e){super(),this.config=t,this.callbacks=e,this.identity=new G("pi-adapter",e.getAgentProfile);const s=t.options??{};this.aibotSessionId=String(s.aibotSessionId??"").trim(),this.agentName=String(s.agentName??"").trim(),this.provider=s.provider,this.bindingStore=s.bindingStore instanceof U?s.bindingStore:null,this.bindingStore&&this.aibotSessionId&&(this.piSessionPath=this.bindingStore.getPiSessionPath(this.aibotSessionId)??null)}async start(){await this.startInternalApi(),await this.spawnPi(),await this.ensureSessionReady(),a.info("pi-adapter",`Ready (pid=${this.process?.pid})`),await this.syncModelBinding()}async syncModelBinding(){if(this.aibotSessionId)for(let t=1;t<=f;t++)try{const[e,s]=await Promise.all([this.transport.send("get_available_models"),this.transport.send("get_state")]),i=e.data?.models;if(!i||i.length===0){if(t<f){await it(st);continue}a.warn("pi-adapter",`syncModelBinding: empty model list after ${f} attempts, giving up`);return}const n=s.data?.model,r={available_models:i.map(d=>({id:d.id,display_name:d.name??d.id,...d.provider?{provider:d.provider}:{}}))};n?.id&&(r.model_id=n.id),n?.provider&&(r.model_provider=n.provider),this.callbacks.sendUpdateBindingCard(this.aibotSessionId,this.isStreaming?"composing":"ready",this.resolveCwd(),r),a.info("pi-adapter",`synced model binding: ${i.length} models, current=${n?.id??"unknown"} (attempt ${t})`);return}catch(e){a.warn("pi-adapter",`syncModelBinding failed (non-fatal): ${e instanceof Error?e.message:String(e)}`);return}}async stop(){if(this.stopped=!0,this.alive=!1,this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.cancelDoneGuard(),this.transport.close(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.mcpConfigPath){try{A(this.mcpConfigPath)}catch{}this.mcpConfigPath=null}if(this.process){const t=this.process;try{S(t,"SIGTERM")}catch{}const e=setTimeout(()=>{try{S(t,"SIGKILL")}catch{}},5e3);t.on("exit",()=>clearTimeout(e)),this.process=null}}isAlive(){return this.alive}async createSession(t){this.sessionReadyPromise=null,await this.createNewSession();const e=this.piSessionPath||`pi-${Date.now()}`;return a.info("pi-adapter",`Session created: ${e} (path=${this.piSessionPath})`),e}async resumeSession(t,e){await this.switchSession()}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(t){this.identity.forgetSession(t),this.piSessionPath=null,this.sessionReadyPromise=null,this.persistPiSessionPath(void 0)}sendPrompt(t){const e=new K(t.adapterSessionId),s=this.buildPromptTextFromRequest(t);return this.ensureSessionReady().then(()=>this.transport.send("prompt",{message:s})).then(i=>{i.success||e.emitDone({status:"failed",error:i.error})}).catch(i=>{e.emitError(i instanceof Error?i:new Error(String(i)))}),e}async cancel(t){try{await this.transport.send("abort")}catch{}}deliverInboundEvent(t){const{event_id:e,session_id:s,content:i}=t,n=this.buildPromptText(t);if(this.isStreaming){if(this.activeEventId&&this.activeEventId!==e){const r=this.activeEventId;a.info("pi-adapter",`steer: cancel ${r} -> ${e}`),this.flushTextBuffer(),this.callbacks.sendEventResult(r,"canceled","steered to new event"),this.activeAuditCapture?.eventId===r&&(this.snapshotAuditBoundary(r),this.emit("eventDone",r))}this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n,streamingBehavior:"steer"}).catch(r=>{a.error("pi-adapter",`steer failed: ${r}`),this.callbacks.sendEventResult(e,"failed",String(r))})}else a.info("pi-adapter",`prompt: event=${e} session=${s}`),this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.isStreaming=!0,this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n}).then(r=>{r.success||(a.error("pi-adapter",`prompt rejected: ${r.error}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",r.error),this.clearActive())}).catch(r=>{a.error("pi-adapter",`prompt error: ${r}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",String(r)),this.clearActive()})}deliverStopEvent(t,e){if(this.activeEventId===t){a.info("pi-adapter",`stop: event=${t}, releasing busy immediately`),this.transport.send("abort").catch(()=>{}),this.flushTextBuffer();const s=this.nextStreamSeq(),i=this.activeClientMsgId??void 0;this.callbacks.sendStreamChunk(t,this.activeSessionId??"","",s,!0,i),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActive()}}async handleLocalAction(t){const e=t.action_id;switch(t.action_type){case"set_model":{const s=t.params??{},i=(s.modelId??s.model_id??"").trim();if(!i)return this.callbacks.sendLocalActionResult(e,"failed",void 0,"invalid_model","model_id is required"),{handled:!0,kind:"set_model_error"};try{let n=s.provider?.trim();return n||(n=(await this.transport.send("get_available_models")).data?.models?.find(l=>l.id===i)?.provider?.trim()),n?(await this.transport.send("set_model",{provider:n,modelId:i}),this.callbacks.sendLocalActionResult(e,"ok",{outcome:"model_set",provider:n,modelId:i}),this.syncModelBinding(),{handled:!0,kind:"set_model"}):(this.callbacks.sendLocalActionResult(e,"failed",void 0,"model_not_found",`No provider for model: ${i}`),{handled:!0,kind:"set_model_error"})}catch(n){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"set_model_error",n instanceof Error?n.message:String(n)),{handled:!0,kind:"set_model_error"}}}case"get_context":try{const s=await this.transport.send("get_state");return this.callbacks.sendLocalActionResult(e,"ok",{state:s}),{handled:!0,kind:"get_context"}}catch(s){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"get_context_error",s instanceof Error?s.message:String(s)),{handled:!1,kind:""}}case"pi_extension_ui_response":return this.transport.sendNoWait({type:"extension_ui_response",...t.params??{}}),this.callbacks.sendLocalActionResult(e,"ok"),{handled:!0,kind:"extension_ui_response"};default:return{handled:!1,kind:""}}}setPermissionHandler(t){}async ping(t){try{return await this.transport.send("get_state"),!0}catch{return!1}}getStatus(){return{alive:this.alive,busy:this.isStreaming,sessions:this.piSessionPath?1:0}}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}takeAuditBoundary(t){const e=this.completedAuditBoundaries.get(t);return e&&this.completedAuditBoundaries.delete(t),e}clearActiveEventForShutdown(){this.stopIdleTimer(),this.activeEventId&&this.snapshotAuditBoundary(this.activeEventId),this.activeEventId=null}getMcpConfig(){if(!this.internalApi)return null;const t=C(R(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const t=this.process?.pid;return t?D(t,[t]):!1}async probe(t){const e=this.getStatus();return{...await H(this.config.command||"pi",{alive:e.alive,busy:e.busy,started:!!this.process},t),session:this.probeSessionRecord()}}probeSessionRecord(){const t=this.piSessionPath;if(!t)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const e=u(t);return{recordPath:t,lastActivityMs:e.mtimeMs,freshMs:Date.now()-e.mtimeMs}}catch{return{recordPath:t,lastActivityMs:null,freshMs:null}}}getSupportedCommands(){return[{name:"model",description:"List or set model",args:"[provider:model_id]"},{name:"interrupt",description:"Interrupt current run"},{name:"status",description:"Show session status"},{name:"skills",description:"List available skills"}]}async execCommand(t,e,s){try{if(!this.alive)return{status:"failed",message:"Pi process not running"};switch(t){case"model":{const i=e.trim();if(i){const d=i.indexOf(":");if(d<1)return{status:"failed",message:"Format: provider:model_id"};const l=i.slice(0,d),h=i.slice(d+1);if(!h)return{status:"failed",message:"Format: provider:model_id"};const o=await this.transport.send("set_model",{provider:l,modelId:h});return o?.success?{status:"ok",message:`Model set to ${l}:${h}`}:{status:"failed",message:`Failed to set model: ${o?.error??"unknown error"}`}}const r=(await this.transport.send("get_available_models"))?.data?.models;return r&&r.length>0?{status:"ok",message:`Available models:
|
|
2
2
|
${r.map(l=>` ${l.provider??"unknown"}:${l.id} (${l.name??l.id})`).join(`
|
|
3
|
-
`)}`,data:r}:{status:"ok",message:"No models available",data:[]}}case"interrupt":return this.isStreaming?(await this.transport.send("abort"),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","interrupted"),this.clearActive(),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"No active run to interrupt"};case"status":{const i=this.getStatus();return{status:"ok",message:`Alive: ${i.alive}, Busy: ${i.busy}, Session: ${this.piSessionPath??"none"}`,data:{alive:i.alive,busy:i.busy,sessions:i.sessions,sessionPath:this.piSessionPath}}}case"skills":{const i=
|
|
4
|
-
`):"No skills found",data:i}}default:return{status:"unsupported",message:`Unknown command: ${t}`}}}catch(i){return{status:"failed",message:i instanceof Error?i.message:String(i)}}}resolveCwd(){if(this.bindingStore&&this.aibotSessionId){const e=this.bindingStore.get(this.aibotSessionId);if(e?.cwd)return e.cwd}const t=(this.config.options??{}).cwd;return typeof t=="string"&&t?t:process.cwd()}async startInternalApi(){try{this.internalApi=new N,this.internalApi.setInvokeHandler(async(r,
|
|
3
|
+
`)}`,data:r}:{status:"ok",message:"No models available",data:[]}}case"interrupt":return this.isStreaming?(await this.transport.send("abort"),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","interrupted"),this.clearActive(),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"No active run to interrupt"};case"status":{const i=this.getStatus();return{status:"ok",message:`Alive: ${i.alive}, Busy: ${i.busy}, Session: ${this.piSessionPath??"none"}`,data:{alive:i.alive,busy:i.busy,sessions:i.sessions,sessionPath:this.piSessionPath}}}case"skills":{const i=q({mode:"pi"}),n=i.map(r=>`- ${r.name}${r.trigger?` (${r.trigger})`:""} [${r.source}]: ${r.description}`);return{status:"ok",message:n.length>0?n.join(`
|
|
4
|
+
`):"No skills found",data:i}}default:return{status:"unsupported",message:`Unknown command: ${t}`}}}catch(i){return{status:"failed",message:i instanceof Error?i.message:String(i)}}}resolveCwd(){if(this.bindingStore&&this.aibotSessionId){const e=this.bindingStore.get(this.aibotSessionId);if(e?.cwd)return e.cwd}const t=(this.config.options??{}).cwd;return typeof t=="string"&&t?t:process.cwd()}async startInternalApi(){try{this.internalApi=new N,this.internalApi.setInvokeHandler(async(r,d,l,h)=>this.callbacks.agentInvoke(r,d,h)),await this.internalApi.start(0),a.info("pi-adapter",`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=J(),s=p(M(),"grix-pi-mcp");w(s,{recursive:!0}),this.mcpConfigPath=p(s,`mcp-${process.pid}-${Date.now()}.json`),T(this.mcpConfigPath,JSON.stringify({mcpServers:{...e,[t.name]:{command:t.command,args:t.args,directTools:!0,lifecycle:"eager"}}}),"utf8"),a.info("pi-adapter",`MCP config written to ${this.mcpConfigPath}`);const i=p(m(),".pi","agent","skills"),n=O(i);n.length>0&&a.info("pi-adapter",`Synced connector skills to ${i}: [${n.join(", ")}]`)}catch(t){a.warn("pi-adapter",`Failed to start MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async spawnPi(){const t=this.config.command||"pi",e=F(t,typeof process.env.PATH=="string"?process.env.PATH:void 0),s=this.config.args??[],n=s.some(o=>o.startsWith("--mode"))?[...s]:["--mode","rpc",...s],r=String(this.config.env?.PI_CODING_AGENT_DIR??"").trim()||p(m(),".pi","agent"),d=W(this.agentName);this.provider?await X(r,d,this.provider)?(n.includes("--provider")||n.push("--provider",d),!n.includes("--model")&&this.provider.model?.trim()&&n.push("--model",this.provider.model.trim())):a.warn("pi-adapter","provider config incomplete (baseUrl/model required); Grix provider not applied"):await Y(r,d).then(o=>{o&&a.info("pi-adapter",`Removed stale Grix provider "${d}" from models.json`)}).catch(o=>{a.warn("pi-adapter",`Failed to clean up Grix provider config: ${o instanceof Error?o.message:String(o)}`)}),this.mcpConfigPath&&!n.some(o=>o==="--mcp-config")&&(await V(e)?n.push("--mcp-config",this.mcpConfigPath):a.warn("pi-adapter","\u5F53\u524D Pi \u4E0D\u652F\u6301 --mcp-config\uFF08\u672A\u68C0\u6D4B\u5230 pi-mcp-adapter \u6269\u5C55\uFF09\uFF0C\u8DF3\u8FC7 MCP \u6CE8\u5165\u4EE5\u907F\u514D\u542F\u52A8\u5931\u8D25"));const l={...process.env,...this.config.env},h=this.resolveCwd();a.info("pi-adapter",`Spawning: ${e} ${n.join(" ")}`);try{if(!(await P(h)).isDirectory())throw new Error(`Bound path is not a directory: ${h}`)}catch(o){throw String(o?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${h}. Please rebind with /grix open <valid-directory>.`):o}try{this.process=B(e,n,{env:l,cwd:h}).process}catch(o){throw a.error("pi-adapter",`PI spawn threw: ${o}`),this.alive=!1,o}this.process.on("error",o=>{a.error("pi-adapter",`Spawn error: ${o.message}`),this.alive=!1,this.transport.close(),this.activeEventId&&(this.callbacks.sendEventResult(this.activeEventId,"failed",`Spawn error: ${o.message}`),this.clearActive()),this.stopped||this.emit("exit",1)}),this.process.on("exit",o=>{a.info("pi-adapter",`PI process exited (code=${o})`),this.alive=!1,this.transport.close(),this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"failed",`PI process exited (code=${o})`),this.isStreaming=!1,this.clearActive(),this.stopped||this.emit("exit",o??1)}),this.process.stderr?.on("data",o=>{const g=o.toString().trim();g&&a.info("pi-adapter",`[pi stderr] ${g}`)}),this.transport.on("event",o=>this.handlePiEvent(o)),this.transport.bind(this.process.stdin,this.process.stdout),this.alive=!0}handlePiEvent(t){if(this.stopped)return;if(this.lastRpcAt=Date.now(),t.type==="tool_execution_start"){const s=t;s.toolCallId&&this.toolCallsInFlight.add(s.toolCallId)}else if(t.type==="tool_execution_end"){const s=t;s.toolCallId&&this.toolCallsInFlight.delete(s.toolCallId)}switch(this.resetIdleTimer(),t.type){case"message_update":{const s=t.assistantMessageEvent;if(!s)break;const i=s.type;if(i==="text_delta"){const n=s.delta;if(n){const r=typeof s.contentIndex=="number"?s.contentIndex:0;this.rememberEmittedText(r,n),this.appendText(n)}}else if(i==="text_end"){const n=s.content,r=typeof s.contentIndex=="number"?s.contentIndex:0;n&&this.appendMissingText(r,n)}else if(i==="thinking_delta"){const n=s.delta;n&&this.activeEventId&&this.activeSessionId&&(this.thinkingSeq++,this.callbacks.sendThinking(this.activeEventId,this.activeSessionId,n))}else if(i==="done"||i==="error"){if(this.flushTextBuffer(),i==="error"&&this.activeEventId&&this.activeSessionId){const n=s.reason??"stream error";this.callbacks.sendRunError(this.activeEventId,this.activeSessionId,String(n),this.nextStreamSeq(),this.activeClientMsgId??void 0)}this.scheduleDoneGuard()}break}case"tool_execution_start":{if(this.cancelDoneGuard(),this.flushTextBuffer(),!this.activeEventId||!this.activeSessionId)break;const s=t;if(s.toolName){const i=L(s.args);this.callbacks.sendToolUse(this.activeEventId,this.activeSessionId,s.toolName,i)}break}case"tool_execution_end":{if(!this.activeEventId||!this.activeSessionId)break;const s=t;s.isError&&this.callbacks.sendToolResult(this.activeEventId,this.activeSessionId,s.toolName,"(failed)");break}case"agent_end":{a.info("pi-adapter",`agent_end event=${this.activeEventId} sessionId=${this.activeSessionId}`),this.cancelDoneGuard(),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.completeActiveEvent();break}case"agent_start":{this.cancelDoneGuard(),this.activeEventId&&(a.info("pi-adapter",`agent_start event=${this.activeEventId}`),this.isStreaming=!0);break}default:break}}appendText(t){if(this.textBuffer+=t,this.textBuffer.length>=et){this.flushTextBuffer();return}this.scheduleTextFlush()}scheduleTextFlush(){this.textFlushTimer||(this.textFlushTimer=setTimeout(()=>{this.textFlushTimer=null,this.flushTextBuffer()},tt))}flushTextBuffer(){this.stopTextFlush(),!(!this.textBuffer||!this.activeEventId||!this.activeSessionId)&&(this.sendTextChunks(this.textBuffer),this.textBuffer="")}sendTextChunks(t){if(!(!this.activeEventId||!this.activeSessionId))for(const e of z(t))this.callbacks.sendStreamChunk(this.activeEventId,this.activeSessionId,e,this.nextStreamSeq(),!1,this.activeClientMsgId??void 0)}stopTextFlush(){this.textFlushTimer&&(clearTimeout(this.textFlushTimer),this.textFlushTimer=null)}buildPromptText(t){let e=t.content||"";if(t.context_messages_json)try{const s=JSON.parse(t.context_messages_json);Array.isArray(s)&&s.length>0&&(e=s.map(n=>`[context] ${n.sender_id??"unknown"}: ${n.content}`).join(`
|
|
5
5
|
`)+`
|
|
6
6
|
|
|
7
7
|
`+e)}catch{}return this.identity.injectOnce(t.session_id,e)}buildPromptTextFromRequest(t){let e=t.text;return t.contextMessages&&t.contextMessages.length>0&&(e=t.contextMessages.map(i=>`[context] ${i.senderId}: ${i.content}`).join(`
|
|
8
8
|
`)+`
|
|
9
9
|
|
|
10
|
-
`+e),this.identity.injectOnce(t.adapterSessionId,e)}clearActive(){const t=this.activeEventId;t&&this.snapshotAuditBoundary(t),this.activeEventId=null,this.activeSessionId=null,this.activeClientMsgId=null,this.textBuffer="",this.emittedTextByIndex.clear(),this.toolCallsInFlight.clear(),this.livenessExtendStartAt=void 0,this.stopIdleTimer(),t&&this.emit("eventDone",t)}beginAuditCapture(t){if(this.activeAuditCapture?.eventId===t.event_id||(this.activeAuditCapture=null,t.audit?.enabled!==!0))return;const e=this.piSessionPath??void 0;let s;if(e)try{s=u(e).size}catch{s=0}this.activeAuditCapture={eventId:t.event_id,...e?{transcriptPath:e}:{},...s===void 0?{}:{transcriptStartOffset:s}}}snapshotAuditBoundary(t){const e=this.activeAuditCapture;if(!e||e.eventId!==t)return;this.activeAuditCapture=null;const s={adapterType:"pi"};if(e.transcriptPath){let i=e.transcriptStartOffset??0;try{i=u(e.transcriptPath).size}catch{}s.transcriptPath=e.transcriptPath,s.transcriptStartOffset=e.transcriptStartOffset??0,s.transcriptEndOffset=i,s.providerSessionId=this.piSessionPath??e.transcriptPath}this.completedAuditBoundaries.set(t,s)}resetRunStreamState(){this.streamSeq=0,this.thinkingSeq=0,this.textBuffer="",this.emittedTextByIndex.clear(),this.activeClientMsgId=`pi_${++this.clientMsgSeq}_${Date.now()}`}nextStreamSeq(){return this.streamSeq++,this.streamSeq}completeActiveEvent(){if(!this.activeEventId){this.clearActive();return}const t=this.activeEventId,e=this.activeSessionId??"",s=this.activeClientMsgId??void 0;this.clearActive(),this.finalizeEvent(t,e,s)}finalizeEvent(t,e,s){const i=this.nextStreamSeq();this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(t,e,s).then(()=>{this.callbacks.sendEventResult(t,"responded"),a.info("pi-adapter",`event completed (reliable) event=${t}`)}).catch(n=>{a.error("pi-adapter",`finalStreamChunk ACK failed event=${t}: ${n}`),this.callbacks.
|
|
10
|
+
`+e),this.identity.injectOnce(t.adapterSessionId,e)}clearActive(){const t=this.activeEventId;t&&this.snapshotAuditBoundary(t),this.activeEventId=null,this.activeSessionId=null,this.activeClientMsgId=null,this.textBuffer="",this.emittedTextByIndex.clear(),this.toolCallsInFlight.clear(),this.livenessExtendStartAt=void 0,this.stopIdleTimer(),t&&this.emit("eventDone",t)}beginAuditCapture(t){if(this.activeAuditCapture?.eventId===t.event_id||(this.activeAuditCapture=null,t.audit?.enabled!==!0))return;const e=this.piSessionPath??void 0;let s;if(e)try{s=u(e).size}catch{s=0}this.activeAuditCapture={eventId:t.event_id,...e?{transcriptPath:e}:{},...s===void 0?{}:{transcriptStartOffset:s}}}snapshotAuditBoundary(t){const e=this.activeAuditCapture;if(!e||e.eventId!==t)return;this.activeAuditCapture=null;const s={adapterType:"pi"};if(e.transcriptPath){let i=e.transcriptStartOffset??0;try{i=u(e.transcriptPath).size}catch{}s.transcriptPath=e.transcriptPath,s.transcriptStartOffset=e.transcriptStartOffset??0,s.transcriptEndOffset=i,s.providerSessionId=this.piSessionPath??e.transcriptPath}this.completedAuditBoundaries.set(t,s)}resetRunStreamState(){this.streamSeq=0,this.thinkingSeq=0,this.textBuffer="",this.emittedTextByIndex.clear(),this.activeClientMsgId=`pi_${++this.clientMsgSeq}_${Date.now()}`}nextStreamSeq(){return this.streamSeq++,this.streamSeq}completeActiveEvent(){if(!this.activeEventId){this.clearActive();return}const t=this.activeEventId,e=this.activeSessionId??"",s=this.activeClientMsgId??void 0;this.clearActive(),this.finalizeEvent(t,e,s)}finalizeEvent(t,e,s){const i=this.nextStreamSeq();this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(t,e,s).then(()=>{this.callbacks.sendEventResult(t,"responded"),a.info("pi-adapter",`event completed (reliable) event=${t}`)}).catch(n=>{a.error("pi-adapter",`finalStreamChunk ACK failed event=${t}: ${n}`),this.callbacks.sendEventResult(t,"failed",`final output delivery failed: ${n instanceof Error?n.message:String(n)}`),a.info("pi-adapter",`event failed (final delivery) event=${t}`)}):(this.callbacks.sendStreamChunk(t,e,"",i,!0,s),this.callbacks.sendEventResult(t,"responded"),a.info("pi-adapter",`event completed event=${t}`))}rememberEmittedText(t,e){this.emittedTextByIndex.set(t,(this.emittedTextByIndex.get(t)??"")+e)}appendMissingText(t,e){const s=this.emittedTextByIndex.get(t)??"";if(e!==s){if(e.startsWith(s)){const i=e.slice(s.length);i&&(this.rememberEmittedText(t,i),this.appendText(i));return}if(!s){this.rememberEmittedText(t,e),this.appendText(e);return}a.info("pi-adapter",`text_end content mismatch at index=${t}, keeping streamed deltas`)}}startComposing(t,e){this.stopComposing(),this.composingSessionId=t,this.callbacks.sendSessionActivitySet(t,"composing",!0,{ref_event_id:e,ttl_ms:12e4}),this.composingInterval=setInterval(()=>{this.composingSessionId&&this.callbacks.sendSessionActivitySet(this.composingSessionId,"composing",!0,{ttl_ms:12e4})},3e4)}stopComposing(){this.composingInterval&&(clearInterval(this.composingInterval),this.composingInterval=null),this.composingSessionId&&(this.callbacks.sendSessionActivitySet(this.composingSessionId,"composing",!1),this.composingSessionId=null)}static DONE_GUARD_MS=5e3;scheduleDoneGuard(){this.cancelDoneGuard(),!(!this.activeEventId||!this.activeSessionId)&&(this.doneGuardTimer=setTimeout(()=>{this.doneGuardTimer=null,!(!this.activeEventId||!this.isStreaming)&&(a.info("pi-adapter",`done guard triggered \u2014 no agent_end received, ending event=${this.activeEventId}`),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.completeActiveEvent())},E.DONE_GUARD_MS))}cancelDoneGuard(){this.doneGuardTimer&&(clearTimeout(this.doneGuardTimer),this.doneGuardTimer=null)}shouldExtendByLiveness(){if(!this.isStreaming||!this.activeEventId)return!1;const t=this.process?.pid;if(!this.alive||!t||!y(t))return!1;const e=Date.now(),s=this.lastRpcAt>0?e-this.lastRpcAt:Number.POSITIVE_INFINITY;if(s<Z)return this.livenessExtendStartAt=e,a.info("pi-adapter",`Liveness check: fresh RPC for ${this.activeEventId} (rpcAge=${s}ms, toolsInFlight=${this.toolCallsInFlight.size}), extending`),!0;const n=this.livenessExtendStartAt??e;return this.livenessExtendStartAt===void 0&&(this.livenessExtendStartAt=n),e-n>x?(a.warn("pi-adapter",`Liveness extension budget exhausted for ${this.activeEventId} (no progress for ${Math.round((e-n)/6e4)}min), allowing idle fail`),!1):(a.info("pi-adapter",`Liveness check: turn in progress for ${this.activeEventId} (toolsInFlight=${this.toolCallsInFlight.size}, rpcAge=${Number.isFinite(s)?`${s}ms`:"n/a"}), extending`),!0)}resetIdleTimer(){this.stopIdleTimer(),!(this.stopped||!this.isStreaming)&&(this.idleTimer=setTimeout(()=>{if(this.stopped||!this.isStreaming)return;if(this.shouldExtendByLiveness()){this.resetIdleTimer();return}const t=this.process?.pid,s=!!(this.alive&&t&&y(t))?`liveness budget exhausted after ${x/6e4}min`:"process not alive";a.error("pi-adapter",`Idle timeout (${s}, toolsInFlight=${this.toolCallsInFlight.size}) \u2014 emitting exit for respawn`),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"failed",`idle timeout: ${s}`),this.clearActive(),this.emit("exit",-1)},Q))}stopIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}async ensureSessionReady(){return this.sessionReadyPromise?this.sessionReadyPromise:(this.sessionReadyPromise=this.restoreOrCreateSession().finally(()=>{this.sessionReadyPromise=null}),this.sessionReadyPromise)}async restoreOrCreateSession(){try{await this.switchSession();return}catch(t){this.piSessionPath&&a.error("pi-adapter",`switch_session failed, creating new session: ${t}`)}await this.createNewSession()}async switchSession(){if(!this.piSessionPath)throw new Error("no PI session path");const t=await this.transport.send("switch_session",{sessionPath:this.piSessionPath});if(!t.success)throw this.piSessionPath=null,this.persistPiSessionPath(void 0),new Error(`switch_session failed: ${t.error}`)}async createNewSession(){const t=await this.transport.send("new_session");if(!t.success)throw new Error(`new_session failed: ${t.error}`);let e=null;if(e=t.data?.sessionFile??null,!e)try{const i=await this.transport.send("get_state");i.success&&(e=i.data?.sessionFile??null)}catch{}this.piSessionPath=e,this.persistPiSessionPath(this.piSessionPath??void 0)}persistPiSessionPath(t){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setPiSessionPath(this.aibotSessionId,t)}}export{E as PiAdapter};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const f=500,c=["command","file_path","path","pattern","query","description","url","text"];function o(t){return typeof t=="string"?t.trim().replace(/\s+/g," "):typeof t=="number"||typeof t=="boolean"?String(t):""}function e(t){if(!t||typeof t!="object"||Array.isArray(t))return"";const r=t;for(const n of c){const i=o(r[n]);if(i)return i}return""}function p(t){let r="";if(typeof t=="string"){const n=o(t);if(n)try{r=e(JSON.parse(n))||n}catch{r=n}}else if(r=e(t),!r)try{r=o(JSON.stringify(t))}catch{r=""}return r.length<=500?r:`${r.slice(0,497)}...`}export{p as compactToolInputForWire};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{readFile as
|
|
2
|
-
`)){const i=Buffer.byteLength(s);if(s.trim())try{const o=JSON.parse(s);R(o)&&n.push({record:o,sourcePath:e,sourceOffset:r,sourceLength:i})}catch{}r+=i+1}return n}function ae(e,t){const n=t.input?.value?.trim();let r=-1;if(n)for(let i=e.length-1;i>=0;i-=1){const o=e[i].record;if(o.type==="turn.prompt"&&Z(o.input).trim()===n){r=i;break}}if(r<0&&(r=e.findIndex(i=>i.record.type==="turn.prompt"&&ie(i.record.time,t))),r<0)return{lines:[],startIndex:r};const s=e.findIndex((i,o)=>o>r&&i.record.type==="turn.prompt");return{lines:e.slice(r,s<0?void 0:s),startIndex:r}}function ce(e){const t=d(e);if(!(!t||se(t)!==t||t==="."||t===".."))return t}async function de(e){const t=d(e.boundary.kimiWirePath);if(t)return J(J(J(t)));const n=ce(e.boundary.providerSessionId);if(!n)return;const r=q(d(process.env.KIMI_CODE_HOME)??q(re(),".kimi-code"),"sessions");let s;try{s=await G(r,{withFileTypes:!0})}catch{return}const i=[];for(const o of s){if(!o.isDirectory())continue;const u=q(r,o.name,n),f=q(u,"agents","main","wire.jsonl");try{const _=await ne(f);let I=!1;const m=d(e.boundary.cwd);if(m)try{const y=JSON.parse(await Y(q(u,"state.json"),"utf8"));I=R(y)&&(y.workDir===m||y.cwd===m)}catch{}i.push({path:u,cwdMatches:I,modifiedAt:_.mtimeMs})}catch{}}return i.sort((o,u)=>Number(u.cwdMatches)-Number(o.cwdMatches)||u.modifiedAt-o.modifiedAt),i[0]?.path}async function ue(e,t,n){const s=d(t.boundary.kimiWirePath)??q(e,"agents","main","wire.jsonl"),i=await j(s),o=ae(i,t),u=o.lines.at(-1)?.sourceOffset??Number.POSITIVE_INFINITY,f=i.filter(O=>O.record.type==="config.update"&&O.sourceOffset<=u),_=i.filter(O=>O.record.type==="llm.tools_snapshot"),I=o.lines.length>0?{agentId:"main",sourcePath:s,lines:o.lines}:void 0,m=[s],y=[];let h=!0;if(!n.subagents)return{main:I,subagents:y,configRecords:f,toolSnapshots:_,sourcePaths:m,subagentDiscoveryComplete:h};const p=q(e,"agents");try{const O=await G(p,{withFileTypes:!0}),U=o.lines[0]?.record.time,W=o.lines.at(-1)?.record.time,H=Date.parse(v(U,t.startedAt)),C=Date.parse(v(W,t.endedAt));for(const D of O){if(!D.isDirectory()||D.name==="main")continue;const c=q(p,D.name,"wire.jsonl");try{const l=(await j(c)).filter(A=>{const g=Date.parse(v(A.record.time,""));return Number.isFinite(g)&&g>=H-5e3&&g<=C+3e4});l.length>0&&(y.push({agentId:D.name,sourcePath:c,lines:l}),m.push(c))}catch{h=!1}}}catch{h=!1}return{main:I,subagents:y,configRecords:f,toolSnapshots:_,sourcePaths:m,subagentDiscoveryComplete:h}}function k(e,t,n){const r=[n.role,n.sourcePath??"",n.sourceOffset??"",n.value??""].join("\0");if(t.has(r))return t.get(r);const s=Q(n);return t.set(r,s),e.push(n),s}function w(e,t){t&&!e.some(n=>n.contentId===t.contentId)&&e.push(t)}function T(e,t,n,r,s="provider_exact"){return{role:t,format:n,value:r,sourcePath:e.sourcePath,sourceOffset:e.sourceOffset,sourceLength:e.sourceLength,captureLevel:s}}function N(e){const t=e.endedAt,n=Date.parse(e.startedAt),r=t?Date.parse(t):Number.NaN;return{spanId:e.spanId,traceId:e.job.traceId,turnId:e.job.turnId,sessionId:e.job.sessionId,...e.parentSpanId?{parentSpanId:e.parentSpanId}:{},kind:e.kind,name:e.name,sequence:e.sequence,status:e.status??"completed",startedAt:e.startedAt,...t?{endedAt:t}:{},...Number.isFinite(n)&&Number.isFinite(r)?{durationMs:Math.max(0,r-n)}:{},inputRefs:[],outputRefs:[],provider:{provider:"acp",nativeEventType:e.nativeEventType,nativeIds:Object.fromEntries(Object.entries(e.nativeIds??{}).filter(s=>typeof s[1]=="string")),...e.rawAttributes?{rawAttributes:e.rawAttributes}:{}},provenance:{source:"provider_transcript",accuracy:"provider_reported"}}}function pe(e){if(!R(e))return;const t=B(e.inputOther),n=B(e.inputCacheRead),r=B(e.inputCacheCreation),s=B(e.output);if(t===void 0||n===void 0||r===void 0||s===void 0)return;const i=t+n+r;return{input:{total:i,uncached:t,cacheRead:n,cacheWrite:r,other:0},output:{total:s,reasoning:null,visible:null},totalProcessed:i+s,requestCount:1,accuracy:"provider_reported",completeness:"complete",provider:"acp",providerUsageRaw:e,normalization:{adapter:"kimi",version:1,formula:"input=inputOther+inputCacheRead+inputCacheCreation; output=output"}}}function le(e){const t=[],n=new Map,r=new Map;let s;for(const i of e){const o=L(i);if(o?.type==="step.begin"){const u=d(o.uuid)??`offset-${i.sourceOffset}`;s={stepUuid:u,turnId:d(o.turnId),step:B(o.step),begin:i,requests:[],parts:[],toolCalls:[]},t.push(s),n.set(u,s);continue}if(i.record.type==="llm.request"){s?.requests.push(i);continue}if(o?.type==="step.end"){const u=n.get(d(o.uuid)??"")??s;u&&(u.end=i),u===s&&(s=void 0);continue}if(o?.type==="content.part"){(n.get(d(o.stepUuid)??"")??s)?.parts.push(i);continue}if(o?.type==="tool.call"){const u=d(o.toolCallId);(n.get(d(o.stepUuid)??"")??s)?.toolCalls.push(i),u&&r.set(u,{line:i,event:o});continue}if(o?.type==="tool.result"){const u=d(o.toolCallId),f=u?r.get(u):void 0;f&&(f.result=i)}}return{steps:t,tools:r}}function ee(e,t){return e.configRecords.filter(n=>t===void 0||n.sourcePath!==t.sourcePath||n.sourceOffset<=t.sourceOffset).at(-1)?.record}function fe(e,t){const n=d(e.record.toolsHash),r=n?t.toolSnapshots.find(i=>i.record.hash===n)?.record:void 0,s=ee(t,e);return{...e.record,...e.record.systemPrompt===void 0&&s?.systemPrompt!==void 0?{systemPrompt:s.systemPrompt}:{},...r?{tools:r.tools}:{}}}function me(e){const t=R(e.result)?e.result:{},n=d(t.output);return{format:n?"text":"json",value:n??z(e.result),failed:t.isError===!0||t.error!==void 0}}function te(e,t,n,r,s,i,o,u,f,_,I){const m=v(n.lines[0]?.record.time,e.startedAt),y=v(n.lines.at(-1)?.record.time,e.endedAt),h=n.lines.find(c=>c.record.type==="turn.prompt");let p,O=_,U;if(I&&(p=N({job:e,spanId:`kimi:${e.sessionId}:subagent:${n.agentId}`,parentSpanId:_,kind:"subagent",name:`Kimi subagent ${n.agentId}`,sequence:f.value++,startedAt:m,endedAt:y,nativeEventType:"agent/wire.jsonl",nativeIds:{agentId:n.agentId,providerSessionId:d(e.boundary.providerSessionId)},rawAttributes:{sourcePath:n.sourcePath}}),o.push(p),O=p.spanId),r.inputOutput&&h){const c=Z(h.record.input);c&&(U=k(s,i,T(h,I?"subagent_prompt":"user_input","text",c)),w(I?p.inputRefs:o[0].inputRefs,U))}const{steps:W,tools:H}=le(n.lines),C=[];for(const[c,a]of W.entries()){const l=a.requests.at(-1)??a.begin,A=l.record,g=a.end?L(a.end):void 0,S=d(g?.messageId)??a.stepUuid,$=d(A.model),M=$??"kimi",F=v(a.begin.record.time,m),V=a.end?v(a.end.record.time,y):y,P=N({job:e,spanId:`kimi:${e.sessionId}:${n.agentId}:request:${S}`,parentSpanId:O,kind:"llm_request",name:M,sequence:f.value++,status:a.end?"completed":"running",startedAt:F,endedAt:V,nativeEventType:"llm.request",nativeIds:{requestId:S,stepUuid:a.stepUuid,providerTurnId:a.turnId,responseId:d(g?.messageId),traceId:d(g?.traceId)},rawAttributes:{sourcePath:l.sourcePath,sourceOffset:l.sourceOffset,provider:A.provider,modelAlias:A.modelAlias,finishReason:g?.finishReason,providerFinishReason:g?.providerFinishReason,rawFinishReason:g?.rawFinishReason,llmFirstTokenLatencyMs:g?.llmFirstTokenLatencyMs,llmStreamDurationMs:g?.llmStreamDurationMs,attemptCount:Math.max(1,a.requests.length)}});$&&(P.modelId=$),P.usage=pe(g?.usage),P.usage||t.gaps.push({category:"provider_usage",reason:"provider_usage_missing",detail:`${n.agentId}:${S}`}),c===0&&(I?w(P.inputRefs,U):e.input&&w(P.inputRefs,Q(e.input))),r.rawProviderBody&&l.record.type==="llm.request"&&w(P.inputRefs,k(s,i,T(l,"model_request","json",z(fe(l,t)),"reconstructed")));for(const x of a.parts){const E=L(x),b=R(E?.part)?E.part:void 0;if(b){if(b.type==="think"&&r.thinking){const K=d(b.think)??d(b.text);K&&w(P.outputRefs,k(s,i,T(x,"thinking","text",K)))}if(b.type==="text"&&r.inputOutput){const K=d(b.text);K&&w(P.outputRefs,k(s,i,T(x,"assistant_text","text",K)))}}}r.rawProviderBody&&a.end&&w(P.outputRefs,k(s,i,T(a.end,"model_response","json",z({parts:a.parts.map(x=>L(x)?.part),stepEnd:g}),"reconstructed"))),o.push(P),C.push(P);for(let x=0;x<a.requests.length-1;x+=1){const E=a.requests[x],b=a.requests[x+1];o.push(N({job:e,spanId:`kimi:${e.sessionId}:${n.agentId}:retry:${a.stepUuid}:${x+1}`,parentSpanId:P.spanId,kind:"retry",name:`Kimi request retry ${x+1}`,sequence:f.value++,startedAt:v(E.record.time,F),endedAt:v(b.record.time,V),nativeEventType:"llm.request",nativeIds:{stepUuid:a.stepUuid,attempt:d(E.record.attempt)??String(x+1)},rawAttributes:{projection:E.record.projection,request:E.record}}))}}if(r.tools)for(const[c,a]of H){const l=a.event,A=a.result?L(a.result):void 0,g=C.find(M=>M.provider.nativeIds.stepUuid===d(l.stepUuid))??C.at(-1),S=A?me(A):void 0,$=N({job:e,spanId:`kimi:${e.sessionId}:${n.agentId}:tool:${c}`,parentSpanId:g?.spanId??O,kind:"tool_call",name:d(l.name)??"tool",sequence:f.value++,status:S?S.failed?"failed":"completed":"running",startedAt:v(a.line.record.time,m),endedAt:a.result?v(a.result.record.time,y):y,nativeEventType:"context.append_loop_event/tool.call",nativeIds:{toolCallId:c,stepUuid:d(l.stepUuid),providerTurnId:d(l.turnId),traceId:d(l.traceId??A?.traceId)},rawAttributes:{description:l.description,display:l.display,extras:l.extras,hasResult:!!A}});if(w($.inputRefs,k(s,i,T(a.line,"tool_arguments","json",z(l.args)))),S&&a.result){const M=k(s,i,T(a.result,"tool_result",S.format,S.value));w($.outputRefs,M);const F=g?C.indexOf(g):-1;F>=0&&w(C[F+1]?.inputRefs??[],M)}else t.gaps.push({category:"tool_result",reason:"correlation_failed",detail:`${n.agentId}:${c}`});o.push($)}for(const c of n.lines)if(c.record.type==="permission.record_approval_result"){const a=R(c.record.result)?c.record.result:{},l=d(a.decision)??"unknown";o.push(N({job:e,spanId:`kimi:${e.sessionId}:${n.agentId}:permission:${c.sourceOffset}`,parentSpanId:O,kind:"permission",name:d(c.record.toolName)??"Kimi tool approval",sequence:f.value++,status:l==="approved"?"completed":"cancelled",startedAt:v(c.record.time,m),endedAt:v(c.record.time,m),nativeEventType:"permission.record_approval_result",nativeIds:{toolCallId:d(c.record.toolCallId),providerTurnId:d(c.record.turnId)},rawAttributes:{action:c.record.action,result:c.record.result}}))}const D=[];for(const c of n.lines){if(c.record.type==="full_compaction.begin"){D.push(c);continue}if(c.record.type!=="full_compaction.complete"&&c.record.type!=="full_compaction.cancel"&&c.record.type!=="micro_compaction.apply")continue;const a=c.record.type==="micro_compaction.apply"?c:D.shift()??c;o.push(N({job:e,spanId:`kimi:${e.sessionId}:${n.agentId}:compaction:${a.sourceOffset}`,parentSpanId:O,kind:"compaction",name:c.record.type==="micro_compaction.apply"?"Kimi micro compaction":"Kimi full compaction",sequence:f.value++,status:c.record.type==="full_compaction.cancel"?"cancelled":"completed",startedAt:v(a.record.time,m),endedAt:v(c.record.time,y),nativeEventType:c.record.type,rawAttributes:{begin:a.record,completion:c.record}}))}if(I&&p&&C.flatMap(a=>a.outputRefs).filter(a=>a.role==="assistant_text").length>0){const a=n.lines.flatMap(l=>{const A=L(l),g=R(A?.part)?A.part:void 0;return g?.type==="text"&&typeof g.text=="string"?[g.text]:[]}).join("");a&&w(p.outputRefs,k(s,i,{role:"subagent_result",format:"text",value:a,sourcePath:n.sourcePath,captureLevel:"provider_exact"}))}W.length===0&&u.push({code:"kimi_llm_step_missing",message:`No Kimi model step was found for agent ${n.agentId}`,providerIds:{agentId:n.agentId}})}class ve{provider="acp";async collect(t){const n=X(t),r=[],s=[],i=await de(t);if(!i)return{subagents:[],configRecords:[],toolSnapshots:[],sourcePaths:[],anomalies:s,gaps:[{category:"provider_transcript",reason:"source_log_missing",detail:"Kimi session directory could not be located"}],subagentDiscoveryComplete:!1};try{const o=await ue(i,t,n);return o.main||r.push({category:"provider_transcript",reason:"correlation_failed",detail:"Kimi wire.jsonl was found, but the audited turn prompt was not correlated"}),n.subagents&&!o.subagentDiscoveryComplete&&r.push({category:"subagents",reason:"source_log_missing",detail:"One or more Kimi subagent wire logs could not be enumerated or read"}),{...o,anomalies:s,gaps:r}}catch(o){return{subagents:[],configRecords:[],toolSnapshots:[],sourcePaths:[],anomalies:[{code:"kimi_wire_read_failed",message:String(o)}],gaps:[{category:"provider_transcript",reason:"source_log_missing",detail:String(o)}],subagentDiscoveryComplete:!1}}}async normalize(t,n){const r=n,s=X(t),i=[],o=new Map,u=[],f=[...r.anomalies],_={value:0},I=`kimi:${t.turnId}:turn`,m=N({job:t,spanId:I,kind:"turn",name:"Kimi audited turn",sequence:_.value++,status:t.outcome==="completed"?"completed":t.outcome==="cancelled"?"cancelled":"failed",startedAt:t.startedAt,endedAt:t.endedAt,nativeEventType:"turn.prompt",nativeIds:{providerSessionId:d(t.boundary.providerSessionId),platformEventId:d(t.boundary.platformEventId)}});u.push(m),s.inputOutput&&t.input&&w(m.inputRefs,k(i,o,{...t.input,captureLevel:t.input.captureLevel??"runtime_exact"})),s.inputOutput&&t.finalOutput&&w(m.outputRefs,k(i,o,{...t.finalOutput,captureLevel:t.finalOutput.captureLevel??"runtime_exact"}));const y=ee(r);if(s.rawProviderBody&&y){const h=d(y.systemPrompt??y.system_prompt),p=r.configRecords.at(-1);h&&p&&w(m.inputRefs,k(i,o,T(p,"system_prompt","text",h)))}r.main&&te(t,r,r.main,s,i,o,u,f,_,I,!1);for(const h of r.subagents)te(t,r,h,s,i,o,u,f,_,I,!0);return u.sort((h,p)=>Date.parse(h.startedAt)-Date.parse(p.startedAt)||h.sequence-p.sequence),u.forEach((h,p)=>{h.sequence=p}),{spans:u,contents:i,anomalies:f,collection:r,capture:s}}async correlate(t,n){return n}validate(t,n){const r=n,s=r.spans.filter(p=>p.kind==="llm_request"),i=r.spans.filter(p=>p.kind==="tool_call"),o=!r.capture.inputOutput||t.input!==void 0||r.contents.some(p=>p.role==="user_input"),u=!r.capture.inputOutput||t.finalOutput!==void 0||r.contents.some(p=>p.role==="assistant_text"),f=!r.capture.tools||i.every(p=>p.status!=="running"),_=!r.capture.subagents||r.collection.subagentDiscoveryComplete,I=s.length>0&&s.every(p=>p.usage?.completeness==="complete"),m=!1,y=[...r.collection.gaps];r.capture.rawProviderBody&&y.push({category:"raw_provider_body",reason:"raw_capture_disabled",detail:"Kimi wire.jsonl reconstructs logical requests and responses but does not retain raw HTTP bodies"});const h=o&&u&&f&&_&&I&&(!r.capture.rawProviderBody||m)&&y.length===0;return{status:!r.collection.main||s.length===0?"failed":h?"complete":"partial",inputComplete:o,outputComplete:u,toolCallsComplete:f,subagentsComplete:_,usageComplete:I,rawRequestsComplete:m,gaps:y}}}export{ve as KimiAuditAdapter};
|
|
1
|
+
import{readFile as Q,readdir as Y,stat as se}from"node:fs/promises";import{homedir as re}from"node:os";import{basename as oe,dirname as V,join as T}from"node:path";import{referenceCaptureContent as X}from"../../core/content-reference.js";import{assessToolCallCompleteness as ie,deduplicateGaps as ae}from"../../core/tool-call-audit.js";const ce=Object.freeze({inputOutput:!0,tools:!0,subagents:!0,thinking:!0,rawProviderBody:!1});function R(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function d(e){return typeof e=="string"&&e.length>0?e:void 0}function B(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:void 0}function z(e){if(typeof e=="string")return e;try{const t=JSON.stringify(e);return t===void 0?"null":t}catch{return String(e)}}function Z(e){return e.options?.enabled===!0?e.options.capture:ce}function w(e,t){if(typeof e=="number"&&Number.isFinite(e)){const n=e>1e10?e:e*1e3,s=new Date(n);if(!Number.isNaN(s.getTime()))return s.toISOString()}return typeof e=="string"&&!Number.isNaN(Date.parse(e))?new Date(e).toISOString():t}function j(e){return typeof e=="string"?e:Array.isArray(e)?e.map(t=>typeof t=="string"?t:R(t)?d(t.text)??d(t.think)??d(t.data)??"":"").join(""):""}function E(e){return e.record.type==="context.append_loop_event"&&R(e.record.event)?e.record.event:void 0}function de(e,t){const n=Date.parse(w(e,""));return Number.isFinite(n)&&n>=Date.parse(t.startedAt)-5e3&&n<=Date.parse(t.endedAt)+3e4}async function ee(e){const t=await Q(e,"utf8"),n=[];let s=0;for(const a of t.split(`
|
|
2
|
+
`)){const r=Buffer.byteLength(a);if(a.trim())try{const o=JSON.parse(a);R(o)&&n.push({record:o,sourcePath:e,sourceOffset:s,sourceLength:r})}catch{}s+=r+1}return n}function ue(e,t){const n=t.input?.value?.trim();let s=-1;if(n)for(let r=e.length-1;r>=0;r-=1){const o=e[r].record;if(o.type==="turn.prompt"&&j(o.input).trim()===n){s=r;break}}if(s<0&&(s=e.findIndex(r=>r.record.type==="turn.prompt"&&de(r.record.time,t))),s<0)return{lines:[],startIndex:s};const a=e.findIndex((r,o)=>o>s&&r.record.type==="turn.prompt");return{lines:e.slice(s,a<0?void 0:a),startIndex:s}}function pe(e){const t=d(e);if(!(!t||oe(t)!==t||t==="."||t===".."))return t}async function le(e){const t=d(e.boundary.kimiWirePath);if(t)return V(V(V(t)));const n=pe(e.boundary.providerSessionId);if(!n)return;const s=T(d(process.env.KIMI_CODE_HOME)??T(re(),".kimi-code"),"sessions");let a;try{a=await Y(s,{withFileTypes:!0})}catch{return}const r=[];for(const o of a){if(!o.isDirectory())continue;const u=T(s,o.name,n),p=T(u,"agents","main","wire.jsonl");try{const I=await se(p);let y=!1;const f=d(e.boundary.cwd);if(f)try{const h=JSON.parse(await Q(T(u,"state.json"),"utf8"));y=R(h)&&(h.workDir===f||h.cwd===f)}catch{}r.push({path:u,cwdMatches:y,modifiedAt:I.mtimeMs})}catch{}}return r.sort((o,u)=>Number(u.cwdMatches)-Number(o.cwdMatches)||u.modifiedAt-o.modifiedAt),r[0]?.path}async function fe(e,t,n){const a=d(t.boundary.kimiWirePath)??T(e,"agents","main","wire.jsonl"),r=await ee(a),o=ue(r,t),u=o.lines.at(-1)?.sourceOffset??Number.POSITIVE_INFINITY,p=r.filter(g=>g.record.type==="config.update"&&g.sourceOffset<=u),I=r.filter(g=>g.record.type==="llm.tools_snapshot"),y=o.lines.length>0?{agentId:"main",sourcePath:a,lines:o.lines}:void 0,f=[a],h=[];let m=!0;if(!n.subagents)return{main:y,subagents:h,configRecords:p,toolSnapshots:I,sourcePaths:f,subagentDiscoveryComplete:m};const _=T(e,"agents");try{const g=await Y(_,{withFileTypes:!0}),L=o.lines[0]?.record.time,W=o.lines.at(-1)?.record.time,H=Date.parse(w(L,t.startedAt)),J=Date.parse(w(W,t.endedAt));for(const k of g){if(!k.isDirectory()||k.name==="main")continue;const M=T(_,k.name,"wire.jsonl");try{const i=(await ee(M)).filter(l=>{const A=Date.parse(w(l.record.time,""));return Number.isFinite(A)&&A>=H-5e3&&A<=J+3e4});i.length>0&&(h.push({agentId:k.name,sourcePath:M,lines:i}),f.push(M))}catch{m=!1}}}catch{m=!1}return{main:y,subagents:h,configRecords:p,toolSnapshots:I,sourcePaths:f,subagentDiscoveryComplete:m}}function P(e,t,n){const s=[n.role,n.sourcePath??"",n.sourceOffset??"",n.value??""].join("\0");if(t.has(s))return t.get(s);const a=X(n);return t.set(s,a),e.push(n),a}function O(e,t){t&&!e.some(n=>n.contentId===t.contentId)&&e.push(t)}function b(e,t,n,s,a="provider_exact"){return{role:t,format:n,value:s,sourcePath:e.sourcePath,sourceOffset:e.sourceOffset,sourceLength:e.sourceLength,captureLevel:a}}function N(e){const t=e.endedAt,n=Date.parse(e.startedAt),s=t?Date.parse(t):Number.NaN;return{spanId:e.spanId,traceId:e.job.traceId,turnId:e.job.turnId,sessionId:e.job.sessionId,...e.parentSpanId?{parentSpanId:e.parentSpanId}:{},kind:e.kind,name:e.name,sequence:e.sequence,status:e.status??"completed",startedAt:e.startedAt,...t?{endedAt:t}:{},...Number.isFinite(n)&&Number.isFinite(s)?{durationMs:Math.max(0,s-n)}:{},inputRefs:[],outputRefs:[],provider:{provider:"acp",nativeEventType:e.nativeEventType,nativeIds:Object.fromEntries(Object.entries(e.nativeIds??{}).filter(a=>typeof a[1]=="string")),...e.rawAttributes?{rawAttributes:e.rawAttributes}:{}},provenance:{source:"provider_transcript",accuracy:"provider_reported"}}}function me(e){if(!R(e))return;const t=B(e.inputOther),n=B(e.inputCacheRead),s=B(e.inputCacheCreation),a=B(e.output);if(t===void 0||n===void 0||s===void 0||a===void 0)return;const r=t+n+s;return{input:{total:r,uncached:t,cacheRead:n,cacheWrite:s,other:0},output:{total:a,reasoning:null,visible:null},totalProcessed:r+a,requestCount:1,accuracy:"provider_reported",completeness:"complete",provider:"acp",providerUsageRaw:e,normalization:{adapter:"kimi",version:1,formula:"input=inputOther+inputCacheRead+inputCacheCreation; output=output"}}}function ge(e){const t=[],n=new Map,s=new Map,a=[];let r;for(const o of e){const u=E(o);if(u?.type==="step.begin"){const p=d(u.uuid)??`offset-${o.sourceOffset}`;r={stepUuid:p,turnId:d(u.turnId),step:B(u.step),begin:o,requests:[],parts:[],toolCalls:[]},t.push(r),n.set(p,r);continue}if(o.record.type==="llm.request"){r?.requests.push(o);continue}if(u?.type==="step.end"){const p=n.get(d(u.uuid)??"")??r;p&&(p.end=o),p===r&&(r=void 0);continue}if(u?.type==="content.part"){(n.get(d(u.stepUuid)??"")??r)?.parts.push(o);continue}if(u?.type==="tool.call"){const p=d(u.toolCallId),I=p??d(u.uuid)??`offset-${o.sourceOffset}`;(n.get(d(u.stepUuid)??"")??r)?.toolCalls.push(o),s.set(I,{toolCallId:I,line:o,event:u,syntheticToolCallId:!p});continue}if(u?.type==="tool.result"){const p=d(u.toolCallId)??d(u.parentUuid),I=p?s.get(p):void 0;I?I.result=o:a.push(o)}}return{steps:t,tools:s,orphanResults:a}}function te(e,t){return e.configRecords.filter(n=>t===void 0||n.sourcePath!==t.sourcePath||n.sourceOffset<=t.sourceOffset).at(-1)?.record}function he(e,t){const n=d(e.record.toolsHash),s=n?t.toolSnapshots.find(r=>r.record.hash===n)?.record:void 0,a=te(t,e);return{...e.record,...e.record.systemPrompt===void 0&&a?.systemPrompt!==void 0?{systemPrompt:a.systemPrompt}:{},...s?{tools:s.tools}:{}}}function Ie(e){const t=R(e.result)?e.result:{},n=typeof t.output=="string";return{format:n?"text":"json",value:n?String(t.output):z(e.result),failed:t.isError===!0||t.error!==void 0}}function ne(e,t,n,s,a,r,o,u,p,I,y){const f=w(n.lines[0]?.record.time,e.startedAt),h=w(n.lines.at(-1)?.record.time,e.endedAt),m=n.lines.find(c=>c.record.type==="turn.prompt");let _,g=I,L;if(y&&(_=N({job:e,spanId:`kimi:${e.sessionId}:subagent:${n.agentId}`,parentSpanId:I,kind:"subagent",name:`Kimi subagent ${n.agentId}`,sequence:p.value++,startedAt:f,endedAt:h,nativeEventType:"agent/wire.jsonl",nativeIds:{agentId:n.agentId,providerSessionId:d(e.boundary.providerSessionId)},rawAttributes:{sourcePath:n.sourcePath}}),o.push(_),g=_.spanId),s.inputOutput&&m){const c=j(m.record.input);c&&(L=P(a,r,b(m,y?"subagent_prompt":"user_input","text",c)),O(y?_.inputRefs:o[0].inputRefs,L))}const{steps:W,tools:H,orphanResults:J}=ge(n.lines);for(const c of J){const i=E(c),l=d(i?.toolCallId)??d(i?.parentUuid)??`offset-${c.sourceOffset}`;u.push({code:"kimi_tool_result_without_call",message:`Kimi tool result ${l} has no matching tool call`,providerIds:{toolCallId:l,agentId:n.agentId}}),t.gaps.push({category:"tool_call",reason:"correlation_failed",detail:`${n.agentId}:${l}:orphan_result`})}const k=[];for(const[c,i]of W.entries()){const l=i.requests.at(-1)??i.begin,A=l.record,v=i.end?E(i.end):void 0,S=d(v?.messageId)??i.stepUuid,q=d(A.model),U=q??"kimi",F=w(i.begin.record.time,f),G=i.end?w(i.end.record.time,h):h,C=N({job:e,spanId:`kimi:${e.sessionId}:${n.agentId}:request:${S}`,parentSpanId:g,kind:"llm_request",name:U,sequence:p.value++,status:i.end?"completed":"running",startedAt:F,endedAt:G,nativeEventType:"llm.request",nativeIds:{requestId:S,stepUuid:i.stepUuid,providerTurnId:i.turnId,responseId:d(v?.messageId),traceId:d(v?.traceId)},rawAttributes:{sourcePath:l.sourcePath,sourceOffset:l.sourceOffset,provider:A.provider,modelAlias:A.modelAlias,finishReason:v?.finishReason,providerFinishReason:v?.providerFinishReason,rawFinishReason:v?.rawFinishReason,llmFirstTokenLatencyMs:v?.llmFirstTokenLatencyMs,llmStreamDurationMs:v?.llmStreamDurationMs,attemptCount:Math.max(1,i.requests.length)}});q&&(C.modelId=q),C.usage=me(v?.usage),C.usage||t.gaps.push({category:"provider_usage",reason:"provider_usage_missing",detail:`${n.agentId}:${S}`}),c===0&&(y?O(C.inputRefs,L):e.input&&O(C.inputRefs,X(e.input))),s.rawProviderBody&&l.record.type==="llm.request"&&O(C.inputRefs,P(a,r,b(l,"model_request","json",z(he(l,t)),"reconstructed")));for(const x of i.parts){const D=E(x),$=R(D?.part)?D.part:void 0;if($){if($.type==="think"&&s.thinking){const K=d($.think)??d($.text);K&&O(C.outputRefs,P(a,r,b(x,"thinking","text",K)))}if($.type==="text"&&s.inputOutput){const K=d($.text);K&&O(C.outputRefs,P(a,r,b(x,"assistant_text","text",K)))}}}s.rawProviderBody&&i.end&&O(C.outputRefs,P(a,r,b(i.end,"model_response","json",z({parts:i.parts.map(x=>E(x)?.part),stepEnd:v}),"reconstructed"))),o.push(C),k.push(C);for(let x=0;x<i.requests.length-1;x+=1){const D=i.requests[x],$=i.requests[x+1];o.push(N({job:e,spanId:`kimi:${e.sessionId}:${n.agentId}:retry:${i.stepUuid}:${x+1}`,parentSpanId:C.spanId,kind:"retry",name:`Kimi request retry ${x+1}`,sequence:p.value++,startedAt:w(D.record.time,F),endedAt:w($.record.time,G),nativeEventType:"llm.request",nativeIds:{stepUuid:i.stepUuid,attempt:d(D.record.attempt)??String(x+1)},rawAttributes:{projection:D.record.projection,request:D.record}}))}}if(s.tools)for(const[c,i]of H){const l=i.event,A=i.result?E(i.result):void 0,v=k.find(U=>U.provider.nativeIds.stepUuid===d(l.stepUuid))??k.at(-1),S=A?Ie(A):void 0,q=N({job:e,spanId:`kimi:${e.sessionId}:${n.agentId}:tool:${c}`,parentSpanId:v?.spanId??g,kind:"tool_call",name:d(l.name)??"tool",sequence:p.value++,status:S?S.failed?"failed":"completed":"running",startedAt:w(i.line.record.time,f),endedAt:i.result?w(i.result.record.time,h):h,nativeEventType:"context.append_loop_event/tool.call",nativeIds:{toolCallId:c,stepUuid:d(l.stepUuid),providerTurnId:d(l.turnId),traceId:d(l.traceId??A?.traceId)},rawAttributes:{description:l.description,display:l.display,extras:l.extras,hasResult:!!A,...i.syntheticToolCallId?{syntheticToolCallId:!0}:{}}});if(i.syntheticToolCallId&&(u.push({code:"kimi_tool_call_id_missing",message:`Kimi tool call at ${i.line.sourcePath}:${i.line.sourceOffset} has no toolCallId`,providerIds:{toolCallId:c,agentId:n.agentId}}),t.gaps.push({category:"tool_call",reason:"correlation_failed",detail:`${n.agentId}:${c}:missing_id`})),d(l.name)||u.push({code:"kimi_tool_name_missing",message:`Kimi tool call ${c} has no name`,providerIds:{toolCallId:c,agentId:n.agentId}}),Object.hasOwn(l,"args")?O(q.inputRefs,P(a,r,b(i.line,"tool_arguments","json",z(l.args)))):(u.push({code:"kimi_tool_arguments_missing",message:`Kimi tool call ${c} has no args payload`,providerIds:{toolCallId:c,agentId:n.agentId}}),t.gaps.push({category:"tool_arguments",reason:"correlation_failed",detail:`${n.agentId}:${c}`})),S&&i.result){const U=P(a,r,b(i.result,"tool_result",S.format,S.value));O(q.outputRefs,U);const F=v?k.indexOf(v):-1;F>=0&&O(k[F+1]?.inputRefs??[],U)}else t.gaps.push({category:"tool_result",reason:"correlation_failed",detail:`${n.agentId}:${c}`});o.push(q)}for(const c of n.lines)if(c.record.type==="permission.record_approval_result"){const i=R(c.record.result)?c.record.result:{},l=d(i.decision)??"unknown";o.push(N({job:e,spanId:`kimi:${e.sessionId}:${n.agentId}:permission:${c.sourceOffset}`,parentSpanId:g,kind:"permission",name:d(c.record.toolName)??"Kimi tool approval",sequence:p.value++,status:l==="approved"?"completed":"cancelled",startedAt:w(c.record.time,f),endedAt:w(c.record.time,f),nativeEventType:"permission.record_approval_result",nativeIds:{toolCallId:d(c.record.toolCallId),providerTurnId:d(c.record.turnId)},rawAttributes:{action:c.record.action,result:c.record.result}}))}const M=[];for(const c of n.lines){if(c.record.type==="full_compaction.begin"){M.push(c);continue}if(c.record.type!=="full_compaction.complete"&&c.record.type!=="full_compaction.cancel"&&c.record.type!=="micro_compaction.apply")continue;const i=c.record.type==="micro_compaction.apply"?c:M.shift()??c;o.push(N({job:e,spanId:`kimi:${e.sessionId}:${n.agentId}:compaction:${i.sourceOffset}`,parentSpanId:g,kind:"compaction",name:c.record.type==="micro_compaction.apply"?"Kimi micro compaction":"Kimi full compaction",sequence:p.value++,status:c.record.type==="full_compaction.cancel"?"cancelled":"completed",startedAt:w(i.record.time,f),endedAt:w(c.record.time,h),nativeEventType:c.record.type,rawAttributes:{begin:i.record,completion:c.record}}))}if(y&&_&&k.flatMap(i=>i.outputRefs).filter(i=>i.role==="assistant_text").length>0){const i=n.lines.flatMap(l=>{const A=E(l),v=R(A?.part)?A.part:void 0;return v?.type==="text"&&typeof v.text=="string"?[v.text]:[]}).join("");i&&O(_.outputRefs,P(a,r,{role:"subagent_result",format:"text",value:i,sourcePath:n.sourcePath,captureLevel:"provider_exact"}))}W.length===0&&u.push({code:"kimi_llm_step_missing",message:`No Kimi model step was found for agent ${n.agentId}`,providerIds:{agentId:n.agentId}})}class Oe{provider="acp";async collect(t){const n=Z(t),s=[],a=[],r=await le(t);if(!r)return{subagents:[],configRecords:[],toolSnapshots:[],sourcePaths:[],anomalies:a,gaps:[{category:"provider_transcript",reason:"source_log_missing",detail:"Kimi session directory could not be located"}],subagentDiscoveryComplete:!1};try{const o=await fe(r,t,n);return o.main||s.push({category:"provider_transcript",reason:"correlation_failed",detail:"Kimi wire.jsonl was found, but the audited turn prompt was not correlated"}),n.subagents&&!o.subagentDiscoveryComplete&&s.push({category:"subagents",reason:"source_log_missing",detail:"One or more Kimi subagent wire logs could not be enumerated or read"}),{...o,anomalies:a,gaps:s}}catch(o){return{subagents:[],configRecords:[],toolSnapshots:[],sourcePaths:[],anomalies:[{code:"kimi_wire_read_failed",message:String(o)}],gaps:[{category:"provider_transcript",reason:"source_log_missing",detail:String(o)}],subagentDiscoveryComplete:!1}}}async normalize(t,n){const s=n,a=Z(t),r=[],o=new Map,u=[],p=[...s.anomalies],I={value:0},y=`kimi:${t.turnId}:turn`,f=N({job:t,spanId:y,kind:"turn",name:"Kimi audited turn",sequence:I.value++,status:t.outcome==="completed"?"completed":t.outcome==="cancelled"?"cancelled":"failed",startedAt:t.startedAt,endedAt:t.endedAt,nativeEventType:"turn.prompt",nativeIds:{providerSessionId:d(t.boundary.providerSessionId),platformEventId:d(t.boundary.platformEventId)}});u.push(f),a.inputOutput&&t.input&&O(f.inputRefs,P(r,o,{...t.input,captureLevel:t.input.captureLevel??"runtime_exact"})),a.inputOutput&&t.finalOutput&&O(f.outputRefs,P(r,o,{...t.finalOutput,captureLevel:t.finalOutput.captureLevel??"runtime_exact"}));const h=te(s);if(a.rawProviderBody&&h){const m=d(h.systemPrompt??h.system_prompt),_=s.configRecords.at(-1);m&&_&&O(f.inputRefs,P(r,o,b(_,"system_prompt","text",m)))}s.main&&ne(t,s,s.main,a,r,o,u,p,I,y,!1);for(const m of s.subagents)ne(t,s,m,a,r,o,u,p,I,y,!0);return u.sort((m,_)=>Date.parse(m.startedAt)-Date.parse(_.startedAt)||m.sequence-_.sequence),u.forEach((m,_)=>{m.sequence=_}),{spans:u,contents:r,anomalies:p,collection:s,capture:a}}async correlate(t,n){return n}validate(t,n){const s=n,a=s.spans.filter(g=>g.kind==="llm_request"),r=s.spans.filter(g=>g.kind==="tool_call"),o=!s.capture.inputOutput||t.input!==void 0||s.contents.some(g=>g.role==="user_input"),u=!s.capture.inputOutput||t.finalOutput!==void 0||s.contents.some(g=>g.role==="assistant_text"),p=ie(r,s.capture.tools),I=p.complete,y=!s.capture.subagents||s.collection.subagentDiscoveryComplete,f=a.length>0&&a.every(g=>g.usage?.completeness==="complete"),h=!1,m=ae([...s.collection.gaps,...p.gaps]);s.capture.rawProviderBody&&m.push({category:"raw_provider_body",reason:"raw_capture_disabled",detail:"Kimi wire.jsonl reconstructs logical requests and responses but does not retain raw HTTP bodies"});const _=o&&u&&I&&y&&f&&(!s.capture.rawProviderBody||h)&&m.length===0;return{status:!s.collection.main||a.length===0?"failed":_?"complete":"partial",inputComplete:o,outputComplete:u,toolCallsComplete:I,subagentsComplete:y,usageComplete:f,rawRequestsComplete:h,gaps:m}}}export{Oe as KimiAuditAdapter};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{referenceCaptureContent as N}from"../../core/content-reference.js";import{InvalidReasonixUsageError as F,normalizeReasonixUsage as z}from"../../core/usage.js";import{collectReasonixTranscript as B}from"./reasonix-transcript-source.js";const H=Object.freeze({inputOutput:!0,tools:!0,subagents:!0,thinking:!0,rawProviderBody:!1});function V(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function u(t){return typeof t=="string"&&t.length>0?t:void 0}function E(t,e){const s=u(t);return s&&Number.isFinite(Date.parse(s))?s:e}function G(t){return t.options?.enabled===!0?t.options.capture:H}function S(t,e,s,n){return{role:e,format:s,value:n,sourcePath:t.sourcePath,sourceOffset:t.sourceOffset,sourceLength:t.sourceLength,captureLevel:"provider_exact"}}function R(t,e,s){const n=[s.role,s.sourcePath??"",s.sourceOffset??"",s.value??""].join("\0");if(e.has(n))return e.get(n);const o=N(s);return e.set(n,o),t.push(s),o}function A(t,e){e&&(t.some(s=>s.contentId===e.contentId)||t.push(e))}function b(t){const e=t.startedAt??t.job.startedAt,s=t.endedAt??t.job.endedAt,n=Date.parse(e),o=Date.parse(s);return{spanId:t.spanId,traceId:t.job.traceId,turnId:t.job.turnId,sessionId:t.job.sessionId,...t.parentSpanId?{parentSpanId:t.parentSpanId}:{},kind:t.kind,name:t.name,sequence:t.sequence,status:t.status??"completed",startedAt:e,endedAt:s,...Number.isFinite(n)&&Number.isFinite(o)?{durationMs:Math.max(0,o-n)}:{},inputRefs:[],outputRefs:[],provider:{provider:"acp",nativeEventType:t.nativeEventType,nativeIds:Object.fromEntries(Object.entries(t.nativeIds??{}).filter(h=>typeof h[1]=="string")),...t.rawAttributes?{rawAttributes:t.rawAttributes}:{}},provenance:{source:"provider_transcript",accuracy:"provider_reported"}}}class W{provider="acp";async collect(e){return B(e.boundary)}async normalize(e,s){const n=s,o=G(e),h=[...n.anomalies],f=[],m=new Map,g=[];let q=0;const y=`reasonix:${e.turnId}:turn`,x=b({job:e,spanId:y,kind:"turn",name:"Reasonix audited turn",sequence:q++,status:e.outcome==="completed"?"completed":e.outcome==="cancelled"?"cancelled":"failed",nativeEventType:"acp_turn",nativeIds:{platformEventId:u(e.boundary.platformEventId),providerSessionId:u(e.boundary.providerSessionId)}});g.push(x);let c;o.inputOutput&&e.input&&(c=R(f,m,{...e.input,captureLevel:e.input.captureLevel??"runtime_exact"}),A(x.inputRefs,c)),o.inputOutput&&e.finalOutput&&A(x.outputRefs,R(f,m,{...e.finalOutput,captureLevel:e.finalOutput.captureLevel??"runtime_exact"}));const D=n.acpEvents.filter(({event:r})=>r.type==="tool_use"),U=new Map(n.acpEvents.filter(({event:r})=>r.type==="tool_result"&&typeof r.toolCallId=="string").map(r=>[String(r.event.toolCallId),r]));let M=0,O=e.startedAt,k,T=c?[c]:[];for(const r of n.lines){const a=r.record,i=u(a.role),p=E(a.ts,O);if(i==="assistant_final"){const v=typeof a.turn=="number"?String(a.turn):`offset-${r.sourceOffset}`,l=`reasonix-${v}-${r.sourceOffset}`,I=u(a.model),d=b({job:e,spanId:`reasonix:${e.sessionId}:request:${l}`,parentSpanId:y,kind:"llm_request",name:I??"Reasonix model request",sequence:q++,startedAt:O,endedAt:p,nativeEventType:i,nativeIds:{requestId:l,providerTurn:v,providerSessionId:u(e.boundary.providerSessionId)},rawAttributes:{sourcePath:r.sourcePath,sourceOffset:r.sourceOffset,...I?{model:I}:{}}});I&&(d.modelId=I);for(const _ of T)A(d.inputRefs,_);if(T=[],V(a.usage))try{d.usage=z(a.usage)}catch(_){h.push({code:"reasonix_provider_usage_invalid",message:_ instanceof F?_.message:String(_),spanId:d.spanId,providerIds:{requestId:l}}),n.gaps.push({category:"provider_usage",reason:"provider_usage_missing",detail:l})}else n.gaps.push({category:"provider_usage",reason:"provider_usage_missing",detail:l});const C=u(a.content);o.inputOutput&&C&&A(d.outputRefs,R(f,m,S(r,"assistant_text","text",C))),g.push(d),k=d,O=p;continue}if(i==="tool"&&o.tools){const v=D[M++],l=u(v?.event.toolCallId)??`offset-${r.sourceOffset}`,I=u(a.tool)??u(v?.event.toolName)??"tool",d=U.get(l),C=E(v?.observedAt,O),_=E(d?.observedAt,p),w=b({job:e,spanId:`reasonix:${e.sessionId}:tool:${l}`,parentSpanId:k?.spanId??y,kind:"tool_call",name:I,sequence:q++,status:a.error?"failed":"completed",startedAt:C,endedAt:_,nativeEventType:i,nativeIds:{toolCallId:l,requestId:k?.provider.nativeIds.requestId},rawAttributes:{sourcePath:r.sourcePath,sourceOffset:r.sourceOffset}}),P=u(a.args)??u(v?.event.toolInput);P&&A(w.inputRefs,R(f,m,S(r,"tool_arguments","json",P)));const $=u(a.content)??u(d?.event.toolOutput)??u(d?.event.content);if($){const L=R(f,m,S(r,"tool_result","text",$));A(w.outputRefs,L),A(T,L)}g.push(w),O=p;continue}O=p}if(o.thinking){const r=g.filter(i=>i.kind==="llm_request").sort((i,p)=>Date.parse(i.endedAt??e.endedAt)-Date.parse(p.endedAt??e.endedAt)),a=new Map;for(const i of n.acpEvents){if(i.event.type!=="thinking")continue;const p=u(i.event.content);if(!p)continue;const v=Date.parse(i.observedAt),l=r.find(I=>{const d=Date.parse(I.endedAt??e.endedAt);return!Number.isFinite(v)||!Number.isFinite(d)||v<=d})??r.at(-1);l&&a.set(l.spanId,`${a.get(l.spanId)??""}${p}`)}for(const i of r){const p=a.get(i.spanId);p&&A(i.outputRefs,R(f,m,{role:"thinking",format:"text",value:p,captureLevel:"runtime_exact"}))}}return{spans:g,contents:f,anomalies:h,collection:n,capture:o}}async correlate(e,s){return s}validate(e,s){const n=s,o=n.spans.filter(c=>c.kind==="llm_request"),h=n.spans.filter(c=>c.kind==="tool_call"),f=!n.capture.inputOutput||e.input!==void 0,m=!n.capture.inputOutput||e.finalOutput!==void 0||n.contents.some(c=>c.role==="assistant_text"),g=!n.capture.tools||h.every(c=>c.status!=="running"),q=o.length>0&&o.every(c=>c.usage?.completeness==="complete"),y=[...n.collection.gaps];n.capture.rawProviderBody&&y.push({category:"raw_provider_body",reason:"raw_capture_disabled",detail:"Reasonix transcript does not retain raw provider HTTP bodies"});const x=f&&m&&g&&q&&y.length===0;return{status:n.collection.lines.length===0||o.length===0?"failed":x?"complete":"partial",inputComplete:f,outputComplete:m,toolCallsComplete:g,subagentsComplete:!0,usageComplete:q,rawRequestsComplete:!1,gaps:y}}}export{W as ReasonixAuditAdapter};
|
|
1
|
+
import{referenceCaptureContent as Q}from"../../core/content-reference.js";import{assessToolCallCompleteness as V,deduplicateGaps as K}from"../../core/tool-call-audit.js";import{InvalidReasonixUsageError as W,normalizeReasonixUsage as X}from"../../core/usage.js";import{collectReasonixTranscript as Y}from"./reasonix-transcript-source.js";const Z=Object.freeze({inputOutput:!0,tools:!0,subagents:!0,thinking:!0,rawProviderBody:!1});function j(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function f(t){return typeof t=="string"&&t.length>0?t:void 0}function P(t,e){const r=f(t);return r&&Number.isFinite(Date.parse(r))?r:e}function L(t){if(typeof t=="string")try{return JSON.parse(t),{format:"json",value:t}}catch{return{format:"text",value:t}}try{const e=JSON.stringify(t);return{format:"json",value:e===void 0?"null":e}}catch{return{format:"text",value:String(t)}}}function N(t){for(const[e,r]of t)if(e&&Object.hasOwn(e,r))return e[r]}function ee(t){return t.options?.enabled===!0?t.options.capture:Z}function J(t,e,r,o){return{role:e,format:r,value:o,sourcePath:t.sourcePath,sourceOffset:t.sourceOffset,sourceLength:t.sourceLength,captureLevel:"provider_exact"}}function O(t,e,r){const o=[r.role,r.sourcePath??"",r.sourceOffset??"",r.value??""].join("\0");if(e.has(o))return e.get(o);const p=Q(r);return e.set(o,p),t.push(r),p}function x(t,e){e&&(t.some(r=>r.contentId===e.contentId)||t.push(e))}function z(t){const e=t.startedAt??t.job.startedAt,r=t.endedAt??t.job.endedAt,o=Date.parse(e),p=Date.parse(r);return{spanId:t.spanId,traceId:t.job.traceId,turnId:t.job.turnId,sessionId:t.job.sessionId,...t.parentSpanId?{parentSpanId:t.parentSpanId}:{},kind:t.kind,name:t.name,sequence:t.sequence,status:t.status??"completed",startedAt:e,endedAt:r,...Number.isFinite(o)&&Number.isFinite(p)?{durationMs:Math.max(0,p-o)}:{},inputRefs:[],outputRefs:[],provider:{provider:"acp",nativeEventType:t.nativeEventType,nativeIds:Object.fromEntries(Object.entries(t.nativeIds??{}).filter(m=>typeof m[1]=="string")),...t.rawAttributes?{rawAttributes:t.rawAttributes}:{}},provenance:{source:"provider_transcript",accuracy:"provider_reported"}}}class re{provider="acp";async collect(e){return Y(e.boundary)}async normalize(e,r){const o=r,p=ee(e),m=[...o.anomalies],_=[],A=new Map,I=[];let $=0;const k=`reasonix:${e.turnId}:turn`,T=z({job:e,spanId:k,kind:"turn",name:"Reasonix audited turn",sequence:$++,status:e.outcome==="completed"?"completed":e.outcome==="cancelled"?"cancelled":"failed",nativeEventType:"acp_turn",nativeIds:{platformEventId:f(e.boundary.platformEventId),providerSessionId:f(e.boundary.providerSessionId)}});I.push(T);let w;p.inputOutput&&e.input&&(w=O(_,A,{...e.input,captureLevel:e.input.captureLevel??"runtime_exact"}),x(T.inputRefs,w)),p.inputOutput&&e.finalOutput&&x(T.outputRefs,O(_,A,{...e.finalOutput,captureLevel:e.finalOutput.captureLevel??"runtime_exact"}));const h=o.acpEvents.filter(({event:n})=>n.type==="tool_use"),G=new Map(h.filter(({event:n})=>typeof n.toolCallId=="string").map(n=>[String(n.event.toolCallId),n])),M=new Map(o.acpEvents.filter(({event:n})=>n.type==="tool_result"&&typeof n.toolCallId=="string").map(n=>[String(n.event.toolCallId),n])),U=new Set;let H=0,S=e.startedAt,B,F=w?[w]:[];for(const n of o.lines){const s=n.record,a=f(s.role),i=P(s.ts,S);if(a==="assistant_final"){const u=typeof s.turn=="number"?String(s.turn):`offset-${n.sourceOffset}`,l=`reasonix-${u}-${n.sourceOffset}`,v=f(s.model),d=z({job:e,spanId:`reasonix:${e.sessionId}:request:${l}`,parentSpanId:k,kind:"llm_request",name:v??"Reasonix model request",sequence:$++,startedAt:S,endedAt:i,nativeEventType:a,nativeIds:{requestId:l,providerTurn:u,providerSessionId:f(e.boundary.providerSessionId)},rawAttributes:{sourcePath:n.sourcePath,sourceOffset:n.sourceOffset,...v?{model:v}:{}}});v&&(d.modelId=v);for(const c of F)x(d.inputRefs,c);if(F=[],j(s.usage))try{d.usage=X(s.usage)}catch(c){m.push({code:"reasonix_provider_usage_invalid",message:c instanceof W?c.message:String(c),spanId:d.spanId,providerIds:{requestId:l}}),o.gaps.push({category:"provider_usage",reason:"provider_usage_missing",detail:l})}else o.gaps.push({category:"provider_usage",reason:"provider_usage_missing",detail:l});const R=f(s.content);p.inputOutput&&R&&x(d.outputRefs,O(_,A,J(n,"assistant_text","text",R))),I.push(d),B=d,S=i;continue}if(a==="tool"&&p.tools){const u=f(s.toolCallId??s.tool_call_id??s.callId??s.call_id);let l=u?G.get(u):void 0;for(u&&!l&&(m.push({code:"reasonix_tool_call_id_mismatch",message:`Reasonix transcript tool call ${u} has no matching ACP tool use`,providerIds:{toolCallId:u}}),o.gaps.push({category:"tool_call",reason:"correlation_failed",detail:`${u}:acp_use_not_found`}));!u&&!l&&H<h.length;){const y=h[H++],b=f(y?.event.toolCallId);(!b||!U.has(b))&&(l=y)}const v=f(l?.event.toolCallId),d=u??v??`offset-${n.sourceOffset}`;v&&U.add(v);const R=f(s.tool)??f(l?.event.toolName)??"tool",c=M.get(d),E=P(l?.observedAt,S),g=P(c?.observedAt,i),q=z({job:e,spanId:`reasonix:${e.sessionId}:tool:${d}`,parentSpanId:B?.spanId??k,kind:"tool_call",name:R,sequence:$++,status:s.error!==void 0||c?.event.isError===!0||c?.event.error!==void 0?"failed":c||Object.hasOwn(s,"content")?"completed":"running",startedAt:E,endedAt:g,nativeEventType:a,nativeIds:{toolCallId:d,requestId:B?.provider.nativeIds.requestId},rawAttributes:{sourcePath:n.sourcePath,sourceOffset:n.sourceOffset,hasAcpUse:!!l,hasAcpResult:!!c}});R==="tool"&&m.push({code:"reasonix_tool_name_missing",message:`Reasonix tool call ${d} has no name`,providerIds:{toolCallId:d}});const D=N([[s,"args"],[l?.event,"toolInput"],[l?.event,"input"]]);if(D!==void 0){const y=L(D);x(q.inputRefs,O(_,A,J(n,"tool_arguments",y.format,y.value)))}else m.push({code:"reasonix_tool_arguments_missing",message:`Reasonix tool call ${d} has no arguments payload`,providerIds:{toolCallId:d}}),o.gaps.push({category:"tool_arguments",reason:"correlation_failed",detail:d});const C=N([[s,"content"],[c?.event,"toolOutput"],[c?.event,"content"],[c?.event,"result"],[c?.event,"error"]]);if(C!==void 0){const y=L(C),b=O(_,A,J(n,"tool_result",y.format,y.value));x(q.outputRefs,b),x(F,b)}else m.push({code:"reasonix_tool_result_missing",message:`Reasonix tool call ${d} has no result`,spanId:q.spanId,providerIds:{toolCallId:d}}),o.gaps.push({category:"tool_result",reason:"correlation_failed",detail:d});I.push(q),S=i;continue}S=i}if(p.tools){const n=I.filter(s=>s.kind==="llm_request");for(const s of h){const a=f(s.event.toolCallId);if(a&&U.has(a))continue;const i=a??`acp-sequence-${s.sequence}`;a&&U.add(a);const u=a?M.get(a):void 0,l=P(s.observedAt,e.startedAt),v=P(u?.observedAt,e.endedAt),d=[...n].filter(g=>Date.parse(g.endedAt??g.startedAt)<=Date.parse(l)).sort((g,q)=>Date.parse(q.endedAt??q.startedAt)-Date.parse(g.endedAt??g.startedAt))[0],R=N([[u?.event,"toolOutput"],[u?.event,"content"],[u?.event,"result"],[u?.event,"error"]]),c=z({job:e,spanId:`reasonix:${e.sessionId}:tool:${i}`,parentSpanId:d?.spanId??k,kind:"tool_call",name:f(s.event.toolName)??"tool",sequence:$++,status:u?u.event.isError===!0||u.event.error!==void 0?"failed":"completed":"running",startedAt:l,endedAt:v,nativeEventType:"tool_use",nativeIds:{toolCallId:i,requestId:d?.provider.nativeIds.requestId},rawAttributes:{acpEventSequence:s.sequence,resultEventSequence:u?.sequence,transcriptRowMissing:!0}});c.provenance={source:"provider_event",accuracy:"provider_reported"};const E=N([[s.event,"toolInput"],[s.event,"input"]]);if(E!==void 0){const g=L(E);x(c.inputRefs,O(_,A,{role:"tool_arguments",format:g.format,value:g.value,captureLevel:"runtime_exact"}))}if(R!==void 0){const g=L(R),q=O(_,A,{role:"tool_result",format:g.format,value:g.value,captureLevel:"runtime_exact"});x(c.outputRefs,q);const D=[...n].filter(C=>C.spanId!==d?.spanId&&Date.parse(C.endedAt??C.startedAt)>=Date.parse(v)).sort((C,y)=>Date.parse(C.endedAt??C.startedAt)-Date.parse(y.endedAt??y.startedAt))[0];D&&x(D.inputRefs,q)}I.push(c),a||(m.push({code:"reasonix_tool_call_id_missing",message:`Reasonix ACP tool use sequence ${s.sequence} has no toolCallId`,providerIds:{toolCallId:i}}),o.gaps.push({category:"tool_call",reason:"correlation_failed",detail:`${i}:missing_id`})),E===void 0&&(m.push({code:"reasonix_tool_arguments_missing",message:`Reasonix tool call ${i} has no arguments payload`,spanId:c.spanId,providerIds:{toolCallId:i}}),o.gaps.push({category:"tool_arguments",reason:"correlation_failed",detail:i})),R===void 0&&(m.push({code:"reasonix_tool_result_missing",message:`Reasonix tool call ${i} has no result`,spanId:c.spanId,providerIds:{toolCallId:i}}),o.gaps.push({category:"tool_result",reason:"correlation_failed",detail:i}))}for(const[s]of M)G.has(s)||(m.push({code:"reasonix_tool_result_without_call",message:`Reasonix tool result ${s} has no matching tool use`,providerIds:{toolCallId:s}}),o.gaps.push({category:"tool_call",reason:"correlation_failed",detail:`${s}:orphan_result`}))}if(p.thinking){const n=I.filter(a=>a.kind==="llm_request").sort((a,i)=>Date.parse(a.endedAt??e.endedAt)-Date.parse(i.endedAt??e.endedAt)),s=new Map;for(const a of o.acpEvents){if(a.event.type!=="thinking")continue;const i=f(a.event.content);if(!i)continue;const u=Date.parse(a.observedAt),l=n.find(v=>{const d=Date.parse(v.endedAt??e.endedAt);return!Number.isFinite(u)||!Number.isFinite(d)||u<=d})??n.at(-1);l&&s.set(l.spanId,`${s.get(l.spanId)??""}${i}`)}for(const a of n){const i=s.get(a.spanId);i&&x(a.outputRefs,O(_,A,{role:"thinking",format:"text",value:i,captureLevel:"runtime_exact"}))}}return I.sort((n,s)=>{if(n.kind==="turn"&&s.kind!=="turn")return-1;if(s.kind==="turn"&&n.kind!=="turn")return 1;const a=n.kind==="llm_request"?n.endedAt??n.startedAt:n.startedAt,i=s.kind==="llm_request"?s.endedAt??s.startedAt:s.startedAt;return Date.parse(a)-Date.parse(i)||n.sequence-s.sequence}),I.forEach((n,s)=>{n.sequence=s}),{spans:I,contents:_,anomalies:m,collection:o,capture:p}}async correlate(e,r){return r}validate(e,r){const o=r,p=o.spans.filter(h=>h.kind==="llm_request"),m=o.spans.filter(h=>h.kind==="tool_call"),_=!o.capture.inputOutput||e.input!==void 0,A=!o.capture.inputOutput||e.finalOutput!==void 0||o.contents.some(h=>h.role==="assistant_text"),I=V(m,o.capture.tools),$=I.complete,k=p.length>0&&p.every(h=>h.usage?.completeness==="complete"),T=K([...o.collection.gaps,...I.gaps]);o.capture.rawProviderBody&&T.push({category:"raw_provider_body",reason:"raw_capture_disabled",detail:"Reasonix transcript does not retain raw provider HTTP bodies"});const w=_&&A&&$&&k&&T.length===0;return{status:o.collection.lines.length===0||p.length===0?"failed":w?"complete":"partial",inputComplete:_,outputComplete:A,toolCallsComplete:$,subagentsComplete:!0,usageComplete:k,rawRequestsComplete:!1,gaps:T}}}export{re as ReasonixAuditAdapter};
|