@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,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate-limit-aware backoff for API retries
|
|
3
|
+
*
|
|
4
|
+
* Rate limit errors (429, capacity exhausted, quota exceeded) need LONGER delays
|
|
5
|
+
* than transient errors (timeouts, network issues).
|
|
6
|
+
*
|
|
7
|
+
* - Regular errors: 2s base, exponential backoff up to 30s
|
|
8
|
+
* - Rate limits: 30s base, exponential backoff up to 5 minutes
|
|
9
|
+
* - Retry-After header: Honored if present (capped at 5 min)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Check if error is a rate limit error
|
|
14
|
+
* @param {Error|string} error - Error object or message
|
|
15
|
+
* @returns {boolean} True if this is a rate limit error
|
|
16
|
+
*/
|
|
17
|
+
function isRateLimitError(error) {
|
|
18
|
+
const msg = error?.message || String(error);
|
|
19
|
+
return /\b429\b|rate.?limit|too many requests|no capacity|quota.?exceeded|resource.?exhausted/i.test(
|
|
20
|
+
msg
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parse Retry-After from error message
|
|
26
|
+
* Looks for patterns like "Retry-After: 120" or "retry after 120 seconds"
|
|
27
|
+
* @param {Error} error - Error object
|
|
28
|
+
* @returns {number|null} Seconds to wait, or null if not found
|
|
29
|
+
*/
|
|
30
|
+
function parseRetryAfter(error) {
|
|
31
|
+
const msg = error?.message || '';
|
|
32
|
+
// Match "Retry-After: 120" or "retry after 120" (seconds)
|
|
33
|
+
const match = msg.match(/retry.?after[:\s]+(\d+)/i);
|
|
34
|
+
return match ? parseInt(match[1], 10) : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Calculate delay for retry with rate-limit awareness
|
|
39
|
+
*
|
|
40
|
+
* Rate limit errors get 30s base delay instead of 2s.
|
|
41
|
+
* Regular errors use exponential backoff from 2s base.
|
|
42
|
+
*
|
|
43
|
+
* @param {Error} error - The error that occurred
|
|
44
|
+
* @param {number} attempt - Current attempt number (1-based)
|
|
45
|
+
* @param {Object} settings - Settings with backoff config
|
|
46
|
+
* @param {number} [settings.backoffBaseMs=2000] - Base delay for regular errors
|
|
47
|
+
* @param {number} [settings.backoffMaxMs=30000] - Max delay for regular errors
|
|
48
|
+
* @param {number} [settings.jitterFactor=0.2] - Jitter factor (±20%)
|
|
49
|
+
* @returns {number} Delay in milliseconds
|
|
50
|
+
*/
|
|
51
|
+
function calculateRateLimitDelay(error, attempt, settings = {}) {
|
|
52
|
+
const baseDelay = settings.backoffBaseMs ?? 2000;
|
|
53
|
+
const maxDelay = settings.backoffMaxMs ?? 30000;
|
|
54
|
+
const jitter = settings.jitterFactor ?? 0.2;
|
|
55
|
+
|
|
56
|
+
// Check for Retry-After header in error message
|
|
57
|
+
const retryAfter = parseRetryAfter(error);
|
|
58
|
+
if (retryAfter) {
|
|
59
|
+
// Honor Retry-After but cap at 5 minutes
|
|
60
|
+
return Math.min(retryAfter * 1000, 300000);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Rate limits get 30s base, others get normal base
|
|
64
|
+
const isRateLimit = isRateLimitError(error);
|
|
65
|
+
const effectiveBase = isRateLimit ? 30000 : baseDelay;
|
|
66
|
+
|
|
67
|
+
// Exponential: base * 2^(attempt-1)
|
|
68
|
+
let delay = effectiveBase * Math.pow(2, attempt - 1);
|
|
69
|
+
|
|
70
|
+
// Cap at appropriate max (5 min for rate limits, settings max for others)
|
|
71
|
+
delay = Math.min(delay, isRateLimit ? 300000 : maxDelay);
|
|
72
|
+
|
|
73
|
+
// Add jitter (±jitterFactor)
|
|
74
|
+
const jitterAmount = delay * jitter * (Math.random() * 2 - 1);
|
|
75
|
+
return Math.round(delay + jitterAmount);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
calculateRateLimitDelay,
|
|
80
|
+
isRateLimitError,
|
|
81
|
+
parseRetryAfter,
|
|
82
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema utilities for normalizing LLM output before validation.
|
|
3
|
+
*
|
|
4
|
+
* PROBLEM: LLMs (Claude, Gemini, Codex) via any interface (CLI, API) may return
|
|
5
|
+
* enum values that don't exactly match the schema (e.g., "simple" vs "SIMPLE").
|
|
6
|
+
*
|
|
7
|
+
* SOLUTION: Normalize enum values BEFORE validation. Provider-agnostic.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const ENUM_VARIATIONS = {
|
|
11
|
+
// taskType variations
|
|
12
|
+
BUG: 'DEBUG',
|
|
13
|
+
FIX: 'DEBUG',
|
|
14
|
+
BUGFIX: 'DEBUG',
|
|
15
|
+
BUG_FIX: 'DEBUG',
|
|
16
|
+
INVESTIGATE: 'DEBUG',
|
|
17
|
+
TROUBLESHOOT: 'DEBUG',
|
|
18
|
+
IMPLEMENT: 'TASK',
|
|
19
|
+
BUILD: 'TASK',
|
|
20
|
+
CREATE: 'TASK',
|
|
21
|
+
ADD: 'TASK',
|
|
22
|
+
FEATURE: 'TASK',
|
|
23
|
+
QUESTION: 'INQUIRY',
|
|
24
|
+
ASK: 'INQUIRY',
|
|
25
|
+
EXPLORE: 'INQUIRY',
|
|
26
|
+
RESEARCH: 'INQUIRY',
|
|
27
|
+
UNDERSTAND: 'INQUIRY',
|
|
28
|
+
// complexity variations
|
|
29
|
+
EASY: 'TRIVIAL',
|
|
30
|
+
BASIC: 'SIMPLE',
|
|
31
|
+
MINOR: 'SIMPLE',
|
|
32
|
+
MODERATE: 'STANDARD',
|
|
33
|
+
MEDIUM: 'STANDARD',
|
|
34
|
+
NORMAL: 'STANDARD',
|
|
35
|
+
HARD: 'STANDARD',
|
|
36
|
+
COMPLEX: 'CRITICAL',
|
|
37
|
+
RISKY: 'CRITICAL',
|
|
38
|
+
HIGH_RISK: 'CRITICAL',
|
|
39
|
+
DANGEROUS: 'CRITICAL',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Normalize enum values in parsed JSON to match schema definitions.
|
|
44
|
+
*
|
|
45
|
+
* Handles:
|
|
46
|
+
* - Case mismatches: "simple" → "SIMPLE"
|
|
47
|
+
* - Whitespace: " SIMPLE " → "SIMPLE"
|
|
48
|
+
* - Common variations: "bug" → "DEBUG", "fix" → "DEBUG"
|
|
49
|
+
*
|
|
50
|
+
* @param {Object} result - Parsed JSON result from LLM
|
|
51
|
+
* @param {Object} schema - JSON schema with enum definitions
|
|
52
|
+
* @returns {Object} Normalized result (mutates and returns same object)
|
|
53
|
+
*/
|
|
54
|
+
function normalizeEnumValues(result, schema) {
|
|
55
|
+
if (!result || typeof result !== 'object' || !schema?.properties) {
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
60
|
+
const matched = normalizeEnumValue({
|
|
61
|
+
result,
|
|
62
|
+
key,
|
|
63
|
+
propSchema,
|
|
64
|
+
});
|
|
65
|
+
if (matched) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Recursively handle nested objects
|
|
70
|
+
normalizeNestedValues(result, propSchema, key);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function normalizeEnumValue({ result, key, propSchema }) {
|
|
77
|
+
if (!propSchema.enum || typeof result[key] !== 'string') {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const rawValue = result[key];
|
|
82
|
+
let value = rawValue.trim().toUpperCase();
|
|
83
|
+
value = normalizeEnumCopyValue(value, propSchema.enum, key, rawValue);
|
|
84
|
+
|
|
85
|
+
// Find exact match (case-insensitive)
|
|
86
|
+
const match = findEnumMatch(propSchema.enum, value);
|
|
87
|
+
if (match) {
|
|
88
|
+
result[key] = match;
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Common variations mapping
|
|
93
|
+
const variation = ENUM_VARIATIONS[value];
|
|
94
|
+
if (variation && propSchema.enum.includes(variation)) {
|
|
95
|
+
result[key] = variation;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function normalizeEnumCopyValue(value, enumValues, key, rawValue) {
|
|
102
|
+
// DETECT: Model copied the enum list instead of choosing (e.g., "TRIVIAL|SIMPLE|STANDARD")
|
|
103
|
+
if (!value.includes('|')) {
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const parts = value.split('|').map((p) => p.trim());
|
|
108
|
+
// Check if this looks like the enum list was copied verbatim
|
|
109
|
+
const matchCount = parts.filter((p) => enumValues.includes(p)).length;
|
|
110
|
+
if (matchCount < 2) {
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Model copied the format - pick the first valid option and warn
|
|
115
|
+
const firstValid = parts.find((p) => enumValues.includes(p));
|
|
116
|
+
if (!firstValid) {
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.warn(
|
|
121
|
+
`⚠️ Model copied enum format instead of choosing. Field "${key}" had "${rawValue}", using "${firstValid}"`
|
|
122
|
+
);
|
|
123
|
+
return firstValid;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function findEnumMatch(enumValues, value) {
|
|
127
|
+
return enumValues.find((entry) => entry.toUpperCase() === value);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function normalizeNestedValues(result, propSchema, key) {
|
|
131
|
+
// Recursively handle nested objects
|
|
132
|
+
if (propSchema.type === 'object' && propSchema.properties && result[key]) {
|
|
133
|
+
normalizeEnumValues(result[key], propSchema);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Handle arrays of objects
|
|
137
|
+
if (propSchema.type === 'array' && propSchema.items?.properties && Array.isArray(result[key])) {
|
|
138
|
+
for (const item of result[key]) {
|
|
139
|
+
normalizeEnumValues(item, propSchema.items);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
normalizeEnumValues,
|
|
146
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const PLATFORM_MISMATCH_REGEX =
|
|
2
|
+
/EBADPLATFORM|Unsupported platform|darwin-arm64|linux-x64|@esbuild\/linux-x64/i;
|
|
3
|
+
|
|
4
|
+
function isPlatformMismatchReason(reason) {
|
|
5
|
+
if (!reason) return false;
|
|
6
|
+
return PLATFORM_MISMATCH_REGEX.test(String(reason));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function findPlatformMismatchReason(result = {}) {
|
|
10
|
+
const criteriaResults = result.criteriaResults;
|
|
11
|
+
if (Array.isArray(criteriaResults)) {
|
|
12
|
+
for (const criteria of criteriaResults) {
|
|
13
|
+
if (criteria?.status !== 'CANNOT_VALIDATE') continue;
|
|
14
|
+
if (isPlatformMismatchReason(criteria.reason)) {
|
|
15
|
+
return String(criteria.reason);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const errors = result.errors;
|
|
21
|
+
if (Array.isArray(errors)) {
|
|
22
|
+
for (const error of errors) {
|
|
23
|
+
if (isPlatformMismatchReason(error)) {
|
|
24
|
+
return String(error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
isPlatformMismatchReason,
|
|
34
|
+
findPlatformMismatchReason,
|
|
35
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getArray,
|
|
3
|
+
getNumber,
|
|
4
|
+
getOptionalString,
|
|
5
|
+
getRecord,
|
|
6
|
+
getString,
|
|
7
|
+
isRecord,
|
|
8
|
+
stringifyJson,
|
|
9
|
+
tryParseJson,
|
|
10
|
+
} from '../json';
|
|
11
|
+
import type { OutputEvent, ProviderParseResult, ResultEvent } from '../types';
|
|
12
|
+
|
|
13
|
+
function cacheReadInputTokens(usage: Record<string, unknown>): number {
|
|
14
|
+
return (
|
|
15
|
+
getNumber(usage, 'cache_read_input_tokens') ?? getNumber(usage, 'cached_input_tokens') ?? 0
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseResultEvent(event: Record<string, unknown>): OutputEvent {
|
|
20
|
+
const usage = getRecord(event, 'usage') ?? {};
|
|
21
|
+
const result: ResultEvent = {
|
|
22
|
+
type: 'result',
|
|
23
|
+
success: getString(event, 'subtype') === 'success',
|
|
24
|
+
result: event.result,
|
|
25
|
+
error: event.is_error ? event.result : null,
|
|
26
|
+
cost: event.total_cost_usd,
|
|
27
|
+
duration: event.duration_ms,
|
|
28
|
+
inputTokens: getNumber(usage, 'input_tokens') ?? 0,
|
|
29
|
+
outputTokens: getNumber(usage, 'output_tokens') ?? 0,
|
|
30
|
+
cacheReadInputTokens: cacheReadInputTokens(usage),
|
|
31
|
+
cacheCreationInputTokens: getNumber(usage, 'cache_creation_input_tokens') ?? 0,
|
|
32
|
+
modelUsage: event.modelUsage ?? null,
|
|
33
|
+
};
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parseStreamEvent(inner: Record<string, unknown>): OutputEvent | null {
|
|
38
|
+
if (getString(inner, 'type') !== 'content_block_delta') return null;
|
|
39
|
+
const delta = getRecord(inner, 'delta');
|
|
40
|
+
if (delta === null) return null;
|
|
41
|
+
|
|
42
|
+
if (getString(delta, 'type') === 'text_delta') {
|
|
43
|
+
const text = getString(delta, 'text');
|
|
44
|
+
return text ? { type: 'text', text } : null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (getString(delta, 'type') === 'thinking_delta') {
|
|
48
|
+
const text = getString(delta, 'thinking');
|
|
49
|
+
return text ? { type: 'thinking', text } : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseAssistantBlock(block: Record<string, unknown>): OutputEvent | null {
|
|
56
|
+
const blockType = getString(block, 'type');
|
|
57
|
+
if (blockType === 'text') {
|
|
58
|
+
const text = getString(block, 'text');
|
|
59
|
+
return text ? { type: 'text', text } : null;
|
|
60
|
+
}
|
|
61
|
+
if (blockType === 'tool_use') {
|
|
62
|
+
return {
|
|
63
|
+
type: 'tool_call',
|
|
64
|
+
toolName: getOptionalString(block, 'name'),
|
|
65
|
+
toolId: getOptionalString(block, 'id'),
|
|
66
|
+
input: block.input,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (blockType === 'thinking') {
|
|
70
|
+
const text = getString(block, 'thinking');
|
|
71
|
+
return text ? { type: 'thinking', text } : null;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function parseEventList(events: readonly OutputEvent[]): ProviderParseResult {
|
|
77
|
+
if (events.length === 1) return events[0] ?? null;
|
|
78
|
+
if (events.length > 1) return events;
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function parseAssistantMessage(message: Record<string, unknown>): ProviderParseResult {
|
|
83
|
+
const results: OutputEvent[] = [];
|
|
84
|
+
for (const blockValue of getArray(message, 'content')) {
|
|
85
|
+
if (!isRecord(blockValue)) continue;
|
|
86
|
+
const event = parseAssistantBlock(blockValue);
|
|
87
|
+
if (event !== null) results.push(event);
|
|
88
|
+
}
|
|
89
|
+
return parseEventList(results);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseUserBlock(block: Record<string, unknown>): OutputEvent | null {
|
|
93
|
+
if (getString(block, 'type') !== 'tool_result') return null;
|
|
94
|
+
const content = block.content;
|
|
95
|
+
return {
|
|
96
|
+
type: 'tool_result',
|
|
97
|
+
toolId: getOptionalString(block, 'tool_use_id'),
|
|
98
|
+
content:
|
|
99
|
+
typeof content === 'string' || content === undefined ? content : stringifyJson(content),
|
|
100
|
+
isError: block.is_error || false,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function parseUserMessage(message: Record<string, unknown>): ProviderParseResult {
|
|
105
|
+
const results: OutputEvent[] = [];
|
|
106
|
+
for (const blockValue of getArray(message, 'content')) {
|
|
107
|
+
if (!isRecord(blockValue)) continue;
|
|
108
|
+
const event = parseUserBlock(blockValue);
|
|
109
|
+
if (event !== null) results.push(event);
|
|
110
|
+
}
|
|
111
|
+
return parseEventList(results);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function parseClaudeEvent(line: string): ProviderParseResult {
|
|
115
|
+
const parsed = tryParseJson(line.trim());
|
|
116
|
+
if (!isRecord(parsed)) return null;
|
|
117
|
+
|
|
118
|
+
const eventType = getString(parsed, 'type');
|
|
119
|
+
if (eventType === 'stream_event') {
|
|
120
|
+
const inner = getRecord(parsed, 'event');
|
|
121
|
+
return inner === null ? null : parseStreamEvent(inner);
|
|
122
|
+
}
|
|
123
|
+
if (eventType === 'assistant') {
|
|
124
|
+
const message = getRecord(parsed, 'message');
|
|
125
|
+
return message === null ? null : parseAssistantMessage(message);
|
|
126
|
+
}
|
|
127
|
+
if (eventType === 'user') {
|
|
128
|
+
const message = getRecord(parsed, 'message');
|
|
129
|
+
return message === null ? null : parseUserMessage(message);
|
|
130
|
+
}
|
|
131
|
+
if (eventType === 'result') return parseResultEvent(parsed);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { getString, isRecord, tryParseJson } from '../json';
|
|
5
|
+
|
|
6
|
+
export const STREAMING_MODE_ERROR = 'only prompt commands are supported in streaming mode';
|
|
7
|
+
export const NO_MESSAGES_RETURNED = 'No messages returned';
|
|
8
|
+
|
|
9
|
+
export interface StreamingModeError {
|
|
10
|
+
readonly sessionId: string;
|
|
11
|
+
readonly line: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface StructuredOutputRecovery {
|
|
15
|
+
readonly payload: Record<string, unknown>;
|
|
16
|
+
readonly sourcePath: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function detectProviderStreamingModeError(
|
|
20
|
+
provider: string,
|
|
21
|
+
line: unknown
|
|
22
|
+
): StreamingModeError | null {
|
|
23
|
+
if (!isClaudeProvider(provider)) return null;
|
|
24
|
+
return detectClaudeStreamingModeError(line);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function detectProviderFatalError(provider: string, line: unknown): string | null {
|
|
28
|
+
if (!isClaudeProvider(provider)) return null;
|
|
29
|
+
return detectClaudeFatalError(line);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function recoverProviderStructuredOutput(
|
|
33
|
+
provider: string,
|
|
34
|
+
sessionId: string
|
|
35
|
+
): StructuredOutputRecovery | null {
|
|
36
|
+
if (!isClaudeProvider(provider)) return null;
|
|
37
|
+
return recoverClaudeStructuredOutput(sessionId);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function supportsProviderStructuredOutputRecovery(provider: string): boolean {
|
|
41
|
+
return isClaudeProvider(provider);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isClaudeProvider(provider: string): boolean {
|
|
45
|
+
const normalized = provider.toLowerCase();
|
|
46
|
+
return normalized === 'claude' || normalized === 'anthropic';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function detectClaudeStreamingModeError(line: unknown): StreamingModeError | null {
|
|
50
|
+
const trimmed = typeof line === 'string' ? line.trim() : '';
|
|
51
|
+
if (!trimmed.startsWith('{')) return null;
|
|
52
|
+
|
|
53
|
+
const parsed = tryParseJson(trimmed);
|
|
54
|
+
if (
|
|
55
|
+
isRecord(parsed) &&
|
|
56
|
+
parsed.type === 'result' &&
|
|
57
|
+
parsed.is_error === true &&
|
|
58
|
+
Array.isArray(parsed.errors) &&
|
|
59
|
+
parsed.errors.includes(STREAMING_MODE_ERROR) &&
|
|
60
|
+
typeof parsed.session_id === 'string'
|
|
61
|
+
) {
|
|
62
|
+
return {
|
|
63
|
+
sessionId: parsed.session_id,
|
|
64
|
+
line: trimmed,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function detectClaudeFatalError(line: unknown): string | null {
|
|
72
|
+
if (typeof line !== 'string') return null;
|
|
73
|
+
const trimmed = line.trim();
|
|
74
|
+
if (!trimmed) return null;
|
|
75
|
+
|
|
76
|
+
if (trimmed.startsWith('{') && tryParseJson(trimmed) !== null) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (trimmed.toLowerCase().includes(NO_MESSAGES_RETURNED.toLowerCase())) {
|
|
81
|
+
return `Claude CLI error: ${NO_MESSAGES_RETURNED}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function findSessionJsonlPath(sessionId: string): string | null {
|
|
88
|
+
const claudeDir = process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude');
|
|
89
|
+
const projectsDir = join(claudeDir, 'projects');
|
|
90
|
+
if (!existsSync(projectsDir)) return null;
|
|
91
|
+
|
|
92
|
+
const target = `${sessionId}.jsonl`;
|
|
93
|
+
const queue: string[] = [projectsDir];
|
|
94
|
+
|
|
95
|
+
while (queue.length > 0) {
|
|
96
|
+
const dir = queue.pop();
|
|
97
|
+
if (dir === undefined) continue;
|
|
98
|
+
|
|
99
|
+
let entries;
|
|
100
|
+
try {
|
|
101
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
102
|
+
} catch {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
for (const entry of entries) {
|
|
107
|
+
if (entry.isFile() && entry.name === target) {
|
|
108
|
+
return join(dir, entry.name);
|
|
109
|
+
}
|
|
110
|
+
if (entry.isDirectory()) {
|
|
111
|
+
queue.push(join(dir, entry.name));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function recoverClaudeStructuredOutput(sessionId: string): StructuredOutputRecovery | null {
|
|
120
|
+
const jsonlPath = findSessionJsonlPath(sessionId);
|
|
121
|
+
if (jsonlPath === null) return null;
|
|
122
|
+
|
|
123
|
+
const fileContents = readJsonlFile(jsonlPath);
|
|
124
|
+
if (fileContents === null) return null;
|
|
125
|
+
|
|
126
|
+
const { structuredOutput, usage } = findStructuredOutput(fileContents);
|
|
127
|
+
if (structuredOutput === null) return null;
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
payload: buildStructuredOutputPayload(sessionId, structuredOutput, usage),
|
|
131
|
+
sourcePath: jsonlPath,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function readJsonlFile(jsonlPath: string): string | null {
|
|
136
|
+
try {
|
|
137
|
+
return readFileSync(jsonlPath, 'utf8');
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function findStructuredOutput(fileContents: string): {
|
|
144
|
+
readonly structuredOutput: Record<string, unknown> | null;
|
|
145
|
+
readonly usage: Record<string, unknown> | null;
|
|
146
|
+
} {
|
|
147
|
+
const lines = fileContents.split('\n');
|
|
148
|
+
let structuredOutput: Record<string, unknown> | null = null;
|
|
149
|
+
let usage: Record<string, unknown> | null = null;
|
|
150
|
+
|
|
151
|
+
for (const line of lines) {
|
|
152
|
+
const entry = tryParseJson(line);
|
|
153
|
+
if (!isRecord(entry)) continue;
|
|
154
|
+
|
|
155
|
+
const extracted = extractStructuredOutputFromEntry(entry);
|
|
156
|
+
if (extracted !== null) {
|
|
157
|
+
structuredOutput = extracted.structuredOutput;
|
|
158
|
+
usage = extracted.usage;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return { structuredOutput, usage };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function extractStructuredOutputFromEntry(entry: Record<string, unknown>): {
|
|
166
|
+
readonly structuredOutput: Record<string, unknown>;
|
|
167
|
+
readonly usage: Record<string, unknown> | null;
|
|
168
|
+
} | null {
|
|
169
|
+
const message = entry.message;
|
|
170
|
+
if (!isRecord(message) || !Array.isArray(message.content)) return null;
|
|
171
|
+
|
|
172
|
+
let structuredOutput: Record<string, unknown> | null = null;
|
|
173
|
+
let usage: Record<string, unknown> | null = null;
|
|
174
|
+
for (const block of message.content) {
|
|
175
|
+
if (
|
|
176
|
+
isRecord(block) &&
|
|
177
|
+
getString(block, 'type') === 'tool_use' &&
|
|
178
|
+
getString(block, 'name') === 'StructuredOutput' &&
|
|
179
|
+
isRecord(block.input)
|
|
180
|
+
) {
|
|
181
|
+
structuredOutput = block.input;
|
|
182
|
+
usage = isRecord(message.usage) ? message.usage : null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (structuredOutput === null) return null;
|
|
187
|
+
return { structuredOutput, usage };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function buildStructuredOutputPayload(
|
|
191
|
+
sessionId: string,
|
|
192
|
+
structuredOutput: Record<string, unknown>,
|
|
193
|
+
usage: Record<string, unknown> | null
|
|
194
|
+
): Record<string, unknown> {
|
|
195
|
+
return {
|
|
196
|
+
type: 'result',
|
|
197
|
+
subtype: 'success',
|
|
198
|
+
is_error: false,
|
|
199
|
+
structured_output: structuredOutput,
|
|
200
|
+
session_id: sessionId,
|
|
201
|
+
...(usage === null ? {} : { usage }),
|
|
202
|
+
};
|
|
203
|
+
}
|