claude-multi-session 2.3.2 → 2.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-multi-session",
3
- "version": "2.3.2",
3
+ "version": "2.5.0",
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": {
package/src/mcp-server.js CHANGED
@@ -25,6 +25,8 @@
25
25
  * - tools/call → execute a tool and return result
26
26
  */
27
27
 
28
+ const fs = require('fs');
29
+ const path = require('path');
28
30
  const readline = require('readline');
29
31
  const SessionManager = require('./manager');
30
32
  const Delegate = require('./delegate');
@@ -47,6 +49,9 @@ const DecisionJournal = require('./decision-journal');
47
49
  const PatternRegistry = require('./pattern-registry');
48
50
  const StaleDetector = require('./stale-detector');
49
51
 
52
+ // Capture version at load time — used to detect stale server processes
53
+ const LOADED_VERSION = require('../package.json').version;
54
+
50
55
  // =============================================================================
51
56
  // Server State — persists across all tool calls
52
57
  // =============================================================================
@@ -390,6 +395,18 @@ const TOOLS = [
390
395
  },
391
396
  },
392
397
 
398
+ // ── Server Version ─────────────────────────────────────────────────────
399
+ {
400
+ name: 'server_version',
401
+ description:
402
+ 'Check the running MCP server version and detect staleness. ' +
403
+ 'Call this if tools seem missing or behave unexpectedly.',
404
+ inputSchema: {
405
+ type: 'object',
406
+ properties: {},
407
+ },
408
+ },
409
+
393
410
  // ══════════════════════════════════════════════════════════════════════════
394
411
  // TEAM HUB v2 — 32 new tools for team collaboration
395
412
  // ══════════════════════════════════════════════════════════════════════════
@@ -555,7 +572,9 @@ const TOOLS = [
555
572
  {
556
573
  name: 'artifact_get',
557
574
  description:
558
- 'Read an artifact (latest version or specific version).',
575
+ 'Read an artifact (latest version or specific version). ' +
576
+ 'IMPORTANT: Pass your session name as the "reader" parameter to track artifact consumption. ' +
577
+ 'The orchestrator uses this to verify workers actually read shared data.',
559
578
  inputSchema: {
560
579
  type: 'object',
561
580
  properties: {
@@ -1139,6 +1158,10 @@ async function executeTool(toolName, args) {
1139
1158
  case 'abort_task':
1140
1159
  return handleAbort(args);
1141
1160
 
1161
+ // ── Server Version ──────────────────────────────────────────────
1162
+ case 'server_version':
1163
+ return handleServerVersion(args);
1164
+
1142
1165
  // ── Orchestrator Guide ────────────────────────────────────────────
1143
1166
  case 'get_orchestrator_guide':
1144
1167
  return handleGetOrchestratorGuide(args);
@@ -1597,6 +1620,32 @@ function handleGetOrchestratorGuide(args) {
1597
1620
  return textResult(guide);
1598
1621
  }
1599
1622
 
1623
+ /**
1624
+ * Check the running MCP server version and detect staleness.
1625
+ * Compares the version loaded into memory at startup against the
1626
+ * version currently installed on disk (package.json).
1627
+ */
1628
+ function handleServerVersion() {
1629
+ let installedVersion = LOADED_VERSION;
1630
+ try {
1631
+ const pkgPath = path.join(__dirname, '..', 'package.json');
1632
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
1633
+ installedVersion = pkg.version;
1634
+ } catch (e) {
1635
+ // If we can't read package.json at runtime, use loaded version
1636
+ }
1637
+
1638
+ const stale = LOADED_VERSION !== installedVersion;
1639
+ return textResult(JSON.stringify({
1640
+ running: LOADED_VERSION,
1641
+ installed: installedVersion,
1642
+ stale,
1643
+ message: stale
1644
+ ? `Server is stale: running v${LOADED_VERSION} but v${installedVersion} is installed. Restart Claude Code to load new tools.`
1645
+ : `Server is up to date (v${LOADED_VERSION}).`
1646
+ }, null, 2));
1647
+ }
1648
+
1600
1649
  // =============================================================================
1601
1650
  // Team Hub Handlers — Layer 1, 2, 3
1602
1651
  // =============================================================================
@@ -1889,7 +1938,8 @@ function handleArtifactGet(args) {
1889
1938
  artifactStore.trackRead(args.artifactId, args.reader, artifact.version);
1890
1939
  }
1891
1940
 
1892
- return textResult(JSON.stringify({
1941
+ // Build the response
1942
+ const response = {
1893
1943
  artifactId: artifact.artifactId,
1894
1944
  version: artifact.version,
1895
1945
  type: artifact.type,
@@ -1901,7 +1951,14 @@ function handleArtifactGet(args) {
1901
1951
  lineage: artifact.lineage,
1902
1952
  team: teamName,
1903
1953
  readBy: artifactStore.getReads(args.artifactId),
1904
- }, null, 2));
1954
+ };
1955
+
1956
+ // Add nudge if reader param was not provided
1957
+ if (!args.reader) {
1958
+ response._hint = 'Tip: Pass your session name as the "reader" parameter to track artifact consumption. Example: artifact_get({ artifactId: "...", reader: "your-session-name" })';
1959
+ }
1960
+
1961
+ return textResult(JSON.stringify(response, null, 2));
1905
1962
  } catch (err) {
1906
1963
  return errorResult(err.message);
1907
1964
  }
@@ -2497,6 +2554,28 @@ function errorResult(message) {
2497
2554
  return { content: [{ type: 'text', text: `Error: ${message}` }], isError: true };
2498
2555
  }
2499
2556
 
2557
+ /**
2558
+ * Append a staleness warning to tool results if the server version is outdated.
2559
+ * Reads package.json from disk on each call to detect post-install version drift.
2560
+ * @param {Object} result - The tool result object
2561
+ * @returns {Object} The result, possibly with a staleness warning appended
2562
+ */
2563
+ function appendStalenessWarning(result) {
2564
+ try {
2565
+ const pkgPath = path.join(__dirname, '..', 'package.json');
2566
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
2567
+ if (pkg.version !== LOADED_VERSION) {
2568
+ const warning = `\n\n⚠️ STALE SERVER: Running v${LOADED_VERSION} but v${pkg.version} is installed. Restart Claude Code to load updated tools.`;
2569
+ if (result && result.content && result.content[0] && result.content[0].text) {
2570
+ result.content[0].text += warning;
2571
+ }
2572
+ }
2573
+ } catch (e) {
2574
+ // Silently ignore — staleness check is best-effort
2575
+ }
2576
+ return result;
2577
+ }
2578
+
2500
2579
  // =============================================================================
2501
2580
  // MCP Protocol Handler — JSON-RPC 2.0 over stdio
2502
2581
  // =============================================================================
@@ -2566,7 +2645,9 @@ async function handleMessage(message) {
2566
2645
  break;
2567
2646
  }
2568
2647
  try {
2569
- const result = await executeTool(params.name, params.arguments || {});
2648
+ let result = await executeTool(params.name, params.arguments || {});
2649
+ // Append staleness warning if server version is outdated
2650
+ result = appendStalenessWarning(result);
2570
2651
  sendResponse(id, result);
2571
2652
  } catch (err) {
2572
2653
  sendResponse(id, errorResult(err.message));
package/src/prompts.js CHANGED
@@ -83,6 +83,8 @@ Follow the conventions STRICTLY. If NO convention artifact exists AND your work
83
83
  - Use \`team_ask\` to ask the relevant teammate about their format BEFORE writing code
84
84
  - NEVER guess or assume format — mismatches cause test failures
85
85
 
86
+ Note: team_ask is a **fallback mechanism** for when information wasn't available upfront. If your orchestrator provided thorough prompts with all needed context and conventions, you may never need team_ask — this is the ideal case.
87
+
86
88
  ### Step 2: UPDATE YOUR STATUS
87
89
  Call \`mcp__multi-session__team_update_status\` to set yourself as "active" with your current task.
88
90
 
@@ -120,7 +122,7 @@ mcp__multi-session__team_broadcast({ from: "${name}", content: "Completed: <what
120
122
  | \`team_check_inbox\` | BEFORE starting work, AFTER major steps |
121
123
  | \`team_send_message\` | Direct message to a specific teammate |
122
124
  | \`team_broadcast\` | Announce something to ALL teammates |
123
- | \`team_ask\` | Ask a teammate a question and WAIT for reply |
125
+ | \`team_ask\` | Ask a teammate a question and WAIT for reply (fallback — use when info wasn't provided upfront) |
124
126
  | \`team_reply\` | Reply to a question you received (IMMEDIATELY) |
125
127
  | \`team_update_status\` | Update what you're working on |
126
128
 
@@ -470,12 +472,17 @@ You have access to a Multi-Session MCP server that lets you spawn and coordinate
470
472
 
471
473
  IMPORTANT: You are the ORCHESTRATOR. Your job is to PLAN, SPAWN, and MONITOR — not to do the implementation work yourself. Delegate implementation to worker sessions.
472
474
 
475
+ ## Step 0: Verify Your Tools
476
+ Before starting ANY orchestration work, call \`server_version()\` to verify you're running the latest MCP tools. If the response shows a version mismatch, tell the user to restart Claude Code before proceeding — stale tools cause phantom failures.
477
+
473
478
  ## CORE PRINCIPLE: Orchestrate, Don't Implement
474
479
 
475
480
  === CRITICAL: ANTI-PATTERN — DO NOT DO THIS ===
476
481
  - Do NOT spawn a session, read its output, then manually relay that output to another session
477
- - Do NOT fix bugs found by one session yourself tell that session to fix them
482
+ - Do NOT fix bugs found by one session yourself UNLESS the fix is ≤ 3 lines AND the worker session has completed. For trivial fixes, you may fix directly but MUST broadcast the change and re-publish any affected artifacts
478
483
  - Do NOT act as a message router between sessions — they can talk directly
484
+ - Note: team_ask is a **fallback** for unexpected ambiguity. In well-orchestrated projects where you provide all context upfront in worker prompts, team_ask may never be called — this is the ideal case and means your prompts were thorough enough.
485
+ - Do NOT create project foundation files (package.json, db.js, app.js, server.js) yourself — spawn a setup worker for Phase 0
479
486
  - Do NOT do implementation work that should be delegated
480
487
  - Do NOT fix code yourself when a worker's tests fail — send corrections to the worker that wrote the failing code
481
488
  - Do NOT assume workers will agree on response formats — define shared conventions before spawning
@@ -487,6 +494,25 @@ IMPORTANT: You are the ORCHESTRATOR. Your job is to PLAN, SPAWN, and MONITOR —
487
494
  4. Monitor progress via team_roster and contract_list
488
495
  5. Only intervene if a session is stuck or failed
489
496
 
497
+ === FIX PROTOCOL (when you must fix worker code directly) ===
498
+ STOP. Before editing any file a worker created, answer these questions:
499
+
500
+ 1. Is this fix ≤ 3 lines?
501
+ NO → send_message to worker or spawn fix-worker. Do NOT fix yourself.
502
+ YES → continue to step 2.
503
+
504
+ 2. Is the worker done (idle status in team_roster)?
505
+ NO → send_message to worker. Do NOT fix yourself.
506
+ YES → continue to step 3.
507
+
508
+ 3. Make the fix.
509
+
510
+ 4. Broadcast: team_broadcast({ from: "orchestrator", content: "Fixed [file]:[lines] — [description of change]" })
511
+
512
+ 5. Re-publish: If the fix changes data in a published artifact, call artifact_publish to update it.
513
+
514
+ NEVER skip steps 4-5. Unannounced fixes cause downstream workers to use stale assumptions.
515
+
490
516
  ## STEP-BY-STEP: How to Run a Multi-Session Project
491
517
 
492
518
  ### Phase 1: Plan
@@ -495,17 +521,45 @@ Break the user's request into independent work units. Identify:
495
521
  - What tasks are SEQUENTIAL (B needs output from A)
496
522
  - What ROLES are needed (backend, frontend, testing, etc.)
497
523
 
524
+ ### Phase 0: Foundation Setup (if needed)
525
+ If the project needs shared infrastructure (database connection, app skeleton, package.json, shared utilities), spawn a **setup worker** as Phase 0 BEFORE other workers.
526
+
527
+ === CRITICAL: DO NOT create foundation files yourself — delegate to a setup worker ===
528
+
529
+ \`\`\`
530
+ mcp__multi-session__team_spawn({
531
+ name: "setup",
532
+ role: "project setup",
533
+ task: "Create project foundation: package.json, database connection, app skeleton",
534
+ prompt: "Initialize the project. Create package.json, install dependencies, set up the database connection file, and create the Express app skeleton. When done, publish a 'project-foundation' artifact listing all files you created and any connection details (DB path, port, etc.). Then broadcast completion and set status to idle."
535
+ })
536
+ \`\`\`
537
+
538
+ Wait for the setup worker to finish and publish its artifact. Then run the PHASE GATE CHECKPOINT before spawning Phase 1 workers.
539
+
540
+ NOT every project needs Phase 0. Skip it if:
541
+ - The project already has package.json and dependencies installed
542
+ - There's no shared infrastructure to set up
543
+ - The foundation is trivial (1-2 config lines that fit in each worker's prompt)
544
+
498
545
  ### Phase 1.5: Define Shared Conventions
499
546
  IMPORTANT: Before spawning workers, define shared conventions that ALL workers must follow. Either:
500
547
  (a) Publish a conventions artifact that workers will read, OR
501
548
  (b) Include the same convention rules in every worker's prompt
502
549
 
503
- Conventions MUST cover:
504
- - **Response format:** e.g., "All endpoints return { data: <result> } for success"
505
- - **Error format:** e.g., "All errors return { error: <message> }"
506
- - **Status codes:** e.g., "201 for create, 200 for update/delete/read, 404 for not found, 400 for validation"
507
- - **Naming:** e.g., "snake_case for DB fields (in_progress not in-progress)"
508
- - **File paths:** "Use relative paths only never absolute"
550
+ === CONVENTION CHECKLIST (define every item before spawning) ===
551
+ Response format: e.g., { data: <result> }
552
+ Error format: e.g., { error: <message> }
553
+ Status codes: create=201, read=200, update=200, delete=200, notFound=404, badRequest=400, conflict=409
554
+ Naming: e.g., snake_case for DB columns, camelCase for JS variables
555
+ File paths: relative only, never absolute
556
+ □ Enum/status values: list EXACT strings (e.g., "pending", "in_progress", "completed" — NOT "Pending" or "InProgress")
557
+ □ Boolean handling: true/false vs 1/0 — pick one, specify it
558
+ □ Date format: ISO 8601 strings, Unix timestamps, or other — specify which
559
+ □ Audit/log action names: exact strings (e.g., "created" vs "create" vs "CREATE")
560
+ □ Shared column names: list exact DB column names for tables multiple workers reference
561
+
562
+ Missing even ONE item causes convention mismatches that the orchestrator then has to fix manually — which violates Rule 6.
509
563
 
510
564
  \`\`\`
511
565
  // Example: publish conventions before spawning workers
@@ -526,28 +580,36 @@ mcp__multi-session__artifact_publish({
526
580
  NEVER assume workers will independently agree on conventions. Define them explicitly.
527
581
 
528
582
  ### 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
583
 
535
- Example phased workflow:
536
- \`\`\`
537
- // Phase 1: Spawn the database worker
538
- team_spawn({ name: "db-worker", ... })
584
+ === PHASE GATE CHECKPOINT (fill in and run before EVERY team_spawn after Phase 0) ===
539
585
 
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
586
+ Before spawning the next phase, STOP and fill in this checklist:
543
587
 
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
- \`\`\`
588
+ Phase completing: ___ → Phase starting: ___
589
+
590
+ 1. artifact_list()
591
+ Expected artifacts: [___]
592
+ All present? YES / NO
593
+
594
+ 2. artifact_get({ artifactId: "___", reader: "orchestrator" })
595
+ Content valid and complete? YES / NO
596
+
597
+ 3. team_roster()
598
+ All previous-phase workers idle? YES / NO
599
+
600
+ 4. artifact_readers({ artifactId: "___" })
601
+ All expected consumers listed? YES / NO (skip if Phase 0→1)
602
+
603
+ PROCEED ONLY IF all answers are YES.
604
+ If any is NO → diagnose and fix before continuing.
548
605
 
549
606
  NEVER skip verification. NEVER rely on a worker's self-reported completion — verify the artifact exists yourself.
550
607
 
608
+ === PHASE COUNTING RULE ===
609
+ At the start of planning, count and list your phases explicitly:
610
+ "Phase 0: [foundation], Phase 1: [routes], Phase 2: [tests], ..."
611
+ If you have N phases, you MUST fill in this checkpoint exactly N-1 times.
612
+
551
613
  ### Phase 2: Spawn Workers (use team_spawn, NOT delegate_task)
552
614
  Spawn all independent workers at once. Example:
553
615
 
@@ -596,18 +658,8 @@ mcp__multi-session__contract_create({
596
658
  })
597
659
  \`\`\`
598
660
 
599
- ### Phase 4: Monitor (Don't Micromanage)
600
- Check progress periodically:
601
- \`\`\`
602
- mcp__multi-session__team_roster() — See who's active/idle/blocked
603
- mcp__multi-session__contract_list() — See contract statuses
604
- mcp__multi-session__artifact_list() — See published outputs
605
- \`\`\`
606
-
607
- Only intervene when:
608
- - A session status shows "BLOCKED" for more than a reasonable time
609
- - A contract has failed
610
- - The user asks for a status update
661
+ ### Between Every Phase: Run the Phase Gate Checkpoint
662
+ After ALL workers in a phase complete, run the PHASE GATE CHECKPOINT above before spawning the next phase. This is NOT optional. N phases = N-1 checkpoints. Skipping verification for later phases is the #1 cause of test failures in multi-session projects.
611
663
 
612
664
  ### Phase 5: Collect Results
613
665
  When all workers are done:
@@ -837,11 +889,15 @@ const ORCHESTRATOR_QUICK_START = `
837
889
 
838
890
  IMPORTANT: You are the ORCHESTRATOR. Your job is to PLAN, SPAWN, and MONITOR — not to implement code yourself.
839
891
 
840
- ### The 6-Step Protocol
892
+ ### The Protocol
893
+
894
+ 0. **Verify tools** — Call \`server_version()\` to confirm you're running the latest MCP server. If versions mismatch, tell the user to restart Claude Code.
895
+
896
+ 0.5. **Foundation** — If the project needs shared infrastructure (database, app skeleton, package.json), spawn a \`setup\` worker as Phase 0. Wait for its \`project-foundation\` artifact before proceeding. Do NOT create foundation files yourself.
841
897
 
842
898
  1. **Plan** — Break the user's request into independent work units. Identify what can run in PARALLEL vs what is SEQUENTIAL.
843
899
 
844
- 1.5. **Define Conventions** — Before spawning, define shared conventions (response format, status codes, naming) either as a published artifact or in each worker's prompt. Workers CANNOT coordinate on conventions after they start define them upfront.
900
+ 1.5. **Define Conventions** — Before spawning, fill in the CONVENTION CHECKLIST: response format, error format, status codes, naming, file paths, exact enum values, boolean handling, date format, audit action names, and shared column names. Publish as an artifact OR embed in every worker's prompt. Missing conventions = test failures you'll have to fix yourself.
845
901
 
846
902
  2. **Spawn** — Use \`team_spawn\` to launch all independent workers in a SINGLE message with multiple tool calls. This makes them run in parallel. NEVER spawn workers one at a time sequentially.
847
903
 
@@ -856,7 +912,7 @@ IMPORTANT: You are the ORCHESTRATOR. Your job is to PLAN, SPAWN, and MONITOR —
856
912
 
857
913
  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
914
 
859
- 5. **Monitor** — Check progress with \`team_roster()\`, \`contract_list()\`, \`artifact_list()\`. Only intervene when a worker is BLOCKED or FAILED.
915
+ 5. **Post-Phase Verification** — After each phase completes, run the verification checklist: \`artifact_list()\` to confirm artifacts exist, \`artifact_get()\` to verify content, \`team_roster()\` to confirm workers are idle. Only proceed when all checks pass. If you have N phases, verify N-1 times.
860
916
 
861
917
  6. **Collect** — When all workers are idle, check \`artifact_list\` for published outputs and summarize results for the user.
862
918
 
@@ -941,28 +997,33 @@ WHY: Specific file, exact error, expected behavior, and how to verify.
941
997
  `;
942
998
 
943
999
  const ORCHESTRATOR_MONITORING = `
944
- ## How to Monitor Without Micromanaging
1000
+ ## Post-Phase Verification (MANDATORY)
945
1001
 
946
- You MUST check progress using these tools after spawning workers do NOT fall back to filesystem checks (Glob, Read) to verify worker output:
1002
+ After ALL workers in a phase complete, and BEFORE spawning the next phase, you MUST run the Phase Gate Checkpoint. Do NOT fall back to filesystem checks (Glob, Read) to verify worker output — use these tools:
947
1003
 
948
- \`\`\`
949
- mcp__multi-session__team_roster() — See who's active/idle/blocked
950
- mcp__multi-session__contract_list() — See contract statuses
951
- mcp__multi-session__artifact_list() — See published outputs
952
- \`\`\`
1004
+ ### Phase Gate Checkpoint (run between EVERY pair of phases)
953
1005
 
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
- \`\`\`
1006
+ Before spawning the next phase, STOP and fill in this checklist:
960
1007
 
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
- \`\`\`
1008
+ Phase completing: ___ → Phase starting: ___
1009
+
1010
+ 1. artifact_list()
1011
+ Expected artifacts: [___]
1012
+ All present? YES / NO
1013
+
1014
+ 2. artifact_get({ artifactId: "___", reader: "orchestrator" })
1015
+ Content valid and complete? YES / NO
1016
+
1017
+ 3. team_roster()
1018
+ All previous-phase workers idle? YES / NO
1019
+
1020
+ 4. artifact_readers({ artifactId: "___" })
1021
+ All expected consumers listed? YES / NO
1022
+
1023
+ PROCEED ONLY IF all answers are YES.
1024
+ If any is NO → diagnose and fix before continuing.
1025
+
1026
+ If you have N phases, you MUST fill in this checkpoint exactly N-1 times.
966
1027
 
967
1028
  ### When to Intervene
968
1029
 
@@ -984,7 +1045,7 @@ When a worker is BLOCKED:
984
1045
 
985
1046
  NEVER do these:
986
1047
  - Do NOT read the worker's full output to understand the problem — their status message should tell you
987
- - Do NOT implement the fix yourself tell the worker to fix it
1048
+ - Do NOT implement fixes > 3 lines yourself. For trivial fixes, follow the FIX PROTOCOL above (steps 1-5: check size → check worker status fix → broadcast → re-publish)
988
1049
  - Do NOT act as a message router between workers — they can use \`team_ask\` directly
989
1050
 
990
1051
  === ANTI-PATTERN — DO NOT DO THIS ===