@gajae-code/coding-agent 0.16.4 → 0.16.6

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 (198) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/types/capability/types.d.ts +4 -0
  3. package/dist/types/cli/customize-doctor.d.ts +22 -0
  4. package/dist/types/config/file-lock.d.ts +31 -3
  5. package/dist/types/coordinator-mcp/question-state.d.ts +7 -0
  6. package/dist/types/coordinator-mcp/server.d.ts +7 -0
  7. package/dist/types/discovery/helpers.d.ts +33 -0
  8. package/dist/types/exec/bash-executor.d.ts +17 -1
  9. package/dist/types/exec/bash-shell-guardian.d.ts +15 -0
  10. package/dist/types/exec/bash-shell-runtime-entry.d.ts +2 -0
  11. package/dist/types/exec/bash-shell-signals.d.ts +2 -0
  12. package/dist/types/exec/bash-shell-supervisor-entry.d.ts +1 -0
  13. package/dist/types/exec/bash-shell-supervisor.d.ts +10 -0
  14. package/dist/types/exec/bash-shell-worker-entry.d.ts +2 -0
  15. package/dist/types/exec/bash-shell-worker-protocol.d.ts +63 -0
  16. package/dist/types/exec/bash-shell-worker.d.ts +1 -0
  17. package/dist/types/exec/isolated-shell.d.ts +19 -0
  18. package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +9 -0
  19. package/dist/types/extensibility/extensions/function-hooks.d.ts +169 -0
  20. package/dist/types/extensibility/extensions/index.d.ts +1 -0
  21. package/dist/types/extensibility/extensions/runner.d.ts +46 -6
  22. package/dist/types/extensibility/extensions/types.d.ts +16 -0
  23. package/dist/types/extensibility/extensions/wrapper.d.ts +3 -1
  24. package/dist/types/extensibility/gjc-plugins/constrained-hooks.d.ts +35 -2
  25. package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
  26. package/dist/types/extensibility/hooks/types.d.ts +4 -0
  27. package/dist/types/extensibility/runtime-skill-discovery.d.ts +4 -1
  28. package/dist/types/extensibility/skill-management.d.ts +9 -4
  29. package/dist/types/extensibility/skills.d.ts +2 -0
  30. package/dist/types/gjc-runtime/empty-delete-gc.d.ts +4 -1
  31. package/dist/types/gjc-runtime/linux-proc.d.ts +2 -0
  32. package/dist/types/gjc-runtime/session-state-lock.d.ts +12 -0
  33. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +41 -3
  34. package/dist/types/gjc-runtime/ultragoal-succession.d.ts +251 -0
  35. package/dist/types/modes/components/bash-execution.d.ts +1 -0
  36. package/dist/types/modes/components/execution-shared.d.ts +1 -0
  37. package/dist/types/modes/interactive-mode.d.ts +2 -0
  38. package/dist/types/modes/types.d.ts +6 -0
  39. package/dist/types/sdk/broker/ensure.d.ts +32 -0
  40. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +0 -1
  41. package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
  42. package/dist/types/sdk/broker/startup-failure.d.ts +4 -1
  43. package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -0
  44. package/dist/types/sdk/bus/chat-effect-journal.d.ts +17 -2
  45. package/dist/types/sdk/bus/conversation-store.d.ts +11 -0
  46. package/dist/types/sdk/bus/lifecycle-commands.d.ts +8 -8
  47. package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
  48. package/dist/types/sdk/bus/topic-registry.d.ts +8 -0
  49. package/dist/types/sdk/client/client.d.ts +6 -0
  50. package/dist/types/sdk/host/host.d.ts +3 -1
  51. package/dist/types/sdk/host/session-runtime.d.ts +4 -2
  52. package/dist/types/sdk/lifecycle/service.d.ts +1 -0
  53. package/dist/types/sdk/router/session-router.d.ts +10 -2
  54. package/dist/types/sdk/session.d.ts +5 -1
  55. package/dist/types/session/agent-session.d.ts +2 -0
  56. package/dist/types/session/fallback-chain-controller.d.ts +15 -0
  57. package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
  58. package/dist/types/session/messages.d.ts +1 -0
  59. package/dist/types/session/session-manager.d.ts +3 -1
  60. package/dist/types/setup/paseo/announce.d.ts +4 -3
  61. package/dist/types/skill-state/canonical-skills.d.ts +4 -0
  62. package/dist/types/system-prompt.d.ts +8 -0
  63. package/dist/types/task/executor.d.ts +3 -0
  64. package/dist/types/tools/bash.d.ts +1 -0
  65. package/dist/types/tools/github-cache.d.ts +2 -1
  66. package/dist/types/tools/index.d.ts +2 -0
  67. package/package.json +9 -7
  68. package/scripts/generate-sdk-operation-inventory.ts +2 -0
  69. package/src/capability/index.ts +14 -2
  70. package/src/capability/types.ts +4 -0
  71. package/src/cli/customize-doctor.ts +155 -12
  72. package/src/cli.ts +22 -0
  73. package/src/commands/sdk.ts +74 -31
  74. package/src/config/file-lock-gc.ts +40 -2
  75. package/src/config/file-lock.ts +198 -14
  76. package/src/config/model-preset-registry.ts +115 -16
  77. package/src/config/model-registry.ts +16 -12
  78. package/src/coordinator-mcp/question-state.ts +33 -2
  79. package/src/coordinator-mcp/server.ts +477 -101
  80. package/src/crash/sanitize.ts +1 -1
  81. package/src/defaults/gjc/skills/ultragoal/SKILL.md +22 -0
  82. package/src/discovery/builtin.ts +50 -62
  83. package/src/discovery/helpers.ts +297 -32
  84. package/src/exec/bash-executor.ts +330 -50
  85. package/src/exec/bash-shell-guardian.ts +388 -0
  86. package/src/exec/bash-shell-runtime-entry.ts +5 -0
  87. package/src/exec/bash-shell-signals.ts +29 -0
  88. package/src/exec/bash-shell-supervisor-entry.ts +3 -0
  89. package/src/exec/bash-shell-supervisor.ts +359 -0
  90. package/src/exec/bash-shell-worker-entry.ts +5 -0
  91. package/src/exec/bash-shell-worker-protocol.ts +36 -0
  92. package/src/exec/bash-shell-worker.ts +116 -0
  93. package/src/exec/isolated-shell.ts +393 -0
  94. package/src/extensibility/extensions/function-hooks-internal.ts +73 -0
  95. package/src/extensibility/extensions/function-hooks.ts +959 -0
  96. package/src/extensibility/extensions/index.ts +22 -0
  97. package/src/extensibility/extensions/loader.ts +53 -2
  98. package/src/extensibility/extensions/runner.ts +895 -264
  99. package/src/extensibility/extensions/types.ts +42 -0
  100. package/src/extensibility/extensions/wrapper.ts +56 -15
  101. package/src/extensibility/gjc-plugins/compiler.ts +20 -2
  102. package/src/extensibility/gjc-plugins/constrained-hooks.ts +321 -23
  103. package/src/extensibility/gjc-plugins/registry.ts +113 -1
  104. package/src/extensibility/gjc-plugins/runtime-quarantine.ts +20 -2
  105. package/src/extensibility/gjc-plugins/schema.ts +121 -4
  106. package/src/extensibility/gjc-plugins/types.ts +25 -0
  107. package/src/extensibility/hooks/loader.ts +133 -16
  108. package/src/extensibility/hooks/types.ts +28 -0
  109. package/src/extensibility/runtime-skill-discovery.ts +80 -47
  110. package/src/extensibility/skill-management.ts +200 -52
  111. package/src/extensibility/skills.ts +29 -9
  112. package/src/gjc-runtime/empty-delete-gc.ts +94 -4
  113. package/src/gjc-runtime/gc-runtime.ts +29 -0
  114. package/src/gjc-runtime/linux-proc.ts +14 -0
  115. package/src/gjc-runtime/session-state-lock.ts +51 -8
  116. package/src/gjc-runtime/session-state-sidecar.ts +27 -11
  117. package/src/gjc-runtime/state-writer.ts +30 -29
  118. package/src/gjc-runtime/ultragoal-runtime.ts +189 -47
  119. package/src/gjc-runtime/ultragoal-succession.ts +1667 -0
  120. package/src/gjc-runtime/workflow-manifest.generated.json +52 -1
  121. package/src/gjc-runtime/workflow-manifest.ts +16 -0
  122. package/src/harness-control-plane/session-lease.ts +2 -2
  123. package/src/internal-urls/docs-index.generated.ts +6 -6
  124. package/src/lsp/config.ts +10 -3
  125. package/src/lsp/index.ts +1 -1
  126. package/src/lsp/path-trust.ts +69 -14
  127. package/src/main.ts +6 -1
  128. package/src/modes/acp/acp-agent.ts +16 -3
  129. package/src/modes/components/bash-execution.ts +4 -1
  130. package/src/modes/components/execution-shared.ts +4 -1
  131. package/src/modes/components/session-observer-overlay.ts +1 -1
  132. package/src/modes/components/tool-status-header.ts +3 -1
  133. package/src/modes/controllers/command-controller.ts +1 -0
  134. package/src/modes/controllers/event-controller.ts +3 -2
  135. package/src/modes/controllers/extension-ui-controller.ts +1 -0
  136. package/src/modes/controllers/input-controller.ts +6 -3
  137. package/src/modes/interactive-mode.ts +6 -5
  138. package/src/modes/print-mode.ts +5 -1
  139. package/src/modes/prompt-action-autocomplete.ts +54 -27
  140. package/src/modes/runtime-init.ts +2 -0
  141. package/src/modes/types.ts +16 -3
  142. package/src/modes/utils/ui-helpers.ts +1 -0
  143. package/src/prompts/tools/subagent.md +1 -0
  144. package/src/runtime-mcp/manager.ts +20 -2
  145. package/src/runtime-mcp/pool.test.ts +20 -11
  146. package/src/sdk/broker/broker.ts +119 -28
  147. package/src/sdk/broker/ensure.ts +248 -41
  148. package/src/sdk/broker/lifecycle-ledger.ts +7 -17
  149. package/src/sdk/broker/lifecycle.ts +56 -6
  150. package/src/sdk/broker/startup-failure.ts +15 -6
  151. package/src/sdk/bus/chat-daemon-control.ts +3 -1
  152. package/src/sdk/bus/chat-daemon-runtime.ts +35 -5
  153. package/src/sdk/bus/chat-effect-journal.ts +132 -7
  154. package/src/sdk/bus/conversation-store.ts +29 -0
  155. package/src/sdk/bus/lifecycle-commands.ts +17 -11
  156. package/src/sdk/bus/slack-conversation.ts +1 -0
  157. package/src/sdk/bus/slack-daemon.ts +387 -48
  158. package/src/sdk/bus/telegram-daemon.ts +16 -2
  159. package/src/sdk/bus/topic-registry.ts +19 -0
  160. package/src/sdk/cli/master-cli.ts +1 -1
  161. package/src/sdk/cli/session-cli.ts +2 -2
  162. package/src/sdk/client/client.ts +66 -17
  163. package/src/sdk/host/host.ts +29 -9
  164. package/src/sdk/host/query/revision-store.ts +19 -13
  165. package/src/sdk/host/reverse-leases.ts +7 -4
  166. package/src/sdk/host/session-runtime.test.ts +50 -8
  167. package/src/sdk/host/session-runtime.ts +158 -51
  168. package/src/sdk/lifecycle/service.ts +4 -0
  169. package/src/sdk/protocol/operation-inventory.generated.json +11 -0
  170. package/src/sdk/router/session-router.ts +89 -38
  171. package/src/sdk/session.ts +155 -26
  172. package/src/sdk/transport/relay.ts +35 -13
  173. package/src/session/agent-session.ts +117 -52
  174. package/src/session/fallback-chain-controller.ts +38 -0
  175. package/src/session/internal/managed-session-storage.ts +19 -5
  176. package/src/session/messages.ts +5 -0
  177. package/src/session/session-manager.ts +29 -5
  178. package/src/session-import/redact.ts +5 -1
  179. package/src/setup/paseo/announce.ts +49 -43
  180. package/src/setup/paseo/setup-deps.ts +1 -1
  181. package/src/skill-state/canonical-skills.ts +12 -0
  182. package/src/skills/skills.test.ts +41 -21
  183. package/src/system-prompt.ts +25 -3
  184. package/src/task/executor.ts +5 -0
  185. package/src/task/index.ts +4 -0
  186. package/src/tools/bash.ts +28 -11
  187. package/src/tools/gh.ts +26 -3
  188. package/src/tools/github-cache.ts +68 -16
  189. package/src/tools/index.ts +2 -0
  190. package/src/tools/job.ts +13 -2
  191. package/src/tools/read.ts +14 -2
  192. package/src/tools/render-utils.ts +1 -3
  193. package/src/tools/resource-gc.ts +28 -8
  194. package/src/tools/skill-discovery.ts +5 -0
  195. package/src/tools/skill.ts +15 -0
  196. package/src/tools/subagent.ts +3 -2
  197. package/src/tools/tool-catalog.generated.ts +2 -2
  198. package/src/tools/ultragoal-ask-guard.ts +2 -2
@@ -62,6 +62,8 @@ export interface CreateAgentSessionOptions {
62
62
  cwd?: string;
63
63
  /** Global config directory. Default: ~/.gjc/agent */
64
64
  agentDir?: string;
65
+ /** Resolver-owned classification for `agentDir`; required when authority must survive HOME/config refreshes. */
66
+ profileAuthority?: "default" | "custom";
65
67
  /** Spawns to allow. Default: "*" */
66
68
  spawns?: string;
67
69
  /** Auth storage for credentials. Default: discoverAuthStorage(agentDir) */
@@ -336,7 +338,7 @@ export declare function discoverSkills(_cwd?: string, _agentDir?: string, _setti
336
338
  * Discover context files (AGENTS.md) walking up from cwd.
337
339
  * Returns files sorted by depth (farther from cwd first, so closer files appear last/more prominent).
338
340
  */
339
- export declare function discoverContextFiles(cwd?: string, _agentDir?: string): Promise<Array<{
341
+ export declare function discoverContextFiles(cwd?: string, agentDir?: string): Promise<Array<{
340
342
  path: string;
341
343
  content: string;
342
344
  depth?: number;
@@ -361,6 +363,8 @@ export interface BuildSystemPromptOptions {
361
363
  content: string;
362
364
  }>;
363
365
  cwd?: string;
366
+ agentDir?: string;
367
+ profileAuthority?: "default" | "custom";
364
368
  appendPrompt?: string;
365
369
  repeatToolDescriptions?: boolean;
366
370
  }
@@ -826,6 +826,8 @@ export declare class AgentSession {
826
826
  awaitSessionSettlement(): Promise<void>;
827
827
  /** Test seam: hold prompt admission while bypassing a pending selection fence. */
828
828
  runWithPromptAdmissionForTests(body: () => Promise<void>): Promise<void>;
829
+ /** Test seam: await all currently admitted coordinator sidecar writes. */
830
+ awaitCoordinatorRuntimeStatePersistenceForTests(): Promise<void>;
829
831
  queueCoordinatorRuntimeStatePersistForTests(event: AgentSessionEvent, gate: Promise<void>): Promise<void>;
830
832
  parkAgentEndForCoordinatorPersistForTests(event: Extract<AgentSessionEvent, {
831
833
  type: "agent_end";
@@ -95,5 +95,20 @@ export declare function cappedExponentialWithFullJitter(baseDelayMs: number, max
95
95
  * A missing, NaN, or infinite hint collapses to "no usable hint".
96
96
  */
97
97
  export declare function compactionRetryDelay(baseDelayMs: number, maxDelayMs: number, attempt: number, retryAfterMs: number | undefined): number;
98
+ export interface CompactionCandidateFailure {
99
+ readonly model: string;
100
+ readonly message: string;
101
+ }
102
+ /**
103
+ * Error for an auto-compaction run whose every candidate failed.
104
+ *
105
+ * The candidate chain starts at the session model and ends on a same-provider
106
+ * largest-context fallback, so surfacing only the final error named a model
107
+ * the user never chose and hid that their own model had already failed the
108
+ * same way. The message leads with the first failure (the one the user can act
109
+ * on), then lists every candidate that was tried with its own error. `cause`
110
+ * is the final error so callers that classify by type keep the same object.
111
+ */
112
+ export declare function describeCompactionCandidateFailures(failures: readonly CompactionCandidateFailure[], lastError: unknown): Error;
98
113
  /** Retry-After is intentionally uncapped. */
99
114
  export declare function effectiveFallbackDelay(baseDelayMs: number, maxDelayMs: number, attemptK: number, retryAfterMs: number | undefined, random?: () => number): number;
@@ -6,6 +6,13 @@ export declare const MANAGED_ARTIFACT_MAX_FILES = 50000;
6
6
  export declare const MANAGED_ARTIFACT_MAX_FILE_BYTES: number;
7
7
  export declare const MANAGED_ARTIFACT_MAX_TOTAL_BYTES: number;
8
8
  export declare const MANAGED_ARTIFACT_COPY_BATCH_SIZE = 256;
9
+ /** A pre-mutation append fence rejection: no bytes from this append were written. */
10
+ export declare class ManagedAppendIdentityMismatchError extends Error {
11
+ readonly code = "managed_append_identity_mismatch";
12
+ readonly relativePath: string;
13
+ readonly operatorMessage = "Session was resumed by another process; this resume did not run. Retry, or resume a different session.";
14
+ constructor(relativePath: string);
15
+ }
9
16
  export declare class ManagedPublishError extends Error {
10
17
  readonly classification: "destination_conflict" | "atomic_unavailable" | "invalid_request" | "durability_not_provable" | "identity_mismatch" | "io_error" | "durability_failed";
11
18
  readonly diagnostic: string;
@@ -82,6 +82,7 @@ export interface BashExecutionMessage {
82
82
  command: string;
83
83
  output: string;
84
84
  exitCode: number | undefined;
85
+ signal?: string;
85
86
  cancelled: boolean;
86
87
  truncated: boolean;
87
88
  meta?: OutputMeta;
@@ -9,7 +9,7 @@ import type { ManagedLegacyLocalMigrationSource } from "../internal-urls/local-p
9
9
  import { ArtifactManager } from "./artifacts";
10
10
  import { type BlobPutResult, BlobStore } from "./blob-store";
11
11
  import { type ManagedMigrationPolicy } from "./internal/managed-session-scope";
12
- import { type ManagedBoundedAppendExpectation, type ManagedDirectoryRoot, type ManagedFileIdentity, ManagedSessionDescendantStore } from "./internal/managed-session-storage";
12
+ import { ManagedAppendIdentityMismatchError, type ManagedBoundedAppendExpectation, type ManagedDirectoryRoot, type ManagedFileIdentity, ManagedSessionDescendantStore } from "./internal/managed-session-storage";
13
13
  import { type BaseAnchor, BoundedLabelsPinsStore, type CommittedTail, type DescriptorSnapshot, type DictionaryPartitionCommit, FixedCacheAccount, type MetadataDeltaValue, type ParentBucketCommit, type ReducerState, type ReopenClassification, SessionMemoryAccountant } from "./internal/session-memory-sidecar";
14
14
  import { type MemoryGuardCreateCheckpointInput, type MemoryGuardParticipantIngressLease, type MemoryGuardRestoreInput, type MemoryGuardRestoreResult, type MemoryGuardSessionManagerCheckpointV1 } from "./memory-guard-checkpoint-participant";
15
15
  import { type BashExecutionMessage, type CustomMessage, type FileMentionMessage, type HookMessage, type PythonExecutionMessage } from "./messages";
@@ -1291,6 +1291,8 @@ export declare class SessionManager {
1291
1291
  }) => Promise<fs.BigIntStats>;
1292
1292
  };
1293
1293
  }): Promise<void>;
1294
+ /** Ownership loss remains observable even when an agent event listener aborts the turn. */
1295
+ getAppendIdentityMismatch(): ManagedAppendIdentityMismatchError | undefined;
1294
1296
  isPersisted(): boolean;
1295
1297
  stageDefaultModelSelection(model: string, thinkingLevel: string | undefined, options?: {
1296
1298
  readonly appendThinkingLevel: boolean;
@@ -1,4 +1,3 @@
1
- export { resolvePaseoHome } from "./setup-deps";
2
1
  export type PaseoDaemonTarget = {
3
2
  readonly kind: "ipc";
4
3
  readonly socketPath: string;
@@ -40,6 +39,8 @@ export interface PaseoAnnounceDependencies {
40
39
  readonly providerKey: string;
41
40
  readonly cwd: string;
42
41
  readonly sessionId: string;
42
+ readonly daemonTarget: PaseoDaemonTarget;
43
+ readonly daemonHost: string;
43
44
  readonly signal?: AbortSignal;
44
45
  }): Promise<PaseoAnnounceOutcome>;
45
46
  }
@@ -64,7 +65,7 @@ export declare function parseDaemonListen(raw: string): PaseoDaemonTarget | unde
64
65
  * environment override, then the running daemon's own pid file, then the
65
66
  * configured listener, then Paseo's default.
66
67
  */
67
- export declare function resolveDaemonTarget(config: unknown, deps: Pick<PaseoAnnounceDependencies, "env" | "paseoHome" | "readJson">): Promise<PaseoDaemonTarget>;
68
+ export declare function resolveDaemonTarget(config: unknown, deps: Pick<PaseoAnnounceDependencies, "env" | "paseoHome" | "readJson">): Promise<PaseoDaemonTarget | undefined>;
68
69
  /**
69
70
  * Turn a non-zero `paseo import` into an outcome.
70
71
  *
@@ -107,5 +108,5 @@ export declare function startPaseoAnnouncement(input: {
107
108
  isEnabled?: () => boolean;
108
109
  onOutcome?: (outcome: PaseoAnnounceOutcome) => void;
109
110
  }, deps: PaseoAnnounceDependencies): PaseoAnnouncementHandle;
110
- export declare function sessionListContainsLiveSessionForTests(sessions: readonly unknown[], sessionId: string, cwd: string): boolean;
111
+ export declare function sessionListContainsLiveSession(sessions: readonly unknown[], sessionId: string, cwd: string): boolean;
111
112
  export declare function createDefaultPaseoAnnounceDependencies(agentDir: string, env?: NodeJS.ProcessEnv): PaseoAnnounceDependencies;
@@ -1,3 +1,7 @@
1
1
  /** Native-free canonical GJC workflow skill identifiers. */
2
2
  export declare const CANONICAL_GJC_WORKFLOW_SKILLS: readonly ["deep-interview", "ralplan", "ultragoal", "autoresearch"];
3
3
  export type CanonicalGjcWorkflowSkill = (typeof CANONICAL_GJC_WORKFLOW_SKILLS)[number];
4
+ /** Case-insensitive, Win32-path-equivalent key used for skill collision checks. */
5
+ export declare function getSkillFilesystemIdentity(name: string): string;
6
+ /** Whether a name resolves to a bundled workflow under portable filesystem semantics. */
7
+ export declare function isCanonicalGjcWorkflowSkillFilesystemAlias(name: string): boolean;
@@ -15,6 +15,10 @@ export declare function resolvePromptInput(input: string | undefined, descriptio
15
15
  export interface LoadContextFilesOptions {
16
16
  /** Working directory to start walking up from. Default: getProjectDir() */
17
17
  cwd?: string;
18
+ /** Agent directory backing native user-scope context and system prompt files. */
19
+ agentDir?: string;
20
+ /** Resolver-owned classification for `agentDir`; preserve it across HOME/config refreshes. */
21
+ profileAuthority?: "default" | "custom";
18
22
  }
19
23
  export interface ProjectContextFilesResult {
20
24
  contextFiles: Array<{
@@ -70,6 +74,10 @@ export interface BuildSystemPromptOptions {
70
74
  skillsSettings?: SkillsSettings;
71
75
  /** Working directory. Default: getProjectDir() */
72
76
  cwd?: string;
77
+ /** Agent directory backing native user-scope prompt discovery. */
78
+ agentDir?: string;
79
+ /** Resolver-owned classification for `agentDir`; preserve it across HOME/config refreshes. */
80
+ profileAuthority?: "default" | "custom";
73
81
  /** Pre-loaded context files (skips discovery if provided). */
74
82
  contextFiles?: Array<{
75
83
  path: string;
@@ -95,6 +95,9 @@ export interface ExecutorOptions {
95
95
  authStorage?: AuthStorage;
96
96
  modelRegistry?: ModelRegistry;
97
97
  settings?: Settings;
98
+ /** Parent session's selected agent directory and immutable resolver-owned authority. */
99
+ agentDir?: string;
100
+ profileAuthority?: "default" | "custom";
98
101
  /** Parent session's registry; shared by child sessions for IRC routing and roster visibility. */
99
102
  agentRegistry?: AgentRegistry;
100
103
  /**
@@ -19,6 +19,7 @@ export declare const STEER_FOLD_GRACE_MS = 2000;
19
19
  /** Model-facing line appended to a steer-folded background-start result. */
20
20
  export declare function steerFoldReasonLine(jobId: string): string;
21
21
  export type BashOriginalArtifactSaveResult = BashArtifactSaveResult;
22
+ export declare function suffixPrefixOverlap(source: string, target: string): number;
22
23
  export declare function saveBashOriginalArtifactForTests(session: ToolSession, originalText: string, onResult?: (result: BashOriginalArtifactSaveResult) => void): Promise<string | undefined>;
23
24
  declare const bashSchemaBase: z.ZodObject<{
24
25
  command: z.ZodString;
@@ -78,9 +78,10 @@ export interface CacheLookupOptions<T> {
78
78
  * must bypass persistent cache reads/writes.
79
79
  */
80
80
  authKey?: string | null;
81
- fetchFresh: () => Promise<FreshResult<T>>;
81
+ fetchFresh: (signal?: AbortSignal) => Promise<FreshResult<T>>;
82
82
  settings?: Settings | undefined;
83
83
  now?: number;
84
+ signal?: AbortSignal;
84
85
  }
85
86
  export type CacheStatus = "miss" | "fresh" | "stale" | "disabled";
86
87
  export interface CacheLookupResult<T> {
@@ -152,6 +152,8 @@ export interface ToolSession {
152
152
  * Runtime discovery only; never threaded into capability loading.
153
153
  */
154
154
  home?: string;
155
+ /** Resolver-owned classification for the session's selected agent directory. */
156
+ profileAuthority?: "default" | "custom";
155
157
  skills?: Skill[];
156
158
  /** Currently executing skill prompt, when this tool session is inside one. */
157
159
  getActiveSkillState?: () => Pick<SkillActiveEntry, "skill" | "session_id"> | undefined;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/coding-agent",
4
- "version": "0.16.4",
4
+ "version": "0.16.6",
5
5
  "description": "Gajae Code CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://gajae-code.com",
7
7
  "author": "Yeachan-Heo and Gajae Code Contributors",
@@ -56,12 +56,12 @@
56
56
  "@agentclientprotocol/sdk": "1.3.0",
57
57
  "@babel/parser": "^7.29.3",
58
58
  "@mozilla/readability": "^0.6.0",
59
- "@gajae-code/stats": "0.16.4",
60
- "@gajae-code/agent-core": "0.16.4",
61
- "@gajae-code/ai": "0.16.4",
62
- "@gajae-code/natives": "0.16.4",
63
- "@gajae-code/tui": "0.16.4",
64
- "@gajae-code/utils": "0.16.4",
59
+ "@gajae-code/stats": "0.16.6",
60
+ "@gajae-code/agent-core": "0.16.6",
61
+ "@gajae-code/ai": "0.16.6",
62
+ "@gajae-code/natives": "0.16.6",
63
+ "@gajae-code/tui": "0.16.6",
64
+ "@gajae-code/utils": "0.16.6",
65
65
  "@puppeteer/browsers": "^2.13.0",
66
66
  "@types/turndown": "5.0.6",
67
67
  "@xterm/headless": "^6.0.0",
@@ -130,6 +130,8 @@
130
130
  "./session/session-manager-internal.js": null,
131
131
  "./session/sdk-run-capability": null,
132
132
  "./session/sdk-run-capability.js": null,
133
+ "./extensibility/extensions/function-hooks-internal": null,
134
+ "./extensibility/extensions/function-hooks-internal.js": null,
133
135
  "./sdk": {
134
136
  "types": "./dist/types/sdk/index.d.ts",
135
137
  "import": "./src/sdk/index.ts"
@@ -115,6 +115,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
115
115
  "agent_session:getPendingNextTurnMessagesForTests": "read-only test seam, not a user-facing SDK control seam",
116
116
  "agent_session:queueCoordinatorRuntimeStatePersistForTests":
117
117
  "test-only coordinator persistence ordering seam, not a user-facing SDK control seam",
118
+ "agent_session:awaitCoordinatorRuntimeStatePersistenceForTests":
119
+ "test-only coordinator persistence drain barrier, not a user-facing SDK control seam",
118
120
  "agent_session:parkAgentEndForCoordinatorPersistForTests":
119
121
  "test-only parked terminal persistence seam, not a user-facing SDK control seam",
120
122
  "agent_session:flushParkedAgentEndForCoordinatorPersistForTests":
@@ -7,7 +7,14 @@
7
7
  * - Loading items for a capability across all providers
8
8
  */
9
9
  import * as path from "node:path";
10
- import { getAgentDir, getProjectDir, getTrustedHomeDir, logger } from "@gajae-code/utils";
10
+ import {
11
+ getAgentDir,
12
+ getAgentProfileAuthority,
13
+ getProjectDir,
14
+ getTrustedHomeDir,
15
+ logger,
16
+ normalizePathForComparison,
17
+ } from "@gajae-code/utils";
11
18
 
12
19
  import type { Settings } from "../config/settings";
13
20
  import { clearCache as clearFsCache, findRepoRoot, cacheStats as fsCacheStats, invalidate as invalidateFs } from "./fs";
@@ -243,8 +250,13 @@ export async function loadCapability<T>(capabilityId: string, options: LoadOptio
243
250
  const cwd = options.cwd ?? getProjectDir();
244
251
  const home = getTrustedHomeDir();
245
252
  const userAgentDir = options.agentDir ? path.resolve(options.agentDir) : getAgentDir();
253
+ const profileAuthority =
254
+ options.profileAuthority ??
255
+ (options.agentDir && normalizePathForComparison(userAgentDir) !== normalizePathForComparison(getAgentDir())
256
+ ? "custom"
257
+ : getAgentProfileAuthority());
246
258
  const repoRoot = await findRepoRoot(cwd);
247
- const ctx: LoadContext = { cwd, home, userAgentDir, repoRoot, settings: options.settings };
259
+ const ctx: LoadContext = { cwd, home, userAgentDir, profileAuthority, repoRoot, settings: options.settings };
248
260
  const providers = filterProviders(capability, options);
249
261
 
250
262
  return await loadImpl(capability, providers, ctx, options);
@@ -29,6 +29,8 @@ export interface LoadContext {
29
29
  * produced. This includes MCP registrations and user-installed skills.
30
30
  */
31
31
  userAgentDir?: string;
32
+ /** Resolver-owned classification for the selected user agent directory. */
33
+ profileAuthority?: "default" | "custom";
32
34
  /** Git repository root (directory containing .git), or null if not in a repo */
33
35
  repoRoot: string | null;
34
36
  /** Owning session settings for provider policy decisions. */
@@ -89,6 +91,8 @@ export interface LoadOptions {
89
91
  * process-wide one (`createAgentSession({ agentDir })`).
90
92
  */
91
93
  agentDir?: string;
94
+ /** Resolver-owned classification for `agentDir`; preserve it across HOME/config refreshes. */
95
+ profileAuthority?: "default" | "custom";
92
96
  /** Include items even if they fail validation. Default: false */
93
97
  includeInvalid?: boolean;
94
98
  /** Include items disabled via settings. Default: false */
@@ -38,6 +38,8 @@ import { initializeWithSettings, loadCapability } from "../discovery";
38
38
  import { inspectClaudeConvention } from "../discovery/claude";
39
39
  import { inspectCodexConvention } from "../discovery/codex";
40
40
  import { scanSkillsFromDir } from "../discovery/helpers";
41
+ import { functionHookGrantHash, normalizeFunctionHookGrant } from "../extensibility/extensions/function-hooks";
42
+ import { compileGjcPluginBundle } from "../extensibility/gjc-plugins/compiler";
41
43
  import { summarizeGjcPluginObservability } from "../extensibility/gjc-plugins/observability";
42
44
  import { loadEffectiveGjcPluginRegistry } from "../extensibility/gjc-plugins/registry";
43
45
  import { getEnabledPlugins } from "../extensibility/plugins/loader";
@@ -47,7 +49,10 @@ import { loadAllMCPConfigs } from "../runtime-mcp/config";
47
49
  import { readDisabledServers } from "../runtime-mcp/config-writer";
48
50
  import { canonicalizeMCPEndpoint } from "../runtime-mcp/pool-key";
49
51
  import { redactMCPEndpoint } from "../runtime-mcp/redaction";
50
- import { CANONICAL_GJC_WORKFLOW_SKILLS } from "../skill-state/canonical-skills";
52
+ import {
53
+ getSkillFilesystemIdentity,
54
+ isCanonicalGjcWorkflowSkillFilesystemAlias,
55
+ } from "../skill-state/canonical-skills";
51
56
  import { expandTilde } from "../tools/path-utils";
52
57
 
53
58
  // =============================================================================
@@ -153,6 +158,29 @@ export interface CustomizeDoctorItem {
153
158
  mcp?: McpSafeSummary;
154
159
  /** Present only for quarantined plugin-bundle surfaces. */
155
160
  quarantineCode?: string;
161
+ /** Metadata-only Function Hook policy projection; hook code is never executed. */
162
+ functionHooks?: CustomizeFunctionHookSummary[];
163
+ }
164
+
165
+ export interface CustomizeFunctionHookSummary {
166
+ extensionId: string;
167
+ order: number;
168
+ event: string;
169
+ target?: string;
170
+ phase?: "before" | "after";
171
+ scope: "user" | "project";
172
+ plugin: string;
173
+ relativePath: string;
174
+ status: "disabled" | "quarantined" | "blocked-isolate" | "legacy-active";
175
+ requestedCapabilities: string[];
176
+ effectiveCapabilities: string[];
177
+ attenuateDownstream: string[];
178
+ networkDestinations: string[];
179
+ filesystemRoots: string[];
180
+ capabilityHash?: string;
181
+ implementationHash?: string;
182
+ quarantineCode?: string;
183
+ remediation: string;
156
184
  }
157
185
 
158
186
  export interface CustomizeDoctorSurface {
@@ -200,8 +228,6 @@ export interface CustomizeDoctorCommandOptions {
200
228
 
201
229
  const REDACTED = "<redacted>";
202
230
 
203
- const BUILT_IN_SKILL_NAMES = new Set<string>(CANONICAL_GJC_WORKFLOW_SKILLS);
204
-
205
231
  const SENSITIVE_KEY_PATTERN =
206
232
  /(?:token|secret|key|credential|password|passwd|pwd|authorization|auth|bearer|cookie|session|apikey)/i;
207
233
 
@@ -655,7 +681,7 @@ async function collectSkills(cwd: string, activeSettings: SettingsInstance): Pro
655
681
  cwd,
656
682
  disabledExtensions: activeSettings.get("disabledExtensions"),
657
683
  });
658
- for (const skill of result.skills) loadedNames.add(skill.name);
684
+ for (const skill of result.skills) loadedNames.add(getSkillFilesystemIdentity(skill.name));
659
685
  }
660
686
 
661
687
  const skillScopeNotes: string[] = [];
@@ -730,7 +756,7 @@ async function collectSkills(cwd: string, activeSettings: SettingsInstance): Pro
730
756
  [`Move the skill under .gjc/skills/<name>/SKILL.md to make it loadable`],
731
757
  );
732
758
  }
733
- if (BUILT_IN_SKILL_NAMES.has(entry.name)) {
759
+ if (isCanonicalGjcWorkflowSkillFilesystemAlias(entry.name)) {
734
760
  return finalizeItem(
735
761
  base,
736
762
  "shadowed",
@@ -740,7 +766,7 @@ async function collectSkills(cwd: string, activeSettings: SettingsInstance): Pro
740
766
  { precedence: { priority: entry.priority, shadowedBy: { provider: "bundled", scope: "native" } } },
741
767
  );
742
768
  }
743
- if (loadedNames.has(entry.name)) {
769
+ if (loadedNames.has(getSkillFilesystemIdentity(entry.name))) {
744
770
  return finalizeItem(base, "loaded", "loaded", "Loaded by session startup.", []);
745
771
  }
746
772
  if (ignoredPatterns.length > 0 || includePatterns.length > 0) {
@@ -809,7 +835,7 @@ async function collectSkills(cwd: string, activeSettings: SettingsInstance): Pro
809
835
  );
810
836
  continue;
811
837
  }
812
- if (BUILT_IN_SKILL_NAMES.has(skill.name)) {
838
+ if (isCanonicalGjcWorkflowSkillFilesystemAlias(skill.name)) {
813
839
  items.push(
814
840
  finalizeItem(
815
841
  base,
@@ -828,7 +854,10 @@ async function collectSkills(cwd: string, activeSettings: SettingsInstance): Pro
828
854
  // in loadedNames even though it loses the name collision to an
829
855
  // earlier-priority source. The collision must report shadowed-by-
830
856
  // precedence, not a second "loaded" item.
831
- const collisionWinner = items.find(i => i.name === skill.name && i.status === "loaded");
857
+ const skillIdentity = getSkillFilesystemIdentity(skill.name);
858
+ const collisionWinner = items.find(
859
+ i => getSkillFilesystemIdentity(i.name) === skillIdentity && i.status === "loaded",
860
+ );
832
861
  if (collisionWinner) {
833
862
  items.push(
834
863
  finalizeItem(
@@ -847,7 +876,7 @@ async function collectSkills(cwd: string, activeSettings: SettingsInstance): Pro
847
876
  );
848
877
  continue;
849
878
  }
850
- if (loadedNames.has(skill.name)) {
879
+ if (loadedNames.has(skillIdentity)) {
851
880
  items.push(
852
881
  finalizeItem(base, "loaded", "loaded", "Loaded by session startup from skills.customDirectories.", []),
853
882
  );
@@ -910,8 +939,11 @@ async function collectHooks(cwd: string, activeSettings: SettingsInstance): Prom
910
939
  nameOf: hook => hook.name,
911
940
  pathOf: hook => hook.path,
912
941
  notExecutedDetail:
913
- "Discovered and shown in the extension dashboard, but standalone sessions do not execute hook files. Runtime hooks come from validated GJC plugin bundles.",
914
- notExecutedRemediation: ["See `gjc plugin list` for plugin bundles that contribute runtime hooks"],
942
+ "Trusted filesystem hook module discovered for session-start activation. Doctor does not execute the module, so dynamic Function Hook registrations and effective runtime order remain opaque until a session loads it.",
943
+ notExecutedRemediation: [
944
+ "Review the trusted hook source before session startup",
945
+ "Use plugin-bundle Function Hooks for declarative grants, hashes, quarantine, and metadata-only policy inspection",
946
+ ],
915
947
  });
916
948
  }
917
949
 
@@ -1140,7 +1172,101 @@ async function collectPluginBundles(cwd: string): Promise<CustomizeDoctorSurface
1140
1172
  const bundleEntries = await loadEffectiveGjcPluginRegistry(cwd, { migrate: false });
1141
1173
  const observability = await summarizeGjcPluginObservability(cwd, { migrate: false });
1142
1174
  const items: CustomizeDoctorItem[] = [...npmItems];
1175
+ let globalFunctionHookOrder = 0;
1143
1176
  for (const entry of bundleEntries) {
1177
+ const surfaces = observability.surfaces.filter(s => s.plugin === entry.name && s.scope === entry.scope);
1178
+ let compiledHooks = new Map<
1179
+ string,
1180
+ Awaited<ReturnType<typeof compileGjcPluginBundle>>["surfaces"]["hooks"][number]
1181
+ >();
1182
+ try {
1183
+ const compiled = await compileGjcPluginBundle(entry.pluginRoot);
1184
+ compiledHooks = new Map(compiled.surfaces.hooks.map(hook => [hook.extensionId, hook]));
1185
+ } catch {
1186
+ // Existing observability reports unreadable/hash-drift bundles. Keep
1187
+ // declarative rows visible and mark unmatched metadata quarantined.
1188
+ }
1189
+ const functionHooks: CustomizeFunctionHookSummary[] = entry.surfaces.hooks.map(hook => {
1190
+ const observed = surfaces.find(surface => surface.extensionId === hook.extensionId);
1191
+ const compiled = compiledHooks.get(hook.extensionId);
1192
+ const hasGrantMetadata =
1193
+ hook.capabilities !== undefined ||
1194
+ hook.networkDestinations !== undefined ||
1195
+ hook.filesystemRoots !== undefined ||
1196
+ hook.capabilityHash !== undefined;
1197
+ let metadataValid =
1198
+ compiled !== undefined &&
1199
+ ((hook.functionHook === true && hasGrantMetadata) ||
1200
+ (hook.functionHook !== true && !hasGrantMetadata && compiled.functionHook !== true));
1201
+ try {
1202
+ const grant = normalizeFunctionHookGrant({
1203
+ capabilities: hook.capabilities,
1204
+ networkDestinations: hook.networkDestinations,
1205
+ filesystemRoots: hook.filesystemRoots,
1206
+ });
1207
+ metadataValid =
1208
+ metadataValid &&
1209
+ hook.functionHook === compiled?.functionHook &&
1210
+ hook.event === compiled?.event &&
1211
+ hook.target === compiled?.target &&
1212
+ hook.phase === compiled?.phase &&
1213
+ hook.relativePath === compiled?.relativePath &&
1214
+ hook.implementationHash === compiled?.implementationHash &&
1215
+ (hook.functionHook !== true ||
1216
+ (hook.capabilityHash?.toLowerCase() === functionHookGrantHash(grant).toLowerCase() &&
1217
+ hook.capabilityHash?.toLowerCase() === compiled?.capabilityHash?.toLowerCase() &&
1218
+ JSON.stringify(grant.capabilities) === JSON.stringify(compiled?.capabilities ?? []) &&
1219
+ JSON.stringify(grant.networkDestinations) ===
1220
+ JSON.stringify(compiled?.networkDestinations ?? []) &&
1221
+ JSON.stringify(grant.filesystemRoots) === JSON.stringify(compiled?.filesystemRoots ?? [])));
1222
+ } catch {
1223
+ metadataValid = false;
1224
+ }
1225
+ const status: CustomizeFunctionHookSummary["status"] =
1226
+ !metadataValid || observed?.status === "quarantined"
1227
+ ? "quarantined"
1228
+ : !entry.enabled || entry.disabledSurfaceIds.includes(hook.extensionId)
1229
+ ? "disabled"
1230
+ : hook.functionHook === true
1231
+ ? "blocked-isolate"
1232
+ : "legacy-active";
1233
+ return {
1234
+ extensionId: hook.extensionId,
1235
+ order: globalFunctionHookOrder++,
1236
+ event: hook.event,
1237
+ ...(hook.target === undefined ? {} : { target: hook.target }),
1238
+ ...(hook.phase === undefined ? {} : { phase: hook.phase }),
1239
+ scope: entry.scope,
1240
+ plugin: entry.name,
1241
+ relativePath: hook.relativePath,
1242
+ status,
1243
+ requestedCapabilities: [...(hook.capabilities ?? [])],
1244
+ effectiveCapabilities: status === "legacy-active" ? [...(hook.capabilities ?? [])] : [],
1245
+ attenuateDownstream: [],
1246
+ networkDestinations: (hook.networkDestinations ?? []).map(destination => {
1247
+ try {
1248
+ const url = new URL(destination);
1249
+ return url.protocol === "https:" && !url.username && !url.password ? url.origin : "[redacted]";
1250
+ } catch {
1251
+ return "[redacted]";
1252
+ }
1253
+ }),
1254
+ filesystemRoots: [...(hook.filesystemRoots ?? [])],
1255
+ ...(hook.capabilityHash === undefined ? {} : { capabilityHash: hook.capabilityHash }),
1256
+ ...(hook.implementationHash === undefined ? {} : { implementationHash: hook.implementationHash }),
1257
+ ...(observed?.quarantineCode === undefined && metadataValid
1258
+ ? {}
1259
+ : { quarantineCode: observed?.quarantineCode ?? "security_policy" }),
1260
+ remediation:
1261
+ status === "blocked-isolate"
1262
+ ? "Function Hook remains disabled until a capability-enforcing plugin isolate is available."
1263
+ : status === "quarantined"
1264
+ ? "Reinstall the bundle and verify installed metadata and file hashes."
1265
+ : status === "disabled"
1266
+ ? `Enable surface ${hook.extensionId} only after reviewing its declared policy.`
1267
+ : "Legacy constrained hook executes under the existing trusted compatibility policy.",
1268
+ };
1269
+ });
1144
1270
  const base: Omit<CustomizeDoctorItem, "status" | "reason" | "detail" | "remediation"> = {
1145
1271
  name: entry.name,
1146
1272
  kind: "plugin-bundle",
@@ -1153,6 +1279,7 @@ async function collectPluginBundles(cwd: string): Promise<CustomizeDoctorSurface
1153
1279
  trust: TRUST_BY_KIND["plugin-bundle"],
1154
1280
  restartRequired: true,
1155
1281
  precedence: { priority: 0 },
1282
+ functionHooks,
1156
1283
  };
1157
1284
  if (!entry.enabled) {
1158
1285
  items.push(
@@ -1166,7 +1293,6 @@ async function collectPluginBundles(cwd: string): Promise<CustomizeDoctorSurface
1166
1293
  );
1167
1294
  continue;
1168
1295
  }
1169
- const surfaces = observability.surfaces.filter(s => s.plugin === entry.name && s.scope === entry.scope);
1170
1296
  const quarantined = surfaces.filter(s => s.status === "quarantined");
1171
1297
  if (quarantined.length > 0) {
1172
1298
  const codes = Array.from(new Set(quarantined.map(s => s.quarantineCode ?? "unknown"))).join(", ");
@@ -1574,6 +1700,23 @@ export function renderCustomizeDoctorText(report: CustomizeDoctorReport): string
1574
1700
  lines.push(` reason: ${item.reason} — ${item.detail}`);
1575
1701
  lines.push(` source: ${item.path}`);
1576
1702
  if (item.quarantineCode) lines.push(` quarantine code: ${item.quarantineCode}`);
1703
+ for (const hook of item.functionHooks ?? []) {
1704
+ lines.push(
1705
+ ` function hook[${hook.order}]: ${hook.extensionId} event=${hook.event}${hook.target ? ` target=${hook.target}` : ""} status=${hook.status}`,
1706
+ );
1707
+ lines.push(` provenance: scope=${hook.scope} plugin=${hook.plugin} path=${hook.relativePath}`);
1708
+ lines.push(
1709
+ ` grants: requested=${hook.requestedCapabilities.join(",") || "none"} effective=${hook.effectiveCapabilities.join(",") || "none"} attenuate=${hook.attenuateDownstream.join(",") || "none"}`,
1710
+ );
1711
+ if (hook.networkDestinations.length > 0)
1712
+ lines.push(` network destinations: ${hook.networkDestinations.join(",")}`);
1713
+ if (hook.filesystemRoots.length > 0)
1714
+ lines.push(` filesystem roots: ${hook.filesystemRoots.join(",")}`);
1715
+ if (hook.capabilityHash) lines.push(` capability hash: ${hook.capabilityHash}`);
1716
+ if (hook.implementationHash) lines.push(` implementation hash: ${hook.implementationHash}`);
1717
+ if (hook.quarantineCode) lines.push(` quarantine code: ${hook.quarantineCode}`);
1718
+ lines.push(` remediation: ${hook.remediation}`);
1719
+ }
1577
1720
  for (const fix of item.remediation) lines.push(` fix: ${fix}`);
1578
1721
  lines.push(` trust: ${item.trust}`);
1579
1722
  lines.push(` restart required: ${item.restartRequired ? "yes (new session)" : "no"}`);
package/src/cli.ts CHANGED
@@ -10,6 +10,15 @@ import { APP_NAME, formatBunRuntimeError, MIN_BUN_VERSION, VERSION } from "@gaja
10
10
  import { runFixtureReport } from "./cli/fixture-report";
11
11
  import { ROOT_LAUNCH_FLAGS } from "./cli/root-flags";
12
12
  import QuickLane from "./commands/quick-lane";
13
+ import { runBashShellGuardian } from "./exec/bash-shell-guardian";
14
+ import { runBashShellSupervisor } from "./exec/bash-shell-supervisor";
15
+ import { runBashShellWorker } from "./exec/bash-shell-worker";
16
+ import {
17
+ BASH_SHELL_RUNTIME_ARG,
18
+ BASH_SHELL_SUPERVISOR_ARG,
19
+ BASH_SHELL_WORKER_ARG,
20
+ } from "./exec/bash-shell-worker-protocol";
21
+ import { smokeTestIsolatedShell } from "./exec/isolated-shell";
13
22
  import { smokeTestTabWorker } from "./tools/browser/tab-worker-smoke";
14
23
 
15
24
  if (Bun.semver.order(Bun.version, MIN_BUN_VERSION) < 0) {
@@ -340,6 +349,7 @@ async function runSmokeTest(): Promise<void> {
340
349
  const { runNativeSmokeTest } = await import("./cli/native-smoke");
341
350
  await runNativeSmokeTest();
342
351
  await smokeTestTabWorker();
352
+ await smokeTestIsolatedShell();
343
353
  process.stdout.write("smoke-test: ok\n");
344
354
  }
345
355
 
@@ -392,6 +402,18 @@ export function routeRootArgv(argv: readonly string[]): string[] {
392
402
 
393
403
  /** Run the CLI with the given argv (no `process.argv` prefix). */
394
404
  export async function runCli(argv: string[]): Promise<void> {
405
+ if (argv.length === 1 && argv[0] === BASH_SHELL_WORKER_ARG) {
406
+ await runBashShellGuardian();
407
+ return;
408
+ }
409
+ if (argv.length === 1 && argv[0] === BASH_SHELL_SUPERVISOR_ARG) {
410
+ await runBashShellSupervisor();
411
+ return;
412
+ }
413
+ if (argv.length === 1 && argv[0] === BASH_SHELL_RUNTIME_ARG) {
414
+ await runBashShellWorker();
415
+ return;
416
+ }
395
417
  // macOS malloc-env launch boundary. Re-exec once with a scrubbed environment
396
418
  // BEFORE any fast path or subprocess spawn, so the startup env snapshot Bun hands
397
419
  // to every child lane (Bun.spawn defaults, node:child_process, native PTY, tmux