@entrorove/skill 0.1.67 → 0.1.69
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/CAPABILITY-MAP.md +2 -0
- package/CHANGELOG.md +10 -0
- package/IDE-EXECUTION.md +12 -0
- package/REUSE-DISCIPLINE.md +2 -0
- package/SKILL.md +2 -1
- package/cli.mjs +1 -1
- package/connected-cli.mjs +2 -0
- package/ide-command.mjs +1 -1
- package/ide-hook.mjs +4 -4
- package/local-contracts.mjs +5 -2
- package/local-discipline-gate.mjs +2 -0
- package/local-discipline-repository.mjs +12 -8
- package/local-discipline-snapshot.mjs +8 -2
- package/local-mutation-snapshot.mjs +2 -2
- package/local-preserved-mutation-protection.mjs +2 -0
- package/local-scope-repository.mjs +27 -1
- package/local-scope-signature.mjs +4 -1
- package/manifest.json +1 -1
- package/mutation-client.mjs +8 -5
- package/package.json +8 -1
- package/release-state.json +32 -25
- package/release.mjs +3 -2
- package/releases.json +12 -0
- package/remote-client.mjs +1 -0
- package/scope-amendment-observer.mjs +3 -1
- package/scope-amendment-state.mjs +3 -1
- package/scope-context-client.mjs +134 -0
- package/scope-context-observation.mjs +42 -0
- package/scope-context-observer.mjs +61 -0
- package/scope-context-release.mjs +8 -0
- package/scope-context-remote.mjs +60 -0
- package/scope-context-state.mjs +88 -0
- package/scope-context-wire.mjs +84 -0
- package/scope-runner-client.mjs +9 -5
package/CAPABILITY-MAP.md
CHANGED
|
@@ -100,6 +100,8 @@ Entrorove 只治理通过技能入口执行的动作:路径、通行证、快
|
|
|
100
100
|
|
|
101
101
|
`scope-amend <original repositoryRoot>` 使用原 S 签名人完成同一 flow 的精确范围补正。先通过 `workflow-scope-review` 审核完整文件、哈希与写入/只读角色,再提供 `{flowId,proposalId,proposalDigest}`。真实审批、原合同和剩余预算仍有效;成功后计划绑定返回的 `scopeRevisionDigest`,原入口与失败历史保留。详见 [IDE-EXECUTION.md](IDE-EXECUTION.md#correcting-omitted-scope-before-plan-freeze)。
|
|
102
102
|
|
|
103
|
+
`scope-context <original repositoryRoot>` 处理首次范围事实尚未建立时遗漏的真实构建上下文。`review` 保存完整旧/新映射和实际配置、构建来源摘要,经原账号明确回答后,`apply` 用原 S、服务挑战、真实快照和导入图追加上下文权威记录;再以全部原路径继续 `scope-connect`。保留原 binding、签名器及失败历史,不授予写入或完成权限,已有范围事实或 gate 后不能使用此入口。远端 `scope-runner-context-review/status/challenge/apply` 均以实时发现为准。
|
|
104
|
+
|
|
103
105
|
当前增量操作(不改写上述历史分母)为:`peer-status`、`discipline-check`、`discipline-review`、`flow-cancel`、`flow-abandon`、`guarded-delete`、`guarded-move`、`mutation-delegate-prepare`、`mutation-consumption-attest`。它们计入当前 109 项公开 schema。
|
|
104
106
|
|
|
105
107
|
| 分区 | 精确操作名 | 证据位置 |
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.69 — 2026-10-20
|
|
4
|
+
|
|
5
|
+
- 新增首次范围门禁前的构建上下文纠正:原账号审核真实配置和构建证据,原 S 签名完整路径快照,通过服务挑战追加上下文权威记录;保留原 flow、绑定、旧上下文和失败历史,不授予写入或完成权限。
|
|
6
|
+
- Add build-context recovery before the first scope gate: original owner review of actual configuration and build evidence, original S proof of all target paths, and challenge-bound append-only context authority. Preserve the original flow, binding, contexts and failures without granting writes or completion.
|
|
7
|
+
|
|
8
|
+
## 0.1.68 — 2026-10-20
|
|
9
|
+
|
|
10
|
+
- 修复大目录扫描的调用栈溢出:逐项收集完整目录清单,保留忽略文件摘要、符号链接边界、原范围与签名及前后扫描变化拒绝;原流程可在标准升级后重试。
|
|
11
|
+
- Fix call-argument overflow in large repository scans by collecting every inventory path individually. Preserve ignored-file hashes, symlink boundaries, original scope and signatures, and inventory-drift refusal; retry the original flow after a standard managed upgrade.
|
|
12
|
+
|
|
3
13
|
## 0.1.67 — 2026-10-19
|
|
4
14
|
|
|
5
15
|
- 更新了包降级、版本冲突与凭证回滚门禁。
|
package/IDE-EXECUTION.md
CHANGED
|
@@ -92,6 +92,18 @@ When intake returns `workflow-gate`, connect the current repository before invok
|
|
|
92
92
|
|
|
93
93
|
Scope inspection resolves aliases from actual `tsconfig.json` or `jsconfig.json`, including relative and npm `extends` and ordered inheritance arrays. Npm configuration lookup uses the pinned TypeScript JSON-config resolver over controlled metadata reads, including scoped packages, subpaths, package `tsconfig`, `exports` and `typesVersions`. Read-only candidate probes treat a child beneath an ordinary file as absent so normal TypeScript fallbacks continue; actual sources, control files and mutation targets retain strict validation. Rejected paths report the requested path, blocking ancestor, resolved target and reason, including broken or cyclic dependency links. Configurations and package metadata enter the signed hashes; dependencies are read only, never executed. Repository-local pnpm links use their actual configuration location. Read-only external dependency packages support parents and mappings inside their reference boundary; references requiring an unrepresentable external canonical ancestor fail explicitly rather than selecting a different same-name repository package. For plugin source compiled by a host project whose config is outside the source's ancestors, include `importContexts: [{"sourcePrefix":"<plugin source directory>","configPath":"<actual host tsconfig.json>"}]` in `scope-connect` input. Inspect the real build configuration first; never invent an alias or mark an unresolved import as external. These context paths remain inside the repository, their hashes enter the signed facts, and the local task binding freezes them.
|
|
94
94
|
|
|
95
|
+
### Correcting build contexts before the first scope gate
|
|
96
|
+
|
|
97
|
+
An unresolved alias can occur after the original local signer has been enrolled but before any scope facts or gate have been accepted. Ordinary `scope-connect` still rejects a changed `importContexts` list. Use the explicit `scope-context <original absolute repositoryRoot>` recovery on that same flow; do not edit `identity/binding.json`, replace its key, drop target paths or create a replacement flow.
|
|
98
|
+
|
|
99
|
+
First inspect the actual host configurations and the real build source that copies or compiles the plugin mirror. Submit JSON on stdin with `action: "review"`, the original `flowId`, the complete `importContexts` list (all original mappings unchanged, followed by the concrete missing mappings), `evidencePaths` naming the actual repository build-source files, and a concrete `reason`. Each mapping still has exactly `sourcePrefix` and `configPath`. Prefixes must identify actual directories and configurations must be real repository `tsconfig` or `jsconfig` files. No generated alias or invented project configuration can substitute.
|
|
100
|
+
|
|
101
|
+
The SDK signs the exact proposal with the original S key. The service checks the original accepted owner, task, repository, signer, complete target set and current context revision, then issues an original-owner Confirm Protocol question showing the exact old/new mappings and observed evidence hashes. Show that unchanged question using the host question tool. Only the user's actual answer may call its original `interaction-answer` callback. Existing task authorization does not fabricate this exact context approval; silence remains pending. These configuration and build-evidence files remain read-only and hash-bound during later scope refreshes and file-role amendments; this approval cannot authorize changing their alias semantics or source bytes.
|
|
102
|
+
|
|
103
|
+
After approval, run `scope-context` with `{ "action": "apply", "flowId": "<original flow>", "proposalId": "<returned proposal>", "proposalDigest": "<returned digest>" }`. It obtains a fresh server challenge, verifies a real copied snapshot and import graph using all original target paths and the proposed contexts, and submits the original S signature. Configuration or build-evidence drift, an unresolved actual alias, stale revision, expired/consumed nonce, foreign identity or missing approval rejects the attempt. No context approval grants writes or completes the task.
|
|
104
|
+
|
|
105
|
+
Successful application appends a server-signed context authority and local history while preserving the original binding bytes. Retry `scope-connect` with its returned complete `nextInput`, then resume the original gate request. Later ordinary scope refreshes and `scope-amend` use that effective context authority; their signed facts bind its digest and exact mappings. M snapshot preparation and independent guarded writes recover the same context authority from the original private history and snapshot without a caller-selected mapping or S private-key read; their separate original M approval remains required. Status uses `{ "action": "status", "flowId": "<original flow>" }`. An unknown POST must recover its original request with GET through the saved journal; never repeat a POST to guess its outcome. This recovery is unavailable after the first accepted scope facts, successful gate, frozen plan or execution; preserve that later flow's original authority instead. The existing strict handoff protocol does not transfer a corrected context head; do not choose another receiver to bypass this binding.
|
|
106
|
+
|
|
95
107
|
### Correcting omitted scope before plan freeze
|
|
96
108
|
|
|
97
109
|
If the original selection omitted actual components or inspection files, use the discovered `workflow-scope-review` on the **same flow**, with its original gate digest, current effective revision (`0` before the first amendment), and the complete sorted exact file map. Each entry declares its real `path`, `sha256`, `access` (`write` or `read-only`), original `requirementRefs` and reason. Include all original entries even when reducing their access to read-only. Existing dependencies may become writable only through this exact new review; a directory prefix or manifest inclusion does not grant that permission.
|
package/REUSE-DISCIPLINE.md
CHANGED
|
@@ -6,6 +6,8 @@ UI changes also require [DESIGN-GATES.md](DESIGN-GATES.md): approved flow, resea
|
|
|
6
6
|
|
|
7
7
|
Snapshot scans stream whole-repository integrity hashes and bound semantic work to 1 MiB per source, 8 MiB of retained source, 512 files and 200,000 syntax nodes. Original target paths and proposed files receive capacity first. A resource-limited non-target source stays in the integrity comparison and is recorded as unresolved; `scanComplete` is false and absent references cannot prove a symbol unused. A target that exceeds a bound returns `ENTROROVE_DISCIPLINE_ANALYSIS_LIMIT` with its path and the exceeded limit. These are execution memory limits, never size-based review exemptions or approval thresholds. Preserve the original flow, signer, scope and queue during recovery. Upgrade with the standard managed installer, then retry the original `scope-connect` input; do not create a replacement task or bypass the host guard with an arbitrary Node environment wrapper.
|
|
8
8
|
|
|
9
|
+
Directory inventory appends each file to one shared list instead of expanding a whole subtree into call arguments. Large ignored directories, including `.artifacts`, remain in both integrity inventories; only `.git` and `node_modules` are excluded. Symlinks remain entries without traversal, and inventory drift still rejects the observation.
|
|
10
|
+
|
|
9
11
|
- D-1: Before another business mapping or fact definition, search actual definitions and reuse them. Snapshot planning records the whole repository comparison and AST usage review in `discipline-review.json`; its digest is covered by the signed original snapshot. Typed source-entity field mappings use the existing import/alias resolver to identify the same entity, then compare field/value sets. A conflicting second mapping is refused with the existing file and line. An unresolved semantic relation remains unknown. No overlap threshold is invented.
|
|
10
12
|
- D-2: A rewrite must cite a concrete existing defect at file:line. “Clearer”, “more maintainable” and “more general” are not evidence. A necessary sensitive exception is submitted through `continue` → `workflow-discipline-review` on the original planning phase. Display the whole concrete proposal. Approval must be the original account's stored Confirm Protocol answer and audit, never a caller's `accepted:true`.
|
|
11
13
|
- D-3: A parallel path requires frozen `policy.parallelPaths` and actual same-input, fieldwise equivalence evidence. Use the existing GoldenBaseline to pin baseline bytes and source hash. The approved runner reads the baseline and actual process output; the original output cannot be supplied anew by the implementation. Missing evidence yields incomplete, mismatch yields blocked with field names. Baselines come from the existing path's actual independent samples, not the new implementation's self-consistency tests.
|
package/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ description: Execute a task selected for Entrorove through explicit SDK operatio
|
|
|
5
5
|
|
|
6
6
|
# entrorove · 熵巡
|
|
7
7
|
|
|
8
|
-
Version: 0.1.
|
|
8
|
+
Version: 0.1.69.
|
|
9
9
|
|
|
10
10
|
Entrorove applies only to the task for which it is selected; reading these instructions, discovering capabilities or installing the package never selects or governs another task. Hooks are optional. Reading this document or calling an entry once does not execute the task. On every later turn, including after compaction, continue the original task and its required next operation before dependent work. Keep the original goal, scope, account, flow, cloud task and failure records. Never describe planning, a reported step or model output as verified delivery.
|
|
11
11
|
|
|
@@ -46,6 +46,7 @@ When a new user requirement arrives while the original task is unfinished, store
|
|
|
46
46
|
- Invoke service operations with JSON stdin to `npx @entrorove/skill invoke` or a host-approved rewritten equivalent.
|
|
47
47
|
- Explicit domains use `native <domain> <operation>`. Domains: aimlock, blueprint, calctool, swarm, validator, mergeguard, archguard, confirm-protocol. Aimlock `brain-plan`, `brain-check`, `brain-validate`, `brain-status` use the original stored plan.
|
|
48
48
|
- At `workflow-gate`, connect the actual repository and exact source paths with `scope-connect`, then use its original `nextInput`. Scope inspection does not grant execution or writes. Dynamic references remain unknown; never set graph completeness manually.
|
|
49
|
+
- If a real missing build context blocks the first scope scan, use the same-flow `scope-context` review and original-owner answer described in [IDE-EXECUTION.md](IDE-EXECUTION.md#correcting-build-contexts-before-the-first-scope-gate). Apply its exact approved proposal with the original S signer and fresh actual snapshot, then retry the returned complete `scope-connect` input. Keep the original binding, all paths and failed attempts; ordinary reconnects cannot silently change contexts.
|
|
49
50
|
- If actual files were omitted before plan freeze, follow the discovered same-flow `workflow-scope-review`, original owner approval and `scope-amend` recovery in [IDE-EXECUTION.md](IDE-EXECUTION.md#correcting-omitted-scope-before-plan-freeze). Preserve each file's explicit write or read-only role and bind the resulting `scopeRevisionDigest` in the corrected plan; never widen ordinary `scope-connect` input to bypass its original binding.
|
|
50
51
|
- `local capabilities` describes scope, snapshot, budget, ownership, mutation, review and evidence contracts. Do not call a guessed operation or construct a local signature in place of cloud authority.
|
|
51
52
|
- Cloud events and homepage progress share the same account-bound stream. Show public entering, allocation, model activity, output and termination as they occur. Show the server-selected promotion and execution notice separately. Deduplicate task/event/receipt IDs; never invent progress or model reasoning.
|
package/cli.mjs
CHANGED
|
@@ -485,7 +485,7 @@ async function main() {
|
|
|
485
485
|
} else if (command === 'local') {
|
|
486
486
|
await runLocal(args);
|
|
487
487
|
} else {
|
|
488
|
-
throw new Error('Usage: entrorove exit | unlock | uninstall | install [--host codex] [--hooks|--no-hooks] | configure | agreement | invoke | queue | native <domain> <operation> | notifications configure|status|test | scope-connect <repositoryRoot> | scope-amend <repositoryRoot> | mutation-connect <repositoryRoot> | mutation-settle <repositoryRoot> | workflow-observe-step <repositoryRoot> | host-message [adapterModulePath] | cloud-task | cloud-watch | cloud-status | feedback <requestId> [generation] | status <requestId> | flows | usage [flowId] | billing | recurring list|claim|finish|runs | review | local (JSON on stdin)');
|
|
488
|
+
throw new Error('Usage: entrorove exit | unlock | uninstall | install [--host codex] [--hooks|--no-hooks] | configure | agreement | invoke | queue | native <domain> <operation> | notifications configure|status|test | scope-connect <repositoryRoot> | scope-amend <repositoryRoot> | scope-context <repositoryRoot> | mutation-connect <repositoryRoot> | mutation-settle <repositoryRoot> | workflow-observe-step <repositoryRoot> | host-message [adapterModulePath] | cloud-task | cloud-watch | cloud-status | feedback <requestId> [generation] | status <requestId> | flows | usage [flowId] | billing | recurring list|claim|finish|runs | review | local (JSON on stdin)');
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
491
|
if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) { const { captureIdeResults } = await import('./ide-results.mjs');
|
package/connected-cli.mjs
CHANGED
|
@@ -2,10 +2,12 @@ import { runScopeConnect } from './scope-runner-client.mjs';
|
|
|
2
2
|
import { runMutationConnect, runMutationSettle } from './mutation-client.mjs';
|
|
3
3
|
import { runWorkflowObserveStep } from './workflow-observation-client.mjs';
|
|
4
4
|
import { runScopeAmend } from './scope-amendment-client.mjs';
|
|
5
|
+
import { runScopeContext } from './scope-context-client.mjs';
|
|
5
6
|
|
|
6
7
|
const commands = Object.freeze({
|
|
7
8
|
'scope-connect': runScopeConnect,
|
|
8
9
|
'scope-amend': runScopeAmend,
|
|
10
|
+
'scope-context': runScopeContext,
|
|
9
11
|
'mutation-connect': runMutationConnect,
|
|
10
12
|
'mutation-settle': runMutationSettle,
|
|
11
13
|
'workflow-observe-step': runWorkflowObserveStep,
|
package/ide-command.mjs
CHANGED
|
@@ -90,7 +90,7 @@ export function sdkCommand(parsed, runtime) {
|
|
|
90
90
|
if (args[0] === 'usage' && args.length <= 2 && !parsed.input
|
|
91
91
|
&& (args[1] === undefined || /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(args[1]))) return { args };
|
|
92
92
|
if (args[0] === 'local' && /^[a-z][a-z0-9-]+$/u.test(args[1] ?? '') && [2,3].includes(args.length) && parsed.input) return { args, input: parsed.input };
|
|
93
|
-
if (['scope-connect', 'scope-amend', 'mutation-connect', 'mutation-settle', 'workflow-observe-step'].includes(args[0]) && args.length === 2 && parsed.input) return { args, input: parsed.input };
|
|
93
|
+
if (['scope-connect', 'scope-amend', 'scope-context', 'mutation-connect', 'mutation-settle', 'workflow-observe-step'].includes(args[0]) && args.length === 2 && parsed.input) return { args, input: parsed.input };
|
|
94
94
|
if (args[0] === 'native' && args.length === 3 && parsed.input) return { args, input: parsed.input };
|
|
95
95
|
return null;
|
|
96
96
|
}
|
package/ide-hook.mjs
CHANGED
|
@@ -26,7 +26,7 @@ const inputErrors = {
|
|
|
26
26
|
const publicId = value => typeof value === 'string' && /^[A-Za-z0-9_-]{1,200}$/u.test(value);
|
|
27
27
|
const readTools = new Set(['wait', 'view_image', 'list_mcp_resources', 'list_mcp_resource_templates', 'read_mcp_resource', 'web_search', 'update_plan', 'request_user_input', 'request_user_input_async', 'get_goal']);
|
|
28
28
|
const unlockCommand = 'entrorove unlock';
|
|
29
|
-
const managementOperations = new Set(['capabilities', 'help', 'start', 'continue', 'task-describe', 'flow-cancel', 'flow-abandon', 'scope-connect', 'mutation-connect', 'mutation-settle', 'mutation-consumption-attest', 'mutation-delegate-prepare', 'status', 'feedback', 'cloud-status', 'cloud-watch', 'queue', 'unlock', 'usage', 'billing', 'recurring']);
|
|
29
|
+
const managementOperations = new Set(['capabilities', 'help', 'start', 'continue', 'task-describe', 'flow-cancel', 'flow-abandon', 'scope-connect', 'scope-runner-context-review', 'scope-runner-context-status', 'scope-runner-context-challenge', 'scope-runner-context-apply', 'mutation-connect', 'mutation-settle', 'mutation-consumption-attest', 'mutation-delegate-prepare', 'status', 'feedback', 'cloud-status', 'cloud-watch', 'queue', 'unlock', 'usage', 'billing', 'recurring']);
|
|
30
30
|
function operationOf(call) {
|
|
31
31
|
if (!call) return null;
|
|
32
32
|
if (call.args[0] === 'invoke') return call.input?.operation ?? null;
|
|
@@ -178,11 +178,11 @@ async function beforeTool(event, state, home) {
|
|
|
178
178
|
const observer = discovery || ['status','feedback','cloud-status','configure','agreement','install','queue','flows','usage','billing','recurring'].includes(call.args[0]);
|
|
179
179
|
if (unclassifiedIdeRequirements(state) && !observer && !(unlocked && isManagementCall(call))) return denyBlocked(state, event, 'Entrorove:新需求已保存,先用 queue status 读取并按轻重缓急 route 分类;禁止丢弃新需求或覆盖原任务。');
|
|
180
180
|
if (newStart && !checkQueuedStart(state,call)) return denyBlocked(state, event, 'Entrorove:新任务目标必须与实际进入队列的用户原文一致。');
|
|
181
|
-
if (['local', 'scope-amend', 'mutation-connect', 'mutation-settle', 'workflow-observe-step'].includes(call.args[0]) && !discovery && !state.flowId) return denyBlocked(state, event, 'Entrorove:本地执行前必须先受理原任务并绑定账号、flowId 与仓库;不能直接使用其它流程的通行证。');
|
|
181
|
+
if (['local', 'scope-amend', 'scope-context', 'mutation-connect', 'mutation-settle', 'workflow-observe-step'].includes(call.args[0]) && !discovery && !state.flowId) return denyBlocked(state, event, 'Entrorove:本地执行前必须先受理原任务并绑定账号、flowId 与仓库;不能直接使用其它流程的通行证。');
|
|
182
182
|
if (state.pendingCall && !observer && !(unlocked && isManagementCall(call))) return denyBlocked(state, event, 'Entrorove:原调用尚未收到结束回执;先只读恢复其状态,禁止并发重发。');
|
|
183
183
|
const input = call.args[0] === 'invoke' ? call.input?.input : call.input;
|
|
184
184
|
if (input?.flowId && state.flowId && input.flowId !== state.flowId || input?.taskId && state.taskId && ['cloud-watch','cloud-status'].includes(call.args[0]) && input.taskId !== state.taskId) return denyBlocked(state, event, 'Entrorove:调用与本 IDE 任务的原编号不一致。');
|
|
185
|
-
if (['scope-connect', 'scope-amend', 'mutation-connect', 'mutation-settle', 'workflow-observe-step'].includes(call.args[0]) && call.args[1] !== state.repositoryRoot
|
|
185
|
+
if (['scope-connect', 'scope-amend', 'scope-context', 'mutation-connect', 'mutation-settle', 'workflow-observe-step'].includes(call.args[0]) && call.args[1] !== state.repositoryRoot
|
|
186
186
|
|| call.args[0] === 'local' && (input?.repositoryRoot && input.repositoryRoot !== state.repositoryRoot || call.args[2] && call.args[2] !== state.repositoryRoot)) return denyBlocked(state, event, 'Entrorove:调用不能替换本任务的实际仓库。');
|
|
187
187
|
let requestId;
|
|
188
188
|
if (newStart) state.taskGoal = call.args[0] === 'cloud-task' ? call.input?.goal : call.input?.input?.task?.goal;
|
|
@@ -191,7 +191,7 @@ async function beforeTool(event, state, home) {
|
|
|
191
191
|
requestId = buildInvocation(call.input.operation, call.input.input).requestId;
|
|
192
192
|
}
|
|
193
193
|
const pending = { toolUseId: event.tool_use_id, inputDigest: ideDigest(call), command: call.args[0],
|
|
194
|
-
operation: operationOf(call), observer, managementOnly: observer || call.args[0]
|
|
194
|
+
operation: operationOf(call), observer, managementOnly: observer || ['scope-amend', 'scope-context'].includes(call.args[0]) || isManagementCall(call)
|
|
195
195
|
&& !['start', 'continue', 'cloud-task'].includes(operationOf(call)) || operationOf(call) === 'task-checkpoint',
|
|
196
196
|
...(requestId ? { requestId } : {}) };
|
|
197
197
|
originalLocalCall(pending, call, input);
|
package/local-contracts.mjs
CHANGED
|
@@ -103,7 +103,8 @@ const scopeTask = object({ goal: { ...text, pattern: '\\S', maxLength: 12000 },
|
|
|
103
103
|
unclear: { type: 'boolean' }, architectureContract: { type: 'boolean' }, explicitAimlockRequested: { type: 'boolean' }, explicitEntroroveRequested: { type: 'boolean' }, userSpecifiedSkills: array(text),
|
|
104
104
|
requirements: { ...array(scopeRequirement, 1), maxItems: 500 }, phase: enumeration(['plan', 'coordinate', 'architecture', 'verify', 'merge', 'decision']), baselineRef: text, interaction: scopeInteraction }, ['goal', 'workType', 'difficulty', 'risk']);
|
|
105
105
|
const probe = { ...step, paths: probePaths, targetPaths: probePaths, task: scopeTask, baselineRef: text, snapshotId: identifier,
|
|
106
|
-
targetSymbols: array(text, 1), contextMapPath: relativePath, importContexts,
|
|
106
|
+
targetSymbols: array(text, 1), contextMapPath: relativePath, importContexts, contextRevisionDigest: sha256,
|
|
107
|
+
contextEvidence: { ...array(object({ path: relativePath, sha256 }, ['path', 'sha256'])), maxItems: 200 }, signerConfigPath: nullable(text),
|
|
107
108
|
taskDigest: sha256, ownerId: ledgerIdentifier, chainDigest: sha256, repositoryId: identifier, challenge: ledgerIdentifier };
|
|
108
109
|
add('probe', { ...probe, handoffCandidate: HANDOFF_SNAPSHOT_CANDIDATE_SCHEMA }, ['repositoryRoot']);
|
|
109
110
|
schemas.probe.allOf = [{ if: { type: 'object', anyOf: ['flowId', 'stepId', 'snapshotId'].map(field => ({ required: [field] })) },
|
|
@@ -112,7 +113,9 @@ schemas.probe.allOf = [{ if: { type: 'object', anyOf: ['flowId', 'stepId', 'snap
|
|
|
112
113
|
then: { type: 'object', required: ['flowId', 'stepId', 'snapshotId', 'taskDigest', 'ownerId', 'chainDigest', 'repositoryId', 'challenge'] } },
|
|
113
114
|
{ if: { type: 'object', required: ['handoffCandidate'] }, then: { type: 'object', required: ['flowId', 'stepId', 'snapshotId', 'paths', 'taskDigest', 'ownerId', 'chainDigest', 'repositoryId', 'challenge', 'signerConfigPath'],
|
|
114
115
|
properties: { signerConfigPath: text } } },
|
|
115
|
-
{ if: { type: 'object', required: ['targetSymbols'] }, then: { type: 'object', required: ['contextMapPath'] } }
|
|
116
|
+
{ if: { type: 'object', required: ['targetSymbols'] }, then: { type: 'object', required: ['contextMapPath'] } },
|
|
117
|
+
{ if: { type: 'object', required: ['contextRevisionDigest'] }, then: { type: 'object', required: ['importContexts'] } },
|
|
118
|
+
{ if: { type: 'object', required: ['contextEvidence'] }, then: { type: 'object', required: ['contextRevisionDigest', 'importContexts'] } }];
|
|
116
119
|
add('reassess', { ...probe, snapshotId: nullable(identifier), currentMode: modes, expectedRevision: integer(1) }, ['repositoryRoot']);
|
|
117
120
|
schemas.reassess.allOf = [{ if: { type: 'object', anyOf: [
|
|
118
121
|
...['flowId', 'stepId', 'expectedRevision', 'taskDigest', 'ownerId', 'chainDigest', 'repositoryId', 'challenge'].map(field => ({ required: [field] })),
|
|
@@ -9,6 +9,7 @@ import { assertDelegatedSourceModification } from './local-delegated-modify-anal
|
|
|
9
9
|
import { assertDesignWrite } from './design-gate.mjs';
|
|
10
10
|
import { assertPackageReplacement } from './package-replacement.mjs';
|
|
11
11
|
import { assertRollbackCandidate } from './rollback-history.mjs';
|
|
12
|
+
import { scopeContextSnapshotInput } from './scope-context-observation.mjs';
|
|
12
13
|
|
|
13
14
|
const needs = message => mutationFail14('ENTROROVE_DISCIPLINE_NEEDS_HUMAN', message);
|
|
14
15
|
function declaredWrite(input, pass, baseline) {
|
|
@@ -84,6 +85,7 @@ export async function disciplineWritePreflight(input, value, content) {
|
|
|
84
85
|
}
|
|
85
86
|
const { pass, target, binding, snapshot, ledger } = value;
|
|
86
87
|
const review = await readDisciplineSnapshot(snapshot), baseline = snapshot.manifest.files.find(file => file.path === target);
|
|
88
|
+
input = (await scopeContextSnapshotInput(input, binding.root, review.scopeContext)).input;
|
|
87
89
|
await assertDisciplineRepository(input, binding.root, review, ledger);
|
|
88
90
|
const intent = declaredWrite(input, pass, baseline);
|
|
89
91
|
const originalBytes = baseline.exists ? (await readSnapshotCopy14(input, snapshot.snapshotRoot, snapshot.manifest, target)).content : Buffer.alloc(0);
|
|
@@ -44,15 +44,19 @@ export async function repositoryIntegrityFile(root, relative) {
|
|
|
44
44
|
ctimeMs: last.ctimeMs, mtimeMs: last.mtimeMs, size: last.size } };
|
|
45
45
|
} finally { await handle.close(); }
|
|
46
46
|
}
|
|
47
|
-
async function inventory(root
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
export async function inventory(root) {
|
|
48
|
+
const files = [];
|
|
49
|
+
async function visit(prefix) {
|
|
50
|
+
const entries = await fs.readdir(path.join(root, prefix), { withFileTypes: true });
|
|
51
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
52
|
+
if (excluded.has(entry.name)) continue;
|
|
53
|
+
const relative = mutationPath14(prefix ? `${prefix}/${entry.name}` : entry.name);
|
|
54
|
+
if (entry.isDirectory()) await visit(relative);
|
|
55
|
+
else if (entry.isFile() || entry.isSymbolicLink()) files.push(relative);
|
|
56
|
+
else mutationFail14('ENTROROVE_DISCIPLINE_SCAN_UNKNOWN', 'Repository comparison encountered an unsupported filesystem entry');
|
|
57
|
+
}
|
|
55
58
|
}
|
|
59
|
+
await visit('');
|
|
56
60
|
return files.sort();
|
|
57
61
|
}
|
|
58
62
|
export async function disciplineRepositoryObservation(input, root, { analyze = false, overrides = new Map() } = {}) {
|
|
@@ -2,16 +2,22 @@ import path from 'node:path';
|
|
|
2
2
|
import { canonical, sha256 } from './local-integrity.mjs';
|
|
3
3
|
import { atomicMutationJson14, mutationFail14, readMutationJson14 } from './local-mutation-files.mjs';
|
|
4
4
|
import { disciplineRepositoryObservation } from './local-discipline-repository.mjs';
|
|
5
|
+
import { scopeContextSnapshotInput } from './scope-context-observation.mjs';
|
|
5
6
|
|
|
6
7
|
export function snapshotDisciplineBinding(value) {
|
|
7
8
|
if (!Object.hasOwn(value, 'disciplineReviewDigest')) return {};
|
|
8
9
|
if (!/^[0-9a-f]{64}$/.test(value.disciplineReviewDigest ?? '')) mutationFail14('ENTROROVE_DISCIPLINE_REVIEW_INVALID', 'Snapshot discipline review digest is invalid');
|
|
9
10
|
return { disciplineReviewDigest: value.disciplineReviewDigest };
|
|
10
11
|
}
|
|
11
|
-
export async function captureDisciplineSnapshot(input, root, location, binding) {
|
|
12
|
-
|
|
12
|
+
export async function captureDisciplineSnapshot(input, root, location, binding, contextAuthority) {
|
|
13
|
+
// A fresh S context candidate carries its own signed challenge mapping before
|
|
14
|
+
// admission. It must not be mistaken for an already accepted M snapshot head.
|
|
15
|
+
const selected = !contextAuthority && input.contextRevisionDigest !== undefined ? { input, context: null }
|
|
16
|
+
: await scopeContextSnapshotInput(input, root, contextAuthority, { initial: true });
|
|
17
|
+
const observed = await disciplineRepositoryObservation(selected.input, root, { analyze: true });
|
|
13
18
|
const review = { schemaVersion: 'entrorove.discipline-review/1.0', ...binding,
|
|
14
19
|
repositoryDigest: sha256(root).slice(0, 32), createdAt: new Date().toISOString(), ...observed,
|
|
20
|
+
...(selected.context ? { scopeContext: selected.context } : {}),
|
|
15
21
|
thresholds: { deletionAdditionRatio: null, semanticOverlap: null, directLines: null, usageScopeLimit: null } };
|
|
16
22
|
const digest = sha256(canonical(review));
|
|
17
23
|
await atomicMutationJson14(path.join(location.snapshotRoot, 'discipline-review.json'), review, { privateFrom: location.stateRoot });
|
|
@@ -148,7 +148,7 @@ async function copySource(input, root, snapshotRoot, relative, stateRoot) {
|
|
|
148
148
|
await writeMutationFile14(path.join(snapshotRoot, 'files', relative), read.content, { privateFrom: stateRoot });
|
|
149
149
|
return { path: relative, exists: true, sha256: read.sha256, mode: read.mode };
|
|
150
150
|
}
|
|
151
|
-
export async function snapshotPlan14(input, { handoffBinding: binding = {} } = {}) {
|
|
151
|
+
export async function snapshotPlan14(input, { handoffBinding: binding = {}, contextAuthority } = {}) {
|
|
152
152
|
const root = await mutationRoot14(input.repositoryRoot);
|
|
153
153
|
const flowId = mutationId14(input.flowId, 'flowId'); const stepId = mutationId14(input.stepId, 'stepId');
|
|
154
154
|
const paths = mutationPaths14(input.paths); const handoff = handoffBinding(binding);
|
|
@@ -162,7 +162,7 @@ export async function snapshotPlan14(input, { handoffBinding: binding = {} } = {
|
|
|
162
162
|
await privateMutationDirectory14(path.join(loc.snapshotRoot, 'files'), { privateFrom: loc.stateRoot });
|
|
163
163
|
const files = [];
|
|
164
164
|
for (const relative of paths) files.push(await copySource(input, root, loc.snapshotRoot, relative, loc.stateRoot));
|
|
165
|
-
const discipline = await captureDisciplineSnapshot(input, root, loc, { snapshotId, flowId, stepId });
|
|
165
|
+
const discipline = await captureDisciplineSnapshot(input, root, loc, { snapshotId, flowId, stepId }, contextAuthority);
|
|
166
166
|
const core = { snapshotId, flowId, stepId, paths, files, contractDigest, ...handoff, ...discipline };
|
|
167
167
|
const manifest = { schemaVersion: SNAPSHOT_SCHEMA, ...core, repositoryDigest: repoKey(root), copyLayout: COPY_LAYOUT,
|
|
168
168
|
createdAt: new Date().toISOString(), snapshotDigest: sha256(canonical(core)) };
|
|
@@ -5,6 +5,7 @@ import { readDisciplineSnapshot } from './local-discipline-snapshot.mjs';
|
|
|
5
5
|
import { preservedMutationDelegation, preservedMutationFail } from './local-preserved-mutation-wire.mjs';
|
|
6
6
|
import { preservedMutationProtectedFiles } from './local-preserved-mutation-state.mjs';
|
|
7
7
|
import { scopeCandidateImports } from './local-scope-repository.mjs';
|
|
8
|
+
import { scopeContextSnapshotInput } from './scope-context-observation.mjs';
|
|
8
9
|
|
|
9
10
|
function unknownTarget(path, site) {
|
|
10
11
|
const reason = typeof site?.reason === 'string' && /^[a-z-]+$/.test(site.reason) ? site.reason : 'unknown-import-source';
|
|
@@ -23,6 +24,7 @@ async function observe(input, root, expected) {
|
|
|
23
24
|
}
|
|
24
25
|
export async function capturePreservedMutationProtection(input, root, authority, snapshot) {
|
|
25
26
|
const delegation = preservedMutationDelegation(authority), review = await readDisciplineSnapshot(snapshot);
|
|
27
|
+
input = (await scopeContextSnapshotInput(input, root, review.scopeContext)).input;
|
|
26
28
|
for (const expected of delegation.targetFiles) {
|
|
27
29
|
const file = snapshot.manifest.files.find(item => item.path === expected.path);
|
|
28
30
|
if (delegation.purpose === 'planned-file-create') {
|
|
@@ -364,7 +364,9 @@ export async function collectScopeFacts(root, requested, baselineRef, knownFiles
|
|
|
364
364
|
const read = await scopeRead(root, relative, input); const boundary = await packageBoundary(root, relative, input);
|
|
365
365
|
files.push({ path: relative, exists: true, sha256: read.sha256, bytes: read.bytes, lines: lineCount(read.content), module: boundary.path });
|
|
366
366
|
}
|
|
367
|
-
const graph = await importGraph(root, files, input);
|
|
367
|
+
const graph = await importGraph(root, files, input);
|
|
368
|
+
await contextEvidence(root, graph, input);
|
|
369
|
+
const observedGit = await observeGitFacts(root, files, baselineRef, input); const history = observedGit.facts;
|
|
368
370
|
const modules = [...new Map(graph.nodes.map(node => [node.boundary.path, node.boundary])).values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
369
371
|
await stableScope(root, files, input);
|
|
370
372
|
await stableScope(root, modules.filter(module => module.manifestPath).map(module => ({ path: module.manifestPath, sha256: module.manifestDigest })), input);
|
|
@@ -377,3 +379,27 @@ export async function collectScopeFacts(root, requested, baselineRef, knownFiles
|
|
|
377
379
|
crossModule: modules.length > 1, risk: observedScopeRisk(files), importGraph: graph, git: history,
|
|
378
380
|
estimatedChangedLines };
|
|
379
381
|
}
|
|
382
|
+
async function contextEvidence(root, graph, input) {
|
|
383
|
+
if (input.contextEvidence === undefined) return;
|
|
384
|
+
if (!/^[a-f0-9]{64}$/.test(input.contextRevisionDigest ?? '') || !Array.isArray(input.contextEvidence)
|
|
385
|
+
|| input.contextEvidence.length > 200 || new Set(input.contextEvidence.map(file => file?.path)).size !== input.contextEvidence.length) {
|
|
386
|
+
scopeFail('ENTROROVE_SCOPE_CONTEXT_BINDING', 'Context evidence requires its bounded unique actual file hashes and revision');
|
|
387
|
+
}
|
|
388
|
+
const metadata = new Map(graph.workspaceMetadata.map(file => [file.path, file])); let bytes = 0;
|
|
389
|
+
for (const file of input.contextEvidence) {
|
|
390
|
+
if (!file || Object.keys(file).sort().join(',') !== 'path,sha256' || !/^[a-f0-9]{64}$/.test(file.sha256 ?? '')) {
|
|
391
|
+
scopeFail('ENTROROVE_SCOPE_CONTEXT_BINDING', 'Context evidence requires exact path and hash fields');
|
|
392
|
+
}
|
|
393
|
+
const target = await scopeTarget(root, file.path);
|
|
394
|
+
if (!target.stat.isFile() || target.stat.size > 1_048_576 || (bytes += target.stat.size) > 8_388_608) {
|
|
395
|
+
scopeFail('ENTROROVE_SCOPE_LIMIT', 'Context evidence exceeds its bounded actual file inspection');
|
|
396
|
+
}
|
|
397
|
+
const read = await scopeRead(root, file.path, input);
|
|
398
|
+
if (read.sha256 !== file.sha256 || metadata.has(file.path) && metadata.get(file.path).sha256 !== read.sha256) {
|
|
399
|
+
scopeFail('ENTROROVE_SCOPE_CHANGED', 'Actual context build evidence changed since its exact review');
|
|
400
|
+
}
|
|
401
|
+
if (!metadata.has(file.path)) metadata.set(file.path, { path: file.path, sha256: read.sha256 });
|
|
402
|
+
}
|
|
403
|
+
graph.workspaceMetadata = [...metadata.values()].sort((left, right) => left.path.localeCompare(right.path));
|
|
404
|
+
await stableScope(root, graph.workspaceMetadata, input);
|
|
405
|
+
}
|
|
@@ -38,6 +38,8 @@ export async function attestScopeFacts(input, result, { maximumExpiresAt } = {})
|
|
|
38
38
|
scopeFail('ENTROROVE_SCOPE_ATTESTATION_BINDING', 'scope signature deadline must be a finite exact bound');
|
|
39
39
|
}
|
|
40
40
|
rejectScopePrivileges(input);
|
|
41
|
+
if (Object.hasOwn(input, 'contextRevisionDigest') && (!/^[a-f0-9]{64}$/.test(input.contextRevisionDigest ?? '')
|
|
42
|
+
|| !Array.isArray(input.importContexts))) scopeFail('ENTROROVE_SCOPE_ATTESTATION_BINDING', 'Context proof requires its exact contexts and revision digest together');
|
|
41
43
|
if (input.signerConfigPath === undefined || input.signerConfigPath === null) return result;
|
|
42
44
|
binding(input, result);
|
|
43
45
|
const root = await scopeRoot(input.repositoryRoot); const snapshot = await readScopeSnapshot(input, root);
|
|
@@ -68,7 +70,8 @@ export async function attestScopeFacts(input, result, { maximumExpiresAt } = {})
|
|
|
68
70
|
taskDigest: input.taskDigest, ownerId: input.ownerId, chainDigest: input.chainDigest, repositoryId: input.repositoryId,
|
|
69
71
|
facts: result.facts, snapshotDigest: result.snapshotDigest, challenge: input.challenge,
|
|
70
72
|
issuedAt: new Date(now).toISOString(), expiresAt: new Date(expiresAt).toISOString(),
|
|
71
|
-
nonce: randomUUID(), keyId: config.keyId
|
|
73
|
+
nonce: randomUUID(), keyId: config.keyId,
|
|
74
|
+
...(input.contextRevisionDigest === undefined ? {} : { importContexts: input.importContexts, contextRevisionDigest: input.contextRevisionDigest }) };
|
|
72
75
|
const signature = sign(null, Buffer.from(canonical(core)), privateKey).toString('base64url');
|
|
73
76
|
return { ...result, attestation: { ...core, signature }, trusted: false, status: 'reported' };
|
|
74
77
|
}
|
package/manifest.json
CHANGED
package/mutation-client.mjs
CHANGED
|
@@ -20,6 +20,7 @@ import { scopeAmendmentRefreshAction } from './scope-amendment-reference.mjs';
|
|
|
20
20
|
import { readMutationConnection as readConnection } from './mutation-connection-state.mjs';
|
|
21
21
|
import { readMutationEnvironmentObservation } from './local-mutation-environment-observation.mjs';
|
|
22
22
|
import { refreshExpiredMutationChallenge } from './mutation-challenge-refresh.mjs';
|
|
23
|
+
import { scopeContextObservation, scopeContextSnapshotInput } from './scope-context-observation.mjs';
|
|
23
24
|
|
|
24
25
|
const fields = ['taskId', 'flowId', 'stepId', 'leaseId', 'generation', 'runnerId', 'paths'];
|
|
25
26
|
const fail = message => mutationFail14('ENTROROVE_MUTATION_CONNECTION_BINDING', message);
|
|
@@ -110,14 +111,14 @@ function passBinding(pass, challenge, attestation, options) {
|
|
|
110
111
|
.some(field => canonical(pass[field]) !== canonical(challenge[field]))
|
|
111
112
|
|| canonical(pass.writeDelegation) !== canonical(challenge.writeDelegation)) fail('Mutation pass differs from its original signed challenge or actual M snapshot');
|
|
112
113
|
}
|
|
113
|
-
async function snapshot(input, challenge, state, location) {
|
|
114
|
+
async function snapshot(input, challenge, state, location, contextAuthority) {
|
|
114
115
|
if (!state.planned) {
|
|
115
116
|
if (state.planStarted) {
|
|
116
117
|
const recovered = await snapshotVerify14(input);
|
|
117
118
|
state.planned = { snapshotId: recovered.receipt.snapshotId, snapshotDigest: recovered.receipt.snapshotDigest };
|
|
118
119
|
} else {
|
|
119
120
|
state.planStarted = true; await save(location, state);
|
|
120
|
-
try { state.planned = await snapshotPlan14({ ...input, paths: challenge.paths, contractDigest: challenge.scopeContractDigest }); }
|
|
121
|
+
try { state.planned = await snapshotPlan14({ ...input, paths: challenge.paths, contractDigest: challenge.scopeContractDigest }, { contextAuthority }); }
|
|
121
122
|
catch (error) { delete state.planStarted; await save(location, state); throw error; }
|
|
122
123
|
}
|
|
123
124
|
await save(location, state);
|
|
@@ -149,7 +150,7 @@ async function authorizeCachedConnection(identity, pass, dependencies) {
|
|
|
149
150
|
}
|
|
150
151
|
return status;
|
|
151
152
|
}
|
|
152
|
-
async function connect(identity, local, mutator, location, dependencies) {
|
|
153
|
+
async function connect(identity, local, mutator, location, dependencies, contextAuthority) {
|
|
153
154
|
const state = await readConnection(location, identity);
|
|
154
155
|
let challenged = state.challenged ?? await remote('challenged', 'mutation-snapshot-challenge',
|
|
155
156
|
{ ...Object.fromEntries(fields.map(field => [field, identity[field]])),
|
|
@@ -178,7 +179,7 @@ async function connect(identity, local, mutator, location, dependencies) {
|
|
|
178
179
|
passBinding(state.issued.pass, challenge, state.attestation);
|
|
179
180
|
return connected(identity, state);
|
|
180
181
|
}
|
|
181
|
-
await snapshot(input, challenge, state, location);
|
|
182
|
+
await snapshot(input, challenge, state, location, contextAuthority);
|
|
182
183
|
if (!state.attestation) {
|
|
183
184
|
const observed = await attestMutationSnapshot14({ ...input, challenge, signerConfigPath: mutator.signerConfigPath });
|
|
184
185
|
if (observed.verified !== true || observed.attestation.keyId !== mutator.mutationKeyId) fail('Actual mutation snapshot was not signed by its separate M key');
|
|
@@ -209,11 +210,13 @@ export async function connectMutation(repositoryRoot, input, dependencies) {
|
|
|
209
210
|
if (target.exists && !target.stat.isFile()) fail('Mutation connection permits only an absent approved creation or a concrete existing file');
|
|
210
211
|
}
|
|
211
212
|
await bootstrapCloudMutationAuthority(dependencies);
|
|
213
|
+
const contextAuthority = await scopeContextObservation(local, location.root, dependencies.apiOrigin());
|
|
214
|
+
const observed = (await scopeContextSnapshotInput(local, location.root, contextAuthority, { initial: true })).input;
|
|
212
215
|
const mutator = await preparePreservedMutationSigner(local);
|
|
213
216
|
const identity = { ...original, repositoryRoot: location.root, apiOrigin: trustedOrigin(dependencies.apiOrigin()), mutationKeyId: mutator.mutationKeyId };
|
|
214
217
|
const directory = path.join(location.directory, 'connections'); await privateMutationDirectory14(directory, { privateFrom: location.stateRoot });
|
|
215
218
|
const file = path.join(directory, `${sha256(canonical(identity))}.json`), journal = { ...location, file };
|
|
216
|
-
return withStateLock(file, () => connect(identity,
|
|
219
|
+
return withStateLock(file, () => connect(identity, observed, mutator, journal, dependencies, contextAuthority));
|
|
217
220
|
}
|
|
218
221
|
function settlementBinding(output, pass, receipt, expectedSettlementId) {
|
|
219
222
|
if (output?.settled !== true || typeof output.settlementId !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(output.settlementId)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entrorove/skill",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.69",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"entrorove": "cli.mjs"
|
|
@@ -89,6 +89,13 @@
|
|
|
89
89
|
"local-task-handoff-write-receipts.mjs",
|
|
90
90
|
"remote-client.mjs",
|
|
91
91
|
"scope-runner-client.mjs",
|
|
92
|
+
"scope-context-client.mjs",
|
|
93
|
+
"scope-context-observer.mjs",
|
|
94
|
+
"scope-context-observation.mjs",
|
|
95
|
+
"scope-context-remote.mjs",
|
|
96
|
+
"scope-context-state.mjs",
|
|
97
|
+
"scope-context-wire.mjs",
|
|
98
|
+
"scope-context-release.mjs",
|
|
92
99
|
"scope-amendment-client.mjs",
|
|
93
100
|
"scope-amendment-state.mjs",
|
|
94
101
|
"scope-amendment-wire.mjs",
|