@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
package/src/provider-health.mjs
CHANGED
|
@@ -1,124 +1,130 @@
|
|
|
1
|
-
// Bounded provider/model callability evidence.
|
|
2
|
-
//
|
|
3
|
-
// Catalog presence is not proof of a callable route. This store keeps only
|
|
4
|
-
// sanitized state and timestamps; raw provider errors never leave the caller.
|
|
5
|
-
|
|
6
|
-
export const PROVIDER_HEALTH_STATES = Object.freeze([
|
|
7
|
-
'callable', 'credential-missing', 'quota-exhausted', 'rate-limited',
|
|
8
|
-
'timeout', 'internal-error', 'disabled', 'not-configured', 'tombstoned', 'unprobed',
|
|
9
|
-
]);
|
|
10
|
-
|
|
11
|
-
const REASON_CODES = Object.freeze({
|
|
12
|
-
callable: 'PROVIDER_CALLABLE',
|
|
13
|
-
'credential-missing': 'CREDENTIAL_MISSING',
|
|
14
|
-
'quota-exhausted': 'QUOTA_EXHAUSTED',
|
|
15
|
-
'rate-limited': 'RATE_LIMITED',
|
|
16
|
-
timeout: 'PROBE_TIMEOUT',
|
|
17
|
-
'internal-error': 'PROVIDER_INTERNAL_ERROR',
|
|
18
|
-
disabled: 'PROVIDER_DISABLED',
|
|
19
|
-
'not-configured': 'PROVIDER_NOT_CONFIGURED',
|
|
20
|
-
tombstoned: 'PROVIDER_TOMBSTONED',
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const DEFAULT_TTLS = Object.freeze({
|
|
24
|
-
callable: 5 * 60 * 1000,
|
|
25
|
-
'credential-missing': 30 * 1000,
|
|
26
|
-
'quota-exhausted': 15 * 60 * 1000,
|
|
27
|
-
'rate-limited': 60 * 1000,
|
|
28
|
-
timeout: 60 * 1000,
|
|
29
|
-
'internal-error': 60 * 1000,
|
|
30
|
-
disabled: 5 * 60 * 1000,
|
|
31
|
-
'not-configured': 30 * 1000,
|
|
32
|
-
tombstoned: 5 * 60 * 1000,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
function text(value) {
|
|
36
|
-
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function providerKey(provider, model) {
|
|
40
|
-
const p = text(provider);
|
|
41
|
-
const m = text(model);
|
|
42
|
-
return p && m ? `${p}\u0000${m}` : null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function errorText(error) {
|
|
46
|
-
if (!error || typeof error !== 'object') return '';
|
|
47
|
-
return [error.code, error.name, error.message, error.status].filter((value) => value !== undefined && value !== null).join(' ').toLowerCase();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Map an upstream error to one bounded, secret-free callability state. */
|
|
51
|
-
export function classifyProviderError(error = {}) {
|
|
52
|
-
const textValue = errorText(error);
|
|
53
|
-
const code = text(error?.code)?.toUpperCase() ?? '';
|
|
54
|
-
if (code === 'MISSING_CREDENTIAL' || code === 'CREDENTIAL_MISSING' || /no (api )?key|credential.*missing|missing.*credential/.test(textValue)) {
|
|
55
|
-
return { state: 'credential-missing', reason_code: REASON_CODES['credential-missing'] };
|
|
56
|
-
}
|
|
57
|
-
if (code === 'QUOTA_EXCEEDED' || /monthly|account/.test(textValue) && /quota|exhaust|limit/.test(textValue)) {
|
|
58
|
-
return { state: 'quota-exhausted', reason_code: REASON_CODES['quota-exhausted'] };
|
|
59
|
-
}
|
|
60
|
-
if (Number(error?.status) === 429 || code === 'RATE_LIMITED' || /rate.?limit|too many requests/.test(textValue)) {
|
|
61
|
-
return { state: 'rate-limited', reason_code: REASON_CODES['rate-limited'] };
|
|
62
|
-
}
|
|
63
|
-
if (error?.name === 'TimeoutError' || error?.name === 'AbortError' || code === 'TIMEOUT' || /timed? out|timeout|aborted/.test(textValue)) {
|
|
64
|
-
return { state: 'timeout', reason_code: REASON_CODES.timeout };
|
|
65
|
-
}
|
|
66
|
-
return { state: 'internal-error', reason_code: REASON_CODES['internal-error'] };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function boundedTimestamp(value, fallback) {
|
|
70
|
-
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function view(entry, now) {
|
|
74
|
-
if (!entry) return { state: 'unprobed', reason_code: null, observed_at: null, expires_at: null, fresh: false };
|
|
75
|
-
const fresh = now < entry.expires_at;
|
|
76
|
-
if (!fresh) return { state: 'unprobed', reason_code: null, observed_at: entry.observed_at, expires_at: entry.expires_at, fresh: false };
|
|
77
|
-
return { ...entry, fresh: true };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function createProviderHealthStore({ clock = () => Date.now(), ttls = {}, maxEntries = 256 } = {}) {
|
|
81
|
-
const records = new Map();
|
|
82
|
-
const limits = { ...DEFAULT_TTLS };
|
|
83
|
-
for (const state of PROVIDER_HEALTH_STATES) {
|
|
84
|
-
if (Number.isFinite(ttls[state]) && ttls[state] >= 0) limits[state] = Math.min(ttls[state], 24 * 60 * 60 * 1000);
|
|
85
|
-
}
|
|
86
|
-
const boundedMax = Number.isInteger(maxEntries) && maxEntries > 0 ? Math.min(maxEntries, 2048) : 256;
|
|
87
|
-
|
|
88
|
-
const get = (provider, model) => {
|
|
89
|
-
const key = providerKey(provider, model);
|
|
90
|
-
if (!key) return { provider: text(provider), model: text(model), state: 'unprobed', reason_code: null, observed_at: null, expires_at: null, fresh: false };
|
|
91
|
-
const entry = records.get(key);
|
|
92
|
-
const current = view(entry, clock());
|
|
93
|
-
if (entry && !current.fresh) records.delete(key);
|
|
94
|
-
return { provider: text(provider), model: text(model), ...current };
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const record = (provider, model, result = {}) => {
|
|
98
|
-
const key = providerKey(provider, model);
|
|
99
|
-
if (!key) return null;
|
|
100
|
-
const now = clock();
|
|
101
|
-
const observed = boundedTimestamp(result.observed_at, now);
|
|
102
|
-
const previous = records.get(key);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (result.
|
|
106
|
-
else
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
1
|
+
// Bounded provider/model callability evidence.
|
|
2
|
+
//
|
|
3
|
+
// Catalog presence is not proof of a callable route. This store keeps only
|
|
4
|
+
// sanitized state and timestamps; raw provider errors never leave the caller.
|
|
5
|
+
|
|
6
|
+
export const PROVIDER_HEALTH_STATES = Object.freeze([
|
|
7
|
+
'callable', 'credential-missing', 'quota-exhausted', 'rate-limited',
|
|
8
|
+
'timeout', 'internal-error', 'disabled', 'not-configured', 'tombstoned', 'unprobed',
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
const REASON_CODES = Object.freeze({
|
|
12
|
+
callable: 'PROVIDER_CALLABLE',
|
|
13
|
+
'credential-missing': 'CREDENTIAL_MISSING',
|
|
14
|
+
'quota-exhausted': 'QUOTA_EXHAUSTED',
|
|
15
|
+
'rate-limited': 'RATE_LIMITED',
|
|
16
|
+
timeout: 'PROBE_TIMEOUT',
|
|
17
|
+
'internal-error': 'PROVIDER_INTERNAL_ERROR',
|
|
18
|
+
disabled: 'PROVIDER_DISABLED',
|
|
19
|
+
'not-configured': 'PROVIDER_NOT_CONFIGURED',
|
|
20
|
+
tombstoned: 'PROVIDER_TOMBSTONED',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const DEFAULT_TTLS = Object.freeze({
|
|
24
|
+
callable: 5 * 60 * 1000,
|
|
25
|
+
'credential-missing': 30 * 1000,
|
|
26
|
+
'quota-exhausted': 15 * 60 * 1000,
|
|
27
|
+
'rate-limited': 60 * 1000,
|
|
28
|
+
timeout: 60 * 1000,
|
|
29
|
+
'internal-error': 60 * 1000,
|
|
30
|
+
disabled: 5 * 60 * 1000,
|
|
31
|
+
'not-configured': 30 * 1000,
|
|
32
|
+
tombstoned: 5 * 60 * 1000,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
function text(value) {
|
|
36
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function providerKey(provider, model) {
|
|
40
|
+
const p = text(provider);
|
|
41
|
+
const m = text(model);
|
|
42
|
+
return p && m ? `${p}\u0000${m}` : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function errorText(error) {
|
|
46
|
+
if (!error || typeof error !== 'object') return '';
|
|
47
|
+
return [error.code, error.name, error.message, error.status].filter((value) => value !== undefined && value !== null).join(' ').toLowerCase();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Map an upstream error to one bounded, secret-free callability state. */
|
|
51
|
+
export function classifyProviderError(error = {}) {
|
|
52
|
+
const textValue = errorText(error);
|
|
53
|
+
const code = text(error?.code)?.toUpperCase() ?? '';
|
|
54
|
+
if (code === 'MISSING_CREDENTIAL' || code === 'CREDENTIAL_MISSING' || /no (api )?key|credential.*missing|missing.*credential/.test(textValue)) {
|
|
55
|
+
return { state: 'credential-missing', reason_code: REASON_CODES['credential-missing'] };
|
|
56
|
+
}
|
|
57
|
+
if (code === 'QUOTA_EXCEEDED' || /monthly|account/.test(textValue) && /quota|exhaust|limit/.test(textValue)) {
|
|
58
|
+
return { state: 'quota-exhausted', reason_code: REASON_CODES['quota-exhausted'] };
|
|
59
|
+
}
|
|
60
|
+
if (Number(error?.status) === 429 || code === 'RATE_LIMITED' || /rate.?limit|too many requests/.test(textValue)) {
|
|
61
|
+
return { state: 'rate-limited', reason_code: REASON_CODES['rate-limited'] };
|
|
62
|
+
}
|
|
63
|
+
if (error?.name === 'TimeoutError' || error?.name === 'AbortError' || code === 'TIMEOUT' || /timed? out|timeout|aborted/.test(textValue)) {
|
|
64
|
+
return { state: 'timeout', reason_code: REASON_CODES.timeout };
|
|
65
|
+
}
|
|
66
|
+
return { state: 'internal-error', reason_code: REASON_CODES['internal-error'] };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function boundedTimestamp(value, fallback) {
|
|
70
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function view(entry, now) {
|
|
74
|
+
if (!entry) return { state: 'unprobed', reason_code: null, observed_at: null, expires_at: null, fresh: false };
|
|
75
|
+
const fresh = now < entry.expires_at;
|
|
76
|
+
if (!fresh) return { state: 'unprobed', reason_code: null, observed_at: entry.observed_at, expires_at: entry.expires_at, fresh: false };
|
|
77
|
+
return { ...entry, fresh: true };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function createProviderHealthStore({ clock = () => Date.now(), ttls = {}, maxEntries = 256 } = {}) {
|
|
81
|
+
const records = new Map();
|
|
82
|
+
const limits = { ...DEFAULT_TTLS };
|
|
83
|
+
for (const state of PROVIDER_HEALTH_STATES) {
|
|
84
|
+
if (Number.isFinite(ttls[state]) && ttls[state] >= 0) limits[state] = Math.min(ttls[state], 24 * 60 * 60 * 1000);
|
|
85
|
+
}
|
|
86
|
+
const boundedMax = Number.isInteger(maxEntries) && maxEntries > 0 ? Math.min(maxEntries, 2048) : 256;
|
|
87
|
+
|
|
88
|
+
const get = (provider, model) => {
|
|
89
|
+
const key = providerKey(provider, model);
|
|
90
|
+
if (!key) return { provider: text(provider), model: text(model), state: 'unprobed', reason_code: null, observed_at: null, expires_at: null, fresh: false };
|
|
91
|
+
const entry = records.get(key);
|
|
92
|
+
const current = view(entry, clock());
|
|
93
|
+
if (entry && !current.fresh) records.delete(key);
|
|
94
|
+
return { provider: text(provider), model: text(model), ...current };
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const record = (provider, model, result = {}) => {
|
|
98
|
+
const key = providerKey(provider, model);
|
|
99
|
+
if (!key) return null;
|
|
100
|
+
const now = clock();
|
|
101
|
+
const observed = boundedTimestamp(result.observed_at, now);
|
|
102
|
+
const previous = records.get(key);
|
|
103
|
+
let classified;
|
|
104
|
+
if (result.ok === true) classified = { state: 'callable', reason_code: REASON_CODES.callable };
|
|
105
|
+
else if (PROVIDER_HEALTH_STATES.includes(result.state) && result.state !== 'unprobed') classified = { state: result.state, reason_code: REASON_CODES[result.state] ?? null };
|
|
106
|
+
else classified = classifyProviderError(result.error ?? result);
|
|
107
|
+
if (previous && previous.observed_at > observed) return get(provider, model);
|
|
108
|
+
if (previous && previous.observed_at === observed) {
|
|
109
|
+
const previousFailure = previous.state !== 'callable';
|
|
110
|
+
const incomingFailure = classified.state !== 'callable';
|
|
111
|
+
if (previousFailure && !incomingFailure) return get(provider, model);
|
|
112
|
+
if (previousFailure && incomingFailure && String(previous.state).localeCompare(String(classified.state)) <= 0) return get(provider, model);
|
|
113
|
+
}
|
|
114
|
+
const expires = observed + limits[classified.state];
|
|
115
|
+
records.set(key, {
|
|
116
|
+
provider: text(provider), model: text(model), ...classified,
|
|
117
|
+
observed_at: observed, expires_at: expires,
|
|
118
|
+
});
|
|
119
|
+
while (records.size > boundedMax) {
|
|
120
|
+
const oldest = [...records.entries()].sort((a, b) => a[1].observed_at - b[1].observed_at)[0]?.[0];
|
|
121
|
+
if (!oldest) break;
|
|
122
|
+
records.delete(oldest);
|
|
123
|
+
}
|
|
124
|
+
return get(provider, model);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const list = () => [...records.values()].map((entry) => view(entry, clock())).filter((entry) => entry.fresh).slice(0, boundedMax);
|
|
128
|
+
|
|
129
|
+
return { get, record, list, ttls: { ...limits }, maxEntries: boundedMax };
|
|
130
|
+
}
|
package/src/readiness-matrix.mjs
CHANGED
|
@@ -13,11 +13,11 @@ export const READINESS_REASON_CODES = Object.freeze({
|
|
|
13
13
|
HUB_INCOMPATIBLE: 'HUB_INCOMPATIBLE',
|
|
14
14
|
PROVIDER_CATALOG_RESOLVED: 'PROVIDER_CATALOG_RESOLVED',
|
|
15
15
|
PROVIDER_CATALOG_UNAVAILABLE: 'PROVIDER_CATALOG_UNAVAILABLE',
|
|
16
|
-
PROVIDER_CATALOG_HEALTH_WARNING: 'PROVIDER_CATALOG_HEALTH_WARNING',
|
|
17
|
-
PROVIDER_CATALOG_NOT_REQUIRED: 'PROVIDER_CATALOG_NOT_REQUIRED',
|
|
18
|
-
PROVIDER_LIFECYCLE_CONSISTENT: 'PROVIDER_LIFECYCLE_CONSISTENT',
|
|
19
|
-
PROVIDER_LIFECYCLE_INCONSISTENT: 'PROVIDER_LIFECYCLE_INCONSISTENT',
|
|
20
|
-
PROVIDER_INVENTORY_UNAVAILABLE: 'PROVIDER_INVENTORY_UNAVAILABLE',
|
|
16
|
+
PROVIDER_CATALOG_HEALTH_WARNING: 'PROVIDER_CATALOG_HEALTH_WARNING',
|
|
17
|
+
PROVIDER_CATALOG_NOT_REQUIRED: 'PROVIDER_CATALOG_NOT_REQUIRED',
|
|
18
|
+
PROVIDER_LIFECYCLE_CONSISTENT: 'PROVIDER_LIFECYCLE_CONSISTENT',
|
|
19
|
+
PROVIDER_LIFECYCLE_INCONSISTENT: 'PROVIDER_LIFECYCLE_INCONSISTENT',
|
|
20
|
+
PROVIDER_INVENTORY_UNAVAILABLE: 'PROVIDER_INVENTORY_UNAVAILABLE',
|
|
21
21
|
PROVIDER_MODE_UNKNOWN: 'PROVIDER_MODE_UNKNOWN',
|
|
22
22
|
NO_CI_EVIDENCE: 'NO_CI_EVIDENCE',
|
|
23
23
|
NO_EXECUTION_EVIDENCE: 'NO_EXECUTION_EVIDENCE',
|
|
@@ -29,16 +29,18 @@ const TARGET_ROWS = Object.freeze([
|
|
|
29
29
|
['linux_deterministic', 'ci'],
|
|
30
30
|
['windows_regressions', 'ci'],
|
|
31
31
|
['macos_smoke', 'ci'],
|
|
32
|
-
['hub_compatibility', 'live-runtime'],
|
|
33
|
-
['provider_catalog', 'live-runtime'],
|
|
34
|
-
['
|
|
35
|
-
['
|
|
36
|
-
['
|
|
37
|
-
['
|
|
38
|
-
['
|
|
39
|
-
['
|
|
40
|
-
['
|
|
41
|
-
['
|
|
32
|
+
['hub_compatibility', 'live-runtime'],
|
|
33
|
+
['provider_catalog', 'live-runtime'],
|
|
34
|
+
['provider_health', 'live-runtime'],
|
|
35
|
+
['reviewer_health', 'live-runtime'],
|
|
36
|
+
['model_execution', 'real-execution'],
|
|
37
|
+
['worker_primary_callable', 'real-execution'],
|
|
38
|
+
['worker_escalation_callable', 'real-execution'],
|
|
39
|
+
['reviewer_primary_callable', 'real-execution'],
|
|
40
|
+
['provider_lifecycle_consistent', 'live-runtime'],
|
|
41
|
+
['deepseek_flash', 'real-execution'],
|
|
42
|
+
['deepseek_pro', 'real-execution'],
|
|
43
|
+
['reviewer_pipeline', 'real-execution'],
|
|
42
44
|
['cancellation_timeout_escalation', 'real-execution'],
|
|
43
45
|
['standalone_official', 'real-execution'],
|
|
44
46
|
]);
|
|
@@ -71,6 +73,9 @@ function normalizeEvidence(row, evidence) {
|
|
|
71
73
|
...(typeof evidence.evidence_ref === 'string' && evidence.evidence_ref.trim()
|
|
72
74
|
? { evidence_ref: evidence.evidence_ref.trim() }
|
|
73
75
|
: {}),
|
|
76
|
+
...(typeof evidence.detail_code === 'string' && /^[A-Z][A-Z0-9_]{0,127}$/u.test(evidence.detail_code)
|
|
77
|
+
? { detail_code: evidence.detail_code }
|
|
78
|
+
: {}),
|
|
74
79
|
};
|
|
75
80
|
}
|
|
76
81
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Browser-safe runtime identity primitives shared by the Hub, extension
|
|
2
|
+
// contract and client. Do not coerce missing values: an incomplete identity
|
|
3
|
+
// is never evidence of the native 3210 Crew runtime.
|
|
4
|
+
|
|
5
|
+
export const PRODUCTION_EXECUTION_PLANE = 'hub-3210';
|
|
6
|
+
export const PRODUCTION_PROFILE = 'dsh-crew';
|
|
7
|
+
export const PRODUCTION_LISTEN_PORT = 3210;
|
|
8
|
+
|
|
9
|
+
function nonEmpty(value) {
|
|
10
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isCompleteRuntimeIdentity(identity) {
|
|
14
|
+
return Boolean(identity && typeof identity === 'object'
|
|
15
|
+
&& nonEmpty(identity.execution_plane)
|
|
16
|
+
&& nonEmpty(identity.profile)
|
|
17
|
+
&& Number.isInteger(identity.listen_port) && identity.listen_port > 0
|
|
18
|
+
&& nonEmpty(identity.runtime_id));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function sameCompleteRuntimeIdentity(left, right) {
|
|
22
|
+
return isCompleteRuntimeIdentity(left) && isCompleteRuntimeIdentity(right)
|
|
23
|
+
&& left.execution_plane === right.execution_plane
|
|
24
|
+
&& left.profile === right.profile
|
|
25
|
+
&& left.listen_port === right.listen_port
|
|
26
|
+
&& left.runtime_id === right.runtime_id;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isExactNativeCrewIdentity(identity) {
|
|
30
|
+
return isCompleteRuntimeIdentity(identity)
|
|
31
|
+
&& identity.execution_plane === PRODUCTION_EXECUTION_PLANE
|
|
32
|
+
&& identity.profile === PRODUCTION_PROFILE
|
|
33
|
+
&& identity.listen_port === PRODUCTION_LISTEN_PORT;
|
|
34
|
+
}
|
package/src/runtime-identity.mjs
CHANGED
|
@@ -17,16 +17,26 @@ import { randomUUID } from 'node:crypto';
|
|
|
17
17
|
import { existsSync, readFileSync } from 'node:fs';
|
|
18
18
|
import { homedir } from 'node:os';
|
|
19
19
|
import { join } from 'node:path';
|
|
20
|
+
import {
|
|
21
|
+
PRODUCTION_EXECUTION_PLANE,
|
|
22
|
+
PRODUCTION_PROFILE,
|
|
23
|
+
PRODUCTION_LISTEN_PORT,
|
|
24
|
+
} from './runtime-identity-contract.mjs';
|
|
25
|
+
export {
|
|
26
|
+
PRODUCTION_EXECUTION_PLANE,
|
|
27
|
+
PRODUCTION_PROFILE,
|
|
28
|
+
PRODUCTION_LISTEN_PORT,
|
|
29
|
+
isCompleteRuntimeIdentity,
|
|
30
|
+
sameCompleteRuntimeIdentity,
|
|
31
|
+
isExactNativeCrewIdentity,
|
|
32
|
+
} from './runtime-identity-contract.mjs';
|
|
20
33
|
|
|
21
34
|
// Production Crew execution is intentionally bound to the isolated 3210 Hub.
|
|
22
35
|
// These fields are public provenance only; no credential or session secret is
|
|
23
36
|
// included in the identity contract.
|
|
24
|
-
export const PRODUCTION_EXECUTION_PLANE = 'hub-3210';
|
|
25
|
-
export const PRODUCTION_PROFILE = 'dsh-crew';
|
|
26
|
-
export const PRODUCTION_LISTEN_PORT = 3210;
|
|
27
37
|
const RUNTIME_ID = randomUUID();
|
|
28
38
|
|
|
29
|
-
export const RUNTIME_VERSION = '1.
|
|
39
|
+
export const RUNTIME_VERSION = '1.2.0-rc.4';
|
|
30
40
|
export const HUB_PROTOCOL_VERSION = 1;
|
|
31
41
|
|
|
32
42
|
export const HUB_CAPABILITIES = Object.freeze([
|