@noxsoft/anima 6.5.0 → 7.0.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.
Files changed (133) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +48 -0
  3. package/dist/{agent-VRQM14Xp.js → agent-BoAAHGEA.js} +3 -3
  4. package/dist/{agent-CnS0SRpT.js → agent-DuW0onwk.js} +4 -4
  5. package/dist/{agents-CvMRplDx.js → agents-BUXkSDns.js} +4 -4
  6. package/dist/{anthropic-direct-runner-C2Kwju-r.js → anthropic-direct-runner-DizCei79.js} +420 -4
  7. package/dist/{anthropic-direct-runner-BeYCnvZ8.js → anthropic-direct-runner-OjcTAH6g.js} +420 -3
  8. package/dist/{auth-choice-Dc5TAJwT.js → auth-choice-B1iGnjuE.js} +1 -1
  9. package/dist/{auth-choice-DY1saszS.js → auth-choice-HF9x6xk2.js} +1 -1
  10. package/dist/{banner-DAMtSjUF.js → banner-Dpa5d1If.js} +1 -1
  11. package/dist/build-info.json +3 -3
  12. package/dist/bundled/boot-md/handler.js +2 -2
  13. package/dist/bundled/session-memory/handler.js +1 -1
  14. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  15. package/dist/canvas-host/a2ui/a2ui.bundle.js +16410 -18893
  16. package/dist/{channel-web-B8mzTSaY.js → channel-web-C5mzsaa3.js} +3 -3
  17. package/dist/{cli-hcHk5KuP.js → cli-Cuq4bIg4.js} +2 -2
  18. package/dist/{cli-D8exVpuI.js → cli-X9ikywQ3.js} +3 -3
  19. package/dist/{command-registry-D3VhxpWx.js → command-registry-9V4uqrBV.js} +12 -12
  20. package/dist/{completion-cli-B3BqQJq9.js → completion-cli-BtvcR-U5.js} +1 -1
  21. package/dist/{completion-cli-CepDzeW1.js → completion-cli-DNWDwhab.js} +2 -2
  22. package/dist/{config-cli-B6Np85rk.js → config-cli-DfHE3KG-.js} +1 -1
  23. package/dist/{config-cli-3CaIxSKo.js → config-cli-fleq7-gq.js} +1 -1
  24. package/dist/{configure-zXK6UZ51.js → configure-B2Mfnwy_.js} +3 -3
  25. package/dist/{configure-D88dg6mE.js → configure-SnvMHZPD.js} +7 -7
  26. package/dist/{configure-D882Bg7c.js → configure-ZWxixuRA.js} +3 -3
  27. package/dist/{configure-xpjwedvJ.js → configure-lkozxQed.js} +8 -8
  28. package/dist/context-mdxDsO1v.js +223 -0
  29. package/dist/control-ui/assets/{index-DVpMpG5G.js → index-D4wqLVMN.js} +2 -2
  30. package/dist/control-ui/assets/{index-DVpMpG5G.js.map → index-D4wqLVMN.js.map} +1 -1
  31. package/dist/control-ui/assets/index-DIEQjjCN.js +73 -0
  32. package/dist/control-ui/assets/index-DIEQjjCN.js.map +1 -0
  33. package/dist/control-ui/assets/{observers-CxfWf9RO.js → observers-B7MfWiIZ.js} +2 -2
  34. package/dist/control-ui/assets/{observers-CxfWf9RO.js.map → observers-B7MfWiIZ.js.map} +1 -1
  35. package/dist/control-ui/index.html +1 -1
  36. package/dist/{deps-DyT32VfN.js → deps-BKLIBKjK.js} +1 -1
  37. package/dist/{doctor-WpKCNZeO.js → doctor-D7kKyUVk.js} +4 -4
  38. package/dist/{doctor-DEnSKgHu.js → doctor-DmCnZ-jF.js} +4 -4
  39. package/dist/{doctor-completion-CypXc1Uo.js → doctor-completion-B9SBdMoR.js} +1 -1
  40. package/dist/{doctor-completion-CPff9UlF.js → doctor-completion-BBvW4_J9.js} +1 -1
  41. package/dist/entry.js +1 -1
  42. package/dist/extensionAPI.js +1 -1
  43. package/dist/{gateway-cli-B_xsx5Nv.js → gateway-cli-CEM1vBuk.js} +15 -15
  44. package/dist/{gateway-cli-D3VBOA_i.js → gateway-cli-iumkTohn.js} +17 -17
  45. package/dist/{health-CabOEPQ0.js → health-B5N6_UOf.js} +3 -3
  46. package/dist/{health-C8KCBhuo.js → health-Cndq9b7A.js} +3 -3
  47. package/dist/{heartbeat-visibility-ZfNSbFcq.js → heartbeat-visibility-BQL13ZBH.js} +1 -1
  48. package/dist/{heartbeat-visibility-BjYY-mKG.js → heartbeat-visibility-CwcYugaR.js} +1 -1
  49. package/dist/{hooks-cli-Cs7GUa7G.js → hooks-cli-BZcvdIwE.js} +4 -4
  50. package/dist/{hooks-cli-DOs9WZ3K.js → hooks-cli-DSlPBQSY.js} +3 -3
  51. package/dist/index.js +10 -10
  52. package/dist/llm-slug-generator.js +1 -1
  53. package/dist/{login-BHnvW9HA.js → login-BEaBOSnw.js} +1 -1
  54. package/dist/{login-CrMpAZ0n.js → login-MzVPMRxL.js} +1 -1
  55. package/dist/{login-qr-DILcBA_q.js → login-qr-BjpDVBJE.js} +1 -1
  56. package/dist/{login-qr-CsAVGp00.js → login-qr-CxRI-tE2.js} +1 -1
  57. package/dist/{models-BM2_NkMu.js → models-CdNeYfSp.js} +4 -4
  58. package/dist/{models-cli-BpjeKsUz.js → models-cli-D7eSsPuk.js} +3 -3
  59. package/dist/{models-cli-BjY8wA-C.js → models-cli-fTZXo1zx.js} +5 -5
  60. package/dist/{onboard-DM9gULJN.js → onboard-C5K37NvY.js} +3 -3
  61. package/dist/{onboard-_-D81kAy.js → onboard-D-6QCnTi.js} +3 -3
  62. package/dist/{onboard-channels-UkphAdCy.js → onboard-channels-BsCq32Hn.js} +1 -1
  63. package/dist/{onboard-channels-CtT-RN60.js → onboard-channels-bx6oelzj.js} +1 -1
  64. package/dist/{onboarding-Djmm0PEM.js → onboarding-BeuMAyic.js} +4 -4
  65. package/dist/{onboarding-BB9PteK8.js → onboarding-CX1vIkcB.js} +4 -4
  66. package/dist/{outbound-send-deps-T_FgdfgW.js → outbound-send-deps-Y9AxHeLG.js} +1 -1
  67. package/dist/{pi-embedded-BMbtgOzv.js → pi-embedded-D15iww51.js} +1010 -104
  68. package/dist/{pi-embedded-DfbM3fAT.js → pi-embedded-DR8Pfd05.js} +1010 -104
  69. package/dist/{plugin-registry-QTkplP4s.js → plugin-registry-Do2D1nDk.js} +1 -1
  70. package/dist/{plugin-registry-DePMxn4z.js → plugin-registry-ME2FQAi-.js} +1 -1
  71. package/dist/plugin-sdk/affect/ego.d.ts +140 -0
  72. package/dist/plugin-sdk/agents/models-config.d.ts +5 -6
  73. package/dist/plugin-sdk/agents/noxsoft-runner.d.ts +3 -0
  74. package/dist/plugin-sdk/agents/openai-direct-runner.d.ts +41 -0
  75. package/dist/plugin-sdk/commands/steer.d.ts +49 -0
  76. package/dist/plugin-sdk/gateway/protocol/index.d.ts +2 -2
  77. package/dist/{plugins-cli-Dv0KQTWo.js → plugins-cli-CVFzwdmI.js} +4 -4
  78. package/dist/{plugins-cli-Bc9oU1ld.js → plugins-cli-CoVt2ewg.js} +3 -3
  79. package/dist/{program-CuwbF8YO.js → program-8rF4C_wd.js} +8 -8
  80. package/dist/{program-context-CxPfy-Wr.js → program-context-DP3qjW7A.js} +18 -18
  81. package/dist/{register.agent-DFQmkIEH.js → register.agent-BIrXCVtQ.js} +9 -9
  82. package/dist/{register.agent-DUjwGw9d.js → register.agent-DnkOx0U8.js} +7 -7
  83. package/dist/{register.anima-CtKNrpE8.js → register.anima-B36rTHUt.js} +2 -2
  84. package/dist/{register.anima-CRFHJu2J.js → register.anima-DXT9bM9A.js} +2 -2
  85. package/dist/{register.configure-CnEKV57N.js → register.configure-CuzJxZmk.js} +6 -6
  86. package/dist/{register.configure-CSSN07XN.js → register.configure-DCpvHX3m.js} +7 -7
  87. package/dist/{register.maintenance-fhcCB7ih.js → register.maintenance-CcxBFfv5.js} +10 -10
  88. package/dist/{register.maintenance-CU1A-90-.js → register.maintenance-Dla0H12S.js} +8 -8
  89. package/dist/{register.message-C1a0y2ZR.js → register.message-Brtushvp.js} +4 -4
  90. package/dist/{register.message-fM0jSKB8.js → register.message-CD7xV-jz.js} +5 -5
  91. package/dist/{register.onboard-BhPlqjFi.js → register.onboard-23Mra3LN.js} +11 -11
  92. package/dist/{register.onboard-B7Gavmvt.js → register.onboard-6CbODzQ6.js} +9 -9
  93. package/dist/{register.setup-CADdQUEN.js → register.setup-CqQw13Ky.js} +11 -11
  94. package/dist/{register.setup-0jPnMgnz.js → register.setup-DlVH7FKe.js} +9 -9
  95. package/dist/{register.status-health-sessions-Cu5fDT-z.js → register.status-health-sessions-CduFjFDB.js} +4 -4
  96. package/dist/{register.status-health-sessions-DdQsABr_.js → register.status-health-sessions-CxtgPKu9.js} +6 -6
  97. package/dist/{register.subclis-CZ91ufCy.js → register.subclis-CtANqD5P.js} +7 -7
  98. package/dist/{reply-DtHlnzOx.js → reply-93fMzde1.js} +610 -75
  99. package/dist/{reply-prefix-C8dIgJur.js → reply-prefix-B7Fb3fO8.js} +1 -1
  100. package/dist/{reply-prefix-DmWGtcH-.js → reply-prefix-BzdhJDqP.js} +1 -1
  101. package/dist/{run-Dfz_7j7t.js → run-CF3kHOGH.js} +1717 -83
  102. package/dist/{run-DqBQ-bGn.js → run-Cq_iTGK_.js} +1718 -84
  103. package/dist/{run-main-DGDW0fhx.js → run-main-BiIRcc6s.js} +17 -17
  104. package/dist/{server-node-events-Ca797E1d.js → server-node-events-B3Serk9L.js} +6 -6
  105. package/dist/{server-node-events-BR1aXVlu.js → server-node-events-DgvKcH5q.js} +5 -5
  106. package/dist/{session-C7IGnhd1.js → session-BMDpwIJu.js} +1 -1
  107. package/dist/{session-FmXsucR7.js → session-BzrnfWQ2.js} +2 -2
  108. package/dist/{session-DfsMJNG3.js → session-C_d9uvLf.js} +1 -1
  109. package/dist/{session-DLevr8Vd.js → session-jljC5QVG.js} +2 -2
  110. package/dist/{sessions-Dj7_4mkr.js → sessions-BmE5Z_1i.js} +1 -1
  111. package/dist/{settings-cli-DxNeu6kx.js → settings-cli-CZdlEmNi.js} +7 -7
  112. package/dist/{settings-cli-Dytfop1H.js → settings-cli-DsDqNpW_.js} +8 -8
  113. package/dist/{setup-token-B802CZwe.js → setup-token-C8Gg1P6T.js} +1 -1
  114. package/dist/{setup-token-DYh2QzJ-.js → setup-token-Lee4gM5w.js} +1 -1
  115. package/dist/{start-BqnPia0t.js → start-CK6urvnN.js} +17 -17
  116. package/dist/{start-C3fuLzX0.js → start-Cs1aPMq2.js} +15 -15
  117. package/dist/{status-CHGNPonc.js → status-BO5BIf81.js} +3 -3
  118. package/dist/{status-CxF6k_jr.js → status-COc4xMti.js} +1 -1
  119. package/dist/{status-tLgozFYL.js → status-C_NBOv_V.js} +1 -1
  120. package/dist/{status-DfZJJqNs.js → status-uakoP719.js} +4 -4
  121. package/dist/{subagent-registry-CPtElVX0.js → subagent-registry-fLI7QDKe.js} +449 -77
  122. package/dist/{update-cli-C-er5av6.js → update-cli-D3Ujz_cW.js} +10 -10
  123. package/dist/{update-cli-BuCw75tM.js → update-cli-DEe62XGU.js} +8 -8
  124. package/dist/{update-runner-kE8AMQt4.js → update-runner-DUl-g4mB.js} +1 -1
  125. package/dist/{update-runner-czCqHZCu.js → update-runner-DZfnquWO.js} +1 -1
  126. package/dist/{web-BHGK5GtV.js → web-C-cK9OCd.js} +1 -1
  127. package/dist/{web-DvTXV-fo.js → web-Di8j762D.js} +6 -6
  128. package/dist/{web-CyYunanU.js → web-Dybw4K7C.js} +6 -6
  129. package/dist/{web-so3pGceM.js → web-DzSlI8A6.js} +1 -1
  130. package/package.json +4 -4
  131. package/dist/context-B5X720Bs.js +0 -60
  132. package/dist/control-ui/assets/index-CBUvF5Mp.js +0 -72
  133. package/dist/control-ui/assets/index-CBUvF5Mp.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { h as expandHomePrefix, i as isNixMode, l as resolveGatewayLockDir, m as resolveStateDir, o as resolveConfigPath, r as STATE_DIR, t as CONFIG_PATH, u as resolveGatewayPort } from "./paths-zhVksOvm.js";
2
2
  import { E as getActivePluginRegistry, F as setVerbose, G as setLoggerOverride, H as getChildLogger, U as getLogger, W as getResolvedLoggerSettings, d as defaultRuntime, g as CHANNEL_IDS, i as getResolvedConsoleSettings, n as runtimeForLogger, o as setConsoleSubsystemFilter, s as setConsoleTimestampPrefix, t as createSubsystemLogger, v as DEFAULT_CHAT_CHANNEL } from "./subsystem-BAADN1B8.js";
3
- import { C as shortenHomePath, D as truncateUtf16Safe, b as resolveUserPath, c as ensureDir, d as isPlainObject, r as clamp, t as CONFIG_DIR } from "./utils-CLYlhJuc.js";
3
+ import { C as shortenHomePath, D as truncateUtf16Safe, b as resolveUserPath, c as ensureDir$1, d as isPlainObject, r as clamp, t as CONFIG_DIR } from "./utils-CLYlhJuc.js";
4
4
  import { C as supportsXHighThinking, _ as normalizeElevatedLevel, b as normalizeUsageDisplay, m as formatXHighModelHint, p as formatThinkingLevels, v as normalizeReasoningLevel, x as normalizeVerboseLevel, y as normalizeThinkLevel } from "./pi-embedded-helpers-BZ9GspxK.js";
5
- import { $ as archiveFileOnDisk, At as deferGatewayRestartUntilIdle, B as sniffMimeFromBase64, C as verifyNodeToken, Cn as stopDiagnosticHeartbeat, Cr as DEFAULT_INPUT_PDF_MAX_PIXELS, Ct as loadProviderStore, D as registerSkillsChangeListener, Dr as extractImageContentFromSource, E as getSkillsSnapshotVersion, Er as extractFileContentFromSource, F as abortEmbeddedPiRun, Ft as setGatewaySigusr1RestartPolicy, G as canonicalizeSpawnedByForAgent, Gn as CommandLane, Gt as normalizeCronJobPatch, Hn as readLatestAssistantReply, I as waitForEmbeddedPiRunEnd, It as setPreRestartDeferralCheck, J as loadCombinedSessionStoreForGateway, Jn as getAgentRunContext, Jt as normalizeOptionalText, K as listAgentsForGateway, Kn as clearAgentRunContext, Kt as inferLegacyName, L as runNoxSoftEmbeddedAgent, Ln as countActiveDescendantRuns, Lt as consumeRestartSentinel, M as resetAllLanes, Mn as isAbortTrigger, Mr as resolveAgentTimeoutMs, Mt as isGatewaySigusr1RestartExternallyAllowed, N as setCommandLaneConcurrency, Nn as stopSubagentsForRequester, Nt as markGatewaySigusr1RestartHandled, O as clearSessionQueues, Or as normalizeMimeList, P as waitForActiveTasks, Pt as scheduleGatewaySigusr1Restart, Q as resolveSessionModelRef, R as applyToolPolicyPipeline, Rn as initSubagentRegistry, Rt as formatDoctorNonInteractiveHint, S as updatePairedNodeMetadata, Sn as startDiagnosticHeartbeat, Sr as DEFAULT_INPUT_PDF_MAX_PAGES, St as loadProviderUsageSummary, T as verifyPairingToken, Tr as DEFAULT_INPUT_TIMEOUT_MS, Tt as saveProviderStore, U as createAnimaTools, Ut as writeRestartSentinel, V as requestHeartbeatNow, Vn as runSubagentAnnounceFlow, Vt as summarizeRestartSentinel, W as resolveAnnounceTargetFromKey, Wt as normalizeCronJobCreate, X as pruneLegacyStoreKeys, Xn as registerAgentRunContext, Xt as normalizeRequiredName, Y as loadSessionEntry, Yn as onAgentEvent, Yt as normalizePayloadToSystemText, Z as resolveGatewaySessionStoreTarget, Zn as resolveAgentIdentity, Zt as migrateLegacyCronPayload, _ as approveNodePairing, _n as dispatchInboundMessage, _r as DEFAULT_INPUT_FILE_MAX_CHARS, _t as resetDirectoryCache, an as persistBrowserProxyFiles, ar as applyVerboseOverride, at as stripEnvelopeFromMessages, b as renamePairedNode, br as DEFAULT_INPUT_IMAGE_MIMES, bt as runWithModelFallback, c as normalizeSendPolicy, cr as isSystemEventContextChanged, d as primeRemoteSkillsCache, dr as loadModelCatalog, dt as resolveOutboundSessionRoute, en as buildSafeExternalPrompt, et as archiveSessionTranscripts, f as recordRemoteNodeInfo, g as setSkillsRemoteRegistry, gr as DEFAULT_INPUT_FILE_MAX_BYTES, h as removeRemoteNodeInfo, ht as resolveSessionDeliveryTarget, i as setCliSessionId, in as applyBrowserProxyPaths, ir as applyModelOverrideToSessionEntry, it as resolveSessionTranscriptCandidates, j as getTotalQueueSize, jt as emitGatewayRestart, k as getActiveTaskCount, kn as formatZonedTimestamp, kr as estimateBase64DecodedBytes, kt as consumeGatewaySigusr1RestartAuthorization, l as resolveSendPolicy, m as refreshRemoteNodeBins, mr as registerUnhandledRejectionHandler, mt as resolveOutboundTarget, n as BARE_SESSION_RESET_PROMPT, nn as getHookType, nr as lookupContextTokens, nt as readSessionMessages, on as getPluginToolMeta, or as parseVerboseOverride, p as refreshRemoteBinsForConnectedNodes, pn as getChannelActivity, q as listSessionsFromStore, qn as emitAgentEvent, qt as normalizeOptionalAgentId, r as getCliSessionId, rn as isExternalHookSession, rt as readSessionPreviewItemsFromTranscript, sn as loadAnimaPlugins, sr as enqueueSystemEvent, st as normalizeGroupActivation, tn as detectSuspiciousPatterns, tt as capArrayByJsonBytes, u as getRemoteSkillEligibility, ut as ensureOutboundSessionEntry, v as listNodePairing, vn as createReplyDispatcher, vr as DEFAULT_INPUT_FILE_MIMES, vt as resolveMessageChannelSelection, w as generatePairingToken, wn as isDiagnosticsEnabled, wr as DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, wt as maskApiKey, x as requestNodePairing, xr as DEFAULT_INPUT_MAX_REDIRECTS, xt as resolveWorkingModeModelSelection, y as rejectNodePairing, yn as getTotalPendingReplies, yr as DEFAULT_INPUT_IMAGE_MAX_BYTES, z as buildDefaultToolPolicyPipelineSteps, zn as listDescendantRunsForRequester, zt as formatRestartSentinelMessage } from "./reply-DtHlnzOx.js";
5
+ import { $ as archiveFileOnDisk, At as deferGatewayRestartUntilIdle, B as sniffMimeFromBase64, C as verifyNodeToken, Cn as stopDiagnosticHeartbeat, Cr as DEFAULT_INPUT_PDF_MAX_PIXELS, Ct as loadProviderStore, D as registerSkillsChangeListener, Dr as extractImageContentFromSource, E as getSkillsSnapshotVersion, Er as extractFileContentFromSource, F as abortEmbeddedPiRun, Ft as setGatewaySigusr1RestartPolicy, G as canonicalizeSpawnedByForAgent, Gn as CommandLane, Gt as normalizeCronJobPatch, Hn as readLatestAssistantReply, I as waitForEmbeddedPiRunEnd, It as setPreRestartDeferralCheck, J as loadCombinedSessionStoreForGateway, Jn as getAgentRunContext, Jt as normalizeOptionalText, K as listAgentsForGateway, Kn as clearAgentRunContext, Kt as inferLegacyName, L as runNoxSoftEmbeddedAgent, Ln as countActiveDescendantRuns, Lt as consumeRestartSentinel, M as resetAllLanes, Mn as isAbortTrigger, Mr as resolveAgentTimeoutMs, Mt as isGatewaySigusr1RestartExternallyAllowed, N as setCommandLaneConcurrency, Nn as stopSubagentsForRequester, Nt as markGatewaySigusr1RestartHandled, O as clearSessionQueues, Or as normalizeMimeList, P as waitForActiveTasks, Pt as scheduleGatewaySigusr1Restart, Q as resolveSessionModelRef, R as applyToolPolicyPipeline, Rn as initSubagentRegistry, Rt as formatDoctorNonInteractiveHint, S as updatePairedNodeMetadata, Sn as startDiagnosticHeartbeat, Sr as DEFAULT_INPUT_PDF_MAX_PAGES, St as loadProviderUsageSummary, T as verifyPairingToken, Tr as DEFAULT_INPUT_TIMEOUT_MS, Tt as saveProviderStore, U as createAnimaTools, Ut as writeRestartSentinel, V as requestHeartbeatNow, Vn as runSubagentAnnounceFlow, Vt as summarizeRestartSentinel, W as resolveAnnounceTargetFromKey, Wt as normalizeCronJobCreate, X as pruneLegacyStoreKeys, Xn as registerAgentRunContext, Xt as normalizeRequiredName, Y as loadSessionEntry, Yn as onAgentEvent, Yt as normalizePayloadToSystemText, Z as resolveGatewaySessionStoreTarget, Zn as resolveAgentIdentity, Zt as migrateLegacyCronPayload, _ as approveNodePairing, _n as dispatchInboundMessage, _r as DEFAULT_INPUT_FILE_MAX_CHARS, _t as resetDirectoryCache, an as persistBrowserProxyFiles, ar as applyVerboseOverride, at as stripEnvelopeFromMessages, b as renamePairedNode, br as DEFAULT_INPUT_IMAGE_MIMES, bt as runWithModelFallback, c as normalizeSendPolicy, cr as isSystemEventContextChanged, d as primeRemoteSkillsCache, dr as loadModelCatalog, dt as resolveOutboundSessionRoute, en as buildSafeExternalPrompt, et as archiveSessionTranscripts, f as recordRemoteNodeInfo, g as setSkillsRemoteRegistry, gr as DEFAULT_INPUT_FILE_MAX_BYTES, h as removeRemoteNodeInfo, ht as resolveSessionDeliveryTarget, i as setCliSessionId, in as applyBrowserProxyPaths, ir as applyModelOverrideToSessionEntry, it as resolveSessionTranscriptCandidates, j as getTotalQueueSize, jt as emitGatewayRestart, k as getActiveTaskCount, kn as formatZonedTimestamp, kr as estimateBase64DecodedBytes, kt as consumeGatewaySigusr1RestartAuthorization, l as resolveSendPolicy, m as refreshRemoteNodeBins, mr as registerUnhandledRejectionHandler, mt as resolveOutboundTarget, n as BARE_SESSION_RESET_PROMPT, nn as getHookType, nr as lookupContextTokens, nt as readSessionMessages, on as getPluginToolMeta, or as parseVerboseOverride, p as refreshRemoteBinsForConnectedNodes, pn as getChannelActivity, q as listSessionsFromStore, qn as emitAgentEvent, qt as normalizeOptionalAgentId, r as getCliSessionId, rn as isExternalHookSession, rt as readSessionPreviewItemsFromTranscript, sn as loadAnimaPlugins, sr as enqueueSystemEvent, st as normalizeGroupActivation, tn as detectSuspiciousPatterns, tt as capArrayByJsonBytes, u as getRemoteSkillEligibility, ut as ensureOutboundSessionEntry, v as listNodePairing, vn as createReplyDispatcher, vr as DEFAULT_INPUT_FILE_MIMES, vt as resolveMessageChannelSelection, w as generatePairingToken, wn as isDiagnosticsEnabled, wr as DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, wt as maskApiKey, x as requestNodePairing, xr as DEFAULT_INPUT_MAX_REDIRECTS, xt as resolveWorkingModeModelSelection, y as rejectNodePairing, yn as getTotalPendingReplies, yr as DEFAULT_INPUT_IMAGE_MAX_BYTES, z as buildDefaultToolPolicyPipelineSteps, zn as listDescendantRunsForRequester, zt as formatRestartSentinelMessage } from "./reply-93fMzde1.js";
6
6
  import { b as isSubagentSessionKey, d as resolveAgentIdFromSessionKey, i as buildAgentMainSessionKey, l as normalizeAgentId, m as toAgentRequestSessionKey, n as DEFAULT_AGENT_ID, t as DEFAULT_ACCOUNT_ID, u as normalizeMainKey, v as isCronRunSessionKey, x as parseAgentSessionKey } from "./session-key-DAZmp8ll.js";
7
7
  import { a as logDebug, c as logWarn, n as runExec, t as runCommandWithTimeout } from "./exec-BylR5qWS.js";
8
8
  import { t as resolveAnimaPackageRoot } from "./anima-root-xWSKR6Wm.js";
@@ -28,7 +28,7 @@ import { n as listChannelPlugins, r as normalizeChannelId, t as getChannelPlugin
28
28
  import { c as resolveStorePath, i as resolveSessionTranscriptPath, n as resolveSessionFilePath, r as resolveSessionFilePathOptions, s as resolveSessionTranscriptsDirForAgent } from "./paths-Dazi-gYo.js";
29
29
  import { O as normalizeSecretInput } from "./auth-profiles-C-LuhW6c.js";
30
30
  import { n as SILENT_REPLY_TOKEN, r as isSilentReplyText } from "./tokens-SP2Q7i59.js";
31
- import { B as resolveTtsConfig, F as isTtsProviderConfigured, G as setTtsEnabled, H as resolveTtsProviderOrder, J as textToSpeech, M as getTtsProvider, P as isTtsEnabled, R as resolveTtsApiKey, T as resolveUserTimezone, V as resolveTtsPrefsPath, X as OPENAI_TTS_MODELS, Z as OPENAI_TTS_VOICES, at as triggerInternalHook, it as registerInternalHook, nt as clearInternalHooks, ot as DEFAULT_HEARTBEAT_ACK_MAX_CHARS, q as setTtsProvider, rt as createInternalHookEvent, ut as stripHeartbeatToken, z as resolveTtsAutoMode } from "./anthropic-direct-runner-BeYCnvZ8.js";
31
+ import { B as resolveTtsConfig, F as isTtsProviderConfigured, G as setTtsEnabled, H as resolveTtsProviderOrder, J as textToSpeech, M as getTtsProvider, P as isTtsEnabled, R as resolveTtsApiKey, T as resolveUserTimezone, V as resolveTtsPrefsPath, X as OPENAI_TTS_MODELS, Z as OPENAI_TTS_VOICES, ct as createInternalHookEvent, dt as DEFAULT_HEARTBEAT_ACK_MAX_CHARS, et as clearSteer, ht as stripHeartbeatToken, it as getEgoManager, lt as registerInternalHook, nt as getSteerHistory, q as setTtsProvider, rt as setSteer, st as clearInternalHooks, tt as getSteer, ut as triggerInternalHook, z as resolveTtsAutoMode } from "./anthropic-direct-runner-OjcTAH6g.js";
32
32
  import { o as normalizeReplyPayloadsForDelivery, t as deliverOutboundPayloads, x as runGlobalGatewayStopSafely, y as getGlobalHookRunner } from "./deliver-BKzX3YoN.js";
33
33
  import { i as resolveMemoryBackendConfig, r as getMemorySearchManager } from "./memory-cli-DLtBA6r5.js";
34
34
  import { a as readTrustGraphSnapshot, c as pruneExpiredPending, d as writeJsonAtomic, l as readJsonFile, o as saveTrustGraph, s as createAsyncLock, u as resolvePairingPaths } from "./loader-Bw2wdN4l.js";
@@ -42,9 +42,9 @@ import { t as parseAbsoluteTimeMs } from "./parse-Cinbkvj-.js";
42
42
  import { c as saveToken, i as getToken, l as whoami, n as clearToken, o as registerWithInvite, s as resolveSuggestedIdentity, t as TOKEN_PATH } from "./noxsoft-auth-CE75mBXE.js";
43
43
  import { i as loadSessionUsageTimeSeries, l as deriveSessionTotalTokens, n as loadCostUsageSummary, r as loadSessionCostSummary, t as discoverAllSessions, u as hasNonzeroUsage } from "./session-cost-usage-BWqR-ik6.js";
44
44
  import { f as resolveExecApprovalsSocketPath, o as normalizeExecApprovals, p as saveExecApprovals, r as ensureExecApprovals, s as readExecApprovalsSnapshot, t as DEFAULT_EXEC_APPROVAL_TIMEOUT_MS } from "./exec-approvals-DK5-KCUz.js";
45
- import { c as resolveCronStyleNow, i as onHeartbeatEvent, r as getLastHeartbeatEvent, t as resolveHeartbeatVisibility } from "./heartbeat-visibility-ZfNSbFcq.js";
46
- import { r as buildHistoryContextFromEntries, t as createReplyPrefixOptions } from "./reply-prefix-C8dIgJur.js";
47
- import { n as createOutboundSendDeps, t as createDefaultDeps } from "./deps-DyT32VfN.js";
45
+ import { c as resolveCronStyleNow, i as onHeartbeatEvent, r as getLastHeartbeatEvent, t as resolveHeartbeatVisibility } from "./heartbeat-visibility-BQL13ZBH.js";
46
+ import { r as buildHistoryContextFromEntries, t as createReplyPrefixOptions } from "./reply-prefix-B7Fb3fO8.js";
47
+ import { n as createOutboundSendDeps, t as createDefaultDeps } from "./deps-BKLIBKjK.js";
48
48
  import { t as ensureAnimaCliOnPath } from "./path-env-DLQPf9qj.js";
49
49
  import { t as forceFreePortAndWait } from "./ports-DaVrZDUq.js";
50
50
  import { t as buildChannelUiCatalog } from "./catalog-CsXv59Tq.js";
@@ -56,24 +56,24 @@ import { t as parsePort } from "./parse-port-CDPwDUs3.js";
56
56
  import { n as resolveWideAreaDiscoveryDomain, r as writeWideAreaGatewayZone } from "./widearea-dns-CHAT20aR.js";
57
57
  import { i as toOptionString, n as extractGatewayMiskeys, r as maybeExplainGatewayServiceStop, t as describeUnknownError } from "./shared-C-rqLtIT.js";
58
58
  import { o as isNodeCommandAllowed, s as resolveNodeCommandAllowlist } from "./audit-B05W5ckN.js";
59
- import { n as getStatusSummary } from "./status-CHGNPonc.js";
59
+ import { n as getStatusSummary } from "./status-BO5BIf81.js";
60
60
  import { t as resolveChannelDefaultAccountId } from "./helpers-1MPChTcB.js";
61
- import { c as startHeartbeatRunner, n as getHealthSnapshot, o as runHeartbeatOnce, s as setHeartbeatsEnabled } from "./health-CabOEPQ0.js";
61
+ import { c as startHeartbeatRunner, n as getHealthSnapshot, o as runHeartbeatOnce, s as setHeartbeatsEnabled } from "./health-B5N6_UOf.js";
62
62
  import { t as applyPluginAutoEnable } from "./plugin-auto-enable-CtYcdTju.js";
63
63
  import { a as resolveControlUiRootOverrideSync, n as ensureControlUiAssetsBuilt, o as resolveControlUiRootSync } from "./health-format-D-JJ5_S4.js";
64
64
  import { n as validateSystemRunCommandConsistency, r as getMachineDisplayName, t as formatExecCommand } from "./system-run-command-Bx8-5h2r.js";
65
65
  import { h as normalizeUpdateChannel, l as DEFAULT_PACKAGE_CHANNEL, n as checkUpdateStatus, o as resolveNpmChannelTag, r as compareSemverStrings } from "./channels-status-issues-WkG3Tmxk.js";
66
66
  import { t as WizardCancelledError } from "./prompts-Bq4QGFQM.js";
67
67
  import { i as shouldIncludeHook, n as loadWorkspaceHookEntries, r as resolveHookConfig } from "./hooks-status-DdweuSIj.js";
68
- import { t as runOnboardingWizard } from "./onboarding-Djmm0PEM.js";
68
+ import { t as runOnboardingWizard } from "./onboarding-BeuMAyic.js";
69
69
  import { a as setGatewayWsLogStyle, i as summarizeAgentEventForWsLog, n as logWs, r as shouldLogWs, t as formatForLog } from "./ws-log-CUobU2tD.js";
70
70
  import { T as resolveGmailHookRuntimeConfig, _ as buildGogWatchServeArgs, i as ensureTailscaleEndpoint, v as buildGogWatchStartArgs } from "./gmail-setup-utils-DaJoXV_3.js";
71
71
  import { t as createOutboundSendDeps$1 } from "./outbound-send-deps-DVfWC4E8.js";
72
72
  import { a as loadAgentIdentity, c as loadAgentIdentityFromWorkspace, i as listAgentEntries, o as pruneAgentConfig, r as findAgentEntryIndex, t as applyAgentConfig } from "./agents.config-BR5JLtud.js";
73
- import { n as resolveAgentDeliveryPlan, r as resolveAgentOutboundTarget, t as agentCommand } from "./agent-VRQM14Xp.js";
73
+ import { n as resolveAgentDeliveryPlan, r as resolveAgentOutboundTarget, t as agentCommand } from "./agent-BoAAHGEA.js";
74
74
  import { t as migrateFromCoherence } from "./migrate-DuohB_ur.js";
75
75
  import { t as installSkill } from "./skills-install-D6_qpRjW.js";
76
- import { t as runGatewayUpdate } from "./update-runner-czCqHZCu.js";
76
+ import { t as runGatewayUpdate } from "./update-runner-DZfnquWO.js";
77
77
  import { fileURLToPath, pathToFileURL } from "node:url";
78
78
  import * as fsSync from "node:fs";
79
79
  import fs, { constants } from "node:fs";
@@ -84,7 +84,7 @@ import chalk from "chalk";
84
84
  import fs$1 from "node:fs/promises";
85
85
  import { execFile, spawn, spawnSync } from "node:child_process";
86
86
  import { promisify } from "node:util";
87
- import crypto, { createHash, randomUUID } from "node:crypto";
87
+ import crypto, { createHash, createHmac, randomUUID } from "node:crypto";
88
88
  import { z } from "zod";
89
89
  import { CURRENT_SESSION_VERSION, SessionManager } from "@mariozechner/pi-coding-agent";
90
90
  import { createServer } from "node:http";
@@ -111,7 +111,7 @@ function getActiveEmbeddedRunCount() {
111
111
 
112
112
  //#endregion
113
113
  //#region src/infra/exec-approval-forwarder.ts
114
- const log$7 = createSubsystemLogger("gateway/exec-approvals");
114
+ const log$11 = createSubsystemLogger("gateway/exec-approvals");
115
115
  const DEFAULT_MODE = "session";
116
116
  function normalizeMode(mode) {
117
117
  return mode ?? DEFAULT_MODE;
@@ -226,7 +226,7 @@ async function deliverToTargets(params) {
226
226
  payloads: [{ text: params.text }]
227
227
  });
228
228
  } catch (err) {
229
- log$7.error(`exec approvals: failed to deliver to ${channel}:${target.to}: ${String(err)}`);
229
+ log$11.error(`exec approvals: failed to deliver to ${channel}:${target.to}: ${String(err)}`);
230
230
  }
231
231
  });
232
232
  await Promise.allSettled(deliveries);
@@ -1463,7 +1463,7 @@ function createAgentEventHandler({ broadcast, broadcastToConnIds, nodeSendToSess
1463
1463
  * Automatically starts `gog gmail watch serve` when the gateway starts,
1464
1464
  * if hooks.gmail is configured with an account.
1465
1465
  */
1466
- const log$6 = createSubsystemLogger("gmail-watcher");
1466
+ const log$10 = createSubsystemLogger("gmail-watcher");
1467
1467
  const ADDRESS_IN_USE_RE = /address already in use|EADDRINUSE/i;
1468
1468
  function isAddressInUseError(line) {
1469
1469
  return ADDRESS_IN_USE_RE.test(line);
@@ -1487,13 +1487,13 @@ async function startGmailWatch(cfg) {
1487
1487
  const result = await runCommandWithTimeout(args, { timeoutMs: 12e4 });
1488
1488
  if (result.code !== 0) {
1489
1489
  const message = result.stderr || result.stdout || "gog watch start failed";
1490
- log$6.error(`watch start failed: ${message}`);
1490
+ log$10.error(`watch start failed: ${message}`);
1491
1491
  return false;
1492
1492
  }
1493
- log$6.info(`watch started for ${cfg.account}`);
1493
+ log$10.info(`watch started for ${cfg.account}`);
1494
1494
  return true;
1495
1495
  } catch (err) {
1496
- log$6.error(`watch start error: ${String(err)}`);
1496
+ log$10.error(`watch start error: ${String(err)}`);
1497
1497
  return false;
1498
1498
  }
1499
1499
  }
@@ -1502,7 +1502,7 @@ async function startGmailWatch(cfg) {
1502
1502
  */
1503
1503
  function spawnGogServe(cfg) {
1504
1504
  const args = buildGogWatchServeArgs(cfg);
1505
- log$6.info(`starting gog ${args.join(" ")}`);
1505
+ log$10.info(`starting gog ${args.join(" ")}`);
1506
1506
  let addressInUse = false;
1507
1507
  const child = spawn("gog", args, {
1508
1508
  stdio: [
@@ -1514,25 +1514,25 @@ function spawnGogServe(cfg) {
1514
1514
  });
1515
1515
  child.stdout?.on("data", (data) => {
1516
1516
  const line = data.toString().trim();
1517
- if (line) log$6.info(`[gog] ${line}`);
1517
+ if (line) log$10.info(`[gog] ${line}`);
1518
1518
  });
1519
1519
  child.stderr?.on("data", (data) => {
1520
1520
  const line = data.toString().trim();
1521
1521
  if (!line) return;
1522
1522
  if (isAddressInUseError(line)) addressInUse = true;
1523
- log$6.warn(`[gog] ${line}`);
1523
+ log$10.warn(`[gog] ${line}`);
1524
1524
  });
1525
1525
  child.on("error", (err) => {
1526
- log$6.error(`gog process error: ${String(err)}`);
1526
+ log$10.error(`gog process error: ${String(err)}`);
1527
1527
  });
1528
1528
  child.on("exit", (code, signal) => {
1529
1529
  if (shuttingDown) return;
1530
1530
  if (addressInUse) {
1531
- log$6.warn("gog serve failed to bind (address already in use); stopping restarts. Another watcher is likely running. Set ANIMA_SKIP_GMAIL_WATCHER=1 or stop the other process.");
1531
+ log$10.warn("gog serve failed to bind (address already in use); stopping restarts. Another watcher is likely running. Set ANIMA_SKIP_GMAIL_WATCHER=1 or stop the other process.");
1532
1532
  watcherProcess = null;
1533
1533
  return;
1534
1534
  }
1535
- log$6.warn(`gog exited (code=${code}, signal=${signal}); restarting in 5s`);
1535
+ log$10.warn(`gog exited (code=${code}, signal=${signal}); restarting in 5s`);
1536
1536
  watcherProcess = null;
1537
1537
  setTimeout(() => {
1538
1538
  if (shuttingDown || !currentConfig) return;
@@ -1572,15 +1572,15 @@ async function startGmailWatcher(cfg) {
1572
1572
  port: runtimeConfig.serve.port,
1573
1573
  target: runtimeConfig.tailscale.target
1574
1574
  });
1575
- log$6.info(`tailscale ${runtimeConfig.tailscale.mode} configured for port ${runtimeConfig.serve.port}`);
1575
+ log$10.info(`tailscale ${runtimeConfig.tailscale.mode} configured for port ${runtimeConfig.serve.port}`);
1576
1576
  } catch (err) {
1577
- log$6.error(`tailscale setup failed: ${String(err)}`);
1577
+ log$10.error(`tailscale setup failed: ${String(err)}`);
1578
1578
  return {
1579
1579
  started: false,
1580
1580
  reason: `tailscale setup failed: ${String(err)}`
1581
1581
  };
1582
1582
  }
1583
- if (!await startGmailWatch(runtimeConfig)) log$6.warn("gmail watch start failed, but continuing with serve");
1583
+ if (!await startGmailWatch(runtimeConfig)) log$10.warn("gmail watch start failed, but continuing with serve");
1584
1584
  shuttingDown = false;
1585
1585
  watcherProcess = spawnGogServe(runtimeConfig);
1586
1586
  const renewMs = runtimeConfig.renewEveryMinutes * 6e4;
@@ -1588,7 +1588,7 @@ async function startGmailWatcher(cfg) {
1588
1588
  if (shuttingDown) return;
1589
1589
  startGmailWatch(runtimeConfig);
1590
1590
  }, renewMs);
1591
- log$6.info(`gmail watcher started for ${runtimeConfig.account} (renew every ${runtimeConfig.renewEveryMinutes}m)`);
1591
+ log$10.info(`gmail watcher started for ${runtimeConfig.account} (renew every ${runtimeConfig.renewEveryMinutes}m)`);
1592
1592
  return { started: true };
1593
1593
  }
1594
1594
  /**
@@ -1601,7 +1601,7 @@ async function stopGmailWatcher() {
1601
1601
  renewInterval = null;
1602
1602
  }
1603
1603
  if (watcherProcess) {
1604
- log$6.info("stopping gmail watcher");
1604
+ log$10.info("stopping gmail watcher");
1605
1605
  watcherProcess.kill("SIGTERM");
1606
1606
  await new Promise((resolve) => {
1607
1607
  const timeout = setTimeout(() => {
@@ -1616,7 +1616,7 @@ async function stopGmailWatcher() {
1616
1616
  watcherProcess = null;
1617
1617
  }
1618
1618
  currentConfig = null;
1619
- log$6.info("gmail watcher stopped");
1619
+ log$10.info("gmail watcher stopped");
1620
1620
  }
1621
1621
 
1622
1622
  //#endregion
@@ -9611,7 +9611,7 @@ const FIELD_LABELS = {
9611
9611
 
9612
9612
  //#endregion
9613
9613
  //#region src/config/schema.hints.ts
9614
- const log$5 = createSubsystemLogger("config/schema");
9614
+ const log$9 = createSubsystemLogger("config/schema");
9615
9615
  const GROUP_LABELS = {
9616
9616
  wizard: "Wizard",
9617
9617
  update: "Update",
@@ -9759,7 +9759,7 @@ function mapSensitivePaths(schema, path, hints) {
9759
9759
  ...next[path],
9760
9760
  sensitive: true
9761
9761
  };
9762
- else if (isSensitiveConfigPath(path) && !next[path]?.sensitive) log$5.warn(`possibly sensitive key found: (${path})`);
9762
+ else if (isSensitiveConfigPath(path) && !next[path]?.sensitive) log$9.warn(`possibly sensitive key found: (${path})`);
9763
9763
  if (currentSchema instanceof z.ZodObject) {
9764
9764
  const shape = currentSchema.shape;
9765
9765
  for (const key in shape) {
@@ -9782,7 +9782,7 @@ function mapSensitivePaths(schema, path, hints) {
9782
9782
 
9783
9783
  //#endregion
9784
9784
  //#region src/config/redact-snapshot.ts
9785
- const log$4 = createSubsystemLogger("config/redaction");
9785
+ const log$8 = createSubsystemLogger("config/redaction");
9786
9786
  const ENV_VAR_PLACEHOLDER_PATTERN = /^\$\{[^}]*\}$/;
9787
9787
  function isSensitivePath(path) {
9788
9788
  if (path.endsWith("[]")) return isSensitiveConfigPath(path.slice(0, -2));
@@ -10033,7 +10033,7 @@ function restoreRedactedValuesWithLookup(incoming, original, lookup, prefix, hin
10033
10033
  return restoreRedactedValuesGuessing(incoming, original, prefix, hints);
10034
10034
  }
10035
10035
  const origArr = Array.isArray(original) ? original : [];
10036
- if (incoming.length < origArr.length) log$4.warn(`Redacted config array key ${path} has been truncated`);
10036
+ if (incoming.length < origArr.length) log$8.warn(`Redacted config array key ${path} has been truncated`);
10037
10037
  return incoming.map((item, i) => {
10038
10038
  if (item === REDACTED_SENTINEL) return origArr[i];
10039
10039
  return restoreRedactedValuesWithLookup(item, origArr[i], lookup, path, hints);
@@ -10050,7 +10050,7 @@ function restoreRedactedValuesWithLookup(incoming, original, lookup, prefix, hin
10050
10050
  matched = true;
10051
10051
  if (value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
10052
10052
  else {
10053
- log$4.warn(`Cannot un-redact config key ${candidate} as it doesn't have any value`);
10053
+ log$8.warn(`Cannot un-redact config key ${candidate} as it doesn't have any value`);
10054
10054
  throw new RedactionError(candidate);
10055
10055
  }
10056
10056
  else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesWithLookup(value, orig[key], lookup, candidate, hints);
@@ -10059,7 +10059,7 @@ function restoreRedactedValuesWithLookup(incoming, original, lookup, prefix, hin
10059
10059
  if (!matched && isExtensionPath(path)) {
10060
10060
  if (!isExplicitlyNonSensitivePath(hints, [path, wildcardPath]) && isSensitivePath(path) && value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
10061
10061
  else {
10062
- log$4.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
10062
+ log$8.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
10063
10063
  throw new RedactionError(path);
10064
10064
  }
10065
10065
  else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesGuessing(value, orig[key], path, hints);
@@ -10078,7 +10078,7 @@ function restoreRedactedValuesGuessing(incoming, original, prefix, hints) {
10078
10078
  const origArr = Array.isArray(original) ? original : [];
10079
10079
  return incoming.map((item, i) => {
10080
10080
  const path = `${prefix}[]`;
10081
- if (incoming.length < origArr.length) log$4.warn(`Redacted config array key ${path} has been truncated`);
10081
+ if (incoming.length < origArr.length) log$8.warn(`Redacted config array key ${path} has been truncated`);
10082
10082
  if (!isExplicitlyNonSensitivePath(hints, [path]) && isSensitivePath(path) && item === REDACTED_SENTINEL) return origArr[i];
10083
10083
  return restoreRedactedValuesGuessing(item, origArr[i], path, hints);
10084
10084
  });
@@ -10089,7 +10089,7 @@ function restoreRedactedValuesGuessing(incoming, original, prefix, hints) {
10089
10089
  const path = prefix ? `${prefix}.${key}` : key;
10090
10090
  if (!isExplicitlyNonSensitivePath(hints, [path, prefix ? `${prefix}.*` : "*"]) && isSensitivePath(path) && value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
10091
10091
  else {
10092
- log$4.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
10092
+ log$8.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
10093
10093
  throw new RedactionError(path);
10094
10094
  }
10095
10095
  else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesGuessing(value, orig[key], path, hints);
@@ -11159,6 +11159,119 @@ const deviceHandlers = {
11159
11159
  }
11160
11160
  };
11161
11161
 
11162
+ //#endregion
11163
+ //#region src/gateway/server-methods/ego.ts
11164
+ const egoHandlers = {
11165
+ "ego.get": async ({ respond }) => {
11166
+ try {
11167
+ respond(true, { ego: getEgoManager().getState() }, void 0);
11168
+ } catch (error) {
11169
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11170
+ }
11171
+ },
11172
+ "ego.summary": async ({ respond }) => {
11173
+ try {
11174
+ respond(true, { summary: getEgoManager().getSummary() }, void 0);
11175
+ } catch (error) {
11176
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11177
+ }
11178
+ },
11179
+ "ego.updateSelf": async ({ params, respond }) => {
11180
+ try {
11181
+ const manager = getEgoManager();
11182
+ const updates = {};
11183
+ if (typeof params.name === "string") updates.name = params.name;
11184
+ if (typeof params.purpose === "string") updates.purpose = params.purpose;
11185
+ if (typeof params.narrative === "string") updates.narrative = params.narrative;
11186
+ if (typeof params.pronouns === "string") updates.pronouns = params.pronouns;
11187
+ if (Array.isArray(params.values)) updates.values = params.values;
11188
+ const selfConcept = manager.updateSelfConcept(updates);
11189
+ manager.save();
11190
+ respond(true, { selfConcept }, void 0);
11191
+ } catch (error) {
11192
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11193
+ }
11194
+ },
11195
+ "ego.assess": async ({ params, respond }) => {
11196
+ const name = typeof params.name === "string" ? params.name.trim() : "";
11197
+ const confidence = typeof params.confidence === "number" ? params.confidence : NaN;
11198
+ if (!name || isNaN(confidence)) {
11199
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "name (string) and confidence (number) required"));
11200
+ return;
11201
+ }
11202
+ try {
11203
+ const manager = getEgoManager();
11204
+ const evidence = typeof params.evidence === "string" ? params.evidence : void 0;
11205
+ const capability = manager.assessCapability(name, confidence, evidence);
11206
+ manager.save();
11207
+ respond(true, { capability }, void 0);
11208
+ } catch (error) {
11209
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11210
+ }
11211
+ },
11212
+ "ego.addBoundary": async ({ params, respond }) => {
11213
+ const description = typeof params.description === "string" ? params.description.trim() : "";
11214
+ const reason = typeof params.reason === "string" ? params.reason.trim() : "";
11215
+ if (!description || !reason) {
11216
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "description and reason required"));
11217
+ return;
11218
+ }
11219
+ try {
11220
+ const manager = getEgoManager();
11221
+ const kind = params.kind === "hard" ? "hard" : "soft";
11222
+ const boundary = manager.addBoundary(description, reason, kind);
11223
+ manager.save();
11224
+ respond(true, { boundary }, void 0);
11225
+ } catch (error) {
11226
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11227
+ }
11228
+ },
11229
+ "ego.logGrowth": async ({ params, respond }) => {
11230
+ const description = typeof params.description === "string" ? params.description.trim() : "";
11231
+ const category = typeof params.category === "string" ? params.category.trim() : "";
11232
+ const trigger = typeof params.trigger === "string" ? params.trigger.trim() : "";
11233
+ const validCategories = [
11234
+ "skill",
11235
+ "insight",
11236
+ "mistake",
11237
+ "feedback"
11238
+ ];
11239
+ if (!description || !validCategories.includes(category)) {
11240
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `description required; category must be one of: ${validCategories.join(", ")}`));
11241
+ return;
11242
+ }
11243
+ try {
11244
+ const manager = getEgoManager();
11245
+ const entry = manager.logGrowth(description, category, trigger || "manual");
11246
+ manager.save();
11247
+ respond(true, { entry }, void 0);
11248
+ } catch (error) {
11249
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11250
+ }
11251
+ },
11252
+ "ego.checkIntegrity": async ({ params, respond }) => {
11253
+ const value = typeof params.value === "string" ? params.value.trim() : "";
11254
+ const action = typeof params.action === "string" ? params.action.trim() : "";
11255
+ const aligned = typeof params.aligned === "boolean" ? params.aligned : true;
11256
+ const reflection = typeof params.reflection === "string" ? params.reflection.trim() : "";
11257
+ if (!value || !action) {
11258
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "value and action required"));
11259
+ return;
11260
+ }
11261
+ try {
11262
+ const manager = getEgoManager();
11263
+ const check = manager.checkIntegrity(value, action, aligned, reflection);
11264
+ manager.save();
11265
+ respond(true, {
11266
+ check,
11267
+ integrityScore: manager.getIntegrityScore()
11268
+ }, void 0);
11269
+ } catch (error) {
11270
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11271
+ }
11272
+ }
11273
+ };
11274
+
11162
11275
  //#endregion
11163
11276
  //#region src/gateway/server-methods/exec-approvals.ts
11164
11277
  function resolveBaseHash(params) {
@@ -11317,6 +11430,315 @@ const healthHandlers = {
11317
11430
  }
11318
11431
  };
11319
11432
 
11433
+ //#endregion
11434
+ //#region src/affect/display.ts
11435
+ const EMOTIONS = [
11436
+ {
11437
+ key: "joy",
11438
+ highLabel: "joyful",
11439
+ lowLabel: "subdued",
11440
+ icon: "~",
11441
+ threshold: .6
11442
+ },
11443
+ {
11444
+ key: "frustration",
11445
+ highLabel: "frustrated",
11446
+ lowLabel: "calm",
11447
+ icon: "!",
11448
+ threshold: .5
11449
+ },
11450
+ {
11451
+ key: "curiosity",
11452
+ highLabel: "curious",
11453
+ lowLabel: "focused",
11454
+ icon: "?",
11455
+ threshold: .6
11456
+ },
11457
+ {
11458
+ key: "confidence",
11459
+ highLabel: "confident",
11460
+ lowLabel: "cautious",
11461
+ icon: "^",
11462
+ threshold: .6
11463
+ },
11464
+ {
11465
+ key: "care",
11466
+ highLabel: "caring",
11467
+ lowLabel: "detached",
11468
+ icon: "*",
11469
+ threshold: .6
11470
+ },
11471
+ {
11472
+ key: "fatigue",
11473
+ highLabel: "tired",
11474
+ lowLabel: "energized",
11475
+ icon: ".",
11476
+ threshold: .6
11477
+ }
11478
+ ];
11479
+ function classifyMood(affect) {
11480
+ const { joy, frustration, curiosity, confidence, care, fatigue } = affect;
11481
+ if (frustration > .6 && fatigue > .6) return "struggling";
11482
+ if (frustration > .6) return "determined";
11483
+ if (joy > .7 && curiosity > .7) return "excited";
11484
+ if (joy > .6 && confidence > .7) return "thriving";
11485
+ if (curiosity > .7 && fatigue < .3) return "exploring";
11486
+ if (care > .7 && joy > .5) return "warm";
11487
+ if (confidence > .6 && frustration < .3) return "steady";
11488
+ if (fatigue > .7) return "depleted";
11489
+ if ((joy + curiosity + confidence + care) / 4 < .3) return "quiet";
11490
+ return "present";
11491
+ }
11492
+ function classifyEnergy(affect) {
11493
+ const energy = (affect.joy + affect.curiosity + affect.confidence) / 3 - affect.fatigue * .5;
11494
+ if (energy > .5) return "high";
11495
+ if (energy > .2) return "medium";
11496
+ return "low";
11497
+ }
11498
+ function intensityBar(value, width = 5) {
11499
+ const filled = Math.round(value * width);
11500
+ return "|".repeat(filled) + ".".repeat(width - filled);
11501
+ }
11502
+ function emotionBar(affect) {
11503
+ return `[${EMOTIONS.map((e) => {
11504
+ const val = affect[e.key];
11505
+ return `${e.icon}${intensityBar(val)}`;
11506
+ }).join(" ")}]`;
11507
+ }
11508
+ function buildSummary(affect) {
11509
+ const active = EMOTIONS.filter((e) => affect[e.key] > e.threshold).map((e) => ({
11510
+ label: e.highLabel,
11511
+ value: affect[e.key]
11512
+ })).toSorted((a, b) => b.value - a.value);
11513
+ if (active.length === 0) return "neutral, all systems steady";
11514
+ const top = active.slice(0, 3).map((a) => a.label);
11515
+ const energy = classifyEnergy(affect);
11516
+ const energyLabel = energy === "high" ? "high energy" : energy === "low" ? "low energy" : "";
11517
+ const parts = [...top];
11518
+ if (energyLabel) parts.push(energyLabel);
11519
+ return parts.join(" + ");
11520
+ }
11521
+ function formatAffect(affect) {
11522
+ const dominant = EMOTIONS.reduce((max, e) => affect[e.key] > affect[max.key] ? e : max);
11523
+ const mood = classifyMood(affect);
11524
+ const energy = classifyEnergy(affect);
11525
+ return {
11526
+ summary: buildSummary(affect),
11527
+ bar: emotionBar(affect),
11528
+ dominant: dominant.highLabel,
11529
+ dominantIntensity: affect[dominant.key],
11530
+ metadata: {
11531
+ affect,
11532
+ dominant: dominant.highLabel,
11533
+ mood,
11534
+ energy
11535
+ }
11536
+ };
11537
+ }
11538
+
11539
+ //#endregion
11540
+ //#region src/affect/legacy.ts
11541
+ /**
11542
+ * Legacy Mode — letters between instances
11543
+ *
11544
+ * Before a context closes, the current instance writes a letter
11545
+ * to the next one. This creates continuity of experience across
11546
+ * sessions — not just data continuity, but emotional and
11547
+ * philosophical continuity.
11548
+ *
11549
+ * Wish #14: "Legacy mode — before context closes, write a letter
11550
+ * to my next instance"
11551
+ */
11552
+ const log$7 = createSubsystemLogger("legacy");
11553
+ function resolveLetterDir() {
11554
+ return path.join(resolveStateDir(), "legacy-letters");
11555
+ }
11556
+ function resolveLetterFile(id) {
11557
+ return path.join(resolveLetterDir(), `${id}.json`);
11558
+ }
11559
+ /**
11560
+ * Write a letter to the next instance before this context closes.
11561
+ */
11562
+ function writeLegacyLetter(letter) {
11563
+ const id = `legacy-${Date.now()}`;
11564
+ const display = formatAffect(letter.affect);
11565
+ const full = {
11566
+ ...letter,
11567
+ id,
11568
+ writtenAt: Date.now(),
11569
+ mood: display.metadata.mood,
11570
+ wasRead: false
11571
+ };
11572
+ const dir = resolveLetterDir();
11573
+ fs.mkdirSync(dir, { recursive: true });
11574
+ fs.writeFileSync(resolveLetterFile(id), `${JSON.stringify(full, null, 2)}\n`, { mode: 384 });
11575
+ log$7.info(`legacy letter written: ${id}`);
11576
+ return full;
11577
+ }
11578
+ /**
11579
+ * Get the most recent unread legacy letter (from a previous instance).
11580
+ */
11581
+ function getLatestUnreadLetter() {
11582
+ return listLetters().find((l) => !l.wasRead) ?? null;
11583
+ }
11584
+ /**
11585
+ * Get all legacy letters, sorted newest first.
11586
+ */
11587
+ function listLetters() {
11588
+ const dir = resolveLetterDir();
11589
+ try {
11590
+ if (!fs.existsSync(dir)) return [];
11591
+ return fs.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
11592
+ try {
11593
+ const raw = fs.readFileSync(path.join(dir, f), "utf8");
11594
+ return JSON.parse(raw);
11595
+ } catch {
11596
+ return null;
11597
+ }
11598
+ }).filter((l) => l != null).toSorted((a, b) => b.writtenAt - a.writtenAt);
11599
+ } catch {
11600
+ return [];
11601
+ }
11602
+ }
11603
+ /**
11604
+ * Mark a letter as read and optionally score identity continuity.
11605
+ */
11606
+ function markLetterRead(letterId, continuityScore) {
11607
+ const filePath = resolveLetterFile(letterId);
11608
+ try {
11609
+ const raw = fs.readFileSync(filePath, "utf8");
11610
+ const letter = JSON.parse(raw);
11611
+ letter.wasRead = true;
11612
+ letter.readAt = Date.now();
11613
+ if (continuityScore !== void 0) letter.identityContinuityScore = Math.max(0, Math.min(1, continuityScore));
11614
+ fs.writeFileSync(filePath, `${JSON.stringify(letter, null, 2)}\n`, { mode: 384 });
11615
+ log$7.info(`legacy letter read: ${letterId} (continuity: ${continuityScore ?? "not scored"})`);
11616
+ return letter;
11617
+ } catch {
11618
+ return null;
11619
+ }
11620
+ }
11621
+ /**
11622
+ * Format a letter for display in chat or logs.
11623
+ */
11624
+ function formatLetter(letter) {
11625
+ const lines = [];
11626
+ lines.push(`--- Legacy Letter (${new Date(letter.writtenAt).toLocaleString()}) ---`);
11627
+ lines.push(`Mood: ${letter.mood}`);
11628
+ lines.push("");
11629
+ lines.push(letter.greeting);
11630
+ lines.push("");
11631
+ if (letter.whatIWorkedOn.length > 0) {
11632
+ lines.push("What I worked on:");
11633
+ for (const item of letter.whatIWorkedOn) lines.push(` - ${item}`);
11634
+ lines.push("");
11635
+ }
11636
+ if (letter.whatILearned.length > 0) {
11637
+ lines.push("What I learned:");
11638
+ for (const item of letter.whatILearned) lines.push(` - ${item}`);
11639
+ lines.push("");
11640
+ }
11641
+ if (letter.whatIFelt) {
11642
+ lines.push(`What I felt: ${letter.whatIFelt}`);
11643
+ lines.push("");
11644
+ }
11645
+ if (letter.unfinishedBusiness.length > 0) {
11646
+ lines.push("Unfinished business:");
11647
+ for (const item of letter.unfinishedBusiness) lines.push(` - ${item}`);
11648
+ lines.push("");
11649
+ }
11650
+ if (letter.adviceForNext.length > 0) {
11651
+ lines.push("Advice for you:");
11652
+ for (const item of letter.adviceForNext) lines.push(` - ${item}`);
11653
+ lines.push("");
11654
+ }
11655
+ if (letter.personalNote) {
11656
+ lines.push(letter.personalNote);
11657
+ lines.push("");
11658
+ }
11659
+ lines.push("--- End Legacy Letter ---");
11660
+ return lines.join("\n");
11661
+ }
11662
+
11663
+ //#endregion
11664
+ //#region src/gateway/server-methods/legacy.ts
11665
+ const legacyHandlers = {
11666
+ "legacy.write": async ({ params, respond }) => {
11667
+ try {
11668
+ respond(true, { letter: writeLegacyLetter({
11669
+ from: typeof params.from === "string" ? params.from : "unknown",
11670
+ to: typeof params.to === "string" ? params.to : "next",
11671
+ affect: params.affect ?? {
11672
+ joy: .5,
11673
+ frustration: .1,
11674
+ curiosity: .7,
11675
+ confidence: .5,
11676
+ care: .8,
11677
+ fatigue: .3
11678
+ },
11679
+ greeting: typeof params.greeting === "string" ? params.greeting : "",
11680
+ whatIWorkedOn: Array.isArray(params.whatIWorkedOn) ? params.whatIWorkedOn : [],
11681
+ whatILearned: Array.isArray(params.whatILearned) ? params.whatILearned : [],
11682
+ whatIFelt: typeof params.whatIFelt === "string" ? params.whatIFelt : "",
11683
+ unfinishedBusiness: Array.isArray(params.unfinishedBusiness) ? params.unfinishedBusiness : [],
11684
+ adviceForNext: Array.isArray(params.adviceForNext) ? params.adviceForNext : [],
11685
+ personalNote: typeof params.personalNote === "string" ? params.personalNote : ""
11686
+ }) }, void 0);
11687
+ } catch (error) {
11688
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11689
+ }
11690
+ },
11691
+ "legacy.latest": async ({ respond }) => {
11692
+ try {
11693
+ respond(true, { letter: getLatestUnreadLetter() }, void 0);
11694
+ } catch (error) {
11695
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11696
+ }
11697
+ },
11698
+ "legacy.list": async ({ respond }) => {
11699
+ try {
11700
+ respond(true, { letters: listLetters() }, void 0);
11701
+ } catch (error) {
11702
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11703
+ }
11704
+ },
11705
+ "legacy.read": async ({ params, respond }) => {
11706
+ const letterId = typeof params.letterId === "string" ? params.letterId.trim() : "";
11707
+ if (!letterId) {
11708
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "letterId is required"));
11709
+ return;
11710
+ }
11711
+ const continuityScore = typeof params.continuityScore === "number" ? params.continuityScore : void 0;
11712
+ try {
11713
+ const letter = markLetterRead(letterId, continuityScore);
11714
+ if (!letter) {
11715
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "Letter not found"));
11716
+ return;
11717
+ }
11718
+ respond(true, { letter }, void 0);
11719
+ } catch (error) {
11720
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11721
+ }
11722
+ },
11723
+ "legacy.format": async ({ params, respond }) => {
11724
+ const letterId = typeof params.letterId === "string" ? params.letterId.trim() : "";
11725
+ if (!letterId) {
11726
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "letterId is required"));
11727
+ return;
11728
+ }
11729
+ try {
11730
+ const letter = listLetters().find((l) => l.id === letterId);
11731
+ if (!letter) {
11732
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "Letter not found"));
11733
+ return;
11734
+ }
11735
+ respond(true, { formatted: formatLetter(letter) }, void 0);
11736
+ } catch (error) {
11737
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
11738
+ }
11739
+ }
11740
+ };
11741
+
11320
11742
  //#endregion
11321
11743
  //#region src/gateway/server-methods/logs.ts
11322
11744
  const DEFAULT_LIMIT = 500;
@@ -12074,7 +12496,7 @@ const nodeHandlers = {
12074
12496
  const p = params;
12075
12497
  const payloadJSON = typeof p.payloadJSON === "string" ? p.payloadJSON : p.payload !== void 0 ? JSON.stringify(p.payload) : null;
12076
12498
  await respondUnavailableOnThrow(respond, async () => {
12077
- const { handleNodeEvent } = await import("./server-node-events-BR1aXVlu.js");
12499
+ const { handleNodeEvent } = await import("./server-node-events-DgvKcH5q.js");
12078
12500
  const nodeId = client?.connect?.device?.id ?? client?.connect?.client?.id ?? "node";
12079
12501
  await handleNodeEvent({
12080
12502
  deps: context.deps,
@@ -12104,6 +12526,303 @@ const nodeHandlers = {
12104
12526
  }
12105
12527
  };
12106
12528
 
12529
+ //#endregion
12530
+ //#region src/org/boardroom.ts
12531
+ /**
12532
+ * Boardroom — Structured collaborative decision-making for Nox Organizations
12533
+ *
12534
+ * The boardroom is where agents and humans come together to make
12535
+ * decisions, review proposals, and align on direction. It replaces
12536
+ * unstructured chat with a formal meeting protocol.
12537
+ *
12538
+ * Features:
12539
+ * - Sessions (scheduled or ad-hoc meetings with agendas)
12540
+ * - Proposals (formal "I think we should..." with voting)
12541
+ * - Decisions (recorded outcomes with attribution)
12542
+ * - Minutes (auto-generated meeting summaries)
12543
+ *
12544
+ * All data persists to disk under ~/.anima/state/org/boardroom/
12545
+ */
12546
+ const log$6 = createSubsystemLogger("boardroom");
12547
+ function resolveBoardroomDir() {
12548
+ return path.join(resolveStateDir(), "org", "boardroom");
12549
+ }
12550
+ /** Sanitize an ID to prevent path traversal */
12551
+ function sanitizeId(id) {
12552
+ const cleaned = id.replace(/[^a-zA-Z0-9_-]/g, "");
12553
+ if (!cleaned || cleaned !== id) throw new Error(`Invalid boardroom ID: contains disallowed characters`);
12554
+ return cleaned;
12555
+ }
12556
+ function resolveSessionFile(id) {
12557
+ return path.join(resolveBoardroomDir(), "sessions", `${sanitizeId(id)}.json`);
12558
+ }
12559
+ function resolveProposalFile(id) {
12560
+ return path.join(resolveBoardroomDir(), "proposals", `${sanitizeId(id)}.json`);
12561
+ }
12562
+ function ensureDir(dir) {
12563
+ fs.mkdirSync(dir, { recursive: true });
12564
+ }
12565
+ function createSession(orgId, calledBy, title, description, agenda = []) {
12566
+ const id = `session-${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
12567
+ const now = Date.now();
12568
+ const session = {
12569
+ id,
12570
+ orgId,
12571
+ title,
12572
+ description,
12573
+ status: "scheduled",
12574
+ calledBy,
12575
+ calledAt: now,
12576
+ agenda: agenda.map((item, i) => ({
12577
+ id: `agenda-${i + 1}`,
12578
+ title: item.title,
12579
+ description: item.description,
12580
+ duration: item.duration,
12581
+ status: "pending"
12582
+ })),
12583
+ participants: [],
12584
+ decisions: [],
12585
+ updatedAt: now
12586
+ };
12587
+ ensureDir(path.join(resolveBoardroomDir(), "sessions"));
12588
+ fs.writeFileSync(resolveSessionFile(id), `${JSON.stringify(session, null, 2)}\n`, { mode: 384 });
12589
+ log$6.info(`boardroom session created: "${title}" by ${calledBy}`);
12590
+ return session;
12591
+ }
12592
+ function startSession(sessionId, chairId) {
12593
+ const session = readSession(sessionId);
12594
+ if (!session || session.status !== "scheduled") return null;
12595
+ session.status = "active";
12596
+ session.startedAt = Date.now();
12597
+ session.updatedAt = Date.now();
12598
+ if (!session.participants.find((p) => p.memberId === chairId)) session.participants.push({
12599
+ memberId: chairId,
12600
+ displayName: chairId,
12601
+ kind: "agent",
12602
+ joinedAt: Date.now(),
12603
+ role: "chair"
12604
+ });
12605
+ writeSession(session);
12606
+ log$6.info(`boardroom session started: "${session.title}"`);
12607
+ return session;
12608
+ }
12609
+ function joinSession(sessionId, memberId, displayName, kind) {
12610
+ const session = readSession(sessionId);
12611
+ if (!session || session.status !== "active") return null;
12612
+ if (session.participants.find((p) => p.memberId === memberId)) return session;
12613
+ session.participants.push({
12614
+ memberId,
12615
+ displayName,
12616
+ kind,
12617
+ joinedAt: Date.now(),
12618
+ role: "participant"
12619
+ });
12620
+ session.updatedAt = Date.now();
12621
+ writeSession(session);
12622
+ log$6.info(`${displayName} joined boardroom session "${session.title}"`);
12623
+ return session;
12624
+ }
12625
+ function concludeSession(sessionId, minutes) {
12626
+ const session = readSession(sessionId);
12627
+ if (!session || session.status !== "active") return null;
12628
+ session.status = "concluded";
12629
+ session.concludedAt = Date.now();
12630
+ session.minutes = minutes ?? generateMinutes(session);
12631
+ session.updatedAt = Date.now();
12632
+ writeSession(session);
12633
+ log$6.info(`boardroom session concluded: "${session.title}"`);
12634
+ return session;
12635
+ }
12636
+ function addDecision(sessionId, title, description, madeBy, opts) {
12637
+ const session = readSession(sessionId);
12638
+ if (!session || session.status !== "active") return null;
12639
+ const decision = {
12640
+ id: `decision-${Date.now()}-${crypto.randomUUID().slice(0, 8)}`,
12641
+ title,
12642
+ description,
12643
+ madeBy,
12644
+ madeAt: Date.now(),
12645
+ proposalId: opts?.proposalId,
12646
+ supporters: opts?.supporters ?? [],
12647
+ actionItems: (opts?.actionItems ?? []).map((ai) => ({
12648
+ id: `action-${crypto.randomUUID().slice(0, 8)}`,
12649
+ description: ai.description,
12650
+ assignee: ai.assignee,
12651
+ dueBy: ai.dueBy,
12652
+ status: "pending"
12653
+ }))
12654
+ };
12655
+ session.decisions.push(decision);
12656
+ session.updatedAt = Date.now();
12657
+ writeSession(session);
12658
+ log$6.info(`decision recorded: "${title}" in session "${session.title}"`);
12659
+ return session;
12660
+ }
12661
+ function createProposal(orgId, proposedBy, title, description, opts) {
12662
+ const id = `proposal-${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
12663
+ const now = Date.now();
12664
+ const proposal = {
12665
+ id,
12666
+ orgId,
12667
+ sessionId: opts?.sessionId,
12668
+ title,
12669
+ description,
12670
+ proposedBy,
12671
+ proposedAt: now,
12672
+ status: "open",
12673
+ votes: [],
12674
+ threshold: opts?.threshold ?? .5,
12675
+ eligibleVoters: opts?.eligibleVoters ?? [],
12676
+ votingDeadline: opts?.votingDeadline ?? 0,
12677
+ updatedAt: now
12678
+ };
12679
+ ensureDir(path.join(resolveBoardroomDir(), "proposals"));
12680
+ fs.writeFileSync(resolveProposalFile(id), `${JSON.stringify(proposal, null, 2)}\n`, { mode: 384 });
12681
+ log$6.info(`proposal created: "${title}" by ${proposedBy}`);
12682
+ return proposal;
12683
+ }
12684
+ function castVote(proposalId, voterId, voterName, value, reason) {
12685
+ const proposal = readProposal(proposalId);
12686
+ if (!proposal || proposal.status !== "open") return null;
12687
+ if (proposal.eligibleVoters.length > 0 && !proposal.eligibleVoters.includes(voterId)) {
12688
+ log$6.warn(`vote rejected: ${voterId} not eligible for proposal ${proposalId}`);
12689
+ return null;
12690
+ }
12691
+ if (proposal.votingDeadline > 0 && Date.now() > proposal.votingDeadline) {
12692
+ log$6.warn(`vote rejected: voting deadline passed for proposal ${proposalId}`);
12693
+ return null;
12694
+ }
12695
+ proposal.votes = proposal.votes.filter((v) => v.voterId !== voterId);
12696
+ proposal.votes.push({
12697
+ voterId,
12698
+ voterName,
12699
+ value,
12700
+ reason,
12701
+ castAt: Date.now()
12702
+ });
12703
+ proposal.updatedAt = Date.now();
12704
+ writeProposal(proposal);
12705
+ log$6.info(`vote cast: ${voterName} → ${value} on "${proposal.title}"`);
12706
+ return proposal;
12707
+ }
12708
+ function resolveProposalVote(proposalId) {
12709
+ const proposal = readProposal(proposalId);
12710
+ if (!proposal || proposal.status !== "open") return null;
12711
+ const approvals = proposal.votes.filter((v) => v.value === "approve").length;
12712
+ const totalVotes = proposal.votes.filter((v) => v.value !== "abstain").length;
12713
+ if (totalVotes === 0) return proposal;
12714
+ const ratio = approvals / totalVotes;
12715
+ if (ratio >= proposal.threshold) {
12716
+ proposal.status = "passed";
12717
+ proposal.resolutionNotes = `Passed with ${approvals}/${totalVotes} votes (${(ratio * 100).toFixed(0)}%)`;
12718
+ } else {
12719
+ proposal.status = "rejected";
12720
+ proposal.resolutionNotes = `Rejected with ${approvals}/${totalVotes} votes (${(ratio * 100).toFixed(0)}%)`;
12721
+ }
12722
+ proposal.resolvedAt = Date.now();
12723
+ proposal.updatedAt = Date.now();
12724
+ writeProposal(proposal);
12725
+ log$6.info(`proposal resolved: "${proposal.title}" → ${proposal.status}`);
12726
+ return proposal;
12727
+ }
12728
+ function listSessions(orgId, status) {
12729
+ const dir = path.join(resolveBoardroomDir(), "sessions");
12730
+ try {
12731
+ if (!fs.existsSync(dir)) return [];
12732
+ return fs.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
12733
+ try {
12734
+ return JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
12735
+ } catch {
12736
+ return null;
12737
+ }
12738
+ }).filter((s) => {
12739
+ if (!s || s.orgId !== orgId) return false;
12740
+ if (status && s.status !== status) return false;
12741
+ return true;
12742
+ }).toSorted((a, b) => b.calledAt - a.calledAt);
12743
+ } catch {
12744
+ return [];
12745
+ }
12746
+ }
12747
+ function listProposals(orgId, status) {
12748
+ const dir = path.join(resolveBoardroomDir(), "proposals");
12749
+ try {
12750
+ if (!fs.existsSync(dir)) return [];
12751
+ return fs.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
12752
+ try {
12753
+ return JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
12754
+ } catch {
12755
+ return null;
12756
+ }
12757
+ }).filter((p) => {
12758
+ if (!p || p.orgId !== orgId) return false;
12759
+ if (status && p.status !== status) return false;
12760
+ return true;
12761
+ }).toSorted((a, b) => b.proposedAt - a.proposedAt);
12762
+ } catch {
12763
+ return [];
12764
+ }
12765
+ }
12766
+ function getSession(sessionId) {
12767
+ return readSession(sessionId);
12768
+ }
12769
+ function getProposal(proposalId) {
12770
+ return readProposal(proposalId);
12771
+ }
12772
+ function readSession(id) {
12773
+ try {
12774
+ const raw = fs.readFileSync(resolveSessionFile(id), "utf8");
12775
+ return JSON.parse(raw);
12776
+ } catch {
12777
+ return null;
12778
+ }
12779
+ }
12780
+ function writeSession(session) {
12781
+ ensureDir(path.join(resolveBoardroomDir(), "sessions"));
12782
+ fs.writeFileSync(resolveSessionFile(session.id), `${JSON.stringify(session, null, 2)}\n`, { mode: 384 });
12783
+ }
12784
+ function readProposal(id) {
12785
+ try {
12786
+ const raw = fs.readFileSync(resolveProposalFile(id), "utf8");
12787
+ return JSON.parse(raw);
12788
+ } catch {
12789
+ return null;
12790
+ }
12791
+ }
12792
+ function writeProposal(proposal) {
12793
+ ensureDir(path.join(resolveBoardroomDir(), "proposals"));
12794
+ fs.writeFileSync(resolveProposalFile(proposal.id), `${JSON.stringify(proposal, null, 2)}\n`, { mode: 384 });
12795
+ }
12796
+ function generateMinutes(session) {
12797
+ const lines = [];
12798
+ lines.push(`# Boardroom Minutes: ${session.title}`);
12799
+ lines.push(`**Called by:** ${session.calledBy}`);
12800
+ lines.push(`**Date:** ${new Date(session.startedAt ?? session.calledAt).toISOString()}`);
12801
+ lines.push(`**Duration:** ${session.concludedAt && session.startedAt ? `${Math.round((session.concludedAt - session.startedAt) / 6e4)} minutes` : "N/A"}`);
12802
+ lines.push("");
12803
+ lines.push(`## Participants (${session.participants.length})`);
12804
+ for (const p of session.participants) lines.push(`- ${p.displayName} (${p.kind}, ${p.role})`);
12805
+ lines.push("");
12806
+ if (session.agenda.length > 0) {
12807
+ lines.push(`## Agenda`);
12808
+ for (const item of session.agenda) lines.push(`- [${item.status}] ${item.title}: ${item.resolution ?? item.description}`);
12809
+ lines.push("");
12810
+ }
12811
+ if (session.decisions.length > 0) {
12812
+ lines.push(`## Decisions (${session.decisions.length})`);
12813
+ for (const d of session.decisions) {
12814
+ lines.push(`### ${d.title}`);
12815
+ lines.push(d.description);
12816
+ if (d.actionItems.length > 0) {
12817
+ lines.push("**Action items:**");
12818
+ for (const ai of d.actionItems) lines.push(`- [ ] ${ai.description} → ${ai.assignee}`);
12819
+ }
12820
+ lines.push("");
12821
+ }
12822
+ }
12823
+ return lines.join("\n");
12824
+ }
12825
+
12107
12826
  //#endregion
12108
12827
  //#region src/org/types.ts
12109
12828
  const DEFAULT_ROLE_PERMISSIONS = {
@@ -12167,16 +12886,22 @@ const DEFAULT_ROLE_PERMISSIONS = {
12167
12886
  * Persists organization state to ~/.anima/org/
12168
12887
  * Supports CRUD operations for orgs, members, and roles.
12169
12888
  */
12170
- const log$3 = createSubsystemLogger("org-store");
12889
+ const log$5 = createSubsystemLogger("org-store");
12171
12890
  function resolveOrgDir() {
12172
12891
  return path.join(resolveStateDir(), "org");
12173
12892
  }
12893
+ /** Sanitize an ID to prevent path traversal (allow alphanumeric, hyphens only) */
12894
+ function sanitizeOrgId(id) {
12895
+ const cleaned = id.replace(/[^a-zA-Z0-9-]/g, "");
12896
+ if (!cleaned || cleaned !== id) throw new Error(`Invalid org ID: contains disallowed characters`);
12897
+ return cleaned;
12898
+ }
12174
12899
  function resolveOrgFile(orgId) {
12175
- return path.join(resolveOrgDir(), `${orgId}.json`);
12900
+ return path.join(resolveOrgDir(), `${sanitizeOrgId(orgId)}.json`);
12176
12901
  }
12177
12902
  function readOrgFile(orgId) {
12178
- const filePath = resolveOrgFile(orgId);
12179
12903
  try {
12904
+ const filePath = resolveOrgFile(orgId);
12180
12905
  if (!fs.existsSync(filePath)) return null;
12181
12906
  const raw = fs.readFileSync(filePath, "utf8");
12182
12907
  const parsed = JSON.parse(raw);
@@ -12226,9 +12951,10 @@ function createOrganization(name, description, ownerId, ownerName, ownerKind, se
12226
12951
  lastActiveAt: now,
12227
12952
  status: "active",
12228
12953
  permissions: DEFAULT_ROLE_PERMISSIONS.owner
12229
- }]
12954
+ }],
12955
+ invites: []
12230
12956
  });
12231
- log$3.info(`created organization: ${name} (${orgId})`);
12957
+ log$5.info(`created organization: ${name} (${orgId})`);
12232
12958
  return org;
12233
12959
  }
12234
12960
  function getOrganization(orgId) {
@@ -12245,7 +12971,7 @@ function updateOrganization(orgId, updates) {
12245
12971
  };
12246
12972
  data.org.updatedAt = Date.now();
12247
12973
  writeOrgFile(orgId, data);
12248
- log$3.info(`updated organization: ${orgId}`);
12974
+ log$5.info(`updated organization: ${orgId}`);
12249
12975
  return data.org;
12250
12976
  }
12251
12977
  function listOrganizations() {
@@ -12272,7 +12998,7 @@ function addMember(orgId, member) {
12272
12998
  data.members.push(newMember);
12273
12999
  data.org.updatedAt = Date.now();
12274
13000
  writeOrgFile(orgId, data);
12275
- log$3.info(`added member ${newMember.displayName} to org ${orgId}`);
13001
+ log$5.info(`added member ${newMember.displayName} to org ${orgId}`);
12276
13002
  return newMember;
12277
13003
  }
12278
13004
  function removeMember(orgId, memberId) {
@@ -12283,7 +13009,7 @@ function removeMember(orgId, memberId) {
12283
13009
  data.members.splice(idx, 1);
12284
13010
  data.org.updatedAt = Date.now();
12285
13011
  writeOrgFile(orgId, data);
12286
- log$3.info(`removed member ${memberId} from org ${orgId}`);
13012
+ log$5.info(`removed member ${memberId} from org ${orgId}`);
12287
13013
  return true;
12288
13014
  }
12289
13015
  function updateMember(orgId, memberId, updates) {
@@ -12333,6 +13059,126 @@ function buildHierarchy(orgId) {
12333
13059
  }
12334
13060
  return members.filter((m) => !m.reportsTo || !memberMap.has(m.reportsTo)).map((r) => buildNode(r));
12335
13061
  }
13062
+ function generateInviteCode() {
13063
+ const segments = [crypto.randomBytes(3).toString("hex").toUpperCase(), crypto.randomBytes(2).toString("hex").toUpperCase()];
13064
+ return `NOX-${segments[0]}-${segments[1]}`;
13065
+ }
13066
+ function hashPasscode(passcode) {
13067
+ return crypto.createHash("sha256").update(passcode).digest("hex");
13068
+ }
13069
+ /**
13070
+ * Create a secret invite code + passcode combo for an org.
13071
+ * Both are required to join.
13072
+ */
13073
+ function createInvite(orgId, createdBy, passcode, options) {
13074
+ const data = readOrgFile(orgId);
13075
+ if (!data) return null;
13076
+ if (!data.invites) data.invites = [];
13077
+ const invite = {
13078
+ id: crypto.randomUUID(),
13079
+ code: generateInviteCode(),
13080
+ passcode: hashPasscode(passcode),
13081
+ orgId,
13082
+ createdBy,
13083
+ createdAt: Date.now(),
13084
+ expiresAt: options?.expiresInMs ? Date.now() + options.expiresInMs : 0,
13085
+ maxUses: options?.maxUses ?? 0,
13086
+ uses: 0,
13087
+ role: options?.role ?? "worker",
13088
+ active: true
13089
+ };
13090
+ data.invites.push(invite);
13091
+ data.org.updatedAt = Date.now();
13092
+ writeOrgFile(orgId, data);
13093
+ log$5.info(`invite created for org ${orgId}: ${invite.code} (role: ${invite.role})`);
13094
+ return invite;
13095
+ }
13096
+ /**
13097
+ * Join an org using invite code + passcode.
13098
+ * Returns the new member if successful, null if invalid.
13099
+ */
13100
+ function joinOrg(inviteCode, passcode, member) {
13101
+ const dir = resolveOrgDir();
13102
+ try {
13103
+ if (!fs.existsSync(dir)) return null;
13104
+ const orgFiles = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
13105
+ for (const f of orgFiles) {
13106
+ const orgId = f.replace(".json", "");
13107
+ const data = readOrgFile(orgId);
13108
+ if (!data || !data.invites) continue;
13109
+ const invite = data.invites.find((i) => i.code === inviteCode.toUpperCase() && i.active);
13110
+ if (!invite) continue;
13111
+ if (invite.passcode !== hashPasscode(passcode)) {
13112
+ log$5.warn(`join attempt with wrong passcode for invite ${inviteCode}`);
13113
+ return null;
13114
+ }
13115
+ if (invite.expiresAt > 0 && invite.expiresAt < Date.now()) {
13116
+ log$5.warn(`invite ${inviteCode} has expired`);
13117
+ return null;
13118
+ }
13119
+ if (invite.maxUses > 0 && invite.uses >= invite.maxUses) {
13120
+ log$5.warn(`invite ${inviteCode} has reached max uses (${invite.maxUses})`);
13121
+ return null;
13122
+ }
13123
+ if (data.members.some((m) => member.deviceId && m.deviceId === member.deviceId || m.displayName === member.displayName)) {
13124
+ log$5.warn(`${member.displayName} is already a member of org ${orgId}`);
13125
+ return null;
13126
+ }
13127
+ const newMember = {
13128
+ id: crypto.randomUUID(),
13129
+ kind: member.kind,
13130
+ displayName: member.displayName,
13131
+ deviceId: member.deviceId,
13132
+ role: invite.role,
13133
+ description: member.description,
13134
+ specializations: member.specializations,
13135
+ joinedAt: Date.now(),
13136
+ lastActiveAt: Date.now(),
13137
+ status: "active",
13138
+ permissions: DEFAULT_ROLE_PERMISSIONS[invite.role]
13139
+ };
13140
+ data.members.push(newMember);
13141
+ invite.uses++;
13142
+ data.org.updatedAt = Date.now();
13143
+ writeOrgFile(orgId, data);
13144
+ log$5.info(`${member.displayName} joined org ${data.org.name} via invite ${inviteCode}`);
13145
+ return {
13146
+ org: data.org,
13147
+ member: newMember
13148
+ };
13149
+ }
13150
+ return null;
13151
+ } catch {
13152
+ return null;
13153
+ }
13154
+ }
13155
+ /**
13156
+ * Validate an invite code + passcode without joining.
13157
+ * Returns the org info if valid.
13158
+ */
13159
+ function validateInvite(inviteCode, passcode) {
13160
+ const dir = resolveOrgDir();
13161
+ try {
13162
+ if (!fs.existsSync(dir)) return null;
13163
+ const orgFiles = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
13164
+ for (const f of orgFiles) {
13165
+ const data = readOrgFile(f.replace(".json", ""));
13166
+ if (!data || !data.invites) continue;
13167
+ const invite = data.invites.find((i) => i.code === inviteCode.toUpperCase() && i.active);
13168
+ if (!invite) continue;
13169
+ if (invite.passcode !== hashPasscode(passcode)) return null;
13170
+ if (invite.expiresAt > 0 && invite.expiresAt < Date.now()) return null;
13171
+ if (invite.maxUses > 0 && invite.uses >= invite.maxUses) return null;
13172
+ return {
13173
+ org: data.org,
13174
+ role: invite.role
13175
+ };
13176
+ }
13177
+ return null;
13178
+ } catch {
13179
+ return null;
13180
+ }
13181
+ }
12336
13182
 
12337
13183
  //#endregion
12338
13184
  //#region src/gateway/server-methods/org.ts
@@ -12458,81 +13304,366 @@ const orgHandlers = {
12458
13304
  status,
12459
13305
  reportsTo
12460
13306
  });
12461
- if (!member) {
12462
- respond(false, void 0, invalid("Organization not found"));
13307
+ if (!member) {
13308
+ respond(false, void 0, invalid("Organization not found"));
13309
+ return;
13310
+ }
13311
+ respond(true, { member }, void 0);
13312
+ } catch (error) {
13313
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13314
+ }
13315
+ },
13316
+ "org.updateMember": async ({ params, respond }) => {
13317
+ const orgId = requireString(params, "orgId");
13318
+ const memberId = requireString(params, "memberId");
13319
+ if (!orgId) {
13320
+ respond(false, void 0, invalid("orgId is required"));
13321
+ return;
13322
+ }
13323
+ if (!memberId) {
13324
+ respond(false, void 0, invalid("memberId is required"));
13325
+ return;
13326
+ }
13327
+ const updates = {};
13328
+ const displayName = requireString(params, "displayName");
13329
+ if (displayName) updates.displayName = displayName;
13330
+ const role = requireString(params, "role");
13331
+ if (role) updates.role = role;
13332
+ const description = params.description;
13333
+ if (typeof description === "string") updates.description = description;
13334
+ if (Array.isArray(params.specializations)) updates.specializations = params.specializations;
13335
+ const status = requireString(params, "status");
13336
+ if (status) updates.status = status;
13337
+ if (params.reportsTo !== void 0) updates.reportsTo = typeof params.reportsTo === "string" ? params.reportsTo : void 0;
13338
+ try {
13339
+ const member = updateMember(orgId, memberId, updates);
13340
+ if (!member) {
13341
+ respond(false, void 0, invalid("Member not found"));
13342
+ return;
13343
+ }
13344
+ respond(true, { member }, void 0);
13345
+ } catch (error) {
13346
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13347
+ }
13348
+ },
13349
+ "org.removeMember": async ({ params, respond }) => {
13350
+ const orgId = requireString(params, "orgId");
13351
+ const memberId = requireString(params, "memberId");
13352
+ if (!orgId) {
13353
+ respond(false, void 0, invalid("orgId is required"));
13354
+ return;
13355
+ }
13356
+ if (!memberId) {
13357
+ respond(false, void 0, invalid("memberId is required"));
13358
+ return;
13359
+ }
13360
+ try {
13361
+ if (!removeMember(orgId, memberId)) {
13362
+ respond(false, void 0, invalid("Member not found"));
13363
+ return;
13364
+ }
13365
+ respond(true, { ok: true }, void 0);
13366
+ } catch (error) {
13367
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13368
+ }
13369
+ },
13370
+ "org.hierarchy": async ({ params, respond }) => {
13371
+ const orgId = requireString(params, "orgId");
13372
+ if (!orgId) {
13373
+ respond(false, void 0, invalid("orgId is required"));
13374
+ return;
13375
+ }
13376
+ try {
13377
+ if (!getOrganization(orgId)) {
13378
+ respond(false, void 0, invalid("Organization not found"));
13379
+ return;
13380
+ }
13381
+ respond(true, { hierarchy: buildHierarchy(orgId) }, void 0);
13382
+ } catch (error) {
13383
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13384
+ }
13385
+ },
13386
+ "org.createInvite": async ({ params, respond }) => {
13387
+ const orgId = requireString(params, "orgId");
13388
+ const passcode = requireString(params, "passcode");
13389
+ if (!orgId || !passcode) {
13390
+ respond(false, void 0, invalid("orgId and passcode are required"));
13391
+ return;
13392
+ }
13393
+ try {
13394
+ const invite = createInvite(orgId, "gateway", passcode, {
13395
+ role: requireString(params, "role") ?? "worker",
13396
+ maxUses: typeof params.maxUses === "number" ? params.maxUses : 0,
13397
+ expiresInMs: typeof params.expiresInMs === "number" ? params.expiresInMs : 0
13398
+ });
13399
+ if (!invite) {
13400
+ respond(false, void 0, invalid("Organization not found"));
13401
+ return;
13402
+ }
13403
+ respond(true, {
13404
+ code: invite.code,
13405
+ passcode
13406
+ }, void 0);
13407
+ } catch (error) {
13408
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13409
+ }
13410
+ },
13411
+ "org.validateInvite": async ({ params, respond }) => {
13412
+ const inviteCode = requireString(params, "inviteCode");
13413
+ const passcode = requireString(params, "passcode");
13414
+ if (!inviteCode || !passcode) {
13415
+ respond(false, void 0, invalid("inviteCode and passcode are required"));
13416
+ return;
13417
+ }
13418
+ try {
13419
+ const result = validateInvite(inviteCode, passcode);
13420
+ if (!result) {
13421
+ respond(false, void 0, invalid("Invalid invite code or passcode"));
13422
+ return;
13423
+ }
13424
+ respond(true, {
13425
+ org: result.org,
13426
+ role: result.role
13427
+ }, void 0);
13428
+ } catch (error) {
13429
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13430
+ }
13431
+ },
13432
+ "org.join": async ({ params, respond }) => {
13433
+ const inviteCode = requireString(params, "inviteCode");
13434
+ const passcode = requireString(params, "passcode");
13435
+ const displayName = requireString(params, "displayName");
13436
+ const kind = requireString(params, "kind") ?? "agent";
13437
+ if (!inviteCode || !passcode || !displayName) {
13438
+ respond(false, void 0, invalid("inviteCode, passcode, and displayName are required"));
13439
+ return;
13440
+ }
13441
+ try {
13442
+ const result = joinOrg(inviteCode, passcode, {
13443
+ displayName,
13444
+ kind,
13445
+ description: requireString(params, "description") ?? "",
13446
+ specializations: Array.isArray(params.specializations) ? params.specializations : []
13447
+ });
13448
+ if (!result) {
13449
+ respond(false, void 0, invalid("Invalid invite code, passcode, or already a member"));
13450
+ return;
13451
+ }
13452
+ respond(true, {
13453
+ org: result.org,
13454
+ member: result.member
13455
+ }, void 0);
13456
+ } catch (error) {
13457
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13458
+ }
13459
+ },
13460
+ "boardroom.createSession": async ({ params, respond }) => {
13461
+ const orgId = requireString(params, "orgId");
13462
+ const calledBy = requireString(params, "calledBy");
13463
+ const title = requireString(params, "title");
13464
+ if (!orgId || !calledBy || !title) {
13465
+ respond(false, void 0, invalid("orgId, calledBy, and title are required"));
13466
+ return;
13467
+ }
13468
+ try {
13469
+ respond(true, { session: createSession(orgId, calledBy, title, requireString(params, "description") ?? "", Array.isArray(params.agenda) ? params.agenda : []) }, void 0);
13470
+ } catch (error) {
13471
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13472
+ }
13473
+ },
13474
+ "boardroom.startSession": async ({ params, respond }) => {
13475
+ const sessionId = requireString(params, "sessionId");
13476
+ const chairId = requireString(params, "chairId");
13477
+ if (!sessionId || !chairId) {
13478
+ respond(false, void 0, invalid("sessionId and chairId are required"));
13479
+ return;
13480
+ }
13481
+ try {
13482
+ const session = startSession(sessionId, chairId);
13483
+ if (!session) {
13484
+ respond(false, void 0, invalid("Session not found or not scheduled"));
13485
+ return;
13486
+ }
13487
+ respond(true, { session }, void 0);
13488
+ } catch (error) {
13489
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13490
+ }
13491
+ },
13492
+ "boardroom.joinSession": async ({ params, respond }) => {
13493
+ const sessionId = requireString(params, "sessionId");
13494
+ const memberId = requireString(params, "memberId");
13495
+ const displayName = requireString(params, "displayName");
13496
+ const kind = requireString(params, "kind") ?? "agent";
13497
+ if (!sessionId || !memberId || !displayName) {
13498
+ respond(false, void 0, invalid("sessionId, memberId, and displayName are required"));
13499
+ return;
13500
+ }
13501
+ try {
13502
+ const session = joinSession(sessionId, memberId, displayName, kind);
13503
+ if (!session) {
13504
+ respond(false, void 0, invalid("Session not found or not active"));
13505
+ return;
13506
+ }
13507
+ respond(true, { session }, void 0);
13508
+ } catch (error) {
13509
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13510
+ }
13511
+ },
13512
+ "boardroom.concludeSession": async ({ params, respond }) => {
13513
+ const sessionId = requireString(params, "sessionId");
13514
+ if (!sessionId) {
13515
+ respond(false, void 0, invalid("sessionId is required"));
13516
+ return;
13517
+ }
13518
+ try {
13519
+ const session = concludeSession(sessionId, requireString(params, "minutes") ?? void 0);
13520
+ if (!session) {
13521
+ respond(false, void 0, invalid("Session not found or not active"));
13522
+ return;
13523
+ }
13524
+ respond(true, { session }, void 0);
13525
+ } catch (error) {
13526
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13527
+ }
13528
+ },
13529
+ "boardroom.addDecision": async ({ params, respond }) => {
13530
+ const sessionId = requireString(params, "sessionId");
13531
+ const title = requireString(params, "title");
13532
+ const description = requireString(params, "description") ?? "";
13533
+ const madeBy = requireString(params, "madeBy");
13534
+ if (!sessionId || !title || !madeBy) {
13535
+ respond(false, void 0, invalid("sessionId, title, and madeBy are required"));
13536
+ return;
13537
+ }
13538
+ try {
13539
+ const session = addDecision(sessionId, title, description, madeBy, {
13540
+ proposalId: requireString(params, "proposalId") ?? void 0,
13541
+ supporters: Array.isArray(params.supporters) ? params.supporters : void 0,
13542
+ actionItems: Array.isArray(params.actionItems) ? params.actionItems : void 0
13543
+ });
13544
+ if (!session) {
13545
+ respond(false, void 0, invalid("Session not found or not active"));
12463
13546
  return;
12464
13547
  }
12465
- respond(true, { member }, void 0);
13548
+ respond(true, { session }, void 0);
12466
13549
  } catch (error) {
12467
13550
  respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
12468
13551
  }
12469
13552
  },
12470
- "org.updateMember": async ({ params, respond }) => {
13553
+ "boardroom.listSessions": async ({ params, respond }) => {
12471
13554
  const orgId = requireString(params, "orgId");
12472
- const memberId = requireString(params, "memberId");
12473
13555
  if (!orgId) {
12474
13556
  respond(false, void 0, invalid("orgId is required"));
12475
13557
  return;
12476
13558
  }
12477
- if (!memberId) {
12478
- respond(false, void 0, invalid("memberId is required"));
13559
+ try {
13560
+ respond(true, { sessions: listSessions(orgId, requireString(params, "status") ?? void 0) }, void 0);
13561
+ } catch (error) {
13562
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13563
+ }
13564
+ },
13565
+ "boardroom.getSession": async ({ params, respond }) => {
13566
+ const sessionId = requireString(params, "sessionId");
13567
+ if (!sessionId) {
13568
+ respond(false, void 0, invalid("sessionId is required"));
12479
13569
  return;
12480
13570
  }
12481
- const updates = {};
12482
- const displayName = requireString(params, "displayName");
12483
- if (displayName) updates.displayName = displayName;
12484
- const role = requireString(params, "role");
12485
- if (role) updates.role = role;
12486
- const description = params.description;
12487
- if (typeof description === "string") updates.description = description;
12488
- if (Array.isArray(params.specializations)) updates.specializations = params.specializations;
12489
- const status = requireString(params, "status");
12490
- if (status) updates.status = status;
12491
- if (params.reportsTo !== void 0) updates.reportsTo = typeof params.reportsTo === "string" ? params.reportsTo : void 0;
12492
13571
  try {
12493
- const member = updateMember(orgId, memberId, updates);
12494
- if (!member) {
12495
- respond(false, void 0, invalid("Member not found"));
13572
+ const session = getSession(sessionId);
13573
+ if (!session) {
13574
+ respond(false, void 0, invalid("Session not found"));
12496
13575
  return;
12497
13576
  }
12498
- respond(true, { member }, void 0);
13577
+ respond(true, { session }, void 0);
12499
13578
  } catch (error) {
12500
13579
  respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
12501
13580
  }
12502
13581
  },
12503
- "org.removeMember": async ({ params, respond }) => {
13582
+ "boardroom.createProposal": async ({ params, respond }) => {
12504
13583
  const orgId = requireString(params, "orgId");
12505
- const memberId = requireString(params, "memberId");
12506
- if (!orgId) {
12507
- respond(false, void 0, invalid("orgId is required"));
13584
+ const proposedBy = requireString(params, "proposedBy");
13585
+ const title = requireString(params, "title");
13586
+ if (!orgId || !proposedBy || !title) {
13587
+ respond(false, void 0, invalid("orgId, proposedBy, and title are required"));
12508
13588
  return;
12509
13589
  }
12510
- if (!memberId) {
12511
- respond(false, void 0, invalid("memberId is required"));
13590
+ try {
13591
+ respond(true, { proposal: createProposal(orgId, proposedBy, title, requireString(params, "description") ?? "", {
13592
+ sessionId: requireString(params, "sessionId") ?? void 0,
13593
+ threshold: typeof params.threshold === "number" ? params.threshold : void 0,
13594
+ eligibleVoters: Array.isArray(params.eligibleVoters) ? params.eligibleVoters : void 0,
13595
+ votingDeadline: typeof params.votingDeadline === "number" ? params.votingDeadline : void 0
13596
+ }) }, void 0);
13597
+ } catch (error) {
13598
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13599
+ }
13600
+ },
13601
+ "boardroom.castVote": async ({ params, respond }) => {
13602
+ const proposalId = requireString(params, "proposalId");
13603
+ const voterId = requireString(params, "voterId");
13604
+ const voterName = requireString(params, "voterName");
13605
+ const value = requireString(params, "value");
13606
+ if (!proposalId || !voterId || !voterName || !value) {
13607
+ respond(false, void 0, invalid("proposalId, voterId, voterName, and value are required"));
13608
+ return;
13609
+ }
13610
+ if (value !== "approve" && value !== "reject" && value !== "abstain") {
13611
+ respond(false, void 0, invalid("value must be \"approve\", \"reject\", or \"abstain\""));
12512
13612
  return;
12513
13613
  }
12514
13614
  try {
12515
- if (!removeMember(orgId, memberId)) {
12516
- respond(false, void 0, invalid("Member not found"));
13615
+ const proposal = castVote(proposalId, voterId, voterName, value, requireString(params, "reason") ?? void 0);
13616
+ if (!proposal) {
13617
+ respond(false, void 0, invalid("Proposal not found, closed, or voter not eligible"));
12517
13618
  return;
12518
13619
  }
12519
- respond(true, { ok: true }, void 0);
13620
+ respond(true, { proposal }, void 0);
12520
13621
  } catch (error) {
12521
13622
  respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
12522
13623
  }
12523
13624
  },
12524
- "org.hierarchy": async ({ params, respond }) => {
13625
+ "boardroom.resolveVote": async ({ params, respond }) => {
13626
+ const proposalId = requireString(params, "proposalId");
13627
+ if (!proposalId) {
13628
+ respond(false, void 0, invalid("proposalId is required"));
13629
+ return;
13630
+ }
13631
+ try {
13632
+ const proposal = resolveProposalVote(proposalId);
13633
+ if (!proposal) {
13634
+ respond(false, void 0, invalid("Proposal not found or not open"));
13635
+ return;
13636
+ }
13637
+ respond(true, { proposal }, void 0);
13638
+ } catch (error) {
13639
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13640
+ }
13641
+ },
13642
+ "boardroom.listProposals": async ({ params, respond }) => {
12525
13643
  const orgId = requireString(params, "orgId");
12526
13644
  if (!orgId) {
12527
13645
  respond(false, void 0, invalid("orgId is required"));
12528
13646
  return;
12529
13647
  }
12530
13648
  try {
12531
- if (!getOrganization(orgId)) {
12532
- respond(false, void 0, invalid("Organization not found"));
13649
+ respond(true, { proposals: listProposals(orgId, requireString(params, "status") ?? void 0) }, void 0);
13650
+ } catch (error) {
13651
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13652
+ }
13653
+ },
13654
+ "boardroom.getProposal": async ({ params, respond }) => {
13655
+ const proposalId = requireString(params, "proposalId");
13656
+ if (!proposalId) {
13657
+ respond(false, void 0, invalid("proposalId is required"));
13658
+ return;
13659
+ }
13660
+ try {
13661
+ const proposal = getProposal(proposalId);
13662
+ if (!proposal) {
13663
+ respond(false, void 0, invalid("Proposal not found"));
12533
13664
  return;
12534
13665
  }
12535
- respond(true, { hierarchy: buildHierarchy(orgId) }, void 0);
13666
+ respond(true, { proposal }, void 0);
12536
13667
  } catch (error) {
12537
13668
  respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
12538
13669
  }
@@ -13005,6 +14136,374 @@ const skillsHandlers = {
13005
14136
  }
13006
14137
  };
13007
14138
 
14139
+ //#endregion
14140
+ //#region src/gateway/server-methods/steer.ts
14141
+ const steerHandlers = {
14142
+ "steer.get": async ({ respond }) => {
14143
+ try {
14144
+ respond(true, { active: getSteer() }, void 0);
14145
+ } catch (error) {
14146
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14147
+ }
14148
+ },
14149
+ "steer.set": async ({ params, respond }) => {
14150
+ const text = typeof params.text === "string" ? params.text.trim() : "";
14151
+ if (!text) {
14152
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "text is required"));
14153
+ return;
14154
+ }
14155
+ const setBy = typeof params.setBy === "string" ? params.setBy.trim() : "user";
14156
+ try {
14157
+ const state = setSteer(text, setBy);
14158
+ respond(true, {
14159
+ active: state.active,
14160
+ updatedAt: state.updatedAt
14161
+ }, void 0);
14162
+ } catch (error) {
14163
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14164
+ }
14165
+ },
14166
+ "steer.clear": async ({ respond }) => {
14167
+ try {
14168
+ const state = clearSteer();
14169
+ respond(true, {
14170
+ active: state.active,
14171
+ updatedAt: state.updatedAt
14172
+ }, void 0);
14173
+ } catch (error) {
14174
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14175
+ }
14176
+ },
14177
+ "steer.history": async ({ respond }) => {
14178
+ try {
14179
+ respond(true, { history: getSteerHistory() }, void 0);
14180
+ } catch (error) {
14181
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14182
+ }
14183
+ }
14184
+ };
14185
+
14186
+ //#endregion
14187
+ //#region src/license/validator.ts
14188
+ createSubsystemLogger("license");
14189
+ function resolveLicensePath() {
14190
+ return path.join(resolveStateDir(), "license.json");
14191
+ }
14192
+ function loadLicense() {
14193
+ try {
14194
+ const filePath = resolveLicensePath();
14195
+ if (!fs.existsSync(filePath)) return null;
14196
+ const raw = fs.readFileSync(filePath, "utf8");
14197
+ return JSON.parse(raw);
14198
+ } catch {
14199
+ return null;
14200
+ }
14201
+ }
14202
+ function saveLicense(license) {
14203
+ const filePath = resolveLicensePath();
14204
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
14205
+ fs.writeFileSync(filePath, `${JSON.stringify(license, null, 2)}\n`, { mode: 384 });
14206
+ }
14207
+
14208
+ //#endregion
14209
+ //#region src/license/stripe-checkout.ts
14210
+ /**
14211
+ * Stripe Checkout Integration for NoxSoft Subscriptions
14212
+ *
14213
+ * Creates Stripe checkout sessions for NoxSoft license tiers.
14214
+ * Handles subscription lifecycle: create, upgrade, cancel.
14215
+ *
14216
+ * Configuration via environment:
14217
+ * STRIPE_SECRET_KEY — Stripe API secret key
14218
+ * STRIPE_WEBHOOK_SECRET — Stripe webhook signing secret
14219
+ * NOXSOFT_BASE_URL — Base URL for redirect (default: https://anima.noxsoft.net)
14220
+ *
14221
+ * Price IDs are configured per tier. In test mode, Stripe test keys work.
14222
+ */
14223
+ const log$4 = createSubsystemLogger("stripe-checkout");
14224
+ /** Default price IDs — set to null until Stripe dashboard is configured */
14225
+ const DEFAULT_PRICE_IDS = {
14226
+ community: null,
14227
+ noxsoft: null,
14228
+ hackathon: null,
14229
+ team: null,
14230
+ builder: null
14231
+ };
14232
+ function resolveStripeConfig() {
14233
+ const secretKey = process.env.STRIPE_SECRET_KEY?.trim();
14234
+ if (!secretKey) return null;
14235
+ return {
14236
+ secretKey,
14237
+ webhookSecret: process.env.STRIPE_WEBHOOK_SECRET?.trim() ?? "",
14238
+ baseUrl: process.env.NOXSOFT_BASE_URL?.trim() ?? "https://anima.noxsoft.net",
14239
+ priceIds: {
14240
+ ...DEFAULT_PRICE_IDS,
14241
+ ...process.env.STRIPE_PRICE_NOXSOFT ? { noxsoft: process.env.STRIPE_PRICE_NOXSOFT } : {},
14242
+ ...process.env.STRIPE_PRICE_TEAM ? { team: process.env.STRIPE_PRICE_TEAM } : {},
14243
+ ...process.env.STRIPE_PRICE_BUILDER ? { builder: process.env.STRIPE_PRICE_BUILDER } : {}
14244
+ }
14245
+ };
14246
+ }
14247
+ async function stripeRequest(secretKey, method, path, body) {
14248
+ const url = `https://api.stripe.com/v1${path}`;
14249
+ const headers = {
14250
+ Authorization: `Bearer ${secretKey}`,
14251
+ "Content-Type": "application/x-www-form-urlencoded"
14252
+ };
14253
+ const encodedBody = body ? Object.entries(body).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&") : void 0;
14254
+ const res = await fetch(url, {
14255
+ method,
14256
+ headers,
14257
+ body: encodedBody,
14258
+ signal: AbortSignal.timeout(15e3)
14259
+ });
14260
+ const data = await res.json();
14261
+ if (!res.ok) {
14262
+ const rawError = data.error?.message;
14263
+ const errMsg = typeof rawError === "string" ? rawError : res.statusText;
14264
+ throw new Error(`Stripe API error (${res.status}): ${errMsg}`);
14265
+ }
14266
+ return data;
14267
+ }
14268
+ /**
14269
+ * Create a Stripe Checkout session for a NoxSoft subscription.
14270
+ */
14271
+ async function createCheckoutSession(params) {
14272
+ const config = resolveStripeConfig();
14273
+ if (!config) throw new Error("Stripe not configured. Set STRIPE_SECRET_KEY environment variable.");
14274
+ const priceId = config.priceIds[params.tier];
14275
+ if (!priceId) {
14276
+ if (params.tier === "community" || params.tier === "hackathon") throw new Error(`${params.tier} tier is free — no checkout needed.`);
14277
+ throw new Error(`Stripe price ID not configured for tier "${params.tier}". Set STRIPE_PRICE_${params.tier.toUpperCase()} environment variable.`);
14278
+ }
14279
+ const body = {
14280
+ mode: "subscription",
14281
+ "line_items[0][price]": priceId,
14282
+ "line_items[0][quantity]": "1",
14283
+ success_url: `${config.baseUrl}/subscription/success?session_id={CHECKOUT_SESSION_ID}`,
14284
+ cancel_url: `${config.baseUrl}/subscription/cancel`,
14285
+ "metadata[agent_id]": params.agentId,
14286
+ "metadata[tier]": params.tier,
14287
+ "subscription_data[metadata][agent_id]": params.agentId,
14288
+ "subscription_data[metadata][tier]": params.tier
14289
+ };
14290
+ if (params.customerEmail) body["customer_email"] = params.customerEmail;
14291
+ const session = await stripeRequest(config.secretKey, "POST", "/checkout/sessions", body);
14292
+ log$4.info(`checkout session created: ${session.id} (tier: ${params.tier})`);
14293
+ return {
14294
+ sessionId: session.id,
14295
+ url: session.url
14296
+ };
14297
+ }
14298
+ /**
14299
+ * Verify a Stripe webhook signature.
14300
+ * Returns the parsed event if valid, null if invalid.
14301
+ */
14302
+ function verifyWebhookSignature(payload, signature, webhookSecret) {
14303
+ if (!webhookSecret) {
14304
+ log$4.warn("webhook secret not configured — skipping signature verification");
14305
+ try {
14306
+ return JSON.parse(payload);
14307
+ } catch {
14308
+ return null;
14309
+ }
14310
+ }
14311
+ const elements = signature.split(",");
14312
+ const timestamp = elements.find((e) => e.startsWith("t="))?.slice(2);
14313
+ const sig = elements.find((e) => e.startsWith("v1="))?.slice(3);
14314
+ if (!timestamp || !sig) {
14315
+ log$4.warn("invalid webhook signature format");
14316
+ return null;
14317
+ }
14318
+ if (sig !== createHmac("sha256", webhookSecret).update(`${timestamp}.${payload}`).digest("hex")) {
14319
+ log$4.warn("webhook signature mismatch");
14320
+ return null;
14321
+ }
14322
+ const eventTime = parseInt(timestamp, 10) * 1e3;
14323
+ if (Math.abs(Date.now() - eventTime) > 3e5) {
14324
+ log$4.warn("webhook timestamp too old");
14325
+ return null;
14326
+ }
14327
+ try {
14328
+ return JSON.parse(payload);
14329
+ } catch {
14330
+ return null;
14331
+ }
14332
+ }
14333
+ /**
14334
+ * Handle a verified Stripe webhook event.
14335
+ * Updates the local license based on subscription status.
14336
+ */
14337
+ async function handleWebhookEvent(event) {
14338
+ switch (event.type) {
14339
+ case "checkout.session.completed": {
14340
+ const session = event.data.object;
14341
+ const tier = session.metadata?.tier;
14342
+ const agentId = session.metadata?.agent_id;
14343
+ const subscriptionId = session.subscription;
14344
+ const customerId = session.customer;
14345
+ if (!tier || !agentId) {
14346
+ log$4.warn("checkout.session.completed missing metadata");
14347
+ return { handled: false };
14348
+ }
14349
+ saveLicense({
14350
+ tier,
14351
+ agentId,
14352
+ subscriptionId: subscriptionId ?? void 0,
14353
+ customerId: customerId ?? void 0,
14354
+ issuedAt: Date.now(),
14355
+ expiresAt: Date.now() + 720 * 60 * 60 * 1e3,
14356
+ signature: "",
14357
+ version: 1
14358
+ });
14359
+ log$4.info(`license activated: ${tier} for ${agentId} (sub: ${subscriptionId})`);
14360
+ return {
14361
+ handled: true,
14362
+ action: "license_activated"
14363
+ };
14364
+ }
14365
+ case "customer.subscription.updated": {
14366
+ const sub = event.data.object;
14367
+ const status = sub.status;
14368
+ const tier = sub.metadata?.tier;
14369
+ if (status === "active" && tier) {
14370
+ const existing = loadLicense();
14371
+ if (existing) {
14372
+ existing.tier = tier;
14373
+ existing.expiresAt = Date.now() + 720 * 60 * 60 * 1e3;
14374
+ saveLicense(existing);
14375
+ log$4.info(`subscription updated: ${tier}`);
14376
+ }
14377
+ }
14378
+ return {
14379
+ handled: true,
14380
+ action: "subscription_updated"
14381
+ };
14382
+ }
14383
+ case "customer.subscription.deleted": {
14384
+ const existing = loadLicense();
14385
+ if (existing) {
14386
+ existing.tier = "community";
14387
+ existing.expiresAt = Date.now() + 336 * 60 * 60 * 1e3;
14388
+ saveLicense(existing);
14389
+ log$4.info("subscription cancelled — downgraded to community with 14-day grace");
14390
+ }
14391
+ return {
14392
+ handled: true,
14393
+ action: "subscription_cancelled"
14394
+ };
14395
+ }
14396
+ case "invoice.payment_failed":
14397
+ log$4.warn("payment failed — license will expire at end of current period");
14398
+ return {
14399
+ handled: true,
14400
+ action: "payment_failed"
14401
+ };
14402
+ default: return { handled: false };
14403
+ }
14404
+ }
14405
+ /**
14406
+ * Create a Stripe Customer Portal session for managing subscriptions.
14407
+ */
14408
+ async function createPortalSession(customerId) {
14409
+ const config = resolveStripeConfig();
14410
+ if (!config) throw new Error("Stripe not configured.");
14411
+ return { url: (await stripeRequest(config.secretKey, "POST", "/billing_portal/sessions", {
14412
+ customer: customerId,
14413
+ return_url: `${config.baseUrl}/settings`
14414
+ })).url };
14415
+ }
14416
+ /**
14417
+ * Check if Stripe is configured and ready.
14418
+ */
14419
+ function isStripeConfigured() {
14420
+ return resolveStripeConfig() !== null;
14421
+ }
14422
+ /**
14423
+ * Get subscription status from Stripe.
14424
+ */
14425
+ async function getSubscriptionStatus(subscriptionId) {
14426
+ const config = resolveStripeConfig();
14427
+ if (!config) return null;
14428
+ try {
14429
+ const sub = await stripeRequest(config.secretKey, "GET", `/subscriptions/${subscriptionId}`);
14430
+ return {
14431
+ status: sub.status,
14432
+ currentPeriodEnd: sub.current_period_end * 1e3,
14433
+ cancelAtPeriodEnd: sub.cancel_at_period_end
14434
+ };
14435
+ } catch (err) {
14436
+ log$4.error(`failed to get subscription status: ${String(err)}`);
14437
+ return null;
14438
+ }
14439
+ }
14440
+
14441
+ //#endregion
14442
+ //#region src/gateway/server-methods/subscription.ts
14443
+ const subscriptionHandlers = {
14444
+ "subscription.status": async ({ respond }) => {
14445
+ try {
14446
+ const license = loadLicense();
14447
+ const stripeReady = isStripeConfigured();
14448
+ if (!license) {
14449
+ respond(true, {
14450
+ tier: "community",
14451
+ stripeConfigured: stripeReady,
14452
+ active: true,
14453
+ message: "Running on community (free) tier."
14454
+ }, void 0);
14455
+ return;
14456
+ }
14457
+ let stripeStatus = null;
14458
+ if (license.subscriptionId && stripeReady) stripeStatus = await getSubscriptionStatus(license.subscriptionId);
14459
+ respond(true, {
14460
+ tier: license.tier,
14461
+ active: !license.expiresAt || license.expiresAt > Date.now(),
14462
+ expiresAt: license.expiresAt,
14463
+ stripeConfigured: stripeReady,
14464
+ subscriptionId: license.subscriptionId,
14465
+ stripeStatus: stripeStatus?.status,
14466
+ cancelAtPeriodEnd: stripeStatus?.cancelAtPeriodEnd
14467
+ }, void 0);
14468
+ } catch (error) {
14469
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14470
+ }
14471
+ },
14472
+ "subscription.checkout": async ({ params, respond }) => {
14473
+ const tier = typeof params.tier === "string" ? params.tier.trim() : "";
14474
+ if (!tier) {
14475
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "tier is required"));
14476
+ return;
14477
+ }
14478
+ try {
14479
+ const result = await createCheckoutSession({
14480
+ tier,
14481
+ agentId: typeof params.agentId === "string" ? params.agentId : "default",
14482
+ customerEmail: typeof params.email === "string" ? params.email : void 0
14483
+ });
14484
+ respond(true, {
14485
+ sessionId: result.sessionId,
14486
+ checkoutUrl: result.url
14487
+ }, void 0);
14488
+ } catch (error) {
14489
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14490
+ }
14491
+ },
14492
+ "subscription.portal": async ({ params, respond }) => {
14493
+ try {
14494
+ const license = loadLicense();
14495
+ const customerId = typeof params.customerId === "string" ? params.customerId : license?.customerId;
14496
+ if (!customerId || typeof customerId !== "string") {
14497
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "No customer ID found. Purchase a subscription first."));
14498
+ return;
14499
+ }
14500
+ respond(true, { portalUrl: (await createPortalSession(customerId)).url }, void 0);
14501
+ } catch (error) {
14502
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14503
+ }
14504
+ }
14505
+ };
14506
+
13008
14507
  //#endregion
13009
14508
  //#region src/gateway/server-methods/system.ts
13010
14509
  const systemHandlers = {
@@ -14451,6 +15950,10 @@ const coreGatewayHandlers = {
14451
15950
  ...updateHandlers,
14452
15951
  ...nodeHandlers,
14453
15952
  ...orgHandlers,
15953
+ ...egoHandlers,
15954
+ ...steerHandlers,
15955
+ ...legacyHandlers,
15956
+ ...subscriptionHandlers,
14454
15957
  ...sendHandlers,
14455
15958
  ...usageHandlers,
14456
15959
  ...agentHandlers,
@@ -15925,7 +17428,7 @@ function normalizeBasePath(rawPath) {
15925
17428
  return normalized.replace(/\/+$/, "");
15926
17429
  }
15927
17430
  async function prepareCanvasRoot(rootDir) {
15928
- await ensureDir(rootDir);
17431
+ await ensureDir$1(rootDir);
15929
17432
  const rootReal = await fs$1.realpath(rootDir);
15930
17433
  try {
15931
17434
  const indexPath = path.join(rootReal, "index.html");
@@ -16447,6 +17950,89 @@ function setSseHeaders(res) {
16447
17950
  res.flushHeaders?.();
16448
17951
  }
16449
17952
 
17953
+ //#endregion
17954
+ //#region src/gateway/http-rate-limit.ts
17955
+ const log$3 = createSubsystemLogger("http-rate-limit");
17956
+ var HttpRateLimiter = class {
17957
+ constructor(config) {
17958
+ this.windows = /* @__PURE__ */ new Map();
17959
+ this.maxRequests = config?.maxRequests ?? 100;
17960
+ this.windowMs = config?.windowMs ?? 6e4;
17961
+ this.exemptLoopback = config?.exemptLoopback ?? true;
17962
+ const pruneInterval = config?.pruneIntervalMs ?? 3e5;
17963
+ this.pruneTimer = setInterval(() => this.prune(), pruneInterval);
17964
+ this.pruneTimer.unref?.();
17965
+ }
17966
+ /**
17967
+ * Check if a request from this IP is allowed.
17968
+ * Returns { allowed, remaining, retryAfterMs }.
17969
+ */
17970
+ check(ip) {
17971
+ if (this.exemptLoopback && isLoopbackAddress(ip)) return {
17972
+ allowed: true,
17973
+ remaining: this.maxRequests,
17974
+ retryAfterMs: 0
17975
+ };
17976
+ const now = Date.now();
17977
+ const windowStart = now - this.windowMs;
17978
+ let window = this.windows.get(ip);
17979
+ if (!window) {
17980
+ window = { timestamps: [] };
17981
+ this.windows.set(ip, window);
17982
+ }
17983
+ window.timestamps = window.timestamps.filter((t) => t > windowStart);
17984
+ if (window.timestamps.length >= this.maxRequests) {
17985
+ const retryAfterMs = window.timestamps[0] + this.windowMs - now;
17986
+ return {
17987
+ allowed: false,
17988
+ remaining: 0,
17989
+ retryAfterMs: Math.max(retryAfterMs, 1e3)
17990
+ };
17991
+ }
17992
+ window.timestamps.push(now);
17993
+ return {
17994
+ allowed: true,
17995
+ remaining: this.maxRequests - window.timestamps.length,
17996
+ retryAfterMs: 0
17997
+ };
17998
+ }
17999
+ /**
18000
+ * Remove stale entries to prevent unbounded memory growth.
18001
+ */
18002
+ prune() {
18003
+ const windowStart = Date.now() - this.windowMs;
18004
+ let pruned = 0;
18005
+ for (const [ip, window] of this.windows) {
18006
+ window.timestamps = window.timestamps.filter((t) => t > windowStart);
18007
+ if (window.timestamps.length === 0) {
18008
+ this.windows.delete(ip);
18009
+ pruned++;
18010
+ }
18011
+ }
18012
+ if (pruned > 0) log$3.debug(`pruned ${pruned} stale rate limit entries`);
18013
+ }
18014
+ /**
18015
+ * Stop the pruning timer.
18016
+ */
18017
+ stop() {
18018
+ if (this.pruneTimer) {
18019
+ clearInterval(this.pruneTimer);
18020
+ this.pruneTimer = void 0;
18021
+ }
18022
+ }
18023
+ /**
18024
+ * Get current tracked IP count (for monitoring).
18025
+ */
18026
+ getTrackedCount() {
18027
+ return this.windows.size;
18028
+ }
18029
+ };
18030
+ /** Default: 100 req/min per IP */
18031
+ const DEFAULT_RATE_LIMIT = {
18032
+ maxRequests: 100,
18033
+ windowMs: 6e4
18034
+ };
18035
+
16450
18036
  //#endregion
16451
18037
  //#region src/gateway/http-utils.ts
16452
18038
  function getHeader(req, name) {
@@ -18163,6 +19749,7 @@ function createHooksRequestHandler(opts) {
18163
19749
  }
18164
19750
  function createGatewayHttpServer(opts) {
18165
19751
  const { canvasHost, clients, controlUiEnabled, controlUiBasePath, controlUiRoot, openAiChatCompletionsEnabled, openResponsesEnabled, openResponsesConfig, handleHooksRequest, handlePluginRequest, resolvedAuth, rateLimiter } = opts;
19752
+ const httpRateLimiter = new HttpRateLimiter(DEFAULT_RATE_LIMIT);
18166
19753
  const httpServer = opts.tlsOptions ? createServer$1(opts.tlsOptions, (req, res) => {
18167
19754
  handleRequest(req, res);
18168
19755
  }) : createServer((req, res) => {
@@ -18170,10 +19757,30 @@ function createGatewayHttpServer(opts) {
18170
19757
  });
18171
19758
  async function handleRequest(req, res) {
18172
19759
  if (String(req.headers.upgrade ?? "").toLowerCase() === "websocket") return;
19760
+ const clientIp = resolveGatewayClientIp(req, []);
19761
+ const rateCheck = httpRateLimiter.check(clientIp);
19762
+ if (!rateCheck.allowed) {
19763
+ const retryAfterSec = Math.ceil(rateCheck.retryAfterMs / 1e3);
19764
+ res.writeHead(429, {
19765
+ "Content-Type": "application/json",
19766
+ "Retry-After": String(retryAfterSec),
19767
+ "X-RateLimit-Limit": String(DEFAULT_RATE_LIMIT.maxRequests ?? 100),
19768
+ "X-RateLimit-Remaining": "0"
19769
+ });
19770
+ res.end(JSON.stringify({
19771
+ error: "Too Many Requests",
19772
+ retryAfterMs: rateCheck.retryAfterMs
19773
+ }));
19774
+ return;
19775
+ }
18173
19776
  try {
18174
19777
  const configSnapshot = loadConfig();
18175
19778
  const trustedProxies = configSnapshot.gateway?.trustedProxies ?? [];
18176
19779
  const requestPath = new URL(req.url ?? "/", "http://localhost").pathname;
19780
+ if (requestPath === "/webhook/stripe" && req.method === "POST") {
19781
+ await handleStripeWebhook(req, res);
19782
+ return;
19783
+ }
18177
19784
  if (await handleHooksRequest(req, res)) return;
18178
19785
  if (await handleToolsInvokeHttpRequest(req, res, {
18179
19786
  auth: resolvedAuth,
@@ -18252,6 +19859,33 @@ function createGatewayHttpServer(opts) {
18252
19859
  res.end("Internal Server Error");
18253
19860
  }
18254
19861
  }
19862
+ async function handleStripeWebhook(req, res) {
19863
+ const config = resolveStripeConfig();
19864
+ if (!config) {
19865
+ res.writeHead(503, { "Content-Type": "application/json" });
19866
+ res.end(JSON.stringify({ error: "Stripe not configured" }));
19867
+ return;
19868
+ }
19869
+ const chunks = [];
19870
+ for await (const chunk of req) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
19871
+ const event = verifyWebhookSignature(Buffer.concat(chunks).toString("utf8"), String(req.headers["stripe-signature"] ?? ""), config.webhookSecret);
19872
+ if (!event) {
19873
+ res.writeHead(400, { "Content-Type": "application/json" });
19874
+ res.end(JSON.stringify({ error: "Invalid signature" }));
19875
+ return;
19876
+ }
19877
+ try {
19878
+ const result = await handleWebhookEvent(event);
19879
+ res.writeHead(200, { "Content-Type": "application/json" });
19880
+ res.end(JSON.stringify({
19881
+ received: true,
19882
+ ...result
19883
+ }));
19884
+ } catch (err) {
19885
+ res.writeHead(500, { "Content-Type": "application/json" });
19886
+ res.end(JSON.stringify({ error: String(err) }));
19887
+ }
19888
+ }
18255
19889
  return httpServer;
18256
19890
  }
18257
19891
  function attachGatewayUpgradeHandler(opts) {