@givolt/i3-composer-agent 2.3.4 → 2.3.6
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/.agents/plugins/marketplace.json +1 -1
- package/.claude/commands/i3-prototype.md +2 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.gemini/commands/i3-prototype.toml +2 -0
- package/README.md +1 -1
- package/commands/i3-prototype.toml +2 -0
- package/docs/codebuddy-setup.md +1 -1
- package/docs/codex-setup.md +1 -1
- package/docs/other-hosts.md +1 -1
- package/docs/workbuddy-setup.md +1 -1
- package/hooks/PROTOTYPE.md +19 -0
- package/hooks/i3-prototype.cjs +2 -1
- package/package.json +3 -1
- package/plugin.json +1 -1
- package/scripts/ui-prototype-binding-recovery-test.js +84 -0
- package/scripts/ui-prototype-buddy-lifecycle-test.js +10 -2
- package/scripts/ui-prototype-codebuddy-hook-test.js +4 -4
- package/scripts/ui-prototype-execution-gates-test.js +4 -3
- package/skills/i3-ui-prototype/SKILL.md +2 -6
- package/skills/i3-ui-prototype/references/execution-evidence.md +13 -1
- package/skills/i3-ui-prototype/scripts/codebuddy-context.cjs +3 -3
- package/skills/i3-ui-prototype/scripts/codebuddy-hook.cjs +134 -30
- package/skills/i3-ui-prototype/scripts/codebuddy-ide-source.cjs +3 -3
- package/skills/i3-ui-prototype/scripts/execution-assertions.cjs +1 -1
- package/skills/i3-ui-prototype/scripts/execution-diagnostics.cjs +94 -0
- package/skills/i3-ui-prototype/scripts/execution-evidence.cjs +44 -13
- package/skills/i3-ui-prototype/scripts/execution-gates.cjs +52 -3
- package/skills/i3-ui-prototype/scripts/execution-recovery.cjs +13 -7
- package/skills/i3-ui-prototype/scripts/execution-runtime.cjs +9 -2
- package/skills/i3-ui-prototype/scripts/hosts/codebuddy.cjs +1 -1
- package/skills/i3-ui-prototype/scripts/native-command.cjs +19 -2
- package/skills/i3-ui-prototype/scripts/native-delivery.cjs +4 -3
- package/skills/i3-ui-prototype/scripts/native-files.cjs +10 -1
- package/skills/i3-ui-prototype/scripts/native-pilot.cjs +6 -1
- package/skills/i3-ui-prototype/scripts/native-tool-name.cjs +5 -0
- package/skills/i3-ui-prototype/scripts/native-tool-result.cjs +14 -0
- package/skills/i3-ui-prototype/scripts/prototype-dependencies.cjs +1 -1
- package/skills/i3-ui-prototype/scripts/prototype-hooks.cjs +7 -3
- package/skills/i3-ui-prototype/scripts/ptc-callbacks.cjs +131 -0
|
@@ -19,3 +19,5 @@ Use the i3-ui-prototype skill in the main session for the user's request below.
|
|
|
19
19
|
$ARGUMENTS
|
|
20
20
|
|
|
21
21
|
With no arguments use create; explicit analysis or read-only limits take precedence. Preserve the user's scope, manual edits, fixed inputs and approved baselines. Use supporting engineering, framework, design and browser skills as the working skill requires; keep its workflow and evidence requirements there.
|
|
22
|
+
|
|
23
|
+
Create the prototype in the current session. Load the required frontend/framework/design Skills before implementation and use the browser Skill for interaction acceptance. Installed Hooks run automatically; do not inspect Hook internals, search host logs, or run a manual binding step. When a required Skill is missing, invoke it and retry; report an infrastructure failure briefly through the separate maintenance entry.
|
|
@@ -18,4 +18,6 @@ Use the i3-ui-prototype skill in the main session for the user's request below.
|
|
|
18
18
|
$ARGUMENTS
|
|
19
19
|
|
|
20
20
|
With no arguments use create; explicit analysis or read-only limits take precedence. Preserve the user's scope, manual edits, fixed inputs and approved baselines. Use supporting engineering, framework, design and browser skills as the working skill requires; keep its workflow and evidence requirements there.
|
|
21
|
+
|
|
22
|
+
Create the prototype in the current session. Load the required frontend/framework/design Skills before implementation and use the browser Skill for interaction acceptance. Installed Hooks run automatically; do not inspect Hook internals, search host logs, or run a manual binding step. When a required Skill is missing, invoke it and retry; report an infrastructure failure briefly through the separate maintenance entry.
|
|
21
23
|
"""
|
package/README.md
CHANGED
|
@@ -18,4 +18,6 @@ Use the i3-ui-prototype skill in the main session for the user's request below.
|
|
|
18
18
|
$ARGUMENTS
|
|
19
19
|
|
|
20
20
|
With no arguments use create; explicit analysis or read-only limits take precedence. Preserve the user's scope, manual edits, fixed inputs and approved baselines. Use supporting engineering, framework, design and browser skills as the working skill requires; keep its workflow and evidence requirements there.
|
|
21
|
+
|
|
22
|
+
Create the prototype in the current session. Load the required frontend/framework/design Skills before implementation and use the browser Skill for interaction acceptance. Installed Hooks run automatically; do not inspect Hook internals, search host logs, or run a manual binding step. When a required Skill is missing, invoke it and retry; report an infrastructure failure briefly through the separate maintenance entry.
|
|
21
23
|
"""
|
package/docs/codebuddy-setup.md
CHANGED
|
@@ -48,7 +48,7 @@ CodeBuddy 与 WorkBuddy 项目共用 `.codebuddy/`;同版本追加客户端会
|
|
|
48
48
|
npx @givolt/i3-composer-agent --codebuddy --scope project --yes --prototype-skills uiux,antdv
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
值为 none、uiux、antdv 或 uiux,antdv;默认 none。可选工具跟随宿主和项目/全局范围,位于基址下 `tools/i3-prototype/`;旧项目 `.prototype-tools` 保持读取兼容。需要固定本次版本时使用 `npx @givolt/i3-composer-agent@2.3.
|
|
51
|
+
值为 none、uiux、antdv 或 uiux,antdv;默认 none。可选工具跟随宿主和项目/全局范围,位于基址下 `tools/i3-prototype/`;旧项目 `.prototype-tools` 保持读取兼容。需要固定本次版本时使用 `npx @givolt/i3-composer-agent@2.3.6`。已安装后可随时单独检测/补装,无需重装 i3:
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
54
|
node .codebuddy/skills/i3-ui-prototype/scripts/prototype-dependencies.cjs check --host codebuddy --scope project --project .
|
package/docs/codex-setup.md
CHANGED
|
@@ -37,6 +37,6 @@ Codex 已有原生技能路由,勿把元技能全文预加载为常驻上下
|
|
|
37
37
|
|
|
38
38
|
## 可选原型工具
|
|
39
39
|
|
|
40
|
-
原型辅助工具位于本宿主基址的 `tools/i3-prototype/`,支持项目和全局范围;旧项目 `.prototype-tools` 继续读取兼容。项目安装可使用 `npx @givolt/i3-composer-agent@2.3.
|
|
40
|
+
原型辅助工具位于本宿主基址的 `tools/i3-prototype/`,支持项目和全局范围;旧项目 `.prototype-tools` 继续读取兼容。项目安装可使用 `npx @givolt/i3-composer-agent@2.3.6 --codex --scope project --yes --prototype-skills uiux,antdv`;全局安装将 scope 改为 global。可选依赖默认 none,后续也可使用已安装 Skill 的 prototype-dependencies.cjs 单独检测/补装。完整用法见原型 Skill 的 references/optional-skills.md。
|
|
41
41
|
|
|
42
42
|
原型共享资料统一在项目 prototypes/:新索引 prototypes/index.json,准备范围 prototypes/.preparation/<id>/scope.json。新原生 scope.moduleIds 自动生成 appliesTo;旧索引、输入和实例保持兼容,安装升级不自动迁移。工具及运行缓存使用所选宿主基址;全局浏览器缓存按业务项目身份隔离。详细规则和只读查询/显式迁移命令见 i3-ui-prototype/references/project-layout.md。
|
package/docs/other-hosts.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## 使用本地分发目录
|
|
8
8
|
|
|
9
|
-
以下命令使用完整分发包解压目录里的 CLI,适用于本地测试和离线接入。`/absolute/path/to/package` 均替换为实际解压目录;已发布版本也可使用 `npx @givolt/i3-composer-agent@2.3.
|
|
9
|
+
以下命令使用完整分发包解压目录里的 CLI,适用于本地测试和离线接入。`/absolute/path/to/package` 均替换为实际解压目录;已发布版本也可使用 `npx @givolt/i3-composer-agent@2.3.6` 的对应入口。
|
|
10
10
|
|
|
11
11
|
### fx
|
|
12
12
|
|
package/docs/workbuddy-setup.md
CHANGED
|
@@ -85,7 +85,7 @@ npx @givolt/i3-composer-agent --workbuddy --scope global --yes --force
|
|
|
85
85
|
|
|
86
86
|
## 可选原型工具
|
|
87
87
|
|
|
88
|
-
原型辅助工具位于本宿主基址的 `tools/i3-prototype/`,支持项目和全局范围;旧项目 `.prototype-tools` 继续读取兼容。推荐使用 `npx @givolt/i3-composer-agent@2.3.
|
|
88
|
+
原型辅助工具位于本宿主基址的 `tools/i3-prototype/`,支持项目和全局范围;旧项目 `.prototype-tools` 继续读取兼容。推荐使用 `npx @givolt/i3-composer-agent@2.3.6 --workbuddy --scope global --yes --prototype-skills uiux,antdv`;项目安装将 scope 改为 project。可选依赖默认 none,后续也可使用已安装 Skill 的 prototype-dependencies.cjs 单独检测/补装。完整用法见原型 Skill 的 references/optional-skills.md。
|
|
89
89
|
|
|
90
90
|
原型共享资料统一在项目 prototypes/:新索引 prototypes/index.json,准备范围 prototypes/.preparation/<id>/scope.json。新原生 scope.moduleIds 自动生成 appliesTo;旧索引、输入和实例保持兼容,安装升级不自动迁移。工具及运行缓存使用所选宿主基址;全局浏览器缓存按业务项目身份隔离。详细规则和只读查询/显式迁移命令见 i3-ui-prototype/references/project-layout.md。
|
|
91
91
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 原型 Hooks 安装与维护
|
|
2
|
+
|
|
3
|
+
<!-- i3:zh-guide:start -->
|
|
4
|
+
## 中文使用说明
|
|
5
|
+
|
|
6
|
+
按下文完整工程步骤、检查项与验证条件执行。根据任务选择适用步骤,保留证据、风险和退出条件;正文示例中的英文标签在面向用户输出时译为中文。
|
|
7
|
+
|
|
8
|
+
默认使用简体中文解释、追问及生成报告;用户明确要求其他语言时可切换。原方法正文、引用、阈值和审批条件完整保留。代码标识、路径、命令、工具参数、JSON 键/枚举与原文引用保持原样。
|
|
9
|
+
<!-- i3:zh-guide:end -->
|
|
10
|
+
|
|
11
|
+
本文件面向安装器和维护人员。i3-ui-prototype 的正常职责是制作、修订和验收原型,不承担宿主或 Hook 排障。
|
|
12
|
+
|
|
13
|
+
安装器在 CodeBuddy/WorkBuddy 项目或全局范围注册 prototype 组。宿主自动触发;不要求原型使用者额外执行 execution-begin 或确认任务绑定。必要 Skill 的真实调用由原生 Pre/Post 回调记录,PTC/REPL 的内部工具分别记录;REPL 脚本文本不能代替调用成功。Bash 不提供 stdout 时由本包第一方入口捕获实际结果;其他只读查询可使用 native-tool-result.cjs 的本地绝对入口,不安装依赖。
|
|
14
|
+
|
|
15
|
+
检查命令:`npx --yes @givolt/i3-composer-agent hooks check --host codebuddy --scope project --project .`;全局将 scope 改为 global。检查只读,不启用/修复设置。registered 表示已注册;active 表示观察到当前注册的有效运行;failure 显示已触发但处理失败的原因、会话、模式、次数和恢复状态。两次同类恢复未成功后转 blocked,不会因重试旧 begin 清零。只读维护不消耗恢复次数。
|
|
16
|
+
|
|
17
|
+
状态与 PTC 机器回调位于宿主基址的 state/i3-prototype/;它们是诊断/证据记录,不是业务项目的原型目录。项目/全局、宿主/会话和当前用户任务分别隔离。保留上游 Hooks、用户注册、旧 HTML 和已有原型;旧 execution-* 命令仅供兼容,不是新流程的前置。
|
|
18
|
+
|
|
19
|
+
CodeBuddy 项目/全局 × IDE/CLI(CLI direct/PTC)真实注册、触发、必要 Skill 调用、缺失补调和失败展示必须分别验收。自动化回放不能冒充真实宿主;正式发布门禁在仓库维护工具中执行,不放到业务原型 Skill 内。
|
package/hooks/i3-prototype.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
|
-
// Stable host Hook entry
|
|
3
|
+
// Stable host Hook entry. Shared legacy helpers are an implementation detail;
|
|
4
|
+
// installation, automatic dispatch and diagnostics belong to Hooks, not UI tasks.
|
|
4
5
|
const fs=require('node:fs'),path=require('node:path');
|
|
5
6
|
function main(argv=process.argv.slice(2),raw=fs.readFileSync(0,'utf8'),load=()=>require(path.join(require('./manage.cjs').modules().scripts,'hosts/codebuddy.cjs'))) {
|
|
6
7
|
return load().main(argv,raw);
|
package/package.json
CHANGED
|
@@ -195,6 +195,7 @@
|
|
|
195
195
|
"scripts/run-evals.js",
|
|
196
196
|
"scripts/run-regressions.js",
|
|
197
197
|
"scripts/template-adapter-test.js",
|
|
198
|
+
"scripts/ui-prototype-binding-recovery-test.js",
|
|
198
199
|
"scripts/ui-prototype-buddy-lifecycle-test.js",
|
|
199
200
|
"scripts/ui-prototype-codebuddy-hook-test.js",
|
|
200
201
|
"scripts/ui-prototype-content-test.js",
|
|
@@ -415,6 +416,7 @@
|
|
|
415
416
|
"scripts/run-evals.js",
|
|
416
417
|
"scripts/run-regressions.js",
|
|
417
418
|
"scripts/template-adapter-test.js",
|
|
419
|
+
"scripts/ui-prototype-binding-recovery-test.js",
|
|
418
420
|
"scripts/ui-prototype-buddy-lifecycle-test.js",
|
|
419
421
|
"scripts/ui-prototype-codebuddy-hook-test.js",
|
|
420
422
|
"scripts/ui-prototype-content-test.js",
|
|
@@ -442,7 +444,7 @@
|
|
|
442
444
|
"scripts/validate-versions.js"
|
|
443
445
|
]
|
|
444
446
|
},
|
|
445
|
-
"version": "2.3.
|
|
447
|
+
"version": "2.3.6",
|
|
446
448
|
"repository": {
|
|
447
449
|
"type": "git",
|
|
448
450
|
"url": "https://github.com/49780522/i3-composer-agent.git"
|
package/plugin.json
CHANGED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Regression cases reduced from a real IDE failure; no native host is installed.
|
|
3
|
+
const test=require('node:test'),assert=require('node:assert/strict'),fs=require('node:fs'),path=require('node:path'),os=require('node:os'),cp=require('node:child_process');
|
|
4
|
+
const installed=path.join(__dirname,'lib/installation.js');
|
|
5
|
+
const scripts=path.join(fs.existsSync(installed)?require(installed).loadInstallation().skillsDirectory:path.resolve(__dirname,'../skills'),'i3-ui-prototype/scripts');
|
|
6
|
+
const E=require(path.join(scripts,'execution-evidence.cjs')),H=require(path.join(scripts,'codebuddy-hook.cjs')),N=require(path.join(scripts,'native-command.cjs')),R=require(path.join(scripts,'execution-recovery.cjs'));
|
|
7
|
+
const raw=rows=>rows.map(row=>JSON.stringify(row)).join('\n')+'\n';
|
|
8
|
+
for(const flat of [false,true])test(`raw ${flat?'flat':'paired'} use_skill is a real Skill without accepting Read or forged names`,()=>{
|
|
9
|
+
const rows=flat?[
|
|
10
|
+
{type:'function_call',callId:'front',name:'use_skill',arguments:'{"command":"i3-frontend-ui-engineering"}',sessionId:'s1'},
|
|
11
|
+
{type:'function_call_result',callId:'front',name:'Skill',status:'completed',output:'Frontend instructions',sessionId:'s1'}
|
|
12
|
+
]:[
|
|
13
|
+
{type:'assistant',sessionId:'s1',message:{content:[{type:'tool_use',id:'front',name:'use_skill',input:{command:'i3-frontend-ui-engineering'}}]}},
|
|
14
|
+
{type:'user',sessionId:'s1',message:{content:[{type:'tool_result',tool_use_id:'front',tool_name:'Skill',content:'Frontend instructions'}]}}
|
|
15
|
+
];
|
|
16
|
+
const calls=E.parse(raw(rows));assert.equal(E.loaded(calls,'frontend').length,1);assert.equal(calls[0].name,'Skill');assert.equal(calls[0].rawName,'use_skill');
|
|
17
|
+
assert.equal(H.eventCall({hook_event_name:'PostToolUse',tool_name:'use_skill',tool_use_id:'front',tool_input:calls[0].input},{prompts:[{line:0}],calls}).toolCallId,'front');
|
|
18
|
+
for(const name of ['Read','use_skill_fake']){
|
|
19
|
+
const changed=JSON.parse(JSON.stringify(rows));if(flat){changed[0].name=changed[1].name=name;}else{changed[0].message.content[0].name=changed[1].message.content[0].tool_name=name;}
|
|
20
|
+
assert.equal(E.loaded(E.parse(raw(changed)),'frontend').length,0);
|
|
21
|
+
}
|
|
22
|
+
const wrong=JSON.parse(JSON.stringify(rows));if(flat)wrong[1].sessionId='s2';else wrong[1].sessionId='s2';assert.throws(()=>E.parse(raw(wrong)),/session/);
|
|
23
|
+
});
|
|
24
|
+
test('native execute_command success cannot hide a failed stage inside result or stdout',()=>{
|
|
25
|
+
for(const result of [
|
|
26
|
+
{type:'execute_command_result',exitCode:1,stdout:'failed'},
|
|
27
|
+
{type:'execute_command_result',exitCode:0,stdout:'{"status":"failed","errors":["No native Hook task binding"]}\r\n\r\n'},
|
|
28
|
+
{type:'execute_command_result',exitCode:0,stdout:'Error: unavailable'},
|
|
29
|
+
{status:'failed',errors:['native stage failed']}
|
|
30
|
+
])assert.equal(E.successful({content:{status:'success',success:true,result}}),false);
|
|
31
|
+
assert.equal(E.successful({content:{status:'success',success:true,result:{type:'execute_command_result',exitCode:0,stdout:'{"status":"bound","runId":"r1"}'}}}),true);
|
|
32
|
+
assert.equal(E.successful({content:{result:{status:'failed'},observedApplicationState:true}}),true,'A browser observation of an error is not a failed tool');
|
|
33
|
+
});
|
|
34
|
+
test('frontend already loaded leaves only execution-begin missing until the native begin actually succeeds',()=>{
|
|
35
|
+
const directory='/work/prototypes/a',binding={entryLine:1,contextLine:0,sessionId:'s1',runId:'r1',directory,project:'/work'};
|
|
36
|
+
const source={prompts:[{line:1,text:'实现原型'}],contextLine:0,calls:[{skill:'frontend',success:true,sessionId:'s1',requestLine:2,resultLine:3}]};
|
|
37
|
+
const input={event:'PreToolUse',source,binding,tool:'Write',input:{file_path:directory+'/app/src/A.vue'},directory,cwd:'/work'};
|
|
38
|
+
let answer=H.decision(input);assert.equal(answer.deny,true);assert.deepEqual(answer.missing,['execution-begin']);assert.doesNotMatch(answer.reason,/调用 i3-frontend/);
|
|
39
|
+
const command=`node /skills/native-pilot.cjs execution-begin '${directory}'`;
|
|
40
|
+
const begin={toolCallId:'begin',name:'Bash',input:{command},cwd:'/work',success:false,sessionId:'s1',result:{status:'bound',runId:'r1',sessionId:'s1',directory},requestLine:4,resultLine:5};source.calls.push(begin);
|
|
41
|
+
assert.equal(H.decision(input).deny,true);begin.success=true;assert.equal(H.decision(input).continue,true);
|
|
42
|
+
begin.result={...begin.result,runId:'another-run'};assert.equal(H.decision(input).deny,true);begin.result.runId='r1';
|
|
43
|
+
begin.result.status='success';assert.equal(H.decision(input).deny,true);begin.result.status='bound';
|
|
44
|
+
source.stableThroughLine=4;assert.equal(H.decision(input).deny,true);delete source.stableThroughLine;
|
|
45
|
+
source.calls.push({...begin,toolCallId:'failed-rebind',requestLine:6,resultLine:7,success:false});assert.equal(H.decision(input).deny,true,'A failed rebind must not inherit old success');
|
|
46
|
+
});
|
|
47
|
+
test('binding recovery emits one absolute quoted command that the strict parser recognizes',()=>{
|
|
48
|
+
const directory="/work with spaces/prototypes/customer's demo";
|
|
49
|
+
const command=N.beginCommand(directory);const parsed=N.nativeCommand('Bash',{command},'/unrelated');
|
|
50
|
+
assert.equal(parsed.operation,'execution-begin');assert.equal(parsed.root,directory);assert.ok(path.isAbsolute(parsed.argv[0]));assert.ok(path.isAbsolute(parsed.argv[1]));
|
|
51
|
+
assert.match(R.instruction(['execution-begin'],{directory}),new RegExp('execution-begin'));assert.ok(R.instruction(['execution-begin'],{directory}).includes(command));
|
|
52
|
+
assert.match(R.instruction(['execution-begin'],{directory}),/管道|pipeline/);
|
|
53
|
+
assert.equal(N.nativeCommand('Bash',{command:`cd /work && ${command} 2>&1 | head -80`},'/work'),null);
|
|
54
|
+
});
|
|
55
|
+
test('unbound CLI execution-begin exits nonzero with an actionable standalone command and creates no proof',t=>{
|
|
56
|
+
const root=fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(),'i3-unbound-regression-')));t.after(()=>fs.rmSync(root,{recursive:true,force:true}));
|
|
57
|
+
const result=cp.spawnSync(process.execPath,[path.join(scripts,'native-pilot.cjs'),'execution-begin',root],{encoding:'utf8'});
|
|
58
|
+
assert.equal(result.status,1);const failure=JSON.parse(result.stderr);assert.equal(failure.status,'failed');assert.match(failure.errors.join(),/No native Hook task binding/);
|
|
59
|
+
assert.ok(failure.errors.join().includes(N.beginCommand(root)));assert.deepEqual(fs.readdirSync(root),[]);
|
|
60
|
+
});
|
|
61
|
+
test('PostToolUse failures use additionalContext and keep the tool return intact',()=>{
|
|
62
|
+
const value=require(path.join(scripts,'hosts/codebuddy.cjs')).format({feedback:true,reason:'实际返回失败,请补调'},'PostToolUse');
|
|
63
|
+
assert.equal(value.output.continue,true);assert.deepEqual(value.output.hookSpecificOutput,{hookEventName:'PostToolUse',additionalContext:'实际返回失败,请补调'});
|
|
64
|
+
const bound={status:'bound',runId:'r1'};assert.deepEqual(E.resultValue([{type:'text',text:JSON.stringify(bound)}]),bound);
|
|
65
|
+
});
|
|
66
|
+
test('native wrappers preserve actual JSON facts and quoted findings without matching escaped wrapper bytes',()=>{
|
|
67
|
+
const facts={form:{errors:1},observed:true},wrapped={status:'success',success:true,result:JSON.stringify(facts)};
|
|
68
|
+
const A=require(path.join(scripts,'execution-assertions.cjs'));
|
|
69
|
+
assert.equal(A.extract(A.resultData(wrapped),'/form/errors'),1);assert.ok(E.resultText(wrapped).includes('"observed":true'));
|
|
70
|
+
assert.deepEqual(A.resultData({result:{status:'failed'},observedApplicationState:true}),{result:{status:'failed'},observedApplicationState:true});
|
|
71
|
+
assert.equal(E.successful({content:{type:'text',text:'{"status":"failed","operation":"execution-begin"}'}}),false);
|
|
72
|
+
});
|
|
73
|
+
test('loading a missing browser Skill cannot let another stage stand in for the denied finalize retry',()=>{
|
|
74
|
+
const source={prompts:[{line:1,digest:'task',text:'实现原型'}],contextLine:0,calls:[]},index={};
|
|
75
|
+
const context={source,sessionId:'s1',directory:'/work/prototypes/a',stage:'delivery',event:'PreToolUse',eventKey:'deny-finalize',expectedRetry:{kind:'stage',stage:'delivery',operation:'finalize'}};
|
|
76
|
+
R.record(index,context,{deny:true,missing:['browser']});
|
|
77
|
+
source.calls.push({skill:'browser',toolCallId:'actual-browser-load',sessionId:'s1',requestLine:2,resultLine:3,success:true});
|
|
78
|
+
const failedPrerequisite=R.record(index,{...context,stage:'browser-entry',event:'PostToolUse',eventKey:'failed-review',expectedRetry:{kind:'stage',stage:'browser-entry',operation:'record-devtools-review'}},{feedback:true,missing:['execution-evidence']});
|
|
79
|
+
assert.equal(failedPrerequisite.recovery.expectedRetry.operation,'finalize');
|
|
80
|
+
const other=R.record(index,{...context,stage:'browser-entry',resolved:['execution-evidence'],event:'PostToolUse',eventKey:'other-stage-result',retrySucceeded:true,retry:{kind:'stage',stage:'browser-entry',operation:'record-devtools-review',requestLine:4,resultLine:5}},{continue:true});
|
|
81
|
+
assert.equal(other.recovery.status,'ready-to-retry');
|
|
82
|
+
const correct=R.record(index,{...context,event:'PostToolUse',eventKey:'finalize-result',retrySucceeded:true,retry:{kind:'stage',stage:'delivery',operation:'finalize',requestLine:6,resultLine:7}},{continue:true});
|
|
83
|
+
assert.equal(correct.recovery.status,'recovered');
|
|
84
|
+
});
|
|
@@ -32,11 +32,11 @@ function fixture(t,host,scope,client){
|
|
|
32
32
|
function tool(name,input,content='success',action=()=>{}){
|
|
33
33
|
const id=request(name,input),decision=hook('PreToolUse',name,input,id);
|
|
34
34
|
if(decision.deny){result(id,decision.reason,true);return {id,decision};}
|
|
35
|
-
action();result(id,content);return {id,decision,post:hook('PostToolUse',name,input,id)};
|
|
35
|
+
action();result(id,typeof content==='function'?content():content);return {id,decision,post:hook('PostToolUse',name,input,id)};
|
|
36
36
|
}
|
|
37
37
|
append({type:'user',message:{content:'实现原型,使用模板模式。Synthetic fixture only.'}});
|
|
38
38
|
tool('Skill',{skill:'i3-ui-prototype'},'Prototype Skill loaded');
|
|
39
|
-
const begin=()=>tool('Bash',{command:`node '${path.join(scripts,'native-pilot.cjs')}' execution-begin '${created.directory}'`},'bound');begin();
|
|
39
|
+
const begin=()=>tool('Bash',{command:`node '${path.join(scripts,'native-pilot.cjs')}' execution-begin '${created.directory}'`},()=>{const pointer=JSON.parse(fs.readFileSync(path.join(created.directory,'qa/execution-context.json')));return JSON.stringify({status:'bound',runId:pointer.runId,sessionId,directory:created.directory});});begin();
|
|
40
40
|
return {root,project,base,directory:created.directory,append,hook,tool,begin,result,request,read(){const pointer=JSON.parse(fs.readFileSync(path.join(created.directory,'qa/execution-context.json')));const selected=C.select({project,product:client==='CodeBuddyCLI'||!client&&host==='codebuddy'?'cli':'ide',host,home,env:{}});return JSON.parse(fs.readFileSync(path.join(C.sessionDirectory(selected,project,pointer.sessionId),'session.json')));}};
|
|
41
41
|
}
|
|
42
42
|
for(const [host,client] of [['codebuddy','CodeBuddyCLI'],['codebuddy','CodeBuddyIDE'],['workbuddy','WorkBuddy'],['codebuddy',null],['workbuddy',null]])for(const scope of ['project','global'])test(`${host} ${client||'native-flat-log'} ${scope}: deny, correlate successful Skills, retry and retain budget`,t=>{
|
|
@@ -62,6 +62,14 @@ test('Stop asks for missing Skills twice, then reports blocked without an endles
|
|
|
62
62
|
f.append({type:'user',message:{content:'先分析,不改代码'}});
|
|
63
63
|
const answer=f.tool('Write',{file_path:path.join(f.directory,'app/src/NoWrite.vue'),content:'no'});assert.equal(answer.decision.deny,true);assert.match(answer.decision.reason,/read-only/);
|
|
64
64
|
});
|
|
65
|
+
test('exhausted recovery cannot silently reopen writes after late Skill loads in the same human task',t=>{
|
|
66
|
+
const f=fixture(t,'codebuddy','project','CodeBuddyCLI');
|
|
67
|
+
for(let n=0;n<3;n++){f.append({type:'assistant',message:{content:'已完成,可以交付 '+n}});f.hook('Stop');}
|
|
68
|
+
f.tool('Skill',{skill:'i3-frontend-ui-engineering'});f.tool('Skill',{skill:'antdv'});
|
|
69
|
+
const file=path.join(f.directory,'app/src/AfterBudget.vue');
|
|
70
|
+
const answer=f.tool('Write',{file_path:file,content:'no'},'written',()=>fs.writeFileSync(file,'no'));
|
|
71
|
+
assert.equal(answer.decision.deny,true);assert.equal(fs.existsSync(file),false);assert.equal(f.read().bindings[f.directory].recovery.status,'blocked');
|
|
72
|
+
});
|
|
65
73
|
test('a duplicated native callback does not spend recovery budget when the transcript grows',t=>{
|
|
66
74
|
const f=fixture(t,'workbuddy','project',null),input={file_path:path.join(f.directory,'app/src/Duplicate.vue'),content:'fixture'};
|
|
67
75
|
const id=f.request('Write',input);assert.equal(f.hook('PreToolUse','Write',input,id).deny,true);
|
|
@@ -8,10 +8,10 @@ test('native command parsing handles quoted paths without evaluating shell subst
|
|
|
8
8
|
for(const command of ['node x; node native-pilot.cjs finalize /x','$(steal) native-pilot.cjs build /x','node x | node native-pilot.cjs finalize /x'])assert.equal(H.nativeCommand('Bash',{command},'/work'),null);
|
|
9
9
|
});
|
|
10
10
|
test('before UI write requires a successful frontend Skill in the same live context and task',()=>{
|
|
11
|
-
const source={prompts:[{line:1,text:'create prototype'}],calls:[],contextLine:0},binding={entryLine:1,contextLine:0,sessionId:'s1'};
|
|
11
|
+
const source={prompts:[{line:1,text:'create prototype'}],calls:[],contextLine:0},binding={entryLine:1,contextLine:0,sessionId:'s1',runId:'r1',directory:'/work/prototypes/p',project:'/work'};
|
|
12
12
|
const input={event:'PreToolUse',source,binding,tool:'Write',input:{file_path:'/work/prototypes/p/app/src/A.vue'},directory:'/work/prototypes/p'};
|
|
13
13
|
assert.equal(H.decision(input).deny,true);
|
|
14
|
-
source.calls=[{skill:'frontend',sessionId:'s1',requestLine:2,resultLine:3,success:true}];assert.equal(H.decision(input).continue,true);
|
|
14
|
+
source.calls=[{skill:'frontend',sessionId:'s1',requestLine:2,resultLine:3,success:true},{name:'Bash',input:{command:'node /skills/native-pilot.cjs execution-begin /work/prototypes/p'},sessionId:'s1',result:{status:'bound',runId:'r1',sessionId:'s1',directory:binding.directory},requestLine:4,resultLine:5,success:true}];assert.equal(H.decision(input).continue,true);
|
|
15
15
|
source.contextLine=4;assert.equal(H.decision(input).deny,true);
|
|
16
16
|
});
|
|
17
17
|
test('analysis overrides old tasks and Stop feedback is bounded without granting tool permissions',()=>{
|
|
@@ -75,7 +75,7 @@ test('IDE streaming requests correlate only within the exact native generation a
|
|
|
75
75
|
assert.throws(()=>H.eventCall({...payload,tool_input:{content:'unrelated'}},source),/uniquely/);
|
|
76
76
|
source.calls.push({...source.calls[0],toolCallId:'second-call'});assert.throws(()=>H.eventCall(payload,source),/uniquely/);
|
|
77
77
|
});
|
|
78
|
-
test('IDE transient reads retry
|
|
78
|
+
test('IDE and CLI transient reads retry within a fixed limit; wrong generations and requests remain rejected',()=>{
|
|
79
79
|
const payload={hook_event_name:'PreToolUse',client:'CodeBuddyIDE',generation_id:'g1',tool_name:'Write',tool_input:{content:'ui'}};
|
|
80
80
|
const pending={format:'codebuddy-ide-index-v1',prompts:[{line:1,nativeRequestId:'g1'}],calls:[{name:'Write',toolCallId:'w1',nativeRequestId:'g1',input:{content:'ui'},requestLine:2}]};
|
|
81
81
|
let reads=0,waits=0;
|
|
@@ -84,7 +84,7 @@ test('IDE transient reads retry while wrong generations and mismatched pending r
|
|
|
84
84
|
assert.equal(result.calls[0].toolCallId,'w1');assert.equal(result.nativeReadAttempts,3);assert.equal(waits,2);
|
|
85
85
|
reads=0;assert.throws(()=>H.eventSource({...payload,generation_id:'g2'},{product:'ide'},'/work',{readSource:()=>{reads++;return pending;},pause:()=>{}}),/generation/);assert.equal(reads,8);
|
|
86
86
|
assert.throws(()=>H.eventSource({...payload,tool_input:{content:'different'}},{product:'ide'},'/work',{readSource:()=>pending,pause:()=>{}}),/uniquely/);
|
|
87
|
-
reads=0;assert.throws(()=>H.eventSource({...payload,client:'cli'},{product:'cli'},'/work',{readSource:()=>{reads++;return {...pending,calls:[]};},pause:()=>
|
|
87
|
+
reads=0;assert.throws(()=>H.eventSource({...payload,client:'cli'},{product:'cli'},'/work',{readSource:()=>{reads++;return {...pending,calls:[]};},pause:()=>{}}),/uniquely/);assert.equal(reads,8);
|
|
88
88
|
});
|
|
89
89
|
test('IDE defers current callback evidence until the host persists it and never invents Skill success',()=>{
|
|
90
90
|
const source={format:'codebuddy-ide-index-v1',prompts:[{line:1,nativeRequestId:'g1',text:'implement prototype'}],calls:[],contextLine:0};
|
|
@@ -12,17 +12,18 @@ test('native UI writes resolve relative paths from the actual tool cwd',()=>{
|
|
|
12
12
|
});
|
|
13
13
|
function fixture() {
|
|
14
14
|
const file='/work/prototypes/orders/app/src/Orders.vue',bytes='<Table />';
|
|
15
|
-
const call=(toolCallId,name,input,result,line,skill)=>({toolCallId,name,input,result,requestLine:line,resultLine:line+
|
|
15
|
+
const call=(toolCallId,name,input,result,line,skill)=>({toolCallId,name,input,result,requestLine:line+2,resultLine:line+3,success:true,resultDigest:E.sha(result),sessionId:'s1',contextLine:0,skill});
|
|
16
16
|
const calls=[call('front','Skill',{},'frontend instructions',3,'frontend'),call('ant','Skill',{},'antdv instructions',5,'antdv'),call('query','Bash',{command:'antdv info Table'},'Table API: rowSelection',7),call('version','Read',{file_path:'/work/app/node_modules/antdv-next/package.json'},'{"version":"1.5.3"}',9),call('write','Write',{file_path:file},'saved',11)];
|
|
17
17
|
const policy=P.create({mode:'template'}),current={digest:'candidate',inputDigest:'input',revision:'r001'};
|
|
18
18
|
const binding={schemaVersion:1,runId:'r1',sessionId:'s1',projectKey:'p1',directory:'/work/prototypes/orders',instanceId:'instance',policyDigest:policy.digest,inputDigest:'input',entryLine:1};
|
|
19
|
+
calls.push(call('begin','Bash',{command:'node /skills/native-pilot.cjs execution-begin /work/prototypes/orders'},JSON.stringify({status:'bound',runId:'r1',sessionId:'s1',directory:binding.directory}),0));
|
|
19
20
|
const proof={schemaVersion:1,runId:'r1',sessionId:'s1',projectKey:'p1',policyDigest:policy.digest,candidateDigest:'candidate',inputDigest:'input',revision:'r001',frontend:{toolCallId:'front',files:[{path:'app/src/Orders.vue',sha256:E.sha(bytes)}],requirementIds:['R1']},official:{skill:{toolCallId:'ant'},version:{toolCallId:'version',quote:'"version":"1.5.3"'},queries:[{component:'Table',toolCallId:'query',quote:'Table API: rowSelection'}]},templateChoice:{quote:'template mode'}};
|
|
20
21
|
return {policy,current,binding,proof,source:{calls,contextLine:0,prompts:[{line:1,text:'generate prototype in template mode',digest:'prompt'}]},scope:{requirements:[{id:'R1'}]},files:{'app/src/Orders.vue':bytes},implementation:{bindings:[{status:'implemented',components:['Table'],files:[{path:'app/src/Orders.vue'}]}]},frameworkVersion:'1.5.3'};
|
|
21
22
|
}
|
|
22
23
|
test('a native frontend load is required before the first actual UI write in both modes',()=>{
|
|
23
24
|
const f=fixture();assert.equal(G.evaluate(f,'implementation').status,'passed');
|
|
24
25
|
f.source.calls[0].success=false;assert.equal(G.evaluate(f,'implementation').status,'failed');
|
|
25
|
-
f.source.calls[0].success=true;f.source.calls[0].resultLine=
|
|
26
|
+
f.source.calls[0].success=true;f.source.calls[0].resultLine=14;assert.match(G.evaluate(f,'implementation').errors.join(),/before/);
|
|
26
27
|
});
|
|
27
28
|
test('IDE relative write evidence uses its bound workspace while legacy evidence keeps its contract',()=>{
|
|
28
29
|
const f=fixture();f.source.format='codebuddy-ide-index-v1';f.binding.project='/work';f.source.calls.find(c=>c.name==='Write').input.file_path='prototypes/orders/app/src/Orders.vue';
|
|
@@ -40,7 +41,7 @@ test('new WorkBuddy implementation uses the shared design gate and rechecks reco
|
|
|
40
41
|
const f=fixture();f.policy=P.create({id:P.AUTO_ID,host:'workbuddy',mode:'template'});
|
|
41
42
|
Object.assign(f.binding,{host:'workbuddy',identitySource:'native-client',policyDigest:f.policy.digest});f.proof.policyDigest=f.policy.digest;
|
|
42
43
|
assert.equal(G.evaluate(f,'implementation').status,'passed');
|
|
43
|
-
f.binding.recovery={status:'recovered',invocations:[{skill:'frontend',toolCallId:'front',resultDigest:f.source.calls[0].resultDigest,requestLine:
|
|
44
|
+
f.binding.recovery={status:'recovered',invocations:[{skill:'frontend',toolCallId:'front',resultDigest:f.source.calls[0].resultDigest,requestLine:5,resultLine:6,feedbackLine:1}],retry:{toolCallId:'write'}};
|
|
44
45
|
assert.equal(G.evaluate(f,'implementation').status,'passed');
|
|
45
46
|
f.binding.recovery.retry.toolCallId='invented';assert.equal(G.evaluate(f,'implementation').status,'failed');
|
|
46
47
|
delete f.binding.recovery;f.source.calls.find(call=>call.skill==='antdv').success=false;
|
|
@@ -57,13 +57,9 @@ analyze 的交付是需求/能力覆盖、模板与框架选型、交互验收
|
|
|
57
57
|
|
|
58
58
|
已有 v2 实例先运行一次 `node <skill-root>/scripts/native-pilot.cjs state <prototype-root>`,从聚合结果恢复身份、固定需求、设计/构建/实际QA、负责人和下一步的精确命令。不要为寻找参数反复遍历全仓库、node_modules或阅读控制器内部实现。这里只读检查,不安装、启动浏览器或更新状态。
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
制作前实际调用所需的前端/框架/设计 Skill,真实浏览器验收使用浏览器 Skill;不能以读取 SKILL.md 代替调用。安装器注册的 Hooks 自动运行。收到缺少 Skill 的反馈时,补调该 Skill、等待成功后重试原操作;基础设施异常只简要报告未验收项,由独立维护入口处理,不在原型任务中排查。
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
CodeBuddy/WorkBuddy 新 create 前,用 `native-pilot.cjs execution-assertions-template --scope <scope-file>` 取得完整场景,按本次 PRD 与能力参数填充每项预期 JSON 事实,存于该原型 `.preparation` 目录。create 增加 `--host <codebuddy|workbuddy> --execution-policy buddy-ab-v2 --execution-assertions <assertions-file>`;官方 Skill 真实不可用时才显式 `--official-source-fallback`,保留其失败调用定位。随后在实际所选宿主主会话执行 `execution-begin <prototype-root>`,以原生 Skill 工具加载当前上下文需要的前端 Skill 后再写业务界面。旧 codebuddy-ab-v1 及未采用 executionPolicy 的实例保留原合同。其他宿主尚未适配此 Hook 合同,按原流程披露调用核验边界。
|
|
65
|
-
|
|
66
|
-
`execution-template <prototype-root>` 返回当前身份与已观察到的真实加载引用;以实际查询、实现复核及浏览器结果补齐 `qa/execution-evidence.json`,不能补造调用 ID。构建、record-run、record-devtools-review、finalize 与 `execution-check --stage <stage>` 共用 A;B 在可识别的写入前检查,反馈明确 Skill 名与下一动作,成功补调后重试;Stop 按同缺项两次、当前原生任务总计六次的预算继续纠正,到上限如实报告阻塞,反复 begin 不清零。只读分析、进度、阻塞及用户停止可正常结束。未通过门禁时交付候选和缺项;新收据的 `native-source-correlated` 不代表不可篡改认证或用户认可。
|
|
62
|
+
新 create 使用实际 `--host <codebuddy|workbuddy>`。按 PRD 和固定能力制定交互验收场景,使用 `native-pilot.cjs execution-assertions-template --scope <scope-file>` 取得场景骨架,填充预期 JSON 事实并传 `--execution-assertions <assertions-file>`。这是原型交互验收,不要求手动绑定会话或填写 Hook 调用记录。旧 HTML 和既有 execution-* 入口保持兼容。
|
|
67
63
|
|
|
68
64
|
### 2. 完整内容模板与需求能力
|
|
69
65
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 原型调用记录:兼容合同
|
|
2
2
|
|
|
3
3
|
<!-- i3:zh-guide:start -->
|
|
4
4
|
## 中文使用说明
|
|
@@ -8,6 +8,12 @@
|
|
|
8
8
|
默认使用简体中文解释、追问及生成报告;用户明确要求其他语言时可切换。原方法正文、引用、阈值和审批条件完整保留。代码标识、路径、命令、工具参数、JSON 键/枚举与原文引用保持原样。
|
|
9
9
|
<!-- i3:zh-guide:end -->
|
|
10
10
|
|
|
11
|
+
当前正常流程由安装器注册的 Hooks 自动采集真实 Skill 和工具调用。原型会话直接制作、修订和验收 UI;没有手动 execution-begin、检查 Hook 或填写内部调用记录的前置。缺少必要 Skill 时只补调该 Skill 并重试原操作。安装、原生协议和失败诊断统一见随包 [Hooks 维护说明](../../../hooks/PROTOTYPE.md),不属于原型工作步骤。
|
|
12
|
+
|
|
13
|
+
以下为已有显式 execution-* 使用者保留的兼容格式与旧手动流程,不作为新任务指导;已有原型不会自动改写。
|
|
14
|
+
|
|
15
|
+
## 旧 Buddy A+B 执行与补调合同
|
|
16
|
+
|
|
11
17
|
本合同适配 CodeBuddy IDE/CLI 和 WorkBuddy 桌面 command Hooks;各宿主项目/全局的真实调用、补调及业务效果仍须逐项手测,profile 保持 pending-user。不是新增专责 Agent,不改 using 的发现/路由、上游前端/浏览器方法或上游 Hooks。A 是确定性门禁,B 采集与绑定原生来源;没有 C 提示词对照表方案。
|
|
12
18
|
|
|
13
19
|
## 安装范围与原生生效
|
|
@@ -44,6 +50,10 @@ node <skill-root>/scripts/native-pilot.cjs execution-assertions-template --scope
|
|
|
44
50
|
|
|
45
51
|
通过自然语言/using 选到工作 Skill,或实际 `/i3-prototype` 快捷入口后,新实例执行 `execution-begin <prototype-root>`。B 回查原生用户请求和工作 Skill/快捷命令,绑定规范化 projectKey、sessionId、runId、实例、输入、模式、策略与注册。只读或停止请求不能启动写任务。新会话/新请求/压缩后的上下文在恢复实施前重新 begin 并加载适用 Skill;用户手改保留。初建骨架不等于实际实施。
|
|
46
52
|
|
|
53
|
+
使用 `state` 中的 `commands.executionBegin.shell` 或拒绝反馈中的绝对路径命令,作为一次独立 Shell 调用;不加 `cd &&`、管道、重定向或 `head`。严格解析器不解释组合 Shell。成功须同时满足进程退出 0 和内层 `status=bound`;外层 `status=success`/`success:true` 不足以证明阶段成功。缺绑定、任务切换、上下文变化或输入变化时重新执行,不能由 CLI 自行写入原生绑定;A 和写入门禁回查当前任务最新一次 begin 的成功结果,失败/待返回不可复用旧成功。
|
|
54
|
+
|
|
55
|
+
原始工具名 `use_skill`/`skill` 与 `Skill` 通过同一有限别名表识别;IDE 历史转换和原始 JSONL 采用相同规则。保留原始请求/结果摘要、调用 ID、会话与时序校验,普通 Read、未知近似名称仍不算 Skill。原生 execute_command 的内层非零退出、错误结果或 stdout 中完整 JSON `status=failed` 都按失败处理;不会把浏览器返回的任意业务 `result.status` 当成工具错误。
|
|
56
|
+
|
|
47
57
|
`execution-template <prototype-root>` 只读返回当前证据骨架和已经成功返回的 Skill 引用;保存并填入 `qa/execution-evidence.json`。字段语义:
|
|
48
58
|
|
|
49
59
|
- 固定身份:schemaVersion=1、runId、sessionId、projectKey、policyDigest、inputDigest、candidateDigest、revision,须与当前绑定一致。
|
|
@@ -74,6 +84,8 @@ B 的 PreToolUse 只拒绝可识别的缺前置 UI 写入或明确阶段命令
|
|
|
74
84
|
|
|
75
85
|
B 拒绝后主会话立即补调,等待成功返回,再重试被拒操作;普通 Read 或另开会话不算本主会话 Skill 调用。缺项、反馈事件、后续成功 toolCallId/resultDigest 及重试结果在宿主会话状态落盘。required → ready-to-retry → recovered 分别表示缺项、已可重试、观察到成功重试;recovered 不表示业务验收通过。A 再回查补调及重试的原生引用和时序。
|
|
76
86
|
|
|
87
|
+
PostToolUse 观察到已落盘的 begin/阶段失败时,通过该事件的 additionalContext 返回补调指令并保留 required;Stop 才使用停止反馈协议。IDE 尚未持久化当前结果时继续等待后续原生来源核验,不制造成功。实际 Skill 已成功但缺绑定时只提示 execution-begin;补调成功不直接记 recovered,仍须观察原操作的成功重试。
|
|
88
|
+
|
|
77
89
|
重试按 kind 区分 ui-write 和 stage;阶段重试绑定实际 operation、stage 与同一原型目录。两类新记录都以 feedbackLine 核对请求发生在反馈之后,旧成功回调不能恢复后续拒绝;旧无 kind 的记录继续仅按页面写入合同核验。
|
|
78
90
|
|
|
79
91
|
默认同一缺项两次、单原生任务总计六次,重复回调不增加预算,反复 begin 和内部 providerData.isMeta 的 Hook 消息不重置任务。达到上限时保持受管操作拒绝,允许真实 blocked 报告正常结束;不无限驱动 Stop。用户只读/暂停优先。入口、Skill 发现、加载成功、CLI 查询、重试成功、交付和用户认可分开记录。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const fs=require('node:fs'),path=require('node:path'),os=require('node:os'),assert=require('node:assert/strict');
|
|
3
3
|
const R=require('./codebuddy-registration.cjs'),E=require('./execution-evidence.cjs'),H=require('./buddy-host.cjs');
|
|
4
|
-
const FILES=['native-command.cjs','buddy-host.cjs','execution-recovery.cjs','execution-policy.cjs','execution-evidence.cjs','codebuddy-ide-source.cjs','execution-gates.cjs','execution-assertions.cjs','execution-runtime.cjs','codebuddy-context.cjs','codebuddy-registration.cjs','codebuddy-hook.cjs','prototype-hooks.cjs','hosts/codebuddy.cjs','native-pilot.cjs','native-pilot-lib.cjs','native-delivery.cjs','native-devtools.cjs','native-files.cjs','native-build.cjs'];
|
|
4
|
+
const FILES=['native-tool-name.cjs','native-command.cjs','buddy-host.cjs','execution-recovery.cjs','execution-diagnostics.cjs','ptc-callbacks.cjs','native-tool-result.cjs','prototype-dependencies.cjs','execution-policy.cjs','execution-evidence.cjs','codebuddy-ide-source.cjs','execution-gates.cjs','execution-assertions.cjs','execution-runtime.cjs','codebuddy-context.cjs','codebuddy-registration.cjs','codebuddy-hook.cjs','prototype-hooks.cjs','hosts/codebuddy.cjs','native-pilot.cjs','native-pilot-lib.cjs','native-delivery.cjs','native-devtools.cjs','native-files.cjs','native-build.cjs'];
|
|
5
5
|
const read=file=>R.fileIO.read(file);
|
|
6
6
|
function scriptsForEntry(scriptPath) {
|
|
7
7
|
if(path.basename(scriptPath)!=='i3-prototype.cjs')return path.dirname(path.dirname(scriptPath));
|
|
@@ -60,7 +60,7 @@ function source(payload,record,project) {
|
|
|
60
60
|
assert.ok(value.prompts.length,'Original user task is absent from native transcript');
|
|
61
61
|
assert.ok(value.calls.every(call=>call.sessionId===payload.session_id),'Native session identity missing or mismatched');
|
|
62
62
|
assert.ok(value.prompts.every(prompt=>!prompt.sessionId||prompt.sessionId===payload.session_id),'Native prompt session differs');
|
|
63
|
-
return value;
|
|
63
|
+
return record.product==='cli'?require('./ptc-callbacks.cjs').read(value,{record,project,sessionId:payload.session_id}):value;
|
|
64
64
|
}
|
|
65
65
|
function readOnly(prompt) {
|
|
66
66
|
if(/不(?:要)?(?:改代码|修改(?:任何)?代码|实施|生成原型)|\b(?:analysis only|do not (?:edit|modify|implement))\b/i.test(prompt))return true;
|
|
@@ -72,4 +72,4 @@ function prefix(file,bytes,digest) {
|
|
|
72
72
|
assert.ok(Number.isSafeInteger(bytes)&&bytes>0&&bytes<=64*1024*1024,'Invalid source cursor');
|
|
73
73
|
const raw=E.cursorBytes(file);assert.ok(raw.length>=bytes,'Native transcript was truncated');assert.equal(E.sha(raw.subarray(0,bytes)),digest,'Native transcript prefix changed');
|
|
74
74
|
}
|
|
75
|
-
module.exports={read,scriptsForEntry,bundle,registration,globalBase,select,projectKey,sessionDirectory,source,readOnly,prefix};
|
|
75
|
+
module.exports={FILES:Object.freeze(FILES),read,scriptsForEntry,bundle,registration,globalBase,select,projectKey,sessionDirectory,source,readOnly,prefix};
|