salmon-loop 0.2.13 → 0.3.0
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.
- package/dist/cli/argv/headless-detection.js +27 -0
- package/dist/cli/chat-flow.js +11 -0
- package/dist/cli/chat.js +160 -24
- package/dist/cli/commands/chat.js +14 -7
- package/dist/cli/commands/flow-mode.js +63 -0
- package/dist/cli/commands/registry.js +2 -0
- package/dist/cli/commands/run/benchmark-artifacts.js +41 -0
- package/dist/cli/commands/run/early-errors.js +23 -0
- package/dist/cli/commands/run/handler.js +115 -27
- package/dist/cli/commands/run/headless-error-writer.js +8 -0
- package/dist/cli/commands/run/loop-params.js +2 -0
- package/dist/cli/commands/run/mode.js +2 -5
- package/dist/cli/commands/run/parse-options.js +16 -0
- package/dist/cli/commands/run/persist-session.js +10 -1
- package/dist/cli/commands/run/preflight.js +10 -0
- package/dist/cli/commands/run/reporter-factory.js +4 -0
- package/dist/cli/commands/run/runtime-llm.js +38 -11
- package/dist/cli/commands/run/runtime-options.js +2 -2
- package/dist/cli/commands/serve.js +97 -77
- package/dist/cli/commands/tool-names.js +78 -78
- package/dist/cli/headless/anthropic-stream-normalized-encoder.js +6 -1
- package/dist/cli/headless/json-protocol.js +37 -0
- package/dist/cli/headless/native-stream-normalized-encoder.js +6 -1
- package/dist/cli/headless/protocol-metadata.js +22 -0
- package/dist/cli/headless/stream-json-protocol.js +34 -1
- package/dist/cli/index.js +6 -4
- package/dist/cli/locales/en.js +30 -6
- package/dist/cli/program-bootstrap.js +10 -5
- package/dist/cli/program-commands.js +5 -1
- package/dist/cli/reporters/anthropic-stream.js +7 -1
- package/dist/cli/reporters/json.js +4 -0
- package/dist/cli/reporters/stream-json.js +17 -2
- package/dist/cli/run-cli.js +5 -3
- package/dist/cli/slash/runtime.js +27 -12
- package/dist/cli/ui/components/CommandInput.js +7 -3
- package/dist/cli/ui/components/CommandSuggestionList.js +1 -1
- package/dist/cli/utils/command-option-source.js +13 -0
- package/dist/cli/utils/verify-resolver.js +8 -4
- package/dist/cli/utils/worktree-prepare-resolver.js +7 -3
- package/dist/core/adapters/fs/file-adapter.js +6 -0
- package/dist/core/adapters/fs/filesystem.js +2 -1
- package/dist/core/adapters/git/git-adapter.js +78 -1
- package/dist/core/backends/salmon-loop/task-executor.js +1 -0
- package/dist/core/benchmark/patch-artifact.js +124 -0
- package/dist/core/benchmark/swe-bench.js +25 -0
- package/dist/core/config/load.js +18 -11
- package/dist/core/config/resolve-llm.js +12 -0
- package/dist/core/config/resolvers/server.js +0 -6
- package/dist/core/config/validate.js +73 -21
- package/dist/core/context/gatherers/metadata-gatherer.js +1 -0
- package/dist/core/context/gatherers/ripgrep-gatherer.js +84 -2
- package/dist/core/context/keywords.js +18 -4
- package/dist/core/context/service-deps.js +2 -2
- package/dist/core/context/service.js +8 -0
- package/dist/core/context/steps/context-gather.js +38 -0
- package/dist/core/context/summarization/summarizer.js +55 -12
- package/dist/core/context/targeting/target-resolver.js +4 -4
- package/dist/core/extensions/index.js +23 -5
- package/dist/core/extensions/merge.js +14 -0
- package/dist/core/extensions/paths.js +31 -0
- package/dist/core/extensions/schemas.js +8 -5
- package/dist/core/facades/cli-chat.js +6 -2
- package/dist/core/facades/cli-command-chat.js +1 -0
- package/dist/core/facades/cli-command-tool-names.js +2 -0
- package/dist/core/facades/cli-observability.js +1 -1
- package/dist/core/facades/cli-program-bootstrap.js +1 -0
- package/dist/core/facades/cli-run-handler.js +4 -2
- package/dist/core/facades/cli-run-persist-session.js +1 -0
- package/dist/core/facades/cli-serve.js +4 -4
- package/dist/core/facades/cli-utils-worktree.js +1 -1
- package/dist/core/failure/diagnostics.js +53 -1
- package/dist/core/grizzco/dsl/llm-strategy.js +4 -1
- package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +67 -9
- package/dist/core/grizzco/engine/pipeline/pipeline.js +6 -2
- package/dist/core/grizzco/engine/transaction/attempt-failure.js +90 -15
- package/dist/core/grizzco/engine/transaction/report-mapper.js +17 -3
- package/dist/core/grizzco/engine/transaction/transaction-runner.js +165 -7
- package/dist/core/grizzco/flows/AutopilotFlow.js +18 -0
- package/dist/core/grizzco/flows/flow-dispatch.js +11 -0
- package/dist/core/grizzco/steps/answer.js +13 -14
- package/dist/core/grizzco/steps/autopilot.js +396 -0
- package/dist/core/grizzco/steps/cache-sharing.js +29 -0
- package/dist/core/grizzco/steps/explore.js +37 -21
- package/dist/core/grizzco/steps/generateReview.js +2 -5
- package/dist/core/grizzco/steps/patch/apply-check.js +10 -0
- package/dist/core/grizzco/steps/patch/diff-normalization.js +70 -0
- package/dist/core/grizzco/steps/patch/diff-salvage.js +46 -0
- package/dist/core/grizzco/steps/patch/prompt-input.js +42 -0
- package/dist/core/grizzco/steps/patch.js +105 -146
- package/dist/core/grizzco/steps/plan.js +101 -25
- package/dist/core/grizzco/steps/preflight.js +5 -6
- package/dist/core/grizzco/steps/request-assembly.js +78 -0
- package/dist/core/grizzco/steps/research.js +39 -36
- package/dist/core/grizzco/steps/tool-runtime.js +47 -0
- package/dist/core/grizzco/steps/verify-shared.js +23 -0
- package/dist/core/grizzco/steps/verify.js +13 -21
- package/dist/core/interaction/orchestration/facade.js +1 -1
- package/dist/core/llm/ai-sdk/chat-executor.js +2 -0
- package/dist/core/llm/ai-sdk/high-level-phase-specs.js +63 -0
- package/dist/core/llm/ai-sdk/message-mapper.js +40 -10
- package/dist/core/llm/ai-sdk/provider-factory.js +14 -0
- package/dist/core/llm/ai-sdk/request-params.js +113 -1
- package/dist/core/llm/ai-sdk/result-mapper.js +16 -0
- package/dist/core/llm/ai-sdk.js +112 -27
- package/dist/core/llm/capabilities.js +12 -0
- package/dist/core/llm/contracts/repair.js +36 -30
- package/dist/core/llm/errors.js +83 -2
- package/dist/core/llm/message-composition.js +7 -22
- package/dist/core/llm/phase-router.js +29 -10
- package/dist/core/llm/redact.js +28 -3
- package/dist/core/llm/registry.js +2 -0
- package/dist/core/llm/request-augmentation.js +55 -0
- package/dist/core/llm/request-envelope.js +334 -0
- package/dist/core/llm/shared-request-assembly.js +35 -0
- package/dist/core/llm/stream-utils.js +13 -4
- package/dist/core/llm/utils.js +18 -29
- package/dist/core/memory/relevant-retrieval.js +144 -0
- package/dist/core/observability/logger.js +11 -2
- package/dist/core/patch/diff.js +1 -0
- package/dist/core/prompts/registry.js +39 -2
- package/dist/core/prompts/runtime.js +50 -12
- package/dist/core/prompts/templates/phases/patch_user.hbs +2 -5
- package/dist/core/prompts/templates/phases/research_user.hbs +11 -0
- package/dist/core/prompts/templates/phases/review_user.hbs +3 -0
- package/dist/core/prompts/templates/system/answer_system.hbs +5 -0
- package/dist/core/prompts/templates/system/autopilot_system.hbs +11 -0
- package/dist/core/prompts/templates/system/explore_system.hbs +14 -23
- package/dist/core/prompts/templates/system/main_system.hbs +4 -16
- package/dist/core/prompts/templates/system/patch_system.hbs +39 -8
- package/dist/core/prompts/templates/system/plan_system.hbs +86 -1
- package/dist/core/prompts/templates/system/research_system.hbs +2 -0
- package/dist/core/protocols/a2a/agent-card.js +5 -3
- package/dist/core/protocols/a2a/sdk/executor.js +2 -1
- package/dist/core/protocols/a2a/sdk/server.js +0 -1
- package/dist/core/protocols/acp/formal-agent.js +300 -58
- package/dist/core/protocols/acp/handlers.js +5 -1
- package/dist/core/protocols/acp/permission-provider.js +1 -1
- package/dist/core/protocols/shared/flow-mode-mapping.js +23 -0
- package/dist/core/public-capabilities/flow-mode-metadata.js +39 -0
- package/dist/core/public-capabilities/projections.js +29 -0
- package/dist/core/public-capabilities/registry.js +26 -0
- package/dist/core/public-capabilities/types.js +2 -0
- package/dist/core/runtime/agent-server-runtime.js +47 -43
- package/dist/core/runtime/execution-profile.js +67 -0
- package/dist/core/session/artifact-state.js +160 -0
- package/dist/core/session/compaction/index.js +183 -0
- package/dist/core/session/compaction/microcompact.js +78 -0
- package/dist/core/session/compaction/tracking.js +48 -0
- package/dist/core/session/compaction/types.js +11 -0
- package/dist/core/session/compression.js +8 -0
- package/dist/core/session/manager.js +244 -8
- package/dist/core/session/pruning-strategy.js +55 -9
- package/dist/core/session/replacement-preview-provider.js +24 -0
- package/dist/core/session/replacement-state.js +131 -0
- package/dist/core/session/resume-repair/pipeline.js +79 -0
- package/dist/core/session/resume-repair/stages/load-raw-archive-state.js +40 -0
- package/dist/core/session/resume-repair/stages/reattach-runtime-state.js +8 -0
- package/dist/core/session/resume-repair/stages/recover-orphaned-branches.js +10 -0
- package/dist/core/session/resume-repair/stages/relink-boundary-and-tail.js +36 -0
- package/dist/core/session/resume-repair/stages/replay-startup-hooks.js +23 -0
- package/dist/core/session/resume-repair/stages/rescue-stale-metadata.js +17 -0
- package/dist/core/session/resume-repair/types.js +2 -0
- package/dist/core/session/summary-sync.js +164 -13
- package/dist/core/session/token-tracker.js +6 -0
- package/dist/core/skills/audit.js +34 -0
- package/dist/core/skills/bridge.js +84 -7
- package/dist/core/skills/discovery.js +94 -0
- package/dist/core/skills/feature-flags.js +52 -0
- package/dist/core/skills/index.js +1 -1
- package/dist/core/skills/loader.js +195 -20
- package/dist/core/skills/parser.js +296 -24
- package/dist/core/skills/permissions.js +117 -0
- package/dist/core/skills/runtime/MicroTaskRunner.js +10 -4
- package/dist/core/skills/runtime/SkillRunner.js +240 -61
- package/dist/core/strata/layers/shadow-driver/shadow-driver.js +37 -7
- package/dist/core/strata/layers/worktree.js +67 -10
- package/dist/core/strata/runtime/synchronizer.js +29 -2
- package/dist/core/streaming/stream-assembler.js +75 -31
- package/dist/core/sub-agent/context-snapshot.js +156 -0
- package/dist/core/sub-agent/core/loop.js +1 -1
- package/dist/core/sub-agent/core/manager.js +119 -20
- package/dist/core/sub-agent/dispatch-policy.js +29 -0
- package/dist/core/sub-agent/prefix-consistency.js +48 -0
- package/dist/core/sub-agent/registry-defaults.js +4 -0
- package/dist/core/sub-agent/tools/task-spawn.js +79 -2
- package/dist/core/sub-agent/types.js +134 -5
- package/dist/core/tools/audit.js +13 -4
- package/dist/core/tools/builtin/ast-grep.js +1 -1
- package/dist/core/tools/builtin/ast.js +1 -1
- package/dist/core/tools/builtin/benchmark.js +360 -0
- package/dist/core/tools/builtin/code-search/backends/rg.js +2 -1
- package/dist/core/tools/builtin/code-search/executor.js +6 -1
- package/dist/core/tools/builtin/code-search/spec.js +26 -2
- package/dist/core/tools/builtin/fs.js +256 -23
- package/dist/core/tools/builtin/git.js +2 -2
- package/dist/core/tools/builtin/index.js +51 -2
- package/dist/core/tools/builtin/interaction.js +8 -1
- package/dist/core/tools/builtin/plan.js +37 -15
- package/dist/core/tools/builtin/shell.js +1 -1
- package/dist/core/tools/loader.js +39 -16
- package/dist/core/tools/mapper.js +17 -3
- package/dist/core/tools/mcp/client.js +2 -1
- package/dist/core/tools/parallel/scheduler.js +35 -4
- package/dist/core/tools/permissions/permission-rules.js +5 -10
- package/dist/core/tools/policy.js +6 -1
- package/dist/core/tools/recoverable-tool-errors.js +10 -0
- package/dist/core/tools/router.js +24 -6
- package/dist/core/tools/session.js +458 -48
- package/dist/core/tools/tool-visibility.js +62 -0
- package/dist/core/tools/types.js +9 -1
- package/dist/core/types/execution.js +4 -0
- package/dist/core/types/flow-mode.js +8 -0
- package/dist/core/utils/path.js +52 -0
- package/dist/core/verification/runner.js +4 -1
- package/dist/core/version.js +17 -0
- package/dist/languages/typescript/index.js +4 -1
- package/dist/locales/en.js +35 -2
- package/dist/utils/eol.js +1 -1
- package/package.json +14 -7
- package/scripts/fix-es-abstract-compat.js +77 -0
- package/dist/core/runtime/fastify-server-bundle.js +0 -26
- package/dist/core/runtime/sidecar-fastify-plugin.js +0 -35
- package/dist/core/runtime/sidecar-paths.js +0 -47
- package/dist/core/runtime/sidecar-route-catalog.js +0 -103
|
@@ -6,8 +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';
|
|
10
12
|
import { buildCanonicalExecutionRequest } from '../shared/execution-request.js';
|
|
13
|
+
import { parseAcpFlowMode } from '../shared/flow-mode-mapping.js';
|
|
11
14
|
import { createAcpCommandRunner } from './acp-command-runner.js';
|
|
12
15
|
import { createAcpFileSystem } from './acp-filesystem.js';
|
|
13
16
|
import { createAcpSessionStore, isTerminalTaskEvent } from './handlers.js';
|
|
@@ -35,7 +38,8 @@ const ACP_PERMISSION_POLICY_CONFIG_ID = '_salmonloop_permission_policy';
|
|
|
35
38
|
const ACP_MODE_CONFIG_ID = '_salmonloop_mode';
|
|
36
39
|
const ACP_PERMISSION_POLICY_ASK = 'ask';
|
|
37
40
|
const ACP_PERMISSION_POLICY_DENY_ALL = 'deny_all';
|
|
38
|
-
const
|
|
41
|
+
const ACP_PERMISSION_POLICY_ALLOW_ALL = 'allow_all';
|
|
42
|
+
const ACP_DEFAULT_MODE_ID = 'autopilot';
|
|
39
43
|
const ACP_SESSION_STORE_MAX_ENTRIES = 200;
|
|
40
44
|
const ACP_SESSION_STORE_MAX_AGE_MS = 1000 * 60 * 60 * 24 * 30;
|
|
41
45
|
const ACP_SESSION_STORE_LOCK_STALE_MS = 1000 * 30;
|
|
@@ -85,11 +89,35 @@ function buildJsonResourceContentBlock(data) {
|
|
|
85
89
|
const ACP_AVAILABLE_COMMANDS = [
|
|
86
90
|
{ name: 'help', description: text.acp.slashHelpDescription },
|
|
87
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
|
+
}
|
|
88
101
|
function formatResourceLink(block) {
|
|
89
102
|
const title = block.title ?? block.name ?? block.uri;
|
|
90
103
|
const description = block.description ? ` - ${block.description}` : '';
|
|
91
104
|
return `Resource: ${title} (${block.uri})${description}`;
|
|
92
105
|
}
|
|
106
|
+
function formatEmbeddedResource(block) {
|
|
107
|
+
const resource = block.resource;
|
|
108
|
+
const uri = typeof resource.uri === 'string' ? resource.uri : 'embedded-resource';
|
|
109
|
+
const mimeType = typeof resource.mimeType === 'string' ? resource.mimeType : undefined;
|
|
110
|
+
if (typeof resource.text === 'string') {
|
|
111
|
+
const header = mimeType
|
|
112
|
+
? `Embedded resource: ${uri} (${mimeType})`
|
|
113
|
+
: `Embedded resource: ${uri}`;
|
|
114
|
+
return `${header}\n${resource.text}`;
|
|
115
|
+
}
|
|
116
|
+
const header = mimeType
|
|
117
|
+
? `Embedded binary resource: ${uri} (${mimeType})`
|
|
118
|
+
: `Embedded binary resource: ${uri}`;
|
|
119
|
+
return header;
|
|
120
|
+
}
|
|
93
121
|
function extractTextFromPrompt(prompt, capabilities) {
|
|
94
122
|
const parts = [];
|
|
95
123
|
for (const block of prompt) {
|
|
@@ -114,6 +142,7 @@ function extractTextFromPrompt(prompt, capabilities) {
|
|
|
114
142
|
if (!capabilities.embeddedContext) {
|
|
115
143
|
throw new RequestError(-32000, 'Prompt content type resource is not supported');
|
|
116
144
|
}
|
|
145
|
+
parts.push(formatEmbeddedResource(block));
|
|
117
146
|
break;
|
|
118
147
|
default:
|
|
119
148
|
throw new RequestError(-32602, 'Invalid params: unsupported content block type');
|
|
@@ -230,11 +259,10 @@ function loopEventToSessionUpdate(event) {
|
|
|
230
259
|
return null;
|
|
231
260
|
}
|
|
232
261
|
}
|
|
233
|
-
function createSessionRuntimeState() {
|
|
234
|
-
return createSessionRuntimeStateFromPersisted();
|
|
235
|
-
}
|
|
236
262
|
function isPermissionPolicyValue(value) {
|
|
237
|
-
return value === ACP_PERMISSION_POLICY_ASK ||
|
|
263
|
+
return (value === ACP_PERMISSION_POLICY_ASK ||
|
|
264
|
+
value === ACP_PERMISSION_POLICY_DENY_ALL ||
|
|
265
|
+
value === ACP_PERMISSION_POLICY_ALLOW_ALL);
|
|
238
266
|
}
|
|
239
267
|
function buildConfigOptions(state) {
|
|
240
268
|
return [
|
|
@@ -255,26 +283,24 @@ function buildConfigOptions(state) {
|
|
|
255
283
|
name: text.acp.permissionPolicyDenyAllName,
|
|
256
284
|
description: text.acp.permissionPolicyDenyAllDescription,
|
|
257
285
|
},
|
|
286
|
+
{
|
|
287
|
+
value: ACP_PERMISSION_POLICY_ALLOW_ALL,
|
|
288
|
+
name: text.acp.permissionPolicyAllowAllName,
|
|
289
|
+
description: text.acp.permissionPolicyAllowAllDescription,
|
|
290
|
+
},
|
|
258
291
|
],
|
|
259
292
|
},
|
|
260
293
|
{
|
|
261
294
|
type: 'select',
|
|
262
295
|
id: ACP_MODE_CONFIG_ID,
|
|
263
|
-
name: '
|
|
264
|
-
description:
|
|
296
|
+
name: 'Execution Flow',
|
|
297
|
+
description: 'Choose how the agent should execute this session.',
|
|
265
298
|
currentValue: state.modeId,
|
|
266
|
-
options:
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
value: 'yolo',
|
|
274
|
-
name: 'YOLO',
|
|
275
|
-
description: text.acp.modeYoloDescription,
|
|
276
|
-
},
|
|
277
|
-
],
|
|
299
|
+
options: ACP_PUBLIC_MODES.map((mode) => ({
|
|
300
|
+
value: mode.id,
|
|
301
|
+
name: mode.name,
|
|
302
|
+
description: mode.description,
|
|
303
|
+
})),
|
|
278
304
|
},
|
|
279
305
|
];
|
|
280
306
|
}
|
|
@@ -313,27 +339,13 @@ function buildSessionInfoUpdateIfChanged(session, state) {
|
|
|
313
339
|
updatedAt,
|
|
314
340
|
};
|
|
315
341
|
}
|
|
316
|
-
function isSessionModeId(value) {
|
|
317
|
-
return value === 'interactive' || value === 'yolo';
|
|
318
|
-
}
|
|
319
342
|
function buildCurrentModeUpdate(modeId) {
|
|
320
343
|
return { sessionUpdate: 'current_mode_update', currentModeId: modeId };
|
|
321
344
|
}
|
|
322
345
|
function buildModesState(modeId) {
|
|
323
346
|
return {
|
|
324
347
|
currentModeId: modeId,
|
|
325
|
-
availableModes:
|
|
326
|
-
{
|
|
327
|
-
id: 'interactive',
|
|
328
|
-
name: 'Interactive',
|
|
329
|
-
description: text.acp.modeInteractiveDescription,
|
|
330
|
-
},
|
|
331
|
-
{
|
|
332
|
-
id: 'yolo',
|
|
333
|
-
name: 'YOLO',
|
|
334
|
-
description: text.acp.modeYoloDescription,
|
|
335
|
-
},
|
|
336
|
-
],
|
|
348
|
+
availableModes: ACP_PUBLIC_MODES.map((mode) => ({ ...mode })),
|
|
337
349
|
};
|
|
338
350
|
}
|
|
339
351
|
function buildCurrentModeUpdateIfChanged(state) {
|
|
@@ -343,18 +355,28 @@ function buildCurrentModeUpdateIfChanged(state) {
|
|
|
343
355
|
state.lastModeDigest = digest;
|
|
344
356
|
return buildCurrentModeUpdate(state.modeId);
|
|
345
357
|
}
|
|
358
|
+
function getLegacyPermissionPolicyForModeValue(value) {
|
|
359
|
+
const normalized = String(value ?? '')
|
|
360
|
+
.trim()
|
|
361
|
+
.toLowerCase();
|
|
362
|
+
if (normalized === 'interactive')
|
|
363
|
+
return ACP_PERMISSION_POLICY_ASK;
|
|
364
|
+
if (normalized === 'yolo')
|
|
365
|
+
return ACP_PERMISSION_POLICY_ALLOW_ALL;
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
346
368
|
function getPermissionPolicyForAuthorization(state) {
|
|
347
|
-
if (state.modeId === 'yolo')
|
|
348
|
-
return 'allow_all';
|
|
349
369
|
return state.permissionPolicy;
|
|
350
370
|
}
|
|
351
371
|
function createSessionRuntimeStateFromPersisted(input) {
|
|
372
|
+
const defaultPermissionPolicy = isPermissionPolicyValue(String(input?.defaultPermissionPolicy))
|
|
373
|
+
? input?.defaultPermissionPolicy
|
|
374
|
+
: ACP_PERMISSION_POLICY_ASK;
|
|
352
375
|
const permissionPolicy = isPermissionPolicyValue(String(input?.permissionPolicy))
|
|
353
376
|
? input?.permissionPolicy
|
|
354
|
-
:
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
: (input?.defaultModeId ?? ACP_DEFAULT_MODE_ID);
|
|
377
|
+
: defaultPermissionPolicy;
|
|
378
|
+
const defaultModeId = resolveExposedAcpModeId(input?.defaultModeId);
|
|
379
|
+
const modeId = resolveExposedAcpModeId(input?.modeId, defaultModeId);
|
|
358
380
|
const state = {
|
|
359
381
|
runtimePlanSessionId: null,
|
|
360
382
|
runtimePlanPathHint: null,
|
|
@@ -444,6 +466,74 @@ function buildPlanUpdateFromCoreIfChanged(read, state) {
|
|
|
444
466
|
entries,
|
|
445
467
|
};
|
|
446
468
|
}
|
|
469
|
+
function envListToRecord(env) {
|
|
470
|
+
const record = {};
|
|
471
|
+
for (const entry of env) {
|
|
472
|
+
record[entry.name] = entry.value;
|
|
473
|
+
}
|
|
474
|
+
return record;
|
|
475
|
+
}
|
|
476
|
+
function headersListToRecord(headers) {
|
|
477
|
+
const record = {};
|
|
478
|
+
for (const entry of headers) {
|
|
479
|
+
record[entry.name] = entry.value;
|
|
480
|
+
}
|
|
481
|
+
return record;
|
|
482
|
+
}
|
|
483
|
+
function acpMcpServersToResolved(mcpServers) {
|
|
484
|
+
if (!Array.isArray(mcpServers))
|
|
485
|
+
return [];
|
|
486
|
+
const resolved = [];
|
|
487
|
+
for (const server of mcpServers) {
|
|
488
|
+
const transportType = 'type' in server ? server.type : 'stdio';
|
|
489
|
+
if (transportType === 'sse' || transportType === 'acp') {
|
|
490
|
+
throw new RequestError(-32602, `Invalid params: unsupported MCP server transport "${transportType}"`);
|
|
491
|
+
}
|
|
492
|
+
if ('type' in server && server.type === 'http') {
|
|
493
|
+
const httpServer = server;
|
|
494
|
+
resolved.push({
|
|
495
|
+
name: httpServer.name,
|
|
496
|
+
enabled: true,
|
|
497
|
+
transport: 'http',
|
|
498
|
+
url: httpServer.url,
|
|
499
|
+
headers: headersListToRecord(httpServer.headers),
|
|
500
|
+
allowTools: ['*'],
|
|
501
|
+
allowResources: [],
|
|
502
|
+
scope: 'repo',
|
|
503
|
+
});
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (transportType !== 'stdio') {
|
|
507
|
+
throw new RequestError(-32602, `Invalid params: unsupported MCP server transport "${transportType}"`);
|
|
508
|
+
}
|
|
509
|
+
const stdioServer = server;
|
|
510
|
+
resolved.push({
|
|
511
|
+
name: stdioServer.name,
|
|
512
|
+
enabled: true,
|
|
513
|
+
transport: 'stdio',
|
|
514
|
+
command: stdioServer.command,
|
|
515
|
+
args: stdioServer.args,
|
|
516
|
+
env: envListToRecord(stdioServer.env),
|
|
517
|
+
allowTools: ['*'],
|
|
518
|
+
allowResources: [],
|
|
519
|
+
scope: 'repo',
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
return resolved;
|
|
523
|
+
}
|
|
524
|
+
function acpMcpServersToExtensions(mcpServers) {
|
|
525
|
+
const resolvedServers = acpMcpServersToResolved(mcpServers);
|
|
526
|
+
if (resolvedServers.length === 0)
|
|
527
|
+
return undefined;
|
|
528
|
+
return {
|
|
529
|
+
mcpServers: resolvedServers,
|
|
530
|
+
toolPlugins: [],
|
|
531
|
+
skillDiscovery: { paths: [], scope: 'repo' },
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function validateAcpMcpServers(mcpServers) {
|
|
535
|
+
void acpMcpServersToResolved(mcpServers);
|
|
536
|
+
}
|
|
447
537
|
function extractSlashInput(prompt) {
|
|
448
538
|
if (prompt.length !== 1)
|
|
449
539
|
return null;
|
|
@@ -499,8 +589,9 @@ export function createAcpFormalAgent(deps) {
|
|
|
499
589
|
embeddedContext: deps.capabilityPolicy?.promptCapabilities?.embeddedContext ?? false,
|
|
500
590
|
};
|
|
501
591
|
const mcpCapabilities = {
|
|
502
|
-
http: deps.capabilityPolicy?.mcpCapabilities?.http ??
|
|
592
|
+
http: deps.capabilityPolicy?.mcpCapabilities?.http ?? true,
|
|
503
593
|
sse: deps.capabilityPolicy?.mcpCapabilities?.sse ?? false,
|
|
594
|
+
acp: deps.capabilityPolicy?.mcpCapabilities?.acp ?? false,
|
|
504
595
|
};
|
|
505
596
|
const sessionPersistencePath = deps.sessionPersistencePath;
|
|
506
597
|
const sessionStorePolicy = {
|
|
@@ -514,6 +605,7 @@ export function createAcpFormalAgent(deps) {
|
|
|
514
605
|
const executionBinding = deps.executionBinding ?? 'local';
|
|
515
606
|
let sessionsHydrated = false;
|
|
516
607
|
let hydratePromise = null;
|
|
608
|
+
const deletedSessionIds = new Map();
|
|
517
609
|
function parseTimestamp(value) {
|
|
518
610
|
if (typeof value !== 'string' || value.length === 0)
|
|
519
611
|
return 0;
|
|
@@ -527,6 +619,31 @@ export function createAcpFormalAgent(deps) {
|
|
|
527
619
|
.sort((a, b) => parseTimestamp(b.updatedAt) - parseTimestamp(a.updatedAt))
|
|
528
620
|
.slice(0, sessionStorePolicy.maxEntries);
|
|
529
621
|
}
|
|
622
|
+
function normalizeDeletedSessionRecords(input) {
|
|
623
|
+
if (!Array.isArray(input))
|
|
624
|
+
return [];
|
|
625
|
+
const byId = new Map();
|
|
626
|
+
for (const entry of input) {
|
|
627
|
+
if (!entry || typeof entry !== 'object')
|
|
628
|
+
continue;
|
|
629
|
+
const record = entry;
|
|
630
|
+
if (typeof record.id !== 'string' || !record.id)
|
|
631
|
+
continue;
|
|
632
|
+
if (typeof record.deletedAt !== 'string' || !record.deletedAt)
|
|
633
|
+
continue;
|
|
634
|
+
const current = byId.get(record.id);
|
|
635
|
+
if (!current || parseTimestamp(record.deletedAt) > parseTimestamp(current.deletedAt)) {
|
|
636
|
+
byId.set(record.id, { id: record.id, deletedAt: record.deletedAt });
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return Array.from(byId.values());
|
|
640
|
+
}
|
|
641
|
+
function pruneDeletedSessionRecords(records) {
|
|
642
|
+
const cutoff = Date.now() - sessionStorePolicy.maxAgeMs;
|
|
643
|
+
return normalizeDeletedSessionRecords(records)
|
|
644
|
+
.filter((record) => parseTimestamp(record.deletedAt) >= cutoff)
|
|
645
|
+
.sort((a, b) => parseTimestamp(b.deletedAt) - parseTimestamp(a.deletedAt));
|
|
646
|
+
}
|
|
530
647
|
function normalizePersistedSessionStore(input) {
|
|
531
648
|
if (!input || typeof input !== 'object') {
|
|
532
649
|
return { schemaVersion: 2, sessions: [] };
|
|
@@ -546,13 +663,20 @@ export function createAcpFormalAgent(deps) {
|
|
|
546
663
|
title: entry.title,
|
|
547
664
|
taskId: undefined,
|
|
548
665
|
history: [],
|
|
549
|
-
permissionPolicy:
|
|
550
|
-
|
|
666
|
+
permissionPolicy: isPermissionPolicyValue(String(deps.defaultPermissionPolicy))
|
|
667
|
+
? deps.defaultPermissionPolicy
|
|
668
|
+
: ACP_PERMISSION_POLICY_ASK,
|
|
669
|
+
modeId: resolveExposedAcpModeId(deps.defaultModeId),
|
|
551
670
|
})),
|
|
671
|
+
deletedSessions: [],
|
|
552
672
|
};
|
|
553
673
|
}
|
|
554
674
|
if (raw.schemaVersion === 2) {
|
|
555
|
-
return {
|
|
675
|
+
return {
|
|
676
|
+
schemaVersion: 2,
|
|
677
|
+
sessions: raw.sessions,
|
|
678
|
+
deletedSessions: pruneDeletedSessionRecords(raw.deletedSessions),
|
|
679
|
+
};
|
|
556
680
|
}
|
|
557
681
|
return { schemaVersion: 2, sessions: [] };
|
|
558
682
|
}
|
|
@@ -608,6 +732,7 @@ export function createAcpFormalAgent(deps) {
|
|
|
608
732
|
}
|
|
609
733
|
}
|
|
610
734
|
const payload = { schemaVersion: 2, sessions: prunedRecords };
|
|
735
|
+
const payloadDeletedSessions = pruneDeletedSessionRecords(Array.from(deletedSessionIds, ([id, deletedAt]) => ({ id, deletedAt })));
|
|
611
736
|
const primaryRepoPath = prunedRecords[0]?.cwd;
|
|
612
737
|
const lockAuditDetails = {
|
|
613
738
|
lockPath,
|
|
@@ -691,13 +816,24 @@ export function createAcpFormalAgent(deps) {
|
|
|
691
816
|
// ignore read failure; writing fresh payload is acceptable
|
|
692
817
|
}
|
|
693
818
|
const merged = new Map();
|
|
819
|
+
const mergedDeletedSessions = pruneDeletedSessionRecords([
|
|
820
|
+
...(existing.deletedSessions ?? []),
|
|
821
|
+
...payloadDeletedSessions,
|
|
822
|
+
]);
|
|
823
|
+
const mergedDeletedIds = new Set(mergedDeletedSessions.map((record) => record.id));
|
|
824
|
+
for (const record of mergedDeletedSessions) {
|
|
825
|
+
deletedSessionIds.set(record.id, record.deletedAt);
|
|
826
|
+
}
|
|
694
827
|
for (const entry of existing.sessions)
|
|
695
828
|
merged.set(entry.id, entry);
|
|
696
829
|
for (const entry of payload.sessions)
|
|
697
830
|
merged.set(entry.id, entry);
|
|
831
|
+
for (const id of mergedDeletedIds)
|
|
832
|
+
merged.delete(id);
|
|
698
833
|
const mergedPayload = {
|
|
699
834
|
schemaVersion: 2,
|
|
700
835
|
sessions: pruneSessionRecords(Array.from(merged.values())),
|
|
836
|
+
deletedSessions: mergedDeletedSessions,
|
|
701
837
|
};
|
|
702
838
|
await writeFile(tempPath, JSON.stringify(mergedPayload, null, 2), 'utf8');
|
|
703
839
|
await rename(tempPath, sessionPersistencePath);
|
|
@@ -740,7 +876,13 @@ export function createAcpFormalAgent(deps) {
|
|
|
740
876
|
try {
|
|
741
877
|
const raw = await readFile(sessionPersistencePath, 'utf8');
|
|
742
878
|
const parsed = normalizePersistedSessionStore(JSON.parse(raw));
|
|
879
|
+
const deletedIds = new Set(parsed.deletedSessions?.map((record) => record.id) ?? []);
|
|
880
|
+
for (const record of parsed.deletedSessions ?? []) {
|
|
881
|
+
deletedSessionIds.set(record.id, record.deletedAt);
|
|
882
|
+
}
|
|
743
883
|
for (const stored of pruneSessionRecords(parsed.sessions)) {
|
|
884
|
+
if (deletedIds.has(stored.id))
|
|
885
|
+
continue;
|
|
744
886
|
sessions.upsert({
|
|
745
887
|
id: stored.id,
|
|
746
888
|
cwd: stored.cwd,
|
|
@@ -757,6 +899,7 @@ export function createAcpFormalAgent(deps) {
|
|
|
757
899
|
if (!sessionRuntime.has(stored.id)) {
|
|
758
900
|
sessionRuntime.set(stored.id, createSessionRuntimeStateFromPersisted({
|
|
759
901
|
permissionPolicy: stored.permissionPolicy,
|
|
902
|
+
defaultPermissionPolicy: deps.defaultPermissionPolicy,
|
|
760
903
|
modeId: stored.modeId,
|
|
761
904
|
defaultModeId: deps.defaultModeId,
|
|
762
905
|
}));
|
|
@@ -876,28 +1019,58 @@ export function createAcpFormalAgent(deps) {
|
|
|
876
1019
|
}
|
|
877
1020
|
async function loadSessionInternal(params) {
|
|
878
1021
|
await hydrateSessionsOnce();
|
|
1022
|
+
validateAcpMcpServers(params.mcpServers);
|
|
879
1023
|
const session = sessions.get(params.sessionId);
|
|
880
1024
|
if (!session) {
|
|
881
1025
|
throw new RequestError(-32004, `Session not found: ${params.sessionId}`);
|
|
882
1026
|
}
|
|
883
1027
|
if (session.cwd !== params.cwd) {
|
|
1028
|
+
throw new RequestError(-32602, 'Invalid params: cwd does not match session cwd');
|
|
1029
|
+
}
|
|
1030
|
+
if (params.mcpServers) {
|
|
1031
|
+
sessions.update(params.sessionId, (current) => ({
|
|
1032
|
+
...current,
|
|
1033
|
+
mcpServers: params.mcpServers,
|
|
1034
|
+
}));
|
|
1035
|
+
await persistSessionsBestEffort();
|
|
1036
|
+
}
|
|
1037
|
+
return session;
|
|
1038
|
+
}
|
|
1039
|
+
async function resumeSessionInternal(params) {
|
|
1040
|
+
await hydrateSessionsOnce();
|
|
1041
|
+
validateAcpMcpServers(params.mcpServers);
|
|
1042
|
+
const session = sessions.get(params.sessionId);
|
|
1043
|
+
if (!session) {
|
|
1044
|
+
throw new RequestError(-32004, `Session not found: ${params.sessionId}`);
|
|
1045
|
+
}
|
|
1046
|
+
if (session.cwd !== params.cwd) {
|
|
1047
|
+
throw new RequestError(-32602, 'Invalid params: cwd does not match session cwd');
|
|
1048
|
+
}
|
|
1049
|
+
if (params.mcpServers) {
|
|
884
1050
|
sessions.update(params.sessionId, (current) => ({
|
|
885
1051
|
...current,
|
|
886
|
-
cwd: params.cwd,
|
|
887
1052
|
mcpServers: params.mcpServers ?? [],
|
|
888
1053
|
}));
|
|
889
1054
|
await persistSessionsBestEffort();
|
|
890
1055
|
}
|
|
891
1056
|
return session;
|
|
892
1057
|
}
|
|
1058
|
+
function toSessionInfo(session) {
|
|
1059
|
+
return {
|
|
1060
|
+
sessionId: session.id,
|
|
1061
|
+
cwd: session.cwd,
|
|
1062
|
+
title: typeof session.title === 'string' && session.title.trim() ? session.title : null,
|
|
1063
|
+
updatedAt: session.updatedAt,
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
893
1066
|
function ensureSessionRuntimeState(sessionId) {
|
|
894
1067
|
const existing = sessionRuntime.get(sessionId);
|
|
895
1068
|
if (existing)
|
|
896
1069
|
return existing;
|
|
897
|
-
const created =
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
}
|
|
1070
|
+
const created = createSessionRuntimeStateFromPersisted({
|
|
1071
|
+
defaultPermissionPolicy: deps.defaultPermissionPolicy,
|
|
1072
|
+
defaultModeId: deps.defaultModeId,
|
|
1073
|
+
});
|
|
901
1074
|
sessionRuntime.set(sessionId, created);
|
|
902
1075
|
return created;
|
|
903
1076
|
}
|
|
@@ -923,7 +1096,11 @@ export function createAcpFormalAgent(deps) {
|
|
|
923
1096
|
loadSession: loadSessionCapability,
|
|
924
1097
|
promptCapabilities: promptCapabilities,
|
|
925
1098
|
mcpCapabilities: mcpCapabilities,
|
|
926
|
-
sessionCapabilities: {
|
|
1099
|
+
sessionCapabilities: {
|
|
1100
|
+
list: {},
|
|
1101
|
+
resume: {},
|
|
1102
|
+
close: {},
|
|
1103
|
+
},
|
|
927
1104
|
},
|
|
928
1105
|
};
|
|
929
1106
|
},
|
|
@@ -935,6 +1112,7 @@ export function createAcpFormalAgent(deps) {
|
|
|
935
1112
|
if (!isAbsolutePath(params.cwd)) {
|
|
936
1113
|
throw new RequestError(-32602, 'Invalid params: cwd must be an absolute path');
|
|
937
1114
|
}
|
|
1115
|
+
validateAcpMcpServers(params.mcpServers);
|
|
938
1116
|
const session = sessions.create({
|
|
939
1117
|
cwd: params.cwd,
|
|
940
1118
|
mcpServers: params.mcpServers ?? [],
|
|
@@ -1004,12 +1182,10 @@ export function createAcpFormalAgent(deps) {
|
|
|
1004
1182
|
if (modeUpdate)
|
|
1005
1183
|
await emitSessionUpdate(session.id, modeUpdate);
|
|
1006
1184
|
for (const entry of session.history) {
|
|
1007
|
-
if (entry.role !== 'assistant')
|
|
1008
|
-
continue;
|
|
1009
1185
|
for (const block of entry.content) {
|
|
1010
1186
|
if (block.type === 'text' && typeof block.text === 'string' && block.text.trim()) {
|
|
1011
1187
|
await emitSessionUpdate(session.id, {
|
|
1012
|
-
sessionUpdate: 'agent_message_chunk',
|
|
1188
|
+
sessionUpdate: entry.role === 'assistant' ? 'agent_message_chunk' : 'user_message_chunk',
|
|
1013
1189
|
content: buildTextContentBlock(block.text),
|
|
1014
1190
|
});
|
|
1015
1191
|
}
|
|
@@ -1082,12 +1258,63 @@ export function createAcpFormalAgent(deps) {
|
|
|
1082
1258
|
}
|
|
1083
1259
|
return response;
|
|
1084
1260
|
},
|
|
1261
|
+
async listSessions(params) {
|
|
1262
|
+
await hydrateSessionsOnce();
|
|
1263
|
+
if (typeof params.cwd === 'string' && params.cwd && !isAbsolutePath(params.cwd)) {
|
|
1264
|
+
throw new RequestError(-32602, 'Invalid params: cwd must be an absolute path');
|
|
1265
|
+
}
|
|
1266
|
+
const filtered = sessions
|
|
1267
|
+
.list()
|
|
1268
|
+
.filter((session) => !params.cwd || session.cwd === params.cwd)
|
|
1269
|
+
.sort((a, b) => parseTimestamp(b.updatedAt) - parseTimestamp(a.updatedAt));
|
|
1270
|
+
return {
|
|
1271
|
+
sessions: filtered.map(toSessionInfo),
|
|
1272
|
+
};
|
|
1273
|
+
},
|
|
1274
|
+
async resumeSession(params) {
|
|
1275
|
+
const session = await resumeSessionInternal({
|
|
1276
|
+
sessionId: params.sessionId,
|
|
1277
|
+
cwd: params.cwd,
|
|
1278
|
+
mcpServers: params.mcpServers,
|
|
1279
|
+
});
|
|
1280
|
+
const runtimeState = ensureSessionRuntimeState(session.id);
|
|
1281
|
+
runtimeState.lastSessionInfoDigest = null;
|
|
1282
|
+
await emitSessionInfoUpdateBestEffort(session.id);
|
|
1283
|
+
const commandsUpdate = buildAvailableCommandsUpdateIfChanged(runtimeState);
|
|
1284
|
+
if (commandsUpdate)
|
|
1285
|
+
await emitSessionUpdate(session.id, commandsUpdate);
|
|
1286
|
+
const modeUpdate = buildCurrentModeUpdateIfChanged(runtimeState);
|
|
1287
|
+
if (modeUpdate)
|
|
1288
|
+
await emitSessionUpdate(session.id, modeUpdate);
|
|
1289
|
+
return {
|
|
1290
|
+
configOptions: buildConfigOptions(runtimeState),
|
|
1291
|
+
modes: buildModesState(runtimeState.modeId),
|
|
1292
|
+
};
|
|
1293
|
+
},
|
|
1294
|
+
async closeSession(params) {
|
|
1295
|
+
await hydrateSessionsOnce();
|
|
1296
|
+
const session = sessions.get(params.sessionId);
|
|
1297
|
+
if (!session)
|
|
1298
|
+
return {};
|
|
1299
|
+
sessions.update(params.sessionId, (current) => ({ ...current, cancelRequested: true }));
|
|
1300
|
+
if (session.taskId) {
|
|
1301
|
+
await deps.facade.cancelTask(session.taskId);
|
|
1302
|
+
}
|
|
1303
|
+
deletedSessionIds.set(params.sessionId, new Date().toISOString());
|
|
1304
|
+
sessionRuntime.delete(params.sessionId);
|
|
1305
|
+
sessions.delete(params.sessionId);
|
|
1306
|
+
await persistSessionsBestEffort();
|
|
1307
|
+
return {};
|
|
1308
|
+
},
|
|
1085
1309
|
async setSessionConfigOption(params) {
|
|
1086
1310
|
await hydrateSessionsOnce();
|
|
1087
1311
|
if (!sessions.get(params.sessionId)) {
|
|
1088
1312
|
throw new RequestError(-32004, `Session not found: ${params.sessionId}`);
|
|
1089
1313
|
}
|
|
1090
1314
|
const runtimeState = ensureSessionRuntimeState(params.sessionId);
|
|
1315
|
+
if (typeof params.value !== 'string') {
|
|
1316
|
+
throw new RequestError(-32602, `Invalid params: unsupported non-string value for "${params.configId}"`);
|
|
1317
|
+
}
|
|
1091
1318
|
if (params.configId === ACP_PERMISSION_POLICY_CONFIG_ID) {
|
|
1092
1319
|
if (!isPermissionPolicyValue(params.value)) {
|
|
1093
1320
|
throw new RequestError(-32602, `Invalid params: unsupported value "${params.value}" for "${params.configId}"`);
|
|
@@ -1095,10 +1322,15 @@ export function createAcpFormalAgent(deps) {
|
|
|
1095
1322
|
runtimeState.permissionPolicy = params.value;
|
|
1096
1323
|
}
|
|
1097
1324
|
else if (params.configId === ACP_MODE_CONFIG_ID) {
|
|
1098
|
-
|
|
1325
|
+
const parsedModeId = parseAcpFlowMode(params.value);
|
|
1326
|
+
if (!parsedModeId || !ACP_PUBLIC_MODE_IDS.has(parsedModeId)) {
|
|
1099
1327
|
throw new RequestError(-32602, `Invalid params: unsupported value "${params.value}" for "${params.configId}"`);
|
|
1100
1328
|
}
|
|
1101
|
-
runtimeState.modeId =
|
|
1329
|
+
runtimeState.modeId = parsedModeId;
|
|
1330
|
+
const legacyPermissionPolicy = getLegacyPermissionPolicyForModeValue(params.value);
|
|
1331
|
+
if (legacyPermissionPolicy) {
|
|
1332
|
+
runtimeState.permissionPolicy = legacyPermissionPolicy;
|
|
1333
|
+
}
|
|
1102
1334
|
}
|
|
1103
1335
|
else {
|
|
1104
1336
|
throw new RequestError(-32602, `Invalid params: unsupported configId "${params.configId}"`);
|
|
@@ -1122,13 +1354,22 @@ export function createAcpFormalAgent(deps) {
|
|
|
1122
1354
|
throw new RequestError(-32004, `Session not found: ${params.sessionId}`);
|
|
1123
1355
|
}
|
|
1124
1356
|
const runtimeState = ensureSessionRuntimeState(params.sessionId);
|
|
1125
|
-
|
|
1357
|
+
const resolvedModeId = parseAcpFlowMode(params.modeId);
|
|
1358
|
+
if (!resolvedModeId || !ACP_PUBLIC_MODE_IDS.has(resolvedModeId)) {
|
|
1126
1359
|
throw new RequestError(-32602, `Invalid params: unsupported modeId "${params.modeId}"`);
|
|
1127
1360
|
}
|
|
1128
|
-
runtimeState.modeId =
|
|
1361
|
+
runtimeState.modeId = resolvedModeId;
|
|
1362
|
+
const legacyPermissionPolicy = getLegacyPermissionPolicyForModeValue(params.modeId);
|
|
1363
|
+
if (legacyPermissionPolicy) {
|
|
1364
|
+
runtimeState.permissionPolicy = legacyPermissionPolicy;
|
|
1365
|
+
}
|
|
1129
1366
|
sessions.update(params.sessionId, (current) => ({ ...current }));
|
|
1130
1367
|
await persistSessionsBestEffort();
|
|
1131
1368
|
await emitSessionInfoUpdateBestEffort(params.sessionId);
|
|
1369
|
+
const configUpdate = buildConfigOptionUpdateIfChanged(runtimeState);
|
|
1370
|
+
if (configUpdate) {
|
|
1371
|
+
await emitSessionUpdate(params.sessionId, configUpdate);
|
|
1372
|
+
}
|
|
1132
1373
|
// Send mode update notification
|
|
1133
1374
|
const modeUpdate = buildCurrentModeUpdateIfChanged(runtimeState);
|
|
1134
1375
|
if (modeUpdate) {
|
|
@@ -1209,7 +1450,7 @@ export function createAcpFormalAgent(deps) {
|
|
|
1209
1450
|
}
|
|
1210
1451
|
const pendingUpdates = [];
|
|
1211
1452
|
const executionRequest = buildCanonicalExecutionRequest({
|
|
1212
|
-
capability:
|
|
1453
|
+
capability: runtimeState.modeId,
|
|
1213
1454
|
instruction: promptText,
|
|
1214
1455
|
checkpointSessionId: params.sessionId,
|
|
1215
1456
|
repoPath: session.cwd,
|
|
@@ -1222,6 +1463,7 @@ export function createAcpFormalAgent(deps) {
|
|
|
1222
1463
|
fileSystemOverride: effectiveExecutionBinding === 'client'
|
|
1223
1464
|
? createAcpFileSystem({ conn: deps.conn, sessionId: params.sessionId })
|
|
1224
1465
|
: undefined,
|
|
1466
|
+
extensions: acpMcpServersToExtensions(session.mcpServers),
|
|
1225
1467
|
authorizationProvider: createAcpToolAuthorizationProvider({
|
|
1226
1468
|
conn: deps.conn,
|
|
1227
1469
|
sessionId: params.sessionId,
|
|
@@ -30,7 +30,11 @@ export function createAcpSessionStore() {
|
|
|
30
30
|
if (!current)
|
|
31
31
|
return undefined;
|
|
32
32
|
const updated = mutate(current);
|
|
33
|
-
|
|
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
|
},
|
|
@@ -104,7 +104,7 @@ export function createAcpToolAuthorizationProvider(params) {
|
|
|
104
104
|
const permissionPolicy = params.getPermissionPolicy?.() ?? 'ask';
|
|
105
105
|
if (permissionPolicy === 'allow_all') {
|
|
106
106
|
await emitInProgressBestEffort(request.id);
|
|
107
|
-
return { outcome: 'allow_session', source: 'auto', reason: '
|
|
107
|
+
return { outcome: 'allow_session', source: 'auto', reason: 'session_config:allow_all' };
|
|
108
108
|
}
|
|
109
109
|
const hasSideEffects = request.sideEffects.some((effect) => effect !== 'fs_read');
|
|
110
110
|
if (permissionPolicy === 'deny_all' && hasSideEffects) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { FLOW_MODE_PUBLIC_METADATA } from '../../public-capabilities/flow-mode-metadata.js';
|
|
2
|
+
import { FLOW_MODES, parseFlowMode } from '../../types/flow-mode.js';
|
|
3
|
+
export const SUPPORTED_PROTOCOL_FLOW_MODES = FLOW_MODES;
|
|
4
|
+
export function parseAcpFlowMode(value) {
|
|
5
|
+
const normalized = String(value ?? '')
|
|
6
|
+
.trim()
|
|
7
|
+
.toLowerCase();
|
|
8
|
+
if (normalized === 'interactive' || normalized === 'yolo') {
|
|
9
|
+
return 'autopilot';
|
|
10
|
+
}
|
|
11
|
+
return parseFlowMode(normalized);
|
|
12
|
+
}
|
|
13
|
+
export function parseA2ASkillFlowMode(value) {
|
|
14
|
+
return parseFlowMode(value);
|
|
15
|
+
}
|
|
16
|
+
export function buildA2AFlowSkills() {
|
|
17
|
+
return SUPPORTED_PROTOCOL_FLOW_MODES.map((mode) => ({
|
|
18
|
+
id: mode,
|
|
19
|
+
title: FLOW_MODE_PUBLIC_METADATA[mode].a2aTitle,
|
|
20
|
+
description: FLOW_MODE_PUBLIC_METADATA[mode].description,
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=flow-mode-mapping.js.map
|