negotium 0.20.2 → 0.20.3
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/dist/agent-helpers.js +3 -3
- package/dist/agent-helpers.js.map +3 -3
- package/dist/background-bash.js +2 -2
- package/dist/background-bash.js.map +2 -2
- package/dist/browser-runtime.js +2 -2
- package/dist/browser-runtime.js.map +2 -2
- package/dist/{chunk-zkyp090x.js → chunk-59ga0p8z.js} +1 -1
- package/dist/{chunk-t7vs5k9t.js → chunk-fymyw41t.js} +2 -2
- package/dist/{chunk-t7vs5k9t.js.map → chunk-fymyw41t.js.map} +2 -2
- package/dist/{chunk-143j4nxd.js → chunk-mvsdbs2r.js} +1 -1
- package/dist/{chunk-myhmb8tn.js → chunk-ymbrft8y.js} +2 -2
- package/dist/{chunk-myhmb8tn.js.map → chunk-ymbrft8y.js.map} +2 -2
- package/dist/{chunk-j2wgr465.js → chunk-zgvek90d.js} +3 -3
- package/dist/{chunk-j2wgr465.js.map → chunk-zgvek90d.js.map} +2 -2
- package/dist/hosted-agent.js +4 -4
- package/dist/hosted-agent.js.map +2 -2
- package/dist/main.js +3 -3
- package/dist/main.js.map +3 -3
- package/dist/mcp-factories.js +3 -3
- package/dist/mcp-factories.js.map +3 -3
- package/dist/media.js +2 -2
- package/dist/media.js.map +2 -2
- package/dist/prompts.js +2 -2
- package/dist/prompts.js.map +2 -2
- package/dist/query-runtime.js +2 -2
- package/dist/query-runtime.js.map +2 -2
- package/dist/registry.js +3 -3
- package/dist/rollout.js +2 -2
- package/dist/runtime/src/platform/config.ts +1 -1
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +2 -2
- package/dist/runtime-helpers.js.map +2 -2
- package/dist/types/packages/core/src/platform/config.d.ts +1 -1
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js +2 -2
- package/dist/vault.js.map +2 -2
- package/install-browser-rs.mjs +4 -4
- package/package.json +1 -1
- /package/dist/{chunk-zkyp090x.js.map → chunk-59ga0p8z.js.map} +0 -0
- /package/dist/{chunk-143j4nxd.js.map → chunk-mvsdbs2r.js.map} +0 -0
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"// --- Abort reason ---\n\nexport enum AbortReason {\n None = \"none\",\n Internal = \"internal\", // replaced by a newer query on the same topic\n External = \"external\", // /abort command or abort_session via session-inbox\n Infrastructure = \"infrastructure\", // required runtime dependency exited unexpectedly\n}\n",
|
|
14
14
|
"/**\n * Room-keyed in-flight query registry + abort-on-new-message priority +\n * inter-session inject queue (requestId dedup + mergeable-prefix merging).\n *\n * ActiveQueries + SessionInjectQueue semantics, adapted to Otium's REST/WS\n * model where the \"room\" is a `topicId` and the live runner is\n * `api/routes/ai.ts`.\n *\n * Invariant: AT MOST ONE in-flight AI turn per room (topicId). A new turn\n * arriving on a busy room is resolved by origin priority:\n * - incoming user, running user → abort running (superseded), replace\n * - incoming user, running inject → re-queue running inject, abort, replace\n * - incoming inject (any running) → defer incoming (the user keeps priority)\n */\n\nimport type { ForkHandle } from \"#agents/fork\";\nimport { logger } from \"#platform/logger\";\nimport type { UserTurnEnvelope } from \"#runtime/user-turn-envelope\";\nimport {\n claimRuntimeTurnLease,\n getRuntimeTurnLease,\n heartbeatRuntimeTurnLease,\n listRuntimeTurnLeases,\n RUNTIME_INSTANCE_ID,\n type RuntimeTurnLease,\n releaseRuntimeTurnLease,\n requestRuntimeTurnAbort,\n} from \"#storage/runtime-leases\";\nimport type { AskReplySource } from \"#storage/session-asks\";\nimport type { AgentKind, PeerRuntimeBridgeContext } from \"#types\";\nimport { createRoomQueryRegistry, type RoomQueryDecision } from \"./room-query-registry\";\nimport { AbortReason } from \"./types\";\n\nexport { isolatedTurnRoomId, isUserOrigin } from \"./room-query-registry\";\n\n/** Lazily create an isolated provider session immediately before execution. */\nexport type PrepareInjectSession = () => Promise<ForkHandle>;\n\n/** A turn started by a session-inject, replayed when its room frees up. */\nexport interface DeferredInject {\n topicId: string;\n /** Topic execution epoch captured when this work was accepted. */\n runtimeEpoch?: number;\n userId: string;\n /** Credential namespace when the executor and Vault owner differ. */\n vaultUserId?: string;\n prompt: string;\n /** Inject source — the topic name/id this inject came from (never \"user\"). */\n origin: string;\n /** Thread of this topic to answer in, e.g. an ask raised from a thread. */\n threadRootId?: string;\n sourceNode?: string;\n /** Inter-session request id for dedup. */\n requestId?: string;\n /** Nesting depth (starts at 1 for the first cross-topic hop). */\n depth?: number;\n /** Whether this inject is silent (injected but hidden from the user). */\n silent?: boolean;\n /** Context chain id for continuing an inter-session conversation. */\n contextId?: string;\n /** Agent override for injected turns that must resume a provider-specific session. */\n agentOverride?: AgentKind;\n /** Model override for specialized internal turns such as cron. */\n modelOverride?: string;\n /** Effort override for specialized internal turns such as cron. */\n effortOverride?: import(\"#types\").EffortLevel;\n /** SDK-native session/thread id to resume for a forked injected turn. */\n sessionId?: string | null;\n /** Explicit provider-session ownership boundary. */\n sessionScope?: \"topic\" | \"isolated\";\n /** Scheduling boundary. Isolated turns do not occupy or wait for the topic room. */\n turnConcurrency?: \"topic\" | \"isolated\";\n /** Rollout file backing a synthetic/native fork; cleaned when the turn finishes. */\n forkHandle?: ForkHandle;\n /** Build a fresh isolated session at dispatch time (used by ask_session). */\n prepareSession?: PrepareInjectSession;\n /** Working directory override for provider-native resumed sessions. */\n cwd?: string;\n /** Conversation/session namespace override (cron jobs keep isolated rollouts). */\n sessionName?: string;\n /** MCP/tool scope override for a specialized internal turn. */\n sessionType?: \"dm\" | \"forum\" | \"ephemeral\" | \"manager\" | \"cron\";\n /** Adapter-granted access to Otium's visual panel tools. */\n visualTools?: boolean;\n /** Adapter-granted access to chat file-delivery tools. */\n fileDeliveryTools?: boolean;\n /** Optional session owner; when present the topic's main session is not replaced. */\n onSessionId?: (sessionId: string) => void;\n /** Clear an externally-owned session after an unrecoverable expiry. */\n onSessionReset?: () => void;\n /** Rebuild a provider-native rollout from the shared conversation log when needed. */\n bridgeSessionFromHistory?: boolean;\n /** Final result hook for optional modules that own an internal turn. */\n onSettled?: (result: {\n queryId: string;\n kind: \"completed\" | \"aborted\" | \"error\";\n abortReason?: AbortReason;\n error?: string;\n }) => void;\n /** Canonical hub identity for a placed-room runtime turn. */\n peerBridge?: PeerRuntimeBridgeContext;\n /** True after a session-expired recovery retry has already been attempted. */\n _sessionRetried?: boolean;\n /** Original ask_session replies represented by this inject, preserved across dequeueAll merges. */\n askReplySources?: AskReplySource[];\n /** Source marker forwarded to startAiTurn (e.g. FROM_AUTO_CONTINUE). */\n from?: string;\n /** Fired with the AI turn's queryId once the deferred turn is dispatched. */\n onDispatched?: (queryId: string) => void;\n}\n\nexport interface RoomQueryControl {\n topicId: string;\n /** Runtime scheduling/lease key. Defaults to topicId for normal turns. */\n roomId?: string;\n queryId: string;\n /** \"user\" for a human message; otherwise the inject source topic name. */\n origin: string;\n /** Prompt currently being processed. User-turn preemption merges this with the new prompt. */\n prompt: string;\n /** Original user submissions represented by prompt, in arrival order. */\n userMessages?: UserTurnEnvelope[];\n /**\n * Product actor a user turn speaks for, when recorded. Decides whether a\n * later human message may steer this turn: only the same actor's follow-up\n * aborts and folds into it; anyone else's waits its turn in the queue.\n */\n actorUserId?: string;\n /** Durable request rows whose messages are already represented by userMessages. */\n durableRequestIds?: string[];\n /** Attachment ids currently being processed. User-turn preemption carries them forward. */\n attachments?: string[];\n /** Latest provider session id observed for this turn. */\n sessionId?: string | null;\n /** True once the provider has published the native session backing this turn. */\n providerSessionObserved?: boolean;\n /** True once provider output proves that this turn's prompt reached the native session. */\n providerTurnContentObserved?: boolean;\n abortController: AbortController;\n abortReason: AbortReason;\n /** Specific failure surfaced when infrastructure aborts the turn. */\n abortError?: string;\n /** Epoch ms when this turn claimed the room (stale-abort guard). */\n startedAt?: number;\n /**\n * Set when this turn was started by a session-inject — the params needed to\n * re-enqueue it if a user message preempts it mid-flight. undefined for user\n * turns (a preempted user turn is simply discarded).\n */\n injectParams?: DeferredInject;\n /** True only after this process successfully queued a local replay. */\n injectRequeued?: boolean;\n}\n\n// ── Room-keyed in-flight registry ──────────────────────────────────────\n\nconst roomQueryRegistry = createRoomQueryRegistry<RoomQueryControl, RuntimeTurnLease, AbortReason>({\n instanceId: RUNTIME_INSTANCE_ID,\n internalAbortReason: AbortReason.Internal,\n externalAbortReason: AbortReason.External,\n listLeases: listRuntimeTurnLeases,\n getLease: getRuntimeTurnLease,\n claimLease: claimRuntimeTurnLease,\n heartbeatLease: heartbeatRuntimeTurnLease,\n releaseLease: releaseRuntimeTurnLease,\n requestAbort: requestRuntimeTurnAbort,\n});\n\nexport function getRoomQuery(roomId: string): RoomQueryControl | undefined {\n return roomQueryRegistry.get(roomId);\n}\n\n/** Topic ids and query ids with an in-flight turn in this or another runtime process. */\nexport function listRunningTopicQueries(): Map<string, string> {\n return roomQueryRegistry.listRunningTopicQueries();\n}\n\nexport function listRunningTopicIds(): Set<string> {\n return roomQueryRegistry.listRunningTopicIds();\n}\n\n/** Single-topic status check for callers that do not need a list snapshot. */\nexport function isTopicRunning(topicId: string): boolean {\n return roomQueryRegistry.isTopicRunning(topicId);\n}\n\nexport function getRoomQueryStatus(topicId: string, queryId: string): \"running\" | \"not_found\" {\n return roomQueryRegistry.status(topicId, queryId);\n}\n\nexport function setRoomQuery(control: RoomQueryControl): boolean {\n return roomQueryRegistry.set(control);\n}\n\n/**\n * Delete the room's in-flight entry, but ONLY if it is still this query — a\n * newer turn may have already replaced it (abort-and-replace), and the dying\n * turn's cleanup must not clobber its successor. Mirrors Otium's\n * \"Only delete our own entry\" guard.\n */\nexport function clearRoomQuery(roomId: string, queryId: string): void {\n roomQueryRegistry.clear(roomId, queryId);\n}\n\n/** Abort the currently-running turn in a room, if any. Cleanup is still owned by\n * the streaming turn's finally block so deferred injects drain in order. */\nexport function abortRoom(roomId: string, reason: AbortReason = AbortReason.External): boolean {\n return roomQueryRegistry.abort(roomId, reason);\n}\n\n/** Abort every in-flight provider turn before the node tears down its resources. */\nexport function abortAllRooms(reason: AbortReason = AbortReason.External): number {\n return roomQueryRegistry.abortAll(reason);\n}\n\n// ── Priority decision ──────────────────────────────────────────────────\n\nexport type NewQueryDecision = RoomQueryDecision<RoomQueryControl, RuntimeTurnLease>;\n\n/**\n * Decide what to do when a new turn (with `incomingOrigin`) arrives on a room.\n * Mirrors the abort-on-new-message priority used by live topic turns.\n */\nexport function decideNewQuery(topicId: string, incomingOrigin: string): NewQueryDecision {\n return roomQueryRegistry.decide(topicId, incomingOrigin);\n}\n\n// ── Session-inject queue (inter-session) ───────────────────────────────\n//\n// Session-inject queue for cross-topic ask/tell messages.\n// Key differences: Otium doesn't have per-user concurrency caps, so the key\n// is just the `topicId` (room).\n// `markPendingAskSources` is handled by the MCP layer; the queue here only\n// manages in-memory defer/replay.\n\nexport class InterSessionQueue {\n private queue = new Map<\n string,\n { items: DeferredInject[]; head: number; requestIds: Set<string> }\n >();\n\n private compactOrDelete(\n topicId: string,\n bucket: { items: DeferredInject[]; head: number; requestIds: Set<string> },\n ): void {\n if (bucket.head >= bucket.items.length) {\n this.queue.delete(topicId);\n return;\n }\n // Keep dequeue O(1) on the hot path; compact only when the reclaimed\n // prefix is large enough to justify copying the remaining references.\n if (bucket.head >= 64 && bucket.head * 2 >= bucket.items.length) {\n bucket.items = bucket.items.slice(bucket.head);\n bucket.head = 0;\n }\n }\n\n /** Enqueue inject if requestId is set and not a duplicate. Returns true if enqueued. */\n enqueue(topicId: string, inject: DeferredInject): boolean {\n if (!inject.requestId) {\n logger.error(\n { topicId, origin: inject.origin },\n \"BUG: session inject reached enqueue without requestId — DROPPING inject\",\n );\n return false;\n }\n let bucket = this.queue.get(topicId);\n if (!bucket) {\n bucket = { items: [], head: 0, requestIds: new Set<string>() };\n this.queue.set(topicId, bucket);\n }\n if (bucket.requestIds.has(inject.requestId)) {\n logger.info(\n { topicId, requestId: inject.requestId, origin: inject.origin },\n \"session inject deduped (existing entry with same requestId)\",\n );\n return false;\n }\n bucket.items.push(inject);\n bucket.requestIds.add(inject.requestId);\n logger.info(\n { topicId, origin: inject.origin, requestId: inject.requestId, depth: inject.depth },\n \"session inject enqueued\",\n );\n return true;\n }\n\n /** True when an entry with this requestId is still queued for this topic. */\n hasRequest(topicId: string, requestId: string): boolean {\n return this.queue.get(topicId)?.requestIds.has(requestId) ?? false;\n }\n\n /** Remove and return the next queued inject for this topic, or undefined if empty. */\n dequeueNext(topicId: string): DeferredInject | undefined {\n const bucket = this.queue.get(topicId);\n if (!bucket || bucket.head >= bucket.items.length) return undefined;\n const next = bucket.items[bucket.head++];\n if (next.requestId) bucket.requestIds.delete(next.requestId);\n this.compactOrDelete(topicId, bucket);\n return next;\n }\n\n /**\n * Remove and return the longest mergeable PREFIX of the queue as a single\n * entry, or undefined if empty. Multiple prompts are joined so they land in\n * one turn instead of chaining N turns for N queued injects.\n *\n * Merge rule (mirrors Otium): caller-bound ask_session replies can merge\n * even when they came from different target topics; ordinary inter-session\n * injects remain conservative and require the same origin/silent/session\n * shape. `onDispatched` callbacks from each merged entry are composed.\n */\n dequeueAll(topicId: string): DeferredInject | undefined {\n const bucket = this.queue.get(topicId);\n if (!bucket || bucket.head >= bucket.items.length) return undefined;\n const base = bucket.items[bucket.head];\n const isAskReplyInject = (e: DeferredInject) => !e.silent && Boolean(e.askReplySources?.length);\n const baseIsAskReplyInject = isAskReplyInject(base);\n const mergeable = (e: DeferredInject) =>\n (baseIsAskReplyInject\n ? isAskReplyInject(e)\n : !isAskReplyInject(e) &&\n e.origin === base.origin &&\n (e.silent ?? false) === (base.silent ?? false)) &&\n (e.threadRootId ?? null) === (base.threadRootId ?? null) &&\n (e.onDispatched !== undefined) === (base.onDispatched !== undefined) &&\n (e.agentOverride ?? null) === (base.agentOverride ?? null) &&\n (e.modelOverride ?? null) === (base.modelOverride ?? null) &&\n (e.effortOverride ?? null) === (base.effortOverride ?? null) &&\n (e.runtimeEpoch ?? 0) === (base.runtimeEpoch ?? 0) &&\n (e.sessionId ?? null) === (base.sessionId ?? null) &&\n (e.forkHandle?.forkId ?? null) === (base.forkHandle?.forkId ?? null) &&\n (e.prepareSession ?? null) === (base.prepareSession ?? null) &&\n (e.cwd ?? null) === (base.cwd ?? null) &&\n (e.sessionName ?? null) === (base.sessionName ?? null) &&\n (e.sessionType ?? null) === (base.sessionType ?? null) &&\n (e.onSessionId ?? null) === (base.onSessionId ?? null) &&\n (e.onSessionReset ?? null) === (base.onSessionReset ?? null) &&\n (e.bridgeSessionFromHistory ?? false) === (base.bridgeSessionFromHistory ?? false) &&\n (e.onSettled ?? null) === (base.onSettled ?? null);\n let end = bucket.head + 1;\n while (end < bucket.items.length && mergeable(bucket.items[end])) end++;\n const batch = bucket.items.slice(bucket.head, end);\n bucket.head = end;\n for (const entry of batch) {\n if (entry.requestId) bucket.requestIds.delete(entry.requestId);\n }\n this.compactOrDelete(topicId, bucket);\n if (batch.length === 1) return batch[0];\n\n const callbacks = batch\n .map((e) => e.onDispatched)\n .filter((cb): cb is NonNullable<typeof cb> => !!cb);\n const askReplySources = batch.flatMap((e): AskReplySource[] => {\n if (e.askReplySources?.length) return e.askReplySources;\n if (!e.requestId || !e.origin) return [];\n return [{ from: e.origin, requestId: e.requestId, contextId: e.contextId }];\n });\n const depths = batch\n .map((e) => e.depth)\n .filter((depth): depth is number => typeof depth === \"number\");\n return {\n ...base,\n prompt: batch.map((e) => e.prompt).join(\"\\n\\n\"),\n origin: baseIsAskReplyInject\n ? [...new Set(batch.map((e) => e.origin).filter(Boolean))].join(\", \")\n : base.origin,\n depth: depths.length ? Math.min(...depths) : base.depth,\n requestId: `merged-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n // Same-origin batches share one context; only drop it when the batch\n // disagrees (a merged turn must not append to the wrong context).\n contextId: batch.every((e) => e.contextId === base.contextId) ? base.contextId : undefined,\n askReplySources,\n onDispatched: callbacks.length\n ? (queryId: string) => {\n callbacks.forEach((cb) => {\n cb(queryId);\n });\n }\n : undefined,\n };\n }\n\n /** All non-empty queue keys for this topic room. */\n keysForRoom(): string[] {\n return Array.from(this.queue.keys());\n }\n\n /** Drop all queued injects for a topic (e.g. after room abort). */\n drop(topicId: string): void {\n this.queue.delete(topicId);\n }\n\n /** Number of queued injects for a topic. */\n size(topicId: string): number {\n const bucket = this.queue.get(topicId);\n return bucket ? bucket.items.length - bucket.head : 0;\n }\n\n /** Remove one queued inject by request id. Used by bounded background queues. */\n remove(topicId: string, requestId: string): DeferredInject | undefined {\n const bucket = this.queue.get(topicId);\n if (!bucket?.requestIds.has(requestId)) return undefined;\n const index = bucket.items.findIndex(\n (entry, entryIndex) => entryIndex >= bucket.head && entry.requestId === requestId,\n );\n if (index < 0) return undefined;\n const [removed] = bucket.items.splice(index, 1);\n bucket.requestIds.delete(requestId);\n this.compactOrDelete(topicId, bucket);\n return removed;\n }\n}\n\nexport interface DeferredInjectBatcherOptions {\n queue: InterSessionQueue;\n delayMs: number;\n isBusy: (topicId: string) => boolean;\n dispatch: (inject: DeferredInject) => void;\n schedule?: (callback: () => void, delayMs: number) => unknown;\n}\n\n/**\n * Gives caller-bound ask replies a short window to collect before dispatch.\n * `dequeueAll()` only merges entries that are already queued, so the first\n * reply must pass through this gate too when its caller room is idle.\n */\nexport class DeferredInjectBatcher {\n private readonly timers = new Map<string, unknown>();\n private readonly scheduleTimer: (callback: () => void, delayMs: number) => unknown;\n\n constructor(private readonly options: DeferredInjectBatcherOptions) {\n this.scheduleTimer = options.schedule ?? ((callback, delayMs) => setTimeout(callback, delayMs));\n }\n\n enqueue(inject: DeferredInject): boolean {\n const queued = this.options.queue.enqueue(inject.topicId, inject);\n if (!queued) return false;\n if (this.timers.has(inject.topicId)) return true;\n\n const timer = this.scheduleTimer(() => {\n this.timers.delete(inject.topicId);\n // A busy room owns the drain in its turn-finally path. Leaving the\n // entries queued avoids a second dispatcher racing that owner.\n if (this.options.isBusy(inject.topicId)) return;\n const batch = this.options.queue.dequeueAll(inject.topicId);\n if (batch) this.options.dispatch(batch);\n }, this.options.delayMs);\n this.timers.set(inject.topicId, timer);\n return true;\n }\n\n isScheduled(topicId: string): boolean {\n return this.timers.has(topicId);\n }\n}\n\nexport const interSessionQueue = new InterSessionQueue();\n\n// ── Defer / drain helpers (thin wrappers around InterSessionQueue) ────\n\n/**\n * Enqueue a deferred inject. With the full queue, this also deduplicates by\n * requestId.\n */\nexport function deferInject(inject: DeferredInject): boolean {\n return interSessionQueue.enqueue(inject.topicId, inject);\n}\n\n/**\n * Remove and return the longest mergeable batch of deferred injects for a room,\n * or undefined if empty.\n */\nexport function takeDeferredInject(topicId: string): DeferredInject | undefined {\n return interSessionQueue.dequeueAll(topicId);\n}\n\n/** Cancel one deferred inject before it claims the room. */\nexport function cancelDeferredInject(topicId: string, requestId: string): boolean {\n return interSessionQueue.remove(topicId, requestId) !== undefined;\n}\n\n// ── WS discriminator ───────────────────────────────────────────────────\n\n/** Map the internal AbortReason → the WS `ai_aborted` discriminator. */\nexport function wsAbortReason(reason: AbortReason): \"superseded\" | \"stopped\" {\n return reason === AbortReason.Internal ? \"superseded\" : \"stopped\";\n}\n",
|
|
15
15
|
"import { mkdirSync, renameSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { ACTIVE_QUERY_STALE_MS, USERS_LOG_DIR } from \"#platform/config\";\nimport { readJsonFile } from \"#platform/jsonl\";\nimport { logger } from \"#platform/logger\";\nimport { sanitizeId } from \"#security/sanitize\";\nimport type { QueryState } from \"#types\";\n\ntype QueryStateUserId = number | string;\n\nexport interface QueryStateStoreLogger {\n warn(context: Record<string, unknown>, message: string): void;\n}\n\nexport interface QueryStateStoreOptions {\n usersLogDir: string;\n logger?: QueryStateStoreLogger;\n sanitizeTopicId?: (topicId: string) => string;\n}\n\nexport interface QueryStateStore {\n write(userId: QueryStateUserId, topicId: string, topicName: string, task?: string): void;\n clear(userId: QueryStateUserId, topicId: string, legacyTopicName?: string): void;\n /**\n * True when a live (non-stale) active-query state file exists for this\n * user/topic pair. This reads the same on-disk marker `peek_session` /\n * `list_active_queries` trust, independent of any in-process bookkeeping\n * (turn leases, subagent watch maps) that does not survive a node restart.\n */\n hasActive(userId: QueryStateUserId, topicId: string): boolean;\n}\n\nexport function createQueryStateStore(options: QueryStateStoreOptions): QueryStateStore {\n const sanitize = options.sanitizeTopicId ?? sanitizeId;\n const queryStateDirPath = (userId: QueryStateUserId): string =>\n join(options.usersLogDir, String(userId), \"active-queries\");\n const queryStateFile = (userId: QueryStateUserId, topicId: string): string =>\n join(queryStateDirPath(userId), `${sanitize(topicId)}.json`);\n const legacyQueryStateFile = (\n userId: QueryStateUserId,\n topicName: string | undefined,\n ): string | null => {\n if (\n !topicName ||\n topicName === \".\" ||\n topicName === \"..\" ||\n basename(topicName) !== topicName\n ) {\n return null;\n }\n return join(queryStateDirPath(userId), `${topicName}.json`);\n };\n\n return {\n write(userId, topicId, topicName, task) {\n const dir = queryStateDirPath(userId);\n mkdirSync(dir, { recursive: true });\n const state: QueryState = { topicId, topicName, since: new Date().toISOString() };\n if (task) state.task = [...task.replace(/\\n+/g, \" \").trim()].slice(0, 100).join(\"\");\n const target = queryStateFile(userId, topicId);\n const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;\n writeFileSync(tmp, JSON.stringify(state));\n renameSync(tmp, target);\n },\n clear(userId, topicId, legacyTopicName) {\n const paths = [\n queryStateFile(userId, topicId),\n legacyQueryStateFile(userId, legacyTopicName),\n ].filter((path): path is string => Boolean(path));\n for (const path of new Set(paths)) {\n try {\n unlinkSync(path);\n } catch (error) {\n if ((error as NodeJS.ErrnoException)?.code !== \"ENOENT\") {\n options.logger?.warn(\n { err: error, userId, topicId, path },\n \"Failed to clear query state file\",\n );\n }\n }\n }\n },\n hasActive(userId, topicId) {\n const state = readJsonFile<QueryState>(queryStateFile(userId, topicId));\n if (!state) return false;\n const sinceMs = new Date(state.since).getTime();\n return Date.now() - sinceMs <= ACTIVE_QUERY_STALE_MS;\n },\n };\n}\n\nconst defaultQueryStateStore = createQueryStateStore({\n usersLogDir: USERS_LOG_DIR,\n logger,\n});\n\nexport function writeQueryState(\n userId: QueryStateUserId,\n topicId: string,\n topicName: string,\n task?: string,\n) {\n defaultQueryStateStore.write(userId, topicId, topicName, task);\n}\n\nexport function clearQueryState(\n userId: QueryStateUserId,\n topicId: string,\n legacyTopicName?: string,\n) {\n defaultQueryStateStore.clear(userId, topicId, legacyTopicName);\n}\n\nexport function hasActiveQuery(userId: QueryStateUserId, topicId: string): boolean {\n return defaultQueryStateStore.hasActive(userId, topicId);\n}\n",
|
|
16
|
-
"import { execFileSync } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport {\n accessSync,\n chmodSync,\n constants,\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport {\n parseRuntimePort,\n readEnvText,\n resolveFallbackAgent,\n safeRuntimePathSegment,\n} from \"#platform/config-helpers\";\nimport { logger } from \"#platform/logger\";\nimport { type AgentKind, EFFORT_VALUES, type EffortLevel } from \"#types\";\n\nexport function envText(envKey: string): string | undefined {\n return readEnvText(process.env, envKey);\n}\n\nconst HOME = homedir();\n\nfunction resolveStateDir(): string {\n const configured = envText(\"NEGOTIUM_STATE_DIR\");\n if (process.env.NODE_ENV === \"test\" && !configured) {\n throw new Error(\n \"NEGOTIUM_STATE_DIR must be set before importing #platform/config in test processes\",\n );\n }\n return configured ? resolve(configured) : resolve(HOME, \".negotium\");\n}\n\n// new URL(\"../..\", import.meta.url) causes webpack to treat \"../..\" as a module import.\n// Split into fileURLToPath → dirname → resolve to avoid that.\nfunction resolveProjectRoot(): string {\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n const packagedRuntime = resolve(moduleDir, \"runtime\");\n if (existsSync(resolve(packagedRuntime, \"src\"))) return packagedRuntime;\n return resolve(moduleDir, \"../..\");\n}\n\nexport const PROJECT_ROOT = resolveProjectRoot();\n\n/** Resolve a dependency executable from either a package-local or hoisted install. */\n/**\n * Names a package-manager shim can carry in `node_modules/.bin`.\n *\n * POSIX gets one extension-less executable. Windows has no execute bit, so the\n * installer writes an extension instead — bun produces `<name>.exe` plus a\n * `<name>.bunx` stub, npm a `.cmd`/`.ps1` pair — and probing only the bare name\n * finds nothing, leaving the resolved path pointing at a file that does not\n * exist.\n */\nfunction dependencyBinNames(name: string): string[] {\n if (process.platform !== \"win32\") return [name];\n return [name, `${name}.exe`, `${name}.cmd`, `${name}.bat`, `${name}.bunx`];\n}\n\nfunction resolveDependencyBin(name: string): string {\n let dir = PROJECT_ROOT;\n while (true) {\n const binDir = resolve(dir, \"node_modules\", \".bin\");\n for (const candidate of dependencyBinNames(name)) {\n const path = resolve(binDir, candidate);\n if (existsSync(path)) return path;\n }\n const parent = dirname(dir);\n // Exhausted: hand back the canonical spelling so the failure names the\n // path that was expected.\n if (parent === dir) return resolve(binDir, name);\n dir = parent;\n }\n}\n\n// Each machine is one negotium node; all node state lives in one dotdir.\n// NEGOTIUM_STATE_DIR overrides (useful for tests and multi-node-on-one-box).\nexport const STATE_DIR = resolveStateDir();\n\nfunction resolveLocalStateDir(envKey: string, stateName: string): string {\n const envValue = envText(envKey);\n if (envValue) return resolve(envValue);\n return resolve(STATE_DIR, stateName);\n}\n\nfunction parsePortEnv(envValue: string | undefined, fallback: number): number {\n return parseRuntimePort(envValue, fallback);\n}\n\nexport const WORKSPACE_DIR = resolveLocalStateDir(\"NEGOTIUM_WORKSPACE_DIR\", \"workspace\");\nexport const TOPIC_WORKSPACE_DIR = resolve(WORKSPACE_DIR, \"topics\");\nexport const SHARED_WIKI_DIR = resolve(WORKSPACE_DIR, \"wiki\");\nexport const CRON_WORKSPACE_DIR = resolve(WORKSPACE_DIR, \"cron\");\nexport const BROWSER_DIR = resolveLocalStateDir(\"NEGOTIUM_BROWSER_DIR\", \"browser\");\nexport const BROWSER_PROFILES_DIR = resolve(BROWSER_DIR, \"profiles\");\nexport const BINARIES_DIR = resolve(STATE_DIR, \"binaries\");\nexport const SECRETS_DIR = resolve(STATE_DIR, \"secrets\");\n// Legacy names remain public, but their durable contents now live under data.\nexport const DM_WORKSPACE_DIR = resolve(STATE_DIR, \"data\", \"dm\");\nexport const SESSION_WORKSPACE_DIR = resolve(STATE_DIR, \"data\", \"sessions\");\n// The Claude Agent SDK ships a platform-matched Claude Code binary. Keep that\n// SDK/CLI pair together by default; only use an external executable when an\n// operator explicitly opts in. This avoids silently pairing an older SDK with\n// a newer globally installed Claude Code release.\nconst CLAUDE_EXECUTABLE_ENV = envText(\"NEGOTIUM_CLAUDE_EXECUTABLE\");\nexport const CLAUDE_EXECUTABLE = CLAUDE_EXECUTABLE_ENV ? resolve(CLAUDE_EXECUTABLE_ENV) : undefined;\n\n// Fallback output language for model-generated prose (topic/channel/manager\n// replies and the archiver's summaries, briefs, articles, completion reply).\n// Defaults to English; set `NEGOTIUM_LANG` to the user's mother tongue (e.g.\n// `Korean`, `ko`). The assistant still mirrors whatever language the user\n// writes in; `NEGOTIUM_MEMORY_LANG` can narrow just the archiver. Fixed system\n// chrome / degraded-path strings emitted by code stay English (can't translate\n// an arbitrary value at runtime).\nexport const DEFAULT_OUTPUT_LANGUAGE = \"English\";\n\nexport function resolveOutputLanguage(): string {\n const raw = envText(\"NEGOTIUM_LANG\")?.trim();\n return raw && raw.length > 0 ? raw : DEFAULT_OUTPUT_LANGUAGE;\n}\n\n/**\n * Suffix the managed Rust helpers carry on this platform.\n *\n * The installers write the binary under its plain name, which on Windows is\n * not executable — the release asset is `browser-rs.exe` and the resolver has\n * to look for exactly that. Empty everywhere else, so POSIX paths are\n * unchanged.\n */\nexport const BINARY_EXTENSION = process.platform === \"win32\" ? \".exe\" : \"\";\n\n/** Browser.rs release tested with this Negotium version. */\nexport const BROWSER_RS_VERSION = \"v0.7.0\";\n/** Require the authenticated listener and the current Browser.rs tool contract. */\nexport const BROWSER_RS_MIN_SECURE_VERSION = \"0.2.1\";\n\nfunction versionAtLeast(actualVersion: string, minimumVersion: string): boolean {\n const actual = actualVersion.split(\".\").map(Number);\n const minimum = minimumVersion.split(\".\").map(Number);\n if (actual.some(Number.isNaN) || minimum.some(Number.isNaN)) return false;\n for (let index = 0; index < minimum.length; index += 1) {\n if ((actual[index] ?? 0) > (minimum[index] ?? 0)) return true;\n if ((actual[index] ?? 0) < (minimum[index] ?? 0)) return false;\n }\n return true;\n}\n\n/**\n * Ceiling for the `--version` probe, not a latency expectation.\n *\n * The probe exists so a wedged binary cannot hang startup; a healthy one\n * answers in milliseconds. At 2s it also failed whenever the machine was merely\n * busy — process startup under a loaded test suite regularly exceeds that —\n * which turned a correctness check into a load measurement and made\n * `resolveBrowserRsBin` intermittently report a perfectly good binary as\n * unusable. Generous enough that only a genuinely stuck process trips it.\n */\nconst BROWSER_RS_VERSION_PROBE_TIMEOUT_MS = 15_000;\n\nfunction browserRsMeetsMinimumVersion(candidate: string): boolean {\n try {\n const output = execFileSync(candidate, [\"--version\"], {\n encoding: \"utf8\",\n timeout: BROWSER_RS_VERSION_PROBE_TIMEOUT_MS,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n // This module is imported by every CLI invocation, so on Windows the\n // probe blinked a console window each time `negotium` was run. Ignored\n // on POSIX.\n windowsHide: true,\n }).trim();\n const match = output.match(/^browser-rs (\\d+)\\.(\\d+)\\.(\\d+)$/);\n if (!match) return false;\n return versionAtLeast(match.slice(1).join(\".\"), BROWSER_RS_MIN_SECURE_VERSION);\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the preferred Browser.rs engine without consulting PATH. The\n * versioned private location keeps different Negotium releases reproducible\n * and avoids changing a user's global browser-rs installation.\n */\nexport function resolveBrowserRsBin(envValue?: string): string | undefined {\n const override = envValue?.trim();\n if (\n !override &&\n !versionAtLeast(BROWSER_RS_VERSION.replace(/^v/, \"\"), BROWSER_RS_MIN_SECURE_VERSION)\n ) {\n return undefined;\n }\n const candidate = override\n ? resolve(override)\n : resolve(BINARIES_DIR, \"browser-rs\", BROWSER_RS_VERSION, `browser-rs${BINARY_EXTENSION}`);\n try {\n accessSync(candidate, constants.X_OK);\n return browserRsMeetsMinimumVersion(candidate) ? candidate : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Backend that stores documents published by `publish_html` and serves them\n * at `<base>/snippets/<id>`. Unset means the deployment has nowhere to\n * publish, and the publish tools are omitted from the catalog rather than\n * offering a link that cannot be minted.\n */\nexport const SNIPPETS_API_URL = (\n envText(\"NEGOTIUM_SNIPPETS_API_URL\") ??\n envText(\"SNIPPETS_API_URL\") ??\n \"\"\n).replace(/\\/+$/, \"\");\n\nexport const BROWSER_RS_BIN = resolveBrowserRsBin(envText(\"NEGOTIUM_BROWSER_RS_BIN\"));\n\n/** bash-rs release tested with this Negotium version — see apps/negotium/install-bash-rs.mjs. */\nexport const BASH_RS_VERSION = \"v0.1.10\";\n\n/**\n * Resolve the bash-rs binary the same way `resolveBrowserRsBin` resolves\n * Browser.rs: a versioned private location, no PATH lookup, `undefined`\n * (rather than throwing) when it's missing. There is no substitute: the manager\n * throws when a turn tries to prepare background-bash without a binary (see\n * `background-bash/manager.ts`), the turn runner catches that and continues\n * with a reminder that the tools are gone, so background-bash is simply\n * unavailable on a platform without a prebuilt binary.\n */\nexport function resolveBashRsBin(envValue?: string): string | undefined {\n const override = envValue?.trim();\n const candidate = override\n ? resolve(override)\n : resolve(BINARIES_DIR, \"bash-rs\", BASH_RS_VERSION, `bash-rs${BINARY_EXTENSION}`);\n try {\n accessSync(candidate, constants.X_OK);\n return candidate;\n } catch {\n return undefined;\n }\n}\n\nexport const BASH_RS_BIN = resolveBashRsBin(envText(\"NEGOTIUM_BASH_RS_BIN\"));\n\n// Managed Browser.rs terminates MCP transports and security policy itself.\nexport function resolveBrowserMcpBin(envValue?: string): string {\n const override = envValue?.trim();\n if (override) return resolve(override);\n return (\n BROWSER_RS_BIN ??\n resolve(BINARIES_DIR, \"browser-rs\", BROWSER_RS_VERSION, `browser-rs${BINARY_EXTENSION}`)\n );\n}\n\nexport const PLAYWRIGHT_MCP_BIN = resolveBrowserMcpBin(envText(\"NEGOTIUM_BROWSER_MCP_BIN\"));\n\n// --- Browser egress proxy ---\n//\n// On a datacenter host (AWS) the browser's egress IP is a known cloud range,\n// so anti-bot services (Cloudflare, DataDome, reCAPTCHA) challenge or block it\n// far more than a residential IP would. Routing the automation browser through\n// a residential/ISP proxy moves the egress IP out of the datacenter range.\n//\n// Operators set BROWSER_PROXY_URL, e.g. http://user:pass@proxy.host:8080 or\n// socks5://proxy.host:1080. Credentials in the URL are split out because\n// Playwright takes them as separate fields. BROWSER_PROXY_BYPASS is an optional\n// comma-separated no-proxy list (e.g. \"localhost,127.0.0.1,*.internal\").\n//\n// NOTE: Chromium does not support authentication for SOCKS proxies — put\n// credentials only on http/https proxy URLs.\nexport type BrowserProxyConfig = {\n server: string;\n username?: string;\n password?: string;\n bypass?: string;\n};\n\nexport function resolveBrowserProxy(): BrowserProxyConfig | null {\n const raw = envText(\"BROWSER_PROXY_URL\");\n if (!raw) return null;\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n logger.warn({ raw }, \"Ignoring malformed BROWSER_PROXY_URL\");\n return null;\n }\n // Playwright wants the server without embedded credentials.\n const server = `${url.protocol}//${url.host}`;\n const proxy: BrowserProxyConfig = { server };\n if (url.username) proxy.username = decodeURIComponent(url.username);\n if (url.password) proxy.password = decodeURIComponent(url.password);\n const bypass = envText(\"BROWSER_PROXY_BYPASS\");\n if (bypass) proxy.bypass = bypass;\n return proxy;\n}\n\n// --- Node/tsx runtime for the `codex` agent's MCP servers ---\n//\n// codex 0.135's rmcp stdio MCP client cannot reliably complete the initialize\n// handshake with servers spawned via `bun` (the JSON-RPC initialize is\n// dropped/raced and no tools ever reach the model). Pure-node servers connect\n// reliably, so codex turns launch the SAME .ts servers via node + tsx instead\n// of `bun run`. claude/maestro keep using `bun run` (fast, native, unaffected).\n//\n// tsx transpiles .ts on the fly and resolves the `@/*` tsconfig path aliases,\n// but only when it can find the tsconfig — and MCP servers run with cwd set to\n// the user's workspace dir, not PROJECT_ROOT — so we pass TSX_TSCONFIG_PATH\n// explicitly via env. Requires package.json `\"type\": \"module\"` so the servers'\n// top-level `await` loads as ESM under node.\nexport const TSX_BIN = resolveDependencyBin(\"tsx\");\n/**\n * In-process tsx loader used by Node MCP entrypoints (avoids the tsx CLI child\n * process).\n *\n * `node --import` takes a module specifier, not a filesystem path. A POSIX\n * absolute path happens to be a valid one; a Windows `C:\\…` is not — Node reads\n * the drive letter as a URL scheme and refuses it with\n * ERR_UNSUPPORTED_ESM_URL_SCHEME, which took out every tsx-hosted MCP server\n * (the Codex tool surface) at spawn. Hand it a `file://` URL there, and leave\n * the POSIX value exactly as it was.\n */\nconst TSX_LOADER_PATH = createRequire(import.meta.url).resolve(\"tsx\");\nexport const TSX_LOADER =\n process.platform === \"win32\" ? pathToFileURL(TSX_LOADER_PATH).href : TSX_LOADER_PATH;\nexport const TSCONFIG_PATH = resolve(PROJECT_ROOT, \"tsconfig.json\");\n\nexport const SESSION_COMM_SERVER = resolve(PROJECT_ROOT, \"src/mcp/session-comm/server.ts\");\n\nexport const TASK_SERVER = resolve(PROJECT_ROOT, \"src/mcp/task-server.ts\");\nexport const DECISION_SERVER = resolve(PROJECT_ROOT, \"src/mcp/decision-server.ts\");\nexport const CANONICAL_MCP_PROXY_SERVER = resolve(\n PROJECT_ROOT,\n \"src/mcp/canonical-proxy-server.ts\",\n);\n\nexport const WIKI_SERVER = resolve(PROJECT_ROOT, \"src/mcp/wiki-server.ts\");\n\nexport const TOKEN_STATS_SERVER = resolve(PROJECT_ROOT, \"src/mcp/token-stats-server.ts\");\n\nexport const COMPACTION_LOG_SERVER = resolve(PROJECT_ROOT, \"src/mcp/compaction-log-server.ts\");\n\nexport const SYSTEM_HEALTH_SERVER = resolve(PROJECT_ROOT, \"src/mcp/system-health-server.ts\");\n\nexport const AGENT_HEALTH_SERVER = resolve(PROJECT_ROOT, \"src/mcp/agent-health-server.ts\");\n\nexport const VAULT_SERVER = resolve(PROJECT_ROOT, \"src/mcp/vault-server.ts\");\n\nexport const BG_BASH_BASE_PORT = parsePortEnv(process.env.BG_BASH_BASE_PORT, 9700);\nexport const BG_BASH_MAX_PORT = parsePortEnv(process.env.BG_BASH_MAX_PORT, 9799);\n\nfunction safeWorkspaceSegment(value: string, fallback: string): string {\n return safeRuntimePathSegment(value, fallback);\n}\n\n/** Resolve the shared filesystem workspace for an API topic. */\nexport function resolveTopicWorkspaceDir(topicId: string): string {\n return join(TOPIC_WORKSPACE_DIR, safeWorkspaceSegment(topicId, \"topic\"));\n}\n\nexport function isProductionEnv(): boolean {\n return process.env.NODE_ENV === \"production\";\n}\n\nfunction loadOrCreateLocalSecret(\n envKey: string,\n filename: string,\n options: { persistEnvValue?: boolean } = {},\n): string {\n const envValue = envText(envKey);\n const secretFile = resolve(SECRETS_DIR, filename);\n mkdirSync(dirname(secretFile), { recursive: true });\n if (envValue) {\n if (options.persistEnvValue) {\n writeFileSync(secretFile, `${envValue}\\n`, { mode: 0o600 });\n chmodSync(secretFile, 0o600);\n }\n return envValue;\n }\n\n if (existsSync(secretFile)) {\n const stored = readFileSync(secretFile, \"utf-8\").trim();\n if (stored) {\n chmodSync(secretFile, 0o600);\n return stored;\n }\n }\n\n const secret = randomBytes(32).toString(\"base64url\");\n try {\n writeFileSync(secretFile, `${secret}\\n`, { mode: 0o600, flag: \"wx\" });\n return secret;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n const stored = readFileSync(secretFile, \"utf-8\").trim();\n if (!stored) throw new Error(`Secret file exists but is empty: ${secretFile}`);\n chmodSync(secretFile, 0o600);\n return stored;\n }\n}\n\nexport const RUNTIME_MCP_SECRET = loadOrCreateLocalSecret(\n \"RUNTIME_MCP_SECRET\",\n \"runtime-mcp-secret\",\n);\n/** Local bearer token for the loopback node-control API. */\nexport const NODE_CONTROL_TOKEN = loadOrCreateLocalSecret(\n \"NEGOTIUM_CONTROL_TOKEN\",\n \"node-control-token\",\n);\n/**\n * Stable identity of this node's store, minted once and kept across restarts.\n *\n * Deliberately not a secret: it is published in the gateway health response so a\n * host can tell \"the node I recorded a mapping against\" apart from \"whatever is\n * answering on that port today\". Without it, pointing a host at a different node\n * makes every existing mapping look like a topic whose owner withdrew it, and the\n * same topics come back as duplicate rooms. It shares the secrets directory only\n * because that is already where per-install state that must survive restarts\n * lives; the stricter file mode costs nothing.\n */\nexport const NODE_ID = loadOrCreateLocalSecret(\"NEGOTIUM_NODE_ID\", \"node-id\");\nexport const VAULT_MASTER_KEY = loadOrCreateLocalSecret(\n \"NEGOTIUM_VAULT_MASTER_KEY\",\n \"vault-master-key\",\n { persistEnvValue: true },\n);\n// Agent/tool subprocesses inherit process.env. Keep the loaded key in this\n// process only so `env`/`ps` inside an agent workspace cannot reveal it.\ndelete process.env.NEGOTIUM_VAULT_MASTER_KEY;\n\n/** The node's single open port: runtime MCP endpoint + node API. */\nexport const NEGOTIUM_PORT = parseInt(process.env.NEGOTIUM_PORT || \"7777\", 10);\nexport const hostname = process.env.HOSTNAME || \"127.0.0.1\";\n\n// Persistent state (survives restarts, long-lived)\nexport const DATA_DIR = resolveLocalStateDir(\"NEGOTIUM_DATA_DIR\", \"data\");\nexport const LOG_DIR = resolveLocalStateDir(\"NEGOTIUM_LOG_DIR\", \"logs\");\nexport const UPLOADS_DIR = resolve(DATA_DIR, \"uploads\");\nexport const VAULT_DIR = resolve(DATA_DIR, \"vault\");\n// SESSIONS_DB_PATH env override lets tests point the DB singleton at a temp file.\nexport const SESSIONS_DB = process.env.SESSIONS_DB_PATH\n ? resolve(process.env.SESSIONS_DB_PATH)\n : resolve(DATA_DIR, \"sessions.db\");\nexport const DEBUG_FILE = resolve(DATA_DIR, \"debug-users.json\");\nexport const USERS_LOG_DIR = resolve(DATA_DIR, \"users\");\n\n// Runtime IPC queues (transient, safe to clear on restart)\n/** @deprecated Use the runtime layout name; NEGOTIUM_RUN_DIR remains a compatibility override. */\nexport const RUN_DIR = resolveLocalStateDir(\"NEGOTIUM_RUN_DIR\", \"runtime\");\nexport const RUNTIME_DIR = RUN_DIR;\nexport const PROGRESS_DIR = resolve(RUN_DIR, \"progress\");\nexport const DM_CMD_DIR = resolve(RUN_DIR, \"dm-commands\");\nexport const DM_RESP_DIR = resolve(RUN_DIR, \"dm-responses\");\nexport const SESSION_INBOX_DIR = resolve(RUN_DIR, \"session-inbox\");\nexport const SESSION_ASKS_DIR = resolve(RUN_DIR, \"session-asks\");\n/**\n * Passed to `bash-rs` as `BASHRS_SPILL_ROOT`. Each job gets a subdirectory\n * here (`{bash_id}/meta.json`, `result.json`, `stdout.log`, `stderr.log`) —\n * see bash-rs-mcp's `journal.rs`. `runtime/bashrs-completions.ts` watches\n * this directory and turns `result.json` into a session-inbox `tell`. The\n * retired TypeScript server used to write one directly instead.\n */\nexport const BASHRS_SPILL_ROOT = resolve(RUN_DIR, \"bashrs\");\nexport const PLAYWRIGHT_BASE_PORT = parsePortEnv(process.env.PLAYWRIGHT_BASE_PORT, 9100);\nexport const PLAYWRIGHT_MAX_PORT = parsePortEnv(process.env.PLAYWRIGHT_MAX_PORT, 9499);\nexport const PLAYWRIGHT_PORTS_DIR = resolve(RUN_DIR, \"playwright-ports\");\nmkdirSync(STATE_DIR, { recursive: true });\nmkdirSync(DATA_DIR, { recursive: true });\nmkdirSync(UPLOADS_DIR, { recursive: true });\nmkdirSync(VAULT_DIR, { recursive: true, mode: 0o700 });\nmkdirSync(LOG_DIR, { recursive: true });\nmkdirSync(PROGRESS_DIR, { recursive: true });\nmkdirSync(DM_CMD_DIR, { recursive: true });\nmkdirSync(DM_RESP_DIR, { recursive: true });\nmkdirSync(SESSION_INBOX_DIR, { recursive: true });\nmkdirSync(SESSION_ASKS_DIR, { recursive: true });\nmkdirSync(PLAYWRIGHT_PORTS_DIR, { recursive: true });\nmkdirSync(WORKSPACE_DIR, { recursive: true });\nmkdirSync(TOPIC_WORKSPACE_DIR, { recursive: true });\nmkdirSync(SHARED_WIKI_DIR, { recursive: true });\nmkdirSync(CRON_WORKSPACE_DIR, { recursive: true });\nmkdirSync(DM_WORKSPACE_DIR, { recursive: true });\nmkdirSync(SESSION_WORKSPACE_DIR, { recursive: true });\nmkdirSync(BROWSER_DIR, { recursive: true });\nmkdirSync(BROWSER_PROFILES_DIR, { recursive: true });\nmkdirSync(BINARIES_DIR, { recursive: true });\nmkdirSync(SECRETS_DIR, { recursive: true, mode: 0o700 });\n\n/** Stale threshold for active-query state files (crash recovery) */\nexport const ACTIVE_QUERY_STALE_MS = 10 * 60 * 1000; // 10 minutes\n\nexport const AGENTS_PROMPTS_DIR = resolve(PROJECT_ROOT, \"src/prompts/agents\");\nexport const RESOURCES_DIR = resolve(PROJECT_ROOT, \"src/resources\");\n\n/** Returns process.env without CLAUDECODE, to prevent nested claude-code detection in subprocesses. */\nexport function getCleanEnv(): NodeJS.ProcessEnv {\n const env = { ...process.env };\n delete env.CLAUDECODE;\n return env;\n}\n\n/**\n * How an absolute path can start.\n *\n * A POSIX root (`/`) is not the only form once the runtime also runs on\n * Windows, where the same tag carries `C:\\…` or a `\\\\server\\share` UNC path.\n * Anchoring on `/` alone silently dropped every Windows attachment: the tag\n * stayed in the visible text and no file was ever sent.\n */\nconst ABSOLUTE_PATH_PREFIX = String.raw`(?:[A-Za-z]:[\\\\/]|\\\\\\\\|\\/)`;\n\nexport const FILE_EXTENSIONS_REGEX = new RegExp(\n `(?:${ABSOLUTE_PATH_PREFIX}[^\\\\s\"'<>|*?[\\\\]]+\\\\.(?:png|jpg|jpeg|gif|webp|svg|pdf|csv|xlsx|xls|json|txt|md|html|zip|py|js|ts|tsx|jsx|css|xml|yaml|yml|docx|pptx))`,\n \"gi\",\n);\n\nexport const FILE_TAG_REGEX = new RegExp(`\\\\[FILE:(${ABSOLUTE_PATH_PREFIX}[^\\\\]]+)\\\\]`, \"gi\");\n\n// Canonical Claude model IDs — update here when Anthropic releases new versions\nexport const MODEL_SONNET = \"claude-sonnet-5\";\nexport const MODEL_OPUS = \"claude-opus-5-5\";\nexport const MODEL_HAIKU = \"claude-haiku-4-5-20251001\";\nexport const MODEL_FABLE = \"claude-fable-5-1\"; // Mythos-class, Fable 5.1 announced 2026-09-01 (was claude-fable-5)\n\n// DeepSeek V4 (released 2026-04-24). API is OpenAI-compatible at\n// https://api.deepseek.com/v1/chat/completions; thinking mode is enabled via\n// `extra_body.thinking.type` + `reasoning_effort`. Legacy `deepseek-chat` /\n// `deepseek-reasoner` are deprecated 2026-07-24.\nexport const MODEL_DEEPSEEK_V4_PRO = \"deepseek-v4-pro\";\nexport const MODEL_DEEPSEEK_V4_FLASH = \"deepseek-v4-flash\";\n\n// Single agent + model default for the whole node. DEFAULT_* remains a legacy alias.\nexport const FALLBACK_AGENT: AgentKind = resolveFallbackAgent(process.env);\nexport const FALLBACK_MODEL = envText(\"FALLBACK_MODEL\") ?? envText(\"DEFAULT_MODEL\");\n\n/**\n * Effort used when a caller (or an archiver assignment) names a model but no\n * effort. Deliberately one fixed node-wide value instead of each agent's\n * registry default: a model-derived agent switch would otherwise move effort —\n * and therefore cost — without anyone asking for it.\n */\nexport const DEFAULT_TOPIC_EFFORT: EffortLevel = ((): EffortLevel => {\n const raw = envText(\"NEGOTIUM_DEFAULT_EFFORT\")?.toLowerCase();\n return raw && (EFFORT_VALUES as readonly string[]).includes(raw)\n ? (raw as EffortLevel)\n : \"medium\";\n})();\n\n// ── External tool binaries + media pipeline env ───────────────────\n// (src/media/* 에서 사용. 미설정 시 fallback 의미는 기존 그대로:\n// FFMPEG_BIN은 text-extractor에서 필수(undefined면 spawn 시점 실패),\n// video.ts에서는 PATH의 ffmpeg/ffprobe로 fallback.)\nexport const FFMPEG_BIN = envText(\"FFMPEG_BIN\");\nexport const FFPROBE_BIN = envText(\"FFPROBE_BIN\");\nexport const PYTHON_BIN = envText(\"PYTHON_BIN\") ?? \"python3\";\nexport const FASTER_WHISPER_WRAPPER =\n envText(\"FASTER_WHISPER_WRAPPER\") ?? resolve(PROJECT_ROOT, \"scripts/faster-whisper-wrapper.py\");\nexport const WHISPER_MODEL = envText(\"WHISPER_MODEL_FILE\") ?? \"turbo\";\nexport const TESSERACT_BIN = envText(\"TESSERACT_BIN\") ?? \"tesseract\";\nexport const PDFTOTEXT_BIN = envText(\"PDFTOTEXT_BIN\") ?? \"pdftotext\";\n\n// ── Whisper backend selection ──────────────────────────────────────\n// Different hosts want different whisper runtimes: Apple Silicon Macs get\n// real acceleration from mlx-whisper (see clawgram's transcribe.ts, which\n// this mirrors), while Linux/Ubuntu hubs have no MLX and use the CPU-friendly\n// faster-whisper wrapper instead. Both CLIs accept the same\n// `INPUT --model M --language L --output-dir D --output-format txt` shape\n// and write `{basename}.txt`, so extractFromAudio only needs to pick which\n// binary/args to use — no per-backend parsing logic.\nexport type WhisperBackend = \"faster-whisper\" | \"mlx-whisper\";\nfunction resolveWhisperBackend(): WhisperBackend {\n return envText(\"WHISPER_BACKEND\") === \"mlx-whisper\" ? \"mlx-whisper\" : \"faster-whisper\";\n}\nexport const WHISPER_BACKEND = resolveWhisperBackend();\n/** Path to the `mlx_whisper` CLI (only used when WHISPER_BACKEND=mlx-whisper). */\nexport const MLX_WHISPER_BIN = envText(\"MLX_WHISPER_BIN\");\n/** mlx-whisper model id, e.g. \"mlx-community/whisper-large-v3-turbo\".\n *\n * Deliberately does NOT fall back to WHISPER_MODEL: that env defaults to the\n * bare alias \"turbo\", which faster-whisper's own model-name normalization\n * accepts but mlx_whisper's `--model` does not — it expects a full\n * HuggingFace repo id and otherwise fails hard with a 401\n * RepositoryNotFoundError trying to resolve \"turbo\" as a repo. Confirmed by\n * actually running mlx_whisper with each value while wiring up the\n * mlx-whisper hub in production. */\nexport const WHISPER_MODEL_MLX =\n envText(\"WHISPER_MODEL_MLX\") ?? \"mlx-community/whisper-large-v3-turbo\";\n\n// Max tell_session relay depth from origin user. ask_session forks reset to\n// depth=0, so this only caps tell_session chains. Override via MAX_TELL_DEPTH\n// (positive int); defaults to 20 when unset or invalid.\nconst _envMaxTellDepth = Number.parseInt(process.env.MAX_TELL_DEPTH ?? \"\", 10);\nexport const MAX_TELL_DEPTH =\n Number.isInteger(_envMaxTellDepth) && _envMaxTellDepth > 0 ? _envMaxTellDepth : 20;\n\n/** Codex CLI auth file. 호출 시점에 env를 읽는다 — 테스트가 런타임에\n * NEGOTIUM_CODEX_AUTH_FILE을 바꾸므로 모듈 로드 상수로 만들면 안 된다. */\nexport function codexAuthFilePath(): string {\n return (\n process.env.NEGOTIUM_CODEX_AUTH_FILE ||\n join(process.env.CODEX_HOME || join(homedir(), \".codex\"), \"auth.json\")\n );\n}\n\n// System defaults moved to per-agent registries\n// (`src/agents/{claude,codex}-registry.ts`). Read via\n// `getRegistry(agent).defaultModel` / `.defaultEffort`.\n\n// MCP server builders -> src/platform/mcp-config.ts\n",
|
|
16
|
+
"import { execFileSync } from \"node:child_process\";\nimport { randomBytes } from \"node:crypto\";\nimport {\n accessSync,\n chmodSync,\n constants,\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport {\n parseRuntimePort,\n readEnvText,\n resolveFallbackAgent,\n safeRuntimePathSegment,\n} from \"#platform/config-helpers\";\nimport { logger } from \"#platform/logger\";\nimport { type AgentKind, EFFORT_VALUES, type EffortLevel } from \"#types\";\n\nexport function envText(envKey: string): string | undefined {\n return readEnvText(process.env, envKey);\n}\n\nconst HOME = homedir();\n\nfunction resolveStateDir(): string {\n const configured = envText(\"NEGOTIUM_STATE_DIR\");\n if (process.env.NODE_ENV === \"test\" && !configured) {\n throw new Error(\n \"NEGOTIUM_STATE_DIR must be set before importing #platform/config in test processes\",\n );\n }\n return configured ? resolve(configured) : resolve(HOME, \".negotium\");\n}\n\n// new URL(\"../..\", import.meta.url) causes webpack to treat \"../..\" as a module import.\n// Split into fileURLToPath → dirname → resolve to avoid that.\nfunction resolveProjectRoot(): string {\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n const packagedRuntime = resolve(moduleDir, \"runtime\");\n if (existsSync(resolve(packagedRuntime, \"src\"))) return packagedRuntime;\n return resolve(moduleDir, \"../..\");\n}\n\nexport const PROJECT_ROOT = resolveProjectRoot();\n\n/** Resolve a dependency executable from either a package-local or hoisted install. */\n/**\n * Names a package-manager shim can carry in `node_modules/.bin`.\n *\n * POSIX gets one extension-less executable. Windows has no execute bit, so the\n * installer writes an extension instead — bun produces `<name>.exe` plus a\n * `<name>.bunx` stub, npm a `.cmd`/`.ps1` pair — and probing only the bare name\n * finds nothing, leaving the resolved path pointing at a file that does not\n * exist.\n */\nfunction dependencyBinNames(name: string): string[] {\n if (process.platform !== \"win32\") return [name];\n return [name, `${name}.exe`, `${name}.cmd`, `${name}.bat`, `${name}.bunx`];\n}\n\nfunction resolveDependencyBin(name: string): string {\n let dir = PROJECT_ROOT;\n while (true) {\n const binDir = resolve(dir, \"node_modules\", \".bin\");\n for (const candidate of dependencyBinNames(name)) {\n const path = resolve(binDir, candidate);\n if (existsSync(path)) return path;\n }\n const parent = dirname(dir);\n // Exhausted: hand back the canonical spelling so the failure names the\n // path that was expected.\n if (parent === dir) return resolve(binDir, name);\n dir = parent;\n }\n}\n\n// Each machine is one negotium node; all node state lives in one dotdir.\n// NEGOTIUM_STATE_DIR overrides (useful for tests and multi-node-on-one-box).\nexport const STATE_DIR = resolveStateDir();\n\nfunction resolveLocalStateDir(envKey: string, stateName: string): string {\n const envValue = envText(envKey);\n if (envValue) return resolve(envValue);\n return resolve(STATE_DIR, stateName);\n}\n\nfunction parsePortEnv(envValue: string | undefined, fallback: number): number {\n return parseRuntimePort(envValue, fallback);\n}\n\nexport const WORKSPACE_DIR = resolveLocalStateDir(\"NEGOTIUM_WORKSPACE_DIR\", \"workspace\");\nexport const TOPIC_WORKSPACE_DIR = resolve(WORKSPACE_DIR, \"topics\");\nexport const SHARED_WIKI_DIR = resolve(WORKSPACE_DIR, \"wiki\");\nexport const CRON_WORKSPACE_DIR = resolve(WORKSPACE_DIR, \"cron\");\nexport const BROWSER_DIR = resolveLocalStateDir(\"NEGOTIUM_BROWSER_DIR\", \"browser\");\nexport const BROWSER_PROFILES_DIR = resolve(BROWSER_DIR, \"profiles\");\nexport const BINARIES_DIR = resolve(STATE_DIR, \"binaries\");\nexport const SECRETS_DIR = resolve(STATE_DIR, \"secrets\");\n// Legacy names remain public, but their durable contents now live under data.\nexport const DM_WORKSPACE_DIR = resolve(STATE_DIR, \"data\", \"dm\");\nexport const SESSION_WORKSPACE_DIR = resolve(STATE_DIR, \"data\", \"sessions\");\n// The Claude Agent SDK ships a platform-matched Claude Code binary. Keep that\n// SDK/CLI pair together by default; only use an external executable when an\n// operator explicitly opts in. This avoids silently pairing an older SDK with\n// a newer globally installed Claude Code release.\nconst CLAUDE_EXECUTABLE_ENV = envText(\"NEGOTIUM_CLAUDE_EXECUTABLE\");\nexport const CLAUDE_EXECUTABLE = CLAUDE_EXECUTABLE_ENV ? resolve(CLAUDE_EXECUTABLE_ENV) : undefined;\n\n// Fallback output language for model-generated prose (topic/channel/manager\n// replies and the archiver's summaries, briefs, articles, completion reply).\n// Defaults to English; set `NEGOTIUM_LANG` to the user's mother tongue (e.g.\n// `Korean`, `ko`). The assistant still mirrors whatever language the user\n// writes in; `NEGOTIUM_MEMORY_LANG` can narrow just the archiver. Fixed system\n// chrome / degraded-path strings emitted by code stay English (can't translate\n// an arbitrary value at runtime).\nexport const DEFAULT_OUTPUT_LANGUAGE = \"English\";\n\nexport function resolveOutputLanguage(): string {\n const raw = envText(\"NEGOTIUM_LANG\")?.trim();\n return raw && raw.length > 0 ? raw : DEFAULT_OUTPUT_LANGUAGE;\n}\n\n/**\n * Suffix the managed Rust helpers carry on this platform.\n *\n * The installers write the binary under its plain name, which on Windows is\n * not executable — the release asset is `browser-rs.exe` and the resolver has\n * to look for exactly that. Empty everywhere else, so POSIX paths are\n * unchanged.\n */\nexport const BINARY_EXTENSION = process.platform === \"win32\" ? \".exe\" : \"\";\n\n/** Browser.rs release tested with this Negotium version. */\nexport const BROWSER_RS_VERSION = \"v0.7.1\";\n/** Require the authenticated listener and the current Browser.rs tool contract. */\nexport const BROWSER_RS_MIN_SECURE_VERSION = \"0.2.1\";\n\nfunction versionAtLeast(actualVersion: string, minimumVersion: string): boolean {\n const actual = actualVersion.split(\".\").map(Number);\n const minimum = minimumVersion.split(\".\").map(Number);\n if (actual.some(Number.isNaN) || minimum.some(Number.isNaN)) return false;\n for (let index = 0; index < minimum.length; index += 1) {\n if ((actual[index] ?? 0) > (minimum[index] ?? 0)) return true;\n if ((actual[index] ?? 0) < (minimum[index] ?? 0)) return false;\n }\n return true;\n}\n\n/**\n * Ceiling for the `--version` probe, not a latency expectation.\n *\n * The probe exists so a wedged binary cannot hang startup; a healthy one\n * answers in milliseconds. At 2s it also failed whenever the machine was merely\n * busy — process startup under a loaded test suite regularly exceeds that —\n * which turned a correctness check into a load measurement and made\n * `resolveBrowserRsBin` intermittently report a perfectly good binary as\n * unusable. Generous enough that only a genuinely stuck process trips it.\n */\nconst BROWSER_RS_VERSION_PROBE_TIMEOUT_MS = 15_000;\n\nfunction browserRsMeetsMinimumVersion(candidate: string): boolean {\n try {\n const output = execFileSync(candidate, [\"--version\"], {\n encoding: \"utf8\",\n timeout: BROWSER_RS_VERSION_PROBE_TIMEOUT_MS,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n // This module is imported by every CLI invocation, so on Windows the\n // probe blinked a console window each time `negotium` was run. Ignored\n // on POSIX.\n windowsHide: true,\n }).trim();\n const match = output.match(/^browser-rs (\\d+)\\.(\\d+)\\.(\\d+)$/);\n if (!match) return false;\n return versionAtLeast(match.slice(1).join(\".\"), BROWSER_RS_MIN_SECURE_VERSION);\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the preferred Browser.rs engine without consulting PATH. The\n * versioned private location keeps different Negotium releases reproducible\n * and avoids changing a user's global browser-rs installation.\n */\nexport function resolveBrowserRsBin(envValue?: string): string | undefined {\n const override = envValue?.trim();\n if (\n !override &&\n !versionAtLeast(BROWSER_RS_VERSION.replace(/^v/, \"\"), BROWSER_RS_MIN_SECURE_VERSION)\n ) {\n return undefined;\n }\n const candidate = override\n ? resolve(override)\n : resolve(BINARIES_DIR, \"browser-rs\", BROWSER_RS_VERSION, `browser-rs${BINARY_EXTENSION}`);\n try {\n accessSync(candidate, constants.X_OK);\n return browserRsMeetsMinimumVersion(candidate) ? candidate : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Backend that stores documents published by `publish_html` and serves them\n * at `<base>/snippets/<id>`. Unset means the deployment has nowhere to\n * publish, and the publish tools are omitted from the catalog rather than\n * offering a link that cannot be minted.\n */\nexport const SNIPPETS_API_URL = (\n envText(\"NEGOTIUM_SNIPPETS_API_URL\") ??\n envText(\"SNIPPETS_API_URL\") ??\n \"\"\n).replace(/\\/+$/, \"\");\n\nexport const BROWSER_RS_BIN = resolveBrowserRsBin(envText(\"NEGOTIUM_BROWSER_RS_BIN\"));\n\n/** bash-rs release tested with this Negotium version — see apps/negotium/install-bash-rs.mjs. */\nexport const BASH_RS_VERSION = \"v0.1.10\";\n\n/**\n * Resolve the bash-rs binary the same way `resolveBrowserRsBin` resolves\n * Browser.rs: a versioned private location, no PATH lookup, `undefined`\n * (rather than throwing) when it's missing. There is no substitute: the manager\n * throws when a turn tries to prepare background-bash without a binary (see\n * `background-bash/manager.ts`), the turn runner catches that and continues\n * with a reminder that the tools are gone, so background-bash is simply\n * unavailable on a platform without a prebuilt binary.\n */\nexport function resolveBashRsBin(envValue?: string): string | undefined {\n const override = envValue?.trim();\n const candidate = override\n ? resolve(override)\n : resolve(BINARIES_DIR, \"bash-rs\", BASH_RS_VERSION, `bash-rs${BINARY_EXTENSION}`);\n try {\n accessSync(candidate, constants.X_OK);\n return candidate;\n } catch {\n return undefined;\n }\n}\n\nexport const BASH_RS_BIN = resolveBashRsBin(envText(\"NEGOTIUM_BASH_RS_BIN\"));\n\n// Managed Browser.rs terminates MCP transports and security policy itself.\nexport function resolveBrowserMcpBin(envValue?: string): string {\n const override = envValue?.trim();\n if (override) return resolve(override);\n return (\n BROWSER_RS_BIN ??\n resolve(BINARIES_DIR, \"browser-rs\", BROWSER_RS_VERSION, `browser-rs${BINARY_EXTENSION}`)\n );\n}\n\nexport const PLAYWRIGHT_MCP_BIN = resolveBrowserMcpBin(envText(\"NEGOTIUM_BROWSER_MCP_BIN\"));\n\n// --- Browser egress proxy ---\n//\n// On a datacenter host (AWS) the browser's egress IP is a known cloud range,\n// so anti-bot services (Cloudflare, DataDome, reCAPTCHA) challenge or block it\n// far more than a residential IP would. Routing the automation browser through\n// a residential/ISP proxy moves the egress IP out of the datacenter range.\n//\n// Operators set BROWSER_PROXY_URL, e.g. http://user:pass@proxy.host:8080 or\n// socks5://proxy.host:1080. Credentials in the URL are split out because\n// Playwright takes them as separate fields. BROWSER_PROXY_BYPASS is an optional\n// comma-separated no-proxy list (e.g. \"localhost,127.0.0.1,*.internal\").\n//\n// NOTE: Chromium does not support authentication for SOCKS proxies — put\n// credentials only on http/https proxy URLs.\nexport type BrowserProxyConfig = {\n server: string;\n username?: string;\n password?: string;\n bypass?: string;\n};\n\nexport function resolveBrowserProxy(): BrowserProxyConfig | null {\n const raw = envText(\"BROWSER_PROXY_URL\");\n if (!raw) return null;\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n logger.warn({ raw }, \"Ignoring malformed BROWSER_PROXY_URL\");\n return null;\n }\n // Playwright wants the server without embedded credentials.\n const server = `${url.protocol}//${url.host}`;\n const proxy: BrowserProxyConfig = { server };\n if (url.username) proxy.username = decodeURIComponent(url.username);\n if (url.password) proxy.password = decodeURIComponent(url.password);\n const bypass = envText(\"BROWSER_PROXY_BYPASS\");\n if (bypass) proxy.bypass = bypass;\n return proxy;\n}\n\n// --- Node/tsx runtime for the `codex` agent's MCP servers ---\n//\n// codex 0.135's rmcp stdio MCP client cannot reliably complete the initialize\n// handshake with servers spawned via `bun` (the JSON-RPC initialize is\n// dropped/raced and no tools ever reach the model). Pure-node servers connect\n// reliably, so codex turns launch the SAME .ts servers via node + tsx instead\n// of `bun run`. claude/maestro keep using `bun run` (fast, native, unaffected).\n//\n// tsx transpiles .ts on the fly and resolves the `@/*` tsconfig path aliases,\n// but only when it can find the tsconfig — and MCP servers run with cwd set to\n// the user's workspace dir, not PROJECT_ROOT — so we pass TSX_TSCONFIG_PATH\n// explicitly via env. Requires package.json `\"type\": \"module\"` so the servers'\n// top-level `await` loads as ESM under node.\nexport const TSX_BIN = resolveDependencyBin(\"tsx\");\n/**\n * In-process tsx loader used by Node MCP entrypoints (avoids the tsx CLI child\n * process).\n *\n * `node --import` takes a module specifier, not a filesystem path. A POSIX\n * absolute path happens to be a valid one; a Windows `C:\\…` is not — Node reads\n * the drive letter as a URL scheme and refuses it with\n * ERR_UNSUPPORTED_ESM_URL_SCHEME, which took out every tsx-hosted MCP server\n * (the Codex tool surface) at spawn. Hand it a `file://` URL there, and leave\n * the POSIX value exactly as it was.\n */\nconst TSX_LOADER_PATH = createRequire(import.meta.url).resolve(\"tsx\");\nexport const TSX_LOADER =\n process.platform === \"win32\" ? pathToFileURL(TSX_LOADER_PATH).href : TSX_LOADER_PATH;\nexport const TSCONFIG_PATH = resolve(PROJECT_ROOT, \"tsconfig.json\");\n\nexport const SESSION_COMM_SERVER = resolve(PROJECT_ROOT, \"src/mcp/session-comm/server.ts\");\n\nexport const TASK_SERVER = resolve(PROJECT_ROOT, \"src/mcp/task-server.ts\");\nexport const DECISION_SERVER = resolve(PROJECT_ROOT, \"src/mcp/decision-server.ts\");\nexport const CANONICAL_MCP_PROXY_SERVER = resolve(\n PROJECT_ROOT,\n \"src/mcp/canonical-proxy-server.ts\",\n);\n\nexport const WIKI_SERVER = resolve(PROJECT_ROOT, \"src/mcp/wiki-server.ts\");\n\nexport const TOKEN_STATS_SERVER = resolve(PROJECT_ROOT, \"src/mcp/token-stats-server.ts\");\n\nexport const COMPACTION_LOG_SERVER = resolve(PROJECT_ROOT, \"src/mcp/compaction-log-server.ts\");\n\nexport const SYSTEM_HEALTH_SERVER = resolve(PROJECT_ROOT, \"src/mcp/system-health-server.ts\");\n\nexport const AGENT_HEALTH_SERVER = resolve(PROJECT_ROOT, \"src/mcp/agent-health-server.ts\");\n\nexport const VAULT_SERVER = resolve(PROJECT_ROOT, \"src/mcp/vault-server.ts\");\n\nexport const BG_BASH_BASE_PORT = parsePortEnv(process.env.BG_BASH_BASE_PORT, 9700);\nexport const BG_BASH_MAX_PORT = parsePortEnv(process.env.BG_BASH_MAX_PORT, 9799);\n\nfunction safeWorkspaceSegment(value: string, fallback: string): string {\n return safeRuntimePathSegment(value, fallback);\n}\n\n/** Resolve the shared filesystem workspace for an API topic. */\nexport function resolveTopicWorkspaceDir(topicId: string): string {\n return join(TOPIC_WORKSPACE_DIR, safeWorkspaceSegment(topicId, \"topic\"));\n}\n\nexport function isProductionEnv(): boolean {\n return process.env.NODE_ENV === \"production\";\n}\n\nfunction loadOrCreateLocalSecret(\n envKey: string,\n filename: string,\n options: { persistEnvValue?: boolean } = {},\n): string {\n const envValue = envText(envKey);\n const secretFile = resolve(SECRETS_DIR, filename);\n mkdirSync(dirname(secretFile), { recursive: true });\n if (envValue) {\n if (options.persistEnvValue) {\n writeFileSync(secretFile, `${envValue}\\n`, { mode: 0o600 });\n chmodSync(secretFile, 0o600);\n }\n return envValue;\n }\n\n if (existsSync(secretFile)) {\n const stored = readFileSync(secretFile, \"utf-8\").trim();\n if (stored) {\n chmodSync(secretFile, 0o600);\n return stored;\n }\n }\n\n const secret = randomBytes(32).toString(\"base64url\");\n try {\n writeFileSync(secretFile, `${secret}\\n`, { mode: 0o600, flag: \"wx\" });\n return secret;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n const stored = readFileSync(secretFile, \"utf-8\").trim();\n if (!stored) throw new Error(`Secret file exists but is empty: ${secretFile}`);\n chmodSync(secretFile, 0o600);\n return stored;\n }\n}\n\nexport const RUNTIME_MCP_SECRET = loadOrCreateLocalSecret(\n \"RUNTIME_MCP_SECRET\",\n \"runtime-mcp-secret\",\n);\n/** Local bearer token for the loopback node-control API. */\nexport const NODE_CONTROL_TOKEN = loadOrCreateLocalSecret(\n \"NEGOTIUM_CONTROL_TOKEN\",\n \"node-control-token\",\n);\n/**\n * Stable identity of this node's store, minted once and kept across restarts.\n *\n * Deliberately not a secret: it is published in the gateway health response so a\n * host can tell \"the node I recorded a mapping against\" apart from \"whatever is\n * answering on that port today\". Without it, pointing a host at a different node\n * makes every existing mapping look like a topic whose owner withdrew it, and the\n * same topics come back as duplicate rooms. It shares the secrets directory only\n * because that is already where per-install state that must survive restarts\n * lives; the stricter file mode costs nothing.\n */\nexport const NODE_ID = loadOrCreateLocalSecret(\"NEGOTIUM_NODE_ID\", \"node-id\");\nexport const VAULT_MASTER_KEY = loadOrCreateLocalSecret(\n \"NEGOTIUM_VAULT_MASTER_KEY\",\n \"vault-master-key\",\n { persistEnvValue: true },\n);\n// Agent/tool subprocesses inherit process.env. Keep the loaded key in this\n// process only so `env`/`ps` inside an agent workspace cannot reveal it.\ndelete process.env.NEGOTIUM_VAULT_MASTER_KEY;\n\n/** The node's single open port: runtime MCP endpoint + node API. */\nexport const NEGOTIUM_PORT = parseInt(process.env.NEGOTIUM_PORT || \"7777\", 10);\nexport const hostname = process.env.HOSTNAME || \"127.0.0.1\";\n\n// Persistent state (survives restarts, long-lived)\nexport const DATA_DIR = resolveLocalStateDir(\"NEGOTIUM_DATA_DIR\", \"data\");\nexport const LOG_DIR = resolveLocalStateDir(\"NEGOTIUM_LOG_DIR\", \"logs\");\nexport const UPLOADS_DIR = resolve(DATA_DIR, \"uploads\");\nexport const VAULT_DIR = resolve(DATA_DIR, \"vault\");\n// SESSIONS_DB_PATH env override lets tests point the DB singleton at a temp file.\nexport const SESSIONS_DB = process.env.SESSIONS_DB_PATH\n ? resolve(process.env.SESSIONS_DB_PATH)\n : resolve(DATA_DIR, \"sessions.db\");\nexport const DEBUG_FILE = resolve(DATA_DIR, \"debug-users.json\");\nexport const USERS_LOG_DIR = resolve(DATA_DIR, \"users\");\n\n// Runtime IPC queues (transient, safe to clear on restart)\n/** @deprecated Use the runtime layout name; NEGOTIUM_RUN_DIR remains a compatibility override. */\nexport const RUN_DIR = resolveLocalStateDir(\"NEGOTIUM_RUN_DIR\", \"runtime\");\nexport const RUNTIME_DIR = RUN_DIR;\nexport const PROGRESS_DIR = resolve(RUN_DIR, \"progress\");\nexport const DM_CMD_DIR = resolve(RUN_DIR, \"dm-commands\");\nexport const DM_RESP_DIR = resolve(RUN_DIR, \"dm-responses\");\nexport const SESSION_INBOX_DIR = resolve(RUN_DIR, \"session-inbox\");\nexport const SESSION_ASKS_DIR = resolve(RUN_DIR, \"session-asks\");\n/**\n * Passed to `bash-rs` as `BASHRS_SPILL_ROOT`. Each job gets a subdirectory\n * here (`{bash_id}/meta.json`, `result.json`, `stdout.log`, `stderr.log`) —\n * see bash-rs-mcp's `journal.rs`. `runtime/bashrs-completions.ts` watches\n * this directory and turns `result.json` into a session-inbox `tell`. The\n * retired TypeScript server used to write one directly instead.\n */\nexport const BASHRS_SPILL_ROOT = resolve(RUN_DIR, \"bashrs\");\nexport const PLAYWRIGHT_BASE_PORT = parsePortEnv(process.env.PLAYWRIGHT_BASE_PORT, 9100);\nexport const PLAYWRIGHT_MAX_PORT = parsePortEnv(process.env.PLAYWRIGHT_MAX_PORT, 9499);\nexport const PLAYWRIGHT_PORTS_DIR = resolve(RUN_DIR, \"playwright-ports\");\nmkdirSync(STATE_DIR, { recursive: true });\nmkdirSync(DATA_DIR, { recursive: true });\nmkdirSync(UPLOADS_DIR, { recursive: true });\nmkdirSync(VAULT_DIR, { recursive: true, mode: 0o700 });\nmkdirSync(LOG_DIR, { recursive: true });\nmkdirSync(PROGRESS_DIR, { recursive: true });\nmkdirSync(DM_CMD_DIR, { recursive: true });\nmkdirSync(DM_RESP_DIR, { recursive: true });\nmkdirSync(SESSION_INBOX_DIR, { recursive: true });\nmkdirSync(SESSION_ASKS_DIR, { recursive: true });\nmkdirSync(PLAYWRIGHT_PORTS_DIR, { recursive: true });\nmkdirSync(WORKSPACE_DIR, { recursive: true });\nmkdirSync(TOPIC_WORKSPACE_DIR, { recursive: true });\nmkdirSync(SHARED_WIKI_DIR, { recursive: true });\nmkdirSync(CRON_WORKSPACE_DIR, { recursive: true });\nmkdirSync(DM_WORKSPACE_DIR, { recursive: true });\nmkdirSync(SESSION_WORKSPACE_DIR, { recursive: true });\nmkdirSync(BROWSER_DIR, { recursive: true });\nmkdirSync(BROWSER_PROFILES_DIR, { recursive: true });\nmkdirSync(BINARIES_DIR, { recursive: true });\nmkdirSync(SECRETS_DIR, { recursive: true, mode: 0o700 });\n\n/** Stale threshold for active-query state files (crash recovery) */\nexport const ACTIVE_QUERY_STALE_MS = 10 * 60 * 1000; // 10 minutes\n\nexport const AGENTS_PROMPTS_DIR = resolve(PROJECT_ROOT, \"src/prompts/agents\");\nexport const RESOURCES_DIR = resolve(PROJECT_ROOT, \"src/resources\");\n\n/** Returns process.env without CLAUDECODE, to prevent nested claude-code detection in subprocesses. */\nexport function getCleanEnv(): NodeJS.ProcessEnv {\n const env = { ...process.env };\n delete env.CLAUDECODE;\n return env;\n}\n\n/**\n * How an absolute path can start.\n *\n * A POSIX root (`/`) is not the only form once the runtime also runs on\n * Windows, where the same tag carries `C:\\…` or a `\\\\server\\share` UNC path.\n * Anchoring on `/` alone silently dropped every Windows attachment: the tag\n * stayed in the visible text and no file was ever sent.\n */\nconst ABSOLUTE_PATH_PREFIX = String.raw`(?:[A-Za-z]:[\\\\/]|\\\\\\\\|\\/)`;\n\nexport const FILE_EXTENSIONS_REGEX = new RegExp(\n `(?:${ABSOLUTE_PATH_PREFIX}[^\\\\s\"'<>|*?[\\\\]]+\\\\.(?:png|jpg|jpeg|gif|webp|svg|pdf|csv|xlsx|xls|json|txt|md|html|zip|py|js|ts|tsx|jsx|css|xml|yaml|yml|docx|pptx))`,\n \"gi\",\n);\n\nexport const FILE_TAG_REGEX = new RegExp(`\\\\[FILE:(${ABSOLUTE_PATH_PREFIX}[^\\\\]]+)\\\\]`, \"gi\");\n\n// Canonical Claude model IDs — update here when Anthropic releases new versions\nexport const MODEL_SONNET = \"claude-sonnet-5\";\nexport const MODEL_OPUS = \"claude-opus-5-5\";\nexport const MODEL_HAIKU = \"claude-haiku-4-5-20251001\";\nexport const MODEL_FABLE = \"claude-fable-5-1\"; // Mythos-class, Fable 5.1 announced 2026-09-01 (was claude-fable-5)\n\n// DeepSeek V4 (released 2026-04-24). API is OpenAI-compatible at\n// https://api.deepseek.com/v1/chat/completions; thinking mode is enabled via\n// `extra_body.thinking.type` + `reasoning_effort`. Legacy `deepseek-chat` /\n// `deepseek-reasoner` are deprecated 2026-07-24.\nexport const MODEL_DEEPSEEK_V4_PRO = \"deepseek-v4-pro\";\nexport const MODEL_DEEPSEEK_V4_FLASH = \"deepseek-v4-flash\";\n\n// Single agent + model default for the whole node. DEFAULT_* remains a legacy alias.\nexport const FALLBACK_AGENT: AgentKind = resolveFallbackAgent(process.env);\nexport const FALLBACK_MODEL = envText(\"FALLBACK_MODEL\") ?? envText(\"DEFAULT_MODEL\");\n\n/**\n * Effort used when a caller (or an archiver assignment) names a model but no\n * effort. Deliberately one fixed node-wide value instead of each agent's\n * registry default: a model-derived agent switch would otherwise move effort —\n * and therefore cost — without anyone asking for it.\n */\nexport const DEFAULT_TOPIC_EFFORT: EffortLevel = ((): EffortLevel => {\n const raw = envText(\"NEGOTIUM_DEFAULT_EFFORT\")?.toLowerCase();\n return raw && (EFFORT_VALUES as readonly string[]).includes(raw)\n ? (raw as EffortLevel)\n : \"medium\";\n})();\n\n// ── External tool binaries + media pipeline env ───────────────────\n// (src/media/* 에서 사용. 미설정 시 fallback 의미는 기존 그대로:\n// FFMPEG_BIN은 text-extractor에서 필수(undefined면 spawn 시점 실패),\n// video.ts에서는 PATH의 ffmpeg/ffprobe로 fallback.)\nexport const FFMPEG_BIN = envText(\"FFMPEG_BIN\");\nexport const FFPROBE_BIN = envText(\"FFPROBE_BIN\");\nexport const PYTHON_BIN = envText(\"PYTHON_BIN\") ?? \"python3\";\nexport const FASTER_WHISPER_WRAPPER =\n envText(\"FASTER_WHISPER_WRAPPER\") ?? resolve(PROJECT_ROOT, \"scripts/faster-whisper-wrapper.py\");\nexport const WHISPER_MODEL = envText(\"WHISPER_MODEL_FILE\") ?? \"turbo\";\nexport const TESSERACT_BIN = envText(\"TESSERACT_BIN\") ?? \"tesseract\";\nexport const PDFTOTEXT_BIN = envText(\"PDFTOTEXT_BIN\") ?? \"pdftotext\";\n\n// ── Whisper backend selection ──────────────────────────────────────\n// Different hosts want different whisper runtimes: Apple Silicon Macs get\n// real acceleration from mlx-whisper (see clawgram's transcribe.ts, which\n// this mirrors), while Linux/Ubuntu hubs have no MLX and use the CPU-friendly\n// faster-whisper wrapper instead. Both CLIs accept the same\n// `INPUT --model M --language L --output-dir D --output-format txt` shape\n// and write `{basename}.txt`, so extractFromAudio only needs to pick which\n// binary/args to use — no per-backend parsing logic.\nexport type WhisperBackend = \"faster-whisper\" | \"mlx-whisper\";\nfunction resolveWhisperBackend(): WhisperBackend {\n return envText(\"WHISPER_BACKEND\") === \"mlx-whisper\" ? \"mlx-whisper\" : \"faster-whisper\";\n}\nexport const WHISPER_BACKEND = resolveWhisperBackend();\n/** Path to the `mlx_whisper` CLI (only used when WHISPER_BACKEND=mlx-whisper). */\nexport const MLX_WHISPER_BIN = envText(\"MLX_WHISPER_BIN\");\n/** mlx-whisper model id, e.g. \"mlx-community/whisper-large-v3-turbo\".\n *\n * Deliberately does NOT fall back to WHISPER_MODEL: that env defaults to the\n * bare alias \"turbo\", which faster-whisper's own model-name normalization\n * accepts but mlx_whisper's `--model` does not — it expects a full\n * HuggingFace repo id and otherwise fails hard with a 401\n * RepositoryNotFoundError trying to resolve \"turbo\" as a repo. Confirmed by\n * actually running mlx_whisper with each value while wiring up the\n * mlx-whisper hub in production. */\nexport const WHISPER_MODEL_MLX =\n envText(\"WHISPER_MODEL_MLX\") ?? \"mlx-community/whisper-large-v3-turbo\";\n\n// Max tell_session relay depth from origin user. ask_session forks reset to\n// depth=0, so this only caps tell_session chains. Override via MAX_TELL_DEPTH\n// (positive int); defaults to 20 when unset or invalid.\nconst _envMaxTellDepth = Number.parseInt(process.env.MAX_TELL_DEPTH ?? \"\", 10);\nexport const MAX_TELL_DEPTH =\n Number.isInteger(_envMaxTellDepth) && _envMaxTellDepth > 0 ? _envMaxTellDepth : 20;\n\n/** Codex CLI auth file. 호출 시점에 env를 읽는다 — 테스트가 런타임에\n * NEGOTIUM_CODEX_AUTH_FILE을 바꾸므로 모듈 로드 상수로 만들면 안 된다. */\nexport function codexAuthFilePath(): string {\n return (\n process.env.NEGOTIUM_CODEX_AUTH_FILE ||\n join(process.env.CODEX_HOME || join(homedir(), \".codex\"), \"auth.json\")\n );\n}\n\n// System defaults moved to per-agent registries\n// (`src/agents/{claude,codex}-registry.ts`). Read via\n// `getRegistry(agent).defaultModel` / `.defaultEffort`.\n\n// MCP server builders -> src/platform/mcp-config.ts\n",
|
|
17
17
|
"import type { HostMcpServerSpec } from \"#runtime-gateway\";\n\nexport interface TokenUsage {\n /** Aggregate billable input across every model call made during this turn. */\n inputTokens: number;\n outputTokens: number;\n cacheCreationInputTokens?: number;\n cacheReadInputTokens?: number;\n /** Provider-reported query cost when available. */\n costUsd?: number;\n /** Tokens occupied by the latest model call, not aggregate turn spend. */\n contextTokens?: number;\n /** Provider-reported context window for the latest model call. */\n contextWindow?: number;\n}\n\n/** Agent identifier — one of the supported AI provider backends. */\nexport type AgentKind = \"maestro\" | \"claude\" | \"codex\";\n\nexport const SUPPORTED_AGENTS: readonly AgentKind[] = [\"maestro\", \"claude\", \"codex\"] as const;\n\nexport function isAgentKind(value: unknown): value is AgentKind {\n return typeof value === \"string\" && (SUPPORTED_AGENTS as readonly string[]).includes(value);\n}\n\n/**\n * Per-agent supported reasoning efforts. Single source of truth for both the\n * `EffortLevel` type and each registry's `validEfforts` runtime list — the\n * registries import these directly so adding a value in one place\n * propagates to validation, footer rendering, and zod enums.\n *\n * Claude SDK rejects 'minimal'; Codex SDK rejects 'max'. The two sets\n * intersect on low/medium/high/xhigh. Maestro (TS port) currently piggybacks\n * on the Anthropic provider, so its efforts mirror the Claude set; this can\n * narrow per-provider once Phase 5 lands.\n *\n * 'minimal' removed from codex: Codex API rejects it when default tools\n * (image_gen, web_search) are active, making agent sessions unusable.\n */\nexport const CLAUDE_EFFORT_VALUES = [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\nexport const CODEX_EFFORT_VALUES = [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\nexport const MAESTRO_EFFORT_VALUES = [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\n\nexport type EffortLevel =\n | (typeof CLAUDE_EFFORT_VALUES)[number]\n | (typeof CODEX_EFFORT_VALUES)[number]\n | (typeof MAESTRO_EFFORT_VALUES)[number];\n\n/**\n * Runtime iteration list (used by zod enums and any callers that need to\n * loop over every accepted value). Manually ordered for readability; the\n * `satisfies` check fails the build if an entry here isn't covered by the\n * per-agent unions above.\n */\nexport const EFFORT_VALUES = [\n \"low\",\n \"medium\",\n \"high\",\n \"xhigh\",\n \"max\",\n] as const satisfies readonly EffortLevel[];\n\n/**\n * Normalized events yielded by any agent provider (claudeProvider, codexProvider).\n * The handler/event-processor consumes these without caring which backend produced them.\n *\n * `user_message` is the lone \"into-the-log\" variant — no provider yields it.\n * The query handler writes it directly to the conversation log right before\n * `runAgent()` starts, so cross-agent rollout reconstruction can pair every\n * assistant turn with the user prompt that triggered it. Consumers that only\n * react to provider output (e.g. processAgentEvent) can safely ignore it.\n */\n/**\n * Wire-safe projection of one task, carried by the `tasks` UnifiedEvent.\n *\n * This is also the on-disk shape of Otium's shared task store, so claude,\n * codex, and maestro render the same live panel from the same source of truth.\n */\nexport interface TaskSnapshot {\n id: string;\n subject: string;\n status: \"pending\" | \"in_progress\" | \"completed\";\n /** Task ids this one is blocked by; omitted when empty. */\n blockedBy?: string[];\n /** Present-continuous label for spinners, when set. */\n activeForm?: string;\n /** Owner / agent name for multi-agent runs, when set. */\n owner?: string;\n}\n\n/** One topic-scoped decision and its incoming causal edges. */\nexport interface DecisionSnapshot {\n id: string;\n action: string;\n reasoning: string;\n agent: AgentKind;\n model?: string;\n status: \"proposed\" | \"accepted\" | \"executed\" | \"rejected\" | \"superseded\";\n /** Upstream decision ids. Each entry forms a directed upstream -> this edge. */\n causedBy?: string[];\n timestamp: number;\n}\n\nexport type UnifiedEvent =\n | {\n type: \"user_message\";\n content: string;\n synthetic?: \"compaction\";\n /** Total ordered user submissions represented by one preempting provider turn. */\n consecutiveBatchSize?: number;\n /** Zero-based position within the ordered preemption batch. */\n consecutiveBatchIndex?: number;\n }\n | { type: \"session\"; sessionId: string }\n | {\n type: \"tool_use\";\n name: string;\n input: Record<string, unknown>;\n /** Provider-assigned id so the client can match tool_use→tool_result pairs. */\n toolUseId?: string;\n }\n | { type: \"tool_progress\"; toolName: string; elapsed: number }\n | { type: \"tool_use_summary\"; summary: string }\n // Provider reasoning/thinking summary text (Codex `reasoning` items; Claude\n // extended-thinking). Surfaced so background runs (cron/archiver) show the\n // agent's thought process, not just tool calls.\n | { type: \"reasoning\"; content: string }\n // Full task-list snapshot (replace, not delta) from Otium's shared task\n // store. Provider-native task/todo stores are not authoritative.\n | { type: \"tasks\"; tasks: TaskSnapshot[] }\n | {\n type: \"tool_result\";\n toolUseId: string;\n content: string;\n /** True when the tool call failed; absent/false means success. */\n isError?: boolean;\n metadata?: {\n truncatedForModel: boolean;\n originalBytes: number;\n returnedBytes: number;\n omittedBytes?: number;\n outputPath?: string;\n };\n }\n | { type: \"text_delta\"; content: string }\n | { type: \"text\"; content: string }\n | { type: \"result\"; content: string; stopReason: string; usage?: TokenUsage }\n | { type: \"file\"; path: string; source: string; origin: \"tag\" | \"extension\" }\n | {\n type: \"error\";\n content: string;\n usage?: TokenUsage;\n code?: \"budget_exceeded\";\n }\n | { type: \"status\"; content: string };\n\nexport interface AgentInputAttachment {\n id: string;\n type: \"image\" | \"file\" | \"audio\";\n filename: string;\n mimeType: string;\n sizeBytes: number;\n path: string;\n}\n\n/** Worker-side runtime tools proxy user-facing state back to the canonical\n * hub topic identified here. */\nexport interface PeerRuntimeBridgeContext {\n hubCellId: string;\n hostTopicId: string;\n hostQueryId: string;\n canSpawnSubagents: boolean;\n}\n\n/**\n * The rooms the turn's human actor may reach on this node, asserted by the\n * hub that owns per-person membership for the `otium` surface.\n *\n * Node topic ids, valid on the node the turn was submitted to. `visible` is\n * every room the actor participates in (owner or member); `owned` is the\n * subset the actor owns. The assertion rides the signed per-turn MCP token,\n * so the agent can never widen it. With one, it is authoritative: reachable is\n * exactly the current room plus `visible`, owned exactly `owned` — the node\n * adds nothing of its own, not even subagent lineage. A turn without one\n * (older hub, or a turn no person started: cron, self-schedule, a subagent's\n * spawn/report/follow-up) is fail-closed to the current room plus the node's\n * own subagent lineage (direct parent, `grant_subagent_tell` targets, own\n * child workers).\n */\nexport interface ActorTopicScope {\n visibleNodeTopicIds: string[];\n ownedNodeTopicIds: string[];\n}\n\n/**\n * Hub-issued authority for the `node/topic` branches of `session-comm` on\n * the `otium` surface, one per turn. `capability` is an opaque bearer\n * (`rsc1.<payload>.<hmac>`) only the hub can verify: it binds the person who\n * spoke, the origin room and this turn's request id, and the hub re-checks\n * the person's membership of the origin and target rooms on every call. The\n * node holds no key, so it can neither mint nor verify one; the only thing it\n * reads out of the payload is `e` (expiry), base64-decoded untrusted to order\n * grants when several requests fold into one turn. `hubUrl` is where the\n * node presents it (`/api/v1/session-comm/remote/*`). Absent — an older hub,\n * a hub with the feature off, or a turn no person started (cron,\n * self-schedule, subagent lineage) — remote session-comm stays fail-closed.\n */\nexport interface RemoteSessionGrant {\n hubUrl: string;\n capability: string;\n}\n\nexport interface AgentQueryOptions {\n agent: AgentKind;\n prompt: string;\n attachments?: AgentInputAttachment[];\n sessionId?: string | null;\n cwd: string;\n systemPrompt: string;\n /**\n * Trusted system-level instructions for this Maestro invocation only.\n * Maestro projects them onto the invocation-start user message without\n * persisting them in session history or compaction. Omitted for Claude/Codex.\n */\n ephemeralSystemPrompt?: string;\n userId?: string;\n /** Product actor identity when execution uses a canonical node principal. */\n actorUserId?: string;\n /** Hub-asserted rooms the product actor may reach; absent means fail-closed. */\n actorTopicScope?: ActorTopicScope;\n /** Hub-issued per-turn authority for remote (`node/topic`) session-comm. */\n remoteSession?: RemoteSessionGrant;\n /** Credential namespace when it differs from the execution principal. */\n vaultUserId?: string;\n session?: string;\n playwrightPort?: number;\n playwrightCapability?: string;\n bgBashPort?: number;\n sessionType?: \"dm\" | \"forum\" | \"ephemeral\" | \"manager\" | \"cron\";\n /** API topic id (REST/WS world). Carries per-query topic context for MCP servers. */\n topicId?: string;\n /** Direct parent topic id when this query runs inside a subagent room. */\n subagentParentTopicId?: string;\n /** API query id for the currently running turn. Used by runtime MCP tools. */\n queryId?: string;\n /** Optional wiki-memory topic id. Derived topics use their root origin here\n * while other per-topic MCP servers keep `topicId` bound to the live room. */\n wikiTopicId?: string;\n /** Whether self-config MCP may enqueue an automatic continue turn after set_* changes. */\n autoContinue?: boolean;\n /** Expose Otium-only visual panel tools for this turn. Default-deny. */\n visualTools?: boolean;\n /** Expose adapter-backed file-delivery tools for this turn. Default-deny. */\n fileDeliveryTools?: boolean;\n /** Validated host MCPs resolved from durable manager-room grants at execution time. */\n hostMcpServers?: Record<string, HostMcpServerSpec>;\n /**\n * Thread this turn answers inside, so the turn's runtime MCP can default\n * `thread_read` to it. Turn-scoped, unlike the session, which is per topic.\n */\n threadRootId?: string;\n abortController?: AbortController;\n model?: string;\n /** Provider-side hard budget when the selected SDK supports one. */\n maxBudgetUsd?: number;\n depth?: number;\n agents?: Record<\n string,\n {\n description: string;\n prompt: string;\n model?: string;\n tools?: string[];\n maxTurns?: number;\n effort?: EffortLevel | number;\n }\n >;\n effort?: EffortLevel;\n /**\n * Per-API-call `max_tokens` ceiling on the assistant's output. Wired\n * through to the underlying provider request body for every agent\n * (claude/codex/maestro). Omit to inherit each provider SDK's per-model\n * default — for maestro that's the v0.1.21+ `getNativeMaxOutputTokens`\n * catalog (deepseek-pro=64K, kimi-k3=64K, kimi-k2.7-code=32K).\n *\n * Pass an explicit number when a specific topic / surface needs a tighter\n * latency cap or a higher ceiling for long-form generation (legal\n * report writing, multi-K Write/Edit file bodies). Pre-0.1.21 maestro\n * builds silently clamped at 4096 and truncated outputs mid-string;\n * setting this field is now the supported way to lift that ceiling.\n */\n maxTokens?: number;\n /**\n * v0.1.22+: Claude-Code-style deferred tool catalog + `ToolSearch` built-in.\n *\n * Wired straight through to `maestro-agent-sdk`'s\n * `AgentQueryOptions.enableToolSearch`. When `true`, the maestro provider\n * registers every MCP tool as deferred — schemas stay off the wire until\n * the model promotes them via `ToolSearch(\"select:Name1,Name2\")` or\n * `ToolSearch(\"keyword\")`. Active set persists across resume.\n *\n * Otium's maestro provider supplies `true` when the caller leaves this\n * option unset, because most forum turns carry enough MCP surface for the\n * reminder-token savings to outweigh the first-use `ToolSearch` round-trip.\n * Callers can still pass `false` per call when a narrow surface or\n * latency-sensitive workflow is better served by eager MCP schemas.\n *\n * No-op for claude / codex agents — they have their own deferred-tool\n * machinery owned by their respective SDKs.\n */\n enableToolSearch?: boolean;\n /**\n * Bounded tool results with the full output kept on disk.\n *\n * Wired to `maestro-agent-sdk`'s `AgentQueryOptions.toolResultTruncation`.\n * The SDK caps a string tool result, writes the untruncated bytes to a file,\n * and splices an opaque `maestro://tool-output/<id>` reference into the text\n * that the `ReadToolOutput` tool can page back through.\n *\n * The maestro provider enables it by default. Left unset, every tool result\n * — a whole-file `Read`, a wide `Grep`, a `WebFetch` of a large page —\n * entered the context at full size, and the `\"ReadToolOutput\"` entry in the\n * provider's builtin list was dead, because the SDK only registers that tool\n * when truncation is on with `saveFullOutput`.\n *\n * Pass an explicit object to tune the budget, or `{ enabled: false }` for a\n * call whose tool results must arrive whole.\n *\n * No-op for claude / codex agents — their SDKs do their own truncation.\n */\n toolResultTruncation?: {\n enabled?: boolean;\n maxBytes?: number;\n headBytes?: number;\n tailBytes?: number;\n saveFullOutput?: boolean;\n outputDir?: string;\n retentionDays?: number;\n ignoreTools?: string[];\n };\n /**\n * Claude-Code-compatible exact tool denylist. Maestro v0.1.42+ hides these\n * tools from provider schemas / ToolSearch and blocks dispatch if a stale\n * call still arrives. Claude maps this to its SDK option. Codex does not\n * support this name-based list; its provider-native multi-agent tool family\n * is disabled separately through the Codex feature config.\n */\n disallowedTools?: readonly string[];\n /**\n * Hard provider tool policy for auxiliary model calls.\n *\n * `\"none\"` removes MCP and provider-native tools before the request is\n * dispatched. `\"compaction-log\"` keeps provider-native tools disabled and\n * exposes only the host-scoped immutable log reader. Use these for untrusted\n * transcript transforms; reacting to tool events after dispatch is not a\n * security boundary.\n */\n toolPolicy?: \"none\" | \"compaction-log\";\n mcpEnabled?: string[] | null;\n peerBridge?: PeerRuntimeBridgeContext;\n mcpExtra?: Record<string, unknown>;\n /**\n * true for silent fork runs generating ask_session replies — restricts session-comm\n * outbound tools (ask/tell/abort) so the forked session can only produce text\n */\n silent?: boolean;\n}\n\n/** State file written to data/users/{userId}/active-queries/{topicId}.json while a query is running. */\nexport interface QueryState {\n topicId?: string;\n topicName?: string;\n task?: string; // first 100 chars of prompt, newlines normalized\n since: string; // ISO timestamp\n}\n",
|
|
18
18
|
"import { resolve } from \"node:path\";\nimport { type AgentKind, isAgentKind } from \"#types\";\n\nexport type RuntimeEnvironment = Readonly<Record<string, string | undefined>>;\n\nexport function readEnvText(env: RuntimeEnvironment, key: string): string | undefined {\n const value = env[key]?.trim();\n return value || undefined;\n}\n\n/**\n * Node-wide default agent (FALLBACK_AGENT env, legacy DEFAULT_AGENT, else\n * \"claude\"). Lives in this side-effect-free leaf module — not #platform/config\n * — so storage layers that only need this one value (e.g. the forum/api-topics\n * repositories, re-exported through the public storage facade) don't drag in\n * config.ts's eager `mkdirSync` of every node data directory just by\n * importing it.\n */\nexport function resolveFallbackAgent(env: RuntimeEnvironment = process.env): AgentKind {\n const value = readEnvText(env, \"FALLBACK_AGENT\") ?? readEnvText(env, \"DEFAULT_AGENT\");\n return isAgentKind(value) ? value : \"claude\";\n}\n\nexport function parseRuntimePort(value: string | undefined, fallback: number): number {\n if (!value) return fallback;\n const port = Number.parseInt(value, 10);\n return Number.isInteger(port) && port > 0 && port <= 65_535 ? port : fallback;\n}\n\nexport function resolveRuntimeStateDir(options: {\n env: RuntimeEnvironment;\n envKey: string;\n fallbackRoot: string;\n fallbackName: string;\n}): string {\n const configured = readEnvText(options.env, options.envKey);\n return configured ? resolve(configured) : resolve(options.fallbackRoot, options.fallbackName);\n}\n\nexport function safeRuntimePathSegment(value: string, fallback: string, maxLength = 160): string {\n const cleaned = value\n .trim()\n .replace(/[^A-Za-z0-9._-]/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .slice(0, maxLength);\n return cleaned || fallback;\n}\n",
|
|
19
19
|
"import {\n appendFileSync,\n closeSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { safeUnlink } from \"#platform/file-utils\";\n\nexport function readJsonlLines(filePath: string): string[] {\n return readFileSync(filePath, \"utf-8\").trim().split(\"\\n\").filter(Boolean);\n}\n\n/**\n * Parse a JSONL string (one JSON value per line) into an array of `T`.\n * Throws on malformed lines — use when a corrupt fixture/log should fail\n * loudly rather than silently drop entries (paired with `readJsonlLines`'\n * resilient counterpart for live data streams).\n */\nexport function parseJsonlText<T = unknown>(raw: string): T[] {\n return raw\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .map((line) => JSON.parse(line) as T);\n}\n\n/**\n * Read and JSON-parse a file. Returns null if the file is missing or contains\n * invalid JSON. Use when a missing/corrupt file should fall through to a\n * default rather than throw.\n */\nexport function readJsonFile<T = unknown>(filePath: string): T | null {\n try {\n return JSON.parse(readFileSync(filePath, \"utf-8\")) as T;\n } catch {\n return null;\n }\n}\n\n/** Atomically replace a JSON file and fsync the new contents before rename. */\nexport function writeJsonFileAtomic(filePath: string, value: unknown): void {\n const dir = dirname(filePath);\n mkdirSync(dir, { recursive: true });\n const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random()\n .toString(16)\n .slice(2)}`;\n let fd: number | null = null;\n try {\n fd = openSync(tmpPath, \"wx\", 0o600);\n writeFileSync(fd, JSON.stringify(value, null, 2));\n fsyncSync(fd);\n closeSync(fd);\n fd = null;\n renameSync(tmpPath, filePath);\n fsyncDirectoryBestEffort(dir);\n } catch (err) {\n if (fd !== null) {\n try {\n closeSync(fd);\n } catch {}\n }\n safeUnlink(tmpPath);\n throw err;\n }\n}\n\n// --- Cross-process append lock ---\n// POSIX `O_APPEND` is only atomic when the write payload fits in PIPE_BUF\n// (Linux 4096, macOS 512). MCP servers and the bot run as separate\n// processes that all `appendFileSync` to shared inbox/outbox files; a long\n// `tell_session` prompt or large `ask_session` reply is comfortably above\n// PIPE_BUF, and a concurrent append from another process can interleave at\n// the byte level — the result fails JSON parse and the entire entry is\n// silently dropped by `parseOutboxLine`.\n//\n// Mitigation: a sidecar `<filePath>.lock` created with `O_EXCL` serializes\n// every append at the OS level. Lock is held for the duration of one\n// `appendFileSync`, so the contention window is microseconds. Stale locks\n// (process crashed mid-write) are detected via mtime and forcibly removed\n// after `LOCK_STALE_MS`.\n//\n// When the lock still cannot be acquired within `LOCK_TIMEOUT_MS`, the append\n// FAILS. It used to fall through to an unlocked `appendFileSync` on the theory\n// that a possibly-interleaved entry beats a dropped one — but an interleaved\n// write is exactly the corruption the lock exists to prevent, it can damage a\n// neighbouring well-formed entry as well as its own, and `parseOutboxLine`\n// discards the wreckage later and far from the cause. The caller was told the\n// write succeeded. Failing loudly lets each caller choose: surface the error to\n// its client, or log and continue.\n//\n// The timeout MUST outlast the staleness threshold. A writer killed while\n// holding the lock leaves the file behind, and only the mtime check reclaims\n// it. With a timeout shorter than `LOCK_STALE_MS` every append issued in the\n// first few seconds after such a crash gave up *before* the reclaim could fire\n// — measured as a ~3.5s window in which every append to that file failed, all\n// of which the pre-throw fallback used to deliver. Waiting past the staleness\n// threshold means a dead holder is always reclaimed rather than waited out, so\n// a timeout now means a *live* holder has been stuck for over five seconds,\n// which is a genuine fault worth reporting.\nconst LOCK_SUFFIX = \".lock\";\nconst LOCK_RETRY_MS = 5;\nconst DEFAULT_LOCK_STALE_MS = 5000;\n/** Headroom so a waiter never gives up exactly as the reclaim becomes due. */\nconst LOCK_TIMEOUT_HEADROOM_MS = 1500;\n\n/**\n * Staleness threshold, overridable so tests can exercise the timeout path\n * without blocking a real five seconds.\n *\n * Read per call rather than captured at import: the constants are consulted\n * once per append, which is nothing next to the file I/O that follows, and a\n * module-level snapshot could not be adjusted by a test that imports this\n * module transitively.\n */\nfunction lockStaleMs(): number {\n const raw = Number.parseInt(process.env.NEGOTIUM_JSONL_LOCK_STALE_MS ?? \"\", 10);\n return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_LOCK_STALE_MS;\n}\n\n/** Always > `lockStaleMs()`, so a dead holder is reclaimed rather than waited out. */\nfunction lockTimeoutMs(): number {\n return lockStaleMs() + LOCK_TIMEOUT_HEADROOM_MS;\n}\nconst LOCK_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));\n\n/**\n * The append lock could not be acquired, so nothing was written.\n *\n * Distinguishable from an I/O error so callers can retry, degrade, or report\n * contention specifically. Nothing was appended when this is thrown.\n */\nexport class JsonlLockTimeoutError extends Error {\n constructor(\n readonly filePath: string,\n readonly timeoutMs: number,\n ) {\n super(`jsonl append lock busy after ${timeoutMs}ms; nothing written to ${filePath}`);\n this.name = \"JsonlLockTimeoutError\";\n }\n}\n\n/** Synchronous, non-spinning sleep supported by both Bun and Node MCP workers. */\nfunction sleepForAppendLock(ms: number): void {\n Atomics.wait(LOCK_SLEEP, 0, 0, ms);\n}\n\nfunction tryAcquireAppendLock(lockPath: string): boolean {\n try {\n closeSync(openSync(lockPath, \"wx\")); // O_CREAT | O_EXCL — atomic\n return true;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== \"EEXIST\") throw e;\n return false;\n }\n}\n\nfunction isStaleLock(lockPath: string): boolean {\n try {\n return Date.now() - statSync(lockPath).mtimeMs > lockStaleMs();\n } catch {\n return false;\n }\n}\n\n/**\n * Append a JSON-encoded entry + newline to a JSONL file. Ensures the parent\n * directory exists and serializes concurrent multi-process appends through a\n * sidecar lock so payloads larger than PIPE_BUF do not interleave.\n *\n * The drainer side (`outbox/file-ops.ts`) claims the file via atomic rename\n * and handles recovery of leftover `.processing` files from crashes.\n */\nexport function appendJsonlEntry(filePath: string, entry: unknown): void {\n appendJsonlLine(filePath, `${JSON.stringify(entry)}\\n`);\n}\n\n/**\n * Append an already-serialized JSONL line (must end with \"\\n\") under the\n * same cross-process append lock as `appendJsonlEntry`. Use when you\n * want to write back a raw line **exactly** — e.g. flusher tail-writeback\n * preserving lines that current parser can't decode, so a future parser\n * release still sees them.\n */\nexport function appendJsonlLine(filePath: string, line: string): void {\n mkdirSync(dirname(filePath), { recursive: true });\n const lockPath = `${filePath}${LOCK_SUFFIX}`;\n const payload = line.endsWith(\"\\n\") ? line : `${line}\\n`;\n\n let acquired = tryAcquireAppendLock(lockPath);\n // Pre-check: if the existing lock is already stale (writer crashed before\n // unlinking), reclaim it before sleeping. Saves the entire retry budget\n // for the common stale-leftover case.\n if (!acquired && isStaleLock(lockPath)) {\n safeUnlink(lockPath);\n acquired = tryAcquireAppendLock(lockPath);\n }\n if (!acquired) {\n const start = Date.now();\n const timeoutMs = lockTimeoutMs();\n while (!acquired && Date.now() - start < timeoutMs) {\n // Codex launches built-in stdio MCPs under Node+tsx while the runtime\n // itself uses Bun. Atomics.wait gives both processes a blocking sleep\n // without a CPU-spinning fallback or a Bun-only global.\n sleepForAppendLock(LOCK_RETRY_MS);\n acquired = tryAcquireAppendLock(lockPath);\n // Recheck staleness inside the loop so a writer that crashes mid-wait\n // doesn't leave us spinning the whole timeout.\n if (!acquired && isStaleLock(lockPath)) {\n safeUnlink(lockPath);\n acquired = tryAcquireAppendLock(lockPath);\n }\n }\n }\n\n if (!acquired) throw new JsonlLockTimeoutError(filePath, lockTimeoutMs());\n\n try {\n appendFileSync(filePath, payload);\n } finally {\n safeUnlink(lockPath);\n }\n}\n\n/** Overwrite a JSONL file with the given entries. Trailing newline included. */\nexport function writeJsonlFile(filePath: string, entries: readonly unknown[]): void {\n const dir = dirname(filePath);\n mkdirSync(dir, { recursive: true });\n const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random()\n .toString(16)\n .slice(2)}`;\n const payload = `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n let fd: number | null = null;\n try {\n fd = openSync(tmpPath, \"w\");\n writeFileSync(fd, payload);\n fsyncSync(fd);\n closeSync(fd);\n fd = null;\n renameSync(tmpPath, filePath);\n fsyncDirectoryBestEffort(dir);\n } catch (err) {\n if (fd !== null) {\n try {\n closeSync(fd);\n } catch {}\n }\n try {\n unlinkSync(tmpPath);\n } catch {}\n throw err;\n }\n}\n\nfunction fsyncDirectoryBestEffort(dir: string): void {\n let fd: number | null = null;\n try {\n fd = openSync(dir, \"r\");\n fsyncSync(fd);\n } catch {\n // Directory fsync is not portable across every runtime/filesystem. The\n // file fsync + rename above is the important integrity boundary; this is\n // best-effort durability for the directory entry.\n } finally {\n if (fd !== null) {\n try {\n closeSync(fd);\n } catch {}\n }\n }\n}\n",
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
"/** Sanitize a topic/session name for use in file paths.\n * Replaces non-alphanumeric characters (excluding Korean) with underscores.\n * Empty input (or input consisting only of disallowed chars) returns \"_\" so\n * callers always get a non-empty single path component.\n * @param name The raw topic or session name.\n * @param lowercase Whether to lowercase the result (default: false).\n * Use true for wiki paths; leave false for log file paths.\n */\nexport function sanitizeTopicName(name: string, lowercase = false): string {\n const safe = name.replace(/[^a-zA-Z0-9가-힣_-]/g, \"_\") || \"_\";\n return lowercase ? safe.toLowerCase() : safe;\n}\n\n/** Sanitize a file name for use in file paths. Allows dots.\n * Pure-dot results (\".\", \"..\") are replaced with \"_\" to block path-traversal\n * when the sanitized value is used as a standalone path component.\n */\nexport function sanitizeFileName(name: string): string {\n const safe = name.replace(/[^a-zA-Z0-9._-]/g, \"_\") || \"_\";\n if (safe === \".\" || safe === \"..\") return \"_\";\n return safe;\n}\n\n/** Sanitize an arbitrary ID (no Korean, no dots) for use in file paths. */\nexport function sanitizeId(id: string): string {\n return id.replace(/[^a-zA-Z0-9_-]/g, \"_\") || \"_\";\n}\n"
|
|
22
22
|
],
|
|
23
23
|
"mappings": "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",
|
|
24
|
-
"debugId": "
|
|
24
|
+
"debugId": "BA0AE225F16D3FEB64756E2164756E21",
|
|
25
25
|
"names": []
|
|
26
26
|
}
|
package/dist/registry.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
encodeClaudeCwd,
|
|
4
4
|
writeClaudeRollout
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-mvsdbs2r.js";
|
|
6
6
|
import {
|
|
7
7
|
CLAUDE_EFFORT_VALUES,
|
|
8
8
|
CODEX_EFFORT_VALUES,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
hostedCodexHomePath,
|
|
18
18
|
logger,
|
|
19
19
|
writeCodexRollout
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-fymyw41t.js";
|
|
21
21
|
|
|
22
22
|
// ../../packages/core/src/agents/claude-registry.ts
|
|
23
23
|
import { existsSync, mkdirSync, readdirSync, renameSync, unlinkSync } from "fs";
|
|
@@ -132,7 +132,7 @@ var codexRegistryOperations = {
|
|
|
132
132
|
return { sessionId: threadId, rolloutPath };
|
|
133
133
|
},
|
|
134
134
|
async forkSession({ parentSessionId }) {
|
|
135
|
-
const { forkCodexSession } = await import("./chunk-
|
|
135
|
+
const { forkCodexSession } = await import("./chunk-zgvek90d.js");
|
|
136
136
|
return await forkCodexSession(parentSessionId);
|
|
137
137
|
},
|
|
138
138
|
async cleanupRollouts({ sessionIds }) {
|
package/dist/rollout.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
encodeClaudeCwd,
|
|
4
4
|
repairPoisonedRollout,
|
|
5
5
|
writeClaudeRollout
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-mvsdbs2r.js";
|
|
7
7
|
import {
|
|
8
8
|
configureRolloutHost,
|
|
9
9
|
decodeUuidV7Timestamp,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
readLatestCodexContextUsage,
|
|
17
17
|
readLatestCodexPatchPreview,
|
|
18
18
|
writeCodexRollout
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-fymyw41t.js";
|
|
20
20
|
|
|
21
21
|
// src/rollout.ts
|
|
22
22
|
import { fileURLToPath } from "url";
|
|
@@ -137,7 +137,7 @@ export function resolveOutputLanguage(): string {
|
|
|
137
137
|
export const BINARY_EXTENSION = process.platform === "win32" ? ".exe" : "";
|
|
138
138
|
|
|
139
139
|
/** Browser.rs release tested with this Negotium version. */
|
|
140
|
-
export const BROWSER_RS_VERSION = "v0.7.
|
|
140
|
+
export const BROWSER_RS_VERSION = "v0.7.1";
|
|
141
141
|
/** Require the authenticated listener and the current Browser.rs tool contract. */
|
|
142
142
|
export const BROWSER_RS_MIN_SECURE_VERSION = "0.2.1";
|
|
143
143
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const NEGOTIUM_VERSION = "0.20.
|
|
1
|
+
export const NEGOTIUM_VERSION = "0.20.3";
|
package/dist/runtime-helpers.js
CHANGED
|
@@ -536,7 +536,7 @@ var SESSION_WORKSPACE_DIR = resolve(STATE_DIR, "data", "sessions");
|
|
|
536
536
|
var CLAUDE_EXECUTABLE_ENV = envText("NEGOTIUM_CLAUDE_EXECUTABLE");
|
|
537
537
|
var CLAUDE_EXECUTABLE = CLAUDE_EXECUTABLE_ENV ? resolve(CLAUDE_EXECUTABLE_ENV) : undefined;
|
|
538
538
|
var BINARY_EXTENSION = process.platform === "win32" ? ".exe" : "";
|
|
539
|
-
var BROWSER_RS_VERSION = "v0.7.
|
|
539
|
+
var BROWSER_RS_VERSION = "v0.7.1";
|
|
540
540
|
var BROWSER_RS_MIN_SECURE_VERSION = "0.2.1";
|
|
541
541
|
function versionAtLeast(actualVersion, minimumVersion) {
|
|
542
542
|
const actual = actualVersion.split(".").map(Number);
|
|
@@ -1767,4 +1767,4 @@ export {
|
|
|
1767
1767
|
CLAUDE_EFFORT_VALUES
|
|
1768
1768
|
};
|
|
1769
1769
|
|
|
1770
|
-
//# debugId=
|
|
1770
|
+
//# debugId=B5C698A336EC707164756E2164756E21
|