scene-capability-engine 3.5.2 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +12 -3
- package/README.zh.md +12 -3
- package/bin/scene-capability-engine.js +2 -0
- package/docs/command-reference.md +22 -3
- package/lib/commands/studio.js +113 -58
- package/lib/commands/task.js +630 -68
- package/lib/state/sce-state-store.js +594 -0
- package/lib/task/task-ref-registry.js +139 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.6.0] - 2026-03-04
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- SQLite-backed SCE state store (`.sce/state/sce-state.sqlite`) for scene/spec/task-ref/event runtime governance.
|
|
14
|
+
- Hierarchical task reference registry (`SS.PP.TT`) persisted in SQLite (`task_ref_registry` table).
|
|
15
|
+
- New task reference commands:
|
|
16
|
+
- `sce task ref --scene <scene-id> --spec <spec-id> --task <task-id> --json`
|
|
17
|
+
- `sce task show --ref <SS.PP.TT> --json`
|
|
18
|
+
- `sce task rerun --ref <SS.PP.TT> [--dry-run] --json`
|
|
19
|
+
- New module: `lib/task/task-ref-registry.js`.
|
|
20
|
+
- New state-store unit tests for sqlite-only backend enforcement and fallback gating.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- Studio task-stream payload now includes `taskRef` at root level and `task.ref` in `task` object.
|
|
24
|
+
- Studio `task.summary` now carries task reference context for UI traceability.
|
|
25
|
+
- Studio event stream persistence switched to SQLite (`studio_event_stream`) as the single source of truth.
|
|
26
|
+
- CLI now registers a dedicated `task` command group (`claim/unclaim/list/status/ref/show/rerun`).
|
|
27
|
+
- State backend selection now enforces sqlite-only runtime policy (no legacy file backend branch).
|
|
28
|
+
|
|
10
29
|
## [3.5.2] - 2026-03-03
|
|
11
30
|
|
|
12
31
|
### Added
|
package/README.md
CHANGED
|
@@ -129,7 +129,7 @@ SCE is tool-agnostic and works with Codex, Claude Code, Cursor, Windsurf, VS Cod
|
|
|
129
129
|
- `sce workspace takeover-apply --json`
|
|
130
130
|
|
|
131
131
|
Studio task-stream output contract (default):
|
|
132
|
-
- IDs: `sessionId`, `sceneId`, `specId`, `taskId`, `eventId`
|
|
132
|
+
- IDs: `sessionId`, `sceneId`, `specId`, `taskId`, `taskRef`, `eventId`
|
|
133
133
|
- Task: `task.goal`, `task.status`, `task.summary` (3-line), `task.handoff`, `task.next_action`
|
|
134
134
|
- File refs: `task.file_changes[]` with `path`, `line`, `diffRef`
|
|
135
135
|
- Command logs: `task.commands[]` with `cmd`, `exit_code`, `stdout`, `stderr`, `log_path`
|
|
@@ -137,11 +137,20 @@ Studio task-stream output contract (default):
|
|
|
137
137
|
- Evidence: `task.evidence[]`
|
|
138
138
|
- Raw audit stream: `event[]` (and `studio events` keeps `events[]` compatibility field)
|
|
139
139
|
- OpenHands bridge: `sce studio events --openhands-events <path>` maps OpenHands raw events into the same task contract (`source_stream=openhands`)
|
|
140
|
+
- Hierarchical task reference operations:
|
|
141
|
+
- `sce task ref --scene <scene-id> --spec <spec-id> --task <task-id> --json`
|
|
142
|
+
- `sce task show --ref <SS.PP.TT> --json`
|
|
143
|
+
- `sce task rerun --ref <SS.PP.TT> [--dry-run] --json`
|
|
144
|
+
- Runtime governance state store policy:
|
|
145
|
+
- SQLite-only backend (`.sce/state/sce-state.sqlite`)
|
|
146
|
+
- In-memory fallback only in `NODE_ENV=test` or when `SCE_STATE_ALLOW_MEMORY_FALLBACK=1`
|
|
147
|
+
- Outside those conditions, unavailable SQLite support fails fast for task-ref/event persistence
|
|
140
148
|
|
|
141
149
|
---
|
|
142
150
|
|
|
143
151
|
## Important Version Changes
|
|
144
152
|
|
|
153
|
+
- `3.6.0`: Added hierarchical task references (`taskRef`, format `SS.PP.TT`) backed by SQLite state store `.sce/state/sce-state.sqlite`, plus new task commands (`sce task ref/show/rerun`) for reference lookup and deterministic rerun.
|
|
145
154
|
- `3.5.2`: Introduced task-stream output contract for Studio commands (`sessionId/sceneId/specId/taskId/eventId`, structured `task.*` fields, `event[]` audit stream) and added OpenHands raw-event bridge via `sce studio events --openhands-events <path>`.
|
|
146
155
|
- `3.5.1`: Enforced stricter Studio intake defaults (`--manual-spec` and `--no-spec-governance` blocked unless policy override), added historical spec scene backfill command (`sce studio backfill-spec-scenes`) and persisted override mapping (`.sce/spec-governance/spec-scene-overrides.json`) for portfolio/related-spec alignment.
|
|
147
156
|
- `3.5.0`: Added Studio automatic goal intake + scene spec portfolio governance (`sce studio intake`, `sce studio portfolio`), including default intake policy baseline and governance artifacts for bounded scene spec growth.
|
|
@@ -191,5 +200,5 @@ MIT. See [LICENSE](LICENSE).
|
|
|
191
200
|
|
|
192
201
|
---
|
|
193
202
|
|
|
194
|
-
**Version**: 3.
|
|
195
|
-
**Last Updated**: 2026-03-
|
|
203
|
+
**Version**: 3.6.0
|
|
204
|
+
**Last Updated**: 2026-03-04
|
package/README.zh.md
CHANGED
|
@@ -129,7 +129,7 @@ SCE 对工具无锁定,可接入 Codex、Claude Code、Cursor、Windsurf、VS
|
|
|
129
129
|
- `sce workspace takeover-apply --json`
|
|
130
130
|
|
|
131
131
|
Studio 任务流输出契约(默认):
|
|
132
|
-
- ID 字段:`sessionId`、`sceneId`、`specId`、`taskId`、`eventId`
|
|
132
|
+
- ID 字段:`sessionId`、`sceneId`、`specId`、`taskId`、`taskRef`、`eventId`
|
|
133
133
|
- 任务主体:`task.goal`、`task.status`、`task.summary`(固定三行)、`task.handoff`、`task.next_action`
|
|
134
134
|
- 文件引用:`task.file_changes[]`(`path`、`line`、`diffRef`)
|
|
135
135
|
- 命令执行:`task.commands[]`(`cmd`、`exit_code`、`stdout`、`stderr`、`log_path`)
|
|
@@ -137,11 +137,20 @@ Studio 任务流输出契约(默认):
|
|
|
137
137
|
- 证据引用:`task.evidence[]`
|
|
138
138
|
- 原始审计流:`event[]`(`studio events` 同时保留 `events[]` 兼容字段)
|
|
139
139
|
- OpenHands 桥接:`sce studio events --openhands-events <path>` 可将 OpenHands 原始事件映射为同一 task 契约(`source_stream=openhands`)
|
|
140
|
+
- 分层任务引用操作:
|
|
141
|
+
- `sce task ref --scene <scene-id> --spec <spec-id> --task <task-id> --json`
|
|
142
|
+
- `sce task show --ref <SS.PP.TT> --json`
|
|
143
|
+
- `sce task rerun --ref <SS.PP.TT> [--dry-run] --json`
|
|
144
|
+
- 运行时治理状态库策略:
|
|
145
|
+
- 仅支持 SQLite 后端(`.sce/state/sce-state.sqlite`)
|
|
146
|
+
- 仅在 `NODE_ENV=test` 或 `SCE_STATE_ALLOW_MEMORY_FALLBACK=1` 时允许内存回退
|
|
147
|
+
- 在上述条件之外若 SQLite 不可用,任务引用/事件持久化会快速失败
|
|
140
148
|
|
|
141
149
|
---
|
|
142
150
|
|
|
143
151
|
## 重要版本变更
|
|
144
152
|
|
|
153
|
+
- `3.6.0`:新增分层任务引用(`taskRef`,格式 `SS.PP.TT`),持久化到 SQLite 状态库 `.sce/state/sce-state.sqlite`;新增 `sce task ref/show/rerun` 用于引用查询与可重放执行。
|
|
145
154
|
- `3.5.2`:新增 Studio 任务流输出契约(`sessionId/sceneId/specId/taskId/eventId`、结构化 `task.*` 字段、`event[]` 审计流),并新增 OpenHands 原始事件桥接能力:`sce studio events --openhands-events <path>`。
|
|
146
155
|
- `3.5.1`:默认强化 Studio intake 治理(`--manual-spec`、`--no-spec-governance` 在未显式放开策略时会被阻断),新增历史 spec 场景回填命令 `sce studio backfill-spec-scenes`,并写入 `.sce/spec-governance/spec-scene-overrides.json` 以统一 portfolio 与 related-spec 的场景映射。
|
|
147
156
|
- `3.5.0`:新增 Studio 目标自动 intake + 场景 spec 组合治理(`sce studio intake`、`sce studio portfolio`),并默认启用 intake 策略基线与治理快照产物,控制场景内 spec 无序增长。
|
|
@@ -191,5 +200,5 @@ MIT,见 [LICENSE](LICENSE)。
|
|
|
191
200
|
|
|
192
201
|
---
|
|
193
202
|
|
|
194
|
-
**版本**:3.
|
|
195
|
-
**最后更新**:2026-03-
|
|
203
|
+
**版本**:3.6.0
|
|
204
|
+
**最后更新**:2026-03-04
|
|
@@ -23,6 +23,7 @@ const { registerSpecDomainCommand } = require('../lib/commands/spec-domain');
|
|
|
23
23
|
const { registerSpecRelatedCommand } = require('../lib/commands/spec-related');
|
|
24
24
|
const { registerTimelineCommands } = require('../lib/commands/timeline');
|
|
25
25
|
const { registerValueCommands } = require('../lib/commands/value');
|
|
26
|
+
const { registerTaskCommands } = require('../lib/commands/task');
|
|
26
27
|
const VersionChecker = require('../lib/version/version-checker');
|
|
27
28
|
const {
|
|
28
29
|
findLegacyKiroDirectories,
|
|
@@ -943,6 +944,7 @@ registerOrchestrateCommands(program);
|
|
|
943
944
|
|
|
944
945
|
// Value realization and observability commands
|
|
945
946
|
registerValueCommands(program);
|
|
947
|
+
registerTaskCommands(program);
|
|
946
948
|
|
|
947
949
|
// Template management commands
|
|
948
950
|
const templatesCommand = require('../lib/commands/templates');
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> Quick reference for all `sce` commands
|
|
4
4
|
|
|
5
5
|
**Version**: 3.5.2
|
|
6
|
-
**Last Updated**: 2026-03-
|
|
6
|
+
**Last Updated**: 2026-03-04
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -181,10 +181,26 @@ sce task claim <spec-name> <task-id>
|
|
|
181
181
|
# Unclaim a task
|
|
182
182
|
sce task unclaim <spec-name> <task-id>
|
|
183
183
|
|
|
184
|
-
#
|
|
184
|
+
# List claimed tasks
|
|
185
|
+
sce task list <spec-name>
|
|
185
186
|
sce task status <spec-name>
|
|
187
|
+
|
|
188
|
+
# Resolve or create hierarchical task reference (scene.spec.task)
|
|
189
|
+
sce task ref --scene <scene-id> --spec <spec-id> --task <task-id> --json
|
|
190
|
+
|
|
191
|
+
# Show task mapping from hierarchical ref
|
|
192
|
+
sce task show --ref <SS.PP.TT> --json
|
|
193
|
+
|
|
194
|
+
# Rerun by hierarchical ref (dry-run preview)
|
|
195
|
+
sce task rerun --ref <SS.PP.TT> --dry-run --json
|
|
186
196
|
```
|
|
187
197
|
|
|
198
|
+
Task references and studio runtime events are persisted in SQLite state store:
|
|
199
|
+
- `.sce/state/sce-state.sqlite`
|
|
200
|
+
- Backend policy: SQLite only (no file-backend mode).
|
|
201
|
+
- In-memory fallback is restricted to `NODE_ENV=test` or `SCE_STATE_ALLOW_MEMORY_FALLBACK=1`.
|
|
202
|
+
- If SQLite runtime support is unavailable outside fallback conditions, `task ref/show/rerun` and `studio events` persistence operations fail fast.
|
|
203
|
+
|
|
188
204
|
### Context & Prompts
|
|
189
205
|
|
|
190
206
|
```bash
|
|
@@ -570,7 +586,7 @@ SCE_STUDIO_REQUIRE_AUTH=1 SCE_STUDIO_AUTH_PASSWORD=top-secret sce studio apply -
|
|
|
570
586
|
```
|
|
571
587
|
|
|
572
588
|
Studio JSON output now includes a stable UI-oriented task stream contract (in addition to existing `job_*` fields):
|
|
573
|
-
- root IDs: `sessionId`, `sceneId`, `specId`, `taskId`, `eventId`
|
|
589
|
+
- root IDs: `sessionId`, `sceneId`, `specId`, `taskId`, `taskRef`, `eventId`
|
|
574
590
|
- `task.goal`, `task.status`, `task.summary` (fixed 3-line summary), `task.handoff`, `task.next_action`
|
|
575
591
|
- `task.file_changes[]`: `path`, `line`, `diffRef`
|
|
576
592
|
- `task.commands[]`: `cmd`, `exit_code`, `stdout`, `stderr`, `log_path`
|
|
@@ -578,6 +594,9 @@ Studio JSON output now includes a stable UI-oriented task stream contract (in ad
|
|
|
578
594
|
- `task.evidence[]`: structured evidence references
|
|
579
595
|
- `event[]`: raw audit event stream (`studio events` also keeps legacy `events[]` for compatibility)
|
|
580
596
|
- `studio events --openhands-events <path>` switches `source_stream=openhands` and maps OpenHands raw events to the same task contract fields.
|
|
597
|
+
- hierarchical task reference lookup/rerun:
|
|
598
|
+
- `sce task show --ref <SS.PP.TT> --json`
|
|
599
|
+
- `sce task rerun --ref <SS.PP.TT> [--dry-run] --json`
|
|
581
600
|
|
|
582
601
|
Stage guardrails are enforced by default:
|
|
583
602
|
- `plan` requires `--scene`; SCE binds one active primary session per scene
|
package/lib/commands/studio.js
CHANGED
|
@@ -11,6 +11,8 @@ const {
|
|
|
11
11
|
const { findRelatedSpecs } = require('../spec/related-specs');
|
|
12
12
|
const { captureTimelineCheckpoint } = require('../runtime/project-timeline');
|
|
13
13
|
const { runProblemEvaluation } = require('../problem/problem-evaluator');
|
|
14
|
+
const { TaskRefRegistry } = require('../task/task-ref-registry');
|
|
15
|
+
const { getSceStateStore } = require('../state/sce-state-store');
|
|
14
16
|
const {
|
|
15
17
|
loadStudioIntakePolicy,
|
|
16
18
|
runStudioAutoIntake,
|
|
@@ -40,8 +42,7 @@ function resolveStudioPaths(projectPath = process.cwd()) {
|
|
|
40
42
|
projectPath,
|
|
41
43
|
studioDir,
|
|
42
44
|
jobsDir: path.join(studioDir, 'jobs'),
|
|
43
|
-
latestFile: path.join(studioDir, 'latest-job.json')
|
|
44
|
-
eventsDir: path.join(studioDir, 'events')
|
|
45
|
+
latestFile: path.join(studioDir, 'latest-job.json')
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
|
|
@@ -597,7 +598,6 @@ async function writeStudioReport(projectPath, relativePath, payload, fileSystem
|
|
|
597
598
|
|
|
598
599
|
async function ensureStudioDirectories(paths, fileSystem = fs) {
|
|
599
600
|
await fileSystem.ensureDir(paths.jobsDir);
|
|
600
|
-
await fileSystem.ensureDir(paths.eventsDir);
|
|
601
601
|
}
|
|
602
602
|
|
|
603
603
|
async function writeLatestJob(paths, jobId, fileSystem = fs) {
|
|
@@ -622,10 +622,6 @@ function getJobFilePath(paths, jobId) {
|
|
|
622
622
|
return path.join(paths.jobsDir, `${jobId}.json`);
|
|
623
623
|
}
|
|
624
624
|
|
|
625
|
-
function getEventLogFilePath(paths, jobId) {
|
|
626
|
-
return path.join(paths.eventsDir, `${jobId}.jsonl`);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
625
|
async function saveJob(paths, job, fileSystem = fs) {
|
|
630
626
|
const jobFile = getJobFilePath(paths, job.job_id);
|
|
631
627
|
await fileSystem.writeJson(jobFile, job, { spaces: 2 });
|
|
@@ -640,40 +636,28 @@ async function appendStudioEvent(paths, job, eventType, metadata = {}, fileSyste
|
|
|
640
636
|
timestamp: nowIso(),
|
|
641
637
|
metadata
|
|
642
638
|
};
|
|
643
|
-
const
|
|
644
|
-
const
|
|
645
|
-
|
|
639
|
+
const sceneId = normalizeString(job?.scene?.id) || null;
|
|
640
|
+
const specId = normalizeString(job?.scene?.spec_id) || normalizeString(job?.source?.spec_id) || null;
|
|
641
|
+
const stateStore = getSceStateStore(paths.projectPath, { fileSystem });
|
|
642
|
+
const persisted = await stateStore.appendStudioEvent({
|
|
643
|
+
...event,
|
|
644
|
+
scene_id: sceneId,
|
|
645
|
+
spec_id: specId
|
|
646
|
+
});
|
|
647
|
+
if (!persisted) {
|
|
648
|
+
throw new Error('Failed to persist studio event into sqlite state store');
|
|
649
|
+
}
|
|
646
650
|
return event;
|
|
647
651
|
}
|
|
648
652
|
|
|
649
653
|
async function readStudioEvents(paths, jobId, options = {}, fileSystem = fs) {
|
|
650
654
|
const { limit = 50 } = options;
|
|
651
|
-
const
|
|
652
|
-
const
|
|
653
|
-
if (
|
|
654
|
-
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
const content = await fileSystem.readFile(eventFile, 'utf8');
|
|
658
|
-
const lines = content
|
|
659
|
-
.split(/\r?\n/)
|
|
660
|
-
.map((line) => line.trim())
|
|
661
|
-
.filter(Boolean);
|
|
662
|
-
|
|
663
|
-
const parsed = [];
|
|
664
|
-
for (const line of lines) {
|
|
665
|
-
try {
|
|
666
|
-
const payload = JSON.parse(line);
|
|
667
|
-
parsed.push(payload);
|
|
668
|
-
} catch (_error) {
|
|
669
|
-
// Ignore malformed lines to keep event stream robust.
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
if (limit <= 0) {
|
|
674
|
-
return parsed;
|
|
655
|
+
const stateStore = getSceStateStore(paths.projectPath, { fileSystem });
|
|
656
|
+
const events = await stateStore.listStudioEvents(jobId, { limit });
|
|
657
|
+
if (events === null) {
|
|
658
|
+
throw new Error('SQLite state backend unavailable while reading studio events');
|
|
675
659
|
}
|
|
676
|
-
return
|
|
660
|
+
return events;
|
|
677
661
|
}
|
|
678
662
|
|
|
679
663
|
async function loadJob(paths, jobId, fileSystem = fs) {
|
|
@@ -1333,20 +1317,20 @@ function collectTaskEvidence(job = {}, stageName = '', stageMetadata = {}) {
|
|
|
1333
1317
|
if (normalizeString(job && job.job_id)) {
|
|
1334
1318
|
evidence.push({
|
|
1335
1319
|
type: 'event-log',
|
|
1336
|
-
ref:
|
|
1337
|
-
detail:
|
|
1320
|
+
ref: '.sce/state/sce-state.sqlite',
|
|
1321
|
+
detail: `studio_event_stream:job_id=${job.job_id}`
|
|
1338
1322
|
});
|
|
1339
1323
|
}
|
|
1340
1324
|
|
|
1341
1325
|
return normalizeTaskEvidence(evidence);
|
|
1342
1326
|
}
|
|
1343
1327
|
|
|
1344
|
-
function buildTaskSummaryLines(job = {}, stageName = '', taskStatus = '', nextAction = '') {
|
|
1328
|
+
function buildTaskSummaryLines(job = {}, stageName = '', taskStatus = '', nextAction = '', taskRef = '') {
|
|
1345
1329
|
const sceneId = normalizeString(job?.scene?.id) || 'scene.n/a';
|
|
1346
1330
|
const specId = normalizeString(job?.scene?.spec_id) || normalizeString(job?.source?.spec_id) || 'spec.n/a';
|
|
1347
1331
|
const progress = buildProgress(job);
|
|
1348
1332
|
return [
|
|
1349
|
-
`Stage: ${stageName || 'plan'} | Status: ${taskStatus || 'unknown'}`,
|
|
1333
|
+
`Stage: ${stageName || 'plan'} | Status: ${taskStatus || 'unknown'}${taskRef ? ` | Ref: ${taskRef}` : ''}`,
|
|
1350
1334
|
`Scene: ${sceneId} | Spec: ${specId} | Progress: ${progress.completed}/${progress.total}`,
|
|
1351
1335
|
`Next: ${nextAction || 'n/a'}`
|
|
1352
1336
|
];
|
|
@@ -1380,6 +1364,7 @@ function buildTaskEnvelope(mode, job, options = {}) {
|
|
|
1380
1364
|
const sessionId = normalizeString(job?.session?.scene_session_id) || null;
|
|
1381
1365
|
const sceneId = normalizeString(job?.scene?.id) || null;
|
|
1382
1366
|
const specId = normalizeString(job?.scene?.spec_id) || normalizeString(job?.source?.spec_id) || null;
|
|
1367
|
+
const taskRef = normalizeString(options.taskRef) || null;
|
|
1383
1368
|
|
|
1384
1369
|
const commands = normalizeTaskCommands([
|
|
1385
1370
|
...(Array.isArray(stageMetadata.commands) ? stageMetadata.commands : []),
|
|
@@ -1401,17 +1386,24 @@ function buildTaskEnvelope(mode, job, options = {}) {
|
|
|
1401
1386
|
release_ref: normalizeString(stageMetadata.release_ref) || normalizeString(job?.artifacts?.release_ref) || null
|
|
1402
1387
|
};
|
|
1403
1388
|
|
|
1389
|
+
const normalizedHandoff = {
|
|
1390
|
+
...handoff,
|
|
1391
|
+
task_ref: taskRef
|
|
1392
|
+
};
|
|
1393
|
+
|
|
1404
1394
|
return {
|
|
1405
1395
|
sessionId,
|
|
1406
1396
|
sceneId,
|
|
1407
1397
|
specId,
|
|
1408
1398
|
taskId,
|
|
1399
|
+
taskRef,
|
|
1409
1400
|
eventId: normalizeString(latestEvent && latestEvent.event_id) || null,
|
|
1410
1401
|
task: {
|
|
1402
|
+
ref: taskRef,
|
|
1411
1403
|
goal,
|
|
1412
1404
|
status: taskStatus,
|
|
1413
|
-
summary: buildTaskSummaryLines(job, stageName, taskStatus, nextAction),
|
|
1414
|
-
handoff,
|
|
1405
|
+
summary: buildTaskSummaryLines(job, stageName, taskStatus, nextAction, taskRef),
|
|
1406
|
+
handoff: normalizedHandoff,
|
|
1415
1407
|
next_action: nextAction,
|
|
1416
1408
|
file_changes: fileChanges,
|
|
1417
1409
|
commands,
|
|
@@ -1862,7 +1854,49 @@ function ensureNotRolledBack(job, stageName) {
|
|
|
1862
1854
|
}
|
|
1863
1855
|
}
|
|
1864
1856
|
|
|
1865
|
-
function
|
|
1857
|
+
function buildStudioTaskKey(stageName = '') {
|
|
1858
|
+
const normalizedStage = normalizeString(stageName) || 'task';
|
|
1859
|
+
return `studio:${normalizedStage}`;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
async function resolveTaskReference(mode, job, options = {}) {
|
|
1863
|
+
const explicitTaskRef = normalizeString(options.taskRef);
|
|
1864
|
+
if (explicitTaskRef) {
|
|
1865
|
+
return explicitTaskRef;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
const sceneId = normalizeString(job?.scene?.id);
|
|
1869
|
+
const specId = normalizeString(job?.scene?.spec_id) || normalizeString(job?.source?.spec_id);
|
|
1870
|
+
if (!sceneId || !specId) {
|
|
1871
|
+
return null;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
const stageName = resolveTaskStage(mode, job, options.stageName);
|
|
1875
|
+
const taskKey = normalizeString(options.taskKey) || buildStudioTaskKey(stageName);
|
|
1876
|
+
const projectPath = normalizeString(options.projectPath) || process.cwd();
|
|
1877
|
+
const fileSystem = options.fileSystem || fs;
|
|
1878
|
+
const taskRefRegistry = options.taskRefRegistry || new TaskRefRegistry(projectPath, { fileSystem });
|
|
1879
|
+
|
|
1880
|
+
try {
|
|
1881
|
+
const taskRef = await taskRefRegistry.resolveOrCreateRef({
|
|
1882
|
+
sceneId,
|
|
1883
|
+
specId,
|
|
1884
|
+
taskKey,
|
|
1885
|
+
source: 'studio-stage',
|
|
1886
|
+
metadata: {
|
|
1887
|
+
mode: normalizeString(mode) || null,
|
|
1888
|
+
stage: stageName || null,
|
|
1889
|
+
job_id: normalizeString(job?.job_id) || null
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1892
|
+
return taskRef.task_ref;
|
|
1893
|
+
} catch (_error) {
|
|
1894
|
+
return null;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
async function buildCommandPayload(mode, job, options = {}) {
|
|
1899
|
+
const taskRef = await resolveTaskReference(mode, job, options);
|
|
1866
1900
|
const base = {
|
|
1867
1901
|
mode,
|
|
1868
1902
|
success: true,
|
|
@@ -1874,7 +1908,10 @@ function buildCommandPayload(mode, job, options = {}) {
|
|
|
1874
1908
|
};
|
|
1875
1909
|
return {
|
|
1876
1910
|
...base,
|
|
1877
|
-
...buildTaskEnvelope(mode, job,
|
|
1911
|
+
...buildTaskEnvelope(mode, job, {
|
|
1912
|
+
...options,
|
|
1913
|
+
taskRef
|
|
1914
|
+
})
|
|
1878
1915
|
};
|
|
1879
1916
|
}
|
|
1880
1917
|
|
|
@@ -2367,9 +2404,11 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
2367
2404
|
}, fileSystem);
|
|
2368
2405
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
2369
2406
|
|
|
2370
|
-
const payload = buildCommandPayload('studio-plan', job, {
|
|
2407
|
+
const payload = await buildCommandPayload('studio-plan', job, {
|
|
2371
2408
|
stageName: 'plan',
|
|
2372
|
-
event: planEvent
|
|
2409
|
+
event: planEvent,
|
|
2410
|
+
projectPath,
|
|
2411
|
+
fileSystem
|
|
2373
2412
|
});
|
|
2374
2413
|
payload.scene = {
|
|
2375
2414
|
id: sceneId,
|
|
@@ -2465,9 +2504,11 @@ async function runStudioGenerateCommand(options = {}, dependencies = {}) {
|
|
|
2465
2504
|
}, fileSystem);
|
|
2466
2505
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
2467
2506
|
|
|
2468
|
-
const payload = buildCommandPayload('studio-generate', job, {
|
|
2507
|
+
const payload = await buildCommandPayload('studio-generate', job, {
|
|
2469
2508
|
stageName: 'generate',
|
|
2470
|
-
event: generateEvent
|
|
2509
|
+
event: generateEvent,
|
|
2510
|
+
projectPath,
|
|
2511
|
+
fileSystem
|
|
2471
2512
|
});
|
|
2472
2513
|
printStudioPayload(payload, options);
|
|
2473
2514
|
return payload;
|
|
@@ -2535,9 +2576,11 @@ async function runStudioApplyCommand(options = {}, dependencies = {}) {
|
|
|
2535
2576
|
}, fileSystem);
|
|
2536
2577
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
2537
2578
|
|
|
2538
|
-
const payload = buildCommandPayload('studio-apply', job, {
|
|
2579
|
+
const payload = await buildCommandPayload('studio-apply', job, {
|
|
2539
2580
|
stageName: 'apply',
|
|
2540
|
-
event: applyEvent
|
|
2581
|
+
event: applyEvent,
|
|
2582
|
+
projectPath,
|
|
2583
|
+
fileSystem
|
|
2541
2584
|
});
|
|
2542
2585
|
printStudioPayload(payload, options);
|
|
2543
2586
|
return payload;
|
|
@@ -2685,9 +2728,11 @@ async function runStudioVerifyCommand(options = {}, dependencies = {}) {
|
|
|
2685
2728
|
}, fileSystem);
|
|
2686
2729
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
2687
2730
|
|
|
2688
|
-
const payload = buildCommandPayload('studio-verify', job, {
|
|
2731
|
+
const payload = await buildCommandPayload('studio-verify', job, {
|
|
2689
2732
|
stageName: 'verify',
|
|
2690
|
-
event: verifyEvent
|
|
2733
|
+
event: verifyEvent,
|
|
2734
|
+
projectPath,
|
|
2735
|
+
fileSystem
|
|
2691
2736
|
});
|
|
2692
2737
|
printStudioPayload(payload, options);
|
|
2693
2738
|
return payload;
|
|
@@ -2894,9 +2939,11 @@ async function runStudioReleaseCommand(options = {}, dependencies = {}) {
|
|
|
2894
2939
|
}, fileSystem);
|
|
2895
2940
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
2896
2941
|
|
|
2897
|
-
const payload = buildCommandPayload('studio-release', job, {
|
|
2942
|
+
const payload = await buildCommandPayload('studio-release', job, {
|
|
2898
2943
|
stageName: 'release',
|
|
2899
|
-
event: releaseEvent
|
|
2944
|
+
event: releaseEvent,
|
|
2945
|
+
projectPath,
|
|
2946
|
+
fileSystem
|
|
2900
2947
|
});
|
|
2901
2948
|
printStudioPayload(payload, options);
|
|
2902
2949
|
return payload;
|
|
@@ -2916,8 +2963,10 @@ async function runStudioResumeCommand(options = {}, dependencies = {}) {
|
|
|
2916
2963
|
|
|
2917
2964
|
const job = await loadJob(paths, jobId, fileSystem);
|
|
2918
2965
|
const events = await readStudioEvents(paths, jobId, { limit: 20 }, fileSystem);
|
|
2919
|
-
const payload = buildCommandPayload('studio-resume', job, {
|
|
2920
|
-
events
|
|
2966
|
+
const payload = await buildCommandPayload('studio-resume', job, {
|
|
2967
|
+
events,
|
|
2968
|
+
projectPath,
|
|
2969
|
+
fileSystem
|
|
2921
2970
|
});
|
|
2922
2971
|
payload.success = true;
|
|
2923
2972
|
printStudioPayload(payload, options);
|
|
@@ -2975,9 +3024,11 @@ async function runStudioRollbackCommand(options = {}, dependencies = {}) {
|
|
|
2975
3024
|
}, fileSystem);
|
|
2976
3025
|
await writeLatestJob(paths, jobId, fileSystem);
|
|
2977
3026
|
|
|
2978
|
-
const payload = buildCommandPayload('studio-rollback', job, {
|
|
3027
|
+
const payload = await buildCommandPayload('studio-rollback', job, {
|
|
2979
3028
|
stageName: 'rollback',
|
|
2980
|
-
event: rollbackEvent
|
|
3029
|
+
event: rollbackEvent,
|
|
3030
|
+
projectPath,
|
|
3031
|
+
fileSystem
|
|
2981
3032
|
});
|
|
2982
3033
|
payload.rollback = { ...job.rollback };
|
|
2983
3034
|
printStudioPayload(payload, options);
|
|
@@ -3035,7 +3086,11 @@ async function runStudioEventsCommand(options = {}, dependencies = {}) {
|
|
|
3035
3086
|
sourceStream = 'openhands';
|
|
3036
3087
|
}
|
|
3037
3088
|
|
|
3038
|
-
const payload = buildCommandPayload('studio-events', job, {
|
|
3089
|
+
const payload = await buildCommandPayload('studio-events', job, {
|
|
3090
|
+
events,
|
|
3091
|
+
projectPath,
|
|
3092
|
+
fileSystem
|
|
3093
|
+
});
|
|
3039
3094
|
payload.limit = limit;
|
|
3040
3095
|
payload.source_stream = sourceStream;
|
|
3041
3096
|
if (sourceStream === 'openhands') {
|