plugin-agent-orchestrator 1.0.27 → 1.0.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.
Files changed (110) hide show
  1. package/README.md +9 -7
  2. package/dist/client/index.js +1 -1
  3. package/dist/client-v2/{214.723affb37c13bf7a.js → 214.79650a549273f163.js} +1 -1
  4. package/dist/client-v2/264.718a107e43fc163c.js +10 -0
  5. package/dist/client-v2/373.f5d5292e53c4e832.js +10 -0
  6. package/dist/client-v2/{41.1805b2edfaa4afe2.js → 41.ba6e080cc0488143.js} +1 -1
  7. package/dist/client-v2/418.29e713f79131eece.js +10 -0
  8. package/dist/client-v2/619.bd3c5698b40705c3.js +10 -0
  9. package/dist/client-v2/677.a991ce0250ff5c77.js +10 -0
  10. package/dist/client-v2/{70.a15d7fcec7c41768.js → 70.bda9518881c05360.js} +1 -1
  11. package/dist/client-v2/925.f5370de8f6632d65.js +10 -0
  12. package/dist/client-v2/index.js +1 -1
  13. package/dist/externalVersion.js +7 -10
  14. package/dist/locale/en-US.json +94 -25
  15. package/dist/locale/vi-VN.json +94 -25
  16. package/dist/locale/zh-CN.json +94 -25
  17. package/dist/server/collections/agent-execution-spans.js +37 -0
  18. package/dist/server/collections/agent-harness-profiles.js +2 -2
  19. package/dist/server/collections/agent-memory-contexts.js +125 -0
  20. package/dist/server/collections/orchestrator-logs.js +2 -2
  21. package/dist/server/migrations/20260425000000-add-interaction-schema.js +3 -1
  22. package/dist/server/migrations/20260427000000-change-packages-to-text.js +3 -1
  23. package/dist/server/migrations/20260427000001-change-other-json-to-text.js +6 -2
  24. package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.js +21 -19
  25. package/dist/server/migrations/20260621000000-native-policy-profile-defaults.js +193 -0
  26. package/dist/server/plugin.js +128 -74
  27. package/dist/server/resources/agent-monitor.js +454 -0
  28. package/dist/server/services/AgentHarness.js +24 -499
  29. package/dist/server/services/AgentMemoryContextService.js +216 -0
  30. package/dist/server/services/ExecutionSpanService.js +2 -2
  31. package/dist/server/services/NativeSubAgentObserver.js +413 -0
  32. package/dist/server/skill-hub/mcp/McpController.js +16 -5
  33. package/dist/server/skill-hub/plugin.js +81 -5
  34. package/dist/server/skill-hub/tasks/SkillExecutionTask.js +9 -3
  35. package/dist/server/tools/delegate-task.js +11 -589
  36. package/dist/server/utils/skill-settings.js +18 -1
  37. package/package.json +47 -49
  38. package/src/client/AIEmployeesContext.tsx +5 -18
  39. package/src/client/AgentRunsTab.tsx +2 -771
  40. package/src/client/HarnessProfilesTab.tsx +2 -257
  41. package/src/client/OrchestratorSettings.tsx +97 -106
  42. package/src/client/RulesTab.tsx +2 -788
  43. package/src/client/plugin.tsx +0 -2
  44. package/src/client/skill-hub/components/ExecutionHistory.tsx +200 -202
  45. package/src/client/skill-hub/components/ExecutionProgress.tsx +51 -55
  46. package/src/client/skill-hub/components/LoopSettings.tsx +331 -331
  47. package/src/client/skill-hub/components/SkillEditor.tsx +43 -39
  48. package/src/client/skill-hub/components/SkillManager.tsx +194 -181
  49. package/src/client/skill-hub/components/SkillTestPanel.tsx +141 -145
  50. package/src/client/skill-hub/locale.ts +16 -16
  51. package/src/client/skill-hub/tools/SkillHubCard.tsx +104 -109
  52. package/src/client/skill-hub/tools/loopTemplates.ts +52 -52
  53. package/src/client/skill-hub/utils/jsonFields.ts +7 -3
  54. package/src/client-v2/components/AIEmployeesContext.tsx +3 -16
  55. package/src/client-v2/components/AgentRunsTab.tsx +182 -455
  56. package/src/client-v2/components/HarnessProfilesTab.tsx +34 -31
  57. package/src/client-v2/components/RulesTab.tsx +2 -782
  58. package/src/client-v2/components/TracingTab.tsx +1 -1
  59. package/src/client-v2/hooks/useApiRequest.ts +8 -1
  60. package/src/client-v2/pages/RulesPage.tsx +2 -2
  61. package/src/client-v2/plugin.tsx +3 -3
  62. package/src/locale/en-US.json +94 -25
  63. package/src/locale/vi-VN.json +94 -25
  64. package/src/locale/zh-CN.json +94 -25
  65. package/src/server/__tests__/native-sub-agent-observer.test.ts +246 -0
  66. package/src/server/__tests__/skill-settings.test.ts +6 -6
  67. package/src/server/__tests__/smoke.test.ts +1 -0
  68. package/src/server/collections/agent-execution-spans.ts +37 -0
  69. package/src/server/collections/agent-harness-profiles.ts +59 -59
  70. package/src/server/collections/agent-loop-events.ts +71 -71
  71. package/src/server/collections/agent-loop-steps.ts +144 -144
  72. package/src/server/collections/agent-memory-contexts.ts +95 -0
  73. package/src/server/collections/orchestrator-logs.ts +4 -4
  74. package/src/server/collections/skill-definitions.ts +111 -111
  75. package/src/server/collections/skill-executions.ts +106 -106
  76. package/src/server/collections/skill-loop-configs.ts +65 -65
  77. package/src/server/migrations/20260423000000-add-progress-fields.ts +14 -14
  78. package/src/server/migrations/20260425000000-add-interaction-schema.ts +3 -1
  79. package/src/server/migrations/20260427000000-change-packages-to-text.ts +4 -2
  80. package/src/server/migrations/20260427000001-change-other-json-to-text.ts +9 -5
  81. package/src/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.ts +30 -30
  82. package/src/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.ts +145 -142
  83. package/src/server/migrations/20260615000000-normalize-ai-employee-tool-bindings.ts +2 -2
  84. package/src/server/migrations/20260621000000-native-policy-profile-defaults.ts +193 -0
  85. package/src/server/plugin.ts +151 -94
  86. package/src/server/resources/agent-monitor.ts +482 -0
  87. package/src/server/services/AgentHarness.ts +38 -623
  88. package/src/server/services/AgentMemoryContextService.ts +256 -0
  89. package/src/server/services/AgentPlanValidator.ts +73 -73
  90. package/src/server/services/ExecutionSpanService.ts +6 -2
  91. package/src/server/services/FileManager.ts +144 -144
  92. package/src/server/services/NativeSubAgentObserver.ts +507 -0
  93. package/src/server/services/SkillManager.ts +583 -583
  94. package/src/server/services/SkillRepositoryService.ts +5 -7
  95. package/src/server/services/TokenTracker.ts +3 -3
  96. package/src/server/services/WorkerEnvManager.ts +1 -2
  97. package/src/server/skill-hub/actions/git-import.ts +5 -7
  98. package/src/server/skill-hub/mcp/McpController.ts +41 -14
  99. package/src/server/skill-hub/plugin.ts +89 -6
  100. package/src/server/skill-hub/tasks/SkillExecutionTask.ts +470 -460
  101. package/src/server/skill-hub/utils/json-fields.ts +1 -1
  102. package/src/server/tools/delegate-task.ts +13 -847
  103. package/src/server/utils/skill-settings.ts +24 -6
  104. package/dist/client-v2/264.0533912e6c5ea2d7.js +0 -10
  105. package/dist/client-v2/418.5ae055abf141820e.js +0 -10
  106. package/dist/client-v2/619.d99d3c9e61c99064.js +0 -10
  107. package/dist/client-v2/892.72db4161511c8a16.js +0 -10
  108. package/dist/client-v2/926.87f660b670d85bcc.js +0 -10
  109. package/src/client/tools/PlanApprovalCard.tsx +0 -176
  110. package/src/client/tools/registerOrchestratorCards.ts +0 -17
@@ -1,106 +1,106 @@
1
- import { CollectionOptions } from '@nocobase/database';
2
-
3
- export default {
4
- name: 'skillExecutions',
5
- title: 'Skill Executions',
6
- fields: [
7
- {
8
- name: 'id',
9
- type: 'bigInt',
10
- autoIncrement: true,
11
- primaryKey: true,
12
- },
13
- {
14
- name: 'skill',
15
- type: 'belongsTo',
16
- target: 'skillDefinitions',
17
- foreignKey: 'skillId',
18
- },
19
- {
20
- // 'pending' | 'running' | 'succeeded' | 'failed' | 'canceled' | 'timeout'
21
- name: 'status',
22
- type: 'string',
23
- length: 20,
24
- defaultValue: 'pending',
25
- },
26
- {
27
- name: 'inputArgs',
28
- type: 'text',
29
- },
30
- {
31
- // Final code after template rendering
32
- name: 'executedCode',
33
- type: 'text',
34
- },
35
- {
36
- name: 'stdout',
37
- type: 'text',
38
- },
39
- {
40
- name: 'stderr',
41
- type: 'text',
42
- },
43
- {
44
- // [{ name, size, mimeType }]
45
- name: 'outputFiles',
46
- type: 'text',
47
- defaultValue: null,
48
- },
49
- {
50
- name: 'durationMs',
51
- type: 'integer',
52
- },
53
- {
54
- name: 'sessionId',
55
- type: 'string',
56
- length: 100,
57
- allowNull: true,
58
- },
59
- {
60
- name: 'orchestratorRootRunId',
61
- type: 'string',
62
- length: 100,
63
- allowNull: true,
64
- },
65
- {
66
- name: 'orchestratorSpanId',
67
- type: 'string',
68
- length: 100,
69
- allowNull: true,
70
- },
71
- {
72
- name: 'orchestratorParentSpanId',
73
- type: 'string',
74
- length: 100,
75
- allowNull: true,
76
- },
77
- {
78
- name: 'orchestratorToolCallId',
79
- type: 'string',
80
- length: 100,
81
- allowNull: true,
82
- },
83
- {
84
- name: 'agentLoopRunId',
85
- type: 'string',
86
- length: 100,
87
- allowNull: true,
88
- },
89
- {
90
- name: 'agentLoopStepId',
91
- type: 'string',
92
- length: 100,
93
- allowNull: true,
94
- },
95
- {
96
- name: 'triggeredBy',
97
- type: 'belongsTo',
98
- target: 'users',
99
- foreignKey: 'triggeredById',
100
- },
101
- {
102
- name: 'createdAt',
103
- type: 'date',
104
- },
105
- ],
106
- } as CollectionOptions;
1
+ import { CollectionOptions } from '@nocobase/database';
2
+
3
+ export default {
4
+ name: 'skillExecutions',
5
+ title: 'Skill Executions',
6
+ fields: [
7
+ {
8
+ name: 'id',
9
+ type: 'bigInt',
10
+ autoIncrement: true,
11
+ primaryKey: true,
12
+ },
13
+ {
14
+ name: 'skill',
15
+ type: 'belongsTo',
16
+ target: 'skillDefinitions',
17
+ foreignKey: 'skillId',
18
+ },
19
+ {
20
+ // 'pending' | 'running' | 'succeeded' | 'failed' | 'canceled' | 'timeout'
21
+ name: 'status',
22
+ type: 'string',
23
+ length: 20,
24
+ defaultValue: 'pending',
25
+ },
26
+ {
27
+ name: 'inputArgs',
28
+ type: 'text',
29
+ },
30
+ {
31
+ // Final code after template rendering
32
+ name: 'executedCode',
33
+ type: 'text',
34
+ },
35
+ {
36
+ name: 'stdout',
37
+ type: 'text',
38
+ },
39
+ {
40
+ name: 'stderr',
41
+ type: 'text',
42
+ },
43
+ {
44
+ // [{ name, size, mimeType }]
45
+ name: 'outputFiles',
46
+ type: 'text',
47
+ defaultValue: null,
48
+ },
49
+ {
50
+ name: 'durationMs',
51
+ type: 'integer',
52
+ },
53
+ {
54
+ name: 'sessionId',
55
+ type: 'string',
56
+ length: 100,
57
+ allowNull: true,
58
+ },
59
+ {
60
+ name: 'orchestratorRootRunId',
61
+ type: 'string',
62
+ length: 100,
63
+ allowNull: true,
64
+ },
65
+ {
66
+ name: 'orchestratorSpanId',
67
+ type: 'string',
68
+ length: 100,
69
+ allowNull: true,
70
+ },
71
+ {
72
+ name: 'orchestratorParentSpanId',
73
+ type: 'string',
74
+ length: 100,
75
+ allowNull: true,
76
+ },
77
+ {
78
+ name: 'orchestratorToolCallId',
79
+ type: 'string',
80
+ length: 100,
81
+ allowNull: true,
82
+ },
83
+ {
84
+ name: 'agentLoopRunId',
85
+ type: 'string',
86
+ length: 100,
87
+ allowNull: true,
88
+ },
89
+ {
90
+ name: 'agentLoopStepId',
91
+ type: 'string',
92
+ length: 100,
93
+ allowNull: true,
94
+ },
95
+ {
96
+ name: 'triggeredBy',
97
+ type: 'belongsTo',
98
+ target: 'users',
99
+ foreignKey: 'triggeredById',
100
+ },
101
+ {
102
+ name: 'createdAt',
103
+ type: 'date',
104
+ },
105
+ ],
106
+ } as CollectionOptions;
@@ -1,65 +1,65 @@
1
- import { CollectionOptions } from '@nocobase/database';
2
-
3
- export default {
4
- name: 'skillLoopConfigs',
5
- title: 'Skill Loop Configs',
6
- fields: [
7
- {
8
- name: 'id',
9
- type: 'bigInt',
10
- autoIncrement: true,
11
- primaryKey: true,
12
- },
13
- {
14
- name: 'skill',
15
- type: 'belongsTo',
16
- target: 'skillDefinitions',
17
- foreignKey: 'skillId',
18
- },
19
- {
20
- name: 'enabled',
21
- type: 'boolean',
22
- defaultValue: true,
23
- },
24
- {
25
- name: 'title',
26
- type: 'string',
27
- length: 200,
28
- },
29
- {
30
- name: 'templateKey',
31
- type: 'string',
32
- length: 80,
33
- defaultValue: 'confirm',
34
- },
35
- {
36
- name: 'prompt',
37
- type: 'text',
38
- },
39
- {
40
- // Resolved interaction schema consumed by the generic Skill Hub UI card.
41
- name: 'schema',
42
- type: 'text',
43
- },
44
- {
45
- // Optional template-specific state for future loop renderers.
46
- name: 'config',
47
- type: 'text',
48
- defaultValue: null,
49
- },
50
- {
51
- name: 'createdAt',
52
- type: 'date',
53
- },
54
- {
55
- name: 'updatedAt',
56
- type: 'date',
57
- },
58
- {
59
- name: 'createdBy',
60
- type: 'belongsTo',
61
- target: 'users',
62
- foreignKey: 'createdById',
63
- },
64
- ],
65
- } as CollectionOptions;
1
+ import { CollectionOptions } from '@nocobase/database';
2
+
3
+ export default {
4
+ name: 'skillLoopConfigs',
5
+ title: 'Skill Loop Configs',
6
+ fields: [
7
+ {
8
+ name: 'id',
9
+ type: 'bigInt',
10
+ autoIncrement: true,
11
+ primaryKey: true,
12
+ },
13
+ {
14
+ name: 'skill',
15
+ type: 'belongsTo',
16
+ target: 'skillDefinitions',
17
+ foreignKey: 'skillId',
18
+ },
19
+ {
20
+ name: 'enabled',
21
+ type: 'boolean',
22
+ defaultValue: true,
23
+ },
24
+ {
25
+ name: 'title',
26
+ type: 'string',
27
+ length: 200,
28
+ },
29
+ {
30
+ name: 'templateKey',
31
+ type: 'string',
32
+ length: 80,
33
+ defaultValue: 'confirm',
34
+ },
35
+ {
36
+ name: 'prompt',
37
+ type: 'text',
38
+ },
39
+ {
40
+ // Resolved interaction schema consumed by the generic Skill Hub UI card.
41
+ name: 'schema',
42
+ type: 'text',
43
+ },
44
+ {
45
+ // Optional template-specific state for future loop renderers.
46
+ name: 'config',
47
+ type: 'text',
48
+ defaultValue: null,
49
+ },
50
+ {
51
+ name: 'createdAt',
52
+ type: 'date',
53
+ },
54
+ {
55
+ name: 'updatedAt',
56
+ type: 'date',
57
+ },
58
+ {
59
+ name: 'createdBy',
60
+ type: 'belongsTo',
61
+ target: 'users',
62
+ foreignKey: 'createdById',
63
+ },
64
+ ],
65
+ } as CollectionOptions;
@@ -19,7 +19,7 @@ export default class AddProgressFieldsMigration extends Migration {
19
19
 
20
20
  const fieldsToSync = [
21
21
  { name: 'initProgressPercent', type: 'integer', defaultValue: 0 },
22
- { name: 'initProgressLog', type: 'text' }
22
+ { name: 'initProgressLog', type: 'text' },
23
23
  ];
24
24
 
25
25
  for (const f of fieldsToSync) {
@@ -28,19 +28,19 @@ export default class AddProgressFieldsMigration extends Migration {
28
28
  });
29
29
 
30
30
  if (!fieldMeta && tableDesc[f.name]) {
31
- // The physical column exists, but NocoBase metadata is missing it!
32
- // Let's create the metadata right now BEFORE NocoBase's collection.sync()
33
- // runs and crashes on addColumn.
34
- await fieldRepo.create({
35
- values: {
36
- name: f.name,
37
- type: f.type,
38
- collectionName,
39
- // Avoid trying to physically add it because it already exists
40
- interface: f.type,
41
- }
42
- });
43
- (this as any).app.logger.info(`[skill-hub] Restored NocoBase metadata for preexisting column ${f.name}`);
31
+ // The physical column exists, but NocoBase metadata is missing it!
32
+ // Let's create the metadata right now BEFORE NocoBase's collection.sync()
33
+ // runs and crashes on addColumn.
34
+ await fieldRepo.create({
35
+ values: {
36
+ name: f.name,
37
+ type: f.type,
38
+ collectionName,
39
+ // Avoid trying to physically add it because it already exists
40
+ interface: f.type,
41
+ },
42
+ });
43
+ (this as any).app.logger.info(`[skill-hub] Restored NocoBase metadata for preexisting column ${f.name}`);
44
44
  }
45
45
  }
46
46
  } catch (error) {
@@ -26,7 +26,9 @@ export default class AddInteractionSchemaMigration extends Migration {
26
26
  interface: 'textarea',
27
27
  },
28
28
  });
29
- (this as any).app.logger.info('[skill-hub] Restored NocoBase metadata for preexisting column interactionSchema');
29
+ (this as any).app.logger.info(
30
+ '[skill-hub] Restored NocoBase metadata for preexisting column interactionSchema',
31
+ );
30
32
  }
31
33
  } catch (error) {
32
34
  (this as any).app.logger.error(`[skill-hub] Failed to check interactionSchema field: ${error.message}`);
@@ -19,13 +19,15 @@ export default class ChangePackagesToTextMigration extends Migration {
19
19
  // Change physical column type in Postgres if needed
20
20
  const dialect = (this as any).db.sequelize.getDialect();
21
21
  if (dialect === 'postgres') {
22
- await (this as any).db.sequelize.query(`ALTER TABLE "${tableName}" ALTER COLUMN "${col}" TYPE text USING "${col}"::text;`);
22
+ await (this as any).db.sequelize.query(
23
+ `ALTER TABLE "${tableName}" ALTER COLUMN "${col}" TYPE text USING "${col}"::text;`,
24
+ );
23
25
  } else {
24
26
  await queryInterface.changeColumn(tableName, col, {
25
27
  type: 'TEXT',
26
28
  });
27
29
  }
28
-
30
+
29
31
  // Also update NocoBase metadata
30
32
  const fieldMeta = await fieldRepo.findOne({
31
33
  filter: { name: col, collectionName },
@@ -7,18 +7,20 @@ export default class ChangeOtherJsonToTextMigration extends Migration {
7
7
  const fieldRepo = (this as any).db.getRepository('fields');
8
8
 
9
9
  const tablePrefix = (this as any).db.options.tablePrefix || '';
10
-
10
+
11
11
  // 1. skillWorkerConfigs
12
12
  const workerTableName = `${tablePrefix}skillWorkerConfigs`;
13
13
  try {
14
14
  if (await queryInterface.tableExists(workerTableName)) {
15
15
  const tableDesc = await queryInterface.describeTable(workerTableName);
16
16
  const columns = ['packageWhitelist', 'customPackages'];
17
-
17
+
18
18
  for (const col of columns) {
19
19
  if (tableDesc[col]) {
20
20
  if (dialect === 'postgres') {
21
- await (this as any).db.sequelize.query(`ALTER TABLE "${workerTableName}" ALTER COLUMN "${col}" TYPE text USING "${col}"::text;`);
21
+ await (this as any).db.sequelize.query(
22
+ `ALTER TABLE "${workerTableName}" ALTER COLUMN "${col}" TYPE text USING "${col}"::text;`,
23
+ );
22
24
  } else {
23
25
  await queryInterface.changeColumn(workerTableName, col, { type: 'TEXT' });
24
26
  }
@@ -38,10 +40,12 @@ export default class ChangeOtherJsonToTextMigration extends Migration {
38
40
  if (await queryInterface.tableExists(execTableName)) {
39
41
  const tableDesc = await queryInterface.describeTable(execTableName);
40
42
  const col = 'outputFiles';
41
-
43
+
42
44
  if (tableDesc[col]) {
43
45
  if (dialect === 'postgres') {
44
- await (this as any).db.sequelize.query(`ALTER TABLE "${execTableName}" ALTER COLUMN "${col}" TYPE text USING "${col}"::text;`);
46
+ await (this as any).db.sequelize.query(
47
+ `ALTER TABLE "${execTableName}" ALTER COLUMN "${col}" TYPE text USING "${col}"::text;`,
48
+ );
45
49
  } else {
46
50
  await queryInterface.changeColumn(execTableName, col, { type: 'TEXT' });
47
51
  }
@@ -1,30 +1,30 @@
1
- import { Migration } from '@nocobase/server';
2
-
3
- export default class AddAgentLoopFieldsToSkillExecutions extends Migration {
4
- on = 'afterLoad';
5
- appVersion = '>=0.1.0';
6
-
7
- async up() {
8
- const queryInterface = (this as any).db.sequelize.getQueryInterface();
9
- const tablePrefix = (this as any).db.options.tablePrefix || '';
10
- const tableName = `${tablePrefix}skillExecutions`;
11
- const tableExists = await queryInterface.tableExists(tableName).catch(() => false);
12
- if (!tableExists) return;
13
-
14
- const tableDesc = await queryInterface.describeTable(tableName);
15
- const addIfMissing = async (name: string) => {
16
- if (tableDesc[name]) return;
17
- await queryInterface.addColumn(tableName, name, {
18
- type: 'VARCHAR(100)',
19
- allowNull: true,
20
- });
21
- };
22
-
23
- await addIfMissing('agentLoopRunId');
24
- await addIfMissing('agentLoopStepId');
25
- }
26
-
27
- async down() {
28
- // No rollback: these nullable trace-link columns are backward compatible.
29
- }
30
- }
1
+ import { Migration } from '@nocobase/server';
2
+
3
+ export default class AddAgentLoopFieldsToSkillExecutions extends Migration {
4
+ on = 'afterLoad';
5
+ appVersion = '>=0.1.0';
6
+
7
+ async up() {
8
+ const queryInterface = (this as any).db.sequelize.getQueryInterface();
9
+ const tablePrefix = (this as any).db.options.tablePrefix || '';
10
+ const tableName = `${tablePrefix}skillExecutions`;
11
+ const tableExists = await queryInterface.tableExists(tableName).catch(() => false);
12
+ if (!tableExists) return;
13
+
14
+ const tableDesc = await queryInterface.describeTable(tableName);
15
+ const addIfMissing = async (name: string) => {
16
+ if (tableDesc[name]) return;
17
+ await queryInterface.addColumn(tableName, name, {
18
+ type: 'VARCHAR(100)',
19
+ allowNull: true,
20
+ });
21
+ };
22
+
23
+ await addIfMissing('agentLoopRunId');
24
+ await addIfMissing('agentLoopStepId');
25
+ }
26
+
27
+ async down() {
28
+ // No rollback: these nullable trace-link columns are backward compatible.
29
+ }
30
+ }