claude-code-session-manager 0.98.0 → 0.101.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/README.md +3 -3
- package/bin/cli.cjs +16 -4
- package/bin/node-floor.cjs +41 -0
- package/dist/assets/DataModel-CXaH2GED.js +1 -0
- package/dist/assets/{History-Bfdoywlh.js → History-Cerii-HA.js} +2 -2
- package/dist/assets/{Hooks-CKDJjTJV.js → Hooks-aXMb5LDy.js} +3 -3
- package/dist/assets/{Library-7akfgy67.js → Library-WrJFKEOJ.js} +1 -1
- package/dist/assets/{MarkdownEditor-DDesiqet.js → MarkdownEditor-D1BMCXS7.js} +1 -1
- package/dist/assets/{McpServers-D4lZtXof.js → McpServers-Cf0davLd.js} +2 -2
- package/dist/assets/{Memory-CeI8G1w5.js → Memory-Dn6NHWPe.js} +6 -6
- package/dist/assets/Permissions-CTVuFPjg.js +3 -0
- package/dist/assets/{Plugins-P4eCXy_S.js → Plugins-C2TyF8Mn.js} +2 -2
- package/dist/assets/ProvenanceBadge-F_jwHSwE.js +1 -0
- package/dist/assets/{SaveBar-l9jnEMES.js → SaveBar-DA1KVJST.js} +1 -1
- package/dist/assets/Scheduler-Bm6Q_hlu.js +16 -0
- package/dist/assets/{ScopeSwitcher-CjJi8ST5.js → ScopeSwitcher-BygZlJca.js} +1 -1
- package/dist/assets/Settings-BYCiv5KV.js +3 -0
- package/dist/assets/{SkillReferenceGraph-CjbO01Sg.js → SkillReferenceGraph-CcLnl9np.js} +1 -1
- package/dist/assets/{Skills-BxZ9_fCR.js → Skills-ChffsdTD.js} +2 -2
- package/dist/assets/{SystemPrompt-D8vfFb8b.js → SystemPrompt-dNkfHE_M.js} +1 -1
- package/dist/assets/{TagLibrary-2vzUo7YL.js → TagLibrary-jVlEuB0m.js} +1 -1
- package/dist/assets/{TiptapBody-B8ZfbK90.js → TiptapBody-6_cdwcP0.js} +1 -1
- package/dist/assets/{Toggle-BbW4nGGf.js → Toggle-CVXqGpAP.js} +1 -1
- package/dist/assets/ViewTabs-DO1YVbpc.js +1 -0
- package/dist/assets/{index-CKH5Uxik.css → index-D89EUizB.css} +1 -1
- package/dist/assets/{index-Cd98Q1lD.js → index-DbCQPWWY.js} +747 -752
- package/dist/assets/{settingsSchema-IhUfucCF.js → settingsSchema-Wrvn-Yz1.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +25 -29
- package/plugins/CLAUDE.md +6 -6
- package/plugins/session-manager-dev/skills/builder/4-manual/SKILL.md +21 -19
- package/plugins/session-manager-dev/skills/builder/SKILL.md +3 -3
- package/plugins/session-manager-dev/skills/develop/SKILL.md +254 -510
- package/plugins/session-manager-dev/skills/develop/standards.md +17 -22
- package/scripts/README.md +3 -11
- package/scripts/audit-ops-hygiene.cjs +3 -3
- package/scripts/hooks/lib/guard-prd-writes-policy.cjs +1 -1
- package/scripts/hooks/lib/guard-self-schedule-policy.cjs +1 -1
- package/scripts/mint-epic.cjs +2 -3
- package/scripts/scheduler-mcp-server.cjs +64 -7
- package/src/main/agentLibrary.cjs +22 -2
- package/src/main/build-info.json +4 -4
- package/src/main/chatRunner.cjs +4 -3
- package/src/main/config.cjs +41 -38
- package/src/main/docEdit.cjs +4 -1
- package/src/main/files.cjs +2 -5
- package/src/main/health.cjs +1 -1
- package/src/main/index.cjs +40 -11
- package/src/main/ipcSchemas.cjs +35 -34
- package/src/main/lib/agentPersonaSchema.cjs +13 -2
- package/src/main/lib/atomicFs.cjs +116 -0
- package/src/main/lib/branchSweep.cjs +13 -12
- package/src/main/lib/buildTarget.cjs +3 -3
- package/src/main/lib/builtinMacros.cjs +39 -0
- package/src/main/lib/claudeCliCaps.cjs +129 -0
- package/src/main/lib/credentials.cjs +2 -4
- package/src/main/lib/crossProjectFeedback.cjs +3 -3
- package/src/main/lib/cwdClassify.cjs +15 -3
- package/src/main/lib/definitionOfDone.cjs +207 -125
- package/src/main/lib/dodDrainHook.cjs +11 -5
- package/src/main/lib/epicMint.cjs +2 -4
- package/src/main/lib/epicStatusMirror.cjs +8 -10
- package/src/main/lib/epicWorktreeProjectConfig.cjs +2 -4
- package/src/main/lib/gateAuthority.cjs +96 -0
- package/src/main/lib/gitExec.cjs +69 -0
- package/src/main/lib/gitWorktree.cjs +656 -92
- package/src/main/lib/instanceLock.cjs +5 -8
- package/src/main/lib/launchFailure.cjs +2 -3
- package/src/main/lib/macroLibrary.cjs +480 -0
- package/src/main/lib/mcpToolCatalog.cjs +49 -17
- package/src/main/lib/needsReviewLedger.cjs +1 -1
- package/src/main/lib/opsErrorLog.cjs +11 -1
- package/src/main/lib/opsOwnership.cjs +5 -9
- package/src/main/lib/pidAlive.cjs +32 -0
- package/src/main/lib/prdCreate.cjs +121 -12
- package/src/main/lib/prdDisposition.cjs +2 -2
- package/src/main/lib/prdGateFiles.cjs +284 -0
- package/src/main/lib/prdLocations.cjs +61 -0
- package/src/main/lib/prdMigration.cjs +2 -3
- package/src/main/lib/prdSizing.cjs +2 -1
- package/src/main/lib/projectHomeAdminRoutes.cjs +136 -21
- package/src/main/lib/promptSessionSchema.cjs +10 -2
- package/src/main/lib/rcaReport.cjs +4 -6
- package/src/main/lib/reaperHelpers.cjs +8 -1
- package/src/main/lib/reservationExpiry.cjs +6 -1
- package/src/main/lib/reviewNotice.cjs +263 -0
- package/src/main/lib/runClaudeP.cjs +4 -1
- package/src/main/lib/schedulerPaths.cjs +22 -2
- package/src/main/lib/sessionSlots.cjs +2 -4
- package/src/main/lib/shippedPersonaSeeds.cjs +226 -0
- package/src/main/lib/smfileHeaders.cjs +66 -0
- package/src/main/lib/timeoutShim.cjs +123 -0
- package/src/main/lib/timeoutShimScript.cjs +275 -0
- package/src/main/lib/upgradeDrain.cjs +4 -10
- package/src/main/lib/watchdogHelpers.cjs +12 -25
- package/src/main/lib/workTypeLibrary.cjs +7 -1
- package/src/main/projectPages.cjs +43 -13
- package/src/main/promptSessionEvents.cjs +37 -13
- package/src/main/scheduler.cjs +910 -198
- package/src/main/seedAgentPersonas.cjs +279 -24
- package/src/main/supervisor.cjs +4 -1
- package/src/main/templates/PRD_AUTHORING.md +126 -406
- package/src/preload/__tests__/preload-surface.test.cjs +68 -0
- package/src/preload/api.d.ts +53 -94
- package/src/preload/index.cjs +14 -9
- package/src/seed/agents/architect.md +2 -0
- package/src/seed/agents/demo-video-builder.md +84 -0
- package/src/seed/agents/dev-lead.md +18 -18
- package/src/seed/agents/project-home-builder.md +66 -35
- package/src/seed/agents/validator.md +10 -4
- package/dist/assets/DataModel-DCEgMTjH.js +0 -1
- package/dist/assets/HostBilko-CDVHE0hT.js +0 -1
- package/dist/assets/Permissions-BGleLja4.js +0 -3
- package/dist/assets/ProvenanceBadge-BFczzI1B.js +0 -1
- package/dist/assets/Scheduler-C_phpeUS.js +0 -16
- package/dist/assets/Settings-BIwZ3FGQ.js +0 -3
- package/src/main/__tests__/activeIndexMerge.test.cjs +0 -235
- package/src/main/__tests__/agentEffortResolve.test.cjs +0 -117
- package/src/main/__tests__/agentLibrary.test.cjs +0 -276
- package/src/main/__tests__/agentModelResolve.test.cjs +0 -311
- package/src/main/__tests__/agentOverlayWrite.test.cjs +0 -95
- package/src/main/__tests__/bilkoHost-deriveSlug.test.cjs +0 -26
- package/src/main/__tests__/bilkoHost-integration.test.cjs +0 -118
- package/src/main/__tests__/bilkoHostCore.test.cjs +0 -72
- package/src/main/__tests__/broadcastCoalescer.test.cjs +0 -122
- package/src/main/__tests__/chat-cancel-terminal.test.cjs +0 -120
- package/src/main/__tests__/chat-dead-channels.test.cjs +0 -63
- package/src/main/__tests__/chat-exit-close-race.test.cjs +0 -146
- package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +0 -139
- package/src/main/__tests__/chat-preamble-anchors.test.cjs +0 -100
- package/src/main/__tests__/chat-queue.test.cjs +0 -97
- package/src/main/__tests__/chat-stop-signal.test.cjs +0 -89
- package/src/main/__tests__/chatRunner-epic-worktree-execcwd.test.cjs +0 -125
- package/src/main/__tests__/chatRunner-session-flag-retry.test.cjs +0 -252
- package/src/main/__tests__/classifyPromptTicket.test.cjs +0 -101
- package/src/main/__tests__/classifyTranscriptLine.test.cjs +0 -201
- package/src/main/__tests__/computeDepHistorySatisfaction.test.cjs +0 -66
- package/src/main/__tests__/config-readText-bounded.test.cjs +0 -84
- package/src/main/__tests__/configWriteBoundaryOwners.test.cjs +0 -59
- package/src/main/__tests__/crossProjectFeedback.test.cjs +0 -334
- package/src/main/__tests__/crossProjectFeedbackRoutes.test.cjs +0 -161
- package/src/main/__tests__/dep-orphan-archive-health.test.cjs +0 -77
- package/src/main/__tests__/develop-skill-failure-modes.test.cjs +0 -70
- package/src/main/__tests__/docEdit.test.cjs +0 -244
- package/src/main/__tests__/dod-batchkey.test.cjs +0 -183
- package/src/main/__tests__/dod-drain-hook.test.cjs +0 -302
- package/src/main/__tests__/dod-report.test.cjs +0 -304
- package/src/main/__tests__/dod-reverify.test.cjs +0 -285
- package/src/main/__tests__/epicContextDigest.test.cjs +0 -174
- package/src/main/__tests__/epicMint.test.cjs +0 -332
- package/src/main/__tests__/epicMintTelemetryTap.test.cjs +0 -64
- package/src/main/__tests__/epicStatusMirror.test.cjs +0 -110
- package/src/main/__tests__/epicValidationHook.test.cjs +0 -291
- package/src/main/__tests__/exchanges.test.cjs +0 -122
- package/src/main/__tests__/exchangesPromptId.test.cjs +0 -61
- package/src/main/__tests__/extractJson.test.cjs +0 -51
- package/src/main/__tests__/files-reject-credentials.test.cjs +0 -40
- package/src/main/__tests__/fixtures/1218-fo-01-move-scripts-lib-into-src-main-lib.log +0 -556
- package/src/main/__tests__/flatPrdTickSweep.test.cjs +0 -110
- package/src/main/__tests__/health-build-freshness.test.cjs +0 -39
- package/src/main/__tests__/health-claude-md-budget.test.cjs +0 -57
- package/src/main/__tests__/health-credentials.test.cjs +0 -81
- package/src/main/__tests__/health-delegation-chain.test.cjs +0 -124
- package/src/main/__tests__/health-per-project-stall.test.cjs +0 -84
- package/src/main/__tests__/health-prd-migration.test.cjs +0 -37
- package/src/main/__tests__/health-queue-dispatch.test.cjs +0 -135
- package/src/main/__tests__/health-starve-escalation.test.cjs +0 -94
- package/src/main/__tests__/health-tick-liveness.test.cjs +0 -179
- package/src/main/__tests__/health-usage-poller.test.cjs +0 -144
- package/src/main/__tests__/health-worktree-cap-blocked.test.cjs +0 -65
- package/src/main/__tests__/heapSnapshot.test.cjs +0 -121
- package/src/main/__tests__/historyAggregatorIntraday.test.cjs +0 -313
- package/src/main/__tests__/historyDashboard.test.cjs +0 -163
- package/src/main/__tests__/historyRollup.test.cjs +0 -333
- package/src/main/__tests__/intradayRefresh.test.cjs +0 -39
- package/src/main/__tests__/ipcSchemas-dependsOn.test.cjs +0 -39
- package/src/main/__tests__/kg-augment.test.cjs +0 -195
- package/src/main/__tests__/loadGateDetailTick.test.cjs +0 -31
- package/src/main/__tests__/machineProfile.test.cjs +0 -152
- package/src/main/__tests__/mcpStatus.test.cjs +0 -61
- package/src/main/__tests__/memoryAggregate.test.cjs +0 -109
- package/src/main/__tests__/memoryStale.test.cjs +0 -88
- package/src/main/__tests__/needsReviewLedger.test.cjs +0 -162
- package/src/main/__tests__/openExternalApp-spawn-error.test.cjs +0 -25
- package/src/main/__tests__/opsErrorLog.test.cjs +0 -109
- package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +0 -173
- package/src/main/__tests__/personaMerge.test.cjs +0 -169
- package/src/main/__tests__/planValidator.test.cjs +0 -125
- package/src/main/__tests__/pollLoop-dispatch-on-failure.test.cjs +0 -176
- package/src/main/__tests__/prd-group-allocator.test.cjs +0 -119
- package/src/main/__tests__/prdAdminRouteParity.test.cjs +0 -70
- package/src/main/__tests__/prdAdminRoutes.test.cjs +0 -718
- package/src/main/__tests__/prdAgentType.test.cjs +0 -103
- package/src/main/__tests__/prdAuthoringSeed.test.cjs +0 -39
- package/src/main/__tests__/prdCreate.test.cjs +0 -979
- package/src/main/__tests__/prdCreateAdoption.test.cjs +0 -205
- package/src/main/__tests__/prdCreateDisposition.test.cjs +0 -201
- package/src/main/__tests__/prdCreatePlanId.test.cjs +0 -132
- package/src/main/__tests__/prdFrontmatterAgentType.test.cjs +0 -117
- package/src/main/__tests__/prdFrontmatterDependsOn.test.cjs +0 -136
- package/src/main/__tests__/prdFrontmatterDisposition.test.cjs +0 -125
- package/src/main/__tests__/prdFrontmatterQuietMachine.test.cjs +0 -108
- package/src/main/__tests__/prdLocations.test.cjs +0 -195
- package/src/main/__tests__/prdLocationsArchived.test.cjs +0 -201
- package/src/main/__tests__/prdMigration.test.cjs +0 -349
- package/src/main/__tests__/prdMigrationLegacyAdopt.test.cjs +0 -91
- package/src/main/__tests__/prdParserHighWater.test.cjs +0 -74
- package/src/main/__tests__/prdParserSourcePromptId.test.cjs +0 -65
- package/src/main/__tests__/prdSetDisposition.test.cjs +0 -222
- package/src/main/__tests__/prdSizing.test.cjs +0 -106
- package/src/main/__tests__/prdSourcePromptIdBackfill.test.cjs +0 -118
- package/src/main/__tests__/prdUpdateDependsOn.test.cjs +0 -160
- package/src/main/__tests__/proc-role-env.test.cjs +0 -125
- package/src/main/__tests__/procname-claude-spawn-sites.test.cjs +0 -304
- package/src/main/__tests__/procname-sm-processes.test.cjs +0 -127
- package/src/main/__tests__/projectHomeAdminRoutes.test.cjs +0 -177
- package/src/main/__tests__/projectPages.test.cjs +0 -137
- package/src/main/__tests__/promptSessionEvents.test.cjs +0 -234
- package/src/main/__tests__/promptSessionSchema.test.cjs +0 -101
- package/src/main/__tests__/promptSessionTranscript.test.cjs +0 -0
- package/src/main/__tests__/promptSessionsCreateEpicHandler.test.cjs +0 -159
- package/src/main/__tests__/pty-epic-worktree-spawn-cwd.test.cjs +0 -282
- package/src/main/__tests__/pty-session-open-telemetry.test.cjs +0 -96
- package/src/main/__tests__/pty-write-result.test.cjs +0 -46
- package/src/main/__tests__/queue-health-verdict.test.cjs +0 -170
- package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +0 -286
- package/src/main/__tests__/queue-starvation-per-project.test.cjs +0 -147
- package/src/main/__tests__/queueHistory.test.cjs +0 -355
- package/src/main/__tests__/queueOps-interactive-ac-lint.test.cjs +0 -65
- package/src/main/__tests__/queueOpsArchiveDestination.test.cjs +0 -65
- package/src/main/__tests__/queueOpsAutoArchive.test.cjs +0 -211
- package/src/main/__tests__/rateLimitPollerStreak.test.cjs +0 -128
- package/src/main/__tests__/rcaReport.test.cjs +0 -266
- package/src/main/__tests__/reconcileFlatPrdSweep.test.cjs +0 -119
- package/src/main/__tests__/reconcileTiming.test.cjs +0 -135
- package/src/main/__tests__/runLogRetention.test.cjs +0 -489
- package/src/main/__tests__/runVerify-atomic-verdicts.test.cjs +0 -26
- package/src/main/__tests__/runVerify-blocked-by-foreign-wip.test.cjs +0 -58
- package/src/main/__tests__/runVerify-landed-commit-outranks.test.cjs +0 -191
- package/src/main/__tests__/runVerify-policy-denial.test.cjs +0 -89
- package/src/main/__tests__/runVerify-transcript-commit-evidence.test.cjs +0 -225
- package/src/main/__tests__/runVerify.test.cjs +0 -1784
- package/src/main/__tests__/scheduleJobSchema.test.cjs +0 -127
- package/src/main/__tests__/scheduleJobStatusDrift.test.cjs +0 -65
- package/src/main/__tests__/scheduleJobTransitions.test.cjs +0 -277
- package/src/main/__tests__/scheduleJobTransitionsGrep.test.cjs +0 -59
- package/src/main/__tests__/scheduleJobTransitionsTelemetryTap.test.cjs +0 -72
- package/src/main/__tests__/scheduler-admin-routes.test.cjs +0 -199
- package/src/main/__tests__/scheduler-adopted-run-supervision.test.cjs +0 -143
- package/src/main/__tests__/scheduler-already-satisfied-on-main.test.cjs +0 -105
- package/src/main/__tests__/scheduler-archive-completed-prd.test.cjs +0 -102
- package/src/main/__tests__/scheduler-archived-twin-guard.test.cjs +0 -155
- package/src/main/__tests__/scheduler-autofix-outcome.test.cjs +0 -188
- package/src/main/__tests__/scheduler-autofix-select.test.cjs +0 -439
- package/src/main/__tests__/scheduler-autopromote.test.cjs +0 -51
- package/src/main/__tests__/scheduler-bash-timeout-env.test.cjs +0 -101
- package/src/main/__tests__/scheduler-blocked-by-foreign-wip.test.cjs +0 -107
- package/src/main/__tests__/scheduler-boot-orphans.test.cjs +0 -153
- package/src/main/__tests__/scheduler-broadcast-reconcile.test.cjs +0 -121
- package/src/main/__tests__/scheduler-clear-queue-history.test.cjs +0 -134
- package/src/main/__tests__/scheduler-commit-guard-noop.test.cjs +0 -244
- package/src/main/__tests__/scheduler-committed-in-window.test.cjs +0 -182
- package/src/main/__tests__/scheduler-cross-project-batch.test.cjs +0 -43
- package/src/main/__tests__/scheduler-default-eligible-heal.test.cjs +0 -168
- package/src/main/__tests__/scheduler-dispatch-loop.test.cjs +0 -58
- package/src/main/__tests__/scheduler-effective-concurrency.test.cjs +0 -81
- package/src/main/__tests__/scheduler-epic-digest.test.cjs +0 -227
- package/src/main/__tests__/scheduler-failed-autoreset.test.cjs +0 -121
- package/src/main/__tests__/scheduler-finalize-dispatch-guards.test.cjs +0 -229
- package/src/main/__tests__/scheduler-find-prd-dir.test.cjs +0 -75
- package/src/main/__tests__/scheduler-fix-plan-path.test.cjs +0 -119
- package/src/main/__tests__/scheduler-force-tick-outcome.test.cjs +0 -46
- package/src/main/__tests__/scheduler-foreign-wip-manifest.test.cjs +0 -78
- package/src/main/__tests__/scheduler-gate-shadow.test.cjs +0 -119
- package/src/main/__tests__/scheduler-guard-verdict-autoresolve.test.cjs +0 -390
- package/src/main/__tests__/scheduler-heal-refusal.test.cjs +0 -61
- package/src/main/__tests__/scheduler-heartbeat-payload.test.cjs +0 -80
- package/src/main/__tests__/scheduler-inplace-salvage.test.cjs +0 -323
- package/src/main/__tests__/scheduler-integration-failure-stamp.test.cjs +0 -41
- package/src/main/__tests__/scheduler-investigation-clean-skip.test.cjs +0 -63
- package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +0 -123
- package/src/main/__tests__/scheduler-job-budget.test.cjs +0 -172
- package/src/main/__tests__/scheduler-job-overrun.test.cjs +0 -175
- package/src/main/__tests__/scheduler-launch-failure.test.cjs +0 -199
- package/src/main/__tests__/scheduler-leftover-fields.test.cjs +0 -52
- package/src/main/__tests__/scheduler-leftover-quarantine.test.cjs +0 -199
- package/src/main/__tests__/scheduler-looks-done.test.cjs +0 -537
- package/src/main/__tests__/scheduler-manual-pause.test.cjs +0 -118
- package/src/main/__tests__/scheduler-mechanical-recovery.test.cjs +0 -245
- package/src/main/__tests__/scheduler-meta-code-sha.test.cjs +0 -46
- package/src/main/__tests__/scheduler-mutate-reentrancy.test.cjs +0 -117
- package/src/main/__tests__/scheduler-needs-review-autoresolve.test.cjs +0 -197
- package/src/main/__tests__/scheduler-never-stop.test.cjs +0 -157
- package/src/main/__tests__/scheduler-no-dead-end-status.test.cjs +0 -152
- package/src/main/__tests__/scheduler-no-orphan-run-dir.test.cjs +0 -81
- package/src/main/__tests__/scheduler-notify-originating-tab-transcript.test.cjs +0 -87
- package/src/main/__tests__/scheduler-notify-originating-tab.test.cjs +0 -343
- package/src/main/__tests__/scheduler-periodic-reverify-guard.test.cjs +0 -217
- package/src/main/__tests__/scheduler-porcelain-rename.test.cjs +0 -164
- package/src/main/__tests__/scheduler-prd-missing-skip.test.cjs +0 -161
- package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +0 -178
- package/src/main/__tests__/scheduler-quarantine-autoresolve.test.cjs +0 -165
- package/src/main/__tests__/scheduler-quiet-machine-lease.test.cjs +0 -257
- package/src/main/__tests__/scheduler-rate-limit-cooldown-freshness.test.cjs +0 -123
- package/src/main/__tests__/scheduler-rate-limit-pause.test.cjs +0 -152
- package/src/main/__tests__/scheduler-rate-limit-spin-guard.test.cjs +0 -156
- package/src/main/__tests__/scheduler-reap-dead-running-jobs.test.cjs +0 -754
- package/src/main/__tests__/scheduler-reaper-helpers-basics.test.cjs +0 -87
- package/src/main/__tests__/scheduler-reconcile-cwd-preserve.test.cjs +0 -100
- package/src/main/__tests__/scheduler-reconcile-history-backfill.test.cjs +0 -105
- package/src/main/__tests__/scheduler-reconcile-invalid-repair.test.cjs +0 -203
- package/src/main/__tests__/scheduler-reconcile-quarantine.test.cjs +0 -247
- package/src/main/__tests__/scheduler-reset-job-fields-guard.test.cjs +0 -77
- package/src/main/__tests__/scheduler-resume-recovery.test.cjs +0 -254
- package/src/main/__tests__/scheduler-shard-quarantine.test.cjs +0 -115
- package/src/main/__tests__/scheduler-shared-tree-guard.test.cjs +0 -380
- package/src/main/__tests__/scheduler-sigterm-commit.test.cjs +0 -43
- package/src/main/__tests__/scheduler-stall-per-project.test.cjs +0 -126
- package/src/main/__tests__/scheduler-starve-escalation.test.cjs +0 -154
- package/src/main/__tests__/scheduler-stranded-autofix-park.test.cjs +0 -252
- package/src/main/__tests__/scheduler-stranded-investigation.test.cjs +0 -185
- package/src/main/__tests__/scheduler-stuck-failed-escalation.test.cjs +0 -136
- package/src/main/__tests__/scheduler-supervisor-record.test.cjs +0 -81
- package/src/main/__tests__/scheduler-tick-cancel-token.test.cjs +0 -54
- package/src/main/__tests__/scheduler-tick-wedge.test.cjs +0 -172
- package/src/main/__tests__/scheduler-transient-failure.test.cjs +0 -141
- package/src/main/__tests__/scheduler-unreadable-queue-guard.test.cjs +0 -62
- package/src/main/__tests__/scheduler-utilization-hold.test.cjs +0 -89
- package/src/main/__tests__/scheduler-verify-prd-path.test.cjs +0 -109
- package/src/main/__tests__/scheduler-worktree-cap-defer.test.cjs +0 -234
- package/src/main/__tests__/scheduler-worktree-exec-cwd.test.cjs +0 -120
- package/src/main/__tests__/scheduler-writeprd-epic-rollback.test.cjs +0 -105
- package/src/main/__tests__/schedulerBatchRootBlocker.test.cjs +0 -117
- package/src/main/__tests__/schedulerStateSidecarRestore.test.cjs +0 -110
- package/src/main/__tests__/seedAgentPersonas.test.cjs +0 -184
- package/src/main/__tests__/seedSchedulerMcp.test.cjs +0 -211
- package/src/main/__tests__/seedStatus.test.cjs +0 -100
- package/src/main/__tests__/seedValidatorPersona.test.cjs +0 -116
- package/src/main/__tests__/stop-signal-anchor.test.cjs +0 -75
- package/src/main/__tests__/telemetryClient.test.cjs +0 -1055
- package/src/main/__tests__/telemetryContract.test.cjs +0 -930
- package/src/main/__tests__/telemetrySettings.test.cjs +0 -210
- package/src/main/__tests__/transcripts-batch-flush.test.cjs +0 -249
- package/src/main/__tests__/transcripts-doFlush-array.test.cjs +0 -124
- package/src/main/__tests__/transcripts-paged-reads.test.cjs +0 -241
- package/src/main/__tests__/transcripts-worktree-epic-path.test.cjs +0 -154
- package/src/main/__tests__/transcriptsUsageFor.test.cjs +0 -206
- package/src/main/__tests__/uniquePrdNumbers.test.cjs +0 -153
- package/src/main/__tests__/usageSingleFlight.test.cjs +0 -169
- package/src/main/__tests__/validationSentinels.test.cjs +0 -84
- package/src/main/__tests__/workTypeLibrary.test.cjs +0 -89
- package/src/main/bilkoHost.cjs +0 -314
- package/src/main/bilkoHostCore.cjs +0 -89
- package/src/main/lib/__tests__/active-sessions.test.cjs +0 -251
- package/src/main/lib/__tests__/activeIndexRebuild.test.cjs +0 -179
- package/src/main/lib/__tests__/agentPersonaSchema.test.cjs +0 -67
- package/src/main/lib/__tests__/auditLog.test.cjs +0 -38
- package/src/main/lib/__tests__/bootSelfHeal.test.cjs +0 -107
- package/src/main/lib/__tests__/branchSweep.test.cjs +0 -164
- package/src/main/lib/__tests__/buildIdentity.test.cjs +0 -121
- package/src/main/lib/__tests__/buildTarget.test.cjs +0 -52
- package/src/main/lib/__tests__/childWithLog.test.cjs +0 -321
- package/src/main/lib/__tests__/coldBootPromptSessionsWrite.test.cjs +0 -87
- package/src/main/lib/__tests__/crashTelemetry.test.cjs +0 -103
- package/src/main/lib/__tests__/credentials-futile-refresh.test.cjs +0 -115
- package/src/main/lib/__tests__/cwdClassify.test.cjs +0 -111
- package/src/main/lib/__tests__/definitionOfDoneSequence.test.cjs +0 -95
- package/src/main/lib/__tests__/delegationReadiness.test.cjs +0 -1175
- package/src/main/lib/__tests__/dispatchLoop.test.cjs +0 -63
- package/src/main/lib/__tests__/effectiveModelInfo.test.cjs +0 -244
- package/src/main/lib/__tests__/ephemeralCwd.test.cjs +0 -91
- package/src/main/lib/__tests__/epicDelegationStats.test.cjs +0 -137
- package/src/main/lib/__tests__/epicSpawnCwd.test.cjs +0 -283
- package/src/main/lib/__tests__/epicSpawnPlan.test.cjs +0 -196
- package/src/main/lib/__tests__/epicTranscriptPath.test.cjs +0 -163
- package/src/main/lib/__tests__/epicWorktreeBoot.test.cjs +0 -136
- package/src/main/lib/__tests__/epicWorktreeMerge.test.cjs +0 -130
- package/src/main/lib/__tests__/epicWorktreeMint.test.cjs +0 -117
- package/src/main/lib/__tests__/epicWorktreeProjectConfig.test.cjs +0 -137
- package/src/main/lib/__tests__/fixChainDepth.test.cjs +0 -40
- package/src/main/lib/__tests__/fixtures/204-mercury-steam-horse.log.txt +0 -13
- package/src/main/lib/__tests__/fixtures/scheduler-machine.json.corrupt-1789147548 +0 -34
- package/src/main/lib/__tests__/gateFixtures.json +0 -20
- package/src/main/lib/__tests__/gitCacheBound.test.cjs +0 -69
- package/src/main/lib/__tests__/gitWorktree.test.cjs +0 -1478
- package/src/main/lib/__tests__/gitWorktreeSalvage.test.cjs +0 -107
- package/src/main/lib/__tests__/gitWorktreeSalvageDelta.test.cjs +0 -153
- package/src/main/lib/__tests__/guardShims.test.cjs +0 -158
- package/src/main/lib/__tests__/importReferences.spec.cjs +0 -56
- package/src/main/lib/__tests__/instanceLock.test.cjs +0 -173
- package/src/main/lib/__tests__/jobSupervisorRecord.test.cjs +0 -78
- package/src/main/lib/__tests__/jobWorktree.test.cjs +0 -199
- package/src/main/lib/__tests__/jobWorktreeBootLive.test.cjs +0 -82
- package/src/main/lib/__tests__/landedSinceRun.test.cjs +0 -133
- package/src/main/lib/__tests__/launchFailure.test.cjs +0 -220
- package/src/main/lib/__tests__/loadGate.test.cjs +0 -303
- package/src/main/lib/__tests__/localAdminHttp.test.cjs +0 -214
- package/src/main/lib/__tests__/loopDelay.test.cjs +0 -68
- package/src/main/lib/__tests__/mcpToolCatalog.test.cjs +0 -107
- package/src/main/lib/__tests__/modelCatalog.test.cjs +0 -202
- package/src/main/lib/__tests__/opsOwnership.test.cjs +0 -113
- package/src/main/lib/__tests__/opsRootAbsoluteCwd.test.cjs +0 -328
- package/src/main/lib/__tests__/opsRootNestedWrite.test.cjs +0 -51
- package/src/main/lib/__tests__/opsRootResolve.test.cjs +0 -149
- package/src/main/lib/__tests__/prdDeclaredPaths.test.cjs +0 -82
- package/src/main/lib/__tests__/prdDisposition.test.cjs +0 -224
- package/src/main/lib/__tests__/procIdentity.test.cjs +0 -119
- package/src/main/lib/__tests__/procName.test.cjs +0 -92
- package/src/main/lib/__tests__/projectBriefCore.test.cjs +0 -216
- package/src/main/lib/__tests__/projectRootResolve.test.cjs +0 -148
- package/src/main/lib/__tests__/queueHealth.test.cjs +0 -58
- package/src/main/lib/__tests__/queueStoreAtomicWrite.test.cjs +0 -88
- package/src/main/lib/__tests__/queueStoreMachineStateRecovery.test.cjs +0 -190
- package/src/main/lib/__tests__/quietMachineLease.test.cjs +0 -39
- package/src/main/lib/__tests__/rateLimitWindow.test.cjs +0 -88
- package/src/main/lib/__tests__/reaperHelpers.test.cjs +0 -577
- package/src/main/lib/__tests__/schedulerBatchDepends.test.cjs +0 -312
- package/src/main/lib/__tests__/schedulerBatchFairness.test.cjs +0 -213
- package/src/main/lib/__tests__/schedulerBatchLaunchHold.test.cjs +0 -125
- package/src/main/lib/__tests__/schedulerBatchProjectCap.test.cjs +0 -127
- package/src/main/lib/__tests__/schedulerBatchQuietMachine.test.cjs +0 -109
- package/src/main/lib/__tests__/schedulerMcpServerHeadlessRefusal.test.cjs +0 -71
- package/src/main/lib/__tests__/schedulerMcpServerHelp.test.cjs +0 -216
- package/src/main/lib/__tests__/schedulerMcpServerProjectHome.test.cjs +0 -183
- package/src/main/lib/__tests__/schedulerPaths.test.cjs +0 -226
- package/src/main/lib/__tests__/schedulerPathsWorktree.test.cjs +0 -133
- package/src/main/lib/__tests__/schedulerRuntimeState.test.cjs +0 -56
- package/src/main/lib/__tests__/sessionSlots.test.cjs +0 -144
- package/src/main/lib/__tests__/telemetryBacklog.test.cjs +0 -626
- package/src/main/lib/__tests__/telemetryBoot.test.cjs +0 -134
- package/src/main/lib/__tests__/telemetryConsent.test.cjs +0 -136
- package/src/main/lib/__tests__/telemetryCounters.test.cjs +0 -57
- package/src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs +0 -98
- package/src/main/lib/__tests__/terminalRunOutcome.test.cjs +0 -200
- package/src/main/lib/__tests__/toolUseClassify.test.cjs +0 -53
- package/src/main/lib/__tests__/updateCheck.test.cjs +0 -63
- package/src/main/lib/__tests__/upgradeDrain.test.cjs +0 -130
- package/src/main/lib/__tests__/usageCircuit.test.cjs +0 -354
- package/src/main/lib/__tests__/watchdog-helpers.test.cjs +0 -441
- package/src/main/lib/__tests__/watchdog-relaunch.test.cjs +0 -266
- package/src/main/lib/kgExchangePairing.cjs +0 -75
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* chat-exit-close-race.test.cjs — regression for the exit/close race that
|
|
3
|
-
* silently dropped a pure-text-only final turn (PRD 716).
|
|
4
|
-
*
|
|
5
|
-
* Bug: the terminal-event fallback was wired to `child.on('exit', ...)`
|
|
6
|
-
* instead of `child.on('close', ...)`. Node only guarantees all buffered
|
|
7
|
-
* stdout 'data' events have been delivered by 'close', not 'exit' — 'exit'
|
|
8
|
-
* can fire before the final stdout chunk (last assistant text + terminating
|
|
9
|
-
* `result` line) reaches the parent's 'data' handler. When that happened,
|
|
10
|
-
* the exit fallback ran first, set the one-shot terminalSent latch, and
|
|
11
|
-
* broadcast a misleading chat:run:error. The real result line then parsed
|
|
12
|
-
* moments later, but emitTerminal silently no-op'd and the genuine
|
|
13
|
-
* chat:run:complete was dropped.
|
|
14
|
-
*
|
|
15
|
-
* Fix: use `child.on('close', ...)` for the fallback so it only fires once
|
|
16
|
-
* Node guarantees stdout is fully drained — a real result event that arrives
|
|
17
|
-
* (even "late", right up against process exit) always wins.
|
|
18
|
-
*
|
|
19
|
-
* This test mocks node:child_process.spawn so it can deterministically
|
|
20
|
-
* reproduce the exact race ordering (exit fires, THEN the final stdout chunk
|
|
21
|
-
* arrives, THEN close fires) rather than relying on real OS/pipe timing,
|
|
22
|
-
* which is not reliably reproducible from a real child process.
|
|
23
|
-
*
|
|
24
|
-
* Run: timeout 300 npx vitest run src/main/__tests__/chat-exit-close-race.test.cjs
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
'use strict';
|
|
28
|
-
|
|
29
|
-
delete process.env.SM_CHAT_CONCURRENCY;
|
|
30
|
-
|
|
31
|
-
import { test, afterAll } from 'vitest';
|
|
32
|
-
const assert = require('node:assert/strict');
|
|
33
|
-
const EventEmitter = require('node:events');
|
|
34
|
-
const cp = require('node:child_process');
|
|
35
|
-
const fs = require('node:fs');
|
|
36
|
-
const os = require('node:os');
|
|
37
|
-
const path = require('node:path');
|
|
38
|
-
|
|
39
|
-
// Replace spawn BEFORE chatRunner.cjs is first required, so the module's
|
|
40
|
-
// top-level `const { spawn } = require('node:child_process')` destructuring
|
|
41
|
-
// captures this mock instead of the real implementation.
|
|
42
|
-
const originalSpawn = cp.spawn;
|
|
43
|
-
let nextChild = null;
|
|
44
|
-
cp.spawn = () => {
|
|
45
|
-
const child = new EventEmitter();
|
|
46
|
-
child.stdout = new EventEmitter();
|
|
47
|
-
child.stderr = new EventEmitter();
|
|
48
|
-
child.pid = 424242;
|
|
49
|
-
child.kill = () => {};
|
|
50
|
-
nextChild = child;
|
|
51
|
-
return child;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const cr = require('../chatRunner.cjs');
|
|
55
|
-
|
|
56
|
-
// Scratch project dir under os.tmpdir() — never the real repo root (opsErrorLog would pollute it).
|
|
57
|
-
const scratchCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-chat-cwd-'));
|
|
58
|
-
|
|
59
|
-
const tick = () => new Promise((r) => setImmediate(r));
|
|
60
|
-
|
|
61
|
-
test('a final-text-only turn whose stdout chunk lands after exit still surfaces chat:run:complete', async () => {
|
|
62
|
-
nextChild = null;
|
|
63
|
-
const events = [];
|
|
64
|
-
cr.attachWindow({
|
|
65
|
-
isDestroyed: () => false,
|
|
66
|
-
webContents: {
|
|
67
|
-
isDestroyed: () => false,
|
|
68
|
-
send: (channel, payload) => events.push({ channel, payload }),
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
cr.run({ tabId: 'T2', sessionId: 'S2', prompt: 'show me a sample post', cwd: scratchCwd, resume: false });
|
|
73
|
-
|
|
74
|
-
for (let i = 0; i < 20 && !nextChild; i++) await tick();
|
|
75
|
-
const child = nextChild;
|
|
76
|
-
assert.ok(child, 'spawn should have been invoked');
|
|
77
|
-
|
|
78
|
-
const text = 'Here is your sample post, all in one go.';
|
|
79
|
-
const lines =
|
|
80
|
-
JSON.stringify({ type: 'assistant', message: { content: [{ type: 'text', text }] } }) +
|
|
81
|
-
'\n' +
|
|
82
|
-
JSON.stringify({ type: 'result', subtype: 'success', result: text }) +
|
|
83
|
-
'\n';
|
|
84
|
-
|
|
85
|
-
// The exact race: 'exit' fires first (process already gone from the OS's
|
|
86
|
-
// perspective), the final stdout chunk is delivered to the 'data' handler
|
|
87
|
-
// only after that, and 'close' — which Node guarantees fires after all
|
|
88
|
-
// stdout data has been delivered — fires last.
|
|
89
|
-
child.emit('exit', 0, null);
|
|
90
|
-
child.stdout.emit('data', Buffer.from(lines));
|
|
91
|
-
child.emit('close', 0, null);
|
|
92
|
-
|
|
93
|
-
await tick();
|
|
94
|
-
await tick();
|
|
95
|
-
|
|
96
|
-
const terminal = events.filter((e) => isTerminal(e.channel));
|
|
97
|
-
assert.equal(terminal.length, 1, 'exactly one terminal event is emitted');
|
|
98
|
-
assert.equal(
|
|
99
|
-
terminal[0].channel,
|
|
100
|
-
'chat:run:complete',
|
|
101
|
-
'the real result event must win over the generic exit-fallback error',
|
|
102
|
-
);
|
|
103
|
-
assert.equal(terminal[0].payload.finalMessage, text);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test('a crashed process with no result event ever emitted still fires the fallback chat:run:error', async () => {
|
|
107
|
-
nextChild = null;
|
|
108
|
-
const events = [];
|
|
109
|
-
cr.attachWindow({
|
|
110
|
-
isDestroyed: () => false,
|
|
111
|
-
webContents: {
|
|
112
|
-
isDestroyed: () => false,
|
|
113
|
-
send: (channel, payload) => events.push({ channel, payload }),
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
cr.run({ tabId: 'T3', sessionId: 'S3', prompt: 'anything', cwd: scratchCwd, resume: false });
|
|
118
|
-
|
|
119
|
-
for (let i = 0; i < 20 && !nextChild; i++) await tick();
|
|
120
|
-
const child = nextChild;
|
|
121
|
-
assert.ok(child, 'spawn should have been invoked');
|
|
122
|
-
|
|
123
|
-
// Crashes with no stdout at all — 'close' is the only event that fires.
|
|
124
|
-
child.emit('close', 1, null);
|
|
125
|
-
|
|
126
|
-
await tick();
|
|
127
|
-
await tick();
|
|
128
|
-
|
|
129
|
-
const terminal = events.filter((e) => isTerminal(e.channel));
|
|
130
|
-
assert.equal(terminal.length, 1, 'exactly one terminal event is emitted');
|
|
131
|
-
assert.equal(terminal[0].channel, 'chat:run:error', 'a crash with no result surfaces as an error turn');
|
|
132
|
-
assert.match(terminal[0].payload.message, /process exited without a result event/);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
function isTerminal(channel) {
|
|
136
|
-
return (
|
|
137
|
-
channel === 'chat:run:complete' ||
|
|
138
|
-
channel === 'chat:run:needs-input' ||
|
|
139
|
-
channel === 'chat:run:error'
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
afterAll(() => {
|
|
144
|
-
cp.spawn = originalSpawn;
|
|
145
|
-
delete process.env.SM_CHAT_CONCURRENCY;
|
|
146
|
-
});
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* chat-mcp-consent-notice.test.cjs — regression for the headless MCP-consent
|
|
3
|
-
* hang: a chat run with stdin closed can never answer an MCP server's own
|
|
4
|
-
* interactive consent flow (e.g. `/design consent`). This drives the real
|
|
5
|
-
* stdout parser with a synthetic stream-json `tool_result` denial line and
|
|
6
|
-
* asserts a `chat:run:notice` event is broadcast — informational, not a
|
|
7
|
-
* terminal event.
|
|
8
|
-
*
|
|
9
|
-
* Run: timeout 300 npx vitest run src/main/__tests__/chat-mcp-consent-notice.test.cjs
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
'use strict';
|
|
13
|
-
|
|
14
|
-
delete process.env.SM_CHAT_CONCURRENCY;
|
|
15
|
-
|
|
16
|
-
import { test } from 'vitest';
|
|
17
|
-
const assert = require('node:assert/strict');
|
|
18
|
-
const fs = require('node:fs');
|
|
19
|
-
const os = require('node:os');
|
|
20
|
-
const path = require('node:path');
|
|
21
|
-
|
|
22
|
-
// Scratch project dir under os.tmpdir() — never the real repo root (opsErrorLog would pollute it).
|
|
23
|
-
const scratchCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-chat-cwd-'));
|
|
24
|
-
const claudeStub = require('../../../tests/helpers/claudeStub.cjs');
|
|
25
|
-
|
|
26
|
-
function writeStub(lines) {
|
|
27
|
-
const body = lines.map((l) => `process.stdout.write(${JSON.stringify(JSON.stringify(l))} + "\\n");`).join('\n');
|
|
28
|
-
return claudeStub.writeClaudeStub({ body: `${body}\nprocess.exit(0);` });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function isTerminal(channel) {
|
|
32
|
-
return (
|
|
33
|
-
channel === 'chat:run:complete' ||
|
|
34
|
-
channel === 'chat:run:needs-input' ||
|
|
35
|
-
channel === 'chat:run:error'
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
test('MCP consent denial in a tool_result surfaces a chat:run:notice event', async () => {
|
|
40
|
-
const stubPath = writeStub([
|
|
41
|
-
{
|
|
42
|
-
type: 'user',
|
|
43
|
-
message: {
|
|
44
|
-
content: [
|
|
45
|
-
{
|
|
46
|
-
type: 'tool_result',
|
|
47
|
-
tool_use_id: 'toolu_1',
|
|
48
|
-
is_error: true,
|
|
49
|
-
content: [
|
|
50
|
-
{
|
|
51
|
-
type: 'text',
|
|
52
|
-
text:
|
|
53
|
-
"Claude Design: write_files The user hasn't granted this — run /design consent " +
|
|
54
|
-
"to grant it (it can't be approved automatically in this permission mode).",
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
{ type: 'result', subtype: 'success', result: 'done anyway' },
|
|
62
|
-
]);
|
|
63
|
-
process.env.SM_CLAUDE_BIN = stubPath;
|
|
64
|
-
delete require.cache[require.resolve('../chatRunner.cjs')];
|
|
65
|
-
const cr = require('../chatRunner.cjs');
|
|
66
|
-
|
|
67
|
-
const events = [];
|
|
68
|
-
cr.attachWindow({
|
|
69
|
-
isDestroyed: () => false,
|
|
70
|
-
webContents: {
|
|
71
|
-
isDestroyed: () => false,
|
|
72
|
-
send: (channel, payload) => events.push({ channel, payload }),
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
cr.run({ tabId: 'T-notice', sessionId: 'S-notice', prompt: 'do a design thing', cwd: scratchCwd, resume: false });
|
|
77
|
-
|
|
78
|
-
for (let i = 0; i < 60 && !events.some((e) => isTerminal(e.channel)); i++) {
|
|
79
|
-
await new Promise((r) => setTimeout(r, 25));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const notice = events.find((e) => e.channel === 'chat:run:notice');
|
|
83
|
-
assert.ok(notice, 'chat:run:notice should be broadcast');
|
|
84
|
-
assert.match(notice.payload.message, /consent/i, 'message mentions consent');
|
|
85
|
-
assert.match(notice.payload.message, /terminal/i, 'message mentions the raw terminal session');
|
|
86
|
-
|
|
87
|
-
// Notice is informational, not terminal — the normal result still fires.
|
|
88
|
-
const terminal = events.filter((e) => isTerminal(e.channel));
|
|
89
|
-
assert.equal(terminal.length, 1, 'exactly one terminal event still fires');
|
|
90
|
-
assert.equal(terminal[0].channel, 'chat:run:complete');
|
|
91
|
-
|
|
92
|
-
try { fs.unlinkSync(stubPath); } catch { /* already gone */ }
|
|
93
|
-
delete process.env.SM_CLAUDE_BIN;
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test('a normal tool_result with no consent marker does not fire chat:run:notice', async () => {
|
|
97
|
-
const stubPath = writeStub([
|
|
98
|
-
{
|
|
99
|
-
type: 'user',
|
|
100
|
-
message: {
|
|
101
|
-
content: [
|
|
102
|
-
{
|
|
103
|
-
type: 'tool_result',
|
|
104
|
-
tool_use_id: 'toolu_2',
|
|
105
|
-
is_error: false,
|
|
106
|
-
content: [{ type: 'text', text: 'Wrote 3 files successfully.' }],
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
{ type: 'result', subtype: 'success', result: 'all good' },
|
|
112
|
-
]);
|
|
113
|
-
process.env.SM_CLAUDE_BIN = stubPath;
|
|
114
|
-
delete require.cache[require.resolve('../chatRunner.cjs')];
|
|
115
|
-
const cr = require('../chatRunner.cjs');
|
|
116
|
-
|
|
117
|
-
const events = [];
|
|
118
|
-
cr.attachWindow({
|
|
119
|
-
isDestroyed: () => false,
|
|
120
|
-
webContents: {
|
|
121
|
-
isDestroyed: () => false,
|
|
122
|
-
send: (channel, payload) => events.push({ channel, payload }),
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
cr.run({ tabId: 'T-clean', sessionId: 'S-clean', prompt: 'do a normal thing', cwd: scratchCwd, resume: false });
|
|
127
|
-
|
|
128
|
-
for (let i = 0; i < 60 && !events.some((e) => isTerminal(e.channel)); i++) {
|
|
129
|
-
await new Promise((r) => setTimeout(r, 25));
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
assert.ok(
|
|
133
|
-
!events.some((e) => e.channel === 'chat:run:notice'),
|
|
134
|
-
'no chat:run:notice should fire for a clean tool_result (no false positive)',
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
try { fs.unlinkSync(stubPath); } catch { /* already gone */ }
|
|
138
|
-
delete process.env.SM_CLAUDE_BIN;
|
|
139
|
-
});
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* chat-preamble-anchors.test.cjs — drift guard between chatRunner's injected
|
|
3
|
-
* instruction blocks and the renderer's splitter that hides them.
|
|
4
|
-
*
|
|
5
|
-
* `src/renderer/lib/promptPreamble.ts` anchors each block on its FIRST and
|
|
6
|
-
* LAST sentence only, because it cannot import this main-process .cjs. That
|
|
7
|
-
* indirection is safe ONLY while those anchors still bracket the real text —
|
|
8
|
-
* so this test composes the exact `fullPrompt` chatRunner builds and asserts
|
|
9
|
-
* the splitter recovers the human's words exactly. Reword either block's
|
|
10
|
-
* opening or closing sentence and this fails here, loudly, instead of the
|
|
11
|
-
* boilerplate silently reappearing in every user bubble.
|
|
12
|
-
*
|
|
13
|
-
* Run: timeout 120 npx vitest run src/main/__tests__/chat-preamble-anchors.test.cjs
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
'use strict';
|
|
17
|
-
|
|
18
|
-
const path = require('path');
|
|
19
|
-
|
|
20
|
-
import { test, expect } from 'vitest';
|
|
21
|
-
|
|
22
|
-
const { STOP_SIGNAL_INSTRUCTION, CHAT_MODE_TRUTH_INSTRUCTION } = require('../chatRunner.cjs');
|
|
23
|
-
|
|
24
|
-
const HUMAN = 'Review the Alpaca limits and report back what we can trade for free.';
|
|
25
|
-
|
|
26
|
-
async function loadSplitter() {
|
|
27
|
-
const mod = await import(
|
|
28
|
-
/* @vite-ignore */ path.resolve(__dirname, '..', '..', 'renderer', 'lib', 'promptPreamble.ts')
|
|
29
|
-
);
|
|
30
|
-
return mod;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
test('the real fullPrompt splits back into exactly the human’s words', async () => {
|
|
34
|
-
const { splitInjectedPreamble } = await loadSplitter();
|
|
35
|
-
// Mirrors chatRunner's own composition at its `-p` call site.
|
|
36
|
-
const fullPrompt = STOP_SIGNAL_INSTRUCTION + CHAT_MODE_TRUTH_INSTRUCTION + HUMAN;
|
|
37
|
-
|
|
38
|
-
const split = splitInjectedPreamble(fullPrompt);
|
|
39
|
-
expect(split.body).toBe(HUMAN);
|
|
40
|
-
expect(split.blockKeys).toEqual(['stop-signal', 'chat-mode-truth']);
|
|
41
|
-
expect(split.preamble).toBeTruthy();
|
|
42
|
-
// Nothing is invented or lost: preamble + body reconstitute the input.
|
|
43
|
-
expect(split.preamble.length + split.body.length).toBeLessThanOrEqual(fullPrompt.length);
|
|
44
|
-
expect(fullPrompt.startsWith(split.preamble)).toBe(true);
|
|
45
|
-
expect(fullPrompt.endsWith(split.body)).toBe(true);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test('each block strips independently, so one being absent still works', async () => {
|
|
49
|
-
const { splitInjectedPreamble } = await loadSplitter();
|
|
50
|
-
|
|
51
|
-
const onlyStop = splitInjectedPreamble(STOP_SIGNAL_INSTRUCTION + HUMAN);
|
|
52
|
-
expect(onlyStop.body).toBe(HUMAN);
|
|
53
|
-
expect(onlyStop.blockKeys).toEqual(['stop-signal']);
|
|
54
|
-
|
|
55
|
-
const onlyTruth = splitInjectedPreamble(CHAT_MODE_TRUTH_INSTRUCTION + HUMAN);
|
|
56
|
-
expect(onlyTruth.body).toBe(HUMAN);
|
|
57
|
-
expect(onlyTruth.blockKeys).toEqual(['chat-mode-truth']);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test('a prompt with no injected blocks is returned untouched', async () => {
|
|
61
|
-
const { splitInjectedPreamble } = await loadSplitter();
|
|
62
|
-
const split = splitInjectedPreamble(HUMAN);
|
|
63
|
-
expect(split.preamble).toBe(null);
|
|
64
|
-
expect(split.body).toBe(HUMAN);
|
|
65
|
-
expect(split.blockKeys).toEqual([]);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('EDGE: a boilerplate-only prompt is NOT stripped to an empty bubble', async () => {
|
|
69
|
-
const { splitInjectedPreamble } = await loadSplitter();
|
|
70
|
-
const onlyBoilerplate = STOP_SIGNAL_INSTRUCTION + CHAT_MODE_TRUTH_INSTRUCTION;
|
|
71
|
-
const split = splitInjectedPreamble(onlyBoilerplate);
|
|
72
|
-
expect(split.preamble).toBe(null);
|
|
73
|
-
expect(split.body).toBe(onlyBoilerplate);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test('EDGE: a truncated block (opening present, closing missing) is left fully intact', async () => {
|
|
77
|
-
const { splitInjectedPreamble } = await loadSplitter();
|
|
78
|
-
// Simulates a reworded/cut-off block: better to over-show boilerplate than
|
|
79
|
-
// to guess at a boundary and eat the human's words.
|
|
80
|
-
const truncated = `${STOP_SIGNAL_INSTRUCTION.slice(0, 120)}\n\n${HUMAN}`;
|
|
81
|
-
const split = splitInjectedPreamble(truncated);
|
|
82
|
-
expect(split.preamble).toBe(null);
|
|
83
|
-
expect(split.body).toBe(truncated);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test('the two blocks strip in either order', async () => {
|
|
87
|
-
const { splitInjectedPreamble } = await loadSplitter();
|
|
88
|
-
const reversed = CHAT_MODE_TRUTH_INSTRUCTION + STOP_SIGNAL_INSTRUCTION + HUMAN;
|
|
89
|
-
const split = splitInjectedPreamble(reversed);
|
|
90
|
-
expect(split.body).toBe(HUMAN);
|
|
91
|
-
expect(split.blockKeys).toEqual(['chat-mode-truth', 'stop-signal']);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test('EDGE: boilerplate quoted inside the human’s own message is never stripped', async () => {
|
|
95
|
-
const { splitInjectedPreamble } = await loadSplitter();
|
|
96
|
-
const quoting = `Why does chat prepend this?\n\n${STOP_SIGNAL_INSTRUCTION}`;
|
|
97
|
-
const split = splitInjectedPreamble(quoting);
|
|
98
|
-
expect(split.preamble).toBe(null);
|
|
99
|
-
expect(split.body).toBe(quoting);
|
|
100
|
-
});
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* chat-queue.test.cjs — unit tests for the chatRunner serial run queue (v0.34).
|
|
3
|
-
* Asserts the "up to two loops at a time" guarantee: SILENT (automated probe)
|
|
4
|
-
* runs execute up to the concurrency cap in FIFO order, and a queued
|
|
5
|
-
* (not-yet-started) silent run can be cancelled without running. PRD 493:
|
|
6
|
-
* this cap/FIFO governs silent runs only — manual runs bypass it entirely
|
|
7
|
-
* (see chatRunner.spec.ts's "manual runs are uncapped" tests).
|
|
8
|
-
*
|
|
9
|
-
* Run: timeout 300 npx vitest run src/main/__tests__/chat-queue.test.cjs
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
'use strict';
|
|
13
|
-
|
|
14
|
-
// Force the default cap (2) regardless of the developer's shell env.
|
|
15
|
-
delete process.env.SM_CHAT_CONCURRENCY;
|
|
16
|
-
|
|
17
|
-
import { test } from 'vitest';
|
|
18
|
-
const assert = require('node:assert/strict');
|
|
19
|
-
const cr = require('../chatRunner.cjs');
|
|
20
|
-
|
|
21
|
-
const tick = () => new Promise((r) => setImmediate(r));
|
|
22
|
-
const job = (id) => ({ tabId: id, sessionId: id, prompt: 'x', cwd: '/', resume: true, silent: true });
|
|
23
|
-
|
|
24
|
-
test('runs execute up to the concurrency cap in FIFO order', async () => {
|
|
25
|
-
const order = [];
|
|
26
|
-
let active = 0;
|
|
27
|
-
let maxActive = 0;
|
|
28
|
-
// Stub the spawn: each run resolves on the next tick, so the queue must
|
|
29
|
-
// advance via the pump's finally hook for C to ever run.
|
|
30
|
-
cr.__setExecutor((j) => {
|
|
31
|
-
active += 1;
|
|
32
|
-
maxActive = Math.max(maxActive, active);
|
|
33
|
-
order.push(j.tabId);
|
|
34
|
-
return new Promise((res) => setImmediate(() => { active -= 1; res(); }));
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
cr.run(job('A'));
|
|
38
|
-
cr.run(job('B'));
|
|
39
|
-
cr.run(job('C'));
|
|
40
|
-
|
|
41
|
-
// Drain the queue (each run takes one tick; allow generous headroom).
|
|
42
|
-
for (let i = 0; i < 12 && order.length < 3; i++) await tick();
|
|
43
|
-
await tick();
|
|
44
|
-
|
|
45
|
-
assert.equal(maxActive, 2, 'up to two runs active at once under the default cap');
|
|
46
|
-
assert.deepEqual(order, ['A', 'B', 'C'], 'runs execute in FIFO order');
|
|
47
|
-
|
|
48
|
-
cr.__setExecutor(null); // restore real executor
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('cancelling a queued run drops it without executing', async () => {
|
|
52
|
-
const order = [];
|
|
53
|
-
cr.__setExecutor((j) => {
|
|
54
|
-
order.push(j.tabId);
|
|
55
|
-
return new Promise((res) => setImmediate(res));
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// X and Y each take one of the two lanes; Z queues behind them. Cancel Z
|
|
59
|
-
// before a lane frees.
|
|
60
|
-
cr.run(job('X'));
|
|
61
|
-
cr.run(job('Y'));
|
|
62
|
-
cr.run(job('Z'));
|
|
63
|
-
cr.cancel('Z'); // Z is still in the waiting list — drop it
|
|
64
|
-
|
|
65
|
-
for (let i = 0; i < 8; i++) await tick();
|
|
66
|
-
|
|
67
|
-
assert.deepEqual(order, ['X', 'Y'], 'only X and Y ran; Z was cancelled while queued');
|
|
68
|
-
|
|
69
|
-
cr.__setExecutor(null);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('a queued silent run never broadcasts chat:run:queued (silent runs stay invisible to the renderer)', async () => {
|
|
73
|
-
const sent = [];
|
|
74
|
-
cr.attachWindow({
|
|
75
|
-
isDestroyed: () => false,
|
|
76
|
-
webContents: { isDestroyed: () => false, send: (channel, payload) => sent.push({ channel, payload }) },
|
|
77
|
-
});
|
|
78
|
-
cr.__setExecutor((j) => new Promise((res) => setImmediate(res)));
|
|
79
|
-
|
|
80
|
-
// P and Q take the two lanes under the default cap; R queues behind them —
|
|
81
|
-
// this is the only path that ever populates chatRunner's `waiting` list,
|
|
82
|
-
// and every job on it is silent (manual runs bypass the queue entirely).
|
|
83
|
-
cr.run(job('P'));
|
|
84
|
-
cr.run(job('Q'));
|
|
85
|
-
cr.run(job('R'));
|
|
86
|
-
|
|
87
|
-
for (let i = 0; i < 8; i++) await tick();
|
|
88
|
-
|
|
89
|
-
assert.equal(
|
|
90
|
-
sent.filter((s) => s.channel === 'chat:run:queued').length,
|
|
91
|
-
0,
|
|
92
|
-
'no chat:run:queued broadcast for a silent (automated probe) run',
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
cr.__setExecutor(null);
|
|
96
|
-
cr.attachWindow(null);
|
|
97
|
-
});
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* chat-stop-signal.test.cjs — unit tests for chatRunner.parseStopSignal, the
|
|
3
|
-
* single source of truth for the terminal-chat stop-signal protocol (PRD 318).
|
|
4
|
-
*
|
|
5
|
-
* Run: timeout 300 npx vitest run src/main/__tests__/chat-stop-signal.test.cjs
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use strict';
|
|
9
|
-
|
|
10
|
-
import { test } from 'vitest';
|
|
11
|
-
const assert = require('node:assert/strict');
|
|
12
|
-
const { parseStopSignal, STOP_SENTINEL } = require('../chatRunner.cjs');
|
|
13
|
-
|
|
14
|
-
test('sentinel present with valid JSON → questions array', () => {
|
|
15
|
-
const text =
|
|
16
|
-
'I did some work but need to know which target.\n' +
|
|
17
|
-
`${STOP_SENTINEL}\n` +
|
|
18
|
-
'{"questions":["Which environment?","Overwrite existing config?"]}';
|
|
19
|
-
const out = parseStopSignal(text);
|
|
20
|
-
assert.deepEqual(out, {
|
|
21
|
-
questions: ['Which environment?', 'Overwrite existing config?'],
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test('sentinel absent → null (run is complete)', () => {
|
|
26
|
-
const text = 'All done. I created the file and the tests pass.';
|
|
27
|
-
assert.equal(parseStopSignal(text), null);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test('sentinel present but malformed JSON → null (treated as complete, no crash)', () => {
|
|
31
|
-
const text = `Here is a summary.\n${STOP_SENTINEL}\n{questions: [oops not json}`;
|
|
32
|
-
assert.equal(parseStopSignal(text), null);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('sentinel present but JSON lacks questions array → null', () => {
|
|
36
|
-
const text = `${STOP_SENTINEL}\n{"foo":"bar"}`;
|
|
37
|
-
assert.equal(parseStopSignal(text), null);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('non-string input → null (no crash)', () => {
|
|
41
|
-
assert.equal(parseStopSignal(undefined), null);
|
|
42
|
-
assert.equal(parseStopSignal(null), null);
|
|
43
|
-
assert.equal(parseStopSignal(42), null);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('only the LAST sentinel occurrence is parsed', () => {
|
|
47
|
-
const text =
|
|
48
|
-
`${STOP_SENTINEL}\n{"questions":["stale earlier block"]}\n` +
|
|
49
|
-
'then the agent kept working...\n' +
|
|
50
|
-
`${STOP_SENTINEL}\n{"questions":["the real question"]}`;
|
|
51
|
-
assert.deepEqual(parseStopSignal(text), { questions: ['the real question'] });
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('blank lines between sentinel and JSON → questions array', () => {
|
|
55
|
-
const text = `${STOP_SENTINEL}\n\n\n{"questions":["Which environment?"]}`;
|
|
56
|
-
assert.deepEqual(parseStopSignal(text), { questions: ['Which environment?'] });
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('JSON wrapped in a ```json code fence → questions array', () => {
|
|
60
|
-
const text =
|
|
61
|
-
`${STOP_SENTINEL}\n` +
|
|
62
|
-
'```json\n' +
|
|
63
|
-
'{"questions":["Overwrite existing config?"]}\n' +
|
|
64
|
-
'```';
|
|
65
|
-
assert.deepEqual(parseStopSignal(text), { questions: ['Overwrite existing config?'] });
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('pretty-printed multi-line JSON → questions array', () => {
|
|
69
|
-
const text =
|
|
70
|
-
`${STOP_SENTINEL}\n` +
|
|
71
|
-
'{\n' +
|
|
72
|
-
' "questions": [\n' +
|
|
73
|
-
' "Which environment?",\n' +
|
|
74
|
-
' "Overwrite existing config?"\n' +
|
|
75
|
-
' ]\n' +
|
|
76
|
-
'}';
|
|
77
|
-
assert.deepEqual(parseStopSignal(text), {
|
|
78
|
-
questions: ['Which environment?', 'Overwrite existing config?'],
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test('trailing prose after the closing brace → questions array', () => {
|
|
83
|
-
const text =
|
|
84
|
-
`${STOP_SENTINEL}\n` +
|
|
85
|
-
'{"questions":["Which environment?"]}\n' +
|
|
86
|
-
'\n' +
|
|
87
|
-
'Let me know and I will continue.';
|
|
88
|
-
assert.deepEqual(parseStopSignal(text), { questions: ['Which environment?'] });
|
|
89
|
-
});
|