@entrorove/skill 0.1.28 → 0.1.31
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/CHANGELOG.md +17 -2
- package/SKILL.md +3 -1
- package/cli.mjs +14 -1
- package/cloud-task-client.mjs +119 -0
- package/manifest.json +1 -1
- package/package.json +2 -1
- package/release-state.json +8 -7
- package/release.mjs +15 -3
- package/releases.json +20 -2
- package/remote-client.mjs +14 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.31 — 2026-10-11
|
|
4
|
+
|
|
5
|
+
- 修复云端沙箱任务响应处理并保留可继续的任务上下文。
|
|
6
|
+
- Hardened cloud sandbox responses and preserved continuation task context.
|
|
7
|
+
|
|
8
|
+
## 0.1.30 — 2026-10-11
|
|
9
|
+
|
|
10
|
+
- 修复云端沙箱任务路由与逐任务选择交互。
|
|
11
|
+
- Fixed cloud sandbox task routing and per-task choice interaction.
|
|
12
|
+
|
|
13
|
+
## 0.1.29 — 2026-10-11
|
|
14
|
+
|
|
15
|
+
- 更新了自动版本与更新日志、远端恢复客户端。
|
|
16
|
+
- Updated automatic versioning and changelog, remote recovery client.
|
|
17
|
+
|
|
3
18
|
## 0.1.28 — 2026-10-09
|
|
4
19
|
|
|
5
|
-
-
|
|
6
|
-
- Updated coordination execution and validator isolation capabilities,
|
|
20
|
+
- 更新了协同执行与验证隔离能力、安装包配置、自动版本与更新日志、统一本地错误信封。
|
|
21
|
+
- Updated coordination execution and validator isolation capabilities, package configuration, automatic versioning and changelog, unified local error envelopes.
|
|
7
22
|
|
|
8
23
|
## 0.1.27 — 2026-10-08
|
|
9
24
|
|
package/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ description: An engineering skill that understands a task, chooses a fitting wor
|
|
|
5
5
|
|
|
6
6
|
# entrorove · 熵巡
|
|
7
7
|
|
|
8
|
-
Version: 0.1.
|
|
8
|
+
Version: 0.1.31.
|
|
9
9
|
|
|
10
10
|
Read the user's goal and repository instructions first. Preserve the original task through planning, execution and verification. Use the workflow only where it helps; a small, low-risk edit can proceed directly. Never claim a reported step is a verified result.
|
|
11
11
|
|
|
@@ -25,6 +25,8 @@ On first use or after a material agreement revision, the CLI returns a Confirm P
|
|
|
25
25
|
|
|
26
26
|
Send JSON on standard input to `npx @entrorove/skill invoke`. Before every execution call, discover `operation: "capabilities"` with `{}` and validate input against the returned operation schema. The CLI performs remote discovery automatically; local adapters discover the installed contract before execution. Discovery failure stops execution. Adapters that expose the four-step task route can call `task-describe`, `message-route`, `message-accept` and `task-resume` in order; `start` remains the one-call workflow equivalent. Then start with `operation: "start"` and an `input.task` containing the goal, work type, risk, difficulty and observed scope facts. Work types are `implementation`, `documentation`, `calculation`, `merge`, `review`, `decision` and `diagnosis`. Assess facts from the real task and repository; do not invent certainty. entrorove chooses the workflow. A small task may return `decision: "direct"`, meaning the IDE should do the work without another service call. The public envelope is `entrorove.skill.request/2.0`; `requestId` is the SHA-256 digest of `operation + canonical(input)`. Version 1.0 remains accepted for older callers.
|
|
27
27
|
|
|
28
|
+
`start` creates a normal durable skill flow. It does not create a cloud-sandbox task or contribute to cloud-sandbox usage. To create a real cloud-sandbox task, call `operation: "cloud-task"` with `input.goal`; the CLI returns a Confirm Protocol choice interaction before any cloud request. Continue the same request with `sandboxChoice: "use"` or `sandboxChoice: "local"`. `use` calls the authenticated `/api/cloud-task` endpoint and returns the real `taskId`; `local` skips only this task. A `needs-input`, `blocked` or `failed` result is not a successful dispatch and must be shown to the user.
|
|
29
|
+
|
|
28
30
|
### Per-task cloud sandbox choice
|
|
29
31
|
|
|
30
32
|
When the host exposes an enabled cloud-sandbox preference and a new task is about to dispatch a cloud model, ask once whether this task should use the cloud sandbox. This is a low-risk routing preference, separate from agreement acceptance and the medium/high-risk confirmations below. Offer `use for this task` and `continue locally`; if the user gives no answer, use the cloud sandbox by the host's stated timeout default. An explicit opt-out applies only to the current task and must not change the account-wide sandbox preference. Do not ask again while continuing or resuming the same task. If the account-wide preference is disabled or unavailable, do not dispatch a cloud task and do not show a misleading per-task prompt; preserve the normal local workflow and report the prerequisite clearly. This choice never bypasses the normal task schema, mutation authority, evidence checks, or risk confirmation.
|
package/cli.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import { runTaskMessageCli } from './task-message-cli.mjs';
|
|
|
12
12
|
import { apiUrl, buildInvocation, invokeRemote, queryFlows, queryStatus } from './remote-client.mjs';
|
|
13
13
|
const packageDir = path.dirname(fileURLToPath(import.meta.url));
|
|
14
14
|
import { buildReview, submitSkillFeedback, feedbackNoticeVersion } from './review-client.mjs';
|
|
15
|
+
import { runCloudTask as dispatchCloudTask } from './cloud-task-client.mjs';
|
|
15
16
|
const markerName = '.entrorove-managed.json';
|
|
16
17
|
const installFiles = ['SKILL.md', 'CAPABILITY-MAP.md', 'AGREEMENT.md', 'manifest.json'];
|
|
17
18
|
const markerSchema = 'entrorove.managed-install/1.0';
|
|
@@ -381,7 +382,17 @@ export function remoteDependencies(home = os.homedir()) {
|
|
|
381
382
|
};
|
|
382
383
|
}
|
|
383
384
|
|
|
385
|
+
export async function runCloudTask(input, dependencies = remoteDependencies()) {
|
|
386
|
+
const result = await dispatchCloudTask(input, dependencies);
|
|
387
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
388
|
+
if (['blocked', 'failed', 'incomplete', 'needs-input'].includes(result.status)) process.exitCode = 2;
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
391
|
+
|
|
384
392
|
async function invoke(data) {
|
|
393
|
+
if (data?.operation === 'cloud-task') {
|
|
394
|
+
return runCloudTask(data.input, remoteDependencies());
|
|
395
|
+
}
|
|
385
396
|
return invokeRemote(data, remoteDependencies());
|
|
386
397
|
}
|
|
387
398
|
|
|
@@ -410,6 +421,8 @@ async function main() {
|
|
|
410
421
|
await invoke(await stdinJson());
|
|
411
422
|
} else if (command === 'host-message') {
|
|
412
423
|
await runTaskMessageCli(args, await stdinJson(), remoteDependencies());
|
|
424
|
+
} else if (command === 'cloud-task' && args.length === 0) {
|
|
425
|
+
await runCloudTask(await stdinJson());
|
|
413
426
|
} else if (command === 'review' && args.length === 0) {
|
|
414
427
|
await submitReview(await stdinJson());
|
|
415
428
|
} else if (command === 'status' && args.length === 1) {
|
|
@@ -431,7 +444,7 @@ async function main() {
|
|
|
431
444
|
} else if (command === 'local') {
|
|
432
445
|
await runLocal(args);
|
|
433
446
|
} else {
|
|
434
|
-
throw new Error('Usage: entrorove install | configure | agreement | invoke | host-message [adapterModulePath] | status <requestId> | flows | review | local (JSON on stdin)');
|
|
447
|
+
throw new Error('Usage: entrorove install | configure | agreement | invoke | host-message [adapterModulePath] | cloud-task | status <requestId> | flows | review | local (JSON on stdin)');
|
|
435
448
|
}
|
|
436
449
|
}
|
|
437
450
|
if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) {
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { appendInvocation } from './local-invocations.mjs';
|
|
3
|
+
import { parseTransportResponse, requestTransport, trustedOrigin } from './remote-transport.mjs';
|
|
4
|
+
|
|
5
|
+
const CLOUD_TASK_SCHEMA = 'entrorove.cloud-task/1.0';
|
|
6
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
7
|
+
|
|
8
|
+
function canonical(value) {
|
|
9
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`;
|
|
10
|
+
if (value && typeof value === 'object') {
|
|
11
|
+
return `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`;
|
|
12
|
+
}
|
|
13
|
+
return JSON.stringify(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function requestId(input) {
|
|
17
|
+
return createHash('sha256').update(`cloud-task${canonical(input)}`).digest('hex');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function validateInput(input) {
|
|
21
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new Error('cloud-task input must be a JSON object');
|
|
22
|
+
if (typeof input.goal !== 'string' || !input.goal.trim() || input.goal.length > 12_000) {
|
|
23
|
+
throw new Error('cloud-task goal must be a non-empty string of at most 12000 characters');
|
|
24
|
+
}
|
|
25
|
+
if (input.ideTaskId !== undefined && (typeof input.ideTaskId !== 'string' || !input.ideTaskId.trim() || input.ideTaskId.length > 200)) {
|
|
26
|
+
throw new Error('cloud-task ideTaskId must be a non-empty string of at most 200 characters');
|
|
27
|
+
}
|
|
28
|
+
if (input.sandboxChoice !== undefined && !['use', 'local'].includes(input.sandboxChoice)) {
|
|
29
|
+
throw new Error('cloud-task sandboxChoice must be use or local');
|
|
30
|
+
}
|
|
31
|
+
return { goal: input.goal, ...(input.ideTaskId === undefined ? {} : { ideTaskId: input.ideTaskId }) };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function validateDependencies(dependencies) {
|
|
35
|
+
if (!dependencies || !['install', 'agreementAccepted', 'agreementRequired', 'readCredential', 'apiOrigin']
|
|
36
|
+
.every(key => typeof dependencies[key] === 'function')) {
|
|
37
|
+
throw new Error('Cloud sandbox client requires installed agreement, credential, and API-origin dependencies');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function sandboxChoiceInteraction(id) {
|
|
42
|
+
return {
|
|
43
|
+
schemaVersion: 'confirm.interaction/1.0', requestId: id, type: 'choice',
|
|
44
|
+
question: '是否为本任务使用云端沙箱?',
|
|
45
|
+
options: [
|
|
46
|
+
{ id: 'use', label: '本任务使用云端沙箱', hint: '创建真实云端任务并持续记录进度' },
|
|
47
|
+
{ id: 'local', label: '继续本地执行', hint: '仅当前任务退出云端沙箱' },
|
|
48
|
+
],
|
|
49
|
+
default: 'use', timeout: 8_000, timeoutAction: 'use', risk: 'low',
|
|
50
|
+
riskDescription: '这是当前任务的执行位置选择,不改变账号级云端沙箱开关。',
|
|
51
|
+
rememberable: false, memoryKey: '',
|
|
52
|
+
callback: { operation: 'cloud-task', payload: { sandboxChoice: 'use' } },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function envelope(input, status, extra = {}) {
|
|
57
|
+
return { schemaVersion: CLOUD_TASK_SCHEMA, status, requestId: requestId(input), ...extra };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function responseBody(response) {
|
|
61
|
+
try { return parseTransportResponse(response, 'cloud-task'); }
|
|
62
|
+
catch (cause) { throw Object.assign(new Error('Cloud sandbox returned an invalid response'), { cause }); }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function runCloudTask(input, dependencies) {
|
|
66
|
+
const task = validateInput(input);
|
|
67
|
+
validateDependencies(dependencies);
|
|
68
|
+
const id = requestId(task);
|
|
69
|
+
await dependencies.install();
|
|
70
|
+
if (!await dependencies.agreementAccepted(dependencies.home)) {
|
|
71
|
+
return envelope(task, 'blocked', { code: 'agreement-required', interaction: dependencies.agreementRequired(dependencies.home) });
|
|
72
|
+
}
|
|
73
|
+
if (input.sandboxChoice === undefined) return envelope(task, 'needs-input', {
|
|
74
|
+
task, interaction: sandboxChoiceInteraction(id),
|
|
75
|
+
});
|
|
76
|
+
if (input.sandboxChoice === 'local') return envelope(task, 'skipped', { reason: 'task' });
|
|
77
|
+
|
|
78
|
+
const token = await dependencies.readCredential(dependencies.home);
|
|
79
|
+
const origin = trustedOrigin(dependencies.apiOrigin());
|
|
80
|
+
const metadata = { kind: 'remote', operation: 'cloud-task', requestId: id };
|
|
81
|
+
await appendInvocation({ ...metadata, event: 'started' }, dependencies.home);
|
|
82
|
+
let response;
|
|
83
|
+
try {
|
|
84
|
+
response = await requestTransport(origin, '/api/cloud-task', {
|
|
85
|
+
method: 'POST', timeoutMs: 30_000,
|
|
86
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${token}` },
|
|
87
|
+
body: JSON.stringify(task),
|
|
88
|
+
});
|
|
89
|
+
} catch (cause) {
|
|
90
|
+
if (cause.established) await appendInvocation({ ...metadata, event: 'outcome-unknown' }, dependencies.home);
|
|
91
|
+
else await appendInvocation({ ...metadata, event: 'not-sent' }, dependencies.home);
|
|
92
|
+
throw cause.established
|
|
93
|
+
? Object.assign(new Error(`Cloud sandbox outcome is unknown; requestId=${id}. Query the task list before retrying.`), { requestId: id, outcomeUnknown: true })
|
|
94
|
+
: cause;
|
|
95
|
+
}
|
|
96
|
+
let result;
|
|
97
|
+
try {
|
|
98
|
+
result = await responseBody(response);
|
|
99
|
+
} catch (cause) {
|
|
100
|
+
await appendInvocation({ ...metadata, event: 'outcome-unknown' }, dependencies.home);
|
|
101
|
+
throw Object.assign(new Error(`Cloud sandbox returned an unusable response; requestId=${id}. Query the task list before retrying.`), {
|
|
102
|
+
requestId: id, outcomeUnknown: true, cause,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
await appendInvocation({ ...metadata, event: response.status >= 500 ? 'outcome-unknown' : 'rejected' }, dependencies.home);
|
|
107
|
+
const code = typeof result?.error === 'string' ? result.error : 'cloud-task-rejected';
|
|
108
|
+
return envelope(task, response.status === 403 ? 'blocked' : 'failed', { code, message: result?.message ?? code, httpStatus: response.status });
|
|
109
|
+
}
|
|
110
|
+
if (!result?.ok || typeof result.taskId !== 'string' || !UUID.test(result.taskId)) {
|
|
111
|
+
await appendInvocation({ ...metadata, event: 'outcome-unknown' }, dependencies.home);
|
|
112
|
+
throw new Error(`Cloud sandbox response lost its task identity; requestId=${id}`);
|
|
113
|
+
}
|
|
114
|
+
await appendInvocation({ ...metadata, event: 'completed' }, dependencies.home);
|
|
115
|
+
return envelope(task, 'started', { taskId: result.taskId,
|
|
116
|
+
taskStatus: typeof result.status === 'string' ? result.status : 'running',
|
|
117
|
+
executionIsolation: typeof result.executionIsolation === 'string' ? result.executionIsolation : 'worker' });
|
|
118
|
+
}
|
|
119
|
+
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entrorove/skill",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"entrorove": "cli.mjs"
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"local-task-handoff-write-archive.mjs",
|
|
77
77
|
"local-task-handoff-write-receipts.mjs",
|
|
78
78
|
"remote-client.mjs",
|
|
79
|
+
"cloud-task-client.mjs",
|
|
79
80
|
"remote-transport.mjs",
|
|
80
81
|
"local-credential.mjs",
|
|
81
82
|
"local-invocations.mjs",
|
package/release-state.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "entrorove.release-state/1.0",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"digest": "
|
|
3
|
+
"version": "0.1.31",
|
|
4
|
+
"digest": "005df83b58c713b893d715a7480c08a91896301f6295746525f49abd2d746044",
|
|
5
5
|
"files": {
|
|
6
6
|
"local-final-gate16.mjs": "b30caab08441449ad556a6e9a3cefbf91190f0b3f7370f46d2a21156375aa8e3",
|
|
7
7
|
"local-final-gate16-checks.mjs": "1dd30e9666d458d8b12ca6e4975f6f0ac8450c3ea9cbecc7401ae037f66b589a",
|
|
@@ -44,15 +44,15 @@
|
|
|
44
44
|
"local-autocoord17-waits.mjs": "2b0a713987822548a94e1ed3a9a891532db45383797dfc5d5bc8d1e35593cf25",
|
|
45
45
|
"local-validator-isolation17.mjs": "48e94604e28b8fddd567dfb35ff60b73fd094a5803bd49f076388b7f310f92aa",
|
|
46
46
|
"local-calctool-composition17.mjs": "ac89fcb41ab0d0d9b47c1b5696945281b0894eb064820775e36a011c76314e6a",
|
|
47
|
-
"cli.mjs": "
|
|
47
|
+
"cli.mjs": "6b2f31ee3204c8584ac4733a9b2d1c6ca72e2ea205e84f8a314a68f31917f629",
|
|
48
48
|
"review-client.mjs": "3491eef68ed533174b31db3c53febb6519b8f088f71a9964644714a9a4c03c77",
|
|
49
49
|
"version.mjs": "1f392147d72c40af4b03b307ea607075fb815af5174fa07e254b95fe12dbf3e7",
|
|
50
|
-
"SKILL.md": "
|
|
50
|
+
"SKILL.md": "fa3fa41f1551e54512de0cae7940e2d7ad670f81a46c2e642cdc2323b171224f",
|
|
51
51
|
"CAPABILITY-MAP.md": "128ecc8d90b2c17846ce6f0c650eada9e7fa7240f5d3c03b31e3ee01135557b8",
|
|
52
52
|
"AGREEMENT.md": "f93a68e92c457200dd024610e65f74e478b8f60860f951da818b70561f1b9bf8",
|
|
53
|
-
"package.json": "
|
|
53
|
+
"package.json": "77e29f7740cc2a2284490a992a9945fbb4a3ad3ac044f4ff9a59b1aa83e44ae0",
|
|
54
54
|
"manifest.json": "ab06e307795bc9da5add926752b6c4dbdaf18625090261a035aa356a679c504f",
|
|
55
|
-
"release.mjs": "
|
|
55
|
+
"release.mjs": "1e6155d10b5e6384414233847815742e13f0945101c5f53253ad9ca827766beb",
|
|
56
56
|
"local-cli.mjs": "ecc85bc5f36a5663fef004e59525374dc7883d3889f5a0cfe219ae5e84b2d3ea",
|
|
57
57
|
"local-state-lock.mjs": "1dbf64264642a0851faf043e07ee9f66d2ad0508e9f799b0626246e42fae893e",
|
|
58
58
|
"local-execution.mjs": "97dfad5e889e679e9cc34434005342526502795abba21078fa40673788073153",
|
|
@@ -117,7 +117,8 @@
|
|
|
117
117
|
"local-scope-state.mjs": "8b600a91fc0068d7a8c0bb752ddba7ed30ada93daafb118d6cba2368fd94cb27",
|
|
118
118
|
"local-scope-workspaces.mjs": "a44c34537e5ed2dcaf193a985e5bc05957989fbb160a650f9e37ba7d66eb2de8",
|
|
119
119
|
"local-task-routing.mjs": "e26d6f1ba134ffe1da2a49b4fb6bb6dfa5617671f774a08e719d04f202cf5eca",
|
|
120
|
-
"remote-client.mjs": "
|
|
120
|
+
"remote-client.mjs": "ce16258faffb09c9db19609d47a0af6f63fcbc256feb35ba7a14bbb89e69cc6e",
|
|
121
|
+
"cloud-task-client.mjs": "b7dc4be6c4d2a60404538e3d875cf95533e1b7be3f43ac7f5d49118042ce1d45",
|
|
121
122
|
"remote-transport.mjs": "5e5a8c0b490371f7f5a052cffbeee6202d4cf713dff1c43b6d910fe944f8429c",
|
|
122
123
|
"local-credential.mjs": "2a302a7f67b5ea8dc73453fc96ab33db349b59ca11fa9456e48ca1d9669d877c",
|
|
123
124
|
"local-invocations.mjs": "f6ac4c41c1a5ec34de358c4cca210ca8e403b3a913150a43bb55ec9be55d9998",
|
package/release.mjs
CHANGED
|
@@ -89,7 +89,7 @@ const sourceFiles = [
|
|
|
89
89
|
'local-scope-snapshot.mjs',
|
|
90
90
|
'local-scope-state.mjs',
|
|
91
91
|
'local-scope-workspaces.mjs',
|
|
92
|
-
'local-task-routing.mjs', 'remote-client.mjs',
|
|
92
|
+
'local-task-routing.mjs', 'remote-client.mjs', 'cloud-task-client.mjs',
|
|
93
93
|
'remote-transport.mjs', 'local-credential.mjs', 'local-invocations.mjs',
|
|
94
94
|
'local-task-handoff-fence.mjs',
|
|
95
95
|
'local-task-handoff-storage.mjs', 'local-task-handoff-overlay-wire.mjs', 'local-task-handoff-completion-wire.mjs',
|
|
@@ -177,6 +177,16 @@ function versionNext(version) {
|
|
|
177
177
|
return `${parts[0]}.${parts[1]}.${parts[2] + 1}`
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
function distinctReleaseLabels(changed, labels) {
|
|
181
|
+
const seen = new Set()
|
|
182
|
+
return changed.map(name => labels[name]).filter(label => {
|
|
183
|
+
const key = `${label.zh}\u0000${label.en}`
|
|
184
|
+
if (seen.has(key)) return false
|
|
185
|
+
seen.add(key)
|
|
186
|
+
return true
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
180
190
|
const calctoolReleaseLabels = Object.freeze({
|
|
181
191
|
'local-decimal.mjs': { zh: 'Calctool Decimal28 公式与本地执行', en: 'Calctool Decimal28 formulas and local execution' },
|
|
182
192
|
'local-formula-ast.mjs': { zh: 'Calctool Decimal28 公式与本地执行', en: 'Calctool Decimal28 formulas and local execution' },
|
|
@@ -333,10 +343,12 @@ function releaseNote(changed) {
|
|
|
333
343
|
'remote-client.mjs': { zh: '远端恢复客户端', en: 'remote recovery client' },
|
|
334
344
|
'task-message-entry.mjs': { zh: '任务消息实际投递入口', en: 'task message host delivery entry' },
|
|
335
345
|
'task-message-cli.mjs': { zh: '任务消息命令与适配器', en: 'task message command and adapter' },
|
|
346
|
+
'cloud-task-client.mjs': { zh: '云端沙箱任务路由与逐任务选择', en: 'cloud sandbox task routing and per-task choice' },
|
|
336
347
|
})
|
|
348
|
+
const distinct = distinctReleaseLabels(changed, labels)
|
|
337
349
|
return {
|
|
338
|
-
zh: `更新了${
|
|
339
|
-
en: `Updated ${
|
|
350
|
+
zh: `更新了${distinct.map(label => label.zh).join('、')}。`,
|
|
351
|
+
en: `Updated ${distinct.map(label => label.en).join(', ')}.`,
|
|
340
352
|
}
|
|
341
353
|
}
|
|
342
354
|
|
package/releases.json
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.1.31",
|
|
4
|
+
"date": "2026-10-11",
|
|
5
|
+
"zh": "修复云端沙箱任务响应处理并保留可继续的任务上下文。",
|
|
6
|
+
"en": "Hardened cloud sandbox responses and preserved continuation task context."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.1.30",
|
|
10
|
+
"date": "2026-10-11",
|
|
11
|
+
"zh": "修复云端沙箱任务路由与逐任务选择交互。",
|
|
12
|
+
"en": "Fixed cloud sandbox task routing and per-task choice interaction."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.1.29",
|
|
16
|
+
"date": "2026-10-11",
|
|
17
|
+
"zh": "更新了自动版本与更新日志、远端恢复客户端。",
|
|
18
|
+
"en": "Updated automatic versioning and changelog, remote recovery client."
|
|
19
|
+
},
|
|
2
20
|
{
|
|
3
21
|
"version": "0.1.28",
|
|
4
22
|
"date": "2026-10-09",
|
|
5
|
-
"zh": "
|
|
6
|
-
"en": "Updated coordination execution and validator isolation capabilities,
|
|
23
|
+
"zh": "更新了协同执行与验证隔离能力、安装包配置、自动版本与更新日志、统一本地错误信封。",
|
|
24
|
+
"en": "Updated coordination execution and validator isolation capabilities, package configuration, automatic versioning and changelog, unified local error envelopes."
|
|
7
25
|
},
|
|
8
26
|
{
|
|
9
27
|
"version": "0.1.27",
|
package/remote-client.mjs
CHANGED
|
@@ -17,6 +17,18 @@ function requestId(operation, input) {
|
|
|
17
17
|
return createHash('sha256').update(operation + canonical(input)).digest('hex');
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// The API advertises JSON Schema formats that are intentionally kept out of
|
|
21
|
+
// Ajv's core package. Register the formats used by the discovered contract so
|
|
22
|
+
// client-side validation can reach the actual operation instead of failing
|
|
23
|
+
// while compiling the schema.
|
|
24
|
+
const discoveredUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
25
|
+
function discoveredAjv() {
|
|
26
|
+
const ajv = new Ajv({ strict: true, strictRequired: false, allErrors: true });
|
|
27
|
+
ajv.addFormat('uuid', discoveredUuid);
|
|
28
|
+
ajv.addFormat('date-time', value => Number.isFinite(Date.parse(value)));
|
|
29
|
+
return ajv;
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
export function buildInvocation(operation, input) {
|
|
21
33
|
if (!['capabilities', 'task-describe', 'message-route', 'message-accept', 'task-resume', 'task-checkpoint',
|
|
22
34
|
'message-status', 'message-delivery-start', 'message-delivery-report', 'message-resolve', 'message-complete',
|
|
@@ -58,7 +70,7 @@ export async function invokeRemote(data, dependencies) {
|
|
|
58
70
|
const capabilities = discovery.result.output.capabilities;
|
|
59
71
|
const schema = capabilities?.operationSchemas?.[data.operation];
|
|
60
72
|
if (!capabilities?.operations?.includes(data.operation) || !schema) throw new Error(`Capability discovery does not support ${data.operation}`);
|
|
61
|
-
const validate =
|
|
73
|
+
const validate = discoveredAjv().compile(schema);
|
|
62
74
|
if (!validate(data.input)) throw new Error(`Invalid ${data.operation} input: ${validate.errors.map(error => `${error.instancePath || '/'} ${error.message}`).join('; ')}`);
|
|
63
75
|
validateDiscoveredChecks(data.input, capabilities.inputChecks?.[data.operation]);
|
|
64
76
|
}
|
|
@@ -120,7 +132,7 @@ function machineOrigin(dependencies) { return trustedOrigin(dependencies.apiOrig
|
|
|
120
132
|
function validateMachineCapability(operation, input, capabilities) {
|
|
121
133
|
const schema = capabilities?.operationSchemas?.[operation];
|
|
122
134
|
if (!capabilities?.operations?.includes(operation) || !schema) throw new Error(`Capability discovery does not support ${operation}`);
|
|
123
|
-
const validate =
|
|
135
|
+
const validate = discoveredAjv().compile(schema);
|
|
124
136
|
if (!validate(input)) throw new Error(`Invalid ${operation} input: ${validate.errors.map(error => `${error.instancePath || '/'} ${error.message}`).join('; ')}`);
|
|
125
137
|
validateDiscoveredChecks(input, capabilities.inputChecks?.[operation]);
|
|
126
138
|
}
|