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
@@ -10,6 +10,7 @@ export function createA2AInteractionExecutor(deps) {
10
10
  const submittedPublished = new Set();
11
11
  // Prevents duplicate terminal status events when publishTaskStatus is called multiple times
12
12
  const terminalPublished = new Set();
13
+ const artifactSignaturesByTaskId = new Map();
13
14
  return {
14
15
  async execute(requestContext, executionEventBus) {
15
16
  const capability = parseA2ASkillFlowMode(deps.capabilityResolver?.(requestContext.userMessage)) ?? 'autopilot';
@@ -89,24 +90,9 @@ export function createA2AInteractionExecutor(deps) {
89
90
  // ALWAYS publish "submitted" first if not yet published
90
91
  if (!submittedPublished.has(taskId)) {
91
92
  submittedPublished.add(taskId);
92
- const submittedStatus = {
93
- state: 'submitted',
94
- timestamp: envelope.createdAt ?? new Date().toISOString(),
95
- message: undefined,
96
- };
97
- const submittedUpdate = {
98
- kind: 'status-update',
99
- taskId: envelope.id,
100
- contextId: metadata.contextId,
101
- status: submittedStatus,
102
- final: false,
103
- metadata: { attempt: envelope.attempt },
104
- };
105
- eventBus.publish(submittedUpdate);
106
- // Save as 'running' state (not completed) to keep task cancelable during grace period.
107
- // SDK rejects cancellation if task is already in terminal state in the store.
108
- const submittedEnvelope = { ...envelope, state: 'running' };
109
- const snapshot = buildTaskSnapshot(submittedEnvelope, metadata);
93
+ const submittedEnvelope = { ...envelope, state: 'accepted' };
94
+ const snapshot = buildTaskSnapshot(submittedEnvelope, metadata, submittedEnvelope.createdAt ?? new Date().toISOString());
95
+ eventBus.publish(snapshot);
110
96
  await store.save(snapshot);
111
97
  // If the current state is still "submitted", we're done
112
98
  if (currentState === 'submitted') {
@@ -124,10 +110,11 @@ export function createA2AInteractionExecutor(deps) {
124
110
  if (cancelledTaskIds.has(taskId)) {
125
111
  terminalPublished.add(taskId);
126
112
  const canceledEnvelope = { ...envelope, state: 'cancelled' };
127
- const snapshot = buildTaskSnapshot(canceledEnvelope, metadata);
113
+ const recordedAt = new Date().toISOString();
114
+ const snapshot = buildTaskSnapshot(canceledEnvelope, metadata, recordedAt);
128
115
  await store.save(snapshot);
129
116
  const status = {
130
- ...buildTaskStatus(envelope, metadata.contextId),
117
+ ...buildTaskStatus(envelope, metadata.contextId, recordedAt),
131
118
  state: 'canceled',
132
119
  };
133
120
  const update = {
@@ -149,13 +136,14 @@ export function createA2AInteractionExecutor(deps) {
149
136
  const wasCancelled = cancelledTaskIds.has(taskId) || taskAfterGrace?.status.state === 'canceled';
150
137
  if (wasCancelled) {
151
138
  terminalPublished.add(taskId);
139
+ const recordedAt = new Date().toISOString();
152
140
  if (taskAfterGrace?.status.state !== 'canceled') {
153
141
  const canceledEnvelope = { ...envelope, state: 'cancelled' };
154
- const snapshot = buildTaskSnapshot(canceledEnvelope, metadata);
142
+ const snapshot = buildTaskSnapshot(canceledEnvelope, metadata, recordedAt);
155
143
  await store.save(snapshot);
156
144
  }
157
145
  const status = {
158
- ...buildTaskStatus(envelope, metadata.contextId),
146
+ ...buildTaskStatus(envelope, metadata.contextId, recordedAt),
159
147
  state: 'canceled',
160
148
  };
161
149
  const update = {
@@ -171,9 +159,11 @@ export function createA2AInteractionExecutor(deps) {
171
159
  }
172
160
  // No cancellation detected, safe to publish "completed"
173
161
  terminalPublished.add(taskId);
174
- const snapshot = buildTaskSnapshot(envelope, metadata);
162
+ const recordedAt = new Date().toISOString();
163
+ const snapshot = buildTaskSnapshot(envelope, metadata, recordedAt);
175
164
  await store.save(snapshot);
176
- const status = buildTaskStatus(envelope, metadata.contextId);
165
+ publishArtifactUpdates(envelope, metadata, eventBus);
166
+ const status = buildTaskStatus(envelope, metadata.contextId, recordedAt);
177
167
  const update = {
178
168
  kind: 'status-update',
179
169
  taskId: envelope.id,
@@ -186,9 +176,11 @@ export function createA2AInteractionExecutor(deps) {
186
176
  return true;
187
177
  }
188
178
  // For all other states (working, failed, canceled, etc.), save and publish immediately
189
- const snapshot = buildTaskSnapshot(envelope, metadata);
179
+ const recordedAt = new Date().toISOString();
180
+ const snapshot = buildTaskSnapshot(envelope, metadata, recordedAt);
190
181
  await store.save(snapshot);
191
- const status = buildTaskStatus(envelope, metadata.contextId);
182
+ publishArtifactUpdates(envelope, metadata, eventBus);
183
+ const status = buildTaskStatus(envelope, metadata.contextId, recordedAt);
192
184
  const isFinal = isTerminalState(status.state);
193
185
  if (isFinal) {
194
186
  terminalPublished.add(taskId);
@@ -215,6 +207,7 @@ export function createA2AInteractionExecutor(deps) {
215
207
  cancelledTaskIds.delete(taskId);
216
208
  submittedPublished.delete(taskId);
217
209
  terminalPublished.delete(taskId);
210
+ artifactSignaturesByTaskId.delete(taskId);
218
211
  }
219
212
  function extractInstruction(message) {
220
213
  const textParts = message.parts
@@ -247,10 +240,10 @@ export function createA2AInteractionExecutor(deps) {
247
240
  function isTerminalState(state) {
248
241
  return (state === 'completed' || state === 'failed' || state === 'canceled' || state === 'rejected');
249
242
  }
250
- function buildTaskStatus(envelope, contextId) {
243
+ function buildTaskStatus(envelope, contextId, timestamp = envelope.createdAt ?? new Date().toISOString()) {
251
244
  return {
252
245
  state: mapState(envelope.state),
253
- timestamp: envelope.createdAt ?? new Date().toISOString(),
246
+ timestamp,
254
247
  message: buildStatusMessage(envelope, contextId),
255
248
  };
256
249
  }
@@ -271,7 +264,7 @@ export function createA2AInteractionExecutor(deps) {
271
264
  ],
272
265
  };
273
266
  }
274
- function buildTaskSnapshot(envelope, metadata) {
267
+ function buildTaskSnapshot(envelope, metadata, timestamp) {
275
268
  return {
276
269
  id: envelope.id,
277
270
  kind: 'task',
@@ -282,22 +275,98 @@ export function createA2AInteractionExecutor(deps) {
282
275
  attempt: envelope.attempt,
283
276
  },
284
277
  artifacts: convertArtifacts(envelope.artifacts),
285
- status: buildTaskStatus(envelope, metadata.contextId),
278
+ status: buildTaskStatus(envelope, metadata.contextId, timestamp),
286
279
  };
287
280
  }
281
+ function publishArtifactUpdates(envelope, metadata, eventBus) {
282
+ if (!envelope.artifacts || envelope.artifacts.length === 0) {
283
+ return;
284
+ }
285
+ let knownSignatures = artifactSignaturesByTaskId.get(envelope.id);
286
+ if (!knownSignatures) {
287
+ knownSignatures = new Map();
288
+ artifactSignaturesByTaskId.set(envelope.id, knownSignatures);
289
+ }
290
+ for (const artifact of envelope.artifacts) {
291
+ const signature = artifactSignature(artifact);
292
+ if (knownSignatures.get(artifact.id) === signature) {
293
+ continue;
294
+ }
295
+ knownSignatures.set(artifact.id, signature);
296
+ const update = {
297
+ kind: 'artifact-update',
298
+ taskId: envelope.id,
299
+ contextId: metadata.contextId,
300
+ artifact: {
301
+ artifactId: artifact.id,
302
+ name: artifact.name,
303
+ parts: [artifactToPart(artifact)],
304
+ },
305
+ append: false,
306
+ lastChunk: true,
307
+ metadata: { attempt: envelope.attempt },
308
+ };
309
+ eventBus.publish(update);
310
+ }
311
+ }
288
312
  function convertArtifacts(artifacts) {
289
313
  if (!artifacts || artifacts.length === 0)
290
314
  return undefined;
291
315
  return artifacts.map((artifact) => ({
292
316
  artifactId: artifact.id,
293
317
  name: artifact.name,
294
- parts: [
295
- {
296
- kind: 'text',
297
- text: artifact.content ?? artifact.name ?? '',
298
- },
299
- ],
318
+ parts: [artifactToPart(artifact)],
300
319
  }));
301
320
  }
321
+ function artifactToPart(artifact) {
322
+ if (artifact.url) {
323
+ return {
324
+ kind: 'file',
325
+ file: {
326
+ name: artifact.name,
327
+ mimeType: artifact.mimeType,
328
+ uri: artifact.url,
329
+ },
330
+ };
331
+ }
332
+ if (artifact.handle) {
333
+ return {
334
+ kind: 'file',
335
+ file: {
336
+ name: artifact.name,
337
+ mimeType: artifact.mimeType,
338
+ bytes: artifact.handle,
339
+ },
340
+ };
341
+ }
342
+ if (artifact.kind === 'data' && artifact.content) {
343
+ try {
344
+ return {
345
+ kind: 'data',
346
+ data: JSON.parse(artifact.content),
347
+ };
348
+ }
349
+ catch {
350
+ // Fall through to text
351
+ }
352
+ }
353
+ return {
354
+ kind: 'text',
355
+ text: artifact.content ?? artifact.name ?? '',
356
+ };
357
+ }
358
+ function artifactSignature(artifact) {
359
+ return JSON.stringify({
360
+ id: artifact.id,
361
+ name: artifact.name,
362
+ kind: artifact.kind,
363
+ mimeType: artifact.mimeType,
364
+ content: artifact.content,
365
+ delivery: artifact.delivery,
366
+ handle: artifact.handle,
367
+ url: artifact.url,
368
+ expiresAt: artifact.expiresAt,
369
+ });
370
+ }
302
371
  }
303
372
  //# sourceMappingURL=executor.js.map