@gajae-code/coding-agent 0.9.0 → 0.9.2

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 (98) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/types/cli/args.d.ts +1 -0
  3. package/dist/types/cli/notify-cli.d.ts +5 -0
  4. package/dist/types/cli.d.ts +3 -0
  5. package/dist/types/commands/launch.d.ts +3 -0
  6. package/dist/types/config/model-profile-activation.d.ts +7 -7
  7. package/dist/types/config/model-registry.d.ts +7 -3
  8. package/dist/types/config/model-resolver.d.ts +2 -2
  9. package/dist/types/config/settings-schema.d.ts +15 -1
  10. package/dist/types/config/settings.d.ts +7 -0
  11. package/dist/types/extensibility/runtime-skill-discovery.d.ts +20 -0
  12. package/dist/types/gjc-runtime/team-runtime.d.ts +9 -0
  13. package/dist/types/gjc-runtime/tmux-common.d.ts +1 -0
  14. package/dist/types/modes/components/settings-selector.d.ts +2 -2
  15. package/dist/types/modes/components/thinking-selector.d.ts +7 -2
  16. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  17. package/dist/types/modes/interactive-mode.d.ts +2 -0
  18. package/dist/types/modes/rpc/rpc-mode.d.ts +1 -3
  19. package/dist/types/modes/rpc/rpc-socket-security.d.ts +3 -0
  20. package/dist/types/modes/types.d.ts +3 -0
  21. package/dist/types/modes/utils/injected-user-submission.d.ts +52 -0
  22. package/dist/types/notifications/config-commands.d.ts +38 -0
  23. package/dist/types/notifications/config.d.ts +16 -0
  24. package/dist/types/notifications/index.d.ts +14 -1
  25. package/dist/types/notifications/lifecycle-commands.d.ts +1 -0
  26. package/dist/types/notifications/lifecycle-control-runtime.d.ts +2 -1
  27. package/dist/types/notifications/reply-sent-store.d.ts +53 -0
  28. package/dist/types/notifications/rich-draft.d.ts +68 -0
  29. package/dist/types/notifications/rich-render.d.ts +90 -0
  30. package/dist/types/notifications/telegram-daemon-cli.d.ts +3 -3
  31. package/dist/types/notifications/telegram-daemon.d.ts +22 -0
  32. package/dist/types/notifications/telegram-reference.d.ts +7 -0
  33. package/dist/types/notifications/threaded-render.d.ts +10 -0
  34. package/dist/types/notifications/topic-registry.d.ts +4 -5
  35. package/dist/types/runtime-credential-selector.d.ts +7 -0
  36. package/dist/types/sdk.d.ts +7 -1
  37. package/dist/types/session/agent-session.d.ts +6 -3
  38. package/dist/types/tools/index.d.ts +1 -0
  39. package/dist/types/tools/skill-discovery.d.ts +40 -0
  40. package/dist/types/utils/pasted-image-path.d.ts +30 -0
  41. package/package.json +8 -11
  42. package/src/cli/args.ts +8 -0
  43. package/src/cli/notify-cli.ts +38 -10
  44. package/src/cli.ts +5 -0
  45. package/src/commands/launch.ts +5 -0
  46. package/src/commands/notify.ts +2 -2
  47. package/src/config/keybindings.ts +1 -1
  48. package/src/config/model-profile-activation.ts +23 -7
  49. package/src/config/model-registry.ts +21 -4
  50. package/src/config/model-resolver.ts +5 -16
  51. package/src/config/settings-schema.ts +11 -2
  52. package/src/config/settings.ts +24 -1
  53. package/src/extensibility/runtime-skill-discovery.ts +229 -0
  54. package/src/gjc-runtime/launch-tmux.ts +78 -12
  55. package/src/gjc-runtime/psmux-detect.ts +17 -2
  56. package/src/gjc-runtime/team-runtime.ts +46 -7
  57. package/src/gjc-runtime/tmux-common.ts +1 -0
  58. package/src/gjc-runtime/ultragoal-runtime.ts +10 -1
  59. package/src/internal-urls/docs-index.generated.ts +6 -6
  60. package/src/lsp/client.ts +1 -0
  61. package/src/main.ts +26 -3
  62. package/src/modes/components/footer.ts +7 -2
  63. package/src/modes/components/settings-selector.ts +3 -3
  64. package/src/modes/components/thinking-selector.ts +83 -20
  65. package/src/modes/controllers/event-controller.ts +11 -2
  66. package/src/modes/controllers/extension-ui-controller.ts +8 -1
  67. package/src/modes/controllers/input-controller.ts +62 -26
  68. package/src/modes/controllers/selector-controller.ts +43 -0
  69. package/src/modes/interactive-mode.ts +5 -0
  70. package/src/modes/rpc/rpc-mode.ts +3 -9
  71. package/src/modes/rpc/rpc-socket-security.ts +13 -1
  72. package/src/modes/types.ts +3 -0
  73. package/src/modes/utils/injected-user-submission.ts +94 -0
  74. package/src/modes/utils/ui-helpers.ts +48 -15
  75. package/src/notifications/config-commands.ts +90 -0
  76. package/src/notifications/config.ts +27 -0
  77. package/src/notifications/index.ts +184 -14
  78. package/src/notifications/lifecycle-commands.ts +37 -16
  79. package/src/notifications/lifecycle-control-runtime.ts +8 -3
  80. package/src/notifications/reply-sent-store.ts +134 -0
  81. package/src/notifications/rich-draft.ts +107 -0
  82. package/src/notifications/rich-render.ts +143 -0
  83. package/src/notifications/telegram-daemon-cli.ts +60 -5
  84. package/src/notifications/telegram-daemon.ts +431 -91
  85. package/src/notifications/telegram-reference.ts +17 -0
  86. package/src/notifications/threaded-render.ts +42 -1
  87. package/src/notifications/topic-registry.ts +9 -8
  88. package/src/prompts/tools/search-tool-bm25.md +5 -0
  89. package/src/prompts/tools/skill-discovery.md +13 -0
  90. package/src/runtime-credential-selector.ts +35 -0
  91. package/src/sdk.ts +104 -13
  92. package/src/session/agent-session.ts +48 -22
  93. package/src/slash-commands/builtin-registry.ts +78 -1
  94. package/src/system-prompt.ts +4 -6
  95. package/src/tools/index.ts +4 -0
  96. package/src/tools/skill-discovery.ts +73 -0
  97. package/src/tools/skill.ts +15 -2
  98. package/src/utils/pasted-image-path.ts +170 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,53 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.2] - 2026-07-09
6
+ ### Added
7
+
8
+ - Added `gjc --credential <selector>` for pinning a stored provider credential by `email:`, `id:`, `account:`, `project:`, or `provider/email:` during a session.
9
+ - Added `--mpreset <profile>` support to Telegram `/session_create`, forwarding both `--mpreset <name>` and `--mpreset=<name>` as split argv to the spawned GJC child.
10
+ - Added the built-in `skill_discovery` tool for runtime discovery of custom project/user skills without injecting the full skill catalog into the core prompt.
11
+ - Pasting or drag-dropping a path to an existing image file now attaches the image and inserts an `[image N]` placeholder, including quoted paths, `file://` URIs, `~/` expansion, spaces, and macOS screenshot narrow no-break spaces.
12
+
13
+ ### Fixed
14
+
15
+ - Finalized notification turn mirrors now default to the bounded full-turn cap so Telegram's existing chunked delivery can send long assistant answers instead of receiving an already-truncated 3500-character summary; `GJC_NOTIFICATIONS_TURN_MAX` remains available to lower the cap for summary-style mirrors, and live previews stay capped as one editable message.
16
+ - `gjc --tmux` now wraps the inner GJC command with a durable `tmux-exit.json` marker next to `runtime-state.json`, so a tmux-resident session that exits before normal runtime-state finalization leaves a public-safe exit timestamp/code for silent-vanish diagnosis (#1746).
17
+ - `gjc --tmux` terminal titles now track live tmux session renames while preserving the friendly project/branch title for untouched generated session ids.
18
+ - Telegram session forum-topic renames now remain retryable after a transient `editForumTopic` failure, so topics do not get stuck at the provisional `GJC <session>` name while the daemon incorrectly records the final title locally.
19
+ - `/effort` selector choices now show the current reasoning effort and mirror `/model` by asking whether to apply the selected effort for the current session or save it as the default, including support for persisting `off`. Default model presets also sync their encoded effort into the persisted effort default so later `/effort` defaults are not overwritten on restart.
20
+ - Composer queue submissions (`Alt+Q` / `app.message.queue`) force one-at-a-time follow-up delivery, including replay after compaction, without disabling broader batch mode for other follow-up callers.
21
+ - `--credential` now rejects a missing selector immediately instead of falling through into session launch with no output.
22
+ - `gjc-session` prompt/monitor postmortem helpers now work on macOS's system Bash/Python, so missing tmux sessions write the public-safe `vanished.json` marker and prompt injection exits through the guarded refusal path.
23
+
24
+ ## [0.9.1] - 2026-07-08
25
+
26
+ ### Added
27
+
28
+ - Added `/effort` to show or set the current model reasoning effort, including a selector UI when run without arguments and validation for invalid effort names.
29
+ - Added `--mpreset <profile>` option to the Telegram `/session_create` command, allowing users to specify a model profile preset when creating a session remotely (e.g. `/session_create path /repo --mpreset codex-eco`). Both `--mpreset <name>` and `--mpreset=<name>` forms are supported.
30
+ - Pasting or drag-dropping a path to any existing image file into the interactive editor now attaches the image and inserts an `[image N]` placeholder instead of leaving the raw path in the prompt. Quoted paths, `file://` URIs, `~/` expansion, spaces, and macOS screenshot narrow no-break spaces are handled when the whole paste is a single supported image file.
31
+ - Added `GJC_NOTIFY=off` as a per-run completion-notification opt-out.
32
+ - Telegram mirror messages now render as native Bot API 10.1 rich messages on by default with a global `/rich on|off` toggle, while failing closed to the previous HTML path.
33
+
34
+ ### Changed
35
+
36
+ - Upgraded the Extragoal review template with a stronger reviewer lane and optional maximalist N-of-N review recipe.
37
+ - Added `--mpreset <profile>` option to the Telegram `/session_create` command, allowing users to specify a model profile preset when creating a session remotely (e.g. `/session_create path /repo --mpreset codex-eco`). Both `--mpreset <name>` and `--mpreset=<name>` forms are supported. The preset is passed as a regular `--mpreset` CLI flag to the spawned `gjc` child, where the existing `applyStartupModelProfiles` flow activates it.
38
+ - Added the built-in `skill_discovery` tool for runtime discovery of custom project `.gjc/skills` and user `~/.gjc/skills` without injecting the full skill catalog into the core prompt; selected discovered skills are loaded narrowly through the existing `skill` invocation path (#1815).
39
+ - Pasting or drag-dropping a path to any existing image file into the interactive editor now attaches the image and inserts an `[image N]` placeholder instead of leaving the raw path in the prompt. Previously this only worked for clipboard temp files (`/tmp/clipboard-*` or `/var/folders/xx/yy/T/clipboard-*`); terminal drag-drop paths — including shell-escaped spaces and the U+202F narrow no-break space in macOS screenshot names — pasted as long raw path text. Quoted paths, `file://` URIs (decoded via Node's `fileURLToPath` semantics, including Windows drive-letter, `file://localhost`, and UNC forms), and `~/` expansion are handled; the whole paste must be a single path to an existing image file whose content carries a supported image signature (PNG/JPEG/GIF/WEBP), so prose containing paths and non-image files with image-looking extensions are inserted unchanged. When attachment still fails (unsupported content, oversized image, load error), the original pasted text is replayed into the editor instead of being consumed.
40
+
41
+ ### Fixed
42
+ - Composer queue submissions (`Alt+Q` / `app.message.queue`) now force one-at-a-time follow-up delivery for those queued prompts, including replay after compaction, so they do not collapse into one batched model call even when the broader follow-up mode is set to `all`.
43
+
44
+ - `gjc team` on Windows/psmux now targets the GJC-managed leader session by name instead of trusting the inherited `TMUX_PANE`.
45
+ - Kitty inline images no longer duplicate/stack or paint over transcript text when the diff renderer repaints the image line.
46
+ - Session creation now forwards `--mpreset` as split argv instead of one combined argument.
47
+ - The RPC server restores the duplicate-listen boundary error instead of masking it.
48
+ - Mac Option+Enter no longer collides with the queue-message binding.
49
+ - Telegram completion notifications suppress dot-only messages, and Telegram-originated input is shown immediately and recorded in prompt history.
50
+ - The computer red-team CI gate avoids false positives from non-computer-control changes.
51
+
5
52
  ## [0.9.0] - 2026-07-07
6
53
  ### Added
7
54
 
@@ -12,6 +59,7 @@
12
59
  - Added `GJC_NOTIFICATIONS_TURN_MAX` to raise the finalized `turn_stream` text cap (default 3500 chars, unchanged) so full long turns reach split-capable clients — the Telegram daemon already fans a long finalized turn across multiple messages via `splitTelegramHtml` — instead of being truncated with an ellipsis at the notification layer. The value is clamped to a finite `[280, 40000]` range: a non-finite or non-positive value (unset, `NaN`, `Infinity`, `<= 0`) falls back to the default, so the cap can never be unbounded. Opt-in and off by default (mirror stays a glanceable per-turn summary); live in-progress frames are intentionally NOT raised so a streaming turn stays one editable preview message rather than fanning across sends.
13
60
 
14
61
  ### Fixed
62
+ - Composer-pasted images now follow the visible `[image N]` placeholders at submit time, so deleting a pasted image placeholder with Backspace removes that attachment instead of still sending it to the model or Telegram session mirrors.
15
63
 
16
64
  - Stopped the Hindsight memory backend from injecting stale public-memory-tool guidance into the system prompt. `hindsight/backend.ts`'s `STATIC_INSTRUCTIONS` still instructed the model to "Use `recall`/`retain`/`reflect`", but those tools were removed from the public surface in #341 — they are unregistered in `BUILTIN_TOOLS`/`HIDDEN_TOOLS` and not discoverable — so every Hindsight-backed session advertised three uncallable tools in its injected `# Memory` block. #341 cleaned the local-memory prompts, public docs, and tool registry but never touched the Hindsight backend's own injected block, and its `public-memory-tool-surface.test.ts` guard list omitted `backend.ts`, so the drift went unnoticed. The block now describes the automatic recall/retain lifecycle instead of naming tools, and the regression test now guards `hindsight/backend.ts`.
17
65
  - Task subagent output-ID allocation now reserves numeric prefixes from `.jsonl` session artifacts and `.patch` sidecars as well as final `.md` outputs, preventing resumed or partial subagent runs from reusing an existing artifact stem (#1733).
@@ -14,6 +14,7 @@ export interface Args {
14
14
  mpreset?: string;
15
15
  default?: boolean;
16
16
  apiKey?: string;
17
+ credential?: string;
17
18
  systemPrompt?: string;
18
19
  appendSystemPrompt?: string;
19
20
  thinking?: Effort;
@@ -19,12 +19,17 @@ export interface NotifyCommandDeps {
19
19
  setupRedact?: boolean;
20
20
  setupInteractive?: boolean;
21
21
  threadedModePrompt?: (message: string) => Promise<string>;
22
+ tokenPrompt?: () => Promise<string>;
23
+ setExitCode?: (code: number) => void;
24
+ exitProcess?: (code: number) => void;
22
25
  }
23
26
  export declare function parseNotifyArgs(args: string[]): NotifyCommandArgs | undefined;
24
27
  export declare function runNotifyCommand(cmd: NotifyCommandArgs, deps?: NotifyCommandDeps): Promise<void>;
28
+ export declare function runNotifyCliCommand(cmd: NotifyCommandArgs, deps?: NotifyCommandDeps): Promise<void>;
25
29
  type TokenPromptInput = NodeJS.ReadStream & {
26
30
  isRaw?: boolean;
27
31
  setRawMode?: (mode: boolean) => unknown;
32
+ pause?: () => unknown;
28
33
  };
29
34
  type TokenPromptOutput = Pick<NodeJS.WriteStream, "write">;
30
35
  export declare function promptForToken(input?: TokenPromptInput, output?: TokenPromptOutput): Promise<string>;
@@ -40,6 +40,9 @@ export declare class RootHelpCommand extends Command {
40
40
  "api-key": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
41
41
  description: string;
42
42
  };
43
+ credential: import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
44
+ description: string;
45
+ };
43
46
  "system-prompt": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
44
47
  description: string;
45
48
  };
@@ -37,6 +37,9 @@ export default class Index extends Command {
37
37
  "api-key": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
38
38
  description: string;
39
39
  };
40
+ credential: import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
41
+ description: string;
42
+ };
40
43
  "system-prompt": import("@gajae-code/utils/cli").FlagDescriptor<"string"> & {
41
44
  description: string;
42
45
  };
@@ -1,4 +1,4 @@
1
- import type { ThinkingLevel } from "@gajae-code/agent-core";
1
+ import { ThinkingLevel } from "@gajae-code/agent-core";
2
2
  import type { Api, Model } from "@gajae-code/ai";
3
3
  import type { AgentSession } from "../session/agent-session";
4
4
  import { type GjcModelAssignmentTargetId, type ModelRegistry } from "./model-registry";
@@ -16,6 +16,10 @@ export interface PrepareModelProfileActivationOptions {
16
16
  settings: Pick<Settings, "get">;
17
17
  profileName: string;
18
18
  }
19
+ export interface ApplyModelProfileActivationOptions {
20
+ persistDefault?: boolean;
21
+ thinkingLevelOverride?: ThinkingLevel;
22
+ }
19
23
  export interface PreparedModelProfileActivation {
20
24
  profileName: string;
21
25
  session: ModelProfileActivationSession & {
@@ -55,9 +59,7 @@ export declare function materializeActiveModelProfileAssignment(options: Materia
55
59
  export declare function materializeActiveModelProfileAssignments(options: MaterializeModelProfileAssignmentsOptions): boolean;
56
60
  export declare function formatModelProfileCredentialError(profileLabel: string, providers: readonly string[]): string;
57
61
  export declare function prepareModelProfileActivation(options: PrepareModelProfileActivationOptions): Promise<PreparedModelProfileActivation>;
58
- export declare function applyPreparedModelProfileActivation(prepared: PreparedModelProfileActivation, options?: {
59
- persistDefault?: boolean;
60
- }): Promise<void>;
62
+ export declare function applyPreparedModelProfileActivation(prepared: PreparedModelProfileActivation, options?: ApplyModelProfileActivationOptions): Promise<void>;
61
63
  export interface MaterializeModelProfileForDeletionResult {
62
64
  modelRoles: Record<string, string>;
63
65
  agentModelOverrides: Record<string, string>;
@@ -75,7 +77,5 @@ export declare function restoreMaterializedModelProfileForDeletion(options: {
75
77
  session: Pick<ModelProfileActivationSession, "setActiveModelProfile">;
76
78
  snapshot: MaterializeModelProfileForDeletionResult;
77
79
  }): Promise<void>;
78
- export declare function activateModelProfile(options: PrepareModelProfileActivationOptions, applyOptions?: {
79
- persistDefault?: boolean;
80
- }): Promise<void>;
80
+ export declare function activateModelProfile(options: PrepareModelProfileActivationOptions, applyOptions?: ApplyModelProfileActivationOptions): Promise<void>;
81
81
  export {};
@@ -1,4 +1,4 @@
1
- import { type Api, type AssistantMessageEventStream, type CacheRetention, type Context, type Model, type ModelRefreshStrategy, type ModelRequestTransform, type SimpleStreamOptions, type ThinkingConfig } from "@gajae-code/ai";
1
+ import { type Api, type AssistantMessageEventStream, type AuthCredentialSelector, type CacheRetention, type Context, type Model, type ModelRefreshStrategy, type ModelRequestTransform, type SimpleStreamOptions, type ThinkingConfig } from "@gajae-code/ai";
2
2
  import type { OAuthCredentials, OAuthLoginCallbacks } from "@gajae-code/ai/utils/oauth/types";
3
3
  import { type ThemeColor } from "../modes/theme/theme";
4
4
  import type { AuthStorage } from "../session/auth-storage";
@@ -386,11 +386,15 @@ export declare class ModelRegistry {
386
386
  /**
387
387
  * Get API key for a model.
388
388
  */
389
- getApiKey(model: Model<Api>, sessionId?: string): Promise<string | undefined>;
389
+ getApiKey(model: Model<Api>, sessionId?: string, options?: {
390
+ credentialSelector?: AuthCredentialSelector;
391
+ }): Promise<string | undefined>;
390
392
  /**
391
393
  * Get API key for a provider (e.g., "openai").
392
394
  */
393
- getApiKeyForProvider(provider: string, sessionId?: string, baseUrl?: string): Promise<string | undefined>;
395
+ getApiKeyForProvider(provider: string, sessionId?: string, baseUrl?: string, options?: {
396
+ credentialSelector?: AuthCredentialSelector;
397
+ }): Promise<string | undefined>;
394
398
  /**
395
399
  * Check if a model is using OAuth credentials (subscription).
396
400
  */
@@ -2,7 +2,7 @@
2
2
  * Model resolution, scoping, and initial selection
3
3
  */
4
4
  import { ThinkingLevel } from "@gajae-code/agent-core";
5
- import { type Api, type Effort, type KnownProvider, type Model } from "@gajae-code/ai";
5
+ import { type Api, type KnownProvider, type Model } from "@gajae-code/ai";
6
6
  import { type ModelRegistry, type ModelRole } from "./model-registry";
7
7
  import type { Settings } from "./settings";
8
8
  /** Default model IDs for each known provider */
@@ -204,7 +204,7 @@ export declare function findInitialModel(options: {
204
204
  isContinuing: boolean;
205
205
  defaultProvider?: string;
206
206
  defaultModelId?: string;
207
- defaultThinkingSelector?: Effort;
207
+ defaultThinkingSelector?: ThinkingLevel;
208
208
  modelRegistry: InitialModelRegistry;
209
209
  }): Promise<InitialModelResult>;
210
210
  /**
@@ -130,6 +130,14 @@ export declare const SETTINGS_SCHEMA: {
130
130
  readonly type: "string";
131
131
  readonly default: undefined;
132
132
  };
133
+ readonly "notifications.telegram.rich.enabled": {
134
+ readonly type: "boolean";
135
+ readonly default: true;
136
+ };
137
+ readonly "notifications.telegram.richDraft.enabled": {
138
+ readonly type: "boolean";
139
+ readonly default: false;
140
+ };
133
141
  readonly "notifications.discord.botToken": {
134
142
  readonly type: "string";
135
143
  readonly default: undefined;
@@ -899,7 +907,7 @@ export declare const SETTINGS_SCHEMA: {
899
907
  };
900
908
  readonly defaultThinkingLevel: {
901
909
  readonly type: "enum";
902
- readonly values: readonly Effort[];
910
+ readonly values: readonly ["off", ...Effort[]];
903
911
  readonly default: Effort;
904
912
  readonly ui: {
905
913
  readonly tab: "model";
@@ -3852,6 +3860,12 @@ export interface NotificationsSettings {
3852
3860
  telegram: {
3853
3861
  botToken: string | undefined;
3854
3862
  chatId: string | undefined;
3863
+ rich: {
3864
+ enabled: boolean;
3865
+ };
3866
+ richDraft: {
3867
+ enabled: boolean;
3868
+ };
3855
3869
  };
3856
3870
  discord: {
3857
3871
  botToken: string | undefined;
@@ -82,6 +82,13 @@ export declare class Settings {
82
82
  * Call before exit to ensure all changes are persisted.
83
83
  */
84
84
  flush(): Promise<void>;
85
+ /**
86
+ * Like {@link flush}, but rejects if the durable save fails instead of
87
+ * swallowing the error. Use where the caller must confirm persistence before
88
+ * reporting success (e.g. the Telegram `/rich` toggle). In-memory instances
89
+ * ({@link isolated}) short-circuit in {@link #saveNow} and never throw.
90
+ */
91
+ flushOrThrow(): Promise<void>;
85
92
  cloneForCwd(cwd: string): Promise<Settings>;
86
93
  getStorage(): AgentStorage | null;
87
94
  getCwd(): string;
@@ -0,0 +1,20 @@
1
+ import type { SkillsSettings } from "../config/settings-schema";
2
+ import type { Skill } from "./skills";
3
+ export type RuntimeSkillDiscoverySource = "project" | "user";
4
+ export interface RuntimeSkillDiscoveryCandidate {
5
+ name: string;
6
+ description: string;
7
+ source: RuntimeSkillDiscoverySource;
8
+ path: string;
9
+ useWhen?: string[];
10
+ }
11
+ export interface DiscoverRuntimeSkillsOptions {
12
+ cwd: string;
13
+ home?: string;
14
+ query?: string;
15
+ limit?: number;
16
+ source?: RuntimeSkillDiscoverySource | "all";
17
+ policy?: SkillsSettings;
18
+ }
19
+ export declare function discoverRuntimeSkills(options: DiscoverRuntimeSkillsOptions): Promise<RuntimeSkillDiscoveryCandidate[]>;
20
+ export declare function findRuntimeSkillByName(cwd: string, name: string, policy?: SkillsSettings, home?: string): Promise<Skill | undefined>;
@@ -221,6 +221,7 @@ export interface GjcTeamStartOptions {
221
221
  cwd?: string;
222
222
  env?: NodeJS.ProcessEnv;
223
223
  dryRun?: boolean;
224
+ platform?: NodeJS.Platform;
224
225
  mailboxDeliveryTransport?: GjcTeamMailboxDeliveryTransport;
225
226
  }
226
227
  export interface GjcTeamApiClaimResult {
@@ -305,6 +306,14 @@ export declare function pruneTeamWorkerGcRecord(record: GcRecord, probe: GcPidPr
305
306
  export declare function persistGjcTeamModeStateSummary(snapshot: GjcTeamSnapshot, cwd?: string): Promise<void>;
306
307
  export declare function recoverGjcTeamStaleClaims(teamName: string, cwd?: string, env?: NodeJS.ProcessEnv): Promise<GjcTeamLivenessRecoveryResult>;
307
308
  type GjcTeamTaskMetadataInput = Partial<Pick<GjcTeamTask, "owner" | "lane" | "required_role" | "allowed_roles" | "depends_on" | "blocked_by">>;
309
+ export declare function resolveWorkerWorktreePath(input: {
310
+ repoRoot: string;
311
+ stateDir: string;
312
+ teamName: string;
313
+ workerId: string;
314
+ platform: NodeJS.Platform;
315
+ isPsmux: boolean;
316
+ }): string;
308
317
  type CommandPathResolver = (command: string) => string | null;
309
318
  export declare function resolveGjcWorkerCommand(cwd?: string, env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform, argv?: string[], execPath?: string, which?: CommandPathResolver): string;
310
319
  /** @internal Exported for unit tests. */
@@ -2,6 +2,7 @@ import type { ResolvedTmuxBinary } from "./psmux-detect";
2
2
  export declare const GJC_DEFAULT_TMUX_SESSION = "gajae_code";
3
3
  export declare const GJC_TMUX_SESSION_PREFIX = "gajae_code_";
4
4
  export declare const GJC_TMUX_COMMAND_ENV = "GJC_TMUX_COMMAND";
5
+ export declare const GJC_TMUX_ACTIVE_SESSION_ENV = "GJC_TMUX_ACTIVE_SESSION";
5
6
  export declare const GJC_TMUX_PROFILE_ENV = "GJC_TMUX_PROFILE";
6
7
  export declare const GJC_TMUX_MOUSE_ENV = "GJC_MOUSE";
7
8
  export declare const GJC_TMUX_PROFILE_OPTION = "@gjc-profile";
@@ -1,4 +1,4 @@
1
- import type { ThinkingLevel } from "@gajae-code/agent-core";
1
+ import { type ThinkingLevel as ThinkingLevelValue } from "@gajae-code/agent-core";
2
2
  import type { Effort } from "@gajae-code/ai";
3
3
  import { Container, SettingsList } from "@gajae-code/tui";
4
4
  import { type SettingPath } from "../../config/settings";
@@ -13,7 +13,7 @@ export interface SettingsRuntimeContext {
13
13
  /** Available thinking levels (from session) */
14
14
  availableThinkingLevels: Effort[];
15
15
  /** Current thinking level (from session) */
16
- thinkingLevel: ThinkingLevel | undefined;
16
+ thinkingLevel: ThinkingLevelValue | undefined;
17
17
  /** Available themes */
18
18
  availableThemes: string[];
19
19
  /** Available model profile names (from the model registry) */
@@ -1,10 +1,15 @@
1
- import type { Effort } from "@gajae-code/ai";
1
+ import { type ThinkingLevel as ThinkingLevelValue } from "@gajae-code/agent-core";
2
2
  import { Container, SelectList } from "@gajae-code/tui";
3
+ export interface ThinkingSelectorSelection {
4
+ level: ThinkingLevelValue;
5
+ persistDefault: boolean;
6
+ }
3
7
  /**
4
8
  * Component that renders a thinking level selector with borders
5
9
  */
6
10
  export declare class ThinkingSelectorComponent extends Container {
7
11
  #private;
8
- constructor(currentLevel: Effort, availableLevels: Effort[], onSelect: (level: Effort) => void, onCancel: () => void);
12
+ constructor(currentLevel: ThinkingLevelValue | undefined, availableLevels: ThinkingLevelValue[], onSelect: (selection: ThinkingSelectorSelection) => void, onCancel: () => void);
13
+ handleInput(data: string): void;
9
14
  getSelectList(): SelectList;
10
15
  }
@@ -26,6 +26,7 @@ export declare class SelectorController {
26
26
  showProviderOnboarding(): void;
27
27
  showCustomModelPresetWizard(snapshot: ModelProfileConfig): void;
28
28
  showCustomProviderWizard(): void;
29
+ showEffortSelector(): void;
29
30
  showSettingsSelector(): void;
30
31
  showThemeSelector(): void;
31
32
  showHistorySearch(): void;
@@ -96,6 +96,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
96
96
  onInputCallback?: (input: SubmittedUserInput) => void;
97
97
  optimisticUserMessageSignature: string | undefined;
98
98
  locallySubmittedUserSignatures: Set<string>;
99
+ optimisticInjectedSignatures: Map<string, number>;
99
100
  lastSigintTime: number;
100
101
  lastEscapeTime: number;
101
102
  lastComposerClearEscapeTime: number;
@@ -218,6 +219,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
218
219
  showModelSelector(options?: {
219
220
  temporaryOnly?: boolean;
220
221
  }): void;
222
+ showEffortSelector(): void;
221
223
  showProviderOnboarding(): void;
222
224
  showPluginSelector(mode?: "install" | "uninstall"): void;
223
225
  showUserMessageSelector(): void;
@@ -64,9 +64,7 @@ export declare function isFastLaneRpcCommand(type: RpcCommand["type"]): boolean;
64
64
  export declare function createRpcCommandScheduler(run: (command: RpcCommand) => Promise<void>, track: (task: Promise<void>) => void): {
65
65
  dispatch: (command: RpcCommand) => void;
66
66
  };
67
- export declare class RpcListenRefusedError extends Error {
68
- constructor(socketPath: string);
69
- }
67
+ export { RpcListenRefusedError } from "./rpc-socket-security";
70
68
  /**
71
69
  * Probe whether a unix-domain socket path has a live server accepting
72
70
  * connections. Returns `true` when a connection succeeds (a previous owner is
@@ -1,6 +1,9 @@
1
1
  export declare class RpcSocketSecurityError extends Error {
2
2
  constructor(message: string);
3
3
  }
4
+ export declare class RpcListenRefusedError extends Error {
5
+ constructor(socketPath: string);
6
+ }
4
7
  export declare function prepareRpcSocketPath(socketPath: string): Promise<void>;
5
8
  export declare function assertSafeClientSocket(socketPath: string): Promise<void>;
6
9
  export declare function verifyRpcSocketAfterListen(socketPath: string): Promise<void>;
@@ -28,6 +28,7 @@ import type { Theme } from "./theme/theme";
28
28
  export type CompactionQueuedMessage = {
29
29
  text: string;
30
30
  mode: "steer" | "followUp";
31
+ followUpQueuePolicy?: "sequential";
31
32
  };
32
33
  export type SubmittedUserInput = {
33
34
  text: string;
@@ -100,6 +101,7 @@ export interface InteractiveModeContext {
100
101
  onInputCallback?: (input: SubmittedUserInput) => void;
101
102
  optimisticUserMessageSignature: string | undefined;
102
103
  locallySubmittedUserSignatures: Set<string>;
104
+ optimisticInjectedSignatures: Map<string, number>;
103
105
  lastSigintTime: number;
104
106
  lastEscapeTime: number;
105
107
  lastComposerClearEscapeTime: number;
@@ -223,6 +225,7 @@ export interface InteractiveModeContext {
223
225
  showModelSelector(options?: {
224
226
  temporaryOnly?: boolean;
225
227
  }): void;
228
+ showEffortSelector(): void;
226
229
  showProviderOnboarding(): void;
227
230
  showPluginSelector(mode?: "install" | "uninstall"): void;
228
231
  showUserMessageSelector(): void;
@@ -0,0 +1,52 @@
1
+ import type { ImageContent, TextContent } from "@gajae-code/ai";
2
+ import type { InteractiveModeContext } from "../types";
3
+ /**
4
+ * Normalize the content passed to an extension `sendUserMessage` call into a
5
+ * plain text string plus its image attachments. Mirrors the normalization in
6
+ * `AgentSession.sendUserMessage` (text parts joined with "\n") so the resulting
7
+ * text matches the eventual user `message_start` payload.
8
+ */
9
+ export declare function normalizeInjectedUserContent(content: string | (TextContent | ImageContent)[]): {
10
+ text: string;
11
+ images: ImageContent[];
12
+ imageCount: number;
13
+ };
14
+ /**
15
+ * Record a remotely/programmatically injected user message (e.g. Telegram
16
+ * inbound routed through the extension API) into the interactive TUI, so it is
17
+ * captured in prompt history and shown immediately instead of only appearing
18
+ * once the eventual `message_start` event lands.
19
+ *
20
+ * Local TUI submissions never reach this path (they go through
21
+ * `session.prompt(...)` / `startPendingSubmission`), so this cannot double-add
22
+ * local prompt history.
23
+ *
24
+ * - Always adds the injected text to editor prompt history.
25
+ * - Idle injections optimistically render the user message and record a pending
26
+ * injected optimistic signature (a counting Map, so multiple idle injections
27
+ * before the first `message_start` do not clobber each other); the later user
28
+ * `message_start` consumes one count and skips both the duplicate chat add and
29
+ * the defensive editor clear (so a locally typed draft is preserved).
30
+ * - Busy/queued injections refresh the pending-message display, which the
31
+ * caller has already populated by invoking `session.sendUserMessage(...)`
32
+ * before this helper.
33
+ *
34
+ * This helper never clears the editor text.
35
+ */
36
+ export declare function applyInjectedUserSubmission(ctx: InteractiveModeContext, input: {
37
+ content: string | (TextContent | ImageContent)[];
38
+ queued: boolean;
39
+ }): void;
40
+ /**
41
+ * Record one pending optimistic render for an injected user message.
42
+ *
43
+ * Injected sends are fire-and-forget and multiple idle injections can be
44
+ * rendered before the first `message_start` arrives, so a counting Map (not a
45
+ * single slot) tracks how many optimistic renders are outstanding per signature.
46
+ */
47
+ export declare function incrementInjectedOptimisticSignature(ctx: InteractiveModeContext, signature: string): void;
48
+ /**
49
+ * Consume one pending injected optimistic render for `signature`. Decrements the
50
+ * count (deleting the key at zero) and returns whether one was outstanding.
51
+ */
52
+ export declare function consumeInjectedOptimisticSignature(ctx: InteractiveModeContext, signature: string): boolean;
@@ -18,9 +18,47 @@ export interface ConfigCommandChange {
18
18
  verbosity?: "lean" | "verbose";
19
19
  redact?: boolean;
20
20
  }
21
+ export type TelegramControlCommandName = "reasoning" | "usage" | "context" | "compact";
22
+ export type TelegramControlCommand = {
23
+ name: "reasoning";
24
+ action: "cycle" | "status" | "set";
25
+ level?: string;
26
+ } | {
27
+ name: "usage";
28
+ } | {
29
+ name: "context";
30
+ } | {
31
+ name: "compact";
32
+ instructions?: string;
33
+ };
34
+ export type TelegramControlCommandParseResult = {
35
+ kind: "none";
36
+ } | {
37
+ kind: "ignored";
38
+ commandName: TelegramControlCommandName;
39
+ } | {
40
+ kind: "command";
41
+ command: TelegramControlCommand;
42
+ } | {
43
+ kind: "invalid";
44
+ commandName: TelegramControlCommandName;
45
+ usage: string;
46
+ };
47
+ export declare function telegramControlCommandUsage(commandName: TelegramControlCommandName): string;
48
+ /** Parse deterministic Telegram session-control commands. Recognised roots fail closed. */
49
+ export declare function parseTelegramControlCommand(text: string, botUsername?: string): TelegramControlCommandParseResult;
21
50
  /**
22
51
  * Parse an in-thread config command. Returns the requested change, or
23
52
  * `undefined` when the text is not a recognised config command (so the daemon
24
53
  * can fall through to treating it as a free-text injection).
25
54
  */
26
55
  export declare function parseInThreadConfigCommand(text: string): ConfigCommandChange | undefined;
56
+ /**
57
+ * Parse a `/rich on|off` toggle. Returns `true`/`false` for a recognised
58
+ * on/off argument, or `undefined` otherwise (not a `/rich` command, or `/rich`
59
+ * with a missing/invalid argument). This is intentionally SEPARATE from
60
+ * `parseInThreadConfigCommand`: `/verbose`/`/redact` are producer/session config
61
+ * forwarded over the WS, whereas rich is Telegram-daemon delivery policy handled
62
+ * daemon-locally, so it never becomes a `config_command` frame or a user turn.
63
+ */
64
+ export declare function parseRichToggleCommand(text: string): boolean | undefined;
@@ -14,6 +14,12 @@ export interface NotificationConfig {
14
14
  redact: boolean;
15
15
  verbosity: "lean" | "verbose";
16
16
  idleTimeoutMs: number;
17
+ rich: {
18
+ enabled: boolean;
19
+ };
20
+ richDraft: {
21
+ enabled: boolean;
22
+ };
17
23
  }
18
24
  /** Read typed config from Settings. */
19
25
  export declare function getNotificationConfig(settings: Settings): NotificationConfig;
@@ -25,6 +31,16 @@ export declare function isTelegramConfigured(cfg: NotificationConfig): cfg is No
25
31
  };
26
32
  /** Is global config sufficient for auto-on (enabled + at least one configured adapter)? */
27
33
  export declare function isGloballyConfigured(cfg: NotificationConfig): boolean;
34
+ /**
35
+ * Per-run opt-out for completion notifications, honored before settings lookups.
36
+ *
37
+ * `GJC_NOTIFY=off` (also `0` / `false`, case-insensitive) suppresses the
38
+ * completion notification surface for this process only. `config.yml` is
39
+ * untouched and child processes inherit the env var, which lets non-interactive
40
+ * fleet runs (`gjc -p --no-session`) stay silent even when a user-level/global
41
+ * completion notification configuration is enabled.
42
+ */
43
+ export declare function completionNotifyDisabledByEnv(env: NodeJS.ProcessEnv): boolean;
28
44
  /** Resolve whether the notifications extension should be registered at SDK startup. */
29
45
  export declare function shouldRegisterNotificationsExtension(input: {
30
46
  env: NodeJS.ProcessEnv;
@@ -18,7 +18,7 @@
18
18
  * generated), or `GJC_NOTIFICATIONS_TOKEN`.
19
19
  */
20
20
  import { Settings } from "../config/settings";
21
- import type { ExtensionAPI } from "../extensibility/extensions";
21
+ import type { ExtensionAPI, ExtensionContext } from "../extensibility/extensions";
22
22
  /** Where a `session_create` should run. Discriminated by `kind`. */
23
23
  export type SessionCreateTarget = {
24
24
  kind: "existing_path";
@@ -61,6 +61,8 @@ export interface SessionCreateFrame {
61
61
  target: SessionCreateTarget;
62
62
  /** Reference to the daemon-written, once-consumed startup-prompt file. */
63
63
  startupPromptRef?: string;
64
+ /** Model profile preset to activate for the spawned session (--mpreset). */
65
+ modelPreset?: string;
64
66
  }
65
67
  /** Close (hard-kill, history preserved) a session. */
66
68
  export interface SessionCloseFrame {
@@ -174,6 +176,17 @@ export interface SessionReadyFrame {
174
176
  */
175
177
  export declare function readGitRepoName(cwd: string): string | undefined;
176
178
  export declare function notificationsEnabled(): boolean;
179
+ interface NotificationControlCommandPayload {
180
+ name?: unknown;
181
+ action?: unknown;
182
+ level?: unknown;
183
+ instructions?: unknown;
184
+ }
185
+ export declare function executeNotificationControlCommand(command: NotificationControlCommandPayload | undefined, ctx: ExtensionContext, api: ExtensionAPI): Promise<{
186
+ status: "ok" | "error" | "unavailable";
187
+ message: string;
188
+ }>;
177
189
  export declare function createNotificationsExtension(api: ExtensionAPI, options?: {
178
190
  settings?: Settings;
179
191
  }): void;
192
+ export {};
@@ -4,6 +4,7 @@ export type LifecycleCommandVerb = "session_create" | "session_close" | "session
4
4
  export type ParsedLifecycleCommand = {
5
5
  kind: "create";
6
6
  target: SessionCreateTarget;
7
+ modelPreset?: string;
7
8
  } | {
8
9
  kind: "close";
9
10
  target: SessionCloseTarget;
@@ -35,7 +35,8 @@ export declare function createRateLimiter(maxPerWindow: number, windowMs: number
35
35
  * The launched session id is carried via `GJC_SESSION_ID` in the child env (see
36
36
  * {@link daemonSpawnCreate}); the root `gjc` launcher has no `--session-id`
37
37
  * flag, so it must never appear in argv. Only flags the launch parser actually
38
- * supports are emitted (`--worktree <branch>` for worktree targets). */
38
+ * supports are emitted (`--worktree <branch>` for worktree targets,
39
+ * `--mpreset <profile>` for model presets). */
39
40
  export declare function buildCreateArgv(frame: SessionCreateFrame, _ids: {
40
41
  intendedSessionId: string;
41
42
  startupPromptRef?: string;