jeo-code 0.12.11 → 0.12.12

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/CHANGELOG.md CHANGED
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  The README mirrors the latest 5 entries — regenerate with `bun run changelog:sync`.
9
9
 
10
+ ## [0.12.12] - 2026-11-03
11
+
12
+ ### Fixed
13
+ - **Discord messenger progress** — show safe, throttled step and tool updates in a single request status message, retaining recent activity and marking completion, failure, or cancellation without exposing tool arguments or output.
14
+
10
15
  ## [0.12.10] - 2026-11-02
11
16
  ## [0.12.11] - 2026-11-02
12
17
 
package/README.ja.md CHANGED
@@ -422,11 +422,11 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
422
422
  ## 変更履歴 (Changelog)
423
423
 
424
424
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
425
+ - **[0.12.12]** (2026-11-03)
425
426
  - **[0.12.10]** (2026-11-02)
426
427
  - **[0.12.11]** (2026-11-02)
427
428
  - **[0.12.9]** (2026-11-02)
428
429
  - **[0.12.7]** (2026-10-18) — Repair desktop screenshot vision feedback, macOS scrolling, complete drag gestures, and keyboard normalization.
429
- - **[0.12.6]** (2026-10-17) — Preserve OpenAI subscription authentication behind configured proxies, isolate account catalogs, and give provider-specific recovery instructions while retaining Antigravity OAuth calls.
430
430
 
431
431
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
432
432
  <!-- CHANGELOG:END -->
package/README.ko.md CHANGED
@@ -477,11 +477,11 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
477
477
  ## 변경 이력 (Changelog)
478
478
 
479
479
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
480
+ - **[0.12.12]** (2026-11-03)
480
481
  - **[0.12.10]** (2026-11-02)
481
482
  - **[0.12.11]** (2026-11-02)
482
483
  - **[0.12.9]** (2026-11-02)
483
484
  - **[0.12.7]** (2026-10-18) — Repair desktop screenshot vision feedback, macOS scrolling, complete drag gestures, and keyboard normalization.
484
- - **[0.12.6]** (2026-10-17) — Preserve OpenAI subscription authentication behind configured proxies, isolate account catalogs, and give provider-specific recovery instructions while retaining Antigravity OAuth calls.
485
485
 
486
486
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
487
487
  <!-- CHANGELOG:END -->
package/README.md CHANGED
@@ -466,11 +466,11 @@ Huge thanks to [gajae-code](https://github.com/Yeachan-Heo/gajae-code) for the i
466
466
  ## Changelog
467
467
 
468
468
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
469
+ - **[0.12.12]** (2026-11-03)
469
470
  - **[0.12.10]** (2026-11-02)
470
471
  - **[0.12.11]** (2026-11-02)
471
472
  - **[0.12.9]** (2026-11-02)
472
473
  - **[0.12.7]** (2026-10-18) — Repair desktop screenshot vision feedback, macOS scrolling, complete drag gestures, and keyboard normalization.
473
- - **[0.12.6]** (2026-10-17) — Preserve OpenAI subscription authentication behind configured proxies, isolate account catalogs, and give provider-specific recovery instructions while retaining Antigravity OAuth calls.
474
474
 
475
475
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
476
476
  <!-- CHANGELOG:END -->
package/README.zh.md CHANGED
@@ -420,11 +420,11 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
420
420
  ## 更新日志 (Changelog)
421
421
 
422
422
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
423
+ - **[0.12.12]** (2026-11-03)
423
424
  - **[0.12.10]** (2026-11-02)
424
425
  - **[0.12.11]** (2026-11-02)
425
426
  - **[0.12.9]** (2026-11-02)
426
427
  - **[0.12.7]** (2026-10-18) — Repair desktop screenshot vision feedback, macOS scrolling, complete drag gestures, and keyboard normalization.
427
- - **[0.12.6]** (2026-10-17) — Preserve OpenAI subscription authentication behind configured proxies, isolate account catalogs, and give provider-specific recovery instructions while retaining Antigravity OAuth calls.
428
428
 
429
429
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
430
430
  <!-- CHANGELOG:END -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jeo-code",
3
- "version": "0.12.11",
3
+ "version": "0.12.12",
4
4
  "description": "Clean, highly optimized AI coding agent using spec-first loop",
5
5
  "type": "module",
6
6
 
@@ -114,7 +114,7 @@ import type { ImageAttachment } from "../ai/types";
114
114
  import { renderMarkdownTables } from "../tui/components/markdown-table";
115
115
 
116
116
  import { stripMarkdown } from "../tui/components/markdown-text";
117
- import { summarizeForgeInvocation } from "../tui/components/forge";
117
+ import { createMessengerActivityEvents } from "../messenger/activity";
118
118
  import { formatDuration, formatUsage } from "../tui/components/duration";
119
119
 
120
120
  import { bashTool } from "../agent/tools";
@@ -1034,28 +1034,9 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
1034
1034
  // `step N/M` display seed: the explicit --max-steps cap, else the dynamic budget's
1035
1035
  // rolling base — the engine's onBudget event keeps the denominator honest as it grows.
1036
1036
  const initialStepLimit = flags.maxSteps > 0 ? flags.maxSteps : initialDynamicStepLimit();
1037
- // Plain (non-TTY / --no-tui) progress sink — the cmd-mode equivalent of the live TUI.
1037
+ // Literal workers send progress without exposing tool arguments or output.
1038
1038
  const streamEvents = (flags.literalPrompt || flags.literalWorker)
1039
- ? {
1040
- onStep: () => {
1041
- process.stderr.write("JEO_MESSENGER_STEP\n");
1042
- },
1043
- onAssistant: (_raw: string, invocation: any) => {
1044
- if (invocation?.tool) {
1045
- const summary = summarizeForgeInvocation(invocation.tool, invocation.arguments);
1046
- process.stderr.write(`JEO_MESSENGER_ACTIVITY ${summary.title}\n`);
1047
- }
1048
- },
1049
- onToolResult: (tool: string) => {
1050
- process.stderr.write(`JEO_MESSENGER_ACTIVITY Finished ${tool}\n`);
1051
- },
1052
- onNotice: (msg: string) => {
1053
- process.stderr.write(`JEO_MESSENGER_ACTIVITY ${msg}\n`);
1054
- },
1055
- onReasoningStart: () => {
1056
- process.stderr.write("JEO_MESSENGER_ACTIVITY Thinking...\n");
1057
- },
1058
- }
1039
+ ? createMessengerActivityEvents(text => process.stderr.write(`JEO_MESSENGER_ACTIVITY ${text}\n`))
1059
1040
  : createStreamEvents(initialStepLimit);
1060
1041
  let queueBusyInput: ((chunk: string) => boolean) | undefined;
1061
1042
  let queueBusyPasteActive: (() => boolean) | undefined;
@@ -1,30 +1,26 @@
1
1
  import type { AgentLoopEvents, ToolInvocation } from "../agent/engine";
2
2
  import type { MessengerActivityReporter } from "./types";
3
3
 
4
- /**
5
- * Derive one-line progress from session events: the model's declared tool intent (falling back to the
6
- * tool name) and long-running lifecycle notices. Raw tool arguments, results and errors never surface.
7
- */
4
+ /** Derive bounded progress without exposing tool arguments, results, or provider errors. */
8
5
  export function createMessengerActivityEvents(report: MessengerActivityReporter): AgentLoopEvents {
9
6
  let step = 0;
10
7
  return {
8
+ onStep(current) {
9
+ step = current;
10
+ report(`Step ${step} · Thinking`);
11
+ },
11
12
  onAssistant(_raw: string, invocation: ToolInvocation | null) {
12
13
  if (!invocation || invocation.tool === "done") return;
13
- step++;
14
- const intent =
15
- invocation.arguments &&
16
- typeof invocation.arguments.i === "string" &&
17
- invocation.arguments.i.trim()
18
- ? invocation.arguments.i.trim()
19
- : `Using ${invocation.tool}`;
20
- report(`Step ${step} · ${intent}`);
14
+ const tool = /^[a-zA-Z0-9_-]{1,40}$/.test(invocation.tool) ? invocation.tool : "tool";
15
+ report(`Step ${step} · Running ${tool}`);
16
+ },
17
+ onToolResult(tool, success) {
18
+ const name = /^[a-zA-Z0-9_-]{1,40}$/.test(tool) ? tool : "tool";
19
+ report(`Step ${step} · ${name} ${success ? "finished" : "failed"}`);
21
20
  },
22
21
  onNotice(message: string) {
23
- if (/compact/i.test(message)) {
24
- report("Compacting context");
25
- } else if (/retry|rate limit/i.test(message)) {
26
- report("Retrying after a provider error");
27
- }
22
+ if (/compact/i.test(message)) report("Compacting context");
23
+ else if (/retry|rate limit/i.test(message)) report("Retrying after a provider error");
28
24
  },
29
25
  };
30
26
  }
@@ -344,6 +344,7 @@ export function createDiscordAdapter(
344
344
  }
345
345
  };
346
346
  const activityMessages = new Map<string, string>();
347
+ const activityHistory = new Map<string, string[]>();
347
348
  const headers = (): Record<string, string> => {
348
349
  if (!token) throw new DiscordConfigurationError("Discord bot token is missing");
349
350
  return { Authorization: `Bot ${token}`, "Content-Type": "application/json" };
@@ -531,43 +532,39 @@ export function createDiscordAdapter(
531
532
  deps.fetch,
532
533
  );
533
534
  },
534
- async activity(message, text, signal) {
535
+ async activity(message, text, signal, outcome = "success") {
535
536
  const channelId = message.threadId ?? message.channelId;
536
537
  const key = JSON.stringify([channelId, message.messageId]);
537
538
  const messages = `${DISCORD_API}/channels/${encodeURIComponent(channelId)}/messages`;
538
539
  const statusId = activityMessages.get(key);
540
+ const previous = activityHistory.get(key) ?? [];
539
541
  if (text === undefined) {
540
542
  if (!statusId) return;
543
+ const label = outcome === "success" ? "✅ Complete" : outcome === "failure" ? "❌ Failed" : "⏹ Cancelled";
541
544
  try {
542
545
  await messengerFetchJson(
543
546
  `${messages}/${encodeURIComponent(statusId)}`,
544
- {
545
- method: "PATCH",
546
- headers: headers(),
547
- body: JSON.stringify({ content: "✅ Complete", allowed_mentions: { parse: [] } }),
548
- },
549
- signal,
550
- deps.fetch,
547
+ { method: "PATCH", headers: headers(), body: JSON.stringify({ content: [label, ...previous].join("\n"), allowed_mentions: { parse: [] } }) },
548
+ signal, deps.fetch,
551
549
  );
552
550
  } catch (error) {
553
551
  if (!(error instanceof MessengerHttpError && error.status === 404)) throw error;
554
552
  }
555
553
  activityMessages.delete(key);
554
+ activityHistory.delete(key);
556
555
  return;
557
556
  }
558
- const content = `⏳ ${text}`;
557
+ // Keep only a short, bounded history of delivered updates in one editable message.
558
+ const history = [...previous, `• ${text}`].slice(-6);
559
+ const content = ["⏳ In progress", ...history].join("\n");
559
560
  if (statusId) {
560
561
  try {
561
562
  await messengerFetchJson(
562
563
  `${messages}/${encodeURIComponent(statusId)}`,
563
- {
564
- method: "PATCH",
565
- headers: headers(),
566
- body: JSON.stringify({ content, allowed_mentions: { parse: [] } }),
567
- },
568
- signal,
569
- deps.fetch,
564
+ { method: "PATCH", headers: headers(), body: JSON.stringify({ content, allowed_mentions: { parse: [] } }) },
565
+ signal, deps.fetch,
570
566
  );
567
+ activityHistory.set(key, history);
571
568
  return;
572
569
  } catch (error) {
573
570
  if (!(error instanceof MessengerHttpError && error.status === 404)) throw error;
@@ -576,17 +573,13 @@ export function createDiscordAdapter(
576
573
  }
577
574
  const created = await messengerFetchJson(
578
575
  messages,
579
- {
580
- method: "POST",
581
- headers: headers(),
582
- body: JSON.stringify({ content, allowed_mentions: { parse: [] }, flags: DISCORD_ACTIVITY_FLAGS }),
583
- },
584
- signal,
585
- deps.fetch,
576
+ { method: "POST", headers: headers(), body: JSON.stringify({ content, allowed_mentions: { parse: [] }, flags: DISCORD_ACTIVITY_FLAGS }) },
577
+ signal, deps.fetch,
586
578
  );
587
579
  if (!isRecord(created) || typeof created.id !== "string" || !created.id)
588
580
  throw new Error("Discord activity message returned no ID");
589
581
  activityMessages.set(key, created.id);
582
+ activityHistory.set(key, history);
590
583
  },
591
584
  async send(message, text, signal) {
592
585
  let files: { name: string; data: Uint8Array }[] = [];
@@ -33,8 +33,8 @@ export function activityLine(text: string): string {
33
33
 
34
34
  export interface ActivityReporter {
35
35
  report: MessengerActivityReporter;
36
- /** Stop updates and remove any shown line; idempotent and never rejects. */
37
- close(): Promise<void>;
36
+ /** Stop updates and finalize the status; idempotent and never rejects. */
37
+ close(outcome?: "success" | "failure" | "cancelled"): Promise<void>;
38
38
  }
39
39
 
40
40
  const NO_ACTIVITY: ActivityReporter = { report() {}, close: () => Promise.resolve() };
@@ -132,7 +132,7 @@ export function createActivityReporter(
132
132
  pumping = true;
133
133
  pump = run();
134
134
  },
135
- close() {
135
+ close(outcome = "success") {
136
136
  closing ??= (async () => {
137
137
  closed = true;
138
138
  wake?.();
@@ -140,7 +140,7 @@ export function createActivityReporter(
140
140
  if (!shown) return;
141
141
  for (let attempt = 0; ; attempt++) {
142
142
  try {
143
- await boundedActivity(bounded => activity.call(adapter, destination, undefined, bounded));
143
+ await boundedActivity(bounded => activity.call(adapter, destination, undefined, bounded, outcome));
144
144
  return;
145
145
  } catch (error) {
146
146
  const wait =
@@ -340,6 +340,7 @@ export function createMessengerGateway(
340
340
  conversationTails.set(conversation, tail);
341
341
  await previous;
342
342
  activity = createActivityReporter(adapter, destination, signal, activityIntervalMs);
343
+ activity.report("Starting request");
343
344
  let text: string;
344
345
  try {
345
346
  text = await reply(destination, signal, activity.report);
@@ -353,9 +354,6 @@ export function createMessengerGateway(
353
354
  }
354
355
  if (signal.aborted) return;
355
356
  text = "The request could not be completed. Please try again.";
356
- } finally {
357
- // Retire the status line alongside reply delivery rather than delaying the reply on it.
358
- void activity.close();
359
357
  }
360
358
  if (!signal.aborted && text.trim()) {
361
359
  // A failed send may already have delivered a chunk: never retry the entire reply.
@@ -363,9 +361,10 @@ export function createMessengerGateway(
363
361
  }
364
362
  if (!signal.aborted) status(failed ? "failure" : "success");
365
363
  } catch {
364
+ failed = true;
366
365
  if (!signal.aborted) status("failure");
367
366
  } finally {
368
- await activity.close();
367
+ await activity.close(failed ? "failure" : signal.aborted ? "cancelled" : "success");
369
368
  release();
370
369
  if (tail && conversationTails.get(conversation) === tail) conversationTails.delete(conversation);
371
370
  resolve();
@@ -31,11 +31,8 @@ export interface MessengerAdapter {
31
31
  status: "processing" | "success" | "failure",
32
32
  signal?: AbortSignal,
33
33
  ): Promise<void>;
34
- /**
35
- * Show `text` (one sanitized line) as the request's single in-progress status, editing it in place on
36
- * later calls; `undefined` removes it. The gateway serializes and throttles calls per request.
37
- */
38
- activity?(message: MessengerMessage, text: string | undefined, signal?: AbortSignal): Promise<void>;
34
+ /** Update the request's progress; undefined finishes it with the supplied outcome. */
35
+ activity?(message: MessengerMessage, text: string | undefined, signal?: AbortSignal, outcome?: "success" | "failure" | "cancelled"): Promise<void>;
39
36
  }
40
37
 
41
38
  export interface MessengerPlatformConfig {