@lobu/cli 3.2.0 → 3.4.0

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.
Files changed (63) hide show
  1. package/README.md +12 -4
  2. package/dist/__tests__/chat.integration.test.d.ts +2 -0
  3. package/dist/__tests__/chat.integration.test.d.ts.map +1 -0
  4. package/dist/__tests__/chat.integration.test.js +337 -0
  5. package/dist/__tests__/chat.integration.test.js.map +1 -0
  6. package/dist/__tests__/init-memory.test.d.ts +2 -0
  7. package/dist/__tests__/init-memory.test.d.ts.map +1 -0
  8. package/dist/__tests__/init-memory.test.js +53 -0
  9. package/dist/__tests__/init-memory.test.js.map +1 -0
  10. package/dist/commands/chat.js +60 -18
  11. package/dist/commands/chat.js.map +1 -1
  12. package/dist/commands/connections/platforms.d.ts.map +1 -1
  13. package/dist/commands/connections/platforms.js +10 -0
  14. package/dist/commands/connections/platforms.js.map +1 -1
  15. package/dist/commands/eval.d.ts.map +1 -1
  16. package/dist/commands/eval.js +6 -2
  17. package/dist/commands/eval.js.map +1 -1
  18. package/dist/commands/init.d.ts +22 -0
  19. package/dist/commands/init.d.ts.map +1 -1
  20. package/dist/commands/init.js +60 -29
  21. package/dist/commands/init.js.map +1 -1
  22. package/dist/commands/providers/add.js +4 -4
  23. package/dist/commands/providers/add.js.map +1 -1
  24. package/dist/commands/providers/list.d.ts.map +1 -1
  25. package/dist/commands/providers/list.js +11 -12
  26. package/dist/commands/providers/list.js.map +1 -1
  27. package/dist/commands/providers/registry.d.ts +17 -0
  28. package/dist/commands/providers/registry.d.ts.map +1 -0
  29. package/dist/commands/{skills → providers}/registry.js +7 -12
  30. package/dist/commands/providers/registry.js.map +1 -0
  31. package/dist/commands/skills/add.d.ts.map +1 -1
  32. package/dist/commands/skills/add.js +2 -47
  33. package/dist/commands/skills/add.js.map +1 -1
  34. package/dist/commands/skills/info.d.ts.map +1 -1
  35. package/dist/commands/skills/info.js +3 -32
  36. package/dist/commands/skills/info.js.map +1 -1
  37. package/dist/commands/skills/list.d.ts.map +1 -1
  38. package/dist/commands/skills/list.js +5 -25
  39. package/dist/commands/skills/list.js.map +1 -1
  40. package/dist/commands/skills/search.d.ts.map +1 -1
  41. package/dist/commands/skills/search.js +2 -19
  42. package/dist/commands/skills/search.js.map +1 -1
  43. package/dist/commands/validate.d.ts.map +1 -1
  44. package/dist/commands/validate.js +0 -8
  45. package/dist/commands/validate.js.map +1 -1
  46. package/dist/eval/client.d.ts +1 -0
  47. package/dist/eval/client.d.ts.map +1 -1
  48. package/dist/eval/client.js +2 -0
  49. package/dist/eval/client.js.map +1 -1
  50. package/dist/eval/runner.d.ts.map +1 -1
  51. package/dist/eval/runner.js +6 -0
  52. package/dist/eval/runner.js.map +1 -1
  53. package/dist/eval/types.d.ts +2 -0
  54. package/dist/eval/types.d.ts.map +1 -1
  55. package/dist/eval/types.js +2 -0
  56. package/dist/eval/types.js.map +1 -1
  57. package/dist/index.js +5 -5
  58. package/dist/index.js.map +1 -1
  59. package/dist/{system-skills.json → providers.json} +1 -71
  60. package/package.json +2 -2
  61. package/dist/commands/skills/registry.d.ts +0 -29
  62. package/dist/commands/skills/registry.d.ts.map +0 -1
  63. package/dist/commands/skills/registry.js.map +0 -1
package/README.md CHANGED
@@ -20,14 +20,22 @@ Scaffold a new Lobu project with interactive prompts:
20
20
  - **Gateway port** and optional **public URL** (for OAuth callbacks)
21
21
  - **Admin password**
22
22
  - **Worker network access** (isolated, allowlist, or unrestricted)
23
- - **AI provider** selection from the skills registry + API key
24
- - **Skills** to enable (from `config/system-skills.json`)
23
+ - **AI provider** selection from the bundled provider registry + API key
24
+ - **Providers** to enable (from `config/providers.json`)
25
25
  - **Messaging platform** (Telegram, Slack, Discord, or none)
26
- - **Auth provider** (Owletto, custom, or none)
27
- - **Memory plugin** configuration
26
+ - **Memory** selection (filesystem, Owletto Cloud, Owletto Local, or custom Owletto URL)
28
27
 
29
28
  **Generates:** `docker-compose.yml`, `.env`, `Dockerfile.worker`, `lobu.toml`, `IDENTITY.md`, `.gitignore`, `README.md`
30
29
 
30
+ When Owletto memory is enabled, `lobu init` also scaffolds the file-first memory layout:
31
+
32
+ - `owletto.yaml`
33
+ - `models/`
34
+ - `data/`
35
+ - `[memory.owletto]` in `lobu.toml`
36
+
37
+ For Owletto Local or a custom Owletto deployment, `.env` keeps `MEMORY_URL` as the optional base MCP URL override.
38
+
31
39
  ## Usage
32
40
 
33
41
  ```bash
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=chat.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/chat.integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,337 @@
1
+ import { afterEach, beforeAll, describe, expect, mock, test } from "bun:test";
2
+ import { join } from "node:path";
3
+ const createInterfaceMock = mock(() => ({
4
+ question: (_prompt, callback) => callback("1"),
5
+ close: () => undefined,
6
+ }));
7
+ mock.module("node:readline", () => ({
8
+ createInterface: createInterfaceMock,
9
+ }));
10
+ let chatCommand;
11
+ const originalFetch = globalThis.fetch;
12
+ const originalStdoutWrite = process.stdout.write.bind(process.stdout);
13
+ const originalStderrWrite = process.stderr.write.bind(process.stderr);
14
+ const originalConsoleError = console.error;
15
+ const originalToken = process.env.LOBU_API_TOKEN;
16
+ const exampleDir = join(import.meta.dir, "../../../../examples/careops");
17
+ function createSseResponse(events) {
18
+ const encoder = new TextEncoder();
19
+ const payload = events
20
+ .map(({ event, data }) => `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`)
21
+ .join("");
22
+ return new Response(new ReadableStream({
23
+ start(controller) {
24
+ controller.enqueue(encoder.encode(payload));
25
+ controller.close();
26
+ },
27
+ }), {
28
+ status: 200,
29
+ headers: { "Content-Type": "text/event-stream" },
30
+ });
31
+ }
32
+ function captureTerminal(output) {
33
+ process.stdout.write = ((chunk, cb) => {
34
+ output.stdout.push(String(chunk));
35
+ if (typeof cb === "function") {
36
+ cb(null);
37
+ }
38
+ return true;
39
+ });
40
+ process.stderr.write = ((chunk, cb) => {
41
+ output.stderr.push(String(chunk));
42
+ if (typeof cb === "function") {
43
+ cb(null);
44
+ }
45
+ return true;
46
+ });
47
+ console.error = (...args) => {
48
+ output.stderr.push(args.map((arg) => String(arg)).join(" "));
49
+ };
50
+ }
51
+ beforeAll(async () => {
52
+ ({ chatCommand } = await import("../commands/chat"));
53
+ });
54
+ afterEach(() => {
55
+ globalThis.fetch = originalFetch;
56
+ process.stdout.write = originalStdoutWrite;
57
+ process.stderr.write = originalStderrWrite;
58
+ console.error = originalConsoleError;
59
+ if (originalToken === undefined) {
60
+ delete process.env.LOBU_API_TOKEN;
61
+ }
62
+ else {
63
+ process.env.LOBU_API_TOKEN = originalToken;
64
+ }
65
+ mock.restore();
66
+ });
67
+ describe("chatCommand example integration", () => {
68
+ test("uses the hr example agent and completes approval plus login interaction flow", async () => {
69
+ process.env.LOBU_API_TOKEN = "cli-token";
70
+ const stdout = [];
71
+ const stderr = [];
72
+ const createBodies = [];
73
+ const approvalBodies = [];
74
+ captureTerminal({ stdout, stderr });
75
+ globalThis.fetch = mock(async (input, init) => {
76
+ const url = String(input);
77
+ if (url === "http://gateway.test/api/v1/agents" &&
78
+ init?.method === "POST") {
79
+ const body = JSON.parse(String(init.body));
80
+ createBodies.push(body);
81
+ return Response.json({
82
+ agentId: "session-1",
83
+ token: "session-token",
84
+ });
85
+ }
86
+ if (url === "http://gateway.test/api/v1/agents/session-1/events" &&
87
+ !init?.method) {
88
+ return createSseResponse([
89
+ {
90
+ event: "output",
91
+ data: { content: "Starting request.\n" },
92
+ },
93
+ {
94
+ event: "tool-approval",
95
+ data: {
96
+ requestId: "approval-1",
97
+ mcpId: "github",
98
+ toolName: "delete_issue",
99
+ args: { issue_number: 42 },
100
+ },
101
+ },
102
+ {
103
+ event: "link-button",
104
+ data: {
105
+ label: "Connect GitHub",
106
+ url: "https://auth.example.com/device",
107
+ },
108
+ },
109
+ {
110
+ event: "question",
111
+ data: {
112
+ question: "Pick one",
113
+ options: ["A", "B"],
114
+ },
115
+ },
116
+ {
117
+ event: "suggestion",
118
+ data: {
119
+ prompts: ["retry", "show me the details"],
120
+ },
121
+ },
122
+ {
123
+ event: "complete",
124
+ data: {},
125
+ },
126
+ ]);
127
+ }
128
+ if (url === "http://gateway.test/api/v1/agents/session-1/messages" &&
129
+ init?.method === "POST") {
130
+ return Response.json({ success: true });
131
+ }
132
+ if (url === "http://gateway.test/api/v1/agents/approve" &&
133
+ init?.method === "POST") {
134
+ const body = JSON.parse(String(init.body));
135
+ approvalBodies.push(body);
136
+ return Response.json({
137
+ result: {
138
+ content: [{ text: "Approved tool result." }],
139
+ },
140
+ });
141
+ }
142
+ throw new Error(`Unexpected fetch: ${url}`);
143
+ });
144
+ await chatCommand(exampleDir, "please run the workflow", {
145
+ gateway: "http://gateway.test",
146
+ new: true,
147
+ });
148
+ expect(createBodies).toEqual([
149
+ {
150
+ agentId: "careops",
151
+ forceNew: true,
152
+ },
153
+ ]);
154
+ expect(approvalBodies).toEqual([
155
+ {
156
+ requestId: "approval-1",
157
+ decision: "1h",
158
+ },
159
+ ]);
160
+ const stdoutText = stdout.join("");
161
+ const stderrText = stderr.join("");
162
+ expect(stdoutText).toContain("Starting request.");
163
+ expect(stdoutText).toContain("Approved tool result.");
164
+ expect(stderrText).toContain("Tool Approval Required");
165
+ expect(stderrText).toContain("github");
166
+ expect(stderrText).toContain('"event":"link-button"');
167
+ expect(stderrText).toContain("Connect GitHub");
168
+ expect(stderrText).toContain('"event":"question"');
169
+ expect(stderrText).toContain('"event":"suggestion"');
170
+ expect(createInterfaceMock).toHaveBeenCalledTimes(1);
171
+ });
172
+ test("prints structured file-uploaded events in platform mode", async () => {
173
+ process.env.LOBU_API_TOKEN = "cli-token";
174
+ const stdout = [];
175
+ const stderr = [];
176
+ captureTerminal({ stdout, stderr });
177
+ globalThis.fetch = mock(async (input, init) => {
178
+ const url = String(input);
179
+ if (url === "http://gateway.test/api/v1/agents/careops/messages" &&
180
+ init?.method === "POST") {
181
+ return Response.json({
182
+ success: true,
183
+ eventsUrl: "/api/v1/agents/careops/events?platform=telegram",
184
+ });
185
+ }
186
+ if (url ===
187
+ "http://gateway.test/api/v1/agents/careops/events?platform=telegram" &&
188
+ !init?.method) {
189
+ return createSseResponse([
190
+ {
191
+ event: "file-uploaded",
192
+ data: {
193
+ tool: "UploadUserFile",
194
+ platform: "telegram",
195
+ fileId: "file-123",
196
+ name: "e2e.txt",
197
+ permalink: "https://files.example/e2e.txt",
198
+ size: 8,
199
+ },
200
+ },
201
+ {
202
+ event: "output",
203
+ data: {
204
+ content: "Uploaded e2e.txt successfully.\n",
205
+ },
206
+ },
207
+ { event: "complete", data: {} },
208
+ ]);
209
+ }
210
+ throw new Error(`Unexpected fetch: ${url}`);
211
+ });
212
+ await chatCommand(exampleDir, "send me e2e.txt as a file", {
213
+ gateway: "http://gateway.test",
214
+ user: "telegram:chat-123",
215
+ });
216
+ expect(stdout.join("")).toContain("Uploaded e2e.txt successfully");
217
+ expect(stderr.join("")).toContain('"event":"file-uploaded"');
218
+ expect(stderr.join("")).toContain('"name":"e2e.txt"');
219
+ expect(stderr.join("")).toContain('"tool":"UploadUserFile"');
220
+ });
221
+ test("warns when a sandbox link is streamed without a file-uploaded event", async () => {
222
+ process.env.LOBU_API_TOKEN = "cli-token";
223
+ const stdout = [];
224
+ const stderr = [];
225
+ captureTerminal({ stdout, stderr });
226
+ globalThis.fetch = mock(async (input, init) => {
227
+ const url = String(input);
228
+ if (url === "http://gateway.test/api/v1/agents/careops/messages" &&
229
+ init?.method === "POST") {
230
+ return Response.json({
231
+ success: true,
232
+ eventsUrl: "/api/v1/agents/careops/events?platform=telegram",
233
+ });
234
+ }
235
+ if (url ===
236
+ "http://gateway.test/api/v1/agents/careops/events?platform=telegram" &&
237
+ !init?.method) {
238
+ return createSseResponse([
239
+ {
240
+ event: "output",
241
+ data: {
242
+ content: "[Download cli-proof.txt](sandbox:/workspace/cli-proof.txt)",
243
+ },
244
+ },
245
+ { event: "complete", data: {} },
246
+ ]);
247
+ }
248
+ throw new Error(`Unexpected fetch: ${url}`);
249
+ });
250
+ await chatCommand(exampleDir, "send me cli-proof.txt as a file", {
251
+ gateway: "http://gateway.test",
252
+ user: "telegram:chat-123",
253
+ });
254
+ expect(stdout.join("")).toContain("cli-proof.txt");
255
+ expect(stderr.join("")).toContain("no file-uploaded event was emitted");
256
+ });
257
+ test("streams raw output chunks without corrupting fragmented markdown", async () => {
258
+ process.env.LOBU_API_TOKEN = "cli-token";
259
+ const stdout = [];
260
+ const stderr = [];
261
+ captureTerminal({ stdout, stderr });
262
+ globalThis.fetch = mock(async (input, init) => {
263
+ const url = String(input);
264
+ if (url === "http://gateway.test/api/v1/agents/careops/messages" &&
265
+ init?.method === "POST") {
266
+ return Response.json({
267
+ success: true,
268
+ eventsUrl: "/api/v1/agents/careops/events?platform=telegram",
269
+ });
270
+ }
271
+ if (url ===
272
+ "http://gateway.test/api/v1/agents/careops/events?platform=telegram" &&
273
+ !init?.method) {
274
+ return createSseResponse([
275
+ { event: "output", data: { content: "**Hello" } },
276
+ { event: "output", data: { content: " world**" } },
277
+ { event: "complete", data: {} },
278
+ ]);
279
+ }
280
+ throw new Error(`Unexpected fetch: ${url}`);
281
+ });
282
+ await chatCommand(exampleDir, "say hello", {
283
+ gateway: "http://gateway.test",
284
+ user: "telegram:chat-123",
285
+ });
286
+ expect(stdout.join("")).toContain("**Hello world**");
287
+ expect(stderr.join("")).not.toContain("file-uploaded");
288
+ });
289
+ test("streams platform-mode output for image and voice requests from the example agent", async () => {
290
+ process.env.LOBU_API_TOKEN = "cli-token";
291
+ const stdout = [];
292
+ const stderr = [];
293
+ const messageBodies = [];
294
+ captureTerminal({ stdout, stderr });
295
+ globalThis.fetch = mock(async (input, init) => {
296
+ const url = String(input);
297
+ if (url === "http://gateway.test/api/v1/agents/careops/messages" &&
298
+ init?.method === "POST") {
299
+ const body = JSON.parse(String(init.body));
300
+ messageBodies.push(body);
301
+ return Response.json({
302
+ success: true,
303
+ eventsUrl: "/api/v1/agents/careops/events?platform=telegram",
304
+ });
305
+ }
306
+ if (url ===
307
+ "http://gateway.test/api/v1/agents/careops/events?platform=telegram" &&
308
+ !init?.method) {
309
+ return createSseResponse([
310
+ {
311
+ event: "output",
312
+ data: {
313
+ content: "Image sent successfully (generated with openai).\nVoice message sent successfully (generated with openai).\n",
314
+ },
315
+ },
316
+ { event: "complete", data: {} },
317
+ ]);
318
+ }
319
+ throw new Error(`Unexpected fetch: ${url}`);
320
+ });
321
+ await chatCommand(exampleDir, "send an image and a voice reply", {
322
+ gateway: "http://gateway.test",
323
+ user: "telegram:chat-123",
324
+ });
325
+ expect(messageBodies).toEqual([
326
+ {
327
+ platform: "telegram",
328
+ content: "send an image and a voice reply",
329
+ telegram: { chatId: "chat-123" },
330
+ },
331
+ ]);
332
+ expect(stdout.join("")).toContain("Image sent successfully");
333
+ expect(stdout.join("")).toContain("Voice message sent successfully");
334
+ expect(stderr.join("")).not.toContain("Failed");
335
+ });
336
+ });
337
+ //# sourceMappingURL=chat.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.integration.test.js","sourceRoot":"","sources":["../../src/__tests__/chat.integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=init-memory.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-memory.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/init-memory.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,53 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { generateDockerCompose, generateLobuToml, generateOwlettoProjectLayout, } from "../commands/init";
6
+ describe("init memory scaffolding", () => {
7
+ let projectDir;
8
+ beforeEach(() => {
9
+ projectDir = mkdtempSync(join(tmpdir(), "lobu-init-memory-"));
10
+ mkdirSync(join(projectDir, "agents", "support"), { recursive: true });
11
+ });
12
+ afterEach(() => {
13
+ rmSync(projectDir, { recursive: true, force: true });
14
+ });
15
+ test("generateLobuToml writes the [memory.owletto] block when enabled", async () => {
16
+ await generateLobuToml(projectDir, {
17
+ agentName: "support",
18
+ allowedDomains: "github.com,.github.com",
19
+ includeOwlettoMemory: true,
20
+ });
21
+ const content = readFileSync(join(projectDir, "lobu.toml"), "utf-8");
22
+ expect(content).toContain("[memory.owletto]");
23
+ expect(content).toContain('config = "./owletto.yaml"');
24
+ expect(content).toContain('models = "./models"');
25
+ expect(content).toContain('data = "./data"');
26
+ });
27
+ test("generateOwlettoProjectLayout creates the new Owletto structure", async () => {
28
+ await generateOwlettoProjectLayout(projectDir, {
29
+ org: "support",
30
+ name: "Support",
31
+ });
32
+ const owlettoYaml = readFileSync(join(projectDir, "owletto.yaml"), "utf-8");
33
+ expect(owlettoYaml).toContain("version: 1");
34
+ expect(owlettoYaml).toContain("org: support");
35
+ expect(statSync(join(projectDir, "models")).isDirectory()).toBe(true);
36
+ expect(statSync(join(projectDir, "data", "entities")).isDirectory()).toBe(true);
37
+ expect(statSync(join(projectDir, "data", "relationships")).isDirectory()).toBe(true);
38
+ });
39
+ test("generateDockerCompose keeps MEMORY_URL as an optional base override", () => {
40
+ const content = generateDockerCompose({
41
+ projectName: "support",
42
+ gatewayPort: "8080",
43
+ dockerfilePath: "./Dockerfile.worker",
44
+ deploymentMode: "embedded",
45
+ includeOwlettoLocal: true,
46
+ });
47
+ expect(content).toContain("Optional Owletto base MCP URL override");
48
+ expect(content).toContain("MEMORY_URL: ${MEMORY_URL:-}");
49
+ expect(content).toContain("owletto:");
50
+ expect(content).toContain("owletto-postgres:");
51
+ });
52
+ });
53
+ //# sourceMappingURL=init-memory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-memory.test.js","sourceRoot":"","sources":["../../src/__tests__/init-memory.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AACzE,OAAO,EACL,SAAS,EACT,WAAW,EACX,YAAY,EACZ,MAAM,EACN,QAAQ,GACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,4BAA4B,GAC7B,MAAM,kBAAkB,CAAC;AAE1B,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,IAAI,UAAkB,CAAC;IAEvB,UAAU,CAAC,GAAG,EAAE;QACd,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAC9D,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,gBAAgB,CAAC,UAAU,EAAE;YACjC,SAAS,EAAE,SAAS;YACpB,cAAc,EAAE,wBAAwB;YACxC,oBAAoB,EAAE,IAAI;SAC3B,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,CAAC;QAErE,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAC9C,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;QACvD,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,4BAA4B,CAAC,UAAU,EAAE;YAC7C,GAAG,EAAE,SAAS;YACd,IAAI,EAAE,SAAS;SAChB,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;QAE5E,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAC5C,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAC9C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CACvE,IAAI,CACL,CAAC;QACF,MAAM,CACJ,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,WAAW,EAAE,CAClE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC/E,MAAM,OAAO,GAAG,qBAAqB,CAAC;YACpC,WAAW,EAAE,SAAS;YACtB,WAAW,EAAE,MAAM;YACnB,cAAc,EAAE,qBAAqB;YACrC,cAAc,EAAE,UAAU;YAC1B,mBAAmB,EAAE,IAAI;SAC1B,CAAC,CAAC;QAEH,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,wCAAwC,CAAC,CAAC;QACpE,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;QACzD,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACtC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -110,7 +110,7 @@ async function sendViaPlatform(gatewayUrl, authToken, opts) {
110
110
  ? result.eventsUrl
111
111
  : `${gatewayUrl}${result.eventsUrl}`;
112
112
  const sseController = new AbortController();
113
- await streamResponse(sseUrl, authToken, sseController);
113
+ await streamResponse(sseUrl, authToken, sseController, result.messageId);
114
114
  }
115
115
  else {
116
116
  console.log(chalk.dim(` Message sent via ${opts.platform}. Response will appear on the platform.\n`));
@@ -175,7 +175,29 @@ async function resolveAgentId(cwd) {
175
175
  const ids = Object.keys(result.config.agents);
176
176
  return ids[0];
177
177
  }
178
- async function streamResponse(sseUrl, token, controller) {
178
+ async function writeStdout(text) {
179
+ await new Promise((resolve, reject) => {
180
+ process.stdout.write(text, (error) => {
181
+ if (error) {
182
+ reject(error);
183
+ return;
184
+ }
185
+ resolve();
186
+ });
187
+ });
188
+ }
189
+ async function writeStderr(text) {
190
+ await new Promise((resolve, reject) => {
191
+ process.stderr.write(text, (error) => {
192
+ if (error) {
193
+ reject(error);
194
+ return;
195
+ }
196
+ resolve();
197
+ });
198
+ });
199
+ }
200
+ async function streamResponse(sseUrl, token, controller, expectedMessageId) {
179
201
  const OVERALL_TIMEOUT_MS = 5 * 60 * 1000;
180
202
  const IDLE_TIMEOUT_MS = 60 * 1000;
181
203
  const overallTimer = setTimeout(() => controller.abort(), OVERALL_TIMEOUT_MS);
@@ -197,6 +219,8 @@ async function streamResponse(sseUrl, token, controller) {
197
219
  const decoder = new TextDecoder();
198
220
  let buffer = "";
199
221
  let currentEvent = "";
222
+ let sawFileUploadedEvent = false;
223
+ let sawSandboxLink = false;
200
224
  while (true) {
201
225
  const { done, value } = await reader.read();
202
226
  if (done)
@@ -213,14 +237,26 @@ async function streamResponse(sseUrl, token, controller) {
213
237
  const data = parseJSON(line.slice(6));
214
238
  if (!data)
215
239
  continue;
240
+ if (expectedMessageId &&
241
+ currentEvent !== "connected" &&
242
+ currentEvent !== "ping" &&
243
+ typeof data.messageId === "string" &&
244
+ data.messageId !== expectedMessageId) {
245
+ currentEvent = "";
246
+ continue;
247
+ }
216
248
  switch (currentEvent) {
217
249
  case "output":
218
- if (typeof data.content === "string")
219
- process.stdout.write(renderMarkdown(data.content));
250
+ if (typeof data.content === "string") {
251
+ if (data.content.includes("sandbox:/")) {
252
+ sawSandboxLink = true;
253
+ }
254
+ await writeStdout(data.content);
255
+ }
220
256
  break;
221
257
  case "ephemeral":
222
258
  if (typeof data.content === "string") {
223
- console.error(`\n${renderMarkdown(data.content)}\n`);
259
+ await writeStderr(`\n${renderMarkdown(data.content)}\n`);
224
260
  }
225
261
  controller.abort();
226
262
  return;
@@ -231,23 +267,22 @@ async function streamResponse(sseUrl, token, controller) {
231
267
  .map(([k, v]) => ` ${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`)
232
268
  .join("\n")
233
269
  : "";
234
- console.error(chalk.yellow(`\n Tool Approval Required\n ${data.mcpId} → ${data.toolName}\n${argsText}\n`));
235
- const options = ["once", "1h", "24h", "always", "deny"];
270
+ await writeStderr(chalk.yellow(`\n Tool Approval Required\n ${data.mcpId} → ${data.toolName}\n${argsText}\n`));
271
+ const options = ["1h", "24h", "always", "deny"];
236
272
  const optionLabels = {
237
- once: "1 min",
238
273
  "1h": "1h",
239
274
  "24h": "24h",
240
275
  always: "always",
241
276
  deny: "deny always",
242
277
  };
243
- console.error(options
278
+ await writeStderr(`${options
244
279
  .map((o, i) => ` ${chalk.bold(`${i + 1}`)}. ${o === "deny" ? chalk.red(optionLabels[o]) : chalk.green(optionLabels[o])}`)
245
- .join("\n"));
280
+ .join("\n")}\n`);
246
281
  const rl = createInterface({
247
282
  input: process.stdin,
248
283
  output: process.stderr,
249
284
  });
250
- const answer = await new Promise((resolve) => rl.question(chalk.dim("\n Choice (1-5): "), (a) => {
285
+ const answer = await new Promise((resolve) => rl.question(chalk.dim("\n Choice (1-4): "), (a) => {
251
286
  rl.close();
252
287
  resolve(a.trim());
253
288
  }));
@@ -270,27 +305,34 @@ async function streamResponse(sseUrl, token, controller) {
270
305
  const text = result.result.content
271
306
  .map((c) => c.text)
272
307
  .join("\n");
273
- process.stdout.write(renderMarkdown(text));
308
+ await writeStdout(renderMarkdown(text));
274
309
  }
275
- console.error(chalk.green(`\n Tool ${decision === "deny" ? "denied" : "approved"} (${decision})\n`));
310
+ await writeStderr(chalk.green(`\n Tool ${decision === "deny" ? "denied" : "approved"} (${decision})\n`));
276
311
  }
277
312
  else {
278
- console.error(chalk.red(`\n Approval failed: ${await approveRes.text()}\n`));
313
+ await writeStderr(chalk.red(`\n Approval failed: ${await approveRes.text()}\n`));
279
314
  }
280
315
  break;
281
316
  }
282
317
  case "link-button":
283
318
  case "question":
284
319
  case "suggestion":
285
- console.error(JSON.stringify({ event: currentEvent, ...data }));
320
+ case "file-uploaded":
321
+ if (currentEvent === "file-uploaded") {
322
+ sawFileUploadedEvent = true;
323
+ }
324
+ await writeStderr(`${JSON.stringify({ event: currentEvent, ...data })}\n`);
286
325
  break;
287
326
  case "complete":
288
- process.stdout.write("\n");
327
+ await writeStdout("\n");
328
+ if (sawSandboxLink && !sawFileUploadedEvent) {
329
+ await writeStderr(chalk.red("\n Warning: assistant output contained a sandbox/local file link, but no file-uploaded event was emitted. Treat this as a failed file-delivery attempt.\n"));
330
+ }
289
331
  controller.abort();
290
332
  return;
291
333
  case "error":
292
- process.stdout.write("\n");
293
- console.error(chalk.red(`\n Agent error: ${String(data.error)}\n`));
334
+ await writeStdout("\n");
335
+ await writeStderr(chalk.red(`\n Agent error: ${String(data.error)}\n`));
294
336
  controller.abort();
295
337
  return;
296
338
  }