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,5 +1,32 @@
|
|
|
1
1
|
import { createPhaseRoutingLlm, createRuntimeLlm, EXECUTION_PHASES, getLogger, Phase, } from '../../../core/facades/cli-run-runtime-llm.js';
|
|
2
2
|
import { text } from '../../locales/index.js';
|
|
3
|
+
function runtimeWarningMessage(code, params) {
|
|
4
|
+
if (code === 'API_KEY_MISSING')
|
|
5
|
+
return text.cli.apiKeyMissing;
|
|
6
|
+
if (code === 'PROVIDER_NOT_SUPPORTED') {
|
|
7
|
+
return text.cli.providerNotSupported(String(params.llmType));
|
|
8
|
+
}
|
|
9
|
+
if (code === 'CLIENT_PACKAGE_NOT_SUPPORTED') {
|
|
10
|
+
return text.cli.clientPackageNotSupported(String(params.clientPackage || ''));
|
|
11
|
+
}
|
|
12
|
+
return code;
|
|
13
|
+
}
|
|
14
|
+
function toHeadlessWarning(code, params) {
|
|
15
|
+
if (code === 'API_KEY_MISSING') {
|
|
16
|
+
return {
|
|
17
|
+
code: 'LLM_CREDENTIAL_MISSING',
|
|
18
|
+
message: 'LLM credential not configured; using StubLLM. Configure provider credentials to use a real LLM.',
|
|
19
|
+
source: 'llm.runtime',
|
|
20
|
+
severity: 'warning',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
code,
|
|
25
|
+
message: runtimeWarningMessage(code, params).replace(/^\[WARN\]\s*/, ''),
|
|
26
|
+
source: 'llm.runtime',
|
|
27
|
+
severity: 'warning',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
3
30
|
export function createRuntimeLlmAndWarn(params) {
|
|
4
31
|
const runtimeLlm = createRuntimeLlm(params.llmConfig, {
|
|
5
32
|
langfuseEnabled: params.langfuseEnabled,
|
|
@@ -8,7 +35,7 @@ export function createRuntimeLlmAndWarn(params) {
|
|
|
8
35
|
const phaseToProviderModel = params.llmConfig?.routing?.phaseToProviderModel;
|
|
9
36
|
const phaseLlms = {};
|
|
10
37
|
if (phaseToProviderModel && typeof phaseToProviderModel === 'object') {
|
|
11
|
-
const validPhases = new Set([...EXECUTION_PHASES, Phase.SLASH]);
|
|
38
|
+
const validPhases = new Set([...EXECUTION_PHASES, Phase.SLASH, Phase.AUTOPILOT]);
|
|
12
39
|
for (const [phase, target] of Object.entries(phaseToProviderModel)) {
|
|
13
40
|
if (!validPhases.has(phase))
|
|
14
41
|
continue;
|
|
@@ -29,6 +56,7 @@ export function createRuntimeLlmAndWarn(params) {
|
|
|
29
56
|
selectedModelId: target.model?.id,
|
|
30
57
|
selectedModelSlot: target.model?.slot || 'default',
|
|
31
58
|
},
|
|
59
|
+
capabilities: target.capabilities,
|
|
32
60
|
};
|
|
33
61
|
const created = createRuntimeLlm(perPhaseConfig, { langfuseEnabled: params.langfuseEnabled });
|
|
34
62
|
warnings.push(...created.warnings);
|
|
@@ -40,17 +68,16 @@ export function createRuntimeLlmAndWarn(params) {
|
|
|
40
68
|
: runtimeLlm.llm;
|
|
41
69
|
const llmType = params.llmConfig?.type;
|
|
42
70
|
const clientPackage = params.llmConfig?.clientPackage;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
else if (w === 'PROVIDER_NOT_SUPPORTED') {
|
|
48
|
-
getLogger().warn(text.cli.providerNotSupported(String(llmType)));
|
|
49
|
-
}
|
|
50
|
-
else if (w === 'CLIENT_PACKAGE_NOT_SUPPORTED') {
|
|
51
|
-
getLogger().warn(text.cli.clientPackageNotSupported(String(clientPackage || '')));
|
|
71
|
+
const uniqueWarnings = Array.from(new Set(warnings));
|
|
72
|
+
for (const w of uniqueWarnings) {
|
|
73
|
+
if (!params.headlessOutput) {
|
|
74
|
+
getLogger().warn(runtimeWarningMessage(w, { llmType, clientPackage }));
|
|
52
75
|
}
|
|
53
76
|
}
|
|
54
|
-
return {
|
|
77
|
+
return {
|
|
78
|
+
llm,
|
|
79
|
+
warnings: uniqueWarnings,
|
|
80
|
+
headlessWarnings: uniqueWarnings.map((w) => toHeadlessWarning(w, { llmType, clientPackage })),
|
|
81
|
+
};
|
|
55
82
|
}
|
|
56
83
|
//# sourceMappingURL=runtime-llm.js.map
|
|
@@ -23,8 +23,8 @@ export async function resolveRunRuntimeOptions(params) {
|
|
|
23
23
|
if (wantPartialMessages && !llmOutput.kinds.includes('plan')) {
|
|
24
24
|
llmOutput.kinds.push('plan');
|
|
25
25
|
}
|
|
26
|
-
const effectiveVerify = await resolveVerifyOption(params.repoPath, params.cliOptions.verify, params.resolvedConfig.verify.command);
|
|
27
|
-
const effectiveWorktreePrepare = await resolveWorktreePrepareOption(params.repoPath, params.cliOptions.checkpointStrategy, params.cliOptions.worktreePrepare);
|
|
26
|
+
const effectiveVerify = await resolveVerifyOption(params.repoPath, params.cliOptions.verify, params.resolvedConfig.verify.command, { quiet: params.headlessOutput });
|
|
27
|
+
const effectiveWorktreePrepare = await resolveWorktreePrepareOption(params.repoPath, params.cliOptions.checkpointStrategy, params.cliOptions.worktreePrepare, { quiet: params.headlessOutput });
|
|
28
28
|
return { ok: true, llmOutput, effectiveVerify, effectiveWorktreePrepare };
|
|
29
29
|
}
|
|
30
30
|
//# sourceMappingURL=runtime-options.js.map
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
export function resolveOutputFormat(raw) {
|
|
2
|
-
if (raw === 'text' || raw === 'stream-json' || raw === 'json')
|
|
3
|
-
return raw;
|
|
4
|
-
return undefined;
|
|
5
|
-
}
|
|
6
1
|
export function validateRunCommandOptions(params) {
|
|
7
2
|
const { parsed } = params;
|
|
8
3
|
if (parsed.explicitInstruction && parsed.printInstruction) {
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { getLogger } from '../../../core/facades/cli-observability.js';
|
|
2
2
|
import { text } from '../../locales/index.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
return 'basic';
|
|
6
|
-
if (typeof raw === 'string')
|
|
7
|
-
return raw;
|
|
8
|
-
return undefined;
|
|
9
|
-
}
|
|
3
|
+
import { resolveVerboseLevel } from '../../utils/verbose-level.js';
|
|
4
|
+
export { resolveVerboseLevel };
|
|
10
5
|
export function logRunVerboseSummary(params) {
|
|
11
6
|
if (!params.verboseLevel)
|
|
12
7
|
return;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizePermissionMode } from '../../core/config/index.js';
|
|
2
|
+
import { buildA2AAgentCard, createAcpFormalAgent, createAgentServerRuntime, createInteractionFacade, createSalmonTaskExecutor, createTaskEventBus, createPluginRegistry, createPromptRegistry, getUserAcpSessionStorePath, GitSnapshotCheckpointService, getLogger, PlainReporter, PluginLoader, resolveExtensions, resolveExecutionProfile, runSalmonLoop, setPluginRegistry, setPromptRegistry, startAcpStdioServer, StderrReporter, } from '../../core/facades/cli-serve.js';
|
|
3
|
+
import { selectPublicCapabilitiesForSurface, toA2APublicSkills, } from '../../core/public-capabilities/projections.js';
|
|
4
|
+
import { buildPublicCapabilityRegistry } from '../../core/public-capabilities/registry.js';
|
|
2
5
|
import { createTerminalAuthorizationProvider } from '../authorization/provider.js';
|
|
3
6
|
import { text } from '../locales/index.js';
|
|
4
|
-
import {
|
|
7
|
+
import { getOptionValueSourceWithGlobalFallback } from '../utils/command-option-source.js';
|
|
5
8
|
import { createOutcomeReporter } from '../utils/outcome-reporter.js';
|
|
9
|
+
import { resolveCliConfig } from '../utils/resolve-cli-config.js';
|
|
6
10
|
import { createRuntimeLlmAndWarn } from './run/runtime-llm.js';
|
|
7
11
|
function parsePort(value, fallback) {
|
|
8
12
|
if (value === undefined || value === null || value === '')
|
|
@@ -10,21 +14,44 @@ function parsePort(value, fallback) {
|
|
|
10
14
|
const parsed = Number(value);
|
|
11
15
|
return Number.isFinite(parsed) ? parsed : Number.NaN;
|
|
12
16
|
}
|
|
13
|
-
function
|
|
17
|
+
function resolveDefaultAcpPermissionPolicy(permissionMode) {
|
|
18
|
+
return permissionMode === 'yolo' ? 'allow_all' : 'ask';
|
|
19
|
+
}
|
|
20
|
+
function resolveServePermissionMode({ command, allOptions, rawConfiguredPermissionMode, flowMode, }) {
|
|
21
|
+
const permissionModeOptionSource = getOptionValueSourceWithGlobalFallback(command, 'mode');
|
|
22
|
+
const configuredPermissionMode = normalizePermissionMode(rawConfiguredPermissionMode);
|
|
23
|
+
const rawPermissionMode = (permissionModeOptionSource === 'cli' ? allOptions.mode : undefined) ??
|
|
24
|
+
configuredPermissionMode ??
|
|
25
|
+
resolveExecutionProfile(flowMode).defaultPermissionMode ??
|
|
26
|
+
'interactive';
|
|
27
|
+
const permissionMode = normalizePermissionMode(rawPermissionMode);
|
|
28
|
+
if (!permissionMode) {
|
|
29
|
+
getLogger().error(`Invalid --mode "${String(rawPermissionMode)}". Expected "interactive" or "yolo".`, true);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
return permissionMode;
|
|
33
|
+
}
|
|
34
|
+
function buildServeLoopExecutionDefaults(mode) {
|
|
35
|
+
const profile = resolveExecutionProfile(mode);
|
|
36
|
+
const strategy = profile.defaultCheckpointStrategy ?? 'worktree';
|
|
14
37
|
return {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
name: deps.name,
|
|
19
|
-
version: deps.version,
|
|
20
|
-
capabilities: deps.capabilities,
|
|
21
|
-
}),
|
|
22
|
-
abort: async () => new Response('Abort not implemented for this runtime', { status: 501 }),
|
|
23
|
-
workspace_files: async () => new Response('Workspace file access not enabled', { status: 501 }),
|
|
24
|
-
logs_stream: async () => new Response('Log streaming not enabled', { status: 501 }),
|
|
25
|
-
config_patch: async () => new Response('Config patch not enabled', { status: 501 }),
|
|
38
|
+
strategy,
|
|
39
|
+
applyBackOnDirty: strategy === 'worktree' ? '3way' : undefined,
|
|
40
|
+
environmentMode: 'strict',
|
|
26
41
|
};
|
|
27
42
|
}
|
|
43
|
+
function registerServeShutdown({ message, closeRuntime, closeAcpStdio, }) {
|
|
44
|
+
process.once('SIGINT', async () => {
|
|
45
|
+
getLogger().info(message);
|
|
46
|
+
try {
|
|
47
|
+
await closeRuntime?.();
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
closeAcpStdio?.();
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
28
55
|
export function registerServeCommands(program) {
|
|
29
56
|
const serve = program
|
|
30
57
|
.command('serve')
|
|
@@ -33,8 +60,6 @@ export function registerServeCommands(program) {
|
|
|
33
60
|
.option('--a2a-port <port>', text.cli.a2aPortOption)
|
|
34
61
|
.option('--a2a-token <token>', text.cli.a2aTokenOption, (value, previous) => previous.concat([value]), [])
|
|
35
62
|
.option('--no-acp-stdio', text.cli.acpStdioDisableOption)
|
|
36
|
-
.option('--sidecar-socket <path>', text.cli.sidecarSocketOption)
|
|
37
|
-
.option('--sidecar-allow-conditional', text.cli.sidecarAllowConditionalOption)
|
|
38
63
|
.option('--no-color', text.cli.noColorOption)
|
|
39
64
|
.action(handleServeCommand);
|
|
40
65
|
serve
|
|
@@ -45,18 +70,27 @@ export function registerServeCommands(program) {
|
|
|
45
70
|
}
|
|
46
71
|
export async function handleServeCommand(_options, command) {
|
|
47
72
|
const allOptions = command.optsWithGlobals();
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
73
|
+
const configResult = await resolveCliConfig({
|
|
74
|
+
repo: allOptions.repo,
|
|
75
|
+
cwd: process.cwd(),
|
|
76
|
+
configPath: allOptions.config,
|
|
77
|
+
enableConfigFile: allOptions.configFile !== false,
|
|
78
|
+
auditScope: allOptions.auditScope,
|
|
79
|
+
logMode: allOptions.logMode,
|
|
54
80
|
});
|
|
55
|
-
if (!
|
|
56
|
-
getLogger().error(
|
|
81
|
+
if (!configResult.ok) {
|
|
82
|
+
getLogger().error(configResult.message, true);
|
|
57
83
|
process.exit(1);
|
|
58
84
|
}
|
|
59
|
-
const auditScope =
|
|
85
|
+
const { resolvedConfig, auditScope, repoPath: defaultRepoPath } = configResult;
|
|
86
|
+
const defaultFlowMode = 'autopilot';
|
|
87
|
+
const defaultPermissionMode = resolveServePermissionMode({
|
|
88
|
+
command,
|
|
89
|
+
allOptions,
|
|
90
|
+
rawConfiguredPermissionMode: resolvedConfig.raw?.mode,
|
|
91
|
+
flowMode: defaultFlowMode,
|
|
92
|
+
});
|
|
93
|
+
const serverConfig = resolvedConfig.server;
|
|
60
94
|
const rawA2aHost = allOptions.a2aHost ?? serverConfig?.a2a?.host;
|
|
61
95
|
const a2aHost = String(rawA2aHost ?? '127.0.0.1');
|
|
62
96
|
const rawA2aPort = allOptions.a2aPort ?? serverConfig?.a2a?.port;
|
|
@@ -69,13 +103,6 @@ export async function handleServeCommand(_options, command) {
|
|
|
69
103
|
if (acpStdioEnabled) {
|
|
70
104
|
getLogger().setReporter(allOptions.color === false ? new StderrReporter() : new PlainReporter());
|
|
71
105
|
}
|
|
72
|
-
const sidecarListen = typeof allOptions.sidecarSocket === 'string' && allOptions.sidecarSocket.length > 0
|
|
73
|
-
? { type: 'pipe', path: allOptions.sidecarSocket }
|
|
74
|
-
: getSidecarListenOptions();
|
|
75
|
-
const allowConditional = allOptions.sidecarAllowConditional ?? serverConfig?.sidecar?.allowConditional ?? false;
|
|
76
|
-
if (sidecarListen.type === 'pipe' && !sidecarListen.path.startsWith('\\\\.\\pipe\\')) {
|
|
77
|
-
await mkdir(defaultPathAdapter.dirname(sidecarListen.path), { recursive: true });
|
|
78
|
-
}
|
|
79
106
|
const languagePlugins = createPluginRegistry();
|
|
80
107
|
setPluginRegistry(languagePlugins);
|
|
81
108
|
setPromptRegistry(createPromptRegistry());
|
|
@@ -99,20 +126,27 @@ export async function handleServeCommand(_options, command) {
|
|
|
99
126
|
const executor = createSalmonTaskExecutor({
|
|
100
127
|
runLoop: async ({ instruction, mode, repoPath, onEvent, signal, authorizationProvider, authorizationMode, fileSystemOverride, }) => {
|
|
101
128
|
const effectiveRepoPath = repoPath ?? defaultRepoPath;
|
|
129
|
+
const flowMode = mode;
|
|
130
|
+
const executionDefaults = buildServeLoopExecutionDefaults(flowMode);
|
|
131
|
+
const permissionMode = resolveServePermissionMode({
|
|
132
|
+
command,
|
|
133
|
+
allOptions,
|
|
134
|
+
rawConfiguredPermissionMode: resolvedConfig.raw?.mode,
|
|
135
|
+
flowMode,
|
|
136
|
+
});
|
|
102
137
|
return await runSalmonLoop({
|
|
103
138
|
instruction,
|
|
104
139
|
repoPath: effectiveRepoPath,
|
|
105
140
|
llm,
|
|
106
|
-
mode:
|
|
141
|
+
mode: flowMode,
|
|
107
142
|
verify: resolvedConfig.verify.command,
|
|
108
|
-
|
|
109
|
-
applyBackOnDirty: '3way',
|
|
110
|
-
environmentMode: 'strict',
|
|
143
|
+
...executionDefaults,
|
|
111
144
|
llmOutput: resolvedConfig.llmOutput,
|
|
112
145
|
outcomeReporter,
|
|
113
146
|
langfuseSessionId: resolvedConfig.observability.langfuse.sessionId,
|
|
114
147
|
langfuseUserId: resolvedConfig.observability.langfuse.userId,
|
|
115
148
|
auditScope,
|
|
149
|
+
permissionMode,
|
|
116
150
|
languagePlugins,
|
|
117
151
|
fileSystemOverride,
|
|
118
152
|
authorizationProvider: authorizationProvider ?? defaultAuthorizationProvider,
|
|
@@ -154,27 +188,20 @@ export async function handleServeCommand(_options, command) {
|
|
|
154
188
|
next();
|
|
155
189
|
}
|
|
156
190
|
: undefined;
|
|
157
|
-
const
|
|
191
|
+
const a2aPublicCapabilities = selectPublicCapabilitiesForSurface('a2a', buildPublicCapabilityRegistry());
|
|
192
|
+
const a2aSkills = toA2APublicSkills(a2aPublicCapabilities);
|
|
158
193
|
const agentCard = buildA2AAgentCard({
|
|
159
194
|
name: 'salmon-loop',
|
|
160
195
|
url: `http://${a2aHost}:${a2aPort}`,
|
|
161
|
-
capabilities,
|
|
196
|
+
capabilities: a2aSkills,
|
|
162
197
|
security: authTokens.length > 0 ? [{ type: 'http', scheme: 'bearer' }] : [],
|
|
163
198
|
});
|
|
164
|
-
const sidecarRoutes = buildSidecarRouteDescriptors({
|
|
165
|
-
strict: true,
|
|
166
|
-
catalog: defaultSidecarRouteCatalog,
|
|
167
|
-
handlers: buildSidecarHandlers({
|
|
168
|
-
name: 'salmon-loop',
|
|
169
|
-
version: '0.2.0',
|
|
170
|
-
capabilities,
|
|
171
|
-
}),
|
|
172
|
-
});
|
|
173
199
|
if (acpStdioEnabled) {
|
|
174
200
|
startAcpStdioServer((conn) => createAcpFormalAgent({
|
|
175
201
|
conn,
|
|
176
202
|
agentInfo: { name: 'salmon-loop', version: '0.2.0' },
|
|
177
|
-
defaultModeId:
|
|
203
|
+
defaultModeId: 'autopilot',
|
|
204
|
+
defaultPermissionPolicy: resolveDefaultAcpPermissionPolicy(defaultPermissionMode),
|
|
178
205
|
checkpointReader: {
|
|
179
206
|
listBySession: async ({ repoPath, sessionId, limit }) => await checkpointService.list({ repoPath, sessionId, limit }),
|
|
180
207
|
getById: async ({ repoPath, checkpointId }) => (await checkpointService.loadWithStatus({ repoPath, checkpointId })).handle,
|
|
@@ -189,47 +216,48 @@ export async function handleServeCommand(_options, command) {
|
|
|
189
216
|
eventBus: sharedEventBus,
|
|
190
217
|
}));
|
|
191
218
|
getLogger().info(text.cli.acpStdioStarted('n/a (stdio)'));
|
|
192
|
-
// Handle SIGINT for graceful shutdown
|
|
193
|
-
process.on('SIGINT', () => {
|
|
194
|
-
getLogger().info('Received SIGINT, shutting down ACP server...');
|
|
195
|
-
process.stdin.destroy();
|
|
196
|
-
process.exit(0);
|
|
197
|
-
});
|
|
198
219
|
}
|
|
199
|
-
const fastify = (await import('fastify')).default;
|
|
200
220
|
const runtime = createAgentServerRuntime({
|
|
201
|
-
createFastify: () => fastify(),
|
|
202
221
|
a2a: {
|
|
203
222
|
buildAgentCard: () => agentCard,
|
|
204
223
|
executeTask: executor.execute,
|
|
205
224
|
eventBus: sharedEventBus,
|
|
206
225
|
authMiddleware,
|
|
207
226
|
},
|
|
208
|
-
sidecar: {
|
|
209
|
-
routes: sidecarRoutes,
|
|
210
|
-
allowConditional,
|
|
211
|
-
},
|
|
212
227
|
listen: {
|
|
213
228
|
a2a: { host: a2aHost, port: a2aPort },
|
|
214
|
-
sidecar: sidecarListen,
|
|
215
229
|
},
|
|
216
|
-
a2aBaseUrl: `http://${a2aHost}:${a2aPort}`,
|
|
217
230
|
});
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
231
|
+
registerServeShutdown({
|
|
232
|
+
message: 'Received SIGINT, shutting down server...',
|
|
233
|
+
closeRuntime: () => runtime.close(),
|
|
234
|
+
closeAcpStdio: acpStdioEnabled ? () => void process.stdin.destroy() : undefined,
|
|
222
235
|
});
|
|
223
236
|
await runtime.start();
|
|
224
|
-
|
|
225
|
-
? `http://${sidecarListen.host}:${sidecarListen.port}`
|
|
226
|
-
: sidecarListen.path;
|
|
227
|
-
getLogger().success(text.cli.serveStarted(a2aHost, a2aPort, sidecarAddress));
|
|
237
|
+
getLogger().success(text.cli.serveStarted(a2aHost, a2aPort));
|
|
228
238
|
}
|
|
229
239
|
export async function handleServeAcpCommand(_options, command) {
|
|
230
240
|
const allOptions = command.optsWithGlobals();
|
|
231
|
-
const
|
|
232
|
-
|
|
241
|
+
const configResult = await resolveCliConfig({
|
|
242
|
+
repo: allOptions.repo,
|
|
243
|
+
cwd: process.cwd(),
|
|
244
|
+
configPath: allOptions.config,
|
|
245
|
+
enableConfigFile: allOptions.configFile !== false,
|
|
246
|
+
auditScope: allOptions.auditScope,
|
|
247
|
+
logMode: allOptions.logMode,
|
|
248
|
+
});
|
|
249
|
+
if (!configResult.ok) {
|
|
250
|
+
getLogger().error(configResult.message, true);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
const { resolvedConfig, auditScope, repoPath: defaultRepoPath } = configResult;
|
|
254
|
+
const defaultFlowMode = 'autopilot';
|
|
255
|
+
const defaultPermissionMode = resolveServePermissionMode({
|
|
256
|
+
command,
|
|
257
|
+
allOptions,
|
|
258
|
+
rawConfiguredPermissionMode: resolvedConfig.raw?.mode,
|
|
259
|
+
flowMode: defaultFlowMode,
|
|
260
|
+
});
|
|
233
261
|
getLogger().setReporter(allOptions.color === false ? new StderrReporter() : new PlainReporter());
|
|
234
262
|
const languagePlugins = createPluginRegistry();
|
|
235
263
|
setPluginRegistry(languagePlugins);
|
|
@@ -240,14 +268,6 @@ export async function handleServeAcpCommand(_options, command) {
|
|
|
240
268
|
llmConfig: resolvedConfig.llm,
|
|
241
269
|
langfuseEnabled: resolvedConfig.observability.langfuse.enabled,
|
|
242
270
|
});
|
|
243
|
-
const auditScopeResolution = resolveAuditScope({
|
|
244
|
-
cliValue: allOptions.auditScope,
|
|
245
|
-
configValue: resolvedConfig.observability.audit.scope,
|
|
246
|
-
});
|
|
247
|
-
if (!auditScopeResolution.ok) {
|
|
248
|
-
getLogger().error(text.cli.invalidAuditScope(auditScopeResolution.invalid), true);
|
|
249
|
-
process.exit(1);
|
|
250
|
-
}
|
|
251
271
|
const outcomeReporter = createOutcomeReporter({
|
|
252
272
|
enabled: resolvedConfig.observability.langfuse.outcome,
|
|
253
273
|
endpoint: resolvedConfig.observability.langfuse.endpoint,
|
|
@@ -262,20 +282,27 @@ export async function handleServeAcpCommand(_options, command) {
|
|
|
262
282
|
const executor = createSalmonTaskExecutor({
|
|
263
283
|
runLoop: async ({ instruction, mode, repoPath, onEvent, signal, authorizationProvider, authorizationMode, }) => {
|
|
264
284
|
const effectiveRepoPath = repoPath ?? defaultRepoPath;
|
|
285
|
+
const flowMode = mode;
|
|
286
|
+
const executionDefaults = buildServeLoopExecutionDefaults(flowMode);
|
|
287
|
+
const permissionMode = resolveServePermissionMode({
|
|
288
|
+
command,
|
|
289
|
+
allOptions,
|
|
290
|
+
rawConfiguredPermissionMode: resolvedConfig.raw?.mode,
|
|
291
|
+
flowMode,
|
|
292
|
+
});
|
|
265
293
|
return await runSalmonLoop({
|
|
266
294
|
instruction,
|
|
267
295
|
repoPath: effectiveRepoPath,
|
|
268
296
|
llm,
|
|
269
|
-
mode:
|
|
297
|
+
mode: flowMode,
|
|
270
298
|
verify: resolvedConfig.verify.command,
|
|
271
|
-
|
|
272
|
-
applyBackOnDirty: '3way',
|
|
273
|
-
environmentMode: 'strict',
|
|
299
|
+
...executionDefaults,
|
|
274
300
|
llmOutput: resolvedConfig.llmOutput,
|
|
275
301
|
outcomeReporter,
|
|
276
302
|
langfuseSessionId: resolvedConfig.observability.langfuse.sessionId,
|
|
277
303
|
langfuseUserId: resolvedConfig.observability.langfuse.userId,
|
|
278
|
-
auditScope
|
|
304
|
+
auditScope,
|
|
305
|
+
permissionMode,
|
|
279
306
|
languagePlugins,
|
|
280
307
|
authorizationProvider: authorizationProvider ?? defaultAuthorizationProvider,
|
|
281
308
|
authorizationMode,
|
|
@@ -299,7 +326,8 @@ export async function handleServeAcpCommand(_options, command) {
|
|
|
299
326
|
startAcpStdioServer((conn) => createAcpFormalAgent({
|
|
300
327
|
conn,
|
|
301
328
|
agentInfo: { name: 'salmon-loop', version: '0.2.0' },
|
|
302
|
-
defaultModeId:
|
|
329
|
+
defaultModeId: 'autopilot',
|
|
330
|
+
defaultPermissionPolicy: resolveDefaultAcpPermissionPolicy(defaultPermissionMode),
|
|
303
331
|
checkpointReader: {
|
|
304
332
|
listBySession: async ({ repoPath, sessionId, limit }) => await checkpointService.list({ repoPath, sessionId, limit }),
|
|
305
333
|
getById: async ({ repoPath, checkpointId }) => (await checkpointService.loadWithStatus({ repoPath, checkpointId })).handle,
|
|
@@ -314,10 +342,9 @@ export async function handleServeAcpCommand(_options, command) {
|
|
|
314
342
|
eventBus: sharedEventBus,
|
|
315
343
|
}));
|
|
316
344
|
getLogger().info(text.cli.acpStdioStarted('n/a (stdio)'));
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
process.stdin.destroy()
|
|
320
|
-
process.exit(0);
|
|
345
|
+
registerServeShutdown({
|
|
346
|
+
message: 'Received SIGINT, shutting down ACP server...',
|
|
347
|
+
closeAcpStdio: () => void process.stdin.destroy(),
|
|
321
348
|
});
|
|
322
349
|
}
|
|
323
350
|
//# sourceMappingURL=serve.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as os from 'os';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import { getLogger, registerAllBuiltins, skillToToolSpec,
|
|
4
|
-
import {
|
|
3
|
+
import { getLogger, registerAllBuiltins, resolveExtensions, skillToToolSpec, SkillLoader, ToolRegistry, } from '../../core/facades/cli-command-tool-names.js';
|
|
4
|
+
import { stat, statSync } from '../utils/safe-fs.js';
|
|
5
5
|
const VALID_SIDE_EFFECTS = new Set([
|
|
6
6
|
'none',
|
|
7
7
|
'fs_read',
|
|
@@ -11,40 +11,23 @@ const VALID_SIDE_EFFECTS = new Set([
|
|
|
11
11
|
'git_read',
|
|
12
12
|
'git_write',
|
|
13
13
|
]);
|
|
14
|
+
// Cache key includes repoRoot. Extensions config is not part of the key because
|
|
15
|
+
// tool-names is used for tab completion where extensions may not be available.
|
|
14
16
|
const toolNameCache = new Map();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (!skillFile || !existsSync(skillFile, root))
|
|
27
|
-
continue;
|
|
28
|
-
try {
|
|
29
|
-
const content = await readFileUtf8(skillFile, root);
|
|
30
|
-
skills.push(SkillParser.parse(content, skillFile));
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
getLogger().warn(`Failed to load skill at ${skillFile}: ${error instanceof Error ? error.message : String(error)}`);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return skills;
|
|
37
|
-
}
|
|
38
|
-
function getSkillSearchPaths(repoRoot) {
|
|
39
|
-
return [
|
|
40
|
-
path.join(os.homedir(), '.claude/skills'),
|
|
41
|
-
path.join(repoRoot, '.salmonloop/skills'),
|
|
42
|
-
path.join(repoRoot, '.claude/skills'),
|
|
17
|
+
/**
|
|
18
|
+
* Compute a cache-invalidation signature based on mtime of all skill search
|
|
19
|
+
* paths that SkillLoader would scan. Mirrors the strict search order.
|
|
20
|
+
*/
|
|
21
|
+
async function computeSkillSignature(repoRoot, extraPaths = []) {
|
|
22
|
+
const searchPaths = [
|
|
23
|
+
...extraPaths,
|
|
24
|
+
path.join(repoRoot, '.salmonloop', 'skills'),
|
|
25
|
+
path.join(repoRoot, '.agents', 'skills'),
|
|
26
|
+
path.join(os.homedir(), '.salmonloop', 'skills'),
|
|
27
|
+
path.join(os.homedir(), '.agents', 'skills'),
|
|
43
28
|
];
|
|
44
|
-
}
|
|
45
|
-
async function computeSkillSignature(repoRoot) {
|
|
46
29
|
const parts = [];
|
|
47
|
-
for (const searchPath of
|
|
30
|
+
for (const searchPath of searchPaths) {
|
|
48
31
|
try {
|
|
49
32
|
const stats = await stat(searchPath);
|
|
50
33
|
parts.push(`${searchPath}:${stats.mtimeMs}`);
|
|
@@ -56,8 +39,14 @@ async function computeSkillSignature(repoRoot) {
|
|
|
56
39
|
return parts.join('|');
|
|
57
40
|
}
|
|
58
41
|
function computeSkillSignatureSync(repoRoot) {
|
|
42
|
+
const searchPaths = [
|
|
43
|
+
path.join(repoRoot, '.salmonloop', 'skills'),
|
|
44
|
+
path.join(repoRoot, '.agents', 'skills'),
|
|
45
|
+
path.join(os.homedir(), '.salmonloop', 'skills'),
|
|
46
|
+
path.join(os.homedir(), '.agents', 'skills'),
|
|
47
|
+
];
|
|
59
48
|
const parts = [];
|
|
60
|
-
for (const searchPath of
|
|
49
|
+
for (const searchPath of searchPaths) {
|
|
61
50
|
try {
|
|
62
51
|
const stats = statSync(searchPath);
|
|
63
52
|
parts.push(`${searchPath}:${stats.mtimeMs}`);
|
|
@@ -68,52 +57,63 @@ function computeSkillSignatureSync(repoRoot) {
|
|
|
68
57
|
}
|
|
69
58
|
return parts.join('|');
|
|
70
59
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
? safePathJoin(root, entry.name, 'SKILL.md')
|
|
79
|
-
: entry.name.endsWith('.md')
|
|
80
|
-
? safePathJoin(root, entry.name)
|
|
81
|
-
: null;
|
|
82
|
-
if (!skillFile || !existsSync(skillFile, root))
|
|
83
|
-
continue;
|
|
84
|
-
try {
|
|
85
|
-
const content = readFileUtf8Sync(skillFile, root);
|
|
86
|
-
skills.push(SkillParser.parse(content, skillFile));
|
|
87
|
-
}
|
|
88
|
-
catch (error) {
|
|
89
|
-
getLogger().warn(`Failed to load skill at ${skillFile}: ${error instanceof Error ? error.message : String(error)}`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return skills;
|
|
93
|
-
}
|
|
60
|
+
/**
|
|
61
|
+
* Get all known tool names including skills.
|
|
62
|
+
*
|
|
63
|
+
* Uses `resolveExtensions` to obtain the same SkillLoader configuration
|
|
64
|
+
* (extraPaths) as the main runtime, so that
|
|
65
|
+
* tool-name discovery and actual runtime skill availability stay consistent.
|
|
66
|
+
*/
|
|
94
67
|
export async function getKnownToolNames(repoRoot) {
|
|
95
|
-
|
|
68
|
+
// Resolve extensions to get the same loader config as the runtime
|
|
69
|
+
let skillDiscovery = {};
|
|
70
|
+
try {
|
|
71
|
+
const { resolved } = await resolveExtensions({ repoRoot });
|
|
72
|
+
skillDiscovery = resolved.skillDiscovery;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Extensions config unavailable — fall back to loader defaults
|
|
76
|
+
}
|
|
77
|
+
const extraPaths = skillDiscovery.paths ?? [];
|
|
78
|
+
const signature = await computeSkillSignature(repoRoot, extraPaths);
|
|
96
79
|
const cached = toolNameCache.get(repoRoot);
|
|
97
80
|
if (cached && cached.signature === signature)
|
|
98
81
|
return cached.names;
|
|
99
82
|
const registry = new ToolRegistry();
|
|
100
83
|
registerAllBuiltins(registry);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
84
|
+
const routerBox = { router: null };
|
|
85
|
+
const skillLoader = new SkillLoader({
|
|
86
|
+
repoRoot,
|
|
87
|
+
extraPaths,
|
|
88
|
+
});
|
|
89
|
+
const catalog = await skillLoader.loadCatalog();
|
|
90
|
+
for (const entry of catalog) {
|
|
91
|
+
try {
|
|
92
|
+
// Name-only registration: executor is never called, so null router is safe.
|
|
93
|
+
// Use catalog entry for lightweight registration (progressive disclosure).
|
|
94
|
+
registry.register(skillToToolSpec({ entry, loader: skillLoader }, routerBox));
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
const label = entry.name || entry.id;
|
|
98
|
+
getLogger().warn(`Failed to register skill ${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
111
99
|
}
|
|
112
100
|
}
|
|
113
101
|
const names = new Set(registry.listAll().map((spec) => spec.name));
|
|
114
102
|
toolNameCache.set(repoRoot, { names, signature });
|
|
115
103
|
return names;
|
|
116
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Synchronous variant for tab completion only.
|
|
107
|
+
*
|
|
108
|
+
* @nonAuthoritative This returns a best-effort approximation of known tool
|
|
109
|
+
* names using loader defaults (no extensions resolution, since
|
|
110
|
+
* resolveExtensions is async). The result may differ from the actual runtime
|
|
111
|
+
* tool set when custom `skills.json` discovery paths are configured.
|
|
112
|
+
*
|
|
113
|
+
* DO NOT use this for security decisions (allowlist enforcement, permission
|
|
114
|
+
* checks). Use the async {@link getKnownToolNames} instead, which resolves
|
|
115
|
+
* extensions for full consistency with the runtime.
|
|
116
|
+
*/
|
|
117
117
|
export function getKnownToolNamesSync(repoRoot) {
|
|
118
118
|
const signature = computeSkillSignatureSync(repoRoot);
|
|
119
119
|
const cached = toolNameCache.get(repoRoot);
|
|
@@ -121,16 +121,16 @@ export function getKnownToolNamesSync(repoRoot) {
|
|
|
121
121
|
return cached.names;
|
|
122
122
|
const registry = new ToolRegistry();
|
|
123
123
|
registerAllBuiltins(registry);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
124
|
+
const routerBox = { router: null };
|
|
125
|
+
const skillLoader = new SkillLoader({ repoRoot });
|
|
126
|
+
const skills = skillLoader.initializeSync();
|
|
127
|
+
for (const skill of skills) {
|
|
128
|
+
try {
|
|
129
|
+
registry.register(skillToToolSpec(skill, routerBox));
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const label = skill.metadata?.name || skill.id;
|
|
133
|
+
getLogger().warn(`Failed to register skill ${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
const names = new Set(registry.listAll().map((spec) => spec.name));
|