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 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.5.2
195
- **Last Updated**: 2026-03-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.5.2
195
- **最后更新**:2026-03-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-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
- # Show task status
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
@@ -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 eventLine = `${JSON.stringify(event)}\n`;
644
- const eventFile = getEventLogFilePath(paths, job.job_id);
645
- await fileSystem.appendFile(eventFile, eventLine, 'utf8');
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 eventFile = getEventLogFilePath(paths, jobId);
652
- const exists = await fileSystem.pathExists(eventFile);
653
- if (!exists) {
654
- return [];
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 parsed.slice(-limit);
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: `.sce/studio/events/${job.job_id}.jsonl`,
1337
- detail: 'raw-audit-stream'
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 buildCommandPayload(mode, job, options = {}) {
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, options)
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, { events });
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') {