salmon-loop 0.3.0 → 0.3.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 (92) hide show
  1. package/dist/cli/authorization/non-interactive.js +7 -21
  2. package/dist/cli/commands/chat.js +1 -1
  3. package/dist/cli/commands/parallel.js +46 -41
  4. package/dist/cli/commands/run/assistant-message.js +3 -0
  5. package/dist/cli/commands/run/handler.js +2 -1
  6. package/dist/cli/commands/serve.js +109 -153
  7. package/dist/cli/headless/json-protocol.js +1 -1
  8. package/dist/cli/headless/stream-json-protocol.js +3 -2
  9. package/dist/cli/slash/runtime.js +5 -1
  10. package/dist/core/adapters/fs/node-fs.js +1 -0
  11. package/dist/core/benchmark/patch-artifact.js +1 -1
  12. package/dist/core/context/service.js +5 -2
  13. package/dist/core/extensions/index.js +2 -35
  14. package/dist/core/extensions/redact.js +9 -3
  15. package/dist/core/extensions/schemas.js +2 -51
  16. package/dist/core/facades/cli-authorization-non-interactive.js +1 -1
  17. package/dist/core/facades/cli-serve.js +0 -1
  18. package/dist/core/grizzco/dsl/strategies.js +1 -3
  19. package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +12 -7
  20. package/dist/core/grizzco/engine/transaction/attempt-failure.js +23 -23
  21. package/dist/core/grizzco/engine/transaction/report-mapper.js +3 -0
  22. package/dist/core/grizzco/engine/transaction/transaction-runner.js +14 -0
  23. package/dist/core/grizzco/flows/AutopilotFlow.js +1 -0
  24. package/dist/core/grizzco/flows/SalmonLoopFlow.js +1 -0
  25. package/dist/core/grizzco/steps/apply.js +0 -7
  26. package/dist/core/grizzco/steps/autopilot.js +108 -6
  27. package/dist/core/grizzco/steps/preflight.js +10 -0
  28. package/dist/core/grizzco/steps/tool-runtime.js +1 -0
  29. package/dist/core/interaction/events/bus.js +14 -0
  30. package/dist/core/interaction/orchestration/facade.js +10 -0
  31. package/dist/core/mcp/bridge/index.js +4 -0
  32. package/dist/core/mcp/bridge/prompt-command-provider.js +261 -0
  33. package/dist/core/mcp/bridge/resource-context-provider.js +259 -0
  34. package/dist/core/mcp/bridge/tool-bridge.js +303 -0
  35. package/dist/core/mcp/cache/resource-cache.js +41 -0
  36. package/dist/core/mcp/catalog/discovery.js +51 -0
  37. package/dist/core/mcp/catalog/notification-router.js +28 -0
  38. package/dist/core/mcp/catalog/prompt-catalog.js +4 -0
  39. package/dist/core/mcp/catalog/resource-catalog.js +7 -0
  40. package/dist/core/mcp/catalog/tool-catalog.js +4 -0
  41. package/dist/core/mcp/client/connection-manager.js +239 -0
  42. package/dist/core/mcp/client/lifecycle.js +13 -0
  43. package/dist/core/mcp/client/transport-factory.js +168 -0
  44. package/dist/core/mcp/config/index.js +32 -0
  45. package/dist/core/mcp/config/schema-v2.js +129 -0
  46. package/dist/core/mcp/host/elicitation-provider.js +209 -0
  47. package/dist/core/mcp/host/roots-provider.js +70 -0
  48. package/dist/core/mcp/host/sampling-provider.js +170 -0
  49. package/dist/core/mcp/index.js +4 -0
  50. package/dist/core/mcp/observability/events.js +19 -0
  51. package/dist/core/mcp/policy/approval-policy.js +2 -0
  52. package/dist/core/mcp/policy/classifier.js +172 -0
  53. package/dist/core/mcp/policy/grants.js +356 -0
  54. package/dist/core/mcp/policy/uri-policy.js +60 -0
  55. package/dist/core/mcp/schema/json-schema-to-zod.js +511 -0
  56. package/dist/core/mcp/types.js +2 -0
  57. package/dist/core/protocols/a2a/agent-card.js +36 -11
  58. package/dist/core/protocols/a2a/sdk/executor.js +105 -36
  59. package/dist/core/protocols/a2a/sdk/server.js +1311 -3
  60. package/dist/core/protocols/acp/acp-checkpoint-probe.js +113 -0
  61. package/dist/core/protocols/acp/acp-session-persistence.js +336 -0
  62. package/dist/core/protocols/acp/acp-types.js +17 -0
  63. package/dist/core/protocols/acp/formal-agent.js +271 -603
  64. package/dist/core/protocols/acp/handlers.js +3 -0
  65. package/dist/core/protocols/acp/permission-provider.js +11 -39
  66. package/dist/core/protocols/acp/stdio-server.js +20 -1
  67. package/dist/core/protocols/acp/tool-kind-mapping.js +62 -0
  68. package/dist/core/protocols/shared/flow-mode-mapping.js +0 -8
  69. package/dist/core/public-capabilities/flow-mode-metadata.js +0 -6
  70. package/dist/core/public-capabilities/projections.js +1 -0
  71. package/dist/core/runtime/agent-server-runtime.js +2 -3
  72. package/dist/core/runtime/spawn-command.js +8 -2
  73. package/dist/core/runtime/spawn-interactive.js +26 -0
  74. package/dist/core/session/manager.js +48 -25
  75. package/dist/core/tools/builtin/index.js +6 -1
  76. package/dist/core/tools/builtin/proposal.js +0 -7
  77. package/dist/core/tools/builtin/workspace.js +76 -0
  78. package/dist/core/tools/dispatcher.js +1 -0
  79. package/dist/core/tools/loader.js +92 -46
  80. package/dist/core/verification/runner.js +60 -31
  81. package/dist/core/workspace/capabilities.js +80 -0
  82. package/dist/locales/en.js +17 -3
  83. package/package.json +4 -2
  84. package/dist/core/protocols/a2a/mapper.js +0 -14
  85. package/dist/core/protocols/a2a/sdk/auth-middleware.js +0 -31
  86. package/dist/core/protocols/a2a/task-projection.js +0 -45
  87. package/dist/core/protocols/acp/checkpoint-meta.js +0 -2
  88. package/dist/core/tools/mcp/client.js +0 -309
  89. package/dist/core/tools/mcp/loader.js +0 -110
  90. package/dist/core/tools/mcp/schema.js +0 -54
  91. package/dist/core/tools/mcp/streamable-http.js +0 -101
  92. package/dist/core/tools/mcp/types.js +0 -26
@@ -1,11 +1,13 @@
1
1
  import { text } from '../../locales/index.js';
2
+ import { registerMcpV2Tools } from '../mcp/bridge/tool-bridge.js';
3
+ import { McpConnectionManager } from '../mcp/client/connection-manager.js';
4
+ import { buildMcpGrantsFromCapabilities, McpPolicyEngine } from '../mcp/policy/grants.js';
2
5
  import { skillToToolSpec } from '../skills/bridge.js';
3
6
  import { SkillLoader } from '../skills/loader.js';
4
7
  import { ToolAuditLogger } from './audit.js';
5
8
  import { BudgetGuard } from './budget.js';
6
9
  import { registerAllBuiltins } from './builtin/index.js';
7
10
  import { ToolDispatcher } from './dispatcher.js';
8
- import { registerMcpTools } from './mcp/loader.js';
9
11
  import { compilePermissionRules, getVisibleToolNamesFromAllowRules, shouldFilterRegistryByAllowRules, } from './permissions/permission-rules.js';
10
12
  import { registerPluginTools } from './plugins/loader.js';
11
13
  import { ToolPolicy } from './policy.js';
@@ -54,6 +56,7 @@ export async function createStandardToolstack(options) {
54
56
  // e. Create ToolRouter with filtered registry
55
57
  // f. Fill RouterBox.router — skill executors now have a valid reference
56
58
  const routerBox = { router: null };
59
+ let mcpManager;
57
60
  // 3a. Load skill catalog (Tier 1: lightweight metadata only) and register
58
61
  // bridge tool specs with lazy activation. Full skill content is loaded
59
62
  // on demand via SkillLoader.activateSkill() (Tier 2) when the executor
@@ -69,55 +72,98 @@ export async function createStandardToolstack(options) {
69
72
  registry.register(skillToToolSpec({ entry, loader: skillLoader }, routerBox));
70
73
  }
71
74
  // 3b. Register MCP + plugin tools
72
- if (extensions) {
73
- await registerMcpTools(registry, extensions.mcpServers);
74
- await registerPluginTools(registry, extensions.toolPlugins);
75
- }
76
- // 3c. Apply allowlist / permission-rule filtering — skills are now included
77
- const allowSets = [];
78
- if (Array.isArray(options.allowedToolNames) && options.allowedToolNames.length > 0) {
79
- allowSets.push(new Set(options.allowedToolNames));
80
- }
81
- if (shouldFilterRegistryByAllowRules(compiledPermissionRules)) {
82
- allowSets.push(getVisibleToolNamesFromAllowRules(compiledPermissionRules));
83
- }
84
- if (allowSets.length > 0) {
85
- const intersect = new Set(allowSets[0]);
86
- for (const next of allowSets.slice(1)) {
87
- for (const name of Array.from(intersect)) {
88
- if (!next.has(name))
89
- intersect.delete(name);
75
+ try {
76
+ if (extensions) {
77
+ if (extensions.mcpServers.length > 0) {
78
+ mcpManager = new McpConnectionManager(extensions.mcpServers, {
79
+ roots: extensions.mcpServers.some((server) => server.capabilities.roots.mode !== 'none'),
80
+ sampling: extensions.mcpServers.some((server) => server.capabilities.sampling.enabled),
81
+ elicitation: extensions.mcpServers.some((server) => server.capabilities.elicitation.enabled),
82
+ });
83
+ const mcpPolicy = new McpPolicyEngine(extensions.mcpServers.flatMap((server) => buildMcpGrantsFromCapabilities(server.name, server.capabilities)));
84
+ await registerMcpV2Tools({
85
+ registry,
86
+ servers: extensions.mcpServers,
87
+ manager: mcpManager,
88
+ policy: mcpPolicy,
89
+ });
90
+ }
91
+ await registerPluginTools(registry, extensions.toolPlugins);
92
+ }
93
+ // 3c. Apply allowlist / permission-rule filtering — skills are now included
94
+ const allowSets = [];
95
+ if (Array.isArray(options.allowedToolNames) && options.allowedToolNames.length > 0) {
96
+ allowSets.push(new Set(options.allowedToolNames));
97
+ }
98
+ if (shouldFilterRegistryByAllowRules(compiledPermissionRules)) {
99
+ allowSets.push(getVisibleToolNamesFromAllowRules(compiledPermissionRules));
100
+ }
101
+ if (allowSets.length > 0) {
102
+ const intersect = new Set(allowSets[0]);
103
+ for (const next of allowSets.slice(1)) {
104
+ for (const name of Array.from(intersect)) {
105
+ if (!next.has(name))
106
+ intersect.delete(name);
107
+ }
108
+ }
109
+ const filtered = new ToolRegistry();
110
+ for (const name of intersect) {
111
+ const spec = registry.getSpec(name);
112
+ if (spec)
113
+ filtered.register(spec);
90
114
  }
115
+ registry = filtered;
91
116
  }
92
- const filtered = new ToolRegistry();
93
- for (const name of intersect) {
94
- const spec = registry.getSpec(name);
95
- if (spec)
117
+ if (options.workspaceCapabilities) {
118
+ const capabilities = options.workspaceCapabilities;
119
+ const filtered = new ToolRegistry();
120
+ for (const spec of registry.listAll()) {
121
+ const needsGit = spec.name.startsWith('git.') ||
122
+ spec.sideEffects.includes('git_read') ||
123
+ spec.sideEffects.includes('git_write');
124
+ const needsReadableFilesystem = spec.sideEffects.includes('fs_read');
125
+ const needsWritableFilesystem = spec.sideEffects.includes('fs_write');
126
+ if (needsGit && !capabilities.git.insideWorkTree)
127
+ continue;
128
+ if (needsReadableFilesystem && !capabilities.filesystem.readable)
129
+ continue;
130
+ if (needsWritableFilesystem && !capabilities.filesystem.writable)
131
+ continue;
96
132
  filtered.register(spec);
133
+ }
134
+ registry = filtered;
97
135
  }
98
- registry = filtered;
136
+ // 3d. Create Router with the FINAL (filtered) registry skills included
137
+ const router = new ToolRouter(registry, policy, budget, audit, sanitize, options.authorizationProvider, { authorizationMode: options.authorizationMode, permissionRules: compiledPermissionRules });
138
+ // 3e. Fill the RouterBox — skill executors can now resolve the router
139
+ routerBox.router = router;
140
+ // 4. Create Dispatcher (The high-level coordinator for LLM text)
141
+ const dispatcher = new ToolDispatcher(router, {
142
+ repoRoot: options.repoRoot,
143
+ persistenceRoot: options.persistenceRoot,
144
+ worktreeRoot: options.worktreeRoot,
145
+ workspaceCapabilities: options.workspaceCapabilities,
146
+ attemptId: options.attemptId,
147
+ dryRun: options.dryRun,
148
+ model: options.model,
149
+ });
150
+ return {
151
+ registry,
152
+ router,
153
+ dispatcher,
154
+ budget,
155
+ audit,
156
+ policy,
157
+ sanitize,
158
+ mcp: mcpManager,
159
+ async dispose() {
160
+ await mcpManager?.stopAll();
161
+ },
162
+ };
163
+ }
164
+ catch (error) {
165
+ await mcpManager?.stopAll();
166
+ throw error;
99
167
  }
100
- // 3d. Create Router with the FINAL (filtered) registry — skills included
101
- const router = new ToolRouter(registry, policy, budget, audit, sanitize, options.authorizationProvider, { authorizationMode: options.authorizationMode, permissionRules: compiledPermissionRules });
102
- // 3e. Fill the RouterBox — skill executors can now resolve the router
103
- routerBox.router = router;
104
- // 4. Create Dispatcher (The high-level coordinator for LLM text)
105
- const dispatcher = new ToolDispatcher(router, {
106
- repoRoot: options.repoRoot,
107
- persistenceRoot: options.persistenceRoot,
108
- worktreeRoot: options.worktreeRoot,
109
- attemptId: options.attemptId,
110
- dryRun: options.dryRun,
111
- model: options.model,
112
- });
113
- return {
114
- registry,
115
- router,
116
- dispatcher,
117
- budget,
118
- audit,
119
- policy,
120
- sanitize,
121
- };
122
168
  }
123
169
  //# sourceMappingURL=loader.js.map
@@ -8,6 +8,7 @@ import { tryGetPluginRegistry } from '../plugin/registry.js';
8
8
  import { isCommandAvailable, spawnCommand } from '../runtime/process-runner.js';
9
9
  import { ErrorType } from '../types/index.js';
10
10
  import { getPlatformShellInvocation } from '../utils/platform-shell.js';
11
+ import { detectWorkspaceCapabilities } from '../workspace/capabilities.js';
11
12
  /**
12
13
  * Classify the error type based on the output of the verification command
13
14
  */
@@ -186,38 +187,65 @@ export async function verifyFileContent(repoPath, filePath, expected, onEvent) {
186
187
  }
187
188
  export async function preflight(workspace, onEvent, options) {
188
189
  const now = () => new Date();
189
- const git = new GitAdapter(workspace.baseRepoPath);
190
- // 1. Check if it's a git repo
191
- const gitCheck = await git.execMeta(['rev-parse', '--is-inside-work-tree'], {
192
- cwd: workspace.baseRepoPath,
193
- limits: { maxStdoutBytes: 4_096, maxStderrChars: 4_096 },
194
- timeoutMs: LIMITS.gitTimeoutMs,
195
- });
196
- if (!gitCheck.ok) {
197
- if (gitCheck.error?.code === 'ENOENT') {
190
+ const requireGit = options?.requireGit !== false;
191
+ const requireWrite = options?.requireWrite !== false;
192
+ const workspacePath = workspace.workPath || workspace.baseRepoPath;
193
+ const capabilities = workspace.capabilities ?? (await detectWorkspaceCapabilities(workspacePath));
194
+ if (!capabilities.filesystem.readable) {
195
+ return {
196
+ ok: false,
197
+ reason: capabilities.filesystem.reason || 'Workspace is not readable',
198
+ reasonCode: 'LOOP_FAILED',
199
+ };
200
+ }
201
+ if (requireWrite && !capabilities.filesystem.writable) {
202
+ return {
203
+ ok: false,
204
+ reason: capabilities.filesystem.reason || 'Workspace is not writable',
205
+ reasonCode: 'LOOP_FAILED',
206
+ };
207
+ }
208
+ if (!capabilities.git.insideWorkTree) {
209
+ if (!requireGit) {
210
+ onEvent?.({
211
+ type: 'resource.status',
212
+ resource: 'git',
213
+ status: 'skipped',
214
+ message: capabilities.git.reason || text.loop.preflightFailedNotGit,
215
+ timestamp: now(),
216
+ });
217
+ }
218
+ else if (!capabilities.git.available) {
198
219
  return { ok: false, reason: text.loop.gitNotFound, reasonCode: 'PREFLIGHT_NOT_GIT' };
199
220
  }
200
- if (gitCheck.error?.message) {
221
+ else {
201
222
  return {
202
223
  ok: false,
203
- reason: text.loop.preflightGitCheckFailed(gitCheck.error.message),
224
+ reason: capabilities.git.reason
225
+ ? text.loop.preflightGitCheckFailed(capabilities.git.reason)
226
+ : text.loop.preflightFailedNotGit,
204
227
  reasonCode: 'PREFLIGHT_NOT_GIT',
205
228
  };
206
229
  }
207
- return { ok: false, reason: text.loop.preflightFailedNotGit, reasonCode: 'PREFLIGHT_NOT_GIT' };
208
230
  }
209
- if (gitCheck.stdoutTruncated) {
210
- return {
211
- ok: false,
212
- reason: text.loop.preflightGitCheckFailed(text.git.outputTruncated(4096)),
213
- reasonCode: 'LOOP_FAILED',
214
- };
231
+ if (!capabilities.git.insideWorkTree) {
232
+ if (!(await isCommandAvailable('rg'))) {
233
+ onEvent?.({
234
+ type: 'resource.status',
235
+ resource: 'ripgrep',
236
+ status: 'warning',
237
+ message: text.verify.ripgrepNotFoundWarning,
238
+ timestamp: now(),
239
+ });
240
+ }
241
+ return { ok: true, capabilities };
215
242
  }
216
- // 2. Check if workspace is dirty (only for direct strategy)
243
+ const git = new GitAdapter(workspacePath);
244
+ // Check if workspace is dirty (only for direct strategy)
217
245
  // Allow dirty workspace by default for worktree strategy
218
246
  if (workspace.strategy === 'direct' && !options?.ignoreDirty) {
219
247
  const statusCheck = await git.execMeta(['status', '--porcelain'], {
220
- cwd: workspace.baseRepoPath,
248
+ cwd: workspacePath,
221
249
  limits: { maxStdoutBytes: 64_000, maxStderrChars: 4_096 },
222
250
  timeoutMs: LIMITS.gitTimeoutMs,
223
251
  });
@@ -243,17 +271,18 @@ export async function preflight(workspace, onEvent, options) {
243
271
  reasonCode: 'PREFLIGHT_DIRTY',
244
272
  };
245
273
  }
246
- return { ok: true };
274
+ return { ok: true, capabilities };
247
275
  }
248
- // Worktree strategy: ignore dirty state in base repository
249
- onEvent?.({
250
- type: 'resource.status',
251
- resource: 'git',
252
- status: 'skipped',
253
- message: text.verify.worktreeStrategyActive,
254
- timestamp: now(),
255
- });
256
- // 3. Check if ripgrep is installed (optional but recommended)
276
+ if (workspace.strategy !== 'direct' || options?.ignoreDirty) {
277
+ onEvent?.({
278
+ type: 'resource.status',
279
+ resource: 'git',
280
+ status: 'skipped',
281
+ message: text.verify.worktreeStrategyActive,
282
+ timestamp: now(),
283
+ });
284
+ }
285
+ // Check if ripgrep is installed (optional but recommended)
257
286
  if (!(await isCommandAvailable('rg'))) {
258
287
  onEvent?.({
259
288
  type: 'resource.status',
@@ -263,6 +292,6 @@ export async function preflight(workspace, onEvent, options) {
263
292
  timestamp: now(),
264
293
  });
265
294
  }
266
- return { ok: true };
295
+ return { ok: true, capabilities };
267
296
  }
268
297
  //# sourceMappingURL=runner.js.map
@@ -0,0 +1,80 @@
1
+ import { access, constants } from '../adapters/fs/node-fs.js';
2
+ import { GitAdapter } from '../adapters/git/git-adapter.js';
3
+ import { LIMITS } from '../config/limits.js';
4
+ const PROBE_LIMITS = { maxStdoutBytes: 4_096, maxStderrChars: 4_096 };
5
+ function gitFailureReason(result) {
6
+ if (result.error?.code === 'ENOENT')
7
+ return 'git executable not found';
8
+ if (result.error?.message)
9
+ return result.error.message;
10
+ if (result.stdoutTruncated)
11
+ return `git output exceeded ${PROBE_LIMITS.maxStdoutBytes} bytes`;
12
+ const stderr = result.stderr?.trim();
13
+ const stdout = result.stdout?.toString('utf8').trim();
14
+ if (stdout)
15
+ return `git reported --is-inside-work-tree=${stdout}`;
16
+ return stderr || 'not a git work tree';
17
+ }
18
+ async function detectFileSystemCapability(workspacePath) {
19
+ try {
20
+ await access(workspacePath, constants.R_OK);
21
+ }
22
+ catch (error) {
23
+ return {
24
+ readable: false,
25
+ writable: false,
26
+ reason: error instanceof Error ? error.message : String(error),
27
+ };
28
+ }
29
+ try {
30
+ await access(workspacePath, constants.W_OK);
31
+ return { readable: true, writable: true };
32
+ }
33
+ catch (error) {
34
+ return {
35
+ readable: true,
36
+ writable: false,
37
+ reason: error instanceof Error ? error.message : String(error),
38
+ };
39
+ }
40
+ }
41
+ export async function detectWorkspaceCapabilities(workspacePath) {
42
+ const git = new GitAdapter(workspacePath);
43
+ const gitCheck = await git.execMeta(['rev-parse', '--is-inside-work-tree'], {
44
+ cwd: workspacePath,
45
+ limits: PROBE_LIMITS,
46
+ timeoutMs: LIMITS.gitTimeoutMs,
47
+ });
48
+ let gitCapability;
49
+ const insideWorkTree = gitCheck.ok ? gitCheck.stdout.toString('utf8').trim() === 'true' : false;
50
+ if (!gitCheck.ok || gitCheck.stdoutTruncated || !insideWorkTree) {
51
+ gitCapability = {
52
+ available: gitCheck.error?.code !== 'ENOENT',
53
+ insideWorkTree: false,
54
+ reason: gitFailureReason(gitCheck),
55
+ };
56
+ }
57
+ else {
58
+ const headResult = await git.execMeta(['rev-parse', '--verify', 'HEAD'], {
59
+ cwd: workspacePath,
60
+ limits: PROBE_LIMITS,
61
+ timeoutMs: LIMITS.gitTimeoutMs,
62
+ });
63
+ gitCapability = {
64
+ available: true,
65
+ insideWorkTree: true,
66
+ head: headResult.ok ? headResult.stdout.toString('utf8').trim() : undefined,
67
+ };
68
+ }
69
+ return {
70
+ git: gitCapability,
71
+ filesystem: await detectFileSystemCapability(workspacePath),
72
+ };
73
+ }
74
+ export function requiresGitWorkspace(params) {
75
+ if (params.strategy === 'worktree' || params.strategy === 'tempCommit') {
76
+ return true;
77
+ }
78
+ return params.mode !== 'autopilot';
79
+ }
80
+ //# sourceMappingURL=capabilities.js.map
@@ -141,7 +141,6 @@ export const en = {
141
141
  acp: {
142
142
  slashHelpDescription: 'Show available ACP slash commands',
143
143
  slashHelpResponse: (commands) => `Available commands: ${commands}`,
144
- slashUnknownCommand: (commandName) => `Unknown command: /${commandName}`,
145
144
  askUserHeader: 'User input required',
146
145
  askUserQuestion: (question) => `Question: ${question}`,
147
146
  askUserOptionsHeader: 'Options:',
@@ -155,8 +154,23 @@ export const en = {
155
154
  permissionPolicyDenyAllDescription: 'Automatically deny side-effecting operations.',
156
155
  permissionPolicyAllowAllName: 'Allow all',
157
156
  permissionPolicyAllowAllDescription: 'Automatically allow side-effecting operations.',
158
- modeInteractiveDescription: 'Request permission before running side-effecting operations.',
159
- modeYoloDescription: 'Bypass permission prompts for side-effecting operations.',
157
+ executionFlowName: 'Execution Flow',
158
+ executionFlowDescription: 'Choose how the agent should execute this session.',
159
+ permissionOptionAllowOnce: 'Allow once',
160
+ permissionOptionAllowSession: 'Allow for session',
161
+ permissionOptionRejectOnce: 'Reject once',
162
+ permissionOptionRejectSession: 'Reject for session',
163
+ taskCancelled: 'Task cancelled.',
164
+ taskCompleted: 'Task completed.',
165
+ taskFailed: 'Task failed.',
166
+ taskFailedWithReason: (reason) => `Task failed: ${reason}`,
167
+ taskAwaitingInput: 'Task awaiting input.',
168
+ checkpointNotFound: 'Checkpoint not found. Start a new session.',
169
+ checkpointManifestParseError: 'Checkpoint metadata is corrupted. Recreate checkpoint metadata and retry.',
170
+ checkpointManifestIoError: 'Checkpoint metadata is unreadable due to filesystem I/O issues.',
171
+ checkpointManifestLockTimeout: 'Checkpoint metadata is busy (lock timeout). Retry shortly.',
172
+ checkpointManifestUnavailable: 'Checkpoint metadata is unavailable in current runtime.',
173
+ checkpointResumeUnavailable: 'Checkpoint resume is unavailable. Start a new session or retry.',
160
174
  },
161
175
  prompts: {
162
176
  definitionHint: 'Definitions should be modified with extreme caution',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "salmon-loop",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "A chat-first coding agent CLI for safe, reviewable repository changes",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,6 +37,7 @@
37
37
  "test:runtime-boundary": "bun test --preload ./tests/setup-bun.ts tests/unit/scripts/target-runtime-boundary.test.ts tests/unit/scripts/bun-purity.test.ts",
38
38
  "test:worktree-smoke": "bun scripts/worktree-smoke.js",
39
39
  "test:headless-smoke": "bun scripts/headless-smoke.ts",
40
+ "smoke:swebench": "bun scripts/swebench-smoke.ts",
40
41
  "test:contract-smoke": "bun test --timeout 15000 --preload ./tests/setup-bun.ts tests/unit/architecture/request-assembly-invariant.test.ts tests/unit/architecture/replacement-preview-boundary-invariant.test.ts tests/unit/architecture/sub-agent-prefix-consistency-invariant.test.ts tests/unit/architecture/tool-naming-contract.test.ts tests/unit/architecture/verify-contract-smoke-gate.test.ts tests/unit/tools/session-streaming.test.ts tests/unit/core/grizzco/steps/plan-patch-toolcalling.test.ts tests/unit/core/session/replacement-state.test.ts",
41
42
  "setup:hooks": "git config core.hooksPath .githooks",
42
43
  "postinstall": "node scripts/fix-es-abstract-compat.js",
@@ -126,10 +127,11 @@
126
127
  },
127
128
  "dependencies": {
128
129
  "@a2a-js/sdk": "0.3.10",
129
- "@agentclientprotocol/sdk": "0.22.0",
130
+ "@agentclientprotocol/sdk": "0.22.1",
130
131
  "@ai-sdk/openai": "^3.0.23",
131
132
  "@ai-sdk/openai-compatible": "^2.0.24",
132
133
  "@inquirer/prompts": "^8.2.0",
134
+ "@modelcontextprotocol/sdk": "^1.29.0",
133
135
  "ai": "^6.0.62",
134
136
  "ajv": "^8.18.0",
135
137
  "chalk": "^5.4.1",
@@ -1,14 +0,0 @@
1
- export function mapA2ATaskToCanonicalTask(input) {
2
- const instruction = input.message.parts
3
- .filter((part) => part.type === 'text' && typeof part.text === 'string')
4
- .map((part) => part.text)
5
- .join('\n');
6
- return {
7
- id: input.id,
8
- capability: 'patch',
9
- state: 'accepted',
10
- request: { instruction },
11
- createdAt: new Date().toISOString(),
12
- };
13
- }
14
- //# sourceMappingURL=mapper.js.map
@@ -1,31 +0,0 @@
1
- import { UserBuilder } from '@a2a-js/sdk/server/express';
2
- /**
3
- * Converts A2AAuthPolicyMiddleware to Express middleware
4
- * Handles authentication failures by returning 401 Unauthorized
5
- */
6
- export function createAuthMiddlewareFromPolicy(_authPolicy) {
7
- return async (req, res, next) => {
8
- try {
9
- // Note: A2AAuthPolicyMiddleware expects Fetch API Request, but Express provides its own Request
10
- // For now, we skip the policy check and just pass through
11
- // In a real implementation, you would need to adapt the Express Request to Fetch API Request
12
- next();
13
- }
14
- catch (_error) {
15
- res.status(500).json({ error: 'Authentication failed' });
16
- }
17
- };
18
- }
19
- /**
20
- * Creates a UserBuilder from Express request with auth context
21
- */
22
- export function createUserBuilderFromAuthContext() {
23
- return (req) => {
24
- const authContext = req.authContext;
25
- if (authContext) {
26
- return UserBuilder.noAuthentication();
27
- }
28
- return UserBuilder.noAuthentication();
29
- };
30
- }
31
- //# sourceMappingURL=auth-middleware.js.map
@@ -1,45 +0,0 @@
1
- function projectTaskState(state) {
2
- if (state === 'accepted')
3
- return 'submitted';
4
- if (state === 'running' || state === 'streaming')
5
- return 'working';
6
- if (state === 'awaiting_input')
7
- return 'input-required';
8
- if (state === 'completed')
9
- return 'completed';
10
- if (state === 'failed')
11
- return 'failed';
12
- if (state === 'cancelled')
13
- return 'canceled';
14
- return 'working';
15
- }
16
- export function projectCanonicalTaskToA2ATask(task) {
17
- return {
18
- id: task.id,
19
- state: task.state,
20
- status: {
21
- state: projectTaskState(task.state),
22
- timestamp: task.createdAt ?? new Date().toISOString(),
23
- message: task.statusMessage,
24
- },
25
- failure: task.failure,
26
- requiredAction: task.inputRequired,
27
- artifacts: (task.artifacts ?? []).map((artifact) => ({
28
- artifactId: artifact.id,
29
- name: artifact.name,
30
- kind: artifact.kind,
31
- mimeType: artifact.mimeType,
32
- content: artifact.content,
33
- delivery: artifact.delivery,
34
- handle: artifact.handle,
35
- url: artifact.url,
36
- expiresAt: artifact.expiresAt,
37
- })),
38
- metadata: {
39
- capability: task.capability,
40
- tenantId: task.tenantId,
41
- attempt: task.attempt,
42
- },
43
- };
44
- }
45
- //# sourceMappingURL=task-projection.js.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=checkpoint-meta.js.map