ccgateway 0.1.7 → 0.1.9

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/router.js CHANGED
@@ -1,3 +1,7 @@
1
+ import { getCcgHome } from "./config.js";
2
+ import { writeFile, mkdir, rm } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
1
5
  // ── MessageRouter ─────────────────────────────────────────────────────────
2
6
  export class MessageRouter {
3
7
  agents;
@@ -5,12 +9,14 @@ export class MessageRouter {
5
9
  context;
6
10
  spawner;
7
11
  bindings;
8
- constructor(agents, sessions, context, spawner, bindings) {
12
+ watcher;
13
+ constructor(agents, sessions, context, spawner, bindings, watcher) {
9
14
  this.agents = agents;
10
15
  this.sessions = sessions;
11
16
  this.context = context;
12
17
  this.spawner = spawner;
13
18
  this.bindings = bindings;
19
+ this.watcher = watcher;
14
20
  }
15
21
  /**
16
22
  * Resolve which agent handles a message based on bindings.
@@ -58,23 +64,107 @@ export class MessageRouter {
58
64
  sourceUser: message.from.user,
59
65
  sourceMessageId: message.from.messageId,
60
66
  });
61
- // 4. Build context
67
+ // 4. Download attachments (if any)
68
+ // Images → base64 in-memory, passed as content blocks (no Read-tool round-trip)
69
+ // Other → saved to temp dir so Claude can read them via the Read tool
70
+ let attachmentDir;
71
+ let messageContent = message.content;
72
+ const images = [];
73
+ if (message.attachments.length > 0) {
74
+ const result = await this.downloadAttachments(message.attachments);
75
+ attachmentDir = result.dir;
76
+ images.push(...result.images);
77
+ if (result.filePaths.length > 0) {
78
+ const fileList = result.filePaths
79
+ .map((p) => ` - ${p}`)
80
+ .join("\n");
81
+ messageContent += `\n\n[Attached files — use the Read tool to view them]\n${fileList}`;
82
+ }
83
+ if (result.rejected.length > 0) {
84
+ const rejectedList = result.rejected.join(", ");
85
+ messageContent += `\n\n[Rejected attachments (blocked file type): ${rejectedList}]`;
86
+ }
87
+ }
88
+ // 5. Build context
62
89
  const systemPrompt = await this.context.build(agentId, sessionKey);
63
- // 5. Spawn claude --print
90
+ // 5.5 Triage: should this run async?
91
+ // Images always go async — they're slow on Opus and the user gets
92
+ // instant feedback instead of waiting 10+ minutes.
93
+ const mode = this.watcher
94
+ ? images.length > 0
95
+ ? "async"
96
+ : await this.spawner.triage(messageContent, agent.model)
97
+ : "sync";
98
+ if (mode === "async") {
99
+ // For async tasks with images: save images to temp files in the
100
+ // workspace so Claude Code can read them via the Read tool.
101
+ let asyncMessage = messageContent;
102
+ let asyncAttachDir;
103
+ if (images.length > 0) {
104
+ const dir = join(agent.workspace, `.ccg-attachments-${Date.now()}`);
105
+ await mkdir(dir, { recursive: true });
106
+ asyncAttachDir = dir;
107
+ const paths = [];
108
+ for (let i = 0; i < images.length; i++) {
109
+ const ext = images[i].mediaType.split("/")[1] || "png";
110
+ const filePath = join(dir, `image-${i}.${ext}`);
111
+ await writeFile(filePath, Buffer.from(images[i].base64, "base64"));
112
+ paths.push(filePath);
113
+ }
114
+ const fileList = paths.map((p) => ` - ${p}`).join("\n");
115
+ asyncMessage += `\n\n[Attached images — use the Read tool to view them]\n${fileList}`;
116
+ }
117
+ const { sessionName, taskDir } = await this.spawner.spawnAsync({
118
+ workspace: agent.workspace,
119
+ message: asyncMessage,
120
+ systemPrompt,
121
+ model: agent.model,
122
+ ccgHome: getCcgHome(),
123
+ agentId,
124
+ });
125
+ // Register with watcher
126
+ const binding = this.bindings.find((b) => b.agent === agentId);
127
+ this.watcher.register({
128
+ sessionName,
129
+ taskDir,
130
+ agentId,
131
+ gateway: message.from.gateway,
132
+ channel: message.from.channel,
133
+ botId: binding?.bot ?? agentId,
134
+ sessionKey,
135
+ workspace: agent.workspace,
136
+ startedAt: Date.now(),
137
+ });
138
+ const placeholder = `On it — working on this in the background. I'll get back to you when it's done. (tmux: \`${sessionName}\`)`;
139
+ await this.sessions.appendMessage(agentId, sessionKey, {
140
+ role: "assistant",
141
+ content: placeholder,
142
+ ts: Date.now(),
143
+ tokens: { in: 0, out: 0 },
144
+ });
145
+ return placeholder;
146
+ }
147
+ // 6. Spawn claude --print (sync path — unchanged)
64
148
  const result = await this.spawner.spawn({
65
149
  workspace: agent.workspace,
66
- message: message.content,
150
+ message: messageContent,
67
151
  systemPrompt,
68
152
  model: agent.model,
69
153
  allowedTools: agent.allowedTools,
70
154
  ...(agent.timeoutMs ? { timeoutMs: agent.timeoutMs } : {}),
155
+ ...(images.length > 0 ? { images } : {}),
71
156
  });
72
- // 6. Handle failure: append error to session and throw
157
+ // 7. Clean up attachment temp dir
158
+ if (attachmentDir) {
159
+ rm(attachmentDir, { recursive: true, force: true }).catch(() => { });
160
+ }
161
+ // 8. Handle failure: append error to session and throw
73
162
  if (result.exitCode !== 0) {
74
163
  const isTimeout = result.exitCode === 124;
164
+ const stderrHint = result.stderr ? `\n\nDetails: ${result.stderr.slice(0, 500)}` : "";
75
165
  const errorContent = isTimeout
76
166
  ? "The task timed out — it took longer than the allowed time. Try breaking it into smaller steps, or increase the agent's `timeoutMs` setting."
77
- : result.response || `Spawner failed with exit code ${result.exitCode}`;
167
+ : (result.response || `Spawner failed with exit code ${result.exitCode}`) + stderrHint;
78
168
  await this.sessions.appendMessage(agentId, sessionKey, {
79
169
  role: "assistant",
80
170
  content: `[error] ${errorContent}`,
@@ -83,15 +173,15 @@ export class MessageRouter {
83
173
  });
84
174
  throw new Error(errorContent);
85
175
  }
86
- // 7. Append assistant response to session
176
+ // 9. Append assistant response to session
87
177
  await this.sessions.appendMessage(agentId, sessionKey, {
88
178
  role: "assistant",
89
179
  content: result.response,
90
180
  ts: Date.now(),
91
181
  tokens: result.tokensEstimate,
92
182
  });
93
- // 8. Return response text
94
- return result.response;
183
+ // 10. Return response text (guard against empty responses)
184
+ return result.response.trim() || "(no response)";
95
185
  }
96
186
  /**
97
187
  * Add a binding at runtime.
@@ -111,5 +201,116 @@ export class MessageRouter {
111
201
  getPrimaryBinding(agentId) {
112
202
  return this.bindings.find((b) => b.agent === agentId);
113
203
  }
204
+ /**
205
+ * Download attachments, separating images (kept as base64 in-memory)
206
+ * from other files (saved to a temp directory for Read-tool access).
207
+ * Rejects blocked file types (executables, archives, etc.).
208
+ */
209
+ async downloadAttachments(attachments) {
210
+ const dir = join(tmpdir(), `ccg-attach-${Date.now()}`);
211
+ const filePaths = [];
212
+ const images = [];
213
+ const rejected = [];
214
+ let dirCreated = false;
215
+ for (const att of attachments) {
216
+ if (!att.url && !att.data)
217
+ continue;
218
+ const filename = att.filename || "attachment";
219
+ if (isBlockedAttachment(att.type, filename)) {
220
+ rejected.push(filename);
221
+ continue;
222
+ }
223
+ try {
224
+ // Use pre-downloaded data if available, otherwise fetch from URL
225
+ let buffer;
226
+ if (att.data) {
227
+ buffer = att.data;
228
+ }
229
+ else {
230
+ const resp = await fetch(att.url);
231
+ if (!resp.ok)
232
+ continue;
233
+ buffer = Buffer.from(await resp.arrayBuffer());
234
+ }
235
+ if (att.type.startsWith("image/")) {
236
+ images.push({
237
+ base64: buffer.toString("base64"),
238
+ mediaType: att.type,
239
+ });
240
+ }
241
+ else {
242
+ if (!dirCreated) {
243
+ await mkdir(dir, { recursive: true });
244
+ dirCreated = true;
245
+ }
246
+ const filePath = join(dir, filename);
247
+ await writeFile(filePath, buffer);
248
+ filePaths.push(filePath);
249
+ }
250
+ }
251
+ catch {
252
+ // Skip failed downloads
253
+ }
254
+ }
255
+ return { dir, filePaths, images, rejected };
256
+ }
257
+ }
258
+ // ── Attachment filtering ──────────────────────────────────────────────────
259
+ /** MIME types that are never allowed through. */
260
+ const BLOCKED_MIME_PREFIXES = [
261
+ "application/x-executable",
262
+ "application/x-msdos-program",
263
+ "application/x-msdownload",
264
+ "application/x-sharedlib",
265
+ "application/x-dosexec",
266
+ "application/vnd.microsoft.portable-executable",
267
+ ];
268
+ const BLOCKED_MIME_EXACT = new Set([
269
+ "application/x-sh",
270
+ "application/x-csh",
271
+ "application/x-bat",
272
+ "application/x-msi",
273
+ "application/java-archive",
274
+ "application/x-rar-compressed",
275
+ "application/x-7z-compressed",
276
+ "application/x-tar",
277
+ "application/gzip",
278
+ "application/zip",
279
+ "application/x-bzip2",
280
+ "application/x-xz",
281
+ "application/x-iso9660-image",
282
+ "application/x-apple-diskimage",
283
+ "application/vnd.debian.binary-package",
284
+ "application/x-rpm",
285
+ ]);
286
+ /** File extensions that are never allowed through (lowercase, with dot). */
287
+ const BLOCKED_EXTENSIONS = new Set([
288
+ // Executables & scripts
289
+ ".exe", ".bat", ".cmd", ".com", ".msi", ".scr", ".pif",
290
+ ".sh", ".bash", ".csh", ".ksh", ".zsh",
291
+ ".ps1", ".psm1", ".psd1", ".vbs", ".vbe", ".wsf", ".wsh",
292
+ // Compiled / bytecode
293
+ ".dll", ".so", ".dylib", ".sys", ".drv", ".o", ".obj",
294
+ ".class", ".jar", ".war", ".ear",
295
+ // Archives
296
+ ".zip", ".tar", ".gz", ".tgz", ".bz2", ".xz", ".7z",
297
+ ".rar", ".iso", ".dmg", ".img",
298
+ // Packages
299
+ ".deb", ".rpm", ".apk", ".snap", ".flatpak",
300
+ ".whl", ".gem", ".nupkg",
301
+ ]);
302
+ /** Returns true if the attachment should be rejected. */
303
+ export function isBlockedAttachment(mimeType, filename) {
304
+ const mime = mimeType.toLowerCase();
305
+ if (BLOCKED_MIME_EXACT.has(mime))
306
+ return true;
307
+ if (BLOCKED_MIME_PREFIXES.some((p) => mime.startsWith(p)))
308
+ return true;
309
+ const ext = filename.lastIndexOf(".") >= 0
310
+ ? filename.slice(filename.lastIndexOf(".")).toLowerCase()
311
+ : "";
312
+ if (ext && BLOCKED_EXTENSIONS.has(ext))
313
+ return true;
314
+ return false;
114
315
  }
115
316
  //# sourceMappingURL=router.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
package/dist/spawner.d.ts CHANGED
@@ -1,11 +1,28 @@
1
1
  export interface SpawnResult {
2
2
  response: string;
3
+ stderr: string;
3
4
  exitCode: number;
4
5
  tokensEstimate: {
5
6
  in: number;
6
7
  out: number;
7
8
  };
8
9
  }
10
+ export interface ImageInput {
11
+ base64: string;
12
+ mediaType: string;
13
+ }
14
+ export interface AsyncSpawnOptions {
15
+ workspace: string;
16
+ message: string;
17
+ systemPrompt: string;
18
+ model: string;
19
+ ccgHome: string;
20
+ agentId: string;
21
+ }
22
+ export interface AsyncSpawnResult {
23
+ sessionName: string;
24
+ taskDir: string;
25
+ }
9
26
  export interface SpawnOptions {
10
27
  workspace: string;
11
28
  message: string;
@@ -13,17 +30,39 @@ export interface SpawnOptions {
13
30
  model: string;
14
31
  allowedTools: string[];
15
32
  timeoutMs?: number;
33
+ /** When provided, switches to --input-format stream-json so images are
34
+ * sent as content blocks directly (no Read-tool round-trip). */
35
+ images?: ImageInput[];
16
36
  }
17
37
  export declare class CCSpawner {
38
+ private static readonly TRIAGE_TIMEOUT_MS;
39
+ private static readonly TRIAGE_PROMPT;
18
40
  /**
19
- * Spawn a `claude --print` invocation.
41
+ * Run a quick `claude --print` with Sonnet to classify a message as
42
+ * "sync" (quick task) or "async" (intensive, long-running).
20
43
  *
21
- * Executes:
22
- * claude --print -p "<message>" --append-system-prompt "<context>"
23
- * --model <model> --allowedTools Tool1,Tool2,...
44
+ * Defaults to "sync" on timeout, unexpected output, or errors.
45
+ */
46
+ triage(message: string, _model: string): Promise<"sync" | "async">;
47
+ /**
48
+ * Spawn a `claude --print` invocation.
24
49
  *
25
- * Captures stdout as the response. Estimates tokens as chars / 4.
50
+ * When `images` are provided, uses `--input-format stream-json` to pass
51
+ * image content blocks directly via stdin (avoids the Read-tool
52
+ * round-trip). Otherwise falls back to the simpler `-p "<message>"` form
53
+ * with stdin closed.
26
54
  */
27
55
  spawn(options: SpawnOptions): Promise<SpawnResult>;
56
+ private detectMultiplexer;
57
+ spawnAsync(options: AsyncSpawnOptions): Promise<AsyncSpawnResult>;
58
+ private spawnText;
59
+ private spawnStreamJson;
28
60
  }
61
+ /**
62
+ * Extract the assistant's text response from stream-json JSONL output.
63
+ *
64
+ * Looks for a `{"type":"result","result":"..."}` event first (final output),
65
+ * then falls back to collecting text from assistant message content blocks.
66
+ */
67
+ export declare function parseStreamOutput(stdout: string): string;
29
68
  //# sourceMappingURL=spawner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spawner.d.ts","sourceRoot":"","sources":["../src/spawner.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,qBAAa,SAAS;IACpB;;;;;;;;OAQG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;CAwDzD"}
1
+ {"version":3,"file":"spawner.d.ts","sourceRoot":"","sources":["../src/spawner.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;qEACiE;IACjE,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;CACvB;AAOD,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAU;IACnD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAIxB;IAEb;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC;IA8CxE;;;;;;;OAOG;IACG,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;IAOxD,OAAO,CAAC,iBAAiB;IAoBnB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAiCvE,OAAO,CAAC,SAAS;IAkFjB,OAAO,CAAC,eAAe;CAmHxB;AAID;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAyCxD"}