scene-capability-engine 3.6.29 → 3.6.32
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 +17 -0
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/docs/agent-runtime/magicball-contract-index.md +25 -0
- package/docs/command-reference.md +2 -0
- package/docs/magicball-capability-iteration-ui.md +1 -0
- package/docs/magicball-task-feedback-timeline-guide.md +8 -0
- package/lib/auto/session-metrics.js +53 -0
- package/lib/capability/catalog-service.js +248 -0
- package/lib/capability/inventory-service.js +100 -0
- package/lib/commands/auto.js +1 -1
- package/lib/commands/capability.js +81 -466
- package/lib/commands/scene.js +1 -1
- package/lib/commands/studio.js +147 -582
- package/lib/commands/timeline.js +6 -82
- package/lib/magicball/capability-inventory-view-model.js +213 -0
- package/lib/magicball/status-language.js +29 -0
- package/lib/magicball/task-feedback-model.js +113 -0
- package/lib/magicball/timeline-view-model.js +95 -0
- package/lib/scene/doctor-feedback.js +3541 -0
- package/lib/studio/task-envelope.js +269 -0
- package/lib/studio/task-intent.js +149 -0
- package/package.json +1 -1
package/lib/commands/studio.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const path = require('path');
|
|
1
|
+
const path = require('path');
|
|
2
2
|
const crypto = require('crypto');
|
|
3
3
|
const { spawnSync } = require('child_process');
|
|
4
4
|
const fs = require('fs-extra');
|
|
@@ -20,6 +20,13 @@ const {
|
|
|
20
20
|
runStudioSpecGovernance,
|
|
21
21
|
runStudioSceneBackfill
|
|
22
22
|
} = require('../studio/spec-intake-governor');
|
|
23
|
+
const { attachTaskFeedbackModel } = require('../magicball/task-feedback-model');
|
|
24
|
+
const { deriveTaskIntentShape, buildTaskSummaryLines, buildTaskAcceptanceCriteria } = require('../studio/task-intent');
|
|
25
|
+
const {
|
|
26
|
+
buildStudioTaskKey,
|
|
27
|
+
resolveTaskReference: resolveSharedTaskReference,
|
|
28
|
+
buildCommandPayload: buildSharedCommandPayload
|
|
29
|
+
} = require('../studio/task-envelope');
|
|
23
30
|
|
|
24
31
|
const STUDIO_JOB_API_VERSION = 'sce.studio.job/v0.1';
|
|
25
32
|
const STAGE_ORDER = ['plan', 'generate', 'apply', 'verify', 'release'];
|
|
@@ -1230,490 +1237,6 @@ function extractErrorsFromStageMetadata(stageState = {}, stageMetadata = {}) {
|
|
|
1230
1237
|
return [...directErrors, ...gateStepErrors];
|
|
1231
1238
|
}
|
|
1232
1239
|
|
|
1233
|
-
function collectTaskFileChanges(job = {}, stageName = '', stageMetadata = {}) {
|
|
1234
|
-
const fileChanges = [];
|
|
1235
|
-
if (Array.isArray(stageMetadata && stageMetadata.file_changes)) {
|
|
1236
|
-
fileChanges.push(...stageMetadata.file_changes);
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
const createdSpec = job?.source?.intake?.created_spec;
|
|
1240
|
-
const createdSpecId = createdSpec && createdSpec.created
|
|
1241
|
-
? normalizeString(createdSpec.spec_id)
|
|
1242
|
-
: '';
|
|
1243
|
-
if (stageName === 'plan' && createdSpecId) {
|
|
1244
|
-
fileChanges.push(
|
|
1245
|
-
{ path: `.sce/specs/${createdSpecId}/requirements.md`, line: 1 },
|
|
1246
|
-
{ path: `.sce/specs/${createdSpecId}/design.md`, line: 1 },
|
|
1247
|
-
{ path: `.sce/specs/${createdSpecId}/tasks.md`, line: 1 },
|
|
1248
|
-
{ path: `.sce/specs/${createdSpecId}/custom/problem-domain-chain.json`, line: 1 },
|
|
1249
|
-
{ path: `.sce/specs/${createdSpecId}/custom/problem-contract.json`, line: 1 }
|
|
1250
|
-
);
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
const artifacts = job && job.artifacts ? job.artifacts : {};
|
|
1254
|
-
if (stageName === 'plan') {
|
|
1255
|
-
if (normalizeString(artifacts.spec_portfolio_report)) {
|
|
1256
|
-
fileChanges.push({ path: artifacts.spec_portfolio_report, line: 1 });
|
|
1257
|
-
}
|
|
1258
|
-
if (normalizeString(artifacts.spec_scene_index)) {
|
|
1259
|
-
fileChanges.push({ path: artifacts.spec_scene_index, line: 1 });
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
if (stageName === 'generate' && normalizeString(artifacts.generate_report)) {
|
|
1263
|
-
fileChanges.push({ path: artifacts.generate_report, line: 1 });
|
|
1264
|
-
}
|
|
1265
|
-
if (stageName === 'verify' && normalizeString(artifacts.verify_report)) {
|
|
1266
|
-
fileChanges.push({ path: artifacts.verify_report, line: 1 });
|
|
1267
|
-
}
|
|
1268
|
-
if (stageName === 'release' && normalizeString(artifacts.release_report)) {
|
|
1269
|
-
fileChanges.push({ path: artifacts.release_report, line: 1 });
|
|
1270
|
-
}
|
|
1271
|
-
return normalizeTaskFileChanges(fileChanges);
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
function collectTaskEvidence(job = {}, stageName = '', stageMetadata = {}) {
|
|
1275
|
-
const evidence = [];
|
|
1276
|
-
const stageReport = normalizeString(stageMetadata && stageMetadata.report);
|
|
1277
|
-
if (stageReport) {
|
|
1278
|
-
evidence.push({ type: 'stage-report', ref: stageReport, detail: stageName });
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
const artifacts = job && job.artifacts ? job.artifacts : {};
|
|
1282
|
-
if (stageName && artifacts.problem_eval_reports && artifacts.problem_eval_reports[stageName]) {
|
|
1283
|
-
evidence.push({
|
|
1284
|
-
type: 'problem-evaluation-report',
|
|
1285
|
-
ref: artifacts.problem_eval_reports[stageName],
|
|
1286
|
-
detail: stageName
|
|
1287
|
-
});
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
if (normalizeString(job?.source?.domain_chain?.chain_path)) {
|
|
1291
|
-
evidence.push({
|
|
1292
|
-
type: 'domain-chain',
|
|
1293
|
-
ref: job.source.domain_chain.chain_path,
|
|
1294
|
-
detail: stageName
|
|
1295
|
-
});
|
|
1296
|
-
}
|
|
1297
|
-
if (normalizeString(job?.source?.problem_contract_path)) {
|
|
1298
|
-
evidence.push({
|
|
1299
|
-
type: 'problem-contract',
|
|
1300
|
-
ref: job.source.problem_contract_path,
|
|
1301
|
-
detail: stageName
|
|
1302
|
-
});
|
|
1303
|
-
}
|
|
1304
|
-
if (Array.isArray(stageMetadata && stageMetadata.auto_errorbook_records)) {
|
|
1305
|
-
for (const item of stageMetadata.auto_errorbook_records) {
|
|
1306
|
-
const entryId = normalizeString(item && item.entry_id);
|
|
1307
|
-
if (!entryId) {
|
|
1308
|
-
continue;
|
|
1309
|
-
}
|
|
1310
|
-
evidence.push({
|
|
1311
|
-
type: 'errorbook-entry',
|
|
1312
|
-
ref: entryId,
|
|
1313
|
-
detail: normalizeString(item && item.step_id) || null
|
|
1314
|
-
});
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
if (normalizeString(job && job.job_id)) {
|
|
1319
|
-
evidence.push({
|
|
1320
|
-
type: 'event-log',
|
|
1321
|
-
ref: '.sce/state/sce-state.sqlite',
|
|
1322
|
-
detail: `studio_event_stream:job_id=${job.job_id}`
|
|
1323
|
-
});
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
return normalizeTaskEvidence(evidence);
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
function buildTaskSummaryLines(job = {}, stageName = '', taskStatus = '', nextAction = '', taskRef = '') {
|
|
1330
|
-
const sceneId = normalizeString(job?.scene?.id) || 'scene.n/a';
|
|
1331
|
-
const specId = normalizeString(job?.scene?.spec_id) || normalizeString(job?.source?.spec_id) || 'spec.n/a';
|
|
1332
|
-
const progress = buildProgress(job);
|
|
1333
|
-
return [
|
|
1334
|
-
`Stage: ${stageName || 'plan'} | Status: ${taskStatus || 'unknown'}${taskRef ? ` | Ref: ${taskRef}` : ''}`,
|
|
1335
|
-
`Scene: ${sceneId} | Spec: ${specId} | Progress: ${progress.completed}/${progress.total}`,
|
|
1336
|
-
`Next: ${nextAction || 'n/a'}`
|
|
1337
|
-
];
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
function truncateTaskText(value = '', maxLength = 96) {
|
|
1341
|
-
const normalized = normalizeString(value).replace(/\s+/g, ' ');
|
|
1342
|
-
if (!normalized) {
|
|
1343
|
-
return '';
|
|
1344
|
-
}
|
|
1345
|
-
if (normalized.length <= maxLength) {
|
|
1346
|
-
return normalized;
|
|
1347
|
-
}
|
|
1348
|
-
return `${normalized.slice(0, Math.max(0, maxLength - 3)).trim()}...`;
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
function dedupeTaskList(items = [], limit = 3) {
|
|
1352
|
-
const seen = new Set();
|
|
1353
|
-
const result = [];
|
|
1354
|
-
for (const item of items) {
|
|
1355
|
-
const normalized = truncateTaskText(item, 120);
|
|
1356
|
-
if (!normalized) {
|
|
1357
|
-
continue;
|
|
1358
|
-
}
|
|
1359
|
-
const key = normalized.toLowerCase();
|
|
1360
|
-
if (seen.has(key)) {
|
|
1361
|
-
continue;
|
|
1362
|
-
}
|
|
1363
|
-
seen.add(key);
|
|
1364
|
-
result.push(normalized);
|
|
1365
|
-
if (result.length >= limit) {
|
|
1366
|
-
break;
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
return result;
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
function splitTaskRawRequest(rawRequest = '') {
|
|
1373
|
-
const normalized = normalizeString(rawRequest).replace(/\s+/g, ' ');
|
|
1374
|
-
if (!normalized) {
|
|
1375
|
-
return [];
|
|
1376
|
-
}
|
|
1377
|
-
const chunks = normalized
|
|
1378
|
-
.split(/(?:\r?\n|[;;。!?!?]|(?:\s+\band\b\s+)|(?:\s+\bthen\b\s+)|(?:\s+\balso\b\s+)|(?:\s*并且\s*)|(?:\s*同时\s*)|(?:\s*以及\s*)|(?:\s*然后\s*))/gi)
|
|
1379
|
-
.map((item) => normalizeString(item).replace(/^(?:and|then|also)\s+/i, ''))
|
|
1380
|
-
.filter(Boolean);
|
|
1381
|
-
return dedupeTaskList(chunks, 3);
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
function deriveTaskIntentShape(rawRequest = '', stageName = '') {
|
|
1385
|
-
const normalizedRaw = normalizeString(rawRequest).replace(/\s+/g, ' ');
|
|
1386
|
-
const clauses = splitTaskRawRequest(normalizedRaw);
|
|
1387
|
-
const hasRaw = normalizedRaw.length > 0;
|
|
1388
|
-
const inferredSubGoals = clauses.length > 1 ? clauses.slice(0, 3) : [];
|
|
1389
|
-
const needsSplit = inferredSubGoals.length > 1;
|
|
1390
|
-
const titleSource = clauses.length > 0
|
|
1391
|
-
? clauses[0]
|
|
1392
|
-
: (hasRaw ? normalizedRaw : `Studio ${stageName || 'task'} execution`);
|
|
1393
|
-
|
|
1394
|
-
let confidence = hasRaw ? 0.9 : 0.6;
|
|
1395
|
-
if (needsSplit) {
|
|
1396
|
-
confidence = 0.72;
|
|
1397
|
-
}
|
|
1398
|
-
if (normalizeString(stageName) && normalizeString(stageName) !== 'plan') {
|
|
1399
|
-
confidence = Math.min(0.95, confidence + 0.03);
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
return {
|
|
1403
|
-
title_norm: truncateTaskText(titleSource, 96) || `Studio ${stageName || 'task'} execution`,
|
|
1404
|
-
raw_request: hasRaw ? normalizedRaw : null,
|
|
1405
|
-
sub_goals: inferredSubGoals,
|
|
1406
|
-
needs_split: needsSplit,
|
|
1407
|
-
confidence: Number(confidence.toFixed(2))
|
|
1408
|
-
};
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
function buildTaskAcceptanceCriteria(stageName = '', job = {}, nextAction = '') {
|
|
1412
|
-
const normalizedStage = normalizeString(stageName) || 'task';
|
|
1413
|
-
const artifacts = job && job.artifacts ? job.artifacts : {};
|
|
1414
|
-
const criteriaByStage = {
|
|
1415
|
-
plan: [
|
|
1416
|
-
'Scene/spec binding is resolved and persisted in studio job metadata.',
|
|
1417
|
-
'Plan stage problem evaluation passes with no blockers.',
|
|
1418
|
-
`Next action is executable (${nextAction || 'sce studio generate --job <job-id>'}).`
|
|
1419
|
-
],
|
|
1420
|
-
generate: [
|
|
1421
|
-
'Patch bundle id is produced for downstream apply stage.',
|
|
1422
|
-
'Generate stage report is written to artifacts.',
|
|
1423
|
-
`Next action is executable (${nextAction || 'sce studio apply --patch-bundle <id> --job <job-id>'}).`
|
|
1424
|
-
],
|
|
1425
|
-
apply: [
|
|
1426
|
-
'Authorization requirements are satisfied for apply stage.',
|
|
1427
|
-
'Apply stage completes without policy blockers.',
|
|
1428
|
-
`Next action is executable (${nextAction || 'sce studio verify --job <job-id>'}).`
|
|
1429
|
-
],
|
|
1430
|
-
verify: [
|
|
1431
|
-
'Verification gates finish with no required-step failures.',
|
|
1432
|
-
`Verify report is available (${normalizeString(artifacts.verify_report) || 'artifact pending'}).`,
|
|
1433
|
-
`Next action is executable (${nextAction || 'sce studio release --job <job-id>'}).`
|
|
1434
|
-
],
|
|
1435
|
-
release: [
|
|
1436
|
-
'Release gates pass under configured release profile.',
|
|
1437
|
-
`Release reference is emitted (${normalizeString(artifacts.release_ref) || 'artifact pending'}).`,
|
|
1438
|
-
`Next action is executable (${nextAction || 'complete'}).`
|
|
1439
|
-
],
|
|
1440
|
-
rollback: [
|
|
1441
|
-
'Rollback stage transitions job status to rolled_back.',
|
|
1442
|
-
'Rollback evidence is appended to studio event stream.',
|
|
1443
|
-
`Recovery next action is executable (${nextAction || 'sce studio plan --scene <scene-id> --from-chat <session>'}).`
|
|
1444
|
-
],
|
|
1445
|
-
events: [
|
|
1446
|
-
'Events stream payload is available for task-level audit.',
|
|
1447
|
-
'Task envelope preserves normalized IDs and handoff fields.',
|
|
1448
|
-
`Next action is explicit (${nextAction || 'n/a'}).`
|
|
1449
|
-
],
|
|
1450
|
-
resume: [
|
|
1451
|
-
'Current job status and stage progress are restored deterministically.',
|
|
1452
|
-
'Task envelope remains schema-compatible for downstream UI.',
|
|
1453
|
-
`Next action is explicit (${nextAction || 'n/a'}).`
|
|
1454
|
-
]
|
|
1455
|
-
};
|
|
1456
|
-
return criteriaByStage[normalizedStage] || [
|
|
1457
|
-
'Task envelope contains normalized identifiers and task contract fields.',
|
|
1458
|
-
'Task output preserves evidence, command logs, and error bundles.',
|
|
1459
|
-
`Next action is explicit (${nextAction || 'n/a'}).`
|
|
1460
|
-
];
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
function buildTaskFeedbackModel(payload = {}) {
|
|
1464
|
-
const task = payload && payload.task && typeof payload.task === 'object' ? payload.task : {};
|
|
1465
|
-
const handoff = task && typeof task.handoff === 'object' ? task.handoff : {};
|
|
1466
|
-
const errors = Array.isArray(task.errors) ? task.errors : [];
|
|
1467
|
-
const commands = Array.isArray(task.commands) ? task.commands : [];
|
|
1468
|
-
const fileChanges = Array.isArray(task.file_changes) ? task.file_changes : [];
|
|
1469
|
-
const evidence = Array.isArray(task.evidence) ? task.evidence : [];
|
|
1470
|
-
const acceptance = Array.isArray(task.acceptance_criteria) ? task.acceptance_criteria : [];
|
|
1471
|
-
const firstError = errors[0] || {};
|
|
1472
|
-
const stage = normalizeString(handoff.stage) || 'task';
|
|
1473
|
-
const status = normalizeString(task.status) || 'unknown';
|
|
1474
|
-
const problemComponent = normalizeString(handoff.component) || normalizeString(payload.sceneId) || null;
|
|
1475
|
-
const expected = normalizeString(acceptance[0]) || ('Complete ' + stage + ' stage successfully');
|
|
1476
|
-
const actual = normalizeString(firstError.message) || ('Current status: ' + status);
|
|
1477
|
-
|
|
1478
|
-
let chainCheckpoint = 'task-envelope';
|
|
1479
|
-
if (errors.length > 0 && commands.length > 0) {
|
|
1480
|
-
chainCheckpoint = 'command-execution';
|
|
1481
|
-
} else if (errors.length > 0) {
|
|
1482
|
-
chainCheckpoint = 'stage-gate';
|
|
1483
|
-
} else if (fileChanges.length > 0) {
|
|
1484
|
-
chainCheckpoint = 'patch-applied';
|
|
1485
|
-
} else if (evidence.length > 0) {
|
|
1486
|
-
chainCheckpoint = 'evidence-collected';
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
let confidence = 'low';
|
|
1490
|
-
if (errors.length > 0) {
|
|
1491
|
-
confidence = 'medium';
|
|
1492
|
-
} else if (status === 'completed') {
|
|
1493
|
-
confidence = 'high';
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
let recommendedAction = '继续当前阶段';
|
|
1497
|
-
if (errors.length > 0) {
|
|
1498
|
-
recommendedAction = '处理阻断后重试';
|
|
1499
|
-
} else if (status === 'completed' && normalizeString(task.next_action) && normalizeString(task.next_action) !== 'complete') {
|
|
1500
|
-
recommendedAction = '执行下一阶段';
|
|
1501
|
-
} else if (status === 'completed') {
|
|
1502
|
-
recommendedAction = '任务完成';
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
return {
|
|
1506
|
-
version: '1.0',
|
|
1507
|
-
problem: {
|
|
1508
|
-
component: problemComponent,
|
|
1509
|
-
action: normalizeString(handoff.action) || stage,
|
|
1510
|
-
expected,
|
|
1511
|
-
actual
|
|
1512
|
-
},
|
|
1513
|
-
execution: {
|
|
1514
|
-
stage,
|
|
1515
|
-
status,
|
|
1516
|
-
summary: Array.isArray(task.summary) ? task.summary.slice(0, 3) : [],
|
|
1517
|
-
blocking_summary: normalizeString(handoff.blocking_summary) || normalizeString(firstError.message) || null
|
|
1518
|
-
},
|
|
1519
|
-
diagnosis: {
|
|
1520
|
-
hypothesis: normalizeString(firstError.error_bundle) || normalizeString(firstError.message) || normalizeString(handoff.reason) || null,
|
|
1521
|
-
chain_checkpoint: chainCheckpoint,
|
|
1522
|
-
root_cause_confidence: confidence
|
|
1523
|
-
},
|
|
1524
|
-
evidence: {
|
|
1525
|
-
file_count: fileChanges.length,
|
|
1526
|
-
file_paths: fileChanges.slice(0, 5).map((item) => normalizeString(item && item.path)).filter(Boolean),
|
|
1527
|
-
command_count: commands.length,
|
|
1528
|
-
error_count: errors.length,
|
|
1529
|
-
verification_result: status === 'completed' ? 'passed-or-advanced' : (errors.length > 0 ? 'blocked' : 'in-progress'),
|
|
1530
|
-
regression_scope: Array.isArray(handoff.regression_scope) ? handoff.regression_scope : []
|
|
1531
|
-
},
|
|
1532
|
-
next_step: {
|
|
1533
|
-
recommended_action: recommendedAction,
|
|
1534
|
-
next_action: normalizeString(task.next_action) || null,
|
|
1535
|
-
next_command: normalizeString(task.next_action) || null
|
|
1536
|
-
}
|
|
1537
|
-
};
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
function buildMagicballStatusLanguage(input = {}) {
|
|
1541
|
-
const attention = normalizeString(input.attention_level) || 'medium';
|
|
1542
|
-
const status = normalizeString(input.status) || 'unknown';
|
|
1543
|
-
const blockingSummary = normalizeString(input.blocking_summary) || null;
|
|
1544
|
-
const recommendedAction = normalizeString(input.recommended_action) || null;
|
|
1545
|
-
return {
|
|
1546
|
-
attention_level: attention,
|
|
1547
|
-
status_tone: attention === 'critical' ? 'danger' : (attention === 'high' ? 'warning' : (attention === 'low' ? 'success' : 'info')),
|
|
1548
|
-
status_label: status || 'unknown',
|
|
1549
|
-
blocking_summary: blockingSummary,
|
|
1550
|
-
recommended_action: recommendedAction
|
|
1551
|
-
};
|
|
1552
|
-
}
|
|
1553
|
-
|
|
1554
|
-
function attachTaskFeedbackModel(payload = {}) {
|
|
1555
|
-
if (!payload || typeof payload !== 'object' || !payload.task || typeof payload.task !== 'object') {
|
|
1556
|
-
return payload;
|
|
1557
|
-
}
|
|
1558
|
-
return {
|
|
1559
|
-
...payload,
|
|
1560
|
-
task: {
|
|
1561
|
-
...payload.task,
|
|
1562
|
-
feedback_model: (() => { const model = buildTaskFeedbackModel(payload); return { ...model, mb_status: buildMagicballStatusLanguage({ status: model.execution.status, attention_level: model.diagnosis.root_cause_confidence === 'high' ? 'high' : (model.execution.status === 'completed' ? 'low' : 'medium'), blocking_summary: model.execution.blocking_summary, recommended_action: model.next_step.recommended_action }) }; })()
|
|
1563
|
-
}
|
|
1564
|
-
};
|
|
1565
|
-
}
|
|
1566
|
-
function buildTaskEnvelope(mode, job, options = {}) {
|
|
1567
|
-
const stageName = resolveTaskStage(mode, job, options.stageName);
|
|
1568
|
-
const stageState = stageName && job && job.stages && job.stages[stageName]
|
|
1569
|
-
? job.stages[stageName]
|
|
1570
|
-
: {};
|
|
1571
|
-
const stageMetadata = stageState && typeof stageState.metadata === 'object' && stageState.metadata
|
|
1572
|
-
? stageState.metadata
|
|
1573
|
-
: {};
|
|
1574
|
-
const nextAction = resolveNextAction(job);
|
|
1575
|
-
|
|
1576
|
-
const events = Array.isArray(options.events)
|
|
1577
|
-
? options.events
|
|
1578
|
-
: (options.event ? [options.event] : []);
|
|
1579
|
-
const latestEvent = events.length > 0 ? events[events.length - 1] : null;
|
|
1580
|
-
|
|
1581
|
-
const taskStatus = normalizeString(stageState && stageState.status)
|
|
1582
|
-
|| (stageName === 'rollback' && normalizeString(job && job.status) === 'rolled_back'
|
|
1583
|
-
? 'completed'
|
|
1584
|
-
: normalizeString(job && job.status) || 'unknown');
|
|
1585
|
-
const taskId = normalizeString(options.taskId)
|
|
1586
|
-
|| (normalizeString(job && job.job_id)
|
|
1587
|
-
? `${job.job_id}:${stageName || 'task'}`
|
|
1588
|
-
: null);
|
|
1589
|
-
const rawRequest = normalizeString(job?.source?.goal);
|
|
1590
|
-
const goal = rawRequest
|
|
1591
|
-
|| `Studio ${stageName || 'task'} execution`;
|
|
1592
|
-
const taskIntent = deriveTaskIntentShape(rawRequest, stageName);
|
|
1593
|
-
const sessionId = normalizeString(job?.session?.scene_session_id) || null;
|
|
1594
|
-
const sceneId = normalizeString(job?.scene?.id) || null;
|
|
1595
|
-
const specId = normalizeString(job?.scene?.spec_id) || normalizeString(job?.source?.spec_id) || null;
|
|
1596
|
-
const taskRef = normalizeString(options.taskRef) || null;
|
|
1597
|
-
|
|
1598
|
-
const commands = normalizeTaskCommands([
|
|
1599
|
-
...(Array.isArray(stageMetadata.commands) ? stageMetadata.commands : []),
|
|
1600
|
-
...extractCommandsFromStageMetadata(stageMetadata)
|
|
1601
|
-
]);
|
|
1602
|
-
const errors = normalizeTaskErrors(
|
|
1603
|
-
extractErrorsFromStageMetadata(stageState, stageMetadata)
|
|
1604
|
-
);
|
|
1605
|
-
const fileChanges = collectTaskFileChanges(job, stageName, stageMetadata);
|
|
1606
|
-
const evidence = collectTaskEvidence(job, stageName, stageMetadata);
|
|
1607
|
-
|
|
1608
|
-
const handoff = stageMetadata.handoff && typeof stageMetadata.handoff === 'object'
|
|
1609
|
-
? stageMetadata.handoff
|
|
1610
|
-
: {
|
|
1611
|
-
stage: stageName,
|
|
1612
|
-
status: taskStatus,
|
|
1613
|
-
completed_at: normalizeString(stageState && stageState.completed_at) || null,
|
|
1614
|
-
report: normalizeString(stageMetadata.report) || null,
|
|
1615
|
-
release_ref: normalizeString(stageMetadata.release_ref) || normalizeString(job?.artifacts?.release_ref) || null
|
|
1616
|
-
};
|
|
1617
|
-
|
|
1618
|
-
const normalizedHandoff = {
|
|
1619
|
-
...handoff,
|
|
1620
|
-
task_ref: taskRef
|
|
1621
|
-
};
|
|
1622
|
-
|
|
1623
|
-
return {
|
|
1624
|
-
sessionId,
|
|
1625
|
-
sceneId,
|
|
1626
|
-
specId,
|
|
1627
|
-
taskId,
|
|
1628
|
-
taskRef,
|
|
1629
|
-
eventId: normalizeString(latestEvent && latestEvent.event_id) || null,
|
|
1630
|
-
task: {
|
|
1631
|
-
ref: taskRef,
|
|
1632
|
-
task_ref: taskRef,
|
|
1633
|
-
title_norm: taskIntent.title_norm,
|
|
1634
|
-
raw_request: taskIntent.raw_request,
|
|
1635
|
-
goal,
|
|
1636
|
-
sub_goals: taskIntent.sub_goals,
|
|
1637
|
-
acceptance_criteria: buildTaskAcceptanceCriteria(stageName, job, nextAction),
|
|
1638
|
-
needs_split: taskIntent.needs_split,
|
|
1639
|
-
confidence: taskIntent.confidence,
|
|
1640
|
-
status: taskStatus,
|
|
1641
|
-
summary: buildTaskSummaryLines(job, stageName, taskStatus, nextAction, taskRef),
|
|
1642
|
-
handoff: normalizedHandoff,
|
|
1643
|
-
next_action: nextAction,
|
|
1644
|
-
file_changes: fileChanges,
|
|
1645
|
-
commands,
|
|
1646
|
-
errors,
|
|
1647
|
-
evidence
|
|
1648
|
-
},
|
|
1649
|
-
event: events
|
|
1650
|
-
};
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
function toRelativePosix(projectPath, absolutePath) {
|
|
1654
|
-
return path.relative(projectPath, absolutePath).replace(/\\/g, '/');
|
|
1655
|
-
}
|
|
1656
|
-
|
|
1657
|
-
async function readSpecDomainChain(projectPath, specId, fileSystem = fs) {
|
|
1658
|
-
const specRoot = path.join(projectPath, '.sce', 'specs', specId);
|
|
1659
|
-
const chainPath = path.join(specRoot, DOMAIN_CHAIN_RELATIVE_PATH);
|
|
1660
|
-
if (!await fileSystem.pathExists(chainPath)) {
|
|
1661
|
-
return null;
|
|
1662
|
-
}
|
|
1663
|
-
try {
|
|
1664
|
-
const payload = await fileSystem.readJson(chainPath);
|
|
1665
|
-
const stat = await fileSystem.stat(chainPath);
|
|
1666
|
-
return {
|
|
1667
|
-
spec_id: specId,
|
|
1668
|
-
chain_path: toRelativePosix(projectPath, chainPath),
|
|
1669
|
-
payload,
|
|
1670
|
-
updated_at: stat && stat.mtime ? stat.mtime.toISOString() : null,
|
|
1671
|
-
mtime_ms: Number(stat && stat.mtimeMs) || 0
|
|
1672
|
-
};
|
|
1673
|
-
} catch (_error) {
|
|
1674
|
-
return null;
|
|
1675
|
-
}
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
async function readSpecProblemContract(projectPath, specId, fileSystem = fs) {
|
|
1679
|
-
const specRoot = path.join(projectPath, '.sce', 'specs', specId);
|
|
1680
|
-
const contractPath = path.join(specRoot, DEFAULT_PROBLEM_CONTRACT_RELATIVE_PATH);
|
|
1681
|
-
if (!await fileSystem.pathExists(contractPath)) {
|
|
1682
|
-
return null;
|
|
1683
|
-
}
|
|
1684
|
-
try {
|
|
1685
|
-
const payload = await fileSystem.readJson(contractPath);
|
|
1686
|
-
const stat = await fileSystem.stat(contractPath);
|
|
1687
|
-
return {
|
|
1688
|
-
spec_id: specId,
|
|
1689
|
-
contract_path: toRelativePosix(projectPath, contractPath),
|
|
1690
|
-
payload,
|
|
1691
|
-
updated_at: stat && stat.mtime ? stat.mtime.toISOString() : null,
|
|
1692
|
-
mtime_ms: Number(stat && stat.mtimeMs) || 0
|
|
1693
|
-
};
|
|
1694
|
-
} catch (_error) {
|
|
1695
|
-
return null;
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
async function readGovernanceSignals(projectPath, fileSystem = fs) {
|
|
1700
|
-
const reportPath = path.join(projectPath, DEFAULT_INTERACTIVE_GOVERNANCE_REPORT);
|
|
1701
|
-
if (!await fileSystem.pathExists(reportPath)) {
|
|
1702
|
-
return {
|
|
1703
|
-
available: false,
|
|
1704
|
-
report_path: null,
|
|
1705
|
-
high_breach_count: 0,
|
|
1706
|
-
medium_breach_count: 0
|
|
1707
|
-
};
|
|
1708
|
-
}
|
|
1709
|
-
const payload = await fileSystem.readJson(reportPath).catch(() => null);
|
|
1710
|
-
const summary = extractGovernanceBreachSignals(payload || {});
|
|
1711
|
-
return {
|
|
1712
|
-
...summary,
|
|
1713
|
-
report_path: toRelativePosix(projectPath, reportPath)
|
|
1714
|
-
};
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
1240
|
async function readVerifyReportSignals(projectPath, verifyReportPath = '', fileSystem = fs) {
|
|
1718
1241
|
const normalized = normalizeString(verifyReportPath);
|
|
1719
1242
|
if (!normalized) {
|
|
@@ -1807,6 +1330,70 @@ function normalizeProblemContract(contract = {}, context = {}) {
|
|
|
1807
1330
|
};
|
|
1808
1331
|
}
|
|
1809
1332
|
|
|
1333
|
+
function toRelativePosix(projectPath, absolutePath) {
|
|
1334
|
+
return path.relative(projectPath, absolutePath).replace(/\\/g, '/');
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
async function readSpecDomainChain(projectPath, specId, fileSystem = fs) {
|
|
1338
|
+
const specRoot = path.join(projectPath, '.sce', 'specs', specId);
|
|
1339
|
+
const chainPath = path.join(specRoot, DOMAIN_CHAIN_RELATIVE_PATH);
|
|
1340
|
+
if (!await fileSystem.pathExists(chainPath)) {
|
|
1341
|
+
return null;
|
|
1342
|
+
}
|
|
1343
|
+
try {
|
|
1344
|
+
const payload = await fileSystem.readJson(chainPath);
|
|
1345
|
+
const stat = await fileSystem.stat(chainPath);
|
|
1346
|
+
return {
|
|
1347
|
+
spec_id: specId,
|
|
1348
|
+
chain_path: toRelativePosix(projectPath, chainPath),
|
|
1349
|
+
payload,
|
|
1350
|
+
updated_at: stat && stat.mtime ? stat.mtime.toISOString() : null,
|
|
1351
|
+
mtime_ms: Number(stat && stat.mtimeMs) || 0
|
|
1352
|
+
};
|
|
1353
|
+
} catch (_error) {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
async function readSpecProblemContract(projectPath, specId, fileSystem = fs) {
|
|
1359
|
+
const specRoot = path.join(projectPath, '.sce', 'specs', specId);
|
|
1360
|
+
const contractPath = path.join(specRoot, DEFAULT_PROBLEM_CONTRACT_RELATIVE_PATH);
|
|
1361
|
+
if (!await fileSystem.pathExists(contractPath)) {
|
|
1362
|
+
return null;
|
|
1363
|
+
}
|
|
1364
|
+
try {
|
|
1365
|
+
const payload = await fileSystem.readJson(contractPath);
|
|
1366
|
+
const stat = await fileSystem.stat(contractPath);
|
|
1367
|
+
return {
|
|
1368
|
+
spec_id: specId,
|
|
1369
|
+
contract_path: toRelativePosix(projectPath, contractPath),
|
|
1370
|
+
payload,
|
|
1371
|
+
updated_at: stat && stat.mtime ? stat.mtime.toISOString() : null,
|
|
1372
|
+
mtime_ms: Number(stat && stat.mtimeMs) || 0
|
|
1373
|
+
};
|
|
1374
|
+
} catch (_error) {
|
|
1375
|
+
return null;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
async function readGovernanceSignals(projectPath, fileSystem = fs) {
|
|
1380
|
+
const reportPath = path.join(projectPath, DEFAULT_INTERACTIVE_GOVERNANCE_REPORT);
|
|
1381
|
+
if (!await fileSystem.pathExists(reportPath)) {
|
|
1382
|
+
return {
|
|
1383
|
+
available: false,
|
|
1384
|
+
report_path: null,
|
|
1385
|
+
high_breach_count: 0,
|
|
1386
|
+
medium_breach_count: 0
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
const payload = await fileSystem.readJson(reportPath).catch(() => null);
|
|
1390
|
+
const summary = extractGovernanceBreachSignals(payload || {});
|
|
1391
|
+
return {
|
|
1392
|
+
...summary,
|
|
1393
|
+
report_path: toRelativePosix(projectPath, reportPath)
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1810
1397
|
function extractGovernanceBreachSignals(report = {}) {
|
|
1811
1398
|
if (!report || typeof report !== 'object') {
|
|
1812
1399
|
return {
|
|
@@ -2049,109 +1636,38 @@ async function resolveDomainChainBinding(options = {}, dependencies = {}) {
|
|
|
2049
1636
|
};
|
|
2050
1637
|
}
|
|
2051
1638
|
|
|
2052
|
-
function
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
console.log(` Status: ${payload.status}`);
|
|
2060
|
-
console.log(` Progress: ${payload.progress.completed}/${payload.progress.total} (${payload.progress.percent}%)`);
|
|
2061
|
-
console.log(` Next: ${payload.next_action}`);
|
|
2062
|
-
}
|
|
2063
|
-
|
|
2064
|
-
function ensureStageCompleted(job, stageName, metadata = {}) {
|
|
2065
|
-
if (!job.stages || !job.stages[stageName]) {
|
|
2066
|
-
job.stages = job.stages || createStageState();
|
|
2067
|
-
job.stages[stageName] = { status: 'pending', completed_at: null, metadata: {} };
|
|
2068
|
-
}
|
|
2069
|
-
|
|
2070
|
-
job.stages[stageName] = {
|
|
2071
|
-
status: 'completed',
|
|
2072
|
-
completed_at: nowIso(),
|
|
2073
|
-
metadata
|
|
2074
|
-
};
|
|
2075
|
-
}
|
|
2076
|
-
|
|
2077
|
-
function isStageCompleted(job, stageName) {
|
|
2078
|
-
return Boolean(job && job.stages && job.stages[stageName] && job.stages[stageName].status === 'completed');
|
|
2079
|
-
}
|
|
2080
|
-
|
|
2081
|
-
function ensureStagePrerequisite(job, stageName, prerequisiteStage) {
|
|
2082
|
-
if (!isStageCompleted(job, prerequisiteStage)) {
|
|
2083
|
-
throw new Error(`Cannot run studio ${stageName}: stage "${prerequisiteStage}" is not completed`);
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
|
|
2087
|
-
function ensureNotRolledBack(job, stageName) {
|
|
2088
|
-
if (job.status === 'rolled_back') {
|
|
2089
|
-
throw new Error(`Cannot run studio ${stageName}: job ${job.job_id} is rolled back`);
|
|
2090
|
-
}
|
|
2091
|
-
}
|
|
2092
|
-
|
|
2093
|
-
function buildStudioTaskKey(stageName = '') {
|
|
2094
|
-
const normalizedStage = normalizeString(stageName) || 'task';
|
|
2095
|
-
return `studio:${normalizedStage}`;
|
|
2096
|
-
}
|
|
2097
|
-
|
|
2098
|
-
async function resolveTaskReference(mode, job, options = {}) {
|
|
2099
|
-
const explicitTaskRef = normalizeString(options.taskRef);
|
|
2100
|
-
if (explicitTaskRef) {
|
|
2101
|
-
return explicitTaskRef;
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
const sceneId = normalizeString(job?.scene?.id);
|
|
2105
|
-
const specId = normalizeString(job?.scene?.spec_id) || normalizeString(job?.source?.spec_id);
|
|
2106
|
-
if (!sceneId || !specId) {
|
|
2107
|
-
return null;
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2110
|
-
const stageName = resolveTaskStage(mode, job, options.stageName);
|
|
2111
|
-
const taskKey = normalizeString(options.taskKey) || buildStudioTaskKey(stageName);
|
|
2112
|
-
const projectPath = normalizeString(options.projectPath) || process.cwd();
|
|
2113
|
-
const fileSystem = options.fileSystem || fs;
|
|
2114
|
-
const taskRefRegistry = options.taskRefRegistry || new TaskRefRegistry(projectPath, { fileSystem });
|
|
2115
|
-
|
|
2116
|
-
try {
|
|
2117
|
-
const taskRef = await taskRefRegistry.resolveOrCreateRef({
|
|
2118
|
-
sceneId,
|
|
2119
|
-
specId,
|
|
2120
|
-
taskKey,
|
|
2121
|
-
source: 'studio-stage',
|
|
2122
|
-
metadata: {
|
|
2123
|
-
mode: normalizeString(mode) || null,
|
|
2124
|
-
stage: stageName || null,
|
|
2125
|
-
job_id: normalizeString(job?.job_id) || null
|
|
2126
|
-
}
|
|
2127
|
-
});
|
|
2128
|
-
return taskRef.task_ref;
|
|
2129
|
-
} catch (_error) {
|
|
2130
|
-
return null;
|
|
2131
|
-
}
|
|
1639
|
+
function resolveTaskReference(mode, job, options = {}, dependencies = {}) {
|
|
1640
|
+
return resolveSharedTaskReference(mode, job, options, {
|
|
1641
|
+
normalizeString,
|
|
1642
|
+
resolveTaskStage,
|
|
1643
|
+
TaskRefRegistry,
|
|
1644
|
+
fileSystem: dependencies.fileSystem || options.fileSystem || fs
|
|
1645
|
+
});
|
|
2132
1646
|
}
|
|
2133
1647
|
|
|
2134
1648
|
async function buildCommandPayload(mode, job, options = {}) {
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
1649
|
+
return buildSharedCommandPayload(mode, job, options, {
|
|
1650
|
+
normalizeString,
|
|
1651
|
+
resolveTaskStage,
|
|
1652
|
+
resolveNextAction,
|
|
1653
|
+
normalizeTaskCommands,
|
|
1654
|
+
normalizeTaskErrors,
|
|
1655
|
+
normalizeTaskEvidence,
|
|
1656
|
+
normalizeTaskFileChanges,
|
|
1657
|
+
extractCommandsFromStageMetadata,
|
|
1658
|
+
extractErrorsFromStageMetadata,
|
|
1659
|
+
deriveTaskIntentShape,
|
|
1660
|
+
buildTaskSummaryLines,
|
|
1661
|
+
buildTaskAcceptanceCriteria,
|
|
1662
|
+
buildProgress,
|
|
1663
|
+
TaskRefRegistry,
|
|
1664
|
+
fileSystem: options.fileSystem || fs,
|
|
1665
|
+
attachTaskFeedbackModel
|
|
2151
1666
|
});
|
|
2152
1667
|
}
|
|
2153
1668
|
|
|
2154
1669
|
function buildJobDomainChainMetadata(job = {}) {
|
|
1670
|
+
|
|
2155
1671
|
const domainChain = job && job.source && job.source.domain_chain
|
|
2156
1672
|
? job.source.domain_chain
|
|
2157
1673
|
: null;
|
|
@@ -2312,6 +1828,19 @@ async function markStudioStageBlockedByProblemEval(paths, job, stageName, evalua
|
|
|
2312
1828
|
throw new Error(`studio ${stageName} blocked by problem evaluation: ${reason}`);
|
|
2313
1829
|
}
|
|
2314
1830
|
|
|
1831
|
+
function printStudioPayload(payload, options = {}) {
|
|
1832
|
+
if (options.json) {
|
|
1833
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
console.log(chalk.blue(`Studio job: ${payload.job_id}`));
|
|
1838
|
+
console.log(` Status: ${payload.status}`);
|
|
1839
|
+
console.log(` Progress: ${payload.progress.completed}/${payload.progress.total} (${payload.progress.percent}%)`);
|
|
1840
|
+
console.log(` Next: ${payload.next_action}`);
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
|
|
2315
1844
|
async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
2316
1845
|
const projectPath = dependencies.projectPath || process.cwd();
|
|
2317
1846
|
const fileSystem = dependencies.fileSystem || fs;
|
|
@@ -2656,6 +2185,36 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
|
|
|
2656
2185
|
return payload;
|
|
2657
2186
|
}
|
|
2658
2187
|
|
|
2188
|
+
function ensureStageCompleted(job, stageName, metadata = {}) {
|
|
2189
|
+
if (!job.stages || !job.stages[stageName]) {
|
|
2190
|
+
job.stages = job.stages || createStageState();
|
|
2191
|
+
job.stages[stageName] = { status: 'pending', completed_at: null, metadata: {} };
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
job.stages[stageName] = {
|
|
2195
|
+
status: 'completed',
|
|
2196
|
+
completed_at: nowIso(),
|
|
2197
|
+
metadata
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
function isStageCompleted(job, stageName) {
|
|
2202
|
+
return Boolean(job && job.stages && job.stages[stageName] && job.stages[stageName].status === 'completed');
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
function ensureStagePrerequisite(job, stageName, prerequisiteStage) {
|
|
2206
|
+
if (!isStageCompleted(job, prerequisiteStage)) {
|
|
2207
|
+
throw new Error(`Cannot run studio ${stageName}: stage "${prerequisiteStage}" is not completed`);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
function ensureNotRolledBack(job, stageName) {
|
|
2212
|
+
if (job.status === 'rolled_back') {
|
|
2213
|
+
throw new Error(`Cannot run studio ${stageName}: job ${job.job_id} is rolled back`);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
|
|
2659
2218
|
async function runStudioGenerateCommand(options = {}, dependencies = {}) {
|
|
2660
2219
|
const projectPath = dependencies.projectPath || process.cwd();
|
|
2661
2220
|
const fileSystem = dependencies.fileSystem || fs;
|
|
@@ -3741,9 +3300,15 @@ module.exports = {
|
|
|
3741
3300
|
runStudioReleaseCommand,
|
|
3742
3301
|
runStudioRollbackCommand,
|
|
3743
3302
|
runStudioEventsCommand,
|
|
3744
|
-
buildMagicballStatusLanguage,
|
|
3745
3303
|
runStudioPortfolioCommand,
|
|
3746
3304
|
runStudioBackfillSpecScenesCommand,
|
|
3747
3305
|
runStudioResumeCommand,
|
|
3748
3306
|
registerStudioCommands
|
|
3749
3307
|
};
|
|
3308
|
+
|
|
3309
|
+
|
|
3310
|
+
|
|
3311
|
+
|
|
3312
|
+
|
|
3313
|
+
|
|
3314
|
+
|