@gajae-code/coding-agent 0.18.8 → 0.18.9

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 (113) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/dist/types/async/endpoint-id.d.ts +16 -0
  3. package/dist/types/async/support.d.ts +0 -14
  4. package/dist/types/commands/mcp-serve.d.ts +4 -0
  5. package/dist/types/discovery/mcp-json.d.ts +1 -0
  6. package/dist/types/eval/py/kernel.d.ts +18 -0
  7. package/dist/types/export/html/html-export-destination.d.ts +2 -0
  8. package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +5 -0
  9. package/dist/types/extensibility/skill-management.d.ts +1 -0
  10. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -3
  11. package/dist/types/gjc-runtime/sidecar-bootstrap-env.d.ts +6 -0
  12. package/dist/types/gjc-runtime/state-writer.d.ts +3 -0
  13. package/dist/types/gjc-runtime/tmux-common.d.ts +8 -5
  14. package/dist/types/lsp/config.d.ts +8 -5
  15. package/dist/types/runtime-mcp/manager.d.ts +2 -1
  16. package/dist/types/runtime-mcp/mcp-redirect.d.ts +9 -0
  17. package/dist/types/runtime-mcp/oauth-public-url.d.ts +3 -1
  18. package/dist/types/runtime-mcp/project-host-values.d.ts +27 -0
  19. package/dist/types/sdk/broker/broker.d.ts +6 -1
  20. package/dist/types/sdk/broker/doctor-restart.d.ts +5 -0
  21. package/dist/types/sdk/broker/ensure.d.ts +14 -1
  22. package/dist/types/session/session-storage.d.ts +7 -0
  23. package/dist/types/skill-state/workflow-mutation-guard.d.ts +3 -0
  24. package/dist/types/task/isolation-dir.d.ts +26 -0
  25. package/dist/types/tools/ast-edit-write-path.d.ts +13 -0
  26. package/dist/types/tools/browser/cdp-url.d.ts +2 -0
  27. package/dist/types/tools/github-side-effect.d.ts +22 -0
  28. package/dist/types/tools/monitor-mutation.d.ts +9 -0
  29. package/package.json +7 -7
  30. package/src/async/endpoint-id.ts +26 -0
  31. package/src/async/job-manager.ts +19 -10
  32. package/src/async/support.ts +0 -25
  33. package/src/capability/index.ts +14 -16
  34. package/src/cli/doctor/repairs.ts +4 -1
  35. package/src/cli/doctor-supervisor.ts +3 -0
  36. package/src/cli/public-command-registry.ts +28 -6
  37. package/src/commands/mcp-serve.ts +7 -0
  38. package/src/commands/sdk.ts +453 -415
  39. package/src/config/file-lock.ts +9 -11
  40. package/src/config/model-profile-activation.ts +5 -5
  41. package/src/coordinator-mcp/server.ts +2 -0
  42. package/src/customization/inventory.ts +1 -13
  43. package/src/dap/config.ts +2 -2
  44. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +10 -6
  45. package/src/discovery/builtin.ts +3 -1
  46. package/src/discovery/helpers.ts +12 -9
  47. package/src/discovery/mcp-json.ts +20 -12
  48. package/src/eval/py/executor.ts +459 -77
  49. package/src/eval/py/kernel.ts +79 -13
  50. package/src/eval/py/tool-bridge.ts +4 -0
  51. package/src/export/html/html-export-destination.ts +16 -0
  52. package/src/export/html/index.ts +2 -6
  53. package/src/extensibility/extensions/function-hooks-internal.ts +27 -0
  54. package/src/extensibility/extensions/runner.ts +11 -1
  55. package/src/extensibility/gjc-plugins/mcp-policy.ts +2 -1
  56. package/src/extensibility/gjc-plugins/runtime-adapters.ts +27 -15
  57. package/src/extensibility/skill-management.ts +3 -0
  58. package/src/gjc-runtime/launch-worktree.ts +56 -5
  59. package/src/gjc-runtime/psmux-detect.ts +28 -4
  60. package/src/gjc-runtime/session-state-sidecar.ts +3 -2
  61. package/src/gjc-runtime/sidecar-bootstrap-env.ts +19 -0
  62. package/src/gjc-runtime/state-writer.ts +22 -8
  63. package/src/gjc-runtime/tmux-common.ts +8 -5
  64. package/src/internal-urls/docs-index.generated.ts +7 -7
  65. package/src/lsp/config.ts +12 -63
  66. package/src/memories/index.ts +88 -7
  67. package/src/modes/components/hook-editor.ts +1 -1
  68. package/src/modes/components/hook-selector.ts +1 -1
  69. package/src/modes/controllers/input-controller.ts +2 -2
  70. package/src/modes/controllers/runtime-mcp-command-controller.ts +21 -13
  71. package/src/modes/controllers/selector-controller.ts +3 -7
  72. package/src/modes/controllers/todo-command-controller.ts +1 -1
  73. package/src/modes/interactive-mode.ts +1 -1
  74. package/src/runtime-mcp/client.ts +4 -2
  75. package/src/runtime-mcp/json-rpc.ts +2 -1
  76. package/src/runtime-mcp/manager.ts +29 -11
  77. package/src/runtime-mcp/mcp-redirect.ts +73 -0
  78. package/src/runtime-mcp/oauth-flow.ts +11 -4
  79. package/src/runtime-mcp/oauth-public-url.ts +8 -2
  80. package/src/runtime-mcp/pool.ts +6 -0
  81. package/src/runtime-mcp/project-host-values.ts +46 -0
  82. package/src/runtime-mcp/transports/http.ts +2 -1
  83. package/src/sdk/acp/adapter.ts +2 -0
  84. package/src/sdk/broker/broker.ts +6 -0
  85. package/src/sdk/broker/doctor-restart.ts +4 -0
  86. package/src/sdk/broker/ensure.ts +29 -2
  87. package/src/sdk/host/session-runtime.test.ts +58 -9
  88. package/src/sdk/host/session-runtime.ts +65 -35
  89. package/src/sdk/router/session-router.ts +7 -6
  90. package/src/sdk/session.ts +2 -7
  91. package/src/session/agent-session.ts +59 -17
  92. package/src/session/artifacts.ts +3 -2
  93. package/src/session/internal/managed-session-scope.ts +403 -81
  94. package/src/session/internal/managed-session-storage.ts +125 -17
  95. package/src/session/session-manager.ts +12 -0
  96. package/src/session/session-storage.ts +30 -1
  97. package/src/skill-state/active-state.ts +13 -0
  98. package/src/skill-state/workflow-mutation-guard.ts +102 -10
  99. package/src/slash-commands/builtin-registry.ts +1 -1
  100. package/src/slash-commands/helpers/mcp.ts +8 -4
  101. package/src/task/isolation-dir.ts +143 -0
  102. package/src/task/worktree.ts +16 -7
  103. package/src/tools/ast-edit-write-path.ts +68 -0
  104. package/src/tools/ast-edit.ts +7 -2
  105. package/src/tools/browser/cdp-url.ts +18 -0
  106. package/src/tools/browser.ts +8 -1
  107. package/src/tools/gh.ts +10 -0
  108. package/src/tools/github-side-effect.ts +112 -0
  109. package/src/tools/monitor-mutation.ts +27 -0
  110. package/src/tools/monitor.ts +7 -0
  111. package/src/tools/python.ts +7 -6
  112. package/src/utils/external-editor.ts +3 -2
  113. package/src/web/insane/url-guard.ts +2 -1
@@ -215,12 +215,12 @@ import {
215
215
  type AsyncJob,
216
216
  type AsyncJobDeliveryState,
217
217
  AsyncJobManager,
218
- asyncJobEndpointId as deriveAsyncJobEndpointId,
219
218
  type FoldReason,
220
219
  type JobFoldEvent,
221
220
  type OwnerSubagentShutdownLease,
222
221
  type SubagentLifecycle,
223
222
  } from "../async";
223
+ import { asyncJobEndpointId as deriveAsyncJobEndpointId } from "../async/endpoint-id";
224
224
  import { reset as resetCapabilities } from "../capability";
225
225
  import type { Rule } from "../capability/rule";
226
226
  import type { CasReceipt } from "../config/atomic-yaml-patch";
@@ -238,11 +238,8 @@ import {
238
238
  } from "../config/model-profile-contract";
239
239
  import {
240
240
  commitDurableModelProfileOwnership,
241
- type DurableModelProfileOwnership,
242
241
  InvalidModelProfileOwnershipError,
243
242
  ModelProfileOwnershipConflictError,
244
- type ModelProfileOwnershipMarker,
245
- modelProfileOwnershipMarkersEqual,
246
243
  readDurableModelProfileOwnership,
247
244
  } from "../config/model-profile-ownership";
248
245
  import { resolveProfileBindings } from "../config/model-profiles";
@@ -427,7 +424,7 @@ import {
427
424
  readVisibleSkillActiveState,
428
425
  syncSkillActiveState,
429
426
  } from "../skill-state/active-state";
430
- import { assertWorkflowMutationAllowed } from "../skill-state/workflow-mutation-guard";
427
+ import { assertWorkflowMutationAllowed, isWorkflowMutationTool } from "../skill-state/workflow-mutation-guard";
431
428
  import { invalidateHostMetadata } from "../ssh/connection-manager";
432
429
  import { buildVolatileProjectContext } from "../system-prompt";
433
430
  import { DelegationHintController } from "../task/delegation-hint";
@@ -452,6 +449,7 @@ import {
452
449
  import { assertEditableFile } from "../tools/auto-generated-guard";
453
450
  import { releaseTabsForOwner } from "../tools/browser/tab-supervisor";
454
451
  import type { CheckpointState } from "../tools/checkpoint";
452
+ import { githubPermissionIntent } from "../tools/github-side-effect";
455
453
  import { outputMeta, wrapToolWithMetaNotice } from "../tools/output-meta";
456
454
  import { normalizeLocalScheme, resolveReadPath, resolveToCwd } from "../tools/path-utils";
457
455
  import { registerResourceGcSession } from "../tools/resource-gc";
@@ -922,6 +920,28 @@ export interface QueuedInputSubmission {
922
920
 
923
921
  /** Listener function for agent session events */
924
922
  export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
923
+
924
+ /**
925
+ * Deliver one event to one listener, containing both synchronous throws and
926
+ * async rejections. Listeners are `void`-typed but interactive mode subscribes
927
+ * with an `async` one whose handlers can persist (plan approval calls
928
+ * `session.prompt()`); a rejection there, e.g. the managed append fence after
929
+ * another process took over the transcript, must not escape as an unhandled
930
+ * rejection and terminate the process.
931
+ */
932
+ function deliverSessionEvent(
933
+ listener: AgentSessionEventListener,
934
+ event: AgentSessionEvent,
935
+ onError: (error: unknown) => void,
936
+ ): void {
937
+ try {
938
+ const result: unknown = listener(event);
939
+ if (result instanceof Promise) result.catch(onError);
940
+ } catch (error) {
941
+ onError(error);
942
+ }
943
+ }
944
+
925
945
  export type AsyncJobSnapshotItem = Pick<
926
946
  AsyncJob,
927
947
  "id" | "type" | "status" | "label" | "startTime" | "endTime" | "metadata"
@@ -1990,7 +2010,7 @@ async function discardPreparedNewSessionAfterFailure(
1990
2010
  // ============================================================================
1991
2011
 
1992
2012
  /** Tools that require user permission before execution when an ACP client is connected. */
1993
- const PERMISSION_REQUIRED_TOOLS = new Set(["bash", "monitor", "eval", "edit", "delete", "move"]);
2013
+ const PERMISSION_REQUIRED_TOOLS = new Set(["bash", "monitor", "eval", "edit", "delete", "move", "github"]);
1994
2014
 
1995
2015
  function isShellExecutionPermissionTool(toolName: string): boolean {
1996
2016
  return toolName === "bash" || toolName === "monitor";
@@ -2114,6 +2134,7 @@ function getPermissionIntent(
2114
2134
  cacheKey: "edit:move",
2115
2135
  };
2116
2136
  }
2137
+ if (toolName === "github") return githubPermissionIntent(args);
2117
2138
  return undefined;
2118
2139
  }
2119
2140
 
@@ -6931,11 +6952,9 @@ export class AgentSession {
6931
6952
  /** Emit an event to all listeners without letting one subscriber poison lifecycle settlement. */
6932
6953
  #emit(event: AgentSessionEvent): void {
6933
6954
  for (const listener of this.#eventListenerSnapshot) {
6934
- try {
6935
- listener(event);
6936
- } catch (error) {
6955
+ deliverSessionEvent(listener, event, error => {
6937
6956
  logger.warn("Agent session event subscriber failed", { event: event.type, error: String(error) });
6938
- }
6957
+ });
6939
6958
  }
6940
6959
  }
6941
6960
 
@@ -11815,7 +11834,9 @@ export class AgentSession {
11815
11834
  : undefined;
11816
11835
  const commandContent = command
11817
11836
  ? [{ type: "content" as const, content: { type: "text" as const, text: `$ ${command}` } }]
11818
- : undefined;
11837
+ : target.name === "github"
11838
+ ? [{ type: "content" as const, content: { type: "text" as const, text: permissionIntent.title } }]
11839
+ : undefined;
11819
11840
  if (this.#sdkPermissionMode === "allow") {
11820
11841
  return await target.execute(toolCallId, args as never, signal, onUpdate, ctx);
11821
11842
  }
@@ -11928,7 +11949,7 @@ export class AgentSession {
11928
11949
  /**
11929
11950
  /** Wrap a tool with the workflow mutation guard before permissions or execution. */
11930
11951
  #wrapToolForWorkflowMutationGuard<T extends AgentTool>(tool: T): T {
11931
- if (!["edit", "write", "ast_edit", "bash"].includes(tool.name)) return tool;
11952
+ if (!isWorkflowMutationTool(tool.name)) return tool;
11932
11953
  return new Proxy(tool, {
11933
11954
  get: (target, prop) => {
11934
11955
  if (prop !== "execute") return Reflect.get(target, prop, target);
@@ -18985,14 +19006,12 @@ export class AgentSession {
18985
19006
  if (thinkingLevelChanged) {
18986
19007
  const event: AgentSessionEvent = { type: "thinking_level_changed", thinkingLevel };
18987
19008
  for (const listener of this.#eventListenerSnapshot) {
18988
- try {
18989
- listener(event);
18990
- } catch {
19009
+ deliverSessionEvent(listener, event, () => {
18991
19010
  logger.warn("Default model selection event listener failed", {
18992
19011
  code: "default_model_selection_listener_failed",
18993
19012
  disposition: "continue",
18994
19013
  });
18995
- }
19014
+ });
18996
19015
  }
18997
19016
  }
18998
19017
  this.#applyPreparedDefaultModelSelectionPrompt(systemPrompt);
@@ -20983,7 +21002,28 @@ export class AgentSession {
20983
21002
  const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
20984
21003
  const errorIsFromBeforeCompaction =
20985
21004
  compactionEntry !== null && assistantMessage.timestamp < new Date(compactionEntry.timestamp).getTime();
21005
+ // A successful empty stop with nonzero usage is not overflow evidence by usage
21006
+ // alone. Zero-usage empty stops stay on the legacy proxy-overflow path, and typed
21007
+ // overflow classification is preserved; only the empty-stop heuristic is disabled.
21008
+ const hasOverflowEvidenceBeyondEmptyStopHeuristic =
21009
+ assistantMessage.stopReason === "stop" &&
21010
+ assistantMessage.content.length === 0 &&
21011
+ classifyContextOverflow(
21012
+ { ...assistantMessage, content: [{ type: "text", text: "" }] },
21013
+ assistantMessage.transportFailure,
21014
+ contextWindow,
21015
+ );
21016
+ const emptyStopPromptTokens =
21017
+ assistantMessage.usage.input + assistantMessage.usage.cacheRead + assistantMessage.usage.cacheWrite;
21018
+ const successfulEmptyStop =
21019
+ assistantMessage.stopReason === "stop" &&
21020
+ assistantMessage.content.length === 0 &&
21021
+ (emptyStopPromptTokens + assistantMessage.usage.output > 0 ||
21022
+ assistantMessage.usage.totalTokens > 0) &&
21023
+ emptyStopPromptTokens <= contextWindow &&
21024
+ !hasOverflowEvidenceBeyondEmptyStopHeuristic;
20986
21025
  if (
21026
+ !successfulEmptyStop &&
20987
21027
  sameModel &&
20988
21028
  !errorIsFromBeforeCompaction &&
20989
21029
  classifyContextOverflow(assistantMessage, assistantMessage.transportFailure, contextWindow)
@@ -23677,6 +23717,7 @@ export class AgentSession {
23677
23717
 
23678
23718
  #managedFallbackPromptOptions(): {
23679
23719
  fallbackManaged?: boolean;
23720
+ contextOverflowManaged?: boolean;
23680
23721
  nextFallbackAttempt?: (model: Model) => FallbackAttemptToken;
23681
23722
  onManagedAttemptAccepted?: () => void;
23682
23723
  onManagedAttemptOutcome?: (
@@ -23684,9 +23725,10 @@ export class AgentSession {
23684
23725
  ) => ManagedAttemptDecision | Promise<ManagedAttemptDecision>;
23685
23726
  } {
23686
23727
  const controller = this.#defaultFallbackChain();
23687
- if (controller.chain.entries.length < 2) return {};
23728
+ if (controller.chain.entries.length < 2) return { contextOverflowManaged: true };
23688
23729
  return {
23689
23730
  fallbackManaged: true,
23731
+ contextOverflowManaged: true,
23690
23732
  nextFallbackAttempt: model => {
23691
23733
  controller.onAttemptStarted();
23692
23734
  this.#managedFallbackProviderAttemptCount++;
@@ -15,7 +15,7 @@ import {
15
15
  publishManagedFileNoReplace,
16
16
  publishManagedFileNoReplaceSync,
17
17
  } from "./internal/managed-session-storage";
18
- import type { SessionStorageStat } from "./session-storage";
18
+ import { type SessionStorageStat, writeExplicitOwnerOnlyTextSync } from "./session-storage";
19
19
  import { DEFAULT_ARTIFACT_MAX_BYTES, truncateHeadBytes } from "./streaming-output";
20
20
 
21
21
  export interface ManagedOutputGeneration {
@@ -246,7 +246,8 @@ export class ArtifactManager {
246
246
  if (!/^[a-zA-Z0-9_.-]+$/.test(filename)) throw new Error("Unsafe named artifact");
247
247
  await this.#ensureDir();
248
248
  if (this.#store) await this.#store.replace(filename, Buffer.from(content, "utf8"));
249
- else await Bun.write(path.join(this.#dir, filename), content);
249
+ // Named files, including draft.txt, are created here.
250
+ else writeExplicitOwnerOnlyTextSync(path.join(this.#dir, filename), content);
250
251
  }
251
252
 
252
253
  async replaceNamedBytes(filename: string, bytes: Uint8Array): Promise<void> {