@ikarmic/forge-core 5.0.0 → 5.1.1

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 (163) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/agent.d.ts +8 -0
  3. package/dist/agent/agent.js +148 -24
  4. package/dist/agent/background.d.ts +58 -0
  5. package/dist/agent/background.js +139 -0
  6. package/dist/agent/enhance-prompt.d.ts +3 -1
  7. package/dist/agent/enhance-prompt.js +6 -2
  8. package/dist/agent/goal.d.ts +54 -0
  9. package/dist/agent/goal.js +65 -0
  10. package/dist/agent/prompts.d.ts +25 -1
  11. package/dist/agent/prompts.js +61 -17
  12. package/dist/agents/agent-files.d.ts +43 -0
  13. package/dist/agents/agent-files.js +109 -0
  14. package/dist/checkpoints/store.js +5 -1
  15. package/dist/commands/custom-commands.d.ts +9 -4
  16. package/dist/commands/custom-commands.js +13 -2
  17. package/dist/config-file.d.ts +43 -0
  18. package/dist/config-file.js +249 -5
  19. package/dist/config-merge.d.ts +13 -0
  20. package/dist/config-merge.js +67 -0
  21. package/dist/config-schema.d.ts +340 -0
  22. package/dist/config-schema.js +126 -0
  23. package/dist/config.js +2 -1
  24. package/dist/context/compaction.js +4 -0
  25. package/dist/context/mask.js +1 -1
  26. package/dist/custom-providers.d.ts +95 -0
  27. package/dist/custom-providers.js +120 -0
  28. package/dist/git/auto-commit.d.ts +2 -2
  29. package/dist/git/auto-commit.js +5 -4
  30. package/dist/github/github-tools.d.ts +2 -2
  31. package/dist/hooks/runner.js +3 -2
  32. package/dist/import/claude-code.d.ts +39 -0
  33. package/dist/import/claude-code.js +254 -0
  34. package/dist/index.d.ts +34 -5
  35. package/dist/index.js +27 -4
  36. package/dist/journey/answers-block.d.ts +17 -0
  37. package/dist/journey/answers-block.js +15 -0
  38. package/dist/journey/index.d.ts +9 -0
  39. package/dist/journey/index.js +8 -0
  40. package/dist/journey/journey.d.ts +13 -0
  41. package/dist/journey/journey.js +40 -0
  42. package/dist/journey/offer.d.ts +25 -0
  43. package/dist/journey/offer.js +48 -0
  44. package/dist/journey/questions.d.ts +23 -0
  45. package/dist/journey/questions.js +49 -0
  46. package/dist/journey/stages.d.ts +13 -0
  47. package/dist/journey/stages.js +107 -0
  48. package/dist/journey/survey.d.ts +10 -0
  49. package/dist/journey/survey.js +203 -0
  50. package/dist/journey/trigger.d.ts +19 -0
  51. package/dist/journey/trigger.js +79 -0
  52. package/dist/journey/types.d.ts +72 -0
  53. package/dist/journey/types.js +1 -0
  54. package/dist/lsp/client.d.ts +5 -0
  55. package/dist/lsp/client.js +39 -3
  56. package/dist/mcp/index.d.ts +5 -1
  57. package/dist/mcp/index.js +3 -1
  58. package/dist/mcp/manager.d.ts +17 -3
  59. package/dist/mcp/manager.js +27 -2
  60. package/dist/mcp/marketplace.d.ts +2 -0
  61. package/dist/mcp/marketplace.js +10 -4
  62. package/dist/mcp/mcp-client.d.ts +12 -2
  63. package/dist/mcp/mcp-client.js +74 -17
  64. package/dist/mcp/mcp-config.d.ts +2 -0
  65. package/dist/mcp/mcp-config.js +1 -0
  66. package/dist/mcp/oauth.d.ts +55 -0
  67. package/dist/mcp/oauth.js +197 -0
  68. package/dist/mcp/resource-tools.d.ts +8 -0
  69. package/dist/mcp/resource-tools.js +56 -0
  70. package/dist/memory/session-digest.d.ts +12 -0
  71. package/dist/memory/session-digest.js +47 -0
  72. package/dist/memory/stale.js +2 -1
  73. package/dist/model-alias.d.ts +18 -2
  74. package/dist/model-alias.js +27 -3
  75. package/dist/model-catalog.js +0 -1
  76. package/dist/model-list.js +2 -1
  77. package/dist/model-overrides.d.ts +30 -0
  78. package/dist/model-overrides.js +48 -0
  79. package/dist/orchestrator/specialists-registry.js +2 -3
  80. package/dist/orchestrator/specialists.js +4 -4
  81. package/dist/policy/ca-certs.d.ts +8 -0
  82. package/dist/policy/ca-certs.js +30 -0
  83. package/dist/policy/proxy.d.ts +10 -3
  84. package/dist/policy/proxy.js +33 -6
  85. package/dist/pricing.js +4 -0
  86. package/dist/providers.d.ts +7 -0
  87. package/dist/providers.js +39 -5
  88. package/dist/semantic/embedder.d.ts +15 -0
  89. package/dist/semantic/embedder.js +25 -0
  90. package/dist/semantic/semantic-index.d.ts +10 -2
  91. package/dist/semantic/semantic-index.js +16 -7
  92. package/dist/sessions/session-store.js +2 -1
  93. package/dist/skills/builtin.d.ts +2 -0
  94. package/dist/skills/builtin.js +68 -0
  95. package/dist/skills/index.d.ts +2 -0
  96. package/dist/skills/index.js +2 -0
  97. package/dist/skills/loader.d.ts +10 -3
  98. package/dist/skills/loader.js +20 -7
  99. package/dist/skills/marketplace.js +2 -1
  100. package/dist/skills/remote.d.ts +14 -0
  101. package/dist/skills/remote.js +131 -0
  102. package/dist/skills/types.d.ts +10 -0
  103. package/dist/specs/generate.d.ts +18 -9
  104. package/dist/specs/generate.js +174 -14
  105. package/dist/specs/index.d.ts +2 -2
  106. package/dist/specs/index.js +1 -1
  107. package/dist/specs/store.d.ts +14 -2
  108. package/dist/specs/store.js +26 -1
  109. package/dist/specs/types.d.ts +11 -0
  110. package/dist/steering/extra-instructions.d.ts +8 -0
  111. package/dist/steering/extra-instructions.js +104 -0
  112. package/dist/steering/folder-instructions.d.ts +8 -0
  113. package/dist/steering/folder-instructions.js +28 -0
  114. package/dist/steering/select.js +2 -1
  115. package/dist/timed-fetch.d.ts +12 -0
  116. package/dist/timed-fetch.js +83 -0
  117. package/dist/tools/apply-patch.d.ts +52 -0
  118. package/dist/tools/apply-patch.js +197 -0
  119. package/dist/tools/ask-user.d.ts +27 -0
  120. package/dist/tools/ask-user.js +60 -0
  121. package/dist/tools/browser-tools.d.ts +6 -6
  122. package/dist/tools/browser-tools.js +3 -13
  123. package/dist/tools/codebase-search.d.ts +4 -2
  124. package/dist/tools/codebase-search.js +2 -2
  125. package/dist/tools/edit-file.js +5 -3
  126. package/dist/tools/fetch-url.d.ts +7 -0
  127. package/dist/tools/fetch-url.js +52 -19
  128. package/dist/tools/find-symbol.d.ts +2 -2
  129. package/dist/tools/ignore-rules.js +2 -1
  130. package/dist/tools/index.d.ts +5 -1
  131. package/dist/tools/index.js +10 -1
  132. package/dist/tools/memory.d.ts +6 -6
  133. package/dist/tools/notebook.d.ts +2 -2
  134. package/dist/tools/permission-rules.d.ts +32 -0
  135. package/dist/tools/permission-rules.js +182 -0
  136. package/dist/tools/read-file.d.ts +5 -0
  137. package/dist/tools/read-file.js +12 -2
  138. package/dist/tools/read-only.d.ts +1 -1
  139. package/dist/tools/read-only.js +2 -0
  140. package/dist/tools/run-command.d.ts +4 -2
  141. package/dist/tools/run-command.js +12 -8
  142. package/dist/tools/sandbox-loader.js +3 -13
  143. package/dist/tools/search-files.d.ts +8 -2
  144. package/dist/tools/search-files.js +43 -0
  145. package/dist/tools/shared.d.ts +5 -0
  146. package/dist/tools/shared.js +30 -2
  147. package/dist/tools/shell-env.d.ts +7 -0
  148. package/dist/tools/shell-env.js +9 -0
  149. package/dist/tools/sub-agent-wait.d.ts +29 -0
  150. package/dist/tools/sub-agent-wait.js +60 -0
  151. package/dist/tools/sub-agent.d.ts +50 -4
  152. package/dist/tools/sub-agent.js +213 -125
  153. package/dist/tools/text-encoding.d.ts +10 -0
  154. package/dist/tools/text-encoding.js +40 -0
  155. package/dist/tools/todo.d.ts +36 -0
  156. package/dist/tools/todo.js +42 -0
  157. package/dist/tools/write-file.js +15 -9
  158. package/dist/tools/write-tools.d.ts +12 -0
  159. package/dist/tools/write-tools.js +18 -0
  160. package/dist/types.d.ts +98 -0
  161. package/dist/worktree/worktree.d.ts +3 -0
  162. package/dist/worktree/worktree.js +22 -0
  163. package/package.json +1 -1
package/dist/types.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import type { ModelMessage, LanguageModel } from 'ai';
2
2
  import type { z } from 'zod';
3
+ import type { BackgroundAgents } from './agent/background.js';
3
4
  import type { Attachment } from './attachments.js';
5
+ import type { Answer } from './journey/types.js';
4
6
  import type { SkillRegistry } from './skills/types.js';
5
7
  /**
6
8
  * The summarizer takes a chunk of messages and returns a structured
@@ -20,6 +22,11 @@ export type ProviderName = 'openai' | 'anthropic' | 'google' | 'ollama' | 'openr
20
22
  export interface ModelConfig {
21
23
  provider: ProviderName;
22
24
  model: string;
25
+ /**
26
+ * A named provider (preset or `customProviders`) resolved to where the call goes. Set together with
27
+ * `provider: 'openai-compatible'`; `createLanguageModel` then ignores the OPENAI_COMPATIBLE_* environment.
28
+ */
29
+ endpoint?: import('./custom-providers.js').ProviderEndpoint;
23
30
  /**
24
31
  * Reasoning effort for models that support it. Passed straight through
25
32
  * to the AI SDK's own unified `reasoning` call option: no per-provider
@@ -27,6 +34,8 @@ export interface ModelConfig {
27
34
  * behavior, unchanged from today.
28
35
  */
29
36
  reasoningEffort?: 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
37
+ /** Sampling temperature (0 to 2); omitted = the provider's default. Set by an agent file. */
38
+ temperature?: number;
30
39
  }
31
40
  /**
32
41
  * A tool definition as the agent sees it: a name, a Zod parameter schema, and
@@ -34,6 +43,18 @@ export interface ModelConfig {
34
43
  * so it can decide whether to expose the `execute` function (autonomous mode)
35
44
  * or strip it (interactive mode where approval is required).
36
45
  */
46
+ /** A question for the person (`ask_user`). */
47
+ export interface UserQuestion {
48
+ question: string;
49
+ options?: string[];
50
+ allowCustom?: boolean;
51
+ }
52
+ /** One step of the agent's checklist (`todo_write`). */
53
+ export interface TodoItem {
54
+ id: string;
55
+ content: string;
56
+ status: 'pending' | 'in_progress' | 'completed';
57
+ }
37
58
  export interface ToolDefinition<TArgs = unknown, TResult = unknown> {
38
59
  name: string;
39
60
  description: string;
@@ -44,6 +65,8 @@ export interface ToolDefinition<TArgs = unknown, TResult = unknown> {
44
65
  * approval even in `--auto` mode unless the user passes `--yes-destructive`.
45
66
  */
46
67
  destructive?: boolean;
68
+ /** True for tools that only change the session's own bookkeeping (the todo list): never asked about, whatever the mode. */
69
+ noApproval?: boolean;
47
70
  /**
48
71
  * Optional text shown in the approval prompt so the user sees what the call
49
72
  * will change (for example a diff). Failures are swallowed: no preview, still asked.
@@ -84,8 +107,37 @@ export interface ToolContext {
84
107
  * the default `child_process` runner.
85
108
  */
86
109
  commandRunner?: CommandRunner;
110
+ /** The model sub-agents use instead of the parent's (`AgentConfig.subAgentModel`). */
111
+ subAgentModel?: ModelConfig;
112
+ /** Folder `AGENTS.md` paths already shown in this run; `read_file` adds each folder's file once. */
113
+ shownFolderInstructions?: Set<string>;
114
+ /** Longest output `run_command` returns per stream before cutting the middle (`AgentConfig.maxToolOutputChars`). */
115
+ maxOutputChars?: number;
116
+ /** Asks the person a question (`ask_user`); absent when no surface can answer. */
117
+ askUser?: (question: UserQuestion, signal?: AbortSignal) => Promise<string>;
118
+ /**
119
+ * How many questions this run may still ask (a guided journey's interview
120
+ * budget). Mutated in place as questions are asked, and handed to a child
121
+ * agent so a child cannot start a fresh four (RB-14). Absent means no limit.
122
+ */
123
+ askBudget?: {
124
+ max: number;
125
+ used: number;
126
+ };
127
+ /** The run's todo list. `set` also tells the UI (`todo_updated`). Supplied by the Agent. */
128
+ todos?: {
129
+ get(): TodoItem[];
130
+ set(todos: TodoItem[]): void;
131
+ };
87
132
  /** The run's abort signal; long-running tools (run_command) stop when it fires. */
88
133
  abortSignal?: AbortSignal;
134
+ /** The tools the parent runs without asking; a sub-agent starts from the same list (it has no one to ask). */
135
+ autoApproveTools?: readonly string[];
136
+ /** The parent's permission rules and tool sandbox, handed to a sub-agent so it cannot step around them. */
137
+ permissionRules?: import('./tools/permission-rules.js').PermissionConfig;
138
+ toolSandbox?: ToolSandbox;
139
+ /** Where `sub_agent { background: true }` runs go; absent where no surface can show them (the child then runs to completion as usual). */
140
+ background?: BackgroundAgents;
89
141
  /** Longest body `memory_write` accepts, in characters; absent = the default of 1200. */
90
142
  memoryMaxBodyChars?: number;
91
143
  /** A command that outlives its timeout must be stopped, not left running (verification checks; a dev server must not be). */
@@ -233,6 +285,9 @@ export type AgentEvent = {
233
285
  name: string;
234
286
  id: string;
235
287
  args: unknown;
288
+ } | {
289
+ type: 'todo_updated';
290
+ todos: TodoItem[];
236
291
  } | {
237
292
  type: 'iteration';
238
293
  iteration: number;
@@ -514,11 +569,24 @@ export interface AgentConfig {
514
569
  * working directory into the system prompt.
515
570
  */
516
571
  disableProjectInstructions?: boolean;
572
+ /** Extra instruction sources (files, globs, URLs) from the user's `instructions` setting. */
573
+ extraInstructions?: string[];
574
+ /** What connected MCP servers say about using them (see `mcpInstructionsBlock`); added to the system prompt as it is. */
575
+ mcpInstructions?: string;
576
+ /** Longest `run_command` output per stream, in characters. Default 200000. */
577
+ maxToolOutputChars?: number;
517
578
  /**
518
579
  * A ready-made `<project-commands>` block (see `formatProjectCommandsBlock`) telling the model how
519
580
  * this project is checked and built. Added to the system prompt after the project instructions.
520
581
  */
521
582
  projectCommands?: string;
583
+ /**
584
+ * Answers the person gave in a guided journey's interview, rendered into the
585
+ * system prompt as a `<journey-answers>` block. The interview runs before the
586
+ * first agent run, because the blocks are assembled only on a session's first
587
+ * turn (see `buildInitialMessages`), so answers that arrive later cannot enter.
588
+ */
589
+ journeyAnswers?: Answer[];
522
590
  /**
523
591
  * Override the byte budget for memory injected into the system prompt.
524
592
  * Default 8 KB. Larger budgets use more context window but surface more
@@ -609,6 +677,9 @@ export interface AgentConfig {
609
677
  */
610
678
  semanticSearch?: {
611
679
  embedder?: import('./semantic/embedder.js').Embedder;
680
+ extensions?: string[];
681
+ minScore?: number;
682
+ defaultLimit?: number;
612
683
  };
613
684
  /**
614
685
  * After the model finishes a run that edited files, run these commands and
@@ -625,6 +696,13 @@ export interface AgentConfig {
625
696
  * "a destructive tool is never auto-approved"; see `isListedCommand`.
626
697
  */
627
698
  autoApproveCommands?: string[];
699
+ /**
700
+ * Ordered permission rules (`read`, `edit`, `bash`, `webfetch`, `websearch`, `task`, `external_directory`, `*`):
701
+ * the last matching rule wins; `deny` blocks the call, `ask` always prompts (even in `auto` mode), `allow` runs it
702
+ * without a prompt. `.env` reads and commands that reach outside `cwd` prompt unless a rule says otherwise.
703
+ * See tools/permission-rules.ts.
704
+ */
705
+ permissionRules?: import('./tools/permission-rules.js').PermissionConfig;
628
706
  /**
629
707
  * Put a token-budgeted outline of the codebase (files and their top symbol
630
708
  * signatures, ranked by how much other code depends on them) in the system prompt.
@@ -669,6 +747,26 @@ export interface AgentConfig {
669
747
  modelRouter?: ModelRouter;
670
748
  /** Replace how `run_command` executes. See {@link CommandRunner}. */
671
749
  commandRunner?: CommandRunner;
750
+ /**
751
+ * Answers `ask_user`. When absent, or when `auto` is on, the model is not offered the tool.
752
+ */
753
+ askUser?: (question: UserQuestion, signal?: AbortSignal) => Promise<string>;
754
+ /**
755
+ * How many `ask_user` questions this run may ask. Reaches tools as
756
+ * `ToolContext.askBudget` and is handed down to sub-agents. Absent means no limit.
757
+ */
758
+ askBudget?: {
759
+ max: number;
760
+ used: number;
761
+ };
762
+ /** The model `sub_agent` children run on; the parent's model when unset. */
763
+ subAgentModel?: ModelConfig;
764
+ /** Where background sub-agents are kept between messages. Without it `sub_agent { background: true }` runs the child to completion. */
765
+ backgroundAgents?: BackgroundAgents;
766
+ /** The todo list a run starts with (the chat keeps it between turns). */
767
+ initialTodos?: TodoItem[];
768
+ /** Never offer `ask_user`, even when `askUser` is set (a goal run must not stop to ask). */
769
+ disableAskUser?: boolean;
672
770
  /** Custom approval callback. If absent, uses an internal yes/no gate. */
673
771
  approve?: (event: ApprovalRequest) => Promise<ApprovalDecision> | ApprovalDecision;
674
772
  /** Abort signal. */
@@ -237,6 +237,7 @@ export declare class WorktreeManager {
237
237
  * clean up so we don't leak orphan directories.
238
238
  */
239
239
  create(opts?: CreateOptions): Promise<WorktreeInfo>;
240
+ private createNow;
240
241
  /**
241
242
  * Remove a worktree previously created by this manager. By default
242
243
  * refuses if the worktree is dirty (uncommitted changes); pass
@@ -252,6 +253,7 @@ export declare class WorktreeManager {
252
253
  * outcome (e.g. emit "removed worktree + branch X" on the CLI).
253
254
  */
254
255
  remove(worktreePath: string, opts?: RemoveOptions): Promise<RemoveResult>;
256
+ private removeNow;
255
257
  /**
256
258
  * Best-effort cleanup of every worktree this manager created. Used
257
259
  * by the sub_agent tool's finally block so a child crash doesn't
@@ -270,6 +272,7 @@ export declare class WorktreeManager {
270
272
  * leak.
271
273
  */
272
274
  pruneStale(): Promise<number>;
275
+ private pruneStaleNow;
273
276
  /**
274
277
  * List worktrees under our `.forge/worktrees/` directory. Returns
275
278
  * the empty array when none exist or git isn't available.
@@ -284,6 +284,9 @@ export class WorktreeManager {
284
284
  * clean up so we don't leak orphan directories.
285
285
  */
286
286
  async create(opts = {}) {
287
+ return onRepoQueue(this.repoRoot, () => this.createNow(opts));
288
+ }
289
+ async createNow(opts) {
287
290
  // Generate a label + branch name first so we can pass them to git.
288
291
  const label = opts.label ? sanitizeLabel(opts.label) : randomLabel();
289
292
  const branch = opts.branchName ?? `forge/${label}-${await shortSha(this.repoRoot)}`;
@@ -341,6 +344,9 @@ export class WorktreeManager {
341
344
  * outcome (e.g. emit "removed worktree + branch X" on the CLI).
342
345
  */
343
346
  async remove(worktreePath, opts = {}) {
347
+ return onRepoQueue(this.repoRoot, () => this.removeNow(worktreePath, opts));
348
+ }
349
+ async removeNow(worktreePath, opts) {
344
350
  const resolved = path.resolve(worktreePath);
345
351
  const normalizedRoot = path.resolve(this.worktreesRoot);
346
352
  if (!resolved.startsWith(normalizedRoot + path.sep) && resolved !== normalizedRoot) {
@@ -395,6 +401,9 @@ export class WorktreeManager {
395
401
  * leak.
396
402
  */
397
403
  async pruneStale() {
404
+ return onRepoQueue(this.repoRoot, () => this.pruneStaleNow());
405
+ }
406
+ async pruneStaleNow() {
398
407
  let pruned = 0;
399
408
  try {
400
409
  const { stdout } = await runGit(this.repoRoot, [
@@ -626,6 +635,19 @@ export function canonicalPath(p) {
626
635
  * with the stderr message in the Error. We use `execFile` (not `exec`)
627
636
  * so user-controlled input can't shell-inject.
628
637
  */
638
+ const repoQueues = new Map();
639
+ /**
640
+ * Run `work` after every earlier queued worktree change of the same repository has finished. Parallel steps create
641
+ * and remove worktrees at the same moment, and two `git worktree add` / `remove` calls on one repository fight over
642
+ * git's lock files; one of them then fails at random, mostly under load. The agents still run in parallel: only these
643
+ * short git changes take turns.
644
+ */
645
+ function onRepoQueue(repoRoot, work) {
646
+ const key = canonicalPath(repoRoot);
647
+ const next = (repoQueues.get(key) ?? Promise.resolve()).then(work, work);
648
+ repoQueues.set(key, next.catch(() => undefined));
649
+ return next;
650
+ }
629
651
  async function runGit(cwd, args) {
630
652
  return await execFileAsync('git', args, { cwd, maxBuffer: 16 * 1024 * 1024 });
631
653
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikarmic/forge-core",
3
- "version": "5.0.0",
3
+ "version": "5.1.1",
4
4
  "description": "Agent engine for IKarmic AI Forge. Model loop, tools, MCP, sandbox, memory, compaction, plan mode, skills, hooks.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",