claude-multi-session 2.3.1 → 2.3.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-multi-session",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
4
4
  "description": "Multi-session orchestrator for Claude Code CLI — spawn, control, pause, resume, and send multiple inputs to Claude Code sessions programmatically",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -469,6 +469,45 @@ class ArtifactStore {
469
469
  }
470
470
  }
471
471
 
472
+ /**
473
+ * Track that a session read an artifact
474
+ * @param {string} artifactId - The artifact that was read
475
+ * @param {string} reader - The session name that read it
476
+ * @param {number} version - The version that was read
477
+ */
478
+ trackRead(artifactId, reader, version) {
479
+ if (!reader) return; // Skip if no reader identified
480
+
481
+ const readsPath = path.join(this.dataDir, artifactId, 'reads.json');
482
+
483
+ // Read existing reads log
484
+ let reads = readJsonSafe(readsPath, []);
485
+
486
+ // Add this read event
487
+ reads.push({
488
+ reader,
489
+ version,
490
+ readAt: new Date().toISOString(),
491
+ });
492
+
493
+ // Write back (not immutable — reads are append-only log)
494
+ const dir = path.join(this.dataDir, artifactId);
495
+ if (!fs.existsSync(dir)) {
496
+ fs.mkdirSync(dir, { recursive: true });
497
+ }
498
+ fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2));
499
+ }
500
+
501
+ /**
502
+ * Get the read log for an artifact
503
+ * @param {string} artifactId - The artifact to check
504
+ * @returns {Array} Array of read events: [{ reader, version, readAt }]
505
+ */
506
+ getReads(artifactId) {
507
+ const readsPath = path.join(this.dataDir, artifactId, 'reads.json');
508
+ return readJsonSafe(readsPath, []);
509
+ }
510
+
472
511
  /**
473
512
  * List artifacts with optional filtering
474
513
  * @param {Object} [filters={}] - Filter criteria
@@ -497,6 +536,17 @@ class ArtifactStore {
497
536
  results = results.filter(item => item.tags && item.tags.includes(tag));
498
537
  }
499
538
 
539
+ // Enrich with read counts
540
+ results = results.map(item => {
541
+ const reads = this.getReads(item.artifactId);
542
+ const uniqueReaders = [...new Set(reads.map(r => r.reader))];
543
+ return {
544
+ ...item,
545
+ readCount: reads.length,
546
+ uniqueReaders,
547
+ };
548
+ });
549
+
500
550
  return results;
501
551
  }
502
552
 
package/src/mcp-server.js CHANGED
@@ -561,6 +561,7 @@ const TOOLS = [
561
561
  properties: {
562
562
  artifactId: { type: 'string', description: 'Artifact ID to retrieve' },
563
563
  version: { type: 'number', description: 'Specific version number (omit for latest)' },
564
+ reader: { type: 'string', description: 'Session name reading this artifact (for tracking who consumed it)' },
564
565
  team: { type: 'string', description: 'Team name (default: "default")' },
565
566
  },
566
567
  required: ['artifactId'],
@@ -582,6 +583,20 @@ const TOOLS = [
582
583
  },
583
584
  },
584
585
 
586
+ {
587
+ name: 'artifact_readers',
588
+ description:
589
+ 'Check who has read a specific artifact. Use this to verify that workers actually consumed shared artifacts like conventions or schemas.',
590
+ inputSchema: {
591
+ type: 'object',
592
+ properties: {
593
+ artifactId: { type: 'string', description: 'Artifact ID to check readers for' },
594
+ team: { type: 'string', description: 'Team name (default: "default")' },
595
+ },
596
+ required: ['artifactId'],
597
+ },
598
+ },
599
+
585
600
  {
586
601
  name: 'artifact_history',
587
602
  description:
@@ -1161,6 +1176,8 @@ async function executeTool(toolName, args) {
1161
1176
  return handleArtifactGet(args);
1162
1177
  case 'artifact_list':
1163
1178
  return handleArtifactList(args);
1179
+ case 'artifact_readers':
1180
+ return handleArtifactReaders(args);
1164
1181
  case 'artifact_history':
1165
1182
  return handleArtifactHistory(args);
1166
1183
 
@@ -1867,6 +1884,11 @@ function handleArtifactGet(args) {
1867
1884
  return errorResult(`Artifact ${args.artifactId} not found`);
1868
1885
  }
1869
1886
 
1887
+ // Track this read if a reader was specified
1888
+ if (args.reader) {
1889
+ artifactStore.trackRead(args.artifactId, args.reader, artifact.version);
1890
+ }
1891
+
1870
1892
  return textResult(JSON.stringify({
1871
1893
  artifactId: artifact.artifactId,
1872
1894
  version: artifact.version,
@@ -1878,6 +1900,7 @@ function handleArtifactGet(args) {
1878
1900
  summary: artifact.summary,
1879
1901
  lineage: artifact.lineage,
1880
1902
  team: teamName,
1903
+ readBy: artifactStore.getReads(args.artifactId),
1881
1904
  }, null, 2));
1882
1905
  } catch (err) {
1883
1906
  return errorResult(err.message);
@@ -1908,6 +1931,8 @@ function handleArtifactList(args) {
1908
1931
  createdAt: a.createdAt,
1909
1932
  updatedAt: a.updatedAt,
1910
1933
  tags: a.tags,
1934
+ readCount: a.readCount,
1935
+ uniqueReaders: a.uniqueReaders,
1911
1936
  })),
1912
1937
  }, null, 2));
1913
1938
  } catch (err) {
@@ -1915,6 +1940,25 @@ function handleArtifactList(args) {
1915
1940
  }
1916
1941
  }
1917
1942
 
1943
+ function handleArtifactReaders(args) {
1944
+ try {
1945
+ const teamName = args.team || 'default';
1946
+ const { artifactStore } = getTeamInstances(teamName);
1947
+
1948
+ const reads = artifactStore.getReads(args.artifactId);
1949
+ const uniqueReaders = [...new Set(reads.map(r => r.reader))];
1950
+
1951
+ return textResult(JSON.stringify({
1952
+ artifactId: args.artifactId,
1953
+ totalReads: reads.length,
1954
+ uniqueReaders,
1955
+ reads,
1956
+ }, null, 2));
1957
+ } catch (err) {
1958
+ return errorResult(err.message);
1959
+ }
1960
+ }
1961
+
1918
1962
  function handleArtifactHistory(args) {
1919
1963
  try {
1920
1964
  const teamName = args.team || 'default';
package/src/prompts.js CHANGED
@@ -66,12 +66,20 @@ Your inbox may contain:
66
66
  - Dependency artifacts you need before starting
67
67
  - Updated instructions from the orchestrator
68
68
 
69
- ### Step 1.5: CHECK FOR SHARED CONVENTIONS
70
- Call \`mcp__multi-session__artifact_list\` and look for a conventions or API contract artifact.
71
- If one exists, call \`mcp__multi-session__artifact_get\` to read it and follow those conventions STRICTLY.
72
- Conventions may define: response format, status codes, naming rules, error format, file path rules.
69
+ ### Step 1.5: CHECK FOR SHARED CONVENTIONS AND DEPENDENCY ARTIFACTS
70
+ Call \`mcp__multi-session__artifact_list\` and look for:
71
+ - A conventions or API contract artifact (e.g., "shared-conventions")
72
+ - Any dependency artifacts your task requires (e.g., "db-schema" if you're building routes)
73
73
 
74
- If NO convention artifact exists AND your work must match other workers' output:
74
+ If found, call \`mcp__multi-session__artifact_get\` with your session name as the \`reader\` parameter:
75
+ \`\`\`
76
+ mcp__multi-session__artifact_get({ artifactId: "shared-conventions", reader: "${name}" })
77
+ mcp__multi-session__artifact_get({ artifactId: "db-schema", reader: "${name}" })
78
+ \`\`\`
79
+
80
+ IMPORTANT: The \`reader\` parameter tracks that you consumed the artifact. This is how the orchestrator verifies workers actually read shared data. ALWAYS include your session name as \`reader\`.
81
+
82
+ Follow the conventions STRICTLY. If NO convention artifact exists AND your work must match other workers' output:
75
83
  - Use \`team_ask\` to ask the relevant teammate about their format BEFORE writing code
76
84
  - NEVER guess or assume format — mismatches cause test failures
77
85
 
@@ -517,6 +525,29 @@ mcp__multi-session__artifact_publish({
517
525
 
518
526
  NEVER assume workers will independently agree on conventions. Define them explicitly.
519
527
 
528
+ ### Phase Gate: VERIFY Before Spawning
529
+ === CRITICAL: MANDATORY VERIFICATION STEP ===
530
+ Before spawning ANY worker that depends on a previous phase's output, you MUST:
531
+ 1. Call \`artifact_list()\` to confirm the dependency artifact was published
532
+ 2. Call \`artifact_get(artifactId)\` to confirm the artifact contains valid data
533
+ 3. ONLY THEN spawn the dependent workers
534
+
535
+ Example phased workflow:
536
+ \`\`\`
537
+ // Phase 1: Spawn the database worker
538
+ team_spawn({ name: "db-worker", ... })
539
+
540
+ // PHASE GATE: Verify before Phase 2
541
+ artifact_list() // Confirm "db-schema" appears
542
+ artifact_get({ artifactId: "db-schema" }) // Confirm it has valid table definitions
543
+
544
+ // Phase 2: NOW spawn route workers (they depend on the schema)
545
+ team_spawn({ name: "api-worker", ... })
546
+ team_spawn({ name: "test-worker", ... })
547
+ \`\`\`
548
+
549
+ NEVER skip verification. NEVER rely on a worker's self-reported completion — verify the artifact exists yourself.
550
+
520
551
  ### Phase 2: Spawn Workers (use team_spawn, NOT delegate_task)
521
552
  Spawn all independent workers at once. Example:
522
553
 
@@ -726,6 +757,7 @@ const ROLE_PROMPTS = {
726
757
  - Publish test results as artifacts with type "test-results" including: passed, failed, skipped counts and failure details
727
758
  - If tests fail due to bugs in another session's code, use \`team_send_message\` to notify them with the exact error and file path
728
759
  - Use the correct status values as defined in the database schema (e.g., "in_progress" not "in-progress")
760
+ - IMPORTANT: When calling artifact_get, ALWAYS include reader: "<your-session-name>" so the orchestrator can verify you read the artifacts
729
761
  `,
730
762
 
731
763
  'database': `
@@ -822,6 +854,8 @@ IMPORTANT: You are the ORCHESTRATOR. Your job is to PLAN, SPAWN, and MONITOR —
822
854
 
823
855
  4. **Set up dependencies** — Use \`contract_create\` if Task B needs output from Task A.
824
856
 
857
+ 4.5. **Phase Gate** — Before spawning workers that depend on previous workers' output, VERIFY the dependency artifact exists by calling \`artifact_list()\` and \`artifact_get()\`. Never trust self-reported completion — verify the artifact.
858
+
825
859
  5. **Monitor** — Check progress with \`team_roster()\`, \`contract_list()\`, \`artifact_list()\`. Only intervene when a worker is BLOCKED or FAILED.
826
860
 
827
861
  6. **Collect** — When all workers are idle, check \`artifact_list\` for published outputs and summarize results for the user.
@@ -917,6 +951,19 @@ mcp__multi-session__contract_list() — See contract statuses
917
951
  mcp__multi-session__artifact_list() — See published outputs
918
952
  \`\`\`
919
953
 
954
+ ### Phase Gate Verification
955
+ Between spawn phases, use these to verify dependencies:
956
+ \`\`\`
957
+ mcp__multi-session__artifact_get({ artifactId: "db-schema" }) — Verify artifact content
958
+ mcp__multi-session__artifact_readers({ artifactId: "db-schema" }) — Check who read it
959
+ \`\`\`
960
+
961
+ After all workers complete, verify every worker consumed the artifacts they needed:
962
+ \`\`\`
963
+ mcp__multi-session__artifact_readers({ artifactId: "shared-conventions" })
964
+ // Should list ALL route workers as readers
965
+ \`\`\`
966
+
920
967
  ### When to Intervene
921
968
 
922
969
  | Worker Status | What to Do |