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.
- package/dist/cli/argv/headless-detection.js +27 -0
- package/dist/cli/chat-flow.js +11 -0
- package/dist/cli/chat.js +161 -24
- package/dist/cli/commands/chat.js +30 -24
- package/dist/cli/commands/context.js +15 -3
- package/dist/cli/commands/flow-mode.js +63 -0
- package/dist/cli/commands/help-format.js +12 -0
- package/dist/cli/commands/registry.js +6 -7
- package/dist/cli/commands/run/benchmark-artifacts.js +41 -0
- package/dist/cli/commands/run/config-resolution.js +30 -24
- package/dist/cli/commands/run/early-errors.js +23 -0
- package/dist/cli/commands/run/handler.js +131 -44
- package/dist/cli/commands/run/headless-error-writer.js +8 -0
- package/dist/cli/commands/run/loop-params.js +3 -0
- package/dist/cli/commands/run/mode.js +2 -5
- package/dist/cli/commands/run/parse-options.js +18 -2
- 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/run/validate-options.js +0 -5
- package/dist/cli/commands/run/verbose.js +2 -7
- package/dist/cli/commands/serve.js +117 -90
- 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 +32 -6
- package/dist/cli/program-bootstrap.js +14 -4
- package/dist/cli/program-commands.js +9 -1
- package/dist/cli/program-options.js +1 -0
- 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 +30 -15
- 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/output-format.js +6 -0
- package/dist/cli/utils/resolve-cli-config.js +98 -0
- package/dist/cli/utils/verbose-level.js +8 -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/benchmark/patch-artifact.js +124 -0
- package/dist/core/benchmark/swe-bench.js +25 -0
- package/dist/core/config/load.js +39 -18
- package/dist/core/config/merge.js +27 -0
- package/dist/core/config/paths.js +24 -5
- package/dist/core/config/resolve-llm.js +12 -0
- package/dist/core/config/resolve.js +7 -5
- package/dist/core/config/resolvers/server.js +0 -6
- package/dist/core/config/validate.js +94 -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/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 +2 -1
- package/dist/core/facades/cli-command-tool-names.js +2 -0
- package/dist/core/facades/cli-context.js +1 -0
- package/dist/core/facades/cli-observability.js +1 -1
- 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 +2 -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 +173 -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 -3
- 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/intent/chat-intent.js +0 -4
- 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 +74 -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 +3 -2
- package/dist/core/protocols/a2a/sdk/executor.js +8 -6
- package/dist/core/protocols/a2a/sdk/server.js +0 -1
- package/dist/core/protocols/acp/formal-agent.js +221 -55
- package/dist/core/protocols/acp/handlers.js +5 -1
- package/dist/core/protocols/acp/permission-provider.js +21 -1
- package/dist/core/protocols/shared/execution-request.js +24 -0
- 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 +12 -4
- package/dist/core/session/manager.js +247 -10
- 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 +70 -13
- 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/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/interfaces/cli/task-runner.js +4 -3
- package/dist/languages/typescript/index.js +4 -1
- package/dist/locales/en.js +87 -2
- package/dist/utils/eol.js +1 -1
- package/package.json +15 -8
- 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
|
@@ -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:
|
|
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) ?? '
|
|
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
|
|
46
|
+
const executionRequest = buildCanonicalExecutionRequest({
|
|
45
47
|
capability,
|
|
46
|
-
|
|
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
|
|
220
|
-
|
|
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
|
|
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 ||
|
|
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: '
|
|
258
|
-
description:
|
|
280
|
+
name: 'Execution Flow',
|
|
281
|
+
description: 'Choose how the agent should execute this session.',
|
|
259
282
|
currentValue: state.modeId,
|
|
260
|
-
options:
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
|
298
|
-
|
|
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
|
-
:
|
|
319
|
-
const
|
|
320
|
-
|
|
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:
|
|
504
|
-
|
|
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 =
|
|
837
|
-
|
|
838
|
-
|
|
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:
|
|
928
|
+
protocolVersion: negotiatedVersion,
|
|
851
929
|
agentInfo: deps.agentInfo,
|
|
852
930
|
authMethods: [],
|
|
853
931
|
agentCapabilities: {
|
|
854
932
|
loadSession: loadSessionCapability,
|
|
855
|
-
promptCapabilities:
|
|
856
|
-
mcpCapabilities:
|
|
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({
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
1258
|
+
pendingUpdates.push(emitSessionUpdate(params.sessionId, update).catch(() => {
|
|
1259
|
+
// Ignore errors in session update notifications
|
|
1260
|
+
}));
|
|
1113
1261
|
}
|
|
1114
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|