@gajae-code/coding-agent 0.16.0 → 0.16.1

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 (163) hide show
  1. package/CHANGELOG.md +41 -1
  2. package/dist/types/async/job-manager.d.ts +32 -1
  3. package/dist/types/browser-backend.d.ts +12 -0
  4. package/dist/types/cli/update-cli.d.ts +1 -0
  5. package/dist/types/commands/sdk.d.ts +2 -0
  6. package/dist/types/config/billing-path.d.ts +25 -0
  7. package/dist/types/config/config-schema-version.d.ts +13 -1
  8. package/dist/types/config/model-registry.d.ts +16 -0
  9. package/dist/types/config/settings-schema.d.ts +17 -7
  10. package/dist/types/extensibility/extensions/types.d.ts +8 -3
  11. package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
  12. package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
  13. package/dist/types/main.d.ts +1 -0
  14. package/dist/types/modes/acp/acp-agent.d.ts +21 -21
  15. package/dist/types/modes/components/model-selector.d.ts +4 -0
  16. package/dist/types/modes/components/session-selector.d.ts +1 -1
  17. package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
  18. package/dist/types/modes/interactive-mode.d.ts +16 -2
  19. package/dist/types/modes/theme/theme.d.ts +2 -1
  20. package/dist/types/modes/types.d.ts +2 -0
  21. package/dist/types/sdk/broker/broker.d.ts +2 -1
  22. package/dist/types/sdk/broker/ensure.d.ts +3 -0
  23. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
  24. package/dist/types/sdk/broker/lifecycle.d.ts +14 -0
  25. package/dist/types/sdk/broker/startup-budget.d.ts +52 -0
  26. package/dist/types/sdk/broker/state-version.d.ts +17 -0
  27. package/dist/types/sdk/bus/discord-daemon.d.ts +1 -1
  28. package/dist/types/sdk/bus/index.d.ts +3 -1
  29. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
  30. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
  31. package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
  32. package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
  33. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  34. package/dist/types/sdk/cli/rows.d.ts +8 -2
  35. package/dist/types/sdk/cli/session-cli.d.ts +2 -1
  36. package/dist/types/sdk/host/events.d.ts +1 -0
  37. package/dist/types/sdk/host/query/cursor.d.ts +4 -2
  38. package/dist/types/sdk/host/query/handlers.d.ts +1 -0
  39. package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
  40. package/dist/types/sdk/host/session-runtime.d.ts +18 -17
  41. package/dist/types/sdk/lifecycle-session.d.ts +1 -1
  42. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
  43. package/dist/types/sdk/receipt-state.d.ts +2 -0
  44. package/dist/types/sdk/router/session-router.d.ts +4 -0
  45. package/dist/types/sdk/turn-result.d.ts +6 -0
  46. package/dist/types/session/agent-session.d.ts +47 -7
  47. package/dist/types/session/fold-coordinator.d.ts +29 -2
  48. package/dist/types/session/session-manager.d.ts +14 -2
  49. package/dist/types/tools/ask.d.ts +1 -0
  50. package/dist/types/tools/bash.d.ts +12 -0
  51. package/dist/types/tools/browser/launch.d.ts +10 -0
  52. package/dist/types/tools/browser/registry.d.ts +8 -0
  53. package/dist/types/tools/index.d.ts +6 -10
  54. package/dist/types/tools/job.d.ts +3 -1
  55. package/dist/types/utils/external-editor.d.ts +7 -0
  56. package/examples/extensions/README.md +1 -1
  57. package/examples/extensions/pirate.ts +7 -4
  58. package/package.json +7 -7
  59. package/scripts/generate-sdk-operation-inventory.ts +7 -1
  60. package/src/async/job-manager.ts +107 -9
  61. package/src/browser-backend.ts +29 -0
  62. package/src/cli/plugin-cli.ts +29 -0
  63. package/src/cli/update-cli.ts +63 -12
  64. package/src/commands/sdk.ts +22 -7
  65. package/src/config/billing-path.ts +42 -0
  66. package/src/config/config-schema-version.ts +18 -1
  67. package/src/config/model-preset-registry.ts +177 -18
  68. package/src/config/model-registry.ts +268 -42
  69. package/src/config/settings-schema.ts +19 -7
  70. package/src/config/settings.ts +46 -15
  71. package/src/coordinator-mcp/projection-scan.ts +6 -6
  72. package/src/coordinator-mcp/server.ts +21 -1
  73. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  74. package/src/extensibility/extensions/runner.ts +27 -0
  75. package/src/extensibility/extensions/types.ts +8 -3
  76. package/src/extensibility/gjc-plugins/installer.ts +51 -11
  77. package/src/extensibility/runtime-skill-discovery.ts +110 -3
  78. package/src/extensibility/skills.ts +22 -12
  79. package/src/gjc-runtime/launch-worktree.ts +285 -4
  80. package/src/gjc-runtime/session-state-lock.ts +154 -28
  81. package/src/gjc-runtime/session-state-sidecar.ts +10 -17
  82. package/src/gjc-runtime/tmux-owner-isolation.ts +16 -1
  83. package/src/internal-urls/docs-index.generated.ts +10 -10
  84. package/src/main.ts +4 -1
  85. package/src/modes/acp/acp-agent.ts +5 -5
  86. package/src/modes/components/model-selector.ts +76 -8
  87. package/src/modes/components/session-selector.ts +23 -15
  88. package/src/modes/components/sessions-dashboard.ts +8 -5
  89. package/src/modes/controllers/command-controller.ts +1 -1
  90. package/src/modes/controllers/event-controller.ts +30 -4
  91. package/src/modes/controllers/extension-ui-controller.ts +13 -13
  92. package/src/modes/controllers/input-controller.ts +2 -2
  93. package/src/modes/controllers/selector-controller.ts +10 -3
  94. package/src/modes/interactive-mode.ts +36 -12
  95. package/src/modes/prompt-action-autocomplete.ts +30 -4
  96. package/src/modes/runtime-init.ts +12 -9
  97. package/src/modes/theme/theme.ts +5 -0
  98. package/src/modes/types.ts +2 -0
  99. package/src/modes/utils/ui-helpers.ts +22 -3
  100. package/src/prompts/agent-fragments/browser-aside.md +22 -0
  101. package/src/prompts/tools/ask.md +1 -1
  102. package/src/prompts/tools/bash.md +1 -0
  103. package/src/prompts/tools/job.md +1 -1
  104. package/src/sdk/acp/adapter.ts +5 -0
  105. package/src/sdk/broker/broker.ts +10 -1
  106. package/src/sdk/broker/ensure.ts +76 -3
  107. package/src/sdk/broker/lifecycle-ledger.ts +6 -0
  108. package/src/sdk/broker/lifecycle.ts +254 -34
  109. package/src/sdk/broker/spawn-substrate.ts +45 -2
  110. package/src/sdk/broker/startup-budget.ts +144 -2
  111. package/src/sdk/broker/state-version.ts +17 -0
  112. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  113. package/src/sdk/bus/chat-daemon-runtime.ts +111 -24
  114. package/src/sdk/bus/discord-daemon.ts +50 -10
  115. package/src/sdk/bus/index.ts +33 -3
  116. package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
  117. package/src/sdk/bus/lifecycle-commands.ts +4 -0
  118. package/src/sdk/bus/notification-service.ts +22 -5
  119. package/src/sdk/bus/prompt-reconciliation.ts +7 -2
  120. package/src/sdk/bus/reconciliation-store.ts +55 -31
  121. package/src/sdk/bus/slack-conversation.ts +1 -1
  122. package/src/sdk/bus/slack-daemon.ts +72 -38
  123. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  124. package/src/sdk/cli/rows.ts +30 -7
  125. package/src/sdk/cli/session-cli.ts +33 -21
  126. package/src/sdk/host/control/dispatch.ts +2 -2
  127. package/src/sdk/host/events.ts +1 -0
  128. package/src/sdk/host/host.ts +2 -0
  129. package/src/sdk/host/query/cursor.ts +52 -7
  130. package/src/sdk/host/query/handlers.ts +33 -12
  131. package/src/sdk/host/query/revision-store.ts +12 -0
  132. package/src/sdk/host/session-runtime.test.ts +463 -8
  133. package/src/sdk/host/session-runtime.ts +383 -54
  134. package/src/sdk/host/surface-policy.ts +3 -3
  135. package/src/sdk/lifecycle-session.ts +7 -2
  136. package/src/sdk/prompt-deadline-manager.ts +2 -1
  137. package/src/sdk/protocol/operation-inventory.generated.json +54 -3
  138. package/src/sdk/protocol/operation-registry.ts +1 -1
  139. package/src/sdk/receipt-state.ts +10 -0
  140. package/src/sdk/router/session-router.ts +152 -59
  141. package/src/sdk/session.ts +60 -41
  142. package/src/sdk/turn-result.ts +22 -0
  143. package/src/session/agent-session.ts +1033 -260
  144. package/src/session/fold-coordinator.ts +40 -3
  145. package/src/session/session-manager.ts +517 -77
  146. package/src/slash-commands/builtin-registry.ts +41 -0
  147. package/src/task/executor.ts +9 -4
  148. package/src/tools/ask-contract.test.ts +64 -0
  149. package/src/tools/ask-contract.ts +36 -1
  150. package/src/tools/ask.ts +10 -8
  151. package/src/tools/bash-interactive.ts +11 -2
  152. package/src/tools/bash.ts +156 -33
  153. package/src/tools/browser/launch.ts +37 -0
  154. package/src/tools/browser/registry.ts +99 -9
  155. package/src/tools/cron.ts +6 -4
  156. package/src/tools/descriptors.test.ts +15 -0
  157. package/src/tools/descriptors.ts +5 -1
  158. package/src/tools/index.ts +6 -6
  159. package/src/tools/job.ts +4 -1
  160. package/src/tools/monitor.ts +12 -9
  161. package/src/tools/resolve.ts +30 -10
  162. package/src/tools/tool-catalog.generated.ts +2 -2
  163. package/src/utils/external-editor.ts +72 -1
@@ -51,6 +51,7 @@ import {
51
51
  isBackgroundJobSupportEnabled,
52
52
  jobElapsedMs,
53
53
  } from "../async";
54
+ import { resolveBrowserBackend } from "../browser-backend";
54
55
  import { loadCapability, reset as resetCapabilities } from "../capability";
55
56
  import { type Rule, ruleCapability, setActiveRules } from "../capability/rule";
56
57
  import type { SourceMeta } from "../capability/types";
@@ -150,7 +151,7 @@ import { markAutoroutingInactive } from "../sdk/host/internal-autorouting-state"
150
151
  import { createSdkSessionRuntimeExtension, registerSdkOnlyNotificationCommand } from "../sdk/host/session-runtime";
151
152
  import { createSdkWebSocketTransport } from "../sdk/host/websocket-transport";
152
153
  import type { SecretObfuscator } from "../secrets";
153
- import { AgentSession, type ForkContextSeed } from "../session/agent-session";
154
+ import { AgentSession, type ForkContextSeed, isSessionDisposalIncompleteError } from "../session/agent-session";
154
155
  import { AuthBrokerClient, AuthStorage, RemoteAuthCredentialStore } from "../session/auth-storage";
155
156
  import { type CustomMessage, convertToLlm } from "../session/messages";
156
157
  import { createReadonlySessionManager, SessionManager } from "../session/session-manager";
@@ -1104,12 +1105,16 @@ function buildMCPPromptCommands(manager: MCPManager): LoadedCustomCommand[] {
1104
1105
  */
1105
1106
 
1106
1107
  function withEmbeddedDefaultGjcSkills(skills: Skill[]): Skill[] {
1107
- const byName = new Map(skills.map(skill => [skill.name, skill]));
1108
+ const byName = new Map<string, Skill>();
1109
+ for (const skill of skills) {
1110
+ const key = skill.name.toLowerCase();
1111
+ if (!byName.has(key)) byName.set(key, skill);
1112
+ }
1108
1113
  // The four public GJC workflow skills are a product invariant: even if a
1109
1114
  // caller-supplied or filesystem skill shares a name, the bundled definition
1110
1115
  // wins so workflow routing can never be silently hijacked.
1111
1116
  for (const defaultSkill of getEmbeddedDefaultGjcSkills()) {
1112
- byName.set(defaultSkill.name, defaultSkill);
1117
+ byName.set(defaultSkill.name.toLowerCase(), defaultSkill);
1113
1118
  }
1114
1119
  return [...byName.values()];
1115
1120
  }
@@ -2438,7 +2443,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2438
2443
  "Cannot rescope a session with caller-owned MCP authority; recreate the session at the target cwd.",
2439
2444
  );
2440
2445
  }
2441
- return sessionManager.runExclusiveCwdTransition(async () => {
2446
+ return sessionManager.runExclusiveCwdMoveTransition(async () => {
2442
2447
  if (moveConsumed) {
2443
2448
  throw new Error(
2444
2449
  "This session has already been rescoped; only one agent-invoked move is allowed per session.",
@@ -2713,7 +2718,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2713
2718
  registerForegroundFoldParticipant: adapter =>
2714
2719
  session?.registerForegroundFoldParticipant(adapter) ?? (() => {}),
2715
2720
  hasForegroundBashBackgroundRequestHandler: () => session?.hasForegroundBashBackgroundRequestHandler() ?? false,
2716
- requestForegroundBashBackground: () => Promise.resolve(session?.requestForegroundBashBackground() ?? false),
2721
+ requestForegroundBashBackground: (reason, adapter) =>
2722
+ Promise.resolve(session?.requestForegroundBashBackground(reason, adapter) ?? false),
2723
+ getToolInterruptPolicy: () => agent?.getToolInterruptPolicy() ?? "abort_tools",
2717
2724
 
2718
2725
  getCredentialSessionId: () => session?.credentialSessionId ?? credentialSessionId,
2719
2726
  getMcpManager: () => mcpManager ?? options.inheritedMcpManager,
@@ -2770,16 +2777,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2770
2777
  return m ? buildNamedToolChoice(name, m) : undefined;
2771
2778
  },
2772
2779
  buildToolChoiceResult: name => buildNamedToolChoiceResult(name, session.model),
2773
- steer: msg =>
2774
- session.agent.steer({
2775
- role: "custom",
2776
- customType: msg.customType,
2777
- content: msg.content,
2778
- display: false,
2779
- details: msg.details,
2780
- attribution: "agent",
2781
- timestamp: Date.now(),
2782
- }),
2783
2780
  sendCustomMessage: (msg, opts) => session.sendCustomMessage(msg, opts),
2784
2781
  purgeQueuedCustomMessages: predicate => session.purgeQueuedCustomMessages(predicate),
2785
2782
  peekQueueInvoker: () => session.peekQueueInvoker(),
@@ -3562,7 +3559,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
3562
3559
  !model &&
3563
3560
  !options.modelPattern &&
3564
3561
  !startupCredentialModelRejected &&
3565
- configuredDefaultPatterns.length === 0
3562
+ (configuredDefaultPatterns.length === 0 || preferredCredentialProvider !== undefined)
3566
3563
  ) {
3567
3564
  // Re-resolve the allowed set: extension factories above may have
3568
3565
  // registered providers/models that weren't visible at startup.
@@ -3896,7 +3893,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
3896
3893
  ? await memoryBackend.buildDeveloperInstructions(agentDir, settings, session)
3897
3894
  : undefined;
3898
3895
 
3899
- const appendPrompt: string | undefined = memoryInstructions ?? undefined;
3896
+ const browserBackend = resolveBrowserBackend(settings);
3897
+ const appendPrompt =
3898
+ [memoryInstructions, browserBackend.developerInstructions].filter(Boolean).join("\n\n") || undefined;
3900
3899
  let pluginSystemAppendices = "";
3901
3900
  try {
3902
3901
  pluginSystemAppendices = await renderAlwaysOnSystemAppendices({ cwd: getLiveCwd() });
@@ -4277,7 +4276,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
4277
4276
  transformContext,
4278
4277
  steeringMode: settings.get("steeringMode") ?? "one-at-a-time",
4279
4278
  followUpMode: settings.get("followUpMode") ?? "one-at-a-time",
4280
- interruptMode: settings.get("interruptMode") ?? "immediate",
4279
+ toolInterruptPolicy: settings.get("toolInterruptPolicy") ?? "abort_tools",
4281
4280
  thinkingBudgets: settings.getGroup("thinkingBudgets"),
4282
4281
  temperature: settings.get("temperature") >= 0 ? settings.get("temperature") : undefined,
4283
4282
  topP: settings.get("topP") >= 0 ? settings.get("topP") : undefined,
@@ -4575,38 +4574,49 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
4575
4574
 
4576
4575
  // Attach the live session to the pre-registered ref so peers can route IRC
4577
4576
  // messages here. Refresh sessionFile in case it was unavailable at pre-register
4578
- // time. The dispose wrapper below unregisters on teardown.
4577
+ // time. The registered terminal cleanup below unregisters on teardown.
4579
4578
  agentRegistry.attachSession(resolvedAgentId, session, sessionManager.getSessionFile() ?? null);
4580
4579
  {
4581
- const originalDispose = session.dispose.bind(session);
4582
- session.dispose = async () => {
4583
- try {
4584
- await originalDispose();
4585
- } finally {
4580
+ let cleanupPromise: Promise<void> | undefined;
4581
+ const finishCleanup = async (): Promise<void> => {
4582
+ if (cleanupPromise) return cleanupPromise;
4583
+ const cleanup = (async () => {
4584
+ const failures: unknown[] = [];
4586
4585
  try {
4587
4586
  agentRegistry.unregister(resolvedAgentId);
4588
4587
  releaseCredentialDisabledSubscription();
4589
4588
  releaseLocalProtocolOverride();
4589
+ } catch (error) {
4590
+ failures.push(error);
4590
4591
  } finally {
4591
- // The endpoint is gone: its owned registrations can never
4592
- // reach a delivery settlement boundary, and foreign-endpoint
4593
- // tuples are deliberately never classified terminal by other
4594
- // managers — retire them before unregistering the manager so
4595
- // repeated session churn cannot saturate the registry
4596
- // (review thread P2). The endpoint is the manager's live
4597
- // registration key, which survives newSession/switchSession
4598
- // rekeying and may differ from the persisted logical session id.
4599
- if (!options.parentTaskPrefix) {
4600
- retireOwnedRegistrationsForEndpoint(
4601
- AsyncJobManager.endpointIdOf(asyncJobManager) ?? asyncJobEndpointId,
4602
- );
4603
- AsyncJobManager.unregisterManager(asyncJobManager);
4592
+ try {
4593
+ if (!options.parentTaskPrefix) {
4594
+ retireOwnedRegistrationsForEndpoint(
4595
+ AsyncJobManager.endpointIdOf(asyncJobManager) ?? asyncJobEndpointId,
4596
+ );
4597
+ AsyncJobManager.unregisterManager(asyncJobManager);
4598
+ }
4599
+ } catch (error) {
4600
+ failures.push(error);
4601
+ }
4602
+ try {
4603
+ await closeOwnedAuthStorage();
4604
+ } catch (error) {
4605
+ failures.push(error);
4606
+ }
4607
+ try {
4608
+ await closeOwnedSettings();
4609
+ } catch (error) {
4610
+ failures.push(error);
4604
4611
  }
4605
- await closeOwnedAuthStorage();
4606
- await closeOwnedSettings();
4607
4612
  }
4608
- }
4613
+ if (failures.length > 0) throw new AggregateError(failures, "SDK disposal cleanup failed.");
4614
+ })();
4615
+ cleanupPromise = cleanup;
4616
+ void cleanup.catch(() => {});
4617
+ return cleanup;
4609
4618
  };
4619
+ session.registerToolSessionCleanup(finishCleanup);
4610
4620
  }
4611
4621
 
4612
4622
  if (model?.api === "openai-codex-responses") {
@@ -4834,7 +4844,16 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
4834
4844
  let cleanupDiagnostic: unknown;
4835
4845
  try {
4836
4846
  if (hasSession) {
4837
- await session.dispose();
4847
+ try {
4848
+ await session.dispose();
4849
+ } catch (disposeError) {
4850
+ if (!isSessionDisposalIncompleteError(disposeError)) {
4851
+ throw disposeError;
4852
+ }
4853
+ // The first call is intentionally bounded for startup callers. Join the
4854
+ // retained teardown before releasing the resources it still owns.
4855
+ await session.awaitDisposeCompletion();
4856
+ }
4838
4857
  } else {
4839
4858
  if (hasRegistered) agentRegistry.unregister(resolvedAgentId);
4840
4859
  // Admission happens before session construction. Any later startup
@@ -17,6 +17,7 @@ export interface TurnResultError {
17
17
  code: string;
18
18
  message: string;
19
19
  }
20
+ export type TurnResultReceiptState = "absent" | "present" | "missing" | "unknown";
20
21
  export interface TurnResultPage {
21
22
  status: TurnResultStatus;
22
23
  kind?: "prompt" | "skill";
@@ -26,6 +27,8 @@ export interface TurnResultPage {
26
27
  acceptedAt?: number;
27
28
  startedAt?: number;
28
29
  terminalAt?: number;
30
+ receiptState?: TurnResultReceiptState;
31
+ outcome?: unknown;
29
32
  content?: TurnResultContent;
30
33
  error?: TurnResultError;
31
34
  }
@@ -51,3 +54,22 @@ export function sanitizeTurnResultContent(text: unknown): TurnResultContent | un
51
54
  truncated,
52
55
  };
53
56
  }
57
+
58
+ export function reportableTurnResultContent(content: TurnResultContent | undefined): boolean {
59
+ return content !== undefined && content.text.trim().length > 0;
60
+ }
61
+
62
+ /** First reportable invocation-owned content wins; blank evidence remains replaceable. */
63
+ export function reduceTurnResultContent(
64
+ existing: TurnResultContent | undefined,
65
+ incoming: TurnResultContent | undefined,
66
+ ): TurnResultContent | undefined {
67
+ if (reportableTurnResultContent(existing)) return existing;
68
+ if (incoming === undefined) return existing;
69
+ const sanitized = sanitizeTurnResultContent(incoming.text);
70
+ if (sanitized === undefined) return existing;
71
+ return {
72
+ ...sanitized,
73
+ truncated: sanitized.truncated || incoming.truncated || existing?.truncated === true,
74
+ };
75
+ }