@messenger-agent/claude-agent 0.24.0-alpha.2 → 0.24.0-alpha.4
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/app.js +1 -15
- package/dist/claude.js +1 -1
- package/dist/config.js +1 -147
- package/dist/index.js +1 -19
- package/dist/input-queue.js +1 -40
- package/dist/live-session.js +1 -225
- package/dist/llm-proxy-bindings.js +3 -37
- package/dist/managed-task-mcp.js +1 -41
- package/dist/pending-asks.js +5 -246
- package/dist/platform-instructions.js +6 -42
- package/dist/routes/chat.js +8 -1229
- package/dist/schemas.js +1 -89
- package/dist/session-manager.js +1 -57
- package/dist/task-aggregator.js +1 -143
- package/dist/token-usage.js +1 -75
- package/dist/tunnel-client.js +1 -223
- package/dist/workspace-files.js +1 -56
- package/package.json +2 -2
package/dist/app.js
CHANGED
|
@@ -1,15 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { logger as honoLogger } from "hono/logger";
|
|
3
|
-
import chat from "./routes/chat.js";
|
|
4
|
-
import { logger } from "@messenger-agent/shared/logger";
|
|
5
|
-
import { agentAuthMiddleware } from "@messenger-agent/shared/agent-auth";
|
|
6
|
-
import { appConfig } from "./config.js";
|
|
7
|
-
import { installAgentActivityResponder } from "@messenger-agent/shared/agent-activity";
|
|
8
|
-
import { claudeActivitySnapshot } from "./routes/chat.js";
|
|
9
|
-
const app = new Hono();
|
|
10
|
-
installAgentActivityResponder("claude", claudeActivitySnapshot);
|
|
11
|
-
app.use(honoLogger((str, ...rest) => logger.info(str, ...rest)));
|
|
12
|
-
app.get("/health", (c) => c.json({ status: "ok", agent: "claude" }));
|
|
13
|
-
app.use("/*", agentAuthMiddleware(appConfig.authTokens));
|
|
14
|
-
app.route("/", chat);
|
|
15
|
-
export default app;
|
|
1
|
+
import{Hono as e}from"hono";import{logger as a}from"hono/logger";import i from"./routes/chat.js";import{logger as m}from"@messenger-agent/shared/logger";import{agentAuthMiddleware as p}from"@messenger-agent/shared/agent-auth";import{appConfig as n}from"./config.js";import{installAgentActivityResponder as f}from"@messenger-agent/shared/agent-activity";import{claudeActivitySnapshot as g}from"./routes/chat.js";const o=new e;f("claude",g),o.use(a((t,...r)=>m.info(t,...r))),o.get("/health",t=>t.json({status:"ok",agent:"claude"})),o.use("/*",p(n.authTokens)),o.route("/",i);var v=o;export{v as default};
|
package/dist/claude.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import{query as o}from"@anthropic-ai/claude-agent-sdk";export{o as query};
|
package/dist/config.js
CHANGED
|
@@ -1,147 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { parse } from "yaml";
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
import { normalizeOptional, parseAgentAuthConfig, parseAgentWorkspacesConfig, parseGitInitConfig, } from "@messenger-agent/shared/agent-config";
|
|
7
|
-
import { logger, normalizeLogLevel, setLogFile, setLogLevel } from "@messenger-agent/shared/logger";
|
|
8
|
-
const defaultTunnelServerUrl = "wss://m.elevo.vip/agent-bridge/tunnel";
|
|
9
|
-
const defaultClaudeBaseUrl = "https://m.elevo.vip/agent-bridge/llm";
|
|
10
|
-
const RawConfigSchema = z
|
|
11
|
-
.object({
|
|
12
|
-
log_level: z.string().optional(),
|
|
13
|
-
port: z.coerce.number().int().positive().optional(),
|
|
14
|
-
auth_tokens: z.record(z.string(), z.string()).optional(),
|
|
15
|
-
workspaces: z
|
|
16
|
-
.array(z.object({
|
|
17
|
-
id: z.string().optional(),
|
|
18
|
-
name: z.string().optional(),
|
|
19
|
-
path: z.string().optional(),
|
|
20
|
-
}))
|
|
21
|
-
.optional(),
|
|
22
|
-
file_uploads: z
|
|
23
|
-
.object({
|
|
24
|
-
temp_dir: z.string().optional(),
|
|
25
|
-
})
|
|
26
|
-
.optional(),
|
|
27
|
-
data_dir: z.string().optional(),
|
|
28
|
-
claude: z
|
|
29
|
-
.object({
|
|
30
|
-
port: z.coerce.number().int().positive().optional(),
|
|
31
|
-
data_dir: z.string().optional(),
|
|
32
|
-
file_uploads: z.object({ temp_dir: z.string().optional() }).optional(),
|
|
33
|
-
api_key: z.string().optional(),
|
|
34
|
-
base_url: z.string().optional(),
|
|
35
|
-
default_model: z.string().optional(),
|
|
36
|
-
max_turns: z.coerce.number().int().positive().optional(),
|
|
37
|
-
ask_timeout_ms: z.coerce.number().int().positive().optional(),
|
|
38
|
-
session_idle_timeout_ms: z.coerce.number().int().positive().optional(),
|
|
39
|
-
})
|
|
40
|
-
.optional(),
|
|
41
|
-
gitlab: z
|
|
42
|
-
.object({
|
|
43
|
-
ca_cert_path: z.string().optional(),
|
|
44
|
-
host: z.string().optional(),
|
|
45
|
-
})
|
|
46
|
-
.optional(),
|
|
47
|
-
git: z
|
|
48
|
-
.object({
|
|
49
|
-
co_authors: z.array(z.string()).optional(),
|
|
50
|
-
})
|
|
51
|
-
.optional(),
|
|
52
|
-
tunnel: z
|
|
53
|
-
.object({
|
|
54
|
-
enabled: z.boolean().optional(),
|
|
55
|
-
server_url: z.string().optional(),
|
|
56
|
-
tunnel_id: z.string().optional(),
|
|
57
|
-
token: z.string().optional(),
|
|
58
|
-
reconnect_initial_ms: z.number().int().positive().optional(),
|
|
59
|
-
reconnect_max_ms: z.number().int().positive().optional(),
|
|
60
|
-
heartbeat_interval_ms: z.number().int().positive().optional(),
|
|
61
|
-
})
|
|
62
|
-
.optional(),
|
|
63
|
-
})
|
|
64
|
-
.loose();
|
|
65
|
-
function loadConfig() {
|
|
66
|
-
const configPath = process.env.AGENT_CONFIG_PATH ?? "./agent-config.yaml";
|
|
67
|
-
if (!existsSync(configPath)) {
|
|
68
|
-
const dataDir = "/data";
|
|
69
|
-
const logFile = join(dataDir, "logs", "claude-agent.log");
|
|
70
|
-
setLogLevel("info");
|
|
71
|
-
setLogFile(logFile);
|
|
72
|
-
logger.warn(`Config file not found at ${configPath}`);
|
|
73
|
-
return {
|
|
74
|
-
configPath,
|
|
75
|
-
logLevel: "info",
|
|
76
|
-
logFile,
|
|
77
|
-
port: 3000,
|
|
78
|
-
fileUploads: { tempDir: join(tmpdir(), "claude-agent-uploads") },
|
|
79
|
-
dataDir,
|
|
80
|
-
claude: { askTimeoutMs: 43_200_000, sessionIdleTimeoutMs: 3_600_000 },
|
|
81
|
-
tunnel: {
|
|
82
|
-
enabled: false,
|
|
83
|
-
reconnectInitialMs: 1000,
|
|
84
|
-
reconnectMaxMs: 30000,
|
|
85
|
-
heartbeatIntervalMs: 30000,
|
|
86
|
-
},
|
|
87
|
-
...parseAgentAuthConfig({}),
|
|
88
|
-
...parseAgentWorkspacesConfig({}),
|
|
89
|
-
...parseGitInitConfig({}),
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
let parsedYaml;
|
|
93
|
-
try {
|
|
94
|
-
parsedYaml = parse(readFileSync(configPath, "utf-8"));
|
|
95
|
-
}
|
|
96
|
-
catch (err) {
|
|
97
|
-
logger.error(`Failed to parse config file at ${configPath}:`, err);
|
|
98
|
-
parsedYaml = {};
|
|
99
|
-
}
|
|
100
|
-
const raw = RawConfigSchema.safeParse(parsedYaml ?? {});
|
|
101
|
-
if (!raw.success) {
|
|
102
|
-
logger.error(`Invalid config format at ${configPath}:`, raw.error.issues);
|
|
103
|
-
}
|
|
104
|
-
const data = raw.success ? raw.data : {};
|
|
105
|
-
const logLevel = normalizeLogLevel(data.log_level);
|
|
106
|
-
const dataDir = normalizeOptional(data.claude?.data_dir) ?? normalizeOptional(data.data_dir) ?? "/data";
|
|
107
|
-
const logFile = join(dataDir, "logs", "claude-agent.log");
|
|
108
|
-
const tunnelEnabled = data.tunnel?.enabled ?? false;
|
|
109
|
-
const tunnelToken = normalizeOptional(data.tunnel?.token);
|
|
110
|
-
setLogLevel(logLevel);
|
|
111
|
-
setLogFile(logFile);
|
|
112
|
-
return {
|
|
113
|
-
configPath,
|
|
114
|
-
logLevel,
|
|
115
|
-
logFile,
|
|
116
|
-
port: data.claude?.port ?? data.port ?? 3000,
|
|
117
|
-
fileUploads: {
|
|
118
|
-
tempDir: normalizeOptional(data.claude?.file_uploads?.temp_dir) ??
|
|
119
|
-
normalizeOptional(data.file_uploads?.temp_dir) ??
|
|
120
|
-
join(tmpdir(), "claude-agent-uploads"),
|
|
121
|
-
},
|
|
122
|
-
dataDir,
|
|
123
|
-
claude: {
|
|
124
|
-
apiKey: normalizeOptional(data.claude?.api_key) ?? (tunnelEnabled ? tunnelToken : undefined),
|
|
125
|
-
baseUrl: normalizeOptional(data.claude?.base_url) ?? (tunnelEnabled ? defaultClaudeBaseUrl : undefined),
|
|
126
|
-
defaultModel: normalizeOptional(data.claude?.default_model),
|
|
127
|
-
maxTurns: data.claude?.max_turns,
|
|
128
|
-
askTimeoutMs: Number.parseInt(process.env.CLAUDE_ASK_TIMEOUT_MS ?? "", 10) || data.claude?.ask_timeout_ms || 43_200_000,
|
|
129
|
-
sessionIdleTimeoutMs: Number.parseInt(process.env.CLAUDE_SESSION_IDLE_TIMEOUT_MS ?? "", 10) ||
|
|
130
|
-
data.claude?.session_idle_timeout_ms ||
|
|
131
|
-
3_600_000,
|
|
132
|
-
},
|
|
133
|
-
tunnel: {
|
|
134
|
-
enabled: tunnelEnabled,
|
|
135
|
-
serverUrl: normalizeOptional(data.tunnel?.server_url) ?? (tunnelEnabled ? defaultTunnelServerUrl : undefined),
|
|
136
|
-
tunnelId: normalizeOptional(data.tunnel?.tunnel_id),
|
|
137
|
-
token: tunnelToken,
|
|
138
|
-
reconnectInitialMs: data.tunnel?.reconnect_initial_ms ?? 1000,
|
|
139
|
-
reconnectMaxMs: data.tunnel?.reconnect_max_ms ?? 30000,
|
|
140
|
-
heartbeatIntervalMs: data.tunnel?.heartbeat_interval_ms ?? 30000,
|
|
141
|
-
},
|
|
142
|
-
...parseAgentAuthConfig(data),
|
|
143
|
-
...parseAgentWorkspacesConfig(data),
|
|
144
|
-
...parseGitInitConfig(data),
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
export const appConfig = loadConfig();
|
|
1
|
+
import{existsSync as h,readFileSync as k}from"node:fs";import{tmpdir as g}from"node:os";import{join as r}from"node:path";import{parse as T}from"yaml";import{z as e}from"zod";import{normalizeOptional as o,parseAgentAuthConfig as f,parseAgentWorkspacesConfig as b,parseGitInitConfig as v}from"@messenger-agent/shared/agent-config";import{logger as p,normalizeLogLevel as C,setLogFile as M,setLogLevel as I}from"@messenger-agent/shared/logger";const L="wss://m.elevo.vip/agent-bridge/tunnel",S="https://m.elevo.vip/agent-bridge/llm",U=e.object({log_level:e.string().optional(),port:e.coerce.number().int().positive().optional(),auth_tokens:e.record(e.string(),e.string()).optional(),workspaces:e.array(e.object({id:e.string().optional(),name:e.string().optional(),path:e.string().optional()})).optional(),file_uploads:e.object({temp_dir:e.string().optional()}).optional(),data_dir:e.string().optional(),claude:e.object({port:e.coerce.number().int().positive().optional(),data_dir:e.string().optional(),file_uploads:e.object({temp_dir:e.string().optional()}).optional(),api_key:e.string().optional(),base_url:e.string().optional(),default_model:e.string().optional(),max_turns:e.coerce.number().int().positive().optional(),ask_timeout_ms:e.coerce.number().int().positive().optional(),session_idle_timeout_ms:e.coerce.number().int().positive().optional()}).optional(),gitlab:e.object({ca_cert_path:e.string().optional(),host:e.string().optional()}).optional(),git:e.object({co_authors:e.array(e.string()).optional()}).optional(),tunnel:e.object({enabled:e.boolean().optional(),server_url:e.string().optional(),tunnel_id:e.string().optional(),token:e.string().optional(),reconnect_initial_ms:e.number().int().positive().optional(),reconnect_max_ms:e.number().int().positive().optional(),heartbeat_interval_ms:e.number().int().positive().optional()}).optional()}).loose();function j(){const n=process.env.AGENT_CONFIG_PATH??"./agent-config.yaml";if(!h(n)){const l="/data",m=r(l,"logs","claude-agent.log");return I("info"),M(m),p.warn(`Config file not found at ${n}`),{configPath:n,logLevel:"info",logFile:m,port:3e3,fileUploads:{tempDir:r(g(),"claude-agent-uploads")},dataDir:l,claude:{askTimeoutMs:432e5,sessionIdleTimeoutMs:36e5},tunnel:{enabled:!1,reconnectInitialMs:1e3,reconnectMaxMs:3e4,heartbeatIntervalMs:3e4},...f({}),...b({}),...v({})}}let s;try{s=T(k(n,"utf-8"))}catch(l){p.error(`Failed to parse config file at ${n}:`,l),s={}}const a=U.safeParse(s??{});a.success||p.error(`Invalid config format at ${n}:`,a.error.issues);const t=a.success?a.data:{},c=C(t.log_level),u=o(t.claude?.data_dir)??o(t.data_dir)??"/data",d=r(u,"logs","claude-agent.log"),i=t.tunnel?.enabled??!1,_=o(t.tunnel?.token);return I(c),M(d),{configPath:n,logLevel:c,logFile:d,port:t.claude?.port??t.port??3e3,fileUploads:{tempDir:o(t.claude?.file_uploads?.temp_dir)??o(t.file_uploads?.temp_dir)??r(g(),"claude-agent-uploads")},dataDir:u,claude:{apiKey:o(t.claude?.api_key)??(i?_:void 0),baseUrl:o(t.claude?.base_url)??(i?S:void 0),defaultModel:o(t.claude?.default_model),maxTurns:t.claude?.max_turns,askTimeoutMs:Number.parseInt(process.env.CLAUDE_ASK_TIMEOUT_MS??"",10)||t.claude?.ask_timeout_ms||432e5,sessionIdleTimeoutMs:Number.parseInt(process.env.CLAUDE_SESSION_IDLE_TIMEOUT_MS??"",10)||t.claude?.session_idle_timeout_ms||36e5},tunnel:{enabled:i,serverUrl:o(t.tunnel?.server_url)??(i?L:void 0),tunnelId:o(t.tunnel?.tunnel_id),token:_,reconnectInitialMs:t.tunnel?.reconnect_initial_ms??1e3,reconnectMaxMs:t.tunnel?.reconnect_max_ms??3e4,heartbeatIntervalMs:t.tunnel?.heartbeat_interval_ms??3e4},...f(t),...b(t),...v(t)}}const N=j();export{N as appConfig};
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import app from "./app.js";
|
|
3
|
-
import { appConfig } from "./config.js";
|
|
4
|
-
import { initGit } from "@messenger-agent/shared/git-init";
|
|
5
|
-
import { logger } from "@messenger-agent/shared/logger";
|
|
6
|
-
import { startClaudeTunnelClient } from "./tunnel-client.js";
|
|
7
|
-
const port = appConfig.port;
|
|
8
|
-
try {
|
|
9
|
-
await initGit(appConfig);
|
|
10
|
-
}
|
|
11
|
-
catch (err) {
|
|
12
|
-
logger.warn("Git initialization failed; continuing agent startup:", err);
|
|
13
|
-
}
|
|
14
|
-
const tunnelClient = startClaudeTunnelClient(appConfig.tunnel);
|
|
15
|
-
if (!tunnelClient) {
|
|
16
|
-
serve({ fetch: app.fetch, port }, () => {
|
|
17
|
-
logger.info(`claude-agent listening on http://0.0.0.0:${port}`);
|
|
18
|
-
});
|
|
19
|
-
}
|
|
1
|
+
import{serve as r}from"@hono/node-server";import e from"./app.js";import{appConfig as t}from"./config.js";import{initGit as a}from"@messenger-agent/shared/git-init";import{logger as i}from"@messenger-agent/shared/logger";import{startClaudeTunnelClient as p}from"./tunnel-client.js";const n=t.port;try{await a(t)}catch(o){i.warn("Git initialization failed; continuing agent startup:",o)}const f=p(t.tunnel);f||r({fetch:e.fetch,port:n},()=>{i.info(`claude-agent listening on http://0.0.0.0:${n}`)});
|
package/dist/input-queue.js
CHANGED
|
@@ -1,40 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const pending = [];
|
|
3
|
-
let closed = false;
|
|
4
|
-
let wake;
|
|
5
|
-
const wakeUp = () => {
|
|
6
|
-
const current = wake;
|
|
7
|
-
wake = undefined;
|
|
8
|
-
current?.();
|
|
9
|
-
};
|
|
10
|
-
return {
|
|
11
|
-
iterable: {
|
|
12
|
-
async *[Symbol.asyncIterator]() {
|
|
13
|
-
while (true) {
|
|
14
|
-
while (pending.length > 0)
|
|
15
|
-
yield pending.shift();
|
|
16
|
-
if (closed)
|
|
17
|
-
return;
|
|
18
|
-
await new Promise((resolve) => {
|
|
19
|
-
wake = resolve;
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
push(message) {
|
|
25
|
-
if (closed)
|
|
26
|
-
throw new Error("Claude input queue is closed");
|
|
27
|
-
pending.push(message);
|
|
28
|
-
wakeUp();
|
|
29
|
-
},
|
|
30
|
-
close() {
|
|
31
|
-
if (closed)
|
|
32
|
-
return;
|
|
33
|
-
closed = true;
|
|
34
|
-
wakeUp();
|
|
35
|
-
},
|
|
36
|
-
get closed() {
|
|
37
|
-
return closed;
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
1
|
+
function i(){const r=[];let e=!1,n;const u=()=>{const t=n;n=void 0,t?.()};return{iterable:{async*[Symbol.asyncIterator](){for(;;){for(;r.length>0;)yield r.shift();if(e)return;await new Promise(t=>{n=t})}}},push(t){if(e)throw new Error("Claude input queue is closed");r.push(t),u()},close(){e||(e=!0,u())},get closed(){return e}}}export{i as createInputQueue};
|
package/dist/live-session.js
CHANGED
|
@@ -1,225 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { query } from "./claude.js";
|
|
3
|
-
import { createInputQueue } from "./input-queue.js";
|
|
4
|
-
function userMessage(prompt, priority) {
|
|
5
|
-
return {
|
|
6
|
-
type: "user",
|
|
7
|
-
parent_tool_use_id: null,
|
|
8
|
-
message: { role: "user", content: prompt },
|
|
9
|
-
origin: { kind: "human" },
|
|
10
|
-
...(priority ? { priority } : null),
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
function logStreamMessage(message) {
|
|
14
|
-
if (message.type === "stream_event" && message.event.type === "content_block_delta")
|
|
15
|
-
return;
|
|
16
|
-
if (message.type === "system" && message.subtype === "thinking_tokens")
|
|
17
|
-
return;
|
|
18
|
-
logger.debug("Received chat stream event:", message);
|
|
19
|
-
}
|
|
20
|
-
export class LiveSession {
|
|
21
|
-
workdir;
|
|
22
|
-
managedTaskContext;
|
|
23
|
-
llmProxyBinding;
|
|
24
|
-
inputQueue = createInputQueue();
|
|
25
|
-
abortController = new AbortController();
|
|
26
|
-
sdkQuery;
|
|
27
|
-
idleTimeoutMs;
|
|
28
|
-
onRecycle;
|
|
29
|
-
currentTurn;
|
|
30
|
-
transition = Promise.resolve();
|
|
31
|
-
idleTimer;
|
|
32
|
-
pumpStarted = false;
|
|
33
|
-
dead = false;
|
|
34
|
-
cancelIntent = false;
|
|
35
|
-
generation = 0;
|
|
36
|
-
_conversationId;
|
|
37
|
-
_model;
|
|
38
|
-
_agentMode;
|
|
39
|
-
constructor(init) {
|
|
40
|
-
this._conversationId = init.conversationId;
|
|
41
|
-
this.workdir = init.workdir;
|
|
42
|
-
this._model = init.model;
|
|
43
|
-
this._agentMode = init.agentMode;
|
|
44
|
-
this.managedTaskContext = init.managedTaskContext;
|
|
45
|
-
this.llmProxyBinding = init.llmProxyBinding;
|
|
46
|
-
this.idleTimeoutMs = init.idleTimeoutMs;
|
|
47
|
-
this.onRecycle = init.onRecycle;
|
|
48
|
-
const canUseTool = (...args) => {
|
|
49
|
-
const turn = this.currentTurn;
|
|
50
|
-
if (!turn || turn.settled || this.cancelIntent) {
|
|
51
|
-
return Promise.resolve({ behavior: "deny", message: "Claude conversation is no longer active" });
|
|
52
|
-
}
|
|
53
|
-
return turn.permissionHandler(...args);
|
|
54
|
-
};
|
|
55
|
-
this.sdkQuery = query({
|
|
56
|
-
prompt: this.inputQueue.iterable,
|
|
57
|
-
options: init.buildOptions(this.abortController, canUseTool, this.managedTaskContext),
|
|
58
|
-
});
|
|
59
|
-
this.resetIdleTimer();
|
|
60
|
-
}
|
|
61
|
-
get conversationId() {
|
|
62
|
-
return this._conversationId;
|
|
63
|
-
}
|
|
64
|
-
get model() {
|
|
65
|
-
return this._model;
|
|
66
|
-
}
|
|
67
|
-
get agentMode() {
|
|
68
|
-
return this._agentMode;
|
|
69
|
-
}
|
|
70
|
-
get isDead() {
|
|
71
|
-
return this.dead;
|
|
72
|
-
}
|
|
73
|
-
get wasCancelled() {
|
|
74
|
-
return this.cancelIntent;
|
|
75
|
-
}
|
|
76
|
-
get currentGeneration() {
|
|
77
|
-
return this.generation;
|
|
78
|
-
}
|
|
79
|
-
hasActiveOwner() {
|
|
80
|
-
return Boolean(this.currentTurn && !this.currentTurn.settled);
|
|
81
|
-
}
|
|
82
|
-
bindConversation(id) {
|
|
83
|
-
this._conversationId = id;
|
|
84
|
-
}
|
|
85
|
-
updateManagedTaskContext(context) {
|
|
86
|
-
for (const key of Object.keys(this.managedTaskContext)) {
|
|
87
|
-
delete this.managedTaskContext[key];
|
|
88
|
-
}
|
|
89
|
-
Object.assign(this.managedTaskContext, context);
|
|
90
|
-
}
|
|
91
|
-
reserveTurn(prompt, handler, permissionHandler, settings) {
|
|
92
|
-
return this.withTransition(async () => {
|
|
93
|
-
if (this.dead || this.cancelIntent)
|
|
94
|
-
throw new Error(`Claude session ${this.conversationId} is closed`);
|
|
95
|
-
this.clearIdleTimer();
|
|
96
|
-
this.updateManagedTaskContext(settings.managedTaskContext);
|
|
97
|
-
if (this.hasActiveOwner()) {
|
|
98
|
-
this.inputQueue.push(userMessage(prompt, "next"));
|
|
99
|
-
return { kind: "interjection", generation: this.generation };
|
|
100
|
-
}
|
|
101
|
-
if (settings.model !== this._model) {
|
|
102
|
-
await this.sdkQuery.setModel?.(settings.model);
|
|
103
|
-
this._model = settings.model;
|
|
104
|
-
}
|
|
105
|
-
if (settings.agentMode !== this._agentMode) {
|
|
106
|
-
await this.sdkQuery.setPermissionMode?.(settings.agentMode === "plan" ? "plan" : "bypassPermissions");
|
|
107
|
-
this._agentMode = settings.agentMode;
|
|
108
|
-
}
|
|
109
|
-
const generation = ++this.generation;
|
|
110
|
-
let resolve;
|
|
111
|
-
let reject;
|
|
112
|
-
const done = new Promise((res, rej) => {
|
|
113
|
-
resolve = res;
|
|
114
|
-
reject = rej;
|
|
115
|
-
});
|
|
116
|
-
this.currentTurn = { generation, handler, permissionHandler, resolve, reject, settled: false };
|
|
117
|
-
if (!this.pumpStarted) {
|
|
118
|
-
this.pumpStarted = true;
|
|
119
|
-
void this.pump();
|
|
120
|
-
}
|
|
121
|
-
this.inputQueue.push(userMessage(prompt));
|
|
122
|
-
return { kind: "owner", generation, done };
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
async setModel(model) {
|
|
126
|
-
await this.withTransition(async () => {
|
|
127
|
-
if (model === this._model || this.dead || this.cancelIntent)
|
|
128
|
-
return;
|
|
129
|
-
await this.sdkQuery.setModel?.(model);
|
|
130
|
-
this._model = model;
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
async setPermissionMode(mode) {
|
|
134
|
-
await this.withTransition(async () => {
|
|
135
|
-
if (this.dead || this.cancelIntent)
|
|
136
|
-
return;
|
|
137
|
-
await this.sdkQuery.setPermissionMode?.(mode);
|
|
138
|
-
this._agentMode = mode === "plan" ? "plan" : "default";
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
abort() {
|
|
142
|
-
this.cancelIntent = true;
|
|
143
|
-
void this.withTransition(async () => {
|
|
144
|
-
if (this.dead)
|
|
145
|
-
return;
|
|
146
|
-
this.dead = true;
|
|
147
|
-
this.clearIdleTimer();
|
|
148
|
-
this.inputQueue.close();
|
|
149
|
-
this.abortController.abort();
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
withTransition(operation) {
|
|
153
|
-
const result = this.transition.then(operation, operation);
|
|
154
|
-
this.transition = result.then(() => undefined, () => undefined);
|
|
155
|
-
return result;
|
|
156
|
-
}
|
|
157
|
-
async pump() {
|
|
158
|
-
try {
|
|
159
|
-
for await (const message of this.sdkQuery) {
|
|
160
|
-
logStreamMessage(message);
|
|
161
|
-
const turn = this.currentTurn;
|
|
162
|
-
if (!turn || turn.settled)
|
|
163
|
-
continue;
|
|
164
|
-
const ended = await turn.handler(message);
|
|
165
|
-
if (ended)
|
|
166
|
-
this.settleTurn(turn, undefined);
|
|
167
|
-
}
|
|
168
|
-
this.dead = true;
|
|
169
|
-
const turn = this.currentTurn;
|
|
170
|
-
if (turn && !turn.settled)
|
|
171
|
-
this.settleTurn(turn, undefined);
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
this.dead = true;
|
|
175
|
-
const turn = this.currentTurn;
|
|
176
|
-
if (turn && !turn.settled)
|
|
177
|
-
this.settleTurn(turn, error);
|
|
178
|
-
else if (!this.cancelIntent)
|
|
179
|
-
logger.error(`Claude session ${this.conversationId} pump failed:`, error);
|
|
180
|
-
}
|
|
181
|
-
finally {
|
|
182
|
-
this.clearIdleTimer();
|
|
183
|
-
this.onRecycle(this, this.generation);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
settleTurn(turn, error) {
|
|
187
|
-
if (turn.settled)
|
|
188
|
-
return;
|
|
189
|
-
turn.settled = true;
|
|
190
|
-
if (this.currentTurn === turn)
|
|
191
|
-
this.currentTurn = undefined;
|
|
192
|
-
if (!this.dead && !this.cancelIntent)
|
|
193
|
-
this.resetIdleTimer();
|
|
194
|
-
if (error === undefined)
|
|
195
|
-
turn.resolve();
|
|
196
|
-
else
|
|
197
|
-
turn.reject(error);
|
|
198
|
-
}
|
|
199
|
-
resetIdleTimer() {
|
|
200
|
-
this.clearIdleTimer();
|
|
201
|
-
if (this.dead || this.cancelIntent)
|
|
202
|
-
return;
|
|
203
|
-
this.idleTimer = setTimeout(() => {
|
|
204
|
-
void this.withTransition(async () => {
|
|
205
|
-
if (this.dead || this.cancelIntent || this.hasActiveOwner()) {
|
|
206
|
-
if (!this.dead && !this.cancelIntent)
|
|
207
|
-
this.resetIdleTimer();
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
logger.info(`Recycling idle Claude session ${this.conversationId}`);
|
|
211
|
-
this.dead = true;
|
|
212
|
-
this.inputQueue.close();
|
|
213
|
-
this.sdkQuery.close?.();
|
|
214
|
-
this.onRecycle(this, this.generation);
|
|
215
|
-
});
|
|
216
|
-
}, this.idleTimeoutMs);
|
|
217
|
-
this.idleTimer.unref?.();
|
|
218
|
-
}
|
|
219
|
-
clearIdleTimer() {
|
|
220
|
-
if (!this.idleTimer)
|
|
221
|
-
return;
|
|
222
|
-
clearTimeout(this.idleTimer);
|
|
223
|
-
this.idleTimer = undefined;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
1
|
+
import{logger as r}from"@messenger-agent/shared/logger";import{query as m}from"./claude.js";import{createInputQueue as T}from"./input-queue.js";function l(i,e){return{type:"user",parent_tool_use_id:null,message:{role:"user",content:i},origin:{kind:"human"},...e?{priority:e}:null}}function f(i){i.type==="stream_event"&&i.event.type==="content_block_delta"||i.type==="system"&&i.subtype==="thinking_tokens"||r.debug("Received chat stream event:",i)}class I{workdir;managedTaskContext;llmProxyBinding;inputQueue=T();abortController=new AbortController;sdkQuery;idleTimeoutMs;onRecycle;currentTurn;transition=Promise.resolve();idleTimer;pumpStarted=!1;dead=!1;cancelIntent=!1;generation=0;_conversationId;_model;_agentMode;constructor(e){this._conversationId=e.conversationId,this.workdir=e.workdir,this._model=e.model,this._agentMode=e.agentMode,this.managedTaskContext=e.managedTaskContext,this.llmProxyBinding=e.llmProxyBinding,this.idleTimeoutMs=e.idleTimeoutMs,this.onRecycle=e.onRecycle;const t=(...s)=>{const n=this.currentTurn;return!n||n.settled||this.cancelIntent?Promise.resolve({behavior:"deny",message:"Claude conversation is no longer active"}):n.permissionHandler(...s)};this.sdkQuery=m({prompt:this.inputQueue.iterable,options:e.buildOptions(this.abortController,t,this.managedTaskContext)}),this.resetIdleTimer()}get conversationId(){return this._conversationId}get model(){return this._model}get agentMode(){return this._agentMode}get isDead(){return this.dead}get wasCancelled(){return this.cancelIntent}get currentGeneration(){return this.generation}hasActiveOwner(){return!!(this.currentTurn&&!this.currentTurn.settled)}bindConversation(e){this._conversationId=e}updateManagedTaskContext(e){for(const t of Object.keys(this.managedTaskContext))delete this.managedTaskContext[t];Object.assign(this.managedTaskContext,e)}reserveTurn(e,t,s,n){return this.withTransition(async()=>{if(this.dead||this.cancelIntent)throw new Error(`Claude session ${this.conversationId} is closed`);if(this.clearIdleTimer(),this.updateManagedTaskContext(n.managedTaskContext),this.hasActiveOwner())return this.inputQueue.push(l(e,"next")),{kind:"interjection",generation:this.generation};n.model!==this._model&&(await this.sdkQuery.setModel?.(n.model),this._model=n.model),n.agentMode!==this._agentMode&&(await this.sdkQuery.setPermissionMode?.(n.agentMode==="plan"?"plan":"bypassPermissions"),this._agentMode=n.agentMode);const o=++this.generation;let a,d;const h=new Promise((u,c)=>{a=u,d=c});return this.currentTurn={generation:o,handler:t,permissionHandler:s,resolve:a,reject:d,settled:!1},this.pumpStarted||(this.pumpStarted=!0,this.pump()),this.inputQueue.push(l(e)),{kind:"owner",generation:o,done:h}})}async setModel(e){await this.withTransition(async()=>{e===this._model||this.dead||this.cancelIntent||(await this.sdkQuery.setModel?.(e),this._model=e)})}async setPermissionMode(e){await this.withTransition(async()=>{this.dead||this.cancelIntent||(await this.sdkQuery.setPermissionMode?.(e),this._agentMode=e==="plan"?"plan":"default")})}abort(){this.cancelIntent=!0,this.withTransition(async()=>{this.dead||(this.dead=!0,this.clearIdleTimer(),this.inputQueue.close(),this.abortController.abort())})}withTransition(e){const t=this.transition.then(e,e);return this.transition=t.then(()=>{},()=>{}),t}async pump(){try{for await(const t of this.sdkQuery){f(t);const s=this.currentTurn;if(!s||s.settled)continue;await s.handler(t)&&this.settleTurn(s,void 0)}this.dead=!0;const e=this.currentTurn;e&&!e.settled&&this.settleTurn(e,void 0)}catch(e){this.dead=!0;const t=this.currentTurn;t&&!t.settled?this.settleTurn(t,e):this.cancelIntent||r.error(`Claude session ${this.conversationId} pump failed:`,e)}finally{this.clearIdleTimer(),this.onRecycle(this,this.generation)}}settleTurn(e,t){e.settled||(e.settled=!0,this.currentTurn===e&&(this.currentTurn=void 0),!this.dead&&!this.cancelIntent&&this.resetIdleTimer(),t===void 0?e.resolve():e.reject(t))}resetIdleTimer(){this.clearIdleTimer(),!(this.dead||this.cancelIntent)&&(this.idleTimer=setTimeout(()=>{this.withTransition(async()=>{if(this.dead||this.cancelIntent||this.hasActiveOwner()){!this.dead&&!this.cancelIntent&&this.resetIdleTimer();return}r.info(`Recycling idle Claude session ${this.conversationId}`),this.dead=!0,this.inputQueue.close(),this.sdkQuery.close?.(),this.onRecycle(this,this.generation)})},this.idleTimeoutMs),this.idleTimer.unref?.())}clearIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=void 0)}}export{I as LiveSession};
|
|
@@ -1,45 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mkdirSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { appConfig } from "./config.js";
|
|
5
|
-
mkdirSync(appConfig.dataDir, { recursive: true });
|
|
6
|
-
const db = new DatabaseSync(join(appConfig.dataDir, "llm-proxy-bindings.sqlite"));
|
|
7
|
-
db.exec(`
|
|
1
|
+
import{DatabaseSync as a}from"node:sqlite";import{mkdirSync as E}from"node:fs";import{join as p}from"node:path";import{appConfig as i}from"./config.js";E(i.dataDir,{recursive:!0});const e=new a(p(i.dataDir,"llm-proxy-bindings.sqlite"));e.exec(`
|
|
8
2
|
CREATE TABLE IF NOT EXISTS llm_proxy_bindings (
|
|
9
3
|
conversation_id TEXT PRIMARY KEY,
|
|
10
4
|
binding_handle TEXT NOT NULL,
|
|
11
5
|
updated_at INTEGER NOT NULL
|
|
12
6
|
)
|
|
13
|
-
`);
|
|
14
|
-
const getStatement = db.prepare("SELECT binding_handle FROM llm_proxy_bindings WHERE conversation_id = ?");
|
|
15
|
-
const setStatement = db.prepare(`INSERT INTO llm_proxy_bindings (conversation_id, binding_handle, updated_at)
|
|
7
|
+
`);const _=e.prepare("SELECT binding_handle FROM llm_proxy_bindings WHERE conversation_id = ?"),o=e.prepare(`INSERT INTO llm_proxy_bindings (conversation_id, binding_handle, updated_at)
|
|
16
8
|
VALUES (?, ?, ?)
|
|
17
9
|
ON CONFLICT(conversation_id) DO UPDATE SET
|
|
18
10
|
binding_handle = excluded.binding_handle,
|
|
19
|
-
updated_at = excluded.updated_at`);
|
|
20
|
-
const deleteStatement = db.prepare("DELETE FROM llm_proxy_bindings WHERE conversation_id = ?");
|
|
21
|
-
export function getLlmProxyBinding(conversationId) {
|
|
22
|
-
if (!conversationId)
|
|
23
|
-
return undefined;
|
|
24
|
-
const row = getStatement.get(conversationId);
|
|
25
|
-
return row?.binding_handle;
|
|
26
|
-
}
|
|
27
|
-
export function setLlmProxyBinding(conversationId, bindingHandle) {
|
|
28
|
-
if (!bindingHandle)
|
|
29
|
-
return;
|
|
30
|
-
setStatement.run(conversationId, bindingHandle, Date.now());
|
|
31
|
-
}
|
|
32
|
-
export function moveLlmProxyBinding(previousConversationId, conversationId, bindingHandle) {
|
|
33
|
-
if (!bindingHandle || previousConversationId === conversationId)
|
|
34
|
-
return;
|
|
35
|
-
db.exec("BEGIN IMMEDIATE");
|
|
36
|
-
try {
|
|
37
|
-
setStatement.run(conversationId, bindingHandle, Date.now());
|
|
38
|
-
deleteStatement.run(previousConversationId);
|
|
39
|
-
db.exec("COMMIT");
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
db.exec("ROLLBACK");
|
|
43
|
-
throw error;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
11
|
+
updated_at = excluded.updated_at`),c=e.prepare("DELETE FROM llm_proxy_bindings WHERE conversation_id = ?");function x(n){return n?_.get(n)?.binding_handle:void 0}function g(n,t){t&&o.run(n,t,Date.now())}function s(n,t,r){if(!(!r||n===t)){e.exec("BEGIN IMMEDIATE");try{o.run(t,r,Date.now()),c.run(n),e.exec("COMMIT")}catch(d){throw e.exec("ROLLBACK"),d}}}export{x as getLlmProxyBinding,s as moveLlmProxyBinding,g as setLlmProxyBinding};
|
package/dist/managed-task-mcp.js
CHANGED
|
@@ -1,41 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { executeManagedTaskTool, MANAGED_TASK_TOOL_DEFINITIONS, } from "@messenger-agent/shared/managed-task-tools";
|
|
3
|
-
import { WorkspaceFileError } from "@messenger-agent/shared/workspace-files";
|
|
4
|
-
export const MANAGED_TASK_MCP_SERVER_NAME = "managed_tasks";
|
|
5
|
-
function jsonResult(data) {
|
|
6
|
-
const structuredContent = data && typeof data === "object" && !Array.isArray(data) ? data : undefined;
|
|
7
|
-
return {
|
|
8
|
-
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
9
|
-
...(structuredContent ? { structuredContent } : null),
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
function errorResult(err) {
|
|
13
|
-
const message = err instanceof WorkspaceFileError && err.code === "INVALID_PATH"
|
|
14
|
-
? err.message
|
|
15
|
-
: err instanceof Error
|
|
16
|
-
? err.message
|
|
17
|
-
: String(err);
|
|
18
|
-
return {
|
|
19
|
-
content: [{ type: "text", text: message }],
|
|
20
|
-
isError: true,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
export function createManagedTaskMcpServer(options) {
|
|
24
|
-
return createSdkMcpServer({
|
|
25
|
-
name: MANAGED_TASK_MCP_SERVER_NAME,
|
|
26
|
-
version: "0.1.0",
|
|
27
|
-
instructions: "Use these tools for managed task records in the current workspace. Do not read or write task storage files directly.",
|
|
28
|
-
alwaysLoad: true,
|
|
29
|
-
tools: MANAGED_TASK_TOOL_DEFINITIONS.map((definition) => tool(definition.name, definition.description, definition.schema.shape, async (args) => {
|
|
30
|
-
try {
|
|
31
|
-
return jsonResult(await executeManagedTaskTool(options.workspaceRoot, definition.name, args, options.context));
|
|
32
|
-
}
|
|
33
|
-
catch (err) {
|
|
34
|
-
return errorResult(err);
|
|
35
|
-
}
|
|
36
|
-
}, {
|
|
37
|
-
alwaysLoad: true,
|
|
38
|
-
searchHint: definition.name,
|
|
39
|
-
})),
|
|
40
|
-
});
|
|
41
|
-
}
|
|
1
|
+
import{createSdkMcpServer as s,tool as n}from"@anthropic-ai/claude-agent-sdk";import{executeManagedTaskTool as a,MANAGED_TASK_TOOL_DEFINITIONS as c}from"@messenger-agent/shared/managed-task-tools";import{WorkspaceFileError as u}from"@messenger-agent/shared/workspace-files";const m="managed_tasks";function p(e){const t=e&&typeof e=="object"&&!Array.isArray(e)?e:void 0;return{content:[{type:"text",text:JSON.stringify(e)}],...t?{structuredContent:t}:null}}function i(e){return{content:[{type:"text",text:e instanceof u&&e.code==="INVALID_PATH"||e instanceof Error?e.message:String(e)}],isError:!0}}function A(e){return s({name:m,version:"0.1.0",instructions:"Use these tools for managed task records in the current workspace. Do not read or write task storage files directly.",alwaysLoad:!0,tools:c.map(t=>n(t.name,t.description,t.schema.shape,async r=>{try{return p(await a(e.workspaceRoot,t.name,r,e.context))}catch(o){return i(o)}},{alwaysLoad:!0,searchHint:t.name}))})}export{m as MANAGED_TASK_MCP_SERVER_NAME,A as createManagedTaskMcpServer};
|