@ran-sh/dsh-crew 1.10.7 → 1.10.8
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/package.json
CHANGED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Drives the installed release's own MCP server over stdio, so the client-side
|
|
2
|
+
// dispatch path is exercised by the shipped code rather than by a long-running
|
|
3
|
+
// session whose MCP process predates the fix.
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
|
|
6
|
+
const serverPath = process.argv[2];
|
|
7
|
+
const scenario = process.argv[3] ?? 'zero-change';
|
|
8
|
+
const child = spawn(process.execPath, [serverPath], { stdio: ['pipe', 'pipe', 'pipe'], env: process.env });
|
|
9
|
+
|
|
10
|
+
let buffer = '';
|
|
11
|
+
let nextId = 1;
|
|
12
|
+
const pending = new Map();
|
|
13
|
+
|
|
14
|
+
child.stdout.on('data', (chunk) => {
|
|
15
|
+
buffer += chunk.toString();
|
|
16
|
+
let index;
|
|
17
|
+
while ((index = buffer.indexOf('\n')) >= 0) {
|
|
18
|
+
const line = buffer.slice(0, index).trim();
|
|
19
|
+
buffer = buffer.slice(index + 1);
|
|
20
|
+
if (line === '') continue;
|
|
21
|
+
let message;
|
|
22
|
+
try { message = JSON.parse(line); } catch { continue; }
|
|
23
|
+
if (message.id !== undefined && pending.has(message.id)) {
|
|
24
|
+
const { resolve } = pending.get(message.id);
|
|
25
|
+
pending.delete(message.id);
|
|
26
|
+
resolve(message);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const stderr = [];
|
|
31
|
+
child.stderr.on('data', (chunk) => stderr.push(chunk.toString()));
|
|
32
|
+
|
|
33
|
+
function send(method, params) {
|
|
34
|
+
const id = nextId++;
|
|
35
|
+
child.stdin.write(`${JSON.stringify({ jsonrpc: '2.0', id, method, params })}\n`);
|
|
36
|
+
return new Promise((resolve) => pending.set(id, { resolve }));
|
|
37
|
+
}
|
|
38
|
+
function notify(method, params) {
|
|
39
|
+
child.stdin.write(`${JSON.stringify({ jsonrpc: '2.0', method, params })}\n`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const scenarios = {
|
|
43
|
+
'zero-change': {
|
|
44
|
+
tool: 'dsh_run_worker',
|
|
45
|
+
arguments: {
|
|
46
|
+
task: 'Create a temporary PowerShell script at work/tmp-hello.ps1 that writes exactly the text 你好 to standard output with no trailing newline, run it, and verify its stdout is exactly 你好 byte-for-byte under both CP936 and UTF-8. Then delete the script and confirm the path no longer exists. Do not leave any file behind: the final workspace must be unchanged.',
|
|
47
|
+
cwd: 'D:/Users/48376/Desktop/crew-verify-1',
|
|
48
|
+
role: 'worker',
|
|
49
|
+
workspace: { worktree: 'none' },
|
|
50
|
+
constraints: { allow_no_changes: true, timeout_seconds: 600 },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
'tool-list': { tool: null },
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const scenarioSpec = scenarios[scenario];
|
|
57
|
+
const init = await send('initialize', {
|
|
58
|
+
protocolVersion: '2024-11-05',
|
|
59
|
+
capabilities: {},
|
|
60
|
+
clientInfo: { name: 'crew-verify', version: '1' },
|
|
61
|
+
});
|
|
62
|
+
if (init.error) {
|
|
63
|
+
console.log('INIT_FAILED', JSON.stringify(init.error));
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
notify('notifications/initialized', {});
|
|
67
|
+
console.log('SERVER', JSON.stringify(init.result?.serverInfo ?? null));
|
|
68
|
+
|
|
69
|
+
const listed = await send('tools/list', {});
|
|
70
|
+
const tools = (listed.result?.tools ?? []).map((t) => t.name);
|
|
71
|
+
console.log('TOOLS', JSON.stringify(tools));
|
|
72
|
+
|
|
73
|
+
if (scenarioSpec.tool) {
|
|
74
|
+
const started = Date.now();
|
|
75
|
+
const call = await send('tools/call', { name: scenarioSpec.tool, arguments: scenarioSpec.arguments });
|
|
76
|
+
console.log('ELAPSED_MS', Date.now() - started);
|
|
77
|
+
if (call.error) {
|
|
78
|
+
console.log('CALL_ERROR', JSON.stringify(call.error));
|
|
79
|
+
} else {
|
|
80
|
+
const text = (call.result?.content ?? []).map((c) => c.text ?? '').join('');
|
|
81
|
+
let parsed = null;
|
|
82
|
+
try { parsed = JSON.parse(text); } catch { /* keep raw */ }
|
|
83
|
+
if (parsed) {
|
|
84
|
+
const summary = parsed.evidence?.summary ?? {};
|
|
85
|
+
console.log('RESULT', JSON.stringify({
|
|
86
|
+
id: parsed.id,
|
|
87
|
+
phase: parsed.phase,
|
|
88
|
+
status: parsed.status,
|
|
89
|
+
isolation: parsed.isolation,
|
|
90
|
+
execution_cwd: parsed.execution_cwd,
|
|
91
|
+
task_status: summary.task_status,
|
|
92
|
+
tests_status: summary.tests_status,
|
|
93
|
+
delivery_complete: summary.delivery_complete,
|
|
94
|
+
workspace_evidence_ok: parsed.workspace_evidence_ok ?? parsed.evidence?.workspace_evidence_ok,
|
|
95
|
+
no_change_verified: parsed.outcome?.no_change_verified,
|
|
96
|
+
failure: parsed.failure?.reason_code ?? null,
|
|
97
|
+
error: parsed.error ?? null,
|
|
98
|
+
}, null, 1));
|
|
99
|
+
} else {
|
|
100
|
+
console.log('RAW', text.slice(0, 1500));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
child.kill();
|
|
106
|
+
if (stderr.length) console.log('STDERR_SAMPLE', stderr.join('').slice(0, 400));
|
package/src/mcp-runtime.mjs
CHANGED
|
@@ -222,6 +222,15 @@ export function buildMcpWorkflowRuntime(deps) {
|
|
|
222
222
|
source,
|
|
223
223
|
preset,
|
|
224
224
|
delivery,
|
|
225
|
+
// Isolation belongs to this client: it already allocated the workspace
|
|
226
|
+
// and `spec.cwd` is the result. Saying so keeps the hub executing in that
|
|
227
|
+
// cwd instead of resolving an isolation of its own and allocating a
|
|
228
|
+
// second workspace — which put the work somewhere the client was not
|
|
229
|
+
// looking and captured no candidate for.
|
|
230
|
+
requested_isolation: 'shared',
|
|
231
|
+
// The same authorization the client was given, so the hub judges the
|
|
232
|
+
// workspace evidence against the same task contract.
|
|
233
|
+
allow_no_changes: spec.allow_no_changes === true,
|
|
225
234
|
});
|
|
226
235
|
spec.onAttemptStarted?.(spawned.id);
|
|
227
236
|
|
package/src/runtime-identity.mjs
CHANGED
|
@@ -36,7 +36,7 @@ export {
|
|
|
36
36
|
// included in the identity contract.
|
|
37
37
|
const RUNTIME_ID = randomUUID();
|
|
38
38
|
|
|
39
|
-
export const RUNTIME_VERSION = '1.10.
|
|
39
|
+
export const RUNTIME_VERSION = '1.10.8';
|
|
40
40
|
export const HUB_PROTOCOL_VERSION = 1;
|
|
41
41
|
|
|
42
42
|
export const HUB_CAPABILITIES = Object.freeze([
|
package/src/workflow-runtime.mjs
CHANGED
|
@@ -382,6 +382,10 @@ export function createWorkflowRuntime(adapters, {
|
|
|
382
382
|
cwd: job.execution_cwd,
|
|
383
383
|
effort: job.effort,
|
|
384
384
|
timeout_seconds: job.timeout_seconds,
|
|
385
|
+
// Passed through so the executor judges the workspace evidence against
|
|
386
|
+
// the same task contract this workflow was given, instead of against a
|
|
387
|
+
// default that would refuse an authorization the caller granted.
|
|
388
|
+
allow_no_changes: job.allow_no_changes === true,
|
|
385
389
|
policy,
|
|
386
390
|
source: job.source,
|
|
387
391
|
model_class_hint: job.model_class_hint,
|
package/src/workflow.mjs
CHANGED
|
@@ -81,12 +81,18 @@ const NO_CHANGE_ASSERTION_RE = new RegExp(
|
|
|
81
81
|
function isNoChangeAssertion(line) {
|
|
82
82
|
const normalized = line
|
|
83
83
|
.replace(/^(?:[-*+]\s+)+/, '')
|
|
84
|
-
.replace(/[`"'
|
|
84
|
+
.replace(/[`"'“”‘’*_]/g, '')
|
|
85
85
|
.replace(/[.!。!]+$/, '')
|
|
86
86
|
.trim()
|
|
87
87
|
.toLowerCase();
|
|
88
88
|
if (normalized === '') return false;
|
|
89
|
-
|
|
89
|
+
// The assertion is usually a whole line, but a report may also end with it —
|
|
90
|
+
// "**Final state: no files changed.**" is the same declaration stated as a
|
|
91
|
+
// summary. Testing each sentence and clause is what finds it without matching
|
|
92
|
+
// a phrase that merely appears inside an unrelated one.
|
|
93
|
+
return normalized
|
|
94
|
+
.split(/\s*(?:[.:;,—–()\[\]]|->)\s*/)
|
|
95
|
+
.some((fragment) => fragment !== '' && NO_CHANGE_ASSERTION_RE.test(fragment.trim()));
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
// Whether the report claims the workspace holds changes. A report that asserts
|