@gajae-code/coding-agent 0.16.0 → 0.16.3

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 (164) hide show
  1. package/CHANGELOG.md +52 -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 +15 -2
  35. package/dist/types/sdk/cli/session-cli.d.ts +2 -1
  36. package/dist/types/sdk/host/events.d.ts +2 -0
  37. package/dist/types/sdk/host/host.d.ts +2 -0
  38. package/dist/types/sdk/host/query/cursor.d.ts +4 -2
  39. package/dist/types/sdk/host/query/handlers.d.ts +1 -0
  40. package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
  41. package/dist/types/sdk/host/session-runtime.d.ts +18 -17
  42. package/dist/types/sdk/lifecycle-session.d.ts +1 -1
  43. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
  44. package/dist/types/sdk/receipt-state.d.ts +2 -0
  45. package/dist/types/sdk/router/session-router.d.ts +4 -0
  46. package/dist/types/sdk/turn-result.d.ts +6 -0
  47. package/dist/types/session/agent-session.d.ts +47 -7
  48. package/dist/types/session/fold-coordinator.d.ts +29 -2
  49. package/dist/types/session/session-manager.d.ts +14 -2
  50. package/dist/types/tools/ask.d.ts +1 -0
  51. package/dist/types/tools/bash.d.ts +12 -0
  52. package/dist/types/tools/browser/launch.d.ts +10 -0
  53. package/dist/types/tools/browser/registry.d.ts +8 -0
  54. package/dist/types/tools/index.d.ts +4 -10
  55. package/dist/types/tools/job.d.ts +3 -1
  56. package/dist/types/utils/external-editor.d.ts +12 -0
  57. package/examples/extensions/README.md +1 -1
  58. package/examples/extensions/pirate.ts +7 -4
  59. package/package.json +7 -7
  60. package/scripts/generate-sdk-operation-inventory.ts +7 -1
  61. package/src/async/job-manager.ts +107 -9
  62. package/src/browser-backend.ts +29 -0
  63. package/src/cli/plugin-cli.ts +29 -0
  64. package/src/cli/update-cli.ts +63 -12
  65. package/src/commands/sdk.ts +22 -7
  66. package/src/config/billing-path.ts +42 -0
  67. package/src/config/config-schema-version.ts +18 -1
  68. package/src/config/model-preset-registry.ts +177 -18
  69. package/src/config/model-registry.ts +268 -42
  70. package/src/config/settings-schema.ts +19 -7
  71. package/src/config/settings.ts +46 -15
  72. package/src/coordinator-mcp/projection-scan.ts +6 -6
  73. package/src/coordinator-mcp/server.ts +21 -1
  74. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  75. package/src/extensibility/extensions/runner.ts +27 -0
  76. package/src/extensibility/extensions/types.ts +8 -3
  77. package/src/extensibility/gjc-plugins/installer.ts +51 -11
  78. package/src/extensibility/runtime-skill-discovery.ts +110 -3
  79. package/src/extensibility/skills.ts +22 -12
  80. package/src/gjc-runtime/launch-worktree.ts +285 -4
  81. package/src/gjc-runtime/session-state-lock.ts +156 -32
  82. package/src/gjc-runtime/session-state-sidecar.ts +10 -17
  83. package/src/gjc-runtime/tmux-owner-isolation.ts +16 -1
  84. package/src/internal-urls/docs-index.generated.ts +11 -11
  85. package/src/main.ts +4 -1
  86. package/src/modes/acp/acp-agent.ts +34 -19
  87. package/src/modes/components/model-selector.ts +76 -8
  88. package/src/modes/components/session-selector.ts +23 -15
  89. package/src/modes/components/sessions-dashboard.ts +8 -5
  90. package/src/modes/controllers/command-controller.ts +1 -1
  91. package/src/modes/controllers/event-controller.ts +30 -4
  92. package/src/modes/controllers/extension-ui-controller.ts +13 -13
  93. package/src/modes/controllers/input-controller.ts +2 -2
  94. package/src/modes/controllers/selector-controller.ts +10 -3
  95. package/src/modes/interactive-mode.ts +36 -12
  96. package/src/modes/prompt-action-autocomplete.ts +30 -4
  97. package/src/modes/runtime-init.ts +12 -9
  98. package/src/modes/theme/theme.ts +5 -0
  99. package/src/modes/types.ts +2 -0
  100. package/src/modes/utils/ui-helpers.ts +22 -3
  101. package/src/prompts/agent-fragments/browser-aside.md +22 -0
  102. package/src/prompts/tools/ask.md +1 -1
  103. package/src/prompts/tools/bash.md +1 -0
  104. package/src/prompts/tools/job.md +1 -1
  105. package/src/sdk/acp/adapter.ts +6 -1
  106. package/src/sdk/broker/broker.ts +10 -1
  107. package/src/sdk/broker/ensure.ts +76 -3
  108. package/src/sdk/broker/lifecycle-ledger.ts +6 -0
  109. package/src/sdk/broker/lifecycle.ts +254 -34
  110. package/src/sdk/broker/spawn-substrate.ts +45 -2
  111. package/src/sdk/broker/startup-budget.ts +144 -2
  112. package/src/sdk/broker/state-version.ts +17 -0
  113. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  114. package/src/sdk/bus/chat-daemon-runtime.ts +118 -24
  115. package/src/sdk/bus/discord-daemon.ts +50 -10
  116. package/src/sdk/bus/index.ts +33 -3
  117. package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
  118. package/src/sdk/bus/lifecycle-commands.ts +4 -0
  119. package/src/sdk/bus/notification-service.ts +22 -5
  120. package/src/sdk/bus/prompt-reconciliation.ts +7 -2
  121. package/src/sdk/bus/reconciliation-store.ts +55 -31
  122. package/src/sdk/bus/slack-conversation.ts +1 -1
  123. package/src/sdk/bus/slack-daemon.ts +72 -38
  124. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  125. package/src/sdk/cli/rows.ts +75 -7
  126. package/src/sdk/cli/session-cli.ts +68 -35
  127. package/src/sdk/host/control/dispatch.ts +2 -2
  128. package/src/sdk/host/events.ts +26 -2
  129. package/src/sdk/host/host.ts +6 -1
  130. package/src/sdk/host/query/cursor.ts +52 -7
  131. package/src/sdk/host/query/handlers.ts +33 -12
  132. package/src/sdk/host/query/revision-store.ts +12 -0
  133. package/src/sdk/host/session-runtime.test.ts +497 -31
  134. package/src/sdk/host/session-runtime.ts +387 -54
  135. package/src/sdk/host/surface-policy.ts +3 -3
  136. package/src/sdk/lifecycle-session.ts +7 -2
  137. package/src/sdk/prompt-deadline-manager.ts +2 -1
  138. package/src/sdk/protocol/operation-inventory.generated.json +54 -3
  139. package/src/sdk/protocol/operation-registry.ts +1 -1
  140. package/src/sdk/receipt-state.ts +10 -0
  141. package/src/sdk/router/session-router.ts +210 -94
  142. package/src/sdk/session.ts +59 -41
  143. package/src/sdk/turn-result.ts +22 -0
  144. package/src/session/agent-session.ts +1033 -260
  145. package/src/session/fold-coordinator.ts +40 -3
  146. package/src/session/session-manager.ts +517 -77
  147. package/src/slash-commands/builtin-registry.ts +41 -0
  148. package/src/task/executor.ts +9 -4
  149. package/src/tools/ask-contract.test.ts +64 -0
  150. package/src/tools/ask-contract.ts +36 -1
  151. package/src/tools/ask.ts +10 -8
  152. package/src/tools/bash-interactive.ts +11 -2
  153. package/src/tools/bash.ts +158 -33
  154. package/src/tools/browser/launch.ts +37 -0
  155. package/src/tools/browser/registry.ts +99 -9
  156. package/src/tools/cron.ts +6 -4
  157. package/src/tools/descriptors.test.ts +15 -0
  158. package/src/tools/descriptors.ts +5 -1
  159. package/src/tools/index.ts +4 -6
  160. package/src/tools/job.ts +4 -1
  161. package/src/tools/monitor.ts +12 -9
  162. package/src/tools/resolve.ts +30 -10
  163. package/src/tools/tool-catalog.generated.ts +2 -2
  164. package/src/utils/external-editor.ts +86 -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,8 @@ 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),
2717
2723
 
2718
2724
  getCredentialSessionId: () => session?.credentialSessionId ?? credentialSessionId,
2719
2725
  getMcpManager: () => mcpManager ?? options.inheritedMcpManager,
@@ -2770,16 +2776,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2770
2776
  return m ? buildNamedToolChoice(name, m) : undefined;
2771
2777
  },
2772
2778
  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
2779
  sendCustomMessage: (msg, opts) => session.sendCustomMessage(msg, opts),
2784
2780
  purgeQueuedCustomMessages: predicate => session.purgeQueuedCustomMessages(predicate),
2785
2781
  peekQueueInvoker: () => session.peekQueueInvoker(),
@@ -3562,7 +3558,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
3562
3558
  !model &&
3563
3559
  !options.modelPattern &&
3564
3560
  !startupCredentialModelRejected &&
3565
- configuredDefaultPatterns.length === 0
3561
+ (configuredDefaultPatterns.length === 0 || preferredCredentialProvider !== undefined)
3566
3562
  ) {
3567
3563
  // Re-resolve the allowed set: extension factories above may have
3568
3564
  // registered providers/models that weren't visible at startup.
@@ -3896,7 +3892,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
3896
3892
  ? await memoryBackend.buildDeveloperInstructions(agentDir, settings, session)
3897
3893
  : undefined;
3898
3894
 
3899
- const appendPrompt: string | undefined = memoryInstructions ?? undefined;
3895
+ const browserBackend = resolveBrowserBackend(settings);
3896
+ const appendPrompt =
3897
+ [memoryInstructions, browserBackend.developerInstructions].filter(Boolean).join("\n\n") || undefined;
3900
3898
  let pluginSystemAppendices = "";
3901
3899
  try {
3902
3900
  pluginSystemAppendices = await renderAlwaysOnSystemAppendices({ cwd: getLiveCwd() });
@@ -4277,7 +4275,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
4277
4275
  transformContext,
4278
4276
  steeringMode: settings.get("steeringMode") ?? "one-at-a-time",
4279
4277
  followUpMode: settings.get("followUpMode") ?? "one-at-a-time",
4280
- interruptMode: settings.get("interruptMode") ?? "immediate",
4278
+ toolInterruptPolicy: settings.get("toolInterruptPolicy") ?? "abort_tools",
4281
4279
  thinkingBudgets: settings.getGroup("thinkingBudgets"),
4282
4280
  temperature: settings.get("temperature") >= 0 ? settings.get("temperature") : undefined,
4283
4281
  topP: settings.get("topP") >= 0 ? settings.get("topP") : undefined,
@@ -4575,38 +4573,49 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
4575
4573
 
4576
4574
  // Attach the live session to the pre-registered ref so peers can route IRC
4577
4575
  // messages here. Refresh sessionFile in case it was unavailable at pre-register
4578
- // time. The dispose wrapper below unregisters on teardown.
4576
+ // time. The registered terminal cleanup below unregisters on teardown.
4579
4577
  agentRegistry.attachSession(resolvedAgentId, session, sessionManager.getSessionFile() ?? null);
4580
4578
  {
4581
- const originalDispose = session.dispose.bind(session);
4582
- session.dispose = async () => {
4583
- try {
4584
- await originalDispose();
4585
- } finally {
4579
+ let cleanupPromise: Promise<void> | undefined;
4580
+ const finishCleanup = async (): Promise<void> => {
4581
+ if (cleanupPromise) return cleanupPromise;
4582
+ const cleanup = (async () => {
4583
+ const failures: unknown[] = [];
4586
4584
  try {
4587
4585
  agentRegistry.unregister(resolvedAgentId);
4588
4586
  releaseCredentialDisabledSubscription();
4589
4587
  releaseLocalProtocolOverride();
4588
+ } catch (error) {
4589
+ failures.push(error);
4590
4590
  } 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);
4591
+ try {
4592
+ if (!options.parentTaskPrefix) {
4593
+ retireOwnedRegistrationsForEndpoint(
4594
+ AsyncJobManager.endpointIdOf(asyncJobManager) ?? asyncJobEndpointId,
4595
+ );
4596
+ AsyncJobManager.unregisterManager(asyncJobManager);
4597
+ }
4598
+ } catch (error) {
4599
+ failures.push(error);
4600
+ }
4601
+ try {
4602
+ await closeOwnedAuthStorage();
4603
+ } catch (error) {
4604
+ failures.push(error);
4605
+ }
4606
+ try {
4607
+ await closeOwnedSettings();
4608
+ } catch (error) {
4609
+ failures.push(error);
4604
4610
  }
4605
- await closeOwnedAuthStorage();
4606
- await closeOwnedSettings();
4607
4611
  }
4608
- }
4612
+ if (failures.length > 0) throw new AggregateError(failures, "SDK disposal cleanup failed.");
4613
+ })();
4614
+ cleanupPromise = cleanup;
4615
+ void cleanup.catch(() => {});
4616
+ return cleanup;
4609
4617
  };
4618
+ session.registerToolSessionCleanup(finishCleanup);
4610
4619
  }
4611
4620
 
4612
4621
  if (model?.api === "openai-codex-responses") {
@@ -4834,7 +4843,16 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
4834
4843
  let cleanupDiagnostic: unknown;
4835
4844
  try {
4836
4845
  if (hasSession) {
4837
- await session.dispose();
4846
+ try {
4847
+ await session.dispose();
4848
+ } catch (disposeError) {
4849
+ if (!isSessionDisposalIncompleteError(disposeError)) {
4850
+ throw disposeError;
4851
+ }
4852
+ // The first call is intentionally bounded for startup callers. Join the
4853
+ // retained teardown before releasing the resources it still owns.
4854
+ await session.awaitDisposeCompletion();
4855
+ }
4838
4856
  } else {
4839
4857
  if (hasRegistered) agentRegistry.unregister(resolvedAgentId);
4840
4858
  // 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
+ }