scene-capability-engine 3.6.61 → 3.6.62
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/CHANGELOG.md +5 -0
- package/README.md +3 -3
- package/README.zh.md +3 -3
- package/docs/command-reference.md +0 -3
- package/docs/magicball-task-feedback-timeline-guide.md +0 -1
- package/docs/releases/README.md +1 -0
- package/docs/releases/v3.6.62.md +32 -0
- package/docs/zh/releases/README.md +1 -0
- package/docs/zh/releases/v3.6.62.md +32 -0
- package/lib/commands/studio.js +1 -319
- package/package.json +1 -1
- package/template/.sce/README.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.6.62] - 2026-03-21
|
|
11
|
+
|
|
12
|
+
### Removed
|
|
13
|
+
- Removed the OpenHands-specific `sce studio events --openhands-events <path>` bridge so current SCE no longer exposes a vendor-specific event-ingestion path in the active CLI surface.
|
|
14
|
+
|
|
10
15
|
## [3.6.61] - 2026-03-20
|
|
11
16
|
|
|
12
17
|
### Added
|
package/README.md
CHANGED
|
@@ -155,7 +155,7 @@ SCE is opinionated by default.
|
|
|
155
155
|
For IDEs, AI shells, or custom frontends, the most important SCE surfaces are:
|
|
156
156
|
|
|
157
157
|
- `sce studio plan|generate|apply|verify|release`
|
|
158
|
-
- `sce studio events
|
|
158
|
+
- `sce studio events`
|
|
159
159
|
- `sce task ref|show|rerun`
|
|
160
160
|
- `sce timeline save|list|show|restore`
|
|
161
161
|
- `sce capability inventory`
|
|
@@ -230,5 +230,5 @@ MIT. See [LICENSE](LICENSE).
|
|
|
230
230
|
|
|
231
231
|
---
|
|
232
232
|
|
|
233
|
-
**Version**: 3.6.
|
|
234
|
-
**Last Updated**: 2026-03-
|
|
233
|
+
**Version**: 3.6.62
|
|
234
|
+
**Last Updated**: 2026-03-21
|
package/README.zh.md
CHANGED
|
@@ -160,7 +160,7 @@ SCE 默认是强治理的。
|
|
|
160
160
|
如果你要对接 IDE、AI 助手或前端,优先关注这些接口面:
|
|
161
161
|
|
|
162
162
|
- `sce studio plan|generate|apply|verify|release`
|
|
163
|
-
- `sce studio events
|
|
163
|
+
- `sce studio events`
|
|
164
164
|
- `sce task ref|show|rerun`
|
|
165
165
|
- `sce timeline save|list|show|restore`
|
|
166
166
|
- `sce capability inventory`
|
|
@@ -235,5 +235,5 @@ MIT,见 [LICENSE](LICENSE)。
|
|
|
235
235
|
|
|
236
236
|
---
|
|
237
237
|
|
|
238
|
-
**版本**:3.6.
|
|
239
|
-
**最后更新**:2026-03-
|
|
238
|
+
**版本**:3.6.62
|
|
239
|
+
**最后更新**:2026-03-21
|
|
@@ -744,8 +744,6 @@ sce studio resume --job <job-id> --json
|
|
|
744
744
|
|
|
745
745
|
# Inspect recent stage events
|
|
746
746
|
sce studio events --job <job-id> --limit 50 --json
|
|
747
|
-
# Map OpenHands raw events into the same task-stream contract
|
|
748
|
-
sce studio events --job <job-id> --openhands-events ./openhands-events.json --json
|
|
749
747
|
|
|
750
748
|
# Rollback a job after apply/release
|
|
751
749
|
sce studio rollback --job <job-id> --reason "manual-check-failed" --json
|
|
@@ -779,7 +777,6 @@ Studio JSON output now includes a stable UI-oriented task stream contract (in ad
|
|
|
779
777
|
- `task.evidence[]`: structured evidence references
|
|
780
778
|
- `task.feedback_model`: human-facing task feedback (`problem`, `execution`, `diagnosis`, `evidence`, `next_step`, `mb_status`)
|
|
781
779
|
- `event[]`: raw audit event stream (`studio events` also keeps legacy `events[]` for compatibility)
|
|
782
|
-
- `studio events --openhands-events <path>` switches `source_stream=openhands` and maps OpenHands raw events to the same task contract fields.
|
|
783
780
|
- hierarchical task reference lookup/rerun:
|
|
784
781
|
- `sce task show --ref <SS.PP.TT> --json`
|
|
785
782
|
- `sce task rerun --ref <SS.PP.TT> [--dry-run] --json`
|
|
@@ -117,7 +117,6 @@ SCE 现在在时间线命令中增加:
|
|
|
117
117
|
## 4. 最小接口清单
|
|
118
118
|
|
|
119
119
|
- `sce studio events --job <job-id> --json`
|
|
120
|
-
- `sce studio events --job <job-id> --openhands-events <path> --json`
|
|
121
120
|
- `sce timeline list --limit 20 --json`
|
|
122
121
|
- `sce timeline show <snapshot-id> --json`
|
|
123
122
|
|
package/docs/releases/README.md
CHANGED
|
@@ -9,6 +9,7 @@ This directory stores release-facing documents:
|
|
|
9
9
|
## Archived Versions
|
|
10
10
|
|
|
11
11
|
- [Release checklist](../release-checklist.md)
|
|
12
|
+
- [v3.6.62 release notes](./v3.6.62.md)
|
|
12
13
|
- [v3.6.61 release notes](./v3.6.61.md)
|
|
13
14
|
- [v3.6.60 release notes](./v3.6.60.md)
|
|
14
15
|
- [v3.6.59 release notes](./v3.6.59.md)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# v3.6.62 Release Notes
|
|
2
|
+
|
|
3
|
+
Release date: 2026-03-21
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
|
|
7
|
+
- Removed the OpenHands-specific `sce studio events --openhands-events <path>` bridge from the tracked CLI surface so SCE no longer publishes a vendor-specific event-ingestion path as a first-class runtime contract.
|
|
8
|
+
- Removed the corresponding OpenHands mapping test coverage and cleaned the main command reference plus README surfaces to keep shipped documentation aligned with the active product boundary.
|
|
9
|
+
- Kept the broader external-runtime direction vendor-neutral so future IDE embedding can focus on Codex CLI, Claude Code, and similar in-process tool endpoints without carrying a stale OpenHands-first entry point.
|
|
10
|
+
|
|
11
|
+
## Validation
|
|
12
|
+
|
|
13
|
+
- `npx jest tests/unit/commands/studio.test.js --runInBand`
|
|
14
|
+
- `npm run test:release`
|
|
15
|
+
- `npm run audit:release-docs`
|
|
16
|
+
- `npm run audit:steering`
|
|
17
|
+
- `npm run test:skip-audit`
|
|
18
|
+
- `npm run test:sce-tracking`
|
|
19
|
+
- `npm run gate:npm-runtime-assets`
|
|
20
|
+
- `npm run test:brand-consistency`
|
|
21
|
+
- `npm run audit:clarification-first`
|
|
22
|
+
- `npm run audit:magicball-engineering-contract`
|
|
23
|
+
- `npm run audit:magicball-project-contract`
|
|
24
|
+
- `npm run gate:collab-governance`
|
|
25
|
+
- `npm run gate:errorbook-registry-health`
|
|
26
|
+
- `npm run gate:errorbook-release`
|
|
27
|
+
- `npm pack --dry-run`
|
|
28
|
+
|
|
29
|
+
## Release Notes
|
|
30
|
+
|
|
31
|
+
- Use `v3.6.62` when you need the published SCE package to exclude the old OpenHands-specific `studio events` bridge from the active CLI and doc surface.
|
|
32
|
+
- This patch intentionally does not rewrite user-local draft Specs; it only cleans the tracked engine, tests, and release-facing documentation.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# v3.6.62 发布说明
|
|
2
|
+
|
|
3
|
+
发布日期:2026-03-21
|
|
4
|
+
|
|
5
|
+
## 重点变化
|
|
6
|
+
|
|
7
|
+
- 从已跟踪 CLI 正式接口面移除了 OpenHands 专有的 `sce studio events --openhands-events <path>` 桥接入口,避免 SCE 继续把某个供应商专有事件摄取路径作为一等运行时契约发布。
|
|
8
|
+
- 同步移除了对应的 OpenHands 映射测试,并清理了主命令参考与 README 中的相关说明,保证随包发布的文档与当前正式产品边界一致。
|
|
9
|
+
- 外部 runtime 方向继续保持 vendor-neutral,为后续 IDE 内嵌 Codex CLI、Claude Code 等执行端预留稳定空间,而不是继续背着过时的 OpenHands-first 入口。
|
|
10
|
+
|
|
11
|
+
## 验证
|
|
12
|
+
|
|
13
|
+
- `npx jest tests/unit/commands/studio.test.js --runInBand`
|
|
14
|
+
- `npm run test:release`
|
|
15
|
+
- `npm run audit:release-docs`
|
|
16
|
+
- `npm run audit:steering`
|
|
17
|
+
- `npm run test:skip-audit`
|
|
18
|
+
- `npm run test:sce-tracking`
|
|
19
|
+
- `npm run gate:npm-runtime-assets`
|
|
20
|
+
- `npm run test:brand-consistency`
|
|
21
|
+
- `npm run audit:clarification-first`
|
|
22
|
+
- `npm run audit:magicball-engineering-contract`
|
|
23
|
+
- `npm run audit:magicball-project-contract`
|
|
24
|
+
- `npm run gate:collab-governance`
|
|
25
|
+
- `npm run gate:errorbook-registry-health`
|
|
26
|
+
- `npm run gate:errorbook-release`
|
|
27
|
+
- `npm pack --dry-run`
|
|
28
|
+
|
|
29
|
+
## 发布说明
|
|
30
|
+
|
|
31
|
+
- 当你需要已发布的 SCE 包不再暴露旧的 OpenHands 专有 `studio events` 桥接入口时,请使用 `v3.6.62`。
|
|
32
|
+
- 本次补丁刻意不去重写用户本地未纳管的草稿 spec,只清理已跟踪的 engine、测试与发布文档面。
|
package/lib/commands/studio.js
CHANGED
|
@@ -921,273 +921,6 @@ function extractEventArrayFromPayload(payload) {
|
|
|
921
921
|
return [payload];
|
|
922
922
|
}
|
|
923
923
|
|
|
924
|
-
async function readOpenHandsEventsFile(openhandsEventsPath, fileSystem = fs) {
|
|
925
|
-
const normalizedPath = normalizeString(openhandsEventsPath);
|
|
926
|
-
if (!normalizedPath) {
|
|
927
|
-
return [];
|
|
928
|
-
}
|
|
929
|
-
const exists = await fileSystem.pathExists(normalizedPath);
|
|
930
|
-
if (!exists) {
|
|
931
|
-
throw new Error(`OpenHands events file not found: ${normalizedPath}`);
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
const content = await fileSystem.readFile(normalizedPath, 'utf8');
|
|
935
|
-
const trimmed = `${content || ''}`.trim();
|
|
936
|
-
if (!trimmed) {
|
|
937
|
-
return [];
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
try {
|
|
941
|
-
const parsed = JSON.parse(trimmed);
|
|
942
|
-
return extractEventArrayFromPayload(parsed);
|
|
943
|
-
} catch (_error) {
|
|
944
|
-
const lines = trimmed
|
|
945
|
-
.split(/\r?\n/)
|
|
946
|
-
.map((line) => line.trim())
|
|
947
|
-
.filter(Boolean);
|
|
948
|
-
const parsedLines = [];
|
|
949
|
-
for (const line of lines) {
|
|
950
|
-
try {
|
|
951
|
-
parsedLines.push(JSON.parse(line));
|
|
952
|
-
} catch (_innerError) {
|
|
953
|
-
// Skip malformed JSONL lines to keep ingestion robust.
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
return parsedLines;
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
function normalizeOpenHandsEventRecord(rawEvent, index, jobId) {
|
|
961
|
-
const raw = rawEvent && typeof rawEvent === 'object'
|
|
962
|
-
? rawEvent
|
|
963
|
-
: { value: rawEvent };
|
|
964
|
-
const eventId = pickFirstString([
|
|
965
|
-
raw.event_id,
|
|
966
|
-
raw.eventId,
|
|
967
|
-
raw.id,
|
|
968
|
-
raw.uuid
|
|
969
|
-
]) || `oh-evt-${index + 1}`;
|
|
970
|
-
const eventType = pickFirstString([
|
|
971
|
-
raw.event_type,
|
|
972
|
-
raw.eventType,
|
|
973
|
-
raw.type,
|
|
974
|
-
raw.kind,
|
|
975
|
-
raw.action
|
|
976
|
-
]) || 'unknown';
|
|
977
|
-
const timestamp = pickFirstString([
|
|
978
|
-
raw.timestamp,
|
|
979
|
-
raw.time,
|
|
980
|
-
raw.created_at,
|
|
981
|
-
raw.createdAt,
|
|
982
|
-
raw.ts
|
|
983
|
-
]) || nowIso();
|
|
984
|
-
return {
|
|
985
|
-
api_version: STUDIO_EVENT_API_VERSION,
|
|
986
|
-
event_id: eventId,
|
|
987
|
-
job_id: jobId,
|
|
988
|
-
event_type: `openhands.${eventType}`,
|
|
989
|
-
timestamp,
|
|
990
|
-
metadata: {
|
|
991
|
-
source: 'openhands',
|
|
992
|
-
raw
|
|
993
|
-
}
|
|
994
|
-
};
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
function tryParseJsonText(value) {
|
|
998
|
-
if (typeof value !== 'string') {
|
|
999
|
-
return null;
|
|
1000
|
-
}
|
|
1001
|
-
const trimmed = value.trim();
|
|
1002
|
-
if (!trimmed) {
|
|
1003
|
-
return null;
|
|
1004
|
-
}
|
|
1005
|
-
if (!(trimmed.startsWith('{') || trimmed.startsWith('['))) {
|
|
1006
|
-
return null;
|
|
1007
|
-
}
|
|
1008
|
-
try {
|
|
1009
|
-
return JSON.parse(trimmed);
|
|
1010
|
-
} catch (_error) {
|
|
1011
|
-
return null;
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
function stringifyCompact(value) {
|
|
1016
|
-
if (value == null) {
|
|
1017
|
-
return '';
|
|
1018
|
-
}
|
|
1019
|
-
if (typeof value === 'string') {
|
|
1020
|
-
return value;
|
|
1021
|
-
}
|
|
1022
|
-
try {
|
|
1023
|
-
return JSON.stringify(value);
|
|
1024
|
-
} catch (_error) {
|
|
1025
|
-
return `${value}`;
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
function extractOpenHandsCommandCandidates(raw = {}) {
|
|
1030
|
-
const command = pickFirstString([
|
|
1031
|
-
raw.command,
|
|
1032
|
-
raw.cmd,
|
|
1033
|
-
raw.input && raw.input.command,
|
|
1034
|
-
raw.action && raw.action.command,
|
|
1035
|
-
raw.observation && raw.observation.command,
|
|
1036
|
-
raw.tool_input && raw.tool_input.command
|
|
1037
|
-
]);
|
|
1038
|
-
|
|
1039
|
-
const toolName = pickFirstString([
|
|
1040
|
-
raw.tool_name,
|
|
1041
|
-
raw.toolName,
|
|
1042
|
-
raw.tool && raw.tool.name,
|
|
1043
|
-
raw.name
|
|
1044
|
-
]);
|
|
1045
|
-
const toolArgs = raw.arguments || raw.tool_input || raw.input || null;
|
|
1046
|
-
|
|
1047
|
-
const cmd = command || (toolName
|
|
1048
|
-
? `tool:${toolName}${toolArgs ? ` ${stringifyCompact(toolArgs)}` : ''}`
|
|
1049
|
-
: '');
|
|
1050
|
-
|
|
1051
|
-
return {
|
|
1052
|
-
cmd,
|
|
1053
|
-
exit_code: pickFirstNumber([
|
|
1054
|
-
raw.exit_code,
|
|
1055
|
-
raw.exitCode,
|
|
1056
|
-
raw.result && (raw.result.exit_code ?? raw.result.exitCode)
|
|
1057
|
-
]),
|
|
1058
|
-
stdout: pickFirstString([
|
|
1059
|
-
raw.stdout,
|
|
1060
|
-
raw.output,
|
|
1061
|
-
raw.result && raw.result.stdout,
|
|
1062
|
-
raw.observation && raw.observation.stdout
|
|
1063
|
-
]),
|
|
1064
|
-
stderr: pickFirstString([
|
|
1065
|
-
raw.stderr,
|
|
1066
|
-
raw.result && raw.result.stderr,
|
|
1067
|
-
raw.error && raw.error.message,
|
|
1068
|
-
typeof raw.error === 'string' ? raw.error : ''
|
|
1069
|
-
]),
|
|
1070
|
-
log_path: pickFirstString([
|
|
1071
|
-
raw.log_path,
|
|
1072
|
-
raw.logPath,
|
|
1073
|
-
raw.log && raw.log.path
|
|
1074
|
-
])
|
|
1075
|
-
};
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
function collectOpenHandsFileChangesFromRaw(raw = {}) {
|
|
1079
|
-
const changes = [];
|
|
1080
|
-
const addPath = (pathValue, lineValue, diffRefValue = '') => {
|
|
1081
|
-
const pathRef = normalizeString(pathValue);
|
|
1082
|
-
if (!pathRef) {
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
const line = Number.parseInt(`${lineValue != null ? lineValue : 1}`, 10);
|
|
1086
|
-
const normalizedLine = Number.isFinite(line) && line > 0 ? line : 1;
|
|
1087
|
-
const diffRef = normalizeString(diffRefValue) || `${pathRef}:${normalizedLine}`;
|
|
1088
|
-
changes.push({
|
|
1089
|
-
path: pathRef,
|
|
1090
|
-
line: normalizedLine,
|
|
1091
|
-
diffRef
|
|
1092
|
-
});
|
|
1093
|
-
};
|
|
1094
|
-
|
|
1095
|
-
if (typeof raw.path === 'string') {
|
|
1096
|
-
addPath(raw.path, raw.line || raw.line_number, raw.diff_ref);
|
|
1097
|
-
}
|
|
1098
|
-
if (raw.file && typeof raw.file === 'object') {
|
|
1099
|
-
addPath(raw.file.path || raw.file.name, raw.file.line || raw.file.line_number, raw.file.diffRef || raw.file.diff_ref);
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
const arrayKeys = ['files', 'changed_files', 'modified_files', 'created_files', 'deleted_files'];
|
|
1103
|
-
for (const key of arrayKeys) {
|
|
1104
|
-
const values = Array.isArray(raw[key]) ? raw[key] : [];
|
|
1105
|
-
for (const value of values) {
|
|
1106
|
-
if (typeof value === 'string') {
|
|
1107
|
-
addPath(value, 1);
|
|
1108
|
-
} else if (value && typeof value === 'object') {
|
|
1109
|
-
addPath(value.path || value.file || value.name, value.line || value.line_number, value.diffRef || value.diff_ref);
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
const patchText = pickFirstString([raw.patch, raw.diff]);
|
|
1115
|
-
if (patchText) {
|
|
1116
|
-
const parsedJson = tryParseJsonText(patchText);
|
|
1117
|
-
if (parsedJson && typeof parsedJson === 'object') {
|
|
1118
|
-
const nested = collectOpenHandsFileChangesFromRaw(parsedJson);
|
|
1119
|
-
changes.push(...nested);
|
|
1120
|
-
} else {
|
|
1121
|
-
const lines = patchText.split(/\r?\n/);
|
|
1122
|
-
for (const line of lines) {
|
|
1123
|
-
const match = line.match(/^\+\+\+\s+b\/(.+)$/);
|
|
1124
|
-
if (match && match[1]) {
|
|
1125
|
-
addPath(match[1], 1);
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
return changes;
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
function mapOpenHandsEventsToTaskSignals(openhandsEvents = [], context = {}) {
|
|
1135
|
-
const normalizedEvents = openhandsEvents.map((item, index) =>
|
|
1136
|
-
normalizeOpenHandsEventRecord(item, index, context.jobId)
|
|
1137
|
-
);
|
|
1138
|
-
|
|
1139
|
-
const commands = [];
|
|
1140
|
-
const fileChanges = [];
|
|
1141
|
-
const errors = [];
|
|
1142
|
-
const evidence = [];
|
|
1143
|
-
|
|
1144
|
-
for (const event of normalizedEvents) {
|
|
1145
|
-
const raw = event && event.metadata ? event.metadata.raw || {} : {};
|
|
1146
|
-
const commandCandidate = extractOpenHandsCommandCandidates(raw);
|
|
1147
|
-
if (commandCandidate.cmd || commandCandidate.stdout || commandCandidate.stderr || commandCandidate.exit_code != null) {
|
|
1148
|
-
commands.push(commandCandidate);
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
fileChanges.push(...collectOpenHandsFileChangesFromRaw(raw));
|
|
1152
|
-
|
|
1153
|
-
const eventType = normalizeString(event.event_type).toLowerCase();
|
|
1154
|
-
const failedByType = eventType.includes('error') || eventType.includes('fail');
|
|
1155
|
-
const failedByExit = commandCandidate.exit_code != null && Number(commandCandidate.exit_code) !== 0;
|
|
1156
|
-
const failedByStderr = commandCandidate.stderr.length > 0 && !commandCandidate.stdout;
|
|
1157
|
-
if (failedByType || failedByExit || failedByStderr) {
|
|
1158
|
-
const message = pickFirstString([
|
|
1159
|
-
raw.message,
|
|
1160
|
-
raw.error && raw.error.message,
|
|
1161
|
-
typeof raw.error === 'string' ? raw.error : '',
|
|
1162
|
-
failedByExit ? `OpenHands command failed (exit ${commandCandidate.exit_code})` : '',
|
|
1163
|
-
'OpenHands event indicates failure'
|
|
1164
|
-
]);
|
|
1165
|
-
errors.push({
|
|
1166
|
-
message,
|
|
1167
|
-
step_id: event.event_type,
|
|
1168
|
-
cmd: commandCandidate.cmd,
|
|
1169
|
-
exit_code: commandCandidate.exit_code,
|
|
1170
|
-
stderr: commandCandidate.stderr,
|
|
1171
|
-
stdout: commandCandidate.stdout
|
|
1172
|
-
});
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
evidence.push({
|
|
1176
|
-
type: 'openhands-event',
|
|
1177
|
-
ref: event.event_id,
|
|
1178
|
-
detail: event.event_type
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
return {
|
|
1183
|
-
event: normalizedEvents,
|
|
1184
|
-
commands: normalizeTaskCommands(commands),
|
|
1185
|
-
file_changes: normalizeTaskFileChanges(fileChanges),
|
|
1186
|
-
errors: normalizeTaskErrors(errors),
|
|
1187
|
-
evidence: normalizeTaskEvidence(evidence)
|
|
1188
|
-
};
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
924
|
function extractCommandsFromStageMetadata(stageMetadata = {}) {
|
|
1192
925
|
const gateSteps = Array.isArray(stageMetadata && stageMetadata.gate_steps)
|
|
1193
926
|
? stageMetadata.gate_steps
|
|
@@ -2919,21 +2652,7 @@ async function runStudioEventsCommand(options = {}, dependencies = {}) {
|
|
|
2919
2652
|
|
|
2920
2653
|
const limit = normalizePositiveInteger(options.limit, 50);
|
|
2921
2654
|
const job = await loadJob(paths, jobId, fileSystem);
|
|
2922
|
-
const openhandsEventsPath = normalizeString(options.openhandsEvents);
|
|
2923
|
-
let sourceStream = 'studio';
|
|
2924
2655
|
let events = await readStudioEvents(paths, jobId, { limit }, fileSystem);
|
|
2925
|
-
let openhandsSignals = null;
|
|
2926
|
-
if (openhandsEventsPath) {
|
|
2927
|
-
const absoluteOpenhandsPath = path.isAbsolute(openhandsEventsPath)
|
|
2928
|
-
? openhandsEventsPath
|
|
2929
|
-
: path.join(projectPath, openhandsEventsPath);
|
|
2930
|
-
const rawOpenhandsEvents = await readOpenHandsEventsFile(absoluteOpenhandsPath, fileSystem);
|
|
2931
|
-
openhandsSignals = mapOpenHandsEventsToTaskSignals(rawOpenhandsEvents, {
|
|
2932
|
-
jobId: job.job_id
|
|
2933
|
-
});
|
|
2934
|
-
events = openhandsSignals.event;
|
|
2935
|
-
sourceStream = 'openhands';
|
|
2936
|
-
}
|
|
2937
2656
|
|
|
2938
2657
|
const payload = await buildCommandPayload('studio-events', job, {
|
|
2939
2658
|
events,
|
|
@@ -2941,47 +2660,11 @@ async function runStudioEventsCommand(options = {}, dependencies = {}) {
|
|
|
2941
2660
|
fileSystem
|
|
2942
2661
|
});
|
|
2943
2662
|
payload.limit = limit;
|
|
2944
|
-
payload.source_stream =
|
|
2945
|
-
if (sourceStream === 'openhands') {
|
|
2946
|
-
payload.openhands_events_file = path.relative(projectPath, path.isAbsolute(openhandsEventsPath)
|
|
2947
|
-
? openhandsEventsPath
|
|
2948
|
-
: path.join(projectPath, openhandsEventsPath)).replace(/\\/g, '/');
|
|
2949
|
-
payload.task = {
|
|
2950
|
-
...payload.task,
|
|
2951
|
-
summary: [
|
|
2952
|
-
`OpenHands events: ${events.length} | commands: ${openhandsSignals.commands.length} | errors: ${openhandsSignals.errors.length}`,
|
|
2953
|
-
`File changes: ${openhandsSignals.file_changes.length} | source: ${payload.openhands_events_file}`,
|
|
2954
|
-
`Next: ${payload.task.next_action}`
|
|
2955
|
-
],
|
|
2956
|
-
handoff: {
|
|
2957
|
-
...(payload.task.handoff || {}),
|
|
2958
|
-
source_stream: 'openhands',
|
|
2959
|
-
openhands_event_count: events.length,
|
|
2960
|
-
openhands_command_count: openhandsSignals.commands.length,
|
|
2961
|
-
openhands_error_count: openhandsSignals.errors.length
|
|
2962
|
-
},
|
|
2963
|
-
commands: openhandsSignals.commands,
|
|
2964
|
-
file_changes: openhandsSignals.file_changes,
|
|
2965
|
-
errors: openhandsSignals.errors,
|
|
2966
|
-
evidence: normalizeTaskEvidence([
|
|
2967
|
-
...(Array.isArray(payload.task.evidence) ? payload.task.evidence : []),
|
|
2968
|
-
...openhandsSignals.evidence,
|
|
2969
|
-
{
|
|
2970
|
-
type: 'openhands-event-file',
|
|
2971
|
-
ref: payload.openhands_events_file,
|
|
2972
|
-
detail: 'mapped'
|
|
2973
|
-
}
|
|
2974
|
-
])
|
|
2975
|
-
};
|
|
2976
|
-
payload.eventId = events.length > 0
|
|
2977
|
-
? events[events.length - 1].event_id
|
|
2978
|
-
: null;
|
|
2979
|
-
}
|
|
2663
|
+
payload.source_stream = 'studio';
|
|
2980
2664
|
payload.events = events;
|
|
2981
2665
|
const normalizedPayload = attachTaskFeedbackModel(payload);
|
|
2982
2666
|
printStudioEventsPayload(normalizedPayload, options);
|
|
2983
2667
|
return normalizedPayload;
|
|
2984
|
-
return payload;
|
|
2985
2668
|
}
|
|
2986
2669
|
|
|
2987
2670
|
function printStudioIntakePayload(payload, options = {}) {
|
|
@@ -3267,7 +2950,6 @@ function registerStudioCommands(program) {
|
|
|
3267
2950
|
.description('Show studio job event stream')
|
|
3268
2951
|
.option('--job <job-id>', 'Studio job id (defaults to latest)')
|
|
3269
2952
|
.option('--limit <number>', 'Maximum number of recent events to return', '50')
|
|
3270
|
-
.option('--openhands-events <path>', 'Optional OpenHands raw events file (.json/.jsonl) mapped to task stream')
|
|
3271
2953
|
.option('--json', 'Print machine-readable JSON output')
|
|
3272
2954
|
.action(async (options) => runStudioCommand(runStudioEventsCommand, options, 'events'));
|
|
3273
2955
|
|
package/package.json
CHANGED
package/template/.sce/README.md
CHANGED
|
@@ -243,6 +243,6 @@ A Spec is a complete feature definition with three parts:
|
|
|
243
243
|
---
|
|
244
244
|
|
|
245
245
|
**Project Type**: Spec-driven development
|
|
246
|
-
**sce Version**: 3.6.
|
|
247
|
-
**Last Updated**: 2026-03-
|
|
246
|
+
**sce Version**: 3.6.62
|
|
247
|
+
**Last Updated**: 2026-03-21
|
|
248
248
|
**Purpose**: Guide AI tools to work effectively with this project
|