@gajae-code/coding-agent 0.12.6 → 0.12.7

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 (70) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/dist/types/async/job-manager.d.ts +36 -1
  3. package/dist/types/eval/backend.d.ts +2 -2
  4. package/dist/types/exec/bash-executor.d.ts +8 -10
  5. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +0 -36
  6. package/dist/types/internal-urls/registry-helpers.d.ts +0 -18
  7. package/dist/types/sdk/bus/chat-daemon-control.d.ts +1 -3
  8. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +2 -4
  9. package/dist/types/sdk/host/host.d.ts +2 -23
  10. package/dist/types/sdk/host/reverse-leases.d.ts +4 -7
  11. package/dist/types/session/artifacts.d.ts +5 -8
  12. package/dist/types/session/client-bridge.d.ts +0 -14
  13. package/dist/types/session/internal/managed-session-storage.d.ts +2 -5
  14. package/dist/types/session/messages.d.ts +1 -4
  15. package/dist/types/session/session-manager.d.ts +4 -5
  16. package/dist/types/session/streaming-output.d.ts +0 -32
  17. package/dist/types/task/receipt.d.ts +1 -2
  18. package/dist/types/task/types.d.ts +43 -178
  19. package/dist/types/tools/bash.d.ts +1 -1
  20. package/dist/types/tools/fetch.d.ts +0 -3
  21. package/dist/types/tools/output-meta.d.ts +5 -37
  22. package/dist/types/tools/subagent.d.ts +15 -0
  23. package/dist/types/tools/todo-write.d.ts +3 -0
  24. package/dist/types/tools/tool-result.d.ts +0 -1
  25. package/package.json +8 -8
  26. package/src/async/job-manager.ts +430 -46
  27. package/src/cursor.ts +13 -20
  28. package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -3
  29. package/src/eval/backend.ts +2 -2
  30. package/src/exec/bash-executor.ts +151 -425
  31. package/src/gjc-runtime/ultragoal-runtime.ts +3 -280
  32. package/src/gjc-runtime/workflow-manifest.generated.json +1 -40
  33. package/src/gjc-runtime/workflow-manifest.ts +0 -12
  34. package/src/internal-urls/agent-protocol.ts +38 -98
  35. package/src/internal-urls/artifact-protocol.ts +17 -28
  36. package/src/internal-urls/docs-index.generated.ts +1 -1
  37. package/src/internal-urls/registry-helpers.ts +0 -62
  38. package/src/modes/acp/acp-agent.ts +0 -24
  39. package/src/modes/acp/acp-client-bridge.ts +11 -154
  40. package/src/prompts/tools/bash.md +2 -2
  41. package/src/sdk/acp/adapter.ts +9 -200
  42. package/src/sdk/bus/chat-daemon-control.ts +3 -5
  43. package/src/sdk/bus/index.ts +8 -324
  44. package/src/sdk/bus/telegram-daemon-contract.ts +2 -4
  45. package/src/sdk/host/host.ts +9 -187
  46. package/src/sdk/host/reverse-leases.ts +42 -249
  47. package/src/session/agent-session.ts +15 -178
  48. package/src/session/artifacts.ts +74 -211
  49. package/src/session/client-bridge.ts +0 -15
  50. package/src/session/internal/managed-session-storage.ts +5 -104
  51. package/src/session/messages.ts +7 -32
  52. package/src/session/session-manager.ts +21 -12
  53. package/src/session/streaming-output.ts +40 -198
  54. package/src/task/index.ts +230 -64
  55. package/src/task/receipt.ts +2 -4
  56. package/src/task/render.ts +0 -30
  57. package/src/task/types.ts +10 -34
  58. package/src/tools/bash.ts +239 -818
  59. package/src/tools/eval.ts +4 -99
  60. package/src/tools/fetch.ts +15 -77
  61. package/src/tools/find.ts +3 -7
  62. package/src/tools/output-meta.ts +114 -696
  63. package/src/tools/search.ts +4 -9
  64. package/src/tools/subagent.ts +97 -75
  65. package/src/tools/todo-write.ts +47 -0
  66. package/src/tools/tool-result.ts +0 -1
  67. package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +0 -83
  68. package/dist/types/utils/artifact-id.d.ts +0 -2
  69. package/src/gjc-runtime/ultragoal-review-source.ts +0 -378
  70. package/src/utils/artifact-id.ts +0 -6
@@ -4,21 +4,11 @@ import type { BrokerIndexWriter, HostEndpointAdapters, SdkFrame } from "./types"
4
4
 
5
5
  export type SdkRequestObserver = (kind: "control" | "query", connectionId: string, frame: SdkFrame) => void;
6
6
 
7
- export interface ProviderLeaseIdentity {
8
- leaseId: string;
9
- connectionId: string;
10
- connectionGeneration: number;
11
- fence: string;
12
- }
13
-
14
- export type ProviderLeaseFence = (identity: ProviderLeaseIdentity, reason?: string) => boolean;
15
-
16
7
  export interface SessionSdkHostOptions extends HostEndpointAdapters {
17
8
  control?: (connectionId: string, frame: SdkFrame) => unknown | Promise<unknown>;
18
9
  query?: (connectionId: string, frame: SdkFrame) => unknown | Promise<unknown>;
19
10
  /** Best-effort diagnostic observation of accepted control/query frames. */
20
11
  onRequest?: SdkRequestObserver;
21
- onFrameOverflow?: (connectionId: string) => void;
22
12
  /** Runs before a control response is sent; identity transitions use sendTerminal. */
23
13
  beforeControlResponse?: (
24
14
  connectionId: string,
@@ -28,25 +18,13 @@ export interface SessionSdkHostOptions extends HostEndpointAdapters {
28
18
  ) => void | Promise<void>;
29
19
  /** Runs only after a successful control response has been sent to the client. */
30
20
  afterControlResponse?: (connectionId: string, request: SdkFrame, response: SdkFrame) => void | Promise<void>;
31
- onProviderLeaseRegistered?: (lease: ProviderLease, connectionGeneration: number) => void;
32
- installProviderDefinitions?: (
33
- capability: string,
34
- definitions: unknown,
35
- leaseId: string,
36
- connectionId: string,
37
- connectionGeneration: number,
38
- ) => void;
21
+ installProviderDefinitions?: (capability: string, definitions: unknown) => void;
39
22
  onProviderDefinitionsRemoved?: (capability: string) => void;
40
23
  onReverseCancel?: (requestId: string, reason: "provider_disconnected" | "lease_released") => void;
41
24
  /** Best-effort capabilities mirrored from the native transport for out-of-band consumers. */
42
25
  connectionCapabilities?: (connectionId: string) => ReadonlySet<string> | undefined;
43
26
  }
44
27
 
45
- /** Bound asynchronous frame admission per transport connection. */
46
- const MAX_PENDING_FRAMES_PER_CONNECTION = 32;
47
- const MAX_QUARANTINED_PROVIDER_LEASES = 256;
48
- const MAX_RETAINED_CLOSED_CONNECTIONS = 256;
49
-
50
28
  const TOOL_ACTIVITY_CAPABILITY = "tool_activity_v2";
51
29
  const CAP_GATED_FRAME_KINDS = new Set(["tool_activity", "reasoning_summary"]);
52
30
  const EMPTY_CAPABILITIES: ReadonlySet<string> = new Set();
@@ -142,12 +120,6 @@ export class SessionSdkHost {
142
120
  #stopPromise?: Promise<"stopped">;
143
121
  #unsubscribe?: () => void;
144
122
  #registration?: { writer: BrokerIndexWriter; generation: number };
145
- #connectionGenerations = new Map<string, number>();
146
- #openConnections = new Set<string>();
147
- #pendingFrames = new Map<string, { generation: number; count: number }>();
148
- #leaseRecords = new Map<string, { capability: string; connectionId: string; connectionGeneration: number }>();
149
- #quarantinedLeases = new Map<string, ProviderLeaseIdentity>();
150
- #closedConnectionOrder: string[] = [];
151
123
 
152
124
  constructor(options: SessionSdkHostOptions) {
153
125
  this.#options = options;
@@ -169,120 +141,9 @@ export class SessionSdkHost {
169
141
  getProviderDefinitions(capability: string): unknown | undefined {
170
142
  return this.reverse.getInstalledDefinitions(capability);
171
143
  }
172
- /** Mark a transport connection live and return its stable generation. */
173
- handleConnect(connectionId: string): number {
174
- if (!this.#openConnections.has(connectionId)) {
175
- const generation = this.#connectionGenerations.get(connectionId) ?? 1;
176
- this.#connectionGenerations.set(connectionId, generation);
177
- this.#openConnections.add(connectionId);
178
- this.#pendingFrames.set(connectionId, { generation, count: 0 });
179
- }
180
- return this.#connectionGenerations.get(connectionId) ?? 0;
181
- }
182
-
183
- /** Current generation for a transport connection, if it has been observed. */
184
- connectionGeneration(connectionId: string): number {
185
- return this.#connectionGenerations.get(connectionId) ?? 0;
186
- }
187
-
188
- isConnectionOpen(connectionId: string, generation?: number): boolean {
189
- return (
190
- this.#openConnections.has(connectionId) &&
191
- (generation === undefined || this.#connectionGenerations.get(connectionId) === generation)
192
- );
193
- }
194
-
195
144
  /** Release reverse leases after the transport reports a WebSocket disconnect. */
196
145
  handleDisconnect(connectionId: string): void {
197
- if (!this.#openConnections.delete(connectionId)) return;
198
- this.#connectionGenerations.set(connectionId, (this.#connectionGenerations.get(connectionId) ?? 0) + 1);
199
- this.#pendingFrames.delete(connectionId);
200
- for (const [leaseId, record] of this.#leaseRecords) {
201
- if (record.connectionId === connectionId) this.#leaseRecords.delete(leaseId);
202
- }
203
146
  this.reverse.disconnect(connectionId);
204
- this.#closedConnectionOrder.push(connectionId);
205
- while (this.#closedConnectionOrder.length > MAX_RETAINED_CLOSED_CONNECTIONS) {
206
- const retired = this.#closedConnectionOrder.shift();
207
- if (retired && !this.#openConnections.has(retired)) this.#connectionGenerations.delete(retired);
208
- }
209
- }
210
-
211
- /**
212
- * Fences one exact provider lease after uncertain terminal cleanup. The fence
213
- * is idempotent for the same identity and never affects a newer generation.
214
- */
215
- quarantineProviderLease(identity: ProviderLeaseIdentity, _reason?: string): boolean {
216
- if (
217
- !identity ||
218
- typeof identity.leaseId !== "string" ||
219
- identity.leaseId.length === 0 ||
220
- typeof identity.connectionId !== "string" ||
221
- identity.connectionId.length === 0 ||
222
- !Number.isSafeInteger(identity.connectionGeneration) ||
223
- identity.connectionGeneration < 0 ||
224
- typeof identity.fence !== "string" ||
225
- identity.fence.length === 0
226
- )
227
- return false;
228
- const previous = this.#quarantinedLeases.get(identity.leaseId);
229
- if (
230
- previous &&
231
- previous.connectionId === identity.connectionId &&
232
- previous.connectionGeneration === identity.connectionGeneration &&
233
- previous.fence === identity.fence
234
- )
235
- return true;
236
- if (this.#quarantinedLeases.size >= MAX_QUARANTINED_PROVIDER_LEASES) return false;
237
- if (this.#connectionGenerations.get(identity.connectionId) !== identity.connectionGeneration) return false;
238
- const record = this.#leaseRecords.get(identity.leaseId);
239
- if (
240
- !record ||
241
- record.connectionId !== identity.connectionId ||
242
- record.connectionGeneration !== identity.connectionGeneration
243
- )
244
- return false;
245
- const lease = this.reverse.getLease(record.capability);
246
- if (!lease || lease.leaseId !== identity.leaseId || lease.connectionId !== identity.connectionId || !lease.active)
247
- return false;
248
- try {
249
- this.reverse.release(identity.connectionId, identity.leaseId);
250
- } catch {
251
- return false;
252
- }
253
- this.#quarantinedLeases.set(identity.leaseId, { ...identity });
254
- return true;
255
- }
256
-
257
- /** Dispatch a frame through bounded per-connection asynchronous admission. */
258
- dispatchFrame(connectionId: string, frame: SdkFrame): Promise<void> {
259
- const generation = this.#connectionGenerations.has(connectionId)
260
- ? (this.#connectionGenerations.get(connectionId) ?? 0)
261
- : this.handleConnect(connectionId);
262
- const pending = this.#pendingFrames.get(connectionId);
263
- const pendingCount = pending?.generation === generation ? pending.count : 0;
264
- if (pendingCount >= MAX_PENDING_FRAMES_PER_CONNECTION) {
265
- this.#options.onFrameOverflow?.(connectionId);
266
- return Promise.resolve();
267
- }
268
- this.#pendingFrames.set(connectionId, { generation, count: pendingCount + 1 });
269
- const task = Promise.resolve().then(() => {
270
- if (this.#connectionGenerations.get(connectionId) !== generation) return;
271
- if (!this.#openConnections.has(connectionId)) return;
272
- return this.#onFrame(connectionId, frame, generation);
273
- });
274
- void task.finally(() => {
275
- const current = this.#pendingFrames.get(connectionId);
276
- if (!current || current.generation !== generation) return;
277
- this.#pendingFrames.set(connectionId, {
278
- generation,
279
- count: Math.max(0, current.count - 1),
280
- });
281
- });
282
- return task.then(
283
- () => {},
284
- () => {},
285
- );
286
147
  }
287
148
 
288
149
  /** Adds an event to the resumable event ring. Transport delivery is owned by bus wiring. */
@@ -295,7 +156,7 @@ export class SessionSdkHost {
295
156
  this.events.restart();
296
157
  this.emitEvent({ name: "session_ready", sessionId: this.#options.sessionId, generation: this.events.generation });
297
158
  const disposer = this.#options.onFrame((connectionId, frame) => {
298
- void this.dispatchFrame(connectionId, frame);
159
+ void this.#onFrame(connectionId, frame);
299
160
  });
300
161
  this.#unsubscribe = typeof disposer === "function" ? disposer : undefined;
301
162
  this.#started = true;
@@ -360,30 +221,18 @@ export class SessionSdkHost {
360
221
  }
361
222
  }
362
223
 
363
- async #onFrame(connectionId: string, frame: SdkFrame, generation: number): Promise<void> {
364
- if (this.#connectionGenerations.has(connectionId) && this.#connectionGenerations.get(connectionId) !== generation)
365
- return;
224
+ async #onFrame(connectionId: string, frame: SdkFrame): Promise<void> {
366
225
  try {
367
226
  switch (frame.type) {
368
227
  case "control_request": {
369
228
  this.#observeRequest("control", connectionId, frame);
370
229
  const result = await this.#options.control?.(connectionId, frame);
371
- if (
372
- this.#connectionGenerations.has(connectionId) &&
373
- this.#connectionGenerations.get(connectionId) !== generation
374
- )
375
- return;
376
230
  if (result !== undefined) {
377
231
  const response = { type: "control_response", ...(result as SdkFrame) };
378
232
  let terminalSent = false;
379
233
  const sendTerminal = async (): Promise<void> => {
380
234
  if (terminalSent) return;
381
235
  terminalSent = true;
382
- if (
383
- this.#connectionGenerations.has(connectionId) &&
384
- this.#connectionGenerations.get(connectionId) !== generation
385
- )
386
- return;
387
236
  await this.#send(connectionId, response);
388
237
  };
389
238
  await this.#options.beforeControlResponse?.(connectionId, frame, response, sendTerminal);
@@ -422,11 +271,6 @@ export class SessionSdkHost {
422
271
  case "query_request": {
423
272
  this.#observeRequest("query", connectionId, frame);
424
273
  const result = await this.#options.query?.(connectionId, frame);
425
- if (
426
- this.#connectionGenerations.has(connectionId) &&
427
- this.#connectionGenerations.get(connectionId) !== generation
428
- )
429
- return;
430
274
  if (result !== undefined)
431
275
  await this.#send(connectionId, { type: "query_response", ...(result as SdkFrame) });
432
276
  break;
@@ -436,30 +280,13 @@ export class SessionSdkHost {
436
280
  requireConnection(connectionId, frame);
437
281
  const capability = requiredString(frame, "capability");
438
282
  if (!has(frame, "definitions")) throw invalidFrame("definitions is required.");
439
- const expectedLeaseId = optionalString(frame, "expectedLeaseId");
440
- if (expectedLeaseId && this.#quarantinedLeases.has(expectedLeaseId))
441
- throw new ReverseLeaseError("lease_unavailable");
442
- const connectionGeneration = this.#connectionGenerations.get(connectionId) ?? 0;
443
283
  const lease = this.reverse.registerProvider(
444
284
  connectionId,
445
285
  capability,
446
286
  frame.definitions,
447
- expectedLeaseId,
287
+ optionalString(frame, "expectedLeaseId"),
448
288
  optionalString(frame, "idempotencyKey"),
449
- connectionGeneration,
450
289
  );
451
- for (const [priorLeaseId, record] of this.#leaseRecords) {
452
- if (record.capability === lease.capability && priorLeaseId !== lease.leaseId)
453
- this.#leaseRecords.delete(priorLeaseId);
454
- }
455
- this.#leaseRecords.set(lease.leaseId, {
456
- capability: lease.capability,
457
- connectionId: lease.connectionId,
458
- connectionGeneration,
459
- });
460
- try {
461
- this.#options.onProviderLeaseRegistered?.(lease, connectionGeneration);
462
- } catch {}
463
290
  await this.#send(connectionId, {
464
291
  id: frame.id,
465
292
  type: "register_provider_result",
@@ -479,7 +306,6 @@ export class SessionSdkHost {
479
306
  requireConnection(connectionId, frame);
480
307
  const handoffTo = optionalString(frame, "handoffTo");
481
308
  const lease = this.reverse.release(connectionId, requiredString(frame, "leaseId"), handoffTo);
482
- this.#leaseRecords.delete(lease.leaseId);
483
309
  await this.#send(connectionId, leaseState(undefined, lease));
484
310
  break;
485
311
  }
@@ -492,7 +318,7 @@ export class SessionSdkHost {
492
318
  if (frame.ok) {
493
319
  if (!has(frame, "result") || has(frame, "error"))
494
320
  throw invalidFrame("Successful reverse responses require result and no error.");
495
- this.reverse.respond(connectionId, id, leaseId, frame.result, undefined, frame);
321
+ this.reverse.respond(connectionId, id, leaseId, frame.result);
496
322
  } else {
497
323
  if (
498
324
  has(frame, "result") ||
@@ -501,14 +327,10 @@ export class SessionSdkHost {
501
327
  typeof responseError.message !== "string"
502
328
  )
503
329
  throw invalidFrame("Failed reverse responses require a structured error and no result.");
504
- this.reverse.respond(
505
- connectionId,
506
- id,
507
- leaseId,
508
- undefined,
509
- { code: responseError.code, message: responseError.message },
510
- frame,
511
- );
330
+ this.reverse.respond(connectionId, id, leaseId, undefined, {
331
+ code: responseError.code,
332
+ message: responseError.message,
333
+ });
512
334
  }
513
335
  break;
514
336
  }