@ran-sh/dsh-crew 1.1.1 → 1.2.0-rc.4
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/marketplace.json +16 -17
- package/.claude-plugin/plugin.json +7 -1
- package/README.md +44 -15
- package/README.zh.md +37 -12
- package/agents/ds-flash.md +16 -18
- package/agents/ds-pro.md +16 -24
- package/agents/ds-reviewer.md +15 -15
- package/agents/ds-worker.md +16 -16
- package/bin/dsh-crew.mjs +16 -12
- package/codex/AGENTS.md +37 -85
- package/codex/agents/ds-flash.toml +18 -14
- package/codex/agents/ds-pro.toml +18 -15
- package/codex/agents/ds-reviewer.toml +17 -13
- package/codex/agents/ds-worker.toml +17 -13
- package/codex/prompts/dsh-config.md +17 -1
- package/codex/prompts/dsh-status.md +5 -1
- package/commands/config.md +16 -4
- package/commands/status.md +6 -2
- package/docs/installation.md +54 -9
- package/docs/readiness-matrix.md +14 -6
- package/docs/ui-surfaces.md +32 -17
- package/lib/client.js +5434 -4844
- package/official-web-bridge/lib/client.js +567 -479
- package/official-web-bridge/overlay-entry.mjs +59 -0
- package/package.json +6 -3
- package/scripts/build-client.mjs +2 -0
- package/scripts/setup.mjs +8 -7
- package/scripts/verify-crew-ui-polish.mjs +145 -0
- package/scripts/verify-history-ui.mjs +97 -0
- package/scripts/verify-npm-install.mjs +1 -1
- package/src/client/history-panel.tsx +108 -0
- package/src/client/host-readiness.mjs +15 -6
- package/src/client/index.tsx +86 -64
- package/src/client/model-callability-view.mjs +17 -0
- package/src/client/panel-chrome.tsx +40 -0
- package/src/client/quick-panel.tsx +88 -73
- package/src/client/readiness-envelope.mjs +34 -0
- package/src/config-readiness.mjs +181 -115
- package/src/extension-contract.mjs +44 -26
- package/src/history/admission-gate.mjs +51 -0
- package/src/history/archive-store.mjs +261 -0
- package/src/history/cleanup-plan.mjs +64 -0
- package/src/history/http.mjs +32 -0
- package/src/history/operation.mjs +86 -0
- package/src/history/runner.mjs +51 -0
- package/src/history/runtime.mjs +28 -0
- package/src/history/service.mjs +137 -0
- package/src/history/state.mjs +27 -0
- package/src/hub/index.mjs +141 -24
- package/src/hub-client.mjs +17 -15
- package/src/image-output.mjs +71 -0
- package/src/install/install-legacy.mjs +126 -23
- package/src/install/npx-lifecycle.mjs +373 -64
- package/src/install/official-frontend-assets.mjs +78 -0
- package/src/install/payload-content.mjs +88 -0
- package/src/install/windows-startup.mjs +107 -19
- package/src/install/windows-supervisor-adapter.mjs +443 -0
- package/src/install/windows-supervisor-lifecycle.mjs +782 -0
- package/src/install/zcode.mjs +53 -14
- package/src/job-contracts.mjs +37 -31
- package/src/jobs.mjs +2 -2
- package/src/mcp-runtime.mjs +74 -9
- package/src/model-callability-contract.mjs +79 -0
- package/src/multimodal.mjs +0 -0
- package/src/official-web-bridge.mjs +0 -2
- package/src/provider-health.mjs +130 -124
- package/src/readiness-matrix.mjs +20 -15
- package/src/runtime-identity-contract.mjs +34 -0
- package/src/runtime-identity.mjs +14 -4
- package/src/runtime-readiness-snapshot.mjs +285 -87
- package/src/server.mjs +86 -76
- package/src/standalone-sdk.mjs +23 -0
- package/src/supervisor/restart-request.mjs +64 -10
- package/src/workflow-runtime.mjs +28 -13
- package/windows/start-dsh-crew.cmd +57 -57
- package/windows/start-dsh-crew.ps1 +314 -51
- package/windows/supervisor-control.ps1 +467 -0
- package/zcode/AGENTS.md +4 -1
- package/zcode/agents/ds-reviewer.md +31 -36
- package/zcode/agents/ds-worker.md +31 -39
- package/zcode/commands/dsh-config.md +17 -6
- package/zcode/commands/dsh-status.md +5 -4
- package/.mcp.json +0 -8
|
@@ -1,87 +1,285 @@
|
|
|
1
|
-
// Single, bounded readiness projection shared by the Hub extension surface,
|
|
2
|
-
// MCP diagnostics and the client. It consumes already-collected evidence and
|
|
3
|
-
// never performs I/O or carries raw task/result/error/credential content.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (!
|
|
50
|
-
return {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
};
|
|
87
|
-
|
|
1
|
+
// Single, bounded readiness projection shared by the Hub extension surface,
|
|
2
|
+
// MCP diagnostics and the client. It consumes already-collected evidence and
|
|
3
|
+
// never performs I/O or carries raw task/result/error/credential content.
|
|
4
|
+
|
|
5
|
+
import { isCompleteRuntimeIdentity, isExactNativeCrewIdentity, sameCompleteRuntimeIdentity } from './runtime-identity-contract.mjs';
|
|
6
|
+
import { MODEL_CALLABILITY_SCHEMA_VERSION, validateModelCallabilityV2 } from './model-callability-contract.mjs';
|
|
7
|
+
|
|
8
|
+
const MAX_HEALTH = 128;
|
|
9
|
+
const DEFAULT_EXECUTION_EVIDENCE_TTL_MS = 5 * 60 * 1000;
|
|
10
|
+
const MIN_EXECUTION_EVIDENCE_TTL_MS = 1_000;
|
|
11
|
+
const MAX_EXECUTION_EVIDENCE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
12
|
+
|
|
13
|
+
function text(value) { return typeof value === 'string' && value.trim() ? value.trim() : null; }
|
|
14
|
+
|
|
15
|
+
function projectRuntime(runtime) {
|
|
16
|
+
if (!runtime || typeof runtime !== 'object') return null;
|
|
17
|
+
return {
|
|
18
|
+
execution_plane: text(runtime.execution_plane),
|
|
19
|
+
profile: text(runtime.profile),
|
|
20
|
+
listen_port: Number.isInteger(runtime.listen_port) ? runtime.listen_port : null,
|
|
21
|
+
runtime_id: text(runtime.runtime_id),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function runtimeIdentity(runtime) {
|
|
26
|
+
return isExactNativeCrewIdentity(runtime)
|
|
27
|
+
? {
|
|
28
|
+
execution_plane: runtime.execution_plane,
|
|
29
|
+
profile: runtime.profile,
|
|
30
|
+
listen_port: runtime.listen_port,
|
|
31
|
+
runtime_id: runtime.runtime_id,
|
|
32
|
+
}
|
|
33
|
+
: null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function projectSelection(value) {
|
|
37
|
+
if (!value || typeof value !== 'object') return null;
|
|
38
|
+
const provider = text(value.provider);
|
|
39
|
+
const model = text(value.model);
|
|
40
|
+
if (!provider || !model) return null;
|
|
41
|
+
return { provider, model, ...(text(value.source) ? { source: text(value.source) } : {}) };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function projectHealth(entry) {
|
|
45
|
+
if (!entry || typeof entry !== 'object') return null;
|
|
46
|
+
const provider = text(entry.provider);
|
|
47
|
+
const model = text(entry.model);
|
|
48
|
+
const state = text(entry.state);
|
|
49
|
+
if (!provider || !model || !state) return null;
|
|
50
|
+
return {
|
|
51
|
+
provider,
|
|
52
|
+
model,
|
|
53
|
+
state,
|
|
54
|
+
...(text(entry.reason_code) ? { reason_code: text(entry.reason_code) } : {}),
|
|
55
|
+
...(Number.isFinite(entry.observed_at) ? { observed_at: entry.observed_at } : {}),
|
|
56
|
+
...(Number.isFinite(entry.expires_at) ? { expires_at: entry.expires_at } : {}),
|
|
57
|
+
fresh: entry.fresh === true,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function matrixRow(matrix, id) {
|
|
62
|
+
return Array.isArray(matrix?.rows) ? matrix.rows.find((row) => row?.id === id) ?? null : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function projectWorkspace(workspace) {
|
|
66
|
+
if (!workspace || typeof workspace !== 'object') return null;
|
|
67
|
+
return {
|
|
68
|
+
...(text(workspace.status) ? { status: text(workspace.status) } : {}),
|
|
69
|
+
...(text(workspace.reason_code) ? { reason_code: text(workspace.reason_code) } : {}),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function routeKey(value) {
|
|
74
|
+
return value?.provider && value?.model ? `${value.provider}\u0000${value.model}` : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeExecutionEvidenceTtl(value) {
|
|
78
|
+
const parsed = Number(value);
|
|
79
|
+
if (!Number.isFinite(parsed) || parsed < MIN_EXECUTION_EVIDENCE_TTL_MS) return DEFAULT_EXECUTION_EVIDENCE_TTL_MS;
|
|
80
|
+
return Math.min(Math.floor(parsed), MAX_EXECUTION_EVIDENCE_TTL_MS);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function projectModelRole({ role, selected, health, jobs, runtime, now, executionEvidenceTtlMs, enabled }) {
|
|
84
|
+
if (enabled === false) return { state: 'NOT_APPLICABLE', reason_code: 'ROLE_DISABLED', selected: selected ?? null, last_success: null };
|
|
85
|
+
const route = routeKey(selected);
|
|
86
|
+
if (!route || !isExactNativeCrewIdentity(runtime)) return { state: 'UNKNOWN', reason_code: 'MODEL_ROUTE_UNAVAILABLE', selected: selected ?? null, last_success: null };
|
|
87
|
+
const matchingHealth = (Array.isArray(health) ? health : [])
|
|
88
|
+
.filter((entry) => routeKey(entry) === route)
|
|
89
|
+
.sort((left, right) => {
|
|
90
|
+
const observedDelta = (Number(right?.observed_at) || 0) - (Number(left?.observed_at) || 0);
|
|
91
|
+
if (observedDelta !== 0) return observedDelta;
|
|
92
|
+
if (left?.state === 'callable' && right?.state !== 'callable') return 1;
|
|
93
|
+
if (left?.state !== 'callable' && right?.state === 'callable') return -1;
|
|
94
|
+
return 0;
|
|
95
|
+
})[0];
|
|
96
|
+
const expiresAt = Number.isFinite(matchingHealth?.expires_at) ? matchingHealth.expires_at : null;
|
|
97
|
+
const observedAt = Number.isFinite(matchingHealth?.observed_at) ? matchingHealth.observed_at : null;
|
|
98
|
+
if (matchingHealth) {
|
|
99
|
+
if (observedAt === null || observedAt > now || expiresAt === null || expiresAt <= now
|
|
100
|
+
|| expiresAt - observedAt > MAX_EXECUTION_EVIDENCE_TTL_MS) {
|
|
101
|
+
return { state: 'STALE', reason_code: 'PROVIDER_HEALTH_STALE_OR_INVALID', selected, observed_at: observedAt, expires_at: expiresAt, source: 'provider_health', last_success: null };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const healthCurrent = matchingHealth && matchingHealth.fresh === true;
|
|
105
|
+
if (healthCurrent && matchingHealth.state !== 'callable') {
|
|
106
|
+
return { state: 'NOT_CALLABLE', reason_code: matchingHealth.reason_code ?? 'PROVIDER_ROUTE_UNCALLABLE', selected, observed_at: matchingHealth.observed_at ?? null, expires_at: expiresAt, source: 'provider_health', last_success: null };
|
|
107
|
+
}
|
|
108
|
+
if (healthCurrent && matchingHealth.state === 'callable') {
|
|
109
|
+
return { state: 'CALLABLE', reason_code: matchingHealth.reason_code ?? 'PROVIDER_CALLABLE', selected, observed_at: matchingHealth.observed_at ?? null, expires_at: expiresAt, source: 'provider_health', last_success: null };
|
|
110
|
+
}
|
|
111
|
+
const completed = (Array.isArray(jobs) ? jobs : [])
|
|
112
|
+
.filter((job) => job?.role === role && job?.provider === selected.provider && job?.model === selected.model
|
|
113
|
+
&& job?.status === 'done' && job?.task_status === 'success'
|
|
114
|
+
&& sameCompleteRuntimeIdentity(job.execution_context, runtime))
|
|
115
|
+
.map((job) => ({ job, endedAt: Date.parse(job.endedAt ?? '') }))
|
|
116
|
+
.filter(({ endedAt }) => Number.isFinite(endedAt))
|
|
117
|
+
.sort((a, b) => b.endedAt - a.endedAt)[0];
|
|
118
|
+
if (completed) {
|
|
119
|
+
const expires = completed.endedAt + executionEvidenceTtlMs;
|
|
120
|
+
const lastSuccess = { observed_at: completed.endedAt, expires_at: expires, job_id: completed.job.id };
|
|
121
|
+
if (completed.endedAt > now) return { state: 'STALE', reason_code: 'EXECUTION_EVIDENCE_FUTURE_DATED', selected, observed_at: completed.endedAt, expires_at: expires, source: 'execution', last_success: lastSuccess };
|
|
122
|
+
if (expires > now) return { state: 'CALLABLE', reason_code: 'RECENT_EXECUTION_PASSED', selected, observed_at: completed.endedAt, expires_at: expires, source: 'execution', last_success: lastSuccess };
|
|
123
|
+
return { state: 'STALE', reason_code: 'EXECUTION_EVIDENCE_EXPIRED', selected, observed_at: completed.endedAt, expires_at: expires, source: 'execution', last_success: lastSuccess };
|
|
124
|
+
}
|
|
125
|
+
return { state: 'UNKNOWN', reason_code: 'NO_CURRENT_MODEL_EVIDENCE', selected, last_success: null };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function projectModelCallability({ runtime = null, selections = {}, health = [], health_status = 'AVAILABLE', jobs = [], now = Date.now(), execution_evidence_ttl_ms = DEFAULT_EXECUTION_EVIDENCE_TTL_MS, enabled_roles = {} } = {}) {
|
|
129
|
+
const ttl = normalizeExecutionEvidenceTtl(execution_evidence_ttl_ms);
|
|
130
|
+
const enabledRoles = {
|
|
131
|
+
worker: enabled_roles?.worker !== false,
|
|
132
|
+
reviewer: enabled_roles?.reviewer !== false,
|
|
133
|
+
};
|
|
134
|
+
const roles = {
|
|
135
|
+
worker: projectModelRole({ role: 'worker', selected: selections.worker ?? selections.flash, health, jobs, runtime, now, executionEvidenceTtlMs: ttl, enabled: enabledRoles.worker }),
|
|
136
|
+
reviewer: projectModelRole({ role: 'reviewer', selected: selections.reviewer ?? selections.pro, health, jobs, runtime, now, executionEvidenceTtlMs: ttl, enabled: enabledRoles.reviewer }),
|
|
137
|
+
};
|
|
138
|
+
if (health_status !== 'AVAILABLE') {
|
|
139
|
+
for (const [name, enabled] of Object.entries(enabledRoles)) {
|
|
140
|
+
if (enabled) roles[name] = { ...roles[name], state: 'UNKNOWN', reason_code: 'PROVIDER_HEALTH_UNAVAILABLE', source: 'provider_health' };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const states = Object.entries(roles).filter(([name]) => enabledRoles[name]).map(([, role]) => role.state);
|
|
144
|
+
const overall = states.length === 0 ? 'UNKNOWN'
|
|
145
|
+
: states.includes('NOT_CALLABLE') ? 'NOT_CALLABLE'
|
|
146
|
+
: states.every((state) => state === 'CALLABLE') ? 'CALLABLE'
|
|
147
|
+
: states.some((state) => state === 'STALE') ? 'STALE' : 'UNKNOWN';
|
|
148
|
+
const expirations = Object.values(roles)
|
|
149
|
+
.map((role) => role?.expires_at)
|
|
150
|
+
.filter((value) => Number.isFinite(value));
|
|
151
|
+
return {
|
|
152
|
+
schema_version: MODEL_CALLABILITY_SCHEMA_VERSION,
|
|
153
|
+
captured_at: now,
|
|
154
|
+
expires_at: expirations.length ? Math.min(...expirations) : null,
|
|
155
|
+
current_runtime_id: runtime?.runtime_id ?? null,
|
|
156
|
+
runtime_identity: runtimeIdentity(runtime),
|
|
157
|
+
execution_evidence_ttl_ms: ttl,
|
|
158
|
+
enabled_roles: enabledRoles,
|
|
159
|
+
overall,
|
|
160
|
+
roles,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function buildRuntimeReadinessSnapshot({
|
|
165
|
+
runtime = null,
|
|
166
|
+
readinessMatrix = null,
|
|
167
|
+
selections = {},
|
|
168
|
+
health = [],
|
|
169
|
+
health_status = null,
|
|
170
|
+
jobs = [],
|
|
171
|
+
workspace = null,
|
|
172
|
+
enabled_roles = {},
|
|
173
|
+
now = Date.now(),
|
|
174
|
+
} = {}) {
|
|
175
|
+
const healthObservations = Array.isArray(health) ? health : (Array.isArray(health?.observations) ? health.observations : []);
|
|
176
|
+
const resolvedHealthStatus = health_status
|
|
177
|
+
?? (health && !Array.isArray(health) ? health.status : null)
|
|
178
|
+
?? (Array.isArray(health) && health.length > 0 ? 'AVAILABLE' : 'UNKNOWN');
|
|
179
|
+
const projectedHealthAll = healthObservations.map(projectHealth).filter(Boolean);
|
|
180
|
+
const projectedJobs = Array.isArray(jobs) ? jobs : [];
|
|
181
|
+
const verified = projectedJobs.filter((job) => job?.status === 'done' && job?.task_status === 'success' && job?.delivery_complete === true).length;
|
|
182
|
+
const workerJobs = projectedJobs.filter((job) => job?.role === 'worker').length;
|
|
183
|
+
const reviewerJobs = projectedJobs.filter((job) => job?.role === 'reviewer').length;
|
|
184
|
+
const workerSelection = projectSelection(selections.worker ?? selections.flash);
|
|
185
|
+
const reviewerSelection = projectSelection(selections.reviewer ?? selections.pro);
|
|
186
|
+
const selectedRoutes = [workerSelection, reviewerSelection].filter(Boolean);
|
|
187
|
+
const selectedHealth = selectedRoutes.map((selection) => projectedHealthAll
|
|
188
|
+
.filter((entry) => entry.provider === selection.provider && entry.model === selection.model)
|
|
189
|
+
.sort((left, right) => (right.observed_at ?? 0) - (left.observed_at ?? 0))[0]).filter(Boolean);
|
|
190
|
+
const selectedKeys = new Set(selectedHealth.map((entry) => `${entry.provider}\u0000${entry.model}`));
|
|
191
|
+
const projectedHealth = [...selectedHealth, ...projectedHealthAll
|
|
192
|
+
.filter((entry) => !selectedKeys.has(`${entry.provider}\u0000${entry.model}`))]
|
|
193
|
+
.slice(0, MAX_HEALTH);
|
|
194
|
+
const modelCallability = projectModelCallability({ runtime, selections: { worker: workerSelection, reviewer: reviewerSelection }, health: projectedHealthAll, health_status: resolvedHealthStatus, jobs: projectedJobs, enabled_roles, now });
|
|
195
|
+
return {
|
|
196
|
+
schema_version: 1,
|
|
197
|
+
captured_at: now,
|
|
198
|
+
expires_at: modelCallability.expires_at,
|
|
199
|
+
health_status: resolvedHealthStatus,
|
|
200
|
+
runtime: projectRuntime(runtime),
|
|
201
|
+
runtime_identity: runtimeIdentity(runtime),
|
|
202
|
+
readiness_matrix: readinessMatrix && typeof readinessMatrix === 'object' ? structuredClone(readinessMatrix) : null,
|
|
203
|
+
provider_lifecycle: matrixRow(readinessMatrix, 'provider_lifecycle_consistent'),
|
|
204
|
+
health: projectedHealth,
|
|
205
|
+
worker: { selected: workerSelection, health: projectedHealth.filter((entry) => entry.provider === workerSelection?.provider && entry.model === workerSelection?.model) },
|
|
206
|
+
reviewer: { selected: reviewerSelection, health: projectedHealth.filter((entry) => entry.provider === reviewerSelection?.provider && entry.model === reviewerSelection?.model) },
|
|
207
|
+
historical_evidence: {
|
|
208
|
+
job_count: projectedJobs.length,
|
|
209
|
+
verified_job_count: verified,
|
|
210
|
+
worker_job_count: workerJobs,
|
|
211
|
+
reviewer_job_count: reviewerJobs,
|
|
212
|
+
},
|
|
213
|
+
workspace: projectWorkspace(workspace),
|
|
214
|
+
model_callability: modelCallability,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Re-project a Hub snapshot when the MCP session changes role enablement. */
|
|
219
|
+
export function reprojectRuntimeModelCallability(snapshot, { enabled_roles = {}, now = Date.now() } = {}) {
|
|
220
|
+
if (!snapshot || typeof snapshot !== 'object') return null;
|
|
221
|
+
const sourceProjection = snapshot.model_callability;
|
|
222
|
+
const sourceValidation = validateModelCallabilityV2({
|
|
223
|
+
projection: sourceProjection,
|
|
224
|
+
runtime: snapshot.runtime,
|
|
225
|
+
expectedEnabledRoles: sourceProjection?.enabled_roles,
|
|
226
|
+
expectedSelections: { worker: snapshot.worker?.selected ?? null, reviewer: snapshot.reviewer?.selected ?? null },
|
|
227
|
+
now,
|
|
228
|
+
});
|
|
229
|
+
if (!sourceValidation.ok) {
|
|
230
|
+
return projectModelCallability({
|
|
231
|
+
runtime: snapshot.runtime,
|
|
232
|
+
selections: { worker: snapshot.worker?.selected, reviewer: snapshot.reviewer?.selected },
|
|
233
|
+
health: [],
|
|
234
|
+
health_status: 'UNAVAILABLE',
|
|
235
|
+
jobs: [],
|
|
236
|
+
enabled_roles,
|
|
237
|
+
now,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
const roles = {};
|
|
241
|
+
for (const role of ['worker', 'reviewer']) {
|
|
242
|
+
if (enabled_roles?.[role] === false) {
|
|
243
|
+
roles[role] = { state: 'NOT_APPLICABLE', reason_code: 'ROLE_DISABLED', selected: null, last_success: null };
|
|
244
|
+
} else if (sourceProjection.enabled_roles?.[role] === true) {
|
|
245
|
+
roles[role] = structuredClone(sourceProjection.roles[role]);
|
|
246
|
+
} else {
|
|
247
|
+
roles[role] = { state: 'UNKNOWN', reason_code: 'ROLE_EVIDENCE_NOT_ENABLED', selected: snapshot[role]?.selected ?? null, last_success: null };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
const activeStates = Object.entries(roles).filter(([role]) => enabled_roles?.[role] === true).map(([, role]) => role.state);
|
|
251
|
+
const overall = activeStates.includes('NOT_CALLABLE') ? 'NOT_CALLABLE'
|
|
252
|
+
: activeStates.length > 0 && activeStates.every((state) => state === 'CALLABLE') ? 'CALLABLE'
|
|
253
|
+
: activeStates.includes('STALE') ? 'STALE' : 'UNKNOWN';
|
|
254
|
+
const expirations = Object.values(roles).map((role) => role?.expires_at).filter((value) => Number.isFinite(value));
|
|
255
|
+
const projected = {
|
|
256
|
+
...structuredClone(sourceProjection),
|
|
257
|
+
captured_at: now,
|
|
258
|
+
enabled_roles: { worker: enabled_roles.worker === true, reviewer: enabled_roles.reviewer === true },
|
|
259
|
+
roles,
|
|
260
|
+
overall,
|
|
261
|
+
expires_at: expirations.length ? Math.min(...expirations) : null,
|
|
262
|
+
};
|
|
263
|
+
const validation = validateModelCallabilityV2({
|
|
264
|
+
projection: projected,
|
|
265
|
+
runtime: snapshot.runtime,
|
|
266
|
+
expectedEnabledRoles: projected.enabled_roles,
|
|
267
|
+
expectedSelections: {
|
|
268
|
+
worker: enabled_roles.worker === true ? snapshot.worker?.selected ?? null : null,
|
|
269
|
+
reviewer: enabled_roles.reviewer === true ? snapshot.reviewer?.selected ?? null : null,
|
|
270
|
+
},
|
|
271
|
+
now,
|
|
272
|
+
});
|
|
273
|
+
if (!validation.ok) {
|
|
274
|
+
return projectModelCallability({
|
|
275
|
+
runtime: snapshot.runtime,
|
|
276
|
+
selections: { worker: snapshot.worker?.selected, reviewer: snapshot.reviewer?.selected },
|
|
277
|
+
health: [],
|
|
278
|
+
health_status: 'UNAVAILABLE',
|
|
279
|
+
jobs: [],
|
|
280
|
+
enabled_roles,
|
|
281
|
+
now,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
return projected;
|
|
285
|
+
}
|
package/src/server.mjs
CHANGED
|
@@ -9,8 +9,8 @@ import { hubCompatibilityMessage, resolveHubExecutionMode } from './hub-compatib
|
|
|
9
9
|
import { RUNTIME_VERSION, getHubRuntimeIdentity } from './runtime-identity.mjs';
|
|
10
10
|
import { resolveWorkerModel } from './model-routing.mjs';
|
|
11
11
|
import { runtimeActivationMetadata } from './runtime-controls.mjs';
|
|
12
|
-
import { buildConfigReadinessMatrix } from './config-readiness.mjs';
|
|
13
|
-
import { buildRuntimeReadinessSnapshot } from './runtime-readiness-snapshot.mjs';
|
|
12
|
+
import { buildConfigReadinessMatrix } from './config-readiness.mjs';
|
|
13
|
+
import { buildRuntimeReadinessSnapshot, reprojectRuntimeModelCallability } from './runtime-readiness-snapshot.mjs';
|
|
14
14
|
import { classifyFailure, classifyFailureCode } from './failure-classification.mjs';
|
|
15
15
|
import {
|
|
16
16
|
normalizeGlobalConfig,
|
|
@@ -57,9 +57,9 @@ const constraintsSchema = z.object({
|
|
|
57
57
|
// Initial values come from the global config (~/.config/dsh-crew/config.json,
|
|
58
58
|
// edited on the DSH settings page); dsh_worker_config overrides per session.
|
|
59
59
|
// All dispatch decisions (dsh_run_worker AND dsh_spawn_worker, hub AND
|
|
60
|
-
// production dispatches go through the 3210-only policy resolver in
|
|
61
|
-
// src/policy.mjs; the historical standalone path is retained only for
|
|
62
|
-
// read/migration compatibility and is never selected for execution.
|
|
60
|
+
// production dispatches go through the 3210-only policy resolver in
|
|
61
|
+
// src/policy.mjs; the historical standalone path is retained only for
|
|
62
|
+
// read/migration compatibility and is never selected for execution.
|
|
63
63
|
import { readGlobalConfig } from './install/install.mjs';
|
|
64
64
|
const initialGlobalConfig = normalizeGlobalConfig(readGlobalConfig());
|
|
65
65
|
const legacyDefaults = deriveLegacyConfig(initialGlobalConfig);
|
|
@@ -68,7 +68,7 @@ const sessionConfig = {
|
|
|
68
68
|
enabled: true,
|
|
69
69
|
default_tier: legacyDefaults.default_tier,
|
|
70
70
|
default_effort: legacyDefaults.default_effort,
|
|
71
|
-
mode: legacyDefaults.mode === 'standalone' ? 'hub' : legacyDefaults.mode, // auto | hub; legacy standalone is migrated to hub
|
|
71
|
+
mode: legacyDefaults.mode === 'standalone' ? 'hub' : legacyDefaults.mode, // auto | hub; legacy standalone is migrated to hub
|
|
72
72
|
default_timeout_seconds: legacyDefaults.default_timeout_seconds,
|
|
73
73
|
tier_policy: undefined,
|
|
74
74
|
escalate_on_failure: legacyDefaults.escalate_on_failure,
|
|
@@ -86,7 +86,7 @@ function resetSessionConfig() {
|
|
|
86
86
|
enabled: true,
|
|
87
87
|
default_tier: legacyDefaults.default_tier,
|
|
88
88
|
default_effort: legacyDefaults.default_effort,
|
|
89
|
-
mode: legacyDefaults.mode === 'standalone' ? 'hub' : legacyDefaults.mode,
|
|
89
|
+
mode: legacyDefaults.mode === 'standalone' ? 'hub' : legacyDefaults.mode,
|
|
90
90
|
default_timeout_seconds: legacyDefaults.default_timeout_seconds,
|
|
91
91
|
tier_policy: undefined,
|
|
92
92
|
escalate_on_failure: legacyDefaults.escalate_on_failure,
|
|
@@ -145,9 +145,9 @@ function dispatchDisabled() {
|
|
|
145
145
|
});
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
async function resolveMode() {
|
|
149
|
-
const status = await hubStatus();
|
|
150
|
-
const decision = resolveHubExecutionMode(sessionConfig.mode, status, { productionOnly: true });
|
|
148
|
+
async function resolveMode() {
|
|
149
|
+
const status = await hubStatus();
|
|
150
|
+
const decision = resolveHubExecutionMode(sessionConfig.mode, status, { productionOnly: true });
|
|
151
151
|
if (!decision.ok) {
|
|
152
152
|
throw Object.assign(new Error(decision.error), {
|
|
153
153
|
code: decision.code,
|
|
@@ -277,7 +277,7 @@ server.registerTool('dsh_worker_config', {
|
|
|
277
277
|
enabled: z.boolean().optional().describe('false = refuse all worker dispatch this session'),
|
|
278
278
|
default_tier: z.enum(['flash', 'pro']).optional(),
|
|
279
279
|
default_effort: z.enum(['off', 'high', 'max']).optional(),
|
|
280
|
-
mode: z.enum(['auto', 'hub']).optional().describe('Execution mode. Production dispatch
|
|
280
|
+
mode: z.enum(['auto', 'hub']).optional().describe('Execution mode. Production dispatch uses the official DeepSeek Harness dsh-crew profile on 3210 with the DSH Crew plugin loaded; standalone is retained only as a legacy read/migration value.'),
|
|
281
281
|
default_timeout_seconds: z.number().int().positive().max(7200).optional(),
|
|
282
282
|
tier_policy: z.enum(['auto', 'flash-only', 'pro-only']).optional().describe('session hard clamp: flash-only / pro-only pin every dispatch to one tier'),
|
|
283
283
|
escalate_on_failure: z.boolean().optional().describe('allow an unverified worker attempt to retry through the stronger model policy (applies to run and spawn)'),
|
|
@@ -312,6 +312,24 @@ const SAFE_GLOBAL_KEYS = [
|
|
|
312
312
|
'preset_flash', 'preset_pro',
|
|
313
313
|
];
|
|
314
314
|
|
|
315
|
+
function sameHubRuntimeIdentity(status, extension, snapshot) {
|
|
316
|
+
const runtime = extension?.runtime;
|
|
317
|
+
const projected = snapshot?.runtime;
|
|
318
|
+
return status?.compatible === true
|
|
319
|
+
&& extension?.schema_version === 1
|
|
320
|
+
&& extension?.kind === 'dsh-crew-extension'
|
|
321
|
+
&& snapshot?.schema_version === 1
|
|
322
|
+
&& runtime?.runtime_id === status.runtime_id
|
|
323
|
+
&& runtime?.protocol_version === status.protocol_version
|
|
324
|
+
&& runtime?.execution_plane === status.execution_plane
|
|
325
|
+
&& runtime?.profile === status.profile
|
|
326
|
+
&& runtime?.listen_port === status.listen_port
|
|
327
|
+
&& projected?.runtime_id === runtime.runtime_id
|
|
328
|
+
&& projected?.execution_plane === runtime.execution_plane
|
|
329
|
+
&& projected?.profile === runtime.profile
|
|
330
|
+
&& projected?.listen_port === runtime.listen_port;
|
|
331
|
+
}
|
|
332
|
+
|
|
315
333
|
async function buildConfigReport() {
|
|
316
334
|
const globalConfig = currentGlobalConfig();
|
|
317
335
|
const legacy = deriveLegacyConfig(globalConfig);
|
|
@@ -324,19 +342,27 @@ async function buildConfigReport() {
|
|
|
324
342
|
const mainAgentMode = sessionConfig.main_agent_mode ?? globalConfig.main_agent_mode;
|
|
325
343
|
const runtimeControls = workflowRuntime.refreshRuntimeControls();
|
|
326
344
|
const activationBoundaries = globalConfig.config_activation ?? runtimeActivationMetadata();
|
|
327
|
-
const hubCompatibility = await hubStatus({ force: true });
|
|
345
|
+
const hubCompatibility = await hubStatus({ force: true, base: globalConfig.hub_url });
|
|
328
346
|
let effectiveWorkerProvider = null;
|
|
329
347
|
let effectiveWorkerSelection = { flash: null, pro: null };
|
|
330
348
|
let providerResolutionError;
|
|
331
|
-
let providerCatalogChecked = false;
|
|
332
|
-
let providerCatalogBody = null;
|
|
333
|
-
let
|
|
334
|
-
let
|
|
335
|
-
let providerHealthChecked = false;
|
|
336
|
-
let providerHealthBody = null;
|
|
337
|
-
let hubJobsChecked = false;
|
|
338
|
-
let hubJobsBody = null;
|
|
349
|
+
let providerCatalogChecked = false;
|
|
350
|
+
let providerCatalogBody = null;
|
|
351
|
+
let hubExtension = null;
|
|
352
|
+
let hubReadinessSnapshot = null;
|
|
339
353
|
const workerProviderMode = globalConfig.worker_provider_mode ?? 'deepseek-official';
|
|
354
|
+
if (hubCompatibility.compatible) {
|
|
355
|
+
try {
|
|
356
|
+
const extensionRes = await fetch(`${globalConfig.hub_url}/_dsh/dsh-crew/extension`, { signal: AbortSignal.timeout(800) });
|
|
357
|
+
const extensionBody = await extensionRes.json();
|
|
358
|
+
const candidateExtension = extensionBody?.extension;
|
|
359
|
+
const candidateSnapshot = candidateExtension?.readiness_snapshot;
|
|
360
|
+
if (extensionRes.ok && extensionBody?.ok === true && sameHubRuntimeIdentity(hubCompatibility, candidateExtension, candidateSnapshot)) {
|
|
361
|
+
hubExtension = candidateExtension;
|
|
362
|
+
hubReadinessSnapshot = candidateSnapshot;
|
|
363
|
+
}
|
|
364
|
+
} catch { /* canonical Hub readiness remains unavailable; fallback stays conservative */ }
|
|
365
|
+
}
|
|
340
366
|
if (workerProviderMode === 'deepseek-official') {
|
|
341
367
|
effectiveWorkerSelection = {
|
|
342
368
|
flash: { provider: 'deepseek-official', model: 'deepseek-v4-flash', source: 'legacy-strict' },
|
|
@@ -369,61 +395,45 @@ async function buildConfigReport() {
|
|
|
369
395
|
} else if (hubCompatibility.reachable) {
|
|
370
396
|
providerResolutionError = hubCompatibilityMessage(hubCompatibility);
|
|
371
397
|
}
|
|
372
|
-
if (
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
providerInventoryBody = inventoryRes.ok
|
|
377
|
-
? await inventoryRes.json()
|
|
378
|
-
: { ok: false, code: 'PROVIDER_INVENTORY_UNAVAILABLE' };
|
|
379
|
-
} catch {
|
|
380
|
-
providerInventoryChecked = true;
|
|
381
|
-
providerInventoryBody = { ok: false, code: 'PROVIDER_INVENTORY_UNAVAILABLE' };
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
if (hubCompatibility.compatible) {
|
|
385
|
-
try {
|
|
386
|
-
providerHealthChecked = true;
|
|
387
|
-
const healthRes = await fetch(`${globalConfig.hub_url}/_dsh/dsh-crew/provider-health`, { signal: AbortSignal.timeout(800) });
|
|
388
|
-
providerHealthBody = healthRes.ok ? await healthRes.json() : { ok: false, code: 'PROVIDER_HEALTH_UNAVAILABLE' };
|
|
389
|
-
} catch {
|
|
390
|
-
providerHealthChecked = true;
|
|
391
|
-
providerHealthBody = { ok: false, code: 'PROVIDER_HEALTH_UNAVAILABLE' };
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
if (hubCompatibility.compatible) {
|
|
395
|
-
try {
|
|
396
|
-
hubJobsChecked = true;
|
|
397
|
-
const jobsRes = await fetch(`${globalConfig.hub_url}/_dsh/dsh-crew/jobs`, { signal: AbortSignal.timeout(800) });
|
|
398
|
-
hubJobsBody = await jobsRes.json();
|
|
399
|
-
} catch {
|
|
400
|
-
hubJobsChecked = true;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
effectiveWorkerProvider = effectiveWorkerSelection.flash?.provider ?? null;
|
|
404
|
-
const readinessMatrix = buildConfigReadinessMatrix({
|
|
398
|
+
if (hubReadinessSnapshot?.worker?.selected) effectiveWorkerSelection.worker = hubReadinessSnapshot.worker.selected;
|
|
399
|
+
if (hubReadinessSnapshot?.reviewer?.selected) effectiveWorkerSelection.reviewer = hubReadinessSnapshot.reviewer.selected;
|
|
400
|
+
effectiveWorkerProvider = effectiveWorkerSelection.worker?.provider ?? effectiveWorkerSelection.flash?.provider ?? null;
|
|
401
|
+
const fallbackReadinessMatrix = buildConfigReadinessMatrix({
|
|
405
402
|
hubCompatibility,
|
|
406
403
|
workerProviderMode,
|
|
407
|
-
providerCatalogChecked,
|
|
408
|
-
providerCatalogBody,
|
|
409
|
-
providerInventoryChecked,
|
|
410
|
-
providerInventoryBody,
|
|
411
|
-
providerHealthChecked,
|
|
412
|
-
providerHealthBody,
|
|
413
|
-
hubJobsChecked,
|
|
414
|
-
hubJobsBody,
|
|
404
|
+
providerCatalogChecked,
|
|
405
|
+
providerCatalogBody,
|
|
415
406
|
});
|
|
407
|
+
const readinessMatrix = hubReadinessSnapshot?.readiness_matrix ?? fallbackReadinessMatrix;
|
|
416
408
|
const roleProfiles = loadRoleProfiles();
|
|
417
|
-
const workspaceReadiness = await assessWorkspaceReadiness({ cwd: process.cwd() });
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
409
|
+
const workspaceReadiness = await assessWorkspaceReadiness({ cwd: process.cwd() });
|
|
410
|
+
const enabledRoles = {
|
|
411
|
+
worker: sessionConfig.enabled !== false && globalConfig.subagents_enabled !== false && globalConfig.worker_state !== 'disabled',
|
|
412
|
+
reviewer: sessionConfig.enabled !== false && globalConfig.subagents_enabled !== false && globalConfig.review_state !== 'disabled',
|
|
413
|
+
};
|
|
414
|
+
const readinessSnapshot = hubReadinessSnapshot
|
|
415
|
+
? (() => {
|
|
416
|
+
const snapshot = {
|
|
417
|
+
...structuredClone(hubReadinessSnapshot),
|
|
418
|
+
readiness_matrix: readinessMatrix,
|
|
419
|
+
workspace: {
|
|
420
|
+
status: workspaceReadiness.status ?? (workspaceReadiness.ok === true ? 'READY' : 'UNAVAILABLE'),
|
|
421
|
+
reason_code: workspaceReadiness.reason_code ?? workspaceReadiness.code ?? 'WORKSPACE_NOT_CHECKED',
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
const modelCallability = reprojectRuntimeModelCallability(snapshot, { enabled_roles });
|
|
425
|
+
return modelCallability ? { ...snapshot, model_callability: modelCallability } : snapshot;
|
|
426
|
+
})()
|
|
427
|
+
: buildRuntimeReadinessSnapshot({
|
|
428
|
+
runtime: getHubRuntimeIdentity(),
|
|
429
|
+
readinessMatrix,
|
|
430
|
+
selections: { worker: effectiveWorkerSelection.worker ?? effectiveWorkerSelection.flash, reviewer: effectiveWorkerSelection.reviewer ?? effectiveWorkerSelection.pro },
|
|
431
|
+
health_status: 'UNAVAILABLE',
|
|
432
|
+
workspace: workspaceReadiness,
|
|
433
|
+
enabled_roles: enabledRoles,
|
|
434
|
+
});
|
|
435
|
+
const extensionRuntime = hubExtension?.runtime ?? getHubRuntimeIdentity();
|
|
436
|
+
const extensionContract = buildExtensionContract({
|
|
427
437
|
config: {
|
|
428
438
|
...globalConfig,
|
|
429
439
|
subagents_enabled: sessionConfig.enabled !== false && globalConfig.subagents_enabled !== false,
|
|
@@ -431,11 +441,11 @@ async function buildConfigReport() {
|
|
|
431
441
|
review_state: globalConfig.review_state,
|
|
432
442
|
escalate_on_failure: sessionConfig.escalate_on_failure ?? legacy.escalate_on_failure,
|
|
433
443
|
},
|
|
434
|
-
readinessMatrix,
|
|
435
|
-
readinessSnapshot,
|
|
444
|
+
readinessMatrix,
|
|
445
|
+
readinessSnapshot,
|
|
436
446
|
workspace: workspaceReadiness,
|
|
437
447
|
profiles: roleProfiles,
|
|
438
|
-
runtime:
|
|
448
|
+
runtime: extensionRuntime,
|
|
439
449
|
});
|
|
440
450
|
return {
|
|
441
451
|
enabled: sessionConfig.enabled,
|
|
@@ -474,8 +484,8 @@ async function buildConfigReport() {
|
|
|
474
484
|
global_defaults: Object.fromEntries(SAFE_GLOBAL_KEYS.map((k) => [k, globalConfig[k]])),
|
|
475
485
|
runtime_controls: runtimeControls,
|
|
476
486
|
activation_boundaries: activationBoundaries,
|
|
477
|
-
readiness_matrix: readinessMatrix,
|
|
478
|
-
readiness_snapshot: readinessSnapshot,
|
|
487
|
+
readiness_matrix: readinessMatrix,
|
|
488
|
+
readiness_snapshot: readinessSnapshot,
|
|
479
489
|
role_profiles: roleProfiles,
|
|
480
490
|
extension_contract: extensionContract,
|
|
481
491
|
hub_reachable: hubCompatibility.reachable,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Keep Hub-only MCP clients independent of optional standalone host peers. */
|
|
2
|
+
export function createStandaloneHarness(options, { load = () => import('@deepseek-ai/dsh-sdk-client') } = {}) {
|
|
3
|
+
let loading;
|
|
4
|
+
let harness;
|
|
5
|
+
let closed = false;
|
|
6
|
+
return {
|
|
7
|
+
async run(...args) {
|
|
8
|
+
if (closed) throw Error('standalone job cancelled before startup');
|
|
9
|
+
loading ??= load().catch(error => {
|
|
10
|
+
if (error?.code !== 'ERR_MODULE_NOT_FOUND' && error?.code !== 'MODULE_NOT_FOUND') throw error;
|
|
11
|
+
throw Object.assign(new Error('Standalone DSH SDK is unavailable. Repair the Crew-owned installation before using standalone mode; Hub mode does not require this SDK.'), { code: 'STANDALONE_SDK_UNAVAILABLE' });
|
|
12
|
+
});
|
|
13
|
+
const { DeepSeekHarness } = await loading;
|
|
14
|
+
if (closed) throw Error('standalone job cancelled before startup');
|
|
15
|
+
harness ??= new DeepSeekHarness(options);
|
|
16
|
+
return harness.run(...args);
|
|
17
|
+
},
|
|
18
|
+
async close() {
|
|
19
|
+
closed = true;
|
|
20
|
+
if (harness) await harness.close();
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|