@gajae-code/coding-agent 0.9.1 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/types/capability/mcp.d.ts +3 -1
  3. package/dist/types/cli/args.d.ts +1 -0
  4. package/dist/types/cli/notify-cli.d.ts +5 -0
  5. package/dist/types/cli.d.ts +3 -0
  6. package/dist/types/commands/launch.d.ts +3 -0
  7. package/dist/types/commands/mcp.d.ts +1 -1
  8. package/dist/types/config/model-profile-activation.d.ts +7 -7
  9. package/dist/types/config/model-registry.d.ts +7 -3
  10. package/dist/types/config/model-resolver.d.ts +2 -2
  11. package/dist/types/config/settings-schema.d.ts +1 -1
  12. package/dist/types/config/settings.d.ts +7 -0
  13. package/dist/types/extensibility/runtime-skill-discovery.d.ts +20 -0
  14. package/dist/types/gjc-runtime/team-runtime.d.ts +9 -0
  15. package/dist/types/modes/components/settings-selector.d.ts +2 -2
  16. package/dist/types/modes/components/status-line/types.d.ts +6 -0
  17. package/dist/types/modes/components/status-line.d.ts +1 -0
  18. package/dist/types/modes/components/thinking-selector.d.ts +6 -1
  19. package/dist/types/modes/types.d.ts +1 -0
  20. package/dist/types/modes/utils/ui-helpers.d.ts +12 -0
  21. package/dist/types/notifications/config-commands.d.ts +29 -0
  22. package/dist/types/notifications/index.d.ts +12 -1
  23. package/dist/types/notifications/telegram-daemon-cli.d.ts +3 -3
  24. package/dist/types/notifications/threaded-render.d.ts +2 -0
  25. package/dist/types/notifications/topic-registry.d.ts +4 -5
  26. package/dist/types/runtime-credential-selector.d.ts +7 -0
  27. package/dist/types/runtime-mcp/config-writer.d.ts +4 -3
  28. package/dist/types/runtime-mcp/types.d.ts +5 -4
  29. package/dist/types/sdk.d.ts +7 -1
  30. package/dist/types/session/agent-session.d.ts +22 -3
  31. package/dist/types/session/session-manager.d.ts +1 -0
  32. package/dist/types/tools/index.d.ts +1 -0
  33. package/dist/types/tools/skill-discovery.d.ts +40 -0
  34. package/dist/types/tools/skill.d.ts +9 -8
  35. package/dist/types/utils/linkedom.d.ts +4 -0
  36. package/dist/types/utils/pasted-image-path.d.ts +5 -3
  37. package/package.json +11 -7
  38. package/scripts/build-binary.ts +2 -40
  39. package/scripts/compile-args.ts +91 -0
  40. package/scripts/generate-docs-index.ts +1 -0
  41. package/src/capability/mcp.ts +3 -1
  42. package/src/cli/args.ts +8 -0
  43. package/src/cli/mcp-cli.ts +108 -26
  44. package/src/cli/notify-cli.ts +27 -1
  45. package/src/cli.ts +26 -0
  46. package/src/commands/launch.ts +5 -0
  47. package/src/commands/mcp.ts +8 -8
  48. package/src/commands/notify.ts +2 -2
  49. package/src/commands/stats.ts +6 -2
  50. package/src/config/mcp-schema.json +21 -17
  51. package/src/config/model-profile-activation.ts +23 -7
  52. package/src/config/model-registry.ts +21 -4
  53. package/src/config/model-resolver.ts +5 -16
  54. package/src/config/settings-schema.ts +3 -2
  55. package/src/config/settings.ts +24 -1
  56. package/src/coordinator-mcp/server.ts +7 -4
  57. package/src/discovery/builtin.ts +14 -0
  58. package/src/discovery/mcp-json.ts +14 -0
  59. package/src/extensibility/runtime-skill-discovery.ts +229 -0
  60. package/src/gjc-runtime/launch-tmux.ts +72 -12
  61. package/src/gjc-runtime/psmux-detect.ts +17 -2
  62. package/src/gjc-runtime/state-runtime.ts +105 -12
  63. package/src/gjc-runtime/team-runtime.ts +35 -3
  64. package/src/gjc-runtime/workflow-manifest.generated.json +4 -28
  65. package/src/gjc-runtime/workflow-manifest.ts +1 -7
  66. package/src/internal-urls/docs-index.generated.ts +3 -2
  67. package/src/internal-urls/gjc-protocol.ts +14 -1
  68. package/src/main.ts +26 -3
  69. package/src/modes/components/assistant-message.ts +1 -1
  70. package/src/modes/components/settings-selector.ts +3 -3
  71. package/src/modes/components/status-line/presets.ts +5 -7
  72. package/src/modes/components/status-line/segments.ts +14 -0
  73. package/src/modes/components/status-line/types.ts +6 -0
  74. package/src/modes/components/status-line.ts +7 -1
  75. package/src/modes/components/thinking-selector.ts +79 -18
  76. package/src/modes/components/tool-execution.ts +1 -1
  77. package/src/modes/controllers/event-controller.ts +9 -9
  78. package/src/modes/controllers/input-controller.ts +52 -11
  79. package/src/modes/controllers/selector-controller.ts +7 -5
  80. package/src/modes/interactive-mode.ts +12 -4
  81. package/src/modes/types.ts +1 -0
  82. package/src/modes/utils/ui-helpers.ts +202 -47
  83. package/src/notifications/config-commands.ts +70 -0
  84. package/src/notifications/index.ts +156 -9
  85. package/src/notifications/rich-render.ts +1 -0
  86. package/src/notifications/telegram-daemon-cli.ts +13 -5
  87. package/src/notifications/telegram-daemon.ts +87 -4
  88. package/src/notifications/threaded-render.ts +14 -0
  89. package/src/notifications/topic-registry.ts +9 -8
  90. package/src/prompts/tools/skill-discovery.md +13 -0
  91. package/src/prompts/tools/skill.md +2 -2
  92. package/src/runtime-credential-selector.ts +35 -0
  93. package/src/runtime-mcp/config-writer.ts +4 -3
  94. package/src/runtime-mcp/config.ts +1 -0
  95. package/src/runtime-mcp/types.ts +5 -4
  96. package/src/sdk.ts +68 -5
  97. package/src/session/agent-session.ts +159 -21
  98. package/src/session/session-manager.ts +68 -40
  99. package/src/system-prompt.ts +4 -6
  100. package/src/tools/bash-interactive.ts +9 -4
  101. package/src/tools/browser/readable.ts +2 -2
  102. package/src/tools/fetch.ts +5 -5
  103. package/src/tools/index.ts +4 -0
  104. package/src/tools/skill-discovery.ts +73 -0
  105. package/src/tools/skill.ts +29 -13
  106. package/src/utils/linkedom.ts +14 -0
  107. package/src/utils/pasted-image-path.ts +23 -9
  108. package/src/web/scrapers/arxiv.ts +2 -2
  109. package/src/web/scrapers/go-pkg.ts +2 -2
  110. package/src/web/scrapers/iacr.ts +2 -2
  111. package/src/web/scrapers/readthedocs.ts +2 -2
  112. package/src/web/scrapers/twitter.ts +3 -2
  113. package/src/web/scrapers/wikipedia.ts +2 -2
  114. package/src/export/html/template.css +0 -1060
  115. package/src/export/html/template.html +0 -47
  116. package/src/export/html/template.js +0 -2314
  117. package/vendor/insane-search/engine/tests/test_hardening.py +0 -57
  118. package/vendor/insane-search/engine/tests/test_smoke.py +0 -152
  119. package/vendor/insane-search/engine/tests/test_u1.py +0 -200
  120. package/vendor/insane-search/engine/tests/test_u4.py +0 -131
  121. package/vendor/insane-search/engine/tests/test_u5.py +0 -163
  122. package/vendor/insane-search/engine/tests/test_u7.py +0 -124
  123. package/vendor/insane-search/engine/tests/test_u8.py +0 -216
@@ -156,6 +156,10 @@ export interface AsyncJobSnapshot {
156
156
  recent: AsyncJobSnapshotItem[];
157
157
  delivery: AsyncJobDeliveryState;
158
158
  }
159
+ export interface RetainedMemorySample {
160
+ tuiChatChildren?: number;
161
+ tuiCachedRenderBytes?: number;
162
+ }
159
163
  export interface AgentSessionConfig {
160
164
  agent: Agent;
161
165
  sessionManager: SessionManager;
@@ -237,6 +241,8 @@ export interface AgentSessionConfig {
237
241
  * **MUST NOT** dispose it on their own teardown.
238
242
  */
239
243
  ownedAsyncJobManager?: AsyncJobManager;
244
+ /** Cheap TUI retained-memory counters; absent for headless sessions. */
245
+ retainedMemorySampler?: () => RetainedMemorySample;
240
246
  /**
241
247
  * MCPManager whose lifecycle this session owns (top-level sessions that
242
248
  * connected plugin-bundle MCP servers). Only the owned manager is
@@ -270,6 +276,8 @@ export interface PromptOptions {
270
276
  images?: ImageContent[];
271
277
  /** When streaming, how to queue the message: "steer" (interrupt) or "followUp" (wait). */
272
278
  streamingBehavior?: "steer" | "followUp";
279
+ /** When set to "sequential", this follow-up is delivered one prompt at a time even if followUpMode is "all". */
280
+ followUpQueuePolicy?: "respect-mode" | "sequential";
273
281
  /** Optional tool choice override for the next LLM call. */
274
282
  toolChoice?: ToolChoice;
275
283
  /** Send as developer/system message instead of user. Providers that support it use the developer role; others fall back to user. */
@@ -366,6 +374,15 @@ export declare const __agentSessionPerfCounters: {
366
374
  reset(): void;
367
375
  };
368
376
  export declare function buildContextInjectionSignature(kind: string, parts: readonly string[]): string;
377
+ export declare class StreamingEditFileCache {
378
+ #private;
379
+ get(path: string): string | undefined;
380
+ set(path: string, content: string): void;
381
+ delete(path: string): void;
382
+ clear(): void;
383
+ has(path: string): boolean;
384
+ get totalBytes(): number;
385
+ }
369
386
  export declare class AgentSession {
370
387
  #private;
371
388
  readonly agent: Agent;
@@ -657,7 +674,7 @@ export declare class AgentSession {
657
674
  * @throws Error if no model selected or no API key available (when not streaming)
658
675
  */
659
676
  prompt(text: string, options?: PromptOptions): Promise<void>;
660
- promptCustomMessage<T = unknown>(message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">, options?: Pick<PromptOptions, "streamingBehavior" | "toolChoice">): Promise<void>;
677
+ promptCustomMessage<T = unknown>(message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">, options?: Pick<PromptOptions, "streamingBehavior" | "toolChoice" | "followUpQueuePolicy">): Promise<void>;
661
678
  /**
662
679
  * Queue a steering message to interrupt the agent mid-run.
663
680
  */
@@ -665,7 +682,7 @@ export declare class AgentSession {
665
682
  /**
666
683
  * Queue a follow-up message to process after the agent would otherwise stop.
667
684
  */
668
- followUp(text: string, images?: ImageContent[]): Promise<void>;
685
+ followUp(text: string, images?: ImageContent[], options?: Pick<PromptOptions, "followUpQueuePolicy">): Promise<void>;
669
686
  queueDeferredMessage(message: CustomMessage): void;
670
687
  queueDeferredMessageForTests(message: CustomMessage, triggerTurn?: boolean): void;
671
688
  /**
@@ -679,6 +696,7 @@ export declare class AgentSession {
679
696
  sendCustomMessage<T = unknown>(message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">, options?: {
680
697
  triggerTurn?: boolean;
681
698
  deliverAs?: "steer" | "followUp" | "nextTurn";
699
+ followUpQueuePolicy?: "respect-mode" | "sequential";
682
700
  }): Promise<void>;
683
701
  /** Remove undelivered queued custom messages matching `predicate` from executable queues and tagged display mirrors. */
684
702
  purgeQueuedCustomMessages(predicate: (message: CustomMessage) => boolean): PurgeQueuedCustomMessagesResult;
@@ -834,7 +852,7 @@ export declare class AgentSession {
834
852
  getAvailableModels(): Model[];
835
853
  /**
836
854
  * Set thinking level.
837
- * Saves the effective metadata-clamped level to session and settings only if it changes.
855
+ * Saves the effective metadata-clamped level to the session, and to settings when requested.
838
856
  */
839
857
  setThinkingLevel(level: ThinkingLevel | undefined, persist?: boolean): void;
840
858
  /**
@@ -932,6 +950,7 @@ export declare class AgentSession {
932
950
  prepareContributionPrep(options?: ContributionPrepOptions): Promise<ContributionPrepResult>;
933
951
  /** Test seam: override the emergency-compaction resource sampler so tests never read real RSS. */
934
952
  setResourceSampler(sampler: () => EmergencyCompactionSample): void;
953
+ setRetainedMemorySampler(sampler: (() => RetainedMemorySample) | undefined): void;
935
954
  /**
936
955
  * Toggle auto-compaction setting.
937
956
  */
@@ -535,6 +535,7 @@ export declare class SessionManager {
535
535
  getInjectedTtsrRules(): string[];
536
536
  getLeafId(): string | null;
537
537
  getLeafEntry(): SessionEntry | undefined;
538
+ getResidentImageBytes(): number;
538
539
  /**
539
540
  * Get the most recent model role from the current session path.
540
541
  * Returns undefined if no model change has been recorded.
@@ -53,6 +53,7 @@ export * from "./review";
53
53
  export * from "./search";
54
54
  export * from "./search-tool-bm25";
55
55
  export * from "./skill";
56
+ export * from "./skill-discovery";
56
57
  export * from "./ssh";
57
58
  export * from "./subagent";
58
59
  export * from "./telegram-send";
@@ -0,0 +1,40 @@
1
+ import type { AgentTool, AgentToolResult } from "@gajae-code/agent-core";
2
+ import * as z from "zod/v4";
3
+ import { type RuntimeSkillDiscoveryCandidate } from "../extensibility/runtime-skill-discovery";
4
+ import type { ToolSession } from ".";
5
+ declare const skillDiscoverySchema: z.ZodObject<{
6
+ query: z.ZodOptional<z.ZodString>;
7
+ source: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
8
+ all: "all";
9
+ project: "project";
10
+ user: "user";
11
+ }>>>;
12
+ limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
13
+ }, z.core.$strict>;
14
+ export type SkillDiscoveryToolInput = z.infer<typeof skillDiscoverySchema>;
15
+ export interface SkillDiscoveryToolDetails {
16
+ candidates: RuntimeSkillDiscoveryCandidate[];
17
+ count: number;
18
+ }
19
+ export declare class SkillDiscoveryTool implements AgentTool<typeof skillDiscoverySchema, SkillDiscoveryToolDetails> {
20
+ #private;
21
+ readonly name = "skill_discovery";
22
+ readonly label = "SkillDiscovery";
23
+ readonly summary = "Discover project and user runtime skills by thin metadata";
24
+ readonly loadMode = "essential";
25
+ readonly description: string;
26
+ readonly parameters: z.ZodObject<{
27
+ query: z.ZodOptional<z.ZodString>;
28
+ source: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
29
+ all: "all";
30
+ project: "project";
31
+ user: "user";
32
+ }>>>;
33
+ limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
34
+ }, z.core.$strict>;
35
+ readonly strict = true;
36
+ constructor(session: ToolSession);
37
+ static createIf(session: ToolSession): SkillDiscoveryTool | null;
38
+ execute(_toolCallId: string, input: SkillDiscoveryToolInput, signal?: AbortSignal): Promise<AgentToolResult<SkillDiscoveryToolDetails>>;
39
+ }
40
+ export {};
@@ -4,15 +4,16 @@
4
4
  * Lets the agent hand off to another available skill in the current turn. The
5
5
  * callee's SKILL.md is dispatched through the same custom-message path used by
6
6
  * `/skill:<name>` typing, as a user-attribution message delivered same-turn
7
- * (without `deliverAs: "nextTurn"`). Before dispatch, the tool calls
8
- * `gjc state <caller> handoff --to <callee>` in-process via the state-runtime
9
- * function so caller and callee mode-states plus `skill-active-state.json`
10
- * transition atomically.
7
+ * (without `deliverAs: "nextTurn"`). When the caller is one of the canonical
8
+ * workflow skills, the tool first runs the native state handoff so workflow
9
+ * mode-state and `skill-active-state.json` transition atomically. Runtime
10
+ * project/user skills have no native workflow mode-state, so they are dispatched
11
+ * directly and tracked by the prompt observer instead.
11
12
  *
12
- * Chaining is refused unless the caller's `current_phase` is in
13
- * `{complete, completed, handoff, failed, cancelled, canceled, inactive}`. The
14
- * agent declares readiness either by writing `current_phase: "handoff"` to its
15
- * mode-state or by running the handoff verb directly.
13
+ * Canonical workflow chaining is refused unless the caller's `current_phase` is
14
+ * in `{complete, completed, handoff, failed, cancelled, canceled, inactive}`.
15
+ * The agent declares readiness either by writing `current_phase: "handoff"` to
16
+ * its mode-state or by running the handoff verb directly.
16
17
  */
17
18
  import type { AgentTool, AgentToolResult } from "@gajae-code/agent-core";
18
19
  import * as z from "zod/v4";
@@ -0,0 +1,4 @@
1
+ type LinkedomModule = typeof import("linkedom");
2
+ type ParseHtml = LinkedomModule["parseHTML"];
3
+ export declare function parseHtmlLazy(markup: string): Promise<ReturnType<ParseHtml>>;
4
+ export {};
@@ -23,8 +23,10 @@ export interface ResolvePastedImagePathOptions extends DecodePastedPathOptions {
23
23
  */
24
24
  export declare function decodePastedPathCandidate(text: string, options?: DecodePastedPathOptions): string | undefined;
25
25
  /**
26
- * Returns the resolved path when the whole pasted text is a single path to an
27
- * existing image file (verified by extension AND content signature),
28
- * otherwise `undefined` (the paste is inserted as text).
26
+ * Returns the resolved path when the whole pasted text is a recognized clipboard
27
+ * temp path to an existing image file, otherwise `undefined` (the paste is
28
+ * inserted as text). Arbitrary saved image paths are not auto-attached here;
29
+ * users attach them explicitly with `@path/to/image.png`.
29
30
  */
30
31
  export declare function resolvePastedImagePath(text: string, options?: ResolvePastedImagePathOptions): string | undefined;
32
+ export declare function formatPastedImageReference(placeholder: string, imagePath: string): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/coding-agent",
4
- "version": "0.9.1",
4
+ "version": "0.9.3",
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",
@@ -49,12 +49,12 @@
49
49
  "@agentclientprotocol/sdk": "0.21.0",
50
50
  "@babel/parser": "^7.29.3",
51
51
  "@mozilla/readability": "^0.6.0",
52
- "@gajae-code/stats": "0.9.1",
53
- "@gajae-code/agent-core": "0.9.1",
54
- "@gajae-code/ai": "0.9.1",
55
- "@gajae-code/natives": "0.9.1",
56
- "@gajae-code/tui": "0.9.1",
57
- "@gajae-code/utils": "0.9.1",
52
+ "@gajae-code/stats": "0.9.3",
53
+ "@gajae-code/agent-core": "0.9.3",
54
+ "@gajae-code/ai": "0.9.3",
55
+ "@gajae-code/natives": "0.9.3",
56
+ "@gajae-code/tui": "0.9.3",
57
+ "@gajae-code/utils": "0.9.3",
58
58
  "@puppeteer/browsers": "^2.13.0",
59
59
  "@types/turndown": "5.0.6",
60
60
  "@xterm/headless": "^6.0.0",
@@ -85,6 +85,10 @@
85
85
  "README.md",
86
86
  "CHANGELOG.md",
87
87
  "vendor",
88
+ "!vendor/insane-search/engine/tests/**",
89
+ "!src/export/html/template.html",
90
+ "!src/export/html/template.css",
91
+ "!src/export/html/template.js",
88
92
  "dist/types"
89
93
  ],
90
94
  "exports": {
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
3
  import * as path from "node:path";
4
+ import { buildDevCompileArgs } from "./compile-args";
4
5
 
5
6
  const packageDir = path.join(import.meta.dir, "..");
6
7
  const outputPath = path.join(packageDir, "dist", "gjc");
@@ -34,46 +35,7 @@ async function main(): Promise<void> {
34
35
  await runCommand(["bun", "--cwd=../natives", "run", "embed:native"]);
35
36
  try {
36
37
  const buildEnv = shouldAdhocSignDarwinBinary() ? { ...Bun.env, BUN_NO_CODESIGN_MACHO_BINARY: "1" } : Bun.env;
37
- await runCommand(
38
- [
39
- "bun",
40
- "build",
41
- "--compile",
42
- // Minify shrinks the bundled JS the compiled binary must parse at
43
- // startup (302MB → ~114MB --help RSS measured on darwin-arm64).
44
- // --keep-names below preserves identifiers for error reports.
45
- "--minify",
46
- "--no-compile-autoload-bunfig",
47
- "--no-compile-autoload-dotenv",
48
- "--no-compile-autoload-tsconfig",
49
- "--no-compile-autoload-package-json",
50
- "--keep-names",
51
- "--define",
52
- 'process.env.PI_COMPILED="true"',
53
- "--external",
54
- "mupdf",
55
- "--root",
56
- "../..",
57
- "./src/cli.ts",
58
- // Worker entrypoints. Bun's `--compile` discovers the literal in
59
- // `new Worker("…", …)` at each spawn site, but only actually
60
- // emits the worker into the bunfs root when it is listed here as
61
- // an explicit additional entry. Paths are relative to this
62
- // script's cwd (packages/coding-agent) and the `--root` above
63
- // (../..) makes them appear inside the binary at
64
- // `/$bunfs/root/packages/<pkg>/src/<worker>.js`, which is
65
- // exactly what the literals at the spawn sites resolve to.
66
- "../stats/src/sync-worker.ts",
67
- "./src/tools/browser/tab-worker-entry.ts",
68
- "./src/eval/js/worker-entry.ts",
69
- // Hidden notify daemon CLI (loaded via dynamic import from notify-cli); list it
70
- // explicitly so Bun standalone keeps `gjc notify daemon-internal` usable.
71
- "./src/notifications/telegram-daemon-cli.ts",
72
- "--outfile",
73
- "dist/gjc",
74
- ],
75
- buildEnv,
76
- );
38
+ await runCommand(buildDevCompileArgs(), buildEnv);
77
39
 
78
40
  await stageWorkspaceNativeAddons();
79
41
  // Bun 1.3.12 emits a truncated Mach-O signature on darwin builds.
@@ -0,0 +1,91 @@
1
+ export interface CompileArgOptions {
2
+ root: string;
3
+ entrypoints: string[];
4
+ outfile: string;
5
+ target?: string;
6
+ defines?: string[];
7
+ externals?: string[];
8
+ }
9
+
10
+ export const compileAutoloadDisableFlags = [
11
+ "--no-compile-autoload-bunfig",
12
+ "--no-compile-autoload-dotenv",
13
+ "--no-compile-autoload-tsconfig",
14
+ "--no-compile-autoload-package-json",
15
+ ];
16
+
17
+ export const compiledDefineFlags = ['process.env.PI_COMPILED="true"'];
18
+ export const compiledExternalPackages = ["mupdf"];
19
+
20
+ export const releaseEntrypoints = [
21
+ "./packages/coding-agent/src/cli.ts",
22
+ "./packages/stats/src/sync-worker.ts",
23
+ "./packages/coding-agent/src/tools/browser/tab-worker-entry.ts",
24
+ "./packages/coding-agent/src/eval/js/worker-entry.ts",
25
+ "./packages/natives/native/index.js",
26
+ "./packages/coding-agent/src/notifications/telegram-daemon-cli.ts",
27
+ "./packages/ai/src/models.json",
28
+ "./node_modules/handlebars/lib/index.js",
29
+ ];
30
+
31
+ export const devEntrypoints = [
32
+ "./src/cli.ts",
33
+ "../stats/src/sync-worker.ts",
34
+ "./src/tools/browser/tab-worker-entry.ts",
35
+ "./src/eval/js/worker-entry.ts",
36
+ "./src/notifications/telegram-daemon-cli.ts",
37
+ "../ai/src/models.json",
38
+ "../../node_modules/handlebars/lib/index.js",
39
+ ];
40
+
41
+ export function buildReleaseCompileArgs(target: string, outfile: string): string[] {
42
+ return buildCompileArgs({
43
+ root: ".",
44
+ entrypoints: releaseEntrypoints,
45
+ outfile,
46
+ target,
47
+ defines: compiledDefineFlags,
48
+ externals: compiledExternalPackages,
49
+ });
50
+ }
51
+
52
+ export function buildDevCompileArgs(outfile = "dist/gjc"): string[] {
53
+ return buildCompileArgs({
54
+ root: "../..",
55
+ entrypoints: devEntrypoints,
56
+ outfile,
57
+ defines: compiledDefineFlags,
58
+ externals: compiledExternalPackages,
59
+ });
60
+ }
61
+
62
+ export function buildCompileArgs(options: CompileArgOptions): string[] {
63
+ const args = [
64
+ "bun",
65
+ "build",
66
+ "--compile",
67
+ // Minify shrinks the bundled JS the compiled binary must parse at
68
+ // startup (302MB → ~114MB --help RSS measured on darwin-arm64).
69
+ // --keep-names below preserves identifiers for error reports.
70
+ "--minify",
71
+ ...compileAutoloadDisableFlags,
72
+ "--keep-names",
73
+ ];
74
+
75
+ for (const define of options.defines ?? []) {
76
+ args.push("--define", define);
77
+ }
78
+
79
+ args.push("--root", options.root);
80
+
81
+ for (const external of options.externals ?? []) {
82
+ args.push("--external", external);
83
+ }
84
+
85
+ if (options.target) {
86
+ args.push("--target", options.target);
87
+ }
88
+
89
+ args.push(...options.entrypoints, "--outfile", options.outfile);
90
+ return args;
91
+ }
@@ -58,6 +58,7 @@ const mapEntries = docsWithContent
58
58
  .join("\n");
59
59
  const output = [
60
60
  "// Auto-generated by scripts/generate-docs-index.ts - DO NOT EDIT",
61
+ `Reflect.set(globalThis, Symbol.for("gjc.docs-index.generated.loaded"), true);`,
61
62
  "",
62
63
  `export const EMBEDDED_DOC_FILENAMES: readonly string[] = ${filenamesLiteral};`,
63
64
  "",
@@ -15,7 +15,7 @@ export interface MCPServer {
15
15
  name: string;
16
16
  /** Whether this server is enabled (default: true) */
17
17
  enabled?: boolean;
18
- /** Whether to connect automatically at session startup (default: true) */
18
+ /** Whether explicit runtime MCP consumers should connect automatically (default: true) */
19
19
  autoload?: boolean;
20
20
  /** Connection timeout in milliseconds */
21
21
  timeout?: number;
@@ -25,6 +25,8 @@ export interface MCPServer {
25
25
  args?: string[];
26
26
  /** Environment variables */
27
27
  env?: Record<string, string>;
28
+ /** Whether explicit stdio runtime consumers should avoid inheriting host environment */
29
+ noInheritEnv?: boolean;
28
30
  /** Working directory for stdio transport */
29
31
  cwd?: string;
30
32
  /** URL (for HTTP/SSE transport) */
package/src/cli/args.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { type Effort, THINKING_EFFORTS } from "@gajae-code/ai";
5
5
  import { APP_NAME, CONFIG_DIR_NAME, logger } from "@gajae-code/utils";
6
+ import { CliParseError } from "@gajae-code/utils/cli";
6
7
  import chalk from "chalk";
7
8
  import { parseEffort } from "../thinking";
8
9
  import { BUILTIN_TOOLS } from "../tools";
@@ -20,6 +21,7 @@ export interface Args {
20
21
  mpreset?: string;
21
22
  default?: boolean;
22
23
  apiKey?: string;
24
+ credential?: string;
23
25
  systemPrompt?: string;
24
26
  appendSystemPrompt?: string;
25
27
  thinking?: Effort;
@@ -136,6 +138,12 @@ export function parseArgs(args: string[]): Args {
136
138
  result.default = true;
137
139
  } else if (arg === "--api-key" && i + 1 < args.length) {
138
140
  result.apiKey = args[++i];
141
+ } else if (arg === "--credential") {
142
+ const next = args[i + 1];
143
+ if (!next || next.startsWith("-")) {
144
+ throw new CliParseError("--credential requires <selector>");
145
+ }
146
+ result.credential = args[++i];
139
147
  } else if (arg === "--system-prompt" && i + 1 < args.length) {
140
148
  result.systemPrompt = args[++i];
141
149
  } else if (arg === "--append-system-prompt" && i + 1 < args.length) {
@@ -37,14 +37,27 @@ interface ScopedPath {
37
37
  path: string;
38
38
  }
39
39
 
40
+ interface RuntimeDisclosure {
41
+ runtimeStatus: "storage-only";
42
+ runtimeLoadedByStandalone: false;
43
+ runtimeNote: string;
44
+ }
45
+
40
46
  interface RedactedServerEntry {
41
47
  name: string;
42
48
  config: MCPServerConfig;
43
49
  }
44
50
 
51
+ interface RuntimeRedactedServerEntry extends RedactedServerEntry, RuntimeDisclosure {}
52
+
45
53
  const REDACTED = "<redacted>";
46
54
  const SENSITIVE_KEY_PATTERN =
47
55
  /(?:token|secret|key|credential|password|passwd|pwd|authorization|auth|bearer|cookie|session)/i;
56
+ const STORAGE_ONLY_RUNTIME_DISCLOSURE: RuntimeDisclosure = {
57
+ runtimeStatus: "storage-only",
58
+ runtimeLoadedByStandalone: false,
59
+ runtimeNote: "Stored MCP registrations are not loaded by normal standalone gjc sessions today.",
60
+ };
48
61
 
49
62
  function resolvePath(args: MCPCommandArgs): ScopedPath {
50
63
  const scope = args.flags.project ? "project" : "user";
@@ -128,6 +141,50 @@ function redactRecord(
128
141
  );
129
142
  }
130
143
 
144
+ function redactUrl(value: string | undefined): string | undefined {
145
+ if (!value) return value;
146
+ try {
147
+ const url = new URL(value);
148
+ if (url.username) url.username = REDACTED;
149
+ if (url.password) url.password = REDACTED;
150
+ if (url.search) {
151
+ for (const key of Array.from(url.searchParams.keys())) {
152
+ url.searchParams.set(key, REDACTED);
153
+ }
154
+ }
155
+ url.hash = "";
156
+ return url.toString();
157
+ } catch {
158
+ return SENSITIVE_KEY_PATTERN.test(value) ? REDACTED : value;
159
+ }
160
+ }
161
+
162
+ function redactArgs(args: string[] | undefined): string[] | undefined {
163
+ if (!args) return undefined;
164
+ const redacted: string[] = [];
165
+ let redactNext = false;
166
+ for (const arg of args) {
167
+ if (redactNext) {
168
+ redacted.push(REDACTED);
169
+ redactNext = false;
170
+ continue;
171
+ }
172
+ const equalsIndex = arg.indexOf("=");
173
+ if (equalsIndex > 0) {
174
+ const key = arg.slice(0, equalsIndex);
175
+ redacted.push(SENSITIVE_KEY_PATTERN.test(key) ? `${key}=${REDACTED}` : arg);
176
+ continue;
177
+ }
178
+ if (arg.startsWith("-") && SENSITIVE_KEY_PATTERN.test(arg)) {
179
+ redacted.push(arg);
180
+ redactNext = true;
181
+ continue;
182
+ }
183
+ redacted.push(SENSITIVE_KEY_PATTERN.test(arg) ? REDACTED : arg);
184
+ }
185
+ return redacted;
186
+ }
187
+
131
188
  export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig {
132
189
  const redacted = { ...config } as MCPServerConfig;
133
190
  if ("env" in redacted) {
@@ -138,11 +195,19 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
138
195
  const headers = redactRecord(redacted.headers, true);
139
196
  if (headers) redacted.headers = headers;
140
197
  }
198
+ if ("url" in redacted) {
199
+ const url = redactUrl(redacted.url);
200
+ if (url) redacted.url = url;
201
+ }
202
+ if ("args" in redacted) {
203
+ const args = redactArgs(redacted.args);
204
+ if (args) redacted.args = args;
205
+ }
141
206
  if (redacted.auth) {
142
207
  redacted.auth = {
143
208
  type: redacted.auth.type,
144
209
  credentialId: redacted.auth.credentialId ? REDACTED : undefined,
145
- tokenUrl: redacted.auth.tokenUrl,
210
+ tokenUrl: redactUrl(redacted.auth.tokenUrl),
146
211
  clientId: redacted.auth.clientId ? REDACTED : undefined,
147
212
  clientSecret: redacted.auth.clientSecret ? REDACTED : undefined,
148
213
  };
@@ -151,7 +216,7 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
151
216
  redacted.oauth = {
152
217
  clientId: redacted.oauth.clientId ? REDACTED : undefined,
153
218
  clientSecret: redacted.oauth.clientSecret ? REDACTED : undefined,
154
- redirectUri: redacted.oauth.redirectUri,
219
+ redirectUri: redactUrl(redacted.oauth.redirectUri),
155
220
  callbackPort: redacted.oauth.callbackPort,
156
221
  callbackPath: redacted.oauth.callbackPath,
157
222
  };
@@ -159,10 +224,14 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
159
224
  return redacted;
160
225
  }
161
226
 
162
- function collectEntries(config: MCPConfigFile): RedactedServerEntry[] {
227
+ function withRuntimeDisclosure<T extends object>(value: T): T & RuntimeDisclosure {
228
+ return { ...value, ...STORAGE_ONLY_RUNTIME_DISCLOSURE };
229
+ }
230
+
231
+ function collectEntries(config: MCPConfigFile): RuntimeRedactedServerEntry[] {
163
232
  return Object.entries(config.mcpServers ?? {})
164
233
  .sort(([a], [b]) => a.localeCompare(b))
165
- .map(([name, serverConfig]) => ({ name, config: redactMCPServerConfig(serverConfig) }));
234
+ .map(([name, serverConfig]) => withRuntimeDisclosure({ name, config: redactMCPServerConfig(serverConfig) }));
166
235
  }
167
236
 
168
237
  function writeJson(value: unknown): void {
@@ -188,37 +257,46 @@ async function runAdd(args: MCPCommandArgs, scoped: ScopedPath): Promise<void> {
188
257
  const result = await upsertMCPServer(scoped.path, args.name, config, { force: args.flags.force });
189
258
  const redacted = redactMCPServerConfig(config);
190
259
  if (args.flags.json) {
191
- writeJson({
192
- action: "add",
193
- status: result.status,
194
- name: args.name,
195
- scope: scoped.scope,
196
- path: scoped.path,
197
- config: redacted,
198
- });
260
+ writeJson(
261
+ withRuntimeDisclosure({
262
+ action: "add",
263
+ status: result.status,
264
+ name: args.name,
265
+ scope: scoped.scope,
266
+ path: scoped.path,
267
+ config: redacted,
268
+ }),
269
+ );
199
270
  return;
200
271
  }
201
272
  if (result.status === "skipped") {
202
273
  process.stdout.write(
203
- `MCP server "${args.name}" already exists in ${scoped.scope} config. Pass --force to overwrite.\n`,
274
+ `MCP server "${args.name}" already exists in ${scoped.scope} config. Pass --force to overwrite. ` +
275
+ "Status: storage-only; normal standalone gjc sessions do not load stored MCP registrations today.\n",
204
276
  );
205
277
  return;
206
278
  }
207
- process.stdout.write(`MCP server "${args.name}" ${result.status} in ${scoped.scope} config: ${scoped.path}\n`);
279
+ process.stdout.write(
280
+ `MCP server "${args.name}" ${result.status} in ${scoped.scope} config: ${scoped.path}\nStatus: storage-only; normal standalone gjc sessions do not load stored MCP registrations today.\n`,
281
+ );
208
282
  }
209
283
 
210
284
  async function runList(args: MCPCommandArgs, scoped: ScopedPath): Promise<void> {
211
285
  const config = await readMCPConfigFile(scoped.path);
212
286
  const entries = collectEntries(config);
213
287
  if (args.flags.json) {
214
- writeJson({ action: "list", scope: scoped.scope, path: scoped.path, servers: entries });
288
+ writeJson(withRuntimeDisclosure({ action: "list", scope: scoped.scope, path: scoped.path, servers: entries }));
215
289
  return;
216
290
  }
217
291
  if (entries.length === 0) {
218
- process.stdout.write(`No MCP servers registered in ${scoped.scope} config: ${scoped.path}\n`);
292
+ process.stdout.write(
293
+ `No MCP servers registered in ${scoped.scope} config: ${scoped.path}\nStatus: storage-only; normal standalone gjc sessions do not load stored MCP registrations today.\n`,
294
+ );
219
295
  return;
220
296
  }
221
- process.stdout.write(`MCP servers in ${scoped.scope} config: ${scoped.path}\n`);
297
+ process.stdout.write(
298
+ `MCP servers in ${scoped.scope} config: ${scoped.path}\nStatus: storage-only; normal standalone gjc sessions do not load stored MCP registrations today.\n`,
299
+ );
222
300
  for (const entry of entries) {
223
301
  process.stdout.write(`${renderDetails(entry)}\n`);
224
302
  }
@@ -233,17 +311,21 @@ async function runRemove(args: MCPCommandArgs, scoped: ScopedPath): Promise<void
233
311
  await removeMCPServer(scoped.path, args.name);
234
312
  const entry = { name: args.name, config: redactMCPServerConfig(existing) };
235
313
  if (args.flags.json) {
236
- writeJson({
237
- action: "remove",
238
- status: "removed",
239
- name: args.name,
240
- scope: scoped.scope,
241
- path: scoped.path,
242
- removed: entry,
243
- });
314
+ writeJson(
315
+ withRuntimeDisclosure({
316
+ action: "remove",
317
+ status: "removed",
318
+ name: args.name,
319
+ scope: scoped.scope,
320
+ path: scoped.path,
321
+ removed: entry,
322
+ }),
323
+ );
244
324
  return;
245
325
  }
246
- process.stdout.write(`Removed MCP server "${args.name}" from ${scoped.scope} config: ${scoped.path}\n`);
326
+ process.stdout.write(
327
+ `Removed MCP server "${args.name}" from ${scoped.scope} config: ${scoped.path}\nStatus: storage-only; normal standalone gjc sessions do not load stored MCP registrations today.\n`,
328
+ );
247
329
  process.stdout.write(`${renderDetails(entry)}\n`);
248
330
  }
249
331