@gajae-code/coding-agent 0.12.10 → 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.
Files changed (120) hide show
  1. package/CHANGELOG.md +47 -2
  2. package/dist/types/cli/notify-cli.d.ts +42 -1
  3. package/dist/types/cli/update-cli.d.ts +9 -3
  4. package/dist/types/commands/notify.d.ts +6 -0
  5. package/dist/types/config/file-lock.d.ts +3 -0
  6. package/dist/types/coordinator/contract.d.ts +1 -1
  7. package/dist/types/coordinator-mcp/question-state.d.ts +3 -1
  8. package/dist/types/extensibility/extensions/index.d.ts +1 -0
  9. package/dist/types/extensibility/extensions/ouroboros-ooo-bridge.d.ts +13 -0
  10. package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +0 -1
  11. package/dist/types/extensibility/extensions/types.d.ts +2 -0
  12. package/dist/types/main.d.ts +6 -2
  13. package/dist/types/modes/components/session-selector.d.ts +1 -1
  14. package/dist/types/modes/utils/host-status.d.ts +23 -0
  15. package/dist/types/runtime-mcp/types.d.ts +2 -0
  16. package/dist/types/sdk/broker/lifecycle.d.ts +12 -0
  17. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -0
  18. package/dist/types/sdk/bus/chat-daemon-command-channel.d.ts +244 -0
  19. package/dist/types/sdk/bus/chat-daemon-control.d.ts +13 -1
  20. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +7 -0
  21. package/dist/types/sdk/bus/control-plane-frames.d.ts +43 -0
  22. package/dist/types/sdk/bus/conversation-store.d.ts +18 -0
  23. package/dist/types/sdk/bus/existing-thread-readiness.d.ts +71 -0
  24. package/dist/types/sdk/bus/notification-service.d.ts +10 -0
  25. package/dist/types/sdk/bus/slack-daemon.d.ts +27 -0
  26. package/dist/types/sdk/bus/slack-live-provider.d.ts +11 -0
  27. package/dist/types/sdk/bus/slack-provider.d.ts +14 -0
  28. package/dist/types/sdk/bus/slack-thread-binding.d.ts +140 -0
  29. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +2 -0
  30. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +25 -7
  31. package/dist/types/sdk/bus/telegram-daemon.d.ts +104 -16
  32. package/dist/types/sdk/bus/telegram-reference.d.ts +10 -1
  33. package/dist/types/sdk/bus/topic-registry.d.ts +130 -85
  34. package/dist/types/sdk/host/host.d.ts +52 -0
  35. package/dist/types/sdk/lifecycle-session.d.ts +6 -0
  36. package/dist/types/sdk/session-activation.d.ts +41 -0
  37. package/dist/types/sdk/session.d.ts +18 -0
  38. package/dist/types/sdk/startup-capability.d.ts +8 -1
  39. package/dist/types/session/agent-session.d.ts +7 -0
  40. package/dist/types/session/artifacts.d.ts +2 -2
  41. package/dist/types/session/internal/managed-session-storage.d.ts +12 -2
  42. package/dist/types/session/session-manager.d.ts +40 -5
  43. package/dist/types/session/session-storage.d.ts +1 -0
  44. package/dist/types/tools/computer.d.ts +35 -2
  45. package/dist/types/tools/index.d.ts +8 -0
  46. package/dist/types/tools/todo-write.d.ts +2 -0
  47. package/dist/types/utils/npm-registry.d.ts +71 -0
  48. package/examples/extensions/README.md +38 -4
  49. package/examples/extensions/ooo-bridge.ts +15 -0
  50. package/package.json +8 -8
  51. package/scripts/generate-sdk-operation-inventory.ts +4 -0
  52. package/src/async/job-manager.ts +9 -2
  53. package/src/cli/notify-cli.ts +226 -2
  54. package/src/cli/update-cli.ts +90 -23
  55. package/src/commands/notify.ts +27 -3
  56. package/src/commands/sdk.ts +1 -0
  57. package/src/config/file-lock-gc.ts +18 -0
  58. package/src/config/file-lock.ts +61 -16
  59. package/src/config/model-profiles.ts +31 -15
  60. package/src/coordinator/contract.ts +1 -0
  61. package/src/coordinator-mcp/question-state.ts +2 -0
  62. package/src/coordinator-mcp/server.ts +358 -7
  63. package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -0
  64. package/src/extensibility/extensions/index.ts +1 -0
  65. package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +235 -0
  66. package/src/extensibility/extensions/prefix-command-bridge.ts +4 -9
  67. package/src/extensibility/extensions/runner.ts +15 -2
  68. package/src/extensibility/extensions/types.ts +2 -0
  69. package/src/gjc-runtime/launch-tmux.ts +53 -1
  70. package/src/internal-urls/docs-index.generated.ts +13 -12
  71. package/src/main.ts +203 -67
  72. package/src/modes/DESIGN.md +8 -5
  73. package/src/modes/components/footer.ts +10 -18
  74. package/src/modes/components/session-selector.ts +26 -3
  75. package/src/modes/components/status-line/gh.ts +23 -2
  76. package/src/modes/controllers/event-controller.ts +3 -0
  77. package/src/modes/controllers/extension-ui-controller.ts +2 -0
  78. package/src/modes/controllers/input-controller.ts +11 -0
  79. package/src/modes/controllers/selector-controller.ts +8 -2
  80. package/src/modes/interactive-mode.ts +7 -1
  81. package/src/modes/utils/host-status.ts +38 -0
  82. package/src/prompts/tools/computer.md +6 -1
  83. package/src/runtime-mcp/types.ts +2 -0
  84. package/src/sdk/broker/lifecycle.ts +60 -3
  85. package/src/sdk/bus/chat-daemon-cli.ts +42 -0
  86. package/src/sdk/bus/chat-daemon-command-channel.ts +921 -0
  87. package/src/sdk/bus/chat-daemon-control.ts +18 -3
  88. package/src/sdk/bus/chat-daemon-runtime.ts +257 -21
  89. package/src/sdk/bus/control-plane-frames.ts +51 -0
  90. package/src/sdk/bus/conversation-store.ts +29 -1
  91. package/src/sdk/bus/existing-thread-readiness.ts +155 -0
  92. package/src/sdk/bus/index.ts +70 -0
  93. package/src/sdk/bus/notification-service.ts +28 -1
  94. package/src/sdk/bus/rich-render.ts +3 -2
  95. package/src/sdk/bus/slack-daemon.ts +99 -4
  96. package/src/sdk/bus/slack-live-provider.ts +32 -2
  97. package/src/sdk/bus/slack-provider.ts +11 -0
  98. package/src/sdk/bus/slack-thread-binding.ts +641 -0
  99. package/src/sdk/bus/telegram-daemon-cli.ts +11 -0
  100. package/src/sdk/bus/telegram-daemon-contract.ts +25 -7
  101. package/src/sdk/bus/telegram-daemon.ts +1961 -497
  102. package/src/sdk/bus/telegram-reference.ts +42 -6
  103. package/src/sdk/bus/topic-registry.ts +674 -181
  104. package/src/sdk/host/host.ts +180 -1
  105. package/src/sdk/lifecycle-session.ts +8 -2
  106. package/src/sdk/protocol/operation-inventory.generated.json +22 -0
  107. package/src/sdk/session-activation.ts +127 -0
  108. package/src/sdk/session.ts +139 -28
  109. package/src/sdk/startup-capability.ts +10 -1
  110. package/src/session/agent-session.ts +331 -30
  111. package/src/session/artifacts.ts +56 -13
  112. package/src/session/internal/managed-session-storage.ts +440 -243
  113. package/src/session/session-manager.ts +288 -44
  114. package/src/session/session-storage.ts +3 -0
  115. package/src/setup/provider-presets.json +1 -1
  116. package/src/task/index.ts +92 -44
  117. package/src/tools/computer.ts +322 -45
  118. package/src/tools/index.ts +8 -0
  119. package/src/tools/todo-write.ts +29 -11
  120. package/src/utils/npm-registry.ts +660 -0
@@ -1,3 +1,4 @@
1
+ import * as crypto from "node:crypto";
1
2
  import type { Stats } from "node:fs";
2
3
  import * as fs from "node:fs/promises";
3
4
  import * as path from "node:path";
@@ -7,9 +8,11 @@ export interface FileLockOptions {
7
8
  staleMs?: number;
8
9
  retries?: number;
9
10
  retryDelayMs?: number;
11
+ /** Stable host identity required to safely reclaim locks on a shared volume. */
12
+ ownerHostId?: string;
10
13
  }
11
14
 
12
- const DEFAULT_OPTIONS: Required<FileLockOptions> = {
15
+ const DEFAULT_OPTIONS: Required<Omit<FileLockOptions, "ownerHostId">> = {
13
16
  staleMs: 10_000,
14
17
  retries: 50,
15
18
  retryDelayMs: 100,
@@ -61,8 +64,16 @@ function ownerIsAlive(owner: FileLockOwnerToken, startTimeCache?: Map<string, st
61
64
  return currentStartTime === null || currentStartTime === owner.start_time;
62
65
  }
63
66
 
64
- function writeLockInfo(lockPath: string): Promise<LockInfo> {
65
- const info: LockInfo = { pid: process.pid, start_time: currentProcessStartTime(), timestamp: Date.now() };
67
+ function lockInfo(ownerHostId?: string): LockInfo {
68
+ return {
69
+ pid: process.pid,
70
+ start_time: currentProcessStartTime(),
71
+ timestamp: Date.now(),
72
+ ...(ownerHostId === undefined ? {} : { owner_host_id: ownerHostId }),
73
+ };
74
+ }
75
+
76
+ function writeLockInfo(lockPath: string, info: LockInfo): Promise<LockInfo> {
66
77
  return Bun.write(`${lockPath}/info`, JSON.stringify(info)).then(() => info);
67
78
  }
68
79
 
@@ -76,17 +87,18 @@ async function readLockInfo(lockPath: string): Promise<LockInfo | null> {
76
87
  }
77
88
 
78
89
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
79
- const { pid, start_time, timestamp } = parsed as Partial<LockInfo>;
90
+ const { pid, start_time, timestamp, owner_host_id } = parsed as Partial<LockInfo>;
80
91
  if (
81
92
  typeof pid !== "number" ||
82
93
  !Number.isInteger(pid) ||
83
94
  pid <= 0 ||
84
95
  typeof timestamp !== "number" ||
85
96
  !Number.isFinite(timestamp) ||
86
- (start_time !== undefined && (typeof start_time !== "string" || !start_time))
97
+ (start_time !== undefined && (typeof start_time !== "string" || !start_time)) ||
98
+ (owner_host_id !== undefined && (typeof owner_host_id !== "string" || !owner_host_id))
87
99
  )
88
100
  return null;
89
- return { pid, start_time, timestamp };
101
+ return { pid, start_time, timestamp, owner_host_id };
90
102
  }
91
103
 
92
104
  /** @internal */
@@ -98,7 +110,7 @@ export async function readFileLockInfoForGc(lockDir: string): Promise<FileLockOw
98
110
  export interface FileLockOwnerToken {
99
111
  pid: number;
100
112
  start_time?: string;
101
-
113
+ owner_host_id?: string;
102
114
  timestamp: number;
103
115
  }
104
116
 
@@ -147,6 +159,7 @@ export async function removeFileLockDirForGc(
147
159
  if (
148
160
  current.pid !== expected.pid ||
149
161
  (expected.start_time !== undefined && current.start_time !== expected.start_time) ||
162
+ current.owner_host_id !== expected.owner_host_id ||
150
163
  current.timestamp !== expected.timestamp
151
164
  ) {
152
165
  return "owner_changed";
@@ -187,6 +200,7 @@ function sameStatToken(a: LockDirStatToken, b: LockDirStatToken): boolean {
187
200
  async function staleLockSnapshot(
188
201
  lockPath: string,
189
202
  staleMs: number,
203
+ ownerHostId?: string,
190
204
  startTimeCache?: Map<string, string | null>,
191
205
  ): Promise<LockStaleSnapshot> {
192
206
  let info: LockInfo | null;
@@ -199,6 +213,7 @@ async function staleLockSnapshot(
199
213
  if (hasFsCode(error, "EPERM")) return { stale: false };
200
214
  throw error;
201
215
  }
216
+ if (!info && ownerHostId !== undefined) return { stale: false };
202
217
  if (!info) {
203
218
  try {
204
219
  const stats = await fs.stat(lockPath);
@@ -210,6 +225,10 @@ async function staleLockSnapshot(
210
225
  }
211
226
  }
212
227
 
228
+ // A host-qualified lock may only be reclaimed after proving that its owner is
229
+ // local. Foreign and malformed host-qualified records fail closed: PID values
230
+ // and clocks are not meaningful across hosts.
231
+ if (ownerHostId !== undefined && info.owner_host_id !== ownerHostId) return { stale: false };
213
232
  // Never reap a live owner by elapsed time: a long legitimate critical section must
214
233
  // not have its lock stolen (#652). Reclaim a dead owner immediately. Only when owner
215
234
  // liveness is indeterminate do we fall back to the staleMs elapsed-time heuristic.
@@ -239,18 +258,43 @@ async function removeStaleLockForAcquire(lockPath: string, snapshot: LockStaleSn
239
258
  }
240
259
  }
241
260
 
242
- async function tryAcquireLock(lockPath: string): Promise<LockInfo | null> {
261
+ async function tryAcquireLock(lockPath: string, ownerHostId?: string): Promise<LockInfo | null> {
243
262
  await fs.mkdir(path.dirname(lockPath), { recursive: true });
244
263
  const afterParentMkdir = FileLockTestHooks.afterParentMkdir;
245
264
  if (afterParentMkdir) await afterParentMkdir(lockPath);
265
+ if (ownerHostId === undefined) {
266
+ try {
267
+ await fs.mkdir(lockPath);
268
+ return await writeLockInfo(lockPath, lockInfo());
269
+ } catch (error) {
270
+ if ((error as NodeJS.ErrnoException).code === "EEXIST") return null;
271
+ throw error;
272
+ }
273
+ }
274
+
275
+ const pendingPath = `${lockPath}.pending.${process.pid}.${crypto.randomUUID()}`;
276
+ const owner = lockInfo(ownerHostId);
246
277
  try {
247
- await fs.mkdir(lockPath);
248
- return await writeLockInfo(lockPath);
249
- } catch (error) {
250
- if ((error as NodeJS.ErrnoException).code === "EEXIST") {
251
- return null;
278
+ await fs.mkdir(pendingPath);
279
+ await writeLockInfo(pendingPath, owner);
280
+ try {
281
+ await fs.rename(pendingPath, lockPath);
282
+ return owner;
283
+ } catch (error) {
284
+ const code = (error as NodeJS.ErrnoException).code;
285
+ if (code === "EEXIST" || code === "ENOTEMPTY") return null;
286
+ if (code === "EPERM") {
287
+ try {
288
+ await fs.stat(lockPath);
289
+ return null;
290
+ } catch (statError) {
291
+ if (!isEnoent(statError)) throw statError;
292
+ }
293
+ }
294
+ throw error;
252
295
  }
253
- throw error;
296
+ } finally {
297
+ await fs.rm(pendingPath, { recursive: true, force: true }).catch(() => undefined);
254
298
  }
255
299
  }
256
300
 
@@ -259,14 +303,15 @@ async function releaseLock(lockPath: string, owner: FileLockOwnerToken): Promise
259
303
  if (outcome !== "removed") throw new Error(`Failed to release file lock: ${outcome}.`);
260
304
  }
261
305
  async function acquireLock(filePath: string, options: FileLockOptions = {}): Promise<() => Promise<void>> {
306
+ if (options.ownerHostId !== undefined && !options.ownerHostId) throw new Error("ownerHostId must be non-empty");
262
307
  const opts = { ...DEFAULT_OPTIONS, ...options };
263
308
  const lockPath = getLockPath(filePath);
264
309
  const contentionStartTimes = new Map<string, string | null>();
265
310
  for (let attempt = 0; attempt < opts.retries; attempt++) {
266
- const owner = await tryAcquireLock(lockPath);
311
+ const owner = await tryAcquireLock(lockPath, opts.ownerHostId);
267
312
  if (owner) return () => releaseLock(lockPath, owner);
268
313
 
269
- const stale = await staleLockSnapshot(lockPath, opts.staleMs, contentionStartTimes);
314
+ const stale = await staleLockSnapshot(lockPath, opts.staleMs, opts.ownerHostId, contentionStartTimes);
270
315
  if (await removeStaleLockForAcquire(lockPath, stale)) continue;
271
316
  await Bun.sleep(opts.retryDelayMs);
272
317
  }
@@ -243,25 +243,25 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
243
243
  architect: "grok-build/grok-build",
244
244
  }),
245
245
  profile("cursor-eco", ["cursor"], {
246
- default: "cursor/composer-1.5:low",
247
- executor: "cursor/composer-1.5:minimal",
248
- planner: "cursor/composer-1.5:low",
249
- critic: "cursor/composer-1.5:medium",
250
- architect: "cursor/composer-1.5:high",
246
+ default: "cursor/composer-2.5",
247
+ executor: "cursor/composer-2.5",
248
+ planner: "cursor/composer-2.5",
249
+ critic: "cursor/composer-2.5",
250
+ architect: "cursor/composer-2.5",
251
251
  }),
252
252
  profile("cursor-medium", ["cursor"], {
253
- default: "cursor/composer-1.5:medium",
254
- executor: "cursor/composer-1.5:low",
255
- planner: "cursor/composer-1.5:medium",
256
- critic: "cursor/composer-1.5:high",
257
- architect: "cursor/composer-1.5:xhigh",
253
+ default: "cursor/composer-2.5",
254
+ executor: "cursor/composer-2.5-fast",
255
+ planner: "cursor/composer-2.5",
256
+ critic: "cursor/composer-2.5-fast",
257
+ architect: "cursor/composer-2.5-fast",
258
258
  }),
259
259
  profile("cursor-pro", ["cursor"], {
260
- default: "cursor/composer-1.5:xhigh",
261
- executor: "cursor/composer-1.5:medium",
262
- planner: "cursor/composer-1.5:high",
263
- critic: "cursor/composer-1.5:xhigh",
264
- architect: "cursor/composer-1.5:xhigh",
260
+ default: "cursor/composer-2.5-fast",
261
+ executor: "cursor/composer-2.5-fast",
262
+ planner: "cursor/composer-2.5-fast",
263
+ critic: "cursor/composer-2.5-fast",
264
+ architect: "cursor/composer-2.5-fast",
265
265
  }),
266
266
  profile("minimax-eco", ["minimax-code"], {
267
267
  default: "minimax-code/minimax-m3:low",
@@ -305,6 +305,20 @@ export const BUILTIN_MODEL_PROFILES: readonly ModelProfileDefinition[] = [
305
305
  architect: "alibaba-token-plan/qwen3.8-max-preview:xhigh",
306
306
  critic: "alibaba-token-plan/qwen3.8-max-preview:xhigh",
307
307
  }),
308
+ profile("alibaba-token-plan-qwen-deepseek", ["alibaba-token-plan"], {
309
+ default: "alibaba-token-plan/qwen-3.8-max:high",
310
+ executor: "alibaba-token-plan/deepseek-v4-flash-0731:high",
311
+ planner: "alibaba-token-plan/deepseek-v4-flash-0731:max",
312
+ architect: "alibaba-token-plan/qwen-3.8-max:xhigh",
313
+ critic: "alibaba-token-plan/qwen-3.8-max:xhigh",
314
+ }),
315
+ profile("alibaba-token-plan-glm-deepseek", ["alibaba-token-plan"], {
316
+ default: "alibaba-token-plan/glm-5.2:high",
317
+ executor: "alibaba-token-plan/deepseek-v4-flash-0731:high",
318
+ planner: "alibaba-token-plan/deepseek-v4-flash-0731:max",
319
+ architect: "alibaba-token-plan/glm-5.2:xhigh",
320
+ critic: "alibaba-token-plan/glm-5.2:xhigh",
321
+ }),
308
322
  profile("opus-codex", ["anthropic", "openai-codex"], {
309
323
  default: "anthropic/claude-opus-5:xhigh",
310
324
  executor: "openai-codex/gpt-5.6-terra:low",
@@ -370,6 +384,8 @@ const PROFILE_PRESENTATION: Record<string, ModelProfilePresentation> = {
370
384
  "alibaba-token-plan-balanced": { displayName: "Balanced", providerGroup: "ALIBABA TOKEN PLAN" },
371
385
  "alibaba-token-plan-pro": { displayName: "Pro", providerGroup: "ALIBABA TOKEN PLAN" },
372
386
  "alibaba-token-plan-qwenmaxxing": { displayName: "QwenMaxxing", providerGroup: "ALIBABA TOKEN PLAN" },
387
+ "alibaba-token-plan-qwen-deepseek": { displayName: "Qwen + DeepSeek", providerGroup: "ALIBABA TOKEN PLAN" },
388
+ "alibaba-token-plan-glm-deepseek": { displayName: "GLM + DeepSeek", providerGroup: "ALIBABA TOKEN PLAN" },
373
389
  "opus-codex": { displayName: "Opus + Codex", providerGroup: "COMBOS" },
374
390
  "codex-opencodego": { displayName: "Codex + OpenCodeGo", providerGroup: "COMBOS" },
375
391
  "fable-opus-codex": { displayName: "Fable + Opus + Codex", providerGroup: "COMBOS" },
@@ -12,6 +12,7 @@ export const COORDINATOR_MCP_TOOL_NAMES = [
12
12
  "gjc_coordinator_watch_events",
13
13
  "gjc_coordinator_register_session",
14
14
  "gjc_coordinator_start_session",
15
+ "gjc_coordinator_activate_session",
15
16
  "gjc_coordinator_stop_session",
16
17
  "gjc_coordinator_send_prompt",
17
18
  "gjc_coordinator_submit_question_answer",
@@ -6,6 +6,8 @@ import type { PrivateAskGateCodecV1, PublicReason } from "./question-gate-codec"
6
6
 
7
7
  export type CoordinatorSessionState =
8
8
  | "booting"
9
+ /** Live and endpoint-addressable, but withholding readiness until activation. */
10
+ | "prepared"
9
11
  | "ready_for_input"
10
12
  | "running"
11
13
  | "needs_user_input"