salmon-loop 0.2.3 → 0.2.16

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 (234) hide show
  1. package/dist/cli/argv/headless-detection.js +27 -0
  2. package/dist/cli/chat-flow.js +11 -0
  3. package/dist/cli/chat.js +161 -24
  4. package/dist/cli/commands/chat.js +30 -24
  5. package/dist/cli/commands/context.js +15 -3
  6. package/dist/cli/commands/flow-mode.js +63 -0
  7. package/dist/cli/commands/help-format.js +12 -0
  8. package/dist/cli/commands/registry.js +6 -7
  9. package/dist/cli/commands/run/benchmark-artifacts.js +41 -0
  10. package/dist/cli/commands/run/config-resolution.js +30 -24
  11. package/dist/cli/commands/run/early-errors.js +23 -0
  12. package/dist/cli/commands/run/handler.js +131 -44
  13. package/dist/cli/commands/run/headless-error-writer.js +8 -0
  14. package/dist/cli/commands/run/loop-params.js +3 -0
  15. package/dist/cli/commands/run/mode.js +2 -5
  16. package/dist/cli/commands/run/parse-options.js +18 -2
  17. package/dist/cli/commands/run/persist-session.js +10 -1
  18. package/dist/cli/commands/run/preflight.js +10 -0
  19. package/dist/cli/commands/run/reporter-factory.js +4 -0
  20. package/dist/cli/commands/run/runtime-llm.js +38 -11
  21. package/dist/cli/commands/run/runtime-options.js +2 -2
  22. package/dist/cli/commands/run/validate-options.js +0 -5
  23. package/dist/cli/commands/run/verbose.js +2 -7
  24. package/dist/cli/commands/serve.js +117 -90
  25. package/dist/cli/commands/tool-names.js +78 -78
  26. package/dist/cli/headless/anthropic-stream-normalized-encoder.js +6 -1
  27. package/dist/cli/headless/json-protocol.js +37 -0
  28. package/dist/cli/headless/native-stream-normalized-encoder.js +6 -1
  29. package/dist/cli/headless/protocol-metadata.js +22 -0
  30. package/dist/cli/headless/stream-json-protocol.js +34 -1
  31. package/dist/cli/index.js +6 -4
  32. package/dist/cli/locales/en.js +32 -6
  33. package/dist/cli/program-bootstrap.js +14 -4
  34. package/dist/cli/program-commands.js +9 -1
  35. package/dist/cli/program-options.js +1 -0
  36. package/dist/cli/reporters/anthropic-stream.js +7 -1
  37. package/dist/cli/reporters/json.js +4 -0
  38. package/dist/cli/reporters/stream-json.js +17 -2
  39. package/dist/cli/run-cli.js +5 -3
  40. package/dist/cli/slash/runtime.js +30 -15
  41. package/dist/cli/ui/components/CommandInput.js +7 -3
  42. package/dist/cli/ui/components/CommandSuggestionList.js +1 -1
  43. package/dist/cli/utils/command-option-source.js +13 -0
  44. package/dist/cli/utils/output-format.js +6 -0
  45. package/dist/cli/utils/resolve-cli-config.js +98 -0
  46. package/dist/cli/utils/verbose-level.js +8 -0
  47. package/dist/cli/utils/verify-resolver.js +8 -4
  48. package/dist/cli/utils/worktree-prepare-resolver.js +7 -3
  49. package/dist/core/adapters/fs/file-adapter.js +6 -0
  50. package/dist/core/adapters/fs/filesystem.js +2 -1
  51. package/dist/core/adapters/git/git-adapter.js +78 -1
  52. package/dist/core/benchmark/patch-artifact.js +124 -0
  53. package/dist/core/benchmark/swe-bench.js +25 -0
  54. package/dist/core/config/load.js +39 -18
  55. package/dist/core/config/merge.js +27 -0
  56. package/dist/core/config/paths.js +24 -5
  57. package/dist/core/config/resolve-llm.js +12 -0
  58. package/dist/core/config/resolve.js +7 -5
  59. package/dist/core/config/resolvers/server.js +0 -6
  60. package/dist/core/config/validate.js +94 -21
  61. package/dist/core/context/gatherers/metadata-gatherer.js +1 -0
  62. package/dist/core/context/gatherers/ripgrep-gatherer.js +84 -2
  63. package/dist/core/context/keywords.js +18 -4
  64. package/dist/core/context/service-deps.js +2 -2
  65. package/dist/core/context/service.js +8 -0
  66. package/dist/core/context/steps/context-gather.js +38 -0
  67. package/dist/core/context/summarization/summarizer.js +55 -12
  68. package/dist/core/context/targeting/target-resolver.js +4 -4
  69. package/dist/core/extensions/index.js +23 -5
  70. package/dist/core/extensions/paths.js +31 -0
  71. package/dist/core/extensions/schemas.js +8 -5
  72. package/dist/core/facades/cli-chat.js +6 -2
  73. package/dist/core/facades/cli-command-chat.js +2 -1
  74. package/dist/core/facades/cli-command-tool-names.js +2 -0
  75. package/dist/core/facades/cli-context.js +1 -0
  76. package/dist/core/facades/cli-observability.js +1 -1
  77. package/dist/core/facades/cli-run-handler.js +4 -2
  78. package/dist/core/facades/cli-run-persist-session.js +1 -0
  79. package/dist/core/facades/cli-serve.js +2 -4
  80. package/dist/core/facades/cli-utils-worktree.js +1 -1
  81. package/dist/core/failure/diagnostics.js +53 -1
  82. package/dist/core/grizzco/dsl/llm-strategy.js +4 -1
  83. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +67 -9
  84. package/dist/core/grizzco/engine/pipeline/pipeline.js +6 -2
  85. package/dist/core/grizzco/engine/transaction/attempt-failure.js +90 -15
  86. package/dist/core/grizzco/engine/transaction/report-mapper.js +17 -3
  87. package/dist/core/grizzco/engine/transaction/transaction-runner.js +173 -7
  88. package/dist/core/grizzco/flows/AutopilotFlow.js +18 -0
  89. package/dist/core/grizzco/flows/flow-dispatch.js +11 -0
  90. package/dist/core/grizzco/steps/answer.js +13 -14
  91. package/dist/core/grizzco/steps/autopilot.js +396 -0
  92. package/dist/core/grizzco/steps/cache-sharing.js +29 -0
  93. package/dist/core/grizzco/steps/explore.js +37 -21
  94. package/dist/core/grizzco/steps/generateReview.js +2 -5
  95. package/dist/core/grizzco/steps/patch/apply-check.js +10 -0
  96. package/dist/core/grizzco/steps/patch/diff-normalization.js +70 -0
  97. package/dist/core/grizzco/steps/patch/diff-salvage.js +46 -0
  98. package/dist/core/grizzco/steps/patch/prompt-input.js +42 -0
  99. package/dist/core/grizzco/steps/patch.js +105 -146
  100. package/dist/core/grizzco/steps/plan.js +101 -25
  101. package/dist/core/grizzco/steps/preflight.js +5 -3
  102. package/dist/core/grizzco/steps/request-assembly.js +78 -0
  103. package/dist/core/grizzco/steps/research.js +39 -36
  104. package/dist/core/grizzco/steps/tool-runtime.js +47 -0
  105. package/dist/core/grizzco/steps/verify-shared.js +23 -0
  106. package/dist/core/grizzco/steps/verify.js +13 -21
  107. package/dist/core/intent/chat-intent.js +0 -4
  108. package/dist/core/llm/ai-sdk/chat-executor.js +2 -0
  109. package/dist/core/llm/ai-sdk/high-level-phase-specs.js +63 -0
  110. package/dist/core/llm/ai-sdk/message-mapper.js +40 -10
  111. package/dist/core/llm/ai-sdk/provider-factory.js +14 -0
  112. package/dist/core/llm/ai-sdk/request-params.js +74 -1
  113. package/dist/core/llm/ai-sdk/result-mapper.js +16 -0
  114. package/dist/core/llm/ai-sdk.js +112 -27
  115. package/dist/core/llm/capabilities.js +12 -0
  116. package/dist/core/llm/contracts/repair.js +36 -30
  117. package/dist/core/llm/errors.js +83 -2
  118. package/dist/core/llm/message-composition.js +7 -22
  119. package/dist/core/llm/phase-router.js +29 -10
  120. package/dist/core/llm/redact.js +28 -3
  121. package/dist/core/llm/registry.js +2 -0
  122. package/dist/core/llm/request-augmentation.js +55 -0
  123. package/dist/core/llm/request-envelope.js +334 -0
  124. package/dist/core/llm/shared-request-assembly.js +35 -0
  125. package/dist/core/llm/stream-utils.js +13 -4
  126. package/dist/core/llm/utils.js +18 -29
  127. package/dist/core/memory/relevant-retrieval.js +144 -0
  128. package/dist/core/observability/logger.js +11 -2
  129. package/dist/core/patch/diff.js +1 -0
  130. package/dist/core/prompts/registry.js +39 -2
  131. package/dist/core/prompts/runtime.js +50 -12
  132. package/dist/core/prompts/templates/phases/patch_user.hbs +2 -5
  133. package/dist/core/prompts/templates/phases/research_user.hbs +11 -0
  134. package/dist/core/prompts/templates/phases/review_user.hbs +3 -0
  135. package/dist/core/prompts/templates/system/answer_system.hbs +5 -0
  136. package/dist/core/prompts/templates/system/autopilot_system.hbs +11 -0
  137. package/dist/core/prompts/templates/system/explore_system.hbs +14 -23
  138. package/dist/core/prompts/templates/system/main_system.hbs +4 -16
  139. package/dist/core/prompts/templates/system/patch_system.hbs +39 -8
  140. package/dist/core/prompts/templates/system/plan_system.hbs +86 -1
  141. package/dist/core/prompts/templates/system/research_system.hbs +2 -0
  142. package/dist/core/protocols/a2a/agent-card.js +3 -2
  143. package/dist/core/protocols/a2a/sdk/executor.js +8 -6
  144. package/dist/core/protocols/a2a/sdk/server.js +0 -1
  145. package/dist/core/protocols/acp/formal-agent.js +221 -55
  146. package/dist/core/protocols/acp/handlers.js +5 -1
  147. package/dist/core/protocols/acp/permission-provider.js +21 -1
  148. package/dist/core/protocols/shared/execution-request.js +24 -0
  149. package/dist/core/protocols/shared/flow-mode-mapping.js +23 -0
  150. package/dist/core/public-capabilities/flow-mode-metadata.js +39 -0
  151. package/dist/core/public-capabilities/projections.js +29 -0
  152. package/dist/core/public-capabilities/registry.js +26 -0
  153. package/dist/core/public-capabilities/types.js +2 -0
  154. package/dist/core/runtime/agent-server-runtime.js +47 -43
  155. package/dist/core/runtime/execution-profile.js +67 -0
  156. package/dist/core/session/artifact-state.js +160 -0
  157. package/dist/core/session/compaction/index.js +183 -0
  158. package/dist/core/session/compaction/microcompact.js +78 -0
  159. package/dist/core/session/compaction/tracking.js +48 -0
  160. package/dist/core/session/compaction/types.js +11 -0
  161. package/dist/core/session/compression.js +12 -4
  162. package/dist/core/session/manager.js +247 -10
  163. package/dist/core/session/pruning-strategy.js +55 -9
  164. package/dist/core/session/replacement-preview-provider.js +24 -0
  165. package/dist/core/session/replacement-state.js +131 -0
  166. package/dist/core/session/resume-repair/pipeline.js +79 -0
  167. package/dist/core/session/resume-repair/stages/load-raw-archive-state.js +40 -0
  168. package/dist/core/session/resume-repair/stages/reattach-runtime-state.js +8 -0
  169. package/dist/core/session/resume-repair/stages/recover-orphaned-branches.js +10 -0
  170. package/dist/core/session/resume-repair/stages/relink-boundary-and-tail.js +36 -0
  171. package/dist/core/session/resume-repair/stages/replay-startup-hooks.js +23 -0
  172. package/dist/core/session/resume-repair/stages/rescue-stale-metadata.js +17 -0
  173. package/dist/core/session/resume-repair/types.js +2 -0
  174. package/dist/core/session/summary-sync.js +164 -13
  175. package/dist/core/session/token-tracker.js +6 -0
  176. package/dist/core/skills/audit.js +34 -0
  177. package/dist/core/skills/bridge.js +84 -7
  178. package/dist/core/skills/discovery.js +94 -0
  179. package/dist/core/skills/feature-flags.js +52 -0
  180. package/dist/core/skills/index.js +1 -1
  181. package/dist/core/skills/loader.js +195 -20
  182. package/dist/core/skills/parser.js +296 -24
  183. package/dist/core/skills/permissions.js +117 -0
  184. package/dist/core/skills/runtime/MicroTaskRunner.js +10 -4
  185. package/dist/core/skills/runtime/SkillRunner.js +240 -61
  186. package/dist/core/strata/layers/shadow-driver/shadow-driver.js +37 -7
  187. package/dist/core/strata/layers/worktree.js +70 -13
  188. package/dist/core/strata/runtime/synchronizer.js +29 -2
  189. package/dist/core/streaming/stream-assembler.js +75 -31
  190. package/dist/core/sub-agent/context-snapshot.js +156 -0
  191. package/dist/core/sub-agent/core/loop.js +1 -1
  192. package/dist/core/sub-agent/core/manager.js +119 -20
  193. package/dist/core/sub-agent/dispatch-policy.js +29 -0
  194. package/dist/core/sub-agent/prefix-consistency.js +48 -0
  195. package/dist/core/sub-agent/registry-defaults.js +4 -0
  196. package/dist/core/sub-agent/tools/task-spawn.js +79 -2
  197. package/dist/core/sub-agent/types.js +134 -5
  198. package/dist/core/tools/audit.js +13 -4
  199. package/dist/core/tools/builtin/ast-grep.js +1 -1
  200. package/dist/core/tools/builtin/ast.js +1 -1
  201. package/dist/core/tools/builtin/benchmark.js +360 -0
  202. package/dist/core/tools/builtin/code-search/backends/rg.js +2 -1
  203. package/dist/core/tools/builtin/code-search/executor.js +6 -1
  204. package/dist/core/tools/builtin/code-search/spec.js +26 -2
  205. package/dist/core/tools/builtin/fs.js +256 -23
  206. package/dist/core/tools/builtin/git.js +2 -2
  207. package/dist/core/tools/builtin/index.js +51 -2
  208. package/dist/core/tools/builtin/interaction.js +8 -1
  209. package/dist/core/tools/builtin/plan.js +37 -15
  210. package/dist/core/tools/builtin/shell.js +1 -1
  211. package/dist/core/tools/loader.js +39 -16
  212. package/dist/core/tools/mapper.js +17 -3
  213. package/dist/core/tools/parallel/scheduler.js +35 -4
  214. package/dist/core/tools/permissions/permission-rules.js +5 -10
  215. package/dist/core/tools/policy.js +6 -1
  216. package/dist/core/tools/recoverable-tool-errors.js +10 -0
  217. package/dist/core/tools/router.js +24 -6
  218. package/dist/core/tools/session.js +458 -48
  219. package/dist/core/tools/tool-visibility.js +62 -0
  220. package/dist/core/tools/types.js +9 -1
  221. package/dist/core/types/execution.js +4 -0
  222. package/dist/core/types/flow-mode.js +8 -0
  223. package/dist/core/utils/path.js +52 -0
  224. package/dist/core/verification/runner.js +4 -1
  225. package/dist/interfaces/cli/task-runner.js +4 -3
  226. package/dist/languages/typescript/index.js +4 -1
  227. package/dist/locales/en.js +87 -2
  228. package/dist/utils/eol.js +1 -1
  229. package/package.json +15 -8
  230. package/scripts/fix-es-abstract-compat.js +77 -0
  231. package/dist/core/runtime/fastify-server-bundle.js +0 -26
  232. package/dist/core/runtime/sidecar-fastify-plugin.js +0 -35
  233. package/dist/core/runtime/sidecar-paths.js +0 -47
  234. package/dist/core/runtime/sidecar-route-catalog.js +0 -103
@@ -1,4 +1,5 @@
1
1
  export function buildA2AAgentCard(input) {
2
+ const capabilities = input.capabilities ?? [];
2
3
  const securitySchemes = input.security.length > 0
3
4
  ? Object.fromEntries(input.security.map((scheme, index) => [
4
5
  scheme.name ?? `${scheme.type}-${index}`,
@@ -18,10 +19,10 @@ export function buildA2AAgentCard(input) {
18
19
  streaming: input.capabilityOptions?.streaming ?? true,
19
20
  stateTransitionHistory: input.capabilityOptions?.stateTransitionHistory ?? true,
20
21
  },
21
- skills: input.capabilities.map((capability) => ({
22
+ skills: capabilities.map((capability) => ({
22
23
  id: capability.id,
23
24
  name: capability.title,
24
- description: capability.title,
25
+ description: capability.description ?? capability.title,
25
26
  tags: [],
26
27
  })),
27
28
  securitySchemes,
@@ -1,4 +1,6 @@
1
1
  import { InMemoryTaskStore } from '@a2a-js/sdk/server';
2
+ import { buildCanonicalExecutionRequest, buildInstructionFromParts, } from '../../shared/execution-request.js';
3
+ import { parseA2ASkillFlowMode } from '../../shared/flow-mode-mapping.js';
2
4
  export function createA2AInteractionExecutor(deps) {
3
5
  const store = deps.taskStore ?? new InMemoryTaskStore();
4
6
  const metadataByTaskId = new Map();
@@ -10,7 +12,7 @@ export function createA2AInteractionExecutor(deps) {
10
12
  const terminalPublished = new Set();
11
13
  return {
12
14
  async execute(requestContext, executionEventBus) {
13
- const capability = deps.capabilityResolver?.(requestContext.userMessage) ?? 'patch';
15
+ const capability = parseA2ASkillFlowMode(deps.capabilityResolver?.(requestContext.userMessage)) ?? 'autopilot';
14
16
  const pendingEvents = [];
15
17
  let resolvedTaskId = null;
16
18
  let cleanedUp = false;
@@ -41,12 +43,13 @@ export function createA2AInteractionExecutor(deps) {
41
43
  }
42
44
  };
43
45
  try {
44
- const { task } = await deps.facade.createTask({
46
+ const executionRequest = buildCanonicalExecutionRequest({
45
47
  capability,
46
- request: { instruction: extractInstruction(requestContext.userMessage) },
48
+ instruction: extractInstruction(requestContext.userMessage),
47
49
  // Pass SDK's taskId to facade to ensure consistency with eventBusManager
48
50
  taskId: requestContext.taskId,
49
51
  });
52
+ const { task } = await deps.facade.createTask(executionRequest);
50
53
  resolvedTaskId = task.id;
51
54
  cleanupByTaskId.set(task.id, cleanup);
52
55
  metadataByTaskId.set(task.id, {
@@ -216,9 +219,8 @@ export function createA2AInteractionExecutor(deps) {
216
219
  function extractInstruction(message) {
217
220
  const textParts = message.parts
218
221
  .filter((part) => part.kind === 'text')
219
- .map((part) => part.text.trim())
220
- .filter(Boolean);
221
- return textParts.join('\n') || 'Run task';
222
+ .map((part) => part.text);
223
+ return buildInstructionFromParts(textParts, { fallbackInstruction: 'Run task' });
222
224
  }
223
225
  function delay(ms) {
224
226
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -7,7 +7,6 @@ export function createA2ASdkExpressApp(options) {
7
7
  const userBuilder = options.userBuilder ?? ((_) => UserBuilder.noAuthentication());
8
8
  const app = express();
9
9
  app.disable('x-powered-by');
10
- app.use(express.json());
11
10
  const agentCardPath = options.agentCardPath ?? '/.well-known/agent-card.json';
12
11
  app.use(agentCardPath, agentCardHandler({
13
12
  agentCardProvider: () => requestHandler.getAgentCard(),
@@ -6,7 +6,11 @@ import { defaultPathAdapter } from '../../adapters/path/path-adapter.js';
6
6
  import { inferTurnStopReasonFromFailure } from '../../interaction/turn-stop-reason.js';
7
7
  import { recordAuditEvent } from '../../observability/audit-trail.js';
8
8
  import { readPlan } from '../../plan/index.js';
9
+ import { toAcpPublicModes } from '../../public-capabilities/projections.js';
10
+ import { buildPublicCapabilityRegistry } from '../../public-capabilities/registry.js';
9
11
  import { parseSlashInput } from '../../slash/parser.js';
12
+ import { buildCanonicalExecutionRequest } from '../shared/execution-request.js';
13
+ import { parseAcpFlowMode } from '../shared/flow-mode-mapping.js';
10
14
  import { createAcpCommandRunner } from './acp-command-runner.js';
11
15
  import { createAcpFileSystem } from './acp-filesystem.js';
12
16
  import { createAcpSessionStore, isTerminalTaskEvent } from './handlers.js';
@@ -34,7 +38,8 @@ const ACP_PERMISSION_POLICY_CONFIG_ID = '_salmonloop_permission_policy';
34
38
  const ACP_MODE_CONFIG_ID = '_salmonloop_mode';
35
39
  const ACP_PERMISSION_POLICY_ASK = 'ask';
36
40
  const ACP_PERMISSION_POLICY_DENY_ALL = 'deny_all';
37
- const ACP_DEFAULT_MODE_ID = 'interactive';
41
+ const ACP_PERMISSION_POLICY_ALLOW_ALL = 'allow_all';
42
+ const ACP_DEFAULT_MODE_ID = 'autopilot';
38
43
  const ACP_SESSION_STORE_MAX_ENTRIES = 200;
39
44
  const ACP_SESSION_STORE_MAX_AGE_MS = 1000 * 60 * 60 * 24 * 30;
40
45
  const ACP_SESSION_STORE_LOCK_STALE_MS = 1000 * 30;
@@ -52,6 +57,16 @@ function isAbsolutePath(filePath) {
52
57
  return true; // UNC path
53
58
  return false;
54
59
  }
60
+ function deriveSessionTitleFromCwd(cwd) {
61
+ const trimmed = cwd.replace(/[\\/]+$/, '');
62
+ if (!trimmed)
63
+ return cwd;
64
+ const segments = trimmed.split(/[\\/]/).filter(Boolean);
65
+ const basename = segments.at(-1);
66
+ if (basename && basename.trim())
67
+ return basename;
68
+ return trimmed;
69
+ }
55
70
  function ensureMarkdownParagraphBreak(text) {
56
71
  if (!text)
57
72
  return text;
@@ -71,14 +86,18 @@ function buildJsonResourceContentBlock(data) {
71
86
  },
72
87
  };
73
88
  }
74
- const defaultPromptCapabilities = {
75
- image: false,
76
- audio: false,
77
- embeddedContext: false,
78
- };
79
89
  const ACP_AVAILABLE_COMMANDS = [
80
90
  { name: 'help', description: text.acp.slashHelpDescription },
81
91
  ];
92
+ const ACP_PUBLIC_MODES = toAcpPublicModes(buildPublicCapabilityRegistry());
93
+ const ACP_PUBLIC_MODE_IDS = new Set(ACP_PUBLIC_MODES.map((mode) => mode.id));
94
+ function resolveExposedAcpModeId(value, fallback = ACP_DEFAULT_MODE_ID) {
95
+ const resolvedModeId = parseAcpFlowMode(value);
96
+ if (resolvedModeId && ACP_PUBLIC_MODE_IDS.has(resolvedModeId)) {
97
+ return resolvedModeId;
98
+ }
99
+ return fallback;
100
+ }
82
101
  function formatResourceLink(block) {
83
102
  const title = block.title ?? block.name ?? block.uri;
84
103
  const description = block.description ? ` - ${block.description}` : '';
@@ -224,11 +243,10 @@ function loopEventToSessionUpdate(event) {
224
243
  return null;
225
244
  }
226
245
  }
227
- function createSessionRuntimeState() {
228
- return createSessionRuntimeStateFromPersisted();
229
- }
230
246
  function isPermissionPolicyValue(value) {
231
- return value === ACP_PERMISSION_POLICY_ASK || value === ACP_PERMISSION_POLICY_DENY_ALL;
247
+ return (value === ACP_PERMISSION_POLICY_ASK ||
248
+ value === ACP_PERMISSION_POLICY_DENY_ALL ||
249
+ value === ACP_PERMISSION_POLICY_ALLOW_ALL);
232
250
  }
233
251
  function buildConfigOptions(state) {
234
252
  return [
@@ -249,26 +267,24 @@ function buildConfigOptions(state) {
249
267
  name: text.acp.permissionPolicyDenyAllName,
250
268
  description: text.acp.permissionPolicyDenyAllDescription,
251
269
  },
270
+ {
271
+ value: ACP_PERMISSION_POLICY_ALLOW_ALL,
272
+ name: text.acp.permissionPolicyAllowAllName,
273
+ description: text.acp.permissionPolicyAllowAllDescription,
274
+ },
252
275
  ],
253
276
  },
254
277
  {
255
278
  type: 'select',
256
279
  id: ACP_MODE_CONFIG_ID,
257
- name: 'Session Mode',
258
- description: text.acp.modeInteractiveDescription,
280
+ name: 'Execution Flow',
281
+ description: 'Choose how the agent should execute this session.',
259
282
  currentValue: state.modeId,
260
- options: [
261
- {
262
- value: 'interactive',
263
- name: 'Interactive',
264
- description: text.acp.modeInteractiveDescription,
265
- },
266
- {
267
- value: 'yolo',
268
- name: 'YOLO',
269
- description: text.acp.modeYoloDescription,
270
- },
271
- ],
283
+ options: ACP_PUBLIC_MODES.map((mode) => ({
284
+ value: mode.id,
285
+ name: mode.name,
286
+ description: mode.description,
287
+ })),
272
288
  },
273
289
  ];
274
290
  }
@@ -294,12 +310,28 @@ function buildAvailableCommandsUpdateIfChanged(state) {
294
310
  availableCommands,
295
311
  };
296
312
  }
297
- function isSessionModeId(value) {
298
- return value === 'interactive' || value === 'yolo';
313
+ function buildSessionInfoUpdateIfChanged(session, state) {
314
+ const title = typeof session.title === 'string' ? session.title : null;
315
+ const updatedAt = typeof session.updatedAt === 'string' ? session.updatedAt : null;
316
+ const digest = JSON.stringify({ title, updatedAt });
317
+ if (digest === state.lastSessionInfoDigest)
318
+ return null;
319
+ state.lastSessionInfoDigest = digest;
320
+ return {
321
+ sessionUpdate: 'session_info_update',
322
+ title,
323
+ updatedAt,
324
+ };
299
325
  }
300
326
  function buildCurrentModeUpdate(modeId) {
301
327
  return { sessionUpdate: 'current_mode_update', currentModeId: modeId };
302
328
  }
329
+ function buildModesState(modeId) {
330
+ return {
331
+ currentModeId: modeId,
332
+ availableModes: ACP_PUBLIC_MODES.map((mode) => ({ ...mode })),
333
+ };
334
+ }
303
335
  function buildCurrentModeUpdateIfChanged(state) {
304
336
  const digest = state.modeId;
305
337
  if (digest === state.lastModeDigest)
@@ -307,18 +339,28 @@ function buildCurrentModeUpdateIfChanged(state) {
307
339
  state.lastModeDigest = digest;
308
340
  return buildCurrentModeUpdate(state.modeId);
309
341
  }
342
+ function getLegacyPermissionPolicyForModeValue(value) {
343
+ const normalized = String(value ?? '')
344
+ .trim()
345
+ .toLowerCase();
346
+ if (normalized === 'interactive')
347
+ return ACP_PERMISSION_POLICY_ASK;
348
+ if (normalized === 'yolo')
349
+ return ACP_PERMISSION_POLICY_ALLOW_ALL;
350
+ return null;
351
+ }
310
352
  function getPermissionPolicyForAuthorization(state) {
311
- if (state.modeId === 'yolo')
312
- return 'allow_all';
313
353
  return state.permissionPolicy;
314
354
  }
315
355
  function createSessionRuntimeStateFromPersisted(input) {
356
+ const defaultPermissionPolicy = isPermissionPolicyValue(String(input?.defaultPermissionPolicy))
357
+ ? input?.defaultPermissionPolicy
358
+ : ACP_PERMISSION_POLICY_ASK;
316
359
  const permissionPolicy = isPermissionPolicyValue(String(input?.permissionPolicy))
317
360
  ? input?.permissionPolicy
318
- : ACP_PERMISSION_POLICY_ASK;
319
- const modeId = isSessionModeId(String(input?.modeId))
320
- ? input?.modeId
321
- : (input?.defaultModeId ?? ACP_DEFAULT_MODE_ID);
361
+ : defaultPermissionPolicy;
362
+ const defaultModeId = resolveExposedAcpModeId(input?.defaultModeId);
363
+ const modeId = resolveExposedAcpModeId(input?.modeId, defaultModeId);
322
364
  const state = {
323
365
  runtimePlanSessionId: null,
324
366
  runtimePlanPathHint: null,
@@ -326,6 +368,7 @@ function createSessionRuntimeStateFromPersisted(input) {
326
368
  lastCommandsDigest: null,
327
369
  lastConfigDigest: null,
328
370
  lastModeDigest: null,
371
+ lastSessionInfoDigest: null,
329
372
  permissionPolicy,
330
373
  modeId,
331
374
  };
@@ -456,6 +499,15 @@ export function createAcpFormalAgent(deps) {
456
499
  terminal: false,
457
500
  };
458
501
  const loadSessionCapability = deps.capabilityPolicy?.loadSession ?? true;
502
+ const promptCapabilities = {
503
+ image: deps.capabilityPolicy?.promptCapabilities?.image ?? false,
504
+ audio: deps.capabilityPolicy?.promptCapabilities?.audio ?? false,
505
+ embeddedContext: deps.capabilityPolicy?.promptCapabilities?.embeddedContext ?? false,
506
+ };
507
+ const mcpCapabilities = {
508
+ http: deps.capabilityPolicy?.mcpCapabilities?.http ?? false,
509
+ sse: deps.capabilityPolicy?.mcpCapabilities?.sse ?? false,
510
+ };
459
511
  const sessionPersistencePath = deps.sessionPersistencePath;
460
512
  const sessionStorePolicy = {
461
513
  maxEntries: deps.sessionStorePolicy?.maxEntries ?? ACP_SESSION_STORE_MAX_ENTRIES,
@@ -500,8 +552,10 @@ export function createAcpFormalAgent(deps) {
500
552
  title: entry.title,
501
553
  taskId: undefined,
502
554
  history: [],
503
- permissionPolicy: ACP_PERMISSION_POLICY_ASK,
504
- modeId: deps.defaultModeId ?? ACP_DEFAULT_MODE_ID,
555
+ permissionPolicy: isPermissionPolicyValue(String(deps.defaultPermissionPolicy))
556
+ ? deps.defaultPermissionPolicy
557
+ : ACP_PERMISSION_POLICY_ASK,
558
+ modeId: resolveExposedAcpModeId(deps.defaultModeId),
505
559
  })),
506
560
  };
507
561
  }
@@ -711,6 +765,7 @@ export function createAcpFormalAgent(deps) {
711
765
  if (!sessionRuntime.has(stored.id)) {
712
766
  sessionRuntime.set(stored.id, createSessionRuntimeStateFromPersisted({
713
767
  permissionPolicy: stored.permissionPolicy,
768
+ defaultPermissionPolicy: deps.defaultPermissionPolicy,
714
769
  modeId: stored.modeId,
715
770
  defaultModeId: deps.defaultModeId,
716
771
  }));
@@ -779,6 +834,21 @@ export function createAcpFormalAgent(deps) {
779
834
  async function emitSessionUpdate(sessionId, update) {
780
835
  await deps.conn.sessionUpdate({ sessionId, update });
781
836
  }
837
+ async function emitSessionInfoUpdateBestEffort(sessionId) {
838
+ const session = sessions.get(sessionId);
839
+ if (!session)
840
+ return;
841
+ const state = ensureSessionRuntimeState(sessionId);
842
+ const update = buildSessionInfoUpdateIfChanged(session, state);
843
+ if (!update)
844
+ return;
845
+ try {
846
+ await emitSessionUpdate(sessionId, update);
847
+ }
848
+ catch {
849
+ // Best-effort: do not fail the request due to notification delivery issues.
850
+ }
851
+ }
782
852
  async function emitRuntimePlanUpdateIfNeeded(params) {
783
853
  if (!shouldRefreshPlanForEvent(params.event))
784
854
  return;
@@ -833,10 +903,10 @@ export function createAcpFormalAgent(deps) {
833
903
  const existing = sessionRuntime.get(sessionId);
834
904
  if (existing)
835
905
  return existing;
836
- const created = createSessionRuntimeState();
837
- if (deps.defaultModeId) {
838
- created.modeId = deps.defaultModeId;
839
- }
906
+ const created = createSessionRuntimeStateFromPersisted({
907
+ defaultPermissionPolicy: deps.defaultPermissionPolicy,
908
+ defaultModeId: deps.defaultModeId,
909
+ });
840
910
  sessionRuntime.set(sessionId, created);
841
911
  return created;
842
912
  }
@@ -846,14 +916,22 @@ export function createAcpFormalAgent(deps) {
846
916
  throw new RequestError(-32602, 'Invalid params: protocolVersion is required');
847
917
  }
848
918
  clientCapabilities = params.clientCapabilities;
919
+ // Protocol version negotiation:
920
+ // - If the client's requested version is supported, return the same version
921
+ // - Otherwise, return the latest version the agent supports
922
+ // Currently, the agent only supports protocol version 1
923
+ const supportedProtocolVersion = PROTOCOL_VERSION;
924
+ const negotiatedVersion = params.protocolVersion <= supportedProtocolVersion
925
+ ? params.protocolVersion
926
+ : supportedProtocolVersion;
849
927
  return {
850
- protocolVersion: PROTOCOL_VERSION,
928
+ protocolVersion: negotiatedVersion,
851
929
  agentInfo: deps.agentInfo,
852
930
  authMethods: [],
853
931
  agentCapabilities: {
854
932
  loadSession: loadSessionCapability,
855
- promptCapabilities: defaultPromptCapabilities,
856
- mcpCapabilities: { http: false, sse: false },
933
+ promptCapabilities: promptCapabilities,
934
+ mcpCapabilities: mcpCapabilities,
857
935
  sessionCapabilities: {},
858
936
  },
859
937
  };
@@ -866,9 +944,14 @@ export function createAcpFormalAgent(deps) {
866
944
  if (!isAbsolutePath(params.cwd)) {
867
945
  throw new RequestError(-32602, 'Invalid params: cwd must be an absolute path');
868
946
  }
869
- const session = sessions.create({ cwd: params.cwd, mcpServers: params.mcpServers ?? [] });
947
+ const session = sessions.create({
948
+ cwd: params.cwd,
949
+ mcpServers: params.mcpServers ?? [],
950
+ title: deriveSessionTitleFromCwd(params.cwd),
951
+ });
870
952
  await persistSessionsBestEffort();
871
953
  const runtimeState = ensureSessionRuntimeState(session.id);
954
+ await emitSessionInfoUpdateBestEffort(session.id);
872
955
  // Restore session state on creation
873
956
  const commandsUpdate = buildAvailableCommandsUpdateIfChanged(runtimeState);
874
957
  if (commandsUpdate)
@@ -894,6 +977,7 @@ export function createAcpFormalAgent(deps) {
894
977
  return {
895
978
  sessionId: session.id,
896
979
  configOptions: buildConfigOptions(runtimeState),
980
+ modes: buildModesState(runtimeState.modeId),
897
981
  ...(sessionMeta ? { _meta: sessionMeta } : {}),
898
982
  };
899
983
  },
@@ -902,8 +986,18 @@ export function createAcpFormalAgent(deps) {
902
986
  throw new RequestError(-32601, '"Method not found": session/load');
903
987
  }
904
988
  await loadSessionInternal(params);
905
- const session = sessions.get(params.sessionId);
989
+ let session = sessions.get(params.sessionId);
906
990
  const runtimeState = ensureSessionRuntimeState(session.id);
991
+ if (typeof session.title !== 'string' || !session.title.trim()) {
992
+ session =
993
+ sessions.update(session.id, (current) => ({
994
+ ...current,
995
+ title: deriveSessionTitleFromCwd(current.cwd),
996
+ })) ?? session;
997
+ await persistSessionsBestEffort();
998
+ }
999
+ runtimeState.lastSessionInfoDigest = null;
1000
+ await emitSessionInfoUpdateBestEffort(session.id);
907
1001
  // Restore plan state if session was running a task
908
1002
  if (session.taskId && session.cwd) {
909
1003
  await emitRuntimePlanUpdateIfNeeded({
@@ -932,6 +1026,7 @@ export function createAcpFormalAgent(deps) {
932
1026
  }
933
1027
  const response = {
934
1028
  configOptions: buildConfigOptions(runtimeState),
1029
+ modes: buildModesState(runtimeState.modeId),
935
1030
  };
936
1031
  if (deps.checkpointReader) {
937
1032
  const startedAt = Date.now();
@@ -1009,16 +1104,22 @@ export function createAcpFormalAgent(deps) {
1009
1104
  runtimeState.permissionPolicy = params.value;
1010
1105
  }
1011
1106
  else if (params.configId === ACP_MODE_CONFIG_ID) {
1012
- if (!isSessionModeId(params.value)) {
1107
+ const parsedModeId = parseAcpFlowMode(params.value);
1108
+ if (!parsedModeId || !ACP_PUBLIC_MODE_IDS.has(parsedModeId)) {
1013
1109
  throw new RequestError(-32602, `Invalid params: unsupported value "${params.value}" for "${params.configId}"`);
1014
1110
  }
1015
- runtimeState.modeId = params.value;
1111
+ runtimeState.modeId = parsedModeId;
1112
+ const legacyPermissionPolicy = getLegacyPermissionPolicyForModeValue(params.value);
1113
+ if (legacyPermissionPolicy) {
1114
+ runtimeState.permissionPolicy = legacyPermissionPolicy;
1115
+ }
1016
1116
  }
1017
1117
  else {
1018
1118
  throw new RequestError(-32602, `Invalid params: unsupported configId "${params.configId}"`);
1019
1119
  }
1020
1120
  sessions.update(params.sessionId, (current) => ({ ...current }));
1021
1121
  await persistSessionsBestEffort();
1122
+ await emitSessionInfoUpdateBestEffort(params.sessionId);
1022
1123
  const update = buildConfigOptionUpdateIfChanged(runtimeState);
1023
1124
  if (update) {
1024
1125
  await emitSessionUpdate(params.sessionId, update);
@@ -1029,6 +1130,35 @@ export function createAcpFormalAgent(deps) {
1029
1130
  }
1030
1131
  return { configOptions: buildConfigOptions(runtimeState) };
1031
1132
  },
1133
+ async setSessionMode(params) {
1134
+ await hydrateSessionsOnce();
1135
+ if (!sessions.get(params.sessionId)) {
1136
+ throw new RequestError(-32004, `Session not found: ${params.sessionId}`);
1137
+ }
1138
+ const runtimeState = ensureSessionRuntimeState(params.sessionId);
1139
+ const resolvedModeId = parseAcpFlowMode(params.modeId);
1140
+ if (!resolvedModeId || !ACP_PUBLIC_MODE_IDS.has(resolvedModeId)) {
1141
+ throw new RequestError(-32602, `Invalid params: unsupported modeId "${params.modeId}"`);
1142
+ }
1143
+ runtimeState.modeId = resolvedModeId;
1144
+ const legacyPermissionPolicy = getLegacyPermissionPolicyForModeValue(params.modeId);
1145
+ if (legacyPermissionPolicy) {
1146
+ runtimeState.permissionPolicy = legacyPermissionPolicy;
1147
+ }
1148
+ sessions.update(params.sessionId, (current) => ({ ...current }));
1149
+ await persistSessionsBestEffort();
1150
+ await emitSessionInfoUpdateBestEffort(params.sessionId);
1151
+ const configUpdate = buildConfigOptionUpdateIfChanged(runtimeState);
1152
+ if (configUpdate) {
1153
+ await emitSessionUpdate(params.sessionId, configUpdate);
1154
+ }
1155
+ // Send mode update notification
1156
+ const modeUpdate = buildCurrentModeUpdateIfChanged(runtimeState);
1157
+ if (modeUpdate) {
1158
+ await emitSessionUpdate(params.sessionId, modeUpdate);
1159
+ }
1160
+ return {};
1161
+ },
1032
1162
  async prompt(params) {
1033
1163
  await hydrateSessionsOnce();
1034
1164
  const session = sessions.get(params.sessionId);
@@ -1039,12 +1169,20 @@ export function createAcpFormalAgent(deps) {
1039
1169
  const fsCaps = caps.fs;
1040
1170
  const clientExecutionReady = caps.terminal === true && Boolean(fsCaps?.readTextFile) && Boolean(fsCaps?.writeTextFile);
1041
1171
  const effectiveExecutionBinding = executionBinding === 'client' && !clientExecutionReady ? 'local' : executionBinding;
1042
- const promptText = extractTextFromPrompt(params.prompt, defaultPromptCapabilities);
1172
+ const promptText = extractTextFromPrompt(params.prompt, promptCapabilities);
1043
1173
  const runtimeState = ensureSessionRuntimeState(params.sessionId);
1174
+ // Check for cancellation before starting processing
1175
+ if (sessions.get(params.sessionId)?.cancelRequested === true) {
1176
+ return { stopReason: 'cancelled' };
1177
+ }
1044
1178
  sessions.update(params.sessionId, (current) => {
1179
+ const title = typeof current.title === 'string' && current.title.trim()
1180
+ ? current.title
1181
+ : deriveSessionTitleFromCwd(current.cwd);
1045
1182
  return {
1046
1183
  ...current,
1047
1184
  cancelRequested: false,
1185
+ title,
1048
1186
  history: [
1049
1187
  ...current.history,
1050
1188
  { role: 'user', content: params.prompt },
@@ -1052,6 +1190,7 @@ export function createAcpFormalAgent(deps) {
1052
1190
  };
1053
1191
  });
1054
1192
  await persistSessionsBestEffort();
1193
+ await emitSessionInfoUpdateBestEffort(params.sessionId);
1055
1194
  const configUpdate = buildConfigOptionUpdateIfChanged(runtimeState);
1056
1195
  if (configUpdate) {
1057
1196
  await emitSessionUpdate(params.sessionId, configUpdate);
@@ -1083,16 +1222,23 @@ export function createAcpFormalAgent(deps) {
1083
1222
  ],
1084
1223
  }));
1085
1224
  await persistSessionsBestEffort();
1225
+ await emitSessionInfoUpdateBestEffort(params.sessionId);
1086
1226
  return { stopReason: 'end_turn' };
1087
1227
  }
1088
1228
  }
1229
+ // Check for cancellation again before creating task
1230
+ if (sessions.get(params.sessionId)?.cancelRequested === true) {
1231
+ return { stopReason: 'cancelled' };
1232
+ }
1233
+ const pendingUpdates = [];
1234
+ const executionRequest = buildCanonicalExecutionRequest({
1235
+ capability: runtimeState.modeId,
1236
+ instruction: promptText,
1237
+ checkpointSessionId: params.sessionId,
1238
+ repoPath: session.cwd,
1239
+ });
1089
1240
  const { task, signal } = await deps.facade.createTask({
1090
- capability: 'patch',
1091
- request: {
1092
- instruction: promptText,
1093
- checkpointSessionId: params.sessionId,
1094
- repoPath: session.cwd,
1095
- },
1241
+ ...executionRequest,
1096
1242
  commandRunner: effectiveExecutionBinding === 'client'
1097
1243
  ? createAcpCommandRunner({ conn: deps.conn, sessionId: params.sessionId })
1098
1244
  : undefined,
@@ -1109,14 +1255,18 @@ export function createAcpFormalAgent(deps) {
1109
1255
  authorizationMode: 'blocking',
1110
1256
  onEvent: (event) => {
1111
1257
  for (const update of loopEventToSessionUpdates(event, runtimeState)) {
1112
- void emitSessionUpdate(params.sessionId, update);
1258
+ pendingUpdates.push(emitSessionUpdate(params.sessionId, update).catch(() => {
1259
+ // Ignore errors in session update notifications
1260
+ }));
1113
1261
  }
1114
- void emitRuntimePlanUpdateIfNeeded({
1262
+ pendingUpdates.push(emitRuntimePlanUpdateIfNeeded({
1115
1263
  sessionId: params.sessionId,
1116
1264
  repoPath: session.cwd,
1117
1265
  event,
1118
1266
  state: runtimeState,
1119
- });
1267
+ }).catch(() => {
1268
+ // Ignore errors in plan update notifications
1269
+ }));
1120
1270
  },
1121
1271
  });
1122
1272
  sessions.update(params.sessionId, (current) => ({ ...current, taskId: task.id }));
@@ -1181,6 +1331,17 @@ export function createAcpFormalAgent(deps) {
1181
1331
  ],
1182
1332
  }));
1183
1333
  await persistSessionsBestEffort();
1334
+ const latestSession = sessions.get(params.sessionId);
1335
+ if (latestSession) {
1336
+ const sessionInfoUpdate = buildSessionInfoUpdateIfChanged(latestSession, runtimeState);
1337
+ if (sessionInfoUpdate) {
1338
+ pendingUpdates.push(emitSessionUpdate(params.sessionId, sessionInfoUpdate).catch(() => {
1339
+ // Ignore errors in session update notifications
1340
+ }));
1341
+ }
1342
+ }
1343
+ // Wait for all pending session updates to be sent before responding
1344
+ await Promise.all(pendingUpdates);
1184
1345
  return { stopReason };
1185
1346
  },
1186
1347
  async cancel(params) {
@@ -1188,11 +1349,16 @@ export function createAcpFormalAgent(deps) {
1188
1349
  const session = sessions.get(params.sessionId);
1189
1350
  if (!session)
1190
1351
  return;
1352
+ // Mark the session as cancelled
1191
1353
  sessions.update(params.sessionId, (current) => ({ ...current, cancelRequested: true }));
1192
1354
  await persistSessionsBestEffort();
1355
+ await emitSessionInfoUpdateBestEffort(params.sessionId);
1356
+ // If a task is running, cancel it
1193
1357
  if (session.taskId) {
1194
1358
  await deps.facade.cancelTask(session.taskId);
1195
1359
  }
1360
+ // Note: The prompt method will check the cancelRequested flag and return
1361
+ // StopReason::Cancelled as required by the protocol
1196
1362
  },
1197
1363
  extMethod: async () => ({}),
1198
1364
  extNotification: async () => { },
@@ -30,7 +30,11 @@ export function createAcpSessionStore() {
30
30
  if (!current)
31
31
  return undefined;
32
32
  const updated = mutate(current);
33
- updated.updatedAt = new Date().toISOString();
33
+ const nextUpdatedAt = new Date().toISOString();
34
+ updated.updatedAt =
35
+ nextUpdatedAt > current.updatedAt
36
+ ? nextUpdatedAt
37
+ : new Date(Date.parse(current.updatedAt) + 1).toISOString();
34
38
  sessions.set(id, updated);
35
39
  return updated;
36
40
  },
@@ -60,6 +60,23 @@ function toToolCallUpdate(request) {
60
60
  };
61
61
  }
62
62
  export function createAcpToolAuthorizationProvider(params) {
63
+ const inProgressEmitted = new Set();
64
+ const emitInProgressBestEffort = async (toolCallId) => {
65
+ if (inProgressEmitted.has(toolCallId))
66
+ return;
67
+ inProgressEmitted.add(toolCallId);
68
+ try {
69
+ const update = {
70
+ sessionUpdate: 'tool_call_update',
71
+ toolCallId,
72
+ status: 'in_progress',
73
+ };
74
+ await params.conn.sessionUpdate({ sessionId: params.sessionId, update });
75
+ }
76
+ catch {
77
+ // Best-effort: do not block authorization if the client can't accept updates.
78
+ }
79
+ };
63
80
  return {
64
81
  async requestAuthorization(request) {
65
82
  const enforceClientCapabilities = params.enforceClientCapabilities ?? true;
@@ -86,7 +103,8 @@ export function createAcpToolAuthorizationProvider(params) {
86
103
  }
87
104
  const permissionPolicy = params.getPermissionPolicy?.() ?? 'ask';
88
105
  if (permissionPolicy === 'allow_all') {
89
- return { outcome: 'allow_session', source: 'auto', reason: 'session_mode:yolo' };
106
+ await emitInProgressBestEffort(request.id);
107
+ return { outcome: 'allow_session', source: 'auto', reason: 'session_config:allow_all' };
90
108
  }
91
109
  const hasSideEffects = request.sideEffects.some((effect) => effect !== 'fs_read');
92
110
  if (permissionPolicy === 'deny_all' && hasSideEffects) {
@@ -103,8 +121,10 @@ export function createAcpToolAuthorizationProvider(params) {
103
121
  }
104
122
  switch (response.outcome.optionId) {
105
123
  case 'allow_once':
124
+ await emitInProgressBestEffort(request.id);
106
125
  return { outcome: 'allow_once', source: 'user' };
107
126
  case 'allow_always':
127
+ await emitInProgressBestEffort(request.id);
108
128
  return { outcome: 'allow_session', source: 'user' };
109
129
  case 'reject_once':
110
130
  case 'reject_always':
@@ -0,0 +1,24 @@
1
+ export function normalizeInstructionText(instruction, options) {
2
+ const normalized = instruction.replace(/\r\n?/g, '\n').trim();
3
+ if (normalized.length > 0)
4
+ return normalized;
5
+ return options?.fallbackInstruction ?? '';
6
+ }
7
+ export function buildInstructionFromParts(parts, options) {
8
+ return normalizeInstructionText(parts.join('\n'), options);
9
+ }
10
+ export function buildCanonicalExecutionRequest(input) {
11
+ const request = {
12
+ instruction: normalizeInstructionText(input.instruction, {
13
+ fallbackInstruction: input.fallbackInstruction,
14
+ }),
15
+ checkpointSessionId: input.checkpointSessionId,
16
+ repoPath: input.repoPath,
17
+ };
18
+ return {
19
+ capability: input.capability,
20
+ request,
21
+ taskId: input.taskId,
22
+ };
23
+ }
24
+ //# sourceMappingURL=execution-request.js.map