@yuandc/aica 0.1.0 → 0.1.2
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/acp/agent.js +1 -54
- package/dist/acp/client/acp-client.js +1 -102
- package/dist/acp/client/acp-content.js +1 -13
- package/dist/acp/client/acp-events.js +1 -106
- package/dist/acp/client/acp-process.js +1 -34
- package/dist/acp/client/acp-runtime-pool.js +1 -248
- package/dist/acp/client/context-usage.js +1 -29
- package/dist/acp/client/json-rpc.js +4 -128
- package/dist/acp/provider-types.js +0 -1
- package/dist/acp/providers/codex/codex-process.js +1 -51
- package/dist/acp/providers/codex/events.js +28 -1473
- package/dist/acp/providers/codex/permissions.js +1 -49
- package/dist/acp/providers/codex/provider.js +1 -376
- package/dist/acp/providers/codex-acp/adapter.js +5 -947
- package/dist/acp/providers/codex-acp/context-maintenance.js +5 -148
- package/dist/acp/providers/codex-acp/launch.js +1 -35
- package/dist/acp/providers/codex-acp/provider.js +1 -486
- package/dist/acp/providers/mimo/provider.js +5 -448
- package/dist/acp/providers/opencode/provider.js +4 -489
- package/dist/acp/providers/registry.js +1 -23
- package/dist/acp/standard-events.js +1 -167
- package/dist/commands/start.js +1 -137
- package/dist/commands/worker-auth.js +4 -100
- package/dist/commands/worker-project.js +1 -57
- package/dist/core/aca-config.js +1 -74
- package/dist/core/aca-server-client.js +1 -57
- package/dist/core/acp-event-coalescer.js +1 -108
- package/dist/core/acp-event-upload-filter.js +1 -16
- package/dist/core/acp-orphan-cleanup.js +1 -91
- package/dist/core/affected-files.js +2 -268
- package/dist/core/auth.js +1 -36
- package/dist/core/file-transfer-worker.js +1 -169
- package/dist/core/fs.js +2 -28
- package/dist/core/heartbeat.js +3 -578
- package/dist/core/job-permission-policy.js +1 -42
- package/dist/core/job-worker.js +6 -749
- package/dist/core/logger.js +3 -42
- package/dist/core/long-poll-worker.js +1 -26
- package/dist/core/machine-filesystem-worker.js +3 -352
- package/dist/core/paths.js +1 -26
- package/dist/core/process-identity.js +1 -34
- package/dist/core/process.js +2 -33
- package/dist/core/provider-health.js +1 -54
- package/dist/core/runtime-options.js +1 -38
- package/dist/core/worktree.js +1 -95
- package/dist/worker-cli.js +1 -26
- package/dist/worker-single-cli.js +1 -16
- package/package.json +1 -1
package/dist/acp/agent.js
CHANGED
|
@@ -1,54 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { createAgentProvider } from "./providers/registry.js";
|
|
3
|
-
export async function sendAgentPrompt(input) {
|
|
4
|
-
if (!fs.existsSync(input.cwd) || !fs.statSync(input.cwd).isDirectory()) {
|
|
5
|
-
throw new Error(`Project root does not exist or is not a directory: ${input.cwd}`);
|
|
6
|
-
}
|
|
7
|
-
const provider = createAgentProvider({
|
|
8
|
-
agentType: input.agentType || "codex",
|
|
9
|
-
cliType: input.cliType || "builtin"
|
|
10
|
-
});
|
|
11
|
-
const session = await provider.createSession({
|
|
12
|
-
cwd: input.cwd,
|
|
13
|
-
providerSessionId: input.acpSessionId ?? null,
|
|
14
|
-
timeoutMs: input.timeoutMs
|
|
15
|
-
});
|
|
16
|
-
try {
|
|
17
|
-
const result = await session.sendPrompt({
|
|
18
|
-
...input,
|
|
19
|
-
providerSessionId: input.acpSessionId ?? null
|
|
20
|
-
});
|
|
21
|
-
return {
|
|
22
|
-
...result,
|
|
23
|
-
acpSessionId: result.providerSessionId
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
finally {
|
|
27
|
-
await session.close().catch(() => void 0);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
export async function compactAgentContext(input) {
|
|
31
|
-
const provider = createAgentProvider({
|
|
32
|
-
agentType: input.agentType || "codex",
|
|
33
|
-
cliType: input.cliType || "builtin"
|
|
34
|
-
});
|
|
35
|
-
const session = await provider.createSession({
|
|
36
|
-
cwd: input.cwd,
|
|
37
|
-
providerSessionId: input.acpSessionId,
|
|
38
|
-
timeoutMs: input.timeoutMs
|
|
39
|
-
});
|
|
40
|
-
if (!session.compactContext) {
|
|
41
|
-
await session.close().catch(() => void 0);
|
|
42
|
-
throw new Error(`Provider does not support context compaction: ${input.agentType || "codex"}`);
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
const result = await session.compactContext(input);
|
|
46
|
-
return {
|
|
47
|
-
...result,
|
|
48
|
-
acpSessionId: result.providerSessionId
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
finally {
|
|
52
|
-
await session.close().catch(() => void 0);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
1
|
+
import s from"node:fs";import{createAgentProvider as r}from"./providers/registry.js";async function a(e){if(!s.existsSync(e.cwd)||!s.statSync(e.cwd).isDirectory())throw new Error(`Project root does not exist or is not a directory: ${e.cwd}`);const o=await r({agentType:e.agentType||"codex",cliType:e.cliType||"builtin"}).createSession({cwd:e.cwd,providerSessionId:e.acpSessionId??null,timeoutMs:e.timeoutMs});try{const t=await o.sendPrompt({...e,providerSessionId:e.acpSessionId??null});return{...t,acpSessionId:t.providerSessionId}}finally{await o.close().catch(()=>{})}}async function d(e){const o=await r({agentType:e.agentType||"codex",cliType:e.cliType||"builtin"}).createSession({cwd:e.cwd,providerSessionId:e.acpSessionId,timeoutMs:e.timeoutMs});if(!o.compactContext)throw await o.close().catch(()=>{}),new Error(`Provider does not support context compaction: ${e.agentType||"codex"}`);try{const t=await o.compactContext(e);return{...t,acpSessionId:t.providerSessionId}}finally{await o.close().catch(()=>{})}}export{d as compactAgentContext,a as sendAgentPrompt};
|
|
@@ -1,102 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export class AcpClient {
|
|
3
|
-
onSessionUpdate;
|
|
4
|
-
onClientRequest;
|
|
5
|
-
rpc;
|
|
6
|
-
constructor(child, onSessionUpdate, onClientRequest) {
|
|
7
|
-
this.onSessionUpdate = onSessionUpdate;
|
|
8
|
-
this.onClientRequest = onClientRequest;
|
|
9
|
-
this.rpc = new JsonLineRpcClient(child, {
|
|
10
|
-
peerName: "ACP server",
|
|
11
|
-
includeJsonRpc: true,
|
|
12
|
-
onNotification: (method, params, message) => {
|
|
13
|
-
if (method === "session/update")
|
|
14
|
-
this.onSessionUpdate(params, message);
|
|
15
|
-
},
|
|
16
|
-
onRequest: async (method, params, message) => {
|
|
17
|
-
if (this.onClientRequest)
|
|
18
|
-
return this.onClientRequest(method, params, message);
|
|
19
|
-
throw new Error(`Unsupported ACP client request: ${method}`);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
initialize(timeoutMs = 60_000) {
|
|
24
|
-
return this.rpc.request("initialize", {
|
|
25
|
-
protocolVersion: 1,
|
|
26
|
-
clientCapabilities: {
|
|
27
|
-
fs: { readTextFile: false, writeTextFile: false },
|
|
28
|
-
terminal: false
|
|
29
|
-
},
|
|
30
|
-
clientInfo: { name: "aca", version: "0.1.0" }
|
|
31
|
-
}, timeoutMs);
|
|
32
|
-
}
|
|
33
|
-
newSession(input, timeoutMs = 120_000) {
|
|
34
|
-
return this.rpc.request("session/new", {
|
|
35
|
-
cwd: input.cwd,
|
|
36
|
-
mcpServers: input.mcpServers ?? [],
|
|
37
|
-
...(input.config ? { config: input.config } : {})
|
|
38
|
-
}, timeoutMs);
|
|
39
|
-
}
|
|
40
|
-
resumeSession(input, timeoutMs = 120_000) {
|
|
41
|
-
return this.rpc.request("session/resume", {
|
|
42
|
-
sessionId: input.sessionId,
|
|
43
|
-
cwd: input.cwd,
|
|
44
|
-
mcpServers: input.mcpServers ?? [],
|
|
45
|
-
...(input.config ? { config: input.config } : {})
|
|
46
|
-
}, timeoutMs);
|
|
47
|
-
}
|
|
48
|
-
loadSession(input, timeoutMs = 120_000) {
|
|
49
|
-
return this.rpc.request("session/load", {
|
|
50
|
-
sessionId: input.sessionId,
|
|
51
|
-
cwd: input.cwd,
|
|
52
|
-
mcpServers: input.mcpServers ?? [],
|
|
53
|
-
...(input.config ? { config: input.config } : {})
|
|
54
|
-
}, timeoutMs);
|
|
55
|
-
}
|
|
56
|
-
closeSession(sessionId, timeoutMs = 30_000) {
|
|
57
|
-
return this.rpc.request("session/close", { sessionId }, timeoutMs);
|
|
58
|
-
}
|
|
59
|
-
listSessions(params = {}, timeoutMs = 60_000) {
|
|
60
|
-
return this.rpc.request("session/list", params, timeoutMs);
|
|
61
|
-
}
|
|
62
|
-
deleteSession(sessionId, timeoutMs = 60_000) {
|
|
63
|
-
return this.rpc.request("session/delete", { sessionId }, timeoutMs);
|
|
64
|
-
}
|
|
65
|
-
forkSession(input, timeoutMs = 120_000) {
|
|
66
|
-
return this.rpc.request("session/fork", {
|
|
67
|
-
sessionId: input.sessionId,
|
|
68
|
-
...(input.cwd ? { cwd: input.cwd } : {}),
|
|
69
|
-
...(input.mcpServers ? { mcpServers: input.mcpServers } : {})
|
|
70
|
-
}, timeoutMs);
|
|
71
|
-
}
|
|
72
|
-
setSessionConfigOption(input, timeoutMs = 60_000) {
|
|
73
|
-
return this.rpc.request("session/set_config_option", input, timeoutMs);
|
|
74
|
-
}
|
|
75
|
-
setSessionMode(input, timeoutMs = 60_000) {
|
|
76
|
-
return this.rpc.request("session/set_mode", input, timeoutMs);
|
|
77
|
-
}
|
|
78
|
-
prompt(input, timeoutMs) {
|
|
79
|
-
return this.rpc.request("session/prompt", input, timeoutMs);
|
|
80
|
-
}
|
|
81
|
-
compactSession(input, timeoutMs = 10 * 60 * 1000) {
|
|
82
|
-
return this.rpc.request("session/compact", input, timeoutMs);
|
|
83
|
-
}
|
|
84
|
-
cancel(sessionId) {
|
|
85
|
-
this.rpc.notify("session/cancel", { sessionId });
|
|
86
|
-
}
|
|
87
|
-
authenticate(input, timeoutMs = 120_000) {
|
|
88
|
-
return this.rpc.request("authenticate", input, timeoutMs);
|
|
89
|
-
}
|
|
90
|
-
logout(input = {}, timeoutMs = 60_000) {
|
|
91
|
-
return this.rpc.request("logout", input, timeoutMs);
|
|
92
|
-
}
|
|
93
|
-
listProviders(input = {}, timeoutMs = 60_000) {
|
|
94
|
-
return this.rpc.request("providers/list", input, timeoutMs);
|
|
95
|
-
}
|
|
96
|
-
setProvider(input, timeoutMs = 60_000) {
|
|
97
|
-
return this.rpc.request("providers/set", input, timeoutMs);
|
|
98
|
-
}
|
|
99
|
-
disableProvider(input, timeoutMs = 60_000) {
|
|
100
|
-
return this.rpc.request("providers/disable", input, timeoutMs);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
1
|
+
import{JsonLineRpcClient as n}from"./json-rpc.js";class l{onSessionUpdate;onClientRequest;rpc;constructor(e,s,o){this.onSessionUpdate=s,this.onClientRequest=o,this.rpc=new n(e,{peerName:"ACP server",includeJsonRpc:!0,onNotification:(r,t,i)=>{r==="session/update"&&this.onSessionUpdate(t,i)},onRequest:async(r,t,i)=>{if(this.onClientRequest)return this.onClientRequest(r,t,i);throw new Error(`Unsupported ACP client request: ${r}`)}})}initialize(e=6e4){return this.rpc.request("initialize",{protocolVersion:1,clientCapabilities:{fs:{readTextFile:!1,writeTextFile:!1},terminal:!1},clientInfo:{name:"aca",version:"0.1.0"}},e)}newSession(e,s=12e4){return this.rpc.request("session/new",{cwd:e.cwd,mcpServers:e.mcpServers??[],...e.config?{config:e.config}:{}},s)}resumeSession(e,s=12e4){return this.rpc.request("session/resume",{sessionId:e.sessionId,cwd:e.cwd,mcpServers:e.mcpServers??[],...e.config?{config:e.config}:{}},s)}loadSession(e,s=12e4){return this.rpc.request("session/load",{sessionId:e.sessionId,cwd:e.cwd,mcpServers:e.mcpServers??[],...e.config?{config:e.config}:{}},s)}closeSession(e,s=3e4){return this.rpc.request("session/close",{sessionId:e},s)}listSessions(e={},s=6e4){return this.rpc.request("session/list",e,s)}deleteSession(e,s=6e4){return this.rpc.request("session/delete",{sessionId:e},s)}forkSession(e,s=12e4){return this.rpc.request("session/fork",{sessionId:e.sessionId,...e.cwd?{cwd:e.cwd}:{},...e.mcpServers?{mcpServers:e.mcpServers}:{}},s)}setSessionConfigOption(e,s=6e4){return this.rpc.request("session/set_config_option",e,s)}setSessionMode(e,s=6e4){return this.rpc.request("session/set_mode",e,s)}prompt(e,s){return this.rpc.request("session/prompt",e,s)}compactSession(e,s=600*1e3){return this.rpc.request("session/compact",e,s)}cancel(e){this.rpc.notify("session/cancel",{sessionId:e})}authenticate(e,s=12e4){return this.rpc.request("authenticate",e,s)}logout(e={},s=6e4){return this.rpc.request("logout",e,s)}listProviders(e={},s=6e4){return this.rpc.request("providers/list",e,s)}setProvider(e,s=6e4){return this.rpc.request("providers/set",e,s)}disableProvider(e,s=6e4){return this.rpc.request("providers/disable",e,s)}}export{l as AcpClient};
|
|
@@ -1,13 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export function promptBlocksToAcpContent(prompt, blocks) {
|
|
3
|
-
const inputBlocks = blocks?.length ? blocks : [{ type: "text", text: prompt }];
|
|
4
|
-
return inputBlocks.map((block) => {
|
|
5
|
-
if (block.type === "image") {
|
|
6
|
-
if (block.uri && fs.existsSync(block.uri)) {
|
|
7
|
-
return { type: "image", uri: block.uri, mimeType: block.mimeType };
|
|
8
|
-
}
|
|
9
|
-
return { type: "image", data: block.data, mimeType: block.mimeType };
|
|
10
|
-
}
|
|
11
|
-
return { type: "text", text: block.text };
|
|
12
|
-
});
|
|
13
|
-
}
|
|
1
|
+
import p from"node:fs";function n(i,e){return(e?.length?e:[{type:"text",text:i}]).map(t=>t.type==="image"?t.uri&&p.existsSync(t.uri)?{type:"image",uri:t.uri,mimeType:t.mimeType}:{type:"image",data:t.data,mimeType:t.mimeType}:{type:"text",text:t.text})}export{n as promptBlocksToAcpContent};
|
|
@@ -1,106 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export function acpSessionUpdateFromParams(params) {
|
|
3
|
-
if (!params || typeof params !== "object" || Array.isArray(params))
|
|
4
|
-
return null;
|
|
5
|
-
const record = params;
|
|
6
|
-
const update = record.update;
|
|
7
|
-
if (!update || typeof update !== "object" || Array.isArray(update))
|
|
8
|
-
return null;
|
|
9
|
-
return {
|
|
10
|
-
sessionId: typeof record.sessionId === "string" ? record.sessionId : undefined,
|
|
11
|
-
update: update
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
export function eventFromAcpSessionUpdate(params) {
|
|
15
|
-
const parsed = acpSessionUpdateFromParams(params);
|
|
16
|
-
if (!parsed)
|
|
17
|
-
return null;
|
|
18
|
-
const updateType = String(parsed.update.sessionUpdate || "");
|
|
19
|
-
if (updateType === "agent_message_chunk") {
|
|
20
|
-
return createAgentMessageChunk({ text: textFromAcpContent(parsed.update.content), method: "session/update", params });
|
|
21
|
-
}
|
|
22
|
-
if (updateType === "agent_progress_chunk") {
|
|
23
|
-
return createAgentProgressChunk({ text: textFromAcpContent(parsed.update.content), method: "session/update", params });
|
|
24
|
-
}
|
|
25
|
-
if (updateType === "agent_thought_chunk") {
|
|
26
|
-
return createAgentThoughtChunk({ text: textFromAcpContent(parsed.update.content), method: "session/update", params });
|
|
27
|
-
}
|
|
28
|
-
if (updateType === "tool_call" || updateType === "tool_call_update") {
|
|
29
|
-
return toolEventFromAcpUpdate(updateType, parsed.update, params);
|
|
30
|
-
}
|
|
31
|
-
if (STANDARD_AGENT_EVENT_TYPES.has(updateType)) {
|
|
32
|
-
return {
|
|
33
|
-
type: updateType,
|
|
34
|
-
label: labelForUpdate(updateType),
|
|
35
|
-
...(textFromAcpContent(parsed.update.content) ? { text: textFromAcpContent(parsed.update.content) } : {}),
|
|
36
|
-
raw: { method: "session/update", params, update: parsed.update },
|
|
37
|
-
atMs: Date.now()
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
export function statusFromAcpSessionUpdate(params) {
|
|
43
|
-
const parsed = acpSessionUpdateFromParams(params);
|
|
44
|
-
if (!parsed)
|
|
45
|
-
return null;
|
|
46
|
-
const updateType = String(parsed.update.sessionUpdate || "");
|
|
47
|
-
if (updateType === "agent_message_chunk")
|
|
48
|
-
return { phase: "thinking", label: "回复中", detail: textPreview(textFromAcpContent(parsed.update.content)), updateType };
|
|
49
|
-
if (updateType === "agent_progress_chunk")
|
|
50
|
-
return { phase: "thinking", label: "处理中", detail: textPreview(textFromAcpContent(parsed.update.content)), updateType };
|
|
51
|
-
if (updateType === "agent_thought_chunk")
|
|
52
|
-
return { phase: "thinking", label: "思考中", detail: textPreview(textFromAcpContent(parsed.update.content)), updateType };
|
|
53
|
-
if (updateType === "tool_call" || updateType === "tool_call_update")
|
|
54
|
-
return { phase: "exploring", label: "执行工具", detail: firstString(parsed.update.title, parsed.update.kind), updateType };
|
|
55
|
-
if (updateType === "plan" || updateType === "plan_update")
|
|
56
|
-
return { phase: "thinking", label: "规划中", updateType };
|
|
57
|
-
if (updateType === "aca_context_maintenance") {
|
|
58
|
-
return {
|
|
59
|
-
phase: "thinking",
|
|
60
|
-
label: firstString(parsed.update.label) || "正在整理会话上下文",
|
|
61
|
-
detail: firstString(parsed.update.detail),
|
|
62
|
-
updateType
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
export function textFromAcpContent(content) {
|
|
68
|
-
if (typeof content === "string")
|
|
69
|
-
return content;
|
|
70
|
-
if (!content || typeof content !== "object" || Array.isArray(content))
|
|
71
|
-
return "";
|
|
72
|
-
const record = content;
|
|
73
|
-
if (typeof record.text === "string")
|
|
74
|
-
return record.text;
|
|
75
|
-
return "";
|
|
76
|
-
}
|
|
77
|
-
function toolEventFromAcpUpdate(updateType, update, params) {
|
|
78
|
-
const toolCallId = firstString(update.toolCallId, update.id) || `acp.${Date.now()}`;
|
|
79
|
-
const kind = normalizeToolKind(firstString(update.kind, update.title));
|
|
80
|
-
const status = normalizeToolStatus(firstString(update.status));
|
|
81
|
-
const input = update.rawInput ?? update.input;
|
|
82
|
-
const output = update.rawOutput ?? update.output;
|
|
83
|
-
const createInput = {
|
|
84
|
-
method: "session/update",
|
|
85
|
-
params,
|
|
86
|
-
toolCallId,
|
|
87
|
-
kind,
|
|
88
|
-
title: firstString(update.title, update.kind),
|
|
89
|
-
status,
|
|
90
|
-
rawInput: input,
|
|
91
|
-
rawOutput: output,
|
|
92
|
-
content: update.content,
|
|
93
|
-
locations: Array.isArray(update.locations) ? update.locations.filter((item) => Boolean(item && typeof item === "object" && !Array.isArray(item))) : undefined,
|
|
94
|
-
meta: update._meta && typeof update._meta === "object" && !Array.isArray(update._meta) ? update._meta : undefined
|
|
95
|
-
};
|
|
96
|
-
return updateType === "tool_call" ? createToolCall(createInput) : createToolCallUpdate(createInput);
|
|
97
|
-
}
|
|
98
|
-
function labelForUpdate(updateType) {
|
|
99
|
-
return updateType.replace(/_/g, " ");
|
|
100
|
-
}
|
|
101
|
-
function textPreview(value) {
|
|
102
|
-
const normalized = value.replace(/\s+/g, " ").trim();
|
|
103
|
-
if (!normalized)
|
|
104
|
-
return undefined;
|
|
105
|
-
return normalized.length > 120 ? `${normalized.slice(0, 117)}...` : normalized;
|
|
106
|
-
}
|
|
1
|
+
import{STANDARD_AGENT_EVENT_TYPES as g,createAgentMessageChunk as h,createAgentProgressChunk as _,createAgentThoughtChunk as y,createToolCall as A,createToolCallUpdate as k,firstString as o,normalizeToolKind as m,normalizeToolStatus as b}from"../standard-events.js";function l(n){if(!n||typeof n!="object"||Array.isArray(n))return null;const t=n,e=t.update;return!e||typeof e!="object"||Array.isArray(e)?null:{sessionId:typeof t.sessionId=="string"?t.sessionId:void 0,update:e}}function w(n){const t=l(n);if(!t)return null;const e=String(t.update.sessionUpdate||"");return e==="agent_message_chunk"?h({text:r(t.update.content),method:"session/update",params:n}):e==="agent_progress_chunk"?_({text:r(t.update.content),method:"session/update",params:n}):e==="agent_thought_chunk"?y({text:r(t.update.content),method:"session/update",params:n}):e==="tool_call"||e==="tool_call_update"?x(e,t.update,n):g.has(e)?{type:e,label:T(e),...r(t.update.content)?{text:r(t.update.content)}:{},raw:{method:"session/update",params:n,update:t.update},atMs:Date.now()}:null}function C(n){const t=l(n);if(!t)return null;const e=String(t.update.sessionUpdate||"");return e==="agent_message_chunk"?{phase:"thinking",label:"回复中",detail:i(r(t.update.content)),updateType:e}:e==="agent_progress_chunk"?{phase:"thinking",label:"处理中",detail:i(r(t.update.content)),updateType:e}:e==="agent_thought_chunk"?{phase:"thinking",label:"思考中",detail:i(r(t.update.content)),updateType:e}:e==="tool_call"||e==="tool_call_update"?{phase:"exploring",label:"执行工具",detail:o(t.update.title,t.update.kind),updateType:e}:e==="plan"||e==="plan_update"?{phase:"thinking",label:"规划中",updateType:e}:e==="aca_context_maintenance"?{phase:"thinking",label:o(t.update.label)||"正在整理会话上下文",detail:o(t.update.detail),updateType:e}:null}function r(n){if(typeof n=="string")return n;if(!n||typeof n!="object"||Array.isArray(n))return"";const t=n;return typeof t.text=="string"?t.text:""}function x(n,t,e){const u=o(t.toolCallId,t.id)||`acp.${Date.now()}`,c=m(o(t.kind,t.title)),p=b(o(t.status)),d=t.rawInput??t.input,f=t.rawOutput??t.output,s={method:"session/update",params:e,toolCallId:u,kind:c,title:o(t.title,t.kind),status:p,rawInput:d,rawOutput:f,content:t.content,locations:Array.isArray(t.locations)?t.locations.filter(a=>!!(a&&typeof a=="object"&&!Array.isArray(a))):void 0,meta:t._meta&&typeof t._meta=="object"&&!Array.isArray(t._meta)?t._meta:void 0};return n==="tool_call"?A(s):k(s)}function T(n){return n.replace(/_/g," ")}function i(n){const t=n.replace(/\s+/g," ").trim();if(t)return t.length>120?`${t.slice(0,117)}...`:t}export{l as acpSessionUpdateFromParams,w as eventFromAcpSessionUpdate,C as statusFromAcpSessionUpdate,r as textFromAcpContent};
|
|
@@ -1,34 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { acpChildEnvironment } from "../../core/process-identity.js";
|
|
3
|
-
export function startAcpProcess(command, args, cwd) {
|
|
4
|
-
const child = spawn(command, args, {
|
|
5
|
-
cwd,
|
|
6
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
7
|
-
env: acpChildEnvironment()
|
|
8
|
-
});
|
|
9
|
-
return { command, args, child };
|
|
10
|
-
}
|
|
11
|
-
export async function closeAcpProcess(child) {
|
|
12
|
-
if (child.exitCode !== null || child.signalCode !== null)
|
|
13
|
-
return;
|
|
14
|
-
child.stdin.end();
|
|
15
|
-
if (await waitForProcessClose(child, 2_000))
|
|
16
|
-
return;
|
|
17
|
-
child.kill("SIGTERM");
|
|
18
|
-
if (await waitForProcessClose(child, 1_000))
|
|
19
|
-
return;
|
|
20
|
-
child.kill("SIGKILL");
|
|
21
|
-
await waitForProcessClose(child, 1_000);
|
|
22
|
-
}
|
|
23
|
-
function waitForProcessClose(child, timeoutMs) {
|
|
24
|
-
if (child.exitCode !== null || child.signalCode !== null)
|
|
25
|
-
return Promise.resolve(true);
|
|
26
|
-
return new Promise((resolve) => {
|
|
27
|
-
const timer = setTimeout(() => resolve(false), timeoutMs);
|
|
28
|
-
timer.unref();
|
|
29
|
-
child.once("close", () => {
|
|
30
|
-
clearTimeout(timer);
|
|
31
|
-
resolve(true);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
}
|
|
1
|
+
import{spawn as i}from"node:child_process";import{acpChildEnvironment as s}from"../../core/process-identity.js";function a(e,t,n){const r=i(e,t,{cwd:n,stdio:["pipe","pipe","pipe"],env:s()});return{command:e,args:t,child:r}}async function l(e){e.exitCode!==null||e.signalCode!==null||(e.stdin.end(),!await o(e,2e3)&&(e.kill("SIGTERM"),!await o(e,1e3)&&(e.kill("SIGKILL"),await o(e,1e3))))}function o(e,t){return e.exitCode!==null||e.signalCode!==null?Promise.resolve(!0):new Promise(n=>{const r=setTimeout(()=>n(!1),t);r.unref(),e.once("close",()=>{clearTimeout(r),n(!0)})})}export{l as closeAcpProcess,a as startAcpProcess};
|
|
@@ -1,248 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { AcpClient } from "./acp-client.js";
|
|
4
|
-
import { closeAcpProcess, startAcpProcess } from "./acp-process.js";
|
|
5
|
-
const DEFAULT_IDLE_TTL_MS = 5 * 60 * 1000;
|
|
6
|
-
const DEFAULT_SWEEP_INTERVAL_MS = 60 * 1000;
|
|
7
|
-
const DEFAULT_MAX_IDLE_RUNTIMES = 4;
|
|
8
|
-
export class AcpRuntimePool {
|
|
9
|
-
runtimes = new Map();
|
|
10
|
-
idleTtlMs;
|
|
11
|
-
sweepIntervalMs;
|
|
12
|
-
maxIdleRuntimes;
|
|
13
|
-
sweepTimer = null;
|
|
14
|
-
disposeAllPromise = null;
|
|
15
|
-
constructor(options = {}) {
|
|
16
|
-
this.idleTtlMs = Math.max(1_000, options.idleTtlMs ?? readRuntimePoolIdleTtlMs());
|
|
17
|
-
this.sweepIntervalMs = Math.max(1_000, options.sweepIntervalMs ?? readRuntimePoolSweepIntervalMs());
|
|
18
|
-
this.maxIdleRuntimes = Math.max(0, options.maxIdleRuntimes ?? readRuntimePoolMaxIdleRuntimes());
|
|
19
|
-
}
|
|
20
|
-
async acquire(input) {
|
|
21
|
-
if (this.disposeAllPromise)
|
|
22
|
-
await this.disposeAllPromise;
|
|
23
|
-
const cwd = fs.realpathSync(input.cwd);
|
|
24
|
-
this.ensureSweep();
|
|
25
|
-
const existingKey = input.providerSessionId ? runtimeKey(input.providerId, cwd, input.providerSessionId) : null;
|
|
26
|
-
const existing = existingKey ? this.runtimes.get(existingKey) : null;
|
|
27
|
-
if (existing && !this.isRuntimeAlive(existing)) {
|
|
28
|
-
await this.disposeEntry(existing).catch(() => void 0);
|
|
29
|
-
}
|
|
30
|
-
const reusable = existingKey ? this.runtimes.get(existingKey) : null;
|
|
31
|
-
if (reusable) {
|
|
32
|
-
this.activateEntry(reusable, input);
|
|
33
|
-
return this.createLease(reusable);
|
|
34
|
-
}
|
|
35
|
-
const tempSessionId = input.providerSessionId || `pending:${randomUUID()}`;
|
|
36
|
-
const key = runtimeKey(input.providerId, cwd, tempSessionId);
|
|
37
|
-
const entry = this.createEntry({ ...input, cwd, key, providerSessionId: input.providerSessionId ?? null });
|
|
38
|
-
this.runtimes.set(key, entry);
|
|
39
|
-
return this.createLease(entry);
|
|
40
|
-
}
|
|
41
|
-
disposeAll() {
|
|
42
|
-
if (this.disposeAllPromise)
|
|
43
|
-
return this.disposeAllPromise;
|
|
44
|
-
const promise = this.performDisposeAll();
|
|
45
|
-
this.disposeAllPromise = promise;
|
|
46
|
-
void promise.finally(() => {
|
|
47
|
-
if (this.disposeAllPromise === promise)
|
|
48
|
-
this.disposeAllPromise = null;
|
|
49
|
-
});
|
|
50
|
-
return promise;
|
|
51
|
-
}
|
|
52
|
-
async performDisposeAll() {
|
|
53
|
-
if (this.sweepTimer) {
|
|
54
|
-
clearInterval(this.sweepTimer);
|
|
55
|
-
this.sweepTimer = null;
|
|
56
|
-
}
|
|
57
|
-
const entries = [...this.runtimes.values()];
|
|
58
|
-
await Promise.all(entries.map((entry) => this.disposeEntry(entry).catch(() => void 0)));
|
|
59
|
-
}
|
|
60
|
-
async sweepIdle(nowMs = Date.now()) {
|
|
61
|
-
const entries = [...this.runtimes.values()];
|
|
62
|
-
for (const entry of entries) {
|
|
63
|
-
if (entry.inUse || entry.disposed)
|
|
64
|
-
continue;
|
|
65
|
-
if (nowMs - entry.lastUsedAtMs < this.idleTtlMs)
|
|
66
|
-
continue;
|
|
67
|
-
await this.disposeEntry(entry).catch(() => void 0);
|
|
68
|
-
}
|
|
69
|
-
await this.trimIdleRuntimes();
|
|
70
|
-
}
|
|
71
|
-
stats() {
|
|
72
|
-
const entries = [...this.runtimes.values()].filter((entry) => !entry.disposed);
|
|
73
|
-
return {
|
|
74
|
-
total: entries.length,
|
|
75
|
-
inUse: entries.filter((entry) => entry.inUse).length,
|
|
76
|
-
idle: entries.filter((entry) => !entry.inUse).length,
|
|
77
|
-
pids: entries.map((entry) => entry.started.child.pid).filter((pid) => Number.isInteger(pid))
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
createEntry(input) {
|
|
81
|
-
const started = startAcpProcess(input.command, input.args, input.cwd);
|
|
82
|
-
const entry = {
|
|
83
|
-
key: input.key,
|
|
84
|
-
providerId: input.providerId,
|
|
85
|
-
cwd: input.cwd,
|
|
86
|
-
providerSessionId: input.providerSessionId,
|
|
87
|
-
started,
|
|
88
|
-
client: null,
|
|
89
|
-
initialized: false,
|
|
90
|
-
sessionEstablished: false,
|
|
91
|
-
inUse: true,
|
|
92
|
-
disposed: false,
|
|
93
|
-
disposePromise: null,
|
|
94
|
-
lastUsedAtMs: Date.now(),
|
|
95
|
-
sessionResponse: null,
|
|
96
|
-
activeHandlers: {
|
|
97
|
-
onSessionUpdate: input.onSessionUpdate,
|
|
98
|
-
onClientRequest: input.onClientRequest
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
entry.client = new AcpClient(started.child, (params, message) => entry.activeHandlers?.onSessionUpdate(params, message), async (method, params, message) => {
|
|
102
|
-
if (!entry.activeHandlers?.onClientRequest)
|
|
103
|
-
throw new Error(`Unsupported ACP client request: ${method}`);
|
|
104
|
-
return entry.activeHandlers.onClientRequest(method, params, message);
|
|
105
|
-
});
|
|
106
|
-
started.child.once("close", () => {
|
|
107
|
-
if (this.runtimes.get(entry.key) === entry)
|
|
108
|
-
this.runtimes.delete(entry.key);
|
|
109
|
-
entry.disposed = true;
|
|
110
|
-
entry.inUse = false;
|
|
111
|
-
entry.activeHandlers = null;
|
|
112
|
-
});
|
|
113
|
-
return entry;
|
|
114
|
-
}
|
|
115
|
-
activateEntry(entry, input) {
|
|
116
|
-
if (entry.inUse) {
|
|
117
|
-
throw new Error(`ACP runtime is already in use: provider=${entry.providerId} session=${entry.providerSessionId ?? "unknown"}`);
|
|
118
|
-
}
|
|
119
|
-
entry.inUse = true;
|
|
120
|
-
entry.activeHandlers = {
|
|
121
|
-
onSessionUpdate: input.onSessionUpdate,
|
|
122
|
-
onClientRequest: input.onClientRequest
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
createLease(entry) {
|
|
126
|
-
let released = false;
|
|
127
|
-
return {
|
|
128
|
-
get client() {
|
|
129
|
-
return entry.client;
|
|
130
|
-
},
|
|
131
|
-
get started() {
|
|
132
|
-
return entry.started;
|
|
133
|
-
},
|
|
134
|
-
get cwd() {
|
|
135
|
-
return entry.cwd;
|
|
136
|
-
},
|
|
137
|
-
get providerSessionId() {
|
|
138
|
-
return entry.providerSessionId;
|
|
139
|
-
},
|
|
140
|
-
get sessionResponse() {
|
|
141
|
-
return entry.sessionResponse;
|
|
142
|
-
},
|
|
143
|
-
get initialized() {
|
|
144
|
-
return entry.initialized;
|
|
145
|
-
},
|
|
146
|
-
get sessionEstablished() {
|
|
147
|
-
return entry.sessionEstablished;
|
|
148
|
-
},
|
|
149
|
-
markInitialized: () => {
|
|
150
|
-
entry.initialized = true;
|
|
151
|
-
},
|
|
152
|
-
setProviderSession: (input) => {
|
|
153
|
-
entry.providerSessionId = input.providerSessionId;
|
|
154
|
-
entry.sessionEstablished = true;
|
|
155
|
-
if ("sessionResponse" in input)
|
|
156
|
-
entry.sessionResponse = input.sessionResponse ?? null;
|
|
157
|
-
this.rekeyEntry(entry, runtimeKey(entry.providerId, entry.cwd, input.providerSessionId));
|
|
158
|
-
},
|
|
159
|
-
setSessionResponse: (sessionResponse) => {
|
|
160
|
-
entry.sessionResponse = sessionResponse;
|
|
161
|
-
},
|
|
162
|
-
release: () => {
|
|
163
|
-
if (released)
|
|
164
|
-
return;
|
|
165
|
-
released = true;
|
|
166
|
-
entry.inUse = false;
|
|
167
|
-
entry.activeHandlers = null;
|
|
168
|
-
entry.lastUsedAtMs = Date.now();
|
|
169
|
-
void this.trimIdleRuntimes();
|
|
170
|
-
},
|
|
171
|
-
dispose: async () => {
|
|
172
|
-
released = true;
|
|
173
|
-
await this.disposeEntry(entry);
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
rekeyEntry(entry, nextKey) {
|
|
178
|
-
if (entry.key === nextKey)
|
|
179
|
-
return;
|
|
180
|
-
const previous = this.runtimes.get(nextKey);
|
|
181
|
-
if (previous && previous !== entry) {
|
|
182
|
-
if (previous.inUse) {
|
|
183
|
-
throw new Error(`ACP runtime key is already active: provider=${previous.providerId} session=${previous.providerSessionId ?? "unknown"}`);
|
|
184
|
-
}
|
|
185
|
-
void this.disposeEntry(previous);
|
|
186
|
-
}
|
|
187
|
-
if (this.runtimes.get(entry.key) === entry)
|
|
188
|
-
this.runtimes.delete(entry.key);
|
|
189
|
-
entry.key = nextKey;
|
|
190
|
-
this.runtimes.set(nextKey, entry);
|
|
191
|
-
}
|
|
192
|
-
async disposeEntry(entry) {
|
|
193
|
-
if (entry.disposePromise)
|
|
194
|
-
return entry.disposePromise;
|
|
195
|
-
if (entry.disposed)
|
|
196
|
-
return;
|
|
197
|
-
const promise = this.performDisposeEntry(entry);
|
|
198
|
-
entry.disposePromise = promise;
|
|
199
|
-
return promise;
|
|
200
|
-
}
|
|
201
|
-
async performDisposeEntry(entry) {
|
|
202
|
-
entry.disposed = true;
|
|
203
|
-
entry.inUse = false;
|
|
204
|
-
entry.activeHandlers = null;
|
|
205
|
-
if (this.runtimes.get(entry.key) === entry)
|
|
206
|
-
this.runtimes.delete(entry.key);
|
|
207
|
-
if (entry.providerSessionId) {
|
|
208
|
-
await entry.client.closeSession(entry.providerSessionId, 5_000).catch(() => void 0);
|
|
209
|
-
}
|
|
210
|
-
await closeAcpProcess(entry.started.child);
|
|
211
|
-
}
|
|
212
|
-
async trimIdleRuntimes() {
|
|
213
|
-
const idle = [...this.runtimes.values()]
|
|
214
|
-
.filter((entry) => !entry.inUse && !entry.disposed)
|
|
215
|
-
.sort((left, right) => left.lastUsedAtMs - right.lastUsedAtMs);
|
|
216
|
-
const excess = Math.max(0, idle.length - this.maxIdleRuntimes);
|
|
217
|
-
for (const entry of idle.slice(0, excess)) {
|
|
218
|
-
await this.disposeEntry(entry).catch(() => void 0);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
isRuntimeAlive(entry) {
|
|
222
|
-
return !entry.disposed && entry.started.child.exitCode === null && entry.started.child.signalCode === null;
|
|
223
|
-
}
|
|
224
|
-
ensureSweep() {
|
|
225
|
-
if (this.sweepTimer)
|
|
226
|
-
return;
|
|
227
|
-
this.sweepTimer = setInterval(() => {
|
|
228
|
-
void this.sweepIdle();
|
|
229
|
-
}, this.sweepIntervalMs);
|
|
230
|
-
this.sweepTimer.unref();
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
export const defaultAcpRuntimePool = new AcpRuntimePool();
|
|
234
|
-
function runtimeKey(providerId, cwd, providerSessionId) {
|
|
235
|
-
return `${providerId}\0${cwd}\0${providerSessionId}`;
|
|
236
|
-
}
|
|
237
|
-
function readRuntimePoolIdleTtlMs() {
|
|
238
|
-
const parsed = Number.parseInt(process.env.ACA_ACP_RUNTIME_IDLE_TTL_MS ?? "", 10);
|
|
239
|
-
return Number.isInteger(parsed) && parsed >= 1_000 ? parsed : DEFAULT_IDLE_TTL_MS;
|
|
240
|
-
}
|
|
241
|
-
function readRuntimePoolSweepIntervalMs() {
|
|
242
|
-
const parsed = Number.parseInt(process.env.ACA_ACP_RUNTIME_SWEEP_INTERVAL_MS ?? "", 10);
|
|
243
|
-
return Number.isInteger(parsed) && parsed >= 1_000 ? parsed : DEFAULT_SWEEP_INTERVAL_MS;
|
|
244
|
-
}
|
|
245
|
-
function readRuntimePoolMaxIdleRuntimes() {
|
|
246
|
-
const parsed = Number.parseInt(process.env.ACA_ACP_RUNTIME_MAX_IDLE ?? "", 10);
|
|
247
|
-
return Number.isInteger(parsed) && parsed >= 0 ? Math.min(parsed, 32) : DEFAULT_MAX_IDLE_RUNTIMES;
|
|
248
|
-
}
|
|
1
|
+
import c from"node:fs";import{randomUUID as p}from"node:crypto";import{AcpClient as u}from"./acp-client.js";import{closeAcpProcess as m,startAcpProcess as h}from"./acp-process.js";const v=300*1e3,I=60*1e3,f=4;class w{runtimes=new Map;idleTtlMs;sweepIntervalMs;maxIdleRuntimes;sweepTimer=null;disposeAllPromise=null;constructor(e={}){this.idleTtlMs=Math.max(1e3,e.idleTtlMs??A()),this.sweepIntervalMs=Math.max(1e3,e.sweepIntervalMs??E()),this.maxIdleRuntimes=Math.max(0,e.maxIdleRuntimes??S())}async acquire(e){this.disposeAllPromise&&await this.disposeAllPromise;const i=c.realpathSync(e.cwd);this.ensureSweep();const s=e.providerSessionId?d(e.providerId,i,e.providerSessionId):null,r=s?this.runtimes.get(s):null;r&&!this.isRuntimeAlive(r)&&await this.disposeEntry(r).catch(()=>{});const n=s?this.runtimes.get(s):null;if(n)return this.activateEntry(n,e),this.createLease(n);const o=e.providerSessionId||`pending:${p()}`,l=d(e.providerId,i,o),a=this.createEntry({...e,cwd:i,key:l,providerSessionId:e.providerSessionId??null});return this.runtimes.set(l,a),this.createLease(a)}disposeAll(){if(this.disposeAllPromise)return this.disposeAllPromise;const e=this.performDisposeAll();return this.disposeAllPromise=e,e.finally(()=>{this.disposeAllPromise===e&&(this.disposeAllPromise=null)}),e}async performDisposeAll(){this.sweepTimer&&(clearInterval(this.sweepTimer),this.sweepTimer=null);const e=[...this.runtimes.values()];await Promise.all(e.map(i=>this.disposeEntry(i).catch(()=>{})))}async sweepIdle(e=Date.now()){const i=[...this.runtimes.values()];for(const s of i)s.inUse||s.disposed||e-s.lastUsedAtMs<this.idleTtlMs||await this.disposeEntry(s).catch(()=>{});await this.trimIdleRuntimes()}stats(){const e=[...this.runtimes.values()].filter(i=>!i.disposed);return{total:e.length,inUse:e.filter(i=>i.inUse).length,idle:e.filter(i=>!i.inUse).length,pids:e.map(i=>i.started.child.pid).filter(i=>Number.isInteger(i))}}createEntry(e){const i=h(e.command,e.args,e.cwd),s={key:e.key,providerId:e.providerId,cwd:e.cwd,providerSessionId:e.providerSessionId,started:i,client:null,initialized:!1,sessionEstablished:!1,inUse:!0,disposed:!1,disposePromise:null,lastUsedAtMs:Date.now(),sessionResponse:null,activeHandlers:{onSessionUpdate:e.onSessionUpdate,onClientRequest:e.onClientRequest}};return s.client=new u(i.child,(r,n)=>s.activeHandlers?.onSessionUpdate(r,n),async(r,n,o)=>{if(!s.activeHandlers?.onClientRequest)throw new Error(`Unsupported ACP client request: ${r}`);return s.activeHandlers.onClientRequest(r,n,o)}),i.child.once("close",()=>{this.runtimes.get(s.key)===s&&this.runtimes.delete(s.key),s.disposed=!0,s.inUse=!1,s.activeHandlers=null}),s}activateEntry(e,i){if(e.inUse)throw new Error(`ACP runtime is already in use: provider=${e.providerId} session=${e.providerSessionId??"unknown"}`);e.inUse=!0,e.activeHandlers={onSessionUpdate:i.onSessionUpdate,onClientRequest:i.onClientRequest}}createLease(e){let i=!1;return{get client(){return e.client},get started(){return e.started},get cwd(){return e.cwd},get providerSessionId(){return e.providerSessionId},get sessionResponse(){return e.sessionResponse},get initialized(){return e.initialized},get sessionEstablished(){return e.sessionEstablished},markInitialized:()=>{e.initialized=!0},setProviderSession:s=>{e.providerSessionId=s.providerSessionId,e.sessionEstablished=!0,"sessionResponse"in s&&(e.sessionResponse=s.sessionResponse??null),this.rekeyEntry(e,d(e.providerId,e.cwd,s.providerSessionId))},setSessionResponse:s=>{e.sessionResponse=s},release:()=>{i||(i=!0,e.inUse=!1,e.activeHandlers=null,e.lastUsedAtMs=Date.now(),this.trimIdleRuntimes())},dispose:async()=>{i=!0,await this.disposeEntry(e)}}}rekeyEntry(e,i){if(e.key===i)return;const s=this.runtimes.get(i);if(s&&s!==e){if(s.inUse)throw new Error(`ACP runtime key is already active: provider=${s.providerId} session=${s.providerSessionId??"unknown"}`);this.disposeEntry(s)}this.runtimes.get(e.key)===e&&this.runtimes.delete(e.key),e.key=i,this.runtimes.set(i,e)}async disposeEntry(e){if(e.disposePromise)return e.disposePromise;if(e.disposed)return;const i=this.performDisposeEntry(e);return e.disposePromise=i,i}async performDisposeEntry(e){e.disposed=!0,e.inUse=!1,e.activeHandlers=null,this.runtimes.get(e.key)===e&&this.runtimes.delete(e.key),e.providerSessionId&&await e.client.closeSession(e.providerSessionId,5e3).catch(()=>{}),await m(e.started.child)}async trimIdleRuntimes(){const e=[...this.runtimes.values()].filter(s=>!s.inUse&&!s.disposed).sort((s,r)=>s.lastUsedAtMs-r.lastUsedAtMs),i=Math.max(0,e.length-this.maxIdleRuntimes);for(const s of e.slice(0,i))await this.disposeEntry(s).catch(()=>{})}isRuntimeAlive(e){return!e.disposed&&e.started.child.exitCode===null&&e.started.child.signalCode===null}ensureSweep(){this.sweepTimer||(this.sweepTimer=setInterval(()=>{this.sweepIdle()},this.sweepIntervalMs),this.sweepTimer.unref())}}const _=new w;function d(t,e,i){return`${t}\0${e}\0${i}`}function A(){const t=Number.parseInt(process.env.ACA_ACP_RUNTIME_IDLE_TTL_MS??"",10);return Number.isInteger(t)&&t>=1e3?t:v}function E(){const t=Number.parseInt(process.env.ACA_ACP_RUNTIME_SWEEP_INTERVAL_MS??"",10);return Number.isInteger(t)&&t>=1e3?t:I}function S(){const t=Number.parseInt(process.env.ACA_ACP_RUNTIME_MAX_IDLE??"",10);return Number.isInteger(t)&&t>=0?Math.min(t,32):f}export{w as AcpRuntimePool,_ as defaultAcpRuntimePool};
|
|
@@ -1,29 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
/**
|
|
3
|
-
* Converts the standard ACP UsageUpdate shape into ACA's persisted session
|
|
4
|
-
* usage shape. ACP defines `used` as tokens currently in context and `size`
|
|
5
|
-
* as the model context-window size. Do not derive this from message length.
|
|
6
|
-
*/
|
|
7
|
-
export function contextUsageFromAcpSessionUpdate(params, fallbackContextWindow) {
|
|
8
|
-
const parsed = acpSessionUpdateFromParams(params);
|
|
9
|
-
if (!parsed || parsed.update.sessionUpdate !== "usage_update")
|
|
10
|
-
return null;
|
|
11
|
-
const usedTokens = nonNegativeNumber(parsed.update.used);
|
|
12
|
-
const reportedWindow = nonNegativeNumber(parsed.update.size);
|
|
13
|
-
const contextWindow = reportedWindow || nonNegativeNumber(fallbackContextWindow);
|
|
14
|
-
if (usedTokens <= 0 || contextWindow <= 0)
|
|
15
|
-
return null;
|
|
16
|
-
return {
|
|
17
|
-
usedTokens,
|
|
18
|
-
// ACP's standard update intentionally has no input-token breakdown.
|
|
19
|
-
inputTokens: usedTokens,
|
|
20
|
-
contextWindow,
|
|
21
|
-
ratio: Math.min(1, usedTokens / contextWindow),
|
|
22
|
-
observedAtMs: Date.now(),
|
|
23
|
-
state: "active",
|
|
24
|
-
maintenanceAction: null
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
function nonNegativeNumber(value) {
|
|
28
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
29
|
-
}
|
|
1
|
+
import{acpSessionUpdateFromParams as i}from"./acp-events.js";function d(e,s){const t=i(e);if(!t||t.update.sessionUpdate!=="usage_update")return null;const n=r(t.update.used),o=r(t.update.size)||r(s);return n<=0||o<=0?null:{usedTokens:n,inputTokens:n,contextWindow:o,ratio:Math.min(1,n/o),observedAtMs:Date.now(),state:"active",maintenanceAction:null}}function r(e){return typeof e=="number"&&Number.isFinite(e)&&e>=0?e:0}export{d as contextUsageFromAcpSessionUpdate};
|