@the-open-engine/zeroshot 5.4.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/CHANGELOG.md +539 -0
- package/LICENSE +21 -0
- package/README.md +508 -0
- package/cli/commands/inspect-render.js +135 -0
- package/cli/commands/inspect.js +294 -0
- package/cli/commands/providers.js +149 -0
- package/cli/index.js +5431 -0
- package/cli/lib/first-run.js +211 -0
- package/cli/lib/update-checker.js +281 -0
- package/cli/message-formatter-utils.js +75 -0
- package/cli/message-formatters-normal.js +275 -0
- package/cli/message-formatters-watch.js +185 -0
- package/cluster-templates/base-templates/debug-workflow.json +422 -0
- package/cluster-templates/base-templates/full-workflow.json +727 -0
- package/cluster-templates/base-templates/heavy-validation.json +272 -0
- package/cluster-templates/base-templates/quick-validation.json +285 -0
- package/cluster-templates/base-templates/single-worker.json +71 -0
- package/cluster-templates/base-templates/worker-validator.json +230 -0
- package/cluster-templates/conductor-bootstrap.json +130 -0
- package/docker/zeroshot-cluster/Dockerfile +153 -0
- package/docker/zeroshot-cluster/pre-baked-deps.json +28 -0
- package/lib/agent-cli-provider/adapters/claude-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/claude-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-parser.js +122 -0
- package/lib/agent-cli-provider/adapters/claude-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.d.ts +15 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.js +165 -0
- package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.js +181 -0
- package/lib/agent-cli-provider/adapters/claude.js.map +1 -0
- package/lib/agent-cli-provider/adapters/codex-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/codex-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/codex-parser.js +192 -0
- package/lib/agent-cli-provider/adapters/codex-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/codex.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/codex.js +151 -0
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -0
- package/lib/agent-cli-provider/adapters/common.d.ts +28 -0
- package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/common.js +71 -0
- package/lib/agent-cli-provider/adapters/common.js.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.js +176 -0
- package/lib/agent-cli-provider/adapters/gemini.js.map +1 -0
- package/lib/agent-cli-provider/adapters/index.d.ts +9 -0
- package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/index.js +95 -0
- package/lib/agent-cli-provider/adapters/index.js.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.js +228 -0
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -0
- package/lib/agent-cli-provider/contract-actions.d.ts +5 -0
- package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-actions.js +140 -0
- package/lib/agent-cli-provider/contract-actions.js.map +1 -0
- package/lib/agent-cli-provider/contract-env.d.ts +11 -0
- package/lib/agent-cli-provider/contract-env.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-env.js +113 -0
- package/lib/agent-cli-provider/contract-env.js.map +1 -0
- package/lib/agent-cli-provider/contract-envelope.d.ts +49 -0
- package/lib/agent-cli-provider/contract-envelope.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-envelope.js +52 -0
- package/lib/agent-cli-provider/contract-envelope.js.map +1 -0
- package/lib/agent-cli-provider/contract-errors.d.ts +22 -0
- package/lib/agent-cli-provider/contract-errors.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-errors.js +71 -0
- package/lib/agent-cli-provider/contract-errors.js.map +1 -0
- package/lib/agent-cli-provider/contract-fallback.d.ts +7 -0
- package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-fallback.js +102 -0
- package/lib/agent-cli-provider/contract-fallback.js.map +1 -0
- package/lib/agent-cli-provider/contract-invoke.d.ts +5 -0
- package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-invoke.js +89 -0
- package/lib/agent-cli-provider/contract-invoke.js.map +1 -0
- package/lib/agent-cli-provider/contract-options.d.ts +3 -0
- package/lib/agent-cli-provider/contract-options.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-options.js +140 -0
- package/lib/agent-cli-provider/contract-options.js.map +1 -0
- package/lib/agent-cli-provider/contract-parse.d.ts +15 -0
- package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-parse.js +81 -0
- package/lib/agent-cli-provider/contract-parse.js.map +1 -0
- package/lib/agent-cli-provider/contract-support.d.ts +18 -0
- package/lib/agent-cli-provider/contract-support.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract-support.js +154 -0
- package/lib/agent-cli-provider/contract-support.js.map +1 -0
- package/lib/agent-cli-provider/contract.d.ts +13 -0
- package/lib/agent-cli-provider/contract.d.ts.map +1 -0
- package/lib/agent-cli-provider/contract.js +30 -0
- package/lib/agent-cli-provider/contract.js.map +1 -0
- package/lib/agent-cli-provider/env-safety.d.ts +11 -0
- package/lib/agent-cli-provider/env-safety.d.ts.map +1 -0
- package/lib/agent-cli-provider/env-safety.js +83 -0
- package/lib/agent-cli-provider/env-safety.js.map +1 -0
- package/lib/agent-cli-provider/errors.d.ts +5 -0
- package/lib/agent-cli-provider/errors.d.ts.map +1 -0
- package/lib/agent-cli-provider/errors.js +115 -0
- package/lib/agent-cli-provider/errors.js.map +1 -0
- package/lib/agent-cli-provider/executable.d.ts +3 -0
- package/lib/agent-cli-provider/executable.d.ts.map +1 -0
- package/lib/agent-cli-provider/executable.js +24 -0
- package/lib/agent-cli-provider/executable.js.map +1 -0
- package/lib/agent-cli-provider/index.d.ts +8 -0
- package/lib/agent-cli-provider/index.d.ts.map +1 -0
- package/lib/agent-cli-provider/index.js +31 -0
- package/lib/agent-cli-provider/index.js.map +1 -0
- package/lib/agent-cli-provider/invoke-evidence.d.ts +4 -0
- package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -0
- package/lib/agent-cli-provider/invoke-evidence.js +17 -0
- package/lib/agent-cli-provider/invoke-evidence.js.map +1 -0
- package/lib/agent-cli-provider/json.d.ts +16 -0
- package/lib/agent-cli-provider/json.d.ts.map +1 -0
- package/lib/agent-cli-provider/json.js +110 -0
- package/lib/agent-cli-provider/json.js.map +1 -0
- package/lib/agent-cli-provider/log-prefix.d.ts +2 -0
- package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -0
- package/lib/agent-cli-provider/log-prefix.js +22 -0
- package/lib/agent-cli-provider/log-prefix.js.map +1 -0
- package/lib/agent-cli-provider/process-runner.d.ts +17 -0
- package/lib/agent-cli-provider/process-runner.d.ts.map +1 -0
- package/lib/agent-cli-provider/process-runner.js +89 -0
- package/lib/agent-cli-provider/process-runner.js.map +1 -0
- package/lib/agent-cli-provider/redaction.d.ts +9 -0
- package/lib/agent-cli-provider/redaction.d.ts.map +1 -0
- package/lib/agent-cli-provider/redaction.js +227 -0
- package/lib/agent-cli-provider/redaction.js.map +1 -0
- package/lib/agent-cli-provider/schema.d.ts +5 -0
- package/lib/agent-cli-provider/schema.d.ts.map +1 -0
- package/lib/agent-cli-provider/schema.js +136 -0
- package/lib/agent-cli-provider/schema.js.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts +15 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.js +228 -0
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -0
- package/lib/agent-cli-provider/types.d.ts +194 -0
- package/lib/agent-cli-provider/types.d.ts.map +1 -0
- package/lib/agent-cli-provider/types.js +12 -0
- package/lib/agent-cli-provider/types.js.map +1 -0
- package/lib/completion.js +174 -0
- package/lib/detached-startup.js +220 -0
- package/lib/docker-config.js +220 -0
- package/lib/git-remote-utils.js +165 -0
- package/lib/id-detector.js +55 -0
- package/lib/provider-defaults.js +62 -0
- package/lib/provider-detection.js +59 -0
- package/lib/provider-names.js +57 -0
- package/lib/repo-settings.js +69 -0
- package/lib/settings/claude-auth.js +78 -0
- package/lib/settings.js +542 -0
- package/lib/start-cluster.js +321 -0
- package/lib/stream-json-parser.js +67 -0
- package/package.json +162 -0
- package/scripts/fix-node-pty-permissions.js +75 -0
- package/scripts/record-demo.sh +279 -0
- package/scripts/setup-merge-queue.sh +170 -0
- package/scripts/test-install.sh +40 -0
- package/scripts/validate-templates.js +107 -0
- package/src/agent/agent-config.js +266 -0
- package/src/agent/agent-context-builder.js +189 -0
- package/src/agent/agent-context-sections.js +338 -0
- package/src/agent/agent-context-sources.js +147 -0
- package/src/agent/agent-hook-executor.js +721 -0
- package/src/agent/agent-input-injector.js +141 -0
- package/src/agent/agent-lifecycle.js +982 -0
- package/src/agent/agent-quality-gate-schema.js +93 -0
- package/src/agent/agent-quality-gates-context.js +51 -0
- package/src/agent/agent-stuck-detector.js +256 -0
- package/src/agent/agent-task-executor.js +2028 -0
- package/src/agent/agent-trigger-evaluator.js +67 -0
- package/src/agent/context-metrics.js +160 -0
- package/src/agent/context-pack-builder.js +367 -0
- package/src/agent/context-replay-policy.js +51 -0
- package/src/agent/guidance-queue.js +77 -0
- package/src/agent/output-extraction.js +367 -0
- package/src/agent/output-reformatter.js +175 -0
- package/src/agent/pr-verification.js +653 -0
- package/src/agent/rate-limit-backoff.js +82 -0
- package/src/agent/schema-utils.js +146 -0
- package/src/agent/validation-platform.js +35 -0
- package/src/agent-cli-provider/adapters/claude-parser.ts +133 -0
- package/src/agent-cli-provider/adapters/claude-recovery.ts +203 -0
- package/src/agent-cli-provider/adapters/claude.ts +247 -0
- package/src/agent-cli-provider/adapters/codex-parser.ts +211 -0
- package/src/agent-cli-provider/adapters/codex.ts +217 -0
- package/src/agent-cli-provider/adapters/common.ts +124 -0
- package/src/agent-cli-provider/adapters/gemini.ts +243 -0
- package/src/agent-cli-provider/adapters/index.ts +126 -0
- package/src/agent-cli-provider/adapters/opencode.ts +286 -0
- package/src/agent-cli-provider/contract-actions.ts +150 -0
- package/src/agent-cli-provider/contract-env.ts +111 -0
- package/src/agent-cli-provider/contract-envelope.ts +110 -0
- package/src/agent-cli-provider/contract-errors.ts +66 -0
- package/src/agent-cli-provider/contract-fallback.ts +121 -0
- package/src/agent-cli-provider/contract-invoke.ts +104 -0
- package/src/agent-cli-provider/contract-options.ts +173 -0
- package/src/agent-cli-provider/contract-parse.ts +94 -0
- package/src/agent-cli-provider/contract-support.ts +167 -0
- package/src/agent-cli-provider/contract.ts +56 -0
- package/src/agent-cli-provider/env-safety.ts +82 -0
- package/src/agent-cli-provider/errors.ts +122 -0
- package/src/agent-cli-provider/executable.ts +24 -0
- package/src/agent-cli-provider/index.ts +83 -0
- package/src/agent-cli-provider/invoke-evidence.ts +18 -0
- package/src/agent-cli-provider/json.ts +114 -0
- package/src/agent-cli-provider/log-prefix.ts +20 -0
- package/src/agent-cli-provider/process-runner.ts +145 -0
- package/src/agent-cli-provider/redaction.ts +282 -0
- package/src/agent-cli-provider/schema.ts +115 -0
- package/src/agent-cli-provider/single-agent-runtime.ts +311 -0
- package/src/agent-cli-provider/types.ts +237 -0
- package/src/agent-wrapper.js +615 -0
- package/src/agents/git-pusher-template.js +705 -0
- package/src/attach/attach-client.js +438 -0
- package/src/attach/attach-server.js +543 -0
- package/src/attach/index.js +37 -0
- package/src/attach/protocol.js +220 -0
- package/src/attach/ring-buffer.js +121 -0
- package/src/attach/send-input.js +88 -0
- package/src/attach/socket-discovery.js +267 -0
- package/src/claude-task-runner.js +661 -0
- package/src/config-router.js +89 -0
- package/src/config-validator.js +2202 -0
- package/src/copy-worker.js +43 -0
- package/src/guidance-topics.js +10 -0
- package/src/input-helpers.js +65 -0
- package/src/isolation-manager.js +1734 -0
- package/src/issue-providers/README.md +305 -0
- package/src/issue-providers/azure-devops-provider.js +307 -0
- package/src/issue-providers/base-provider.js +232 -0
- package/src/issue-providers/github-provider.js +210 -0
- package/src/issue-providers/gitlab-provider.js +262 -0
- package/src/issue-providers/index.js +196 -0
- package/src/issue-providers/jira-provider.js +260 -0
- package/src/ledger.js +692 -0
- package/src/lib/gc.js +232 -0
- package/src/lib/safe-exec.js +88 -0
- package/src/logic-engine.js +201 -0
- package/src/message-buffer.js +81 -0
- package/src/message-bus-bridge.js +144 -0
- package/src/message-bus.js +256 -0
- package/src/name-generator.js +232 -0
- package/src/orchestrator.js +3924 -0
- package/src/preflight.js +712 -0
- package/src/process-metrics.js +608 -0
- package/src/providers/anthropic/index.js +3 -0
- package/src/providers/base-provider.js +355 -0
- package/src/providers/capabilities.js +60 -0
- package/src/providers/google/index.js +3 -0
- package/src/providers/index.js +293 -0
- package/src/providers/openai/index.js +3 -0
- package/src/providers/opencode/index.js +3 -0
- package/src/quality-gates.js +143 -0
- package/src/schemas/sub-cluster.js +208 -0
- package/src/state-snapshot.js +398 -0
- package/src/state-snapshotter.js +142 -0
- package/src/status-footer.js +1026 -0
- package/src/sub-cluster-wrapper.js +693 -0
- package/src/task-runner.js +30 -0
- package/src/template-resolver.js +425 -0
- package/src/template-validation/index.js +338 -0
- package/src/template-validation/simulate-consensus-gates.js +324 -0
- package/src/template-validation/simulate-random-topology.js +541 -0
- package/src/template-validation/simulate-two-stage-validation.js +270 -0
- package/src/worktree-claude-config.js +135 -0
- package/src/worktree-tooling-env.js +150 -0
- package/task-lib/attachable-watcher.js +381 -0
- package/task-lib/commands/clean.js +50 -0
- package/task-lib/commands/episodes.js +105 -0
- package/task-lib/commands/get-log-path.js +23 -0
- package/task-lib/commands/kill.js +32 -0
- package/task-lib/commands/list.js +105 -0
- package/task-lib/commands/logs.js +439 -0
- package/task-lib/commands/resume.js +42 -0
- package/task-lib/commands/run.js +57 -0
- package/task-lib/commands/schedule.js +105 -0
- package/task-lib/commands/scheduler-cmd.js +96 -0
- package/task-lib/commands/schedules.js +148 -0
- package/task-lib/commands/status.js +44 -0
- package/task-lib/commands/unschedule.js +16 -0
- package/task-lib/completion.js +9 -0
- package/task-lib/config.js +11 -0
- package/task-lib/name-generator.js +230 -0
- package/task-lib/package.json +3 -0
- package/task-lib/provider-helper-runtime.js +29 -0
- package/task-lib/runner.js +190 -0
- package/task-lib/scheduler.js +252 -0
- package/task-lib/store.js +529 -0
- package/task-lib/watcher.js +305 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { appendJsonSchemaPrompt } from '../schema';
|
|
2
|
+
import {
|
|
3
|
+
getNumber,
|
|
4
|
+
getOptionalString,
|
|
5
|
+
getRecord,
|
|
6
|
+
getString,
|
|
7
|
+
isRecord,
|
|
8
|
+
tryParseJson,
|
|
9
|
+
} from '../json';
|
|
10
|
+
import {
|
|
11
|
+
type BuildProviderCommandOptions,
|
|
12
|
+
type CommandSpec,
|
|
13
|
+
type ErrorClassification,
|
|
14
|
+
type LevelModelSpec,
|
|
15
|
+
type LevelOverrides,
|
|
16
|
+
type ModelCatalogEntry,
|
|
17
|
+
type ModelLevel,
|
|
18
|
+
type OpencodeCliFeatures,
|
|
19
|
+
type OutputEvent,
|
|
20
|
+
type ProviderAdapter,
|
|
21
|
+
type ResolvedModelSpec,
|
|
22
|
+
type WarningMetadata,
|
|
23
|
+
} from '../types';
|
|
24
|
+
import {
|
|
25
|
+
classifyBaseProviderError,
|
|
26
|
+
commandSpec,
|
|
27
|
+
createParserState,
|
|
28
|
+
optionFeatures,
|
|
29
|
+
resolveModelSpecWithConfig,
|
|
30
|
+
unsupportedSessionControlWarnings,
|
|
31
|
+
validateModelIdFromCatalog,
|
|
32
|
+
warning,
|
|
33
|
+
} from './common';
|
|
34
|
+
|
|
35
|
+
const MODEL_CATALOG: Readonly<Record<string, ModelCatalogEntry>> = {
|
|
36
|
+
'opencode/big-pickle': { rank: 1 },
|
|
37
|
+
'opencode/glm-4.7-free': { rank: 1 },
|
|
38
|
+
'opencode/gpt-5-nano': { rank: 1 },
|
|
39
|
+
'opencode/grok-code': { rank: 1 },
|
|
40
|
+
'opencode/minimax-m2.1-free': { rank: 1 },
|
|
41
|
+
'google/gemini-1.5-flash': { rank: 1 },
|
|
42
|
+
'google/gemini-1.5-flash-8b': { rank: 1 },
|
|
43
|
+
'google/gemini-1.5-pro': { rank: 1 },
|
|
44
|
+
'google/gemini-2.0-flash': { rank: 1 },
|
|
45
|
+
'google/gemini-2.0-flash-lite': { rank: 1 },
|
|
46
|
+
'google/gemini-2.5-flash': { rank: 1 },
|
|
47
|
+
'google/gemini-2.5-flash-image': { rank: 1 },
|
|
48
|
+
'google/gemini-2.5-flash-image-preview': { rank: 1 },
|
|
49
|
+
'google/gemini-2.5-flash-lite': { rank: 1 },
|
|
50
|
+
'google/gemini-2.5-flash-lite-preview-06-17': { rank: 1 },
|
|
51
|
+
'google/gemini-2.5-flash-lite-preview-09-2025': { rank: 1 },
|
|
52
|
+
'google/gemini-2.5-flash-preview-04-17': { rank: 1 },
|
|
53
|
+
'google/gemini-2.5-flash-preview-05-20': { rank: 1 },
|
|
54
|
+
'google/gemini-2.5-flash-preview-09-2025': { rank: 1 },
|
|
55
|
+
'google/gemini-2.5-flash-preview-tts': { rank: 1 },
|
|
56
|
+
'google/gemini-2.5-pro': { rank: 1 },
|
|
57
|
+
'google/gemini-2.5-pro-preview-05-06': { rank: 1 },
|
|
58
|
+
'google/gemini-2.5-pro-preview-06-05': { rank: 1 },
|
|
59
|
+
'google/gemini-2.5-pro-preview-tts': { rank: 1 },
|
|
60
|
+
'google/gemini-3-flash-preview': { rank: 1 },
|
|
61
|
+
'google/gemini-3-pro-preview': { rank: 1 },
|
|
62
|
+
'google/gemini-embedding-001': { rank: 1 },
|
|
63
|
+
'google/gemini-flash-latest': { rank: 1 },
|
|
64
|
+
'google/gemini-flash-lite-latest': { rank: 1 },
|
|
65
|
+
'google/gemini-live-2.5-flash': { rank: 1 },
|
|
66
|
+
'google/gemini-live-2.5-flash-preview-native-audio': { rank: 1 },
|
|
67
|
+
'openai/gpt-5.1-codex-max': { rank: 1 },
|
|
68
|
+
'openai/gpt-5.1-codex-mini': { rank: 1 },
|
|
69
|
+
'openai/gpt-5.2': { rank: 1 },
|
|
70
|
+
'openai/gpt-5.2-codex': { rank: 1 },
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const LEVEL_MAPPING: Readonly<Record<ModelLevel, LevelModelSpec>> = {
|
|
74
|
+
level1: { rank: 1, model: null, reasoningEffort: 'low' },
|
|
75
|
+
level2: { rank: 2, model: null, reasoningEffort: 'medium' },
|
|
76
|
+
level3: { rank: 3, model: null, reasoningEffort: 'high' },
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
function detectCliFeatures(helpText?: string | null): OpencodeCliFeatures {
|
|
80
|
+
const help = helpText ?? '';
|
|
81
|
+
const unknown = !help;
|
|
82
|
+
return {
|
|
83
|
+
provider: 'opencode',
|
|
84
|
+
supportsJson: unknown ? true : /--format\b/.test(help),
|
|
85
|
+
supportsModel: unknown ? true : /--model\b/.test(help),
|
|
86
|
+
supportsVariant: unknown ? true : /--variant\b/.test(help),
|
|
87
|
+
supportsCwd: unknown ? false : /--cwd\b/.test(help),
|
|
88
|
+
supportsAutoApprove: false,
|
|
89
|
+
unknown,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function addOpencodeOptionalArgs(args: string[], options: BuildProviderCommandOptions): void {
|
|
94
|
+
const features = optionFeatures(options);
|
|
95
|
+
if (
|
|
96
|
+
(options.outputFormat === 'stream-json' || options.outputFormat === 'json') &&
|
|
97
|
+
features.supportsJson
|
|
98
|
+
) {
|
|
99
|
+
args.push('--format', 'json');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (options.modelSpec?.model) {
|
|
103
|
+
args.push('--model', options.modelSpec.model);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (options.modelSpec?.reasoningEffort && features.supportsVariant) {
|
|
107
|
+
args.push('--variant', options.modelSpec.reasoningEffort);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (options.cwd && features.supportsCwd) {
|
|
111
|
+
args.push('--cwd', options.cwd);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function collectOpencodeWarnings(options: BuildProviderCommandOptions): WarningMetadata[] {
|
|
116
|
+
const features = optionFeatures(options);
|
|
117
|
+
const warnings: WarningMetadata[] = unsupportedSessionControlWarnings('opencode', options);
|
|
118
|
+
if (options.modelSpec?.reasoningEffort && features.supportsVariant === false) {
|
|
119
|
+
warnings.push(
|
|
120
|
+
warning(
|
|
121
|
+
'opencode',
|
|
122
|
+
'opencode-variant',
|
|
123
|
+
'Opencode CLI does not support --variant; skipping reasoningEffort.'
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return warnings;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function buildCommand(context: string, options: BuildProviderCommandOptions = {}): CommandSpec {
|
|
131
|
+
const finalContext = options.jsonSchema
|
|
132
|
+
? appendJsonSchemaPrompt(context, options.jsonSchema)
|
|
133
|
+
: context;
|
|
134
|
+
const args: string[] = ['run'];
|
|
135
|
+
addOpencodeOptionalArgs(args, options);
|
|
136
|
+
|
|
137
|
+
args.push(finalContext);
|
|
138
|
+
|
|
139
|
+
return commandSpec({
|
|
140
|
+
binary: 'opencode',
|
|
141
|
+
args,
|
|
142
|
+
env: {},
|
|
143
|
+
...(options.cwd === undefined ? {} : { cwd: options.cwd }),
|
|
144
|
+
warnings: collectOpencodeWarnings(options),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function parseToolPart(part: Record<string, unknown>): OutputEvent | null {
|
|
149
|
+
const state = getRecord(part, 'state') ?? {};
|
|
150
|
+
const status = getString(state, 'status');
|
|
151
|
+
if (status === 'pending' || status === 'running') {
|
|
152
|
+
return {
|
|
153
|
+
type: 'tool_call',
|
|
154
|
+
toolName: getOptionalString(part, 'tool'),
|
|
155
|
+
toolId: getOptionalString(part, 'callID'),
|
|
156
|
+
input: state.input ?? {},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (status === 'completed') {
|
|
161
|
+
return {
|
|
162
|
+
type: 'tool_result',
|
|
163
|
+
toolId: getOptionalString(part, 'callID'),
|
|
164
|
+
content: state.output || '',
|
|
165
|
+
isError: false,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (status === 'error') {
|
|
170
|
+
return {
|
|
171
|
+
type: 'tool_result',
|
|
172
|
+
toolId: getOptionalString(part, 'callID'),
|
|
173
|
+
content: state.error || '',
|
|
174
|
+
isError: true,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function parseStepFinish(part: Record<string, unknown>): OutputEvent {
|
|
182
|
+
const tokens = getRecord(part, 'tokens') ?? {};
|
|
183
|
+
return {
|
|
184
|
+
type: 'result',
|
|
185
|
+
success: true,
|
|
186
|
+
inputTokens: getNumber(tokens, 'input') ?? 0,
|
|
187
|
+
outputTokens: getNumber(tokens, 'output') ?? 0,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function parsePart(part: unknown): OutputEvent | null {
|
|
192
|
+
if (!isRecord(part)) return null;
|
|
193
|
+
if (getString(part, 'type') === 'text') {
|
|
194
|
+
const text = getString(part, 'text');
|
|
195
|
+
if (text) return { type: 'text', text };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (getString(part, 'type') === 'reasoning') {
|
|
199
|
+
const text = getString(part, 'text');
|
|
200
|
+
if (text) return { type: 'thinking', text };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (getString(part, 'type') === 'tool') {
|
|
204
|
+
return parseToolPart(part);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (getString(part, 'type') === 'step-finish') {
|
|
208
|
+
return parseStepFinish(part);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function parseErrorEvent(event: Record<string, unknown>): OutputEvent {
|
|
215
|
+
const error = getRecord(event, 'error') ?? {};
|
|
216
|
+
const data = getRecord(error, 'data');
|
|
217
|
+
return {
|
|
218
|
+
type: 'result',
|
|
219
|
+
success: false,
|
|
220
|
+
error:
|
|
221
|
+
(data ? getString(data, 'message') : null) ??
|
|
222
|
+
getString(error, 'message') ??
|
|
223
|
+
getString(error, 'name') ??
|
|
224
|
+
'Unknown error',
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function parseEvent(line: string): OutputEvent | null {
|
|
229
|
+
const event = tryParseJson(line);
|
|
230
|
+
if (!isRecord(event)) return null;
|
|
231
|
+
|
|
232
|
+
const type = getString(event, 'type');
|
|
233
|
+
if (type === 'error') return parseErrorEvent(event);
|
|
234
|
+
if (type === 'text' || type === 'step_start' || type === 'step_finish') {
|
|
235
|
+
return parsePart(event.part ?? event);
|
|
236
|
+
}
|
|
237
|
+
if (type === 'message.part.updated') {
|
|
238
|
+
const properties = getRecord(event, 'properties');
|
|
239
|
+
return parsePart(properties?.part);
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function resolveModelSpec(level: ModelLevel, overrides?: LevelOverrides): ResolvedModelSpec {
|
|
245
|
+
return resolveModelSpecWithConfig({
|
|
246
|
+
mapping: LEVEL_MAPPING,
|
|
247
|
+
defaultLevel: 'level2',
|
|
248
|
+
level,
|
|
249
|
+
overrides,
|
|
250
|
+
validateModelId,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function validateModelId(modelId: string | null | undefined): string | null | undefined {
|
|
255
|
+
return validateModelIdFromCatalog('opencode', MODEL_CATALOG, modelId);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function classifyError(error: unknown): ErrorClassification {
|
|
259
|
+
return classifyBaseProviderError(error, [], []);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export const opencodeAdapter: ProviderAdapter = {
|
|
263
|
+
id: 'opencode',
|
|
264
|
+
displayName: 'Opencode',
|
|
265
|
+
binary: 'opencode',
|
|
266
|
+
adapterVersion: '1',
|
|
267
|
+
credentialEnvKeys: [
|
|
268
|
+
'OPENCODE_API_KEY',
|
|
269
|
+
'OPENAI_API_KEY',
|
|
270
|
+
'ANTHROPIC_API_KEY',
|
|
271
|
+
'GEMINI_API_KEY',
|
|
272
|
+
'GOOGLE_API_KEY',
|
|
273
|
+
],
|
|
274
|
+
modelCatalog: MODEL_CATALOG,
|
|
275
|
+
levelMapping: LEVEL_MAPPING,
|
|
276
|
+
defaultLevel: 'level2',
|
|
277
|
+
defaultMaxLevel: 'level3',
|
|
278
|
+
defaultMinLevel: 'level1',
|
|
279
|
+
detectCliFeatures,
|
|
280
|
+
buildCommand,
|
|
281
|
+
parseEvent,
|
|
282
|
+
createParserState: () => createParserState('opencode'),
|
|
283
|
+
resolveModelSpec,
|
|
284
|
+
validateModelId,
|
|
285
|
+
classifyError,
|
|
286
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { classifyProviderError } from './adapters';
|
|
2
|
+
import { envRedactions, commandRedactions } from './contract-env';
|
|
3
|
+
import { contractError } from './contract-errors';
|
|
4
|
+
import {
|
|
5
|
+
providerExecutableSchemaVersion,
|
|
6
|
+
successEnvelope,
|
|
7
|
+
type ContractEnvelope,
|
|
8
|
+
type ContractEvidence,
|
|
9
|
+
} from './contract-envelope';
|
|
10
|
+
import { runInvoke } from './contract-invoke';
|
|
11
|
+
import { runParseOutput } from './contract-parse';
|
|
12
|
+
import {
|
|
13
|
+
adapterForProvider,
|
|
14
|
+
buildCommandSpec,
|
|
15
|
+
schemaMode,
|
|
16
|
+
type RequestData,
|
|
17
|
+
} from './contract-support';
|
|
18
|
+
import { detectRuntimeProviderCliFeatures } from './single-agent-runtime';
|
|
19
|
+
import { getNumber, getRecord, getString, isRecord, unknownToMessage } from './json';
|
|
20
|
+
import type { ErrorClassification } from './types';
|
|
21
|
+
import type { ProcessRunner } from './process-runner';
|
|
22
|
+
|
|
23
|
+
function runBuildCommand(request: RequestData): ContractEnvelope {
|
|
24
|
+
const { adapter, commandSpec, options } = buildCommandSpec(request);
|
|
25
|
+
return successEnvelope({
|
|
26
|
+
command: request.command ?? 'build-command',
|
|
27
|
+
adapter,
|
|
28
|
+
warnings: commandSpec.warnings,
|
|
29
|
+
redactions: commandRedactions(commandSpec),
|
|
30
|
+
evidence: {
|
|
31
|
+
outputFormat: options.outputFormat ?? null,
|
|
32
|
+
schemaMode: schemaMode(options),
|
|
33
|
+
},
|
|
34
|
+
result: {
|
|
35
|
+
commandSpec,
|
|
36
|
+
outputFormat: options.outputFormat ?? null,
|
|
37
|
+
schemaMode: schemaMode(options),
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function runProbe(request: RequestData): ContractEnvelope {
|
|
43
|
+
const adapter = adapterForProvider(request.provider);
|
|
44
|
+
const helpText = typeof request.raw.helpText === 'string' ? request.raw.helpText : null;
|
|
45
|
+
const capabilities =
|
|
46
|
+
helpText === null ? detectRuntimeProviderCliFeatures(adapter.id) : adapter.detectCliFeatures(helpText);
|
|
47
|
+
return successEnvelope({
|
|
48
|
+
command: request.command ?? 'probe',
|
|
49
|
+
adapter,
|
|
50
|
+
redactions: envRedactions(request.env),
|
|
51
|
+
result: {
|
|
52
|
+
provider: {
|
|
53
|
+
id: adapter.id,
|
|
54
|
+
displayName: adapter.displayName,
|
|
55
|
+
binary: adapter.binary,
|
|
56
|
+
},
|
|
57
|
+
contractVersion: providerExecutableSchemaVersion,
|
|
58
|
+
adapterVersion: adapter.adapterVersion,
|
|
59
|
+
capabilities,
|
|
60
|
+
credentials: adapter.credentialEnvKeys.map((key) => ({
|
|
61
|
+
key,
|
|
62
|
+
present: Boolean(request.env[key] ?? process.env[key]),
|
|
63
|
+
})),
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function statusFromError(error: unknown): number | null {
|
|
69
|
+
if (!isRecord(error)) return null;
|
|
70
|
+
const directStatus = getNumber(error, 'status') ?? getNumber(error, 'statusCode');
|
|
71
|
+
if (directStatus !== null) return directStatus;
|
|
72
|
+
|
|
73
|
+
const response = getRecord(error, 'response');
|
|
74
|
+
if (response === null) return null;
|
|
75
|
+
return getNumber(response, 'status') ?? getNumber(response, 'statusCode');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function categoryForClassification(classification: ErrorClassification, error: unknown): string {
|
|
79
|
+
const status = statusFromError(error);
|
|
80
|
+
if (status === 401 || status === 403) return 'auth';
|
|
81
|
+
if (status === 429) return 'rate-limit';
|
|
82
|
+
const message = unknownToMessage(error);
|
|
83
|
+
if (/auth|api[_ -]?key|unauthorized|forbidden|permission/i.test(message)) return 'auth';
|
|
84
|
+
if (/rate|429|quota|resource_exhausted/i.test(message)) return 'rate-limit';
|
|
85
|
+
if (/schema|json|parse|format/i.test(message)) return 'schema';
|
|
86
|
+
return classification.retryable ? 'retryable' : 'permanent';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function errorEvidence(error: unknown): ContractEvidence {
|
|
90
|
+
const evidence: Record<string, unknown> = {
|
|
91
|
+
message: unknownToMessage(error),
|
|
92
|
+
};
|
|
93
|
+
if (isRecord(error)) {
|
|
94
|
+
const status = statusFromError(error);
|
|
95
|
+
const code = getString(error, 'code');
|
|
96
|
+
if (status !== null) evidence.status = status;
|
|
97
|
+
if (code !== null) evidence.code = code;
|
|
98
|
+
}
|
|
99
|
+
return evidence;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function runClassifyError(request: RequestData): ContractEnvelope {
|
|
103
|
+
const adapter = adapterForProvider(request.provider);
|
|
104
|
+
const error = request.raw.error;
|
|
105
|
+
if (error === undefined) {
|
|
106
|
+
throw contractError({
|
|
107
|
+
code: 'missing-field',
|
|
108
|
+
message: 'error is required.',
|
|
109
|
+
exitCode: 2,
|
|
110
|
+
field: 'error',
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const classification = classifyProviderError(adapter.id, error);
|
|
114
|
+
return successEnvelope({
|
|
115
|
+
command: request.command ?? 'classify-error',
|
|
116
|
+
adapter,
|
|
117
|
+
redactions: envRedactions(request.env),
|
|
118
|
+
evidence: errorEvidence(error),
|
|
119
|
+
result: {
|
|
120
|
+
classification,
|
|
121
|
+
category: categoryForClassification(classification, error),
|
|
122
|
+
evidence: errorEvidence(error),
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function dispatchRequest(
|
|
128
|
+
request: RequestData,
|
|
129
|
+
runner: ProcessRunner
|
|
130
|
+
): ContractEnvelope | Promise<ContractEnvelope> {
|
|
131
|
+
switch (request.command) {
|
|
132
|
+
case 'probe':
|
|
133
|
+
return runProbe(request);
|
|
134
|
+
case 'build-command':
|
|
135
|
+
return runBuildCommand(request);
|
|
136
|
+
case 'parse-output':
|
|
137
|
+
return runParseOutput(request);
|
|
138
|
+
case 'classify-error':
|
|
139
|
+
return runClassifyError(request);
|
|
140
|
+
case 'invoke':
|
|
141
|
+
return runInvoke(request, runner);
|
|
142
|
+
default:
|
|
143
|
+
throw contractError({
|
|
144
|
+
code: 'unknown-command',
|
|
145
|
+
message: `Unknown command: ${request.command ?? ''}.`,
|
|
146
|
+
exitCode: 3,
|
|
147
|
+
field: 'command',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { getProviderAdapter } from './adapters';
|
|
2
|
+
import { findUnsafeProviderEnvKey } from './env-safety';
|
|
3
|
+
import { isRecord } from './json';
|
|
4
|
+
import { mergeRedactions } from './redaction';
|
|
5
|
+
import { contractError } from './contract-errors';
|
|
6
|
+
import type { CommandSpec, ProviderAdapter, RedactionMetadata } from './types';
|
|
7
|
+
|
|
8
|
+
export function stringRecord(value: unknown, field: string): Readonly<Record<string, string>> {
|
|
9
|
+
if (value === undefined) return {};
|
|
10
|
+
if (!isRecord(value)) {
|
|
11
|
+
throw contractError({
|
|
12
|
+
code: 'invalid-field',
|
|
13
|
+
message: `${field} must be an object with string values.`,
|
|
14
|
+
exitCode: 2,
|
|
15
|
+
field,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const result: Record<string, string> = {};
|
|
20
|
+
for (const [key, item] of Object.entries(value)) {
|
|
21
|
+
if (typeof item !== 'string') {
|
|
22
|
+
throw contractError({
|
|
23
|
+
code: 'invalid-field',
|
|
24
|
+
message: `${field}.${key} must be a string.`,
|
|
25
|
+
exitCode: 2,
|
|
26
|
+
field: `${field}.${key}`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
result[key] = item;
|
|
30
|
+
}
|
|
31
|
+
assertNoUnsafeProviderEnv(result, field);
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function assertNoUnsafeProviderEnv(env: Readonly<Record<string, string>>, field: string): void {
|
|
36
|
+
const key = findUnsafeProviderEnvKey(env);
|
|
37
|
+
if (key === null) return;
|
|
38
|
+
throw contractError({
|
|
39
|
+
code: 'forbidden-field',
|
|
40
|
+
message: `${field}.${key} is not accepted by provider executable requests; provider adapters own executable resolution and process-control environment.`,
|
|
41
|
+
exitCode: 2,
|
|
42
|
+
field: `${field}.${key}`,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function envRedactions(env: Readonly<Record<string, string>>): readonly RedactionMetadata[] {
|
|
47
|
+
return Object.keys(env).map((key) => ({ kind: 'env', key }));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function commandRedactions(commandSpec: CommandSpec): readonly RedactionMetadata[] {
|
|
51
|
+
return mergeRedactions(commandSpec.redactions, envRedactions(commandSpec.env));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function providerCredentialEnv(provider: string | null): Readonly<Record<string, string>> {
|
|
55
|
+
if (provider === null) return {};
|
|
56
|
+
let adapter: ProviderAdapter;
|
|
57
|
+
try {
|
|
58
|
+
adapter = getProviderAdapter(provider);
|
|
59
|
+
} catch {
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
62
|
+
const result: Record<string, string> = {};
|
|
63
|
+
for (const key of adapter.credentialEnvKeys) {
|
|
64
|
+
const value = process.env[key];
|
|
65
|
+
if (typeof value === 'string' && value.length > 0) result[key] = value;
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function mergeEnvForRedaction(
|
|
71
|
+
...inputs: readonly {
|
|
72
|
+
readonly source: string;
|
|
73
|
+
readonly env: Readonly<Record<string, string>>;
|
|
74
|
+
}[]
|
|
75
|
+
): Readonly<Record<string, string>> {
|
|
76
|
+
const result: Record<string, string> = {};
|
|
77
|
+
for (const input of inputs) {
|
|
78
|
+
for (const [key, value] of Object.entries(input.env)) {
|
|
79
|
+
if (value.length === 0) continue;
|
|
80
|
+
if (result[key] === undefined || result[key] === value) {
|
|
81
|
+
result[key] = value;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
result[`${key}:${input.source}`] = value;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function collectCommandSpecEnv(value: unknown): Readonly<Record<string, string>> {
|
|
91
|
+
const result: Record<string, string> = {};
|
|
92
|
+
collectCommandSpecEnvInto(value, result);
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function collectCommandSpecEnvInto(value: unknown, result: Record<string, string>): void {
|
|
97
|
+
if (Array.isArray(value)) {
|
|
98
|
+
for (const item of value) collectCommandSpecEnvInto(item, result);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (!isRecord(value)) return;
|
|
102
|
+
|
|
103
|
+
const commandSpec = value.commandSpec;
|
|
104
|
+
if (isRecord(commandSpec) && isRecord(commandSpec.env)) {
|
|
105
|
+
for (const [key, item] of Object.entries(commandSpec.env)) {
|
|
106
|
+
if (typeof item === 'string' && item.length > 0) result[key] = item;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (const item of Object.values(value)) collectCommandSpecEnvInto(item, result);
|
|
111
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { collectCommandSpecEnv, mergeEnvForRedaction, providerCredentialEnv } from './contract-env';
|
|
2
|
+
import { isRecord } from './json';
|
|
3
|
+
import { mergeRedactions, redactObject } from './redaction';
|
|
4
|
+
import type { ProviderAdapter, RedactionMetadata, WarningMetadata } from './types';
|
|
5
|
+
|
|
6
|
+
export const providerExecutableSchemaVersion = 1 as const;
|
|
7
|
+
|
|
8
|
+
export interface ContractErrorObject {
|
|
9
|
+
readonly code: string;
|
|
10
|
+
readonly message: string;
|
|
11
|
+
readonly field?: string;
|
|
12
|
+
readonly classification?: unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ContractEvidence {
|
|
16
|
+
readonly [key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ContractEnvelopeBase {
|
|
20
|
+
readonly schemaVersion: 1;
|
|
21
|
+
readonly command: string | null;
|
|
22
|
+
readonly provider: string | null;
|
|
23
|
+
readonly adapterVersion: string | null;
|
|
24
|
+
readonly warnings: readonly WarningMetadata[];
|
|
25
|
+
readonly redactions: readonly RedactionMetadata[];
|
|
26
|
+
readonly evidence: ContractEvidence;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ContractSuccessEnvelope<Result = unknown> extends ContractEnvelopeBase {
|
|
30
|
+
readonly ok: true;
|
|
31
|
+
readonly result: Result;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ContractErrorEnvelope extends ContractEnvelopeBase {
|
|
35
|
+
readonly ok: false;
|
|
36
|
+
readonly error: ContractErrorObject;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type ContractEnvelope<Result = unknown> =
|
|
40
|
+
| ContractSuccessEnvelope<Result>
|
|
41
|
+
| ContractErrorEnvelope;
|
|
42
|
+
|
|
43
|
+
export function finalizeEnvelope(
|
|
44
|
+
envelope: ContractEnvelope,
|
|
45
|
+
env: Readonly<Record<string, string>>
|
|
46
|
+
): ContractEnvelope {
|
|
47
|
+
const redacted = redactObject(
|
|
48
|
+
envelope,
|
|
49
|
+
mergeEnvForRedaction(
|
|
50
|
+
{ source: 'commandSpec', env: collectCommandSpecEnv(envelope) },
|
|
51
|
+
{ source: 'process', env: providerCredentialEnv(envelope.provider) },
|
|
52
|
+
{ source: 'request', env }
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
if (!isContractEnvelope(redacted.value)) {
|
|
56
|
+
throw new Error('Redacted provider contract envelope lost its JSON shape.');
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
...redacted.value,
|
|
60
|
+
redactions: mergeRedactions(envelope.redactions, redacted.redactions),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isContractEnvelope(value: unknown): value is ContractEnvelope {
|
|
65
|
+
if (!isRecord(value)) return false;
|
|
66
|
+
return value.schemaVersion === providerExecutableSchemaVersion && typeof value.ok === 'boolean';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function successEnvelope<Result>(input: {
|
|
70
|
+
readonly command: string;
|
|
71
|
+
readonly adapter: ProviderAdapter;
|
|
72
|
+
readonly result: Result;
|
|
73
|
+
readonly warnings?: readonly WarningMetadata[];
|
|
74
|
+
readonly redactions?: readonly RedactionMetadata[];
|
|
75
|
+
readonly evidence?: ContractEvidence;
|
|
76
|
+
}): ContractSuccessEnvelope<Result> {
|
|
77
|
+
return {
|
|
78
|
+
schemaVersion: providerExecutableSchemaVersion,
|
|
79
|
+
ok: true,
|
|
80
|
+
command: input.command,
|
|
81
|
+
provider: input.adapter.id,
|
|
82
|
+
adapterVersion: input.adapter.adapterVersion,
|
|
83
|
+
warnings: input.warnings ?? [],
|
|
84
|
+
redactions: input.redactions ?? [],
|
|
85
|
+
evidence: input.evidence ?? {},
|
|
86
|
+
result: input.result,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function errorEnvelope(input: {
|
|
91
|
+
readonly command: string | null;
|
|
92
|
+
readonly provider: string | null;
|
|
93
|
+
readonly adapterVersion?: string | null;
|
|
94
|
+
readonly error: ContractErrorObject;
|
|
95
|
+
readonly warnings?: readonly WarningMetadata[];
|
|
96
|
+
readonly redactions?: readonly RedactionMetadata[];
|
|
97
|
+
readonly evidence?: ContractEvidence;
|
|
98
|
+
}): ContractErrorEnvelope {
|
|
99
|
+
return {
|
|
100
|
+
schemaVersion: providerExecutableSchemaVersion,
|
|
101
|
+
ok: false,
|
|
102
|
+
command: input.command,
|
|
103
|
+
provider: input.provider,
|
|
104
|
+
adapterVersion: input.adapterVersion ?? null,
|
|
105
|
+
warnings: input.warnings ?? [],
|
|
106
|
+
redactions: input.redactions ?? [],
|
|
107
|
+
evidence: input.evidence ?? {},
|
|
108
|
+
error: input.error,
|
|
109
|
+
};
|
|
110
|
+
}
|