@ia-ccun/code-agent-cli 0.0.54-beta.1 → 0.0.54-beta.3
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/bin/cli.js +97 -69
- package/config/agent/extensions/aicode-advisor.ts +187 -64
- package/config/agent/extensions/aicode-banner.ts +101 -0
- package/config/agent/extensions/aicode-btw.ts +619 -182
- package/config/agent/extensions/aicode-cd.ts +246 -63
- package/config/agent/extensions/aicode-pets.ts +0 -11
- package/config/agent/extensions/aicode-plan.ts +2 -0
- package/config/agent/extensions/aicode-safety-guard.ts +3 -0
- package/config/agent/extensions/aicode-tips.ts +1 -16
- package/config/agent/extensions/context-monitor.ts +30 -15
- package/config/agent/extensions/context.ts +132 -60
- package/config/agent/extensions/custom-footer.ts +0 -5
- package/config/agent/extensions/custom.ts +41 -29
- package/config/agent/extensions/error-monitor.ts +37 -33
- package/config/agent/extensions/goal.ts +8 -6
- package/config/agent/extensions/msg-debug.ts +103 -112
- package/config/agent/extensions/review.ts +365 -164
- package/config/agent/extensions/working-msg.ts +2 -4
- package/config/agent/settings.json +15 -14
- package/config/agent/skills/hello/SKILL.md +1 -1
- package/config/agent/skills/refactor/SKILL.md +40 -0
- package/dist/banner.d.ts.map +1 -1
- package/dist/banner.js +8 -1
- package/dist/banner.js.map +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +9 -2
- package/dist/config-loader.js.map +1 -1
- package/package.json +6 -7
- package/scripts/regression/README.md +224 -0
- package/scripts/regression/cases/01-version-and-deps.sh +94 -0
- package/scripts/regression/cases/02-extension-loading.sh +130 -0
- package/scripts/regression/cases/03-rpc-protocol.sh +259 -0
- package/scripts/regression/cases/04-prompt-stream.sh +130 -0
- package/scripts/regression/cases/05-slash-commands-and-skills.sh +119 -0
- package/scripts/regression/cases/06-tool-execution.sh +115 -0
- package/scripts/regression/cases/07-pi-new-features.sh +141 -0
- package/scripts/regression/lib/common.sh +287 -0
- package/scripts/regression/results/.last_pid +1 -0
- package/scripts/regression/results/.last_workdir +1 -0
- package/scripts/regression/results/02-extension-loading.log +21 -0
- package/scripts/regression/results/03-prompt-stream.log +16 -0
- package/scripts/regression/results/03-rpc-protocol.log +27 -0
- package/scripts/regression/results/04-slash-commands-and-skills.log +11 -0
- package/scripts/regression/results/05-tool-execution.log +8 -0
- package/scripts/regression/results/06-pi-new-features.log +27 -0
- package/scripts/regression/run-all.sh +187 -0
- package/config/agent/bin/fd.exe +0 -0
- package/config/agent/bin/rg.exe +0 -0
- package/config/agent/extensions/aicode-filter-outputs.ts +0 -131
- package/config/agent/extensions/aicode-snake.ts +0 -354
- /package/config/agent/{skills_bak → skills}/csv-data-summarizer/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/csv-data-summarizer/analyze.py +0 -0
- /package/config/agent/{skills_bak → skills}/csv-data-summarizer/examples/showcase_financial_pl_data.csv +0 -0
- /package/config/agent/{skills_bak → skills}/csv-data-summarizer/requirements.txt +0 -0
- /package/config/agent/{skills_bak → skills}/csv-data-summarizer/resources/sample.csv +0 -0
- /package/config/agent/{skills_bak → skills}/dispatching-parallel-agents/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/find-skills/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/finishing-a-development-branch/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/LICENSE +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/README.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/README.zh.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/agentloop-core.svg +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/agents/openai.yaml +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style1-flat.png +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style2-dark.png +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style3-blueprint.png +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style4-notion.png +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style5-glass.png +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style6-claude.png +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style7-openai.png +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/agent-memory-types-style4.json +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/api-flow-style7.json +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/mem0-style1.json +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/microservices-style3.json +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/multi-agent-style5.json +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/system-architecture-style6.json +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/tool-call-style2.json +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/package.json +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/icons.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-1-flat-icon.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-2-dark-terminal.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-3-blueprint.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-4-notion-clean.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-5-glassmorphism.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-6-claude-official.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-7-openai.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-diagram-matrix.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/svg-layout-best-practices.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/scripts/README.md +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/scripts/generate-diagram.sh +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/scripts/generate-from-template.py +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/scripts/test-all-styles.sh +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/scripts/validate-svg.sh +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/agent-architecture.svg +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/architecture.svg +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/comparison-matrix.svg +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/data-flow.svg +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/er-diagram.svg +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/flowchart.svg +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/sequence.svg +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/state-machine.svg +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/timeline.svg +0 -0
- /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/use-case.svg +0 -0
- /package/config/agent/{skills_bak → skills}/librarian/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/markdown-to-html/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/milvus-skill/LICENSE +0 -0
- /package/config/agent/{skills_bak → skills}/milvus-skill/README.md +0 -0
- /package/config/agent/{skills_bak → skills}/milvus-skill/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/milvus-skill/references/collection.md +0 -0
- /package/config/agent/{skills_bak → skills}/milvus-skill/references/database.md +0 -0
- /package/config/agent/{skills_bak → skills}/milvus-skill/references/index.md +0 -0
- /package/config/agent/{skills_bak → skills}/milvus-skill/references/partition.md +0 -0
- /package/config/agent/{skills_bak → skills}/milvus-skill/references/patterns.md +0 -0
- /package/config/agent/{skills_bak → skills}/milvus-skill/references/user-role.md +0 -0
- /package/config/agent/{skills_bak → skills}/milvus-skill/references/vector.md +0 -0
- /package/config/agent/{skills_bak → skills}/pr/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/receiving-code-review/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/requesting-code-review/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/requesting-code-review/code-reviewer.md +0 -0
- /package/config/agent/{skills_bak → skills}/subagent-driven-development/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/subagent-driven-development/code-quality-reviewer-prompt.md +0 -0
- /package/config/agent/{skills_bak → skills}/subagent-driven-development/implementer-prompt.md +0 -0
- /package/config/agent/{skills_bak → skills}/subagent-driven-development/spec-reviewer-prompt.md +0 -0
- /package/config/agent/{skills_bak → skills}/systematic-debugging/CREATION-LOG.md +0 -0
- /package/config/agent/{skills_bak → skills}/systematic-debugging/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/systematic-debugging/condition-based-waiting-example.ts +0 -0
- /package/config/agent/{skills_bak → skills}/systematic-debugging/condition-based-waiting.md +0 -0
- /package/config/agent/{skills_bak → skills}/systematic-debugging/defense-in-depth.md +0 -0
- /package/config/agent/{skills_bak → skills}/systematic-debugging/find-polluter.sh +0 -0
- /package/config/agent/{skills_bak → skills}/systematic-debugging/root-cause-tracing.md +0 -0
- /package/config/agent/{skills_bak → skills}/systematic-debugging/test-academic.md +0 -0
- /package/config/agent/{skills_bak → skills}/systematic-debugging/test-pressure-1.md +0 -0
- /package/config/agent/{skills_bak → skills}/systematic-debugging/test-pressure-2.md +0 -0
- /package/config/agent/{skills_bak → skills}/systematic-debugging/test-pressure-3.md +0 -0
- /package/config/agent/{skills_bak → skills}/test-driven-development/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/test-driven-development/testing-anti-patterns.md +0 -0
- /package/config/agent/{skills_bak → skills}/unit-test/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/using-git-worktrees/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/zread/README.md +0 -0
- /package/config/agent/{skills_bak → skills}/zread/SKILL.md +0 -0
- /package/config/agent/{skills_bak → skills}/zread/references/stdio-protocol.md +0 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 05-tool-execution.sh
|
|
3
|
+
# 验证:tool_execution_* 事件流(AicodeRpcClient.kt 的 ToolStart/ToolUpdate/ToolEnd 映射)
|
|
4
|
+
#
|
|
5
|
+
# 关键事件序列:
|
|
6
|
+
# 1. {"type":"tool_execution_start",toolCallId,toolName,args}
|
|
7
|
+
# 2. N × {"type":"tool_execution_update",toolCallId,partialResult:{content:[{text:"..."}]}}
|
|
8
|
+
# 3. {"type":"tool_execution_end",toolCallId,result:{content:[{text:"..."}]},isError:"false"|"true"}
|
|
9
|
+
#
|
|
10
|
+
# 0.84.3 修复了 toolcall_start 缺 id/name 的 bug,所以 toolCallId 字段必须存在
|
|
11
|
+
|
|
12
|
+
set -uo pipefail
|
|
13
|
+
CURRENT_CASE="05-tool-execution"
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
source "$SCRIPT_DIR/../lib/common.sh"
|
|
16
|
+
|
|
17
|
+
header "$CURRENT_CASE" "tool_execution event flow"
|
|
18
|
+
|
|
19
|
+
if ! spawn_rpc 120; then
|
|
20
|
+
fail "spawn aicode RPC" "进程启动失败"
|
|
21
|
+
summary
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
pass "spawn aicode RPC (pid=$REGRESSION_PID)"
|
|
25
|
+
|
|
26
|
+
# 触发 read:让模型读取一个已知文件
|
|
27
|
+
TEST_FILE="/etc/hostname"
|
|
28
|
+
send "{\"type\":\"prompt\",\"message\":\"use read tool to read /etc/hostname\",\"id\":\"t1\"}"
|
|
29
|
+
|
|
30
|
+
# 等 turn 完成(最长 90s)
|
|
31
|
+
header "$CURRENT_CASE" "waiting for turn"
|
|
32
|
+
|
|
33
|
+
waited=0
|
|
34
|
+
while [[ $waited -lt 90 ]]; do
|
|
35
|
+
if jq -e 'select(.id=="t1" and .type=="response")' "$REGRESSION_LOG" >/dev/null 2>&1; then
|
|
36
|
+
break
|
|
37
|
+
fi
|
|
38
|
+
sleep 1
|
|
39
|
+
waited=$((waited + 1))
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
if ! jq -e 'select(.id=="t1" and .type=="response")' "$REGRESSION_LOG" >/dev/null 2>&1; then
|
|
43
|
+
fail "prompt 在 90s 内完成" "超时"
|
|
44
|
+
cleanup_rpc; summary; exit 1
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# 验证 tool 事件
|
|
48
|
+
header "$CURRENT_CASE" "tool_execution events"
|
|
49
|
+
|
|
50
|
+
start_count=$(jq -c 'select(.type=="tool_execution_start")' "$REGRESSION_LOG" 2>/dev/null | wc -l | tr -d ' ')
|
|
51
|
+
end_count=$(jq -c 'select(.type=="tool_execution_end")' "$REGRESSION_LOG" 2>/dev/null | wc -l | tr -d ' ')
|
|
52
|
+
|
|
53
|
+
echo " tool_execution_start: $start_count"
|
|
54
|
+
echo " tool_execution_end: $end_count"
|
|
55
|
+
|
|
56
|
+
if [[ $start_count -gt 0 ]]; then
|
|
57
|
+
pass "tool_execution_start 出现 ($start_count 个)"
|
|
58
|
+
|
|
59
|
+
# 0.84.3 修复:toolCallId 必现
|
|
60
|
+
tool_call_id=$(jq -c 'select(.type=="tool_execution_start") | .toolCallId // "missing"' "$REGRESSION_LOG" 2>/dev/null | head -1)
|
|
61
|
+
if [[ "$tool_call_id" != '"missing"' && "$tool_call_id" != '""' && -n "$tool_call_id" ]]; then
|
|
62
|
+
pass "tool_execution_start.toolCallId 存在 (0.84.3 修复点)"
|
|
63
|
+
else
|
|
64
|
+
fail "tool_execution_start.toolCallId 存在" "got: $tool_call_id"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# 0.84.3 修复:toolName 必现
|
|
68
|
+
tool_name=$(jq -c 'select(.type=="tool_execution_start") | .toolName // "missing"' "$REGRESSION_LOG" 2>/dev/null | head -1)
|
|
69
|
+
if [[ "$tool_name" != '"missing"' && "$tool_name" != '""' && -n "$tool_name" ]]; then
|
|
70
|
+
pass "tool_execution_start.toolName 存在 (0.84.3 修复点) = $tool_name"
|
|
71
|
+
else
|
|
72
|
+
fail "tool_execution_start.toolName 存在" "got: $tool_name"
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# args 字段
|
|
76
|
+
args=$(jq -c 'select(.type=="tool_execution_start") | .args // "missing" | type' "$REGRESSION_LOG" 2>/dev/null | head -1)
|
|
77
|
+
if [[ "$args" == '"object"' ]]; then
|
|
78
|
+
pass "tool_execution_start.args 是 object"
|
|
79
|
+
else
|
|
80
|
+
fail "tool_execution_start.args 是 object" "got type: $args"
|
|
81
|
+
fi
|
|
82
|
+
else
|
|
83
|
+
skip "tool_execution_start 出现" "模型没调用工具"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
if [[ $end_count -gt 0 ]]; then
|
|
87
|
+
pass "tool_execution_end 出现 ($end_count 个)"
|
|
88
|
+
|
|
89
|
+
# isError 字段
|
|
90
|
+
is_error=$(jq -c 'select(.type=="tool_execution_end") | .isError // "missing"' "$REGRESSION_LOG" 2>/dev/null | head -1)
|
|
91
|
+
if [[ "$is_error" == '"false"' || "$is_error" == '"true"' ]]; then
|
|
92
|
+
pass "tool_execution_end.isError 是 \"false\"|\"true\""
|
|
93
|
+
else
|
|
94
|
+
fail "tool_execution_end.isError 格式" "got: $is_error"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# result.content 数组
|
|
98
|
+
result_type=$(jq -c 'select(.type=="tool_execution_end") | .result.content | type' "$REGRESSION_LOG" 2>/dev/null | head -1)
|
|
99
|
+
if [[ "$result_type" == '"array"' ]]; then
|
|
100
|
+
pass "tool_execution_end.result.content 是数组"
|
|
101
|
+
else
|
|
102
|
+
fail "tool_execution_end.result.content 是数组" "got: $result_type"
|
|
103
|
+
fi
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# start 与 end 配对
|
|
107
|
+
if [[ $start_count -gt 0 && $start_count -eq $end_count ]]; then
|
|
108
|
+
pass "tool_execution_start 与 tool_execution_end 配对 ($start_count == $end_count)"
|
|
109
|
+
elif [[ $start_count -gt 0 ]]; then
|
|
110
|
+
fail "tool_execution_start 与 tool_execution_end 配对" "$start_count start vs $end_count end"
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
cleanup_rpc
|
|
114
|
+
summary
|
|
115
|
+
exit $?
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 06-pi-new-features.sh
|
|
3
|
+
# 验证:pi 0.85+ 新功能在 aicode 端可用
|
|
4
|
+
#
|
|
5
|
+
# 重点:
|
|
6
|
+
# - clear_queue RPC(0.84.4+ 新增)
|
|
7
|
+
# - GPT-6 Astra 是否出现在 available_models
|
|
8
|
+
# - thinking 持久化(set_thinking_level 后 turn 间不丢)
|
|
9
|
+
# - usage.cacheWrite1h 字段(0.85+ 新增)
|
|
10
|
+
# - 持久化 Claude thinking effort(0.85+)
|
|
11
|
+
# - /thinking 命令选择器(0.84.3+)
|
|
12
|
+
|
|
13
|
+
set -uo pipefail
|
|
14
|
+
CURRENT_CASE="06-pi-new-features"
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
source "$SCRIPT_DIR/../lib/common.sh"
|
|
17
|
+
|
|
18
|
+
header "$CURRENT_CASE" "pi 0.85+ new features"
|
|
19
|
+
|
|
20
|
+
if ! spawn_rpc 90; then
|
|
21
|
+
fail "spawn aicode RPC" "进程启动失败"
|
|
22
|
+
summary
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
pass "spawn aicode RPC (pid=$REGRESSION_PID)"
|
|
26
|
+
|
|
27
|
+
# ----- Test 1: clear_queue RPC -----
|
|
28
|
+
header "$CURRENT_CASE" "clear_queue RPC (0.84.4+)"
|
|
29
|
+
|
|
30
|
+
send '{"type":"clear_queue","id":"n1"}'
|
|
31
|
+
sleep 0.5
|
|
32
|
+
|
|
33
|
+
if jq -e 'select(.id=="n1" and .type=="response" and .success==true)' "$REGRESSION_LOG" >/dev/null 2>&1; then
|
|
34
|
+
pass "clear_queue RPC 成功"
|
|
35
|
+
|
|
36
|
+
# 验证返回结构
|
|
37
|
+
if jq -e 'select(.id=="n1" and .type=="response") | .data | has("steering") and has("followUp")' "$REGRESSION_LOG" >/dev/null 2>&1; then
|
|
38
|
+
pass "clear_queue 返回 {steering,followUp} 字段"
|
|
39
|
+
else
|
|
40
|
+
skip "clear_queue 返回 {steering,followUp} 字段" "字段名可能不同(followUps/follow_ups)"
|
|
41
|
+
fi
|
|
42
|
+
else
|
|
43
|
+
err=$(jq -c 'select(.id=="n1" and .type=="response") | .error // ""' "$REGRESSION_LOG" 2>/dev/null | head -1)
|
|
44
|
+
if [[ -n "$err" && "$err" != '""' ]]; then
|
|
45
|
+
skip "clear_queue RPC" "pi < 0.84.4 不支持: $err"
|
|
46
|
+
else
|
|
47
|
+
fail "clear_queue RPC" "无 response"
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# ----- Test 2: GPT-6 Astra 在 available_models -----
|
|
52
|
+
header "$CURRENT_CASE" "GPT-6 Astra (0.85.0+)"
|
|
53
|
+
|
|
54
|
+
send '{"type":"get_available_models","id":"n2"}'
|
|
55
|
+
sleep 1
|
|
56
|
+
|
|
57
|
+
models=$(jq -c 'select(.id=="n2" and .type=="response") | .data' "$REGRESSION_LOG" 2>/dev/null | head -1)
|
|
58
|
+
if [[ -n "$models" ]]; then
|
|
59
|
+
if echo "$models" | jq -e '.[] | select(.id | tostring | contains("gpt-6"))' >/dev/null 2>&1; then
|
|
60
|
+
pass "GPT-6 系列模型在 catalog 中"
|
|
61
|
+
gpt6=$(echo "$models" | jq -r '.[] | select(.id | tostring | contains("gpt-6")) | .id' 2>/dev/null | head -3)
|
|
62
|
+
echo " 发现: $(echo "$gpt6" | tr '\n' ' ')"
|
|
63
|
+
else
|
|
64
|
+
skip "GPT-6 Astra 在 catalog 中" "未找到 gpt-6* 模型(取决于 provider 配置)"
|
|
65
|
+
fi
|
|
66
|
+
else
|
|
67
|
+
fail "get_available_models 返回 data" "无响应"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# ----- Test 3: set_thinking_level + 持久化(0.85.0)-----
|
|
71
|
+
header "$CURRENT_CASE" "thinking level persistence (0.85.0)"
|
|
72
|
+
|
|
73
|
+
# 设置 high
|
|
74
|
+
send '{"type":"set_thinking_level","level":"high","id":"n3a"}'
|
|
75
|
+
sleep 0.5
|
|
76
|
+
if jq -e 'select(.id=="n3a" and .type=="response" and .success==true)' "$REGRESSION_LOG" >/dev/null 2>&1; then
|
|
77
|
+
pass "set_thinking_level high 成功"
|
|
78
|
+
else
|
|
79
|
+
fail "set_thinking_level high" "无响应"
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# 再发一个 prompt 让它跑
|
|
83
|
+
send '{"type":"prompt","message":"say hi","id":"n3b"}'
|
|
84
|
+
sleep 8
|
|
85
|
+
|
|
86
|
+
# 等 prompt 完成
|
|
87
|
+
waited=0
|
|
88
|
+
while [[ $waited -lt 60 ]]; do
|
|
89
|
+
if jq -e 'select(.id=="n3b" and .type=="response")' "$REGRESSION_LOG" >/dev/null 2>&1; then
|
|
90
|
+
break
|
|
91
|
+
fi
|
|
92
|
+
sleep 1
|
|
93
|
+
waited=$((waited + 1))
|
|
94
|
+
done
|
|
95
|
+
|
|
96
|
+
# 查询 get_state — thinking level 应该还是 high
|
|
97
|
+
send '{"type":"get_state","id":"n3c"}'
|
|
98
|
+
sleep 0.5
|
|
99
|
+
|
|
100
|
+
state_level=$(jq -c 'select(.id=="n3c" and .type=="response") | .data.thinkingLevel // "missing"' "$REGRESSION_LOG" 2>/dev/null | head -1)
|
|
101
|
+
if [[ "$state_level" == '"high"' ]]; then
|
|
102
|
+
pass "thinking level 在 turn 间保持 high(持久化)"
|
|
103
|
+
else
|
|
104
|
+
if [[ "$state_level" == '"medium"' ]]; then
|
|
105
|
+
skip "thinking level 持久化" "回到 medium(0.85 之前行为)"
|
|
106
|
+
else
|
|
107
|
+
fail "thinking level 持久化" "got: $state_level"
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# ----- Test 4: usage.cacheWrite1h 字段(0.85.0+)-----
|
|
112
|
+
header "$CURRENT_CASE" "usage.cacheWrite1h field (0.85.0+)"
|
|
113
|
+
|
|
114
|
+
# 看 turn_end 事件的 usage 是否包含 cacheWrite1h
|
|
115
|
+
has_cw1h=$(jq -c 'select(.type=="turn_end") | .message.usage | has("cacheWrite1h")' "$REGRESSION_LOG" 2>/dev/null | head -1)
|
|
116
|
+
if [[ "$has_cw1h" == "true" ]]; then
|
|
117
|
+
pass "turn_end.usage.cacheWrite1h 字段存在"
|
|
118
|
+
else
|
|
119
|
+
skip "turn_end.usage.cacheWrite1h 字段" "不存在(pi < 0.85.0 或 provider 不报告)"
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# ----- Test 5: get_available_thinking_levels -----
|
|
123
|
+
header "$CURRENT_CASE" "thinking levels list"
|
|
124
|
+
|
|
125
|
+
send '{"type":"get_available_thinking_levels","id":"n5"}'
|
|
126
|
+
sleep 0.5
|
|
127
|
+
|
|
128
|
+
if jq -e 'select(.id=="n5" and .type=="response" and .success==true)' "$REGRESSION_LOG" >/dev/null 2>&1; then
|
|
129
|
+
levels=$(jq -c 'select(.id=="n5" and .type=="response") | .data | length' "$REGRESSION_LOG" 2>/dev/null | head -1)
|
|
130
|
+
if [[ -n "$levels" && "$levels" -gt 0 ]]; then
|
|
131
|
+
pass "get_available_thinking_levels 返回 $levels 个级别"
|
|
132
|
+
else
|
|
133
|
+
fail "thinking levels 非空" "got: $levels"
|
|
134
|
+
fi
|
|
135
|
+
else
|
|
136
|
+
fail "get_available_thinking_levels" "无响应"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
cleanup_rpc
|
|
140
|
+
summary
|
|
141
|
+
exit $?
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# common.sh - 回归测试公共工具
|
|
3
|
+
# 用法:在 case 脚本开头 source "$(dirname "${BASH_SOURCE[0]}")/lib/common.sh"
|
|
4
|
+
|
|
5
|
+
# 严格模式:未定义变量报错,但允许管道部分失败(grep 找不到时 exit 1)
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
# 默认配置
|
|
9
|
+
: "${AICODE_BIN:=aicode}" # aicode 二进制路径
|
|
10
|
+
: "${AICODE_CONFIG:=$HOME/.aicode-cli}" # 配置目录
|
|
11
|
+
: "${AICODE_TIMEOUT:=30}" # RPC 测试默认超时(秒)
|
|
12
|
+
: "${REGRESSION_QUIET:=0}" # 1 = 减少输出
|
|
13
|
+
|
|
14
|
+
# 路径
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
16
|
+
RESULTS_DIR="$SCRIPT_DIR/results"
|
|
17
|
+
mkdir -p "$RESULTS_DIR"
|
|
18
|
+
|
|
19
|
+
# 颜色(仅 TTY 启用)
|
|
20
|
+
if [[ -t 1 ]] && [[ "${REGRESSION_QUIET:-0}" != "1" ]]; then
|
|
21
|
+
RED=$'\033[0;31m'; GREEN=$'\033[0;32m'; YELLOW=$'\033[0;33m'; BLUE=$'\033[0;34m'; CYAN=$'\033[0;36m'; DIM=$'\033[2m'; NC=$'\033[0m'
|
|
22
|
+
else
|
|
23
|
+
RED=''; GREEN=''; YELLOW=''; BLUE=''; CYAN=''; DIM=''; NC=''
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# 计数器
|
|
27
|
+
PASS_COUNT=0
|
|
28
|
+
FAIL_COUNT=0
|
|
29
|
+
SKIP_COUNT=0
|
|
30
|
+
CURRENT_CASE="${CURRENT_CASE:-$(basename "${BASH_SOURCE[1]:-unknown}" .sh)}"
|
|
31
|
+
|
|
32
|
+
# ----- 输出 -----
|
|
33
|
+
header() {
|
|
34
|
+
[[ "${REGRESSION_QUIET:-0}" == "1" ]] && return
|
|
35
|
+
echo ""
|
|
36
|
+
echo "${CYAN}─── $CURRENT_CASE: $2 ───${NC}"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
pass() {
|
|
40
|
+
local name="$1"
|
|
41
|
+
echo " ${GREEN}✓${NC} $name"
|
|
42
|
+
PASS_COUNT=$((PASS_COUNT + 1))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fail() {
|
|
46
|
+
local name="$1"
|
|
47
|
+
local reason="${2:-no reason}"
|
|
48
|
+
echo " ${RED}✗${NC} $name"
|
|
49
|
+
echo " ${DIM}└─ $reason${NC}"
|
|
50
|
+
FAIL_COUNT=$((FAIL_COUNT + 1))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
skip() {
|
|
54
|
+
local name="$1"
|
|
55
|
+
local reason="${2:-no reason}"
|
|
56
|
+
echo " ${YELLOW}○${NC} $name ${DIM}(skipped: $reason)${NC}"
|
|
57
|
+
SKIP_COUNT=$((SKIP_COUNT + 1))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
summary() {
|
|
61
|
+
local exit_code=0
|
|
62
|
+
echo ""
|
|
63
|
+
if [[ $FAIL_COUNT -eq 0 && $SKIP_COUNT -eq 0 ]]; then
|
|
64
|
+
echo "${GREEN}✓ $CURRENT_CASE: ALL PASS${NC} ${DIM}($PASS_COUNT checks)${NC}"
|
|
65
|
+
elif [[ $FAIL_COUNT -eq 0 ]]; then
|
|
66
|
+
echo "${YELLOW}△ $CURRENT_CASE: PASS with skips${NC} ${DIM}($PASS_COUNT pass, $SKIP_COUNT skip)${NC}"
|
|
67
|
+
else
|
|
68
|
+
echo "${RED}✗ $CURRENT_CASE: FAILED${NC} ${DIM}($FAIL_COUNT fail, $PASS_COUNT pass, $SKIP_COUNT skip)${NC}"
|
|
69
|
+
exit_code=1
|
|
70
|
+
fi
|
|
71
|
+
return $exit_code
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# ----- RPC 进程管理 -----
|
|
75
|
+
#
|
|
76
|
+
# spawn_rpc [timeout_seconds]
|
|
77
|
+
# 启动 aicode -ne --mode rpc --no-session,stdin 从 fd 11 读,stdout/stderr 到 fd 12 写
|
|
78
|
+
# 用法见 cases/02-rpc-handshake.sh
|
|
79
|
+
#
|
|
80
|
+
spawn_rpc() {
|
|
81
|
+
local timeout="${1:-$AICODE_TIMEOUT}"
|
|
82
|
+
local skip_extensions="${2:-true}"
|
|
83
|
+
|
|
84
|
+
if ! command -v "$AICODE_BIN" >/dev/null 2>&1; then
|
|
85
|
+
echo "ERROR: $AICODE_BIN not found. Set AICODE_BIN env var to override." >&2
|
|
86
|
+
return 127
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# 临时配置必须在 $HOME 下,aicode-cli 的 config-loader.ts 安全检查
|
|
90
|
+
# 会拒绝非 home 目录并回退到 ~/.aicode-cli
|
|
91
|
+
# 用 home 下的隐藏目录避免污染用户配置
|
|
92
|
+
local workdir="$HOME/.aicode-regression-$$-$(date +%s%N | tail -c 8)"
|
|
93
|
+
mkdir -p "$workdir/agent/extensions"
|
|
94
|
+
local cfg="$workdir/config.json"
|
|
95
|
+
cat >"$cfg" <<EOF
|
|
96
|
+
{
|
|
97
|
+
"name": "aicode-regression",
|
|
98
|
+
"banner": "",
|
|
99
|
+
"themeColor": "#49bccf",
|
|
100
|
+
"configDir": "$(basename "$workdir")"
|
|
101
|
+
}
|
|
102
|
+
EOF
|
|
103
|
+
|
|
104
|
+
# 复用用户全局的 auth.json / models.json / settings.json + prompts / skills / extensions
|
|
105
|
+
# (避免重新登录/配置,且能让 pi 加载预置 prompts 和 skills)
|
|
106
|
+
# ~/.pi 通常是 ~/.aicode-cli 的 symlink
|
|
107
|
+
local user_agent_dir="$HOME/.aicode-cli/agent"
|
|
108
|
+
if [[ -d "$user_agent_dir" ]]; then
|
|
109
|
+
for f in auth.json models.json settings.json; do
|
|
110
|
+
[[ -f "$user_agent_dir/$f" ]] && cp "$user_agent_dir/$f" "$workdir/agent/$f" 2>/dev/null
|
|
111
|
+
done
|
|
112
|
+
# 复制 prompts/skills/extensions 目录(不覆盖本地)
|
|
113
|
+
for d in prompts skills extensions themes; do
|
|
114
|
+
if [[ -d "$user_agent_dir/$d" ]]; then
|
|
115
|
+
cp -R "$user_agent_dir/$d" "$workdir/agent/" 2>/dev/null
|
|
116
|
+
fi
|
|
117
|
+
done
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# 覆盖 provider(如果设置了 AICODE_REGRESSION_PROVIDER_*)
|
|
121
|
+
# 用于跨供应商测试:minimax / siliconflow-anthropic / siliconflow-openai 等
|
|
122
|
+
if [[ -n "${AICODE_REGRESSION_BASE_URL:-}" ]] && [[ -n "${AICODE_REGRESSION_API_KEY:-}" ]]; then
|
|
123
|
+
# 在 models.json 里加一个 anthropic 兼容 provider
|
|
124
|
+
local api_kind="${AICODE_REGRESSION_API_KIND:-anthropic-messages}"
|
|
125
|
+
local provider_id="${AICODE_REGRESSION_PROVIDER_ID:-regression}"
|
|
126
|
+
local model_id="${AICODE_REGRESSION_MODEL_ID:-claude-sonnet-4-5}"
|
|
127
|
+
python3 -c "
|
|
128
|
+
import json
|
|
129
|
+
p = '$workdir/agent/models.json'
|
|
130
|
+
try:
|
|
131
|
+
d = json.load(open(p))
|
|
132
|
+
except: d = {'providers': {}}
|
|
133
|
+
prov = d.setdefault('providers', {})
|
|
134
|
+
prov['$provider_id'] = {
|
|
135
|
+
'baseUrl': '$AICODE_REGRESSION_BASE_URL',
|
|
136
|
+
'apiKey': '$AICODE_REGRESSION_API_KEY',
|
|
137
|
+
'api': '$api_kind',
|
|
138
|
+
'models': [
|
|
139
|
+
{'id': '$model_id', 'name': '$model_id', 'contextWindow': 128000, 'maxTokens': 16384}
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
json.dump(d, open(p, 'w'), indent=2)
|
|
143
|
+
" 2>/dev/null
|
|
144
|
+
|
|
145
|
+
# 修改 settings.json 的 defaultProvider
|
|
146
|
+
[[ -f "$workdir/agent/settings.json" ]] && python3 -c "
|
|
147
|
+
import json
|
|
148
|
+
p = '$workdir/agent/settings.json'
|
|
149
|
+
try:
|
|
150
|
+
d = json.load(open(p))
|
|
151
|
+
except: d = {}
|
|
152
|
+
d['defaultProvider'] = '$provider_id'
|
|
153
|
+
d['defaultModel'] = '$model_id'
|
|
154
|
+
json.dump(d, open(p, 'w'), indent=2)
|
|
155
|
+
" 2>/dev/null
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
# 创建 stdin fifo(aicode 需要从 stdin 持续读取 RPC 命令)
|
|
159
|
+
mkfifo "$workdir/stdin.fifo" 2>/dev/null || true
|
|
160
|
+
|
|
161
|
+
# fd 11 = stdin (case 脚本可以 printf 到 &11)
|
|
162
|
+
# fd 12 = stdout+stderr 合并的 log(用于后续分析)
|
|
163
|
+
exec 11<>"$workdir/stdin.fifo"
|
|
164
|
+
exec 12>"$workdir/output.log"
|
|
165
|
+
|
|
166
|
+
# spawn 进程(默认 -ne 跳过扩展避免 conflict;要加载扩展传 false)
|
|
167
|
+
local ext_flag=""
|
|
168
|
+
if [[ "$skip_extensions" == "true" ]]; then
|
|
169
|
+
ext_flag="-ne"
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
"$AICODE_BIN" $ext_flag --config "$workdir" --mode rpc --no-session \
|
|
173
|
+
<&11 >&12 2>&1 &
|
|
174
|
+
local pid=$!
|
|
175
|
+
|
|
176
|
+
echo "$workdir" > "$RESULTS_DIR/.last_workdir"
|
|
177
|
+
echo "$pid" > "$RESULTS_DIR/.last_pid"
|
|
178
|
+
|
|
179
|
+
# 主动发一个 get_state 触发首个 response,验证 RPC 通道已就绪
|
|
180
|
+
printf '{"type":"get_state","id":"ready_probe"}\n' >&11
|
|
181
|
+
|
|
182
|
+
# 等待 response 出现(最多 5 秒)
|
|
183
|
+
local ready=0
|
|
184
|
+
for _ in $(seq 1 50); do
|
|
185
|
+
if grep -q '"id":"ready_probe"' "$workdir/output.log" 2>/dev/null; then
|
|
186
|
+
ready=1; break
|
|
187
|
+
fi
|
|
188
|
+
sleep 0.1
|
|
189
|
+
done
|
|
190
|
+
|
|
191
|
+
if [[ $ready -ne 1 ]]; then
|
|
192
|
+
kill "$pid" 2>/dev/null
|
|
193
|
+
echo "ERROR: aicode RPC 启动超时(未收到 ready_probe 响应)" >&2
|
|
194
|
+
echo " log 前 20 行:$(head -20 "$workdir/output.log" 2>/dev/null | tr '\n' '|' | head -c 300)" >&2
|
|
195
|
+
return 1
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
REGRESSION_PID="$pid"
|
|
199
|
+
REGRESSION_WORKDIR="$workdir"
|
|
200
|
+
REGRESSION_LOG="$workdir/output.log"
|
|
201
|
+
return 0
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
send() {
|
|
205
|
+
# send <json>
|
|
206
|
+
printf '%s\n' "$1" >&11
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
wait_for() {
|
|
210
|
+
# wait_for <jq-filter> [timeout_seconds]
|
|
211
|
+
local filter="$1"
|
|
212
|
+
local timeout="${2:-$AICODE_TIMEOUT}"
|
|
213
|
+
local jqbin="${JQBIN:-jq}"
|
|
214
|
+
|
|
215
|
+
if ! command -v "$jqbin" >/dev/null 2>&1; then
|
|
216
|
+
echo "ERROR: jq not found. brew install jq" >&2
|
|
217
|
+
return 127
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
# 等到匹配事件出现
|
|
221
|
+
"$jqbin" -c "$filter" "$REGRESSION_LOG" 2>/dev/null | head -1
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
cleanup_rpc() {
|
|
225
|
+
# 关掉 stdin pipe + 杀进程 + 保留 log
|
|
226
|
+
[[ -n "${REGRESSION_PID:-}" ]] && kill "$REGRESSION_PID" 2>/dev/null
|
|
227
|
+
exec 11<&- 2>/dev/null
|
|
228
|
+
exec 12<&- 2>/dev/null
|
|
229
|
+
|
|
230
|
+
if [[ -n "${REGRESSION_LOG:-}" ]] && [[ -f "$REGRESSION_LOG" ]]; then
|
|
231
|
+
cp "$REGRESSION_LOG" "$RESULTS_DIR/${CURRENT_CASE}.log" 2>/dev/null
|
|
232
|
+
fi
|
|
233
|
+
[[ -n "${REGRESSION_WORKDIR:-}" ]] && rm -rf "$REGRESSION_WORKDIR" 2>/dev/null
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
trap_cleanup() {
|
|
237
|
+
cleanup_rpc
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
# ----- 断言辅助 -----
|
|
241
|
+
|
|
242
|
+
assert_eq() {
|
|
243
|
+
# assert_eq <name> <expected> <actual>
|
|
244
|
+
local name="$1" expected="$2" actual="$3"
|
|
245
|
+
if [[ "$expected" == "$actual" ]]; then
|
|
246
|
+
pass "$name"
|
|
247
|
+
else
|
|
248
|
+
fail "$name" "expected: $expected | got: $actual"
|
|
249
|
+
fi
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
assert_contains() {
|
|
253
|
+
# assert_contains <name> <substring> <haystack>
|
|
254
|
+
local name="$1" needle="$2" hay="$3"
|
|
255
|
+
if [[ "$hay" == *"$needle"* ]]; then
|
|
256
|
+
pass "$name"
|
|
257
|
+
else
|
|
258
|
+
fail "$name" "expected to contain: $needle | got: ${hay:0:200}"
|
|
259
|
+
fi
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
assert_event_field() {
|
|
263
|
+
# assert_event_field <name> <jq-filter> <field-equals-value>
|
|
264
|
+
# 例:assert_event_field "model 是 anthropic" 'select(.type=="message_start")' '.message.api' "anthropic-messages"
|
|
265
|
+
local name="$1" event_filter="$2" field="$3" expected="$4"
|
|
266
|
+
local jqbin="${JQBIN:-jq}"
|
|
267
|
+
local actual
|
|
268
|
+
actual=$("$jqbin" -c "$event_filter | $field // \"\"" "$REGRESSION_LOG" 2>/dev/null | head -1)
|
|
269
|
+
if [[ "$actual" == *"\"$expected\""* ]] || [[ "$actual" == "\"$expected\"" ]]; then
|
|
270
|
+
pass "$name"
|
|
271
|
+
else
|
|
272
|
+
fail "$name" "expected $field == '$expected' | got: $actual"
|
|
273
|
+
fi
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
assert_event_count() {
|
|
277
|
+
# assert_event_count <name> <jq-filter> <min-count>
|
|
278
|
+
local name="$1" filter="$2" min="$3"
|
|
279
|
+
local jqbin="${JQBIN:-jq}"
|
|
280
|
+
local count
|
|
281
|
+
count=$("$jqbin" -c "$filter" "$REGRESSION_LOG" 2>/dev/null | wc -l | tr -d ' ')
|
|
282
|
+
if [[ $count -ge $min ]]; then
|
|
283
|
+
pass "$name ($count ≥ $min)"
|
|
284
|
+
else
|
|
285
|
+
fail "$name" "expected ≥ $min matches | got: $count"
|
|
286
|
+
fi
|
|
287
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
41096
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/Users/jianjiang/.aicode-regression-41074-9749000
|