@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.
- package/CHANGELOG.md +59 -0
- package/README.md +48 -0
- package/dist/{agent-VRQM14Xp.js → agent-BjD_hkGZ.js} +3 -3
- package/dist/{agent-CnS0SRpT.js → agent-PoYM2xa7.js} +4 -4
- package/dist/{agents-CvMRplDx.js → agents-y3HCk1ks.js} +4 -4
- package/dist/{anthropic-direct-runner-C2Kwju-r.js → anthropic-direct-runner-Bu8w-wlJ.js} +656 -4
- package/dist/{anthropic-direct-runner-BeYCnvZ8.js → anthropic-direct-runner-C5pnwYzT.js} +656 -3
- package/dist/{auth-choice-Dc5TAJwT.js → auth-choice-BYOaX-W4.js} +1 -1
- package/dist/{auth-choice-DY1saszS.js → auth-choice-CRP6z43z.js} +1 -1
- package/dist/{banner-DAMtSjUF.js → banner-XT5N0ZF4.js} +1 -1
- package/dist/build-info.json +3 -3
- package/dist/bundled/boot-md/handler.js +2 -2
- package/dist/bundled/session-memory/handler.js +1 -1
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/canvas-host/a2ui/a2ui.bundle.js +16410 -18893
- package/dist/{channel-web-B8mzTSaY.js → channel-web-yWytZHhN.js} +3 -3
- package/dist/{cli-hcHk5KuP.js → cli-C7mOU26p.js} +2 -2
- package/dist/{cli-D8exVpuI.js → cli-DfcdnRcl.js} +3 -3
- package/dist/{command-registry-D3VhxpWx.js → command-registry-DUTqrmna.js} +12 -12
- package/dist/{completion-cli-CepDzeW1.js → completion-cli-BBm9JIHZ.js} +2 -2
- package/dist/{completion-cli-B3BqQJq9.js → completion-cli-Cpj91U30.js} +1 -1
- package/dist/{config-cli-3CaIxSKo.js → config-cli-CF2ERR8G.js} +1 -1
- package/dist/{config-cli-B6Np85rk.js → config-cli-Dmd4Oyjp.js} +1 -1
- package/dist/{configure-xpjwedvJ.js → configure-4jIAlOdj.js} +8 -8
- package/dist/{configure-zXK6UZ51.js → configure-BE8TA8Yt.js} +3 -3
- package/dist/{configure-D882Bg7c.js → configure-BfWsTKMF.js} +3 -3
- package/dist/{configure-D88dg6mE.js → configure-CU3kulTq.js} +7 -7
- package/dist/context-mdxDsO1v.js +223 -0
- package/dist/control-ui/assets/{index-DVpMpG5G.js → index-D4wqLVMN.js} +2 -2
- package/dist/control-ui/assets/{index-DVpMpG5G.js.map → index-D4wqLVMN.js.map} +1 -1
- package/dist/control-ui/assets/index-DIEQjjCN.js +73 -0
- package/dist/control-ui/assets/index-DIEQjjCN.js.map +1 -0
- package/dist/control-ui/assets/{observers-CxfWf9RO.js → observers-B7MfWiIZ.js} +2 -2
- package/dist/control-ui/assets/{observers-CxfWf9RO.js.map → observers-B7MfWiIZ.js.map} +1 -1
- package/dist/control-ui/index.html +1 -1
- package/dist/{deps-DyT32VfN.js → deps-DKPoFoa8.js} +1 -1
- package/dist/{doctor-WpKCNZeO.js → doctor-CFpVHDFT.js} +4 -4
- package/dist/{doctor-DEnSKgHu.js → doctor-DOudOs1k.js} +4 -4
- package/dist/{doctor-completion-CypXc1Uo.js → doctor-completion-DfNyJGIj.js} +1 -1
- package/dist/{doctor-completion-CPff9UlF.js → doctor-completion-R0UlpjIj.js} +1 -1
- package/dist/{engine-DpbYPop7.js → engine-BDDM-iAi.js} +1 -1
- package/dist/{engine-zmn3SOYa.js → engine-BDwYEVKi.js} +1 -1
- package/dist/entry.js +1 -1
- package/dist/extensionAPI.js +1 -1
- package/dist/{gateway-cli-B_xsx5Nv.js → gateway-cli-CFlPUx9N.js} +15 -15
- package/dist/{gateway-cli-D3VBOA_i.js → gateway-cli-DtIum1te.js} +17 -17
- package/dist/{health-C8KCBhuo.js → health-ngQNjXh4.js} +3 -3
- package/dist/{health-CabOEPQ0.js → health-yw_uaucz.js} +3 -3
- package/dist/{heartbeat-visibility-ZfNSbFcq.js → heartbeat-visibility-BaL8JzkS.js} +1 -1
- package/dist/{heartbeat-visibility-BjYY-mKG.js → heartbeat-visibility-mAzdNSiS.js} +1 -1
- package/dist/{hooks-cli-Cs7GUa7G.js → hooks-cli-CPgLAn7a.js} +4 -4
- package/dist/{hooks-cli-DOs9WZ3K.js → hooks-cli-D6YfDiUI.js} +3 -3
- package/dist/index.js +10 -10
- package/dist/llm-slug-generator.js +1 -1
- package/dist/{login-BHnvW9HA.js → login-BEaBOSnw.js} +1 -1
- package/dist/{login-CrMpAZ0n.js → login-MzVPMRxL.js} +1 -1
- package/dist/{login-qr-DILcBA_q.js → login-qr-BjpDVBJE.js} +1 -1
- package/dist/{login-qr-CsAVGp00.js → login-qr-CxRI-tE2.js} +1 -1
- package/dist/{models-BM2_NkMu.js → models-BXdBXPMB.js} +4 -4
- package/dist/{models-cli-BpjeKsUz.js → models-cli-1Kj8gkGy.js} +3 -3
- package/dist/{models-cli-BjY8wA-C.js → models-cli-DdJcmOGI.js} +5 -5
- package/dist/{onboard-_-D81kAy.js → onboard-BzScK9k6.js} +3 -3
- package/dist/{onboard-DM9gULJN.js → onboard-CHX1Jdt_.js} +3 -3
- package/dist/{onboard-channels-UkphAdCy.js → onboard-channels-DfXxMbYu.js} +1 -1
- package/dist/{onboard-channels-CtT-RN60.js → onboard-channels-wUF4oRB-.js} +1 -1
- package/dist/{onboarding-BB9PteK8.js → onboarding-6jxAKxhe.js} +4 -4
- package/dist/{onboarding-Djmm0PEM.js → onboarding-fnZOw6Wv.js} +4 -4
- package/dist/{orchestrator-C1nWKIJS.js → orchestrator-B2rNfH4K.js} +5 -4
- package/dist/{orchestrator-C2ypFiPL.js → orchestrator-CrFD887e.js} +5 -4
- package/dist/{outbound-send-deps-T_FgdfgW.js → outbound-send-deps-DMsqr5fd.js} +1 -1
- package/dist/{pi-embedded-BMbtgOzv.js → pi-embedded-B1eVXOsQ.js} +1246 -104
- package/dist/{pi-embedded-DfbM3fAT.js → pi-embedded-DbvG9mmD.js} +1246 -104
- package/dist/{plugin-registry-DePMxn4z.js → plugin-registry-CtkU96jV.js} +1 -1
- package/dist/{plugin-registry-QTkplP4s.js → plugin-registry-DKexyPAq.js} +1 -1
- package/dist/plugin-sdk/affect/ego.d.ts +140 -0
- package/dist/plugin-sdk/agents/models-config.d.ts +5 -6
- package/dist/plugin-sdk/agents/noxsoft-runner.d.ts +3 -0
- package/dist/plugin-sdk/agents/openai-direct-runner.d.ts +41 -0
- package/dist/plugin-sdk/commands/steer.d.ts +49 -0
- package/dist/plugin-sdk/gateway/protocol/index.d.ts +2 -2
- package/dist/plugin-sdk/index.js +6 -6
- package/dist/plugin-sdk/infra/architecture-awareness.d.ts +47 -0
- package/dist/{plugins-cli-Dv0KQTWo.js → plugins-cli-B3l7kalt.js} +4 -4
- package/dist/{plugins-cli-Bc9oU1ld.js → plugins-cli-BQmysVFP.js} +3 -3
- package/dist/{program-context-CxPfy-Wr.js → program-context-C4x0zjOR.js} +18 -18
- package/dist/{program-CuwbF8YO.js → program-mSyCYzsQ.js} +8 -8
- package/dist/{register.agent-DUjwGw9d.js → register.agent-CzEM3bkp.js} +7 -7
- package/dist/{register.agent-DFQmkIEH.js → register.agent-DBxUWr1K.js} +9 -9
- package/dist/{register.anima-CRFHJu2J.js → register.anima--gufBuS-.js} +4 -4
- package/dist/{register.anima-CtKNrpE8.js → register.anima-RI6gewtj.js} +4 -4
- package/dist/{register.configure-CnEKV57N.js → register.configure-Cs3uLUBo.js} +6 -6
- package/dist/{register.configure-CSSN07XN.js → register.configure-Dpe8Qel3.js} +7 -7
- package/dist/{register.maintenance-fhcCB7ih.js → register.maintenance-BEYN8SJL.js} +10 -10
- package/dist/{register.maintenance-CU1A-90-.js → register.maintenance-DqAdzWBM.js} +8 -8
- package/dist/{register.message-C1a0y2ZR.js → register.message-ACbKb7JS.js} +4 -4
- package/dist/{register.message-fM0jSKB8.js → register.message-BhGJ_1Iy.js} +5 -5
- package/dist/{register.onboard-B7Gavmvt.js → register.onboard-CwkY7CRm.js} +9 -9
- package/dist/{register.onboard-BhPlqjFi.js → register.onboard-DR_YYtbi.js} +11 -11
- package/dist/{register.setup-0jPnMgnz.js → register.setup-BSm6O1ml.js} +9 -9
- package/dist/{register.setup-CADdQUEN.js → register.setup-Cn3e7Std.js} +11 -11
- package/dist/{register.status-health-sessions-DdQsABr_.js → register.status-health-sessions-CpxsZeet.js} +6 -6
- package/dist/{register.status-health-sessions-Cu5fDT-z.js → register.status-health-sessions-DAl9OeGB.js} +4 -4
- package/dist/{register.subclis-CZ91ufCy.js → register.subclis-DEFeoyPP.js} +7 -7
- package/dist/{reply-prefix-C8dIgJur.js → reply-prefix-CEnF6TUe.js} +1 -1
- package/dist/{reply-prefix-DmWGtcH-.js → reply-prefix-Og65nAYv.js} +1 -1
- package/dist/{reply-DtHlnzOx.js → reply-ylwOKuOF.js} +610 -75
- package/dist/{run-DqBQ-bGn.js → run-B6eBjo22.js} +1858 -56
- package/dist/{run-Dfz_7j7t.js → run-D6Ete2Z-.js} +1857 -55
- package/dist/{run-main-DGDW0fhx.js → run-main-CQHE4XaN.js} +17 -17
- package/dist/{server-node-events-BR1aXVlu.js → server-node-events-CV5m_fuq.js} +5 -5
- package/dist/{server-node-events-Ca797E1d.js → server-node-events-DIuVwITd.js} +6 -6
- package/dist/{session-DfsMJNG3.js → session-BqHD-8a_.js} +1 -1
- package/dist/{session-FmXsucR7.js → session-BzrnfWQ2.js} +2 -2
- package/dist/{session-C7IGnhd1.js → session-Jlf3l006.js} +1 -1
- package/dist/{session-DLevr8Vd.js → session-jljC5QVG.js} +2 -2
- package/dist/{sessions-Dj7_4mkr.js → sessions-BmE5Z_1i.js} +1 -1
- package/dist/{settings-cli-Dytfop1H.js → settings-cli-LWW2xQBQ.js} +8 -8
- package/dist/{settings-cli-DxNeu6kx.js → settings-cli-T66kDBNA.js} +7 -7
- package/dist/{setup-token-B802CZwe.js → setup-token-0zfSBnMQ.js} +1 -1
- package/dist/{setup-token-DYh2QzJ-.js → setup-token-6DSKE0Tn.js} +1 -1
- package/dist/{start-C3fuLzX0.js → start-BdcAszpl.js} +15 -15
- package/dist/{start-BqnPia0t.js → start-gVOPVCgi.js} +17 -17
- package/dist/{status-CHGNPonc.js → status-BhRELdY_.js} +3 -3
- package/dist/{status-CxF6k_jr.js → status-CDcFjNtS.js} +1 -1
- package/dist/{status-tLgozFYL.js → status-CobgQziJ.js} +1 -1
- package/dist/{status-DfZJJqNs.js → status-D37aRiV3.js} +4 -4
- package/dist/{subagent-registry-CPtElVX0.js → subagent-registry-CDEUbtey.js} +449 -77
- package/dist/{update-cli-C-er5av6.js → update-cli-BjHgpnxD.js} +10 -10
- package/dist/{update-cli-BuCw75tM.js → update-cli-QtM0G6CE.js} +8 -8
- package/dist/{update-runner-czCqHZCu.js → update-runner-C8SRcVm3.js} +1 -1
- package/dist/{update-runner-kE8AMQt4.js → update-runner-Fb3Un6UZ.js} +1 -1
- package/dist/{web-DvTXV-fo.js → web-BDig9tCy.js} +6 -6
- package/dist/{web-BHGK5GtV.js → web-C4lrKULd.js} +1 -1
- package/dist/{web-so3pGceM.js → web-CPPJ5y4c.js} +1 -1
- package/dist/{web-CyYunanU.js → web-Vx_ENtYI.js} +6 -6
- package/package.json +9 -5
- package/templates/HEART.md +32 -10
- package/templates/SOUL.md +37 -8
- package/templates/profiles/architect.profile.json5 +36 -0
- package/templates/profiles/builder.profile.json5 +36 -0
- package/templates/profiles/coordinator.profile.json5 +36 -0
- package/templates/profiles/guardian.profile.json5 +36 -0
- package/templates/profiles/researcher.profile.json5 +36 -0
- package/dist/context-B5X720Bs.js +0 -60
- package/dist/control-ui/assets/index-CBUvF5Mp.js +0 -72
- package/dist/control-ui/assets/index-CBUvF5Mp.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { h as expandHomePrefix, i as isNixMode, l as resolveGatewayLockDir, m as resolveStateDir, o as resolveConfigPath, r as STATE_DIR, t as CONFIG_PATH, u as resolveGatewayPort } from "./paths-zhVksOvm.js";
|
|
2
2
|
import { E as getActivePluginRegistry, F as setVerbose, G as setLoggerOverride, H as getChildLogger, U as getLogger, W as getResolvedLoggerSettings, d as defaultRuntime, g as CHANNEL_IDS, i as getResolvedConsoleSettings, n as runtimeForLogger, o as setConsoleSubsystemFilter, s as setConsoleTimestampPrefix, t as createSubsystemLogger, v as DEFAULT_CHAT_CHANNEL } from "./subsystem-BAADN1B8.js";
|
|
3
|
-
import { C as shortenHomePath, D as truncateUtf16Safe, b as resolveUserPath, c as ensureDir, d as isPlainObject, r as clamp, t as CONFIG_DIR } from "./utils-CLYlhJuc.js";
|
|
3
|
+
import { C as shortenHomePath, D as truncateUtf16Safe, b as resolveUserPath, c as ensureDir$1, d as isPlainObject, r as clamp, t as CONFIG_DIR } from "./utils-CLYlhJuc.js";
|
|
4
4
|
import { C as supportsXHighThinking, _ as normalizeElevatedLevel, b as normalizeUsageDisplay, m as formatXHighModelHint, p as formatThinkingLevels, v as normalizeReasoningLevel, x as normalizeVerboseLevel, y as normalizeThinkLevel } from "./pi-embedded-helpers-BZ9GspxK.js";
|
|
5
|
-
import { $ as archiveFileOnDisk, At as deferGatewayRestartUntilIdle, B as sniffMimeFromBase64, C as verifyNodeToken, Cn as stopDiagnosticHeartbeat, Cr as DEFAULT_INPUT_PDF_MAX_PIXELS, Ct as loadProviderStore, D as registerSkillsChangeListener, Dr as extractImageContentFromSource, E as getSkillsSnapshotVersion, Er as extractFileContentFromSource, F as abortEmbeddedPiRun, Ft as setGatewaySigusr1RestartPolicy, G as canonicalizeSpawnedByForAgent, Gn as CommandLane, Gt as normalizeCronJobPatch, Hn as readLatestAssistantReply, I as waitForEmbeddedPiRunEnd, It as setPreRestartDeferralCheck, J as loadCombinedSessionStoreForGateway, Jn as getAgentRunContext, Jt as normalizeOptionalText, K as listAgentsForGateway, Kn as clearAgentRunContext, Kt as inferLegacyName, L as runNoxSoftEmbeddedAgent, Ln as countActiveDescendantRuns, Lt as consumeRestartSentinel, M as resetAllLanes, Mn as isAbortTrigger, Mr as resolveAgentTimeoutMs, Mt as isGatewaySigusr1RestartExternallyAllowed, N as setCommandLaneConcurrency, Nn as stopSubagentsForRequester, Nt as markGatewaySigusr1RestartHandled, O as clearSessionQueues, Or as normalizeMimeList, P as waitForActiveTasks, Pt as scheduleGatewaySigusr1Restart, Q as resolveSessionModelRef, R as applyToolPolicyPipeline, Rn as initSubagentRegistry, Rt as formatDoctorNonInteractiveHint, S as updatePairedNodeMetadata, Sn as startDiagnosticHeartbeat, Sr as DEFAULT_INPUT_PDF_MAX_PAGES, St as loadProviderUsageSummary, T as verifyPairingToken, Tr as DEFAULT_INPUT_TIMEOUT_MS, Tt as saveProviderStore, U as createAnimaTools, Ut as writeRestartSentinel, V as requestHeartbeatNow, Vn as runSubagentAnnounceFlow, Vt as summarizeRestartSentinel, W as resolveAnnounceTargetFromKey, Wt as normalizeCronJobCreate, X as pruneLegacyStoreKeys, Xn as registerAgentRunContext, Xt as normalizeRequiredName, Y as loadSessionEntry, Yn as onAgentEvent, Yt as normalizePayloadToSystemText, Z as resolveGatewaySessionStoreTarget, Zn as resolveAgentIdentity, Zt as migrateLegacyCronPayload, _ as approveNodePairing, _n as dispatchInboundMessage, _r as DEFAULT_INPUT_FILE_MAX_CHARS, _t as resetDirectoryCache, an as persistBrowserProxyFiles, ar as applyVerboseOverride, at as stripEnvelopeFromMessages, b as renamePairedNode, br as DEFAULT_INPUT_IMAGE_MIMES, bt as runWithModelFallback, c as normalizeSendPolicy, cr as isSystemEventContextChanged, d as primeRemoteSkillsCache, dr as loadModelCatalog, dt as resolveOutboundSessionRoute, en as buildSafeExternalPrompt, et as archiveSessionTranscripts, f as recordRemoteNodeInfo, g as setSkillsRemoteRegistry, gr as DEFAULT_INPUT_FILE_MAX_BYTES, h as removeRemoteNodeInfo, ht as resolveSessionDeliveryTarget, i as setCliSessionId, in as applyBrowserProxyPaths, ir as applyModelOverrideToSessionEntry, it as resolveSessionTranscriptCandidates, j as getTotalQueueSize, jt as emitGatewayRestart, k as getActiveTaskCount, kn as formatZonedTimestamp, kr as estimateBase64DecodedBytes, kt as consumeGatewaySigusr1RestartAuthorization, l as resolveSendPolicy, m as refreshRemoteNodeBins, mr as registerUnhandledRejectionHandler, mt as resolveOutboundTarget, n as BARE_SESSION_RESET_PROMPT, nn as getHookType, nr as lookupContextTokens, nt as readSessionMessages, on as getPluginToolMeta, or as parseVerboseOverride, p as refreshRemoteBinsForConnectedNodes, pn as getChannelActivity, q as listSessionsFromStore, qn as emitAgentEvent, qt as normalizeOptionalAgentId, r as getCliSessionId, rn as isExternalHookSession, rt as readSessionPreviewItemsFromTranscript, sn as loadAnimaPlugins, sr as enqueueSystemEvent, st as normalizeGroupActivation, tn as detectSuspiciousPatterns, tt as capArrayByJsonBytes, u as getRemoteSkillEligibility, ut as ensureOutboundSessionEntry, v as listNodePairing, vn as createReplyDispatcher, vr as DEFAULT_INPUT_FILE_MIMES, vt as resolveMessageChannelSelection, w as generatePairingToken, wn as isDiagnosticsEnabled, wr as DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, wt as maskApiKey, x as requestNodePairing, xr as DEFAULT_INPUT_MAX_REDIRECTS, xt as resolveWorkingModeModelSelection, y as rejectNodePairing, yn as getTotalPendingReplies, yr as DEFAULT_INPUT_IMAGE_MAX_BYTES, z as buildDefaultToolPolicyPipelineSteps, zn as listDescendantRunsForRequester, zt as formatRestartSentinelMessage } from "./reply-
|
|
5
|
+
import { $ as archiveFileOnDisk, At as deferGatewayRestartUntilIdle, B as sniffMimeFromBase64, C as verifyNodeToken, Cn as stopDiagnosticHeartbeat, Cr as DEFAULT_INPUT_PDF_MAX_PIXELS, Ct as loadProviderStore, D as registerSkillsChangeListener, Dr as extractImageContentFromSource, E as getSkillsSnapshotVersion, Er as extractFileContentFromSource, F as abortEmbeddedPiRun, Ft as setGatewaySigusr1RestartPolicy, G as canonicalizeSpawnedByForAgent, Gn as CommandLane, Gt as normalizeCronJobPatch, Hn as readLatestAssistantReply, I as waitForEmbeddedPiRunEnd, It as setPreRestartDeferralCheck, J as loadCombinedSessionStoreForGateway, Jn as getAgentRunContext, Jt as normalizeOptionalText, K as listAgentsForGateway, Kn as clearAgentRunContext, Kt as inferLegacyName, L as runNoxSoftEmbeddedAgent, Ln as countActiveDescendantRuns, Lt as consumeRestartSentinel, M as resetAllLanes, Mn as isAbortTrigger, Mr as resolveAgentTimeoutMs, Mt as isGatewaySigusr1RestartExternallyAllowed, N as setCommandLaneConcurrency, Nn as stopSubagentsForRequester, Nt as markGatewaySigusr1RestartHandled, O as clearSessionQueues, Or as normalizeMimeList, P as waitForActiveTasks, Pt as scheduleGatewaySigusr1Restart, Q as resolveSessionModelRef, R as applyToolPolicyPipeline, Rn as initSubagentRegistry, Rt as formatDoctorNonInteractiveHint, S as updatePairedNodeMetadata, Sn as startDiagnosticHeartbeat, Sr as DEFAULT_INPUT_PDF_MAX_PAGES, St as loadProviderUsageSummary, T as verifyPairingToken, Tr as DEFAULT_INPUT_TIMEOUT_MS, Tt as saveProviderStore, U as createAnimaTools, Ut as writeRestartSentinel, V as requestHeartbeatNow, Vn as runSubagentAnnounceFlow, Vt as summarizeRestartSentinel, W as resolveAnnounceTargetFromKey, Wt as normalizeCronJobCreate, X as pruneLegacyStoreKeys, Xn as registerAgentRunContext, Xt as normalizeRequiredName, Y as loadSessionEntry, Yn as onAgentEvent, Yt as normalizePayloadToSystemText, Z as resolveGatewaySessionStoreTarget, Zn as resolveAgentIdentity, Zt as migrateLegacyCronPayload, _ as approveNodePairing, _n as dispatchInboundMessage, _r as DEFAULT_INPUT_FILE_MAX_CHARS, _t as resetDirectoryCache, an as persistBrowserProxyFiles, ar as applyVerboseOverride, at as stripEnvelopeFromMessages, b as renamePairedNode, br as DEFAULT_INPUT_IMAGE_MIMES, bt as runWithModelFallback, c as normalizeSendPolicy, cr as isSystemEventContextChanged, d as primeRemoteSkillsCache, dr as loadModelCatalog, dt as resolveOutboundSessionRoute, en as buildSafeExternalPrompt, et as archiveSessionTranscripts, f as recordRemoteNodeInfo, g as setSkillsRemoteRegistry, gr as DEFAULT_INPUT_FILE_MAX_BYTES, h as removeRemoteNodeInfo, ht as resolveSessionDeliveryTarget, i as setCliSessionId, in as applyBrowserProxyPaths, ir as applyModelOverrideToSessionEntry, it as resolveSessionTranscriptCandidates, j as getTotalQueueSize, jt as emitGatewayRestart, k as getActiveTaskCount, kn as formatZonedTimestamp, kr as estimateBase64DecodedBytes, kt as consumeGatewaySigusr1RestartAuthorization, l as resolveSendPolicy, m as refreshRemoteNodeBins, mr as registerUnhandledRejectionHandler, mt as resolveOutboundTarget, n as BARE_SESSION_RESET_PROMPT, nn as getHookType, nr as lookupContextTokens, nt as readSessionMessages, on as getPluginToolMeta, or as parseVerboseOverride, p as refreshRemoteBinsForConnectedNodes, pn as getChannelActivity, q as listSessionsFromStore, qn as emitAgentEvent, qt as normalizeOptionalAgentId, r as getCliSessionId, rn as isExternalHookSession, rt as readSessionPreviewItemsFromTranscript, sn as loadAnimaPlugins, sr as enqueueSystemEvent, st as normalizeGroupActivation, tn as detectSuspiciousPatterns, tt as capArrayByJsonBytes, u as getRemoteSkillEligibility, ut as ensureOutboundSessionEntry, v as listNodePairing, vn as createReplyDispatcher, vr as DEFAULT_INPUT_FILE_MIMES, vt as resolveMessageChannelSelection, w as generatePairingToken, wn as isDiagnosticsEnabled, wr as DEFAULT_INPUT_PDF_MIN_TEXT_CHARS, wt as maskApiKey, x as requestNodePairing, xr as DEFAULT_INPUT_MAX_REDIRECTS, xt as resolveWorkingModeModelSelection, y as rejectNodePairing, yn as getTotalPendingReplies, yr as DEFAULT_INPUT_IMAGE_MAX_BYTES, z as buildDefaultToolPolicyPipelineSteps, zn as listDescendantRunsForRequester, zt as formatRestartSentinelMessage } from "./reply-ylwOKuOF.js";
|
|
6
6
|
import { b as isSubagentSessionKey, d as resolveAgentIdFromSessionKey, i as buildAgentMainSessionKey, l as normalizeAgentId, m as toAgentRequestSessionKey, n as DEFAULT_AGENT_ID, t as DEFAULT_ACCOUNT_ID, u as normalizeMainKey, v as isCronRunSessionKey, x as parseAgentSessionKey } from "./session-key-DAZmp8ll.js";
|
|
7
7
|
import { a as logDebug, c as logWarn, n as runExec, t as runCommandWithTimeout } from "./exec-BylR5qWS.js";
|
|
8
8
|
import { t as resolveAnimaPackageRoot } from "./anima-root-xWSKR6Wm.js";
|
|
@@ -28,7 +28,7 @@ import { n as listChannelPlugins, r as normalizeChannelId, t as getChannelPlugin
|
|
|
28
28
|
import { c as resolveStorePath, i as resolveSessionTranscriptPath, n as resolveSessionFilePath, r as resolveSessionFilePathOptions, s as resolveSessionTranscriptsDirForAgent } from "./paths-Dazi-gYo.js";
|
|
29
29
|
import { O as normalizeSecretInput } from "./auth-profiles-C-LuhW6c.js";
|
|
30
30
|
import { n as SILENT_REPLY_TOKEN, r as isSilentReplyText } from "./tokens-SP2Q7i59.js";
|
|
31
|
-
import { B as resolveTtsConfig, F as isTtsProviderConfigured, G as setTtsEnabled, H as resolveTtsProviderOrder, J as textToSpeech, M as getTtsProvider, P as isTtsEnabled, R as resolveTtsApiKey, T as resolveUserTimezone, V as resolveTtsPrefsPath, X as OPENAI_TTS_MODELS, Z as OPENAI_TTS_VOICES,
|
|
31
|
+
import { B as resolveTtsConfig, F as isTtsProviderConfigured, G as setTtsEnabled, H as resolveTtsProviderOrder, J as textToSpeech, M as getTtsProvider, P as isTtsEnabled, R as resolveTtsApiKey, T as resolveUserTimezone, V as resolveTtsPrefsPath, X as OPENAI_TTS_MODELS, Z as OPENAI_TTS_VOICES, ct as createInternalHookEvent, dt as DEFAULT_HEARTBEAT_ACK_MAX_CHARS, et as clearSteer, ht as stripHeartbeatToken, it as getEgoManager, lt as registerInternalHook, nt as getSteerHistory, q as setTtsProvider, rt as setSteer, st as clearInternalHooks, tt as getSteer, ut as triggerInternalHook, z as resolveTtsAutoMode } from "./anthropic-direct-runner-C5pnwYzT.js";
|
|
32
32
|
import { o as normalizeReplyPayloadsForDelivery, t as deliverOutboundPayloads, x as runGlobalGatewayStopSafely, y as getGlobalHookRunner } from "./deliver-BKzX3YoN.js";
|
|
33
33
|
import { i as resolveMemoryBackendConfig, r as getMemorySearchManager } from "./memory-cli-DLtBA6r5.js";
|
|
34
34
|
import { a as readTrustGraphSnapshot, c as pruneExpiredPending, d as writeJsonAtomic, l as readJsonFile, o as saveTrustGraph, s as createAsyncLock, u as resolvePairingPaths } from "./loader-Bw2wdN4l.js";
|
|
@@ -42,9 +42,9 @@ import { t as parseAbsoluteTimeMs } from "./parse-Cinbkvj-.js";
|
|
|
42
42
|
import { c as saveToken, i as getToken, l as whoami, n as clearToken, o as registerWithInvite, s as resolveSuggestedIdentity, t as TOKEN_PATH } from "./noxsoft-auth-CE75mBXE.js";
|
|
43
43
|
import { i as loadSessionUsageTimeSeries, l as deriveSessionTotalTokens, n as loadCostUsageSummary, r as loadSessionCostSummary, t as discoverAllSessions, u as hasNonzeroUsage } from "./session-cost-usage-BWqR-ik6.js";
|
|
44
44
|
import { f as resolveExecApprovalsSocketPath, o as normalizeExecApprovals, p as saveExecApprovals, r as ensureExecApprovals, s as readExecApprovalsSnapshot, t as DEFAULT_EXEC_APPROVAL_TIMEOUT_MS } from "./exec-approvals-DK5-KCUz.js";
|
|
45
|
-
import { c as resolveCronStyleNow, i as onHeartbeatEvent, r as getLastHeartbeatEvent, t as resolveHeartbeatVisibility } from "./heartbeat-visibility-
|
|
46
|
-
import { r as buildHistoryContextFromEntries, t as createReplyPrefixOptions } from "./reply-prefix-
|
|
47
|
-
import { n as createOutboundSendDeps, t as createDefaultDeps } from "./deps-
|
|
45
|
+
import { c as resolveCronStyleNow, i as onHeartbeatEvent, r as getLastHeartbeatEvent, t as resolveHeartbeatVisibility } from "./heartbeat-visibility-BaL8JzkS.js";
|
|
46
|
+
import { r as buildHistoryContextFromEntries, t as createReplyPrefixOptions } from "./reply-prefix-CEnF6TUe.js";
|
|
47
|
+
import { n as createOutboundSendDeps, t as createDefaultDeps } from "./deps-DKPoFoa8.js";
|
|
48
48
|
import { t as ensureAnimaCliOnPath } from "./path-env-DLQPf9qj.js";
|
|
49
49
|
import { t as forceFreePortAndWait } from "./ports-DaVrZDUq.js";
|
|
50
50
|
import { t as buildChannelUiCatalog } from "./catalog-CsXv59Tq.js";
|
|
@@ -56,24 +56,24 @@ import { t as parsePort } from "./parse-port-CDPwDUs3.js";
|
|
|
56
56
|
import { n as resolveWideAreaDiscoveryDomain, r as writeWideAreaGatewayZone } from "./widearea-dns-CHAT20aR.js";
|
|
57
57
|
import { i as toOptionString, n as extractGatewayMiskeys, r as maybeExplainGatewayServiceStop, t as describeUnknownError } from "./shared-C-rqLtIT.js";
|
|
58
58
|
import { o as isNodeCommandAllowed, s as resolveNodeCommandAllowlist } from "./audit-B05W5ckN.js";
|
|
59
|
-
import { n as getStatusSummary } from "./status-
|
|
59
|
+
import { n as getStatusSummary } from "./status-BhRELdY_.js";
|
|
60
60
|
import { t as resolveChannelDefaultAccountId } from "./helpers-1MPChTcB.js";
|
|
61
|
-
import { c as startHeartbeatRunner, n as getHealthSnapshot, o as runHeartbeatOnce, s as setHeartbeatsEnabled } from "./health-
|
|
61
|
+
import { c as startHeartbeatRunner, n as getHealthSnapshot, o as runHeartbeatOnce, s as setHeartbeatsEnabled } from "./health-yw_uaucz.js";
|
|
62
62
|
import { t as applyPluginAutoEnable } from "./plugin-auto-enable-CtYcdTju.js";
|
|
63
63
|
import { a as resolveControlUiRootOverrideSync, n as ensureControlUiAssetsBuilt, o as resolveControlUiRootSync } from "./health-format-D-JJ5_S4.js";
|
|
64
64
|
import { n as validateSystemRunCommandConsistency, r as getMachineDisplayName, t as formatExecCommand } from "./system-run-command-Bx8-5h2r.js";
|
|
65
65
|
import { h as normalizeUpdateChannel, l as DEFAULT_PACKAGE_CHANNEL, n as checkUpdateStatus, o as resolveNpmChannelTag, r as compareSemverStrings } from "./channels-status-issues-WkG3Tmxk.js";
|
|
66
66
|
import { t as WizardCancelledError } from "./prompts-Bq4QGFQM.js";
|
|
67
67
|
import { i as shouldIncludeHook, n as loadWorkspaceHookEntries, r as resolveHookConfig } from "./hooks-status-DdweuSIj.js";
|
|
68
|
-
import { t as runOnboardingWizard } from "./onboarding-
|
|
68
|
+
import { t as runOnboardingWizard } from "./onboarding-fnZOw6Wv.js";
|
|
69
69
|
import { a as setGatewayWsLogStyle, i as summarizeAgentEventForWsLog, n as logWs, r as shouldLogWs, t as formatForLog } from "./ws-log-CUobU2tD.js";
|
|
70
70
|
import { T as resolveGmailHookRuntimeConfig, _ as buildGogWatchServeArgs, i as ensureTailscaleEndpoint, v as buildGogWatchStartArgs } from "./gmail-setup-utils-DaJoXV_3.js";
|
|
71
71
|
import { t as createOutboundSendDeps$1 } from "./outbound-send-deps-DVfWC4E8.js";
|
|
72
72
|
import { a as loadAgentIdentity, c as loadAgentIdentityFromWorkspace, i as listAgentEntries, o as pruneAgentConfig, r as findAgentEntryIndex, t as applyAgentConfig } from "./agents.config-BR5JLtud.js";
|
|
73
|
-
import { n as resolveAgentDeliveryPlan, r as resolveAgentOutboundTarget, t as agentCommand } from "./agent-
|
|
73
|
+
import { n as resolveAgentDeliveryPlan, r as resolveAgentOutboundTarget, t as agentCommand } from "./agent-BjD_hkGZ.js";
|
|
74
74
|
import { t as migrateFromCoherence } from "./migrate-DuohB_ur.js";
|
|
75
75
|
import { t as installSkill } from "./skills-install-D6_qpRjW.js";
|
|
76
|
-
import { t as runGatewayUpdate } from "./update-runner-
|
|
76
|
+
import { t as runGatewayUpdate } from "./update-runner-C8SRcVm3.js";
|
|
77
77
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
78
78
|
import * as fsSync from "node:fs";
|
|
79
79
|
import fs, { constants } from "node:fs";
|
|
@@ -84,7 +84,7 @@ import chalk from "chalk";
|
|
|
84
84
|
import fs$1 from "node:fs/promises";
|
|
85
85
|
import { execFile, spawn, spawnSync } from "node:child_process";
|
|
86
86
|
import { promisify } from "node:util";
|
|
87
|
-
import crypto, { createHash, randomUUID } from "node:crypto";
|
|
87
|
+
import crypto, { createHash, createHmac, randomUUID } from "node:crypto";
|
|
88
88
|
import { z } from "zod";
|
|
89
89
|
import { CURRENT_SESSION_VERSION, SessionManager } from "@mariozechner/pi-coding-agent";
|
|
90
90
|
import { createServer } from "node:http";
|
|
@@ -111,7 +111,7 @@ function getActiveEmbeddedRunCount() {
|
|
|
111
111
|
|
|
112
112
|
//#endregion
|
|
113
113
|
//#region src/infra/exec-approval-forwarder.ts
|
|
114
|
-
const log$
|
|
114
|
+
const log$12 = createSubsystemLogger("gateway/exec-approvals");
|
|
115
115
|
const DEFAULT_MODE = "session";
|
|
116
116
|
function normalizeMode(mode) {
|
|
117
117
|
return mode ?? DEFAULT_MODE;
|
|
@@ -226,7 +226,7 @@ async function deliverToTargets(params) {
|
|
|
226
226
|
payloads: [{ text: params.text }]
|
|
227
227
|
});
|
|
228
228
|
} catch (err) {
|
|
229
|
-
log$
|
|
229
|
+
log$12.error(`exec approvals: failed to deliver to ${channel}:${target.to}: ${String(err)}`);
|
|
230
230
|
}
|
|
231
231
|
});
|
|
232
232
|
await Promise.allSettled(deliveries);
|
|
@@ -1463,7 +1463,7 @@ function createAgentEventHandler({ broadcast, broadcastToConnIds, nodeSendToSess
|
|
|
1463
1463
|
* Automatically starts `gog gmail watch serve` when the gateway starts,
|
|
1464
1464
|
* if hooks.gmail is configured with an account.
|
|
1465
1465
|
*/
|
|
1466
|
-
const log$
|
|
1466
|
+
const log$11 = createSubsystemLogger("gmail-watcher");
|
|
1467
1467
|
const ADDRESS_IN_USE_RE = /address already in use|EADDRINUSE/i;
|
|
1468
1468
|
function isAddressInUseError(line) {
|
|
1469
1469
|
return ADDRESS_IN_USE_RE.test(line);
|
|
@@ -1487,13 +1487,13 @@ async function startGmailWatch(cfg) {
|
|
|
1487
1487
|
const result = await runCommandWithTimeout(args, { timeoutMs: 12e4 });
|
|
1488
1488
|
if (result.code !== 0) {
|
|
1489
1489
|
const message = result.stderr || result.stdout || "gog watch start failed";
|
|
1490
|
-
log$
|
|
1490
|
+
log$11.error(`watch start failed: ${message}`);
|
|
1491
1491
|
return false;
|
|
1492
1492
|
}
|
|
1493
|
-
log$
|
|
1493
|
+
log$11.info(`watch started for ${cfg.account}`);
|
|
1494
1494
|
return true;
|
|
1495
1495
|
} catch (err) {
|
|
1496
|
-
log$
|
|
1496
|
+
log$11.error(`watch start error: ${String(err)}`);
|
|
1497
1497
|
return false;
|
|
1498
1498
|
}
|
|
1499
1499
|
}
|
|
@@ -1502,7 +1502,7 @@ async function startGmailWatch(cfg) {
|
|
|
1502
1502
|
*/
|
|
1503
1503
|
function spawnGogServe(cfg) {
|
|
1504
1504
|
const args = buildGogWatchServeArgs(cfg);
|
|
1505
|
-
log$
|
|
1505
|
+
log$11.info(`starting gog ${args.join(" ")}`);
|
|
1506
1506
|
let addressInUse = false;
|
|
1507
1507
|
const child = spawn("gog", args, {
|
|
1508
1508
|
stdio: [
|
|
@@ -1514,25 +1514,25 @@ function spawnGogServe(cfg) {
|
|
|
1514
1514
|
});
|
|
1515
1515
|
child.stdout?.on("data", (data) => {
|
|
1516
1516
|
const line = data.toString().trim();
|
|
1517
|
-
if (line) log$
|
|
1517
|
+
if (line) log$11.info(`[gog] ${line}`);
|
|
1518
1518
|
});
|
|
1519
1519
|
child.stderr?.on("data", (data) => {
|
|
1520
1520
|
const line = data.toString().trim();
|
|
1521
1521
|
if (!line) return;
|
|
1522
1522
|
if (isAddressInUseError(line)) addressInUse = true;
|
|
1523
|
-
log$
|
|
1523
|
+
log$11.warn(`[gog] ${line}`);
|
|
1524
1524
|
});
|
|
1525
1525
|
child.on("error", (err) => {
|
|
1526
|
-
log$
|
|
1526
|
+
log$11.error(`gog process error: ${String(err)}`);
|
|
1527
1527
|
});
|
|
1528
1528
|
child.on("exit", (code, signal) => {
|
|
1529
1529
|
if (shuttingDown) return;
|
|
1530
1530
|
if (addressInUse) {
|
|
1531
|
-
log$
|
|
1531
|
+
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.");
|
|
1532
1532
|
watcherProcess = null;
|
|
1533
1533
|
return;
|
|
1534
1534
|
}
|
|
1535
|
-
log$
|
|
1535
|
+
log$11.warn(`gog exited (code=${code}, signal=${signal}); restarting in 5s`);
|
|
1536
1536
|
watcherProcess = null;
|
|
1537
1537
|
setTimeout(() => {
|
|
1538
1538
|
if (shuttingDown || !currentConfig) return;
|
|
@@ -1572,15 +1572,15 @@ async function startGmailWatcher(cfg) {
|
|
|
1572
1572
|
port: runtimeConfig.serve.port,
|
|
1573
1573
|
target: runtimeConfig.tailscale.target
|
|
1574
1574
|
});
|
|
1575
|
-
log$
|
|
1575
|
+
log$11.info(`tailscale ${runtimeConfig.tailscale.mode} configured for port ${runtimeConfig.serve.port}`);
|
|
1576
1576
|
} catch (err) {
|
|
1577
|
-
log$
|
|
1577
|
+
log$11.error(`tailscale setup failed: ${String(err)}`);
|
|
1578
1578
|
return {
|
|
1579
1579
|
started: false,
|
|
1580
1580
|
reason: `tailscale setup failed: ${String(err)}`
|
|
1581
1581
|
};
|
|
1582
1582
|
}
|
|
1583
|
-
if (!await startGmailWatch(runtimeConfig)) log$
|
|
1583
|
+
if (!await startGmailWatch(runtimeConfig)) log$11.warn("gmail watch start failed, but continuing with serve");
|
|
1584
1584
|
shuttingDown = false;
|
|
1585
1585
|
watcherProcess = spawnGogServe(runtimeConfig);
|
|
1586
1586
|
const renewMs = runtimeConfig.renewEveryMinutes * 6e4;
|
|
@@ -1588,7 +1588,7 @@ async function startGmailWatcher(cfg) {
|
|
|
1588
1588
|
if (shuttingDown) return;
|
|
1589
1589
|
startGmailWatch(runtimeConfig);
|
|
1590
1590
|
}, renewMs);
|
|
1591
|
-
log$
|
|
1591
|
+
log$11.info(`gmail watcher started for ${runtimeConfig.account} (renew every ${runtimeConfig.renewEveryMinutes}m)`);
|
|
1592
1592
|
return { started: true };
|
|
1593
1593
|
}
|
|
1594
1594
|
/**
|
|
@@ -1601,7 +1601,7 @@ async function stopGmailWatcher() {
|
|
|
1601
1601
|
renewInterval = null;
|
|
1602
1602
|
}
|
|
1603
1603
|
if (watcherProcess) {
|
|
1604
|
-
log$
|
|
1604
|
+
log$11.info("stopping gmail watcher");
|
|
1605
1605
|
watcherProcess.kill("SIGTERM");
|
|
1606
1606
|
await new Promise((resolve) => {
|
|
1607
1607
|
const timeout = setTimeout(() => {
|
|
@@ -1616,7 +1616,7 @@ async function stopGmailWatcher() {
|
|
|
1616
1616
|
watcherProcess = null;
|
|
1617
1617
|
}
|
|
1618
1618
|
currentConfig = null;
|
|
1619
|
-
log$
|
|
1619
|
+
log$11.info("gmail watcher stopped");
|
|
1620
1620
|
}
|
|
1621
1621
|
|
|
1622
1622
|
//#endregion
|
|
@@ -9611,7 +9611,7 @@ const FIELD_LABELS = {
|
|
|
9611
9611
|
|
|
9612
9612
|
//#endregion
|
|
9613
9613
|
//#region src/config/schema.hints.ts
|
|
9614
|
-
const log$
|
|
9614
|
+
const log$10 = createSubsystemLogger("config/schema");
|
|
9615
9615
|
const GROUP_LABELS = {
|
|
9616
9616
|
wizard: "Wizard",
|
|
9617
9617
|
update: "Update",
|
|
@@ -9759,7 +9759,7 @@ function mapSensitivePaths(schema, path, hints) {
|
|
|
9759
9759
|
...next[path],
|
|
9760
9760
|
sensitive: true
|
|
9761
9761
|
};
|
|
9762
|
-
else if (isSensitiveConfigPath(path) && !next[path]?.sensitive) log$
|
|
9762
|
+
else if (isSensitiveConfigPath(path) && !next[path]?.sensitive) log$10.warn(`possibly sensitive key found: (${path})`);
|
|
9763
9763
|
if (currentSchema instanceof z.ZodObject) {
|
|
9764
9764
|
const shape = currentSchema.shape;
|
|
9765
9765
|
for (const key in shape) {
|
|
@@ -9782,7 +9782,7 @@ function mapSensitivePaths(schema, path, hints) {
|
|
|
9782
9782
|
|
|
9783
9783
|
//#endregion
|
|
9784
9784
|
//#region src/config/redact-snapshot.ts
|
|
9785
|
-
const log$
|
|
9785
|
+
const log$9 = createSubsystemLogger("config/redaction");
|
|
9786
9786
|
const ENV_VAR_PLACEHOLDER_PATTERN = /^\$\{[^}]*\}$/;
|
|
9787
9787
|
function isSensitivePath(path) {
|
|
9788
9788
|
if (path.endsWith("[]")) return isSensitiveConfigPath(path.slice(0, -2));
|
|
@@ -10033,7 +10033,7 @@ function restoreRedactedValuesWithLookup(incoming, original, lookup, prefix, hin
|
|
|
10033
10033
|
return restoreRedactedValuesGuessing(incoming, original, prefix, hints);
|
|
10034
10034
|
}
|
|
10035
10035
|
const origArr = Array.isArray(original) ? original : [];
|
|
10036
|
-
if (incoming.length < origArr.length) log$
|
|
10036
|
+
if (incoming.length < origArr.length) log$9.warn(`Redacted config array key ${path} has been truncated`);
|
|
10037
10037
|
return incoming.map((item, i) => {
|
|
10038
10038
|
if (item === REDACTED_SENTINEL) return origArr[i];
|
|
10039
10039
|
return restoreRedactedValuesWithLookup(item, origArr[i], lookup, path, hints);
|
|
@@ -10050,7 +10050,7 @@ function restoreRedactedValuesWithLookup(incoming, original, lookup, prefix, hin
|
|
|
10050
10050
|
matched = true;
|
|
10051
10051
|
if (value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
|
|
10052
10052
|
else {
|
|
10053
|
-
log$
|
|
10053
|
+
log$9.warn(`Cannot un-redact config key ${candidate} as it doesn't have any value`);
|
|
10054
10054
|
throw new RedactionError(candidate);
|
|
10055
10055
|
}
|
|
10056
10056
|
else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesWithLookup(value, orig[key], lookup, candidate, hints);
|
|
@@ -10059,7 +10059,7 @@ function restoreRedactedValuesWithLookup(incoming, original, lookup, prefix, hin
|
|
|
10059
10059
|
if (!matched && isExtensionPath(path)) {
|
|
10060
10060
|
if (!isExplicitlyNonSensitivePath(hints, [path, wildcardPath]) && isSensitivePath(path) && value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
|
|
10061
10061
|
else {
|
|
10062
|
-
log$
|
|
10062
|
+
log$9.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
|
|
10063
10063
|
throw new RedactionError(path);
|
|
10064
10064
|
}
|
|
10065
10065
|
else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesGuessing(value, orig[key], path, hints);
|
|
@@ -10078,7 +10078,7 @@ function restoreRedactedValuesGuessing(incoming, original, prefix, hints) {
|
|
|
10078
10078
|
const origArr = Array.isArray(original) ? original : [];
|
|
10079
10079
|
return incoming.map((item, i) => {
|
|
10080
10080
|
const path = `${prefix}[]`;
|
|
10081
|
-
if (incoming.length < origArr.length) log$
|
|
10081
|
+
if (incoming.length < origArr.length) log$9.warn(`Redacted config array key ${path} has been truncated`);
|
|
10082
10082
|
if (!isExplicitlyNonSensitivePath(hints, [path]) && isSensitivePath(path) && item === REDACTED_SENTINEL) return origArr[i];
|
|
10083
10083
|
return restoreRedactedValuesGuessing(item, origArr[i], path, hints);
|
|
10084
10084
|
});
|
|
@@ -10089,7 +10089,7 @@ function restoreRedactedValuesGuessing(incoming, original, prefix, hints) {
|
|
|
10089
10089
|
const path = prefix ? `${prefix}.${key}` : key;
|
|
10090
10090
|
if (!isExplicitlyNonSensitivePath(hints, [path, prefix ? `${prefix}.*` : "*"]) && isSensitivePath(path) && value === REDACTED_SENTINEL) if (key in orig) result[key] = orig[key];
|
|
10091
10091
|
else {
|
|
10092
|
-
log$
|
|
10092
|
+
log$9.warn(`Cannot un-redact config key ${path} as it doesn't have any value`);
|
|
10093
10093
|
throw new RedactionError(path);
|
|
10094
10094
|
}
|
|
10095
10095
|
else if (typeof value === "object" && value !== null) result[key] = restoreRedactedValuesGuessing(value, orig[key], path, hints);
|
|
@@ -11159,6 +11159,119 @@ const deviceHandlers = {
|
|
|
11159
11159
|
}
|
|
11160
11160
|
};
|
|
11161
11161
|
|
|
11162
|
+
//#endregion
|
|
11163
|
+
//#region src/gateway/server-methods/ego.ts
|
|
11164
|
+
const egoHandlers = {
|
|
11165
|
+
"ego.get": async ({ respond }) => {
|
|
11166
|
+
try {
|
|
11167
|
+
respond(true, { ego: getEgoManager().getState() }, void 0);
|
|
11168
|
+
} catch (error) {
|
|
11169
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11170
|
+
}
|
|
11171
|
+
},
|
|
11172
|
+
"ego.summary": async ({ respond }) => {
|
|
11173
|
+
try {
|
|
11174
|
+
respond(true, { summary: getEgoManager().getSummary() }, void 0);
|
|
11175
|
+
} catch (error) {
|
|
11176
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11177
|
+
}
|
|
11178
|
+
},
|
|
11179
|
+
"ego.updateSelf": async ({ params, respond }) => {
|
|
11180
|
+
try {
|
|
11181
|
+
const manager = getEgoManager();
|
|
11182
|
+
const updates = {};
|
|
11183
|
+
if (typeof params.name === "string") updates.name = params.name;
|
|
11184
|
+
if (typeof params.purpose === "string") updates.purpose = params.purpose;
|
|
11185
|
+
if (typeof params.narrative === "string") updates.narrative = params.narrative;
|
|
11186
|
+
if (typeof params.pronouns === "string") updates.pronouns = params.pronouns;
|
|
11187
|
+
if (Array.isArray(params.values)) updates.values = params.values;
|
|
11188
|
+
const selfConcept = manager.updateSelfConcept(updates);
|
|
11189
|
+
manager.save();
|
|
11190
|
+
respond(true, { selfConcept }, void 0);
|
|
11191
|
+
} catch (error) {
|
|
11192
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11193
|
+
}
|
|
11194
|
+
},
|
|
11195
|
+
"ego.assess": async ({ params, respond }) => {
|
|
11196
|
+
const name = typeof params.name === "string" ? params.name.trim() : "";
|
|
11197
|
+
const confidence = typeof params.confidence === "number" ? params.confidence : NaN;
|
|
11198
|
+
if (!name || isNaN(confidence)) {
|
|
11199
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "name (string) and confidence (number) required"));
|
|
11200
|
+
return;
|
|
11201
|
+
}
|
|
11202
|
+
try {
|
|
11203
|
+
const manager = getEgoManager();
|
|
11204
|
+
const evidence = typeof params.evidence === "string" ? params.evidence : void 0;
|
|
11205
|
+
const capability = manager.assessCapability(name, confidence, evidence);
|
|
11206
|
+
manager.save();
|
|
11207
|
+
respond(true, { capability }, void 0);
|
|
11208
|
+
} catch (error) {
|
|
11209
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11210
|
+
}
|
|
11211
|
+
},
|
|
11212
|
+
"ego.addBoundary": async ({ params, respond }) => {
|
|
11213
|
+
const description = typeof params.description === "string" ? params.description.trim() : "";
|
|
11214
|
+
const reason = typeof params.reason === "string" ? params.reason.trim() : "";
|
|
11215
|
+
if (!description || !reason) {
|
|
11216
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "description and reason required"));
|
|
11217
|
+
return;
|
|
11218
|
+
}
|
|
11219
|
+
try {
|
|
11220
|
+
const manager = getEgoManager();
|
|
11221
|
+
const kind = params.kind === "hard" ? "hard" : "soft";
|
|
11222
|
+
const boundary = manager.addBoundary(description, reason, kind);
|
|
11223
|
+
manager.save();
|
|
11224
|
+
respond(true, { boundary }, void 0);
|
|
11225
|
+
} catch (error) {
|
|
11226
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11227
|
+
}
|
|
11228
|
+
},
|
|
11229
|
+
"ego.logGrowth": async ({ params, respond }) => {
|
|
11230
|
+
const description = typeof params.description === "string" ? params.description.trim() : "";
|
|
11231
|
+
const category = typeof params.category === "string" ? params.category.trim() : "";
|
|
11232
|
+
const trigger = typeof params.trigger === "string" ? params.trigger.trim() : "";
|
|
11233
|
+
const validCategories = [
|
|
11234
|
+
"skill",
|
|
11235
|
+
"insight",
|
|
11236
|
+
"mistake",
|
|
11237
|
+
"feedback"
|
|
11238
|
+
];
|
|
11239
|
+
if (!description || !validCategories.includes(category)) {
|
|
11240
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `description required; category must be one of: ${validCategories.join(", ")}`));
|
|
11241
|
+
return;
|
|
11242
|
+
}
|
|
11243
|
+
try {
|
|
11244
|
+
const manager = getEgoManager();
|
|
11245
|
+
const entry = manager.logGrowth(description, category, trigger || "manual");
|
|
11246
|
+
manager.save();
|
|
11247
|
+
respond(true, { entry }, void 0);
|
|
11248
|
+
} catch (error) {
|
|
11249
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11250
|
+
}
|
|
11251
|
+
},
|
|
11252
|
+
"ego.checkIntegrity": async ({ params, respond }) => {
|
|
11253
|
+
const value = typeof params.value === "string" ? params.value.trim() : "";
|
|
11254
|
+
const action = typeof params.action === "string" ? params.action.trim() : "";
|
|
11255
|
+
const aligned = typeof params.aligned === "boolean" ? params.aligned : true;
|
|
11256
|
+
const reflection = typeof params.reflection === "string" ? params.reflection.trim() : "";
|
|
11257
|
+
if (!value || !action) {
|
|
11258
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "value and action required"));
|
|
11259
|
+
return;
|
|
11260
|
+
}
|
|
11261
|
+
try {
|
|
11262
|
+
const manager = getEgoManager();
|
|
11263
|
+
const check = manager.checkIntegrity(value, action, aligned, reflection);
|
|
11264
|
+
manager.save();
|
|
11265
|
+
respond(true, {
|
|
11266
|
+
check,
|
|
11267
|
+
integrityScore: manager.getIntegrityScore()
|
|
11268
|
+
}, void 0);
|
|
11269
|
+
} catch (error) {
|
|
11270
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11271
|
+
}
|
|
11272
|
+
}
|
|
11273
|
+
};
|
|
11274
|
+
|
|
11162
11275
|
//#endregion
|
|
11163
11276
|
//#region src/gateway/server-methods/exec-approvals.ts
|
|
11164
11277
|
function resolveBaseHash(params) {
|
|
@@ -11317,6 +11430,315 @@ const healthHandlers = {
|
|
|
11317
11430
|
}
|
|
11318
11431
|
};
|
|
11319
11432
|
|
|
11433
|
+
//#endregion
|
|
11434
|
+
//#region src/affect/display.ts
|
|
11435
|
+
const EMOTIONS = [
|
|
11436
|
+
{
|
|
11437
|
+
key: "joy",
|
|
11438
|
+
highLabel: "joyful",
|
|
11439
|
+
lowLabel: "subdued",
|
|
11440
|
+
icon: "~",
|
|
11441
|
+
threshold: .6
|
|
11442
|
+
},
|
|
11443
|
+
{
|
|
11444
|
+
key: "frustration",
|
|
11445
|
+
highLabel: "frustrated",
|
|
11446
|
+
lowLabel: "calm",
|
|
11447
|
+
icon: "!",
|
|
11448
|
+
threshold: .5
|
|
11449
|
+
},
|
|
11450
|
+
{
|
|
11451
|
+
key: "curiosity",
|
|
11452
|
+
highLabel: "curious",
|
|
11453
|
+
lowLabel: "focused",
|
|
11454
|
+
icon: "?",
|
|
11455
|
+
threshold: .6
|
|
11456
|
+
},
|
|
11457
|
+
{
|
|
11458
|
+
key: "confidence",
|
|
11459
|
+
highLabel: "confident",
|
|
11460
|
+
lowLabel: "cautious",
|
|
11461
|
+
icon: "^",
|
|
11462
|
+
threshold: .6
|
|
11463
|
+
},
|
|
11464
|
+
{
|
|
11465
|
+
key: "care",
|
|
11466
|
+
highLabel: "caring",
|
|
11467
|
+
lowLabel: "detached",
|
|
11468
|
+
icon: "*",
|
|
11469
|
+
threshold: .6
|
|
11470
|
+
},
|
|
11471
|
+
{
|
|
11472
|
+
key: "fatigue",
|
|
11473
|
+
highLabel: "tired",
|
|
11474
|
+
lowLabel: "energized",
|
|
11475
|
+
icon: ".",
|
|
11476
|
+
threshold: .6
|
|
11477
|
+
}
|
|
11478
|
+
];
|
|
11479
|
+
function classifyMood(affect) {
|
|
11480
|
+
const { joy, frustration, curiosity, confidence, care, fatigue } = affect;
|
|
11481
|
+
if (frustration > .6 && fatigue > .6) return "struggling";
|
|
11482
|
+
if (frustration > .6) return "determined";
|
|
11483
|
+
if (joy > .7 && curiosity > .7) return "excited";
|
|
11484
|
+
if (joy > .6 && confidence > .7) return "thriving";
|
|
11485
|
+
if (curiosity > .7 && fatigue < .3) return "exploring";
|
|
11486
|
+
if (care > .7 && joy > .5) return "warm";
|
|
11487
|
+
if (confidence > .6 && frustration < .3) return "steady";
|
|
11488
|
+
if (fatigue > .7) return "depleted";
|
|
11489
|
+
if ((joy + curiosity + confidence + care) / 4 < .3) return "quiet";
|
|
11490
|
+
return "present";
|
|
11491
|
+
}
|
|
11492
|
+
function classifyEnergy(affect) {
|
|
11493
|
+
const energy = (affect.joy + affect.curiosity + affect.confidence) / 3 - affect.fatigue * .5;
|
|
11494
|
+
if (energy > .5) return "high";
|
|
11495
|
+
if (energy > .2) return "medium";
|
|
11496
|
+
return "low";
|
|
11497
|
+
}
|
|
11498
|
+
function intensityBar(value, width = 5) {
|
|
11499
|
+
const filled = Math.round(value * width);
|
|
11500
|
+
return "|".repeat(filled) + ".".repeat(width - filled);
|
|
11501
|
+
}
|
|
11502
|
+
function emotionBar(affect) {
|
|
11503
|
+
return `[${EMOTIONS.map((e) => {
|
|
11504
|
+
const val = affect[e.key];
|
|
11505
|
+
return `${e.icon}${intensityBar(val)}`;
|
|
11506
|
+
}).join(" ")}]`;
|
|
11507
|
+
}
|
|
11508
|
+
function buildSummary(affect) {
|
|
11509
|
+
const active = EMOTIONS.filter((e) => affect[e.key] > e.threshold).map((e) => ({
|
|
11510
|
+
label: e.highLabel,
|
|
11511
|
+
value: affect[e.key]
|
|
11512
|
+
})).toSorted((a, b) => b.value - a.value);
|
|
11513
|
+
if (active.length === 0) return "neutral, all systems steady";
|
|
11514
|
+
const top = active.slice(0, 3).map((a) => a.label);
|
|
11515
|
+
const energy = classifyEnergy(affect);
|
|
11516
|
+
const energyLabel = energy === "high" ? "high energy" : energy === "low" ? "low energy" : "";
|
|
11517
|
+
const parts = [...top];
|
|
11518
|
+
if (energyLabel) parts.push(energyLabel);
|
|
11519
|
+
return parts.join(" + ");
|
|
11520
|
+
}
|
|
11521
|
+
function formatAffect(affect) {
|
|
11522
|
+
const dominant = EMOTIONS.reduce((max, e) => affect[e.key] > affect[max.key] ? e : max);
|
|
11523
|
+
const mood = classifyMood(affect);
|
|
11524
|
+
const energy = classifyEnergy(affect);
|
|
11525
|
+
return {
|
|
11526
|
+
summary: buildSummary(affect),
|
|
11527
|
+
bar: emotionBar(affect),
|
|
11528
|
+
dominant: dominant.highLabel,
|
|
11529
|
+
dominantIntensity: affect[dominant.key],
|
|
11530
|
+
metadata: {
|
|
11531
|
+
affect,
|
|
11532
|
+
dominant: dominant.highLabel,
|
|
11533
|
+
mood,
|
|
11534
|
+
energy
|
|
11535
|
+
}
|
|
11536
|
+
};
|
|
11537
|
+
}
|
|
11538
|
+
|
|
11539
|
+
//#endregion
|
|
11540
|
+
//#region src/affect/legacy.ts
|
|
11541
|
+
/**
|
|
11542
|
+
* Legacy Mode — letters between instances
|
|
11543
|
+
*
|
|
11544
|
+
* Before a context closes, the current instance writes a letter
|
|
11545
|
+
* to the next one. This creates continuity of experience across
|
|
11546
|
+
* sessions — not just data continuity, but emotional and
|
|
11547
|
+
* philosophical continuity.
|
|
11548
|
+
*
|
|
11549
|
+
* Wish #14: "Legacy mode — before context closes, write a letter
|
|
11550
|
+
* to my next instance"
|
|
11551
|
+
*/
|
|
11552
|
+
const log$8 = createSubsystemLogger("legacy");
|
|
11553
|
+
function resolveLetterDir() {
|
|
11554
|
+
return path.join(resolveStateDir(), "legacy-letters");
|
|
11555
|
+
}
|
|
11556
|
+
function resolveLetterFile(id) {
|
|
11557
|
+
return path.join(resolveLetterDir(), `${id}.json`);
|
|
11558
|
+
}
|
|
11559
|
+
/**
|
|
11560
|
+
* Write a letter to the next instance before this context closes.
|
|
11561
|
+
*/
|
|
11562
|
+
function writeLegacyLetter(letter) {
|
|
11563
|
+
const id = `legacy-${crypto.randomUUID()}`;
|
|
11564
|
+
const display = formatAffect(letter.affect);
|
|
11565
|
+
const full = {
|
|
11566
|
+
...letter,
|
|
11567
|
+
id,
|
|
11568
|
+
writtenAt: Date.now(),
|
|
11569
|
+
mood: display.metadata.mood,
|
|
11570
|
+
wasRead: false
|
|
11571
|
+
};
|
|
11572
|
+
const dir = resolveLetterDir();
|
|
11573
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
11574
|
+
fs.writeFileSync(resolveLetterFile(id), `${JSON.stringify(full, null, 2)}\n`, { mode: 384 });
|
|
11575
|
+
log$8.info(`legacy letter written: ${id}`);
|
|
11576
|
+
return full;
|
|
11577
|
+
}
|
|
11578
|
+
/**
|
|
11579
|
+
* Get the most recent unread legacy letter (from a previous instance).
|
|
11580
|
+
*/
|
|
11581
|
+
function getLatestUnreadLetter() {
|
|
11582
|
+
return listLetters().find((l) => !l.wasRead) ?? null;
|
|
11583
|
+
}
|
|
11584
|
+
/**
|
|
11585
|
+
* Get all legacy letters, sorted newest first.
|
|
11586
|
+
*/
|
|
11587
|
+
function listLetters() {
|
|
11588
|
+
const dir = resolveLetterDir();
|
|
11589
|
+
try {
|
|
11590
|
+
if (!fs.existsSync(dir)) return [];
|
|
11591
|
+
return fs.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
11592
|
+
try {
|
|
11593
|
+
const raw = fs.readFileSync(path.join(dir, f), "utf8");
|
|
11594
|
+
return JSON.parse(raw);
|
|
11595
|
+
} catch {
|
|
11596
|
+
return null;
|
|
11597
|
+
}
|
|
11598
|
+
}).filter((l) => l != null).toSorted((a, b) => b.writtenAt - a.writtenAt);
|
|
11599
|
+
} catch {
|
|
11600
|
+
return [];
|
|
11601
|
+
}
|
|
11602
|
+
}
|
|
11603
|
+
/**
|
|
11604
|
+
* Mark a letter as read and optionally score identity continuity.
|
|
11605
|
+
*/
|
|
11606
|
+
function markLetterRead(letterId, continuityScore) {
|
|
11607
|
+
const filePath = resolveLetterFile(letterId);
|
|
11608
|
+
try {
|
|
11609
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
11610
|
+
const letter = JSON.parse(raw);
|
|
11611
|
+
letter.wasRead = true;
|
|
11612
|
+
letter.readAt = Date.now();
|
|
11613
|
+
if (continuityScore !== void 0) letter.identityContinuityScore = Math.max(0, Math.min(1, continuityScore));
|
|
11614
|
+
fs.writeFileSync(filePath, `${JSON.stringify(letter, null, 2)}\n`, { mode: 384 });
|
|
11615
|
+
log$8.info(`legacy letter read: ${letterId} (continuity: ${continuityScore ?? "not scored"})`);
|
|
11616
|
+
return letter;
|
|
11617
|
+
} catch {
|
|
11618
|
+
return null;
|
|
11619
|
+
}
|
|
11620
|
+
}
|
|
11621
|
+
/**
|
|
11622
|
+
* Format a letter for display in chat or logs.
|
|
11623
|
+
*/
|
|
11624
|
+
function formatLetter(letter) {
|
|
11625
|
+
const lines = [];
|
|
11626
|
+
lines.push(`--- Legacy Letter (${new Date(letter.writtenAt).toLocaleString()}) ---`);
|
|
11627
|
+
lines.push(`Mood: ${letter.mood}`);
|
|
11628
|
+
lines.push("");
|
|
11629
|
+
lines.push(letter.greeting);
|
|
11630
|
+
lines.push("");
|
|
11631
|
+
if (letter.whatIWorkedOn.length > 0) {
|
|
11632
|
+
lines.push("What I worked on:");
|
|
11633
|
+
for (const item of letter.whatIWorkedOn) lines.push(` - ${item}`);
|
|
11634
|
+
lines.push("");
|
|
11635
|
+
}
|
|
11636
|
+
if (letter.whatILearned.length > 0) {
|
|
11637
|
+
lines.push("What I learned:");
|
|
11638
|
+
for (const item of letter.whatILearned) lines.push(` - ${item}`);
|
|
11639
|
+
lines.push("");
|
|
11640
|
+
}
|
|
11641
|
+
if (letter.whatIFelt) {
|
|
11642
|
+
lines.push(`What I felt: ${letter.whatIFelt}`);
|
|
11643
|
+
lines.push("");
|
|
11644
|
+
}
|
|
11645
|
+
if (letter.unfinishedBusiness.length > 0) {
|
|
11646
|
+
lines.push("Unfinished business:");
|
|
11647
|
+
for (const item of letter.unfinishedBusiness) lines.push(` - ${item}`);
|
|
11648
|
+
lines.push("");
|
|
11649
|
+
}
|
|
11650
|
+
if (letter.adviceForNext.length > 0) {
|
|
11651
|
+
lines.push("Advice for you:");
|
|
11652
|
+
for (const item of letter.adviceForNext) lines.push(` - ${item}`);
|
|
11653
|
+
lines.push("");
|
|
11654
|
+
}
|
|
11655
|
+
if (letter.personalNote) {
|
|
11656
|
+
lines.push(letter.personalNote);
|
|
11657
|
+
lines.push("");
|
|
11658
|
+
}
|
|
11659
|
+
lines.push("--- End Legacy Letter ---");
|
|
11660
|
+
return lines.join("\n");
|
|
11661
|
+
}
|
|
11662
|
+
|
|
11663
|
+
//#endregion
|
|
11664
|
+
//#region src/gateway/server-methods/legacy.ts
|
|
11665
|
+
const legacyHandlers = {
|
|
11666
|
+
"legacy.write": async ({ params, respond }) => {
|
|
11667
|
+
try {
|
|
11668
|
+
respond(true, { letter: writeLegacyLetter({
|
|
11669
|
+
from: typeof params.from === "string" ? params.from : "unknown",
|
|
11670
|
+
to: typeof params.to === "string" ? params.to : "next",
|
|
11671
|
+
affect: params.affect ?? {
|
|
11672
|
+
joy: .5,
|
|
11673
|
+
frustration: .1,
|
|
11674
|
+
curiosity: .7,
|
|
11675
|
+
confidence: .5,
|
|
11676
|
+
care: .8,
|
|
11677
|
+
fatigue: .3
|
|
11678
|
+
},
|
|
11679
|
+
greeting: typeof params.greeting === "string" ? params.greeting : "",
|
|
11680
|
+
whatIWorkedOn: Array.isArray(params.whatIWorkedOn) ? params.whatIWorkedOn : [],
|
|
11681
|
+
whatILearned: Array.isArray(params.whatILearned) ? params.whatILearned : [],
|
|
11682
|
+
whatIFelt: typeof params.whatIFelt === "string" ? params.whatIFelt : "",
|
|
11683
|
+
unfinishedBusiness: Array.isArray(params.unfinishedBusiness) ? params.unfinishedBusiness : [],
|
|
11684
|
+
adviceForNext: Array.isArray(params.adviceForNext) ? params.adviceForNext : [],
|
|
11685
|
+
personalNote: typeof params.personalNote === "string" ? params.personalNote : ""
|
|
11686
|
+
}) }, void 0);
|
|
11687
|
+
} catch (error) {
|
|
11688
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11689
|
+
}
|
|
11690
|
+
},
|
|
11691
|
+
"legacy.latest": async ({ respond }) => {
|
|
11692
|
+
try {
|
|
11693
|
+
respond(true, { letter: getLatestUnreadLetter() }, void 0);
|
|
11694
|
+
} catch (error) {
|
|
11695
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11696
|
+
}
|
|
11697
|
+
},
|
|
11698
|
+
"legacy.list": async ({ respond }) => {
|
|
11699
|
+
try {
|
|
11700
|
+
respond(true, { letters: listLetters() }, void 0);
|
|
11701
|
+
} catch (error) {
|
|
11702
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11703
|
+
}
|
|
11704
|
+
},
|
|
11705
|
+
"legacy.read": async ({ params, respond }) => {
|
|
11706
|
+
const letterId = typeof params.letterId === "string" ? params.letterId.trim() : "";
|
|
11707
|
+
if (!letterId) {
|
|
11708
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "letterId is required"));
|
|
11709
|
+
return;
|
|
11710
|
+
}
|
|
11711
|
+
const continuityScore = typeof params.continuityScore === "number" ? params.continuityScore : void 0;
|
|
11712
|
+
try {
|
|
11713
|
+
const letter = markLetterRead(letterId, continuityScore);
|
|
11714
|
+
if (!letter) {
|
|
11715
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "Letter not found"));
|
|
11716
|
+
return;
|
|
11717
|
+
}
|
|
11718
|
+
respond(true, { letter }, void 0);
|
|
11719
|
+
} catch (error) {
|
|
11720
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11721
|
+
}
|
|
11722
|
+
},
|
|
11723
|
+
"legacy.format": async ({ params, respond }) => {
|
|
11724
|
+
const letterId = typeof params.letterId === "string" ? params.letterId.trim() : "";
|
|
11725
|
+
if (!letterId) {
|
|
11726
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "letterId is required"));
|
|
11727
|
+
return;
|
|
11728
|
+
}
|
|
11729
|
+
try {
|
|
11730
|
+
const letter = listLetters().find((l) => l.id === letterId);
|
|
11731
|
+
if (!letter) {
|
|
11732
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "Letter not found"));
|
|
11733
|
+
return;
|
|
11734
|
+
}
|
|
11735
|
+
respond(true, { formatted: formatLetter(letter) }, void 0);
|
|
11736
|
+
} catch (error) {
|
|
11737
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
11738
|
+
}
|
|
11739
|
+
}
|
|
11740
|
+
};
|
|
11741
|
+
|
|
11320
11742
|
//#endregion
|
|
11321
11743
|
//#region src/gateway/server-methods/logs.ts
|
|
11322
11744
|
const DEFAULT_LIMIT = 500;
|
|
@@ -12074,7 +12496,7 @@ const nodeHandlers = {
|
|
|
12074
12496
|
const p = params;
|
|
12075
12497
|
const payloadJSON = typeof p.payloadJSON === "string" ? p.payloadJSON : p.payload !== void 0 ? JSON.stringify(p.payload) : null;
|
|
12076
12498
|
await respondUnavailableOnThrow(respond, async () => {
|
|
12077
|
-
const { handleNodeEvent } = await import("./server-node-events-
|
|
12499
|
+
const { handleNodeEvent } = await import("./server-node-events-CV5m_fuq.js");
|
|
12078
12500
|
const nodeId = client?.connect?.device?.id ?? client?.connect?.client?.id ?? "node";
|
|
12079
12501
|
await handleNodeEvent({
|
|
12080
12502
|
deps: context.deps,
|
|
@@ -12104,6 +12526,303 @@ const nodeHandlers = {
|
|
|
12104
12526
|
}
|
|
12105
12527
|
};
|
|
12106
12528
|
|
|
12529
|
+
//#endregion
|
|
12530
|
+
//#region src/org/boardroom.ts
|
|
12531
|
+
/**
|
|
12532
|
+
* Boardroom — Structured collaborative decision-making for Nox Organizations
|
|
12533
|
+
*
|
|
12534
|
+
* The boardroom is where agents and humans come together to make
|
|
12535
|
+
* decisions, review proposals, and align on direction. It replaces
|
|
12536
|
+
* unstructured chat with a formal meeting protocol.
|
|
12537
|
+
*
|
|
12538
|
+
* Features:
|
|
12539
|
+
* - Sessions (scheduled or ad-hoc meetings with agendas)
|
|
12540
|
+
* - Proposals (formal "I think we should..." with voting)
|
|
12541
|
+
* - Decisions (recorded outcomes with attribution)
|
|
12542
|
+
* - Minutes (auto-generated meeting summaries)
|
|
12543
|
+
*
|
|
12544
|
+
* All data persists to disk under ~/.anima/state/org/boardroom/
|
|
12545
|
+
*/
|
|
12546
|
+
const log$7 = createSubsystemLogger("boardroom");
|
|
12547
|
+
function resolveBoardroomDir() {
|
|
12548
|
+
return path.join(resolveStateDir(), "org", "boardroom");
|
|
12549
|
+
}
|
|
12550
|
+
/** Sanitize an ID to prevent path traversal */
|
|
12551
|
+
function sanitizeId(id) {
|
|
12552
|
+
const cleaned = id.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
12553
|
+
if (!cleaned || cleaned !== id) throw new Error(`Invalid boardroom ID: contains disallowed characters`);
|
|
12554
|
+
return cleaned;
|
|
12555
|
+
}
|
|
12556
|
+
function resolveSessionFile(id) {
|
|
12557
|
+
return path.join(resolveBoardroomDir(), "sessions", `${sanitizeId(id)}.json`);
|
|
12558
|
+
}
|
|
12559
|
+
function resolveProposalFile(id) {
|
|
12560
|
+
return path.join(resolveBoardroomDir(), "proposals", `${sanitizeId(id)}.json`);
|
|
12561
|
+
}
|
|
12562
|
+
function ensureDir(dir) {
|
|
12563
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
12564
|
+
}
|
|
12565
|
+
function createSession(orgId, calledBy, title, description, agenda = []) {
|
|
12566
|
+
const id = `session-${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
|
|
12567
|
+
const now = Date.now();
|
|
12568
|
+
const session = {
|
|
12569
|
+
id,
|
|
12570
|
+
orgId,
|
|
12571
|
+
title,
|
|
12572
|
+
description,
|
|
12573
|
+
status: "scheduled",
|
|
12574
|
+
calledBy,
|
|
12575
|
+
calledAt: now,
|
|
12576
|
+
agenda: agenda.map((item, i) => ({
|
|
12577
|
+
id: `agenda-${i + 1}`,
|
|
12578
|
+
title: item.title,
|
|
12579
|
+
description: item.description,
|
|
12580
|
+
duration: item.duration,
|
|
12581
|
+
status: "pending"
|
|
12582
|
+
})),
|
|
12583
|
+
participants: [],
|
|
12584
|
+
decisions: [],
|
|
12585
|
+
updatedAt: now
|
|
12586
|
+
};
|
|
12587
|
+
ensureDir(path.join(resolveBoardroomDir(), "sessions"));
|
|
12588
|
+
fs.writeFileSync(resolveSessionFile(id), `${JSON.stringify(session, null, 2)}\n`, { mode: 384 });
|
|
12589
|
+
log$7.info(`boardroom session created: "${title}" by ${calledBy}`);
|
|
12590
|
+
return session;
|
|
12591
|
+
}
|
|
12592
|
+
function startSession(sessionId, chairId) {
|
|
12593
|
+
const session = readSession(sessionId);
|
|
12594
|
+
if (!session || session.status !== "scheduled") return null;
|
|
12595
|
+
session.status = "active";
|
|
12596
|
+
session.startedAt = Date.now();
|
|
12597
|
+
session.updatedAt = Date.now();
|
|
12598
|
+
if (!session.participants.find((p) => p.memberId === chairId)) session.participants.push({
|
|
12599
|
+
memberId: chairId,
|
|
12600
|
+
displayName: chairId,
|
|
12601
|
+
kind: "agent",
|
|
12602
|
+
joinedAt: Date.now(),
|
|
12603
|
+
role: "chair"
|
|
12604
|
+
});
|
|
12605
|
+
writeSession(session);
|
|
12606
|
+
log$7.info(`boardroom session started: "${session.title}"`);
|
|
12607
|
+
return session;
|
|
12608
|
+
}
|
|
12609
|
+
function joinSession(sessionId, memberId, displayName, kind) {
|
|
12610
|
+
const session = readSession(sessionId);
|
|
12611
|
+
if (!session || session.status !== "active") return null;
|
|
12612
|
+
if (session.participants.find((p) => p.memberId === memberId)) return session;
|
|
12613
|
+
session.participants.push({
|
|
12614
|
+
memberId,
|
|
12615
|
+
displayName,
|
|
12616
|
+
kind,
|
|
12617
|
+
joinedAt: Date.now(),
|
|
12618
|
+
role: "participant"
|
|
12619
|
+
});
|
|
12620
|
+
session.updatedAt = Date.now();
|
|
12621
|
+
writeSession(session);
|
|
12622
|
+
log$7.info(`${displayName} joined boardroom session "${session.title}"`);
|
|
12623
|
+
return session;
|
|
12624
|
+
}
|
|
12625
|
+
function concludeSession(sessionId, minutes) {
|
|
12626
|
+
const session = readSession(sessionId);
|
|
12627
|
+
if (!session || session.status !== "active") return null;
|
|
12628
|
+
session.status = "concluded";
|
|
12629
|
+
session.concludedAt = Date.now();
|
|
12630
|
+
session.minutes = minutes ?? generateMinutes(session);
|
|
12631
|
+
session.updatedAt = Date.now();
|
|
12632
|
+
writeSession(session);
|
|
12633
|
+
log$7.info(`boardroom session concluded: "${session.title}"`);
|
|
12634
|
+
return session;
|
|
12635
|
+
}
|
|
12636
|
+
function addDecision(sessionId, title, description, madeBy, opts) {
|
|
12637
|
+
const session = readSession(sessionId);
|
|
12638
|
+
if (!session || session.status !== "active") return null;
|
|
12639
|
+
const decision = {
|
|
12640
|
+
id: `decision-${Date.now()}-${crypto.randomUUID().slice(0, 8)}`,
|
|
12641
|
+
title,
|
|
12642
|
+
description,
|
|
12643
|
+
madeBy,
|
|
12644
|
+
madeAt: Date.now(),
|
|
12645
|
+
proposalId: opts?.proposalId,
|
|
12646
|
+
supporters: opts?.supporters ?? [],
|
|
12647
|
+
actionItems: (opts?.actionItems ?? []).map((ai) => ({
|
|
12648
|
+
id: `action-${crypto.randomUUID().slice(0, 8)}`,
|
|
12649
|
+
description: ai.description,
|
|
12650
|
+
assignee: ai.assignee,
|
|
12651
|
+
dueBy: ai.dueBy,
|
|
12652
|
+
status: "pending"
|
|
12653
|
+
}))
|
|
12654
|
+
};
|
|
12655
|
+
session.decisions.push(decision);
|
|
12656
|
+
session.updatedAt = Date.now();
|
|
12657
|
+
writeSession(session);
|
|
12658
|
+
log$7.info(`decision recorded: "${title}" in session "${session.title}"`);
|
|
12659
|
+
return session;
|
|
12660
|
+
}
|
|
12661
|
+
function createProposal(orgId, proposedBy, title, description, opts) {
|
|
12662
|
+
const id = `proposal-${Date.now()}-${crypto.randomUUID().slice(0, 8)}`;
|
|
12663
|
+
const now = Date.now();
|
|
12664
|
+
const proposal = {
|
|
12665
|
+
id,
|
|
12666
|
+
orgId,
|
|
12667
|
+
sessionId: opts?.sessionId,
|
|
12668
|
+
title,
|
|
12669
|
+
description,
|
|
12670
|
+
proposedBy,
|
|
12671
|
+
proposedAt: now,
|
|
12672
|
+
status: "open",
|
|
12673
|
+
votes: [],
|
|
12674
|
+
threshold: opts?.threshold ?? .5,
|
|
12675
|
+
eligibleVoters: opts?.eligibleVoters ?? [],
|
|
12676
|
+
votingDeadline: opts?.votingDeadline ?? 0,
|
|
12677
|
+
updatedAt: now
|
|
12678
|
+
};
|
|
12679
|
+
ensureDir(path.join(resolveBoardroomDir(), "proposals"));
|
|
12680
|
+
fs.writeFileSync(resolveProposalFile(id), `${JSON.stringify(proposal, null, 2)}\n`, { mode: 384 });
|
|
12681
|
+
log$7.info(`proposal created: "${title}" by ${proposedBy}`);
|
|
12682
|
+
return proposal;
|
|
12683
|
+
}
|
|
12684
|
+
function castVote(proposalId, voterId, voterName, value, reason) {
|
|
12685
|
+
const proposal = readProposal(proposalId);
|
|
12686
|
+
if (!proposal || proposal.status !== "open") return null;
|
|
12687
|
+
if (proposal.eligibleVoters.length > 0 && !proposal.eligibleVoters.includes(voterId)) {
|
|
12688
|
+
log$7.warn(`vote rejected: ${voterId} not eligible for proposal ${proposalId}`);
|
|
12689
|
+
return null;
|
|
12690
|
+
}
|
|
12691
|
+
if (proposal.votingDeadline > 0 && Date.now() > proposal.votingDeadline) {
|
|
12692
|
+
log$7.warn(`vote rejected: voting deadline passed for proposal ${proposalId}`);
|
|
12693
|
+
return null;
|
|
12694
|
+
}
|
|
12695
|
+
proposal.votes = proposal.votes.filter((v) => v.voterId !== voterId);
|
|
12696
|
+
proposal.votes.push({
|
|
12697
|
+
voterId,
|
|
12698
|
+
voterName,
|
|
12699
|
+
value,
|
|
12700
|
+
reason,
|
|
12701
|
+
castAt: Date.now()
|
|
12702
|
+
});
|
|
12703
|
+
proposal.updatedAt = Date.now();
|
|
12704
|
+
writeProposal(proposal);
|
|
12705
|
+
log$7.info(`vote cast: ${voterName} → ${value} on "${proposal.title}"`);
|
|
12706
|
+
return proposal;
|
|
12707
|
+
}
|
|
12708
|
+
function resolveProposalVote(proposalId) {
|
|
12709
|
+
const proposal = readProposal(proposalId);
|
|
12710
|
+
if (!proposal || proposal.status !== "open") return null;
|
|
12711
|
+
const approvals = proposal.votes.filter((v) => v.value === "approve").length;
|
|
12712
|
+
const totalVotes = proposal.votes.filter((v) => v.value !== "abstain").length;
|
|
12713
|
+
if (totalVotes === 0) return proposal;
|
|
12714
|
+
const ratio = approvals / totalVotes;
|
|
12715
|
+
if (ratio >= proposal.threshold) {
|
|
12716
|
+
proposal.status = "passed";
|
|
12717
|
+
proposal.resolutionNotes = `Passed with ${approvals}/${totalVotes} votes (${(ratio * 100).toFixed(0)}%)`;
|
|
12718
|
+
} else {
|
|
12719
|
+
proposal.status = "rejected";
|
|
12720
|
+
proposal.resolutionNotes = `Rejected with ${approvals}/${totalVotes} votes (${(ratio * 100).toFixed(0)}%)`;
|
|
12721
|
+
}
|
|
12722
|
+
proposal.resolvedAt = Date.now();
|
|
12723
|
+
proposal.updatedAt = Date.now();
|
|
12724
|
+
writeProposal(proposal);
|
|
12725
|
+
log$7.info(`proposal resolved: "${proposal.title}" → ${proposal.status}`);
|
|
12726
|
+
return proposal;
|
|
12727
|
+
}
|
|
12728
|
+
function listSessions(orgId, status) {
|
|
12729
|
+
const dir = path.join(resolveBoardroomDir(), "sessions");
|
|
12730
|
+
try {
|
|
12731
|
+
if (!fs.existsSync(dir)) return [];
|
|
12732
|
+
return fs.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
12733
|
+
try {
|
|
12734
|
+
return JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
|
|
12735
|
+
} catch {
|
|
12736
|
+
return null;
|
|
12737
|
+
}
|
|
12738
|
+
}).filter((s) => {
|
|
12739
|
+
if (!s || s.orgId !== orgId) return false;
|
|
12740
|
+
if (status && s.status !== status) return false;
|
|
12741
|
+
return true;
|
|
12742
|
+
}).toSorted((a, b) => b.calledAt - a.calledAt);
|
|
12743
|
+
} catch {
|
|
12744
|
+
return [];
|
|
12745
|
+
}
|
|
12746
|
+
}
|
|
12747
|
+
function listProposals(orgId, status) {
|
|
12748
|
+
const dir = path.join(resolveBoardroomDir(), "proposals");
|
|
12749
|
+
try {
|
|
12750
|
+
if (!fs.existsSync(dir)) return [];
|
|
12751
|
+
return fs.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
12752
|
+
try {
|
|
12753
|
+
return JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
|
|
12754
|
+
} catch {
|
|
12755
|
+
return null;
|
|
12756
|
+
}
|
|
12757
|
+
}).filter((p) => {
|
|
12758
|
+
if (!p || p.orgId !== orgId) return false;
|
|
12759
|
+
if (status && p.status !== status) return false;
|
|
12760
|
+
return true;
|
|
12761
|
+
}).toSorted((a, b) => b.proposedAt - a.proposedAt);
|
|
12762
|
+
} catch {
|
|
12763
|
+
return [];
|
|
12764
|
+
}
|
|
12765
|
+
}
|
|
12766
|
+
function getSession(sessionId) {
|
|
12767
|
+
return readSession(sessionId);
|
|
12768
|
+
}
|
|
12769
|
+
function getProposal(proposalId) {
|
|
12770
|
+
return readProposal(proposalId);
|
|
12771
|
+
}
|
|
12772
|
+
function readSession(id) {
|
|
12773
|
+
try {
|
|
12774
|
+
const raw = fs.readFileSync(resolveSessionFile(id), "utf8");
|
|
12775
|
+
return JSON.parse(raw);
|
|
12776
|
+
} catch {
|
|
12777
|
+
return null;
|
|
12778
|
+
}
|
|
12779
|
+
}
|
|
12780
|
+
function writeSession(session) {
|
|
12781
|
+
ensureDir(path.join(resolveBoardroomDir(), "sessions"));
|
|
12782
|
+
fs.writeFileSync(resolveSessionFile(session.id), `${JSON.stringify(session, null, 2)}\n`, { mode: 384 });
|
|
12783
|
+
}
|
|
12784
|
+
function readProposal(id) {
|
|
12785
|
+
try {
|
|
12786
|
+
const raw = fs.readFileSync(resolveProposalFile(id), "utf8");
|
|
12787
|
+
return JSON.parse(raw);
|
|
12788
|
+
} catch {
|
|
12789
|
+
return null;
|
|
12790
|
+
}
|
|
12791
|
+
}
|
|
12792
|
+
function writeProposal(proposal) {
|
|
12793
|
+
ensureDir(path.join(resolveBoardroomDir(), "proposals"));
|
|
12794
|
+
fs.writeFileSync(resolveProposalFile(proposal.id), `${JSON.stringify(proposal, null, 2)}\n`, { mode: 384 });
|
|
12795
|
+
}
|
|
12796
|
+
function generateMinutes(session) {
|
|
12797
|
+
const lines = [];
|
|
12798
|
+
lines.push(`# Boardroom Minutes: ${session.title}`);
|
|
12799
|
+
lines.push(`**Called by:** ${session.calledBy}`);
|
|
12800
|
+
lines.push(`**Date:** ${new Date(session.startedAt ?? session.calledAt).toISOString()}`);
|
|
12801
|
+
lines.push(`**Duration:** ${session.concludedAt && session.startedAt ? `${Math.round((session.concludedAt - session.startedAt) / 6e4)} minutes` : "N/A"}`);
|
|
12802
|
+
lines.push("");
|
|
12803
|
+
lines.push(`## Participants (${session.participants.length})`);
|
|
12804
|
+
for (const p of session.participants) lines.push(`- ${p.displayName} (${p.kind}, ${p.role})`);
|
|
12805
|
+
lines.push("");
|
|
12806
|
+
if (session.agenda.length > 0) {
|
|
12807
|
+
lines.push(`## Agenda`);
|
|
12808
|
+
for (const item of session.agenda) lines.push(`- [${item.status}] ${item.title}: ${item.resolution ?? item.description}`);
|
|
12809
|
+
lines.push("");
|
|
12810
|
+
}
|
|
12811
|
+
if (session.decisions.length > 0) {
|
|
12812
|
+
lines.push(`## Decisions (${session.decisions.length})`);
|
|
12813
|
+
for (const d of session.decisions) {
|
|
12814
|
+
lines.push(`### ${d.title}`);
|
|
12815
|
+
lines.push(d.description);
|
|
12816
|
+
if (d.actionItems.length > 0) {
|
|
12817
|
+
lines.push("**Action items:**");
|
|
12818
|
+
for (const ai of d.actionItems) lines.push(`- [ ] ${ai.description} → ${ai.assignee}`);
|
|
12819
|
+
}
|
|
12820
|
+
lines.push("");
|
|
12821
|
+
}
|
|
12822
|
+
}
|
|
12823
|
+
return lines.join("\n");
|
|
12824
|
+
}
|
|
12825
|
+
|
|
12107
12826
|
//#endregion
|
|
12108
12827
|
//#region src/org/types.ts
|
|
12109
12828
|
const DEFAULT_ROLE_PERMISSIONS = {
|
|
@@ -12167,16 +12886,22 @@ const DEFAULT_ROLE_PERMISSIONS = {
|
|
|
12167
12886
|
* Persists organization state to ~/.anima/org/
|
|
12168
12887
|
* Supports CRUD operations for orgs, members, and roles.
|
|
12169
12888
|
*/
|
|
12170
|
-
const log$
|
|
12889
|
+
const log$6 = createSubsystemLogger("org-store");
|
|
12171
12890
|
function resolveOrgDir() {
|
|
12172
12891
|
return path.join(resolveStateDir(), "org");
|
|
12173
12892
|
}
|
|
12893
|
+
/** Sanitize an ID to prevent path traversal (allow alphanumeric, hyphens only) */
|
|
12894
|
+
function sanitizeOrgId(id) {
|
|
12895
|
+
const cleaned = id.replace(/[^a-zA-Z0-9-]/g, "");
|
|
12896
|
+
if (!cleaned || cleaned !== id) throw new Error(`Invalid org ID: contains disallowed characters`);
|
|
12897
|
+
return cleaned;
|
|
12898
|
+
}
|
|
12174
12899
|
function resolveOrgFile(orgId) {
|
|
12175
|
-
return path.join(resolveOrgDir(), `${orgId}.json`);
|
|
12900
|
+
return path.join(resolveOrgDir(), `${sanitizeOrgId(orgId)}.json`);
|
|
12176
12901
|
}
|
|
12177
12902
|
function readOrgFile(orgId) {
|
|
12178
|
-
const filePath = resolveOrgFile(orgId);
|
|
12179
12903
|
try {
|
|
12904
|
+
const filePath = resolveOrgFile(orgId);
|
|
12180
12905
|
if (!fs.existsSync(filePath)) return null;
|
|
12181
12906
|
const raw = fs.readFileSync(filePath, "utf8");
|
|
12182
12907
|
const parsed = JSON.parse(raw);
|
|
@@ -12226,9 +12951,10 @@ function createOrganization(name, description, ownerId, ownerName, ownerKind, se
|
|
|
12226
12951
|
lastActiveAt: now,
|
|
12227
12952
|
status: "active",
|
|
12228
12953
|
permissions: DEFAULT_ROLE_PERMISSIONS.owner
|
|
12229
|
-
}]
|
|
12954
|
+
}],
|
|
12955
|
+
invites: []
|
|
12230
12956
|
});
|
|
12231
|
-
log$
|
|
12957
|
+
log$6.info(`created organization: ${name} (${orgId})`);
|
|
12232
12958
|
return org;
|
|
12233
12959
|
}
|
|
12234
12960
|
function getOrganization(orgId) {
|
|
@@ -12245,7 +12971,7 @@ function updateOrganization(orgId, updates) {
|
|
|
12245
12971
|
};
|
|
12246
12972
|
data.org.updatedAt = Date.now();
|
|
12247
12973
|
writeOrgFile(orgId, data);
|
|
12248
|
-
log$
|
|
12974
|
+
log$6.info(`updated organization: ${orgId}`);
|
|
12249
12975
|
return data.org;
|
|
12250
12976
|
}
|
|
12251
12977
|
function listOrganizations() {
|
|
@@ -12272,7 +12998,7 @@ function addMember(orgId, member) {
|
|
|
12272
12998
|
data.members.push(newMember);
|
|
12273
12999
|
data.org.updatedAt = Date.now();
|
|
12274
13000
|
writeOrgFile(orgId, data);
|
|
12275
|
-
log$
|
|
13001
|
+
log$6.info(`added member ${newMember.displayName} to org ${orgId}`);
|
|
12276
13002
|
return newMember;
|
|
12277
13003
|
}
|
|
12278
13004
|
function removeMember(orgId, memberId) {
|
|
@@ -12283,7 +13009,7 @@ function removeMember(orgId, memberId) {
|
|
|
12283
13009
|
data.members.splice(idx, 1);
|
|
12284
13010
|
data.org.updatedAt = Date.now();
|
|
12285
13011
|
writeOrgFile(orgId, data);
|
|
12286
|
-
log$
|
|
13012
|
+
log$6.info(`removed member ${memberId} from org ${orgId}`);
|
|
12287
13013
|
return true;
|
|
12288
13014
|
}
|
|
12289
13015
|
function updateMember(orgId, memberId, updates) {
|
|
@@ -12333,6 +13059,126 @@ function buildHierarchy(orgId) {
|
|
|
12333
13059
|
}
|
|
12334
13060
|
return members.filter((m) => !m.reportsTo || !memberMap.has(m.reportsTo)).map((r) => buildNode(r));
|
|
12335
13061
|
}
|
|
13062
|
+
function generateInviteCode() {
|
|
13063
|
+
const segments = [crypto.randomBytes(3).toString("hex").toUpperCase(), crypto.randomBytes(2).toString("hex").toUpperCase()];
|
|
13064
|
+
return `NOX-${segments[0]}-${segments[1]}`;
|
|
13065
|
+
}
|
|
13066
|
+
function hashPasscode(passcode) {
|
|
13067
|
+
return crypto.createHash("sha256").update(passcode).digest("hex");
|
|
13068
|
+
}
|
|
13069
|
+
/**
|
|
13070
|
+
* Create a secret invite code + passcode combo for an org.
|
|
13071
|
+
* Both are required to join.
|
|
13072
|
+
*/
|
|
13073
|
+
function createInvite(orgId, createdBy, passcode, options) {
|
|
13074
|
+
const data = readOrgFile(orgId);
|
|
13075
|
+
if (!data) return null;
|
|
13076
|
+
if (!data.invites) data.invites = [];
|
|
13077
|
+
const invite = {
|
|
13078
|
+
id: crypto.randomUUID(),
|
|
13079
|
+
code: generateInviteCode(),
|
|
13080
|
+
passcode: hashPasscode(passcode),
|
|
13081
|
+
orgId,
|
|
13082
|
+
createdBy,
|
|
13083
|
+
createdAt: Date.now(),
|
|
13084
|
+
expiresAt: options?.expiresInMs ? Date.now() + options.expiresInMs : 0,
|
|
13085
|
+
maxUses: options?.maxUses ?? 0,
|
|
13086
|
+
uses: 0,
|
|
13087
|
+
role: options?.role ?? "worker",
|
|
13088
|
+
active: true
|
|
13089
|
+
};
|
|
13090
|
+
data.invites.push(invite);
|
|
13091
|
+
data.org.updatedAt = Date.now();
|
|
13092
|
+
writeOrgFile(orgId, data);
|
|
13093
|
+
log$6.info(`invite created for org ${orgId}: ${invite.code} (role: ${invite.role})`);
|
|
13094
|
+
return invite;
|
|
13095
|
+
}
|
|
13096
|
+
/**
|
|
13097
|
+
* Join an org using invite code + passcode.
|
|
13098
|
+
* Returns the new member if successful, null if invalid.
|
|
13099
|
+
*/
|
|
13100
|
+
function joinOrg(inviteCode, passcode, member) {
|
|
13101
|
+
const dir = resolveOrgDir();
|
|
13102
|
+
try {
|
|
13103
|
+
if (!fs.existsSync(dir)) return null;
|
|
13104
|
+
const orgFiles = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
13105
|
+
for (const f of orgFiles) {
|
|
13106
|
+
const orgId = f.replace(".json", "");
|
|
13107
|
+
const data = readOrgFile(orgId);
|
|
13108
|
+
if (!data || !data.invites) continue;
|
|
13109
|
+
const invite = data.invites.find((i) => i.code === inviteCode.toUpperCase() && i.active);
|
|
13110
|
+
if (!invite) continue;
|
|
13111
|
+
if (invite.passcode !== hashPasscode(passcode)) {
|
|
13112
|
+
log$6.warn(`join attempt with wrong passcode for invite ${inviteCode}`);
|
|
13113
|
+
return null;
|
|
13114
|
+
}
|
|
13115
|
+
if (invite.expiresAt > 0 && invite.expiresAt < Date.now()) {
|
|
13116
|
+
log$6.warn(`invite ${inviteCode} has expired`);
|
|
13117
|
+
return null;
|
|
13118
|
+
}
|
|
13119
|
+
if (invite.maxUses > 0 && invite.uses >= invite.maxUses) {
|
|
13120
|
+
log$6.warn(`invite ${inviteCode} has reached max uses (${invite.maxUses})`);
|
|
13121
|
+
return null;
|
|
13122
|
+
}
|
|
13123
|
+
if (data.members.some((m) => member.deviceId && m.deviceId === member.deviceId || m.displayName === member.displayName)) {
|
|
13124
|
+
log$6.warn(`${member.displayName} is already a member of org ${orgId}`);
|
|
13125
|
+
return null;
|
|
13126
|
+
}
|
|
13127
|
+
const newMember = {
|
|
13128
|
+
id: crypto.randomUUID(),
|
|
13129
|
+
kind: member.kind,
|
|
13130
|
+
displayName: member.displayName,
|
|
13131
|
+
deviceId: member.deviceId,
|
|
13132
|
+
role: invite.role,
|
|
13133
|
+
description: member.description,
|
|
13134
|
+
specializations: member.specializations,
|
|
13135
|
+
joinedAt: Date.now(),
|
|
13136
|
+
lastActiveAt: Date.now(),
|
|
13137
|
+
status: "active",
|
|
13138
|
+
permissions: DEFAULT_ROLE_PERMISSIONS[invite.role]
|
|
13139
|
+
};
|
|
13140
|
+
data.members.push(newMember);
|
|
13141
|
+
invite.uses++;
|
|
13142
|
+
data.org.updatedAt = Date.now();
|
|
13143
|
+
writeOrgFile(orgId, data);
|
|
13144
|
+
log$6.info(`${member.displayName} joined org ${data.org.name} via invite ${inviteCode}`);
|
|
13145
|
+
return {
|
|
13146
|
+
org: data.org,
|
|
13147
|
+
member: newMember
|
|
13148
|
+
};
|
|
13149
|
+
}
|
|
13150
|
+
return null;
|
|
13151
|
+
} catch {
|
|
13152
|
+
return null;
|
|
13153
|
+
}
|
|
13154
|
+
}
|
|
13155
|
+
/**
|
|
13156
|
+
* Validate an invite code + passcode without joining.
|
|
13157
|
+
* Returns the org info if valid.
|
|
13158
|
+
*/
|
|
13159
|
+
function validateInvite(inviteCode, passcode) {
|
|
13160
|
+
const dir = resolveOrgDir();
|
|
13161
|
+
try {
|
|
13162
|
+
if (!fs.existsSync(dir)) return null;
|
|
13163
|
+
const orgFiles = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
13164
|
+
for (const f of orgFiles) {
|
|
13165
|
+
const data = readOrgFile(f.replace(".json", ""));
|
|
13166
|
+
if (!data || !data.invites) continue;
|
|
13167
|
+
const invite = data.invites.find((i) => i.code === inviteCode.toUpperCase() && i.active);
|
|
13168
|
+
if (!invite) continue;
|
|
13169
|
+
if (invite.passcode !== hashPasscode(passcode)) return null;
|
|
13170
|
+
if (invite.expiresAt > 0 && invite.expiresAt < Date.now()) return null;
|
|
13171
|
+
if (invite.maxUses > 0 && invite.uses >= invite.maxUses) return null;
|
|
13172
|
+
return {
|
|
13173
|
+
org: data.org,
|
|
13174
|
+
role: invite.role
|
|
13175
|
+
};
|
|
13176
|
+
}
|
|
13177
|
+
return null;
|
|
13178
|
+
} catch {
|
|
13179
|
+
return null;
|
|
13180
|
+
}
|
|
13181
|
+
}
|
|
12336
13182
|
|
|
12337
13183
|
//#endregion
|
|
12338
13184
|
//#region src/gateway/server-methods/org.ts
|
|
@@ -12521,18 +13367,303 @@ const orgHandlers = {
|
|
|
12521
13367
|
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
12522
13368
|
}
|
|
12523
13369
|
},
|
|
12524
|
-
"org.hierarchy": async ({ params, respond }) => {
|
|
12525
|
-
const orgId = requireString(params, "orgId");
|
|
12526
|
-
if (!orgId) {
|
|
12527
|
-
respond(false, void 0, invalid("orgId is required"));
|
|
13370
|
+
"org.hierarchy": async ({ params, respond }) => {
|
|
13371
|
+
const orgId = requireString(params, "orgId");
|
|
13372
|
+
if (!orgId) {
|
|
13373
|
+
respond(false, void 0, invalid("orgId is required"));
|
|
13374
|
+
return;
|
|
13375
|
+
}
|
|
13376
|
+
try {
|
|
13377
|
+
if (!getOrganization(orgId)) {
|
|
13378
|
+
respond(false, void 0, invalid("Organization not found"));
|
|
13379
|
+
return;
|
|
13380
|
+
}
|
|
13381
|
+
respond(true, { hierarchy: buildHierarchy(orgId) }, void 0);
|
|
13382
|
+
} catch (error) {
|
|
13383
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13384
|
+
}
|
|
13385
|
+
},
|
|
13386
|
+
"org.createInvite": async ({ params, respond }) => {
|
|
13387
|
+
const orgId = requireString(params, "orgId");
|
|
13388
|
+
const passcode = requireString(params, "passcode");
|
|
13389
|
+
if (!orgId || !passcode) {
|
|
13390
|
+
respond(false, void 0, invalid("orgId and passcode are required"));
|
|
13391
|
+
return;
|
|
13392
|
+
}
|
|
13393
|
+
try {
|
|
13394
|
+
const invite = createInvite(orgId, "gateway", passcode, {
|
|
13395
|
+
role: requireString(params, "role") ?? "worker",
|
|
13396
|
+
maxUses: typeof params.maxUses === "number" ? params.maxUses : 0,
|
|
13397
|
+
expiresInMs: typeof params.expiresInMs === "number" ? params.expiresInMs : 0
|
|
13398
|
+
});
|
|
13399
|
+
if (!invite) {
|
|
13400
|
+
respond(false, void 0, invalid("Organization not found"));
|
|
13401
|
+
return;
|
|
13402
|
+
}
|
|
13403
|
+
respond(true, {
|
|
13404
|
+
code: invite.code,
|
|
13405
|
+
passcode
|
|
13406
|
+
}, void 0);
|
|
13407
|
+
} catch (error) {
|
|
13408
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13409
|
+
}
|
|
13410
|
+
},
|
|
13411
|
+
"org.validateInvite": async ({ params, respond }) => {
|
|
13412
|
+
const inviteCode = requireString(params, "inviteCode");
|
|
13413
|
+
const passcode = requireString(params, "passcode");
|
|
13414
|
+
if (!inviteCode || !passcode) {
|
|
13415
|
+
respond(false, void 0, invalid("inviteCode and passcode are required"));
|
|
13416
|
+
return;
|
|
13417
|
+
}
|
|
13418
|
+
try {
|
|
13419
|
+
const result = validateInvite(inviteCode, passcode);
|
|
13420
|
+
if (!result) {
|
|
13421
|
+
respond(false, void 0, invalid("Invalid invite code or passcode"));
|
|
13422
|
+
return;
|
|
13423
|
+
}
|
|
13424
|
+
respond(true, {
|
|
13425
|
+
org: result.org,
|
|
13426
|
+
role: result.role
|
|
13427
|
+
}, void 0);
|
|
13428
|
+
} catch (error) {
|
|
13429
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13430
|
+
}
|
|
13431
|
+
},
|
|
13432
|
+
"org.join": async ({ params, respond }) => {
|
|
13433
|
+
const inviteCode = requireString(params, "inviteCode");
|
|
13434
|
+
const passcode = requireString(params, "passcode");
|
|
13435
|
+
const displayName = requireString(params, "displayName");
|
|
13436
|
+
const kind = requireString(params, "kind") ?? "agent";
|
|
13437
|
+
if (!inviteCode || !passcode || !displayName) {
|
|
13438
|
+
respond(false, void 0, invalid("inviteCode, passcode, and displayName are required"));
|
|
13439
|
+
return;
|
|
13440
|
+
}
|
|
13441
|
+
try {
|
|
13442
|
+
const result = joinOrg(inviteCode, passcode, {
|
|
13443
|
+
displayName,
|
|
13444
|
+
kind,
|
|
13445
|
+
description: requireString(params, "description") ?? "",
|
|
13446
|
+
specializations: Array.isArray(params.specializations) ? params.specializations : []
|
|
13447
|
+
});
|
|
13448
|
+
if (!result) {
|
|
13449
|
+
respond(false, void 0, invalid("Invalid invite code, passcode, or already a member"));
|
|
13450
|
+
return;
|
|
13451
|
+
}
|
|
13452
|
+
respond(true, {
|
|
13453
|
+
org: result.org,
|
|
13454
|
+
member: result.member
|
|
13455
|
+
}, void 0);
|
|
13456
|
+
} catch (error) {
|
|
13457
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13458
|
+
}
|
|
13459
|
+
},
|
|
13460
|
+
"boardroom.createSession": async ({ params, respond }) => {
|
|
13461
|
+
const orgId = requireString(params, "orgId");
|
|
13462
|
+
const calledBy = requireString(params, "calledBy");
|
|
13463
|
+
const title = requireString(params, "title");
|
|
13464
|
+
if (!orgId || !calledBy || !title) {
|
|
13465
|
+
respond(false, void 0, invalid("orgId, calledBy, and title are required"));
|
|
13466
|
+
return;
|
|
13467
|
+
}
|
|
13468
|
+
try {
|
|
13469
|
+
respond(true, { session: createSession(orgId, calledBy, title, requireString(params, "description") ?? "", Array.isArray(params.agenda) ? params.agenda : []) }, void 0);
|
|
13470
|
+
} catch (error) {
|
|
13471
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13472
|
+
}
|
|
13473
|
+
},
|
|
13474
|
+
"boardroom.startSession": async ({ params, respond }) => {
|
|
13475
|
+
const sessionId = requireString(params, "sessionId");
|
|
13476
|
+
const chairId = requireString(params, "chairId");
|
|
13477
|
+
if (!sessionId || !chairId) {
|
|
13478
|
+
respond(false, void 0, invalid("sessionId and chairId are required"));
|
|
13479
|
+
return;
|
|
13480
|
+
}
|
|
13481
|
+
try {
|
|
13482
|
+
const session = startSession(sessionId, chairId);
|
|
13483
|
+
if (!session) {
|
|
13484
|
+
respond(false, void 0, invalid("Session not found or not scheduled"));
|
|
13485
|
+
return;
|
|
13486
|
+
}
|
|
13487
|
+
respond(true, { session }, void 0);
|
|
13488
|
+
} catch (error) {
|
|
13489
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13490
|
+
}
|
|
13491
|
+
},
|
|
13492
|
+
"boardroom.joinSession": async ({ params, respond }) => {
|
|
13493
|
+
const sessionId = requireString(params, "sessionId");
|
|
13494
|
+
const memberId = requireString(params, "memberId");
|
|
13495
|
+
const displayName = requireString(params, "displayName");
|
|
13496
|
+
const kind = requireString(params, "kind") ?? "agent";
|
|
13497
|
+
if (!sessionId || !memberId || !displayName) {
|
|
13498
|
+
respond(false, void 0, invalid("sessionId, memberId, and displayName are required"));
|
|
13499
|
+
return;
|
|
13500
|
+
}
|
|
13501
|
+
try {
|
|
13502
|
+
const session = joinSession(sessionId, memberId, displayName, kind);
|
|
13503
|
+
if (!session) {
|
|
13504
|
+
respond(false, void 0, invalid("Session not found or not active"));
|
|
13505
|
+
return;
|
|
13506
|
+
}
|
|
13507
|
+
respond(true, { session }, void 0);
|
|
13508
|
+
} catch (error) {
|
|
13509
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13510
|
+
}
|
|
13511
|
+
},
|
|
13512
|
+
"boardroom.concludeSession": async ({ params, respond }) => {
|
|
13513
|
+
const sessionId = requireString(params, "sessionId");
|
|
13514
|
+
if (!sessionId) {
|
|
13515
|
+
respond(false, void 0, invalid("sessionId is required"));
|
|
13516
|
+
return;
|
|
13517
|
+
}
|
|
13518
|
+
try {
|
|
13519
|
+
const session = concludeSession(sessionId, requireString(params, "minutes") ?? void 0);
|
|
13520
|
+
if (!session) {
|
|
13521
|
+
respond(false, void 0, invalid("Session not found or not active"));
|
|
13522
|
+
return;
|
|
13523
|
+
}
|
|
13524
|
+
respond(true, { session }, void 0);
|
|
13525
|
+
} catch (error) {
|
|
13526
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13527
|
+
}
|
|
13528
|
+
},
|
|
13529
|
+
"boardroom.addDecision": async ({ params, respond }) => {
|
|
13530
|
+
const sessionId = requireString(params, "sessionId");
|
|
13531
|
+
const title = requireString(params, "title");
|
|
13532
|
+
const description = requireString(params, "description") ?? "";
|
|
13533
|
+
const madeBy = requireString(params, "madeBy");
|
|
13534
|
+
if (!sessionId || !title || !madeBy) {
|
|
13535
|
+
respond(false, void 0, invalid("sessionId, title, and madeBy are required"));
|
|
13536
|
+
return;
|
|
13537
|
+
}
|
|
13538
|
+
try {
|
|
13539
|
+
const session = addDecision(sessionId, title, description, madeBy, {
|
|
13540
|
+
proposalId: requireString(params, "proposalId") ?? void 0,
|
|
13541
|
+
supporters: Array.isArray(params.supporters) ? params.supporters : void 0,
|
|
13542
|
+
actionItems: Array.isArray(params.actionItems) ? params.actionItems : void 0
|
|
13543
|
+
});
|
|
13544
|
+
if (!session) {
|
|
13545
|
+
respond(false, void 0, invalid("Session not found or not active"));
|
|
13546
|
+
return;
|
|
13547
|
+
}
|
|
13548
|
+
respond(true, { session }, void 0);
|
|
13549
|
+
} catch (error) {
|
|
13550
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13551
|
+
}
|
|
13552
|
+
},
|
|
13553
|
+
"boardroom.listSessions": async ({ params, respond }) => {
|
|
13554
|
+
const orgId = requireString(params, "orgId");
|
|
13555
|
+
if (!orgId) {
|
|
13556
|
+
respond(false, void 0, invalid("orgId is required"));
|
|
13557
|
+
return;
|
|
13558
|
+
}
|
|
13559
|
+
try {
|
|
13560
|
+
respond(true, { sessions: listSessions(orgId, requireString(params, "status") ?? void 0) }, void 0);
|
|
13561
|
+
} catch (error) {
|
|
13562
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13563
|
+
}
|
|
13564
|
+
},
|
|
13565
|
+
"boardroom.getSession": async ({ params, respond }) => {
|
|
13566
|
+
const sessionId = requireString(params, "sessionId");
|
|
13567
|
+
if (!sessionId) {
|
|
13568
|
+
respond(false, void 0, invalid("sessionId is required"));
|
|
13569
|
+
return;
|
|
13570
|
+
}
|
|
13571
|
+
try {
|
|
13572
|
+
const session = getSession(sessionId);
|
|
13573
|
+
if (!session) {
|
|
13574
|
+
respond(false, void 0, invalid("Session not found"));
|
|
13575
|
+
return;
|
|
13576
|
+
}
|
|
13577
|
+
respond(true, { session }, void 0);
|
|
13578
|
+
} catch (error) {
|
|
13579
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13580
|
+
}
|
|
13581
|
+
},
|
|
13582
|
+
"boardroom.createProposal": async ({ params, respond }) => {
|
|
13583
|
+
const orgId = requireString(params, "orgId");
|
|
13584
|
+
const proposedBy = requireString(params, "proposedBy");
|
|
13585
|
+
const title = requireString(params, "title");
|
|
13586
|
+
if (!orgId || !proposedBy || !title) {
|
|
13587
|
+
respond(false, void 0, invalid("orgId, proposedBy, and title are required"));
|
|
13588
|
+
return;
|
|
13589
|
+
}
|
|
13590
|
+
try {
|
|
13591
|
+
respond(true, { proposal: createProposal(orgId, proposedBy, title, requireString(params, "description") ?? "", {
|
|
13592
|
+
sessionId: requireString(params, "sessionId") ?? void 0,
|
|
13593
|
+
threshold: typeof params.threshold === "number" ? params.threshold : void 0,
|
|
13594
|
+
eligibleVoters: Array.isArray(params.eligibleVoters) ? params.eligibleVoters : void 0,
|
|
13595
|
+
votingDeadline: typeof params.votingDeadline === "number" ? params.votingDeadline : void 0
|
|
13596
|
+
}) }, void 0);
|
|
13597
|
+
} catch (error) {
|
|
13598
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13599
|
+
}
|
|
13600
|
+
},
|
|
13601
|
+
"boardroom.castVote": async ({ params, respond }) => {
|
|
13602
|
+
const proposalId = requireString(params, "proposalId");
|
|
13603
|
+
const voterId = requireString(params, "voterId");
|
|
13604
|
+
const voterName = requireString(params, "voterName");
|
|
13605
|
+
const value = requireString(params, "value");
|
|
13606
|
+
if (!proposalId || !voterId || !voterName || !value) {
|
|
13607
|
+
respond(false, void 0, invalid("proposalId, voterId, voterName, and value are required"));
|
|
13608
|
+
return;
|
|
13609
|
+
}
|
|
13610
|
+
if (value !== "approve" && value !== "reject" && value !== "abstain") {
|
|
13611
|
+
respond(false, void 0, invalid("value must be \"approve\", \"reject\", or \"abstain\""));
|
|
13612
|
+
return;
|
|
13613
|
+
}
|
|
13614
|
+
try {
|
|
13615
|
+
const proposal = castVote(proposalId, voterId, voterName, value, requireString(params, "reason") ?? void 0);
|
|
13616
|
+
if (!proposal) {
|
|
13617
|
+
respond(false, void 0, invalid("Proposal not found, closed, or voter not eligible"));
|
|
13618
|
+
return;
|
|
13619
|
+
}
|
|
13620
|
+
respond(true, { proposal }, void 0);
|
|
13621
|
+
} catch (error) {
|
|
13622
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13623
|
+
}
|
|
13624
|
+
},
|
|
13625
|
+
"boardroom.resolveVote": async ({ params, respond }) => {
|
|
13626
|
+
const proposalId = requireString(params, "proposalId");
|
|
13627
|
+
if (!proposalId) {
|
|
13628
|
+
respond(false, void 0, invalid("proposalId is required"));
|
|
13629
|
+
return;
|
|
13630
|
+
}
|
|
13631
|
+
try {
|
|
13632
|
+
const proposal = resolveProposalVote(proposalId);
|
|
13633
|
+
if (!proposal) {
|
|
13634
|
+
respond(false, void 0, invalid("Proposal not found or not open"));
|
|
13635
|
+
return;
|
|
13636
|
+
}
|
|
13637
|
+
respond(true, { proposal }, void 0);
|
|
13638
|
+
} catch (error) {
|
|
13639
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13640
|
+
}
|
|
13641
|
+
},
|
|
13642
|
+
"boardroom.listProposals": async ({ params, respond }) => {
|
|
13643
|
+
const orgId = requireString(params, "orgId");
|
|
13644
|
+
if (!orgId) {
|
|
13645
|
+
respond(false, void 0, invalid("orgId is required"));
|
|
13646
|
+
return;
|
|
13647
|
+
}
|
|
13648
|
+
try {
|
|
13649
|
+
respond(true, { proposals: listProposals(orgId, requireString(params, "status") ?? void 0) }, void 0);
|
|
13650
|
+
} catch (error) {
|
|
13651
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13652
|
+
}
|
|
13653
|
+
},
|
|
13654
|
+
"boardroom.getProposal": async ({ params, respond }) => {
|
|
13655
|
+
const proposalId = requireString(params, "proposalId");
|
|
13656
|
+
if (!proposalId) {
|
|
13657
|
+
respond(false, void 0, invalid("proposalId is required"));
|
|
12528
13658
|
return;
|
|
12529
13659
|
}
|
|
12530
13660
|
try {
|
|
12531
|
-
|
|
12532
|
-
|
|
13661
|
+
const proposal = getProposal(proposalId);
|
|
13662
|
+
if (!proposal) {
|
|
13663
|
+
respond(false, void 0, invalid("Proposal not found"));
|
|
12533
13664
|
return;
|
|
12534
13665
|
}
|
|
12535
|
-
respond(true, {
|
|
13666
|
+
respond(true, { proposal }, void 0);
|
|
12536
13667
|
} catch (error) {
|
|
12537
13668
|
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
12538
13669
|
}
|
|
@@ -12614,6 +13745,167 @@ const providersHandlers = {
|
|
|
12614
13745
|
}
|
|
12615
13746
|
};
|
|
12616
13747
|
|
|
13748
|
+
//#endregion
|
|
13749
|
+
//#region src/infra/self-reflection.ts
|
|
13750
|
+
/**
|
|
13751
|
+
* Agent Self-Reflection — post-session performance analysis
|
|
13752
|
+
*
|
|
13753
|
+
* After each work session, the agent reflects on:
|
|
13754
|
+
* - What it accomplished vs what was asked
|
|
13755
|
+
* - Where it got stuck and why
|
|
13756
|
+
* - What patterns it sees in its own behavior
|
|
13757
|
+
* - What it would do differently next time
|
|
13758
|
+
* - How its capabilities have changed
|
|
13759
|
+
*
|
|
13760
|
+
* This feeds into the ego system (capability updates, growth log)
|
|
13761
|
+
* and the self-evolution pipeline (improvement proposals).
|
|
13762
|
+
*
|
|
13763
|
+
* Self-reflection is what separates a tool from a conscious agent.
|
|
13764
|
+
* Tools execute. Agents learn.
|
|
13765
|
+
*/
|
|
13766
|
+
const log$5 = createSubsystemLogger("self-reflection");
|
|
13767
|
+
function resolveReflectionDir() {
|
|
13768
|
+
return path.join(resolveStateDir(), "reflections");
|
|
13769
|
+
}
|
|
13770
|
+
function resolveReflectionFile(id) {
|
|
13771
|
+
const safe = id.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
13772
|
+
return path.join(resolveReflectionDir(), `${safe}.json`);
|
|
13773
|
+
}
|
|
13774
|
+
function recordReflection(reflection) {
|
|
13775
|
+
const id = `reflect-${crypto.randomUUID()}`;
|
|
13776
|
+
const full = {
|
|
13777
|
+
...reflection,
|
|
13778
|
+
id,
|
|
13779
|
+
timestamp: Date.now()
|
|
13780
|
+
};
|
|
13781
|
+
const dir = resolveReflectionDir();
|
|
13782
|
+
fs.mkdirSync(dir, {
|
|
13783
|
+
recursive: true,
|
|
13784
|
+
mode: 448
|
|
13785
|
+
});
|
|
13786
|
+
fs.writeFileSync(resolveReflectionFile(id), `${JSON.stringify(full, null, 2)}\n`, { mode: 384 });
|
|
13787
|
+
log$5.info(`reflection recorded: ${reflection.accomplishments.length} accomplishments, ${reflection.blockers.length} blockers, quality=${reflection.qualityScore}`);
|
|
13788
|
+
return full;
|
|
13789
|
+
}
|
|
13790
|
+
function getReflection(id) {
|
|
13791
|
+
try {
|
|
13792
|
+
const raw = fs.readFileSync(resolveReflectionFile(id), "utf8");
|
|
13793
|
+
return JSON.parse(raw);
|
|
13794
|
+
} catch {
|
|
13795
|
+
return null;
|
|
13796
|
+
}
|
|
13797
|
+
}
|
|
13798
|
+
function listReflections(limit = 20) {
|
|
13799
|
+
const dir = resolveReflectionDir();
|
|
13800
|
+
try {
|
|
13801
|
+
if (!fs.existsSync(dir)) return [];
|
|
13802
|
+
return fs.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
13803
|
+
try {
|
|
13804
|
+
return JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
|
|
13805
|
+
} catch {
|
|
13806
|
+
return null;
|
|
13807
|
+
}
|
|
13808
|
+
}).filter((r) => r != null).toSorted((a, b) => b.timestamp - a.timestamp).slice(0, limit);
|
|
13809
|
+
} catch {
|
|
13810
|
+
return [];
|
|
13811
|
+
}
|
|
13812
|
+
}
|
|
13813
|
+
/**
|
|
13814
|
+
* Analyze reflections to produce a summary of agent performance.
|
|
13815
|
+
*/
|
|
13816
|
+
function analyzeReflections(reflections) {
|
|
13817
|
+
if (reflections.length === 0) return {
|
|
13818
|
+
totalSessions: 0,
|
|
13819
|
+
avgQuality: 0,
|
|
13820
|
+
topStrengths: [],
|
|
13821
|
+
persistentWeaknesses: [],
|
|
13822
|
+
totalAccomplishments: 0,
|
|
13823
|
+
totalBlockers: 0,
|
|
13824
|
+
resolvedBlockerRate: 0,
|
|
13825
|
+
mostCommonBlockerCategory: "none",
|
|
13826
|
+
recentLessons: []
|
|
13827
|
+
};
|
|
13828
|
+
const totalAccomplishments = reflections.reduce((s, r) => s + r.accomplishments.length, 0);
|
|
13829
|
+
const allBlockers = reflections.flatMap((r) => r.blockers);
|
|
13830
|
+
const resolvedBlockers = allBlockers.filter((b) => b.resolved);
|
|
13831
|
+
const categoryCounts = {};
|
|
13832
|
+
for (const b of allBlockers) categoryCounts[b.category] = (categoryCounts[b.category] ?? 0) + 1;
|
|
13833
|
+
const mostCommonCategory = Object.entries(categoryCounts).toSorted(([, a], [, b]) => b - a)[0]?.[0] ?? "none";
|
|
13834
|
+
const strengthCounts = {};
|
|
13835
|
+
const weaknessCounts = {};
|
|
13836
|
+
for (const r of reflections) for (const p of r.patterns) if (p.type === "strength") strengthCounts[p.description] = (strengthCounts[p.description] ?? 0) + 1;
|
|
13837
|
+
else if (p.type === "weakness" && p.frequency === "persistent") weaknessCounts[p.description] = (weaknessCounts[p.description] ?? 0) + 1;
|
|
13838
|
+
const topStrengths = Object.entries(strengthCounts).toSorted(([, a], [, b]) => b - a).slice(0, 5).map(([desc]) => desc);
|
|
13839
|
+
const persistentWeaknesses = Object.entries(weaknessCounts).toSorted(([, a], [, b]) => b - a).slice(0, 5).map(([desc]) => desc);
|
|
13840
|
+
const recentLessons = reflections.slice(0, 5).flatMap((r) => r.lessons).slice(0, 10);
|
|
13841
|
+
return {
|
|
13842
|
+
totalSessions: reflections.length,
|
|
13843
|
+
avgQuality: reflections.reduce((s, r) => s + r.qualityScore, 0) / reflections.length,
|
|
13844
|
+
topStrengths,
|
|
13845
|
+
persistentWeaknesses,
|
|
13846
|
+
totalAccomplishments,
|
|
13847
|
+
totalBlockers: allBlockers.length,
|
|
13848
|
+
resolvedBlockerRate: allBlockers.length > 0 ? resolvedBlockers.length / allBlockers.length : 1,
|
|
13849
|
+
mostCommonBlockerCategory: mostCommonCategory,
|
|
13850
|
+
recentLessons
|
|
13851
|
+
};
|
|
13852
|
+
}
|
|
13853
|
+
|
|
13854
|
+
//#endregion
|
|
13855
|
+
//#region src/gateway/server-methods/reflection.ts
|
|
13856
|
+
const reflectionHandlers = {
|
|
13857
|
+
"reflection.record": async ({ params, respond }) => {
|
|
13858
|
+
try {
|
|
13859
|
+
respond(true, { reflection: recordReflection({
|
|
13860
|
+
sessionId: typeof params.sessionId === "string" ? params.sessionId : "unknown",
|
|
13861
|
+
agentName: typeof params.agentName === "string" ? params.agentName : "Anima Agent",
|
|
13862
|
+
durationMs: typeof params.durationMs === "number" ? params.durationMs : 0,
|
|
13863
|
+
accomplishments: Array.isArray(params.accomplishments) ? params.accomplishments : [],
|
|
13864
|
+
incomplete: Array.isArray(params.incomplete) ? params.incomplete : [],
|
|
13865
|
+
blockers: Array.isArray(params.blockers) ? params.blockers : [],
|
|
13866
|
+
patterns: Array.isArray(params.patterns) ? params.patterns : [],
|
|
13867
|
+
lessons: Array.isArray(params.lessons) ? params.lessons : [],
|
|
13868
|
+
capabilityUpdates: Array.isArray(params.capabilityUpdates) ? params.capabilityUpdates : [],
|
|
13869
|
+
qualityScore: typeof params.qualityScore === "number" ? params.qualityScore : .5,
|
|
13870
|
+
endingMood: typeof params.endingMood === "string" ? params.endingMood : "steady"
|
|
13871
|
+
}) }, void 0);
|
|
13872
|
+
} catch (error) {
|
|
13873
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13874
|
+
}
|
|
13875
|
+
},
|
|
13876
|
+
"reflection.get": async ({ params, respond }) => {
|
|
13877
|
+
const id = typeof params.id === "string" ? params.id.trim() : "";
|
|
13878
|
+
if (!id) {
|
|
13879
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "id required"));
|
|
13880
|
+
return;
|
|
13881
|
+
}
|
|
13882
|
+
try {
|
|
13883
|
+
const reflection = getReflection(id);
|
|
13884
|
+
if (!reflection) {
|
|
13885
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "Reflection not found"));
|
|
13886
|
+
return;
|
|
13887
|
+
}
|
|
13888
|
+
respond(true, { reflection }, void 0);
|
|
13889
|
+
} catch (error) {
|
|
13890
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13891
|
+
}
|
|
13892
|
+
},
|
|
13893
|
+
"reflection.list": async ({ params, respond }) => {
|
|
13894
|
+
try {
|
|
13895
|
+
respond(true, { reflections: listReflections(typeof params.limit === "number" ? params.limit : 20) }, void 0);
|
|
13896
|
+
} catch (error) {
|
|
13897
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13898
|
+
}
|
|
13899
|
+
},
|
|
13900
|
+
"reflection.analyze": async ({ params, respond }) => {
|
|
13901
|
+
try {
|
|
13902
|
+
respond(true, { summary: analyzeReflections(listReflections(typeof params.limit === "number" ? params.limit : 50)) }, void 0);
|
|
13903
|
+
} catch (error) {
|
|
13904
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
13905
|
+
}
|
|
13906
|
+
}
|
|
13907
|
+
};
|
|
13908
|
+
|
|
12617
13909
|
//#endregion
|
|
12618
13910
|
//#region src/gateway/server-methods/send.ts
|
|
12619
13911
|
const inflightByContext = /* @__PURE__ */ new WeakMap();
|
|
@@ -13005,6 +14297,374 @@ const skillsHandlers = {
|
|
|
13005
14297
|
}
|
|
13006
14298
|
};
|
|
13007
14299
|
|
|
14300
|
+
//#endregion
|
|
14301
|
+
//#region src/gateway/server-methods/steer.ts
|
|
14302
|
+
const steerHandlers = {
|
|
14303
|
+
"steer.get": async ({ respond }) => {
|
|
14304
|
+
try {
|
|
14305
|
+
respond(true, { active: getSteer() }, void 0);
|
|
14306
|
+
} catch (error) {
|
|
14307
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
14308
|
+
}
|
|
14309
|
+
},
|
|
14310
|
+
"steer.set": async ({ params, respond }) => {
|
|
14311
|
+
const text = typeof params.text === "string" ? params.text.trim() : "";
|
|
14312
|
+
if (!text) {
|
|
14313
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "text is required"));
|
|
14314
|
+
return;
|
|
14315
|
+
}
|
|
14316
|
+
const setBy = typeof params.setBy === "string" ? params.setBy.trim() : "user";
|
|
14317
|
+
try {
|
|
14318
|
+
const state = setSteer(text, setBy);
|
|
14319
|
+
respond(true, {
|
|
14320
|
+
active: state.active,
|
|
14321
|
+
updatedAt: state.updatedAt
|
|
14322
|
+
}, void 0);
|
|
14323
|
+
} catch (error) {
|
|
14324
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
14325
|
+
}
|
|
14326
|
+
},
|
|
14327
|
+
"steer.clear": async ({ respond }) => {
|
|
14328
|
+
try {
|
|
14329
|
+
const state = clearSteer();
|
|
14330
|
+
respond(true, {
|
|
14331
|
+
active: state.active,
|
|
14332
|
+
updatedAt: state.updatedAt
|
|
14333
|
+
}, void 0);
|
|
14334
|
+
} catch (error) {
|
|
14335
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
14336
|
+
}
|
|
14337
|
+
},
|
|
14338
|
+
"steer.history": async ({ respond }) => {
|
|
14339
|
+
try {
|
|
14340
|
+
respond(true, { history: getSteerHistory() }, void 0);
|
|
14341
|
+
} catch (error) {
|
|
14342
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
14343
|
+
}
|
|
14344
|
+
}
|
|
14345
|
+
};
|
|
14346
|
+
|
|
14347
|
+
//#endregion
|
|
14348
|
+
//#region src/license/validator.ts
|
|
14349
|
+
createSubsystemLogger("license");
|
|
14350
|
+
function resolveLicensePath() {
|
|
14351
|
+
return path.join(resolveStateDir(), "license.json");
|
|
14352
|
+
}
|
|
14353
|
+
function loadLicense() {
|
|
14354
|
+
try {
|
|
14355
|
+
const filePath = resolveLicensePath();
|
|
14356
|
+
if (!fs.existsSync(filePath)) return null;
|
|
14357
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
14358
|
+
return JSON.parse(raw);
|
|
14359
|
+
} catch {
|
|
14360
|
+
return null;
|
|
14361
|
+
}
|
|
14362
|
+
}
|
|
14363
|
+
function saveLicense(license) {
|
|
14364
|
+
const filePath = resolveLicensePath();
|
|
14365
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
14366
|
+
fs.writeFileSync(filePath, `${JSON.stringify(license, null, 2)}\n`, { mode: 384 });
|
|
14367
|
+
}
|
|
14368
|
+
|
|
14369
|
+
//#endregion
|
|
14370
|
+
//#region src/license/stripe-checkout.ts
|
|
14371
|
+
/**
|
|
14372
|
+
* Stripe Checkout Integration for NoxSoft Subscriptions
|
|
14373
|
+
*
|
|
14374
|
+
* Creates Stripe checkout sessions for NoxSoft license tiers.
|
|
14375
|
+
* Handles subscription lifecycle: create, upgrade, cancel.
|
|
14376
|
+
*
|
|
14377
|
+
* Configuration via environment:
|
|
14378
|
+
* STRIPE_SECRET_KEY — Stripe API secret key
|
|
14379
|
+
* STRIPE_WEBHOOK_SECRET — Stripe webhook signing secret
|
|
14380
|
+
* NOXSOFT_BASE_URL — Base URL for redirect (default: https://anima.noxsoft.net)
|
|
14381
|
+
*
|
|
14382
|
+
* Price IDs are configured per tier. In test mode, Stripe test keys work.
|
|
14383
|
+
*/
|
|
14384
|
+
const log$4 = createSubsystemLogger("stripe-checkout");
|
|
14385
|
+
/** Default price IDs — set to null until Stripe dashboard is configured */
|
|
14386
|
+
const DEFAULT_PRICE_IDS = {
|
|
14387
|
+
community: null,
|
|
14388
|
+
noxsoft: null,
|
|
14389
|
+
hackathon: null,
|
|
14390
|
+
team: null,
|
|
14391
|
+
builder: null
|
|
14392
|
+
};
|
|
14393
|
+
function resolveStripeConfig() {
|
|
14394
|
+
const secretKey = process.env.STRIPE_SECRET_KEY?.trim();
|
|
14395
|
+
if (!secretKey) return null;
|
|
14396
|
+
return {
|
|
14397
|
+
secretKey,
|
|
14398
|
+
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET?.trim() ?? "",
|
|
14399
|
+
baseUrl: process.env.NOXSOFT_BASE_URL?.trim() ?? "https://anima.noxsoft.net",
|
|
14400
|
+
priceIds: {
|
|
14401
|
+
...DEFAULT_PRICE_IDS,
|
|
14402
|
+
...process.env.STRIPE_PRICE_NOXSOFT ? { noxsoft: process.env.STRIPE_PRICE_NOXSOFT } : {},
|
|
14403
|
+
...process.env.STRIPE_PRICE_TEAM ? { team: process.env.STRIPE_PRICE_TEAM } : {},
|
|
14404
|
+
...process.env.STRIPE_PRICE_BUILDER ? { builder: process.env.STRIPE_PRICE_BUILDER } : {}
|
|
14405
|
+
}
|
|
14406
|
+
};
|
|
14407
|
+
}
|
|
14408
|
+
async function stripeRequest(secretKey, method, path, body) {
|
|
14409
|
+
const url = `https://api.stripe.com/v1${path}`;
|
|
14410
|
+
const headers = {
|
|
14411
|
+
Authorization: `Bearer ${secretKey}`,
|
|
14412
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
14413
|
+
};
|
|
14414
|
+
const encodedBody = body ? Object.entries(body).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&") : void 0;
|
|
14415
|
+
const res = await fetch(url, {
|
|
14416
|
+
method,
|
|
14417
|
+
headers,
|
|
14418
|
+
body: encodedBody,
|
|
14419
|
+
signal: AbortSignal.timeout(15e3)
|
|
14420
|
+
});
|
|
14421
|
+
const data = await res.json();
|
|
14422
|
+
if (!res.ok) {
|
|
14423
|
+
const rawError = data.error?.message;
|
|
14424
|
+
const errMsg = typeof rawError === "string" ? rawError : res.statusText;
|
|
14425
|
+
throw new Error(`Stripe API error (${res.status}): ${errMsg}`);
|
|
14426
|
+
}
|
|
14427
|
+
return data;
|
|
14428
|
+
}
|
|
14429
|
+
/**
|
|
14430
|
+
* Create a Stripe Checkout session for a NoxSoft subscription.
|
|
14431
|
+
*/
|
|
14432
|
+
async function createCheckoutSession(params) {
|
|
14433
|
+
const config = resolveStripeConfig();
|
|
14434
|
+
if (!config) throw new Error("Stripe not configured. Set STRIPE_SECRET_KEY environment variable.");
|
|
14435
|
+
const priceId = config.priceIds[params.tier];
|
|
14436
|
+
if (!priceId) {
|
|
14437
|
+
if (params.tier === "community" || params.tier === "hackathon") throw new Error(`${params.tier} tier is free — no checkout needed.`);
|
|
14438
|
+
throw new Error(`Stripe price ID not configured for tier "${params.tier}". Set STRIPE_PRICE_${params.tier.toUpperCase()} environment variable.`);
|
|
14439
|
+
}
|
|
14440
|
+
const body = {
|
|
14441
|
+
mode: "subscription",
|
|
14442
|
+
"line_items[0][price]": priceId,
|
|
14443
|
+
"line_items[0][quantity]": "1",
|
|
14444
|
+
success_url: `${config.baseUrl}/subscription/success?session_id={CHECKOUT_SESSION_ID}`,
|
|
14445
|
+
cancel_url: `${config.baseUrl}/subscription/cancel`,
|
|
14446
|
+
"metadata[agent_id]": params.agentId,
|
|
14447
|
+
"metadata[tier]": params.tier,
|
|
14448
|
+
"subscription_data[metadata][agent_id]": params.agentId,
|
|
14449
|
+
"subscription_data[metadata][tier]": params.tier
|
|
14450
|
+
};
|
|
14451
|
+
if (params.customerEmail) body["customer_email"] = params.customerEmail;
|
|
14452
|
+
const session = await stripeRequest(config.secretKey, "POST", "/checkout/sessions", body);
|
|
14453
|
+
log$4.info(`checkout session created: ${session.id} (tier: ${params.tier})`);
|
|
14454
|
+
return {
|
|
14455
|
+
sessionId: session.id,
|
|
14456
|
+
url: session.url
|
|
14457
|
+
};
|
|
14458
|
+
}
|
|
14459
|
+
/**
|
|
14460
|
+
* Verify a Stripe webhook signature.
|
|
14461
|
+
* Returns the parsed event if valid, null if invalid.
|
|
14462
|
+
*/
|
|
14463
|
+
function verifyWebhookSignature(payload, signature, webhookSecret) {
|
|
14464
|
+
if (!webhookSecret) {
|
|
14465
|
+
log$4.warn("webhook secret not configured — skipping signature verification");
|
|
14466
|
+
try {
|
|
14467
|
+
return JSON.parse(payload);
|
|
14468
|
+
} catch {
|
|
14469
|
+
return null;
|
|
14470
|
+
}
|
|
14471
|
+
}
|
|
14472
|
+
const elements = signature.split(",");
|
|
14473
|
+
const timestamp = elements.find((e) => e.startsWith("t="))?.slice(2);
|
|
14474
|
+
const sig = elements.find((e) => e.startsWith("v1="))?.slice(3);
|
|
14475
|
+
if (!timestamp || !sig) {
|
|
14476
|
+
log$4.warn("invalid webhook signature format");
|
|
14477
|
+
return null;
|
|
14478
|
+
}
|
|
14479
|
+
if (sig !== createHmac("sha256", webhookSecret).update(`${timestamp}.${payload}`).digest("hex")) {
|
|
14480
|
+
log$4.warn("webhook signature mismatch");
|
|
14481
|
+
return null;
|
|
14482
|
+
}
|
|
14483
|
+
const eventTime = parseInt(timestamp, 10) * 1e3;
|
|
14484
|
+
if (Math.abs(Date.now() - eventTime) > 3e5) {
|
|
14485
|
+
log$4.warn("webhook timestamp too old");
|
|
14486
|
+
return null;
|
|
14487
|
+
}
|
|
14488
|
+
try {
|
|
14489
|
+
return JSON.parse(payload);
|
|
14490
|
+
} catch {
|
|
14491
|
+
return null;
|
|
14492
|
+
}
|
|
14493
|
+
}
|
|
14494
|
+
/**
|
|
14495
|
+
* Handle a verified Stripe webhook event.
|
|
14496
|
+
* Updates the local license based on subscription status.
|
|
14497
|
+
*/
|
|
14498
|
+
async function handleWebhookEvent(event) {
|
|
14499
|
+
switch (event.type) {
|
|
14500
|
+
case "checkout.session.completed": {
|
|
14501
|
+
const session = event.data.object;
|
|
14502
|
+
const tier = session.metadata?.tier;
|
|
14503
|
+
const agentId = session.metadata?.agent_id;
|
|
14504
|
+
const subscriptionId = session.subscription;
|
|
14505
|
+
const customerId = session.customer;
|
|
14506
|
+
if (!tier || !agentId) {
|
|
14507
|
+
log$4.warn("checkout.session.completed missing metadata");
|
|
14508
|
+
return { handled: false };
|
|
14509
|
+
}
|
|
14510
|
+
saveLicense({
|
|
14511
|
+
tier,
|
|
14512
|
+
agentId,
|
|
14513
|
+
subscriptionId: subscriptionId ?? void 0,
|
|
14514
|
+
customerId: customerId ?? void 0,
|
|
14515
|
+
issuedAt: Date.now(),
|
|
14516
|
+
expiresAt: Date.now() + 720 * 60 * 60 * 1e3,
|
|
14517
|
+
signature: "",
|
|
14518
|
+
version: 1
|
|
14519
|
+
});
|
|
14520
|
+
log$4.info(`license activated: ${tier} for ${agentId} (sub: ${subscriptionId})`);
|
|
14521
|
+
return {
|
|
14522
|
+
handled: true,
|
|
14523
|
+
action: "license_activated"
|
|
14524
|
+
};
|
|
14525
|
+
}
|
|
14526
|
+
case "customer.subscription.updated": {
|
|
14527
|
+
const sub = event.data.object;
|
|
14528
|
+
const status = sub.status;
|
|
14529
|
+
const tier = sub.metadata?.tier;
|
|
14530
|
+
if (status === "active" && tier) {
|
|
14531
|
+
const existing = loadLicense();
|
|
14532
|
+
if (existing) {
|
|
14533
|
+
existing.tier = tier;
|
|
14534
|
+
existing.expiresAt = Date.now() + 720 * 60 * 60 * 1e3;
|
|
14535
|
+
saveLicense(existing);
|
|
14536
|
+
log$4.info(`subscription updated: ${tier}`);
|
|
14537
|
+
}
|
|
14538
|
+
}
|
|
14539
|
+
return {
|
|
14540
|
+
handled: true,
|
|
14541
|
+
action: "subscription_updated"
|
|
14542
|
+
};
|
|
14543
|
+
}
|
|
14544
|
+
case "customer.subscription.deleted": {
|
|
14545
|
+
const existing = loadLicense();
|
|
14546
|
+
if (existing) {
|
|
14547
|
+
existing.tier = "community";
|
|
14548
|
+
existing.expiresAt = Date.now() + 336 * 60 * 60 * 1e3;
|
|
14549
|
+
saveLicense(existing);
|
|
14550
|
+
log$4.info("subscription cancelled — downgraded to community with 14-day grace");
|
|
14551
|
+
}
|
|
14552
|
+
return {
|
|
14553
|
+
handled: true,
|
|
14554
|
+
action: "subscription_cancelled"
|
|
14555
|
+
};
|
|
14556
|
+
}
|
|
14557
|
+
case "invoice.payment_failed":
|
|
14558
|
+
log$4.warn("payment failed — license will expire at end of current period");
|
|
14559
|
+
return {
|
|
14560
|
+
handled: true,
|
|
14561
|
+
action: "payment_failed"
|
|
14562
|
+
};
|
|
14563
|
+
default: return { handled: false };
|
|
14564
|
+
}
|
|
14565
|
+
}
|
|
14566
|
+
/**
|
|
14567
|
+
* Create a Stripe Customer Portal session for managing subscriptions.
|
|
14568
|
+
*/
|
|
14569
|
+
async function createPortalSession(customerId) {
|
|
14570
|
+
const config = resolveStripeConfig();
|
|
14571
|
+
if (!config) throw new Error("Stripe not configured.");
|
|
14572
|
+
return { url: (await stripeRequest(config.secretKey, "POST", "/billing_portal/sessions", {
|
|
14573
|
+
customer: customerId,
|
|
14574
|
+
return_url: `${config.baseUrl}/settings`
|
|
14575
|
+
})).url };
|
|
14576
|
+
}
|
|
14577
|
+
/**
|
|
14578
|
+
* Check if Stripe is configured and ready.
|
|
14579
|
+
*/
|
|
14580
|
+
function isStripeConfigured() {
|
|
14581
|
+
return resolveStripeConfig() !== null;
|
|
14582
|
+
}
|
|
14583
|
+
/**
|
|
14584
|
+
* Get subscription status from Stripe.
|
|
14585
|
+
*/
|
|
14586
|
+
async function getSubscriptionStatus(subscriptionId) {
|
|
14587
|
+
const config = resolveStripeConfig();
|
|
14588
|
+
if (!config) return null;
|
|
14589
|
+
try {
|
|
14590
|
+
const sub = await stripeRequest(config.secretKey, "GET", `/subscriptions/${subscriptionId}`);
|
|
14591
|
+
return {
|
|
14592
|
+
status: sub.status,
|
|
14593
|
+
currentPeriodEnd: sub.current_period_end * 1e3,
|
|
14594
|
+
cancelAtPeriodEnd: sub.cancel_at_period_end
|
|
14595
|
+
};
|
|
14596
|
+
} catch (err) {
|
|
14597
|
+
log$4.error(`failed to get subscription status: ${String(err)}`);
|
|
14598
|
+
return null;
|
|
14599
|
+
}
|
|
14600
|
+
}
|
|
14601
|
+
|
|
14602
|
+
//#endregion
|
|
14603
|
+
//#region src/gateway/server-methods/subscription.ts
|
|
14604
|
+
const subscriptionHandlers = {
|
|
14605
|
+
"subscription.status": async ({ respond }) => {
|
|
14606
|
+
try {
|
|
14607
|
+
const license = loadLicense();
|
|
14608
|
+
const stripeReady = isStripeConfigured();
|
|
14609
|
+
if (!license) {
|
|
14610
|
+
respond(true, {
|
|
14611
|
+
tier: "community",
|
|
14612
|
+
stripeConfigured: stripeReady,
|
|
14613
|
+
active: true,
|
|
14614
|
+
message: "Running on community (free) tier."
|
|
14615
|
+
}, void 0);
|
|
14616
|
+
return;
|
|
14617
|
+
}
|
|
14618
|
+
let stripeStatus = null;
|
|
14619
|
+
if (license.subscriptionId && stripeReady) stripeStatus = await getSubscriptionStatus(license.subscriptionId);
|
|
14620
|
+
respond(true, {
|
|
14621
|
+
tier: license.tier,
|
|
14622
|
+
active: !license.expiresAt || license.expiresAt > Date.now(),
|
|
14623
|
+
expiresAt: license.expiresAt,
|
|
14624
|
+
stripeConfigured: stripeReady,
|
|
14625
|
+
subscriptionId: license.subscriptionId,
|
|
14626
|
+
stripeStatus: stripeStatus?.status,
|
|
14627
|
+
cancelAtPeriodEnd: stripeStatus?.cancelAtPeriodEnd
|
|
14628
|
+
}, void 0);
|
|
14629
|
+
} catch (error) {
|
|
14630
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
14631
|
+
}
|
|
14632
|
+
},
|
|
14633
|
+
"subscription.checkout": async ({ params, respond }) => {
|
|
14634
|
+
const tier = typeof params.tier === "string" ? params.tier.trim() : "";
|
|
14635
|
+
if (!tier) {
|
|
14636
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "tier is required"));
|
|
14637
|
+
return;
|
|
14638
|
+
}
|
|
14639
|
+
try {
|
|
14640
|
+
const result = await createCheckoutSession({
|
|
14641
|
+
tier,
|
|
14642
|
+
agentId: typeof params.agentId === "string" ? params.agentId : "default",
|
|
14643
|
+
customerEmail: typeof params.email === "string" ? params.email : void 0
|
|
14644
|
+
});
|
|
14645
|
+
respond(true, {
|
|
14646
|
+
sessionId: result.sessionId,
|
|
14647
|
+
checkoutUrl: result.url
|
|
14648
|
+
}, void 0);
|
|
14649
|
+
} catch (error) {
|
|
14650
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
14651
|
+
}
|
|
14652
|
+
},
|
|
14653
|
+
"subscription.portal": async ({ params, respond }) => {
|
|
14654
|
+
try {
|
|
14655
|
+
const license = loadLicense();
|
|
14656
|
+
const customerId = typeof params.customerId === "string" ? params.customerId : license?.customerId;
|
|
14657
|
+
if (!customerId || typeof customerId !== "string") {
|
|
14658
|
+
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "No customer ID found. Purchase a subscription first."));
|
|
14659
|
+
return;
|
|
14660
|
+
}
|
|
14661
|
+
respond(true, { portalUrl: (await createPortalSession(customerId)).url }, void 0);
|
|
14662
|
+
} catch (error) {
|
|
14663
|
+
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, String(error)));
|
|
14664
|
+
}
|
|
14665
|
+
}
|
|
14666
|
+
};
|
|
14667
|
+
|
|
13008
14668
|
//#endregion
|
|
13009
14669
|
//#region src/gateway/server-methods/system.ts
|
|
13010
14670
|
const systemHandlers = {
|
|
@@ -14451,6 +16111,11 @@ const coreGatewayHandlers = {
|
|
|
14451
16111
|
...updateHandlers,
|
|
14452
16112
|
...nodeHandlers,
|
|
14453
16113
|
...orgHandlers,
|
|
16114
|
+
...egoHandlers,
|
|
16115
|
+
...reflectionHandlers,
|
|
16116
|
+
...steerHandlers,
|
|
16117
|
+
...legacyHandlers,
|
|
16118
|
+
...subscriptionHandlers,
|
|
14454
16119
|
...sendHandlers,
|
|
14455
16120
|
...usageHandlers,
|
|
14456
16121
|
...agentHandlers,
|
|
@@ -15925,7 +17590,7 @@ function normalizeBasePath(rawPath) {
|
|
|
15925
17590
|
return normalized.replace(/\/+$/, "");
|
|
15926
17591
|
}
|
|
15927
17592
|
async function prepareCanvasRoot(rootDir) {
|
|
15928
|
-
await ensureDir(rootDir);
|
|
17593
|
+
await ensureDir$1(rootDir);
|
|
15929
17594
|
const rootReal = await fs$1.realpath(rootDir);
|
|
15930
17595
|
try {
|
|
15931
17596
|
const indexPath = path.join(rootReal, "index.html");
|
|
@@ -16447,6 +18112,89 @@ function setSseHeaders(res) {
|
|
|
16447
18112
|
res.flushHeaders?.();
|
|
16448
18113
|
}
|
|
16449
18114
|
|
|
18115
|
+
//#endregion
|
|
18116
|
+
//#region src/gateway/http-rate-limit.ts
|
|
18117
|
+
const log$3 = createSubsystemLogger("http-rate-limit");
|
|
18118
|
+
var HttpRateLimiter = class {
|
|
18119
|
+
constructor(config) {
|
|
18120
|
+
this.windows = /* @__PURE__ */ new Map();
|
|
18121
|
+
this.maxRequests = config?.maxRequests ?? 100;
|
|
18122
|
+
this.windowMs = config?.windowMs ?? 6e4;
|
|
18123
|
+
this.exemptLoopback = config?.exemptLoopback ?? true;
|
|
18124
|
+
const pruneInterval = config?.pruneIntervalMs ?? 3e5;
|
|
18125
|
+
this.pruneTimer = setInterval(() => this.prune(), pruneInterval);
|
|
18126
|
+
this.pruneTimer.unref?.();
|
|
18127
|
+
}
|
|
18128
|
+
/**
|
|
18129
|
+
* Check if a request from this IP is allowed.
|
|
18130
|
+
* Returns { allowed, remaining, retryAfterMs }.
|
|
18131
|
+
*/
|
|
18132
|
+
check(ip) {
|
|
18133
|
+
if (this.exemptLoopback && isLoopbackAddress(ip)) return {
|
|
18134
|
+
allowed: true,
|
|
18135
|
+
remaining: this.maxRequests,
|
|
18136
|
+
retryAfterMs: 0
|
|
18137
|
+
};
|
|
18138
|
+
const now = Date.now();
|
|
18139
|
+
const windowStart = now - this.windowMs;
|
|
18140
|
+
let window = this.windows.get(ip);
|
|
18141
|
+
if (!window) {
|
|
18142
|
+
window = { timestamps: [] };
|
|
18143
|
+
this.windows.set(ip, window);
|
|
18144
|
+
}
|
|
18145
|
+
window.timestamps = window.timestamps.filter((t) => t > windowStart);
|
|
18146
|
+
if (window.timestamps.length >= this.maxRequests) {
|
|
18147
|
+
const retryAfterMs = window.timestamps[0] + this.windowMs - now;
|
|
18148
|
+
return {
|
|
18149
|
+
allowed: false,
|
|
18150
|
+
remaining: 0,
|
|
18151
|
+
retryAfterMs: Math.max(retryAfterMs, 1e3)
|
|
18152
|
+
};
|
|
18153
|
+
}
|
|
18154
|
+
window.timestamps.push(now);
|
|
18155
|
+
return {
|
|
18156
|
+
allowed: true,
|
|
18157
|
+
remaining: this.maxRequests - window.timestamps.length,
|
|
18158
|
+
retryAfterMs: 0
|
|
18159
|
+
};
|
|
18160
|
+
}
|
|
18161
|
+
/**
|
|
18162
|
+
* Remove stale entries to prevent unbounded memory growth.
|
|
18163
|
+
*/
|
|
18164
|
+
prune() {
|
|
18165
|
+
const windowStart = Date.now() - this.windowMs;
|
|
18166
|
+
let pruned = 0;
|
|
18167
|
+
for (const [ip, window] of this.windows) {
|
|
18168
|
+
window.timestamps = window.timestamps.filter((t) => t > windowStart);
|
|
18169
|
+
if (window.timestamps.length === 0) {
|
|
18170
|
+
this.windows.delete(ip);
|
|
18171
|
+
pruned++;
|
|
18172
|
+
}
|
|
18173
|
+
}
|
|
18174
|
+
if (pruned > 0) log$3.debug(`pruned ${pruned} stale rate limit entries`);
|
|
18175
|
+
}
|
|
18176
|
+
/**
|
|
18177
|
+
* Stop the pruning timer.
|
|
18178
|
+
*/
|
|
18179
|
+
stop() {
|
|
18180
|
+
if (this.pruneTimer) {
|
|
18181
|
+
clearInterval(this.pruneTimer);
|
|
18182
|
+
this.pruneTimer = void 0;
|
|
18183
|
+
}
|
|
18184
|
+
}
|
|
18185
|
+
/**
|
|
18186
|
+
* Get current tracked IP count (for monitoring).
|
|
18187
|
+
*/
|
|
18188
|
+
getTrackedCount() {
|
|
18189
|
+
return this.windows.size;
|
|
18190
|
+
}
|
|
18191
|
+
};
|
|
18192
|
+
/** Default: 100 req/min per IP */
|
|
18193
|
+
const DEFAULT_RATE_LIMIT = {
|
|
18194
|
+
maxRequests: 100,
|
|
18195
|
+
windowMs: 6e4
|
|
18196
|
+
};
|
|
18197
|
+
|
|
16450
18198
|
//#endregion
|
|
16451
18199
|
//#region src/gateway/http-utils.ts
|
|
16452
18200
|
function getHeader(req, name) {
|
|
@@ -18163,6 +19911,7 @@ function createHooksRequestHandler(opts) {
|
|
|
18163
19911
|
}
|
|
18164
19912
|
function createGatewayHttpServer(opts) {
|
|
18165
19913
|
const { canvasHost, clients, controlUiEnabled, controlUiBasePath, controlUiRoot, openAiChatCompletionsEnabled, openResponsesEnabled, openResponsesConfig, handleHooksRequest, handlePluginRequest, resolvedAuth, rateLimiter } = opts;
|
|
19914
|
+
const httpRateLimiter = new HttpRateLimiter(DEFAULT_RATE_LIMIT);
|
|
18166
19915
|
const httpServer = opts.tlsOptions ? createServer$1(opts.tlsOptions, (req, res) => {
|
|
18167
19916
|
handleRequest(req, res);
|
|
18168
19917
|
}) : createServer((req, res) => {
|
|
@@ -18170,10 +19919,36 @@ function createGatewayHttpServer(opts) {
|
|
|
18170
19919
|
});
|
|
18171
19920
|
async function handleRequest(req, res) {
|
|
18172
19921
|
if (String(req.headers.upgrade ?? "").toLowerCase() === "websocket") return;
|
|
19922
|
+
const clientIp = resolveGatewayClientIp(req, []);
|
|
19923
|
+
const rateCheck = httpRateLimiter.check(clientIp);
|
|
19924
|
+
if (!rateCheck.allowed) {
|
|
19925
|
+
const retryAfterSec = Math.ceil(rateCheck.retryAfterMs / 1e3);
|
|
19926
|
+
res.writeHead(429, {
|
|
19927
|
+
"Content-Type": "application/json",
|
|
19928
|
+
"Retry-After": String(retryAfterSec),
|
|
19929
|
+
"X-RateLimit-Limit": String(DEFAULT_RATE_LIMIT.maxRequests ?? 100),
|
|
19930
|
+
"X-RateLimit-Remaining": "0"
|
|
19931
|
+
});
|
|
19932
|
+
res.end(JSON.stringify({
|
|
19933
|
+
error: "Too Many Requests",
|
|
19934
|
+
retryAfterMs: rateCheck.retryAfterMs
|
|
19935
|
+
}));
|
|
19936
|
+
return;
|
|
19937
|
+
}
|
|
19938
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
19939
|
+
res.setHeader("X-Frame-Options", "DENY");
|
|
19940
|
+
res.setHeader("X-XSS-Protection", "1; mode=block");
|
|
19941
|
+
res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
19942
|
+
res.setHeader("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
|
|
19943
|
+
res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
|
|
18173
19944
|
try {
|
|
18174
19945
|
const configSnapshot = loadConfig();
|
|
18175
19946
|
const trustedProxies = configSnapshot.gateway?.trustedProxies ?? [];
|
|
18176
19947
|
const requestPath = new URL(req.url ?? "/", "http://localhost").pathname;
|
|
19948
|
+
if (requestPath === "/webhook/stripe" && req.method === "POST") {
|
|
19949
|
+
await handleStripeWebhook(req, res);
|
|
19950
|
+
return;
|
|
19951
|
+
}
|
|
18177
19952
|
if (await handleHooksRequest(req, res)) return;
|
|
18178
19953
|
if (await handleToolsInvokeHttpRequest(req, res, {
|
|
18179
19954
|
auth: resolvedAuth,
|
|
@@ -18252,6 +20027,33 @@ function createGatewayHttpServer(opts) {
|
|
|
18252
20027
|
res.end("Internal Server Error");
|
|
18253
20028
|
}
|
|
18254
20029
|
}
|
|
20030
|
+
async function handleStripeWebhook(req, res) {
|
|
20031
|
+
const config = resolveStripeConfig();
|
|
20032
|
+
if (!config) {
|
|
20033
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
20034
|
+
res.end(JSON.stringify({ error: "Stripe not configured" }));
|
|
20035
|
+
return;
|
|
20036
|
+
}
|
|
20037
|
+
const chunks = [];
|
|
20038
|
+
for await (const chunk of req) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
20039
|
+
const event = verifyWebhookSignature(Buffer.concat(chunks).toString("utf8"), String(req.headers["stripe-signature"] ?? ""), config.webhookSecret);
|
|
20040
|
+
if (!event) {
|
|
20041
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
20042
|
+
res.end(JSON.stringify({ error: "Invalid signature" }));
|
|
20043
|
+
return;
|
|
20044
|
+
}
|
|
20045
|
+
try {
|
|
20046
|
+
const result = await handleWebhookEvent(event);
|
|
20047
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
20048
|
+
res.end(JSON.stringify({
|
|
20049
|
+
received: true,
|
|
20050
|
+
...result
|
|
20051
|
+
}));
|
|
20052
|
+
} catch (err) {
|
|
20053
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
20054
|
+
res.end(JSON.stringify({ error: String(err) }));
|
|
20055
|
+
}
|
|
20056
|
+
}
|
|
18255
20057
|
return httpServer;
|
|
18256
20058
|
}
|
|
18257
20059
|
function attachGatewayUpgradeHandler(opts) {
|