@entrorove/skill 0.1.40 → 0.1.43

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.
@@ -0,0 +1,92 @@
1
+ // Delivery-boundary and stop-loss contract (额度可见性与可交付边界 · WP-C).
2
+ //
3
+ // A boundary is the only legal stopping point: it exists where the system is
4
+ // runnable, demonstrable and roll-backable. Stopping at a boundary yields the
5
+ // independent terminal status `delivered-stage-N`; it must never be reported as
6
+ // failed or blocked, and resuming continues at the next boundary without
7
+ // re-running delivered stages.
8
+ //
9
+ // Consumption is measured, never predicted: without an enforced budget there is
10
+ // no baseline, so the decision stays explicitly not-estimable instead of
11
+ // inventing a number.
12
+ const terminalPattern = /^delivered-stage-([1-9][0-9]{0,2})$/u;
13
+ export const STAGE_BOUNDARY_SCHEMA = 'entrorove.stage-boundary/1.0';
14
+
15
+ export function stageTerminal(count) {
16
+ if (!Number.isSafeInteger(count) || count < 1 || count > 999) throw new Error('A delivered stage count requires a positive bounded integer');
17
+ return `delivered-stage-${count}`;
18
+ }
19
+
20
+ export function stageOf(status) {
21
+ const match = typeof status === 'string' ? terminalPattern.exec(status) : null;
22
+ return match ? Number(match[1]) : null;
23
+ }
24
+
25
+ export function isStageTerminal(status) { return stageOf(status) !== null; }
26
+
27
+ export function stageMessage(status, remaining) {
28
+ const stage = stageOf(status);
29
+ if (stage === null) throw new Error('A delivered stage message requires a delivered terminal status');
30
+ if (!Number.isSafeInteger(remaining) || remaining < 0) throw new Error('Delivered stages require their remaining bounded count');
31
+ return remaining === 0
32
+ ? `阶段 ${stage} 已交付,全部阶段完成。`
33
+ : `阶段 ${stage} 已交付,剩余 ${remaining} 个阶段待续跑;从下一个边界继续,不重新执行已交付阶段。`;
34
+ }
35
+
36
+ function measuredMetering(metering) {
37
+ if (!metering || typeof metering !== 'object' || Array.isArray(metering)) throw new Error('Boundary metering requires the actual consumption view');
38
+ const { enforced, elapsedMs, maxDurationMs } = metering;
39
+ if (typeof enforced !== 'boolean') throw new Error('Boundary metering requires its enforcement flag');
40
+ if (!enforced) return null;
41
+ if (!Number.isSafeInteger(elapsedMs) || elapsedMs < 0 || !Number.isSafeInteger(maxDurationMs) || maxDurationMs < 1) {
42
+ throw new Error('Enforced boundary metering requires measured elapsed and bounded duration');
43
+ }
44
+ return { elapsedMs, maxDurationMs };
45
+ }
46
+
47
+ // The decision is taken at a clean boundary: close here when the measured
48
+ // consumption of the stage just delivered reaches the configured share of the
49
+ // enforced budget, otherwise continue to the next boundary.
50
+ export function boundaryDecision(input) {
51
+ const { steps, deliveredSequence, metering, ratio = 0.8 } = input ?? {};
52
+ if (!Array.isArray(steps) || steps.length === 0 || steps.some(step => !step || !Number.isSafeInteger(step.sequence))) {
53
+ throw new Error('Boundary decision requires the accepted plan steps with their sequences');
54
+ }
55
+ if (!Number.isSafeInteger(deliveredSequence) || deliveredSequence < 0 || deliveredSequence >= steps.length) {
56
+ throw new Error('Boundary decision requires a delivered sequence inside the accepted plan');
57
+ }
58
+ if (typeof ratio !== 'number' || !(ratio > 0 && ratio <= 1)) throw new Error('Boundary threshold ratio must be inside (0,1]');
59
+ const remainingStages = steps.length - deliveredSequence;
60
+ const nextSequence = deliveredSequence + 1;
61
+ const measured = measuredMetering(metering);
62
+ if (!measured) {
63
+ return { schemaVersion: STAGE_BOUNDARY_SCHEMA, action: 'continue', status: null, nextSequence, remainingStages,
64
+ reason: 'not-estimable', basis: 'none', detail: '没有强制预算基线,不预估剩余量;继续到下一个边界。' };
65
+ }
66
+ const consumed = measured.elapsedMs / measured.maxDurationMs;
67
+ const reached = consumed >= ratio;
68
+ return { schemaVersion: STAGE_BOUNDARY_SCHEMA, action: reached ? 'close' : 'continue',
69
+ status: reached ? stageTerminal(deliveredSequence) : null, nextSequence, remainingStages,
70
+ reason: reached ? 'near-threshold' : 'below-threshold', basis: 'measured',
71
+ consumedMs: measured.elapsedMs, budgetMs: measured.maxDurationMs, ratio,
72
+ detail: reached ? '已消耗达到阈值的既定比例,在当前干净边界收口。' : '仍在阈值以内,继续到下一个边界。' };
73
+ }
74
+
75
+ // Resuming starts at the first stage that was not delivered; delivered stages
76
+ // are never re-executed.
77
+ export function nextBoundary(steps, deliveredSequence) {
78
+ if (!Array.isArray(steps)) throw new Error('Resuming a boundary requires the accepted plan steps');
79
+ if (!Number.isSafeInteger(deliveredSequence) || deliveredSequence < 0) throw new Error('Resuming a boundary requires a delivered sequence');
80
+ return steps.find(step => step.sequence === deliveredSequence + 1) ?? null;
81
+ }
82
+
83
+ export function stageReport(status, steps) {
84
+ const delivered = stageOf(status);
85
+ if (delivered === null || !Array.isArray(steps)) throw new Error('A stage report requires a delivered terminal status and the accepted plan steps');
86
+ const next = nextBoundary(steps, delivered);
87
+ return { schemaVersion: STAGE_BOUNDARY_SCHEMA, status, deliveredStages: delivered, totalStages: steps.length,
88
+ remainingStages: Math.max(0, steps.length - delivered),
89
+ deliveredStepIds: steps.filter(step => step.sequence <= delivered).map(step => step.stepId ?? null),
90
+ nextStepId: next?.stepId ?? null, terminal: true,
91
+ message: stageMessage(status, Math.max(0, steps.length - delivered)) };
92
+ }
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "entrorove.skill-manifest/1.0",
3
3
  "name": "entrorove",
4
- "version": "0.1.40",
4
+ "version": "0.1.43",
5
5
  "endpoint": "https://entrorove.com",
6
6
  "requestSchemaVersion": "entrorove.skill.request/2.0",
7
7
  "supportedRequestSchemaVersions": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entrorove/skill",
3
- "version": "0.1.40",
3
+ "version": "0.1.43",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "entrorove": "cli.mjs"
@@ -14,6 +14,7 @@
14
14
  "local-contracts.mjs",
15
15
  "local-contract-types.mjs",
16
16
  "local-state-lock.mjs",
17
+ "local-stage-boundary.mjs",
17
18
  "local-execution.mjs",
18
19
  "local-swarm-authority.mjs",
19
20
  "local-swarm-baseline.mjs",
@@ -203,6 +204,7 @@
203
204
  "ide-command.mjs",
204
205
  "ide-hook.mjs",
205
206
  "ide-install.mjs",
207
+ "install-cleanup.mjs",
206
208
  "ide-session.mjs",
207
209
  "break-glass.mjs",
208
210
  "ide-results.mjs",
@@ -226,6 +228,7 @@
226
228
  "runtime/validator-runtime-v7.0.44.d.mts",
227
229
  "IDE-EXECUTION.md",
228
230
  "REUSE-DISCIPLINE.md",
231
+ "DELIVERY-BOUNDARIES.md",
229
232
  "reuse-discipline-release.mjs"
230
233
  ],
231
234
  "scripts": {
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "schemaVersion": "entrorove.release-state/1.0",
3
- "version": "0.1.40",
4
- "digest": "4175f36123295cacefbcd09150116696d7528716ba1541256cf345ec3f31a2f3",
3
+ "version": "0.1.43",
4
+ "digest": "4def2226e316c684fd009096f1bcafeec0c3519a3d4532cebe203ef095834467",
5
5
  "files": {
6
- "ide-code-mode.mjs": "cd5318342aac23ba9719ad760b7d7b8018d31839df1bed4ed7142c2782e22def",
7
- "ide-command.mjs": "e50a7e0b68bfc1812568f60562598a463ae169984a1abe9d401229c2b66c78c2",
8
- "ide-hook.mjs": "567593a4c06911889954dc6499bd22be0ab67c78f0d8d46ad671bf55934f9041",
9
- "ide-install.mjs": "65814d1e73cbdccdae4d8703cf9e8573492a3fd6a549ef8562a3f9d703f0ba09",
10
- "ide-session.mjs": "964c5088454a9069fbf5672d2456ba136718c3b2e9eec31c96f7c5943972731b",
6
+ "ide-code-mode.mjs": "f3f53357ba09727b3b597851976a24de93287c0faca5052448277d0904bb61f7",
7
+ "ide-command.mjs": "158d8f26c1dc28a04284e429150a2060d7278661f656a75e7e9f7600f536b7bf",
8
+ "ide-hook.mjs": "a2f151a6d1651872b0160487493d3ecedf01e30301b7af3d336201e7f6135e32",
9
+ "ide-install.mjs": "10247efadcfeef5da001022d12080bd0f789e67fdf7862bcc65db73dda3bbe57",
10
+ "ide-session.mjs": "ce3ee463804faf4c557d82ff221b0bf7ddb6f6ab7257ce7e9e0af8fbc15d57b8",
11
11
  "break-glass.mjs": "2ad6f084c788d63fec11e64412864ac715bcb1a282dd2d8f03dc14e89e82135e",
12
12
  "ide-results.mjs": "dfe0d3f773c65a9fc21056538d7005012c982ed4509bc0410d7f62813d0863fd",
13
13
  "ide-queue.mjs": "806e716c775fd0079417027b135977c8516f47dddb0bc5ca19e32a355dab0e9e",
@@ -19,9 +19,9 @@
19
19
  "local-discipline-gate.mjs": "04b1ba092d7f4342a0e77cfbeb44d9634dbce9fec0c79e6a1e4b11a662d904ac",
20
20
  "local-discipline-native.mjs": "8b99b5ce951cab3c1426428ad807650c61c0ffb2c346d747b73b99057b382049",
21
21
  "local-discipline-operations.mjs": "9fcb7b8889b7718d42e1d37936485d8198d5017d405e0cdbbabdbbd12fcc9d48",
22
- "local-discipline-repository.mjs": "a5f894ec7551ff084fd477eaabff3391fbd7d295df54889649d540f4d64f9a47",
22
+ "local-discipline-repository.mjs": "d818aa6137459ef200892479cc8c47fe82c884dc0e76dc7e6d45a6b41a13c6ec",
23
23
  "local-discipline-snapshot.mjs": "8fd35b2cfbece8f64579f144f589a66dec9b6022d50fe931c4c1f886cb83134d",
24
- "local-runner-discipline.mjs": "44694f4444e039d5440805eb051bb4b2b8e2c2412f9e66c2f35f965a16206c3c",
24
+ "local-runner-discipline.mjs": "86f451de2f1e12a24f5596a29828891c1eba13cd4714ed0c18a77d66cecef934",
25
25
  "local-integrity.d.mts": "21357f306234bd841c0d3d90bf2d09f7663f63746f897b9f38190428ae8198b3",
26
26
  "local-equivalence-runner.mjs": "df2f406c693c4da7bb30542ec7b4d46a9336f56e9fdd263196c0f3a73d2f6e91",
27
27
  "validator-runtime-build.mjs": "da20bd19eb088d79d62e33969b4c075dc2199761b395e40060227f8f86b9519a",
@@ -29,7 +29,7 @@
29
29
  "runtime/validator-runtime-v7.0.44.d.mts": "7797edbfe2f7de24da370204dce4dfd22822a1bd80df93514fff900b98afe36f",
30
30
  "snapshot-retention.mjs": "4f8919fe8ec49fb4c23e080a8ad2fa3a9282bca3fffe7cf55c64e89ef316697e",
31
31
  "snapshot-cleanup-journal.mjs": "76a9684cc95b4eb076e1298d78a9dff37948b00d9607b8f546c599072d3ed331",
32
- "IDE-EXECUTION.md": "fe1c65819749fdd7cad4663eab7d11e6e566ef623dc30352e47effe0bd1263d9",
32
+ "IDE-EXECUTION.md": "64f48dd283f8b7ad76bd33b542baebc3ae4380315c4b7158974927fd4f7a4c17",
33
33
  "REUSE-DISCIPLINE.md": "295ecd41c931b00c21d009ab65ac160f0e2ab81b4000ed498d2e8e97c2dc6c63",
34
34
  "reuse-discipline-release.mjs": "20ded9c21bd71b8779c454f5eba4c7c0a943ef99d94117b9bbef891d2ab6e3de",
35
35
  "local-final-gate16.mjs": "b30caab08441449ad556a6e9a3cefbf91190f0b3f7370f46d2a21156375aa8e3",
@@ -73,15 +73,17 @@
73
73
  "local-autocoord17-waits.mjs": "2b0a713987822548a94e1ed3a9a891532db45383797dfc5d5bc8d1e35593cf25",
74
74
  "local-validator-isolation17.mjs": "48e94604e28b8fddd567dfb35ff60b73fd094a5803bd49f076388b7f310f92aa",
75
75
  "local-calctool-composition17.mjs": "ac89fcb41ab0d0d9b47c1b5696945281b0894eb064820775e36a011c76314e6a",
76
- "cli.mjs": "bb99f5a52c770ded055fe27a35b3067ac348441b9488df336e6bf751b803e459",
76
+ "cli.mjs": "14e27d25acf2838f3eddd24a21ec438baa82650ac2235fb471f15718e996109f",
77
+ "install-cleanup.mjs": "72bb6bd55382449a986828cd1ae3e98e5fd0bf373f89bbdcc039023ae72a2337",
77
78
  "review-client.mjs": "3491eef68ed533174b31db3c53febb6519b8f088f71a9964644714a9a4c03c77",
78
79
  "version.mjs": "1f392147d72c40af4b03b307ea607075fb815af5174fa07e254b95fe12dbf3e7",
79
- "SKILL.md": "d89c2a83a080a217d67d75fa0251b0f054b92e81a9e40a6c0b52c7ecf3d79099",
80
+ "SKILL.md": "5531f764b82b3c2fe89592d291d05f508711b1498a65c75219c2b4396b532467",
80
81
  "CAPABILITY-MAP.md": "a0f0468a098725ced52a96dd8e22bfc7d1ce7681a8446ebf7dae3696c76c3385",
81
82
  "AGREEMENT.md": "f93a68e92c457200dd024610e65f74e478b8f60860f951da818b70561f1b9bf8",
82
- "package.json": "a522540d3967d9348a37c6538c1abb39fe6d50872aa8f7f90b85e920590091c7",
83
+ "DELIVERY-BOUNDARIES.md": "d1bb2895e23fc14bf1c76707bd1f6f03b39695501a78a0b7289dc37b099889dc",
84
+ "package.json": "8be823b5756da9b393af966a4152bee301cb49f988823aaf757e8c51bf867085",
83
85
  "manifest.json": "ab06e307795bc9da5add926752b6c4dbdaf18625090261a035aa356a679c504f",
84
- "release.mjs": "5a84da222a179cdc3b6be5db2a4a1c48085de27dff378b7598eb725f0627c98b",
86
+ "release.mjs": "9b71dc6dbda1e8db1097f0b14dbc1d66b3968ed241813f32851ceef54facec77",
85
87
  "local-cli.mjs": "aa4c217c5029408020f76d6c0c5dafc9a0f51216085b2624f3b6aca3cffbe555",
86
88
  "local-state-lock.mjs": "1dbf64264642a0851faf043e07ee9f66d2ad0508e9f799b0626246e42fae893e",
87
89
  "local-execution.mjs": "46fddd78cd954fb080c660affe90cdaa716695580427336acb820089441a8025",
@@ -114,8 +116,9 @@
114
116
  "local-budget-source-bindings.mjs": "cfe97b257fd2da0dd45f95f5e45be20baa6445d11ac6d03538a0105b4985b8ff",
115
117
  "local-budget-store.mjs": "2ae36c977ecd32d81f88e2eaa697ce8c228cb4405ecc5c4169cdda104c8ce138",
116
118
  "local-budget-transition.mjs": "3894f05d4b5d4d6e16e3f201ff8902e6311cf6434010262934e0d1251aa8b9f0",
117
- "local-budget.mjs": "e4ba1b4873a7625f9ff3c43e932b7289e7f95468c9dd88c2aeddd1242d7ea415",
119
+ "local-budget.mjs": "857bf879addbfbc13bd95d0957b223e05e73a69b8092f1d1933f09ff7968659a",
118
120
  "local-flow-lifecycle.mjs": "47534e16599002e5e7cf7d3e95195f4d7a364d2b651ec339d8c6c017d71c1b05",
121
+ "local-stage-boundary.mjs": "84c3bb5beab29306274c5ca8a78f19a83ed664ae1b93a8372bb493158b304fe3",
119
122
  "local-scope-candidate.mjs": "8f0bd9606e70d4a0ebacdd3cd600ec3afed85e2bb3e1760aa1144cf55d4e028a",
120
123
  "local-scope-context.mjs": "79753abfc6c76089d17f003a1be537dfaea10d6731c542f7fc53f2a9996ed164",
121
124
  "local-scope-files.mjs": "96c301f2a45e5fa22f0b11fb15e5e564f118e53187fc7749263c78d5ed87b056",
@@ -149,7 +152,7 @@
149
152
  "local-scope-state.mjs": "8b600a91fc0068d7a8c0bb752ddba7ed30ada93daafb118d6cba2368fd94cb27",
150
153
  "local-scope-workspaces.mjs": "a44c34537e5ed2dcaf193a985e5bc05957989fbb160a650f9e37ba7d66eb2de8",
151
154
  "local-task-routing.mjs": "6e2e506b2fb0e2e8d8c5147d7df32849b57a4e94a23634f01a355d45735858e4",
152
- "remote-client.mjs": "9e4ef758fb7d83e30e82dca68623740e7848cd5a527b7a90846e125d00df0a12",
155
+ "remote-client.mjs": "8c2450613480d0bdb39e942fb1dea14d74d5ffadd3c72809c74aa5fbc3c8d773",
153
156
  "cloud-task-client.mjs": "11acb1a033c94cdfbdfb177a3402ad97f6a5226f577138c8cf6395dc3ebb1a39",
154
157
  "scope-runner-client.mjs": "646ab4f80d4a8209e922fd45f40efd968f0744d9dfc03188b222c1a2e11e83b8",
155
158
  "native-client.mjs": "6f5575909bae9daba643287f876ffa0f9ff1c53a367b32a40145d740456d6605",
@@ -159,7 +162,7 @@
159
162
  "cloud-event-stream.mjs": "e0eec9fc3e0613094523929634f25030df5ca5c5b5fe2c1e1a4586ab10574808",
160
163
  "ide-presentation.mjs": "3bac21bfc824fcdad63aafb0d7c3326cd6456923f4224106dd5c38682060f315",
161
164
  "automatic-feedback-client.mjs": "75e92f50d6fbfa4f482af36e16c9ce5dd323a02e99eb9b434dcfa3faa846a532",
162
- "task-entry-client.mjs": "1ebbbfd26c978e7cb291ab4930974bb86d1fb56f1a702808b8dc348e2cdf69f1",
165
+ "task-entry-client.mjs": "869f234402c4149eadf1ec138705fc140e2fa16b2a3ea5dc2b2f51dfb189d7de",
163
166
  "task-identity.mjs": "e0e6cf1f48bfa5ee3c5cb86a3c21adfce6d66b4220f4a164ca099263c7a09fb4",
164
167
  "task-identity.d.mts": "55472e1ffc81b9d5e5c5bd56c0bf1d18ecde03ee5170a58a15ce9caf222d8761",
165
168
  "remote-transport.mjs": "93caaca646fca298a4e08142678b134ecc3a657e00de5b9939e2364b2db9d6c4",
@@ -177,12 +180,12 @@
177
180
  "local-task-handoff-write-archive.mjs": "3668f39d8ab5664ed717994cf1b4bcc7ea23e146a0e06291f851d30337faeb77",
178
181
  "local-task-handoff-write-receipts.mjs": "e1321127bd6fcad7840e185639f81ae68a8722a0ee4264a7b504a02b27887647",
179
182
  "workflow-local-runner.mjs": "9fa899a11da2b6b7a436bf9b561641856adcbc5f8b914f6ba89a64dae224900d",
180
- "workflow-local-contracts.mjs": "604afa9e97c6dfc3d07b01509ef2476887a2fc219c9e827bcac5d563ab4719e7",
183
+ "workflow-local-contracts.mjs": "bc52a461727b34c72daa058e1aa77d58206eafad14a93a347537f9969ae51184",
181
184
  "workflow-local-client.mjs": "45c954f694d96bf0ea87ee2dc6902a6763dac2cb1df713b6a5e749632054e9af",
182
185
  "workflow-local-request.mjs": "4e24c543ce386292a87dc9cd41f90d23375f12079cacffc515c8cd01fd571780",
183
186
  "task-message-entry.mjs": "31d10056cb905237cd5649e8eb067009ff1612ec5dc1061e0373209fbc0b646a",
184
187
  "task-message-cli.mjs": "3ca65e96c28129b3bd5cac962d106e0859a1f17df1f6a66ee543c6efc22f918d",
185
- "local-contracts.mjs": "d47022162233ee0b840bfd808b9d12590f4a367133ef0999233b7f76d76daa43",
188
+ "local-contracts.mjs": "bb1df682f06782f1c8814d50fe1c455153b29f3a1a093e87dd4ab2ed5ae16e1a",
186
189
  "local-contract-types.mjs": "85e71fa4dd67e351a4b67573c740bcdb1661cede53f097189f11837771e75e91",
187
190
  "local-archguard-ast.mjs": "7ff7df9dab0d98ed62983add751c80edea598bc94ef4f38ede0ebd4d41226c87",
188
191
  "local-archguard-checkpoint.mjs": "37896902520cc995720c96288e4a5ed3585952b55c9c35be281f91fa754c71ec",
package/release.mjs CHANGED
@@ -48,7 +48,7 @@ const sourceFiles = [
48
48
  'local-merge-transaction.mjs',
49
49
  ...swarmSourceFiles,
50
50
  ...coordination17SourceFiles,
51
- 'cli.mjs', 'review-client.mjs', 'version.mjs', 'SKILL.md', 'CAPABILITY-MAP.md', 'AGREEMENT.md',
51
+ 'cli.mjs', 'install-cleanup.mjs', 'review-client.mjs', 'version.mjs', 'SKILL.md', 'CAPABILITY-MAP.md', 'AGREEMENT.md', 'DELIVERY-BOUNDARIES.md',
52
52
  'package.json', 'manifest.json', 'release.mjs',
53
53
  'local-cli.mjs', 'local-state-lock.mjs', 'local-execution.mjs', 'local-integrity.mjs', 'local-evidence-verification.mjs', 'local-formula.mjs', 'local-ledger.mjs',
54
54
  'local-error.mjs',
@@ -60,7 +60,7 @@ const sourceFiles = [
60
60
  'local-budget-grants.mjs', 'local-budget-handoff.mjs', 'local-budget-handoff-contracts.mjs', 'local-budget-lock.mjs',
61
61
  'local-budget-handoff-completion.mjs',
62
62
  'local-budget-reads.mjs', 'local-budget-source-bindings.mjs', 'local-budget-store.mjs',
63
- 'local-budget-transition.mjs', 'local-budget.mjs', 'local-flow-lifecycle.mjs',
63
+ 'local-budget-transition.mjs', 'local-budget.mjs', 'local-flow-lifecycle.mjs', 'local-stage-boundary.mjs',
64
64
  'local-scope-candidate.mjs',
65
65
  'local-scope-context.mjs',
66
66
  'local-scope-files.mjs',
@@ -299,8 +299,10 @@ function releaseLabels() {
299
299
  'archguard-runtime-build.mjs': { zh: 'Archguard 架构合同与执行守卫', en: 'Archguard architecture contract and execution guard' },
300
300
  'runtime/archguard-runtime-v7.0.44.mjs': { zh: 'Archguard 架构合同与执行守卫', en: 'Archguard architecture contract and execution guard' },
301
301
  'cli.mjs': { zh: '技能运行和受管安装', en: 'skill runtime and managed installation' },
302
+ 'install-cleanup.mjs': { zh: '旧版受管安装清理', en: 'managed installation cleanup' },
302
303
  'version.mjs': { zh: '版本安全检查', en: 'version safety checks' },
303
304
  'SKILL.md': { zh: 'IDE 工作流程说明', en: 'IDE workflow instructions' },
305
+ 'DELIVERY-BOUNDARIES.md': { zh: '任务交付边界说明', en: 'task delivery boundaries' },
304
306
  'CAPABILITY-MAP.md': { zh: '能力覆盖映射', en: 'capability coverage map' },
305
307
  'AGREEMENT.md': { zh: '使用、隐私与推广告知', en: 'usage, privacy, and promotion notice' },
306
308
  'package.json': { zh: '安装包配置', en: 'package configuration' },
@@ -329,6 +331,7 @@ function releaseLabels() {
329
331
  'local-budget-transition.mjs': { zh: '候选范围与正式范围预算转换', en: 'candidate and accepted scope budget transition' },
330
332
  'local-budget.mjs': { zh: '本地三轴读取预算', en: 'local three-axis read budgets' },
331
333
  'local-flow-lifecycle.mjs': { zh: 'Flow 终止与任务解绑', en: 'flow termination and task unbinding' },
334
+ 'local-stage-boundary.mjs': { zh: '本地阶段边界与可恢复停止', en: 'local stage boundaries and recoverable stops' },
332
335
  'local-probe.mjs': { zh: '范围探测与分档', en: 'scope probing and tiering' },
333
336
  'local-scope-candidate.mjs': { zh: '临时接管范围独立验签', en: 'temporary takeover scope verification' },
334
337
  'local-scope-context.mjs': { zh: '精确符号范围绑定', en: 'exact symbol scope binding' },
@@ -400,6 +403,11 @@ const metadata = JSON.parse(await read('package.json'))
400
403
  const manifest = JSON.parse(await read('manifest.json'))
401
404
  const state = JSON.parse(await read(stateFile))
402
405
  const skill = await read('SKILL.md')
406
+ const generatedMetadata = new Set([stateFile, changelogFile, releasesFile])
407
+ const untrackedPackageFiles = metadata.files.filter(name => !generatedMetadata.has(name) && !sourceFiles.includes(name))
408
+ if (untrackedPackageFiles.length) {
409
+ throw new Error(`Packaged skill files lack release tracking: ${untrackedPackageFiles.join(', ')}`)
410
+ }
403
411
  if (metadata.version !== manifest.version || metadata.version !== state.version
404
412
  || !skill.includes(`Version: ${metadata.version}.`)) {
405
413
  throw new Error('Skill package, manifest, instruction, and release state versions differ')
package/releases.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "version": "0.1.43",
4
+ "date": "2026-10-16",
5
+ "zh": "更新了Codex 后续轮次工具门禁、复用与变更一致性门禁、IDE 执行与复用纪律文档、技能运行和受管安装、旧版受管安装清理、IDE 工作流程说明、任务交付边界说明、安装包配置、自动版本与更新日志、本地三轴读取预算、本地阶段边界与可恢复停止、远端恢复客户端、账号沙箱偏好与任务入口确认、工作流本地调用契约。",
6
+ "en": "Updated Codex tool guards across turns, reuse and change consistency checks, IDE execution and reuse discipline documentation, skill runtime and managed installation, managed installation cleanup, IDE workflow instructions, task delivery boundaries, package configuration, automatic versioning and changelog, local three-axis read budgets, local stage boundaries and recoverable stops, remote recovery client, account sandbox preferences and task entry confirmation, workflow local invocation contracts."
7
+ },
8
+ {
9
+ "version": "0.1.42",
10
+ "date": "2026-10-16",
11
+ "zh": "修复受管钩子空文件恢复、旧安装清理及仓库控制快照误扫。",
12
+ "en": "Restored empty managed hook configs, cleaned old installations, and excluded control snapshots from semantic scanning."
13
+ },
14
+ {
15
+ "version": "0.1.41",
16
+ "date": "2026-10-15",
17
+ "zh": "修复 0.1.39 受管技能升级与 Codex hook 配置权限误拒;补齐本地 probe/reassess 的 importContexts 输入合同。",
18
+ "en": "Fixed upgrades from the 0.1.39 managed skill and Codex hook config permission checks; added importContexts to local probe and reassess inputs."
19
+ },
2
20
  {
3
21
  "version": "0.1.40",
4
22
  "date": "2026-10-15",
package/remote-client.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
+ import { pathToFileURL } from 'node:url';
2
3
  import Ajv from 'ajv';
3
4
  import { parseTransportResponse, requestTransport, trustedOrigin } from './remote-transport.mjs';
4
5
  import { appendInvocation } from './local-invocations.mjs';
@@ -79,7 +80,8 @@ export async function invokeRemote(data, dependencies) {
79
80
  if (['blocked','failed','incomplete'].includes(routed.status)) process.exitCode = 2;
80
81
  return routed;
81
82
  }
82
- const { response, result } = await invokeDiscovered(data, token, { home, apiOrigin });
83
+ const resolvedApiOrigin = typeof apiOrigin === 'function' ? apiOrigin : () => new URL(apiUrl).origin;
84
+ const { response, result } = await invokeDiscovered(data, token, { home, apiOrigin: resolvedApiOrigin });
83
85
  process.stdout.write(JSON.stringify(presentInvocationNotices(result,dependencies.onIdeEvent,{home})) + '\n');
84
86
  if (!response.ok || result.output.status === 'blocked' || result.output.status === 'failed'
85
87
  || result.output.status === 'incomplete') process.exitCode = 2;
@@ -233,6 +235,81 @@ export async function queryStatus(requestId, dependencies) {
233
235
  process.stdout.write(JSON.stringify(presentInvocationNotices(result,dependencies.onIdeEvent)) + '\n');
234
236
  }
235
237
 
238
+ // Host quota (WP-D): a declared host adapter may report the host's own
239
+ // remaining quota. Without one, the answer is explicitly unknown and the
240
+ // skill keeps its own measured ledger only.
241
+ export async function hostQuotaView(environment = process.env) {
242
+ const declared = String(environment.ENTROROVE_IDE_ADAPTER ?? '').split(',').map(value => value.trim()).filter(Boolean);
243
+ for (const pathname of declared) {
244
+ let adapter;
245
+ try { const module = await import(pathToFileURL(pathname).href); adapter = module.default ?? module.adapter; }
246
+ catch { return { schemaVersion: 'entrorove.host-quota/1.0', state: 'unknown', reason: 'adapter-unavailable', detail: '宿主额度接口无法加载;只按 entrorove 这本账止损。' }; }
247
+ if (typeof adapter?.hostQuota !== 'function') continue;
248
+ let value;
249
+ try { value = await adapter.hostQuota(); }
250
+ catch { return { schemaVersion: 'entrorove.host-quota/1.0', state: 'unknown', reason: 'adapter-unavailable', detail: '宿主额度接口调用失败;只按 entrorove 这本账止损。' }; }
251
+ if (value && value.state === 'measured' && Number.isSafeInteger(value.remaining) && value.remaining >= 0) {
252
+ return { schemaVersion: 'entrorove.host-quota/1.0', state: 'measured', remaining: value.remaining, basis: 'host-reported' };
253
+ }
254
+ return { schemaVersion: 'entrorove.host-quota/1.0', state: 'unknown', reason: 'adapter-unavailable', detail: '宿主额度接口未返回可用测量值;只按 entrorove 这本账止损。' };
255
+ }
256
+ return { schemaVersion: 'entrorove.host-quota/1.0', state: 'unknown', reason: 'no-host-adapter', detail: '未接入宿主额度接口;只按 entrorove 这本账止损。' };
257
+ }
258
+
259
+ async function recurringCall(pathname, method, payload, dependencies, label) {
260
+ const token = await dependencies.readCredential(dependencies.home);
261
+ const response = await requestTransport(dependencies.apiOrigin(), pathname, { method,
262
+ headers: { authorization: `Bearer ${token}`, ...(payload ? { 'content-type': 'application/json' } : {}) },
263
+ ...(payload ? { body: JSON.stringify(payload) } : {}) });
264
+ if (response.status === 204) return null;
265
+ const result = await readResponse(response, label);
266
+ if (!response.ok) throw new Error(`${label} failed: HTTP ${response.status}`);
267
+ return result;
268
+ }
269
+
270
+ export async function listRecurring(dependencies) {
271
+ process.stdout.write(JSON.stringify(await recurringCall('/api/recurring', 'GET', null, dependencies, 'recurring list')) + '\n');
272
+ }
273
+
274
+ export async function claimRecurring(claimId, dependencies) {
275
+ const claimed = await recurringCall('/api/recurring/claim', 'POST', { claimId }, dependencies, 'recurring claim');
276
+ process.stdout.write(JSON.stringify(claimed ?? { claimed: false, detail: '当前没有到期的周期任务。' }) + '\n');
277
+ }
278
+
279
+ export async function finishRecurring(runId, input, dependencies) {
280
+ if (!/^[a-f0-9-]{36}$/u.test(runId)) throw new Error('finishing a recurring run requires its runId');
281
+ process.stdout.write(JSON.stringify(await recurringCall(`/api/recurring/runs/${runId}`, 'POST', input, dependencies, 'recurring finish')) + '\n');
282
+ }
283
+
284
+ export async function readRecurringRuns(recurringId, dependencies) {
285
+ if (!/^[a-f0-9-]{36}$/u.test(recurringId)) throw new Error('recurring runs require the original recurringId');
286
+ process.stdout.write(JSON.stringify(await recurringCall(`/api/recurring/${recurringId}/runs`, 'GET', null, dependencies, 'recurring runs')) + '\n');
287
+ }
288
+
289
+ export async function queryBillingExport(dependencies) {
290
+ const token = await dependencies.readCredential(dependencies.home);
291
+ const response = await readOnlyGet(dependencies.apiOrigin(), '/api/skill/billing-export', { authorization: `Bearer ${token}` }, 'billing export');
292
+ const result = await readResponse(response, 'billing export');
293
+ if (response.status === 404) throw new Error('entrorove billing failed: HTTP 404; the reachable server does not expose /api/skill/billing-export yet (deploy the matching API version)');
294
+ if (!response.ok || result?.schemaVersion !== 'entrorove.billing-export/1.0') {
295
+ throw new Error(`entrorove billing failed: HTTP ${response.status}`);
296
+ }
297
+ process.stdout.write(JSON.stringify(result) + '\n');
298
+ }
299
+
300
+ export async function queryUsageSummary(flowId, dependencies) {
301
+ if (flowId !== undefined && !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(flowId)) throw new Error('usage requires an original flowId when given');
302
+ const path = flowId === undefined ? '/api/skill/usage-summary' : `/api/skill/usage-summary?flowId=${encodeURIComponent(flowId)}`;
303
+ const token = await dependencies.readCredential(dependencies.home);
304
+ const response = await readOnlyGet(dependencies.apiOrigin(), path, { authorization: `Bearer ${token}` }, 'usage summary');
305
+ const result = await readResponse(response, 'usage summary');
306
+ if (response.status === 404) throw new Error('entrorove usage failed: HTTP 404; the reachable server does not expose /api/skill/usage-summary yet (deploy the matching API version)');
307
+ if (!response.ok || result?.schemaVersion !== 'entrorove.usage-summary/1.0') {
308
+ throw new Error(`entrorove usage failed: HTTP ${response.status}`);
309
+ }
310
+ process.stdout.write(JSON.stringify({ ...result, hostQuota: await hostQuotaView(dependencies.environment) }) + '\n');
311
+ }
312
+
236
313
  export async function queryFlows(dependencies) {
237
314
  const token = await dependencies.readCredential(dependencies.home);
238
315
  const response = await readOnlyGet(dependencies.apiOrigin(), '/api/skill/flows', { authorization: `Bearer ${token}` }, 'flow list')
@@ -5,6 +5,10 @@ import { displayIdeEvent, ideClientHeaders, ideTaskIdentity } from './ide-presen
5
5
  /** New IDE tasks use the authenticated account preference; resumes keep their original route. */
6
6
  export async function routeTaskEntry(data, token, dependencies) {
7
7
  if (data.operation !== 'start' || data.input?.sandboxTaskId) return null;
8
+ // Older embedders can invoke the remote client without the optional cloud
9
+ // sandbox preference callback. Keep those callers on the normal API path;
10
+ // production installs provide the callback and retain the preference gate.
11
+ if (typeof dependencies.apiOrigin !== 'function') return null;
8
12
  const choice = data.sandboxChoice;
9
13
  if (choice !== undefined && !['use','local'].includes(choice)) throw new Error('sandboxChoice must be use or local');
10
14
  if (choice === 'local') {
@@ -1,4 +1,5 @@
1
1
  import { array, enumeration, integer, ledgerIdentifier, object, relativePath, sha256 } from './local-contract-types.mjs';
2
+ import { DISCIPLINE_POLICY_SCHEMA } from './local-discipline-contracts.mjs';
2
3
 
3
4
  const exact = properties => object(properties, Object.keys(properties));
4
5
  const boundedArray = (items, minimum, maximum) => ({ ...array(items, minimum), maxItems: maximum });
@@ -17,6 +18,7 @@ const executionPolicy = exact({ executable: absolutePath, executableSha256: sha2
17
18
  additionalProperties: { type: 'string', maxLength: 16_384, pattern: '^[^\\u0000]*$' } },
18
19
  timeoutMs: integer(1, 300_000), maxOutputBytes: integer(1, 1024 * 1024), requiredExitCode: { const: 0 },
19
20
  });
21
+ executionPolicy.properties.discipline = DISCIPLINE_POLICY_SCHEMA;
20
22
  const baseline = exact({ schemaVersion: { const: 'validator.golden-baseline/1.0' }, baselineId: id,
21
23
  source: exact({ kind: enumeration(['repository-commit', 'artifact', 'approved-record']), locator: shortText, digestSha256: sha256 }),
22
24
  version: id, frozen: { const: true }, frozenAt: timestamp, frozenBy: id, testsSha256: sha256,