abelien-harness 0.20.1 → 0.22.0

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.
Files changed (41) hide show
  1. package/package.json +1 -1
  2. package/skills/abelien-appearance/references/points.md +7 -3
  3. package/skills/abelien-appearance/schemas/points-reading-reply.v1.json +2 -2
  4. package/skills/abelien-appearance/scripts/points.mjs +46 -4
  5. package/skills/abelien-appearance/scripts/spatial-images.mjs +16 -0
  6. package/skills/abelien-hardware/domain.json +33 -1
  7. package/skills/abelien-hardware/references/selection-service-contract.md +2 -0
  8. package/skills/abelien-harness/runtime/README.md +3 -0
  9. package/skills/abelien-harness/runtime/actions.mjs +8 -2
  10. package/skills/abelien-harness/runtime/engine.mjs +51 -10
  11. package/skills/abelien-harness/runtime/graph.mjs +4 -2
  12. package/skills/abelien-harness/runtime/index.mjs +2 -0
  13. package/skills/abelien-harness/runtime/model-actions.md +17 -0
  14. package/skills/abelien-harness/runtime/task-view/README.md +7 -2
  15. package/skills/abelien-harness/runtime/task-view/index.mjs +10 -4
  16. package/skills/abelien-harness/runtime/task-view/retention.mjs +39 -0
  17. package/skills/abelien-harness/scripts/ab.mjs +1 -1
  18. package/skills/abelien-structure/domain.json +1 -0
  19. package/skills/abelien-structure/flows/user-journey-cad.json +34 -2
  20. package/skills/abelien-structure/references/skeleton.md +1 -0
  21. package/skills/abelien-structure/scripts/cad.mjs +5 -4
  22. package/skills/abelien-workspace/actions/image.generate.json +2 -2
  23. package/skills/abelien-workspace/actions/step.tool-result.json +1 -1
  24. package/skills/abelien-workspace/assets/journey-stage-plan.json +465 -4
  25. package/skills/abelien-workspace/assets/lane-plan.json +2 -2
  26. package/skills/abelien-workspace/contract/host-tools.generated.json +463 -16
  27. package/skills/abelien-workspace/flows/creation.create.json +855 -152
  28. package/skills/abelien-workspace/flows/creation.modify.json +855 -152
  29. package/skills/abelien-workspace/flows/creation.remix.json +189 -2
  30. package/skills/abelien-workspace/flows/creation.restore.json +443 -6
  31. package/skills/abelien-workspace/references/actions/image.generate.generated.md +41 -2
  32. package/skills/abelien-workspace/references/actions/step.tool-result.generated.md +23 -3
  33. package/skills/abelien-workspace/references/actions.generated.md +1 -1
  34. package/skills/abelien-workspace/references/journey-stage-plan.md +35 -2
  35. package/skills/abelien-workspace/schemas/appearance-views.v1.json +208 -0
  36. package/skills/abelien-workspace/schemas/host-tools.v1.json +131 -5
  37. package/skills/abelien-workspace/schemas/image-task.v1.json +92 -0
  38. package/skills/abelien-workspace/schemas/plan-common.v1.json +3 -1
  39. package/skills/abelien-workspace/scripts/appearance-views.mjs +49 -0
  40. package/skills/abelien-workspace/scripts/creation-tasks.mjs +20 -10
  41. package/skills/abelien-workspace/scripts/flow-compile.mjs +3 -1
@@ -0,0 +1,39 @@
1
+ // Frozen node data is exact. Only declared, durably stored tool results may become read-back references.
2
+ // This contract is runtime-owned; text in refs, replies or summaries never changes its permissions.
3
+ import { runtimeError } from '../errors.mjs';
4
+ import { STEP_TOOL_RESULT } from '../model-actions.mjs';
5
+
6
+ export const CONTEXT_RETENTION_SCHEMA = 'abelien.node-context-retention.v1';
7
+ export const CONTEXT_RETENTION_REQUIRED = Object.freeze([
8
+ 'schema', 'reply_to', 'flow', 'target_revision_ref', 'attempt', 'goal', 'instruction', 'refs', 'skills',
9
+ 'actions', 'output', 'work', 'reply_without_model', 'correction', 'attempts', 'budget',
10
+ ]);
11
+ export function defaultContextRetention() {
12
+ return { schema: CONTEXT_RETENTION_SCHEMA, required: [...CONTEXT_RETENTION_REQUIRED],
13
+ history: { reasoning: 'summarize', assistant_text: 'exact', tool_results: { default: 'exact', by_action: {} } } };
14
+ }
15
+ const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);
16
+ const keys = (value, expected) => object(value) && Object.keys(value).length === expected.length && expected.every(key => Object.hasOwn(value, key));
17
+ /** Validate after action bindings freeze, and again when assembling an old frozen node. Never consult current package declarations. */
18
+ export function nodeContextRetention(node, { code = 'FLOW_INVALID' } = {}) {
19
+ const declared = node.task?.context_retention;
20
+ if (declared === undefined) return defaultContextRetention();
21
+ const fail = detail => { throw runtimeError(code, `model ${node.id} context_retention: ${detail}`, { node: node.id }); };
22
+ if (!keys(declared, ['schema', 'required', 'history']) || declared.schema !== CONTEXT_RETENTION_SCHEMA) fail('unsupported contract or fields');
23
+ if (!Array.isArray(declared.required) || declared.required.length !== CONTEXT_RETENTION_REQUIRED.length
24
+ || new Set(declared.required).size !== declared.required.length || !CONTEXT_RETENTION_REQUIRED.every(key => declared.required.includes(key))) fail('all task-view data fields must remain exact');
25
+ const h = declared.history;
26
+ if (!keys(h, ['reasoning', 'assistant_text', 'tool_results']) || h.reasoning !== 'summarize' || h.assistant_text !== 'exact') fail('reasoning must be summarize and assistant_text exact');
27
+ const results = h.tool_results;
28
+ if (!keys(results, ['default', 'by_action']) || results.default !== 'exact' || !object(results.by_action)) fail('tool results default must be exact with an action policy object');
29
+ const by_action = {};
30
+ for (const [id, policy] of Object.entries(results.by_action).sort(([a], [b]) => a.localeCompare(b))) {
31
+ if (!['exact', 'reference'].includes(policy)) fail(`unsupported tool result policy for ${id}`);
32
+ if (!Array.isArray(node.task?.actions) || !node.task.actions.includes(id) || !node.model_action_bindings?.[id]) fail(`action ${id} is not declared and frozen`);
33
+ if (policy === 'reference' && !node.model_action_bindings?.[STEP_TOOL_RESULT]) fail(`action ${id} reference requires frozen ${STEP_TOOL_RESULT}`);
34
+ Object.defineProperty(by_action, id, { value: policy, enumerable: true, writable: true, configurable: true });
35
+ }
36
+ const value = defaultContextRetention();
37
+ value.history.tool_results.by_action = by_action;
38
+ return value;
39
+ }
@@ -25,7 +25,7 @@ export { interpolate, findWorkflow, loadWorkflow, workflowDefinitionDigest, pars
25
25
  // doctor must report the shipped package version, never a frozen constant (0.2.3 drifted across
26
26
  // six releases). The npm tree always ships package.json at the skill root; standalone skill copies
27
27
  // fall back to FALLBACK_VERSION, which scripts/release-check.mjs pins to package.json.
28
- const FALLBACK_VERSION = "0.20.1";
28
+ const FALLBACK_VERSION = "0.22.0";
29
29
  export let VERSION = FALLBACK_VERSION;
30
30
  try {
31
31
  VERSION = String(JSON.parse(fs.readFileSync(new URL("../../../package.json", import.meta.url), "utf8")).version || FALLBACK_VERSION);
@@ -4,6 +4,7 @@
4
4
  "kind": "agent",
5
5
  "description": "结构域包(Agent 受众):骨架子流程的确定性动作(冻结布局请求、布局校验、换件条件)与 CAD 子流程 user-journey-cad:硬件前置模块按冻结选型修订与骨架生成硬件几何(模型只拿 ID)、装配源码并检查“只拿 ID”、结果检查与失败归因(只返工出错模块)。布局提案与自定义件模块由模型完成;CAD 预检与构建是宿主 Studio 工具(cad.precheck、cad.build),执行 CAD 的 Structure Worker 在宿主包",
6
6
  "requires": [
7
+ "abelien-appearance",
7
8
  "abelien-clarify",
8
9
  "abelien-hardware",
9
10
  "abelien-harness",
@@ -1,8 +1,11 @@
1
1
  {
2
- "input_schema": {"package": "abelien-structure", "schema": "schemas/cad-flow-input.v1.json"},
2
+ "input_schema": {
3
+ "package": "abelien-structure",
4
+ "schema": "schemas/cad-flow-input.v1.json"
5
+ },
3
6
  "schema": "abelien.runtime-flow.v2",
4
7
  "flow_id": "user-journey-cad",
5
- "version": "7",
8
+ "version": "8",
6
9
  "description": "CAD v2 子流程:宿主先按 component_id 解析组件 CAD(目录库采购件),再冻结硬件前置几何与视觉目标,模型只写自定义件。产物数据边调度装配、预检、构建与检查;corrects 有界返工只重写归因模块。换视觉、需求或外形锁定时重写自定义件,换件只重写服务于变化硬件的模块。输入见 cad-flow-input.v1.json。",
7
10
  "inputs": [
8
11
  "prelude",
@@ -171,6 +174,35 @@
171
174
  "module": "scripts/cad.mjs",
172
175
  "view": "cadModulesView",
173
176
  "finalize": "cadModulesFinalize"
177
+ },
178
+ "context_retention": {
179
+ "schema": "abelien.node-context-retention.v1",
180
+ "required": [
181
+ "schema",
182
+ "reply_to",
183
+ "flow",
184
+ "target_revision_ref",
185
+ "attempt",
186
+ "goal",
187
+ "instruction",
188
+ "refs",
189
+ "skills",
190
+ "actions",
191
+ "output",
192
+ "work",
193
+ "reply_without_model",
194
+ "correction",
195
+ "attempts",
196
+ "budget"
197
+ ],
198
+ "history": {
199
+ "reasoning": "summarize",
200
+ "assistant_text": "exact",
201
+ "tool_results": {
202
+ "default": "exact",
203
+ "by_action": {}
204
+ }
205
+ }
174
206
  }
175
207
  }
176
208
  },
@@ -6,6 +6,7 @@
6
6
 
7
7
  - 选型修订就绪后开始;点位产物 ready 或 degraded 都可以,读图失败不阻塞骨架。
8
8
  - 输入:冻结的选型修订(槽位、BOM 行、选中零件的真实尺寸)、点位产物、当前图片引用、外壳(盒形或圆柱形外包络、统一壁厚、外形是否锁定、未锁定时的外观容差)和本次检查容差;有拆机、官方结构或同类整机资料时一并给出。
9
+ - create/modify 还通过共享的 `work.images` 提供同版概念原图与内部空间视图,辅助部件连接、朝向、遮挡和空间判断;内部图标明 `generated_inference`,不替代组件真实尺寸或正式布局检查。CAD 从既有 appearance baseline 的 `spatial_views` 接收同一集合;不新增一套 CAD 输入协议。
9
10
  - 外壳尺寸由布局请求按来源生成(过渡口径,appearance 还不产出外观容差):`shell.basis` 为 `reference_product` 时只给轴向映射(盒形 x、y、z,圆柱 diameter、height 取参考尺寸的 width、depth、height),外壳固定、不带容差;为 `user_hard_limit` 时给 `limit_id`(`shell_sources` 里那条用户硬尺寸,盒形包络另给 `axes` 取包络的哪一维),包络(各轴上下限、壁厚下限)是上下限,各轴尺寸是提案方的选择,越界阻断、不带容差,上下限相等的轴写 `null` 由请求填值;只有图片可依时才用 `appearance_target`,给出各轴尺寸,外形未锁定时另给外观容差(带依据的估算),用它判定的外形检查注明“容差为估算”并记进未知项。每个数要么是选择,写 `{value, choice}`(数加依据),要么是派生,写 `Formula`(至少一个操作数用 `input` 指针取任务视图冻结输入(指针从任务视图 `work` 的根写起,例如 `/fixed_input/shell_sources/user_hard_limit/0/envelope/max_height_mm`)或用 `result` 取本步工具结果,例如取硬尺寸上限、锚定尺寸乘以量出的比例);只有 choice 操作数的 `Formula` 按选择写。出处记在请求的 `shell_source`。其余要算的数先用 `calc.evaluate`,不心算。
10
11
  - 不做按品类的骨架模板:同类整机资料只借分区方式、上下顺序和必备件,每个作品按自己的外观目标、需求和已选硬件推导,不照抄坐标。
11
12
  - 图鉴参考记录以 `source: "catalog"` 给出,带 `catalog_ref`(记录的发布版、摘要和借用的分区/基准面)。借的分区在骨架里用 `architecture_ref_id` 指回它;记录里的高度比例只作合理性对照(例如同类机电池都在底部、本方案放到了顶部),基准面高度仍按已选件的真实尺寸加间隙推导。
@@ -5,6 +5,7 @@
5
5
  // 返工不在这里推进:失败经流程的 corrects 回到建模节点,任务视图钩子(cadModulesView)按归因只列出错模块及依赖它的模块,
6
6
  // 其余模块按摘要原样沿用(cadModulesFinalize 补全回复并记下沿用来源)。布局规划不在 CAD 里重复:骨架已经过 structure.layout-check。
7
7
  import { createHash } from "node:crypto";
8
+ import { spatialImages } from "../../abelien-appearance/scripts/spatial-images.mjs";
8
9
  import { digest, canonical } from "../../abelien-harness/contracts/index.mjs";
9
10
  import { failure } from "../../abelien-harness/scripts/web/common.mjs";
10
11
  import { sliceCodePoints, storableText } from "../../abelien-harness/scripts/web/safe-text.mjs";
@@ -1120,16 +1121,16 @@ function productTargets(input, prelude) {
1120
1121
  */
1121
1122
  export function cadModulesView(ctx) {
1122
1123
  checkSchema("cad-flow-input.v1.json", ctx.run.input, "user-journey-cad input");
1123
- const image = ctx.run.input.appearance_baseline.data.image;
1124
+ const pictures = spatialImages({ appearance_views: ctx.run.input.appearance_baseline.data.spatial_views ? { data: ctx.run.input.appearance_baseline.data.spatial_views } : null }, ctx.run.input.appearance_baseline.data.image);
1124
1125
  const prelude = ctx.refs.prelude.data;
1125
1126
  const targets = { ...productTargets(ctx.run.input, prelude), shape_lock: shapeLockOf(ctx.run.input) };
1126
1127
  const previous = latest(ctx.history("modules"), item => item.state === "succeeded" && item.seq < ctx.current.seq);
1127
- if (!previous) return { work: { image, ...targets, mode: "initial", write: "all", attribution: null, changed_hardware: null, frozen: [], previous: [], dependencies: [] } };
1128
+ if (!previous) return { work: { ...pictures, ...targets, mode: "initial", write: "all", attribution: null, changed_hardware: null, frozen: [], previous: [], dependencies: [] } };
1128
1129
  const set = previous.output;
1129
1130
  const summary = module => ({ module_id: module.module_id, purpose: module.purpose, parts: module.parts, serves: module.serves, depends_on: module.depends_on, digest: module.digest });
1130
1131
  // 新的明确实例声明可删掉旧件;旧模块保留历史,不强迫模型为已删除实例再写一个占位件。
1131
1132
  if (!same(unique(set.modules.flatMap(module => module.parts)).sort(), targets.custom_occurrence_ids))
1132
- return { work: { image, ...targets, mode: "initial", write: "all", attribution: null, changed_hardware: null,
1133
+ return { work: { ...pictures, ...targets, mode: "initial", write: "all", attribution: null, changed_hardware: null,
1133
1134
  changed_target: ["appearance_baseline"], frozen: [], previous: set.modules.map(module => ({ ...summary(module), source: module.source })), dependencies: [] } };
1134
1135
  const before = set.prelude_digest === prelude.prelude_digest ? prelude
1135
1136
  : latest(ctx.history("prelude"), item => item.output?.prelude_digest === set.prelude_digest)?.output ?? null;
@@ -1148,7 +1149,7 @@ export function cadModulesView(ctx) {
1148
1149
  const mode = attribution ? "rework" : changed === null || changed.length ? "revise" : write.length ? "revise" : "reuse";
1149
1150
  return {
1150
1151
  work: {
1151
- image, ...targets, mode, write, attribution, changed_hardware: changed, changed_target: changedTarget,
1152
+ ...pictures, ...targets, mode, write, attribution, changed_hardware: changed, changed_target: changedTarget,
1152
1153
  frozen: set.modules.filter(module => !pending.has(module.module_id)).map(summary),
1153
1154
  previous: set.modules.filter(module => pending.has(module.module_id)).map(module => ({ ...summary(module), source: module.source })),
1154
1155
  dependencies: set.modules.filter(module => needed.has(module.module_id)).map(module => ({ module_id: module.module_id, source: module.source })),
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "action_id": "image.generate",
3
- "contract_version": 1,
3
+ "contract_version": 2,
4
4
  "description": "生成概念图:等待已结算回执并冻结外观参考;可能收费,未知或在途的调用只按原身份恢复",
5
5
  "audience": "agent",
6
- "input_schema": "schemas/host-tools.v1.json#/$defs/NoInput",
6
+ "input_schema": "schemas/image-task.v1.json",
7
7
  "output_schema": "schemas/host-tools.v1.json#/$defs/HostToolResult",
8
8
  "permissions": [
9
9
  "image.generate",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "action_id": "step.tool-result",
3
3
  "contract_version": 1,
4
- "description": "按 result_id 回读本模型步骤此前的一条工具结果(同一执行实例、同一任务视图版本),按字符分页(start/length,sha256 为全文摘要);用于取回被压缩成摘要(compacted)的结果,不回 already_read,回读的字节再次计入本步骤的读取预算",
4
+ "description": "按 result_id 回读本模型步骤此前的原工具结果(同一执行实例、同一任务视图)。无 pointer 时保留原全文字符分页与 sha256 校验;pointer 模式须同时提供原全文 sha256 和选中值 value_sha256,回读原值 canonical JSON 的 UTF-16 分页,不重跑原工具。读取字节再次计入本步骤预算,不回 already_read。",
5
5
  "audience": "agent",
6
6
  "input_schema": "schemas/host-tools.v1.json#/$defs/StepToolResult",
7
7
  "output_schema": "schemas/host-tools.v1.json#/$defs/StepToolResultPage",