@ran-sh/dsh-crew 0.3.6 → 0.3.7
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/.claude-plugin/plugin.json +8 -8
- package/.mcp.json +8 -8
- package/LICENSE +21 -21
- package/README.de.md +359 -359
- package/README.es.md +359 -359
- package/README.fr.md +359 -359
- package/README.hi.md +359 -359
- package/README.id.md +359 -359
- package/README.ja.md +359 -359
- package/README.ko.md +359 -359
- package/README.md +125 -180
- package/README.pt.md +359 -359
- package/README.ru.md +359 -359
- package/README.th.md +359 -359
- package/README.tr.md +359 -359
- package/README.vi.md +359 -359
- package/README.zh-TW.md +359 -359
- package/README.zh.md +125 -180
- package/agents/ds-flash.md +26 -26
- package/agents/ds-pro.md +32 -32
- package/agents/ds-reviewer.md +23 -23
- package/agents/ds-worker.md +22 -22
- package/codex/agents/ds-flash.toml +30 -30
- package/codex/agents/ds-pro.toml +31 -31
- package/codex/agents/ds-reviewer.toml +28 -28
- package/codex/agents/ds-worker.toml +28 -28
- package/codex/prompts/dsh-config.md +3 -3
- package/codex/prompts/dsh-status.md +1 -1
- package/commands/config.md +11 -11
- package/commands/off.md +5 -5
- package/commands/on.md +5 -5
- package/commands/status.md +5 -5
- package/cordis.patch.yml +4 -4
- package/lib/client.js +3446 -2765
- package/package.json +131 -131
- package/scripts/build-client.mjs +28 -28
- package/scripts/live-crew-smoke.mjs +39 -39
- package/scripts/live-policy-matrix.mjs +177 -177
- package/scripts/policy-probe.mjs +101 -101
- package/scripts/setup.mjs +284 -284
- package/scripts/smoke-real.mjs +110 -110
- package/scripts/smoke.mjs +78 -78
- package/scripts/verify-installer-fix.mjs +26 -26
- package/src/adaptive-routing.mjs +260 -260
- package/src/client/activation-summary.tsx +64 -64
- package/src/client/collapsible-sections.mjs +55 -0
- package/src/client/entry.tsx +236 -236
- package/src/client/index.tsx +1213 -1120
- package/src/config-readiness.mjs +59 -59
- package/src/delivery.mjs +205 -205
- package/src/dsh-cli-runtime.mjs +239 -239
- package/src/failure-classification.mjs +172 -172
- package/src/hub/entry.mjs +98 -98
- package/src/hub-client.mjs +132 -132
- package/src/hub-compatibility.mjs +49 -49
- package/src/i18n.mjs +19 -19
- package/src/install/cli.mjs +28 -28
- package/src/install/install-legacy.mjs +462 -462
- package/src/install/install.mjs +451 -451
- package/src/install/npx-lifecycle.mjs +1055 -1055
- package/src/mcp-runtime.mjs +257 -257
- package/src/model-catalog.mjs +173 -173
- package/src/model-routing.mjs +391 -391
- package/src/policy.mjs +197 -197
- package/src/readiness-matrix.mjs +169 -169
- package/src/runtime-controls.mjs +90 -90
- package/src/runtime-identity.mjs +108 -108
- package/src/server.mjs +477 -477
- package/src/status-shard.mjs +52 -52
- package/src/structured-error-code.mjs +38 -38
- package/src/vision-route.mjs +138 -138
- package/src/workflow-runtime.mjs +573 -573
- package/src/workflow.mjs +160 -160
- package/src/workspace-audit.mjs +231 -231
- package/src/workspace-isolation.mjs +365 -365
- package/statusline/statusline.sh +14 -14
- package/statusline/worker-segment.sh +35 -35
- package/worker.cordis.yml +77 -77
|
@@ -1,172 +1,172 @@
|
|
|
1
|
-
// Pure, secret-free failure classification for workflow and MCP results.
|
|
2
|
-
//
|
|
3
|
-
// Classification consumes structured status/codes/outcomes only. It never
|
|
4
|
-
// parses raw exception text, provider responses, credentials, quotas or logs.
|
|
5
|
-
|
|
6
|
-
export const FAILURE_CATEGORIES = Object.freeze([
|
|
7
|
-
'none',
|
|
8
|
-
'policy',
|
|
9
|
-
'compatibility',
|
|
10
|
-
'provider',
|
|
11
|
-
'runtime',
|
|
12
|
-
'verification',
|
|
13
|
-
'cancelled',
|
|
14
|
-
]);
|
|
15
|
-
|
|
16
|
-
export const FAILURE_REASON_CODES = Object.freeze({
|
|
17
|
-
NONE: 'NONE',
|
|
18
|
-
CANCELLED: 'CANCELLED',
|
|
19
|
-
ATTEMPT_TIMEOUT: 'ATTEMPT_TIMEOUT',
|
|
20
|
-
RUNTIME_FAILURE: 'RUNTIME_FAILURE',
|
|
21
|
-
EXECUTION_FAILED: 'EXECUTION_FAILED',
|
|
22
|
-
TESTS_FAILED: 'TESTS_FAILED',
|
|
23
|
-
TESTS_NOT_RUN: 'TESTS_NOT_RUN',
|
|
24
|
-
DELIVERY_INCOMPLETE: 'DELIVERY_INCOMPLETE',
|
|
25
|
-
WORKSPACE_MISMATCH: 'WORKSPACE_MISMATCH',
|
|
26
|
-
TASK_BLOCKED: 'TASK_BLOCKED',
|
|
27
|
-
TASK_PARTIAL: 'TASK_PARTIAL',
|
|
28
|
-
REVIEW_CHANGES_REQUESTED: 'REVIEW_CHANGES_REQUESTED',
|
|
29
|
-
REVIEW_INCONCLUSIVE: 'REVIEW_INCONCLUSIVE',
|
|
30
|
-
POLICY_REJECTED: 'POLICY_REJECTED',
|
|
31
|
-
HUB_INCOMPATIBLE: 'HUB_INCOMPATIBLE',
|
|
32
|
-
PROVIDER_UNAVAILABLE: 'PROVIDER_UNAVAILABLE',
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const PROVIDER_CODES = new Set([
|
|
36
|
-
'NO_DSH_PROVIDER_SELECTED',
|
|
37
|
-
'NO_WORKER_MODEL_AVAILABLE',
|
|
38
|
-
'MODEL_CATALOG_UNAVAILABLE',
|
|
39
|
-
'PROVIDER_CATALOG_UNAVAILABLE',
|
|
40
|
-
'PROVIDER_CATALOG_HEALTH_WARNING',
|
|
41
|
-
]);
|
|
42
|
-
|
|
43
|
-
const POLICY_CODES = new Set([
|
|
44
|
-
'SUBAGENTS_DISABLED',
|
|
45
|
-
'TIER_DISABLED',
|
|
46
|
-
'NO_AUTO_TIER',
|
|
47
|
-
'NO_WORKER_TIER',
|
|
48
|
-
'PRO_NOT_AUTO',
|
|
49
|
-
'VISION_DISABLED',
|
|
50
|
-
'ROLE_DISABLED',
|
|
51
|
-
'ROLE_NOT_AUTO',
|
|
52
|
-
'ROLE_TIER_CONFLICT',
|
|
53
|
-
]);
|
|
54
|
-
|
|
55
|
-
const COMPATIBILITY_CODES = new Set([
|
|
56
|
-
'HUB_UNREACHABLE',
|
|
57
|
-
'HUB_HTTP_ERROR',
|
|
58
|
-
'HUB_SERVICE_MISMATCH',
|
|
59
|
-
'HUB_PROTOCOL_MISSING',
|
|
60
|
-
'HUB_PROTOCOL_MISMATCH',
|
|
61
|
-
'HUB_CAPABILITY_MISSING',
|
|
62
|
-
]);
|
|
63
|
-
|
|
64
|
-
const RUNTIME_CODES = new Set([
|
|
65
|
-
'ISOLATION_UNAVAILABLE',
|
|
66
|
-
'NOT_GIT_REPOSITORY',
|
|
67
|
-
'GIT_NOT_FOUND',
|
|
68
|
-
'GIT_TIMEOUT',
|
|
69
|
-
'GIT_ERROR',
|
|
70
|
-
'WORKTREE_LOCKED',
|
|
71
|
-
'WORKTREE_CREATE_FAILED',
|
|
72
|
-
'CANDIDATE_CAPTURE_FAILED',
|
|
73
|
-
'ATTEMPT_INFRA_FAILURE',
|
|
74
|
-
'HUB_REQUEST_FAILED',
|
|
75
|
-
]);
|
|
76
|
-
|
|
77
|
-
function normalizedCode(value) {
|
|
78
|
-
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function result(category, reasonCode, { sourceCode = null, terminalReason = null } = {}) {
|
|
82
|
-
return {
|
|
83
|
-
schema_version: 1,
|
|
84
|
-
category,
|
|
85
|
-
reason_code: reasonCode,
|
|
86
|
-
...(sourceCode ? { source_code: sourceCode } : {}),
|
|
87
|
-
...(terminalReason ? { terminal_reason: terminalReason } : {}),
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function verificationRoot({ outcome, review } = {}) {
|
|
92
|
-
if (review?.verdict === 'request_changes') return FAILURE_REASON_CODES.REVIEW_CHANGES_REQUESTED;
|
|
93
|
-
if (review?.verdict === 'inconclusive' && review?.status === 'failed') return FAILURE_REASON_CODES.REVIEW_INCONCLUSIVE;
|
|
94
|
-
if (outcome?.tests_status === 'FAIL') return FAILURE_REASON_CODES.TESTS_FAILED;
|
|
95
|
-
if (outcome?.delivery?.complete === false) return FAILURE_REASON_CODES.DELIVERY_INCOMPLETE;
|
|
96
|
-
if (outcome?.workspace_evidence_ok === false) return FAILURE_REASON_CODES.WORKSPACE_MISMATCH;
|
|
97
|
-
if (outcome?.task_status === 'blocked') return FAILURE_REASON_CODES.TASK_BLOCKED;
|
|
98
|
-
if (outcome?.tests_status === 'NOT RUN') return FAILURE_REASON_CODES.TESTS_NOT_RUN;
|
|
99
|
-
if (outcome?.task_status === 'partial') return FAILURE_REASON_CODES.TASK_PARTIAL;
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Classify one workflow/result snapshot.
|
|
105
|
-
*
|
|
106
|
-
* Precedence is intentional:
|
|
107
|
-
* 1) cancellation is explicit user/runtime intent;
|
|
108
|
-
* 2) stable compatibility/provider/policy codes identify the failing boundary;
|
|
109
|
-
* 3) timeout/runtime execution failures outrank verification;
|
|
110
|
-
* 4) verification uses the business evidence that caused escalation/failure;
|
|
111
|
-
* 5) otherwise the result is `none`.
|
|
112
|
-
*
|
|
113
|
-
* `decision.reason` is retained only as a bounded terminal reason (for example
|
|
114
|
-
* max_attempts_reached); it never replaces the underlying verification cause.
|
|
115
|
-
*/
|
|
116
|
-
export function classifyFailure({
|
|
117
|
-
phase = null,
|
|
118
|
-
status = null,
|
|
119
|
-
errorCode = null,
|
|
120
|
-
outcome = null,
|
|
121
|
-
decision = null,
|
|
122
|
-
review = null,
|
|
123
|
-
childAttempts = [],
|
|
124
|
-
} = {}) {
|
|
125
|
-
const code = normalizedCode(errorCode);
|
|
126
|
-
const terminalReason = normalizedCode(decision?.reason);
|
|
127
|
-
|
|
128
|
-
if (phase === 'cancelled' || status === 'cancelled' || code === 'WORKFLOW_CANCELLED') {
|
|
129
|
-
return result('cancelled', FAILURE_REASON_CODES.CANCELLED, {
|
|
130
|
-
sourceCode: code === 'WORKFLOW_CANCELLED' ? code : null,
|
|
131
|
-
terminalReason,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (code && COMPATIBILITY_CODES.has(code)) {
|
|
136
|
-
return result('compatibility', code, { sourceCode: code, terminalReason });
|
|
137
|
-
}
|
|
138
|
-
if (code && PROVIDER_CODES.has(code)) {
|
|
139
|
-
return result('provider', FAILURE_REASON_CODES.PROVIDER_UNAVAILABLE, { sourceCode: code, terminalReason });
|
|
140
|
-
}
|
|
141
|
-
if (code && POLICY_CODES.has(code)) {
|
|
142
|
-
return result('policy', FAILURE_REASON_CODES.POLICY_REJECTED, { sourceCode: code, terminalReason });
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const attempts = Array.isArray(childAttempts) ? childAttempts : [];
|
|
146
|
-
if (attempts.some((attempt) => attempt?.timed_out === true || attempt?.stopReason === 'timeout')) {
|
|
147
|
-
return result('runtime', FAILURE_REASON_CODES.ATTEMPT_TIMEOUT, { terminalReason });
|
|
148
|
-
}
|
|
149
|
-
if (code && RUNTIME_CODES.has(code)) {
|
|
150
|
-
return result('runtime', code, { sourceCode: code, terminalReason });
|
|
151
|
-
}
|
|
152
|
-
if (outcome?.execution_status === 'failed') {
|
|
153
|
-
return result('runtime', FAILURE_REASON_CODES.EXECUTION_FAILED, { terminalReason });
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const verification = verificationRoot({ outcome, review });
|
|
157
|
-
if (verification) return result('verification', verification, { terminalReason });
|
|
158
|
-
|
|
159
|
-
if (phase === 'failed' || status === 'failed') {
|
|
160
|
-
return result('runtime', FAILURE_REASON_CODES.RUNTIME_FAILURE, {
|
|
161
|
-
sourceCode: code,
|
|
162
|
-
terminalReason,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return result('none', FAILURE_REASON_CODES.NONE);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/** Classify an immediate MCP rejection before a workflow exists. */
|
|
170
|
-
export function classifyFailureCode(code) {
|
|
171
|
-
return classifyFailure({ status: 'failed', errorCode: code });
|
|
172
|
-
}
|
|
1
|
+
// Pure, secret-free failure classification for workflow and MCP results.
|
|
2
|
+
//
|
|
3
|
+
// Classification consumes structured status/codes/outcomes only. It never
|
|
4
|
+
// parses raw exception text, provider responses, credentials, quotas or logs.
|
|
5
|
+
|
|
6
|
+
export const FAILURE_CATEGORIES = Object.freeze([
|
|
7
|
+
'none',
|
|
8
|
+
'policy',
|
|
9
|
+
'compatibility',
|
|
10
|
+
'provider',
|
|
11
|
+
'runtime',
|
|
12
|
+
'verification',
|
|
13
|
+
'cancelled',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
export const FAILURE_REASON_CODES = Object.freeze({
|
|
17
|
+
NONE: 'NONE',
|
|
18
|
+
CANCELLED: 'CANCELLED',
|
|
19
|
+
ATTEMPT_TIMEOUT: 'ATTEMPT_TIMEOUT',
|
|
20
|
+
RUNTIME_FAILURE: 'RUNTIME_FAILURE',
|
|
21
|
+
EXECUTION_FAILED: 'EXECUTION_FAILED',
|
|
22
|
+
TESTS_FAILED: 'TESTS_FAILED',
|
|
23
|
+
TESTS_NOT_RUN: 'TESTS_NOT_RUN',
|
|
24
|
+
DELIVERY_INCOMPLETE: 'DELIVERY_INCOMPLETE',
|
|
25
|
+
WORKSPACE_MISMATCH: 'WORKSPACE_MISMATCH',
|
|
26
|
+
TASK_BLOCKED: 'TASK_BLOCKED',
|
|
27
|
+
TASK_PARTIAL: 'TASK_PARTIAL',
|
|
28
|
+
REVIEW_CHANGES_REQUESTED: 'REVIEW_CHANGES_REQUESTED',
|
|
29
|
+
REVIEW_INCONCLUSIVE: 'REVIEW_INCONCLUSIVE',
|
|
30
|
+
POLICY_REJECTED: 'POLICY_REJECTED',
|
|
31
|
+
HUB_INCOMPATIBLE: 'HUB_INCOMPATIBLE',
|
|
32
|
+
PROVIDER_UNAVAILABLE: 'PROVIDER_UNAVAILABLE',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const PROVIDER_CODES = new Set([
|
|
36
|
+
'NO_DSH_PROVIDER_SELECTED',
|
|
37
|
+
'NO_WORKER_MODEL_AVAILABLE',
|
|
38
|
+
'MODEL_CATALOG_UNAVAILABLE',
|
|
39
|
+
'PROVIDER_CATALOG_UNAVAILABLE',
|
|
40
|
+
'PROVIDER_CATALOG_HEALTH_WARNING',
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
const POLICY_CODES = new Set([
|
|
44
|
+
'SUBAGENTS_DISABLED',
|
|
45
|
+
'TIER_DISABLED',
|
|
46
|
+
'NO_AUTO_TIER',
|
|
47
|
+
'NO_WORKER_TIER',
|
|
48
|
+
'PRO_NOT_AUTO',
|
|
49
|
+
'VISION_DISABLED',
|
|
50
|
+
'ROLE_DISABLED',
|
|
51
|
+
'ROLE_NOT_AUTO',
|
|
52
|
+
'ROLE_TIER_CONFLICT',
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
const COMPATIBILITY_CODES = new Set([
|
|
56
|
+
'HUB_UNREACHABLE',
|
|
57
|
+
'HUB_HTTP_ERROR',
|
|
58
|
+
'HUB_SERVICE_MISMATCH',
|
|
59
|
+
'HUB_PROTOCOL_MISSING',
|
|
60
|
+
'HUB_PROTOCOL_MISMATCH',
|
|
61
|
+
'HUB_CAPABILITY_MISSING',
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
const RUNTIME_CODES = new Set([
|
|
65
|
+
'ISOLATION_UNAVAILABLE',
|
|
66
|
+
'NOT_GIT_REPOSITORY',
|
|
67
|
+
'GIT_NOT_FOUND',
|
|
68
|
+
'GIT_TIMEOUT',
|
|
69
|
+
'GIT_ERROR',
|
|
70
|
+
'WORKTREE_LOCKED',
|
|
71
|
+
'WORKTREE_CREATE_FAILED',
|
|
72
|
+
'CANDIDATE_CAPTURE_FAILED',
|
|
73
|
+
'ATTEMPT_INFRA_FAILURE',
|
|
74
|
+
'HUB_REQUEST_FAILED',
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
function normalizedCode(value) {
|
|
78
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function result(category, reasonCode, { sourceCode = null, terminalReason = null } = {}) {
|
|
82
|
+
return {
|
|
83
|
+
schema_version: 1,
|
|
84
|
+
category,
|
|
85
|
+
reason_code: reasonCode,
|
|
86
|
+
...(sourceCode ? { source_code: sourceCode } : {}),
|
|
87
|
+
...(terminalReason ? { terminal_reason: terminalReason } : {}),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function verificationRoot({ outcome, review } = {}) {
|
|
92
|
+
if (review?.verdict === 'request_changes') return FAILURE_REASON_CODES.REVIEW_CHANGES_REQUESTED;
|
|
93
|
+
if (review?.verdict === 'inconclusive' && review?.status === 'failed') return FAILURE_REASON_CODES.REVIEW_INCONCLUSIVE;
|
|
94
|
+
if (outcome?.tests_status === 'FAIL') return FAILURE_REASON_CODES.TESTS_FAILED;
|
|
95
|
+
if (outcome?.delivery?.complete === false) return FAILURE_REASON_CODES.DELIVERY_INCOMPLETE;
|
|
96
|
+
if (outcome?.workspace_evidence_ok === false) return FAILURE_REASON_CODES.WORKSPACE_MISMATCH;
|
|
97
|
+
if (outcome?.task_status === 'blocked') return FAILURE_REASON_CODES.TASK_BLOCKED;
|
|
98
|
+
if (outcome?.tests_status === 'NOT RUN') return FAILURE_REASON_CODES.TESTS_NOT_RUN;
|
|
99
|
+
if (outcome?.task_status === 'partial') return FAILURE_REASON_CODES.TASK_PARTIAL;
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Classify one workflow/result snapshot.
|
|
105
|
+
*
|
|
106
|
+
* Precedence is intentional:
|
|
107
|
+
* 1) cancellation is explicit user/runtime intent;
|
|
108
|
+
* 2) stable compatibility/provider/policy codes identify the failing boundary;
|
|
109
|
+
* 3) timeout/runtime execution failures outrank verification;
|
|
110
|
+
* 4) verification uses the business evidence that caused escalation/failure;
|
|
111
|
+
* 5) otherwise the result is `none`.
|
|
112
|
+
*
|
|
113
|
+
* `decision.reason` is retained only as a bounded terminal reason (for example
|
|
114
|
+
* max_attempts_reached); it never replaces the underlying verification cause.
|
|
115
|
+
*/
|
|
116
|
+
export function classifyFailure({
|
|
117
|
+
phase = null,
|
|
118
|
+
status = null,
|
|
119
|
+
errorCode = null,
|
|
120
|
+
outcome = null,
|
|
121
|
+
decision = null,
|
|
122
|
+
review = null,
|
|
123
|
+
childAttempts = [],
|
|
124
|
+
} = {}) {
|
|
125
|
+
const code = normalizedCode(errorCode);
|
|
126
|
+
const terminalReason = normalizedCode(decision?.reason);
|
|
127
|
+
|
|
128
|
+
if (phase === 'cancelled' || status === 'cancelled' || code === 'WORKFLOW_CANCELLED') {
|
|
129
|
+
return result('cancelled', FAILURE_REASON_CODES.CANCELLED, {
|
|
130
|
+
sourceCode: code === 'WORKFLOW_CANCELLED' ? code : null,
|
|
131
|
+
terminalReason,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (code && COMPATIBILITY_CODES.has(code)) {
|
|
136
|
+
return result('compatibility', code, { sourceCode: code, terminalReason });
|
|
137
|
+
}
|
|
138
|
+
if (code && PROVIDER_CODES.has(code)) {
|
|
139
|
+
return result('provider', FAILURE_REASON_CODES.PROVIDER_UNAVAILABLE, { sourceCode: code, terminalReason });
|
|
140
|
+
}
|
|
141
|
+
if (code && POLICY_CODES.has(code)) {
|
|
142
|
+
return result('policy', FAILURE_REASON_CODES.POLICY_REJECTED, { sourceCode: code, terminalReason });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const attempts = Array.isArray(childAttempts) ? childAttempts : [];
|
|
146
|
+
if (attempts.some((attempt) => attempt?.timed_out === true || attempt?.stopReason === 'timeout')) {
|
|
147
|
+
return result('runtime', FAILURE_REASON_CODES.ATTEMPT_TIMEOUT, { terminalReason });
|
|
148
|
+
}
|
|
149
|
+
if (code && RUNTIME_CODES.has(code)) {
|
|
150
|
+
return result('runtime', code, { sourceCode: code, terminalReason });
|
|
151
|
+
}
|
|
152
|
+
if (outcome?.execution_status === 'failed') {
|
|
153
|
+
return result('runtime', FAILURE_REASON_CODES.EXECUTION_FAILED, { terminalReason });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const verification = verificationRoot({ outcome, review });
|
|
157
|
+
if (verification) return result('verification', verification, { terminalReason });
|
|
158
|
+
|
|
159
|
+
if (phase === 'failed' || status === 'failed') {
|
|
160
|
+
return result('runtime', FAILURE_REASON_CODES.RUNTIME_FAILURE, {
|
|
161
|
+
sourceCode: code,
|
|
162
|
+
terminalReason,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return result('none', FAILURE_REASON_CODES.NONE);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Classify an immediate MCP rejection before a workflow exists. */
|
|
170
|
+
export function classifyFailureCode(code) {
|
|
171
|
+
return classifyFailure({ status: 'failed', errorCode: code });
|
|
172
|
+
}
|
package/src/hub/entry.mjs
CHANGED
|
@@ -1,98 +1,98 @@
|
|
|
1
|
-
// Thin DSH plugin entry wrapper for v0.3 runtime compatibility discovery.
|
|
2
|
-
//
|
|
3
|
-
// The legacy /ping endpoint stays reachability-only inside index.mjs. This
|
|
4
|
-
// wrapper adds a separate /runtime endpoint carrying the explicit Hub/MCP
|
|
5
|
-
// compatibility contract, then delegates all existing Hub behavior unchanged.
|
|
6
|
-
// It also owns the v0.3 adaptive-routing observer so the large legacy Hub body
|
|
7
|
-
// stays untouched: only jobs that explicitly opted into adaptive routing feed
|
|
8
|
-
// bounded process-local health for later selections.
|
|
9
|
-
|
|
10
|
-
import { apply as applyHub, inject, name, WorkerRegistry } from './index.mjs';
|
|
11
|
-
import { getHubRuntimeIdentity } from '../runtime-identity.mjs';
|
|
12
|
-
import { getProcessAdaptiveHealthStore } from '../adaptive-routing.mjs';
|
|
13
|
-
|
|
14
|
-
const RUNTIME_PATH = '/_dsh/dsh-crew/runtime';
|
|
15
|
-
const ADAPTIVE_OBSERVER_INSTALLED = Symbol.for('@ran-sh/dsh-crew/adaptive-observer-installed');
|
|
16
|
-
const ADAPTIVE_JOB_OBSERVED = Symbol.for('@ran-sh/dsh-crew/adaptive-job-observed');
|
|
17
|
-
|
|
18
|
-
export { inject, name };
|
|
19
|
-
|
|
20
|
-
function sendJson(res, status, value) {
|
|
21
|
-
const body = JSON.stringify(value);
|
|
22
|
-
res.writeHead(status, {
|
|
23
|
-
'content-type': 'application/json; charset=utf-8',
|
|
24
|
-
'content-length': Buffer.byteLength(body),
|
|
25
|
-
'cache-control': 'no-store',
|
|
26
|
-
'x-content-type-options': 'nosniff',
|
|
27
|
-
'cross-origin-resource-policy': 'same-origin',
|
|
28
|
-
});
|
|
29
|
-
res.end(body);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function registerRuntimeEndpoint(ctx) {
|
|
33
|
-
return ctx.inject(['webServer'], (webCtx) => webCtx.webServer.register({
|
|
34
|
-
kind: 'exact',
|
|
35
|
-
path: RUNTIME_PATH,
|
|
36
|
-
handler: (_req, res) => sendJson(res, 200, { ok: true, ...getHubRuntimeIdentity() }),
|
|
37
|
-
}));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Record only an opt-in adaptive Hub attempt. The store accepts bounded
|
|
42
|
-
* provider/model identifiers plus outcome/latency; arbitrary job errors,
|
|
43
|
-
* credentials, quota and provider payloads never cross this boundary.
|
|
44
|
-
*/
|
|
45
|
-
export function recordAdaptiveJobOutcome(job, store = getProcessAdaptiveHealthStore()) {
|
|
46
|
-
if (job?.selection_trace?.adaptive?.enabled !== true) return false;
|
|
47
|
-
const started = Date.parse(job.startedAt ?? '');
|
|
48
|
-
const ended = Date.parse(job.endedAt ?? '');
|
|
49
|
-
const latencyMs = Number.isFinite(started) && Number.isFinite(ended)
|
|
50
|
-
? Math.max(0, ended - started)
|
|
51
|
-
: undefined;
|
|
52
|
-
return store.record(
|
|
53
|
-
{ provider: job.provider, model: job.model },
|
|
54
|
-
{
|
|
55
|
-
role: job.role,
|
|
56
|
-
status: job.status,
|
|
57
|
-
stopReason: job.stopReason,
|
|
58
|
-
latencyMs,
|
|
59
|
-
},
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Wrap WorkerRegistry.spawn once per process. Selection itself still lives in
|
|
65
|
-
* the existing Hub implementation; rankAdaptiveCandidates reads the same
|
|
66
|
-
* process-local store through adaptive-routing.mjs. Replacing job.promise with
|
|
67
|
-
* a chained promise preserves all legacy completion/waiter behavior and runs
|
|
68
|
-
* this observer only after the Hub's own finalizer populated endedAt/status.
|
|
69
|
-
*/
|
|
70
|
-
export function installAdaptiveHealthObserver() {
|
|
71
|
-
if (WorkerRegistry.prototype[ADAPTIVE_OBSERVER_INSTALLED] === true) return false;
|
|
72
|
-
const originalSpawn = WorkerRegistry.prototype.spawn;
|
|
73
|
-
Object.defineProperty(WorkerRegistry.prototype, ADAPTIVE_OBSERVER_INSTALLED, {
|
|
74
|
-
value: true,
|
|
75
|
-
enumerable: false,
|
|
76
|
-
configurable: false,
|
|
77
|
-
});
|
|
78
|
-
WorkerRegistry.prototype.spawn = async function observedAdaptiveSpawn(...args) {
|
|
79
|
-
const job = await originalSpawn.apply(this, args);
|
|
80
|
-
if (!job || job[ADAPTIVE_JOB_OBSERVED] === true || typeof job.promise?.finally !== 'function') return job;
|
|
81
|
-
Object.defineProperty(job, ADAPTIVE_JOB_OBSERVED, {
|
|
82
|
-
value: true,
|
|
83
|
-
enumerable: false,
|
|
84
|
-
configurable: false,
|
|
85
|
-
});
|
|
86
|
-
job.promise = job.promise.finally(() => {
|
|
87
|
-
recordAdaptiveJobOutcome(job);
|
|
88
|
-
});
|
|
89
|
-
return job;
|
|
90
|
-
};
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export async function apply(ctx) {
|
|
95
|
-
registerRuntimeEndpoint(ctx);
|
|
96
|
-
installAdaptiveHealthObserver();
|
|
97
|
-
return applyHub(ctx);
|
|
98
|
-
}
|
|
1
|
+
// Thin DSH plugin entry wrapper for v0.3 runtime compatibility discovery.
|
|
2
|
+
//
|
|
3
|
+
// The legacy /ping endpoint stays reachability-only inside index.mjs. This
|
|
4
|
+
// wrapper adds a separate /runtime endpoint carrying the explicit Hub/MCP
|
|
5
|
+
// compatibility contract, then delegates all existing Hub behavior unchanged.
|
|
6
|
+
// It also owns the v0.3 adaptive-routing observer so the large legacy Hub body
|
|
7
|
+
// stays untouched: only jobs that explicitly opted into adaptive routing feed
|
|
8
|
+
// bounded process-local health for later selections.
|
|
9
|
+
|
|
10
|
+
import { apply as applyHub, inject, name, WorkerRegistry } from './index.mjs';
|
|
11
|
+
import { getHubRuntimeIdentity } from '../runtime-identity.mjs';
|
|
12
|
+
import { getProcessAdaptiveHealthStore } from '../adaptive-routing.mjs';
|
|
13
|
+
|
|
14
|
+
const RUNTIME_PATH = '/_dsh/dsh-crew/runtime';
|
|
15
|
+
const ADAPTIVE_OBSERVER_INSTALLED = Symbol.for('@ran-sh/dsh-crew/adaptive-observer-installed');
|
|
16
|
+
const ADAPTIVE_JOB_OBSERVED = Symbol.for('@ran-sh/dsh-crew/adaptive-job-observed');
|
|
17
|
+
|
|
18
|
+
export { inject, name };
|
|
19
|
+
|
|
20
|
+
function sendJson(res, status, value) {
|
|
21
|
+
const body = JSON.stringify(value);
|
|
22
|
+
res.writeHead(status, {
|
|
23
|
+
'content-type': 'application/json; charset=utf-8',
|
|
24
|
+
'content-length': Buffer.byteLength(body),
|
|
25
|
+
'cache-control': 'no-store',
|
|
26
|
+
'x-content-type-options': 'nosniff',
|
|
27
|
+
'cross-origin-resource-policy': 'same-origin',
|
|
28
|
+
});
|
|
29
|
+
res.end(body);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function registerRuntimeEndpoint(ctx) {
|
|
33
|
+
return ctx.inject(['webServer'], (webCtx) => webCtx.webServer.register({
|
|
34
|
+
kind: 'exact',
|
|
35
|
+
path: RUNTIME_PATH,
|
|
36
|
+
handler: (_req, res) => sendJson(res, 200, { ok: true, ...getHubRuntimeIdentity() }),
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Record only an opt-in adaptive Hub attempt. The store accepts bounded
|
|
42
|
+
* provider/model identifiers plus outcome/latency; arbitrary job errors,
|
|
43
|
+
* credentials, quota and provider payloads never cross this boundary.
|
|
44
|
+
*/
|
|
45
|
+
export function recordAdaptiveJobOutcome(job, store = getProcessAdaptiveHealthStore()) {
|
|
46
|
+
if (job?.selection_trace?.adaptive?.enabled !== true) return false;
|
|
47
|
+
const started = Date.parse(job.startedAt ?? '');
|
|
48
|
+
const ended = Date.parse(job.endedAt ?? '');
|
|
49
|
+
const latencyMs = Number.isFinite(started) && Number.isFinite(ended)
|
|
50
|
+
? Math.max(0, ended - started)
|
|
51
|
+
: undefined;
|
|
52
|
+
return store.record(
|
|
53
|
+
{ provider: job.provider, model: job.model },
|
|
54
|
+
{
|
|
55
|
+
role: job.role,
|
|
56
|
+
status: job.status,
|
|
57
|
+
stopReason: job.stopReason,
|
|
58
|
+
latencyMs,
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Wrap WorkerRegistry.spawn once per process. Selection itself still lives in
|
|
65
|
+
* the existing Hub implementation; rankAdaptiveCandidates reads the same
|
|
66
|
+
* process-local store through adaptive-routing.mjs. Replacing job.promise with
|
|
67
|
+
* a chained promise preserves all legacy completion/waiter behavior and runs
|
|
68
|
+
* this observer only after the Hub's own finalizer populated endedAt/status.
|
|
69
|
+
*/
|
|
70
|
+
export function installAdaptiveHealthObserver() {
|
|
71
|
+
if (WorkerRegistry.prototype[ADAPTIVE_OBSERVER_INSTALLED] === true) return false;
|
|
72
|
+
const originalSpawn = WorkerRegistry.prototype.spawn;
|
|
73
|
+
Object.defineProperty(WorkerRegistry.prototype, ADAPTIVE_OBSERVER_INSTALLED, {
|
|
74
|
+
value: true,
|
|
75
|
+
enumerable: false,
|
|
76
|
+
configurable: false,
|
|
77
|
+
});
|
|
78
|
+
WorkerRegistry.prototype.spawn = async function observedAdaptiveSpawn(...args) {
|
|
79
|
+
const job = await originalSpawn.apply(this, args);
|
|
80
|
+
if (!job || job[ADAPTIVE_JOB_OBSERVED] === true || typeof job.promise?.finally !== 'function') return job;
|
|
81
|
+
Object.defineProperty(job, ADAPTIVE_JOB_OBSERVED, {
|
|
82
|
+
value: true,
|
|
83
|
+
enumerable: false,
|
|
84
|
+
configurable: false,
|
|
85
|
+
});
|
|
86
|
+
job.promise = job.promise.finally(() => {
|
|
87
|
+
recordAdaptiveJobOutcome(job);
|
|
88
|
+
});
|
|
89
|
+
return job;
|
|
90
|
+
};
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function apply(ctx) {
|
|
95
|
+
registerRuntimeEndpoint(ctx);
|
|
96
|
+
installAdaptiveHealthObserver();
|
|
97
|
+
return applyHub(ctx);
|
|
98
|
+
}
|