@gajae-code/coding-agent 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +45 -8600
  2. package/dist/types/cli/setup-cli.d.ts +1 -0
  3. package/dist/types/cli/update-cli.d.ts +3 -0
  4. package/dist/types/commands/deep-interview.d.ts +41 -0
  5. package/dist/types/commands/setup.d.ts +3 -0
  6. package/dist/types/config/settings-schema.d.ts +56 -0
  7. package/dist/types/defaults/gjc-defaults.d.ts +19 -6
  8. package/dist/types/discovery/helpers.d.ts +2 -0
  9. package/dist/types/extensibility/extensions/types.d.ts +6 -0
  10. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +18 -0
  11. package/dist/types/hooks/skill-state.d.ts +5 -0
  12. package/dist/types/memories/index.d.ts +1 -1
  13. package/dist/types/memory-backend/local-backend.d.ts +3 -3
  14. package/dist/types/modes/components/hook-selector.d.ts +7 -0
  15. package/dist/types/modes/components/settings-selector.d.ts +3 -1
  16. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  17. package/dist/types/modes/interactive-mode.d.ts +1 -0
  18. package/dist/types/modes/theme/defaults/index.d.ts +126 -0
  19. package/dist/types/modes/theme/theme.d.ts +5 -0
  20. package/dist/types/modes/types.d.ts +1 -0
  21. package/dist/types/modes/utils/context-usage.d.ts +6 -2
  22. package/dist/types/sdk.d.ts +6 -2
  23. package/dist/types/session/agent-session.d.ts +45 -1
  24. package/dist/types/session/session-manager.d.ts +3 -0
  25. package/dist/types/setup/model-onboarding-guidance.d.ts +1 -0
  26. package/dist/types/setup/provider-onboarding.d.ts +29 -5
  27. package/dist/types/skill-state/active-state.d.ts +26 -1
  28. package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
  29. package/dist/types/skill-state/initial-phase.d.ts +12 -0
  30. package/dist/types/task/executor.d.ts +2 -0
  31. package/dist/types/task/types.d.ts +11 -0
  32. package/dist/types/tools/index.d.ts +20 -1
  33. package/dist/types/tools/skill.d.ts +47 -0
  34. package/dist/types/utils/changelog.d.ts +18 -2
  35. package/package.json +7 -7
  36. package/src/cli/setup-cli.ts +26 -12
  37. package/src/cli/update-cli.ts +67 -16
  38. package/src/cli.ts +1 -0
  39. package/src/commands/deep-interview.ts +25 -2
  40. package/src/commands/setup.ts +2 -0
  41. package/src/commands/state.ts +1 -0
  42. package/src/config/settings-schema.ts +63 -0
  43. package/src/defaults/gjc/skills/deep-interview/SKILL.md +58 -5
  44. package/src/defaults/gjc/skills/deep-interview/auto-answer-uncertain.md +37 -0
  45. package/src/defaults/gjc/skills/deep-interview/auto-research-greenfield.md +42 -0
  46. package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -0
  47. package/src/defaults/gjc/skills/team/SKILL.md +10 -0
  48. package/src/defaults/gjc/skills/ultragoal/SKILL.md +19 -6
  49. package/src/defaults/gjc-defaults.ts +68 -16
  50. package/src/discovery/helpers.ts +24 -1
  51. package/src/extensibility/extensions/types.ts +6 -0
  52. package/src/gjc-runtime/deep-interview-runtime.ts +312 -1
  53. package/src/gjc-runtime/state-runtime.ts +175 -5
  54. package/src/goals/tools/goal-tool.ts +5 -1
  55. package/src/hooks/skill-state.ts +8 -6
  56. package/src/internal-urls/docs-index.generated.ts +6 -4
  57. package/src/internal-urls/memory-protocol.ts +3 -2
  58. package/src/main.ts +2 -3
  59. package/src/memories/index.ts +6 -4
  60. package/src/memory-backend/local-backend.ts +14 -6
  61. package/src/modes/components/hook-selector.ts +156 -1
  62. package/src/modes/components/settings-selector.ts +16 -12
  63. package/src/modes/controllers/command-controller.ts +3 -4
  64. package/src/modes/controllers/extension-ui-controller.ts +1 -0
  65. package/src/modes/controllers/selector-controller.ts +69 -9
  66. package/src/modes/interactive-mode.ts +14 -1
  67. package/src/modes/theme/defaults/blue-crab.json +126 -0
  68. package/src/modes/theme/defaults/index.ts +2 -0
  69. package/src/modes/theme/theme.ts +40 -1
  70. package/src/modes/types.ts +1 -0
  71. package/src/modes/utils/context-usage.ts +66 -17
  72. package/src/prompts/agents/architect.md +3 -0
  73. package/src/prompts/agents/executor.md +2 -0
  74. package/src/prompts/agents/frontmatter.md +1 -0
  75. package/src/prompts/memories/unavailable.md +9 -0
  76. package/src/prompts/system/subagent-system-prompt.md +6 -0
  77. package/src/prompts/tools/skill.md +28 -0
  78. package/src/prompts/tools/task.md +3 -0
  79. package/src/sdk.ts +54 -10
  80. package/src/session/agent-session.ts +204 -21
  81. package/src/session/session-manager.ts +9 -1
  82. package/src/setup/model-onboarding-guidance.ts +6 -3
  83. package/src/setup/provider-onboarding.ts +177 -16
  84. package/src/skill-state/active-state.ts +150 -25
  85. package/src/skill-state/deep-interview-mutation-guard.ts +11 -24
  86. package/src/skill-state/initial-phase.ts +17 -0
  87. package/src/slash-commands/builtin-registry.ts +62 -14
  88. package/src/slash-commands/helpers/context-report.ts +123 -13
  89. package/src/task/agents.ts +1 -0
  90. package/src/task/executor.ts +9 -1
  91. package/src/task/index.ts +91 -4
  92. package/src/task/types.ts +6 -0
  93. package/src/tools/ask.ts +2 -0
  94. package/src/tools/index.ts +23 -1
  95. package/src/tools/skill.ts +153 -0
  96. package/src/utils/changelog.ts +67 -44
@@ -12,9 +12,32 @@
12
12
  *
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
- import { type Agent, type AgentEvent, type AgentMessage, type AgentState, type AgentTool, ThinkingLevel } from "@gajae-code/agent-core";
15
+ import { type Agent, type AgentEvent, type AgentMessage, type AgentState, type AgentTool, type StablePrefixSnapshot, ThinkingLevel } from "@gajae-code/agent-core";
16
16
  import { type CompactionResult } from "@gajae-code/agent-core/compaction";
17
17
  import type { AssistantMessage, Effort, ImageContent, Message, MessageAttribution, Model, ProviderSessionState, ServiceTier, SimpleStreamOptions, TextContent, ToolChoice, UsageReport } from "@gajae-code/ai";
18
+ export interface ForkContextSeedMetadata {
19
+ sourceSessionId: string;
20
+ parentMessageCount: number;
21
+ includedMessages: number;
22
+ skippedMessages: number;
23
+ approximateTokens: number;
24
+ maxMessages: number;
25
+ maxTokens: number;
26
+ skippedReasons: Record<string, number>;
27
+ }
28
+ export interface ForkContextSeed {
29
+ messages: Message[];
30
+ agentMessages: AgentMessage[];
31
+ metadata: ForkContextSeedMetadata;
32
+ cacheIdentity?: string;
33
+ appendOnlyPrefixSnapshot?: StablePrefixSnapshot;
34
+ }
35
+ export interface ForkContextSeedOptions {
36
+ maxMessages: number;
37
+ maxTokens: number;
38
+ cacheIdentity?: string;
39
+ signal?: AbortSignal;
40
+ }
18
41
  import { type AsyncJob, type AsyncJobDeliveryState, AsyncJobManager } from "../async";
19
42
  import type { Rule } from "../capability/rule";
20
43
  import { type ModelRegistry } from "../config/model-registry";
@@ -138,6 +161,8 @@ export interface AgentSessionConfig {
138
161
  skillsSettings?: SkillsSettings;
139
162
  /** Model registry for API key resolution and model discovery */
140
163
  modelRegistry: ModelRegistry;
164
+ /** Task recursion depth for nested sessions. Top-level sessions use 0. */
165
+ taskDepth?: number;
141
166
  /** Tool registry for LSP and settings */
142
167
  toolRegistry?: Map<string, AgentTool>;
143
168
  /** Current session pre-LLM message transform pipeline */
@@ -188,6 +213,10 @@ export interface AgentSessionConfig {
188
213
  * **MUST NOT** dispose it on their own teardown.
189
214
  */
190
215
  ownedAsyncJobManager?: AsyncJobManager;
216
+ /** Optional fork-context seed used to initialize a child session before its first prompt. */
217
+ forkContextSeed?: ForkContextSeed;
218
+ /** Optional provider state override. Fork-context children should omit this by default. */
219
+ providerSessionState?: Map<string, ProviderSessionState>;
191
220
  /** Agent identity (registry id like "0-Main" or "3-Alice") used for IRC routing. */
192
221
  agentId?: string;
193
222
  /** Shared agent registry (for forwarding IRC observations to the main session UI). */
@@ -199,6 +228,8 @@ export interface AgentSessionConfig {
199
228
  * so that credential sticky selection is consistent with the session's streaming calls.
200
229
  */
201
230
  providerSessionId?: string;
231
+ /** Optional provider-facing cache identity, distinct from logical session identity. */
232
+ providerCacheSessionId?: string;
202
233
  }
203
234
  /** Options for AgentSession.prompt() */
204
235
  export interface PromptOptions {
@@ -263,6 +294,7 @@ export declare class AgentSession {
263
294
  readonly agent: Agent;
264
295
  readonly sessionManager: SessionManager;
265
296
  readonly settings: Settings;
297
+ readonly taskDepth: number;
266
298
  readonly yieldQueue: YieldQueue;
267
299
  readonly configWarnings: string[];
268
300
  readonly rawSseDebugBuffer: RawSseDebugBuffer;
@@ -279,12 +311,24 @@ export declare class AgentSession {
279
311
  setForcedToolChoice(toolName: string): void;
280
312
  /** The tool-choice queue: forces forthcoming tool invocations and carries handlers. */
281
313
  get toolChoiceQueue(): ToolChoiceQueue;
314
+ /** Current skill prompt executing in this session, if any. */
315
+ getActiveSkillState(): {
316
+ skill: string;
317
+ session_id?: string;
318
+ } | undefined;
319
+ /** Best-effort accessor for the active skill's `current_phase` field from
320
+ * its persisted mode-state file. Used by the `skill` tool to enforce the
321
+ * terminal-phase chain guard. Returns undefined when no active skill is
322
+ * recorded or the mode-state file is missing/unreadable; callers should
323
+ * treat undefined as a non-terminal phase (refuses to chain). */
324
+ getActiveSkillPhase(): string | undefined;
282
325
  /** Peek the in-flight directive's invocation handler for use by the resolve tool. */
283
326
  peekQueueInvoker(): ((input: unknown) => Promise<unknown> | unknown) | undefined;
284
327
  peekStandingResolveHandler(): ((input: unknown) => Promise<unknown> | unknown) | undefined;
285
328
  setStandingResolveHandler(handler: ((input: unknown) => Promise<unknown> | unknown) | null): void;
286
329
  /** Provider-scoped mutable state store for transport/session caches. */
287
330
  get providerSessionState(): Map<string, ProviderSessionState>;
331
+ buildForkContextSeed(options: ForkContextSeedOptions): Promise<ForkContextSeed>;
288
332
  getHindsightSessionState(): HindsightSessionState | undefined;
289
333
  setHindsightSessionState(state: HindsightSessionState | undefined): HindsightSessionState | undefined;
290
334
  /** TTSR manager for time-traveling stream rules */
@@ -111,6 +111,8 @@ export interface SessionInitEntry extends SessionEntryBase {
111
111
  tools: string[];
112
112
  /** Output schema if structured output was requested */
113
113
  outputSchema?: unknown;
114
+ /** Fork-context seed metadata for subagent debugging/replay. */
115
+ forkContext?: unknown;
114
116
  }
115
117
  /** Mode change entry - tracks agent mode transitions (e.g. plan mode). */
116
118
  export interface ModeChangeEntry extends SessionEntryBase {
@@ -393,6 +395,7 @@ export declare class SessionManager {
393
395
  task: string;
394
396
  tools: string[];
395
397
  outputSchema?: unknown;
398
+ forkContext?: unknown;
396
399
  }): string;
397
400
  /** Append a compaction summary as child of current leaf, then advance leaf. Returns entry id. */
398
401
  appendCompaction<T = unknown>(summary: string, shortSummary: string | undefined, firstKeptEntryId: string, tokensBefore: number, details?: T, fromExtension?: boolean, preserveData?: Record<string, unknown>): string;
@@ -1,4 +1,5 @@
1
1
  export declare const MODEL_ONBOARDING_API_PROVIDER_COMMAND = "/provider add --compat <openai|anthropic> --provider <id> --base-url <url> --api-key-env <ENV> --model <model>";
2
+ export declare const MODEL_ONBOARDING_PROVIDER_PRESET_COMMAND = "/provider add --preset <minimax|minimax-cn|glm>";
2
3
  export declare const MODEL_ONBOARDING_SETUP_COMMAND = "gjc setup provider";
3
4
  export declare const MODEL_ONBOARDING_OAUTH_COMMAND = "/provider login [provider-id] or /login [provider-id]";
4
5
  export declare function formatModelOnboardingGuidance(): string;
@@ -1,28 +1,52 @@
1
+ import { type ModelsConfig } from "../config/models-config-schema";
1
2
  export type ProviderCompatibility = "openai" | "anthropic";
3
+ export type ProviderSetupApi = "openai-responses" | "openai-completions" | "anthropic-messages";
2
4
  export interface ProviderSetupInput {
3
- compatibility: ProviderCompatibility;
4
- providerId: string;
5
- baseUrl: string;
5
+ compatibility?: ProviderCompatibility;
6
+ preset?: string;
7
+ providerId?: string;
8
+ baseUrl?: string;
6
9
  apiKey?: string;
7
10
  apiKeyEnv?: string;
8
- models: string[];
11
+ models?: string[];
9
12
  modelsPath?: string;
10
13
  force?: boolean;
11
14
  }
12
15
  export interface ProviderSetupResult {
13
16
  providerId: string;
14
17
  compatibility: ProviderCompatibility;
15
- api: "openai-responses" | "anthropic-messages";
18
+ api: ProviderSetupApi;
16
19
  baseUrl: string;
17
20
  modelIds: string[];
18
21
  modelsPath: string;
19
22
  redactedApiKey: string;
20
23
  credentialSource: "literal" | "env";
24
+ preset?: string;
25
+ presetName?: string;
26
+ }
27
+ type ProviderConfig = NonNullable<NonNullable<ModelsConfig["providers"]>[string]>;
28
+ type ProviderCompatConfig = NonNullable<ProviderConfig["compat"]>;
29
+ interface ProviderPreset {
30
+ id: string;
31
+ aliases: readonly string[];
32
+ name: string;
33
+ description: string;
34
+ compatibility: ProviderCompatibility;
35
+ api: ProviderSetupApi;
36
+ providerId: string;
37
+ baseUrl: string;
38
+ apiKeyEnv: string;
39
+ models: readonly string[];
40
+ compat?: ProviderCompatConfig;
21
41
  }
42
+ export declare const PROVIDER_PRESETS: readonly ProviderPreset[];
22
43
  export declare function getDefaultModelsPath(): string;
23
44
  export declare function normalizeProviderId(providerId: string): string;
24
45
  export declare function parseProviderCompatibility(value: string): ProviderCompatibility;
46
+ export declare function findProviderPreset(value: string | undefined): ProviderPreset | undefined;
47
+ export declare function formatProviderPresetList(): string;
25
48
  export declare function parseModelList(values: readonly string[]): string[];
26
49
  export declare function redactSecret(secret: string): string;
27
50
  export declare function addApiCompatibleProvider(input: ProviderSetupInput): Promise<ProviderSetupResult>;
28
51
  export declare function formatProviderSetupResult(result: ProviderSetupResult): string;
52
+ export {};
@@ -1,6 +1,5 @@
1
1
  import type { WorkflowStateReceipt } from "./workflow-state-contract";
2
2
  export declare const SKILL_ACTIVE_STATE_FILE = "skill-active-state.json";
3
- export declare const SKILL_ACTIVE_STALE_MS: number;
4
3
  export declare const CANONICAL_GJC_WORKFLOW_SKILLS: readonly ["deep-interview", "ralplan", "ultragoal", "team"];
5
4
  export type CanonicalGjcWorkflowSkill = (typeof CANONICAL_GJC_WORKFLOW_SKILLS)[number];
6
5
  export type WorkflowHudSeverity = "info" | "warning" | "blocked" | "error" | "success";
@@ -31,6 +30,9 @@ export interface SkillActiveEntry {
31
30
  hud?: WorkflowHudSummary;
32
31
  stale?: boolean;
33
32
  receipt?: WorkflowStateReceipt;
33
+ handoff_from?: string;
34
+ handoff_to?: string;
35
+ handoff_at?: string;
34
36
  }
35
37
  export interface SkillActiveState {
36
38
  version?: number;
@@ -63,6 +65,9 @@ export interface SyncSkillActiveStateOptions {
63
65
  source?: string;
64
66
  hud?: WorkflowHudSummary;
65
67
  receipt?: WorkflowStateReceipt;
68
+ handoff_from?: string;
69
+ handoff_to?: string;
70
+ handoff_at?: string;
66
71
  }
67
72
  export declare function normalizeWorkflowHudSummary(raw: unknown): WorkflowHudSummary | undefined;
68
73
  export declare function isCanonicalGjcWorkflowSkill(skill: string): skill is CanonicalGjcWorkflowSkill;
@@ -71,3 +76,23 @@ export declare function normalizeSkillActiveState(raw: unknown): SkillActiveStat
71
76
  export declare function getSkillActiveStatePaths(cwd: string, sessionId?: string): SkillActiveStatePaths;
72
77
  export declare function readVisibleSkillActiveState(cwd: string, sessionId?: string): Promise<SkillActiveState | null>;
73
78
  export declare function syncSkillActiveState(options: SyncSkillActiveStateOptions): Promise<void>;
79
+ export interface ApplyHandoffOptions {
80
+ cwd: string;
81
+ caller: SyncSkillActiveStateOptions;
82
+ callee: SyncSkillActiveStateOptions;
83
+ /** Shared timestamp; falls back to new Date().toISOString(). */
84
+ nowIso?: string;
85
+ /** When true, read errors other than ENOENT propagate. */
86
+ strict?: boolean;
87
+ }
88
+ /**
89
+ * Atomically apply a workflow-skill handoff to both the session-scoped and
90
+ * root `skill-active-state.json` files in a single write per file.
91
+ *
92
+ * Write order: **session first, root last**. The session file is the
93
+ * source of truth for HUD; the root aggregate must never lead the session
94
+ * during a handoff window. Each file is rewritten once with caller demoted
95
+ * to `active:false` (preserving `handoff_to`/`handoff_at` lineage) and
96
+ * callee promoted to `active:true` (with `handoff_from`/`handoff_at`).
97
+ */
98
+ export declare function applyHandoffToActiveState(options: ApplyHandoffOptions): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import type { AgentTool } from "@gajae-code/agent-core";
2
- export declare const DEEP_INTERVIEW_MUTATION_BLOCK_MESSAGE = "Deep-interview is active; continue interviewing with `ask`, write/finalize pending specs through the required GJC workflow CLI, or use an explicit force override. Direct `.gjc/` and product-code edits are blocked until explicit execution approval.";
2
+ export declare const DEEP_INTERVIEW_MUTATION_BLOCK_MESSAGE = "Deep-interview phase boundary: continue gathering context/questions/risks and emit a handoff/spec before code edits. Mutation tools and patch execution are blocked while deep-interview is active; finalize specs through `gjc deep-interview --write --stage final` or hand off to an execution phase.";
3
3
  export declare const WORKFLOW_STATE_MUTATION_BLOCK_MESSAGE = "Workflow state JSON is runtime-owned. Use `gjc state <skill> read|write --input '<json>'` for deep-interview, ralplan, ultragoal, and team. Planning artifacts under `.gjc/specs/` and `.gjc/plans/` remain allowed.";
4
4
  type ToolWithEditMode = AgentTool & {
5
5
  mode?: unknown;
@@ -0,0 +1,12 @@
1
+ import type { CanonicalGjcWorkflowSkill } from "./active-state";
2
+ /**
3
+ * Canonical initial phase for each GJC workflow skill. Used by both
4
+ * `recordSkillActivation` (UserPromptSubmit hook seeding initial mode-state)
5
+ * and the `gjc state <caller> handoff --to <callee>` runtime when promoting
6
+ * the callee.
7
+ *
8
+ * Keeping this mapping in a neutral skill-state module avoids cycles between
9
+ * `gjc-runtime/state-runtime.ts` and `hooks/skill-state.ts` (which pulls in
10
+ * session-manager and ultragoal verification code).
11
+ */
12
+ export declare function initialPhaseForSkill(skill: CanonicalGjcWorkflowSkill | string): string;
@@ -10,6 +10,7 @@ import { Settings } from "../config/settings";
10
10
  import { type Skill } from "../extensibility/skills";
11
11
  import type { HindsightSessionState } from "../hindsight/state";
12
12
  import type { LocalProtocolOptions } from "../internal-urls";
13
+ import type { ForkContextSeed } from "../session/agent-session";
13
14
  import type { ArtifactManager } from "../session/artifacts";
14
15
  import type { AuthStorage } from "../session/auth-storage";
15
16
  import type { ContextFileEntry } from "../tools";
@@ -73,6 +74,7 @@ export interface ExecutorOptions {
73
74
  parentTelemetry?: AgentTelemetryConfig;
74
75
  /** Skills to autoload via sendCustomMessage before the first prompt */
75
76
  autoloadSkills?: Skill[];
77
+ forkContextSeed?: ForkContextSeed;
76
78
  }
77
79
  export interface YieldItem {
78
80
  data?: unknown;
@@ -5,6 +5,7 @@ import { type TaskSimpleMode } from "./simple-mode";
5
5
  import type { NestedRepoPatch } from "./worktree";
6
6
  /** Source of an agent definition */
7
7
  export type AgentSource = "bundled" | "user" | "project";
8
+ export type ForkContextPolicy = "forbidden" | "allowed";
8
9
  /** Maximum output bytes per agent */
9
10
  export declare const MAX_OUTPUT_BYTES: number;
10
11
  /** Maximum output lines per agent */
@@ -40,6 +41,7 @@ export declare const taskItemSchema: z.ZodObject<{
40
41
  id: z.ZodString;
41
42
  description: z.ZodString;
42
43
  assignment: z.ZodString;
44
+ inheritContext: z.ZodOptional<z.ZodBoolean>;
43
45
  }, z.core.$strip>;
44
46
  export type TaskItem = z.infer<typeof taskItemSchema>;
45
47
  export declare const taskSchema: z.ZodObject<{
@@ -48,6 +50,7 @@ export declare const taskSchema: z.ZodObject<{
48
50
  id: z.ZodString;
49
51
  description: z.ZodString;
50
52
  assignment: z.ZodString;
53
+ inheritContext: z.ZodOptional<z.ZodBoolean>;
51
54
  }, z.core.$strip>>;
52
55
  }, z.core.$strip>;
53
56
  export declare const taskSchemaNoIsolation: z.ZodObject<{
@@ -56,6 +59,7 @@ export declare const taskSchemaNoIsolation: z.ZodObject<{
56
59
  id: z.ZodString;
57
60
  description: z.ZodString;
58
61
  assignment: z.ZodString;
62
+ inheritContext: z.ZodOptional<z.ZodBoolean>;
59
63
  }, z.core.$strip>>;
60
64
  }, z.core.$strip>;
61
65
  declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
@@ -64,6 +68,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
64
68
  id: z.ZodString;
65
69
  description: z.ZodString;
66
70
  assignment: z.ZodString;
71
+ inheritContext: z.ZodOptional<z.ZodBoolean>;
67
72
  }, z.core.$strip>>;
68
73
  }, z.core.$strip>, z.ZodObject<{
69
74
  agent: z.ZodString;
@@ -71,6 +76,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
71
76
  id: z.ZodString;
72
77
  description: z.ZodString;
73
78
  assignment: z.ZodString;
79
+ inheritContext: z.ZodOptional<z.ZodBoolean>;
74
80
  }, z.core.$strip>>;
75
81
  }, z.core.$strip>, z.ZodObject<{
76
82
  agent: z.ZodString;
@@ -78,6 +84,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
78
84
  id: z.ZodString;
79
85
  description: z.ZodString;
80
86
  assignment: z.ZodString;
87
+ inheritContext: z.ZodOptional<z.ZodBoolean>;
81
88
  }, z.core.$strip>>;
82
89
  }, z.core.$strip>, z.ZodObject<{
83
90
  agent: z.ZodString;
@@ -85,6 +92,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
85
92
  id: z.ZodString;
86
93
  description: z.ZodString;
87
94
  assignment: z.ZodString;
95
+ inheritContext: z.ZodOptional<z.ZodBoolean>;
88
96
  }, z.core.$strip>>;
89
97
  }, z.core.$strip>, z.ZodObject<{
90
98
  agent: z.ZodString;
@@ -92,6 +100,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
92
100
  id: z.ZodString;
93
101
  description: z.ZodString;
94
102
  assignment: z.ZodString;
103
+ inheritContext: z.ZodOptional<z.ZodBoolean>;
95
104
  }, z.core.$strip>>;
96
105
  }, z.core.$strip>, z.ZodObject<{
97
106
  agent: z.ZodString;
@@ -99,6 +108,7 @@ declare const ALL_TASK_SCHEMAS: readonly [z.ZodObject<{
99
108
  id: z.ZodString;
100
109
  description: z.ZodString;
101
110
  assignment: z.ZodString;
111
+ inheritContext: z.ZodOptional<z.ZodBoolean>;
102
112
  }, z.core.$strip>>;
103
113
  }, z.core.$strip>];
104
114
  type DynamicTaskSchema = (typeof ALL_TASK_SCHEMAS)[number];
@@ -150,6 +160,7 @@ export interface AgentDefinition {
150
160
  blocking?: boolean;
151
161
  autoloadSkills?: string[];
152
162
  hide?: boolean;
163
+ forkContext?: ForkContextPolicy;
153
164
  source: AgentSource;
154
165
  filePath?: string;
155
166
  }
@@ -1,5 +1,5 @@
1
1
  import type { AgentTelemetryConfig, AgentTool } from "@gajae-code/agent-core";
2
- import type { ToolChoice } from "@gajae-code/ai";
2
+ import type { Model, ToolChoice } from "@gajae-code/ai";
3
3
  import type { PromptTemplate } from "../config/prompt-templates";
4
4
  import type { Settings } from "../config/settings";
5
5
  import type { Skill } from "../extensibility/skills";
@@ -7,10 +7,12 @@ import type { GoalModeState, GoalRuntime } from "../goals";
7
7
  import type { HindsightSessionState } from "../hindsight/state";
8
8
  import type { PlanModeState } from "../plan-mode/state";
9
9
  import type { AgentRegistry } from "../registry/agent-registry";
10
+ import type { ForkContextSeed, ForkContextSeedOptions } from "../session/agent-session";
10
11
  import type { ArtifactManager } from "../session/artifacts";
11
12
  import type { ClientBridge } from "../session/client-bridge";
12
13
  import type { CustomMessage } from "../session/messages";
13
14
  import type { ToolChoiceQueue } from "../session/tool-choice-queue";
15
+ import type { SkillActiveEntry } from "../skill-state/active-state";
14
16
  import type { AgentOutputManager } from "../task/output-manager";
15
17
  import type { DiscoverableTool, DiscoverableToolSearchIndex } from "../tool-discovery/tool-index";
16
18
  import type { EventBus } from "../utils/event-bus";
@@ -48,6 +50,7 @@ export * from "./resolve";
48
50
  export * from "./review";
49
51
  export * from "./search";
50
52
  export * from "./search-tool-bm25";
53
+ export * from "./skill";
51
54
  export * from "./ssh";
52
55
  export * from "./subagent";
53
56
  export * from "./todo-write";
@@ -77,6 +80,12 @@ export interface ToolSession {
77
80
  workspaceTree?: WorkspaceTree;
78
81
  /** Pre-loaded skills */
79
82
  skills?: Skill[];
83
+ /** Currently executing skill prompt, when this tool session is inside one. */
84
+ getActiveSkillState?: () => Pick<SkillActiveEntry, "skill" | "session_id"> | undefined;
85
+ /** Get the active skill prompt's current phase so the skill tool can apply
86
+ * its terminal-phase chain guard. Returns the raw phase string or undefined
87
+ * when no active skill (or accessor) is available. */
88
+ getActiveSkillPhase?: () => string | undefined;
80
89
  /** Pre-loaded prompt templates */
81
90
  promptTemplates?: PromptTemplate[];
82
91
  /** Whether LSP integrations are enabled */
@@ -122,6 +131,8 @@ export interface ToolSession {
122
131
  getSessionSpawns: () => string | null;
123
132
  /** Get resolved model string if explicitly set for this session */
124
133
  getModelString?: () => string | undefined;
134
+ /** Current model, when selected. */
135
+ model?: Model;
125
136
  /** Get the current session model string, regardless of how it was chosen */
126
137
  getActiveModelString?: () => string | undefined;
127
138
  /** Auth storage for passing to subagents (avoids re-discovery) */
@@ -204,9 +215,17 @@ export interface ToolSession {
204
215
  conflictHistory?: import("./conflict-detect").ConflictHistory;
205
216
  /** Queue a hidden message to be injected at the next agent turn. */
206
217
  queueDeferredMessage?(message: CustomMessage): void;
218
+ /** Dispatch a custom message through the active session. Used by the `skill`
219
+ * tool to dispatch another skill prompt same-turn after recording a handoff. */
220
+ sendCustomMessage?(message: Pick<CustomMessage, "customType" | "content" | "display" | "details" | "attribution">, options?: {
221
+ triggerTurn?: boolean;
222
+ deliverAs?: "steer" | "followUp" | "nextTurn";
223
+ }): Promise<void>;
207
224
  /** Get the active OpenTelemetry config so subagent dispatch can forward
208
225
  * the parent's tracer/hooks with the subagent's own identity stamped. */
209
226
  getTelemetry?: () => AgentTelemetryConfig | undefined;
227
+ /** Build a sanitized fork-context seed for task subagents. */
228
+ buildForkContextSeed?: (options: ForkContextSeedOptions) => Promise<ForkContextSeed>;
210
229
  }
211
230
  export type ToolFactory = (session: ToolSession) => Tool | null | Promise<Tool | null>;
212
231
  export type BuiltinToolLoadMode = "essential" | "discoverable";
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Skill Tool — agent-initiated skill chaining.
3
+ *
4
+ * Lets the agent hand off to another available skill in the current turn. The
5
+ * callee's SKILL.md is dispatched through the same custom-message path used by
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.
11
+ *
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.
16
+ */
17
+ import type { AgentTool, AgentToolResult } from "@gajae-code/agent-core";
18
+ import * as z from "zod/v4";
19
+ import type { ToolSession } from ".";
20
+ declare const skillSchema: z.ZodObject<{
21
+ name: z.ZodString;
22
+ args: z.ZodOptional<z.ZodString>;
23
+ }, z.core.$strip>;
24
+ type SkillToolInput = z.infer<typeof skillSchema>;
25
+ export interface SkillToolDetails {
26
+ name: string;
27
+ path: string;
28
+ args?: string;
29
+ lineCount: number;
30
+ }
31
+ export declare class SkillTool implements AgentTool<typeof skillSchema, SkillToolDetails> {
32
+ #private;
33
+ readonly name = "skill";
34
+ readonly label = "Skill";
35
+ readonly summary = "Chain into another available skill in the current turn";
36
+ readonly loadMode = "discoverable";
37
+ readonly description: string;
38
+ readonly parameters: z.ZodObject<{
39
+ name: z.ZodString;
40
+ args: z.ZodOptional<z.ZodString>;
41
+ }, z.core.$strip>;
42
+ readonly strict = true;
43
+ constructor(session: ToolSession);
44
+ static createIf(session: ToolSession): SkillTool | null;
45
+ execute(_toolCallId: string, input: SkillToolInput, signal?: AbortSignal): Promise<AgentToolResult<SkillToolDetails>>;
46
+ }
47
+ export {};
@@ -5,10 +5,26 @@ export interface ChangelogEntry {
5
5
  content: string;
6
6
  }
7
7
  /**
8
- * Parse changelog entries from CHANGELOG.md
9
- * Scans for ## lines and collects content until next ## or EOF
8
+ * Parse changelog entries from a CHANGELOG.md text body.
9
+ * Scans for ## lines and collects content until next ## or EOF.
10
+ * Pure and synchronous so it can be reused by the embedded display path.
11
+ */
12
+ export declare function parseChangelogContent(content: string): ChangelogEntry[];
13
+ /**
14
+ * Parse changelog entries from a CHANGELOG.md file on disk.
15
+ * Returns [] on ENOENT; logs and returns [] on other read/parse errors.
10
16
  */
11
17
  export declare function parseChangelog(changelogPath: string): Promise<ChangelogEntry[]>;
18
+ /**
19
+ * Return changelog entries from the CHANGELOG.md that shipped with this binary.
20
+ *
21
+ * The text is embedded at build time via `with { type: "text" }`, so the
22
+ * displayed changelog is deterministic across compiled binaries, source-tree
23
+ * dev runs, and `GJC_PACKAGE_DIR` / `PI_PACKAGE_DIR` overrides (which scope to
24
+ * optional package assets like docs/examples and do not influence the
25
+ * binary-identity changelog).
26
+ */
27
+ export declare function getDisplayChangelogEntries(): ChangelogEntry[];
12
28
  /**
13
29
  * Compare versions. Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
14
30
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/coding-agent",
4
- "version": "0.2.2",
4
+ "version": "0.2.4",
5
5
  "description": "Gajae Code CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://gaebal-gajae.dev",
7
7
  "author": "Yeachan-Heo",
@@ -48,12 +48,12 @@
48
48
  "@agentclientprotocol/sdk": "0.21.0",
49
49
  "@babel/parser": "^7.29.3",
50
50
  "@mozilla/readability": "^0.6.0",
51
- "@gajae-code/stats": "0.2.2",
52
- "@gajae-code/agent-core": "0.2.2",
53
- "@gajae-code/ai": "0.2.2",
54
- "@gajae-code/natives": "0.2.2",
55
- "@gajae-code/tui": "0.2.2",
56
- "@gajae-code/utils": "0.2.2",
51
+ "@gajae-code/stats": "0.2.4",
52
+ "@gajae-code/agent-core": "0.2.4",
53
+ "@gajae-code/ai": "0.2.4",
54
+ "@gajae-code/natives": "0.2.4",
55
+ "@gajae-code/tui": "0.2.4",
56
+ "@gajae-code/utils": "0.2.4",
57
57
  "@puppeteer/browsers": "^2.13.0",
58
58
  "@types/turndown": "5.0.6",
59
59
  "@xterm/headless": "^6.0.0",
@@ -16,6 +16,7 @@ import {
16
16
  import { theme } from "../modes/theme/theme";
17
17
  import {
18
18
  addApiCompatibleProvider,
19
+ formatProviderPresetList,
19
20
  formatProviderSetupResult,
20
21
  parseProviderCompatibility,
21
22
  } from "../setup/provider-onboarding";
@@ -28,6 +29,7 @@ export interface SetupCommandArgs {
28
29
  json?: boolean;
29
30
  check?: boolean;
30
31
  force?: boolean;
32
+ preset?: string;
31
33
  compat?: string;
32
34
  provider?: string;
33
35
  baseUrl?: string;
@@ -42,6 +44,7 @@ const VALID_COMPONENTS: SetupComponent[] = ["defaults", "hooks", "provider", "py
42
44
  function hasProviderSetupFlags(flags: SetupCommandArgs["flags"]): boolean {
43
45
  return (
44
46
  flags.compat !== undefined ||
47
+ flags.preset !== undefined ||
45
48
  flags.provider !== undefined ||
46
49
  flags.baseUrl !== undefined ||
47
50
  flags.apiKeyEnv !== undefined ||
@@ -57,7 +60,7 @@ function rejectProviderFlagsOutsideProvider(component: SetupComponent, flags: Se
57
60
  console.error(chalk.red("Provider setup flags require the explicit `provider` component."));
58
61
  console.error(
59
62
  chalk.dim(
60
- `Run: ${APP_NAME} setup provider --compat <openai|anthropic> --provider <id> --base-url <url> --api-key-env <ENV> --model <id>`,
63
+ `Run: ${APP_NAME} setup provider --preset <minimax|glm> or ${APP_NAME} setup provider --compat <openai|anthropic> --provider <id> --base-url <url> --api-key-env <ENV> --model <id>`,
61
64
  ),
62
65
  );
63
66
  process.exit(1);
@@ -87,6 +90,8 @@ export function parseSetupArgs(args: string[]): SetupCommandArgs | undefined {
87
90
  flags.force = true;
88
91
  } else if (arg === "--compat") {
89
92
  flags.compat = args[++i];
93
+ } else if (arg === "--preset") {
94
+ flags.preset = args[++i];
90
95
  } else if (arg === "--provider") {
91
96
  flags.provider = args[++i];
92
97
  } else if (arg === "--base-url") {
@@ -190,6 +195,7 @@ export async function runSetupCommand(cmd: SetupCommandArgs): Promise<void> {
190
195
  async function handleProviderSetup(flags: {
191
196
  json?: boolean;
192
197
  force?: boolean;
198
+ preset?: string;
193
199
  compat?: string;
194
200
  provider?: string;
195
201
  baseUrl?: string;
@@ -199,20 +205,25 @@ async function handleProviderSetup(flags: {
199
205
  }): Promise<void> {
200
206
  try {
201
207
  const missing: string[] = [];
202
- if (!flags.compat) missing.push("--compat");
203
- if (!flags.provider) missing.push("--provider");
204
- if (!flags.baseUrl) missing.push("--base-url");
205
- if (!flags.apiKeyEnv) missing.push("--api-key-env");
206
- if (!flags.model || flags.model.length === 0) missing.push("--model");
208
+ if (!flags.preset) {
209
+ if (!flags.compat) missing.push("--compat");
210
+ if (!flags.provider) missing.push("--provider");
211
+ if (!flags.baseUrl) missing.push("--base-url");
212
+ if (!flags.apiKeyEnv) missing.push("--api-key-env");
213
+ if (!flags.model || flags.model.length === 0) missing.push("--model");
214
+ }
207
215
  if (missing.length > 0) {
208
- throw new Error(`Missing required provider setup option(s): ${missing.join(", ")}`);
216
+ throw new Error(
217
+ `Missing required provider setup option(s): ${missing.join(", ")}. Or use --preset <preset>.\nAvailable presets:\n${formatProviderPresetList()}`,
218
+ );
209
219
  }
210
220
  const result = await addApiCompatibleProvider({
211
- compatibility: parseProviderCompatibility(flags.compat!),
212
- providerId: flags.provider!,
213
- baseUrl: flags.baseUrl!,
221
+ compatibility: flags.compat ? parseProviderCompatibility(flags.compat) : undefined,
222
+ preset: flags.preset,
223
+ providerId: flags.provider,
224
+ baseUrl: flags.baseUrl,
214
225
  apiKeyEnv: flags.apiKeyEnv,
215
- models: flags.model!,
226
+ models: flags.model,
216
227
  modelsPath: flags.modelsPath,
217
228
  force: flags.force,
218
229
  });
@@ -400,18 +411,21 @@ ${chalk.bold("Usage:")}
400
411
  ${chalk.bold("Components:")}
401
412
  defaults Install bundled GJC default workflow skills (default)
402
413
  hooks Optional: install GJC native Codex UserPromptSubmit/Stop skill-state hooks
403
- provider Optional: add an OpenAI-compatible or Anthropic-compatible API provider
414
+ provider Optional: add a preset, OpenAI-compatible, or Anthropic-compatible API provider
404
415
  python Optional: verify a Python 3 interpreter is reachable for code execution
405
416
  stt Optional: install speech-to-text dependencies (openai-whisper, recording tools)
406
417
 
407
418
 
408
419
  ${chalk.bold("Provider example:")}
420
+ ${APP_NAME} setup provider --preset minimax
421
+ ${APP_NAME} setup provider --preset glm
409
422
  MY_PROVIDER_KEY=sk-... ${APP_NAME} setup provider --compat openai --provider my-oai --base-url https://api.example.com/v1 --api-key-env MY_PROVIDER_KEY --model gpt-example
410
423
 
411
424
  ${chalk.bold("Options:")}
412
425
  -c, --check Check if dependencies are installed without installing
413
426
  -f, --force Overwrite existing default workflow skill files
414
427
  --json Output status as JSON
428
+ --preset Provider preset: minimax, minimax-cn, or glm (aliases include minimax-code and zai)
415
429
  --compat Provider compatibility: openai or anthropic
416
430
  --provider Provider id to add to models.yml
417
431
  --base-url Provider API base URL