@gajae-code/coding-agent 0.11.8 → 0.11.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/dist/types/cli.d.ts +1 -0
  3. package/dist/types/config/model-profiles.d.ts +2 -3
  4. package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +12 -5
  5. package/dist/types/gjc-runtime/deep-interview-draft.d.ts +8 -0
  6. package/dist/types/gjc-runtime/deep-interview-payload.d.ts +41 -0
  7. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +32 -17
  8. package/dist/types/gjc-runtime/deep-interview-repair.d.ts +17 -0
  9. package/dist/types/gjc-runtime/deep-interview-state.d.ts +135 -4
  10. package/dist/types/gjc-runtime/state-writer.d.ts +44 -0
  11. package/dist/types/gjc-runtime/workflow-manifest.d.ts +5 -3
  12. package/dist/types/modes/components/welcome.d.ts +2 -0
  13. package/dist/types/runtime-mcp/redaction.d.ts +2 -0
  14. package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -1
  15. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
  16. package/dist/types/session/internal/managed-session-scope.d.ts +12 -2
  17. package/dist/types/session/internal/managed-session-storage.d.ts +8 -3
  18. package/dist/types/session/session-manager.d.ts +5 -1
  19. package/dist/types/session/session-storage.d.ts +13 -0
  20. package/dist/types/skill-state/active-state.d.ts +4 -0
  21. package/package.json +8 -8
  22. package/src/cli/mcp-cli.ts +4 -21
  23. package/src/cli.ts +32 -0
  24. package/src/commands/deep-interview.ts +9 -4
  25. package/src/commit/agentic/index.ts +3 -3
  26. package/src/commit/map-reduce/index.ts +2 -2
  27. package/src/config/model-profiles.ts +2 -3
  28. package/src/defaults/gjc/skills/deep-interview/SKILL.md +78 -163
  29. package/src/gjc-runtime/deep-interview-ambiguity.ts +68 -47
  30. package/src/gjc-runtime/deep-interview-draft.ts +842 -0
  31. package/src/gjc-runtime/deep-interview-payload.ts +262 -0
  32. package/src/gjc-runtime/deep-interview-recorder.ts +294 -226
  33. package/src/gjc-runtime/deep-interview-repair.ts +1674 -0
  34. package/src/gjc-runtime/deep-interview-runtime.ts +264 -103
  35. package/src/gjc-runtime/deep-interview-state.ts +916 -4
  36. package/src/gjc-runtime/ralplan-runtime.ts +3 -1
  37. package/src/gjc-runtime/state-writer.ts +166 -2
  38. package/src/gjc-runtime/workflow-manifest.generated.json +386 -1
  39. package/src/gjc-runtime/workflow-manifest.ts +191 -5
  40. package/src/internal-urls/docs-index.generated.ts +3 -2
  41. package/src/modes/components/welcome.ts +10 -2
  42. package/src/modes/interactive-mode.ts +29 -9
  43. package/src/prompts/system/system-prompt.md +1 -0
  44. package/src/runtime-mcp/json-rpc.ts +7 -4
  45. package/src/runtime-mcp/redaction.ts +30 -0
  46. package/src/sdk/broker/session-index.ts +11 -1
  47. package/src/sdk/bus/chat-daemon-control.ts +5 -3
  48. package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
  49. package/src/sdk/bus/telegram-daemon.ts +18 -2
  50. package/src/session/agent-session.ts +86 -0
  51. package/src/session/internal/managed-session-scope.ts +206 -111
  52. package/src/session/internal/managed-session-storage.ts +17 -4
  53. package/src/session/session-manager.ts +96 -33
  54. package/src/session/session-storage.ts +23 -0
  55. package/src/skill-state/active-state.ts +10 -0
  56. package/src/task/executor.ts +13 -5
  57. package/src/tools/ask.ts +1 -0
@@ -327,6 +327,9 @@ export declare class SessionMigrationPolicyError extends Error {
327
327
  readonly code = "legacy_migration_disabled";
328
328
  constructor();
329
329
  }
330
+ export declare class SessionArtifactCapacityError extends Error {
331
+ constructor(message: string);
332
+ }
330
333
  export type ResumeTailInspection = ResumeTailResumable | ResumeTailTerminal | ResumeTailError;
331
334
  export interface StrictSessionOpenSuccess {
332
335
  kind: "opened";
@@ -334,7 +337,8 @@ export interface StrictSessionOpenSuccess {
334
337
  }
335
338
  export interface StrictSessionOpenFailure {
336
339
  kind: "error";
337
- reason: ResumeTailError["reason"] | "identity-mismatch" | "migration-required";
340
+ reason: ResumeTailError["reason"] | "identity-mismatch" | "migration-required" | "artifact_capacity_exceeded";
341
+ message?: string;
338
342
  }
339
343
  /** Exact transcript bytes and authority captured from one descriptor-bound read. */
340
344
  export interface CapturedSessionTranscriptSnapshot {
@@ -112,6 +112,11 @@ export interface SessionStorage {
112
112
  readSnapshotSync?(path: string): SessionStorageSnapshot;
113
113
  statSync(path: string): SessionStorageStat;
114
114
  listFilesSync(dir: string, pattern: string): string[];
115
+ /** List matching files with mtimes without issuing one JavaScript stat call per path. */
116
+ listFilesByMtime?(dir: string, pattern: string): Promise<Array<{
117
+ path: string;
118
+ mtimeMs: number;
119
+ }>>;
115
120
  /**
116
121
  * Strict directory scan that never suppresses scan/root errors. Used by strict
117
122
  * authorization inventory; the forgiving {@link listFilesSync} stays display-only.
@@ -267,6 +272,10 @@ export declare class FileSessionStorage implements SessionStorage {
267
272
  readSnapshotSync(fpath: string): SessionStorageSnapshot;
268
273
  statSync(path: string): SessionStorageStat;
269
274
  listFilesSync(dir: string, pattern: string): string[];
275
+ listFilesByMtime(dir: string, pattern: string): Promise<Array<{
276
+ path: string;
277
+ mtimeMs: number;
278
+ }>>;
270
279
  listFilesStrictSync(dir: string, pattern: string): string[];
271
280
  exists(path: string): Promise<boolean>;
272
281
  readText(path: string): Promise<string>;
@@ -299,6 +308,10 @@ export declare class MemorySessionStorage implements SessionStorage {
299
308
  readSnapshotSync(path: string): SessionStorageSnapshot;
300
309
  statSync(path: string): SessionStorageStat;
301
310
  listFilesSync(dir: string, pattern: string): string[];
311
+ listFilesByMtime(dir: string, pattern: string): Promise<Array<{
312
+ path: string;
313
+ mtimeMs: number;
314
+ }>>;
302
315
  listFilesStrictSync(dir: string, pattern: string): string[];
303
316
  exists(path: string): Promise<boolean>;
304
317
  readText(path: string): Promise<string>;
@@ -45,6 +45,8 @@ export interface SkillActiveEntry {
45
45
  handoff_at?: string;
46
46
  active_subskills?: ActiveSubskillEntry[];
47
47
  source_state_revision?: number;
48
+ /** Durable ordering token from the source workflow mode-state, not this cache file's revision. */
49
+ committed_mode_state_revision?: number;
48
50
  }
49
51
  export interface SkillActiveState {
50
52
  version?: number;
@@ -85,6 +87,8 @@ export interface SyncSkillActiveStateOptions {
85
87
  handoff_at?: string;
86
88
  active_subskills?: ActiveSubskillEntry[];
87
89
  sourceRevision?: number;
90
+ /** Durable ordering token from the source workflow mode-state, not this cache file's revision. */
91
+ committedModeRevision?: number;
88
92
  }
89
93
  export declare function normalizeWorkflowHudSummary(raw: unknown): WorkflowHudSummary | undefined;
90
94
  export declare function isCanonicalGjcWorkflowSkill(skill: string): skill is CanonicalGjcWorkflowSkill;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/coding-agent",
4
- "version": "0.11.8",
4
+ "version": "0.11.9",
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",
@@ -53,13 +53,13 @@
53
53
  "@agentclientprotocol/sdk": "1.2.1",
54
54
  "@babel/parser": "^7.29.3",
55
55
  "@mozilla/readability": "^0.6.0",
56
- "@gajae-code/stats": "0.11.8",
57
- "@gajae-code/agent-core": "0.11.8",
58
- "@gajae-code/ai": "0.11.8",
59
- "@gajae-code/bridge-client": "0.11.8",
60
- "@gajae-code/natives": "0.11.8",
61
- "@gajae-code/tui": "0.11.8",
62
- "@gajae-code/utils": "0.11.8",
56
+ "@gajae-code/stats": "0.11.9",
57
+ "@gajae-code/agent-core": "0.11.9",
58
+ "@gajae-code/ai": "0.11.9",
59
+ "@gajae-code/bridge-client": "0.11.9",
60
+ "@gajae-code/natives": "0.11.9",
61
+ "@gajae-code/tui": "0.11.9",
62
+ "@gajae-code/utils": "0.11.9",
63
63
  "@puppeteer/browsers": "^2.13.0",
64
64
  "@types/turndown": "5.0.6",
65
65
  "@xterm/headless": "^6.0.0",
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { getMCPConfigPath, getProjectDir } from "@gajae-code/utils";
8
8
  import { getMCPServer, readMCPConfigFile, removeMCPServer, upsertMCPServer } from "../runtime-mcp/config-writer";
9
+ import { redactMCPEndpoint } from "../runtime-mcp/redaction";
9
10
  import type { MCPConfigFile, MCPServerConfig } from "../runtime-mcp/types";
10
11
 
11
12
  export type MCPAction = "add" | "list" | "remove";
@@ -141,24 +142,6 @@ function redactRecord(
141
142
  );
142
143
  }
143
144
 
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
145
  function redactArgs(args: string[] | undefined): string[] | undefined {
163
146
  if (!args) return undefined;
164
147
  const redacted: string[] = [];
@@ -196,7 +179,7 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
196
179
  if (headers) redacted.headers = headers;
197
180
  }
198
181
  if ("url" in redacted) {
199
- const url = redactUrl(redacted.url);
182
+ const url = redactMCPEndpoint(redacted.url);
200
183
  if (url) redacted.url = url;
201
184
  }
202
185
  if ("args" in redacted) {
@@ -207,7 +190,7 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
207
190
  redacted.auth = {
208
191
  type: redacted.auth.type,
209
192
  credentialId: redacted.auth.credentialId ? REDACTED : undefined,
210
- tokenUrl: redactUrl(redacted.auth.tokenUrl),
193
+ tokenUrl: redactMCPEndpoint(redacted.auth.tokenUrl),
211
194
  clientId: redacted.auth.clientId ? REDACTED : undefined,
212
195
  clientSecret: redacted.auth.clientSecret ? REDACTED : undefined,
213
196
  };
@@ -216,7 +199,7 @@ export function redactMCPServerConfig(config: MCPServerConfig): MCPServerConfig
216
199
  redacted.oauth = {
217
200
  clientId: redacted.oauth.clientId ? REDACTED : undefined,
218
201
  clientSecret: redacted.oauth.clientSecret ? REDACTED : undefined,
219
- redirectUri: redactUrl(redacted.oauth.redirectUri),
202
+ redirectUri: redactMCPEndpoint(redacted.oauth.redirectUri),
220
203
  callbackPort: redacted.oauth.callbackPort,
221
204
  callbackPath: redacted.oauth.callbackPath,
222
205
  };
package/src/cli.ts CHANGED
@@ -118,6 +118,36 @@ Options:
118
118
  `);
119
119
  }
120
120
 
121
+ export function interactiveBootstrapText(
122
+ argv: readonly string[],
123
+ stdinIsTTY = process.stdin.isTTY,
124
+ stdoutIsTTY = process.stdout.isTTY,
125
+ ): string | undefined {
126
+ if (!stdinIsTTY || !stdoutIsTTY || argv[0] !== "launch") return undefined;
127
+ for (let index = 1; index < argv.length; index++) {
128
+ const arg = argv[index];
129
+ if (
130
+ arg === "--print" ||
131
+ arg?.startsWith("--print=") ||
132
+ arg === "-p" ||
133
+ arg === "--export" ||
134
+ arg?.startsWith("--export=") ||
135
+ arg === "--list-models" ||
136
+ arg?.startsWith("--list-models=") ||
137
+ arg === "--mode" ||
138
+ arg?.startsWith("--mode=") ||
139
+ arg === "--help" ||
140
+ arg?.startsWith("--help=") ||
141
+ arg === "-h" ||
142
+ arg === "--version" ||
143
+ arg?.startsWith("--version=") ||
144
+ arg === "-v"
145
+ )
146
+ return undefined;
147
+ }
148
+ return "\u001b[?25h\u001b[38;5;45mGJC\u001b[0m warming workspace\r\n\r\n> ";
149
+ }
150
+
121
151
  function isNotifyDaemonInternalFastPath(argv: string[]): boolean {
122
152
  return argv[0] === "notify" && argv[1] === "daemon-internal";
123
153
  }
@@ -412,6 +442,8 @@ export async function runCli(argv: string[]): Promise<void> {
412
442
  showStatsFastHelp();
413
443
  return;
414
444
  }
445
+ const bootstrap = interactiveBootstrapText(runArgv);
446
+ if (bootstrap) process.stdout.write(bootstrap);
415
447
  await installRuntimeGlobals();
416
448
  return run({ bin: APP_NAME, version: VERSION, argv: runArgv, commands, help: showHelp });
417
449
  }
@@ -2,7 +2,8 @@ import { Command, Flags } from "@gajae-code/utils/cli";
2
2
  import { runNativeDeepInterviewCommand } from "../gjc-runtime/deep-interview-runtime";
3
3
 
4
4
  export default class DeepInterview extends Command {
5
- static description = "Run native GJC deep-interview workflow";
5
+ static description =
6
+ "Run native GJC deep-interview workflow. Use `draft` for editable CLI-owned mutation inputs; JSON repair verbs are compatibility-only.";
6
7
  static strict = false;
7
8
  static flags = {
8
9
  quick: Flags.boolean({ description: "Seed a quick deep-interview run" }),
@@ -26,9 +27,13 @@ export default class DeepInterview extends Command {
26
27
  json: Flags.boolean({ description: "Output JSON" }),
27
28
  };
28
29
  static examples = [
29
- '$ gjc deep-interview --trace --standard "<idea>"',
30
- "$ gjc deep-interview --write --stage final --slug my-feature --spec ./final-spec.md",
31
- "$ gjc deep-interview --write --stage final --slug my-feature --spec ./final-spec.md --deliberate",
30
+ '$ gjc deep-interview --trace --standard "<idea>" --json',
31
+ "$ gjc deep-interview draft create --for initialize-context --session-id <id> --json",
32
+ "$ gjc deep-interview draft edit --draft-id <id> --expected-draft-revision 1 --op set --path /type --value greenfield --json",
33
+ "$ gjc deep-interview draft check --draft-id <id> --json",
34
+ "$ gjc deep-interview initialize-context --draft-id <id> --expected-draft-revision <n> --json",
35
+ "$ gjc deep-interview --write --stage final --slug my-feature --spec ./final-spec.md --json",
36
+ "$ gjc deep-interview inspect --session-id <id> --selector summary --json",
32
37
  ];
33
38
 
34
39
  async run(): Promise<void> {
@@ -1,6 +1,6 @@
1
1
  import * as path from "node:path";
2
2
  import { createInterface } from "node:readline/promises";
3
- import { $env, getProjectDir, isEnoent, prompt } from "@gajae-code/utils";
3
+ import { $env, $pickenv, getProjectDir, isEnoent, prompt } from "@gajae-code/utils";
4
4
  import { applyChangelogProposals } from "../../commit/changelog";
5
5
  import { detectChangelogBoundaries } from "../../commit/changelog/detect";
6
6
  import { parseUnreleasedSection } from "../../commit/changelog/parse";
@@ -88,7 +88,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
88
88
  } else {
89
89
  process.stdout.write(" └─ (none found)\n");
90
90
  }
91
- const forceFallback = $env.PI_COMMIT_TEST_FALLBACK?.toLowerCase() === "true";
91
+ const forceFallback = $pickenv("GJC_COMMIT_TEST_FALLBACK", "PI_COMMIT_TEST_FALLBACK")?.toLowerCase() === "true";
92
92
  if (forceFallback) {
93
93
  process.stdout.write("● Forcing fallback commit generation...\n");
94
94
  const fallbackProposal = generateFallbackProposal(numstat);
@@ -152,7 +152,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
152
152
  }
153
153
 
154
154
  if (!usedFallback && !commitState.proposal && !commitState.splitProposal) {
155
- if ($env.PI_COMMIT_NO_FALLBACK?.toLowerCase() !== "true") {
155
+ if ($pickenv("GJC_COMMIT_NO_FALLBACK", "PI_COMMIT_NO_FALLBACK")?.toLowerCase() !== "true") {
156
156
  process.stdout.write("● Agent did not provide proposal, using fallback...\n");
157
157
  commitState.proposal = generateFallbackProposal(numstat);
158
158
  usedFallback = true;
@@ -1,6 +1,6 @@
1
1
  import type { ThinkingLevel } from "@gajae-code/agent-core";
2
2
  import type { Api, Model } from "@gajae-code/ai";
3
- import { $env } from "@gajae-code/utils";
3
+ import { $pickenv } from "@gajae-code/utils";
4
4
  import { parseFileDiffs } from "../../commit/git/diff";
5
5
  import type { ConventionalAnalysis } from "../../commit/types";
6
6
  import { isExcludedFile } from "../../commit/utils/exclusions";
@@ -34,7 +34,7 @@ export interface MapReduceInput {
34
34
  }
35
35
 
36
36
  export function shouldUseMapReduce(diff: string, settings?: MapReduceSettings): boolean {
37
- if ($env.PI_COMMIT_MAP_REDUCE?.toLowerCase() === "false") return false;
37
+ if ($pickenv("GJC_COMMIT_MAP_REDUCE", "PI_COMMIT_MAP_REDUCE")?.toLowerCase() === "false") return false;
38
38
  if (settings?.enabled === false) return false;
39
39
  const minFiles = settings?.minFiles ?? MIN_FILES_FOR_MAP_REDUCE;
40
40
  const maxFileTokens = settings?.maxFileTokens ?? MAX_FILE_TOKENS;
@@ -1,9 +1,8 @@
1
1
  import { sanitizeText } from "@gajae-code/utils";
2
- import type { GjcModelAssignmentTargetId } from "./model-registry";
3
2
  import { type ModelSelectorValue, normalizeModelSelectorValue } from "./model-selector-value";
4
- import type { ModelsConfig } from "./models-config-schema";
3
+ import type { GJC_MODEL_ASSIGNMENT_TARGET_IDS, ModelsConfig } from "./models-config-schema";
5
4
 
6
- export type ModelProfileRole = GjcModelAssignmentTargetId;
5
+ export type ModelProfileRole = (typeof GJC_MODEL_ASSIGNMENT_TARGET_IDS)[number];
7
6
 
8
7
  export interface ModelProfileDefinition {
9
8
  name: string;