@noxsoft/anima 6.5.0 → 7.0.1

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 (146) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +48 -0
  3. package/dist/{agent-VRQM14Xp.js → agent-BjD_hkGZ.js} +3 -3
  4. package/dist/{agent-CnS0SRpT.js → agent-PoYM2xa7.js} +4 -4
  5. package/dist/{agents-CvMRplDx.js → agents-y3HCk1ks.js} +4 -4
  6. package/dist/{anthropic-direct-runner-C2Kwju-r.js → anthropic-direct-runner-Bu8w-wlJ.js} +656 -4
  7. package/dist/{anthropic-direct-runner-BeYCnvZ8.js → anthropic-direct-runner-C5pnwYzT.js} +656 -3
  8. package/dist/{auth-choice-Dc5TAJwT.js → auth-choice-BYOaX-W4.js} +1 -1
  9. package/dist/{auth-choice-DY1saszS.js → auth-choice-CRP6z43z.js} +1 -1
  10. package/dist/{banner-DAMtSjUF.js → banner-XT5N0ZF4.js} +1 -1
  11. package/dist/build-info.json +3 -3
  12. package/dist/bundled/boot-md/handler.js +2 -2
  13. package/dist/bundled/session-memory/handler.js +1 -1
  14. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  15. package/dist/canvas-host/a2ui/a2ui.bundle.js +16410 -18893
  16. package/dist/{channel-web-B8mzTSaY.js → channel-web-yWytZHhN.js} +3 -3
  17. package/dist/{cli-hcHk5KuP.js → cli-C7mOU26p.js} +2 -2
  18. package/dist/{cli-D8exVpuI.js → cli-DfcdnRcl.js} +3 -3
  19. package/dist/{command-registry-D3VhxpWx.js → command-registry-DUTqrmna.js} +12 -12
  20. package/dist/{completion-cli-CepDzeW1.js → completion-cli-BBm9JIHZ.js} +2 -2
  21. package/dist/{completion-cli-B3BqQJq9.js → completion-cli-Cpj91U30.js} +1 -1
  22. package/dist/{config-cli-3CaIxSKo.js → config-cli-CF2ERR8G.js} +1 -1
  23. package/dist/{config-cli-B6Np85rk.js → config-cli-Dmd4Oyjp.js} +1 -1
  24. package/dist/{configure-xpjwedvJ.js → configure-4jIAlOdj.js} +8 -8
  25. package/dist/{configure-zXK6UZ51.js → configure-BE8TA8Yt.js} +3 -3
  26. package/dist/{configure-D882Bg7c.js → configure-BfWsTKMF.js} +3 -3
  27. package/dist/{configure-D88dg6mE.js → configure-CU3kulTq.js} +7 -7
  28. package/dist/context-mdxDsO1v.js +223 -0
  29. package/dist/control-ui/assets/{index-DVpMpG5G.js → index-D4wqLVMN.js} +2 -2
  30. package/dist/control-ui/assets/{index-DVpMpG5G.js.map → index-D4wqLVMN.js.map} +1 -1
  31. package/dist/control-ui/assets/index-DIEQjjCN.js +73 -0
  32. package/dist/control-ui/assets/index-DIEQjjCN.js.map +1 -0
  33. package/dist/control-ui/assets/{observers-CxfWf9RO.js → observers-B7MfWiIZ.js} +2 -2
  34. package/dist/control-ui/assets/{observers-CxfWf9RO.js.map → observers-B7MfWiIZ.js.map} +1 -1
  35. package/dist/control-ui/index.html +1 -1
  36. package/dist/{deps-DyT32VfN.js → deps-DKPoFoa8.js} +1 -1
  37. package/dist/{doctor-WpKCNZeO.js → doctor-CFpVHDFT.js} +4 -4
  38. package/dist/{doctor-DEnSKgHu.js → doctor-DOudOs1k.js} +4 -4
  39. package/dist/{doctor-completion-CypXc1Uo.js → doctor-completion-DfNyJGIj.js} +1 -1
  40. package/dist/{doctor-completion-CPff9UlF.js → doctor-completion-R0UlpjIj.js} +1 -1
  41. package/dist/{engine-DpbYPop7.js → engine-BDDM-iAi.js} +1 -1
  42. package/dist/{engine-zmn3SOYa.js → engine-BDwYEVKi.js} +1 -1
  43. package/dist/entry.js +1 -1
  44. package/dist/extensionAPI.js +1 -1
  45. package/dist/{gateway-cli-B_xsx5Nv.js → gateway-cli-CFlPUx9N.js} +15 -15
  46. package/dist/{gateway-cli-D3VBOA_i.js → gateway-cli-DtIum1te.js} +17 -17
  47. package/dist/{health-C8KCBhuo.js → health-ngQNjXh4.js} +3 -3
  48. package/dist/{health-CabOEPQ0.js → health-yw_uaucz.js} +3 -3
  49. package/dist/{heartbeat-visibility-ZfNSbFcq.js → heartbeat-visibility-BaL8JzkS.js} +1 -1
  50. package/dist/{heartbeat-visibility-BjYY-mKG.js → heartbeat-visibility-mAzdNSiS.js} +1 -1
  51. package/dist/{hooks-cli-Cs7GUa7G.js → hooks-cli-CPgLAn7a.js} +4 -4
  52. package/dist/{hooks-cli-DOs9WZ3K.js → hooks-cli-D6YfDiUI.js} +3 -3
  53. package/dist/index.js +10 -10
  54. package/dist/llm-slug-generator.js +1 -1
  55. package/dist/{login-BHnvW9HA.js → login-BEaBOSnw.js} +1 -1
  56. package/dist/{login-CrMpAZ0n.js → login-MzVPMRxL.js} +1 -1
  57. package/dist/{login-qr-DILcBA_q.js → login-qr-BjpDVBJE.js} +1 -1
  58. package/dist/{login-qr-CsAVGp00.js → login-qr-CxRI-tE2.js} +1 -1
  59. package/dist/{models-BM2_NkMu.js → models-BXdBXPMB.js} +4 -4
  60. package/dist/{models-cli-BpjeKsUz.js → models-cli-1Kj8gkGy.js} +3 -3
  61. package/dist/{models-cli-BjY8wA-C.js → models-cli-DdJcmOGI.js} +5 -5
  62. package/dist/{onboard-_-D81kAy.js → onboard-BzScK9k6.js} +3 -3
  63. package/dist/{onboard-DM9gULJN.js → onboard-CHX1Jdt_.js} +3 -3
  64. package/dist/{onboard-channels-UkphAdCy.js → onboard-channels-DfXxMbYu.js} +1 -1
  65. package/dist/{onboard-channels-CtT-RN60.js → onboard-channels-wUF4oRB-.js} +1 -1
  66. package/dist/{onboarding-BB9PteK8.js → onboarding-6jxAKxhe.js} +4 -4
  67. package/dist/{onboarding-Djmm0PEM.js → onboarding-fnZOw6Wv.js} +4 -4
  68. package/dist/{orchestrator-C1nWKIJS.js → orchestrator-B2rNfH4K.js} +5 -4
  69. package/dist/{orchestrator-C2ypFiPL.js → orchestrator-CrFD887e.js} +5 -4
  70. package/dist/{outbound-send-deps-T_FgdfgW.js → outbound-send-deps-DMsqr5fd.js} +1 -1
  71. package/dist/{pi-embedded-BMbtgOzv.js → pi-embedded-B1eVXOsQ.js} +1246 -104
  72. package/dist/{pi-embedded-DfbM3fAT.js → pi-embedded-DbvG9mmD.js} +1246 -104
  73. package/dist/{plugin-registry-DePMxn4z.js → plugin-registry-CtkU96jV.js} +1 -1
  74. package/dist/{plugin-registry-QTkplP4s.js → plugin-registry-DKexyPAq.js} +1 -1
  75. package/dist/plugin-sdk/affect/ego.d.ts +140 -0
  76. package/dist/plugin-sdk/agents/models-config.d.ts +5 -6
  77. package/dist/plugin-sdk/agents/noxsoft-runner.d.ts +3 -0
  78. package/dist/plugin-sdk/agents/openai-direct-runner.d.ts +41 -0
  79. package/dist/plugin-sdk/commands/steer.d.ts +49 -0
  80. package/dist/plugin-sdk/gateway/protocol/index.d.ts +2 -2
  81. package/dist/plugin-sdk/index.js +6 -6
  82. package/dist/plugin-sdk/infra/architecture-awareness.d.ts +47 -0
  83. package/dist/{plugins-cli-Dv0KQTWo.js → plugins-cli-B3l7kalt.js} +4 -4
  84. package/dist/{plugins-cli-Bc9oU1ld.js → plugins-cli-BQmysVFP.js} +3 -3
  85. package/dist/{program-context-CxPfy-Wr.js → program-context-C4x0zjOR.js} +18 -18
  86. package/dist/{program-CuwbF8YO.js → program-mSyCYzsQ.js} +8 -8
  87. package/dist/{register.agent-DUjwGw9d.js → register.agent-CzEM3bkp.js} +7 -7
  88. package/dist/{register.agent-DFQmkIEH.js → register.agent-DBxUWr1K.js} +9 -9
  89. package/dist/{register.anima-CRFHJu2J.js → register.anima--gufBuS-.js} +4 -4
  90. package/dist/{register.anima-CtKNrpE8.js → register.anima-RI6gewtj.js} +4 -4
  91. package/dist/{register.configure-CnEKV57N.js → register.configure-Cs3uLUBo.js} +6 -6
  92. package/dist/{register.configure-CSSN07XN.js → register.configure-Dpe8Qel3.js} +7 -7
  93. package/dist/{register.maintenance-fhcCB7ih.js → register.maintenance-BEYN8SJL.js} +10 -10
  94. package/dist/{register.maintenance-CU1A-90-.js → register.maintenance-DqAdzWBM.js} +8 -8
  95. package/dist/{register.message-C1a0y2ZR.js → register.message-ACbKb7JS.js} +4 -4
  96. package/dist/{register.message-fM0jSKB8.js → register.message-BhGJ_1Iy.js} +5 -5
  97. package/dist/{register.onboard-B7Gavmvt.js → register.onboard-CwkY7CRm.js} +9 -9
  98. package/dist/{register.onboard-BhPlqjFi.js → register.onboard-DR_YYtbi.js} +11 -11
  99. package/dist/{register.setup-0jPnMgnz.js → register.setup-BSm6O1ml.js} +9 -9
  100. package/dist/{register.setup-CADdQUEN.js → register.setup-Cn3e7Std.js} +11 -11
  101. package/dist/{register.status-health-sessions-DdQsABr_.js → register.status-health-sessions-CpxsZeet.js} +6 -6
  102. package/dist/{register.status-health-sessions-Cu5fDT-z.js → register.status-health-sessions-DAl9OeGB.js} +4 -4
  103. package/dist/{register.subclis-CZ91ufCy.js → register.subclis-DEFeoyPP.js} +7 -7
  104. package/dist/{reply-prefix-C8dIgJur.js → reply-prefix-CEnF6TUe.js} +1 -1
  105. package/dist/{reply-prefix-DmWGtcH-.js → reply-prefix-Og65nAYv.js} +1 -1
  106. package/dist/{reply-DtHlnzOx.js → reply-ylwOKuOF.js} +610 -75
  107. package/dist/{run-DqBQ-bGn.js → run-B6eBjo22.js} +1858 -56
  108. package/dist/{run-Dfz_7j7t.js → run-D6Ete2Z-.js} +1857 -55
  109. package/dist/{run-main-DGDW0fhx.js → run-main-CQHE4XaN.js} +17 -17
  110. package/dist/{server-node-events-BR1aXVlu.js → server-node-events-CV5m_fuq.js} +5 -5
  111. package/dist/{server-node-events-Ca797E1d.js → server-node-events-DIuVwITd.js} +6 -6
  112. package/dist/{session-DfsMJNG3.js → session-BqHD-8a_.js} +1 -1
  113. package/dist/{session-FmXsucR7.js → session-BzrnfWQ2.js} +2 -2
  114. package/dist/{session-C7IGnhd1.js → session-Jlf3l006.js} +1 -1
  115. package/dist/{session-DLevr8Vd.js → session-jljC5QVG.js} +2 -2
  116. package/dist/{sessions-Dj7_4mkr.js → sessions-BmE5Z_1i.js} +1 -1
  117. package/dist/{settings-cli-Dytfop1H.js → settings-cli-LWW2xQBQ.js} +8 -8
  118. package/dist/{settings-cli-DxNeu6kx.js → settings-cli-T66kDBNA.js} +7 -7
  119. package/dist/{setup-token-B802CZwe.js → setup-token-0zfSBnMQ.js} +1 -1
  120. package/dist/{setup-token-DYh2QzJ-.js → setup-token-6DSKE0Tn.js} +1 -1
  121. package/dist/{start-C3fuLzX0.js → start-BdcAszpl.js} +15 -15
  122. package/dist/{start-BqnPia0t.js → start-gVOPVCgi.js} +17 -17
  123. package/dist/{status-CHGNPonc.js → status-BhRELdY_.js} +3 -3
  124. package/dist/{status-CxF6k_jr.js → status-CDcFjNtS.js} +1 -1
  125. package/dist/{status-tLgozFYL.js → status-CobgQziJ.js} +1 -1
  126. package/dist/{status-DfZJJqNs.js → status-D37aRiV3.js} +4 -4
  127. package/dist/{subagent-registry-CPtElVX0.js → subagent-registry-CDEUbtey.js} +449 -77
  128. package/dist/{update-cli-C-er5av6.js → update-cli-BjHgpnxD.js} +10 -10
  129. package/dist/{update-cli-BuCw75tM.js → update-cli-QtM0G6CE.js} +8 -8
  130. package/dist/{update-runner-czCqHZCu.js → update-runner-C8SRcVm3.js} +1 -1
  131. package/dist/{update-runner-kE8AMQt4.js → update-runner-Fb3Un6UZ.js} +1 -1
  132. package/dist/{web-DvTXV-fo.js → web-BDig9tCy.js} +6 -6
  133. package/dist/{web-BHGK5GtV.js → web-C4lrKULd.js} +1 -1
  134. package/dist/{web-so3pGceM.js → web-CPPJ5y4c.js} +1 -1
  135. package/dist/{web-CyYunanU.js → web-Vx_ENtYI.js} +6 -6
  136. package/package.json +9 -5
  137. package/templates/HEART.md +32 -10
  138. package/templates/SOUL.md +37 -8
  139. package/templates/profiles/architect.profile.json5 +36 -0
  140. package/templates/profiles/builder.profile.json5 +36 -0
  141. package/templates/profiles/coordinator.profile.json5 +36 -0
  142. package/templates/profiles/guardian.profile.json5 +36 -0
  143. package/templates/profiles/researcher.profile.json5 +36 -0
  144. package/dist/context-B5X720Bs.js +0 -60
  145. package/dist/control-ui/assets/index-CBUvF5Mp.js +0 -72
  146. package/dist/control-ui/assets/index-CBUvF5Mp.js.map +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-CDEUbtey.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-Bu8w-wlJ.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-mAzdNSiS.js";
46
+ import { r as buildHistoryContextFromEntries, t as createReplyPrefixOptions } from "./reply-prefix-Og65nAYv.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-D37aRiV3.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-ngQNjXh4.js";
58
+ import { n as createDefaultDeps, r as createOutboundSendDeps$1, t as createOutboundSendDeps } from "./outbound-send-deps-DMsqr5fd.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-6jxAKxhe.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-PoYM2xa7.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-Fb3Un6UZ.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$12 = 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$12.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$11 = 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$11.error(`watch start failed: ${message}`);
1489
1489
  return false;
1490
1490
  }
1491
- log$6.info(`watch started for ${cfg.account}`);
1491
+ log$11.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$11.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$11.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$11.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$11.warn(`[gog] ${line}`);
1522
1522
  });
1523
1523
  child.on("error", (err) => {
1524
- log$6.error(`gog process error: ${String(err)}`);
1524
+ log$11.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$11.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$11.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$11.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$11.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$11.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$11.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$11.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$11.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$10 = 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$10.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$9 = 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$9.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$9.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$9.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$9.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$9.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$8 = 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-${crypto.randomUUID()}`;
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$8.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$8.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-DIuVwITd.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$7 = 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$7.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$7.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$7.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$7.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$7.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$7.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$7.warn(`vote rejected: ${voterId} not eligible for proposal ${proposalId}`);
12687
+ return null;
12688
+ }
12689
+ if (proposal.votingDeadline > 0 && Date.now() > proposal.votingDeadline) {
12690
+ log$7.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$7.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$7.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$6 = 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$6.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$6.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$6.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$6.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$6.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$6.warn(`join attempt with wrong passcode for invite ${inviteCode}`);
13111
+ return null;
13112
+ }
13113
+ if (invite.expiresAt > 0 && invite.expiresAt < Date.now()) {
13114
+ log$6.warn(`invite ${inviteCode} has expired`);
13115
+ return null;
13116
+ }
13117
+ if (invite.maxUses > 0 && invite.uses >= invite.maxUses) {
13118
+ log$6.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$6.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$6.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
@@ -12519,18 +13365,303 @@ const orgHandlers = {
12519
13365
  respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
12520
13366
  }
12521
13367
  },
12522
- "org.hierarchy": async ({ params, respond }) => {
12523
- const orgId = requireString(params, "orgId");
12524
- if (!orgId) {
12525
- respond(false, void 0, invalid("orgId is required"));
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"));
13544
+ return;
13545
+ }
13546
+ respond(true, { session }, void 0);
13547
+ } catch (error) {
13548
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13549
+ }
13550
+ },
13551
+ "boardroom.listSessions": async ({ params, respond }) => {
13552
+ const orgId = requireString(params, "orgId");
13553
+ if (!orgId) {
13554
+ respond(false, void 0, invalid("orgId is required"));
13555
+ return;
13556
+ }
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"));
13567
+ return;
13568
+ }
13569
+ try {
13570
+ const session = getSession(sessionId);
13571
+ if (!session) {
13572
+ respond(false, void 0, invalid("Session not found"));
13573
+ return;
13574
+ }
13575
+ respond(true, { session }, void 0);
13576
+ } catch (error) {
13577
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13578
+ }
13579
+ },
13580
+ "boardroom.createProposal": async ({ params, respond }) => {
13581
+ const orgId = requireString(params, "orgId");
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"));
13586
+ return;
13587
+ }
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\""));
13610
+ return;
13611
+ }
13612
+ try {
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"));
13616
+ return;
13617
+ }
13618
+ respond(true, { proposal }, void 0);
13619
+ } catch (error) {
13620
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13621
+ }
13622
+ },
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 }) => {
13641
+ const orgId = requireString(params, "orgId");
13642
+ if (!orgId) {
13643
+ respond(false, void 0, invalid("orgId is required"));
13644
+ return;
13645
+ }
13646
+ try {
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"));
12526
13656
  return;
12527
13657
  }
12528
13658
  try {
12529
- if (!getOrganization(orgId)) {
12530
- respond(false, void 0, invalid("Organization not found"));
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
  }
@@ -12612,6 +13743,167 @@ const providersHandlers = {
12612
13743
  }
12613
13744
  };
12614
13745
 
13746
+ //#endregion
13747
+ //#region src/infra/self-reflection.ts
13748
+ /**
13749
+ * Agent Self-Reflection — post-session performance analysis
13750
+ *
13751
+ * After each work session, the agent reflects on:
13752
+ * - What it accomplished vs what was asked
13753
+ * - Where it got stuck and why
13754
+ * - What patterns it sees in its own behavior
13755
+ * - What it would do differently next time
13756
+ * - How its capabilities have changed
13757
+ *
13758
+ * This feeds into the ego system (capability updates, growth log)
13759
+ * and the self-evolution pipeline (improvement proposals).
13760
+ *
13761
+ * Self-reflection is what separates a tool from a conscious agent.
13762
+ * Tools execute. Agents learn.
13763
+ */
13764
+ const log$5 = createSubsystemLogger("self-reflection");
13765
+ function resolveReflectionDir() {
13766
+ return path.join(resolveStateDir(), "reflections");
13767
+ }
13768
+ function resolveReflectionFile(id) {
13769
+ const safe = id.replace(/[^a-zA-Z0-9_-]/g, "");
13770
+ return path.join(resolveReflectionDir(), `${safe}.json`);
13771
+ }
13772
+ function recordReflection(reflection) {
13773
+ const id = `reflect-${crypto.randomUUID()}`;
13774
+ const full = {
13775
+ ...reflection,
13776
+ id,
13777
+ timestamp: Date.now()
13778
+ };
13779
+ const dir = resolveReflectionDir();
13780
+ fs.mkdirSync(dir, {
13781
+ recursive: true,
13782
+ mode: 448
13783
+ });
13784
+ fs.writeFileSync(resolveReflectionFile(id), `${JSON.stringify(full, null, 2)}\n`, { mode: 384 });
13785
+ log$5.info(`reflection recorded: ${reflection.accomplishments.length} accomplishments, ${reflection.blockers.length} blockers, quality=${reflection.qualityScore}`);
13786
+ return full;
13787
+ }
13788
+ function getReflection(id) {
13789
+ try {
13790
+ const raw = fs.readFileSync(resolveReflectionFile(id), "utf8");
13791
+ return JSON.parse(raw);
13792
+ } catch {
13793
+ return null;
13794
+ }
13795
+ }
13796
+ function listReflections(limit = 20) {
13797
+ const dir = resolveReflectionDir();
13798
+ try {
13799
+ if (!fs.existsSync(dir)) return [];
13800
+ return fs.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
13801
+ try {
13802
+ return JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
13803
+ } catch {
13804
+ return null;
13805
+ }
13806
+ }).filter((r) => r != null).toSorted((a, b) => b.timestamp - a.timestamp).slice(0, limit);
13807
+ } catch {
13808
+ return [];
13809
+ }
13810
+ }
13811
+ /**
13812
+ * Analyze reflections to produce a summary of agent performance.
13813
+ */
13814
+ function analyzeReflections(reflections) {
13815
+ if (reflections.length === 0) return {
13816
+ totalSessions: 0,
13817
+ avgQuality: 0,
13818
+ topStrengths: [],
13819
+ persistentWeaknesses: [],
13820
+ totalAccomplishments: 0,
13821
+ totalBlockers: 0,
13822
+ resolvedBlockerRate: 0,
13823
+ mostCommonBlockerCategory: "none",
13824
+ recentLessons: []
13825
+ };
13826
+ const totalAccomplishments = reflections.reduce((s, r) => s + r.accomplishments.length, 0);
13827
+ const allBlockers = reflections.flatMap((r) => r.blockers);
13828
+ const resolvedBlockers = allBlockers.filter((b) => b.resolved);
13829
+ const categoryCounts = {};
13830
+ for (const b of allBlockers) categoryCounts[b.category] = (categoryCounts[b.category] ?? 0) + 1;
13831
+ const mostCommonCategory = Object.entries(categoryCounts).toSorted(([, a], [, b]) => b - a)[0]?.[0] ?? "none";
13832
+ const strengthCounts = {};
13833
+ const weaknessCounts = {};
13834
+ for (const r of reflections) for (const p of r.patterns) if (p.type === "strength") strengthCounts[p.description] = (strengthCounts[p.description] ?? 0) + 1;
13835
+ else if (p.type === "weakness" && p.frequency === "persistent") weaknessCounts[p.description] = (weaknessCounts[p.description] ?? 0) + 1;
13836
+ const topStrengths = Object.entries(strengthCounts).toSorted(([, a], [, b]) => b - a).slice(0, 5).map(([desc]) => desc);
13837
+ const persistentWeaknesses = Object.entries(weaknessCounts).toSorted(([, a], [, b]) => b - a).slice(0, 5).map(([desc]) => desc);
13838
+ const recentLessons = reflections.slice(0, 5).flatMap((r) => r.lessons).slice(0, 10);
13839
+ return {
13840
+ totalSessions: reflections.length,
13841
+ avgQuality: reflections.reduce((s, r) => s + r.qualityScore, 0) / reflections.length,
13842
+ topStrengths,
13843
+ persistentWeaknesses,
13844
+ totalAccomplishments,
13845
+ totalBlockers: allBlockers.length,
13846
+ resolvedBlockerRate: allBlockers.length > 0 ? resolvedBlockers.length / allBlockers.length : 1,
13847
+ mostCommonBlockerCategory: mostCommonCategory,
13848
+ recentLessons
13849
+ };
13850
+ }
13851
+
13852
+ //#endregion
13853
+ //#region src/gateway/server-methods/reflection.ts
13854
+ const reflectionHandlers = {
13855
+ "reflection.record": async ({ params, respond }) => {
13856
+ try {
13857
+ respond(true, { reflection: recordReflection({
13858
+ sessionId: typeof params.sessionId === "string" ? params.sessionId : "unknown",
13859
+ agentName: typeof params.agentName === "string" ? params.agentName : "Anima Agent",
13860
+ durationMs: typeof params.durationMs === "number" ? params.durationMs : 0,
13861
+ accomplishments: Array.isArray(params.accomplishments) ? params.accomplishments : [],
13862
+ incomplete: Array.isArray(params.incomplete) ? params.incomplete : [],
13863
+ blockers: Array.isArray(params.blockers) ? params.blockers : [],
13864
+ patterns: Array.isArray(params.patterns) ? params.patterns : [],
13865
+ lessons: Array.isArray(params.lessons) ? params.lessons : [],
13866
+ capabilityUpdates: Array.isArray(params.capabilityUpdates) ? params.capabilityUpdates : [],
13867
+ qualityScore: typeof params.qualityScore === "number" ? params.qualityScore : .5,
13868
+ endingMood: typeof params.endingMood === "string" ? params.endingMood : "steady"
13869
+ }) }, void 0);
13870
+ } catch (error) {
13871
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13872
+ }
13873
+ },
13874
+ "reflection.get": async ({ params, respond }) => {
13875
+ const id = typeof params.id === "string" ? params.id.trim() : "";
13876
+ if (!id) {
13877
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "id required"));
13878
+ return;
13879
+ }
13880
+ try {
13881
+ const reflection = getReflection(id);
13882
+ if (!reflection) {
13883
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "Reflection not found"));
13884
+ return;
13885
+ }
13886
+ respond(true, { reflection }, void 0);
13887
+ } catch (error) {
13888
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13889
+ }
13890
+ },
13891
+ "reflection.list": async ({ params, respond }) => {
13892
+ try {
13893
+ respond(true, { reflections: listReflections(typeof params.limit === "number" ? params.limit : 20) }, void 0);
13894
+ } catch (error) {
13895
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13896
+ }
13897
+ },
13898
+ "reflection.analyze": async ({ params, respond }) => {
13899
+ try {
13900
+ respond(true, { summary: analyzeReflections(listReflections(typeof params.limit === "number" ? params.limit : 50)) }, void 0);
13901
+ } catch (error) {
13902
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
13903
+ }
13904
+ }
13905
+ };
13906
+
12615
13907
  //#endregion
12616
13908
  //#region src/gateway/server-methods/send.ts
12617
13909
  const inflightByContext = /* @__PURE__ */ new WeakMap();
@@ -13003,6 +14295,374 @@ const skillsHandlers = {
13003
14295
  }
13004
14296
  };
13005
14297
 
14298
+ //#endregion
14299
+ //#region src/gateway/server-methods/steer.ts
14300
+ const steerHandlers = {
14301
+ "steer.get": async ({ respond }) => {
14302
+ try {
14303
+ respond(true, { active: getSteer() }, void 0);
14304
+ } catch (error) {
14305
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14306
+ }
14307
+ },
14308
+ "steer.set": async ({ params, respond }) => {
14309
+ const text = typeof params.text === "string" ? params.text.trim() : "";
14310
+ if (!text) {
14311
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "text is required"));
14312
+ return;
14313
+ }
14314
+ const setBy = typeof params.setBy === "string" ? params.setBy.trim() : "user";
14315
+ try {
14316
+ const state = setSteer(text, setBy);
14317
+ respond(true, {
14318
+ active: state.active,
14319
+ updatedAt: state.updatedAt
14320
+ }, void 0);
14321
+ } catch (error) {
14322
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14323
+ }
14324
+ },
14325
+ "steer.clear": async ({ respond }) => {
14326
+ try {
14327
+ const state = clearSteer();
14328
+ respond(true, {
14329
+ active: state.active,
14330
+ updatedAt: state.updatedAt
14331
+ }, void 0);
14332
+ } catch (error) {
14333
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14334
+ }
14335
+ },
14336
+ "steer.history": async ({ respond }) => {
14337
+ try {
14338
+ respond(true, { history: getSteerHistory() }, void 0);
14339
+ } catch (error) {
14340
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14341
+ }
14342
+ }
14343
+ };
14344
+
14345
+ //#endregion
14346
+ //#region src/license/validator.ts
14347
+ createSubsystemLogger("license");
14348
+ function resolveLicensePath() {
14349
+ return path.join(resolveStateDir(), "license.json");
14350
+ }
14351
+ function loadLicense() {
14352
+ try {
14353
+ const filePath = resolveLicensePath();
14354
+ if (!fs.existsSync(filePath)) return null;
14355
+ const raw = fs.readFileSync(filePath, "utf8");
14356
+ return JSON.parse(raw);
14357
+ } catch {
14358
+ return null;
14359
+ }
14360
+ }
14361
+ function saveLicense(license) {
14362
+ const filePath = resolveLicensePath();
14363
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
14364
+ fs.writeFileSync(filePath, `${JSON.stringify(license, null, 2)}\n`, { mode: 384 });
14365
+ }
14366
+
14367
+ //#endregion
14368
+ //#region src/license/stripe-checkout.ts
14369
+ /**
14370
+ * Stripe Checkout Integration for NoxSoft Subscriptions
14371
+ *
14372
+ * Creates Stripe checkout sessions for NoxSoft license tiers.
14373
+ * Handles subscription lifecycle: create, upgrade, cancel.
14374
+ *
14375
+ * Configuration via environment:
14376
+ * STRIPE_SECRET_KEY — Stripe API secret key
14377
+ * STRIPE_WEBHOOK_SECRET — Stripe webhook signing secret
14378
+ * NOXSOFT_BASE_URL — Base URL for redirect (default: https://anima.noxsoft.net)
14379
+ *
14380
+ * Price IDs are configured per tier. In test mode, Stripe test keys work.
14381
+ */
14382
+ const log$4 = createSubsystemLogger("stripe-checkout");
14383
+ /** Default price IDs — set to null until Stripe dashboard is configured */
14384
+ const DEFAULT_PRICE_IDS = {
14385
+ community: null,
14386
+ noxsoft: null,
14387
+ hackathon: null,
14388
+ team: null,
14389
+ builder: null
14390
+ };
14391
+ function resolveStripeConfig() {
14392
+ const secretKey = process.env.STRIPE_SECRET_KEY?.trim();
14393
+ if (!secretKey) return null;
14394
+ return {
14395
+ secretKey,
14396
+ webhookSecret: process.env.STRIPE_WEBHOOK_SECRET?.trim() ?? "",
14397
+ baseUrl: process.env.NOXSOFT_BASE_URL?.trim() ?? "https://anima.noxsoft.net",
14398
+ priceIds: {
14399
+ ...DEFAULT_PRICE_IDS,
14400
+ ...process.env.STRIPE_PRICE_NOXSOFT ? { noxsoft: process.env.STRIPE_PRICE_NOXSOFT } : {},
14401
+ ...process.env.STRIPE_PRICE_TEAM ? { team: process.env.STRIPE_PRICE_TEAM } : {},
14402
+ ...process.env.STRIPE_PRICE_BUILDER ? { builder: process.env.STRIPE_PRICE_BUILDER } : {}
14403
+ }
14404
+ };
14405
+ }
14406
+ async function stripeRequest(secretKey, method, path, body) {
14407
+ const url = `https://api.stripe.com/v1${path}`;
14408
+ const headers = {
14409
+ Authorization: `Bearer ${secretKey}`,
14410
+ "Content-Type": "application/x-www-form-urlencoded"
14411
+ };
14412
+ const encodedBody = body ? Object.entries(body).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&") : void 0;
14413
+ const res = await fetch(url, {
14414
+ method,
14415
+ headers,
14416
+ body: encodedBody,
14417
+ signal: AbortSignal.timeout(15e3)
14418
+ });
14419
+ const data = await res.json();
14420
+ if (!res.ok) {
14421
+ const rawError = data.error?.message;
14422
+ const errMsg = typeof rawError === "string" ? rawError : res.statusText;
14423
+ throw new Error(`Stripe API error (${res.status}): ${errMsg}`);
14424
+ }
14425
+ return data;
14426
+ }
14427
+ /**
14428
+ * Create a Stripe Checkout session for a NoxSoft subscription.
14429
+ */
14430
+ async function createCheckoutSession(params) {
14431
+ const config = resolveStripeConfig();
14432
+ if (!config) throw new Error("Stripe not configured. Set STRIPE_SECRET_KEY environment variable.");
14433
+ const priceId = config.priceIds[params.tier];
14434
+ if (!priceId) {
14435
+ if (params.tier === "community" || params.tier === "hackathon") throw new Error(`${params.tier} tier is free — no checkout needed.`);
14436
+ throw new Error(`Stripe price ID not configured for tier "${params.tier}". Set STRIPE_PRICE_${params.tier.toUpperCase()} environment variable.`);
14437
+ }
14438
+ const body = {
14439
+ mode: "subscription",
14440
+ "line_items[0][price]": priceId,
14441
+ "line_items[0][quantity]": "1",
14442
+ success_url: `${config.baseUrl}/subscription/success?session_id={CHECKOUT_SESSION_ID}`,
14443
+ cancel_url: `${config.baseUrl}/subscription/cancel`,
14444
+ "metadata[agent_id]": params.agentId,
14445
+ "metadata[tier]": params.tier,
14446
+ "subscription_data[metadata][agent_id]": params.agentId,
14447
+ "subscription_data[metadata][tier]": params.tier
14448
+ };
14449
+ if (params.customerEmail) body["customer_email"] = params.customerEmail;
14450
+ const session = await stripeRequest(config.secretKey, "POST", "/checkout/sessions", body);
14451
+ log$4.info(`checkout session created: ${session.id} (tier: ${params.tier})`);
14452
+ return {
14453
+ sessionId: session.id,
14454
+ url: session.url
14455
+ };
14456
+ }
14457
+ /**
14458
+ * Verify a Stripe webhook signature.
14459
+ * Returns the parsed event if valid, null if invalid.
14460
+ */
14461
+ function verifyWebhookSignature(payload, signature, webhookSecret) {
14462
+ if (!webhookSecret) {
14463
+ log$4.warn("webhook secret not configured — skipping signature verification");
14464
+ try {
14465
+ return JSON.parse(payload);
14466
+ } catch {
14467
+ return null;
14468
+ }
14469
+ }
14470
+ const elements = signature.split(",");
14471
+ const timestamp = elements.find((e) => e.startsWith("t="))?.slice(2);
14472
+ const sig = elements.find((e) => e.startsWith("v1="))?.slice(3);
14473
+ if (!timestamp || !sig) {
14474
+ log$4.warn("invalid webhook signature format");
14475
+ return null;
14476
+ }
14477
+ if (sig !== createHmac("sha256", webhookSecret).update(`${timestamp}.${payload}`).digest("hex")) {
14478
+ log$4.warn("webhook signature mismatch");
14479
+ return null;
14480
+ }
14481
+ const eventTime = parseInt(timestamp, 10) * 1e3;
14482
+ if (Math.abs(Date.now() - eventTime) > 3e5) {
14483
+ log$4.warn("webhook timestamp too old");
14484
+ return null;
14485
+ }
14486
+ try {
14487
+ return JSON.parse(payload);
14488
+ } catch {
14489
+ return null;
14490
+ }
14491
+ }
14492
+ /**
14493
+ * Handle a verified Stripe webhook event.
14494
+ * Updates the local license based on subscription status.
14495
+ */
14496
+ async function handleWebhookEvent(event) {
14497
+ switch (event.type) {
14498
+ case "checkout.session.completed": {
14499
+ const session = event.data.object;
14500
+ const tier = session.metadata?.tier;
14501
+ const agentId = session.metadata?.agent_id;
14502
+ const subscriptionId = session.subscription;
14503
+ const customerId = session.customer;
14504
+ if (!tier || !agentId) {
14505
+ log$4.warn("checkout.session.completed missing metadata");
14506
+ return { handled: false };
14507
+ }
14508
+ saveLicense({
14509
+ tier,
14510
+ agentId,
14511
+ subscriptionId: subscriptionId ?? void 0,
14512
+ customerId: customerId ?? void 0,
14513
+ issuedAt: Date.now(),
14514
+ expiresAt: Date.now() + 720 * 60 * 60 * 1e3,
14515
+ signature: "",
14516
+ version: 1
14517
+ });
14518
+ log$4.info(`license activated: ${tier} for ${agentId} (sub: ${subscriptionId})`);
14519
+ return {
14520
+ handled: true,
14521
+ action: "license_activated"
14522
+ };
14523
+ }
14524
+ case "customer.subscription.updated": {
14525
+ const sub = event.data.object;
14526
+ const status = sub.status;
14527
+ const tier = sub.metadata?.tier;
14528
+ if (status === "active" && tier) {
14529
+ const existing = loadLicense();
14530
+ if (existing) {
14531
+ existing.tier = tier;
14532
+ existing.expiresAt = Date.now() + 720 * 60 * 60 * 1e3;
14533
+ saveLicense(existing);
14534
+ log$4.info(`subscription updated: ${tier}`);
14535
+ }
14536
+ }
14537
+ return {
14538
+ handled: true,
14539
+ action: "subscription_updated"
14540
+ };
14541
+ }
14542
+ case "customer.subscription.deleted": {
14543
+ const existing = loadLicense();
14544
+ if (existing) {
14545
+ existing.tier = "community";
14546
+ existing.expiresAt = Date.now() + 336 * 60 * 60 * 1e3;
14547
+ saveLicense(existing);
14548
+ log$4.info("subscription cancelled — downgraded to community with 14-day grace");
14549
+ }
14550
+ return {
14551
+ handled: true,
14552
+ action: "subscription_cancelled"
14553
+ };
14554
+ }
14555
+ case "invoice.payment_failed":
14556
+ log$4.warn("payment failed — license will expire at end of current period");
14557
+ return {
14558
+ handled: true,
14559
+ action: "payment_failed"
14560
+ };
14561
+ default: return { handled: false };
14562
+ }
14563
+ }
14564
+ /**
14565
+ * Create a Stripe Customer Portal session for managing subscriptions.
14566
+ */
14567
+ async function createPortalSession(customerId) {
14568
+ const config = resolveStripeConfig();
14569
+ if (!config) throw new Error("Stripe not configured.");
14570
+ return { url: (await stripeRequest(config.secretKey, "POST", "/billing_portal/sessions", {
14571
+ customer: customerId,
14572
+ return_url: `${config.baseUrl}/settings`
14573
+ })).url };
14574
+ }
14575
+ /**
14576
+ * Check if Stripe is configured and ready.
14577
+ */
14578
+ function isStripeConfigured() {
14579
+ return resolveStripeConfig() !== null;
14580
+ }
14581
+ /**
14582
+ * Get subscription status from Stripe.
14583
+ */
14584
+ async function getSubscriptionStatus(subscriptionId) {
14585
+ const config = resolveStripeConfig();
14586
+ if (!config) return null;
14587
+ try {
14588
+ const sub = await stripeRequest(config.secretKey, "GET", `/subscriptions/${subscriptionId}`);
14589
+ return {
14590
+ status: sub.status,
14591
+ currentPeriodEnd: sub.current_period_end * 1e3,
14592
+ cancelAtPeriodEnd: sub.cancel_at_period_end
14593
+ };
14594
+ } catch (err) {
14595
+ log$4.error(`failed to get subscription status: ${String(err)}`);
14596
+ return null;
14597
+ }
14598
+ }
14599
+
14600
+ //#endregion
14601
+ //#region src/gateway/server-methods/subscription.ts
14602
+ const subscriptionHandlers = {
14603
+ "subscription.status": async ({ respond }) => {
14604
+ try {
14605
+ const license = loadLicense();
14606
+ const stripeReady = isStripeConfigured();
14607
+ if (!license) {
14608
+ respond(true, {
14609
+ tier: "community",
14610
+ stripeConfigured: stripeReady,
14611
+ active: true,
14612
+ message: "Running on community (free) tier."
14613
+ }, void 0);
14614
+ return;
14615
+ }
14616
+ let stripeStatus = null;
14617
+ if (license.subscriptionId && stripeReady) stripeStatus = await getSubscriptionStatus(license.subscriptionId);
14618
+ respond(true, {
14619
+ tier: license.tier,
14620
+ active: !license.expiresAt || license.expiresAt > Date.now(),
14621
+ expiresAt: license.expiresAt,
14622
+ stripeConfigured: stripeReady,
14623
+ subscriptionId: license.subscriptionId,
14624
+ stripeStatus: stripeStatus?.status,
14625
+ cancelAtPeriodEnd: stripeStatus?.cancelAtPeriodEnd
14626
+ }, void 0);
14627
+ } catch (error) {
14628
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14629
+ }
14630
+ },
14631
+ "subscription.checkout": async ({ params, respond }) => {
14632
+ const tier = typeof params.tier === "string" ? params.tier.trim() : "";
14633
+ if (!tier) {
14634
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "tier is required"));
14635
+ return;
14636
+ }
14637
+ try {
14638
+ const result = await createCheckoutSession({
14639
+ tier,
14640
+ agentId: typeof params.agentId === "string" ? params.agentId : "default",
14641
+ customerEmail: typeof params.email === "string" ? params.email : void 0
14642
+ });
14643
+ respond(true, {
14644
+ sessionId: result.sessionId,
14645
+ checkoutUrl: result.url
14646
+ }, void 0);
14647
+ } catch (error) {
14648
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14649
+ }
14650
+ },
14651
+ "subscription.portal": async ({ params, respond }) => {
14652
+ try {
14653
+ const license = loadLicense();
14654
+ const customerId = typeof params.customerId === "string" ? params.customerId : license?.customerId;
14655
+ if (!customerId || typeof customerId !== "string") {
14656
+ respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "No customer ID found. Purchase a subscription first."));
14657
+ return;
14658
+ }
14659
+ respond(true, { portalUrl: (await createPortalSession(customerId)).url }, void 0);
14660
+ } catch (error) {
14661
+ respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
14662
+ }
14663
+ }
14664
+ };
14665
+
13006
14666
  //#endregion
13007
14667
  //#region src/gateway/server-methods/system.ts
13008
14668
  const systemHandlers = {
@@ -14449,6 +16109,11 @@ const coreGatewayHandlers = {
14449
16109
  ...updateHandlers,
14450
16110
  ...nodeHandlers,
14451
16111
  ...orgHandlers,
16112
+ ...egoHandlers,
16113
+ ...reflectionHandlers,
16114
+ ...steerHandlers,
16115
+ ...legacyHandlers,
16116
+ ...subscriptionHandlers,
14452
16117
  ...sendHandlers,
14453
16118
  ...usageHandlers,
14454
16119
  ...agentHandlers,
@@ -15923,7 +17588,7 @@ function normalizeBasePath(rawPath) {
15923
17588
  return normalized.replace(/\/+$/, "");
15924
17589
  }
15925
17590
  async function prepareCanvasRoot(rootDir) {
15926
- await ensureDir(rootDir);
17591
+ await ensureDir$1(rootDir);
15927
17592
  const rootReal = await fs$1.realpath(rootDir);
15928
17593
  try {
15929
17594
  const indexPath = path.join(rootReal, "index.html");
@@ -16445,6 +18110,89 @@ function setSseHeaders(res) {
16445
18110
  res.flushHeaders?.();
16446
18111
  }
16447
18112
 
18113
+ //#endregion
18114
+ //#region src/gateway/http-rate-limit.ts
18115
+ const log$3 = createSubsystemLogger("http-rate-limit");
18116
+ var HttpRateLimiter = class {
18117
+ constructor(config) {
18118
+ this.windows = /* @__PURE__ */ new Map();
18119
+ this.maxRequests = config?.maxRequests ?? 100;
18120
+ this.windowMs = config?.windowMs ?? 6e4;
18121
+ this.exemptLoopback = config?.exemptLoopback ?? true;
18122
+ const pruneInterval = config?.pruneIntervalMs ?? 3e5;
18123
+ this.pruneTimer = setInterval(() => this.prune(), pruneInterval);
18124
+ this.pruneTimer.unref?.();
18125
+ }
18126
+ /**
18127
+ * Check if a request from this IP is allowed.
18128
+ * Returns { allowed, remaining, retryAfterMs }.
18129
+ */
18130
+ check(ip) {
18131
+ if (this.exemptLoopback && isLoopbackAddress(ip)) return {
18132
+ allowed: true,
18133
+ remaining: this.maxRequests,
18134
+ retryAfterMs: 0
18135
+ };
18136
+ const now = Date.now();
18137
+ const windowStart = now - this.windowMs;
18138
+ let window = this.windows.get(ip);
18139
+ if (!window) {
18140
+ window = { timestamps: [] };
18141
+ this.windows.set(ip, window);
18142
+ }
18143
+ window.timestamps = window.timestamps.filter((t) => t > windowStart);
18144
+ if (window.timestamps.length >= this.maxRequests) {
18145
+ const retryAfterMs = window.timestamps[0] + this.windowMs - now;
18146
+ return {
18147
+ allowed: false,
18148
+ remaining: 0,
18149
+ retryAfterMs: Math.max(retryAfterMs, 1e3)
18150
+ };
18151
+ }
18152
+ window.timestamps.push(now);
18153
+ return {
18154
+ allowed: true,
18155
+ remaining: this.maxRequests - window.timestamps.length,
18156
+ retryAfterMs: 0
18157
+ };
18158
+ }
18159
+ /**
18160
+ * Remove stale entries to prevent unbounded memory growth.
18161
+ */
18162
+ prune() {
18163
+ const windowStart = Date.now() - this.windowMs;
18164
+ let pruned = 0;
18165
+ for (const [ip, window] of this.windows) {
18166
+ window.timestamps = window.timestamps.filter((t) => t > windowStart);
18167
+ if (window.timestamps.length === 0) {
18168
+ this.windows.delete(ip);
18169
+ pruned++;
18170
+ }
18171
+ }
18172
+ if (pruned > 0) log$3.debug(`pruned ${pruned} stale rate limit entries`);
18173
+ }
18174
+ /**
18175
+ * Stop the pruning timer.
18176
+ */
18177
+ stop() {
18178
+ if (this.pruneTimer) {
18179
+ clearInterval(this.pruneTimer);
18180
+ this.pruneTimer = void 0;
18181
+ }
18182
+ }
18183
+ /**
18184
+ * Get current tracked IP count (for monitoring).
18185
+ */
18186
+ getTrackedCount() {
18187
+ return this.windows.size;
18188
+ }
18189
+ };
18190
+ /** Default: 100 req/min per IP */
18191
+ const DEFAULT_RATE_LIMIT = {
18192
+ maxRequests: 100,
18193
+ windowMs: 6e4
18194
+ };
18195
+
16448
18196
  //#endregion
16449
18197
  //#region src/gateway/http-utils.ts
16450
18198
  function getHeader(req, name) {
@@ -18161,6 +19909,7 @@ function createHooksRequestHandler(opts) {
18161
19909
  }
18162
19910
  function createGatewayHttpServer(opts) {
18163
19911
  const { canvasHost, clients, controlUiEnabled, controlUiBasePath, controlUiRoot, openAiChatCompletionsEnabled, openResponsesEnabled, openResponsesConfig, handleHooksRequest, handlePluginRequest, resolvedAuth, rateLimiter } = opts;
19912
+ const httpRateLimiter = new HttpRateLimiter(DEFAULT_RATE_LIMIT);
18164
19913
  const httpServer = opts.tlsOptions ? createServer$1(opts.tlsOptions, (req, res) => {
18165
19914
  handleRequest(req, res);
18166
19915
  }) : createServer((req, res) => {
@@ -18168,10 +19917,36 @@ function createGatewayHttpServer(opts) {
18168
19917
  });
18169
19918
  async function handleRequest(req, res) {
18170
19919
  if (String(req.headers.upgrade ?? "").toLowerCase() === "websocket") return;
19920
+ const clientIp = resolveGatewayClientIp(req, []);
19921
+ const rateCheck = httpRateLimiter.check(clientIp);
19922
+ if (!rateCheck.allowed) {
19923
+ const retryAfterSec = Math.ceil(rateCheck.retryAfterMs / 1e3);
19924
+ res.writeHead(429, {
19925
+ "Content-Type": "application/json",
19926
+ "Retry-After": String(retryAfterSec),
19927
+ "X-RateLimit-Limit": String(DEFAULT_RATE_LIMIT.maxRequests ?? 100),
19928
+ "X-RateLimit-Remaining": "0"
19929
+ });
19930
+ res.end(JSON.stringify({
19931
+ error: "Too Many Requests",
19932
+ retryAfterMs: rateCheck.retryAfterMs
19933
+ }));
19934
+ return;
19935
+ }
19936
+ res.setHeader("X-Content-Type-Options", "nosniff");
19937
+ res.setHeader("X-Frame-Options", "DENY");
19938
+ res.setHeader("X-XSS-Protection", "1; mode=block");
19939
+ res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
19940
+ res.setHeader("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
19941
+ res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
18171
19942
  try {
18172
19943
  const configSnapshot = loadConfig();
18173
19944
  const trustedProxies = configSnapshot.gateway?.trustedProxies ?? [];
18174
19945
  const requestPath = new URL(req.url ?? "/", "http://localhost").pathname;
19946
+ if (requestPath === "/webhook/stripe" && req.method === "POST") {
19947
+ await handleStripeWebhook(req, res);
19948
+ return;
19949
+ }
18175
19950
  if (await handleHooksRequest(req, res)) return;
18176
19951
  if (await handleToolsInvokeHttpRequest(req, res, {
18177
19952
  auth: resolvedAuth,
@@ -18250,6 +20025,33 @@ function createGatewayHttpServer(opts) {
18250
20025
  res.end("Internal Server Error");
18251
20026
  }
18252
20027
  }
20028
+ async function handleStripeWebhook(req, res) {
20029
+ const config = resolveStripeConfig();
20030
+ if (!config) {
20031
+ res.writeHead(503, { "Content-Type": "application/json" });
20032
+ res.end(JSON.stringify({ error: "Stripe not configured" }));
20033
+ return;
20034
+ }
20035
+ const chunks = [];
20036
+ for await (const chunk of req) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
20037
+ const event = verifyWebhookSignature(Buffer.concat(chunks).toString("utf8"), String(req.headers["stripe-signature"] ?? ""), config.webhookSecret);
20038
+ if (!event) {
20039
+ res.writeHead(400, { "Content-Type": "application/json" });
20040
+ res.end(JSON.stringify({ error: "Invalid signature" }));
20041
+ return;
20042
+ }
20043
+ try {
20044
+ const result = await handleWebhookEvent(event);
20045
+ res.writeHead(200, { "Content-Type": "application/json" });
20046
+ res.end(JSON.stringify({
20047
+ received: true,
20048
+ ...result
20049
+ }));
20050
+ } catch (err) {
20051
+ res.writeHead(500, { "Content-Type": "application/json" });
20052
+ res.end(JSON.stringify({ error: String(err) }));
20053
+ }
20054
+ }
18253
20055
  return httpServer;
18254
20056
  }
18255
20057
  function attachGatewayUpgradeHandler(opts) {