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