@reactive-skills/runtime 0.4.0 โ†’ 0.4.1

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
@@ -1,4 +1,12 @@
1
1
 
2
+ ## [0.4.1] - 2026-09-14
3
+
4
+ - feat(runtime,axi): add mcp job tools, template jobId context, and flexible jobs cli arguments (2b31451)
5
+ - feat(mcp): add reactive_switch_job and reactive_archive_job tools with active job indicators
6
+ - feat(projections): expose jobId directly in ProjectionContext for Handlebars templates
7
+ - feat(axi): support bidirectional argument ordering in jobs command (jobs list <skill> and jobs <skill> list)
8
+ - docs: introduce PERFORMANCE-STANDARDS.md for P0-P3 latency budgets, caching tiers, and CRAP score limits
9
+
2
10
  ## [0.4.0] - 2026-09-14
3
11
 
4
12
  - feat(axi): implement native validate command for skill manifest and statechart verification (3b75a21)
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Reactive Skills Architecture (RSA) core runtime โ€” FSM engine, event store, guard evaluator, projection engine, job manager, and MCP server.
4
4
 
5
- > ๐Ÿš€ **What's New in v0.4.0:** Native `validate` command in AXI, first-class Job & Run Management, isolated event logs, and dual-write deliverable mirroring. [Read Full Release Notes โ†’](https://github.com/Reactive-Skills/reactive-skills/releases/tag/v0.4.0) ยท [View Changelog](https://github.com/Reactive-Skills/reactive-skills/blob/main/CHANGELOG.md)
5
+ > ๐Ÿš€ **What's New in v0.4.1:** MCP job management tools (`reactive_switch_job`, `reactive_archive_job`), template-level `jobId` context, and `PERFORMANCE-STANDARDS.md`. [Read Full Release Notes โ†’](https://github.com/Reactive-Skills/reactive-skills/releases/tag/v0.4.1) ยท [View Changelog](https://github.com/Reactive-Skills/reactive-skills/blob/main/CHANGELOG.md)
6
6
 
7
7
  ## Installation
8
8
 
@@ -3,6 +3,7 @@ import { EventStore } from './event-store.js';
3
3
  export interface ProjectionContext {
4
4
  skillName: string;
5
5
  currentState: string;
6
+ jobId?: string;
6
7
  context: Record<string, any>;
7
8
  events: SignalEvent[];
8
9
  transitions: {
@@ -125,6 +125,7 @@ export class ProjectionEngine {
125
125
  const projContext = {
126
126
  skillName,
127
127
  currentState,
128
+ jobId: this.jobId,
128
129
  context,
129
130
  events,
130
131
  transitions,
@@ -455,7 +455,10 @@ export function createReactiveMcpServer(options = {}) {
455
455
  const targetSkill = skill || defaultSkill;
456
456
  const jobManager = new JobManager(workspaceDir);
457
457
  const activeJobId = jobManager.getActiveJobId(targetSkill);
458
- const jobs = jobManager.listJobs(targetSkill);
458
+ const jobs = jobManager.listJobs(targetSkill).map(j => ({
459
+ ...j,
460
+ isActive: j.id === activeJobId,
461
+ }));
459
462
  return {
460
463
  content: [
461
464
  {
@@ -476,6 +479,106 @@ export function createReactiveMcpServer(options = {}) {
476
479
  };
477
480
  }
478
481
  });
482
+ // 10. TOOL: reactive_switch_job
483
+ server.tool('reactive_switch_job', 'Switch the active execution job pointer for a reactive skill and synchronize deliverables', {
484
+ skill: z.string().optional().describe('Name of the reactive skill (defaults to active skill)'),
485
+ job_id: z.string().describe('Target job ID to set as active'),
486
+ }, async ({ skill, job_id }) => {
487
+ try {
488
+ const skillName = skill || defaultSkill;
489
+ const jobManager = new JobManager(workspaceDir);
490
+ const job = jobManager.getJob(skillName, job_id);
491
+ if (!job) {
492
+ return {
493
+ content: [{ type: 'text', text: JSON.stringify({ error: `Job '${job_id}' not found for skill '${skillName}'` }) }],
494
+ isError: true,
495
+ };
496
+ }
497
+ jobManager.setActiveJobId(skillName, job_id);
498
+ // Re-mirror deliverables: copy archive deliverables to root if they exist
499
+ const docsDir = path.join(workspaceDir, '.docs', skillName);
500
+ const archiveDir = path.join(docsDir, 'jobs', job_id);
501
+ let mirroredCount = 0;
502
+ if (fs.existsSync(archiveDir)) {
503
+ const files = fs.readdirSync(archiveDir);
504
+ for (const file of files) {
505
+ const src = path.join(archiveDir, file);
506
+ if (fs.statSync(src).isFile()) {
507
+ const dest = path.join(docsDir, file);
508
+ fs.copyFileSync(src, dest);
509
+ mirroredCount++;
510
+ }
511
+ }
512
+ }
513
+ return {
514
+ content: [
515
+ {
516
+ type: 'text',
517
+ text: JSON.stringify({
518
+ skill: skillName,
519
+ activeJobId: job_id,
520
+ status: job.status,
521
+ currentState: job.currentState,
522
+ mirroredDeliverables: mirroredCount,
523
+ }, null, 2),
524
+ },
525
+ ],
526
+ };
527
+ }
528
+ catch (err) {
529
+ return {
530
+ content: [{ type: 'text', text: JSON.stringify({ error: err.message }) }],
531
+ isError: true,
532
+ };
533
+ }
534
+ });
535
+ // 11. TOOL: reactive_archive_job
536
+ server.tool('reactive_archive_job', 'Archive an execution job for a reactive skill and rotate active pointer if archived was active', {
537
+ skill: z.string().optional().describe('Name of the reactive skill (defaults to active skill)'),
538
+ job_id: z.string().optional().describe('Job ID to archive (defaults to active job)'),
539
+ }, async ({ skill, job_id }) => {
540
+ try {
541
+ const skillName = skill || defaultSkill;
542
+ const jobManager = new JobManager(workspaceDir);
543
+ const targetId = job_id || jobManager.getActiveJobId(skillName);
544
+ const currentActive = jobManager.getActiveJobId(skillName);
545
+ const job = jobManager.getJob(skillName, targetId);
546
+ if (!job) {
547
+ return {
548
+ content: [{ type: 'text', text: JSON.stringify({ error: `Job '${targetId}' not found for skill '${skillName}'` }) }],
549
+ isError: true,
550
+ };
551
+ }
552
+ jobManager.updateJob(skillName, targetId, {
553
+ status: 'archived',
554
+ completedAt: new Date().toISOString(),
555
+ });
556
+ let freshJobId = currentActive;
557
+ if (currentActive === targetId) {
558
+ const freshJob = jobManager.createJob(skillName, { setActive: true });
559
+ freshJobId = freshJob.id;
560
+ }
561
+ return {
562
+ content: [
563
+ {
564
+ type: 'text',
565
+ text: JSON.stringify({
566
+ skill: skillName,
567
+ archivedJobId: targetId,
568
+ status: 'archived',
569
+ activeJobId: freshJobId,
570
+ }, null, 2),
571
+ },
572
+ ],
573
+ };
574
+ }
575
+ catch (err) {
576
+ return {
577
+ content: [{ type: 'text', text: JSON.stringify({ error: err.message }) }],
578
+ isError: true,
579
+ };
580
+ }
581
+ });
479
582
  // RESOURCE 1: reactive://events
480
583
  server.resource('reactive-events', 'reactive://events', async (uri) => {
481
584
  const store = new EventStore({ enableSqlite: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactive-skills/runtime",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Reactive Skills Architecture (RSA) core runtime โ€” FSM engine, event store, guard evaluator, projection engine, MCP server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",