opencode-claw 0.2.3 → 0.2.5
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/channels/router.js +43 -13
- package/dist/cli.js +0 -0
- package/package.json +1 -1
package/dist/channels/router.js
CHANGED
|
@@ -31,11 +31,13 @@ function parseCommand(text) {
|
|
|
31
31
|
return { name: trimmed.slice(1).toLowerCase(), args: "" };
|
|
32
32
|
return { name: trimmed.slice(1, space).toLowerCase(), args: trimmed.slice(space + 1).trim() };
|
|
33
33
|
}
|
|
34
|
+
const PAGE_SIZE = 10;
|
|
34
35
|
const HELP_TEXT = `Available commands:
|
|
35
36
|
/new [title] — Create a new session
|
|
36
37
|
/switch <id> — Switch to an existing session
|
|
37
|
-
/sessions — List your sessions
|
|
38
|
+
/sessions [page] — List your sessions (paginated)
|
|
38
39
|
/current — Show current session
|
|
40
|
+
/status — Show current agent run status
|
|
39
41
|
/fork — Fork current session into a new one
|
|
40
42
|
/cancel — Abort the currently running agent
|
|
41
43
|
/help — Show this help`;
|
|
@@ -43,7 +45,7 @@ const HELP_TEXT = `Available commands:
|
|
|
43
45
|
function peerKey(channel, peerId) {
|
|
44
46
|
return `${channel}:${peerId}`;
|
|
45
47
|
}
|
|
46
|
-
async function handleCommand(cmd, msg, deps, activeStreams) {
|
|
48
|
+
async function handleCommand(cmd, msg, deps, activeStreams, activeStreamsMeta) {
|
|
47
49
|
const key = buildSessionKey(msg.channel, msg.peerId, msg.threadId);
|
|
48
50
|
switch (cmd.name) {
|
|
49
51
|
case "new": {
|
|
@@ -60,12 +62,18 @@ async function handleCommand(cmd, msg, deps, activeStreams) {
|
|
|
60
62
|
const list = await deps.sessions.listSessions(key);
|
|
61
63
|
if (list.length === 0)
|
|
62
64
|
return "No sessions found.";
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
const page = Math.max(1, Number.parseInt(cmd.args) || 1);
|
|
66
|
+
const totalPages = Math.ceil(list.length / PAGE_SIZE);
|
|
67
|
+
const clamped = Math.min(page, totalPages);
|
|
68
|
+
const slice = list.slice((clamped - 1) * PAGE_SIZE, clamped * PAGE_SIZE);
|
|
69
|
+
const lines = slice.map((s) => {
|
|
65
70
|
const marker = s.active ? " (active)" : "";
|
|
66
71
|
return `• ${s.id} — ${s.title}${marker}`;
|
|
67
|
-
})
|
|
68
|
-
|
|
72
|
+
});
|
|
73
|
+
if (totalPages > 1) {
|
|
74
|
+
lines.push(`\nPage ${clamped}/${totalPages}${clamped < totalPages ? ` — use /sessions ${clamped + 1} for next` : ""}`);
|
|
75
|
+
}
|
|
76
|
+
return lines.join("\n");
|
|
69
77
|
}
|
|
70
78
|
case "current": {
|
|
71
79
|
const id = deps.sessions.currentSession(key);
|
|
@@ -96,6 +104,19 @@ async function handleCommand(cmd, msg, deps, activeStreams) {
|
|
|
96
104
|
deps.logger.info("router: session aborted by user", { sessionId, aborted });
|
|
97
105
|
return aborted ? "Agent aborted." : "Abort request sent (agent may already be done).";
|
|
98
106
|
}
|
|
107
|
+
case "status": {
|
|
108
|
+
const pk = peerKey(msg.channel, msg.peerId);
|
|
109
|
+
const sessionId = activeStreams.get(pk);
|
|
110
|
+
if (!sessionId)
|
|
111
|
+
return "No agent is currently running.";
|
|
112
|
+
const meta = activeStreamsMeta.get(pk);
|
|
113
|
+
const elapsedSec = meta ? Math.floor((Date.now() - meta.startedAt) / 1000) : 0;
|
|
114
|
+
const mins = Math.floor(elapsedSec / 60);
|
|
115
|
+
const secs = elapsedSec % 60;
|
|
116
|
+
const elapsed = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
117
|
+
const tool = meta?.lastTool ? ` — last tool: ${humanizeToolName(meta.lastTool)}` : "";
|
|
118
|
+
return `⏳ Agent is running (${elapsed} elapsed${tool})`;
|
|
119
|
+
}
|
|
99
120
|
case "help": {
|
|
100
121
|
return HELP_TEXT;
|
|
101
122
|
}
|
|
@@ -110,7 +131,7 @@ function humanizeToolName(raw) {
|
|
|
110
131
|
return raw;
|
|
111
132
|
return raw.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
112
133
|
}
|
|
113
|
-
async function routeMessage(msg, deps, activeStreams, pendingQuestions) {
|
|
134
|
+
async function routeMessage(msg, deps, activeStreams, activeStreamsMeta, pendingQuestions) {
|
|
114
135
|
const adapter = deps.adapters.get(msg.channel);
|
|
115
136
|
if (!adapter) {
|
|
116
137
|
deps.logger.warn("router: no adapter for channel", { channel: msg.channel });
|
|
@@ -131,7 +152,7 @@ async function routeMessage(msg, deps, activeStreams, pendingQuestions) {
|
|
|
131
152
|
// Command interception
|
|
132
153
|
const cmd = parseCommand(msg.text);
|
|
133
154
|
if (cmd) {
|
|
134
|
-
const reply = await handleCommand(cmd, msg, deps, activeStreams);
|
|
155
|
+
const reply = await handleCommand(cmd, msg, deps, activeStreams, activeStreamsMeta);
|
|
135
156
|
await adapter.send(msg.peerId, { text: reply, replyToId: msg.replyToId });
|
|
136
157
|
return;
|
|
137
158
|
}
|
|
@@ -141,6 +162,7 @@ async function routeMessage(msg, deps, activeStreams, pendingQuestions) {
|
|
|
141
162
|
deps.logger.debug("router: prompting session", { sessionId, channel: msg.channel });
|
|
142
163
|
const pk = peerKey(msg.channel, msg.peerId);
|
|
143
164
|
activeStreams.set(pk, sessionId);
|
|
165
|
+
activeStreamsMeta.set(pk, { startedAt: Date.now(), lastTool: undefined });
|
|
144
166
|
// Start typing indicator
|
|
145
167
|
if (adapter.sendTyping) {
|
|
146
168
|
await adapter.sendTyping(msg.peerId).catch(() => { });
|
|
@@ -179,10 +201,15 @@ async function routeMessage(msg, deps, activeStreams, pendingQuestions) {
|
|
|
179
201
|
}
|
|
180
202
|
const progress = progressEnabled
|
|
181
203
|
? {
|
|
182
|
-
onToolRunning: (_tool, title) =>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
204
|
+
onToolRunning: (_tool, title) => {
|
|
205
|
+
const meta = activeStreamsMeta.get(pk);
|
|
206
|
+
if (meta)
|
|
207
|
+
meta.lastTool = title;
|
|
208
|
+
return adapter.send(msg.peerId, {
|
|
209
|
+
text: `🔧 ${humanizeToolName(title)}...`,
|
|
210
|
+
replyToId: msg.replyToId,
|
|
211
|
+
});
|
|
212
|
+
},
|
|
186
213
|
onHeartbeat: async () => {
|
|
187
214
|
if (adapter.sendTyping) {
|
|
188
215
|
await adapter.sendTyping(msg.peerId).catch(() => { });
|
|
@@ -219,6 +246,7 @@ async function routeMessage(msg, deps, activeStreams, pendingQuestions) {
|
|
|
219
246
|
}
|
|
220
247
|
finally {
|
|
221
248
|
activeStreams.delete(pk);
|
|
249
|
+
activeStreamsMeta.delete(pk);
|
|
222
250
|
pendingQuestions.delete(pk);
|
|
223
251
|
if (adapter.stopTyping) {
|
|
224
252
|
await adapter.stopTyping(msg.peerId).catch(() => { });
|
|
@@ -234,6 +262,8 @@ async function routeMessage(msg, deps, activeStreams, pendingQuestions) {
|
|
|
234
262
|
export function createRouter(deps) {
|
|
235
263
|
// Tracks which sessionId is currently streaming for each channel:peerId pair
|
|
236
264
|
const activeStreams = new Map();
|
|
265
|
+
// Tracks timing + last tool for each active stream
|
|
266
|
+
const activeStreamsMeta = new Map();
|
|
237
267
|
// Tracks pending question resolvers — when agent asks a question, user's next message resolves it
|
|
238
268
|
const pendingQuestions = new Map();
|
|
239
269
|
async function handler(msg) {
|
|
@@ -247,7 +277,7 @@ export function createRouter(deps) {
|
|
|
247
277
|
pending.resolve(msg.text);
|
|
248
278
|
return;
|
|
249
279
|
}
|
|
250
|
-
await routeMessage(msg, deps, activeStreams, pendingQuestions);
|
|
280
|
+
await routeMessage(msg, deps, activeStreams, activeStreamsMeta, pendingQuestions);
|
|
251
281
|
}
|
|
252
282
|
catch (err) {
|
|
253
283
|
deps.logger.error("router: unhandled error", {
|
package/dist/cli.js
CHANGED
|
File without changes
|