cli-aimlock 7.0.32 → 7.0.34

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.
@@ -31,7 +31,7 @@ import { assertChainNotSuspended } from './aimlock-coordination.mjs'
31
31
 
32
32
  const execFile = promisify(execFileCallback)
33
33
  const BUDGET_SCHEMA = 'aimlock.read-budget/1.0'
34
- const CONFIRMATION_SCHEMA = 'confirm-protocol.answer/1.0'
34
+ const CONFIRMATION_SCHEMA = 'confirm-protocol.skill.response/1.0'
35
35
  const MAX_DISCOVERED_FILES = 1_000
36
36
  const MAX_SOURCE_BYTES = 1_048_576
37
37
  const TOKEN_ESTIMATE_ALGORITHM = 'utf8-bytes-div-4-ceil'
@@ -52,6 +52,20 @@ const schema = (required, properties) => ({ type: 'object', additionalProperties
52
52
  const stringSchema = { type: 'string', minLength: 1 }
53
53
  const stringArraySchema = { type: 'array', items: stringSchema }
54
54
  const objectValueSchema = { type: 'object' }
55
+ const BUDGET_ADDITIONS_SCHEMA = schema(['files', 'tokenEstimate', 'durationMs'], {
56
+ files: { type: 'integer', minimum: 0 }, tokenEstimate: { type: 'integer', minimum: 0 }, durationMs: { type: 'integer', minimum: 0 },
57
+ })
58
+ const BUDGET_CONFIRMATION_SCHEMA = schema(['schemaVersion', 'requestId', 'status', 'callbackRequest', 'auditEntry', 'nextStep'], {
59
+ schemaVersion: { const: 'confirm-protocol.skill.response/1.0' }, requestId: stringSchema, status: { const: 'succeeded' },
60
+ callbackRequest: schema(['operation', 'payload'], { operation: { const: 'budget-extend' },
61
+ payload: schema(['chainId', 'additions', 'requestId', 'answer'], { chainId: stringSchema,
62
+ additions: BUDGET_ADDITIONS_SCHEMA, requestId: stringSchema, answer: { const: 'approve' } }) }),
63
+ auditEntry: schema(['schemaVersion', 'auditId', 'requestId', 'actorId', 'question', 'answer', 'remembered', 'risk', 'answeredAt'], {
64
+ schemaVersion: { const: 'confirm.audit-entry/1.0' }, auditId: stringSchema, requestId: stringSchema, actorId: stringSchema,
65
+ question: stringSchema, answer: { const: 'approve' }, remembered: { type: 'boolean' }, risk: { const: 'low' },
66
+ answeredAt: { type: 'string', format: 'date-time' },
67
+ }), nextStep: objectValueSchema,
68
+ })
55
69
  const LOCAL_OPERATION_SCHEMAS = Object.freeze({
56
70
  capabilities: schema([], {}),
57
71
  probe: schema(['goal', 'targetHints'], { goal: stringSchema, targetHints: stringArraySchema,
@@ -64,7 +78,7 @@ const LOCAL_OPERATION_SCHEMAS = Object.freeze({
64
78
  'budget-read': schema(['chainId', 'path'], { chainId: stringSchema, path: stringSchema }),
65
79
  'budget-status': schema(['chainId'], { chainId: stringSchema }),
66
80
  'budget-extend': schema(['chainId', 'confirmation', 'additions'], {
67
- chainId: stringSchema, confirmation: objectValueSchema, additions: objectValueSchema }),
81
+ chainId: stringSchema, confirmation: BUDGET_CONFIRMATION_SCHEMA, additions: BUDGET_ADDITIONS_SCHEMA }),
68
82
  'gate-issue': schema(['chainId', 'snapshotRoot', 'receipt', 'contract', 'nodes', 'coordinationRequired'], {
69
83
  chainId: stringSchema, snapshotRoot: stringSchema, receipt: objectValueSchema,
70
84
  contract: objectValueSchema, nodes: { type: 'array', items: objectValueSchema },
@@ -331,23 +345,53 @@ async function readFileWithinBudget(input) {
331
345
  })
332
346
  }
333
347
 
348
+ async function checkCachedReadAccess(input) {
349
+ const root = await repositoryRoot(input.repositoryRoot)
350
+ const chainId = identifier(input.chainId, 'chainId')
351
+ await assertChainNotSuspended({ repositoryRoot: root, chainId })
352
+ const budgetPath = managedPath(root, 'runs', chainId, 'read-budget.json')
353
+ return withFileLock(budgetPath, async () => {
354
+ const { state } = await readBudget(root, chainId)
355
+ const path = safeRelativePath(input.path)
356
+ const budget = budgetView(state)
357
+ if (budget.remainingDurationMs === 0) fail('AIMLOCK_DECISION_REQUIRED', 'read deadline exhausted')
358
+ if (budget.remainingTokenEstimate === 0) fail('AIMLOCK_DECISION_REQUIRED', 'read token estimate budget exhausted')
359
+ if (!state.uniqueFiles.includes(path)) fail('AIMLOCK_CACHE_UNCHARGED', 'cached source was not read by this chain')
360
+ return { schemaVersion: LOCAL_SCHEMA, path, budget }
361
+ })
362
+ }
363
+
334
364
  async function readBudgetStatus(input) {
335
365
  const root = await repositoryRoot(input.repositoryRoot)
336
366
  return budgetView((await readBudget(root, input.chainId)).state)
337
367
  }
338
368
 
339
- async function extendReadBudget(input) {
340
- const root = await repositoryRoot(input.repositoryRoot)
369
+ function confirmedBudgetExtension(input) {
341
370
  const confirmation = input.confirmation
342
- if (!confirmation || confirmation.schemaVersion !== CONFIRMATION_SCHEMA
343
- || confirmation.confirmed !== true || confirmation.risk !== 'low'
344
- || !identifier(confirmation.confirmationId, 'confirmationId')) {
345
- fail('AIMLOCK_CONFIRMATION_REQUIRED', 'a low-risk confirmation receipt is required')
371
+ const audit = confirmation?.auditEntry
372
+ const callback = confirmation?.callbackRequest
373
+ const payload = callback?.payload
374
+ const fields = ['files', 'tokenEstimate', 'durationMs']
375
+ if (!confirmation || confirmation.schemaVersion !== CONFIRMATION_SCHEMA || confirmation.status !== 'succeeded'
376
+ || audit?.schemaVersion !== 'confirm.audit-entry/1.0' || audit.risk !== 'low' || audit.answer !== 'approve'
377
+ || typeof audit.remembered !== 'boolean' || !Number.isFinite(Date.parse(audit.answeredAt))
378
+ || callback?.operation !== 'budget-extend' || payload?.answer !== 'approve'
379
+ || payload.chainId !== input.chainId || payload.requestId !== audit.requestId
380
+ || !payload.additions || fields.some((key) => payload.additions[key] !== input.additions?.[key])) {
381
+ fail('AIMLOCK_CONFIRMATION_REQUIRED', 'a low-risk Confirm Protocol interaction-answer bound to this chain and exact additions is required')
346
382
  }
383
+ identifier(audit.actorId, 'actorId')
384
+ identifier(audit.requestId, 'requestId')
385
+ return identifier(audit.auditId, 'auditId')
386
+ }
387
+
388
+ async function extendReadBudget(input) {
389
+ const root = await repositoryRoot(input.repositoryRoot)
390
+ const confirmationId = confirmedBudgetExtension(input)
347
391
  const additions = input.additions
348
- if (!additions || !Number.isInteger(additions.files) || additions.files < 0
349
- || !Number.isInteger(additions.tokenEstimate) || additions.tokenEstimate < 0
350
- || !Number.isInteger(additions.durationMs) || additions.durationMs < 0
392
+ if (!additions || !Number.isSafeInteger(additions.files) || additions.files < 0
393
+ || !Number.isSafeInteger(additions.tokenEstimate) || additions.tokenEstimate < 0
394
+ || !Number.isSafeInteger(additions.durationMs) || additions.durationMs < 0
351
395
  || additions.files + additions.tokenEstimate + additions.durationMs === 0) {
352
396
  fail('AIMLOCK_EXTENSION_INVALID', 'budget additions must contain a positive integer increase')
353
397
  }
@@ -356,6 +400,9 @@ async function extendReadBudget(input) {
356
400
  return withFileLock(budgetPath, async () => {
357
401
  const authority = await readBudget(root, chainId)
358
402
  const state = authority.state
403
+ if (state.extensions.some((item) => item.confirmationId === confirmationId)) {
404
+ fail('AIMLOCK_CONFIRMATION_REPLAYED', 'this budget confirmation has already been applied')
405
+ }
359
406
  const updated = {
360
407
  ...state,
361
408
  maxFiles: state.maxFiles + additions.files,
@@ -363,14 +410,14 @@ async function extendReadBudget(input) {
363
410
  ? null : (state.maxTokenEstimate ?? 0) + additions.tokenEstimate,
364
411
  maxDurationMs: state.maxDurationMs + additions.durationMs,
365
412
  extensions: [...state.extensions, {
366
- confirmationId: confirmation.confirmationId,
413
+ confirmationId,
367
414
  additions,
368
415
  at: new Date().toISOString(),
369
416
  }],
370
417
  }
371
418
  await atomicJson(authority.path, updated)
372
419
  await appendAudit(root, { event: 'read-budget-extended', chainId,
373
- confirmationId: confirmation.confirmationId, additions })
420
+ confirmationId, additions })
374
421
  return budgetView(updated)
375
422
  })
376
423
  }
@@ -395,6 +442,7 @@ export {
395
442
  LOCAL_SCHEMA,
396
443
  PASS_SCHEMA,
397
444
  READ_BUDGETS,
445
+ checkCachedReadAccess,
398
446
  extendReadBudget,
399
447
  guardedWriteFile,
400
448
  initializeReadBudget,
@@ -209,7 +209,7 @@ const RESPONSE_SCHEMA = "aimlock.skill.response/1.1";
209
209
  const ERROR_SCHEMA = "aimlock.skill.error/1.0";
210
210
  const CONTRACT_SCHEMA = "aimlock.scope-contract/1.0";
211
211
  const COMPILER_NAME = "aimlock";
212
- const COMPILER_VERSION = "v7.0.32";
212
+ const COMPILER_VERSION = "v7.0.34";
213
213
  const KEEP_ALIVE_SECONDS = 90;
214
214
  const KEEP_ALIVE_MESSAGE = "智能目标持续执行中,请勿关闭!";
215
215
  const BYPASS_LINE_BUDGET = 500;
package/broker.mjs CHANGED
@@ -25,6 +25,19 @@ const EVALUATION_SCHEMA = 'skill-automatic-evaluation/1.0'
25
25
  const IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/
26
26
  const REQUEST_SCHEMA_PATTERN = /^([A-Za-z0-9.-]+\.skill)\.request\/([0-9]+\.[0-9]+)$/
27
27
  const TRANSPORT_ERROR_CODE_PATTERN = /^[A-Z][A-Z0-9_]{1,63}$/
28
+ const NETWORK_TRANSPORT_ERROR = 'NETWORK_TRANSPORT'
29
+ const SKILL_INVOCATION_ERROR = 'SKILL_INVOCATION_FAILED'
30
+
31
+ class OfficialSkillInvocationError extends Error {
32
+ constructor(context, operation, transportCode) {
33
+ super(`${context.displayName} ${operation} invocation failed: network transport ${transportCode}`)
34
+ this.name = 'OfficialSkillInvocationError'
35
+ this.code = NETWORK_TRANSPORT_ERROR
36
+ this.operation = operation
37
+ this.retryable = false
38
+ this.transportCode = transportCode
39
+ }
40
+ }
28
41
 
29
42
  function asObject(value, label) {
30
43
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
@@ -237,6 +250,29 @@ export function transportFailureCode(error) {
237
250
  return 'UNKNOWN_TRANSPORT_ERROR'
238
251
  }
239
252
 
253
+ export function officialSkillFailureResponse(error) {
254
+ if (error instanceof OfficialSkillInvocationError) {
255
+ return {
256
+ ok: false,
257
+ error: {
258
+ code: error.code,
259
+ message: error.message,
260
+ operation: error.operation,
261
+ retryable: error.retryable,
262
+ transportCode: error.transportCode,
263
+ },
264
+ }
265
+ }
266
+ return {
267
+ ok: false,
268
+ error: {
269
+ code: SKILL_INVOCATION_ERROR,
270
+ message: error instanceof Error ? error.message : 'Skill invocation failed',
271
+ retryable: false,
272
+ },
273
+ }
274
+ }
275
+
240
276
  export async function invokeOfficialSkill(context, operation, input, dependencies) {
241
277
  const environment = asObject(dependencies.environment, 'broker environment')
242
278
  if (typeof dependencies.request !== 'function') {
@@ -253,9 +289,7 @@ export async function invokeOfficialSkill(context, operation, input, dependencie
253
289
  signal: AbortSignal.timeout(CALL_TIMEOUT_MS),
254
290
  })
255
291
  } catch (error) {
256
- throw new Error(
257
- `${context.displayName} ${operation} invocation failed: network transport ${transportFailureCode(error)}`,
258
- )
292
+ throw new OfficialSkillInvocationError(context, operation, transportFailureCode(error))
259
293
  }
260
294
  const payload = await responsePayload(response, `${context.displayName} ${operation} response`)
261
295
  if (!response.ok || payload.ok !== true) {
package/cli.mjs CHANGED
@@ -4,6 +4,7 @@ import { dirname, resolve } from 'node:path'
4
4
  import { cwd, stdin, stdout } from 'node:process'
5
5
  import { createInterface } from 'node:readline/promises'
6
6
  import { fileURLToPath } from 'node:url'
7
+ import { CHAIN_USAGE, runChainCli } from './aimlock-chain-cli.mjs'
7
8
  import { defaultUsage, dispatchOfficialSkillCli, runIntakeHandshake } from './installer.mjs'
8
9
  import {
9
10
  LOCAL_CAPABILITIES,
@@ -90,7 +91,7 @@ export function localAimlockApplicability(facts) {
90
91
  export function aimlockUsage(context) {
91
92
  const usage = defaultUsage(context)
92
93
  if (!usage.includes(COMMON_RUN_USAGE)) throw new Error('Shared CLI run usage contract changed')
93
- return usage.replace(COMMON_RUN_USAGE, AIMLOCK_RUN_USAGE)
94
+ return usage.replace(COMMON_RUN_USAGE, AIMLOCK_RUN_USAGE) + '\n\n' + CHAIN_USAGE
94
95
  }
95
96
 
96
97
  async function collectApplicability(input, output) {
@@ -184,7 +185,9 @@ async function dispatchLocal(args) {
184
185
 
185
186
  const cliPath = fileURLToPath(import.meta.url)
186
187
  if (process.argv[1] && realpathSync(resolve(process.argv[1])) === cliPath) {
187
- if (process.argv[2] === 'local') {
188
+ if (process.argv[2] === 'chain') {
189
+ await runChainCli(process.argv.slice(3))
190
+ } else if (process.argv[2] === 'local') {
188
191
  try {
189
192
  console.log(JSON.stringify(await dispatchLocal(process.argv.slice(3))))
190
193
  } catch (error) {
package/installer.mjs CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  brokerCommandInput,
14
14
  invokeCommandInput,
15
15
  invokeOfficialSkill,
16
+ officialSkillFailureResponse,
16
17
  } from './broker.mjs'
17
18
 
18
19
  export {
@@ -25,6 +26,7 @@ export {
25
26
  callOfficialSkill,
26
27
  invokeCommandInput,
27
28
  invokeOfficialSkill,
29
+ officialSkillFailureResponse,
28
30
  } from './broker.mjs'
29
31
 
30
32
  const INSTALL_META = 'install-meta.json'
@@ -184,11 +186,19 @@ async function readBrokerSource(input) {
184
186
  }
185
187
 
186
188
  async function runBrokerInvocation(context, commandInput) {
187
- const invocation = await invokeOfficialSkill(
188
- context, commandInput.operation, commandInput.input, brokerDependencies(),
189
- )
190
- console.log(JSON.stringify(invocation))
191
- return invocation
189
+ try {
190
+ const invocation = await invokeOfficialSkill(
191
+ context, commandInput.operation, commandInput.input, brokerDependencies(),
192
+ )
193
+ console.log(JSON.stringify(invocation))
194
+ return invocation
195
+ } catch (error) {
196
+ const response = officialSkillFailureResponse(error)
197
+ console.log(JSON.stringify({ response }))
198
+ console.error(response.error.message)
199
+ process.exitCode = 1
200
+ return null
201
+ }
192
202
  }
193
203
 
194
204
  export async function runIntakeHandshake(context, spec) {
package/package.json CHANGED
@@ -3,13 +3,14 @@
3
3
  "cli-aimlock": "./cli.mjs"
4
4
  },
5
5
  "dependencies": {
6
- "cli-swarm": "7.0.32"
6
+ "cli-swarm": "7.0.34"
7
7
  },
8
8
  "description": "Aimlock skill installer for CLI.Tax: lock a user request into an executable aim and route Blueprint, Swarm, and Calctool.",
9
9
  "exports": {
10
10
  "./coordination": "./aimlock-coordination.mjs",
11
11
  "./local-runner": "./aimlock-local-runner.mjs",
12
- "./runtime": "./aimlock-runtime.mjs"
12
+ "./runtime": "./aimlock-runtime.mjs",
13
+ "./chain-executor": "./aimlock-chain-executor.mjs"
13
14
  },
14
15
  "files": [
15
16
  "cli.mjs",
@@ -18,12 +19,21 @@
18
19
  "README.md",
19
20
  "skill/SKILL.md",
20
21
  "skill/skill.json",
22
+ "aimlock-chain-model.mjs",
23
+ "aimlock-chain-store.mjs",
24
+ "aimlock-chain-process.mjs",
25
+ "aimlock-chain-calls.mjs",
26
+ "aimlock-chain-human.mjs",
27
+ "aimlock-chain-executor.mjs",
28
+ "aimlock-chain-cli.mjs",
29
+ "aimlock-chain-outcomes.mjs",
21
30
  "aimlock-context-map.mjs",
22
31
  "aimlock-coordination.mjs",
23
32
  "aimlock-local-fs.mjs",
24
33
  "aimlock-local-gate.mjs",
25
34
  "aimlock-local-runner.mjs",
26
- "aimlock-runtime.mjs"
35
+ "aimlock-runtime.mjs",
36
+ "skill/references/chain-executor.md"
27
37
  ],
28
38
  "license": "UNLICENSED",
29
39
  "name": "cli-aimlock",
@@ -32,5 +42,5 @@
32
42
  "url": "https://github.com/88208555/aimlock-clitax.git"
33
43
  },
34
44
  "type": "module",
35
- "version": "7.0.32"
45
+ "version": "7.0.34"
36
46
  }
package/skill/SKILL.md CHANGED
@@ -5,7 +5,7 @@ description: "Aimlock 仅用于大型、深度、跨模块、高风险、需要
5
5
 
6
6
  # Aimlock Skill
7
7
 
8
- Package version: v7.0.32
8
+ Package version: v7.0.34
9
9
 
10
10
  Endpoint: https://cli.tax/R3mQ8kWpXn
11
11
 
@@ -161,3 +161,7 @@ Aimlock returns the protocol; it does not start a timer.
161
161
  - 本地 CLI 不提供手工评分或评语提交命令,人类不得选择技能分数或填写技能评价;日常聊天不属于评价协议。
162
162
 
163
163
  调用示例:`npx cli-aimlock@latest invoke <operation> '<JSON对象>'`。IDE 集成可向 `npx cli-aimlock@latest broker` 的 stdin 发送 `{"operation":"capabilities","input":{}}`。
164
+
165
+ ## 宿主持久执行
166
+
167
+ 使用 [chain-executor.md](references/chain-executor.md) 的显式 `chain init/resume/status/answer` 协议驱动本地持久步骤。`run` 的需求采集、远端 `nextStep` 与 `completed` 均不等于已执行。只有真实 broker/协调器/命令结果及绑定证据能推进;未答复人工裁决禁止恢复,发送后结果不确定禁止自动重发。CLI 终端不提供 OS 隔离或独立可信 runner。
@@ -0,0 +1,90 @@
1
+ # 持久 CLI 链执行器
2
+
3
+ 服务端技能仍是无状态协议;宿主通过显式计划执行真实步骤,将状态写入仓库的 `.aimlock/executions/<chainId>/state.json`。旧 `run` 仍负责适用性判断与需求采集,生成需求文件不等于执行技能链。
4
+
5
+ ## 命令
6
+
7
+ ```sh
8
+ cli-aimlock chain init /absolute/repository < execution-plan.json
9
+ cli-aimlock chain resume /absolute/repository my-chain
10
+ cli-aimlock chain status /absolute/repository my-chain
11
+ cli-aimlock chain answer /absolute/repository my-chain human-actor
12
+ ```
13
+
14
+ `init` 只验证并保存不可变计划与已安装技能元数据,不触发 HTTP。
15
+ `resume` 按依赖顺序执行尚未完成的步骤;已成功步骤不会重复调用。
16
+ `status` 只读取本地状态。等待时退出码为 2,失败/结果不确定为 1。
17
+ `answer` 必须在真实交互终端使用,读取展示后的选项 ID;拒绝重定向答案。actorId 是本地审计标签,不代表服务端已验证真人身份。
18
+
19
+ ## 显式计划
20
+
21
+ ```json
22
+ {
23
+ "schemaVersion": "aimlock.execution-plan/1.0",
24
+ "chainId": "my-chain",
25
+ "skills": [],
26
+ "steps": [
27
+ {
28
+ "stepId": "test",
29
+ "kind": "command",
30
+ "skillId": null,
31
+ "operation": "exec",
32
+ "input": {
33
+ "executable": "/usr/local/bin/node",
34
+ "args": ["--test"],
35
+ "workingDirectory": ".",
36
+ "timeoutMs": 600000,
37
+ "evidenceKind": "test",
38
+ "environment": {}
39
+ },
40
+ "dependsOn": [],
41
+ "bindings": []
42
+ }
43
+ ]
44
+ }
45
+ ```
46
+
47
+ 每步必须提供全部七个字段;Confirm interaction-request 可额外声明 continueWhen。其它额外字段均拒绝。支持三种 kind:
48
+
49
+ - `skill`:skillId 必须在 skills 中声明 `{skillId, packageRoot}`;packageRoot 指向已安装 npm 技能包。复用受限 broker 的真实 HTTP、requestId 校验、自动评价和提交回执;不读取或输出令牌。初始化后的包元数据变化会阻止恢复。
50
+ - `coordinator`:skillId=null,operation 使用 Swarm 本地接口。`dependency-wait` 会实际登记并驱动 `wait-for-event`/tick,保存唤醒包;不是仅返回 nextStep。`resolve-human` 禁止从计划注入。
51
+ - `command`:skillId=null、operation=exec,按显式 executable/args 执行无 shell 的真实进程;workingDirectory 限于仓库,timeoutMs 为 1..3600000,输出上限 1 MiB,超限/超时/非零退出显式失败。命令输入不得动态绑定;environment 必须显式提供字符串字典,spawn 不继承宿主环境,拒绝令牌、私钥、授权等保留变量。使用非绝对 executable 时应显式声明 PATH。示例路径需改成实际 Node 安装位置。evidenceKind 为 test/build/lint/security/benchmark。
52
+
53
+ 步骤必须按拓扑顺序排列;依赖只能引用前面已声明的步骤。运行时仅从已成功步骤的实际输出取值,忽略服务端 nextStep/completed 字段,不据此调用隐含步骤。
54
+
55
+ 绑定使用 RFC 6901 JSON Pointer:
56
+ ```json
57
+ {
58
+ "stepId": "dispatch",
59
+ "kind": "skill",
60
+ "skillId": "swarm",
61
+ "operation": "dispatch",
62
+ "input": { "tasks": null },
63
+ "dependsOn": ["compile"],
64
+ "bindings": [
65
+ { "stepId": "compile", "source": "/machineTasks/tasks", "target": "/tasks" }
66
+ ]
67
+ }
68
+ ```
69
+
70
+ 这里 compile 必须是计划中真实调用 Blueprint compile-inline 的步骤,并在 skills 声明两个安装包。source 指向协议 output 本身,不含 broker 外层包装。目标键必须事先在 input 中声明;缺失值、非法指针、循环依赖均报错。Blueprint 的 blueprintSha256 与 criterionId 原样绑定;不要重算或改变前缀。
71
+
72
+ ## 等待、恢复与证据
73
+
74
+ - 持久记录 pending/running/waiting/blocked/failed/uncertain/succeeded、调用 ID、实际 requestId、输入摘要、结果、反馈回执和错误。网络发送前先保存 requestId。
75
+ - 事件到达才记为依赖满足。死亡、放弃和超时不冒充事件成功;人工选中恢复时保留 `resolved-by-human`、原唤醒原因与 Confirm 审计。
76
+ - 遇到 Swarm 高风险裁决,必须在 skills 声明 confirm-protocol 包。宿主真实调用 interaction-request,保存待答状态。resume 不会代答;answer 读取终端输入、真实调用 interaction-answer,校验问题/答案/回调绑定后才调用 resolve-human。
77
+ - 普通 Confirm 步骤还必须声明结构化继续条件,例如步骤字段 `continueWhen: {"answer":"yes"}`。协议没有通用“同意”的 option ID;不依据 label 推测授权。真人答案未匹配或未声明条件时,下游保持 blocked;choice/input/multi 同样须明确条件。
78
+ - Validator 的协议 succeeded 不代表验收通过:verdict=incomplete/blocked、空或失败执行证据、sandbox-run 等 pending-execution 描述均阻塞链。原协议结果与回执仍保留,不能冒充已执行测试。
79
+ - 普通技能 blocked/failed 保持阻塞/失败,不自动重试。发送后断线、无法校验回执、执行中断等不确定结果禁止自动重发;应先人工核查外部效果,再制定新计划。已登记的事件等待可恢复轮询,不重新声明等待。
80
+ - 本地命令提供 `cli.tax.test-evidence/1.0`:真实 exitCode、durationMs、stdout/stderr 摘要;runner=local、producer=local-cli-process、independentRunnerVerified=false。可把 output.evidence 绑定到 Blueprint acceptance-report 的逐项 results;报告对账不等于独立可信执行。
81
+ - 同链正常 completed 任务不再封锁活跃同伴;全部终态仍封锁。失败/回收任务必须显式注册 supersedesTaskId,保留原任务历史、范围与约束,并先处理人工裁决;任意新任务不构成失败豁免。
82
+ - 人工选中 active 任务后,同链未选中的 human-decision 停放任务可继续保持 blocked;旧 lease 不能写入,因为写入联锁仍要求对应 taskId 为 active。未决裁决继续阻止读取与写入。
83
+
84
+ ## 边界
85
+
86
+ 共享 chainId 的只读预算不是智能体身份隔离。命令步骤是用户已声明的本地进程调用,不是操作系统沙箱,不拦截任意外部进程或其文件读取,也不保证其他命令自动遵守 Aimlock 写入门禁。修改源码的受控调用仍必须走 guarded-write 和有效快照/租约。没有隐式 ContextBase 调用;不要省略其适用的预算链参数。没有独立桌面壳或 OS 弹窗承诺;当前 Confirm 宿主是 CLI 终端。
87
+
88
+ 排队锁恢复:允许排队的请求必须显式给出正整数 `queueTimeoutMs`,与授锁后 `ttlSeconds` 独立;`lock-acquire` 返回 queued 后保存 queueId 并挂起本步骤;`resume` 运行一次协调扫描后只查询 `lock-queue-status`,不会重放申请。只有同一任务、链、资源、路径及当前未过期租约全部匹配的真实 grant 才继续。仍在排队时返回包含 deadlineAt 的等待快照;到期后 tick 落账并返回 need-human/Confirm 请求,原队列阻断。本执行器不会把队列超时的人工恢复解释为已授锁;宿主需处理返回的确认,再提交新的显式申请。终止、过期、失配及旧版缺少 queue→grant 关联的授锁明确阻断,不推测归属。
89
+
90
+ 命令结束与进程回收分开记录:超时或输出超限后最多再等待 1 秒关闭输出管道。仍无法确认回收时返回 `uncertain` 和空执行证据,释放执行锁并禁止自动重放;这不证明脱离进程已停止。已启动子进程后落账失败同样按结果不确定记录。
package/skill/skill.json CHANGED
@@ -6,5 +6,5 @@
6
6
  "name": "aimlock",
7
7
  "schemaVersion": "aimlock.skill.request/1.1",
8
8
  "type": "Skill",
9
- "version": "v7.0.32"
9
+ "version": "v7.0.34"
10
10
  }