@gajae-code/coding-agent 0.12.11 → 0.12.13

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 (140) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/types/cli/marketplace-hint.d.ts +28 -0
  3. package/dist/types/cli/notify-cli.d.ts +42 -1
  4. package/dist/types/cli/update-cli.d.ts +9 -3
  5. package/dist/types/cli.d.ts +10 -0
  6. package/dist/types/commands/notify.d.ts +6 -0
  7. package/dist/types/config/file-lock-gc.d.ts +13 -1
  8. package/dist/types/config/file-lock.d.ts +3 -0
  9. package/dist/types/config/model-registry.d.ts +6 -0
  10. package/dist/types/config/models-config-schema.d.ts +99 -0
  11. package/dist/types/coordinator/contract.d.ts +1 -1
  12. package/dist/types/coordinator-mcp/question-state.d.ts +3 -1
  13. package/dist/types/extensibility/extensions/index.d.ts +1 -0
  14. package/dist/types/extensibility/extensions/ouroboros-ooo-bridge.d.ts +13 -0
  15. package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +0 -1
  16. package/dist/types/extensibility/extensions/types.d.ts +2 -0
  17. package/dist/types/gjc-runtime/deep-interview-state.d.ts +14 -0
  18. package/dist/types/gjc-runtime/gc-runtime.d.ts +11 -0
  19. package/dist/types/main.d.ts +6 -2
  20. package/dist/types/modes/acp/acp-agent.d.ts +24 -1
  21. package/dist/types/modes/components/session-selector.d.ts +1 -1
  22. package/dist/types/modes/utils/host-status.d.ts +23 -0
  23. package/dist/types/runtime-mcp/types.d.ts +2 -0
  24. package/dist/types/sdk/broker/lifecycle.d.ts +12 -0
  25. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -0
  26. package/dist/types/sdk/bus/chat-daemon-command-channel.d.ts +244 -0
  27. package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -1
  28. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +7 -0
  29. package/dist/types/sdk/bus/control-plane-frames.d.ts +43 -0
  30. package/dist/types/sdk/bus/conversation-store.d.ts +18 -0
  31. package/dist/types/sdk/bus/existing-thread-readiness.d.ts +71 -0
  32. package/dist/types/sdk/bus/html-format.d.ts +10 -0
  33. package/dist/types/sdk/bus/notification-service.d.ts +10 -0
  34. package/dist/types/sdk/bus/slack-daemon.d.ts +27 -0
  35. package/dist/types/sdk/bus/slack-live-provider.d.ts +11 -0
  36. package/dist/types/sdk/bus/slack-provider.d.ts +14 -0
  37. package/dist/types/sdk/bus/slack-thread-binding.d.ts +140 -0
  38. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +2 -0
  39. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +28 -7
  40. package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -17
  41. package/dist/types/sdk/bus/telegram-reference.d.ts +20 -1
  42. package/dist/types/sdk/bus/topic-registry.d.ts +130 -85
  43. package/dist/types/sdk/host/host.d.ts +52 -0
  44. package/dist/types/sdk/lifecycle-session.d.ts +6 -0
  45. package/dist/types/sdk/session-activation.d.ts +41 -0
  46. package/dist/types/sdk/session.d.ts +18 -0
  47. package/dist/types/sdk/startup-capability.d.ts +8 -1
  48. package/dist/types/session/agent-session.d.ts +7 -2
  49. package/dist/types/session/artifacts.d.ts +2 -2
  50. package/dist/types/session/session-manager.d.ts +40 -6
  51. package/dist/types/tools/index.d.ts +15 -0
  52. package/dist/types/utils/npm-registry.d.ts +71 -0
  53. package/examples/extensions/README.md +38 -4
  54. package/examples/extensions/ooo-bridge.ts +15 -0
  55. package/package.json +8 -8
  56. package/scripts/generate-sdk-operation-inventory.ts +4 -0
  57. package/src/async/job-manager.ts +9 -2
  58. package/src/cli/fast-help.ts +1 -0
  59. package/src/cli/marketplace-hint.ts +39 -0
  60. package/src/cli/notify-cli.ts +226 -2
  61. package/src/cli/plugin-cli.ts +10 -0
  62. package/src/cli/update-cli.ts +90 -23
  63. package/src/cli.ts +23 -0
  64. package/src/commands/notify.ts +27 -3
  65. package/src/commands/sdk.ts +1 -0
  66. package/src/config/file-lock-gc.ts +65 -26
  67. package/src/config/file-lock.ts +61 -16
  68. package/src/config/model-profiles.ts +46 -30
  69. package/src/config/model-registry.ts +17 -5
  70. package/src/config/models-config-schema.ts +9 -4
  71. package/src/coordinator/contract.ts +1 -0
  72. package/src/coordinator-mcp/question-state.ts +2 -0
  73. package/src/coordinator-mcp/server.ts +358 -7
  74. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  75. package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -0
  76. package/src/extensibility/extensions/index.ts +1 -0
  77. package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +235 -0
  78. package/src/extensibility/extensions/prefix-command-bridge.ts +4 -9
  79. package/src/extensibility/extensions/runner.ts +15 -2
  80. package/src/extensibility/extensions/types.ts +2 -0
  81. package/src/gjc-runtime/deep-interview-recorder.ts +9 -5
  82. package/src/gjc-runtime/deep-interview-state.ts +27 -3
  83. package/src/gjc-runtime/gc-render.ts +8 -1
  84. package/src/gjc-runtime/gc-runtime.ts +15 -1
  85. package/src/gjc-runtime/launch-tmux.ts +53 -1
  86. package/src/internal-urls/docs-index.generated.ts +14 -13
  87. package/src/main.ts +214 -70
  88. package/src/modes/DESIGN.md +8 -5
  89. package/src/modes/acp/acp-agent.ts +123 -7
  90. package/src/modes/components/footer.ts +10 -18
  91. package/src/modes/components/model-selector.ts +52 -5
  92. package/src/modes/components/session-selector.ts +26 -3
  93. package/src/modes/components/status-line/gh.ts +23 -2
  94. package/src/modes/controllers/event-controller.ts +3 -0
  95. package/src/modes/controllers/extension-ui-controller.ts +2 -0
  96. package/src/modes/controllers/input-controller.ts +11 -0
  97. package/src/modes/controllers/selector-controller.ts +8 -2
  98. package/src/modes/interactive-mode.ts +7 -1
  99. package/src/modes/print-mode.ts +10 -0
  100. package/src/modes/utils/host-status.ts +38 -0
  101. package/src/prompts/system/project-prompt.md +19 -0
  102. package/src/runtime-mcp/types.ts +2 -0
  103. package/src/sdk/broker/lifecycle.ts +60 -3
  104. package/src/sdk/bus/chat-daemon-cli.ts +42 -0
  105. package/src/sdk/bus/chat-daemon-command-channel.ts +921 -0
  106. package/src/sdk/bus/chat-daemon-control.ts +16 -3
  107. package/src/sdk/bus/chat-daemon-runtime.ts +257 -21
  108. package/src/sdk/bus/control-plane-frames.ts +51 -0
  109. package/src/sdk/bus/conversation-store.ts +29 -1
  110. package/src/sdk/bus/existing-thread-readiness.ts +155 -0
  111. package/src/sdk/bus/html-format.ts +19 -3
  112. package/src/sdk/bus/index.ts +91 -12
  113. package/src/sdk/bus/notification-service.ts +28 -1
  114. package/src/sdk/bus/rich-render.ts +3 -2
  115. package/src/sdk/bus/slack-daemon.ts +99 -4
  116. package/src/sdk/bus/slack-live-provider.ts +32 -2
  117. package/src/sdk/bus/slack-provider.ts +11 -0
  118. package/src/sdk/bus/slack-thread-binding.ts +641 -0
  119. package/src/sdk/bus/telegram-daemon-cli.ts +11 -0
  120. package/src/sdk/bus/telegram-daemon-contract.ts +28 -7
  121. package/src/sdk/bus/telegram-daemon.ts +2007 -509
  122. package/src/sdk/bus/telegram-reference.ts +114 -15
  123. package/src/sdk/bus/topic-registry.ts +674 -181
  124. package/src/sdk/host/host.ts +180 -1
  125. package/src/sdk/lifecycle-session.ts +8 -2
  126. package/src/sdk/protocol/operation-inventory.generated.json +22 -0
  127. package/src/sdk/session-activation.ts +127 -0
  128. package/src/sdk/session.ts +144 -29
  129. package/src/sdk/startup-capability.ts +10 -1
  130. package/src/session/agent-session.ts +220 -13
  131. package/src/session/artifacts.ts +56 -13
  132. package/src/session/internal/managed-session-storage.ts +228 -7
  133. package/src/session/session-manager.ts +225 -60
  134. package/src/setup/provider-presets.json +3 -3
  135. package/src/task/index.ts +92 -44
  136. package/src/thinking-metadata.ts +2 -1
  137. package/src/tools/ask.ts +10 -0
  138. package/src/tools/index.ts +15 -0
  139. package/src/tools/todo-write.ts +4 -4
  140. package/src/utils/npm-registry.ts +660 -0
@@ -14,12 +14,15 @@
14
14
  * Dependency-free: uses global `fetch` and `WebSocket` (Bun/Node 22+).
15
15
  */
16
16
 
17
+ import * as crypto from "node:crypto";
17
18
  import * as fs from "node:fs";
19
+ import { logger } from "@gajae-code/utils";
18
20
  import {
19
21
  bold,
20
22
  buildCompactChoiceGrid,
21
23
  escapeHtml,
22
24
  numberedOptionList,
25
+ renumberableOptionText,
23
26
  splitTelegramHtml,
24
27
  TELEGRAM_PARSE_MODE,
25
28
  } from "./html-format";
@@ -33,6 +36,44 @@ const REFERENCE_CLIENT_HELLO = {
33
36
  capabilities: ["ask_controls_v1"],
34
37
  } as const;
35
38
 
39
+ const MAX_DIAGNOSTIC_CAPABILITIES = 4;
40
+ const MAX_DIAGNOSTIC_CAPABILITY_CODE_POINTS = 64;
41
+ const MAX_DIAGNOSTIC_ERROR_NAME_CODE_POINTS = 64;
42
+ const MAX_DIAGNOSTIC_ERROR_MESSAGE_CODE_POINTS = 128;
43
+
44
+ function boundedDiagnosticText(value: string, maxCodePoints: number): string {
45
+ let result = "";
46
+ let codePoints = 0;
47
+ for (const character of value) {
48
+ const codePoint = character.codePointAt(0)!;
49
+ if (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f)) continue;
50
+ result += character;
51
+ codePoints++;
52
+ if (codePoints === maxCodePoints) break;
53
+ }
54
+ return result;
55
+ }
56
+
57
+ function boundedDiagnosticCallbackError(error: unknown): Readonly<{ errorName: string; errorMessage: string }> {
58
+ let errorName = "UnknownError";
59
+ let errorMessage = "Diagnostic callback threw";
60
+ try {
61
+ if (error instanceof Error) {
62
+ errorName = boundedDiagnosticText(error.name, MAX_DIAGNOSTIC_ERROR_NAME_CODE_POINTS) || "Error";
63
+ errorMessage =
64
+ boundedDiagnosticText(error.message, MAX_DIAGNOSTIC_ERROR_MESSAGE_CODE_POINTS) ||
65
+ "Diagnostic callback threw";
66
+ } else {
67
+ errorMessage =
68
+ boundedDiagnosticText(String(error), MAX_DIAGNOSTIC_ERROR_MESSAGE_CODE_POINTS) ||
69
+ "Diagnostic callback threw";
70
+ }
71
+ } catch {
72
+ // Hostile thrown values must not escape the diagnostic failure boundary.
73
+ }
74
+ return Object.freeze({ errorName, errorMessage });
75
+ }
76
+
36
77
  /** One inline-keyboard button. */
37
78
  export interface InlineButton {
38
79
  text: string;
@@ -85,18 +126,27 @@ export interface CallbackRoute {
85
126
  sessionId: string;
86
127
  actionId: string;
87
128
  answer: TelegramCallbackAnswer;
129
+ /** Durable audit metadata only; never sufficient to route a callback after restart. */
130
+ chatId?: string;
131
+ messageId?: number;
132
+ ownerId?: string;
133
+ generation?: number;
88
134
  }
89
135
 
90
136
  export interface SerializedAliasTable {
91
- version: 1;
137
+ version: 1 | 2;
92
138
  next: number;
93
139
  routes: Record<string, CallbackRoute>;
94
140
  }
95
141
 
96
142
  export interface AliasTable {
143
+ allocate(isReserved?: (alias: string) => boolean): string;
144
+ activate(alias: string, route: CallbackRoute): boolean;
97
145
  put(route: CallbackRoute): string;
98
146
  get(alias: string): CallbackRoute | undefined;
147
+ update(alias: string, patch: Partial<CallbackRoute>): boolean;
99
148
  delete(alias: string): boolean;
149
+ clear(): void;
100
150
  serialize(): SerializedAliasTable;
101
151
  load(json: unknown): void;
102
152
  entries(): Array<[string, CallbackRoute]>;
@@ -112,7 +162,11 @@ function isCallbackRoute(value: unknown): value is CallbackRoute {
112
162
  typeof route.answer === "number" ||
113
163
  (typeof route.answer === "object" &&
114
164
  route.answer !== null &&
115
- (route.answer as { controlId?: unknown }).controlId === "navigation_forward"))
165
+ (route.answer as { controlId?: unknown }).controlId === "navigation_forward")) &&
166
+ (route.chatId === undefined || typeof route.chatId === "string") &&
167
+ (route.messageId === undefined || (Number.isSafeInteger(route.messageId) && route.messageId > 0)) &&
168
+ (route.ownerId === undefined || typeof route.ownerId === "string") &&
169
+ (route.generation === undefined || (Number.isSafeInteger(route.generation) && route.generation >= 0))
116
170
  );
117
171
  }
118
172
 
@@ -121,24 +175,46 @@ export function createAliasTable(): AliasTable {
121
175
  let next = 1;
122
176
  const routes = new Map<string, CallbackRoute>();
123
177
  return {
124
- put(route) {
178
+ allocate(isReserved) {
125
179
  let alias: string;
126
180
  do {
127
- alias = `a${(next++).toString(36)}`;
128
- } while (routes.has(alias));
181
+ // `b1_` is intentionally disjoint from every legacy sequential `aN`
182
+ // token, including accepted keyboards lost before their old daemon
183
+ // could persist them.
184
+ alias = `b1_${crypto.randomBytes(18).toString("base64url")}`;
185
+ } while (routes.has(alias) || isReserved?.(alias) === true);
186
+ next++;
129
187
  if (Buffer.byteLength(alias, "utf8") > 64) throw new Error("callback alias exceeded Telegram limit");
188
+ return alias;
189
+ },
190
+ activate(alias, route) {
191
+ if (routes.has(alias) || Buffer.byteLength(alias, "utf8") > 64 || !isCallbackRoute(route)) return false;
130
192
  routes.set(alias, { ...route });
193
+ return true;
194
+ },
195
+ put(route) {
196
+ const alias = this.allocate();
197
+ if (!this.activate(alias, route)) throw new Error("callback alias activation failed");
131
198
  return alias;
132
199
  },
133
200
  get(alias) {
134
201
  const route = routes.get(alias);
135
202
  return route ? { ...route } : undefined;
136
203
  },
204
+ update(alias, patch) {
205
+ const route = routes.get(alias);
206
+ if (!route) return false;
207
+ routes.set(alias, { ...route, ...patch });
208
+ return true;
209
+ },
137
210
  delete(alias) {
138
211
  return routes.delete(alias);
139
212
  },
213
+ clear() {
214
+ routes.clear();
215
+ },
140
216
  serialize() {
141
- return { version: 1, next, routes: Object.fromEntries(routes.entries()) };
217
+ return { version: 2, next, routes: Object.fromEntries(routes.entries()) };
142
218
  },
143
219
  load(json) {
144
220
  routes.clear();
@@ -218,7 +294,7 @@ export function buildActionMarkdown(action: {
218
294
  const options = action.options ?? [];
219
295
  const displayOptions = withRecommendedOptionLabel(options, action.recommendedIndex);
220
296
  if (options.length === 0) return `${heading}\n\n(reply with text)`;
221
- const list = displayOptions.map((label, i) => `${i + 1}. ${label.replace(/^\s*\d+[.)]\s+/, "")}`).join("\n");
297
+ const list = displayOptions.map((label, i) => `${i + 1}. ${renumberableOptionText(label)}`).join("\n");
222
298
  return `${heading}\n\n${list}`;
223
299
  }
224
300
 
@@ -370,6 +446,12 @@ export function readEndpoint(path: string): { url: string; token: string; pid?:
370
446
  };
371
447
  }
372
448
 
449
+ /** A bounded, diagnostic-only notification mirrored to reference-client embedders. */
450
+ export type TelegramReferenceDiagnostic = Readonly<{
451
+ code: "action_unavailable";
452
+ requiredCapabilities: readonly string[];
453
+ }>;
454
+
373
455
  /** Options for {@link runTelegramReferenceClient}. */
374
456
  export interface TelegramReferenceOptions {
375
457
  botToken: string;
@@ -378,6 +460,11 @@ export interface TelegramReferenceOptions {
378
460
  apiBase?: string;
379
461
  fetchImpl?: typeof fetch;
380
462
  sound?: TelegramNotificationSound;
463
+ /**
464
+ * Additive mirror of bounded logger diagnostics. It never receives tokens,
465
+ * identifiers, raw frames, Telegram content, or WebSocket replies.
466
+ */
467
+ onDiagnostic?: (diagnostic: TelegramReferenceDiagnostic) => void;
381
468
  }
382
469
 
383
470
  /**
@@ -434,15 +521,27 @@ export async function runTelegramReferenceClient(opts: TelegramReferenceOptions)
434
521
  msg.kind ?? "ask",
435
522
  );
436
523
  } else if (msg.type === "action_unavailable") {
437
- const requiredCapabilities = Array.isArray(msg.requiredCapabilities)
438
- ? msg.requiredCapabilities
439
- .filter((capability): capability is string => typeof capability === "string")
440
- .slice(0, 4)
441
- .map(capability => capability.slice(0, 64))
442
- : [];
443
- console.warn(
444
- `Telegram reference client: server withheld a controlled ask because this client lacks requiredCapabilities=[${requiredCapabilities.join(", ") || "unspecified"}].`,
524
+ const requiredCapabilities = Object.freeze(
525
+ Array.isArray(msg.requiredCapabilities)
526
+ ? msg.requiredCapabilities
527
+ .filter((capability): capability is string => typeof capability === "string")
528
+ .slice(0, MAX_DIAGNOSTIC_CAPABILITIES)
529
+ .map(capability => boundedDiagnosticText(capability, MAX_DIAGNOSTIC_CAPABILITY_CODE_POINTS))
530
+ : [],
445
531
  );
532
+ const diagnostic: TelegramReferenceDiagnostic = Object.freeze({
533
+ code: "action_unavailable",
534
+ requiredCapabilities,
535
+ });
536
+ logger.warn("Telegram reference client action unavailable", diagnostic);
537
+ try {
538
+ opts.onDiagnostic?.(diagnostic);
539
+ } catch (error) {
540
+ logger.warn("Telegram reference client diagnostic callback failed", {
541
+ code: "diagnostic_callback_failed",
542
+ ...boundedDiagnosticCallbackError(error),
543
+ });
544
+ }
446
545
  // Diagnostic only: never turn it into a Telegram prompt or option buttons.
447
546
  return;
448
547
  } else if (msg.type === "action_resolved" && msg.id === latestPendingAskId) {