@cgh567/agent 2.4.2 → 2.4.4

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 (157) hide show
  1. package/agents/business/talisman-ceo.md +183 -0
  2. package/agents/business/talisman-comms.md +257 -0
  3. package/agents/business/talisman-cto.md +153 -0
  4. package/agents/business/talisman-finance.md +246 -0
  5. package/agents/business/talisman-marketing.md +240 -0
  6. package/agents/business/talisman-sales.md +242 -0
  7. package/agents/business/talisman-support.md +236 -0
  8. package/bin/helios-rpc.js +19 -0
  9. package/daemon/adapters/helios-rpc-adapter.js +5 -12
  10. package/daemon/adapters/tui_wakeup.js +8 -0
  11. package/daemon/context-enrichment.js +27 -0
  12. package/daemon/daemon-manager.js +1 -1
  13. package/daemon/db/email-infrastructure-migrate.js +192 -0
  14. package/daemon/db/hbo-core-migrate.js +189 -0
  15. package/daemon/helios-api.js +863 -64
  16. package/daemon/helios-company-daemon.js +233 -33
  17. package/daemon/lib/blast-radius-analyzer.js +75 -0
  18. package/daemon/lib/domain-bootstrap-orchestrator.js +267 -0
  19. package/daemon/lib/forensic-log.js +113 -0
  20. package/daemon/lib/goal-research-pipeline.js +644 -0
  21. package/daemon/lib/harada/cascade-judge.js +84 -1
  22. package/daemon/lib/harada/cascade-research-dispatcher.js +282 -0
  23. package/daemon/lib/harada/pillar-dispatcher.js +23 -2
  24. package/daemon/lib/hbo-bridge.js +74 -6
  25. package/daemon/lib/headroom-middleware.js +129 -0
  26. package/daemon/lib/headroom-proxy-manager.js +309 -0
  27. package/daemon/lib/hed-engine.js +25 -0
  28. package/daemon/lib/intelligence/department-page-generator.js +46 -1
  29. package/daemon/lib/interpretation-engine.js +92 -0
  30. package/daemon/lib/mental-model-cache.js +96 -0
  31. package/daemon/lib/project-factory.js +47 -0
  32. package/daemon/lib/session-log-reader.js +93 -0
  33. package/daemon/lib/standard-work-bootstrap.js +87 -1
  34. package/daemon/lib/task-completion-processor.js +23 -0
  35. package/daemon/lib/wizard-engine.js +57 -6
  36. package/daemon/package.json +2 -1
  37. package/daemon/routes/agents.js +51 -6
  38. package/daemon/routes/channels.js +116 -2
  39. package/daemon/routes/crm.js +85 -0
  40. package/daemon/routes/dashboard.js +62 -16
  41. package/daemon/routes/dept.js +10 -1
  42. package/daemon/routes/email-triage.js +19 -10
  43. package/daemon/routes/hbo.js +618 -58
  44. package/daemon/routes/hed.js +133 -0
  45. package/daemon/routes/inbox.js +397 -8
  46. package/daemon/routes/project.js +580 -66
  47. package/daemon/routes/routines.js +14 -0
  48. package/daemon/routes/tasks.js +15 -1
  49. package/daemon/schema-apply.js +174 -0
  50. package/daemon/schema-definitions.js +433 -0
  51. package/daemon/schema-migrations-hbo.js +20 -0
  52. package/daemon/schema-migrations-hed.js +18 -0
  53. package/daemon/schema-migrations-proj.js +153 -0
  54. package/extensions/__tests__/codebase-index.test.ts +73 -0
  55. package/extensions/__tests__/extension-command-registration.test.ts +35 -0
  56. package/extensions/__tests__/git-push-guard.test.ts +68 -0
  57. package/extensions/context-compaction.ts +104 -76
  58. package/extensions/cortex/__tests__/cortex-core.test.ts +100 -0
  59. package/extensions/cortex/wal-replay.ts +91 -0
  60. package/extensions/email/actions/draft-response.ts +21 -1
  61. package/extensions/email/auth/accounts.ts +5 -11
  62. package/extensions/email/auth/inbox-dog.ts +5 -2
  63. package/extensions/email/backfill.ts +20 -13
  64. package/extensions/email/providers/gmail.ts +164 -0
  65. package/extensions/email/providers/google-calendar.ts +34 -5
  66. package/extensions/helios-browser/__tests__/browser-routing.test.ts +57 -0
  67. package/extensions/helios-browser/backends/playwright.ts +3 -1
  68. package/extensions/helios-governance/__tests__/governance-gates.test.ts +40 -0
  69. package/extensions/helios-governance/__tests__/tournament-consumer.test.js +66 -0
  70. package/extensions/hema-dispatch-v3/headroom-compress.ts +103 -0
  71. package/extensions/hema-dispatch-v3/index.ts +46 -72
  72. package/extensions/interview/__tests__/server.test.ts +117 -0
  73. package/extensions/lib/helios-root.cjs +46 -0
  74. package/extensions/subagent-mesh/__tests__/handlers.test.ts +98 -0
  75. package/extensions/warm-tick/warm-tick-maintenance.ts +164 -0
  76. package/lib/__tests__/bulk-ingest.live.test.ts +66 -0
  77. package/lib/__tests__/crash-fixes.test.ts +49 -0
  78. package/lib/__tests__/hbo-core-store.test.js +238 -0
  79. package/lib/__tests__/maintenance-mission-wiring.test.ts +35 -0
  80. package/lib/broker/__tests__/jit-subscription.test.js +44 -1
  81. package/lib/broker/__tests__/lifecycle-channels.test.js +25 -1
  82. package/lib/compression/__tests__/ccr-store.test.js +138 -0
  83. package/lib/compression/__tests__/pipeline.test.js +280 -0
  84. package/lib/compression/__tests__/smart-crusher.test.js +242 -0
  85. package/lib/compression/dist/server.js +34 -1
  86. package/lib/compression/dist/start-server.js +77 -0
  87. package/lib/event-bus.mts +1 -1
  88. package/lib/graph/learning/headroom-learn-bridge.js +175 -0
  89. package/lib/graph-availability.js +62 -0
  90. package/lib/hbo-core-store.compiled.js +834 -0
  91. package/lib/hbo-core-store.js +124 -0
  92. package/lib/hbo-core-store.ts +979 -0
  93. package/lib/mission-loop/__tests__/research-handler.test.ts +143 -0
  94. package/lib/skill-sync.js +6 -1
  95. package/lib/startup-integrity.js +9 -2
  96. package/lib/triage-core/__tests__/classifier-fixture.test.ts +254 -0
  97. package/lib/triage-core/__tests__/classifier-post-norm.test.ts +1 -1
  98. package/lib/triage-core/__tests__/classifier.test.ts +45 -7
  99. package/lib/triage-core/__tests__/correction-detector.test.ts +36 -0
  100. package/lib/triage-core/__tests__/d6-dunbar-boost.test.ts +5 -5
  101. package/lib/triage-core/__tests__/orchestrator-pipeline.test.ts +107 -0
  102. package/lib/triage-core/__tests__/orchestrator.test.ts +113 -1
  103. package/lib/triage-core/__tests__/signals.test.ts +357 -0
  104. package/lib/triage-core/__tests__/sql-parity.test.ts +216 -0
  105. package/lib/triage-core/backfill-cost-estimator.ts +91 -0
  106. package/lib/triage-core/backfill-orchestrator.ts +119 -0
  107. package/lib/triage-core/classifier.ts +41 -8
  108. package/lib/triage-core/cos/cross-channel-escalation.ts +2 -3
  109. package/lib/triage-core/cos/response-debt.ts +2 -2
  110. package/lib/triage-core/graph/__tests__/batch-persistence.test.ts +283 -0
  111. package/lib/triage-core/graph/batch-persistence.ts +66 -2
  112. package/lib/triage-core/graph/betweenness-worker.js +75 -0
  113. package/lib/triage-core/graph/graph-rank-sql.ts +67 -0
  114. package/lib/triage-core/graph/persistence.ts +1 -1
  115. package/lib/triage-core/graph/schema-v2.ts +2 -0
  116. package/lib/triage-core/graph/schema.cypher +11 -0
  117. package/lib/triage-core/graph/triage-query.ts +1 -1
  118. package/lib/triage-core/learning.ts +15 -20
  119. package/lib/triage-core/mental-model/bedrock-config.ts +78 -0
  120. package/lib/triage-core/mental-model/cos-integration.ts +1 -1
  121. package/lib/triage-core/mental-model/entity-extractor.ts +51 -4
  122. package/lib/triage-core/mental-model/identity-resolver.ts +5 -5
  123. package/lib/triage-core/mental-model/key-facts.ts +1 -2
  124. package/lib/triage-core/mental-model/model-assembler-sql.ts +200 -0
  125. package/lib/triage-core/mental-model/model-assembler.ts +16 -3
  126. package/lib/triage-core/orchestrator.ts +8 -15
  127. package/lib/triage-core/scheduled-sends.ts +39 -2
  128. package/lib/triage-core/signals/comms-style.ts +1 -1
  129. package/lib/triage-core/signals/cross-channel-escalation.ts +2 -2
  130. package/lib/triage-core/signals/favee-type.ts +6 -1
  131. package/lib/triage-core/signals/goal-relevance.ts +31 -2
  132. package/lib/triage-core/signals/personal-importance.ts +1 -1
  133. package/lib/triage-core/signals/referral-chain.ts +0 -1
  134. package/lib/triage-core/signals/relationship-decay.ts +4 -0
  135. package/lib/triage-core/signals/relationship-health.ts +6 -1
  136. package/lib/triage-core/signals/trajectory-signal.ts +38 -3
  137. package/lib/triage-core/tournament-runner.js +11 -1
  138. package/lib/triage-core/triage-llm-factory.ts +110 -0
  139. package/lib/triage-core/triage-local-llm.ts +145 -0
  140. package/lib/triage-core/triage-sql-store.ts +337 -0
  141. package/lib/triage-core/types.ts +2 -2
  142. package/lib/unified-graph.atomic.test.ts +52 -0
  143. package/lib/unified-graph.failure-categories.test.ts +55 -0
  144. package/package.json +18 -7
  145. package/prebuilds/darwin-arm64/better_sqlite3.node +0 -0
  146. package/prebuilds/linux-x64/better_sqlite3.node +0 -0
  147. package/prebuilds/win32-x64/better_sqlite3.node +0 -0
  148. package/skills/helios-bookkeeping/SKILL.md +321 -0
  149. package/skills/helios-briefer/SKILL.md +44 -0
  150. package/skills/helios-client-relations/SKILL.md +322 -0
  151. package/skills/helios-personal-triager/SKILL.md +45 -0
  152. package/skills/helios-recruitment/SKILL.md +317 -0
  153. package/skills/helios-relationship-nudger/SKILL.md +77 -0
  154. package/skills/helios-researcher/SKILL.md +44 -0
  155. package/skills/helios-scheduler/SKILL.md +58 -0
  156. package/skills/helios-tax-analyst/SKILL.md +280 -0
  157. package/lib/triage-core/orchestrator.ts.bak-r005-r006-r008 +0 -1823
@@ -29,16 +29,28 @@ async function handleGetCosts(req, res, ctx) {
29
29
 
30
30
  const url = new URL(req.url, 'http://localhost');
31
31
  const groupBy = url.searchParams.get('groupBy');
32
+ // G-01: date range filtering — ?from=ISO&to=ISO
33
+ const fromDate = url.searchParams.get('from') || null;
34
+ const toDate = url.searchParams.get('to') || null;
32
35
 
33
36
  try {
34
37
  if (groupBy && ['provider', 'model', 'agent'].includes(groupBy)) {
35
38
  const field = groupBy === 'agent' ? 'agentId' : groupBy;
36
- const result = await mgQuery(
37
- `MATCH (c:CostEvent {companyId: $cid})
38
- RETURN c.${field} AS groupKey, sum(coalesce(c.costCents, c.amount * 100, 0)) / 100.0 AS total, count(c) AS count
39
- ORDER BY total DESC`,
40
- { cid: ctx.cid }
41
- );
39
+ let groupCypher = `MATCH (c:CostEvent {companyId: $cid})`;
40
+ const groupParams = { cid: ctx.cid };
41
+ if (fromDate && toDate) {
42
+ groupCypher += ' WHERE c.createdAt >= $from AND c.createdAt <= $to';
43
+ groupParams.from = fromDate;
44
+ groupParams.to = toDate;
45
+ } else if (fromDate) {
46
+ groupCypher += ' WHERE c.createdAt >= $from';
47
+ groupParams.from = fromDate;
48
+ } else if (toDate) {
49
+ groupCypher += ' WHERE c.createdAt <= $to';
50
+ groupParams.to = toDate;
51
+ }
52
+ groupCypher += ` RETURN c.${field} AS groupKey, sum(coalesce(c.costCents, c.amount * 100, 0)) / 100.0 AS total, count(c) AS count ORDER BY total DESC`;
53
+ const result = await mgQuery(groupCypher, groupParams);
42
54
  const rows = (result?.rows ?? []);
43
55
  const groups = rows.map(r => ({ [groupBy]: r[0], total: r[1], count: r[2] }));
44
56
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -46,8 +58,20 @@ async function handleGetCosts(req, res, ctx) {
46
58
  return;
47
59
  }
48
60
 
49
- const result = await mgQuery(
50
- `MATCH (c:CostEvent {companyId: $cid})
61
+ let listCypher = `MATCH (c:CostEvent {companyId: $cid})`;
62
+ const listParams = { cid: ctx.cid, limit: 100 };
63
+ if (fromDate && toDate) {
64
+ listCypher += ' WHERE c.createdAt >= $from AND c.createdAt <= $to';
65
+ listParams.from = fromDate;
66
+ listParams.to = toDate;
67
+ } else if (fromDate) {
68
+ listCypher += ' WHERE c.createdAt >= $from';
69
+ listParams.from = fromDate;
70
+ } else if (toDate) {
71
+ listCypher += ' WHERE c.createdAt <= $to';
72
+ listParams.to = toDate;
73
+ }
74
+ listCypher += `
51
75
  RETURN c.id AS id, c.provider AS provider, c.model AS model,
52
76
  c.agentId AS agentId,
53
77
  coalesce(c.costCents, c.amount * 100, 0) / 100.0 AS amount,
@@ -55,9 +79,8 @@ async function handleGetCosts(req, res, ctx) {
55
79
  c.inputTokens AS inputTokens, c.outputTokens AS outputTokens,
56
80
  c.createdAt AS createdAt
57
81
  ORDER BY c.createdAt DESC
58
- LIMIT toInteger($limit)`,
59
- { cid: ctx.cid, limit: 100 }
60
- );
82
+ LIMIT toInteger($limit)`;
83
+ const result = await mgQuery(listCypher, listParams);
61
84
  const rows = (result?.rows ?? []);
62
85
  const keys = ['id', 'provider', 'model', 'agentId', 'amount', 'biller', 'inputTokens', 'outputTokens', 'createdAt'];
63
86
  const events = rows.map(r => {
@@ -78,14 +101,31 @@ async function handleGetCosts(req, res, ctx) {
78
101
  * Returns all budget policies with current spend percentage.
79
102
  */
80
103
  async function handleGetBudgetStatus(req, res, ctx) {
104
+ // C-1 fix: filter by agentId when provided — prevents one agent seeing another's budget policy
105
+ const url = new URL(req.url, 'http://localhost');
106
+ const agentIdFilter = url.searchParams.get('agentId') || null;
107
+ // M-3 fix: guard against Memgraph unavailability
108
+ if (!ctx.mgQuery) {
109
+ res.writeHead(503, { 'Content-Type': 'application/json' });
110
+ res.end(JSON.stringify({ error: 'Memgraph not connected', policies: [] }));
111
+ return;
112
+ }
81
113
  try {
82
- const result = await ctx.mgQuery(
83
- `MATCH (bp:BudgetPolicy {companyId: $cid})
114
+ let cypher, params;
115
+ if (agentIdFilter) {
116
+ cypher = `MATCH (bp:BudgetPolicy {companyId: $cid, agentId: $agentId})
117
+ RETURN bp.id AS id, bp.scope AS scope, bp.agentId AS agentId,
118
+ bp.limitCents AS limitCents, bp.spentCents AS spentCents,
119
+ bp.warnPercent AS warnPercent`;
120
+ params = { cid: ctx.cid, agentId: agentIdFilter };
121
+ } else {
122
+ cypher = `MATCH (bp:BudgetPolicy {companyId: $cid})
84
123
  RETURN bp.id AS id, bp.scope AS scope, bp.agentId AS agentId,
85
124
  bp.limitCents AS limitCents, bp.spentCents AS spentCents,
86
- bp.warnPercent AS warnPercent`,
87
- { cid: ctx.cid }
88
- );
125
+ bp.warnPercent AS warnPercent`;
126
+ params = { cid: ctx.cid };
127
+ }
128
+ const result = await ctx.mgQuery(cypher, params);
89
129
 
90
130
  const policies = (result?.rows ?? []).map(([id, scope, agentId, limitCents, spentCents, warnPercent]) => {
91
131
  const limit = limitCents ?? 0;
@@ -116,6 +156,12 @@ module.exports = function createDashboardRouter(handlers) {
116
156
  return async function dashboardRoute(req, res, ctx, pathname, method) {
117
157
  // POST /api/companies -- register a company at runtime (for tests and dynamic wizard creation)
118
158
  if (method === 'POST' && pathname === '/api/companies') {
159
+ // C-2 fix: guard against null handler — use /api/daemon/register-company instead
160
+ if (!handleRegisterCompany) {
161
+ res.writeHead(501, { 'Content-Type': 'application/json' });
162
+ res.end(JSON.stringify({ error: 'Company registration via /api/companies is not configured. Use POST /api/daemon/register-company instead.' }));
163
+ return true;
164
+ }
119
165
  await handleRegisterCompany(req, res, ctx);
120
166
  return true;
121
167
  }
@@ -149,7 +149,16 @@ module.exports = function createDeptRoute({ mgQuery, broadcast, semanticUpdater
149
149
  pcp.rationale = $rationale,
150
150
  pcp.status = 'pending',
151
151
  pcp.updatedAt = datetime()`,
152
- { proposalId, cid, pillarId, dept, section, proposedChange: String(proposedChange), rationale: rationale ? String(rationale) : '', authorId }
152
+ { proposalId, cid, pillarId, dept, section, proposedChange: String(proposedChange), rationale: rationale ? String(rationale) : '', authorId }
153
+ );
154
+
155
+ // P1-C: Create Approval node for plan_change_review so it surfaces in GET /api/hbo/approvals
156
+ await mgQuery(
157
+ `MERGE (a:Approval {id: $approvalId})
158
+ ON CREATE SET a.type = 'plan_change_review', a.companyId = $cid, a.status = 'pending',
159
+ a.proposalId = $proposalId, a.createdAt = datetime()
160
+ ON MATCH SET a.status = 'pending', a.proposalId = $proposalId`,
161
+ { approvalId: `approval:pcp:${proposalId}`, proposalId, cid }
153
162
  );
154
163
 
155
164
  // Respond immediately — LLM analysis is async
@@ -16,14 +16,16 @@ const { spawn } = require('child_process');
16
16
  const path = require('path');
17
17
 
18
18
  const HOME = homedir();
19
- const TRIAGE_DATA = join(HOME, 'helios-agent', 'data', 'email-triage');
19
+ const HELIOS_ROOT = process.env.HELIOS_ROOT
20
+ || join(HOME, 'Desktop', 'Helios', 'helios-agent-main');
21
+ const TRIAGE_DATA = join(HELIOS_ROOT, 'data', 'email-triage');
20
22
  const BRIEFING_PATH = join(TRIAGE_DATA, 'latest-briefing.json');
21
23
  const INBOX_PATH = join(TRIAGE_DATA, 'latest-inbox.json');
22
24
  const DRAFTS_PATH = join(TRIAGE_DATA, 'drafted-responses.json');
23
- const DASHBOARD_PATH = join(HOME, 'helios-agent', 'daemon', 'triage', 'dashboard.html');
25
+ const DASHBOARD_PATH = join(HELIOS_ROOT, 'daemon', 'triage', 'dashboard.html');
24
26
 
25
27
  // ─── Backfill Process Mutex (PID file) ──────────────────────────────────────
26
- const BACKFILL_PID_FILE = path.join(HOME, 'helios-agent', 'data', 'backfill.pid');
28
+ const BACKFILL_PID_FILE = path.join(HELIOS_ROOT, 'data', 'backfill.pid');
27
29
 
28
30
  function isBackfillRunning() {
29
31
  try {
@@ -64,14 +66,14 @@ function jsonResponse(res, status, data) {
64
66
  */
65
67
  async function regenerateDashboard() {
66
68
  return new Promise((resolve) => {
67
- const updaterPath = join(HOME, 'helios-agent', 'scripts', 'update-dashboard.js');
69
+ const updaterPath = join(HELIOS_ROOT, 'scripts', 'update-dashboard.js');
68
70
  if (!existsSync(updaterPath)) {
69
71
  console.warn('[email-triage] update-dashboard.js not found — skipping regen');
70
72
  resolve();
71
73
  return;
72
74
  }
73
75
  const child = spawn(process.execPath, [updaterPath], {
74
- cwd: join(HOME, 'helios-agent'),
76
+ cwd: HELIOS_ROOT,
75
77
  env: { ...process.env },
76
78
  stdio: ['ignore', 'pipe', 'pipe'],
77
79
  windowsHide: true,
@@ -237,6 +239,7 @@ module.exports = function emailTriageRoute({ broadcast } = {}) {
237
239
  // Parse optional body for config
238
240
  let since = '30d';
239
241
  let limit = 100;
242
+ let account = null;
240
243
  try {
241
244
  const chunks = [];
242
245
  await new Promise((resolve, reject) => {
@@ -247,6 +250,7 @@ module.exports = function emailTriageRoute({ broadcast } = {}) {
247
250
  const body = chunks.length > 0 ? JSON.parse(Buffer.concat(chunks).toString()) : {};
248
251
  if (body.since) since = body.since;
249
252
  if (body.limit) limit = parseInt(body.limit, 10) || 100;
253
+ if (body.account) account = body.account;
250
254
  } catch (_) { /* use defaults */ }
251
255
 
252
256
  // Set up SSE headers
@@ -268,10 +272,12 @@ module.exports = function emailTriageRoute({ broadcast } = {}) {
268
272
 
269
273
  // Resolve the backfill script — prefer run-backfill-30d.ts (full triage + drafts)
270
274
  const scriptCandidates = [
271
- join(HOME, 'helios-agent', 'run-backfill-30d.ts'), // W1-3: correct script (full 5-phase email triage + drafts)
272
- join(HOME, 'helios-agent', 'run-triage-backfill.ts'), // legacy name (does not exist, kept as fallback)
273
- join(HOME, 'helios-agent', 'extensions', 'email', 'backfill.ts'), // graph ingestion only
274
- join(HOME, 'helios-agent', 'scripts', 'run-backfill.ts'), // full graph backfill (wrong for email triage)
275
+ process.env.HELIOS_ROOT
276
+ ? join(process.env.HELIOS_ROOT, 'run-backfill-30d.ts')
277
+ : join(HOME, 'Desktop', 'Helios', 'helios-agent-main', 'run-backfill-30d.ts'), // W1-3: correct script (full 5-phase email triage + drafts)
278
+ join(HELIOS_ROOT, 'run-triage-backfill.ts'), // legacy name (does not exist, kept as fallback)
279
+ join(HELIOS_ROOT, 'extensions', 'email', 'backfill.ts'), // graph ingestion only
280
+ join(HELIOS_ROOT, 'scripts', 'run-backfill.ts'), // full graph backfill (wrong for email triage)
275
281
  ];
276
282
 
277
283
  const scriptPath = scriptCandidates.find(p => existsSync(p));
@@ -293,17 +299,20 @@ module.exports = function emailTriageRoute({ broadcast } = {}) {
293
299
  }
294
300
 
295
301
  const args = [scriptPath, '--since', sinceDate, '--limit', limit];
302
+ if (account) args.push('--account', account);
296
303
  sendSSE({ phase: 1, message: `Running: npx tsx ${path.basename(scriptPath)} --since ${sinceDate} --limit ${limit}` });
297
304
 
298
305
  let child;
299
306
  try {
300
307
  // C6: use npx.cmd on Windows so spawn finds the correct executable
308
+ // shell:true required on Windows for .cmd files (EINVAL without it)
301
309
  const npxBin = process.platform === 'win32' ? 'npx.cmd' : 'npx';
302
310
  child = spawn(npxBin, ['tsx', ...args], {
303
- cwd: join(HOME, 'helios-agent'),
311
+ cwd: HELIOS_ROOT,
304
312
  env: { ...process.env, HELIOS_SKIP_INTERNAL_DASHBOARD_REGEN: '1' },
305
313
  stdio: ['ignore', 'pipe', 'pipe'],
306
314
  windowsHide: true,
315
+ shell: process.platform === 'win32',
307
316
  });
308
317
  } catch (spawnErr) {
309
318
  sendSSE({ phase: -1, message: `Spawn error: ${spawnErr.message}`, error: true, done: true });