@lotagate/cli 0.1.26 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/agent/bounded-text-accumulator.d.ts +15 -0
- package/dist/application/agent/bounded-text-accumulator.js +1 -0
- package/dist/application/agent/cli-agent-execution-service.d.ts +1 -1
- package/dist/application/agent/cli-agent-execution-service.js +1 -1
- package/dist/application/commands/application-command-executor.js +2 -2
- package/dist/application/commands/cli-command-services.d.ts +1 -1
- package/dist/application/commands/cli-command-services.js +31 -31
- package/dist/application/commands/goal-command-handlers.js +6 -6
- package/dist/application/commands/memory-command-handler.d.ts +1 -1
- package/dist/application/commands/memory-command-handler.js +11 -11
- package/dist/application/commands/runtime-command-handlers.js +11 -11
- package/dist/application/extensions/plugin-contribution-registry.js +1 -1
- package/dist/application/lifecycle/operation-registry.d.ts +11 -0
- package/dist/application/lifecycle/operation-registry.js +1 -0
- package/dist/application/orchestration/headless-agent-turn.js +3 -3
- package/dist/application/protocol/desktop-agent-command.js +1 -3
- package/dist/application/protocol/desktop-title-generator.d.ts +5 -0
- package/dist/application/protocol/desktop-title-generator.js +3 -0
- package/dist/application/protocol/jsonl-agent-command.js +2 -2
- package/dist/application/workspace/workspace-file-index.d.ts +2 -2
- package/dist/application/workspace/workspace-file-index.js +1 -1
- package/dist/domain/auth/auth-profile.d.ts +1 -0
- package/dist/domain/auth/auth-profile.js +1 -1
- package/dist/domain/runtime/resource-limits.d.ts +9 -0
- package/dist/domain/runtime/resource-limits.js +1 -1
- package/dist/infrastructure/agent-sdk/tool-calling-model-client.js +1 -1
- package/dist/infrastructure/cache/file-cache-store.d.ts +5 -0
- package/dist/infrastructure/cache/file-cache-store.js +2 -2
- package/dist/infrastructure/clipboard/clipboard-adapter-factory.js +1 -1
- package/dist/infrastructure/clipboard/clipboard-process.d.ts +20 -0
- package/dist/infrastructure/clipboard/clipboard-process.js +1 -0
- package/dist/infrastructure/clipboard/clipboard-text-adapter-factory.js +1 -1
- package/dist/infrastructure/clipboard/windows-clipboard-adapter.js +1 -1
- package/dist/infrastructure/clipboard/windows-clipboard-text-adapter.js +1 -1
- package/dist/infrastructure/credentials/encrypted-file-credential-store.js +2 -2
- package/dist/infrastructure/credentials/secret-input.d.ts +1 -1
- package/dist/infrastructure/credentials/secret-input.js +3 -3
- package/dist/infrastructure/extensions/mcp-runtime.d.ts +2 -0
- package/dist/infrastructure/extensions/mcp-runtime.js +1 -1
- package/dist/infrastructure/extensions/plugin-agent-discovery.d.ts +3 -1
- package/dist/infrastructure/extensions/plugin-agent-discovery.js +1 -1
- package/dist/infrastructure/extensions/skill-manager.js +2 -2
- package/dist/infrastructure/filesystem/atomic-file-store.js +1 -1
- package/dist/infrastructure/sessions/file-session-store.d.ts +8 -0
- package/dist/infrastructure/sessions/file-session-store.js +4 -4
- package/dist/main.js +2 -2
- package/dist/presentation/tui/tui-agent-turn-controller.js +1 -1
- package/dist/presentation/tui/tui-application.d.ts +2 -0
- package/dist/presentation/tui/tui-application.js +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
import k from"node:crypto";import T from"node:path";import{AgentSdkError as _,AgentSdkErrorCode as D,DEFAULT_MCP_STARTUP_CONCURRENCY as C,mapWithMcpConcurrency as j,McpClientManager as F,McpToolExecutor as G}from"@lotagate/agent-sdk";import{CliError as A}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as P}from"../../domain/errors/error-codes.js";import{FileMcpConfigStore as $}from"./file-mcp-config-store.js";import{readBoundedEnvironmentInteger as h}from"../config/environment-values.js";import{redactLogText as v}from"../logging/log-redactor.js";import{DEFAULT_MCP_CONNECT_TIMEOUT_MS as H,DEFAULT_MCP_DISCONNECT_TIMEOUT_MS as M}from"./extensions-constants.js";function K(t=process.env){return{connectTimeoutMs:h(t.LOTAGATE_MCP_CONNECT_TIMEOUT_MS,H,500,12e4),disconnectTimeoutMs:h(t.LOTAGATE_MCP_DISCONNECT_TIMEOUT_MS,M,500,12e4),idleTtlMs:h(t.LOTAGATE_MCP_RUNTIME_TTL_SECONDS,300,1,86400)*1e3,startupConcurrency:h(t.LOTAGATE_MCP_STARTUP_CONCURRENCY,C,1,32),probeTimeoutMs:h(t.LOTAGATE_MCP_PROBE_TIMEOUT_MS,5e3,500,12e4),probeMaxRetries:h(t.LOTAGATE_MCP_PROBE_MAX_RETRIES,1,0,5),reconnectMaxAttempts:h(t.LOTAGATE_MCP_RECONNECT_MAX_ATTEMPTS,3,0,10),healthCheckIntervalMs:h(t.LOTAGATE_MCP_HEALTH_CHECK_INTERVAL_SECONDS,30,5,3600)*1e3,healthCheckTimeoutMs:h(t.LOTAGATE_MCP_HEALTH_CHECK_TIMEOUT_MS,5e3,500,12e4)}}class re{storeFactory;policy;logger;now;environment;runtimes=new Map;retiring=new Map;keyLocks=new Map;closed=!1;constructor(e=a=>new $(a),n=K(),o,s=Date.now,r=process.env){this.storeFactory=e,this.policy=n,this.logger=o,this.now=s,this.environment=r}async ensure(e,n,o,s={}){if(!o)throw new A(P.PROJECT_TRUST_REQUIRED,"MCP connections require project trust.",{category:"project",userMessage:"Trust the project before connecting to MCP servers."});const r=T.resolve(e);return this.withKeyLock(r,async()=>this.ensureUnlocked(r,n,s))}async connectConfigured(e,n){if(!n)throw new A(P.PROJECT_TRUST_REQUIRED,"MCP connections require project trust.",{category:"project",userMessage:"Trust the project before connecting to MCP servers."});const o=await this.storeFactory(e).list(),s=S(I(o,this.environment),this.policy,new Set);try{return await this.connectAndDiscover(T.resolve(e),s.manager,s.executor,new Set)}finally{await R(s.manager,this.policy.disconnectTimeoutMs??M,this.logger,T.resolve(e))}}async invalidate(e){const n=T.resolve(e);await this.withKeyLock(n,()=>this.invalidateUnlocked(n))}async close(){this.closed=!0,await Promise.all([...this.keyLocks.values()]);const e=[...this.runtimes.values()];this.runtimes.clear();for(const n of e)p(n);await Promise.allSettled(e.map(n=>R(n.manager,this.policy.disconnectTimeoutMs??M,this.logger,n.cwd)))}async ensureUnlocked(e,n,o){if(this.closed)throw new Error("MCP runtime is closed.");await this.waitForRetiring(e);const s=B(n),r=this.runtimes.get(e);if(r?.failed&&r.fingerprint===s&&r.leases>0)return p(r),r.lastUsedAt=this.now(),r.leases+=1,E(r,o.onEvent),await r.ready,w(r,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,r));if(r!==void 0&&!r.failed&&r.fingerprint===s&&(r.leases>0||this.now()-r.lastUsedAt<=this.policy.idleTtlMs)){p(r);const u=r.leases===0;r.lastUsedAt=this.now(),r.leases+=1,E(r,o.onEvent),await r.ready;const d=w(r,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,r));return u&&!r.failed&&z(r.manager)?(d.release(),await this.invalidateUnlocked(e),this.ensureUnlocked(e,n,o)):d}r!==void 0&&await this.invalidateUnlocked(e),await this.waitForRetiring(e);const a=new Set;E(a,o.onEvent);const i=S(I(n,this.environment),this.policy,a,o.reconnect??!0),c={cwd:e,fingerprint:s,manager:i.manager,executor:i.executor,ready:this.connectAndDiscover(e,i.manager,i.executor,a),listeners:a,failed:!1,lastUsedAt:this.now(),leases:1,idleTimer:void 0};this.runtimes.set(e,c);const l=await c.ready;return c.failed=l.some(u=>u.status==="failed"),w(c,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,c))}async invalidateUnlocked(e){const n=this.runtimes.get(e);if(n===void 0)return;this.runtimes.delete(e),p(n),n.listeners.clear();const o=O(n.manager,this.policy.disconnectTimeoutMs??M,this.logger,e),s=o.settled.finally(()=>{this.retiring.get(e)===s&&this.retiring.delete(e)});this.retiring.set(e,s),await o.withinTimeout,this.logger?.info("MCP runtime invalidated.",{cwd:e})}async waitForRetiring(e){await this.retiring.get(e)}async withKeyLock(e,n){const o=this.keyLocks.get(e);let s;const r=new Promise(i=>{s=i}),a=(o??Promise.resolve()).then(()=>r);this.keyLocks.set(e,a),await o;try{return await n()}finally{s(),this.keyLocks.get(e)===a&&this.keyLocks.delete(e)}}scheduleIdleInvalidation(e,n){p(n),n.idleTimer=setTimeout(()=>{if(n.idleTimer=void 0,!(this.runtimes.get(e)!==n||n.leases>0)){if(this.now()-n.lastUsedAt<this.policy.idleTtlMs){this.scheduleIdleInvalidation(e,n);return}this.invalidate(e)}},this.policy.idleTtlMs),n.idleTimer.unref?.()}async connectAndDiscover(e,n,o,s){const r=performance.now();this.logger?.debug("MCP runtime startup started.",{cwd:e,serverCount:n.configuredServerIds.length,startupConcurrency:this.policy.startupConcurrency??C});const a=await j(n.configuredServerIds,async i=>{const c=performance.now();try{m(s,{type:"mcp.server.status",summary:{serverId:i,status:"connecting"}});const l=await b(n.connect(i),this.policy.connectTimeoutMs,()=>n.disconnect(i),"connection");if(m(s,{type:"mcp.server.status",summary:{...l,latencyMs:f(c)}}),this.logger?.debug("MCP server connected.",{cwd:e,serverId:i,durationMs:f(c),protocolEra:l.protocolEra,protocolVersion:l.protocolVersion}),l.status!=="connected")return l;try{const u=performance.now(),d=await b(n.listTools(i),this.policy.connectTimeoutMs,()=>n.disconnect(i),"tool discovery");o.replaceServerTools(i,d.tools),m(s,{type:"mcp.tool.catalog.updated",serverId:i,toolCount:d.tools.length}),this.logger?.debug("MCP tools discovered.",{cwd:e,serverId:i,toolCount:d.tools.length,durationMs:f(u),totalDurationMs:f(c)})}catch(u){const d=U(u,i);return m(s,{type:"mcp.server.status",summary:{...l,status:"failed",error:d,latencyMs:f(c)}}),m(s,{type:"mcp.tool.catalog.updated",serverId:i,toolCount:0,error:d}),this.logger?.warn("MCP tool discovery failed.",{cwd:e,serverId:i,durationMs:f(c),reason:d.message,...L(u)}),{...l,status:"failed",error:d}}return l}catch(l){const u=U(l,i);return m(s,{type:"mcp.server.status",summary:{serverId:i,status:"failed",error:u,latencyMs:f(c)}}),this.logger?.warn("MCP server connection failed.",{cwd:e,serverId:i,durationMs:f(c),reason:u.message,...L(l)}),{serverId:i,status:"failed",error:u}}},this.policy.startupConcurrency??C);return this.logger?.info("MCP runtime startup completed.",{cwd:e,serverCount:a.length,connectedCount:a.filter(i=>i.status==="connected").length,failedCount:a.filter(i=>i.status==="failed").length,durationMs:f(r)}),a}}function S(t,e,n,o=!0){let s;const r=new F(t,{protocol:{mode:"auto",...e.probeTimeoutMs===void 0?{}:{probeTimeoutMs:e.probeTimeoutMs},...e.probeMaxRetries===void 0?{}:{probeMaxRetries:e.probeMaxRetries}},reconnect:{enabled:o,...e.reconnectMaxAttempts===void 0?{}:{maxAttempts:e.reconnectMaxAttempts},...e.healthCheckIntervalMs===void 0?{}:{healthCheckIntervalMs:e.healthCheckIntervalMs},...e.healthCheckTimeoutMs===void 0?{}:{healthCheckTimeoutMs:e.healthCheckTimeoutMs}},onToolsChanged:a=>{s!==void 0&&(a.tools===null||a.error!==null?s.removeServerTools(a.serverId):s.replaceServerTools(a.serverId,a.tools),m(n,{type:"mcp.tool.catalog.updated",serverId:a.serverId,toolCount:a.tools?.length??0,...a.error===null?{}:{error:a.error}}))},onServerChanged:a=>{a.type==="removed"&&s?.removeServerTools(a.serverId)}});return s=new G(r),{manager:r,executor:s}}function O(t,e,n,o){let s,r=!1;const a=Promise.resolve().then(()=>t.disconnectAll()),i=new Promise(u=>{s=setTimeout(()=>{r=!0,u()},e)}),c=a.then(()=>{},u=>{r||n?.warn("MCP runtime disconnect failed.",{cwd:o,reason:u instanceof Error?u.message:String(u)})});return{withinTimeout:Promise.race([c,i]).finally(()=>{s!==void 0&&clearTimeout(s),r&&n?.warn("MCP runtime disconnect timed out.",{cwd:o,timeoutMs:e})}),settled:c}}async function R(t,e,n,o){await O(t,e,n,o).withinTimeout}function w(t,e,n,o){let s=!1;return{executor:t.executor,release:()=>{s||(s=!0,t.leases=Math.max(0,t.leases-1),t.lastUsedAt=e(),J(t,n),t.leases===0&&o?.())}}}function p(t){t.idleTimer!==void 0&&clearTimeout(t.idleTimer),t.idleTimer=void 0}function E(t,e){if(e!==void 0){if(t instanceof Set){t.add(e);return}t.listeners.add(e)}}function J(t,e){e!==void 0&&t.listeners.delete(e)}function m(t,e){for(const n of t)Promise.resolve(n(e)).catch(()=>{})}function z(t){const e=t.connectionSummaries;return e.length>0&&e.some(n=>n.status!=="connected")}function B(t){return k.createHash("sha256").update(y(t)).digest("hex")}function y(t){return Array.isArray(t)?`[${t.map(y).join(",")}]`:typeof t!="object"||t===null?JSON.stringify(t):`{${Object.keys(t).sort().map(e=>`${JSON.stringify(e)}:${y(t[e])}`).join(",")}}`}async function b(t,e,n,o){let s,r=!1;const a=new Promise((i,c)=>{s=setTimeout(()=>{r||(r=!0,c(new Error(`MCP ${o} timed out after ${e}ms.`)),Promise.resolve(n()).catch(()=>{}))},e)});try{return await Promise.race([t.then(c=>(r=!0,c)),a])}finally{s!==void 0&&clearTimeout(s),t.catch(()=>{})}}function f(t){return Math.max(0,Math.round(performance.now()-t))}function U(t,e){return t instanceof _?t:new _(D.McpConnectionFailed,"MCP server connection failed.",{cause:t,details:{serverId:e}})}function L(t){const e=[],n=[],o=[],s=[];let r=t;for(let a=0;a<4&&r!==void 0&&r!==null;a+=1){if(r instanceof Error){e.push(v(r.message)),n.push(r.name),r=r.cause;continue}if(typeof r!="object")break;const i=r,c=i.message;typeof c=="string"&&e.push(v(c));const l=i.name;typeof l=="string"&&n.push(l);const u=i.code;typeof u=="string"&&o.push(u);for(const d of["status","statusCode","httpStatus"]){const g=i[d];typeof g=="number"&&Number.isInteger(g)&&s.push(g)}r=i.cause}return{...e.length===0?{}:{errorMessages:e},...n.length===0?{}:{errorTypes:n},...o.length===0?{}:{errorCodes:o},...s.length===0?{}:{httpStatuses:s}}}function I(t,e=process.env){const n={};for(const[o,s]of Object.entries(t))s.enabled&&(n[o]=V(s.config,e));return n}function V(t,e){if(t.type==="stdio"){const{env:r,...a}=t;return{...a,...t.args===void 0?{}:{args:[...t.args]},...r===void 0?{}:{env:x(r,e)}}}const{headers:n,auth:o,...s}=t;return{...s,...n===void 0?{}:{headers:x(n,e)},...o?.type==="bearer"?{auth:{type:"bearer",token:N(o.token,e,"MCP bearer token")}}:{}}}function x(t,e){return Object.fromEntries(Object.entries(t).map(([n,o])=>[n,N(o,e,`MCP secret ${n}`)]))}function N(t,e,n){if(typeof t=="string")return t;const o=e[t.name];if(o===void 0||o.length===0)throw new Error(`${n} environment variable is not set.`);return o}export{re as McpRuntime,K as resolveMcpRuntimePolicy,I as toAgentMcpConfig};
|
|
1
|
+
import N from"node:crypto";import T from"node:path";import{AgentSdkError as P,AgentSdkErrorCode as k,DEFAULT_MCP_STARTUP_CONCURRENCY as C,mapWithMcpConcurrency as D,McpClientManager as j,McpToolExecutor as F}from"@lotagate/agent-sdk";import{CliError as v}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as A}from"../../domain/errors/error-codes.js";import{FileMcpConfigStore as G}from"./file-mcp-config-store.js";import{readBoundedEnvironmentInteger as h}from"../config/environment-values.js";import{redactLogText as S}from"../logging/log-redactor.js";import{DEFAULT_MCP_CONNECT_TIMEOUT_MS as $,DEFAULT_MCP_DISCONNECT_TIMEOUT_MS as g}from"./extensions-constants.js";function H(n=process.env){return{connectTimeoutMs:h(n.LOTAGATE_MCP_CONNECT_TIMEOUT_MS,$,500,12e4),disconnectTimeoutMs:h(n.LOTAGATE_MCP_DISCONNECT_TIMEOUT_MS,g,500,12e4),idleTtlMs:h(n.LOTAGATE_MCP_RUNTIME_TTL_SECONDS,300,1,86400)*1e3,startupConcurrency:h(n.LOTAGATE_MCP_STARTUP_CONCURRENCY,C,1,32),probeTimeoutMs:h(n.LOTAGATE_MCP_PROBE_TIMEOUT_MS,5e3,500,12e4),probeMaxRetries:h(n.LOTAGATE_MCP_PROBE_MAX_RETRIES,1,0,5),reconnectMaxAttempts:h(n.LOTAGATE_MCP_RECONNECT_MAX_ATTEMPTS,3,0,10),healthCheckIntervalMs:h(n.LOTAGATE_MCP_HEALTH_CHECK_INTERVAL_SECONDS,30,5,3600)*1e3,healthCheckTimeoutMs:h(n.LOTAGATE_MCP_HEALTH_CHECK_TIMEOUT_MS,5e3,500,12e4)}}class se{storeFactory;policy;logger;now;environment;runtimes=new Map;retiring=new Map;cleanupOperations=new Set;keyLocks=new Map;closed=!1;constructor(e=a=>new G(a),t=H(),o,r=Date.now,s=process.env){this.storeFactory=e,this.policy=t,this.logger=o,this.now=r,this.environment=s}async ensure(e,t,o,r={}){if(!o)throw new v(A.PROJECT_TRUST_REQUIRED,"MCP connections require project trust.",{category:"project",userMessage:"Trust the project before connecting to MCP servers."});const s=T.resolve(e);return this.withKeyLock(s,async()=>this.ensureUnlocked(s,t,r))}async connectConfigured(e,t){if(!t)throw new v(A.PROJECT_TRUST_REQUIRED,"MCP connections require project trust.",{category:"project",userMessage:"Trust the project before connecting to MCP servers."});const o=await this.storeFactory(e).list(),r=O(L(o,this.environment),this.policy,new Set);try{return await this.connectAndDiscover(T.resolve(e),r.manager,r.executor,new Set)}finally{await K(r.manager,this.policy.disconnectTimeoutMs??g,this.logger,T.resolve(e))}}async invalidate(e){const t=T.resolve(e);await this.withKeyLock(t,()=>this.invalidateUnlocked(t))}async close(){this.closed=!0,await Promise.all([...this.keyLocks.values()]);const e=[...this.runtimes.values()];this.runtimes.clear();for(const r of e)p(r);const t=this.policy.disconnectTimeoutMs??g,o=e.map(r=>w(r.manager,t,this.logger,r.cwd));await Promise.allSettled(o.map(r=>r.withinTimeout)),await J([...o.map(r=>r.settled),...this.retiring.values(),...this.cleanupOperations],t,this.logger)}async ensureUnlocked(e,t,o){if(this.closed)throw new Error("MCP runtime is closed.");await this.waitForRetiring(e);const r=V(t),s=this.runtimes.get(e);if(s?.failed&&s.fingerprint===r&&s.leases>0)return p(s),s.lastUsedAt=this.now(),s.leases+=1,y(s,o.onEvent),await s.ready,E(s,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,s));if(s!==void 0&&!s.failed&&s.fingerprint===r&&(s.leases>0||this.now()-s.lastUsedAt<=this.policy.idleTtlMs)){p(s);const l=s.leases===0;s.lastUsedAt=this.now(),s.leases+=1,y(s,o.onEvent),await s.ready;const d=E(s,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,s));return l&&!s.failed&&B(s.manager)?(d.release(),await this.invalidateUnlocked(e),this.ensureUnlocked(e,t,o)):d}s!==void 0&&await this.invalidateUnlocked(e),await this.waitForRetiring(e);const a=new Set;y(a,o.onEvent);const i=O(L(t,this.environment),this.policy,a,o.reconnect??!0),c={cwd:e,fingerprint:r,manager:i.manager,executor:i.executor,ready:this.connectAndDiscover(e,i.manager,i.executor,a),listeners:a,failed:!1,lastUsedAt:this.now(),leases:1,idleTimer:void 0};this.runtimes.set(e,c);const u=await c.ready;return c.failed=u.some(l=>l.status==="failed"),E(c,this.now,o.onEvent,()=>this.scheduleIdleInvalidation(e,c))}async invalidateUnlocked(e){const t=this.runtimes.get(e);if(t===void 0)return;this.runtimes.delete(e),p(t),t.listeners.clear();const o=w(t.manager,this.policy.disconnectTimeoutMs??g,this.logger,e),r=o.settled.finally(()=>{this.retiring.get(e)===r&&this.retiring.delete(e)});this.retiring.set(e,r),await o.withinTimeout,this.logger?.info("MCP runtime invalidated.",{cwd:e})}async waitForRetiring(e){await this.retiring.get(e)}async withKeyLock(e,t){const o=this.keyLocks.get(e);let r;const s=new Promise(i=>{r=i}),a=(o??Promise.resolve()).then(()=>s);this.keyLocks.set(e,a),await o;try{return await t()}finally{r(),this.keyLocks.get(e)===a&&this.keyLocks.delete(e)}}scheduleIdleInvalidation(e,t){p(t),t.idleTimer=setTimeout(()=>{if(t.idleTimer=void 0,!(this.runtimes.get(e)!==t||t.leases>0)){if(this.now()-t.lastUsedAt<this.policy.idleTtlMs){this.scheduleIdleInvalidation(e,t);return}this.invalidate(e)}},this.policy.idleTtlMs),t.idleTimer.unref?.()}async connectAndDiscover(e,t,o,r){const s=performance.now();this.logger?.debug("MCP runtime startup started.",{cwd:e,serverCount:t.configuredServerIds.length,startupConcurrency:this.policy.startupConcurrency??C});const a=await D(t.configuredServerIds,async i=>{const c=performance.now();try{f(r,{type:"mcp.server.status",summary:{serverId:i,status:"connecting"}});const u=await R(t.connect(i),this.policy.connectTimeoutMs,()=>this.trackCleanup(()=>t.disconnect(i)),"connection");if(f(r,{type:"mcp.server.status",summary:{...u,latencyMs:m(c)}}),this.logger?.debug("MCP server connected.",{cwd:e,serverId:i,durationMs:m(c),protocolEra:u.protocolEra,protocolVersion:u.protocolVersion}),u.status!=="connected")return u;try{const l=performance.now(),d=await R(t.listTools(i),this.policy.connectTimeoutMs,()=>this.trackCleanup(()=>t.disconnect(i)),"tool discovery");o.replaceServerTools(i,d.tools),f(r,{type:"mcp.tool.catalog.updated",serverId:i,toolCount:d.tools.length}),this.logger?.debug("MCP tools discovered.",{cwd:e,serverId:i,toolCount:d.tools.length,durationMs:m(l),totalDurationMs:m(c)})}catch(l){const d=b(l,i);return f(r,{type:"mcp.server.status",summary:{...u,status:"failed",error:d,latencyMs:m(c)}}),f(r,{type:"mcp.tool.catalog.updated",serverId:i,toolCount:0,error:d}),this.logger?.warn("MCP tool discovery failed.",{cwd:e,serverId:i,durationMs:m(c),reason:d.message,...U(l)}),{...u,status:"failed",error:d}}return u}catch(u){const l=b(u,i);return f(r,{type:"mcp.server.status",summary:{serverId:i,status:"failed",error:l,latencyMs:m(c)}}),this.logger?.warn("MCP server connection failed.",{cwd:e,serverId:i,durationMs:m(c),reason:l.message,...U(u)}),{serverId:i,status:"failed",error:l}}},this.policy.startupConcurrency??C);return this.logger?.info("MCP runtime startup completed.",{cwd:e,serverCount:a.length,connectedCount:a.filter(i=>i.status==="connected").length,failedCount:a.filter(i=>i.status==="failed").length,durationMs:m(s)}),a}trackCleanup(e){const t=Promise.resolve().then(e).catch(o=>{this.logger?.warn("MCP timeout cleanup failed.",{reason:o instanceof Error?o.message:String(o)})});return this.cleanupOperations.add(t),t.then(()=>this.cleanupOperations.delete(t),()=>this.cleanupOperations.delete(t)),t}}function O(n,e,t,o=!0){let r;const s=new j(n,{protocol:{mode:"auto",...e.probeTimeoutMs===void 0?{}:{probeTimeoutMs:e.probeTimeoutMs},...e.probeMaxRetries===void 0?{}:{probeMaxRetries:e.probeMaxRetries}},reconnect:{enabled:o,...e.reconnectMaxAttempts===void 0?{}:{maxAttempts:e.reconnectMaxAttempts},...e.healthCheckIntervalMs===void 0?{}:{healthCheckIntervalMs:e.healthCheckIntervalMs},...e.healthCheckTimeoutMs===void 0?{}:{healthCheckTimeoutMs:e.healthCheckTimeoutMs}},onToolsChanged:a=>{r!==void 0&&(a.tools===null||a.error!==null?r.removeServerTools(a.serverId):r.replaceServerTools(a.serverId,a.tools),f(t,{type:"mcp.tool.catalog.updated",serverId:a.serverId,toolCount:a.tools?.length??0,...a.error===null?{}:{error:a.error}}))},onServerChanged:a=>{a.type==="removed"&&r?.removeServerTools(a.serverId)}});return r=new F(s),{manager:s,executor:r}}function w(n,e,t,o){let r,s=!1;const a=Promise.resolve().then(()=>n.disconnectAll()),i=new Promise(l=>{r=setTimeout(()=>{s=!0,l()},e)}),c=a.then(()=>{},l=>{s||t?.warn("MCP runtime disconnect failed.",{cwd:o,reason:l instanceof Error?l.message:String(l)})});return{withinTimeout:Promise.race([c,i]).finally(()=>{r!==void 0&&clearTimeout(r),s&&t?.warn("MCP runtime disconnect timed out.",{cwd:o,timeoutMs:e})}),settled:c}}async function K(n,e,t,o){await w(n,e,t,o).withinTimeout}async function J(n,e,t){if(n.length===0)return;let o,r=!1;await Promise.race([Promise.allSettled(n).then(()=>{r=!0}),new Promise(s=>{o=setTimeout(s,e)})]),o!==void 0&&clearTimeout(o),r?t?.debug("MCP runtime drain completed.",{operationCount:n.length}):t?.warn("MCP runtime drain reached its deadline.",{operationCount:n.length,timeoutMs:e})}function E(n,e,t,o){let r=!1;return{executor:n.executor,release:()=>{r||(r=!0,n.leases=Math.max(0,n.leases-1),n.lastUsedAt=e(),z(n,t),n.leases===0&&o?.())}}}function p(n){n.idleTimer!==void 0&&clearTimeout(n.idleTimer),n.idleTimer=void 0}function y(n,e){if(e!==void 0){if(n instanceof Set){n.add(e);return}n.listeners.add(e)}}function z(n,e){e!==void 0&&n.listeners.delete(e)}function f(n,e){for(const t of n)Promise.resolve(t(e)).catch(()=>{})}function B(n){const e=n.connectionSummaries;return e.length>0&&e.some(t=>t.status!=="connected")}function V(n){return N.createHash("sha256").update(_(n)).digest("hex")}function _(n){return Array.isArray(n)?`[${n.map(_).join(",")}]`:typeof n!="object"||n===null?JSON.stringify(n):`{${Object.keys(n).sort().map(e=>`${JSON.stringify(e)}:${_(n[e])}`).join(",")}}`}async function R(n,e,t,o){let r,s=!1;const a=new Promise((i,c)=>{r=setTimeout(()=>{s||(s=!0,c(new Error(`MCP ${o} timed out after ${e}ms.`)),Promise.resolve(t()).catch(()=>{}))},e)});try{return await Promise.race([n.then(c=>(s=!0,c)),a])}finally{r!==void 0&&clearTimeout(r),n.catch(()=>{})}}function m(n){return Math.max(0,Math.round(performance.now()-n))}function b(n,e){return n instanceof P?n:new P(k.McpConnectionFailed,"MCP server connection failed.",{cause:n,details:{serverId:e}})}function U(n){const e=[],t=[],o=[],r=[];let s=n;for(let a=0;a<4&&s!==void 0&&s!==null;a+=1){if(s instanceof Error){e.push(S(s.message)),t.push(s.name),s=s.cause;continue}if(typeof s!="object")break;const i=s,c=i.message;typeof c=="string"&&e.push(S(c));const u=i.name;typeof u=="string"&&t.push(u);const l=i.code;typeof l=="string"&&o.push(l);for(const d of["status","statusCode","httpStatus"]){const M=i[d];typeof M=="number"&&Number.isInteger(M)&&r.push(M)}s=i.cause}return{...e.length===0?{}:{errorMessages:e},...t.length===0?{}:{errorTypes:t},...o.length===0?{}:{errorCodes:o},...r.length===0?{}:{httpStatuses:r}}}function L(n,e=process.env){const t={};for(const[o,r]of Object.entries(n))r.enabled&&(t[o]=q(r.config,e));return t}function q(n,e){if(n.type==="stdio"){const{env:s,...a}=n;return{...a,...n.args===void 0?{}:{args:[...n.args]},...s===void 0?{}:{env:I(s,e)}}}const{headers:t,auth:o,...r}=n;return{...r,...t===void 0?{}:{headers:I(t,e)},...o?.type==="bearer"?{auth:{type:"bearer",token:x(o.token,e,"MCP bearer token")}}:{}}}function I(n,e){return Object.fromEntries(Object.entries(n).map(([t,o])=>[t,x(o,e,`MCP secret ${t}`)]))}function x(n,e,t){if(typeof n=="string")return n;const o=e[n.name];if(o===void 0||o.length===0)throw new Error(`${t} environment variable is not set.`);return o}export{se as McpRuntime,H as resolveMcpRuntimePolicy,L as toAgentMcpConfig};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type PluginAgentDefinition } from '../../domain/extensions/plugin-agent-manifest.js';
|
|
2
2
|
export declare class PluginAgentDiscovery {
|
|
3
|
-
discover(root: string
|
|
3
|
+
discover(root: string, options?: {
|
|
4
|
+
readonly maxEntries?: number;
|
|
5
|
+
}): Promise<readonly PluginAgentDefinition[]>;
|
|
4
6
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import c from"node:fs/promises";import
|
|
1
|
+
import c from"node:fs/promises";import g from"node:path";import{parsePluginAgentDocument as p}from"../../domain/extensions/plugin-agent-manifest.js";import{CliError as d}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as h}from"../../domain/errors/error-codes.js";import{isFileNotFound as y}from"../filesystem/fs-errors.js";import{readFileBounded as w}from"../filesystem/bounded-file-reader.js";import{RESOURCE_LIMITS as u}from"../../domain/runtime/resource-limits.js";class C{async discover(r,f={}){let s;try{s=await c.readdir(r,{withFileTypes:!0})}catch(t){if(y(t))return[];throw t}const a=[];for(const t of s.filter(e=>e.isFile()&&e.name.endsWith(".yaml")).sort((e,i)=>e.name.localeCompare(i.name))){if(a.length>=(f.maxEntries??u.pluginMaxAgentFiles))throw n("Plugin contains too many agent definitions.");const e=t.name.slice(0,-5);if(!/^[a-z0-9][a-z0-9-]{0,63}$/u.test(e))throw n(`Invalid plugin agent file name: ${t.name}`);const i=g.join(r,t.name);if((await c.lstat(i)).isSymbolicLink())throw n(`Plugin agent must not be a symbolic link: ${t.name}`);const m=await w(i,u.skillDocumentBytes);if(m.truncated)throw n(`Plugin agent exceeds the supported size: ${t.name}`);const l=p(m.bytes.toString("utf8"),r);if(l.name!==e)throw n(`Plugin agent name must match its file name: ${t.name}`);a.push(l)}return a}}function n(o){return new d(h.PLUGIN_MANIFEST_INVALID,o,{category:"extension",userMessage:`Invalid plugin agent: ${o}`})}export{C as PluginAgentDiscovery};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
`)}}export{
|
|
1
|
+
import h from"node:fs/promises";import p from"node:path";import{SkillDiscovery as l}from"./skill-discovery.js";import{atomicWriteFile as w,withFileLock as d}from"../filesystem/atomic-file-store.js";import{isFileNotFound as S}from"../filesystem/fs-errors.js";import{readJsonFileBounded as y}from"../filesystem/bounded-json-reader.js";import{RESOURCE_LIMITS as g}from"../../domain/runtime/resource-limits.js";import{parseExtensionState as P}from"../../domain/extensions/extension-state.js";class x{statePath;constructor(e){this.statePath=e}async listScoped(e,a={}){const s=new Map,i=[],o=new Set,n=new Set;for(const t of e){s.has(t.statePath)||s.set(t.statePath,await this.readState(t.statePath));const m=s.get(t.statePath);for(const r of await new l().discover([t.root])){const c=t.scope==="plugin",f=o.has(r.name)||!c&&t.scope==="builtin"&&n.has(r.name);if(a.includeShadowed!==!0&&f)continue;c?n.add(r.name):t.scope!=="builtin"&&o.add(r.name);const u=c?`${t.pluginName??"plugin"}:${r.name}`:r.name;i.push({...r,skillKey:u,enabled:m[u]?.enabled!==!1,scope:t.scope,root:t.root,statePath:t.statePath,...t.pluginName===void 0?{}:{pluginName:t.pluginName}})}}return i}async setEnabled(e,a,s=this.statePath){await d(s,async()=>{const i=await this.readState(s);i[e]={enabled:a},await this.writeState(i,s)})}async remove(e,a,s=this.statePath){const i=(await new l().discover([a])).find(n=>n.name===e||`${n.name}`===e);if(i===void 0)return!1;const o=`${p.join(a,i.name)}.install`;return d(o,async()=>{const n=(await new l().discover([a])).find(t=>t.name===e||`${t.name}`===e);return n===void 0?!1:(await h.rm(n.directory,{recursive:!0,force:!0}),await d(s,async()=>{const t=await this.readState(s);delete t[e],await this.writeState(t,s)}),!0)})}async readState(e=this.statePath){try{return P(await y(e,g.workspaceSettingsBytes))}catch(a){if(S(a))return{};throw a}}async writeState(e,a=this.statePath){await w(a,`${JSON.stringify(e,null,2)}
|
|
2
|
+
`)}}export{x as SkillManager};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import m from"node:crypto";import a from"node:fs/promises";import p from"node:path";import{CliError as M}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as S}from"../../domain/errors/error-codes.js";import{isAlreadyExists as T,isFileNotFound as u}from"./fs-errors.js";import{currentProcessStartTimeMs as E,readProcessStartTimeMs as h}from"../platform/process-identity.js";import{DEFAULT_LOCK_STALE_MS as A,DEFAULT_LOCK_TIMEOUT_MS as _,LOCK_RETRY_MS as C,PROCESS_START_TIME_CACHE_MS as L,PROCESS_START_TIME_TOLERANCE_MS as g}from"./filesystem-constants.js";async function J(e,r,t=384){await a.mkdir(p.dirname(e),{recursive:!0});const i=`${e}.tmp-${process.pid}-${m.randomUUID()}`;try{await a.writeFile(i,r,{mode:t}),await a.rename(i,e)}finally{await a.rm(i,{force:!0})}}async function W(e,r){const t=`${r}.backup-${process.pid}-${m.randomUUID()}`;let i=!1,
|
|
1
|
+
import m from"node:crypto";import a from"node:fs/promises";import p from"node:path";import{CliError as M}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as S}from"../../domain/errors/error-codes.js";import{isAlreadyExists as T,isFileNotFound as u}from"./fs-errors.js";import{currentProcessStartTimeMs as E,readProcessStartTimeMs as h}from"../platform/process-identity.js";import{DEFAULT_LOCK_STALE_MS as A,DEFAULT_LOCK_TIMEOUT_MS as _,LOCK_RETRY_MS as C,PROCESS_START_TIME_CACHE_MS as L,PROCESS_START_TIME_TOLERANCE_MS as g}from"./filesystem-constants.js";async function J(e,r,t=384){await a.mkdir(p.dirname(e),{recursive:!0});const i=`${e}.tmp-${process.pid}-${m.randomUUID()}`;try{await a.writeFile(i,r,{mode:t}),await a.rename(i,e)}finally{await a.rm(i,{force:!0})}}async function W(e,r){const t=`${r}.backup-${process.pid}-${m.randomUUID()}`;let i=!1,n=!1;try{try{await a.rename(r,t),i=!0}catch(o){if(!u(o))throw o}try{await a.rename(e,r)}catch(o){if(i)try{await a.rename(t,r)}catch(s){throw n=!0,new AggregateError([o,s],`Installation and recovery failed. Previous contents are preserved at ${t}.`)}throw o}}finally{n||await a.rm(t,{recursive:!0,force:!0})}}async function k(e){const r=await a.open(e,"wx",384);try{return await r.writeFile(JSON.stringify({pid:process.pid,processStartTimeMs:E(),createdAt:new Date().toISOString()})),r}catch(t){throw await w(r,e),t}}async function v(e,r){if(T(e))return!0;if(!l(e))return!1;try{return await a.stat(r),!0}catch(t){return u(t)||l(t)}}async function w(e,r){try{await e.close()}finally{await a.rm(r,{force:!0})}}async function B(e,r,t={}){const i=await O(e,t);try{return await r()}finally{await i()}}async function O(e,r={}){const t=`${e}.lock`,i=r.timeoutMs??_,n=r.staleMs??A;await a.mkdir(p.dirname(e),{recursive:!0});const o=Date.now()+i,s=new Map;let c;for(;c===void 0;)try{c=await k(t)}catch(d){if(!await v(d,t))throw d;if(await F(t,n,s))continue;if(Date.now()>=o)throw new M(S.CONFIG_LOCKED,`Configuration file is locked: ${e}`,{category:"config",userMessage:r.userMessage??"A configuration update is already in progress. Try again shortly.",retryable:!0});await new Promise(y=>setTimeout(y,C))}let f=!1;return async()=>{f||(f=!0,await w(c,t))}}function l(e){return typeof e=="object"&&e!==null&&"code"in e&&(e.code==="EPERM"||e.code==="EACCES")}async function F(e,r,t=new Map){let i;try{i=await a.stat(e)}catch(o){if(u(o))return!1;throw o}let n={};try{n=R(JSON.parse(await a.readFile(e,"utf8")))}catch{}return n.pid!==void 0&&!await b(n,t)?(await a.rm(e,{force:!0}),!0):Date.now()-i.mtimeMs<r||n.pid!==void 0?!1:(await a.rm(e,{force:!0}),!0)}function R(e){if(typeof e!="object"||e===null)return{};const r=e;return{...typeof r.pid=="number"&&Number.isSafeInteger(r.pid)&&r.pid>0?{pid:r.pid}:{},...typeof r.processStartTimeMs=="number"&&Number.isFinite(r.processStartTimeMs)?{processStartTimeMs:r.processStartTimeMs}:{}}}async function b(e,r){const t=e.pid;if(t===void 0||!x(t))return!1;if(e.processStartTimeMs===void 0)return!0;const i=await D(t,r);return i===void 0?!0:Math.abs(i-e.processStartTimeMs)<=g}async function D(e,r){const t=Date.now(),i=r.get(e);if(i!==void 0&&t-i.checkedAt<L)return i.startTimeMs;const n=await h(e);return r.set(e,{checkedAt:t,...n===void 0?{}:{startTimeMs:n}}),n}function x(e){if(e===process.pid)return!0;try{return process.kill(e,0),!0}catch(r){return r instanceof Error&&"code"in r&&r.code==="EPERM"}}export{k as acquireAdvisoryFileLock,O as acquireFileLock,J as atomicWriteFile,v as isAdvisoryLockContention,F as recoverStaleAdvisoryLock,w as releaseAdvisoryFileLock,W as replaceDirectoryWithRollback,B as withFileLock};
|
|
@@ -5,8 +5,11 @@ import type { PlatformPaths } from '../platform/platform-paths.js';
|
|
|
5
5
|
export declare class FileSessionStore implements SessionStore {
|
|
6
6
|
private readonly paths;
|
|
7
7
|
private readonly journalHealth;
|
|
8
|
+
private readonly journalSnapshotCache;
|
|
9
|
+
private journalSnapshotCacheBytes;
|
|
8
10
|
constructor(paths: PlatformPaths);
|
|
9
11
|
create(input: Omit<SessionMetadata, 'id' | 'createdAt' | 'updatedAt'>): Promise<SessionMetadata>;
|
|
12
|
+
private createUnpublished;
|
|
10
13
|
get(sessionId: string): Promise<SessionMetadata | undefined>;
|
|
11
14
|
list(projectFingerprint?: string): Promise<readonly SessionMetadata[]>;
|
|
12
15
|
updateStatus(sessionId: string, status: SessionMetadata['status']): Promise<SessionMetadata>;
|
|
@@ -33,6 +36,7 @@ export declare class FileSessionStore implements SessionStore {
|
|
|
33
36
|
recoverStaleLock(sessionId: string): Promise<boolean>;
|
|
34
37
|
repair(sessionId: string): Promise<SessionRepairResult>;
|
|
35
38
|
withLock<T>(sessionId: string, action: () => Promise<T>): Promise<T>;
|
|
39
|
+
private withSessionPathsLock;
|
|
36
40
|
private require;
|
|
37
41
|
private requirePaths;
|
|
38
42
|
private locate;
|
|
@@ -44,7 +48,11 @@ export declare class FileSessionStore implements SessionStore {
|
|
|
44
48
|
private rebuildProjectIndex;
|
|
45
49
|
private readSnapshot;
|
|
46
50
|
private appendJournalLocked;
|
|
51
|
+
private removeUnpublishedSession;
|
|
47
52
|
private readSessionJournal;
|
|
53
|
+
private readJournalVersion;
|
|
54
|
+
private cacheJournalSnapshot;
|
|
55
|
+
private forgetJournalSnapshot;
|
|
48
56
|
private readAttachmentMetadata;
|
|
49
57
|
private corrupt;
|
|
50
58
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
${
|
|
3
|
-
`),
|
|
4
|
-
`);for(const
|
|
1
|
+
import R from"node:crypto";import h from"node:fs/promises";import d from"node:path";import{CliError as u}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as l}from"../../domain/errors/error-codes.js";import{isRecord as T,isSessionEvent as I,isSessionMetadata as q,isTranscriptEntry as H}from"../../domain/session/session-schema.js";import{isGoalEventType as $,isPersistedGoalEventData as V}from"../../domain/orchestration/goal.js";import{isAlreadyExists as U,isFileNotFound as y}from"../filesystem/fs-errors.js";import{acquireAdvisoryFileLock as G,atomicWriteFile as M,isAdvisoryLockContention as K,recoverStaleAdvisoryLock as J,releaseAdvisoryFileLock as W}from"../filesystem/atomic-file-store.js";import{isSafeProjectKey as L,resolveProjectSessionPaths as Z,resolveProjectSessionPathsForKey as v}from"./project-session-paths.js";import{appendJournalRecord as S,appendJournalRecords as Q,eventRecord as b,journalEvents as F,journalTranscript as E,latestMetadata as O,metadataRecord as k,readJournal as B,repairJournal as X,transcriptRecord as C}from"./session-journal.js";import{indexProjectSession as g,readProjectSessionIndex as Y,readSessionLocator as tt,removeIndexedSession as et}from"./session-index.js";import{RESOURCE_LIMITS as m}from"../../domain/runtime/resource-limits.js";import{readJsonFileBounded as at}from"../filesystem/bounded-json-reader.js";import{SessionJournalHealth as nt}from"./session-journal-health.js";import{ATTACHMENT_ID_PATTERN as j,SESSION_ID_PATTERN as D,SESSION_LOCK_STALE_MS as N}from"./session-constants.js";class xt{paths;journalHealth=new nt;journalSnapshotCache=new Map;journalSnapshotCacheBytes=0;constructor(t){this.paths=t}async create(t){const e=await this.createUnpublished(t);return await g(this.paths,e.metadata,e.sessionPaths.projectKey),e.metadata}async createUnpublished(t){const e=new Date().toISOString(),a={...t,id:R.randomUUID(),createdAt:e,updatedAt:e},n=Z(this.paths,a.cwd,a.id);return await h.mkdir(n.attachmentsDirectory,{recursive:!0}),await S(n.journalFile,k(a)),await this.journalHealth.markValid(n.journalFile),{metadata:a,sessionPaths:n}}async get(t){const e=await this.locate(t);if(e===void 0)return;const a=(await this.readSessionJournal(t,e)).metadata;if(a===void 0)throw this.corrupt(t,new Error("Session metadata is missing."));return a}async list(t){const e=[];for(const a of await this.projectKeys()){const n=v(this.paths,a,"00000000-0000-4000-8000-000000000000"),r=await this.readOrRebuildIndex(n.projectIndexFile,a);for(const s of Object.values(r))(t===void 0||s.projectFingerprint===t)&&e.push(s)}return e.sort((a,n)=>n.updatedAt.localeCompare(a.updatedAt))}async updateStatus(t,e){return this.withLock(t,async()=>{const n={...await this.require(t),status:e,updatedAt:new Date().toISOString()},r=await this.requirePaths(t);return await S(r.journalFile,k(n)),await this.journalHealth.markValid(r.journalFile),await g(this.paths,n,r.projectKey),n})}async rename(t,e){const a=e.trim().replace(/\s+/gu," ");if(a.length===0||a.length>120||/[\r\n]/u.test(a)||a.includes("\0"))throw new u(l.INVALID_ARGUMENT,"Session name is invalid.",{category:"session",userMessage:"Session name must be between 1 and 120 printable characters."});return this.withLock(t,async()=>{const r={...await this.require(t),name:a,updatedAt:new Date().toISOString()},s=await this.requirePaths(t);return await S(s.journalFile,k(r)),await this.journalHealth.markValid(s.journalFile),await g(this.paths,r,s.projectKey),r})}async appendEvent(t,e={}){const a=[{kind:"event",event:t}];e.locked===!0?await this.appendJournalLocked(t.sessionId,a):await this.appendJournal(t.sessionId,a)}async appendTranscript(t,e){await this.appendJournal(t,[{kind:"transcript",entry:e}])}async appendJournal(t,e){e.length!==0&&await this.withLock(t,()=>this.appendJournalLocked(t,e))}async readEvents(t){const e=await this.readSessionJournal(t);if(e.metadata===void 0)throw this.corrupt(t,new Error("Session metadata is missing."));return F(e.records)}async readTranscript(t){const e=await this.readSessionJournal(t);if(e.metadata===void 0)throw this.corrupt(t,new Error("Session metadata is missing."));return E(e.records)}async readHistory(t){return this.withLock(t,async()=>{const e=await this.readSessionJournal(t);if(e.metadata===void 0)throw this.corrupt(t,new Error("Session metadata is missing."));const a=e.records;return{events:F(a),transcript:E(a)}})}async delete(t){const e=await this.get(t);return e===void 0?!1:(await this.withLock(t,async()=>{const a=await this.requirePaths(t);await h.rm(a.journalFile,{force:!0}),await h.rm(a.sessionDirectory,{recursive:!0,force:!0}),this.forgetJournalSnapshot(a.journalFile),this.journalHealth.forget(a.journalFile),await et(this.paths,e,a.projectKey)}),!0)}async fork(t,e={}){const a=await this.readSnapshot(t),n=a.metadata,r=await this.create({projectFingerprint:n.projectFingerprint,cwd:n.cwd,model:e.model??n.model,profile:e.profile??n.profile,status:"active"});try{return await this.withLock(r.id,async()=>{const s=await this.requirePaths(r.id);for(const o of a.events){const c=rt(o.data);await S(s.journalFile,b({...o,sessionId:r.id,...c===void 0?{}:{data:c}}))}for(const o of a.transcript)await S(s.journalFile,C(o));await this.journalHealth.markValid(s.journalFile);for(const[o,c]of a.attachments)c.attachment!==void 0&&await this.persistAttachment(r.id,{...c.attachment,id:o},c.bytes)}),r}catch(s){try{await this.delete(r.id)}catch(o){throw new AggregateError([s,o],"Session fork failed and cleanup was incomplete.")}throw s}}async compact(t,e,a={}){const n=e.trim();if(n.length===0)throw new u(l.SESSION_CORRUPTED,"Compaction summary cannot be empty.",{category:"session",userMessage:"Compaction summary cannot be empty."});const r=new Date().toISOString();await this.appendJournal(t,[{kind:"transcript",entry:{type:"system",timestamp:r,content:`[Compacted context]
|
|
2
|
+
${n}`,...a.turnId===void 0?{}:{turnId:a.turnId}}},{kind:"event",event:{type:"context.compacted",timestamp:r,sessionId:t,...a.turnId===void 0?{}:{turnId:a.turnId},data:{summary:n}}}])}async export(t){const e=await this.readSnapshot(t),a=[];for(const n of e.transcript)for(const r of n.attachmentIds??[]){if(a.some(c=>c.attachment.id===r))continue;const s=e.attachments.get(r);if(s===void 0)continue;const o=s.attachment??{id:r,name:r,mimeType:"application/octet-stream",sizeBytes:s.bytes.byteLength,source:"file"};a.push({attachment:o,dataBase64:Buffer.from(s.bytes).toString("base64")})}return{metadata:e.metadata,events:e.events,transcript:e.transcript,attachments:a}}async import(t,e,a){ct(t);const n=await this.createUnpublished({projectFingerprint:e,cwd:a,model:t.metadata.model,profile:t.metadata.profile,status:"active"}),{metadata:r,sessionPaths:s}=n;try{return await this.withSessionPathsLock(r.id,s,async()=>{const o=[...t.events.map(c=>{const p=st(c.data);return{kind:"event",event:{...c,sessionId:r.id,...p===void 0?{}:{data:p}}}}),...t.transcript.map(c=>({kind:"transcript",entry:c}))];await this.appendJournalLocked(r.id,o,s);for(const c of t.attachments)await this.persistAttachment(r.id,c.attachment,Buffer.from(c.dataBase64,"base64"),{sessionPaths:s,requirePublished:!1})}),await g(this.paths,r,s.projectKey),r}catch(o){try{await this.removeUnpublishedSession(s)}catch(c){throw new AggregateError([o,c],"Session import failed and cleanup was incomplete.")}throw o}}async saveAttachment(t,e,a){if(!j.test(e.id))throw new u(l.ATTACHMENT_INVALID_IMAGE,"Attachment id is invalid.",{category:"attachment",userMessage:"Attachment name is invalid."});if(e.sizeBytes<0||e.sizeBytes>m.maxAttachmentBytes)throw new u(l.ATTACHMENT_TOO_LARGE,"Attachment exceeds the allowed size.",{category:"attachment",userMessage:"Attachment exceeds the allowed size."});if(a.byteLength!==e.sizeBytes)throw new u(l.ATTACHMENT_INVALID_IMAGE,"Attachment size does not match metadata.",{category:"attachment",userMessage:"Attachment size does not match its metadata."});return this.withLock(t,()=>this.persistAttachment(t,e,a))}async persistAttachment(t,e,a,n={}){const r=n.sessionPaths??await this.requirePaths(t);n.requirePublished!==!1&&await this.require(t),await h.mkdir(r.attachmentsDirectory,{recursive:!0});const s=await this.readAttachmentMetadata(r,e.id),o=s?.storagePath===void 0?await ot(r.attachmentsDirectory,e.storageName,e.id):d.basename(s.storagePath),c=d.join(r.attachmentsDirectory,o);await h.writeFile(c,a,{flag:"wx",mode:384}).catch(async w=>{if(!U(w))throw w;await h.writeFile(c,a,{mode:384})});const p={...e,storagePath:d.posix.join("attachments",o)};return await M(d.join(r.attachmentsDirectory,`${e.id}.json`),`${JSON.stringify(p,null,2)}
|
|
3
|
+
`),p}async readAttachment(t,e){const a=await this.requirePaths(t);if(await this.require(t),!j.test(e))return;const n=await this.readAttachmentMetadata(a,e),r=d.join(a.attachmentsDirectory,z(n,e));try{if((await h.stat(r)).size>m.maxAttachmentBytes)throw new u(l.ATTACHMENT_TOO_LARGE,"Stored attachment exceeds the allowed size.",{category:"attachment",userMessage:"Stored attachment exceeds the allowed size."});return await h.readFile(r)}catch(s){if(y(s))return;throw s}}async deleteAttachment(t,e){return j.test(e)?this.withLock(t,async()=>{const a=await this.requirePaths(t);await this.require(t);const n=await this.readAttachmentMetadata(a,e),r=d.join(a.attachmentsDirectory,z(n,e)),s=d.join(a.attachmentsDirectory,`${e}.json`);let o=!1;for(const c of[r,s])try{await h.unlink(c),o=!0}catch(p){if(!y(p))throw p}return o}):!1}async recoverStaleLock(t){const e=await this.locate(t);return e===void 0?!1:J(e.lockFile,N)}async repair(t){const e=await this.requirePaths(t),a=await this.withLock(t,()=>X(e.journalFile));return this.forgetJournalSnapshot(e.journalFile),await this.journalHealth.markValid(e.journalFile),{eventsRemoved:a,transcriptRemoved:0}}async withLock(t,e){const a=await this.requirePaths(t);return this.withSessionPathsLock(t,a,e)}async withSessionPathsLock(t,e,a){await h.mkdir(e.sessionDirectory,{recursive:!0});let n;try{n=await G(e.lockFile)}catch(r){const s=await K(r,e.lockFile);if(s&&await J(e.lockFile,N))return this.withSessionPathsLock(t,e,a);throw s?new u(l.SESSION_LOCKED,`Session is locked: ${t}`,{category:"session",userMessage:"The session is being used by another process. Try again later.",retryable:!0}):r}try{return await a()}finally{await W(n,e.lockFile)}}async require(t){const e=await this.get(t);if(e===void 0)throw new u(l.SESSION_NOT_FOUND,`Session not found: ${t}`,{category:"session",userMessage:`Session not found: ${t}.`});return e}async requirePaths(t){const e=await this.locate(t);if(e===void 0)throw new u(l.SESSION_NOT_FOUND,`Session not found: ${t}`,{category:"session",userMessage:`Session not found: ${t}.`});return e}async locate(t){if(!D.test(t))return;const a=(await tt(this.paths)).sessions[t];if(typeof a=="string"&&L(a)){const n=v(this.paths,a,t);if(await P(n.journalFile))return n}return this.locateByScan(t)}async locateByScan(t){for(const e of await this.projectKeys()){const a=v(this.paths,e,t);if(await P(a.journalFile))return a}}async projectKeys(){try{return(await h.readdir(this.paths.projectsDir,{withFileTypes:!0})).filter(t=>t.isDirectory()&&L(t.name)).map(t=>t.name)}catch(t){if(y(t))return[];throw t}}async readOrRebuildIndex(t,e){try{const a=(await Y(t,e)).sessions;return await this.indexMatchesJournals(e,a)?a:this.rebuildProjectIndex(e)}catch{return this.rebuildProjectIndex(e)}}async indexMatchesJournals(t,e){const a=await this.projectJournalIds(t),n=Object.keys(e);return a.length===n.length&&a.every(r=>e[r]!==void 0)}async projectJournalIds(t){const e=d.join(this.paths.projectsDir,t);try{return(await h.readdir(e,{withFileTypes:!0})).filter(a=>a.isFile()&&a.name.endsWith(".jsonl")).map(a=>a.name.slice(0,-6)).filter(a=>D.test(a))}catch(a){if(y(a))return[];throw a}}async rebuildProjectIndex(t){const e={},a=d.join(this.paths.projectsDir,t);for(const r of await h.readdir(a,{withFileTypes:!0}))if(!(!r.isFile()||!r.name.endsWith(".jsonl")))try{const s=O(await B(d.join(a,r.name)));s!==void 0&&(e[s.id]=s)}catch{}const n=d.join(a,"index.json");await h.mkdir(a,{recursive:!0}),await M(n,`${JSON.stringify({projectKey:t,sessions:e},null,2)}
|
|
4
|
+
`);for(const r of Object.values(e))await g(this.paths,r,t);return e}async readSnapshot(t){return this.withLock(t,async()=>{const e=await this.readSessionJournal(t),a=e.metadata;if(a===void 0)throw this.corrupt(t,new Error("Session metadata is missing."));const n=e.sessionPaths,r=e.records,s=F(r),o=E(r),c=new Map;for(const p of o)for(const w of p.attachmentIds??[]){if(c.has(w))continue;const x=await this.readAttachment(t,w);x!==void 0&&c.set(w,{bytes:x,attachment:await this.readAttachmentMetadata(n,w)})}return{metadata:a,events:s,transcript:o,attachments:c}})}async appendJournalLocked(t,e,a){const n=a??await this.requirePaths(t);try{if(!await this.journalHealth.ensureValid(n.journalFile))throw new Error("Session metadata is missing.")}catch(s){throw this.corrupt(t,s)}const r=e.map(s=>{if(s.kind==="event"&&s.event.sessionId!==t)throw new u(l.SESSION_CORRUPTED,"Cannot append an event to a different session.",{category:"session",userMessage:"Session event data is invalid."});return s.kind==="event"?b(s.event):C(s.entry)});await Q(n.journalFile,r),this.forgetJournalSnapshot(n.journalFile),await this.journalHealth.markValid(n.journalFile)}async removeUnpublishedSession(t){await h.rm(t.journalFile,{force:!0}),await h.rm(t.sessionDirectory,{recursive:!0,force:!0}),this.forgetJournalSnapshot(t.journalFile),this.journalHealth.forget(t.journalFile)}async readSessionJournal(t,e){const a=e??await this.requirePaths(t);try{const n=await this.readJournalVersion(a.journalFile),r=n===void 0?void 0:this.journalSnapshotCache.get(a.journalFile);if(n!==void 0&&r!==void 0&&it(r,n))return this.journalSnapshotCache.delete(a.journalFile),this.journalSnapshotCache.set(a.journalFile,r),await this.journalHealth.markValid(a.journalFile,r.metadata!==void 0),{sessionPaths:a,records:r.records,metadata:r.metadata};const s=await B(a.journalFile),o=O(s),c=await this.readJournalVersion(a.journalFile);return c!==void 0&&this.cacheJournalSnapshot(a.journalFile,c,s,o),await this.journalHealth.markValid(a.journalFile,o!==void 0),{sessionPaths:a,records:s,metadata:o}}catch(n){throw this.corrupt(t,n)}}async readJournalVersion(t){try{const e=await h.stat(t);return{size:e.size,mtimeMs:e.mtimeMs,ctimeMs:e.ctimeMs}}catch(e){if(y(e))return;throw e}}cacheJournalSnapshot(t,e,a,n){if(this.forgetJournalSnapshot(t),e.size>m.sessionJournalCacheMaxBytes)return;const r={...e,records:a,metadata:n};for(this.journalSnapshotCache.set(t,r),this.journalSnapshotCacheBytes+=e.size;this.journalSnapshotCache.size>m.sessionJournalCacheMaxEntries||this.journalSnapshotCacheBytes>m.sessionJournalCacheMaxBytes;){const s=this.journalSnapshotCache.keys().next().value;if(s===void 0)break;this.forgetJournalSnapshot(s)}}forgetJournalSnapshot(t){const e=this.journalSnapshotCache.get(t);e!==void 0&&(this.journalSnapshotCacheBytes-=e.size),this.journalSnapshotCache.delete(t)}async readAttachmentMetadata(t,e){try{return dt(await at(d.join(t.attachmentsDirectory,`${e}.json`),m.configFileBytes))}catch(a){if(y(a))return;throw a}}corrupt(t,e){return new u(l.SESSION_CORRUPTED,`Session metadata is corrupted: ${t}`,{category:"session",userMessage:"Session is corrupted and its metadata cannot be read.",cause:e})}}function rt(i){return _(i)}function st(i){return _(i)}function _(i){if(i===void 0||typeof i.goal!="object"||i.goal===null)return i;const t=i.goal;return t.status!=="active"?i:{...i,goal:{...t,status:"paused",updatedAt:new Date().toISOString()},reason:"Explicit resume is required after fork or import."}}async function P(i){try{return await h.access(i),!0}catch(t){if(y(t))return!1;throw t}}function z(i,t){const e=i?.storagePath;if(typeof e=="string"&&e.startsWith("attachments/")){const a=d.basename(e);if(a.length>0&&a!=="."&&a!=="..")return a}return`${t}.bin`}function it(i,t){return i.size===t.size&&i.mtimeMs===t.mtimeMs&&i.ctimeMs===t.ctimeMs}async function ot(i,t,e){if(t===void 0)return`${e}.bin`;const a=d.basename(t).replace(/[^A-Za-z0-9._-]/gu,"_").slice(0,120)||`${e}.bin`,n=d.extname(a),r=n.length===0?a:a.slice(0,-n.length);for(let s=0;s<1e4;s+=1){const o=s===0?a:`${r}-${s}${n}`;if(!await P(d.join(i,o)))return o}throw new u(l.ATTACHMENT_TOO_LARGE,"Too many attachments share the same filename.",{category:"attachment",userMessage:"Too many attachments share the same filename."})}function ct(i){if(!q(i.metadata)||!Array.isArray(i.events)||!Array.isArray(i.transcript)||!Array.isArray(i.attachments))throw f("Session export file is invalid.");if(i.events.length>m.sessionEvents||i.transcript.length>m.sessionTranscriptEntries||i.attachments.length>m.sessionAttachments)throw f("Session export exceeds the supported entry limits.");for(const a of i.events){if(!I(a))throw f("Session export contains an invalid event.");if($(a.type)&&!V(a.data))throw f("Session export contains an invalid goal event.");if(A(a.type)>256||A(a.timestamp)>128||A(a.sessionId)>128)throw f("Session event metadata is too large.")}const t=new Set;let e=0;for(const a of i.transcript){if(!H(a)||A(a.content)>m.sessionTextBytes)throw f("Session export contains an invalid transcript entry.");if(a.attachmentIds!==void 0&&(!Array.isArray(a.attachmentIds)||a.attachmentIds.some(n=>typeof n!="string"||!j.test(n))))throw f("Session transcript contains invalid attachment references.")}for(const a of i.attachments){if(!ht(a))throw f("Session export contains an invalid attachment.");const n=a.attachment;if(typeof n.id!="string"||!j.test(n.id)||t.has(n.id)||typeof n.name!="string"||typeof n.mimeType!="string"||typeof n.sizeBytes!="number"||!Number.isSafeInteger(n.sizeBytes)||n.sizeBytes<0||n.sizeBytes>m.maxAttachmentBytes||n.source!=="clipboard"&&n.source!=="file")throw f("Session export contains invalid attachment metadata.");const r=Buffer.from(a.dataBase64,"base64");if(r.toString("base64")!==a.dataBase64||r.byteLength!==n.sizeBytes)throw f("Session export contains an invalid attachment payload.");if(e+=r.byteLength,e>m.sessionAttachmentTotalBytes)throw f("Session export exceeds the total attachment limit.");t.add(n.id)}}function ht(i){if(!T(i)||!T(i.attachment)||typeof i.dataBase64!="string")return!1;const t=i.attachment;return typeof t.id=="string"&&typeof t.name=="string"&&typeof t.mimeType=="string"&&typeof t.sizeBytes=="number"&&Number.isSafeInteger(t.sizeBytes)&&(t.source==="clipboard"||t.source==="file")}function A(i){return Buffer.byteLength(i,"utf8")}function f(i){return new u(l.SESSION_CORRUPTED,i,{category:"session",userMessage:i})}function dt(i){if(typeof i!="object"||i===null)throw new Error("Invalid attachment metadata.");const t=i;if(typeof t.id!="string"||typeof t.name!="string"||typeof t.mimeType!="string"||typeof t.sizeBytes!="number"||t.source!=="clipboard"&&t.source!=="file")throw new Error("Invalid attachment metadata.");return i}export{xt as FileSessionStore};
|
package/dist/main.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{asCliError as S}from"./domain/errors/cli-error.js";import{createCompositionRoot as E}from"./bootstrap/composition-root.js";import{resolveStartupContext as
|
|
1
|
+
import{asCliError as S}from"./domain/errors/cli-error.js";import{createCompositionRoot as E}from"./bootstrap/composition-root.js";import{resolveStartupContext as h}from"./bootstrap/startup-sequence.js";import{parseGlobalCliOptions as O}from"./bootstrap/global-cli-options.js";import{TuiApplication as x}from"./presentation/tui/tui-application.js";import{formatCliErrorMessage as T}from"./application/commands/command-hints.js";import{COMMAND_REGISTRY as b}from"./application/commands/command-runtime.js";import{parseCommandInvocation as R}from"./application/commands/command-parser.js";import{executeApplicationCommand as v,executeInformationalCommand as G}from"./application/commands/application-command-executor.js";import{CLI_VERSION as B}from"./version.js";async function V(o,e={}){const y=e.stdin??process.stdin,n=e.stdout??process.stdout,u=e.stderr??process.stderr;let r;const i=new AbortController;let p=143;const f=()=>i.abort(new Error("Process termination requested."));let s;process.once("SIGTERM",f);try{const l=O(o),t=M(l.commandTokens);if(t.length===0){r=E({stderr:u,interactive:!0});const d=r.logger;d.info("Interactive session started.");const w=await new x(r,h(o).cwd,{output:n,signal:i.signal}).run();return d.info("Interactive session completed.",{exitCode:w}),w}const c=h(o).cwd,a=R(t,"direct",b),g=G(a,n);if(g!==void 0)return g;s=()=>{p=130,i.abort(new Error("Process interruption requested."))},process.once("SIGINT",s);const m=E({stderr:u});r=m;const C=m.logger;C.info("CLI command started.",{command:a.action.id,cwd:c});const I=await v(a,{runtime:m,cwd:c,output:n,stdin:y,writableOutput:n,signal:i.signal,resumeInteractive:async d=>new x(m,c,{output:n,sessionId:d,signal:i.signal}).run()});return C.info("CLI command completed.",{command:a.action.id,exitCode:I}),I}catch(l){if(i.signal.aborted)return p;const t=S(l);return r?.logger.error("CLI command failed.",{code:t.code,category:t.category}),N(o)?n.write(`${JSON.stringify({error:t.toJSON()})}
|
|
2
2
|
`):r!==void 0?r.logger.error(T(t,"direct"),{code:t.code,category:t.category}):u.write(`[error] ${T(t,"direct")}
|
|
3
|
-
`),t.exitCode}finally{process.off("SIGTERM",
|
|
3
|
+
`),t.exitCode}finally{process.off("SIGTERM",f),s!==void 0&&process.off("SIGINT",s),await r?.shutdown.shutdown()}}function N(o){return o.some(e=>e==="--json"||e.startsWith("--json="))}function M(o){return o.length===1&&(o[0]==="--help"||o[0]==="-h")?["help"]:o.length===1&&o[0]==="--version"?["version"]:o}export{B as CLI_VERSION,V as main};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AuthenticationService as
|
|
1
|
+
import{AuthenticationService as j}from"../../application/auth/authentication-service.js";import{loadExecutionConfig as H}from"../../application/config/execution-config.js";import{CliError as B,asCliError as R}from"../../domain/errors/cli-error.js";import{CLI_ERROR_CODES as P}from"../../domain/errors/error-codes.js";import{buildUserMessage as q}from"../../infrastructure/clipboard/image-normalizer.js";import{createLotaGateClient as G}from"../../infrastructure/sdk/lotagate-client-factory.js";import{estimateTurnUsage as $}from"../../infrastructure/tokens/tiktoken-token-estimator.js";import{AgentRunTracker as J}from"../../application/agent/agent-run-tracker.js";import{prepareCliAgent as V}from"../../application/agent/cli-agent-execution-service.js";import{BoundedTextAccumulator as Y}from"../../application/agent/bounded-text-accumulator.js";import{createTurnId as z,recordTurnCompletion as K,recordTurnFailure as Q,recordTurnStart as X}from"../../application/session/session-turn-lifecycle.js";import{createTuiContextManagement as Z,createTuiContinuationContext as ee}from"./tui-context-compaction.js";import{isAbortError as te,throwIfAborted as k}from"./tui-lifecycle-actions.js";import{runTuiAgentEvents as oe}from"./tui-agent-event-runner.js";import{AUTH_LOGIN_HINT as se,MODEL_SELECT_HINT as re,formatCliErrorMessage as M,formatCommandHint as I}from"../../application/commands/command-hints.js";class Ee{host;activeAbortController;activePromptRun;constructor(s){this.host=s}isRunning(){return this.activePromptRun!==void 0}getActiveAbortController(){return this.activeAbortController}setActiveAbortController(s){this.activeAbortController=s}abortActive(s){this.activeAbortController?.abort(s)}async runPrompt(s,n,g){return this.sendPrompt(s,this.host.pendingAttachments.list(),n,g)}async sendPrompt(s,n=[],g,d){if(this.activePromptRun!==void 0)return this.host.renderer.notice("A response is already running. Stop it before sending another prompt."),"";try{this.host.pendingAttachments.assertPromptFits(s,n)}catch(A){const w=R(A);return this.host.renderer.error(M(w,"tui")),""}const u=this.host.getSession(),h=u===void 0?void 0:await this.host.executionWorkspaces.acquireTurn(u.id),m=this.executePrompt(s,n,g,d);this.activePromptRun=m,this.host.renderer.setRunActive(!0);try{return await m}finally{this.activePromptRun===m&&(this.activePromptRun=void 0),this.host.renderer.setRunActive(!1),await h?.()}}async executePrompt(s,n,g,d){const u=await H(this.host.runtime,this.host.cwd),h=this.host.getActiveModel()??u.config.model;if(h===null)return this.host.renderer.error(`No model is selected. ${I(re,"tui")}`),"";const m=this.host.getActiveProfile()??u.config.profile??j.defaultProfileName(),A=await this.host.runtime.credentialStore.get(m);if(A===void 0)return this.host.renderer.error(`No API key is configured. ${I(se,"tui")}`),"";const w=await this.host.runtime.projectTrust.inspect(this.host.cwd),t=this.host.getSession();t!==void 0&&this.host.setExecutionWorkspace(await this.host.executionWorkspaces.prepare({sessionId:t.id,projectRoot:w.canonicalPath})),await this.host.runHookEvent("prompt.before",t?.id??"tui"),this.host.renderer.userPrompt(s,n.map(({attachment:e})=>e));const C=z();t!==void 0&&await X(this.host.runtime.sessionStore,t.id,C,s,n.map(({attachment:e})=>e.id));const x=Date.now();let c,v=!1;this.host.renderer.status("Thinking");const p=new AbortController;this.activeAbortController=p;const T=G(A.profile,A.apiKey),U=await this.host.runtime.projectTrust.isTrusted(w),o=t===void 0?void 0:await this.host.runtime.goalStore.get(t.id),i=new J,L={start:e=>this.host.renderer.startToolActivity(e.key,e.label)},N={onSubagentLifecycle:e=>{e.sessionId===t?.id&&this.host.renderer.subagentLifecycle(e)}},W=await this.host.contextService.getModelContextWindow(h,m),_=e=>{i.observeModel(e),e.usage!==void 0&&(c=e.usage,v=!0,d?.onUsage({usage:e.usage,modelCalls:1})),this.host.runtime.logger.debug("Model turn completed.",{model:e.model,stream:e.stream,contentLength:e.contentLength,toolCallCount:e.toolCallCount,toolNames:e.toolNames,finishReason:e.finishReason})},E=await V({runtime:this.host.runtime,projectRoot:w.canonicalPath,executionCwd:this.host.getExecutionWorkspace().executionCwd,executionWorkspaceId:this.host.getExecutionWorkspace().id,model:h,reasoningEffort:u.config.effort,client:T,profile:U?"interactive-trusted":"interactive-untrusted",approvalMode:u.config.approvalMode,subagentJobs:this.host.getSubagentJobs(),...g===void 0?{}:{initialSkill:g},onWrite:e=>this.host.renderer.fileChange(e),prompt:(e,a)=>this.host.approvals.ask(e,a),rememberApproval:e=>this.host.approvals.remember(w.canonicalPath,e),onApprovalPersistenceFailure:(e,a)=>this.host.approvals.rememberFailure(a),audit:e=>this.host.approvals.audit(e),onModelComplete:_,onSubagentModelComplete:e=>{e.usage!==void 0&&d?.onUsage({usage:e.usage,modelCalls:1})},contextManagement:Z(T,h,W,e=>this.host.runtime.logger.warn("Context summarization failed; using fallback summary.",{reason:e instanceof Error?e.message:String(e)}),()=>ee(i.snapshot(),o)),agentSessionStore:this.host.agentSessionStore,toolActivity:L,onIntentLifecycle:e=>{e.type==="intent.compiled"?this.host.renderer.status("Understanding the task"):e.type==="intent.verification.required"?this.host.renderer.status("Verifying the result"):e.type==="intent.blocked"&&this.host.renderer.status("Verification is incomplete")},subagentLifecycle:N,...o===void 0?{}:{goal:{objective:o.objective,status:o.status,turns:o.turns,evaluatorRuns:o.evaluatorRuns,tokensUsed:o.usage.totalTokens,...o.tokenBudget===void 0?{}:{tokenBudget:o.tokenBudget},...o.maxTurns===void 0?{}:{maxTurns:o.maxTurns},...o.maxDurationMs===void 0?{}:{maxDurationMs:o.maxDurationMs},...o.lastError===void 0?{}:{lastError:o.lastError},...o.lastEvaluation===void 0?{}:{lastEvaluation:o.lastEvaluation.reason}}}}),f=new Y;let S=!1;const D=e=>{k(p.signal),f.append(e),this.host.renderer.assistantDelta(e)},F=e=>{k(p.signal),f.replace(e),this.host.renderer.replaceAssistant(e)};try{k(p.signal);const e=await E.buildInput(s);this.host.pendingAttachments.assertPromptFits(e.input,n);const a=n.length===0?void 0:q(e.input,n),r=a?.content===null?void 0:a?.content;if(await oe({prepared:E,input:e.input,userInput:e.userInput,...e.workspaceContext===void 0?{}:{workspaceContext:e.workspaceContext},...r===void 0?{}:{inputContent:r},model:h,...t===void 0?{}:{sessionId:t.id},turnId:C,signal:p.signal,agentSessionStore:this.host.agentSessionStore,tracker:i,runtime:this.host.runtime,renderer:this.host.renderer,startedAt:x,getUsage:()=>c,appendAssistantDelta:D,replaceAssistantResponse:F,hasResponse:()=>!f.isEmpty}),f.toString().trim().length===0){const l=i.snapshot();l.toolCalls>0&&i.markStuck();const O=l.toolCalls>0?P.MODEL_EMPTY_FINAL:P.MODEL_PROTOCOL_EMPTY_RESPONSE,y=l.toolCalls>0?"The model completed its tool work without a final text response.":"The model returned neither text nor a tool call.";throw new B(O,y,{category:"model",userMessage:`${y} Check the selected model and gateway tool-calling support, then try again.`,retryable:!0,details:{modelTurns:l.modelTurns,toolCalls:l.toolCalls,toolCompletions:l.toolCompletions,toolFailures:l.toolFailures,toolNames:l.toolNames,finishReason:l.lastFinishReason}})}t!==void 0&&(await this.host.executionWorkspaces.apply(t.id),S=!0),d?.onSnapshot(i.snapshot())}catch(e){const a=p.signal.aborted||te(e);if(t!==void 0&&!S)try{const r=await this.host.executionWorkspaces.apply(t.id);S=!0,r.appliedPaths.length>0&&this.host.runtime.logger.info("Applied session workspace changes after turn failure.",{sessionId:t.id,turnId:C,appliedPaths:r.appliedPaths})}catch(r){this.host.runtime.logger.error("Failed to apply session workspace changes after turn failure.",{sessionId:t.id,turnId:C,reason:r instanceof Error?r.message:String(r)})}if(t!==void 0&&this.host.agentSessionStore.delete(t.id),a?i.markCancelled():i.snapshot().state!=="stuck"&&i.markFailed(),t!==void 0&&await Q(this.host.runtime.sessionStore,t.id,C,e,a),this.host.renderer.finishAssistant(),a)this.host.renderer.notice("Interrupted.");else{const r=R(e);this.host.renderer.failed(),this.host.runtime.logger.error("Agent run failed.",{code:r.code,category:r.category,...r.details??{}}),this.host.renderer.error(M(r,"tui"))}return d?.onError(e),d?.onSnapshot(i.snapshot()),""}finally{this.activeAbortController===p&&(this.activeAbortController=void 0);try{await E.close()}catch(e){this.host.runtime.logger.warn("Agent cleanup failed after a completed turn.",{reason:e instanceof Error?e.message:String(e)})}if(t!==void 0)try{await this.host.executionWorkspaces.markReady(t.id)}catch(e){this.host.runtime.logger.warn("Unable to release TUI execution workspace after turn.",{sessionId:t.id,reason:e instanceof Error?e.message:String(e)})}}this.host.renderer.finishAssistant();try{t!==void 0&&await K(this.host.runtime.sessionStore,t.id,C,f.toString())}catch(e){this.host.runtime.logger.warn("Unable to persist the completed turn.",{reason:e instanceof Error?e.message:String(e)}),this.host.renderer.notice("Response completed, but the session state could not be saved.")}try{await this.host.runHookEvent("response.after",t?.id??"tui")}catch(e){this.host.runtime.logger.warn("Response hook failed after a completed turn.",{reason:e instanceof Error?e.message:String(e)}),this.host.renderer.notice("Response completed, but the response hook failed.")}this.host.pendingAttachments.clear(),this.host.renderer.setPendingAttachments([]),!v&&c!==void 0&&d?.onUsage({usage:c,modelCalls:1});const b=c?.totalTokens===void 0?$({model:h,prompt:s,response:f.toString()}):c;return!v&&c===void 0&&b!==void 0&&d?.onUsage({usage:b,modelCalls:1}),this.host.renderer.complete(Date.now()-x,b),await this.host.refreshContextUsage(),f.toString()}}export{Ee as TuiAgentTurnController};
|
|
@@ -30,6 +30,7 @@ export declare class TuiApplication {
|
|
|
30
30
|
private readonly approvals;
|
|
31
31
|
private readonly contextService;
|
|
32
32
|
private readonly agentSessionStore;
|
|
33
|
+
private readonly backgroundOperations;
|
|
33
34
|
private subagentJobs;
|
|
34
35
|
private readonly executionWorkspaces;
|
|
35
36
|
private executionWorkspace;
|
|
@@ -56,6 +57,7 @@ export declare class TuiApplication {
|
|
|
56
57
|
compactSession(): Promise<void>;
|
|
57
58
|
resumeSession(sessionId: string): Promise<void>;
|
|
58
59
|
private handleInterrupt;
|
|
60
|
+
private startBackgroundMcpWarmup;
|
|
59
61
|
private awaitActiveForegroundRun;
|
|
60
62
|
private runHookEvent;
|
|
61
63
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{createElement as
|
|
2
|
-
${
|
|
3
|
-
`)}}async processPrompt(i){if(this.activeForegroundRun!==void 0)return this.renderer.notice("An operation is already running. Stop it before starting another one."),!1;const
|
|
1
|
+
import{createElement as f}from"react";import{render as w}from"ink";import{detectTerminalCapabilities as v}from"./terminal-capabilities.js";import{enterInteractiveScreen as S,exitInteractiveScreen as b}from"./terminal-screen.js";import{createImageAttachment as x,normalizeImage as C}from"../../infrastructure/clipboard/image-normalizer.js";import{PendingAttachmentStore as A}from"../../application/chat/pending-attachment-store.js";import{CLI_VERSION as u}from"../../version.js";import{InkTuiBridge as k}from"./ink/tui-bridge.js";import{LotaGateTui as P}from"./ink/lotagate-tui.js";import{WorkspaceFileIndex as y}from"../../application/workspace/workspace-file-index.js";import{ProjectSessionAccess as T}from"../../application/session/project-session-access.js";import{createTuiCommandHost as I}from"./tui-command-host-factory.js";import{workspaceFileSuggestions as E}from"./tui-file-suggestions.js";import{warmMcpRuntime as R}from"../../application/agent/cli-agent-execution-service.js";import{TuiToolApprovalCoordinator as M}from"./tui-tool-approval.js";import{TuiContextService as W}from"./tui-context-service.js";import{createTuiClipboardPasteHandlers as U}from"./tui-clipboard-paste-handlers.js";import{dispatchTuiPrompt as j}from"./tui-prompt-dispatcher.js";import{CliAgentSessionStore as H,retainLatestCompaction as m}from"../../application/agent/agent-session-store.js";import{compactManualTuiSession as F}from"./tui-manual-compaction.js";import{resolveInitialModel as p}from"./tui-initial-model.js";import{cancelTuiResponse as O,handleTuiInterrupt as G,isAbortError as L,runTuiHookEvent as N}from"./tui-lifecycle-actions.js";import{loadExecutionConfig as B}from"../../application/config/execution-config.js";import{resumeTuiGoal as D,runTuiGoal as $}from"./tui-goal-controller.js";import{runTuiSkill as J}from"./tui-skill-controller.js";import{SubagentJobRegistry as g}from"../../application/orchestration/cli-subagent-service.js";import{formatResumeCommand as z}from"./tui-application-contract.js";import{SessionExecutionWorkspaceService as q}from"../../application/workspace/session-execution-workspace.js";import{loadTuiExtensions as V}from"./tui-extension-loader.js";import{TuiAgentTurnController as _}from"./tui-agent-turn-controller.js";import{OperationRegistry as K}from"../../application/lifecycle/operation-registry.js";class It{runtime;cwd;options;input;output;renderer;pendingAttachments=new A;session;hookRuntime;hooks=[];skillRootScopes=[];enabledPluginNames=[];pluginMcpConfig={};activeForegroundRun;exitArmedUntil=0;lastInterruptAt=0;printResumeCommandOnExit=!1;activeModel=null;activeProfile=null;approvals;contextService;agentSessionStore=new H;backgroundOperations=new K;subagentJobs=new g;executionWorkspaces;executionWorkspace;turnController;constructor(i,t,e={}){this.runtime=i,this.cwd=t,this.options=e,this.input=e.input??process.stdin,this.output=e.output??process.stdout,this.renderer=new k,this.executionWorkspaces=this.runtime.executionWorkspaces??new q(this.runtime.paths.globalConfigDir,this.runtime.platform),this.contextService=new W(this.runtime,this.cwd),this.approvals=new M({renderer:this.renderer,runtime:this.runtime,getSessionId:()=>this.session?.id,getActiveSignal:()=>this.turnController.getActiveAbortController()?.signal}),this.turnController=new _({runtime:this.runtime,cwd:this.cwd,renderer:this.renderer,pendingAttachments:this.pendingAttachments,approvals:this.approvals,contextService:this.contextService,agentSessionStore:this.agentSessionStore,executionWorkspaces:this.executionWorkspaces,getSession:()=>this.session,getActiveModel:()=>this.activeModel,getActiveProfile:()=>this.activeProfile,getExecutionWorkspace:()=>this.executionWorkspace,setExecutionWorkspace:r=>{this.executionWorkspace=r},getSubagentJobs:()=>this.subagentJobs,runHookEvent:(r,s)=>this.runHookEvent(r,s),refreshContextUsage:()=>this.refreshContextUsage()}),this.renderer.setInterruptHandler(()=>this.handleInterrupt()),this.renderer.setCancelHandler(()=>O(this.turnController.getActiveAbortController(),this.renderer));const n=U({platform:this.runtime.platform,notify:r=>this.renderer.notice(r),attachImage:(r,s)=>this.attachClipboardImage(r,!1,s)});this.renderer.setImagePasteHandler(n.image),this.renderer.setTextPasteHandler(n.text),this.renderer.setClearAttachmentsHandler(()=>{this.pendingAttachments.clear(),this.renderer.setPendingAttachments([])})}async run(){if(!v(this.input,this.output).isInteractive)return this.output.write('Interactive TUI requires a terminal. Use `lotagate chat "prompt"` for non-interactive streaming.\n'),2;S(this.output);const t=w(f(P,{bridge:this.renderer}),{stdin:this.input,stdout:this.output,exitOnCtrlC:!1}),e=()=>this.handleInterrupt(),n=()=>{const s=new Error("Interactive session is shutting down.");this.backgroundOperations.abort(s),this.turnController.abortActive(s),this.renderer.requestExit(143)};process.on("SIGINT",e),this.options.signal?.addEventListener("abort",n,{once:!0});let r="completed";try{const s=await this.runtime.projectTrust.inspect(this.cwd);let o=await this.runtime.projectTrust.isTrusted(s),h=await this.runtime.configLoader.load(this.cwd,{includeProject:o}),l=await p(this.runtime,h.config),c={...h,config:{...h.config,model:l}};if(this.activeModel=c.config.model,this.activeProfile=c.config.profile,this.renderer.setWelcome({version:u,cwd:s.canonicalPath,model:c.config.model,profile:c.config.profile,trusted:o}),this.renderer.setFileSuggestions([]),o||(o=await this.renderer.confirmTrust(),o?(await this.runtime.projectTrust.grant(s),h=await this.runtime.configLoader.load(this.cwd),l=await p(this.runtime,h.config),c={...h,config:{...h.config,model:l}},this.activeModel=c.config.model,this.activeProfile=c.config.profile,this.renderer.setWelcome({version:u,cwd:s.canonicalPath,model:this.activeModel,profile:this.activeProfile,trusted:!0}),this.renderer.notice("Project trusted.")):this.renderer.notice("Project remains untrusted; local tools and extensions stay disabled.")),o&&await this.runtime.workspaceSettings.ensureInitialized(s.canonicalPath),await this.reloadExtensions(o,c.config.approvalMode,s.canonicalPath),this.options.sessionId!==void 0){const a=await this.runtime.sessionStore.get(this.options.sessionId);if(a===void 0)throw new Error(`Session not found: ${this.options.sessionId}`);if(a.projectFingerprint!==s.fingerprint)throw new Error("Session belongs to another project.");this.session=a,this.renderer.status(`Resuming session ${a.id}`);for(const d of m(await this.runtime.sessionStore.readTranscript(a.id)))d.type==="user"?this.renderer.userPrompt(d.content):d.type==="assistant"&&(this.renderer.beginAssistant(),this.renderer.assistantDelta(d.content),this.renderer.finishAssistant())}else this.session=await this.runtime.sessionStore.create({projectFingerprint:s.fingerprint,cwd:s.canonicalPath,model:c.config.model,profile:c.config.profile,status:"active"});return this.executionWorkspace=await this.executionWorkspaces.prepare({sessionId:this.session.id,projectRoot:s.canonicalPath}),await this.refreshContextUsage(),await this.runHookEvent("session.start",this.session.id),this.startBackgroundMcpWarmup(),this.renderer.setSubmitHandler(async a=>{await this.processPrompt(a)&&this.renderer.requestExit()}),this.renderer.clearActivity(),this.renderer.setReady(),this.backgroundOperations.start(a=>new y(s.canonicalPath).listEntries(a)).then(a=>this.renderer.setFileSuggestions(E(a))).catch(a=>this.runtime.logger.debug("Unable to load workspace file suggestions.",{reason:a instanceof Error?a.message:String(a)})),await this.renderer.waitForExit()}catch(s){throw r=L(s)?"cancelled":"failed",s}finally{this.turnController.abortActive(new Error("Interactive session is closing.")),await this.backgroundOperations.close(),this.renderer.resolveActiveModal(void 0),await this.awaitActiveForegroundRun();try{await this.subagentJobs.close()}catch(s){this.runtime.logger.warn("Unable to close subagent jobs during TUI shutdown.",{reason:s instanceof Error?s.message:String(s)})}if(this.agentSessionStore.clear(),this.session!==void 0){try{await this.runHookEvent("session.end",this.session.id)}catch(s){this.runtime.logger.warn("Session end hook failed during TUI shutdown.",{sessionId:this.session.id,reason:s instanceof Error?s.message:String(s)})}try{await this.runtime.sessionStore.updateStatus(this.session.id,r)}catch(s){this.runtime.logger.warn("Unable to update TUI session status during shutdown.",{sessionId:this.session.id,reason:s instanceof Error?s.message:String(s)})}try{await this.executionWorkspaces.markReady(this.session.id)}catch(s){this.runtime.logger.warn("Unable to release TUI execution workspace.",{sessionId:this.session.id,reason:s instanceof Error?s.message:String(s)})}}process.off("SIGINT",e),this.options.signal?.removeEventListener("abort",n),this.renderer.dispose(),t.unmount(),b(this.output),this.printResumeCommandOnExit&&this.session!==void 0&&this.output.write(`
|
|
2
|
+
${z(this.session.id)}
|
|
3
|
+
`)}}async processPrompt(i){if(this.activeForegroundRun!==void 0)return this.renderer.notice("An operation is already running. Stop it before starting another one."),!1;const t=j(i,{renderer:this.renderer,logger:this.runtime.logger,commandHost:I({runtime:this.runtime,cwd:this.cwd,renderer:this.renderer,pendingAttachments:this.pendingAttachments,getHooks:()=>this.hooks,getSession:()=>this.session,getActiveAbortController:()=>this.turnController.getActiveAbortController(),setActiveAbortController:e=>{this.turnController.setActiveAbortController(e)},attachClipboardImage:e=>this.attachClipboardImage(e),runPrompt:(e,n)=>this.turnController.runPrompt(e,n),runGoal:(e,n,r)=>this.runGoal(e,n,r),resumeGoal:e=>this.resumeGoal(e),runSkill:(e,n,r,s,o)=>this.runSkill(e,n,r,s,o),refreshExtensions:()=>this.refreshExtensions(),compactSession:()=>this.compactSession(),refreshWelcome:e=>this.refreshWelcome(e),getContextSnapshot:()=>this.getContextSnapshot(),resumeSession:e=>this.resumeSession(e)}),pendingAttachments:this.pendingAttachments,commandContext:()=>this.commandContext(),sendPrompt:(e,n)=>this.turnController.sendPrompt(e,n),resetActiveAbortController:()=>{this.turnController.isRunning()||this.turnController.setActiveAbortController(void 0)}});this.activeForegroundRun=t;try{return await t}finally{this.activeForegroundRun===t&&(this.activeForegroundRun=void 0)}}async attachClipboardImage(i,t=!0,e=""){if(i===void 0){t&&this.renderer.notice("Clipboard does not contain an image.");return}const n=C(i.bytes,i.mimeType);this.pendingAttachments.assertCanAdd(n.bytes.byteLength,e,{byteLength:n.bytes.byteLength,mimeType:n.mimeType});const r=this.pendingAttachments.nextId(),s=x(n,"clipboard",r),o=this.session===void 0?s:await this.runtime.sessionStore.saveAttachment(this.session.id,s,n.bytes);this.pendingAttachments.add({attachment:o,bytes:n.bytes},e),this.renderer.setPendingAttachments(this.pendingAttachments.list().map(({attachment:h})=>h)),t&&this.renderer.notice(`Attached ${s.name} \xB7 ${s.mimeType} \xB7 ${s.sizeBytes} bytes`)}async runPrompt(i,t,e){return this.turnController.runPrompt(i,t,e)}async runGoal(i,t,e={}){if(this.turnController.isRunning()){t.write("Stop the active response before starting a goal.");return}await $({runtime:this.runtime,cwd:this.cwd,sessionId:this.session?.id,model:this.activeModel??this.session?.model??null,profile:this.activeProfile??this.session?.profile??null,runPrompt:(n,r)=>this.runPrompt(n,void 0,r),write:n=>t.write(n)},i,e)}async resumeGoal(i){if(this.turnController.isRunning()){i.write("Stop the active response before resuming a goal.");return}await D({runtime:this.runtime,cwd:this.cwd,sessionId:this.session?.id,model:this.activeModel??this.session?.model??null,profile:this.activeProfile??this.session?.profile??null,runPrompt:(t,e)=>this.runPrompt(t,void 0,e),write:t=>i.write(t)})}commandContext(){return{write:i=>this.renderer.commandOutput(i)}}getActiveModel(){return this.activeModel}getActiveProfile(){return this.activeProfile}async refreshExtensions(){const i=await this.runtime.projectTrust.inspect(this.cwd);this.runtime.agentPreparation?.clear(i.canonicalPath);const t=await this.runtime.projectTrust.isTrusted(i),e=await this.runtime.configLoader.load(this.cwd,{includeProject:t});await this.reloadExtensions(t,e.config.approvalMode,i.canonicalPath),await this.refreshContextUsage()}async refreshWelcome(i={}){const t=await this.runtime.projectTrust.inspect(this.cwd);this.runtime.agentPreparation?.clear(t.canonicalPath);const e=await this.runtime.projectTrust.isTrusted(t),n=await this.runtime.configLoader.load(this.cwd,{includeProject:e});this.activeModel=i.model===void 0?this.activeModel??n.config.model:i.model,this.activeProfile=i.profile===void 0?this.activeProfile??n.config.profile:i.profile,this.renderer.setWelcome({version:u,cwd:t.canonicalPath,model:this.activeModel,profile:this.activeProfile,trusted:e}),await this.refreshContextUsage()}async getContextSnapshot(){const i=this.activeModel??this.session?.model??null,t=this.session===void 0?void 0:await this.runtime.goalStore.get(this.session.id),e=await this.contextService.buildForProject({...this.session===void 0?{}:{sessionId:this.session.id},model:i,profileName:this.activeProfile??this.session?.profile??null,pluginNames:this.enabledPluginNames,pluginMcpConfig:this.pluginMcpConfig,pendingAttachments:this.pendingAttachments.size,skillRootScopes:this.skillRootScopes,agentSessionStore:this.agentSessionStore,...t===void 0?{}:{goal:{objective:t.objective,status:t.status,turns:t.turns,evaluatorRuns:t.evaluatorRuns,tokensUsed:t.usage.totalTokens,...t.tokenBudget===void 0?{}:{tokenBudget:t.tokenBudget},...t.maxTurns===void 0?{}:{maxTurns:t.maxTurns},...t.maxDurationMs===void 0?{}:{maxDurationMs:t.maxDurationMs},...t.lastError===void 0?{}:{lastError:t.lastError},...t.lastEvaluation===void 0?{}:{lastEvaluation:t.lastEvaluation.reason}}}});return this.renderer.setContextUsage({usedTokens:e.usedTokens,...e.contextWindow===void 0?{}:{contextWindow:e.contextWindow}}),e}async refreshContextUsage(){try{await this.getContextSnapshot()}catch(i){this.runtime.logger.debug("Unable to refresh context usage.",{reason:i instanceof Error?i.message:String(i)})}}async runSkill(i,t,e,n,r){await J({globalConfigDir:this.runtime.paths.globalConfigDir,skillRootScopes:this.skillRootScopes,runPrompt:(s,o)=>this.turnController.sendPrompt(s,this.pendingAttachments.list(),o)},i,t,e,r,n)}async reloadExtensions(i,t,e){const n=await V({runtime:this.runtime,cwd:this.cwd,trusted:i,approvalMode:t,workspaceRoot:e,approvals:this.approvals});this.hookRuntime=n.hookRuntime,this.skillRootScopes=n.skillRootScopes,this.enabledPluginNames=n.enabledPluginNames,this.pluginMcpConfig=n.pluginMcpConfig,this.hooks=n.hooks}async compactSession(){await F({runtime:this.runtime,cwd:this.cwd,session:this.session,activeModel:this.activeModel,activeProfile:this.activeProfile,activeResponse:this.turnController.getActiveAbortController()!==void 0,renderer:this.renderer,contextService:this.contextService,agentSessionStore:this.agentSessionStore,refreshContextUsage:()=>this.refreshContextUsage()})}async resumeSession(i){if(this.turnController.isRunning())throw new Error("Stop the active response before resuming another session.");const t=await this.runtime.projectTrust.inspect(this.cwd),e=await new T(this.runtime.sessionStore).require(i,t.fingerprint);if(e.id===this.session?.id){this.renderer.notice(`Already using session: ${e.id}`);return}const n=await this.runtime.sessionStore.readTranscript(e.id),r=this.session;r!==void 0&&(await this.subagentJobs.close(),this.subagentJobs=new g,await this.runHookEvent("session.end",r.id),await this.runtime.sessionStore.updateStatus(r.id,"completed"),this.agentSessionStore.delete(r.id)),this.session=await this.runtime.sessionStore.updateStatus(e.id,"active"),this.executionWorkspace=await this.executionWorkspaces.prepare({sessionId:e.id,projectRoot:t.canonicalPath});const s=await B(this.runtime,this.cwd);this.activeModel=e.model??s.config.model,this.activeProfile=e.profile??s.config.profile,this.renderer.setWelcome({version:u,cwd:t.canonicalPath,model:this.activeModel,profile:this.activeProfile,trusted:await this.runtime.projectTrust.isTrusted(t)}),this.renderer.clearTranscript();for(const o of m(n))o.type==="user"?this.renderer.userPrompt(o.content):o.type==="assistant"&&(this.renderer.beginAssistant(),this.renderer.assistantDelta(o.content),this.renderer.finishAssistant());await this.refreshContextUsage(),await this.runHookEvent("session.start",e.id),this.pendingAttachments.clear(),this.renderer.setPendingAttachments([]),this.startBackgroundMcpWarmup(),this.renderer.notice(`Resumed session: ${e.id}`)}handleInterrupt(){const i=G({lastInterruptAt:this.lastInterruptAt,exitArmedUntil:this.exitArmedUntil,printResumeCommandOnExit:this.printResumeCommandOnExit},this.turnController.getActiveAbortController(),this.renderer);this.lastInterruptAt=i.lastInterruptAt,this.exitArmedUntil=i.exitArmedUntil,this.printResumeCommandOnExit=i.printResumeCommandOnExit}startBackgroundMcpWarmup(){this.backgroundOperations.start(i=>R(this.runtime,this.cwd,i)).catch(i=>this.runtime.logger.warn("MCP session warm-up failed.",{reason:i instanceof Error?i.message:String(i)}))}async awaitActiveForegroundRun(){const i=this.activeForegroundRun;if(i!==void 0)try{await i}catch(t){this.runtime.logger.warn("Foreground TUI operation failed during shutdown.",{reason:t instanceof Error?t.message:String(t)})}}async runHookEvent(i,t){await N(this.hookRuntime,this.hooks,i,this.executionWorkspace?.executionCwd??this.cwd,t)}}export{It as TuiApplication};
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.28";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const o="0.1.
|
|
1
|
+
const o="0.1.28";export{o as CLI_VERSION};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotagate/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.28",
|
|
4
4
|
"description": "Enterprise-grade terminal client for LotaGate AI agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -64,19 +64,19 @@
|
|
|
64
64
|
"prepack": "npm run build"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@lotagate/agent-sdk": "^0.1.
|
|
67
|
+
"@lotagate/agent-sdk": "^0.1.9",
|
|
68
68
|
"@lotagate/sdk": "^0.1.3",
|
|
69
69
|
"ink": "^7.1.1",
|
|
70
70
|
"react": "^19.2.4",
|
|
71
71
|
"tiktoken": "^1.0.22"
|
|
72
72
|
},
|
|
73
73
|
"optionalDependencies": {
|
|
74
|
-
"@lotagate/cli-native-linux-arm64-gnu": "0.1.
|
|
75
|
-
"@lotagate/cli-native-linux-x64-gnu": "0.1.
|
|
76
|
-
"@lotagate/cli-native-macos-arm64": "0.1.
|
|
77
|
-
"@lotagate/cli-native-macos-x64": "0.1.
|
|
78
|
-
"@lotagate/cli-native-windows-arm64": "0.1.
|
|
79
|
-
"@lotagate/cli-native-windows-x64": "0.1.
|
|
74
|
+
"@lotagate/cli-native-linux-arm64-gnu": "0.1.28",
|
|
75
|
+
"@lotagate/cli-native-linux-x64-gnu": "0.1.28",
|
|
76
|
+
"@lotagate/cli-native-macos-arm64": "0.1.28",
|
|
77
|
+
"@lotagate/cli-native-macos-x64": "0.1.28",
|
|
78
|
+
"@lotagate/cli-native-windows-arm64": "0.1.28",
|
|
79
|
+
"@lotagate/cli-native-windows-x64": "0.1.28"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@eslint/js": "9.39.5",
|