@gajae-code/coding-agent 0.15.2 → 0.15.4

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 (107) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/dist/types/config/autorouting-tier-map.d.ts +0 -9
  3. package/dist/types/config/file-lock.d.ts +7 -1
  4. package/dist/types/config/keybindings.d.ts +13 -3
  5. package/dist/types/config/model-registry.d.ts +5 -0
  6. package/dist/types/config/model-resolver.d.ts +1 -0
  7. package/dist/types/config/settings-schema.d.ts +65 -1
  8. package/dist/types/coordinator/contract.d.ts +1 -1
  9. package/dist/types/coordinator-mcp/question-state.d.ts +42 -2
  10. package/dist/types/extensibility/extensions/types.d.ts +2 -0
  11. package/dist/types/gjc-runtime/deep-interview-state.d.ts +12 -0
  12. package/dist/types/gjc-runtime/workflow-placeholder.d.ts +6 -0
  13. package/dist/types/main.d.ts +1 -1
  14. package/dist/types/modes/action-registry.d.ts +1 -0
  15. package/dist/types/modes/components/hook-selector.d.ts +1 -0
  16. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +1 -1
  17. package/dist/types/modes/components/welcome.d.ts +2 -0
  18. package/dist/types/modes/controllers/input-controller.d.ts +16 -1
  19. package/dist/types/modes/controllers/runtime-mcp-command-controller.d.ts +42 -0
  20. package/dist/types/modes/interactive-mode.d.ts +7 -0
  21. package/dist/types/modes/shared/oauth-url-copy.d.ts +9 -0
  22. package/dist/types/modes/types.d.ts +3 -0
  23. package/dist/types/modes/ui-language.d.ts +2 -1
  24. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +1 -0
  25. package/dist/types/sdk/broker/session-index.d.ts +18 -0
  26. package/dist/types/sdk/bus/index.d.ts +4 -1
  27. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -1
  28. package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
  29. package/dist/types/sdk/cli/session-cli.d.ts +1 -1
  30. package/dist/types/sdk/lifecycle/service.d.ts +32 -5
  31. package/dist/types/sdk/session.d.ts +5 -0
  32. package/dist/types/session/agent-session.d.ts +4 -0
  33. package/dist/types/session/fallback-chain-controller.d.ts +8 -0
  34. package/dist/types/slash-commands/helpers/aside.d.ts +54 -0
  35. package/dist/types/tools/ask.d.ts +9 -0
  36. package/dist/types/utils/clipboard.d.ts +9 -1
  37. package/package.json +7 -7
  38. package/scripts/generate-sdk-adapter-parity-manifest.ts +5 -2
  39. package/scripts/generate-sdk-operation-inventory.ts +3 -0
  40. package/src/commands/sdk.ts +2 -2
  41. package/src/config/autorouting-tier-map.ts +0 -9
  42. package/src/config/file-lock.ts +15 -1
  43. package/src/config/keybindings.ts +13 -3
  44. package/src/config/model-registry.ts +132 -21
  45. package/src/config/model-resolver.ts +2 -1
  46. package/src/config/models-config-schema.ts +2 -2
  47. package/src/config/settings-schema.ts +66 -1
  48. package/src/config/settings.ts +1 -1
  49. package/src/coordinator/contract.ts +1 -0
  50. package/src/coordinator-mcp/question-state.ts +235 -4
  51. package/src/coordinator-mcp/server.ts +402 -2
  52. package/src/dap/client.ts +19 -8
  53. package/src/deep-interview/render-middleware.ts +6 -2
  54. package/src/extensibility/extensions/types.ts +2 -0
  55. package/src/gjc-runtime/deep-interview-stage.ts +20 -1
  56. package/src/gjc-runtime/deep-interview-state.ts +3 -1
  57. package/src/gjc-runtime/ralplan-runtime.ts +6 -1
  58. package/src/gjc-runtime/session-state-lock.ts +11 -2
  59. package/src/gjc-runtime/ultragoal-runtime.ts +5 -0
  60. package/src/gjc-runtime/workflow-placeholder.ts +33 -0
  61. package/src/internal-urls/docs-index.generated.ts +11 -9
  62. package/src/lsp/client.ts +39 -4
  63. package/src/main.ts +42 -11
  64. package/src/modes/action-registry.ts +7 -0
  65. package/src/modes/components/hook-selector.ts +2 -1
  66. package/src/modes/components/iterm-pet-transport.ts +4 -2
  67. package/src/modes/components/runtime-mcp-add-wizard.ts +38 -1
  68. package/src/modes/components/settings-selector.ts +1 -0
  69. package/src/modes/components/tool-status-header.ts +149 -41
  70. package/src/modes/components/welcome.ts +8 -0
  71. package/src/modes/controllers/extension-ui-controller.ts +1 -0
  72. package/src/modes/controllers/goal-mode-controller.ts +7 -2
  73. package/src/modes/controllers/input-controller.ts +141 -7
  74. package/src/modes/controllers/plan-mode-controller.ts +12 -4
  75. package/src/modes/controllers/runtime-mcp-command-controller.ts +164 -20
  76. package/src/modes/controllers/selector-controller.ts +11 -2
  77. package/src/modes/interactive-mode.ts +88 -1
  78. package/src/modes/shared/oauth-url-copy.ts +33 -0
  79. package/src/modes/types.ts +3 -0
  80. package/src/modes/ui-language.ts +44 -3
  81. package/src/runtime-mcp/oauth-flow.ts +19 -8
  82. package/src/runtime-mcp/transports/stdio.ts +27 -13
  83. package/src/sdk/acp/adapter.ts +29 -2
  84. package/src/sdk/broker/broker.ts +26 -6
  85. package/src/sdk/broker/lifecycle-ledger.ts +28 -1
  86. package/src/sdk/broker/lifecycle.ts +664 -5
  87. package/src/sdk/broker/session-index.ts +246 -0
  88. package/src/sdk/broker/transport.ts +1 -0
  89. package/src/sdk/bus/index.ts +135 -38
  90. package/src/sdk/bus/natives-augment.d.ts +20 -1
  91. package/src/sdk/bus/telegram-daemon-contract.ts +4 -1
  92. package/src/sdk/bus/telegram-daemon.ts +3 -0
  93. package/src/sdk/cli/session-cli.ts +34 -7
  94. package/src/sdk/lifecycle/service.ts +166 -11
  95. package/src/sdk/mcp/server.ts +2 -1
  96. package/src/sdk/protocol/operation-inventory.generated.json +40 -0
  97. package/src/sdk/protocol/operation-registry.ts +2 -1
  98. package/src/sdk/session.ts +91 -7
  99. package/src/session/agent-session.ts +473 -66
  100. package/src/session/fallback-chain-controller.ts +17 -0
  101. package/src/setup/frictionless-onboarding.ts +4 -4
  102. package/src/slash-commands/aside-command.test.ts +366 -0
  103. package/src/slash-commands/builtin-registry.ts +19 -0
  104. package/src/slash-commands/helpers/aside.ts +299 -0
  105. package/src/tools/ask-contract.ts +54 -3
  106. package/src/tools/ask.ts +54 -6
  107. package/src/utils/clipboard.ts +37 -28
@@ -188,6 +188,24 @@ export declare class SessionIndex {
188
188
  * disturbing the surviving authority.
189
189
  */
190
190
  listSessionIdentities(probedIncarnations?: ReadonlyMap<string, string | undefined>): IndexedSession[];
191
+ /** Reconstructs a receipt-bound identity even when admission superseded it. */
192
+ findHistoricalSessionIdentity(expected: {
193
+ sessionId: string;
194
+ stateRoot: string;
195
+ endpointGeneration: number;
196
+ pid: number;
197
+ processIncarnation?: string;
198
+ hostIncarnation?: string;
199
+ endpointMtimeMs?: number;
200
+ lifecycleRequestId?: string;
201
+ }): IndexedSession | undefined;
202
+ /** Returns the latest identity-bound terminal event, if retained. */
203
+ findSessionTerminalEvidence(expected: Pick<IndexedSession, "sessionId" | "locator" | "endpointGeneration" | "pid" | "processIncarnation" | "hostIncarnation" | "endpointMtimeMs" | "lifecycleRequestId">): {
204
+ type: "host_unregistered" | "session_closed" | "session_deleted";
205
+ indexSeq: number;
206
+ } | undefined;
207
+ /** Returns the latest identity-bound session_closed evidence, even after a tombstone. */
208
+ findSessionClosedEvidence(expected: Pick<IndexedSession, "sessionId" | "locator" | "endpointGeneration" | "pid" | "processIncarnation" | "hostIncarnation" | "endpointMtimeMs" | "lifecycleRequestId">): number | undefined;
191
209
  /**
192
210
  * Production coalesced heartbeat checkpoint pass (C2): appends one
193
211
  * `host_heartbeat` per session at most once per {@link SESSION_HEARTBEAT_INTERVAL_MS}.
@@ -267,7 +267,10 @@ interface SessionRuntime {
267
267
  host: SessionSdkHost;
268
268
  /** Delivers one ring-positioned event envelope to every attached subscriber
269
269
  * connection, applying the same capability gate as event replay. */
270
- broadcastEventFrame: (event: SdkFrame) => void;
270
+ broadcastEventFrame: (event: SdkFrame) => string[];
271
+ /** Delivers one positioned event and returns opaque receipts only for the
272
+ * notification-effect generations that accepted it. */
273
+ broadcastEventFrameWithReceipts: (event: SdkFrame) => string[];
271
274
  /** Owns stateRoot-backed revisions and removes their spills on terminal shutdown. */
272
275
  revisions: RevisionStore;
273
276
  /** Releases all snapshot pins before the revision store is closed. */
@@ -275,8 +275,11 @@ export declare const SDK_LIFECYCLE_ROUTER_PROTOCOL_VERSION = 1;
275
275
  * still running the pre-#4689 router keeps the old hot polling loop and its
276
276
  * lock contention, so an already-running owner must be replaced rather than
277
277
  * retained across this upgrade.
278
+ * Generation 177 consumes an exact same-window lean settlement receipt only
279
+ * after its autonomous ask lead-in publishes successfully, preventing idle
280
+ * replay without changing the serving protocol.
278
281
  */
279
- export declare const DAEMON_GENERATION = 176;
282
+ export declare const DAEMON_GENERATION = 178;
280
283
  /**
281
284
  * Serving-compatibility boundary for daemon lifecycle requests. Epoch 7
282
285
  * requires durable publication receipts and acknowledged attachment dispatch,
@@ -130,6 +130,8 @@ export declare const ASK_CONTROLS_CAPABILITY = "ask_controls_v1";
130
130
  export declare const TOOL_ACTIVITY_CAPABILITY = "tool_activity_v2";
131
131
  /** Receive-only compatibility capability for pre-v2 hosts. */
132
132
  export declare const LEGACY_TOOL_ACTIVITY_CAPABILITY = "tool_activity_v1";
133
+ /** Opts notification adapters into positioned-only delivery for matching live effects. */
134
+ export declare const POSITIONED_NOTIFICATION_EFFECTS_CAPABILITY = "positioned_notification_effects_v1";
133
135
  /**
134
136
  * File-lock options whose acquisition budget covers the full reload-reservation
135
137
  * critical section: the in-lock freshness poll plus the controller's
@@ -1,5 +1,5 @@
1
1
  import { SessionRouter } from "../router";
2
- export type SdkSessionCliAction = "list" | "inspect" | "send" | "status" | "tail" | "raw" | "control" | "query" | "global";
2
+ export type SdkSessionCliAction = "list" | "inspect" | "send" | "status" | "tail" | "retire" | "raw" | "control" | "query" | "global";
3
3
  export type SdkSessionCliRawKind = "control" | "query" | "global";
4
4
  export interface SdkSessionCliArgs {
5
5
  action?: string;
@@ -1,4 +1,4 @@
1
- export type SessionLifecycleOperation = "session.create" | "session.fork" | "session.resume" | "session.close" | "session.delete" | "session.list";
1
+ export type SessionLifecycleOperation = "session.create" | "session.fork" | "session.resume" | "session.close" | "session.delete" | "session.reconcile_uncertain" | "session.list";
2
2
  export interface SessionLifecycleActor {
3
3
  readonly id: string;
4
4
  readonly namespace: string;
@@ -84,6 +84,17 @@ export interface SessionCloseTarget {
84
84
  readonly endpointGeneration?: number;
85
85
  readonly endpointIncarnation?: string;
86
86
  }
87
+ export interface SessionReconcileUncertainTarget {
88
+ readonly sessionId: string;
89
+ readonly cwd: string;
90
+ readonly stateRoot: string;
91
+ readonly endpointGeneration: number;
92
+ readonly endpointMtimeMs: number;
93
+ readonly processIncarnation: string;
94
+ readonly hostIncarnation: string;
95
+ readonly lifecycleRequestId: string;
96
+ readonly remoteCreateKey: string;
97
+ }
87
98
  export interface SessionDeleteTarget {
88
99
  readonly sessionId: string;
89
100
  readonly cwd?: string;
@@ -107,6 +118,7 @@ export type SessionForkRequest = SessionLifecycleMutationRequestBase<"session.fo
107
118
  export type SessionResumeRequest = SessionLifecycleMutationRequestBase<"session.resume", SessionResumeTarget>;
108
119
  export type SessionCloseRequest = SessionLifecycleMutationRequestBase<"session.close", SessionCloseTarget>;
109
120
  export type SessionDeleteRequest = SessionLifecycleMutationRequestBase<"session.delete", SessionDeleteTarget>;
121
+ export type SessionReconcileUncertainRequest = SessionLifecycleMutationRequestBase<"session.reconcile_uncertain", SessionReconcileUncertainTarget>;
110
122
  export interface SessionListRequest {
111
123
  readonly operation: "session.list";
112
124
  readonly actor: SessionLifecycleActor;
@@ -115,7 +127,7 @@ export interface SessionListRequest {
115
127
  readonly timeoutMs?: number;
116
128
  }
117
129
  export type SessionLifecycleRequest = SessionLifecycleMutationRequest | SessionListRequest;
118
- export type SessionLifecycleMutationRequest = SessionCreateRequest | SessionForkRequest | SessionResumeRequest | SessionCloseRequest | SessionDeleteRequest;
130
+ export type SessionLifecycleMutationRequest = SessionCreateRequest | SessionForkRequest | SessionResumeRequest | SessionCloseRequest | SessionDeleteRequest | SessionReconcileUncertainRequest;
119
131
  export type SessionLifecycleCertainty = "terminal" | "retryable" | "cleanup_pending" | "uncertain";
120
132
  export interface SessionLifecycleSessionResult {
121
133
  readonly sessionId: string;
@@ -162,6 +174,11 @@ export interface SessionDeleteResult {
162
174
  readonly operation: "session.delete";
163
175
  readonly result: SessionLifecycleSessionResult;
164
176
  }
177
+ export interface SessionReconcileUncertainResult {
178
+ readonly ok: true;
179
+ readonly operation: "session.reconcile_uncertain";
180
+ readonly result: SessionLifecycleSessionResult;
181
+ }
165
182
  export interface SessionListSuccessResult {
166
183
  readonly ok: true;
167
184
  readonly operation: "session.list";
@@ -201,6 +218,12 @@ export type SessionDeleteFailure = {
201
218
  readonly certainty: SessionLifecycleCertainty;
202
219
  readonly error: SessionLifecycleError;
203
220
  };
221
+ export type SessionReconcileUncertainFailure = {
222
+ readonly ok: false;
223
+ readonly operation: "session.reconcile_uncertain";
224
+ readonly certainty: SessionLifecycleCertainty;
225
+ readonly error: SessionLifecycleError;
226
+ };
204
227
  export type SessionListFailure = {
205
228
  readonly ok: false;
206
229
  readonly operation: "session.list";
@@ -212,8 +235,9 @@ export type SessionForkOutcome = SessionForkResult | SessionForkFailure;
212
235
  export type SessionResumeOutcome = SessionResumeResult | SessionResumeFailure;
213
236
  export type SessionCloseOutcome = SessionCloseResult | SessionCloseFailure;
214
237
  export type SessionDeleteOutcome = SessionDeleteResult | SessionDeleteFailure;
238
+ export type SessionReconcileUncertainOutcome = SessionReconcileUncertainResult | SessionReconcileUncertainFailure;
215
239
  export type SessionListOutcome = SessionListSuccessResult | SessionListFailure;
216
- export type SessionLifecycleResult = SessionCreateOutcome | SessionForkOutcome | SessionResumeOutcome | SessionCloseOutcome | SessionDeleteOutcome | SessionListOutcome;
240
+ export type SessionLifecycleResult = SessionCreateOutcome | SessionForkOutcome | SessionResumeOutcome | SessionCloseOutcome | SessionDeleteOutcome | SessionReconcileUncertainOutcome | SessionListOutcome;
217
241
  /** Shared, side-effect-free validation for lifecycle mutation requests. */
218
242
  export type SessionLifecycleMutationValidation = {
219
243
  readonly ok: true;
@@ -221,20 +245,23 @@ export type SessionLifecycleMutationValidation = {
221
245
  readonly actor: SessionLifecycleActor;
222
246
  readonly requestKey: string;
223
247
  readonly target: Readonly<Record<string, unknown>>;
224
- } | SessionCreateFailure | SessionForkFailure | SessionResumeFailure | SessionCloseFailure | SessionDeleteFailure;
248
+ } | SessionCreateFailure | SessionForkFailure | SessionResumeFailure | SessionCloseFailure | SessionDeleteFailure | SessionReconcileUncertainFailure;
225
249
  /** Derives a stable Broker idempotency key without exposing caller identity to Broker inputs. */
226
250
  export declare function deriveSessionLifecycleIdempotencyKey(actor: SessionLifecycleActor, requestKey: string, operation: SessionLifecycleOperation): string;
251
+ export declare function validateSessionReconcileUncertainTarget(value: unknown): value is SessionReconcileUncertainTarget;
227
252
  /** Validates lifecycle authority and shape without contacting the Broker. */
228
253
  export declare function validateSessionLifecycleMutationRequest(request: unknown): SessionLifecycleMutationValidation;
229
254
  export declare class SessionLifecycleService {
230
255
  #private;
231
256
  constructor(client: SessionLifecycleClient);
232
- execute(request: SessionLifecycleMutationRequest): Promise<SessionCreateOutcome | SessionForkOutcome | SessionResumeOutcome | SessionCloseOutcome | SessionDeleteOutcome>;
257
+ execute(request: SessionLifecycleMutationRequest): Promise<SessionCreateOutcome | SessionForkOutcome | SessionResumeOutcome | SessionCloseOutcome | SessionDeleteOutcome | SessionReconcileUncertainOutcome>;
258
+ executeWithIdempotencyKey(request: SessionLifecycleMutationRequest, idempotencyKey: string): Promise<SessionCreateOutcome | SessionForkOutcome | SessionResumeOutcome | SessionCloseOutcome | SessionDeleteOutcome | SessionReconcileUncertainOutcome>;
233
259
  create(request: Omit<SessionCreateRequest, "operation">): Promise<SessionCreateOutcome>;
234
260
  fork(request: Omit<SessionForkRequest, "operation">): Promise<SessionForkOutcome>;
235
261
  resume(request: Omit<SessionResumeRequest, "operation">): Promise<SessionResumeOutcome>;
236
262
  close(request: Omit<SessionCloseRequest, "operation">): Promise<SessionCloseOutcome>;
237
263
  delete(request: Omit<SessionDeleteRequest, "operation">): Promise<SessionDeleteOutcome>;
264
+ reconcileUncertain(request: Omit<SessionReconcileUncertainRequest, "operation">): Promise<SessionReconcileUncertainOutcome>;
238
265
  list(request: Omit<SessionListRequest, "operation">): Promise<SessionListOutcome>;
239
266
  }
240
267
  export {};
@@ -67,6 +67,11 @@ export interface CreateAgentSessionOptions {
67
67
  authStorage?: AuthStorage;
68
68
  /** Model registry. Default: discoverModels(authStorage, agentDir) */
69
69
  modelRegistry?: ModelRegistry;
70
+ /** @internal Allows the first-party CLI root to mutate its own registry and observe an admission attempt. */
71
+ modelRegistryStartupMutation?: {
72
+ owner: "cli-root";
73
+ onAttempt(): void;
74
+ };
70
75
  /** Model to use. Default: from settings, else first available */
71
76
  model?: Model;
72
77
  /** Raw model pattern string (e.g. from --model CLI flag) to resolve after extensions load.
@@ -302,6 +302,8 @@ export interface AgentSessionConfig {
302
302
  initialMCPToolSelectionIsExplicit?: boolean;
303
303
  /** Whether a discoverable built-in selection was explicitly supplied to the constructor, including an empty selection. */
304
304
  initialDiscoveredBuiltinToolSelectionIsExplicit?: boolean;
305
+ /** Whether the caller explicitly disabled every built-in tool for this session. */
306
+ explicitEmptyToolSelection?: boolean;
305
307
  /** Whether constructor-provided MCP selections should be persisted immediately. */
306
308
  persistInitialMCPToolSelection?: boolean;
307
309
  /** Whether constructor-provided discovered-built-in selections should be persisted immediately. */
@@ -816,6 +818,8 @@ export declare class AgentSession {
816
818
  * Returns the names of tools currently set on the agent.
817
819
  */
818
820
  getActiveToolNames(): string[];
821
+ /** Whether this session explicitly requested zero built-in tools. */
822
+ isExplicitEmptyToolSelection(): boolean;
819
823
  /** Whether the edit tool is registered in this session. */
820
824
  get hasEditTool(): boolean;
821
825
  /**
@@ -60,6 +60,14 @@ export declare class FallbackChainController {
60
60
  reason: string;
61
61
  }>): void;
62
62
  onAttemptFailure(triggerClass: FallbackTriggerClass, reason: string): FallbackFailureResult;
63
+ /**
64
+ * Record one bounded escaped-argument exhaustion for the active model and
65
+ * advance immediately. The discarded wire attempts are intentionally not
66
+ * provider charges; this single policy charge prevents the same model from
67
+ * being retried forever while still allowing every eligible fallback model
68
+ * its own literal-UTF-8 recovery budget.
69
+ */
70
+ recordEscapedArgumentsFailure(reason: string, advance?: boolean): boolean;
63
71
  advance(): boolean;
64
72
  /**
65
73
  * Restore the entry that just advanced for one attempt with a rotated credential.
@@ -0,0 +1,54 @@
1
+ import { type ExecOptions, type ExecResult } from "../../exec/exec";
2
+ import type { ParsedSlashCommand, SlashCommandResult, SlashCommandRuntime } from "../types";
3
+ /**
4
+ * Aside CLI discovery and `/aside` dispatch.
5
+ *
6
+ * Probe-only: never runs the installer. Missing-CLI handling is the caller's
7
+ * decision. This is an explicit composer frontend, not a GJC browser backend.
8
+ */
9
+ export declare const ASIDE_INSTALL_URL = "https://releases.aside.com/install.sh";
10
+ export declare const ASIDE_INSTALL_COMMAND = "curl -fsSL https://releases.aside.com/install.sh | bash";
11
+ export declare const ASIDE_USAGE: string;
12
+ export type AsideCliProbe = {
13
+ ok: true;
14
+ path: string;
15
+ } | {
16
+ ok: false;
17
+ searched: string[];
18
+ manualInstallCommand: string;
19
+ url: string;
20
+ };
21
+ export type AsideWhich = (command: string) => string | null | undefined;
22
+ export type AsideIsExecutable = (filePath: string) => boolean;
23
+ export type AsideExec = (command: string, args: string[], cwd: string, options?: ExecOptions) => Promise<ExecResult>;
24
+ export interface AsideHandlerOptions {
25
+ homedir?: () => string;
26
+ which?: AsideWhich;
27
+ isExecutable?: AsideIsExecutable;
28
+ exec?: AsideExec;
29
+ }
30
+ /** Candidate absolute paths for the `aside` CLI, in priority order. */
31
+ export declare function asideCliCandidates(home?: string): string[];
32
+ /** Resolve the first executable Aside CLI path, or null when none is found. */
33
+ export declare function resolveAsideCliPath(home?: string, options?: Pick<AsideHandlerOptions, "which" | "isExecutable">): string | null;
34
+ /** Structured probe result for callers that need to guide installation. */
35
+ export declare function probeAsideCli(home?: string, options?: Pick<AsideHandlerOptions, "which" | "isExecutable">): AsideCliProbe;
36
+ export declare function posixQuote(value: string): string;
37
+ /** Quote a path for the PowerShell command shown on native Windows. */
38
+ export declare function windowsPowerShellQuote(value: string): string;
39
+ type AsideArgvResult = {
40
+ ok: true;
41
+ args: string[];
42
+ } | {
43
+ ok: false;
44
+ error: string;
45
+ };
46
+ /** Parse user-provided Aside argv without silently accepting malformed quoting. */
47
+ export declare function parseAsideArgv(input: string): AsideArgvResult;
48
+ export declare function formatAsideMissingCli(probe: Extract<AsideCliProbe, {
49
+ ok: false;
50
+ }>): string;
51
+ export declare function formatAsideMcpRegistration(cliPath: string, platform?: NodeJS.Platform): string;
52
+ export declare function createAsideHandler(options?: AsideHandlerOptions): (command: ParsedSlashCommand, runtime: SlashCommandRuntime) => Promise<SlashCommandResult>;
53
+ export declare const handleAsideAcp: (command: ParsedSlashCommand, runtime: SlashCommandRuntime) => Promise<SlashCommandResult>;
54
+ export {};
@@ -45,6 +45,7 @@ export interface AskToolDetails {
45
45
  export declare const OTHER_OPTION = "Other (type your own)";
46
46
  export declare const ASK_CLARIFICATION_OPTION = "Ask about these choices";
47
47
  export declare const RECOMMENDED_SUFFIX = " (Recommended)";
48
+ export declare const MAX_EMPTY_CUSTOM_ATTEMPTS = 3;
48
49
  /** The only remote navigation control; labels are presentation, never protocol. */
49
50
  export declare function askRemoteControls(input: {
50
51
  multi: boolean;
@@ -86,6 +87,14 @@ export declare class AskTool implements AgentTool<AskParametersSchema, AskToolDe
86
87
  get parameters(): AskParametersSchema;
87
88
  readonly rawArgumentValidation: (arguments_: Record<string, unknown>) => RawArgumentValidationResult;
88
89
  readonly strict = true;
90
+ /**
91
+ * Display-only fields: question text and option labels render to the user
92
+ * and carry no executable or persisted meaning, so `\uXXXX`-escaped
93
+ * non-ASCII text inside them degrades to a warning instead of failing the
94
+ * run (issue #4983). Ids, workflow-gate metadata, and deep-interview
95
+ * records stay load-bearing and keep the fail-closed rejection.
96
+ */
97
+ readonly displaySafeEscapedArgFields: readonly ["questions.question", "questions.options.label"];
89
98
  readonly loadMode = "discoverable";
90
99
  constructor(session: ToolSession);
91
100
  static createIf(session: ToolSession): AskTool | null;
@@ -8,6 +8,14 @@ export declare class ClipboardTransportError extends Error {
8
8
  readonly context?: Record<string, unknown> | undefined;
9
9
  constructor(message: string, context?: Record<string, unknown> | undefined);
10
10
  }
11
+ export type ClipboardDeliveryOutcome = {
12
+ status: "verified";
13
+ transport: "ssh";
14
+ } | {
15
+ status: "attempted";
16
+ transport: "auto" | "native" | "osc52";
17
+ reason: string;
18
+ };
11
19
  /**
12
20
  * Read text from the configured SSH clipboard host (`pbpaste`).
13
21
  *
@@ -31,7 +39,7 @@ export declare function pasteFromClipboardViaSsh(host: string): Promise<string>;
31
39
  *
32
40
  * @param text - UTF-8 text to place on the clipboard.
33
41
  */
34
- export declare function copyToClipboard(text: string): Promise<void>;
42
+ export declare function copyToClipboard(text: string): Promise<ClipboardDeliveryOutcome>;
35
43
  /**
36
44
  * Paste text from the configured clipboard transport.
37
45
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/coding-agent",
4
- "version": "0.15.2",
4
+ "version": "0.15.4",
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.15.2",
60
- "@gajae-code/agent-core": "0.15.2",
61
- "@gajae-code/ai": "0.15.2",
62
- "@gajae-code/natives": "0.15.2",
63
- "@gajae-code/tui": "0.15.2",
64
- "@gajae-code/utils": "0.15.2",
59
+ "@gajae-code/stats": "0.15.4",
60
+ "@gajae-code/agent-core": "0.15.4",
61
+ "@gajae-code/ai": "0.15.4",
62
+ "@gajae-code/natives": "0.15.4",
63
+ "@gajae-code/tui": "0.15.4",
64
+ "@gajae-code/utils": "0.15.4",
65
65
  "@puppeteer/browsers": "^2.13.0",
66
66
  "@types/turndown": "5.0.6",
67
67
  "@xterm/headless": "^6.0.0",
@@ -44,7 +44,8 @@ const required = [...commandFiles];
44
44
  const excluded: Manifest["excluded"] = [];
45
45
  const adapters = ["telegram", "discord", "slack", "mcp", "acp", "daemonCli"] as const;
46
46
 
47
- function expectedOutcome(disposition: AdapterDisposition): ManifestAdapterRow["expected"] {
47
+ function expectedOutcome(disposition: AdapterDisposition, sdkId?: string): ManifestAdapterRow["expected"] {
48
+ if (sdkId === "session.reconcile_uncertain") return "rejected_before_send";
48
49
  return disposition === "prohibited"
49
50
  ? "rejected_before_send"
50
51
  : disposition === "machine_only" || disposition === "provider_only"
@@ -88,7 +89,9 @@ function row(adapter: Adapter, operation: (typeof OPERATIONS)[number], secret =
88
89
  "--test-name-pattern",
89
90
  `^AD-${adapterTestPrefix(adapter)}-${operation.id}${suffix}:`,
90
91
  ],
91
- expected: secret ? "rejected_before_send" : expectedOutcome(operation.adapterDispositions[adapter]),
92
+ expected: secret
93
+ ? "rejected_before_send"
94
+ : expectedOutcome(operation.adapterDispositions[adapter], operation.sdkId),
92
95
  };
93
96
  }
94
97
 
@@ -43,6 +43,7 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
43
43
  "slash_command:exit": "visual/local-only command, not a user-facing SDK control seam",
44
44
  "slash_command:notify":
45
45
  "interactive diagnostics command; session on/off delegate to the notifications extension, not an SDK ingress seam",
46
+ "slash_command:aside": "explicit user-owned external CLI process launcher; not a credential-free SDK control seam",
46
47
  "slash_command:extensions":
47
48
  "interactive local customization dashboard mutates trusted local skill, hook, and MCP configuration (including filesystem import/removal); ACP and local headless cannot dispatch it, while SDK extension list/enable operations do not expose this dashboard authority",
48
49
  "slash_command:credential":
@@ -78,6 +79,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
78
79
  "agent_session:setForcedToolChoice": "internal accessor/plumbing, not a user-facing control seam",
79
80
  "agent_session:getActiveSkillState": "internal accessor/plumbing, not a user-facing control seam",
80
81
  "agent_session:getActiveSkillPhase": "internal accessor/plumbing, not a user-facing control seam",
82
+ "agent_session:isExplicitEmptyToolSelection":
83
+ "internal tool-selection predicate used to preserve the --no-tools boundary, not a user-facing SDK control seam",
81
84
  "agent_session:getEffectiveActiveWorkflowSkillState":
82
85
  "internal restored-or-live workflow guard for session rescope, not a user-facing SDK control seam",
83
86
  "agent_session:replaceOwnedMcpManager":
@@ -816,12 +816,12 @@ class SdkServeHelp extends Command {
816
816
 
817
817
  class SdkSessionHelp extends Command {
818
818
  static description =
819
- "Manage SDK sessions: `gjc sdk session list|inspect|send|status|tail`, or the explicit raw hatch `gjc sdk session raw control|query|global`. The session CLI is broker-bound and credential-free.";
819
+ "Manage SDK sessions: `gjc sdk session list|inspect|send|status|tail|retire`, or the explicit raw hatch `gjc sdk session raw control|query|global`. The session CLI is broker-bound and credential-free.";
820
820
  static args = {
821
821
  verb: Args.string({
822
822
  description: "Session verb",
823
823
  required: false,
824
- options: ["list", "inspect", "send", "status", "tail", "raw"],
824
+ options: ["list", "inspect", "send", "status", "tail", "retire", "raw"],
825
825
  }),
826
826
  target: Args.string({
827
827
  description: "Session id (or the raw kind control|query|global for `raw`)",
@@ -155,15 +155,6 @@ export const TIER_MAP_SKIP_LIST = {
155
155
  "cursor/gemini-3.7-flash-medium": {
156
156
  rationale: "post-rebase catalog addition from dev; not yet curated",
157
157
  },
158
- "google-antigravity/gemini-3.7-flash-high": {
159
- rationale: "post-rebase catalog addition from dev; not yet curated",
160
- },
161
- "google-antigravity/gemini-3.7-flash-low": {
162
- rationale: "post-rebase catalog addition from dev; not yet curated",
163
- },
164
- "google-antigravity/gemini-3.7-flash-medium": {
165
- rationale: "post-rebase catalog addition from dev; not yet curated",
166
- },
167
158
  "google-antigravity/gemini-3.7-flash-tiered": {
168
159
  rationale: "post-rebase catalog addition from dev; not yet curated",
169
160
  },
@@ -3,6 +3,7 @@ import type { Stats } from "node:fs";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path from "node:path";
5
5
  import { hasFsCode, isEnoent } from "@gajae-code/utils/fs-error";
6
+ import { nativeProcessBindings } from "@gajae-code/utils/native-process";
6
7
 
7
8
  export interface FileLockOptions {
8
9
  staleMs?: number;
@@ -33,9 +34,22 @@ export const FileLockTestHooks: {
33
34
  /**
34
35
  * Returns the OS-provided process start timestamp for PID-reuse detection.
35
36
  * `ps` is available on the supported Unix hosts (macOS and Linux), unlike
36
- * Linux's `/proc/<pid>/stat` pseudo-file.
37
+ * Linux's `/proc/<pid>/stat` pseudo-file. Windows has no `ps`; there the
38
+ * kernel-derived process creation time exposed by the natives addon
39
+ * (`Process.incarnation`, the same identity evidence the SDK broker prefers)
40
+ * is used instead. Either way the value is only ever compared for equality
41
+ * against a value this same function produced on the same platform, and `null`
42
+ * stays fail-closed: an owner whose incarnation cannot be proved is never
43
+ * treated as reused.
37
44
  */
38
45
  export function processStartTime(pid: number): string | null {
46
+ if (process.platform === "win32") {
47
+ try {
48
+ return nativeProcessBindings().Process.fromPid(pid)?.incarnation ?? null;
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
39
53
  try {
40
54
  const result = Bun.spawnSync(["ps", "-o", "lstart=", "-p", String(pid)], { stdout: "pipe", stderr: "ignore" });
41
55
  if (result.exitCode !== 0) return null;
@@ -38,6 +38,7 @@ interface AppKeybindings {
38
38
  "app.clipboard.pasteText": true;
39
39
  "app.clipboard.copyLine": true;
40
40
  "app.clipboard.copyPrompt": true;
41
+ "app.clipboard.copyOAuthUrl": true;
41
42
  "app.session.new": true;
42
43
  "app.session.tree": true;
43
44
  "app.session.fork": true;
@@ -61,6 +62,7 @@ interface AppKeybindings {
61
62
  "app.transcript.nextTurn": true;
62
63
  "app.mode.cycle": true;
63
64
  "app.tasks.toggle": true;
65
+ "app.todo.toggle": true;
64
66
  "app.queue.togglePane": true;
65
67
  "app.message.sendNow": true;
66
68
  }
@@ -171,20 +173,24 @@ export const KEYBINDINGS = {
171
173
  defaultKeys: "alt+shift+c",
172
174
  description: "Copy prompt",
173
175
  },
176
+ "app.clipboard.copyOAuthUrl": {
177
+ defaultKeys: "alt+shift+u",
178
+ description: "Copy OAuth URL",
179
+ },
174
180
  "app.session.new": {
175
181
  defaultKeys: "ctrl+n",
176
182
  description: "Start a new session",
177
183
  },
178
184
  "app.session.tree": {
179
- defaultKeys: [],
185
+ defaultKeys: "alt+shift+s",
180
186
  description: "Show session tree",
181
187
  },
182
188
  "app.session.fork": {
183
- defaultKeys: [],
189
+ defaultKeys: "alt+shift+f",
184
190
  description: "Branch from message",
185
191
  },
186
192
  "app.session.resume": {
187
- defaultKeys: [],
193
+ defaultKeys: "alt+shift+r",
188
194
  description: "Resume session",
189
195
  },
190
196
  "app.session.observe": {
@@ -264,6 +270,10 @@ export const KEYBINDINGS = {
264
270
  defaultKeys: "alt+t",
265
271
  description: "Toggle tasks pane",
266
272
  },
273
+ "app.todo.toggle": {
274
+ defaultKeys: "alt+shift+t",
275
+ description: "Toggle todo list expansion",
276
+ },
267
277
  "app.queue.togglePane": {
268
278
  defaultKeys: [],
269
279
  description: "Toggle message queue pane",