@ttfw/envoi 1.0.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/README.md +238 -0
- package/dist/commands/app.d.ts +2 -0
- package/dist/commands/app.d.ts.map +1 -0
- package/dist/commands/app.js +31 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/autonomy.d.ts +6 -0
- package/dist/commands/autonomy.d.ts.map +1 -0
- package/dist/commands/autonomy.js +89 -0
- package/dist/commands/autonomy.js.map +1 -0
- package/dist/commands/builder.d.ts +13 -0
- package/dist/commands/builder.d.ts.map +1 -0
- package/dist/commands/builder.js +142 -0
- package/dist/commands/builder.js.map +1 -0
- package/dist/commands/idea.d.ts +12 -0
- package/dist/commands/idea.d.ts.map +1 -0
- package/dist/commands/idea.js +79 -0
- package/dist/commands/idea.js.map +1 -0
- package/dist/commands/init.d.ts +18 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +423 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mode.d.ts +13 -0
- package/dist/commands/mode.d.ts.map +1 -0
- package/dist/commands/mode.js +96 -0
- package/dist/commands/mode.js.map +1 -0
- package/dist/commands/onboard.d.ts +37 -0
- package/dist/commands/onboard.d.ts.map +1 -0
- package/dist/commands/onboard.js +743 -0
- package/dist/commands/onboard.js.map +1 -0
- package/dist/commands/pr-note.d.ts +8 -0
- package/dist/commands/pr-note.d.ts.map +1 -0
- package/dist/commands/pr-note.js +27 -0
- package/dist/commands/pr-note.js.map +1 -0
- package/dist/commands/undo.d.ts +7 -0
- package/dist/commands/undo.d.ts.map +1 -0
- package/dist/commands/undo.js +59 -0
- package/dist/commands/undo.js.map +1 -0
- package/dist/commands/update.d.ts +24 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +248 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/constants/report_codes.d.ts +29 -0
- package/dist/constants/report_codes.d.ts.map +1 -0
- package/dist/constants/report_codes.js +69 -0
- package/dist/constants/report_codes.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +675 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/autonomy.d.ts +16 -0
- package/dist/lib/autonomy.d.ts.map +1 -0
- package/dist/lib/autonomy.js +38 -0
- package/dist/lib/autonomy.js.map +1 -0
- package/dist/lib/blocked.d.ts +87 -0
- package/dist/lib/blocked.d.ts.map +1 -0
- package/dist/lib/blocked.js +134 -0
- package/dist/lib/blocked.js.map +1 -0
- package/dist/lib/branding.d.ts +13 -0
- package/dist/lib/branding.d.ts.map +1 -0
- package/dist/lib/branding.js +19 -0
- package/dist/lib/branding.js.map +1 -0
- package/dist/lib/claude.d.ts +42 -0
- package/dist/lib/claude.d.ts.map +1 -0
- package/dist/lib/claude.js +291 -0
- package/dist/lib/claude.js.map +1 -0
- package/dist/lib/config.d.ts +71 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +410 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/diff.d.ts +150 -0
- package/dist/lib/diff.d.ts.map +1 -0
- package/dist/lib/diff.js +257 -0
- package/dist/lib/diff.js.map +1 -0
- package/dist/lib/doctor.d.ts +67 -0
- package/dist/lib/doctor.d.ts.map +1 -0
- package/dist/lib/doctor.js +211 -0
- package/dist/lib/doctor.js.map +1 -0
- package/dist/lib/fingerprint.d.ts +27 -0
- package/dist/lib/fingerprint.d.ts.map +1 -0
- package/dist/lib/fingerprint.js +116 -0
- package/dist/lib/fingerprint.js.map +1 -0
- package/dist/lib/fs.d.ts +93 -0
- package/dist/lib/fs.d.ts.map +1 -0
- package/dist/lib/fs.js +179 -0
- package/dist/lib/fs.js.map +1 -0
- package/dist/lib/git.d.ts +177 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +355 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/git_branching.d.ts +84 -0
- package/dist/lib/git_branching.d.ts.map +1 -0
- package/dist/lib/git_branching.js +327 -0
- package/dist/lib/git_branching.js.map +1 -0
- package/dist/lib/gitignore.d.ts +26 -0
- package/dist/lib/gitignore.d.ts.map +1 -0
- package/dist/lib/gitignore.js +119 -0
- package/dist/lib/gitignore.js.map +1 -0
- package/dist/lib/guardrails.d.ts +232 -0
- package/dist/lib/guardrails.d.ts.map +1 -0
- package/dist/lib/guardrails.js +323 -0
- package/dist/lib/guardrails.js.map +1 -0
- package/dist/lib/history.d.ts +110 -0
- package/dist/lib/history.d.ts.map +1 -0
- package/dist/lib/history.js +236 -0
- package/dist/lib/history.js.map +1 -0
- package/dist/lib/index.d.ts +29 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +29 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/json-extract.d.ts +42 -0
- package/dist/lib/json-extract.d.ts.map +1 -0
- package/dist/lib/json-extract.js +201 -0
- package/dist/lib/json-extract.js.map +1 -0
- package/dist/lib/judge.d.ts +237 -0
- package/dist/lib/judge.d.ts.map +1 -0
- package/dist/lib/judge.js +501 -0
- package/dist/lib/judge.js.map +1 -0
- package/dist/lib/lock.d.ts +79 -0
- package/dist/lib/lock.d.ts.map +1 -0
- package/dist/lib/lock.js +254 -0
- package/dist/lib/lock.js.map +1 -0
- package/dist/lib/migration.d.ts +9 -0
- package/dist/lib/migration.d.ts.map +1 -0
- package/dist/lib/migration.js +74 -0
- package/dist/lib/migration.js.map +1 -0
- package/dist/lib/paths.d.ts +18 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +27 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/preflight.d.ts +33 -0
- package/dist/lib/preflight.d.ts.map +1 -0
- package/dist/lib/preflight.js +177 -0
- package/dist/lib/preflight.js.map +1 -0
- package/dist/lib/prompt_budget.d.ts +18 -0
- package/dist/lib/prompt_budget.d.ts.map +1 -0
- package/dist/lib/prompt_budget.js +36 -0
- package/dist/lib/prompt_budget.js.map +1 -0
- package/dist/lib/report.d.ts +102 -0
- package/dist/lib/report.d.ts.map +1 -0
- package/dist/lib/report.js +347 -0
- package/dist/lib/report.js.map +1 -0
- package/dist/lib/reviewer-flow.d.ts +80 -0
- package/dist/lib/reviewer-flow.d.ts.map +1 -0
- package/dist/lib/reviewer-flow.js +138 -0
- package/dist/lib/reviewer-flow.js.map +1 -0
- package/dist/lib/reviewer.d.ts +53 -0
- package/dist/lib/reviewer.d.ts.map +1 -0
- package/dist/lib/reviewer.js +199 -0
- package/dist/lib/reviewer.js.map +1 -0
- package/dist/lib/risk.d.ts +127 -0
- package/dist/lib/risk.d.ts.map +1 -0
- package/dist/lib/risk.js +192 -0
- package/dist/lib/risk.js.map +1 -0
- package/dist/lib/rollback.d.ts +143 -0
- package/dist/lib/rollback.d.ts.map +1 -0
- package/dist/lib/rollback.js +244 -0
- package/dist/lib/rollback.js.map +1 -0
- package/dist/lib/schema.d.ts +47 -0
- package/dist/lib/schema.d.ts.map +1 -0
- package/dist/lib/schema.js +91 -0
- package/dist/lib/schema.js.map +1 -0
- package/dist/lib/scope.d.ts +89 -0
- package/dist/lib/scope.d.ts.map +1 -0
- package/dist/lib/scope.js +135 -0
- package/dist/lib/scope.js.map +1 -0
- package/dist/lib/self_update.d.ts +13 -0
- package/dist/lib/self_update.d.ts.map +1 -0
- package/dist/lib/self_update.js +172 -0
- package/dist/lib/self_update.js.map +1 -0
- package/dist/lib/state.d.ts +143 -0
- package/dist/lib/state.d.ts.map +1 -0
- package/dist/lib/state.js +258 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/lib/tick.d.ts +310 -0
- package/dist/lib/tick.d.ts.map +1 -0
- package/dist/lib/tick.js +424 -0
- package/dist/lib/tick.js.map +1 -0
- package/dist/lib/transport.d.ts +145 -0
- package/dist/lib/transport.d.ts.map +1 -0
- package/dist/lib/transport.js +237 -0
- package/dist/lib/transport.js.map +1 -0
- package/dist/lib/verdict_labels.d.ts +5 -0
- package/dist/lib/verdict_labels.d.ts.map +1 -0
- package/dist/lib/verdict_labels.js +25 -0
- package/dist/lib/verdict_labels.js.map +1 -0
- package/dist/lib/verify-safety.d.ts +63 -0
- package/dist/lib/verify-safety.d.ts.map +1 -0
- package/dist/lib/verify-safety.js +123 -0
- package/dist/lib/verify-safety.js.map +1 -0
- package/dist/lib/verify.d.ts +139 -0
- package/dist/lib/verify.d.ts.map +1 -0
- package/dist/lib/verify.js +311 -0
- package/dist/lib/verify.js.map +1 -0
- package/dist/lib/workspace_state.d.ts +79 -0
- package/dist/lib/workspace_state.d.ts.map +1 -0
- package/dist/lib/workspace_state.js +283 -0
- package/dist/lib/workspace_state.js.map +1 -0
- package/dist/runner/builder.d.ts +58 -0
- package/dist/runner/builder.d.ts.map +1 -0
- package/dist/runner/builder.js +775 -0
- package/dist/runner/builder.js.map +1 -0
- package/dist/runner/builder_parse.d.ts +37 -0
- package/dist/runner/builder_parse.d.ts.map +1 -0
- package/dist/runner/builder_parse.js +76 -0
- package/dist/runner/builder_parse.js.map +1 -0
- package/dist/runner/index.d.ts +9 -0
- package/dist/runner/index.d.ts.map +1 -0
- package/dist/runner/index.js +7 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/runner/loop.d.ts +51 -0
- package/dist/runner/loop.d.ts.map +1 -0
- package/dist/runner/loop.js +221 -0
- package/dist/runner/loop.js.map +1 -0
- package/dist/runner/orchestrator.d.ts +67 -0
- package/dist/runner/orchestrator.d.ts.map +1 -0
- package/dist/runner/orchestrator.js +376 -0
- package/dist/runner/orchestrator.js.map +1 -0
- package/dist/runner/tick.d.ts +10 -0
- package/dist/runner/tick.d.ts.map +1 -0
- package/dist/runner/tick.js +1639 -0
- package/dist/runner/tick.js.map +1 -0
- package/dist/types/blocked.d.ts +52 -0
- package/dist/types/blocked.d.ts.map +1 -0
- package/dist/types/blocked.js +8 -0
- package/dist/types/blocked.js.map +1 -0
- package/dist/types/builder.d.ts +25 -0
- package/dist/types/builder.d.ts.map +1 -0
- package/dist/types/builder.js +7 -0
- package/dist/types/builder.js.map +1 -0
- package/dist/types/claude.d.ts +86 -0
- package/dist/types/claude.d.ts.map +1 -0
- package/dist/types/claude.js +48 -0
- package/dist/types/claude.js.map +1 -0
- package/dist/types/config.d.ts +384 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +7 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/lock.d.ts +21 -0
- package/dist/types/lock.d.ts.map +1 -0
- package/dist/types/lock.js +8 -0
- package/dist/types/lock.js.map +1 -0
- package/dist/types/preflight.d.ts +49 -0
- package/dist/types/preflight.d.ts.map +1 -0
- package/dist/types/preflight.js +8 -0
- package/dist/types/preflight.js.map +1 -0
- package/dist/types/report.d.ts +161 -0
- package/dist/types/report.d.ts.map +1 -0
- package/dist/types/report.js +8 -0
- package/dist/types/report.js.map +1 -0
- package/dist/types/reviewer.d.ts +66 -0
- package/dist/types/reviewer.d.ts.map +1 -0
- package/dist/types/reviewer.js +5 -0
- package/dist/types/reviewer.js.map +1 -0
- package/dist/types/state.d.ts +124 -0
- package/dist/types/state.d.ts.map +1 -0
- package/dist/types/state.js +20 -0
- package/dist/types/state.js.map +1 -0
- package/dist/types/task.d.ts +117 -0
- package/dist/types/task.d.ts.map +1 -0
- package/dist/types/task.js +7 -0
- package/dist/types/task.js.map +1 -0
- package/dist/types/workspace_state.d.ts +125 -0
- package/dist/types/workspace_state.d.ts.map +1 -0
- package/dist/types/workspace_state.js +10 -0
- package/dist/types/workspace_state.js.map +1 -0
- package/envoi.config.json +191 -0
- package/package.json +52 -0
- package/relais/prompts/.gitkeep +0 -0
- package/relais/prompts/builder.system.txt +13 -0
- package/relais/prompts/builder.user.txt +15 -0
- package/relais/prompts/orchestrator.system.txt +37 -0
- package/relais/prompts/orchestrator.user.txt +34 -0
- package/relais/prompts/reviewer.system.txt +33 -0
- package/relais/prompts/reviewer.user.txt +35 -0
- package/relais/schemas/.gitkeep +0 -0
- package/relais/schemas/builder_result.schema.json +29 -0
- package/relais/schemas/report.schema.json +195 -0
- package/relais/schemas/reviewer_result.schema.json +70 -0
- package/relais/schemas/task.schema.json +155 -0
package/dist/lib/tick.js
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tick runner with transport stall handling and retry policy.
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to handle transport stalls during ORCHESTRATE or BUILD phases.
|
|
5
|
+
* When a stall is detected:
|
|
6
|
+
* 1. Check if repo is dirty
|
|
7
|
+
* 2. Rollback if needed
|
|
8
|
+
* 3. Return BLOCKED_TRANSPORT_STALLED with evidence
|
|
9
|
+
*
|
|
10
|
+
* Retry policy (M21):
|
|
11
|
+
* - Attempt 1: Retry same task unchanged
|
|
12
|
+
* - Attempt 2: Retry with degraded settings
|
|
13
|
+
* - Attempt 3+: Block and require human action
|
|
14
|
+
*/
|
|
15
|
+
import { rollbackToCommit } from './rollback.js';
|
|
16
|
+
import { isWorktreeClean } from './git.js';
|
|
17
|
+
import { resetRetryState, recordTransportStall } from './state.js';
|
|
18
|
+
/**
|
|
19
|
+
* Maximum retry attempts before blocking.
|
|
20
|
+
*/
|
|
21
|
+
export const MAX_RETRY_ATTEMPTS = 3;
|
|
22
|
+
/**
|
|
23
|
+
* Recovery prompt to prepend when retrying after a transport stall.
|
|
24
|
+
*
|
|
25
|
+
* This prompt reminds the model that previous work may have been lost
|
|
26
|
+
* and to read files before making assumptions about their state.
|
|
27
|
+
*/
|
|
28
|
+
export const RECOVERY_PROMPT = `IMPORTANT: This is a retry after a transport stall. The previous attempt may have been interrupted.
|
|
29
|
+
|
|
30
|
+
Do NOT assume your previous edits were applied. The connection may have stalled:
|
|
31
|
+
- Before any changes were made
|
|
32
|
+
- In the middle of changes (partial writes)
|
|
33
|
+
- After changes were made but before confirmation
|
|
34
|
+
|
|
35
|
+
BEFORE making any edits:
|
|
36
|
+
1. Read the relevant files to verify their current state
|
|
37
|
+
2. Check if your intended changes already exist
|
|
38
|
+
3. Proceed based on the actual file contents, not your memory of what you tried to do
|
|
39
|
+
|
|
40
|
+
If the task appears completed, verify and report success. If not, complete it from the current state.`;
|
|
41
|
+
/**
|
|
42
|
+
* Returns the recovery prompt to prepend when retrying.
|
|
43
|
+
*
|
|
44
|
+
* The recovery prompt is used on any retry (retry_unchanged or retry_degraded)
|
|
45
|
+
* to remind the model that previous work may have been lost.
|
|
46
|
+
*
|
|
47
|
+
* @param retryCount - Current retry count (0 = first attempt, no recovery prompt)
|
|
48
|
+
* @returns Recovery prompt string if retrying, empty string for first attempt
|
|
49
|
+
*/
|
|
50
|
+
export function getRecoveryPrompt(retryCount) {
|
|
51
|
+
if (retryCount < 1) {
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
return RECOVERY_PROMPT;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Builds the full prompt with optional recovery prefix.
|
|
58
|
+
*
|
|
59
|
+
* If retrying after a stall, prepends the recovery prompt to the original prompt.
|
|
60
|
+
*
|
|
61
|
+
* @param originalPrompt - The original prompt content
|
|
62
|
+
* @param retryCount - Current retry count (0 = first attempt)
|
|
63
|
+
* @returns Full prompt with recovery prefix if applicable
|
|
64
|
+
*/
|
|
65
|
+
export function buildPromptWithRecovery(originalPrompt, retryCount) {
|
|
66
|
+
const recovery = getRecoveryPrompt(retryCount);
|
|
67
|
+
if (!recovery) {
|
|
68
|
+
return originalPrompt;
|
|
69
|
+
}
|
|
70
|
+
return `${recovery}\n\n---\n\n${originalPrompt}`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Computes the retry action based on the current retry count.
|
|
74
|
+
*
|
|
75
|
+
* Retry policy:
|
|
76
|
+
* - retry_count 0 or 1 → retry_unchanged (first failure, try again)
|
|
77
|
+
* - retry_count 2 → retry_degraded (second failure, use safer settings)
|
|
78
|
+
* - retry_count >= 3 → block (third failure, require human)
|
|
79
|
+
*
|
|
80
|
+
* @param retryCount - Current retry count from state (before this attempt)
|
|
81
|
+
* @returns The action to take
|
|
82
|
+
*/
|
|
83
|
+
export function getRetryAction(retryCount) {
|
|
84
|
+
if (retryCount < 1) {
|
|
85
|
+
return 'retry_unchanged';
|
|
86
|
+
}
|
|
87
|
+
else if (retryCount < 2) {
|
|
88
|
+
return 'retry_degraded';
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
return 'block';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Computes the full retry decision including degraded settings if needed.
|
|
96
|
+
*
|
|
97
|
+
* @param retryCount - Current retry count from state
|
|
98
|
+
* @param originalMaxTurns - Original max_turns from config (default 50)
|
|
99
|
+
* @param originalDiffLimits - Original diff limits from config
|
|
100
|
+
* @returns Full retry decision with settings and reason
|
|
101
|
+
*/
|
|
102
|
+
export function computeRetryDecision(retryCount, originalMaxTurns = 50, originalDiffLimits = { max_files_touched: 20, max_lines_changed: 500 }) {
|
|
103
|
+
const action = getRetryAction(retryCount);
|
|
104
|
+
const newRetryCount = retryCount + 1;
|
|
105
|
+
if (action === 'retry_unchanged') {
|
|
106
|
+
return {
|
|
107
|
+
action,
|
|
108
|
+
retry_count: newRetryCount,
|
|
109
|
+
reason: `Retry attempt ${newRetryCount}/${MAX_RETRY_ATTEMPTS}: retrying unchanged`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (action === 'retry_degraded') {
|
|
113
|
+
const degraded_settings = computeDegradedSettings(originalMaxTurns, originalDiffLimits);
|
|
114
|
+
return {
|
|
115
|
+
action,
|
|
116
|
+
retry_count: newRetryCount,
|
|
117
|
+
degraded_settings,
|
|
118
|
+
reason: `Retry attempt ${newRetryCount}/${MAX_RETRY_ATTEMPTS}: retrying with degraded settings (max_turns=${degraded_settings.max_turns}, max_files=${degraded_settings.diff_limits.max_files_touched}, max_lines=${degraded_settings.diff_limits.max_lines_changed})`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// action === 'block'
|
|
122
|
+
return {
|
|
123
|
+
action,
|
|
124
|
+
retry_count: newRetryCount,
|
|
125
|
+
reason: `Retry limit reached (${MAX_RETRY_ATTEMPTS} attempts). Blocking for human intervention.`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Computes degraded settings for retry attempt 2.
|
|
130
|
+
*
|
|
131
|
+
* Applies conservative reductions:
|
|
132
|
+
* - max_turns: 50% of original, minimum 5
|
|
133
|
+
* - max_files_touched: 50% of original, minimum 5
|
|
134
|
+
* - max_lines_changed: 50% of original, minimum 100
|
|
135
|
+
* - prefer_patch_mode: true
|
|
136
|
+
*
|
|
137
|
+
* @param originalMaxTurns - Original max_turns from config
|
|
138
|
+
* @param originalDiffLimits - Original diff limits from config
|
|
139
|
+
* @returns Degraded settings
|
|
140
|
+
*/
|
|
141
|
+
export function computeDegradedSettings(originalMaxTurns, originalDiffLimits) {
|
|
142
|
+
return {
|
|
143
|
+
max_turns: Math.max(5, Math.floor(originalMaxTurns / 2)),
|
|
144
|
+
diff_limits: {
|
|
145
|
+
max_files_touched: Math.max(5, Math.floor(originalDiffLimits.max_files_touched / 2)),
|
|
146
|
+
max_lines_changed: Math.max(100, Math.floor(originalDiffLimits.max_lines_changed / 2)),
|
|
147
|
+
},
|
|
148
|
+
prefer_patch_mode: true,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Checks if retry is allowed based on current retry count.
|
|
153
|
+
*
|
|
154
|
+
* @param retryCount - Current retry count
|
|
155
|
+
* @returns true if retry is allowed, false if blocked
|
|
156
|
+
*/
|
|
157
|
+
export function canRetry(retryCount) {
|
|
158
|
+
return retryCount < MAX_RETRY_ATTEMPTS - 1;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Formats retry decision as human-readable message.
|
|
162
|
+
*
|
|
163
|
+
* @param decision - The retry decision
|
|
164
|
+
* @returns Formatted message
|
|
165
|
+
*/
|
|
166
|
+
export function formatRetryDecision(decision) {
|
|
167
|
+
const lines = [decision.reason];
|
|
168
|
+
if (decision.degraded_settings) {
|
|
169
|
+
lines.push('Degraded settings:');
|
|
170
|
+
lines.push(` - max_turns: ${decision.degraded_settings.max_turns}`);
|
|
171
|
+
lines.push(` - max_files: ${decision.degraded_settings.diff_limits.max_files_touched}`);
|
|
172
|
+
lines.push(` - max_lines: ${decision.degraded_settings.diff_limits.max_lines_changed}`);
|
|
173
|
+
lines.push(` - patch_mode: ${decision.degraded_settings.prefer_patch_mode}`);
|
|
174
|
+
}
|
|
175
|
+
if (decision.action === 'block') {
|
|
176
|
+
lines.push('');
|
|
177
|
+
lines.push('Human action required: Review logs, fix issues, then reset retry state.');
|
|
178
|
+
}
|
|
179
|
+
return lines.join('\n');
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Applies degraded settings to a EnvoiConfig.
|
|
183
|
+
*
|
|
184
|
+
* Used during retry attempt 2 to run with more conservative settings.
|
|
185
|
+
* Creates a new config object without mutating the original.
|
|
186
|
+
*
|
|
187
|
+
* Settings applied:
|
|
188
|
+
* - builder.claude_code.max_turns: reduced to degraded value
|
|
189
|
+
* - diff_limits.default_max_files_touched: reduced to degraded value
|
|
190
|
+
* - diff_limits.default_max_lines_changed: reduced to degraded value
|
|
191
|
+
* - builder.default_mode: set to 'patch' if allow_patch_mode is true and prefer_patch_mode is set
|
|
192
|
+
*
|
|
193
|
+
* @param config - Original EnvoiConfig
|
|
194
|
+
* @param degraded - Degraded settings to apply
|
|
195
|
+
* @returns New config with degraded settings applied
|
|
196
|
+
*/
|
|
197
|
+
export function applyDegradedConfig(config, degraded) {
|
|
198
|
+
return {
|
|
199
|
+
...config,
|
|
200
|
+
builder: {
|
|
201
|
+
...config.builder,
|
|
202
|
+
// Prefer patch mode if available and degraded settings request it
|
|
203
|
+
default_mode: degraded.prefer_patch_mode && config.builder.allow_patch_mode
|
|
204
|
+
? 'patch'
|
|
205
|
+
: config.builder.default_mode,
|
|
206
|
+
claude_code: {
|
|
207
|
+
...config.builder.claude_code,
|
|
208
|
+
max_turns: degraded.max_turns,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
diff_limits: {
|
|
212
|
+
...config.diff_limits,
|
|
213
|
+
default_max_files_touched: degraded.diff_limits.max_files_touched,
|
|
214
|
+
default_max_lines_changed: degraded.diff_limits.max_lines_changed,
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Extracts relevant settings from config for computing degraded settings.
|
|
220
|
+
*
|
|
221
|
+
* @param config - EnvoiConfig to extract from
|
|
222
|
+
* @returns Object with max_turns and diff_limits for degradation computation
|
|
223
|
+
*/
|
|
224
|
+
export function extractDegradationInputs(config) {
|
|
225
|
+
return {
|
|
226
|
+
max_turns: config.builder.claude_code.max_turns,
|
|
227
|
+
diff_limits: {
|
|
228
|
+
max_files_touched: config.diff_limits.default_max_files_touched,
|
|
229
|
+
max_lines_changed: config.diff_limits.default_max_lines_changed,
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Convenience function to degrade a config based on retry count.
|
|
235
|
+
*
|
|
236
|
+
* If retry action is 'retry_degraded', applies degraded settings to config.
|
|
237
|
+
* Otherwise, returns the original config unchanged.
|
|
238
|
+
*
|
|
239
|
+
* @param config - Original config
|
|
240
|
+
* @param retryCount - Current retry count
|
|
241
|
+
* @returns Degraded config if appropriate, original config otherwise
|
|
242
|
+
*/
|
|
243
|
+
export function getDegradedConfigIfNeeded(config, retryCount) {
|
|
244
|
+
const action = getRetryAction(retryCount);
|
|
245
|
+
if (action !== 'retry_degraded') {
|
|
246
|
+
return config;
|
|
247
|
+
}
|
|
248
|
+
const inputs = extractDegradationInputs(config);
|
|
249
|
+
const degraded = computeDegradedSettings(inputs.max_turns, inputs.diff_limits);
|
|
250
|
+
return applyDegradedConfig(config, degraded);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Determines if retry state should be reset based on tick outcome.
|
|
254
|
+
*
|
|
255
|
+
* Retry state is reset on:
|
|
256
|
+
* - SUCCESS: Task completed successfully
|
|
257
|
+
* - STOP: Task failed with a STOP code (not a transport issue)
|
|
258
|
+
*
|
|
259
|
+
* Retry state is NOT reset on:
|
|
260
|
+
* - BLOCKED_STALL: Transport stall, may retry
|
|
261
|
+
* - BLOCKED_OTHER: Other blocked conditions
|
|
262
|
+
*
|
|
263
|
+
* @param outcome - The tick outcome
|
|
264
|
+
* @returns true if retry state should be reset
|
|
265
|
+
*/
|
|
266
|
+
export function shouldResetRetryState(outcome) {
|
|
267
|
+
return outcome === 'SUCCESS' || outcome === 'STOP';
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Updates tick state based on completion outcome.
|
|
271
|
+
*
|
|
272
|
+
* On SUCCESS or STOP:
|
|
273
|
+
* - Resets retry_count to 0
|
|
274
|
+
* - Clears last_error_kind and last_request_id
|
|
275
|
+
*
|
|
276
|
+
* On BLOCKED_STALL:
|
|
277
|
+
* - Records the stall (increments retry_count, sets error fields)
|
|
278
|
+
*
|
|
279
|
+
* On BLOCKED_OTHER:
|
|
280
|
+
* - No changes to retry state
|
|
281
|
+
*
|
|
282
|
+
* @param state - Current tick state
|
|
283
|
+
* @param outcome - The tick outcome
|
|
284
|
+
* @param stallInfo - Stall info if outcome is BLOCKED_STALL
|
|
285
|
+
* @returns Updated tick state
|
|
286
|
+
*/
|
|
287
|
+
export function updateStateForOutcome(state, outcome, stallInfo) {
|
|
288
|
+
if (shouldResetRetryState(outcome)) {
|
|
289
|
+
return resetRetryState(state);
|
|
290
|
+
}
|
|
291
|
+
if (outcome === 'BLOCKED_STALL' && stallInfo) {
|
|
292
|
+
return recordTransportStall(state, stallInfo.errorKind, stallInfo.requestId);
|
|
293
|
+
}
|
|
294
|
+
// BLOCKED_OTHER - no changes
|
|
295
|
+
return state;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Convenience function to handle successful tick completion.
|
|
299
|
+
*
|
|
300
|
+
* Resets all retry state fields to clear recovery mode.
|
|
301
|
+
* Call this when a tick completes with SUCCESS verdict.
|
|
302
|
+
*
|
|
303
|
+
* @param state - Current tick state
|
|
304
|
+
* @returns Updated state with retry fields cleared
|
|
305
|
+
*/
|
|
306
|
+
export function handleTickSuccess(state) {
|
|
307
|
+
return resetRetryState(state);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Convenience function to handle tick failure with STOP code.
|
|
311
|
+
*
|
|
312
|
+
* Resets retry state because STOP is a definitive failure,
|
|
313
|
+
* not a transient transport issue that might benefit from retry.
|
|
314
|
+
*
|
|
315
|
+
* @param state - Current tick state
|
|
316
|
+
* @returns Updated state with retry fields cleared
|
|
317
|
+
*/
|
|
318
|
+
export function handleTickStop(state) {
|
|
319
|
+
return resetRetryState(state);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Convenience function to handle transport stall.
|
|
323
|
+
*
|
|
324
|
+
* Increments retry_count and records error information.
|
|
325
|
+
* Call this when a tick is blocked due to transport stall.
|
|
326
|
+
*
|
|
327
|
+
* @param state - Current tick state
|
|
328
|
+
* @param requestId - Request ID from the stall (for debugging)
|
|
329
|
+
* @returns Updated state with incremented retry count
|
|
330
|
+
*/
|
|
331
|
+
export function handleTickStall(state, requestId) {
|
|
332
|
+
return recordTransportStall(state, 'transport_stalled', requestId);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Handles a transport stall during tick execution.
|
|
336
|
+
*
|
|
337
|
+
* When a stall is detected:
|
|
338
|
+
* 1. Checks if the git worktree is dirty
|
|
339
|
+
* 2. If dirty, rolls back to the base commit
|
|
340
|
+
* 3. Returns a BLOCKED result with stall evidence
|
|
341
|
+
*
|
|
342
|
+
* @param stallError - The structured stall error
|
|
343
|
+
* @param baseCommit - The commit to rollback to if needed
|
|
344
|
+
* @param options - Optional handling options
|
|
345
|
+
* @returns StallHandlingResult with rollback status and stall evidence
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* ```typescript
|
|
349
|
+
* const result = await invokeWithStallDetection(config, invocation, 'BUILD');
|
|
350
|
+
* if (!result.ok) {
|
|
351
|
+
* const stallResult = await handleTransportStall(result.error, baseCommit);
|
|
352
|
+
* // stallResult.blockedCode === 'BLOCKED_TRANSPORT_STALLED'
|
|
353
|
+
* }
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
export async function handleTransportStall(stallError, baseCommit, options = {}) {
|
|
357
|
+
// Check if worktree is dirty
|
|
358
|
+
const cleanCheck = isWorktreeClean();
|
|
359
|
+
const wasDirty = !cleanCheck;
|
|
360
|
+
let rollbackPerformed = false;
|
|
361
|
+
let rollbackResult = null;
|
|
362
|
+
// If dirty and rollback not skipped, perform rollback
|
|
363
|
+
if (wasDirty && !options.skipRollback) {
|
|
364
|
+
rollbackResult = rollbackToCommit(baseCommit);
|
|
365
|
+
rollbackPerformed = rollbackResult.ok;
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
status: 'BLOCKED',
|
|
369
|
+
blockedCode: 'BLOCKED_TRANSPORT_STALLED',
|
|
370
|
+
stage: stallError.stage,
|
|
371
|
+
requestId: stallError.request_id,
|
|
372
|
+
rawError: stallError.raw_error,
|
|
373
|
+
rollbackPerformed,
|
|
374
|
+
rollbackResult,
|
|
375
|
+
wasDirty,
|
|
376
|
+
baseCommit,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Checks if a stall occurred and handles it if so.
|
|
381
|
+
*
|
|
382
|
+
* This is a convenience wrapper that combines stall detection with handling.
|
|
383
|
+
*
|
|
384
|
+
* @param error - Any error that might be a stall
|
|
385
|
+
* @param stage - The stage where the error occurred
|
|
386
|
+
* @param baseCommit - The commit to rollback to if needed
|
|
387
|
+
* @param options - Optional handling options
|
|
388
|
+
* @returns StallHandlingResult if stall detected, null otherwise
|
|
389
|
+
*/
|
|
390
|
+
export async function checkAndHandleStall(stallError, baseCommit, options = {}) {
|
|
391
|
+
if (!stallError) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return handleTransportStall(stallError, baseCommit, options);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Creates a human-readable message for a stall handling result.
|
|
398
|
+
*
|
|
399
|
+
* @param result - The stall handling result
|
|
400
|
+
* @returns Formatted message string
|
|
401
|
+
*/
|
|
402
|
+
export function formatStallResult(result) {
|
|
403
|
+
const lines = [
|
|
404
|
+
`Transport stall detected during ${result.stage}`,
|
|
405
|
+
`Status: ${result.blockedCode}`,
|
|
406
|
+
];
|
|
407
|
+
if (result.requestId) {
|
|
408
|
+
lines.push(`Request ID: ${result.requestId}`);
|
|
409
|
+
}
|
|
410
|
+
if (result.wasDirty) {
|
|
411
|
+
if (result.rollbackPerformed) {
|
|
412
|
+
lines.push(`Rollback: performed to ${result.baseCommit.substring(0, 7)}`);
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
lines.push(`Rollback: skipped (repo was dirty)`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
lines.push(`Rollback: not needed (repo was clean)`);
|
|
420
|
+
}
|
|
421
|
+
lines.push(`Error: ${result.rawError.substring(0, 100)}${result.rawError.length > 100 ? '...' : ''}`);
|
|
422
|
+
return lines.join('\n');
|
|
423
|
+
}
|
|
424
|
+
//# sourceMappingURL=tick.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tick.js","sourceRoot":"","sources":["../../src/lib/tick.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport layer utilities for Claude invocations.
|
|
3
|
+
*
|
|
4
|
+
* Provides stall detection and timeout wrapper for reliable
|
|
5
|
+
* handling of connection issues, timeouts, and transport failures.
|
|
6
|
+
*/
|
|
7
|
+
import type { ClaudeCodeCliConfig } from '../types/config.js';
|
|
8
|
+
import type { ClaudeInvocation, ClaudeResponse } from '../types/claude.js';
|
|
9
|
+
import type { TransportStallError, TransportStallStage } from '../types/preflight.js';
|
|
10
|
+
/**
|
|
11
|
+
* Result of stall pattern detection.
|
|
12
|
+
*/
|
|
13
|
+
export interface StallDetectionResult {
|
|
14
|
+
/** Whether a stall pattern was detected */
|
|
15
|
+
stalled: boolean;
|
|
16
|
+
/** Request ID if found in error output */
|
|
17
|
+
request_id: string | null;
|
|
18
|
+
/** Which pattern was matched, if any */
|
|
19
|
+
matched_pattern: string | null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Detects transport stall patterns in error output.
|
|
23
|
+
*
|
|
24
|
+
* Checks for known patterns that indicate connection issues:
|
|
25
|
+
* - "Connection stalled" - Cursor/CLI connection issue
|
|
26
|
+
* - "streamFromAgentBackend" - Backend stream failure
|
|
27
|
+
* - "ECONNRESET" - Connection reset by peer
|
|
28
|
+
* - "ETIMEDOUT" - Connection timeout
|
|
29
|
+
* - "socket hang up" - Socket disconnection
|
|
30
|
+
*
|
|
31
|
+
* Also extracts Request ID if present for debugging.
|
|
32
|
+
*
|
|
33
|
+
* @param error - Error message or stderr output to analyze
|
|
34
|
+
* @returns Detection result with stalled flag and optional request_id
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const result = isTransportStall('Connection stalled. Request ID: abc123');
|
|
39
|
+
* // result.stalled === true
|
|
40
|
+
* // result.request_id === 'abc123'
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function isTransportStall(error: string): StallDetectionResult;
|
|
44
|
+
/**
|
|
45
|
+
* Creates a TransportStallError from error information.
|
|
46
|
+
*
|
|
47
|
+
* @param stage - The stage where the stall occurred (ORCHESTRATE or BUILD)
|
|
48
|
+
* @param rawError - The raw error message
|
|
49
|
+
* @param requestId - Optional request ID extracted from error
|
|
50
|
+
* @returns Structured TransportStallError
|
|
51
|
+
*/
|
|
52
|
+
export declare function createTransportStallError(stage: TransportStallStage, rawError: string, requestId?: string | null): TransportStallError;
|
|
53
|
+
/**
|
|
54
|
+
* Result type for invokeWithStallDetection.
|
|
55
|
+
* Either a successful ClaudeResponse or a TransportStallError.
|
|
56
|
+
*/
|
|
57
|
+
export type InvokeResult = {
|
|
58
|
+
ok: true;
|
|
59
|
+
response: ClaudeResponse;
|
|
60
|
+
} | {
|
|
61
|
+
ok: false;
|
|
62
|
+
error: TransportStallError;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Invokes Claude Code CLI with stall detection.
|
|
66
|
+
*
|
|
67
|
+
* Wraps the standard invokeClaudeCode function and:
|
|
68
|
+
* 1. Catches timeout errors and converts to TransportStallError
|
|
69
|
+
* 2. Detects stall patterns in error output
|
|
70
|
+
* 3. Returns structured error for transport failures
|
|
71
|
+
*
|
|
72
|
+
* @param config - Claude Code CLI configuration
|
|
73
|
+
* @param invocation - Invocation parameters
|
|
74
|
+
* @param stage - The stage (ORCHESTRATE or BUILD) for error context
|
|
75
|
+
* @returns InvokeResult with either response or stall error
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const result = await invokeWithStallDetection(config, invocation, 'BUILD');
|
|
80
|
+
* if (result.ok) {
|
|
81
|
+
* console.log('Success:', result.response.result);
|
|
82
|
+
* } else {
|
|
83
|
+
* console.log('Stall:', result.error.raw_error);
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare function invokeWithStallDetection(config: ClaudeCodeCliConfig, invocation: ClaudeInvocation, stage: TransportStallStage): Promise<InvokeResult>;
|
|
88
|
+
/**
|
|
89
|
+
* Result of normalizing an error for transport stall detection.
|
|
90
|
+
*/
|
|
91
|
+
export interface NormalizedError {
|
|
92
|
+
/** Whether this error represents a transport stall */
|
|
93
|
+
isStall: boolean;
|
|
94
|
+
/** Structured stall error if isStall is true */
|
|
95
|
+
stallError: TransportStallError | null;
|
|
96
|
+
/** The original error wrapped as Error */
|
|
97
|
+
originalError: Error;
|
|
98
|
+
/** Raw error message extracted from the error */
|
|
99
|
+
message: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Normalizes any error type into a consistent format for stall detection.
|
|
103
|
+
*
|
|
104
|
+
* Handles:
|
|
105
|
+
* - ClaudeError: extracts message + stderr, checks for timeout
|
|
106
|
+
* - Error: extracts message
|
|
107
|
+
* - string: wraps in Error
|
|
108
|
+
* - unknown: converts to string and wraps in Error
|
|
109
|
+
*
|
|
110
|
+
* @param error - Any error type (ClaudeError, Error, string, unknown)
|
|
111
|
+
* @param stage - The stage where the error occurred (ORCHESTRATE or BUILD)
|
|
112
|
+
* @returns Normalized error with stall detection result
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* try {
|
|
117
|
+
* await invokeClaudeCode(config, invocation);
|
|
118
|
+
* } catch (error) {
|
|
119
|
+
* const normalized = normalizeTransportError(error, 'BUILD');
|
|
120
|
+
* if (normalized.isStall) {
|
|
121
|
+
* handleStall(normalized.stallError);
|
|
122
|
+
* } else {
|
|
123
|
+
* throw normalized.originalError;
|
|
124
|
+
* }
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
/**
|
|
129
|
+
* Type guard to check if an error is a TransportStallError.
|
|
130
|
+
*
|
|
131
|
+
* @param error - Any value to check
|
|
132
|
+
* @returns True if the error is a TransportStallError
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* const result = await invokeWithStallDetection(config, invocation, 'BUILD');
|
|
137
|
+
* if (!result.ok && isTransportStallError(result.error)) {
|
|
138
|
+
* console.log('Stage:', result.error.stage);
|
|
139
|
+
* console.log('Request ID:', result.error.request_id);
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export declare function isTransportStallError(error: unknown): error is TransportStallError;
|
|
144
|
+
export declare function normalizeTransportError(error: unknown, stage: TransportStallStage): NormalizedError;
|
|
145
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/lib/transport.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAoBtF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wCAAwC;IACxC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,CA2BpE;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,mBAAmB,EAC1B,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAM,GAAG,IAAW,GAC9B,mBAAmB,CAarB;AAED;;;GAGG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,GACtC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,mBAAmB,EAC3B,UAAU,EAAE,gBAAgB,EAC5B,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,YAAY,CAAC,CAyCvB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,OAAO,EAAE,OAAO,CAAC;IACjB,gDAAgD;IAChD,UAAU,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACvC,0CAA0C;IAC1C,aAAa,EAAE,KAAK,CAAC;IACrB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAYlF;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,mBAAmB,GACzB,eAAe,CAmCjB"}
|